From 4079bb9920be8cf6c568c1b0f91bfbe3a2544b30 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 11 Sep 2019 18:56:07 +0200 Subject: [PATCH 1/2] Capture Sharpening: automatic radius calculation --- rtdata/languages/default | 2 + rtengine/capturesharpening.cc | 307 ++++++++++++++++++++++++++++++++-- rtengine/imagesource.h | 2 +- rtengine/improccoordinator.cc | 7 +- rtengine/improccoordinator.h | 6 + rtengine/procparams.cc | 4 + rtengine/procparams.h | 1 + rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 2 +- rtengine/rt_algo.cc | 23 ++- rtengine/rt_algo.h | 2 +- rtengine/rtengine.h | 8 + rtengine/simpleprocess.cc | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/paramsedited.cc | 8 +- rtgui/paramsedited.h | 1 + rtgui/pdsharpening.cc | 74 ++++++-- rtgui/pdsharpening.h | 5 +- rtgui/toolpanelcoord.cc | 1 + rtgui/toolpanelcoord.h | 1 - 20 files changed, 416 insertions(+), 44 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 5bf1863df..3749a706a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -766,6 +766,7 @@ 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_AUTO_RADIUS;CAS - Auto radius HISTORY_MSG_PDSHARPEN_GAMMA;CAS - Gamma HISTORY_MSG_PDSHARPEN_ITERATIONS;CAS - Iterations HISTORY_MSG_PDSHARPEN_RADIUS;CAS - Radius @@ -1800,6 +1801,7 @@ TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse, TP_PCVIGNETTE_STRENGTH;Strength TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners). TP_PDSHARPENING_LABEL;Capture Sharpening +TP_PDSHARPENING_AUTORADIUS_TOOLTIP;If the checkbox is checked, RawTherapee calculates a value based on the raw data of the image. TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_VERTICAL;Vertical diff --git a/rtengine/capturesharpening.cc b/rtengine/capturesharpening.cc index eb95d2633..e741016db 100644 --- a/rtengine/capturesharpening.cc +++ b/rtengine/capturesharpening.cc @@ -37,6 +37,243 @@ #include "../rtgui/multilangmgr.h" namespace { + +void buildClipMaskBayer(const float * const *rawData, int W, int H, float** clipMask, const float whites[2][2]) +{ + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 0; row < H; ++row) { + for (int col = 0; col < W; ++col) { + clipMask[row][col] = 1.f; + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 2; row < H - 2; ++row) { + float clip0 = whites[row & 1][0]; + float clip1 = whites[row & 1][1]; + for (int col = 2; col < W - 2; ++col) { + if (rawData[row][col] >= clip0) { + clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f; + clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f; + clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f; + clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f; + clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f; + } + std::swap(clip0, clip1); + } + } +} + +void buildClipMaskXtrans(const float * const *rawData, int W, int H, float** clipMask, const float whites[6][6]) +{ + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 0; row < H; ++row) { + for (int col = 0; col < W; ++col) { + clipMask[row][col] = 1.f; + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 2; row < H - 2; ++row) { + for (int col = 2; col < W - 2; ++col) { + const float clip = whites[row % 6][col % 6]; + if (rawData[row][col] >= clip) { + clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f; + clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f; + clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f; + clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f; + clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f; + } + } + } +} + +void buildClipMaskMono(const float * const *rawData, int W, int H, float** clipMask, float white) +{ + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 0; row < H; ++row) { + for (int col = 0; col < W; ++col) { + clipMask[row][col] = 1.f; + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int row = 2; row < H - 2; ++row) { + for (int col = 2; col < W - 2; ++col) { + if (rawData[row][col] >= white) { + clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f; + clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f; + clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f; + clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f; + clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f; + } + } + } +} + +float calcRadiusBayer(const float * const *rawData, int W, int H, float lowerLimit, float upperLimit, const unsigned int fc[2]) +{ + + float maxRatio = 1.f; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxRatio) schedule(dynamic, 16) +#endif + for (int row = 4; row < H - 4; ++row) { + for (int col = 5 + (fc[row & 1] & 1); col < W - 4; col += 2) { + const float val00 = rawData[row][col]; + if (val00 > 0.f) { + const float val1m1 = rawData[row + 1][col - 1]; + const float val1p1 = rawData[row + 1][col + 1]; + const float maxVal0 = std::max(val00, val1m1); + if (val1m1 > 0.f && maxVal0 > lowerLimit) { + const float minVal = std::min(val00, val1m1); + if (UNLIKELY(maxVal0 > maxRatio * minVal)) { + bool clipped = false; + if (maxVal0 == val00) { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col - 1], rawData[row - 1][col + 1], val1p1) >= upperLimit) { + clipped = true; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row][col - 2], val00, rawData[row + 2][col - 2], rawData[row + 2][col]) >= upperLimit) { + clipped = true; + } + } + if (!clipped) { + maxRatio = maxVal0 / minVal; + } + } + } + const float maxVal1 = std::max(val00, val1p1); + if (val1p1 > 0.f && maxVal1 > lowerLimit) { + const float minVal = std::min(val00, val1p1); + if (UNLIKELY(maxVal1 > maxRatio * minVal)) { + if (maxVal1 == val00) { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col - 1], rawData[row - 1][col + 1], val1p1) >= upperLimit) { + continue; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(val00, rawData[row][col + 2], rawData[row + 2][col], rawData[row + 2][col + 2]) >= upperLimit) { + continue; + } + } + maxRatio = maxVal1 / minVal; + } + } + } + } + } + return std::sqrt((1.f / (std::log(1.f / maxRatio) / 2.f)) / -2.f); +} + +float calcRadiusXtrans(const float * const *rawData, int W, int H, float lowerLimit, float upperLimit, unsigned int starty, unsigned int startx) +{ + + float maxRatio = 1.f; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxRatio) schedule(dynamic, 16) +#endif + for (int row = starty + 3; row < H - 4; row += 3) { + for (int col = startx + 3; col < W - 4; col += 3) { + const float valtl = rawData[row][col]; + const float valtr = rawData[row][col + 1]; + const float valbl = rawData[row + 1][col]; + const float valbr = rawData[row + 1][col + 1]; + if (valtl > 1.f) { + const float maxValtltr = std::max(valtl, valtr); + if (valtr > 1.f && maxValtltr > lowerLimit) { + const float minVal = std::min(valtl, valtr); + if (UNLIKELY(maxValtltr > maxRatio * minVal)) { + bool clipped = false; + if (maxValtltr == valtl) { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col - 1], valtr, valbl, valbr) >= upperLimit) { + clipped = true; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col + 2], valtl, valbl, valbr) >= upperLimit) { + clipped = true; + } + } + if (!clipped) { + maxRatio = maxValtltr / minVal; + } + } + } + const float maxValtlbl = std::max(valtl, valbl); + if (valbl > 1.f && maxValtlbl > lowerLimit) { + const float minVal = std::min(valtl, valbl); + if (UNLIKELY(maxValtlbl > maxRatio * minVal)) { + bool clipped = false; + if (maxValtlbl == valtl) { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col - 1], valtr, valbl, valbr) >= upperLimit) { + clipped = true; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(valtl, valtr, rawData[row + 2][col - 1], valbr) >= upperLimit) { + clipped = true; + } + } + if (!clipped) { + maxRatio = maxValtlbl / minVal; + } + } + } + } + if (valbr > 1.f) { + const float maxValblbr = std::max(valbl, valbr); + if (valbl > 1.f && maxValblbr > lowerLimit) { + const float minVal = std::min(valbl, valbr); + if (UNLIKELY(maxValblbr > maxRatio * minVal)) { + bool clipped = false; + if (maxValblbr == valbr) { // check for influence by clipped green in neighborhood + if (rtengine::max(valtl, valtr, valbl, rawData[row + 2][col + 2]) >= upperLimit) { + clipped = true; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(valtl, valtr, rawData[row + 2][col - 1], valbr) >= upperLimit) { + clipped = true; + } + } + if (!clipped) { + maxRatio = maxValblbr / minVal; + } + } + } + const float maxValtrbr = std::max(valtr, valbr); + if (valtr > 1.f && maxValtrbr > lowerLimit) { + const float minVal = std::min(valtr, valbr); + if (UNLIKELY(maxValtrbr > maxRatio * minVal)) { + if (maxValtrbr == valbr) { // check for influence by clipped green in neighborhood + if (rtengine::max(valtl, valtr, valbl, rawData[row + 2][col + 2]) >= upperLimit) { + continue; + } + } else { // check for influence by clipped green in neighborhood + if (rtengine::max(rawData[row - 1][col + 2], valtl, valbl, valbr) >= upperLimit) { + continue; + } + } + maxRatio = maxValtrbr / minVal; + } + } + } + } + } + return std::sqrt((1.f / (std::log(1.f / maxRatio))) / -2.f); +} 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) { @@ -54,7 +291,7 @@ void CaptureDeconvSharpening (float** luminance, float** tmp, const float * cons 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); @@ -85,9 +322,7 @@ void CaptureDeconvSharpening (float** luminance, float** tmp, const float * cons namespace rtengine { -void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { -BENCHFUN - +void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) { if (plistener) { plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); @@ -102,12 +337,62 @@ BENCHFUN float contrast = conrastThreshold / 100.f; + const float clipVal = (ri->get_white(1) - ri->get_cblack(1)) * scale_mul[1]; + array2D& redVals = redCache ? *redCache : red; array2D& greenVals = greenCache ? *greenCache : green; array2D& blueVals = blueCache ? *blueCache : blue; + array2D clipMask(W, H); + constexpr float clipLimit = 0.95f; + if (ri->getSensorType() == ST_BAYER) { + const float whites[2][2] = { + {(ri->get_white(FC(0,0)) - c_black[FC(0,0)]) * scale_mul[FC(0,0)] * clipLimit, (ri->get_white(FC(0,1)) - c_black[FC(0,1)]) * scale_mul[FC(0,1)] * clipLimit}, + {(ri->get_white(FC(1,0)) - c_black[FC(1,0)]) * scale_mul[FC(1,0)] * clipLimit, (ri->get_white(FC(1,1)) - c_black[FC(1,1)]) * scale_mul[FC(1,1)] * clipLimit} + }; + buildClipMaskBayer(rawData, W, H, clipMask, whites); + const unsigned int fc[2] = {FC(0,0), FC(1,0)}; + if (sharpeningParams.autoRadius) { + radius = calcRadiusBayer(rawData, W, H, 1000.f, clipVal, fc); + } + } else if (ri->getSensorType() == ST_FUJI_XTRANS) { + float whites[6][6]; + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { + const auto color = ri->XTRANSFC(i, j); + whites[i][j] = (ri->get_white(color) - c_black[color]) * scale_mul[color] * clipLimit; + } + } + buildClipMaskXtrans(rawData, W, H, clipMask, whites); + bool found = false; + int i, j; + for (i = 6; i < 12 && !found; ++i) { + for (j = 6; j < 12 && !found; ++j) { + if (ri->XTRANSFC(i, j) == 1) { + if (ri->XTRANSFC(i, j - 1) != ri->XTRANSFC(i, j + 1)) { + if (ri->XTRANSFC(i - 1, j) != 1) { + if (ri->XTRANSFC(i, j - 1) != 1) { + found = true; + break; + } + } + } + } + } + } + if (sharpeningParams.autoRadius) { + radius = calcRadiusXtrans(rawData, W, H, 1000.f, clipVal, i, j); + } + + } else if (ri->get_colors() == 1) { + buildClipMaskMono(rawData, W, H, clipMask, (ri->get_white(0) - c_black[0]) * scale_mul[0] * clipLimit); + if (sharpeningParams.autoRadius) { + const unsigned int fc[2] = {0, 0}; + radius = calcRadiusBayer(rawData, W, H, 1000.f, clipVal, fc); + } + } + 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 @@ -119,8 +404,9 @@ BENCHFUN 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); + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast, clipMask); if (plistener) { plistener->setProgress(0.2); } @@ -157,7 +443,6 @@ BENCHFUN array2D& YOld = YOldbuffer ? * YOldbuffer : green; array2D& YNew = YNewbuffer ? * YNewbuffer : blue; - StopWatch Stop1("rgb2YL"); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) #endif @@ -170,19 +455,17 @@ BENCHFUN } // 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); + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast, clipMask); 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); + CaptureDeconvSharpening(YNew, tmp, blend, W, H, radius, 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) @@ -206,7 +489,7 @@ BENCHFUN blue[i][j] = blueVals[i][j] * factor; } } - Stop2.stop(); + delete Lbuffer; delete YOldbuffer; delete YNewbuffer; diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index c62dfe7d8..edc1102c4 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -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::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; + virtual void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) = 0; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index c6268576a..8b9e49124 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -122,6 +122,7 @@ ImProcCoordinator::ImProcCoordinator() : bayerAutoContrastListener(nullptr), xtransAutoContrastListener(nullptr), pdSharpenAutoContrastListener(nullptr), + pdSharpenAutoRadiusListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), actListener(nullptr), @@ -346,10 +347,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) if ((todo & (M_RAW | M_CSHARP)) && params->pdsharpening.enabled) { double pdSharpencontrastThreshold = params->pdsharpening.contrast; - imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold); + double pdSharpenRadius = params->pdsharpening.deconvradius; + imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold, pdSharpenRadius); if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) { pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold); } + if (pdSharpenAutoRadiusListener && params->pdsharpening.autoRadius) { + pdSharpenAutoRadiusListener->autoRadiusChanged(pdSharpenRadius); + } } diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index df4ec8134..379a3fb20 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -162,6 +162,7 @@ protected: AutoContrastListener *bayerAutoContrastListener; AutoContrastListener *xtransAutoContrastListener; AutoContrastListener *pdSharpenAutoContrastListener; + AutoRadiusListener *pdSharpenAutoRadiusListener; FrameCountListener *frameCountListener; ImageTypeListener *imageTypeListener; @@ -364,6 +365,11 @@ public: xtransAutoContrastListener = acl; } + void setpdSharpenAutoRadiusListener (AutoRadiusListener* acl) override + { + pdSharpenAutoRadiusListener = acl; + } + void setpdSharpenAutoContrastListener (AutoContrastListener* acl) override { pdSharpenAutoContrastListener = acl; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 2fcc5d358..f88220c4e 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1152,6 +1152,7 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const CaptureSharpeningParams::CaptureSharpeningParams() : enabled(false), autoContrast(true), + autoRadius(true), contrast(10.0), gamma(1.00), deconvradius(0.75), @@ -1166,6 +1167,7 @@ bool CaptureSharpeningParams::operator ==(const CaptureSharpeningParams& other) && contrast == other.contrast && gamma == other.gamma && autoContrast == other.autoContrast + && autoRadius == other.autoRadius && deconvradius == other.deconvradius && deconviter == other.deconviter; } @@ -3370,6 +3372,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo 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.autoRadius, "PostDemosaicSharpening", "AutoRadius", pdsharpening.autoRadius, 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); @@ -4458,6 +4461,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) 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", "AutoRadius", pedited, pdsharpening.autoRadius, pedited->pdsharpening.autoRadius); assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvGamma", pedited, pdsharpening.gamma, pedited->pdsharpening.gamma); assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvRadius", pedited, pdsharpening.deconvradius, pedited->pdsharpening.deconvradius); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index e8f846a1d..ce03efc7d 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -545,6 +545,7 @@ struct SharpenMicroParams { struct CaptureSharpeningParams { bool enabled; bool autoContrast; + bool autoRadius; double contrast; double gamma; double deconvradius; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index e20024f3c..e52adea18 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::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) override; }; } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index db56500b8..6917d856e 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -521,7 +521,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RGBCURVE, // EvRGBEnabled LUMINANCECURVE, // EvLEnabled DEMOSAIC, // EvPdShrEnabled - ALLNORAW // EvPdShrMaskToggled + CAPTURESHARPEN // EvPdShrMaskToggled }; diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc index 9fdb59652..4afd8ac73 100644 --- a/rtengine/rt_algo.cc +++ b/rtengine/rt_algo.cc @@ -156,7 +156,7 @@ float calcContrastThreshold(const float* const * luminance, int tileY, int tileX } } - return c / 100.f; + return (c + 1) / 100.f; } } @@ -299,7 +299,7 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& maxOut = rtengine::LIM(maxOut, minVal, maxVal); } -void buildBlendMask(const float* const * 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, float ** clipMask) { if (autoContrast) { constexpr float minLuminance = 2000.f; @@ -424,11 +424,20 @@ void buildBlendMask(const float* const * luminance, float **blend, int W, int H, for(int j = 2; j < H - 2; ++j) { int i = 2; #ifdef __SSE2__ - for(; i < W - 5; i += 4) { - vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) + - SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev; + if (clipMask) { + for(; i < W - 5; i += 4) { + vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) + + SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev; - STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv)); + STVFU(blend[j][i], LVFU(clipMask[j][i]) * amountv * calcBlendFactor(contrastv, contrastThresholdv)); + } + } else { + for(; i < W - 5; i += 4) { + vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) + + SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev; + + STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv)); + } } #endif for(; i < W - 2; ++i) { @@ -436,7 +445,7 @@ void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float contrast = sqrtf(rtengine::SQR(luminance[j][i+1] - luminance[j][i-1]) + rtengine::SQR(luminance[j+1][i] - luminance[j-1][i]) + rtengine::SQR(luminance[j][i+2] - luminance[j][i-2]) + rtengine::SQR(luminance[j+2][i] - luminance[j-2][i])) * scale; - blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold); + blend[j][i] = (clipMask ? clipMask[j][i] : 1.f) * amount * calcBlendFactor(contrast, contrastThreshold); } } diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index 0ea000a26..5485346c7 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(const float* const * 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, float ** clipmask = nullptr); } diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 33cca1aba..c188622af 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -415,6 +415,13 @@ public : virtual void autoContrastChanged (double autoContrast) = 0; }; +class AutoRadiusListener +{ +public : + virtual ~AutoRadiusListener() = default; + virtual void autoRadiusChanged (double autoRadius) = 0; +}; + class WaveletListener { public: @@ -524,6 +531,7 @@ public: virtual void setBayerAutoContrastListener (AutoContrastListener* l) = 0; virtual void setXtransAutoContrastListener (AutoContrastListener* l) = 0; virtual void setpdSharpenAutoContrastListener (AutoContrastListener* l) = 0; + virtual void setpdSharpenAutoRadiusListener (AutoRadiusListener* l) = 0; virtual void setAutoBWListener (AutoBWListener* l) = 0; virtual void setAutoWBListener (AutoWBListener* l) = 0; virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 2022d2d15..6dbb0e649 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -222,7 +222,7 @@ private: imgsrc->demosaic (params.raw, autoContrast, contrastThreshold, params.pdsharpening.enabled && pl); if (params.pdsharpening.enabled) { - imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast); + imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast, params.pdsharpening.deconvradius); } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index ef1294dea..0dffe2fd0 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 {}; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) override {}; }; } #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 84860329f..2ab5702ea 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -168,6 +168,7 @@ void ParamsEdited::set(bool v) pdsharpening.enabled = v; pdsharpening.contrast = v; pdsharpening.autoContrast = v; + pdsharpening.autoRadius = v; pdsharpening.gamma = v; pdsharpening.deconvradius = v; pdsharpening.deconviter = v; @@ -752,6 +753,7 @@ void ParamsEdited::initFrom(const std::vector& 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.autoRadius = pdsharpening.autoRadius && p.pdsharpening.autoRadius == other.pdsharpening.autoRadius; 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; @@ -1732,6 +1734,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast; } + if (pdsharpening.autoRadius) { + toEdit.pdsharpening.autoRadius = mods.pdsharpening.autoRadius; + } + if (pdsharpening.gamma) { toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma; } @@ -3289,5 +3295,5 @@ bool FilmNegativeParamsEdited::isUnchanged() const bool CaptureSharpeningParamsEdited::isUnchanged() const { - return enabled && contrast && autoContrast && gamma && deconvradius && deconviter; + return enabled && contrast && autoContrast && autoRadius && gamma && deconvradius && deconviter; } \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 860ffc551..1bd7170d4 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -202,6 +202,7 @@ struct CaptureSharpeningParamsEdited { bool enabled; bool contrast; bool autoContrast; + bool autoRadius; bool gamma; bool deconvradius; bool deconviter; diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index c85b42e95..ef0ad90c2 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -35,6 +35,7 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS 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"); + EvPdShrAutoRadius = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_AUTO_RADIUS"); Gtk::HBox* hb = Gtk::manage(new Gtk::HBox()); hb->show(); @@ -51,6 +52,8 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS 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)); + dradius->addAutoButton(M("TP_PDSHARPENING_AUTORADIUS_TOOLTIP")); + dradius->setAutoValue(true); diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 1, 100, 1, 20)); rld->pack_start(*gamma); rld->pack_start(*dradius); @@ -85,6 +88,7 @@ void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited) if (pedited) { contrast->setEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited); contrast->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoContrast); + dradius->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoRadius); gamma->setEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited); dradius->setEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited); diter->setEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited); @@ -98,8 +102,10 @@ void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited) contrast->setAutoValue(pp->pdsharpening.autoContrast); gamma->setValue(pp->pdsharpening.gamma); dradius->setValue(pp->pdsharpening.deconvradius); + dradius->setAutoValue(pp->pdsharpening.autoRadius); diter->setValue(pp->pdsharpening.deconviter); lastAutoContrast = pp->pdsharpening.autoContrast; + lastAutoRadius = pp->pdsharpening.autoRadius; enableListener(); } @@ -112,6 +118,7 @@ void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited) pp->pdsharpening.enabled = getEnabled(); pp->pdsharpening.gamma = gamma->getValue(); pp->pdsharpening.deconvradius = dradius->getValue(); + pp->pdsharpening.autoRadius = dradius->getAutoValue(); pp->pdsharpening.deconviter =(int)diter->getValue(); if (pedited) { @@ -119,6 +126,7 @@ void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited) pedited->pdsharpening.autoContrast = !contrast->getAutoInconsistent(); pedited->pdsharpening.gamma = gamma->getEditedState(); pedited->pdsharpening.deconvradius = dradius->getEditedState(); + pedited->pdsharpening.autoRadius = !dradius->getAutoInconsistent(); pedited->pdsharpening.deconviter = diter->getEditedState(); pedited->pdsharpening.enabled = !get_inconsistent(); } @@ -224,26 +232,62 @@ void PdSharpening::autoContrastChanged(double autoContrast) ); } +void PdSharpening::autoRadiusChanged(double autoRadius) +{ + idle_register.add( + [this, autoRadius]() -> bool + { + disableListener(); + dradius->setValue(autoRadius); + 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); + if (a == contrast) { + if (multiImage) { + if (contrast->getAutoInconsistent()) { + contrast->setAutoInconsistent(false); + contrast->setAutoValue(false); + } else if (lastAutoContrast) { + contrast->setAutoInconsistent(true); + } + + lastAutoContrast = contrast->getAutoValue(); } - 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")); + } + } + } else { // must be dradius + if (multiImage) { + if (dradius->getAutoInconsistent()) { + dradius->setAutoInconsistent(false); + dradius->setAutoValue(false); + } else if (lastAutoRadius) { + dradius->setAutoInconsistent(true); + } - 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")); + lastAutoRadius = dradius->getAutoValue(); + } + + if (listener) { + if (dradius->getAutoInconsistent()) { + listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_UNCHANGED")); + } else if (dradius->getAutoValue()) { + listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_DISABLED")); + } } } } diff --git a/rtgui/pdsharpening.h b/rtgui/pdsharpening.h index b2f7b6e57..e56b4b085 100644 --- a/rtgui/pdsharpening.h +++ b/rtgui/pdsharpening.h @@ -21,7 +21,7 @@ #include "adjuster.h" #include "toolpanel.h" -class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener +class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener, public rtengine::AutoRadiusListener { protected: @@ -31,11 +31,13 @@ protected: Adjuster* diter; bool lastAutoContrast; + bool lastAutoRadius; rtengine::ProcEvent EvPdShrContrast; rtengine::ProcEvent EvPdShrDRadius; rtengine::ProcEvent EvPdSharpenGamma; rtengine::ProcEvent EvPdShrDIterations; rtengine::ProcEvent EvPdShrAutoContrast; + rtengine::ProcEvent EvPdShrAutoRadius; IdleRegister idle_register; public: @@ -53,6 +55,7 @@ public: void enabledChanged () override; void autoContrastChanged (double autoContrast) override; + void autoRadiusChanged (double autoRadius) 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 a664942a1..4fec4a18a 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -573,6 +573,7 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool ipc->setBayerAutoContrastListener (bayerprocess); ipc->setXtransAutoContrastListener (xtransprocess); ipc->setpdSharpenAutoContrastListener (pdSharpening); + ipc->setpdSharpenAutoRadiusListener (pdSharpening); ipc->setAutoWBListener (whitebalance); ipc->setAutoColorTonListener (colortoning); ipc->setAutoChromaListener (dirpyrdenoise); diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 390967395..3a4ddc05f 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -242,7 +242,6 @@ public: void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false) override; -// void autoContrastChanged (double autoContrast); // profilechangelistener interface void profileChange( const rtengine::procparams::PartialProfile* nparams, From af10bf8c7c8f06c5fc2effecda4533937016ee9a Mon Sep 17 00:00:00 2001 From: "luz.paz" Date: Wed, 11 Sep 2019 17:38:55 -0400 Subject: [PATCH 2/2] Fix misc. source comment typo Found via `codespell -q 3 -I ../rawtherapy-whitelist.txt -S ./rtdata/languages -L bord,hist,fo,reall,bloc,alph,dof,thre,makro,chang,currentry,portugues,vektor,ue` --- rtengine/demosaic_algos.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index 49b386b0a..51db8bb3f 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -2024,7 +2024,8 @@ void RawImageSource::refinement(int PassCount) // Refinement based on EECI demozaicing algorithm by L. Chang and Y.P. Tan // from "Lassus" : Luis Sanz Rodriguez, adapted by Jacques Desmis - JDC - and Oliver Duis for RawTherapee -// increases the signal to noise ratio (PSNR) # +1 to +2 dB : tested with Dcraw : eg: Lighthouse + AMaZE : whitout refinement:39.96dB, with refinement:41.86 dB +// increases the signal to noise ratio (PSNR) # +1 to +2 dB : tested with Dcraw : +// eg: Lighthouse + AMaZE : without refinement:39.96 dB, with refinement:41.86 dB // reduce color artifacts, improves the interpolation // but it's relatively slow //