diff --git a/rtdata/languages/default b/rtdata/languages/default index 499005d32..5bf1863df 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -726,6 +726,7 @@ HISTORY_MSG_490;DRC - Amount HISTORY_MSG_491;White Balance HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments +HISTORY_MSG_494;Capture Sharpening HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction @@ -763,6 +764,11 @@ HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius HISTORY_MSG_METADATA_MODE;Metadata copy mode HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold +HISTORY_MSG_PDSHARPEN_CONTRAST;CAS - Contrast threshold +HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CAS - Auto threshold +HISTORY_MSG_PDSHARPEN_GAMMA;CAS - Gamma +HISTORY_MSG_PDSHARPEN_ITERATIONS;CAS - Iterations +HISTORY_MSG_PDSHARPEN_RADIUS;CAS - Radius HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter @@ -773,6 +779,7 @@ HISTORY_MSG_RAW_BORDER;Raw border HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling HISTORY_MSG_SHARPENING_BLUR;Sharpening - Blur radius HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold +HISTORY_MSG_SHARPENING_GAMMA;Sharpening - Gamma HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength @@ -1792,6 +1799,7 @@ TP_PCVIGNETTE_ROUNDNESS;Roundness TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse,\n100 = circle. TP_PCVIGNETTE_STRENGTH;Strength TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners). +TP_PDSHARPENING_LABEL;Capture Sharpening TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_VERTICAL;Vertical @@ -2020,6 +2028,7 @@ TP_SHARPENING_BLUR;Blur radius TP_SHARPENING_CONTRAST;Contrast threshold TP_SHARPENING_EDRADIUS;Radius TP_SHARPENING_EDTOLERANCE;Edge tolerance +TP_SHARPENING_GAMMA;Gamma TP_SHARPENING_HALOCONTROL;Halo control TP_SHARPENING_HCAMOUNT;Amount TP_SHARPENING_LABEL;Sharpening 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..eb95d2633 --- /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(vmaxf(LVFU(YNew[i][j]), ZEROV) / 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(std::max(YNew[i][j], 0.f) / 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/color.cc b/rtengine/color.cc index 24266e52d..7a91f0073 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -1835,21 +1835,21 @@ void Color::RGB2L(float *R, float *G, float *B, float *L, const float wp[3][3], { #ifdef __SSE2__ - vfloat minvalfv = F2V(0.f); - vfloat maxvalfv = F2V(MAXVALF); + const vfloat maxvalfv = F2V(MAXVALF); + const vfloat rmv = F2V(wp[1][0]); + const vfloat gmv = F2V(wp[1][1]); + const vfloat bmv = F2V(wp[1][2]); #endif int i = 0; #ifdef __SSE2__ - for(;i < width - 3; i+=4) { + for(; i < width - 3; i+=4) { const vfloat rv = LVFU(R[i]); const vfloat gv = LVFU(G[i]); const vfloat bv = LVFU(B[i]); - const vfloat yv = F2V(wp[1][0]) * rv + F2V(wp[1][1]) * gv + F2V(wp[1][2]) * bv; + const vfloat yv = rmv * rv + gmv * gv + bmv * bv; - vmask maxMask = vmaskf_gt(yv, maxvalfv); - vmask minMask = vmaskf_lt(yv, minvalfv); - if (_mm_movemask_ps((vfloat)vorm(maxMask, minMask))) { + if (_mm_movemask_ps((vfloat)vorm(vmaskf_gt(yv, maxvalfv), vmaskf_lt(yv, ZEROV)))) { // take slower code path for all 4 pixels if one of the values is > MAXVALF. Still faster than non SSE2 version for(int k = 0; k < 4; ++k) { float y = yv[k]; @@ -1860,7 +1860,7 @@ void Color::RGB2L(float *R, float *G, float *B, float *L, const float wp[3][3], } } #endif - for(;i < width; ++i) { + for(; i < width; ++i) { const float rv = R[i]; const float gv = G[i]; const float bv = B[i]; diff --git a/rtengine/color.h b/rtengine/color.h index 214f420d7..b859fb0cf 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1804,6 +1804,31 @@ public: return (hr); } + static inline void RGB2Y(const float* R, const float* G, const float* B, float* Y1, float * Y2, float gamma, int W) { + gamma = 1.f / gamma; + int i = 0; +#ifdef __SSE2__ + const vfloat gammav = F2V(gamma); + const vfloat c1v = F2V(0.2627f); + const vfloat c2v = F2V(0.6780f); + const vfloat c3v = F2V(0.0593f); + for (; i < W - 3; i += 4) { + const vfloat Rv = vmaxf(LVFU(R[i]), ZEROV); + const vfloat Gv = vmaxf(LVFU(G[i]), ZEROV); + const vfloat Bv = vmaxf(LVFU(B[i]), ZEROV); + vfloat yv = pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav); + STVFU(Y1[i], yv); + STVFU(Y2[i], yv); + } +#endif + for (; i < W; ++i) { + const float r = std::max(R[i], 0.f); + const float g = std::max(G[i], 0.f); + const float b = std::max(B[i], 0.f); + Y1[i] = Y2[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma); + } + } + }; } diff --git a/rtengine/gauss.cc b/rtengine/gauss.cc index 40014f211..ca330f9b9 100644 --- a/rtengine/gauss.cc +++ b/rtengine/gauss.cc @@ -17,14 +17,56 @@ * along with RawTherapee. If not, see . */ #include "gauss.h" +#include "rt_math.h" #include #include #include "opthelper.h" #include "boxblur.h" - namespace { +void compute7x7kernel(float sigma, float kernel[7][7]) { + const double temp = -2.f * rtengine::SQR(sigma); + float sum = 0.f; + for (int i = -3; i <= 3; ++i) { + for (int j = -3; j <= 3; ++j) { + if((rtengine::SQR(i) + rtengine::SQR(j)) <= rtengine::SQR(3.0 * 1.15)) { + kernel[i + 3][j + 3] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); + sum += kernel[i + 3][j + 3]; + } else { + kernel[i + 3][j + 3] = 0.f; + } + } + } + + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 7; ++j) { + kernel[i][j] /= sum; + } + } +} + +void compute5x5kernel(float sigma, float kernel[5][5]) { + const double temp = -2.f * rtengine::SQR(sigma); + float sum = 0.f; + for (int i = -2; i <= 2; ++i) { + for (int j = -2; j <= 2; ++j) { + if((rtengine::SQR(i) + rtengine::SQR(j)) <= rtengine::SQR(3.0 * 0.84)) { + kernel[i + 2][j + 2] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); + sum += kernel[i + 2][j + 2]; + } else { + kernel[i + 2][j + 2] = 0.f; + } + } + } + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + kernel[i][j] /= sum; + } + } +} + template void calculateYvVFactors( const T sigma, T &b1, T &b2, T &b3, T &B, T M[3][3]) { // coefficient calculation @@ -207,6 +249,174 @@ template void gauss3x3div (T** RESTRICT src, T** RESTRICT dst, T** REST } } +template void gauss7x7div (T** RESTRICT src, T** RESTRICT dst, T** RESTRICT divBuffer, const int W, const int H, float sigma) +{ + + float kernel[7][7]; + compute7x7kernel(sigma, kernel); + + const float c31 = kernel[0][2]; + const float c30 = kernel[0][3]; + const float c22 = kernel[1][1]; + const float c21 = kernel[1][2]; + const float c20 = kernel[1][3]; + const float c11 = kernel[2][2]; + const float c10 = kernel[2][3]; + const float c00 = kernel[3][3]; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) nowait +#endif + + for (int i = 3; i < H - 3; ++i) { + dst[i][0] = dst[i][1] = dst[i][2] = 1.f; + // I tried hand written SSE code but gcc vectorizes better + for (int j = 3; j < W - 3; ++j) { + const float val = c31 * (src[i - 3][j - 1] + src[i - 3][j + 1] + src[i - 1][j - 3] + src[i - 1][j + 3] + src[i + 1][j - 3] + src[i + 1][j + 3] + src[i + 3][j - 1] + src[i + 3][j + 1]) + + c30 * (src[i - 3][j] + src[i][j - 3] + src[i][j + 3] + src[i + 3][j]) + + c22 * (src[i - 2][j - 2] + src[i - 2][j + 2] + src[i + 2][j - 2] + src[i + 2][j + 2]) + + c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] * c21 + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) + + c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) + + c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) + + c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) + + c00 * src[i][j]; + + dst[i][j] = divBuffer[i][j] / std::max(val, 0.00001f); + } + dst[i][W - 3] = dst[i][W - 2] = dst[i][W - 1] = 1.f; + } + + // first and last rows +#ifdef _OPENMP + #pragma omp single +#endif + { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < W; ++j) { + dst[i][j] = 1.f; + } + } + for (int i = H - 3 ; i < H; ++i) { + for (int j = 0; j < W; ++j) { + dst[i][j] = 1.f; + } + } + } +} + +template void gauss5x5div (T** RESTRICT src, T** RESTRICT dst, T** RESTRICT divBuffer, const int W, const int H, float sigma) +{ + + float kernel[5][5]; + compute5x5kernel(sigma, kernel); + + const float c21 = kernel[0][1]; + const float c20 = kernel[0][2]; + const float c11 = kernel[1][1]; + const float c10 = kernel[1][2]; + const float c00 = kernel[2][2]; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) nowait +#endif + + for (int i = 2; i < H - 2; ++i) { + dst[i][0] = dst[i][1] = 1.f; + // I tried hand written SSE code but gcc vectorizes better + for (int j = 2; j < W - 2; ++j) { + const float val = c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) + + c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) + + c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) + + c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) + + c00 * src[i][j]; + + dst[i][j] = divBuffer[i][j] / std::max(val, 0.00001f); + } + dst[i][W - 2] = dst[i][W - 1] = 1.f; + } + + // first and last rows +#ifdef _OPENMP + #pragma omp single +#endif + { + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < W; ++j) { + dst[i][j] = 1.f; + } + } + for (int i = H - 2 ; i < H; ++i) { + for (int j = 0; j < W; ++j) { + dst[i][j] = 1.f; + } + } + } +} + +template void gauss7x7mult (T** RESTRICT src, T** RESTRICT dst, const int W, const int H, float sigma) +{ + + float kernel[7][7]; + compute7x7kernel(sigma, kernel); + const float c31 = kernel[0][2]; + const float c30 = kernel[0][3]; + const float c22 = kernel[1][1]; + const float c21 = kernel[1][2]; + const float c20 = kernel[1][3]; + const float c11 = kernel[2][2]; + const float c10 = kernel[2][3]; + const float c00 = kernel[3][3]; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + + for (int i = 3; i < H - 3; ++i) { + // I tried hand written SSE code but gcc vectorizes better + for (int j = 3; j < W - 3; ++j) { + const float val = c31 * (src[i - 3][j - 1] + src[i - 3][j + 1] + src[i - 1][j - 3] + src[i - 1][j + 3] + src[i + 1][j - 3] + src[i + 1][j + 3] + src[i + 3][j - 1] + src[i + 3][j + 1]) + + c30 * (src[i - 3][j] + src[i][j - 3] + src[i][j + 3] + src[i + 3][j]) + + c22 * (src[i - 2][j - 2] + src[i - 2][j + 2] + src[i + 2][j - 2] + src[i + 2][j + 2]) + + c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] * c21 + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) + + c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) + + c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) + + c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) + + c00 * src[i][j]; + + dst[i][j] *= val; + } + } +} + +template void gauss5x5mult (T** RESTRICT src, T** RESTRICT dst, const int W, const int H, float sigma) +{ + + float kernel[5][5]; + compute5x5kernel(sigma, kernel); + + const float c21 = kernel[0][1]; + const float c20 = kernel[0][2]; + const float c11 = kernel[1][1]; + const float c10 = kernel[1][2]; + const float c00 = kernel[2][2]; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + + for (int i = 2; i < H - 2; ++i) { + // I tried hand written SSE code but gcc vectorizes better + for (int j = 2; j < W - 2; ++j) { + const float val = c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) + + c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) + + c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) + + c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) + + c00 * src[i][j]; + + dst[i][j] *= val; + } + } +} // use separated filter if the support window is small and src == dst template void gaussHorizontal3 (T** src, T** dst, int W, int H, const float c0, const float c1) @@ -1143,6 +1353,8 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int { static constexpr auto GAUSS_SKIP = 0.25; static constexpr auto GAUSS_3X3_LIMIT = 0.6; + static constexpr auto GAUSS_5X5_LIMIT = 0.84; + static constexpr auto GAUSS_7X7_LIMIT = 1.15; static constexpr auto GAUSS_DOUBLE = 25.0; if(buffer) { @@ -1241,14 +1453,26 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int if (sigma < GAUSS_DOUBLE) { switch (gausstype) { case GAUSS_MULT : { - gaussHorizontalSse (src, src, W, H, sigma); - gaussVerticalSsemult (src, dst, W, H, sigma); + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { + gauss5x5mult(src, dst, W, H, sigma); + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { + gauss7x7mult(src, dst, W, H, sigma); + } else { + gaussHorizontalSse (src, src, W, H, sigma); + gaussVerticalSsemult (src, dst, W, H, sigma); + } break; } case GAUSS_DIV : { - gaussHorizontalSse (src, dst, W, H, sigma); - gaussVerticalSsediv (dst, dst, buffer2, W, H, sigma); + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { + gauss5x5div (src, dst, buffer2, W, H, sigma); + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { + gauss7x7div (src, dst, buffer2, W, H, sigma); + } else { + gaussHorizontalSse (src, dst, W, H, sigma); + gaussVerticalSsediv (dst, dst, buffer2, W, H, sigma); + } break; } @@ -1268,14 +1492,26 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int if (sigma < GAUSS_DOUBLE) { switch (gausstype) { case GAUSS_MULT : { - gaussHorizontal (src, src, W, H, sigma); - gaussVerticalmult (src, dst, W, H, sigma); + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { + gauss5x5mult(src, dst, W, H, sigma); + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { + gauss7x7mult(src, dst, W, H, sigma); + } else { + gaussHorizontal (src, src, W, H, sigma); + gaussVerticalmult (src, dst, W, H, sigma); + } break; } case GAUSS_DIV : { - gaussHorizontal (src, dst, W, H, sigma); - gaussVerticaldiv (dst, dst, buffer2, W, H, sigma); + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { + gauss5x5div (src, dst, buffer2, W, H, sigma); + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { + gauss7x7div (src, dst, buffer2, W, H, sigma); + } else { + gaussHorizontal (src, dst, W, H, sigma); + gaussVerticaldiv (dst, dst, buffer2, W, H, sigma); + } break; } diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 705d29d62..c62dfe7d8 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -46,7 +46,7 @@ struct LensProfParams; struct RAWParams; struct RetinexParams; struct ToneCurveParams; - +struct CaptureSharpeningParams; } class ImageMatrices @@ -83,7 +83,7 @@ public: virtual void preprocess (const procparams::RAWParams &raw, const procparams::LensProfParams &lensProf, const procparams::CoarseTransformParams& coarse, bool prepareDenoise = true) {}; virtual void filmNegativeProcess (const procparams::FilmNegativeParams ¶ms) {}; virtual bool getFilmNegativeExponents (Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams& currentParams, std::array& newExps) { return false; }; - virtual void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold) {}; + virtual void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache = false) {}; virtual void retinex (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &deh, const procparams::ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) {}; virtual void retinexPrepareCurves (const procparams::RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI) {}; virtual void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI) {}; @@ -182,6 +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::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index b74c14e2e..c6268576a 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -53,6 +53,7 @@ ImProcCoordinator::ImProcCoordinator() : softProof(false), gamutCheck(false), sharpMask(false), + sharpMaskChanged(false), scale(10), highDetailPreprocessComputed(false), highDetailRawComputed(false), @@ -120,6 +121,7 @@ ImProcCoordinator::ImProcCoordinator() : flatFieldAutoClipListener(nullptr), bayerAutoContrastListener(nullptr), xtransAutoContrastListener(nullptr), + pdSharpenAutoContrastListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), actListener(nullptr), @@ -330,18 +332,31 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicAutoContrast : params->raw.xtranssensor.dualDemosaicAutoContrast; double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast; - imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic + imgsrc->demosaic(rp, autoContrast, contrastThreshold, params->pdsharpening.enabled); if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) { - bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); - } - if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) { + bayerAutoContrastListener->autoContrastChanged(contrastThreshold); + } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) { xtransAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); } - // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag - todo |= M_INIT; + todo |= (M_INIT | M_CSHARP); + } + + if ((todo & (M_RAW | M_CSHARP)) && params->pdsharpening.enabled) { + double pdSharpencontrastThreshold = params->pdsharpening.contrast; + imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold); + if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) { + pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold); + } + } + + + if ((todo & M_RAW) + || (!highDetailRawComputed && highDetailNeeded) + || (params->toneCurve.hrenabled && params->toneCurve.method != "Color" && imgsrc->isRGBSourceModified()) + || (!params->toneCurve.hrenabled && params->toneCurve.method == "Color" && imgsrc->isRGBSourceModified())) { if (highDetailNeeded) { highDetailRawComputed = true; } else { @@ -1344,9 +1359,16 @@ void ImProcCoordinator::getSoftProofing(bool &softProof, bool &gamutCheck) gamutCheck = this->gamutCheck; } -void ImProcCoordinator::setSharpMask (bool sharpMask) +ProcEvent ImProcCoordinator::setSharpMask (bool sharpMask) { - this->sharpMask = sharpMask; + if (this->sharpMask != sharpMask) { + sharpMaskChanged = true; + this->sharpMask = sharpMask; + return params->pdsharpening.enabled ? rtengine::EvPdShrMaskToggled : rtengine::EvShrEnabled; + } else { + sharpMaskChanged = false; + return rtengine::EvShrEnabled; + } } void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb) @@ -1546,8 +1568,11 @@ void ImProcCoordinator::process() || params->retinex != nextParams->retinex || params->wavelet != nextParams->wavelet || params->dirpyrequalizer != nextParams->dirpyrequalizer - || params->dehaze != nextParams->dehaze; + || params->dehaze != nextParams->dehaze + || params->pdsharpening != nextParams->pdsharpening + || sharpMaskChanged; + sharpMaskChanged = false; *params = *nextParams; int change = changeSinceLast; changeSinceLast = 0; diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index fbe233607..df4ec8134 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -77,7 +77,7 @@ protected: bool softProof; bool gamutCheck; bool sharpMask; - + bool sharpMaskChanged; int scale; bool highDetailPreprocessComputed; bool highDetailRawComputed; @@ -161,6 +161,7 @@ protected: FlatFieldAutoClipListener *flatFieldAutoClipListener; AutoContrastListener *bayerAutoContrastListener; AutoContrastListener *xtransAutoContrastListener; + AutoContrastListener *pdSharpenAutoContrastListener; FrameCountListener *frameCountListener; ImageTypeListener *imageTypeListener; @@ -277,7 +278,7 @@ public: void getMonitorProfile (Glib::ustring& profile, RenderingIntent& intent) const override; void setSoftProofing (bool softProof, bool gamutCheck) override; void getSoftProofing (bool &softProof, bool &gamutCheck) override; - void setSharpMask (bool sharpMask) override; + ProcEvent setSharpMask (bool sharpMask) override; bool updateTryLock () override { return updaterThreadStart.trylock(); @@ -363,6 +364,11 @@ public: xtransAutoContrastListener = acl; } + void setpdSharpenAutoContrastListener (AutoContrastListener* acl) override + { + pdSharpenAutoContrastListener = acl; + } + void setImageTypeListener (ImageTypeListener* itl) override { imageTypeListener = itl; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 8278e2f37..7f159090a 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -248,7 +248,7 @@ public: void Lanczos(const LabImage* src, LabImage* dst, float scale); void Lanczos(const Imagefloat* src, Imagefloat* dst, float scale); - void deconvsharpening(float** luminance, float** buffer, int W, int H, const procparams::SharpeningParams &sharpenParam); + void deconvsharpening(float** luminance, float** buffer, const float* const * blend, int W, int H, const procparams::SharpeningParams &sharpenParam, double Scale); void MLsharpen(LabImage* lab); // Manuel's clarity / sharpening void MLmicrocontrast(float** luminance, int W, int H); //Manuel's microcontrast void MLmicrocontrast(LabImage* lab); //Manuel's microcontrast diff --git a/rtengine/ipsharpen.cc b/rtengine/ipsharpen.cc index 73510fe92..bbd1de155 100644 --- a/rtengine/ipsharpen.cc +++ b/rtengine/ipsharpen.cc @@ -158,7 +158,7 @@ namespace rtengine extern const Settings* settings; -void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, int W, int H, const SharpeningParams &sharpenParam) +void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, const float * const * blend, int W, int H, const SharpeningParams &sharpenParam, double Scale) { if (sharpenParam.deconvamount == 0 && sharpenParam.blurradius < 0.25f) { return; @@ -175,10 +175,6 @@ BENCHFUN } } - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - float contrast = sharpenParam.contrast / 100.f; - buildBlendMask(luminance, blend, W, H, contrast, 1.f); JaggedArray* blurbuffer = nullptr; if (sharpenParam.blurradius >= 0.25f) { @@ -201,7 +197,7 @@ BENCHFUN } const float damping = sharpenParam.deconvdamping / 5.0; const bool needdamp = sharpenParam.deconvdamping > 0; - const double sigma = sharpenParam.deconvradius / scale; + const double sigma = sharpenParam.deconvradius / Scale; const float amount = sharpenParam.deconvamount / 100.f; #ifdef _OPENMP @@ -254,11 +250,12 @@ void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpen int W = lab->W, H = lab->H; + // calculate contrast based blend factors to reduce sharpening in regions with low contrast + JaggedArray blend(W, H); + float contrast = sharpenParam.contrast / 100.f; + buildBlendMask(lab->L, blend, W, H, contrast, 1.f); + if(showMask) { - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - float contrast = sharpenParam.contrast / 100.f; - buildBlendMask(lab->L, blend, W, H, contrast, 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -274,7 +271,7 @@ void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpen JaggedArray b2(W, H); if (sharpenParam.method == "rld") { - deconvsharpening (lab->L, b2, lab->W, lab->H, sharpenParam); + deconvsharpening (lab->L, b2, blend, lab->W, lab->H, sharpenParam, scale); return; } BENCHFUN @@ -290,11 +287,6 @@ BENCHFUN } } - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - float contrast = sharpenParam.contrast / 100.f; - buildBlendMask(lab->L, blend, W, H, contrast); - JaggedArray blur(W, H); if (sharpenParam.blurradius >= 0.25f) { @@ -886,11 +878,11 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) int W = ncie->W, H = ncie->H; + // calculate contrast based blend factors to reduce sharpening in regions with low contrast + JaggedArray blend(W, H); + float contrast = params->sharpening.contrast / 100.f; + buildBlendMask(ncie->sh_p, blend, W, H, contrast); if(showMask) { - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - float contrast = params->sharpening.contrast / 100.f; - buildBlendMask(ncie->sh_p, blend, W, H, contrast); #ifdef _OPENMP #pragma omp parallel for #endif @@ -903,9 +895,8 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) return; } - if (params->sharpening.method == "rld") { - deconvsharpening (ncie->sh_p, b2, ncie->W, ncie->H, params->sharpening); + deconvsharpening (ncie->sh_p, b2, blend, ncie->W, ncie->H, params->sharpening, scale); return; } @@ -921,11 +912,6 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) } } - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - float contrast = params->sharpening.contrast / 100.f; - buildBlendMask(ncie->sh_p, blend, W, H, contrast); - #ifdef _OPENMP #pragma omp parallel #endif diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 32fdb0f4e..ca098f216 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -520,7 +520,8 @@ enum ProcEventCode { EvWBEnabled = 490, EvRGBEnabled = 491, EvLEnabled = 492, -// EvPixelShiftOneGreen = 493, can be reused + EvPdShrEnabled = 493, + EvPdShrMaskToggled = 494, NUMOFEVENTS diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 0d7d55286..2fcc5d358 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1102,7 +1102,9 @@ void ColorToningParams::getCurves(ColorGradientCurve& colorCurveLUT, OpacityCurv SharpeningParams::SharpeningParams() : enabled(false), contrast(20.0), + autoContrast(false), blurradius(0.2), + gamma(1.0), radius(0.5), amount(200), threshold(20, 80, 2000, 1200, false), @@ -1125,9 +1127,11 @@ bool SharpeningParams::operator ==(const SharpeningParams& other) const enabled == other.enabled && contrast == other.contrast && blurradius == other.blurradius + && gamma == other.gamma && radius == other.radius && amount == other.amount && threshold == other.threshold + && autoContrast == other.autoContrast && edgesonly == other.edgesonly && edges_radius == other.edges_radius && edges_tolerance == other.edges_tolerance @@ -1145,6 +1149,32 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const return !(*this == other); } +CaptureSharpeningParams::CaptureSharpeningParams() : + enabled(false), + autoContrast(true), + contrast(10.0), + gamma(1.00), + deconvradius(0.75), + deconviter(20) +{ +} + +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), @@ -2825,6 +2855,8 @@ void ProcParams::setDefaults() prsharpening.deconviter = 100; prsharpening.deconvdamping = 0; + pdsharpening = {}; + vibrance = {}; wb = {}; @@ -3334,6 +3366,14 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->resize.height, "Resize", "Height", resize.height, keyFile); saveToKeyfile(!pedited || pedited->resize.allowUpscaling, "Resize", "AllowUpscaling", resize.allowUpscaling, keyFile); +// Post demosaic sharpening + saveToKeyfile(!pedited || pedited->pdsharpening.enabled, "PostDemosaicSharpening", "Enabled", pdsharpening.enabled, keyFile); + saveToKeyfile(!pedited || pedited->pdsharpening.contrast, "PostDemosaicSharpening", "Contrast", pdsharpening.contrast, keyFile); + saveToKeyfile(!pedited || pedited->pdsharpening.autoContrast, "PostDemosaicSharpening", "AutoContrast", pdsharpening.autoContrast, keyFile); + saveToKeyfile(!pedited || pedited->pdsharpening.gamma, "PostDemosaicSharpening", "DeconvGamma", pdsharpening.gamma, keyFile); + saveToKeyfile(!pedited || pedited->pdsharpening.deconvradius, "PostDemosaicSharpening", "DeconvRadius", pdsharpening.deconvradius, keyFile); + saveToKeyfile(!pedited || pedited->pdsharpening.deconviter, "PostDemosaicSharpening", "DeconvIterations", pdsharpening.deconviter, keyFile); + // Post resize sharpening saveToKeyfile(!pedited || pedited->prsharpening.enabled, "PostResizeSharpening", "Enabled", prsharpening.enabled, keyFile); saveToKeyfile(!pedited || pedited->prsharpening.contrast, "PostResizeSharpening", "Contrast", prsharpening.contrast, keyFile); @@ -4414,6 +4454,16 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } } + if (keyFile.has_group("PostDemosaicSharpening")) { + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled); + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast); + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "AutoContrast", pedited, pdsharpening.autoContrast, pedited->pdsharpening.autoContrast); + + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvGamma", pedited, pdsharpening.gamma, pedited->pdsharpening.gamma); + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvRadius", pedited, pdsharpening.deconvradius, pedited->pdsharpening.deconvradius); + assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvIterations", pedited, pdsharpening.deconviter, pedited->pdsharpening.deconviter); + } + if (keyFile.has_group("PostResizeSharpening")) { assignFromKeyfile(keyFile, "PostResizeSharpening", "Enabled", pedited, prsharpening.enabled, pedited->prsharpening.enabled); assignFromKeyfile(keyFile, "PostResizeSharpening", "Contrast", pedited, prsharpening.contrast, pedited->prsharpening.contrast); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 253ed26eb..e8f846a1d 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -494,7 +494,9 @@ struct ColorToningParams { struct SharpeningParams { bool enabled; double contrast; + bool autoContrast; double blurradius; + double gamma; double radius; int amount; Threshold threshold; @@ -540,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 */ @@ -1528,6 +1544,7 @@ public: ColorToningParams colorToning; ///< Color Toning parameters SharpeningParams sharpening; ///< Sharpening parameters SharpeningParams prsharpening; ///< Sharpening parameters for post resize 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 d0fb87313..5f1d13c79 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -37,6 +37,9 @@ #include "pdaflinesfilter.h" #include "camconst.h" #include "procparams.h" +#include "color.h" +//#define BENCHMARK +//#include "StopWatch.h" #ifdef _OPENMP #include #endif @@ -453,6 +456,9 @@ RawImageSource::RawImageSource () , green(0, 0) , red(0, 0) , blue(0, 0) + , greenCache(nullptr) + , redCache(nullptr) + , blueCache(nullptr) , rawDirty(true) , histMatchingParams(new procparams::ColorManagementParams) { @@ -470,6 +476,9 @@ RawImageSource::~RawImageSource () { delete idata; + delete redCache; + delete greenCache; + delete blueCache; for(size_t i = 0; i < numFrames; ++i) { delete riFrames[i]; @@ -1552,7 +1561,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold) +void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache) { MyTime t1, t2; t1.set(); @@ -1623,7 +1632,49 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c rgbSourceModified = false; - + if (cache) { + if (!redCache) { + redCache = new array2D(W, H); + greenCache = new array2D(W, H); + blueCache = new array2D(W, H); + } +#ifdef _OPENMP + #pragma omp parallel sections +#endif + { +#ifdef _OPENMP + #pragma omp section +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + (*redCache)[i][j] = red[i][j]; + } + } +#ifdef _OPENMP + #pragma omp section +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + (*greenCache)[i][j] = green[i][j]; + } + } +#ifdef _OPENMP + #pragma omp section +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + (*blueCache)[i][j] = blue[i][j]; + } + } + } + } else { + delete redCache; + redCache = nullptr; + delete greenCache; + greenCache = nullptr; + delete blueCache; + blueCache = nullptr; + } if( settings->verbose ) { if (getSensorType() == ST_BAYER) { printf("Demosaicing Bayer data: %s - %d usec\n", raw.bayersensor.method.c_str(), t2.etime(t1)); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index decdcef12..e20024f3c 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -88,6 +88,12 @@ protected: array2D red; // the interpolated blue plane: array2D blue; + // the interpolated green plane: + array2D* greenCache; + // the interpolated red plane: + array2D* redCache; + // the interpolated blue plane: + array2D* blueCache; bool rawDirty; float psRedBrightness[4]; float psGreenBrightness[4]; @@ -117,7 +123,7 @@ public: void preprocess (const procparams::RAWParams &raw, const procparams::LensProfParams &lensProf, const procparams::CoarseTransformParams& coarse, bool prepareDenoise = true) override; void filmNegativeProcess (const procparams::FilmNegativeParams ¶ms) override; bool getFilmNegativeExponents (Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams ¤tParams, std::array& newExps) override; - void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold) override; + void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache = false) override; void retinex (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &deh, const procparams::ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) override; void retinexPrepareCurves (const procparams::RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI) override; void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI) override; @@ -305,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::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; }; } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 94c3e71f8..db56500b8 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -519,7 +519,10 @@ int refreshmap[rtengine::NUMOFEVENTS] = { HDR, // EvTMFattalAmount ALLNORAW, // EvWBEnabled RGBCURVE, // EvRGBEnabled - LUMINANCECURVE // EvLEnabled + LUMINANCECURVE, // EvLEnabled + DEMOSAIC, // EvPdShrEnabled + ALLNORAW // EvPdShrMaskToggled + }; diff --git a/rtengine/refreshmap.h b/rtengine/refreshmap.h index 2890d8f19..3f4831329 100644 --- a/rtengine/refreshmap.h +++ b/rtengine/refreshmap.h @@ -32,6 +32,7 @@ // Elementary functions that can be done to // the preview image when an event occurs +#define M_CSHARP (1<<18) #define M_MONITOR (1<<14) #define M_RETINEX (1<<13) #define M_CROP (1<<12) @@ -56,6 +57,7 @@ #define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #define ALLNORAW (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) +#define CAPTURESHARPEN (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP) #define HDR (M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #define AUTOEXP (M_HDR|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc index 5c5da6bca..9fdb59652 100644 --- a/rtengine/rt_algo.cc +++ b/rtengine/rt_algo.cc @@ -52,7 +52,7 @@ vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) { } #endif -float tileAverage(float **data, size_t tileY, size_t tileX, size_t tilesize) { +float tileAverage(const float * const *data, size_t tileY, size_t tileX, size_t tilesize) { float avg = 0.f; #ifdef __SSE2__ @@ -75,7 +75,7 @@ float tileAverage(float **data, size_t tileY, size_t tileX, size_t tilesize) { return avg / rtengine::SQR(tilesize); } -float tileVariance(float **data, size_t tileY, size_t tileX, size_t tilesize, float avg) { +float tileVariance(const float * const *data, size_t tileY, size_t tileX, size_t tilesize, float avg) { float var = 0.f; #ifdef __SSE2__ @@ -99,7 +99,7 @@ float tileVariance(float **data, size_t tileY, size_t tileX, size_t tilesize, fl return var / (rtengine::SQR(tilesize) * avg); } -float calcContrastThreshold(float** luminance, int tileY, int tileX, int tilesize) { +float calcContrastThreshold(const float* const * luminance, int tileY, int tileX, int tilesize) { constexpr float scale = 0.0625f / 327.68f; std::vector> blend(tilesize - 4, std::vector(tilesize - 4)); @@ -299,7 +299,7 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& maxOut = rtengine::LIM(maxOut, minVal, maxVal); } -void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount, bool autoContrast) { +void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, float amount, bool autoContrast) { if (autoContrast) { constexpr float minLuminance = 2000.f; diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index bf442b9be..0ea000a26 100644 --- a/rtengine/rt_algo.h +++ b/rtengine/rt_algo.h @@ -24,5 +24,5 @@ namespace rtengine { void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multiThread = true); -void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false); +void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false); } diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index a0be5a7f6..33cca1aba 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -523,6 +523,7 @@ public: virtual void setFrameCountListener (FrameCountListener* l) = 0; virtual void setBayerAutoContrastListener (AutoContrastListener* l) = 0; virtual void setXtransAutoContrastListener (AutoContrastListener* l) = 0; + virtual void setpdSharpenAutoContrastListener (AutoContrastListener* l) = 0; virtual void setAutoBWListener (AutoBWListener* l) = 0; virtual void setAutoWBListener (AutoWBListener* l) = 0; virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0; @@ -535,7 +536,7 @@ public: virtual void getMonitorProfile (Glib::ustring& monitorProfile, RenderingIntent& intent) const = 0; virtual void setSoftProofing (bool softProof, bool gamutCheck) = 0; virtual void getSoftProofing (bool &softProof, bool &gamutCheck) = 0; - virtual void setSharpMask (bool sharpMask) = 0; + virtual ProcEvent setSharpMask (bool sharpMask) = 0; virtual ~StagedImageProcessor () {} diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index d80abe045..2022d2d15 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -220,7 +220,10 @@ private: bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicAutoContrast : params.raw.xtranssensor.dualDemosaicAutoContrast; double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicContrast : params.raw.xtranssensor.dualDemosaicContrast; - imgsrc->demosaic (params.raw, autoContrast, contrastThreshold); + imgsrc->demosaic (params.raw, autoContrast, contrastThreshold, params.pdsharpening.enabled && pl); + if (params.pdsharpening.enabled) { + imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast); + } if (pl) { diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 8ad8cb8ea..ef1294dea 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::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; }; } #endif diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index bdd166938..bc52b64b0 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -106,6 +106,7 @@ set(NONCLISOURCEFILES partialpastedlg.cc pathutils.cc pcvignette.cc + pdsharpening.cc perspective.cc placesbrowser.cc popupbutton.cc diff --git a/rtgui/addsetids.h b/rtgui/addsetids.h index 163bc27ef..6a4ea83d3 100644 --- a/rtgui/addsetids.h +++ b/rtgui/addsetids.h @@ -142,6 +142,7 @@ enum { ADDSET_XTRANS_FALSE_COLOR_SUPPRESSION, ADDSET_SOFTLIGHT_STRENGTH, ADDSET_DEHAZE_STRENGTH, + ADDSET_SHARP_GAMMA, ADDSET_PARAM_NUM // THIS IS USED AS A DELIMITER!! }; diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index 592b59a4f..87320892e 100644 --- a/rtgui/batchtoolpanelcoord.cc +++ b/rtgui/batchtoolpanelcoord.cc @@ -152,6 +152,7 @@ void BatchToolPanelCoordinator::initSession () cacorrection->setAdjusterBehavior (false); sharpening->setAdjusterBehavior (false, false, false, false, false, false, false); prsharpening->setAdjusterBehavior (false, false, false, false, false, false, false); + pdSharpening->setAdjusterBehavior (false, false, false, false); sharpenEdge->setAdjusterBehavior (false, false); sharpenMicro->setAdjusterBehavior (false, false, false); epd->setAdjusterBehavior (false, false, false, false, false); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index e0e3ea47b..84860329f 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -152,6 +152,7 @@ void ParamsEdited::set(bool v) sharpening.contrast = v; sharpening.radius = v; sharpening.blurradius = v; + sharpening.gamma = v; sharpening.amount = v; sharpening.threshold = v; sharpening.edgesonly = v; @@ -164,6 +165,12 @@ void ParamsEdited::set(bool v) sharpening.deconvradius = v; sharpening.deconviter = v; sharpening.deconvdamping = v; + pdsharpening.enabled = v; + pdsharpening.contrast = v; + pdsharpening.autoContrast = v; + pdsharpening.gamma = v; + pdsharpening.deconvradius = v; + pdsharpening.deconviter = v; prsharpening.enabled = v; prsharpening.contrast = v; prsharpening.radius = v; @@ -729,6 +736,7 @@ void ParamsEdited::initFrom(const std::vector& sharpening.contrast = sharpening.contrast && p.sharpening.contrast == other.sharpening.contrast; sharpening.radius = sharpening.radius && p.sharpening.radius == other.sharpening.radius; sharpening.blurradius = sharpening.blurradius && p.sharpening.blurradius == other.sharpening.blurradius; + sharpening.gamma = sharpening.gamma && p.sharpening.gamma == other.sharpening.gamma; sharpening.amount = sharpening.amount && p.sharpening.amount == other.sharpening.amount; sharpening.threshold = sharpening.threshold && p.sharpening.threshold == other.sharpening.threshold; sharpening.edgesonly = sharpening.edgesonly && p.sharpening.edgesonly == other.sharpening.edgesonly; @@ -741,6 +749,12 @@ void ParamsEdited::initFrom(const std::vector& sharpening.deconvradius = sharpening.deconvradius && p.sharpening.deconvradius == other.sharpening.deconvradius; sharpening.deconviter = sharpening.deconviter && p.sharpening.deconviter == other.sharpening.deconviter; sharpening.deconvdamping = sharpening.deconvdamping && p.sharpening.deconvdamping == other.sharpening.deconvdamping; + pdsharpening.enabled = pdsharpening.enabled && p.pdsharpening.enabled == other.pdsharpening.enabled; + pdsharpening.contrast = pdsharpening.contrast && p.pdsharpening.contrast == other.pdsharpening.contrast; + pdsharpening.autoContrast = pdsharpening.autoContrast && p.pdsharpening.autoContrast == other.pdsharpening.autoContrast; + pdsharpening.gamma = pdsharpening.gamma && p.pdsharpening.gamma == other.pdsharpening.gamma; + pdsharpening.deconvradius = pdsharpening.deconvradius && p.pdsharpening.deconvradius == other.pdsharpening.deconvradius; + pdsharpening.deconviter = pdsharpening.deconviter && p.pdsharpening.deconviter == other.pdsharpening.deconviter; prsharpening.enabled = prsharpening.enabled && p.prsharpening.enabled == other.prsharpening.enabled; prsharpening.contrast = prsharpening.contrast && p.prsharpening.contrast == other.prsharpening.contrast; prsharpening.radius = prsharpening.radius && p.prsharpening.radius == other.prsharpening.radius; @@ -1654,6 +1668,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.sharpening.blurradius = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.sharpening.blurradius + mods.sharpening.blurradius : mods.sharpening.blurradius; } + if (sharpening.gamma) { + toEdit.sharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.sharpening.gamma + mods.sharpening.gamma : mods.sharpening.gamma; + } + if (sharpening.amount) { toEdit.sharpening.amount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.sharpening.amount + mods.sharpening.amount : mods.sharpening.amount; } @@ -1702,6 +1720,30 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.sharpening.deconvdamping = dontforceSet && options.baBehav[ADDSET_SHARP_DAMPING] ? toEdit.sharpening.deconvdamping + mods.sharpening.deconvdamping : mods.sharpening.deconvdamping; } + if (pdsharpening.enabled) { + toEdit.pdsharpening.enabled = mods.pdsharpening.enabled; + } + + if (pdsharpening.contrast) { + toEdit.pdsharpening.contrast = dontforceSet && options.baBehav[ADDSET_SHARP_CONTRAST] ? toEdit.pdsharpening.contrast + mods.pdsharpening.contrast : mods.pdsharpening.contrast; + } + + if (pdsharpening.autoContrast) { + toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast; + } + + if (pdsharpening.gamma) { + toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma; + } + + if (pdsharpening.deconvradius) { + toEdit.pdsharpening.deconvradius = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.pdsharpening.deconvradius + mods.pdsharpening.deconvradius : mods.pdsharpening.deconvradius; + } + + if (pdsharpening.deconviter) { + toEdit.pdsharpening.deconviter = dontforceSet && options.baBehav[ADDSET_SHARP_ITER] ? toEdit.pdsharpening.deconviter + mods.pdsharpening.deconviter : mods.pdsharpening.deconviter; + } + if (prsharpening.enabled) { toEdit.prsharpening.enabled = mods.prsharpening.enabled; } @@ -3244,3 +3286,8 @@ bool FilmNegativeParamsEdited::isUnchanged() const { return enabled && redRatio && greenExp && blueRatio; } + +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 0b765db22..860ffc551 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -179,7 +179,9 @@ struct SharpenMicroParamsEdited { struct SharpeningParamsEdited { bool enabled; bool contrast; + bool autoContrast; bool blurradius; + bool gamma; bool radius; bool amount; bool threshold; @@ -196,6 +198,16 @@ struct SharpeningParamsEdited { bool deconvdamping; }; +struct CaptureSharpeningParamsEdited { + bool enabled; + bool contrast; + bool autoContrast; + bool gamma; + bool deconvradius; + bool deconviter; + bool isUnchanged() const; +}; + struct VibranceParamsEdited { bool enabled; bool pastels; @@ -684,6 +696,7 @@ struct ParamsEdited { ColorToningEdited colorToning; RetinexParamsEdited retinex; SharpeningParamsEdited sharpening; + CaptureSharpeningParamsEdited pdsharpening; SharpeningParamsEdited prsharpening; SharpenEdgeParamsEdited sharpenEdge; SharpenMicroParamsEdited sharpenMicro; diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc new file mode 100644 index 000000000..c85b42e95 --- /dev/null +++ b/rtgui/pdsharpening.cc @@ -0,0 +1,249 @@ +/* + * 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 "eventmapper.h" +#include "pdsharpening.h" +#include "options.h" +#include "../rtengine/procparams.h" + +using namespace rtengine; +using namespace rtengine::procparams; + +PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDSHARPENING_LABEL"), false, true) +{ + + auto m = ProcEventMapper::getInstance(); + EvPdShrContrast = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_CONTRAST"); + EvPdSharpenGamma = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_GAMMA"); + EvPdShrDRadius = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_RADIUS"); + EvPdShrDIterations = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); + EvPdShrAutoContrast = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST"); + + Gtk::HBox* hb = Gtk::manage(new Gtk::HBox()); + hb->show(); + contrast = Gtk::manage(new Adjuster(M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 10)); + contrast->setAdjusterListener(this); + contrast->addAutoButton(M("TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP")); + contrast->setAutoValue(true); + + pack_start(*contrast); + contrast->show(); + + pack_start(*hb); + + Gtk::VBox* rld = Gtk::manage(new Gtk::VBox()); + gamma = Gtk::manage(new Adjuster(M("TP_SHARPENING_GAMMA"), 0.5, 6.0, 0.05, 1.00)); + dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 1.15, 0.01, 0.75)); + diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 1, 100, 1, 20)); + rld->pack_start(*gamma); + rld->pack_start(*dradius); + rld->pack_start(*diter); + gamma->show(); + dradius->show(); + diter->show(); + rld->show(); + pack_start(*rld); + + dradius->setAdjusterListener(this); + gamma->setAdjusterListener(this); + diter->setAdjusterListener(this); + + contrast->delay = std::max(contrast->delay, options.adjusterMaxDelay); + dradius->delay = std::max(dradius->delay, options.adjusterMaxDelay); + gamma->delay = std::max(gamma->delay, options.adjusterMaxDelay); + diter->delay = std::max(diter->delay, options.adjusterMaxDelay); +} + +PdSharpening::~PdSharpening() +{ + idle_register.destroy(); +} + + +void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited) +{ + + disableListener(); + + if (pedited) { + contrast->setEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited); + contrast->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoContrast); + gamma->setEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited); + dradius->setEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited); + diter->setEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited); + + set_inconsistent(multiImage && !pedited->pdsharpening.enabled); + } + + setEnabled(pp->pdsharpening.enabled); + + contrast->setValue(pp->pdsharpening.contrast); + contrast->setAutoValue(pp->pdsharpening.autoContrast); + gamma->setValue(pp->pdsharpening.gamma); + dradius->setValue(pp->pdsharpening.deconvradius); + diter->setValue(pp->pdsharpening.deconviter); + lastAutoContrast = pp->pdsharpening.autoContrast; + + enableListener(); +} + +void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited) +{ + + pp->pdsharpening.contrast = contrast->getValue(); + pp->pdsharpening.autoContrast = contrast->getAutoValue(); + pp->pdsharpening.enabled = getEnabled(); + pp->pdsharpening.gamma = gamma->getValue(); + pp->pdsharpening.deconvradius = dradius->getValue(); + pp->pdsharpening.deconviter =(int)diter->getValue(); + + if (pedited) { + pedited->pdsharpening.contrast = contrast->getEditedState(); + pedited->pdsharpening.autoContrast = !contrast->getAutoInconsistent(); + pedited->pdsharpening.gamma = gamma->getEditedState(); + pedited->pdsharpening.deconvradius = dradius->getEditedState(); + pedited->pdsharpening.deconviter = diter->getEditedState(); + pedited->pdsharpening.enabled = !get_inconsistent(); + } +} + +void PdSharpening::setDefaults(const ProcParams* defParams, const ParamsEdited* pedited) +{ + + contrast->setDefault(defParams->pdsharpening.contrast); + gamma->setDefault(defParams->pdsharpening.gamma); + dradius->setDefault(defParams->pdsharpening.deconvradius); + diter->setDefault(defParams->pdsharpening.deconviter); + + if (pedited) { + contrast->setDefaultEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited); + gamma->setDefaultEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited); + dradius->setDefaultEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited); + diter->setDefaultEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited); + } else { + contrast->setDefaultEditedState(Irrelevant); + gamma->setDefaultEditedState(Irrelevant); + dradius->setDefaultEditedState(Irrelevant); + diter->setDefaultEditedState(Irrelevant); + } +} + +void PdSharpening::adjusterChanged(Adjuster* a, double newval) +{ + if (listener && (multiImage || getEnabled())) { + + Glib::ustring costr; + + if (a == gamma || a == dradius) { + costr = Glib::ustring::format(std::setw(3), std::fixed, std::setprecision(2), a->getValue()); + } else { + costr = Glib::ustring::format((int)a->getValue()); + } + + if (a == contrast) { + listener->panelChanged(EvPdShrContrast, costr); + } else if (a == gamma) { + listener->panelChanged(EvPdSharpenGamma, costr); + } else if (a == dradius) { + listener->panelChanged(EvPdShrDRadius, costr); + } else if (a == diter) { + listener->panelChanged(EvPdShrDIterations, costr); + } + } +} + +void PdSharpening::enabledChanged() +{ + if (listener) { + if (get_inconsistent()) { + listener->panelChanged(EvPdShrEnabled, M("GENERAL_UNCHANGED")); + } else if (getEnabled()) { + listener->panelChanged(EvPdShrEnabled, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(EvPdShrEnabled, M("GENERAL_DISABLED")); + } + } +} + +void PdSharpening::setBatchMode(bool batchMode) +{ + + ToolPanel::setBatchMode(batchMode); + + contrast->showEditedCB(); + gamma->showEditedCB(); + dradius->showEditedCB(); + diter->showEditedCB(); +} + +void PdSharpening::setAdjusterBehavior(bool contrastadd, bool gammaadd, bool radiusadd, bool iteradd) +{ + + contrast->setAddMode(contrastadd); + gamma->setAddMode(gammaadd); + dradius->setAddMode(radiusadd); + diter->setAddMode(iteradd); +} + +void PdSharpening::trimValues(rtengine::procparams::ProcParams* pp) +{ + + contrast->trimValue(pp->pdsharpening.contrast); + gamma->trimValue(pp->pdsharpening.gamma); + dradius->trimValue(pp->pdsharpening.deconvradius); + diter->trimValue(pp->pdsharpening.deconviter); +} + +void PdSharpening::autoContrastChanged(double autoContrast) +{ + idle_register.add( + [this, autoContrast]() -> bool + { + disableListener(); + contrast->setValue(autoContrast); + enableListener(); + return false; + } + ); +} + +void PdSharpening::adjusterAutoToggled(Adjuster* a, bool newval) +{ + if (multiImage) { + if (contrast->getAutoInconsistent()) { + contrast->setAutoInconsistent(false); + contrast->setAutoValue(false); + } else if (lastAutoContrast) { + contrast->setAutoInconsistent(true); + } + + lastAutoContrast = contrast->getAutoValue(); + } + + if (listener) { + if (contrast->getAutoInconsistent()) { + listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_UNCHANGED")); + } else if (contrast->getAutoValue()) { + listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_DISABLED")); + } + } +} diff --git a/rtgui/pdsharpening.h b/rtgui/pdsharpening.h new file mode 100644 index 000000000..b2f7b6e57 --- /dev/null +++ b/rtgui/pdsharpening.h @@ -0,0 +1,59 @@ +/* + * 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 . +*/ +#pragma once + +#include "adjuster.h" +#include "toolpanel.h" + +class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener +{ + +protected: + Adjuster* contrast; + Adjuster* gamma; + Adjuster* dradius; + Adjuster* diter; + + bool lastAutoContrast; + rtengine::ProcEvent EvPdShrContrast; + rtengine::ProcEvent EvPdShrDRadius; + rtengine::ProcEvent EvPdSharpenGamma; + rtengine::ProcEvent EvPdShrDIterations; + rtengine::ProcEvent EvPdShrAutoContrast; + IdleRegister idle_register; + +public: + + PdSharpening (); + ~PdSharpening () override; + + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void setBatchMode (bool batchMode) override; + + void adjusterAutoToggled (Adjuster* a, bool newval) override; + void adjusterChanged (Adjuster* a, double newval) override; + void enabledChanged () override; + + void autoContrastChanged (double autoContrast) override; + + void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool iteradds); + void trimValues (rtengine::procparams::ProcParams* pp) override; +}; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index e920e0e97..a664942a1 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -93,7 +93,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit xtransrawexposure = Gtk::manage (new XTransRAWExposure ()); fattal = Gtk::manage (new FattalToneMapping ()); filmNegative = Gtk::manage (new FilmNegative ()); - + pdSharpening = Gtk::manage (new PdSharpening()); // So Demosaic, Line noise filter, Green Equilibration, Ca-Correction (garder le nom de section identique!) and Black-Level will be moved in a "Bayer sensor" tool, // and a separate Demosaic and Black Level tool will be created in an "X-Trans sensor" tool @@ -156,6 +156,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit addfavoritePanel (rawPanel, darkframe); addfavoritePanel (rawPanel, flatfield); addfavoritePanel (rawPanel, filmNegative); + addfavoritePanel (rawPanel, pdSharpening); int favoriteCount = 0; for(auto it = favorites.begin(); it != favorites.end(); ++it) { @@ -309,6 +310,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt preprocess->FoldableToolPanel::show(); flatfield->FoldableToolPanel::show(); filmNegative->FoldableToolPanel::show(); + pdSharpening->FoldableToolPanel::show(); retinex->FoldableToolPanel::setGrayedOut(false); return false; @@ -325,6 +327,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt preprocess->FoldableToolPanel::show(); flatfield->FoldableToolPanel::show(); filmNegative->FoldableToolPanel::show(); + pdSharpening->FoldableToolPanel::show(); retinex->FoldableToolPanel::setGrayedOut(false); return false; @@ -341,6 +344,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt preprocess->FoldableToolPanel::hide(); flatfield->FoldableToolPanel::show(); filmNegative->FoldableToolPanel::hide(); + pdSharpening->FoldableToolPanel::show(); retinex->FoldableToolPanel::setGrayedOut(false); return false; @@ -356,6 +360,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt preprocess->FoldableToolPanel::hide(); flatfield->FoldableToolPanel::hide(); filmNegative->FoldableToolPanel::hide(); + pdSharpening->FoldableToolPanel::hide(); retinex->FoldableToolPanel::setGrayedOut(false); return false; @@ -368,6 +373,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt { rawPanelSW->set_sensitive(false); filmNegative->FoldableToolPanel::hide(); + pdSharpening->FoldableToolPanel::hide(); retinex->FoldableToolPanel::setGrayedOut(true); return false; @@ -484,7 +490,7 @@ void ToolPanelCoordinator::profileChange( lParams[1] = *mergedParams; pe.initFrom (lParams); - filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.filmNegative.isUnchanged(); + filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.filmNegative.isUnchanged() && pe.pdsharpening.isUnchanged(); } *params = *mergedParams; @@ -566,6 +572,7 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool ipc->setFlatFieldAutoClipListener (flatfield); ipc->setBayerAutoContrastListener (bayerprocess); ipc->setXtransAutoContrastListener (xtransprocess); + ipc->setpdSharpenAutoContrastListener (pdSharpening); ipc->setAutoWBListener (whitebalance); ipc->setAutoColorTonListener (colortoning); ipc->setAutoChromaListener (dirpyrdenoise); @@ -689,8 +696,7 @@ void ToolPanelCoordinator::sharpMaskSelected(bool sharpMask) return; } ipc->beginUpdateParams (); - ipc->setSharpMask(sharpMask); - ipc->endUpdateParams (rtengine::EvShrEnabled); + ipc->endUpdateParams (ipc->setSharpMask(sharpMask)); } int ToolPanelCoordinator::getSpotWBRectSize() const diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index fea55990e..390967395 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -77,6 +77,7 @@ #include "colortoning.h" #include "filmsimulation.h" #include "prsharpening.h" +#include "pdsharpening.h" #include "fattaltonemap.h" #include "localcontrast.h" #include "softlight.h" @@ -157,7 +158,7 @@ protected: FattalToneMapping *fattal; MetaDataPanel* metadata; FilmNegative* filmNegative; - + PdSharpening* pdSharpening; std::vector paramcListeners; rtengine::StagedImageProcessor* ipc;