diff --git a/rtdata/languages/default b/rtdata/languages/default index 278b0f5e3..ed0d098b2 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -650,7 +650,9 @@ HISTORY_MSG_417;Retinex - Transmission median HISTORY_MSG_418;Retinex - Threshold HISTORY_MSG_419;Retinex - Color space HISTORY_MSG_420;Retinex - Histogram - HSL -HISTORY_MSG_421;Retinex - Gamma +HISTORY_MSG_421;Retinex - Gamma retinex +HISTORY_MSG_422;Retinex - gamma +HISTORY_MSG_423;Retinex - slope HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOTS;Snapshots @@ -1561,11 +1563,14 @@ TP_RETINEX_CURVEEDITOR_CD;L=f(L) TP_RETINEX_CURVEEDITOR_CD_TOOLTIP;Correct raw data to reduce halos and artifacts TP_RETINEX_GAIN;Gain TP_RETINEX_GAIN_TOOLTIP;Acts on the transmission in combination with offset, this is very different from others settings. Used for black or white pixels, and for better balance the histogram. -TP_RETINEX_GAM;Gamma +TP_RETINEX_GAM;Gamma retinex TP_RETINEX_GAMMA_NONE;None TP_RETINEX_GAMMA_LOW;Low TP_RETINEX_GAMMA_MID;Middle TP_RETINEX_GAMMA_HIGH;High +TP_RETINEX_GAMMA_FREE;Free +TP_RETINEX_GAMMA;Gamma +TP_RETINEX_SLOPE;Slope TP_RETINEX_GAMMA_TOOLTIP;Restore tones by applying gamma before and after Retinex\nDifferent from Retinex curves or others curves (Lab, Exposure,..) TP_RETINEX_HIGH;High TP_RETINEX_HSLSPACE_LIN;HSL-Linear diff --git a/rtengine/color.cc b/rtengine/color.cc index b403ee7dc..03a9bda93 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -233,7 +233,7 @@ void Color::init () for (int i = 0; i < 65536; i++) { gammatab_26_11[i] = (65535.0 * gamma26_11 (i / 65535.0)); } - +//gammatab_145_3 for (int i = 0; i < 65536; i++) { igammatab_26_11[i] = (65535.0 * igamma26_11 (i / 65535.0)); } diff --git a/rtengine/color.h b/rtengine/color.h index 53d5a8b19..1eb799cda 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -981,6 +981,17 @@ public: { return x <= 0.027345 ? x / 3.0 : exp(log((x + 0.012305) / 1.012305) * 1.45); } + +//gamma for Retinex + static inline double gammareti (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add); + } + static inline double igammareti (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) ); + } + // gamma function with adjustable parameters diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 0f87bf1bd..ddae2f583 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -448,6 +448,8 @@ enum ProcEvent { EvretinexColorSpace = 418, // 418 if we want a separate history entry "Retinex - Color space", 406 if we don't EvLCDHCurve = 419, Evretinexgamma = 420, + EvLgam = 421, + EvLslope = 422, NUMOFEVENTS }; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index b2162bed7..566bcd13a 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -142,6 +142,8 @@ void RetinexParams::setDefaults() enabled = false; str = 20; scal = 3; + gam = 1.30; + slope = 3.; neigh = 80; gain = 50; offs = 0; @@ -1436,6 +1438,14 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol keyFile.set_integer ("Retinex", "Scal", retinex.scal); } + if (!pedited || pedited->retinex.gam) { + keyFile.set_double ("Retinex", "Gam", retinex.gam); + } + + if (!pedited || pedited->retinex.slope) { + keyFile.set_double ("Retinex", "Slope", retinex.slope); + } + if (!pedited || pedited->retinex.enabled) { keyFile.set_boolean ("Retinex", "Enabled", retinex.enabled); } @@ -3818,6 +3828,22 @@ int ProcParams::load (Glib::ustring fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("Retinex", "Gam")) { + retinex.gam = keyFile.get_double ("Retinex", "Gam"); + + if (pedited) { + pedited->retinex.gam = true; + } + } + + if (keyFile.has_key ("Retinex", "Slope")) { + retinex.slope = keyFile.get_double ("Retinex", "Slope"); + + if (pedited) { + pedited->retinex.slope = true; + } + } + if (keyFile.has_key ("Retinex", "Gain")) { retinex.gain = keyFile.get_integer ("Retinex", "Gain"); @@ -7240,6 +7266,8 @@ bool ProcParams::operator== (const ProcParams& other) && retinex.transmissionCurve == other.retinex.transmissionCurve && retinex.str == other.retinex.str && retinex.scal == other.retinex.scal + && retinex.gam == other.retinex.gam + && retinex.slope == other.retinex.slope && retinex.neigh == other.retinex.neigh && retinex.gain == other.retinex.gain && retinex.limd == other.retinex.limd diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 9bd5ecb29..4c631b6c8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -274,6 +274,8 @@ public: std::vector transmissionCurve; int str; int scal; + double gam; + double slope; int neigh; int gain; int offs; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index a9c4016c9..8897dca15 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1813,14 +1813,36 @@ void RawImageSource::retinexPrepareBuffers(ColorManagementParams cmp, RetinexPar conversionBuffer[2] (W - 2 * border, H - 2 * border); LUTf *retinexgamtab;//gamma before and after Retinex to restore tones + LUTf lutTonereti; + lutTonereti(65536); + if(retinexParams.gammaretinex == "low") retinexgamtab = &(Color::gammatab_115_2); else if(retinexParams.gammaretinex == "mid") retinexgamtab = &(Color::gammatab_13_2); else if(retinexParams.gammaretinex == "hig") retinexgamtab = &(Color::gammatab_145_3); + else if(retinexParams.gammaretinex == "fre"){ + double g_a0, g_a1, g_a2, g_a3, g_a4, g_a5; + double pwr = 1.0 / retinexParams.gam; + double gamm = retinexParams.gam; + double ts = retinexParams.slope; + int mode = 0, imax = 0; + Color::calcGamma(pwr, ts, mode, imax, g_a0, g_a1, g_a2, g_a3, g_a4, g_a5); // call to calcGamma with selected gamma and slope + // printf("g_a0=%f g_a1=%f g_a2=%f g_a3=%f g_a4=%f\n", g_a0,g_a1,g_a2,g_a3,g_a4); + for (int i = 0; i < 65536; i++) { + double val = (i) / 65535.; + double start = g_a3; + double add = g_a3; + double mul = 1. + g_a4; + double x; + x = Color::gammareti (val, gamm, start, ts, mul , add); + lutTonereti[i] = CLIP(x * 65535.);// CLIP avoid in some case extra values + } + retinexgamtab = &lutTonereti; + } -if(retinexParams.gammaretinex != "none") {//gamma +if(retinexParams.gammaretinex != "none" && retinexParams.str != 0) {//gamma #ifdef _OPENMP #pragma omp parallel for #endif @@ -1832,7 +1854,7 @@ if(retinexParams.gammaretinex != "none") {//gamma B_=blue[i][j]; red[i][j] = (*retinexgamtab)[R_]; green[i][j] = (*retinexgamtab)[G_]; - blue[i][j] = (*retinexgamtab)[B_]; + blue[i][j] = (*retinexgamtab)[B_]; } } } @@ -1946,10 +1968,6 @@ if(retinexParams.gammaretinex != "none") {//gamma G_ = green[i][j]; B_ = blue[i][j]; //rgb=>lab - // R_ = (*retinexigamtab)[red[i][j]]; - // G_ = (*retinexigamtab)[green[i][j]]; - // B_ = (*retinexigamtab)[blue[i][j]]; - Color::rgbxyz(R_, G_, B_, X, Y, Z, wp); //convert Lab Color::XYZ2Lab(X, Y, Z, L, aa, bb); @@ -2004,6 +2022,9 @@ void RawImageSource::retinex(ColorManagementParams cmp, RetinexParams deh, LUTf if (settings->verbose) { printf ("Applying Retinex\n"); } + LUTf lutToneireti; + lutToneireti(65536); + LUTf *retinexigamtab;//gamma before and after Retinex to restore tones if(deh.gammaretinex == "low") retinexigamtab = &(Color::igammatab_115_2); @@ -2011,7 +2032,25 @@ void RawImageSource::retinex(ColorManagementParams cmp, RetinexParams deh, LUTf retinexigamtab = &(Color::igammatab_13_2); else if(deh.gammaretinex == "hig") retinexigamtab = &(Color::igammatab_145_3); - + else if(deh.gammaretinex == "fre"){ + double g_a0, g_a1, g_a2, g_a3, g_a4, g_a5; + double pwr = 1.0 / deh.gam; + double gamm = deh.gam; + double ts = deh.slope; + int mode = 0, imax = 0; + Color::calcGamma(pwr, ts, mode, imax, g_a0, g_a1, g_a2, g_a3, g_a4, g_a5); // call to calcGamma with selected gamma and slope + // printf("g_a0=%f g_a1=%f g_a2=%f g_a3=%f g_a4=%f\n", g_a0,g_a1,g_a2,g_a3,g_a4); + for (int i = 0; i < 65536; i++) { + double val = (i) / 65535.; + double x; + double mul = 1. + g_a4; + double add = g_a4; + double start = g_a2; + x = Color::igammareti (val, gamm, start, ts, mul , add); + lutToneireti[i] = CLIP(x * 65535.); + } + retinexigamtab = &lutToneireti; + } // We need a buffer with original L data to allow correct blending // red, green and blue still have original size of raw, but we can't use the borders const int HNew = H - 2 * border; @@ -2164,11 +2203,10 @@ void RawImageSource::retinex(ColorManagementParams cmp, RetinexParams deh, LUTf red[i][j] = R; green[i][j] = G; blue[i][j] = B; - } } } -if(deh.gammaretinex != "none"){//inverse gamma +if(deh.gammaretinex != "none" && deh.str !=0){//inverse gamma #ifdef _OPENMP #pragma omp parallel for #endif @@ -2180,7 +2218,7 @@ if(deh.gammaretinex != "none"){//inverse gamma B_=blue[i][j]; red[i][j] = (*retinexigamtab)[R_]; green[i][j] = (*retinexigamtab)[G_]; - blue[i][j] = (*retinexigamtab)[B_]; + blue[i][j] = (*retinexigamtab)[B_]; } } } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 1ce5a4211..4ad96ebd2 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -447,6 +447,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { ALLNORAW, // EvLlimd DEMOSAIC, // Evretinexcolorspace ALLNORAW, // EvLCDHCurve - DEMOSAIC // Evretinexgamma + DEMOSAIC, // Evretinexgamma + DEMOSAIC, // EvLgam + DEMOSAIC // EvLslope }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index e3fa2de1e..dc142f6de 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -57,6 +57,8 @@ void ParamsEdited::set (bool v) retinex.enabled = v; retinex.str = v; retinex.scal = v; + retinex.gam = v; + retinex.slope = v; retinex.neigh = v; retinex.gain = v; retinex.offs = v; @@ -528,6 +530,8 @@ void ParamsEdited::initFrom (const std::vector retinex.gammaretinex = retinex.gammaretinex && p.retinex.gammaretinex == other.retinex.gammaretinex; retinex.str = retinex.str && p.retinex.str == other.retinex.str; retinex.scal = retinex.scal && p.retinex.scal == other.retinex.scal; + retinex.gam = retinex.gam && p.retinex.gam == other.retinex.gam; + retinex.slope = retinex.slope && p.retinex.slope == other.retinex.slope; retinex.neigh = retinex.neigh && p.retinex.neigh == other.retinex.neigh; retinex.gain = retinex.gain && p.retinex.gain == other.retinex.gain; retinex.offs = retinex.offs && p.retinex.offs == other.retinex.offs; @@ -1053,6 +1057,14 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten if (retinex.gammaretinex) { toEdit.retinex.gammaretinex = mods.retinex.gammaretinex; } + + if (retinex.gam) { + toEdit.retinex.gam = mods.retinex.gam; + } + + if (retinex.slope) { + toEdit.retinex.slope = mods.retinex.slope; + } if (retinex.str) { toEdit.retinex.str = dontforceSet && options.baBehav[ADDSET_DH_STR] ? toEdit.retinex.str + mods.retinex.str : mods.retinex.str; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 850c8589d..bfe7fbe02 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -61,6 +61,8 @@ public: bool enabled; bool str; bool scal; + bool gam; + bool slope; bool neigh; bool gain; bool offs; diff --git a/rtgui/retinex.cc b/rtgui/retinex.cc index 9c4b2e3e2..1a848a037 100644 --- a/rtgui/retinex.cc +++ b/rtgui/retinex.cc @@ -105,10 +105,13 @@ Retinex::Retinex () : FoldableToolPanel(this, "retinex", M("TP_RETINEX_LABEL"), gammaretinex->append_text (M("TP_RETINEX_GAMMA_LOW")); gammaretinex->append_text (M("TP_RETINEX_GAMMA_MID")); gammaretinex->append_text (M("TP_RETINEX_GAMMA_HIGH")); + gammaretinex->append_text (M("TP_RETINEX_GAMMA_FREE")); gammaretinex->set_active(0); gammaretinexConn = gammaretinex->signal_changed().connect ( sigc::mem_fun(*this, &Retinex::gammaretinexChanged) ); gammaretinex->set_tooltip_markup (M("TP_RETINEX_GAMMA_TOOLTIP")); + gam = Gtk::manage (new Adjuster (M("TP_RETINEX_GAMMA"), 1.1, 1.6, 0.01, 1.30)); + slope = Gtk::manage (new Adjuster (M("TP_RETINEX_SLOPE"), 2., 20., 0.1, 3.)); str = Gtk::manage (new Adjuster (M("TP_RETINEX_STRENGTH"), 0, 100., 1., 20.)); neigh = Gtk::manage (new Adjuster (M("TP_RETINEX_NEIGHBOR"), 6, 100., 1., 80.)); @@ -158,6 +161,13 @@ Retinex::Retinex () : FoldableToolPanel(this, "retinex", M("TP_RETINEX_LABEL"), settingsVBox->pack_start(*gambox); gammaretinex->show(); + + settingsVBox->pack_start (*gam); + gam->show (); + + settingsVBox->pack_start (*slope); + slope->show (); + settingsVBox->pack_start (*scal); scal->show (); @@ -193,6 +203,18 @@ Retinex::Retinex () : FoldableToolPanel(this, "retinex", M("TP_RETINEX_LABEL"), if (scal->delay < 200) { scal->delay = 200; } + + gam->setAdjusterListener (this); + + if (gam->delay < 500) { + gam->delay = 500; + } + + slope->setAdjusterListener (this); + + if (slope->delay < 500) { + slope->delay = 500; + } neigh->setAdjusterListener (this); @@ -350,6 +372,8 @@ void Retinex::read (const ProcParams* pp, const ParamsEdited* pedited) if (pedited) { scal->setEditedState (pedited->retinex.scal ? Edited : UnEdited); neigh->setEditedState (pedited->retinex.neigh ? Edited : UnEdited); + gam->setEditedState (pedited->retinex.gam ? Edited : UnEdited); + slope->setEditedState (pedited->retinex.slope ? Edited : UnEdited); gain->setEditedState (pedited->retinex.gain ? Edited : UnEdited); offs->setEditedState (pedited->retinex.offs ? Edited : UnEdited); vart->setEditedState (pedited->retinex.vart ? Edited : UnEdited); @@ -383,6 +407,8 @@ void Retinex::read (const ProcParams* pp, const ParamsEdited* pedited) scal->setValue (pp->retinex.scal); vart->setValue (pp->retinex.vart); limd->setValue (pp->retinex.limd); + gam->setValue (pp->retinex.gam); + slope->setValue (pp->retinex.slope); setEnabled (pp->retinex.enabled); @@ -416,6 +442,8 @@ void Retinex::read (const ProcParams* pp, const ParamsEdited* pedited) gammaretinex->set_active (2); } else if (pp->retinex.gammaretinex == "hig") { gammaretinex->set_active (3); + } else if (pp->retinex.gammaretinex == "fre") { + gammaretinex->set_active (4); } retinexMethodChanged (); @@ -445,6 +473,8 @@ void Retinex::write (ProcParams* pp, ParamsEdited* pedited) pp->retinex.str = str->getValue (); pp->retinex.scal = (int)scal->getValue (); + pp->retinex.gam = gam->getValue (); + pp->retinex.slope = slope->getValue (); pp->retinex.neigh = neigh->getValue (); pp->retinex.gain = (int)gain->getValue (); pp->retinex.offs = (int)offs->getValue (); @@ -464,6 +494,8 @@ void Retinex::write (ProcParams* pp, ParamsEdited* pedited) //%%%%%%%%%%%%%%%%%%%%%% pedited->retinex.str = str->getEditedState (); pedited->retinex.scal = scal->getEditedState (); + pedited->retinex.gam = gam->getEditedState (); + pedited->retinex.slope = slope->getEditedState (); pedited->retinex.neigh = neigh->getEditedState (); pedited->retinex.gain = gain->getEditedState (); pedited->retinex.offs = offs->getEditedState (); @@ -501,6 +533,8 @@ void Retinex::write (ProcParams* pp, ParamsEdited* pedited) pp->retinex.gammaretinex = "mid"; } else if (gammaretinex->get_active_row_number() == 3) { pp->retinex.gammaretinex = "hig"; + } else if (gammaretinex->get_active_row_number() == 4) { + pp->retinex.gammaretinex = "fre"; } } @@ -531,7 +565,6 @@ void Retinex::ColorSpaceUpdateUI () void Retinex::retinexColorSpaceChanged() { - ColorSpaceUpdateUI(); if (listener) { @@ -541,6 +574,15 @@ void Retinex::retinexColorSpaceChanged() void Retinex::gammaretinexChanged() { + if (!batchMode) { + if(gammaretinex->get_active_row_number() == 4) { + gam->show(); + slope->show(); + } else if(gammaretinex->get_active_row_number() != 4) { + gam->hide(); + slope->hide(); + } + } ColorSpaceUpdateUI(); @@ -589,6 +631,8 @@ void Retinex::setDefaults (const ProcParams* defParams, const ParamsEdited* pedi scal->setDefault (defParams->retinex.scal); vart->setDefault (defParams->retinex.vart); limd->setDefault (defParams->retinex.limd); + gam->setDefault (defParams->retinex.gam); + slope->setDefault (defParams->retinex.slope); if (pedited) { neigh->setDefaultEditedState (pedited->retinex.neigh ? Edited : UnEdited); @@ -598,6 +642,8 @@ void Retinex::setDefaults (const ProcParams* defParams, const ParamsEdited* pedi scal->setDefaultEditedState (pedited->retinex.scal ? Edited : UnEdited); vart->setDefaultEditedState (pedited->retinex.vart ? Edited : UnEdited); limd->setDefaultEditedState (pedited->retinex.limd ? Edited : UnEdited); + gam->setDefaultEditedState (pedited->retinex.gam ? Edited : UnEdited); + slope->setDefaultEditedState (pedited->retinex.slope ? Edited : UnEdited); } else { neigh->setDefaultEditedState (Irrelevant); @@ -607,6 +653,8 @@ void Retinex::setDefaults (const ProcParams* defParams, const ParamsEdited* pedi limd->setDefaultEditedState (Irrelevant); str->setDefaultEditedState (Irrelevant); scal->setDefaultEditedState (Irrelevant); + gam->setDefaultEditedState (Irrelevant); + slope->setDefaultEditedState (Irrelevant); } } @@ -644,6 +692,10 @@ void Retinex::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvLvart, vart->getTextValue()); } else if (a == limd) { listener->panelChanged (EvLlimd, limd->getTextValue()); + } else if (a == gam) { + listener->panelChanged (EvLgam, gam->getTextValue()); + } else if (a == slope) { + listener->panelChanged (EvLslope, slope->getTextValue()); } } @@ -696,6 +748,8 @@ void Retinex::trimValues (rtengine::procparams::ProcParams* pp) offs->trimValue(pp->retinex.offs); vart->trimValue(pp->retinex.vart); limd->trimValue(pp->retinex.limd); + gam->trimValue(pp->retinex.gam); + slope->trimValue(pp->retinex.slope); } void Retinex::updateCurveBackgroundHistogram (LUTu & histToneCurve, LUTu & histLCurve, LUTu & histCCurve,/* LUTu & histCLurve, LUTu & histLLCurve,*/ LUTu & histLCAM, LUTu & histCCAM, LUTu & histRed, LUTu & histGreen, LUTu & histBlue, LUTu & histLuma, LUTu & histLRETI) @@ -713,6 +767,8 @@ void Retinex::setBatchMode (bool batchMode) offs->showEditedCB (); str->showEditedCB (); scal->showEditedCB (); + gam->showEditedCB (); + slope->showEditedCB (); vart->showEditedCB (); limd->showEditedCB (); curveEditorGD->setBatchMode (batchMode); diff --git a/rtgui/retinex.h b/rtgui/retinex.h index 4b01865db..a563fa271 100644 --- a/rtgui/retinex.h +++ b/rtgui/retinex.h @@ -28,6 +28,8 @@ protected: Adjuster* offs; Adjuster* vart; Adjuster* limd; + Adjuster* gam; + Adjuster* slope; MyExpander* expsettings; Gtk::Label* labmdh;