diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 5746ce285..09a9ccd4e 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -54,13 +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 - }; 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, 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 d73dc8ac0..ff976137e 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 ) ); @@ -325,18 +325,31 @@ 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; - } else if (!fullImage) { - mode = TRANSFORM_HIGH_QUALITY; + highQuality = false; } 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 + // 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(original->getWidth(), original->getHeight())); + transformLCPCAOnly(original, tmpimg.get(), cx, cy, 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) { @@ -723,8 +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 enableCA = highQuality && needsCA(); + bool enableGradient = needsGradient(); + bool enablePCVignetting = needsPCVignetting(); + bool enableVignetting = needsVignetting(); + bool enablePerspective = needsPerspective(); + bool enableDistortion = needsDistortion(); + double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; @@ -733,13 +755,13 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag 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 +777,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 @@ -783,45 +804,13 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag 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; - #if defined( __GNUC__ ) && __GNUC__ >= 7// silence warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif - 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; - } - } #if defined( __GNUC__ ) && __GNUC__ >= 7 #pragma GCC diagnostic pop #endif - - if (enableLCPCA) { - enableLCPDist = false; - } - - if (!enableCA) { - chDist[0] = 0.0; - } - // main cycle bool darkening = (params->vignetting.amount <= 0.0); #pragma omp parallel for if (multiThread) @@ -842,12 +831,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); @@ -864,14 +853,14 @@ 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); @@ -885,11 +874,6 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag Dx += w2; Dy += h2; - // LCP CA - if (enableLCPCA) { - pLCPMap->correctCA (Dx, Dy, c); - } - // Extract integer and fractions of source screen coordinates int xc = (int)Dx; Dx -= (double)xc; @@ -904,7 +888,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 { @@ -912,11 +896,11 @@ 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); } @@ -924,7 +908,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag // all interpolation pixels inside image 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); @@ -962,6 +946,62 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } +void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, const LensCorrection *pLCPMap) +{ + assert(pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()); + + 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)) { diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 3824d2b2f..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 @@ -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 aa163a4b0..8216f0b83 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -72,7 +72,7 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double bool LFModifier::isCACorrectionAvailable() const { - return false; + return (flags_ & LF_MODIFY_TCA); } #ifdef __GNUC__ // silence warning, can be removed when function is implemented @@ -80,8 +80,29 @@ bool LFModifier::isCACorrectionAvailable() const #pragma GCC diagnostic ignored "-Wunused-parameter" #endif -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); + + // 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 + x += cx; + y += cy; + + 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); + } + x -= cx; + y -= cy; } #ifdef __GNUC__ @@ -117,6 +138,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"; @@ -262,6 +288,15 @@ bool LFLens::hasDistortionCorrection() const } } +bool LFLens::hasCACorrection() const +{ + if (data_) { + return data_->CalibTCA; + } else { + return false; + } +} + //----------------------------------------------------------------------------- // LFDatabase @@ -443,7 +478,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 686b8b9c4..7690ef544 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; @@ -91,6 +91,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 310c48d41..b9ec44d7c 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; @@ -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); + } } } @@ -370,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)