From bd3bd809b5415b875c0a8d38c18cfb0c1cc2e34a Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 1 May 2022 16:13:27 -0700 Subject: [PATCH 1/9] Port tone equalizer from ART Merge with local adjustments tone equalizer image processing function for consistent results. To-do: Enable for batch editing and add pivot/colormap to the local adjustments version. --- rtdata/languages/default | 15 ++ rtengine/CMakeLists.txt | 1 + rtengine/imagefloat.cc | 68 ++++--- rtengine/imagefloat.h | 5 +- rtengine/improccoordinator.cc | 1 + rtengine/improcfun.cc | 6 + rtengine/improcfun.h | 4 +- rtengine/iplocallab.cc | 227 +-------------------- rtengine/iptoneequalizer.cc | 362 ++++++++++++++++++++++++++++++++++ rtengine/procparams.cc | 44 +++++ rtengine/procparams.h | 17 ++ rtgui/CMakeLists.txt | 1 + rtgui/adjuster.cc | 27 ++- rtgui/adjuster.h | 2 + rtgui/paramsedited.cc | 30 +++ rtgui/paramsedited.h | 9 + rtgui/partialpastedlg.cc | 9 + rtgui/partialpastedlg.h | 2 + rtgui/thumbnail.cc | 1 + rtgui/toneequalizer.cc | 169 ++++++++++++++++ rtgui/toneequalizer.h | 56 ++++++ rtgui/toolpanelcoord.cc | 2 + rtgui/toolpanelcoord.h | 2 + 23 files changed, 794 insertions(+), 266 deletions(-) create mode 100644 rtengine/iptoneequalizer.cc create mode 100644 rtgui/toneequalizer.cc create mode 100644 rtgui/toneequalizer.h diff --git a/rtdata/languages/default b/rtdata/languages/default index a3e99cfc2..6dd32ef7b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1520,6 +1520,11 @@ HISTORY_MSG_SPOT_ENTRY;Spot removal - Point modif. HISTORY_MSG_TEMPOUT;CAM02 automatic temperature HISTORY_MSG_THRESWAV;Balance threshold HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor +HISTORY_MSG_TONE_EQUALIZER_BANDS;Tone equalizer - Bands +HISTORY_MSG_TONE_EQUALIZER_ENABLED;Tone equalizer +HISTORY_MSG_TONE_EQUALIZER_PIVOT;Tone equalizer - Pivot +HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION;Tone equalizer - Regularization +HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP;Tone equalizer - Tonal map HISTORY_MSG_TRANS_METHOD;Geometry - Method HISTORY_MSG_WAVBALCHROM;Equalizer chrominance HISTORY_MSG_WAVBALLUM;Equalizer luminance @@ -1827,6 +1832,7 @@ PARTIALPASTE_SHARPENMICRO;Microcontrast PARTIALPASTE_SOFTLIGHT;Soft light PARTIALPASTE_SPOT;Spot removal PARTIALPASTE_TM_FATTAL;Dynamic range compression +PARTIALPASTE_TONE_EQUALIZER;Tone equalizer PARTIALPASTE_VIBRANCE;Vibrance PARTIALPASTE_VIGNETTING;Vignetting correction PARTIALPASTE_WHITEBALANCE;White balance @@ -3837,6 +3843,15 @@ TP_TM_FATTAL_AMOUNT;Amount TP_TM_FATTAL_ANCHOR;Anchor TP_TM_FATTAL_LABEL;Dynamic Range Compression TP_TM_FATTAL_THRESHOLD;Detail +TP_TONE_EQUALIZER_BAND_0;Blacks +TP_TONE_EQUALIZER_BAND_1;Shadows +TP_TONE_EQUALIZER_BAND_2;Midtones +TP_TONE_EQUALIZER_BAND_3;Highlights +TP_TONE_EQUALIZER_BAND_4;Whites +TP_TONE_EQUALIZER_DETAIL;Regularization +TP_TONE_EQUALIZER_LABEL;Tone Equalizer +TP_TONE_EQUALIZER_PIVOT;Pivot (Ev) +TP_TONE_EQUALIZER_SHOW_COLOR_MAP;Show tonal map TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL;Skin-tones diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 1f3cf352b..3cc8df625 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -133,6 +133,7 @@ set(RTENGINESOURCEFILES ipsharpen.cc ipsharpenedges.cc ipsoftlight.cc + iptoneequalizer.cc iptransform.cc ipvibrance.cc ipwavelet.cc diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index 7bc75fe6d..e4cb28e6b 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -341,6 +341,38 @@ void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* imag #endif } +// From ART. +void Imagefloat::multiply(float factor, bool multithread) +{ + const int W = width; + const int H = height; +#ifdef __SSE2__ + vfloat vfactor = F2V(factor); +#endif + +#ifdef _OPENMP +# pragma omp parallel for firstprivate(W, H) schedule(dynamic, 5) if (multithread) +#endif + for (int y = 0; y < H; y++) { + int x = 0; +#ifdef __SSE2__ + for (; x < W-3; x += 4) { + vfloat rv = LVF(r(y, x)); + vfloat gv = LVF(g(y, x)); + vfloat bv = LVF(b(y, x)); + STVF(r(y, x), rv * vfactor); + STVF(g(y, x), gv * vfactor); + STVF(b(y, x), bv * vfactor); + } +#endif + for (; x < W; ++x) { + r(y, x) *= factor; + g(y, x) *= factor; + b(y, x) *= factor; + } + } +} + void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal) { @@ -362,43 +394,15 @@ void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal) } // convert values's range to [0;1] ; this method assumes that the input values's range is [0;65535] -void Imagefloat::normalizeFloatTo1() +void Imagefloat::normalizeFloatTo1(bool multithread) { - - const int w = width; - const int h = height; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 5) -#endif - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - r(y, x) /= 65535.f; - g(y, x) /= 65535.f; - b(y, x) /= 65535.f; - } - } + multiply(1.f/65535.f, multithread); } // convert values's range to [0;65535 ; this method assumes that the input values's range is [0;1] -void Imagefloat::normalizeFloatTo65535() +void Imagefloat::normalizeFloatTo65535(bool multithread) { - - const int w = width; - const int h = height; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 5) -#endif - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - r(y, x) *= 65535.f; - g(y, x) *= 65535.f; - b(y, x) *= 65535.f; - } - } + multiply(65535.f, multithread); } // Parallelized transformation; create transform with cmsFLAGS_NOCACHE! diff --git a/rtengine/imagefloat.h b/rtengine/imagefloat.h index fc3ba318d..3bee52d9a 100644 --- a/rtengine/imagefloat.h +++ b/rtengine/imagefloat.h @@ -207,9 +207,10 @@ public: return (uint32_t) ((lsign << 31) | (exponent << 23) | mantissa); } + void multiply(float factor, bool multithread); void normalizeFloat(float srcMinVal, float srcMaxVal) override; - void normalizeFloatTo1(); - void normalizeFloatTo65535(); + void normalizeFloatTo1(bool multithread=true); + void normalizeFloatTo65535(bool multithread=true); void ExecCMSTransform(cmsHTRANSFORM hTransform); void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); }; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 33cce1842..cb9f6500f 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -2734,6 +2734,7 @@ void ImProcCoordinator::process() || params->epd != nextParams->epd || params->fattal != nextParams->fattal || params->sh != nextParams->sh + || params->toneEqualizer != nextParams->toneEqualizer || params->crop != nextParams->crop || params->coarse != nextParams->coarse || params->commonTrans != nextParams->commonTrans diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 15ebcc2e6..e50e28a01 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -2258,6 +2258,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // For tonecurve histogram const float lumimulf[3] = {static_cast(lumimul[0]), static_cast(lumimul[1]), static_cast(lumimul[2])}; + std::unique_ptr workimg_copy; + if (params->toneEqualizer.enabled) { + working = working->copy(); + workimg_copy.reset(working); + toneEqualizer(working); + } #define TS 112 diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 1be1d0371..70aed428c 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -97,6 +97,7 @@ struct LocalContrastParams; struct LocallabParams; struct SharpeningParams; struct SoftLightParams; +struct ToneEqualizerParams; struct VibranceParams; struct VignettingParams; struct WaveletParams; @@ -491,7 +492,8 @@ enum class BlurType { void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); //void shadowsHighlights(LabImage *lab); void shadowsHighlights(LabImage *lab, bool ena, int labmode, int hightli, int shado, int rad, int scal, int hltonal, int shtonal); - + bool toneEqualizer(Imagefloat *rgb); + static void toneEqualizer(array2D &R, array2D &G, array2D &B, const procparams::ToneEqualizerParams & params, const Glib::ustring &workingProfile, double scale, bool multithread, bool show_color_map); void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams); void labColorCorrectionRegions(LabImage *lab); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index ac87c75d7..7a427b0eb 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2258,230 +2258,11 @@ void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, } void tone_eq(array2D &R, array2D &G, array2D &B, const struct local_params & lp, const Glib::ustring &workingProfile, double scale, bool multithread) -// adapted from the tone equalizer of darktable -/* - Copyright 2019 Alberto Griggio - Small adaptation to Local Adjustment 10 2019 Jacques Desmis - This file is part of darktable, - copyright (c) 2018 Aurelien Pierre. - - darktable 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. - - darktable 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 darktable. If not, see . -*/ - { - // BENCHFUN - - const int W = R.getWidth(); - const int H = R.getHeight(); - array2D Y(W, H); - - const auto log2 = - [](float x) -> float { - static const float l2 = xlogf(2); - return xlogf(x) / l2; - }; - - const auto exp2 = - [](float x) -> float { - return pow_F(2.f, x); - }; - // Build the luma channels: band-pass filters with gaussian windows of - // std 2 EV, spaced by 2 EV - const float centers[12] = { - -18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f, - -4.0f, -2.0f, 0.0f, 2.0f, 4.0f - }; - - const auto conv = [&](int v, float lo, float hi) -> float { - const float f = v < 0 ? lo : hi; - return exp2(float(v) / 100.f * f); - }; - const float factors[12] = { - conv(lp.mullocsh[0], 2.f, 3.f), // -18 EV - conv(lp.mullocsh[0], 2.f, 3.f), // -16 EV - conv(lp.mullocsh[0], 2.f, 3.f), // -14 EV - conv(lp.mullocsh[0], 2.f, 3.f), // -12 EV - conv(lp.mullocsh[0], 2.f, 3.f), // -10 EV - conv(lp.mullocsh[0], 2.f, 3.f), // -8 EV - conv(lp.mullocsh[1], 2.f, 3.f), // -6 EV - conv(lp.mullocsh[2], 2.5f, 2.5f), // -4 EV - conv(lp.mullocsh[3], 3.f, 2.f), // -2 EV - conv(lp.mullocsh[4], 3.f, 2.f), // 0 EV - conv(lp.mullocsh[4], 3.f, 2.f), // 2 EV - conv(lp.mullocsh[4], 3.f, 2.f) // 4 EV - }; - - TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile); - -#ifdef _OPENMP - #pragma omp parallel for if (multithread) -#endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws); - } - } - - int detail = LIM(lp.detailsh + 5, 0, 5); - int radius = detail / scale + 0.5; - float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0); - - if (radius > 0) { - rtengine::guidedFilterLog(10.f, Y, radius, epsilon2, multithread); - } - - if (lp.detailsh > 0) { - array2D Y2(W, H); - constexpr float base_epsilon = 0.02f; - constexpr float base_posterization = 5.f; - -#ifdef _OPENMP - #pragma omp parallel for if (multithread) -#endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]); - float ll = round(l * base_posterization) / base_posterization; - Y2[y][x] = Y[y][x]; - Y[y][x] = exp2(ll); - } - } - - radius = 350.0 / scale; - epsilon2 = base_epsilon / float(6 - rtengine::min(lp.detailsh, 5)); - rtengine::guidedFilter(Y2, Y, Y, radius, epsilon2, multithread); - } - - const auto gauss = - [](float b, float x) -> float { - return xexpf((-SQR(x - b) / 4.0f)); - }; - - // For every pixel luminance, the sum of the gaussian masks - float w_sum = 0.f; - - for (int i = 0; i < 12; ++i) { - w_sum += gauss(centers[i], 0.f); - } - - const auto process_pixel = - [&](float y) -> float { - // convert to log space - const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); - - // build the correction as the sum of the contribution of each - // luminance channel to current pixel - float correction = 0.0f; - - for (int c = 0; c < 12; ++c) - { - correction += gauss(centers[c], luma) * factors[c]; - } - - correction /= w_sum; - - return correction; - }; - - LUTf lut(65536); - - for (int i = 0; i < 65536; ++i) { - float y = float(i) / 65535.f; - float c = process_pixel(y); - lut[i] = c; - } - - -#ifdef __SSE2__ - vfloat vfactors[12]; - vfloat vcenters[12]; - - for (int i = 0; i < 12; ++i) { - vfactors[i] = F2V(factors[i]); - vcenters[i] = F2V(centers[i]); - } - - const auto vgauss = - [](vfloat b, vfloat x) -> vfloat { - static const vfloat fourv = F2V(4.f); - return xexpf((-SQR(x - b) / fourv)); - }; - - vfloat zerov = F2V(0.f); - vfloat vw_sum = F2V(w_sum); - - const vfloat noisev = F2V(-18.f); - const vfloat xlog2v = F2V(xlogf(2.f)); - - const auto vprocess_pixel = - [&](vfloat y) -> vfloat { - const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev); - - vfloat correction = zerov; - - for (int c = 0; c < 12; ++c) - { - correction += vgauss(vcenters[c], luma) * vfactors[c]; - } - - correction /= vw_sum; - - return correction; - }; - - - vfloat v1 = F2V(1.f); - vfloat v65535 = F2V(65535.f); -#endif // __SSE2__ - - -#ifdef _OPENMP - #pragma omp parallel for if (multithread) -#endif - for (int y = 0; y < H; ++y) { - int x = 0; - - -#ifdef __SSE2__ - - for (; x < W - 3; x += 4) { - vfloat cY = LVFU(Y[y][x]); - vmask m = vmaskf_gt(cY, v1); - vfloat corr; - - if (_mm_movemask_ps((vfloat)m)) { - corr = vprocess_pixel(cY); - } else { - corr = lut[cY * v65535]; - } - - STVF(R[y][x], LVF(R[y][x]) * corr); - STVF(G[y][x], LVF(G[y][x]) * corr); - STVF(B[y][x], LVF(B[y][x]) * corr); - } - -#endif // __SSE2__ - - for (; x < W; ++x) { - float cY = Y[y][x]; - float corr = cY > 1.f ? process_pixel(cY) : lut[cY * 65535.f]; - R[y][x] *= corr; - G[y][x] *= corr; - B[y][x] *= corr; - } - } - + ToneEqualizerParams params; + params.regularization = lp.detailsh; + std::copy(lp.mullocsh, lp.mullocsh + params.bands.size(), params.bands.begin()); + ImProcFunctions::toneEqualizer(R, G, B, params, workingProfile, scale, multithread, false); } void ImProcFunctions::loccont(int bfw, int bfh, LabImage* tmp1, float rad, float stren, int sk) { diff --git a/rtengine/iptoneequalizer.cc b/rtengine/iptoneequalizer.cc new file mode 100644 index 000000000..c177b8f98 --- /dev/null +++ b/rtengine/iptoneequalizer.cc @@ -0,0 +1,362 @@ +#include "color.h" +#include "guidedfilter.h" +#include "iccstore.h" +#include "imagefloat.h" +#include "improcfun.h" +#include "sleef.h" +#include "StopWatch.h" + + +namespace +{ + +const std::vector> colormap = { + {0.5f, 0.f, 0.5f}, + {0.5f, 0.f, 0.5f}, + {0.5f, 0.f, 0.5f}, + {0.5f, 0.f, 0.5f}, + {0.5f, 0.f, 0.5f}, + {0.5f, 0.f, 0.5f}, // blacks + {0.f, 0.f, 1.f}, // shadows + {0.5f, 0.5f, 0.5f}, // midtones + {1.f, 1.f, 0.f}, // highlights + {1.f, 0.f, 0.f}, // whites + {1.f, 0.f, 0.f}, + {1.f, 0.f, 0.f} +}; + +} + + +namespace rtengine +{ + +void ImProcFunctions::toneEqualizer( + array2D &R, array2D &G, array2D &B, + const struct ToneEqualizerParams ¶ms, + const Glib::ustring &workingProfile, + double scale, + bool multithread, + bool show_color_map) +// adapted from the tone equalizer of darktable +/* + Copyright 2019 Alberto Griggio + Small adaptation to Local Adjustment 10 2019 Jacques Desmis + This file is part of darktable, + copyright (c) 2018 Aurelien Pierre. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + +{ + // BENCHFUN + + const int W = R.getWidth(); + const int H = R.getHeight(); + array2D Y(W, H); + + const auto log2 = + [](float x) -> float { + static const float l2 = xlogf(2); + return xlogf(x) / l2; + }; + + const auto exp2 = + [](float x) -> float { + return pow_F(2.f, x); + }; + // Build the luma channels: band-pass filters with gaussian windows of + // std 2 EV, spaced by 2 EV + const float centers[12] = { + -18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f, + -4.0f, -2.0f, 0.0f, 2.0f, 4.0f + }; + + const auto conv = [&](int v, float lo, float hi) -> float { + const float f = v < 0 ? lo : hi; + return exp2(float(v) / 100.f * f); + }; + const float factors[12] = { + conv(params.bands[0], 2.f, 3.f), // -18 EV + conv(params.bands[0], 2.f, 3.f), // -16 EV + conv(params.bands[0], 2.f, 3.f), // -14 EV + conv(params.bands[0], 2.f, 3.f), // -12 EV + conv(params.bands[0], 2.f, 3.f), // -10 EV + conv(params.bands[0], 2.f, 3.f), // -8 EV + conv(params.bands[1], 2.f, 3.f), // -6 EV + conv(params.bands[2], 2.5f, 2.5f), // -4 EV + conv(params.bands[3], 3.f, 2.f), // -2 EV + conv(params.bands[4], 3.f, 2.f), // 0 EV + conv(params.bands[4], 3.f, 2.f), // 2 EV + conv(params.bands[4], 3.f, 2.f) // 4 EV + }; + + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile); + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws); + } + } + + int detail = LIM(params.regularization + 5, 0, 5); + int radius = detail / scale + 0.5; + float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0); + + if (radius > 0) { + rtengine::guidedFilterLog(10.f, Y, radius, epsilon2, multithread); + } + + if (params.regularization > 0) { + array2D Y2(W, H); + constexpr float base_epsilon = 0.02f; + constexpr float base_posterization = 5.f; + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]); + float ll = round(l * base_posterization) / base_posterization; + Y2[y][x] = Y[y][x]; + Y[y][x] = exp2(ll); + } + } + + radius = 350.0 / scale; + epsilon2 = base_epsilon / float(6 - rtengine::min(params.regularization, 5)); + rtengine::guidedFilter(Y2, Y, Y, radius, epsilon2, multithread); + } + + const auto gauss = + [](float b, float x) -> float { + return xexpf((-SQR(x - b) / 4.0f)); + }; + + // For every pixel luminance, the sum of the gaussian masks + float w_sum = 0.f; + + for (int i = 0; i < 12; ++i) { + w_sum += gauss(centers[i], 0.f); + } + + const auto process_pixel = + [&](float y) -> float { + // convert to log space + const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); + + // build the correction as the sum of the contribution of each + // luminance channel to current pixel + float correction = 0.0f; + + for (int c = 0; c < 12; ++c) + { + correction += gauss(centers[c], luma) * factors[c]; + } + + correction /= w_sum; + + return correction; + }; + + std::vector> cur_colormap; + if (show_color_map) { + lcmsMutex->lock(); + cmsHPROFILE in = ICCStore::getInstance()->getsRGBProfile(); + cmsHPROFILE out = ICCStore::getInstance()->workingSpace(workingProfile); + cmsHTRANSFORM xform = cmsCreateTransform(in, TYPE_RGB_FLT, out, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); + lcmsMutex->unlock(); + + for (auto &c : colormap) { + cur_colormap.push_back(c); + auto &cc = cur_colormap.back(); + cmsDoTransform(xform, &cc[0], &cc[0], 1); + } + + cmsDeleteTransform(xform); + } + + const auto process_colormap = + [&](float y) -> std::array + { + std::array ret = { 0.f, 0.f, 0.f }; + + // convert to log space + const float luma = max(log2(max(y, 0.f)), -18.0f); + + // build the correction as the sum of the contribution of each + // luminance channel to current pixel + for (int c = 0; c < 12; ++c) { + float w = gauss(centers[c], luma); + for (int i = 0; i < 3; ++i) { + ret[i] += w * cur_colormap[c][i]; + } + } + for (int i = 0; i < 3; ++i) { + ret[i] = LIM01(ret[i] / w_sum); + } + + return ret; + }; + + +#ifdef __SSE2__ + vfloat vfactors[12]; + vfloat vcenters[12]; + + for (int i = 0; i < 12; ++i) { + vfactors[i] = F2V(factors[i]); + vcenters[i] = F2V(centers[i]); + } + + const auto vgauss = + [](vfloat b, vfloat x) -> vfloat { + static const vfloat fourv = F2V(4.f); + return xexpf((-SQR(x - b) / fourv)); + }; + + vfloat zerov = F2V(0.f); + vfloat vw_sum = F2V(w_sum); + + const vfloat noisev = F2V(-18.f); + const vfloat xlog2v = F2V(xlogf(2.f)); + + const auto vprocess_pixel = + [&](vfloat y) -> vfloat { + const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev); + + vfloat correction = zerov; + + for (int c = 0; c < 12; ++c) + { + correction += vgauss(vcenters[c], luma) * vfactors[c]; + } + + correction /= vw_sum; + + return correction; + }; + + + vfloat v1 = F2V(1.f); + vfloat v65535 = F2V(65535.f); +#endif // __SSE2__ + + + if (show_color_map) { + LUTf lut_r(65537), lut_g(65537), lut_b(65537); + for (int i = 0; i < 65536; ++i) { + float y = float(i)/65535.f; + auto rgb = process_colormap(y); + lut_r[i] = rgb[0]; + lut_g[i] = rgb[1]; + lut_b[i] = rgb[2]; + } + lut_r[65536] = cur_colormap.back()[0]; + lut_g[65536] = cur_colormap.back()[1]; + lut_b[65536] = cur_colormap.back()[2]; + +#ifdef _OPENMP +# pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float cY = Y[y][x] * 65535.f; + R[y][x] = lut_r[cY]; + G[y][x] = lut_g[cY]; + B[y][x] = lut_b[cY]; + } + } + return; + } + + + LUTf lut(65536); + + for (int i = 0; i < 65536; ++i) { + float y = float(i) / 65535.f; + float c = process_pixel(y); + lut[i] = c; + } + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + int x = 0; + + +#ifdef __SSE2__ + + for (; x < W - 3; x += 4) { + vfloat cY = LVFU(Y[y][x]); + vmask m = vmaskf_gt(cY, v1); + vfloat corr; + + if (_mm_movemask_ps((vfloat)m)) { + corr = vprocess_pixel(cY); + } else { + corr = lut[cY * v65535]; + } + + STVF(R[y][x], LVF(R[y][x]) * corr); + STVF(G[y][x], LVF(G[y][x]) * corr); + STVF(B[y][x], LVF(B[y][x]) * corr); + } + +#endif // __SSE2__ + + for (; x < W; ++x) { + float cY = Y[y][x]; + float corr = cY > 1.f ? process_pixel(cY) : lut[cY * 65535.f]; + R[y][x] *= corr; + G[y][x] *= corr; + B[y][x] *= corr; + } + } + +} + +bool ImProcFunctions::toneEqualizer(Imagefloat *rgb) +{ + if (!params->toneEqualizer.enabled) { + return false; + } + + BENCHFUN + + const float gain = 1.f / 65535.f * std::pow(2.f, -params->toneEqualizer.pivot); + + rgb->multiply(gain, multiThread); + + const int W = rgb->getWidth(); + const int H = rgb->getHeight(); + + array2D R(W, H, rgb->r.ptrs, ARRAY2D_BYREFERENCE); + array2D G(W, H, rgb->g.ptrs, ARRAY2D_BYREFERENCE); + array2D B(W, H, rgb->b.ptrs, ARRAY2D_BYREFERENCE); + + bool show_color_map = params->toneEqualizer.show_colormap; + toneEqualizer(R, G, B, params->toneEqualizer, params->icm.workingProfile, scale, multiThread, show_color_map); + + rgb->multiply(1.f/gain, multiThread); + + return show_color_map; +} + +} diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 9a9e4fef1..555af6879 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1786,6 +1786,30 @@ bool SHParams::operator !=(const SHParams& other) const return !(*this == other); } +ToneEqualizerParams::ToneEqualizerParams() : + enabled(false), + bands{0, 0, 0, 0, 0}, + regularization(4), + show_colormap(false), + pivot(0) +{ +} + +bool ToneEqualizerParams::operator ==(const ToneEqualizerParams &other) const +{ + return + enabled == other.enabled + && bands == other.bands + && regularization == other.regularization + && show_colormap == other.show_colormap + && pivot == other.pivot; +} + +bool ToneEqualizerParams::operator !=(const ToneEqualizerParams &other) const +{ + return !(*this == other); +} + CropParams::CropParams() : enabled(false), x(-1), @@ -5814,6 +5838,8 @@ void ProcParams::setDefaults() sh = {}; + toneEqualizer = {}; + crop = {}; coarse = {}; @@ -6241,6 +6267,14 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->sh.radius, "Shadows & Highlights", "Radius", sh.radius, keyFile); saveToKeyfile(!pedited || pedited->sh.lab, "Shadows & Highlights", "Lab", sh.lab, keyFile); +// Tone equalizer + saveToKeyfile(!pedited || pedited->toneEqualizer.enabled, "ToneEqualizer", "Enabled", toneEqualizer.enabled, keyFile); + for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { + saveToKeyfile(!pedited || pedited->toneEqualizer.bands, "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile); + } + saveToKeyfile(!pedited || pedited->toneEqualizer.regularization, "ToneEqualizer", "Regularization", toneEqualizer.regularization, keyFile); + saveToKeyfile(!pedited || pedited->toneEqualizer.pivot, "ToneEqualizer", "Pivot", toneEqualizer.pivot, keyFile); + // Crop saveToKeyfile(!pedited || pedited->crop.enabled, "Crop", "Enabled", crop.enabled, keyFile); saveToKeyfile(!pedited || pedited->crop.x, "Crop", "X", crop.x, keyFile); @@ -8226,6 +8260,15 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } } + if (keyFile.has_group("ToneEqualizer")) { + assignFromKeyfile(keyFile, "ToneEqualizer", "Enabled", pedited, toneEqualizer.enabled, pedited->toneEqualizer.enabled); + for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { + assignFromKeyfile(keyFile, "ToneEqualizer", "Band" + std::to_string(i), pedited, toneEqualizer.bands[i], pedited->toneEqualizer.bands); + } + assignFromKeyfile(keyFile, "ToneEqualizer", "Regularization", pedited, toneEqualizer.regularization, pedited->toneEqualizer.regularization); + assignFromKeyfile(keyFile, "ToneEqualizer", "Pivot", pedited, toneEqualizer.pivot, pedited->toneEqualizer.pivot); + } + if (keyFile.has_group("Crop")) { assignFromKeyfile(keyFile, "Crop", "Enabled", pedited, crop.enabled, pedited->crop.enabled); assignFromKeyfile(keyFile, "Crop", "X", pedited, crop.x, pedited->crop.x); @@ -10447,6 +10490,7 @@ bool ProcParams::operator ==(const ProcParams& other) const && fattal == other.fattal && defringe == other.defringe && sh == other.sh + && toneEqualizer == other.toneEqualizer && crop == other.crop && coarse == other.coarse && rotate == other.rotate diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 04229867b..5a86b40a0 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -827,6 +827,22 @@ struct SHParams { bool operator !=(const SHParams& other) const; }; +/** + * Tone equalizer parameters. + */ +struct ToneEqualizerParams { + bool enabled; + std::array bands; + int regularization; + bool show_colormap; + double pivot; + + ToneEqualizerParams(); + + bool operator ==(const ToneEqualizerParams &other) const; + bool operator !=(const ToneEqualizerParams &other) const; +}; + /** * Parameters of the cropping */ @@ -2547,6 +2563,7 @@ public: EPDParams epd; ///< Edge Preserving Decomposition parameters FattalToneMappingParams fattal; ///< Fattal02 tone mapping SHParams sh; ///< Shadow/highlight enhancement parameters + ToneEqualizerParams toneEqualizer; ///< Tone equalizer parameters CropParams crop; ///< Crop parameters CoarseTransformParams coarse; ///< Coarse transformation (90, 180, 270 deg rotation, h/v flipping) parameters CommonTransformParams commonTrans; ///< Common transformation parameters (autofill) diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 7976bdc7a..1d56f2638 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -159,6 +159,7 @@ set(NONCLISOURCEFILES thumbimageupdater.cc thumbnail.cc tonecurve.cc + toneequalizer.cc toolbar.cc toolpanel.cc toolpanelcoord.cc diff --git a/rtgui/adjuster.cc b/rtgui/adjuster.cc index a2f96cac3..3fe3d61eb 100644 --- a/rtgui/adjuster.cc +++ b/rtgui/adjuster.cc @@ -51,6 +51,7 @@ Adjuster::Adjuster( grid(nullptr), label(nullptr), imageIcon1(imgIcon1), + imageIcon2(imgIcon2), automatic(nullptr), adjusterListener(nullptr), spinChange(options.adjusterMinDelay, options.adjusterMaxDelay), @@ -76,8 +77,8 @@ Adjuster::Adjuster( setExpandAlignProperties(imageIcon1, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); } - if (imgIcon2) { - setExpandAlignProperties(imgIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); + if (imageIcon2) { + setExpandAlignProperties(imageIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); } set_column_spacing(0); @@ -120,9 +121,9 @@ Adjuster::Adjuster( attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1); } - if (imgIcon2) { - attach_next_to(*imgIcon2, *slider, Gtk::POS_RIGHT, 1, 1); - attach_next_to(*spin, *imgIcon2, Gtk::POS_RIGHT, 1, 1); + if (imageIcon2) { + attach_next_to(*imageIcon2, *slider, Gtk::POS_RIGHT, 1, 1); + attach_next_to(*spin, *imageIcon2, Gtk::POS_RIGHT, 1, 1); } else { attach_next_to(*spin, *slider, Gtk::POS_RIGHT, 1, 1); } @@ -140,9 +141,9 @@ Adjuster::Adjuster( grid->attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1); } - if (imgIcon2) { - grid->attach_next_to(*imgIcon2, Gtk::POS_RIGHT, 1, 1); - grid->attach_next_to(*reset, *imgIcon2, Gtk::POS_RIGHT, 1, 1); + if (imageIcon2) { + grid->attach_next_to(*imageIcon2, Gtk::POS_RIGHT, 1, 1); + grid->attach_next_to(*reset, *imageIcon2, Gtk::POS_RIGHT, 1, 1); } else { grid->attach_next_to(*reset, *slider, Gtk::POS_RIGHT, 1, 1); } @@ -683,3 +684,13 @@ void Adjuster::setDelay(unsigned int min_delay_ms, unsigned int max_delay_ms) spinChange.setDelay(min_delay_ms, max_delay_ms); sliderChange.setDelay(min_delay_ms, max_delay_ms); } + +void Adjuster::showIcons(bool yes) +{ + if (imageIcon1) { + imageIcon1->set_visible(yes); + } + if (imageIcon2) { + imageIcon2->set_visible(yes); + } +} diff --git a/rtgui/adjuster.h b/rtgui/adjuster.h index abafbd730..76b46b094 100644 --- a/rtgui/adjuster.h +++ b/rtgui/adjuster.h @@ -41,6 +41,7 @@ protected: Gtk::Grid* grid; Gtk::Label* label; Gtk::Image *imageIcon1; + Gtk::Image *imageIcon2; MyHScale* slider; MySpinButton* spin; Gtk::Button* reset; @@ -133,4 +134,5 @@ public: void trimValue (int &val) const; void setLogScale(double base, double pivot, bool anchorMiddle = false); void setDelay(unsigned int min_delay_ms, unsigned int max_delay_ms = 0); + void showIcons(bool yes); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 529d57da6..ff48a58b1 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -317,6 +317,11 @@ void ParamsEdited::set(bool v) sh.stonalwidth = v; sh.radius = v; sh.lab = v; + toneEqualizer.enabled = v; + toneEqualizer.bands = v; + toneEqualizer.regularization = v; + toneEqualizer.show_colormap = v; + toneEqualizer.pivot = v; crop.enabled = v; crop.x = v; crop.y = v; @@ -1031,6 +1036,11 @@ void ParamsEdited::initFrom(const std::vector& crop.ratio = crop.ratio && p.crop.ratio == other.crop.ratio; crop.orientation = crop.orientation && p.crop.orientation == other.crop.orientation; crop.guide = crop.guide && p.crop.guide == other.crop.guide; + toneEqualizer.enabled = toneEqualizer.enabled && p.toneEqualizer.enabled == other.toneEqualizer.enabled; + toneEqualizer.bands = toneEqualizer.bands && p.toneEqualizer.bands == other.toneEqualizer.bands; + toneEqualizer.regularization = toneEqualizer.regularization && p.toneEqualizer.regularization == other.toneEqualizer.regularization; + toneEqualizer.show_colormap = toneEqualizer.show_colormap && p.toneEqualizer.show_colormap == other.toneEqualizer.show_colormap; + toneEqualizer.pivot = toneEqualizer.pivot && p.toneEqualizer.pivot == other.toneEqualizer.pivot; coarse.rotate = coarse.rotate && p.coarse.rotate == other.coarse.rotate; coarse.hflip = coarse.hflip && p.coarse.hflip == other.coarse.hflip; coarse.vflip = coarse.vflip && p.coarse.vflip == other.coarse.vflip; @@ -3194,6 +3204,26 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.sh.lab = mods.sh.lab; } + if (toneEqualizer.enabled) { + toEdit.toneEqualizer.enabled = mods.toneEqualizer.enabled; + } + + if (toneEqualizer.bands) { + toEdit.toneEqualizer.bands = mods.toneEqualizer.bands; + } + + if (toneEqualizer.regularization) { + toEdit.toneEqualizer.regularization = mods.toneEqualizer.regularization; + } + + if (toneEqualizer.show_colormap) { + toEdit.toneEqualizer.show_colormap = mods.toneEqualizer.show_colormap; + } + + if (toneEqualizer.pivot) { + toEdit.toneEqualizer.pivot = mods.toneEqualizer.pivot; + } + if (crop.enabled) { toEdit.crop.enabled = mods.crop.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 94abed470..d70336a54 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -358,6 +358,14 @@ struct SHParamsEdited { bool lab; }; +struct ToneEqualizerParamsEdited { + bool enabled; + bool bands; + bool regularization; + bool show_colormap; + bool pivot; +}; + struct CropParamsEdited { bool enabled; bool x; @@ -1545,6 +1553,7 @@ struct ParamsEdited { FattalToneMappingParamsEdited fattal; ImpulseDenoiseParamsEdited impulseDenoise; SHParamsEdited sh; + ToneEqualizerParamsEdited toneEqualizer; CropParamsEdited crop; CoarseTransformParamsEdited coarse; CommonTransformParamsEdited commonTrans; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index b3c49dfd7..08cf23f72 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -219,6 +219,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren wb = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_WHITEBALANCE"))); exposure = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_EXPOSURE"))); sh = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHADOWSHIGHLIGHTS"))); + toneEqualizer = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_TONE_EQUALIZER"))); epd = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_EPD"))); fattal = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_TM_FATTAL"))); pcvignette = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PCVIGNETTE"))); @@ -331,6 +332,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren vboxes[0]->pack_start (*wb, Gtk::PACK_SHRINK, 2); vboxes[0]->pack_start (*exposure, Gtk::PACK_SHRINK, 2); vboxes[0]->pack_start (*sh, Gtk::PACK_SHRINK, 2); + vboxes[0]->pack_start (*toneEqualizer, Gtk::PACK_SHRINK, 2); vboxes[0]->pack_start (*epd, Gtk::PACK_SHRINK, 2); vboxes[0]->pack_start (*fattal, Gtk::PACK_SHRINK, 2); vboxes[0]->pack_start (*pcvignette, Gtk::PACK_SHRINK, 2); @@ -496,6 +498,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren wbConn = wb->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); exposureConn = exposure->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); shConn = sh->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); + toneEqualizerConn = toneEqualizer->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); epdConn = epd->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); fattalConn = fattal->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); pcvignetteConn = pcvignette->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); @@ -701,6 +704,7 @@ void PartialPasteDlg::basicToggled () ConnectionBlocker wbBlocker(wbConn); ConnectionBlocker exposureBlocker(exposureConn); ConnectionBlocker shBlocker(shConn); + ConnectionBlocker toneEqualizerBlocker(toneEqualizerConn); ConnectionBlocker epdBlocker(epdConn); ConnectionBlocker fattalBlocker(fattalConn); ConnectionBlocker pcvignetteBlocker(pcvignetteConn); @@ -712,6 +716,7 @@ void PartialPasteDlg::basicToggled () wb->set_active (basic->get_active ()); exposure->set_active (basic->get_active ()); sh->set_active (basic->get_active ()); + toneEqualizer->set_active (basic->get_active ()); epd->set_active (basic->get_active ()); fattal->set_active (basic->get_active ()); pcvignette->set_active (basic->get_active ()); @@ -889,6 +894,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param filterPE.sh = falsePE.sh; } + if (!toneEqualizer->get_active ()) { + filterPE.toneEqualizer = falsePE.toneEqualizer; + } + if (!epd->get_active ()) { filterPE.epd = falsePE.epd; } diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h index 19e1eb462..fc45bd5e4 100644 --- a/rtgui/partialpastedlg.h +++ b/rtgui/partialpastedlg.h @@ -133,6 +133,7 @@ public: Gtk::CheckButton* exposure; Gtk::CheckButton* localcontrast; Gtk::CheckButton* sh; + Gtk::CheckButton* toneEqualizer; Gtk::CheckButton* epd; Gtk::CheckButton* fattal; Gtk::CheckButton* retinex; @@ -225,6 +226,7 @@ public: sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn; sigc::connection locallabConn; sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn; + sigc::connection toneEqualizerConn; sigc::connection spotConn, sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn; sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn; sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn; diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index cc8e9ad81..01aba228f 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -462,6 +462,7 @@ void Thumbnail::setProcParams (const ProcParams& pp, ParamsEdited* pe, int whoCh || pparams->epd != pp.epd || pparams->fattal != pp.fattal || pparams->sh != pp.sh + || pparams->toneEqualizer != pp.toneEqualizer || pparams->crop != pp.crop || pparams->coarse != pp.coarse || pparams->commonTrans != pp.commonTrans diff --git a/rtgui/toneequalizer.cc b/rtgui/toneequalizer.cc new file mode 100644 index 000000000..366376ea4 --- /dev/null +++ b/rtgui/toneequalizer.cc @@ -0,0 +1,169 @@ +/* + * Adapted from ART. + * + * 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 "eventmapper.h" +#include "toneequalizer.h" +#include "rtimage.h" + +using namespace rtengine; +using namespace rtengine::procparams; + + +ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_TONE_EQUALIZER_LABEL"), false, true) +{ + auto m = ProcEventMapper::getInstance(); + EvEnabled = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_ENABLED"); + EvBands = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_BANDS"); + EvRegularization = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION"); + EvColormap = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP"); + EvPivot = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_PIVOT"); + + std::array images = { + "purple", + "blue", + "gray", + "yellow", + "red" + }; + for (size_t i = 0; i < bands.size(); ++i) { + bands[i] = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_BAND_" + std::to_string(i)), -100, 100, 1, 0, Gtk::manage(new RTImage(Glib::ustring("circle-") + images[i] + "-small.png")))); + bands[i]->setAdjusterListener(this); + pack_start(*bands[i]); + bands[i]->showIcons(false); + } + + pivot = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_PIVOT"), -12, 12, 0.05, 0)); + pivot->setLogScale(64, 0, true); + pivot->setAdjusterListener(this); + pack_start(*pivot); + + pack_start(*Gtk::manage(new Gtk::HSeparator())); + regularization = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_DETAIL"), -5, 5, 1, 5)); + regularization->setAdjusterListener(this); + pack_start(*regularization); + + show_colormap = Gtk::manage(new Gtk::CheckButton(M("TP_TONE_EQUALIZER_SHOW_COLOR_MAP"))); + pack_start(*show_colormap); + show_colormap->signal_toggled().connect(sigc::mem_fun(this, &ToneEqualizer::colormapToggled)); + + show_all_children (); +} + + +void ToneEqualizer::read(const ProcParams *pp, const ParamsEdited* pedited) +{ + disableListener(); + + setEnabled(pp->toneEqualizer.enabled); + + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->setValue(pp->toneEqualizer.bands[i]); + bands[i]->showIcons(pp->toneEqualizer.show_colormap); + } + regularization->setValue(pp->toneEqualizer.regularization); + + pivot->setValue(pp->toneEqualizer.pivot); + show_colormap->set_active(pp->toneEqualizer.show_colormap); + + enableListener(); +} + + +void ToneEqualizer::write(ProcParams *pp, ParamsEdited* pedited) +{ + for (size_t i = 0; i < bands.size(); ++i) { + pp->toneEqualizer.bands[i] = bands[i]->getValue(); + } + pp->toneEqualizer.enabled = getEnabled(); + pp->toneEqualizer.regularization = regularization->getValue(); + pp->toneEqualizer.show_colormap = show_colormap->get_active(); + pp->toneEqualizer.pivot = pivot->getValue(); +} + + +void ToneEqualizer::setDefaults(const ProcParams *defParams, const ParamsEdited* pedited) +{ + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->setDefault(defParams->toneEqualizer.bands[i]); + } + regularization->setDefault(defParams->toneEqualizer.regularization); + + pivot->setDefault(defParams->toneEqualizer.pivot); + inital_params = defParams->toneEqualizer; +} + + +void ToneEqualizer::adjusterChanged(Adjuster *a, double newval) +{ + if (listener && getEnabled()) { + if (a == regularization) { + listener->panelChanged(EvRegularization, Glib::ustring::format(a->getValue())); + } else if (a == pivot) { + listener->panelChanged(EvPivot, Glib::ustring::format(a->getValue())); + } else { + Glib::ustring s; + for (size_t i = 0; i < bands.size(); ++i) { + s += Glib::ustring::format((int)bands[i]->getValue()) + " "; + } + listener->panelChanged(EvBands, s); + } + } +} + + +void ToneEqualizer::adjusterAutoToggled(Adjuster *a) +{ +} + + +void ToneEqualizer::enabledChanged() +{ + if (listener) { + if (get_inconsistent()) { + listener->panelChanged(EvEnabled, M("GENERAL_UNCHANGED")); + } else if (getEnabled()) { + listener->panelChanged(EvEnabled, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(EvEnabled, M("GENERAL_DISABLED")); + } + } +} + + +void ToneEqualizer::colormapToggled() +{ + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->showIcons(show_colormap->get_active()); + } + if (listener && getEnabled()) { + listener->panelChanged(EvColormap, show_colormap->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + +void ToneEqualizer::trimValues(rtengine::procparams::ProcParams *pp) +{ + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->trimValue(pp->toneEqualizer.bands[i]); + } + regularization->trimValue(pp->toneEqualizer.regularization); + pivot->trimValue(pp->toneEqualizer.pivot); +} + diff --git a/rtgui/toneequalizer.h b/rtgui/toneequalizer.h new file mode 100644 index 000000000..f4fed79af --- /dev/null +++ b/rtgui/toneequalizer.h @@ -0,0 +1,56 @@ +/* -*- C++ -*- + * + * Adapted from ART. + * + * 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 . + */ +#pragma once + +#include +#include "adjuster.h" +#include "toolpanel.h" + +class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel { +public: + ToneEqualizer(); + + 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 adjusterChanged(Adjuster *a, double newval) override; + void adjusterAutoToggled(Adjuster *a) override; + void enabledChanged() override; + + void trimValues(rtengine::procparams::ProcParams *pp) override; + +private: + void colormapToggled(); + + std::array bands; + Adjuster *regularization; + Adjuster *pivot; + Gtk::CheckButton *show_colormap; + + rtengine::ProcEvent EvEnabled; + rtengine::ProcEvent EvBands; + rtengine::ProcEvent EvRegularization; + rtengine::ProcEvent EvColormap; + rtengine::ProcEvent EvPivot; + + rtengine::procparams::ToneEqualizerParams inital_params; +}; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 2c506710f..b61b059f6 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -47,6 +47,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit coarse = Gtk::manage (new CoarsePanel ()); toneCurve = Gtk::manage (new ToneCurve ()); shadowshighlights = Gtk::manage (new ShadowsHighlights ()); + toneEqualizer = Gtk::manage (new ToneEqualizer ()); impulsedenoise = Gtk::manage (new ImpulseDenoise ()); defringe = Gtk::manage (new Defringe ()); spot = Gtk::manage (new Spot ()); @@ -118,6 +119,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit addfavoritePanel (colorPanel, chmixer); addfavoritePanel (colorPanel, blackwhite); addfavoritePanel (exposurePanel, shadowshighlights); + addfavoritePanel (exposurePanel, toneEqualizer); addfavoritePanel (detailsPanel, spot); addfavoritePanel (detailsPanel, sharpening); addfavoritePanel (detailsPanel, localContrast); diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 13686d6e3..3f7afacc6 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -78,6 +78,7 @@ #include "softlight.h" #include "spot.h" #include "tonecurve.h" +#include "toneequalizer.h" #include "toolbar.h" #include "toolpanel.h" #include "vibrance.h" @@ -133,6 +134,7 @@ protected: Crop* crop; ToneCurve* toneCurve; ShadowsHighlights* shadowshighlights; + ToneEqualizer* toneEqualizer; LocalContrast *localContrast; Spot* spot; Defringe* defringe; From 3e2337bfae4ae74e1c1286d916a4f8eb7d6b2e61 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 12 Jun 2022 16:57:40 -0700 Subject: [PATCH 2/9] Add tone equalizer to batch editor --- rtdata/languages/default | 1 + rtengine/procparams.cc | 4 +- rtengine/procparams.h | 1 + rtgui/addsetids.h | 3 ++ rtgui/adjuster.cc | 2 + rtgui/batchtoolpanelcoord.cc | 4 ++ rtgui/paramsedited.cc | 25 +++++++++--- rtgui/paramsedited.h | 3 +- rtgui/preferences.cc | 6 +++ rtgui/toneequalizer.cc | 79 +++++++++++++++++++++++++++++++++--- rtgui/toneequalizer.h | 8 +++- 11 files changed, 119 insertions(+), 17 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 6dd32ef7b..3ec76b494 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -3848,6 +3848,7 @@ TP_TONE_EQUALIZER_BAND_1;Shadows TP_TONE_EQUALIZER_BAND_2;Midtones TP_TONE_EQUALIZER_BAND_3;Highlights TP_TONE_EQUALIZER_BAND_4;Whites +TP_TONE_EQUALIZER_BANDS;Bands TP_TONE_EQUALIZER_DETAIL;Regularization TP_TONE_EQUALIZER_LABEL;Tone Equalizer TP_TONE_EQUALIZER_PIVOT;Pivot (Ev) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 555af6879..edc7d7980 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -6270,7 +6270,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo // Tone equalizer saveToKeyfile(!pedited || pedited->toneEqualizer.enabled, "ToneEqualizer", "Enabled", toneEqualizer.enabled, keyFile); for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { - saveToKeyfile(!pedited || pedited->toneEqualizer.bands, "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile); + saveToKeyfile(!pedited || pedited->toneEqualizer.bands[i], "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile); } saveToKeyfile(!pedited || pedited->toneEqualizer.regularization, "ToneEqualizer", "Regularization", toneEqualizer.regularization, keyFile); saveToKeyfile(!pedited || pedited->toneEqualizer.pivot, "ToneEqualizer", "Pivot", toneEqualizer.pivot, keyFile); @@ -8263,7 +8263,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) if (keyFile.has_group("ToneEqualizer")) { assignFromKeyfile(keyFile, "ToneEqualizer", "Enabled", pedited, toneEqualizer.enabled, pedited->toneEqualizer.enabled); for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { - assignFromKeyfile(keyFile, "ToneEqualizer", "Band" + std::to_string(i), pedited, toneEqualizer.bands[i], pedited->toneEqualizer.bands); + assignFromKeyfile(keyFile, "ToneEqualizer", "Band" + std::to_string(i), pedited, toneEqualizer.bands[i], pedited->toneEqualizer.bands[i]); } assignFromKeyfile(keyFile, "ToneEqualizer", "Regularization", pedited, toneEqualizer.regularization, pedited->toneEqualizer.regularization); assignFromKeyfile(keyFile, "ToneEqualizer", "Pivot", pedited, toneEqualizer.pivot, pedited->toneEqualizer.pivot); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 5a86b40a0..ddee7cbf3 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -18,6 +18,7 @@ */ #pragma once +#include #include #include #include diff --git a/rtgui/addsetids.h b/rtgui/addsetids.h index 6f68c6ae7..8a2ae0466 100644 --- a/rtgui/addsetids.h +++ b/rtgui/addsetids.h @@ -10,6 +10,9 @@ enum { ADDSET_SH_HIGHLIGHTS, ADDSET_SH_SHADOWS, ADDSET_SH_LOCALCONTRAST, // not used anymore + ADDSET_TONE_EQUALIZER_BANDS, + ADDSET_TONE_EQUALIZER_PIVOT, + ADDSET_TONE_EQUALIZER_REGULARIZATION, ADDSET_LC_BRIGHTNESS, ADDSET_LC_CONTRAST, ADDSET_SHARP_AMOUNT, diff --git a/rtgui/adjuster.cc b/rtgui/adjuster.cc index 3fe3d61eb..c9382832d 100644 --- a/rtgui/adjuster.cc +++ b/rtgui/adjuster.cc @@ -689,8 +689,10 @@ void Adjuster::showIcons(bool yes) { if (imageIcon1) { imageIcon1->set_visible(yes); + imageIcon1->set_no_show_all(!yes); } if (imageIcon2) { imageIcon2->set_visible(yes); + imageIcon2->set_no_show_all(!yes); } } diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index 9e74ddb90..82f47b619 100644 --- a/rtgui/batchtoolpanelcoord.cc +++ b/rtgui/batchtoolpanelcoord.cc @@ -219,6 +219,7 @@ void BatchToolPanelCoordinator::initSession () chmixer->setAdjusterBehavior (options.baBehav[ADDSET_CHMIXER] ); blackwhite->setAdjusterBehavior (options.baBehav[ADDSET_BLACKWHITE_HUES], options.baBehav[ADDSET_BLACKWHITE_GAMMA]); shadowshighlights->setAdjusterBehavior (options.baBehav[ADDSET_SH_HIGHLIGHTS], options.baBehav[ADDSET_SH_SHADOWS]); + toneEqualizer->setAdjusterBehavior(options.baBehav[ADDSET_TONE_EQUALIZER_BANDS], options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION], options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT]); dirpyrequalizer->setAdjusterBehavior (options.baBehav[ADDSET_DIRPYREQ], options.baBehav[ADDSET_DIRPYREQ_THRESHOLD], options.baBehav[ADDSET_DIRPYREQ_SKINPROTECT]); wavelet->setAdjusterBehavior (options.baBehav[ADDSET_WA], options.baBehav[ADDSET_WA_THRESHOLD], options.baBehav[ADDSET_WA_THRESHOLD2], options.baBehav[ADDSET_WA_THRES], options.baBehav[ADDSET_WA_CHRO], options.baBehav[ADDSET_WA_CHROMA], options.baBehav[ADDSET_WA_CONTRAST], options.baBehav[ADDSET_WA_SKINPROTECT], options.baBehav[ADDSET_WA_RESCHRO], options.baBehav[ADDSET_WA_TMRS], options.baBehav[ADDSET_WA_EDGS], options.baBehav[ADDSET_WA_SCALE], options.baBehav[ADDSET_WA_RESCON], options.baBehav[ADDSET_WA_RESCONH], options.baBehav[ADDSET_WA_THRR], options.baBehav[ADDSET_WA_THRRH], options.baBehav[ADDSET_WA_RADIUS], options.baBehav[ADDSET_WA_SKYPROTECT], options.baBehav[ADDSET_WA_EDGRAD], options.baBehav[ADDSET_WA_EDGVAL], options.baBehav[ADDSET_WA_STRENGTH], options.baBehav[ADDSET_WA_GAMMA], options.baBehav[ADDSET_WA_EDGEDETECT], options.baBehav[ADDSET_WA_EDGEDETECTTHR], options.baBehav[ADDSET_WA_EDGEDETECTTHR2]); dirpyrdenoise->setAdjusterBehavior (options.baBehav[ADDSET_DIRPYRDN_LUMA], options.baBehav[ADDSET_DIRPYRDN_LUMDET], options.baBehav[ADDSET_DIRPYRDN_CHROMA], options.baBehav[ADDSET_DIRPYRDN_CHROMARED], options.baBehav[ADDSET_DIRPYRDN_CHROMABLUE], options.baBehav[ADDSET_DIRPYRDN_GAMMA], options.baBehav[ADDSET_DIRPYRDN_PASSES]); @@ -242,6 +243,9 @@ void BatchToolPanelCoordinator::initSession () if (options.baBehav[ADDSET_TC_SATURATION]) { pparams.toneCurve.saturation = 0;} if (options.baBehav[ADDSET_SH_HIGHLIGHTS]) { pparams.sh.highlights = 0; } if (options.baBehav[ADDSET_SH_SHADOWS]) { pparams.sh.shadows = 0; } + if (options.baBehav[ADDSET_TONE_EQUALIZER_BANDS]) { pparams.toneEqualizer.bands = {}; } + if (options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT]) { pparams.toneEqualizer.pivot = 0; } + if (options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION]) { pparams.toneEqualizer.regularization = 0; } if (options.baBehav[ADDSET_LC_BRIGHTNESS]) { pparams.labCurve.brightness = 0; } if (options.baBehav[ADDSET_LC_CONTRAST]) { pparams.labCurve.contrast = 0; } if (options.baBehav[ADDSET_LC_CHROMATICITY]) { pparams.labCurve.chromaticity = 0; } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ff48a58b1..598e15673 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -318,7 +318,7 @@ void ParamsEdited::set(bool v) sh.radius = v; sh.lab = v; toneEqualizer.enabled = v; - toneEqualizer.bands = v; + toneEqualizer.bands.fill(v); toneEqualizer.regularization = v; toneEqualizer.show_colormap = v; toneEqualizer.pivot = v; @@ -1037,7 +1037,9 @@ void ParamsEdited::initFrom(const std::vector& crop.orientation = crop.orientation && p.crop.orientation == other.crop.orientation; crop.guide = crop.guide && p.crop.guide == other.crop.guide; toneEqualizer.enabled = toneEqualizer.enabled && p.toneEqualizer.enabled == other.toneEqualizer.enabled; - toneEqualizer.bands = toneEqualizer.bands && p.toneEqualizer.bands == other.toneEqualizer.bands; + for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { + toneEqualizer.bands[i] = toneEqualizer.bands[i] && p.toneEqualizer.bands[i] == other.toneEqualizer.bands[i]; + } toneEqualizer.regularization = toneEqualizer.regularization && p.toneEqualizer.regularization == other.toneEqualizer.regularization; toneEqualizer.show_colormap = toneEqualizer.show_colormap && p.toneEqualizer.show_colormap == other.toneEqualizer.show_colormap; toneEqualizer.pivot = toneEqualizer.pivot && p.toneEqualizer.pivot == other.toneEqualizer.pivot; @@ -3208,12 +3210,20 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.toneEqualizer.enabled = mods.toneEqualizer.enabled; } - if (toneEqualizer.bands) { - toEdit.toneEqualizer.bands = mods.toneEqualizer.bands; + for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) { + if (toneEqualizer.bands[i]) { + toEdit.toneEqualizer.bands[i] = + dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_BANDS] + ? toEdit.toneEqualizer.bands[i] + mods.toneEqualizer.bands[i] + : mods.toneEqualizer.bands[i]; + } } if (toneEqualizer.regularization) { - toEdit.toneEqualizer.regularization = mods.toneEqualizer.regularization; + toEdit.toneEqualizer.regularization = + dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION] + ? toEdit.toneEqualizer.regularization + mods.toneEqualizer.regularization + : mods.toneEqualizer.regularization; } if (toneEqualizer.show_colormap) { @@ -3221,7 +3231,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng } if (toneEqualizer.pivot) { - toEdit.toneEqualizer.pivot = mods.toneEqualizer.pivot; + toEdit.toneEqualizer.pivot = + dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT] + ? toEdit.toneEqualizer.pivot + mods.toneEqualizer.pivot + : mods.toneEqualizer.pivot; } if (crop.enabled) { diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index d70336a54..f34eba69b 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -18,6 +18,7 @@ */ #pragma once +#include #include namespace rtengine @@ -360,7 +361,7 @@ struct SHParamsEdited { struct ToneEqualizerParamsEdited { bool enabled; - bool bands; + std::array bands; bool regularization; bool show_colormap; bool pivot; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index c6c2eb61b..2df816b44 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -227,6 +227,12 @@ Gtk::Widget* Preferences::getBatchProcPanel() appendBehavList(mi, M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), ADDSET_SH_HIGHLIGHTS, false); appendBehavList(mi, M("TP_SHADOWSHLIGHTS_SHADOWS"), ADDSET_SH_SHADOWS, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_TONE_EQUALIZER_LABEL")); + appendBehavList(mi, M("TP_TONE_EQUALIZER_BANDS"), ADDSET_TONE_EQUALIZER_BANDS, false); + appendBehavList(mi, M("TP_TONE_EQUALIZER_PIVOT"), ADDSET_TONE_EQUALIZER_PIVOT, false); + appendBehavList(mi, M("TP_TONE_EQUALIZER_DETAIL"), ADDSET_TONE_EQUALIZER_REGULARIZATION, false); + mi = behModel->append(); mi->set_value(behavColumns.label, M("TP_LABCURVE_LABEL")); appendBehavList(mi, M("TP_LABCURVE_BRIGHTNESS"), ADDSET_LC_BRIGHTNESS, false); diff --git a/rtgui/toneequalizer.cc b/rtgui/toneequalizer.cc index 366376ea4..27d35d514 100644 --- a/rtgui/toneequalizer.cc +++ b/rtgui/toneequalizer.cc @@ -59,9 +59,9 @@ ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_T regularization->setAdjusterListener(this); pack_start(*regularization); - show_colormap = Gtk::manage(new Gtk::CheckButton(M("TP_TONE_EQUALIZER_SHOW_COLOR_MAP"))); + show_colormap = Gtk::manage(new CheckBox(M("TP_TONE_EQUALIZER_SHOW_COLOR_MAP"), multiImage)); pack_start(*show_colormap); - show_colormap->signal_toggled().connect(sigc::mem_fun(this, &ToneEqualizer::colormapToggled)); + show_colormap->setCheckBoxListener(this); show_all_children (); } @@ -71,6 +71,16 @@ void ToneEqualizer::read(const ProcParams *pp, const ParamsEdited* pedited) { disableListener(); + if (pedited) { + set_inconsistent(multiImage && !pedited->toneEqualizer.enabled); + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->setEditedState(pedited->toneEqualizer.bands[i] ? Edited : UnEdited); + } + regularization->setEditedState(pedited->toneEqualizer.regularization ? Edited : UnEdited); + pivot->setEditedState(pedited->toneEqualizer.pivot ? Edited : UnEdited); + show_colormap->setEdited(pedited->toneEqualizer.show_colormap ? Edited : UnEdited); + } + setEnabled(pp->toneEqualizer.enabled); for (size_t i = 0; i < bands.size(); ++i) { @@ -80,7 +90,7 @@ void ToneEqualizer::read(const ProcParams *pp, const ParamsEdited* pedited) regularization->setValue(pp->toneEqualizer.regularization); pivot->setValue(pp->toneEqualizer.pivot); - show_colormap->set_active(pp->toneEqualizer.show_colormap); + show_colormap->setValue(pp->toneEqualizer.show_colormap); enableListener(); } @@ -93,8 +103,19 @@ void ToneEqualizer::write(ProcParams *pp, ParamsEdited* pedited) } pp->toneEqualizer.enabled = getEnabled(); pp->toneEqualizer.regularization = regularization->getValue(); - pp->toneEqualizer.show_colormap = show_colormap->get_active(); + pp->toneEqualizer.show_colormap = show_colormap->getLastActive(); pp->toneEqualizer.pivot = pivot->getValue(); + + if (pedited) { + auto &edited = pedited->toneEqualizer; + edited.enabled = !get_inconsistent(); + for (size_t i = 0; i < bands.size(); ++i) { + edited.bands[i] = bands[i]->getEditedState(); + } + edited.regularization = regularization->getEditedState(); + edited.pivot = pivot->getEditedState(); + edited.show_colormap = show_colormap->getEdited(); + } } @@ -107,6 +128,21 @@ void ToneEqualizer::setDefaults(const ProcParams *defParams, const ParamsEdited* pivot->setDefault(defParams->toneEqualizer.pivot); inital_params = defParams->toneEqualizer; + + if (pedited) { + auto &edited = pedited->toneEqualizer; + for (size_t i = 0; i < bands.size(); ++i) { + bands[i]->setDefaultEditedState(edited.bands[i] ? Edited : UnEdited); + } + regularization->setDefaultEditedState(edited.regularization ? Edited : UnEdited); + pivot->setDefaultEditedState(edited.pivot ? Edited : UnEdited); + } else { + for (auto band : bands) { + band->setDefaultEditedState(Irrelevant); + } + regularization->setDefaultEditedState(Irrelevant); + pivot->setDefaultEditedState(Irrelevant); + } } @@ -147,13 +183,44 @@ void ToneEqualizer::enabledChanged() } +void ToneEqualizer::setBatchMode(bool batchMode) +{ + ToolPanel::setBatchMode(batchMode); + if (batchMode) { + for (auto band : bands) { + band->showEditedCB(); + } + regularization->showEditedCB(); + pivot->showEditedCB(); + } +} + + +void ToneEqualizer::setAdjusterBehavior(bool bands_add, bool regularization_add, bool pivot_add) +{ + for (auto band : bands) { + band->setAddMode(bands_add); + } + regularization->setAddMode(regularization_add); + pivot->setAddMode(pivot_add); +} + + +void ToneEqualizer::checkBoxToggled(CheckBox *c, CheckValue newval) +{ + if (c == show_colormap) { + colormapToggled(); + } +} + + void ToneEqualizer::colormapToggled() { for (size_t i = 0; i < bands.size(); ++i) { - bands[i]->showIcons(show_colormap->get_active()); + bands[i]->showIcons(show_colormap->getLastActive()); } if (listener && getEnabled()) { - listener->panelChanged(EvColormap, show_colormap->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged(EvColormap, show_colormap->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } diff --git a/rtgui/toneequalizer.h b/rtgui/toneequalizer.h index f4fed79af..657167f05 100644 --- a/rtgui/toneequalizer.h +++ b/rtgui/toneequalizer.h @@ -23,9 +23,10 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "toolpanel.h" -class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel { +class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public CheckBoxListener { public: ToneEqualizer(); @@ -35,6 +36,9 @@ public: void adjusterChanged(Adjuster *a, double newval) override; void adjusterAutoToggled(Adjuster *a) override; void enabledChanged() override; + void setBatchMode(bool batchMode) override; + void setAdjusterBehavior(bool bands_add, bool regularization_add, bool pivot_add); + void checkBoxToggled(CheckBox* c, CheckValue newval) override; void trimValues(rtengine::procparams::ProcParams *pp) override; @@ -44,7 +48,7 @@ private: std::array bands; Adjuster *regularization; Adjuster *pivot; - Gtk::CheckButton *show_colormap; + CheckBox *show_colormap; rtengine::ProcEvent EvEnabled; rtengine::ProcEvent EvBands; From c4ea272d6580a7dc50cfda7bd1d4260c8f2af11c Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 26 Jun 2022 15:14:05 -0700 Subject: [PATCH 3/9] Fix tone equalizer causing double LA application Change the tone equalizer event refresh type to force correct computation of local of local adjustments of local adjustments. Idea from from https://github.com/Beep6581/RawTherapee/issues/6069#issuecomment-765263602. --- rtgui/toneequalizer.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rtgui/toneequalizer.cc b/rtgui/toneequalizer.cc index 27d35d514..fd60d4936 100644 --- a/rtgui/toneequalizer.cc +++ b/rtgui/toneequalizer.cc @@ -29,11 +29,11 @@ using namespace rtengine::procparams; ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_TONE_EQUALIZER_LABEL"), false, true) { auto m = ProcEventMapper::getInstance(); - EvEnabled = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_ENABLED"); - EvBands = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_BANDS"); - EvRegularization = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION"); - EvColormap = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP"); - EvPivot = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_PIVOT"); + EvEnabled = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_ENABLED"); + EvBands = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_BANDS"); + EvRegularization = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION"); + EvColormap = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP"); + EvPivot = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_PIVOT"); std::array images = { "purple", From a877ac4878cba816e1e378244775d2ea7321412e Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 26 Jun 2022 16:06:34 -0700 Subject: [PATCH 4/9] Add pivot to local adjustments tone equalizer --- rtdata/languages/default | 2 ++ rtengine/improcfun.h | 4 +-- rtengine/iplocallab.cc | 22 +++++------- rtengine/iptoneequalizer.cc | 67 ++++++++++++++++++++----------------- rtengine/procparams.cc | 4 +++ rtengine/procparams.h | 1 + rtgui/locallabtools.cc | 18 +++++++++- rtgui/locallabtools.h | 3 ++ rtgui/paramsedited.cc | 7 ++++ rtgui/paramsedited.h | 1 + 10 files changed, 82 insertions(+), 47 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3ec76b494..3064c13e3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1475,6 +1475,7 @@ HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Darkness HISTORY_MSG_LOCALCONTRAST_ENABLED;Local Contrast HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius +HISTORY_MSG_LOCALLAB_TE_PIVOT;Local - Equalizer pivot HISTORY_MSG_METADATA_MODE;Metadata copy mode HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold @@ -3433,6 +3434,7 @@ TP_LOCALLAB_STYPE_TOOLTIP;You can choose between:\nSymmetrical - left handle lin TP_LOCALLAB_SYM;Symmetrical (mouse) TP_LOCALLAB_SYMSL;Symmetrical (mouse + sliders) TP_LOCALLAB_TARGET_GRAY;Mean luminance (Yb%) +TP_LOCALLAB_TE_PIVOT;Pivot (Ev) TP_LOCALLAB_THRES;Threshold structure TP_LOCALLAB_THRESDELTAE;ΔE scope threshold TP_LOCALLAB_THRESRETI;Threshold diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 70aed428c..12487cdbd 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -492,8 +492,8 @@ enum class BlurType { void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); //void shadowsHighlights(LabImage *lab); void shadowsHighlights(LabImage *lab, bool ena, int labmode, int hightli, int shado, int rad, int scal, int hltonal, int shtonal); - bool toneEqualizer(Imagefloat *rgb); - static void toneEqualizer(array2D &R, array2D &G, array2D &B, const procparams::ToneEqualizerParams & params, const Glib::ustring &workingProfile, double scale, bool multithread, bool show_color_map); + void toneEqualizer(Imagefloat *rgb); + void toneEqualizer(Imagefloat *rgb, const procparams::ToneEqualizerParams ¶ms, const Glib::ustring &workingProfile, double scale, bool multiThread); void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams); void labColorCorrectionRegions(LabImage *lab); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 7a427b0eb..005b960a5 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -702,6 +702,7 @@ struct local_params { float mulloc[6]; int mullocsh[5]; int detailsh; + double tePivot; float threshol; float chromacb; float strengt; @@ -1691,6 +1692,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.detailsh = locallab.spots.at(sp).detailSH; + lp.tePivot = locallab.spots.at(sp).tePivot; lp.threshol = thresho; lp.chromacb = chromcbdl; lp.expvib = locallab.spots.at(sp).expvibrance && lp.activspot ; @@ -2257,12 +2259,14 @@ void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, } } -void tone_eq(array2D &R, array2D &G, array2D &B, const struct local_params & lp, const Glib::ustring &workingProfile, double scale, bool multithread) +void tone_eq(ImProcFunctions *ipf, Imagefloat *rgb, const struct local_params &lp, const Glib::ustring &workingProfile, double scale, bool multithread) { ToneEqualizerParams params; + params.enabled = true; params.regularization = lp.detailsh; + params.pivot = lp.tePivot; std::copy(lp.mullocsh, lp.mullocsh + params.bands.size(), params.bands.begin()); - ImProcFunctions::toneEqualizer(R, G, B, params, workingProfile, scale, multithread, false); + ipf->toneEqualizer(rgb, params, workingProfile, scale, multithread); } void ImProcFunctions::loccont(int bfw, int bfh, LabImage* tmp1, float rad, float stren, int sk) { @@ -7586,12 +7590,7 @@ void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp } if (tonequ) { - tmpImage->normalizeFloatTo1(); - array2D Rtemp(GW, GH, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); - array2D Gtemp(GW, GH, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); - array2D Btemp(GW, GH, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); - tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, sk, multiThread); - tmpImage->normalizeFloatTo65535(); + tone_eq(this, tmpImage.get(), lp, params->icm.workingProfile, sk, multiThread); } rgb2lab(*tmpImage, *temp, params->icm.workingProfile); @@ -15766,12 +15765,7 @@ void ImProcFunctions::Lab_Local( } if (tonequ) { - tmpImage->normalizeFloatTo1(); - array2D Rtemp(bfw, bfh, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); - array2D Gtemp(bfw, bfh, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); - array2D Btemp(bfw, bfh, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); - tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, scal, multiThread); - tmpImage->normalizeFloatTo65535(); + tone_eq(this, tmpImage, lp, params->icm.workingProfile, scal, multiThread); } rgb2lab(*tmpImage, *bufexpfin, params->icm.workingProfile); diff --git a/rtengine/iptoneequalizer.cc b/rtengine/iptoneequalizer.cc index c177b8f98..a661d6b41 100644 --- a/rtengine/iptoneequalizer.cc +++ b/rtengine/iptoneequalizer.cc @@ -25,19 +25,13 @@ const std::vector> colormap = { {1.f, 0.f, 0.f} }; -} - -namespace rtengine -{ - -void ImProcFunctions::toneEqualizer( +void toneEqualizer( array2D &R, array2D &G, array2D &B, - const struct ToneEqualizerParams ¶ms, + const rtengine::ToneEqualizerParams ¶ms, const Glib::ustring &workingProfile, double scale, - bool multithread, - bool show_color_map) + bool multithread) // adapted from the tone equalizer of darktable /* Copyright 2019 Alberto Griggio @@ -102,18 +96,18 @@ void ImProcFunctions::toneEqualizer( conv(params.bands[4], 3.f, 2.f) // 4 EV }; - TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile); + rtengine::TMatrix ws = rtengine::ICCStore::getInstance()->workingSpaceMatrix(workingProfile); #ifdef _OPENMP #pragma omp parallel for if (multithread) #endif for (int y = 0; y < H; ++y) { for (int x = 0; x < W; ++x) { - Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws); + Y[y][x] = rtengine::Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws); } } - int detail = LIM(params.regularization + 5, 0, 5); + int detail = rtengine::LIM(params.regularization + 5, 0, 5); int radius = detail / scale + 0.5; float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0); @@ -131,7 +125,7 @@ void ImProcFunctions::toneEqualizer( #endif for (int y = 0; y < H; ++y) { for (int x = 0; x < W; ++x) { - float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]); + float l = rtengine::LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]); float ll = round(l * base_posterization) / base_posterization; Y2[y][x] = Y[y][x]; Y[y][x] = exp2(ll); @@ -145,7 +139,7 @@ void ImProcFunctions::toneEqualizer( const auto gauss = [](float b, float x) -> float { - return xexpf((-SQR(x - b) / 4.0f)); + return xexpf((-rtengine::SQR(x - b) / 4.0f)); }; // For every pixel luminance, the sum of the gaussian masks @@ -175,12 +169,12 @@ void ImProcFunctions::toneEqualizer( }; std::vector> cur_colormap; - if (show_color_map) { - lcmsMutex->lock(); - cmsHPROFILE in = ICCStore::getInstance()->getsRGBProfile(); - cmsHPROFILE out = ICCStore::getInstance()->workingSpace(workingProfile); + if (params.show_colormap) { + rtengine::lcmsMutex->lock(); + cmsHPROFILE in = rtengine::ICCStore::getInstance()->getsRGBProfile(); + cmsHPROFILE out = rtengine::ICCStore::getInstance()->workingSpace(workingProfile); cmsHTRANSFORM xform = cmsCreateTransform(in, TYPE_RGB_FLT, out, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); - lcmsMutex->unlock(); + rtengine::lcmsMutex->unlock(); for (auto &c : colormap) { cur_colormap.push_back(c); @@ -197,7 +191,7 @@ void ImProcFunctions::toneEqualizer( std::array ret = { 0.f, 0.f, 0.f }; // convert to log space - const float luma = max(log2(max(y, 0.f)), -18.0f); + const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); // build the correction as the sum of the contribution of each // luminance channel to current pixel @@ -208,7 +202,7 @@ void ImProcFunctions::toneEqualizer( } } for (int i = 0; i < 3; ++i) { - ret[i] = LIM01(ret[i] / w_sum); + ret[i] = rtengine::LIM01(ret[i] / w_sum); } return ret; @@ -227,7 +221,7 @@ void ImProcFunctions::toneEqualizer( const auto vgauss = [](vfloat b, vfloat x) -> vfloat { static const vfloat fourv = F2V(4.f); - return xexpf((-SQR(x - b) / fourv)); + return xexpf((-rtengine::SQR(x - b) / fourv)); }; vfloat zerov = F2V(0.f); @@ -258,7 +252,7 @@ void ImProcFunctions::toneEqualizer( #endif // __SSE2__ - if (show_color_map) { + if (params.show_colormap) { LUTf lut_r(65537), lut_g(65537), lut_b(65537); for (int i = 0; i < 65536; ++i) { float y = float(i)/65535.f; @@ -332,15 +326,26 @@ void ImProcFunctions::toneEqualizer( } -bool ImProcFunctions::toneEqualizer(Imagefloat *rgb) +} + + +namespace rtengine { - if (!params->toneEqualizer.enabled) { - return false; + +void ImProcFunctions::toneEqualizer( + Imagefloat *rgb, + const ToneEqualizerParams ¶ms, + const Glib::ustring &workingProfile, + double scale, + bool multiThread) +{ + if (!params.enabled) { + return; } BENCHFUN - const float gain = 1.f / 65535.f * std::pow(2.f, -params->toneEqualizer.pivot); + const float gain = 1.f / 65535.f * std::pow(2.f, -params.pivot); rgb->multiply(gain, multiThread); @@ -351,12 +356,14 @@ bool ImProcFunctions::toneEqualizer(Imagefloat *rgb) array2D G(W, H, rgb->g.ptrs, ARRAY2D_BYREFERENCE); array2D B(W, H, rgb->b.ptrs, ARRAY2D_BYREFERENCE); - bool show_color_map = params->toneEqualizer.show_colormap; - toneEqualizer(R, G, B, params->toneEqualizer, params->icm.workingProfile, scale, multiThread, show_color_map); + ::toneEqualizer(R, G, B, params, workingProfile, scale, multiThread); rgb->multiply(1.f/gain, multiThread); +} - return show_color_map; +void ImProcFunctions::toneEqualizer(Imagefloat *rgb) +{ + toneEqualizer(rgb, params->toneEqualizer, params->icm.workingProfile, scale, multiThread); } } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index edc7d7980..52d6be8dd 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3324,6 +3324,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : slomaskSH(0.0), lapmaskSH(0.0), detailSH(0), + tePivot(0.), reparsh(100.), LmaskSHcurve{ static_cast(DCT_NURBS), @@ -4769,6 +4770,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && slomaskSH == other.slomaskSH && lapmaskSH == other.lapmaskSH && detailSH == other.detailSH + && tePivot == other.tePivot && reparsh == other.reparsh && LmaskSHcurve == other.LmaskSHcurve && fatamountSH == other.fatamountSH @@ -6562,6 +6564,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->gammaskSH, "Locallab", "GammaskSH_" + index_str, spot.gammaskSH, keyFile); saveToKeyfile(!pedited || spot_edited->slomaskSH, "Locallab", "SlomaskSH_" + index_str, spot.slomaskSH, keyFile); saveToKeyfile(!pedited || spot_edited->detailSH, "Locallab", "DetailSH_" + index_str, spot.detailSH, keyFile); + saveToKeyfile(!pedited || spot_edited->tePivot, "Locallab", "TePivot_" + index_str, spot.tePivot, keyFile); saveToKeyfile(!pedited || spot_edited->reparsh, "Locallab", "Reparsh_" + index_str, spot.reparsh, keyFile); saveToKeyfile(!pedited || spot_edited->LmaskSHcurve, "Locallab", "LmaskSHCurve_" + index_str, spot.LmaskSHcurve, keyFile); saveToKeyfile(!pedited || spot_edited->fatamountSH, "Locallab", "FatamountSH_" + index_str, spot.fatamountSH, keyFile); @@ -8668,6 +8671,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "SlomaskSH_" + index_str, pedited, spot.slomaskSH, spotEdited.slomaskSH); assignFromKeyfile(keyFile, "Locallab", "LapmaskSH_" + index_str, pedited, spot.lapmaskSH, spotEdited.lapmaskSH); assignFromKeyfile(keyFile, "Locallab", "DetailSH_" + index_str, pedited, spot.detailSH, spotEdited.detailSH); + assignFromKeyfile(keyFile, "Locallab", "TePivot_" + index_str, pedited, spot.tePivot, spotEdited.tePivot); assignFromKeyfile(keyFile, "Locallab", "Reparsh_" + index_str, pedited, spot.reparsh, spotEdited.reparsh); assignFromKeyfile(keyFile, "Locallab", "LmaskSHCurve_" + index_str, pedited, spot.LmaskSHcurve, spotEdited.LmaskSHcurve); assignFromKeyfile(keyFile, "Locallab", "FatamountSH_" + index_str, pedited, spot.fatamountSH, spotEdited.fatamountSH); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index ddee7cbf3..ba58cd32c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1214,6 +1214,7 @@ struct LocallabParams { double slomaskSH; double lapmaskSH; int detailSH; + double tePivot; double reparsh; std::vector LmaskSHcurve; double fatamountSH; diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 51da93ab3..250bd958f 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -3971,6 +3971,7 @@ LocallabShadow::LocallabShadow(): } ()), detailSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAILSH"), -5, 5, 1, 0))), + tePivot(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TE_PIVOT"), -12, 12, 0.05, 0))), highlights(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), 0, 100, 1, 0))), h_tonalwidth(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HLTONALW"), 10, 100, 1, 70))), shadows(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_SHADOWS"), 0, 100, 1, 0))), @@ -4011,7 +4012,8 @@ LocallabShadow::LocallabShadow(): LmaskSHshape(static_cast(mask2SHCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), fatSHFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_FATSHFRA")))), fatamountSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), - fatanchorSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))) + fatanchorSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + EvlocallabTePivot(ProcEventMapper::getInstance()->newEvent(AUTOEXP, "HISTORY_MSG_LOCALLAB_TE_PIVOT")) { set_orientation(Gtk::ORIENTATION_VERTICAL); @@ -4028,6 +4030,7 @@ LocallabShadow::LocallabShadow(): } detailSH->setAdjusterListener(this); + tePivot->setAdjusterListener(this); reparsh->setAdjusterListener(this); highlights->setAdjusterListener(this); @@ -4138,6 +4141,7 @@ LocallabShadow::LocallabShadow(): } pack_start(*detailSH); + pack_start(*tePivot); pack_start(*highlights); pack_start(*h_tonalwidth); pack_start(*shadows); @@ -4358,6 +4362,7 @@ void LocallabShadow::read(const rtengine::procparams::ProcParams* pp, const Para decays->setValue((double)spot.decays); detailSH->setValue((double)spot.detailSH); + tePivot->setValue(spot.tePivot); reparsh->setValue(spot.reparsh); highlights->setValue((double)spot.highlights); h_tonalwidth->setValue((double)spot.h_tonalwidth); @@ -4423,6 +4428,7 @@ void LocallabShadow::write(rtengine::procparams::ProcParams* pp, ParamsEdited* p } spot.detailSH = detailSH->getIntValue(); + spot.tePivot = tePivot->getValue(); spot.reparsh = reparsh->getValue(); spot.highlights = highlights->getIntValue(); spot.h_tonalwidth = h_tonalwidth->getIntValue(); @@ -4471,6 +4477,7 @@ void LocallabShadow::setDefaults(const rtengine::procparams::ProcParams* defPara } detailSH->setDefault((double)defSpot.detailSH); + tePivot->setDefault(defSpot.tePivot); reparsh->setDefault(defSpot.reparsh); highlights->setDefault((double)defSpot.highlights); h_tonalwidth->setDefault((double)defSpot.h_tonalwidth); @@ -4522,6 +4529,13 @@ void LocallabShadow::adjusterChanged(Adjuster* a, double newval) } } + if (a == tePivot) { + if (listener) { + listener->panelChanged(EvlocallabTePivot, + tePivot->getTextValue() + " (" + escapeHtmlChars(getSpotName()) + ")"); + } + } + if (a == reparsh) { if (listener) { listener->panelChanged(Evlocallabreparsh, @@ -5038,6 +5052,7 @@ void LocallabShadow::updateShadowGUI2() gamFrame->hide(); detailSH->hide(); + tePivot->hide(); highlights->show(); h_tonalwidth->show(); shadows->show(); @@ -5053,6 +5068,7 @@ void LocallabShadow::updateShadowGUI2() } detailSH->show(); + tePivot->show(); highlights->hide(); h_tonalwidth->hide(); shadows->hide(); diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index e7fcfc1d0..72c6730e8 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -451,6 +451,7 @@ private: Adjuster* const reparsh; const std::array multipliersh; Adjuster* const detailSH; + Adjuster* const tePivot; Adjuster* const highlights; Adjuster* const h_tonalwidth; Adjuster* const shadows; @@ -492,6 +493,8 @@ private: Adjuster* const fatamountSH; Adjuster* const fatanchorSH; + rtengine::ProcEvent EvlocallabTePivot; + sigc::connection shMethodConn, inversshConn, showmaskSHMethodConn, showmaskSHMethodConninv, enaSHMaskConn; public: diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 598e15673..5ef83ed86 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1283,6 +1283,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).slomaskSH = locallab.spots.at(j).slomaskSH && pSpot.slomaskSH == otherSpot.slomaskSH; locallab.spots.at(j).lapmaskSH = locallab.spots.at(j).lapmaskSH && pSpot.lapmaskSH == otherSpot.lapmaskSH; locallab.spots.at(j).detailSH = locallab.spots.at(j).detailSH && pSpot.detailSH == otherSpot.detailSH; + locallab.spots.at(j).tePivot = locallab.spots.at(j).tePivot && pSpot.tePivot == otherSpot.tePivot; locallab.spots.at(j).reparsh = locallab.spots.at(j).reparsh && pSpot.reparsh == otherSpot.reparsh; locallab.spots.at(j).LmaskSHcurve = locallab.spots.at(j).LmaskSHcurve && pSpot.LmaskSHcurve == otherSpot.LmaskSHcurve; locallab.spots.at(j).fatamountSH = locallab.spots.at(j).fatamountSH && pSpot.fatamountSH == otherSpot.fatamountSH; @@ -4182,6 +4183,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).detailSH = mods.locallab.spots.at(i).detailSH; } + if (locallab.spots.at(i).tePivot) { + toEdit.locallab.spots.at(i).tePivot = mods.locallab.spots.at(i).tePivot; + } + if (locallab.spots.at(i).reparsh) { toEdit.locallab.spots.at(i).reparsh = mods.locallab.spots.at(i).reparsh; } @@ -7624,6 +7629,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : slomaskSH(v), lapmaskSH(v), detailSH(v), + tePivot(v), reparsh(v), LmaskSHcurve(v), fatamountSH(v), @@ -8320,6 +8326,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) slomaskSH = v; lapmaskSH = v; detailSH = v; + tePivot = v; reparsh = v; LmaskSHcurve = v; fatamountSH = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index f34eba69b..f5f318088 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -589,6 +589,7 @@ public: bool slomaskSH; bool lapmaskSH; bool detailSH; + bool tePivot; bool reparsh; bool LmaskSHcurve; bool fatamountSH; From efc350228ee943a5c1c48c4c8c868ddf1bb81ac3 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 26 Jun 2022 18:37:28 -0700 Subject: [PATCH 5/9] Fix tone equalizer colormap brightness Don't apply pivot to the colormap itself. --- rtengine/iptoneequalizer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/iptoneequalizer.cc b/rtengine/iptoneequalizer.cc index a661d6b41..481d14114 100644 --- a/rtengine/iptoneequalizer.cc +++ b/rtengine/iptoneequalizer.cc @@ -358,7 +358,7 @@ void ImProcFunctions::toneEqualizer( ::toneEqualizer(R, G, B, params, workingProfile, scale, multiThread); - rgb->multiply(1.f/gain, multiThread); + rgb->multiply(params.show_colormap ? 65535.f : 1.f/gain, multiThread); } void ImProcFunctions::toneEqualizer(Imagefloat *rgb) From 38d85581df13108ae8647cb58eca3260bb14da08 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Thu, 30 Jun 2022 21:19:30 -0700 Subject: [PATCH 6/9] Fix tone equalizer darkening of extreme luminances Extend the extreme bands' contributions all the way to -infinity and +infinity EVs. --- rtengine/iptoneequalizer.cc | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/rtengine/iptoneequalizer.cc b/rtengine/iptoneequalizer.cc index 481d14114..7fb6c791f 100644 --- a/rtengine/iptoneequalizer.cc +++ b/rtengine/iptoneequalizer.cc @@ -15,13 +15,13 @@ const std::vector> colormap = { {0.5f, 0.f, 0.5f}, {0.5f, 0.f, 0.5f}, {0.5f, 0.f, 0.5f}, - {0.5f, 0.f, 0.5f}, {0.5f, 0.f, 0.5f}, // blacks {0.f, 0.f, 1.f}, // shadows {0.5f, 0.5f, 0.5f}, // midtones {1.f, 1.f, 0.f}, // highlights {1.f, 0.f, 0.f}, // whites {1.f, 0.f, 0.f}, + {1.f, 0.f, 0.f}, {1.f, 0.f, 0.f} }; @@ -73,8 +73,8 @@ void toneEqualizer( // Build the luma channels: band-pass filters with gaussian windows of // std 2 EV, spaced by 2 EV const float centers[12] = { - -18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f, - -4.0f, -2.0f, 0.0f, 2.0f, 4.0f + -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f, + -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f }; const auto conv = [&](int v, float lo, float hi) -> float { @@ -82,7 +82,6 @@ void toneEqualizer( return exp2(float(v) / 100.f * f); }; const float factors[12] = { - conv(params.bands[0], 2.f, 3.f), // -18 EV conv(params.bands[0], 2.f, 3.f), // -16 EV conv(params.bands[0], 2.f, 3.f), // -14 EV conv(params.bands[0], 2.f, 3.f), // -12 EV @@ -93,7 +92,8 @@ void toneEqualizer( conv(params.bands[3], 3.f, 2.f), // -2 EV conv(params.bands[4], 3.f, 2.f), // 0 EV conv(params.bands[4], 3.f, 2.f), // 2 EV - conv(params.bands[4], 3.f, 2.f) // 4 EV + conv(params.bands[4], 3.f, 2.f), // 4 EV + conv(params.bands[4], 3.f, 2.f) // 6 EV }; rtengine::TMatrix ws = rtengine::ICCStore::getInstance()->workingSpaceMatrix(workingProfile); @@ -149,10 +149,13 @@ void toneEqualizer( w_sum += gauss(centers[i], 0.f); } + constexpr float luma_lo = -14.f; + constexpr float luma_hi = 4.f; + const auto process_pixel = [&](float y) -> float { // convert to log space - const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); + const float luma = rtengine::LIM(log2(rtengine::max(y, 0.f)), luma_lo, luma_hi); // build the correction as the sum of the contribution of each // luminance channel to current pixel @@ -191,7 +194,7 @@ void toneEqualizer( std::array ret = { 0.f, 0.f, 0.f }; // convert to log space - const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); + const float luma = rtengine::LIM(log2(rtengine::max(y, 0.f)), luma_lo, luma_hi); // build the correction as the sum of the contribution of each // luminance channel to current pixel @@ -227,12 +230,13 @@ void toneEqualizer( vfloat zerov = F2V(0.f); vfloat vw_sum = F2V(w_sum); - const vfloat noisev = F2V(-18.f); + const vfloat vluma_lo = F2V(luma_lo); + const vfloat vluma_hi = F2V(luma_hi); const vfloat xlog2v = F2V(xlogf(2.f)); const auto vprocess_pixel = [&](vfloat y) -> vfloat { - const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev); + const vfloat luma = vminf(vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, vluma_lo), vluma_hi); vfloat correction = zerov; From a13c8a5a474aad3140c997dac0d011bda9147f98 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Thu, 30 Jun 2022 21:58:10 -0700 Subject: [PATCH 7/9] Make default tone eq. regularization consistent Use 0 for the initial and reset value, which is the same as the details in the local adjustments version. --- rtengine/procparams.cc | 2 +- rtgui/toneequalizer.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 52d6be8dd..361ee847c 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1789,7 +1789,7 @@ bool SHParams::operator !=(const SHParams& other) const ToneEqualizerParams::ToneEqualizerParams() : enabled(false), bands{0, 0, 0, 0, 0}, - regularization(4), + regularization(0), show_colormap(false), pivot(0) { diff --git a/rtgui/toneequalizer.cc b/rtgui/toneequalizer.cc index fd60d4936..de748c2b7 100644 --- a/rtgui/toneequalizer.cc +++ b/rtgui/toneequalizer.cc @@ -55,7 +55,7 @@ ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_T pack_start(*pivot); pack_start(*Gtk::manage(new Gtk::HSeparator())); - regularization = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_DETAIL"), -5, 5, 1, 5)); + regularization = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_DETAIL"), -5, 5, 1, 0)); regularization->setAdjusterListener(this); pack_start(*regularization); From 12ef477a6d6e150d513161b634661fdb688764f3 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Mon, 6 Feb 2023 21:44:57 -0800 Subject: [PATCH 8/9] Fix merge issues Adapt tone equalizer to favorites tab changes. --- rtgui/toneequalizer.cc | 3 ++- rtgui/toneequalizer.h | 2 ++ rtgui/toollocationpref.cc | 2 ++ rtgui/toolpanelcoord.cc | 7 +++++++ rtgui/toolpanelcoord.h | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/rtgui/toneequalizer.cc b/rtgui/toneequalizer.cc index de748c2b7..d524bdc05 100644 --- a/rtgui/toneequalizer.cc +++ b/rtgui/toneequalizer.cc @@ -25,8 +25,9 @@ using namespace rtengine; using namespace rtengine::procparams; +const Glib::ustring ToneEqualizer::TOOL_NAME = "toneequalizer"; -ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_TONE_EQUALIZER_LABEL"), false, true) +ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, TOOL_NAME, M("TP_TONE_EQUALIZER_LABEL"), false, true) { auto m = ProcEventMapper::getInstance(); EvEnabled = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_ENABLED"); diff --git a/rtgui/toneequalizer.h b/rtgui/toneequalizer.h index 657167f05..88a275799 100644 --- a/rtgui/toneequalizer.h +++ b/rtgui/toneequalizer.h @@ -28,6 +28,8 @@ class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public CheckBoxListener { public: + static const Glib::ustring TOOL_NAME; + ToneEqualizer(); void read(const rtengine::procparams::ProcParams *pp, const ParamsEdited* pedited = nullptr) override; diff --git a/rtgui/toollocationpref.cc b/rtgui/toollocationpref.cc index cf25dbc34..a2e2c9480 100644 --- a/rtgui/toollocationpref.cc +++ b/rtgui/toollocationpref.cc @@ -70,6 +70,8 @@ Glib::ustring getToolTitleKey(Tool tool) return "TP_EXPOSURE_LABEL"; case Tool::SHADOWS_HIGHLIGHTS: return "TP_SHADOWSHLIGHTS_LABEL"; + case Tool::TONE_EQUALIZER: + return "TP_TONE_EQUALIZER_LABEL"; case Tool::IMPULSE_DENOISE: return "TP_IMPULSEDENOISE_LABEL"; case Tool::DEFRINGE_TOOL: diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 0a8a510c7..e9b0ce5cc 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -44,6 +44,9 @@ const std::vector EXPOSURE_PANEL_TOOLS = { { .id = Tool::SHADOWS_HIGHLIGHTS, }, + { + .id = Tool::TONE_EQUALIZER, + }, { .id = Tool::EPD, }, @@ -562,6 +565,8 @@ std::string ToolPanelCoordinator::getToolName(Tool tool) return ToneCurve::TOOL_NAME; case Tool::SHADOWS_HIGHLIGHTS: return ShadowsHighlights::TOOL_NAME; + case Tool::TONE_EQUALIZER: + return ToneEqualizer::TOOL_NAME; case Tool::IMPULSE_DENOISE: return ImpulseDenoise::TOOL_NAME; case Tool::DEFRINGE_TOOL: @@ -1908,6 +1913,8 @@ FoldableToolPanel *ToolPanelCoordinator::getFoldableToolPanel(Tool tool) const return toneCurve; case Tool::SHADOWS_HIGHLIGHTS: return shadowshighlights; + case Tool::TONE_EQUALIZER: + return toneEqualizer; case Tool::IMPULSE_DENOISE: return impulsedenoise; case Tool::DEFRINGE_TOOL: diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index e7338c90b..bc1e1a2d3 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -247,6 +247,7 @@ public: enum class Tool { TONE_CURVE, SHADOWS_HIGHLIGHTS, + TONE_EQUALIZER, IMPULSE_DENOISE, DEFRINGE_TOOL, SPOT, From 9893e02aab0f38ec96abebc1d8d5e94b825eaef3 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sat, 15 Apr 2023 16:53:58 -0700 Subject: [PATCH 9/9] Move tone equalizer after exposure Place it between the shadows curve (black and shadow compression) and DCP tone curve and look table. Moving it after exposure makes it possible to easily adjust the exposure to the ball-park range before applying the tone equalizer. Keeping it before the tone curves allows it to affect the image before clipping occurs. --- rtengine/improcfun.cc | 134 +++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index b417f00ce..19a1907da 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -1979,7 +1979,9 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer stop.reset(new StopWatch("rgb processing")); } - Imagefloat *tmpImage = nullptr; + const bool split_tiled_parts_1_2 = params->toneEqualizer.enabled; + + std::unique_ptr tmpImage; Imagefloat* editImgFloat = nullptr; PlanarWhateverData* editWhatever = nullptr; @@ -2139,6 +2141,7 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer const float comp = (max(0.0, expcomp) + 1.0) * hlcompr / 100.0; const float shoulder = ((65536.f / max(1.0f, exp_scale)) * (hlcomprthresh / 200.f)) + 0.1f; const float hlrange = 65536.f - shoulder; + const int tone_curve_black = params->toneCurve.black; const bool isProPhoto = (params->icm.workingProfile == "ProPhoto"); // extracting data from 'params' to avoid cache flush (to be confirmed) ToneCurveMode curveMode = params->toneCurve.curveMode; @@ -2247,8 +2250,8 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer } bool hasgammabw = gammabwr != 1.f || gammabwg != 1.f || gammabwb != 1.f; - if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled)) { - tmpImage = new Imagefloat(working->getWidth(), working->getHeight()); + if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled) || split_tiled_parts_1_2) { + tmpImage.reset(new Imagefloat(working->getWidth(), working->getHeight())); } // For tonecurve histogram @@ -2263,15 +2266,53 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer // For tonecurve histogram const float lumimulf[3] = {static_cast(lumimul[0]), static_cast(lumimul[1]), static_cast(lumimul[2])}; - std::unique_ptr workimg_copy; - if (params->toneEqualizer.enabled) { - working = working->copy(); - workimg_copy.reset(working); - toneEqualizer(working); - } - #define TS 112 + const auto tiled_part_1 = + [working, + mixchannels, + &hltonecurve, &shtonecurve, + chMixRR, chMixRG, chMixRB, + chMixGR, chMixGG, chMixGB, + chMixBR, chMixBG, chMixBB, + exp_scale, comp, hlrange, tone_curve_black]( + int istart, int jstart, int tH, int tW, + float *rtemp, float *gtemp, float *btemp) { + + for (int i = istart, ti = 0; i < tH; i++, ti++) { + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + rtemp[ti * TS + tj] = working->r(i, j); + gtemp[ti * TS + tj] = working->g(i, j); + btemp[ti * TS + tj] = working->b(i, j); + } + } + + if (mixchannels) { + for (int i = istart, ti = 0; i < tH; i++, ti++) { + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + float r = rtemp[ti * TS + tj]; + float g = gtemp[ti * TS + tj]; + float b = btemp[ti * TS + tj]; + + // if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b); + float rmix = (r * chMixRR + g * chMixRG + b * chMixRB) / 100.f; + float gmix = (r * chMixGR + g * chMixGG + b * chMixGB) / 100.f; + float bmix = (r * chMixBR + g * chMixBG + b * chMixBB) / 100.f; + + rtemp[ti * TS + tj] = rmix; + gtemp[ti * TS + tj] = gmix; + btemp[ti * TS + tj] = bmix; + } + } + } + + highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange); + + if (tone_curve_black != 0) { + shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS); + } + }; + #ifdef _OPENMP #pragma omp parallel if (multiThread) #endif @@ -2322,6 +2363,41 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer histToneCurveThr.clear(); } + if (split_tiled_parts_1_2) { + +#ifdef _OPENMP + #pragma omp for schedule(dynamic, chunkSize) collapse(2) +#endif + + for (int ii = 0; ii < working->getHeight(); ii += TS) { + for (int jj = 0; jj < working->getWidth(); jj += TS) { + istart = ii; + jstart = jj; + tH = min(ii + TS, working->getHeight()); + tW = min(jj + TS, working->getWidth()); + + + tiled_part_1(istart, jstart, tH, tW, rtemp, gtemp, btemp); + + // Copy tile to image. + for (int i = istart, ti = 0; i < tH; i++, ti++) { + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + tmpImage->r(i, j) = rtemp[ti * TS + tj]; + tmpImage->g(i, j) = gtemp[ti * TS + tj]; + tmpImage->b(i, j) = btemp[ti * TS + tj]; + } + } + } + } + } + +#ifdef _OPENMP + #pragma omp single +#endif + if (params->toneEqualizer.enabled) { + toneEqualizer(tmpImage.get()); + } + #ifdef _OPENMP #pragma omp for schedule(dynamic, chunkSize) collapse(2) #endif @@ -2333,38 +2409,16 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer tH = min(ii + TS, working->getHeight()); tW = min(jj + TS, working->getWidth()); - - for (int i = istart, ti = 0; i < tH; i++, ti++) { - for (int j = jstart, tj = 0; j < tW; j++, tj++) { - rtemp[ti * TS + tj] = working->r(i, j); - gtemp[ti * TS + tj] = working->g(i, j); - btemp[ti * TS + tj] = working->b(i, j); - } - } - - if (mixchannels) { + if (split_tiled_parts_1_2) { for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - float r = rtemp[ti * TS + tj]; - float g = gtemp[ti * TS + tj]; - float b = btemp[ti * TS + tj]; - - //if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b); - float rmix = (r * chMixRR + g * chMixRG + b * chMixRB) / 100.f; - float gmix = (r * chMixGR + g * chMixGG + b * chMixGB) / 100.f; - float bmix = (r * chMixBR + g * chMixBG + b * chMixBB) / 100.f; - - rtemp[ti * TS + tj] = rmix; - gtemp[ti * TS + tj] = gmix; - btemp[ti * TS + tj] = bmix; + rtemp[ti * TS + tj] = tmpImage->r(i, j); + gtemp[ti * TS + tj] = tmpImage->g(i, j); + btemp[ti * TS + tj] = tmpImage->b(i, j); } } - } - - highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange); - - if (params->toneCurve.black != 0.0) { - shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS); + } else { + tiled_part_1(istart, jstart, tH, tW, rtemp, gtemp, btemp); } if (dcpProf) { @@ -3515,10 +3569,6 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer } - if (tmpImage) { - delete tmpImage; - } - if (hCurveEnabled) { delete hCurve; }