pbootcms网站模板|日韩1区2区|织梦模板||网站源码|日韩1区2区|jquery建站特效-html5模板网

使用 C++ 用 Qt 制作我自己的照片馬賽克應用程序

Making my own photo-mosaic app with Qt using C++(使用 C++ 用 Qt 制作我自己的照片馬賽克應用程序)
本文介紹了使用 C++ 用 Qt 制作我自己的照片馬賽克應用程序的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

這是我到目前為止所做的輸出:

This is the output of what I have done till now:

雖然這僅顯示黑白圖像,但該代碼也適用于彩色圖像.該代碼基本上使用來自小型數據庫的較小圖像填充較大圖像.
所以這就是我被卡住的地方,相當無能為力.
我該怎么辦獲得這樣的效果. 或 這個.
Qn1:我需要獲取另一個輸入(大)圖像*(要看到效果的一個)*并合并它們,但是如何?
Qn2 : 我如何評估照片馬賽克的好壞?我有一個為此編寫的遺傳算法,但無法修復適應度函數,(突變和交叉工作完美).

Although this shows only black and white images, the code works for color images too. The code basically populates the larger image using smaller images from a small database.
So this is where I am stuck, rather clueless.
How can I get an effect like this one. or this one.
Qn1 : I need to take another input(large) image*(One for which the effect is to be seen)* and merge them, but how?
Qn2 : How can I evaluate the goodness of the photo-mosaic? I have a genetic algorithm written for this but am unable to fix the fitness function,(mutation and crossover work perfectly).

這就是我能想到的(對于Qn1):
1. 取上面顯示的圖像和必須制作馬賽克的圖像的交替像素.
2. 取上述像素值的平均值和需要制作馬賽克的輸入圖像.
但是沒有線索來評價它的好壞.

This is what I could think of(for Qn1):
1. Take alternate pixels of the image shown above and the image for which the mosaic has to be made.
2. Take average of the pixel values of the above and input image for which the mosaic has to be made.
But have no clue to evaluate the goodness.

推薦答案

下面是一個自包含的草圖.鑲嵌算法是優秀參考.我認為,它足以應付兩個小時的工作.我試圖讓代碼合理正確,但有兩個警告,正如他們所說,留給讀者作為練習.

Below is a self contained sketch. The mosaicing algorithm is mid-way through the algorithms implemented in an excellent reference. It works well enough for two hours of work, I think. I tried for the code to be reasonably correct, with two caveats, left, as they say, as an exercise to the reader.

  1. 我沒有跟蹤工作線程 - 如果您在工作線程處于活動狀態時嘗試退出應用程序,預計會在退出時崩潰.這不是很好,但在其他方面是良性的,不會影響整體功能.磁盤上可能會殘留一些損壞的圖像,但在重新加載時應忽略這些圖像.

  1. I'm not tracking the worker threads - if you try to exit the application while workers are active, it is expected to crash on exit. This is not nice, but otherwise benign and doesn't affect the overall functionality. There may be a few corrupt images left on disk, but those should be ignored when reloading.

標簽中顯示的圖像沒有縮放.窗口將調整為圖像大小.

There is no scaling of the image displayed in the label. The window will resize to the image size.

瓷磚圖像數據庫可以用來自imgur的隨機圖像填充,您也可以通過自己將它們存儲在磁盤上來用自己的圖像填充它.它位于以 /so-photomosaic/image 為后綴的標準應用程序數據路徑中.獲取的圖像被添加到那里.啟動時,圖像數據庫會在后臺從磁盤重新填充 - 這就是您自己的平鋪圖像的加載方式.事實上,所有的圖像處理都是在非 GUI 線程中完成的.在一個相當不起眼的 5 年前的 Core 2 OS X 系統上,磁盤映像加載以大約 5000 個映像/秒的速度進行.從 imgur 請求的圖像是它們的小尺寸,或 90x90.

The tile image database can be filled with random images from imgur, you can also fill it with your own images by storing them on disk yourself. It's located in a standard application data path suffixed by /so-photomosaic/image. The fetched images are added there. Upon startup, the image database is repopulated from disk in the background - that's how your own tile images would be loaded. In fact, all of image processing is done in non-GUI threads. On a rather unassuming 5 year old Core 2 OS X system, disk image loading proceeds at about 5000 images/s. The images requested from imgur are their small size, or 90x90.

拼貼匹配是使用 4x4 細分網格(divs 參數給 calcPropsFor)完成的.圖像被下采樣為 4x4 馬賽克,并且該網格中連續像素的 RGB 顏色值存儲在 Props 向量中.這些向量的元素的差的平方和是擬合的度量.對于每個要替換的圖塊,圖像根據它們的大小進行排序,并隨機挑選最好的一個.隨機性參數是隨機選擇圖像的樣本大小的冪.

The tile matching is done with a 4x4 subdivision grid (divs parameter to calcPropsFor). The images are downsampled to a 4x4 mosaic, and the RGB color values of consecutive pixels in that grid are stored in Props vectors. The squared sums of differences of elements of those vectors are the measure of fit. For each tile to be replaced, the images are sorted according to their fit, and one of the best ones are picked up at random. The randomness parameter is a power-of-to of the sample size from which the image is randomly selected.

它使用 Qt 5 和 C++11.長度:300行,其中64行是隨機圖片源,25行是磁盤圖片庫,88行實際上是馬賽克.如果使用 OpenCV 或 Eigen 而不是 valarray/QImage,圖像處理代碼的外觀和性能可能會更好,但很好.

It uses Qt 5 and C++11. Length: 300 lines, out of which 64 are the random image source, 25 are the disk image database, and 88 are actually to do with mosaics. The image processing code would probably look and perform better if OpenCV or Eigen was used instead of valarray/QImage, but oh well.

此外,所有這些在 Mathematica 中可能只有 50 行 :)

Also, all of this would be probably 50 lines in Mathematica :)

# main.pro
# Make sure to re-run quake once this is set.
TEMPLATE = app
QT += widgets network concurrent
CONFIG += c++11
SOURCES += main.cpp
TARGET = photomosaic

#include <QApplication>
#include <QLabel>
#include <QSlider>
#include <QPushButton>
#include <QCheckBox>
#include <QBoxLayout>
#include <QFileDialog>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QRegularExpression>
#include <QImage>
#include <QPainter>
#include <QColor>
#include <QAtomicInt>
#include <QMutex>
#include <QtConcurrent>
#include <QStandardPaths>
#include <algorithm>
#include <functional>
#include <valarray>

/// Provides random images. There may be more than one response per request.
class RandomImageSource : public QObject {
  Q_OBJECT
  int m_parallelism;
  bool m_auto;
  QNetworkAccessManager m_mgr;
  QSet<QNetworkReply*> m_replies;
  QList<QUrl> m_deferred;
  QRegularExpression m_imgTagRE, m_imgUrlRE;
  QUrl m_randomGallery;
  void get(const QUrl & url) {
    if (m_replies.count() < m_parallelism) {
      QNetworkRequest req(url);
      req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
      m_replies.insert(m_mgr.get(req));
    } else
      m_deferred << url;
  }
  void finishReply(QNetworkReply * reply) {
    m_replies.remove(reply);
    if (reply) reply->deleteLater();
    if (! m_deferred.isEmpty()) get(m_deferred.takeLast());
    while (m_deferred.isEmpty() && m_auto) get(m_randomGallery);
  }
  Q_SLOT void rsp(QNetworkReply * reply) {
    auto loc = reply->header(QNetworkRequest::LocationHeader);
    if (loc.isValid()) {
      get(loc.toUrl()); // redirect
    } else {
      auto ct = reply->header(QNetworkRequest::ContentTypeHeader).toString();
      if (ct.startsWith("text/html"))
        foreach (QUrl url, parseImageUrls(reply->readAll()))
          get(url);
      else if (ct.startsWith("image")) {
        auto img = QImage::fromData(reply->readAll());
        img.setText("filename", m_imgUrlRE.match(reply->url().toString()).captured(1));
        if (!img.isNull()) emit rspImage(img);
      }
    }
    finishReply(reply);
  }
  QList<QUrl> parseImageUrls(const QByteArray & html) {
    QList<QUrl> urls;
    auto it = m_imgTagRE.globalMatch(QString::fromUtf8(html));
    while (it.hasNext()) { auto match = it.next(); // get small images
      urls << QUrl("http:" + match.captured(1) + "s" + match.captured(2)); }
    return urls;
  }
public:
  RandomImageSource(QObject * parent = 0) : QObject (parent),
    m_parallelism(20), m_auto(false),
    m_imgTagRE("<img src="(//i\.imgur\.com/[^.]+)(\.[^"]+)""),
    m_imgUrlRE("http://i\.imgur\.com/(.+)$"),
    m_randomGallery("http://imgur.com/gallery/random")
  {
    connect(&m_mgr, SIGNAL(finished(QNetworkReply*)), SLOT(rsp(QNetworkReply*)));
  }
  Q_SLOT void reqImages(int count) {
    while (count--) get(m_randomGallery);
  }
  Q_SIGNAL void rspImage(const QImage &);
  bool automatic() const { return m_auto; }
  Q_SLOT void setAutomatic(bool a) { if ((m_auto = a)) finishReply(0); }
  int parallelism() const { return m_parallelism; }
  Q_SLOT void setParallelism(int p) { m_parallelism = p; if (m_auto) finishReply(0); }
};

/// Stores images on disk, and loads them in the background.
class ImageStorage : public QObject {
  Q_OBJECT
  QString const m_path;
public:
  ImageStorage() :
    m_path(QStandardPaths::writableLocation(QStandardPaths::DataLocation)
           + "/images/")
  { QDir().mkpath(m_path); }
  Q_SLOT void addImage(const QImage & img) {
    QString path = img.text("filename");
    if (path.isEmpty()) return;
    path.prepend(m_path);
    QtConcurrent::run([img, path]{ img.save(path); });
  }
  Q_SLOT void retrieveAll() {
    QString const path = m_path;
    QtConcurrent::run([this, path] {
      QStringList const images = QDir(path).entryList(QDir::Files);
      foreach (QString image, images) QtConcurrent::run([this, image, path] {
        QImage img; if (img.load(path + image)) emit retrieved(img);
      });
    });
  }
  Q_SIGNAL void retrieved(const QImage &);
};

/// A memory database of images. Finds best match to a given image.
class ImageDatabase : public QObject {
  Q_OBJECT
  typedef std::valarray<qreal> Props;
  typedef QPair<QImage, Props> ImageProps;
  QMutex mutable m_mutex;
  QList<ImageProps> m_images;
  static void inline addProps(Props & p, int i, QRgb rgb) {
    QColor const c = QColor::fromRgb(rgb);
    p[i+0] += c.redF(); p[i+1] += c.greenF(); p[i+2] += c.blueF();
  }
  static Props calcPropsFor(const QImage & img, int divs = 4) {
    Props props(0.0, 3 * divs * divs);
    std::valarray<int> counts(0, divs * divs);
    QSize div = img.size() / divs;
    for (int y = 0; y < img.height(); ++y)
      for (int x = 0; x < img.width(); ++x) {
        int slice = x/div.width() + (y*divs/div.height());
        if (slice >= divs*divs) continue;
        addProps(props, slice*3, img.pixel(x, y));
        counts[slice] ++;
      }
    for (size_t i = 0; i < props.size(); ++i) props[i] /= counts[i/3];
    return props;
  }
public:
  Q_SIGNAL void newImageCount(int);
  Q_SLOT void addImage(const QImage & img) {
    QtConcurrent::run([this, img]{
      Props props = calcPropsFor(img);
      QMutexLocker lock(&m_mutex);
      m_images << qMakePair(img, props);
      int count = m_images.count();
      lock.unlock();
      emit newImageCount(count);
    });
  }
  ImageProps bestMatchFor(const QImage & img, int randLog2) const {
    QMutexLocker lock(&m_mutex);
    QList<ImageProps> const images = m_images;
    lock.unlock();
    Props const props = calcPropsFor(img);
    typedef QPair<qreal, const ImageProps *> Match;
    QList<Match> matches; matches.reserve(images.size());
    std::transform(images.begin(), images.end(), std::back_inserter(matches),
                   [props](const ImageProps & prop){
      return qMakePair(pow(props - prop.second, 2).sum(), &prop);
    });
    std::sort(matches.begin(), matches.end(),
              [](Match a, Match b) { return b.first < a.first; });
    randLog2 = 1<<randLog2;
    return *(matches.end()-randLog2+qrand()%randLog2)->second;
  }
};

QImage getMosaic(QImage img, const ImageDatabase & db, int size, int randLog2)
{
  QPainter p(&img);
  for (int y = 0; y < img.height(); y += size)
    for (int x = 0; x < img.width(); x += size) {
      QImage r = db.bestMatchFor(img.copy(x, y, size, size), randLog2).first
          .scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
      p.drawImage(x, y, r);
    }
  return img;
}

class MosaicGenerator : public QObject {
  Q_OBJECT
  QPointer<ImageDatabase> m_db;
  int m_size, m_randLog2;
  QAtomicInt m_busy;
  QImage m_image;
  void update() {
    if (m_image.isNull() || m_busy.fetchAndAddOrdered(1)) return;
    QImage image = m_image;
    QtConcurrent::run([this, image]{ while (true) {
        emit hasMosaic(getMosaic(image, *m_db, m_size, m_randLog2));
        if (m_busy.testAndSetOrdered(1, 0)) return;
        m_busy.fetchAndStoreOrdered(1);
      }});
  }
public:
  MosaicGenerator(ImageDatabase * db) : m_db(db), m_size(16), m_randLog2(0) {}
  Q_SLOT void setImage(const QImage & img) { m_image = img; update(); }
  Q_SLOT void setSize(int s) { m_size = s; update(); }
  Q_SLOT void setRandLog2(int r) { m_randLog2 = r; update(); }
  Q_SIGNAL void hasMosaic(const QImage &);
};

class Window : public QWidget {
  Q_OBJECT
  bool m_showSource;
  QImage m_source, m_mosaic;
  QBoxLayout m_layout;
  QSlider m_parallelism, m_cellSize, m_randomness;
  QLabel m_imgCount, m_parCount, m_image;
  QPushButton m_add, m_load, m_toggle;
  MosaicGenerator m_gen;
  Q_SIGNAL void newSource(const QImage &);
  void updateImage() {
    const QImage & img = m_showSource ? m_source : m_mosaic;
    m_image.setPixmap(QPixmap::fromImage(img));
  }
public:
  Window(ImageDatabase * db, QWidget * parent = 0) : QWidget(parent),
    m_showSource(true), m_layout(QBoxLayout::TopToBottom, this),
    m_parallelism(Qt::Horizontal), m_cellSize(Qt::Horizontal),
    m_randomness(Qt::Horizontal), m_add("Fetch Images"),
    m_load("Open for Mosaic"), m_toggle("Toggle Mosaic"), m_gen(db)
  {
    QBoxLayout * row = new QBoxLayout(QBoxLayout::LeftToRight);
    row->addWidget(new QLabel("Images in DB:"));
    row->addWidget(&m_imgCount);
    row->addWidget(new QLabel("Fetch parallelism:"));
    row->addWidget(&m_parallelism);
    row->addWidget(&m_parCount);
    row->addWidget(&m_add);
    m_parallelism.setRange(1, 100);
    m_layout.addLayout(row);
    m_layout.addWidget(&m_image);
    row = new QBoxLayout(QBoxLayout::LeftToRight);
    row->addWidget(new QLabel("Cell Size:"));
    row->addWidget(&m_cellSize);
    row->addWidget(new QLabel("Randomness:"));
    row->addWidget(&m_randomness);
    m_cellSize.setRange(4, 64); m_cellSize.setTracking(false);
    m_randomness.setRange(0,6); m_randomness.setTracking(false);
    m_layout.addLayout(row);
    row = new QBoxLayout(QBoxLayout::LeftToRight);
    row->addWidget(&m_load);
    row->addWidget(&m_toggle);
    m_layout.addLayout(row);
    m_add.setCheckable(true);
    m_parCount.connect(&m_parallelism, SIGNAL(valueChanged(int)), SLOT(setNum(int)));
    connect(&m_add, SIGNAL(clicked(bool)), SIGNAL(reqAutoFetch(bool)));
    connect(&m_parallelism, SIGNAL(valueChanged(int)), SIGNAL(reqParallelism(int)));
    m_gen.connect(&m_cellSize, SIGNAL(valueChanged(int)), SLOT(setSize(int)));
    m_gen.connect(&m_randomness, SIGNAL(valueChanged(int)), SLOT(setRandLog2(int)));
    m_parallelism.setValue(20);
    m_cellSize.setValue(16);
    m_randomness.setValue(4);
    connect(&m_load, &QPushButton::clicked, [this]{
      QString file = QFileDialog::getOpenFileName(this);
      QtConcurrent::run([this, file]{
        QImage img; if (!img.load(file)) return;
        emit newSource(img);
      });
    });
    connect(this, &Window::newSource, [this](const QImage &img){
      m_source = m_mosaic = img; updateImage(); m_gen.setImage(m_source);
    });
    connect(&m_gen, &MosaicGenerator::hasMosaic, [this](const QImage &img){
      m_mosaic = img; updateImage();
    });
    connect(&m_toggle, &QPushButton::clicked, [this]{
      m_showSource = !m_showSource; updateImage();
    });
  }
  Q_SLOT void setImageCount(int n) { m_imgCount.setNum(n); }
  Q_SIGNAL void reqAutoFetch(bool);
  Q_SIGNAL void reqParallelism(int);
};

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  a.setOrganizationDomain("stackoverflow.com");
  a.setApplicationName("so-photomosaic");
  RandomImageSource src;
  ImageDatabase db;
  ImageStorage stg;
  Window ui(&db);
  db.connect(&src, SIGNAL(rspImage(QImage)), SLOT(addImage(QImage)));
  stg.connect(&src, SIGNAL(rspImage(QImage)), SLOT(addImage(QImage)));
  db.connect(&stg, SIGNAL(retrieved(QImage)), SLOT(addImage(QImage)));
  ui.connect(&db, SIGNAL(newImageCount(int)), SLOT(setImageCount(int)));
  src.connect(&ui, SIGNAL(reqAutoFetch(bool)), SLOT(setAutomatic(bool)));
  src.connect(&ui, SIGNAL(reqParallelism(int)), SLOT(setParallelism(int)));
  stg.retrieveAll();
  ui.show();
  return a.exec();
}

#include "main.moc"

這篇關于使用 C++ 用 Qt 制作我自己的照片馬賽克應用程序的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

How can I read and manipulate CSV file data in C++?(如何在 C++ 中讀取和操作 CSV 文件數據?)
In C++ why can#39;t I write a for() loop like this: for( int i = 1, double i2 = 0; (在 C++ 中,為什么我不能像這樣編寫 for() 循環: for( int i = 1, double i2 = 0;)
How does OpenMP handle nested loops?(OpenMP 如何處理嵌套循環?)
Reusing thread in loop c++(在循環 C++ 中重用線程)
Precise thread sleep needed. Max 1ms error(需要精確的線程睡眠.最大 1ms 誤差)
Is there ever a need for a quot;do {...} while ( )quot; loop?(是否需要“do {...} while ()?環形?)
主站蜘蛛池模板: 注塑机-压铸机-塑料注塑机-卧式注塑机-高速注塑机-单缸注塑机厂家-广东联升精密智能装备科技有限公司 | 台湾HIWIN上银直线模组|导轨滑块|TBI滚珠丝杆丝杠-深圳汉工 | 99文库_实习生实用的范文资料文库站 | 不锈钢轴流风机,不锈钢电机-许昌光维防爆电机有限公司(原许昌光维特种电机技术有限公司) | 挨踢网-大家的导航!| 托利多电子平台秤-高精度接线盒-托利多高精度电子秤|百科 | 杭州网络公司_百度SEO优化-外贸网络推广_抖音小程序开发-杭州乐软科技有限公司 | 江苏全风,高压风机,全风环保风机,全风环形高压风机,防爆高压风机厂家-江苏全风环保科技有限公司(官网) | 广州展台特装搭建商|特装展位设计搭建|展会特装搭建|特装展台制作设计|展览特装公司 | 陶氏道康宁消泡剂_瓦克消泡剂_蓝星_海明斯德谦_广百进口消泡剂 | 庭院灯_太阳能景观灯_草坪灯厂家_仿古壁灯-重庆恒投科技 | 挤塑板-XPS挤塑板-挤塑板设备厂家[襄阳欧格] | 帽子厂家_帽子工厂_帽子定做_义乌帽厂_帽厂_制帽厂 | 电动手术床,医用护理床,led手术无影灯-曲阜明辉医疗设备有限公司 | 鹤壁创新仪器公司-全自动量热仪,定硫仪,煤炭测硫仪,灰熔点测定仪,快速自动测氢仪,工业分析仪,煤质化验仪器 | 爱德华真空泵油/罗茨泵维修,爱发科-比其尔产品供应东莞/杭州/上海等全国各地 | 波纹补偿器_不锈钢波纹补偿器_巩义市润达管道设备制造有限公司 | 工业淬火油烟净化器,北京油烟净化器厂家,热处理油烟净化器-北京众鑫百科 | 动力配电箱-不锈钢配电箱-高压开关柜-重庆宇轩机电设备有限公司 聚天冬氨酸,亚氨基二琥珀酸四钠,PASP,IDS - 远联化工 | 鄂泉泵业官网|(杭州、上海、全国畅销)大流量防汛排涝泵-LW立式排污泵 | 郑州宣传片拍摄-TVC广告片拍摄-微电影短视频制作-河南优柿文化传媒有限公司 | 砂石生产线_石料生产线设备_制砂生产线设备价格_生产厂家-河南中誉鼎力智能装备有限公司 | 凝胶成像仪,化学发光凝胶成像系统,凝胶成像分析系统-上海培清科技有限公司 | 厂房出租-厂房规划-食品技术-厂房设计-厂房装修-建筑施工-设备供应-设备求购-龙爪豆食品行业平台 | 悬浮拼装地板_篮球场木地板翻新_运动木地板价格-上海越禾运动地板厂家 | 根系分析仪,大米外观品质检测仪,考种仪,藻类鉴定计数仪,叶面积仪,菌落计数仪,抑菌圈测量仪,抗生素效价测定仪,植物表型仪,冠层分析仪-杭州万深检测仪器网 | 非小号行情 - 专业的区块链、数字藏品行情APP、金色财经官网 | 千淘酒店差旅平台-中国第一家针对TMC行业的酒店资源供应平台 | 壹作文_中小学生优秀满分作文大全| 精密冲床,高速冲床等冲压设备生产商-常州晋志德压力机厂 | 智能楼宇-楼宇自控系统-楼宇智能化-楼宇自动化-三水智能化 | 环讯传媒,永康网络公司,永康网站建设,永康小程序开发制作,永康网站制作,武义网页设计,金华地区网站SEO优化推广 - 永康市环讯电子商务有限公司 | 四川成都干燥设备_回转筒干燥机_脉冲除尘器_输送设备_热风炉_成都川工星科机电设备有限公司 | 北京康百特科技有限公司-分子蒸馏-短程分子蒸馏设备-实验室分子蒸馏设备 | 高柔性拖链电缆-聚氨酯卷筒电缆-柔性屏蔽电缆厂家-玖泰电缆 | ★济南领跃标识制作公司★济南标识制作,标牌制作,山东标识制作,济南标牌厂 | 高精度电阻回路测试仪-回路直流电阻测试仪-武汉特高压电力科技有限公司 | 一路商机网-品牌招商加盟优选平台-加盟店排行榜平台 | 重庆网站建设,重庆网站设计,重庆网站制作,重庆seo,重庆做网站,重庆seo,重庆公众号运营,重庆小程序开发 | 岛津二手液相色谱仪,岛津10A液相,安捷伦二手液相,安捷伦1100液相-杭州森尼欧科学仪器有限公司 | 煤机配件厂家_刮板机配件_链轮轴组_河南双志机械设备有限公司 |