diff --git a/core/libs/dimg/filters/lens/antivignettingfilter.cpp b/core/libs/dimg/filters/lens/antivignettingfilter.cpp index a22513fd78..900e5830ec 100644 --- a/core/libs/dimg/filters/lens/antivignettingfilter.cpp +++ b/core/libs/dimg/filters/lens/antivignettingfilter.cpp @@ -1,240 +1,242 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : Antivignetting threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Julien Narboux * Copyright (C) 2010 by Martin Klapetek * * Original AntiVignettingFilter algorithm copyrighted 2003 by * John Walker from 'pnmctrfilt' implementation. See * https://www.fourmilab.ch/netpbm/pnmctrfilt for more * information. * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "antivignettingfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" namespace Digikam { AntiVignettingFilter::AntiVignettingFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } AntiVignettingFilter::AntiVignettingFilter(DImg* const orgImage, QObject* const parent, const AntiVignettingContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("AntiVignettingFilter")), m_settings(settings) { initFilter(); } AntiVignettingFilter::~AntiVignettingFilter() { cancelFilter(); } QString AntiVignettingFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Anti-Vignetting Tool")); } /** * This method is inspired from John Walker 'pnmctrfilt' algorithm code. */ void AntiVignettingFilter::filterImage() { int progress; int col, row, xd, td, yd, p; int xsize, ysize, /*diagonal,*/ erad, irad, xctr, yctr; uchar* NewBits = m_destImage.bits(); uchar* data = m_orgImage.bits(); unsigned short* NewBits16 = reinterpret_cast(m_destImage.bits()); unsigned short* data16 = reinterpret_cast(m_orgImage.bits()); int Width = m_orgImage.width(); int Height = m_orgImage.height(); // Determine the shift in pixels from the shift in percentage. + m_settings.yshift = m_settings.yshift * Height / 200.0; m_settings.xshift = m_settings.xshift * Width / 200.0; // Determine the outer radius of the filter. This is the half diagonal // measure of the image multiplied by the radius factor. xsize = (Height + 1) / 2; ysize = (Width + 1) / 2; erad = qRound(hypothenuse(xsize, ysize) * m_settings.outerradius); irad = qRound(hypothenuse(xsize, ysize) * m_settings.outerradius * m_settings.innerradius); /* xsize = qRound(Width / 2.0 + fabs(m_settings.xshift)); ysize = qRound(Height / 2.0 + fabs(m_settings.yshift)); diagonal = qRound(hypothenuse(xsize, ysize)) + 1; */ xctr = qRound(Width / 2.0 + m_settings.xshift); yctr = qRound(Height / 2.0 + m_settings.yshift); for (row = 0 ; runningFlag() && (row < Width) ; ++row) { yd = abs(xctr - row); for (col = 0 ; runningFlag() && (col < Height) ; ++col) { p = (col * Width + row) * 4; xd = abs(yctr - col); td = qRound(hypothenuse(xd, yd)); if (!m_orgImage.sixteenBit()) // 8 bits image { NewBits[ p ] = clamp8bits(data[ p ] * real_attenuation(irad, erad, td)); NewBits[p + 1] = clamp8bits(data[p + 1] * real_attenuation(irad, erad, td)); NewBits[p + 2] = clamp8bits(data[p + 2] * real_attenuation(irad, erad, td)); NewBits[p + 3] = data[p + 3]; } else // 16 bits image. { NewBits16[ p ] = clamp16bits(data16[ p ] * real_attenuation(irad, erad, td)); NewBits16[p + 1] = clamp16bits(data16[p + 1] * real_attenuation(irad, erad, td)); NewBits16[p + 2] = clamp16bits(data16[p + 2] * real_attenuation(irad, erad, td)); NewBits16[p + 3] = data16[p + 3]; } } // Update the progress bar in dialog. + progress = (int)(((double)row * 100.0) / Width); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } inline double AntiVignettingFilter::hypothenuse(double x, double y) { return (sqrt(x * x + y * y)); } double AntiVignettingFilter::attenuation(double r1, double r2, double dist_center) { - if (dist_center < r1) + if (dist_center < r1) { return 1.0; } else if (dist_center > r2) { return 1.0 + m_settings.density; } else { return (1.0 + m_settings.density * (pow((dist_center - r1) / (r2 - r1), m_settings.power))); } } double AntiVignettingFilter::real_attenuation(double r1, double r2, double dist_center) { if (!m_settings.addvignetting) { return (attenuation(r1, r2, dist_center)); } else { return (1.0 / attenuation(r1, r2, dist_center)); } } uchar AntiVignettingFilter::clamp8bits(double x) { - if (x < 0) + if (x < 0) { return 0; } else if (x > 255) { return 255; } else { return ((uchar) x); } } unsigned short AntiVignettingFilter::clamp16bits(double x) { - if (x < 0) + if (x < 0) { return 0; } else if (x > 65535) { return 65535; } else { return ((unsigned short) x); } } FilterAction AntiVignettingFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("addvignetting"), m_settings.addvignetting); action.addParameter(QLatin1String("density"), m_settings.density); action.addParameter(QLatin1String("innerradius"), m_settings.innerradius); action.addParameter(QLatin1String("outerradius"), m_settings.outerradius); action.addParameter(QLatin1String("power"), m_settings.power); action.addParameter(QLatin1String("xshift"), m_settings.xshift); action.addParameter(QLatin1String("yshift"), m_settings.yshift); return action; } void AntiVignettingFilter::readParameters(const Digikam::FilterAction& action) { m_settings.addvignetting = action.parameter(QLatin1String("addvignetting")).toBool(); m_settings.density = action.parameter(QLatin1String("density")).toDouble(); m_settings.innerradius = action.parameter(QLatin1String("innerradius")).toDouble(); m_settings.outerradius = action.parameter(QLatin1String("outerradius")).toDouble(); m_settings.power = action.parameter(QLatin1String("power")).toDouble(); m_settings.xshift = action.parameter(QLatin1String("xshift")).toDouble(); m_settings.yshift = action.parameter(QLatin1String("yshift")).toDouble(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/lens/antivignettingfilter.h b/core/libs/dimg/filters/lens/antivignettingfilter.h index 17fbbc3e0e..6612cc91b7 100644 --- a/core/libs/dimg/filters/lens/antivignettingfilter.h +++ b/core/libs/dimg/filters/lens/antivignettingfilter.h @@ -1,124 +1,124 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : Antivignetting threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ANTI_VIGNETTING_FILTER_H #define DIGIKAM_ANTI_VIGNETTING_FILTER_H // Local includes #include "dimgthreadedfilter.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT AntiVignettingContainer { public: explicit AntiVignettingContainer() + : addvignetting(true), + density(2.0), + power(1.0), + innerradius(1.0), + outerradius(1.0), + xshift(0.0), + yshift(0.0) { - addvignetting = true; - density = 2.0; - power = 1.0; - innerradius = 1.0; - outerradius = 1.0; - xshift = 0.0; - yshift = 0.0; }; ~AntiVignettingContainer() { }; public: bool addvignetting; double density; double power; double innerradius; double outerradius; double xshift; double yshift; }; // ----------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT AntiVignettingFilter : public DImgThreadedFilter { public: explicit AntiVignettingFilter(QObject* const parent = nullptr); explicit AntiVignettingFilter(DImg* const orgImage, QObject* const parent=nullptr, const AntiVignettingContainer& settings=AntiVignettingContainer()); ~AntiVignettingFilter(); - void readParameters(const FilterAction& action) override; - - virtual QString filterIdentifier() const override - { - return FilterIdentifier(); - } - - virtual FilterAction filterAction() override; - static QString FilterIdentifier() { return QLatin1String("digikam:AntiVignettingFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } + void readParameters(const FilterAction& action) override; + + virtual QString filterIdentifier() const override + { + return FilterIdentifier(); + } + + virtual FilterAction filterAction() override; + private: - void filterImage() override; + void filterImage() override; double hypothenuse(double x, double y); uchar clamp8bits(double x); unsigned short clamp16bits(double x); double attenuation(double r1, double r2, double distcenter); double real_attenuation(double r1, double r2, double distcenter); private: AntiVignettingContainer m_settings; }; } // namespace Digikam #endif // DIGIKAM_ANTI_VIGNETTING_FILTER_H diff --git a/core/libs/dimg/filters/lens/antivignettingsettings.cpp b/core/libs/dimg/filters/lens/antivignettingsettings.cpp index cd1b71da6a..cb6195bb6a 100644 --- a/core/libs/dimg/filters/lens/antivignettingsettings.cpp +++ b/core/libs/dimg/filters/lens/antivignettingsettings.cpp @@ -1,327 +1,330 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-03-15 * Description : Anti vignetting settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "antivignettingsettings.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dlayoutbox.h" #include "dexpanderbox.h" #include "dnuminput.h" #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN AntiVignettingSettings::Private { public: explicit Private() : addVignettingCheck(nullptr), maskPreviewLabel(nullptr), densityInput(nullptr), powerInput(nullptr), innerRadiusInput(nullptr), outerRadiusInput(nullptr), xOffsetInput(nullptr), yOffsetInput(nullptr) - {} + { + } static const QString configAddVignettingAdjustmentEntry; static const QString configDensityAdjustmentEntry; static const QString configPowerAdjustmentEntry; static const QString configInnerRadiusAdjustmentEntry; static const QString configOuterRadiusAdjustmentEntry; static const QString configAddVignettingEntry; static const QString configXOffsetEntry; static const QString configYOffsetEntry; QCheckBox* addVignettingCheck; QLabel* maskPreviewLabel; DDoubleNumInput* densityInput; DDoubleNumInput* powerInput; DDoubleNumInput* innerRadiusInput; DDoubleNumInput* outerRadiusInput; DDoubleNumInput* xOffsetInput; DDoubleNumInput* yOffsetInput; }; const QString AntiVignettingSettings::Private::configAddVignettingAdjustmentEntry(QLatin1String("AddVignettingAdjustment")); const QString AntiVignettingSettings::Private::configDensityAdjustmentEntry(QLatin1String("DensityAdjustment")); const QString AntiVignettingSettings::Private::configPowerAdjustmentEntry(QLatin1String("PowerAdjustment")); const QString AntiVignettingSettings::Private::configInnerRadiusAdjustmentEntry(QLatin1String("InnerRadiusAdjustment")); const QString AntiVignettingSettings::Private::configOuterRadiusAdjustmentEntry(QLatin1String("OuterRadiusAdjustment")); const QString AntiVignettingSettings::Private::configAddVignettingEntry(QLatin1String("AddVignetting")); const QString AntiVignettingSettings::Private::configXOffsetEntry(QLatin1String("XOffset")); const QString AntiVignettingSettings::Private::configYOffsetEntry(QLatin1String("YOffset")); // -------------------------------------------------------- AntiVignettingSettings::AntiVignettingSettings(QWidget* parent) : QWidget(parent), d(new Private) { - const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(parent); - d->addVignettingCheck = new QCheckBox(i18n("Add vignetting")); + d->addVignettingCheck = new QCheckBox(i18n("Add vignetting")); d->addVignettingCheck->setWhatsThis(i18n("This option adds vignetting to the image instead of removing it. " "Use it for creative effects.")); d->addVignettingCheck->setChecked(false); // ------------------------------------------------------------- - d->maskPreviewLabel = new QLabel(); + d->maskPreviewLabel = new QLabel(); d->maskPreviewLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); d->maskPreviewLabel->setPixmap(QPixmap(120, 120)); d->maskPreviewLabel->setWhatsThis(i18n("You can see here a thumbnail preview of the anti-vignetting " "mask applied to the image.")); // ------------------------------------------------------------- QLabel* const label1 = new QLabel(i18n("Amount:")); d->densityInput = new DDoubleNumInput(); d->densityInput->setDecimals(1); d->densityInput->setRange(0.05, 5.0, 0.05); d->densityInput->setDefaultValue(1.0); d->densityInput->setWhatsThis(i18n("This value controls the degree of intensity " "of the filter at its point of maximum density.")); // ------------------------------------------------------------- QLabel* const label2 = new QLabel(i18n("Feather:")); d->powerInput = new DDoubleNumInput(); d->powerInput->setDecimals(1); d->powerInput->setRange(1.0, 4.0, 0.05); d->powerInput->setDefaultValue(1.5); d->powerInput->setWhatsThis(i18n("This value is used as the exponent controlling the " "fall-off in density from the inner circle of the filter " "to the outer circle.")); // ------------------------------------------------------------- QLabel* const label3 = new QLabel(i18n("Inner Radius:")); d->innerRadiusInput = new DDoubleNumInput(); d->innerRadiusInput->setDecimals(1); d->innerRadiusInput->setRange(0.0, 0.9, 0.05); d->innerRadiusInput->setDefaultValue(0.5); d->innerRadiusInput->setWhatsThis(i18n("This value is the radius of the inner circle of the filter. " "In the inner circle the image is preserved. " "It is expressed as a multiple of the outer radius of the filter.")); // ------------------------------------------------------------- QLabel* const label4 = new QLabel(i18n("Outer Radius:")); d->outerRadiusInput = new DDoubleNumInput(); d->outerRadiusInput->setDecimals(1); d->outerRadiusInput->setRange(0.1, 2.0, 0.05); d->outerRadiusInput->setDefaultValue(1.0); d->outerRadiusInput->setWhatsThis(i18n("This value is the radius of the outer circle of the filter. " "Outside the outer circle the effect of the filter is at its maximum. " "It is expressed as a multiple of the diagonal of the image.")); // ------------------------------------------------------------- QLabel* const label5 = new QLabel(i18n("X offset:")); d->xOffsetInput = new DDoubleNumInput(); d->xOffsetInput->setDecimals(0); d->xOffsetInput->setRange(-100, 100, 1); d->xOffsetInput->setDefaultValue(0); d->xOffsetInput->setWhatsThis(i18n("X offset ")); // ------------------------------------------------------------- QLabel* const label6 = new QLabel(i18n("Y offset:")); d->yOffsetInput = new DDoubleNumInput(); d->yOffsetInput->setDecimals(0); d->yOffsetInput->setRange(-100, 100, 1); d->yOffsetInput->setDefaultValue(0); d->yOffsetInput->setWhatsThis(i18n("Y offset ")); DLineWidget* const line = new DLineWidget(Qt::Horizontal); // ------------------------------------------------------------- grid->addWidget(d->maskPreviewLabel, 0, 0, 1, 3); grid->addWidget(label1, 1, 0, 1, 3); grid->addWidget(d->densityInput, 2, 0, 1, 3); grid->addWidget(label2, 3, 0, 1, 3); grid->addWidget(d->powerInput, 4, 0, 1, 3); grid->addWidget(label3, 5, 0, 1, 3); grid->addWidget(d->innerRadiusInput, 6, 0, 1, 3); grid->addWidget(label4, 7, 0, 1, 3); grid->addWidget(d->outerRadiusInput, 8, 0, 1, 3); grid->addWidget(label5, 9, 0, 1, 3); grid->addWidget(d->xOffsetInput, 10, 0, 1, 3); grid->addWidget(label6, 11, 0, 1, 3); grid->addWidget(d->yOffsetInput, 12, 0, 1, 3); grid->addWidget(line, 13, 0, 1, 3); grid->addWidget(d->addVignettingCheck, 14, 0, 1, 3); grid->setRowStretch(15, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->densityInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->powerInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->innerRadiusInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->outerRadiusInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->xOffsetInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->yOffsetInput, SIGNAL(valueChanged(double)), this, SLOT(slotSettingsChanged())); connect(d->addVignettingCheck, SIGNAL(toggled(bool)), this, SLOT(slotSettingsChanged())); // ------------------------------------------------------------- slotSettingsChanged(); } AntiVignettingSettings::~AntiVignettingSettings() { delete d; } AntiVignettingContainer AntiVignettingSettings::settings() const { AntiVignettingContainer prm; prm.addvignetting = d->addVignettingCheck->isChecked(); prm.density = d->densityInput->value(); prm.power = d->powerInput->value(); prm.innerradius = d->innerRadiusInput->value(); prm.outerradius = d->outerRadiusInput->value(); prm.xshift = d->xOffsetInput->value(); prm.yshift = d->yOffsetInput->value(); return prm; } void AntiVignettingSettings::setSettings(const AntiVignettingContainer& settings) { blockSignals(true); d->addVignettingCheck->setChecked(settings.addvignetting); d->densityInput->setValue(settings.density); d->powerInput->setValue(settings.power); d->innerRadiusInput->setValue(settings.innerradius); d->outerRadiusInput->setValue(settings.outerradius); d->xOffsetInput->setValue(settings.xshift); d->yOffsetInput->setValue(settings.yshift); blockSignals(false); } void AntiVignettingSettings::resetToDefault() { setSettings(defaultSettings()); } AntiVignettingContainer AntiVignettingSettings::defaultSettings() const { AntiVignettingContainer prm; + return prm; } void AntiVignettingSettings::readSettings(KConfigGroup& group) { AntiVignettingContainer prm; AntiVignettingContainer defaultPrm = defaultSettings(); prm.addvignetting = group.readEntry(d->configAddVignettingEntry, defaultPrm.addvignetting); prm.density = group.readEntry(d->configDensityAdjustmentEntry, defaultPrm.density); prm.power = group.readEntry(d->configPowerAdjustmentEntry, defaultPrm.power); prm.innerradius = group.readEntry(d->configInnerRadiusAdjustmentEntry, defaultPrm.innerradius); prm.outerradius = group.readEntry(d->configOuterRadiusAdjustmentEntry, defaultPrm.outerradius); prm.xshift = group.readEntry(d->configXOffsetEntry, defaultPrm.xshift); prm.yshift = group.readEntry(d->configYOffsetEntry, defaultPrm.yshift); setSettings(prm); } void AntiVignettingSettings::writeSettings(KConfigGroup& group) { AntiVignettingContainer prm = settings(); group.writeEntry(d->configAddVignettingEntry, prm.addvignetting); group.writeEntry(d->configDensityAdjustmentEntry, prm.density); group.writeEntry(d->configPowerAdjustmentEntry, prm.power); group.writeEntry(d->configInnerRadiusAdjustmentEntry, prm.innerradius); group.writeEntry(d->configOuterRadiusAdjustmentEntry, prm.outerradius); group.writeEntry(d->configXOffsetEntry, prm.xshift); group.writeEntry(d->configYOffsetEntry, prm.yshift); } void AntiVignettingSettings::slotSettingsChanged() { // Compute preview mask. + DImg preview(120, 120, false); memset(preview.bits(), 255, preview.numBytes()); AntiVignettingFilter maskPreview(&preview, nullptr, settings()); maskPreview.startFilterDirectly(); QPixmap pix = maskPreview.getTargetImage().convertToPixmap(); QPainter pt(&pix); pt.setPen(QPen(Qt::black, 1)); pt.drawRect(0, 0, pix.width(), pix.height()); pt.end(); d->maskPreviewLabel->setPixmap(pix); emit signalSettingsChanged(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/lens/antivignettingsettings.h b/core/libs/dimg/filters/lens/antivignettingsettings.h index 13a65373a7..58bb2206b1 100644 --- a/core/libs/dimg/filters/lens/antivignettingsettings.h +++ b/core/libs/dimg/filters/lens/antivignettingsettings.h @@ -1,77 +1,77 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-03-15 * Description : Anti vignetting settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ANTI_VIGNETTING_SETTINGS_H #define DIGIKAM_ANTI_VIGNETTING_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "antivignettingfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT AntiVignettingSettings : public QWidget { Q_OBJECT public: explicit AntiVignettingSettings(QWidget* parent); ~AntiVignettingSettings(); AntiVignettingContainer defaultSettings() const; void resetToDefault(); - AntiVignettingContainer settings() const; + AntiVignettingContainer settings() const; void setSettings(const AntiVignettingContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); void setMaskPreviewPixmap(const QPixmap& pix); Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotSettingsChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ANTI_VIGNETTING_SETTINGS_H diff --git a/core/libs/dimg/filters/lens/lensdistortionfilter.cpp b/core/libs/dimg/filters/lens/lensdistortionfilter.cpp index 7448c57b3a..c7a4e42abb 100644 --- a/core/libs/dimg/filters/lens/lensdistortionfilter.cpp +++ b/core/libs/dimg/filters/lens/lensdistortionfilter.cpp @@ -1,189 +1,190 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : lens distortion algorithm. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2001-2003 by David Hodson * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "lensdistortionfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "lensdistortionpixelaccess.h" namespace Digikam { LensDistortionFilter::LensDistortionFilter(QObject* const parent) - : DImgThreadedFilter(parent) + : DImgThreadedFilter(parent), + m_main(0.0), + m_edge(0.0), + m_rescale(0.0), + m_brighten(0.0), + m_centre_x(0), + m_centre_y(0) { - m_main = 0.0; - m_edge = 0.0; - m_rescale = 0.0; - m_brighten = 0.0; - m_centre_x = 0; - m_centre_y = 0; - initFilter(); } LensDistortionFilter::LensDistortionFilter(DImg* const orgImage, QObject* const parent, double main, double edge, double rescale, double brighten, int center_x, int center_y) - : DImgThreadedFilter(orgImage, parent, QLatin1String("LensDistortionFilter")) + : DImgThreadedFilter(orgImage, parent, QLatin1String("LensDistortionFilter")), + m_main(main), + m_edge(edge), + m_rescale(rescale), + m_brighten(brighten), + m_centre_x(center_x), + m_centre_y(center_y) { - m_main = main; - m_edge = edge; - m_rescale = rescale; - m_brighten = brighten; - m_centre_x = center_x; - m_centre_y = center_y; initFilter(); } LensDistortionFilter::~LensDistortionFilter() { cancelFilter(); } QString LensDistortionFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Lens Distortion Tool")); } void LensDistortionFilter::filterImage() { int Width = m_orgImage.width(); int Height = m_orgImage.height(); int bytesDepth = m_orgImage.bytesDepth(); uchar* data = m_destImage.bits(); // initial copy m_destImage.bitBltImage(&m_orgImage, 0, 0); // initialize coefficients double normallise_radius_sq = 4.0 / (Width * Width + Height * Height); double center_x = Width * (100.0 + m_centre_x) / 200.0; double center_y = Height * (100.0 + m_centre_y) / 200.0; double mult_sq = m_main / 200.0; double mult_qd = m_edge / 200.0; double rescale = pow(2.0, - m_rescale / 100.0); double brighten = - m_brighten / 10.0; LensDistortionPixelAccess* pa = new LensDistortionPixelAccess(&m_orgImage); /* * start at image (i, j), increment by (step, step) * output goes to dst, which is w x h x d in size * NB: d <= image.bpp */ // We are working on the full image. + int dstWidth = Width; int dstHeight = Height; uchar* dst = (uchar*)data; int step = 1, progress; int iLimit, jLimit; double srcX, srcY, mag; iLimit = dstWidth * step; jLimit = dstHeight * step; for (int dstJ = 0 ; runningFlag() && (dstJ < jLimit) ; dstJ += step) { for (int dstI = 0 ; runningFlag() && (dstI < iLimit) ; dstI += step) { // Get source Coordinates. + double radius_sq; double off_x; double off_y; double radius_mult; off_x = dstI - center_x; off_y = dstJ - center_y; radius_sq = (off_x * off_x) + (off_y * off_y); radius_sq *= normallise_radius_sq; radius_mult = radius_sq * mult_sq + radius_sq * radius_sq * mult_qd; mag = radius_mult; radius_mult = rescale * (1.0 + radius_mult); srcX = center_x + radius_mult * off_x; srcY = center_y + radius_mult * off_y; - brighten = 1.0 + mag * brighten; + brighten = 1.0 + mag * brighten; pa->pixelAccessGetCubic(srcX, srcY, brighten, dst); - dst += bytesDepth; + dst += bytesDepth; } // Update progress bar in dialog. progress = (int)(((double)dstJ * 100.0) / jLimit); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } delete pa; } FilterAction LensDistortionFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("brighten"), m_brighten); action.addParameter(QLatin1String("centre_x"), m_centre_x); action.addParameter(QLatin1String("centre_y"), m_centre_y); action.addParameter(QLatin1String("edge"), m_edge); action.addParameter(QLatin1String("main"), m_main); action.addParameter(QLatin1String("rescale"), m_rescale); return action; } void LensDistortionFilter::readParameters(const Digikam::FilterAction& action) { m_brighten = action.parameter(QLatin1String("brighten")).toDouble(); m_centre_x = action.parameter(QLatin1String("centre_x")).toInt(); m_centre_y = action.parameter(QLatin1String("centre_y")).toInt(); m_edge = action.parameter(QLatin1String("edge")).toDouble(); m_main = action.parameter(QLatin1String("main")).toDouble(); m_rescale = action.parameter(QLatin1String("rescale")).toDouble(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/lens/lensdistortionfilter.h b/core/libs/dimg/filters/lens/lensdistortionfilter.h index 14f4131355..37a3a0f37b 100644 --- a/core/libs/dimg/filters/lens/lensdistortionfilter.h +++ b/core/libs/dimg/filters/lens/lensdistortionfilter.h @@ -1,91 +1,91 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : lens distortion algorithm. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2001-2003 by David Hodson * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_LENS_DISTORTION_FILTER_H #define DIGIKAM_LENS_DISTORTION_FILTER_H // Local includes #include "dimgthreadedfilter.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT LensDistortionFilter : public DImgThreadedFilter { public: explicit LensDistortionFilter(QObject* const parent = nullptr); explicit LensDistortionFilter(DImg* const orgImage, QObject* const parent=nullptr, double main=0.0, double edge=0.0, double rescale=0.0, double brighten=0.0, int center_x=0, int center_y=0); ~LensDistortionFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:LensDistortionFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; private: int m_centre_x; int m_centre_y; double m_main; double m_edge; double m_rescale; double m_brighten; }; } // namespace Digikam #endif // DIGIKAM_LENS_DISTORTION_FILTER_H diff --git a/core/libs/dimg/filters/lens/lensdistortionpixelaccess.cpp b/core/libs/dimg/filters/lens/lensdistortionpixelaccess.cpp index 12f2d5d2a4..4238bc2fdd 100644 --- a/core/libs/dimg/filters/lens/lensdistortionpixelaccess.cpp +++ b/core/libs/dimg/filters/lens/lensdistortionpixelaccess.cpp @@ -1,333 +1,332 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-12-27 * Description : access pixels method for lens distortion algorithm. * * Copyright (C) 2004-2020 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "lensdistortionpixelaccess.h" // C++ includes #include #include #include namespace Digikam { LensDistortionPixelAccess::LensDistortionPixelAccess(DImg* srcImage) + : m_image(srcImage), + m_width(LensDistortionPixelAccessWidth), + m_height(LensDistortionPixelAccessHeight), + m_depth(m_image->bytesDepth()), + m_imageWidth(m_image->width()), + m_imageHeight(m_image->height()), + m_sixteenBit(m_image->sixteenBit()) { - m_image = srcImage; - - m_width = LensDistortionPixelAccessWidth; - m_height = LensDistortionPixelAccessHeight; - - m_depth = m_image->bytesDepth(); - m_imageWidth = m_image->width(); - m_imageHeight = m_image->height(); - m_sixteenBit = m_image->sixteenBit(); - for (int i = 0 ; i < LensDistortionPixelAccessRegions ; ++i) { - m_buffer[i] = new DImg(m_image->copy(0, 0, m_width, m_height)); + m_buffer[i] = new DImg(m_image->copy(0, 0, m_width, m_height)); m_tileMinX[i] = 1; m_tileMaxX[i] = m_width - 2; m_tileMinY[i] = 1; m_tileMaxY[i] = m_height - 2; } } LensDistortionPixelAccess::~LensDistortionPixelAccess() { for (int i = 0 ; i < LensDistortionPixelAccessRegions ; ++i) { delete m_buffer[i]; } } uchar* LensDistortionPixelAccess::pixelAccessAddress(int i, int j) { - return m_buffer[0]->bits() + m_depth * (m_width * (j + 1 - m_tileMinY[0]) + (i + 1 - m_tileMinX[0])); + return (m_buffer[0]->bits() + m_depth * (m_width * (j + 1 - m_tileMinY[0]) + (i + 1 - m_tileMinX[0]))); } // Swap region[n] with region[0]. void LensDistortionPixelAccess::pixelAccessSelectRegion(int n) { DImg* temp = nullptr; int a, b, c, d; int i; temp = m_buffer[n]; a = m_tileMinX[n]; b = m_tileMaxX[n]; c = m_tileMinY[n]; d = m_tileMaxY[n]; for (i = n ; i > 0 ; --i) { m_buffer[i] = m_buffer[i - 1]; m_tileMinX[i] = m_tileMinX[i - 1]; m_tileMaxX[i] = m_tileMaxX[i - 1]; m_tileMinY[i] = m_tileMinY[i - 1]; m_tileMaxY[i] = m_tileMaxY[i - 1]; } m_buffer[0] = temp; m_tileMinX[0] = a; m_tileMaxX[0] = b; m_tileMinY[0] = c; m_tileMaxY[0] = d; } -// Buffer[0] is cleared, should start at [i, j], fill rows that overlap image. +// NOTE: Buffer[0] is cleared, should start at [i, j], fill rows that overlap image. void LensDistortionPixelAccess::pixelAccessDoEdge(int i, int j) { int lineStart, lineEnd; int rowStart, rowEnd; int lineWidth; uchar* line = nullptr; lineStart = i; if (lineStart < 0) { lineStart = 0; } lineEnd = i + m_width; if (lineEnd > m_imageWidth) { lineEnd = m_imageWidth; } lineWidth = lineEnd - lineStart; if (lineStart >= lineEnd) { return; } rowStart = j; if (rowStart < 0) { rowStart = 0; } rowEnd = j + m_height; if (rowEnd > m_imageHeight) { rowEnd = m_imageHeight; } for (int y = rowStart ; y < rowEnd ; ++y) { line = pixelAccessAddress(lineStart, y); memcpy(line, m_image->scanLine(y) + lineStart * m_depth, lineWidth * m_depth); } } // Moves buffer[0] so that [x, y] is inside it. void LensDistortionPixelAccess::pixelAccessReposition(int xInt, int yInt) { int newStartX = xInt - LensDistortionPixelAccessXOffset; int newStartY = yInt - LensDistortionPixelAccessYOffset; m_tileMinX[0] = newStartX + 1; m_tileMaxX[0] = newStartX + m_width - 2; m_tileMinY[0] = newStartY + 1; m_tileMaxY[0] = newStartY + m_height - 2; - if ((newStartX < 0) || ((newStartX + m_width) >= m_imageWidth) || + if ((newStartX < 0) || ((newStartX + m_width) >= m_imageWidth) || (newStartY < 0) || ((newStartY + m_height) >= m_imageHeight)) { // some data is off edge of image m_buffer[0]->fill(DColor(0, 0, 0, 0, m_sixteenBit)); // This could probably be done by bitBltImage but I did not figure out how, // so leave the working code here. And no, it is not this: - //m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); - - if (((newStartX + m_width) < 0) || (newStartX >= m_imageWidth) || +/* + m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); +*/ + if (((newStartX + m_width) < 0) || (newStartX >= m_imageWidth) || ((newStartY + m_height) < 0) || (newStartY >= m_imageHeight)) { // totally outside, just leave it. } else { pixelAccessDoEdge(newStartX, newStartY); } } else { m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); } } void LensDistortionPixelAccess::pixelAccessGetCubic(double srcX, double srcY, double brighten, uchar* dst) { int xInt, yInt; double dx, dy; uchar* corner = nullptr; - xInt = (int)floor(srcX); - dx = srcX - xInt; - yInt = (int)floor(srcY); - dy = srcY - yInt; + xInt = (int)floor(srcX); + dx = srcX - xInt; + yInt = (int)floor(srcY); + dy = srcY - yInt; // We need 4x4 pixels, xInt-1 to xInt+2 horz, yInt-1 to yInt+2 vert // they're probably in the last place we looked... if ((xInt >= m_tileMinX[0]) && (xInt < m_tileMaxX[0]) && (yInt >= m_tileMinY[0]) && (yInt < m_tileMaxY[0])) { corner = pixelAccessAddress(xInt - 1, yInt - 1); cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); return; } // Or maybe it was a while back... for (int i = 1 ; i < LensDistortionPixelAccessRegions ; ++i) { if ((xInt >= m_tileMinX[i]) && (xInt < m_tileMaxX[i]) && (yInt >= m_tileMinY[i]) && (yInt < m_tileMaxY[i])) { // Check here first next time pixelAccessSelectRegion(i); corner = pixelAccessAddress(xInt - 1, yInt - 1); cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); return; } } // Nope, recycle an old region. pixelAccessSelectRegion(LensDistortionPixelAccessRegions - 1); pixelAccessReposition(xInt, yInt); corner = pixelAccessAddress(xInt - 1, yInt - 1); cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); } /* * Catmull-Rom cubic interpolation * * equally spaced points p0, p1, p2, p3 * interpolate 0 <= u < 1 between p1 and p2 * * (1 u u^2 u^3) ( 0.0 1.0 0.0 0.0 ) (p0) * ( -0.5 0.0 0.5 0.0 ) (p1) * ( 1.0 -2.5 2.0 -0.5 ) (p2) * ( -0.5 1.5 -1.5 0.5 ) (p3) * */ void LensDistortionPixelAccess::cubicInterpolate(uchar* src, int rowStride, uchar* dst, - bool sixteenBit, double dx, double dy, double brighten) + bool sixteenBit, double dx, double dy, double brighten) { float um1, u, up1, up2; float vm1, v, vp1, vp2; int c; const int numberOfComponents = 4; float verts[4 * numberOfComponents]; um1 = ((-0.5 * dx + 1.0) * dx - 0.5) * dx; u = (1.5 * dx - 2.5) * dx * dx + 1.0; up1 = ((-1.5 * dx + 2.0) * dx + 0.5) * dx; up2 = (0.5 * dx - 0.5) * dx * dx; vm1 = ((-0.5 * dy + 1.0) * dy - 0.5) * dy; v = (1.5 * dy - 2.5) * dy * dy + 1.0; vp1 = ((-1.5 * dy + 2.0) * dy + 0.5) * dy; vp2 = (0.5 * dy - 0.5) * dy * dy; if (sixteenBit) { unsigned short* src16 = reinterpret_cast(src); unsigned short* dst16 = reinterpret_cast(dst); // for each component, read the values of 4 pixels into array for (c = 0 ; c < 4 * numberOfComponents ; ++c) { verts[c] = vm1 * src16[c] + v * src16[c + rowStride] + vp1 * src16[c + rowStride * 2] + vp2 * src16[c + rowStride * 3]; } // for each component, compute resulting value from array for (c = 0 ; c < numberOfComponents ; ++c) { float result; - result = um1 * verts[c] + u * verts[c + numberOfComponents] - + up1 * verts[c + numberOfComponents * 2] + up2 * verts[c + numberOfComponents * 3]; + result = um1 * verts[c] + u * verts[c + numberOfComponents] + + up1 * verts[c + numberOfComponents * 2] + up2 * verts[c + numberOfComponents * 3]; result *= brighten; - if (result < 0.0) + if (result < 0.0) { dst16[c] = 0; } else if (result > 65535.0) { dst16[c] = 65535; } else { dst16[c] = (uint)result; } } } else { for (c = 0 ; c < 4 * numberOfComponents ; ++c) { verts[c] = vm1 * src[c] + v * src[c + rowStride] + vp1 * src[c + rowStride * 2] + vp2 * src[c + rowStride * 3]; } for (c = 0 ; c < numberOfComponents ; ++c) { float result; - result = um1 * verts[c] + u * verts[c + numberOfComponents] - + up1 * verts[c + numberOfComponents * 2] + up2 * verts[c + numberOfComponents * 3]; + result = um1 * verts[c] + u * verts[c + numberOfComponents] + + up1 * verts[c + numberOfComponents * 2] + up2 * verts[c + numberOfComponents * 3]; + result *= brighten; - if (result < 0.0) + if (result < 0.0) { dst[c] = 0; } else if (result > 255.0) { dst[c] = 255; } else { dst[c] = (uint)result; } } } } } // namespace Digikam diff --git a/core/libs/dimg/filters/lens/lensdistortionpixelaccess.h b/core/libs/dimg/filters/lens/lensdistortionpixelaccess.h index 721cc406ae..d5b13f4d2e 100644 --- a/core/libs/dimg/filters/lens/lensdistortionpixelaccess.h +++ b/core/libs/dimg/filters/lens/lensdistortionpixelaccess.h @@ -1,94 +1,95 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-12-27 * Description : access pixels method for lens distortion algorithm. * * Copyright (C) 2004-2020 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_LENS_DISTORTION_PIXEL_ACCESS_H #define DIGIKAM_LENS_DISTORTION_PIXEL_ACCESS_H #define LensDistortionPixelAccessRegions 20 #define LensDistortionPixelAccessWidth 40 #define LensDistortionPixelAccessHeight 20 #define LensDistortionPixelAccessXOffset 3 #define LensDistortionPixelAccessYOffset 3 // Local includes #include "dimg.h" #include "digikam_export.h" namespace Digikam { -/** LensDistortionPixelAccess class: solving the eternal problem: random, cubic-interpolated, +/** + * LensDistortionPixelAccess class: solving the eternal problem: random, cubic-interpolated, * sub-pixel coordinate access to an image. * Assuming that accesses are at least slightly coherent, * LensDistortionPixelAccess keeps LensDistortionPixelAccessRegions buffers, each containing a * LensDistortionPixelAccessWidth x LensDistortionPixelAccessHeight region of pixels. * Buffer[0] is always checked first, so move the last accessed * region into that position. * When a request arrives which is outside all the regions, * get a new region. * The new region is placed so that the requested pixel is positioned * at [LensDistortionPixelAccessXOffset, LensDistortionPixelAccessYOffset] in the region. */ class DIGIKAM_EXPORT LensDistortionPixelAccess { public: explicit LensDistortionPixelAccess(DImg* srcImage); ~LensDistortionPixelAccess(); void pixelAccessGetCubic(double srcX, double srcY, double brighten, uchar* dst); protected: inline uchar* pixelAccessAddress(int i, int j); void pixelAccessSelectRegion(int n); void pixelAccessDoEdge(int i, int j); void pixelAccessReposition(int xInt, int yInt); void cubicInterpolate(uchar* src, int rowStride, uchar* dst, bool sixteenBit, double dx, double dy, double brighten); private: DImg* m_image; DImg* m_buffer[LensDistortionPixelAccessRegions]; int m_width; int m_height; int m_depth; int m_imageWidth; int m_imageHeight; bool m_sixteenBit; int m_tileMinX[LensDistortionPixelAccessRegions]; int m_tileMaxX[LensDistortionPixelAccessRegions]; int m_tileMinY[LensDistortionPixelAccessRegions]; int m_tileMaxY[LensDistortionPixelAccessRegions]; }; } // namespace Digikam #endif // DIGIKAM_LENS_DISTORTION_PIXEL_ACCESS_H diff --git a/core/libs/dimg/filters/lens/lensfuncameraselector.cpp b/core/libs/dimg/filters/lens/lensfuncameraselector.cpp index 481eab6c47..c92e39807a 100644 --- a/core/libs/dimg/filters/lens/lensfuncameraselector.cpp +++ b/core/libs/dimg/filters/lens/lensfuncameraselector.cpp @@ -1,826 +1,832 @@ /* ============================================================ * * Date : 2008-02-10 * Description : a tool to fix automatically camera lens aberrations * * Copyright (C) 2008 by Adrian Schroeter * Copyright (C) 2008-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "lensfuncameraselector.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dlayoutbox.h" #include "digikam_debug.h" #include "squeezedcombobox.h" #include "dnuminput.h" #include "dexpanderbox.h" // Disable deprecated API from Lensfun. #if defined(Q_CC_GNU) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif namespace Digikam { class Q_DECL_HIDDEN LensFunCameraSelector::Private { public: explicit Private() - : configUseMetadata(QLatin1String("UseMetadata")), + : passiveMetadataUsage(false), + metadataUsage(nullptr), + metadataResult(nullptr), + makeLabel(nullptr), + modelLabel(nullptr), + lensLabel(nullptr), + focalLabel(nullptr), + aperLabel(nullptr), + distLabel(nullptr), + configUseMetadata(QLatin1String("UseMetadata")), configCameraModel(QLatin1String("CameraModel")), configCameraMake(QLatin1String("CameraMake")), configLensModel(QLatin1String("LensModel")), configSubjectDistance(QLatin1String("SubjectDistance")), configFocalLength(QLatin1String("FocalLength")), configCropFactor(QLatin1String("CropFactor")), configAperture(QLatin1String("Aperture")), redStyle(QLatin1String("QLabel {color: red;}")), orangeStyle(QLatin1String("QLabel {color: orange;}")), - greenStyle(QLatin1String("QLabel {color: green;}")) + greenStyle(QLatin1String("QLabel {color: green;}")), + lensDescription(nullptr), + makeDescription(nullptr), + modelDescription(nullptr), + make(nullptr), + model(nullptr), + lens(nullptr), + focal(nullptr), + aperture(nullptr), + distance(nullptr), + iface(nullptr) { - metadataUsage = nullptr; - make = nullptr; - model = nullptr; - lens = nullptr; - focal = nullptr; - aperture = nullptr; - distance = nullptr; - iface = nullptr; - metadataResult = nullptr; - makeLabel = nullptr; - modelLabel = nullptr; - lensLabel = nullptr; - focalLabel = nullptr; - aperLabel = nullptr; - distLabel = nullptr; - lensDescription = nullptr; - makeDescription = nullptr; - modelDescription = nullptr; - passiveMetadataUsage = false; } bool passiveMetadataUsage; QCheckBox* metadataUsage; QLabel* metadataResult; QLabel* makeLabel; QLabel* modelLabel; QLabel* lensLabel; QLabel* focalLabel; QLabel* aperLabel; QLabel* distLabel; const QString configUseMetadata; const QString configCameraModel; const QString configCameraMake; const QString configLensModel; const QString configSubjectDistance; const QString configFocalLength; const QString configCropFactor; const QString configAperture; const QString redStyle; const QString orangeStyle; const QString greenStyle; DAdjustableLabel* lensDescription; DAdjustableLabel* makeDescription; DAdjustableLabel* modelDescription; SqueezedComboBox* make; SqueezedComboBox* model; SqueezedComboBox* lens; DDoubleNumInput* focal; DDoubleNumInput* aperture; DDoubleNumInput* distance; DMetadata metadata; LensFunIface* iface; }; LensFunCameraSelector::LensFunCameraSelector(QWidget* const parent) - : QWidget(parent), d(new Private) + : QWidget(parent), + d(new Private) { d->iface = new LensFunIface(); QGridLayout* const grid = new QGridLayout(this); DHBox* const hbox = new DHBox(this); d->metadataUsage = new QCheckBox(i18n("Use Metadata"), hbox); QLabel* const space = new QLabel(hbox); d->metadataResult = new QLabel(hbox); hbox->setStretchFactor(space, 10); DHBox* const hbox1 = new DHBox(this); d->makeLabel = new QLabel(i18nc("camera make", "Make:"), hbox1); QLabel* const space1 = new QLabel(hbox1); d->makeDescription = new DAdjustableLabel(hbox1); hbox1->setStretchFactor(space1, 10); d->makeDescription->setAlignment(Qt::AlignVCenter | Qt::AlignRight); d->makeDescription->setWhatsThis(i18n("This is the camera maker description string found in image meta-data. " "This one is used to query and find relevant camera device information from Lensfun database.")); d->make = new SqueezedComboBox(this); d->make->setCurrentIndex(0); DHBox* const hbox2 = new DHBox(this); d->modelLabel = new QLabel(i18nc("camera model", "Model:"), hbox2); QLabel* const space2 = new QLabel(hbox2); d->modelDescription = new DAdjustableLabel(hbox2); hbox2->setStretchFactor(space2, 10); d->modelDescription->setAlignment(Qt::AlignVCenter | Qt::AlignRight); d->modelDescription->setWhatsThis(i18n("This is the camera model description string found in image meta-data. " "This one is used to query and found relevant camera device information from Lensfun database.")); d->model = new SqueezedComboBox(this); d->model->setCurrentIndex(0); DHBox* const hbox3 = new DHBox(this); d->lensLabel = new QLabel(i18nc("camera lens", "Lens:"), hbox3); QLabel* const space3 = new QLabel(hbox3); d->lensDescription = new DAdjustableLabel(hbox3); // Workaround for layout problem with long lens names. d->lensDescription->setAdjustedText(QString(40, QLatin1Char(' '))); d->lensDescription->setAlignment(Qt::AlignVCenter | Qt::AlignRight); d->lensDescription->setWhatsThis(i18n("This is the lens description string found in image meta-data. " "This one is used to query and found relevant lens information from Lensfun database.")); hbox3->setStretchFactor(space3, 10); d->lens = new SqueezedComboBox(this); d->lens->setCurrentIndex(0); d->metadataUsage->setEnabled(false); d->metadataUsage->setCheckState(Qt::Unchecked); d->metadataUsage->setWhatsThis(i18n("Set this option to try to guess the right camera/lens settings " "from the image metadata (as Exif or XMP).")); d->focalLabel = new QLabel(i18n("Focal Length (mm):"), this); d->aperLabel = new QLabel(i18n("Aperture:"), this); d->distLabel = new QLabel(i18n("Subject Distance (m):"), this); d->focal = new DDoubleNumInput(this); d->focal->setDecimals(1); d->focal->setRange(1.0, 10000.0, 0.01); d->focal->setDefaultValue(1.0); d->aperture = new DDoubleNumInput(this); d->aperture->setDecimals(1); d->aperture->setRange(1.1, 256.0, 0.1); d->aperture->setDefaultValue(1.1); d->distance = new DDoubleNumInput(this); d->distance->setDecimals(1); d->distance->setRange(0.0, 10000.0, 0.1); d->distance->setDefaultValue(0.0); grid->addWidget(hbox, 0, 0, 1, 3); grid->addWidget(hbox1, 1, 0, 1, 3); grid->addWidget(d->make, 2, 0, 1, 3); grid->addWidget(hbox2, 3, 0, 1, 3); grid->addWidget(d->model, 4, 0, 1, 3); grid->addWidget(hbox3, 5, 0, 1, 3); grid->addWidget(d->lens, 6, 0, 1, 3); grid->addWidget(d->focalLabel, 7, 0, 1, 1); grid->addWidget(d->focal, 7, 1, 1, 2); grid->addWidget(d->aperLabel, 8, 0, 1, 1); grid->addWidget(d->aperture, 8, 1, 1, 2); grid->addWidget(d->distLabel, 9, 0, 1, 1); grid->addWidget(d->distance, 9, 1, 1, 2); grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); grid->setContentsMargins(QMargins()); connect(d->metadataUsage, SIGNAL(toggled(bool)), this, SLOT(slotUseMetadata(bool))); connect(d->make, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMakeSelected())); connect(d->model, SIGNAL(currentIndexChanged(int)), this, SLOT(slotModelChanged())); connect(d->lens, SIGNAL(currentIndexChanged(int)), this, SLOT(slotLensSelected())); connect(d->focal, SIGNAL(valueChanged(double)), this, SLOT(slotFocalChanged())); connect(d->aperture, SIGNAL(valueChanged(double)), this, SLOT(slotApertureChanged())); connect(d->distance, SIGNAL(valueChanged(double)), this, SLOT(slotDistanceChanged())); } LensFunCameraSelector::~LensFunCameraSelector() { delete d->iface; delete d; } LensFunIface* LensFunCameraSelector::iface() const { return d->iface; } LensFunContainer LensFunCameraSelector::settings() { // Update settings in LensFun interface blockSignals(true); slotModelSelected(); slotLensSelected(); slotFocalChanged(); slotApertureChanged(); slotDistanceChanged(); blockSignals(false); return d->iface->settings(); } void LensFunCameraSelector::setSettings(const LensFunContainer& settings) { blockSignals(true); d->iface->setSettings(settings); refreshSettingsView(); blockSignals(false); } void LensFunCameraSelector::resetToDefault() { setUseMetadata(true); } void LensFunCameraSelector::readSettings(KConfigGroup& group) { setUseMetadata(group.readEntry(d->configUseMetadata, true)); if (!useMetadata()) { LensFunContainer settings = d->iface->settings(); settings.cameraModel = group.readEntry(d->configCameraModel, QString()); settings.cameraMake = group.readEntry(d->configCameraMake, QString()); settings.lensModel = group.readEntry(d->configLensModel, QString()); if (settings.subjectDistance <= 0.0) { settings.subjectDistance = group.readEntry(d->configSubjectDistance, -1.0); } if (settings.focalLength <= 0.0) { settings.focalLength = group.readEntry(d->configFocalLength, -1.0); } settings.cropFactor = group.readEntry(d->configCropFactor, -1.0); if (settings.aperture <= 0.0) { settings.aperture = group.readEntry(d->configAperture, -1.0); } setSettings(settings); } slotUseMetadata(useMetadata()); } void LensFunCameraSelector::writeSettings(KConfigGroup& group) { group.writeEntry(d->configUseMetadata, useMetadata()); group.writeEntry(d->configCameraModel, d->iface->settings().cameraModel); group.writeEntry(d->configCameraMake, d->iface->settings().cameraMake); group.writeEntry(d->configLensModel, d->iface->settings().lensModel); group.writeEntry(d->configSubjectDistance, d->iface->settings().subjectDistance); group.writeEntry(d->configFocalLength, d->iface->settings().focalLength); group.writeEntry(d->configCropFactor, d->iface->settings().cropFactor); group.writeEntry(d->configAperture, d->iface->settings().aperture); } void LensFunCameraSelector::setMetadata(const DMetadata& meta) { d->metadata = meta; } void LensFunCameraSelector::setEnabledUseMetadata(bool b) { d->metadataUsage->setEnabled(b); } void LensFunCameraSelector::setUseMetadata(bool b) { d->metadataUsage->setChecked(b); } bool LensFunCameraSelector::useMetadata() const { return (d->metadataUsage->isChecked()); } void LensFunCameraSelector::setPassiveMetadataUsage(bool b) { d->passiveMetadataUsage = b; } void LensFunCameraSelector::slotUseMetadata(bool b) { d->makeDescription->setAdjustedText(); d->modelDescription->setAdjustedText(); d->lensDescription->setAdjustedText(); d->metadataResult->clear(); d->makeLabel->setStyleSheet(qApp->styleSheet()); d->modelLabel->setStyleSheet(qApp->styleSheet()); d->lensLabel->setStyleSheet(qApp->styleSheet()); d->focalLabel->setStyleSheet(qApp->styleSheet()); d->aperLabel->setStyleSheet(qApp->styleSheet()); d->distLabel->setStyleSheet(qApp->styleSheet()); d->make->setEnabled(true); d->model->setEnabled(true); d->lens->setEnabled(true); d->focal->setEnabled(true); d->aperture->setEnabled(true); d->distance->setEnabled(true); if (b) { if (d->passiveMetadataUsage) { d->make->setEnabled(false); d->model->setEnabled(false); d->lens->setEnabled(false); d->focal->setEnabled(false); d->aperture->setEnabled(false); d->distance->setEnabled(false); emit signalLensSettingsChanged(); } else { LensFunIface::MetadataMatch ret = findFromMetadata(); switch (ret) { case LensFunIface::MetadataUnavailable: d->metadataResult->setText(i18n("(no metadata available)")); d->metadataResult->setStyleSheet(d->redStyle); break; case LensFunIface::MetadataNoMatch: d->metadataResult->setText(i18n("(no match found)")); d->metadataResult->setStyleSheet(d->redStyle); break; case LensFunIface::MetadataPartialMatch: d->metadataResult->setText(i18n("(partial match found)")); d->metadataResult->setStyleSheet(d->orangeStyle); break; default: d->metadataResult->setText(i18n("(exact match found)")); d->metadataResult->setStyleSheet(d->greenStyle); break; } } } } LensFunIface::MetadataMatch LensFunCameraSelector::findFromMetadata() { LensFunIface::MetadataMatch ret = d->iface->findFromMetadata(d->metadata); refreshSettingsView(); slotModelSelected(); slotLensSelected(); + return ret; } void LensFunCameraSelector::refreshSettingsView() { d->make->blockSignals(true); d->model->blockSignals(true); d->lens->blockSignals(true); d->makeLabel->setStyleSheet(qApp->styleSheet()); d->modelLabel->setStyleSheet(qApp->styleSheet()); d->lensLabel->setStyleSheet(qApp->styleSheet()); d->focalLabel->setStyleSheet(qApp->styleSheet()); d->aperLabel->setStyleSheet(qApp->styleSheet()); d->distLabel->setStyleSheet(qApp->styleSheet()); if (!d->passiveMetadataUsage) { d->makeDescription->setAdjustedText(QString::fromLatin1("%1").arg(d->iface->makeDescription())); } int makerIdx = -1; if (d->iface->usedCamera()) { makerIdx = d->make->findText(d->iface->settings().cameraMake); qCDebug(DIGIKAM_DIMG_LOG) << "makerIdx: " << makerIdx << " (" << d->iface->settings().cameraMake << ")"; } else { int i = d->make->findText(d->iface->makeDescription()); if (i == -1) { i = d->make->findText(QLatin1String("Generic")); } if (i >= 0) { d->make->setCurrentIndex(i); populateDeviceCombos(); } if (!d->passiveMetadataUsage) { d->makeLabel->setStyleSheet(d->orangeStyle); } } if (makerIdx >= 0) { d->make->setCurrentIndex(makerIdx); d->make->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->makeLabel->setStyleSheet(d->greenStyle); } populateDeviceCombos(); } // ------------------------------------------------------------------------------------------------ if (!d->passiveMetadataUsage) { d->modelDescription->setAdjustedText(QString::fromLatin1("%1").arg(d->iface->modelDescription())); } int modelIdx = -1; if (d->iface->usedCamera()) { modelIdx = d->model->findText(d->iface->settings().cameraModel); qCDebug(DIGIKAM_DIMG_LOG) << "modelIdx: " << modelIdx << " (" << d->iface->settings().cameraModel << ")"; } if (modelIdx >= 0) { d->model->setCurrentIndex(modelIdx); d->model->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->modelLabel->setStyleSheet(d->greenStyle); } populateLensCombo(); } else { if (!d->passiveMetadataUsage) { d->modelLabel->setStyleSheet(d->orangeStyle); } } // ------------------------------------------------------------------------------------------------ if (!d->passiveMetadataUsage) { d->lensDescription->setAdjustedText(QString::fromLatin1("%1").arg(d->iface->lensDescription())); } int lensIdx = -1; if (d->iface->usedLens()) { lensIdx = d->lens->findText(d->iface->settings().lensModel); qCDebug(DIGIKAM_DIMG_LOG) << "lensIdx: " << lensIdx << " (" << d->iface->settings().lensModel << ")"; } if (lensIdx >= 0) { // found lens model directly, best case :) d->lens->setCurrentIndex(lensIdx); d->lens->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->lensLabel->setStyleSheet(d->greenStyle); } } else { if (!d->passiveMetadataUsage) { d->lensLabel->setStyleSheet(d->orangeStyle); } } // ------------------------------------------------------------------------------------------------ if (d->iface->settings().focalLength != -1.0) { d->focal->setValue(d->iface->settings().focalLength); d->focal->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->focalLabel->setStyleSheet(d->greenStyle); } } else { if (!d->passiveMetadataUsage) { d->focalLabel->setStyleSheet(d->orangeStyle); } } if (d->iface->settings().aperture != -1.0) { d->aperture->setValue(d->iface->settings().aperture); d->aperture->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->aperLabel->setStyleSheet(d->greenStyle); } } else { if (!d->passiveMetadataUsage) { d->aperLabel->setStyleSheet(d->orangeStyle); } } if (d->iface->settings().subjectDistance != -1.0) { d->distance->setValue(d->iface->settings().subjectDistance); d->distance->setEnabled(d->passiveMetadataUsage); if (!d->passiveMetadataUsage) { d->distLabel->setStyleSheet(d->greenStyle); } } else { if (!d->passiveMetadataUsage) { d->distLabel->setStyleSheet(d->orangeStyle); } } d->make->blockSignals(false); d->model->blockSignals(false); d->lens->blockSignals(false); } void LensFunCameraSelector::populateDeviceCombos() { d->make->blockSignals(true); d->model->blockSignals(true); const lfCamera* const* it = d->iface->lensFunCameras(); // reset box + d->model->clear(); bool firstRun = false; if (d->make->count() == 0) { firstRun = true; } while (*it) { if (firstRun) { // Maker DB does not change, so we fill it only once. + if ((*it)->Maker) { QString t = QLatin1String((*it)->Maker); if (d->make->findText(t, Qt::MatchExactly) < 0) { d->make->addSqueezedItem(t); } } } // Fill models for current selected maker if ((*it)->Model && QLatin1String((*it)->Maker) == d->make->itemHighlighted()) { LensFunIface::DevicePtr dev = *it; QVariant b = qVariantFromValue(dev); d->model->addSqueezedItem(QLatin1String(dev->Model), b); } ++it; } //d->make->model()->sort(0, Qt::AscendingOrder); //d->model->model()->sort(0, Qt::AscendingOrder); d->make->blockSignals(false); d->model->blockSignals(false); } void LensFunCameraSelector::populateLensCombo() { d->lens->blockSignals(true); d->lens->clear(); d->lens->blockSignals(false); QVariant v = d->model->itemData(d->model->currentIndex()); if (!v.isValid() || v.isNull()) { qCDebug(DIGIKAM_DIMG_LOG) << "Invalid variant value for device!"; return; } qCDebug(DIGIKAM_DIMG_LOG) << "variant: " << v; LensFunIface::DevicePtr dev = v.value(); if (!dev) { qCDebug(DIGIKAM_DIMG_LOG) << "Device is null!"; return; } qCDebug(DIGIKAM_DIMG_LOG) << "dev: " << dev->Maker << " :: " << dev->Model << " :: " << dev->CropFactor; d->lens->blockSignals(true); const lfLens** lenses = d->iface->lensFunDataBase()->FindLenses(dev, nullptr, nullptr); LensFunContainer settings = d->iface->settings(); settings.cropFactor = dev ? dev->CropFactor : -1.0; d->iface->setSettings(settings); QMultiMap lensMap; while (lenses && *lenses) { LensFunIface::LensPtr lens = *lenses; QVariant b = qVariantFromValue(lens); lensMap.insert(QLatin1String(lens->Model), b); ++lenses; } QMultiMap::ConstIterator it = lensMap.constBegin(); for ( ; it != lensMap.constEnd() ; ++it) { d->lens->addSqueezedItem(it.key(), it.value()); } - - //d->lens->model()->sort(0, Qt::AscendingOrder); +/* + d->lens->model()->sort(0, Qt::AscendingOrder); +*/ d->lens->blockSignals(false); } void LensFunCameraSelector::slotMakeSelected() { populateDeviceCombos(); slotModelSelected(); - // Fill Lens list for current Maker & Model and fire signalLensSettingsChanged() + // Fill Lens list for current Maker & Model and fire signalLensSettingsChanged(). + populateLensCombo(); slotLensSelected(); } void LensFunCameraSelector::slotModelChanged() { populateLensCombo(); slotModelSelected(); } void LensFunCameraSelector::slotModelSelected() { QVariant v = d->model->itemData(d->model->currentIndex()); d->iface->setUsedCamera(d->metadataUsage->isChecked() && d->passiveMetadataUsage ? 0 : v.value()); emit signalLensSettingsChanged(); } void LensFunCameraSelector::slotLensSelected() { QVariant v = d->lens->itemData(d->lens->currentIndex()); d->iface->setUsedLens(d->metadataUsage->isChecked() && d->passiveMetadataUsage ? 0 : v.value()); LensFunContainer settings = d->iface->settings(); if (d->iface->usedLens() && - settings.cropFactor <= 0.0) // this should not happen + (settings.cropFactor <= 0.0)) // this should not happen { qCDebug(DIGIKAM_DIMG_LOG) << "No crop factor is set for camera, using lens calibration data: " << d->iface->usedLens()->CropFactor; settings.cropFactor = d->iface->usedLens()->CropFactor; } d->iface->setSettings(settings); emit signalLensSettingsChanged(); } void LensFunCameraSelector::slotFocalChanged() { LensFunContainer settings = d->iface->settings(); - settings.focalLength = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 : - d->focal->value(); + settings.focalLength = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 + : d->focal->value(); d->iface->setSettings(settings); emit signalLensSettingsChanged(); } void LensFunCameraSelector::slotApertureChanged() { LensFunContainer settings = d->iface->settings(); - settings.aperture = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 : - d->aperture->value(); + settings.aperture = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 + : d->aperture->value(); d->iface->setSettings(settings); emit signalLensSettingsChanged(); } void LensFunCameraSelector::slotDistanceChanged() { LensFunContainer settings = d->iface->settings(); - settings.subjectDistance = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 : - d->distance->value(); + settings.subjectDistance = d->metadataUsage->isChecked() && d->passiveMetadataUsage ? -1.0 + : d->distance->value(); d->iface->setSettings(settings); emit signalLensSettingsChanged(); } void LensFunCameraSelector::showEvent(QShowEvent* event) { QWidget::showEvent(event); populateDeviceCombos(); populateLensCombo(); if (d->metadata.isEmpty()) { d->metadataUsage->setCheckState(Qt::Unchecked); setEnabledUseMetadata(false); } else { setEnabledUseMetadata(true); findFromMetadata(); } } } // namespace Digikam // Restore warnings #if defined(Q_CC_GNU) # pragma GCC diagnostic pop #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif diff --git a/core/libs/dimg/filters/lens/lensfuncameraselector.h b/core/libs/dimg/filters/lens/lensfuncameraselector.h index 7bdecd7605..f7f9e9c205 100644 --- a/core/libs/dimg/filters/lens/lensfuncameraselector.h +++ b/core/libs/dimg/filters/lens/lensfuncameraselector.h @@ -1,109 +1,109 @@ /* ============================================================ * * Date : 2008-02-10 * Description : a tool to fix automatically camera lens aberrations * * Copyright (C) 2008 by Adrian Schroeter * Copyright (C) 2008-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_LENS_FUN_CAMERA_SELECTOR_H #define DIGIKAM_LENS_FUN_CAMERA_SELECTOR_H // Qt includes #include // Local includes #include "dmetadata.h" #include "digikam_export.h" #include "lensfunfilter.h" #include "lensfuniface.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT LensFunCameraSelector : public QWidget { Q_OBJECT public: typedef QMap Device; public: explicit LensFunCameraSelector(QWidget* const parent=nullptr); ~LensFunCameraSelector(); void setEnabledUseMetadata(bool b); void setUseMetadata(bool b); - bool useMetadata() const; + bool useMetadata() const; LensFunContainer settings(); void setSettings(const LensFunContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); void resetToDefault(); /** Special mode used with BQM which processes multiple items at the same time. */ void setPassiveMetadataUsage(bool b); - LensFunIface* iface() const; + LensFunIface* iface() const; void setMetadata(const DMetadata&); Q_SIGNALS: void signalLensSettingsChanged(); private Q_SLOTS: void slotUseMetadata(bool); void slotMakeSelected(); void slotModelChanged(); void slotModelSelected(); void slotLensSelected(); void slotFocalChanged(); void slotApertureChanged(); void slotDistanceChanged(); private: LensFunIface::MetadataMatch findFromMetadata(); void refreshSettingsView(); void populateDeviceCombos(); void populateLensCombo(); protected: void showEvent(QShowEvent* event) override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_LENS_FUN_CAMERA_SELECTOR_H diff --git a/core/libs/dimg/filters/lens/lensfunfilter.cpp b/core/libs/dimg/filters/lens/lensfunfilter.cpp index 5ef0ced0f4..4da5e96040 100644 --- a/core/libs/dimg/filters/lens/lensfunfilter.cpp +++ b/core/libs/dimg/filters/lens/lensfunfilter.cpp @@ -1,426 +1,433 @@ /* ============================================================ * * Date : 2008-02-10 * Description : a tool to fix automatically camera lens aberrations * * Copyright (C) 2008 by Adrian Schroeter * Copyright (C) 2008-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "lensfunfilter.h" // Qt includes #include #include #include #include // krazy:exclude=includes // KDE includes #include // Local includes #include "digikam_debug.h" #include "lensfuniface.h" #include "dmetadata.h" // Disable deprecated API from Lensfun. #if defined(Q_CC_GNU) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif namespace Digikam { class Q_DECL_HIDDEN LensFunFilter::Private { public: explicit Private() + : iface(nullptr), + modifier(nullptr), + loop(0) { - iface = nullptr; - modifier = nullptr; - loop = 0; } DImg tempImage; LensFunIface* iface; lfModifier* modifier; int loop; }; LensFunFilter::LensFunFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { d->iface = new LensFunIface; initFilter(); } LensFunFilter::LensFunFilter(DImg* const orgImage, QObject* const parent, const LensFunContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("LensCorrection")), d(new Private) { d->iface = new LensFunIface; d->iface->setSettings(settings); initFilter(); } LensFunFilter::~LensFunFilter() { cancelFilter(); if (d->modifier) { d->modifier->Destroy(); } delete d->iface; delete d; } QString LensFunFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Lens Auto-Correction Tool")); } void LensFunFilter::filterCCAMultithreaded(uint start, uint stop) { QScopedArrayPointer pos(new float[m_orgImage.width() * 2 * 3]); - for (unsigned int y = start; runningFlag() && (y < stop); ++y) + for (unsigned int y = start ; runningFlag() && (y < stop) ; ++y) { if (d->modifier->ApplySubpixelDistortion(0.0, y, m_orgImage.width(), 1, pos.data())) { float* src = pos.data(); - for (unsigned x = 0; runningFlag() && (x < m_destImage.width()); ++x) + for (unsigned x = 0 ; runningFlag() && (x < m_destImage.width()) ; ++x) { DColor destPixel(0, 0, 0, 0xFFFF, m_destImage.sixteenBit()); destPixel.setRed(m_orgImage.getSubPixelColorFast(src[0], src[1]).red()); destPixel.setGreen(m_orgImage.getSubPixelColorFast(src[2], src[3]).green()); destPixel.setBlue(m_orgImage.getSubPixelColorFast(src[4], src[5]).blue()); m_destImage.setPixelColor(x, y, destPixel); src += 2 * 3; } } } } void LensFunFilter::filterVIGMultithreaded(uint start, uint stop) { uchar* data = m_destImage.bits(); data += m_destImage.width() * m_destImage.bytesDepth() * start; - for (unsigned int y = start; runningFlag() && (y < stop); ++y) + for (unsigned int y = start ; runningFlag() && (y < stop) ; ++y) { if (d->modifier->ApplyColorModification(data, 0.0, y, m_destImage.width(), 1, LF_CR_4(RED, GREEN, BLUE, UNKNOWN), 0)) { data += m_destImage.width() * m_destImage.bytesDepth(); } } } void LensFunFilter::filterDSTMultithreaded(uint start, uint stop) { QScopedArrayPointer pos(new float[m_orgImage.width() * 2 * 3]); - for (unsigned int y = start; runningFlag() && (y < stop); ++y) + for (unsigned int y = start ; runningFlag() && (y < stop) ; ++y) { if (d->modifier->ApplyGeometryDistortion(0.0, y, d->tempImage.width(), 1, pos.data())) { float* src = pos.data(); - for (unsigned int x = 0; runningFlag() && (x < d->tempImage.width()); ++x, ++d->loop) + for (unsigned int x = 0 ; runningFlag() && (x < d->tempImage.width()) ; ++x, ++d->loop) { d->tempImage.setPixelColor(x, y, m_destImage.getSubPixelColor(src[0], src[1])); src += 2; } } } } void LensFunFilter::filterImage() { m_destImage.bitBltImage(&m_orgImage, 0, 0); if (!d->iface) { qCDebug(DIGIKAM_DIMG_LOG) << "ERROR: LensFun Interface is null."; return; } if (!d->iface->usedLens()) { qCDebug(DIGIKAM_DIMG_LOG) << "ERROR: LensFun Interface Lens device is null."; return; } // Lensfun Modifier flags to process int modifyFlags = 0; if (d->iface->settings().filterDST) { modifyFlags |= LF_MODIFY_DISTORTION; } if (d->iface->settings().filterGEO) { modifyFlags |= LF_MODIFY_GEOMETRY; } if (d->iface->settings().filterCCA) { modifyFlags |= LF_MODIFY_TCA; } if (d->iface->settings().filterVIG) { modifyFlags |= LF_MODIFY_VIGNETTING; } // Init lensfun lib, we are working on the full image. lfPixelFormat colorDepth = m_orgImage.bytesDepth() == 4 ? LF_PF_U8 : LF_PF_U16; d->modifier = lfModifier::Create(d->iface->usedLens(), d->iface->settings().cropFactor, m_orgImage.width(), m_orgImage.height()); int modflags = d->modifier->Initialize(d->iface->usedLens(), colorDepth, d->iface->settings().focalLength, d->iface->settings().aperture, d->iface->settings().subjectDistance, 1.0, /* no scaling */ LF_RECTILINEAR, modifyFlags, - 0 /*no inverse*/); + 0 /*no inverse*/ + ); if (!d->modifier) { qCDebug(DIGIKAM_DIMG_LOG) << "ERROR: cannot initialize LensFun Modifier."; return; } // Calc necessary steps for progress bar int steps = ((d->iface->settings().filterCCA) ? 1 : 0) + ((d->iface->settings().filterVIG) ? 1 : 0) + ((d->iface->settings().filterDST || d->iface->settings().filterGEO) ? 1 : 0); qCDebug(DIGIKAM_DIMG_LOG) << "LensFun Modifier Flags: " << modflags << " Steps:" << steps; if (steps < 1) { qCDebug(DIGIKAM_DIMG_LOG) << "No LensFun Modifier steps. There is nothing to process..."; return; } qCDebug(DIGIKAM_DIMG_LOG) << "Image size to process: (" << m_orgImage.width() << ", " << m_orgImage.height() << ")"; QList vals = multithreadedSteps(m_destImage.height()); // Stage 1: Chromatic Aberration Corrections if (d->iface->settings().filterCCA) { m_orgImage.prepareSubPixelAccess(); // init lanczos kernel QList > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &LensFunFilter::filterCCAMultithreaded, vals[j], vals[j+1])); } foreach (QFuture t, tasks) + { t.waitForFinished(); + } qCDebug(DIGIKAM_DIMG_LOG) << "Chromatic Aberration Corrections applied."; } postProgress(30); // Stage 2: Color Corrections: Vignetting and Color Contribution Index if (d->iface->settings().filterVIG) { QList > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &LensFunFilter::filterVIGMultithreaded, vals[j], vals[j+1])); } foreach (QFuture t, tasks) + { t.waitForFinished(); + } qCDebug(DIGIKAM_DIMG_LOG) << "Vignetting and Color Corrections applied."; } postProgress(60); // Stage 3: Distortion and Geometry Corrections if (d->iface->settings().filterDST || d->iface->settings().filterGEO) { d->loop = 0; // we need a deep copy first d->tempImage = DImg(m_destImage.width(), m_destImage.height(), m_destImage.sixteenBit(), m_destImage.hasAlpha()); m_destImage.prepareSubPixelAccess(); // init lanczos kernel QList > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &LensFunFilter::filterDSTMultithreaded, vals[j], vals[j+1])); } foreach (QFuture t, tasks) + { t.waitForFinished(); + } qCDebug(DIGIKAM_DIMG_LOG) << "Distortion and Geometry Corrections applied."; if (d->loop) { m_destImage = d->tempImage; } } postProgress(90); } bool LensFunFilter::registerSettingsToXmp(MetaEngineData& data) const { // Register in digiKam Xmp namespace all information about Lens corrections. QString str; LensFunContainer prm = d->iface->settings(); str.append(i18n("Camera: %1-%2", prm.cameraMake, prm.cameraModel)); str.append(QLatin1Char('\n')); str.append(i18n("Lens: %1", prm.lensModel)); str.append(QLatin1Char('\n')); str.append(i18n("Subject Distance: %1", prm.subjectDistance)); str.append(QLatin1Char('\n')); str.append(i18n("Aperture: %1", prm.aperture)); str.append(QLatin1Char('\n')); str.append(i18n("Focal Length: %1", prm.focalLength)); str.append(QLatin1Char('\n')); str.append(i18n("Crop Factor: %1", prm.cropFactor)); str.append(QLatin1Char('\n')); str.append(i18n("CCA Correction: %1", prm.filterCCA && d->iface->supportsCCA() ? i18n("enabled") : i18n("disabled"))); str.append(QLatin1Char('\n')); str.append(i18n("VIG Correction: %1", prm.filterVIG && d->iface->supportsVig() ? i18n("enabled") : i18n("disabled"))); str.append(QLatin1Char('\n')); str.append(i18n("DST Correction: %1", prm.filterDST && d->iface->supportsDistortion() ? i18n("enabled") : i18n("disabled"))); str.append(QLatin1Char('\n')); str.append(i18n("GEO Correction: %1", prm.filterGEO && d->iface->supportsGeometry() ? i18n("enabled") : i18n("disabled"))); DMetadata meta(data); bool ret = meta.setXmpTagString("Xmp.digiKam.LensCorrectionSettings", str.replace(QLatin1Char('\n'), QLatin1String(" ; "))); data = meta.data(); return ret; } FilterAction LensFunFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); LensFunContainer prm = d->iface->settings(); action.addParameter(QLatin1String("ccaCorrection"), prm.filterCCA); action.addParameter(QLatin1String("vigCorrection"), prm.filterVIG); action.addParameter(QLatin1String("dstCorrection"), prm.filterDST); action.addParameter(QLatin1String("geoCorrection"), prm.filterGEO); action.addParameter(QLatin1String("cropFactor"), prm.cropFactor); action.addParameter(QLatin1String("focalLength"), prm.focalLength); action.addParameter(QLatin1String("aperture"), prm.aperture); action.addParameter(QLatin1String("subjectDistance"), prm.subjectDistance); action.addParameter(QLatin1String("cameraMake"), prm.cameraMake); action.addParameter(QLatin1String("cameraModel"), prm.cameraModel); action.addParameter(QLatin1String("lensModel"), prm.lensModel); return action; } void LensFunFilter::readParameters(const Digikam::FilterAction& action) { LensFunContainer prm = d->iface->settings(); prm.filterCCA = action.parameter(QLatin1String("ccaCorrection")).toBool(); prm.filterVIG = action.parameter(QLatin1String("vigCorrection")).toBool(); prm.filterDST = action.parameter(QLatin1String("dstCorrection")).toBool(); prm.filterGEO = action.parameter(QLatin1String("geoCorrection")).toBool(); prm.cropFactor = action.parameter(QLatin1String("cropFactor")).toDouble(); prm.focalLength = action.parameter(QLatin1String("focalLength")).toDouble(); prm.aperture = action.parameter(QLatin1String("aperture")).toDouble(); prm.subjectDistance = action.parameter(QLatin1String("subjectDistance")).toDouble(); prm.cameraMake = action.parameter(QLatin1String("cameraMake")).toString(); prm.cameraModel = action.parameter(QLatin1String("cameraModel")).toString(); prm.lensModel = action.parameter(QLatin1String("lensModel")).toString(); d->iface->setSettings(prm); } } // namespace Digikam // Restore warnings #if defined(Q_CC_GNU) # pragma GCC diagnostic pop #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif diff --git a/core/libs/dimg/filters/lens/lensfunfilter.h b/core/libs/dimg/filters/lens/lensfunfilter.h index 89f7c5719a..56f454ebc9 100644 --- a/core/libs/dimg/filters/lens/lensfunfilter.h +++ b/core/libs/dimg/filters/lens/lensfunfilter.h @@ -1,128 +1,130 @@ /* ============================================================ * * Date : 2008-02-10 * Description : a tool to fix automatically camera lens aberrations * * Copyright (C) 2008 by Adrian Schroeter * Copyright (C) 2008-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_LENS_FUN_FILTER_H #define DIGIKAM_LENS_FUN_FILTER_H // Local includes #include "digikam_config.h" #include "dimgthreadedfilter.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT LensFunContainer { public: explicit LensFunContainer() + : filterCCA(true), + filterVIG(true), + filterDST(true), + filterGEO(true), + cropFactor(-1.0), + focalLength(-1.0), + aperture(-1.0), + subjectDistance(-1.0), + cameraMake(QString()), + cameraModel(QString()), + lensModel(QString()) { - filterCCA = true; - filterVIG = true; - filterDST = true; - filterGEO = true; - focalLength = -1.0; - aperture = -1.0; - subjectDistance = -1.0; - cropFactor = -1.0; - cameraMake = QString(); - cameraModel = QString(); - lensModel = QString(); }; ~LensFunContainer() { }; public: - bool filterCCA; /// Chromatic Aberration Corrections - bool filterVIG; /// Vignetting Corrections - bool filterDST; /// Distortion Corrections - bool filterGEO; /// Geometry Corrections + bool filterCCA; ///< Chromatic Aberration Corrections + bool filterVIG; ///< Vignetting Corrections + bool filterDST; ///< Distortion Corrections + bool filterGEO; ///< Geometry Corrections double cropFactor; double focalLength; double aperture; double subjectDistance; QString cameraMake; QString cameraModel; QString lensModel; }; +// ----------------------------------------------------------------- + class DIGIKAM_EXPORT LensFunFilter : public DImgThreadedFilter { public: explicit LensFunFilter(QObject* const parent = nullptr); explicit LensFunFilter(DImg* const origImage, QObject* const parent, const LensFunContainer& settings); ~LensFunFilter(); bool registerSettingsToXmp(MetaEngineData& data) const; void readParameters(const FilterAction& action) override; static QString FilterIdentifier() { return QLatin1String("digikam:LensFunFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1 << 2; } static int CurrentVersion() { return 2; } virtual QString filterIdentifier() const override { return FilterIdentifier(); } virtual FilterAction filterAction() override; private: void filterImage() override; void filterCCAMultithreaded(uint start, uint stop); void filterVIGMultithreaded(uint start, uint stop); void filterDSTMultithreaded(uint start, uint stop); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_LENS_FUN_FILTER_H diff --git a/core/libs/dimg/filters/lens/lensfuniface.cpp b/core/libs/dimg/filters/lens/lensfuniface.cpp index cc9f20b6a7..08fa0aa04b 100644 --- a/core/libs/dimg/filters/lens/lensfuniface.cpp +++ b/core/libs/dimg/filters/lens/lensfuniface.cpp @@ -1,591 +1,607 @@ /* ============================================================ * * Date : 2008-02-10 * Description : a tool to fix automatically camera lens aberrations * * Copyright (C) 2008 by Adrian Schroeter * Copyright (C) 2008-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "lensfuniface.h" // Qt includes #include #include #include // Local includes #include "digikam_debug.h" // Disable deprecated API from Lensfun. #if defined(Q_CC_GNU) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif namespace Digikam { class Q_DECL_HIDDEN LensFunIface::Private { public: explicit Private() + : lfDb(nullptr), + lfCameras(nullptr), + usedLens(nullptr), + usedCamera(nullptr) { - usedLens = nullptr; - usedCamera = nullptr; - lfDb = nullptr; - lfCameras = nullptr; } // To be used for modification LensFunContainer settings; // Database items lfDatabase* lfDb; const lfCamera* const* lfCameras; QString makeDescription; QString modelDescription; QString lensDescription; LensPtr usedLens; DevicePtr usedCamera; }; LensFunIface::LensFunIface() : d(new Private) { d->lfDb = lf_db_new(); // Lensfun host XML files in a dedicated sub-directory. + QString lensPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("lensfun"), QStandardPaths::LocateDirectory); QDir lensDir; // In first try to use last Lensfun version data dir. + lensDir = QDir(lensPath + QLatin1String("/version_2"), QLatin1String("*.xml")); if (lensDir.entryList().isEmpty()) { // Fail-back to revision 1. lensDir = QDir(lensPath + QLatin1String("/version_1"), QLatin1String("*.xml")); if (lensDir.entryList().isEmpty()) { // Fail-back to revision 0 which host XML data in root data directory. + lensDir = QDir(lensPath, QLatin1String("*.xml")); } } qCDebug(DIGIKAM_DIMG_LOG) << "Using root lens database dir: " << lensPath; foreach (const QString& lens, lensDir.entryList()) { qCDebug(DIGIKAM_DIMG_LOG) << "Load lens database file: " << lens; d->lfDb->Load(QFile::encodeName(lensDir.absoluteFilePath(lens)).constData()); } d->lfDb->Load(); d->lfCameras = d->lfDb->GetCameras(); } LensFunIface::~LensFunIface() { lf_db_destroy(d->lfDb); delete d; } void LensFunIface::setSettings(const LensFunContainer& other) { d->settings = other; d->usedCamera = findCamera(d->settings.cameraMake, d->settings.cameraModel); d->usedLens = findLens(d->settings.lensModel); } LensFunContainer LensFunIface::settings() const { return d->settings; } LensFunIface::DevicePtr LensFunIface::usedCamera() const { return d->usedCamera; } void LensFunIface::setUsedCamera(DevicePtr cam) { d->usedCamera = cam; d->settings.cameraMake = d->usedCamera ? QLatin1String(d->usedCamera->Maker) : QString(); d->settings.cameraModel = d->usedCamera ? QLatin1String(d->usedCamera->Model) : QString(); d->settings.cropFactor = d->usedCamera ? d->usedCamera->CropFactor : -1.0; } LensFunIface::LensPtr LensFunIface::usedLens() const { return d->usedLens; } void LensFunIface::setUsedLens(LensPtr lens) { d->usedLens = lens; d->settings.lensModel = d->usedLens ? QLatin1String(d->usedLens->Model) : QString(); } lfDatabase* LensFunIface::lensFunDataBase() const { return d->lfDb; } QString LensFunIface::makeDescription() const { return d->makeDescription; } QString LensFunIface::modelDescription() const { return d->modelDescription; } QString LensFunIface::lensDescription() const { return d->lensDescription; } const lfCamera* const* LensFunIface::lensFunCameras() const { return d->lfCameras; } void LensFunIface::setFilterSettings(const LensFunContainer& other) { d->settings.filterCCA = other.filterCCA; d->settings.filterVIG = other.filterVIG; d->settings.filterDST = other.filterDST; d->settings.filterGEO = other.filterGEO; } LensFunIface::DevicePtr LensFunIface::findCamera(const QString& make, const QString& model) const { const lfCamera* const* cameras = d->lfDb->GetCameras(); while (cameras && *cameras) { DevicePtr cam = *cameras; // qCDebug(DIGIKAM_DIMG_LOG) << "Query camera:" << cam->Maker << "-" << cam->Model; - if (QString::fromLatin1(cam->Maker).toLower() == make.toLower() && - QString::fromLatin1(cam->Model).toLower() == model.toLower()) + if ((QString::fromLatin1(cam->Maker).toLower() == make.toLower()) && + (QString::fromLatin1(cam->Model).toLower() == model.toLower())) { qCDebug(DIGIKAM_DIMG_LOG) << "Search for camera " << make << "-" << model << " ==> true"; return cam; } ++cameras; } qCDebug(DIGIKAM_DIMG_LOG) << "Search for camera " << make << "-" << model << " ==> false"; return nullptr; } LensFunIface::LensPtr LensFunIface::findLens(const QString& model) const { const lfLens* const* lenses = d->lfDb->GetLenses(); while (lenses && *lenses) { LensPtr lens = *lenses; if (QString::fromLatin1(lens->Model) == model) { qCDebug(DIGIKAM_DIMG_LOG) << "Search for lens " << model << " ==> true"; return lens; } ++lenses; } qCDebug(DIGIKAM_DIMG_LOG) << "Search for lens " << model << " ==> false"; return nullptr; } LensFunIface::LensList LensFunIface::findLenses(const lfCamera* const lfCamera, const QString& lensDesc, const QString& lensMaker) const { LensList lensList; if (lfCamera) { const auto maker = lensMaker.isEmpty() ? nullptr : lensMaker.toLatin1().constData(); const auto model = lensDesc.isEmpty() ? nullptr : lensDesc.toLatin1().constData(); const lfLens *const *lfLens = d->lfDb->FindLenses(lfCamera, maker, model); while (lfLens && *lfLens) { lensList << (*lfLens); ++lfLens; } } return lensList; } LensFunIface::MetadataMatch LensFunIface::findFromMetadata(const DMetadata& meta) { MetadataMatch ret = MetadataNoMatch; d->settings = LensFunContainer(); d->usedCamera = nullptr; d->usedLens = nullptr; d->lensDescription.clear(); if (meta.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "No metadata available"; return LensFunIface::MetadataUnavailable; } PhotoInfoContainer photoInfo = meta.getPhotographInformation(); d->makeDescription = photoInfo.make.trimmed(); d->modelDescription = photoInfo.model.trimmed(); bool exactMatch = true; if (d->makeDescription.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "No camera maker info available"; exactMatch = false; } else { // NOTE: see bug #184156: // Some rules to wrap unknown camera device from Lensfun database, which have equivalent in fact. + if (d->makeDescription == QLatin1String("Canon")) { if (d->modelDescription == QLatin1String("Canon EOS Kiss Digital X")) { d->modelDescription = QLatin1String("Canon EOS 400D DIGITAL"); } if (d->modelDescription == QLatin1String("G1 X")) { d->modelDescription = QLatin1String("G1X"); } } d->lensDescription = photoInfo.lens.trimmed(); // ------------------------------------------------------------------------------------------------ DevicePtr lfCamera = findCamera(d->makeDescription, d->modelDescription); if (lfCamera) { setUsedCamera(lfCamera); qCDebug(DIGIKAM_DIMG_LOG) << "Camera maker : " << d->settings.cameraMake; qCDebug(DIGIKAM_DIMG_LOG) << "Camera model : " << d->settings.cameraModel; // ------------------------------------------------------------------------------------------------ // -- Performing lens description searches. LensList lensMatches; if (!d->lensDescription.isEmpty()) { QString lensCutted; LensList lensList; // STAGE 1, search in LensFun database as well. + lensList = findLenses(d->usedCamera, d->lensDescription); qCDebug(DIGIKAM_DIMG_LOG) << "* Check for lens by direct query (" << d->lensDescription << " : " << lensList.count() << ")"; lensMatches.append(lensList); // STAGE 2, Adapt exiv2 strings to lensfun strings for Nikon. + lensCutted = d->lensDescription; if (lensCutted.contains(QLatin1String("Nikon"))) { lensCutted.remove(QLatin1String("Nikon ")); lensCutted.remove(QLatin1String("Zoom-")); lensCutted.replace(QLatin1String("IF-ID"), QLatin1String("ED-IF")); lensList = findLenses(d->usedCamera, lensCutted); qCDebug(DIGIKAM_DIMG_LOG) << "* Check for Nikon lens (" << lensCutted << " : " << lensList.count() << ")"; lensMatches.append(lensList); } // TODO : Add here more specific lens maker rules. // LAST STAGE, Adapt exiv2 strings to lensfun strings. Some lens description use something like that : // "10.0 - 20.0 mm". This must be adapted like this : "10-20mm" + lensCutted = d->lensDescription; lensCutted.replace(QRegExp(QLatin1String("\\.[0-9]")), QLatin1String("")); //krazy:exclude=doublequote_chars lensCutted.replace(QLatin1String(" - "), QLatin1String("-")); lensCutted.replace(QLatin1String(" mm"), QLatin1String("mn")); lensList = findLenses(d->usedCamera, lensCutted); qCDebug(DIGIKAM_DIMG_LOG) << "* Check for no maker lens (" << lensCutted << " : " << lensList.count() << ")"; lensMatches.append(lensList); // Remove all duplicate lenses in the list by using QSet. + lensMatches = lensMatches.toSet().toList(); } else { qCDebug(DIGIKAM_DIMG_LOG) << "Lens description string is empty"; const auto lensList = findLenses(d->usedCamera, QString{}); if (lensList.count() == 1) { // NOTE: see bug #407157 + qCDebug(DIGIKAM_DIMG_LOG) << "For the camera " << d->settings.cameraModel << " there is exactly one lens in the database: " << lensList.first()->Model; lensMatches.append(lensList); } else { exactMatch = false; } } // Display the results. - if (lensMatches.isEmpty()) + if (lensMatches.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "lens matches : NOT FOUND"; exactMatch = false; } else if (lensMatches.count() == 1) { // Best case for an exact match is to have only one item returned by Lensfun searches. + setUsedLens(lensMatches.first()); qCDebug(DIGIKAM_DIMG_LOG) << "Lens found : " << d->settings.lensModel; qCDebug(DIGIKAM_DIMG_LOG) << "Crop Factor : " << d->settings.cropFactor; } else { qCDebug(DIGIKAM_DIMG_LOG) << "lens matches : more than one..."; const lfLens* exact = nullptr; foreach (const lfLens* const l, lensMatches) { if (QLatin1String(l->Model) == d->lensDescription) { qCDebug(DIGIKAM_DIMG_LOG) << "found exact match from" << lensMatches.count() << "possibilities:" << l->Model; exact = l; } } if (exact) { setUsedLens(exact); } else { exactMatch = false; } } } else { qCDebug(DIGIKAM_DIMG_LOG) << "Cannot find Lensfun camera device for (" << d->makeDescription << " - " << d->modelDescription << ")"; exactMatch = false; } } // ------------------------------------------------------------------------------------------------ // Performing Lens settings searches. QString temp = photoInfo.focalLength; if (temp.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "Focal Length : NOT FOUND"; exactMatch = false; } d->settings.focalLength = temp.mid(0, temp.length() - 3).toDouble(); // HACK: strip the " mm" at the end ... qCDebug(DIGIKAM_DIMG_LOG) << "Focal Length : " << d->settings.focalLength; // ------------------------------------------------------------------------------------------------ temp = photoInfo.aperture; if (temp.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "Aperture : NOT FOUND"; exactMatch = false; } d->settings.aperture = temp.mid(1).toDouble(); qCDebug(DIGIKAM_DIMG_LOG) << "Aperture : " << d->settings.aperture; // ------------------------------------------------------------------------------------------------ // Try to get subject distance value. // From standard Exif. + temp = meta.getExifTagString("Exif.Photo.SubjectDistance"); if (temp.isEmpty()) { // From standard XMP. + temp = meta.getXmpTagString("Xmp.exif.SubjectDistance"); } if (temp.isEmpty()) { // From Canon Makernote. + temp = meta.getExifTagString("Exif.CanonSi.SubjectDistance"); } if (temp.isEmpty()) { // From Nikon Makernote. + temp = meta.getExifTagString("Exif.NikonLd2.FocusDistance"); } if (temp.isEmpty()) { // From Nikon Makernote. + temp = meta.getExifTagString("Exif.NikonLd3.FocusDistance"); } if (temp.isEmpty()) { // From Olympus Makernote. + temp = meta.getExifTagString("Exif.OlympusFi.FocusDistance"); } // TODO: Add here others Makernotes tags. if (temp.isEmpty()) { qCDebug(DIGIKAM_DIMG_LOG) << "Subject dist. : NOT FOUND : Use default value."; temp = QLatin1String("1000"); } temp = temp.remove(QLatin1String(" m")); bool ok; d->settings.subjectDistance = temp.toDouble(&ok); if (!ok) { d->settings.subjectDistance = -1.0; } qCDebug(DIGIKAM_DIMG_LOG) << "Subject dist. : " << d->settings.subjectDistance; // ------------------------------------------------------------------------------------------------ ret = exactMatch ? MetadataExactMatch : MetadataPartialMatch; qCDebug(DIGIKAM_DIMG_LOG) << "Metadata match : " << metadataMatchDebugStr(ret); return ret; } QString LensFunIface::metadataMatchDebugStr(MetadataMatch val) const { QString ret; switch (val) { case MetadataNoMatch: ret = QLatin1String("No Match"); break; case MetadataPartialMatch: ret = QLatin1String("Partial Match"); break; default: ret = QLatin1String("Exact Match"); break; } return ret; } bool LensFunIface::supportsDistortion() const { if (!d->usedLens) { return false; } lfLensCalibDistortion res; return d->usedLens->InterpolateDistortion(d->settings.focalLength, res); } bool LensFunIface::supportsCCA() const { if (!d->usedLens) { return false; } lfLensCalibTCA res; return d->usedLens->InterpolateTCA(d->settings.focalLength, res); } bool LensFunIface::supportsVig() const { if (!d->usedLens) { return false; } lfLensCalibVignetting res; return d->usedLens->InterpolateVignetting(d->settings.focalLength, d->settings.aperture, d->settings.subjectDistance, res); } bool LensFunIface::supportsGeometry() const { return supportsDistortion(); } QString LensFunIface::lensFunVersion() { return QString::fromLatin1("%1.%2.%3-%4").arg(LF_VERSION_MAJOR) .arg(LF_VERSION_MINOR) .arg(LF_VERSION_MICRO) .arg(LF_VERSION_BUGFIX); } // Restore warnings #if defined(Q_CC_GNU) # pragma GCC diagnostic pop #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif } // namespace Digikam