使用C ++使用Qt制作自己的照片马赛克应用程序

萨希尔·萨琳

这是我到目前为止所做的输出:
在此处输入图片说明

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).

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.

Kuba hasn't forgotten Monica

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. 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.

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

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.

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.

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行:)

截屏

# 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"

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用C ++使用Qt制作自己的照片马赛克应用程序

来自分类Dev

如何使用ffmpeg输出屏幕截图库/马赛克?

来自分类Dev

如何使用ffmpeg输出屏幕截图库/马赛克?

来自分类Dev

使用 numpy 数组列表创建马赛克的问题

来自分类Dev

使用数据库中的数据创建CSV文件,可得出“马赛克,混乱”的结果

来自分类Dev

使用标准评估为带有Ggmosaic的马赛克图创建函数

来自分类Dev

创建一个水晶(或瓷砖马赛克?)渐变背景,大概是使用画布(或 svg?)

来自分类Dev

如何阻止页面在移动设备上自动向上滚动?(使用Blogger动态视图“马赛克”模板)

来自分类Dev

使用Glade制作GtkHeaderBar应用程序

来自分类Dev

在Windows上使用Dev C ++编译Qt应用程序

来自分类Dev

在我的应用程序中使用相机拍摄的照片是颠倒的

来自分类Dev

应用程序不要求照片库使用许可

来自分类Dev

位图马赛克

来自分类Dev

使用QNetworkAccessManager的Qt Console应用程序

来自分类Dev

在Qt应用程序中使用dll

来自分类Dev

使用 Qt 的可扩展应用程序

来自分类Dev

每次加载应用程序时,如何使用照片库ios中的照片?

来自分类Dev

使用springSecurity制作Grails应用程序的索引UI

来自分类Dev

Laravel:使用Angular制作实时应用程序

来自分类Dev

如何关闭使用python制作的Chrome本机应用程序?

来自分类Dev

使用springSecurity制作Grails应用程序的索引UI

来自分类Dev

是否可以使用JavaScript制作GUI应用程序?

来自分类Dev

使用网络服务制作电话差距应用程序

来自分类Dev

如何使用Java制作Windows应用程序?

来自分类Dev

如何使用街景制作类似的应用程序?

来自分类Dev

如何制作gif/使用哪个应用程序?

来自分类Dev

C#如何使用SQL Server数据库制作Windows应用程序的独立版本?

来自分类Dev

如何使用C#制作Windows窗体应用程序以接收来自串行端口的数据

来自分类Dev

C#如何使用SQL Server数据库制作Windows应用程序的独立版本?

Related 相关文章

  1. 1

    使用C ++使用Qt制作自己的照片马赛克应用程序

  2. 2

    如何使用ffmpeg输出屏幕截图库/马赛克?

  3. 3

    如何使用ffmpeg输出屏幕截图库/马赛克?

  4. 4

    使用 numpy 数组列表创建马赛克的问题

  5. 5

    使用数据库中的数据创建CSV文件,可得出“马赛克,混乱”的结果

  6. 6

    使用标准评估为带有Ggmosaic的马赛克图创建函数

  7. 7

    创建一个水晶(或瓷砖马赛克?)渐变背景,大概是使用画布(或 svg?)

  8. 8

    如何阻止页面在移动设备上自动向上滚动?(使用Blogger动态视图“马赛克”模板)

  9. 9

    使用Glade制作GtkHeaderBar应用程序

  10. 10

    在Windows上使用Dev C ++编译Qt应用程序

  11. 11

    在我的应用程序中使用相机拍摄的照片是颠倒的

  12. 12

    应用程序不要求照片库使用许可

  13. 13

    位图马赛克

  14. 14

    使用QNetworkAccessManager的Qt Console应用程序

  15. 15

    在Qt应用程序中使用dll

  16. 16

    使用 Qt 的可扩展应用程序

  17. 17

    每次加载应用程序时,如何使用照片库ios中的照片?

  18. 18

    使用springSecurity制作Grails应用程序的索引UI

  19. 19

    Laravel:使用Angular制作实时应用程序

  20. 20

    如何关闭使用python制作的Chrome本机应用程序?

  21. 21

    使用springSecurity制作Grails应用程序的索引UI

  22. 22

    是否可以使用JavaScript制作GUI应用程序?

  23. 23

    使用网络服务制作电话差距应用程序

  24. 24

    如何使用Java制作Windows应用程序?

  25. 25

    如何使用街景制作类似的应用程序?

  26. 26

    如何制作gif/使用哪个应用程序?

  27. 27

    C#如何使用SQL Server数据库制作Windows应用程序的独立版本?

  28. 28

    如何使用C#制作Windows窗体应用程序以接收来自串行端口的数据

  29. 29

    C#如何使用SQL Server数据库制作Windows应用程序的独立版本?

热门标签

归档