diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index c5c8afa82..f52cfa256 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -32,6 +32,7 @@ set(CAMCONSTSFILE "camconst.json") set(RTENGINESOURCEFILES badpixels.cc CA_correct_RT.cc + capturesharpening.cc EdgePreservingDecomposition.cc FTblockDN.cc PF_correct_RT.cc diff --git a/rtengine/capturesharpening.cc b/rtengine/capturesharpening.cc new file mode 100644 index 000000000..71cefaca6 --- /dev/null +++ b/rtengine/capturesharpening.cc @@ -0,0 +1,218 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Ingo Weyrich (heckflosse67@gmx.de) + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . +*/ +#include +#include + +#include "jaggedarray.h" +#include "rtengine.h" +#include "rawimagesource.h" +#include "rt_math.h" +#include "improcfun.h" +#include "procparams.h" +#include "color.h" +#include "gauss.h" +#include "rt_algo.h" +#define BENCHMARK +#include "StopWatch.h" +#ifdef _OPENMP +#include +#endif +#include "opthelper.h" +#include "../rtgui/multilangmgr.h" + +namespace { +void CaptureDeconvSharpening (float** luminance, float** tmp, const float * const * blend, int W, int H, double sigma, int iterations, rtengine::ProgressListener* plistener, double start, double step) +{ + + rtengine::JaggedArray tmpI(W, H); + +#ifdef _OPENMP + #pragma omp parallel +#endif + { +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H; i++) { + for(int j = 0; j < W; j++) { + tmpI[i][j] = max(luminance[i][j], 0.f); + } + } + + for (int k = 0; k < iterations; k++) { + // apply gaussian blur and divide luminance by result of gaussian blur + gaussianBlur(tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance); + gaussianBlur(tmp, tmpI, W, H, sigma, nullptr, GAUSS_MULT); + if (plistener) { +#ifdef _OPENMP + #pragma omp single +#endif + start += step; + plistener->setProgress(start); + } + } // end for + +#ifdef _OPENMP + #pragma omp for +#endif + + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + luminance[i][j] = rtengine::intp(blend[i][j], max(tmpI[i][j], 0.0f), luminance[i][j]); + } + } + } // end parallel +} + +} + +namespace rtengine +{ + +void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { +BENCHFUN + + + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.0); + } + + const float xyz_rgb[3][3] = { // XYZ from RGB + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } + }; + + float contrast = conrastThreshold / 100.f; + + array2D& redVals = redCache ? *redCache : red; + array2D& greenVals = greenCache ? *greenCache : green; + array2D& blueVals = blueCache ? *blueCache : blue; + + if (showMask) { + StopWatch Stop1("Show mask"); + array2D& L = blue; // blue will be overridden anyway => we can use its buffer to store L +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H; ++i) { + Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); + } + if (plistener) { + plistener->setProgress(0.1); + } + array2D& blend = red; // red will be overridden anyway => we can use its buffer to store the blend mask + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + if (plistener) { + plistener->setProgress(0.2); + } + conrastThreshold = contrast * 100.f; +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f; + } + } + if (plistener) { + plistener->setProgress(1.0); + } + return; + } + + array2D* Lbuffer = nullptr; + if (!redCache) { + Lbuffer = new array2D(W, H); + } + + array2D* YOldbuffer = nullptr; + if (!greenCache) { + YOldbuffer = new array2D(W, H); + } + + array2D* YNewbuffer = nullptr; + if (!blueCache) { + YNewbuffer = new array2D(W, H); + } + array2D& L = Lbuffer ? *Lbuffer : red; + array2D& YOld = YOldbuffer ? * YOldbuffer : green; + array2D& YNew = YNewbuffer ? * YNewbuffer : blue; + + StopWatch Stop1("rgb2YL"); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int i = 0; i < H; ++i) { + Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); + Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W); + } + if (plistener) { + plistener->setProgress(0.1); + } + // calculate contrast based blend factors to reduce sharpening in regions with low contrast + JaggedArray blend(W, H); + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + if (plistener) { + plistener->setProgress(0.2); + } + conrastThreshold = contrast * 100.f; + + Stop1.stop(); + array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer + CaptureDeconvSharpening(YNew, tmp, blend, W, H, sharpeningParams.deconvradius, sharpeningParams.deconviter, plistener, 0.2, (0.9 - 0.2) / sharpeningParams.deconviter); + if (plistener) { + plistener->setProgress(0.9); + } + StopWatch Stop2("Y2RGB"); + const float gamma = sharpeningParams.gamma; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int i = 0; i < H; ++i) { + int j = 0; +#ifdef __SSE2__ + const vfloat gammav = F2V(gamma); + for (; j < W - 3; j += 4) { + const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); + STVFU(red[i][j], LVFU(redVals[i][j]) * factor); + STVFU(green[i][j], LVFU(greenVals[i][j]) * factor); + STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor); + } + +#endif + for (; j < W; ++j) { + const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); + red[i][j] = redVals[i][j] * factor; + green[i][j] = greenVals[i][j] * factor; + blue[i][j] = blueVals[i][j] * factor; + } + } + Stop2.stop(); + delete Lbuffer; + delete YOldbuffer; + delete YNewbuffer; + if (plistener) { + plistener->setProgress(1.0); + } +} + +} /* namespace */ diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 1a5469744..64bb53a49 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -46,7 +46,7 @@ struct LensProfParams; struct RAWParams; struct RetinexParams; struct ToneCurveParams; -struct SharpeningParams; +struct CaptureSharpeningParams; } class ImageMatrices @@ -182,7 +182,7 @@ public: return this; } virtual void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) = 0; - virtual void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; + virtual void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; }; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 60b95a2e8..d88892a3b 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1149,6 +1149,32 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const return !(*this == other); } +CaptureSharpeningParams::CaptureSharpeningParams() : + enabled(false), + autoContrast(true), + contrast(10.0), + gamma(1.35), + deconvradius(0.75), + deconviter(30) +{ +} + +bool CaptureSharpeningParams::operator ==(const CaptureSharpeningParams& other) const +{ + return + enabled == other.enabled + && contrast == other.contrast + && gamma == other.gamma + && autoContrast == other.autoContrast + && deconvradius == other.deconvradius + && deconviter == other.deconviter; +} + +bool CaptureSharpeningParams::operator !=(const CaptureSharpeningParams& other) const +{ + return !(*this == other); +} + SharpenEdgeParams::SharpenEdgeParams() : enabled(false), passes(2), @@ -2830,12 +2856,6 @@ void ProcParams::setDefaults() prsharpening.deconvdamping = 0; pdsharpening = {}; - pdsharpening.contrast = 10.0; - pdsharpening.autoContrast = true; - prsharpening.method = "rld"; - pdsharpening.gamma = 1.35; - pdsharpening.deconvradius = 0.75; - pdsharpening.deconviter = 30; vibrance = {}; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 4be6de254..cbcec0e18 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -542,6 +542,20 @@ struct SharpenMicroParams { bool operator !=(const SharpenMicroParams& other) const; }; +struct CaptureSharpeningParams { + bool enabled; + bool autoContrast; + double contrast; + double gamma; + double deconvradius; + int deconviter; + + CaptureSharpeningParams(); + + bool operator ==(const CaptureSharpeningParams& other) const; + bool operator !=(const CaptureSharpeningParams& other) const; +}; + /** * Parameters of the vibrance */ @@ -1530,7 +1544,7 @@ public: ColorToningParams colorToning; ///< Color Toning parameters SharpeningParams sharpening; ///< Sharpening parameters SharpeningParams prsharpening; ///< Sharpening parameters for post resize sharpening - SharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening + CaptureSharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening SharpenEdgeParams sharpenEdge; ///< Sharpen edge parameters SharpenMicroParams sharpenMicro; ///< Sharpen microcontrast parameters VibranceParams vibrance; ///< Vibrance parameters diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 2b45bfe38..931a46ede 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -38,14 +38,12 @@ #include "camconst.h" #include "procparams.h" #include "color.h" -#include "rt_algo.h" -#define BENCHMARK -#include "StopWatch.h" +//#define BENCHMARK +//#include "StopWatch.h" #ifdef _OPENMP #include #endif #include "opthelper.h" -#include "../rtgui/multilangmgr.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #undef CLIPD #define CLIPD(a) ((a)>0.0f?((a)<1.0f?(a):1.0f):0.0f) @@ -5000,138 +4998,6 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int } } -void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { -BENCHFUN - - - if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); - plistener->setProgress(0.0); - } - - const float xyz_rgb[3][3] = { // XYZ from RGB - { 0.412453, 0.357580, 0.180423 }, - { 0.212671, 0.715160, 0.072169 }, - { 0.019334, 0.119193, 0.950227 } - }; - - float contrast = conrastThreshold / 100.f; - - array2D& redVals = redCache ? *redCache : red; - array2D& greenVals = greenCache ? *greenCache : green; - array2D& blueVals = blueCache ? *blueCache : blue; - - if (showMask) { - StopWatch Stop1("Show mask"); - array2D& L = blue; // blue will be overridden anyway => we can use its buffer to store L -#ifdef _OPENMP - #pragma omp parallel for -#endif - - for (int i = 0; i < H; ++i) { - Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); - } - if (plistener) { - plistener->setProgress(0.1); - } - array2D& blend = red; // red will be overridden anyway => we can use its buffer to store the blend mask - buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); - if (plistener) { - plistener->setProgress(0.2); - } - conrastThreshold = contrast * 100.f; -#ifdef _OPENMP - #pragma omp parallel for -#endif - for (int i = 0; i < H; ++i) { - for (int j = 0; j < W; ++j) { - red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f; - } - } - if (plistener) { - plistener->setProgress(1.0); - } - return; - } - - array2D* Lbuffer = nullptr; - if (!redCache) { - Lbuffer = new array2D(W, H); - } - - array2D* YOldbuffer = nullptr; - if (!greenCache) { - YOldbuffer = new array2D(W, H); - } - - array2D* YNewbuffer = nullptr; - if (!blueCache) { - YNewbuffer = new array2D(W, H); - } - array2D& L = Lbuffer ? *Lbuffer : red; - array2D& YOld = YOldbuffer ? * YOldbuffer : green; - array2D& YNew = YNewbuffer ? * YNewbuffer : blue; - - StopWatch Stop1("rgb2YL"); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) -#endif - for (int i = 0; i < H; ++i) { - Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); - Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W); - } - if (plistener) { - plistener->setProgress(0.1); - } - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); - if (plistener) { - plistener->setProgress(0.2); - } - conrastThreshold = contrast * 100.f; - - Stop1.stop(); - array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer - ProcParams dummy; - ImProcFunctions ipf(&dummy); - ipf.deconvsharpening(YNew, tmp, blend, W, H, sharpeningParams, 1.0); - if (plistener) { - plistener->setProgress(0.9); - } - StopWatch Stop2("Y2RGB"); - const float gamma = sharpeningParams.gamma; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) -#endif - for (int i = 0; i < H; ++i) { - int j = 0; -#ifdef __SSE2__ - const vfloat gammav = F2V(gamma); - for (; j < W - 3; j += 4) { - const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); - STVFU(red[i][j], LVFU(redVals[i][j]) * factor); - STVFU(green[i][j], LVFU(greenVals[i][j]) * factor); - STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor); - } - -#endif - for (; j < W; ++j) { - const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); - red[i][j] = redVals[i][j] * factor; - green[i][j] = greenVals[i][j] * factor; - blue[i][j] = blueVals[i][j] * factor; - } - } - Stop2.stop(); - delete Lbuffer; - delete YOldbuffer; - delete YNewbuffer; - if (plistener) { - plistener->setProgress(1.0); - } -} - void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index bbc15c448..5e1c9b424 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -311,7 +311,7 @@ protected: void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; }; } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 175f664f8..6b4a67773 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -102,7 +102,7 @@ public: void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;} void flushRGB () override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; }; } #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 4029d0b9b..db0c24123 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -169,7 +169,6 @@ void ParamsEdited::set(bool v) pdsharpening.contrast = v; pdsharpening.autoContrast = v; pdsharpening.gamma = v; - pdsharpening.deconvamount = v; pdsharpening.deconvradius = v; pdsharpening.deconviter = v; prsharpening.enabled = v; @@ -1733,10 +1732,6 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast; } - if (pdsharpening.deconvamount) { - toEdit.pdsharpening.deconvamount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.pdsharpening.deconvamount + mods.pdsharpening.deconvamount : mods.pdsharpening.deconvamount; - } - if (pdsharpening.gamma) { toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma; } @@ -3292,7 +3287,7 @@ bool FilmNegativeParamsEdited::isUnchanged() const return enabled && redRatio && greenExp && blueRatio; } -bool SharpeningParamsEdited::isUnchanged() const +bool CaptureSharpeningParamsEdited::isUnchanged() const { return enabled && contrast && autoContrast && gamma && deconvradius && deconviter; } \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 6d229e689..c697aaacd 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -196,6 +196,15 @@ struct SharpeningParamsEdited { bool deconvradius; bool deconviter; bool deconvdamping; +}; + +struct CaptureSharpeningParamsEdited { + bool enabled; + bool contrast; + bool autoContrast; + bool gamma; + bool deconvradius; + bool deconviter; bool isUnchanged() const; }; @@ -687,7 +696,7 @@ struct ParamsEdited { ColorToningEdited colorToning; RetinexParamsEdited retinex; SharpeningParamsEdited sharpening; - SharpeningParamsEdited pdsharpening; + CaptureSharpeningParamsEdited pdsharpening; SharpeningParamsEdited prsharpening; SharpenEdgeParamsEdited sharpenEdge; SharpenMicroParamsEdited sharpenMicro;