diff --git a/rtdata/languages/default b/rtdata/languages/default index 46496826a..e6eb09b06 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2837,6 +2837,7 @@ TP_LENSGEOM_FILL;Auto-fill TP_LENSGEOM_LABEL;Lens / Geometry TP_LENSGEOM_LIN;Linear TP_LENSGEOM_LOG;Logarithmic +TP_LENSPROFILE_CORRECTION_METADATA;From file metadata TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 5d87310b9..c937198c8 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -148,6 +148,7 @@ set(RTENGINESOURCEFILES jpeg_ijg/jpeg_memsrc.cc labimage.cc lcp.cc + lensmetadata.cc lmmse_demosaic.cc loadinitial.cc metadata.cc diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 580475c37..e630d1936 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1850,7 +1850,7 @@ bool check_need_larger_crop_for_lcp_distortion(int fw, int fh, int x, int y, int return false; } - return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp())); + return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp() || params.lensProf.useMetadata())); } } // namespace diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 9c6c794f0..c8c6e166e 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -130,6 +130,7 @@ class ImProcFunctions bool needsGradient() const; bool needsVignetting() const; bool needsLCP() const; + bool needsMetadata() const; bool needsLensfun() const; // static cmsUInt8Number* Mempro = NULL; @@ -155,7 +156,7 @@ enum class BlurType { ~ImProcFunctions(); bool needsLuminanceOnly() const { - return !(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient()); + return !(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun() || needsMetadata()) && (needsVignetting() || needsPCVignetting() || needsGradient()); } void setScale(double iscale); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 468507385..10f37e781 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -26,6 +26,7 @@ #include "rt_math.h" #include "rtengine.h" #include "rtlensfun.h" +#include "lensmetadata.h" #include "sleef.h" using namespace std; @@ -659,7 +660,13 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, std::unique_ptr pLCPMap; - if (needsLensfun()) { + if (needsMetadata()) { + auto corr = MetadataLensCorrectionFinder::findCorrection(metadata); + if (corr) { + corr->initCorrections(oW, oH, params->coarse, rawRotationDeg); + pLCPMap = std::move(corr); + } + } else if (needsLensfun()) { pLCPMap = LFDatabase::getInstance()->findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg); } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile); @@ -675,7 +682,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, } } - if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { + if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsMetadata() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH); } else { bool highQuality; @@ -1422,6 +1429,11 @@ bool ImProcFunctions::needsVignetting () const return params->vignetting.amount; } +bool ImProcFunctions::needsMetadata () const +{ + return params->lensProf.useMetadata(); +} + bool ImProcFunctions::needsLCP () const { return params->lensProf.useLcp(); @@ -1439,7 +1451,7 @@ bool ImProcFunctions::needsTransform (int oW, int oH, int rawRotationDeg, const std::unique_ptr pLCPMap = LFDatabase::getInstance()->findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg); needsLf = pLCPMap.get(); } - return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsLf; + return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsMetadata() || needsLf; } diff --git a/rtengine/lensmetadata.cc b/rtengine/lensmetadata.cc new file mode 100644 index 000000000..e92fc6cc9 --- /dev/null +++ b/rtengine/lensmetadata.cc @@ -0,0 +1,42 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2024 Rawtherapee developers + * + * RawTherapee 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 3 of the License, or + * (at your option) any later version. + * + * RawTherapee 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. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#include +#include + +#include "lensmetadata.h" + +namespace rtengine +{ + +std::unique_ptr MetadataLensCorrectionFinder::findCorrection(const FramesMetaData *meta) +{ + static const std::unordered_set makers = {}; + + std::string make = Glib::ustring(meta->getMake()).uppercase(); + + if (makers.find(make) == makers.end()) { + return nullptr; + } + + std::unique_ptr correction; + + return correction; +} + +} // namespace rtengine diff --git a/rtengine/lensmetadata.h b/rtengine/lensmetadata.h new file mode 100644 index 000000000..7991af8cd --- /dev/null +++ b/rtengine/lensmetadata.h @@ -0,0 +1,47 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2024 Rawtherapee developers + * + * RawTherapee 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 3 of the License, or + * (at your option) any later version. + * + * RawTherapee 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. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#pragma once + +#include + +#include "lcp.h" +#include "metadata.h" +#include "procparams.h" +#include "rtengine.h" + +namespace rtengine +{ + +/* MetadataLensCorrection is an abstract class for various lens correction based on raw file metadata + this metadata is vendor dependent */ +class MetadataLensCorrection : public LensCorrection, + public NonCopyable +{ +public: + virtual void initCorrections(int width, int height, const procparams::CoarseTransformParams &coarse, int rawRotationDeg) = 0; +}; + +/* MetadataLensCorrectionFinder tries to find and return MetadataLensCorrection for the provided metadata */ +class MetadataLensCorrectionFinder +{ +public: + static std::unique_ptr findCorrection(const FramesMetaData *meta); +}; + +} // namespace rtengine diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index b868a7627..e1a56a533 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2064,13 +2064,19 @@ bool LensProfParams::lfManual() const return lcMode == LcMode::LENSFUNMANUAL; } +bool LensProfParams::useMetadata() const +{ + return lcMode == LcMode::METADATA; +} + const std::vector& LensProfParams::getMethodStrings() const { static const std::vector method_strings = { "none", "lfauto", "lfmanual", - "lcp" + "lcp", + "metadata" }; return method_strings; } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index d71c3d172..e6945cfbd 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -966,7 +966,8 @@ struct LensProfParams { NONE, // No lens correction LENSFUNAUTOMATCH, // Lens correction using auto matched lensfun database entry LENSFUNMANUAL, // Lens correction using manually selected lensfun database entry - LCP // Lens correction using lcp file + LCP, // Lens correction using lcp file + METADATA // Lens correction using embedded metadata }; LcMode lcMode; @@ -985,6 +986,7 @@ struct LensProfParams { bool lfAutoMatch() const; bool useLcp() const; bool lfManual() const; + bool useMetadata() const; const std::vector& getMethodStrings() const; Glib::ustring getMethodString(LcMode mode) const; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 09b64ad41..8cd6ffedd 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -44,6 +44,7 @@ #include "rt_math.h" #include "rtengine.h" #include "rtlensfun.h" +#include "lensmetadata.h" #include "../rtgui/options.h" #define BENCHMARK @@ -1583,7 +1584,13 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens if (!hasFlatField && lensProf.useVign && lensProf.lcMode != LensProfParams::LcMode::NONE) { std::unique_ptr pmap; - if (lensProf.useLensfun()) { + if (lensProf.useMetadata()) { + auto corr = MetadataLensCorrectionFinder::findCorrection(idata); + if (corr) { + corr->initCorrections(W, H, coarse, -1); + pmap = std::move(corr); + } + } else if (lensProf.useLensfun()) { pmap = LFDatabase::getInstance()->findModifier(lensProf, idata, W, H, coarse, -1); } else { const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile(lensProf.lcpFile); diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 5f42a1cde..4410b2fe0 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -32,6 +32,7 @@ #include "../rtengine/lcp.h" #include "../rtengine/procparams.h" #include "../rtengine/rtlensfun.h" +#include "../rtengine/lensmetadata.h" using namespace rtengine; using namespace rtengine::procparams; @@ -56,6 +57,7 @@ LensProfilePanel::LensProfilePanel() : distGrid(Gtk::manage((new Gtk::Grid()))), corrUnchangedRB(Gtk::manage((new Gtk::RadioButton(M("GENERAL_UNCHANGED"))))), corrOffRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("GENERAL_NONE"))))), + corrMetadata(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_METADATA"))))), corrLensfunAutoRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_AUTOMATCH"))))), corrLensfunManualRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_MANUAL"))))), corrLcpFileRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_LCPFILE"))))), @@ -144,17 +146,18 @@ LensProfilePanel::LensProfilePanel() : // Populate modes grid: modesGrid->attach(*corrOffRB, 0, 0, 3, 1); - modesGrid->attach(*corrLensfunAutoRB, 0, 1, 3, 1); - modesGrid->attach(*corrLensfunManualRB, 0, 2, 3, 1); + modesGrid->attach(*corrMetadata, 0, 1, 3, 1); + modesGrid->attach(*corrLensfunAutoRB, 0, 2, 3, 1); + modesGrid->attach(*corrLensfunManualRB, 0, 3, 3, 1); - modesGrid->attach(*lensfunCamerasLbl, 0, 3, 1, 1); - modesGrid->attach(*lensfunCameras, 1, 3, 1, 1); - modesGrid->attach(*lensfunLensesLbl, 0, 4, 1, 1); - modesGrid->attach(*lensfunLenses, 1, 4, 1, 1); - modesGrid->attach(*warning, 2, 3, 1, 2); + modesGrid->attach(*lensfunCamerasLbl, 0, 4, 1, 1); + modesGrid->attach(*lensfunCameras, 1, 4, 1, 1); + modesGrid->attach(*lensfunLensesLbl, 0, 5, 1, 1); + modesGrid->attach(*lensfunLenses, 1, 5, 1, 1); + modesGrid->attach(*warning, 2, 4, 1, 2); - modesGrid->attach(*corrLcpFileRB, 0, 5, 1, 1); - modesGrid->attach(*corrLcpFileChooser, 1, 5, 1, 1); + modesGrid->attach(*corrLcpFileRB, 0, 6, 1, 1); + modesGrid->attach(*corrLcpFileChooser, 1, 6, 1, 1); // Populate distortions grid: @@ -179,6 +182,7 @@ LensProfilePanel::LensProfilePanel() : lensfunCameras->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunCameraChanged)); lensfunLenses->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunLensChanged)); corrOffRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrOffRB)); + corrMetadata->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrMetadata)); corrLensfunAutoRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunAutoRB)); corrLensfunManualRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunManualRB)); corrLcpFileRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLcpFileRB)); @@ -211,30 +215,52 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa break; } + case procparams::LensProfParams::LcMode::METADATA: { + if (metadata) { + auto metadataCorrection= rtengine::MetadataLensCorrectionFinder::findCorrection(metadata); + if (metadataCorrection) { + corrMetadata->set_active(true); + corrMetadata->set_sensitive(true); + } else { + corrMetadata->set_sensitive(false); + corrOffRB->set_active(true); + } + } else { + corrMetadata->set_sensitive(false); + } + break; + } + case procparams::LensProfParams::LcMode::NONE: { corrOffRB->set_active(true); setManualParamsVisibility(false); + + ckbUseDist->set_sensitive(false); + ckbUseVign->set_sensitive(false); + ckbUseCA->set_sensitive(false); break; } } - if (pp->lensProf.lcpFile.empty()) { - const Glib::ustring lastFolder = corrLcpFileChooser->get_current_folder(); - corrLcpFileChooser->set_current_folder(lastFolder); - corrLcpFileChooser->unselect_all(); - bindCurrentFolder(*corrLcpFileChooser, options.lastLensProfileDir); - updateDisabled(false); - } - else if (LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) { - corrLcpFileChooser->set_filename(pp->lensProf.lcpFile); - - if (corrLcpFileRB->get_active()) { - updateDisabled(true); + if (pp->lensProf.lcMode == procparams::LensProfParams::LcMode::LCP) { + if (pp->lensProf.lcpFile.empty()) { + const Glib::ustring lastFolder = corrLcpFileChooser->get_current_folder(); + corrLcpFileChooser->set_current_folder(lastFolder); + corrLcpFileChooser->unselect_all(); + bindCurrentFolder(*corrLcpFileChooser, options.lastLensProfileDir); + updateLCPDisabled(false); + } + else if (LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) { + corrLcpFileChooser->set_filename(pp->lensProf.lcpFile); + + if (corrLcpFileRB->get_active()) { + updateLCPDisabled(true); + } + } + else { + corrLcpFileChooser->unselect_filename(corrLcpFileChooser->get_filename()); + updateLCPDisabled(false); } - } - else { - corrLcpFileChooser->unselect_filename(corrLcpFileChooser->get_filename()); - updateDisabled(false); } const LFDatabase* const db = LFDatabase::getInstance(); @@ -308,6 +334,9 @@ void LensProfilePanel::write(rtengine::procparams::ProcParams* pp, ParamsEdited* if (corrLcpFileRB->get_active()) { pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LCP; } + else if (corrMetadata->get_active()) { + pp->lensProf.lcMode = procparams::LensProfParams::LcMode::METADATA; + } else if (corrLensfunManualRB->get_active()) { pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LENSFUNMANUAL; } @@ -367,12 +396,18 @@ void LensProfilePanel::setRawMeta(bool raw, const rtengine::FramesMetaData* pMet // CA is very focus layer dependent, otherwise it might even worsen things allowFocusDep = false; - ckbUseCA->set_active(false); - ckbUseCA->set_sensitive(false); enableListener(); } + corrMetadata->set_sensitive(false); + if (pMeta) { + metadataCorrection = MetadataLensCorrectionFinder::findCorrection(pMeta); + if (metadataCorrection) { + corrMetadata->set_sensitive(true); + } + } + isRaw = raw; metadata = pMeta; } @@ -381,7 +416,7 @@ void LensProfilePanel::onLCPFileChanged() { lcpFileChanged = true; const bool valid = LCPStore::getInstance()->isValidLCPFileName(corrLcpFileChooser->get_filename()); - updateDisabled(valid); + updateLCPDisabled(valid); if (listener) { if (valid) { @@ -552,10 +587,20 @@ void LensProfilePanel::onCorrModeChanged(const Gtk::RadioButton* rbChanged) lensfunAutoChanged = true; lcpFileChanged = true; - updateDisabled(true); + updateLCPDisabled(true); mode = M("TP_LENSPROFILE_CORRECTION_LCPFILE"); + } else if (rbChanged == corrMetadata) { + lcModeChanged = true; + useLensfunChanged = true; + lensfunAutoChanged = true; + lcpFileChanged = true; + + updateMetadataDisabled(); + + mode = M("TP_LENSPROFILE_CORRECTION_METADATA"); + } else if (rbChanged == corrUnchangedRB) { lcModeChanged = false; useLensfunChanged = false; @@ -680,7 +725,7 @@ void LensProfilePanel::LFDbHelper::fillLensfunLenses() } } -void LensProfilePanel::updateDisabled(bool enable) +void LensProfilePanel::updateLCPDisabled(bool enable) { if (!batchMode) { ckbUseDist->set_sensitive(enable); @@ -689,6 +734,21 @@ void LensProfilePanel::updateDisabled(bool enable) } } +void LensProfilePanel::updateMetadataDisabled() +{ + if (!batchMode) { + if (metadataCorrection) { + ckbUseDist->set_sensitive(metadataCorrection->hasDistortionCorrection()); + ckbUseVign->set_sensitive(metadataCorrection->hasVignettingCorrection()); + ckbUseCA->set_sensitive(metadataCorrection->hasCACorrection()); + } else { + ckbUseDist->set_sensitive(false); + ckbUseVign->set_sensitive(false); + ckbUseCA->set_sensitive(false); + } + } +} + bool LensProfilePanel::setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model) { if (!make.empty() && !model.empty()) { diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 42746f41e..cdf7bd6e4 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -22,6 +22,7 @@ #include "guiutils.h" #include "toolpanel.h" +#include "../rtengine/lensmetadata.h" class LensProfilePanel final : public ToolParamBlock, @@ -89,7 +90,8 @@ private: void fillLensfunLenses(); }; - void updateDisabled(bool enable); + void updateLCPDisabled(bool enable); + void updateMetadataDisabled(); bool setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model); bool setLensfunLens(const Glib::ustring& lens); @@ -113,12 +115,14 @@ private: bool allowFocusDep; bool isRaw; const rtengine::FramesMetaData* metadata; + std::unique_ptr metadataCorrection; Gtk::Grid* const modesGrid; Gtk::Grid* const distGrid; Gtk::RadioButton* const corrUnchangedRB; Gtk::RadioButton::Group corrGroup; Gtk::RadioButton* const corrOffRB; + Gtk::RadioButton* const corrMetadata; Gtk::RadioButton* const corrLensfunAutoRB; Gtk::RadioButton* const corrLensfunManualRB; Gtk::RadioButton* const corrLcpFileRB;