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:
parent
b989c271d8
commit
bd3bd809b5
@ -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
|
||||
|
@ -133,6 +133,7 @@ set(RTENGINESOURCEFILES
|
||||
ipsharpen.cc
|
||||
ipsharpenedges.cc
|
||||
ipsoftlight.cc
|
||||
iptoneequalizer.cc
|
||||
iptransform.cc
|
||||
ipvibrance.cc
|
||||
ipwavelet.cc
|
||||
|
@ -341,6 +341,38 @@ void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* imag
|
||||
#endif
|
||||
}
|
||||
|
||||
// From ART.
|
||||
void Imagefloat::multiply(float factor, bool multithread)
|
||||
{
|
||||
const int W = width;
|
||||
const int H = height;
|
||||
#ifdef __SSE2__
|
||||
vfloat vfactor = F2V(factor);
|
||||
#endif
|
||||
|
||||
#ifdef _OPENMP
|
||||
# pragma omp parallel for firstprivate(W, H) schedule(dynamic, 5) if (multithread)
|
||||
#endif
|
||||
for (int y = 0; y < H; y++) {
|
||||
int x = 0;
|
||||
#ifdef __SSE2__
|
||||
for (; x < W-3; x += 4) {
|
||||
vfloat rv = LVF(r(y, x));
|
||||
vfloat gv = LVF(g(y, x));
|
||||
vfloat bv = LVF(b(y, x));
|
||||
STVF(r(y, x), rv * vfactor);
|
||||
STVF(g(y, x), gv * vfactor);
|
||||
STVF(b(y, x), bv * vfactor);
|
||||
}
|
||||
#endif
|
||||
for (; x < W; ++x) {
|
||||
r(y, x) *= factor;
|
||||
g(y, x) *= factor;
|
||||
b(y, x) *= factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal)
|
||||
{
|
||||
|
||||
@ -362,43 +394,15 @@ void Imagefloat::normalizeFloat(float srcMinVal, float srcMaxVal)
|
||||
}
|
||||
|
||||
// convert values's range to [0;1] ; this method assumes that the input values's range is [0;65535]
|
||||
void Imagefloat::normalizeFloatTo1()
|
||||
void Imagefloat::normalizeFloatTo1(bool multithread)
|
||||
{
|
||||
|
||||
const int w = width;
|
||||
const int h = height;
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for schedule(dynamic, 5)
|
||||
#endif
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
r(y, x) /= 65535.f;
|
||||
g(y, x) /= 65535.f;
|
||||
b(y, x) /= 65535.f;
|
||||
}
|
||||
}
|
||||
multiply(1.f/65535.f, multithread);
|
||||
}
|
||||
|
||||
// convert values's range to [0;65535 ; this method assumes that the input values's range is [0;1]
|
||||
void Imagefloat::normalizeFloatTo65535()
|
||||
void Imagefloat::normalizeFloatTo65535(bool multithread)
|
||||
{
|
||||
|
||||
const int w = width;
|
||||
const int h = height;
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for schedule(dynamic, 5)
|
||||
#endif
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
r(y, x) *= 65535.f;
|
||||
g(y, x) *= 65535.f;
|
||||
b(y, x) *= 65535.f;
|
||||
}
|
||||
}
|
||||
multiply(65535.f, multithread);
|
||||
}
|
||||
|
||||
// Parallelized transformation; create transform with cmsFLAGS_NOCACHE!
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
362
rtengine/iptoneequalizer.cc
Normal 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 ¶ms,
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -159,6 +159,7 @@ set(NONCLISOURCEFILES
|
||||
thumbimageupdater.cc
|
||||
thumbnail.cc
|
||||
tonecurve.cc
|
||||
toneequalizer.cc
|
||||
toolbar.cc
|
||||
toolpanel.cc
|
||||
toolpanelcoord.cc
|
||||
|
@ -51,6 +51,7 @@ Adjuster::Adjuster(
|
||||
grid(nullptr),
|
||||
label(nullptr),
|
||||
imageIcon1(imgIcon1),
|
||||
imageIcon2(imgIcon2),
|
||||
automatic(nullptr),
|
||||
adjusterListener(nullptr),
|
||||
spinChange(options.adjusterMinDelay, options.adjusterMaxDelay),
|
||||
@ -76,8 +77,8 @@ Adjuster::Adjuster(
|
||||
setExpandAlignProperties(imageIcon1, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
|
||||
}
|
||||
|
||||
if (imgIcon2) {
|
||||
setExpandAlignProperties(imgIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
|
||||
if (imageIcon2) {
|
||||
setExpandAlignProperties(imageIcon2, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
|
||||
}
|
||||
|
||||
set_column_spacing(0);
|
||||
@ -120,9 +121,9 @@ Adjuster::Adjuster(
|
||||
attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1);
|
||||
}
|
||||
|
||||
if (imgIcon2) {
|
||||
attach_next_to(*imgIcon2, *slider, Gtk::POS_RIGHT, 1, 1);
|
||||
attach_next_to(*spin, *imgIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
if (imageIcon2) {
|
||||
attach_next_to(*imageIcon2, *slider, Gtk::POS_RIGHT, 1, 1);
|
||||
attach_next_to(*spin, *imageIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
} else {
|
||||
attach_next_to(*spin, *slider, Gtk::POS_RIGHT, 1, 1);
|
||||
}
|
||||
@ -140,9 +141,9 @@ Adjuster::Adjuster(
|
||||
grid->attach_next_to(*imageIcon1, *slider, Gtk::POS_LEFT, 1, 1);
|
||||
}
|
||||
|
||||
if (imgIcon2) {
|
||||
grid->attach_next_to(*imgIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
grid->attach_next_to(*reset, *imgIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
if (imageIcon2) {
|
||||
grid->attach_next_to(*imageIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
grid->attach_next_to(*reset, *imageIcon2, Gtk::POS_RIGHT, 1, 1);
|
||||
} else {
|
||||
grid->attach_next_to(*reset, *slider, Gtk::POS_RIGHT, 1, 1);
|
||||
}
|
||||
@ -683,3 +684,13 @@ void Adjuster::setDelay(unsigned int min_delay_ms, unsigned int max_delay_ms)
|
||||
spinChange.setDelay(min_delay_ms, max_delay_ms);
|
||||
sliderChange.setDelay(min_delay_ms, max_delay_ms);
|
||||
}
|
||||
|
||||
void Adjuster::showIcons(bool yes)
|
||||
{
|
||||
if (imageIcon1) {
|
||||
imageIcon1->set_visible(yes);
|
||||
}
|
||||
if (imageIcon2) {
|
||||
imageIcon2->set_visible(yes);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
169
rtgui/toneequalizer.cc
Normal 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
56
rtgui/toneequalizer.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user