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"