From 25b066e257f45d680d8de086722f206a3c1f5751 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 13 Apr 2018 17:30:29 +0200 Subject: [PATCH] first version of new shadows/highlights tool --- rtengine/CMakeLists.txt | 1 + rtengine/dcrop.cc | 34 +++++----- rtengine/improccoordinator.cc | 28 ++++---- rtengine/improcfun.cc | 42 ++++++------ rtengine/improcfun.h | 1 + rtengine/ipshadowshighlights.cc | 111 ++++++++++++++++++++++++++++++++ rtengine/rtthumbnail.cc | 18 +++--- rtengine/simpleprocess.cc | 18 +++--- 8 files changed, 184 insertions(+), 69 deletions(-) create mode 100644 rtengine/ipshadowshighlights.cc diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 0693f919f..0c8b561a0 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -120,6 +120,7 @@ set(RTENGINESOURCEFILES histmatching.cc pdaflinesfilter.cc gamutwarning.cc + ipshadowshighlights.cc ) if(LENSFUN_HAS_LOAD_DIRECTORY) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 079a5de16..a4296d601 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -800,24 +800,24 @@ void Crop::update (int todo) } // blurmap for shadow & highlights - if ((todo & M_BLURMAP) && params.sh.enabled) { - double radius = sqrt (double (skips (parent->fw, skip) * skips (parent->fw, skip) + skips (parent->fh, skip) * skips (parent->fh, skip))) / 2.0; - double shradius = params.sh.radius; + // if ((todo & M_BLURMAP) && params.sh.enabled) { + // double radius = sqrt (double (skips (parent->fw, skip) * skips (parent->fw, skip) + skips (parent->fh, skip) * skips (parent->fh, skip))) / 2.0; + // double shradius = params.sh.radius; - if (!params.sh.hq) { - shradius *= radius / 1800.0; - } + // if (!params.sh.hq) { + // shradius *= radius / 1800.0; + // } - if (!cshmap) { - cshmap = new SHMap (cropw, croph, true); - } + // if (!cshmap) { + // cshmap = new SHMap (cropw, croph, true); + // } - cshmap->update (baseCrop, shradius, parent->ipf.lumimul, params.sh.hq, skip); + // cshmap->update (baseCrop, shradius, parent->ipf.lumimul, params.sh.hq, skip); - if (parent->shmap->min_f < 65535.f) { // don't call forceStat with wrong values - cshmap->forceStat (parent->shmap->max_f, parent->shmap->min_f, parent->shmap->avg); - } - } + // if (parent->shmap->min_f < 65535.f) { // don't call forceStat with wrong values + // cshmap->forceStat (parent->shmap->max_f, parent->shmap->min_f, parent->shmap->avg); + // } + // } // shadows & highlights & tone curve & convert to cielab @@ -1330,9 +1330,9 @@ bool Crop::setCropSizes (int rcx, int rcy, int rcw, int rch, int skip, bool inte cbuffer[i] = cbuf_real + cropw * i + cropw; } - if (params.sh.enabled) { - cshmap = new SHMap (cropw, croph, true); - } + // if (params.sh.enabled) { + // cshmap = new SHMap (cropw, croph, true); + // } if (editType == ET_PIPETTE) { PipetteBuffer::resize (cropw, croph); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index e22aa3b0d..2f657b884 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -415,20 +415,20 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) readyphase++; progress ("Preparing shadow/highlight map...", 100 * readyphase / numofphases); - if ((todo & M_BLURMAP) && params.sh.enabled) { - double radius = sqrt (double (pW * pW + pH * pH)) / 2.0; - double shradius = params.sh.radius; + // if ((todo & M_BLURMAP) && params.sh.enabled) { + // double radius = sqrt (double (pW * pW + pH * pH)) / 2.0; + // double shradius = params.sh.radius; - if (!params.sh.hq) { - shradius *= radius / 1800.0; - } + // if (!params.sh.hq) { + // shradius *= radius / 1800.0; + // } - if (!shmap) { - shmap = new SHMap (pW, pH, true); - } + // if (!shmap) { + // shmap = new SHMap (pW, pH, true); + // } - shmap->update (oprevi, shradius, ipf.lumimul, params.sh.hq, scale); - } + // shmap->update (oprevi, shradius, ipf.lumimul, params.sh.hq, scale); + // } @@ -966,9 +966,9 @@ void ImProcCoordinator::setScale (int prevscale) previmg = new Image8 (pW, pH); workimg = new Image8 (pW, pH); - if (params.sh.enabled) { - shmap = new SHMap (pW, pH, true); - } + // if (params.sh.enabled) { + // shmap = new SHMap (pW, pH, true); + // } allocated = true; } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index c30df4d7a..30c1a75ca 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -2047,7 +2047,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer s_th = params->sh.stonalwidth * (shmap->avg - shmap->min_f) / 100; } - bool processSH = params->sh.enabled && shmap && (params->sh.highlights > 0 || params->sh.shadows > 0); + bool processSH = false; //params->sh.enabled && shmap && (params->sh.highlights > 0 || params->sh.shadows > 0); TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working); TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.working); @@ -2437,30 +2437,30 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } - if (processSH) { - for (int i = istart, ti = 0; i < tH; i++, ti++) { - for (int j = jstart, tj = 0; j < tW; j++, tj++) { + // if (processSH) { + // 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]; + // float r = rtemp[ti * TS + tj]; + // float g = gtemp[ti * TS + tj]; + // float b = btemp[ti * TS + tj]; - float mapval = 1.f + shmap->map[i][j]; - float factor = 1.f; + // float mapval = 1.f + shmap->map[i][j]; + // float factor = 1.f; - if (mapval > h_th) { - factor = (h_th + (100.0 - shHighlights) * (mapval - h_th) / 100.0) / mapval; - } else if (mapval < s_th) { - factor = (s_th - (100.0 - shShadows) * (s_th - mapval) / 100.0) / mapval; - } + // if (mapval > h_th) { + // factor = (h_th + (100.0 - shHighlights) * (mapval - h_th) / 100.0) / mapval; + // } else if (mapval < s_th) { + // factor = (s_th - (100.0 - shShadows) * (s_th - mapval) / 100.0) / mapval; + // } - rtemp[ti * TS + tj] = factor * r; - gtemp[ti * TS + tj] = factor * g; - btemp[ti * TS + tj] = factor * b; - } - } - } + // rtemp[ti * TS + tj] = factor * r; + // gtemp[ti * TS + tj] = factor * g; + // btemp[ti * TS + tj] = factor * b; + // } + // } + // } highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange); shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS); @@ -3647,6 +3647,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer delete vCurve; } + shadowsHighlights(lab); + if (params->localContrast.enabled) { // Alberto's local contrast localContrast(lab); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index fbddd491d..ab96a2d2d 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -344,6 +344,7 @@ public: void ToneMapFattal02(Imagefloat *rgb); void localContrast(LabImage *lab); void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); + void shadowsHighlights(LabImage *lab); Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings=true); Imagefloat* lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); diff --git a/rtengine/ipshadowshighlights.cc b/rtengine/ipshadowshighlights.cc new file mode 100644 index 000000000..9a967d52b --- /dev/null +++ b/rtengine/ipshadowshighlights.cc @@ -0,0 +1,111 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright 2018 Alberto Griggio + * + * 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 . + */ + +#ifdef _OPENMP +#include +#endif + +#include "improcfun.h" +#include "gauss.h" + +namespace rtengine { + +void ImProcFunctions::shadowsHighlights(LabImage *lab) +{ + if (!params->sh.enabled || (!params->sh.highlights && !params->sh.shadows)){ + return; + } + + const int width = lab->W; + const int height = lab->H; + + array2D mask(width, height); + const float sigma = params->sh.radius * 5.f / scale; + + const auto apply = + [&](int amount, int tonalwidth, bool hl) -> void + { + // first highlights + const float thresh = tonalwidth * 327.68f; + const float scale = hl ? (thresh > 0.f ? 0.9f / thresh : 1.f) : thresh * 0.9f; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + float l = lab->L[y][x]; + if (hl) { + mask[y][x] = (l > thresh) ? 1.f : std::pow(l * scale, 4); + } else { + mask[y][x] = l <= thresh ? 1.f : std::pow(scale / l, 4); + } + } + } +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(mask, mask, width, height, sigma); + } + + const float base = std::pow(4.f, float(amount)/100.f); + const float gamma = hl ? base : 1.f / base; + + LUTf f(32768); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int l = 0; l < 32768; ++l) { + f[l] = std::pow(l / 32768.f, gamma) * 32768.f; + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + float l = lab->L[y][x]; + float blend = mask[y][x]; + float orig = 1.f - blend; + if (l >= 0.f && l < 32768.f) { + lab->L[y][x] = f[l] * blend + l * orig; + if (!hl && l > 1.f) { + // when pushing shadows, scale also the chromaticity + float s = max(lab->L[y][x] / l * 0.5f, 1.f) * blend; + float a = lab->a[y][x]; + float b = lab->b[y][x]; + lab->a[y][x] = a * s + a * orig; + lab->b[y][x] = b * s + b * orig; + } + } + } + } + }; + + if (params->sh.highlights > 0) { + apply(params->sh.highlights, params->sh.htonalwidth, true); + } + + if (params->sh.shadows > 0) { + apply(params->sh.shadows, params->sh.stonalwidth, false); + } +} + +} // namespace rtengine diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 0adf4ce84..b57f3ae50 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1187,17 +1187,17 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT // update blurmap SHMap* shmap = nullptr; - if (params.sh.enabled) { - shmap = new SHMap (fw, fh, false); - double radius = sqrt (double (fw * fw + fh * fh)) / 2.0; - double shradius = params.sh.radius; + // if (params.sh.enabled) { + // shmap = new SHMap (fw, fh, false); + // double radius = sqrt (double (fw * fw + fh * fh)) / 2.0; + // double shradius = params.sh.radius; - if (!params.sh.hq) { - shradius *= radius / 1800.0; - } + // if (!params.sh.hq) { + // shradius *= radius / 1800.0; + // } - shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 16); - } + // shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 16); + // } // RGB processing double expcomp = params.toneCurve.expcomp; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 0e9629498..cec3943d4 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -884,17 +884,17 @@ private: // update blurmap SHMap* shmap = nullptr; - if (params.sh.enabled) { - shmap = new SHMap (fw, fh, true); - double radius = sqrt (double (fw * fw + fh * fh)) / 2.0; - double shradius = params.sh.radius; + // if (params.sh.enabled) { + // shmap = new SHMap (fw, fh, true); + // double radius = sqrt (double (fw * fw + fh * fh)) / 2.0; + // double shradius = params.sh.radius; - if (!params.sh.hq) { - shradius *= radius / 1800.0; - } + // if (!params.sh.hq) { + // shradius *= radius / 1800.0; + // } - shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 1); - } + // shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 1); + // } // RGB processing