From 1a3fd9f157858b9e69709dae44afd1482ae68ac4 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 25 Oct 2018 16:46:11 +0200 Subject: [PATCH 01/11] Added new color toning method "L*a*b* regions" Allows to specify various "regions" of the image with masks, and to correct for hue, saturation and lightness. Inspired by the existing L*a*b* grid (in turn taken from darktable) --- rtdata/languages/default | 48 +++-- rtengine/CMakeLists.txt | 1 + rtengine/dcrop.cc | 1 + rtengine/improccoordinator.cc | 1 + rtengine/improcfun.cc | 36 ++++ rtengine/improcfun.h | 1 + rtengine/iplabregions.cc | 159 ++++++++++++++ rtengine/procparams.cc | 125 +++++++++++- rtengine/procparams.h | 16 ++ rtengine/rt_math.h | 16 ++ rtengine/rtthumbnail.cc | 1 + rtengine/simpleprocess.cc | 1 + rtgui/colortoning.cc | 375 +++++++++++++++++++++++++++++++++- rtgui/colortoning.h | 40 ++++ rtgui/labgrid.cc | 41 +++- rtgui/labgrid.h | 10 +- rtgui/paramsedited.cc | 12 ++ rtgui/paramsedited.h | 2 + 18 files changed, 854 insertions(+), 32 deletions(-) create mode 100644 rtengine/iplabregions.cc diff --git a/rtdata/languages/default b/rtdata/languages/default index cc978ce92..40ff2ff88 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -398,7 +398,7 @@ HISTORY_MSG_145;Microcontrast - Uniformity HISTORY_MSG_146;Edge sharpening HISTORY_MSG_147;ES - Luminance only HISTORY_MSG_148;Microcontrast -HISTORY_MSG_149;Microcontrast - 3×3 matrix +HISTORY_MSG_149;Microcontrast - 33 matrix HISTORY_MSG_150;Post-demosaic artifact/noise red. HISTORY_MSG_151;Vibrance HISTORY_MSG_152;Vib - Pastel tones @@ -728,6 +728,14 @@ HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction +HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - L*a*b* region list +HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction +HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - L*a*b* region saturation +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - L*a*b* region lightness +HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - L*a*b* region H mask +HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - L*a*b* region C mask +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L*a*b* region L mask +HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - L*a*b* region show mask HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold HISTORY_MSG_HISTMATCHING;Auto-matched tone curve @@ -808,7 +816,7 @@ IPTCPANEL_CITY;City IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. IPTCPANEL_COPYHINT;Copy IPTC settings to clipboard. IPTCPANEL_COPYRIGHT;Copyright notice -IPTCPANEL_COPYRIGHTHINT;Enter a Notice on the current owner of the Copyright for this image, such as ©2008 Jane Doe. +IPTCPANEL_COPYRIGHTHINT;Enter a Notice on the current owner of the Copyright for this image, such as 2008 Jane Doe. IPTCPANEL_COUNTRY;Country IPTCPANEL_COUNTRYHINT;Enter the name of the country pictured in this image. IPTCPANEL_CREATOR;Creator @@ -1367,9 +1375,9 @@ TP_COARSETRAF_TOOLTIP_ROTLEFT;Rotate left.\n\nShortcuts:\n[ - Multiple Ed TP_COARSETRAF_TOOLTIP_ROTRIGHT;Rotate right.\n\nShortcuts:\n] - Multiple Editor Tabs Mode,\nAlt-] - Single Editor Tab Mode. TP_COARSETRAF_TOOLTIP_VFLIP;Flip vertically. TP_COLORAPP_ADAPTSCENE;Scene absolute luminance -TP_COLORAPP_ADAPTSCENE_TOOLTIP;Absolute luminance of the scene environment (cd/m²).\n1) Calculated from the Exif data:\nShutter speed - ISO speed - F number - camera exposure correction.\n2) Calculated from the raw white point and RT's Exposure Compensation slider. -TP_COLORAPP_ADAPTVIEWING;Viewing absolute luminance (cd/m²) -TP_COLORAPP_ADAPTVIEWING_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16cd/m²). +TP_COLORAPP_ADAPTSCENE_TOOLTIP;Absolute luminance of the scene environment (cd/m).\n1) Calculated from the Exif data:\nShutter speed - ISO speed - F number - camera exposure correction.\n2) Calculated from the raw white point and RT's Exposure Compensation slider. +TP_COLORAPP_ADAPTVIEWING;Viewing absolute luminance (cd/m) +TP_COLORAPP_ADAPTVIEWING_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16cd/m). TP_COLORAPP_ADAP_AUTO_TOOLTIP;If the checkbox is checked (recommended) RawTherapee calculates an optimum value from the Exif data.\nTo set the value manually, uncheck the checkbox first. TP_COLORAPP_ALGO;Algorithm TP_COLORAPP_ALGO_ALL;All @@ -1406,7 +1414,7 @@ TP_COLORAPP_FREE;Free temp+green + CAT02 + [output] TP_COLORAPP_GAMUT;Gamut control (L*a*b*) TP_COLORAPP_GAMUT_TOOLTIP;Allow gamut control in L*a*b* mode. TP_COLORAPP_HUE;Hue (h) -TP_COLORAPP_HUE_TOOLTIP;Hue (h) - angle between 0° and 360°. +TP_COLORAPP_HUE_TOOLTIP;Hue (h) - angle between 0 and 360. TP_COLORAPP_LABEL;CIE Color Appearance Model 2002 TP_COLORAPP_LABEL_CAM02;Image Adjustments TP_COLORAPP_LABEL_SCENE;Scene Conditions @@ -1458,6 +1466,16 @@ TP_COLORTONING_LAB;L*a*b* blending TP_COLORTONING_LABEL;Color Toning TP_COLORTONING_LABGRID;L*a*b* color correction grid TP_COLORTONING_LABGRID_VALUES;HL: a=%1 b=%2\nS: a=%3 b=%4 +TP_COLORTONING_LABREGION_LIST_TITLE;Correction +TP_COLORTONING_LABREGION_SATURATION;Saturation +TP_COLORTONING_LABREGION_LIGHTNESS;Lightness +TP_COLORTONING_LABREGION_MASK;Mask +TP_COLORTONING_LABREGION_HUEMASK;H +TP_COLORTONING_LABREGION_CHROMATICITYMASK;C +TP_COLORTONING_LABREGION_LIGHTNESSMASK;L +TP_COLORTONING_LABREGION_ABVALUES;a=%1 b=%2 +TP_COLORTONING_LABREGION_SHOWMASK;Show mask +TP_COLORTONING_LABREGIONS;L*a*b* correction regions TP_COLORTONING_LUMA;Luminance TP_COLORTONING_LUMAMODE;Preserve luminance TP_COLORTONING_LUMAMODE_TOOLTIP;If enabled, when you change color (red, green, cyan, blue, etc.) the luminance of each pixel is preserved. @@ -1553,16 +1571,16 @@ TP_DIRPYRDENOISE_MEDIAN_METHOD_RGB;RGB TP_DIRPYRDENOISE_MEDIAN_METHOD_TOOLTIP;When using the "Luminance only" and "L*a*b*" methods, median filtering will be performed just after the wavelet step in the noise reduction pipeline.\nWhen using the "RGB" mode, it will be performed at the very end of the noise reduction pipeline. TP_DIRPYRDENOISE_MEDIAN_METHOD_WEIGHTED;Weighted L* (little) + a*b* (normal) TP_DIRPYRDENOISE_MEDIAN_PASSES;Median iterations -TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Applying three median filter iterations with a 3×3 window size often leads to better results than using one median filter iteration with a 7×7 window size. +TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Applying three median filter iterations with a 33 window size often leads to better results than using one median filter iteration with a 77 window size. TP_DIRPYRDENOISE_MEDIAN_TYPE;Median type -TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Apply a median filter of the desired window size. The larger the window's size, the longer it takes.\n\n3×3 soft: treats 5 pixels in a 3×3 pixel window.\n3×3: treats 9 pixels in a 3×3 pixel window.\n5×5 soft: treats 13 pixels in a 5×5 pixel window.\n5×5: treats 25 pixels in a 5×5 pixel window.\n7×7: treats 49 pixels in a 7×7 pixel window.\n9×9: treats 81 pixels in a 9×9 pixel window.\n\nSometimes it is possible to achieve higher quality running several iterations with a smaller window size than one iteration with a larger one. +TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Apply a median filter of the desired window size. The larger the window's size, the longer it takes.\n\n33 soft: treats 5 pixels in a 33 pixel window.\n33: treats 9 pixels in a 33 pixel window.\n55 soft: treats 13 pixels in a 55 pixel window.\n55: treats 25 pixels in a 55 pixel window.\n77: treats 49 pixels in a 77 pixel window.\n99: treats 81 pixels in a 99 pixel window.\n\nSometimes it is possible to achieve higher quality running several iterations with a smaller window size than one iteration with a larger one. TP_DIRPYRDENOISE_SLI;Slider -TP_DIRPYRDENOISE_TYPE_3X3;3×3 -TP_DIRPYRDENOISE_TYPE_3X3SOFT;3×3 soft -TP_DIRPYRDENOISE_TYPE_5X5;5×5 -TP_DIRPYRDENOISE_TYPE_5X5SOFT;5×5 soft -TP_DIRPYRDENOISE_TYPE_7X7;7×7 -TP_DIRPYRDENOISE_TYPE_9X9;9×9 +TP_DIRPYRDENOISE_TYPE_3X3;33 +TP_DIRPYRDENOISE_TYPE_3X3SOFT;33 soft +TP_DIRPYRDENOISE_TYPE_5X5;55 +TP_DIRPYRDENOISE_TYPE_5X5SOFT;55 soft +TP_DIRPYRDENOISE_TYPE_7X7;77 +TP_DIRPYRDENOISE_TYPE_9X9;99 TP_DIRPYREQUALIZER_ALGO;Skin Color Range TP_DIRPYREQUALIZER_ALGO_TOOLTIP;Fine: closer to the colors of the skin, minimizing the action on other colors\nLarge: avoid more artifacts. TP_DIRPYREQUALIZER_ARTIF;Reduce artifacts @@ -2008,7 +2026,7 @@ TP_SHARPENING_USM;Unsharp Mask TP_SHARPENMICRO_AMOUNT;Quantity TP_SHARPENMICRO_CONTRAST;Contrast threshold TP_SHARPENMICRO_LABEL;Microcontrast -TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5 +TP_SHARPENMICRO_MATRIX;33 matrix instead of 55 TP_SHARPENMICRO_UNIFORMITY;Uniformity TP_SOFTLIGHT_LABEL;Soft Light TP_SOFTLIGHT_STRENGTH;Strength diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 759316e33..063fec2f7 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -128,6 +128,7 @@ set(RTENGINESOURCEFILES vng4_demosaic_RT.cc ipsoftlight.cc guidedfilter.cc + iplabregions.cc ) if(LENSFUN_HAS_LOAD_DIRECTORY) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 3b1ebfdab..5859493e7 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -896,6 +896,7 @@ void Crop::update(int todo) // parent->ipf.MSR(labnCrop, labnCrop->W, labnCrop->H, 1); parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); parent->ipf.vibrance(labnCrop); + parent->ipf.labColorCorrectionRegions(labnCrop); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { parent->ipf.EPDToneMap(labnCrop, 5, skip); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 5cc93d3c7..9d552f747 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -717,6 +717,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) histLCurve.clear(); ipf.chromiLuminanceCurve(nullptr, pW, nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, histCCurve, histLCurve); ipf.vibrance(nprevl); + ipf.labColorCorrectionRegions(nprevl); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { ipf.EPDToneMap(nprevl, 5, scale); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 7d9d080e2..013a629fd 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -4151,6 +4151,42 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW } } + //------------------------------------------------------------------------- + // support for pipettes for the new LabRegions color toning mode this is a + // hack to fill the pipette buffers also when + // !params->labCurve.enabled. It is ugly, but it's the smallest code + // change that I could find + //------------------------------------------------------------------------- + class TempParams { + const ProcParams **p_; + const ProcParams *old_; + ProcParams tmp_; + + public: + explicit TempParams(const ProcParams **p): p_(p) + { + old_ = *p; + tmp_.labCurve.enabled = true; + *p_ = &tmp_; + } + + ~TempParams() + { + *p_ = old_; + } + }; + std::unique_ptr tempparams; + bool pipette_for_colortoning_labregions = + editPipette && + params->colorToning.enabled && params->colorToning.method == "LabRegions"; + if (!params->labCurve.enabled && pipette_for_colortoning_labregions) { + utili = autili = butili = ccutili = cclutili = clcutili = false; + tempparams.reset(new TempParams(¶ms)); + curve.makeIdentity(); + } + //------------------------------------------------------------------------- + + if (!params->labCurve.enabled) { if (editPipette && (editID == EUID_Lab_LCurve || editID == EUID_Lab_aCurve || editID == EUID_Lab_bCurve || editID == EUID_Lab_LHCurve || editID == EUID_Lab_CHCurve || editID == EUID_Lab_HHCurve || editID == EUID_Lab_CLCurve || editID == EUID_Lab_CCurve || editID == EUID_Lab_LCCurve)) { // fill pipette buffer with zeros to avoid crashes diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index a97ecef40..982c48cfe 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -344,6 +344,7 @@ public: void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); void shadowsHighlights(LabImage *lab); void softLight(float *red, float *green, float *blue, int istart, int jstart, int tW, int tH, int TS); + void labColorCorrectionRegions(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); diff --git a/rtengine/iplabregions.cc b/rtengine/iplabregions.cc new file mode 100644 index 000000000..e69f91d5a --- /dev/null +++ b/rtengine/iplabregions.cc @@ -0,0 +1,159 @@ +/* -*- 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 "guidedfilter.h" + +namespace rtengine { + +void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) +{ + if (!params->colorToning.enabled || params->colorToning.method != "LabRegions") { + return; + } + + const float factor = ColorToningParams::LABGRID_CORR_MAX * 3.f; + const float scaling = 1.f; + + int n = params->colorToning.labregions.size(); + std::vector> hmask(n); + std::vector> cmask(n); + std::vector> lmask(n); + + for (int i = 0; i < n; ++i) { + auto &r = params->colorToning.labregions[i]; + if (!r.hueMask.empty() && r.hueMask[0] != FCT_Linear) { + hmask[i].reset(new FlatCurve(r.hueMask, true)); + } + if (!r.chromaticityMask.empty() && r.chromaticityMask[0] != FCT_Linear) { + cmask[i].reset(new FlatCurve(r.chromaticityMask, false)); + } + if (!r.lightnessMask.empty() && r.lightnessMask[0] != FCT_Linear) { + lmask[i].reset(new FlatCurve(r.lightnessMask, false)); + } + } + + array2D guide(lab->W, lab->H, lab->L, ARRAY2D_BYREFERENCE); + std::vector> abmask(n); + std::vector> Lmask(n); + for (int i = 0; i < n; ++i) { + abmask[i](lab->W, lab->H); + Lmask[i](lab->W, lab->H); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < lab->H; ++y) { + for (int x = 0; x < lab->W; ++x) { + float l = lab->L[y][x]; + float a = lab->a[y][x]; + float b = lab->b[y][x]; + float c, h; + Color::Lab2Lch(a, b, c, h); + float c1 = lin2log(c * (327.68f / 48000.f), 10.f); + float h1 = Color::huelab_to_huehsv2(h); + h1 = h1 + 1.f/6.f; + if (h1 > 1.f) { + h1 -= 1.f; + } + h1 = lin2log(h1, 3.f); + float l1 = l / 32768.f; + + if (params->colorToning.labregionsShowMask >= 0 && params->colorToning.labregionsShowMask < n) { + int idx = params->colorToning.labregionsShowMask; + auto &hm = hmask[idx]; + auto &cm = cmask[idx]; + auto &lm = lmask[idx]; + float blend = (hm ? hm->getVal(h1) : 1.f) * (cm ? cm->getVal(c1) : 1.f) * (lm ? lm->getVal(l1) : 1.f); + abmask[idx][y][x] = blend; + } else { + for (int i = 0; i < n; ++i) { + auto &hm = hmask[i]; + auto &cm = cmask[i]; + auto &lm = lmask[i]; + float blend = (hm ? hm->getVal(h1) : 1.f) * (cm ? cm->getVal(c1) : 1.f) * (lm ? lm->getVal(l1) : 1.f); + Lmask[i][y][x] = abmask[i][y][x] = blend; + } + } + } + } + + for (int i = 0; i < n; ++i) { + rtengine::guidedFilter(guide, abmask[i], abmask[i], max(int(4 / scale + 0.5), 1), 0.001, multiThread); + rtengine::guidedFilter(guide, Lmask[i], Lmask[i], max(int(25 / scale + 0.5), 1), 0.0001, multiThread); + } + + if (params->colorToning.labregionsShowMask >= 0 && params->colorToning.labregionsShowMask < n) { + int idx = params->colorToning.labregionsShowMask; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < lab->H; ++y) { + for (int x = 0; x < lab->W; ++x) { + auto blend = abmask[idx][y][x]; + lab->a[y][x] = 0.f; + lab->b[y][x] = blend * 42000.f; + lab->L[y][x] = LIM(lab->L[y][x] + 32768.f * blend, 0.f, 32768.f); + } + } + + return; + } + + const auto abcoord = + [](float x) -> float + { + const float m = ColorToningParams::LABGRID_CORR_MAX; + return SGN(x) * log2lin(std::abs(x) / m, 4.f) * m; + }; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < lab->H; ++y) { + for (int x = 0; x < lab->W; ++x) { + float l = lab->L[y][x]; + float a = lab->a[y][x]; + float b = lab->b[y][x]; + + for (int i = 0; i < n; ++i) { + auto &r = params->colorToning.labregions[i]; + float blend = abmask[i][y][x]; + float s = 1.f + r.saturation / 100.f; + float a_new = s * (a + 32768.f * abcoord(r.a) / factor / scaling); + float b_new = s * (b + 32768.f * abcoord(r.b) / factor / scaling); + float l_new = l * (1.f + r.lightness / 1000.f); + l = intp(Lmask[i][y][x], l_new, l); + a = intp(blend, a_new, a); + b = intp(blend, b_new, b); + } + lab->L[y][x] = l; + lab->a[y][x] = a; + lab->b[y][x] = b; + } + } +} + +} // namespace rtengine diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 526a36765..bfb91aea8 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -614,6 +614,66 @@ bool LocalContrastParams::operator!=(const LocalContrastParams &other) const const double ColorToningParams::LABGRID_CORR_MAX = 12000.f; const double ColorToningParams::LABGRID_CORR_SCALE = 3.f; +ColorToningParams::LabCorrectionRegion::LabCorrectionRegion(): + a(0), + b(0), + saturation(0), + lightness(0), + hueMask{ + FCT_MinMaxCPoints, + 0.166666667, + 1., + 0.35, + 0.35, + 0.8287775246, + 1., + 0.35, + 0.35 + }, + chromaticityMask{ + FCT_MinMaxCPoints, + 0., + 1., + 0.35, + 0.35, + 1., + 1., + 0.35, + 0.35 + }, + lightnessMask{ + FCT_MinMaxCPoints, + 0., + 1., + 0.35, + 0.35, + 1., + 1., + 0.35, + 0.35 + } +{ +} + + +bool ColorToningParams::LabCorrectionRegion::operator==(const LabCorrectionRegion &other) const +{ + return a == other.a + && b == other.b + && saturation == other.saturation + && lightness == other.lightness + && hueMask == other.hueMask + && chromaticityMask == other.chromaticityMask + && lightnessMask == other.lightnessMask; +} + + +bool ColorToningParams::LabCorrectionRegion::operator!=(const LabCorrectionRegion &other) const +{ + return !(*this == other); +} + + ColorToningParams::ColorToningParams() : enabled(false), autosat(true), @@ -688,7 +748,9 @@ ColorToningParams::ColorToningParams() : labgridALow(0.0), labgridBLow(0.0), labgridAHigh(0.0), - labgridBHigh(0.0) + labgridBHigh(0.0), + labregions{LabCorrectionRegion()}, + labregionsShowMask(-1) { } @@ -724,7 +786,9 @@ bool ColorToningParams::operator ==(const ColorToningParams& other) const && labgridALow == other.labgridALow && labgridBLow == other.labgridBLow && labgridAHigh == other.labgridAHigh - && labgridBHigh == other.labgridBHigh; + && labgridBHigh == other.labgridBHigh + && labregions == other.labregions + && labregionsShowMask == other.labregionsShowMask; } bool ColorToningParams::operator !=(const ColorToningParams& other) const @@ -3383,6 +3447,20 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->colorToning.labgridBLow, "ColorToning", "LabGridBLow", colorToning.labgridBLow, keyFile); saveToKeyfile(!pedited || pedited->colorToning.labgridAHigh, "ColorToning", "LabGridAHigh", colorToning.labgridAHigh, keyFile); saveToKeyfile(!pedited || pedited->colorToning.labgridBHigh, "ColorToning", "LabGridBHigh", colorToning.labgridBHigh, keyFile); + if (!pedited || pedited->colorToning.labregions) { + for (size_t j = 0; j < colorToning.labregions.size(); ++j) { + std::string n = std::to_string(j+1); + auto &l = colorToning.labregions[j]; + putToKeyfile("ColorToning", Glib::ustring("LabRegionA_") + n, l.a, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionB_") + n, l.b, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionSaturation_") + n, l.saturation, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionLightness_") + n, l.lightness, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionHueMask_") + n, l.hueMask, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionChromaticityMask_") + n, l.chromaticityMask, keyFile); + putToKeyfile("ColorToning", Glib::ustring("LabRegionLightnessMask_") + n, l.lightnessMask, keyFile); + } + } + saveToKeyfile(!pedited || pedited->colorToning.labregionsShowMask, "ColorToning", "LabRegionsShowMask", colorToning.labregionsShowMask, keyFile); // Raw saveToKeyfile(!pedited || pedited->raw.darkFrame, "RAW", "DarkFrame", relativePathIfInside(fname, fnameAbsolute, raw.dark_frame), keyFile); @@ -4740,6 +4818,49 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) colorToning.labgridBLow *= scale; colorToning.labgridBHigh *= scale; } + std::vector lg; + bool found = false; + bool done = false; + for (int i = 1; !done; ++i) { + ColorToningParams::LabCorrectionRegion cur; + done = true; + std::string n = std::to_string(i); + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionA_") + n, pedited, cur.a, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionB_") + n, pedited, cur.b, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionSaturation_") + n, pedited, cur.saturation, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionLightness_") + n, pedited, cur.lightness, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionHueMask_") + n, pedited, cur.hueMask, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionChromaticityMask_") + n, pedited, cur.chromaticityMask, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (assignFromKeyfile(keyFile, "ColorToning", Glib::ustring("LabRegionLightnessMask_") + n, pedited, cur.lightnessMask, pedited->colorToning.labregions)) { + found = true; + done = false; + } + if (!done) { + lg.emplace_back(cur); + } + } + if (found) { + colorToning.labregions = std::move(lg); + } + assignFromKeyfile(keyFile, "ColorToning", "LabRegionsShowMask", pedited, colorToning.labregionsShowMask, pedited->colorToning.labregionsShowMask); } if (keyFile.has_group("RAW")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 22868a844..ad0339325 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -455,6 +455,22 @@ struct ColorToningParams { static const double LABGRID_CORR_MAX; static const double LABGRID_CORR_SCALE; + struct LabCorrectionRegion { + double a; + double b; + double saturation; + double lightness; + std::vector hueMask; + std::vector chromaticityMask; + std::vector lightnessMask; + + LabCorrectionRegion(); + bool operator==(const LabCorrectionRegion &other) const; + bool operator!=(const LabCorrectionRegion &other) const; + }; + std::vector labregions; + int labregionsShowMask; + ColorToningParams(); bool operator ==(const ColorToningParams& other) const; diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 8d55b492e..9342f5430 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -220,5 +220,21 @@ std::array dotProduct(const std::array, 3> &a, const std: return res; } + +template +T lin2log(T x, T base) +{ + constexpr T one(1); + return std::log(x * (base - one) + one) / std::log(base); +} + + +template +T log2lin(T x, T base) +{ + constexpr T one(1); + return (std::pow(base, x) - one) / (base - one); +} + } diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 79dee36bf..7adb88490 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1363,6 +1363,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.chromiLuminanceCurve (nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); ipf.vibrance (labView); + ipf.labColorCorrectionRegions(labView); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || !params.colorappearance.enabled) { ipf.EPDToneMap (labView, 5, 6); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 06e2b13bf..cf933bdcb 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1082,6 +1082,7 @@ private: ipf.vibrance (labView); + ipf.labColorCorrectionRegions(labView); if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { ipf.impulsedenoise (labView); diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 8b357c3f9..79e913019 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -11,6 +11,8 @@ using namespace rtengine; using namespace rtengine::procparams; +static constexpr int ID_LABREGION_HUE = 5; + ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLORTONING_LABEL"), false, true) { nextbw = 0; @@ -25,6 +27,7 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR method->append (M("TP_COLORTONING_SPLITCOCO")); method->append (M("TP_COLORTONING_SPLITLR")); method->append(M("TP_COLORTONING_LABGRID")); + method->append(M("TP_COLORTONING_LABREGIONS")); method->set_active (0); method->set_tooltip_text (M("TP_COLORTONING_METHOD_TOOLTIP")); @@ -321,7 +324,7 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR auto m = ProcEventMapper::getInstance(); EvColorToningLabGridValue = m->newEvent(RGBCURVE, "HISTORY_MSG_COLORTONING_LABGRID_VALUE"); labgridBox = Gtk::manage(new Gtk::HBox()); - labgrid = Gtk::manage(new LabGrid(EvColorToningLabGridValue)); + labgrid = Gtk::manage(new LabGrid(EvColorToningLabGridValue, M("TP_COLORTONING_LABGRID_VALUES"))); labgridBox->pack_start(*labgrid, true, true); labgridReset = Gtk::manage(new Gtk::Button ()); labgridReset->add (*Gtk::manage(new RTImage ("undo-small.png", "redo-small.png"))); @@ -336,6 +339,116 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR pack_start(*labgridBox, Gtk::PACK_EXPAND_WIDGET, 4); //------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // LAB regions + + const auto add_button = + [&](Gtk::Button *btn, Gtk::Box *box) -> void + { + setExpandAlignProperties(btn, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); + btn->set_relief(Gtk::RELIEF_NONE); + btn->get_style_context()->add_class(GTK_STYLE_CLASS_FLAT); + btn->set_can_focus(false); + btn->set_size_request(-1, 20); + box->pack_start(*btn, false, false); + }; + + EvLabRegionList = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_LIST"); + EvLabRegionAB = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_AB"); + EvLabRegionSaturation = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_SATURATION"); + EvLabRegionLightness = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS"); + EvLabRegionHueMask = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_HUEMASK"); + EvLabRegionChromaticityMask = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK"); + EvLabRegionLightnessMask = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK"); + EvLabRegionShowMask = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK"); + labRegionBox = Gtk::manage(new Gtk::VBox()); + + labRegionList = Gtk::manage(new Gtk::ListViewText(3)); + labRegionList->set_size_request(-1, 100); + labRegionList->set_column_title(0, "#"); + labRegionList->set_column_title(1, M("TP_COLORTONING_LABREGION_LIST_TITLE")); + labRegionList->set_column_title(2, M("TP_COLORTONING_LABREGION_MASK")); + labRegionList->set_activate_on_single_click(true); + labRegionSelectionConn = labRegionList->get_selection()->signal_changed().connect(sigc::mem_fun(this, &ColorToning::onLabRegionSelectionChanged)); + Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*labRegionList, Gtk::PACK_EXPAND_WIDGET); + Gtk::VBox *vb = Gtk::manage(new Gtk::VBox()); + labRegionAdd = Gtk::manage(new Gtk::Button()); + labRegionAdd->add(*Gtk::manage(new RTImage("add-small.png"))); + labRegionAdd->signal_clicked().connect(sigc::mem_fun(*this, &ColorToning::labRegionAddPressed)); + add_button(labRegionAdd, vb); + labRegionRemove = Gtk::manage(new Gtk::Button()); + labRegionRemove->add(*Gtk::manage(new RTImage("remove-small.png"))); + labRegionRemove->signal_clicked().connect(sigc::mem_fun(*this, &ColorToning::labRegionRemovePressed)); + add_button(labRegionRemove, vb); + labRegionUp = Gtk::manage(new Gtk::Button()); + labRegionUp->add(*Gtk::manage(new RTImage("arrow-up-small.png"))); + labRegionUp->signal_clicked().connect(sigc::mem_fun(*this, &ColorToning::labRegionUpPressed)); + add_button(labRegionUp, vb); + labRegionDown = Gtk::manage(new Gtk::Button()); + labRegionDown->add(*Gtk::manage(new RTImage("arrow-down-small.png"))); + labRegionDown->signal_clicked().connect(sigc::mem_fun(*this, &ColorToning::labRegionDownPressed)); + add_button(labRegionDown, vb); + hb->pack_start(*vb, Gtk::PACK_SHRINK); + labRegionBox->pack_start(*hb, true, true); + + labRegionAB = Gtk::manage(new LabGrid(EvLabRegionAB, M("TP_COLORTONING_LABREGION_ABVALUES"), false)); + hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*labRegionAB, true, true); + labRegionABReset = Gtk::manage(new Gtk::Button()); + labRegionABReset->set_tooltip_markup(M("ADJUSTER_RESET_TO_DEFAULT")); + labRegionABReset->add(*Gtk::manage(new RTImage("undo-small.png", "redo-small.png"))); + labRegionABReset->signal_button_release_event().connect(sigc::mem_fun(*this, &ColorToning::labRegionResetPressed)); + add_button(labRegionABReset, hb); + labRegionBox->pack_start(*hb); + + labRegionSaturation = Gtk::manage(new Adjuster(M("TP_COLORTONING_LABREGION_SATURATION"), -100, 100, 1, 0)); + labRegionSaturation->setAdjusterListener(this); + labRegionBox->pack_start(*labRegionSaturation); + labRegionLightness = Gtk::manage(new Adjuster(M("TP_COLORTONING_LABREGION_LIGHTNESS"), -100, 100, 1, 0)); + labRegionLightness->setAdjusterListener(this); + labRegionBox->pack_start(*labRegionLightness); + + CurveEditorGroup *labRegionEditorG = Gtk::manage(new CurveEditorGroup(options.lastColorToningCurvesDir, M("TP_COLORTONING_LABREGION_MASK"))); + labRegionEditorG->setCurveListener(this); + + labRegionHueMask = static_cast(labRegionEditorG->addCurve(CT_Flat, M("TP_COLORTONING_LABREGION_HUEMASK"), nullptr, false, true)); + labRegionHueMask->setIdentityValue(0.); + labRegionHueMask->setResetCurve(FlatCurveType(default_params.labregions[0].hueMask[0]), default_params.labregions[0].hueMask); + labRegionHueMask->setCurveColorProvider(this, ID_LABREGION_HUE); + labRegionHueMask->setBottomBarColorProvider(this, ID_LABREGION_HUE); + labRegionHueMask->setEditID(EUID_Lab_HHCurve, BT_SINGLEPLANE_FLOAT); + + labRegionChromaticityMask = static_cast(labRegionEditorG->addCurve(CT_Flat, M("TP_COLORTONING_LABREGION_CHROMATICITYMASK"), nullptr, false, false)); + labRegionChromaticityMask->setIdentityValue(0.); + labRegionChromaticityMask->setResetCurve(FlatCurveType(default_params.labregions[0].chromaticityMask[0]), default_params.labregions[0].chromaticityMask); + labRegionChromaticityMask->setBottomBarColorProvider(this, ID_LABREGION_HUE+1); + labRegionChromaticityMask->setEditID(EUID_Lab_CCurve, BT_SINGLEPLANE_FLOAT); + + labRegionLightnessMask = static_cast(labRegionEditorG->addCurve(CT_Flat, M("TP_COLORTONING_LABREGION_LIGHTNESSMASK"), nullptr, false, false)); + labRegionLightnessMask->setIdentityValue(0.); + labRegionLightnessMask->setResetCurve(FlatCurveType(default_params.labregions[0].lightnessMask[0]), default_params.labregions[0].lightnessMask); + labRegionLightnessMask->setBottomBarBgGradient(milestones); + labRegionLightnessMask->setEditID(EUID_Lab_LCurve, BT_SINGLEPLANE_FLOAT); + + labRegionData = default_params.labregions; + labRegionSelected = 0; + { + auto n = labRegionList->append("1"); + labRegionList->set_text(n, 1, "a=0 b=0 s=0 l=0"); + } + + labRegionEditorG->curveListComplete(); + labRegionEditorG->show(); + labRegionBox->pack_start(*labRegionEditorG, Gtk::PACK_SHRINK, 2); + + labRegionShowMask = Gtk::manage(new Gtk::CheckButton(M("TP_COLORTONING_LABREGION_SHOWMASK"))); + labRegionShowMask->signal_toggled().connect(sigc::mem_fun(*this, &ColorToning::labRegionShowMaskChanged)); + labRegionBox->pack_start(*labRegionShowMask, Gtk::PACK_SHRINK, 4); + + pack_start(*labRegionBox, Gtk::PACK_EXPAND_WIDGET, 4); + //------------------------------------------------------------------------ + show_all(); disableListener(); @@ -358,6 +471,7 @@ void ColorToning::setListener(ToolPanelListener *tpl) { ToolPanel::setListener(tpl); labgrid->setListener(tpl); + labRegionAB->setListener(tpl); } /* @@ -409,6 +523,20 @@ void ColorToning::read (const ProcParams* pp, const ParamsEdited* pedited) clshape->setCurve (pp->colorToning.clcurve); cl2shape->setCurve (pp->colorToning.cl2curve); + labRegionData = pp->colorToning.labregions; + if (labRegionData.empty()) { + labRegionData.emplace_back(rtengine::ColorToningParams::LabCorrectionRegion()); + } + if (pp->colorToning.labregionsShowMask >= 0) { + labRegionSelected = pp->colorToning.labregionsShowMask; + labRegionShowMask->set_active(true); + } else { + labRegionSelected = 0; + labRegionShowMask->set_active(false); + } + labRegionPopulateList(); + labRegionShow(labRegionSelected); + if (pedited) { redlow->setEditedState (pedited->colorToning.redlow ? Edited : UnEdited); greenlow->setEditedState (pedited->colorToning.greenlow ? Edited : UnEdited); @@ -433,6 +561,9 @@ void ColorToning::read (const ProcParams* pp, const ParamsEdited* pedited) lumamode->set_inconsistent (!pedited->colorToning.lumamode); labgrid->setEdited(pedited->colorToning.labgridALow || pedited->colorToning.labgridBLow || pedited->colorToning.labgridAHigh || pedited->colorToning.labgridBHigh); + + labRegionAB->setEdited(pedited->colorToning.labregions); + labRegionShowMask->set_inconsistent(!pedited->colorToning.labregionsShowMask); } redlow->setValue (pp->colorToning.redlow); @@ -480,6 +611,8 @@ void ColorToning::read (const ProcParams* pp, const ParamsEdited* pedited) method->set_active (4); } else if (pp->colorToning.method == "LabGrid") { method->set_active(5); + } else if (pp->colorToning.method == "LabRegions") { + method->set_active(6); } methodChanged(); @@ -534,6 +667,15 @@ void ColorToning::write (ProcParams* pp, ParamsEdited* pedited) labgrid->getParams(pp->colorToning.labgridALow, pp->colorToning.labgridBLow, pp->colorToning.labgridAHigh, pp->colorToning.labgridBHigh); + labRegionGet(labRegionSelected); + labRegionShow(labRegionSelected, true); + pp->colorToning.labregions = labRegionData; + if (labRegionShowMask->get_active()) { + pp->colorToning.labregionsShowMask = labRegionSelected; + } else { + pp->colorToning.labregionsShowMask = -1; + } + if (pedited) { pedited->colorToning.redlow = redlow->getEditedState (); pedited->colorToning.greenlow = greenlow->getEditedState (); @@ -560,6 +702,9 @@ void ColorToning::write (ProcParams* pp, ParamsEdited* pedited) pedited->colorToning.shadowsColSat = shadowsColSat->getEditedState (); pedited->colorToning.labgridALow = pedited->colorToning.labgridBLow = pedited->colorToning.labgridAHigh = pedited->colorToning.labgridBHigh = labgrid->getEdited(); + + pedited->colorToning.labregions = labRegionAB->getEdited(); + pedited->colorToning.labregionsShowMask = !labRegionShowMask->get_inconsistent(); } if (method->get_active_row_number() == 0) { @@ -574,6 +719,8 @@ void ColorToning::write (ProcParams* pp, ParamsEdited* pedited) pp->colorToning.method = "Splitlr"; } else if (method->get_active_row_number() == 5) { pp->colorToning.method = "LabGrid"; + } else if (method->get_active_row_number() == 6) { + pp->colorToning.method = "LabRegions"; } if (twocolor->get_active_row_number() == 0) { @@ -631,6 +778,7 @@ void ColorToning::setDefaults (const ProcParams* defParams, const ParamsEdited* shadowsColSat->setDefault (defParams->colorToning.shadowsColSat); strength->setDefault (defParams->colorToning.strength); labgrid->setDefault(defParams->colorToning.labgridALow, defParams->colorToning.labgridBLow, defParams->colorToning.labgridAHigh, defParams->colorToning.labgridBHigh); + if (pedited) { redlow->setDefaultEditedState (pedited->colorToning.redlow ? Edited : UnEdited); @@ -649,6 +797,8 @@ void ColorToning::setDefaults (const ProcParams* defParams, const ParamsEdited* shadowsColSat->setDefaultEditedState (pedited->colorToning.shadowsColSat ? Edited : UnEdited); strength->setDefaultEditedState (pedited->colorToning.strength ? Edited : UnEdited); labgrid->setEdited((pedited->colorToning.labgridALow || pedited->colorToning.labgridBLow || pedited->colorToning.labgridAHigh || pedited->colorToning.labgridBHigh) ? Edited : UnEdited); + + labRegionAB->setEdited(pedited->colorToning.labregions ? Edited : UnEdited); } else { redlow->setDefaultEditedState (Irrelevant); greenlow->setDefaultEditedState (Irrelevant); @@ -666,6 +816,7 @@ void ColorToning::setDefaults (const ProcParams* defParams, const ParamsEdited* shadowsColSat->setDefaultEditedState (Irrelevant); strength->setDefaultEditedState (Irrelevant); labgrid->setEdited(Edited); + labRegionAB->setEdited(Edited); } } @@ -814,6 +965,7 @@ void ColorToning::methodChanged () if (!batchMode) { labgridBox->hide(); + labRegionBox->hide(); if (method->get_active_row_number() == 0) { // Lab colorSep->show(); @@ -962,7 +1114,7 @@ void ColorToning::methodChanged () chanMixerBox->hide(); neutrHBox->hide(); lumamode->show(); - } else if (method->get_active_row_number() == 5) { // Lab Grid + } else if (method->get_active_row_number() == 5 || method->get_active_row_number() == 6) { // Lab Grid or Lab Regions colorSep->hide(); colorCurveEditorG->hide(); twocolor->hide(); @@ -981,7 +1133,11 @@ void ColorToning::methodChanged () neutrHBox->hide(); lumamode->hide(); - labgridBox->show(); + if (method->get_active_row_number() == 5) { + labgridBox->show(); + } else { + labRegionBox->show(); + } } } @@ -1038,6 +1194,17 @@ void ColorToning::colorForValue (double valX, double valY, enum ColorCaller::Ele } } else if (callerId == 4) { // color curve vertical and horizontal crosshair Color::hsv2rgb01(float(valY), 1.0f, 0.5f, R, G, B); + } else if (callerId == ID_LABREGION_HUE) { + // TODO + float x = valX - 1.f/6.f; + if (x < 0.f) { + x += 1.f; + } + x = log2lin(x, 3.f); + // float x = valX; + Color::hsv2rgb01(x, 0.5f, 0.5f, R, G, B); + } else if (callerId == ID_LABREGION_HUE+1) { + Color::hsv2rgb01(float(valY), float(valX), 0.5f, R, G, B); } caller->ccRed = double(R); @@ -1057,6 +1224,12 @@ void ColorToning::curveChanged (CurveEditor* ce) listener->panelChanged (EvColorToningCLCurve, M("HISTORY_CUSTOMCURVE")); } else if (ce == cl2shape) { listener->panelChanged (EvColorToningLLCurve, M("HISTORY_CUSTOMCURVE")); + } else if (ce == labRegionHueMask) { + listener->panelChanged(EvLabRegionHueMask, M("HISTORY_CUSTOMCURVE")); + } else if (ce == labRegionChromaticityMask) { + listener->panelChanged(EvLabRegionChromaticityMask, M("HISTORY_CUSTOMCURVE")); + } else if (ce == labRegionLightnessMask) { + listener->panelChanged(EvLabRegionLightnessMask, M("HISTORY_CUSTOMCURVE")); } } } @@ -1158,6 +1331,10 @@ void ColorToning::adjusterChanged(Adjuster* a, double newval) listener->panelChanged (EvColorToningSatProtection, a->getTextValue()); } else if (a == strength) { listener->panelChanged (EvColorToningStrength, a->getTextValue()); + } else if (a == labRegionSaturation) { + listener->panelChanged(EvLabRegionSaturation, a->getTextValue()); + } else if (a == labRegionLightness) { + listener->panelChanged(EvLabRegionLightness, a->getTextValue()); } } @@ -1195,3 +1372,195 @@ bool ColorToning::resetPressed(GdkEventButton* event) labgrid->reset(event->state & GDK_CONTROL_MASK); return false; } + + +bool ColorToning::labRegionResetPressed(GdkEventButton *event) +{ + labRegionAB->reset(event->state & GDK_CONTROL_MASK); + return false; +} + + +void ColorToning::onLabRegionSelectionChanged() +{ + auto s = labRegionList->get_selected(); + if (!s.empty()) { + // update the selected values + labRegionGet(labRegionSelected); + labRegionSelected = s[0]; + labRegionShow(labRegionSelected); + if (labRegionShowMask->get_active()) { + labRegionShowMaskChanged(); + } + } +} + + +void ColorToning::labRegionGet(int idx) +{ + auto &r = labRegionData[idx]; + double la, lb; + labRegionAB->getParams(la, lb, r.a, r.b); + r.saturation = labRegionSaturation->getValue(); + r.lightness = labRegionLightness->getValue(); + r.hueMask = labRegionHueMask->getCurve(); + r.chromaticityMask = labRegionChromaticityMask->getCurve(); + r.lightnessMask = labRegionLightnessMask->getCurve(); +} + + +void ColorToning::labRegionAddPressed() +{ + labRegionData.insert(labRegionData.begin(), rtengine::ColorToningParams::LabCorrectionRegion()); + labRegionSelected = 0; + labRegionPopulateList(); + labRegionShow(labRegionSelected); + + if (listener) { + listener->panelChanged(EvLabRegionList, M("HISTORY_CHANGED")); + } +} + + +void ColorToning::labRegionRemovePressed() +{ + if (labRegionList->size() > 1) { + labRegionData.erase(labRegionData.begin() + labRegionSelected); + labRegionSelected = LIM(labRegionSelected-1, 0, int(labRegionData.size()-1)); + labRegionPopulateList(); + labRegionShow(labRegionSelected); + + if (listener) { + listener->panelChanged(EvLabRegionList, M("HISTORY_CHANGED")); + } + } +} + + +void ColorToning::labRegionUpPressed() +{ + if (labRegionSelected > 0) { + auto r = labRegionData[labRegionSelected]; + labRegionData.erase(labRegionData.begin() + labRegionSelected); + --labRegionSelected; + labRegionData.insert(labRegionData.begin() + labRegionSelected, r); + labRegionPopulateList(); + + if (listener) { + listener->panelChanged(EvLabRegionList, M("HISTORY_CHANGED")); + } + } +} + + +void ColorToning::labRegionDownPressed() +{ + if (labRegionSelected < int(labRegionData.size()-1)) { + auto r = labRegionData[labRegionSelected]; + labRegionData.erase(labRegionData.begin() + labRegionSelected); + ++labRegionSelected; + labRegionData.insert(labRegionData.begin() + labRegionSelected, r); + labRegionPopulateList(); + + if (listener) { + listener->panelChanged(EvLabRegionList, M("HISTORY_CHANGED")); + } + } +} + + +void ColorToning::labRegionShowMaskChanged() +{ + if (listener) { + listener->panelChanged(EvLabRegionShowMask, labRegionShowMask->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + +namespace { + +bool hasMask(const std::vector &dflt, const std::vector &mask) +{ + if (mask.empty() || mask[0] == FCT_Linear || mask == dflt) { + return false; + } else { + return true; + } +} + +} // namespace + + +void ColorToning::labRegionPopulateList() +{ + ConnectionBlocker b(labRegionSelectionConn); + labRegionList->clear_items(); + rtengine::ColorToningParams::LabCorrectionRegion dflt; + + for (size_t i = 0; i < labRegionData.size(); ++i) { + auto &r = labRegionData[i]; + auto j = labRegionList->append(std::to_string(i+1)); + labRegionList->set_text(j, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", int(r.a), int(r.b), r.saturation, r.lightness)); + labRegionList->set_text( + j, 2, Glib::ustring::compose( + "%1%2%3", + hasMask(dflt.hueMask, r.hueMask) ? "H" : "", + hasMask(dflt.chromaticityMask, r.chromaticityMask) ? "C" : "", + hasMask(dflt.lightnessMask, r.lightnessMask) ? "L" : "")); + } +} + + +void ColorToning::labRegionShow(int idx, bool list_only) +{ + bool disable = listener; + if (disable) { + disableListener(); + } + rtengine::ColorToningParams::LabCorrectionRegion dflt; + auto &r = labRegionData[idx]; + if (!list_only) { + labRegionAB->setParams(0, 0, r.a, r.b, false); + labRegionSaturation->setValue(r.saturation); + labRegionLightness->setValue(r.lightness); + labRegionHueMask->setCurve(r.hueMask); + labRegionChromaticityMask->setCurve(r.chromaticityMask); + labRegionLightnessMask->setCurve(r.lightnessMask); + } + labRegionList->set_text(idx, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", int(r.a), int(r.b), r.saturation, r.lightness)); + labRegionList->set_text( + idx, 2, Glib::ustring::compose( + "%1%2%3", + hasMask(dflt.hueMask, r.hueMask) ? "H" : "", + hasMask(dflt.chromaticityMask, r.chromaticityMask) ? "C" : "", + hasMask(dflt.lightnessMask, r.lightnessMask) ? "L" : "")); + Gtk::TreePath pth; + pth.push_back(idx); + labRegionList->get_selection()->select(pth); + if (disable) { + enableListener(); + } +} + + +void ColorToning::setEditProvider(EditDataProvider *provider) +{ + labRegionHueMask->setEditProvider(provider); + labRegionChromaticityMask->setEditProvider(provider); + labRegionLightnessMask->setEditProvider(provider); +} + + +float ColorToning::blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3) +{ + if (ce == labRegionChromaticityMask && chan1 > 0.f) { + return lin2log(chan1, 10.f); + } else if (ce == labRegionHueMask && chan1 > 0.f) { + float x = chan1 + 1.f/6.f; + if (x > 1.f) { + x -= 1.f; + } + return lin2log(x, 3.f); + } + return CurveListener::blendPipetteValues(ce, chan1, chan2, chan3); +} diff --git a/rtgui/colortoning.h b/rtgui/colortoning.h index cb021e242..eff26bd46 100644 --- a/rtgui/colortoning.h +++ b/rtgui/colortoning.h @@ -58,8 +58,21 @@ public: void setListener(ToolPanelListener *tpl); + void setEditProvider(EditDataProvider *provider); + float blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3); + private: bool resetPressed(GdkEventButton* event); + bool labRegionResetPressed(GdkEventButton *event); + void onLabRegionSelectionChanged(); + void labRegionAddPressed(); + void labRegionRemovePressed(); + void labRegionUpPressed(); + void labRegionDownPressed(); + void labRegionShowMaskChanged(); + void labRegionPopulateList(); + void labRegionShow(int idx, bool list_only=false); + void labRegionGet(int idx); //Gtk::HSeparator* satLimiterSep; Gtk::HSeparator* colorSep; @@ -117,6 +130,33 @@ private: LabGrid *labgrid; Gtk::HBox *labgridBox; + rtengine::ProcEvent EvLabRegionList; + rtengine::ProcEvent EvLabRegionAB; + rtengine::ProcEvent EvLabRegionSaturation; + rtengine::ProcEvent EvLabRegionLightness; + rtengine::ProcEvent EvLabRegionHueMask; + rtengine::ProcEvent EvLabRegionChromaticityMask; + rtengine::ProcEvent EvLabRegionLightnessMask; + rtengine::ProcEvent EvLabRegionShowMask; + + Gtk::VBox *labRegionBox; + Gtk::ListViewText *labRegionList; + Gtk::Button *labRegionAdd; + Gtk::Button *labRegionRemove; + Gtk::Button *labRegionUp; + Gtk::Button *labRegionDown; + Gtk::Button *labRegionABReset; + LabGrid *labRegionAB; + Adjuster *labRegionSaturation; + Adjuster *labRegionLightness; + FlatCurveEditor *labRegionHueMask; + FlatCurveEditor *labRegionChromaticityMask; + FlatCurveEditor *labRegionLightnessMask; + Gtk::CheckButton *labRegionShowMask; + std::vector labRegionData; + int labRegionSelected; + sigc::connection labRegionSelectionConn; + IdleRegister idle_register; }; diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc index a52ba9163..fde03194b 100644 --- a/rtgui/labgrid.cc +++ b/rtgui/labgrid.cc @@ -44,20 +44,22 @@ using rtengine::Color; bool LabGrid::notifyListener() { if (listener) { - listener->panelChanged(evt, Glib::ustring::compose(M("TP_COLORTONING_LABGRID_VALUES"), int(low_a), int(low_b), int(high_a), int(high_b))); + listener->panelChanged(evt, Glib::ustring::compose(evtMsg, int(high_a), int(high_b), int(low_a), int(low_b))); } return false; } -LabGrid::LabGrid(rtengine::ProcEvent evt): +LabGrid::LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low): Gtk::DrawingArea(), - evt(evt), litPoint(NONE), + evt(evt), evtMsg(msg), + litPoint(NONE), low_a(0.f), high_a(0.f), low_b(0.f), high_b(0.f), defaultLow_a(0.f), defaultHigh_a(0.f), defaultLow_b(0.f), defaultHigh_b(0.f), listener(nullptr), edited(false), - isDragged(false) + isDragged(false), + low_enabled(enable_low) { set_can_focus(true); add_events(Gdk::EXPOSURE_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK); @@ -201,13 +203,15 @@ bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) cr->stroke(); // drawing points - cr->set_source_rgb(0.1, 0.1, 0.1); - if (litPoint == LOW) { - cr->arc(loa, lob, 5, 0, 2. * rtengine::RT_PI); - } else { - cr->arc(loa, lob, 3, 0, 2. * rtengine::RT_PI); + if (low_enabled) { + cr->set_source_rgb(0.1, 0.1, 0.1); + if (litPoint == LOW) { + cr->arc(loa, lob, 5, 0, 2. * rtengine::RT_PI); + } else { + cr->arc(loa, lob, 3, 0, 2. * rtengine::RT_PI); + } + cr->fill(); } - cr->fill(); cr->set_source_rgb(0.9, 0.9, 0.9); if (litPoint == HIGH) { @@ -298,7 +302,7 @@ bool LabGrid::on_motion_notify_event(GdkEventMotion *event) const float thrs = 0.05f; const float distlo = (la - ma) * (la - ma) + (lb - mb) * (lb - mb); const float disthi = (ha - ma) * (ha - ma) + (hb - mb) * (hb - mb); - if (distlo < thrs * thrs && distlo < disthi) { + if (low_enabled && distlo < thrs * thrs && distlo < disthi) { litPoint = LOW; } else if (disthi < thrs * thrs && disthi <= distlo) { litPoint = HIGH; @@ -328,3 +332,18 @@ void LabGrid::get_preferred_height_for_width_vfunc(int width, int &minimum_heigh { minimum_height = natural_height = width; } + + +bool LabGrid::lowEnabled() const +{ + return low_enabled; +} + + +void LabGrid::setLowEnabled(bool yes) +{ + if (low_enabled != yes) { + low_enabled = yes; + queue_draw(); + } +} diff --git a/rtgui/labgrid.h b/rtgui/labgrid.h index 348ab2398..715175f74 100644 --- a/rtgui/labgrid.h +++ b/rtgui/labgrid.h @@ -46,6 +46,8 @@ class LabGrid: public Gtk::DrawingArea, public BackBuffer { private: rtengine::ProcEvent evt; + Glib::ustring evtMsg; + enum State { NONE, HIGH, LOW }; State litPoint; float low_a; @@ -64,11 +66,14 @@ private: sigc::connection delayconn; static const int inset = 2; + bool grid_visible; + bool low_enabled; + bool notifyListener(); void getLitPoint(); public: - LabGrid(rtengine::ProcEvent evt); + LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low=true); void getParams(double &la, double &lb, double &ha, double &hb) const; void setParams(double la, double lb, double ha, double hb, bool notify); @@ -78,6 +83,9 @@ public: void reset(bool toInitial); void setListener(ToolPanelListener *l); + bool lowEnabled() const; + void setLowEnabled(bool yes); + bool on_draw(const ::Cairo::RefPtr &crf); void on_style_updated (); bool on_button_press_event(GdkEventButton *event); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 570d6ab2b..dc0ae617c 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -141,6 +141,8 @@ void ParamsEdited::set(bool v) colorToning.labgridBLow = v; colorToning.labgridAHigh = v; colorToning.labgridBHigh = v; + colorToning.labregions = v; + colorToning.labregionsShowMask = v; sharpening.enabled = v; sharpening.contrast = v; @@ -699,6 +701,8 @@ void ParamsEdited::initFrom(const std::vector& colorToning.labgridBLow = colorToning.labgridBLow && p.colorToning.labgridBLow == other.colorToning.labgridBLow; colorToning.labgridAHigh = colorToning.labgridAHigh && p.colorToning.labgridAHigh == other.colorToning.labgridAHigh; colorToning.labgridBHigh = colorToning.labgridBHigh && p.colorToning.labgridBHigh == other.colorToning.labgridBHigh; + colorToning.labregions = colorToning.labregions && p.colorToning.labregions == other.colorToning.labregions; + colorToning.labregionsShowMask = colorToning.labregionsShowMask && p.colorToning.labregionsShowMask == other.colorToning.labregionsShowMask; sharpenEdge.enabled = sharpenEdge.enabled && p.sharpenEdge.enabled == other.sharpenEdge.enabled; sharpenEdge.passes = sharpenEdge.passes && p.sharpenEdge.passes == other.sharpenEdge.passes; sharpenEdge.amount = sharpenEdge.amount && p.sharpenEdge.amount == other.sharpenEdge.amount; @@ -1571,6 +1575,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.colorToning.labgridBHigh = mods.colorToning.labgridBHigh; } + if (colorToning.labregions) { + toEdit.colorToning.labregions = mods.colorToning.labregions; + } + + if (colorToning.labregionsShowMask) { + toEdit.colorToning.labregionsShowMask = mods.colorToning.labregionsShowMask; + } + if (sharpenEdge.enabled) { toEdit.sharpenEdge.enabled = mods.sharpenEdge.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index aa9c01bd9..7a62bed68 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -178,6 +178,8 @@ public: bool labgridBLow; bool labgridAHigh; bool labgridBHigh; + bool labregions; + bool labregionsShowMask; }; class SharpenEdgeParamsEdited From 91f9dfe11084cd1da0d60b500b2944444f205cf8 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 26 Oct 2018 18:04:41 +0200 Subject: [PATCH 02/11] labregions: properly scale the guide to [0,1] for the guidedFilter calls --- rtengine/iplabregions.cc | 66 ++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/rtengine/iplabregions.cc b/rtengine/iplabregions.cc index e69f91d5a..3854570d3 100644 --- a/rtengine/iplabregions.cc +++ b/rtengine/iplabregions.cc @@ -37,11 +37,18 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) const float scaling = 1.f; int n = params->colorToning.labregions.size(); + int show_mask_idx = params->colorToning.labregionsShowMask; + if (show_mask_idx >= n) { + show_mask_idx = -1; + } std::vector> hmask(n); std::vector> cmask(n); std::vector> lmask(n); - for (int i = 0; i < n; ++i) { + const int begin_idx = max(show_mask_idx, 0); + const int end_idx = (show_mask_idx < 0 ? n : show_mask_idx+1); + + for (int i = begin_idx; i < end_idx; ++i) { auto &r = params->colorToning.labregions[i]; if (!r.hueMask.empty() && r.hueMask[0] != FCT_Linear) { hmask[i].reset(new FlatCurve(r.hueMask, true)); @@ -54,10 +61,9 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) } } - array2D guide(lab->W, lab->H, lab->L, ARRAY2D_BYREFERENCE); std::vector> abmask(n); std::vector> Lmask(n); - for (int i = 0; i < n; ++i) { + for (int i = begin_idx; i < end_idx; ++i) { abmask[i](lab->W, lab->H); Lmask[i](lab->W, lab->H); } @@ -81,38 +87,40 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) h1 = lin2log(h1, 3.f); float l1 = l / 32768.f; - if (params->colorToning.labregionsShowMask >= 0 && params->colorToning.labregionsShowMask < n) { - int idx = params->colorToning.labregionsShowMask; - auto &hm = hmask[idx]; - auto &cm = cmask[idx]; - auto &lm = lmask[idx]; - float blend = (hm ? hm->getVal(h1) : 1.f) * (cm ? cm->getVal(c1) : 1.f) * (lm ? lm->getVal(l1) : 1.f); - abmask[idx][y][x] = blend; - } else { - for (int i = 0; i < n; ++i) { - auto &hm = hmask[i]; - auto &cm = cmask[i]; - auto &lm = lmask[i]; - float blend = (hm ? hm->getVal(h1) : 1.f) * (cm ? cm->getVal(c1) : 1.f) * (lm ? lm->getVal(l1) : 1.f); - Lmask[i][y][x] = abmask[i][y][x] = blend; - } + for (int i = begin_idx; i < end_idx; ++i) { + auto &hm = hmask[i]; + auto &cm = cmask[i]; + auto &lm = lmask[i]; + float blend = LIM01((hm ? hm->getVal(h1) : 1.f) * (cm ? cm->getVal(c1) : 1.f) * (lm ? lm->getVal(l1) : 1.f)); + Lmask[i][y][x] = abmask[i][y][x] = blend; } } } - for (int i = 0; i < n; ++i) { - rtengine::guidedFilter(guide, abmask[i], abmask[i], max(int(4 / scale + 0.5), 1), 0.001, multiThread); - rtengine::guidedFilter(guide, Lmask[i], Lmask[i], max(int(25 / scale + 0.5), 1), 0.0001, multiThread); - } - - if (params->colorToning.labregionsShowMask >= 0 && params->colorToning.labregionsShowMask < n) { - int idx = params->colorToning.labregionsShowMask; + { + array2D guide(lab->W, lab->H, lab->L); #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif for (int y = 0; y < lab->H; ++y) { for (int x = 0; x < lab->W; ++x) { - auto blend = abmask[idx][y][x]; + guide[y][x] = LIM01(lab->L[y][x] / 32768.f); + } + } + + for (int i = begin_idx; i < end_idx; ++i) { + rtengine::guidedFilter(guide, abmask[i], abmask[i], max(int(4 / scale + 0.5), 1), 0.001, multiThread); + rtengine::guidedFilter(guide, Lmask[i], Lmask[i], max(int(25 / scale + 0.5), 1), 0.0001, multiThread); + } + } + + if (show_mask_idx >= 0) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < lab->H; ++y) { + for (int x = 0; x < lab->W; ++x) { + auto blend = abmask[show_mask_idx][y][x]; lab->a[y][x] = 0.f; lab->b[y][x] = blend * 42000.f; lab->L[y][x] = LIM(lab->L[y][x] + 32768.f * blend, 0.f, 32768.f); @@ -142,9 +150,9 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) auto &r = params->colorToning.labregions[i]; float blend = abmask[i][y][x]; float s = 1.f + r.saturation / 100.f; - float a_new = s * (a + 32768.f * abcoord(r.a) / factor / scaling); - float b_new = s * (b + 32768.f * abcoord(r.b) / factor / scaling); - float l_new = l * (1.f + r.lightness / 1000.f); + float a_new = LIM(s * (a + 32768.f * abcoord(r.a) / factor / scaling), -42000.f, 42000.f); + float b_new = LIM(s * (b + 32768.f * abcoord(r.b) / factor / scaling), -42000.f, 42000.f); + float l_new = LIM(l * (1.f + float(r.lightness) / 1000.f), 0.f, 32768.f); l = intp(Lmask[i][y][x], l_new, l); a = intp(blend, a_new, a); b = intp(blend, b_new, b); From db07a8cdd31242937693de5029eeda0e4a37c41e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 26 Oct 2018 18:16:12 +0200 Subject: [PATCH 03/11] L*a*b* regions: add new regions at the end of the list, not at the beginning --- rtgui/colortoning.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 79e913019..8b08acd70 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -1411,8 +1411,8 @@ void ColorToning::labRegionGet(int idx) void ColorToning::labRegionAddPressed() { - labRegionData.insert(labRegionData.begin(), rtengine::ColorToningParams::LabCorrectionRegion()); - labRegionSelected = 0; + labRegionSelected = labRegionData.size(); + labRegionData.push_back(rtengine::ColorToningParams::LabCorrectionRegion()); labRegionPopulateList(); labRegionShow(labRegionSelected); From f673a4a881a084856b5193e657aa87303b85edcd Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 28 Oct 2018 18:38:40 +0100 Subject: [PATCH 04/11] avoid grabbing focus in the L*a*b* region list --- rtgui/colortoning.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 8b08acd70..c296a6b27 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -365,6 +365,7 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR labRegionList = Gtk::manage(new Gtk::ListViewText(3)); labRegionList->set_size_request(-1, 100); + labRegionList->set_can_focus(false); labRegionList->set_column_title(0, "#"); labRegionList->set_column_title(1, M("TP_COLORTONING_LABREGION_LIST_TITLE")); labRegionList->set_column_title(2, M("TP_COLORTONING_LABREGION_MASK")); From 50c623802a1db8312e836ff6f2a71df490341bd6 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 28 Oct 2018 18:38:55 +0100 Subject: [PATCH 05/11] set LabRegions as the default color toning method --- rtengine/procparams.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index bfb91aea8..b8f836f34 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -731,7 +731,7 @@ ColorToningParams::ColorToningParams() : 1.00, 1.00 }, - method("Lab"), + method("LabRegions"), twocolor("Std"), redlow(0.0), greenlow(0.0), From fb44445f2acb061e5f017b6ea92a834e2002bcdd Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Mon, 29 Oct 2018 11:11:11 +0100 Subject: [PATCH 06/11] Reverted changes to 'default' language file #4893 Commit 1a3fd9 accidentally wiped all non-ASCII characters from the file while adding new keys. This commit restores them. --- rtdata/languages/default | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 40ff2ff88..f6ffc873c 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -398,7 +398,7 @@ HISTORY_MSG_145;Microcontrast - Uniformity HISTORY_MSG_146;Edge sharpening HISTORY_MSG_147;ES - Luminance only HISTORY_MSG_148;Microcontrast -HISTORY_MSG_149;Microcontrast - 33 matrix +HISTORY_MSG_149;Microcontrast - 3×3 matrix HISTORY_MSG_150;Post-demosaic artifact/noise red. HISTORY_MSG_151;Vibrance HISTORY_MSG_152;Vib - Pastel tones @@ -728,14 +728,14 @@ HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction -HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - L*a*b* region list +HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - List HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction -HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - L*a*b* region saturation -HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - L*a*b* region lightness -HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - L*a*b* region H mask -HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - L*a*b* region C mask -HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L*a*b* region L mask -HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - L*a*b* region show mask +HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - Saturation +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - Lightness +HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - H mask +HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - C mask +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L mask +HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - Show mask HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold HISTORY_MSG_HISTMATCHING;Auto-matched tone curve @@ -816,7 +816,7 @@ IPTCPANEL_CITY;City IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. IPTCPANEL_COPYHINT;Copy IPTC settings to clipboard. IPTCPANEL_COPYRIGHT;Copyright notice -IPTCPANEL_COPYRIGHTHINT;Enter a Notice on the current owner of the Copyright for this image, such as 2008 Jane Doe. +IPTCPANEL_COPYRIGHTHINT;Enter a Notice on the current owner of the Copyright for this image, such as ©2008 Jane Doe. IPTCPANEL_COUNTRY;Country IPTCPANEL_COUNTRYHINT;Enter the name of the country pictured in this image. IPTCPANEL_CREATOR;Creator @@ -1375,9 +1375,9 @@ TP_COARSETRAF_TOOLTIP_ROTLEFT;Rotate left.\n\nShortcuts:\n[ - Multiple Ed TP_COARSETRAF_TOOLTIP_ROTRIGHT;Rotate right.\n\nShortcuts:\n] - Multiple Editor Tabs Mode,\nAlt-] - Single Editor Tab Mode. TP_COARSETRAF_TOOLTIP_VFLIP;Flip vertically. TP_COLORAPP_ADAPTSCENE;Scene absolute luminance -TP_COLORAPP_ADAPTSCENE_TOOLTIP;Absolute luminance of the scene environment (cd/m).\n1) Calculated from the Exif data:\nShutter speed - ISO speed - F number - camera exposure correction.\n2) Calculated from the raw white point and RT's Exposure Compensation slider. -TP_COLORAPP_ADAPTVIEWING;Viewing absolute luminance (cd/m) -TP_COLORAPP_ADAPTVIEWING_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16cd/m). +TP_COLORAPP_ADAPTSCENE_TOOLTIP;Absolute luminance of the scene environment (cd/m²).\n1) Calculated from the Exif data:\nShutter speed - ISO speed - F number - camera exposure correction.\n2) Calculated from the raw white point and RT's Exposure Compensation slider. +TP_COLORAPP_ADAPTVIEWING;Viewing absolute luminance (cd/m²) +TP_COLORAPP_ADAPTVIEWING_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16cd/m²). TP_COLORAPP_ADAP_AUTO_TOOLTIP;If the checkbox is checked (recommended) RawTherapee calculates an optimum value from the Exif data.\nTo set the value manually, uncheck the checkbox first. TP_COLORAPP_ALGO;Algorithm TP_COLORAPP_ALGO_ALL;All @@ -1414,7 +1414,7 @@ TP_COLORAPP_FREE;Free temp+green + CAT02 + [output] TP_COLORAPP_GAMUT;Gamut control (L*a*b*) TP_COLORAPP_GAMUT_TOOLTIP;Allow gamut control in L*a*b* mode. TP_COLORAPP_HUE;Hue (h) -TP_COLORAPP_HUE_TOOLTIP;Hue (h) - angle between 0 and 360. +TP_COLORAPP_HUE_TOOLTIP;Hue (h) - angle between 0° and 360°. TP_COLORAPP_LABEL;CIE Color Appearance Model 2002 TP_COLORAPP_LABEL_CAM02;Image Adjustments TP_COLORAPP_LABEL_SCENE;Scene Conditions @@ -1571,16 +1571,16 @@ TP_DIRPYRDENOISE_MEDIAN_METHOD_RGB;RGB TP_DIRPYRDENOISE_MEDIAN_METHOD_TOOLTIP;When using the "Luminance only" and "L*a*b*" methods, median filtering will be performed just after the wavelet step in the noise reduction pipeline.\nWhen using the "RGB" mode, it will be performed at the very end of the noise reduction pipeline. TP_DIRPYRDENOISE_MEDIAN_METHOD_WEIGHTED;Weighted L* (little) + a*b* (normal) TP_DIRPYRDENOISE_MEDIAN_PASSES;Median iterations -TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Applying three median filter iterations with a 33 window size often leads to better results than using one median filter iteration with a 77 window size. +TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Applying three median filter iterations with a 3×3 window size often leads to better results than using one median filter iteration with a 7×7 window size. TP_DIRPYRDENOISE_MEDIAN_TYPE;Median type -TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Apply a median filter of the desired window size. The larger the window's size, the longer it takes.\n\n33 soft: treats 5 pixels in a 33 pixel window.\n33: treats 9 pixels in a 33 pixel window.\n55 soft: treats 13 pixels in a 55 pixel window.\n55: treats 25 pixels in a 55 pixel window.\n77: treats 49 pixels in a 77 pixel window.\n99: treats 81 pixels in a 99 pixel window.\n\nSometimes it is possible to achieve higher quality running several iterations with a smaller window size than one iteration with a larger one. +TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Apply a median filter of the desired window size. The larger the window's size, the longer it takes.\n\n3×3 soft: treats 5 pixels in a 3×3 pixel window.\n3×3: treats 9 pixels in a 3×3 pixel window.\n5×5 soft: treats 13 pixels in a 5×5 pixel window.\n5×5: treats 25 pixels in a 5×5 pixel window.\n7×7: treats 49 pixels in a 7×7 pixel window.\n9×9: treats 81 pixels in a 9×9 pixel window.\n\nSometimes it is possible to achieve higher quality running several iterations with a smaller window size than one iteration with a larger one. TP_DIRPYRDENOISE_SLI;Slider -TP_DIRPYRDENOISE_TYPE_3X3;33 -TP_DIRPYRDENOISE_TYPE_3X3SOFT;33 soft -TP_DIRPYRDENOISE_TYPE_5X5;55 -TP_DIRPYRDENOISE_TYPE_5X5SOFT;55 soft -TP_DIRPYRDENOISE_TYPE_7X7;77 -TP_DIRPYRDENOISE_TYPE_9X9;99 +TP_DIRPYRDENOISE_TYPE_3X3;3×3 +TP_DIRPYRDENOISE_TYPE_3X3SOFT;3×3 soft +TP_DIRPYRDENOISE_TYPE_5X5;5×5 +TP_DIRPYRDENOISE_TYPE_5X5SOFT;5×5 soft +TP_DIRPYRDENOISE_TYPE_7X7;7×7 +TP_DIRPYRDENOISE_TYPE_9X9;9×9 TP_DIRPYREQUALIZER_ALGO;Skin Color Range TP_DIRPYREQUALIZER_ALGO_TOOLTIP;Fine: closer to the colors of the skin, minimizing the action on other colors\nLarge: avoid more artifacts. TP_DIRPYREQUALIZER_ARTIF;Reduce artifacts @@ -2026,7 +2026,7 @@ TP_SHARPENING_USM;Unsharp Mask TP_SHARPENMICRO_AMOUNT;Quantity TP_SHARPENMICRO_CONTRAST;Contrast threshold TP_SHARPENMICRO_LABEL;Microcontrast -TP_SHARPENMICRO_MATRIX;33 matrix instead of 55 +TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5 TP_SHARPENMICRO_UNIFORMITY;Uniformity TP_SOFTLIGHT_LABEL;Soft Light TP_SOFTLIGHT_STRENGTH;Strength From 74a467fb4e20aca7b9b9ba40eefa405f0d32e2c4 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Oct 2018 21:12:44 +0100 Subject: [PATCH 07/11] labgrid: work on [0,1] values --- rtengine/improcfun.cc | 4 ++-- rtengine/iplabregions.cc | 2 +- rtgui/colortoning.cc | 8 ++++++-- rtgui/labgrid.cc | 37 +++++++++++++++++++++---------------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 013a629fd..748e46187 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -5782,8 +5782,8 @@ void ImProcFunctions::lab2rgb (const LabImage &src, Imagefloat &dst, const Glib: */ void ImProcFunctions::colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread) { - const float factor = ColorToningParams::LABGRID_CORR_MAX * 3.f; - const float scaling = ColorToningParams::LABGRID_CORR_SCALE; + const float factor = 3.f; + const float scaling = 3.f; float a_scale = (params->colorToning.labgridAHigh - params->colorToning.labgridALow) / factor / scaling; float a_base = params->colorToning.labgridALow / scaling; float b_scale = (params->colorToning.labgridBHigh - params->colorToning.labgridBLow) / factor / scaling; diff --git a/rtengine/iplabregions.cc b/rtengine/iplabregions.cc index 3854570d3..fe3325b1d 100644 --- a/rtengine/iplabregions.cc +++ b/rtengine/iplabregions.cc @@ -33,7 +33,7 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) return; } - const float factor = ColorToningParams::LABGRID_CORR_MAX * 3.f; + const float factor = 3.f; const float scaling = 1.f; int n = params->colorToning.labregions.size(); diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index c296a6b27..28b2a59ea 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -596,7 +596,7 @@ void ColorToning::read (const ProcParams* pp, const ParamsEdited* pedited) lastLumamode = pp->colorToning.lumamode; - labgrid->setParams(pp->colorToning.labgridALow, pp->colorToning.labgridBLow, pp->colorToning.labgridAHigh, pp->colorToning.labgridBHigh, false); + labgrid->setParams(pp->colorToning.labgridALow / ColorToningParams::LABGRID_CORR_MAX, pp->colorToning.labgridBLow / ColorToningParams::LABGRID_CORR_MAX, pp->colorToning.labgridAHigh / ColorToningParams::LABGRID_CORR_MAX, pp->colorToning.labgridBHigh / ColorToningParams::LABGRID_CORR_MAX, false); if (pedited && !pedited->colorToning.method) { method->set_active (5); @@ -667,6 +667,10 @@ void ColorToning::write (ProcParams* pp, ParamsEdited* pedited) pp->colorToning.strength = strength->getIntValue(); labgrid->getParams(pp->colorToning.labgridALow, pp->colorToning.labgridBLow, pp->colorToning.labgridAHigh, pp->colorToning.labgridBHigh); + pp->colorToning.labgridALow *= ColorToningParams::LABGRID_CORR_MAX; + pp->colorToning.labgridAHigh *= ColorToningParams::LABGRID_CORR_MAX; + pp->colorToning.labgridBLow *= ColorToningParams::LABGRID_CORR_MAX; + pp->colorToning.labgridBHigh *= ColorToningParams::LABGRID_CORR_MAX; labRegionGet(labRegionSelected); labRegionShow(labRegionSelected, true); @@ -778,7 +782,7 @@ void ColorToning::setDefaults (const ProcParams* defParams, const ParamsEdited* hlColSat->setDefault (defParams->colorToning.hlColSat); shadowsColSat->setDefault (defParams->colorToning.shadowsColSat); strength->setDefault (defParams->colorToning.strength); - labgrid->setDefault(defParams->colorToning.labgridALow, defParams->colorToning.labgridBLow, defParams->colorToning.labgridAHigh, defParams->colorToning.labgridBHigh); + labgrid->setDefault(defParams->colorToning.labgridALow / ColorToningParams::LABGRID_CORR_MAX, defParams->colorToning.labgridBLow / ColorToningParams::LABGRID_CORR_MAX, defParams->colorToning.labgridAHigh / ColorToningParams::LABGRID_CORR_MAX, defParams->colorToning.labgridBHigh / ColorToningParams::LABGRID_CORR_MAX); if (pedited) { diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc index fde03194b..fe984ffe9 100644 --- a/rtgui/labgrid.cc +++ b/rtgui/labgrid.cc @@ -44,7 +44,12 @@ using rtengine::Color; bool LabGrid::notifyListener() { if (listener) { - listener->panelChanged(evt, Glib::ustring::compose(evtMsg, int(high_a), int(high_b), int(low_a), int(low_b))); + const auto round = + [](float v) -> float + { + return int(v * 1000) / 1000.f; + }; + listener->panelChanged(evt, Glib::ustring::compose(evtMsg, round(high_a), round(high_b), round(low_a), round(low_b))); } return false; } @@ -76,8 +81,8 @@ void LabGrid::getParams(double &la, double &lb, double &ha, double &hb) const void LabGrid::setParams(double la, double lb, double ha, double hb, bool notify) { - const double lo = -rtengine::ColorToningParams::LABGRID_CORR_MAX; - const double hi = rtengine::ColorToningParams::LABGRID_CORR_MAX; + const double lo = -1.0; + const double hi = 1.0; low_a = rtengine::LIM(la, lo, hi); low_b = rtengine::LIM(lb, lo, hi); high_a = rtengine::LIM(ha, lo, hi); @@ -172,7 +177,7 @@ bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) cr->translate(0, height); cr->scale(1., -1.); const int cells = 8; - float step = rtengine::ColorToningParams::LABGRID_CORR_MAX / float(cells/2); + float step = 1.f / float(cells/2); for (int j = 0; j < cells; j++) { for (int i = 0; i < cells; i++) { float R, G, B; @@ -192,10 +197,10 @@ bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) // drawing the connection line cr->set_antialias(Cairo::ANTIALIAS_DEFAULT); float loa, hia, lob, hib; - loa = .5f * (width + width * low_a / rtengine::ColorToningParams::LABGRID_CORR_MAX); - hia = .5f * (width + width * high_a / rtengine::ColorToningParams::LABGRID_CORR_MAX); - lob = .5f * (height + height * low_b / rtengine::ColorToningParams::LABGRID_CORR_MAX); - hib = .5f * (height + height * high_b / rtengine::ColorToningParams::LABGRID_CORR_MAX); + loa = .5f * (width + width * low_a); + hia = .5f * (width + width * high_a); + lob = .5f * (height + height * low_b); + hib = .5f * (height + height * high_b); cr->set_line_width(2.); cr->set_source_rgb(0.6, 0.6, 0.6); cr->move_to(loa, lob); @@ -279,11 +284,11 @@ bool LabGrid::on_motion_notify_event(GdkEventMotion *event) const float mb = (2.0 * mouse_y - height) / (float)height; if (isDragged) { if (litPoint == LOW) { - low_a = ma * rtengine::ColorToningParams::LABGRID_CORR_MAX; - low_b = mb * rtengine::ColorToningParams::LABGRID_CORR_MAX; + low_a = ma; + low_b = mb; } else if (litPoint == HIGH) { - high_a = ma * rtengine::ColorToningParams::LABGRID_CORR_MAX; - high_b = mb * rtengine::ColorToningParams::LABGRID_CORR_MAX; + high_a = ma; + high_b = mb; } edited = true; grab_focus(); @@ -295,10 +300,10 @@ bool LabGrid::on_motion_notify_event(GdkEventMotion *event) queue_draw(); } else { litPoint = NONE; - float la = low_a / rtengine::ColorToningParams::LABGRID_CORR_MAX; - float lb = low_b / rtengine::ColorToningParams::LABGRID_CORR_MAX; - float ha = high_a / rtengine::ColorToningParams::LABGRID_CORR_MAX; - float hb = high_b / rtengine::ColorToningParams::LABGRID_CORR_MAX; + float la = low_a; + float lb = low_b; + float ha = high_a; + float hb = high_b; const float thrs = 0.05f; const float distlo = (la - ma) * (la - ma) + (lb - mb) * (lb - mb); const float disthi = (ha - ma) * (ha - ma) + (hb - mb) * (hb - mb); From 4eb93c24f8da371a2d858adb098b07f37dac159d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 30 Oct 2018 22:30:12 +0100 Subject: [PATCH 08/11] fixed broken labgrid after previous commit --- rtgui/colortoning.cc | 10 ++++++++-- rtgui/labgrid.cc | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 28b2a59ea..2b80c6be2 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -1493,6 +1493,12 @@ bool hasMask(const std::vector &dflt, const std::vector &mask) } } + +inline float round_ab(float v) +{ + return int(v * 1000) / 1000.f; +} + } // namespace @@ -1505,7 +1511,7 @@ void ColorToning::labRegionPopulateList() for (size_t i = 0; i < labRegionData.size(); ++i) { auto &r = labRegionData[i]; auto j = labRegionList->append(std::to_string(i+1)); - labRegionList->set_text(j, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", int(r.a), int(r.b), r.saturation, r.lightness)); + labRegionList->set_text(j, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", round_ab(r.a), round_ab(r.b), r.saturation, r.lightness)); labRegionList->set_text( j, 2, Glib::ustring::compose( "%1%2%3", @@ -1532,7 +1538,7 @@ void ColorToning::labRegionShow(int idx, bool list_only) labRegionChromaticityMask->setCurve(r.chromaticityMask); labRegionLightnessMask->setCurve(r.lightnessMask); } - labRegionList->set_text(idx, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", int(r.a), int(r.b), r.saturation, r.lightness)); + labRegionList->set_text(idx, 1, Glib::ustring::compose("a=%1 b=%2 s=%3 l=%4", round_ab(r.a), round_ab(r.b), r.saturation, r.lightness)); labRegionList->set_text( idx, 2, Glib::ustring::compose( "%1%2%3", diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc index fe984ffe9..5e0d571a5 100644 --- a/rtgui/labgrid.cc +++ b/rtgui/labgrid.cc @@ -177,7 +177,7 @@ bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) cr->translate(0, height); cr->scale(1., -1.); const int cells = 8; - float step = 1.f / float(cells/2); + float step = 12000.f / float(cells/2); for (int j = 0; j < cells; j++) { for (int i = 0; i < cells; i++) { float R, G, B; From be03d1061a4f1ab45a7d2f59b3c313d279955a18 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 1 Nov 2018 00:18:59 +0100 Subject: [PATCH 09/11] L*a*b* regions: allow for a bit more latitude in corrections --- rtengine/iplabregions.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/rtengine/iplabregions.cc b/rtengine/iplabregions.cc index fe3325b1d..1d42b39f1 100644 --- a/rtengine/iplabregions.cc +++ b/rtengine/iplabregions.cc @@ -33,9 +33,6 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) return; } - const float factor = 3.f; - const float scaling = 1.f; - int n = params->colorToning.labregions.size(); int show_mask_idx = params->colorToning.labregionsShowMask; if (show_mask_idx >= n) { @@ -133,8 +130,7 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) const auto abcoord = [](float x) -> float { - const float m = ColorToningParams::LABGRID_CORR_MAX; - return SGN(x) * log2lin(std::abs(x) / m, 4.f) * m; + return 12000.f * SGN(x) * log2lin(std::abs(x), 4.f); }; #ifdef _OPENMP @@ -150,9 +146,9 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) auto &r = params->colorToning.labregions[i]; float blend = abmask[i][y][x]; float s = 1.f + r.saturation / 100.f; - float a_new = LIM(s * (a + 32768.f * abcoord(r.a) / factor / scaling), -42000.f, 42000.f); - float b_new = LIM(s * (b + 32768.f * abcoord(r.b) / factor / scaling), -42000.f, 42000.f); - float l_new = LIM(l * (1.f + float(r.lightness) / 1000.f), 0.f, 32768.f); + float a_new = LIM(s * (a + abcoord(r.a)), -42000.f, 42000.f); + float b_new = LIM(s * (b + abcoord(r.b)), -42000.f, 42000.f); + float l_new = LIM(l * (1.f + float(r.lightness) / 500.f), 0.f, 32768.f); l = intp(Lmask[i][y][x], l_new, l); a = intp(blend, a_new, a); b = intp(blend, b_new, b); From e9f0b6ac60089550b23132b3ff24ec75d63a30f6 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 1 Nov 2018 13:34:10 +0100 Subject: [PATCH 10/11] L*a*b* regions: some code cleanups --- rtengine/iplabregions.cc | 6 ++++-- rtgui/colortoning.cc | 44 ++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/rtengine/iplabregions.cc b/rtengine/iplabregions.cc index 1d42b39f1..2a4d431e6 100644 --- a/rtengine/iplabregions.cc +++ b/rtengine/iplabregions.cc @@ -75,9 +75,11 @@ void ImProcFunctions::labColorCorrectionRegions(LabImage *lab) float b = lab->b[y][x]; float c, h; Color::Lab2Lch(a, b, c, h); - float c1 = lin2log(c * (327.68f / 48000.f), 10.f); + // magic constant c_factor: normally chromaticity is in [0; 42000] (see color.h), but here we use the constant to match how the chromaticity pipette works (see improcfun.cc lines 4705-4706 and color.cc line 1930 + constexpr float c_factor = 327.68f / 48000.f; + float c1 = lin2log(c * c_factor, 10.f); float h1 = Color::huelab_to_huehsv2(h); - h1 = h1 + 1.f/6.f; + h1 = h1 + 1.f/6.f; // offset the hue because we start from purple instead of red if (h1 > 1.f) { h1 -= 1.f; } diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 2b80c6be2..b6e001b70 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -11,7 +11,23 @@ using namespace rtengine; using namespace rtengine::procparams; -static constexpr int ID_LABREGION_HUE = 5; +namespace { + +constexpr int ID_LABREGION_HUE = 5; + +inline bool hasMask(const std::vector &dflt, const std::vector &mask) +{ + return !(mask.empty() || mask[0] == FCT_Linear || mask == dflt); +} + + +inline float round_ab(float v) +{ + return int(v * 1000) / 1000.f; +} + +} // namespace + ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLORTONING_LABEL"), false, true) { @@ -1403,6 +1419,10 @@ void ColorToning::onLabRegionSelectionChanged() void ColorToning::labRegionGet(int idx) { + if (idx < 0 || size_t(idx) >= labRegionData.size()) { + return; + } + auto &r = labRegionData[idx]; double la, lb; labRegionAB->getParams(la, lb, r.a, r.b); @@ -1482,26 +1502,6 @@ void ColorToning::labRegionShowMaskChanged() } -namespace { - -bool hasMask(const std::vector &dflt, const std::vector &mask) -{ - if (mask.empty() || mask[0] == FCT_Linear || mask == dflt) { - return false; - } else { - return true; - } -} - - -inline float round_ab(float v) -{ - return int(v * 1000) / 1000.f; -} - -} // namespace - - void ColorToning::labRegionPopulateList() { ConnectionBlocker b(labRegionSelectionConn); @@ -1524,7 +1524,7 @@ void ColorToning::labRegionPopulateList() void ColorToning::labRegionShow(int idx, bool list_only) { - bool disable = listener; + const bool disable = listener; if (disable) { disableListener(); } From a8bbc9cb8bdf9f8fc383ac8904da39e1be4f300a Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 1 Nov 2018 14:25:06 +0100 Subject: [PATCH 11/11] labgrid: code refactoring to avoid duplication for adding reset buttons --- rtgui/colortoning.cc | 40 +++-------------------- rtgui/colortoning.h | 5 --- rtgui/labgrid.cc | 75 ++++++++++++++++++++++++++++++++------------ rtgui/labgrid.h | 25 +++++++++++++-- 4 files changed, 82 insertions(+), 63 deletions(-) diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index b6e001b70..3ec149a0a 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -339,20 +339,8 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR // LAB grid auto m = ProcEventMapper::getInstance(); EvColorToningLabGridValue = m->newEvent(RGBCURVE, "HISTORY_MSG_COLORTONING_LABGRID_VALUE"); - labgridBox = Gtk::manage(new Gtk::HBox()); labgrid = Gtk::manage(new LabGrid(EvColorToningLabGridValue, M("TP_COLORTONING_LABGRID_VALUES"))); - labgridBox->pack_start(*labgrid, true, true); - labgridReset = Gtk::manage(new Gtk::Button ()); - labgridReset->add (*Gtk::manage(new RTImage ("undo-small.png", "redo-small.png"))); - setExpandAlignProperties(labgridReset, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); - labgridReset->set_relief(Gtk::RELIEF_NONE); - labgridReset->set_tooltip_markup(M("ADJUSTER_RESET_TO_DEFAULT")); - labgridReset->get_style_context()->add_class(GTK_STYLE_CLASS_FLAT); - labgridReset->set_can_focus(false); - labgridReset->set_size_request(-1, 20); - labgridReset->signal_button_release_event().connect(sigc::mem_fun(*this, &ColorToning::resetPressed)); - labgridBox->pack_start(*labgridReset, false, false); - pack_start(*labgridBox, Gtk::PACK_EXPAND_WIDGET, 4); + pack_start(*labgrid, Gtk::PACK_EXPAND_WIDGET, 4); //------------------------------------------------------------------------ //------------------------------------------------------------------------ @@ -410,14 +398,7 @@ ColorToning::ColorToning () : FoldableToolPanel(this, "colortoning", M("TP_COLOR labRegionBox->pack_start(*hb, true, true); labRegionAB = Gtk::manage(new LabGrid(EvLabRegionAB, M("TP_COLORTONING_LABREGION_ABVALUES"), false)); - hb = Gtk::manage(new Gtk::HBox()); - hb->pack_start(*labRegionAB, true, true); - labRegionABReset = Gtk::manage(new Gtk::Button()); - labRegionABReset->set_tooltip_markup(M("ADJUSTER_RESET_TO_DEFAULT")); - labRegionABReset->add(*Gtk::manage(new RTImage("undo-small.png", "redo-small.png"))); - labRegionABReset->signal_button_release_event().connect(sigc::mem_fun(*this, &ColorToning::labRegionResetPressed)); - add_button(labRegionABReset, hb); - labRegionBox->pack_start(*hb); + labRegionBox->pack_start(*labRegionAB); labRegionSaturation = Gtk::manage(new Adjuster(M("TP_COLORTONING_LABREGION_SATURATION"), -100, 100, 1, 0)); labRegionSaturation->setAdjusterListener(this); @@ -985,7 +966,7 @@ void ColorToning::methodChanged () { if (!batchMode) { - labgridBox->hide(); + labgrid->hide(); labRegionBox->hide(); if (method->get_active_row_number() == 0) { // Lab @@ -1155,7 +1136,7 @@ void ColorToning::methodChanged () lumamode->hide(); if (method->get_active_row_number() == 5) { - labgridBox->show(); + labgrid->show(); } else { labRegionBox->show(); } @@ -1388,19 +1369,6 @@ void ColorToning::setBatchMode (bool batchMode) } -bool ColorToning::resetPressed(GdkEventButton* event) -{ - labgrid->reset(event->state & GDK_CONTROL_MASK); - return false; -} - - -bool ColorToning::labRegionResetPressed(GdkEventButton *event) -{ - labRegionAB->reset(event->state & GDK_CONTROL_MASK); - return false; -} - void ColorToning::onLabRegionSelectionChanged() { diff --git a/rtgui/colortoning.h b/rtgui/colortoning.h index eff26bd46..8fb640cba 100644 --- a/rtgui/colortoning.h +++ b/rtgui/colortoning.h @@ -62,8 +62,6 @@ public: float blendPipetteValues(CurveEditor *ce, float chan1, float chan2, float chan3); private: - bool resetPressed(GdkEventButton* event); - bool labRegionResetPressed(GdkEventButton *event); void onLabRegionSelectionChanged(); void labRegionAddPressed(); void labRegionRemovePressed(); @@ -126,9 +124,7 @@ private: sigc::connection lumamodeConn; rtengine::ProcEvent EvColorToningLabGridValue; - Gtk::Button *labgridReset; LabGrid *labgrid; - Gtk::HBox *labgridBox; rtengine::ProcEvent EvLabRegionList; rtengine::ProcEvent EvLabRegionAB; @@ -145,7 +141,6 @@ private: Gtk::Button *labRegionRemove; Gtk::Button *labRegionUp; Gtk::Button *labRegionDown; - Gtk::Button *labRegionABReset; LabGrid *labRegionAB; Adjuster *labRegionSaturation; Adjuster *labRegionLightness; diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc index 5e0d571a5..f680c7322 100644 --- a/rtgui/labgrid.cc +++ b/rtgui/labgrid.cc @@ -41,7 +41,11 @@ using rtengine::Color; -bool LabGrid::notifyListener() +//----------------------------------------------------------------------------- +// LabGridArea +//----------------------------------------------------------------------------- + +bool LabGridArea::notifyListener() { if (listener) { const auto round = @@ -55,7 +59,7 @@ bool LabGrid::notifyListener() } -LabGrid::LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low): +LabGridArea::LabGridArea(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low): Gtk::DrawingArea(), evt(evt), evtMsg(msg), litPoint(NONE), @@ -70,7 +74,7 @@ LabGrid::LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_ add_events(Gdk::EXPOSURE_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK); } -void LabGrid::getParams(double &la, double &lb, double &ha, double &hb) const +void LabGridArea::getParams(double &la, double &lb, double &ha, double &hb) const { la = low_a; ha = high_a; @@ -79,7 +83,7 @@ void LabGrid::getParams(double &la, double &lb, double &ha, double &hb) const } -void LabGrid::setParams(double la, double lb, double ha, double hb, bool notify) +void LabGridArea::setParams(double la, double lb, double ha, double hb, bool notify) { const double lo = -1.0; const double hi = 1.0; @@ -93,7 +97,7 @@ void LabGrid::setParams(double la, double lb, double ha, double hb, bool notify) } } -void LabGrid::setDefault (double la, double lb, double ha, double hb) +void LabGridArea::setDefault (double la, double lb, double ha, double hb) { defaultLow_a = la; defaultLow_b = lb; @@ -102,7 +106,7 @@ void LabGrid::setDefault (double la, double lb, double ha, double hb) } -void LabGrid::reset(bool toInitial) +void LabGridArea::reset(bool toInitial) { if (toInitial) { setParams(defaultLow_a, defaultLow_b, defaultHigh_a, defaultHigh_b, true); @@ -112,32 +116,32 @@ void LabGrid::reset(bool toInitial) } -void LabGrid::setEdited(bool yes) +void LabGridArea::setEdited(bool yes) { edited = yes; } -bool LabGrid::getEdited() const +bool LabGridArea::getEdited() const { return edited; } -void LabGrid::setListener(ToolPanelListener *l) +void LabGridArea::setListener(ToolPanelListener *l) { listener = l; } -void LabGrid::on_style_updated () +void LabGridArea::on_style_updated () { setDirty(true); queue_draw (); } -bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) +bool LabGridArea::on_draw(const ::Cairo::RefPtr &crf) { Gtk::Allocation allocation = get_allocation(); allocation.set_x(0); @@ -232,7 +236,7 @@ bool LabGrid::on_draw(const ::Cairo::RefPtr &crf) } -bool LabGrid::on_button_press_event(GdkEventButton *event) +bool LabGridArea::on_button_press_event(GdkEventButton *event) { if (event->button == 1) { if (event->type == GDK_2BUTTON_PRESS) { @@ -259,7 +263,7 @@ bool LabGrid::on_button_press_event(GdkEventButton *event) } -bool LabGrid::on_button_release_event(GdkEventButton *event) +bool LabGridArea::on_button_release_event(GdkEventButton *event) { if (event->button == 1) { isDragged = false; @@ -269,7 +273,7 @@ bool LabGrid::on_button_release_event(GdkEventButton *event) } -bool LabGrid::on_motion_notify_event(GdkEventMotion *event) +bool LabGridArea::on_motion_notify_event(GdkEventMotion *event) { if (isDragged && delayconn.connected()) { delayconn.disconnect(); @@ -295,7 +299,7 @@ bool LabGrid::on_motion_notify_event(GdkEventMotion *event) if (options.adjusterMinDelay == 0) { notifyListener(); } else { - delayconn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &LabGrid::notifyListener), options.adjusterMinDelay); + delayconn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &LabGridArea::notifyListener), options.adjusterMinDelay); } queue_draw(); } else { @@ -320,35 +324,66 @@ bool LabGrid::on_motion_notify_event(GdkEventMotion *event) } -Gtk::SizeRequestMode LabGrid::get_request_mode_vfunc() const +Gtk::SizeRequestMode LabGridArea::get_request_mode_vfunc() const { return Gtk::SIZE_REQUEST_HEIGHT_FOR_WIDTH; } -void LabGrid::get_preferred_width_vfunc(int &minimum_width, int &natural_width) const +void LabGridArea::get_preferred_width_vfunc(int &minimum_width, int &natural_width) const { minimum_width = 50; natural_width = 150; // same as GRAPH_SIZE from mycurve.h } -void LabGrid::get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const +void LabGridArea::get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const { minimum_height = natural_height = width; } -bool LabGrid::lowEnabled() const +bool LabGridArea::lowEnabled() const { return low_enabled; } -void LabGrid::setLowEnabled(bool yes) +void LabGridArea::setLowEnabled(bool yes) { if (low_enabled != yes) { low_enabled = yes; queue_draw(); } } + + +//----------------------------------------------------------------------------- +// LabGrid +//----------------------------------------------------------------------------- + +LabGrid::LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low): + grid(evt, msg, enable_low) +{ + Gtk::Button *reset = Gtk::manage(new Gtk::Button()); + reset->set_tooltip_markup(M("ADJUSTER_RESET_TO_DEFAULT")); + reset->add(*Gtk::manage(new RTImage("undo-small.png", "redo-small.png"))); + reset->signal_button_release_event().connect(sigc::mem_fun(*this, &LabGrid::resetPressed)); + + setExpandAlignProperties(reset, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); + reset->set_relief(Gtk::RELIEF_NONE); + reset->get_style_context()->add_class(GTK_STYLE_CLASS_FLAT); + reset->set_can_focus(false); + reset->set_size_request(-1, 20); + + pack_start(grid, true, true); + pack_start(*reset, false, false); + show_all_children(); +} + + +bool LabGrid::resetPressed(GdkEventButton *event) +{ + grid.reset(event->state & GDK_CONTROL_MASK); + return false; +} diff --git a/rtgui/labgrid.h b/rtgui/labgrid.h index 715175f74..35e259996 100644 --- a/rtgui/labgrid.h +++ b/rtgui/labgrid.h @@ -43,7 +43,7 @@ #include "toolpanel.h" -class LabGrid: public Gtk::DrawingArea, public BackBuffer { +class LabGridArea: public Gtk::DrawingArea, public BackBuffer { private: rtengine::ProcEvent evt; Glib::ustring evtMsg; @@ -73,7 +73,7 @@ private: void getLitPoint(); public: - LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low=true); + LabGridArea(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low=true); void getParams(double &la, double &lb, double &ha, double &hb) const; void setParams(double la, double lb, double ha, double hb, bool notify); @@ -96,3 +96,24 @@ public: void get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const; }; + +class LabGrid: public Gtk::HBox { +private: + LabGridArea grid; + + bool resetPressed(GdkEventButton *event); + +public: + LabGrid(rtengine::ProcEvent evt, const Glib::ustring &msg, bool enable_low=true); + + void getParams(double &la, double &lb, double &ha, double &hb) const { return grid.getParams(la, lb, ha, hb); } + void setParams(double la, double lb, double ha, double hb, bool notify) { grid.setParams(la, lb, ha, hb, notify); } + void setDefault (double la, double lb, double ha, double hb) { grid.setDefault(la, lb, ha, hb); } + void setEdited(bool yes) { grid.setEdited(yes); } + bool getEdited() const { return grid.getEdited(); } + void reset(bool toInitial) { grid.reset(toInitial); } + void setListener(ToolPanelListener *l) { grid.setListener(l); } + bool lowEnabled() const { return grid.lowEnabled(); } + void setLowEnabled(bool yes) { grid.setLowEnabled(yes); } +}; +