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.
This commit is contained in:
Simone Gotti 2024-06-13 20:02:15 +02:00
parent 55fd4b975e
commit dc0e23c82c
5 changed files with 42 additions and 8 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
@ -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
{

View File

@ -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;