Port tone equalizer from ART

Merge with local adjustments tone equalizer image processing function
for consistent results.

To-do: Enable for batch editing and add pivot/colormap to the local
adjustments version.
This commit is contained in:
Lawrence Lee 2022-05-01 16:13:27 -07:00
parent b989c271d8
commit bd3bd809b5
No known key found for this signature in database
GPG Key ID: 048FF2B76A63895F
23 changed files with 794 additions and 266 deletions

View File

@ -1520,6 +1520,11 @@ HISTORY_MSG_SPOT_ENTRY;Spot removal - Point modif.
HISTORY_MSG_TEMPOUT;CAM02 automatic temperature
HISTORY_MSG_THRESWAV;Balance threshold
HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor
HISTORY_MSG_TONE_EQUALIZER_BANDS;Tone equalizer - Bands
HISTORY_MSG_TONE_EQUALIZER_ENABLED;Tone equalizer
HISTORY_MSG_TONE_EQUALIZER_PIVOT;Tone equalizer - Pivot
HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION;Tone equalizer - Regularization
HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP;Tone equalizer - Tonal map
HISTORY_MSG_TRANS_METHOD;Geometry - Method
HISTORY_MSG_WAVBALCHROM;Equalizer chrominance
HISTORY_MSG_WAVBALLUM;Equalizer luminance
@ -1827,6 +1832,7 @@ PARTIALPASTE_SHARPENMICRO;Microcontrast
PARTIALPASTE_SOFTLIGHT;Soft light
PARTIALPASTE_SPOT;Spot removal
PARTIALPASTE_TM_FATTAL;Dynamic range compression
PARTIALPASTE_TONE_EQUALIZER;Tone equalizer
PARTIALPASTE_VIBRANCE;Vibrance
PARTIALPASTE_VIGNETTING;Vignetting correction
PARTIALPASTE_WHITEBALANCE;White balance
@ -3837,6 +3843,15 @@ TP_TM_FATTAL_AMOUNT;Amount
TP_TM_FATTAL_ANCHOR;Anchor
TP_TM_FATTAL_LABEL;Dynamic Range Compression
TP_TM_FATTAL_THRESHOLD;Detail
TP_TONE_EQUALIZER_BAND_0;Blacks
TP_TONE_EQUALIZER_BAND_1;Shadows
TP_TONE_EQUALIZER_BAND_2;Midtones
TP_TONE_EQUALIZER_BAND_3;Highlights
TP_TONE_EQUALIZER_BAND_4;Whites
TP_TONE_EQUALIZER_DETAIL;Regularization
TP_TONE_EQUALIZER_LABEL;Tone Equalizer
TP_TONE_EQUALIZER_PIVOT;Pivot (Ev)
TP_TONE_EQUALIZER_SHOW_COLOR_MAP;Show tonal map
TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift
TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH
TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL;Skin-tones

View File

@ -133,6 +133,7 @@ set(RTENGINESOURCEFILES
ipsharpen.cc
ipsharpenedges.cc
ipsoftlight.cc
iptoneequalizer.cc
iptransform.cc
ipvibrance.cc
ipwavelet.cc

View File

@ -341,6 +341,38 @@ void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* imag
#endif
}
// From ART.
void Imagefloat::multiply(float factor, bool multithread)
{
const int W = width;
const int H = height;
#ifdef __SSE2__
vfloat vfactor = F2V(factor);
#endif
#ifdef _OPENMP
# pragma omp parallel for firstprivate(W, H) schedule(dynamic, 5) if (multithread)
#endif
for (int y = 0; y < H; y++) {
int x = 0;
#ifdef __SSE2__
for (; x < W-3; x += 4) {
vfloat rv = LVF(r(y, x));
vfloat gv = LVF(g(y, x));
vfloat bv = LVF(b(y, x));
STVF(r(y, x), rv * vfactor);
STVF(g(y, x), gv * vfactor);
STVF(b(y, x), bv * vfactor);
}
#endif
for (; x < W; ++x) {
r(y, x) *= factor;
g(y, x) *= factor;
b(y, x) *= factor;
}
}
}
void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal)
{
@ -362,43 +394,15 @@ void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal)
}
// convert values's range to [0;1] ; this method assumes that the input values's range is [0;65535]
void Imagefloat::normalizeFloatTo1()
void Imagefloat::normalizeFloatTo1(bool multithread)
{
const int w = width;
const int h = height;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 5)
#endif
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
r(y, x) /= 65535.f;
g(y, x) /= 65535.f;
b(y, x) /= 65535.f;
}
}
multiply(1.f/65535.f, multithread);
}
// convert values's range to [0;65535 ; this method assumes that the input values's range is [0;1]
void Imagefloat::normalizeFloatTo65535()
void Imagefloat::normalizeFloatTo65535(bool multithread)
{
const int w = width;
const int h = height;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 5)
#endif
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
r(y, x) *= 65535.f;
g(y, x) *= 65535.f;
b(y, x) *= 65535.f;
}
}
multiply(65535.f, multithread);
}
// Parallelized transformation; create transform with cmsFLAGS_NOCACHE!

View File

@ -207,9 +207,10 @@ public:
return (uint32_t) ((lsign << 31) | (exponent << 23) | mantissa);
}
void multiply(float factor, bool multithread);
void normalizeFloat(float srcMinVal, float srcMaxVal) override;
void normalizeFloatTo1();
void normalizeFloatTo65535();
void normalizeFloatTo1(bool multithread=true);
void normalizeFloatTo65535(bool multithread=true);
void ExecCMSTransform(cmsHTRANSFORM hTransform);
void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy);
};

View File

@ -2734,6 +2734,7 @@ void ImProcCoordinator::process()
|| params->epd != nextParams->epd
|| params->fattal != nextParams->fattal
|| params->sh != nextParams->sh
|| params->toneEqualizer != nextParams->toneEqualizer
|| params->crop != nextParams->crop
|| params->coarse != nextParams->coarse
|| params->commonTrans != nextParams->commonTrans

View File

@ -2258,6 +2258,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer
// For tonecurve histogram
const float lumimulf[3] = {static_cast<float>(lumimul[0]), static_cast<float>(lumimul[1]), static_cast<float>(lumimul[2])};
std::unique_ptr<Imagefloat> workimg_copy;
if (params->toneEqualizer.enabled) {
working = working->copy();
workimg_copy.reset(working);
toneEqualizer(working);
}
#define TS 112

View File

@ -97,6 +97,7 @@ struct LocalContrastParams;
struct LocallabParams;
struct SharpeningParams;
struct SoftLightParams;
struct ToneEqualizerParams;
struct VibranceParams;
struct VignettingParams;
struct WaveletParams;
@ -491,7 +492,8 @@ enum class BlurType {
void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread);
//void shadowsHighlights(LabImage *lab);
void shadowsHighlights(LabImage *lab, bool ena, int labmode, int hightli, int shado, int rad, int scal, int hltonal, int shtonal);
bool toneEqualizer(Imagefloat *rgb);
static void toneEqualizer(array2D<float> &R, array2D<float> &G, array2D<float> &B, const procparams::ToneEqualizerParams & params, const Glib::ustring &workingProfile, double scale, bool multithread, bool show_color_map);
void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams);
void labColorCorrectionRegions(LabImage *lab);

View File

@ -2258,230 +2258,11 @@ void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg,
}
void tone_eq(array2D<float> &R, array2D<float> &G, array2D<float> &B, const struct local_params & lp, const Glib::ustring &workingProfile, double scale, bool multithread)
// adapted from the tone equalizer of darktable
/*
Copyright 2019 Alberto Griggio <alberto.griggio@gmail.com>
Small adaptation to Local Adjustment 10 2019 Jacques Desmis <jdesmis@gmail.com>
This file is part of darktable,
copyright (c) 2018 Aurelien Pierre.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
*/
{
// BENCHFUN
const int W = R.getWidth();
const int H = R.getHeight();
array2D<float> Y(W, H);
const auto log2 =
[](float x) -> float {
static const float l2 = xlogf(2);
return xlogf(x) / l2;
};
const auto exp2 =
[](float x) -> float {
return pow_F(2.f, x);
};
// Build the luma channels: band-pass filters with gaussian windows of
// std 2 EV, spaced by 2 EV
const float centers[12] = {
-18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f,
-4.0f, -2.0f, 0.0f, 2.0f, 4.0f
};
const auto conv = [&](int v, float lo, float hi) -> float {
const float f = v < 0 ? lo : hi;
return exp2(float(v) / 100.f * f);
};
const float factors[12] = {
conv(lp.mullocsh[0], 2.f, 3.f), // -18 EV
conv(lp.mullocsh[0], 2.f, 3.f), // -16 EV
conv(lp.mullocsh[0], 2.f, 3.f), // -14 EV
conv(lp.mullocsh[0], 2.f, 3.f), // -12 EV
conv(lp.mullocsh[0], 2.f, 3.f), // -10 EV
conv(lp.mullocsh[0], 2.f, 3.f), // -8 EV
conv(lp.mullocsh[1], 2.f, 3.f), // -6 EV
conv(lp.mullocsh[2], 2.5f, 2.5f), // -4 EV
conv(lp.mullocsh[3], 3.f, 2.f), // -2 EV
conv(lp.mullocsh[4], 3.f, 2.f), // 0 EV
conv(lp.mullocsh[4], 3.f, 2.f), // 2 EV
conv(lp.mullocsh[4], 3.f, 2.f) // 4 EV
};
TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile);
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws);
}
}
int detail = LIM(lp.detailsh + 5, 0, 5);
int radius = detail / scale + 0.5;
float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0);
if (radius > 0) {
rtengine::guidedFilterLog(10.f, Y, radius, epsilon2, multithread);
}
if (lp.detailsh > 0) {
array2D<float> Y2(W, H);
constexpr float base_epsilon = 0.02f;
constexpr float base_posterization = 5.f;
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]);
float ll = round(l * base_posterization) / base_posterization;
Y2[y][x] = Y[y][x];
Y[y][x] = exp2(ll);
}
}
radius = 350.0 / scale;
epsilon2 = base_epsilon / float(6 - rtengine::min(lp.detailsh, 5));
rtengine::guidedFilter(Y2, Y, Y, radius, epsilon2, multithread);
}
const auto gauss =
[](float b, float x) -> float {
return xexpf((-SQR(x - b) / 4.0f));
};
// For every pixel luminance, the sum of the gaussian masks
float w_sum = 0.f;
for (int i = 0; i < 12; ++i) {
w_sum += gauss(centers[i], 0.f);
}
const auto process_pixel =
[&](float y) -> float {
// convert to log space
const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f);
// build the correction as the sum of the contribution of each
// luminance channel to current pixel
float correction = 0.0f;
for (int c = 0; c < 12; ++c)
{
correction += gauss(centers[c], luma) * factors[c];
}
correction /= w_sum;
return correction;
};
LUTf lut(65536);
for (int i = 0; i < 65536; ++i) {
float y = float(i) / 65535.f;
float c = process_pixel(y);
lut[i] = c;
}
#ifdef __SSE2__
vfloat vfactors[12];
vfloat vcenters[12];
for (int i = 0; i < 12; ++i) {
vfactors[i] = F2V(factors[i]);
vcenters[i] = F2V(centers[i]);
}
const auto vgauss =
[](vfloat b, vfloat x) -> vfloat {
static const vfloat fourv = F2V(4.f);
return xexpf((-SQR(x - b) / fourv));
};
vfloat zerov = F2V(0.f);
vfloat vw_sum = F2V(w_sum);
const vfloat noisev = F2V(-18.f);
const vfloat xlog2v = F2V(xlogf(2.f));
const auto vprocess_pixel =
[&](vfloat y) -> vfloat {
const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev);
vfloat correction = zerov;
for (int c = 0; c < 12; ++c)
{
correction += vgauss(vcenters[c], luma) * vfactors[c];
}
correction /= vw_sum;
return correction;
};
vfloat v1 = F2V(1.f);
vfloat v65535 = F2V(65535.f);
#endif // __SSE2__
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
int x = 0;
#ifdef __SSE2__
for (; x < W - 3; x += 4) {
vfloat cY = LVFU(Y[y][x]);
vmask m = vmaskf_gt(cY, v1);
vfloat corr;
if (_mm_movemask_ps((vfloat)m)) {
corr = vprocess_pixel(cY);
} else {
corr = lut[cY * v65535];
}
STVF(R[y][x], LVF(R[y][x]) * corr);
STVF(G[y][x], LVF(G[y][x]) * corr);
STVF(B[y][x], LVF(B[y][x]) * corr);
}
#endif // __SSE2__
for (; x < W; ++x) {
float cY = Y[y][x];
float corr = cY > 1.f ? process_pixel(cY) : lut[cY * 65535.f];
R[y][x] *= corr;
G[y][x] *= corr;
B[y][x] *= corr;
}
}
ToneEqualizerParams params;
params.regularization = lp.detailsh;
std::copy(lp.mullocsh, lp.mullocsh + params.bands.size(), params.bands.begin());
ImProcFunctions::toneEqualizer(R, G, B, params, workingProfile, scale, multithread, false);
}
void ImProcFunctions::loccont(int bfw, int bfh, LabImage* tmp1, float rad, float stren, int sk)
{

362
rtengine/iptoneequalizer.cc Normal file
View File

@ -0,0 +1,362 @@
#include "color.h"
#include "guidedfilter.h"
#include "iccstore.h"
#include "imagefloat.h"
#include "improcfun.h"
#include "sleef.h"
#include "StopWatch.h"
namespace
{
const std::vector<std::array<float, 3>> colormap = {
{0.5f, 0.f, 0.5f},
{0.5f, 0.f, 0.5f},
{0.5f, 0.f, 0.5f},
{0.5f, 0.f, 0.5f},
{0.5f, 0.f, 0.5f},
{0.5f, 0.f, 0.5f}, // blacks
{0.f, 0.f, 1.f}, // shadows
{0.5f, 0.5f, 0.5f}, // midtones
{1.f, 1.f, 0.f}, // highlights
{1.f, 0.f, 0.f}, // whites
{1.f, 0.f, 0.f},
{1.f, 0.f, 0.f}
};
}
namespace rtengine
{
void ImProcFunctions::toneEqualizer(
array2D<float> &R, array2D<float> &G, array2D<float> &B,
const struct ToneEqualizerParams &params,
const Glib::ustring &workingProfile,
double scale,
bool multithread,
bool show_color_map)
// adapted from the tone equalizer of darktable
/*
Copyright 2019 Alberto Griggio <alberto.griggio@gmail.com>
Small adaptation to Local Adjustment 10 2019 Jacques Desmis <jdesmis@gmail.com>
This file is part of darktable,
copyright (c) 2018 Aurelien Pierre.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
*/
{
// BENCHFUN
const int W = R.getWidth();
const int H = R.getHeight();
array2D<float> Y(W, H);
const auto log2 =
[](float x) -> float {
static const float l2 = xlogf(2);
return xlogf(x) / l2;
};
const auto exp2 =
[](float x) -> float {
return pow_F(2.f, x);
};
// Build the luma channels: band-pass filters with gaussian windows of
// std 2 EV, spaced by 2 EV
const float centers[12] = {
-18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f,
-4.0f, -2.0f, 0.0f, 2.0f, 4.0f
};
const auto conv = [&](int v, float lo, float hi) -> float {
const float f = v < 0 ? lo : hi;
return exp2(float(v) / 100.f * f);
};
const float factors[12] = {
conv(params.bands[0], 2.f, 3.f), // -18 EV
conv(params.bands[0], 2.f, 3.f), // -16 EV
conv(params.bands[0], 2.f, 3.f), // -14 EV
conv(params.bands[0], 2.f, 3.f), // -12 EV
conv(params.bands[0], 2.f, 3.f), // -10 EV
conv(params.bands[0], 2.f, 3.f), // -8 EV
conv(params.bands[1], 2.f, 3.f), // -6 EV
conv(params.bands[2], 2.5f, 2.5f), // -4 EV
conv(params.bands[3], 3.f, 2.f), // -2 EV
conv(params.bands[4], 3.f, 2.f), // 0 EV
conv(params.bands[4], 3.f, 2.f), // 2 EV
conv(params.bands[4], 3.f, 2.f) // 4 EV
};
TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile);
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws);
}
}
int detail = LIM(params.regularization + 5, 0, 5);
int radius = detail / scale + 0.5;
float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0);
if (radius > 0) {
rtengine::guidedFilterLog(10.f, Y, radius, epsilon2, multithread);
}
if (params.regularization > 0) {
array2D<float> Y2(W, H);
constexpr float base_epsilon = 0.02f;
constexpr float base_posterization = 5.f;
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]);
float ll = round(l * base_posterization) / base_posterization;
Y2[y][x] = Y[y][x];
Y[y][x] = exp2(ll);
}
}
radius = 350.0 / scale;
epsilon2 = base_epsilon / float(6 - rtengine::min(params.regularization, 5));
rtengine::guidedFilter(Y2, Y, Y, radius, epsilon2, multithread);
}
const auto gauss =
[](float b, float x) -> float {
return xexpf((-SQR(x - b) / 4.0f));
};
// For every pixel luminance, the sum of the gaussian masks
float w_sum = 0.f;
for (int i = 0; i < 12; ++i) {
w_sum += gauss(centers[i], 0.f);
}
const auto process_pixel =
[&](float y) -> float {
// convert to log space
const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f);
// build the correction as the sum of the contribution of each
// luminance channel to current pixel
float correction = 0.0f;
for (int c = 0; c < 12; ++c)
{
correction += gauss(centers[c], luma) * factors[c];
}
correction /= w_sum;
return correction;
};
std::vector<std::array<float, 3>> cur_colormap;
if (show_color_map) {
lcmsMutex->lock();
cmsHPROFILE in = ICCStore::getInstance()->getsRGBProfile();
cmsHPROFILE out = ICCStore::getInstance()->workingSpace(workingProfile);
cmsHTRANSFORM xform = cmsCreateTransform(in, TYPE_RGB_FLT, out, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE);
lcmsMutex->unlock();
for (auto &c : colormap) {
cur_colormap.push_back(c);
auto &cc = cur_colormap.back();
cmsDoTransform(xform, &cc[0], &cc[0], 1);
}
cmsDeleteTransform(xform);
}
const auto process_colormap =
[&](float y) -> std::array<float, 3>
{
std::array<float, 3> ret = { 0.f, 0.f, 0.f };
// convert to log space
const float luma = max(log2(max(y, 0.f)), -18.0f);
// build the correction as the sum of the contribution of each
// luminance channel to current pixel
for (int c = 0; c < 12; ++c) {
float w = gauss(centers[c], luma);
for (int i = 0; i < 3; ++i) {
ret[i] += w * cur_colormap[c][i];
}
}
for (int i = 0; i < 3; ++i) {
ret[i] = LIM01(ret[i] / w_sum);
}
return ret;
};
#ifdef __SSE2__
vfloat vfactors[12];
vfloat vcenters[12];
for (int i = 0; i < 12; ++i) {
vfactors[i] = F2V(factors[i]);
vcenters[i] = F2V(centers[i]);
}
const auto vgauss =
[](vfloat b, vfloat x) -> vfloat {
static const vfloat fourv = F2V(4.f);
return xexpf((-SQR(x - b) / fourv));
};
vfloat zerov = F2V(0.f);
vfloat vw_sum = F2V(w_sum);
const vfloat noisev = F2V(-18.f);
const vfloat xlog2v = F2V(xlogf(2.f));
const auto vprocess_pixel =
[&](vfloat y) -> vfloat {
const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev);
vfloat correction = zerov;
for (int c = 0; c < 12; ++c)
{
correction += vgauss(vcenters[c], luma) * vfactors[c];
}
correction /= vw_sum;
return correction;
};
vfloat v1 = F2V(1.f);
vfloat v65535 = F2V(65535.f);
#endif // __SSE2__
if (show_color_map) {
LUTf lut_r(65537), lut_g(65537), lut_b(65537);
for (int i = 0; i < 65536; ++i) {
float y = float(i)/65535.f;
auto rgb = process_colormap(y);
lut_r[i] = rgb[0];
lut_g[i] = rgb[1];
lut_b[i] = rgb[2];
}
lut_r[65536] = cur_colormap.back()[0];
lut_g[65536] = cur_colormap.back()[1];
lut_b[65536] = cur_colormap.back()[2];
#ifdef _OPENMP
# pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
float cY = Y[y][x] * 65535.f;
R[y][x] = lut_r[cY];
G[y][x] = lut_g[cY];
B[y][x] = lut_b[cY];
}
}
return;
}
LUTf lut(65536);
for (int i = 0; i < 65536; ++i) {
float y = float(i) / 65535.f;
float c = process_pixel(y);
lut[i] = c;
}
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
int x = 0;
#ifdef __SSE2__
for (; x < W - 3; x += 4) {
vfloat cY = LVFU(Y[y][x]);
vmask m = vmaskf_gt(cY, v1);
vfloat corr;
if (_mm_movemask_ps((vfloat)m)) {
corr = vprocess_pixel(cY);
} else {
corr = lut[cY * v65535];
}
STVF(R[y][x], LVF(R[y][x]) * corr);
STVF(G[y][x], LVF(G[y][x]) * corr);
STVF(B[y][x], LVF(B[y][x]) * corr);
}
#endif // __SSE2__
for (; x < W; ++x) {
float cY = Y[y][x];
float corr = cY > 1.f ? process_pixel(cY) : lut[cY * 65535.f];
R[y][x] *= corr;
G[y][x] *= corr;
B[y][x] *= corr;
}
}
}
bool ImProcFunctions::toneEqualizer(Imagefloat *rgb)
{
if (!params->toneEqualizer.enabled) {
return false;
}
BENCHFUN
const float gain = 1.f / 65535.f * std::pow(2.f, -params->toneEqualizer.pivot);
rgb->multiply(gain, multiThread);
const int W = rgb->getWidth();
const int H = rgb->getHeight();
array2D<float> R(W, H, rgb->r.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> G(W, H, rgb->g.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> B(W, H, rgb->b.ptrs, ARRAY2D_BYREFERENCE);
bool show_color_map = params->toneEqualizer.show_colormap;
toneEqualizer(R, G, B, params->toneEqualizer, params->icm.workingProfile, scale, multiThread, show_color_map);
rgb->multiply(1.f/gain, multiThread);
return show_color_map;
}
}

View File

@ -1786,6 +1786,30 @@ bool SHParams::operator !=(const SHParams& other) const
return !(*this == other);
}
ToneEqualizerParams::ToneEqualizerParams() :
enabled(false),
bands{0, 0, 0, 0, 0},
regularization(4),
show_colormap(false),
pivot(0)
{
}
bool ToneEqualizerParams::operator ==(const ToneEqualizerParams &other) const
{
return
enabled == other.enabled
&& bands == other.bands
&& regularization == other.regularization
&& show_colormap == other.show_colormap
&& pivot == other.pivot;
}
bool ToneEqualizerParams::operator !=(const ToneEqualizerParams &other) const
{
return !(*this == other);
}
CropParams::CropParams() :
enabled(false),
x(-1),
@ -5814,6 +5838,8 @@ void ProcParams::setDefaults()
sh = {};
toneEqualizer = {};
crop = {};
coarse = {};
@ -6241,6 +6267,14 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->sh.radius, "Shadows & Highlights", "Radius", sh.radius, keyFile);
saveToKeyfile(!pedited || pedited->sh.lab, "Shadows & Highlights", "Lab", sh.lab, keyFile);
// Tone equalizer
saveToKeyfile(!pedited || pedited->toneEqualizer.enabled, "ToneEqualizer", "Enabled", toneEqualizer.enabled, keyFile);
for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
saveToKeyfile(!pedited || pedited->toneEqualizer.bands, "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile);
}
saveToKeyfile(!pedited || pedited->toneEqualizer.regularization, "ToneEqualizer", "Regularization", toneEqualizer.regularization, keyFile);
saveToKeyfile(!pedited || pedited->toneEqualizer.pivot, "ToneEqualizer", "Pivot", toneEqualizer.pivot, keyFile);
// Crop
saveToKeyfile(!pedited || pedited->crop.enabled, "Crop", "Enabled", crop.enabled, keyFile);
saveToKeyfile(!pedited || pedited->crop.x, "Crop", "X", crop.x, keyFile);
@ -8226,6 +8260,15 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
}
}
if (keyFile.has_group("ToneEqualizer")) {
assignFromKeyfile(keyFile, "ToneEqualizer", "Enabled", pedited, toneEqualizer.enabled, pedited->toneEqualizer.enabled);
for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
assignFromKeyfile(keyFile, "ToneEqualizer", "Band" + std::to_string(i), pedited, toneEqualizer.bands[i], pedited->toneEqualizer.bands);
}
assignFromKeyfile(keyFile, "ToneEqualizer", "Regularization", pedited, toneEqualizer.regularization, pedited->toneEqualizer.regularization);
assignFromKeyfile(keyFile, "ToneEqualizer", "Pivot", pedited, toneEqualizer.pivot, pedited->toneEqualizer.pivot);
}
if (keyFile.has_group("Crop")) {
assignFromKeyfile(keyFile, "Crop", "Enabled", pedited, crop.enabled, pedited->crop.enabled);
assignFromKeyfile(keyFile, "Crop", "X", pedited, crop.x, pedited->crop.x);
@ -10447,6 +10490,7 @@ bool ProcParams::operator ==(const ProcParams& other) const
&& fattal == other.fattal
&& defringe == other.defringe
&& sh == other.sh
&& toneEqualizer == other.toneEqualizer
&& crop == other.crop
&& coarse == other.coarse
&& rotate == other.rotate

View File

@ -827,6 +827,22 @@ struct SHParams {
bool operator !=(const SHParams& other) const;
};
/**
* Tone equalizer parameters.
*/
struct ToneEqualizerParams {
bool enabled;
std::array<int, 5> bands;
int regularization;
bool show_colormap;
double pivot;
ToneEqualizerParams();
bool operator ==(const ToneEqualizerParams &other) const;
bool operator !=(const ToneEqualizerParams &other) const;
};
/**
* Parameters of the cropping
*/
@ -2547,6 +2563,7 @@ public:
EPDParams epd; ///< Edge Preserving Decomposition parameters
FattalToneMappingParams fattal; ///< Fattal02 tone mapping
SHParams sh; ///< Shadow/highlight enhancement parameters
ToneEqualizerParams toneEqualizer; ///< Tone equalizer parameters
CropParams crop; ///< Crop parameters
CoarseTransformParams coarse; ///< Coarse transformation (90, 180, 270 deg rotation, h/v flipping) parameters
CommonTransformParams commonTrans; ///< Common transformation parameters (autofill)

View File

@ -159,6 +159,7 @@ set(NONCLISOURCEFILES
thumbimageupdater.cc
thumbnail.cc
tonecurve.cc
toneequalizer.cc
toolbar.cc
toolpanel.cc
toolpanelcoord.cc

View File

@ -51,6 +51,7 @@ Adjuster::Adjuster(
grid(nullptr),
label(nullptr),
imageIcon1(imgIcon1),
imageIcon2(imgIcon2),
automatic(nullptr),
adjusterListener(nullptr),
spinChange(options.adjusterMinDelay, options.adjusterMaxDelay),
@ -76,8 +77,8 @@ Adjuster::Adjuster(
setExpandAlignProperties(imageIcon1, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
}
if (imgIcon2) {
setExpandAlignProperties(imgIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
if (imageIcon2) {
setExpandAlignProperties(imageIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
}
set_column_spacing(0);
@ -120,9 +121,9 @@ Adjuster::Adjuster(
attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1);
}
if (imgIcon2) {
attach_next_to(*imgIcon2, *slider, Gtk::POS_RIGHT, 1, 1);
attach_next_to(*spin, *imgIcon2, Gtk::POS_RIGHT, 1, 1);
if (imageIcon2) {
attach_next_to(*imageIcon2, *slider, Gtk::POS_RIGHT, 1, 1);
attach_next_to(*spin, *imageIcon2, Gtk::POS_RIGHT, 1, 1);
} else {
attach_next_to(*spin, *slider, Gtk::POS_RIGHT, 1, 1);
}
@ -140,9 +141,9 @@ Adjuster::Adjuster(
grid->attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1);
}
if (imgIcon2) {
grid->attach_next_to(*imgIcon2, Gtk::POS_RIGHT, 1, 1);
grid->attach_next_to(*reset, *imgIcon2, Gtk::POS_RIGHT, 1, 1);
if (imageIcon2) {
grid->attach_next_to(*imageIcon2, Gtk::POS_RIGHT, 1, 1);
grid->attach_next_to(*reset, *imageIcon2, Gtk::POS_RIGHT, 1, 1);
} else {
grid->attach_next_to(*reset, *slider, Gtk::POS_RIGHT, 1, 1);
}
@ -683,3 +684,13 @@ void Adjuster::setDelay(unsigned int min_delay_ms, unsigned int max_delay_ms)
spinChange.setDelay(min_delay_ms, max_delay_ms);
sliderChange.setDelay(min_delay_ms, max_delay_ms);
}
void Adjuster::showIcons(bool yes)
{
if (imageIcon1) {
imageIcon1->set_visible(yes);
}
if (imageIcon2) {
imageIcon2->set_visible(yes);
}
}

View File

@ -41,6 +41,7 @@ protected:
Gtk::Grid* grid;
Gtk::Label* label;
Gtk::Image *imageIcon1;
Gtk::Image *imageIcon2;
MyHScale* slider;
MySpinButton* spin;
Gtk::Button* reset;
@ -133,4 +134,5 @@ public:
void trimValue (int &val) const;
void setLogScale(double base, double pivot, bool anchorMiddle = false);
void setDelay(unsigned int min_delay_ms, unsigned int max_delay_ms = 0);
void showIcons(bool yes);
};

View File

@ -317,6 +317,11 @@ void ParamsEdited::set(bool v)
sh.stonalwidth = v;
sh.radius = v;
sh.lab = v;
toneEqualizer.enabled = v;
toneEqualizer.bands = v;
toneEqualizer.regularization = v;
toneEqualizer.show_colormap = v;
toneEqualizer.pivot = v;
crop.enabled = v;
crop.x = v;
crop.y = v;
@ -1031,6 +1036,11 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
crop.ratio = crop.ratio && p.crop.ratio == other.crop.ratio;
crop.orientation = crop.orientation && p.crop.orientation == other.crop.orientation;
crop.guide = crop.guide && p.crop.guide == other.crop.guide;
toneEqualizer.enabled = toneEqualizer.enabled && p.toneEqualizer.enabled == other.toneEqualizer.enabled;
toneEqualizer.bands = toneEqualizer.bands && p.toneEqualizer.bands == other.toneEqualizer.bands;
toneEqualizer.regularization = toneEqualizer.regularization && p.toneEqualizer.regularization == other.toneEqualizer.regularization;
toneEqualizer.show_colormap = toneEqualizer.show_colormap && p.toneEqualizer.show_colormap == other.toneEqualizer.show_colormap;
toneEqualizer.pivot = toneEqualizer.pivot && p.toneEqualizer.pivot == other.toneEqualizer.pivot;
coarse.rotate = coarse.rotate && p.coarse.rotate == other.coarse.rotate;
coarse.hflip = coarse.hflip && p.coarse.hflip == other.coarse.hflip;
coarse.vflip = coarse.vflip && p.coarse.vflip == other.coarse.vflip;
@ -3194,6 +3204,26 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.sh.lab = mods.sh.lab;
}
if (toneEqualizer.enabled) {
toEdit.toneEqualizer.enabled = mods.toneEqualizer.enabled;
}
if (toneEqualizer.bands) {
toEdit.toneEqualizer.bands = mods.toneEqualizer.bands;
}
if (toneEqualizer.regularization) {
toEdit.toneEqualizer.regularization = mods.toneEqualizer.regularization;
}
if (toneEqualizer.show_colormap) {
toEdit.toneEqualizer.show_colormap = mods.toneEqualizer.show_colormap;
}
if (toneEqualizer.pivot) {
toEdit.toneEqualizer.pivot = mods.toneEqualizer.pivot;
}
if (crop.enabled) {
toEdit.crop.enabled = mods.crop.enabled;
}

View File

@ -358,6 +358,14 @@ struct SHParamsEdited {
bool lab;
};
struct ToneEqualizerParamsEdited {
bool enabled;
bool bands;
bool regularization;
bool show_colormap;
bool pivot;
};
struct CropParamsEdited {
bool enabled;
bool x;
@ -1545,6 +1553,7 @@ struct ParamsEdited {
FattalToneMappingParamsEdited fattal;
ImpulseDenoiseParamsEdited impulseDenoise;
SHParamsEdited sh;
ToneEqualizerParamsEdited toneEqualizer;
CropParamsEdited crop;
CoarseTransformParamsEdited coarse;
CommonTransformParamsEdited commonTrans;

View File

@ -219,6 +219,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
wb = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_WHITEBALANCE")));
exposure = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_EXPOSURE")));
sh = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHADOWSHIGHLIGHTS")));
toneEqualizer = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_TONE_EQUALIZER")));
epd = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_EPD")));
fattal = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_TM_FATTAL")));
pcvignette = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PCVIGNETTE")));
@ -331,6 +332,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
vboxes[0]->pack_start (*wb, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*exposure, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*sh, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*toneEqualizer, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*epd, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*fattal, Gtk::PACK_SHRINK, 2);
vboxes[0]->pack_start (*pcvignette, Gtk::PACK_SHRINK, 2);
@ -496,6 +498,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
wbConn = wb->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
exposureConn = exposure->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
shConn = sh->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
toneEqualizerConn = toneEqualizer->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
epdConn = epd->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
fattalConn = fattal->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
pcvignetteConn = pcvignette->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
@ -701,6 +704,7 @@ void PartialPasteDlg::basicToggled ()
ConnectionBlocker wbBlocker(wbConn);
ConnectionBlocker exposureBlocker(exposureConn);
ConnectionBlocker shBlocker(shConn);
ConnectionBlocker toneEqualizerBlocker(toneEqualizerConn);
ConnectionBlocker epdBlocker(epdConn);
ConnectionBlocker fattalBlocker(fattalConn);
ConnectionBlocker pcvignetteBlocker(pcvignetteConn);
@ -712,6 +716,7 @@ void PartialPasteDlg::basicToggled ()
wb->set_active (basic->get_active ());
exposure->set_active (basic->get_active ());
sh->set_active (basic->get_active ());
toneEqualizer->set_active (basic->get_active ());
epd->set_active (basic->get_active ());
fattal->set_active (basic->get_active ());
pcvignette->set_active (basic->get_active ());
@ -889,6 +894,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
filterPE.sh = falsePE.sh;
}
if (!toneEqualizer->get_active ()) {
filterPE.toneEqualizer = falsePE.toneEqualizer;
}
if (!epd->get_active ()) {
filterPE.epd = falsePE.epd;
}

View File

@ -133,6 +133,7 @@ public:
Gtk::CheckButton* exposure;
Gtk::CheckButton* localcontrast;
Gtk::CheckButton* sh;
Gtk::CheckButton* toneEqualizer;
Gtk::CheckButton* epd;
Gtk::CheckButton* fattal;
Gtk::CheckButton* retinex;
@ -225,6 +226,7 @@ public:
sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn;
sigc::connection locallabConn;
sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn;
sigc::connection toneEqualizerConn;
sigc::connection spotConn, sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn;
sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn;
sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn;

View File

@ -462,6 +462,7 @@ void Thumbnail::setProcParams (const ProcParams& pp, ParamsEdited* pe, int whoCh
|| pparams->epd != pp.epd
|| pparams->fattal != pp.fattal
|| pparams->sh != pp.sh
|| pparams->toneEqualizer != pp.toneEqualizer
|| pparams->crop != pp.crop
|| pparams->coarse != pp.coarse
|| pparams->commonTrans != pp.commonTrans

169
rtgui/toneequalizer.cc Normal file
View File

@ -0,0 +1,169 @@
/*
* Adapted from ART.
*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "eventmapper.h"
#include "toneequalizer.h"
#include "rtimage.h"
using namespace rtengine;
using namespace rtengine::procparams;
ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, "toneequalizer", M("TP_TONE_EQUALIZER_LABEL"), false, true)
{
auto m = ProcEventMapper::getInstance();
EvEnabled = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_ENABLED");
EvBands = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_BANDS");
EvRegularization = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION");
EvColormap = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP");
EvPivot = m->newEvent(RGBCURVE, "HISTORY_MSG_TONE_EQUALIZER_PIVOT");
std::array<const char *, 5> images = {
"purple",
"blue",
"gray",
"yellow",
"red"
};
for (size_t i = 0; i < bands.size(); ++i) {
bands[i] = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_BAND_" + std::to_string(i)), -100, 100, 1, 0, Gtk::manage(new RTImage(Glib::ustring("circle-") + images[i] + "-small.png"))));
bands[i]->setAdjusterListener(this);
pack_start(*bands[i]);
bands[i]->showIcons(false);
}
pivot = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_PIVOT"), -12, 12, 0.05, 0));
pivot->setLogScale(64, 0, true);
pivot->setAdjusterListener(this);
pack_start(*pivot);
pack_start(*Gtk::manage(new Gtk::HSeparator()));
regularization = Gtk::manage(new Adjuster(M("TP_TONE_EQUALIZER_DETAIL"), -5, 5, 1, 5));
regularization->setAdjusterListener(this);
pack_start(*regularization);
show_colormap = Gtk::manage(new Gtk::CheckButton(M("TP_TONE_EQUALIZER_SHOW_COLOR_MAP")));
pack_start(*show_colormap);
show_colormap->signal_toggled().connect(sigc::mem_fun(this, &ToneEqualizer::colormapToggled));
show_all_children ();
}
void ToneEqualizer::read(const ProcParams *pp, const ParamsEdited* pedited)
{
disableListener();
setEnabled(pp->toneEqualizer.enabled);
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->setValue(pp->toneEqualizer.bands[i]);
bands[i]->showIcons(pp->toneEqualizer.show_colormap);
}
regularization->setValue(pp->toneEqualizer.regularization);
pivot->setValue(pp->toneEqualizer.pivot);
show_colormap->set_active(pp->toneEqualizer.show_colormap);
enableListener();
}
void ToneEqualizer::write(ProcParams *pp, ParamsEdited* pedited)
{
for (size_t i = 0; i < bands.size(); ++i) {
pp->toneEqualizer.bands[i] = bands[i]->getValue();
}
pp->toneEqualizer.enabled = getEnabled();
pp->toneEqualizer.regularization = regularization->getValue();
pp->toneEqualizer.show_colormap = show_colormap->get_active();
pp->toneEqualizer.pivot = pivot->getValue();
}
void ToneEqualizer::setDefaults(const ProcParams *defParams, const ParamsEdited* pedited)
{
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->setDefault(defParams->toneEqualizer.bands[i]);
}
regularization->setDefault(defParams->toneEqualizer.regularization);
pivot->setDefault(defParams->toneEqualizer.pivot);
inital_params = defParams->toneEqualizer;
}
void ToneEqualizer::adjusterChanged(Adjuster *a, double newval)
{
if (listener && getEnabled()) {
if (a == regularization) {
listener->panelChanged(EvRegularization, Glib::ustring::format(a->getValue()));
} else if (a == pivot) {
listener->panelChanged(EvPivot, Glib::ustring::format(a->getValue()));
} else {
Glib::ustring s;
for (size_t i = 0; i < bands.size(); ++i) {
s += Glib::ustring::format((int)bands[i]->getValue()) + " ";
}
listener->panelChanged(EvBands, s);
}
}
}
void ToneEqualizer::adjusterAutoToggled(Adjuster *a)
{
}
void ToneEqualizer::enabledChanged()
{
if (listener) {
if (get_inconsistent()) {
listener->panelChanged(EvEnabled, M("GENERAL_UNCHANGED"));
} else if (getEnabled()) {
listener->panelChanged(EvEnabled, M("GENERAL_ENABLED"));
} else {
listener->panelChanged(EvEnabled, M("GENERAL_DISABLED"));
}
}
}
void ToneEqualizer::colormapToggled()
{
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->showIcons(show_colormap->get_active());
}
if (listener && getEnabled()) {
listener->panelChanged(EvColormap, show_colormap->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
}
}
void ToneEqualizer::trimValues(rtengine::procparams::ProcParams *pp)
{
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->trimValue(pp->toneEqualizer.bands[i]);
}
regularization->trimValue(pp->toneEqualizer.regularization);
pivot->trimValue(pp->toneEqualizer.pivot);
}

56
rtgui/toneequalizer.h Normal file
View File

@ -0,0 +1,56 @@
/* -*- C++ -*-
*
* Adapted from ART.
*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gtkmm.h>
#include "adjuster.h"
#include "toolpanel.h"
class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel {
public:
ToneEqualizer();
void read(const rtengine::procparams::ProcParams *pp, const ParamsEdited* pedited = nullptr) override;
void write(rtengine::procparams::ProcParams *pp, ParamsEdited* pedited = nullptr) override;
void setDefaults(const rtengine::procparams::ProcParams *defParams, const ParamsEdited* pedited = nullptr) override;
void adjusterChanged(Adjuster *a, double newval) override;
void adjusterAutoToggled(Adjuster *a) override;
void enabledChanged() override;
void trimValues(rtengine::procparams::ProcParams *pp) override;
private:
void colormapToggled();
std::array<Adjuster *, 5> bands;
Adjuster *regularization;
Adjuster *pivot;
Gtk::CheckButton *show_colormap;
rtengine::ProcEvent EvEnabled;
rtengine::ProcEvent EvBands;
rtengine::ProcEvent EvRegularization;
rtengine::ProcEvent EvColormap;
rtengine::ProcEvent EvPivot;
rtengine::procparams::ToneEqualizerParams inital_params;
};

View File

@ -47,6 +47,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
coarse = Gtk::manage (new CoarsePanel ());
toneCurve = Gtk::manage (new ToneCurve ());
shadowshighlights = Gtk::manage (new ShadowsHighlights ());
toneEqualizer = Gtk::manage (new ToneEqualizer ());
impulsedenoise = Gtk::manage (new ImpulseDenoise ());
defringe = Gtk::manage (new Defringe ());
spot = Gtk::manage (new Spot ());
@ -118,6 +119,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
addfavoritePanel (colorPanel, chmixer);
addfavoritePanel (colorPanel, blackwhite);
addfavoritePanel (exposurePanel, shadowshighlights);
addfavoritePanel (exposurePanel, toneEqualizer);
addfavoritePanel (detailsPanel, spot);
addfavoritePanel (detailsPanel, sharpening);
addfavoritePanel (detailsPanel, localContrast);

View File

@ -78,6 +78,7 @@
#include "softlight.h"
#include "spot.h"
#include "tonecurve.h"
#include "toneequalizer.h"
#include "toolbar.h"
#include "toolpanel.h"
#include "vibrance.h"
@ -133,6 +134,7 @@ protected:
Crop* crop;
ToneCurve* toneCurve;
ShadowsHighlights* shadowshighlights;
ToneEqualizer* toneEqualizer;
LocalContrast *localContrast;
Spot* spot;
Defringe* defringe;