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