From dc0e23c82c4ebedd901fb66593702ae3fb6df724 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Thu, 13 Jun 2024 20:02:15 +0200 Subject: [PATCH] iptransform: apply distortion and CA correction in a single pass Some lens profile methods provides a way to correct distortion and CA in a single step. When available, applying distortion and CA correction when both enabled in a single pass is more precise (see lensfun ApplySubpixelGeometryDistortion doc) since it's usually how the profile is done. Instead applying the CA correction in a step after distortion correction could lead to a bit different (also if not always visible) correction. This is also required for future lens correction methods (like DNG WarpRectilinear or corrections based on vendor metadata) that provides only merged distortion and CA correction in a single pass. --- rtengine/iptransform.cc | 10 +++++----- rtengine/lcp.cc | 6 ++++++ rtengine/lcp.h | 3 +++ rtengine/rtlensfun.cc | 30 +++++++++++++++++++++++++++--- rtengine/rtlensfun.h | 1 + 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 757d3d54c..08ee9a4a0 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -1233,12 +1233,12 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I double Dx = Dxr; double Dy = Dyr; - if (enableLCPCA) { - pLCPMap->correctCA(Dx, Dy, w2, h2, c); - } - - if (enableLCPDist) { + if (enableLCPDist && enableLCPCA) { + pLCPMap->correctDistortionAndCA(Dx, Dy, w2, h2, c); + } else if (enableLCPDist) { pLCPMap->correctDistortion(Dx, Dy, w2, h2); + } else if (enableLCPCA) { + pLCPMap->correctCA(Dx, Dy, w2, h2, c); } // distortion correction diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 7507bbb11..ba96147dc 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -990,6 +990,12 @@ bool rtengine::LCPMapper::isCACorrectionAvailable() const return enableCA; } +void rtengine::LCPMapper::correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const +{ + correctDistortion(x, y, cx, cy); + correctCA(x, y, cx, cy, channel); +} + void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy) const { x += cx; diff --git a/rtengine/lcp.h b/rtengine/lcp.h index b59cc84c6..97cdd0890 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -166,6 +166,8 @@ private: class LensCorrection { public: virtual ~LensCorrection() {} + + virtual void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const = 0; virtual void correctDistortion(double &x, double &y, int cx, int cy) const = 0; virtual bool isCACorrectionAvailable() const = 0; virtual void correctCA(double &x, double &y, int cx, int cy, int channel) const = 0; @@ -194,6 +196,7 @@ public: ); + void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const override; void correctDistortion(double &x, double &y, int cx, int cy) const override; bool isCACorrectionAvailable() const override; void correctCA(double& x, double& y, int cx, int cy, int channel) const override; diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 08010f50f..505346ad0 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -125,7 +125,6 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy) const } } - bool LFModifier::isCACorrectionAvailable() const { return (flags_ & LF_MODIFY_TCA); @@ -141,7 +140,7 @@ void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) co // channels. We could consider caching the info to speed this up x += cx; y += cy; - + float pos[6]; if (swap_xy_) { std::swap(x, y); @@ -156,6 +155,31 @@ void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) co y -= cy; } +void LFModifier::correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const +{ + assert(channel >= 0 && channel <= 2); + + // RT currently applies the CA correction per channel, whereas + // lensfun applies it to all the three channels simultaneously. This means + // we do the work 3 times, because each time we discard 2 of the 3 + // channels. We could consider caching the info to speed this up + x += cx; + y += cy; + + float pos[6]; + if (swap_xy_) { + std::swap(x, y); + } + data_->ApplySubpixelGeometryDistortion(x, y, 1, 1, pos); // This is thread-safe + x = pos[2*channel]; + y = pos[2*channel+1]; + if (swap_xy_) { + std::swap(x, y); + } + x -= cx; + y -= cy; +} + #ifdef _OPENMP void LFModifier::processVignette(int width, int height, float** rawData) const { @@ -396,7 +420,7 @@ bool LFDatabase::init(const Glib::ustring &dbdir) if (settings->verbose) { std::cout << (ok ? "OK" : "FAIL") << std::endl; } - + return ok; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 1d941246f..39ca67ec2 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -53,6 +53,7 @@ public: explicit operator bool() const; + void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const override; void correctDistortion(double &x, double &y, int cx, int cy) const override; bool isCACorrectionAvailable() const override; void correctCA(double &x, double &y, int cx, int cy, int channel) const override;