Merge pull request #6641 from Lawrence37/tone-equalizer

Global tone equalizer
This commit is contained in:
Lawrence37 2023-06-09 21:15:58 -07:00 committed by GitHub
commit c07a6e8901
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1047 additions and 316 deletions

View File

@ -1444,6 +1444,7 @@ HISTORY_MSG_LOCALCONTRAST_ENABLED;Local Contrast
HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness
HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius
HISTORY_MSG_LOCAL_GAMUTMUNSEL;Local - Gamut-Munsell
HISTORY_MSG_LOCALLAB_TE_PIVOT;Local - Equalizer pivot
HISTORY_MSG_METADATA_MODE;Metadata copy mode
HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold
HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold
@ -1489,6 +1490,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
@ -1796,6 +1802,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
@ -3387,6 +3394,7 @@ TP_LOCALLAB_STYPE_TOOLTIP;You can choose between:\nSymmetrical - left handle lin
TP_LOCALLAB_SYM;Symmetrical (mouse)
TP_LOCALLAB_SYMSL;Symmetrical (mouse + sliders)
TP_LOCALLAB_TARGET_GRAY;Mean luminance (Yb%)
TP_LOCALLAB_TE_PIVOT;Pivot (Ev)
TP_LOCALLAB_THRES;Threshold structure
TP_LOCALLAB_THRESDELTAE;ΔE scope threshold
TP_LOCALLAB_THRESRETI;Threshold
@ -3783,6 +3791,16 @@ 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_BANDS;Bands
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

@ -213,9 +213,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

@ -2809,6 +2809,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

@ -1979,7 +1979,9 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
stop.reset(new StopWatch("rgb processing"));
}
Imagefloat *tmpImage = nullptr;
const bool split_tiled_parts_1_2 = params->toneEqualizer.enabled;
std::unique_ptr<Imagefloat> tmpImage;
Imagefloat* editImgFloat = nullptr;
PlanarWhateverData<float>* editWhatever = nullptr;
@ -2139,6 +2141,7 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
const float comp = (max(0.0, expcomp) + 1.0) * hlcompr / 100.0;
const float shoulder = ((65536.f / max(1.0f, exp_scale)) * (hlcomprthresh / 200.f)) + 0.1f;
const float hlrange = 65536.f - shoulder;
const int tone_curve_black = params->toneCurve.black;
const bool isProPhoto = (params->icm.workingProfile == "ProPhoto");
// extracting data from 'params' to avoid cache flush (to be confirmed)
ToneCurveMode curveMode = params->toneCurve.curveMode;
@ -2247,8 +2250,8 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
}
bool hasgammabw = gammabwr != 1.f || gammabwg != 1.f || gammabwb != 1.f;
if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled)) {
tmpImage = new Imagefloat(working->getWidth(), working->getHeight());
if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled) || split_tiled_parts_1_2) {
tmpImage.reset(new Imagefloat(working->getWidth(), working->getHeight()));
}
// For tonecurve histogram
@ -2263,9 +2266,53 @@ 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])};
#define TS 112
const auto tiled_part_1 =
[working,
mixchannels,
&hltonecurve, &shtonecurve,
chMixRR, chMixRG, chMixRB,
chMixGR, chMixGG, chMixGB,
chMixBR, chMixBG, chMixBB,
exp_scale, comp, hlrange, tone_curve_black](
int istart, int jstart, int tH, int tW,
float *rtemp, float *gtemp, float *btemp) {
for (int i = istart, ti = 0; i < tH; i++, ti++) {
for (int j = jstart, tj = 0; j < tW; j++, tj++) {
rtemp[ti * TS + tj] = working->r(i, j);
gtemp[ti * TS + tj] = working->g(i, j);
btemp[ti * TS + tj] = working->b(i, j);
}
}
if (mixchannels) {
for (int i = istart, ti = 0; i < tH; i++, ti++) {
for (int j = jstart, tj = 0; j < tW; j++, tj++) {
float r = rtemp[ti * TS + tj];
float g = gtemp[ti * TS + tj];
float b = btemp[ti * TS + tj];
// if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b);
float rmix = (r * chMixRR + g * chMixRG + b * chMixRB) / 100.f;
float gmix = (r * chMixGR + g * chMixGG + b * chMixGB) / 100.f;
float bmix = (r * chMixBR + g * chMixBG + b * chMixBB) / 100.f;
rtemp[ti * TS + tj] = rmix;
gtemp[ti * TS + tj] = gmix;
btemp[ti * TS + tj] = bmix;
}
}
}
highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange);
if (tone_curve_black != 0) {
shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS);
}
};
#ifdef _OPENMP
#pragma omp parallel if (multiThread)
#endif
@ -2316,6 +2363,41 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
histToneCurveThr.clear();
}
if (split_tiled_parts_1_2) {
#ifdef _OPENMP
#pragma omp for schedule(dynamic, chunkSize) collapse(2)
#endif
for (int ii = 0; ii < working->getHeight(); ii += TS) {
for (int jj = 0; jj < working->getWidth(); jj += TS) {
istart = ii;
jstart = jj;
tH = min(ii + TS, working->getHeight());
tW = min(jj + TS, working->getWidth());
tiled_part_1(istart, jstart, tH, tW, rtemp, gtemp, btemp);
// Copy tile to image.
for (int i = istart, ti = 0; i < tH; i++, ti++) {
for (int j = jstart, tj = 0; j < tW; j++, tj++) {
tmpImage->r(i, j) = rtemp[ti * TS + tj];
tmpImage->g(i, j) = gtemp[ti * TS + tj];
tmpImage->b(i, j) = btemp[ti * TS + tj];
}
}
}
}
}
#ifdef _OPENMP
#pragma omp single
#endif
if (params->toneEqualizer.enabled) {
toneEqualizer(tmpImage.get());
}
#ifdef _OPENMP
#pragma omp for schedule(dynamic, chunkSize) collapse(2)
#endif
@ -2327,38 +2409,16 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
tH = min(ii + TS, working->getHeight());
tW = min(jj + TS, working->getWidth());
for (int i = istart, ti = 0; i < tH; i++, ti++) {
for (int j = jstart, tj = 0; j < tW; j++, tj++) {
rtemp[ti * TS + tj] = working->r(i, j);
gtemp[ti * TS + tj] = working->g(i, j);
btemp[ti * TS + tj] = working->b(i, j);
}
}
if (mixchannels) {
if (split_tiled_parts_1_2) {
for (int i = istart, ti = 0; i < tH; i++, ti++) {
for (int j = jstart, tj = 0; j < tW; j++, tj++) {
float r = rtemp[ti * TS + tj];
float g = gtemp[ti * TS + tj];
float b = btemp[ti * TS + tj];
//if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b);
float rmix = (r * chMixRR + g * chMixRG + b * chMixRB) / 100.f;
float gmix = (r * chMixGR + g * chMixGG + b * chMixGB) / 100.f;
float bmix = (r * chMixBR + g * chMixBG + b * chMixBB) / 100.f;
rtemp[ti * TS + tj] = rmix;
gtemp[ti * TS + tj] = gmix;
btemp[ti * TS + tj] = bmix;
rtemp[ti * TS + tj] = tmpImage->r(i, j);
gtemp[ti * TS + tj] = tmpImage->g(i, j);
btemp[ti * TS + tj] = tmpImage->b(i, j);
}
}
}
highlightToneCurve(hltonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS, exp_scale, comp, hlrange);
if (params->toneCurve.black != 0.0) {
shadowToneCurve(shtonecurve, rtemp, gtemp, btemp, istart, tH, jstart, tW, TS);
} else {
tiled_part_1(istart, jstart, tH, tW, rtemp, gtemp, btemp);
}
if (dcpProf) {
@ -3509,10 +3569,6 @@ void ImProcFunctions::rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer
}
if (tmpImage) {
delete tmpImage;
}
if (hCurveEnabled) {
delete hCurve;
}

View File

@ -97,6 +97,7 @@ struct LocalContrastParams;
struct LocallabParams;
struct SharpeningParams;
struct SoftLightParams;
struct ToneEqualizerParams;
struct VibranceParams;
struct VignettingParams;
struct WaveletParams;
@ -493,7 +494,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);
void toneEqualizer(Imagefloat *rgb);
void toneEqualizer(Imagefloat *rgb, const procparams::ToneEqualizerParams &params, const Glib::ustring &workingProfile, double scale, bool multiThread);
void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams);
void labColorCorrectionRegions(LabImage *lab);

View File

@ -703,6 +703,7 @@ struct local_params {
float mulloc[6];
int mullocsh[5];
int detailsh;
double tePivot;
float threshol;
float chromacb;
float strengt;
@ -1700,6 +1701,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall
lp.detailsh = locallab.spots.at(sp).detailSH;
lp.tePivot = locallab.spots.at(sp).tePivot;
lp.threshol = thresho;
lp.chromacb = chromcbdl;
lp.expvib = locallab.spots.at(sp).expvibrance && lp.activspot ;
@ -2267,231 +2269,14 @@ 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/>.
*/
void tone_eq(ImProcFunctions *ipf, Imagefloat *rgb, const struct local_params &lp, const Glib::ustring &workingProfile, double scale, bool multithread)
{
// 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.enabled = true;
params.regularization = lp.detailsh;
params.pivot = lp.tePivot;
std::copy(lp.mullocsh, lp.mullocsh + params.bands.size(), params.bands.begin());
ipf->toneEqualizer(rgb, params, workingProfile, scale, multithread);
}
void ImProcFunctions::loccont(int bfw, int bfh, LabImage* tmp1, float rad, float stren, int sk)
{
@ -7815,12 +7600,7 @@ void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp
}
if (tonequ) {
tmpImage->normalizeFloatTo1();
array2D<float> Rtemp(GW, GH, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> Gtemp(GW, GH, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> Btemp(GW, GH, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE);
tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, sk, multiThread);
tmpImage->normalizeFloatTo65535();
tone_eq(this, tmpImage.get(), lp, params->icm.workingProfile, sk, multiThread);
}
rgb2lab(*tmpImage, *temp, params->icm.workingProfile);
@ -16019,12 +15799,7 @@ void ImProcFunctions::Lab_Local(
}
if (tonequ) {
tmpImage->normalizeFloatTo1();
array2D<float> Rtemp(bfw, bfh, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> Gtemp(bfw, bfh, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE);
array2D<float> Btemp(bfw, bfh, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE);
tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, scal, multiThread);
tmpImage->normalizeFloatTo65535();
tone_eq(this, tmpImage, lp, params->icm.workingProfile, scal, multiThread);
}
rgb2lab(*tmpImage, *bufexpfin, params->icm.workingProfile);

373
rtengine/iptoneequalizer.cc Normal file
View File

@ -0,0 +1,373 @@
#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}, // blacks
{0.f, 0.f, 1.f}, // shadows
{0.5f, 0.5f, 0.5f}, // midtones
{1.f, 1.f, 0.f}, // highlights
{1.f, 0.f, 0.f}, // whites
{1.f, 0.f, 0.f},
{1.f, 0.f, 0.f},
{1.f, 0.f, 0.f}
};
void toneEqualizer(
array2D<float> &R, array2D<float> &G, array2D<float> &B,
const rtengine::ToneEqualizerParams &params,
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] = {
-16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f,
-4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f
};
const auto conv = [&](int v, float lo, float hi) -> float {
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), // -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
conv(params.bands[4], 3.f, 2.f) // 6 EV
};
rtengine::TMatrix ws = rtengine::ICCStore::getInstance()->workingSpaceMatrix(workingProfile);
#ifdef _OPENMP
#pragma omp parallel for if (multithread)
#endif
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
Y[y][x] = rtengine::Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws);
}
}
int detail = rtengine::LIM(params.regularization + 5, 0, 5);
int radius = detail / scale + 0.5;
float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0);
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 = rtengine::LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]);
float ll = round(l * base_posterization) / base_posterization;
Y2[y][x] = Y[y][x];
Y[y][x] = exp2(ll);
}
}
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((-rtengine::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);
}
constexpr float luma_lo = -14.f;
constexpr float luma_hi = 4.f;
const auto process_pixel =
[&](float y) -> float {
// convert to log space
const float luma = rtengine::LIM(log2(rtengine::max(y, 0.f)), luma_lo, luma_hi);
// build the correction as the sum of the contribution of each
// luminance channel to current pixel
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 (params.show_colormap) {
rtengine::lcmsMutex->lock();
cmsHPROFILE in = rtengine::ICCStore::getInstance()->getsRGBProfile();
cmsHPROFILE out = rtengine::ICCStore::getInstance()->workingSpace(workingProfile);
cmsHTRANSFORM xform = cmsCreateTransform(in, TYPE_RGB_FLT, out, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE);
rtengine::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 = rtengine::LIM(log2(rtengine::max(y, 0.f)), luma_lo, luma_hi);
// build the correction as the sum of the contribution of each
// luminance channel to current pixel
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] = rtengine::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((-rtengine::SQR(x - b) / fourv));
};
vfloat zerov = F2V(0.f);
vfloat vw_sum = F2V(w_sum);
const vfloat vluma_lo = F2V(luma_lo);
const vfloat vluma_hi = F2V(luma_hi);
const vfloat xlog2v = F2V(xlogf(2.f));
const auto vprocess_pixel =
[&](vfloat y) -> vfloat {
const vfloat luma = vminf(vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, vluma_lo), vluma_hi);
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 (params.show_colormap) {
LUTf lut_r(65537), lut_g(65537), lut_b(65537);
for (int i = 0; i < 65536; ++i) {
float y = float(i)/65535.f;
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;
}
}
}
}
namespace rtengine
{
void ImProcFunctions::toneEqualizer(
Imagefloat *rgb,
const ToneEqualizerParams &params,
const Glib::ustring &workingProfile,
double scale,
bool multiThread)
{
if (!params.enabled) {
return;
}
BENCHFUN
const float gain = 1.f / 65535.f * std::pow(2.f, -params.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);
::toneEqualizer(R, G, B, params, workingProfile, scale, multiThread);
rgb->multiply(params.show_colormap ? 65535.f : 1.f/gain, multiThread);
}
void ImProcFunctions::toneEqualizer(Imagefloat *rgb)
{
toneEqualizer(rgb, params->toneEqualizer, params->icm.workingProfile, scale, multiThread);
}
}

View File

@ -1866,6 +1866,30 @@ bool SHParams::operator !=(const SHParams& other) const
return !(*this == other);
}
ToneEqualizerParams::ToneEqualizerParams() :
enabled(false),
bands{0, 0, 0, 0, 0},
regularization(0),
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),
@ -3381,6 +3405,7 @@ LocallabParams::LocallabSpot::LocallabSpot() :
slomaskSH(0.0),
lapmaskSH(0.0),
detailSH(0),
tePivot(0.),
reparsh(100.),
LmaskSHcurve{
static_cast<double>(DCT_NURBS),
@ -4825,6 +4850,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const
&& slomaskSH == other.slomaskSH
&& lapmaskSH == other.lapmaskSH
&& detailSH == other.detailSH
&& tePivot == other.tePivot
&& reparsh == other.reparsh
&& LmaskSHcurve == other.LmaskSHcurve
&& fatamountSH == other.fatamountSH
@ -5896,6 +5922,8 @@ void ProcParams::setDefaults()
sh = {};
toneEqualizer = {};
crop = {};
coarse = {};
@ -6334,6 +6362,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[i], "ToneEqualizer", "Band" + std::to_string(i), toneEqualizer.bands[i], keyFile);
}
saveToKeyfile(!pedited || pedited->toneEqualizer.regularization, "ToneEqualizer", "Regularization", toneEqualizer.regularization, keyFile);
saveToKeyfile(!pedited || pedited->toneEqualizer.pivot, "ToneEqualizer", "Pivot", toneEqualizer.pivot, keyFile);
// Crop
saveToKeyfile(!pedited || pedited->crop.enabled, "Crop", "Enabled", crop.enabled, keyFile);
saveToKeyfile(!pedited || pedited->crop.x, "Crop", "X", crop.x, keyFile);
@ -6620,6 +6656,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || spot_edited->gammaskSH, "Locallab", "GammaskSH_" + index_str, spot.gammaskSH, keyFile);
saveToKeyfile(!pedited || spot_edited->slomaskSH, "Locallab", "SlomaskSH_" + index_str, spot.slomaskSH, keyFile);
saveToKeyfile(!pedited || spot_edited->detailSH, "Locallab", "DetailSH_" + index_str, spot.detailSH, keyFile);
saveToKeyfile(!pedited || spot_edited->tePivot, "Locallab", "TePivot_" + index_str, spot.tePivot, keyFile);
saveToKeyfile(!pedited || spot_edited->reparsh, "Locallab", "Reparsh_" + index_str, spot.reparsh, keyFile);
saveToKeyfile(!pedited || spot_edited->LmaskSHcurve, "Locallab", "LmaskSHCurve_" + index_str, spot.LmaskSHcurve, keyFile);
saveToKeyfile(!pedited || spot_edited->fatamountSH, "Locallab", "FatamountSH_" + index_str, spot.fatamountSH, keyFile);
@ -8372,6 +8409,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[i]);
}
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);
@ -8779,6 +8825,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
assignFromKeyfile(keyFile, "Locallab", "SlomaskSH_" + index_str, pedited, spot.slomaskSH, spotEdited.slomaskSH);
assignFromKeyfile(keyFile, "Locallab", "LapmaskSH_" + index_str, pedited, spot.lapmaskSH, spotEdited.lapmaskSH);
assignFromKeyfile(keyFile, "Locallab", "DetailSH_" + index_str, pedited, spot.detailSH, spotEdited.detailSH);
assignFromKeyfile(keyFile, "Locallab", "TePivot_" + index_str, pedited, spot.tePivot, spotEdited.tePivot);
assignFromKeyfile(keyFile, "Locallab", "Reparsh_" + index_str, pedited, spot.reparsh, spotEdited.reparsh);
assignFromKeyfile(keyFile, "Locallab", "LmaskSHCurve_" + index_str, pedited, spot.LmaskSHcurve, spotEdited.LmaskSHcurve);
assignFromKeyfile(keyFile, "Locallab", "FatamountSH_" + index_str, pedited, spot.fatamountSH, spotEdited.fatamountSH);
@ -10615,6 +10662,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

@ -18,6 +18,7 @@
*/
#pragma once
#include <array>
#include <cmath>
#include <cstdio>
#include <map>
@ -840,6 +841,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
*/
@ -1210,6 +1227,7 @@ struct LocallabParams {
double slomaskSH;
double lapmaskSH;
int detailSH;
double tePivot;
double reparsh;
std::vector<double> LmaskSHcurve;
double fatamountSH;
@ -2562,6 +2580,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

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

View File

@ -10,6 +10,9 @@ enum {
ADDSET_SH_HIGHLIGHTS,
ADDSET_SH_SHADOWS,
ADDSET_SH_LOCALCONTRAST, // not used anymore
ADDSET_TONE_EQUALIZER_BANDS,
ADDSET_TONE_EQUALIZER_PIVOT,
ADDSET_TONE_EQUALIZER_REGULARIZATION,
ADDSET_LC_BRIGHTNESS,
ADDSET_LC_CONTRAST,
ADDSET_SHARP_AMOUNT,

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,15 @@ 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);
imageIcon1->set_no_show_all(!yes);
}
if (imageIcon2) {
imageIcon2->set_visible(yes);
imageIcon2->set_no_show_all(!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

@ -219,6 +219,7 @@ void BatchToolPanelCoordinator::initSession ()
chmixer->setAdjusterBehavior (options.baBehav[ADDSET_CHMIXER] );
blackwhite->setAdjusterBehavior (options.baBehav[ADDSET_BLACKWHITE_HUES], options.baBehav[ADDSET_BLACKWHITE_GAMMA]);
shadowshighlights->setAdjusterBehavior (options.baBehav[ADDSET_SH_HIGHLIGHTS], options.baBehav[ADDSET_SH_SHADOWS]);
toneEqualizer->setAdjusterBehavior(options.baBehav[ADDSET_TONE_EQUALIZER_BANDS], options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION], options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT]);
dirpyrequalizer->setAdjusterBehavior (options.baBehav[ADDSET_DIRPYREQ], options.baBehav[ADDSET_DIRPYREQ_THRESHOLD], options.baBehav[ADDSET_DIRPYREQ_SKINPROTECT]);
wavelet->setAdjusterBehavior (options.baBehav[ADDSET_WA], options.baBehav[ADDSET_WA_THRESHOLD], options.baBehav[ADDSET_WA_THRESHOLD2], options.baBehav[ADDSET_WA_THRES], options.baBehav[ADDSET_WA_CHRO], options.baBehav[ADDSET_WA_CHROMA], options.baBehav[ADDSET_WA_CONTRAST], options.baBehav[ADDSET_WA_SKINPROTECT], options.baBehav[ADDSET_WA_RESCHRO], options.baBehav[ADDSET_WA_TMRS], options.baBehav[ADDSET_WA_EDGS], options.baBehav[ADDSET_WA_SCALE], options.baBehav[ADDSET_WA_RESCON], options.baBehav[ADDSET_WA_RESCONH], options.baBehav[ADDSET_WA_THRR], options.baBehav[ADDSET_WA_THRRH], options.baBehav[ADDSET_WA_RADIUS], options.baBehav[ADDSET_WA_SKYPROTECT], options.baBehav[ADDSET_WA_EDGRAD], options.baBehav[ADDSET_WA_EDGVAL], options.baBehav[ADDSET_WA_STRENGTH], options.baBehav[ADDSET_WA_GAMMA], options.baBehav[ADDSET_WA_EDGEDETECT], options.baBehav[ADDSET_WA_EDGEDETECTTHR], options.baBehav[ADDSET_WA_EDGEDETECTTHR2]);
dirpyrdenoise->setAdjusterBehavior (options.baBehav[ADDSET_DIRPYRDN_LUMA], options.baBehav[ADDSET_DIRPYRDN_LUMDET], options.baBehav[ADDSET_DIRPYRDN_CHROMA], options.baBehav[ADDSET_DIRPYRDN_CHROMARED], options.baBehav[ADDSET_DIRPYRDN_CHROMABLUE], options.baBehav[ADDSET_DIRPYRDN_GAMMA], options.baBehav[ADDSET_DIRPYRDN_PASSES]);
@ -242,6 +243,9 @@ void BatchToolPanelCoordinator::initSession ()
if (options.baBehav[ADDSET_TC_SATURATION]) { pparams.toneCurve.saturation = 0;}
if (options.baBehav[ADDSET_SH_HIGHLIGHTS]) { pparams.sh.highlights = 0; }
if (options.baBehav[ADDSET_SH_SHADOWS]) { pparams.sh.shadows = 0; }
if (options.baBehav[ADDSET_TONE_EQUALIZER_BANDS]) { pparams.toneEqualizer.bands = {}; }
if (options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT]) { pparams.toneEqualizer.pivot = 0; }
if (options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION]) { pparams.toneEqualizer.regularization = 0; }
if (options.baBehav[ADDSET_LC_BRIGHTNESS]) { pparams.labCurve.brightness = 0; }
if (options.baBehav[ADDSET_LC_CONTRAST]) { pparams.labCurve.contrast = 0; }
if (options.baBehav[ADDSET_LC_CHROMATICITY]) { pparams.labCurve.chromaticity = 0; }

View File

@ -3977,6 +3977,7 @@ LocallabShadow::LocallabShadow():
}
()),
detailSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAILSH"), -5, 5, 1, 0))),
tePivot(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TE_PIVOT"), -12, 12, 0.05, 0))),
highlights(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), 0, 100, 1, 0))),
h_tonalwidth(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HLTONALW"), 10, 100, 1, 70))),
shadows(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_SHADOWS"), 0, 100, 1, 0))),
@ -4017,7 +4018,8 @@ LocallabShadow::LocallabShadow():
LmaskSHshape(static_cast<DiagonalCurveEditor*>(mask2SHCurveEditorG->addCurve(CT_Diagonal, "L(L)"))),
fatSHFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_FATSHFRA")))),
fatamountSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))),
fatanchorSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png")))))
fatanchorSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))),
EvlocallabTePivot(ProcEventMapper::getInstance()->newEvent(AUTOEXP, "HISTORY_MSG_LOCALLAB_TE_PIVOT"))
{
set_orientation(Gtk::ORIENTATION_VERTICAL);
@ -4034,6 +4036,7 @@ LocallabShadow::LocallabShadow():
}
detailSH->setAdjusterListener(this);
tePivot->setAdjusterListener(this);
reparsh->setAdjusterListener(this);
highlights->setAdjusterListener(this);
@ -4144,6 +4147,7 @@ LocallabShadow::LocallabShadow():
}
pack_start(*detailSH);
pack_start(*tePivot);
pack_start(*highlights);
pack_start(*h_tonalwidth);
pack_start(*shadows);
@ -4364,6 +4368,7 @@ void LocallabShadow::read(const rtengine::procparams::ProcParams* pp, const Para
decays->setValue((double)spot.decays);
detailSH->setValue((double)spot.detailSH);
tePivot->setValue(spot.tePivot);
reparsh->setValue(spot.reparsh);
highlights->setValue((double)spot.highlights);
h_tonalwidth->setValue((double)spot.h_tonalwidth);
@ -4429,6 +4434,7 @@ void LocallabShadow::write(rtengine::procparams::ProcParams* pp, ParamsEdited* p
}
spot.detailSH = detailSH->getIntValue();
spot.tePivot = tePivot->getValue();
spot.reparsh = reparsh->getValue();
spot.highlights = highlights->getIntValue();
spot.h_tonalwidth = h_tonalwidth->getIntValue();
@ -4477,6 +4483,7 @@ void LocallabShadow::setDefaults(const rtengine::procparams::ProcParams* defPara
}
detailSH->setDefault((double)defSpot.detailSH);
tePivot->setDefault(defSpot.tePivot);
reparsh->setDefault(defSpot.reparsh);
highlights->setDefault((double)defSpot.highlights);
h_tonalwidth->setDefault((double)defSpot.h_tonalwidth);
@ -4528,6 +4535,13 @@ void LocallabShadow::adjusterChanged(Adjuster* a, double newval)
}
}
if (a == tePivot) {
if (listener) {
listener->panelChanged(EvlocallabTePivot,
tePivot->getTextValue() + " (" + escapeHtmlChars(getSpotName()) + ")");
}
}
if (a == reparsh) {
if (listener) {
listener->panelChanged(Evlocallabreparsh,
@ -5044,6 +5058,7 @@ void LocallabShadow::updateShadowGUI2()
gamFrame->hide();
detailSH->hide();
tePivot->hide();
highlights->show();
h_tonalwidth->show();
shadows->show();
@ -5059,6 +5074,7 @@ void LocallabShadow::updateShadowGUI2()
}
detailSH->show();
tePivot->show();
highlights->hide();
h_tonalwidth->hide();
shadows->hide();

View File

@ -451,6 +451,7 @@ private:
Adjuster* const reparsh;
const std::array<Adjuster*, 5> multipliersh;
Adjuster* const detailSH;
Adjuster* const tePivot;
Adjuster* const highlights;
Adjuster* const h_tonalwidth;
Adjuster* const shadows;
@ -492,6 +493,8 @@ private:
Adjuster* const fatamountSH;
Adjuster* const fatanchorSH;
rtengine::ProcEvent EvlocallabTePivot;
sigc::connection shMethodConn, inversshConn, showmaskSHMethodConn, showmaskSHMethodConninv, enaSHMaskConn;
public:

View File

@ -328,6 +328,11 @@ void ParamsEdited::set(bool v)
sh.stonalwidth = v;
sh.radius = v;
sh.lab = v;
toneEqualizer.enabled = v;
toneEqualizer.bands.fill(v);
toneEqualizer.regularization = v;
toneEqualizer.show_colormap = v;
toneEqualizer.pivot = v;
crop.enabled = v;
crop.x = v;
crop.y = v;
@ -1054,6 +1059,13 @@ 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;
for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
toneEqualizer.bands[i] = toneEqualizer.bands[i] && p.toneEqualizer.bands[i] == other.toneEqualizer.bands[i];
}
toneEqualizer.regularization = toneEqualizer.regularization && p.toneEqualizer.regularization == other.toneEqualizer.regularization;
toneEqualizer.show_colormap = toneEqualizer.show_colormap && p.toneEqualizer.show_colormap == other.toneEqualizer.show_colormap;
toneEqualizer.pivot = toneEqualizer.pivot && p.toneEqualizer.pivot == other.toneEqualizer.pivot;
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;
@ -1293,6 +1305,7 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
locallab.spots.at(j).slomaskSH = locallab.spots.at(j).slomaskSH && pSpot.slomaskSH == otherSpot.slomaskSH;
locallab.spots.at(j).lapmaskSH = locallab.spots.at(j).lapmaskSH && pSpot.lapmaskSH == otherSpot.lapmaskSH;
locallab.spots.at(j).detailSH = locallab.spots.at(j).detailSH && pSpot.detailSH == otherSpot.detailSH;
locallab.spots.at(j).tePivot = locallab.spots.at(j).tePivot && pSpot.tePivot == otherSpot.tePivot;
locallab.spots.at(j).reparsh = locallab.spots.at(j).reparsh && pSpot.reparsh == otherSpot.reparsh;
locallab.spots.at(j).LmaskSHcurve = locallab.spots.at(j).LmaskSHcurve && pSpot.LmaskSHcurve == otherSpot.LmaskSHcurve;
locallab.spots.at(j).fatamountSH = locallab.spots.at(j).fatamountSH && pSpot.fatamountSH == otherSpot.fatamountSH;
@ -3263,6 +3276,37 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.sh.lab = mods.sh.lab;
}
if (toneEqualizer.enabled) {
toEdit.toneEqualizer.enabled = mods.toneEqualizer.enabled;
}
for (size_t i = 0; i < toneEqualizer.bands.size(); ++i) {
if (toneEqualizer.bands[i]) {
toEdit.toneEqualizer.bands[i] =
dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_BANDS]
? toEdit.toneEqualizer.bands[i] + mods.toneEqualizer.bands[i]
: mods.toneEqualizer.bands[i];
}
}
if (toneEqualizer.regularization) {
toEdit.toneEqualizer.regularization =
dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_REGULARIZATION]
? toEdit.toneEqualizer.regularization + mods.toneEqualizer.regularization
: mods.toneEqualizer.regularization;
}
if (toneEqualizer.show_colormap) {
toEdit.toneEqualizer.show_colormap = mods.toneEqualizer.show_colormap;
}
if (toneEqualizer.pivot) {
toEdit.toneEqualizer.pivot =
dontforceSet && options.baBehav[ADDSET_TONE_EQUALIZER_PIVOT]
? toEdit.toneEqualizer.pivot + mods.toneEqualizer.pivot
: mods.toneEqualizer.pivot;
}
if (crop.enabled) {
toEdit.crop.enabled = mods.crop.enabled;
}
@ -4205,6 +4249,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.locallab.spots.at(i).detailSH = mods.locallab.spots.at(i).detailSH;
}
if (locallab.spots.at(i).tePivot) {
toEdit.locallab.spots.at(i).tePivot = mods.locallab.spots.at(i).tePivot;
}
if (locallab.spots.at(i).reparsh) {
toEdit.locallab.spots.at(i).reparsh = mods.locallab.spots.at(i).reparsh;
}
@ -7654,6 +7702,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) :
slomaskSH(v),
lapmaskSH(v),
detailSH(v),
tePivot(v),
reparsh(v),
LmaskSHcurve(v),
fatamountSH(v),
@ -8349,6 +8398,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v)
slomaskSH = v;
lapmaskSH = v;
detailSH = v;
tePivot = v;
reparsh = v;
LmaskSHcurve = v;
fatamountSH = v;

View File

@ -18,6 +18,7 @@
*/
#pragma once
#include <array>
#include <vector>
namespace rtengine
@ -370,6 +371,14 @@ struct SHParamsEdited {
bool lab;
};
struct ToneEqualizerParamsEdited {
bool enabled;
std::array<bool, 5> bands;
bool regularization;
bool show_colormap;
bool pivot;
};
struct CropParamsEdited {
bool enabled;
bool x;
@ -591,6 +600,7 @@ public:
bool slomaskSH;
bool lapmaskSH;
bool detailSH;
bool tePivot;
bool reparsh;
bool LmaskSHcurve;
bool fatamountSH;
@ -1558,6 +1568,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")));
@ -332,6 +333,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);
@ -498,6 +500,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));
@ -706,6 +709,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);
@ -717,6 +721,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 ());
@ -894,6 +899,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;
@ -226,6 +227,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

@ -232,6 +232,12 @@ Gtk::Widget* Preferences::getBatchProcPanel()
appendBehavList(mi, M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), ADDSET_SH_HIGHLIGHTS, false);
appendBehavList(mi, M("TP_SHADOWSHLIGHTS_SHADOWS"), ADDSET_SH_SHADOWS, false);
mi = behModel->append();
mi->set_value(behavColumns.label, M("TP_TONE_EQUALIZER_LABEL"));
appendBehavList(mi, M("TP_TONE_EQUALIZER_BANDS"), ADDSET_TONE_EQUALIZER_BANDS, false);
appendBehavList(mi, M("TP_TONE_EQUALIZER_PIVOT"), ADDSET_TONE_EQUALIZER_PIVOT, false);
appendBehavList(mi, M("TP_TONE_EQUALIZER_DETAIL"), ADDSET_TONE_EQUALIZER_REGULARIZATION, false);
mi = behModel->append();
mi->set_value(behavColumns.label, M("TP_LABCURVE_LABEL"));
appendBehavList(mi, M("TP_LABCURVE_BRIGHTNESS"), ADDSET_LC_BRIGHTNESS, false);

View File

@ -463,6 +463,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

237
rtgui/toneequalizer.cc Normal file
View File

@ -0,0 +1,237 @@
/*
* 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;
const Glib::ustring ToneEqualizer::TOOL_NAME = "toneequalizer";
ToneEqualizer::ToneEqualizer(): FoldableToolPanel(this, TOOL_NAME, M("TP_TONE_EQUALIZER_LABEL"), false, true)
{
auto m = ProcEventMapper::getInstance();
EvEnabled = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_ENABLED");
EvBands = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_BANDS");
EvRegularization = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION");
EvColormap = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP");
EvPivot = m->newEvent(AUTOEXP, "HISTORY_MSG_TONE_EQUALIZER_PIVOT");
std::array<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, 0));
regularization->setAdjusterListener(this);
pack_start(*regularization);
show_colormap = Gtk::manage(new CheckBox(M("TP_TONE_EQUALIZER_SHOW_COLOR_MAP"), multiImage));
pack_start(*show_colormap);
show_colormap->setCheckBoxListener(this);
show_all_children ();
}
void ToneEqualizer::read(const ProcParams *pp, const ParamsEdited* pedited)
{
disableListener();
if (pedited) {
set_inconsistent(multiImage && !pedited->toneEqualizer.enabled);
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->setEditedState(pedited->toneEqualizer.bands[i] ? Edited : UnEdited);
}
regularization->setEditedState(pedited->toneEqualizer.regularization ? Edited : UnEdited);
pivot->setEditedState(pedited->toneEqualizer.pivot ? Edited : UnEdited);
show_colormap->setEdited(pedited->toneEqualizer.show_colormap ? Edited : UnEdited);
}
setEnabled(pp->toneEqualizer.enabled);
for (size_t i = 0; i < bands.size(); ++i) {
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->setValue(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->getLastActive();
pp->toneEqualizer.pivot = pivot->getValue();
if (pedited) {
auto &edited = pedited->toneEqualizer;
edited.enabled = !get_inconsistent();
for (size_t i = 0; i < bands.size(); ++i) {
edited.bands[i] = bands[i]->getEditedState();
}
edited.regularization = regularization->getEditedState();
edited.pivot = pivot->getEditedState();
edited.show_colormap = show_colormap->getEdited();
}
}
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;
if (pedited) {
auto &edited = pedited->toneEqualizer;
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->setDefaultEditedState(edited.bands[i] ? Edited : UnEdited);
}
regularization->setDefaultEditedState(edited.regularization ? Edited : UnEdited);
pivot->setDefaultEditedState(edited.pivot ? Edited : UnEdited);
} else {
for (auto band : bands) {
band->setDefaultEditedState(Irrelevant);
}
regularization->setDefaultEditedState(Irrelevant);
pivot->setDefaultEditedState(Irrelevant);
}
}
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::setBatchMode(bool batchMode)
{
ToolPanel::setBatchMode(batchMode);
if (batchMode) {
for (auto band : bands) {
band->showEditedCB();
}
regularization->showEditedCB();
pivot->showEditedCB();
}
}
void ToneEqualizer::setAdjusterBehavior(bool bands_add, bool regularization_add, bool pivot_add)
{
for (auto band : bands) {
band->setAddMode(bands_add);
}
regularization->setAddMode(regularization_add);
pivot->setAddMode(pivot_add);
}
void ToneEqualizer::checkBoxToggled(CheckBox *c, CheckValue newval)
{
if (c == show_colormap) {
colormapToggled();
}
}
void ToneEqualizer::colormapToggled()
{
for (size_t i = 0; i < bands.size(); ++i) {
bands[i]->showIcons(show_colormap->getLastActive());
}
if (listener && getEnabled()) {
listener->panelChanged(EvColormap, show_colormap->getLastActive() ? 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);
}

62
rtgui/toneequalizer.h Normal file
View File

@ -0,0 +1,62 @@
/* -*- 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 "checkbox.h"
#include "toolpanel.h"
class ToneEqualizer: public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public CheckBoxListener {
public:
static const Glib::ustring TOOL_NAME;
ToneEqualizer();
void read(const rtengine::procparams::ProcParams *pp, const ParamsEdited* pedited = nullptr) override;
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 setBatchMode(bool batchMode) override;
void setAdjusterBehavior(bool bands_add, bool regularization_add, bool pivot_add);
void checkBoxToggled(CheckBox* c, CheckValue newval) override;
void trimValues(rtengine::procparams::ProcParams *pp) override;
private:
void colormapToggled();
std::array<Adjuster *, 5> bands;
Adjuster *regularization;
Adjuster *pivot;
CheckBox *show_colormap;
rtengine::ProcEvent EvEnabled;
rtengine::ProcEvent EvBands;
rtengine::ProcEvent EvRegularization;
rtengine::ProcEvent EvColormap;
rtengine::ProcEvent EvPivot;
rtengine::procparams::ToneEqualizerParams inital_params;
};

View File

@ -70,6 +70,8 @@ Glib::ustring getToolTitleKey(Tool tool)
return "TP_EXPOSURE_LABEL";
case Tool::SHADOWS_HIGHLIGHTS:
return "TP_SHADOWSHLIGHTS_LABEL";
case Tool::TONE_EQUALIZER:
return "TP_TONE_EQUALIZER_LABEL";
case Tool::IMPULSE_DENOISE:
return "TP_IMPULSEDENOISE_LABEL";
case Tool::DEFRINGE_TOOL:

View File

@ -44,6 +44,9 @@ const std::vector<ToolTree> EXPOSURE_PANEL_TOOLS = {
{
.id = Tool::SHADOWS_HIGHLIGHTS,
},
{
.id = Tool::TONE_EQUALIZER,
},
{
.id = Tool::EPD,
},
@ -286,6 +289,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 ());
@ -561,6 +565,8 @@ std::string ToolPanelCoordinator::getToolName(Tool tool)
return ToneCurve::TOOL_NAME;
case Tool::SHADOWS_HIGHLIGHTS:
return ShadowsHighlights::TOOL_NAME;
case Tool::TONE_EQUALIZER:
return ToneEqualizer::TOOL_NAME;
case Tool::IMPULSE_DENOISE:
return ImpulseDenoise::TOOL_NAME;
case Tool::DEFRINGE_TOOL:
@ -1907,6 +1913,8 @@ FoldableToolPanel *ToolPanelCoordinator::getFoldableToolPanel(Tool tool) const
return toneCurve;
case Tool::SHADOWS_HIGHLIGHTS:
return shadowshighlights;
case Tool::TONE_EQUALIZER:
return toneEqualizer;
case Tool::IMPULSE_DENOISE:
return impulsedenoise;
case Tool::DEFRINGE_TOOL:

View File

@ -79,6 +79,7 @@
#include "softlight.h"
#include "spot.h"
#include "tonecurve.h"
#include "toneequalizer.h"
#include "toolbar.h"
#include "toolpanel.h"
#include "vibrance.h"
@ -134,6 +135,7 @@ protected:
Crop* crop;
ToneCurve* toneCurve;
ShadowsHighlights* shadowshighlights;
ToneEqualizer* toneEqualizer;
LocalContrast *localContrast;
Spot* spot;
Defringe* defringe;
@ -245,6 +247,7 @@ public:
enum class Tool {
TONE_CURVE,
SHADOWS_HIGHLIGHTS,
TONE_EQUALIZER,
IMPULSE_DENOISE,
DEFRINGE_TOOL,
SPOT,