File indexing completed on 2025-03-09 03:50:50

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-11-13
0007  * Description : a tool to blend bracketed images.
0008  *
0009  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2015      by Benjamin Girault <benjamin dot girault at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "enfusesettings.h"
0017 
0018 // Qt includes
0019 
0020 #include <QCheckBox>
0021 #include <QGridLayout>
0022 #include <QLabel>
0023 #include <QStyle>
0024 #include <QApplication>
0025 #include <QSpinBox>
0026 #include <QDoubleSpinBox>
0027 
0028 // KDE includes
0029 
0030 #include <klocalizedstring.h>
0031 #include <kconfiggroup.h>
0032 
0033 namespace DigikamGenericExpoBlendingPlugin
0034 {
0035 
0036 QString EnfuseSettings::asCommentString() const
0037 {
0038     QString ret;
0039 
0040     ret.append(hardMask ? i18nc("@info", "Hardmask: enabled") : i18nc("@info", "Hardmask: disabled"));
0041     ret.append(QLatin1Char('\n'));
0042     ret.append(ciecam02 ? i18nc("@info", "CIECAM02: enabled") : i18nc("@info", "CIECAM02: disabled"));
0043     ret.append(QLatin1Char('\n'));
0044     ret.append(autoLevels ? i18nc("@info", "Levels: auto")    : i18nc("@info", "Levels: <numid>%1</numid>", levels));
0045     ret.append(QLatin1Char('\n'));
0046     ret.append(i18nc("@info", "Exposure: <numid>%1</numid>",   exposure));
0047     ret.append(QLatin1Char('\n'));
0048     ret.append(i18nc("@info", "Saturation: <numid>%1</numid>", saturation));
0049     ret.append(QLatin1Char('\n'));
0050     ret.append(i18nc("@info", "Contrast: <numid>%1</numid>",   contrast));
0051 
0052     return ret;
0053 }
0054 
0055 QString EnfuseSettings::inputImagesList() const
0056 {
0057     QString ret;
0058 
0059     Q_FOREACH (const QUrl& url, inputUrls)
0060     {
0061         ret.append(url.fileName() + QLatin1String(" ; "));
0062     }
0063 
0064     ret.truncate(ret.length() - 3);
0065 
0066     return ret;
0067 }
0068 
0069 class Q_DECL_HIDDEN EnfuseSettingsWidget::Private
0070 {
0071 public:
0072 
0073     explicit Private()
0074       : autoLevelsCB    (nullptr),
0075         hardMaskCB      (nullptr),
0076         ciecam02CB      (nullptr),
0077         levelsLabel     (nullptr),
0078         exposureLabel   (nullptr),
0079         saturationLabel (nullptr),
0080         contrastLabel   (nullptr),
0081         levelsInput     (nullptr),
0082         exposureInput   (nullptr),
0083         saturationInput (nullptr),
0084         contrastInput   (nullptr)
0085     {
0086     }
0087 
0088 public:
0089 
0090     QCheckBox*       autoLevelsCB;
0091     QCheckBox*       hardMaskCB;
0092     QCheckBox*       ciecam02CB;
0093 
0094     QLabel*          levelsLabel;
0095     QLabel*          exposureLabel;
0096     QLabel*          saturationLabel;
0097     QLabel*          contrastLabel;
0098 
0099     QSpinBox*        levelsInput;
0100 
0101     QDoubleSpinBox*  exposureInput;
0102     QDoubleSpinBox*  saturationInput;
0103     QDoubleSpinBox*  contrastInput;
0104 };
0105 
0106 EnfuseSettingsWidget::EnfuseSettingsWidget(QWidget* const parent)
0107     : QWidget(parent),
0108       d      (new Private)
0109 {
0110     setAttribute(Qt::WA_DeleteOnClose);
0111 
0112     const int spacing       = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0113                              QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
0114 
0115     QGridLayout* const grid = new QGridLayout(this);
0116 
0117     // ------------------------------------------------------------------------
0118 
0119     d->autoLevelsCB = new QCheckBox(i18nc("@option:check Enfuse setting", "Automatic Local/Global Image Features Balance (Levels)"), this);
0120     d->autoLevelsCB->setToolTip(i18nc("@info:tooltip",
0121                                       "Optimize image features (contrast, saturation, . . .) to be as global as possible."));
0122     d->autoLevelsCB->setWhatsThis(i18nc("@info:whatsthis",
0123                                         "Set automatic level selection (maximized) for pyramid blending, "
0124                                         "i.e. optimize image features (contrast, saturation, . . .) to be as global as possible."));
0125 
0126     d->levelsLabel  = new QLabel(i18nc("@label:slider Enfuse settings", "Image Features Balance:"));
0127     d->levelsInput  = new QSpinBox(this);
0128     d->levelsInput->setRange(1, 29);
0129     d->levelsInput->setSingleStep(1);
0130     d->levelsInput->setToolTip(i18nc("@info:tooltip",
0131                                      "Balances between local features (small number) or global features (high number)."));
0132     d->levelsInput->setWhatsThis(i18nc("@info:whatsthis",
0133                                        "Set the number of levels for pyramid blending. "
0134                                        "Balances towards local features (small number) or global features (high number). "
0135                                        "Additionally, a low number trades off quality of results for faster "
0136                                        "execution time and lower memory usage."));
0137 
0138     d->hardMaskCB = new QCheckBox(i18nc("@option:check", "Hard Mask"), this);
0139     d->hardMaskCB->setToolTip(i18nc("@info:tooltip",
0140                                     "Useful only for focus stack to improve sharpness."));
0141     d->hardMaskCB->setWhatsThis(i18nc("@info:whatsthis",
0142                                       "Force hard blend masks without averaging on finest "
0143                                       "scale. This is only useful for focus "
0144                                       "stacks with thin and high contrast features. "
0145                                       "It improves sharpness at the expense of increased noise."));
0146 
0147     d->exposureLabel = new QLabel(i18nc("@label:slider Enfuse settings", "Well-Exposedness Contribution:"));
0148     d->exposureInput = new QDoubleSpinBox(this);
0149     d->exposureInput->setRange(0.0, 1.0);
0150     d->exposureInput->setSingleStep(0.01);
0151     d->exposureInput->setToolTip(i18nc("@info:tooltip",
0152                                        "Contribution of well exposed pixels to the blending process."));
0153     d->exposureInput->setWhatsThis(i18nc("@info:whatsthis",
0154                                          "Set the well-exposedness criterion contribution for the blending process. "
0155                                          "Higher values will favor well-exposed pixels."));
0156 
0157     d->saturationLabel = new QLabel(i18nc("@label:slider enfuse settings", "High-Saturation Contribution:"));
0158     d->saturationInput = new QDoubleSpinBox(this);
0159     d->saturationInput->setDecimals(2);
0160     d->saturationInput->setRange(0.0, 1.0);
0161     d->saturationInput->setSingleStep(0.01);
0162     d->saturationInput->setToolTip(i18nc("@info:tooltip",
0163                                          "Contribution of highly saturated pixels to the blending process."));
0164     d->saturationInput->setWhatsThis(i18nc("@info:whatsthis",
0165                                            "Increasing this value makes pixels with high "
0166                                            "saturation contribute more to the final output."));
0167 
0168     d->contrastLabel = new QLabel(i18nc("@label:slider enfuse settings", "High-Contrast Contribution:"));
0169     d->contrastInput = new QDoubleSpinBox(this);
0170     d->contrastInput->setDecimals(2);
0171     d->contrastInput->setRange(0.0, 1.0);
0172     d->contrastInput->setSingleStep(0.01);
0173     d->contrastInput->setToolTip(i18nc("@info:tooltip",
0174                                        "Contribution of highly contrasted pixels to the blending process."));
0175     d->contrastInput->setWhatsThis(i18nc("@info:whatsthis",
0176                                          "Sets the relative weight of high-contrast pixels. "
0177                                          "Increasing this weight makes pixels with neighboring differently colored "
0178                                          "pixels contribute more to the final output. Particularly useful for focus stacks."));
0179 
0180     d->ciecam02CB = new QCheckBox(i18nc("@option:check", "Use Color Appearance Model (CIECAM02)"), this);
0181     d->ciecam02CB->setToolTip(i18nc("@info:tooltip",
0182                                     "Convert to CIECAM02 color appearance model during the blending process instead of RGB."));
0183     d->ciecam02CB->setWhatsThis(i18nc("@info:whatsthis",
0184                                       "Use Color Appearance Modelling (CIECAM02) to render detailed colors. "
0185                                       "Your input files should have embedded ICC profiles. If no ICC profile is present, "
0186                                       "sRGB color space will be assumed. The difference between using this option "
0187                                       "and default color blending algorithm is very slight, and will be most noticeable "
0188                                       "when you need to blend areas of different primary colors together."));
0189 
0190     // ------------------------------------------------------------------------
0191 
0192     grid->addWidget(d->autoLevelsCB,    0, 0, 1, 2);
0193     grid->addWidget(d->levelsLabel,     1, 0, 1, 1);
0194     grid->addWidget(d->levelsInput,     1, 1, 1, 1);
0195     grid->addWidget(d->hardMaskCB,      2, 0, 1, 2);
0196     grid->addWidget(d->exposureLabel,   3, 0, 1, 1);
0197     grid->addWidget(d->exposureInput,   3, 1, 1, 1);
0198     grid->addWidget(d->saturationLabel, 4, 0, 1, 1);
0199     grid->addWidget(d->saturationInput, 4, 1, 1, 1);
0200     grid->addWidget(d->contrastLabel,   5, 0, 1, 1);
0201     grid->addWidget(d->contrastInput,   5, 1, 1, 1);
0202     grid->addWidget(d->ciecam02CB,      6, 0, 1, 2);
0203     grid->setRowStretch(7, 10);
0204     grid->setContentsMargins(spacing, spacing, spacing, spacing);
0205     grid->setSpacing(spacing);
0206 
0207     // ------------------------------------------------------------------------
0208 
0209     connect(d->autoLevelsCB, SIGNAL(toggled(bool)),
0210             d->levelsLabel, SLOT(setDisabled(bool)));
0211 
0212     connect(d->autoLevelsCB, SIGNAL(toggled(bool)),
0213             d->levelsInput, SLOT(setDisabled(bool)));
0214 }
0215 
0216 EnfuseSettingsWidget::~EnfuseSettingsWidget()
0217 {
0218     delete d;
0219 }
0220 
0221 void EnfuseSettingsWidget::resetToDefault()
0222 {
0223     d->autoLevelsCB->setChecked(true);
0224     d->levelsInput->setValue(20);
0225     d->hardMaskCB->setChecked(false);
0226     d->exposureInput->setValue(1.0);
0227     d->saturationInput->setValue(0.2);
0228     d->contrastInput->setValue(0.0);
0229     d->ciecam02CB->setChecked(false);
0230 }
0231 
0232 void EnfuseSettingsWidget::setSettings(const EnfuseSettings& settings)
0233 {
0234     d->autoLevelsCB->setChecked(settings.autoLevels);
0235     d->levelsInput->setValue(settings.levels);
0236     d->hardMaskCB->setChecked(settings.hardMask);
0237     d->exposureInput->setValue(settings.exposure);
0238     d->saturationInput->setValue(settings.saturation);
0239     d->contrastInput->setValue(settings.contrast);
0240     d->ciecam02CB->setChecked(settings.ciecam02);
0241 }
0242 
0243 EnfuseSettings EnfuseSettingsWidget::settings() const
0244 {
0245     EnfuseSettings settings;
0246     settings.autoLevels = d->autoLevelsCB->isChecked();
0247     settings.levels     = d->levelsInput->value();
0248     settings.hardMask   = d->hardMaskCB->isChecked();
0249     settings.exposure   = d->exposureInput->value();
0250     settings.saturation = d->saturationInput->value();
0251     settings.contrast   = d->contrastInput->value();
0252     settings.ciecam02   = d->ciecam02CB->isChecked();
0253 
0254     return settings;
0255 }
0256 
0257 void EnfuseSettingsWidget::readSettings(const KConfigGroup& group)
0258 {
0259     d->autoLevelsCB->setChecked(group.readEntry("Auto Levels",       true));
0260     d->levelsInput->setValue(group.readEntry("Levels Value",         20));
0261     d->hardMaskCB->setChecked(group.readEntry("Hard Mask",           false));
0262     d->exposureInput->setValue(group.readEntry("Exposure Value",     1.0));
0263     d->saturationInput->setValue(group.readEntry("Saturation Value", 0.2));
0264     d->contrastInput->setValue(group.readEntry("Contrast Value",     0.0));
0265     d->ciecam02CB->setChecked(group.readEntry("CIECAM02",            false));
0266 }
0267 
0268 void EnfuseSettingsWidget::writeSettings(KConfigGroup& group)
0269 {
0270     group.writeEntry("Auto Levels",      d->autoLevelsCB->isChecked());
0271     group.writeEntry("Levels Value",     d->levelsInput->value());
0272     group.writeEntry("Hard Mask",        d->hardMaskCB->isChecked());
0273     group.writeEntry("Exposure Value",   d->exposureInput->value());
0274     group.writeEntry("Saturation Value", d->saturationInput->value());
0275     group.writeEntry("Contrast Value",   d->contrastInput->value());
0276     group.writeEntry("CIECAM02",         d->ciecam02CB->isChecked());
0277 }
0278 
0279 } // namespace DigikamGenericExpoBlendingPlugin
0280 
0281 #include "moc_enfusesettings.cpp"