Merge pull request #6641 from Lawrence37/tone-equalizer
Global tone equalizer
This commit is contained in:
commit
c07a6e8901
@ -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
|
||||
|
@ -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!
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ¶ms, const Glib::ustring &workingProfile, double scale, bool multiThread);
|
||||
void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams);
|
||||
void labColorCorrectionRegions(LabImage *lab);
|
||||
|
||||
|
@ -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
373
rtengine/iptoneequalizer.cc
Normal 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 ¶ms,
|
||||
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 ¶ms,
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -161,6 +161,7 @@ set(NONCLISOURCEFILES
|
||||
thumbimageupdater.cc
|
||||
thumbnail.cc
|
||||
tonecurve.cc
|
||||
toneequalizer.cc
|
||||
toolbar.cc
|
||||
toollocationpref.cc
|
||||
toolpanel.cc
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
237
rtgui/toneequalizer.cc
Normal 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
62
rtgui/toneequalizer.h
Normal 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;
|
||||
};
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user