From 5ef6e320c76ae469770be913cf17021069027bb1 Mon Sep 17 00:00:00 2001 From: Javier Celaya Date: Sat, 17 May 2014 19:41:18 +0100 Subject: [PATCH] Several performance improvements with OpenMP. - Faster image load. - Faster image alignment. - Faster boxblur. - Faster result render. - Faster writing DNG tiles. --- DngFloatWriter.cpp | 6 +++--- EditableMask.cpp | 2 ++ Image.cpp | 4 ---- ImageStack.cpp | 52 +++++++++++++++++++++++++++++++++------------- Launcher.cpp | 15 ++++++++----- MainWindow.cpp | 11 +++++----- PreviewWidget.cpp | 3 +++ hdrmerge_es.ts | 8 +++++-- 8 files changed, 67 insertions(+), 34 deletions(-) diff --git a/DngFloatWriter.cpp b/DngFloatWriter.cpp index 38dd2e9..a960143 100644 --- a/DngFloatWriter.cpp +++ b/DngFloatWriter.cpp @@ -494,12 +494,12 @@ void DngFloatWriter::writeRawData() { int bytesps = bps >> 3; uLongf dstLen = tileWidth * tileLength * bytesps; -// #pragma omp parallel + #pragma omp parallel { Bytef cBuffer[dstLen]; Bytef uBuffer[dstLen]; -// #pragma omp for collapse(2) + #pragma omp for collapse(2) schedule(dynamic) for (size_t y = 0; y < height; y += tileLength) { for (size_t x = 0; x < width; x += tileWidth) { size_t t = (y / tileLength) * tilesAcross + (x / tileWidth); @@ -520,7 +520,7 @@ void DngFloatWriter::writeRawData() { if (err != Z_OK) { std::cerr << "DNG Deflate: Failed compressing tile " << t << ", with error " << err << std::endl; } else { -// #pragma omp critical + #pragma omp critical { tileOffsets[t] = file.tellp(); file.write((const char *)cBuffer, tileBytes[t]); diff --git a/EditableMask.cpp b/EditableMask.cpp index a53c7a1..63ebb3d 100644 --- a/EditableMask.cpp +++ b/EditableMask.cpp @@ -174,6 +174,7 @@ void EditableMask::BoxBlur::boxBlur_4(size_t radius) { void EditableMask::BoxBlur::boxBlurH_4(size_t r) { float iarr = 1.0 / (r+r+1); + #pragma omp parallel for schedule(dynamic) for (size_t i = 0; i < m.height; ++i) { size_t ti = i * m.width, li = ti, ri = ti + r; float val = map[li] * (r + 1); @@ -198,6 +199,7 @@ void EditableMask::BoxBlur::boxBlurH_4(size_t r) { void EditableMask::BoxBlur::boxBlurT_4(size_t r) { float iarr = 1.0 / (r+r+1); + #pragma omp parallel for schedule(dynamic) for (size_t i = 0; i < m.width; ++i) { size_t ti = i, li = ti, ri = ti + r*m.width; float val = map[li] * (r + 1); diff --git a/Image.cpp b/Image.cpp index ae0bcfd..b5672b5 100644 --- a/Image.cpp +++ b/Image.cpp @@ -175,10 +175,6 @@ void Image::alignWith(const Image & r) { dx <<= 1; dy <<= 1; } - dx += r.dx; - dy += r.dy; - alignedPixels = &rawPixels[-dy*width - dx]; - Log::msg(Log::DEBUG, "Image ", metaData->fileName, " displaced to (", dx, ", ", dy, ")"); } diff --git a/ImageStack.cpp b/ImageStack.cpp index 324262e..a020230 100644 --- a/ImageStack.cpp +++ b/ImageStack.cpp @@ -52,18 +52,35 @@ bool ImageStack::addImage(std::unique_ptr & i) { int ImageStack::load(const LoadOptions & options, ProgressIndicator & progress) { - int step = 100 / (options.fileNames.size() + 1); + int numImages = options.fileNames.size(); + int step = 100 / (numImages + 1); int p = -step; - for (auto & name : options.fileNames) { - progress.advance(p += step, "Loading %1", name.c_str()); - std::unique_ptr image(measureTime("Load raw", [&] () { return new Image(name.c_str()); })); - if (image.get() == nullptr || !image->good()) { - return 1; - } - if (!addImage(image)) { - return 2; + int error = 0, failedImage = 0; + { + Timer t("Load files"); + #pragma omp parallel for schedule(dynamic) + for (int i = 0; i < numImages; ++i) { + if (!error) { // We cannot break from the for loop if we are using OpenMP + string name = options.fileNames[i]; + #pragma omp critical + progress.advance(p += step, "Loading %1", name.c_str()); + std::unique_ptr image(new Image(name.c_str())); + #pragma omp critical + if (!error) { // Report on the first image that fails, ignore the rest + if (image.get() == nullptr || !image->good()) { + error = 1; + failedImage = i; + } else if (!addImage(image)) { + error = 2; + failedImage = i; + } + } + } } } + if (error) { + return (failedImage << 1) + error - 1; + } if (options.align) { Timer t("Align"); progress.advance(p += step, "Aligning"); @@ -75,7 +92,7 @@ int ImageStack::load(const LoadOptions & options, ProgressIndicator & progress) computeRelExposures(); mask.generateFrom(*this); progress.advance(100, "Done loading!"); - return 0; + return numImages << 1; } @@ -93,8 +110,12 @@ int ImageStack::save(const SaveOptions & options, ProgressIndicator & progress) void ImageStack::align() { if (images.size() > 1) { - for (auto cur = images.rbegin(), next = cur++; cur != images.rend(); next = cur++) { - (*cur)->alignWith(**next); + for (int i = images.size() - 2; i >= 0; --i) { + images[i]->alignWith(*images[i + 1]); + } + for (int i = images.size() - 2; i >= 0; --i) { + images[i]->displace(images[i + 1]->getDeltaX(), images[i + 1]->getDeltaY()); + Log::msg(Log::DEBUG, "Image ", i, " displaced to (", images[i]->getDeltaX(), ", ", images[i]->getDeltaY(), ")"); } for (auto & i : images) { i->releaseAlignData(); @@ -134,13 +155,14 @@ double ImageStack::value(size_t x, size_t y) const { void ImageStack::compose(float * dst) const { - Timer t("Compose"); unique_ptr map = measureTime("Blur", [&] () { return mask.blur(); }); + Timer t("Compose"); const MetaData & md = images.front()->getMetaData(); int imageMax = images.size() - 1; float max = 0.0; - for (size_t y = 0, pos = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x, ++pos) { + #pragma omp parallel for schedule(dynamic) + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0, pos = y*width; x < width; ++x, ++pos) { int j = map[pos] > imageMax ? imageMax : ceil(map[pos]); double v = 0.0, vv = 0.0, p; if (images[j]->contains(x, y)) { diff --git a/Launcher.cpp b/Launcher.cpp index c199403..e632c09 100644 --- a/Launcher.cpp +++ b/Launcher.cpp @@ -73,12 +73,17 @@ int Launcher::automaticMerge() { auto tr = [&] (const char * text) { return QCoreApplication::translate("LoadSave", text); }; CoutProgressIndicator progress; ImageStack stack; - if (stack.load(options, progress)) { - int i = progress.getPercent() * (options.fileNames.size() + 1) / 100; - if (i) { - cerr << tr("Error loading %1").arg(options.fileNames[i].c_str()) << endl; - return 1; + int numImages = options.fileNames.size(); + int result = stack.load(options, progress); + if (result < numImages * 2) { + int format = result & 1; + int i = result >> 1; + if (format) { + cerr << tr("Error loading %1, it has a different format.").arg(options.fileNames[i].c_str()) << endl; + } else { + cerr << tr("Error loading %1, file not found.").arg(options.fileNames[i].c_str()) << endl; } + return 1; } if (!wOptions.fileName.empty()) { size_t extPos = wOptions.fileName.find_last_of('.'); diff --git a/MainWindow.cpp b/MainWindow.cpp index 4c10941..c940734 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -273,11 +273,12 @@ void MainWindow::loadImages(const LoadOptions & options) { QFuture error = QtConcurrent::run([&] () { return newImages->load(options, progress); }); while (error.isRunning()) QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents); - if (error.result()) { - int i = progress.getPercent() * (numImages + 1) / 100; - QString message = error.result() == 1 ? - tr("Unable to open file %1.").arg(options.fileNames[i].c_str()) : - tr("File %1 has not the same format as the previous ones.").arg(options.fileNames[i].c_str()); + int result = error.result(); + if (result < numImages * 2) { + int i = result >> 1; + QString message = result & 1 ? + tr("File %1 has not the same format as the previous ones.").arg(options.fileNames[i].c_str()) : + tr("Unable to open file %1.").arg(options.fileNames[i].c_str()); QMessageBox::warning(this, tr("Error opening file"), message); delete newImages; return; diff --git a/PreviewWidget.cpp b/PreviewWidget.cpp index 8233959..7abb912 100644 --- a/PreviewWidget.cpp +++ b/PreviewWidget.cpp @@ -121,6 +121,7 @@ void PreviewWidget::render(int minx, int miny, int maxx, int maxy) { if (area.isEmpty()) return; area.getCoords(&minx, &miny, &maxx, &maxy); QImage image(area.width() - 1, area.height() - 1, QImage::Format_RGB32); + #pragma omp parallel for schedule(dynamic) for (int row = miny; row < maxy; row++) { QRgb * scanLine = reinterpret_cast(image.scanLine(row - miny)); for (int col = minx; col < maxx; col++) { @@ -188,6 +189,8 @@ void PreviewWidget::mousePressEvent(QMouseEvent * event) { } else event->ignore(); } + + void PreviewWidget::mouseMoveEvent(QMouseEvent * event) { if (addPixels || rmPixels) { event->accept(); diff --git a/hdrmerge_es.ts b/hdrmerge_es.ts index 5112466..4f364a2 100644 --- a/hdrmerge_es.ts +++ b/hdrmerge_es.ts @@ -75,8 +75,12 @@ LoadSave - Error loading %1 - Error cargando %1 + Error loading %1, it has a different format. + Error cargando %1, tiene un formato diferente. + + + Error loading %1, file not found. + Error cargando %1, no se encontrĂ³ el fichero. Writing result to %1