1 /**
2  * This file is a part of Luminance HDR package
3  * ----------------------------------------------------------------------
4  * Copyright (C) 2006,2007 Giuseppe Rota
5  * Copyright (C) 2010-2012 Franco Comida
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  * ----------------------------------------------------------------------
21  *
22  * @author Giuseppe Rota <grota@users.sourceforge.net>
23  *
24  * Manual and auto antighosting, improvements, bugfixing
25  * @author Franco Comida <fcomida@users.sourceforge.net>
26  *
27  */
28 
29 #include "HdrCreationManager.h"
30 
31 #include <QApplication>
32 #include <QColor>
33 #include <QDebug>
34 #include <QFile>
35 #include <QFileInfo>
36 #include <QScopedPointer>
37 #include <QtConcurrentFilter>
38 #include <QtConcurrentMap>
39 
40 #include <algorithm>
41 #include <functional>
42 #include <boost/limits.hpp>
43 #include <boost/numeric/conversion/bounds.hpp>
44 #include <cmath>
45 #include <iostream>
46 #include <vector>
47 
48 #include <Common/CommonFunctions.h>
49 #include <Libpfs/colorspace/colorspace.h>
50 #include <Libpfs/colorspace/convert.h>
51 #include <Libpfs/colorspace/normalizer.h>
52 #include <Libpfs/frame.h>
53 #include <Libpfs/io/framereader.h>
54 #include <Libpfs/io/framereaderfactory.h>
55 #include <Libpfs/io/framewriter.h>
56 #include <Libpfs/io/framewriterfactory.h>
57 #include <Libpfs/io/tiffreader.h>
58 #include <Libpfs/io/tiffwriter.h>
59 #include <Libpfs/manip/copy.h>
60 #include <Libpfs/manip/cut.h>
61 #include <Libpfs/manip/shift.h>
62 #include <Libpfs/utils/msec_timer.h>
63 #include <Libpfs/utils/transform.h>
64 
65 #include <Exif/ExifOperations.h>
66 #include <HdrCreation/mtb_alignment.h>
67 #include <HdrWizard/WhiteBalance.h>
68 #include <TonemappingOperators/fattal02/pde.h>
69 #include <arch/math.h>
70 
71 using namespace std;
72 using namespace std::placeholders;
73 using namespace pfs;
74 using namespace pfs::io;
75 using namespace colorspace;
76 using namespace utils;
77 using namespace libhdr::fusion;
78 
79 const FusionOperatorConfig predef_confs[6] = {
80     {WEIGHT_TRIANGULAR, RESPONSE_LINEAR, DEBEVEC, QString(), QString()},
81     {WEIGHT_TRIANGULAR, RESPONSE_GAMMA, DEBEVEC, QString(), QString()},
82     {WEIGHT_PLATEAU, RESPONSE_LINEAR, DEBEVEC, QString(), QString()},
83     {WEIGHT_PLATEAU, RESPONSE_GAMMA, DEBEVEC, QString(), QString()},
84     {WEIGHT_GAUSSIAN, RESPONSE_LINEAR, DEBEVEC, QString(), QString()},
85     {WEIGHT_GAUSSIAN, RESPONSE_GAMMA, DEBEVEC, QString(), QString()},
86 };
87 
88 // --- NEW CODE ---
89 namespace {
90 
shiftQImage(const QImage * in,int dx,int dy)91 QImage *shiftQImage(const QImage *in, int dx, int dy) {
92     QImage *out = new QImage(in->size(), QImage::Format_ARGB32);
93     assert(out != NULL);
94     out->fill(qRgba(0, 0, 0, 0));  // transparent black
95     for (int i = 0; i < in->height(); i++) {
96         if ((i + dy) < 0) continue;
97         if ((i + dy) >= in->height()) break;
98         QRgb *inp = (QRgb *)in->scanLine(i);
99         QRgb *outp = (QRgb *)out->scanLine(i + dy);
100         for (int j = 0; j < in->width(); j++) {
101             if ((j + dx) >= in->width()) break;
102             if ((j + dx) >= 0) outp[j + dx] = *inp;
103             inp++;
104         }
105     }
106     return out;
107 }
108 
shiftItem(HdrCreationItem & item,int dx,int dy)109 void shiftItem(HdrCreationItem &item, int dx, int dy) {
110     FramePtr shiftedFrame(pfs::shift(*item.frame(), dx, dy));
111     item.frame().swap(shiftedFrame);
112     shiftedFrame.reset();  // release memory
113 
114     QScopedPointer<QImage> img(shiftQImage(&item.qimage(), dx, dy));
115     item.qimage().swap(*img);
116     img.reset();  // release memory
117 }
118 }
119 
checkFileName(const HdrCreationItem & item,const QString & str)120 static bool checkFileName(const HdrCreationItem &item, const QString &str) {
121     return (item.filename().compare(str) == 0);
122 }
123 
loadFiles(const QStringList & filenames)124 void HdrCreationManager::loadFiles(const QStringList &filenames) {
125     for (const auto &filename : filenames) {
126         qDebug() << QStringLiteral(
127                         "HdrCreationManager::loadFiles(): Checking %1")
128                         .arg(filename);
129         HdrCreationItemContainer::iterator it =
130             find_if(m_data.begin(), m_data.end(),
131                     std::bind(&checkFileName, _1, filename));
132         // has the file been inserted already?
133         if (it == m_data.end()) {
134             qDebug() << QStringLiteral(
135                             "HdrCreationManager::loadFiles(): \
136                             Schedule loading for %1")
137                             .arg(filename);
138             m_tmpdata.push_back(HdrCreationItem(filename));
139         } else {
140             qDebug() << QStringLiteral(
141                             "HdrCreationManager::loadFiles(): %1 \
142                             has already been loaded")
143                             .arg(filename);
144         }
145     }
146 
147     // parallel load of the data...
148     connect(&m_futureWatcher, &QFutureWatcherBase::finished, this,
149             &HdrCreationManager::loadFilesDone, Qt::DirectConnection);
150 
151     // Start the computation.
152     m_futureWatcher.setFuture(
153         QtConcurrent::map(m_tmpdata.begin(), m_tmpdata.end(), LoadFile()));
154 }
155 
loadFilesDone()156 void HdrCreationManager::loadFilesDone() {
157     qDebug() << "HdrCreationManager::loadFilesDone(): Data loaded ... move to \
158                 internal structure!";
159     if (m_futureWatcher.isCanceled())  // LoadFile() threw an exception
160     {
161         emit errorWhileLoading(
162             tr("HdrCreationManager::loadFilesDone(): Error loading a file."));
163         disconnect(&m_futureWatcher, &QFutureWatcherBase::finished, this,
164                    &HdrCreationManager::loadFilesDone);
165         m_tmpdata.clear();
166         return;
167     }
168 
169     if (isLoadResponseCurve()) {
170         try {
171             m_response->readFromFile(
172                 QFile::encodeName(getResponseCurveInputFilename()).constData());
173             setLoadResponseCurve(false);
174         } catch (std::runtime_error &e) {
175             emit errorWhileLoading(QString(e.what()));
176         }
177     }
178     disconnect(&m_futureWatcher, &QFutureWatcherBase::finished, this,
179                &HdrCreationManager::loadFilesDone);
180     for (const auto &hdrCreationItem : m_tmpdata) {
181         if (hdrCreationItem.isValid()) {
182             qDebug() << QStringLiteral(
183                             "HdrCreationManager::loadFilesDone(): \
184                             Insert data for %1")
185                             .arg(hdrCreationItem.filename());
186             m_data.push_back(hdrCreationItem);
187         }
188     }
189     m_tmpdata.clear();
190 
191     refreshEVOffset();
192 
193     if (!framesHaveSameSize()) {
194         m_data.clear();
195         emit errorWhileLoading(
196             tr("HdrCreationManager::loadFilesDone(): The images have different "
197                "size."));
198     } else {
199         emit finishedLoadingFiles();
200     }
201 }
202 
refreshEVOffset()203 void HdrCreationManager::refreshEVOffset() {
204     // no data
205     if (m_data.size() <= 0) {
206         m_evOffset = 0.f;
207         return;
208     }
209 
210     std::vector<float> evs;
211     for (const auto &hdrCreationItem : m_data) {
212         if (hdrCreationItem.hasEV()) {
213             evs.push_back(hdrCreationItem.getEV());
214         }
215     }
216 
217     // no image has EV
218     if (evs.size() <= 0) {
219         m_evOffset = 0.f;
220         return;
221     }
222 
223     // only one image available
224     if (evs.size() == 1) {
225         m_evOffset = evs[0];
226         return;
227     }
228 
229     // sort...
230     std::sort(evs.begin(), evs.end());
231     m_evOffset = evs[(evs.size() + 1) / 2 - 1];
232 
233     qDebug() << QStringLiteral(
234                     "HdrCreationManager::refreshEVOffset(): offset = %1")
235                     .arg(m_evOffset);
236 }
237 
getEVOffset() const238 float HdrCreationManager::getEVOffset() const { return m_evOffset; }
239 
getFilesWithoutExif() const240 QStringList HdrCreationManager::getFilesWithoutExif() const {
241     QStringList invalidFiles;
242     foreach (const HdrCreationItem &fileData, m_data) {
243         if (!fileData.hasAverageLuminance()) {
244             invalidFiles.push_back(fileData.filename());
245         }
246     }
247     return invalidFiles;
248 }
249 
numFilesWithoutExif() const250 size_t HdrCreationManager::numFilesWithoutExif() const {
251     size_t counter = 0;
252     foreach (const HdrCreationItem &fileData, m_data) {
253         if (!fileData.hasAverageLuminance()) {
254             ++counter;
255         }
256     }
257     return counter;
258 }
259 
removeFile(int idx)260 void HdrCreationManager::removeFile(int idx) {
261     Q_ASSERT(idx >= 0);
262     Q_ASSERT(idx < (int)m_data.size());
263 
264     m_data.erase(m_data.begin() + idx);
265 
266     refreshEVOffset();
267 }
268 
HdrCreationManager(bool fromCommandLine)269 HdrCreationManager::HdrCreationManager(bool fromCommandLine)
270     : m_evOffset(0.f),
271       m_response(new ResponseCurve(predef_confs[0].responseCurve)),
272       m_weight(new WeightFunction(predef_confs[0].weightFunction)),
273       m_responseCurveInputFilename(),
274       m_agMask(NULL),
275       m_align(),
276       m_ais_crop_flag(false),
277       fromCommandLine(fromCommandLine),
278       m_isLoadResponseCurve(false) {
279     // setConfig(predef_confs[0]);
280     setFusionOperator(predef_confs[0].fusionOperator);
281 
282     for (int i = 0; i < agGridSize; i++) {
283         for (int j = 0; j < agGridSize; j++) {
284             m_patches[i][j] = false;
285         }
286     }
287 
288     connect(&m_futureWatcher, &QFutureWatcherBase::started, this,
289             &HdrCreationManager::progressStarted, Qt::DirectConnection);
290     connect(&m_futureWatcher, &QFutureWatcherBase::finished, this,
291             &HdrCreationManager::progressFinished, Qt::DirectConnection);
292     connect(this, &HdrCreationManager::progressCancel, &m_futureWatcher,
293             &QFutureWatcherBase::cancel, Qt::DirectConnection);
294     connect(&m_futureWatcher, &QFutureWatcherBase::progressRangeChanged, this,
295             &HdrCreationManager::progressRangeChanged, Qt::DirectConnection);
296     connect(&m_futureWatcher, &QFutureWatcherBase::progressValueChanged, this,
297             &HdrCreationManager::progressValueChanged, Qt::DirectConnection);
298 }
299 
setConfig(const FusionOperatorConfig & c)300 void HdrCreationManager::setConfig(const FusionOperatorConfig &c) {
301     if (!c.inputResponseCurveFilename.isEmpty()) {
302         setLoadResponseCurve(true);
303         setResponseCurveInputFilename(c.inputResponseCurveFilename);
304     } else {
305         m_response->setType(c.responseCurve);
306     }
307     if (!c.outputResponseCurveFilename.isEmpty()) {
308         setSaveResponseCurve(true);
309         setResponseCurveOutputFilename(c.outputResponseCurveFilename);
310     } else {
311         m_response->setType(c.responseCurve);
312     }
313     getWeightFunction().setType(c.weightFunction);
314     setFusionOperator(c.fusionOperator);
315 }
316 
getExpotimes() const317 QVector<float> HdrCreationManager::getExpotimes() const {
318     QVector<float> expotimes;
319     for (HdrCreationItemContainer::const_iterator it = m_data.begin(),
320                                                   itEnd = m_data.end();
321          it != itEnd; ++it) {
322         expotimes.push_back(it->getEV());
323     }
324     return expotimes;
325 }
326 
framesHaveSameSize()327 bool HdrCreationManager::framesHaveSameSize() {
328     size_t width = m_data[0].frame()->getWidth();
329     size_t height = m_data[0].frame()->getHeight();
330     for (HdrCreationItemContainer::const_iterator it = m_data.begin() + 1,
331                                                   itEnd = m_data.end();
332          it != itEnd; ++it) {
333         if (it->frame()->getWidth() != width ||
334             it->frame()->getHeight() != height)
335             return false;
336     }
337     return true;
338 }
339 
align_with_mtb()340 void HdrCreationManager::align_with_mtb() {
341     // build temporary container...
342     vector<FramePtr> frames;
343     for (size_t i = 0; i < m_data.size(); ++i) {
344         frames.push_back(m_data[i].frame());
345     }
346 
347     // run MTB
348     libhdr::mtb_alignment(frames);
349 
350     // rebuild previews
351     QFutureWatcher<void> futureWatcher;
352     futureWatcher.setFuture(
353         QtConcurrent::map(m_data.begin(), m_data.end(), RefreshPreview()));
354     futureWatcher.waitForFinished();
355 
356     // emit finished
357     emit finishedAligning(0);
358 }
359 
set_ais_crop_flag(bool flag)360 void HdrCreationManager::set_ais_crop_flag(bool flag) {
361     m_ais_crop_flag = flag;
362 }
363 
align_with_ais()364 void HdrCreationManager::align_with_ais() {
365     m_align.reset(new Align(m_data, fromCommandLine, 1));
366     connect(m_align.get(), &Align::finishedAligning, this,
367             &HdrCreationManager::finishedAligning);
368     connect(m_align.get(), &Align::failedAligning, this,
369             &HdrCreationManager::ais_failed);
370     connect(m_align.get(), &Align::failedAligning, this,
371             &HdrCreationManager::ais_failed_slot);
372     connect(m_align.get(), &Align::dataReady, this,
373             &HdrCreationManager::aisDataReady);
374 
375     m_align->align_with_ais(m_ais_crop_flag);
376 }
377 
ais_failed_slot(QProcess::ProcessError error)378 void HdrCreationManager::ais_failed_slot(QProcess::ProcessError error) {
379     qDebug() << "align_image_stack failed";
380     m_align->removeTempFiles();
381 }
382 
createHdr()383 pfs::Frame *HdrCreationManager::createHdr() {
384     std::vector<FrameEnhanced> frames;
385 
386     for (size_t idx = 0; idx < m_data.size(); ++idx) {
387         frames.push_back(
388             FrameEnhanced(m_data[idx].frame(),
389                           std::pow(2.f, m_data[idx].getEV() - m_evOffset)));
390     }
391 
392     libhdr::fusion::FusionOperatorPtr fusionOperatorPtr =
393         IFusionOperator::build(m_fusionOperator);
394     pfs::Frame *outputFrame(
395         fusionOperatorPtr->computeFusion(*m_response, *m_weight, frames));
396 
397     if (!m_responseCurveOutputFilename.isEmpty()) {
398         m_response->writeToFile(
399             QFile::encodeName(m_responseCurveOutputFilename).constData());
400     }
401 
402     return outputFrame;
403 }
404 
applyShiftsToItems(const QList<QPair<int,int>> & hvOffsets)405 void HdrCreationManager::applyShiftsToItems(
406     const QList<QPair<int, int>> &hvOffsets) {
407     int size = m_data.size();
408     // shift the frames and images
409     for (int i = 0; i < size; i++) {
410         if (hvOffsets[i].first == hvOffsets[i].second &&
411             hvOffsets[i].first == 0) {
412             continue;
413         }
414         shiftItem(m_data[i], hvOffsets[i].first, hvOffsets[i].second);
415     }
416 }
417 
cropItems(const QRect & ca)418 void HdrCreationManager::cropItems(const QRect &ca) {
419     // crop all frames and images
420     int size = m_data.size();
421     for (int idx = 0; idx < size; idx++) {
422         std::unique_ptr<QImage> newimage(
423             new QImage(m_data[idx].qimage().copy(ca)));
424         if (newimage == NULL) {
425             exit(1);  // TODO: exit gracefully
426         }
427         m_data[idx].qimage().swap(*newimage);
428         newimage.reset();
429 
430         int x_ul, y_ur, x_bl, y_br;
431         ca.getCoords(&x_ul, &y_ur, &x_bl, &y_br);
432 
433         FramePtr cropped(
434             cut(m_data[idx].frame().get(), static_cast<size_t>(x_ul),
435                 static_cast<size_t>(y_ur), static_cast<size_t>(x_bl),
436                 static_cast<size_t>(y_br)));
437         m_data[idx].frame().swap(cropped);
438         cropped.reset();
439     }
440 }
441 
~HdrCreationManager()442 HdrCreationManager::~HdrCreationManager() {
443     this->reset();
444     delete m_agMask;
445 }
446 
saveImages(const QString & prefix)447 void HdrCreationManager::saveImages(const QString &prefix) {
448     int idx = 0;
449     for (HdrCreationItemContainer::const_iterator it = m_data.begin(),
450                                                   itEnd = m_data.end();
451          it != itEnd; ++it) {
452         QString filename = prefix + QStringLiteral("_%1").arg(idx) + ".tiff";
453         pfs::io::TiffWriter writer(QFile::encodeName(filename).constData());
454         writer.write(*it->frame(), pfs::Params("tiff_mode", 1));
455 
456         QFileInfo qfi(filename);
457         QString absoluteFileName = qfi.absoluteFilePath();
458         QByteArray encodedName = QFile::encodeName(absoluteFileName);
459         ExifOperations::copyExifData(
460             QFile::encodeName(it->filename()).constData(),
461             encodedName.constData(), false);
462         ++idx;
463     }
464     emit imagesSaved();
465 }
466 
computePatches(float threshold,bool patches[][agGridSize],float & percent,QList<QPair<int,int>> HV_offset)467 int HdrCreationManager::computePatches(float threshold,
468                                        bool patches[][agGridSize],
469                                        float &percent,
470                                        QList<QPair<int, int>> HV_offset) {
471     qDebug() << "HdrCreationManager::computePatches";
472     qDebug() << threshold;
473 #ifdef TIMER_PROFILING
474     msec_timer stop_watch;
475     stop_watch.start();
476 #endif
477     const int width = m_data[0].frame()->getWidth();
478     const int height = m_data[0].frame()->getHeight();
479     const int gridX = width / agGridSize;
480     const int gridY = height / agGridSize;
481     const int size = m_data.size();
482     assert(size >= 2);
483 
484     vector<float> HE(size);
485 
486     hueSquaredMean(m_data, HE);
487 
488     m_agGoodImageIndex = findIndex(HE.data(), size);
489     qDebug() << "h0: " << m_agGoodImageIndex;
490 
491     for (int j = 0; j < agGridSize; j++) {
492         for (int i = 0; i < agGridSize; i++) {
493             m_patches[i][j] = false;
494         }
495     }
496 
497     for (int h = 0; h < size; h++) {
498         if (h == m_agGoodImageIndex) continue;
499         float deltaEV = log2(m_data[m_agGoodImageIndex].getAverageLuminance()) -
500                         log2(m_data[h].getAverageLuminance());
501         int dx = HV_offset[m_agGoodImageIndex].first - HV_offset[h].first;
502         int dy = HV_offset[m_agGoodImageIndex].second - HV_offset[h].second;
503         float sR, sG, sB;
504         sdv(m_data[m_agGoodImageIndex], m_data[h], deltaEV, dx, dy, sR, sG, sB);
505         //#pragma omp parallel for schedule(static)
506         for (int j = 0; j < agGridSize; j++) {
507             for (int i = 0; i < agGridSize; i++) {
508                 if (comparePatches(m_data[m_agGoodImageIndex], m_data[h], i, j,
509                                    gridX, gridY, threshold, sR, sG, sB, deltaEV,
510                                    dx, dy)) {
511                     m_patches[i][j] = true;
512                 }
513             }
514         }
515     }
516 
517     int count = 0;
518     for (int i = 0; i < agGridSize; i++)
519         for (int j = 0; j < agGridSize; j++)
520             if (m_patches[i][j] == true) count++;
521     percent = static_cast<float>(count) /
522               static_cast<float>(agGridSize * agGridSize) * 100.0f;
523     qDebug() << "Total patches: " << percent << "%";
524 
525     memcpy(patches, m_patches, agGridSize * agGridSize);
526 
527 #ifdef TIMER_PROFILING
528     stop_watch.stop_and_update();
529     std::cout << "computePatches = " << stop_watch.get_time() << " msec"
530               << std::endl;
531 #endif
532     return m_agGoodImageIndex;
533 }
534 
doAntiGhosting(bool patches[][agGridSize],int h0,bool manualAg,ProgressHelper * ph)535 pfs::Frame *HdrCreationManager::doAntiGhosting(bool patches[][agGridSize],
536                                                int h0, bool manualAg,
537                                                ProgressHelper *ph) {
538     qDebug() << "HdrCreationManager::doAntiGhosting";
539 #ifdef TIMER_PROFILING
540     msec_timer stop_watch;
541     stop_watch.start();
542 #endif
543     const int width = m_data[0].frame()->getWidth();
544     const int height = m_data[0].frame()->getHeight();
545     const int gridX = width / agGridSize;
546     const int gridY = height / agGridSize;
547     connect(ph, &ProgressHelper::qtSetRange, this,
548             &HdrCreationManager::progressRangeChanged);
549     connect(ph, &ProgressHelper::qtSetValue, this,
550             &HdrCreationManager::progressValueChanged);
551     ph->setRange(0, 100);
552     ph->setValue(0);
553     emit progressStarted();
554 
555     float cmax[3];
556     float cmin[3];
557     float Max, Min;
558 
559     const Channel *Good_Rc, *Good_Gc, *Good_Bc;
560     Channel *Ch_Good[3];
561     m_data[h0].frame().get()->getXYZChannels(Ch_Good[0], Ch_Good[1],
562                                              Ch_Good[2]);
563 
564     Good_Rc = Ch_Good[0];
565     Good_Gc = Ch_Good[1];
566     Good_Bc = Ch_Good[2];
567 
568     const Channel *Rc, *Gc, *Bc;
569     Channel *Ch[3];
570     std::unique_ptr<Frame> ghosted(createHdr());
571     ghosted->getXYZChannels(Ch[0], Ch[1], Ch[2]);
572 
573     for (int c = 0; c < 3; c++) {
574         cmax[c] = *max_element(Ch[c]->begin(), Ch[c]->end());
575         cmin[c] = *min_element(Ch[c]->begin(), Ch[c]->end());
576     }
577     Max = std::max(cmax[0], std::max(cmax[1], cmax[2]));
578     Min = std::min(cmin[0], std::min(cmin[1], cmin[2]));
579 
580     for (int c = 0; c < 3; c++) {
581         replace_if(Ch[c]->begin(), Ch[c]->end(),
582                    [](float f) { return !isnormal(f); }, Max);
583         replace_if(Ch[c]->begin(), Ch[c]->end(),
584                    [](float f) { return !isfinite(f); }, Max);
585         transform(Ch[c]->begin(), Ch[c]->end(), Ch[c]->begin(),
586                   Normalizer(Min, Max));
587     }
588 
589     Rc = Ch[0];
590     Gc = Ch[1];
591     Bc = Ch[2];
592 
593     ph->setValue(5);
594     if (ph->canceled()) return NULL;
595 
596     //Red channel
597     std::unique_ptr<Array2Df> logIrradianceGood_R(new Array2Df(width, height));
598     computeLogIrradiance(*logIrradianceGood_R, *Good_Rc);
599     ph->setValue(10);
600     if (ph->canceled()) {
601         return NULL;
602     }
603     std::unique_ptr<Array2Df> logIrradiance_R(new Array2Df(width, height));
604     computeLogIrradiance(*logIrradiance_R, *Rc);
605     ph->setValue(13);
606     if (ph->canceled()) {
607         return NULL;
608     }
609     std::unique_ptr<Array2Df> gradientXGood_R(new Array2Df(width, height));
610     std::unique_ptr<Array2Df> gradientYGood_R(new Array2Df(width, height));
611     std::unique_ptr<Array2Df> gradientX_R(new Array2Df(width, height));
612     std::unique_ptr<Array2Df> gradientY_R(new Array2Df(width, height));
613     std::unique_ptr<Array2Df> gradientXBlended_R(new Array2Df(width, height));
614     std::unique_ptr<Array2Df> gradientYBlended_R(new Array2Df(width, height));
615 
616     computeGradient(*gradientXGood_R, *gradientYGood_R, *logIrradianceGood_R);
617     ph->setValue(15);
618     if (ph->canceled()) {
619         return NULL;
620     }
621     computeGradient(*gradientX_R, *gradientY_R, *logIrradiance_R);
622     ph->setValue(20);
623     if (ph->canceled()) {
624         return NULL;
625     }
626     if (manualAg)
627         blendGradients(*gradientXBlended_R, *gradientYBlended_R, *gradientX_R,
628                        *gradientY_R, *gradientXGood_R, *gradientYGood_R,
629                        *m_agMask);
630     else
631         blendGradients(*gradientXBlended_R, *gradientYBlended_R, *gradientX_R,
632                        *gradientY_R, *gradientXGood_R, *gradientYGood_R,
633                        patches, gridX, gridY);
634 
635     ph->setValue(25);
636     if (ph->canceled()) {
637         return NULL;
638     }
639 
640     std::unique_ptr<Array2Df> divergence_R(new Array2Df(width, height));
641     computeDivergence(*divergence_R, *gradientXBlended_R, *gradientYBlended_R);
642     ph->setValue(28);
643     if (ph->canceled()) {
644         return NULL;
645     }
646 
647     qDebug() << "solve_pde";
648     solve_pde_dct(*divergence_R, *logIrradiance_R);
649     ph->setValue(33);
650     if (ph->canceled()) {
651         return NULL;
652     }
653 
654     //Green channel
655     std::unique_ptr<Array2Df> logIrradianceGood_G = std::move(logIrradianceGood_R); // reuse of logIrradianceGood_R
656     computeLogIrradiance(*logIrradianceGood_G, *Good_Gc);
657     ph->setValue(36);
658     if (ph->canceled()) {
659         return NULL;
660     }
661     std::unique_ptr<Array2Df> logIrradiance_G(new Array2Df(width, height));
662     computeLogIrradiance(*logIrradiance_G, *Gc);
663     ph->setValue(38);
664     if (ph->canceled()) {
665         return NULL;
666     }
667     std::unique_ptr<Array2Df> gradientXGood_G = std::move(gradientXGood_R);
668     std::unique_ptr<Array2Df> gradientYGood_G = std::move(gradientYGood_R);
669     std::unique_ptr<Array2Df> gradientX_G = std::move(gradientX_R);
670     std::unique_ptr<Array2Df> gradientY_G = std::move(gradientY_R);
671     std::unique_ptr<Array2Df> gradientXBlended_G = std::move(gradientXBlended_R);
672     std::unique_ptr<Array2Df> gradientYBlended_G = std::move(gradientYBlended_R);
673 
674     computeGradient(*gradientXGood_G, *gradientYGood_G, *logIrradianceGood_G);
675     ph->setValue(43);
676     if (ph->canceled()) {
677         return NULL;
678     }
679     computeGradient(*gradientX_G, *gradientY_G, *logIrradiance_G);
680     ph->setValue(48);
681     if (ph->canceled()) {
682         return NULL;
683     }
684     if (manualAg)
685         blendGradients(*gradientXBlended_G, *gradientYBlended_G, *gradientX_G,
686                        *gradientY_G, *gradientXGood_G, *gradientYGood_G,
687                        *m_agMask);
688     else
689         blendGradients(*gradientXBlended_G, *gradientYBlended_G, *gradientX_G,
690                        *gradientY_G, *gradientXGood_G, *gradientYGood_G,
691                        patches, gridX, gridY);
692 
693     ph->setValue(53);
694     if (ph->canceled()) {
695         return NULL;
696     }
697 
698     std::unique_ptr<Array2Df> divergence_G(new Array2Df(width, height));
699     computeDivergence(*divergence_G, *gradientXBlended_G, *gradientYBlended_G);
700     ph->setValue(60);
701     if (ph->canceled()) {
702         return NULL;
703     }
704 
705     qDebug() << "solve_pde";
706     solve_pde_dct(*divergence_G, *logIrradiance_G);
707     ph->setValue(66);
708     if (ph->canceled()) {
709         return NULL;
710     }
711 
712     //Blue channel
713     std::unique_ptr<Array2Df> logIrradianceGood_B = std::move(logIrradianceGood_G); // reuse of logIrradianceGood_R
714     computeLogIrradiance(*logIrradianceGood_B, *Good_Bc);
715     ph->setValue(71);
716     if (ph->canceled()) {
717         return NULL;
718     }
719     std::unique_ptr<Array2Df> logIrradiance_B(new Array2Df(width, height));
720     computeLogIrradiance(*logIrradiance_B, *Bc);
721     ph->setValue(76);
722     if (ph->canceled()) {
723         return NULL;
724     }
725     std::unique_ptr<Array2Df> gradientXGood_B = std::move(gradientXGood_G);
726     std::unique_ptr<Array2Df> gradientYGood_B = std::move(gradientYGood_G);
727     std::unique_ptr<Array2Df> gradientX_B = std::move(gradientX_G);
728     std::unique_ptr<Array2Df> gradientY_B = std::move(gradientY_G);
729     std::unique_ptr<Array2Df> gradientXBlended_B = std::move(gradientXBlended_G);
730     std::unique_ptr<Array2Df> gradientYBlended_B = std::move(gradientYBlended_G);
731 
732     computeGradient(*gradientXGood_B, *gradientYGood_B, *logIrradianceGood_B);
733     ph->setValue(81);
734     if (ph->canceled()) {
735         return NULL;
736     }
737     computeGradient(*gradientX_B, *gradientY_B, *logIrradiance_B);
738     ph->setValue(86);
739     if (ph->canceled()) {
740         return NULL;
741     }
742     if (manualAg)
743         blendGradients(*gradientXBlended_B, *gradientYBlended_B, *gradientX_B,
744                        *gradientY_B, *gradientXGood_B, *gradientYGood_B,
745                        *m_agMask);
746     else
747         blendGradients(*gradientXBlended_B, *gradientYBlended_B, *gradientX_B,
748                        *gradientY_B, *gradientXGood_B, *gradientYGood_B,
749                        patches, gridX, gridY);
750 
751     ph->setValue(90);
752     if (ph->canceled()) {
753         return NULL;
754     }
755 
756     std::unique_ptr<Array2Df> divergence_B(new Array2Df(width, height));
757     computeDivergence(*divergence_B, *gradientXBlended_B, *gradientYBlended_B);
758     ph->setValue(93);
759     if (ph->canceled()) {
760         return NULL;
761     }
762 
763     qDebug() << "solve_pde";
764     solve_pde_dct(*divergence_B, *logIrradiance_B);
765     ph->setValue(94);
766     if (ph->canceled()) {
767         return NULL;
768     }
769 
770     //Blend
771     Frame *deghosted = new Frame(width, height);
772     // Channel *Urc, *Ugc, *Ubc;
773     Channel *Uc[3];
774     deghosted->createXYZChannels(Uc[0], Uc[1], Uc[2]);
775 
776     computeIrradiance(*Uc[0], *logIrradiance_R);
777     ph->setValue(95);
778     if (ph->canceled()) {
779         delete deghosted;
780         return NULL;
781     }
782     computeIrradiance(*Uc[1], *logIrradiance_G);
783     ph->setValue(97);
784     if (ph->canceled()) {
785         delete deghosted;
786         return NULL;
787     }
788     computeIrradiance(*Uc[2], *logIrradiance_B);
789     ph->setValue(99);
790     if (ph->canceled()) {
791         delete deghosted;
792         return NULL;
793     }
794     // shadesOfGrayAWB(*Uc[0], *Uc[1], *Uc[2]);
795 
796     for (int c = 0; c < 3; c++) {
797         cmax[c] = *max_element(Uc[c]->begin(), Uc[c]->end());
798         cmin[c] = *min_element(Uc[c]->begin(), Uc[c]->end());
799     }
800     Max = std::max(cmax[0], std::max(cmax[1], cmax[2]));
801     Min = std::min(cmin[0], std::min(cmin[1], cmin[2]));
802 
803     for (int c = 0; c < 3; c++) {
804         replace_if(Uc[c]->begin(), Uc[c]->end(),
805                    [](float f) { return !isnormal(f); }, Max);
806         replace_if(Uc[c]->begin(), Uc[c]->end(),
807                    [](float f) { return !isfinite(f); }, Max);
808         transform(Uc[c]->begin(), Uc[c]->end(), Uc[c]->begin(),
809                   Normalizer(Min, Max));
810     }
811 
812     ph->setValue(100);
813 
814     emit progressFinished();
815     //this->reset();
816 #ifdef TIMER_PROFILING
817     stop_watch.stop_and_update();
818     std::cout << "doAntiGhosting = " << stop_watch.get_time() << " msec"
819               << std::endl;
820 #endif
821     return deghosted;
822 }
823 
getAgData(bool patches[][agGridSize],int & h0)824 void HdrCreationManager::getAgData(bool patches[][agGridSize], int &h0) {
825     memcpy(patches, m_patches, agGridSize * agGridSize);
826 
827     h0 = m_agGoodImageIndex;
828 }
829 
setPatches(bool patches[][agGridSize])830 void HdrCreationManager::setPatches(bool patches[][agGridSize]) {
831     memcpy(m_patches, patches, agGridSize * agGridSize);
832 }
833 
reset()834 void HdrCreationManager::reset() {
835     if (m_align != NULL) {
836         m_align->reset();
837     }
838 
839     if (m_futureWatcher.isRunning()) {
840         qDebug() << "Aborting loadFiles...";
841         m_futureWatcher.cancel();
842         m_futureWatcher.waitForFinished();
843         emit loadFilesAborted();
844     }
845 
846     disconnect(&m_futureWatcher, &QFutureWatcherBase::finished, this,
847                &HdrCreationManager::loadFilesDone);
848     m_data.clear();
849     m_tmpdata.clear();
850 }
851