From a29287eaa08268b46e728d5e6d79bf81952ee6fe Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 26 Oct 2017 22:51:03 +0200 Subject: [PATCH 1/7] started working on enabling full support for CA correction with a lens profile --- rtengine/improcfun.h | 3 +- rtengine/iptransform.cc | 128 +++++++++++++++++++++++++--------------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 6c5ded3b6..3b835a124 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -57,7 +57,8 @@ class ImProcFunctions enum TransformMode { TRANSFORM_PREVIEW, TRANSFORM_HIGH_QUALITY, - TRANSFORM_HIGH_QUALITY_FULLIMAGE + TRANSFORM_HIGH_QUALITY_FULLIMAGE, + TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA }; void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index d06872a64..5a50fafe9 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -315,7 +315,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, pLCPMap.reset( new LCPMapper (pLCPProf, focalLen, focalLen35mm, focusDist, fNumber, false, - params->lensProf.useDist, + false, oW, oH, params->coarse, rawRotationDeg ) ); @@ -326,12 +326,26 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH); } else { TransformMode mode; + std::unique_ptr tmpimg; if (!needsCA() && scale != 1) { mode = TRANSFORM_PREVIEW; } else if (!fullImage) { mode = TRANSFORM_HIGH_QUALITY; } else { mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; + // agriggio: CA correction via the lens profile has to be + // performed before all the other transformations (except for the + // coarse rotation/flipping). In order to not change the code too + // much, I simply introduced a new mode + // TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA, which applies *only* + // profile-based CA correction. So, the correction in this case + // occurs in two steps, using an intermediate temporary + // image. There's room for optimization of course... + if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) { + tmpimg.reset(new Imagefloat(transformed->getWidth(), transformed->getHeight())); + transformGeneral(TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA, original, tmpimg.get(), cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); + original = tmpimg.get(); + } } transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); } @@ -725,21 +739,59 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat* void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap) { + // set up stuff, depending on the mode we are + bool enableLCPDist = pLCPMap && params->lensProf.useDist; + bool enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); + bool enableCA = needsCA(); + bool enableGradient = needsGradient(); + bool enablePCVignetting = needsPCVignetting(); + bool enableVignetting = needsVignetting(); + bool enablePerspective = needsPerspective(); + bool enableDistortion = needsDistortion(); + + switch (mode) { + case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA: + enableLCPDist = false; + enablePerspective = false; + enableDistortion = false; + enableGradient = false; + enableVignetting = false; + enablePCVignetting = false; + enableCA = false; + break; + case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: + enableLCPCA = false; + break; + case ImProcFunctions::TRANSFORM_HIGH_QUALITY: + if (enableLCPDist) { + enableLCPCA = false; + } + break; + case ImProcFunctions::TRANSFORM_PREVIEW: + enableLCPCA = false; + enableCA = false; + break; + } + + bool useCA = enableCA || enableLCPCA; + double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; double vig_w2, vig_h2, maxRadius, v, b, mul; - calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); + if (enableVignetting) { + calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); + } struct grad_params gp; - if (needsGradient()) { + if (enableGradient) { calcGradientParams (oW, oH, params->gradient, gp); } struct pcv_params pcv; - if (needsPCVignetting()) { + if (enablePCVignetting) { calcPCVignetteParams (fW, fH, oW, oH, params->pcvignette, params->crop, pcv); } @@ -755,12 +807,11 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag // auxiliary variables for c/a correction double chDist[3]; - chDist[0] = params->cacorrection.red; + chDist[0] = enableCA ? params->cacorrection.red : 0.0; chDist[1] = 0.0; - chDist[2] = params->cacorrection.blue; + chDist[2] = enableCA ? params->cacorrection.blue : 0.0; // auxiliary variables for distortion correction - bool needsDist = needsDistortion(); // for performance double distAmount = params->distortion.amount; // auxiliary variables for rotation @@ -781,39 +832,20 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag oH * tan (hpalpha) * sqrt (SQR (4 * maxRadius) + SQR (oH * tan (hpalpha)))) / (SQR (maxRadius) * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); - double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; - - // smaller crop images are a problem, so only when processing fully - bool enableLCPCA = false; - bool enableLCPDist = false; - bool enableCA = false; - - switch (mode) { - case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: { - enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); - } - //no break on purpose - - case ImProcFunctions::TRANSFORM_HIGH_QUALITY: { - enableLCPDist = pLCPMap && params->lensProf.useDist; - enableCA = enableLCPCA || needsCA(); - } - //no break on purpose - - default: - case ImProcFunctions::TRANSFORM_PREVIEW: { - enableLCPDist = pLCPMap && params->lensProf.useDist; - break; - } + //double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; + double ascale = 1.0; + if (mode != ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA && + params->commonTrans.autofill) { + ascale = getTransformAutoFill (oW, oH, pLCPMap); } - if (enableLCPCA) { - enableLCPDist = false; - } + // if (enableLCPCA) { + // enableLCPDist = false; + // } - if (!enableCA) { - chDist[0] = 0.0; - } + // if (!enableCA) { + // chDist[0] = 0.0; + // } // main cycle bool darkening = (params->vignetting.amount <= 0.0); @@ -835,12 +867,12 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag double vig_x_d = 0., vig_y_d = 0.; - if (needsVignetting()) { + if (enableVignetting) { vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale } - if (needsPerspective()) { + if (enablePerspective) { // horizontal perspective transformation y_d *= maxRadius / (maxRadius + x_d * hptanpt); x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); @@ -857,20 +889,20 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag // distortion correction double s = 1; - if (needsDist) { + if (enableDistortion) { double r = sqrt (Dxc * Dxc + Dyc * Dyc) / maxRadius; // sqrt is slow s = 1.0 - distAmount + distAmount * r ; } double r2 = 0.; - if (needsVignetting()) { + if (enableVignetting) { double vig_Dx = vig_x_d * cost - vig_y_d * sint; double vig_Dy = vig_x_d * sint + vig_y_d * cost; r2 = sqrt (vig_Dx * vig_Dx + vig_Dy * vig_Dy); } - for (int c = 0; c < (enableCA ? 3 : 1); c++) { + for (int c = 0; c < (useCA ? 3 : 1); c++) { double Dx = Dxc * (s + chDist[c]); double Dy = Dyc * (s + chDist[c]); @@ -897,7 +929,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag // multiplier for vignetting correction double vignmul = 1.0; - if (needsVignetting()) { + if (enableVignetting) { if (darkening) { vignmul /= std::max (v + mul * tanh (b * (maxRadius - s * r2) / maxRadius), 0.001); } else { @@ -905,17 +937,17 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } } - if (needsGradient()) { + if (enableGradient) { vignmul *= calcGradientFactor (gp, cx + x, cy + y); } - if (needsPCVignetting()) { + if (enablePCVignetting) { vignmul *= calcPCVignetteFactor (pcv, cx + x, cy + y); } if (yc > 0 && yc < original->getHeight() - 2 && xc > 0 && xc < original->getWidth() - 2) { // all interpolation pixels inside image - if (enableCA) { + if (useCA) { interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, & (chTrans[c][y][x]), vignmul); } else if (mode == ImProcFunctions::TRANSFORM_PREVIEW) { transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy); @@ -931,7 +963,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag int x1 = LIM (xc, 0, original->getWidth() - 1); int x2 = LIM (xc + 1, 0, original->getWidth() - 1); - if (enableCA) { + if (useCA) { chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy); } else { transformed->r (y, x) = vignmul * (original->r (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->r (y1, x2) * Dx * (1.0 - Dy) + original->r (y2, x1) * (1.0 - Dx) * Dy + original->r (y2, x2) * Dx * Dy); @@ -940,7 +972,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } } } else { - if (enableCA) { + if (useCA) { // not valid (source pixel x,y not inside source image, etc.) chTrans[c][y][x] = 0; } else { From 830fd6fdbb3933a616cb0e2125f8055a3d2bbe4c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 26 Oct 2017 23:55:52 +0200 Subject: [PATCH 2/7] added support for CA correction via lensfun Performance could be improved (see the comments marked "agriggio" in iptransform.cc and rtlensfun.cc) --- rtengine/rtlensfun.cc | 35 +++++++++++++++++++++++++++++++++-- rtengine/rtlensfun.h | 1 + rtgui/lensprofile.cc | 10 ++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index ed19f44f0..99315c20d 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -72,12 +72,29 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double bool LFModifier::isCACorrectionAvailable() const { - return false; + return (flags_ & LF_MODIFY_TCA); } void LFModifier::correctCA(double &x, double &y, int channel) const { + assert(channel >= 0 && channel <= 2); + + // agriggio: 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 + + float pos[6]; + if (swap_xy_) { + std::swap(x, y); + } + data_->ApplySubpixelDistortion(x, y, 1, 1, pos); + x = pos[2*channel]; + y = pos[2*channel+1]; + if (swap_xy_) { + std::swap(x, y); + } } @@ -109,6 +126,11 @@ Glib::ustring LFModifier::getDisplayString() const ret += "vignetting"; sep = ", "; } + if (flags_ & LF_MODIFY_TCA) { + ret += sep; + ret += "CA"; + sep = ", "; + } if (flags_ & LF_MODIFY_SCALE) { ret += sep; ret += "autoscaling"; @@ -245,6 +267,15 @@ bool LFLens::hasDistortionCorrection() const } } +bool LFLens::hasCACorrection() const +{ + if (data_) { + return data_->CalibTCA; + } else { + return false; + } +} + //----------------------------------------------------------------------------- // LFDatabase @@ -428,7 +459,7 @@ std::unique_ptr LFDatabase::getModifier(const LFCamera &camera, cons if (data_) { if (camera && lens) { lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); - int flags = LF_MODIFY_DISTORTION | LF_MODIFY_SCALE; + int flags = LF_MODIFY_DISTORTION | LF_MODIFY_SCALE | LF_MODIFY_TCA; if (aperture > 0) { flags |= LF_MODIFY_VIGNETTING; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 207e4e86e..ddc6d885a 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -90,6 +90,7 @@ public: float getCropFactor() const; bool hasVignettingCorrection() const; bool hasDistortionCorrection() const; + bool hasCACorrection() const; private: friend class LFDatabase; diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 6415c1fce..d158f44f4 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -254,6 +254,16 @@ void LensProfilePanel::updateLensfunWarning() } ckbUseVign->set_sensitive(l.hasVignettingCorrection()); ckbUseDist->set_sensitive(l.hasDistortionCorrection()); + ckbUseCA->set_sensitive(l.hasCACorrection()); + if (!isRaw || !l.hasVignettingCorrection()) { + ckbUseVign->set_active(false); + } + if (!l.hasDistortionCorrection()) { + ckbUseDist->set_active(false); + } + if (!l.hasCACorrection()) { + ckbUseCA->set_active(false); + } } } From 5a05b04e0973feb28a5fac9e3abcd5b883fa7608 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 29 Oct 2017 21:41:19 +0100 Subject: [PATCH 3/7] profile-based CA correction applied during 1:1 preview --- rtengine/improcfun.h | 2 +- rtengine/iptransform.cc | 24 +++++++++++++----------- rtengine/lcp.cc | 8 +++++++- rtengine/lcp.h | 4 ++-- rtengine/rtlensfun.cc | 6 +++++- rtengine/rtlensfun.h | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 3b835a124..699193632 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -58,7 +58,7 @@ class ImProcFunctions TRANSFORM_PREVIEW, TRANSFORM_HIGH_QUALITY, TRANSFORM_HIGH_QUALITY_FULLIMAGE, - TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA + TRANSFORM_HIGH_QUALITY_CA }; void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 5a50fafe9..87ab7b922 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -329,21 +329,23 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, std::unique_ptr tmpimg; if (!needsCA() && scale != 1) { mode = TRANSFORM_PREVIEW; - } else if (!fullImage) { - mode = TRANSFORM_HIGH_QUALITY; } else { - mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; + if (!fullImage) { + mode = TRANSFORM_HIGH_QUALITY; + } else { + mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; + } // agriggio: CA correction via the lens profile has to be // performed before all the other transformations (except for the // coarse rotation/flipping). In order to not change the code too // much, I simply introduced a new mode - // TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA, which applies *only* + // TRANSFORM_HIGH_QUALITY_CA, which applies *only* // profile-based CA correction. So, the correction in this case // occurs in two steps, using an intermediate temporary // image. There's room for optimization of course... if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) { - tmpimg.reset(new Imagefloat(transformed->getWidth(), transformed->getHeight())); - transformGeneral(TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA, original, tmpimg.get(), cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); + tmpimg.reset(new Imagefloat(original->getWidth(), original->getHeight())); + transformGeneral(TRANSFORM_HIGH_QUALITY_CA, original, tmpimg.get(), 0, 0, 0, 0, fW, fH, fW, fH, pLCPMap.get()); original = tmpimg.get(); } } @@ -750,7 +752,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag bool enableDistortion = needsDistortion(); switch (mode) { - case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA: + case ImProcFunctions::TRANSFORM_HIGH_QUALITY_CA: enableLCPDist = false; enablePerspective = false; enableDistortion = false; @@ -763,9 +765,9 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag enableLCPCA = false; break; case ImProcFunctions::TRANSFORM_HIGH_QUALITY: - if (enableLCPDist) { + // if (enableLCPDist) { enableLCPCA = false; - } + // } break; case ImProcFunctions::TRANSFORM_PREVIEW: enableLCPCA = false; @@ -834,7 +836,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag //double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; double ascale = 1.0; - if (mode != ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE_CA && + if (mode != ImProcFunctions::TRANSFORM_HIGH_QUALITY_CA && params->commonTrans.autofill) { ascale = getTransformAutoFill (oW, oH, pLCPMap); } @@ -912,7 +914,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag // LCP CA if (enableLCPCA) { - pLCPMap->correctCA (Dx, Dy, c); + pLCPMap->correctCA (Dx, Dy, cx, cy, c); } // Extract integer and fractions of source screen coordinates diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 3824d2b2f..44a470e17 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -1077,12 +1077,15 @@ void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy y -= cy * scale; } -void rtengine::LCPMapper::correctCA(double& x, double& y, int channel) const +void rtengine::LCPMapper::correctCA(double& x, double& y, int cx, int cy, int channel) const { if (!enableCA) { return; } + x += cx; + y += cy; + double xgreen, ygreen; // First calc the green channel like normal distortion @@ -1123,6 +1126,9 @@ void rtengine::LCPMapper::correctCA(double& x, double& y, int channel) const x = (chrom[channel].scale_factor * ( xd * commonSum + xfac * rsqr )) * chrom[channel].fx + chrom[channel].x0; y = (chrom[channel].scale_factor * ( yd * commonSum + yfac * rsqr )) * chrom[channel].fy + chrom[channel].y0; } + + x -= cx; + x -= cy; } SSEFUNCTION void rtengine::LCPMapper::processVignetteLine(int width, int y, float* line) const diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 17eb892c8..b50fd335e 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -164,7 +164,7 @@ public: virtual ~LensCorrection() {} virtual void correctDistortion(double &x, double &y, int cx, int cy, double scale) const = 0; virtual bool isCACorrectionAvailable() const = 0; - virtual void correctCA(double &x, double &y, int channel) const = 0; + virtual void correctCA(double &x, double &y, int cx, int cy, int channel) const = 0; virtual void processVignetteLine(int width, int y, float *line) const = 0; virtual void processVignetteLine3Channels(int width, int y, float *line) const = 0; }; @@ -192,7 +192,7 @@ public: void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; // MUST be the first stage bool isCACorrectionAvailable() const; - void correctCA(double& x, double& y, int channel) const; + void correctCA(double& x, double& y, int cx, int cy, int channel) const; void processVignetteLine(int width, int y, float* line) const; void processVignetteLine3Channels(int width, int y, float* line) const; diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 99315c20d..cc4f6f53c 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -76,7 +76,7 @@ bool LFModifier::isCACorrectionAvailable() const } -void LFModifier::correctCA(double &x, double &y, int channel) const +void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) const { assert(channel >= 0 && channel <= 2); @@ -84,6 +84,8 @@ void LFModifier::correctCA(double &x, double &y, int channel) const // 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_) { @@ -95,6 +97,8 @@ void LFModifier::correctCA(double &x, double &y, int channel) const if (swap_xy_) { std::swap(x, y); } + x -= cx; + y -= cy; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index ddc6d885a..60ef461c7 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -44,7 +44,7 @@ public: void correctDistortion(double &x, double &y, int cx, int cy, double scale) const override; bool isCACorrectionAvailable() const override; - void correctCA(double &x, double &y, int channel) const override; + void correctCA(double &x, double &y, int cx, int cy, int channel) const override; void processVignetteLine(int width, int y, float *line) const override; void processVignetteLine3Channels(int width, int y, float *line) const override; From dfff9812bfb9851f2fe80409e8f9ba6569c0784b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 31 Oct 2017 00:51:01 +0100 Subject: [PATCH 4/7] improved profile-based CA correction in preview also cleaned up the code --- rtengine/improcfun.h | 9 +-- rtengine/iptransform.cc | 135 ++++++++++++++++++++++------------------ 2 files changed, 75 insertions(+), 69 deletions(-) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 699193632..7f9acc9e7 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -54,14 +54,9 @@ class ImProcFunctions void calcVignettingParams (int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul); - enum TransformMode { - TRANSFORM_PREVIEW, - TRANSFORM_HIGH_QUALITY, - TRANSFORM_HIGH_QUALITY_FULLIMAGE, - TRANSFORM_HIGH_QUALITY_CA - }; void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); - void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); + void transformGeneral(bool highQuality, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); + void transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, int oW, int oH, const LensCorrection *pLCPMap); void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam); void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 87ab7b922..9871c7a57 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -325,16 +325,12 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH); } else { - TransformMode mode; + bool highQuality; std::unique_ptr tmpimg; if (!needsCA() && scale != 1) { - mode = TRANSFORM_PREVIEW; + highQuality = false; } else { - if (!fullImage) { - mode = TRANSFORM_HIGH_QUALITY; - } else { - mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; - } + highQuality = true; // agriggio: CA correction via the lens profile has to be // performed before all the other transformations (except for the // coarse rotation/flipping). In order to not change the code too @@ -345,14 +341,15 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, // image. There's room for optimization of course... if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) { tmpimg.reset(new Imagefloat(original->getWidth(), original->getHeight())); - transformGeneral(TRANSFORM_HIGH_QUALITY_CA, original, tmpimg.get(), 0, 0, 0, 0, fW, fH, fW, fH, pLCPMap.get()); + transformLCPCAOnly(original, tmpimg.get(), cx, cy, oW, oH, pLCPMap.get()); original = tmpimg.get(); } } - transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); + transformGeneral(highQuality, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); } } + // helper function void ImProcFunctions::calcVignettingParams (int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul) { @@ -739,44 +736,17 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat* } -void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap) +void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap) { // set up stuff, depending on the mode we are bool enableLCPDist = pLCPMap && params->lensProf.useDist; - bool enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); - bool enableCA = needsCA(); + bool enableCA = highQuality && needsCA(); bool enableGradient = needsGradient(); bool enablePCVignetting = needsPCVignetting(); bool enableVignetting = needsVignetting(); bool enablePerspective = needsPerspective(); bool enableDistortion = needsDistortion(); - switch (mode) { - case ImProcFunctions::TRANSFORM_HIGH_QUALITY_CA: - enableLCPDist = false; - enablePerspective = false; - enableDistortion = false; - enableGradient = false; - enableVignetting = false; - enablePCVignetting = false; - enableCA = false; - break; - case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: - enableLCPCA = false; - break; - case ImProcFunctions::TRANSFORM_HIGH_QUALITY: - // if (enableLCPDist) { - enableLCPCA = false; - // } - break; - case ImProcFunctions::TRANSFORM_PREVIEW: - enableLCPCA = false; - enableCA = false; - break; - } - - bool useCA = enableCA || enableLCPCA; - double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; @@ -834,20 +804,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag oH * tan (hpalpha) * sqrt (SQR (4 * maxRadius) + SQR (oH * tan (hpalpha)))) / (SQR (maxRadius) * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); - //double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; - double ascale = 1.0; - if (mode != ImProcFunctions::TRANSFORM_HIGH_QUALITY_CA && - params->commonTrans.autofill) { - ascale = getTransformAutoFill (oW, oH, pLCPMap); - } - - // if (enableLCPCA) { - // enableLCPDist = false; - // } - - // if (!enableCA) { - // chDist[0] = 0.0; - // } + double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; // main cycle bool darkening = (params->vignetting.amount <= 0.0); @@ -904,7 +861,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag r2 = sqrt (vig_Dx * vig_Dx + vig_Dy * vig_Dy); } - for (int c = 0; c < (useCA ? 3 : 1); c++) { + for (int c = 0; c < (enableCA ? 3 : 1); c++) { double Dx = Dxc * (s + chDist[c]); double Dy = Dyc * (s + chDist[c]); @@ -912,11 +869,6 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag Dx += w2; Dy += h2; - // LCP CA - if (enableLCPCA) { - pLCPMap->correctCA (Dx, Dy, cx, cy, c); - } - // Extract integer and fractions of source screen coordinates int xc = (int)Dx; Dx -= (double)xc; @@ -949,9 +901,9 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag if (yc > 0 && yc < original->getHeight() - 2 && xc > 0 && xc < original->getWidth() - 2) { // all interpolation pixels inside image - if (useCA) { + if (enableCA) { interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, & (chTrans[c][y][x]), vignmul); - } else if (mode == ImProcFunctions::TRANSFORM_PREVIEW) { + } else if (!highQuality) { transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy); transformed->g (y, x) = vignmul * (original->g (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g (yc, xc + 1) * Dx * (1.0 - Dy) + original->g (yc + 1, xc) * (1.0 - Dx) * Dy + original->g (yc + 1, xc + 1) * Dx * Dy); transformed->b (y, x) = vignmul * (original->b (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b (yc, xc + 1) * Dx * (1.0 - Dy) + original->b (yc + 1, xc) * (1.0 - Dx) * Dy + original->b (yc + 1, xc + 1) * Dx * Dy); @@ -965,7 +917,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag int x1 = LIM (xc, 0, original->getWidth() - 1); int x2 = LIM (xc + 1, 0, original->getWidth() - 1); - if (useCA) { + if (enableCA) { chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy); } else { transformed->r (y, x) = vignmul * (original->r (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->r (y1, x2) * Dx * (1.0 - Dy) + original->r (y2, x1) * (1.0 - Dx) * Dy + original->r (y2, x2) * Dx * Dy); @@ -974,7 +926,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } } } else { - if (useCA) { + if (enableCA) { // not valid (source pixel x,y not inside source image, etc.) chTrans[c][y][x] = 0; } else { @@ -989,6 +941,65 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } +void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, int oW, int oH, const LensCorrection *pLCPMap) +{ + assert(pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()); + + double w2 = (double) oW / 2.0 - 0.5; + double h2 = (double) oH / 2.0 - 0.5; + + float** chOrig[3]; + chOrig[0] = original->r.ptrs; + chOrig[1] = original->g.ptrs; + chOrig[2] = original->b.ptrs; + + float** chTrans[3]; + chTrans[0] = transformed->r.ptrs; + chTrans[1] = transformed->g.ptrs; + chTrans[2] = transformed->b.ptrs; + + #pragma omp parallel for if (multiThread) + + for (int y = 0; y < transformed->getHeight(); y++) { + for (int x = 0; x < transformed->getWidth(); x++) { + for (int c = 0; c < 3; c++) { + double Dx = x; + double Dy = y; + + pLCPMap->correctCA(Dx, Dy, cx, cy, c); + + // Extract integer and fractions of coordinates + int xc = (int)Dx; + Dx -= (double)xc; + int yc = (int)Dy; + Dy -= (double)yc; + + // Convert only valid pixels + if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) { + + // multiplier for vignetting correction + if (yc > 0 && yc < original->getHeight() - 2 && xc > 0 && xc < original->getWidth() - 2) { + // all interpolation pixels inside image + interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, & (chTrans[c][y][x]), 1.0); + } else { + // edge pixels + int y1 = LIM (yc, 0, original->getHeight() - 1); + int y2 = LIM (yc + 1, 0, original->getHeight() - 1); + int x1 = LIM (xc, 0, original->getWidth() - 1); + int x2 = LIM (xc + 1, 0, original->getWidth() - 1); + + chTrans[c][y][x] = (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy); + } + } else { + // not valid (source pixel x,y not inside source image, etc.) + chTrans[c][y][x] = 0; + } + } + } + } +} + + double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap) { if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) { From 770479f643e0dde2629d97917025bd17f0df4b02 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 31 Oct 2017 09:38:27 +0100 Subject: [PATCH 5/7] removed unused parameters from transformLCPCAOnly --- rtengine/improcfun.h | 2 +- rtengine/iptransform.cc | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 7f9acc9e7..136e9491b 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -56,7 +56,7 @@ class ImProcFunctions void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); void transformGeneral(bool highQuality, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); - void transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, int oW, int oH, const LensCorrection *pLCPMap); + void transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, const LensCorrection *pLCPMap); void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam); void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 9871c7a57..cb14727a9 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -341,7 +341,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, // image. There's room for optimization of course... if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) { tmpimg.reset(new Imagefloat(original->getWidth(), original->getHeight())); - transformLCPCAOnly(original, tmpimg.get(), cx, cy, oW, oH, pLCPMap.get()); + transformLCPCAOnly(original, tmpimg.get(), cx, cy, pLCPMap.get()); original = tmpimg.get(); } } @@ -941,13 +941,10 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I } -void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, int oW, int oH, const LensCorrection *pLCPMap) +void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, const LensCorrection *pLCPMap) { assert(pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()); - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; - float** chOrig[3]; chOrig[0] = original->r.ptrs; chOrig[1] = original->g.ptrs; From 2006a1755b49799f919a2242fbb46dcd5ee0b035 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 31 Oct 2017 10:49:40 +0100 Subject: [PATCH 6/7] fixed regression in ImProcFunctions::transformGeneral introduced by recent refactorings --- rtengine/iptransform.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index cb14727a9..4d71b236b 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -751,9 +751,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I double h2 = (double) oH / 2.0 - 0.5; double vig_w2, vig_h2, maxRadius, v, b, mul; - if (enableVignetting) { - calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); - } + calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); struct grad_params gp; From 99f41baf4f0d6ae8adb59b44b7efa8200d486c1d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 14 Nov 2017 22:52:57 +0100 Subject: [PATCH 7/7] disable LCP-based CA correction, as it is currently broken --- rtengine/lcp.cc | 2 +- rtgui/lensprofile.cc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 44a470e17..cd338258f 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -1023,7 +1023,7 @@ rtengine::LCPMapper::LCPMapper( bool rtengine::LCPMapper::isCACorrectionAvailable() const { - return enableCA; + return false /*enableCA*/; // agriggio TODO -- this is currently broken } void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy, double scale) const diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index d158f44f4..d96d65cb6 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -192,7 +192,7 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa ckbUseDist->set_active (pp->lensProf.useDist); ckbUseVign->set_active (pp->lensProf.useVign && isRaw); - ckbUseCA->set_active (pp->lensProf.useCA && isRaw); + ckbUseCA->set_active(pp->lensProf.useCA && isRaw && ckbUseCA->get_sensitive()); const LFDatabase *db = LFDatabase::getInstance(); LFCamera c; @@ -380,7 +380,8 @@ void LensProfilePanel::updateDisabled(bool enable) { ckbUseDist->set_sensitive(enable); ckbUseVign->set_sensitive(enable && isRaw); - ckbUseCA->set_sensitive(enable && allowFocusDep); + // agriggio TODO -- CA correction via LCP is currently broken + ckbUseCA->set_sensitive(false);//enable && allowFocusDep); } void LensProfilePanel::setBatchMode(bool yes)