From 55fd4b975e48141e73e068c8332a46caba5755d1 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Thu, 13 Jun 2024 09:34:14 +0200 Subject: [PATCH] iptransform: move profile based CA with and after distortion Doing profile based CA as the really first correction in a separate steps is probably not useful. Additionally many lens profile (lensfun) and future one provide functions to do them in a single step with better precision while other just provide a single step that does both (i.e. DNG WarpRectilinear). For the above reasons this patch removes the additional pass for CA correction. This will also improve the perfomance due to less work. --- rtengine/iptransform.cc | 136 +++++++++------------------------------- 1 file changed, 29 insertions(+), 107 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index d62a87199..757d3d54c 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -420,7 +420,6 @@ homogeneous::Matrix perspectiveMatrix(double camera_focal_length, double bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef, const LensCorrection *pLCPMap) const { - enum PerspType { NONE, SIMPLE, CAMERA_BASED }; const PerspType perspectiveType = needsPerspective() ? ( (params->perspective.method == "camera_based") ? @@ -686,24 +685,8 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, highQuality = false; } else { highQuality = true; - // agriggio: CA correction via the lens profile has to be - // performed before separately from the 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_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())); - dest = tmpimg.get(); - } } transformGeneral(highQuality, original, dest, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get(), useOriginalBuffer); - - if (highQuality && dest != transformed) { - transformLCPCAOnly(dest, transformed, cx, cy, pLCPMap.get(), useOriginalBuffer); - } } } @@ -1095,7 +1078,9 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I // set up stuff, depending on the mode we are enum PerspType { NONE, SIMPLE, CAMERA_BASED }; const bool enableLCPDist = pLCPMap && params->lensProf.useDist; + const bool enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); const bool enableCA = highQuality && needsCA(); + const bool doCACorrection = enableCA || enableLCPCA; const bool enableGradient = needsGradient(); const bool enablePCVignetting = needsPCVignetting(); const bool enableVignetting = needsVignetting(); @@ -1241,24 +1226,32 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I } // rotate - double Dxr = x_d * cost - y_d * sint; - double Dyr = x_d * sint + y_d * cost; + const double Dxr = x_d * cost - y_d * sint; + const double Dyr = x_d * sint + y_d * cost; - if (enableLCPDist) { - pLCPMap->correctDistortion(Dxr, Dyr, w2, h2); - } + for (int c = 0; c < (doCACorrection ? 3 : 1); ++c) { + double Dx = Dxr; + double Dy = Dyr; - // distortion correction - double s = 1.0; + if (enableLCPCA) { + pLCPMap->correctCA(Dx, Dy, w2, h2, c); + } - if (enableDistortion) { - const double r = sqrt(Dxr * Dxr + Dyr * Dyr) / maxRadius; - s = 1.0 - distAmount + distAmount * r; - } + if (enableLCPDist) { + pLCPMap->correctDistortion(Dx, Dy, w2, h2); + } - for (int c = 0; c < (enableCA ? 3 : 1); ++c) { - double Dx = Dxr * (s + chDist[c]); - double Dy = Dyr * (s + chDist[c]); + // distortion correction + double s = 1.0; + + if (enableDistortion) { + const double r = sqrt(Dx * Dx + Dy * Dy) / maxRadius; + s = 1.0 - distAmount + distAmount * r; + } + + // CA correction + Dx *= s + chDist[c]; + Dy *= s + chDist[c]; // de-center Dx += w2; @@ -1305,13 +1298,13 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I 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); } else if (!useLog) { - if (enableCA) { + if (doCACorrection) { interpolateTransformChannelsCubic(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul); } else { interpolateTransformCubic(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul); } } else { - if (enableCA) { + if (doCACorrection) { interpolateTransformChannelsCubicLog(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul); } else { interpolateTransformCubicLog(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul); @@ -1325,7 +1318,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I const int x2 = LIM(xc + 1, 0, original->getWidth() - 1); if (useLog) { - if (enableCA) { + if (doCACorrection) { chTrans[c][y][x] = vignmul * xexpf(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 * xexpf(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); @@ -1333,7 +1326,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I transformed->b(y, x) = vignmul * xexpf(original->b(y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->b(y1, x2) * Dx * (1.0 - Dy) + original->b(y2, x1) * (1.0 - Dx) * Dy + original->b(y2, x2) * Dx * Dy); } } else { - if (enableCA) { + if (doCACorrection) { 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); @@ -1343,7 +1336,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I } } } else { - if (enableCA) { + if (doCACorrection) { // not valid (source pixel x,y not inside source image, etc.) chTrans[c][y][x] = 0; } else { @@ -1357,77 +1350,6 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I } } - -void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, const LensCorrection *pLCPMap, bool useOriginalBuffer) -{ - assert(pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()); - const bool useLog = params->commonTrans.method == "log"; - - float** chTrans[3] = {transformed->r.ptrs, transformed->g.ptrs, transformed->b.ptrs}; - - std::unique_ptr tempLog; - if (useLog) { - if (!useOriginalBuffer) { - tempLog.reset(new Imagefloat(original->getWidth(), original->getHeight())); - logEncode(original, tempLog.get(), multiThread); - original = tempLog.get(); - } else { - logEncode(original, original, multiThread); - } - } - float** chOrig[3] = {original->r.ptrs, original->g.ptrs, original->b.ptrs}; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - - 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 - if (!useLog) { - interpolateTransformChannelsCubic(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], 1.0); - } else { - interpolateTransformChannelsCubicLog(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); - if (!useLog) { - 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 { - chTrans[c][y][x] = xexpf(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) const { if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) {