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.
This commit is contained in:
Simone Gotti
2024-06-13 09:34:14 +02:00
parent 363f3cc2a6
commit 55fd4b975e

View File

@@ -420,7 +420,6 @@ homogeneous::Matrix<double> perspectiveMatrix(double camera_focal_length, double
bool ImProcFunctions::transCoord (int W, int H, const std::vector<Coord2D> &src, std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &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<Imagefloat> 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)) {