From 506254ce4631985bd2832d490dd760a2dc925949 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 14 Aug 2019 17:19:26 +0200 Subject: [PATCH 01/21] capture sharpening: first mockup --- rtdata/languages/default | 8 ++ rtengine/color.h | 12 ++ rtengine/imagesource.h | 3 +- rtengine/improccoordinator.cc | 6 +- rtengine/improcfun.h | 2 +- rtengine/ipsharpen.cc | 8 +- rtengine/procparams.cc | 25 ++++ rtengine/procparams.h | 2 + rtengine/rawimagesource.cc | 33 +++++ rtengine/rawimagesource.h | 2 +- rtengine/simpleprocess.cc | 3 + rtengine/stdimagesource.h | 2 +- rtgui/CMakeLists.txt | 1 + rtgui/addsetids.h | 1 + rtgui/paramsedited.cc | 46 +++++++ rtgui/paramsedited.h | 3 + rtgui/pdsharpening.cc | 221 ++++++++++++++++++++++++++++++++++ rtgui/pdsharpening.h | 62 ++++++++++ rtgui/toolpanelcoord.cc | 10 +- rtgui/toolpanelcoord.h | 3 +- 20 files changed, 441 insertions(+), 12 deletions(-) create mode 100644 rtgui/pdsharpening.cc create mode 100644 rtgui/pdsharpening.h diff --git a/rtdata/languages/default b/rtdata/languages/default index e8392db6b..9aa743ab0 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -763,6 +763,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_ENABLED;Capture Sharpening +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 +778,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 +1798,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 +2027,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/color.h b/rtengine/color.h index 5bf178636..22a648634 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1804,6 +1804,18 @@ public: return (hr); } + static inline void RGB2YCbCr(float R, float G, float B, float &Y, float &Cb, float &Cr) { + Y = 0.2627f * R + 0.6780f * G + 0.0593f * B; + Cb = -0.2627f * R - 0.6780f * G + (1.f - 0.0593f) * B; + Cr = (1.f - 0.2627f) * R - 0.6780f * G - 0.0593f * B; + + } + + static inline void YCbCr2RGB(float Y, float Cb, float Cr, float &R, float &G, float &B) { + R = std::max(Y + Cr, 0.f); + G = std::max(Y - (0.0593f / 0.6780f) * Cb - (0.2627f / 0.6780f) * Cr, 0.f); + B = std::max(Y + Cb, 0.f); + } }; } diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index bf73b5bb2..6e57bd532 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -46,7 +46,7 @@ struct LensProfParams; struct RAWParams; struct RetinexParams; struct ToneCurveParams; - +struct SharpeningParams; } class ImageMatrices @@ -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::SharpeningParams &sharpeningParams) = 0; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 6f471eedf..2e2ae9084 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -332,6 +332,9 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast; imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic + if (params->pdsharpening.enabled) { + imgsrc->captureSharpening(params->pdsharpening); + } if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) { bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); } @@ -1546,7 +1549,8 @@ 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; *params = *nextParams; int change = changeSinceLast; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 987a460d7..65f0f5a96 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, 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 9d7358fa9..eeda786a5 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, int W, int H, const SharpeningParams &sharpenParam, double Scale) { if (sharpenParam.deconvamount == 0 && sharpenParam.blurradius < 0.25f) { return; @@ -201,7 +201,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 @@ -274,7 +274,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, lab->W, lab->H, sharpenParam, scale); return; } BENCHFUN @@ -905,7 +905,7 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) if (params->sharpening.method == "rld") { - deconvsharpening (ncie->sh_p, b2, ncie->W, ncie->H, params->sharpening); + deconvsharpening (ncie->sh_p, b2, ncie->W, ncie->H, params->sharpening, scale); return; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 66837f89b..4f2cdce2e 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1080,6 +1080,7 @@ SharpeningParams::SharpeningParams() : enabled(false), contrast(20.0), blurradius(0.2), + gamma(1.0), radius(0.5), amount(200), threshold(20, 80, 2000, 1200, false), @@ -1102,6 +1103,7 @@ 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 @@ -2787,6 +2789,13 @@ void ProcParams::setDefaults() prsharpening.deconviter = 100; prsharpening.deconvdamping = 0; + pdsharpening = {}; + pdsharpening.contrast = 0.0; + prsharpening.method = "rld"; + pdsharpening.gamma = 1.0; + pdsharpening.deconvradius = 0.75; + pdsharpening.deconviter = 30; + vibrance = {}; wb = {}; @@ -3296,6 +3305,13 @@ 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.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); @@ -4376,6 +4392,15 @@ 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", "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 369af85fa..f9255f815 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -494,6 +494,7 @@ struct SharpeningParams { bool enabled; double contrast; double blurradius; + double gamma; double radius; int amount; Threshold threshold; @@ -1526,6 +1527,7 @@ public: ColorToningParams colorToning; ///< Color Toning parameters SharpeningParams sharpening; ///< Sharpening parameters SharpeningParams prsharpening; ///< Sharpening parameters for post resize sharpening + SharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening 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 6cb6fff16..5d8dd8448 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 @@ -4949,6 +4952,36 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int } } +void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams) { +BENCHFUN + + array2D Y(W,H); + array2D Cb(W,H); + array2D Cr(W,H); + const float gamma = sharpeningParams.gamma; + StopWatch Stop1("rgb2Y"); + #pragma omp parallel for + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W ; ++j) { + Color::RGB2YCbCr(std::max(red[i][j], 0.f), std::max(green[i][j], 0.f), std::max(blue[i][j], 0.f), Y[i][j], Cb[i][j], Cr[i][j]); + Y[i][j] = pow_F(Y[i][j], 1.f / gamma); + } + } + Stop1.stop(); + array2D tmp(W, H); + ProcParams dummy; + ImProcFunctions ipf(&dummy); + ipf.deconvsharpening(Y, tmp, W, H, sharpeningParams, 1.0); + StopWatch Stop2("Y2RGB"); + #pragma omp parallel for + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W ; ++j) { + Y[i][j] = pow_F(Y[i][j], gamma); + Color::YCbCr2RGB(Y[i][j], Cb[i][j], Cr[i][j], red[i][j], green[i][j], blue[i][j]); + } + } + Stop2.stop(); +} void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 7c50991c0..7b86111af 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -305,7 +305,7 @@ protected: void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override; - + void captureSharpening(const procparams::SharpeningParams &sharpeningParams) override; }; } diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 2d594e0af..a9d32dbac 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -221,6 +221,9 @@ private: double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicContrast : params.raw.xtranssensor.dualDemosaicContrast; imgsrc->demosaic (params.raw, autoContrast, contrastThreshold); + if (params.pdsharpening.enabled) { + imgsrc->captureSharpening(params.pdsharpening); + } if (pl) { diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 8f16880dc..a8251ade0 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -102,7 +102,7 @@ public: void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;} void flushRGB () override; - + void captureSharpening(const procparams::SharpeningParams &sharpeningParams) 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/paramsedited.cc b/rtgui/paramsedited.cc index cb3d4151c..da65cbf8e 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.gamma = v; + pdsharpening.deconvamount = 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,11 @@ 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.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 +1667,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 +1719,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.deconvamount) { + toEdit.pdsharpening.deconvamount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.pdsharpening.deconvamount + mods.pdsharpening.deconvamount : mods.pdsharpening.deconvamount; + } + + if (pdsharpening.gamma) { + toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma; + } + + 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 +3285,8 @@ bool FilmNegativeParamsEdited::isUnchanged() const { return enabled && redRatio && greenExp && blueRatio; } + +bool SharpeningParamsEdited::isUnchanged() const +{ + return enabled && contrast && gamma && deconvradius && deconviter; +} \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 08a41fc7a..5191bce56 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -180,6 +180,7 @@ struct SharpeningParamsEdited { bool enabled; bool contrast; bool blurradius; + bool gamma; bool radius; bool amount; bool threshold; @@ -194,6 +195,7 @@ struct SharpeningParamsEdited { bool deconvradius; bool deconviter; bool deconvdamping; + bool isUnchanged() const; }; struct VibranceParamsEdited { @@ -684,6 +686,7 @@ struct ParamsEdited { ColorToningEdited colorToning; RetinexParamsEdited retinex; SharpeningParamsEdited sharpening; + SharpeningParamsEdited pdsharpening; SharpeningParamsEdited prsharpening; SharpenEdgeParamsEdited sharpenEdge; SharpenMicroParamsEdited sharpenMicro; diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc new file mode 100644 index 000000000..a33da813d --- /dev/null +++ b/rtgui/pdsharpening.cc @@ -0,0 +1,221 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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" + +using namespace rtengine; +using namespace rtengine::procparams; + +PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PDSHARPENING_LABEL"), false, true) +{ + + auto m = ProcEventMapper::getInstance(); + EvPdShrEnabled = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ENABLED"); + EvPdShrContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_CONTRAST"); + EvPdSharpenGamma = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_GAMMA"); + EvPdShrDRadius = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_RADIUS"); + EvPdShrDIterations = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); + + Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ()); + hb->show (); + contrast = Gtk::manage(new Adjuster (M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 15)); + contrast->setAdjusterListener (this); + pack_start(*contrast); + contrast->show(); + + pack_start (*hb); + + rld = new Gtk::VBox (); + gamma = Gtk::manage (new Adjuster (M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.0)); + dradius = Gtk::manage (new Adjuster (M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); + diter = Gtk::manage (new Adjuster (M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); + 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); + + rld->reference(); +} + +PdSharpening::~PdSharpening () +{ + + delete rld; +} + + +void PdSharpening::read (const ProcParams* pp, const ParamsEdited* pedited) +{ + + disableListener (); + + if (pedited) { + contrast->setEditedState (pedited->pdsharpening.contrast ? Edited : UnEdited); + 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); + gamma->setValue (pp->pdsharpening.gamma); + dradius->setValue (pp->pdsharpening.deconvradius); + diter->setValue (pp->pdsharpening.deconviter); + + enableListener (); +} + +void PdSharpening::write (ProcParams* pp, ParamsEdited* pedited) +{ + + pp->pdsharpening.contrast = contrast->getValue (); + 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.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::adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) +{ +} + +void PdSharpening::adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) +{ +} + +void PdSharpening::adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) +{ +} + +void PdSharpening::adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) +{ +} + +void PdSharpening::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) +{ +} + +void PdSharpening::setBatchMode (bool batchMode) +{ + + ToolPanel::setBatchMode (batchMode); + +// pack_start (*rld); + + contrast->showEditedCB (); + gamma->showEditedCB (); + dradius->showEditedCB (); + diter->showEditedCB (); +} + +void PdSharpening::setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool amountadd, bool dampingadd, bool iteradd, bool edgetoladd, bool haloctrladd) +{ + + 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); +} diff --git a/rtgui/pdsharpening.h b/rtgui/pdsharpening.h new file mode 100644 index 000000000..998322fe4 --- /dev/null +++ b/rtgui/pdsharpening.h @@ -0,0 +1,62 @@ +/* + * 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 +#include "adjuster.h" +#include "thresholdadjuster.h" +#include "toolpanel.h" + +class PdSharpening : public ToolParamBlock, public ThresholdAdjusterListener, public AdjusterListener, public FoldableToolPanel +{ + +protected: + Adjuster* contrast; + Adjuster* gamma; + Adjuster* dradius; + Adjuster* diter; + Gtk::VBox* rld; + + rtengine::ProcEvent EvPdShrEnabled; + rtengine::ProcEvent EvPdShrContrast; + rtengine::ProcEvent EvPdShrDRadius; + rtengine::ProcEvent EvPdSharpenGamma; + rtengine::ProcEvent EvPdShrDIterations; +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 adjusterChanged (Adjuster* a, double newval) override; + void enabledChanged () override; + + void adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override; + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override; + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override; + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override; + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; + + void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool amountadd, bool dampingadd, bool iteradd, bool edgetoladd, bool haloctrladd); + void trimValues (rtengine::procparams::ProcParams* pp) override; +}; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index f7e2991e1..64e44b3fb 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::hide(); 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; diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index a1b26b29f..b88fef124 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; From 8421f8780a1c404d72ed92b33ba57f5acecee0c6 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 15 Aug 2019 20:40:32 +0200 Subject: [PATCH 02/21] capture sharpening: contrast threshold --- rtdata/languages/default | 2 +- rtengine/imagesource.h | 2 +- rtengine/improccoordinator.cc | 18 +++++++++--- rtengine/improccoordinator.h | 4 +-- rtengine/improcfun.h | 2 +- rtengine/ipsharpen.cc | 38 ++++++++---------------- rtengine/procevents.h | 2 +- rtengine/procparams.cc | 4 +-- rtengine/rawimagesource.cc | 55 ++++++++++++++++++++++++++++++----- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 4 ++- rtengine/rtengine.h | 2 +- rtengine/simpleprocess.cc | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/pdsharpening.cc | 19 ++++++++++-- rtgui/pdsharpening.h | 1 - rtgui/toolpanelcoord.cc | 3 +- 17 files changed, 106 insertions(+), 56 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 9aa743ab0..ba96a43cb 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 @@ -764,7 +765,6 @@ 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_ENABLED;Capture Sharpening HISTORY_MSG_PDSHARPEN_GAMMA;CAS - Gamma HISTORY_MSG_PDSHARPEN_ITERATIONS;CAS - Iterations HISTORY_MSG_PDSHARPEN_RADIUS;CAS - Radius diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 6e57bd532..006fe59a6 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::SharpeningParams &sharpeningParams) = 0; + virtual void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) = 0; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 2e2ae9084..29d56e79e 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), @@ -333,7 +334,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic if (params->pdsharpening.enabled) { - imgsrc->captureSharpening(params->pdsharpening); + imgsrc->captureSharpening(params->pdsharpening, sharpMask); } if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) { bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); @@ -1347,9 +1348,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::EvPdShrEnabled : rtengine::EvShrEnabled; + } else { + sharpMaskChanged = false; + return rtengine::EvShrEnabled; + } } void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb) @@ -1550,8 +1558,10 @@ void ImProcCoordinator::process() || params->wavelet != nextParams->wavelet || params->dirpyrequalizer != nextParams->dirpyrequalizer || params->dehaze != nextParams->dehaze - || params->pdsharpening != nextParams->pdsharpening; + || 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 fdf74d297..0315bc180 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; @@ -277,7 +277,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(); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 65f0f5a96..6fe8a785d 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, double Scale); + void deconvsharpening(float** luminance, float** buffer, float** 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 eeda786a5..897aaf7b5 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, double Scale) +void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, float ** 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) { @@ -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, scale); + 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, scale); + 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 bb6a30038..670c0c102 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -520,7 +520,7 @@ enum ProcEventCode { EvWBEnabled = 490, EvRGBEnabled = 491, EvLEnabled = 492, -// EvPixelShiftOneGreen = 493, can be reused + EvPdShrEnabled = 493, NUMOFEVENTS diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 4f2cdce2e..e1e66d99c 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2790,9 +2790,9 @@ void ProcParams::setDefaults() prsharpening.deconvdamping = 0; pdsharpening = {}; - pdsharpening.contrast = 0.0; + pdsharpening.contrast = 10.0; prsharpening.method = "rld"; - pdsharpening.gamma = 1.0; + pdsharpening.gamma = 1.35; pdsharpening.deconvradius = 0.75; pdsharpening.deconviter = 30; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 5d8dd8448..96f161d9f 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -38,6 +38,7 @@ #include "camconst.h" #include "procparams.h" #include "color.h" +#include "rt_algo.h" #define BENCHMARK #include "StopWatch.h" #ifdef _OPENMP @@ -4952,26 +4953,65 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int } } -void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams) { +void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) { BENCHFUN - array2D Y(W,H); - array2D Cb(W,H); - array2D Cr(W,H); + 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 = sharpeningParams.contrast / 100.f; + + 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(red[i], green[i], blue[i], L[i], xyz_rgb, W); + } + 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); + +#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; + } + } + return; + } + + array2D L(W,H); + array2D& Y = red; // red will be overridden anyway => we can use its buffer to store Y + array2D& Cb = green; // green will be overridden anyway => we can use its buffer to store Cb + array2D& Cr = blue; // blue will be overridden anyway => we can use its buffer to store Cr const float gamma = sharpeningParams.gamma; + StopWatch Stop1("rgb2Y"); #pragma omp parallel for for (int i = 0; i < H; ++i) { - for (int j = 0; j < W ; ++j) { + Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); + for (int j = 0; j < W; ++j) { Color::RGB2YCbCr(std::max(red[i][j], 0.f), std::max(green[i][j], 0.f), std::max(blue[i][j], 0.f), Y[i][j], Cb[i][j], Cr[i][j]); Y[i][j] = pow_F(Y[i][j], 1.f / gamma); } } + // 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); + Stop1.stop(); - array2D tmp(W, H); + array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer ProcParams dummy; ImProcFunctions ipf(&dummy); - ipf.deconvsharpening(Y, tmp, W, H, sharpeningParams, 1.0); + ipf.deconvsharpening(Y, tmp, blend, W, H, sharpeningParams, 1.0); StopWatch Stop2("Y2RGB"); #pragma omp parallel for for (int i = 0; i < H; ++i) { @@ -4982,6 +5022,7 @@ BENCHFUN } Stop2.stop(); } + void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 7b86111af..a7aee0843 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -305,7 +305,7 @@ protected: void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams) override; + void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) override; }; } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index d741b1744..8282f5b4b 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -519,7 +519,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { HDR, // EvTMFattalAmount ALLNORAW, // EvWBEnabled RGBCURVE, // EvRGBEnabled - LUMINANCECURVE // EvLEnabled + LUMINANCECURVE, // EvLEnabled + DEMOSAIC // EvPdShrEnabled + }; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index f772975b0..ca4cebe2e 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -535,7 +535,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 a9d32dbac..24a3f3263 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -222,7 +222,7 @@ private: imgsrc->demosaic (params.raw, autoContrast, contrastThreshold); if (params.pdsharpening.enabled) { - imgsrc->captureSharpening(params.pdsharpening); + imgsrc->captureSharpening(params.pdsharpening, false); } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index a8251ade0..5acb8d113 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -102,7 +102,7 @@ public: void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;} void flushRGB () override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams) override {}; + void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) override {}; }; } #endif diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index a33da813d..896d416b5 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -19,6 +19,7 @@ #include #include "eventmapper.h" #include "pdsharpening.h" +#include "options.h" using namespace rtengine; using namespace rtengine::procparams; @@ -27,7 +28,6 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD { auto m = ProcEventMapper::getInstance(); - EvPdShrEnabled = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ENABLED"); EvPdShrContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_CONTRAST"); EvPdSharpenGamma = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_GAMMA"); EvPdShrDRadius = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_RADIUS"); @@ -35,7 +35,7 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ()); hb->show (); - contrast = Gtk::manage(new Adjuster (M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 15)); + contrast = Gtk::manage(new Adjuster (M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 10)); contrast->setAdjusterListener (this); pack_start(*contrast); contrast->show(); @@ -43,7 +43,7 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD pack_start (*hb); rld = new Gtk::VBox (); - gamma = Gtk::manage (new Adjuster (M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.0)); + gamma = Gtk::manage (new Adjuster (M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.35)); dradius = Gtk::manage (new Adjuster (M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); diter = Gtk::manage (new Adjuster (M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); rld->pack_start (*gamma); @@ -59,6 +59,19 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD gamma->setAdjusterListener (this); diter->setAdjusterListener (this); + if (contrast->delay < options.adjusterMaxDelay) { + contrast->delay = options.adjusterMaxDelay; + } + if (dradius->delay < options.adjusterMaxDelay) { + dradius->delay = options.adjusterMaxDelay; + } + if (gamma->delay < options.adjusterMaxDelay) { + gamma->delay = options.adjusterMaxDelay; + } + if (diter->delay < options.adjusterMaxDelay) { + diter->delay = options.adjusterMaxDelay; + } + rld->reference(); } diff --git a/rtgui/pdsharpening.h b/rtgui/pdsharpening.h index 998322fe4..fe9359349 100644 --- a/rtgui/pdsharpening.h +++ b/rtgui/pdsharpening.h @@ -33,7 +33,6 @@ protected: Adjuster* diter; Gtk::VBox* rld; - rtengine::ProcEvent EvPdShrEnabled; rtengine::ProcEvent EvPdShrContrast; rtengine::ProcEvent EvPdShrDRadius; rtengine::ProcEvent EvPdSharpenGamma; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 64e44b3fb..93f45aefe 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -695,8 +695,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 From e4b955523e7a46882bd1982c962cfe2bc9e72467 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 15 Aug 2019 23:23:28 +0200 Subject: [PATCH 03/21] Capture sharpening: small speedup --- rtengine/color.h | 58 ++++++++++++++++++++++++++++++++++++-- rtengine/rawimagesource.cc | 10 ++----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/rtengine/color.h b/rtengine/color.h index 22a648634..7b198f284 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1806,9 +1806,37 @@ public: static inline void RGB2YCbCr(float R, float G, float B, float &Y, float &Cb, float &Cr) { Y = 0.2627f * R + 0.6780f * G + 0.0593f * B; - Cb = -0.2627f * R - 0.6780f * G + (1.f - 0.0593f) * B; - Cr = (1.f - 0.2627f) * R - 0.6780f * G - 0.0593f * B; + Cb = (1.f - 0.0593f) * B - (0.2627f * R + 0.6780f * G); + Cr = (1.f - 0.2627f) * R - (0.6780f * G + 0.0593f * B); + } + static inline void RGB2YCbCr(float* R, float* G, float* B, float* Y, float* Cb, float *Cr, 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); + const vfloat c4v = F2V(1.f - 0.0593f); + const vfloat c5v = F2V(1.f - 0.2627f); + 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); + STVFU(Y[i], pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav)); + STVFU(Cb[i], c4v * Bv - (c1v * Rv + c2v * Gv)); + STVFU(Cr[i], c5v * Rv - (c2v * Gv + c3v * Bv)); + } +#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); + Y[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma); + Cb[i] = (1.f - 0.0593f) * b - (0.2627f * r + 0.6780f * g); + Cr[i] = (1.f - 0.2627f) * r - (0.6780f * g + 0.0593f * b); + } } static inline void YCbCr2RGB(float Y, float Cb, float Cr, float &R, float &G, float &B) { @@ -1816,6 +1844,32 @@ public: G = std::max(Y - (0.0593f / 0.6780f) * Cb - (0.2627f / 0.6780f) * Cr, 0.f); B = std::max(Y + Cb, 0.f); } + + static inline void YCbCr2RGB(float* Y, float* Cb, float* Cr, float* R, float* G, float* B, float gamma, int W) { + int i = 0; +#ifdef __SSE2__ + const vfloat gammav = F2V(gamma); + const vfloat c1v = F2V(0.0593f / 0.6780f); + const vfloat c2v = F2V(0.2627f / 0.6780f); + + for (; i < W - 3; i += 4) { + const vfloat Yv = pow_F(LVFU(Y[i]), gammav); + const vfloat Crv = LVFU(Cr[i]); + const vfloat Cbv = LVFU(Cb[i]); + STVFU(R[i], vmaxf(Yv + Crv, ZEROV)); + STVFU(G[i], vmaxf(Yv - c1v * Cbv - c2v * Crv, ZEROV)); + STVFU(B[i], vmaxf(Yv + Cbv, ZEROV)); + } +#endif + for (; i < W; ++i) { + const float y = pow_F(Y[i], gamma); + const float cr = Cr[i]; + const float cb = Cb[i]; + R[i] = std::max(y + cr, 0.f); + G[i] = std::max(y - (0.0593f / 0.6780f) * cb - (0.2627f / 0.6780f) * cr, 0.f); + B[i] = std::max(y + cb, 0.f); + } + } }; } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 96f161d9f..25beb6208 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -4998,10 +4998,7 @@ BENCHFUN #pragma omp parallel for for (int i = 0; i < H; ++i) { Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - for (int j = 0; j < W; ++j) { - Color::RGB2YCbCr(std::max(red[i][j], 0.f), std::max(green[i][j], 0.f), std::max(blue[i][j], 0.f), Y[i][j], Cb[i][j], Cr[i][j]); - Y[i][j] = pow_F(Y[i][j], 1.f / gamma); - } + Color::RGB2YCbCr(red[i], green[i], blue[i], Y[i], Cb[i], Cr[i], gamma, W); } // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); @@ -5015,10 +5012,7 @@ BENCHFUN StopWatch Stop2("Y2RGB"); #pragma omp parallel for for (int i = 0; i < H; ++i) { - for (int j = 0; j < W ; ++j) { - Y[i][j] = pow_F(Y[i][j], gamma); - Color::YCbCr2RGB(Y[i][j], Cb[i][j], Cr[i][j], red[i][j], green[i][j], blue[i][j]); - } + Color::YCbCr2RGB(Y[i], Cb[i], Cr[i], red[i], green[i], blue[i], gamma, W); } Stop2.stop(); } From 7b3c50bc315e865259945ed246ab08926439d440 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 17 Aug 2019 15:18:24 +0200 Subject: [PATCH 04/21] Capture sharpening: auto contrast threshold --- rtdata/languages/default | 1 + rtengine/imagesource.h | 2 +- rtengine/improccoordinator.cc | 18 ++++++--- rtengine/improccoordinator.h | 6 +++ rtengine/procparams.cc | 5 +++ rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 11 +++--- rtengine/rawimagesource.h | 2 +- rtengine/rtengine.h | 1 + rtengine/simpleprocess.cc | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/paramsedited.cc | 8 +++- rtgui/paramsedited.h | 1 + rtgui/pdsharpening.cc | 72 +++++++++++++++++++++++++---------- rtgui/pdsharpening.h | 15 ++++---- rtgui/toolpanelcoord.cc | 1 + 16 files changed, 103 insertions(+), 45 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index ba96a43cb..d891d8692 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -765,6 +765,7 @@ 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 diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 006fe59a6..46cbc28a6 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::SharpeningParams &sharpeningParams, bool showMask) = 0; + virtual void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 29d56e79e..7d485fd19 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -121,6 +121,7 @@ ImProcCoordinator::ImProcCoordinator() : flatFieldAutoClipListener(nullptr), bayerAutoContrastListener(nullptr), xtransAutoContrastListener(nullptr), + pdSharpenAutoContrastListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), actListener(nullptr), @@ -333,16 +334,21 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast; imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic - if (params->pdsharpening.enabled) { - imgsrc->captureSharpening(params->pdsharpening, sharpMask); - } 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 (params->pdsharpening.enabled) { + double pdSharpencontrastThreshold = params->pdsharpening.contrast; + imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold); + if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) { + pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold); + } + } + + // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag todo |= M_INIT; diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 0315bc180..ef1c52bc6 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -161,6 +161,7 @@ protected: FlatFieldAutoClipListener *flatFieldAutoClipListener; AutoContrastListener *bayerAutoContrastListener; AutoContrastListener *xtransAutoContrastListener; + AutoContrastListener *pdSharpenAutoContrastListener; FrameCountListener *frameCountListener; ImageTypeListener *imageTypeListener; @@ -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/procparams.cc b/rtengine/procparams.cc index e1e66d99c..467f1446a 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1079,6 +1079,7 @@ void ColorToningParams::getCurves(ColorGradientCurve& colorCurveLUT, OpacityCurv SharpeningParams::SharpeningParams() : enabled(false), contrast(20.0), + autoContrast(false), blurradius(0.2), gamma(1.0), radius(0.5), @@ -1107,6 +1108,7 @@ bool SharpeningParams::operator ==(const SharpeningParams& other) const && 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 @@ -2791,6 +2793,7 @@ void ProcParams::setDefaults() pdsharpening = {}; pdsharpening.contrast = 10.0; + pdsharpening.autoContrast = true; prsharpening.method = "rld"; pdsharpening.gamma = 1.35; pdsharpening.deconvradius = 0.75; @@ -3308,6 +3311,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo // 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); @@ -4395,6 +4399,7 @@ 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); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index f9255f815..83b6c3e44 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -493,6 +493,7 @@ struct ColorToningParams { struct SharpeningParams { bool enabled; double contrast; + bool autoContrast; double blurradius; double gamma; double radius; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 25beb6208..d84c0436a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -4953,7 +4953,7 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int } } -void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) { +void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { BENCHFUN const float xyz_rgb[3][3] = { // XYZ from RGB @@ -4962,7 +4962,7 @@ BENCHFUN { 0.019334, 0.119193, 0.950227 } }; - float contrast = sharpeningParams.contrast / 100.f; + float contrast = conrastThreshold / 100.f; if (showMask) { StopWatch Stop1("Show mask"); @@ -4975,8 +4975,8 @@ BENCHFUN Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); } 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); - + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + conrastThreshold = contrast * 100.f; #ifdef _OPENMP #pragma omp parallel for #endif @@ -5002,7 +5002,8 @@ 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); + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + 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 diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index a7aee0843..8cf6b08a9 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -305,7 +305,7 @@ protected: void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) override; + void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; }; } diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index ca4cebe2e..86e9ade75 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; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 24a3f3263..0524734e0 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -222,7 +222,7 @@ private: imgsrc->demosaic (params.raw, autoContrast, contrastThreshold); if (params.pdsharpening.enabled) { - imgsrc->captureSharpening(params.pdsharpening, false); + imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast); } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 5acb8d113..175f664f8 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -102,7 +102,7 @@ public: void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;} void flushRGB () override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask) override {}; + void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; }; } #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index da65cbf8e..4029d0b9b 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -167,6 +167,7 @@ void ParamsEdited::set(bool v) sharpening.deconvdamping = v; pdsharpening.enabled = v; pdsharpening.contrast = v; + pdsharpening.autoContrast = v; pdsharpening.gamma = v; pdsharpening.deconvamount = v; pdsharpening.deconvradius = v; @@ -751,6 +752,7 @@ void ParamsEdited::initFrom(const std::vector& 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; @@ -1727,6 +1729,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng 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.deconvamount) { toEdit.pdsharpening.deconvamount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.pdsharpening.deconvamount + mods.pdsharpening.deconvamount : mods.pdsharpening.deconvamount; } @@ -3288,5 +3294,5 @@ bool FilmNegativeParamsEdited::isUnchanged() const bool SharpeningParamsEdited::isUnchanged() const { - return enabled && contrast && gamma && deconvradius && deconviter; + return enabled && contrast && autoContrast && gamma && deconvradius && deconviter; } \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 5191bce56..6d229e689 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -179,6 +179,7 @@ struct SharpenMicroParamsEdited { struct SharpeningParamsEdited { bool enabled; bool contrast; + bool autoContrast; bool blurradius; bool gamma; bool radius; diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 896d416b5..43b32ce7e 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -20,6 +20,7 @@ #include "eventmapper.h" #include "pdsharpening.h" #include "options.h" +#include "../rtengine/procparams.h" using namespace rtengine; using namespace rtengine::procparams; @@ -32,11 +33,15 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD EvPdSharpenGamma = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_GAMMA"); EvPdShrDRadius = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_RADIUS"); EvPdShrDIterations = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); + EvPdShrAutoContrast = m->newEvent(DEMOSAIC, "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(); @@ -77,7 +82,7 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD PdSharpening::~PdSharpening () { - + idle_register.destroy(); delete rld; } @@ -89,6 +94,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); gamma->setEditedState (pedited->pdsharpening.gamma ? Edited : UnEdited); dradius->setEditedState (pedited->pdsharpening.deconvradius ? Edited : UnEdited); diter->setEditedState (pedited->pdsharpening.deconviter ? Edited : UnEdited); @@ -99,9 +105,11 @@ void PdSharpening::read (const ProcParams* pp, const ParamsEdited* pedited) 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 (); } @@ -110,6 +118,7 @@ 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 (); @@ -117,6 +126,7 @@ void PdSharpening::write (ProcParams* pp, ParamsEdited* pedited) 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 (); @@ -182,26 +192,6 @@ void PdSharpening::enabledChanged () } } -void PdSharpening::adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) -{ -} - -void PdSharpening::adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) -{ -} - -void PdSharpening::adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) -{ -} - -void PdSharpening::adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) -{ -} - -void PdSharpening::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) -{ -} - void PdSharpening::setBatchMode (bool batchMode) { @@ -232,3 +222,43 @@ void PdSharpening::trimValues (rtengine::procparams::ProcParams* pp) 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 (a == contrast) { + 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 index fe9359349..af09a9b6b 100644 --- a/rtgui/pdsharpening.h +++ b/rtgui/pdsharpening.h @@ -20,10 +20,9 @@ #include #include "adjuster.h" -#include "thresholdadjuster.h" #include "toolpanel.h" -class PdSharpening : public ToolParamBlock, public ThresholdAdjusterListener, public AdjusterListener, public FoldableToolPanel +class PdSharpening : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener { protected: @@ -32,11 +31,14 @@ protected: Adjuster* dradius; Adjuster* diter; Gtk::VBox* rld; - + bool lastAutoContrast; rtengine::ProcEvent EvPdShrContrast; rtengine::ProcEvent EvPdShrDRadius; rtengine::ProcEvent EvPdSharpenGamma; rtengine::ProcEvent EvPdShrDIterations; + rtengine::ProcEvent EvPdShrAutoContrast; + IdleRegister idle_register; + public: PdSharpening (); @@ -47,14 +49,11 @@ public: 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 adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override; - void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override; - void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override; - void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override; - void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; + void autoContrastChanged (double autoContrast) override; void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool amountadd, bool dampingadd, bool iteradd, bool edgetoladd, bool haloctrladd); void trimValues (rtengine::procparams::ProcParams* pp) override; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 93f45aefe..f64205219 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -572,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); From ce3ca966f6c8a5e11cd7a4cbeecf40a505a46d22 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 18 Aug 2019 11:16:53 +0200 Subject: [PATCH 05/21] Capture sharpening: some cleanups --- rtengine/rawimagesource.cc | 9 +- rtgui/batchtoolpanelcoord.cc | 1 + rtgui/pdsharpening.cc | 207 ++++++++++++++++------------------- rtgui/pdsharpening.h | 21 ++-- 4 files changed, 113 insertions(+), 125 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index d84c0436a..ed0fac707 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -4992,13 +4992,14 @@ BENCHFUN array2D& Y = red; // red will be overridden anyway => we can use its buffer to store Y array2D& Cb = green; // green will be overridden anyway => we can use its buffer to store Cb array2D& Cr = blue; // blue will be overridden anyway => we can use its buffer to store Cr - const float gamma = sharpeningParams.gamma; StopWatch Stop1("rgb2Y"); +#ifdef _OPENMP #pragma omp parallel for +#endif for (int i = 0; i < H; ++i) { Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - Color::RGB2YCbCr(red[i], green[i], blue[i], Y[i], Cb[i], Cr[i], gamma, W); + Color::RGB2YCbCr(red[i], green[i], blue[i], Y[i], Cb[i], Cr[i], sharpeningParams.gamma, W); } // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); @@ -5011,9 +5012,11 @@ BENCHFUN ImProcFunctions ipf(&dummy); ipf.deconvsharpening(Y, tmp, blend, W, H, sharpeningParams, 1.0); StopWatch Stop2("Y2RGB"); +#ifdef _OPENMP #pragma omp parallel for +#endif for (int i = 0; i < H; ++i) { - Color::YCbCr2RGB(Y[i], Cb[i], Cr[i], red[i], green[i], blue[i], gamma, W); + Color::YCbCr2RGB(Y[i], Cb[i], Cr[i], red[i], green[i], blue[i], sharpeningParams.gamma, W); } Stop2.stop(); } diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index 1a0eaeb28..62867996e 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/pdsharpening.cc b/rtgui/pdsharpening.cc index 43b32ce7e..1151684e7 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * 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 @@ -15,7 +15,8 @@ * * 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" @@ -25,7 +26,7 @@ using namespace rtengine; using namespace rtengine::procparams; -PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PDSHARPENING_LABEL"), false, true) +PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDSHARPENING_LABEL"), false, true) { auto m = ProcEventMapper::getInstance(); @@ -35,177 +36,164 @@ PdSharpening::PdSharpening () : FoldableToolPanel(this, "pdsharpening", M("TP_PD EvPdShrDIterations = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); EvPdShrAutoContrast = m->newEvent(DEMOSAIC, "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); + 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); + pack_start(*hb); - rld = new Gtk::VBox (); - gamma = Gtk::manage (new Adjuster (M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.35)); - dradius = Gtk::manage (new Adjuster (M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); - diter = Gtk::manage (new Adjuster (M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); - rld->pack_start (*gamma); - rld->pack_start (*dradius); - rld->pack_start (*diter); + Gtk::VBox* rld = Gtk::manage(new Gtk::VBox()); + gamma = Gtk::manage(new Adjuster(M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.35)); + dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); + diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); + rld->pack_start(*gamma); + rld->pack_start(*dradius); + rld->pack_start(*diter); gamma->show(); - dradius->show (); - diter->show (); - rld->show (); + dradius->show(); + diter->show(); + rld->show(); pack_start(*rld); - dradius->setAdjusterListener (this); - gamma->setAdjusterListener (this); - diter->setAdjusterListener (this); + dradius->setAdjusterListener(this); + gamma->setAdjusterListener(this); + diter->setAdjusterListener(this); - if (contrast->delay < options.adjusterMaxDelay) { - contrast->delay = options.adjusterMaxDelay; - } - if (dradius->delay < options.adjusterMaxDelay) { - dradius->delay = options.adjusterMaxDelay; - } - if (gamma->delay < options.adjusterMaxDelay) { - gamma->delay = options.adjusterMaxDelay; - } - if (diter->delay < options.adjusterMaxDelay) { - diter->delay = options.adjusterMaxDelay; - } - - rld->reference(); + 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 () +PdSharpening::~PdSharpening() { idle_register.destroy(); - delete rld; } -void PdSharpening::read (const ProcParams* pp, const ParamsEdited* pedited) +void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited) { - disableListener (); + 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); + 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); + 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); + 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 (); + enableListener(); } -void PdSharpening::write (ProcParams* pp, ParamsEdited* pedited) +void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited) { - pp->pdsharpening.contrast = contrast->getValue (); + 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 (); + 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(); + 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) +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); + 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); + 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); + contrast->setDefaultEditedState(Irrelevant); + gamma->setDefaultEditedState(Irrelevant); + dradius->setDefaultEditedState(Irrelevant); + diter->setDefaultEditedState(Irrelevant); } } -void PdSharpening::adjusterChanged (Adjuster* a, double newval) +void PdSharpening::adjusterChanged(Adjuster* a, double newval) { - if (listener && (multiImage || getEnabled()) ) { + 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()); + costr = Glib::ustring::format(std::setw(3), std::fixed, std::setprecision(2), a->getValue()); } else { - costr = Glib::ustring::format ((int)a->getValue()); + costr = Glib::ustring::format((int)a->getValue()); } if (a == contrast) { - listener->panelChanged (EvPdShrContrast, costr); + listener->panelChanged(EvPdShrContrast, costr); } else if (a == gamma) { - listener->panelChanged (EvPdSharpenGamma, costr); + listener->panelChanged(EvPdSharpenGamma, costr); } else if (a == dradius) { - listener->panelChanged (EvPdShrDRadius, costr); + listener->panelChanged(EvPdShrDRadius, costr); } else if (a == diter) { - listener->panelChanged (EvPdShrDIterations, costr); + listener->panelChanged(EvPdShrDIterations, costr); } } } -void PdSharpening::enabledChanged () +void PdSharpening::enabledChanged() { if (listener) { if (get_inconsistent()) { - listener->panelChanged (EvPdShrEnabled, M("GENERAL_UNCHANGED")); + listener->panelChanged(EvPdShrEnabled, M("GENERAL_UNCHANGED")); } else if (getEnabled()) { - listener->panelChanged (EvPdShrEnabled, M("GENERAL_ENABLED")); + listener->panelChanged(EvPdShrEnabled, M("GENERAL_ENABLED")); } else { - listener->panelChanged (EvPdShrEnabled, M("GENERAL_DISABLED")); + listener->panelChanged(EvPdShrEnabled, M("GENERAL_DISABLED")); } } } -void PdSharpening::setBatchMode (bool batchMode) +void PdSharpening::setBatchMode(bool batchMode) { - ToolPanel::setBatchMode (batchMode); + ToolPanel::setBatchMode(batchMode); -// pack_start (*rld); - - contrast->showEditedCB (); - gamma->showEditedCB (); - dradius->showEditedCB (); - diter->showEditedCB (); + contrast->showEditedCB(); + gamma->showEditedCB(); + dradius->showEditedCB(); + diter->showEditedCB(); } -void PdSharpening::setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool amountadd, bool dampingadd, bool iteradd, bool edgetoladd, bool haloctrladd) +void PdSharpening::setAdjusterBehavior(bool contrastadd, bool gammaadd, bool radiusadd, bool iteradd) { contrast->setAddMode(contrastadd); @@ -214,7 +202,7 @@ void PdSharpening::setAdjusterBehavior (bool contrastadd, bool gammaadd, bool ra diter->setAddMode(iteradd); } -void PdSharpening::trimValues (rtengine::procparams::ProcParams* pp) +void PdSharpening::trimValues(rtengine::procparams::ProcParams* pp) { contrast->trimValue(pp->pdsharpening.contrast); @@ -223,7 +211,7 @@ void PdSharpening::trimValues (rtengine::procparams::ProcParams* pp) diter->trimValue(pp->pdsharpening.deconviter); } -void PdSharpening::autoContrastChanged (double autoContrast) +void PdSharpening::autoContrastChanged(double autoContrast) { idle_register.add( [this, autoContrast]() -> bool @@ -240,25 +228,22 @@ void PdSharpening::adjusterAutoToggled(Adjuster* a, bool newval) { if (multiImage) { if (contrast->getAutoInconsistent()) { - contrast->setAutoInconsistent (false); - contrast->setAutoValue (false); + contrast->setAutoInconsistent(false); + contrast->setAutoValue(false); } else if (lastAutoContrast) { - contrast->setAutoInconsistent (true); + contrast->setAutoInconsistent(true); } lastAutoContrast = contrast->getAutoValue(); } if (listener) { - - if (a == contrast) { - 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")); - } + 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 index af09a9b6b..b2f7b6e57 100644 --- a/rtgui/pdsharpening.h +++ b/rtgui/pdsharpening.h @@ -15,14 +15,13 @@ * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . - */ +*/ #pragma once -#include #include "adjuster.h" #include "toolpanel.h" -class PdSharpening : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener +class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener { protected: @@ -30,7 +29,7 @@ protected: Adjuster* gamma; Adjuster* dradius; Adjuster* diter; - Gtk::VBox* rld; + bool lastAutoContrast; rtengine::ProcEvent EvPdShrContrast; rtengine::ProcEvent EvPdShrDRadius; @@ -44,17 +43,17 @@ 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 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 enabledChanged () override; void autoContrastChanged (double autoContrast) override; - void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool amountadd, bool dampingadd, bool iteradd, bool edgetoladd, bool haloctrladd); - void trimValues (rtengine::procparams::ProcParams* pp) override; + void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool iteradds); + void trimValues (rtengine::procparams::ProcParams* pp) override; }; From dab39dae76888c71b05e3cfdd382a71f8848f33a Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Mon, 19 Aug 2019 21:17:25 +0200 Subject: [PATCH 06/21] Capture Sharpening: avoid hue shift --- rtengine/color.cc | 16 ++++++++-------- rtengine/color.h | 36 ++++++++++++++++++++++++------------ rtengine/rawimagesource.cc | 25 ++++++++++++++++++------- rtengine/rt_algo.cc | 8 ++++---- rtengine/rt_algo.h | 2 +- rtgui/pdsharpening.cc | 2 +- 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index 7c12c0ca5..26c2cf7c9 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 7b198f284..7cc7368b3 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1804,12 +1804,6 @@ public: return (hr); } - static inline void RGB2YCbCr(float R, float G, float B, float &Y, float &Cb, float &Cr) { - Y = 0.2627f * R + 0.6780f * G + 0.0593f * B; - Cb = (1.f - 0.0593f) * B - (0.2627f * R + 0.6780f * G); - Cr = (1.f - 0.2627f) * R - (0.6780f * G + 0.0593f * B); - } - static inline void RGB2YCbCr(float* R, float* G, float* B, float* Y, float* Cb, float *Cr, float gamma, int W) { gamma = 1.f / gamma; int i = 0; @@ -1839,12 +1833,6 @@ public: } } - static inline void YCbCr2RGB(float Y, float Cb, float Cr, float &R, float &G, float &B) { - R = std::max(Y + Cr, 0.f); - G = std::max(Y - (0.0593f / 0.6780f) * Cb - (0.2627f / 0.6780f) * Cr, 0.f); - B = std::max(Y + Cb, 0.f); - } - static inline void YCbCr2RGB(float* Y, float* Cb, float* Cr, float* R, float* G, float* B, float gamma, int W) { int i = 0; #ifdef __SSE2__ @@ -1870,6 +1858,30 @@ public: B[i] = std::max(y + cb, 0.f); } } + + static inline void RGB2Y(float* R, float* G, float* B, float* Y, 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); + STVFU(Y[i], pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav)); + } +#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); + Y[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma); + } + } + }; } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 050b62692..d947d4195 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -4987,17 +4987,22 @@ BENCHFUN } array2D L(W,H); - array2D& Y = red; // red will be overridden anyway => we can use its buffer to store Y - array2D& Cb = green; // green will be overridden anyway => we can use its buffer to store Cb - array2D& Cr = blue; // blue will be overridden anyway => we can use its buffer to store Cr + array2D YOld(W,H); + array2D YNew(W,H); +// array2D& Y = red; // red will be overridden anyway => we can use its buffer to store Y +// array2D& Cb = green; // green will be overridden anyway => we can use its buffer to store Cb +// array2D& Cr = blue; // blue will be overridden anyway => we can use its buffer to store Cr StopWatch Stop1("rgb2Y"); #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic, 16) #endif for (int i = 0; i < H; ++i) { Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - Color::RGB2YCbCr(red[i], green[i], blue[i], Y[i], Cb[i], Cr[i], sharpeningParams.gamma, W); + Color::RGB2Y(red[i], green[i], blue[i], YOld[i], sharpeningParams.gamma, W); + for (int j = 0; j < W; ++j) { + YNew[i][j] = YOld[i][j]; + } } // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); @@ -5008,13 +5013,19 @@ BENCHFUN array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer ProcParams dummy; ImProcFunctions ipf(&dummy); - ipf.deconvsharpening(Y, tmp, blend, W, H, sharpeningParams, 1.0); + ipf.deconvsharpening(YNew, tmp, blend, W, H, sharpeningParams, 1.0); StopWatch Stop2("Y2RGB"); + const float gamma = sharpeningParams.gamma; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H; ++i) { - Color::YCbCr2RGB(Y[i], Cb[i], Cr[i], red[i], green[i], blue[i], sharpeningParams.gamma, W); + for (int j = 0; j < W; ++j) { + const float factor = pow_F(YNew[i][j] / (YOld[i][j] == 0.f ? 0.00001f : YOld[i][j]), gamma); + red[i][j] *= factor; + green[i][j] *= factor; + blue[i][j] *= factor; + } } Stop2.stop(); } diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc index 1011ae7b7..97c44dfc1 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 a8e2e3e23..a13f3cb8e 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/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 1151684e7..7a428806f 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -49,7 +49,7 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS pack_start(*hb); Gtk::VBox* rld = Gtk::manage(new Gtk::VBox()); - gamma = Gtk::manage(new Adjuster(M("TP_SHARPENING_GAMMA"), 0.5, 3.0, 0.05, 1.35)); + gamma = Gtk::manage(new Adjuster(M("TP_SHARPENING_GAMMA"), 0.5, 6.0, 0.05, 1.35)); dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); rld->pack_start(*gamma); From 0c1caf6c3658f8230eb16d0e1ba4ccac892463eb Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Tue, 20 Aug 2019 18:41:06 +0200 Subject: [PATCH 07/21] capture sharpening: further improvements and speedups --- rtengine/color.h | 25 ++++ rtengine/gauss.cc | 269 ++++++++++++++++++++++++++++++++++++- rtengine/improcfun.h | 2 +- rtengine/ipsharpen.cc | 2 +- rtengine/rawimagesource.cc | 27 ++-- 5 files changed, 307 insertions(+), 18 deletions(-) diff --git a/rtengine/color.h b/rtengine/color.h index 7cc7368b3..789bf27c6 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1882,6 +1882,31 @@ public: } } + 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 b7de67851..171ed84e8 100644 --- a/rtengine/gauss.cc +++ b/rtengine/gauss.cc @@ -17,14 +17,48 @@ * 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) { + kernel[i + 3][j + 3] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); + sum += kernel[i + 3][j + 3]; + } + } + + 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) { + kernel[i + 2][j + 2] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); + sum += kernel[i + 2][j + 2]; + } + } + + 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 +241,219 @@ 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); + +#ifdef __SSE2__ + vfloat kernelv[7][7] ALIGNED16; + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 7; ++j) { + kernelv[i][j] = F2V(kernel[i][j]); + } + } + const vfloat onev = F2V(1.f); +#endif + +#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; + int j = 3; +#ifdef __SSE2__ + for (; j < W - 6; j += 4) { + vfloat valv = ZEROV; + for (int k = -3; k <= 3; ++k) { + for (int l = -3; l <= 3; ++l) { + valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 3][l + 3]); + } + } + STVFU(dst[i][j], vmaxf(LVFU(divBuffer[i][j]) / (vself(vmaskf_gt(valv, ZEROV), valv, onev)), ZEROV)); + } +#endif + for (; j < W - 3; ++j) { + float val = 0; + for (int k = -3; k <= 3; ++k) { + for (int l = -3; l <= 3; ++l) { + val += src[i + k][j + l] * kernel[k + 3][l + 3]; + } + } + dst[i][j] = rtengine::max(divBuffer[i][j] / (val > 0.f ? val : 1.f), 0.f); + } + 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); + +#ifdef __SSE2__ + vfloat kernelv[5][5] ALIGNED16; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + kernelv[i][j] = F2V(kernel[i][j]); + } + } + const vfloat onev = F2V(1.f); +#endif + +#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; + int j = 2; +#ifdef __SSE2__ + for (; j < W - 5; j += 4) { + vfloat valv = ZEROV; + for (int k = -2; k <= 2; ++k) { + for (int l = -2; l <= 2; ++l) { + valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 2][l + 2]); + } + } + STVFU(dst[i][j], vmaxf(LVFU(divBuffer[i][j]) / (vself(vmaskf_gt(valv, ZEROV), valv, onev)), ZEROV)); + } +#endif + for (; j < W - 2; ++j) { + float val = 0; + for (int k = -2; k <= 2; ++k) { + for (int l = -2; l <= 2; ++l) { + val += src[i + k][j + l] * kernel[k + 2][l + 2]; + } + } + dst[i][j] = rtengine::max(divBuffer[i][j] / (val > 0.f ? val : 1.f), 0.f); + } + 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); +#ifdef __SSE2__ + vfloat kernelv[7][7] ALIGNED16; + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 7; ++j) { + kernelv[i][j] = F2V(kernel[i][j]); + } + } +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + + for (int i = 3; i < H - 3; ++i) { + int j = 3; +#ifdef __SSE2__ + for (; j < W - 6; j += 4) { + vfloat valv = ZEROV; + for (int k = -3; k <= 3; ++k) { + for (int l = -3; l <= 3; ++l) { + valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 3][l + 3]); + } + } + STVFU(dst[i][j], LVFU(dst[i][j]) * valv); + } +#endif + for (; j < W - 3; ++j) { + float val = 0; + for (int k = -3; k <= 3; ++k) { + for (int l = -3; l <= 3; ++l) { + val += src[i + k][j + l] * kernel[k + 3][l + 3]; + } + } + 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); +#ifdef __SSE2__ + vfloat kernelv[5][5] ALIGNED16; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + kernelv[i][j] = F2V(kernel[i][j]); + } + } +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + + for (int i = 2; i < H - 2; ++i) { + int j = 2; +#ifdef __SSE2__ + for (; j < W - 5; j += 4) { + vfloat valv = ZEROV; + for (int k = -2; k <= 2; ++k) { + for (int l = -2; l <= 2; ++l) { + valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 2][l + 2]); + } + } + STVFU(dst[i][j], LVFU(dst[i][j]) * valv); + } +#endif + for (; j < W - 2; ++j) { + float val = 0; + for (int k = -2; k <= 2; ++k) { + for (int l = -2; l <= 2; ++l) { + val += src[i + k][j + l] * kernel[k + 2][l + 2]; + } + } + 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) @@ -1241,14 +1488,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 < 0.84 && src != dst) { + gauss5x5mult(src, dst, W, H, sigma); + } else if (sigma < 1.15f && 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 < 0.84f && src != dst) { + gauss5x5div (src, dst, buffer2, W, H, sigma); + } else if (sigma < 1.15f && src != dst) { + gauss7x7div (src, dst, buffer2, W, H, sigma); + } else { + gaussHorizontalSse (src, dst, W, H, sigma); + gaussVerticalSsediv (dst, dst, buffer2, W, H, sigma); + } break; } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 6fe8a785d..5cd65cad8 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, float** blend, int W, int H, const procparams::SharpeningParams &sharpenParam, double Scale); + 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 897aaf7b5..1d3d6375e 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, float ** blend, int W, int H, const SharpeningParams &sharpenParam, double Scale) +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; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index d947d4195..0602c770b 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -4989,20 +4989,14 @@ BENCHFUN array2D L(W,H); array2D YOld(W,H); array2D YNew(W,H); -// array2D& Y = red; // red will be overridden anyway => we can use its buffer to store Y -// array2D& Cb = green; // green will be overridden anyway => we can use its buffer to store Cb -// array2D& Cr = blue; // blue will be overridden anyway => we can use its buffer to store Cr - StopWatch Stop1("rgb2Y"); + StopWatch Stop1("rgb2YL"); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) #endif for (int i = 0; i < H; ++i) { Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - Color::RGB2Y(red[i], green[i], blue[i], YOld[i], sharpeningParams.gamma, W); - for (int j = 0; j < W; ++j) { - YNew[i][j] = YOld[i][j]; - } + Color::RGB2Y(red[i], green[i], blue[i], YOld[i], YNew[i], sharpeningParams.gamma, W); } // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); @@ -5017,11 +5011,22 @@ BENCHFUN StopWatch Stop2("Y2RGB"); const float gamma = sharpeningParams.gamma; #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic, 16) #endif for (int i = 0; i < H; ++i) { - for (int j = 0; j < W; ++j) { - const float factor = pow_F(YNew[i][j] / (YOld[i][j] == 0.f ? 0.00001f : YOld[i][j]), gamma); + int j = 0; +#ifdef __SSE2__ + const vfloat gammav = F2V(gamma); + for (; j < W - 3; j += 4) { + const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); + STVFU(red[i][j], LVFU(red[i][j]) * factor); + STVFU(green[i][j], LVFU(green[i][j]) * factor); + STVFU(blue[i][j], LVFU(blue[i][j]) * factor); + } + +#endif + for (; j < W; ++j) { + const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); red[i][j] *= factor; green[i][j] *= factor; blue[i][j] *= factor; From a0f95fe9e6f53ce9f9e653b6283b291518f560ae Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 21 Aug 2019 17:29:59 +0200 Subject: [PATCH 08/21] Speedup for gauss5x5 and gauss7x7 --- rtengine/gauss.cc | 337 +++++++++++++++++++++++++++++----------------- 1 file changed, 213 insertions(+), 124 deletions(-) diff --git a/rtengine/gauss.cc b/rtengine/gauss.cc index 171ed84e8..5665394cc 100644 --- a/rtengine/gauss.cc +++ b/rtengine/gauss.cc @@ -30,8 +30,12 @@ void compute7x7kernel(float sigma, float kernel[7][7]) { float sum = 0.f; for (int i = -3; i <= 3; ++i) { for (int j = -3; j <= 3; ++j) { - kernel[i + 3][j + 3] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); - sum += kernel[i + 3][j + 3]; + 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; + } } } @@ -47,8 +51,12 @@ void compute5x5kernel(float sigma, float kernel[5][5]) { float sum = 0.f; for (int i = -2; i <= 2; ++i) { for (int j = -2; j <= 2; ++j) { - kernel[i + 2][j + 2] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp); - sum += kernel[i + 2][j + 2]; + 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; + } } } @@ -247,15 +255,14 @@ template void gauss7x7div (T** RESTRICT src, T** RESTRICT dst, T** REST float kernel[7][7]; compute7x7kernel(sigma, kernel); -#ifdef __SSE2__ - vfloat kernelv[7][7] ALIGNED16; - for (int i = 0; i < 7; ++i) { - for (int j = 0; j < 7; ++j) { - kernelv[i][j] = F2V(kernel[i][j]); - } - } - const vfloat onev = F2V(1.f); -#endif + 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 @@ -263,26 +270,54 @@ template void gauss7x7div (T** RESTRICT src, T** RESTRICT dst, T** REST for (int i = 3; i < H - 3; ++i) { dst[i][0] = dst[i][1] = dst[i][2] = 1.f; - int j = 3; -#ifdef __SSE2__ - for (; j < W - 6; j += 4) { - vfloat valv = ZEROV; - for (int k = -3; k <= 3; ++k) { - for (int l = -3; l <= 3; ++l) { - valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 3][l + 3]); - } - } - STVFU(dst[i][j], vmaxf(LVFU(divBuffer[i][j]) / (vself(vmaskf_gt(valv, ZEROV), valv, onev)), ZEROV)); - } -#endif - for (; j < W - 3; ++j) { - float val = 0; - for (int k = -3; k <= 3; ++k) { - for (int l = -3; l <= 3; ++l) { - val += src[i + k][j + l] * kernel[k + 3][l + 3]; - } - } - dst[i][j] = rtengine::max(divBuffer[i][j] / (val > 0.f ? val : 1.f), 0.f); + // I tried hand written SSE code but gcc vectorizes better + for (int j = 3; j < W - 3; ++j) { + float val = src[i - 3][j - 1] * c31; + val += src[i - 3][j - 0] * c30; + val += src[i - 3][j + 1] * c31; + + val += src[i - 2][j - 2] * c22; + val += src[i - 2][j - 1] * c21; + val += src[i - 2][j - 0] * c20; + val += src[i - 2][j + 1] * c21; + val += src[i - 2][j + 2] * c22; + + val += src[i - 1][j - 3] * c31; + val += src[i - 1][j - 2] * c21; + val += src[i - 1][j - 1] * c11; + val += src[i - 1][j - 0] * c10; + val += src[i - 1][j + 1] * c11; + val += src[i - 1][j + 2] * c21; + val += src[i - 1][j + 3] * c31; + + val += src[i][j - 3] * c30; + val += src[i][j - 2] * c20; + val += src[i][j - 1] * c10; + val += src[i][j - 0] * c00; + val += src[i][j + 1] * c10; + val += src[i][j + 2] * c20; + val += src[i][j + 3] * c30; + + val += src[i + 1][j - 3] * c31; + val += src[i + 1][j - 2] * c21; + val += src[i + 1][j - 1] * c11; + val += src[i + 1][j - 0] * c10; + val += src[i + 1][j + 1] * c11; + val += src[i + 1][j + 2] * c21; + val += src[i + 1][j + 3] * c31; + + val += src[i + 2][j - 2] * c22; + val += src[i + 2][j - 1] * c21; + val += src[i + 2][j - 0] * c20; + val += src[i + 2][j + 1] * c21; + val += src[i + 2][j + 2] * c22; + + val += src[i + 3][j - 1] * c31; + val += src[i + 3][j - 0] * c30; + val += src[i + 3][j + 1] * c31; + + val = val > 0.f ? val : 1.f; + dst[i][j] = std::max(divBuffer[i][j] / val, 0.f); } dst[i][W - 3] = dst[i][W - 2] = dst[i][W - 1] = 1.f; } @@ -311,15 +346,11 @@ template void gauss5x5div (T** RESTRICT src, T** RESTRICT dst, T** REST float kernel[5][5]; compute5x5kernel(sigma, kernel); -#ifdef __SSE2__ - vfloat kernelv[5][5] ALIGNED16; - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 5; ++j) { - kernelv[i][j] = F2V(kernel[i][j]); - } - } - const vfloat onev = F2V(1.f); -#endif + 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 @@ -327,26 +358,36 @@ template void gauss5x5div (T** RESTRICT src, T** RESTRICT dst, T** REST for (int i = 2; i < H - 2; ++i) { dst[i][0] = dst[i][1] = 1.f; - int j = 2; -#ifdef __SSE2__ - for (; j < W - 5; j += 4) { - vfloat valv = ZEROV; - for (int k = -2; k <= 2; ++k) { - for (int l = -2; l <= 2; ++l) { - valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 2][l + 2]); - } - } - STVFU(dst[i][j], vmaxf(LVFU(divBuffer[i][j]) / (vself(vmaskf_gt(valv, ZEROV), valv, onev)), ZEROV)); - } -#endif - for (; j < W - 2; ++j) { - float val = 0; - for (int k = -2; k <= 2; ++k) { - for (int l = -2; l <= 2; ++l) { - val += src[i + k][j + l] * kernel[k + 2][l + 2]; - } - } - dst[i][j] = rtengine::max(divBuffer[i][j] / (val > 0.f ? val : 1.f), 0.f); + // I tried hand written SSE code but gcc vectorizes better + for (int j = 2; j < W - 2; ++j) { + float val = src[i - 2][j - 1] * c21; + val += src[i - 2][j - 0] * c20; + val += src[i - 2][j + 1] * c21; + + val += src[i - 1][j - 2] * c21; + val += src[i - 1][j - 1] * c11; + val += src[i - 1][j - 0] * c10; + val += src[i - 1][j + 1] * c11; + val += src[i - 1][j + 2] * c21; + + val += src[i - 0][j - 2] * c20; + val += src[i - 0][j - 1] * c10; + val += src[i - 0][j - 0] * c00; + val += src[i - 0][j + 1] * c10; + val += src[i - 0][j + 2] * c20; + + val += src[i + 1][j - 2] * c21; + val += src[i + 1][j - 1] * c11; + val += src[i + 1][j - 0] * c10; + val += src[i + 1][j + 1] * c11; + val += src[i + 1][j + 2] * c21; + + val += src[i + 2][j - 1] * c21; + val += src[i + 2][j - 0] * c20; + val += src[i + 2][j + 1] * c21; + + val = val > 0.f ? val : 1.f; + dst[i][j] = std::max(divBuffer[i][j] / val, 0.f); } dst[i][W - 2] = dst[i][W - 1] = 1.f; } @@ -374,39 +415,66 @@ template void gauss7x7mult (T** RESTRICT src, T** RESTRICT dst, const i float kernel[7][7]; compute7x7kernel(sigma, kernel); -#ifdef __SSE2__ - vfloat kernelv[7][7] ALIGNED16; - for (int i = 0; i < 7; ++i) { - for (int j = 0; j < 7; ++j) { - kernelv[i][j] = F2V(kernel[i][j]); - } - } -#endif + 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) { - int j = 3; -#ifdef __SSE2__ - for (; j < W - 6; j += 4) { - vfloat valv = ZEROV; - for (int k = -3; k <= 3; ++k) { - for (int l = -3; l <= 3; ++l) { - valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 3][l + 3]); - } - } - STVFU(dst[i][j], LVFU(dst[i][j]) * valv); - } -#endif - for (; j < W - 3; ++j) { - float val = 0; - for (int k = -3; k <= 3; ++k) { - for (int l = -3; l <= 3; ++l) { - val += src[i + k][j + l] * kernel[k + 3][l + 3]; - } - } + // I tried hand written SSE code but gcc vectorizes better + for (int j = 3; j < W - 3; ++j) { + float val = src[i - 3][j - 1] * c31; + val += src[i - 3][j - 0] * c30; + val += src[i - 3][j + 1] * c31; + + val += src[i - 2][j - 2] * c22; + val += src[i - 2][j - 1] * c21; + val += src[i - 2][j - 0] * c20; + val += src[i - 2][j + 1] * c21; + val += src[i - 2][j + 2] * c22; + + val += src[i - 1][j - 3] * c31; + val += src[i - 1][j - 2] * c21; + val += src[i - 1][j - 1] * c11; + val += src[i - 1][j - 0] * c10; + val += src[i - 1][j + 1] * c11; + val += src[i - 1][j + 2] * c21; + val += src[i - 1][j + 3] * c31; + + val += src[i][j - 3] * c30; + val += src[i][j - 2] * c20; + val += src[i][j - 1] * c10; + val += src[i][j - 0] * c00; + val += src[i][j + 1] * c10; + val += src[i][j + 2] * c20; + val += src[i][j + 3] * c30; + + val += src[i + 1][j - 3] * c31; + val += src[i + 1][j - 2] * c21; + val += src[i + 1][j - 1] * c11; + val += src[i + 1][j - 0] * c10; + val += src[i + 1][j + 1] * c11; + val += src[i + 1][j + 2] * c21; + val += src[i + 1][j + 3] * c31; + + val += src[i + 2][j - 2] * c22; + val += src[i + 2][j - 1] * c21; + val += src[i + 2][j - 0] * c20; + val += src[i + 2][j + 1] * c21; + val += src[i + 2][j + 2] * c22; + + val += src[i + 3][j - 1] * c31; + val += src[i + 3][j - 0] * c30; + val += src[i + 3][j + 1] * c31; + dst[i][j] *= val; } } @@ -417,39 +485,46 @@ template void gauss5x5mult (T** RESTRICT src, T** RESTRICT dst, const i float kernel[5][5]; compute5x5kernel(sigma, kernel); -#ifdef __SSE2__ - vfloat kernelv[5][5] ALIGNED16; - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 5; ++j) { - kernelv[i][j] = F2V(kernel[i][j]); - } - } -#endif + + 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) { - int j = 2; -#ifdef __SSE2__ - for (; j < W - 5; j += 4) { - vfloat valv = ZEROV; - for (int k = -2; k <= 2; ++k) { - for (int l = -2; l <= 2; ++l) { - valv += LVFU(src[i + k][j + l]) * LVF(kernelv[k + 2][l + 2]); - } - } - STVFU(dst[i][j], LVFU(dst[i][j]) * valv); - } -#endif - for (; j < W - 2; ++j) { - float val = 0; - for (int k = -2; k <= 2; ++k) { - for (int l = -2; l <= 2; ++l) { - val += src[i + k][j + l] * kernel[k + 2][l + 2]; - } - } + // I tried hand written SSE code but gcc vectorizes better + for (int j = 2; j < W - 2; ++j) { + float val = src[i - 2][j - 1] * c21; + val += src[i - 2][j - 0] * c20; + val += src[i - 2][j + 1] * c21; + + val += src[i - 1][j - 2] * c21; + val += src[i - 1][j - 1] * c11; + val += src[i - 1][j - 0] * c10; + val += src[i - 1][j + 1] * c11; + val += src[i - 1][j + 2] * c21; + + val += src[i - 0][j - 2] * c20; + val += src[i - 0][j - 1] * c10; + val += src[i - 0][j - 0] * c00; + val += src[i - 0][j + 1] * c10; + val += src[i - 0][j + 2] * c20; + + val += src[i + 1][j - 2] * c21; + val += src[i + 1][j - 1] * c11; + val += src[i + 1][j - 0] * c10; + val += src[i + 1][j + 1] * c11; + val += src[i + 1][j + 2] * c21; + + val += src[i + 2][j - 1] * c21; + val += src[i + 2][j - 0] * c20; + val += src[i + 2][j + 1] * c21; + dst[i][j] *= val; } } @@ -1390,6 +1465,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) { @@ -1488,9 +1565,9 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int if (sigma < GAUSS_DOUBLE) { switch (gausstype) { case GAUSS_MULT : { - if (sigma < 0.84 && src != dst) { + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { gauss5x5mult(src, dst, W, H, sigma); - } else if (sigma < 1.15f && src != dst) { + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { gauss7x7mult(src, dst, W, H, sigma); } else { gaussHorizontalSse (src, src, W, H, sigma); @@ -1500,9 +1577,9 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int } case GAUSS_DIV : { - if (sigma < 0.84f && src != dst) { + if (sigma <= GAUSS_5X5_LIMIT && src != dst) { gauss5x5div (src, dst, buffer2, W, H, sigma); - } else if (sigma < 1.15f && src != dst) { + } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) { gauss7x7div (src, dst, buffer2, W, H, sigma); } else { gaussHorizontalSse (src, dst, W, H, sigma); @@ -1527,14 +1604,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; } From d4ffcde1a3416ff96a6ac845577d77001e7b4b81 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 21 Aug 2019 21:22:22 +0200 Subject: [PATCH 09/21] Capture sharpening: reduce upper limit of radius adjuster to 1.0 --- rtgui/pdsharpening.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 7a428806f..5c14c052f 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -50,7 +50,7 @@ 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.35)); - dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 2.5, 0.01, 0.75)); + dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 1.0, 0.01, 0.75)); diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); rld->pack_start(*gamma); rld->pack_start(*dradius); From cb7b31f3a4c760e305174d7b7ad5d9e609a41878 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 24 Aug 2019 12:14:54 +0200 Subject: [PATCH 10/21] Allow Capture Sharpening for monochrome raw files --- rtgui/toolpanelcoord.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index f64205219..0bf976c2b 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -344,7 +344,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt preprocess->FoldableToolPanel::hide(); flatfield->FoldableToolPanel::show(); filmNegative->FoldableToolPanel::hide(); - pdSharpening->FoldableToolPanel::hide(); + pdSharpening->FoldableToolPanel::show(); retinex->FoldableToolPanel::setGrayedOut(false); return false; From bca760c8baa7ca9298f85f4ddc8980c3aa8ee1c7 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 24 Aug 2019 20:52:36 +0200 Subject: [PATCH 11/21] Capture sharpening: removed unused code, also small speedup for 5x5 and 7x7 gauss --- rtengine/color.h | 78 --------------------- rtengine/gauss.cc | 168 ++++++++-------------------------------------- 2 files changed, 28 insertions(+), 218 deletions(-) diff --git a/rtengine/color.h b/rtengine/color.h index 789bf27c6..d36a7db8e 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -1804,84 +1804,6 @@ public: return (hr); } - static inline void RGB2YCbCr(float* R, float* G, float* B, float* Y, float* Cb, float *Cr, 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); - const vfloat c4v = F2V(1.f - 0.0593f); - const vfloat c5v = F2V(1.f - 0.2627f); - 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); - STVFU(Y[i], pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav)); - STVFU(Cb[i], c4v * Bv - (c1v * Rv + c2v * Gv)); - STVFU(Cr[i], c5v * Rv - (c2v * Gv + c3v * Bv)); - } -#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); - Y[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma); - Cb[i] = (1.f - 0.0593f) * b - (0.2627f * r + 0.6780f * g); - Cr[i] = (1.f - 0.2627f) * r - (0.6780f * g + 0.0593f * b); - } - } - - static inline void YCbCr2RGB(float* Y, float* Cb, float* Cr, float* R, float* G, float* B, float gamma, int W) { - int i = 0; -#ifdef __SSE2__ - const vfloat gammav = F2V(gamma); - const vfloat c1v = F2V(0.0593f / 0.6780f); - const vfloat c2v = F2V(0.2627f / 0.6780f); - - for (; i < W - 3; i += 4) { - const vfloat Yv = pow_F(LVFU(Y[i]), gammav); - const vfloat Crv = LVFU(Cr[i]); - const vfloat Cbv = LVFU(Cb[i]); - STVFU(R[i], vmaxf(Yv + Crv, ZEROV)); - STVFU(G[i], vmaxf(Yv - c1v * Cbv - c2v * Crv, ZEROV)); - STVFU(B[i], vmaxf(Yv + Cbv, ZEROV)); - } -#endif - for (; i < W; ++i) { - const float y = pow_F(Y[i], gamma); - const float cr = Cr[i]; - const float cb = Cb[i]; - R[i] = std::max(y + cr, 0.f); - G[i] = std::max(y - (0.0593f / 0.6780f) * cb - (0.2627f / 0.6780f) * cr, 0.f); - B[i] = std::max(y + cb, 0.f); - } - } - - static inline void RGB2Y(float* R, float* G, float* B, float* Y, 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); - STVFU(Y[i], pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav)); - } -#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); - Y[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma); - } - } - 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; diff --git a/rtengine/gauss.cc b/rtengine/gauss.cc index 5665394cc..dfb7cb225 100644 --- a/rtengine/gauss.cc +++ b/rtengine/gauss.cc @@ -272,52 +272,16 @@ template void gauss7x7div (T** RESTRICT src, T** RESTRICT dst, T** REST 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) { - float val = src[i - 3][j - 1] * c31; - val += src[i - 3][j - 0] * c30; - val += src[i - 3][j + 1] * c31; + 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]; - val += src[i - 2][j - 2] * c22; - val += src[i - 2][j - 1] * c21; - val += src[i - 2][j - 0] * c20; - val += src[i - 2][j + 1] * c21; - val += src[i - 2][j + 2] * c22; - - val += src[i - 1][j - 3] * c31; - val += src[i - 1][j - 2] * c21; - val += src[i - 1][j - 1] * c11; - val += src[i - 1][j - 0] * c10; - val += src[i - 1][j + 1] * c11; - val += src[i - 1][j + 2] * c21; - val += src[i - 1][j + 3] * c31; - - val += src[i][j - 3] * c30; - val += src[i][j - 2] * c20; - val += src[i][j - 1] * c10; - val += src[i][j - 0] * c00; - val += src[i][j + 1] * c10; - val += src[i][j + 2] * c20; - val += src[i][j + 3] * c30; - - val += src[i + 1][j - 3] * c31; - val += src[i + 1][j - 2] * c21; - val += src[i + 1][j - 1] * c11; - val += src[i + 1][j - 0] * c10; - val += src[i + 1][j + 1] * c11; - val += src[i + 1][j + 2] * c21; - val += src[i + 1][j + 3] * c31; - - val += src[i + 2][j - 2] * c22; - val += src[i + 2][j - 1] * c21; - val += src[i + 2][j - 0] * c20; - val += src[i + 2][j + 1] * c21; - val += src[i + 2][j + 2] * c22; - - val += src[i + 3][j - 1] * c31; - val += src[i + 3][j - 0] * c30; - val += src[i + 3][j + 1] * c31; - - val = val > 0.f ? val : 1.f; - dst[i][j] = std::max(divBuffer[i][j] / val, 0.f); + 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; } @@ -360,34 +324,13 @@ template void gauss5x5div (T** RESTRICT src, T** RESTRICT dst, T** REST 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) { - float val = src[i - 2][j - 1] * c21; - val += src[i - 2][j - 0] * c20; - val += src[i - 2][j + 1] * c21; + 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]; - val += src[i - 1][j - 2] * c21; - val += src[i - 1][j - 1] * c11; - val += src[i - 1][j - 0] * c10; - val += src[i - 1][j + 1] * c11; - val += src[i - 1][j + 2] * c21; - - val += src[i - 0][j - 2] * c20; - val += src[i - 0][j - 1] * c10; - val += src[i - 0][j - 0] * c00; - val += src[i - 0][j + 1] * c10; - val += src[i - 0][j + 2] * c20; - - val += src[i + 1][j - 2] * c21; - val += src[i + 1][j - 1] * c11; - val += src[i + 1][j - 0] * c10; - val += src[i + 1][j + 1] * c11; - val += src[i + 1][j + 2] * c21; - - val += src[i + 2][j - 1] * c21; - val += src[i + 2][j - 0] * c20; - val += src[i + 2][j + 1] * c21; - - val = val > 0.f ? val : 1.f; - dst[i][j] = std::max(divBuffer[i][j] / val, 0.f); + dst[i][j] = divBuffer[i][j] / std::max(val, 0.00001f); } dst[i][W - 2] = dst[i][W - 1] = 1.f; } @@ -431,49 +374,14 @@ template void gauss7x7mult (T** RESTRICT src, T** RESTRICT dst, const i 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) { - float val = src[i - 3][j - 1] * c31; - val += src[i - 3][j - 0] * c30; - val += src[i - 3][j + 1] * c31; - - val += src[i - 2][j - 2] * c22; - val += src[i - 2][j - 1] * c21; - val += src[i - 2][j - 0] * c20; - val += src[i - 2][j + 1] * c21; - val += src[i - 2][j + 2] * c22; - - val += src[i - 1][j - 3] * c31; - val += src[i - 1][j - 2] * c21; - val += src[i - 1][j - 1] * c11; - val += src[i - 1][j - 0] * c10; - val += src[i - 1][j + 1] * c11; - val += src[i - 1][j + 2] * c21; - val += src[i - 1][j + 3] * c31; - - val += src[i][j - 3] * c30; - val += src[i][j - 2] * c20; - val += src[i][j - 1] * c10; - val += src[i][j - 0] * c00; - val += src[i][j + 1] * c10; - val += src[i][j + 2] * c20; - val += src[i][j + 3] * c30; - - val += src[i + 1][j - 3] * c31; - val += src[i + 1][j - 2] * c21; - val += src[i + 1][j - 1] * c11; - val += src[i + 1][j - 0] * c10; - val += src[i + 1][j + 1] * c11; - val += src[i + 1][j + 2] * c21; - val += src[i + 1][j + 3] * c31; - - val += src[i + 2][j - 2] * c22; - val += src[i + 2][j - 1] * c21; - val += src[i + 2][j - 0] * c20; - val += src[i + 2][j + 1] * c21; - val += src[i + 2][j + 2] * c22; - - val += src[i + 3][j - 1] * c31; - val += src[i + 3][j - 0] * c30; - val += src[i + 3][j + 1] * c31; + 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; } @@ -499,31 +407,11 @@ template void gauss5x5mult (T** RESTRICT src, T** RESTRICT dst, const i 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) { - float val = src[i - 2][j - 1] * c21; - val += src[i - 2][j - 0] * c20; - val += src[i - 2][j + 1] * c21; - - val += src[i - 1][j - 2] * c21; - val += src[i - 1][j - 1] * c11; - val += src[i - 1][j - 0] * c10; - val += src[i - 1][j + 1] * c11; - val += src[i - 1][j + 2] * c21; - - val += src[i - 0][j - 2] * c20; - val += src[i - 0][j - 1] * c10; - val += src[i - 0][j - 0] * c00; - val += src[i - 0][j + 1] * c10; - val += src[i - 0][j + 2] * c20; - - val += src[i + 1][j - 2] * c21; - val += src[i + 1][j - 1] * c11; - val += src[i + 1][j - 0] * c10; - val += src[i + 1][j + 1] * c11; - val += src[i + 1][j + 2] * c21; - - val += src[i + 2][j - 1] * c21; - val += src[i + 2][j - 0] * c20; - val += src[i + 2][j + 1] * c21; + 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; } From ba8c3d15bf5e553e3f28c5b323ca1a2793b082f0 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 28 Aug 2019 18:03:31 +0200 Subject: [PATCH 12/21] capture sharpening: do not trigger demosaic when changing adjusters --- rtengine/imagesource.h | 2 +- rtengine/improccoordinator.cc | 27 +++++---- rtengine/rawimagesource.cc | 109 ++++++++++++++++++++++++++++++---- rtengine/rawimagesource.h | 8 ++- rtengine/simpleprocess.cc | 2 +- rtgui/pdsharpening.cc | 10 ++-- 6 files changed, 128 insertions(+), 30 deletions(-) diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 46cbc28a6..1a5469744 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -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) {}; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 7d485fd19..7496cb2aa 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -332,26 +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(contrastThreshold); } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) { xtransAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); } - - if (params->pdsharpening.enabled) { - double pdSharpencontrastThreshold = params->pdsharpening.contrast; - imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold); - if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) { - pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold); - } - } - - // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag todo |= M_INIT; + } + + if ((todo & M_INIT) && 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 { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 0602c770b..82863267f 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -45,6 +45,7 @@ #include #endif #include "opthelper.h" +#include "../rtgui/multilangmgr.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #undef CLIPD #define CLIPD(a) ((a)>0.0f?((a)<1.0f?(a):1.0f):0.0f) @@ -457,6 +458,9 @@ RawImageSource::RawImageSource () , green(0, 0) , red(0, 0) , blue(0, 0) + , greenCache(nullptr) + , redCache(nullptr) + , blueCache(nullptr) , rawDirty(true) , histMatchingParams(new procparams::ColorManagementParams) { @@ -474,6 +478,9 @@ RawImageSource::~RawImageSource () { delete idata; + delete redCache; + delete greenCache; + delete blueCache; for(size_t i = 0; i < numFrames; ++i) { delete riFrames[i]; @@ -1524,7 +1531,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(); @@ -1595,7 +1602,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)); @@ -4954,6 +5003,12 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { BENCHFUN + + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.0); + } + const float xyz_rgb[3][3] = { // XYZ from RGB { 0.412453, 0.357580, 0.180423 }, { 0.212671, 0.715160, 0.072169 }, @@ -4962,6 +5017,10 @@ BENCHFUN 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 @@ -4970,10 +5029,18 @@ BENCHFUN #endif for (int i = 0; i < H; ++i) { - Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); + Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); + } + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + 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->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.2); + } conrastThreshold = contrast * 100.f; #ifdef _OPENMP #pragma omp parallel for @@ -4983,6 +5050,10 @@ BENCHFUN red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f; } } + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(1.0); + } return; } @@ -4995,12 +5066,20 @@ BENCHFUN #pragma omp parallel for schedule(dynamic, 16) #endif for (int i = 0; i < H; ++i) { - Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - Color::RGB2Y(red[i], green[i], blue[i], YOld[i], YNew[i], sharpeningParams.gamma, W); + 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->setProgressStr(M("TP_PDSHARPENING_LABEL")); + 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->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.2); + } conrastThreshold = contrast * 100.f; Stop1.stop(); @@ -5008,6 +5087,10 @@ BENCHFUN ProcParams dummy; ImProcFunctions ipf(&dummy); ipf.deconvsharpening(YNew, tmp, blend, W, H, sharpeningParams, 1.0); + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.9); + } StopWatch Stop2("Y2RGB"); const float gamma = sharpeningParams.gamma; #ifdef _OPENMP @@ -5019,20 +5102,24 @@ BENCHFUN const vfloat gammav = F2V(gamma); for (; j < W - 3; j += 4) { const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); - STVFU(red[i][j], LVFU(red[i][j]) * factor); - STVFU(green[i][j], LVFU(green[i][j]) * factor); - STVFU(blue[i][j], LVFU(blue[i][j]) * factor); + STVFU(red[i][j], LVFU(redVals[i][j]) * factor); + STVFU(green[i][j], LVFU(greenVals[i][j]) * factor); + STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor); } #endif for (; j < W; ++j) { const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); - red[i][j] *= factor; - green[i][j] *= factor; - blue[i][j] *= factor; + red[i][j] = redVals[i][j] * factor; + green[i][j] = greenVals[i][j] * factor; + blue[i][j] = blueVals[i][j] * factor; } } Stop2.stop(); + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(1.0); + } } void RawImageSource::cleanup () diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 8cf6b08a9..bbc15c448 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; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 0524734e0..1751a7cea 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -220,7 +220,7 @@ 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); } diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 5c14c052f..8e7c0e8b8 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -30,11 +30,11 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS { auto m = ProcEventMapper::getInstance(); - EvPdShrContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_CONTRAST"); - EvPdSharpenGamma = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_GAMMA"); - EvPdShrDRadius = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_RADIUS"); - EvPdShrDIterations = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); - EvPdShrAutoContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST"); + EvPdShrContrast = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_CONTRAST"); + EvPdSharpenGamma = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_GAMMA"); + EvPdShrDRadius = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_RADIUS"); + EvPdShrDIterations = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); + EvPdShrAutoContrast = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST"); Gtk::HBox* hb = Gtk::manage(new Gtk::HBox()); hb->show(); From d3ec7f52773864d21c39c6ca5b930e576360dc6e Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 28 Aug 2019 19:26:09 +0200 Subject: [PATCH 13/21] capture sharpening: Do not trigger demosaic when toggling mask preview, #5412 --- rtengine/improccoordinator.cc | 2 +- rtengine/procevents.h | 1 + rtengine/rawimagesource.cc | 7 ------- rtengine/refreshmap.cc | 5 +++-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 7496cb2aa..4b255d663 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1364,7 +1364,7 @@ ProcEvent ImProcCoordinator::setSharpMask (bool sharpMask) if (this->sharpMask != sharpMask) { sharpMaskChanged = true; this->sharpMask = sharpMask; - return params->pdsharpening.enabled ? rtengine::EvPdShrEnabled : rtengine::EvShrEnabled; + return params->pdsharpening.enabled ? rtengine::EvPdShrMaskToggled : rtengine::EvShrEnabled; } else { sharpMaskChanged = false; return rtengine::EvShrEnabled; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 670c0c102..d8491e533 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -521,6 +521,7 @@ enum ProcEventCode { EvRGBEnabled = 491, EvLEnabled = 492, EvPdShrEnabled = 493, + EvPdShrMaskToggled = 494, NUMOFEVENTS diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 82863267f..487bb1959 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5032,13 +5032,11 @@ BENCHFUN Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); } if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); 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->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(0.2); } conrastThreshold = contrast * 100.f; @@ -5051,7 +5049,6 @@ BENCHFUN } } if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(1.0); } return; @@ -5070,14 +5067,12 @@ BENCHFUN Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W); } if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); 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->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(0.2); } conrastThreshold = contrast * 100.f; @@ -5088,7 +5083,6 @@ BENCHFUN ImProcFunctions ipf(&dummy); ipf.deconvsharpening(YNew, tmp, blend, W, H, sharpeningParams, 1.0); if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(0.9); } StopWatch Stop2("Y2RGB"); @@ -5117,7 +5111,6 @@ BENCHFUN } Stop2.stop(); if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(1.0); } } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 8282f5b4b..56db1f71b 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -519,8 +519,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { HDR, // EvTMFattalAmount ALLNORAW, // EvWBEnabled RGBCURVE, // EvRGBEnabled - LUMINANCECURVE, // EvLEnabled - DEMOSAIC // EvPdShrEnabled + LUMINANCECURVE, // EvLEnabled + DEMOSAIC, // EvPdShrEnabled + ALLNORAW // EvPdShrMaskToggled }; From cca484f6c9251d4200c8c81e28e36e29fed3f469 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 28 Aug 2019 19:59:55 +0200 Subject: [PATCH 14/21] capture sharpening: reduce memory usage in preview mode, #5412 --- rtengine/rawimagesource.cc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 487bb1959..2b45bfe38 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5054,9 +5054,23 @@ BENCHFUN return; } - array2D L(W,H); - array2D YOld(W,H); - array2D YNew(W,H); + 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 @@ -5110,6 +5124,9 @@ BENCHFUN } } Stop2.stop(); + delete Lbuffer; + delete YOldbuffer; + delete YNewbuffer; if (plistener) { plistener->setProgress(1.0); } From b72d4e27eb935e2894933016587a1bd386b5539c Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 29 Aug 2019 13:02:54 +0200 Subject: [PATCH 15/21] capture sharpening: early exit if Autocontrast is changed from true to false, #5412 --- rtengine/rawimagesource.cc | 7 ++++++- rtengine/rawimagesource.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 2b45bfe38..3cab9b6b1 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -461,6 +461,7 @@ RawImageSource::RawImageSource () , greenCache(nullptr) , redCache(nullptr) , blueCache(nullptr) + , captureSharpeningAutoContrast(false) , rawDirty(true) , histMatchingParams(new procparams::ColorManagementParams) { @@ -5003,7 +5004,11 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { BENCHFUN - + if (!sharpeningParams.autoContrast && captureSharpeningAutoContrast) { + captureSharpeningAutoContrast = false; + return; + } + captureSharpeningAutoContrast = sharpeningParams.autoContrast; if (plistener) { plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(0.0); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index bbc15c448..cebedd32f 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -94,6 +94,7 @@ protected: array2D* redCache; // the interpolated blue plane: array2D* blueCache; + bool captureSharpeningAutoContrast; bool rawDirty; float psRedBrightness[4]; float psGreenBrightness[4]; From 205db936f9a3f5556e442e2a57820ca3ecea0e3f Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 29 Aug 2019 13:58:46 +0200 Subject: [PATCH 16/21] Revert "capture sharpening: early exit if Autocontrast is changed from true to false, #5412" This reverts commit b72d4e27eb935e2894933016587a1bd386b5539c. --- rtengine/rawimagesource.cc | 7 +------ rtengine/rawimagesource.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 3cab9b6b1..2b45bfe38 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -461,7 +461,6 @@ RawImageSource::RawImageSource () , greenCache(nullptr) , redCache(nullptr) , blueCache(nullptr) - , captureSharpeningAutoContrast(false) , rawDirty(true) , histMatchingParams(new procparams::ColorManagementParams) { @@ -5004,11 +5003,7 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { BENCHFUN - if (!sharpeningParams.autoContrast && captureSharpeningAutoContrast) { - captureSharpeningAutoContrast = false; - return; - } - captureSharpeningAutoContrast = sharpeningParams.autoContrast; + if (plistener) { plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); plistener->setProgress(0.0); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index cebedd32f..bbc15c448 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -94,7 +94,6 @@ protected: array2D* redCache; // the interpolated blue plane: array2D* blueCache; - bool captureSharpeningAutoContrast; bool rawDirty; float psRedBrightness[4]; float psGreenBrightness[4]; From 48bcf9e71a1348a15f91bd92285638edd97bd93e Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 29 Aug 2019 22:46:09 +0200 Subject: [PATCH 17/21] Fix broken export when filmnegative was used on xtrans files --- rtengine/simpleprocess.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 1751a7cea..b14b4435d 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -210,7 +210,7 @@ private: imgsrc->preprocess ( params.raw, params.lensProf, params.coarse, params.dirpyrDenoise.enabled); // After preprocess, run film negative processing if enabled - if (imgsrc->getSensorType() == ST_BAYER && params.filmNegative.enabled) { + if ((imgsrc->getSensorType() == ST_BAYER || (imgsrc->getSensorType() == ST_FUJI_XTRANS)) && params.filmNegative.enabled) { imgsrc->filmNegativeProcess (params.filmNegative); } From f882309f594444abcce2df5eee6f8c8969822e06 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Fri, 30 Aug 2019 14:45:45 +0200 Subject: [PATCH 18/21] capture sharpening: own compilation unit, smooth progressbar, #5412 --- rtengine/CMakeLists.txt | 1 + rtengine/capturesharpening.cc | 218 ++++++++++++++++++++++++++++++++++ rtengine/imagesource.h | 4 +- rtengine/procparams.cc | 32 ++++- rtengine/procparams.h | 16 ++- rtengine/rawimagesource.cc | 138 +-------------------- rtengine/rawimagesource.h | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/paramsedited.cc | 7 +- rtgui/paramsedited.h | 11 +- 10 files changed, 277 insertions(+), 154 deletions(-) create mode 100644 rtengine/capturesharpening.cc diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index c5c8afa82..f52cfa256 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -32,6 +32,7 @@ set(CAMCONSTSFILE "camconst.json") set(RTENGINESOURCEFILES badpixels.cc CA_correct_RT.cc + capturesharpening.cc EdgePreservingDecomposition.cc FTblockDN.cc PF_correct_RT.cc diff --git a/rtengine/capturesharpening.cc b/rtengine/capturesharpening.cc new file mode 100644 index 000000000..71cefaca6 --- /dev/null +++ b/rtengine/capturesharpening.cc @@ -0,0 +1,218 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Ingo Weyrich (heckflosse67@gmx.de) + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . +*/ +#include +#include + +#include "jaggedarray.h" +#include "rtengine.h" +#include "rawimagesource.h" +#include "rt_math.h" +#include "improcfun.h" +#include "procparams.h" +#include "color.h" +#include "gauss.h" +#include "rt_algo.h" +#define BENCHMARK +#include "StopWatch.h" +#ifdef _OPENMP +#include +#endif +#include "opthelper.h" +#include "../rtgui/multilangmgr.h" + +namespace { +void CaptureDeconvSharpening (float** luminance, float** tmp, const float * const * blend, int W, int H, double sigma, int iterations, rtengine::ProgressListener* plistener, double start, double step) +{ + + rtengine::JaggedArray tmpI(W, H); + +#ifdef _OPENMP + #pragma omp parallel +#endif + { +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H; i++) { + for(int j = 0; j < W; j++) { + tmpI[i][j] = max(luminance[i][j], 0.f); + } + } + + for (int k = 0; k < iterations; k++) { + // apply gaussian blur and divide luminance by result of gaussian blur + gaussianBlur(tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance); + gaussianBlur(tmp, tmpI, W, H, sigma, nullptr, GAUSS_MULT); + if (plistener) { +#ifdef _OPENMP + #pragma omp single +#endif + start += step; + plistener->setProgress(start); + } + } // end for + +#ifdef _OPENMP + #pragma omp for +#endif + + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + luminance[i][j] = rtengine::intp(blend[i][j], max(tmpI[i][j], 0.0f), luminance[i][j]); + } + } + } // end parallel +} + +} + +namespace rtengine +{ + +void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { +BENCHFUN + + + if (plistener) { + plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); + plistener->setProgress(0.0); + } + + const float xyz_rgb[3][3] = { // XYZ from RGB + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } + }; + + float contrast = conrastThreshold / 100.f; + + array2D& redVals = redCache ? *redCache : red; + array2D& greenVals = greenCache ? *greenCache : green; + array2D& blueVals = blueCache ? *blueCache : blue; + + if (showMask) { + StopWatch Stop1("Show mask"); + array2D& L = blue; // blue will be overridden anyway => we can use its buffer to store L +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H; ++i) { + Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); + } + if (plistener) { + plistener->setProgress(0.1); + } + array2D& blend = red; // red will be overridden anyway => we can use its buffer to store the blend mask + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + if (plistener) { + plistener->setProgress(0.2); + } + conrastThreshold = contrast * 100.f; +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f; + } + } + if (plistener) { + plistener->setProgress(1.0); + } + return; + } + + array2D* Lbuffer = nullptr; + if (!redCache) { + Lbuffer = new array2D(W, H); + } + + array2D* YOldbuffer = nullptr; + if (!greenCache) { + YOldbuffer = new array2D(W, H); + } + + array2D* YNewbuffer = nullptr; + if (!blueCache) { + YNewbuffer = new array2D(W, H); + } + array2D& L = Lbuffer ? *Lbuffer : red; + array2D& YOld = YOldbuffer ? * YOldbuffer : green; + array2D& YNew = YNewbuffer ? * YNewbuffer : blue; + + StopWatch Stop1("rgb2YL"); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int i = 0; i < H; ++i) { + Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); + Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W); + } + if (plistener) { + plistener->setProgress(0.1); + } + // calculate contrast based blend factors to reduce sharpening in regions with low contrast + JaggedArray blend(W, H); + buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); + if (plistener) { + plistener->setProgress(0.2); + } + conrastThreshold = contrast * 100.f; + + Stop1.stop(); + array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer + CaptureDeconvSharpening(YNew, tmp, blend, W, H, sharpeningParams.deconvradius, sharpeningParams.deconviter, plistener, 0.2, (0.9 - 0.2) / sharpeningParams.deconviter); + if (plistener) { + plistener->setProgress(0.9); + } + StopWatch Stop2("Y2RGB"); + const float gamma = sharpeningParams.gamma; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) +#endif + for (int i = 0; i < H; ++i) { + int j = 0; +#ifdef __SSE2__ + const vfloat gammav = F2V(gamma); + for (; j < W - 3; j += 4) { + const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); + STVFU(red[i][j], LVFU(redVals[i][j]) * factor); + STVFU(green[i][j], LVFU(greenVals[i][j]) * factor); + STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor); + } + +#endif + for (; j < W; ++j) { + const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); + red[i][j] = redVals[i][j] * factor; + green[i][j] = greenVals[i][j] * factor; + blue[i][j] = blueVals[i][j] * factor; + } + } + Stop2.stop(); + delete Lbuffer; + delete YOldbuffer; + delete YNewbuffer; + if (plistener) { + plistener->setProgress(1.0); + } +} + +} /* namespace */ diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 1a5469744..64bb53a49 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -46,7 +46,7 @@ struct LensProfParams; struct RAWParams; struct RetinexParams; struct ToneCurveParams; -struct SharpeningParams; +struct CaptureSharpeningParams; } class ImageMatrices @@ -182,7 +182,7 @@ public: return this; } virtual void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) = 0; - virtual void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; + virtual void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0; }; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 60b95a2e8..d88892a3b 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1149,6 +1149,32 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const return !(*this == other); } +CaptureSharpeningParams::CaptureSharpeningParams() : + enabled(false), + autoContrast(true), + contrast(10.0), + gamma(1.35), + deconvradius(0.75), + deconviter(30) +{ +} + +bool CaptureSharpeningParams::operator ==(const CaptureSharpeningParams& other) const +{ + return + enabled == other.enabled + && contrast == other.contrast + && gamma == other.gamma + && autoContrast == other.autoContrast + && deconvradius == other.deconvradius + && deconviter == other.deconviter; +} + +bool CaptureSharpeningParams::operator !=(const CaptureSharpeningParams& other) const +{ + return !(*this == other); +} + SharpenEdgeParams::SharpenEdgeParams() : enabled(false), passes(2), @@ -2830,12 +2856,6 @@ void ProcParams::setDefaults() prsharpening.deconvdamping = 0; pdsharpening = {}; - pdsharpening.contrast = 10.0; - pdsharpening.autoContrast = true; - prsharpening.method = "rld"; - pdsharpening.gamma = 1.35; - pdsharpening.deconvradius = 0.75; - pdsharpening.deconviter = 30; vibrance = {}; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 4be6de254..cbcec0e18 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -542,6 +542,20 @@ struct SharpenMicroParams { bool operator !=(const SharpenMicroParams& other) const; }; +struct CaptureSharpeningParams { + bool enabled; + bool autoContrast; + double contrast; + double gamma; + double deconvradius; + int deconviter; + + CaptureSharpeningParams(); + + bool operator ==(const CaptureSharpeningParams& other) const; + bool operator !=(const CaptureSharpeningParams& other) const; +}; + /** * Parameters of the vibrance */ @@ -1530,7 +1544,7 @@ public: ColorToningParams colorToning; ///< Color Toning parameters SharpeningParams sharpening; ///< Sharpening parameters SharpeningParams prsharpening; ///< Sharpening parameters for post resize sharpening - SharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening + CaptureSharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening SharpenEdgeParams sharpenEdge; ///< Sharpen edge parameters SharpenMicroParams sharpenMicro; ///< Sharpen microcontrast parameters VibranceParams vibrance; ///< Vibrance parameters diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 2b45bfe38..931a46ede 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -38,14 +38,12 @@ #include "camconst.h" #include "procparams.h" #include "color.h" -#include "rt_algo.h" -#define BENCHMARK -#include "StopWatch.h" +//#define BENCHMARK +//#include "StopWatch.h" #ifdef _OPENMP #include #endif #include "opthelper.h" -#include "../rtgui/multilangmgr.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #undef CLIPD #define CLIPD(a) ((a)>0.0f?((a)<1.0f?(a):1.0f):0.0f) @@ -5000,138 +4998,6 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int } } -void RawImageSource::captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) { -BENCHFUN - - - if (plistener) { - plistener->setProgressStr(M("TP_PDSHARPENING_LABEL")); - plistener->setProgress(0.0); - } - - const float xyz_rgb[3][3] = { // XYZ from RGB - { 0.412453, 0.357580, 0.180423 }, - { 0.212671, 0.715160, 0.072169 }, - { 0.019334, 0.119193, 0.950227 } - }; - - float contrast = conrastThreshold / 100.f; - - array2D& redVals = redCache ? *redCache : red; - array2D& greenVals = greenCache ? *greenCache : green; - array2D& blueVals = blueCache ? *blueCache : blue; - - if (showMask) { - StopWatch Stop1("Show mask"); - array2D& L = blue; // blue will be overridden anyway => we can use its buffer to store L -#ifdef _OPENMP - #pragma omp parallel for -#endif - - for (int i = 0; i < H; ++i) { - Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); - } - if (plistener) { - plistener->setProgress(0.1); - } - array2D& blend = red; // red will be overridden anyway => we can use its buffer to store the blend mask - buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); - if (plistener) { - plistener->setProgress(0.2); - } - conrastThreshold = contrast * 100.f; -#ifdef _OPENMP - #pragma omp parallel for -#endif - for (int i = 0; i < H; ++i) { - for (int j = 0; j < W; ++j) { - red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f; - } - } - if (plistener) { - plistener->setProgress(1.0); - } - return; - } - - array2D* Lbuffer = nullptr; - if (!redCache) { - Lbuffer = new array2D(W, H); - } - - array2D* YOldbuffer = nullptr; - if (!greenCache) { - YOldbuffer = new array2D(W, H); - } - - array2D* YNewbuffer = nullptr; - if (!blueCache) { - YNewbuffer = new array2D(W, H); - } - array2D& L = Lbuffer ? *Lbuffer : red; - array2D& YOld = YOldbuffer ? * YOldbuffer : green; - array2D& YNew = YNewbuffer ? * YNewbuffer : blue; - - StopWatch Stop1("rgb2YL"); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) -#endif - for (int i = 0; i < H; ++i) { - Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W); - Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W); - } - if (plistener) { - plistener->setProgress(0.1); - } - // calculate contrast based blend factors to reduce sharpening in regions with low contrast - JaggedArray blend(W, H); - buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast); - if (plistener) { - plistener->setProgress(0.2); - } - conrastThreshold = contrast * 100.f; - - Stop1.stop(); - array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer - ProcParams dummy; - ImProcFunctions ipf(&dummy); - ipf.deconvsharpening(YNew, tmp, blend, W, H, sharpeningParams, 1.0); - if (plistener) { - plistener->setProgress(0.9); - } - StopWatch Stop2("Y2RGB"); - const float gamma = sharpeningParams.gamma; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) -#endif - for (int i = 0; i < H; ++i) { - int j = 0; -#ifdef __SSE2__ - const vfloat gammav = F2V(gamma); - for (; j < W - 3; j += 4) { - const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); - STVFU(red[i][j], LVFU(redVals[i][j]) * factor); - STVFU(green[i][j], LVFU(greenVals[i][j]) * factor); - STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor); - } - -#endif - for (; j < W; ++j) { - const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); - red[i][j] = redVals[i][j] * factor; - green[i][j] = greenVals[i][j] * factor; - blue[i][j] = blueVals[i][j] * factor; - } - } - Stop2.stop(); - delete Lbuffer; - delete YOldbuffer; - delete YNewbuffer; - if (plistener) { - plistener->setProgress(1.0); - } -} - void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index bbc15c448..5e1c9b424 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -311,7 +311,7 @@ protected: void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override; }; } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 175f664f8..6b4a67773 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -102,7 +102,7 @@ public: void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;} void flushRGB () override; - void captureSharpening(const procparams::SharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; + void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {}; }; } #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 4029d0b9b..db0c24123 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -169,7 +169,6 @@ void ParamsEdited::set(bool v) pdsharpening.contrast = v; pdsharpening.autoContrast = v; pdsharpening.gamma = v; - pdsharpening.deconvamount = v; pdsharpening.deconvradius = v; pdsharpening.deconviter = v; prsharpening.enabled = v; @@ -1733,10 +1732,6 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast; } - if (pdsharpening.deconvamount) { - toEdit.pdsharpening.deconvamount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.pdsharpening.deconvamount + mods.pdsharpening.deconvamount : mods.pdsharpening.deconvamount; - } - if (pdsharpening.gamma) { toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma; } @@ -3292,7 +3287,7 @@ bool FilmNegativeParamsEdited::isUnchanged() const return enabled && redRatio && greenExp && blueRatio; } -bool SharpeningParamsEdited::isUnchanged() const +bool CaptureSharpeningParamsEdited::isUnchanged() const { return enabled && contrast && autoContrast && gamma && deconvradius && deconviter; } \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 6d229e689..c697aaacd 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -196,6 +196,15 @@ struct SharpeningParamsEdited { bool deconvradius; bool deconviter; bool deconvdamping; +}; + +struct CaptureSharpeningParamsEdited { + bool enabled; + bool contrast; + bool autoContrast; + bool gamma; + bool deconvradius; + bool deconviter; bool isUnchanged() const; }; @@ -687,7 +696,7 @@ struct ParamsEdited { ColorToningEdited colorToning; RetinexParamsEdited retinex; SharpeningParamsEdited sharpening; - SharpeningParamsEdited pdsharpening; + CaptureSharpeningParamsEdited pdsharpening; SharpeningParamsEdited prsharpening; SharpenEdgeParamsEdited sharpenEdge; SharpenMicroParamsEdited sharpenMicro; From fb51c4ca920f840d250938324f64ac06f828d3c5 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Tue, 3 Sep 2019 17:08:37 +0200 Subject: [PATCH 19/21] capture sharpening: Fix crash when using gamma < 1.0, #5412 --- rtengine/capturesharpening.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/capturesharpening.cc b/rtengine/capturesharpening.cc index 71cefaca6..eb95d2633 100644 --- a/rtengine/capturesharpening.cc +++ b/rtengine/capturesharpening.cc @@ -192,7 +192,7 @@ BENCHFUN #ifdef __SSE2__ const vfloat gammav = F2V(gamma); for (; j < W - 3; j += 4) { - const vfloat factor = pow_F(LVFU(YNew[i][j]) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav); + 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); @@ -200,7 +200,7 @@ BENCHFUN #endif for (; j < W; ++j) { - const float factor = pow_F(YNew[i][j] / std::max(YOld[i][j], 0.00001f), gamma); + 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; From 062b5a6bbc4333ab7d95d766e63e834835adc389 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 7 Sep 2019 16:16:28 +0200 Subject: [PATCH 20/21] Capture Sharpening: change default values for iterations and gamma --- rtengine/procparams.cc | 4 ++-- rtgui/pdsharpening.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index d88892a3b..4f31d7539 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1153,9 +1153,9 @@ CaptureSharpeningParams::CaptureSharpeningParams() : enabled(false), autoContrast(true), contrast(10.0), - gamma(1.35), + gamma(1.00), deconvradius(0.75), - deconviter(30) + deconviter(20) { } diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 8e7c0e8b8..85d174df8 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -49,9 +49,9 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS 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.35)); - dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 1.0, 0.01, 0.75)); - diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 5, 100, 1, 30)); + 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); From ec2f7778bbedcfbd6c70d7af74ebb150fae30ef2 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Mon, 9 Sep 2019 15:48:00 +0200 Subject: [PATCH 21/21] trigger capture sharpening less often --- rtengine/improccoordinator.cc | 4 ++-- rtengine/refreshmap.h | 2 ++ rtgui/pdsharpening.cc | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 10d484495..ee7d07b11 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -340,11 +340,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) 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_INIT) && params->pdsharpening.enabled) { + 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) { diff --git a/rtengine/refreshmap.h b/rtengine/refreshmap.h index b9ccc2b65..72d8dcadd 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/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc index 85d174df8..c85b42e95 100644 --- a/rtgui/pdsharpening.cc +++ b/rtgui/pdsharpening.cc @@ -30,11 +30,11 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS { auto m = ProcEventMapper::getInstance(); - EvPdShrContrast = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_CONTRAST"); - EvPdSharpenGamma = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_GAMMA"); - EvPdShrDRadius = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_RADIUS"); - EvPdShrDIterations = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_ITERATIONS"); - EvPdShrAutoContrast = m->newEvent(ALLNORAW, "HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST"); + 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();