File indexing completed on 2025-01-05 03:53:11
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 * Acknowledge : based on the expoblending tool 0009 * 0010 * SPDX-FileCopyrightText: 2011-2016 by Benjamin Girault <benjamin dot girault at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "panopreprocesspage.h" 0018 0019 // Qt includes 0020 0021 #include <QDir> 0022 #include <QLabel> 0023 #include <QTimer> 0024 #include <QPixmap> 0025 #include <QPushButton> 0026 #include <QCheckBox> 0027 #include <QMutex> 0028 #include <QMutexLocker> 0029 #include <QStandardPaths> 0030 #include <QApplication> 0031 #include <QVBoxLayout> 0032 #include <QHBoxLayout> 0033 #include <QTextBrowser> 0034 #include <QList> 0035 0036 // KDE includes 0037 0038 #include <klocalizedstring.h> 0039 #include <ksharedconfig.h> 0040 #include <kconfiggroup.h> 0041 0042 // Local includes 0043 0044 #include "digikam_debug.h" 0045 #include "cpcleanbinary.h" 0046 #include "cpfindbinary.h" 0047 #include "panomanager.h" 0048 #include "panoactionthread.h" 0049 #include "dlayoutbox.h" 0050 #include "dworkingpixmap.h" 0051 0052 namespace DigikamGenericPanoramaPlugin 0053 { 0054 0055 class Q_DECL_HIDDEN PanoPreProcessPage::Private 0056 { 0057 public: 0058 0059 explicit Private() 0060 : progressCount (0), 0061 progressLabel (nullptr), 0062 progressTimer (nullptr), 0063 preprocessingDone (false), 0064 canceled (false), 0065 nbFilesProcessed (0), 0066 title (nullptr), 0067 celesteCheckBox (nullptr), 0068 detailsText (nullptr), 0069 progressPix (nullptr), 0070 mngr (nullptr) 0071 { 0072 } 0073 0074 int progressCount; 0075 QLabel* progressLabel; 0076 QTimer* progressTimer; 0077 QMutex progressMutex; ///< This is a precaution in case the user does a back / next action at the wrong moment 0078 bool preprocessingDone; 0079 bool canceled; 0080 0081 int nbFilesProcessed; 0082 QMutex nbFilesProcessed_mutex; 0083 0084 QLabel* title; 0085 0086 QCheckBox* celesteCheckBox; 0087 0088 QTextBrowser* detailsText; 0089 0090 DWorkingPixmap* progressPix; 0091 0092 PanoManager* mngr; 0093 }; 0094 0095 PanoPreProcessPage::PanoPreProcessPage(PanoManager* const mngr, QWizard* const dlg) 0096 : DWizardPage(dlg, QString::fromLatin1("<b>%1</b>").arg(i18nc("@title: window", "Pre-Processing Images"))), 0097 d (new Private) 0098 { 0099 d->mngr = mngr; 0100 d->progressTimer = new QTimer(this); 0101 d->progressPix = new DWorkingPixmap(this); 0102 DVBox* const vbox = new DVBox(this); 0103 d->title = new QLabel(vbox); 0104 d->title->setWordWrap(true); 0105 d->title->setOpenExternalLinks(true); 0106 0107 KSharedConfigPtr config = KSharedConfig::openConfig(); 0108 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0109 0110 d->celesteCheckBox = new QCheckBox(i18nc("@option: check", "Detect moving skies"), vbox); 0111 d->celesteCheckBox->setChecked(group.readEntry("Celeste", false)); 0112 d->celesteCheckBox->setToolTip(i18nc("@info: tooltip", "Automatic detection of clouds to prevent wrong keypoints matching " 0113 "between images due to moving clouds.")); 0114 d->celesteCheckBox->setWhatsThis(i18nc("@info: whatsthis", "\"Detect moving skies\": During the control points selection and matching, " 0115 "this option discards any points that are associated to a possible cloud. This " 0116 "is useful to prevent moving clouds from altering the control points matching " 0117 "process.")); 0118 vbox->setStretchFactor(new QWidget(vbox), 2); 0119 0120 d->detailsText = new QTextBrowser(vbox); 0121 d->detailsText->hide(); 0122 0123 vbox->setStretchFactor(new QWidget(vbox), 2); 0124 0125 d->progressLabel = new QLabel(vbox); 0126 d->progressLabel->setAlignment(Qt::AlignCenter); 0127 0128 vbox->setStretchFactor(new QWidget(vbox), 10); 0129 0130 setPageWidget(vbox); 0131 0132 QPixmap leftPix(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/data/assistant-preprocessing.png"))); 0133 setLeftBottomPix(leftPix.scaledToWidth(128, Qt::SmoothTransformation)); 0134 0135 connect(d->progressTimer, SIGNAL(timeout()), 0136 this, SLOT(slotProgressTimerDone())); 0137 } 0138 0139 PanoPreProcessPage::~PanoPreProcessPage() 0140 { 0141 KSharedConfigPtr config = KSharedConfig::openConfig(); 0142 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0143 group.writeEntry("Celeste", d->celesteCheckBox->isChecked()); 0144 config->sync(); 0145 0146 delete d; 0147 } 0148 0149 void PanoPreProcessPage::process() 0150 { 0151 QMutexLocker lock(&d->progressMutex); 0152 0153 d->title->setText(QString::fromUtf8("<qt>" 0154 "<p>%1</p>" 0155 "<p>%2</p>" 0156 "</qt>") 0157 .arg(i18nc("@info", "Pre-processing is in progress, please wait.")) 0158 .arg(i18nc("@info", "This can take a while..."))); 0159 0160 d->celesteCheckBox->hide(); 0161 d->progressTimer->start(300); 0162 0163 connect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0164 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0165 0166 connect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0167 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0168 /* 0169 d->nbFilesProcessed = 0; 0170 */ 0171 d->mngr->resetBasePto(); 0172 d->mngr->resetCpFindPto(); 0173 d->mngr->resetCpCleanPto(); 0174 d->mngr->preProcessedMap().clear(); 0175 d->mngr->thread()->preProcessFiles(d->mngr->itemsList(), 0176 d->mngr->preProcessedMap(), 0177 d->mngr->basePtoUrl(), 0178 d->mngr->cpFindPtoUrl(), 0179 d->mngr->cpCleanPtoUrl(), 0180 d->celesteCheckBox->isChecked(), 0181 /* 0182 d->mngr->hdr(), 0183 */ 0184 d->mngr->format(), 0185 d->mngr->gPano(), 0186 d->mngr->cpFindBinary().version(), 0187 d->mngr->cpCleanBinary().path(), 0188 d->mngr->cpFindBinary().path()); 0189 } 0190 0191 void PanoPreProcessPage::initializePage() 0192 { 0193 d->title->setText(QString::fromUtf8("<qt>" 0194 "<p>%1</p>" 0195 "<p>%2</p>" 0196 "<p>%3</p>" 0197 "<p>%4</p>" 0198 "</qt>") 0199 .arg(i18nc("@info", "Now, we will pre-process images before stitching them.")) 0200 .arg(i18nc("@info", "Pre-processing operations include Raw demosaicing. Raw images will be converted " 0201 "to 16-bit sRGB images with auto-gamma.")) 0202 .arg(i18nc("@info", "Pre-processing also include a calculation of some control points to match " 0203 "overlaps between images. For that purpose, the \"%1\" program will be used.", 0204 QDir::toNativeSeparators(d->mngr->cpFindBinary().path()))) 0205 .arg(i18nc("@info", "Press the \"Next\" button to start pre-processing."))); 0206 0207 d->detailsText->hide(); 0208 d->celesteCheckBox->show(); 0209 0210 d->canceled = false; 0211 d->preprocessingDone = false; 0212 0213 setComplete(true); 0214 Q_EMIT completeChanged(); 0215 } 0216 0217 bool PanoPreProcessPage::validatePage() 0218 { 0219 if (d->preprocessingDone) 0220 { 0221 return true; 0222 } 0223 0224 setComplete(false); 0225 process(); 0226 0227 return false; 0228 } 0229 0230 void PanoPreProcessPage::cleanupPage() 0231 { 0232 d->canceled = true; 0233 0234 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0235 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0236 0237 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0238 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0239 0240 d->mngr->thread()->cancel(); 0241 0242 QMutexLocker lock(&d->progressMutex); 0243 0244 if (d->progressTimer->isActive()) 0245 { 0246 d->progressTimer->stop(); 0247 d->progressLabel->clear(); 0248 } 0249 } 0250 0251 void PanoPreProcessPage::slotProgressTimerDone() 0252 { 0253 d->progressLabel->setPixmap(d->progressPix->frameAt(d->progressCount)); 0254 0255 if (d->progressPix->frameCount()) 0256 { 0257 d->progressCount = (d->progressCount + 1) % d->progressPix->frameCount(); 0258 } 0259 0260 d->progressTimer->start(300); 0261 } 0262 0263 void PanoPreProcessPage::slotPanoAction(const DigikamGenericPanoramaPlugin::PanoActionData& ad) 0264 { 0265 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "SlotPanoAction (preprocessing)"; 0266 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "starting, success, canceled, action: " << ad.starting << ad.success << d->canceled << ad.action; 0267 QString text; 0268 0269 QMutexLocker lock(&d->progressMutex); 0270 0271 if (!ad.starting) // Something is complete... 0272 { 0273 if (!ad.success) // Something is failed... 0274 { 0275 if (d->canceled) // In that case, the error is expected 0276 { 0277 return; 0278 } 0279 0280 switch (ad.action) 0281 { 0282 case PANO_PREPROCESS_INPUT: 0283 case PANO_CREATEPTO: 0284 case PANO_CPFIND: 0285 case PANO_CPCLEAN: 0286 { 0287 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0288 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0289 0290 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0291 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0292 0293 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Job failed (preprocessing): " << ad.action; 0294 0295 if (d->detailsText->isHidden()) // Ensures only the first failed task is shown 0296 { 0297 d->title->setText(QString::fromUtf8("<qt>" 0298 "<p>%1</p>" 0299 "<p>%2</p>" 0300 "</qt>") 0301 .arg(i18nc("@info", "Pre-processing has failed.")) 0302 .arg(i18nc("@info", "See processing messages below."))); 0303 0304 d->progressTimer->stop(); 0305 d->celesteCheckBox->hide(); 0306 d->detailsText->show(); 0307 d->progressLabel->clear(); 0308 d->detailsText->setText(ad.message); 0309 0310 setComplete(false); 0311 Q_EMIT completeChanged(); 0312 0313 } 0314 break; 0315 } 0316 0317 default: 0318 { 0319 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (preprocessing) " << ad.action; 0320 break; 0321 } 0322 } 0323 } 0324 else // Something is done... 0325 { 0326 switch (ad.action) 0327 { 0328 case PANO_PREPROCESS_INPUT: 0329 { 0330 /* 0331 QMutexLocker nbProcessed(&d->nbFilesProcessed_mutex); 0332 d->nbFilesProcessed++; 0333 */ 0334 break; 0335 } 0336 0337 case PANO_CREATEPTO: 0338 case PANO_CPFIND: 0339 { 0340 // Nothing to do, that just another step towards the end 0341 break; 0342 } 0343 0344 case PANO_CPCLEAN: 0345 { 0346 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0347 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0348 0349 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0350 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0351 0352 d->progressTimer->stop(); 0353 d->progressLabel->clear(); 0354 d->preprocessingDone = true; 0355 0356 Q_EMIT signalPreProcessed(); 0357 initializePage(); 0358 0359 break; 0360 } 0361 0362 default: 0363 { 0364 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (preprocessing) " << ad.action; 0365 0366 break; 0367 } 0368 } 0369 } 0370 } 0371 } 0372 0373 } // namespace DigikamGenericPanoramaPlugin 0374 0375 #include "moc_panopreprocesspage.cpp"