File indexing completed on 2025-01-05 03:53:08
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2011-05-23 0007 * Description : a tool to create panorama by fusion of several images. 0008 * 0009 * SPDX-FileCopyrightText: 2011-2016 by Benjamin Girault <benjamin dot girault at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2011 by Johannes Wienke <languitar at semipol dot de> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "panoactionthread.h" 0018 0019 // Qt includes 0020 0021 #include <QSharedPointer> 0022 #include <QTemporaryDir> 0023 #include <QString> 0024 0025 // KDE includes 0026 0027 #include <ThreadWeaver/Queue> 0028 #include <ThreadWeaver/QObjectDecorator> 0029 #include <threadweaver/debuggingaids.h> 0030 0031 // Local includes 0032 0033 #include "digikam_debug.h" 0034 #include "panotasks.h" 0035 0036 using namespace ThreadWeaver; 0037 0038 namespace DigikamGenericPanoramaPlugin 0039 { 0040 0041 class Q_DECL_HIDDEN PanoActionThread::Private 0042 { 0043 public: 0044 0045 explicit Private(QObject* const parent = nullptr) 0046 : threadQueue(new Queue(parent)) 0047 { 0048 ThreadWeaver::setDebugLevel(true, 10); 0049 } 0050 0051 ~Private() 0052 { 0053 threadQueue->dequeue(); 0054 threadQueue->requestAbort(); 0055 threadQueue->finish(); 0056 } 0057 0058 QSharedPointer<QTemporaryDir> preprocessingTmpDir; 0059 QString preprocessingTmpPath; 0060 QSharedPointer<Queue> threadQueue; 0061 }; 0062 0063 PanoActionThread::PanoActionThread(QObject* const parent) 0064 : QObject(parent), 0065 d (new Private(this)) 0066 { 0067 // PanoActionThread init 0068 0069 qRegisterMetaType<PanoActionData>("PanoActionData"); 0070 0071 d->threadQueue->setMaximumNumberOfThreads(qMax(QThread::idealThreadCount(), 1)); 0072 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Starting Main Thread"; 0073 } 0074 0075 PanoActionThread::~PanoActionThread() 0076 { 0077 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Calling action thread destructor"; 0078 0079 delete d; 0080 } 0081 0082 void PanoActionThread::cancel() 0083 { 0084 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Cancel (PanoAction Thread)"; 0085 d->threadQueue->dequeue(); 0086 d->threadQueue->requestAbort(); 0087 } 0088 0089 void PanoActionThread::finish() 0090 { 0091 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Finish (PanoAction Thread)"; 0092 0093 // Wait for all queued jobs to finish 0094 0095 d->threadQueue->finish(); 0096 d->threadQueue->resume(); 0097 } 0098 0099 void PanoActionThread::preProcessFiles(const QList<QUrl>& urlList, 0100 PanoramaItemUrlsMap& preProcessedMap, 0101 QUrl& baseUrl, 0102 QUrl& cpFindPtoUrl, 0103 QUrl& cpCleanPtoUrl, 0104 bool celeste, 0105 PanoramaFileType fileType, 0106 bool gPano, 0107 const QString& huginVersion, 0108 const QString& cpCleanPath, 0109 const QString& cpFindPath) 0110 { 0111 QString prefix = QDir::tempPath() + QLatin1Char('/') + 0112 QLatin1String("digiKam-panorama-tmp-XXXXXX"); 0113 0114 d->preprocessingTmpDir = QSharedPointer<QTemporaryDir>(new QTemporaryDir(prefix)); 0115 QDir().mkpath(d->preprocessingTmpDir->path() + QLatin1String("/work")); 0116 d->preprocessingTmpPath = d->preprocessingTmpDir->path() + QLatin1String("/work"); 0117 0118 QSharedPointer<Sequence> jobSeq(new Sequence()); 0119 0120 QSharedPointer<Collection> preprocessJobs(new Collection()); 0121 0122 int id = 0; 0123 0124 for (const QUrl& file : urlList) 0125 { 0126 preProcessedMap.insert(file, PanoramaPreprocessedUrls()); 0127 0128 QObjectDecorator* const t = new QObjectDecorator(new PreProcessTask(d->preprocessingTmpPath, 0129 id++, 0130 preProcessedMap[file], 0131 file)); 0132 0133 connect(t, SIGNAL(started(ThreadWeaver::JobPointer)), 0134 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0135 0136 connect(t, SIGNAL(done(ThreadWeaver::JobPointer)), 0137 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0138 0139 (*preprocessJobs) << JobPointer(t); 0140 } 0141 0142 (*jobSeq) << preprocessJobs; 0143 0144 QObjectDecorator* const pto = new QObjectDecorator(new CreatePtoTask(d->preprocessingTmpPath, 0145 fileType, 0146 baseUrl, 0147 urlList, 0148 preProcessedMap, 0149 gPano, 0150 huginVersion)); 0151 0152 connect(pto, SIGNAL(started(ThreadWeaver::JobPointer)), 0153 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0154 0155 connect(pto, SIGNAL(done(ThreadWeaver::JobPointer)), 0156 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0157 0158 (*jobSeq) << JobPointer(pto); 0159 0160 QObjectDecorator* const cpFind = new QObjectDecorator(new CpFindTask(d->preprocessingTmpPath, 0161 baseUrl, 0162 cpFindPtoUrl, 0163 celeste, 0164 cpFindPath)); 0165 0166 connect(cpFind, SIGNAL(started(ThreadWeaver::JobPointer)), 0167 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0168 0169 connect(cpFind, SIGNAL(done(ThreadWeaver::JobPointer)), 0170 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0171 0172 (*jobSeq) << JobPointer(cpFind); 0173 0174 QObjectDecorator* const cpClean = new QObjectDecorator(new CpCleanTask(d->preprocessingTmpPath, 0175 cpFindPtoUrl, 0176 cpCleanPtoUrl, 0177 cpCleanPath)); 0178 0179 connect(cpClean, SIGNAL(started(ThreadWeaver::JobPointer)), 0180 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0181 0182 connect(cpClean, SIGNAL(done(ThreadWeaver::JobPointer)), 0183 this, SLOT(slotDone(ThreadWeaver::JobPointer))); 0184 0185 (*jobSeq) << JobPointer(cpClean); 0186 0187 d->threadQueue->enqueue(jobSeq); 0188 } 0189 0190 void PanoActionThread::optimizeProject(const QUrl& ptoUrl, 0191 QUrl& optimizePtoUrl, 0192 QUrl& viewCropPtoUrl, 0193 bool levelHorizon, 0194 bool buildGPano, 0195 const QString& autooptimiserPath, 0196 const QString& panoModifyPath) 0197 { 0198 QSharedPointer<Sequence> jobs(new Sequence()); 0199 0200 QObjectDecorator* const ot = new QObjectDecorator(new OptimisationTask(d->preprocessingTmpPath, 0201 ptoUrl, 0202 optimizePtoUrl, 0203 levelHorizon, 0204 buildGPano, 0205 autooptimiserPath)); 0206 0207 connect(ot, SIGNAL(started(ThreadWeaver::JobPointer)), 0208 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0209 0210 connect(ot, SIGNAL(done(ThreadWeaver::JobPointer)), 0211 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0212 0213 (*jobs) << ot; 0214 0215 QObjectDecorator* const act = new QObjectDecorator(new AutoCropTask(d->preprocessingTmpPath, 0216 optimizePtoUrl, 0217 viewCropPtoUrl, 0218 buildGPano, 0219 panoModifyPath)); 0220 0221 connect(act, SIGNAL(started(ThreadWeaver::JobPointer)), 0222 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0223 0224 connect(act, SIGNAL(done(ThreadWeaver::JobPointer)), 0225 this, SLOT(slotDone(ThreadWeaver::JobPointer))); 0226 0227 (*jobs) << act; 0228 0229 d->threadQueue->enqueue(jobs); 0230 } 0231 0232 void PanoActionThread::generatePanoramaPreview(QSharedPointer<const PTOType> ptoData, 0233 QUrl& previewPtoUrl, 0234 QUrl& previewMkUrl, 0235 QUrl& previewUrl, 0236 const PanoramaItemUrlsMap& preProcessedUrlsMap, 0237 const QString& makePath, 0238 const QString& pto2mkPath, 0239 const QString& huginExecutorPath, 0240 bool hugin2015, 0241 const QString& enblendPath, 0242 const QString& nonaPath) 0243 { 0244 QSharedPointer<Sequence> jobs(new Sequence()); 0245 0246 QObjectDecorator* const ptoTask = new QObjectDecorator(new CreatePreviewTask(d->preprocessingTmpPath, 0247 ptoData, 0248 previewPtoUrl, 0249 preProcessedUrlsMap)); 0250 0251 connect(ptoTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0252 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0253 0254 connect(ptoTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0255 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0256 0257 (*jobs) << ptoTask; 0258 0259 if (!hugin2015) 0260 { 0261 appendStitchingJobs(jobs, 0262 previewPtoUrl, 0263 previewMkUrl, 0264 previewUrl, 0265 preProcessedUrlsMap, 0266 JPEG, 0267 makePath, 0268 pto2mkPath, 0269 enblendPath, 0270 nonaPath, 0271 true); 0272 } 0273 else 0274 { 0275 QObjectDecorator* const huginExecutorTask = new QObjectDecorator(new HuginExecutorTask(d->preprocessingTmpPath, 0276 previewPtoUrl, 0277 previewUrl, 0278 JPEG, 0279 huginExecutorPath, 0280 true)); 0281 0282 connect(huginExecutorTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0283 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0284 0285 connect(huginExecutorTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0286 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0287 0288 (*jobs) << huginExecutorTask; 0289 } 0290 0291 d->threadQueue->enqueue(jobs); 0292 } 0293 0294 void PanoActionThread::compileProject(QSharedPointer<const PTOType> basePtoData, 0295 QUrl& panoPtoUrl, 0296 QUrl& mkUrl, 0297 QUrl& panoUrl, 0298 const PanoramaItemUrlsMap& preProcessedUrlsMap, 0299 PanoramaFileType fileType, 0300 const QRect& crop, 0301 const QString& makePath, 0302 const QString& pto2mkPath, 0303 const QString& huginExecutorPath, 0304 bool hugin2015, 0305 const QString& enblendPath, 0306 const QString& nonaPath) 0307 { 0308 QSharedPointer<Sequence> jobs(new Sequence()); 0309 0310 QObjectDecorator* const ptoTask = new QObjectDecorator(new CreateFinalPtoTask(d->preprocessingTmpPath, 0311 basePtoData, 0312 panoPtoUrl, 0313 crop)); 0314 0315 connect(ptoTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0316 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0317 0318 connect(ptoTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0319 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0320 0321 (*jobs) << ptoTask; 0322 0323 if (!hugin2015) 0324 { 0325 appendStitchingJobs(jobs, 0326 panoPtoUrl, 0327 mkUrl, 0328 panoUrl, 0329 preProcessedUrlsMap, 0330 fileType, 0331 makePath, 0332 pto2mkPath, 0333 enblendPath, 0334 nonaPath, 0335 false); 0336 } 0337 else 0338 { 0339 QObjectDecorator* const huginExecutorTask = new QObjectDecorator(new HuginExecutorTask(d->preprocessingTmpPath, 0340 panoPtoUrl, 0341 panoUrl, 0342 fileType, 0343 huginExecutorPath, 0344 false)); 0345 0346 connect(huginExecutorTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0347 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0348 0349 connect(huginExecutorTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0350 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0351 0352 (*jobs) << huginExecutorTask; 0353 } 0354 0355 d->threadQueue->enqueue(jobs); 0356 } 0357 0358 void PanoActionThread::copyFiles(const QUrl& ptoUrl, 0359 const QUrl& panoUrl, 0360 const QUrl& finalPanoUrl, 0361 const PanoramaItemUrlsMap& preProcessedUrlsMap, 0362 bool savePTO, 0363 bool addGPlusMetadata) 0364 { 0365 QObjectDecorator* const t = new QObjectDecorator(new CopyFilesTask(d->preprocessingTmpPath, 0366 panoUrl, 0367 finalPanoUrl, 0368 ptoUrl, 0369 preProcessedUrlsMap, 0370 savePTO, 0371 addGPlusMetadata)); 0372 0373 connect(t, SIGNAL(started(ThreadWeaver::JobPointer)), 0374 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0375 0376 connect(t, SIGNAL(done(ThreadWeaver::JobPointer)), 0377 this, SLOT(slotDone(ThreadWeaver::JobPointer))); 0378 0379 d->threadQueue->enqueue(JobPointer(t)); 0380 } 0381 0382 void PanoActionThread::slotStarting(JobPointer j) 0383 { 0384 QSharedPointer<QObjectDecorator> dec = j.staticCast<QObjectDecorator>(); 0385 PanoTask* const t = static_cast<PanoTask*>(dec->job()); 0386 0387 PanoActionData ad; 0388 ad.starting = true; 0389 ad.action = t->action; 0390 ad.id = -1; 0391 0392 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Starting (PanoAction Thread) (action):" << ad.action; 0393 0394 if (t->action == PANO_NONAFILE) 0395 { 0396 CompileMKStepTask* const c = static_cast<CompileMKStepTask*>(t); 0397 ad.id = c->id; 0398 } 0399 else if (t->action == PANO_PREPROCESS_INPUT) 0400 { 0401 PreProcessTask* const p = static_cast<PreProcessTask*>(t); 0402 ad.id = p->id; 0403 } 0404 0405 Q_EMIT starting(ad); 0406 } 0407 0408 void PanoActionThread::slotStepDone(JobPointer j) 0409 { 0410 QSharedPointer<QObjectDecorator> dec = j.staticCast<QObjectDecorator>(); 0411 PanoTask* const t = static_cast<PanoTask*>(dec->job()); 0412 0413 PanoActionData ad; 0414 ad.starting = false; 0415 ad.action = t->action; 0416 ad.id = -1; 0417 ad.success = t->success(); 0418 ad.message = t->errString; 0419 0420 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Step done (PanoAction Thread) (action, success):" << ad.action << ad.success; 0421 0422 if (t->action == PANO_NONAFILE) 0423 { 0424 CompileMKStepTask* const c = static_cast<CompileMKStepTask*>(t); 0425 ad.id = c->id; 0426 } 0427 else if (t->action == PANO_PREPROCESS_INPUT) 0428 { 0429 PreProcessTask* const p = static_cast<PreProcessTask*>(t); 0430 ad.id = p->id; 0431 } 0432 0433 if (!ad.success) 0434 { 0435 d->threadQueue->dequeue(); 0436 } 0437 0438 Q_EMIT stepFinished(ad); 0439 } 0440 0441 void PanoActionThread::slotDone(JobPointer j) 0442 { 0443 QSharedPointer<QObjectDecorator> dec = j.staticCast<QObjectDecorator>(); 0444 PanoTask* const t = static_cast<PanoTask*>(dec->job()); 0445 0446 PanoActionData ad; 0447 ad.starting = false; 0448 ad.action = t->action; 0449 ad.id = -1; 0450 ad.success = t->success(); 0451 ad.message = t->errString; 0452 0453 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Done (PanoAction Thread) (action, success):" 0454 << ad.action << ad.success; 0455 0456 if (t->action == PANO_NONAFILE) 0457 { 0458 CompileMKStepTask* const c = static_cast<CompileMKStepTask*>(t); 0459 ad.id = c->id; 0460 } 0461 else if (t->action == PANO_PREPROCESS_INPUT) 0462 { 0463 PreProcessTask* const p = static_cast<PreProcessTask*>(t); 0464 ad.id = p->id; 0465 } 0466 0467 Q_EMIT jobCollectionFinished(ad); 0468 } 0469 0470 void PanoActionThread::appendStitchingJobs(const QSharedPointer<Sequence>& js, 0471 const QUrl& ptoUrl, 0472 QUrl& mkUrl, 0473 QUrl& outputUrl, 0474 const PanoramaItemUrlsMap& preProcessedUrlsMap, 0475 PanoramaFileType fileType, 0476 const QString& makePath, 0477 const QString& pto2mkPath, 0478 const QString& enblendPath, 0479 const QString& nonaPath, 0480 bool preview) 0481 { 0482 QSharedPointer<Sequence> jobs(new Sequence()); 0483 0484 QObjectDecorator* const createMKTask = new QObjectDecorator(new CreateMKTask(d->preprocessingTmpPath, 0485 ptoUrl, 0486 mkUrl, 0487 outputUrl, 0488 fileType, 0489 pto2mkPath, 0490 preview)); 0491 0492 connect(createMKTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0493 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0494 0495 connect(createMKTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0496 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0497 0498 (*jobs) << createMKTask; 0499 0500 for (int i = 0 ; i < preProcessedUrlsMap.size() ; ++i) 0501 { 0502 QObjectDecorator* const t = new QObjectDecorator(new CompileMKStepTask(d->preprocessingTmpPath, 0503 i, 0504 mkUrl, 0505 nonaPath, 0506 enblendPath, 0507 makePath, 0508 preview)); 0509 0510 connect(t, SIGNAL(started(ThreadWeaver::JobPointer)), 0511 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0512 0513 connect(t, SIGNAL(done(ThreadWeaver::JobPointer)), 0514 this, SLOT(slotStepDone(ThreadWeaver::JobPointer))); 0515 0516 (*jobs) << t; 0517 } 0518 0519 QObjectDecorator* const compileMKTask = new QObjectDecorator(new CompileMKTask(d->preprocessingTmpPath, 0520 mkUrl, 0521 outputUrl, 0522 nonaPath, 0523 enblendPath, 0524 makePath, 0525 preview)); 0526 0527 connect(compileMKTask, SIGNAL(started(ThreadWeaver::JobPointer)), 0528 this, SLOT(slotStarting(ThreadWeaver::JobPointer))); 0529 0530 connect(compileMKTask, SIGNAL(done(ThreadWeaver::JobPointer)), 0531 this, SLOT(slotDone(ThreadWeaver::JobPointer))); 0532 0533 (*jobs) << compileMKTask; 0534 0535 (*js) << jobs; 0536 } 0537 0538 } // namespace DigikamGenericPanoramaPlugin 0539 0540 #include "moc_panoactionthread.cpp"