From 08842039666b9b46ec16c4ef2b0aeb1e2ab65a2f Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 21 Jun 2020 09:49:53 +0200 Subject: [PATCH 001/114] Added tool mask --- rtengine/procevents.h | 2 +- rtengine/procparams.cc | 22 ++++++- rtengine/procparams.h | 3 + rtengine/refreshmap.cc | 4 +- rtgui/locallab.cc | 2 + rtgui/locallab.h | 1 + rtgui/locallabtools.h | 49 ++++++++++++++++ rtgui/locallabtools2.cc | 125 ++++++++++++++++++++++++++++++++++++++++ rtgui/paramsedited.cc | 24 +++++++- rtgui/paramsedited.h | 3 + 10 files changed, 229 insertions(+), 6 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index e3243938f..160441c4d 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -950,7 +950,7 @@ enum ProcEventCode { EvLocallabSpotcolorscope = 924, EvlocallabshowmasktypMethod = 925, Evlocallabshadmaskblsha = 926, - + EvLocenamask = 927, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index f72cbcb62..0aedc8d42 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3732,7 +3732,11 @@ LocallabParams::LocallabSpot::LocallabSpot() : sensilog(60), baselog(2.), strlog(0.0), - anglog(0.0) + anglog(0.0), + // mask + visimask(false), + expmask(false) + { } @@ -4202,7 +4206,11 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && sensilog == other.sensilog && baselog == other.baselog && strlog == other.strlog - && anglog == other.anglog; + && anglog == other.anglog + // mask + && visimask == other.visimask + && expmask == other.expmask; + } bool LocallabParams::LocallabSpot::operator !=(const LocallabSpot& other) const @@ -5696,6 +5704,10 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->strlog, "Locallab", "Strlog_" + index_str, spot.strlog, keyFile); saveToKeyfile(!pedited || spot_edited->anglog, "Locallab", "Anglog_" + index_str, spot.anglog, keyFile); } + //mask + if ((!pedited || spot_edited->visimask) && spot.visimask) { + saveToKeyfile(!pedited || spot_edited->expmask, "Locallab", "Expmask_" + index_str, spot.expmask, keyFile); + } } } @@ -7409,6 +7421,12 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Baselog_" + index_str, pedited, spot.baselog, spotEdited.baselog); assignFromKeyfile(keyFile, "Locallab", "Strlog_" + index_str, pedited, spot.strlog, spotEdited.strlog); assignFromKeyfile(keyFile, "Locallab", "Anglog_" + index_str, pedited, spot.anglog, spotEdited.anglog); + // mask + spot.visimask = assignFromKeyfile(keyFile, "Locallab", "Expmask_" + index_str, pedited, spot.expmask, spotEdited.expmask); + + if (spot.visimask) { + spotEdited.visimask = true; + } // Append LocallabSpot and LocallabParamsEdited locallab.spots.push_back(spot); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index c304c02ff..8fafa3a46 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1421,6 +1421,9 @@ struct LocallabParams { double baselog; double strlog; double anglog; + // mask + bool visimask; + bool expmask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 773ec8b68..6ebcd74f1 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -953,8 +953,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { M_VOID, // EvlocallabcomplexityWithoutRefresh LUMINANCECURVE, // EvLocallabSpotcolorscope LUMINANCECURVE, //EvlocallabshowmasktypMethod - LUMINANCECURVE // Evlocallabshadmaskblsha - + LUMINANCECURVE, // Evlocallabshadmaskblsha + LUMINANCECURVE //EvLocenamask }; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index a4351c710..004031c72 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -162,6 +162,7 @@ Locallab::Locallab(): expcontrast(Gtk::manage(new LocallabContrast())), expcbdl(Gtk::manage(new LocallabCBDL())), explog(Gtk::manage(new LocallabLog())), + expmask(Gtk::manage(new LocallabMask())), // Other widgets resetshowButton(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_RESETSHOW")))) @@ -198,6 +199,7 @@ Locallab::Locallab(): addTool(toolpanel, expcontrast); addTool(toolpanel, expcbdl); addTool(toolpanel, explog); + addTool(toolpanel, expmask); panel->pack_start(*toolpanel, false, false); // Add separator diff --git a/rtgui/locallab.h b/rtgui/locallab.h index 8d68576f4..75523a5d3 100644 --- a/rtgui/locallab.h +++ b/rtgui/locallab.h @@ -115,6 +115,7 @@ private: LocallabContrast* const expcontrast; LocallabCBDL* const expcbdl; LocallabLog* const explog; + LocallabMask* const expmask; std::vector locallabTools; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 4e6b30f00..5da061b89 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1196,4 +1196,53 @@ private: void updateLogGUI(); }; + +/* ==== LocallabMask ==== */ +class LocallabMask: + public Gtk::VBox, + public LocallabTool +{ +private: +/* + Gtk::ToggleButton* const autocompute; + Gtk::Frame* const logPFrame; + Adjuster* const blackEv; + Adjuster* const whiteEv; + Gtk::CheckButton* const fullimage; + Gtk::CheckButton* const Autogray; + Adjuster* const sourceGray; + Adjuster* const targetGray; + Adjuster* const detail; + Adjuster* const baselog; + Adjuster* const sensilog; + Adjuster* const strlog; + Adjuster* const anglog; + + sigc::connection autoconn, fullimageConn, AutograyConn; +*/ +public: + LocallabMask(); + void updateAdviceTooltips(const bool showTooltips) override; + + void disableListener() override; + void enableListener() override; + 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 updateAutocompute(const float blackev, const float whiteev, const float sourceg, const float targetg); + +private: + void enabledChanged() override; + /* + void autocomputeToggled(); + void fullimageChanged(); + void AutograyChanged(); + + void updateLogGUI(); + */ +}; + + #endif diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 816bfcd30..930145bc8 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4723,3 +4723,128 @@ void LocallabLog::updateLogGUI() // targetGray->set_sensitive(true); } } + + +/* ==== LocallabMask ==== */ +LocallabMask::LocallabMask(): + LocallabTool(this, M("TP_LOCALLAB_MASK_TOOLNAME"), M("TP_LOCALLAB_MASK"), false, false) + +{ + // Parameter Mask encoding specific widgets +} + +void LocallabMask::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_MASK_TOOLTIP")); + } else { + exp->set_tooltip_text(M("")); + } +} + +void LocallabMask::disableListener() +{ + LocallabTool::disableListener(); + +} + +void LocallabMask::enableListener() +{ + LocallabTool::enableListener(); + +} + +void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visimask); + exp->setEnabled(spot.expmask); + + } + + // Enable all listeners + enableListener(); + + // Update Log Encoding GUI according to autocompute button state +// updateLogGUI(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expmask = exp->getEnabled(); + spot.visimask = exp->get_visible(); + + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default value for adjuster widgets + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabMask::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + } +} + + +void LocallabMask::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenamask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenamask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + + +/* +void LocallabLog::updateLogGUI() +{ + if (autocompute->get_active()) { + blackEv->set_sensitive(false); + whiteEv->set_sensitive(false); + sourceGray->set_sensitive(false); + // targetGray->set_sensitive(true); + } else { + blackEv->set_sensitive(true); + whiteEv->set_sensitive(true); + sourceGray->set_sensitive(true); + // targetGray->set_sensitive(true); + } +} +*/ \ No newline at end of file diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index bbfb7cc4e..086d6f7a6 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1477,6 +1477,10 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).baselog = locallab.spots.at(j).baselog && pSpot.baselog == otherSpot.baselog; locallab.spots.at(j).strlog = locallab.spots.at(j).strlog && pSpot.strlog == otherSpot.strlog; locallab.spots.at(j).anglog = locallab.spots.at(j).anglog && pSpot.anglog == otherSpot.anglog; + //mask + locallab.spots.at(j).visimask = locallab.spots.at(j).visimask && pSpot.visimask == otherSpot.visimask; + locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; + } } @@ -4781,6 +4785,16 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (locallab.spots.at(i).anglog) { toEdit.locallab.spots.at(i).anglog = mods.locallab.spots.at(i).anglog; } + + // mask + if (locallab.spots.at(i).visimask) { + toEdit.locallab.spots.at(i).visimask = mods.locallab.spots.at(i).visimask; + } + + if (locallab.spots.at(i).explog) { + toEdit.locallab.spots.at(i).expmask = mods.locallab.spots.at(i).expmask; + } + } if (pcvignette.enabled) { @@ -6349,7 +6363,11 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : sensilog(v), baselog(v), strlog(v), - anglog(v) + anglog(v), + // mask + visimask(v), + expmask(v) + { } @@ -6810,6 +6828,10 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) baselog = v; strlog = v; anglog = v; + // mask + visimask = v; + expmask = v; + } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 1bd2e045e..be0c0e347 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -835,6 +835,9 @@ public: bool baselog; bool strlog; bool anglog; + //mask + bool visimask; + bool expmask; LocallabSpotEdited(bool v); From 5ac22b65645cf6c1bcaa971c76bbcbd1b1fbf24f Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 21 Jun 2020 12:33:15 +0200 Subject: [PATCH 002/114] Added mask common scope and blend --- rtdata/languages/default | 8 ++++++- rtengine/procevents.h | 2 ++ rtengine/procparams.cc | 14 ++++++++++--- rtengine/procparams.h | 2 ++ rtengine/refreshmap.cc | 8 ++++--- rtgui/locallabtools.h | 3 +++ rtgui/locallabtools2.cc | 45 +++++++++++++++++++++++++++++++++++++--- rtgui/paramsedited.cc | 22 +++++++++++++++----- rtgui/paramsedited.h | 2 ++ 9 files changed, 91 insertions(+), 15 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index cfe9b1372..c99155cd6 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1165,6 +1165,9 @@ HISTORY_MSG_924;Local - Tool complexity mode HISTORY_MSG_925;Local - Scope color tools HISTORY_MSG_926;Local - Show mask type HISTORY_MSG_927;Local - Shadow mask +HISTORY_MSG_928;Local - Common color mask +HISTORY_MSG_929;Local - Mask common scope +HISTORY_MSG_930;Local - Mask Common blend HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction @@ -2554,7 +2557,9 @@ TP_LOCALLAB_LUMADARKEST;Darkest TP_LOCALLAB_LUMASK;Luminance Background Mask TP_LOCALLAB_LUMASK_TOOLTIP;Adjust the gray of the mask background in Show Mask (Mask and modifications) TP_LOCALLAB_LUMAWHITESEST;Whiteest -TP_LOCALLAB_LUMONLY;Luminance only +TP_LOCALLAB_LUMONLY;Luminance only +TP_LOCALLAB_MASKCOM;Common Color Mask +TP_LOCALLAB_MASKCOM_TOOLTIP;These masks works as all tools, they take into account scope color.\nThey are different from others masks which complete a tool (Color and Light, Exposure...) TP_LOCALLAB_MASFRAME;Mask and Merge TP_LOCALLAB_MASFRAME_TOOLTIP;For all masks.\nTake into account deltaE image to avoid retouching the selection area when sliders gamma mask, slope mask, chroma mask and curves contrast , levels contrasts, and mask blur, structure(if enabled tool) are used.\nDisabled in Inverse TP_LOCALLAB_MASK2;Contrast curve mask @@ -2819,6 +2824,7 @@ TP_LOCALLAB_SHARP_TOOLNAME;Sharpening - 8 TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2 TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0 +TP_LOCALLAB_MASKCOM_TOOLNAME;Common Color Mask - 13 TP_LOCALLAB_WAMASKCOL;Ψ Mask Wavelet level TP_LOCALLAB_WARM;Warm - Cool & Color artifacts TP_LOCALLAB_WARM_TOOLTIP;This slider use Ciecam algorithm and acts as White Balance, it can warm or cool the area selected.\nIt can also in some cases reduce color artifacts. diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 160441c4d..d1115b593 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -951,6 +951,8 @@ enum ProcEventCode { EvlocallabshowmasktypMethod = 925, Evlocallabshadmaskblsha = 926, EvLocenamask = 927, + Evlocallabsensimask = 928, + Evlocallabblendmask = 929, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 0aedc8d42..992281437 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3735,7 +3735,9 @@ LocallabParams::LocallabSpot::LocallabSpot() : anglog(0.0), // mask visimask(false), - expmask(false) + expmask(false), + sensimask(60), + blendmask(0) { } @@ -4209,8 +4211,10 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && anglog == other.anglog // mask && visimask == other.visimask - && expmask == other.expmask; - + && expmask == other.expmask + && sensimask == other.sensimask + && blendmask == other.blendmask; + } bool LocallabParams::LocallabSpot::operator !=(const LocallabSpot& other) const @@ -5707,6 +5711,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo //mask if ((!pedited || spot_edited->visimask) && spot.visimask) { saveToKeyfile(!pedited || spot_edited->expmask, "Locallab", "Expmask_" + index_str, spot.expmask, keyFile); + saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); } } } @@ -7423,6 +7429,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Anglog_" + index_str, pedited, spot.anglog, spotEdited.anglog); // mask spot.visimask = assignFromKeyfile(keyFile, "Locallab", "Expmask_" + index_str, pedited, spot.expmask, spotEdited.expmask); + assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 8fafa3a46..3c00f3b16 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1424,6 +1424,8 @@ struct LocallabParams { // mask bool visimask; bool expmask; + int sensimask; + int blendmask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 6ebcd74f1..cf9424297 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -951,10 +951,12 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabblwh LUMINANCECURVE, // EvlocallabcomplexityWithRefresh M_VOID, // EvlocallabcomplexityWithoutRefresh - LUMINANCECURVE, // EvLocallabSpotcolorscope - LUMINANCECURVE, //EvlocallabshowmasktypMethod + LUMINANCECURVE, // EvLocallabSpotcolorscope + LUMINANCECURVE, //EvlocallabshowmasktypMethod LUMINANCECURVE, // Evlocallabshadmaskblsha - LUMINANCECURVE //EvLocenamask + LUMINANCECURVE, // EvLocenamask + LUMINANCECURVE, //Evlocallabsensimask + LUMINANCECURVE //Evlocallabblendmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 5da061b89..245b0d65a 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1203,6 +1203,9 @@ class LocallabMask: public LocallabTool { private: + Adjuster* const sensimask; + Adjuster* const blendmask; + /* Gtk::ToggleButton* const autocompute; Gtk::Frame* const logPFrame; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 930145bc8..142955791 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4727,18 +4727,31 @@ void LocallabLog::updateLogGUI() /* ==== LocallabMask ==== */ LocallabMask::LocallabMask(): - LocallabTool(this, M("TP_LOCALLAB_MASK_TOOLNAME"), M("TP_LOCALLAB_MASK"), false, false) + LocallabTool(this, M("TP_LOCALLAB_MASKCOM_TOOLNAME"), M("TP_LOCALLAB_MASKCOM"), false, false), + sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), + blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))) { - // Parameter Mask encoding specific widgets + // Parameter Mask common specific widgets + sensimask->setAdjusterListener(this); + blendmask->setAdjusterListener(this); + + pack_start(*sensimask, Gtk::PACK_SHRINK, 0); + pack_start(*blendmask, Gtk::PACK_SHRINK, 0); + } void LocallabMask::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { - exp->set_tooltip_text(M("TP_LOCALLAB_MASK_TOOLTIP")); + exp->set_tooltip_text(M("TP_LOCALLAB_MASKCOM_TOOLTIP")); + sensimask->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); + blendmask->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + } else { exp->set_tooltip_text(M("")); + sensimask->set_tooltip_text(M("")); + blendmask->set_tooltip_text(M("")); } } @@ -4769,6 +4782,10 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params exp->set_visible(spot.visimask); exp->setEnabled(spot.expmask); + + + sensimask->setValue(spot.sensimask); + blendmask->setValue(spot.blendmask); } @@ -4790,6 +4807,9 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.expmask = exp->getEnabled(); spot.visimask = exp->get_visible(); + + spot.sensimask = sensimask->getIntValue(); + spot.blendmask = blendmask->getIntValue(); } @@ -4804,6 +4824,9 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); // Set default value for adjuster widgets + sensimask->setDefault((double)defSpot.sensimask); + blendmask->setDefault((double)defSpot.blendmask); + } // Note: No need to manage pedited as batch mode is deactivated for Locallab @@ -4812,6 +4835,22 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams void LocallabMask::adjusterChanged(Adjuster* a, double newval) { if (isLocActivated && exp->getEnabled()) { + + if (a == sensimask) { + if (listener) { + listener->panelChanged(Evlocallabsensimask, + sensimask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + + if (a == blendmask) { + if (listener) { + listener->panelChanged(Evlocallabblendmask, + blendmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 086d6f7a6..cd83a784e 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1480,7 +1480,8 @@ void ParamsEdited::initFrom(const std::vector& //mask locallab.spots.at(j).visimask = locallab.spots.at(j).visimask && pSpot.visimask == otherSpot.visimask; locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; - + locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; + locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; } } @@ -4791,10 +4792,18 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).visimask = mods.locallab.spots.at(i).visimask; } - if (locallab.spots.at(i).explog) { + if (locallab.spots.at(i).expmask) { toEdit.locallab.spots.at(i).expmask = mods.locallab.spots.at(i).expmask; } - + + if (locallab.spots.at(i).sensimask) { + toEdit.locallab.spots.at(i).sensimask = mods.locallab.spots.at(i).sensimask; + } + + if (locallab.spots.at(i).blendmask) { + toEdit.locallab.spots.at(i).blendmask = mods.locallab.spots.at(i).blendmask; + } + } if (pcvignette.enabled) { @@ -6366,7 +6375,9 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : anglog(v), // mask visimask(v), - expmask(v) + expmask(v), + sensimask(v), + blendmask(v) { } @@ -6831,7 +6842,8 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) // mask visimask = v; expmask = v; - + sensimask = v; + blendmask = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index be0c0e347..962a0d7cb 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -838,6 +838,8 @@ public: //mask bool visimask; bool expmask; + bool sensimask; + bool blendmask; LocallabSpotEdited(bool v); From 20b9928b74eee83a7be6f1de5cdc71820e2ee540 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 21 Jun 2020 14:10:05 +0200 Subject: [PATCH 003/114] Added showmethod common mask --- rtengine/procevents.h | 1 + rtengine/refreshmap.cc | 7 ++--- rtgui/locallab.cc | 8 +++--- rtgui/locallab.h | 1 + rtgui/locallabtools.cc | 12 ++++----- rtgui/locallabtools.h | 49 +++++++++++++++-------------------- rtgui/locallabtools2.cc | 57 ++++++++++++++++++++++++++++++++++++----- 7 files changed, 88 insertions(+), 47 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index d1115b593..03ae99862 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -953,6 +953,7 @@ enum ProcEventCode { EvLocenamask = 927, Evlocallabsensimask = 928, Evlocallabblendmask = 929, + EvlocallabshowmaskmaskMethod = 930, NUMOFEVENTS }; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index cf9424297..c64b09de8 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -952,11 +952,12 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabcomplexityWithRefresh M_VOID, // EvlocallabcomplexityWithoutRefresh LUMINANCECURVE, // EvLocallabSpotcolorscope - LUMINANCECURVE, //EvlocallabshowmasktypMethod + LUMINANCECURVE, // EvlocallabshowmasktypMethod LUMINANCECURVE, // Evlocallabshadmaskblsha LUMINANCECURVE, // EvLocenamask - LUMINANCECURVE, //Evlocallabsensimask - LUMINANCECURVE //Evlocallabblendmask + LUMINANCECURVE, // Evlocallabsensimask + LUMINANCECURVE, // Evlocallabblendmask + LUMINANCECURVE // EvlocallabshowmaskmaskMethod }; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 004031c72..1760928b5 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -1027,20 +1027,20 @@ Locallab::llMaskVisibility Locallab::getMaskVisibility() const const bool prevDeltaE = expsettings->isDeltaEPrevActive(); // Get mask preview from Locallab tools - int colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask; + int colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask, maskMask; for (auto tool : locallabTools) { - tool->getMaskView(colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask); + tool->getMaskView(colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask, maskMask); } // Indicate to spot control panel if one mask preview is active const bool isMaskActive = (colorMask == 0) || (colorMaskinv == 0) || (expMask == 0) || (expMaskinv == 0) || (shMask == 0) || (shMaskinv == 0) || (vibMask == 0) || (softMask == 0) || (blMask == 0) || (tmMask == 0) || (retiMask == 0) || (sharMask == 0) || - (lcMask == 0) || (cbMask == 0); + (lcMask == 0) || (cbMask == 0) || (maskMask == 0); expsettings->setMaskPrevActive(isMaskActive); - return {prevDeltaE, colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask}; + return {prevDeltaE, colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask, maskMask}; } void Locallab::resetshowPressed() diff --git a/rtgui/locallab.h b/rtgui/locallab.h index 75523a5d3..44e408d49 100644 --- a/rtgui/locallab.h +++ b/rtgui/locallab.h @@ -163,6 +163,7 @@ public: int sharMask; int lcMask; int cbMask; + int maskMask; }; void resetMaskVisibility(); diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 78dfacbbe..13415cbe5 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -866,7 +866,7 @@ void LocallabColor::resetMaskView() showmaskcolMethodConninv.block(false); } -void LocallabColor::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabColor::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { colorMask = showmaskcolMethod->get_active_row_number(); colorMaskinv = showmaskcolMethodinv->get_active_row_number(); @@ -2456,7 +2456,7 @@ void LocallabExposure::resetMaskView() showmaskexpMethodConninv.block(false); } -void LocallabExposure::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabExposure::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { expMask = showmaskexpMethod->get_active_row_number(); expMaskinv = showmaskexpMethodinv->get_active_row_number(); @@ -3491,7 +3491,7 @@ void LocallabShadow::resetMaskView() showmaskSHMethodConninv.block(false); } -void LocallabShadow::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabShadow::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { shMask = showmaskSHMethod->get_active_row_number(); shMaskinv = showmaskSHMethodinv->get_active_row_number(); @@ -4312,7 +4312,7 @@ void LocallabVibrance::resetMaskView() showmaskvibMethodConn.block(false); } -void LocallabVibrance::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabVibrance::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { vibMask = showmaskvibMethod->get_active_row_number(); } @@ -4938,7 +4938,7 @@ void LocallabSoft::resetMaskView() showmasksoftMethodConn.block(false); } -void LocallabSoft::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabSoft::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { softMask = showmasksoftMethod->get_active_row_number(); } @@ -5499,7 +5499,7 @@ void LocallabBlur::resetMaskView() showmaskblMethodConn.block(false); } -void LocallabBlur::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabBlur::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { blMask = showmaskblMethod->get_active_row_number(); } diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 245b0d65a..0692b14da 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -123,7 +123,7 @@ public: return false; }; virtual void resetMaskView() {}; - virtual void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) {}; + virtual void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) {}; // Advice tooltips management function virtual void updateAdviceTooltips(const bool showTooltips) {}; @@ -259,7 +259,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -369,7 +369,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -454,7 +454,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -532,7 +532,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -588,7 +588,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -684,7 +684,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -765,7 +765,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -855,7 +855,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -913,7 +913,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -1033,7 +1033,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -1123,7 +1123,7 @@ public: bool isMaskViewActive() override; void resetMaskView() override; - void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; void updateAdviceTooltips(const bool showTooltips) override; @@ -1205,26 +1205,17 @@ class LocallabMask: private: Adjuster* const sensimask; Adjuster* const blendmask; + MyComboBoxText* const showmaskMethod; -/* - Gtk::ToggleButton* const autocompute; - Gtk::Frame* const logPFrame; - Adjuster* const blackEv; - Adjuster* const whiteEv; - Gtk::CheckButton* const fullimage; - Gtk::CheckButton* const Autogray; - Adjuster* const sourceGray; - Adjuster* const targetGray; - Adjuster* const detail; - Adjuster* const baselog; - Adjuster* const sensilog; - Adjuster* const strlog; - Adjuster* const anglog; + sigc::connection showmaskMethodConn; - sigc::connection autoconn, fullimageConn, AutograyConn; -*/ public: LocallabMask(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) override; + void updateAdviceTooltips(const bool showTooltips) override; void disableListener() override; @@ -1238,6 +1229,8 @@ public: private: void enabledChanged() override; + void showmaskMethodChanged(); + /* void autocomputeToggled(); void fullimageChanged(); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 142955791..3e8e6464c 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -266,7 +266,7 @@ void LocallabTone::resetMaskView() showmasktmMethodConn.block(false); } -void LocallabTone::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabTone::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { tmMask = showmasktmMethod->get_active_row_number(); } @@ -1011,7 +1011,7 @@ void LocallabRetinex::resetMaskView() showmaskretiMethodConn.block(false); } -void LocallabRetinex::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabRetinex::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { retiMask = showmaskretiMethod->get_active_row_number(); } @@ -1820,7 +1820,7 @@ void LocallabSharp::resetMaskView() showmasksharMethodConn.block(false); } -void LocallabSharp::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabSharp::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { sharMask = showmasksharMethod->get_active_row_number(); } @@ -2563,7 +2563,7 @@ void LocallabContrast::resetMaskView() showmasklcMethodConn.block(false); } -void LocallabContrast::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabContrast::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { lcMask = showmasklcMethod->get_active_row_number(); } @@ -3892,7 +3892,7 @@ void LocallabCBDL::resetMaskView() showmaskcbMethodConn.block(false); } -void LocallabCBDL::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +void LocallabCBDL::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { cbMask = showmaskcbMethod->get_active_row_number(); } @@ -4729,18 +4729,47 @@ void LocallabLog::updateLogGUI() LocallabMask::LocallabMask(): LocallabTool(this, M("TP_LOCALLAB_MASKCOM_TOOLNAME"), M("TP_LOCALLAB_MASKCOM"), false, false), sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), - blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))) + blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + showmaskMethod(Gtk::manage(new MyComboBoxText())) { // Parameter Mask common specific widgets sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); + showmaskMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskMethod->set_active(0); + showmaskMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskMethodConn = showmaskMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmaskMethodChanged)); pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); + pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); } +bool LocallabMask::isMaskViewActive() +{ + return ((showmaskMethod->get_active_row_number() != 0)); +} + + +void LocallabMask::resetMaskView() +{ + showmaskMethodConn.block(true); + + showmaskMethod->set_active(0); + + showmaskMethodConn.block(false); +} + +void LocallabMask::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) +{ + colorMask = showmaskMethod->get_active_row_number(); +} + void LocallabMask::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { @@ -4758,15 +4787,31 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) void LocallabMask::disableListener() { LocallabTool::disableListener(); + showmaskMethodConn.block(true); } void LocallabMask::enableListener() { LocallabTool::enableListener(); + showmaskMethodConn.block(false); } +void LocallabMask::showmaskMethodChanged() +{ + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskmaskMethod, ""); + } +} + + void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { // Disable all listeners From 4739428abc2bed3a20a14cb53c0d3c687d66066b Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 21 Jun 2020 17:21:43 +0200 Subject: [PATCH 004/114] Added mask common checkbutton enable mask --- rtdata/languages/default | 1 + rtengine/procevents.h | 1 + rtengine/procparams.cc | 8 ++++++-- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/locallabtools.h | 4 +++- rtgui/locallabtools2.cc | 26 +++++++++++++++++++++++++- rtgui/paramsedited.cc | 10 ++++++++-- rtgui/paramsedited.h | 1 + 9 files changed, 48 insertions(+), 7 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index c99155cd6..090edf72a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1168,6 +1168,7 @@ HISTORY_MSG_927;Local - Shadow mask HISTORY_MSG_928;Local - Common color mask HISTORY_MSG_929;Local - Mask common scope HISTORY_MSG_930;Local - Mask Common blend +HISTORY_MSG_932;Local - Mask Common enable HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 03ae99862..998215c86 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -954,6 +954,7 @@ enum ProcEventCode { Evlocallabsensimask = 928, Evlocallabblendmask = 929, EvlocallabshowmaskmaskMethod = 930, + EvLocallabEnaMask = 931, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 992281437..524fc600f 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3737,7 +3737,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : visimask(false), expmask(false), sensimask(60), - blendmask(0) + blendmask(0), + enamask(false) { } @@ -4213,7 +4214,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && visimask == other.visimask && expmask == other.expmask && sensimask == other.sensimask - && blendmask == other.blendmask; + && blendmask == other.blendmask + && enamask == other.enamask; } @@ -5713,6 +5715,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->expmask, "Locallab", "Expmask_" + index_str, spot.expmask, keyFile); saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); + saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); } } } @@ -7431,6 +7434,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) spot.visimask = assignFromKeyfile(keyFile, "Locallab", "Expmask_" + index_str, pedited, spot.expmask, spotEdited.expmask); assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); + assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 3c00f3b16..b20323c52 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1426,6 +1426,7 @@ struct LocallabParams { bool expmask; int sensimask; int blendmask; + bool enamask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index c64b09de8..586532ce5 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -957,7 +957,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvLocenamask LUMINANCECURVE, // Evlocallabsensimask LUMINANCECURVE, // Evlocallabblendmask - LUMINANCECURVE // EvlocallabshowmaskmaskMethod + LUMINANCECURVE, // EvlocallabshowmaskmaskMethod + LUMINANCECURVE // EvLocallabEnaMask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 0692b14da..b094a81e3 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1206,8 +1206,9 @@ private: Adjuster* const sensimask; Adjuster* const blendmask; MyComboBoxText* const showmaskMethod; + Gtk::CheckButton* const enamask; - sigc::connection showmaskMethodConn; + sigc::connection showmaskMethodConn, enamaskConn; public: LocallabMask(); @@ -1230,6 +1231,7 @@ public: private: void enabledChanged() override; void showmaskMethodChanged(); + void enamaskChanged(); /* void autocomputeToggled(); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 3e8e6464c..923cfbb27 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4730,7 +4730,8 @@ LocallabMask::LocallabMask(): LocallabTool(this, M("TP_LOCALLAB_MASKCOM_TOOLNAME"), M("TP_LOCALLAB_MASKCOM"), false, false), sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - showmaskMethod(Gtk::manage(new MyComboBoxText())) + showmaskMethod(Gtk::manage(new MyComboBoxText())), + enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))) { // Parameter Mask common specific widgets @@ -4743,10 +4744,12 @@ LocallabMask::LocallabMask(): showmaskMethod->set_active(0); showmaskMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); showmaskMethodConn = showmaskMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmaskMethodChanged)); + enamaskConn = enamask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::enamaskChanged)); pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); + pack_start(*enamask, Gtk::PACK_SHRINK, 0); } @@ -4788,6 +4791,7 @@ void LocallabMask::disableListener() { LocallabTool::disableListener(); showmaskMethodConn.block(true); + enamaskConn.block(true); } @@ -4795,6 +4799,7 @@ void LocallabMask::enableListener() { LocallabTool::enableListener(); showmaskMethodConn.block(false); + enamaskConn.block(false); } @@ -4831,6 +4836,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params sensimask->setValue(spot.sensimask); blendmask->setValue(spot.blendmask); + enamask->set_active(spot.enamask); } @@ -4855,12 +4861,30 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); + spot.enamask = enamask->get_active(); } // Note: No need to manage pedited as batch mode is deactivated for Locallab } +void LocallabMask::enamaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enamask->get_active()) { + listener->panelChanged(EvLocallabEnaMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + + + void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) { const int index = defParams->locallab.selspot; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index cd83a784e..427ada9ad 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1482,6 +1482,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; + locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; } } @@ -4804,6 +4805,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).blendmask = mods.locallab.spots.at(i).blendmask; } + if (locallab.spots.at(i).enamask) { + toEdit.locallab.spots.at(i).enamask = mods.locallab.spots.at(i).enamask; + } + } if (pcvignette.enabled) { @@ -6377,8 +6382,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : visimask(v), expmask(v), sensimask(v), - blendmask(v) - + blendmask(v), + enamask(v) { } @@ -6844,6 +6849,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) expmask = v; sensimask = v; blendmask = v; + enamask = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 962a0d7cb..a559408e2 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -840,6 +840,7 @@ public: bool expmask; bool sensimask; bool blendmask; + bool enamask; LocallabSpotEdited(bool v); From d522d4a70109d8a1f6b3eec7cc894fa3f17ab4d8 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 21 Jun 2020 17:47:47 +0200 Subject: [PATCH 005/114] Added laplacian threshold and radius to mask common --- rtdata/languages/default | 2 ++ rtengine/procevents.h | 2 ++ rtengine/procparams.cc | 14 +++++++++++--- rtengine/procparams.h | 2 ++ rtengine/refreshmap.cc | 4 +++- rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 28 +++++++++++++++++++++++++++- rtgui/paramsedited.cc | 17 ++++++++++++++++- rtgui/paramsedited.h | 2 ++ 9 files changed, 67 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 090edf72a..ae246e5ed 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1169,6 +1169,8 @@ HISTORY_MSG_928;Local - Common color mask HISTORY_MSG_929;Local - Mask common scope HISTORY_MSG_930;Local - Mask Common blend HISTORY_MSG_932;Local - Mask Common enable +HISTORY_MSG_933;Local - Mask Common radius soft +HISTORY_MSG_934;Local - Mask Common laplacian HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 998215c86..b155cb9be 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -955,6 +955,8 @@ enum ProcEventCode { Evlocallabblendmask = 929, EvlocallabshowmaskmaskMethod = 930, EvLocallabEnaMask = 931, + Evlocallabradmask = 932, + Evlocallablapmask = 933, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 524fc600f..358e5fd94 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3738,8 +3738,10 @@ LocallabParams::LocallabSpot::LocallabSpot() : expmask(false), sensimask(60), blendmask(0), - enamask(false) - + enamask(false), + radmask(0.0), + lapmask(0.0) + { } @@ -4215,7 +4217,9 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && expmask == other.expmask && sensimask == other.sensimask && blendmask == other.blendmask - && enamask == other.enamask; + && enamask == other.enamask + && radmask == other.radmask + && lapmask == other.lapmask; } @@ -5716,6 +5720,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); + saveToKeyfile(!pedited || spot_edited->radmask, "Locallab", "Radmask_" + index_str, spot.radmask, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmask, "Locallab", "Lapmask_" + index_str, spot.lapmask, keyFile); } } } @@ -7435,6 +7441,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); + assignFromKeyfile(keyFile, "Locallab", "Radmask_" + index_str, pedited, spot.radmask, spotEdited.radmask); + assignFromKeyfile(keyFile, "Locallab", "Lapmask_" + index_str, pedited, spot.lapmask, spotEdited.lapmask); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b20323c52..b2b23e2e7 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1427,6 +1427,8 @@ struct LocallabParams { int sensimask; int blendmask; bool enamask; + double radmask; + double lapmask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 586532ce5..091cf4532 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -958,7 +958,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabsensimask LUMINANCECURVE, // Evlocallabblendmask LUMINANCECURVE, // EvlocallabshowmaskmaskMethod - LUMINANCECURVE // EvLocallabEnaMask + LUMINANCECURVE, // EvLocallabEnaMask + LUMINANCECURVE, // Evlocallabradmask + LUMINANCECURVE // Evlocallablapmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index b094a81e3..ae0c7cf98 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1207,6 +1207,8 @@ private: Adjuster* const blendmask; MyComboBoxText* const showmaskMethod; Gtk::CheckButton* const enamask; + Adjuster* const radmask; + Adjuster* const lapmask; sigc::connection showmaskMethodConn, enamaskConn; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 923cfbb27..8aea1d0e8 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4731,7 +4731,9 @@ LocallabMask::LocallabMask(): sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), showmaskMethod(Gtk::manage(new MyComboBoxText())), - enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))) + enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))) { // Parameter Mask common specific widgets @@ -4745,11 +4747,15 @@ LocallabMask::LocallabMask(): showmaskMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); showmaskMethodConn = showmaskMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmaskMethodChanged)); enamaskConn = enamask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::enamaskChanged)); + radmask->setAdjusterListener(this); + lapmask->setAdjusterListener(this); pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); pack_start(*enamask, Gtk::PACK_SHRINK, 0); + pack_start(*radmask, Gtk::PACK_SHRINK, 0); + pack_start(*lapmask, Gtk::PACK_SHRINK, 0); } @@ -4837,6 +4843,8 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params sensimask->setValue(spot.sensimask); blendmask->setValue(spot.blendmask); enamask->set_active(spot.enamask); + radmask->setValue(spot.radmask); + lapmask->setValue(spot.lapmask); } @@ -4862,6 +4870,8 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); spot.enamask = enamask->get_active(); + spot.radmask = radmask->getValue(); + spot.lapmask = lapmask->getValue(); } @@ -4895,6 +4905,8 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams // Set default value for adjuster widgets sensimask->setDefault((double)defSpot.sensimask); blendmask->setDefault((double)defSpot.blendmask); + radmask->setDefault(defSpot.radmask); + lapmask->setDefault(defSpot.lapmask); } @@ -4920,6 +4932,20 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == radmask) { + if (listener) { + listener->panelChanged(Evlocallabradmask, + radmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmask) { + if (listener) { + listener->panelChanged(Evlocallablapmask, + lapmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 427ada9ad..a981f06e5 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1483,6 +1483,8 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; + locallab.spots.at(j).radmask = locallab.spots.at(j).radmask && pSpot.radmask == otherSpot.radmask; + locallab.spots.at(j).lapmask = locallab.spots.at(j).lapmask && pSpot.lapmask == otherSpot.lapmask; } } @@ -4809,6 +4811,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).enamask = mods.locallab.spots.at(i).enamask; } + if (locallab.spots.at(i).radmask) { + toEdit.locallab.spots.at(i).radmask = mods.locallab.spots.at(i).radmask; + } + + if (locallab.spots.at(i).lapmask) { + toEdit.locallab.spots.at(i).lapmask = mods.locallab.spots.at(i).lapmask; + } + } if (pcvignette.enabled) { @@ -6383,7 +6393,10 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : expmask(v), sensimask(v), blendmask(v), - enamask(v) + enamask(v), + radmask(v), + lapmask(v) + { } @@ -6850,6 +6863,8 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) sensimask = v; blendmask = v; enamask = v; + radmask = v; + lapmask = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index a559408e2..d235e4987 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -841,6 +841,8 @@ public: bool sensimask; bool blendmask; bool enamask; + bool radmask; + bool lapmask; LocallabSpotEdited(bool v); From 93c68aa3c7dff1b864718a90c237e761072e4dd5 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 21 Jun 2020 22:36:04 +0200 Subject: [PATCH 006/114] wavontrast4(): cleanup and speedup for Pyramid, Graduated filter --- rtengine/iplocallab.cc | 68 +++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 7b27712e1..12dd9fba3 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7243,22 +7243,18 @@ BENCHFUN int H_Lm = wdspot->level_H(maxlvl - 1); if (lp.strwav != 0.f && lp.wavgradl) { +StopWatch Stop1("test"); array2D factorwav(W_Lm, H_Lm); calclocalGradientParams(lp, gpwav, 0, 0, W_Lm, H_Lm, 10); - - + const float mult = lp.strwav < 0.f ? -1.f : 1.f; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif for (int y = 0; y < H_Lm; y++) { for (int x = 0; x < W_Lm; x++) { - float factor = ImProcFunctions::calcGradientFactor(gpwav, x, y); - factorwav[y][x] = factor; - factorwav[y][x] = 1.f - factorwav[y][x]; - - if (lp.strwav < 0.f) { - factorwav[y][x] *= -1.f; - } + factorwav[y][x] = mult * (1.f - ImProcFunctions::calcGradientFactor(gpwav, x, y)); } } - float mean[10]; float meanN[10]; float sigma[10]; @@ -7287,7 +7283,7 @@ BENCHFUN if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { const int W_L = wdspot->level_W(level); const int H_L = wdspot->level_H(level); - float* const* wav_L = wdspot->level_coeffs(level); + auto wav_L = wdspot->level_coeffs(level)[dir]; const float effect = lp.sigmalc2; constexpr float offset = 1.f; float mea[10]; @@ -7301,14 +7297,29 @@ BENCHFUN const float asig = 0.166f / (sigma[level] * lp.sigmalc2); const float bsig = 0.5f - asig * mean[level]; const float amean = 0.5f / mean[level]; + float klev = 1.f; + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alowg * level + blowg; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahighg * level + bhighg; + } + } + klev *= 0.8f; + const float threshold = mean[level] + lp.sigmalc2 * sigma[level]; #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel for schedule(dynamic, 16) if (multiThread) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { - const float WavCL = std::fabs(wav_L[dir][y * W_L + x]); + const float WavCL = std::fabs(wav_L[y * W_L + x]); float beta; if (WavCL < mea[0]) { @@ -7336,37 +7347,20 @@ BENCHFUN } float absciss; - float &val = wav_L[dir][y * W_L + x]; - - if (std::fabs(val) >= (mean[level] + lp.sigmalc2 * sigma[level])) { //for max - const float valc = xlogf(std::fabs(val)) - logmax; - absciss = xexpf(valc * rap); - } else if (std::fabs(val) >= mean[level]) { - absciss = asig * std::fabs(val) + bsig; + if (WavCL >= threshold) { //for max + absciss = pow_F(WavCL - logmax, rap); + } else if (WavCL >= mean[level]) { + absciss = asig * WavCL + bsig; } else { - absciss = amean * std::fabs(val); + absciss = amean * WavCL; } - float klev = 1.f; - - if (level_hl != level_bl) { - if (level >= level_bl && level < level_hl) { - klev = alowg * level + blowg; - } - } - - if (level_hr != level_br) { - if (level > level_hr && level <= level_br) { - klev = ahighg * level + bhighg; - } - } - - const float kc = 0.8f * klev * factorwav[y][x] * absciss; + const float kc = klev * factorwav[y][x] * absciss; const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; float kinterm = 1.f + reduceeffect * kc; kinterm = kinterm <= 0.f ? 0.01f : kinterm; - val *= (1.f + (kinterm - 1.f) * beta); + wav_L[y * W_L + x] *= (1.f + (kinterm - 1.f) * beta); } } } From 750c1a62f1139521bb2f16cf1862f52e479c7bdb Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 08:44:15 +0200 Subject: [PATCH 007/114] Added chrom gamma slope mask common --- rtdata/languages/default | 3 +++ rtengine/procevents.h | 3 +++ rtengine/procparams.cc | 16 ++++++++++++++-- rtengine/procparams.h | 3 +++ rtengine/refreshmap.cc | 5 ++++- rtgui/locallabtools.h | 3 +++ rtgui/locallabtools2.cc | 41 +++++++++++++++++++++++++++++++++++++++- rtgui/paramsedited.cc | 23 +++++++++++++++++++++- rtgui/paramsedited.h | 3 +++ 9 files changed, 95 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index ae246e5ed..55361cd0f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1171,6 +1171,9 @@ HISTORY_MSG_930;Local - Mask Common blend HISTORY_MSG_932;Local - Mask Common enable HISTORY_MSG_933;Local - Mask Common radius soft HISTORY_MSG_934;Local - Mask Common laplacian +HISTORY_MSG_935;Local - Mask Common chroma +HISTORY_MSG_936;Local - Mask Common gamma +HISTORY_MSG_937;Local - Mask Common slope HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index b155cb9be..53cf55590 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -957,6 +957,9 @@ enum ProcEventCode { EvLocallabEnaMask = 931, Evlocallabradmask = 932, Evlocallablapmask = 933, + Evlocallabchromask = 934, + Evlocallabgammask = 935, + Evlocallabslopmask = 936, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 358e5fd94..ee1834b1d 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3740,7 +3740,10 @@ LocallabParams::LocallabSpot::LocallabSpot() : blendmask(0), enamask(false), radmask(0.0), - lapmask(0.0) + lapmask(0.0), + chromask(0.0), + gammask(1.0), + slopmask(0.0) { } @@ -4219,7 +4222,10 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && blendmask == other.blendmask && enamask == other.enamask && radmask == other.radmask - && lapmask == other.lapmask; + && lapmask == other.lapmask + && chromask == other.chromask + && gammask == other.gammask + && slopmask == other.slopmask; } @@ -5722,6 +5728,9 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); saveToKeyfile(!pedited || spot_edited->radmask, "Locallab", "Radmask_" + index_str, spot.radmask, keyFile); saveToKeyfile(!pedited || spot_edited->lapmask, "Locallab", "Lapmask_" + index_str, spot.lapmask, keyFile); + saveToKeyfile(!pedited || spot_edited->chromask, "Locallab", "Chromask_" + index_str, spot.chromask, keyFile); + saveToKeyfile(!pedited || spot_edited->gammask, "Locallab", "Gammask_" + index_str, spot.gammask, keyFile); + saveToKeyfile(!pedited || spot_edited->slopmask, "Locallab", "Slopmask_" + index_str, spot.slopmask, keyFile); } } } @@ -7443,6 +7452,9 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); assignFromKeyfile(keyFile, "Locallab", "Radmask_" + index_str, pedited, spot.radmask, spotEdited.radmask); assignFromKeyfile(keyFile, "Locallab", "Lapmask_" + index_str, pedited, spot.lapmask, spotEdited.lapmask); + assignFromKeyfile(keyFile, "Locallab", "Chromask_" + index_str, pedited, spot.chromask, spotEdited.chromask); + assignFromKeyfile(keyFile, "Locallab", "Gammask_" + index_str, pedited, spot.gammask, spotEdited.gammask); + assignFromKeyfile(keyFile, "Locallab", "Slopmask_" + index_str, pedited, spot.slopmask, spotEdited.slopmask); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b2b23e2e7..73e3b4c70 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1429,6 +1429,9 @@ struct LocallabParams { bool enamask; double radmask; double lapmask; + double chromask; + double gammask; + double slopmask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 091cf4532..2347b8917 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -960,7 +960,10 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabshowmaskmaskMethod LUMINANCECURVE, // EvLocallabEnaMask LUMINANCECURVE, // Evlocallabradmask - LUMINANCECURVE // Evlocallablapmask + LUMINANCECURVE, // Evlocallablapmask + LUMINANCECURVE, // Evlocallabchromask + LUMINANCECURVE, // Evlocallabgammask + LUMINANCECURVE // Evlocallabslopmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index ae0c7cf98..7ddf1bbb1 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1209,6 +1209,9 @@ private: Gtk::CheckButton* const enamask; Adjuster* const radmask; Adjuster* const lapmask; + Adjuster* const chromask; + Adjuster* const gammask; + Adjuster* const slopmask; sigc::connection showmaskMethodConn, enamaskConn; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 8aea1d0e8..e8e9e460d 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4733,7 +4733,10 @@ LocallabMask::LocallabMask(): showmaskMethod(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), - lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))) + lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slopmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))) { // Parameter Mask common specific widgets @@ -4749,6 +4752,9 @@ LocallabMask::LocallabMask(): enamaskConn = enamask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::enamaskChanged)); radmask->setAdjusterListener(this); lapmask->setAdjusterListener(this); + chromask->setAdjusterListener(this); + gammask->setAdjusterListener(this); + slopmask->setAdjusterListener(this); pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); @@ -4756,6 +4762,9 @@ LocallabMask::LocallabMask(): pack_start(*enamask, Gtk::PACK_SHRINK, 0); pack_start(*radmask, Gtk::PACK_SHRINK, 0); pack_start(*lapmask, Gtk::PACK_SHRINK, 0); + pack_start(*chromask, Gtk::PACK_SHRINK, 0); + pack_start(*gammask, Gtk::PACK_SHRINK, 0); + pack_start(*slopmask, Gtk::PACK_SHRINK, 0); } @@ -4845,6 +4854,9 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params enamask->set_active(spot.enamask); radmask->setValue(spot.radmask); lapmask->setValue(spot.lapmask); + chromask->setValue(spot.chromask); + gammask->setValue(spot.gammask); + slopmask->setValue(spot.slopmask); } @@ -4872,6 +4884,9 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.enamask = enamask->get_active(); spot.radmask = radmask->getValue(); spot.lapmask = lapmask->getValue(); + spot.chromask = chromask->getValue(); + spot.gammask = gammask->getValue(); + spot.slopmask = slopmask->getValue(); } @@ -4907,6 +4922,9 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams blendmask->setDefault((double)defSpot.blendmask); radmask->setDefault(defSpot.radmask); lapmask->setDefault(defSpot.lapmask); + chromask->setDefault(defSpot.chromask); + lapmask->setDefault(defSpot.lapmask); + slopmask->setDefault(defSpot.slopmask); } @@ -4946,6 +4964,27 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == chromask) { + if (listener) { + listener->panelChanged(Evlocallabchromask, + chromask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammask) { + if (listener) { + listener->panelChanged(Evlocallabgammask, + gammask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slopmask) { + if (listener) { + listener->panelChanged(Evlocallabslopmask, + slopmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index a981f06e5..8866885be 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1485,6 +1485,9 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; locallab.spots.at(j).radmask = locallab.spots.at(j).radmask && pSpot.radmask == otherSpot.radmask; locallab.spots.at(j).lapmask = locallab.spots.at(j).lapmask && pSpot.lapmask == otherSpot.lapmask; + locallab.spots.at(j).chromask = locallab.spots.at(j).chromask && pSpot.chromask == otherSpot.chromask; + locallab.spots.at(j).gammask = locallab.spots.at(j).gammask && pSpot.gammask == otherSpot.gammask; + locallab.spots.at(j).slopmask = locallab.spots.at(j).slopmask && pSpot.slopmask == otherSpot.slopmask; } } @@ -4819,6 +4822,18 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).lapmask = mods.locallab.spots.at(i).lapmask; } + if (locallab.spots.at(i).chromask) { + toEdit.locallab.spots.at(i).chromask = mods.locallab.spots.at(i).chromask; + } + + if (locallab.spots.at(i).gammask) { + toEdit.locallab.spots.at(i).gammask = mods.locallab.spots.at(i).gammask; + } + + if (locallab.spots.at(i).slopmask) { + toEdit.locallab.spots.at(i).slopmask = mods.locallab.spots.at(i).slopmask; + } + } if (pcvignette.enabled) { @@ -6395,7 +6410,10 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : blendmask(v), enamask(v), radmask(v), - lapmask(v) + lapmask(v), + chromask(v), + gammask(v), + slopmask(v) { } @@ -6865,6 +6883,9 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) enamask = v; radmask = v; lapmask = v; + chromask = v; + gammask = v; + slopmask = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index d235e4987..77d34d239 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -843,6 +843,9 @@ public: bool enamask; bool radmask; bool lapmask; + bool chromask; + bool gammask; + bool slopmask; LocallabSpotEdited(bool v); From 9f7412f509187606e5d790e280bd1da217b52220 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 09:24:37 +0200 Subject: [PATCH 008/114] Added complexity mode to common mask --- rtengine/procparams.cc | 4 ++++ rtengine/procparams.h | 1 + rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 41 ++++++++++++++++++++++++++++++++++++++++- rtgui/paramsedited.cc | 7 +++++++ rtgui/paramsedited.h | 1 + 6 files changed, 55 insertions(+), 1 deletion(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index ee1834b1d..f038738cc 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3735,6 +3735,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : anglog(0.0), // mask visimask(false), + complexmask(0), expmask(false), sensimask(60), blendmask(0), @@ -4217,6 +4218,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && anglog == other.anglog // mask && visimask == other.visimask + && complexmask == other.complexmask && expmask == other.expmask && sensimask == other.sensimask && blendmask == other.blendmask @@ -5723,6 +5725,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo //mask if ((!pedited || spot_edited->visimask) && spot.visimask) { saveToKeyfile(!pedited || spot_edited->expmask, "Locallab", "Expmask_" + index_str, spot.expmask, keyFile); + saveToKeyfile(!pedited || spot_edited->complexmask, "Locallab", "Complexmask_" + index_str, spot.complexmask, keyFile); saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); @@ -7447,6 +7450,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Anglog_" + index_str, pedited, spot.anglog, spotEdited.anglog); // mask spot.visimask = assignFromKeyfile(keyFile, "Locallab", "Expmask_" + index_str, pedited, spot.expmask, spotEdited.expmask); + assignFromKeyfile(keyFile, "Locallab", "Complexmask_" + index_str, pedited, spot.complexmask, spotEdited.complexmask); assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 73e3b4c70..abb32e137 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1423,6 +1423,7 @@ struct LocallabParams { double anglog; // mask bool visimask; + int complexmask; bool expmask; int sensimask; int blendmask; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 7ddf1bbb1..1cc184b24 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1237,6 +1237,8 @@ private: void enabledChanged() override; void showmaskMethodChanged(); void enamaskChanged(); + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; /* void autocomputeToggled(); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index e8e9e460d..0efc7e0ad 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4727,7 +4727,9 @@ void LocallabLog::updateLogGUI() /* ==== LocallabMask ==== */ LocallabMask::LocallabMask(): - LocallabTool(this, M("TP_LOCALLAB_MASKCOM_TOOLNAME"), M("TP_LOCALLAB_MASKCOM"), false, false), + LocallabTool(this, M("TP_LOCALLAB_MASKCOM_TOOLNAME"), M("TP_LOCALLAB_MASKCOM"), false), + + // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), showmaskMethod(Gtk::manage(new MyComboBoxText())), @@ -4847,6 +4849,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params exp->set_visible(spot.visimask); exp->setEnabled(spot.expmask); + complexity->set_active(spot.complexmask); sensimask->setValue(spot.sensimask); @@ -4863,6 +4866,9 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params // Enable all listeners enableListener(); + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + // Update Log Encoding GUI according to autocompute button state // updateLogGUI(); @@ -4878,6 +4884,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.expmask = exp->getEnabled(); spot.visimask = exp->get_visible(); + spot.complexmask = complexity->get_active_row_number(); spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); @@ -4931,6 +4938,38 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams // Note: No need to manage pedited as batch mode is deactivated for Locallab } +void LocallabMask::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + lapmask->hide(); + gammask->hide(); + slopmask->hide(); + } else { + // Advanced widgets are shown in Expert mode + lapmask->show(); + gammask->show(); + slopmask->show(); + } +} + +void LocallabMask::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + + lapmask->setValue(defSpot.lapmask); + gammask->setValue(defSpot.gammask); + slopmask->setValue(defSpot.slopmask); + + // Enable all listeners + enableListener(); + +} + void LocallabMask::adjusterChanged(Adjuster* a, double newval) { if (isLocActivated && exp->getEnabled()) { diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 8866885be..1cb15568a 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1479,6 +1479,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).anglog = locallab.spots.at(j).anglog && pSpot.anglog == otherSpot.anglog; //mask locallab.spots.at(j).visimask = locallab.spots.at(j).visimask && pSpot.visimask == otherSpot.visimask; + locallab.spots.at(j).complexmask = locallab.spots.at(j).complexmask && pSpot.complexmask == otherSpot.complexmask; locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; @@ -4798,6 +4799,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).visimask = mods.locallab.spots.at(i).visimask; } + if (locallab.spots.at(i).complexmask) { + toEdit.locallab.spots.at(i).complexmask = mods.locallab.spots.at(i).complexmask; + } + if (locallab.spots.at(i).expmask) { toEdit.locallab.spots.at(i).expmask = mods.locallab.spots.at(i).expmask; } @@ -6405,6 +6410,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : anglog(v), // mask visimask(v), + complexmask(v), expmask(v), sensimask(v), blendmask(v), @@ -6877,6 +6883,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) anglog = v; // mask visimask = v; + complexmask = v; expmask = v; sensimask = v; blendmask = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 77d34d239..f91b93f61 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -837,6 +837,7 @@ public: bool anglog; //mask bool visimask; + bool complexmask; bool expmask; bool sensimask; bool blendmask; From 04855a8db6776c39ec754183abe1d5556d20c0c8 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 11:44:02 +0200 Subject: [PATCH 009/114] added common mask curves L(L) C(C) LC(H) --- rtdata/languages/default | 3 ++ rtengine/procevents.h | 3 ++ rtengine/procparams.cc | 54 +++++++++++++++++++++++++++++ rtengine/procparams.h | 3 ++ rtengine/refreshmap.cc | 5 ++- rtgui/locallabtools.h | 8 +++++ rtgui/locallabtools2.cc | 73 ++++++++++++++++++++++++++++++++++++++++ rtgui/paramsedited.cc | 21 ++++++++++++ rtgui/paramsedited.h | 3 ++ 9 files changed, 172 insertions(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 55361cd0f..724af3f0a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1174,6 +1174,9 @@ HISTORY_MSG_934;Local - Mask Common laplacian HISTORY_MSG_935;Local - Mask Common chroma HISTORY_MSG_936;Local - Mask Common gamma HISTORY_MSG_937;Local - Mask Common slope +HISTORY_MSG_938;Local - Mask Common curve C(C) +HISTORY_MSG_939;Local - Mask Common curve L(L) +HISTORY_MSG_940;Local - Mask Common curve LC(H) HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 53cf55590..3402f0388 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -960,6 +960,9 @@ enum ProcEventCode { Evlocallabchromask = 934, Evlocallabgammask = 935, Evlocallabslopmask = 936, + EvlocallabCCmask_shape = 937, + EvlocallabLLmask_shape = 938, + EvlocallabHHmask_shape = 939, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index f038738cc..8f35d68df 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3740,6 +3740,51 @@ LocallabParams::LocallabSpot::LocallabSpot() : sensimask(60), blendmask(0), enamask(false), + CCmask_curve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, + LLmask_curve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, + HHmask_curve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, radmask(0.0), lapmask(0.0), chromask(0.0), @@ -4223,6 +4268,9 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && sensimask == other.sensimask && blendmask == other.blendmask && enamask == other.enamask + && CCmask_curve == other.CCmask_curve + && LLmask_curve == other.LLmask_curve + && HHmask_curve == other.HHmask_curve && radmask == other.radmask && lapmask == other.lapmask && chromask == other.chromask @@ -5729,6 +5777,9 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmask_curve, "Locallab", "CCmask_Curve_" + index_str, spot.CCmask_curve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmask_curve, "Locallab", "LLmask_Curve_" + index_str, spot.LLmask_curve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmask_curve, "Locallab", "HHmask_Curve_" + index_str, spot.HHmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->radmask, "Locallab", "Radmask_" + index_str, spot.radmask, keyFile); saveToKeyfile(!pedited || spot_edited->lapmask, "Locallab", "Lapmask_" + index_str, spot.lapmask, keyFile); saveToKeyfile(!pedited || spot_edited->chromask, "Locallab", "Chromask_" + index_str, spot.chromask, keyFile); @@ -7454,6 +7505,9 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); + assignFromKeyfile(keyFile, "Locallab", "CCmask_Curve_" + index_str, pedited, spot.CCmask_curve, spotEdited.CCmask_curve); + assignFromKeyfile(keyFile, "Locallab", "LLmask_Curve_" + index_str, pedited, spot.LLmask_curve, spotEdited.LLmask_curve); + assignFromKeyfile(keyFile, "Locallab", "HHmask_Curve_" + index_str, pedited, spot.HHmask_curve, spotEdited.HHmask_curve); assignFromKeyfile(keyFile, "Locallab", "Radmask_" + index_str, pedited, spot.radmask, spotEdited.radmask); assignFromKeyfile(keyFile, "Locallab", "Lapmask_" + index_str, pedited, spot.lapmask, spotEdited.lapmask); assignFromKeyfile(keyFile, "Locallab", "Chromask_" + index_str, pedited, spot.chromask, spotEdited.chromask); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index abb32e137..b744b4be2 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1428,6 +1428,9 @@ struct LocallabParams { int sensimask; int blendmask; bool enamask; + std::vector CCmask_curve; + std::vector LLmask_curve; + std::vector HHmask_curve; double radmask; double lapmask; double chromask; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 2347b8917..c778560be 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -963,7 +963,10 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallablapmask LUMINANCECURVE, // Evlocallabchromask LUMINANCECURVE, // Evlocallabgammask - LUMINANCECURVE // Evlocallabslopmask + LUMINANCECURVE, // Evlocallabslopmask + LUMINANCECURVE, // EvlocallabCCmask_shape + LUMINANCECURVE, // EvlocallabLLmask_shape + LUMINANCECURVE // EvlocallabHHmask_shape }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 1cc184b24..3472c3ea4 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1201,12 +1201,18 @@ private: class LocallabMask: public Gtk::VBox, public LocallabTool + { private: Adjuster* const sensimask; Adjuster* const blendmask; MyComboBoxText* const showmaskMethod; Gtk::CheckButton* const enamask; + CurveEditorGroup* const mask_CurveEditorG; + FlatCurveEditor* const CCmask_shape; + FlatCurveEditor* const LLmask_shape; + FlatCurveEditor* const HHmask_shape; + Adjuster* const radmask; Adjuster* const lapmask; Adjuster* const chromask; @@ -1217,6 +1223,7 @@ private: public: LocallabMask(); + ~LocallabMask(); bool isMaskViewActive() override; void resetMaskView() override; @@ -1230,6 +1237,7 @@ public: 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 curveChanged(CurveEditor* ce) override; // void updateAutocompute(const float blackev, const float whiteev, const float sourceg, const float targetg); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 0efc7e0ad..7c7de4a91 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4734,6 +4734,11 @@ LocallabMask::LocallabMask(): blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), showmaskMethod(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + mask_CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), + CCmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), @@ -4742,6 +4747,8 @@ LocallabMask::LocallabMask(): { // Parameter Mask common specific widgets + const LocallabParams::LocallabSpot defSpot; + sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); showmaskMethod->append(M("TP_LOCALLAB_SHOWMNONE")); @@ -4752,6 +4759,23 @@ LocallabMask::LocallabMask(): showmaskMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); showmaskMethodConn = showmaskMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmaskMethodChanged)); enamaskConn = enamask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::enamaskChanged)); + mask_CurveEditorG->setCurveListener(this); + + CCmask_shape->setIdentityValue(0.); + CCmask_shape->setResetCurve(FlatCurveType(defSpot.CCmask_curve.at(0)), defSpot.CCmask_curve); + CCmask_shape->setBottomBarColorProvider(this, 1); + + LLmask_shape->setIdentityValue(0.); + LLmask_shape->setResetCurve(FlatCurveType(defSpot.LLmask_curve.at(0)), defSpot.LLmask_curve); + LLmask_shape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmask_shape->setIdentityValue(0.); + HHmask_shape->setResetCurve(FlatCurveType(defSpot.HHmask_curve.at(0)), defSpot.HHmask_curve); + HHmask_shape->setCurveColorProvider(this, 2); + HHmask_shape->setBottomBarColorProvider(this, 2); + + mask_CurveEditorG->curveListComplete(); + radmask->setAdjusterListener(this); lapmask->setAdjusterListener(this); chromask->setAdjusterListener(this); @@ -4761,6 +4785,7 @@ LocallabMask::LocallabMask(): pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); + pack_start(*mask_CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor pack_start(*enamask, Gtk::PACK_SHRINK, 0); pack_start(*radmask, Gtk::PACK_SHRINK, 0); pack_start(*lapmask, Gtk::PACK_SHRINK, 0); @@ -4796,14 +4821,25 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) exp->set_tooltip_text(M("TP_LOCALLAB_MASKCOM_TOOLTIP")); sensimask->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); blendmask->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + CCmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); } else { exp->set_tooltip_text(M("")); sensimask->set_tooltip_text(M("")); blendmask->set_tooltip_text(M("")); + CCmask_shape->setTooltip(M("")); + LLmask_shape->setTooltip(M("")); + HHmask_shape->setTooltip(M("")); } } +LocallabMask::~LocallabMask() +{ + delete mask_CurveEditorG; +} + void LocallabMask::disableListener() { LocallabTool::disableListener(); @@ -4855,6 +4891,9 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params sensimask->setValue(spot.sensimask); blendmask->setValue(spot.blendmask); enamask->set_active(spot.enamask); + CCmask_shape->setCurve(spot.CCmask_curve); + LLmask_shape->setCurve(spot.LLmask_curve); + HHmask_shape->setCurve(spot.HHmask_curve); radmask->setValue(spot.radmask); lapmask->setValue(spot.lapmask); chromask->setValue(spot.chromask); @@ -4889,6 +4928,9 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); spot.enamask = enamask->get_active(); + spot.CCmask_curve = CCmask_shape->getCurve(); + spot.LLmask_curve = LLmask_shape->getCurve(); + spot.HHmask_curve = HHmask_shape->getCurve(); spot.radmask = radmask->getValue(); spot.lapmask = lapmask->getValue(); spot.chromask = chromask->getValue(); @@ -4970,6 +5012,37 @@ void LocallabMask::convertParamToNormal() } +void LocallabMask::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + + if (ce == CCmask_shape) { + if (listener) { + listener->panelChanged(EvlocallabCCmask_shape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmask_shape) { + if (listener) { + listener->panelChanged(EvlocallabLLmask_shape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmask_shape) { + if (listener) { + listener->panelChanged(EvlocallabHHmask_shape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + + } +} + + + void LocallabMask::adjusterChanged(Adjuster* a, double newval) { if (isLocActivated && exp->getEnabled()) { diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 1cb15568a..8f0abbcf4 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1484,6 +1484,9 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; + locallab.spots.at(j).CCmask_curve = locallab.spots.at(j).CCmask_curve && pSpot.CCmask_curve == otherSpot.CCmask_curve; + locallab.spots.at(j).LLmask_curve = locallab.spots.at(j).LLmask_curve && pSpot.LLmask_curve == otherSpot.LLmask_curve; + locallab.spots.at(j).HHmask_curve = locallab.spots.at(j).HHmask_curve && pSpot.HHmask_curve == otherSpot.HHmask_curve; locallab.spots.at(j).radmask = locallab.spots.at(j).radmask && pSpot.radmask == otherSpot.radmask; locallab.spots.at(j).lapmask = locallab.spots.at(j).lapmask && pSpot.lapmask == otherSpot.lapmask; locallab.spots.at(j).chromask = locallab.spots.at(j).chromask && pSpot.chromask == otherSpot.chromask; @@ -4819,6 +4822,18 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).enamask = mods.locallab.spots.at(i).enamask; } + if (locallab.spots.at(i).CCmask_curve) { + toEdit.locallab.spots.at(i).CCmask_curve = mods.locallab.spots.at(i).CCmask_curve; + } + + if (locallab.spots.at(i).LLmask_curve) { + toEdit.locallab.spots.at(i).LLmask_curve = mods.locallab.spots.at(i).LLmask_curve; + } + + if (locallab.spots.at(i).HHmask_curve) { + toEdit.locallab.spots.at(i).HHmask_curve = mods.locallab.spots.at(i).HHmask_curve; + } + if (locallab.spots.at(i).radmask) { toEdit.locallab.spots.at(i).radmask = mods.locallab.spots.at(i).radmask; } @@ -6415,6 +6430,9 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : sensimask(v), blendmask(v), enamask(v), + CCmask_curve(v), + LLmask_curve(v), + HHmask_curve(v), radmask(v), lapmask(v), chromask(v), @@ -6888,6 +6906,9 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) sensimask = v; blendmask = v; enamask = v; + CCmask_curve = v; + LLmask_curve = v; + HHmask_curve = v; radmask = v; lapmask = v; chromask = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index f91b93f61..49fc7d07a 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -842,6 +842,9 @@ public: bool sensimask; bool blendmask; bool enamask; + bool CCmask_curve; + bool LLmask_curve; + bool HHmask_curve; bool radmask; bool lapmask; bool chromask; From 804ed732c4ce0f03db5130e9f736dc46f1ca4a71 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 13:26:05 +0200 Subject: [PATCH 010/114] Added common mask structure mask --- rtengine/procevents.h | 4 +- rtengine/procparams.cc | 8 ++++ rtengine/procparams.h | 2 + rtengine/refreshmap.cc | 6 ++- rtgui/locallabtools.h | 6 ++- rtgui/locallabtools2.cc | 83 +++++++++++++++++++++++++++++++++++------ rtgui/paramsedited.cc | 14 +++++++ rtgui/paramsedited.h | 2 + 8 files changed, 110 insertions(+), 15 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 3402f0388..d98a8a6c2 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -950,7 +950,7 @@ enum ProcEventCode { EvLocallabSpotcolorscope = 924, EvlocallabshowmasktypMethod = 925, Evlocallabshadmaskblsha = 926, - EvLocenamask = 927, + EvLocena_mask = 927, Evlocallabsensimask = 928, Evlocallabblendmask = 929, EvlocallabshowmaskmaskMethod = 930, @@ -963,6 +963,8 @@ enum ProcEventCode { EvlocallabCCmask_shape = 937, EvlocallabLLmask_shape = 938, EvlocallabHHmask_shape = 939, + EvLocallabtoolmask = 940, + Evlocallabstrumaskmask = 941, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 8f35d68df..1bcec4de2 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3785,6 +3785,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : 0.35, 0.35 }, + strumaskmask(0.), + toolmask(true), radmask(0.0), lapmask(0.0), chromask(0.0), @@ -4271,6 +4273,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && CCmask_curve == other.CCmask_curve && LLmask_curve == other.LLmask_curve && HHmask_curve == other.HHmask_curve + && strumaskmask == other.strumaskmask + && toolmask == other.toolmask && radmask == other.radmask && lapmask == other.lapmask && chromask == other.chromask @@ -5780,6 +5784,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->CCmask_curve, "Locallab", "CCmask_Curve_" + index_str, spot.CCmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->LLmask_curve, "Locallab", "LLmask_Curve_" + index_str, spot.LLmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->HHmask_curve, "Locallab", "HHmask_Curve_" + index_str, spot.HHmask_curve, keyFile); + saveToKeyfile(!pedited || spot_edited->strumaskmask, "Locallab", "Strumaskmask_" + index_str, spot.strumaskmask, keyFile); + saveToKeyfile(!pedited || spot_edited->toolmask, "Locallab", "Toolmask_" + index_str, spot.toolmask, keyFile); saveToKeyfile(!pedited || spot_edited->radmask, "Locallab", "Radmask_" + index_str, spot.radmask, keyFile); saveToKeyfile(!pedited || spot_edited->lapmask, "Locallab", "Lapmask_" + index_str, spot.lapmask, keyFile); saveToKeyfile(!pedited || spot_edited->chromask, "Locallab", "Chromask_" + index_str, spot.chromask, keyFile); @@ -7508,6 +7514,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "CCmask_Curve_" + index_str, pedited, spot.CCmask_curve, spotEdited.CCmask_curve); assignFromKeyfile(keyFile, "Locallab", "LLmask_Curve_" + index_str, pedited, spot.LLmask_curve, spotEdited.LLmask_curve); assignFromKeyfile(keyFile, "Locallab", "HHmask_Curve_" + index_str, pedited, spot.HHmask_curve, spotEdited.HHmask_curve); + assignFromKeyfile(keyFile, "Locallab", "Strumaskmask_" + index_str, pedited, spot.strumaskmask, spotEdited.strumaskmask); + assignFromKeyfile(keyFile, "Locallab", "Toolmask_" + index_str, pedited, spot.toolmask, spotEdited.toolmask); assignFromKeyfile(keyFile, "Locallab", "Radmask_" + index_str, pedited, spot.radmask, spotEdited.radmask); assignFromKeyfile(keyFile, "Locallab", "Lapmask_" + index_str, pedited, spot.lapmask, spotEdited.lapmask); assignFromKeyfile(keyFile, "Locallab", "Chromask_" + index_str, pedited, spot.chromask, spotEdited.chromask); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b744b4be2..79c81e63e 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1431,6 +1431,8 @@ struct LocallabParams { std::vector CCmask_curve; std::vector LLmask_curve; std::vector HHmask_curve; + double strumaskmask; + bool toolmask; double radmask; double lapmask; double chromask; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index c778560be..29417c059 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -958,7 +958,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabsensimask LUMINANCECURVE, // Evlocallabblendmask LUMINANCECURVE, // EvlocallabshowmaskmaskMethod - LUMINANCECURVE, // EvLocallabEnaMask + LUMINANCECURVE, // EvLocallabEna_Mask LUMINANCECURVE, // Evlocallabradmask LUMINANCECURVE, // Evlocallablapmask LUMINANCECURVE, // Evlocallabchromask @@ -966,7 +966,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabslopmask LUMINANCECURVE, // EvlocallabCCmask_shape LUMINANCECURVE, // EvlocallabLLmask_shape - LUMINANCECURVE // EvlocallabHHmask_shape + LUMINANCECURVE, // EvlocallabHHmask_shape + LUMINANCECURVE, // EvLocallabtoolmask + LUMINANCECURVE // Evlocallabstrumaskmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 3472c3ea4..91f7d4989 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1212,6 +1212,9 @@ private: FlatCurveEditor* const CCmask_shape; FlatCurveEditor* const LLmask_shape; FlatCurveEditor* const HHmask_shape; + Gtk::Frame* const struFrame; + Adjuster* const strumaskmask; + Gtk::CheckButton* const toolmask; Adjuster* const radmask; Adjuster* const lapmask; @@ -1219,7 +1222,7 @@ private: Adjuster* const gammask; Adjuster* const slopmask; - sigc::connection showmaskMethodConn, enamaskConn; + sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn; public: LocallabMask(); @@ -1245,6 +1248,7 @@ private: void enabledChanged() override; void showmaskMethodChanged(); void enamaskChanged(); + void toolmaskChanged(); void convertParamToNormal() override; void updateGUIToMode(const modeType new_type) override; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 7c7de4a91..f3448de71 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4738,7 +4738,11 @@ LocallabMask::LocallabMask(): CCmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), LLmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), - + + struFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABSTRUM")))), + strumaskmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), + toolmask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), @@ -4776,6 +4780,12 @@ LocallabMask::LocallabMask(): mask_CurveEditorG->curveListComplete(); + struFrame->set_label_align(0.025, 0.5); + + strumaskmask->setAdjusterListener(this); + + toolmaskConn = toolmask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::toolmaskChanged)); + radmask->setAdjusterListener(this); lapmask->setAdjusterListener(this); chromask->setAdjusterListener(this); @@ -4784,15 +4794,31 @@ LocallabMask::LocallabMask(): pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); - pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); - pack_start(*mask_CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor - pack_start(*enamask, Gtk::PACK_SHRINK, 0); - pack_start(*radmask, Gtk::PACK_SHRINK, 0); - pack_start(*lapmask, Gtk::PACK_SHRINK, 0); - pack_start(*chromask, Gtk::PACK_SHRINK, 0); - pack_start(*gammask, Gtk::PACK_SHRINK, 0); - pack_start(*slopmask, Gtk::PACK_SHRINK, 0); + + ToolParamBlock* const maskmaskBox = Gtk::manage(new ToolParamBlock()); + maskmaskBox->pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); + maskmaskBox->pack_start(*mask_CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskmaskBox->pack_start(*enamask, Gtk::PACK_SHRINK, 0); + ToolParamBlock* const strumBox = Gtk::manage(new ToolParamBlock()); + strumBox->pack_start(*strumaskmask); + strumBox->pack_start(*toolmask); + + struFrame->add(*strumBox); + maskmaskBox->pack_start(*struFrame, Gtk::PACK_SHRINK, 0); + + Gtk::Frame* const toolmaskFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK"))); + toolmaskFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const toolmaskBox = Gtk::manage(new ToolParamBlock()); + + toolmaskBox->pack_start(*radmask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*lapmask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*chromask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*gammask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*slopmask, Gtk::PACK_SHRINK, 0); + toolmaskFrame->add(*toolmaskBox); + maskmaskBox->pack_start(*toolmaskFrame); + pack_start(*maskmaskBox); } bool LocallabMask::isMaskViewActive() @@ -4824,6 +4850,7 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) CCmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + struFrame->set_tooltip_text(M("TP_LOCALLAB_STRUMASK_TOOLTIP")); } else { exp->set_tooltip_text(M("")); @@ -4832,6 +4859,7 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) CCmask_shape->setTooltip(M("")); LLmask_shape->setTooltip(M("")); HHmask_shape->setTooltip(M("")); + struFrame->set_tooltip_text(M("")); } } @@ -4845,6 +4873,7 @@ void LocallabMask::disableListener() LocallabTool::disableListener(); showmaskMethodConn.block(true); enamaskConn.block(true); + toolmaskConn.block(true); } @@ -4853,6 +4882,7 @@ void LocallabMask::enableListener() LocallabTool::enableListener(); showmaskMethodConn.block(false); enamaskConn.block(false); + toolmaskConn.block(false); } @@ -4894,6 +4924,8 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params CCmask_shape->setCurve(spot.CCmask_curve); LLmask_shape->setCurve(spot.LLmask_curve); HHmask_shape->setCurve(spot.HHmask_curve); + strumaskmask->setValue(spot.strumaskmask); + toolmask->set_active(spot.toolmask); radmask->setValue(spot.radmask); lapmask->setValue(spot.lapmask); chromask->setValue(spot.chromask); @@ -4931,6 +4963,8 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.CCmask_curve = CCmask_shape->getCurve(); spot.LLmask_curve = LLmask_shape->getCurve(); spot.HHmask_curve = HHmask_shape->getCurve(); + spot.strumaskmask = strumaskmask->getValue(); + spot.toolmask = toolmask->get_active(); spot.radmask = radmask->getValue(); spot.lapmask = lapmask->getValue(); spot.chromask = chromask->getValue(); @@ -4968,6 +5002,8 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams // Set default value for adjuster widgets sensimask->setDefault((double)defSpot.sensimask); + strumaskmask->setDefault(defSpot.strumaskmask); + toolmask->set_active(defSpot.toolmask); blendmask->setDefault((double)defSpot.blendmask); radmask->setDefault(defSpot.radmask); lapmask->setDefault(defSpot.lapmask); @@ -4987,11 +5023,13 @@ void LocallabMask::updateGUIToMode(const modeType new_type) lapmask->hide(); gammask->hide(); slopmask->hide(); + struFrame->hide(); } else { // Advanced widgets are shown in Expert mode lapmask->show(); gammask->show(); slopmask->show(); + struFrame->show(); } } @@ -5006,6 +5044,8 @@ void LocallabMask::convertParamToNormal() lapmask->setValue(defSpot.lapmask); gammask->setValue(defSpot.gammask); slopmask->setValue(defSpot.slopmask); + strumaskmask->setValue(defSpot.strumaskmask); + toolmask->set_active(defSpot.toolmask); // Enable all listeners enableListener(); @@ -5054,6 +5094,12 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == strumaskmask) { + if (listener) { + listener->panelChanged(Evlocallabstrumaskmask, + strumaskmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } if (a == blendmask) { if (listener) { @@ -5106,10 +5152,25 @@ void LocallabMask::enabledChanged() if (isLocActivated) { if (listener) { if (exp->getEnabled()) { - listener->panelChanged(EvLocenamask, + listener->panelChanged(EvLocena_mask, M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); } else { - listener->panelChanged(EvLocenamask, + listener->panelChanged(EvLocena_mask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabMask::toolmaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (toolmask->get_active()) { + listener->panelChanged(EvLocallabtoolmask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabtoolmask, M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 8f0abbcf4..7111d1986 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1487,6 +1487,8 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).CCmask_curve = locallab.spots.at(j).CCmask_curve && pSpot.CCmask_curve == otherSpot.CCmask_curve; locallab.spots.at(j).LLmask_curve = locallab.spots.at(j).LLmask_curve && pSpot.LLmask_curve == otherSpot.LLmask_curve; locallab.spots.at(j).HHmask_curve = locallab.spots.at(j).HHmask_curve && pSpot.HHmask_curve == otherSpot.HHmask_curve; + locallab.spots.at(j).strumaskmask = locallab.spots.at(j).strumaskmask && pSpot.strumaskmask == otherSpot.strumaskmask; + locallab.spots.at(j).toolmask = locallab.spots.at(j).toolmask && pSpot.toolmask == otherSpot.toolmask; locallab.spots.at(j).radmask = locallab.spots.at(j).radmask && pSpot.radmask == otherSpot.radmask; locallab.spots.at(j).lapmask = locallab.spots.at(j).lapmask && pSpot.lapmask == otherSpot.lapmask; locallab.spots.at(j).chromask = locallab.spots.at(j).chromask && pSpot.chromask == otherSpot.chromask; @@ -4834,6 +4836,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).HHmask_curve = mods.locallab.spots.at(i).HHmask_curve; } + if (locallab.spots.at(i).strumaskmask) { + toEdit.locallab.spots.at(i).strumaskmask = mods.locallab.spots.at(i).strumaskmask; + } + + if (locallab.spots.at(i).toolmask) { + toEdit.locallab.spots.at(i).toolmask = mods.locallab.spots.at(i).toolmask; + } + if (locallab.spots.at(i).radmask) { toEdit.locallab.spots.at(i).radmask = mods.locallab.spots.at(i).radmask; } @@ -6433,6 +6443,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : CCmask_curve(v), LLmask_curve(v), HHmask_curve(v), + strumaskmask(v), + toolmask(v), radmask(v), lapmask(v), chromask(v), @@ -6909,6 +6921,8 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) CCmask_curve = v; LLmask_curve = v; HHmask_curve = v; + strumaskmask = v; + toolmask = v; radmask = v; lapmask = v; chromask = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 49fc7d07a..b177101f3 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -845,6 +845,8 @@ public: bool CCmask_curve; bool LLmask_curve; bool HHmask_curve; + bool strumaskmask; + bool toolmask; bool radmask; bool lapmask; bool chromask; From 84b59f187aa8418e0b5f0986caa6a5a7e5f08136 Mon Sep 17 00:00:00 2001 From: Anders Bennehag Date: Sun, 21 Jun 2020 14:08:45 -0400 Subject: [PATCH 011/114] createJPEGMarker(): Removes MakerNotes.ShotInfo from jpeg export as it is 52Kb large on Nikon Z-series cameras, which results in jpeg exif tags being larger than allowed 65Kb limit. Fixes https://github.com/Beep6581/RawTherapee/issues/5698 --- rtexif/rtexif.cc | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index c0038c067..f32a9feb1 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -3248,7 +3248,43 @@ int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::pro TagDirectory* cl; if (root) { - cl = (const_cast (root))->clone (nullptr); + cl = root->clone(nullptr); + + // Drop unwanted tags before exporting + // For example, Nikon Z-series has a 52Kb MakerNotes->ShotInfo tag + // which does not fit into the 65Kb limit on JPEG exif tags + const Tag* const make_tag = cl->getTag(271); + if (make_tag && !std::strncmp((const char*)make_tag->getValue(), "NIKON CORPORATION", 17)) { + [cl]() + { + Tag* const exif_tag = cl->getTag(34665); + if (!exif_tag) { + return; + } + + TagDirectory* const exif_dir = exif_tag->getDirectory(); + if (!exif_dir) { + return; + } + + Tag* const make_notes_tag = exif_dir->getTag(37500); + if (!make_notes_tag) { + return; + } + + TagDirectory* const maker_notes_dir = make_notes_tag->getDirectory(); + if (!maker_notes_dir) { + return; + } + + Tag* const shot_info_tag = maker_notes_dir->getTag(145); + if (!shot_info_tag) { + return; + } + + shot_info_tag->setKeep(false); + }(); + } } else { cl = new TagDirectory (nullptr, ifdAttribs, INTEL); } From ac2974980631389ef2fcd85577a6804390b95a4d Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Mon, 22 Jun 2020 15:15:09 +0200 Subject: [PATCH 012/114] wavcontrast4(): cleanups --- rtengine/iplocallab.cc | 104 +++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 12dd9fba3..edfdcac0a 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7227,7 +7227,7 @@ void ImProcFunctions::wavcontrast4(struct local_params& lp, float ** tmp, float float sigm, float offs, int & maxlvl, float sigmadc, float deltad, float chromalev, float chromablu, bool blurlc, bool blurena, bool levelena, bool comprena, bool compreena, float compress, float thres) { BENCHFUN - wavelet_decomposition *wdspot = new wavelet_decomposition(tmp[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + std::unique_ptr wdspot(new wavelet_decomposition(tmp[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); //first decomposition for compress dynamic range positive values and other process if (wdspot->memory_allocation_failed()) { @@ -7368,10 +7368,6 @@ StopWatch Stop1("test"); } } - //declare a and b if need - wavelet_decomposition *wdspota = nullptr; - wavelet_decomposition *wdspotb = nullptr; - int W_L = wdspot->level_W(0); int H_L = wdspot->level_H(0); float *wav_L0 = wdspot->get_coeff0(); @@ -7886,16 +7882,16 @@ StopWatch Stop1("test"); #pragma omp parallel for schedule(dynamic, 16 * W_L) if (multiThread) #endif for (int i = 0; i < W_L * H_L; i++) { - const float val = wav_L[dir][i]; + const float val = std::fabs(wav_L[dir][i]); float absciss; - if (std::fabs(val) >= limit1) { //for max - const float valcour = xlogf(std::fabs(val)); + if (val >= limit1) { //for max + const float valcour = xlogf(val); absciss = xexpf((valcour - logmax) * rap); - } else if (std::fabs(val) >= limit2) { - absciss = asig * std::fabs(val) + bsig; + } else if (val >= limit2) { + absciss = asig * val + bsig; } else { - absciss = amean * std::fabs(val); + absciss = amean * val; } const float kc = klev * (locwavCurve[absciss * 500.f] - 0.5f); @@ -7910,66 +7906,60 @@ StopWatch Stop1("test"); } } } - //reconstruct all for L wdspot->reconstruct(tmp[0], 1.f); - delete wdspot; - if (wavcurvecon && (chromalev != 1.f) && levelena) { // a and b if need ) {//contrast by levels for chroma a and b - wdspota = new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); - - if (wdspota->memory_allocation_failed()) { + bool reconstruct = false; + if (wavcurvecon && (chromalev != 1.f) && levelena) { // a if need ) {//contrast by levels for chroma a + // a + wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { return; } + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + reconstruct = true; + } + if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need + // a + if (!reconstruct) { + wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { + return; + } + } + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + reconstruct = true; + } + if (reconstruct) { + wdspot->reconstruct(tmpa[0], 1.f); + } - wavcbd(*wdspota, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); - wdspota->reconstruct(tmpa[0], 1.f); - delete wdspota; - - wdspotb = new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); - - if (wdspotb->memory_allocation_failed()) { + reconstruct = false; + if (wavcurvecon && (chromalev != 1.f) && levelena) { // b if need ) {//contrast by levels for chroma b + //b + wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { return; } - - wavcbd(*wdspotb, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); - wdspotb->reconstruct(tmpb[0], 1.f); - delete wdspotb; - + //b + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + reconstruct = true; } - if (wavcurvelev && radlevblur > 0.f && blurena) {//chroma blur if need - if (!blurlc) { - // a - wdspota = new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); - - if (wdspota->memory_allocation_failed()) { + if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need + //b + if (!reconstruct) { + wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { return; } - - if (radlevblur > 0.f && chromablu > 0.f) { - wavcont(lp, tmp, *wdspota, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); - } - - wdspota->reconstruct(tmpa[0], 1.f); - delete wdspota; - - //b - wdspotb = new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); - - if (wdspotb->memory_allocation_failed()) { - return; - } - - if (radlevblur > 0.f && chromablu > 0.f) { - wavcont(lp, tmp, *wdspotb, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); - } - - wdspotb->reconstruct(tmpb[0], 1.f); - delete wdspotb; } + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + reconstruct = true; + } + if (reconstruct) { + wdspot->reconstruct(tmpb[0], 1.f); } - } From cc6e837e7500f0431a7a009a9636fd84fc73edfd Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 16:28:26 +0200 Subject: [PATCH 013/114] Added common mask H(H) curve --- rtdata/languages/default | 3 +++ rtengine/procevents.h | 1 + rtengine/procparams.cc | 22 ++++++++++++++++++++-- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 33 +++++++++++++++++++++++++++++++-- rtgui/paramsedited.cc | 9 ++++++++- rtgui/paramsedited.h | 1 + 9 files changed, 69 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 724af3f0a..72e9c0602 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1177,6 +1177,9 @@ HISTORY_MSG_937;Local - Mask Common slope HISTORY_MSG_938;Local - Mask Common curve C(C) HISTORY_MSG_939;Local - Mask Common curve L(L) HISTORY_MSG_940;Local - Mask Common curve LC(H) +HISTORY_MSG_941;Local - Mask Common structure as tool +HISTORY_MSG_942;Local - Mask Common structure strength +HISTORY_MSG_943;Local - Mask Common H(H) curve HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index d98a8a6c2..c750593f8 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -965,6 +965,7 @@ enum ProcEventCode { EvlocallabHHmask_shape = 939, EvLocallabtoolmask = 940, Evlocallabstrumaskmask = 941, + EvlocallabHHhmask_shape = 942, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 1bcec4de2..fd2499ea7 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3791,7 +3791,22 @@ LocallabParams::LocallabSpot::LocallabSpot() : lapmask(0.0), chromask(0.0), gammask(1.0), - slopmask(0.0) + slopmask(0.0), + HHhmask_curve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 0.50, + 0.5, + 0.35, + 0.35, + 1.00, + 0.5, + 0.35, + 0.35 + } { } @@ -4279,7 +4294,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && lapmask == other.lapmask && chromask == other.chromask && gammask == other.gammask - && slopmask == other.slopmask; + && slopmask == other.slopmask + && HHhmask_curve == other.HHhmask_curve; } @@ -5791,6 +5807,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->chromask, "Locallab", "Chromask_" + index_str, spot.chromask, keyFile); saveToKeyfile(!pedited || spot_edited->gammask, "Locallab", "Gammask_" + index_str, spot.gammask, keyFile); saveToKeyfile(!pedited || spot_edited->slopmask, "Locallab", "Slopmask_" + index_str, spot.slopmask, keyFile); + saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); } } } @@ -7521,6 +7538,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Chromask_" + index_str, pedited, spot.chromask, spotEdited.chromask); assignFromKeyfile(keyFile, "Locallab", "Gammask_" + index_str, pedited, spot.gammask, spotEdited.gammask); assignFromKeyfile(keyFile, "Locallab", "Slopmask_" + index_str, pedited, spot.slopmask, spotEdited.slopmask); + assignFromKeyfile(keyFile, "Locallab", "HHhmask_Curve_" + index_str, pedited, spot.HHhmask_curve, spotEdited.HHhmask_curve); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 79c81e63e..ab785df76 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1438,6 +1438,7 @@ struct LocallabParams { double chromask; double gammask; double slopmask; + std::vector HHhmask_curve; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 29417c059..e0ce25926 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -968,7 +968,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabLLmask_shape LUMINANCECURVE, // EvlocallabHHmask_shape LUMINANCECURVE, // EvLocallabtoolmask - LUMINANCECURVE // Evlocallabstrumaskmask + LUMINANCECURVE, // Evlocallabstrumaskmask + LUMINANCECURVE //EvlocallabHHhmask_shape }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 91f7d4989..ee000da56 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1221,6 +1221,8 @@ private: Adjuster* const chromask; Adjuster* const gammask; Adjuster* const slopmask; + CurveEditorGroup* const mask_HCurveEditorG; + FlatCurveEditor* const HHhmask_shape; sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index f3448de71..384134fd8 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4747,7 +4747,10 @@ LocallabMask::LocallabMask(): lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), - slopmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))) + slopmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + + mask_HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), + HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))) { // Parameter Mask common specific widgets @@ -4791,7 +4794,16 @@ LocallabMask::LocallabMask(): chromask->setAdjusterListener(this); gammask->setAdjusterListener(this); slopmask->setAdjusterListener(this); - + + mask_HCurveEditorG->setCurveListener(this); + + HHhmask_shape->setIdentityValue(0.); + HHhmask_shape->setResetCurve(FlatCurveType(defSpot.HHhmask_curve.at(0)), defSpot.HHhmask_curve); + HHhmask_shape->setCurveColorProvider(this, 2); + HHhmask_shape->setBottomBarColorProvider(this, 2); + + mask_HCurveEditorG->curveListComplete(); + pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); @@ -4816,6 +4828,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*chromask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*gammask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*slopmask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*mask_HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); pack_start(*maskmaskBox); @@ -4851,6 +4864,9 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); struFrame->set_tooltip_text(M("TP_LOCALLAB_STRUMASK_TOOLTIP")); + mask_HCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_HHMASK_TOOLTIP")); + radmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); } else { exp->set_tooltip_text(M("")); @@ -4860,12 +4876,16 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) LLmask_shape->setTooltip(M("")); HHmask_shape->setTooltip(M("")); struFrame->set_tooltip_text(M("")); + mask_HCurveEditorG->set_tooltip_text(M("")); + radmask->set_tooltip_text(M("")); + lapmask->set_tooltip_text(M("")); } } LocallabMask::~LocallabMask() { delete mask_CurveEditorG; + delete mask_HCurveEditorG; } void LocallabMask::disableListener() @@ -4931,6 +4951,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params chromask->setValue(spot.chromask); gammask->setValue(spot.gammask); slopmask->setValue(spot.slopmask); + HHhmask_shape->setCurve(spot.HHhmask_curve); } @@ -4970,6 +4991,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.chromask = chromask->getValue(); spot.gammask = gammask->getValue(); spot.slopmask = slopmask->getValue(); + spot.HHhmask_curve = HHhmask_shape->getCurve(); } @@ -5010,6 +5032,7 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams chromask->setDefault(defSpot.chromask); lapmask->setDefault(defSpot.lapmask); slopmask->setDefault(defSpot.slopmask); + HHhmask_shape->setCurve(defSpot.HHhmask_curve); } @@ -5077,6 +5100,12 @@ void LocallabMask::curveChanged(CurveEditor* ce) } } + if (ce == HHhmask_shape) { + if (listener) { + listener->panelChanged(EvlocallabHHhmask_shape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 7111d1986..49ad58aac 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1494,6 +1494,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).chromask = locallab.spots.at(j).chromask && pSpot.chromask == otherSpot.chromask; locallab.spots.at(j).gammask = locallab.spots.at(j).gammask && pSpot.gammask == otherSpot.gammask; locallab.spots.at(j).slopmask = locallab.spots.at(j).slopmask && pSpot.slopmask == otherSpot.slopmask; + locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; } } @@ -4864,6 +4865,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).slopmask = mods.locallab.spots.at(i).slopmask; } + if (locallab.spots.at(i).HHhmask_curve) { + toEdit.locallab.spots.at(i).HHhmask_curve = mods.locallab.spots.at(i).HHhmask_curve; + } + } if (pcvignette.enabled) { @@ -6449,7 +6454,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : lapmask(v), chromask(v), gammask(v), - slopmask(v) + slopmask(v), + HHhmask_curve(v) { } @@ -6928,6 +6934,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) chromask = v; gammask = v; slopmask = v; + HHhmask_curve =(v); } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b177101f3..27e9887c0 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -852,6 +852,7 @@ public: bool chromask; bool gammask; bool slopmask; + bool HHhmask_curve; LocallabSpotEdited(bool v); From 0fe20b52d6f002e72008d791d6bba28b9c2f7b5a Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 17:18:39 +0200 Subject: [PATCH 014/114] Added mask common fft checkbutton --- rtdata/languages/default | 1 + rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 +++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 +- rtgui/locallabtools.h | 6 +++- rtgui/locallabtools2.cc | 59 ++++++++++++++++++++++++++++++++++++++-- rtgui/paramsedited.cc | 7 +++++ rtgui/paramsedited.h | 1 + 9 files changed, 78 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 72e9c0602..210048b8a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1180,6 +1180,7 @@ HISTORY_MSG_940;Local - Mask Common curve LC(H) HISTORY_MSG_941;Local - Mask Common structure as tool HISTORY_MSG_942;Local - Mask Common structure strength HISTORY_MSG_943;Local - Mask Common H(H) curve +HISTORY_MSG_944;Local - Mask Common FFT HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index c750593f8..879edf337 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -966,6 +966,7 @@ enum ProcEventCode { EvLocallabtoolmask = 940, Evlocallabstrumaskmask = 941, EvlocallabHHhmask_shape = 942, + EvLocallabfftmask = 943, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index fd2499ea7..059b15057 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3740,6 +3740,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : sensimask(60), blendmask(0), enamask(false), + fftmask(true), CCmask_curve{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -4285,6 +4286,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && sensimask == other.sensimask && blendmask == other.blendmask && enamask == other.enamask + && fftmask == other.fftmask && CCmask_curve == other.CCmask_curve && LLmask_curve == other.LLmask_curve && HHmask_curve == other.HHmask_curve @@ -5797,6 +5799,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); + saveToKeyfile(!pedited || spot_edited->fftmask, "Locallab", "Fftmask_" + index_str, spot.fftmask, keyFile); saveToKeyfile(!pedited || spot_edited->CCmask_curve, "Locallab", "CCmask_Curve_" + index_str, spot.CCmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->LLmask_curve, "Locallab", "LLmask_Curve_" + index_str, spot.LLmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->HHmask_curve, "Locallab", "HHmask_Curve_" + index_str, spot.HHmask_curve, keyFile); @@ -7528,6 +7531,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); + assignFromKeyfile(keyFile, "Locallab", "Fftmask_" + index_str, pedited, spot.fftmask, spotEdited.fftmask); assignFromKeyfile(keyFile, "Locallab", "CCmask_Curve_" + index_str, pedited, spot.CCmask_curve, spotEdited.CCmask_curve); assignFromKeyfile(keyFile, "Locallab", "LLmask_Curve_" + index_str, pedited, spot.LLmask_curve, spotEdited.LLmask_curve); assignFromKeyfile(keyFile, "Locallab", "HHmask_Curve_" + index_str, pedited, spot.HHmask_curve, spotEdited.HHmask_curve); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index ab785df76..c13055aa7 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1428,6 +1428,7 @@ struct LocallabParams { int sensimask; int blendmask; bool enamask; + bool fftmask; std::vector CCmask_curve; std::vector LLmask_curve; std::vector HHmask_curve; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index e0ce25926..f9a0c39d5 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -969,7 +969,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabHHmask_shape LUMINANCECURVE, // EvLocallabtoolmask LUMINANCECURVE, // Evlocallabstrumaskmask - LUMINANCECURVE //EvlocallabHHhmask_shape + LUMINANCECURVE, // EvlocallabHHhmask_shape + LUMINANCECURVE // EvLocallabfftmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index ee000da56..0eaf0d206 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1215,6 +1215,8 @@ private: Gtk::Frame* const struFrame; Adjuster* const strumaskmask; Gtk::CheckButton* const toolmask; + Gtk::Frame* const blurFrame; + Gtk::CheckButton* const fftmask; Adjuster* const radmask; Adjuster* const lapmask; @@ -1224,7 +1226,7 @@ private: CurveEditorGroup* const mask_HCurveEditorG; FlatCurveEditor* const HHhmask_shape; - sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn; + sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn, fftmaskConn; public: LocallabMask(); @@ -1253,6 +1255,8 @@ private: void toolmaskChanged(); void convertParamToNormal() override; void updateGUIToMode(const modeType new_type) override; + void fftmaskChanged(); + void updatemaskGUI3(); /* void autocomputeToggled(); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 384134fd8..40826613e 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4742,7 +4742,9 @@ LocallabMask::LocallabMask(): struFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABSTRUM")))), strumaskmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), toolmask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), - + blurFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABBLURM")))), + fftmask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTCOL_MASK")))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), @@ -4788,6 +4790,9 @@ LocallabMask::LocallabMask(): strumaskmask->setAdjusterListener(this); toolmaskConn = toolmask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::toolmaskChanged)); + blurFrame->set_label_align(0.025, 0.5); + + fftmaskConn = fftmask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::fftmaskChanged)); radmask->setAdjusterListener(this); lapmask->setAdjusterListener(this); @@ -4819,6 +4824,11 @@ LocallabMask::LocallabMask(): struFrame->add(*strumBox); maskmaskBox->pack_start(*struFrame, Gtk::PACK_SHRINK, 0); + ToolParamBlock* const blurmBox = Gtk::manage(new ToolParamBlock()); + blurmBox->pack_start(*fftmask, Gtk::PACK_SHRINK, 0); + blurFrame->add(*blurmBox); + maskmaskBox->pack_start(*blurFrame, Gtk::PACK_SHRINK, 0); + Gtk::Frame* const toolmaskFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK"))); toolmaskFrame->set_label_align(0.025, 0.5); ToolParamBlock* const toolmaskBox = Gtk::manage(new ToolParamBlock()); @@ -4894,6 +4904,7 @@ void LocallabMask::disableListener() showmaskMethodConn.block(true); enamaskConn.block(true); toolmaskConn.block(true); + fftmaskConn.block(true); } @@ -4903,6 +4914,7 @@ void LocallabMask::enableListener() showmaskMethodConn.block(false); enamaskConn.block(false); toolmaskConn.block(false); + fftmaskConn.block(false); } @@ -4936,9 +4948,11 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params exp->set_visible(spot.visimask); exp->setEnabled(spot.expmask); complexity->set_active(spot.complexmask); - - + + sensimask->setValue(spot.sensimask); + updatemaskGUI3(); + blendmask->setValue(spot.blendmask); enamask->set_active(spot.enamask); CCmask_shape->setCurve(spot.CCmask_curve); @@ -4952,6 +4966,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params gammask->setValue(spot.gammask); slopmask->setValue(spot.slopmask); HHhmask_shape->setCurve(spot.HHhmask_curve); + fftmask->set_active(spot.fftmask); } @@ -4992,6 +5007,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.gammask = gammask->getValue(); spot.slopmask = slopmask->getValue(); spot.HHhmask_curve = HHhmask_shape->getCurve(); + spot.fftmask = fftmask->get_active(); } @@ -5047,12 +5063,14 @@ void LocallabMask::updateGUIToMode(const modeType new_type) gammask->hide(); slopmask->hide(); struFrame->hide(); + blurFrame->hide(); } else { // Advanced widgets are shown in Expert mode lapmask->show(); gammask->show(); slopmask->show(); struFrame->show(); + blurFrame->show(); } } @@ -5069,12 +5087,47 @@ void LocallabMask::convertParamToNormal() slopmask->setValue(defSpot.slopmask); strumaskmask->setValue(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); + fftmask->set_active(defSpot.fftmask); // Enable all listeners enableListener(); } +void LocallabMask::updatemaskGUI3() +{ /* + const double temp = blurcol->getValue(); + + if (fftColorMask->get_active()) { + blurcol->setLimits(0.2, 1000., 0.5, 0.2); + } else { + blurcol->setLimits(0.2, 100., 0.5, 0.2); + } + + blurcol->setValue(temp); + */ +} + + + +void LocallabMask::fftmaskChanged() +{ + updatemaskGUI3(); // Update GUI according to fftmask button state + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fftmask->get_active()) { + listener->panelChanged(EvLocallabfftmask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabfftmask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + + void LocallabMask::curveChanged(CurveEditor* ce) { if (isLocActivated && exp->getEnabled()) { diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 49ad58aac..bfa727be3 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1484,6 +1484,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; + locallab.spots.at(j).fftmask = locallab.spots.at(j).fftmask && pSpot.fftmask == otherSpot.fftmask; locallab.spots.at(j).CCmask_curve = locallab.spots.at(j).CCmask_curve && pSpot.CCmask_curve == otherSpot.CCmask_curve; locallab.spots.at(j).LLmask_curve = locallab.spots.at(j).LLmask_curve && pSpot.LLmask_curve == otherSpot.LLmask_curve; locallab.spots.at(j).HHmask_curve = locallab.spots.at(j).HHmask_curve && pSpot.HHmask_curve == otherSpot.HHmask_curve; @@ -4825,6 +4826,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).enamask = mods.locallab.spots.at(i).enamask; } + if (locallab.spots.at(i).fftmask) { + toEdit.locallab.spots.at(i).fftmask = mods.locallab.spots.at(i).fftmask; + } + if (locallab.spots.at(i).CCmask_curve) { toEdit.locallab.spots.at(i).CCmask_curve = mods.locallab.spots.at(i).CCmask_curve; } @@ -6445,6 +6450,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : sensimask(v), blendmask(v), enamask(v), + fftmask(v), CCmask_curve(v), LLmask_curve(v), HHmask_curve(v), @@ -6924,6 +6930,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) sensimask = v; blendmask = v; enamask = v; + fftmask = v; CCmask_curve = v; LLmask_curve = v; HHmask_curve = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 27e9887c0..69d1e885b 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -842,6 +842,7 @@ public: bool sensimask; bool blendmask; bool enamask; + bool fftmask; bool CCmask_curve; bool LLmask_curve; bool HHmask_curve; From bc740affe8d674b0f443a9b7b19da4ac40259a43 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 17:59:58 +0200 Subject: [PATCH 015/114] Added common mask blur mask --- rtdata/languages/default | 2 ++ rtengine/procevents.h | 2 ++ rtengine/procparams.cc | 8 +++++++ rtengine/procparams.h | 2 ++ rtengine/refreshmap.cc | 4 +++- rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 49 ++++++++++++++++++++++++++++++++++------ rtgui/paramsedited.cc | 14 ++++++++++++ rtgui/paramsedited.h | 2 ++ 9 files changed, 77 insertions(+), 8 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 210048b8a..e954b156e 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1181,6 +1181,8 @@ HISTORY_MSG_941;Local - Mask Common structure as tool HISTORY_MSG_942;Local - Mask Common structure strength HISTORY_MSG_943;Local - Mask Common H(H) curve HISTORY_MSG_944;Local - Mask Common FFT +HISTORY_MSG_945;Local - Mask Common Blur radius +HISTORY_MSG_946;Local - Mask Common contrast threshold HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 879edf337..ad5cd34c4 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -967,6 +967,8 @@ enum ProcEventCode { Evlocallabstrumaskmask = 941, EvlocallabHHhmask_shape = 942, EvLocallabfftmask = 943, + Evlocallabblurmask = 944, + Evlocallabcontmask = 945, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 059b15057..591f57b13 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3741,6 +3741,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : blendmask(0), enamask(false), fftmask(true), + blurmask(0.2), + contmask(0.), CCmask_curve{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -4287,6 +4289,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && blendmask == other.blendmask && enamask == other.enamask && fftmask == other.fftmask + && blurmask == other.blurmask + && contmask == other.contmask && CCmask_curve == other.CCmask_curve && LLmask_curve == other.LLmask_curve && HHmask_curve == other.HHmask_curve @@ -5800,6 +5804,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); saveToKeyfile(!pedited || spot_edited->fftmask, "Locallab", "Fftmask_" + index_str, spot.fftmask, keyFile); + saveToKeyfile(!pedited || spot_edited->blurmask, "Locallab", "Blurmask_" + index_str, spot.blurmask, keyFile); + saveToKeyfile(!pedited || spot_edited->contmask, "Locallab", "Contmask_" + index_str, spot.contmask, keyFile); saveToKeyfile(!pedited || spot_edited->CCmask_curve, "Locallab", "CCmask_Curve_" + index_str, spot.CCmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->LLmask_curve, "Locallab", "LLmask_Curve_" + index_str, spot.LLmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->HHmask_curve, "Locallab", "HHmask_Curve_" + index_str, spot.HHmask_curve, keyFile); @@ -7532,6 +7538,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); assignFromKeyfile(keyFile, "Locallab", "Fftmask_" + index_str, pedited, spot.fftmask, spotEdited.fftmask); + assignFromKeyfile(keyFile, "Locallab", "Blurmask_" + index_str, pedited, spot.blurmask, spotEdited.blurmask); + assignFromKeyfile(keyFile, "Locallab", "Contmask_" + index_str, pedited, spot.contmask, spotEdited.contmask); assignFromKeyfile(keyFile, "Locallab", "CCmask_Curve_" + index_str, pedited, spot.CCmask_curve, spotEdited.CCmask_curve); assignFromKeyfile(keyFile, "Locallab", "LLmask_Curve_" + index_str, pedited, spot.LLmask_curve, spotEdited.LLmask_curve); assignFromKeyfile(keyFile, "Locallab", "HHmask_Curve_" + index_str, pedited, spot.HHmask_curve, spotEdited.HHmask_curve); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index c13055aa7..a0e5719f0 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1429,6 +1429,8 @@ struct LocallabParams { int blendmask; bool enamask; bool fftmask; + double blurmask; + double contmask; std::vector CCmask_curve; std::vector LLmask_curve; std::vector HHmask_curve; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index f9a0c39d5..d2459851d 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -970,7 +970,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvLocallabtoolmask LUMINANCECURVE, // Evlocallabstrumaskmask LUMINANCECURVE, // EvlocallabHHhmask_shape - LUMINANCECURVE // EvLocallabfftmask + LUMINANCECURVE, // EvLocallabfftmask + LUMINANCECURVE, // Evlocallabblurmask + LUMINANCECURVE // Evlocallabcontmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 0eaf0d206..fe3a3df79 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1217,6 +1217,8 @@ private: Gtk::CheckButton* const toolmask; Gtk::Frame* const blurFrame; Gtk::CheckButton* const fftmask; + Adjuster* const contmask; + Adjuster* const blurmask; Adjuster* const radmask; Adjuster* const lapmask; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 40826613e..60c2c9707 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4744,6 +4744,8 @@ LocallabMask::LocallabMask(): toolmask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), blurFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABBLURM")))), fftmask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTCOL_MASK")))), + contmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), + blurmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), @@ -4793,6 +4795,9 @@ LocallabMask::LocallabMask(): blurFrame->set_label_align(0.025, 0.5); fftmaskConn = fftmask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::fftmaskChanged)); + contmask->setAdjusterListener(this); + + blurmask->setAdjusterListener(this); radmask->setAdjusterListener(this); lapmask->setAdjusterListener(this); @@ -4826,6 +4831,8 @@ LocallabMask::LocallabMask(): ToolParamBlock* const blurmBox = Gtk::manage(new ToolParamBlock()); blurmBox->pack_start(*fftmask, Gtk::PACK_SHRINK, 0); + blurmBox->pack_start(*contmask); + blurmBox->pack_start(*blurmask); blurFrame->add(*blurmBox); maskmaskBox->pack_start(*blurFrame, Gtk::PACK_SHRINK, 0); @@ -4951,7 +4958,9 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params sensimask->setValue(spot.sensimask); + contmask->setValue(spot.contmask); updatemaskGUI3(); + blurmask->setValue(spot.blurmask); blendmask->setValue(spot.blendmask); enamask->set_active(spot.enamask); @@ -5008,6 +5017,8 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.slopmask = slopmask->getValue(); spot.HHhmask_curve = HHhmask_shape->getCurve(); spot.fftmask = fftmask->get_active(); + spot.contmask = contmask->getValue(); + spot.blurmask = blurmask->getValue(); } @@ -5049,6 +5060,8 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams lapmask->setDefault(defSpot.lapmask); slopmask->setDefault(defSpot.slopmask); HHhmask_shape->setCurve(defSpot.HHhmask_curve); + contmask->setDefault(defSpot.contmask); + blurmask->setDefault(defSpot.blurmask); } @@ -5064,6 +5077,8 @@ void LocallabMask::updateGUIToMode(const modeType new_type) slopmask->hide(); struFrame->hide(); blurFrame->hide(); + mask_HCurveEditorG->hide(); + } else { // Advanced widgets are shown in Expert mode lapmask->show(); @@ -5071,6 +5086,8 @@ void LocallabMask::updateGUIToMode(const modeType new_type) slopmask->show(); struFrame->show(); blurFrame->show(); + mask_HCurveEditorG->show(); + } } @@ -5088,6 +5105,9 @@ void LocallabMask::convertParamToNormal() strumaskmask->setValue(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); fftmask->set_active(defSpot.fftmask); + blurmask->setValue(defSpot.blurmask); + contmask->setValue(defSpot.contmask); + HHhmask_shape->setCurve(defSpot.HHhmask_curve); // Enable all listeners enableListener(); @@ -5095,17 +5115,17 @@ void LocallabMask::convertParamToNormal() } void LocallabMask::updatemaskGUI3() -{ /* - const double temp = blurcol->getValue(); +{ + const double temp = blurmask->getValue(); - if (fftColorMask->get_active()) { - blurcol->setLimits(0.2, 1000., 0.5, 0.2); + if (fftmask->get_active()) { + blurmask->setLimits(0.2, 1000., 0.5, 0.2); } else { - blurcol->setLimits(0.2, 100., 0.5, 0.2); + blurmask->setLimits(0.2, 100., 0.5, 0.2); } - blurcol->setValue(temp); - */ + blurmask->setValue(temp); + } @@ -5183,6 +5203,21 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == contmask) { + if (listener) { + listener->panelChanged(Evlocallabcontmask, + contmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurmask) { + if (listener) { + listener->panelChanged(Evlocallabblurmask, + blurmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmask) { if (listener) { listener->panelChanged(Evlocallabblendmask, diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index bfa727be3..0f88e8ec9 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1485,6 +1485,8 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; locallab.spots.at(j).fftmask = locallab.spots.at(j).fftmask && pSpot.fftmask == otherSpot.fftmask; + locallab.spots.at(j).blurmask = locallab.spots.at(j).blurmask && pSpot.blurmask == otherSpot.blurmask; + locallab.spots.at(j).contmask = locallab.spots.at(j).contmask && pSpot.contmask == otherSpot.contmask; locallab.spots.at(j).CCmask_curve = locallab.spots.at(j).CCmask_curve && pSpot.CCmask_curve == otherSpot.CCmask_curve; locallab.spots.at(j).LLmask_curve = locallab.spots.at(j).LLmask_curve && pSpot.LLmask_curve == otherSpot.LLmask_curve; locallab.spots.at(j).HHmask_curve = locallab.spots.at(j).HHmask_curve && pSpot.HHmask_curve == otherSpot.HHmask_curve; @@ -4830,6 +4832,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).fftmask = mods.locallab.spots.at(i).fftmask; } + if (locallab.spots.at(i).blurmask) { + toEdit.locallab.spots.at(i).blurmask = mods.locallab.spots.at(i).blurmask; + } + + if (locallab.spots.at(i).contmask) { + toEdit.locallab.spots.at(i).contmask = mods.locallab.spots.at(i).contmask; + } + if (locallab.spots.at(i).CCmask_curve) { toEdit.locallab.spots.at(i).CCmask_curve = mods.locallab.spots.at(i).CCmask_curve; } @@ -6451,6 +6461,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : blendmask(v), enamask(v), fftmask(v), + blurmask(v), + contmask(v), CCmask_curve(v), LLmask_curve(v), HHmask_curve(v), @@ -6931,6 +6943,8 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) blendmask = v; enamask = v; fftmask = v; + blurmask = v; + contmask = v; CCmask_curve = v; LLmask_curve = v; HHmask_curve = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 69d1e885b..bd76fc8c7 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -843,6 +843,8 @@ public: bool blendmask; bool enamask; bool fftmask; + bool blurmask; + bool contmask; bool CCmask_curve; bool LLmask_curve; bool HHmask_curve; From cddf5bd395f6d56c7bf79c69a8aecde231309f4b Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Mon, 22 Jun 2020 19:10:23 +0200 Subject: [PATCH 016/114] Exclude_Local(): Speedup --- rtengine/iplocallab.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index edfdcac0a..8d1d0e0b8 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -47,7 +47,7 @@ #include "cplx_wavelet_dec.h" #include "ciecam02.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" #include "guidedfilter.h" @@ -4601,10 +4601,7 @@ void ImProcFunctions::Exclude_Local(float **deltaso, float hueref, float chromar const bool isZone0x = lox > (lp.xc + lp.lx - 1) || lox < lp.xc - lp.lxL; // -1 fix issue 5554 if (isZone0x) { // outside selection and outside transition zone => no effect, keep original values - for (int x = 0; x < transformed->W; x++) { - transformed->L[y][x] = original->L[y][x]; - } - + transformed->L[y][x] = original->L[y][x]; continue; } From 789bb3471835fea2218bdb11ab550b6d92057b44 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 19:50:02 +0200 Subject: [PATCH 017/114] Added common mask shadows --- rtdata/languages/default | 1 + rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 ++++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/locallabtools.cc | 1 + rtgui/locallabtools.h | 1 + rtgui/locallabtools2.cc | 16 ++++++++++++++++ rtgui/paramsedited.cc | 7 +++++++ rtgui/paramsedited.h | 1 + 10 files changed, 35 insertions(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index e954b156e..c8e7a7bf4 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1183,6 +1183,7 @@ HISTORY_MSG_943;Local - Mask Common H(H) curve HISTORY_MSG_944;Local - Mask Common FFT HISTORY_MSG_945;Local - Mask Common Blur radius HISTORY_MSG_946;Local - Mask Common contrast threshold +HISTORY_MSG_947;Local - Mask Common shadows HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index ad5cd34c4..9ed965ccd 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -969,6 +969,7 @@ enum ProcEventCode { EvLocallabfftmask = 943, Evlocallabblurmask = 944, Evlocallabcontmask = 945, + Evlocallabshadmask = 946, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 591f57b13..20a976bde 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3795,6 +3795,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : chromask(0.0), gammask(1.0), slopmask(0.0), + shadmask(0.0), HHhmask_curve{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -4301,6 +4302,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && chromask == other.chromask && gammask == other.gammask && slopmask == other.slopmask + && shadmask == other.shadmask && HHhmask_curve == other.HHhmask_curve; } @@ -5816,6 +5818,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->chromask, "Locallab", "Chromask_" + index_str, spot.chromask, keyFile); saveToKeyfile(!pedited || spot_edited->gammask, "Locallab", "Gammask_" + index_str, spot.gammask, keyFile); saveToKeyfile(!pedited || spot_edited->slopmask, "Locallab", "Slopmask_" + index_str, spot.slopmask, keyFile); + saveToKeyfile(!pedited || spot_edited->shadmask, "Locallab", "Shadmask_" + index_str, spot.shadmask, keyFile); saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); } } @@ -7550,6 +7553,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Chromask_" + index_str, pedited, spot.chromask, spotEdited.chromask); assignFromKeyfile(keyFile, "Locallab", "Gammask_" + index_str, pedited, spot.gammask, spotEdited.gammask); assignFromKeyfile(keyFile, "Locallab", "Slopmask_" + index_str, pedited, spot.slopmask, spotEdited.slopmask); + assignFromKeyfile(keyFile, "Locallab", "Shadmask_" + index_str, pedited, spot.shadmask, spotEdited.shadmask); assignFromKeyfile(keyFile, "Locallab", "HHhmask_Curve_" + index_str, pedited, spot.HHhmask_curve, spotEdited.HHhmask_curve); if (spot.visimask) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index a0e5719f0..0079433cc 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1441,6 +1441,7 @@ struct LocallabParams { double chromask; double gammask; double slopmask; + double shadmask; std::vector HHhmask_curve; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index d2459851d..7ddd1fc4c 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -972,7 +972,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabHHhmask_shape LUMINANCECURVE, // EvLocallabfftmask LUMINANCECURVE, // Evlocallabblurmask - LUMINANCECURVE // Evlocallabcontmask + LUMINANCECURVE, // Evlocallabcontmask + LUMINANCECURVE // Evlocallabshadmask }; diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 13415cbe5..35050176b 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -1769,6 +1769,7 @@ void LocallabColor::convertParamToNormal() toolcol->set_active(defSpot.toolcol); fftColorMask->set_active(defSpot.fftColorMask); contcol->setValue(defSpot.contcol); + blurcol->setValue(defSpot.blurcol); lapmaskcol->setValue(defSpot.lapmaskcol); gammaskcol->setValue(defSpot.gammaskcol); slomaskcol->setValue(defSpot.slomaskcol); diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index fe3a3df79..b65ac707b 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1225,6 +1225,7 @@ private: Adjuster* const chromask; Adjuster* const gammask; Adjuster* const slopmask; + Adjuster* const shadmask; CurveEditorGroup* const mask_HCurveEditorG; FlatCurveEditor* const HHhmask_shape; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 60c2c9707..2246f9bea 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4752,6 +4752,7 @@ LocallabMask::LocallabMask(): chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), slopmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), mask_HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))) @@ -4804,6 +4805,7 @@ LocallabMask::LocallabMask(): chromask->setAdjusterListener(this); gammask->setAdjusterListener(this); slopmask->setAdjusterListener(this); + shadmask->setAdjusterListener(this); mask_HCurveEditorG->setCurveListener(this); @@ -4845,6 +4847,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*chromask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*gammask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*slopmask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*shadmask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*mask_HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); @@ -4974,6 +4977,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params chromask->setValue(spot.chromask); gammask->setValue(spot.gammask); slopmask->setValue(spot.slopmask); + shadmask->setValue(spot.shadmask); HHhmask_shape->setCurve(spot.HHhmask_curve); fftmask->set_active(spot.fftmask); @@ -5015,6 +5019,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.chromask = chromask->getValue(); spot.gammask = gammask->getValue(); spot.slopmask = slopmask->getValue(); + spot.shadmask = shadmask->getValue(); spot.HHhmask_curve = HHhmask_shape->getCurve(); spot.fftmask = fftmask->get_active(); spot.contmask = contmask->getValue(); @@ -5059,6 +5064,7 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams chromask->setDefault(defSpot.chromask); lapmask->setDefault(defSpot.lapmask); slopmask->setDefault(defSpot.slopmask); + shadmask->setDefault(defSpot.shadmask); HHhmask_shape->setCurve(defSpot.HHhmask_curve); contmask->setDefault(defSpot.contmask); blurmask->setDefault(defSpot.blurmask); @@ -5075,6 +5081,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) lapmask->hide(); gammask->hide(); slopmask->hide(); + shadmask->hide(); struFrame->hide(); blurFrame->hide(); mask_HCurveEditorG->hide(); @@ -5084,6 +5091,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) lapmask->show(); gammask->show(); slopmask->show(); + shadmask->show(); struFrame->show(); blurFrame->show(); mask_HCurveEditorG->show(); @@ -5102,6 +5110,7 @@ void LocallabMask::convertParamToNormal() lapmask->setValue(defSpot.lapmask); gammask->setValue(defSpot.gammask); slopmask->setValue(defSpot.slopmask); + shadmask->setValue(defSpot.shadmask); strumaskmask->setValue(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); fftmask->set_active(defSpot.fftmask); @@ -5260,6 +5269,13 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == shadmask) { + if (listener) { + listener->panelChanged(Evlocallabshadmask, + shadmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 0f88e8ec9..9fccd0637 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1497,6 +1497,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).chromask = locallab.spots.at(j).chromask && pSpot.chromask == otherSpot.chromask; locallab.spots.at(j).gammask = locallab.spots.at(j).gammask && pSpot.gammask == otherSpot.gammask; locallab.spots.at(j).slopmask = locallab.spots.at(j).slopmask && pSpot.slopmask == otherSpot.slopmask; + locallab.spots.at(j).shadmask = locallab.spots.at(j).shadmask && pSpot.shadmask == otherSpot.shadmask; locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; } } @@ -4880,6 +4881,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).slopmask = mods.locallab.spots.at(i).slopmask; } + if (locallab.spots.at(i).shadmask) { + toEdit.locallab.spots.at(i).shadmask = mods.locallab.spots.at(i).shadmask; + } + if (locallab.spots.at(i).HHhmask_curve) { toEdit.locallab.spots.at(i).HHhmask_curve = mods.locallab.spots.at(i).HHhmask_curve; } @@ -6473,6 +6478,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : chromask(v), gammask(v), slopmask(v), + shadmask(v), HHhmask_curve(v) { @@ -6955,6 +6961,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) chromask = v; gammask = v; slopmask = v; + shadmask = v; HHhmask_curve =(v); } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index bd76fc8c7..52893d34d 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -855,6 +855,7 @@ public: bool chromask; bool gammask; bool slopmask; + bool shadmask; bool HHhmask_curve; LocallabSpotEdited(bool v); From 476ea4861ad6d438bc171d09bef1fabe34c050fc Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 22:05:40 +0200 Subject: [PATCH 018/114] Added common mask contrast curve --- rtdata/languages/default | 1 + rtengine/procevents.h | 1 + rtengine/procparams.cc | 13 +++++++++++- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 7 +++--- rtgui/locallabtools.h | 11 +++------- rtgui/locallabtools2.cc | 46 +++++++++++++++++++++++++++++++++++++++- rtgui/paramsedited.cc | 9 +++++++- rtgui/paramsedited.h | 1 + 9 files changed, 76 insertions(+), 14 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index c8e7a7bf4..1611444a3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1184,6 +1184,7 @@ HISTORY_MSG_944;Local - Mask Common FFT HISTORY_MSG_945;Local - Mask Common Blur radius HISTORY_MSG_946;Local - Mask Common contrast threshold HISTORY_MSG_947;Local - Mask Common shadows +HISTORY_MSG_948;Local - Mask Common Contrast curve HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 9ed965ccd..01fb707d3 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -970,6 +970,7 @@ enum ProcEventCode { Evlocallabblurmask = 944, Evlocallabcontmask = 945, Evlocallabshadmask = 946, + EvlocallabLmask_shape = 947, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 20a976bde..0a8b6ff65 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3810,7 +3810,15 @@ LocallabParams::LocallabSpot::LocallabSpot() : 0.5, 0.35, 0.35 + }, + Lmask_curve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 } + { } @@ -4303,7 +4311,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && gammask == other.gammask && slopmask == other.slopmask && shadmask == other.shadmask - && HHhmask_curve == other.HHhmask_curve; + && HHhmask_curve == other.HHhmask_curve + && Lmask_curve == other.Lmask_curve; } @@ -5820,6 +5829,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->slopmask, "Locallab", "Slopmask_" + index_str, spot.slopmask, keyFile); saveToKeyfile(!pedited || spot_edited->shadmask, "Locallab", "Shadmask_" + index_str, spot.shadmask, keyFile); saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmask_curve, "Locallab", "Lmask_Curve_" + index_str, spot.Lmask_curve, keyFile); } } } @@ -7555,6 +7565,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Slopmask_" + index_str, pedited, spot.slopmask, spotEdited.slopmask); assignFromKeyfile(keyFile, "Locallab", "Shadmask_" + index_str, pedited, spot.shadmask, spotEdited.shadmask); assignFromKeyfile(keyFile, "Locallab", "HHhmask_Curve_" + index_str, pedited, spot.HHhmask_curve, spotEdited.HHhmask_curve); + assignFromKeyfile(keyFile, "Locallab", "Lmask_Curve_" + index_str, pedited, spot.Lmask_curve, spotEdited.Lmask_curve); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 0079433cc..258d69cf1 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1443,6 +1443,7 @@ struct LocallabParams { double slopmask; double shadmask; std::vector HHhmask_curve; + std::vector Lmask_curve; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 7ddd1fc4c..f9fae6873 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -970,10 +970,11 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvLocallabtoolmask LUMINANCECURVE, // Evlocallabstrumaskmask LUMINANCECURVE, // EvlocallabHHhmask_shape - LUMINANCECURVE, // EvLocallabfftmask + LUMINANCECURVE, // EvLocallabfftmask LUMINANCECURVE, // Evlocallabblurmask - LUMINANCECURVE, // Evlocallabcontmask - LUMINANCECURVE // Evlocallabshadmask + LUMINANCECURVE, // Evlocallabcontmask + LUMINANCECURVE, // Evlocallabshadmask + LUMINANCECURVE // EvlocallabLmask_shape }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index b65ac707b..9fa353fec 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1228,6 +1228,8 @@ private: Adjuster* const shadmask; CurveEditorGroup* const mask_HCurveEditorG; FlatCurveEditor* const HHhmask_shape; + CurveEditorGroup* const mask2CurveEditorG; + DiagonalCurveEditor* const Lmask_shape; sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn, fftmaskConn; @@ -1260,14 +1262,7 @@ private: void updateGUIToMode(const modeType new_type) override; void fftmaskChanged(); void updatemaskGUI3(); - - /* - void autocomputeToggled(); - void fullimageChanged(); - void AutograyChanged(); - - void updateLogGUI(); - */ + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; }; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 2246f9bea..003bb96bf 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4755,7 +4755,9 @@ LocallabMask::LocallabMask(): shadmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), mask_HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), - HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))) + HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))), + mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))) { // Parameter Mask common specific widgets @@ -4816,6 +4818,14 @@ LocallabMask::LocallabMask(): mask_HCurveEditorG->curveListComplete(); + mask2CurveEditorG->setCurveListener(this); + + Lmask_shape->setResetCurve(DiagonalCurveType(defSpot.Lmask_curve.at(0)), defSpot.Lmask_curve); + Lmask_shape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmask_shape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2CurveEditorG->curveListComplete(); + pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); @@ -4849,6 +4859,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*slopmask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*shadmask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*mask_HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolmaskBox->pack_start(*mask2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); pack_start(*maskmaskBox); @@ -4887,6 +4898,8 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) mask_HCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_HHMASK_TOOLTIP")); radmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); lapmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + mask2CurveEditorG->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); + Lmask_shape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); } else { exp->set_tooltip_text(M("")); @@ -4899,6 +4912,8 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) mask_HCurveEditorG->set_tooltip_text(M("")); radmask->set_tooltip_text(M("")); lapmask->set_tooltip_text(M("")); + mask2CurveEditorG->set_tooltip_text(M("")); + Lmask_shape->setTooltip(M("")); } } @@ -4906,6 +4921,7 @@ LocallabMask::~LocallabMask() { delete mask_CurveEditorG; delete mask_HCurveEditorG; + delete mask2CurveEditorG; } void LocallabMask::disableListener() @@ -4980,6 +4996,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params shadmask->setValue(spot.shadmask); HHhmask_shape->setCurve(spot.HHhmask_curve); fftmask->set_active(spot.fftmask); + Lmask_shape->setCurve(spot.Lmask_curve); } @@ -5024,6 +5041,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.fftmask = fftmask->get_active(); spot.contmask = contmask->getValue(); spot.blurmask = blurmask->getValue(); + spot.Lmask_curve = Lmask_shape->getCurve(); } @@ -5085,6 +5103,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) struFrame->hide(); blurFrame->hide(); mask_HCurveEditorG->hide(); +// mask2CurveEditorG->hide(); } else { // Advanced widgets are shown in Expert mode @@ -5095,6 +5114,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) struFrame->show(); blurFrame->show(); mask_HCurveEditorG->show(); +// mask2CurveEditorG->show(); } } @@ -5117,6 +5137,7 @@ void LocallabMask::convertParamToNormal() blurmask->setValue(defSpot.blurmask); contmask->setValue(defSpot.contmask); HHhmask_shape->setCurve(defSpot.HHhmask_curve); +// Lmask_shape->setCurve(defSpot.Lmask_curve); // Enable all listeners enableListener(); @@ -5137,6 +5158,22 @@ void LocallabMask::updatemaskGUI3() } +void LocallabMask::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmask_shape->updateLocallabBackground(normChromar); + LLmask_shape->updateLocallabBackground(normLumar); + HHmask_shape->updateLocallabBackground(normHuer); + HHhmask_shape->updateLocallabBackground(normHuer); + + return false; + } + ); +} void LocallabMask::fftmaskChanged() @@ -5189,6 +5226,13 @@ void LocallabMask::curveChanged(CurveEditor* ce) } } + if (ce == Lmask_shape) { + if (listener) { + listener->panelChanged(EvlocallabLmask_shape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 9fccd0637..0927b8ff5 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1499,6 +1499,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).slopmask = locallab.spots.at(j).slopmask && pSpot.slopmask == otherSpot.slopmask; locallab.spots.at(j).shadmask = locallab.spots.at(j).shadmask && pSpot.shadmask == otherSpot.shadmask; locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; + locallab.spots.at(j).Lmask_curve = locallab.spots.at(j).Lmask_curve && pSpot.Lmask_curve == otherSpot.Lmask_curve; } } @@ -4889,6 +4890,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).HHhmask_curve = mods.locallab.spots.at(i).HHhmask_curve; } + if (locallab.spots.at(i).Lmask_curve) { + toEdit.locallab.spots.at(i).Lmask_curve = mods.locallab.spots.at(i).Lmask_curve; + } + } if (pcvignette.enabled) { @@ -6479,7 +6484,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : gammask(v), slopmask(v), shadmask(v), - HHhmask_curve(v) + HHhmask_curve(v), + Lmask_curve(v) { } @@ -6963,6 +6969,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) slopmask = v; shadmask = v; HHhmask_curve =(v); + Lmask_curve =(v); } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 52893d34d..b2c933afa 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -857,6 +857,7 @@ public: bool slopmask; bool shadmask; bool HHhmask_curve; + bool Lmask_curve; LocallabSpotEdited(bool v); From e3bed2c92034c4dee802178e5ba7e55cc4d3ad9f Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 22 Jun 2020 22:36:02 +0200 Subject: [PATCH 019/114] Added common mask wavelet curve --- rtengine/procevents.h | 1 + rtengine/procparams.cc | 16 +++++++++++++++- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 34 +++++++++++++++++++++++++++++++--- rtgui/paramsedited.cc | 9 ++++++++- rtgui/paramsedited.h | 1 + 8 files changed, 61 insertions(+), 6 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 01fb707d3..7fe72099f 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -971,6 +971,7 @@ enum ProcEventCode { Evlocallabcontmask = 945, Evlocallabshadmask = 946, EvlocallabLmask_shape = 947, + EvlocallabLLmask_shapewav = 948, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 0a8b6ff65..7fdf1e673 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3817,6 +3817,17 @@ LocallabParams::LocallabSpot::LocallabSpot() : 0.0, 1.0, 1.0 + }, + LLmask_curvewav{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 1., + 0.5, + 0.35, + 0.35 } @@ -4312,7 +4323,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && slopmask == other.slopmask && shadmask == other.shadmask && HHhmask_curve == other.HHhmask_curve - && Lmask_curve == other.Lmask_curve; + && Lmask_curve == other.Lmask_curve + && LLmask_curvewav == other.LLmask_curvewav; } @@ -5830,6 +5842,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->shadmask, "Locallab", "Shadmask_" + index_str, spot.shadmask, keyFile); saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->Lmask_curve, "Locallab", "Lmask_Curve_" + index_str, spot.Lmask_curve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmask_curvewav, "Locallab", "LLmask_Curvewav_" + index_str, spot.LLmask_curvewav, keyFile); } } } @@ -7566,6 +7579,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Shadmask_" + index_str, pedited, spot.shadmask, spotEdited.shadmask); assignFromKeyfile(keyFile, "Locallab", "HHhmask_Curve_" + index_str, pedited, spot.HHhmask_curve, spotEdited.HHhmask_curve); assignFromKeyfile(keyFile, "Locallab", "Lmask_Curve_" + index_str, pedited, spot.Lmask_curve, spotEdited.Lmask_curve); + assignFromKeyfile(keyFile, "Locallab", "LLmask_Curvewav_" + index_str, pedited, spot.LLmask_curvewav, spotEdited.LLmask_curvewav); if (spot.visimask) { spotEdited.visimask = true; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 258d69cf1..ecde2d41a 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1444,6 +1444,7 @@ struct LocallabParams { double shadmask; std::vector HHhmask_curve; std::vector Lmask_curve; + std::vector LLmask_curvewav; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index f9fae6873..11ea7d5d6 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -974,7 +974,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabblurmask LUMINANCECURVE, // Evlocallabcontmask LUMINANCECURVE, // Evlocallabshadmask - LUMINANCECURVE // EvlocallabLmask_shape + LUMINANCECURVE, // EvlocallabLmask_shape + LUMINANCECURVE // EvlocallabLLmask_shapewav }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 9fa353fec..473ebf8be 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1230,6 +1230,8 @@ private: FlatCurveEditor* const HHhmask_shape; CurveEditorGroup* const mask2CurveEditorG; DiagonalCurveEditor* const Lmask_shape; + CurveEditorGroup* const mask2CurveEditorGwav; + FlatCurveEditor* const LLmask_shapewav; sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn, fftmaskConn; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 003bb96bf..2556c248a 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4757,7 +4757,9 @@ LocallabMask::LocallabMask(): mask_HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))), mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), - Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))) + Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmask_shapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))) { // Parameter Mask common specific widgets @@ -4826,6 +4828,14 @@ LocallabMask::LocallabMask(): mask2CurveEditorG->curveListComplete(); + mask2CurveEditorGwav->setCurveListener(this); + + LLmask_shapewav->setIdentityValue(0.); + LLmask_shapewav->setResetCurve(FlatCurveType(defSpot.LLmask_curvewav.at(0)), defSpot.LLmask_curvewav); + LLmask_shapewav->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2CurveEditorGwav->curveListComplete(); + pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); @@ -4860,6 +4870,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*shadmask, Gtk::PACK_SHRINK, 0); toolmaskBox->pack_start(*mask_HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskBox->pack_start(*mask2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolmaskBox->pack_start(*mask2CurveEditorGwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); pack_start(*maskmaskBox); @@ -4900,6 +4911,8 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) lapmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); mask2CurveEditorG->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); Lmask_shape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + mask2CurveEditorGwav->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); + LLmask_shapewav->setTooltip(M("TP_LOCALLAB_LMASK_LEVEL_TOOLTIP")); } else { exp->set_tooltip_text(M("")); @@ -4914,6 +4927,8 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) lapmask->set_tooltip_text(M("")); mask2CurveEditorG->set_tooltip_text(M("")); Lmask_shape->setTooltip(M("")); + mask2CurveEditorGwav->set_tooltip_text(M("")); + LLmask_shapewav->setTooltip(M("")); } } @@ -4922,6 +4937,7 @@ LocallabMask::~LocallabMask() delete mask_CurveEditorG; delete mask_HCurveEditorG; delete mask2CurveEditorG; + delete mask2CurveEditorGwav; } void LocallabMask::disableListener() @@ -4997,6 +5013,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params HHhmask_shape->setCurve(spot.HHhmask_curve); fftmask->set_active(spot.fftmask); Lmask_shape->setCurve(spot.Lmask_curve); + LLmask_shapewav->setCurve(spot.LLmask_curvewav); } @@ -5042,6 +5059,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.contmask = contmask->getValue(); spot.blurmask = blurmask->getValue(); spot.Lmask_curve = Lmask_shape->getCurve(); + spot.LLmask_curvewav = LLmask_shapewav->getCurve(); } @@ -5104,7 +5122,8 @@ void LocallabMask::updateGUIToMode(const modeType new_type) blurFrame->hide(); mask_HCurveEditorG->hide(); // mask2CurveEditorG->hide(); - + mask2CurveEditorGwav->hide(); + } else { // Advanced widgets are shown in Expert mode lapmask->show(); @@ -5115,7 +5134,8 @@ void LocallabMask::updateGUIToMode(const modeType new_type) blurFrame->show(); mask_HCurveEditorG->show(); // mask2CurveEditorG->show(); - + mask2CurveEditorGwav->show(); + } } @@ -5138,6 +5158,7 @@ void LocallabMask::convertParamToNormal() contmask->setValue(defSpot.contmask); HHhmask_shape->setCurve(defSpot.HHhmask_curve); // Lmask_shape->setCurve(defSpot.Lmask_curve); + LLmask_shapewav->setCurve(defSpot.LLmask_curvewav); // Enable all listeners enableListener(); @@ -5233,6 +5254,13 @@ void LocallabMask::curveChanged(CurveEditor* ce) } } + if (ce == LLmask_shapewav) { + if (listener) { + listener->panelChanged(EvlocallabLLmask_shapewav, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 0927b8ff5..407e67064 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1500,6 +1500,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).shadmask = locallab.spots.at(j).shadmask && pSpot.shadmask == otherSpot.shadmask; locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; locallab.spots.at(j).Lmask_curve = locallab.spots.at(j).Lmask_curve && pSpot.Lmask_curve == otherSpot.Lmask_curve; + locallab.spots.at(j).LLmask_curvewav = locallab.spots.at(j).LLmask_curvewav && pSpot.LLmask_curvewav == otherSpot.LLmask_curvewav; } } @@ -4894,6 +4895,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).Lmask_curve = mods.locallab.spots.at(i).Lmask_curve; } + if (locallab.spots.at(i).LLmask_curvewav) { + toEdit.locallab.spots.at(i).LLmask_curvewav = mods.locallab.spots.at(i).LLmask_curvewav; + } + } if (pcvignette.enabled) { @@ -6485,7 +6490,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : slopmask(v), shadmask(v), HHhmask_curve(v), - Lmask_curve(v) + Lmask_curve(v), + LLmask_curvewav(v) { } @@ -6970,6 +6976,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) shadmask = v; HHhmask_curve =(v); Lmask_curve =(v); + LLmask_curvewav =(v); } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b2c933afa..7f82e128a 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -858,6 +858,7 @@ public: bool shadmask; bool HHhmask_curve; bool Lmask_curve; + bool LLmask_curvewav; LocallabSpotEdited(bool v); From f495be9935a61ffccd875236382f7c3b72a2b802 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 06:55:48 +0200 Subject: [PATCH 020/114] Added common mask threshold level --- rtdata/languages/default | 2 ++ rtengine/procevents.h | 2 ++ rtengine/procparams.cc | 18 +++++++++++++++--- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 4 +++- rtgui/locallabtools.h | 11 ++++++++--- rtgui/locallabtools2.cc | 25 ++++++++++++++++++++++++- rtgui/paramsedited.cc | 9 ++++++++- rtgui/paramsedited.h | 1 + 9 files changed, 64 insertions(+), 9 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 1611444a3..747602045 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1185,6 +1185,8 @@ HISTORY_MSG_945;Local - Mask Common Blur radius HISTORY_MSG_946;Local - Mask Common contrast threshold HISTORY_MSG_947;Local - Mask Common shadows HISTORY_MSG_948;Local - Mask Common Contrast curve +HISTORY_MSG_949;Local - Mask Common Wavelet curve +HISTORY_MSG_950;Local - Mask Common Threshold levels HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 7fe72099f..fe429d7d8 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -972,6 +972,8 @@ enum ProcEventCode { Evlocallabshadmask = 946, EvlocallabLmask_shape = 947, EvlocallabLLmask_shapewav = 948, + EvlocallabcsThresholdmask = 949, + NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 7fdf1e673..31173afeb 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3828,8 +3828,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : 0.5, 0.35, 0.35 - } - + }, + csthresholdmask(0, 0, 6, 5, false) { } @@ -4324,7 +4324,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && shadmask == other.shadmask && HHhmask_curve == other.HHhmask_curve && Lmask_curve == other.Lmask_curve - && LLmask_curvewav == other.LLmask_curvewav; + && LLmask_curvewav == other.LLmask_curvewav + && csthresholdmask == other.csthresholdmask; } @@ -5843,6 +5844,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->Lmask_curve, "Locallab", "Lmask_Curve_" + index_str, spot.Lmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->LLmask_curvewav, "Locallab", "LLmask_Curvewav_" + index_str, spot.LLmask_curvewav, keyFile); + saveToKeyfile(!pedited || spot_edited->csthresholdmask, "Locallab", "CSThresholdmask_" + index_str, spot.csthresholdmask.toVector(), keyFile); } } } @@ -7581,6 +7583,16 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Lmask_Curve_" + index_str, pedited, spot.Lmask_curve, spotEdited.Lmask_curve); assignFromKeyfile(keyFile, "Locallab", "LLmask_Curvewav_" + index_str, pedited, spot.LLmask_curvewav, spotEdited.LLmask_curvewav); + if (keyFile.has_key("Locallab", "CSThresholdmask_" + index_str)) { + const std::vector thresh = keyFile.get_integer_list("Locallab", "CSThresholdmask_" + index_str); + + if (thresh.size() >= 4) { + spot.csthresholdmask.setValues(thresh[0], thresh[1], min(thresh[2], 10), min(thresh[3], 10)); + } + + spotEdited.csthresholdmask = true; + } + if (spot.visimask) { spotEdited.visimask = true; } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index ecde2d41a..22a33d52a 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1445,6 +1445,7 @@ struct LocallabParams { std::vector HHhmask_curve; std::vector Lmask_curve; std::vector LLmask_curvewav; + Threshold csthresholdmask; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 11ea7d5d6..03e37988f 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -975,7 +975,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabcontmask LUMINANCECURVE, // Evlocallabshadmask LUMINANCECURVE, // EvlocallabLmask_shape - LUMINANCECURVE // EvlocallabLLmask_shapewav + LUMINANCECURVE, // EvlocallabLLmask_shapewav + LUMINANCECURVE // EvlocallabcsThresholdmask + }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 473ebf8be..17b31fac5 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1200,8 +1200,8 @@ private: /* ==== LocallabMask ==== */ class LocallabMask: public Gtk::VBox, - public LocallabTool - + public LocallabTool, + public ThresholdAdjusterListener { private: Adjuster* const sensimask; @@ -1232,6 +1232,7 @@ private: DiagonalCurveEditor* const Lmask_shape; CurveEditorGroup* const mask2CurveEditorGwav; FlatCurveEditor* const LLmask_shapewav; + ThresholdAdjuster* const csThresholdmask; sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn, fftmaskConn; @@ -1251,9 +1252,13 @@ public: 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 adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override {}; // Not used + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; void curveChanged(CurveEditor* ce) override; -// void updateAutocompute(const float blackev, const float whiteev, const float sourceg, const float targetg); private: void enabledChanged() override; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 2556c248a..8af2fa605 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4759,7 +4759,8 @@ LocallabMask::LocallabMask(): mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), - LLmask_shapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))) + LLmask_shapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + csThresholdmask(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) { // Parameter Mask common specific widgets @@ -4830,6 +4831,8 @@ LocallabMask::LocallabMask(): mask2CurveEditorGwav->setCurveListener(this); + csThresholdmask->setAdjusterListener(this); + LLmask_shapewav->setIdentityValue(0.); LLmask_shapewav->setResetCurve(FlatCurveType(defSpot.LLmask_curvewav.at(0)), defSpot.LLmask_curvewav); LLmask_shapewav->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); @@ -4871,6 +4874,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*mask_HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskBox->pack_start(*mask2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskBox->pack_start(*mask2CurveEditorGwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolmaskBox->pack_start(*csThresholdmask, Gtk::PACK_SHRINK, 0); toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); pack_start(*maskmaskBox); @@ -5014,6 +5018,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params fftmask->set_active(spot.fftmask); Lmask_shape->setCurve(spot.Lmask_curve); LLmask_shapewav->setCurve(spot.LLmask_curvewav); + csThresholdmask->setValue(spot.csthresholdmask); } @@ -5060,6 +5065,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.blurmask = blurmask->getValue(); spot.Lmask_curve = Lmask_shape->getCurve(); spot.LLmask_curvewav = LLmask_shapewav->getCurve(); + spot.csthresholdmask = csThresholdmask->getValue(); } @@ -5104,6 +5110,7 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams HHhmask_shape->setCurve(defSpot.HHhmask_curve); contmask->setDefault(defSpot.contmask); blurmask->setDefault(defSpot.blurmask); + csThresholdmask->setDefault(defSpot.csthresholdmask); } @@ -5123,6 +5130,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) mask_HCurveEditorG->hide(); // mask2CurveEditorG->hide(); mask2CurveEditorGwav->hide(); + csThresholdmask->hide(); } else { // Advanced widgets are shown in Expert mode @@ -5135,6 +5143,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) mask_HCurveEditorG->show(); // mask2CurveEditorG->show(); mask2CurveEditorGwav->show(); + csThresholdmask->show(); } } @@ -5159,6 +5168,7 @@ void LocallabMask::convertParamToNormal() HHhmask_shape->setCurve(defSpot.HHhmask_curve); // Lmask_shape->setCurve(defSpot.Lmask_curve); LLmask_shapewav->setCurve(defSpot.LLmask_curvewav); + csThresholdmask->setValue(defSpot.csthresholdmask); // Enable all listeners enableListener(); @@ -5215,6 +5225,8 @@ void LocallabMask::fftmaskChanged() } + + void LocallabMask::curveChanged(CurveEditor* ce) { if (isLocActivated && exp->getEnabled()) { @@ -5351,6 +5363,17 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } +void LocallabMask::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == csThresholdmask) { + if (listener) { + listener->panelChanged(EvlocallabcsThresholdmask, + csThresholdmask->getHistoryString() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} void LocallabMask::enabledChanged() { diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 407e67064..6349b84e0 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1501,6 +1501,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; locallab.spots.at(j).Lmask_curve = locallab.spots.at(j).Lmask_curve && pSpot.Lmask_curve == otherSpot.Lmask_curve; locallab.spots.at(j).LLmask_curvewav = locallab.spots.at(j).LLmask_curvewav && pSpot.LLmask_curvewav == otherSpot.LLmask_curvewav; + locallab.spots.at(j).csthresholdmask = locallab.spots.at(j).csthresholdmask && pSpot.csthresholdmask == otherSpot.csthresholdmask; } } @@ -4899,6 +4900,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).LLmask_curvewav = mods.locallab.spots.at(i).LLmask_curvewav; } + if (locallab.spots.at(i).csthresholdmask) { + toEdit.locallab.spots.at(i).csthresholdmask = mods.locallab.spots.at(i).csthresholdmask; + } + } if (pcvignette.enabled) { @@ -6491,7 +6496,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : shadmask(v), HHhmask_curve(v), Lmask_curve(v), - LLmask_curvewav(v) + LLmask_curvewav(v), + csthresholdmask(v) { } @@ -6977,6 +6983,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) HHhmask_curve =(v); Lmask_curve =(v); LLmask_curvewav =(v); + csthresholdmask = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 7f82e128a..dea2a4a9f 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -859,6 +859,7 @@ public: bool HHhmask_curve; bool Lmask_curve; bool LLmask_curvewav; + bool csthresholdmask; LocallabSpotEdited(bool v); From bef6d823bf9d6daba97c4926c91e1fad559402eb Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 07:36:49 +0200 Subject: [PATCH 021/114] Change tooltips --- rtdata/languages/default | 3 +++ rtgui/locallabtools2.cc | 32 ++++++-------------------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 747602045..cf0b41ac3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2334,6 +2334,7 @@ TP_LOCALLAB_BILATERAL;Bilateral filter TP_LOCALLAB_BLACK_EV;Black Ev TP_LOCALLAB_BLENDMASKCOL;Blend TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image +TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;If blend = 0 no action.\nAdd or subtract the mask from the original image TP_LOCALLAB_BLGUID;Guided Filter TP_LOCALLAB_BLINV;Inverse TP_LOCALLAB_BLCO;Chrominance only @@ -2412,6 +2413,7 @@ TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Mask Wavelet level TP_LOCALLAB_CURV;Lightness - Contrast - Chrominance "Super" TP_LOCALLAB_CURVCURR;Normal TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;If curves at the top, mask is completely black no transformation is made by the mask on the image.\nAs you go down the curve, the mask gradually more colorful and brilliant, the image is changing more and more.\n\nIt is recommended (not required) to position the top of the curves on the gray transition line which represents the references (chroma, luma, hue). +TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP;If curves at the top, mask is completely black no transformation is made by the mask on the image.\nAs you go down the curve, the mask gradually more colorful and brilliant, the image is changing more and more.\n\nThe gray transition line which represents the references (chroma, luma, hue).\nYou can choose or not to position the top of the curves on this transition. TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;To be active, you must enable combobox 'Curves type' TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Tone curve TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), can be used with L(H) in Color and Light @@ -2715,6 +2717,7 @@ TP_LOCALLAB_SENSILOG;Scope TP_LOCALLAB_SENSIS;Scope TP_LOCALLAB_SENSIS_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. TP_LOCALLAB_SENSI_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. +TP_LOCALLAB_SENSIMASK_TOOLTIP;Adjust scope of action for this common mask tool.\nActs on the gap between the original image and the mask.\nThe references (luma, chroma, hue) are those of the center of the RT-spot TP_LOCALLAB_SETTINGS;Settings TP_LOCALLAB_SH1;Shadows Highlights TP_LOCALLAB_SH2;Equalizer diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 8af2fa605..2d94407d0 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4904,11 +4904,11 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { exp->set_tooltip_text(M("TP_LOCALLAB_MASKCOM_TOOLTIP")); - sensimask->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); - blendmask->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); - CCmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); - LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); - HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + sensimask->set_tooltip_text(M("TP_LOCALLAB_SENSIMASK_TOOLTIP")); + blendmask->set_tooltip_text(M("TP_LOCALLAB_BLENDMASKMASK_TOOLTIP")); + CCmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); + LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); + HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); struFrame->set_tooltip_text(M("TP_LOCALLAB_STRUMASK_TOOLTIP")); mask_HCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_HHMASK_TOOLTIP")); radmask->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); @@ -5028,8 +5028,6 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params // Update GUI according to complexity mode updateGUIToMode(static_cast(complexity->get_active_row_number())); - // Update Log Encoding GUI according to autocompute button state -// updateLogGUI(); // Note: No need to manage pedited as batch mode is deactivated for Locallab } @@ -5044,7 +5042,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.expmask = exp->getEnabled(); spot.visimask = exp->get_visible(); spot.complexmask = complexity->get_active_row_number(); - + spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); spot.enamask = enamask->get_active(); @@ -5404,21 +5402,3 @@ void LocallabMask::toolmaskChanged() } } } - - -/* -void LocallabLog::updateLogGUI() -{ - if (autocompute->get_active()) { - blackEv->set_sensitive(false); - whiteEv->set_sensitive(false); - sourceGray->set_sensitive(false); - // targetGray->set_sensitive(true); - } else { - blackEv->set_sensitive(true); - whiteEv->set_sensitive(true); - sourceGray->set_sensitive(true); - // targetGray->set_sensitive(true); - } -} -*/ \ No newline at end of file From 90dedb001faf18fe871f8228afb8386499a40006 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 09:17:37 +0200 Subject: [PATCH 022/114] Add common mask curve to rtengine and iplocallab.cc --- rtengine/dcrop.cc | 36 +++++++++++++++++++++++++++++++++-- rtengine/improccoordinator.cc | 29 +++++++++++++++++++++++++++- rtengine/improccoordinator.h | 15 +++++++++++++++ rtengine/improcfun.h | 6 ++++++ rtengine/iplocallab.cc | 6 ++++++ rtengine/simpleprocess.cc | 26 +++++++++++++++++++++++++ 6 files changed, 115 insertions(+), 3 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 99bc2581d..20f7835d8 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -889,6 +889,7 @@ void Crop::update(int todo) bool localmaskcbutili = parent->localmaskcbutili; bool localmaskblutili = parent->localmaskblutili; bool localmasklcutili = parent->localmasklcutili; + bool localmask_utili = parent->localmask_utili; LUTf lmasklocalcurve2(65536, 0); LUTf lmaskexplocalcurve2(65536, 0); LUTf lmaskSHlocalcurve2(65536, 0); @@ -898,6 +899,7 @@ void Crop::update(int todo) LUTf lmaskcblocalcurve2(65536, 0); LUTf lmaskbllocalcurve2(65536, 0); LUTf lmasklclocalcurve2(65536, 0); + LUTf lmasklocal_curve2(65536, 0); LUTf hltonecurveloc2(65536, 0); //65536 LUTf shtonecurveloc2(65536, 0); LUTf tonecurveloc2(65536, 0); @@ -932,6 +934,11 @@ void Crop::update(int todo) bool lhmasblutili = parent->lhmasblutili; bool lcmasblutili = parent->lcmasblutili; bool llmasblutili = parent->llmasblutili; + bool llmas_utili = parent->llmas_utili; + bool lhmas_utili = parent->lhmas_utili; + bool lcmas_utili = parent->lcmas_utili; + bool lhhmas_utili = parent->lhhmas_utili; + bool locwavutili = parent->locwavutili; bool locwavdenutili = parent->locwavdenutili; bool loclevwavutili = parent->loclevwavutili; @@ -941,6 +948,7 @@ void Crop::update(int todo) bool locedgwavutili = parent->locedgwavutili; bool lmasutiliblwav = parent->lmasutiliblwav; bool lmasutilicolwav = parent->lmasutilicolwav; + bool lmasutili_wav = parent->lmasutili_wav; // float avg = parent->avg; LUTu dummy; @@ -978,9 +986,15 @@ void Crop::update(int todo) LocCCmaskCurve locccmasblCurve; LocLLmaskCurve locllmasblCurve; LocHHmaskCurve lochhmasblCurve; + LocCCmaskCurve locccmas_Curve; + LocLLmaskCurve locllmas_Curve; + LocHHmaskCurve lochhmas_Curve; + LocHHmaskCurve lochhhmas_Curve; + LocwavCurve locwavCurve; LocwavCurve loclmasCurveblwav; LocwavCurve loclmasCurvecolwav; + LocwavCurve loclmasCurve_wav; LocwavCurve loclevwavCurve; LocwavCurve locconwavCurve; LocwavCurve loccompwavCurve; @@ -1031,6 +1045,11 @@ void Crop::update(int todo) locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve, llmaslcutili); lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); + locccmas_Curve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmas_utili); + locllmas_Curve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmas_utili); + lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmas_utili); + lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav, lmasutili_wav); locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); @@ -1069,6 +1088,8 @@ void Crop::update(int todo) CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, sca); localmaskblutili = false; CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, sca); + localmask_utili = false; + CurveFactory::curvemaskLocal(localmask_utili, params.locallab.spots.at(sp).Lmask_curve, lmasklocal_curve2, sca); double ecomp = params.locallab.spots.at(sp).expcomp; double black = params.locallab.spots.at(sp).black; @@ -1121,7 +1142,9 @@ void Crop::update(int todo) lmaskretilocalcurve2, localmaskretiutili, lmaskcblocalcurve2, localmaskcbutili, lmaskbllocalcurve2, localmaskblutili, - lmasklclocalcurve2, localmasklcutili, + lmasklclocalcurve2, localmasklcutili, + lmasklocal_curve2, localmask_utili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, @@ -1130,6 +1153,8 @@ void Crop::update(int todo) locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, + lochhhmas_Curve, lhhmas_utili, loclmasCurveblwav,lmasutiliblwav, loclmasCurvecolwav,lmasutilicolwav, locwavCurve, locwavutili, @@ -1139,6 +1164,7 @@ void Crop::update(int todo) loccomprewavCurve, loccomprewavutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, + loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask, @@ -1157,7 +1183,9 @@ void Crop::update(int todo) lmaskretilocalcurve2, localmaskretiutili, lmaskcblocalcurve2, localmaskcbutili, lmaskbllocalcurve2, localmaskblutili, - lmasklclocalcurve2, localmasklcutili, + lmasklclocalcurve2, localmasklcutili, + lmasklocal_curve2, localmask_utili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili,lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, @@ -1166,6 +1194,9 @@ void Crop::update(int todo) locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, + lochhhmas_Curve, lhhmas_utili, + loclmasCurveblwav,lmasutiliblwav, loclmasCurvecolwav,lmasutilicolwav, locwavCurve, locwavutili, @@ -1175,6 +1206,7 @@ void Crop::update(int todo) loccomprewavCurve, loccomprewavutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, + loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 019486198..5e3aaa0fc 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -209,6 +209,7 @@ ImProcCoordinator::ImProcCoordinator() : lmaskcblocalcurve(65536, 0), lmaskbllocalcurve(65536, 0), lmasklclocalcurve(65536, 0), + lmasklocal_curve(65536, 0), locallutili(false), localclutili(false), locallcutili(false), @@ -228,6 +229,7 @@ ImProcCoordinator::ImProcCoordinator() : localmaskcbutili(false), localmaskblutili(false), localmasklcutili(false), + localmask_utili(false), lcmasexputili(false), lhmasexputili(false), llmasexputili(false), @@ -252,6 +254,11 @@ ImProcCoordinator::ImProcCoordinator() : lcmasblutili(false), lhmasblutili(false), llmasblutili(false), + llmas_utili(false), + lhmas_utili(false), + lcmas_utili(false), + lhhmas_utili(false), + locwavutili(false), locwavdenutili(false), loclevwavutili(false), @@ -261,6 +268,7 @@ ImProcCoordinator::ImProcCoordinator() : locedgwavutili(false), lmasutiliblwav(false), lmasutilicolwav(false), + lmasutili_wav(false), LHutili(false), HHutili(false), lastsavrests(500, -10000), @@ -1151,6 +1159,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) localmaskcbutili = false; localmaskblutili = false; localmasklcutili = false; + localmask_utili = false; lcmasSHutili = false; lhmasSHutili = false; llmasSHutili = false; @@ -1172,7 +1181,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) lcmasblutili = false; lhmasblutili = false; llmasblutili = false; - lcmasutili = false; + llmas_utili = false; + lhmas_utili = false; + lcmas_utili = false; + lhhmas_utili = false; + locwavutili = false; locwavdenutili = false; loclevwavutili = false; @@ -1182,6 +1195,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locedgwavutili = false; lmasutiliblwav = false; lmasutilicolwav = false; + lmasutili_wav = false; locRETgainCurve.Set(params->locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params->locallab.spots.at(sp).localTtranscurve); loclhCurve.Set(params->locallab.spots.at(sp).LHcurve, LHutili); @@ -1214,6 +1228,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve, llmasblutili); locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); + locccmas_Curve.Set(params->locallab.spots.at(sp).CCmaskcurve, lcmas_utili); + locllmas_Curve.Set(params->locallab.spots.at(sp).LLmaskcurve, llmas_utili); + lochhmas_Curve.Set(params->locallab.spots.at(sp).HHmaskcurve, lhmas_utili); + lochhhmas_Curve.Set(params->locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); locwavCurve.Set(params->locallab.spots.at(sp).locwavcurve, locwavutili); @@ -1223,6 +1242,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) loccomprewavCurve.Set(params->locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); locwavCurveden.Set(params->locallab.spots.at(sp).locwavcurveden, locwavdenutili); locedgwavCurve.Set(params->locallab.spots.at(sp).locedgwavcurve, locedgwavutili); + loclmasCurve_wav.Set(params->locallab.spots.at(sp).LLmask_curvewav, lmasutili_wav); + CurveFactory::curveLocal(locallutili, params->locallab.spots.at(sp).llcurve, lllocalcurve, sca); CurveFactory::curveLocal(localclutili, params->locallab.spots.at(sp).clcurve, cllocalcurve, sca); CurveFactory::curveLocal(locallcutili, params->locallab.spots.at(sp).lccurve, lclocalcurve, sca); @@ -1238,6 +1259,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) CurveFactory::curvemaskLocal(localmaskcbutili, params->locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, sca); CurveFactory::curvemaskLocal(localmaskblutili, params->locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, sca); CurveFactory::curvemaskLocal(localmasklcutili, params->locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, sca); + CurveFactory::curvemaskLocal(localmask_utili, params->locallab.spots.at(sp).Lmask_curve, lmasklocal_curve, sca); double ecomp = params->locallab.spots.at(sp).expcomp; double black = params->locallab.spots.at(sp).black; double hlcompr = params->locallab.spots.at(sp).hlcompr; @@ -1303,6 +1325,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) lmaskcblocalcurve, localmaskcbutili, lmaskbllocalcurve, localmaskblutili, lmasklclocalcurve, localmasklcutili, + lmasklocal_curve, localmask_utili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, @@ -1311,6 +1335,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, + lochhhmas_Curve, lhhmas_utili, loclmasCurveblwav, lmasutiliblwav, loclmasCurvecolwav, lmasutilicolwav, locwavCurve, locwavutili, @@ -1320,6 +1346,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) loccomprewavCurve, loccomprewavutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, + loclmasCurve_wav, lmasutili_wav, LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index da253c072..8ad20f0d8 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -257,6 +257,8 @@ protected: LUTf lmaskcblocalcurve; LUTf lmaskbllocalcurve; LUTf lmasklclocalcurve; + LUTf lmasklocal_curve; + // LUTu lhist16loc; LocretigainCurve locRETgainCurve; LocretitransCurve locRETtransCurve; @@ -291,6 +293,11 @@ protected: LocCCmaskCurve locccmasblCurve; LocLLmaskCurve locllmasblCurve; LocHHmaskCurve lochhmasblCurve; + LocCCmaskCurve locccmas_Curve; + LocLLmaskCurve locllmas_Curve; + LocHHmaskCurve lochhmas_Curve; + LocHHmaskCurve lochhhmas_Curve; + LocwavCurve locwavCurve; LocwavCurve loclmasCurveblwav; LocwavCurve loclmasCurvecolwav; @@ -300,6 +307,7 @@ protected: LocwavCurve loccomprewavCurve; LocwavCurve locwavCurveden; LocwavCurve locedgwavCurve; + LocwavCurve loclmasCurve_wav; bool locallutili; bool localclutili; @@ -320,6 +328,7 @@ protected: bool localmaskcbutili; bool localmaskblutili; bool localmasklcutili; + bool localmask_utili; bool lcmasexputili; bool lhmasexputili; bool llmasexputili; @@ -344,6 +353,11 @@ protected: bool lcmasblutili; bool lhmasblutili; bool llmasblutili; + bool llmas_utili; + bool lhmas_utili; + bool lcmas_utili; + bool lhhmas_utili; + bool locwavutili; bool locwavdenutili; bool loclevwavutili; @@ -353,6 +367,7 @@ protected: bool locedgwavutili; bool lmasutiliblwav; bool lmasutilicolwav; + bool lmasutili_wav; bool LHutili; bool HHutili; LUTu lastsavrests; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 355b5d435..d09a1aabd 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -299,6 +299,8 @@ public: const LUTf& lmaskcblocalcurve, bool localmaskcbutili, const LUTf& lmaskbllocalcurve, bool localmaskblutili, const LUTf& lmasklclocalcurve, bool localmasklcutili, + const LUTf& lmasklocal_curve, bool localmask_utili, + const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, @@ -308,6 +310,9 @@ public: const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, + const LocCCmaskCurve& locccmas_Curve, bool lcmas_utili, const LocLLmaskCurve& locllmas_Curve, bool llmas_utili, const LocHHmaskCurve& lochhmas_Curve, bool lhmas_utili, + const LocHHmaskCurve& lochhhmas_Curve, bool lhhmas_utili, + const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, const LocwavCurve& locwavCurve, bool locwavutili, @@ -317,6 +322,7 @@ public: const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, const LocwavCurve& locwavCurveden, bool locwavdenutili, const LocwavCurve& locedgwavCurve, bool locedgwavutili, + const LocwavCurve& loclmasCurve_wav, bool lmasutili_wav, bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, double& huerefblur, double &chromarefblur, double& lumarefblur, double &hueref, double &chromaref, double &lumaref, double &sobelref, int &lastsav, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 7b27712e1..65c0e120b 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -9527,6 +9527,8 @@ void ImProcFunctions::Lab_Local( const LUTf& lmaskcblocalcurve, bool localmaskcbutili, const LUTf& lmaskbllocalcurve, bool localmaskblutili, const LUTf& lmasklclocalcurve, bool localmasklcutili, + const LUTf& lmasklocal_curve, bool localmask_utili, + const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, @@ -9536,6 +9538,8 @@ void ImProcFunctions::Lab_Local( const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, + const LocCCmaskCurve& locccmas_Curve, bool lcmas_utili, const LocLLmaskCurve& locllmas_Curve, bool llmas_utili, const LocHHmaskCurve& lochhmas_Curve, bool lhmas_utili, + const LocHHmaskCurve& lochhhmas_Curve, bool lhhmas_utili, const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, const LocwavCurve& locwavCurve, bool locwavutili, @@ -9545,6 +9549,8 @@ void ImProcFunctions::Lab_Local( const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, const LocwavCurve& locwavCurveden, bool locwavdenutili, const LocwavCurve& locedgwavCurve, bool locedgwavutili, + const LocwavCurve& loclmasCurve_wav, bool lmasutili_wav, + bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, double& huerefblur, double& chromarefblur, double& lumarefblur, double& hueref, double& chromaref, double& lumaref, double& sobelref, int &lastsav, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 3c0e0aee8..d991a0f9f 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1125,8 +1125,14 @@ private: LocCCmaskCurve locccmasblCurve; LocLLmaskCurve locllmasblCurve; LocHHmaskCurve lochhmasblCurve; + LocCCmaskCurve locccmas_Curve; + LocLLmaskCurve locllmas_Curve; + LocHHmaskCurve lochhmas_Curve; + LocHHmaskCurve lochhhmas_Curve; + LocwavCurve loclmasCurveblwav; LocwavCurve loclmasCurvecolwav; + LocwavCurve loclmasCurve_wav; LocwavCurve locwavCurve; LocwavCurve loclevwavCurve; LocwavCurve locconwavCurve; @@ -1153,6 +1159,7 @@ private: LUTf lmaskcblocalcurve(65536, 0); LUTf lmaskbllocalcurve(65536, 0); LUTf lmasklclocalcurve(65536, 0); + LUTf lmasklocal_curve(65536, 0); // int maxspot = 1; float** shbuffer = nullptr; @@ -1188,6 +1195,7 @@ private: bool localmaskcbutili = false; bool localmaskblutili = false; bool localmasklcutili = false; + bool localmask_utili = false; bool lcmasexputili = false; bool lhmasexputili = false; bool llmasexputili = false; @@ -1212,6 +1220,11 @@ private: bool lcmasblutili = false; bool lhmasblutili = false; bool llmasblutili = false; + bool llmas_utili = false; + bool lhmas_utili = false; + bool lcmas_utili = false; + bool lhhmas_utili = false; + bool locwavutili = false; bool locwavdenutili = false; bool loclevwavutili = false; @@ -1221,6 +1234,7 @@ private: bool locedgwavutili = false; bool lmasutiliblwav = false; bool lmasutilicolwav = false; + bool lmasutili_wav = false; locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); loclhCurve.Set(params.locallab.spots.at(sp).LHcurve, LHutili); @@ -1250,8 +1264,14 @@ private: locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); + locccmas_Curve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmas_utili); + locllmas_Curve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmas_utili); + lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmas_utili); + lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); + loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav, lmasutili_wav); locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); @@ -1275,6 +1295,7 @@ private: CurveFactory::curvemaskLocal(localmaskcbutili, params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, 1); CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, 1); CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, 1); + CurveFactory::curvemaskLocal(localmask_utili, params.locallab.spots.at(sp).Lmask_curve, lmasklocal_curve, 1); //provisory double ecomp = params.locallab.spots.at(sp).expcomp; double black = params.locallab.spots.at(sp).black; @@ -1323,6 +1344,8 @@ private: lmaskcblocalcurve, localmaskcbutili, lmaskbllocalcurve, localmaskblutili, lmasklclocalcurve, localmasklcutili, + lmasklocal_curve, localmask_utili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, @@ -1331,6 +1354,8 @@ private: locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, + lochhhmas_Curve, lhhmas_utili, loclmasCurveblwav,lmasutiliblwav, loclmasCurvecolwav,lmasutilicolwav, locwavCurve, locwavutili, @@ -1340,6 +1365,7 @@ private: loccomprewavCurve, loccomprewavutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, + loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); From fb06a4dcfd4071a1d9f429a31df5f24677559b41 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 12:00:23 +0200 Subject: [PATCH 023/114] Pass GUI parameters mask to rtengine and iplocallab.cc --- rtdata/languages/default | 38 ++++++++++---------- rtengine/dcrop.cc | 4 +-- rtengine/improccoordinator.cc | 3 +- rtengine/improccoordinator.h | 4 ++- rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 65 ++++++++++++++++++++--------------- rtengine/procevents.h | 39 ++++++++++----------- rtengine/refreshmap.cc | 1 - rtengine/rtengine.h | 2 +- rtengine/simpleprocess.cc | 2 +- rtgui/locallabtools.h | 6 ++-- rtgui/locallabtools2.cc | 36 +++++++++---------- rtgui/toolpanelcoord.cc | 6 ++-- 13 files changed, 109 insertions(+), 99 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index cf0b41ac3..297466f2a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1168,25 +1168,25 @@ HISTORY_MSG_927;Local - Shadow mask HISTORY_MSG_928;Local - Common color mask HISTORY_MSG_929;Local - Mask common scope HISTORY_MSG_930;Local - Mask Common blend -HISTORY_MSG_932;Local - Mask Common enable -HISTORY_MSG_933;Local - Mask Common radius soft -HISTORY_MSG_934;Local - Mask Common laplacian -HISTORY_MSG_935;Local - Mask Common chroma -HISTORY_MSG_936;Local - Mask Common gamma -HISTORY_MSG_937;Local - Mask Common slope -HISTORY_MSG_938;Local - Mask Common curve C(C) -HISTORY_MSG_939;Local - Mask Common curve L(L) -HISTORY_MSG_940;Local - Mask Common curve LC(H) -HISTORY_MSG_941;Local - Mask Common structure as tool -HISTORY_MSG_942;Local - Mask Common structure strength -HISTORY_MSG_943;Local - Mask Common H(H) curve -HISTORY_MSG_944;Local - Mask Common FFT -HISTORY_MSG_945;Local - Mask Common Blur radius -HISTORY_MSG_946;Local - Mask Common contrast threshold -HISTORY_MSG_947;Local - Mask Common shadows -HISTORY_MSG_948;Local - Mask Common Contrast curve -HISTORY_MSG_949;Local - Mask Common Wavelet curve -HISTORY_MSG_950;Local - Mask Common Threshold levels +HISTORY_MSG_931;Local - Mask Common enable +HISTORY_MSG_932;Local - Mask Common radius soft +HISTORY_MSG_933;Local - Mask Common laplacian +HISTORY_MSG_934;Local - Mask Common chroma +HISTORY_MSG_935;Local - Mask Common gamma +HISTORY_MSG_936;Local - Mask Common slope +HISTORY_MSG_937;Local - Mask Common curve C(C) +HISTORY_MSG_938;Local - Mask Common curve L(L) +HISTORY_MSG_939;Local - Mask Common curve LC(H) +HISTORY_MSG_940;Local - Mask Common structure as tool +HISTORY_MSG_941;Local - Mask Common structure strength +HISTORY_MSG_942;Local - Mask Common H(H) curve +HISTORY_MSG_943;Local - Mask Common FFT +HISTORY_MSG_944;Local - Mask Common Blur radius +HISTORY_MSG_945;Local - Mask Common contrast threshold +HISTORY_MSG_946;Local - Mask Common shadows +HISTORY_MSG_947;Local - Mask Common Contrast curve +HISTORY_MSG_948;Local - Mask Common Wavelet curve +HISTORY_MSG_949;Local - Mask Common Threshold levels HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 20f7835d8..ab5ec0196 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1168,7 +1168,7 @@ void Crop::update(int todo) LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask, - minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + parent->locall_Mask, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); } else { parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop, lastorigCrop, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, lllocalcurve2,locallutili, @@ -1208,7 +1208,7 @@ void Crop::update(int todo) locedgwavCurve, locedgwavutili, loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, - huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 5e3aaa0fc..53a30daa5 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -305,6 +305,7 @@ ImProcCoordinator::ImProcCoordinator() : localltmMask(0), locallblMask(0), locallsharMask(0), + locall_Mask(0), retistrsav(nullptr) { } @@ -1348,7 +1349,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locedgwavCurve, locedgwavutili, loclmasCurve_wav, lmasutili_wav, LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, - huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); lastorigimp->CopyFrom(nprevl); diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 8ad20f0d8..129161a34 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -398,6 +398,7 @@ protected: int localltmMask; int locallblMask; int locallsharMask; + int locall_Mask; public: @@ -468,7 +469,7 @@ public: updaterThreadStart.unlock(); } - void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask) override + void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask, int locall_Mask) override { this->previewDeltaE = previewDeltaE; this->locallColorMask = locallColorMask; @@ -485,6 +486,7 @@ public: this->locallsharMask = locallsharMask; this->localllcMask = localllcMask; this->locallcbMask = locallcbMask; + this->locall_Mask = locall_Mask; } void setProgressListener (ProgressListener* pl) override diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index d09a1aabd..31c8cdefc 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -325,7 +325,7 @@ public: const LocwavCurve& loclmasCurve_wav, bool lmasutili_wav, bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, double& huerefblur, double &chromarefblur, double& lumarefblur, double &hueref, double &chromaref, double &lumaref, double &sobelref, int &lastsav, - bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, + bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax); void addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 65c0e120b..97fd43a0d 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -522,6 +522,7 @@ struct local_params { int showmasksoftmet; int showmasktmmet; int showmaskblmet; + int showmask_met; bool fftbl; float laplacexp; float balanexp; @@ -571,6 +572,7 @@ struct local_params { bool hsena; bool vibena; bool logena; + bool maskena; bool cut_past; float past; float satur; @@ -605,6 +607,7 @@ struct local_params { bool enaretiMasktmap; bool enatmMask; bool enablMask; + bool ena_Mask; int highlihs; int shadowhs; int radiushs; @@ -637,7 +640,7 @@ struct local_params { }; -static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locallab, struct local_params& lp, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, const LocwavCurve & locwavCurveden, bool locwavdenutili) +static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locallab, struct local_params& lp, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, const LocwavCurve & locwavCurveden, bool locwavdenutili) { int w = oW; int h = oH; @@ -729,19 +732,23 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.showmasktmmet = lltmMask; lp.showmaskblmet = llblMask; - lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaExpMask = locallab.spots.at(sp).enaExpMask && llExpMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaExpMaskinv = locallab.spots.at(sp).enaExpMask && llExpMaskinv == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaSHMask = locallab.spots.at(sp).enaSHMask && llSHMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enaSHMaskinv = locallab.spots.at(sp).enaSHMask && llSHMaskinv == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enacbMask = locallab.spots.at(sp).enacbMask && llcbMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enaretiMask = locallab.spots.at(sp).enaretiMask && lllcMask == 0 && llsharMask == 0 && llsoftMask == 0 && llretiMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enatmMask = locallab.spots.at(sp).enatmMask && lltmMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enablMask = locallab.spots.at(sp).enablMask && llblMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; - lp.enavibMask = locallab.spots.at(sp).enavibMask && llvibMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llSHMask == 0; - lp.enalcMask = locallab.spots.at(sp).enalcMask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - lp.enasharMask = lllcMask == 0 && llcbMask == 0 && llsharMask == 0 && llsoftMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.showmask_met = ll_Mask; + printf("mask=%i \n", lp.showmask_met); + + lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMask = locallab.spots.at(sp).enaExpMask && llExpMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMaskinv = locallab.spots.at(sp).enaExpMask && llExpMaskinv == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaSHMask = locallab.spots.at(sp).enaSHMask && llSHMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enaSHMaskinv = locallab.spots.at(sp).enaSHMask && llSHMaskinv == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enacbMask = locallab.spots.at(sp).enacbMask && llcbMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enaretiMask = locallab.spots.at(sp).enaretiMask && lllcMask == 0 && llsharMask == 0 && llsoftMask == 0 && llretiMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enatmMask = locallab.spots.at(sp).enatmMask && lltmMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enablMask = locallab.spots.at(sp).enablMask && llblMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enavibMask = locallab.spots.at(sp).enavibMask && llvibMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.enalcMask = locallab.spots.at(sp).enalcMask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0 ; + lp.enasharMask = lllcMask == 0 && llcbMask == 0 && llsharMask == 0 && llsoftMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.ena_Mask = locallab.spots.at(sp).enamask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; // printf("llColorMask=%i lllcMask=%i llExpMask=%i llSHMask=%i llcbMask=%i llretiMask=%i lltmMask=%i llblMask=%i llvibMask=%i\n", llColorMask, lllcMask, llExpMask, llSHMask, llcbMask, llretiMask, lltmMask, llblMask, llvibMask); if (locallab.spots.at(sp).softMethod == "soft") { @@ -1301,17 +1308,19 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.threshol = thresho; lp.chromacb = chromcbdl; lp.expvib = locallab.spots.at(sp).expvibrance; - lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask - lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0; - lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0; - lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0; - lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0; - lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0; - lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible - lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible - lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible - lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0; - lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0; + lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask + lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible + lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible + lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && ll_Mask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.maskena = locallab.spots.at(sp).expmask && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.sensv = local_sensiv; lp.past = chromaPastel; lp.satur = chromaSatur; @@ -5491,7 +5500,7 @@ void ImProcFunctions::calc_ref(int sp, LabImage * original, LabImage * transform if (params->locallab.enabled) { //always calculate hueref, chromaref, lumaref before others operations use in normal mode for all modules exceprt denoise struct local_params lp; - calcLocalParams(sp, oW, oH, params->locallab, lp, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, locwavCurveden, locwavdenutili); + calcLocalParams(sp, oW, oH, params->locallab, lp, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, locwavCurveden, locwavdenutili); int begy = lp.yc - lp.lyT; int begx = lp.xc - lp.lxL; int yEn = lp.yc + lp.ly; @@ -9553,7 +9562,7 @@ void ImProcFunctions::Lab_Local( bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, double& huerefblur, double& chromarefblur, double& lumarefblur, double& hueref, double& chromaref, double& lumaref, double& sobelref, int &lastsav, - bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, + bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, float& minCD, float& maxCD, float& mini, float& maxi, float& Tmean, float& Tsigma, float& Tmin, float& Tmax ) { @@ -9566,7 +9575,7 @@ void ImProcFunctions::Lab_Local( constexpr int del = 3; // to avoid crash with [loy - begy] and [lox - begx] and bfh bfw // with gtk2 [loy - begy-1] [lox - begx -1 ] and del = 1 struct local_params lp; - calcLocalParams(sp, oW, oH, params->locallab, lp, prevDeltaE, llColorMask, llColorMaskinv, llExpMask, llExpMaskinv, llSHMask, llSHMaskinv, llvibMask, lllcMask, llsharMask, llcbMask, llretiMask, llsoftMask, lltmMask, llblMask, locwavCurveden, locwavdenutili); + calcLocalParams(sp, oW, oH, params->locallab, lp, prevDeltaE, llColorMask, llColorMaskinv, llExpMask, llExpMaskinv, llSHMask, llSHMaskinv, llvibMask, lllcMask, llsharMask, llcbMask, llretiMask, llsoftMask, lltmMask, llblMask, ll_Mask, locwavCurveden, locwavdenutili); const float radius = lp.rad / (sk * 1.4f); //0 to 70 ==> see skip int levred; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index fe429d7d8..63d8d7082 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -953,26 +953,25 @@ enum ProcEventCode { EvLocena_mask = 927, Evlocallabsensimask = 928, Evlocallabblendmask = 929, - EvlocallabshowmaskmaskMethod = 930, - EvLocallabEnaMask = 931, - Evlocallabradmask = 932, - Evlocallablapmask = 933, - Evlocallabchromask = 934, - Evlocallabgammask = 935, - Evlocallabslopmask = 936, - EvlocallabCCmask_shape = 937, - EvlocallabLLmask_shape = 938, - EvlocallabHHmask_shape = 939, - EvLocallabtoolmask = 940, - Evlocallabstrumaskmask = 941, - EvlocallabHHhmask_shape = 942, - EvLocallabfftmask = 943, - Evlocallabblurmask = 944, - Evlocallabcontmask = 945, - Evlocallabshadmask = 946, - EvlocallabLmask_shape = 947, - EvlocallabLLmask_shapewav = 948, - EvlocallabcsThresholdmask = 949, + EvLocallabEnaMask = 930, + Evlocallabradmask = 931, + Evlocallablapmask = 932, + Evlocallabchromask = 933, + Evlocallabgammask = 934, + Evlocallabslopmask = 935, + EvlocallabCCmask_shape = 936, + EvlocallabLLmask_shape = 937, + EvlocallabHHmask_shape = 938, + EvLocallabtoolmask = 939, + Evlocallabstrumaskmask = 940, + EvlocallabHHhmask_shape = 941, + EvLocallabfftmask = 942, + Evlocallabblurmask = 943, + Evlocallabcontmask = 944, + Evlocallabshadmask = 945, + EvlocallabLmask_shape = 946, + EvlocallabLLmask_shapewav = 947, + EvlocallabcsThresholdmask = 948, NUMOFEVENTS }; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 03e37988f..d145b3f38 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -957,7 +957,6 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvLocenamask LUMINANCECURVE, // Evlocallabsensimask LUMINANCECURVE, // Evlocallabblendmask - LUMINANCECURVE, // EvlocallabshowmaskmaskMethod LUMINANCECURVE, // EvLocallabEna_Mask LUMINANCECURVE, // Evlocallabradmask LUMINANCECURVE, // Evlocallablapmask diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 7561d68aa..1671ae1f5 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -549,7 +549,7 @@ public: virtual void updateUnLock() = 0; - virtual void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask) = 0; + virtual void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask, int locall_Mask) = 0; /** Creates and returns a Crop instance that acts as a window on the image * @param editDataProvider pointer to the EditDataProvider that communicates with the EditSubscriber diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index d991a0f9f..e68a1b92c 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1367,7 +1367,7 @@ private: locedgwavCurve, locedgwavutili, loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, - huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); lastorigView->CopyFrom(labView); diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 17b31fac5..32b833d04 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1206,7 +1206,7 @@ class LocallabMask: private: Adjuster* const sensimask; Adjuster* const blendmask; - MyComboBoxText* const showmaskMethod; + MyComboBoxText* const showmask_Method; Gtk::CheckButton* const enamask; CurveEditorGroup* const mask_CurveEditorG; FlatCurveEditor* const CCmask_shape; @@ -1234,7 +1234,7 @@ private: FlatCurveEditor* const LLmask_shapewav; ThresholdAdjuster* const csThresholdmask; - sigc::connection showmaskMethodConn, enamaskConn, toolmaskConn, fftmaskConn; + sigc::connection showmask_MethodConn, enamaskConn, toolmaskConn, fftmaskConn; public: LocallabMask(); @@ -1262,7 +1262,7 @@ public: private: void enabledChanged() override; - void showmaskMethodChanged(); + void showmask_MethodChanged(); void enamaskChanged(); void toolmaskChanged(); void convertParamToNormal() override; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 2d94407d0..52526cf5f 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4732,7 +4732,7 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - showmaskMethod(Gtk::manage(new MyComboBoxText())), + showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), mask_CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), CCmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), @@ -4768,13 +4768,13 @@ LocallabMask::LocallabMask(): sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); - showmaskMethod->append(M("TP_LOCALLAB_SHOWMNONE")); - showmaskMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); - showmaskMethod->append(M("TP_LOCALLAB_SHOWMASK")); - showmaskMethod->append(M("TP_LOCALLAB_SHOWREF")); - showmaskMethod->set_active(0); - showmaskMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); - showmaskMethodConn = showmaskMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmaskMethodChanged)); + showmask_Method->append(M("TP_LOCALLAB_SHOWMNONE")); + showmask_Method->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmask_Method->append(M("TP_LOCALLAB_SHOWMASK")); + showmask_Method->append(M("TP_LOCALLAB_SHOWREF")); + showmask_Method->set_active(0); + showmask_Method->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmask_MethodConn = showmask_Method->signal_changed().connect(sigc::mem_fun(*this, &LocallabMask::showmask_MethodChanged)); enamaskConn = enamask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabMask::enamaskChanged)); mask_CurveEditorG->setCurveListener(this); @@ -4843,7 +4843,7 @@ LocallabMask::LocallabMask(): pack_start(*blendmask, Gtk::PACK_SHRINK, 0); ToolParamBlock* const maskmaskBox = Gtk::manage(new ToolParamBlock()); - maskmaskBox->pack_start(*showmaskMethod, Gtk::PACK_SHRINK, 4); + maskmaskBox->pack_start(*showmask_Method, Gtk::PACK_SHRINK, 4); maskmaskBox->pack_start(*mask_CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor maskmaskBox->pack_start(*enamask, Gtk::PACK_SHRINK, 0); @@ -4882,22 +4882,22 @@ LocallabMask::LocallabMask(): bool LocallabMask::isMaskViewActive() { - return ((showmaskMethod->get_active_row_number() != 0)); + return ((showmask_Method->get_active_row_number() != 0)); } void LocallabMask::resetMaskView() { - showmaskMethodConn.block(true); + showmask_MethodConn.block(true); - showmaskMethod->set_active(0); + showmask_Method->set_active(0); - showmaskMethodConn.block(false); + showmask_MethodConn.block(false); } void LocallabMask::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask, int &maskMask) { - colorMask = showmaskMethod->get_active_row_number(); + maskMask = showmask_Method->get_active_row_number(); } void LocallabMask::updateAdviceTooltips(const bool showTooltips) @@ -4947,7 +4947,7 @@ LocallabMask::~LocallabMask() void LocallabMask::disableListener() { LocallabTool::disableListener(); - showmaskMethodConn.block(true); + showmask_MethodConn.block(true); enamaskConn.block(true); toolmaskConn.block(true); fftmaskConn.block(true); @@ -4957,14 +4957,14 @@ void LocallabMask::disableListener() void LocallabMask::enableListener() { LocallabTool::enableListener(); - showmaskMethodConn.block(false); + showmask_MethodConn.block(false); enamaskConn.block(false); toolmaskConn.block(false); fftmaskConn.block(false); } -void LocallabMask::showmaskMethodChanged() +void LocallabMask::showmask_MethodChanged() { // If mask preview is activated, deactivate all other tool mask preview @@ -4973,7 +4973,7 @@ void LocallabMask::showmaskMethodChanged() } if (listener) { - listener->panelChanged(EvlocallabshowmaskmaskMethod, ""); + listener->panelChanged(EvlocallabshowmaskMethod, ""); } } diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 70ae232a4..53ad0cbe5 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -534,12 +534,12 @@ void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const ipc->setLocallabMaskVisibility(maskStruc.previewDeltaE, maskStruc.colorMask, maskStruc.colorMaskinv, maskStruc.expMask, maskStruc.expMaskinv, maskStruc.SHMask, maskStruc.SHMaskinv, maskStruc.vibMask, maskStruc.softMask, maskStruc.blMask, maskStruc.tmMask, maskStruc.retiMask, maskStruc.sharMask, - maskStruc.lcMask, maskStruc.cbMask); + maskStruc.lcMask, maskStruc.cbMask, maskStruc.maskMask); } else if (event == rtengine::EvLocallabSpotCreated || event == rtengine::EvLocallabSpotSelectedWithMask || event == rtengine::EvLocallabSpotDeleted || event == rtengine::Evlocallabshowreset || event == rtengine::EvlocallabToolRemovedWithRefresh) { locallab->resetMaskVisibility(); - ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } ipc->endUpdateParams(changeFlags); // starts the IPC processing @@ -649,7 +649,7 @@ void ToolPanelCoordinator::profileChange( // Reset Locallab mask visibility locallab->resetMaskVisibility(); - ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // start the IPC processing if (filterRawRefresh) { From cb6dac29993a6f9bf9b849d02002eef7df4fbc13 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 13:01:48 +0200 Subject: [PATCH 024/114] Change settings visibility --- rtgui/controlspotpanel.cc | 37 ++++++++++++++++++++++++++++++++----- rtgui/locallab.cc | 2 +- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc index 83d62f572..5a1c76690 100644 --- a/rtgui/controlspotpanel.cc +++ b/rtgui/controlspotpanel.cc @@ -360,11 +360,15 @@ ControlSpotPanel::ControlSpotPanel(): artifBox->pack_start(*balan_); artifBox->pack_start(*balanh_); artifBox->pack_start(*colorde_); - artifBox->pack_start(*preview_); - artifBox->pack_start(*colorscope_); +// artifBox->pack_start(*preview_); +// artifBox->pack_start(*colorscope_); expShapeDetect_->add(*artifBox, false); pack_start(*expShapeDetect_, false, false); - + ToolParamBlock* const artifBox2 = Gtk::manage(new ToolParamBlock()); + + artifBox2->pack_start(*preview_); + artifBox2->pack_start(*colorscope_); + pack_start(*artifBox2); ToolParamBlock* const specCaseBox = Gtk::manage(new ToolParamBlock()); avoidConn_ = avoid_->signal_toggled().connect( @@ -389,6 +393,29 @@ ControlSpotPanel::ControlSpotPanel(): } specCaseBox->pack_start(*recurs_); + specCaseBox->pack_start(*ctboxshapemethod); + + Gtk::HBox* const ctboxwavmethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelwavmethod = Gtk::manage(new Gtk::Label(M("TP_WAVELET_DAUBLOCAL") + ":")); + ctboxwavmethod->pack_start(*labelwavmethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + ctboxwavmethod->set_tooltip_markup(M("TP_WAVELET_DAUB_TOOLTIP")); + } + + wavMethod_->append(M("TP_WAVELET_DAUB2")); + wavMethod_->append(M("TP_WAVELET_DAUB4")); + wavMethod_->append(M("TP_WAVELET_DAUB6")); + wavMethod_->append(M("TP_WAVELET_DAUB10")); + wavMethod_->append(M("TP_WAVELET_DAUB14")); + wavMethod_->set_active(1); + wavMethodconn_ = wavMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::wavMethodChanged)); + ctboxwavmethod->pack_start(*wavMethod_); + specCaseBox->pack_start(*ctboxwavmethod); + + expSpecCases_->add(*specCaseBox, false); pack_start(*expSpecCases_, false, false); @@ -452,7 +479,7 @@ ControlSpotPanel::ControlSpotPanel(): *this, &ControlSpotPanel::complexMethodChanged)); ctboxcomplexmethod->pack_start(*complexMethod_); // pack_start(*ctboxcomplexmethod); - +/* Gtk::HBox* const ctboxwavmethod = Gtk::manage(new Gtk::HBox()); Gtk::Label* const labelwavmethod = Gtk::manage(new Gtk::Label(M("TP_WAVELET_DAUBLOCAL") + ":")); ctboxwavmethod->pack_start(*labelwavmethod, Gtk::PACK_SHRINK, 4); @@ -472,7 +499,7 @@ ControlSpotPanel::ControlSpotPanel(): *this, &ControlSpotPanel::wavMethodChanged)); ctboxwavmethod->pack_start(*wavMethod_); pack_start(*ctboxwavmethod); - +*/ show_all(); // Define row background color diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 1760928b5..12de07536 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -191,6 +191,7 @@ Locallab::Locallab(): addTool(toolpanel, expexpose); addTool(toolpanel, expshadhigh); addTool(toolpanel, expvibrance); + addTool(toolpanel, expmask); addTool(toolpanel, expsoft); addTool(toolpanel, expblur); addTool(toolpanel, exptonemap); @@ -199,7 +200,6 @@ Locallab::Locallab(): addTool(toolpanel, expcontrast); addTool(toolpanel, expcbdl); addTool(toolpanel, explog); - addTool(toolpanel, expmask); panel->pack_start(*toolpanel, false, false); // Add separator From 6a03d8e32546078f73ac970d2e25896b65f20240 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 17:53:56 +0200 Subject: [PATCH 025/114] first run that work...with bug...but save --- rtengine/dcrop.cc | 8 +- rtengine/improccoordinator.cc | 8 +- rtengine/iplocallab.cc | 148 ++++++++++++++++++++++++++++++++-- rtengine/simpleprocess.cc | 8 +- 4 files changed, 152 insertions(+), 20 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index ab5ec0196..70878d77e 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1045,10 +1045,10 @@ void Crop::update(int todo) locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve, llmaslcutili); lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); - locccmas_Curve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmas_utili); - locllmas_Curve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmas_utili); - lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmas_utili); - lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + locccmas_Curve.Set(params.locallab.spots.at(sp).CCmask_curve, lcmas_utili); + locllmas_Curve.Set(params.locallab.spots.at(sp).LLmask_curve, llmas_utili); + lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmask_curve, lhmas_utili); + lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmask_curve, lhhmas_utili); loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav, lmasutili_wav); locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 53a30daa5..054a962ac 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1229,10 +1229,10 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve, llmasblutili); locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); - locccmas_Curve.Set(params->locallab.spots.at(sp).CCmaskcurve, lcmas_utili); - locllmas_Curve.Set(params->locallab.spots.at(sp).LLmaskcurve, llmas_utili); - lochhmas_Curve.Set(params->locallab.spots.at(sp).HHmaskcurve, lhmas_utili); - lochhhmas_Curve.Set(params->locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + locccmas_Curve.Set(params->locallab.spots.at(sp).CCmask_curve, lcmas_utili); + locllmas_Curve.Set(params->locallab.spots.at(sp).LLmask_curve, llmas_utili); + lochhmas_Curve.Set(params->locallab.spots.at(sp).HHmask_curve, lhmas_utili); + lochhhmas_Curve.Set(params->locallab.spots.at(sp).HHhmask_curve, lhhmas_utili); loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 97fd43a0d..e00517959 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -620,6 +620,7 @@ struct local_params { float whiteev; float detail; int sensilog; + int sensimas; bool Autogray; bool autocompute; float baselog; @@ -733,7 +734,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.showmasktmmet = lltmMask; lp.showmaskblmet = llblMask; lp.showmask_met = ll_Mask; - printf("mask=%i \n", lp.showmask_met); + // printf("mask=%i \n", lp.showmask_met); lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible @@ -1114,6 +1115,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.Autogray = locallab.spots.at(sp).Autogray; lp.autocompute = locallab.spots.at(sp).autocompute; lp.baselog = (float) locallab.spots.at(sp).baselog; + lp.sensimas = locallab.spots.at(sp).sensimask; lp.deltaem = locallab.spots.at(sp).deltae; lp.scalereti = scaleret; @@ -3737,7 +3739,6 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int } if (deltaE || modmask || enaMask || showmaske) { - #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif @@ -3885,6 +3886,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int } if (locllmasCurve && llmasutili) { + // printf("s"); kmaskL = 32768.f * LIM01(kinv - kneg * locllmasCurve[(500.f / 32768.f) * bufcolorig->L[ir][jr]]); } @@ -5990,8 +5992,9 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag varsens = lp.senslc; } else if (senstype == 11) { //encoding log varsens = lp.sensilog; + } else if (senstype == 20) { //common mask + varsens = lp.sensimas; } - bool delt = lp.deltaem; //sobel @@ -6017,7 +6020,8 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); const bool lcshow = ((lp.showmasklcmet == 1 || lp.showmasklcmet == 2) && senstype == 10); const bool origshow = ((lp.showmasksoftmet == 5) && senstype == 3 && lp.softmet == 1); - + + const bool masshow = ((lp.showmask_met == 1) && senstype == 20); const bool previewvib = ((lp.showmaskvibmet == 4) && senstype == 2); const bool previewexp = ((lp.showmaskexpmet == 5) && senstype == 1); @@ -6026,6 +6030,7 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); const bool previewlc = ((lp.showmasklcmet == 4) && senstype == 10); const bool previeworig = ((lp.showmasksoftmet == 6) && senstype == 3 && lp.softmet == 1); + const bool previewmas = ((lp.showmask_met == 3) && senstype == 20); float radius = 3.f / sk; @@ -6063,7 +6068,8 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag const bool usemaskSH = (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 4) && senstype == 9; const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; const bool usemasklc = (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 4) && senstype == 10; - const bool usemaskall = (usemaskexp || usemaskvib || usemaskcol || usemaskSH || usemasktm || usemasklc); + const bool usemaskmas = (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 3) && senstype == 20; + const bool usemaskall = (usemaskexp || usemaskvib || usemaskcol || usemaskSH || usemasktm || usemasklc || usemaskmas); //blur a little mask if (usemaskall) { @@ -6248,13 +6254,13 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag const float difb = factorx * realstrbdE; float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); - if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow) && lp.colorde < 0) { //show modifications with use "b" + if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde < 0) { //show modifications with use "b" // (origshow && lp.colorde < 0) { //original Retinex transformed->a[y + ystart][x + xstart] = 0.f; transformed->b[y + ystart][x + xstart] = ampli * 8.f * diflc * reducdE; transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); - } else if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow) && lp.colorde > 0) {//show modifications without use "b" + } else if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde > 0) {//show modifications without use "b" if (diflc < 1000.f) {//if too low to be view use ab diflc += 0.5f * maxdifab; } @@ -6262,7 +6268,7 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); transformed->a[y + ystart][x + xstart] = clipC(ampli * difa); transformed->b[y + ystart][x + xstart] = clipC(ampli * difb); - } else if (previewexp || previewvib || previewcol || previewSH || previewtm || previewlc || previeworig || lp.prevdE) {//show deltaE + } else if (previewexp || previewvib || previewcol || previewSH || previewtm || previewlc || previeworig || previewmas || lp.prevdE) {//show deltaE float difbdisp = reducdE * 10000.f * lp.colorde; if (transformed->L[y + ystart][x + xstart] < darklim) { //enhance dark luminance as user can see! @@ -14465,6 +14471,132 @@ void ImProcFunctions::Lab_Local( } } } + +//begin common mask + if(lp.maskena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + if (bfw >= mSP && bfh >= mSP) { + float blurma = params->locallab.spots.at(sp).blurmask; + bool fftma = params->locallab.spots.at(sp).fftmask; + + if (blurma >= 0.25f && fftma && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + std::unique_ptr bufcolorigsav; + std::unique_ptr bufcolorig; + std::unique_ptr bufcolfin; + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + std::unique_ptr bufcolreserv; + std::unique_ptr buftemp; + + int wo = original->W; + int ho = original->H; + LabImage *origsav = nullptr; + origsav = new LabImage(wo, ho); + origsav->CopyFrom(original); + + if (call <= 3) { + bufcolorig.reset(new LabImage(bfw, bfh)); + bufcolfin.reset(new LabImage(bfw, bfh)); + buftemp.reset(new LabImage(bfw, bfh)); + bufcolorigsav.reset(new LabImage(bfw, bfh)); + + if (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 2 || lp.showmask_met == 3) { + bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); + originalmaskcol.reset(new LabImage(bfw, bfh)); + } +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; + + bufcolorigsav->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorigsav->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorigsav->b[y][x] = original->b[y + ystart][x + xstart]; + + bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; + buftemp->L[y][x] = original->L[y + ystart][x + xstart]; + buftemp->a[y][x] = original->a[y + ystart][x + xstart]; + buftemp->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + const int inv = 0; + const bool showmaske = lp.showmask_met == 2; + const bool enaMask = lp.ena_Mask; + const bool deltaE = lp.showmask_met == 3; + const bool modmask = lp.showmask_met == 1; + const bool zero = lp.showmask_met == 0; + const bool modif = lp.showmask_met == 1; + const float chrom = params->locallab.spots.at(sp).chromask; + const float rad = params->locallab.spots.at(sp).radmask; + const float gamma = params->locallab.spots.at(sp).gammask; + const float slope = params->locallab.spots.at(sp).slopmask; + const float blendm = params->locallab.spots.at(sp).blendmask; + const float lap = params->locallab.spots.at(sp).lapmask; + const bool pde = params->locallab.spots.at(sp).laplac; + const int shado = params->locallab.spots.at(sp).shadmask; + const int sco = params->locallab.spots.at(sp).scopemask; + const int level_bl = params->locallab.spots.at(sp).csthresholdmask.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdmask.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdmask.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdmask.getTopRight(); + const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskmask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float amountcd = 0.f; + const float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, astool, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, lochhhmas_Curve, lhhmas_utili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, + level_bl, level_hl, level_br, level_hr, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + if (lp.showmask_met == 2) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); + return; + } +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = bufcolorig->L[y][x]; + bufcolfin->a[y][x] = bufcolorig->a[y][x]; + bufcolfin->b[y][x] = bufcolorig->b[y][x]; + } + } + array2D blend2; + blend2(bfw, bfh); + float meansob = 0.f; + transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, origsav, transformed, cx, cy, sk); + delete origsav; + origsav = NULL; + } + } + } + +//end common mask // Gamut and Munsell control - very important do not deactivated to avoid crash if (params->locallab.spots.at(sp).avoid) { diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index e68a1b92c..f6a66242b 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1264,10 +1264,10 @@ private: locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); - locccmas_Curve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmas_utili); - locllmas_Curve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmas_utili); - lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmas_utili); - lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmas_utili); + locccmas_Curve.Set(params.locallab.spots.at(sp).CCmask_curve, lcmas_utili); + locllmas_Curve.Set(params.locallab.spots.at(sp).LLmask_curve, llmas_utili); + lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmask_curve, lhmas_utili); + lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmask_curve, lhhmas_utili); loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); From 155f91fcef188216db9ab9cea4bd8df87d34e070 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 23 Jun 2020 18:35:22 +0200 Subject: [PATCH 026/114] Fixed some bugs... --- rtengine/iplocallab.cc | 44 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index e00517959..b7bd88012 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -638,6 +638,9 @@ struct local_params { float residhi; float residhithr; bool blwh; + bool fftma; + float blurma; + float contma; }; @@ -1295,6 +1298,9 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.rewe = rewe; lp.senstm = local_sensitm; lp.amo = amo; + lp.blurma = (float) locallab.spots.at(sp).blurmask; + lp.fftma = locallab.spots.at(sp).fftmask; + lp.contma = (float) locallab.spots.at(sp).contmask; for (int y = 0; y < 6; y++) { lp.mulloc[y] = LIM(multi[y], 0.f, 4.f);//to prevent crash with old pp3 integer @@ -3755,7 +3761,21 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int JaggedArray blendstru(bfw, bfh); - if (lp.blurcolmask >= 0.25f && strumask == 0.f) { + float blu_ma = 0.f; + float cont_ma = 0.f; + bool fftt = false; + + if(lp.colorena) { + blu_ma = lp.blurcolmask; + cont_ma = lp.contcolmask; + fftt = lp.fftColorMask; + } else if(lp.maskena) { + blu_ma = lp.blurma; + cont_ma = lp.contma; + fftt = lp.fftma; + } + + if (blu_ma >= 0.25f && strumask == 0.f) { strumask = 0.1f; // to enable a small mask make FFT good ...why ?? } @@ -3781,15 +3801,15 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int JaggedArray blur(bfw, bfh); - if (lp.contcolmask > 0.f) { - float contra = lp.contcolmask; + if (cont_ma > 0.f) { + float contra = cont_ma; buildBlendMask(bufcolorig->L, blendblur, bfw, bfh, contra); float radblur = 0.25f + 0.002f * std::fabs(rad);//empirical value float rm = radblur / sk; - if (lp.fftColorMask) { + if (fftt) { if (rm < 0.3f) { rm = 0.3f; } @@ -3805,16 +3825,16 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int } } - if (lp.blurcolmask >= 0.25f) { - if (!lp.fftColorMask) { // || (lp.fftColorMask && call != 2)) { + if (blu_ma >= 0.25f) { + if (!fftt) { // || (lp.fftColorMask && call != 2)) { #ifdef _OPENMP #pragma omp parallel if (multiThread) #endif { - gaussianBlur(bufcolorig->L, blur, bfw, bfh, lp.blurcolmask / sk); + gaussianBlur(bufcolorig->L, blur, bfw, bfh, blu_ma / sk); } } else { - ImProcFunctions::fftw_convol_blur2(bufcolorig->L, blur, bfw, bfh, lp.blurcolmask / sk, 0, 0); + ImProcFunctions::fftw_convol_blur2(bufcolorig->L, blur, bfw, bfh, blu_ma / sk, 0, 0); } for (int i = 0; i < bfh; i++) { @@ -3875,9 +3895,9 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int kmasstru = bufcolorig->L[ir][jr] * blendstru[ir][jr]; } - if (lp.contcolmask > 0.f) { + if (cont_ma > 0.f) { - if (lp.blurcolmask >= 0.25f) { + if (blu_ma >= 0.25f) { float prov = intp(blendstru[ir][jr], bufcolorig->L[ir][jr], rtengine::max(blur[ir][jr], 0.0f)); kmasblur = bufcolorig->L[ir][jr] - prov ; @@ -14481,10 +14501,8 @@ void ImProcFunctions::Lab_Local( int bfh = yend - ystart; int bfw = xend - xstart; if (bfw >= mSP && bfh >= mSP) { - float blurma = params->locallab.spots.at(sp).blurmask; - bool fftma = params->locallab.spots.at(sp).fftmask; - if (blurma >= 0.25f && fftma && call == 2) { + if (lp.blurma >= 0.25f && lp.fftma && call == 2) { optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); } From ebb8b16583dceac8ac6651479d998c459c72ce60 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 24 Jun 2020 05:50:11 +0200 Subject: [PATCH 027/114] Improvment to maskcacol --- rtdata/languages/default | 2 +- rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 45 ++++++++++++++++------------------------ 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 297466f2a..82d5eecf5 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2717,7 +2717,7 @@ TP_LOCALLAB_SENSILOG;Scope TP_LOCALLAB_SENSIS;Scope TP_LOCALLAB_SENSIS_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. TP_LOCALLAB_SENSI_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. -TP_LOCALLAB_SENSIMASK_TOOLTIP;Adjust scope of action for this common mask tool.\nActs on the gap between the original image and the mask.\nThe references (luma, chroma, hue) are those of the center of the RT-spot +TP_LOCALLAB_SENSIMASK_TOOLTIP;Adjust scope of action for this common mask tool.\nActs on the gap between the original image and the mask.\nThe references (luma, chroma, hue) are those of the center of the RT-spot\n\nYou can also act on deltaE internal to the mask with 'Scope Mask deltaE image' in 'Settings' TP_LOCALLAB_SETTINGS;Settings TP_LOCALLAB_SH1;Shadows Highlights TP_LOCALLAB_SH2;Equalizer diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 31c8cdefc..e1c999891 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -243,7 +243,7 @@ public: const LUTf& lmasklocalcurve, bool localmaskutili, const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, - float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope); + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, bool fftt, float blu_ma, float cont_ma); void deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index b7bd88012..ccf6c81b0 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -3725,8 +3725,11 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int const LUTf& lmasklocalcurve, bool localmaskutili, const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, - float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, + bool fftt, float blu_ma, float cont_ma ) + + { array2D ble(bfw, bfh); array2D blechro(bfw, bfh); @@ -3761,20 +3764,6 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int JaggedArray blendstru(bfw, bfh); - float blu_ma = 0.f; - float cont_ma = 0.f; - bool fftt = false; - - if(lp.colorena) { - blu_ma = lp.blurcolmask; - cont_ma = lp.contcolmask; - fftt = lp.fftColorMask; - } else if(lp.maskena) { - blu_ma = lp.blurma; - cont_ma = lp.contma; - fftt = lp.fftma; - } - if (blu_ma >= 0.25f && strumask == 0.f) { strumask = 0.1f; // to enable a small mask make FFT good ...why ?? } @@ -10810,7 +10799,7 @@ void ImProcFunctions::Lab_Local( locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f ); if (lp.showmaskcbmet == 3) { @@ -11026,7 +11015,7 @@ void ImProcFunctions::Lab_Local( locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmaskvibmet == 3) { @@ -11257,7 +11246,7 @@ void ImProcFunctions::Lab_Local( locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmasktmmet == 3) { @@ -11298,7 +11287,7 @@ void ImProcFunctions::Lab_Local( locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmasktmmet == 3) {//display mask @@ -11481,7 +11470,7 @@ void ImProcFunctions::Lab_Local( locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmaskSHmet == 3) { @@ -11632,7 +11621,7 @@ void ImProcFunctions::Lab_Local( locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); @@ -11903,7 +11892,7 @@ void ImProcFunctions::Lab_Local( locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmasklcmet == 3) { @@ -13067,7 +13056,7 @@ void ImProcFunctions::Lab_Local( locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmaskexpmet == 3) { @@ -13331,7 +13320,7 @@ void ImProcFunctions::Lab_Local( locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f ); if (lp.showmaskexpmetinv == 1) { @@ -13519,7 +13508,7 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask ); if (lp.showmaskcolmet == 3) { @@ -14473,7 +14462,7 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, false, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask ); if (lp.showmaskcolmetinv == 1) { @@ -14588,8 +14577,10 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma ); + + if (lp.showmask_met == 2) { showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); return; From 344864a8ecb32e1df0224084d3ae9f45662613db Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 24 Jun 2020 07:31:48 +0200 Subject: [PATCH 028/114] Added Graduated Filter luminance common mask --- rtdata/languages/default | 2 ++ rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 40 +++++++++++++++++++++------------- rtengine/procevents.h | 2 ++ rtengine/procparams.cc | 8 +++++++ rtengine/procparams.h | 2 ++ rtengine/refreshmap.cc | 4 +++- rtgui/locallabtools.h | 3 +++ rtgui/locallabtools2.cc | 46 +++++++++++++++++++++++++++++++++++++++- rtgui/paramsedited.cc | 14 ++++++++++++ rtgui/paramsedited.h | 2 ++ 11 files changed, 107 insertions(+), 18 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 82d5eecf5..28573ee52 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1187,6 +1187,8 @@ HISTORY_MSG_946;Local - Mask Common shadows HISTORY_MSG_947;Local - Mask Common Contrast curve HISTORY_MSG_948;Local - Mask Common Wavelet curve HISTORY_MSG_949;Local - Mask Common Threshold levels +HISTORY_MSG_950;Local - Mask Common GF strength +HISTORY_MSG_951;Local - Mask Common GF angle HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index e1c999891..682c97b90 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -243,7 +243,7 @@ public: const LUTf& lmasklocalcurve, bool localmaskutili, const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, - float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, bool fftt, float blu_ma, float cont_ma); + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, bool fftt, float blu_ma, float cont_ma, int indic); void deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index ccf6c81b0..ab4aa4d80 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -406,6 +406,8 @@ struct local_params { float slomaexp; float strmaexp; float angmaexp; + float str_mas; + float ang_mas; float strexp; float angexp; float strSH; @@ -1009,6 +1011,8 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall float slomaskexpo = ((float) locallab.spots.at(sp).slomaskexp); float strmaskexpo = ((float) locallab.spots.at(sp).strmaskexp); float angmaskexpo = ((float) locallab.spots.at(sp).angmaskexp); + float strmask = ((float) locallab.spots.at(sp).str_mask); + float angmask = ((float) locallab.spots.at(sp).ang_mask); float strexpo = ((float) locallab.spots.at(sp).strexp); float angexpo = ((float) locallab.spots.at(sp).angexp); float strSH = ((float) locallab.spots.at(sp).strSH); @@ -1147,6 +1151,9 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.slomaexp = slomaskexpo; lp.strmaexp = strmaskexpo; lp.angmaexp = angmaskexpo; + lp.str_mas = strmask; + lp.ang_mas = angmask; + lp.strexp = strexpo; lp.angexp = angexpo; lp.strSH = strSH; @@ -3068,6 +3075,9 @@ void calclocalGradientParams(const struct local_params& lp, struct grad_params& } else if (indic == 11) { stops = lp.strlog; angs = lp.anglog; + } else if (indic == 12) { + stops = -lp.str_mas; + angs = lp.ang_mas; } @@ -3726,7 +3736,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, - bool fftt, float blu_ma, float cont_ma + bool fftt, float blu_ma, float cont_ma, int indic ) @@ -4244,8 +4254,8 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int struct grad_params gp; - if (lp.strmaexp != 0.f) { - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 0); + if ((indic == 0 && lp.strmaexp != 0.f) || (indic ==12 && lp.str_mas != 0.f)) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, indic); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif @@ -10799,7 +10809,7 @@ void ImProcFunctions::Lab_Local( locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f, -1 ); if (lp.showmaskcbmet == 3) { @@ -11015,7 +11025,7 @@ void ImProcFunctions::Lab_Local( locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); if (lp.showmaskvibmet == 3) { @@ -11246,7 +11256,7 @@ void ImProcFunctions::Lab_Local( locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); if (lp.showmasktmmet == 3) { @@ -11287,7 +11297,7 @@ void ImProcFunctions::Lab_Local( locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); if (lp.showmasktmmet == 3) {//display mask @@ -11470,7 +11480,7 @@ void ImProcFunctions::Lab_Local( locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); if (lp.showmaskSHmet == 3) { @@ -11621,7 +11631,7 @@ void ImProcFunctions::Lab_Local( locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11892,7 +11902,7 @@ void ImProcFunctions::Lab_Local( locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); if (lp.showmasklcmet == 3) { @@ -13056,7 +13066,7 @@ void ImProcFunctions::Lab_Local( locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 ); if (lp.showmaskexpmet == 3) { @@ -13320,7 +13330,7 @@ void ImProcFunctions::Lab_Local( locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 ); if (lp.showmaskexpmetinv == 1) { @@ -13508,7 +13518,7 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 ); if (lp.showmaskcolmet == 3) { @@ -14462,7 +14472,7 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, false, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 ); if (lp.showmaskcolmetinv == 1) { @@ -14577,7 +14587,7 @@ void ImProcFunctions::Lab_Local( enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma, 12 ); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 63d8d7082..8fd00238c 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -972,6 +972,8 @@ enum ProcEventCode { EvlocallabLmask_shape = 946, EvlocallabLLmask_shapewav = 947, EvlocallabcsThresholdmask = 948, + Evlocallabstr_mask = 949, + Evlocallabang_mask = 950, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 31173afeb..e7267bc30 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3796,6 +3796,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : gammask(1.0), slopmask(0.0), shadmask(0.0), + str_mask(0), + ang_mask(0), HHhmask_curve{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -4322,6 +4324,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && gammask == other.gammask && slopmask == other.slopmask && shadmask == other.shadmask + && str_mask == other.str_mask + && ang_mask == other.ang_mask && HHhmask_curve == other.HHhmask_curve && Lmask_curve == other.Lmask_curve && LLmask_curvewav == other.LLmask_curvewav @@ -5841,6 +5845,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->gammask, "Locallab", "Gammask_" + index_str, spot.gammask, keyFile); saveToKeyfile(!pedited || spot_edited->slopmask, "Locallab", "Slopmask_" + index_str, spot.slopmask, keyFile); saveToKeyfile(!pedited || spot_edited->shadmask, "Locallab", "Shadmask_" + index_str, spot.shadmask, keyFile); + saveToKeyfile(!pedited || spot_edited->str_mask, "Locallab", "Str_mask_" + index_str, spot.str_mask, keyFile); + saveToKeyfile(!pedited || spot_edited->ang_mask, "Locallab", "Ang_mask_" + index_str, spot.ang_mask, keyFile); saveToKeyfile(!pedited || spot_edited->HHhmask_curve, "Locallab", "HHhmask_Curve_" + index_str, spot.HHhmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->Lmask_curve, "Locallab", "Lmask_Curve_" + index_str, spot.Lmask_curve, keyFile); saveToKeyfile(!pedited || spot_edited->LLmask_curvewav, "Locallab", "LLmask_Curvewav_" + index_str, spot.LLmask_curvewav, keyFile); @@ -7579,6 +7585,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Gammask_" + index_str, pedited, spot.gammask, spotEdited.gammask); assignFromKeyfile(keyFile, "Locallab", "Slopmask_" + index_str, pedited, spot.slopmask, spotEdited.slopmask); assignFromKeyfile(keyFile, "Locallab", "Shadmask_" + index_str, pedited, spot.shadmask, spotEdited.shadmask); + assignFromKeyfile(keyFile, "Locallab", "Str_mask_" + index_str, pedited, spot.str_mask, spotEdited.str_mask); + assignFromKeyfile(keyFile, "Locallab", "Ang_mask_" + index_str, pedited, spot.ang_mask, spotEdited.ang_mask); assignFromKeyfile(keyFile, "Locallab", "HHhmask_Curve_" + index_str, pedited, spot.HHhmask_curve, spotEdited.HHhmask_curve); assignFromKeyfile(keyFile, "Locallab", "Lmask_Curve_" + index_str, pedited, spot.Lmask_curve, spotEdited.Lmask_curve); assignFromKeyfile(keyFile, "Locallab", "LLmask_Curvewav_" + index_str, pedited, spot.LLmask_curvewav, spotEdited.LLmask_curvewav); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 22a33d52a..f50f96058 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1442,6 +1442,8 @@ struct LocallabParams { double gammask; double slopmask; double shadmask; + int str_mask; + int ang_mask; std::vector HHhmask_curve; std::vector Lmask_curve; std::vector LLmask_curvewav; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index d145b3f38..9dde3a160 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -975,7 +975,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabshadmask LUMINANCECURVE, // EvlocallabLmask_shape LUMINANCECURVE, // EvlocallabLLmask_shapewav - LUMINANCECURVE // EvlocallabcsThresholdmask + LUMINANCECURVE, // EvlocallabcsThresholdmask + LUMINANCECURVE, // Evlocallabstr_mask + LUMINANCECURVE // Evlocallabang_mask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 32b833d04..6e1a0df3f 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1233,6 +1233,9 @@ private: CurveEditorGroup* const mask2CurveEditorGwav; FlatCurveEditor* const LLmask_shapewav; ThresholdAdjuster* const csThresholdmask; + Gtk::Frame* const gradFramemask; + Adjuster* const str_mask; + Adjuster* const ang_mask; sigc::connection showmask_MethodConn, enamaskConn, toolmaskConn, fftmaskConn; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 52526cf5f..6b64a97e3 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4760,7 +4760,10 @@ LocallabMask::LocallabMask(): Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), LLmask_shapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), - csThresholdmask(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) + csThresholdmask(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))), + gradFramemask(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADFRA")))), + str_mask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2., 2., 0.05, 0.))), + ang_mask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180., 180., 0.1, 0.))) { // Parameter Mask common specific widgets @@ -4861,6 +4864,18 @@ LocallabMask::LocallabMask(): blurFrame->add(*blurmBox); maskmaskBox->pack_start(*blurFrame, Gtk::PACK_SHRINK, 0); + + gradFramemask->set_label_align(0.025, 0.5); + + str_mask->setAdjusterListener(this); + + ang_mask->setAdjusterListener(this); + ang_mask->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + ToolParamBlock* const gradmaskBox = Gtk::manage(new ToolParamBlock()); + gradmaskBox->pack_start(*str_mask); + gradmaskBox->pack_start(*ang_mask); + gradFramemask->add(*gradmaskBox); + Gtk::Frame* const toolmaskFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK"))); toolmaskFrame->set_label_align(0.025, 0.5); ToolParamBlock* const toolmaskBox = Gtk::manage(new ToolParamBlock()); @@ -4875,6 +4890,7 @@ LocallabMask::LocallabMask(): toolmaskBox->pack_start(*mask2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskBox->pack_start(*mask2CurveEditorGwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor toolmaskBox->pack_start(*csThresholdmask, Gtk::PACK_SHRINK, 0); + toolmaskBox->pack_start(*gradFramemask, Gtk::PACK_SHRINK, 0); toolmaskFrame->add(*toolmaskBox); maskmaskBox->pack_start(*toolmaskFrame); pack_start(*maskmaskBox); @@ -5014,6 +5030,8 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params gammask->setValue(spot.gammask); slopmask->setValue(spot.slopmask); shadmask->setValue(spot.shadmask); + str_mask->setValue(spot.str_mask); + ang_mask->setValue(spot.ang_mask); HHhmask_shape->setCurve(spot.HHhmask_curve); fftmask->set_active(spot.fftmask); Lmask_shape->setCurve(spot.Lmask_curve); @@ -5057,6 +5075,8 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.gammask = gammask->getValue(); spot.slopmask = slopmask->getValue(); spot.shadmask = shadmask->getValue(); + spot.str_mask = str_mask->getIntValue(); + spot.ang_mask = ang_mask->getIntValue(); spot.HHhmask_curve = HHhmask_shape->getCurve(); spot.fftmask = fftmask->get_active(); spot.contmask = contmask->getValue(); @@ -5105,6 +5125,8 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams lapmask->setDefault(defSpot.lapmask); slopmask->setDefault(defSpot.slopmask); shadmask->setDefault(defSpot.shadmask); + str_mask->setDefault(defSpot.str_mask); + ang_mask->setDefault(defSpot.ang_mask); HHhmask_shape->setCurve(defSpot.HHhmask_curve); contmask->setDefault(defSpot.contmask); blurmask->setDefault(defSpot.blurmask); @@ -5123,8 +5145,11 @@ void LocallabMask::updateGUIToMode(const modeType new_type) gammask->hide(); slopmask->hide(); shadmask->hide(); + str_mask->hide(); + ang_mask->hide(); struFrame->hide(); blurFrame->hide(); + gradFramemask->hide(); mask_HCurveEditorG->hide(); // mask2CurveEditorG->hide(); mask2CurveEditorGwav->hide(); @@ -5136,8 +5161,11 @@ void LocallabMask::updateGUIToMode(const modeType new_type) gammask->show(); slopmask->show(); shadmask->show(); + str_mask->show(); + ang_mask->show(); struFrame->show(); blurFrame->show(); + gradFramemask->show(); mask_HCurveEditorG->show(); // mask2CurveEditorG->show(); mask2CurveEditorGwav->show(); @@ -5158,6 +5186,8 @@ void LocallabMask::convertParamToNormal() gammask->setValue(defSpot.gammask); slopmask->setValue(defSpot.slopmask); shadmask->setValue(defSpot.shadmask); + str_mask->setValue(defSpot.str_mask); + ang_mask->setValue(defSpot.ang_mask); strumaskmask->setValue(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); fftmask->set_active(defSpot.fftmask); @@ -5358,6 +5388,20 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == str_mask) { + if (listener) { + listener->panelChanged(Evlocallabstr_mask, + str_mask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == ang_mask) { + if (listener) { + listener->panelChanged(Evlocallabang_mask, + ang_mask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 6349b84e0..84a283ea9 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1498,6 +1498,8 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).gammask = locallab.spots.at(j).gammask && pSpot.gammask == otherSpot.gammask; locallab.spots.at(j).slopmask = locallab.spots.at(j).slopmask && pSpot.slopmask == otherSpot.slopmask; locallab.spots.at(j).shadmask = locallab.spots.at(j).shadmask && pSpot.shadmask == otherSpot.shadmask; + locallab.spots.at(j).str_mask = locallab.spots.at(j).str_mask && pSpot.str_mask == otherSpot.str_mask; + locallab.spots.at(j).ang_mask = locallab.spots.at(j).ang_mask && pSpot.ang_mask == otherSpot.ang_mask; locallab.spots.at(j).HHhmask_curve = locallab.spots.at(j).HHhmask_curve && pSpot.HHhmask_curve == otherSpot.HHhmask_curve; locallab.spots.at(j).Lmask_curve = locallab.spots.at(j).Lmask_curve && pSpot.Lmask_curve == otherSpot.Lmask_curve; locallab.spots.at(j).LLmask_curvewav = locallab.spots.at(j).LLmask_curvewav && pSpot.LLmask_curvewav == otherSpot.LLmask_curvewav; @@ -4888,6 +4890,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).shadmask = mods.locallab.spots.at(i).shadmask; } + if (locallab.spots.at(i).str_mask) { + toEdit.locallab.spots.at(i).str_mask = mods.locallab.spots.at(i).str_mask; + } + + if (locallab.spots.at(i).ang_mask) { + toEdit.locallab.spots.at(i).ang_mask = mods.locallab.spots.at(i).ang_mask; + } + if (locallab.spots.at(i).HHhmask_curve) { toEdit.locallab.spots.at(i).HHhmask_curve = mods.locallab.spots.at(i).HHhmask_curve; } @@ -6494,6 +6504,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : gammask(v), slopmask(v), shadmask(v), + str_mask(v), + ang_mask(v), HHhmask_curve(v), Lmask_curve(v), LLmask_curvewav(v), @@ -6980,6 +6992,8 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) gammask = v; slopmask = v; shadmask = v; + str_mask = v; + ang_mask = v; HHhmask_curve =(v); Lmask_curve =(v); LLmask_curvewav =(v); diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index dea2a4a9f..b58a96b46 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -856,6 +856,8 @@ public: bool gammask; bool slopmask; bool shadmask; + bool str_mask; + bool ang_mask; bool HHhmask_curve; bool Lmask_curve; bool LLmask_curvewav; From e324d6d960e092bff49290a48d5f1d27419ad513 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 24 Jun 2020 17:18:39 +0200 Subject: [PATCH 029/114] Added forgotten recursive to common mask --- rtengine/iplocallab.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index ab4aa4d80..616cfa859 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -47,7 +47,7 @@ #include "cplx_wavelet_dec.h" #include "ciecam02.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" #include "guidedfilter.h" @@ -14611,6 +14611,13 @@ void ImProcFunctions::Lab_Local( transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, origsav, transformed, cx, cy, sk); delete origsav; origsav = NULL; + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } } } From 339d8de30de3e40e0c7842dd68ce4816da893a81 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 24 Jun 2020 19:11:17 +0200 Subject: [PATCH 030/114] Suppress unused variables --- rtengine/iplocallab.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 616cfa859..fe0c0a7fd 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -14511,7 +14511,6 @@ void ImProcFunctions::Lab_Local( std::unique_ptr bufmaskblurcol; std::unique_ptr originalmaskcol; std::unique_ptr bufcolreserv; - std::unique_ptr buftemp; int wo = original->W; int ho = original->H; @@ -14522,7 +14521,6 @@ void ImProcFunctions::Lab_Local( if (call <= 3) { bufcolorig.reset(new LabImage(bfw, bfh)); bufcolfin.reset(new LabImage(bfw, bfh)); - buftemp.reset(new LabImage(bfw, bfh)); bufcolorigsav.reset(new LabImage(bfw, bfh)); if (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 2 || lp.showmask_met == 3) { @@ -14545,9 +14543,6 @@ void ImProcFunctions::Lab_Local( bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; - buftemp->L[y][x] = original->L[y + ystart][x + xstart]; - buftemp->a[y][x] = original->a[y + ystart][x + xstart]; - buftemp->b[y][x] = original->b[y + ystart][x + xstart]; } } const int inv = 0; @@ -14605,10 +14600,9 @@ void ImProcFunctions::Lab_Local( bufcolfin->b[y][x] = bufcolorig->b[y][x]; } } - array2D blend2; - blend2(bfw, bfh); + //perhaps we can put here a softproc to reduce artifacts between bufcolorigsav and bufcolfin, just a slider... ?? but is it necessary with this type of change ?? float meansob = 0.f; - transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, origsav, transformed, cx, cy, sk); + transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); delete origsav; origsav = NULL; From bbb3300831c7332a84579db86cb6aff0bd5c7694 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 24 Jun 2020 19:44:51 +0200 Subject: [PATCH 031/114] Added soft radius common mask --- rtdata/languages/default | 1 + rtengine/iplocallab.cc | 5 +++++ rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 ++++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 4 ++-- rtgui/locallabtools.h | 2 ++ rtgui/locallabtools2.cc | 15 ++++++++++++++- rtgui/paramsedited.cc | 7 +++++++ rtgui/paramsedited.h | 1 + 10 files changed, 38 insertions(+), 3 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 28573ee52..2d64b34be 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1189,6 +1189,7 @@ HISTORY_MSG_948;Local - Mask Common Wavelet curve HISTORY_MSG_949;Local - Mask Common Threshold levels HISTORY_MSG_950;Local - Mask Common GF strength HISTORY_MSG_951;Local - Mask Common GF angle +HISTORY_MSG_952;Local - Mask Common soft radius HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index fe0c0a7fd..50e7a4177 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -14568,6 +14568,7 @@ void ImProcFunctions::Lab_Local( const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; const int lumask = params->locallab.spots.at(sp).lumask; const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskmask; + const float softr = params->locallab.spots.at(sp).softradiusmask; const float mindE = 2.f + MINSCOPE * sco * lp.thr; const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); @@ -14601,6 +14602,10 @@ void ImProcFunctions::Lab_Local( } } //perhaps we can put here a softproc to reduce artifacts between bufcolorigsav and bufcolfin, just a slider... ?? but is it necessary with this type of change ?? + if (softr > 0.f) { + softproc(bufcolorigsav.get(), bufcolfin.get(), softr, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + } + float meansob = 0.f; transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); delete origsav; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 8fd00238c..cea95c451 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -974,6 +974,7 @@ enum ProcEventCode { EvlocallabcsThresholdmask = 948, Evlocallabstr_mask = 949, Evlocallabang_mask = 950, + Evlocallabsoftradiusmask = 951, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index e7267bc30..88f474a84 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3739,6 +3739,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : expmask(false), sensimask(60), blendmask(0), + softradiusmask(0.0), enamask(false), fftmask(true), blurmask(0.2), @@ -4309,6 +4310,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && expmask == other.expmask && sensimask == other.sensimask && blendmask == other.blendmask + && softradiusmask == other.softradiusmask && enamask == other.enamask && fftmask == other.fftmask && blurmask == other.blurmask @@ -5830,6 +5832,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->complexmask, "Locallab", "Complexmask_" + index_str, spot.complexmask, keyFile); saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiusmask, "Locallab", "Softradiusmask_" + index_str, spot.softradiusmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); saveToKeyfile(!pedited || spot_edited->fftmask, "Locallab", "Fftmask_" + index_str, spot.fftmask, keyFile); saveToKeyfile(!pedited || spot_edited->blurmask, "Locallab", "Blurmask_" + index_str, spot.blurmask, keyFile); @@ -7570,6 +7573,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Complexmask_" + index_str, pedited, spot.complexmask, spotEdited.complexmask); assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); + assignFromKeyfile(keyFile, "Locallab", "Softradiusmask_" + index_str, pedited, spot.softradiusmask, spotEdited.softradiusmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); assignFromKeyfile(keyFile, "Locallab", "Fftmask_" + index_str, pedited, spot.fftmask, spotEdited.fftmask); assignFromKeyfile(keyFile, "Locallab", "Blurmask_" + index_str, pedited, spot.blurmask, spotEdited.blurmask); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index f50f96058..08fc55d02 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1427,6 +1427,7 @@ struct LocallabParams { bool expmask; int sensimask; int blendmask; + double softradiusmask; bool enamask; bool fftmask; double blurmask; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 9dde3a160..b3a111b1b 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -977,8 +977,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabLLmask_shapewav LUMINANCECURVE, // EvlocallabcsThresholdmask LUMINANCECURVE, // Evlocallabstr_mask - LUMINANCECURVE // Evlocallabang_mask - + LUMINANCECURVE, // Evlocallabang_mask + LUMINANCECURVE // Evlocallabsoftradiusmask }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 6e1a0df3f..4171c30b8 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1206,6 +1206,8 @@ class LocallabMask: private: Adjuster* const sensimask; Adjuster* const blendmask; + Adjuster* const softradiusmask; + MyComboBoxText* const showmask_Method; Gtk::CheckButton* const enamask; CurveEditorGroup* const mask_CurveEditorG; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 6b64a97e3..b27eb3aeb 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4732,6 +4732,8 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), mask_CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), @@ -4771,6 +4773,7 @@ LocallabMask::LocallabMask(): sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); + softradiusmask->setAdjusterListener(this); showmask_Method->append(M("TP_LOCALLAB_SHOWMNONE")); showmask_Method->append(M("TP_LOCALLAB_SHOWMODIFMASK")); showmask_Method->append(M("TP_LOCALLAB_SHOWMASK")); @@ -4844,7 +4847,7 @@ LocallabMask::LocallabMask(): pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); - + pack_start(*softradiusmask, Gtk::PACK_SHRINK, 0); ToolParamBlock* const maskmaskBox = Gtk::manage(new ToolParamBlock()); maskmaskBox->pack_start(*showmask_Method, Gtk::PACK_SHRINK, 4); maskmaskBox->pack_start(*mask_CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor @@ -5018,6 +5021,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params blurmask->setValue(spot.blurmask); blendmask->setValue(spot.blendmask); + softradiusmask->setValue(spot.softradiusmask); enamask->set_active(spot.enamask); CCmask_shape->setCurve(spot.CCmask_curve); LLmask_shape->setCurve(spot.LLmask_curve); @@ -5063,6 +5067,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); + spot.softradiusmask = softradiusmask->getValue(); spot.enamask = enamask->get_active(); spot.CCmask_curve = CCmask_shape->getCurve(); spot.LLmask_curve = LLmask_shape->getCurve(); @@ -5119,6 +5124,7 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams strumaskmask->setDefault(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); blendmask->setDefault((double)defSpot.blendmask); + softradiusmask->setDefault((double)defSpot.softradiusmask); radmask->setDefault(defSpot.radmask); lapmask->setDefault(defSpot.lapmask); chromask->setDefault(defSpot.chromask); @@ -5346,6 +5352,13 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == softradiusmask) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiusmask, + softradiusmask->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + if (a == radmask) { if (listener) { listener->panelChanged(Evlocallabradmask, diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 84a283ea9..2031a7e8a 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1483,6 +1483,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; + locallab.spots.at(j).softradiusmask = locallab.spots.at(j).softradiusmask && pSpot.softradiusmask == otherSpot.softradiusmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; locallab.spots.at(j).fftmask = locallab.spots.at(j).fftmask && pSpot.fftmask == otherSpot.fftmask; locallab.spots.at(j).blurmask = locallab.spots.at(j).blurmask && pSpot.blurmask == otherSpot.blurmask; @@ -4830,6 +4831,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).blendmask = mods.locallab.spots.at(i).blendmask; } + if (locallab.spots.at(i).softradiusmask) { + toEdit.locallab.spots.at(i).softradiusmask = mods.locallab.spots.at(i).softradiusmask; + } + if (locallab.spots.at(i).enamask) { toEdit.locallab.spots.at(i).enamask = mods.locallab.spots.at(i).enamask; } @@ -6489,6 +6494,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : expmask(v), sensimask(v), blendmask(v), + softradiusmask(v), enamask(v), fftmask(v), blurmask(v), @@ -6977,6 +6983,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) expmask = v; sensimask = v; blendmask = v; + softradiusmask = v; enamask = v; fftmask = v; blurmask = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b58a96b46..e6e6a2b2b 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -841,6 +841,7 @@ public: bool expmask; bool sensimask; bool blendmask; + bool softradiusmask; bool enamask; bool fftmask; bool blurmask; From 96296015ce5690e418713bbdd2336e4f151837c1 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 25 Jun 2020 08:30:45 +0200 Subject: [PATCH 032/114] Improve soft radius with color --- rtengine/iplocallab.cc | 63 ++++++++++++++++++++++++++++++++--------- rtgui/locallabtools2.cc | 3 +- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 50e7a4177..4a3edb017 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -47,7 +47,7 @@ #include "cplx_wavelet_dec.h" #include "ciecam02.h" -#define BENCHMARK +//#define BENCHMARK #include "StopWatch.h" #include "guidedfilter.h" @@ -2300,7 +2300,7 @@ void ImProcFunctions::ciecamloc_02float(int sp, LabImage* lab) void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufcolfin, float rad, int bfh, int bfw, float epsilmax, float epsilmin, float thres, int sk, bool multiThread, int flag) { - if (rad > 0.f) { + if (rad != 0.f) { array2D ble(bfw, bfh); array2D guid(bfw, bfh); if (flag == 0) { @@ -2315,9 +2315,10 @@ void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufco } } - const float aepsil = (epsilmax - epsilmin) / 90.f; - const float bepsil = epsilmax - 100.f * aepsil; - const float epsil = aepsil * 0.1f * rad + bepsil; + const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + // const float epsil = aepsil * 0.1f * rad + bepsil; + const float epsil = aepsil * rad + bepsil; const float blur = 10.f / sk * (thres + 0.8f * rad); rtengine::guidedFilter(guid, ble, ble, blur, epsil, multiThread, 4); @@ -2361,6 +2362,7 @@ void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufco } } + void ImProcFunctions::softprocess(const LabImage* bufcolorig, array2D &buflight, float rad, int bfh, int bfw, double epsilmax, double epsilmin, float thres, int sk, bool multiThread) { float minlig = buflight[0][0]; @@ -14504,6 +14506,10 @@ void ImProcFunctions::Lab_Local( if (lp.blurma >= 0.25f && lp.fftma && call == 2) { optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); } + array2D blechro(bfw, bfh); + array2D ble(bfw, bfh); + array2D hue(bfw, bfh); + array2D guid(bfw, bfh); std::unique_ptr bufcolorigsav; std::unique_ptr bufcolorig; @@ -14599,17 +14605,48 @@ void ImProcFunctions::Lab_Local( bufcolfin->L[y][x] = bufcolorig->L[y][x]; bufcolfin->a[y][x] = bufcolorig->a[y][x]; bufcolfin->b[y][x] = bufcolorig->b[y][x]; + hue[y][x] = xatan2f(bufcolfin->b[y][x], bufcolfin->a[y][x]); + const float chromah = std::sqrt(SQR(bufcolfin->b[y][x]) + SQR(bufcolfin->a[y][x])); + ble[y][x] = bufcolfin->L[y][x] / 32768.f; + blechro[y][x] = chromah / 32768.f; + guid[y][x] = bufcolorigsav->L[y][x] / 32768.f; } } - //perhaps we can put here a softproc to reduce artifacts between bufcolorigsav and bufcolfin, just a slider... ?? but is it necessary with this type of change ?? - if (softr > 0.f) { - softproc(bufcolorigsav.get(), bufcolfin.get(), softr, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); - } + if (softr != 0.f) {//soft for L a b because we change color... + float rad = softr; + const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); + const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); - float meansob = 0.f; - transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); - delete origsav; - origsav = NULL; + constexpr float epsilmax = 0.0008f; + constexpr float epsilmin = 0.00001f; + + constexpr float aepsil = (epsilmax - epsilmin) / 1000.f; + constexpr float bepsil = epsilmin; + const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + float2 sincosval = xsincosf(hue[y][x]); + bufcolfin->L[y][x] = 32768.f * ble[y][x]; + bufcolfin->a[y][x] = 32768.f * blechro[y][x] * sincosval.y; + bufcolfin->b[y][x] = 32768.f * blechro[y][x] * sincosval.x; + } + } + } + + + + float meansob = 0.f; + transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); + delete origsav; + origsav = NULL; if (params->locallab.spots.at(sp).recurs) { original->CopyFrom(transformed, multiThread); diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index b27eb3aeb..bc859cd70 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4732,7 +4732,7 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 1000.0, 0.5, 0.))), showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), @@ -4773,6 +4773,7 @@ LocallabMask::LocallabMask(): sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); + softradiusmask->setLogScale(10, -10); softradiusmask->setAdjusterListener(this); showmask_Method->append(M("TP_LOCALLAB_SHOWMNONE")); showmask_Method->append(M("TP_LOCALLAB_SHOWMODIFMASK")); From eb509889d979da6f3b1ef4aa03c0d293840670f5 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 25 Jun 2020 10:18:14 +0200 Subject: [PATCH 033/114] various change to soft process --- rtengine/iplocallab.cc | 32 ++++++++++++++++---------------- rtengine/ipretinex.cc | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 4a3edb017..ee3005813 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2342,9 +2342,9 @@ void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufco guid[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; } - const float aepsil = (epsilmax - epsilmin) / 90.f; - const float bepsil = epsilmax - 100.f * aepsil; - const float epsil = rad < 0.f ? 0.0001f : aepsil * 0.1f * rad + bepsil; + const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.0001f : aepsil * rad + bepsil; const float blur = rad < 0.f ? -1.f / rad : 1.f + rad; const int r2 = rtengine::max(int(25 / sk * blur + 0.5f), 1); @@ -4002,12 +4002,12 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); - constexpr float epsilmax = 0.0005f; + constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; - constexpr float aepsil = (epsilmax - epsilmin) / 90.f; - constexpr float bepsil = epsilmax - 100.f * aepsil; - const float epsil = rad < 0.f ? 0.001f : aepsil * 0.1f * rad + bepsil; + constexpr float aepsil = (epsilmax - epsilmin) / 1000.f; + constexpr float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); @@ -9880,11 +9880,11 @@ void ImProcFunctions::Lab_Local( const int r1 = rtengine::max(4 / sk * blur + 0.5f, 1); const int r2 = rtengine::max(25 / sk * blur + 0.5f, 1); - constexpr float epsilmax = 0.0005f; + constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; - const float aepsil = (epsilmax - epsilmin) / 90.f; - const float bepsil = epsilmax - 100.f * aepsil; + const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; const float epsil = lp.radmabl < 0.f ? 0.001f : aepsil * lp.radmabl + bepsil; rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); @@ -10851,7 +10851,7 @@ void ImProcFunctions::Lab_Local( ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, lp.mulloc, 1.f, lp.threshol, lp.clarityml, lp.contresid, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); if (lp.softradiuscb > 0.f) { - softproc(origcbdl.get(), loctemp.get(), lp.softradiuscb, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + softproc(origcbdl.get(), loctemp.get(), lp.softradiuscb, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); } } @@ -12173,7 +12173,7 @@ void ImProcFunctions::Lab_Local( } if (softr != 0.f && (compreena || locwavCurve || comprena || blurena || levelena || lp.wavgradl || lp.edgwena || std::fabs(mL) > 0.001f)) { - softproc(tmpres.get(), tmp1.get(), softr, bfh, bfw, 0.0001, 0.00001, thr, sk, multiThread, flag); + softproc(tmpres.get(), tmp1.get(), softr, bfh, bfw, 0.001, 0.00001, thr, sk, multiThread, flag); } } } @@ -13259,7 +13259,7 @@ void ImProcFunctions::Lab_Local( } if (lp.softradiusexp > 0.f && lp.expmet == 0) { - softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); } float meansob = 0.f; transit_shapedetect2(call, 1, bufexporig.get(), bufexpfin.get(), originalmaskexp.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); @@ -14306,7 +14306,7 @@ void ImProcFunctions::Lab_Local( } if (lp.softradiuscol > 0.f) { - softproc(bufcolreserv.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + softproc(bufcolreserv.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); } float meansob = 0.f; transit_shapedetect2(call, 0, bufcolreserv.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); @@ -14379,7 +14379,7 @@ void ImProcFunctions::Lab_Local( if (lp.softradiuscol > 0.f) { - softproc(bufcolorig.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + softproc(bufcolorig.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); } float meansob = 0.f; transit_shapedetect2(call, 0, bufcolorig.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); @@ -14618,7 +14618,7 @@ void ImProcFunctions::Lab_Local( const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); - constexpr float epsilmax = 0.0008f; + constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; constexpr float aepsil = (epsilmax - epsilmin) / 1000.f; diff --git a/rtengine/ipretinex.cc b/rtengine/ipretinex.cc index 02d04c270..101b3726d 100644 --- a/rtengine/ipretinex.cc +++ b/rtengine/ipretinex.cc @@ -920,12 +920,12 @@ void ImProcFunctions::maskforretinex(int sp, int before, float ** luminance, flo int r1 = max(int(4 / skip * blur + 0.5), 1); int r2 = max(int(25 / skip * blur + 0.5), 1); - double epsilmax = 0.0005; + double epsilmax = 0.005; double epsilmin = 0.00001; - double aepsil = (epsilmax - epsilmin) / 90.f; - double bepsil = epsilmax - 100.f * aepsil; - double epsil = aepsil * 0.1 * rad + bepsil; + double aepsil = (epsilmax - epsilmin) / 1000.f; + double bepsil = epsilmin; //epsilmax - 100.f * aepsil; + double epsil = aepsil * rad + bepsil; if (rad < 0.f) { epsil = 0.001; } From 1404c96ba09d8d5e431ea8c5dcf4484da80622e9 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 06:12:13 +0200 Subject: [PATCH 034/114] push change reviewed by Pandagrapher in locallabtools2 --- rtgui/locallabtools2.cc | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index bc859cd70..5efafcb5b 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4940,19 +4940,6 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) } else { exp->set_tooltip_text(M("")); - sensimask->set_tooltip_text(M("")); - blendmask->set_tooltip_text(M("")); - CCmask_shape->setTooltip(M("")); - LLmask_shape->setTooltip(M("")); - HHmask_shape->setTooltip(M("")); - struFrame->set_tooltip_text(M("")); - mask_HCurveEditorG->set_tooltip_text(M("")); - radmask->set_tooltip_text(M("")); - lapmask->set_tooltip_text(M("")); - mask2CurveEditorG->set_tooltip_text(M("")); - Lmask_shape->setTooltip(M("")); - mask2CurveEditorGwav->set_tooltip_text(M("")); - LLmask_shapewav->setTooltip(M("")); } } @@ -5204,7 +5191,7 @@ void LocallabMask::convertParamToNormal() // Lmask_shape->setCurve(defSpot.Lmask_curve); LLmask_shapewav->setCurve(defSpot.LLmask_curvewav); csThresholdmask->setValue(defSpot.csthresholdmask); - + updatemaskGUI3(); // Enable all listeners enableListener(); From 20bc7af14f19b22f46a6789057928d8cbc999be2 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 11:19:41 +0200 Subject: [PATCH 035/114] Change labels blend mask and default values - change range all soft guidedfilter --- rtdata/languages/default | 1 + rtengine/dcrop.cc | 6 +++--- rtengine/improccoordinator.cc | 6 +++--- rtengine/iplocallab.cc | 8 ++++---- rtengine/ipretinex.cc | 2 +- rtengine/procparams.cc | 2 +- rtengine/simpleprocess.cc | 6 +++--- rtgui/locallabtools.cc | 14 +++++++------- rtgui/locallabtools2.cc | 24 ++++++++++++------------ 9 files changed, 35 insertions(+), 34 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2d64b34be..45fcac2e4 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2336,6 +2336,7 @@ TP_LOCALLAB_BASELOG;Logarithm base TP_LOCALLAB_BILATERAL;Bilateral filter TP_LOCALLAB_BLACK_EV;Black Ev TP_LOCALLAB_BLENDMASKCOL;Blend +TP_LOCALLAB_BLENDMASKMAK;Add / substract mask TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;If blend = 0 no action.\nAdd or subtract the mask from the original image TP_LOCALLAB_BLGUID;Guided Filter diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 70878d77e..93c85b7c8 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1476,11 +1476,11 @@ void Crop::update(int todo) } double epsilmax = 0.0001; double epsilmin = 0.00001; - double aepsil = (epsilmax - epsilmin) / 90.f; - double bepsil = epsilmax - 100.f * aepsil; + double aepsil = (epsilmax - epsilmin) / 100.f; + double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * WaveParams.softrad + bepsil; - float blur = 10.f / skip * (0.0001f + 0.8f * WaveParams.softrad); + float blur = 10.f / skip * (0.5f + 0.8f * WaveParams.softrad); rtengine::guidedFilter(guid, ble, ble, blur, epsil, false); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 054a962ac..fedfbb4ec 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1568,11 +1568,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) double epsilmax = 0.0001; double epsilmin = 0.00001; - double aepsil = (epsilmax - epsilmin) / 90.f; - double bepsil = epsilmax - 100.f * aepsil; + double aepsil = (epsilmax - epsilmin) / 100.f; + double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * WaveParams.softrad + bepsil; - float blur = 10.f / scale * (0.0001f + 0.8f * WaveParams.softrad); + float blur = 10.f / scale * (0.5f + 0.8f * WaveParams.softrad); // rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiTh); rtengine::guidedFilter(guid, ble, ble, blur, epsil, false); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index ee3005813..1f3be839a 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2315,7 +2315,7 @@ void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufco } } - const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float aepsil = (epsilmax - epsilmin) / 100.f; const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; // const float epsil = aepsil * 0.1f * rad + bepsil; const float epsil = aepsil * rad + bepsil; @@ -4005,7 +4005,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; - constexpr float aepsil = (epsilmax - epsilmin) / 1000.f; + constexpr float aepsil = (epsilmax - epsilmin) / 100.f; constexpr float bepsil = epsilmin; //epsilmax - 100.f * aepsil; const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; @@ -9883,7 +9883,7 @@ void ImProcFunctions::Lab_Local( constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; - const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float aepsil = (epsilmax - epsilmin) / 100.f; const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; const float epsil = lp.radmabl < 0.f ? 0.001f : aepsil * lp.radmabl + bepsil; @@ -14621,7 +14621,7 @@ void ImProcFunctions::Lab_Local( constexpr float epsilmax = 0.005f; constexpr float epsilmin = 0.00001f; - constexpr float aepsil = (epsilmax - epsilmin) / 1000.f; + constexpr float aepsil = (epsilmax - epsilmin) / 100.f; constexpr float bepsil = epsilmin; const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; diff --git a/rtengine/ipretinex.cc b/rtengine/ipretinex.cc index 101b3726d..ec812c270 100644 --- a/rtengine/ipretinex.cc +++ b/rtengine/ipretinex.cc @@ -923,7 +923,7 @@ void ImProcFunctions::maskforretinex(int sp, int before, float ** luminance, flo double epsilmax = 0.005; double epsilmin = 0.00001; - double aepsil = (epsilmax - epsilmin) / 1000.f; + double aepsil = (epsilmax - epsilmin) / 100.f; double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * rad + bepsil; if (rad < 0.f) { diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 88f474a84..da54d0650 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3738,7 +3738,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : complexmask(0), expmask(false), sensimask(60), - blendmask(0), + blendmask(-10), softradiusmask(0.0), enamask(false), fftmask(true), diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index f6a66242b..5868f101a 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1554,11 +1554,11 @@ private: } double epsilmax = 0.0001; double epsilmin = 0.00001; - double aepsil = (epsilmax - epsilmin) / 90.f; - double bepsil = epsilmax - 100.f * aepsil; + double aepsil = (epsilmax - epsilmin) / 100.f; + double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * WaveParams.softrad + bepsil; - float blur = 10.f / 1 * (0.0001f + 0.8f * WaveParams.softrad); + float blur = 10.f / 1 * (0.5f + 0.8f * WaveParams.softrad); // rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiTh); rtengine::guidedFilter(guid, ble, ble, blur, epsil, false); diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 35050176b..469e1f64b 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -399,7 +399,7 @@ LocallabColor::LocallabColor(): sensi(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 15))), structcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUCCOL1"), 0, 100, 1, 0))), blurcolde(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURDE"), 2, 100, 1, 5))), - softradiuscol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + softradiuscol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 0.))), invers(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), expgradcol(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), strcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRLUM"), -4., 4., 0.05, 0.))), @@ -449,7 +449,7 @@ LocallabColor::LocallabColor(): contcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), blurcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), blendmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -2201,7 +2201,7 @@ LocallabExposure::LocallabExposure(): expgradexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), strexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -4., 4., 0.05, 0.))), angexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), - softradiusexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + softradiusexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 0.))), inversex(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), expmaskexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWE")))), showmaskexpMethod(Gtk::manage(new MyComboBoxText())), @@ -2213,7 +2213,7 @@ LocallabExposure::LocallabExposure(): LLmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -3304,7 +3304,7 @@ LocallabShadow::LocallabShadow(): LLmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -4149,7 +4149,7 @@ LocallabVibrance::LocallabVibrance(): LLmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -5228,7 +5228,7 @@ LocallabBlur::LocallabBlur(): strumaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), toolbl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), blendmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 5efafcb5b..0799757e0 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -126,7 +126,7 @@ LocallabTone::LocallabTone(): estop(Gtk::manage(new Adjuster(M("TP_LOCALLAB_ESTOP"), 0.1, 4., 0.01, 1.4))), scaltm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCALTM"), 0.1, 10.0, 0.01, 1.0))), rewei(Gtk::manage(new Adjuster(M("TP_LOCALLAB_REWEI"), 0, 3, 1, 0))), - softradiustm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.1, 0.))), + softradiustm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.1, 0.))), sensitm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 30))), expmasktm(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWT")))), showmasktmMethod(Gtk::manage(new MyComboBoxText())), @@ -138,7 +138,7 @@ LocallabTone::LocallabTone(): HHmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), lapmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), - radmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), chromasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), slomasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), @@ -748,7 +748,7 @@ LocallabRetinex::LocallabRetinex(): darkness(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DARKRETI"), 0.01, 6.0, 0.01, 2.0))), lightnessreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LIGHTRETI"), 0.01, 4.0, 0.01, 1.))), cliptm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLIPTM"), 0.02, 2.0, 0.01, 1.))), - softradiusret(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRETI"), 0.0, 1000.0, 0.5, 40.))), + softradiusret(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRETI"), 0.0, 100.0, 0.5, 40.))), LocalcurveEditortransT(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONMAP"))), cTtransshape(static_cast(LocalcurveEditortransT->addCurve(CT_Flat, "", nullptr, false, false))), mMLabels(Gtk::manage(new Gtk::Label("---"))), @@ -765,7 +765,7 @@ LocallabRetinex::LocallabRetinex(): LLmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 10.))), + radmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 10.))), lapmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), @@ -2090,7 +2090,7 @@ LocallabContrast::LocallabContrast(): clariFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CLARIFRA")))), clarilres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARILRES"), -20., 100., 0.5, 0.))), claricres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARICRES"), -20., 100., 0.5, 0.))), - clarisoft(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 1.))), + clarisoft(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 1.))), origlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ORIGLC")))), expcontrastpyr(Gtk::manage(new MyExpander(false, Gtk::manage(new Gtk::HBox())))), wavgradl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_GRALWFRA")))), @@ -2149,7 +2149,7 @@ LocallabContrast::LocallabContrast(): LLmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), chromasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), mask2lcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), Lmasklcshape(static_cast(mask2lcCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) @@ -3734,7 +3734,7 @@ LocallabCBDL::LocallabCBDL(): blurcbdl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCBDL"), 0., 100., 0.1, 0.))), clarityml(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARITYML"), 0.1, 100., 0.1, 0.1))), contresid(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTRESID"), -100, 100, 1, 0))), - softradiuscb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + softradiuscb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 0.))), sensicb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSICB"), 0, 100, 1, 60))), expmaskcb(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWCB")))), showmaskcbMethod(Gtk::manage(new MyComboBoxText())), @@ -3744,7 +3744,7 @@ LocallabCBDL::LocallabCBDL(): LLmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -4731,8 +4731,8 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), - blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 1000.0, 0.5, 0.))), + blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMAK"), -100, 100, 1, -10))), + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 100.0, 0.5, 0.))), showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), @@ -4749,7 +4749,7 @@ LocallabMask::LocallabMask(): contmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), blurmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), - radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -4773,7 +4773,7 @@ LocallabMask::LocallabMask(): sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); - softradiusmask->setLogScale(10, -10); + // softradiusmask->setLogScale(10, -10); softradiusmask->setAdjusterListener(this); showmask_Method->append(M("TP_LOCALLAB_SHOWMNONE")); showmask_Method->append(M("TP_LOCALLAB_SHOWMODIFMASK")); From 89f776d62fc75b21d32c0beab34bb49f6f48273e Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 11:24:12 +0200 Subject: [PATCH 036/114] Change one tooltip --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 45fcac2e4..e1c7c9ddc 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2338,7 +2338,7 @@ TP_LOCALLAB_BLACK_EV;Black Ev TP_LOCALLAB_BLENDMASKCOL;Blend TP_LOCALLAB_BLENDMASKMAK;Add / substract mask TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image -TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;If blend = 0 no action.\nAdd or subtract the mask from the original image +TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;If this slider = 0 no action.\nAdd or subtract the mask from the original image TP_LOCALLAB_BLGUID;Guided Filter TP_LOCALLAB_BLINV;Inverse TP_LOCALLAB_BLCO;Chrominance only From d7c6b4b8f919f258d5e61335a3b94d858a9cc1d4 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Fri, 26 Jun 2020 12:34:54 +0200 Subject: [PATCH 037/114] Export: Do not allocate memory for locallab if locallab is disabled --- rtengine/simpleprocess.cc | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 3c0e0aee8..392d61678 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -971,8 +971,6 @@ private: } labView = new LabImage(fw, fh); - reservView = new LabImage(fw, fh); - lastorigView = new LabImage(fw, fh); if (params.blackwhite.enabled) { CurveFactory::curveBW(params.blackwhite.beforeCurve, params.blackwhite.afterCurve, hist16, dummy, customToneCurvebw1, customToneCurvebw2, 1); @@ -1080,15 +1078,13 @@ private: params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 1); - // bool locallutili = false; - // bool localcutili = false; - reservView->CopyFrom(labView); - lastorigView->CopyFrom(labView); - if (params.locallab.enabled) { MyTime t1, t2; t1.set(); - + const std::unique_ptr reservView(new LabImage(fw, fh)); + reservView->CopyFrom(labView); + const std::unique_ptr lastorigView(new LabImage(fw, fh)); + lastorigView->CopyFrom(labView); LUTf huerefs(500, -10000.f); LUTf sobelrefs(500, -10000.f); LUTi centerx(500, -10000); @@ -1292,7 +1288,7 @@ private: int lastsav; float avge; if (params.locallab.spots.at(sp).spotMethod == "exc") { - ipf.calc_ref(sp, reservView, reservView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + ipf.calc_ref(sp, reservView.get(), reservView.get(), 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } else { ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } @@ -1309,7 +1305,7 @@ private: float Tmax; // No Locallab mask is shown in exported picture - ipf.Lab_Local(2, sp, (float**)shbuffer, labView, labView, reservView, lastorigView, 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, + ipf.Lab_Local(2, sp, (float**)shbuffer, labView, labView, reservView.get(), lastorigView.get(), 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, lllocalcurve, locallutili, cllocalcurve, localclutili, lclocalcurve, locallcutili, @@ -1347,7 +1343,7 @@ private: lastorigView->CopyFrom(labView); if (params.locallab.spots.at(sp).spotMethod == "exc") { - ipf.calc_ref(sp, reservView, reservView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + ipf.calc_ref(sp, reservView.get(), reservView.get(), 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } else { ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } @@ -1391,11 +1387,6 @@ private: } - delete reservView; - reservView = nullptr; - delete lastorigView; - lastorigView = nullptr; - ipf.chromiLuminanceCurve(nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { @@ -1742,10 +1733,6 @@ private: delete labView; labView = nullptr; -// delete reservView; -// reservView = nullptr; - - if (bwonly) { //force BW r=g=b if (settings->verbose) { printf("Force BW\n"); @@ -2020,8 +2007,6 @@ private: ColorTemp currWB; Imagefloat *baseImg; LabImage* labView; - LabImage* reservView; - LabImage* lastorigView; LUTu hist16; From 710fa0331a7200c855c878514a7a53f94364f6d6 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 12:42:51 +0200 Subject: [PATCH 038/114] Soft radius hide in normal --- rtgui/locallabtools2.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 0799757e0..805be2ae8 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -5148,7 +5148,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) // mask2CurveEditorG->hide(); mask2CurveEditorGwav->hide(); csThresholdmask->hide(); - + softradiusmask->hide(); } else { // Advanced widgets are shown in Expert mode lapmask->show(); @@ -5164,6 +5164,7 @@ void LocallabMask::updateGUIToMode(const modeType new_type) // mask2CurveEditorG->show(); mask2CurveEditorGwav->show(); csThresholdmask->show(); + softradiusmask->show(); } } @@ -5175,7 +5176,7 @@ void LocallabMask::convertParamToNormal() // Disable all listeners disableListener(); - + softradiusmask->setValue(defSpot.softradiusmask); lapmask->setValue(defSpot.lapmask); gammask->setValue(defSpot.gammask); slopmask->setValue(defSpot.slopmask); From 8c74737c3ecd5842bd2688ee98fe76f4e33b8519 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 13:05:47 +0200 Subject: [PATCH 039/114] set default soft radius to 1 --- rtengine/procparams.cc | 2 +- rtgui/locallabtools2.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index da54d0650..474814642 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3739,7 +3739,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : expmask(false), sensimask(60), blendmask(-10), - softradiusmask(0.0), + softradiusmask(1.0), enamask(false), fftmask(true), blurmask(0.2), diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 805be2ae8..288e46c48 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4732,7 +4732,7 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMAK"), -100, 100, 1, -10))), - softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 100.0, 0.5, 0.))), + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 100.0, 0.5, 1.))), showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), From 7b16764daae6dbf85f77abe3b69e9886cec864fd Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 16:31:15 +0200 Subject: [PATCH 040/114] Harmonize showmask --- rtengine/iplocallab.cc | 5 ++++- rtgui/locallabtools2.cc | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 1f3be839a..32fa07612 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -14562,7 +14562,10 @@ void ImProcFunctions::Lab_Local( const float rad = params->locallab.spots.at(sp).radmask; const float gamma = params->locallab.spots.at(sp).gammask; const float slope = params->locallab.spots.at(sp).slopmask; - const float blendm = params->locallab.spots.at(sp).blendmask; + float blendm = params->locallab.spots.at(sp).blendmask; + if (lp.showmask_met == 2) { + blendm = 0.f;//normalize behavior mask with others no action of blend + } const float lap = params->locallab.spots.at(sp).lapmask; const bool pde = params->locallab.spots.at(sp).laplac; const int shado = params->locallab.spots.at(sp).shadmask; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 288e46c48..14659376e 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4732,7 +4732,7 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMAK"), -100, 100, 1, -10))), - softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 100.0, 0.5, 1.))), + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 1.))), showmask_Method(Gtk::manage(new MyComboBoxText())), enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), @@ -4749,7 +4749,7 @@ LocallabMask::LocallabMask(): contmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), blurmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), - radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), From 722638526e05e03e3a07e3ef9fb7cec403c684dd Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 26 Jun 2020 16:47:46 +0200 Subject: [PATCH 041/114] harmonize all smooth radius mask --- rtgui/locallabtools.cc | 18 +++++++++--------- rtgui/locallabtools2.cc | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 469e1f64b..0242e0a89 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -449,7 +449,7 @@ LocallabColor::LocallabColor(): contcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), blurcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), blendmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -685,7 +685,7 @@ LocallabColor::LocallabColor(): blendmaskcol->setAdjusterListener(this); - radmaskcol->setLogScale(10, -10); + // radmaskcol->setLogScale(10, -10); radmaskcol->setAdjusterListener(this); lapmaskcol->setAdjusterListener(this); @@ -2213,7 +2213,7 @@ LocallabExposure::LocallabExposure(): LLmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -2342,7 +2342,7 @@ LocallabExposure::LocallabExposure(): blendmaskexp->setAdjusterListener(this); - radmaskexp->setLogScale(10, -10); + //radmaskexp->setLogScale(10, -10); radmaskexp->setAdjusterListener(this); lapmaskexp->setAdjusterListener(this); @@ -3304,7 +3304,7 @@ LocallabShadow::LocallabShadow(): LLmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -3395,7 +3395,7 @@ LocallabShadow::LocallabShadow(): blendmaskSH->setAdjusterListener(this); - radmaskSH->setLogScale(10, -10); + //radmaskSH->setLogScale(10, -10); radmaskSH->setAdjusterListener(this); lapmaskSH->setAdjusterListener(this); @@ -4149,7 +4149,7 @@ LocallabVibrance::LocallabVibrance(): LLmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -4243,7 +4243,7 @@ LocallabVibrance::LocallabVibrance(): blendmaskvib->setAdjusterListener(this); - radmaskvib->setLogScale(10, -10); + //radmaskvib->setLogScale(10, -10); radmaskvib->setAdjusterListener(this); lapmaskvib->setAdjusterListener(this); @@ -5228,7 +5228,7 @@ LocallabBlur::LocallabBlur(): strumaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), toolbl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), blendmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 14659376e..9bbf5e50e 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -138,7 +138,7 @@ LocallabTone::LocallabTone(): HHmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), lapmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), - radmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), slomasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), @@ -205,7 +205,7 @@ LocallabTone::LocallabTone(): lapmasktm->setAdjusterListener(this); - radmasktm->setLogScale(10, -10); + //radmasktm->setLogScale(10, -10); radmasktm->setAdjusterListener(this); chromasktm->setAdjusterListener(this); @@ -765,7 +765,7 @@ LocallabRetinex::LocallabRetinex(): LLmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 10.))), + radmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 10.))), lapmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), @@ -2149,7 +2149,7 @@ LocallabContrast::LocallabContrast(): LLmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), mask2lcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), Lmasklcshape(static_cast(mask2lcCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) @@ -2382,7 +2382,7 @@ LocallabContrast::LocallabContrast(): blendmasklc->setAdjusterListener(this); - radmasklc->setLogScale(10, -10); + // radmasklc->setLogScale(10, -10); radmasklc->setAdjusterListener(this); chromasklc->setAdjusterListener(this); @@ -3744,7 +3744,7 @@ LocallabCBDL::LocallabCBDL(): LLmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), HHmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), blendmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), - radmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 100.0, 0.1, 0.))), + radmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), lapmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), chromaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), gammaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), @@ -3808,7 +3808,7 @@ LocallabCBDL::LocallabCBDL(): blendmaskcb->setAdjusterListener(this); - radmaskcb->setLogScale(10, -10); + // radmaskcb->setLogScale(10, -10); radmaskcb->setAdjusterListener(this); lapmaskcb->setAdjusterListener(this); From 01e48cc809369ba18bc62d01eb7ee7c771616306 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Fri, 26 Jun 2020 21:20:26 +0200 Subject: [PATCH 042/114] Some first cleanups for locallab periphery --- rtengine/curves.cc | 99 ++----- rtengine/curves.h | 17 +- rtengine/dcrop.cc | 489 +++++++++++++--------------------- rtengine/dcrop.h | 2 - rtengine/improccoordinator.cc | 243 +++++------------ rtengine/improccoordinator.h | 56 ---- rtengine/simpleprocess.cc | 188 +++++-------- 7 files changed, 344 insertions(+), 750 deletions(-) diff --git a/rtengine/curves.cc b/rtengine/curves.cc index 7f74467be..017f7755e 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -203,7 +203,6 @@ const double CurveFactory::sRGBGammaCurve = 2.4; void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool needed) { if (needed) { - for (int i = 0; i <= 0xffff; i += i < 0xffff - skip ? skip : 1) { // change to [0,1] range float val = (float)i / 65535.f; @@ -441,41 +440,21 @@ void CurveFactory::curveToning(const std::vector& curvePoints, LUTf & To fillCurveArray(dCurve.get(), ToningCurve, skip, needed); } -void CurveFactory::curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip) +bool CurveFactory::curveLocal(const std::vector& curvePoints, LUTf& LocalCurve, int skip) { bool needed = false; std::unique_ptr dCurve; if (!curvePoints.empty() && curvePoints[0] != 0) { - dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve.reset(new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { needed = true; - locallutili = true; } } - fillCurveArray(dCurve.get(), LocalLCurve, skip, needed); - //LocalLCurve.dump("wav"); - -} - -void CurveFactory::curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip) -{ - bool needed = false; - std::unique_ptr dCurve; - - if (!curvePoints.empty() && curvePoints[0] != 0) { - dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); - - if (dCurve && !dCurve->isIdentity()) { - needed = true; - localcutili = true; - } - } - - fillCurveArray(dCurve.get(), LocalCCurve, skip, needed); - //LocalLCurve.dump("wav"); + fillCurveArray(dCurve.get(), LocalCurve, skip, needed); + return needed; } @@ -498,46 +477,6 @@ void CurveFactory::curveskLocal(bool & localskutili, const std::vector& } -void CurveFactory::curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip) -{ - bool needed = false; - std::unique_ptr dCurve; - -// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) { - if (!curvePoints.empty() && curvePoints[0] != 0) { - dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); - - if (dCurve && !dCurve->isIdentity()) { - needed = true; - localexutili = true; - } - } - - fillCurveArray(dCurve.get(), LocalexCurve, skip, needed); - -} - -void CurveFactory::curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, int skip) -{ - bool needed = false; - std::unique_ptr dCurve; - -// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) { - if (!curvePoints.empty() && curvePoints[0] != 0) { - dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); - - if (dCurve && !dCurve->isIdentity()) { - needed = true; - localmaskutili = true; - } - } - - fillCurveArray(dCurve.get(), LocalmaskCurve, skip, needed); - -} - - - void CurveFactory::localLCurve(double br, double contr, /*const std::vector& curvePoints,*/ LUTu & histogram, LUTf & outCurve, int skip, bool & utili) @@ -2548,16 +2487,17 @@ void LocHHmaskCurve::Set(const Curve &pCurve) -void LocHHmaskCurve::Set(const std::vector &curvePoints, bool & lhmasutili) +bool LocHHmaskCurve::Set(const std::vector &curvePoints) { // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); ttcurve.setIdentityValue(0.); - lhmasutili = true; Set(ttcurve); + return true; } else { Reset(); + return false; } } @@ -2598,16 +2538,17 @@ void LocCCmaskCurve::Set(const Curve &pCurve) -void LocCCmaskCurve::Set(const std::vector &curvePoints, bool & lcmasutili) +bool LocCCmaskCurve::Set(const std::vector &curvePoints) { // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); ttcurve.setIdentityValue(0.); - lcmasutili = true; Set(ttcurve); + return true; } else { Reset(); + return false; } } @@ -2644,16 +2585,17 @@ void LocLLmaskCurve::Set(const Curve &pCurve) -void LocLLmaskCurve::Set(const std::vector &curvePoints, bool & llmasutili) +bool LocLLmaskCurve::Set(const std::vector &curvePoints) { // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); ttcurve.setIdentityValue(0.); - llmasutili = true; Set(ttcurve); + return true; } else { Reset(); + return false; } } @@ -2692,16 +2634,17 @@ void LocHHCurve::Set(const Curve &pCurve) -void LocHHCurve::Set(const std::vector &curvePoints, bool &HHutili) +bool LocHHCurve::Set(const std::vector &curvePoints) { // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); ttcurve.setIdentityValue(0.); Set(ttcurve); - HHutili = true; + return true; } else { Reset(); + return false; } } @@ -2740,7 +2683,7 @@ void LocLHCurve::Set(const Curve &pCurve) -void LocLHCurve::Set(const std::vector &curvePoints, bool &LHutili) +bool LocLHCurve::Set(const std::vector &curvePoints) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { @@ -2748,9 +2691,10 @@ void LocLHCurve::Set(const std::vector &curvePoints, bool &LHutili) FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); tcurve.setIdentityValue(0.); Set(tcurve); - LHutili = true; + return true; } else { Reset(); + return false; } } @@ -2784,16 +2728,17 @@ void LocwavCurve::Set(const Curve &pCurve) //lutLocCurve.dump("wav"); } -void LocwavCurve::Set(const std::vector &curvePoints, bool & lcwavutili) +bool LocwavCurve::Set(const std::vector &curvePoints) { if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); tcurve.setIdentityValue(0.); - lcwavutili = true; Set(tcurve); + return true; } else { Reset(); + return false; } } diff --git a/rtengine/curves.h b/rtengine/curves.h index 9eb3d8e56..71875fa94 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -375,11 +375,8 @@ public: static void curveToning(const std::vector& curvePoints, LUTf & ToningCurve, int skip); - static void curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip); - static void curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip); + static bool curveLocal(const std::vector& curvePoints, LUTf& LocalCurve, int skip); static void curveskLocal(bool & localskutili, const std::vector& curvePoints, LUTf & LocalskCurve, int skip); - static void curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip); - static void curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, int skip); static void complexsgnCurve(bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints, const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, @@ -632,7 +629,7 @@ public: virtual ~LocLHCurve() {}; LocLHCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool &LHutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; @@ -1184,7 +1181,7 @@ public: virtual ~LocHHmaskCurve() {}; LocHHmaskCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool & lhmasutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; @@ -1213,7 +1210,7 @@ public: virtual ~LocCCmaskCurve() {}; LocCCmaskCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool & lcmasutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; @@ -1241,7 +1238,7 @@ public: virtual ~LocLLmaskCurve() {}; LocLLmaskCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool & llmasutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; @@ -1270,7 +1267,7 @@ public: virtual ~LocHHCurve() {}; LocHHCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool &HHutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; @@ -1355,7 +1352,7 @@ public: virtual ~LocwavCurve() {}; LocwavCurve(); void Reset(); - void Set(const std::vector &curvePoints, bool &lcwavutili); + bool Set(const std::vector &curvePoints); float getSum() const { return sum; diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 99bc2581d..8b8e5dba2 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -52,7 +52,7 @@ namespace rtengine { Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow) - : PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr), reservCrop(nullptr), lastorigCrop(nullptr), + : PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr), cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr), updating(false), newUpdatePending(false), skip(10), cropx(0), cropy(0), cropw(-1), croph(-1), @@ -856,9 +856,6 @@ void Crop::update(int todo) //if (tutu) { // //I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here. labnCrop->CopyFrom(laboCrop); - reservCrop->CopyFrom(laboCrop); - lastorigCrop->CopyFrom(laboCrop); - //parent->ipf.luminanceCurve (labnCrop, labnCrop, parent->lumacurve); bool utili = parent->utili; @@ -868,207 +865,131 @@ void Crop::update(int todo) bool clcutili = parent->clcutili; bool cclutili = parent->cclutili; - bool locallutili = parent->locallutili; - LUTf lllocalcurve2(65536, 0); - bool localclutili = parent->localclutili; - LUTf cllocalcurve2(65536, 0); - bool locallcutili = parent->locallcutili; - LUTf lclocalcurve2(65536, 0); - bool localcutili = parent->locallutili; - LUTf cclocalcurve2(65536, 0); - bool localrgbutili = parent->localrgbutili; - LUTf rgblocalcurve2(65536, 0); - bool localexutili = parent->localexutili; - LUTf exlocalcurve2(65536, 0); - bool localmaskutili = parent->localmaskutili; - bool localmaskexputili = parent->localmaskexputili; - bool localmaskSHutili = parent->localmaskSHutili; - bool localmaskvibutili = parent->localmaskvibutili; - bool localmasktmutili = parent->localmasktmutili; - bool localmaskretiutili = parent->localmaskretiutili; - bool localmaskcbutili = parent->localmaskcbutili; - bool localmaskblutili = parent->localmaskblutili; - bool localmasklcutili = parent->localmasklcutili; - LUTf lmasklocalcurve2(65536, 0); - LUTf lmaskexplocalcurve2(65536, 0); - LUTf lmaskSHlocalcurve2(65536, 0); - LUTf lmaskviblocalcurve2(65536, 0); - LUTf lmasktmlocalcurve2(65536, 0); - LUTf lmaskretilocalcurve2(65536, 0); - LUTf lmaskcblocalcurve2(65536, 0); - LUTf lmaskbllocalcurve2(65536, 0); - LUTf lmasklclocalcurve2(65536, 0); - LUTf hltonecurveloc2(65536, 0); //65536 - LUTf shtonecurveloc2(65536, 0); - LUTf tonecurveloc2(65536, 0); - LUTf lightCurveloc2(32770, 0); - bool LHutili = parent->LHutili; - bool HHutili = parent->HHutili; - bool llmasutili = parent->llmasutili; - bool lhmasutili = parent->lhmasutili; - bool lhhmasutili = parent->lhhmasutili; - bool lcmasutili = parent->lcmasutili; - bool lhmasexputili = parent->lhmasexputili; - bool lcmasexputili = parent->lcmasexputili; - bool llmasexputili = parent->llmasexputili; - bool lhmasSHutili = parent->lhmasSHutili; - bool lcmasSHutili = parent->lcmasSHutili; - bool llmasSHutili = parent->llmasSHutili; - bool lhmasvibutili = parent->lhmasvibutili; - bool lcmasvibutili = parent->lcmasvibutili; - bool llmasvibutili = parent->llmasvibutili; - bool lhmaslcutili = parent->lhmaslcutili; - bool lcmaslcutili = parent->lcmaslcutili; - bool llmaslcutili = parent->llmaslcutili; - bool lhmascbutili = parent->lhmascbutili; - bool lcmascbutili = parent->lcmascbutili; - bool llmascbutili = parent->llmascbutili; - bool lhmasretiutili = parent->lhmasretiutili; - bool lcmasretiutili = parent->lcmasretiutili; - bool llmasretiutili = parent->llmasretiutili; - bool lhmastmutili = parent->lhmastmutili; - bool lcmastmutili = parent->lcmastmutili; - bool llmastmutili = parent->llmastmutili; - bool lhmasblutili = parent->lhmasblutili; - bool lcmasblutili = parent->lcmasblutili; - bool llmasblutili = parent->llmasblutili; - bool locwavutili = parent->locwavutili; - bool locwavdenutili = parent->locwavdenutili; - bool loclevwavutili = parent->loclevwavutili; - bool locconwavutili = parent->locconwavutili; - bool loccompwavutili = parent->loccompwavutili; - bool loccomprewavutili = parent->loccomprewavutili; - bool locedgwavutili = parent->locedgwavutili; - bool lmasutiliblwav = parent->lmasutiliblwav; - bool lmasutilicolwav = parent->lmasutilicolwav; - - // float avg = parent->avg; - LUTu dummy; - bool needslocal = params.locallab.enabled; - LocretigainCurve locRETgainCurve; - LocretitransCurve locRETtransCurve; - LocLHCurve loclhCurve; - LocHHCurve lochhCurve; - LocCCmaskCurve locccmasCurve; - LocLLmaskCurve locllmasCurve; - LocHHmaskCurve lochhmasCurve; - LocHHmaskCurve lochhhmasCurve; - LocCCmaskCurve locccmasexpCurve; - LocLLmaskCurve locllmasexpCurve; - LocHHmaskCurve lochhmasexpCurve; - LocCCmaskCurve locccmasSHCurve; - LocLLmaskCurve locllmasSHCurve; - LocHHmaskCurve lochhmasSHCurve; - // LocHHmaskCurve lochhhmasSHCurve; - LocCCmaskCurve locccmasvibCurve; - LocLLmaskCurve locllmasvibCurve; - LocHHmaskCurve lochhmasvibCurve; - LocCCmaskCurve locccmaslcCurve; - LocLLmaskCurve locllmaslcCurve; - LocHHmaskCurve lochhmaslcCurve; - LocCCmaskCurve locccmascbCurve; - LocLLmaskCurve locllmascbCurve; - LocHHmaskCurve lochhmascbCurve; - LocCCmaskCurve locccmasretiCurve; - LocLLmaskCurve locllmasretiCurve; - LocHHmaskCurve lochhmasretiCurve; - LocCCmaskCurve locccmastmCurve; - LocLLmaskCurve locllmastmCurve; - LocHHmaskCurve lochhmastmCurve; - LocCCmaskCurve locccmasblCurve; - LocLLmaskCurve locllmasblCurve; - LocHHmaskCurve lochhmasblCurve; - LocwavCurve locwavCurve; - LocwavCurve loclmasCurveblwav; - LocwavCurve loclmasCurvecolwav; - LocwavCurve loclevwavCurve; - LocwavCurve locconwavCurve; - LocwavCurve loccompwavCurve; - LocwavCurve loccomprewavCurve; - LocwavCurve locedgwavCurve; - LocwavCurve locwavCurveden; - - LocretigainCurverab locRETgainCurverab; - locallutili = false; - int sca = skip; - - // bool tyty = false; - // int maxspot = 1; + bool needslocal = params.locallab.enabled && !params.locallab.spots.empty(); if (needslocal) { + const std::unique_ptr reservCrop(new LabImage(laboCrop->W, laboCrop->H)); + reservCrop->CopyFrom(laboCrop); + const std::unique_ptr lastorigCrop(new LabImage(laboCrop->W, laboCrop->H)); + lastorigCrop->CopyFrom(laboCrop); + auto& lllocalcurve2 = parent->lllocalcurve; + auto& cllocalcurve2 = parent->cllocalcurve; + auto& lclocalcurve2 = parent->lclocalcurve; + auto& cclocalcurve2 = parent->cclocalcurve; + auto& rgblocalcurve2 = parent->rgblocalcurve; + auto& exlocalcurve2 = parent->exlocalcurve; + auto& lmasklocalcurve2 = parent->lmasklocalcurve; + auto& lmaskexplocalcurve2 = parent->lmaskexplocalcurve; + auto& lmaskSHlocalcurve2 = parent->lmaskSHlocalcurve; + auto& lmaskviblocalcurve2 = parent->lmaskviblocalcurve; + auto& lmasktmlocalcurve2 = parent->lmasktmlocalcurve; + auto& lmaskretilocalcurve2 = parent->lmaskretilocalcurve; + auto& lmaskcblocalcurve2 = parent->lmaskcblocalcurve; + auto& lmaskbllocalcurve2 = parent->lmaskbllocalcurve; + auto& lmasklclocalcurve2 = parent->lmasklclocalcurve; + auto& hltonecurveloc2 = parent->hltonecurveloc; + auto& shtonecurveloc2 = parent->shtonecurveloc; + auto& tonecurveloc2 = parent->tonecurveloc; + auto& lightCurveloc2 = parent->lightCurveloc; + auto& locRETgainCurve = parent->locRETgainCurve; + auto& locRETtransCurve = parent->locRETtransCurve; + auto& loclhCurve = parent->loclhCurve; + auto& lochhCurve = parent->lochhCurve; + auto& locccmasCurve = parent->locccmasCurve; + auto& locllmasCurve = parent->locllmasCurve; + auto& lochhmasCurve = parent->lochhmasCurve; + auto& lochhhmasCurve = parent->lochhhmasCurve; + auto& locccmasexpCurve = parent->locccmasexpCurve; + auto& locllmasexpCurve = parent->locllmasexpCurve; + auto& lochhmasexpCurve = parent->lochhmasexpCurve; + auto& locccmasSHCurve = parent->locccmasSHCurve; + auto& locllmasSHCurve = parent->locllmasSHCurve; + auto& lochhmasSHCurve = parent->lochhmasSHCurve; + auto& locccmasvibCurve = parent->locccmasvibCurve; + auto& locllmasvibCurve = parent->locllmasvibCurve; + auto& lochhmasvibCurve = parent->lochhmasvibCurve; + auto& locccmaslcCurve = parent->locccmaslcCurve; + auto& locllmaslcCurve = parent->locllmaslcCurve; + auto& lochhmaslcCurve = parent->lochhmaslcCurve; + auto& locccmascbCurve = parent->locccmascbCurve; + auto& locllmascbCurve = parent->locllmascbCurve; + auto& lochhmascbCurve = parent->lochhmascbCurve; + auto& locccmasretiCurve = parent->locccmasretiCurve; + auto& locllmasretiCurve = parent->locllmasretiCurve; + auto& lochhmasretiCurve = parent->lochhmasretiCurve; + auto& locccmastmCurve = parent->locccmastmCurve; + auto& locllmastmCurve = parent->locllmastmCurve; + auto& lochhmastmCurve = parent->lochhmastmCurve; + auto& locccmasblCurve = parent->locccmasblCurve; + auto& locllmasblCurve = parent->locllmasblCurve; + auto& lochhmasblCurve = parent->lochhmasblCurve; + auto& locwavCurve = parent->locwavCurve; + auto& loclmasCurveblwav = parent->loclmasCurveblwav; + auto& loclmasCurvecolwav = parent->loclmasCurvecolwav; + auto& loclevwavCurve = parent->loclevwavCurve; + auto& locconwavCurve = parent->locconwavCurve; + auto& loccompwavCurve = parent->loccompwavCurve; + auto& loccomprewavCurve = parent->loccomprewavCurve; + auto& locedgwavCurve = parent->locedgwavCurve; + auto& locwavCurveden = parent->locwavCurveden; + for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) { locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); - loclhCurve.Set(params.locallab.spots.at(sp).LHcurve, LHutili); - lochhCurve.Set(params.locallab.spots.at(sp).HHcurve, HHutili); - locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmasutili); - locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmasutili); - lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmasutili); - lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); - locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); - locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); - lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); - locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); - locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); - lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); - locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); - locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); - lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); - locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); - locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); - lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); - locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); - locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); - lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); - locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); - locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve, llmastmutili); - lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); - locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); - locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); - lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); - loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); - loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); - locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); - locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve, llmaslcutili); - lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); - - locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); - locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); - loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve, loclevwavutili); - locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve, locconwavutili); - loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve, loccompwavutili); - loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); - locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve, locedgwavutili); - locallutili = false; - CurveFactory::curveLocal(locallutili, params.locallab.spots.at(sp).llcurve, lllocalcurve2, sca); - localclutili = false; - CurveFactory::curveLocal(localclutili, params.locallab.spots.at(sp).clcurve, cllocalcurve2, sca); - locallcutili = false; - CurveFactory::curveLocal(locallcutili, params.locallab.spots.at(sp).lccurve, lclocalcurve2, sca); - localrgbutili = false; - CurveFactory::curveLocal(localrgbutili, params.locallab.spots.at(sp).rgbcurve, rgblocalcurve2, sca); - localcutili = false; - CurveFactory::curveCCLocal(localcutili, params.locallab.spots.at(sp).cccurve, cclocalcurve2, sca); - localexutili = false; - CurveFactory::curveexLocal(localexutili, params.locallab.spots.at(sp).excurve, exlocalcurve2, sca); - localmaskutili = false; - CurveFactory::curvemaskLocal(localmaskutili, params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve2, sca); - localmaskexputili = false; - CurveFactory::curvemaskLocal(localmaskexputili, params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve2, sca); - localmaskSHutili = false; - CurveFactory::curvemaskLocal(localmaskSHutili, params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve2, sca); - localmaskvibutili = false; - CurveFactory::curvemaskLocal(localmaskvibutili, params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve2, sca); - localmasktmutili = false; - CurveFactory::curvemaskLocal(localmasktmutili, params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve2, sca); - localmaskretiutili = false; - CurveFactory::curvemaskLocal(localmaskretiutili, params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve2, sca); - localmaskcbutili = false; - CurveFactory::curvemaskLocal(localmaskcbutili, params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve2, sca); - localmasklcutili = false; - CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, sca); - localmaskblutili = false; - CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, sca); + const bool LHutili = loclhCurve.Set(params.locallab.spots.at(sp).LHcurve); + const bool HHutili = lochhCurve.Set(params.locallab.spots.at(sp).HHcurve); + const bool lcmasutili = locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve); + const bool llmasutili = locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve); + const bool lhmasutili = lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve); + const bool lhhmasutili = lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve); + const bool lcmasexputili = locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve); + const bool llmasexputili = locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve); + const bool lhmasexputili = lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve); + const bool lcmasSHutili = locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve); + const bool llmasSHutili = locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve); + const bool lhmasSHutili = lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve); + const bool lcmasvibutili = locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve); + const bool llmasvibutili = locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve); + const bool lhmasvibutili = lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve); + const bool lcmascbutili = locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve); + const bool llmascbutili = locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve); + const bool lhmascbutili = lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve); + const bool lcmasretiutili = locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve); + const bool llmasretiutili = locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve); + const bool lhmasretiutili = lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve); + const bool lcmastmutili = locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve); + const bool llmastmutili = locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve); + const bool lhmastmutili = lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve); + const bool lcmasblutili = locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve); + const bool llmasblutili = locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve); + const bool lhmasblutili = lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve); + const bool lmasutiliblwav = loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav); + const bool lmasutilicolwav = loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav); + const bool lcmaslcutili = locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve); + const bool llmaslcutili = locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve); + const bool lhmaslcutili = lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve); + const bool locwavutili = locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve); + const bool locwavdenutili = locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden); + const bool loclevwavutili = loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve); + const bool locconwavutili = locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve); + const bool loccompwavutili = loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve); + const bool loccomprewavutili = loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve); + const bool locedgwavutili = locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve); + const bool locallutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).llcurve, lllocalcurve2, skip); + const bool localclutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).clcurve, cllocalcurve2, skip); + const bool locallcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).lccurve, lclocalcurve2, skip); + const bool localrgbutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).rgbcurve, rgblocalcurve2, skip); + const bool localcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).cccurve, cclocalcurve2, skip); + const bool localexutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).excurve, exlocalcurve2, skip); + const bool localmaskutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve2, skip); + const bool localmaskexputili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve2, skip); + const bool localmaskSHutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve2, skip); + const bool localmaskvibutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve2, skip); + const bool localmasktmutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve2, skip); + const bool localmaskretiutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve2, skip); + const bool localmaskcbutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve2, skip); + const bool localmasklcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, skip); + const bool localmaskblutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, skip); double ecomp = params.locallab.spots.at(sp).expcomp; double black = params.locallab.spots.at(sp).black; @@ -1082,8 +1003,6 @@ void Crop::update(int todo) double cont = params.locallab.spots.at(sp).contrast; double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; - int lastsav; - float avge; huerefblu = parent->huerefblurs[sp]; chromarefblu = parent->chromarefblurs[sp]; lumarefblu = parent->lumarefblurs[sp]; @@ -1091,9 +1010,9 @@ void Crop::update(int todo) chromare = parent->chromarefs[sp]; lumare = parent->lumarefs[sp]; sobelre = parent->sobelrefs[sp]; - avge = parent->avgs[sp]; + const float avge = parent->avgs[sp]; - lastsav = parent->lastsavrests[sp]; + int lastsav = parent->lastsavrests[sp]; float minCD; float maxCD; @@ -1105,10 +1024,10 @@ void Crop::update(int todo) float Tmax; CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, avge, - sca); + skip); // Locallab mask are only shown for selected spot if (sp == params.locallab.selspot) { - parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop, lastorigCrop, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, + parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), lastorigCrop.get(), cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, lllocalcurve2,locallutili, cllocalcurve2, localclutili, lclocalcurve2, locallcutili, @@ -1144,7 +1063,7 @@ void Crop::update(int todo) parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); } else { - parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop, lastorigCrop, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, + parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), lastorigCrop.get(), cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, lllocalcurve2,locallutili, cllocalcurve2, localclutili, lclocalcurve2, locallcutili, @@ -1180,65 +1099,67 @@ void Crop::update(int todo) minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); } - lastorigCrop->CopyFrom(labnCrop); - - lllocalcurve2.clear(); - lclocalcurve2.clear(); - cllocalcurve2.clear(); - lightCurveloc2.clear(); - rgblocalcurve2.clear(); - cclocalcurve2.clear(); - exlocalcurve2.clear(); - lmasklocalcurve2.clear(); - lmaskexplocalcurve2.clear(); - lmaskSHlocalcurve2.clear(); - lmaskviblocalcurve2.clear(); - lmasktmlocalcurve2.clear(); - lmaskretilocalcurve2.clear(); - lmaskcblocalcurve2.clear(); - lmaskbllocalcurve2.clear(); - hltonecurveloc2.clear(); - shtonecurveloc2.clear(); - tonecurveloc2.clear(); - locRETgainCurve.Reset(); - locRETtransCurve.Reset(); - loclhCurve.Reset(); - lochhCurve.Reset(); - locccmasCurve.Reset(); - locllmasCurve.Reset(); - lochhmasCurve.Reset(); - lochhhmasCurve.Reset(); - locllmasexpCurve.Reset(); - locccmasexpCurve.Reset(); - lochhmasexpCurve.Reset(); - locllmasSHCurve.Reset(); - locccmasSHCurve.Reset(); - lochhmasSHCurve.Reset(); - locllmasvibCurve.Reset(); - locccmasvibCurve.Reset(); - lochhmasvibCurve.Reset(); - locllmascbCurve.Reset(); - locccmascbCurve.Reset(); - lochhmascbCurve.Reset(); - locllmasretiCurve.Reset(); - locccmasretiCurve.Reset(); - lochhmasretiCurve.Reset(); - locllmastmCurve.Reset(); - locccmastmCurve.Reset(); - lochhmastmCurve.Reset(); - locllmasblCurve.Reset(); - locccmasblCurve.Reset(); - lochhmasblCurve.Reset(); - locllmaslcCurve.Reset(); - locccmaslcCurve.Reset(); - lochhmaslcCurve.Reset(); - locwavCurve.Reset(); - loclevwavCurve.Reset(); - locconwavCurve.Reset(); - locconwavCurve.Reset(); - locwavCurveden.Reset(); - loclmasCurveblwav.Reset(); - loclmasCurvecolwav.Reset(); + if (sp + 1u < params.locallab.spots.size()) { + // do not do this for last spot as it is not needed anymore + lastorigCrop->CopyFrom(labnCrop); + lllocalcurve2.clear(); + lclocalcurve2.clear(); + cllocalcurve2.clear(); + lightCurveloc2.clear(); + rgblocalcurve2.clear(); + cclocalcurve2.clear(); + exlocalcurve2.clear(); + lmasklocalcurve2.clear(); + lmaskexplocalcurve2.clear(); + lmaskSHlocalcurve2.clear(); + lmaskviblocalcurve2.clear(); + lmasktmlocalcurve2.clear(); + lmaskretilocalcurve2.clear(); + lmaskcblocalcurve2.clear(); + lmaskbllocalcurve2.clear(); + hltonecurveloc2.clear(); + shtonecurveloc2.clear(); + tonecurveloc2.clear(); + locRETgainCurve.Reset(); + locRETtransCurve.Reset(); + loclhCurve.Reset(); + lochhCurve.Reset(); + locccmasCurve.Reset(); + locllmasCurve.Reset(); + lochhmasCurve.Reset(); + lochhhmasCurve.Reset(); + locllmasexpCurve.Reset(); + locccmasexpCurve.Reset(); + lochhmasexpCurve.Reset(); + locllmasSHCurve.Reset(); + locccmasSHCurve.Reset(); + lochhmasSHCurve.Reset(); + locllmasvibCurve.Reset(); + locccmasvibCurve.Reset(); + lochhmasvibCurve.Reset(); + locllmascbCurve.Reset(); + locccmascbCurve.Reset(); + lochhmascbCurve.Reset(); + locllmasretiCurve.Reset(); + locccmasretiCurve.Reset(); + lochhmasretiCurve.Reset(); + locllmastmCurve.Reset(); + locccmastmCurve.Reset(); + lochhmastmCurve.Reset(); + locllmasblCurve.Reset(); + locccmasblCurve.Reset(); + lochhmasblCurve.Reset(); + locllmaslcCurve.Reset(); + locccmaslcCurve.Reset(); + lochhmaslcCurve.Reset(); + locwavCurve.Reset(); + loclevwavCurve.Reset(); + locconwavCurve.Reset(); + locconwavCurve.Reset(); + locwavCurveden.Reset(); + loclmasCurveblwav.Reset(); + loclmasCurvecolwav.Reset(); + } if (skip <= 2) { Glib::usleep(settings->cropsleep); //wait to avoid crash when crop 100% and move window @@ -1246,7 +1167,7 @@ void Crop::update(int todo) } } - // int moderetinex; + LUTu dummy; parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); parent->ipf.vibrance(labnCrop, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); parent->ipf.labColorCorrectionRegions(labnCrop); @@ -1635,22 +1556,6 @@ void Crop::freeAll() labnCrop = nullptr; } - if (reservCrop) { - delete reservCrop; - reservCrop = nullptr; - } - - if (lastorigCrop) { - delete lastorigCrop; - lastorigCrop = nullptr; - } - - - /* if (lablocCrop ) { - delete lablocCrop; - lablocCrop = NULL; - } - */ if (cropImg) { delete cropImg; cropImg = nullptr; @@ -1826,24 +1731,6 @@ bool Crop::setCropSizes(int rcx, int rcy, int rcw, int rch, int skip, bool inter labnCrop = new LabImage(cropw, croph); - if (reservCrop) { - delete reservCrop; // labnCrop can't be resized - } - - reservCrop = new LabImage(cropw, croph); - - if (lastorigCrop) { - delete lastorigCrop; // labnCrop can't be resized - } - - lastorigCrop = new LabImage(cropw, croph); - - /* if (lablocCrop) { - delete lablocCrop; // labnCrop can't be resized - } - - lablocCrop = new LabImage (cropw, croph); - */ if (!cropImg) { cropImg = new Image8; } diff --git a/rtengine/dcrop.h b/rtengine/dcrop.h index 3f8a8ad6d..1840eabfa 100644 --- a/rtengine/dcrop.h +++ b/rtengine/dcrop.h @@ -40,8 +40,6 @@ protected: Imagefloat* origCrop; // "one chunk" allocation LabImage* laboCrop; // "one chunk" allocation LabImage* labnCrop; // "one chunk" allocation - LabImage* reservCrop; // "one chunk" allocation - LabImage* lastorigCrop; // "one chunk" allocation Image8* cropImg; // "one chunk" allocation ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not float * shbuf_real; // "one chunk" allocation diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 019486198..2221a53f3 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -187,8 +187,6 @@ ImProcCoordinator::ImProcCoordinator() : // Locallab locallListener(nullptr), - reserv(nullptr), - lastorigimp(nullptr), coordX(0), coordY(0), localX(0), localY(0), lllocalcurve(65536, 0), cllocalcurve(65536, 0), @@ -209,60 +207,6 @@ ImProcCoordinator::ImProcCoordinator() : lmaskcblocalcurve(65536, 0), lmaskbllocalcurve(65536, 0), lmasklclocalcurve(65536, 0), - locallutili(false), - localclutili(false), - locallcutili(false), - localcutili(false), - localrgbutili(false), - localexutili(false), - llmasutili(false), - lhmasutili(false), - lhhmasutili(false), - lcmasutili(false), - localmaskutili(false), - localmaskexputili(false), - localmaskSHutili(false), - localmaskvibutili(false), - localmasktmutili(false), - localmaskretiutili(false), - localmaskcbutili(false), - localmaskblutili(false), - localmasklcutili(false), - lcmasexputili(false), - lhmasexputili(false), - llmasexputili(false), - lcmasSHutili(false), - lhmasSHutili(false), - llmasSHutili(false), - lcmasvibutili(false), - lhmasvibutili(false), - llmasvibutili(false), - lcmaslcutili(false), - lhmaslcutili(false), - llmaslcutili(false), - lcmascbutili(false), - lhmascbutili(false), - llmascbutili(false), - lcmasretiutili(false), - lhmasretiutili(false), - llmasretiutili(false), - lcmastmutili(false), - lhmastmutili(false), - llmastmutili(false), - lcmasblutili(false), - lhmasblutili(false), - llmasblutili(false), - locwavutili(false), - locwavdenutili(false), - loclevwavutili(false), - locconwavutili(false), - loccompwavutili(false), - loccomprewavutili(false), - locedgwavutili(false), - lmasutiliblwav(false), - lmasutilicolwav(false), - LHutili(false), - HHutili(false), lastsavrests(500, -10000), huerefs(500, -100000.f), huerefblurs(500, -100000.f), @@ -796,7 +740,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } // Encoding log with locallab - if (params->locallab.enabled) { + if (params->locallab.enabled && !params->locallab.spots.empty()) { const int sizespot = (int)params->locallab.spots.size(); float *sourceg = nullptr; @@ -1088,15 +1032,13 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) if ((todo & (M_LUMINANCE + M_COLOR)) || (todo & M_AUTOEXP)) { nprevl->CopyFrom(oprevl); - reserv->CopyFrom(oprevl); - lastorigimp->CopyFrom(oprevl); // int maxspot = 1; //************************************************************* // locallab //************************************************************* - if (params->locallab.enabled) { + if (params->locallab.enabled && !params->locallab.spots.empty()) { /* * This file is part of RawTherapee. * @@ -1117,7 +1059,10 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) * 2017 2018 Jacques Desmis * 2019 Pierre Cabrera */ - + const std::unique_ptr reserv(new LabImage(oprevl->W, oprevl->H)); + reserv->CopyFrom(oprevl); + const std::unique_ptr lastorigimp(new LabImage(oprevl->W, oprevl->H)); + lastorigimp->CopyFrom(oprevl); float **shbuffer = nullptr; int sca = 1; double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; @@ -1127,117 +1072,62 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) for (int sp = 0; sp < (int)params->locallab.spots.size(); sp++) { // Set local curves of current spot to LUT - LHutili = false; - HHutili = false; - locallutili = false; - localclutili = false; - locallcutili = false; - localexutili = false; - localrgbutili = false; - localcutili = false; - llmasutili = false; - lhmasutili = false; - lhhmasutili = false; - lcmasutili = false; - localmaskutili = false; - lcmasexputili = false; - lhmasexputili = false; - llmasexputili = false; - localmaskexputili = false; - localmaskSHutili = false; - localmaskvibutili = false; - localmasktmutili = false; - localmaskretiutili = false; - localmaskcbutili = false; - localmaskblutili = false; - localmasklcutili = false; - lcmasSHutili = false; - lhmasSHutili = false; - llmasSHutili = false; - lcmasvibutili = false; - lhmasvibutili = false; - llmasvibutili = false; - lcmascbutili = false; - lhmascbutili = false; - llmascbutili = false; - lcmaslcutili = false; - lhmaslcutili = false; - llmaslcutili = false; - lcmasretiutili = false; - lhmasretiutili = false; - llmasretiutili = false; - lcmastmutili = false; - lhmastmutili = false; - llmastmutili = false; - lcmasblutili = false; - lhmasblutili = false; - llmasblutili = false; - lcmasutili = false; - locwavutili = false; - locwavdenutili = false; - loclevwavutili = false; - locconwavutili = false; - loccompwavutili = false; - loccomprewavutili = false; - locedgwavutili = false; - lmasutiliblwav = false; - lmasutilicolwav = false; locRETgainCurve.Set(params->locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params->locallab.spots.at(sp).localTtranscurve); - loclhCurve.Set(params->locallab.spots.at(sp).LHcurve, LHutili); - lochhCurve.Set(params->locallab.spots.at(sp).HHcurve, HHutili); - locccmasCurve.Set(params->locallab.spots.at(sp).CCmaskcurve, lcmasutili); - locllmasCurve.Set(params->locallab.spots.at(sp).LLmaskcurve, llmasutili); - lochhmasCurve.Set(params->locallab.spots.at(sp).HHmaskcurve, lhmasutili); - lochhhmasCurve.Set(params->locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); - locllmasexpCurve.Set(params->locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); - locccmasexpCurve.Set(params->locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); - lochhmasexpCurve.Set(params->locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); - locllmasSHCurve.Set(params->locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); - locccmasSHCurve.Set(params->locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); - lochhmasSHCurve.Set(params->locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); - locllmasvibCurve.Set(params->locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); - locccmasvibCurve.Set(params->locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); - lochhmasvibCurve.Set(params->locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); - locllmascbCurve.Set(params->locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); - locccmascbCurve.Set(params->locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); - lochhmascbCurve.Set(params->locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); - locllmaslcCurve.Set(params->locallab.spots.at(sp).LLmasklccurve, llmaslcutili); - locccmaslcCurve.Set(params->locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); - lochhmaslcCurve.Set(params->locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); - locllmasretiCurve.Set(params->locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); - locccmasretiCurve.Set(params->locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); - lochhmasretiCurve.Set(params->locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); - locllmastmCurve.Set(params->locallab.spots.at(sp).LLmasktmcurve, llmastmutili); - locccmastmCurve.Set(params->locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); - lochhmastmCurve.Set(params->locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); - locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve, llmasblutili); - locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); - lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); - loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); - loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); - locwavCurve.Set(params->locallab.spots.at(sp).locwavcurve, locwavutili); - loclevwavCurve.Set(params->locallab.spots.at(sp).loclevwavcurve, loclevwavutili); - locconwavCurve.Set(params->locallab.spots.at(sp).locconwavcurve, locconwavutili); - loccompwavCurve.Set(params->locallab.spots.at(sp).loccompwavcurve, loccompwavutili); - loccomprewavCurve.Set(params->locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); - locwavCurveden.Set(params->locallab.spots.at(sp).locwavcurveden, locwavdenutili); - locedgwavCurve.Set(params->locallab.spots.at(sp).locedgwavcurve, locedgwavutili); - CurveFactory::curveLocal(locallutili, params->locallab.spots.at(sp).llcurve, lllocalcurve, sca); - CurveFactory::curveLocal(localclutili, params->locallab.spots.at(sp).clcurve, cllocalcurve, sca); - CurveFactory::curveLocal(locallcutili, params->locallab.spots.at(sp).lccurve, lclocalcurve, sca); - CurveFactory::curveCCLocal(localcutili, params->locallab.spots.at(sp).cccurve, cclocalcurve, sca); - CurveFactory::curveLocal(localrgbutili, params->locallab.spots.at(sp).rgbcurve, rgblocalcurve, sca); - CurveFactory::curveexLocal(localexutili, params->locallab.spots.at(sp).excurve, exlocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskutili, params->locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskexputili, params->locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskSHutili, params->locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskvibutili, params->locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, sca); - CurveFactory::curvemaskLocal(localmasktmutili, params->locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskretiutili, params->locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskcbutili, params->locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, sca); - CurveFactory::curvemaskLocal(localmaskblutili, params->locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, sca); - CurveFactory::curvemaskLocal(localmasklcutili, params->locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, sca); + const bool LHutili = loclhCurve.Set(params->locallab.spots.at(sp).LHcurve); + const bool HHutili = lochhCurve.Set(params->locallab.spots.at(sp).HHcurve); + const bool lcmasutili = locccmasCurve.Set(params->locallab.spots.at(sp).CCmaskcurve); + const bool llmasutili = locllmasCurve.Set(params->locallab.spots.at(sp).LLmaskcurve); + const bool lhmasutili = lochhmasCurve.Set(params->locallab.spots.at(sp).HHmaskcurve); + const bool lhhmasutili = lochhhmasCurve.Set(params->locallab.spots.at(sp).HHhmaskcurve); + const bool llmasexputili = locllmasexpCurve.Set(params->locallab.spots.at(sp).LLmaskexpcurve); + const bool lcmasexputili = locccmasexpCurve.Set(params->locallab.spots.at(sp).CCmaskexpcurve); + const bool lhmasexputili = lochhmasexpCurve.Set(params->locallab.spots.at(sp).HHmaskexpcurve); + const bool llmasSHutili = locllmasSHCurve.Set(params->locallab.spots.at(sp).LLmaskSHcurve); + const bool lcmasSHutili = locccmasSHCurve.Set(params->locallab.spots.at(sp).CCmaskSHcurve); + const bool lhmasSHutili = lochhmasSHCurve.Set(params->locallab.spots.at(sp).HHmaskSHcurve); + const bool llmasvibutili = locllmasvibCurve.Set(params->locallab.spots.at(sp).LLmaskvibcurve); + const bool lcmasvibutili = locccmasvibCurve.Set(params->locallab.spots.at(sp).CCmaskvibcurve); + const bool lhmasvibutili = lochhmasvibCurve.Set(params->locallab.spots.at(sp).HHmaskvibcurve); + const bool llmascbutili = locllmascbCurve.Set(params->locallab.spots.at(sp).LLmaskcbcurve); + const bool lcmascbutili = locccmascbCurve.Set(params->locallab.spots.at(sp).CCmaskcbcurve); + const bool lhmascbutili = lochhmascbCurve.Set(params->locallab.spots.at(sp).HHmaskcbcurve); + const bool llmaslcutili = locllmaslcCurve.Set(params->locallab.spots.at(sp).LLmasklccurve); + const bool lcmaslcutili = locccmaslcCurve.Set(params->locallab.spots.at(sp).CCmasklccurve); + const bool lhmaslcutili = lochhmaslcCurve.Set(params->locallab.spots.at(sp).HHmasklccurve); + const bool llmasretiutili = locllmasretiCurve.Set(params->locallab.spots.at(sp).LLmaskreticurve); + const bool lcmasretiutili = locccmasretiCurve.Set(params->locallab.spots.at(sp).CCmaskreticurve); + const bool lhmasretiutili = lochhmasretiCurve.Set(params->locallab.spots.at(sp).HHmaskreticurve); + const bool llmastmutili = locllmastmCurve.Set(params->locallab.spots.at(sp).LLmasktmcurve); + const bool lcmastmutili = locccmastmCurve.Set(params->locallab.spots.at(sp).CCmasktmcurve); + const bool lhmastmutili = lochhmastmCurve.Set(params->locallab.spots.at(sp).HHmasktmcurve); + const bool llmasblutili = locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve); + const bool lcmasblutili = locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve); + const bool lhmasblutili = lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve); + const bool lmasutiliblwav = loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav); + const bool lmasutilicolwav = loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav); + const bool locwavutili = locwavCurve.Set(params->locallab.spots.at(sp).locwavcurve); + const bool loclevwavutili = loclevwavCurve.Set(params->locallab.spots.at(sp).loclevwavcurve); + const bool locconwavutili = locconwavCurve.Set(params->locallab.spots.at(sp).locconwavcurve); + const bool loccompwavutili = loccompwavCurve.Set(params->locallab.spots.at(sp).loccompwavcurve); + const bool loccomprewavutili = loccomprewavCurve.Set(params->locallab.spots.at(sp).loccomprewavcurve); + const bool locwavdenutili = locwavCurveden.Set(params->locallab.spots.at(sp).locwavcurveden); + const bool locedgwavutili = locedgwavCurve.Set(params->locallab.spots.at(sp).locedgwavcurve); + const bool locallutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).llcurve, lllocalcurve, sca); + const bool localclutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).clcurve, cllocalcurve, sca); + const bool locallcutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).lccurve, lclocalcurve, sca); + const bool localcutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).cccurve, cclocalcurve, sca); + const bool localrgbutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).rgbcurve, rgblocalcurve, sca); + const bool localexutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).excurve, exlocalcurve, sca); + const bool localmaskutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, sca); + const bool localmaskexputili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, sca); + const bool localmaskSHutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, sca); + const bool localmaskvibutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, sca); + const bool localmasktmutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, sca); + const bool localmaskretiutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, sca); + const bool localmaskcbutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, sca); + const bool localmaskblutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, sca); + const bool localmasklcutili = CurveFactory::curveLocal(params->locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, sca); double ecomp = params->locallab.spots.at(sp).expcomp; double black = params->locallab.spots.at(sp).black; double hlcompr = params->locallab.spots.at(sp).hlcompr; @@ -1252,7 +1142,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) // Reference parameters computation if (params->locallab.spots.at(sp).spotMethod == "exc") { - ipf.calc_ref(sp, reserv, reserv, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + ipf.calc_ref(sp, reserv.get(), reserv.get(), 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } else { ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } @@ -1289,7 +1179,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) float Tsigma; float Tmin; float Tmax; - ipf.Lab_Local(3, sp, (float**)shbuffer, nprevl, nprevl, reserv, lastorigimp, 0, 0, pW, pH, scale, locRETgainCurve, locRETtransCurve, + ipf.Lab_Local(3, sp, (float**)shbuffer, nprevl, nprevl, reserv.get(), lastorigimp.get(), 0, 0, pW, pH, scale, locRETgainCurve, locRETtransCurve, lllocalcurve, locallutili, cllocalcurve, localclutili, lclocalcurve, locallcutili, @@ -1340,7 +1230,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) // Recalculate references after if (params->locallab.spots.at(sp).spotMethod == "exc") { - ipf.calc_ref(sp, reserv, reserv, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili); + ipf.calc_ref(sp, reserv.get(), reserv.get(), 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili); } else { ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili); } @@ -1848,10 +1738,6 @@ void ImProcCoordinator::freeAll() oprevl = nullptr; delete nprevl; nprevl = nullptr; - delete reserv; - reserv = nullptr; - delete lastorigimp; - lastorigimp = nullptr; if (ncie) { delete ncie; @@ -1905,10 +1791,7 @@ void ImProcCoordinator::setScale(int prevscale) oprevi = orig_prev; oprevl = new LabImage(pW, pH); nprevl = new LabImage(pW, pH); - reserv = new LabImage(pW, pH); - lastorigimp = new LabImage(pW, pH); - // nprevloc = new LabImage (pW, pH); //ncie is only used in ImProcCoordinator::updatePreviewImage, it will be allocated on first use and deleted if not used anymore previmg = new Image8(pW, pH); workimg = new Image8(pW, pH); diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index da253c072..128b2e03b 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -235,8 +235,6 @@ protected: //locallab LocallabListener* locallListener; - LabImage *reserv; - LabImage *lastorigimp; int coordX, coordY, localX, localY; LUTf lllocalcurve; LUTf cllocalcurve; @@ -301,60 +299,6 @@ protected: LocwavCurve locwavCurveden; LocwavCurve locedgwavCurve; - bool locallutili; - bool localclutili; - bool locallcutili; - bool localcutili; - bool localrgbutili; - bool localexutili; - bool llmasutili; - bool lhmasutili; - bool lhhmasutili; - bool lcmasutili; - bool localmaskutili; - bool localmaskexputili; - bool localmaskSHutili; - bool localmaskvibutili; - bool localmasktmutili; - bool localmaskretiutili; - bool localmaskcbutili; - bool localmaskblutili; - bool localmasklcutili; - bool lcmasexputili; - bool lhmasexputili; - bool llmasexputili; - bool lcmasSHutili; - bool lhmasSHutili; - bool llmasSHutili; - bool lcmasvibutili; - bool lhmasvibutili; - bool llmasvibutili; - bool lcmaslcutili; - bool lhmaslcutili; - bool llmaslcutili; - bool lcmascbutili; - bool lhmascbutili; - bool llmascbutili; - bool lcmasretiutili; - bool lhmasretiutili; - bool llmasretiutili; - bool lcmastmutili; - bool lhmastmutili; - bool llmastmutili; - bool lcmasblutili; - bool lhmasblutili; - bool llmasblutili; - bool locwavutili; - bool locwavdenutili; - bool loclevwavutili; - bool locconwavutili; - bool loccompwavutili; - bool loccomprewavutili; - bool locedgwavutili; - bool lmasutiliblwav; - bool lmasutilicolwav; - bool LHutili; - bool HHutili; LUTu lastsavrests; LUTf huerefs; LUTf huerefblurs; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 392d61678..e1ac8e4cd 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1078,7 +1078,7 @@ private: params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 1); - if (params.locallab.enabled) { + if (params.locallab.enabled && params.locallab.spots.size() > 0) { MyTime t1, t2; t1.set(); const std::unique_ptr reservView(new LabImage(fw, fh)); @@ -1150,127 +1150,74 @@ private: LUTf lmaskbllocalcurve(65536, 0); LUTf lmasklclocalcurve(65536, 0); - // int maxspot = 1; - float** shbuffer = nullptr; - + bool needsShBuffer = false; for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { if (params.locallab.spots.at(sp).inverssha) { - shbuffer = new float*[fh]; - - for (int i = 0; i < fh; i++) { - shbuffer[i] = new float[fw]; - } + needsShBuffer = true; + break; } + } + std::unique_ptr> shbuffer(needsShBuffer ? new array2D(fw, fh) : nullptr); + for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { // Set local curves of current spot to LUT - bool LHutili = false; - bool HHutili = false; - bool locallutili = false; - bool localclutili = false; - bool locallcutili = false; - bool localcutili = false; - bool localrgbutili = false; - bool localexutili = false; - bool llmasutili = false; - bool lhmasutili = false; - bool lhhmasutili = false; - bool lcmasutili = false; - bool localmaskutili = false; - bool localmaskexputili = false; - bool localmaskSHutili = false; - bool localmaskvibutili = false; - bool localmasktmutili = false; - bool localmaskretiutili = false; - bool localmaskcbutili = false; - bool localmaskblutili = false; - bool localmasklcutili = false; - bool lcmasexputili = false; - bool lhmasexputili = false; - bool llmasexputili = false; - bool lcmasSHutili = false; - bool lhmasSHutili = false; - bool llmasSHutili = false; - bool lcmasvibutili = false; - bool lhmasvibutili = false; - bool llmasvibutili = false; bool lcmaslcutili = false; - bool lhmaslcutili = false; bool llmaslcutili = false; - bool lcmascbutili = false; - bool lhmascbutili = false; - bool llmascbutili = false; - bool lcmasretiutili = false; - bool lhmasretiutili = false; - bool llmasretiutili = false; - bool lcmastmutili = false; - bool lhmastmutili = false; - bool llmastmutili = false; - bool lcmasblutili = false; - bool lhmasblutili = false; - bool llmasblutili = false; - bool locwavutili = false; - bool locwavdenutili = false; - bool loclevwavutili = false; - bool locconwavutili = false; - bool loccompwavutili = false; - bool loccomprewavutili = false; - bool locedgwavutili = false; - bool lmasutiliblwav = false; - bool lmasutilicolwav = false; locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); - loclhCurve.Set(params.locallab.spots.at(sp).LHcurve, LHutili); - lochhCurve.Set(params.locallab.spots.at(sp).HHcurve, HHutili); - locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmasutili); - locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmasutili); - lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmasutili); - lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); - locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); - locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); - lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); - locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); - locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); - lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); - locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); - locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); - lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); - locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); - locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); - lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); - locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); - locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); - lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); - locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); - locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve, llmastmutili); - lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); - locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); - locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); - lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); - loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); - loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); + const bool LHutili = loclhCurve.Set(params.locallab.spots.at(sp).LHcurve); + const bool HHutili = lochhCurve.Set(params.locallab.spots.at(sp).HHcurve); + const bool lcmasutili = locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve); + const bool llmasutili = locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve); + const bool lhmasutili = lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve); + const bool lhhmasutili = lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve); + const bool lcmasexputili = locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve); + const bool llmasexputili = locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve); + const bool lhmasexputili = lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve); + const bool lcmasSHutili = locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve); + const bool llmasSHutili = locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve); + const bool lhmasSHutili = lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve); + const bool lcmasvibutili = locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve); + const bool llmasvibutili = locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve); + const bool lhmasvibutili = lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve); + const bool lcmascbutili = locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve); + const bool llmascbutili = locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve); + const bool lhmascbutili = lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve); + const bool lcmasretiutili = locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve); + const bool llmasretiutili = locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve); + const bool lhmasretiutili = lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve); + const bool lcmastmutili = locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve); + const bool lhmaslcutili = lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve); + const bool llmastmutili = locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve); + const bool lhmastmutili = lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve); + const bool lcmasblutili = locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve); + const bool llmasblutili = locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve); + const bool lhmasblutili = lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve); + const bool lmasutiliblwav = loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav); + const bool lmasutilicolwav = loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav); - locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); - locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); - loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve, loclevwavutili); - locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve, locconwavutili); - loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve, loccompwavutili); - loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); - locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve, locedgwavutili); - CurveFactory::curveLocal(locallutili, params.locallab.spots.at(sp).llcurve, lllocalcurve, 1); - CurveFactory::curveLocal(localclutili, params.locallab.spots.at(sp).clcurve, cllocalcurve, 1); - CurveFactory::curveLocal(locallcutili, params.locallab.spots.at(sp).lccurve, lclocalcurve, 1); - CurveFactory::curveCCLocal(localcutili, params.locallab.spots.at(sp).cccurve, cclocalcurve, 1); - CurveFactory::curveLocal(localrgbutili, params.locallab.spots.at(sp).rgbcurve, rgblocalcurve, 1); - CurveFactory::curveexLocal(localexutili, params.locallab.spots.at(sp).excurve, exlocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskutili, params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskexputili, params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskSHutili, params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskvibutili, params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, 1); - CurveFactory::curvemaskLocal(localmasktmutili, params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskretiutili, params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskcbutili, params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, 1); - CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, 1); - CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, 1); + const bool locwavutili = locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve); + const bool locwavdenutili = locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden); + const bool loclevwavutili = loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve); + const bool locconwavutili = locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve); + const bool loccompwavutili = loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve); + const bool loccomprewavutili = loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve); + const bool locedgwavutili = locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve); + const bool locallutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).llcurve, lllocalcurve, 1); + const bool localclutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).clcurve, cllocalcurve, 1); + const bool locallcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).lccurve, lclocalcurve, 1); + const bool localcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).cccurve, cclocalcurve, 1); + const bool localrgbutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).rgbcurve, rgblocalcurve, 1); + const bool localexutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).excurve, exlocalcurve, 1); + const bool localmaskutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, 1); + const bool localmaskexputili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, 1); + const bool localmaskSHutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, 1); + const bool localmaskvibutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, 1); + const bool localmasktmutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, 1); + const bool localmaskretiutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, 1); + const bool localmaskcbutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, 1); + const bool localmaskblutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, 1); + const bool localmasklcutili = CurveFactory::curveLocal(params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, 1); //provisory double ecomp = params.locallab.spots.at(sp).expcomp; double black = params.locallab.spots.at(sp).black; @@ -1305,7 +1252,7 @@ private: float Tmax; // No Locallab mask is shown in exported picture - ipf.Lab_Local(2, sp, (float**)shbuffer, labView, labView, reservView.get(), lastorigView.get(), 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, + ipf.Lab_Local(2, sp, *shbuffer.get(), labView, labView, reservView.get(), lastorigView.get(), 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, lllocalcurve, locallutili, cllocalcurve, localclutili, lclocalcurve, locallcutili, @@ -1340,7 +1287,10 @@ private: huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); - lastorigView->CopyFrom(labView); + if (sp + 1u < params.locallab.spots.size()) { + // do not copy for last spot as it is not needed anymore + lastorigView->CopyFrom(labView); + } if (params.locallab.spots.at(sp).spotMethod == "exc") { ipf.calc_ref(sp, reservView.get(), reservView.get(), 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); @@ -1367,16 +1317,6 @@ private: shtonecurveloc.clear(); tonecurveloc.clear(); lightCurveloc.clear(); - if (params.locallab.spots.at(sp).inverssha) { - - for (int i = 0; i < fh; i++) { - delete [] shbuffer[i]; - } - - delete [] shbuffer; - } - - } t2.set(); From de787688a4a9edb8c42c27ac2ed46f0f68922375 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Fri, 26 Jun 2020 21:49:43 +0200 Subject: [PATCH 043/114] Further cleanups to locallab periphery --- rtengine/simpleprocess.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index e1ac8e4cd..4d31559b3 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1161,8 +1161,6 @@ private: for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { // Set local curves of current spot to LUT - bool lcmaslcutili = false; - bool llmaslcutili = false; locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); const bool LHutili = loclhCurve.Set(params.locallab.spots.at(sp).LHcurve); @@ -1195,7 +1193,8 @@ private: const bool lhmasblutili = lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve); const bool lmasutiliblwav = loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav); const bool lmasutilicolwav = loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav); - + const bool lcmaslcutili = locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve); + const bool llmaslcutili = locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve); const bool locwavutili = locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve); const bool locwavdenutili = locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden); const bool loclevwavutili = loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve); From 6d824842afb2ba7d7308f5e519f914929459ee46 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Fri, 26 Jun 2020 22:25:58 +0200 Subject: [PATCH 044/114] Further cleanups to locallab periphery --- rtengine/dcrop.cc | 59 +--------------------------- rtengine/improccoordinator.cc | 73 ++--------------------------------- rtengine/simpleprocess.cc | 20 ---------- 3 files changed, 5 insertions(+), 147 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 8b8e5dba2..ddc360003 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1100,65 +1100,8 @@ void Crop::update(int todo) } if (sp + 1u < params.locallab.spots.size()) { - // do not do this for last spot as it is not needed anymore + // do not copy for last spot as it is not needed anymore lastorigCrop->CopyFrom(labnCrop); - lllocalcurve2.clear(); - lclocalcurve2.clear(); - cllocalcurve2.clear(); - lightCurveloc2.clear(); - rgblocalcurve2.clear(); - cclocalcurve2.clear(); - exlocalcurve2.clear(); - lmasklocalcurve2.clear(); - lmaskexplocalcurve2.clear(); - lmaskSHlocalcurve2.clear(); - lmaskviblocalcurve2.clear(); - lmasktmlocalcurve2.clear(); - lmaskretilocalcurve2.clear(); - lmaskcblocalcurve2.clear(); - lmaskbllocalcurve2.clear(); - hltonecurveloc2.clear(); - shtonecurveloc2.clear(); - tonecurveloc2.clear(); - locRETgainCurve.Reset(); - locRETtransCurve.Reset(); - loclhCurve.Reset(); - lochhCurve.Reset(); - locccmasCurve.Reset(); - locllmasCurve.Reset(); - lochhmasCurve.Reset(); - lochhhmasCurve.Reset(); - locllmasexpCurve.Reset(); - locccmasexpCurve.Reset(); - lochhmasexpCurve.Reset(); - locllmasSHCurve.Reset(); - locccmasSHCurve.Reset(); - lochhmasSHCurve.Reset(); - locllmasvibCurve.Reset(); - locccmasvibCurve.Reset(); - lochhmasvibCurve.Reset(); - locllmascbCurve.Reset(); - locccmascbCurve.Reset(); - lochhmascbCurve.Reset(); - locllmasretiCurve.Reset(); - locccmasretiCurve.Reset(); - lochhmasretiCurve.Reset(); - locllmastmCurve.Reset(); - locccmastmCurve.Reset(); - lochhmastmCurve.Reset(); - locllmasblCurve.Reset(); - locccmasblCurve.Reset(); - lochhmasblCurve.Reset(); - locllmaslcCurve.Reset(); - locccmaslcCurve.Reset(); - lochhmaslcCurve.Reset(); - locwavCurve.Reset(); - loclevwavCurve.Reset(); - locconwavCurve.Reset(); - locconwavCurve.Reset(); - locwavCurveden.Reset(); - loclmasCurveblwav.Reset(); - loclmasCurvecolwav.Reset(); } if (skip <= 2) { diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 2221a53f3..52cf79203 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1214,7 +1214,10 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); - lastorigimp->CopyFrom(nprevl); + if (sp + 1u < params->locallab.spots.size()) { + // do not copy for last spot as it is not needed anymore + lastorigimp->CopyFrom(nprevl); + } // Save Locallab Retinex min/max for current spot LocallabListener::locallabRetiMinMax retiMinMax; @@ -1241,74 +1244,6 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) locallref.at(sp).lumar = lumar; locallref.at(sp).huer = huer; } - - /* - //very bad idea : it's the story of the cat biting its tail - // brings big bugs.. - //restore ref values - huerefs[sp] = huer; - chromarefs[sp] = chromar; - lumarefs[sp] = lumar ; - sobelrefs[sp] = sobeler; - */ - lllocalcurve.clear(); - lclocalcurve.clear(); - cllocalcurve.clear(); - lightCurveloc.clear(); - cclocalcurve.clear(); - rgblocalcurve.clear(); - exlocalcurve.clear(); - lmasklocalcurve.clear(); - lmaskexplocalcurve.clear(); - lmaskSHlocalcurve.clear(); - lmaskviblocalcurve.clear(); - lmasktmlocalcurve.clear(); - lmaskretilocalcurve.clear(); - lmaskcblocalcurve.clear(); - lmaskbllocalcurve.clear(); - lmasklclocalcurve.clear(); - hltonecurveloc.clear(); - shtonecurveloc.clear(); - tonecurveloc.clear(); - locRETgainCurve.Reset(); - locRETtransCurve.Reset(); - loclhCurve.Reset(); - lochhCurve.Reset(); - locccmasCurve.Reset(); - locllmasCurve.Reset(); - lochhmasCurve.Reset(); - lochhhmasCurve.Reset(); - locllmasexpCurve.Reset(); - locccmasexpCurve.Reset(); - lochhmasexpCurve.Reset(); - locllmasSHCurve.Reset(); - locccmasSHCurve.Reset(); - lochhmasSHCurve.Reset(); - locllmasvibCurve.Reset(); - locccmasvibCurve.Reset(); - lochhmasvibCurve.Reset(); - locllmascbCurve.Reset(); - locccmascbCurve.Reset(); - lochhmascbCurve.Reset(); - locllmasretiCurve.Reset(); - locccmasretiCurve.Reset(); - lochhmasretiCurve.Reset(); - locllmastmCurve.Reset(); - locccmastmCurve.Reset(); - lochhmastmCurve.Reset(); - locllmasblCurve.Reset(); - locccmasblCurve.Reset(); - lochhmasblCurve.Reset(); - locllmaslcCurve.Reset(); - locccmaslcCurve.Reset(); - lochhmaslcCurve.Reset(); - locwavCurve.Reset(); - loclevwavCurve.Reset(); - locconwavCurve.Reset(); - locwavCurveden.Reset(); - locwavCurve.Reset(); - loclmasCurveblwav.Reset(); - loclmasCurvecolwav.Reset(); } // Transmit Locallab reference values and Locallab Retinex min/max to LocallabListener diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 4d31559b3..e28efb8c7 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1296,26 +1296,6 @@ private: } else { ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } - - // Clear local curves - lllocalcurve.clear(); - lclocalcurve.clear(); - cllocalcurve.clear(); - cclocalcurve.clear(); - rgblocalcurve.clear(); - exlocalcurve.clear(); - hltonecurveloc.clear(); - lmasklocalcurve.clear(); - lmaskexplocalcurve.clear(); - lmaskSHlocalcurve.clear(); - lmaskviblocalcurve.clear(); - lmasktmlocalcurve.clear(); - lmaskretilocalcurve.clear(); - lmaskcblocalcurve.clear(); - lmaskbllocalcurve.clear(); - shtonecurveloc.clear(); - tonecurveloc.clear(); - lightCurveloc.clear(); } t2.set(); From 1e4810b2c40f6275afa3e81093638388c3c396ba Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Fri, 26 Jun 2020 23:00:09 -0700 Subject: [PATCH 045/114] Make preview update when its crop origin changes Addresses issue #5806 by triggering a re-rendering of the preview's crop when the crop moves but the dimensions stay the same. --- rtengine/dcrop.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 99bc2581d..ba1884c93 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1780,8 +1780,11 @@ bool Crop::setCropSizes(int rcx, int rcy, int rcw, int rch, int skip, bool inter int orW, orH; parent->imgsrc->getSize(cp, orW, orH); - trafx = orx; - trafy = ory; + if (trafx != orx || trafy != ory) { + trafx = orx; + trafy = ory; + changed = true; + } int cw = skips(bw, skip); int ch = skips(bh, skip); From d779a5ee2df0a6fb1a77e306daaed73f9746a34d Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 27 Jun 2020 10:47:51 +0200 Subject: [PATCH 046/114] fix segfault --- rtengine/simpleprocess.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index e28efb8c7..064710098 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1150,16 +1150,15 @@ private: LUTf lmaskbllocalcurve(65536, 0); LUTf lmasklclocalcurve(65536, 0); - bool needsShBuffer = false; + array2D shbuffer; for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { if (params.locallab.spots.at(sp).inverssha) { - needsShBuffer = true; + shbuffer(fw, fh); break; } } - std::unique_ptr> shbuffer(needsShBuffer ? new array2D(fw, fh) : nullptr); - for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { + for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { // Set local curves of current spot to LUT locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); @@ -1251,7 +1250,7 @@ private: float Tmax; // No Locallab mask is shown in exported picture - ipf.Lab_Local(2, sp, *shbuffer.get(), labView, labView, reservView.get(), lastorigView.get(), 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, + ipf.Lab_Local(2, sp, shbuffer, labView, labView, reservView.get(), lastorigView.get(), 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, lllocalcurve, locallutili, cllocalcurve, localclutili, lclocalcurve, locallcutili, From 96c5eff50c978fe5a0f30c0a7f3df763e5dab555 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 27 Jun 2020 13:13:26 +0200 Subject: [PATCH 047/114] Further cleanups to locallab periphery --- rtengine/dcrop.cc | 24 +++++++------------ rtengine/improccoordinator.cc | 44 ++++++++++++++--------------------- rtengine/improccoordinator.h | 22 +++++++----------- rtengine/simpleprocess.cc | 4 ---- 4 files changed, 34 insertions(+), 60 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index ddc360003..28e5afc1f 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -851,23 +851,11 @@ void Crop::update(int todo) }*/ // apply luminance operations - //bool tutu = true; if (todo & (M_LUMINANCE + M_COLOR)) { // - //if (tutu) { // //I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here. labnCrop->CopyFrom(laboCrop); - //parent->ipf.luminanceCurve (labnCrop, labnCrop, parent->lumacurve); - bool utili = parent->utili; - bool autili = parent->autili; - bool butili = parent->butili; - bool ccutili = parent->ccutili; - bool clcutili = parent->clcutili; - bool cclutili = parent->cclutili; - - - bool needslocal = params.locallab.enabled && !params.locallab.spots.empty(); - if (needslocal) { + if (params.locallab.enabled && !params.locallab.spots.empty()) { const std::unique_ptr reservCrop(new LabImage(laboCrop->W, laboCrop->H)); reservCrop->CopyFrom(laboCrop); const std::unique_ptr lastorigCrop(new LabImage(laboCrop->W, laboCrop->H)); @@ -1012,8 +1000,6 @@ void Crop::update(int todo) sobelre = parent->sobelrefs[sp]; const float avge = parent->avgs[sp]; - int lastsav = parent->lastsavrests[sp]; - float minCD; float maxCD; float mini; @@ -1022,6 +1008,7 @@ void Crop::update(int todo) float Tsigma; float Tmin; float Tmax; + int lastsav; CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, avge, skip); @@ -1110,6 +1097,13 @@ void Crop::update(int todo) } } + bool utili = parent->utili; + bool autili = parent->autili; + bool butili = parent->butili; + bool ccutili = parent->ccutili; + bool clcutili = parent->clcutili; + bool cclutili = parent->cclutili; + LUTu dummy; parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); parent->ipf.vibrance(labnCrop, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 52cf79203..2b83a661b 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -187,7 +187,6 @@ ImProcCoordinator::ImProcCoordinator() : // Locallab locallListener(nullptr), - coordX(0), coordY(0), localX(0), localY(0), lllocalcurve(65536, 0), cllocalcurve(65536, 0), lclocalcurve(65536, 0), @@ -207,24 +206,6 @@ ImProcCoordinator::ImProcCoordinator() : lmaskcblocalcurve(65536, 0), lmaskbllocalcurve(65536, 0), lmasklclocalcurve(65536, 0), - lastsavrests(500, -10000), - huerefs(500, -100000.f), - huerefblurs(500, -100000.f), - chromarefblurs(500, -100000.f), - lumarefblurs(500, -100000.f), - chromarefs(500, -100000.f), - lumarefs(500, -100000.f), - sobelrefs(500, -100000.f), - avgs(500, -100000.f), - huer(0), - huerblu(0), - chromarblu(0), - lumarblu(0), - chromar(0), - lumar(0), - sobeler(0), - lastsav(0), - avg(0), lastspotdup(false), previewDeltaE(false), locallColorMask(0), @@ -1069,6 +1050,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) float avge; std::vector locallref; std::vector locallretiminmax; + huerefs.resize(params->locallab.spots.size()); + huerefblurs.resize(params->locallab.spots.size()); + chromarefblurs.resize(params->locallab.spots.size()); + lumarefblurs.resize(params->locallab.spots.size()); + chromarefs.resize(params->locallab.spots.size()); + lumarefs.resize(params->locallab.spots.size()); + sobelrefs.resize(params->locallab.spots.size()); + avgs.resize(params->locallab.spots.size()); for (int sp = 0; sp < (int)params->locallab.spots.size(); sp++) { // Set local curves of current spot to LUT @@ -1147,14 +1136,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } - huerblu = huerefblurs[sp] = huerefblu; - chromarblu = chromarefblurs[sp] = chromarefblu; - lumarblu = lumarefblurs[sp] = lumarefblu; - huer = huerefs[sp] = huere; - chromar = chromarefs[sp] = chromare; - lumar = lumarefs[sp] = lumare ; - sobeler = sobelrefs[sp] = sobelre; - avg = avgs[sp] = avge; + double huerblu = huerefblurs[sp] = huerefblu; + double chromarblu = chromarefblurs[sp] = chromarefblu; + double lumarblu = lumarefblurs[sp] = lumarefblu; + double huer = huerefs[sp] = huere; + double chromar = chromarefs[sp] = chromare; + double lumar = lumarefs[sp] = lumare ; + double sobeler = sobelrefs[sp] = sobelre; + float avg = avgs[sp] = avge; CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumar, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avg, sca); @@ -1179,6 +1168,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) float Tsigma; float Tmin; float Tmax; + int lastsav; ipf.Lab_Local(3, sp, (float**)shbuffer, nprevl, nprevl, reserv.get(), lastorigimp.get(), 0, 0, pW, pH, scale, locRETgainCurve, locRETtransCurve, lllocalcurve, locallutili, cllocalcurve, localclutili, diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 128b2e03b..cc76bf23e 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -235,7 +235,6 @@ protected: //locallab LocallabListener* locallListener; - int coordX, coordY, localX, localY; LUTf lllocalcurve; LUTf cllocalcurve; LUTf lclocalcurve; @@ -255,7 +254,6 @@ protected: LUTf lmaskcblocalcurve; LUTf lmaskbllocalcurve; LUTf lmasklclocalcurve; -// LUTu lhist16loc; LocretigainCurve locRETgainCurve; LocretitransCurve locRETtransCurve; LocretigainCurverab locRETgainCurverab; @@ -299,18 +297,14 @@ protected: LocwavCurve locwavCurveden; LocwavCurve locedgwavCurve; - LUTu lastsavrests; - LUTf huerefs; - LUTf huerefblurs; - LUTf chromarefblurs; - LUTf lumarefblurs; - LUTf chromarefs; - LUTf lumarefs; - LUTf sobelrefs; - LUTf avgs; - double huer, huerblu, chromarblu, lumarblu, chromar, lumar, sobeler; - int lastsav; - float avg; + std::vector huerefs; + std::vector huerefblurs; + std::vector chromarefblurs; + std::vector lumarefblurs; + std::vector chromarefs; + std::vector lumarefs; + std::vector sobelrefs; + std::vector avgs; bool lastspotdup; bool previewDeltaE; int locallColorMask; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 064710098..dae1973d5 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1085,10 +1085,6 @@ private: reservView->CopyFrom(labView); const std::unique_ptr lastorigView(new LabImage(fw, fh)); lastorigView->CopyFrom(labView); - LUTf huerefs(500, -10000.f); - LUTf sobelrefs(500, -10000.f); - LUTi centerx(500, -10000); - LUTi centery(500, -10000); LocretigainCurve locRETgainCurve; LocretitransCurve locRETtransCurve; LocLHCurve loclhCurve; From 76e86ffa718c3820f9ef90a7e7d27edaf62fe44a Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Sat, 27 Jun 2020 13:15:04 +0200 Subject: [PATCH 048/114] Regeenrated rtdata/languages/default --- rtdata/languages/default | 220 +++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index cfe9b1372..0f53a4898 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2,6 +2,7 @@ #01 Developers should add translations to this file and then run the 'generateTranslationDiffs' Bash script to update other locales. #02 Translators please append a comment here with the current date and your name(s) as used in the RawTherapee forum or GitHub page, e.g.: #03 2525-12-24 Zager and Evans + ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Credits ABOUT_TAB_LICENSE;License @@ -1165,6 +1166,10 @@ HISTORY_MSG_924;Local - Tool complexity mode HISTORY_MSG_925;Local - Scope color tools HISTORY_MSG_926;Local - Show mask type HISTORY_MSG_927;Local - Shadow mask +HISTORY_MSG_BLSHAPE;Blur by level +HISTORY_MSG_BLURCWAV;Blur chroma +HISTORY_MSG_BLURWAV;Blur luminance +HISTORY_MSG_BLUWAV;Attenuation Response HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction @@ -1188,6 +1193,7 @@ HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Dehaze - Show depth map HISTORY_MSG_DEHAZE_STRENGTH;Dehaze - Strength HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold +HISTORY_MSG_EDGEFFECT;Edge Attenuation Response HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative HISTORY_MSG_FILMNEGATIVE_FILMBASE;Film base color HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values @@ -1224,7 +1230,9 @@ HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter HISTORY_MSG_PREPROCWB_MODE;Preprocess WB Mode +HISTORY_MSG_PROTAB;Protection HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - Contrast threshold +HISTORY_MSG_RANGEAB;Range ab HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correction - Iterations HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correction - Avoid color shift HISTORY_MSG_RAW_BORDER;Raw border @@ -1232,19 +1240,28 @@ HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling HISTORY_MSG_SHARPENING_BLUR;Sharpening - Blur radius HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace +HISTORY_MSG_SIGMACOL;Chroma Attenuation Response +HISTORY_MSG_SIGMADIR;Dir Attenuation Response +HISTORY_MSG_SIGMAFIN;Final contrast Attenuation Response +HISTORY_MSG_SIGMATON;Toning Attenuation Response HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength HISTORY_MSG_TEMPOUT;CAM02 automatic temperature +HISTORY_MSG_THRESWAV;Balance threshold HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor HISTORY_MSG_TRANS_Method;Geometry - Method HISTORY_MSG_WAVBALCHROM;Equalizer chrominance HISTORY_MSG_WAVBALLUM;Equalizer luminance -HISTORY_MSG_WAVCHROMFI;Chroma fine +HISTORY_MSG_WAVBL;Blur levels HISTORY_MSG_WAVCHROMCO;Chroma coarse +HISTORY_MSG_WAVCHROMFI;Chroma fine HISTORY_MSG_WAVCLARI;Clarity HISTORY_MSG_WAVEDGS;Edge stopping +HISTORY_MSG_WAVLOWTHR;Threshold low contrast HISTORY_MSG_WAVMERGEC;Merge C HISTORY_MSG_WAVMERGEL;Merge L +HISTORY_MSG_WAVOFFSET;Offset +HISTORY_MSG_WAVOLDSH;Old algorithm HISTORY_MSG_WAVRADIUS;Radius Shadows-Highlight HISTORY_MSG_WAVSCALE;Scale HISTORY_MSG_WAVSHOWMASK;Show wavelet mask @@ -1252,22 +1269,6 @@ HISTORY_MSG_WAVSIGMA;Attenuation Response HISTORY_MSG_WAVSOFTRAD;Soft radius clarity HISTORY_MSG_WAVSOFTRADEND;Soft radius final HISTORY_MSG_WAVUSHAMET;Clarity method -HISTORY_MSG_THRESWAV;Balance threshold -HISTORY_MSG_BLUWAV;Attenuation Response -HISTORY_MSG_WAVOLDSH;Old algorithm -HISTORY_MSG_WAVOFFSET;Offset -HISTORY_MSG_WAVLOWTHR;Threshold low contrast -HISTORY_MSG_BLSHAPE;Blur by level -HISTORY_MSG_WAVBL;Blur levels -HISTORY_MSG_BLURWAV;Blur luminance -HISTORY_MSG_BLURCWAV;Blur chroma -HISTORY_MSG_EDGEFFECT;Edge Attenuation Response -HISTORY_MSG_SIGMAFIN;Final contrast Attenuation Response -HISTORY_MSG_SIGMATON;Toning Attenuation Response -HISTORY_MSG_SIGMACOL;Chroma Attenuation Response -HISTORY_MSG_SIGMADIR;Dir Attenuation Response -HISTORY_MSG_RANGEAB;Range ab -HISTORY_MSG_PROTAB;Protection HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1451,7 +1452,6 @@ NAVIGATOR_XY_NA;x: --, y: -- OPTIONS_BUNDLED_MISSING;The bundled profile "%1" could not be found!\n\nYour installation could be damaged.\n\nDefault internal values will be used instead. OPTIONS_DEFIMG_MISSING;The default profile for non-raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. OPTIONS_DEFRAW_MISSING;The default profile for raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. -PARTIALPASTE_LOCALLABGROUP;Local Adjustments Settings PARTIALPASTE_ADVANCEDGROUP;Advanced Settings PARTIALPASTE_BASICGROUP;Basic Settings PARTIALPASTE_CACORRECTION;Chromatic aberration correction @@ -1492,10 +1492,11 @@ PARTIALPASTE_IMPULSEDENOISE;Impulse noise reduction PARTIALPASTE_IPTCINFO;IPTC PARTIALPASTE_LABCURVE;L*a*b* adjustments PARTIALPASTE_LENSGROUP;Lens Related Settings -PARTIALPASTE_LOCALLAB;Local Adjustments -PARTIALPASTE_LOCGROUP;Local PARTIALPASTE_LENSPROFILE;Profiled lens correction PARTIALPASTE_LOCALCONTRAST;Local contrast +PARTIALPASTE_LOCALLAB;Local Adjustments +PARTIALPASTE_LOCALLABGROUP;Local Adjustments Settings +PARTIALPASTE_LOCGROUP;Local PARTIALPASTE_METADATA;Metadata mode PARTIALPASTE_METAGROUP;Metadata settings PARTIALPASTE_PCVIGNETTE;Vignette filter @@ -1687,8 +1688,8 @@ PREFERENCES_SERIALIZE_TIFF_READ_TOOLTIP;Enabling this option when working with f PREFERENCES_SET;Set PREFERENCES_SHOWBASICEXIF;Show basic Exif info PREFERENCES_SHOWDATETIME;Show date and time -PREFERENCES_SHOWFILMSTRIPTOOLBAR;Show Filmstrip toolbar PREFERENCES_SHOWEXPOSURECOMPENSATION;Append exposure compensation +PREFERENCES_SHOWFILMSTRIPTOOLBAR;Show Filmstrip toolbar PREFERENCES_SHOWTOOLTIP;Show Local Adjustments advice tooltips PREFERENCES_SHTHRESHOLD;Threshold for clipped shadows PREFERENCES_SINGLETAB;Single Editor Tab Mode @@ -1915,16 +1916,16 @@ TP_COLORAPP_GAMUT;Gamut control (L*a*b*) TP_COLORAPP_GAMUT_TOOLTIP;Allow gamut control in L*a*b* mode. TP_COLORAPP_HUE;Hue (h) TP_COLORAPP_HUE_TOOLTIP;Hue (h) - angle between 0° and 360°. -TP_COLORAPP_ILLUM;Illuminant -TP_COLORAPP_ILLUM_TOOLTIP;Select the illuminant closest to the shooting conditions.\nIn general D50, but it can change depending on the time and lattitude. -TP_COLORAPP_ILA;Incandescent StdA 2856K TP_COLORAPP_IL41;D41 TP_COLORAPP_IL50;D50 TP_COLORAPP_IL55;D55 TP_COLORAPP_IL60;D60 TP_COLORAPP_IL65;D65 TP_COLORAPP_IL75;D75 +TP_COLORAPP_ILA;Incandescent StdA 2856K TP_COLORAPP_ILFREE;Free +TP_COLORAPP_ILLUM;Illuminant +TP_COLORAPP_ILLUM_TOOLTIP;Select the illuminant closest to the shooting conditions.\nIn general D50, but it can change depending on the time and lattitude. TP_COLORAPP_LABEL;CIE Color Appearance Model 2002 TP_COLORAPP_LABEL_CAM02;Image Adjustments TP_COLORAPP_LABEL_SCENE;Scene Conditions @@ -1954,9 +1955,9 @@ TP_COLORAPP_TCMODE_LABEL2;Curve mode 2 TP_COLORAPP_TCMODE_LABEL3;Curve chroma mode TP_COLORAPP_TCMODE_LIGHTNESS;Lightness TP_COLORAPP_TCMODE_SATUR;Saturation -TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504 TP_COLORAPP_TEMP2_TOOLTIP;Either symmetrical mode temp = White balance.\nEither select illuminant always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504 TP_COLORAPP_TEMPOUT_TOOLTIP;Disable to change temperature and tint +TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504 TP_COLORAPP_TONECIE;Tone mapping using CIECAM02 TP_COLORAPP_TONECIE_TOOLTIP;If this option is disabled, tone mapping is done in L*a*b* space.\nIf this option is enabled, tone mapping is done using CIECAM02.\nThe Tone Mapping tool must be enabled for this setting to take effect. TP_COLORAPP_VIEWING_ABSOLUTELUMINANCE_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16 cd/m²). @@ -2295,6 +2296,11 @@ TP_LENSPROFILE_USE_CA;Chromatic aberration TP_LENSPROFILE_USE_GEOMETRIC;Geometric distortion TP_LENSPROFILE_USE_HEADER;Correct TP_LENSPROFILE_USE_VIGNETTING;Vignetting +TP_LOCALCONTRAST_AMOUNT;Amount +TP_LOCALCONTRAST_DARKNESS;Darkness level +TP_LOCALCONTRAST_LABEL;Local Contrast +TP_LOCALCONTRAST_LIGHTNESS;Lightness level +TP_LOCALCONTRAST_RADIUS;Radius TP_LOCALLAB_ACTIV;Luminance only TP_LOCALLAB_ADJ;Equalizer Blue-yellow Red-green TP_LOCALLAB_ALL;All rubrics @@ -2304,54 +2310,56 @@ TP_LOCALLAB_ARTIF_TOOLTIP;Threshold deltaE-scope increase the range of scope-del TP_LOCALLAB_AUTOGRAY;Automatic TP_LOCALLAB_AVOID;Avoid color shift TP_LOCALLAB_BALAN;Balance ΔE ab-L +TP_LOCALLAB_BALANEXP;ΔØ PDE balance TP_LOCALLAB_BALANH;Balance ΔE C-H TP_LOCALLAB_BALAN_TOOLTIP;Change algorithm ΔE parameter.\nMore or less ab-L, more or less C - H.\nNot for Denoise -TP_LOCALLAB_BALANEXP;ΔØ PDE balance TP_LOCALLAB_BASELOG;Logarithm base TP_LOCALLAB_BILATERAL;Bilateral filter TP_LOCALLAB_BLACK_EV;Black Ev +TP_LOCALLAB_BLCO;Chrominance only TP_LOCALLAB_BLENDMASKCOL;Blend TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image TP_LOCALLAB_BLGUID;Guided Filter TP_LOCALLAB_BLINV;Inverse -TP_LOCALLAB_BLCO;Chrominance only -TP_LOCALLAB_BLLO;Luminance only TP_LOCALLAB_BLLC;Luminance & Chrominance +TP_LOCALLAB_BLLO;Luminance only TP_LOCALLAB_BLMED;Median TP_LOCALLAB_BLMETHOD_TOOLTIP;Normal - direct blur and noise with all settings.\nInverse blur and noise with all settings. Be careful some results may be curious +TP_LOCALLAB_BLNOI_EXP;Blur & Noise TP_LOCALLAB_BLNORM;Normal TP_LOCALLAB_BLSYM;Symmetric -TP_LOCALLAB_BLWH;All changes forced in Black and White -TP_LOCALLAB_BLWH_TOOLTIP;Force color change composante "a" and "b" to zero.\nUsefull when the user chooses black and white processes, or film. -TP_LOCALLAB_SPOTNAME;New Spot TP_LOCALLAB_BLUFR;Smooth - Blur - Grain - Denoise TP_LOCALLAB_BLUMETHOD_TOOLTIP;To blur the background and isolate the foreground:\n*Blur the background by a RT-spot fully covering the image (high values for scope and transition) - normal or inverse.\n*Isolate the foreground by one or more excluding RT-spot with the tools you want (increse scope).\n\nThis module can be used in additional noise reduction,including "median" and "Guided filter" -TP_LOCALLAB_BLURMASK_TOOLTIP;Generate a blur mask, take into account the structure with the contrast threshold Mask Blur slider. TP_LOCALLAB_BLUR;Gaussian Blur - Noise - Grain TP_LOCALLAB_BLURCBDL;Blur levels 0-1-2-3-4 TP_LOCALLAB_BLURCOL;Radius Mask Blur TP_LOCALLAB_BLURDE;Blur Shape detection TP_LOCALLAB_BLURLC;Luminance Only TP_LOCALLAB_BLURLEVELFRA;Blur Levels +TP_LOCALLAB_BLURMASK_TOOLTIP;Generate a blur mask, take into account the structure with the contrast threshold Mask Blur slider. TP_LOCALLAB_BLURRESIDFRA;Blur Residual +TP_LOCALLAB_BLUR_TOOLNAME;Smooth Blur Gain & Denoise - 1 +TP_LOCALLAB_BLWH;All changes forced in Black and White +TP_LOCALLAB_BLWH_TOOLTIP;Force color change composante "a" and "b" to zero.\nUsefull when the user chooses black and white processes, or film. TP_LOCALLAB_BUTTON_ADD;Add TP_LOCALLAB_BUTTON_DEL;Delete TP_LOCALLAB_BUTTON_DUPL;Duplicate TP_LOCALLAB_BUTTON_REN;Rename TP_LOCALLAB_BUTTON_VIS;Show/Hide TP_LOCALLAB_CBDL;Contrast by detail levels - Defects -TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details. TP_LOCALLAB_CBDLCLARI_TOOLTIP;Takes the midtones and enhance them. +TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Prevent the sharpening of noise +TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2 TP_LOCALLAB_CENTER_X;Center X TP_LOCALLAB_CENTER_Y;Center Y TP_LOCALLAB_CH;Curves CL - LC TP_LOCALLAB_CHROMA;Chrominance +TP_LOCALLAB_CHROMABLU;Chroma levels +TP_LOCALLAB_CHROMABLU_TOOLTIP;Acts as an amplifier-reducer action compare to settings of luma.\nUnder 1 reduce, above 1 amplifie TP_LOCALLAB_CHROMACBDL;Chroma TP_LOCALLAB_CHROMACB_TOOLTIP;Acts as an amplifier-reducer action compare to sliders of luminance.\nUnder 100 reduce, above 100 amplifie TP_LOCALLAB_CHROMALEV;Chroma levels -TP_LOCALLAB_CHROMABLU;Chroma levels -TP_LOCALLAB_CHROMABLU_TOOLTIP;Acts as an amplifier-reducer action compare to settings of luma.\nUnder 1 reduce, above 1 amplifie TP_LOCALLAB_CHROMASKCOL;Chroma mask TP_LOCALLAB_CHROMASK_TOOLTIP;You can use this slider to desaturated background (inverse mask - curve near 0).\nAlso to attenuate or enhance the action of a mask on the chroma TP_LOCALLAB_CHRRT;Chroma @@ -2366,19 +2374,20 @@ TP_LOCALLAB_CLARITYML;Clarity TP_LOCALLAB_CLARI_TOOLTIP;Under or equal level wavelet 4, 'Sharp mask' is enabled.\nAbove level wavelet 5 'Clarity' is enabled.\nUsefull if you use 'Level dynamic Range Compression' TP_LOCALLAB_CLIPTM;Clip Restored datas (gain) TP_LOCALLAB_COFR;Color & Light - Small defects -TP_LOCALLAB_COL_NAME;Name -TP_LOCALLAB_COL_VIS;Status TP_LOCALLAB_COLORDE;Color preview selection ΔE - Intensity -TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative. TP_LOCALLAB_COLORDEPREV_TOOLTIP;Button Preview ΔE needs that only one tool is enabled (expander).\nTo be able to have an Preview ΔE with several enable tools use Mask and modifications - Preview ΔE +TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative. TP_LOCALLAB_COLORSCOPE;Scope Color Tools TP_LOCALLAB_COLORSCOPE_TOOLTIP;Use a common Scope for Color and light, Exposure (Standard), Shadows highlight, Vibrance.\nOthers tools have their specific scope. +TP_LOCALLAB_COLOR_TOOLNAME;Color&Light (Defects) - 11 +TP_LOCALLAB_COL_NAME;Name +TP_LOCALLAB_COL_VIS;Status TP_LOCALLAB_COMPFRA;Levels Directional Contrast -TP_LOCALLAB_COMPREFRA;Levels Dynamic Wavelet Range (un)Compression -TP_LOCALLAB_COMPRESS_TOOLTIP;Use if necessary the module 'Clarity & Sharp mask and Blend & Soft Images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_COMPFRAME_TOOLTIP;Allows special effects. You can reduce artifacts with 'Clarity & Sharp mask - Blend & Soft Images".\nUses a lot of resources TP_LOCALLAB_COMPLEX_METHOD;Software Complexity TP_LOCALLAB_COMPLEX_TOOLTIP; Allow user to select Local adjustments rubrics. +TP_LOCALLAB_COMPREFRA;Levels Dynamic Wavelet Range (un)Compression +TP_LOCALLAB_COMPRESS_TOOLTIP;Use if necessary the module 'Clarity & Sharp mask and Blend & Soft Images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_CONTCOL;Contrast threshold Mask Blur TP_LOCALLAB_CONTFRA;Contrast by Level TP_LOCALLAB_CONTRAST;Contrast @@ -2406,6 +2415,7 @@ TP_LOCALLAB_DEHAZ_TOOLTIP;Negative values adds haze TP_LOCALLAB_DELTAD;Delta balance TP_LOCALLAB_DELTAEC;Mask ΔE Image TP_LOCALLAB_DENOIS;Ψ Denoise +TP_LOCALLAB_DENOI_EXP;Denoise TP_LOCALLAB_DENOI_TOOLTIP;This module can be used alone (at the end of process), or in complement of main denoise (at the beginning).\nScope allows to differentiate the action according to the color (deltaE).\nYou can complete the action with "median" or "Guided Filter" (Smooth Blur...).\nYou can complete the action with "Blur levels" "Wavelet pyramid" TP_LOCALLAB_DEPTH;Depth TP_LOCALLAB_DETAIL;Local contrast @@ -2440,23 +2450,24 @@ TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), TP_LOCALLAB_EXPCHROMA;Chroma compensation TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) -TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_EXPCURV;Curves TP_LOCALLAB_EXPGRAD;Graduated Filter -TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greater the action of reducing contrast. TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balances the action between the original image and the Laplace transform. -TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Add linear exposure component before application Laplace transform TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Apply a gamma before and after Laplace transform +TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Add linear exposure component before application Laplace transform +TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greater the action of reducing contrast. TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties) -TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise. +TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. TP_LOCALLAB_EXPOSE;Exposure - PDE algorithms TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high weakening transition values and scope to simulate small RT-spot. TP_LOCALLAB_EXPTOOL;Tools exposure TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC +TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10 TP_LOCALLAB_FATAMOUNT;Amount TP_LOCALLAB_FATANCHOR;Anchor TP_LOCALLAB_FATANCHORA;Offset @@ -2486,8 +2497,8 @@ TP_LOCALLAB_GRADLOGFRA;Graduated Filter Luminance TP_LOCALLAB_GRADSTR;Gradient strength TP_LOCALLAB_GRADSTRAB_TOOLTIP;Filter chroma strength TP_LOCALLAB_GRADSTRCHRO;Gradient strength Chrominance -TP_LOCALLAB_GRADSTRHUE2;Gradient strength Hue TP_LOCALLAB_GRADSTRHUE;Gradient strength Hue +TP_LOCALLAB_GRADSTRHUE2;Gradient strength Hue TP_LOCALLAB_GRADSTRHUE_TOOLTIP;Filter Hue strength TP_LOCALLAB_GRADSTRLUM;Gradient strength Luminance TP_LOCALLAB_GRADSTR_TOOLTIP;Filter strength in stops @@ -2499,27 +2510,28 @@ TP_LOCALLAB_GRIDTWO;Direct TP_LOCALLAB_GUIDBL;Soft radius TP_LOCALLAB_GUIDFILTER;Guided filter radius TP_LOCALLAB_GUIDFILTER_TOOLTIP;Adapt this values according to images - can reduce or increase artifacts. -TP_LOCALLAB_HIGHMASKCOL;Highlights mask TP_LOCALLAB_HHMASK_TOOLTIP;Fine hue adjustments for example for the skin. +TP_LOCALLAB_HIGHMASKCOL;Highlights mask TP_LOCALLAB_HLH;Curves H TP_LOCALLAB_IND;Independent (mouse) TP_LOCALLAB_INDSL;Independent (mouse + sliders) TP_LOCALLAB_INVERS;Inverse TP_LOCALLAB_INVERS_TOOLTIP;If selected (inverse) less possibilities.\n\nAlternative\nFirst Spot:\n full image - delimiter outside preview\n Shape RT-spot area : rectangle. Transition 100\n\nSecond spot : Excluding spot TP_LOCALLAB_ISOGR;Coarseness (ISO) -TP_LOCALLAB_LABEL;Local Adjustments TP_LOCALLAB_LABBLURM;Mask Blur +TP_LOCALLAB_LABEL;Local Adjustments TP_LOCALLAB_LABGRID;Color correction grid TP_LOCALLAB_LABGRIDMERG;Background TP_LOCALLAB_LABGRID_VALUES;High(a)=%1 High(b)=%2\nLow(a)=%3 Low(b)=%4 TP_LOCALLAB_LABSTRUM;Mask Structure TP_LOCALLAB_LAPLACC;ΔØ Mask Laplacian solve PDE -TP_LOCALLAB_LAP_MASK_TOOLTIP;Solve PDE for all Laplacian masks.\nIf enabled Laplacian threshold mask reduce artifacts and smooth result.\nIf disabled linear response. TP_LOCALLAB_LAPLACE;Δ - Laplacian threshold ΔE TP_LOCALLAB_LAPLACEXP;∆ - Laplacian threshold TP_LOCALLAB_LAPMASKCOL;∆ - Laplacian threshold mask TP_LOCALLAB_LAPRAD_TOOLTIP;Avoid using Radius and Laplace Threshold simultaneously.\nLaplacian threshold reduce contrast, artifacts, smooth result (if PDE settings enabled). +TP_LOCALLAB_LAP_MASK_TOOLTIP;Solve PDE for all Laplacian masks.\nIf enabled Laplacian threshold mask reduce artifacts and smooth result.\nIf disabled linear response. TP_LOCALLAB_LC_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated.\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\n +TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 TP_LOCALLAB_LEVELBLUR;Maximum Blur levels TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;On the abscissa local contrast (near concept luminance). On the ordinate, amplification or reduction local contrast. TP_LOCALLAB_LEVELWAV;Ψ Wavelets Levels @@ -2528,27 +2540,30 @@ TP_LOCALLAB_LIGHTNESS;Lightness TP_LOCALLAB_LIGHTN_TOOLTIP;In inverse mode: selection = -100 force luminance to zero TP_LOCALLAB_LIGHTRETI;Lightness TP_LOCALLAB_LINEAR;Linearity +TP_LOCALLAB_LIST_NAME;Add tool to current spot... +TP_LOCALLAB_LIST_TOOLTIP;Choose a tool and then its level of complexity "Normal" or "Expert".\nThe number reflects the place of the tool in the process of each RT-Spot TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Give priority to action on midtones and high lights and by choosing the concerned wavelet levels TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights TP_LOCALLAB_LOCCONT;Unsharp Mask TP_LOCALLAB_LOC_CONTRAST;Local contrast -Wavelet - defects TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramid 1: -TP_LOCALLAB_LOC_CONTRASTPYRLAB; Graduated Filter - Edge Sharpness - Blur TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramid 2: TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contrast by Levels- Tone Mapping - Dir. Contrast +TP_LOCALLAB_LOC_CONTRASTPYRLAB; Graduated Filter - Edge Sharpness - Blur TP_LOCALLAB_LOC_RESIDPYR;Residual Image Main TP_LOCALLAB_LOG;Encoding log TP_LOCALLAB_LOGAUTO;Automatic +TP_LOCALLAB_LOGAUTO_TOOLTIP;Pressing this button will bring an evaluation of dynamic range and Source Gray point (if "Automatic" Source gray enabled).\nTo be able to touch up the automatic values, press the button again +TP_LOCALLAB_LOGBASE_TOOLTIP;Default = 2.\nValues less than 2 reduce the action of the algorithm, the shadows are darker, the highlights are brighter.\nValues greater than 2 change the action of the algorithm, the shadows are grayer the highlights are more washed out +TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;Estimated values of Dynamic Range - Black Ev and White Ev +TP_LOCALLAB_LOGENCOD_TOOLTIP;Allows Tone Mapping with Logarithm encoding (ACES).\nUsefull for underexposed pictures, or with high dynamic range.\n\nTwo steps in the process : 1) Calculate Dynamic Range 2) User adaptation TP_LOCALLAB_LOGFRA;Source Gray Point +TP_LOCALLAB_LOGFRAME_TOOLTIP;Calculate or use Exposure levels of the image early in the process:\n Black Ev, White Ev and Source Gray point.\n Take into account main exposure compensation. TP_LOCALLAB_LOGLIN;Logarithm mode TP_LOCALLAB_LOGPFRA;Relative Exposure Levels -TP_LOCALLAB_LOGENCOD_TOOLTIP;Allows Tone Mapping with Logarithm encoding (ACES).\nUsefull for underexposed pictures, or with high dynamic range.\n\nTwo steps in the process : 1) Calculate Dynamic Range 2) User adaptation -TP_LOCALLAB_LOGFRAME_TOOLTIP;Calculate or use Exposure levels of the image early in the process:\n Black Ev, White Ev and Source Gray point.\n Take into account main exposure compensation. -TP_LOCALLAB_LOGAUTO_TOOLTIP;Pressing this button will bring an evaluation of dynamic range and Source Gray point (if "Automatic" Source gray enabled).\nTo be able to touch up the automatic values, press the button again -TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;Estimated values of Dynamic Range - Black Ev and White Ev TP_LOCALLAB_LOGSRCGREY_TOOLTIP;Estimated gray point value of the image, early in the process TP_LOCALLAB_LOGTARGGREY_TOOLTIP;You can change this value to adapt it to your taste. -TP_LOCALLAB_LOGBASE_TOOLTIP;Default = 2.\nValues less than 2 reduce the action of the algorithm, the shadows are darker, the highlights are brighter.\nValues greater than 2 change the action of the algorithm, the shadows are grayer the highlights are more washed out +TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0 TP_LOCALLAB_LUM;Curves LL - CC TP_LOCALLAB_LUMADARKEST;Darkest TP_LOCALLAB_LUMASK;Luminance Background Mask @@ -2557,8 +2572,8 @@ TP_LOCALLAB_LUMAWHITESEST;Whiteest TP_LOCALLAB_LUMONLY;Luminance only TP_LOCALLAB_MASFRAME;Mask and Merge TP_LOCALLAB_MASFRAME_TOOLTIP;For all masks.\nTake into account deltaE image to avoid retouching the selection area when sliders gamma mask, slope mask, chroma mask and curves contrast , levels contrasts, and mask blur, structure(if enabled tool) are used.\nDisabled in Inverse -TP_LOCALLAB_MASK2;Contrast curve mask TP_LOCALLAB_MASK;Mask +TP_LOCALLAB_MASK2;Contrast curve mask TP_LOCALLAB_MASKCOL;Mask Curves TP_LOCALLAB_MASKH;Hue curve mask TP_LOCALLAB_MASK_TOOLTIP;You can enable multiple masks for a single tool, this requires activating another tool (but without using the tool : sliders to 0,...) where is the mask you want to activate.\n\nYou can also duplicate the RT-spot and place it right next to each other,variations of references allow fine work on images. @@ -2575,6 +2590,7 @@ TP_LOCALLAB_MERGE1COLFRA;Merge with Original or Previous or Background TP_LOCALLAB_MERGECOLFRA;Mask: LCH & Structure TP_LOCALLAB_MERGEFIV;Previous Spot(Mask 7) + Mask LCH TP_LOCALLAB_MERGEFOU;Previous Spot(Mask 7) +TP_LOCALLAB_MERGEMER_TOOLTIP;Take into account ΔE to merge files (equivalent of scope for this use) TP_LOCALLAB_MERGENONE;None TP_LOCALLAB_MERGEONE;Short Curves 'L' Mask TP_LOCALLAB_MERGEOPA_TOOLTIP;Opacity merge % current Spot with original or previous Spot.\nContrast threshold : adjust result in function of Original contrast @@ -2582,7 +2598,6 @@ TP_LOCALLAB_MERGETHR;Original(Mask 7) + Mask LCH TP_LOCALLAB_MERGETWO;Original(Mask 7) TP_LOCALLAB_MERGETYPE;Merge image and mask TP_LOCALLAB_MERGETYPE_TOOLTIP;None, use all mask in LCH mode.\nShort curves 'L' mask, use a short circuit for mask 2, 3, 4, 6, 7.\nOriginal mask 8, blend current image with original -TP_LOCALLAB_MERGEMER_TOOLTIP;Take into account ΔE to merge files (equivalent of scope for this use) TP_LOCALLAB_MERHEI;Overlay TP_LOCALLAB_MERHUE;Hue TP_LOCALLAB_MERLUCOL;Luminance @@ -2590,10 +2605,10 @@ TP_LOCALLAB_MERLUM;Luminosity TP_LOCALLAB_MERNIN;Screen TP_LOCALLAB_MERONE;Normal TP_LOCALLAB_MERSAT;Saturation +TP_LOCALLAB_MERSEV;Soft Light Photshop TP_LOCALLAB_MERSEV0;Soft Light Illusion TP_LOCALLAB_MERSEV1;Soft Light W3C TP_LOCALLAB_MERSEV2;Hard Light -TP_LOCALLAB_MERSEV;Soft Light Photshop TP_LOCALLAB_MERSIX;Divide TP_LOCALLAB_MERTEN;Darken only TP_LOCALLAB_MERTHI;Color Burn @@ -2629,8 +2644,8 @@ TP_LOCALLAB_OFFS;Offset TP_LOCALLAB_OFFSETWAV;Offset TP_LOCALLAB_OPACOL;Opacity TP_LOCALLAB_ORIGLC;Merge only with original image -TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced TP_LOCALLAB_ORRETILAP_TOOLTIP;Acts on a second Laplacian threshold, to take into account ΔE to differentiate the action especially with the background (different from Scope) +TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced TP_LOCALLAB_PASTELS2;Vibrance TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ @@ -2659,15 +2674,16 @@ TP_LOCALLAB_RESIDHITHR;Highlights threshold TP_LOCALLAB_RESIDSHA;Shadows TP_LOCALLAB_RESIDSHATHR;Shadows threshold TP_LOCALLAB_RETI;Dehaze - Retinex Strong contrast -TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible. -TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast. -TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo. -TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times TP_LOCALLAB_RETIFRA;Retinex TP_LOCALLAB_RETIM;Original Retinex TP_LOCALLAB_RETITOOLFRA;Retinex Tools TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated\nThe treatment time depends on the value of scale (be carefull to high values).\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\nOptimization not used in Preview +TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast. +TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible. +TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo. TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP;Adapt these values according to images - if misty images and depending on whether you want to act on the front or the background +TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times +TP_LOCALLAB_RET_TOOLNAME;Dehaze & Retinex - 9 TP_LOCALLAB_REWEI;Reweighting iterates TP_LOCALLAB_RGB;RGB Tone Curve TP_LOCALLAB_ROW_NVIS;Not visible @@ -2704,14 +2720,15 @@ TP_LOCALLAB_SHAPE_TOOLTIP;Elipse is normal mode.\nRectangle can be used in some TP_LOCALLAB_SHARAMOUNT;Amount TP_LOCALLAB_SHARBLUR;Blur radius TP_LOCALLAB_SHARDAMPING;Damping -TP_LOCALLAB_SHARITER;Iterations TP_LOCALLAB_SHARFRAME;Modifications +TP_LOCALLAB_SHARITER;Iterations TP_LOCALLAB_SHARP;Sharpening +TP_LOCALLAB_SHARP_TOOLNAME;Sharpening - 8 TP_LOCALLAB_SHARRADIUS;Radius TP_LOCALLAB_SHORTC;Short Curves 'L' Mask TP_LOCALLAB_SHORTCMASK_TOOLTIP;Short circuit the 2 curves L(L) and L(H).\nAllows you to mix the current image with the original image modified by the mask job.\nUsable with masks 2, 3, 4, 6, 7 -TP_LOCALLAB_SHOWC1;Merge file TP_LOCALLAB_SHOWC;Mask and modifications +TP_LOCALLAB_SHOWC1;Merge file TP_LOCALLAB_SHOWCB;Mask and modifications TP_LOCALLAB_SHOWDCT;Show process Fourier ƒ TP_LOCALLAB_SHOWE;Mask and modifications @@ -2732,14 +2749,15 @@ TP_LOCALLAB_SHOWNORMAL;Normalize luminance (no) TP_LOCALLAB_SHOWPLUS;Mask and modifications - Smooth-Blur & Denoise TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ) TP_LOCALLAB_SHOWR;Mask and modifications -TP_LOCALLAB_SHOWS;Mask and modifications TP_LOCALLAB_SHOWREF;Preview ΔE +TP_LOCALLAB_SHOWS;Mask and modifications TP_LOCALLAB_SHOWSTRUC;Show structure Spot TP_LOCALLAB_SHOWSTRUCEX;Show structure Spot TP_LOCALLAB_SHOWT;Mask and modifications TP_LOCALLAB_SHOWVI;Mask and modifications TP_LOCALLAB_SHRESFRA;Shadows/Highlights TP_LOCALLAB_SHTRC_TOOLTIP;Modifies the tones of the image by acting on a TRC (Tone Response Curve).\nGamma acts mainly on light tones.\nSlope acts mainly on dark tones +TP_LOCALLAB_SH_TOOLNAME;Shadows Highlight & Tone Equalizer - 5 TP_LOCALLAB_SIGMAWAV;Attenuation Response TP_LOCALLAB_SIM;Simple TP_LOCALLAB_SLOMASKCOL;Slope mask @@ -2750,11 +2768,12 @@ TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex is very different from others Re TP_LOCALLAB_SOFTRADIUSCOL;Soft radius TP_LOCALLAB_SOFTRETI;Reduce artifact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Take into account deltaE to improve Transmission map +TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 TP_LOCALLAB_SOURCE_GRAY;Value +TP_LOCALLAB_SPECCASE; Specific cases TP_LOCALLAB_SPECIAL;Special use of RGB curves TP_LOCALLAB_SPECIAL_TOOLTIP;Only for this RGB curve, disabled (or reduce effects) of Scope, mask...for example, if you want to have a negative effect. -TP_LOCALLAB_SPECCASE; Specific cases -TP_LOCALLAB_SPOTNAME;Control Spot # +TP_LOCALLAB_SPOTNAME;New Spot TP_LOCALLAB_STD;Standard TP_LOCALLAB_STR;Strength TP_LOCALLAB_STRBL;Strength @@ -2765,9 +2784,9 @@ TP_LOCALLAB_STRENGTH;Noise TP_LOCALLAB_STRGRID;Strength TP_LOCALLAB_STRRETI_TOOLTIP;if Strength Retinex < 0.2 only Dehaze is enabled.\nif Strength Retinex >= 0.1 Dehaze is in luminance mode. TP_LOCALLAB_STRUC;Structure +TP_LOCALLAB_STRUCCOL;Structure TP_LOCALLAB_STRUCCOL1;Structure Spot TP_LOCALLAB_STRUCT_TOOLTIP;Use Sobel algorithm to take into account structure in shape detection.\nyou can have a preview by activating "mask and modifications - Show structure spot".\n\nCan be used in conjunction with masks (expert) structure, blur, wavelet to improve edge detection.\n\nNeeds maskless adjustments to be activated (lightness, exposure...) -TP_LOCALLAB_STRUCCOL;Structure TP_LOCALLAB_STRUMASKCOL;Structure mask strength TP_LOCALLAB_STRUMASK_TOOLTIP;Generate a structure mask with difference between surface areas and reliefs.\nIf structure mask as tool is enabled, this mask is used in addition to the other tools (gamma, slope, contrast curve ...) TP_LOCALLAB_STYPE;Shape method @@ -2779,17 +2798,18 @@ TP_LOCALLAB_THRES;Threshold structure TP_LOCALLAB_THRESDELTAE;Threshold ΔE-scope TP_LOCALLAB_THRESRETI;Threshold TP_LOCALLAB_THRESWAV;Balance Threshold -TP_LOCALLAB_TLABEL2;TM Effective Tm=%1 TM=%2 TP_LOCALLAB_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 (Threshold) +TP_LOCALLAB_TLABEL2;TM Effective Tm=%1 TM=%2 TP_LOCALLAB_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.\nYou can act on Threshold to normalize TP_LOCALLAB_TM;Tone Mapping - Texture TP_LOCALLAB_TM_MASK;Use transmission map TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;This parameter affects sensitivity to edges.\n The greater it is the more likely an illumination change is to be considered an "edge".\n If set to zero tone mapping will have an effect similar to unsharp masking. -TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;In some cases tone mapping may result in a cartoonish appearance, and in some rare cases soft but wide halos may appear.\n Increasing the number of reweighting iterates will help fight some of these problems. -TP_LOCALLAB_TONEMASCALE_TOOLTIP;This control gives meaning to the difference between "local" and "global" contrast.\nThe greater it is the larger a detail needs to be in order to be boosted TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma moves the action of tone-mapping to shadows or highlights. -TP_LOCALLAB_TOOLCOL;Structure mask as tool +TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;In some cases tone mapping may result in a cartoonish appearance, and in some rare cases soft but wide halos may appear.\n Increasing the number of reweighting iterates will help fight some of these problems. TP_LOCALLAB_TONEMAP_TOOLTIP;Tone Mapping - main menu must be disabled +TP_LOCALLAB_TONEMASCALE_TOOLTIP;This control gives meaning to the difference between "local" and "global" contrast.\nThe greater it is the larger a detail needs to be in order to be boosted +TP_LOCALLAB_TONE_TOOLNAME;Tone Mapping - 4 +TP_LOCALLAB_TOOLCOL;Structure mask as tool TP_LOCALLAB_TOOLMASK;Tools TP_LOCALLAB_TRANSIT;Transition Gradient TP_LOCALLAB_TRANSITGRAD;Transition differentiation XY @@ -2804,58 +2824,38 @@ TP_LOCALLAB_TRANSMISSION_TOOLTIP;Transmission according to transmission.\nAbscis TP_LOCALLAB_USEMASK;Use mask TP_LOCALLAB_VART;Variance (contrast) TP_LOCALLAB_VIBRANCE;Vibrance - Warm & Cool -TP_LOCALLAB_VIS_TOOLTIP;Click to show/hide selected Control Spot.\nCtrl+click to show/hide all Control Spot. -TP_LOCALLAB_LIST_NAME;Add tool to current spot... -TP_LOCALLAB_LIST_TOOLTIP;Choose a tool and then its level of complexity "Normal" or "Expert".\nThe number reflects the place of the tool in the process of each RT-Spot -TP_LOCALLAB_COLOR_TOOLNAME;Color&Light (Defects) - 11 -TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10 -TP_LOCALLAB_SH_TOOLNAME;Shadows Highlight & Tone Equalizer - 5 TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Warm & Cool - 3 -TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 -TP_LOCALLAB_BLUR_TOOLNAME;Smooth Blur Gain & Denoise - 1 -TP_LOCALLAB_TONE_TOOLNAME;Tone Mapping - 4 -TP_LOCALLAB_RET_TOOLNAME;Dehaze & Retinex - 9 -TP_LOCALLAB_SHARP_TOOLNAME;Sharpening - 8 -TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 -TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2 -TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0 +TP_LOCALLAB_VIS_TOOLTIP;Click to show/hide selected Control Spot.\nCtrl+click to show/hide all Control Spot. TP_LOCALLAB_WAMASKCOL;Ψ Mask Wavelet level TP_LOCALLAB_WARM;Warm - Cool & Color artifacts TP_LOCALLAB_WARM_TOOLTIP;This slider use Ciecam algorithm and acts as White Balance, it can warm or cool the area selected.\nIt can also in some cases reduce color artifacts. +TP_LOCALLAB_WASDEN_TOOLTIP;Denoise luminance for the 3 first levels (fine).\nThe right limit of the curve correspond to coarse : level 3 and beyond TP_LOCALLAB_WAV;Levels local contrast -TP_LOCALLAB_WAVMASK_TOOLTIP;Allows fine work on mask levels contrasts (structure) TP_LOCALLAB_WAVBLUR_TOOLTIP;Performs a blur for each level of decomposition, as well as the residual image. -TP_LOCALLAB_WAVEDG;Local contrast TP_LOCALLAB_WAVCOMP;Compression by Level -TP_LOCALLAB_WAVCOMP_TOOLTIP;Achive local contrast in function of the direction wavelet decomposition : horizontal, vertical, diagonal TP_LOCALLAB_WAVCOMPRE;(un)Compression by Level TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Achieve a Tone-mapping or reduction local contrast by levels.\nOn abscissa levels +TP_LOCALLAB_WAVCOMP_TOOLTIP;Achive local contrast in function of the direction wavelet decomposition : horizontal, vertical, diagonal TP_LOCALLAB_WAVCON;Contrast by Level TP_LOCALLAB_WAVCONTF_TOOLTIP;Similar to Contrast By Detail Levels : on abscissa levels. TP_LOCALLAB_WAVDEN;Luminance denoise by level (0 1 2 + 3 and more) -TP_LOCALLAB_WASDEN_TOOLTIP;Denoise luminance for the 3 first levels (fine).\nThe right limit of the curve correspond to coarse : level 3 and beyond TP_LOCALLAB_WAVE;Ψ Wavelet +TP_LOCALLAB_WAVEDG;Local contrast TP_LOCALLAB_WAVEEDG_TOOLTIP;Achieves a sharpness taking into account the notion of edges wavelet.\nRequires that at least the first 4 levels are usable TP_LOCALLAB_WAVGRAD_TOOLTIP;Graduated filter for Local contrast "luminance" TP_LOCALLAB_WAVHIGH;Ψ Wavelet high TP_LOCALLAB_WAVLEV;Blur by Level TP_LOCALLAB_WAVLOW;Ψ Wavelet low TP_LOCALLAB_WAVMASK;Ψ Mask Levels local contrast +TP_LOCALLAB_WAVMASK_TOOLTIP;Allows fine work on mask levels contrasts (structure) TP_LOCALLAB_WAVMED;Ψ Wavelet normal TP_LOCALLAB_WEDIANHI;Median Hi TP_LOCALLAB_WHITE_EV;White Ev -TP_LOCALLAB_BLNOI_EXP;Blur & Noise -TP_LOCALLAB_DENOI_EXP;Denoise TP_LOCAL_HEIGHT;Bottom TP_LOCAL_HEIGHT_T;Top TP_LOCAL_WIDTH;Right TP_LOCAL_WIDTH_L;Left TP_LOCRETI_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\n -TP_LOCALCONTRAST_AMOUNT;Amount -TP_LOCALCONTRAST_DARKNESS;Darkness level -TP_LOCALCONTRAST_LABEL;Local Contrast -TP_LOCALCONTRAST_LIGHTNESS;Lightness level -TP_LOCALCONTRAST_RADIUS;Radius TP_METADATA_EDIT;Apply modifications TP_METADATA_MODE;Metadata copy mode TP_METADATA_STRIP;Strip all metadata @@ -2871,8 +2871,8 @@ TP_PCVIGNETTE_STRENGTH;Strength TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners). TP_PDSHARPENING_LABEL;Capture Sharpening TP_PERSPECTIVE_CAMERA_CROP_FACTOR;Crop factor -TP_PERSPECTIVE_CAMERA_FRAME;Correction TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH;Focal length +TP_PERSPECTIVE_CAMERA_FRAME;Correction TP_PERSPECTIVE_CAMERA_PITCH;Vertical TP_PERSPECTIVE_CAMERA_ROLL;Rotation TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift @@ -2881,8 +2881,8 @@ TP_PERSPECTIVE_CAMERA_YAW;Horizontal TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_METHOD;Method -TP_PERSPECTIVE_METHOD_SIMPLE;Simple TP_PERSPECTIVE_METHOD_CAMERA_BASED;Camera-based +TP_PERSPECTIVE_METHOD_SIMPLE;Simple TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME;Post-correction adjustment TP_PERSPECTIVE_PROJECTION_PITCH;Vertical TP_PERSPECTIVE_PROJECTION_ROTATE;Rotation @@ -3188,12 +3188,13 @@ TP_WAVELET_BACUR;Curve TP_WAVELET_BALANCE;Contrast balance d/v-h TP_WAVELET_BALANCE_TOOLTIP;Alters the balance between the wavelet directions: vertical-horizontal and diagonal.\nIf contrast, chroma or residual tone mapping are activated, the effect due to balance is amplified. TP_WAVELET_BALCHRO;Chrominance balance -TP_WAVELET_BALCHRO_TOOLTIP;If enabled, the 'Contrast balance' curve or slider also modifies chroma balance. TP_WAVELET_BALCHROM;Denoise Equalizer Blue-yellow Red-green +TP_WAVELET_BALCHRO_TOOLTIP;If enabled, the 'Contrast balance' curve or slider also modifies chroma balance. TP_WAVELET_BALLUM;Denoise Equalizer White-Black TP_WAVELET_BANONE;None TP_WAVELET_BASLI;Slider TP_WAVELET_BATYPE;Contrast balance method +TP_WAVELET_BL;Blur levels TP_WAVELET_BLCURVE;Blur by levels TP_WAVELET_BLURFRAME;Blur TP_WAVELET_BLUWAV;Attenuation Response @@ -3211,8 +3212,8 @@ TP_WAVELET_CHROMAFRAME;Chroma TP_WAVELET_CHROMCO;Chrominance Coarse TP_WAVELET_CHROMFI;Chrominance Fine TP_WAVELET_CHRO_TOOLTIP;Sets the wavelet level which will be the threshold between saturated and pastel colors.\n1-x: saturated\nx-9: pastel\n\nIf the value exceeds the amount of wavelet levels you are using then it will be ignored. -TP_WAVELET_CHR_TOOLTIP;Adjusts chroma as a function of "contrast levels" and "chroma-contrast link strength" TP_WAVELET_CHRWAV;Blur chroma +TP_WAVELET_CHR_TOOLTIP;Adjusts chroma as a function of "contrast levels" and "chroma-contrast link strength" TP_WAVELET_CHSL;Sliders TP_WAVELET_CHTYPE;Chrominance method TP_WAVELET_CLA;Clarity @@ -3241,18 +3242,20 @@ TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Modifies the residual image's hue as a function of hue. TP_WAVELET_DALL;All directions TP_WAVELET_DAUB;Edge performance -TP_WAVELET_DAUBLOCAL;Wavelet Edge performance TP_WAVELET_DAUB2;D2 - low TP_WAVELET_DAUB4;D4 - standard TP_WAVELET_DAUB6;D6 - standard plus TP_WAVELET_DAUB10;D10 - medium TP_WAVELET_DAUB14;D14 - high +TP_WAVELET_DAUBLOCAL;Wavelet Edge performance TP_WAVELET_DAUB_TOOLTIP;Changes Daubechies coefficients:\nD4 = Standard,\nD14 = Often best performance, 10% more time-intensive.\n\nAffects edge detection as well as the general quality of the firsts levels. However the quality is not strictly related to this coefficient and can vary with images and uses. TP_WAVELET_DIRFRAME;Directional contrast TP_WAVELET_DONE;Vertical TP_WAVELET_DTHR;Diagonal TP_WAVELET_DTWO;Horizontal TP_WAVELET_EDCU;Curve +TP_WAVELET_EDEFFECT;Attenuation Response +TP_WAVELET_EDEFFECT_TOOLTIP;This slider controls how wide the range of contrast values are that receive the maximum effect from the tool.\nMaximum value (2.5) disabled the tool TP_WAVELET_EDGCONT;Local contrast TP_WAVELET_EDGCONT_TOOLTIP;Adjusting the points to the left decreases contrast, and to the right increases it.\nBottom-left, top-left, top-right and bottom-right represent respectively local contrast for low values, mean, mean+stdev and maxima. TP_WAVELET_EDGE;Edge Sharpness @@ -3262,8 +3265,6 @@ TP_WAVELET_EDGEDETECTTHR;Threshold low (noise) TP_WAVELET_EDGEDETECTTHR2;Threshold high (detection) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;This adjuster lets you target edge detection for example to avoid applying edge sharpness to fine details, such as noise in the sky. TP_WAVELET_EDGEDETECT_TOOLTIP;Moving the slider to the right increases edge sensitivity. This affects local contrast, edge settings and noise. -TP_WAVELET_EDEFFECT;Attenuation Response -TP_WAVELET_EDEFFECT_TOOLTIP;This slider controls how wide the range of contrast values are that receive the maximum effect from the tool.\nMaximum value (2.5) disabled the tool TP_WAVELET_EDGESENSI;Edge sensitivity TP_WAVELET_EDGREINF_TOOLTIP;Reinforce or reduce the action of the first level, do the opposite to the second level, and leave the rest unchanged. TP_WAVELET_EDGTHRESH;Detail @@ -3275,9 +3276,9 @@ TP_WAVELET_EDTYPE;Local contrast method TP_WAVELET_EDVAL;Strength TP_WAVELET_FINAL;Final Touchup TP_WAVELET_FINCFRAME;Final Local Contrast +TP_WAVELET_FINCOAR_TOOLTIP;The left (positive) part of the curve acts on the finer levels (increase).\nThe 2 points on the abscissa represent the respective action limits of finer and coarser levels 5 and 6 (default).\nThe right (negative) part of the curve acts on the coarser levels (increase).\nAvoid moving the left part of the curve with negative values. Avoid moving the right part of the curve with positives values TP_WAVELET_FINEST;Finest TP_WAVELET_HIGHLIGHT;Finer levels luminance range -TP_WAVELET_FINCOAR_TOOLTIP;The left (positive) part of the curve acts on the finer levels (increase).\nThe 2 points on the abscissa represent the respective action limits of finer and coarser levels 5 and 6 (default).\nThe right (negative) part of the curve acts on the coarser levels (increase).\nAvoid moving the left part of the curve with negative values. Avoid moving the right part of the curve with positives values TP_WAVELET_HS1;Whole luminance range TP_WAVELET_HS2;Selective luminance range TP_WAVELET_HUESKIN;Skin hue @@ -3320,6 +3321,7 @@ TP_WAVELET_NPLOW;Low TP_WAVELET_NPNONE;None TP_WAVELET_NPTYPE;Neighboring pixels TP_WAVELET_NPTYPE_TOOLTIP;This algorithm uses the proximity of a pixel and eight of its neighbors. If less difference, edges are reinforced. +TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Attenuation Response value you will able to select the contrasts that will be enhanced. TP_WAVELET_OLDSH;Algorithm using negatives values TP_WAVELET_OPACITY;Opacity Blue-Yellow TP_WAVELET_OPACITYW;Contrast balance d/v-h curve @@ -3359,8 +3361,8 @@ TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Shadows threshold TP_WAVELET_THRESHOLD;Finer levels TP_WAVELET_THRESHOLD2;Coarser levels -TP_WAVELET_THRESHOLD_TOOLTIP;Only levels below and including the chosen value will be affected by the Highlight luminance range. TP_WAVELET_THRESHOLD2_TOOLTIP;Only levels from the chosen value to the selected number of ‘wavelet levels’ will be affected by the Shadow luminance range. +TP_WAVELET_THRESHOLD_TOOLTIP;Only levels below and including the chosen value will be affected by the Highlight luminance range. TP_WAVELET_THRESWAV;Balance Threshold TP_WAVELET_THRH;Highlights threshold TP_WAVELET_TILESBIG;Tiles @@ -3368,22 +3370,19 @@ TP_WAVELET_TILESFULL;Full image TP_WAVELET_TILESIZE;Tiling method TP_WAVELET_TILESLIT;Little tiles TP_WAVELET_TILES_TOOLTIP;Processing the full image leads to better quality and is the recommended option, while using tiles is a fall-back solution for users with little RAM. Refer to RawPedia for memory requirements. -TP_WAVELET_BL;Blur levels TP_WAVELET_TMEDGS;Edge stopping TP_WAVELET_TMSCALE;Scale TP_WAVELET_TMSTRENGTH;Compression strength TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. -TP_WAVELET_TMEDGS;Edge stopping +TP_WAVELET_TMTYPE;Compression method TP_WAVELET_TON;Toning TP_WAVELET_TONFRAME;Excluded Colors -TP_WAVELET_TMTYPE;Compression method TP_WAVELET_USH;None TP_WAVELET_USHARP;Clarity method TP_WAVELET_USHARP_TOOLTIP;Origin : the source file is the file before Wavelet.\nWavelet : the source file is the file including wavelet threatment TP_WAVELET_USH_TOOLTIP;If you select Sharp-mask, wavelet settings will be automatically positioned :\nBackground=black, Process=below, level=3...you can change level between 1 and 4.\n\nIf you select Clarity, wavelet settings will be automatically positioned :\nBackground=residual, Process=above, level=7..you can change level between 5 and 10 and wavelet levels. TP_WAVELET_WAVLOWTHR;Low contrast threshold TP_WAVELET_WAVOFFSET;Offset -TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Attenuation Response value you will able to select the contrasts that will be enhanced. TP_WBALANCE_AUTO;Auto TP_WBALANCE_AUTOITCGREEN;Temperature correlation TP_WBALANCE_AUTOOLD;RGB grey @@ -3445,3 +3444,4 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - + From 8293d847d36711e0fb07d4b284631be7795db83f Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Sat, 27 Jun 2020 13:19:25 +0200 Subject: [PATCH 049/114] Japanese translation updated by Yz2house, closes #5812 --- rtdata/languages/Japanese | 1293 ++++++++++++++++++++++++++++++++++--- 1 file changed, 1216 insertions(+), 77 deletions(-) diff --git a/rtdata/languages/Japanese b/rtdata/languages/Japanese index b7c0b7e22..22dc027ea 100644 --- a/rtdata/languages/Japanese +++ b/rtdata/languages/Japanese @@ -8,6 +8,7 @@ #08 2012-12-22 a3novy #09 2013-04-01 a3novy #10 2013-04-19 a3novy +#11 2020-06-24 Yz2house ABOUT_TAB_BUILD;バージョン ABOUT_TAB_CREDITS;クレジット @@ -25,7 +26,7 @@ CURVEEDITOR_CURVE;カーブ CURVEEDITOR_CURVES;カーブ CURVEEDITOR_CUSTOM;カスタム CURVEEDITOR_DARKS;ダーク -CURVEEDITOR_EDITPOINT_HINT;ボタンを押すと数値で入出力を編集出来ます\n\n編集したいカーブ上のポイントを右クリックします\n編集を無効にする場合はポイント以外の部分で右クリックします +CURVEEDITOR_EDITPOINT_HINT;ボタンを押すと数値で入出力値を変えられるようになります\n\nカーブ上で目標ポイントを右クリックし、カーブ下に表示されるI(入力値)或いはO(出力値)\n編集するポイントを変更する場合はポイント以外の部分で右クリックします CURVEEDITOR_HIGHLIGHTS;ハイライト CURVEEDITOR_LIGHTS;ライト CURVEEDITOR_LINEAR;リニア @@ -103,14 +104,14 @@ EXPORT_BYPASS_SHARPENEDGE;エッジ・シャープニングを迂回 EXPORT_BYPASS_SHARPENING;シャープニングを迂回 EXPORT_BYPASS_SHARPENMICRO;マイクロコントラストを迂回 EXPORT_FASTEXPORTOPTIONS;高速書き出しオプション -EXPORT_INSTRUCTIONS;現像の設定に要する時間と手間を省くために、高速書き出しを優先させるオプションです。処理速度が優先される場合や、既定の現像パラメータを変えずに何枚ものリサイズ画像が要求される場合に奨められる方法で、低解像度画像を迅速に生成します。 +EXPORT_INSTRUCTIONS;出力設定に要する時間と手間を省くために、高速書き出しを優先させるオプションです。処理速度が優先される場合や、既定の出力パラメータを変えずに何枚ものリサイズ画像が要求される場合に奨められる方法で、低解像度画像を迅速に生成します。 EXPORT_MAXHEIGHT;最大高: EXPORT_MAXWIDTH;最大幅: EXPORT_PIPELINE;高速書き出しの方法 EXPORT_PUTTOQUEUEFAST; 高速書き出しのキューに追加 EXPORT_RAW_DMETHOD;デモザイクの方式 EXPORT_USE_FAST_PIPELINE;処理速度優先(リサイズした画像に調整を全て行う) -EXPORT_USE_FAST_PIPELINE_TIP;処理速度を優先すると、処理は速くなりますが、画像の質は落ちます。通常、画像のリサイズは全現像処理工程の最後で行われますが、ここでは処理工程の初めの方でリサイズが行われます。処理速度は著しく上がりますが、現像処理された画像にアーティファクトが発生したり、全体的な質が低下したりします。 +EXPORT_USE_FAST_PIPELINE_TIP;処理速度を優先すると、処理は速くなりますが、画像の質は落ちます。通常、画像のリサイズは全処理工程の最後で行われますが、ここでは処理工程の初めの方でリサイズが行われます。処理速度は著しく上がりますが、処理された画像にアーティファクトが発生したり、全体的な質が低下したりします。 EXPORT_USE_NORMAL_PIPELINE;標準(リサイズ処理は最後、幾つかの処理を迂回) EXTPROGTARGET_1;raw EXTPROGTARGET_2;キュー処理 @@ -232,6 +233,7 @@ GENERAL_DISABLED;無効 GENERAL_ENABLE;有効 GENERAL_ENABLED;有効 GENERAL_FILE;ファイル +GENERAL_HELP;ヘルプ GENERAL_LANDSCAPE;横 GENERAL_NA;n/a GENERAL_NO;No @@ -262,7 +264,7 @@ HISTORY_MSG_1;写真を読み込みました HISTORY_MSG_2;PP3を読み込みました HISTORY_MSG_3;PP3を変更しました HISTORY_MSG_4;履歴ブラウジング -HISTORY_MSG_5;明度 +HISTORY_MSG_5;明るさ HISTORY_MSG_6;コントラスト HISTORY_MSG_7;黒 HISTORY_MSG_8;露光量補正 @@ -431,16 +433,16 @@ HISTORY_MSG_171;L*a*b* LC カーブ HISTORY_MSG_172;LCの適用をレッドと肌色トーンだけに制限 HISTORY_MSG_173;ノイズ低減 - 細部の復元 HISTORY_MSG_174;CIE色の見えモデル2002 -HISTORY_MSG_175;CAM02 - 色順応量 -HISTORY_MSG_176;CAM02 - 観視の暗い周囲環境 -HISTORY_MSG_177;CAM02 - 撮影環境の順応輝度 -HISTORY_MSG_178;CAM02 - 観視の順応輝度 -HISTORY_MSG_179;CAM02 - モデル +HISTORY_MSG_175;CAM02 - CAT02 +HISTORY_MSG_176;CAM02 - 観視環境 +HISTORY_MSG_177;CAM02 - 画像の輝度 +HISTORY_MSG_178;CAM02 - 観視の輝度 +HISTORY_MSG_179;CAM02 - ホワイトポイントモデル HISTORY_MSG_180;CAM02 - 明度 (J) HISTORY_MSG_181;CAM02 - 色度 (C) HISTORY_MSG_182;CAM02 - 自動 CAT02 HISTORY_MSG_183;CAM02 - コントラスト (J) -HISTORY_MSG_184;CAM02 - 暗い周囲環境を伴う撮影環境 +HISTORY_MSG_184;CAM02 - 画像の周辺環境 HISTORY_MSG_185;CAM02 - 色域制御 HISTORY_MSG_186;CAM02 - アルゴリズム HISTORY_MSG_187;CAM02 - レッドと肌色トーンを保護 @@ -455,8 +457,8 @@ HISTORY_MSG_195;CAM02 - トーンカーブ 1 HISTORY_MSG_196;CAM02 - トーンカーブ 2 HISTORY_MSG_197;CAM02 - カラー・カーブ HISTORY_MSG_198;CAM02 - カラー・カーブ -HISTORY_MSG_199;CAM02 - カーブでCIECAM02出力のヒストグラムを表示 -HISTORY_MSG_200;CAM02 - CIECAM02 Q でトーンマッピング +HISTORY_MSG_199;CAM02 - 出力のヒストグラムを表示 +HISTORY_MSG_200;CAM02 - トーンマッピング HISTORY_MSG_201;色差 レッド/グリーン HISTORY_MSG_202;色差 ブルー/イエロー HISTORY_MSG_203;ノイズ低減 - 方式 @@ -592,7 +594,7 @@ HISTORY_MSG_333;W- 残差 シャドウ HISTORY_MSG_334;W- 残差 色度 HISTORY_MSG_335;W- 残差 ハイライト HISTORY_MSG_336;W- 残差 ハイライトのしきい値 -HISTORY_MSG_337;W- 残差 青空の色相 +HISTORY_MSG_337;W- 残差 色相の目標/保護 HISTORY_MSG_338;W- ES 半径 HISTORY_MSG_339;W- ES 強さ HISTORY_MSG_340;W- 強さ @@ -667,7 +669,7 @@ HISTORY_MSG_408;レティネックス - 半径 HISTORY_MSG_409;レティネックス - コントラスト HISTORY_MSG_410;レティネックス - 明るさ HISTORY_MSG_411;レティネックス - 強さ -HISTORY_MSG_412;レティネックス - ガウス暈しのグラデーション +HISTORY_MSG_412;レティネックス - ガウスフィルタの勾配 HISTORY_MSG_413;レティネックス - 差異 HISTORY_MSG_414;レティネックス - ヒストグラム - Lab HISTORY_MSG_415;レティネックス - 透過 @@ -685,8 +687,8 @@ HISTORY_MSG_426;レティネックス - 色相イコライザ HISTORY_MSG_427;出力レンダリングの意図 HISTORY_MSG_428;モニターレンダリングの意図 HISTORY_MSG_429;レティネックス - 繰り返し -HISTORY_MSG_430;レティネックス - 透過のグラデーション -HISTORY_MSG_431;レティネックス - 強さのグラデーション +HISTORY_MSG_430;レティネックス - 透過マップの勾配 +HISTORY_MSG_431;レティネックス - 強さの勾配 HISTORY_MSG_432;レティネックス - M - ハイライト HISTORY_MSG_433;レティネックス - M - ハイライト TW HISTORY_MSG_434;レティネックス - M - シャドウ @@ -695,21 +697,37 @@ HISTORY_MSG_436;レティネックス - M - 半径 HISTORY_MSG_437;レティネックス - M - 方式 HISTORY_MSG_438;レティネックス - M - イコライザ HISTORY_MSG_439;レティネックス - プロセス -HISTORY_MSG_440;詳細レベルコントラスト - 適用 +HISTORY_MSG_440;詳細レベルコントラスト - 処理の順番 HISTORY_MSG_441;レティネックス - 透過率の増加 HISTORY_MSG_442;レティネックス - スケール HISTORY_MSG_443;出力のブラックポイント補正 HISTORY_MSG_444;WB - 色温度のバイアス HISTORY_MSG_445;Raw サブイメージ +HISTORY_MSG_446;EvPixelShiftMotion +HISTORY_MSG_447;EvPixelShiftMotionCorrection +HISTORY_MSG_448;EvPixelShiftStddevFactorGreen HISTORY_MSG_449;PS - ISOへの適合 +HISTORY_MSG_450;EvPixelShiftNreadIso +HISTORY_MSG_451;EvPixelShiftPrnu HISTORY_MSG_452;PS - モーションを表示 HISTORY_MSG_453;PS - マスクだけを表示 +HISTORY_MSG_454;EvPixelShiftAutomatic +HISTORY_MSG_455;EvPixelShiftNonGreenHorizontal +HISTORY_MSG_456;EvPixelShiftNonGreenVertical HISTORY_MSG_457;PS - レッド/ブルーを確認 +HISTORY_MSG_458;EvPixelShiftStddevFactorRed +HISTORY_MSG_459;EvPixelShiftStddevFactorBlue +HISTORY_MSG_460;EvPixelShiftGreenAmaze +HISTORY_MSG_461;EvPixelShiftNonGreenAmaze HISTORY_MSG_462;PS - グリーンを確認 +HISTORY_MSG_463;EvPixelShiftRedBlueWeight HISTORY_MSG_464;PS - モーションマスクをぼかす HISTORY_MSG_465;PS - ぼかしの半径 +HISTORY_MSG_466;EvPixelShiftSum +HISTORY_MSG_467;EvPixelShiftExp0 HISTORY_MSG_468;PS - 穴を埋める HISTORY_MSG_469;PS - メディアン +HISTORY_MSG_470;EvPixelShiftMedian3 HISTORY_MSG_471;PS - 振れの補正 HISTORY_MSG_472;PS - 境界を滑らかにする HISTORY_MSG_473;PS - LMMSEを使う @@ -732,8 +750,434 @@ HISTORY_MSG_489;DRC - CbDL HISTORY_MSG_490;DRC - 量 HISTORY_MSG_491;ホワイトバランス HISTORY_MSG_492;RGBカーブ -HISTORY_MSG_493;L*a*b*調整 +HISTORY_MSG_493;ローカル調整 HISTORY_MSG_494;キャプチャーシャープニング +HISTORY_MSG_496;ローカル スポット 削除 +HISTORY_MSG_497;ローカル スポット 選択 +HISTORY_MSG_498;ローカル スポット 名前 +HISTORY_MSG_499;ローカル スポット 表示 +HISTORY_MSG_500;ローカル スポット 形状 +HISTORY_MSG_501;ローカル スポット 方法 +HISTORY_MSG_502;ローカル スポット 形状の方式 +HISTORY_MSG_503;ローカル スポット 右の垂直線 +HISTORY_MSG_504;ローカル スポット 左の垂直線 +HISTORY_MSG_505;ローカル スポット 下の水平線 +HISTORY_MSG_506;ローカル スポット 上の水平線 +HISTORY_MSG_507;ローカル スポット 中心 +HISTORY_MSG_508;ローカル スポット 大きさ +HISTORY_MSG_509;ローカル スポット 質の種類 +HISTORY_MSG_510;ローカル スポット 境界 +HISTORY_MSG_511;ローカル スポット しきい値 +HISTORY_MSG_512;ローカル スポット ΔEの減衰 +HISTORY_MSG_513;ローカル スポット スコープ +HISTORY_MSG_514;ローカル スポット 構造 +HISTORY_MSG_515;ローカル調整 +HISTORY_MSG_516;ローカル - 色と明るさ +HISTORY_MSG_517;ローカル - 強力を有効にする +HISTORY_MSG_518;ローカル - 明るさ +HISTORY_MSG_519;ローカル - コントラスト +HISTORY_MSG_520;ローカル - 色度 +HISTORY_MSG_521;ローカル - スコープ +HISTORY_MSG_522;ローカル - カーブの方式 +HISTORY_MSG_523;ローカル - LL カーブ +HISTORY_MSG_524;ローカル - CC カーブ +HISTORY_MSG_525;ローカル - LH カーブ +HISTORY_MSG_526;ローカル - H カーブ +HISTORY_MSG_527;ローカル - 反対色 +HISTORY_MSG_528;ローカル - 露光補正 +HISTORY_MSG_529;ローカル - Exp 露光量補正 +HISTORY_MSG_530;ローカル - Exp ハイライト圧縮 +HISTORY_MSG_531;ローカル - Exp ハイライト圧縮のしきい値 +HISTORY_MSG_532;ローカル - Exp 黒レベル +HISTORY_MSG_533;ローカル - Exp 黒レベルの圧縮 +HISTORY_MSG_534;ローカル - ウォームとクール +HISTORY_MSG_535;ローカル - Exp スコープ +HISTORY_MSG_536;ローカル - Exp コントラストカーブ +HISTORY_MSG_537;ローカル - 自然な彩度 +HISTORY_MSG_538;ローカル - Vib 純色 +HISTORY_MSG_539;ローカル - Vib パステル +HISTORY_MSG_540;ローカル - Vib しきい値 +HISTORY_MSG_541;ローカル - Vib 肌色の保護 +HISTORY_MSG_542;ローカル - Vib 色ずれの回避 +HISTORY_MSG_543;ローカル - Vib リンク +HISTORY_MSG_544;ローカル - Vib スコープ +HISTORY_MSG_545;ローカル - Vib H カーブ +HISTORY_MSG_546;ローカル - ぼかしとノイズ +HISTORY_MSG_547;ローカル - 半径 +HISTORY_MSG_548;ローカル - ノイズ +HISTORY_MSG_549;ローカル - ぼかしのスコープ +HISTORY_MSG_550;ローカル - ぼかしの方式 +HISTORY_MSG_551;ローカル - ぼかし 輝度だけ +HISTORY_MSG_552;ローカル - トーンマッピング +HISTORY_MSG_553;ローカル - TM 強さ +HISTORY_MSG_554;ローカル - TM ガンマ +HISTORY_MSG_555;ローカル - TM エッジ停止 +HISTORY_MSG_556;ローカル - TM スケール +HISTORY_MSG_557;ローカル - TM 再加重 +HISTORY_MSG_558;ローカル - TM スコープ +HISTORY_MSG_559;ローカル - レティネックス +HISTORY_MSG_560;ローカル - レティネックス 方式 +HISTORY_MSG_561;ローカル - レティネックス 強さ +HISTORY_MSG_562;ローカル - レティネックス 色度 +HISTORY_MSG_563;ローカル - レティネックス 半径 +HISTORY_MSG_564;ローカル - レティネックス コントラスト +HISTORY_MSG_565;ローカル - スコープ +HISTORY_MSG_566;ローカル - レティネックス ゲインのカーブ +HISTORY_MSG_567;ローカル - レティネックス 反対処理 +HISTORY_MSG_568;ローカル - シャープニング +HISTORY_MSG_569;ローカル - Sh 半径 +HISTORY_MSG_570;ローカル - Sh 量 +HISTORY_MSG_571;ローカル - Sh 減衰 +HISTORY_MSG_572;ローカル - Sh 繰り返し +HISTORY_MSG_573;ローカル - Sh スコープ +HISTORY_MSG_574;ローカル - Sh 反対処理 +HISTORY_MSG_575;ローカル - 詳細レベルによるコントラスト調整 +HISTORY_MSG_576;ローカル - CbDL 複数のレベル +HISTORY_MSG_577;ローカル - CbDL 色度 +HISTORY_MSG_578;ローカル - CbDL しきい値 +HISTORY_MSG_579;ローカル - CbDL スコープ +HISTORY_MSG_580;ローカル - ノイズ除去 +HISTORY_MSG_581;ローカル - ノイズ除去 輝度 細かい1 +HISTORY_MSG_582;ローカル - ノイズ除去 輝度 祖い +HISTORY_MSG_583;ローカル - ノイズ除去 細部の回復 +HISTORY_MSG_584;ローカル - ノイズ除去 イコライザ 白黒 +HISTORY_MSG_585;ローカル - ノイズ除去 色度 細かい +HISTORY_MSG_586;ローカル - ノイズ除去 色度 粗い +HISTORY_MSG_587;ローカル - ノイズ除去 色度の回復 +HISTORY_MSG_588;ローカル - ノイズ除去 イコライザ ブルー-レッド +HISTORY_MSG_589;ローカル - ノイズ除去 平滑化フィルタ +HISTORY_MSG_590;ローカル - ノイズ除去 スコープ +HISTORY_MSG_591;ローカル - 色ずれの回避 +HISTORY_MSG_592;ローカル - Sh コントラスト +HISTORY_MSG_593;ローカル - ローカルコントラスト +HISTORY_MSG_594;ローカル - ローカルコントラスト 半径 +HISTORY_MSG_595;ローカル - ローカルコントラスト 量 +HISTORY_MSG_596;ローカル - ローカルコントラスト 暗さ +HISTORY_MSG_597;ローカル - ローカルコントラスト 明るさ +HISTORY_MSG_598;ローカル - ローカルコントラスト スコープ +HISTORY_MSG_599;ローカル - レティネックス 霞除去 +HISTORY_MSG_600;ローカル - ソフトライト 有効 +HISTORY_MSG_601;ローカル - ソフトライト 強さ +HISTORY_MSG_602;ローカル - ソフトライト スコープ +HISTORY_MSG_603;ローカル - Sh ぼかしの半径 +HISTORY_MSG_605;ローカル - 色と明るさの変更 +HISTORY_MSG_606;ローカル - 露光の変更 +HISTORY_MSG_607;ローカル - 色と明るさ マスク C +HISTORY_MSG_608;ローカル - 色と明るさ マスク L +HISTORY_MSG_609;ローカル - 露光補正 マスク C +HISTORY_MSG_610;ローカル - 露光補正 マスク L +HISTORY_MSG_611;ローカル - 色と明るさ マスク H +HISTORY_MSG_612;ローカル - 色と明るさ 構造 +HISTORY_MSG_613;ローカル - 露光補正 構造 +HISTORY_MSG_614;ローカル - 露光補正 マスク H +HISTORY_MSG_615;ローカル - 色と明るさ ブレンド +HISTORY_MSG_616;ローカル - 露光補正 ブレンド +HISTORY_MSG_617;ローカル - 露光補正 ぼかし +HISTORY_MSG_618;ローカル - 色と明るさ マスクを使う +HISTORY_MSG_619;ローカル - 露光補正 マスクを使う +HISTORY_MSG_620;ローカル - 色と明るさ ぼかし +HISTORY_MSG_621;ローカル - 露光補正 反対処理 +HISTORY_MSG_622;ローカル - 構造の除外 +HISTORY_MSG_623;ローカル - 露光補正 色の補間 +HISTORY_MSG_624;ローカル - 色と明るさ 補正グリッド +HISTORY_MSG_625;ローカル - 色と明るさ 補正の強さ +HISTORY_MSG_626;ローカル - 色と明るさ 補正の方式 +HISTORY_MSG_627;ローカル - シャドウ/ハイライト +HISTORY_MSG_628;ローカル - SH ハイライト +HISTORY_MSG_629;ローカル - SH ハイライトトーンの幅 +HISTORY_MSG_630;ローカル - SH シャドウ +HISTORY_MSG_631;ローカル - SH シャドウトーンの幅 +HISTORY_MSG_632;ローカル - SH 半径 +HISTORY_MSG_633;ローカル - SH スコープ +HISTORY_MSG_634;ローカル - 色と明るさ 半径 +HISTORY_MSG_635;ローカル - 露光補正 半径 +HISTORY_MSG_636;ローカル - 追加された機能 +HISTORY_MSG_637;ローカル - SH マスク C +HISTORY_MSG_638;ローカル - SH マスク L +HISTORY_MSG_639;ローカル - SH マスク H +HISTORY_MSG_640;ローカル - SH ブレンド +HISTORY_MSG_641;ローカル - SH マスクを使用 +HISTORY_MSG_642;ローカル - SH 半径 +HISTORY_MSG_643;ローカル - SH ぼかし +HISTORY_MSG_644;ローカル - SH 反対処理 +HISTORY_MSG_645;ローカル - 色差のバランス ab-L +HISTORY_MSG_646;ローカル - 露光補正 色度のマスク +HISTORY_MSG_647;ローカル - 露光補正 ガンマのマスク +HISTORY_MSG_648;ローカル - 露光補正 スロープのマスク +HISTORY_MSG_649;ローカル - 露光補正 ソフトな半径 +HISTORY_MSG_650;ローカル - 色と明るさ 色度のマスク +HISTORY_MSG_651;ローカル - 色と明るさ ガンマのマスク +HISTORY_MSG_652;ローカル - 色と明るさ スロープのマスク +HISTORY_MSG_653;ローカル - SH 色度のマスク +HISTORY_MSG_654;ローカル - SH ガンマのマスク +HISTORY_MSG_655;ローカル - SH スロープのマスク +HISTORY_MSG_656;ローカル - 色と明るさ ソフトな半径 +HISTORY_MSG_657;ローカル - レティネックス アーティファクトの軽減 +HISTORY_MSG_658;ローカル - CbDL ソフトな半径 +HISTORY_MSG_659;ローカル スポット 境界値の減衰 +HISTORY_MSG_660;ローカル - CbDL 明瞭 +HISTORY_MSG_661;ローカル - CbDL 残差のコントラスト +HISTORY_MSG_662;ローカル - deNoise 輝度 細かい0 +HISTORY_MSG_663;ローカル - deNoise 輝度 細かい2 +HISTORY_MSG_664;ローカル - CbDL ぼかし +HISTORY_MSG_665;ローカル - CbDL ブレンドのマスク +HISTORY_MSG_666;ローカル - CbDL 半径のマスク +HISTORY_MSG_667;ローカル - CbDL 色度のマスク +HISTORY_MSG_668;ローカル - CbDL ガンマのマスク +HISTORY_MSG_669;ローカル - CbDL スロープのマスク +HISTORY_MSG_670;ローカル - CbDL マスク C +HISTORY_MSG_671;ローカル - CbDL マスク L +HISTORY_MSG_672;ローカル - CbDL マスク CL +HISTORY_MSG_673;ローカル - CbDL マスクを使う +HISTORY_MSG_674;ローカル - 削除された機能 +HISTORY_MSG_675;ローカル - TM ソフトな半径 +HISTORY_MSG_676;ローカル スポット 境界の差異 +HISTORY_MSG_677;ローカル - TM 量 +HISTORY_MSG_678;ローカル - TM 彩度 +HISTORY_MSG_679;ローカル - レティネックス マスク C +HISTORY_MSG_680;ローカル - レティネックス マスク L +HISTORY_MSG_681;ローカル - レティネックス マスク CL +HISTORY_MSG_682;ローカル - レティネックス マスク +HISTORY_MSG_683;ローカル - レティネックス ブレンドのマスク +HISTORY_MSG_684;ローカル - レティネックス 半径のマスク +HISTORY_MSG_685;ローカル - レティネックス 色度のマスク +HISTORY_MSG_686;ローカル - レティネックス ガンマのマスク +HISTORY_MSG_687;ローカル - レティネックス スロープのマスク +HISTORY_MSG_688;ローカル - 削除された機能 +HISTORY_MSG_689;ローカル - レティネックス 透過マップのマスク +HISTORY_MSG_690;ローカル - レティネックス スケール +HISTORY_MSG_691;ローカル - レティネックス 暗さ +HISTORY_MSG_692;ローカル - レティネックス 明るさ +HISTORY_MSG_693;ローカル - レティネックス しきい値 +HISTORY_MSG_694;ローカル - レティネックス ラプラシアンのしきい値 +HISTORY_MSG_695;ローカル - ソフトの方式 +HISTORY_MSG_696;ローカル - レティネックス 標準化 +HISTORY_MSG_697;ローカル - TM 標準化 +HISTORY_MSG_698;ローカル - ローカルコントラスト 高速フーリエ変換 +HISTORY_MSG_699;ローカル - レティネックス 高速フーリエ変換 +HISTORY_MSG_701;ローカル - Exp シャドウ +HISTORY_MSG_702;ローカル - Exp 方式 +HISTORY_MSG_703;ローカル - Exp ラプラシアンのしきい値 +HISTORY_MSG_704;ローカル - Exp PDEのバランス +HISTORY_MSG_705;ローカル - Exp 線形 +HISTORY_MSG_706;ローカル - TM マスク C +HISTORY_MSG_707;ローカル - TM マスク L +HISTORY_MSG_708;ローカル - TM マスク CL +HISTORY_MSG_709;ローカル - TM マスク マスク +HISTORY_MSG_710;ローカル - TM ブレンドのマスク +HISTORY_MSG_711;ローカル - TM 半径のマスク +HISTORY_MSG_712;ローカル - TM 色度のマスク +HISTORY_MSG_713;ローカル - TM ガンマのマスク +HISTORY_MSG_714;ローカル - TM スロープのマスク +HISTORY_MSG_716;ローカル - ローカル 方式 +HISTORY_MSG_717;ローカル - ローカルコントラスト +HISTORY_MSG_718;ローカル - ローカルコントラストのレベル +HISTORY_MSG_719;ローカル - ローカルコントラスト 残差L +HISTORY_MSG_720;ローカル - ぼかし マスク C +HISTORY_MSG_721;ローカル - ぼかし マスク L +HISTORY_MSG_722;ローカル - ぼかし マスク CL +HISTORY_MSG_723;ローカル - ぼかし マスクを使う +HISTORY_MSG_725;ローカル - ぼかし ブレンドのマスク +HISTORY_MSG_726;ローカル - ぼかし 半径のマスク +HISTORY_MSG_727;ローカル - ぼかし 色度のマスク +HISTORY_MSG_728;ローカル - ぼかし ガンマのマスク +HISTORY_MSG_729;ローカル - ぼかし スロープのマスク +HISTORY_MSG_730;ローカル - ぼかしの方式 +HISTORY_MSG_731;ローカル - メディアンの方式 +HISTORY_MSG_732;ローカル - メディアン 繰り返し +HISTORY_MSG_733;ローカル - ソフトな半径 +HISTORY_MSG_734;ローカル - ディテール +HISTORY_MSG_738;ローカル - ローカルコントラスト Lを統合 +HISTORY_MSG_739;ローカル - ローカルコントラスト ソフトな半径 +HISTORY_MSG_740;ローカル - ローカルコントラスト Cを統合 +HISTORY_MSG_741;ローカル - ローカルコントラスト 残差C +HISTORY_MSG_742;ローカル - Exp ラプラシアンのガンマ +HISTORY_MSG_743;ローカル - Exp Fattal 量 +HISTORY_MSG_744;ローカル - Exp Fattal ディテール +HISTORY_MSG_745;ローカル - Exp Fattal オフセット +HISTORY_MSG_746;ローカル - Exp Fattal シグマ +HISTORY_MSG_747;ローカル 作成されたスポット +HISTORY_MSG_748;ローカル - Exp ノイズ除去 +HISTORY_MSG_749;ローカル - Reti 深度 +HISTORY_MSG_750;ローカル - Reti モード 対数 - 線形 +HISTORY_MSG_751;ローカル - Reti 霞除去 輝度だけ +HISTORY_MSG_752;ローカル - Reti オフセット +HISTORY_MSG_753;ローカル - Reti 透過マップ +HISTORY_MSG_754;ローカル - Reti クリップ +HISTORY_MSG_755;ローカル - TM マスクを使う +HISTORY_MSG_756;ローカル - Exp 露光補正マスクのアルゴリズムを使う +HISTORY_MSG_757;ローカル - Exp ラプラシアンマスク +HISTORY_MSG_758;ローカル - Reti ラプラシアンマスク +HISTORY_MSG_759;ローカル - Exp ラプラシアンマスク +HISTORY_MSG_760;ローカル - Color ラプラシアンマスク +HISTORY_MSG_761;ローカル - SH ラプラシアンマスク +HISTORY_MSG_762;ローカル - cbdl ラプラシアンマスク +HISTORY_MSG_763;ローカル - Blur ラプラシアンマスク +HISTORY_MSG_764;ローカル - Solve PDE ラプラシアンマスク +HISTORY_MSG_765;ローカル - deNoise ディテールのしきい値 +HISTORY_MSG_766;ローカル - Blur 高速フーリエ変換 +HISTORY_MSG_767;ローカル - Grain ISO +HISTORY_MSG_768;ローカル - Grain 強さ +HISTORY_MSG_769;ローカル - Grain スケール +HISTORY_MSG_770;ローカル - Color コントラストカーブのマスク +HISTORY_MSG_771;ローカル - Exp コントラストカーブのマスク +HISTORY_MSG_772;ローカル - SH コントラストカーブのマスク +HISTORY_MSG_773;ローカル - TM コントラストカーブのマスク +HISTORY_MSG_774;ローカル - Reti コントラストカーブのマスク +HISTORY_MSG_775;ローカル - CBDL コントラストカーブのマスク +HISTORY_MSG_776;ローカル - Blur Denoise コントラストカーブのマスク +HISTORY_MSG_777;ローカル - Blur ローカルコントラストカーブのマスク +HISTORY_MSG_778;ローカル - ハイライトのマスク +HISTORY_MSG_779;ローカル - 色と明るさ ローカルコントラストカーブのマスク +HISTORY_MSG_780;ローカル - 色と明るさ シャドウのマスク +HISTORY_MSG_781;ローカル - コントラスト ウェーブレットのレベルのマスク +HISTORY_MSG_782;ローカル - Blur Denoise ウェーブレットのレベルのマスク +HISTORY_MSG_783;ローカル - 色と明るさ ウェーブレットのレベル +HISTORY_MSG_784;ローカル - ΔEのマスク +HISTORY_MSG_785;ローカル - ΔEのスコープのマスク +HISTORY_MSG_786;ローカル - SH 方式 +HISTORY_MSG_787;ローカル - イコライザの乗数 +HISTORY_MSG_788;ローカル - イコライザのディテール +HISTORY_MSG_789;ローカル - SH マスクの量 +HISTORY_MSG_790;ローカル - SH マスクのアンカー +HISTORY_MSG_791;ローカル - マスク ショートLカーブ +HISTORY_MSG_792;ローカル - マスク 背景輝度 +HISTORY_MSG_793;ローカル - SH TRCのガンマ +HISTORY_MSG_794;ローカル - SH TRCのスロープ +HISTORY_MSG_795;ローカル - マスク 復元したイメージの保存 +HISTORY_MSG_796;ローカル - 参考値の繰り返し +HISTORY_MSG_797;ローカル - オリジナルとの融合方式 +HISTORY_MSG_798;ローカル - 不透明度 +HISTORY_MSG_799;ローカル - Color RGB トーンカーブ +HISTORY_MSG_800;ローカル - Color トーンカーブの方式 +HISTORY_MSG_801;ローカル - Color 特殊なトーンカーブ +HISTORY_MSG_802;ローカル - コントラストしきい値 +HISTORY_MSG_803;ローカル - 色と明るさ 融合 +HISTORY_MSG_804;ローカル - 色と明るさ マスクの構造 +HISTORY_MSG_805;ローカル - ぼかしとノイズ マスクの構造 +HISTORY_MSG_806;ローカル - 色と明るさ 機能としてのマスクの構造 +HISTORY_MSG_807;ローカル - ぼかしとノイズ 機能としてのマスクの構造 +HISTORY_MSG_808;ローカル - 色と明るさ マスクカーブ H(H) +HISTORY_MSG_809;ローカル - Vib カーブのマスク C(C) +HISTORY_MSG_810;ローカル - Vib カーブのマスク L(L) +HISTORY_MSG_811;ローカル - Vib カーブのマスク LC(H) +HISTORY_MSG_813;ローカル - 自然な彩度 マスクを使う +HISTORY_MSG_814;ローカル - Vib ブレンドのマスク +HISTORY_MSG_815;ローカル - Vib 半径のマスク +HISTORY_MSG_816;ローカル - Vib 色度のマスク +HISTORY_MSG_817;ローカル - Vib ガンマのマスク +HISTORY_MSG_818;ローカル - Vib 勾配のマスク +HISTORY_MSG_819;ローカル - Vib ラプラシアンのマスク +HISTORY_MSG_820;ローカル - Vib コントラストカーブのマスク +HISTORY_MSG_821;ローカル - 色と明るさ 背景のグリッド +HISTORY_MSG_822;ローカル - 色と明るさ 背景の融合 +HISTORY_MSG_823;ローカル - 色と明るさ 背景の融合 輝度だけ +HISTORY_MSG_824;ローカル - Exp 減光マスクの強さ +HISTORY_MSG_825;ローカル - Exp 減光マスクの角度 +HISTORY_MSG_826;ローカル - Exp 減光の強さ +HISTORY_MSG_827;ローカル - Exp 減光の角度 +HISTORY_MSG_828;ローカル - SH 階調 強さ +HISTORY_MSG_829;ローカル - SH 階調 角度 +HISTORY_MSG_830;ローカル - 色と明るさ 階調 Lの強さ +HISTORY_MSG_831;ローカル - 色と明るさ 階調 角度 +HISTORY_MSG_832;ローカル - 色と明るさ 階調 Cの強さ +HISTORY_MSG_833;ローカル - 減光のフェザー処理 +HISTORY_MSG_834;ローカル - 色と明るさ 減光の強さ H +HISTORY_MSG_835;ローカル - Vib 諧調 Lの強さ +HISTORY_MSG_836;ローカル - Vib 階調 角度 +HISTORY_MSG_837;ローカル - Vib 階調 Cの強さ +HISTORY_MSG_838;ローカル - Vib 階調 Hの強さ +HISTORY_MSG_839;ローカル - ソフトウェアの難易度 +HISTORY_MSG_840;ローカル - CL カーブ +HISTORY_MSG_841;ローカル - LC カーブ +HISTORY_MSG_842;ローカル - マスクぼかしのコントラストしきい値 +HISTORY_MSG_843;ローカル - マスクぼかしの半径 +HISTORY_MSG_844;ローカル - 色と明るさ マスク FTTW +HISTORY_MSG_845;ローカル - 対数符号化 +HISTORY_MSG_846;ローカル - 符号化 自動 +HISTORY_MSG_847;ローカル - グレーポイントの源泉 +HISTORY_MSG_848;ローカル - グレーポイントの源泉 自動 +HISTORY_MSG_849;ローカル - グレーポイントの自動選択 +HISTORY_MSG_850;ローカル - ブラックEv +HISTORY_MSG_851;ローカル - ホワイトEv +HISTORY_MSG_852;ローカル - グレーポイントの目標 +HISTORY_MSG_853;ローカル - ローカルコントラスト +HISTORY_MSG_854;ローカル - 対数符号化のスコープ +HISTORY_MSG_855;ローカル - 画像全体 +HISTORY_MSG_856;ローカル - 対数の基数 +HISTORY_MSG_857;ローカル - Contrast 残差のぼかし +HISTORY_MSG_858;ローカル - Contrast 輝度だけ +HISTORY_MSG_859;ローカル - Contrast 最大値 ぼかしレベル +HISTORY_MSG_860;ローカル - Contrast カーブ ぼかすレベル +HISTORY_MSG_861;ローカル - Contrast カーブ コントラストレベル +HISTORY_MSG_862;ローカル - Contrast シグマ 輝度 +HISTORY_MSG_863;ローカル - Contrast 元画像との融合 +HISTORY_MSG_864;ローカル - Contrast ディテール +HISTORY_MSG_865;ローカル - Contrast アンカー +HISTORY_MSG_866;ローカル - Contrast カーブの圧縮 +HISTORY_MSG_867;ローカル - Contrast 残差の量 +HISTORY_MSG_868;ローカル - ΔEのバランス C-H +HISTORY_MSG_869;ローカル - ノイズ除去カーブ 輝度 +HISTORY_MSG_870;ローカル - LC カーブのマスク LC(H) +HISTORY_MSG_871;ローカル - LC カーブのマスク C(C) +HISTORY_MSG_872;ローカル - LC カーブのマスク L(L) +HISTORY_MSG_873;ローカル - LC マスク 有効 +HISTORY_MSG_875;ローカル - LC マスク ブレンド +HISTORY_MSG_876;ローカル - LC マスク 半径 +HISTORY_MSG_877;ローカル - LC マスク 色度 +HISTORY_MSG_878;ローカル - LC カーブのマスク コントラスト +HISTORY_MSG_879;ローカル - LC 色度 レベル +HISTORY_MSG_880;ローカル - LC 色度のぼかし レベル +HISTORY_MSG_881;ローカル - Contrast オフセット 輝度 +HISTORY_MSG_882;ローカル - Contrast ぼかし +HISTORY_MSG_883;ローカル - Contrast レベルごと +HISTORY_MSG_884;ローカル - Contrast ダイナミックレンジ ラプラシアン +HISTORY_MSG_885;ローカル - Contrast ダイナミックレンジ ウェーブレット +HISTORY_MSG_886;ローカル - Contrast ウェーブレット カーブの圧縮 +HISTORY_MSG_887;ローカル - Contrast ウェーブレット 残差の圧縮 +HISTORY_MSG_888;ローカル - Contrast ウェーブレット バランスのしきい値 +HISTORY_MSG_889;ローカル - Contrast ウェーブレット 階調の強さ +HISTORY_MSG_890;ローカル - Contrast ウェーブレット 階調の角度 +HISTORY_MSG_891;ローカル - Contrast ウェーブレット 階調フィルタ +HISTORY_MSG_892;ローカル - 対数符号化 階調の強さ +HISTORY_MSG_893;ローカル - 対数符号化 階調の角度 +HISTORY_MSG_894;ローカル - 色と明るさ 色差のプレビュー +HISTORY_MSG_897;ローカル - Contrast ウェーブレット ES 強さ +HISTORY_MSG_898;ローカル - Contrast ウェーブレット ES 半径 +HISTORY_MSG_899;ローカル - Contrast ウェーブレット ES ディテール +HISTORY_MSG_900;ローカル - Contrast ウェーブレット ES 勾配 +HISTORY_MSG_901;ローカル - Contrast ウェーブレット ES しきい値 低 +HISTORY_MSG_902;ローカル - Contrast ウェーブレット ES しきい値 高 +HISTORY_MSG_903;ローカル - Contrast ウェーブレット ES ローカルコントラスト +HISTORY_MSG_904;ローカル - Contrast ウェーブレット ES 最初のレベル +HISTORY_MSG_905;ローカル - Contrast ウェーブレット エッジシャープネス +HISTORY_MSG_906;ローカル - Contrast ウェーブレット ES 感度 +HISTORY_MSG_907;ローカル - Contrast ウェーブレット ES 増幅 +HISTORY_MSG_908;ローカル - Contrast ウェーブレット ES 隣接 +HISTORY_MSG_909;ローカル - Contrast ウェーブレット ES 表示 +HISTORY_MSG_910;ローカル - ウェーブレット エッジ検出の効果 +HISTORY_MSG_911;ローカル - ぼかし 色度 輝度 +HISTORY_MSG_912;ローカル - ガイド付きフィルターの強さのぼかし +HISTORY_MSG_913;ローカル - Contrast Wavelet Sigma DR +HISTORY_MSG_914;ローカル - ウェーブレットのぼかし シグマ BL +HISTORY_MSG_915;ローカル - ウェーブレットのエッジ シグマ ED +HISTORY_MSG_916;ローカル - ウェーブレットの残差画像 シャドウ +HISTORY_MSG_917;ローカル - ウェーブレットの残差画像 シャドウのしきい値 +HISTORY_MSG_918;ローカル - ウェーブレットの残差画像 ハイライト +HISTORY_MSG_919;ローカル - ウェーブレットの残差画像 ハイライトのしきい値 +HISTORY_MSG_920;ローカル - ウェーブレット シグマ LC +HISTORY_MSG_921;ローカル - ウェーブレット 階調のシグマ LC2 +HISTORY_MSG_922;ローカル - 白黒での変更 +HISTORY_MSG_923;ローカル - 機能の複雑度モード +HISTORY_MSG_924;ローカル - 機能の複雑度モード +HISTORY_MSG_925;Local - カラー機能のスコープ +HISTORY_MSG_926;Local - マスクのタイプを表示 +HISTORY_MSG_927;Local - シャドウマスク +HISTORY_MSG_BLSHAPE;詳細レベルによるぼかし +HISTORY_MSG_BLURCWAV;色度のぼかし +HISTORY_MSG_BLURWAV;輝度のぼかし +HISTORY_MSG_BLUWAV;減衰応答 +HISTORY_MSG_CAT02PRESET;Cat02 自動プリセット HISTORY_MSG_CLAMPOOG;色域外の色を切り取る HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - カラー補正 HISTORY_MSG_COLORTONING_LABREGION_AB;CT - 色の補正 @@ -756,7 +1200,9 @@ HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;霞除去 - 深度マップの表示 HISTORY_MSG_DEHAZE_STRENGTH;霞除去 - 強さ HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;デュアルデモザイク - 自動しきい値 HISTORY_MSG_DUALDEMOSAIC_CONTRAST;AMaZE+VNG4 - コントラストのしきい値 +HISTORY_MSG_EDGEFFECT;エッジの効果調整 HISTORY_MSG_FILMNEGATIVE_ENABLED;ネガフィルム +HISTORY_MSG_FILMNEGATIVE_FILMBASE;フィルムのベースカラー HISTORY_MSG_FILMNEGATIVE_VALUES;ネガフィルムの値 HISTORY_MSG_HISTMATCHING;トーンカーブの自動調節 HISTORY_MSG_ICM_OUTPUT_PRIMARIES;出力 - プライマリ @@ -765,6 +1211,7 @@ HISTORY_MSG_ICM_OUTPUT_TYPE;出力 - タイプ HISTORY_MSG_ICM_WORKING_GAMMA;作業色空間 - ガンマ HISTORY_MSG_ICM_WORKING_SLOPE;作業色空間 - 勾配 HISTORY_MSG_ICM_WORKING_TRC_METHOD;作業色空間 - TRCの方式 +HISTORY_MSG_ILLUM;輝度 HISTORY_MSG_LOCALCONTRAST_AMOUNT;ローカルコントラスト - 量 HISTORY_MSG_LOCALCONTRAST_DARKNESS;ローカルコントラスト - 暗い部分 HISTORY_MSG_LOCALCONTRAST_ENABLED;ローカルコントラスト @@ -779,10 +1226,20 @@ HISTORY_MSG_PDSHARPEN_CONTRAST;CS - コントラストのしきい値 HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - 繰り返し HISTORY_MSG_PDSHARPEN_RADIUS;CS - シグマ HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - 周辺のシグマを増やす +HISTORY_MSG_PERSP_CAM_ANGLE;パースペクティブ - カメラ +HISTORY_MSG_PERSP_CAM_FL;パースペクティブ - カメラ +HISTORY_MSG_PERSP_CAM_SHIFT;パースペクティブ - カメラ +HISTORY_MSG_PERSP_METHOD;パースペクティブ - 方法 +HISTORY_MSG_PERSP_PROJ_ANGLE;パースペクティブ - 回復 +HISTORY_MSG_PERSP_PROJ_ROTATE;パースペクティブ - PCA 回転 +HISTORY_MSG_PERSP_PROJ_SHIFT;パースペクティブ - PCA HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - 振れに対するデモザイクの方式 HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;ラインノイズフィルタの方向 HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAFラインフィルタ +HISTORY_MSG_PREPROCWB_MODE;ホワイトバランスモードの前処理 +HISTORY_MSG_PROTAB;保護 HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - コントラストのしきい値 +HISTORY_MSG_RANGEAB;abの範囲 HISTORY_MSG_RAWCACORR_AUTOIT;Rawの色収差補正 - 繰り返し HISTORY_MSG_RAWCACORR_COLORSHIFT;Rawの色収差補正 - 色ずれを回避 HISTORY_MSG_RAW_BORDER;Rawの境界 @@ -790,9 +1247,35 @@ HISTORY_MSG_RESIZE_ALLOWUPSCALING;リサイズ - アップスケーリングを HISTORY_MSG_SHARPENING_BLUR;シャープニング - ぼかしの半径 HISTORY_MSG_SHARPENING_CONTRAST;シャープニング - コントラストのしきい値 HISTORY_MSG_SH_COLORSPACE;S/H - 色空間 +HISTORY_MSG_SIGMACOL;色度の効果調整 +HISTORY_MSG_SIGMADIR;Dirの効果調整 +HISTORY_MSG_SIGMAFIN;最終的なコントラストの効果調整 +HISTORY_MSG_SIGMATON;トーンの効果調整 HISTORY_MSG_SOFTLIGHT_ENABLED;ソフトライト HISTORY_MSG_SOFTLIGHT_STRENGTH;ソフトライト - 強さ +HISTORY_MSG_TEMPOUT;CAM02 自動色温度設定 +HISTORY_MSG_THRESWAV;バランスのしきい値 HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - アンカー +HISTORY_MSG_TRANS_Method;ジオメトリ - 方式 +HISTORY_MSG_WAVBALCHROM;イコライザ 色度 +HISTORY_MSG_WAVBALLUM;イコライザ 輝度 +HISTORY_MSG_WAVBL;レベルのぼかし +HISTORY_MSG_WAVCHROMCO;粗い部分の色度 +HISTORY_MSG_WAVCHROMFI;細部の色度 +HISTORY_MSG_WAVCLARI;明瞭 +HISTORY_MSG_WAVEDGS;エッジ停止 +HISTORY_MSG_WAVLOWTHR;最小コントラストのしきい値 +HISTORY_MSG_WAVMERGEC;色度の融合 +HISTORY_MSG_WAVMERGEL;輝度の融合 +HISTORY_MSG_WAVOFFSET;オフセット +HISTORY_MSG_WAVOLDSH;古いアルゴリズムを使う +HISTORY_MSG_WAVRADIUS;シャドウ/ハイライトの半径 +HISTORY_MSG_WAVSCALE;スケール +HISTORY_MSG_WAVSHOWMASK;ウェーブレットのマスクを表示 +HISTORY_MSG_WAVSIGMA;シグマ +HISTORY_MSG_WAVSOFTRAD;明瞭のソフトな半径 +HISTORY_MSG_WAVSOFTRADEND;最終画像のソフトな半径 +HISTORY_MSG_WAVUSHAMET;明瞭の方式 HISTORY_NEWSNAPSHOT;追加 HISTORY_NEWSNAPSHOT_TOOLTIP;ショートカット: Alt-s HISTORY_SNAPSHOT;スナップショット @@ -880,7 +1363,6 @@ IPTCPANEL_TITLE;タイトル IPTCPANEL_TITLEHINT;画像を短く表す言葉や撮影者名、或いは画像のタイトルでもよい IPTCPANEL_TRANSREFERENCE;作業のID IPTCPANEL_TRANSREFERENCEHINT;作業工程の管理やトラッキングのための画像の数字或いは識別 -LENSPROFILE_LENS_WARNING;注意:レンズプロファイルに関する切り抜きの因数がカメラの因数より大きいと、誤った結果になるかもしれません MAIN_BUTTON_FULLSCREEN;フルスクリーン MAIN_BUTTON_ICCPROFCREATOR;ICCプロファイルクリエーター MAIN_BUTTON_NAVNEXT_TOOLTIP;エディタで開いている画像に対応する次の画像に移動します\nショートカット: Shift-F4\n\nファイルブラウザで選択したサムネイルに対応する次の画像に移動するには\nショートカット: F4 @@ -921,7 +1403,7 @@ MAIN_TAB_ADVANCED;高度な機能 MAIN_TAB_ADVANCED_TOOLTIP;ショートカット: Alt-a MAIN_TAB_COLOR;カラー MAIN_TAB_COLOR_TOOLTIP;ショートカット: Alt-c -MAIN_TAB_DETAIL;CbDL +MAIN_TAB_DETAIL;ディテール MAIN_TAB_DETAIL_TOOLTIP;ショートカット: Alt-d MAIN_TAB_DEVELOP;一括編集 MAIN_TAB_EXIF;Exif @@ -933,6 +1415,8 @@ MAIN_TAB_FAVORITES_TOOLTIP;ショートカット: Alt-u MAIN_TAB_FILTER;絞り込み MAIN_TAB_INSPECT;カメラ出しJPEG MAIN_TAB_IPTC;IPTC +MAIN_TAB_LOCALLAB;ローカル調整 +MAIN_TAB_LOCALLAB_TOOLTIP;ショートカット Alt-o MAIN_TAB_METADATA;メタデータ MAIN_TAB_METADATA_TOOLTIP;ショートカット: Alt-m MAIN_TAB_RAW;raw @@ -1012,20 +1496,24 @@ PARTIALPASTE_GRADIENT;減光フィルター PARTIALPASTE_HSVEQUALIZER;HSV イコライザ PARTIALPASTE_ICMSETTINGS;ICM 設定 PARTIALPASTE_IMPULSEDENOISE;インパルス・ノイズ低減 -PARTIALPASTE_IPTCINFO;IPTC 情報 +PARTIALPASTE_IPTCINFO;IPTC PARTIALPASTE_LABCURVE;L*a*b* 調整 -PARTIALPASTE_LENSGROUP;レンズ設定 +PARTIALPASTE_LENSGROUP;レンズ関係の設定 PARTIALPASTE_LENSPROFILE;レンズ補正プロファイル PARTIALPASTE_LOCALCONTRAST;ローカルコントラスト +PARTIALPASTE_LOCALLAB;ローカル調整 +PARTIALPASTE_LOCALLABGROUP;ローカル調整の設定 +PARTIALPASTE_LOCGROUP;ローカル PARTIALPASTE_METADATA;メタデータモード PARTIALPASTE_METAGROUP;メタデータ PARTIALPASTE_PCVIGNETTE;ビネットフィルター PARTIALPASTE_PERSPECTIVE;パースペクティブの補正 PARTIALPASTE_PREPROCESS_DEADPIXFILT;デッドピクセルフィルターを適用 PARTIALPASTE_PREPROCESS_GREENEQUIL;グリーン 平衡化 -PARTIALPASTE_PREPROCESS_HOTPIXFILT;ホットピクセルフィルターを適用 +PARTIALPASTE_PREPROCESS_HOTPIXFILT;ホットピクセルフィルター PARTIALPASTE_PREPROCESS_LINEDENOISE;ラインノイズ フィルタ PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;PDAF ラインフィルタ +PARTIALPASTE_PREPROCWB;ホワイトバランスの前処理 PARTIALPASTE_PRSHARPENING;リサイズ後のシャープニング PARTIALPASTE_RAWCACORR_AUTO;自動色収差補正 PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA 色ずれを回避 @@ -1049,7 +1537,7 @@ PARTIALPASTE_SHADOWSHIGHLIGHTS;シャドウ/ハイライト PARTIALPASTE_SHARPENEDGE;エッジ PARTIALPASTE_SHARPENING;シャープニング (USM/RL) PARTIALPASTE_SHARPENMICRO;マイクロコントラスト -PARTIALPASTE_SOFTLIGHT;ソフトな明るさ +PARTIALPASTE_SOFTLIGHT;ソフトライト PARTIALPASTE_TM_FATTAL;ダイナミックレンジ圧縮 PARTIALPASTE_VIBRANCE;自然な彩度 PARTIALPASTE_VIGNETTING;周辺光量補正 @@ -1090,6 +1578,9 @@ PREFERENCES_CLUTSCACHE;HaldCLUT cache PREFERENCES_CLUTSCACHE_LABEL;cacheに入れるHaldCLUTの最大数 PREFERENCES_CLUTSDIR;HaldCLUTのディレクトリー PREFERENCES_CMMBPC;ブラックポイントの補正 +PREFERENCES_COMPLEXITYLOC;ローカル調整のデフォルトの複雑度 +PREFERENCES_COMPLEXITY_EXP;エキスパート +PREFERENCES_COMPLEXITY_NORM;通常 PREFERENCES_CROP;切り抜き画像の編集 PREFERENCES_CROP_AUTO_FIT;切り抜き画像を自動的に拡大します PREFERENCES_CROP_GUIDES;切り抜き画像が編集されていない時はガイドを表示します @@ -1155,7 +1646,7 @@ PREFERENCES_MENUGROUPLABEL;"カラーラベル"のグループ PREFERENCES_MENUGROUPPROFILEOPERATIONS;"処理プロファイル操作"のグループ PREFERENCES_MENUGROUPRANK;"ランキング"のグループ PREFERENCES_MENUOPTIONS;メニューオプションの状況 -PREFERENCES_MONINTENT;デフォルトのモニターインテント +PREFERENCES_MONINTENT;デフォルトのレンダリングの目標 PREFERENCES_MONITOR;モニター PREFERENCES_MONPROFILE;デフォルトのモニタープロファイル PREFERENCES_MONPROFILE_WARNOSX;MacのOSの制約により、サポート出来るのはsRGBだけです @@ -1206,6 +1697,7 @@ PREFERENCES_SHOWBASICEXIF;基本Exif情報を表示 PREFERENCES_SHOWDATETIME;日付表示 PREFERENCES_SHOWEXPOSURECOMPENSATION;露光補正追加 PREFERENCES_SHOWFILMSTRIPTOOLBAR;画像スライドにツールバーを表示する +PREFERENCES_SHOWTOOLTIP;ローカル調整の機能のヒントを表示 PREFERENCES_SHTHRESHOLD;シャドウ・クリッピング領域のしきい値 PREFERENCES_SINGLETAB;シングルタブモードモード PREFERENCES_SINGLETABVERTAB;シングル編集タブモード, 垂直タブ @@ -1413,7 +1905,7 @@ TP_COLORAPP_CHROMA_M_TOOLTIP;CIECAM02の鮮やかさは L*a*b*やRGBの鮮やか TP_COLORAPP_CHROMA_S;彩度 (S) TP_COLORAPP_CHROMA_S_TOOLTIP;CIECAM02の彩度は L*a*b*やRGBの彩度とは異なります TP_COLORAPP_CHROMA_TOOLTIP;CIECAM02の色度は L*a*b*やRGBの色度とは異なります -TP_COLORAPP_CIECAT_DEGREE;CAT02に適応 +TP_COLORAPP_CIECAT_DEGREE;CAT02 TP_COLORAPP_CONTRAST;コントラスト (J) TP_COLORAPP_CONTRAST_Q;コントラスト (Q) TP_COLORAPP_CONTRAST_Q_TOOLTIP;CIECAM02のコントラスト(明るさQ)スライダーは L*a*b*やRGBとは異なります @@ -1424,17 +1916,27 @@ TP_COLORAPP_CURVEEDITOR2;トーンカーブ2 TP_COLORAPP_CURVEEDITOR2_TOOLTIP;2番目の露光トーンカーブも同じ使い方です TP_COLORAPP_CURVEEDITOR3;カラーカーブ TP_COLORAPP_CURVEEDITOR3_TOOLTIP;色度、彩度、鮮やかさのいずれかを調整します\n\nCIECAM02調整前の色度(L*a*b*)のヒストグラムを表示します\nチェックボックスの"カーブにCIECAM02出力のヒストグラムを表示" が有効の場合、CIECAM02調整後のC,sまたはMのヒストグラムを表示します\n\nC, sとMは、メインのヒストグラム・パネルには表示されません\n最終出力は、メインのヒストグラム・パネルを参照してください -TP_COLORAPP_DATACIE;カーブにCIECAM02出力のヒストグラムを表示 +TP_COLORAPP_DATACIE;カーブでCIECAM02出力のヒストグラムを表示 TP_COLORAPP_DATACIE_TOOLTIP;有効の場合、CIECAM02カーブのヒストグラムは、JかQ、CIECAM02調整後のCかs、またはMの値/範囲の近似値を表示します\nこの選択はメイン・ヒストグラムパネルには影響を与えません\n\n無効の場合、CIECAM02カーブのヒストグラムは、CIECAM調整前のL*a*b*値を表示します -TP_COLORAPP_FREE;任意の色温度+グリーン + CAT02 + [出力] +TP_COLORAPP_FREE;任意の色温度と色偏差 + CAT02 + [出力] TP_COLORAPP_GAMUT;色域制御 (L*a*b*) -TP_COLORAPP_GAMUT_TOOLTIP;L*a*b*モードの色域制御を許可 +TP_COLORAPP_GAMUT_TOOLTIP;L*a*b*モードの色域制御を可能にします TP_COLORAPP_HUE;色相 (h) TP_COLORAPP_HUE_TOOLTIP;色相 (h) - 0° から 360°の角度 +TP_COLORAPP_IL41;D41 +TP_COLORAPP_IL50;D50 +TP_COLORAPP_IL55;D55 +TP_COLORAPP_IL60;D60 +TP_COLORAPP_IL65;D65 +TP_COLORAPP_IL75;D75 +TP_COLORAPP_ILA;白熱灯標準A 2856K +TP_COLORAPP_ILFREE;フリー +TP_COLORAPP_ILLUM;光源 +TP_COLORAPP_ILLUM_TOOLTIP;撮影条件に最も近い条件を選択します\n一般的にはD50 ですが、時間と緯度に応じて変えます TP_COLORAPP_LABEL;CIE色の見えモデル2002 TP_COLORAPP_LABEL_CAM02;画像の調整 -TP_COLORAPP_LABEL_SCENE;撮影環境条件 -TP_COLORAPP_LABEL_VIEWING;観視条件 +TP_COLORAPP_LABEL_SCENE;撮影環境 +TP_COLORAPP_LABEL_VIEWING;観視環境 TP_COLORAPP_LIGHT;明度 (J) TP_COLORAPP_LIGHT_TOOLTIP;CIECAM02の明度は L*a*b*やRGBの明度とは異なります TP_COLORAPP_MEANLUMINANCE;中間輝度 (Yb%) @@ -1442,6 +1944,8 @@ TP_COLORAPP_MODEL;ホワイトポイント・モデル TP_COLORAPP_MODEL_TOOLTIP;WB [RT] + [出力]:\nRTのホワイトバランスは、撮影環境に使用されます。CIECAM02はD50の設定, 出力デバイスのホワイトバランスは「環境設定」の「カラーマネジメント」の設定\n\nWB [RT+CAT02] + [出力]:\nRTのホワイトバランス設定は、CAT02で使用され、出力デバイスのホワイトバランスは環境設定の値を使用します TP_COLORAPP_NEUTRAL;リセット TP_COLORAPP_NEUTRAL_TIP;全てのスライダーチェックボックスとカーブをデフォルトにリセットします +TP_COLORAPP_PRESETCAT02;cat02の自動プリセット +TP_COLORAPP_PRESETCAT02_TIP;これを有効にすると、スライダー、色温度、色偏差がCAT02自動に合わせて設定されます\n撮影環境の輝度は変えることが出来ます\n必要であれば、CAT02の観視環境を変更します\n必要に応じて観視環境の色温度、色偏差、を変更します TP_COLORAPP_RSTPRO;レッドと肌色トーンを保護 TP_COLORAPP_RSTPRO_TOOLTIP;レッドと肌色トーンを保護はスライダーとカーブの両方に影響します TP_COLORAPP_SURROUND;周囲環境 @@ -1458,6 +1962,8 @@ TP_COLORAPP_TCMODE_LABEL2;カーブ・モード2 TP_COLORAPP_TCMODE_LABEL3;カーブ・色度モード TP_COLORAPP_TCMODE_LIGHTNESS;明度 TP_COLORAPP_TCMODE_SATUR;彩度 +TP_COLORAPP_TEMP2_TOOLTIP;シンメトリカルモードの場合は色温度 = White balance.\n色偏差は常に1.0\n\nA光源 色温度=2856\nD41 色温度=4100\nD50 色温度=5003\nD55 色温度=5503\nD60 色温度=6000\nD65 色温度=6504\nD75 色温度=7504 +TP_COLORAPP_TEMPOUT_TOOLTIP;色温度と色偏差を変えるために無効にします TP_COLORAPP_TEMP_TOOLTIP;選択した光源に関し色偏差は常に1が使われます\n\n色温度=2856\nD50 色温度=5003\nD55 色温度=5503\nD65 色温度=6504\nD75 色温度=7504 TP_COLORAPP_TONECIE;CIECAM02 明るさ(Q)を使用してトーンマッピング TP_COLORAPP_TONECIE_TOOLTIP;このオプションが無効になっている場合、トーンマッピングはL*a*b*空間を使用します\nこのオプションが有効になっている場合、トーンマッピングは、CIECAM02を使用します\nトーンマッピング(L*a*b*/CIECAM02)ツールを有効にするには、この設定を有効にする必要があります @@ -1538,8 +2044,8 @@ TP_CROP_PPI;PPI TP_CROP_RESETCROP;リセット TP_CROP_SELECTCROP;セレクト TP_CROP_W;W 幅 -TP_CROP_X;X -TP_CROP_Y;Y +TP_CROP_X;左 +TP_CROP_Y;上部 TP_DARKFRAME_AUTOSELECT;自動選択 TP_DARKFRAME_LABEL;ダークフレーム TP_DEFRINGE_LABEL;フリンジ低減 @@ -1552,7 +2058,7 @@ TP_DEHAZE_SHOW_DEPTH_MAP;深度マップの表示 TP_DEHAZE_STRENGTH;強さ TP_DIRPYRDENOISE_CHROMINANCE_AMZ;自動(多分割方式) TP_DIRPYRDENOISE_CHROMINANCE_AUTOGLOBAL;自動(分割方式) -TP_DIRPYRDENOISE_CHROMINANCE_BLUEYELLOW;色差 ブルー/イエロー +TP_DIRPYRDENOISE_CHROMINANCE_BLUEYELLOW;ブルー/イエロー TP_DIRPYRDENOISE_CHROMINANCE_CURVE;色ノイズ低減のカーブ TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;クロミナンススライダー全ての値を増幅します\n色度をベースにこのカーブで色ノイズ低減の強さを加減します。例えば彩度の低い部分で作用を強める、或いは色度の高い部分で作用を弱めるように使います TP_DIRPYRDENOISE_CHROMINANCE_FRAME;色ノイズ @@ -1568,7 +2074,7 @@ TP_DIRPYRDENOISE_CHROMINANCE_PREVIEW_INFO;プレビュー画像のサイズ=%1, TP_DIRPYRDENOISE_CHROMINANCE_PREVIEW_NOISEINFO;プレビュー画像のノイズ: 平均値=%1 最大値=%2 TP_DIRPYRDENOISE_CHROMINANCE_PREVIEW_NOISEINFO_EMPTY;プレビュー画像のノイズ: 平均値= - 最大値= - TP_DIRPYRDENOISE_CHROMINANCE_PREVIEW_TILEINFO;タイルのサイズ=%1, 中心位置: X座標=%2 Y座標=%3 -TP_DIRPYRDENOISE_CHROMINANCE_REDGREEN;色差 レッド/グリーン +TP_DIRPYRDENOISE_CHROMINANCE_REDGREEN;レッド/グリーン TP_DIRPYRDENOISE_LABEL;ノイズ低減 TP_DIRPYRDENOISE_LUMINANCE_CONTROL;輝度ノイズの調整法 TP_DIRPYRDENOISE_LUMINANCE_CURVE;輝度カーブ @@ -1585,7 +2091,7 @@ TP_DIRPYRDENOISE_MAIN_MODE;モード TP_DIRPYRDENOISE_MAIN_MODE_AGGRESSIVE;積極的 TP_DIRPYRDENOISE_MAIN_MODE_CONSERVATIVE;控えめ TP_DIRPYRDENOISE_MAIN_MODE_TOOLTIP;”控えめ”モードは低周波の色度パターンが維持されますが、”積極的”モードではそれらも取り除かれます -TP_DIRPYRDENOISE_MEDIAN_METHOD;方式 +TP_DIRPYRDENOISE_MEDIAN_METHOD;メディアンの方式 TP_DIRPYRDENOISE_MEDIAN_METHOD_CHROMINANCE;色ノイズだけ TP_DIRPYRDENOISE_MEDIAN_METHOD_LAB;L*a*b* TP_DIRPYRDENOISE_MEDIAN_METHOD_LABEL;メディアンフィルター @@ -1609,7 +2115,7 @@ TP_DIRPYREQUALIZER_ARTIF;アーティファクトを軽減 TP_DIRPYREQUALIZER_HUESKIN;肌色の色相 TP_DIRPYREQUALIZER_HUESKIN_TOOLTIP;このカーブは上部ほど、アルゴリズムが効率良く働くことを示しています。\n下部ほど、色相の遷移が見られる部分です。\nコントロールポイントを左右に大きく動かす必要が生じたり、アーティファクトが生じたりする場合は、ホワイトバランスが妥当ではない時です。\n他の色への影響を避けるには、調整範囲を少し減らします TP_DIRPYREQUALIZER_LABEL;詳細レベルによるコントラスト調整 -TP_DIRPYREQUALIZER_LUMACOARSEST;粗い +TP_DIRPYREQUALIZER_LUMACOARSEST;大まか TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS;コントラスト- TP_DIRPYREQUALIZER_LUMACONTRAST_PLUS;コントラスト+ TP_DIRPYREQUALIZER_LUMAFINEST;細かい @@ -1628,12 +2134,12 @@ TP_EPD_REWEIGHTINGITERATES;再加重反復 TP_EPD_SCALE;スケール TP_EPD_STRENGTH;強さ TP_EXPOSURE_AUTOLEVELS;自動露光補正 -TP_EXPOSURE_AUTOLEVELS_TIP;画像を解析し露光補正を自動で設定します +TP_EXPOSURE_AUTOLEVELS_TIP;画像を解析し、露光補正を自動で行います\n必要に応じてハイライト復元を有効にします TP_EXPOSURE_BLACKLEVEL;黒レベル -TP_EXPOSURE_BRIGHTNESS;明度 +TP_EXPOSURE_BRIGHTNESS;明るさ TP_EXPOSURE_CLAMPOOG;色域から外れた色を切り取る TP_EXPOSURE_CLIP;クリップ % -TP_EXPOSURE_CLIP_TIP;自動露光補正によるハイライトとシャドウ部分での飽和ピクセルの割合制限 +TP_EXPOSURE_CLIP_TIP;自動露光補正で使う飽和ピクセルの割合 TP_EXPOSURE_COMPRHIGHLIGHTS;ハイライト圧縮 TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD;ハイライト圧縮 しきい値 TP_EXPOSURE_COMPRSHADOWS;シャドウ圧縮 @@ -1657,6 +2163,9 @@ TP_EXPOSURE_TCMODE_WEIGHTEDSTD;加重平均 TP_EXPOS_BLACKPOINT_LABEL;raw ブラック・ポイント TP_EXPOS_WHITEPOINT_LABEL;raw ホワイト・ポイント TP_FILMNEGATIVE_BLUE;ブルーの比率 +TP_FILMNEGATIVE_FILMBASE_PICK;フィルのベースカラーをピック +TP_FILMNEGATIVE_FILMBASE_TOOLTIP;実際のフィルムのベースカラーを取得して、処理プロファイルに保存するために未露光のスポットをピック(例 フレームの境)\nこの方が、同じフィルムロールからの複数の画像をバッチ処理する際に、一貫性のあるカラーバランスを取り易いです\n変換する画像が極端に暗い、明るい、色のバランスが悪い場合に使えます。 +TP_FILMNEGATIVE_FILMBASE_VALUES;フィルムをベースにしたRGB: TP_FILMNEGATIVE_GREEN;参考指数(コントラスト) TP_FILMNEGATIVE_GUESS_TOOLTIP;原画像の中で色相がニュートラルな部分2か所をピックすることでレッドとブルーの比率を自動で設定します。明るさが異なる2か所をピックします。その後、ホワイトバランスを設定します。 TP_FILMNEGATIVE_LABEL;ネガフィルム @@ -1734,7 +2243,7 @@ TP_ICM_SAVEREFERENCE_TOOLTIP;入力プロファイルが適用される前のリ TP_ICM_TONECURVE;DCPトーンカーブ使用 TP_ICM_TONECURVE_TOOLTIP;DCPのプロファイルに含まれているトーンカーブを使用することができます TP_ICM_WORKINGPROFILE;作業プロファイル -TP_ICM_WORKING_TRC;トーン再現カーブ: +TP_ICM_WORKING_TRC;TRC: TP_ICM_WORKING_TRC_CUSTOM;カスタム TP_ICM_WORKING_TRC_GAMMA;ガンマ TP_ICM_WORKING_TRC_NONE;なし @@ -1782,6 +2291,8 @@ TP_LABCURVE_RSTPRO_TOOLTIP;色度スライダーとCCカーブを使用するこ TP_LENSGEOM_AUTOCROP;自動的に切り抜き選択 TP_LENSGEOM_FILL;オートフィル TP_LENSGEOM_LABEL;レンズ / ジオメトリ +TP_LENSGEOM_LIN;線形 +TP_LENSGEOM_LOG;対数 TP_LENSPROFILE_CORRECTION_AUTOMATCH;自動で選択 TP_LENSPROFILE_CORRECTION_LCPFILE;LCPファイル TP_LENSPROFILE_CORRECTION_MANUAL;手動で選択 @@ -1797,6 +2308,561 @@ TP_LOCALCONTRAST_DARKNESS;暗い部分のレベル TP_LOCALCONTRAST_LABEL;ローカルコントラスト TP_LOCALCONTRAST_LIGHTNESS;明るい部分のレベル TP_LOCALCONTRAST_RADIUS;半径 +TP_LOCALLAB_ACTIV;輝度だけ +TP_LOCALLAB_ADJ;イコライザ ブルー/イエロー レッド/グリーン +TP_LOCALLAB_ALL;全ての種類 +TP_LOCALLAB_AMOUNT;量 +TP_LOCALLAB_ARTIF;形状検出 +TP_LOCALLAB_ARTIF_TOOLTIP;色差の軽減を強めることで形状検出を向上させますが、形状の検出の範囲が狭まることがあります\n画像の形状のしきい値が画像のベタな部分のレベルを考慮します +TP_LOCALLAB_AUTOGRAY;自動 +TP_LOCALLAB_AVOID;色ずれの回避 +TP_LOCALLAB_BALAN;ΔEのバランス ab-L +TP_LOCALLAB_BALANEXP;ΔØ PDE バランス +TP_LOCALLAB_BALANH;ΔEのバランス C-H +TP_LOCALLAB_BALAN_TOOLTIP;色差のアルゴリズムのパラメータを変えます。\nab-L、C - Hを増減させます\nノイズ除去は変わりません +TP_LOCALLAB_BASELOG;対数の基数 +TP_LOCALLAB_BILATERAL;平滑化フィルタ +TP_LOCALLAB_BLACK_EV;ブラックEv +TP_LOCALLAB_BLCO;色度だけ +TP_LOCALLAB_BLENDMASKCOL;ブレンド +TP_LOCALLAB_BLENDMASK_TOOLTIP;ブレンド=0の場合は、形状検出だけが改善します\nブレンドが0より大きい場合は、画像にマスクが追加されます。 ブレンドが0より小さい場合は、画像からマスクが除かれます。 +TP_LOCALLAB_BLGUID;ガイド付きフィルタ +TP_LOCALLAB_BLINV;インバース +TP_LOCALLAB_BLLC;輝度と色度 +TP_LOCALLAB_BLLO;輝度だけ +TP_LOCALLAB_BLMED;メディアン +TP_LOCALLAB_BLMETHOD_TOOLTIP;通常-全ての設定に対し、直接的なぼかしとノイズ\n反対‐スコープと拡張アルゴリズムを除いた設定で、ぼかしとノイズの反対処理\nシンメトリック‐全ての設定でぼかしとノイズの反対処理(予期せぬ結果になることが有り) +TP_LOCALLAB_BLNOI_EXP;ぼかし & ノイズ +TP_LOCALLAB_BLNORM;通常 +TP_LOCALLAB_BLSYM;シンメトリック +TP_LOCALLAB_BLUFR;平滑化 - ぼかし - 質感 - ノイズ除去 +TP_LOCALLAB_BLUMETHOD_TOOLTIP;背景をぼかし、前景を分離するために:\n*RT-スポットが画像全体をカバーできるようにして(スコープと境界を高くする)背景をぼかします-通常或いはリバースモード\n*使用したい機能で一つ以上の除外RT-スポットを追加し(スコープを大きくします)、前景を分離します、マスクを使うことで効果を増幅出来ます +TP_LOCALLAB_BLUR;ガウスぼかし - ノイズ - ノイズ除去 +TP_LOCALLAB_BLURCBDL;ぼかしのレベル 0-1-2-3-4 +TP_LOCALLAB_BLURCOL;マスクぼかしの半径 +TP_LOCALLAB_BLURDE;形状検出のぼかし +TP_LOCALLAB_BLURLC;輝度だけ +TP_LOCALLAB_BLURLEVELFRA;レベルのぼかし +TP_LOCALLAB_BLURMASK_TOOLTIP;コントラストのしきい値と構造を考慮したマスクぼかしのスライダーでぼかしマスクを生成します。 +TP_LOCALLAB_BLURRESIDFRA;残差のぼかし +TP_LOCALLAB_BLUR_TOOLNAME;平滑化ぼかし 質感 & ノイズ除去 - 1 +TP_LOCALLAB_BLWH;全ての変更を強制的に白黒にする +TP_LOCALLAB_BLWH_TOOLTIP;色の構成要素、"a"と"b"の値を強制的にゼロにします +TP_LOCALLAB_BUTTON_ADD;追加 +TP_LOCALLAB_BUTTON_DEL;削除 +TP_LOCALLAB_BUTTON_DUPL;複製 +TP_LOCALLAB_BUTTON_REN;名前の変更 +TP_LOCALLAB_BUTTON_VIS;表示/非表示 +TP_LOCALLAB_CBDL;詳細レベルによるコントラスト調整 - 不良の補正 +TP_LOCALLAB_CBDLCLARI_TOOLTIP;中間トーンを強化します +TP_LOCALLAB_CBDL_ADJ_TOOLTIP;ウェーブレット機能のように作用します\n最初のレベル(0)は2x2ピクセルで解析します\n最後のレベル(5)は64x64ピクセルで解析します +TP_LOCALLAB_CBDL_THRES_TOOLTIP;ノイズが先鋭化するのを避けます +TP_LOCALLAB_CBDL_TOOLNAME;詳細レベルによるコントラスト調整 (不良部分の補正) - 2 +TP_LOCALLAB_CENTER_X;センターX +TP_LOCALLAB_CENTER_Y;センターY +TP_LOCALLAB_CH;カーブ CL - LC +TP_LOCALLAB_CHROMA;色度 +TP_LOCALLAB_CHROMABLU;色度のレベル +TP_LOCALLAB_CHROMABLU_TOOLTIP;輝度のスライダーと比べて増幅と減衰の作用の働きをします\n1以下で減衰、1以上で増幅の作用となります +TP_LOCALLAB_CHROMACBDL;色度 +TP_LOCALLAB_CHROMACB_TOOLTIP;輝度のスライダーと比べて増幅と減衰の作用の働きをします\n100以下で減衰、100以上で増幅の作用となります +TP_LOCALLAB_CHROMALEV;色度のレベル +TP_LOCALLAB_CHROMASKCOL;色度のマスク +TP_LOCALLAB_CHROMASK_TOOLTIP;このスライダーを使って背景の彩度を下げることが出来ます(インバースマスクで言う0に近いカーブ).\n色度に対するマスクの作用を強めることも出来ます。 +TP_LOCALLAB_CHRRT;色度 +TP_LOCALLAB_CIRCRADIUS;スポットサイズ +TP_LOCALLAB_CIRCRAD_TOOLTIP;RT-スポットの参考値を含んでいるので、形状検出(色相、輝度、色度、Sobel)に有利です\n小さい値は花びらの補正などに便利です\n大きな値は肌などの補正に便利です +TP_LOCALLAB_CLARICRES;色度を融合 +TP_LOCALLAB_CLARIFRA;明瞭とシャープマスク - ブレンド & ソフトイメージ +TP_LOCALLAB_CLARILRES;輝度の融合 +TP_LOCALLAB_CLARISOFT;ソフトな半径 +TP_LOCALLAB_CLARISOFT_TOOLTIP;輝度の融合が0以外の場合に明瞭とシャープマスクが有効となります。\n\nウェーブレットピラミッドモジュールの全てが有効となります\nソフトな半径が0の場合は無効となります +TP_LOCALLAB_CLARITYML;明瞭 +TP_LOCALLAB_CLARI_TOOLTIP;ウェーブレットのレベルが4以下の場合は、’シャープマスク’が有効となります。\n5以上のレベルでは’明瞭化’が有効となります。 +TP_LOCALLAB_CLIPTM;復元されたデータの切り取り(ゲイン) +TP_LOCALLAB_COFR;色と明るさ - 小さな不良 +TP_LOCALLAB_COLORDE;プレビューのカラー選択 ΔE - 強さ +TP_LOCALLAB_COLORDEPREV_TOOLTIP;有効になっている機能が1つだけの時は、設定のパネル(拡張する)のΔEのプレビューボタンを使います。\n複数の機能が有効になっている時は、各機能に備わっているマスクと調節の中のΔEのプレビューを使います。 +TP_LOCALLAB_COLORDE_TOOLTIP;設定値がマイナスの場合は色差(ΔE)のプレビューの色をブルーで表示、プラスの場合はグリーンで表示\n\nマスクと調節(マスクなしで調節を表示):プラスであれば、実際の変更を表示、マイナスであれば、強化した調節(輝度のみ)をブルーとイエローで表示 +TP_LOCALLAB_COLORSCOPE;カラー機能のスコープ +TP_LOCALLAB_COLORSCOPE_TOOLTIP;色と明るさ、露光補正(標準)、シャドウ/ハイライト、自然な彩度は共通したスコープを使います。\n他の機能に関しては、それぞれ特定のスコープを使います。 +TP_LOCALLAB_COLOR_TOOLNAME;色&&明るさ (不良部分の補正) - 11 +TP_LOCALLAB_COL_NAME;名前 +TP_LOCALLAB_COL_VIS;ステータス +TP_LOCALLAB_COMPFRA;詳細レベルの方向によるコントラスト +TP_LOCALLAB_COMPFRAME_TOOLTIP;特殊な効果を付けるために使います。アーティファクトを軽減するためには'明瞭 & シャープマスク、ブレンド & ソフトイメージ'を使います\n処理時間が大きく増えます +TP_LOCALLAB_COMPLEX_METHOD;ソフトウェアの複雑度 +TP_LOCALLAB_COMPLEX_TOOLTIP; ローカル調整の扱いの複雑度を選択出来ます +TP_LOCALLAB_COMPREFRA;ウェーブレットを使ったレベルのダイナミックレンジ(非)圧縮 +TP_LOCALLAB_COMPRESS_TOOLTIP;アーティファクトを軽減するため必要に応じて'明瞭 & シャープマスク、ブレンド & ソフトイメージ'の'ソフトな半径'使います +TP_LOCALLAB_CONTCOL;マスクぼかしのコントラストしきい値 +TP_LOCALLAB_CONTFRA;レベルによるコントラスト調整 +TP_LOCALLAB_CONTRAST;コントラスト +TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;メインのマスクのコントラストをコントロール +TP_LOCALLAB_CONTRESID;コントラスト +TP_LOCALLAB_CONTTHR;コントラストのしきい値 +TP_LOCALLAB_CSTHRESHOLD;Ψ ウェーブレットのレベル +TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ ウェーブレットレベルのマスク +TP_LOCALLAB_CURV;明るさ - コントラスト - 色度 "強力" +TP_LOCALLAB_CURVCURR;通常 +TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;カーブが最上部に位置している時は、マスクが完全に黒く表示され、マスクの作用がない状態\nカーブを下に下げるにつれ、マスクの色が変わり、合わせて画像の状態も変わる +TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;使うためには'カーブを有効にする'に✔を入れる +TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;トーンカーブ +TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), 色と明るさでL(H)との併用可 +TP_LOCALLAB_CURVEMETHOD_TOOLTIP;'通常', L=f(L)カーブはスライダーと同じアルゴリズムを使っています\n'強力' L=f(L)カーブで作用を強めた新しいアルゴリズムを使っていますが、場合によってアーティファクトが出ることがあります +TP_LOCALLAB_CURVENCONTRAST;強力+コントラストのしきい値(試験的) +TP_LOCALLAB_CURVENH;強力 +TP_LOCALLAB_CURVENHSU;色相と色度の組み合わせ(試験的) +TP_LOCALLAB_CURVENSOB2;色相と色度の組み合わせ+コントラストのしきい値(試験的) +TP_LOCALLAB_CURVNONE;カーブを無効 +TP_LOCALLAB_DARKRETI;暗さ +TP_LOCALLAB_DEHAFRA;霞除去 +TP_LOCALLAB_DEHAZ;強さ +TP_LOCALLAB_DEHAZ_TOOLTIP;マイナス値にすると霞が増えます +TP_LOCALLAB_DELTAD;色差のバランス +TP_LOCALLAB_DELTAEC;ΔE画像のマスク +TP_LOCALLAB_DENOIS;Ψ ノイズ除去 +TP_LOCALLAB_DENOI_EXP;ノイズ除去 +TP_LOCALLAB_DENOI_TOOLTIP;このモジュール(処理工程の後の方に位置)だけで使うことも、メインのノイズ低減(処理工程の最初の方に位置)と併用することも出来ます\nスコープは色(ΔE)に応じてノイズ除去の作用に違いを持たせます。\n”メディアン”と”ガイド付きフィルタ”(平滑化ぼかし)も使えば、ノイズ除去のパフォーマンスが良くなります。\n”レベルのぼかし”や”ウェーブレットピラミッド”の併用で更にノイズ除去のパフォーマンスが上がります。 +TP_LOCALLAB_DEPTH;深度 +TP_LOCALLAB_DETAIL;ローカルコントラスト +TP_LOCALLAB_DETAILSH;ディテール +TP_LOCALLAB_DETAILTHR;細部の色の明るさのしきい値 (DCT) +TP_LOCALLAB_DUPLSPOTNAME;コピー +TP_LOCALLAB_EDGFRA;エッジシャープネス +TP_LOCALLAB_EDGSHOW;全ての機能を表示 +TP_LOCALLAB_ELI;楕円 +TP_LOCALLAB_ENABLE_AFTER_MASK;トーンマッピングを使う +TP_LOCALLAB_ENABLE_MASK;マスクを有効にする +TP_LOCALLAB_ENABLE_MASKAFT;露光補正の全てのアルゴリズムを使う +TP_LOCALLAB_ENARETIMASKTMAP_TOOLTIP;元のデータの代わりに透過マップを使った後は、有効にしたマスクは保持されているデータを使います。 +TP_LOCALLAB_ENH;強化 +TP_LOCALLAB_ENHDEN;強化 + 色ノイズの軽減 +TP_LOCALLAB_EPSBL;ディテール +TP_LOCALLAB_EQUIL;輝度の標準化 +TP_LOCALLAB_EQUILTM_TOOLTIP;出力画像の輝度の平均と分散が元画像のそれらと同じになるように輝度を回復するオプションです +TP_LOCALLAB_ESTOP;エッジ停止 +TP_LOCALLAB_EV_DUPL;のコピー +TP_LOCALLAB_EV_NVIS;非表示 +TP_LOCALLAB_EV_NVIS_ALL;全て非表示 +TP_LOCALLAB_EV_VIS;表示 +TP_LOCALLAB_EV_VIS_ALL;全て表示 +TP_LOCALLAB_EXCLUF;除外 +TP_LOCALLAB_EXCLUF_TOOLTIP;スコープを動かして色を拡張するような場合、データの一部を除外する場合に使います\nRT-スポットに対する全ての設定で適用できます。 +TP_LOCALLAB_EXCLUTYPE;スポットのタイプ +TP_LOCALLAB_EXCLUTYPE_TOOLTIP;通常方式は、スポットのデータを繰り返して使います\n例外方式は元のデータに戻して使う方式です +TP_LOCALLAB_EXECLU;除外スポット +TP_LOCALLAB_EXNORM;通常スポット +TP_LOCALLAB_EXPCBDL_TOOLTIP;センサーの汚れに起因する不良で、それらが画像の大切な部分にある、或いは複数個所にそれらある場合\n\na) RT-スポットをその部分に作成(必要であれば大きさを調節); b) または複数個所をカバーするスポットを作成; c) 比較的大きな境界の調整を設定; d) レベル3と4のスライダー、場合によっては2の値も100以下にします。必要に応じて色度のスライダーも使います。 +TP_LOCALLAB_EXPCHROMA;色度の補間 +TP_LOCALLAB_EXPCHROMA_TOOLTIP;この機能は露光量補正とPDE IPOLだけに関わります\n色が褪せるのを避けます +TP_LOCALLAB_EXPCOLOR_TOOLTIP;不良個所か小さい場合:\n\n赤目 : 中心円を赤い部分に合わせ、RT-スポットの範囲を眼の大きさ程度にします。スコープの値を小さくし、"明るさ" -100, "色" -100にします\n\n赤外線センサーのスポット:中心円を不良個所に合わせます、 RT-スポットの範囲はデフォルトに近い大きさで構いません - "色"を減らし、場合によっては"スコープ"を使って作用の及ぶ範囲を調節します\n\nセンサーの汚れ(小):中心円を不良部分に合わせます(スポットサイズも調整します)、RT-スポットの範囲が不良個所にあまり近づかないようにします(境界部分が目立たないようにする) a) "境界の調節"は小さくします; b) "輝度"、場合によっては"色" を使って不良個所のレンダリングを正常な部分のレンダリングに近づけます; c) "スコープ"を適度に使って作用の及ぶ範囲を調節します +TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;ウェーブレットのレベル或いはノイズ除去の説明を参照して下さい。\n但し、幾つか変更されている部分もあります:より多くの機能がノイズ除去に関する詳細が多くなっています。\nウェーブレットのレベルのトーンマッピング +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;大きさが50x50ピクセルより小さいスポットを使うのは避けます\n代わりに、境界値を低く、境界の減衰値とスコープ値を高く設定して小さなRT-スポットを真似ます\nアーティストを軽減するために、必要であれば’ソフトな半径’を調整しながら’明瞭 & シャープマスクとイメージのブレンド’モジュールを使います +TP_LOCALLAB_EXPCURV;カーブ +TP_LOCALLAB_EXPGRAD;階調フィルタ +TP_LOCALLAB_EXPLAPBAL_TOOLTIP;元画像とラプラス変換の間のバランスをとります +TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;ラプラス変換の適用前後でガンマを適用します +TP_LOCALLAB_EXPLAPLIN_TOOLTIP;ラプラス変換の適用前に、露光補正の線形要素を追加します +TP_LOCALLAB_EXPLAP_TOOLTIP;しきい値のスライダーを増やすほど、コントラストの減衰作用が強くなります +TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;画像を融合するために複数の機能が使えます(Photoshopのレイヤーのように):差分、積算、ソフトライト、オーバーレイ+不透明度。。。\n元画像 : 現在のRT-スポットを元画像と融合.\n前のスポット : 現在のRT-スポットを前のRT-スポットと融合 – スポットが一つだけの場合は元画像との融合になります\n背景:現在のRT-スポットを背景の色と輝度と融合します +TP_LOCALLAB_EXPMETHOD_TOOLTIP;標準:メインの露光補正と類似したアルゴリズムを使いますが、 L*a*b*で作業するため色差を考慮します\n\nラプラスとポアソン方程式:色差を考慮する別なアルゴリズムですが、フーリエ空間でのラプラスの欠点を解決すためポアソン方程式(PDE)を使います\nPDEを使ったアルゴリズムは結果が大きく異なるため、標準とは異なる設定が必要でしょう\n露出不足の画像に有効かもしれません\nPDEはアーティファクトとノイズを軽減します +TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;アーティファクト(ノイズ)の発生を避けるため、ラプラス変換の前にメディアンを適用します +TP_LOCALLAB_EXPOSE;露光補正-PDEアルゴリズム +TP_LOCALLAB_EXPOSURE_TOOLTIP;シャドウ部分が強いような場合は、”シャドウ/ハイライト”のモジュールが使えます +TP_LOCALLAB_EXPRETITOOLS;高度なレティネックス機能 +TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-スポットの大きさが最低でも39x39ピクセル必要です\nスポットが小さい場合は、低い境界値、高い減衰値、高いスコープ値を設定します +TP_LOCALLAB_EXPTOOL;露光補正の機能 +TP_LOCALLAB_EXPTRC;トーンリプロダクションカーブ - TRC +TP_LOCALLAB_EXP_TOOLNAME;露光補正 - ダイナミックレンジ圧縮 - 10 +TP_LOCALLAB_FATAMOUNT;量 +TP_LOCALLAB_FATANCHOR;アンカー +TP_LOCALLAB_FATANCHORA;オフセット +TP_LOCALLAB_FATDETAIL;ディテール +TP_LOCALLAB_FATFRA;ダイナミックレンジ圧縮 f +TP_LOCALLAB_FATFRAME_TOOLTIP;ここではFattalトーンマッピングアルゴリズムを使います\nアンカーで画像の露出不足・過多に応じた選択が出来ます\n現在のスポットに近く、マスクを使用する2番目のスポットの輝度を増やすのに便利です +TP_LOCALLAB_FATLEVEL;シグマ +TP_LOCALLAB_FATRES;残差画像の量 +TP_LOCALLAB_FATSHFRA;マスクのダイナミックレンジ圧縮のマスク f +TP_LOCALLAB_FEATH_TOOLTIP;スポットの対角線に対する減光フィルタの幅の割合\n.. +TP_LOCALLAB_FEATVALUE;フェザー処理(階調フィルタ) +TP_LOCALLAB_FFTCOL_MASK;FFTW f +TP_LOCALLAB_FFTW;f 高速フーリエ変換を使う +TP_LOCALLAB_FFTW2;f 高速フーリエ変換を使う(TIF, JPG,..) +TP_LOCALLAB_FFTWBLUR;ƒ - 常に高速フーリエ変換を使う +TP_LOCALLAB_FULLIMAGE;画像全体のブラックEvとホワイトEvを計算 +TP_LOCALLAB_GAM;ガンマ +TP_LOCALLAB_GAMFRA;トーンリプロダクションカーブ(TRC) +TP_LOCALLAB_GAMM;ガンマ +TP_LOCALLAB_GAMMASKCOL;ガンマのマスク +TP_LOCALLAB_GAMSH;ガンマ +TP_LOCALLAB_GRADANG;階調フィルタの角度 +TP_LOCALLAB_GRADANG_TOOLTIP;-180度から+180度の間で角度を調整 +TP_LOCALLAB_GRADFRA;階調フィルタ +TP_LOCALLAB_GRADGEN_TOOLTIP;階調フィルタの機能は”色と明るさ”と、”露光”、”シャドウ/ハイライト”、”自然な彩度”に備わっています\n\n自然な彩度、色と明るさには輝度、色調、色相の階調フィルタが使えます\nフェザー処理は設定の中にあります +TP_LOCALLAB_GRADLOGFRA;階調フィルタ 輝度 +TP_LOCALLAB_GRADSTR;階調フィルタ 強さ +TP_LOCALLAB_GRADSTRAB_TOOLTIP;色度の階調の強さを調整します +TP_LOCALLAB_GRADSTRCHRO;色調の階調の強さ +TP_LOCALLAB_GRADSTRHUE;色相の階調の強さ(融合されたファイル) +TP_LOCALLAB_GRADSTRHUE2;色相の階調の強さ +TP_LOCALLAB_GRADSTRHUE_TOOLTIP;色相の階調の強さを調整します +TP_LOCALLAB_GRADSTRLUM;輝度の階調の強さ +TP_LOCALLAB_GRADSTR_TOOLTIP;露出度の階調の強さを調整します +TP_LOCALLAB_GRAINFRA;フィルムの質感 1:1 +TP_LOCALLAB_GRALWFRA;階調フィルタ ローカルコントラスト +TP_LOCALLAB_GRIDFRAME_TOOLTIP;スポットは均一な画像部分にある方が望ましいです\n\n通常モードの場合だけに使えます。融合された背景による色相、彩度、色、輝度が関係します。 +TP_LOCALLAB_GRIDONE;カラートーン調整 +TP_LOCALLAB_GRIDTWO;直接 +TP_LOCALLAB_GUIDBL;ソフトな半径 +TP_LOCALLAB_GUIDFILTER;ガイド付きフィルタの半径 +TP_LOCALLAB_GUIDFILTER_TOOLTIP;画像に応じて値を決めます - 画像が霞んでいなければ値を低く取ります +TP_LOCALLAB_HHMASK_TOOLTIP;例えば肌の微妙な色相調整に使います +TP_LOCALLAB_HIGHMASKCOL;ハイライトマスク +TP_LOCALLAB_HLH;カーブ H +TP_LOCALLAB_IND;独立 (マウス) +TP_LOCALLAB_INDSL;独立 (マウス + スライダー) +TP_LOCALLAB_INVERS;反対 +TP_LOCALLAB_INVERS_TOOLTIP;反対を選択すると調整の多様性が失われます\n\n代わりの方法\n初めのスポット:\n画像全体 –境界線をプレビュー画像の外側にセットします\n スポットの形状は矩形、境界値は100\n\n2番目のスポットを作成し除外スポットにします +TP_LOCALLAB_ISOGR;粗さ (ISO) +TP_LOCALLAB_LABBLURM;マスクぼかし +TP_LOCALLAB_LABEL;ローカル調整 +TP_LOCALLAB_LABGRID;カラー補正グリッド +TP_LOCALLAB_LABGRIDMERG;背景 +TP_LOCALLAB_LABGRID_VALUES;高(a)=%1 高(b)=%2\n低(a)=%3 低(b)=%4 +TP_LOCALLAB_LABSTRUM;マスクの構造 +TP_LOCALLAB_LAPLACC;ΔØ ラプラシアンマスク PDEの境界条件あり +TP_LOCALLAB_LAPLACE;Δ ラプラシアンのしきい値 ΔE +TP_LOCALLAB_LAPLACEXP;Δ ラプラシアンのしきい値 +TP_LOCALLAB_LAPMASKCOL;Δ ラプラシアンのしきい値マスク +TP_LOCALLAB_LAPRAD_TOOLTIP;半径とラプラス変換のしきい値を同時に使うことを避けます。 +TP_LOCALLAB_LAP_MASK_TOOLTIP;全てのラプラシアンマスクのポアソン方程式の解を求めます\nラプラシアンのしきい値マスクを有効にするとアーティファクトが軽減され、スムーズな効果が得られます\n無効の場合は線形的な応答となります +TP_LOCALLAB_LC_FFTW_TOOLTIP;高速フーリエ変換は画像の質を改善し、大きな半径を使えるようにします\n処理する領域の大きさに応じて処理時間が増えます\n大きな半径で使うことを奨めます\n\nFFTWの最適化を図るために領域を数ピクセル削ります +TP_LOCALLAB_LC_TOOLNAME;ローカルコントラスト & ウェーブレット (不良部分の補正) - 7 +TP_LOCALLAB_LEVELBLUR;ぼかしを施すレベルの最大値 +TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;横軸はローカルコントラスト(輝度に近い)を表し、縦軸はローカルコントラストの増減を表します +TP_LOCALLAB_LEVELWAV;Ψ ウェーブレットのレベル +TP_LOCALLAB_LEVELWAV_TOOLTIP;詳細レベルの数はスポットとプレビューのサイズに応じて自動で決まります\n最大512ピクセルで解析するレベル8から最大4ピクセルで解析するレベル1まで +TP_LOCALLAB_LIGHTNESS;明るさ +TP_LOCALLAB_LIGHTN_TOOLTIP;反対モードで明るさを-100にすると輝度が0になります +TP_LOCALLAB_LIGHTRETI;明るさ +TP_LOCALLAB_LINEAR;線形性 +TP_LOCALLAB_LIST_NAME;現在のスポットに機能を追加 +TP_LOCALLAB_LIST_TOOLTIP;機能を選んだ後、その複雑度、”通常”或いは”エキスパート”を選択します\n付随する番号は各RT-スポットの処理の中の機能がある場所をしましています +TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;選択したウェーブレットのレベルによって、中間トーンとハイライトへの作用を優先します +TP_LOCALLAB_LMASK_LL_TOOLTIP;中間トーンとハイライトへの作用を優先します +TP_LOCALLAB_LOCCONT;アンシャープマスク +TP_LOCALLAB_LOC_CONTRAST;ローカルコントラスト-ウェーブレット-欠損/汚れの補正 +TP_LOCALLAB_LOC_CONTRASTPYR;Ψ ピラミッド1: +TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ ピラミッド2: +TP_LOCALLAB_LOC_CONTRASTPYR2LAB;レベルによるコントラスト調整- トーンマッピング(s) +TP_LOCALLAB_LOC_CONTRASTPYRLAB;階調フィルタ - エッジシャープネス - ぼかし +TP_LOCALLAB_LOC_RESIDPYR;残差画像 メイン +TP_LOCALLAB_LOG;対数符号化 +TP_LOCALLAB_LOGAUTO;自動 +TP_LOCALLAB_LOGAUTO_TOOLTIP;このボタンを押すことで、ダイナミックレンジとグレーポイントの源泉の推定値が得られます(グレーポイントの源泉で"自動"が有効になっている場合)\n自動計算による値を仕上げるためには、もう一度ボタンを押します +TP_LOCALLAB_LOGBASE_TOOLTIP;デフォルト値は2です\n2以下にするとアルゴリズムの作用が減少します。シャドウ部分がより暗く、ハイライト部分がより明るくなります\n2以上にするとアルゴリズムの作用が変わります。シャドウ部分は灰色がかり、ハイライト部分の色がさめた印象になります +TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;ダイナミックレンジの推定値 - ブラックEvとホワイトEv +TP_LOCALLAB_LOGENCOD_TOOLTIP;対数符号化(ACES)を使ってトーンマッピングを行います\n露出不足やハイダイナミックレンジの画像の補正に便利です\n\n処理は : 1) ダイナミックレンジを計算、2) 好みに応じて調節 +TP_LOCALLAB_LOGFRA;グレーポイントの源泉 +TP_LOCALLAB_LOGFRAME_TOOLTIP;処理の初期段階で画像の露光レベルを計算する、或いはそのまま使用します:\n前者はブラックEv, ホワイトEv、グレーポイントの源泉から計算\n後者はメインの露光量補正の値を使います +TP_LOCALLAB_LOGLIN;対数モード +TP_LOCALLAB_LOGPFRA;相対的な露光レベル +TP_LOCALLAB_LOGSRCGREY_TOOLTIP;画像のグレーポイントの推定値、処理行程の前の方 +TP_LOCALLAB_LOGTARGGREY_TOOLTIP;好みに合わせて適用する値を変えられます +TP_LOCALLAB_LOG_TOOLNAME;対数符号化 - 0 +TP_LOCALLAB_LUM;カーブ LL - CC +TP_LOCALLAB_LUMADARKEST;最も暗い部分 +TP_LOCALLAB_LUMASK;マスクの背景輝度 +TP_LOCALLAB_LUMASK_TOOLTIP;マスクの表示(マスクと調節)で、背景のグレーを調節します +TP_LOCALLAB_LUMAWHITESEST;最も明るい部分 +TP_LOCALLAB_LUMONLY;輝度だけ +TP_LOCALLAB_MASFRAME;マスクと融合 +TP_LOCALLAB_MASFRAME_TOOLTIP;全てのマスクに共通する\nガンマ、スロープ、色度、コントラストカーブ、詳細レベルのコントラストカーブのマスクが使われる時は、選択領域のレタッチを避けるために色差イメージを考慮する。 +TP_LOCALLAB_MASK;マスク +TP_LOCALLAB_MASK2;コントラストカーブのマスク +TP_LOCALLAB_MASKCOL;カーブマスク +TP_LOCALLAB_MASKH;色相のカーブマスク +TP_LOCALLAB_MASK_TOOLTIP;使いたい一つの機能で複数のマスクを有効に出来ますが、そのためには別の機能を有効にする必要があります(但し、その機能自体を使う必要はありません、例えば、スライダーが0でも構いません)\nこの特性を有するマスクには’+’のマークがあります\n追加的な機能を持つマスクには’*’のマークが付いています\n数字が示すのはマスクの使用の順番です\n\nインバースマスクがある機能に関するマスクは組み合わせることが可能です\nこの場合、マスクに関連する新しい機能はインバースの中で選択します;その機能を使う際は値を非常に小さくしなければなりません(例えば、露光0.01) +TP_LOCALLAB_MED;中間 +TP_LOCALLAB_MEDIAN;メディアン 低 +TP_LOCALLAB_MEDNONE;なし +TP_LOCALLAB_MERCOL;色 +TP_LOCALLAB_MERDCOL;背景の融合(ΔE) +TP_LOCALLAB_MERELE;明るくするだけ +TP_LOCALLAB_MERFIV;追加 +TP_LOCALLAB_MERFOR;色の覆い焼き +TP_LOCALLAB_MERFOU;乗算 +TP_LOCALLAB_MERGE1COLFRA;オリジナル或いは前のイメージと融合 +TP_LOCALLAB_MERGECOLFRA;マスク: LCHと構造 +TP_LOCALLAB_MERGEFIV;前のスポット(マスク7) + LCHマスク +TP_LOCALLAB_MERGEFOU;前のスポット(マスク7) +TP_LOCALLAB_MERGEMER_TOOLTIP;融合画像に対しΔEを計算に入れます(この方法はスコープの作用と同等です) +TP_LOCALLAB_MERGENONE;なし +TP_LOCALLAB_MERGEONE;ショートカーブ'L'のマスク +TP_LOCALLAB_MERGEOPA_TOOLTIP;オリジナル或いは前のスポットと現在のスポットとの融合する際の不透明度の % \nコントラストのしきい値:オリジナルコントラストの機能の結果を調整 +TP_LOCALLAB_MERGETHR;オリジナル(マスク7) + LCHマスク +TP_LOCALLAB_MERGETWO;オリジナル(マスク7) +TP_LOCALLAB_MERGETYPE;イメージとマスクの融合 +TP_LOCALLAB_MERGETYPE_TOOLTIP;なしの場合、LCHモードの全てのマスクを使います\nショートカーブ 'L'マスクの場合、マスク2、3、4、6、7はスキップします\nオリジナルマスク7の場合、現在のイメージと元のイメージを融合します +TP_LOCALLAB_MERHEI;重ね合わせ +TP_LOCALLAB_MERHUE;色相 +TP_LOCALLAB_MERLUCOL;輝度 +TP_LOCALLAB_MERLUM;光度 +TP_LOCALLAB_MERNIN;スクリーン +TP_LOCALLAB_MERONE;標準 +TP_LOCALLAB_MERSAT;彩度 +TP_LOCALLAB_MERSEV;ソフトライト Photoshop +TP_LOCALLAB_MERSEV0;ソフトライト イリュージョン +TP_LOCALLAB_MERSEV1;ソフトライト W3C +TP_LOCALLAB_MERSEV2;ハードライト +TP_LOCALLAB_MERSIX;分割 +TP_LOCALLAB_MERTEN;暗くするだけ +TP_LOCALLAB_MERTHI;色の焼き込み +TP_LOCALLAB_MERTHR;差異 +TP_LOCALLAB_MERTWE;除外 +TP_LOCALLAB_MERTWO;減算 +TP_LOCALLAB_METHOD_TOOLTIP;'強化 + 色ノイズ低減'を選ぶと処理時間が著しく増加します\nしかし、アーティファクトは軽減されます +TP_LOCALLAB_MLABEL;復元されたデータ 最小値=%1 最大値=%2 (クリップ - オフセット) +TP_LOCALLAB_MLABEL_TOOLTIP;最低値=0、最大値=32768の近くになるよう調整します\n標準化を行うため‘保持されたデータを切り取る’と‘オフセット’を使えます\n\n混成のない画像に戻します +TP_LOCALLAB_MODE_EXPERT;エキスパート +TP_LOCALLAB_MODE_NORMAL;通常 +TP_LOCALLAB_MRFIV;背景 +TP_LOCALLAB_MRFOU;前のスポット +TP_LOCALLAB_MRONE;なし +TP_LOCALLAB_MRTHR;元のイメージ +TP_LOCALLAB_MRTWO;ショートカーブ 'L'マスク +TP_LOCALLAB_MULTIPL_TOOLTIP;非常に広範囲(-18EV~+4EV)でトーンのレタッチが出来ます。初めのスライダーは-18EV~-6EVの非常に暗い部分に作用します。最後のスライダーは4EVまでの明るい部分に作用します +TP_LOCALLAB_NEIGH;半径 +TP_LOCALLAB_NOISECHROCOARSE;色度 粗い (ウェーブレット) +TP_LOCALLAB_NOISECHROC_TOOLTIP;0より大きい値で効果の高いアルゴリズムが働き始めます\n大まかなスライダーの場合は2以上からです +TP_LOCALLAB_NOISECHRODETAIL;色度 細部の回復 (DCT) +TP_LOCALLAB_NOISECHROFINE;色度 細かい (ウェーブレット) +TP_LOCALLAB_NOISEDETAIL_TOOLTIP;スライダー値が100になると無効 +TP_LOCALLAB_NOISELEQUAL;イコライザ 白黒 +TP_LOCALLAB_NOISELUMCOARSE;輝度 大まか(ウェーブレット) +TP_LOCALLAB_NOISELUMDETAIL;輝度 細部の回復 (DCT) +TP_LOCALLAB_NOISELUMFINE;輝度 詳細レベル2(ウェーブレット) +TP_LOCALLAB_NOISELUMFINETWO;輝度 詳細レベル3(ウェーブレット) +TP_LOCALLAB_NOISELUMFINEZERO;輝度 詳細レベル1(ウェーブレット) +TP_LOCALLAB_NOISEMETH;ノイズ低減 +TP_LOCALLAB_NONENOISE;なし +TP_LOCALLAB_OFFS;オフセット +TP_LOCALLAB_OFFSETWAV;オフセット +TP_LOCALLAB_OPACOL;不透明度 +TP_LOCALLAB_ORIGLC;元画像だけと融合 +TP_LOCALLAB_ORRETILAP_TOOLTIP;2次ラプラシアンのしきい値に作用します。作用に差をつけるため、特に背景に対する作用、ΔEを計算に入れます(スコープの作用と異なります) +TP_LOCALLAB_ORRETISTREN_TOOLTIP;1次ラプラシアンのしきい値に作用します。設定値を高くするほど、コントラストの違いが減少します +TP_LOCALLAB_PASTELS2;自然な彩度 +TP_LOCALLAB_PDE;ΔØ ラプラシアン PDE - ダイナミックレンジ圧縮 + 標準 +TP_LOCALLAB_PDEFRA;PDE IPOL - コントラスト減衰 +TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - IPOLから取り入れ、独自にRawtherapee用にアレンジしたアルゴリズム:非常に異なる効果が出るので設定を変える必要があります。標準的にはブラックをマイナス値、ガンマを1以下にするなど\n露出の低い画像に便利だと思われます\n +TP_LOCALLAB_PREVIEW;ΔEのプレビュー +TP_LOCALLAB_PROXI;ΔEの減衰 +TP_LOCALLAB_QUALCURV_METHOD;カーブのタイプ +TP_LOCALLAB_QUAL_METHOD;全体の質 +TP_LOCALLAB_RADIUS;半径 +TP_LOCALLAB_RADIUS_TOOLTIP;半径の値が30より大きい場合は、高速フーリエ変換を使います +TP_LOCALLAB_RADMASKCOL;半径のマスクを滑らかにする +TP_LOCALLAB_RECT;長方形 +TP_LOCALLAB_RECURS;参考値の繰り返し +TP_LOCALLAB_RECURS_TOOLTIP;新しいモジュール使用とRT-スポットが作成される度に、色相、輝度、色度の参考値が再計算されます。\nマスクを使った作業に便利です。 +TP_LOCALLAB_REFLABEL;参照 (0..1) 色度=%1 輝度=%2 色相=%3 +TP_LOCALLAB_REN_DIALOG_LAB;新しいコントロールスポットの名前を入力 +TP_LOCALLAB_REN_DIALOG_NAME;コントロールスポットの名前変更 +TP_LOCALLAB_RESETSHOW;全ての表示変更をリセット +TP_LOCALLAB_RESID;残差画像 +TP_LOCALLAB_RESIDBLUR;残差画像をぼかす +TP_LOCALLAB_RESIDCHRO;残差画像の色度 +TP_LOCALLAB_RESIDCOMP;残差画像の圧縮 +TP_LOCALLAB_RESIDCONT;残差画像のコントラスト +TP_LOCALLAB_RESIDHI;ハイライト +TP_LOCALLAB_RESIDHITHR;ハイライトのしきい値 +TP_LOCALLAB_RESIDSHA;シャドウ +TP_LOCALLAB_RESIDSHATHR;シャドウのしきい値 +TP_LOCALLAB_RETI;霞除去 - レティネックス 強いコントラスト +TP_LOCALLAB_RETIFRA;レティネックス +TP_LOCALLAB_RETIM;独自のレティネックス +TP_LOCALLAB_RETITOOLFRA;レティネックスの機能 +TP_LOCALLAB_RETI_FFTW_TOOLTIP;高速フーリエ変換は質を向上させ大きな半径の使用が可能になります\n処理時間は編集する領域の大きさに応じて変わります \n大きな半径を扱う場合に適用するのがいいでしょう\n\n処理領域を数ピクセル減らすことでFFTWの最適化を図ることが出来ます \n但し、RT-スポットの境界線(縦或いは横)が画像からはみ出している場合は最適化を図れません +TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast. +TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible. +TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo. +TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP;画像に応じてこれらの値を適用します - 霧のかかった画像の場合で、調整したいのが前景或いは背景なのかに応じて。 +TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times +TP_LOCALLAB_RET_TOOLNAME;霞除去 & レティネックス - 9 +TP_LOCALLAB_REWEI;再加重平均の繰り返し +TP_LOCALLAB_RGB;RGB トーンカーブ +TP_LOCALLAB_ROW_NVIS;非表示 +TP_LOCALLAB_ROW_VIS;表示 +TP_LOCALLAB_SATUR;彩度 +TP_LOCALLAB_SAVREST;保存 - 元に戻した現在のイメージ +TP_LOCALLAB_SCALEGR;スケール +TP_LOCALLAB_SCALERETI;スケール +TP_LOCALLAB_SCALTM;スケール +TP_LOCALLAB_SCOPEMASK;ΔE画像のスコープマスク +TP_LOCALLAB_SCOPEMASK_TOOLTIP;色差画像のマスクを有効にすると使えます\n低い値にすると選択した領域の調整が行われません +TP_LOCALLAB_SENSI;スコープ +TP_LOCALLAB_SENSIBN;スコープ +TP_LOCALLAB_SENSICB;スコープ +TP_LOCALLAB_SENSIDEN;スコープ +TP_LOCALLAB_SENSIEXCLU;スコープ +TP_LOCALLAB_SENSIEXCLU_TOOLTIP;除外モードに含まれている色も調整 +TP_LOCALLAB_SENSIH;スコープ +TP_LOCALLAB_SENSIH_TOOLTIP;スコープの作用を調整します:\n小さい値を設定すると調整領域の色の変化は中心円に近いものに制限されます\n高い値を設定すると色の変化の範囲が広がります。\n20以下の設定がアルゴリズムの働きにとっていいでしょう +TP_LOCALLAB_SENSILOG;スコープ +TP_LOCALLAB_SENSIS;スコープ +TP_LOCALLAB_SENSIS_TOOLTIP;スコープの作用を調整します:\n小さい値を設定すると調整領域の色の変化は中心円に近いものに制限されます\n高い値を設定すると色の変化の範囲が広がります。\n20以下の設定がアルゴリズムの働きにとっていいでしょう +TP_LOCALLAB_SENSI_TOOLTIP;スコープの作用を調整します:\n小さい値を設定すると調整領域の色の変化は中心円に近いものに制限されます\n高い値を設定すると色の変化の範囲が広がります。\n20以下の設定がアルゴリズムの働きにとっていいでしょう +TP_LOCALLAB_SETTINGS;設定 +TP_LOCALLAB_SH1;シャドウ/ハイライト +TP_LOCALLAB_SH2;イコライザ +TP_LOCALLAB_SHADEX;シャドウ +TP_LOCALLAB_SHADEXCOMP;シャドウの圧縮とトーンの幅 +TP_LOCALLAB_SHADHIGH;シャドウ/ハイライト-階調-トーンイコライザ-TRC +TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP;露光モジュールだけでは処理が困難な場合に、代わりに使う、或いは補間に使います。\nノイズ低減の使用が必要かもしれません:シャドウを明るく +TP_LOCALLAB_SHAMASKCOL;シャドウのマスク +TP_LOCALLAB_SHAPETYPE;RT-スポット領域の形状 +TP_LOCALLAB_SHAPE_TOOLTIP;長方形は通常モードのみ +TP_LOCALLAB_SHARAMOUNT;量 +TP_LOCALLAB_SHARBLUR;半径のぼかし +TP_LOCALLAB_SHARDAMPING;減衰 +TP_LOCALLAB_SHARFRAME;変更 +TP_LOCALLAB_SHARITER;繰り返し +TP_LOCALLAB_SHARP;シャープニング +TP_LOCALLAB_SHARP_TOOLNAME;シャープニング - 8 +TP_LOCALLAB_SHARRADIUS;半径 +TP_LOCALLAB_SHORTC;ショートカーブ'L'マスク +TP_LOCALLAB_SHORTCMASK_TOOLTIP;L(L)とL(H)2つのカーブをつスキップします。\nマスクの作用によって調整された現在のイメージと元イメージを融合します\n但し、これが使えるのは2, 3, 4, 6, 7のマスクです +TP_LOCALLAB_SHOWC;マスクと調節 +TP_LOCALLAB_SHOWC1;ファイルの融合 +TP_LOCALLAB_SHOWCB;マスクと調節 +TP_LOCALLAB_SHOWDCT;フーリエの処理を表示 +TP_LOCALLAB_SHOWE;マスクと調節 +TP_LOCALLAB_SHOWFOURIER;フーリエ (DCT) +TP_LOCALLAB_SHOWLAPLACE;Δ ラプラシアン (最初) +TP_LOCALLAB_SHOWLC;マスクと調節 +TP_LOCALLAB_SHOWMASK;マスクの表示 +TP_LOCALLAB_SHOWMASKCOL_TOOLTIP;調整具合を表示させます\n但し、見られる表示は1度に1種類(色と明るさ、或いは露光のセクション)だけです\n'マスクと調整'のドロップダウンボックスで、'なし' 或いは マスクを表示できるものを選択します\n\nマスクの処理はは形状検出の前に行われます +TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP;フーリエによる処理を表示します:\n処理の異なる段階を表示\nラプラス - しきい値に応じた2次微分を行う(最初のステップ)\nフーリエ -変換したラプラスをDCTで表示\nポアソン - ポアソンDCEの解を表示\n標準化 - 輝度の標準化なしに結果を表示 +TP_LOCALLAB_SHOWMASKTYP1;ぼかしとノイズ +TP_LOCALLAB_SHOWMASKTYP2;ノイズ除去 +TP_LOCALLAB_SHOWMASKTYP3;ぼかしとノイズ + ノイズ除去 +TP_LOCALLAB_SHOWMASKTYP_TOOLTIP;マスクと調節を選択出来ます\nぼかしとノイズ:この場合は、'ノイズ除去'は使えません\nノイズ除去:この場合は、'ぼかしとノイズ'は使えません\n\nぼかしとノイズ+ノイズ除去:マスクを共用しますが、'調節を表示'と'スコープ'の扱いには注意が必要です +TP_LOCALLAB_SHOWMNONE;なし +TP_LOCALLAB_SHOWMODIF;マスクなしで変更を表示 +TP_LOCALLAB_SHOWMODIFMASK;マスクと共に変更を表示 +TP_LOCALLAB_SHOWNORMAL;輝度の標準化(なし) +TP_LOCALLAB_SHOWPLUS;マスクと調節- 平滑化によるぼかしとノイズ低減 +TP_LOCALLAB_SHOWPOISSON;ポアソン (pde f) +TP_LOCALLAB_SHOWR;マスクと調節 +TP_LOCALLAB_SHOWREF;ΔEのプレビュー +TP_LOCALLAB_SHOWS;マスクと調節 +TP_LOCALLAB_SHOWSTRUC;構造スポットを表示 +TP_LOCALLAB_SHOWSTRUCEX;構造スポットを表示 - "通常"では不可 +TP_LOCALLAB_SHOWT;マスクと調節 +TP_LOCALLAB_SHOWVI;マスクと調節 +TP_LOCALLAB_SHRESFRA;シャドウ/ハイライト +TP_LOCALLAB_SHTRC_TOOLTIP;TRC(諧調再現カーブ)を使って画像のトーンを調節します。\nガンマは主に明るいトーンに作用します\n勾配は主に暗いトーンに作用します +TP_LOCALLAB_SH_TOOLNAME;シャドウ/ハイライト & トーンイコライザ - 5 +TP_LOCALLAB_SIGMAWAV;減衰応答 +TP_LOCALLAB_SIM;シンプル +TP_LOCALLAB_SLOMASKCOL;スロープのマスク +TP_LOCALLAB_SLOSH;スロープ +TP_LOCALLAB_SOFT;ソフトライトと独自のレティネックス +TP_LOCALLAB_SOFTM;ソフトライト +TP_LOCALLAB_SOFTMETHOD_TOOLTIP;独自のレティネックスは他のレティネックス方式とは大きく異なります\nグレーと輝度のバランスに作用します +TP_LOCALLAB_SOFTRADIUSCOL;ソフトな半径 +TP_LOCALLAB_SOFTRETI;アーティファクトの軽減 ΔE +TP_LOCALLAB_SOFTRETI_TOOLTIP;透過マップを向上させるため色差を考慮します。 +TP_LOCALLAB_SOFT_TOOLNAME;ソフトライト & 独自のレティネックス - 6 +TP_LOCALLAB_SOURCE_GRAY;値 +TP_LOCALLAB_SPECCASE;特定のケース +TP_LOCALLAB_SPECIAL;RGBカーブの特殊な利用 +TP_LOCALLAB_SPECIAL_TOOLTIP;Only for this RGB curve, disabled (or reduce effects) of Scope, mask...for example, if you want to have a negative effect. +TP_LOCALLAB_SPOTNAME;新しいスポット +TP_LOCALLAB_STD;標準 +TP_LOCALLAB_STR;強さ +TP_LOCALLAB_STRBL;強さ +TP_LOCALLAB_STREN;圧縮の強さ +TP_LOCALLAB_STRENG;強さ +TP_LOCALLAB_STRENGR;強さ +TP_LOCALLAB_STRENGTH;ノイズ +TP_LOCALLAB_STRGRID;強さ +TP_LOCALLAB_STRRETI_TOOLTIP;レティネックスの強さが0.2より小さい場合は霞除去だけが有効となります。\nレティネックスの強さが0.1以上の場合、霞除去の作用は輝度だけです。 +TP_LOCALLAB_STRUC;構造 +TP_LOCALLAB_STRUCCOL;構造 +TP_LOCALLAB_STRUCCOL1;構造のスポット +TP_LOCALLAB_STRUCT_TOOLTIP;形状検出で構造を計算に入れるため、Sobelアルゴリズムを使います。\n“マスクと調節の構造スポットを表示”を有効にすればプレビューを見ることが出来ます。 +TP_LOCALLAB_STRUMASKCOL;構造マスクの強さ +TP_LOCALLAB_STRUMASK_TOOLTIP;Generate a structure mask with difference between surface areas and reliefs.\nIf structure mask as tool is enabled, this mask is used in addition to the other tools (gamma, slope, contrast curve ...) +TP_LOCALLAB_STYPE;形状の方式 +TP_LOCALLAB_STYPE_TOOLTIP;2つのタイプから選びます:\nシンメトリックは左と右の境界線、上部と底部の境界線がリンクしています\n独立は全ての境界線を独立で動かすことが出来ます +TP_LOCALLAB_SYM;シンメトリック(マウス) +TP_LOCALLAB_SYMSL;シンメトリック(マウス + スライダー) +TP_LOCALLAB_TARGET_GRAY;グレーポイントの目標 +TP_LOCALLAB_THRES;構造のしきい値 +TP_LOCALLAB_THRESDELTAE;ΔE-スコープのしきい値 +TP_LOCALLAB_THRESRETI;しきい値 +TP_LOCALLAB_THRESWAV;バランスのしきい値 +TP_LOCALLAB_TLABEL;TM データ 最小値=%1 最大値=%2 平均値=%3 標準偏差=%4 (しきい値) +TP_LOCALLAB_TLABEL2;TM 効果 Tm=%1 TM=%2 +TP_LOCALLAB_TLABEL_TOOLTIP;これは透過マップの結果を示しています。\n最小値と最大値は分散に使われます。\nTm、TMはそれぞれ透過マップの最小値と最大値です。\nしきい値を調整して標準化できます。 +TP_LOCALLAB_TM;トーンマッピング - 質感 +TP_LOCALLAB_TM_MASK;透過マップを使う +TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;エッジ停止を増やす - 或いは再加重平均の繰り返しを増やすと処理時間も増えます - 但し、その分効果が増します +TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma moves the action of tone-mapping to shadows or highlights. +TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;In some cases tone mapping may result in a cartoonish appearance, and in some rare cases soft but wide halos may appear.\n Increasing the number of reweighting iterates will help fight some of these problems. +TP_LOCALLAB_TONEMAP_TOOLTIP;トーンマッピング - メインメニューの同じ機能は必ず無効にする +TP_LOCALLAB_TONEMASCALE_TOOLTIP;This control gives meaning to the difference between "local" and "global" contrast.\nThe greater it is the larger a detail needs to be in order to be boosted +TP_LOCALLAB_TONE_TOOLNAME;トーンマッピング - 4 +TP_LOCALLAB_TOOLCOL;機能としての構造マスク +TP_LOCALLAB_TOOLMASK;機能 +TP_LOCALLAB_TRANSIT;境界の階調調整 +TP_LOCALLAB_TRANSITGRAD;境界の差異 XY +TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Y軸方向の境界に対するX軸方向の境界の割合を変える +TP_LOCALLAB_TRANSITVALUE;境界値 +TP_LOCALLAB_TRANSITWEAK;境界値の減衰 +TP_LOCALLAB_TRANSITWEAK_TOOLTIP;境界値の減衰を調節 : 処理の滑らかさを変える - 1 線形 - 2 パラボリック - 3 3乗 +TP_LOCALLAB_TRANSIT_TOOLTIP;作用が及ぶ部分と及ばない部分の境界の平滑化を調整します +TP_LOCALLAB_TRANSMISSIONGAIN;透過のゲイン +TP_LOCALLAB_TRANSMISSIONMAP;透過マップ +TP_LOCALLAB_TRANSMISSION_TOOLTIP;透過に応じて透過を決めるカーブです\n横軸はマイナス値(最小)から平均値、プラス値(最大)まであります\n\nこのカーブを使って透過を変え、アーティファクトを軽減できます +TP_LOCALLAB_USEMASK;マスクの使う +TP_LOCALLAB_VART;分散(コントラスト) +TP_LOCALLAB_VIBRANCE;自然な彩度 - ウォーム & クール +TP_LOCALLAB_VIB_TOOLNAME;自然な彩度 - ウォーム & クール - 3 +TP_LOCALLAB_VIS_TOOLTIP;選択したコントロールスポットを表示・非表示するためにはクリックします\n全てのコントロールスポットを表示・非表示するためにはCtrlを押しながらクリックします +TP_LOCALLAB_WAMASKCOL;Ψ ウェーブレットレベルのマスク +TP_LOCALLAB_WARM;ウォーム & -クールと偽色 +TP_LOCALLAB_WARM_TOOLTIP;このスライダーはホワイトバランスのようなCIECAMのアルゴリズムを使っています、選択したエリアの印象を暖かくしたり、冷たくしたりします。\n場合によっては、色のアーティファクトを軽減できます +TP_LOCALLAB_WASDEN_TOOLTIP;最初の3つの細かいレベルのノイズを軽減\n3より粗いレベルのノイズを軽減 +TP_LOCALLAB_WAV;レベルによるローカルコントラスト調整 +TP_LOCALLAB_WAVBLUR_TOOLTIP;残差画像を含め、分解された各詳細レベルに対してぼかしを実行します +TP_LOCALLAB_WAVCOMP;レベルごとの圧縮 +TP_LOCALLAB_WAVCOMPRE;レベルごとの(非)圧縮 +TP_LOCALLAB_WAVCOMPRE_TOOLTIP;トーンマッピング或いはレベルごとのローカルコントラストが軽減出来ます\n横軸がレベルの番手を表しています +TP_LOCALLAB_WAVCOMP_TOOLTIP;ウェーブレットによる分解の方向(水平、垂直、斜め)に応じたローカルコントラストを調整できます +TP_LOCALLAB_WAVCON;レベルによるコントラスト調整 +TP_LOCALLAB_WAVCONTF_TOOLTIP;詳細レベルによるコントラスト調整と似ています:横軸がレベルを表しています +TP_LOCALLAB_WAVDEN;レベルによる輝度ノイズの軽減(0 1 2 + 3、それ以上) +TP_LOCALLAB_WAVE;Ψ ウェーブレット +TP_LOCALLAB_WAVEDG;ローカルコントラスト +TP_LOCALLAB_WAVEEDG_TOOLTIP;少なくとも最初の4つのレベルが使える必要があります +TP_LOCALLAB_WAVGRAD_TOOLTIP;ローカルコントラストの”輝度”に関する諧調調整 +TP_LOCALLAB_WAVHIGH;Ψ ウェーブレット 高 +TP_LOCALLAB_WAVLEV;レベルごとのぼかし +TP_LOCALLAB_WAVLOW;Ψ ウェーブレット 低 +TP_LOCALLAB_WAVMASK;Ψ ローカルコントラストのレベルのマスク +TP_LOCALLAB_WAVMASK_TOOLTIP;Allows fine work on mask levels contrasts (structure) +TP_LOCALLAB_WAVMED;Ψ ウェーブレット 普通 +TP_LOCALLAB_WEDIANHI;メディアン 高 +TP_LOCALLAB_WHITE_EV;ホワイトEv +TP_LOCAL_HEIGHT;ボトム +TP_LOCAL_HEIGHT_T;トップ +TP_LOCAL_WIDTH;右 +TP_LOCAL_WIDTH_L;左 +TP_LOCRETI_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\n TP_METADATA_EDIT;変更を適用 TP_METADATA_MODE;メタデータ コピーモード TP_METADATA_STRIP;メタデータを全て取り除く @@ -1811,8 +2877,26 @@ TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;形状: 0=長方形、50=楕円形、100=円 TP_PCVIGNETTE_STRENGTH;強さ TP_PCVIGNETTE_STRENGTH_TOOLTIP;終点位置でのフィルターの強さ(四隅) TP_PDSHARPENING_LABEL;キャプチャーシャープニング +TP_PERSPECTIVE_CAMERA_CROP_FACTOR;クロップ・ファクター +TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH;焦点距離 +TP_PERSPECTIVE_CAMERA_FRAME;補正 +TP_PERSPECTIVE_CAMERA_PITCH;垂直 +TP_PERSPECTIVE_CAMERA_ROLL;回転 +TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;水平移動 +TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;垂直移動 +TP_PERSPECTIVE_CAMERA_YAW;水平 TP_PERSPECTIVE_HORIZONTAL;水平 TP_PERSPECTIVE_LABEL;パースペクティブ +TP_PERSPECTIVE_METHOD;方式 +TP_PERSPECTIVE_METHOD_CAMERA_BASED;カメラベース +TP_PERSPECTIVE_METHOD_SIMPLE;シンプル +TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME;補正後の調整 +TP_PERSPECTIVE_PROJECTION_PITCH;垂直 +TP_PERSPECTIVE_PROJECTION_ROTATE;回転 +TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL;水平移動 +TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL;垂直移動 +TP_PERSPECTIVE_PROJECTION_YAW;水平 +TP_PERSPECTIVE_RECOVERY_FRAME;回復 TP_PERSPECTIVE_VERTICAL;垂直 TP_PFCURVE_CURVEEDITOR_CH;色相 TP_PFCURVE_CURVEEDITOR_CH_TOOLTIP;デフリンジの強さをコントロールします。値が高いと効果が強まり、低いと弱まります @@ -1830,6 +2914,10 @@ TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;PDAFの場合は水平方向だ TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;垂直方向 TP_PREPROCESS_NO_FOUND;未検出 TP_PREPROCESS_PDAFLINESFILTER;PDAFラインフィルタ +TP_PREPROCWB_LABEL;ホワイトバランスの前処理 +TP_PREPROCWB_MODE;モード +TP_PREPROCWB_MODE_AUTO;自動 +TP_PREPROCWB_MODE_CAMERA;カメラ TP_PRSHARPENING_LABEL;リサイズ後のシャープニング TP_PRSHARPENING_TOOLTIP;リサイズ後の画像をシャープニングします。但し、リサイズの方式がランチョスの場合に限ります。プレビュー画面でこの機能の効果を見ることは出来ません。使用法に関してはRawPediaを参照して下さい。 TP_RAWCACORR_AUTO;自動補正 @@ -1934,8 +3022,8 @@ TP_RESIZE_SCALE;スケール TP_RESIZE_SPECIFY;条件指定: TP_RESIZE_W;幅: TP_RESIZE_WIDTH;幅 -TP_RETINEX_CONTEDIT_HSL;ヒストグラムイコライザ HSL -TP_RETINEX_CONTEDIT_LAB;ヒストグラムイコライザ L*a*b* +TP_RETINEX_CONTEDIT_HSL;HSLヒストグラム +TP_RETINEX_CONTEDIT_LAB;L*a*b*ヒストグラム TP_RETINEX_CONTEDIT_LH;色相イコライザ TP_RETINEX_CONTEDIT_MAP;イコライザ TP_RETINEX_CURVEEDITOR_CD;輝度=f(輝度) @@ -1947,8 +3035,8 @@ TP_RETINEX_CURVEEDITOR_MAP_TOOLTIP;このカーブは単独で使うことも、 TP_RETINEX_EQUAL;イコライザ TP_RETINEX_FREEGAMMA;フリーガンマ TP_RETINEX_GAIN;ゲイン -TP_RETINEX_GAINOFFS;ゲインとオフセット(明るさ) -TP_RETINEX_GAINTRANSMISSION;ゲインの透過 +TP_RETINEX_GAINOFFS;透過マップのゲインとオフセット +TP_RETINEX_GAINTRANSMISSION;ゲイン TP_RETINEX_GAINTRANSMISSION_TOOLTIP;目標とする輝度を得るために、透過マップ増幅したり減衰させたりします。\n横軸:左から最小、平均、最大となります。\n縦軸:ゲイン TP_RETINEX_GAMMA;ガンマ TP_RETINEX_GAMMA_FREE;フリー @@ -1957,8 +3045,8 @@ TP_RETINEX_GAMMA_LOW;低 TP_RETINEX_GAMMA_MID;中間 TP_RETINEX_GAMMA_NONE;なし TP_RETINEX_GAMMA_TOOLTIP;レティネックス機能の前後のガンマで画像のトーンを修復します。レティネックスのカーブや他のカーブ(Lab調整、露光補正などのカーブ)とは異なるカーブです。 -TP_RETINEX_GRAD;透過のグラデーション -TP_RETINEX_GRADS;強さのグラデーション +TP_RETINEX_GRAD;透過マップの勾配 +TP_RETINEX_GRADS;強さの勾配 TP_RETINEX_GRADS_TOOLTIP;スライダー値が0の場合、全ての繰り返しは同じになります\n0より大きい場合は、繰り返しが増加すると強さが減ります。0より小さい場合はその逆です TP_RETINEX_GRAD_TOOLTIP;スライダー値が0の場合、全ての繰り返しが同じになります\n0より大きい場合は、繰り返しが増加すると差異としきい値が減ります。0より小さい場合はその逆です TP_RETINEX_HIGH;高 @@ -1978,25 +3066,25 @@ TP_RETINEX_MAP;方式 TP_RETINEX_MAP_GAUS;ガウシアンマスク TP_RETINEX_MAP_MAPP;シャープマスク (一部ウェーブレット) TP_RETINEX_MAP_MAPT;シャープマスク (全てウェーブレット) -TP_RETINEX_MAP_METHOD_TOOLTIP;ガウス関数を利用して生成したマスクをハロやアーティファクトを減らすために使う方法です\n\nカーブだけ:マスクにコントラストカーブを適用します\nアーティファクト発生に注意\n\nガウシアンマスク:元画像に対しガウス暈しでマスクを生成し、それを使います\n処理時間短い\n\nシャープマスク:元画像に対しウェーブレットでマスクを生成し、それを使います\n処理時間が長い +TP_RETINEX_MAP_METHOD_TOOLTIP;ガウス関数(半径や方式)で生成されたマスクを利用してハロやアーティファクトを減らします\n\nカーブだけ:マスクにコントラストカーブを適用します\nアーティファクト発生に注意\n\nガウシアンマスク:元画像に対しガウス暈しでマスクを生成し、それを使います\n処理時間短い\n\nシャープマスク:元画像に対しウェーブレットでマスクを生成し、それを使います\n処理時間が長い TP_RETINEX_MAP_NONE;なし TP_RETINEX_MEDIAN;透過のメディアンフィルター TP_RETINEX_METHOD;方法 -TP_RETINEX_METHOD_TOOLTIP;高=明瞭な部分のレンダリングを補正します\n均等=明瞭な部分と不明瞭な部分をバランスよく補正します\n低=不明瞭な部分を重点的に補正します\nハイライト=ハイライト部分のマゼンタ被りを補正します +TP_RETINEX_METHOD_TOOLTIP;高=光度の高い部分のレンダリングを補正します\n均等=光度の高い部分と低い部分を均等に補正します\n低=光度の低い部分を重点的に補正します\nハイライト=ハイライト部分のマゼンタ被りを補正します TP_RETINEX_MLABEL;霞のない画像に修復 最小値=%1 最大値=%2 TP_RETINEX_MLABEL_TOOLTIP;最小値=0 最大値=32768に近づける\nバランスの良い霞のない画像 TP_RETINEX_NEIGHBOR;半径 TP_RETINEX_NEUTRAL;リセット TP_RETINEX_NEUTRAL_TIP;全てのスライダー値とカーブをデフォルトの状態に戻します -TP_RETINEX_OFFSET;オフセット(明るさ) -TP_RETINEX_SCALES;ガウス暈しのグラデーション +TP_RETINEX_OFFSET;オフセット +TP_RETINEX_SCALES;ガウスフィルタの勾配 TP_RETINEX_SCALES_TOOLTIP;スライダー値が0の場合、同一の作業を繰り返します\n0より大きい値を設定すると、繰り返し作業を増やした時に、スケールと隣接するピクセルに対する作用は減ります。0より小さい場合は、その逆です TP_RETINEX_SETTINGS;設定 TP_RETINEX_SKAL;スケール -TP_RETINEX_SLOPE;フリーなガンマの勾配 +TP_RETINEX_SLOPE;フリーガンマの勾配 TP_RETINEX_STRENGTH;強さ TP_RETINEX_THRESHOLD;しきい値 -TP_RETINEX_THRESHOLD_TOOLTIP;インとアウトの制限を意味します\nイン=原画像\nアウト=ガウス暈しを施した原画像 +TP_RETINEX_THRESHOLD_TOOLTIP;インとアウトの制限を意味します\nイン=原画像\nアウト=ガウスフィルタを施した原画像 TP_RETINEX_TLABEL;透過 差異の最小値=%1 最大値=%2 平均=%3 標準偏差=%4 TP_RETINEX_TLABEL2;透過 最小値=%1 最大値=%2 TP_RETINEX_TLABEL_TOOLTIP;透過マップの結果を表示しています\n差異の最小値と最大値です\n平均と標準偏差\n透過マップの最小値と最大値です @@ -2024,11 +3112,11 @@ TP_ROTATE_DEGREE;角度 TP_ROTATE_LABEL;回転 TP_ROTATE_SELECTLINE;直線選択・角度補正ツール TP_SAVEDIALOG_OK_TIP;ショートカット Ctrl-Enter -TP_SHADOWSHLIGHTS_HIGHLIGHTS;ハイライトを暗く +TP_SHADOWSHLIGHTS_HIGHLIGHTS;ハイライト TP_SHADOWSHLIGHTS_HLTONALW;ハイライトトーンの幅 TP_SHADOWSHLIGHTS_LABEL;シャドウ/ハイライト TP_SHADOWSHLIGHTS_RADIUS;半径 -TP_SHADOWSHLIGHTS_SHADOWS;シャドウを明るく +TP_SHADOWSHLIGHTS_SHADOWS;シャドウ TP_SHADOWSHLIGHTS_SHTONALW;シャドウトーンの幅 TP_SHARPENEDGE_AMOUNT;適用量 TP_SHARPENEDGE_LABEL;エッジ @@ -2105,14 +3193,20 @@ TP_WAVELET_B2;残差 TP_WAVELET_BACKGROUND;背景 TP_WAVELET_BACUR;カーブ TP_WAVELET_BALANCE;コントラストバランス 斜め/垂直-水平 -TP_WAVELET_BALANCE_TOOLTIP;ウェーブレットの方向で垂直-水平と斜めのバランスを変えます\nコントラスト、色度、或いは残差画像のトーンマッピングが有効の場合、バランスにより効果が増幅されます +TP_WAVELET_BALANCE_TOOLTIP;ウェーブレットの解析方向の垂直-水平と斜めのバランスを変えます\nコントラスト、色度、或いは残差画像のトーンマッピングが有効の場合、バランスにより効果が増幅されます TP_WAVELET_BALCHRO;色度のバランス +TP_WAVELET_BALCHROM;色ノイズ除去のイコライザ ブルー/イエロー レッド/グリーン TP_WAVELET_BALCHRO_TOOLTIP;有効にすると、’コントラストバランス’のカーブやスライダーも色度のバランスに影響します +TP_WAVELET_BALLUM;輝度ノイズ除去のイコライザ 白黒 TP_WAVELET_BANONE;なし TP_WAVELET_BASLI;スライダー -TP_WAVELET_BATYPE;バランス方式 +TP_WAVELET_BATYPE;コントラストバランス方式 +TP_WAVELET_BL;ぼかしのレベル +TP_WAVELET_BLCURVE;詳細レベルによるぼかし +TP_WAVELET_BLURFRAME;ぼかし +TP_WAVELET_BLUWAV;減衰応答 TP_WAVELET_CBENAB;カラートーンとカラーバランス -TP_WAVELET_CB_TOOLTIP;高い値は、’カラートーン’と併用するか、或いはカラートーンは使わないで単独で使います。\n低い値の場合は前景の色合いを変えずに背景(sky, ...) のホワイトバランスを変えるような効果を得られます。一般的にはコントラストが高くなる印象があります。 +TP_WAVELET_CB_TOOLTIP;高い値は、’カラートーン’と併用するか、或いはカラートーンは使わないで単独で使います。\n低い値の場合は前景の色合いを変えずに背景(空 ...) のホワイトバランスを変えるような効果を得られます。一般的にはコントラストが高くなる印象があります。 TP_WAVELET_CCURVE;ローカルコントラスト TP_WAVELET_CH1;全ての色 TP_WAVELET_CH2;明清色 - 純色 @@ -2120,22 +3214,32 @@ TP_WAVELET_CH3;コントラストのレベルとリンク TP_WAVELET_CHCU;カーブ TP_WAVELET_CHR;色度とコントラストのリンクの強さ TP_WAVELET_CHRO;純色 - 明清色のしきい値 +TP_WAVELET_CHROFRAME;色ノイズの除去 +TP_WAVELET_CHROMAFRAME;色度 +TP_WAVELET_CHROMCO;番手の高いレベルの色度 +TP_WAVELET_CHROMFI;番手の低いレベルの色度 TP_WAVELET_CHRO_TOOLTIP;どのレベルで明清色と純色を調整するか決めます\n1-x:純色を調整するレベルの範囲\nx-9:明清色を調整するレベルの範囲\n\n但し、値がレベルの総数より多い場合は機能が無効となります +TP_WAVELET_CHRWAV;色度のぼかし TP_WAVELET_CHR_TOOLTIP;色度を”コントラストレベル”と”色度とコントラストのリンクの強さ”の相関関係で調整します TP_WAVELET_CHSL;スライダー TP_WAVELET_CHTYPE;調整の方法 +TP_WAVELET_CLA;明瞭 +TP_WAVELET_CLARI;シャープマスクと明瞭 TP_WAVELET_COLORT;レッド/グリーンの不透明度 TP_WAVELET_COMPCONT;コントラスト TP_WAVELET_COMPGAMMA;ガンマの圧縮 TP_WAVELET_COMPGAMMA_TOOLTIP;残差画像のガンマを調整することで、画像データとヒストグラムの均衡を図ります。 TP_WAVELET_COMPTM;トーンマッピング TP_WAVELET_CONTEDIT;'後の' コントラストカーブ +TP_WAVELET_CONTFRAME;コントラスト - 圧縮 TP_WAVELET_CONTR;色域 TP_WAVELET_CONTRA;コントラスト +TP_WAVELET_CONTRASTEDIT;細かい~大まか レベルの指定 TP_WAVELET_CONTRAST_MINUS;コントラスト - TP_WAVELET_CONTRAST_PLUS;コントラスト + TP_WAVELET_CONTRA_TOOLTIP;残差画像のコントラストを変えます TP_WAVELET_CTYPE;色の制御 +TP_WAVELET_CURVEEDITOR_BL_TOOLTIP;拡大率が300%より上になると無効になります TP_WAVELET_CURVEEDITOR_CC_TOOLTIP;元画像のローカルコントラストに応じてローカルコントラストを調節します\n横軸の低い部分は細かいローカルコントラストを表しています(実質値10~20)\n50%はローカルコントラストの平均(実質値100~300)を表しています\n66%はローカルコントラストの標準偏差(実質値300~800)を表しています\n100%はローカルコントラストの最大値を表しています(実質値3000~8000) TP_WAVELET_CURVEEDITOR_CH;コントラストレベル=f(色相) TP_WAVELET_CURVEEDITOR_CH_TOOLTIP;色相に応じて各レベルのコントラストを調節します\n色域抑制のカーブ調整と重複しないように注意します\nウェーブレットのコントラストレベルスライダー値が0の場合は効果はありません @@ -2150,11 +3254,15 @@ TP_WAVELET_DAUB4;D4 - 標準 TP_WAVELET_DAUB6;D6 - 標準プラス TP_WAVELET_DAUB10;D10 - やや高い TP_WAVELET_DAUB14;D14 - 高い +TP_WAVELET_DAUBLOCAL;ウェーブレット エッジ検出の効果 TP_WAVELET_DAUB_TOOLTIP;ドブシー関数の係数を変更します\nD4=標準的なエッジ検出の効果\nD14=通常はエッジ検出の効果が高いが、処理時間が約10%増加\n\n初めのレベルの質だけでなくエッジ検出にも影響します。但し、レベル質は厳格に係数の種類に比例している訳ではありません。画像や使い方にも影響されます。 +TP_WAVELET_DIRFRAME;方向によるコントラスト TP_WAVELET_DONE;垂直 TP_WAVELET_DTHR;対角線 TP_WAVELET_DTWO;水平 TP_WAVELET_EDCU;カーブ +TP_WAVELET_EDEFFECT;減衰応答 +TP_WAVELET_EDEFFECT_TOOLTIP;このスライダーは機能の最大効果を受けるコントラスト値の範囲を調整するものです。最大値(2.5)を設定すると機能の効果が無効となります。 TP_WAVELET_EDGCONT;ローカルコントラスト TP_WAVELET_EDGCONT_TOOLTIP;スライダーを左に動かすとコントラストが減り、右に動かすと増えます\n底部の左、天井部の左、底部の右、天井部の右は、それぞれ低いコントラスト、平均的コントラスト、平均+1標準偏差のコントラスト、最も高いコントラストを示しています TP_WAVELET_EDGE;エッジのシャープネス @@ -2174,10 +3282,11 @@ TP_WAVELET_EDSL;しきい値スライダー TP_WAVELET_EDTYPE;ローカルコントラストの方式 TP_WAVELET_EDVAL;強さ TP_WAVELET_FINAL;最終調整 +TP_WAVELET_FINCFRAME;最終的なローカルコントラスト TP_WAVELET_FINEST;最も細かい -TP_WAVELET_HIGHLIGHT;ハイライトの輝度範囲 +TP_WAVELET_HIGHLIGHT;細かいレベルの輝度調整範囲 TP_WAVELET_HS1;全輝度範囲 -TP_WAVELET_HS2;シャドウ-ハイライト +TP_WAVELET_HS2;指定した輝度範囲 TP_WAVELET_HUESKIN;肌色の色相 TP_WAVELET_HUESKIN_TOOLTIP;底部の2つのポイントは、色相変化が始まる部分に設定されています、天井部の2つのポイントは変化が終わる所で、色相調整の効果が最も高い部分です\n\n設定ポイントを著しく動かす必要がある場合、或いはアーティファクトが発生するようであれば、ホワイトバランスが不適切と考えられます TP_WAVELET_HUESKY;色相の範囲(デフォルト:青空) @@ -2185,7 +3294,7 @@ TP_WAVELET_HUESKY_TOOLTIP;底部の2つのポイントは、色相変化が始 TP_WAVELET_ITER;デルタバランスのレベル TP_WAVELET_ITER_TOOLTIP;スライダーを左に動かすと、低いレベルのデルタが増え、高いレベルのデルタが減ります\n右に動かすとその逆です TP_WAVELET_LABEL;ウェーブレット -TP_WAVELET_LARGEST;最も粗い +TP_WAVELET_LARGEST;最も大まか TP_WAVELET_LEVCH;色度 TP_WAVELET_LEVDIR_ALL;全てのレベルと方向を合わせた画像 TP_WAVELET_LEVDIR_INF;選択したレベル以下を合わせた画像 @@ -2201,57 +3310,89 @@ TP_WAVELET_LEVTWO;レベル3 TP_WAVELET_LEVZERO;レベル1 TP_WAVELET_LINKEDG;エッジのシャープネスの強さとリンク TP_WAVELET_LIPST;高度なアルゴリズム -TP_WAVELET_LOWLIGHT;シャドウの輝度範囲 +TP_WAVELET_LOWLIGHT;大まかなレベルの輝度調整範囲 +TP_WAVELET_LOWTHR_TOOLTIP;詳細とノイズの増幅を避けます TP_WAVELET_MEDGREINF;最初のレベル TP_WAVELET_MEDI;青空のアーティファクトを軽減 TP_WAVELET_MEDILEV;エッジ検出 TP_WAVELET_MEDILEV_TOOLTIP;エッジの検出を有効にした際には、次の操作が奨められます:\n- アーティファクト発生を避けるため低いレベルのコントラストを使わない\n- グラデーション感度では高い値を使う\n\n効果を和らげるには、ノイズ低減とリファインの’リファイン’を下げる +TP_WAVELET_MERGEC;色度を融合 +TP_WAVELET_MERGEL;輝度を融合 TP_WAVELET_NEUTRAL;ニュートラル TP_WAVELET_NOIS;ノイズ低減 TP_WAVELET_NOISE;ノイズ低減とリファイン +TP_WAVELET_NOISE_TOOLTIP;詳細レベル4の輝度ノイズ除去が20以上の時にはアグレッシブモードが使われます\n色ノイズ除去で、大まかなレベルの値が20以上の時は、アグレッシブモードが使われます TP_WAVELET_NPHIGH;高い TP_WAVELET_NPLOW;低い TP_WAVELET_NPNONE;なし TP_WAVELET_NPTYPE;隣接するピクセルに対する効果 TP_WAVELET_NPTYPE_TOOLTIP;このアルゴリズムは近傍する8つのピクセルを使って比較します。違いが少ない場合に、エッジを強化します。 +TP_WAVELET_OFFSET_TOOLTIP;オフセットはシャドウとハイライトのバランスを調整する機能です\n 高い値を設定するとシャドウ部分のコントラスト強化が増幅されます。最小コントラストのしきい値と合わせて使えば、コントラストを高くしたい部分の見極めに役立つでしょう +TP_WAVELET_OLDSH;マイナス値が使えるアルゴリズム TP_WAVELET_OPACITY;ブルー/イエローの不透明度 TP_WAVELET_OPACITYW;コントラストバランス d/v-hカーブ TP_WAVELET_OPACITYWL;最終的なローカルコントラスト TP_WAVELET_OPACITYWL_TOOLTIP;ウェーブレット処理の最後で最終的なローカルコントラストを調整します\n\nイコライザは左から右に向かって、最も細かいローカルコントラストから大きいローカルコントラストを表しています TP_WAVELET_PASTEL;明清色の色度 TP_WAVELET_PROC;プロセス +TP_WAVELET_PROTAB;保護 +TP_WAVELET_RADIUS;シャドウ/ハイライトの半径 +TP_WAVELET_RANGEAB;aとbの範囲 % TP_WAVELET_RE1;強める TP_WAVELET_RE2;変えない TP_WAVELET_RE3;弱める -TP_WAVELET_RESCHRO;色度 +TP_WAVELET_RESBLUR;輝度のぼかし +TP_WAVELET_RESBLURC;色度のぼかし +TP_WAVELET_RESBLUR_TOOLTIP;拡大率が500%以上の場合は作用が無効となります +TP_WAVELET_RESCHRO;強さ TP_WAVELET_RESCON;シャドウ TP_WAVELET_RESCONH;ハイライト TP_WAVELET_RESID;残差画像 TP_WAVELET_SAT;純色の色度 TP_WAVELET_SETTINGS;ウェーブレットの設定 -TP_WAVELET_SKIN;色相-トーン (肌色) の目標/保護 +TP_WAVELET_SHA;シャープマスク +TP_WAVELET_SHFRAME;シャドウ/ハイライト +TP_WAVELET_SHOWMASK;ウェーブレットの'マスク'を表示 +TP_WAVELET_SIGMA;減衰応答 +TP_WAVELET_SIGMAFIN;減衰応答 +TP_WAVELET_SIGMA_TOOLTIP;コントラストスライダーの調整効果は、中間的なマイクロコントラストは強く、低い、或いは高いマイクロコントラストはそうでもありません。\n減衰応答は、この調整作用を極端なコントラスト値に向けて、どれだけ素早く減衰させるかコントロールするスライダーです。\n高い値を設定すると、減衰が強く作用するマイクロコントラストの領域が広がりますが、アーティファクトが発生することがあります。\n低い値を設定すると、減衰が強く作用するマイクロコントラストの領域が狭くなるので、よりピンポイントに効果が表れます。 +TP_WAVELET_SKIN;肌色の目標/保護 TP_WAVELET_SKIN_TOOLTIP;-100にすると肌色のトーンだけが調整の対象になります\n0にすると全てのカラートーンが調整されます\n+100にすると肌色のトーンは保護され、他のカラートーンが調整されます -TP_WAVELET_SKY;色相-トーン(青空)の目標/保護 +TP_WAVELET_SKY;色相の目標/保護 TP_WAVELET_SKY_TOOLTIP;-100にすると青空のトーンだけが調整の対象になります\n0にすると全てのカラートーンが調整されます\n+100にすると青空のトーンは保護され、他のカラートーンが調整されます +TP_WAVELET_SOFTRAD;ソフトな半径 TP_WAVELET_STREN;強さ TP_WAVELET_STRENGTH;強さ TP_WAVELET_SUPE;エキストラ TP_WAVELET_THR;シャドウのしきい値 -TP_WAVELET_THRESHOLD;ハイライトを調整するレベル -TP_WAVELET_THRESHOLD2;シャドウを調整するレベル -TP_WAVELET_THRESHOLD2_TOOLTIP;レベル9と(9-設定値)のレベルの間でシャドウの輝度が調整されます。他のレベルは輝度範囲全体で調整されます。 -TP_WAVELET_THRESHOLD_TOOLTIP;設定値以上のレベルだけが、ハイライトの輝度で調整されます。他のレベルは輝度範囲全体で調整されます。選べる設定値の最大数はシャドウレベルの設定に左右されます。 +TP_WAVELET_THRESHOLD;調整するレベル 細かい +TP_WAVELET_THRESHOLD2;調整するレベル 大まか +TP_WAVELET_THRESHOLD2_TOOLTIP;設定値より上の詳細レベルだけが、大まかなレベルの輝度範囲で設定された条件で調整されます。 +TP_WAVELET_THRESHOLD_TOOLTIP;設定値以下の詳細レベルだけが、細かいレベルの輝度範囲で設定された条件で調整されます。 +TP_WAVELET_THRESWAV;バランスのしきい値 TP_WAVELET_THRH;ハイライトのしきい値 TP_WAVELET_TILESBIG;大きいタイル TP_WAVELET_TILESFULL;画像全体 TP_WAVELET_TILESIZE;タイルのサイズ TP_WAVELET_TILESLIT;小さいタイル TP_WAVELET_TILES_TOOLTIP;画像全体を処理する方が良い結果をもたらすので、推奨される選択です。タイルによる処理はRAMの容量が小さいユーザー向けです。必要なメモリー容量に関してはRawPediaを参照して下さい。 -TP_WAVELET_TMSTRENGTH;残差の効力を圧縮 +TP_WAVELET_TMEDGS;エッジ停止 +TP_WAVELET_TMSCALE;スケール +TP_WAVELET_TMSTRENGTH;圧縮の強さ TP_WAVELET_TMSTRENGTH_TOOLTIP;トーンマッピングの強さや残差画像のコントラストの圧縮を加減します。値が0以外の場合、露光補正パネルのトーンマッピングでは、強さとガンマのスライダー無効となります TP_WAVELET_TMTYPE;圧縮の方法 TP_WAVELET_TON;カラートーン +TP_WAVELET_TONFRAME;除外されたカラー +TP_WAVELET_USH;なし +TP_WAVELET_USHARP;明瞭の方式 +TP_WAVELET_USHARP_TOOLTIP;オリジン : ウェーブレットによる調整を含まないソースファイル\nウェーブレット : ウェーブレットによる調整を含むソースファイル +TP_WAVELET_USH_TOOLTIP;シャープマスクを選択すると、ウェーブレットの設定が次の様に自動的に行われます:\n背景=ブラック、処理=レベル3以下...レベルは1~4の間で変えられます\n\n明瞭を選択すると、ウェーブレットの設定が次の様に自動的に行われます:\n背景=残差画像、処理=レベル7以上 レベルは5~10の間で変えられます +TP_WAVELET_WAVLOWTHR;最小コントラストのしきい値 +TP_WAVELET_WAVOFFSET;オフセット TP_WBALANCE_AUTO;自動補正 +TP_WBALANCE_AUTOITCGREEN;色温度の相関関係を繰り返し解析する +TP_WBALANCE_AUTOOLD;RGBグレーを使う +TP_WBALANCE_AUTO_HEADER;自動 TP_WBALANCE_CAMERA;カメラ TP_WBALANCE_CLOUDY;曇天 TP_WBALANCE_CUSTOM;カスタム @@ -2284,7 +3425,7 @@ TP_WBALANCE_LAMP_HEADER;ランプ TP_WBALANCE_LED_CRS;CRS SP12 WWMR16 TP_WBALANCE_LED_HEADER;LED TP_WBALANCE_LED_LSI;LSI Lumelex 2040 -TP_WBALANCE_METHOD;モード +TP_WBALANCE_METHOD;方式 TP_WBALANCE_PICKER;ピック TP_WBALANCE_SHADE;日陰 TP_WBALANCE_SIZE;サイズ: @@ -2292,7 +3433,9 @@ TP_WBALANCE_SOLUX35;Solux 3500K TP_WBALANCE_SOLUX41;Solux 4100K TP_WBALANCE_SOLUX47;Solux 4700K (vendor) TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) -TP_WBALANCE_SPOTWB;スポットWB +TP_WBALANCE_SPOTWB;ピペットを使ってプレビュー画像のニュートラルな部分をピックアップ +TP_WBALANCE_STUDLABEL;t検定 Itcwb: %1 +TP_WBALANCE_STUDLABEL_TOOLTIP;t検定の結果を表示\n低い値ほど相関関係が良いことになります\n値が0.002以下はエクセレント\n0.005以下は非常に良い\n0.01以下は良い\n0.05以下は十分\n0.5以上は悪い\n光源が標準的ではない場合は、t検定が良好であってもホワイトバラスが良いことにはなりません\nt検定結果が1000と表示された場合は反復解析が行われなかったことを意味します。良い結果と想定される前の計算結果が使われます TP_WBALANCE_TEMPBIAS;自動ホワイトバランス 色温度のバイアス TP_WBALANCE_TEMPBIAS_TOOLTIP;”自動ホワイトバランスの計算に変更を加えます”\n色温度を変えることで画像の暖かみを増やしたり、冷たさを増やしたりします。\n偏向の度合いは色温度の割合で表示されます\n従って計算値は "算出した色温度 + 算出した色温度 * 偏向"で計算したものです TP_WBALANCE_TEMPERATURE;色温度 @@ -2300,7 +3443,6 @@ TP_WBALANCE_TUNGSTEN;タングステン TP_WBALANCE_WATER1;水中 1 TP_WBALANCE_WATER2;水中 2 TP_WBALANCE_WATER_HEADER;水中 -The last update by firefly 2019-12-21 ZOOMPANEL_100;(100%) ZOOMPANEL_NEWCROPWINDOW;新規ディテール ウィンドウを開く ZOOMPANEL_ZOOM100;100%にズーム\nショートカット: z @@ -2313,7 +3455,4 @@ ZOOMPANEL_ZOOMOUT;ズームアウト\nショートカット: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! -!GENERAL_HELP;Help -!HISTORY_MSG_TRANS_Method;Geometry - Method -!TP_LENSGEOM_LIN;Linear -!TP_LENSGEOM_LOG;Logarithmic +!TP_WAVELET_FINCOAR_TOOLTIP;The left (positive) part of the curve acts on the finer levels (increase).\nThe 2 points on the abscissa represent the respective action limits of finer and coarser levels 5 and 6 (default).\nThe right (negative) part of the curve acts on the coarser levels (increase).\nAvoid moving the left part of the curve with negative values. Avoid moving the right part of the curve with positives values From 60728717a6ba1fe4e6554400607e15b07546aec6 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 27 Jun 2020 14:16:07 +0200 Subject: [PATCH 050/114] differentiate the action luma and chroma blend --- rtdata/languages/default | 6 +++-- rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 56 +++++++++++++++++++++------------------- rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 +++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 4 ++- rtgui/locallabtools.h | 1 + rtgui/locallabtools2.cc | 16 +++++++++++- rtgui/paramsedited.cc | 7 +++++ rtgui/paramsedited.h | 1 + 11 files changed, 67 insertions(+), 32 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index e1c7c9ddc..ffdeec93b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1167,7 +1167,7 @@ HISTORY_MSG_926;Local - Show mask type HISTORY_MSG_927;Local - Shadow mask HISTORY_MSG_928;Local - Common color mask HISTORY_MSG_929;Local - Mask common scope -HISTORY_MSG_930;Local - Mask Common blend +HISTORY_MSG_930;Local - Mask Common blend luma HISTORY_MSG_931;Local - Mask Common enable HISTORY_MSG_932;Local - Mask Common radius soft HISTORY_MSG_933;Local - Mask Common laplacian @@ -1190,6 +1190,7 @@ HISTORY_MSG_949;Local - Mask Common Threshold levels HISTORY_MSG_950;Local - Mask Common GF strength HISTORY_MSG_951;Local - Mask Common GF angle HISTORY_MSG_952;Local - Mask Common soft radius +HISTORY_MSG_953;Local - Mask Common blend chroma HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction @@ -2336,7 +2337,8 @@ TP_LOCALLAB_BASELOG;Logarithm base TP_LOCALLAB_BILATERAL;Bilateral filter TP_LOCALLAB_BLACK_EV;Black Ev TP_LOCALLAB_BLENDMASKCOL;Blend -TP_LOCALLAB_BLENDMASKMAK;Add / substract mask +TP_LOCALLAB_BLENDMASKMASK;Add / substract mask Luminance +TP_LOCALLAB_BLENDMASKMASKAB;Add / substract mask Chrominance TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;If this slider = 0 no action.\nAdd or subtract the mask from the original image TP_LOCALLAB_BLGUID;Guided Filter diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 682c97b90..3af1e0ec6 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -239,7 +239,7 @@ public: const LocCCmaskCurve & locccmasCurve, bool lcmasutili, const LocLLmaskCurve & locllmasCurve, bool llmasutili, const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, - bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, int shado, float amountcd, float anchorcd, + bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, float blendmab, int shado, float amountcd, float anchorcd, const LUTf& lmasklocalcurve, bool localmaskutili, const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 32fa07612..df6eaf567 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -3207,7 +3207,7 @@ void ImProcFunctions::blendstruc(int bfw, int bfh, LabImage* bufcolorig, float r } -static void blendmask(const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* original, LabImage* bufmaskor, LabImage* originalmas, float bl, int inv) +static void blendmask(const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* original, LabImage* bufmaskor, LabImage* originalmas, float bl, float blab, int inv) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) @@ -3231,8 +3231,8 @@ static void blendmask(const local_params& lp, int xstart, int ystart, int cx, in if (inv == 0) { if (zone > 0) { bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); - bufexporig->a[y][x] *= (1.f + bl * bufmaskor->a[y][x]); - bufexporig->b[y][x] *= (1.f + bl * bufmaskor->b[y][x]); + bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); @@ -3243,8 +3243,8 @@ static void blendmask(const local_params& lp, int xstart, int ystart, int cx, in originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->b[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); @@ -3255,8 +3255,8 @@ static void blendmask(const local_params& lp, int xstart, int ystart, int cx, in if (zone < 2) { bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); - bufexporig->a[y][x] *= (1.f + bl * bufmaskor->a[y][x]); - bufexporig->b[y][x] *= (1.f + bl * bufmaskor->b[y][x]); + bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); @@ -3269,8 +3269,8 @@ static void blendmask(const local_params& lp, int xstart, int ystart, int cx, in switch (zone) { case 0: { original->L[y + ystart][x + xstart] += (bl * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + bl * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + bl * bufmaskor->b[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->b[y][x]); original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); @@ -3279,8 +3279,8 @@ static void blendmask(const local_params& lp, int xstart, int ystart, int cx, in case 1: { original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->b[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); @@ -3733,7 +3733,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int const LocCCmaskCurve & locccmasCurve, bool lcmasutili, const LocLLmaskCurve & locllmasCurve, bool llmasutili, const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, - bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, int shado, float amountcd, float anchorcd, + bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, float blendmab, int shado, float amountcd, float anchorcd, const LUTf& lmasklocalcurve, bool localmaskutili, const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, @@ -4303,7 +4303,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int if (zero || modif || modmask || deltaE || enaMask) { originalmaskcol->CopyFrom(bufcolorig, multiThread); - blendmask(lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig, original, bufmaskblurcol, originalmaskcol, blendm, inv); + blendmask(lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig, original, bufmaskblurcol, originalmaskcol, blendm, blendmab, inv); } } } @@ -10141,10 +10141,10 @@ void ImProcFunctions::Lab_Local( } if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { - blendmask(lp, 0, 0, cx, cy, GW, GH, bufgb.get(), original, bufmaskorigbl.get(), originalmaskbl, lp.blendmabl, invers); + blendmask(lp, 0, 0, cx, cy, GW, GH, bufgb.get(), original, bufmaskorigbl.get(), originalmaskbl, lp.blendmabl, lp.blendmabl, invers); } else if (lp.showmaskblmet == 3) { showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufgb.get(), transformed, bufmaskorigbl.get(), invers); - return; + return; } } @@ -10809,7 +10809,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, loctemp.get(), bufmaskorigcb.get(), originalmaskcb.get(), original, reserved, inv, lp, 0.f, false, locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f, -1 ); @@ -11025,7 +11025,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigvib.get(), originalmaskvib.get(), original, reserved, inv, lp, 0.f, false, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11256,7 +11256,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgbm.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, 0.f, false, locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11297,7 +11297,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, tmp1.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, 0.f, false, locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11480,7 +11480,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigSH.get(), originalmaskSH.get(), original, reserved, inv, lp, 0.f, false, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11631,7 +11631,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskSH.get(), original, reserved, inv, lp, 0.f, false, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -11902,7 +11902,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgb.get(), bufmaskoriglc.get(), originalmasklc.get(), original, reserved, inv, lp, 0.f, false, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 ); @@ -13066,7 +13066,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, 0.f, false, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 ); @@ -13330,7 +13330,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, 0.f, false, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 ); @@ -13517,7 +13517,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, strumask, astool, locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 @@ -14471,7 +14471,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, strumask, params->locallab.spots.at(sp).toolcol, locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, level_bl, level_hl, level_br, level_hr, shortcu, false, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 @@ -14563,8 +14563,10 @@ void ImProcFunctions::Lab_Local( const float gamma = params->locallab.spots.at(sp).gammask; const float slope = params->locallab.spots.at(sp).slopmask; float blendm = params->locallab.spots.at(sp).blendmask; + float blendmab = params->locallab.spots.at(sp).blendmaskab; if (lp.showmask_met == 2) { blendm = 0.f;//normalize behavior mask with others no action of blend + blendmab = 0.f; } const float lap = params->locallab.spots.at(sp).lapmask; const bool pde = params->locallab.spots.at(sp).laplac; @@ -14589,7 +14591,7 @@ void ImProcFunctions::Lab_Local( maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, strumask, astool, locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, lochhhmas_Curve, lhhmas_utili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendmab, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, level_bl, level_hl, level_br, level_hr, shortcu, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma, 12 diff --git a/rtengine/procevents.h b/rtengine/procevents.h index cea95c451..afc6036f2 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -975,6 +975,7 @@ enum ProcEventCode { Evlocallabstr_mask = 949, Evlocallabang_mask = 950, Evlocallabsoftradiusmask = 951, + Evlocallabblendmaskab = 952, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 474814642..689f4dacf 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3739,6 +3739,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : expmask(false), sensimask(60), blendmask(-10), + blendmaskab(-10), softradiusmask(1.0), enamask(false), fftmask(true), @@ -4310,6 +4311,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && expmask == other.expmask && sensimask == other.sensimask && blendmask == other.blendmask + && blendmaskab == other.blendmaskab && softradiusmask == other.softradiusmask && enamask == other.enamask && fftmask == other.fftmask @@ -5832,6 +5834,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->complexmask, "Locallab", "Complexmask_" + index_str, spot.complexmask, keyFile); saveToKeyfile(!pedited || spot_edited->sensimask, "Locallab", "Sensimask_" + index_str, spot.sensimask, keyFile); saveToKeyfile(!pedited || spot_edited->blendmask, "Locallab", "Blendmaskmask_" + index_str, spot.blendmask, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskab, "Locallab", "Blendmaskmaskab_" + index_str, spot.blendmaskab, keyFile); saveToKeyfile(!pedited || spot_edited->softradiusmask, "Locallab", "Softradiusmask_" + index_str, spot.softradiusmask, keyFile); saveToKeyfile(!pedited || spot_edited->enamask, "Locallab", "Enamask_" + index_str, spot.enamask, keyFile); saveToKeyfile(!pedited || spot_edited->fftmask, "Locallab", "Fftmask_" + index_str, spot.fftmask, keyFile); @@ -7573,6 +7576,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Complexmask_" + index_str, pedited, spot.complexmask, spotEdited.complexmask); assignFromKeyfile(keyFile, "Locallab", "Sensimask_" + index_str, pedited, spot.sensimask, spotEdited.sensimask); assignFromKeyfile(keyFile, "Locallab", "Blendmaskmask_" + index_str, pedited, spot.blendmask, spotEdited.blendmask); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskmaskab_" + index_str, pedited, spot.blendmaskab, spotEdited.blendmaskab); assignFromKeyfile(keyFile, "Locallab", "Softradiusmask_" + index_str, pedited, spot.softradiusmask, spotEdited.softradiusmask); assignFromKeyfile(keyFile, "Locallab", "Enamask_" + index_str, pedited, spot.enamask, spotEdited.enamask); assignFromKeyfile(keyFile, "Locallab", "Fftmask_" + index_str, pedited, spot.fftmask, spotEdited.fftmask); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 08fc55d02..1d08049cd 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1427,6 +1427,7 @@ struct LocallabParams { bool expmask; int sensimask; int blendmask; + int blendmaskab; double softradiusmask; bool enamask; bool fftmask; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index b3a111b1b..855f09572 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -978,7 +978,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvlocallabcsThresholdmask LUMINANCECURVE, // Evlocallabstr_mask LUMINANCECURVE, // Evlocallabang_mask - LUMINANCECURVE // Evlocallabsoftradiusmask + LUMINANCECURVE, // Evlocallabsoftradiusmask + LUMINANCECURVE // Evlocallabblendmaskab + }; diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 4171c30b8..d3672b819 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1206,6 +1206,7 @@ class LocallabMask: private: Adjuster* const sensimask; Adjuster* const blendmask; + Adjuster* const blendmaskab; Adjuster* const softradiusmask; MyComboBoxText* const showmask_Method; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 9bbf5e50e..fee80a664 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4731,7 +4731,8 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), - blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMAK"), -100, 100, 1, -10))), + blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASK"), -100, 100, 1, -10))), + blendmaskab(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASKAB"), -100, 100, 1, -10))), softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 1.))), showmask_Method(Gtk::manage(new MyComboBoxText())), @@ -4773,6 +4774,7 @@ LocallabMask::LocallabMask(): sensimask->setAdjusterListener(this); blendmask->setAdjusterListener(this); + blendmaskab->setAdjusterListener(this); // softradiusmask->setLogScale(10, -10); softradiusmask->setAdjusterListener(this); showmask_Method->append(M("TP_LOCALLAB_SHOWMNONE")); @@ -4848,6 +4850,7 @@ LocallabMask::LocallabMask(): pack_start(*sensimask, Gtk::PACK_SHRINK, 0); pack_start(*blendmask, Gtk::PACK_SHRINK, 0); + pack_start(*blendmaskab, Gtk::PACK_SHRINK, 0); pack_start(*softradiusmask, Gtk::PACK_SHRINK, 0); ToolParamBlock* const maskmaskBox = Gtk::manage(new ToolParamBlock()); maskmaskBox->pack_start(*showmask_Method, Gtk::PACK_SHRINK, 4); @@ -4926,6 +4929,7 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) exp->set_tooltip_text(M("TP_LOCALLAB_MASKCOM_TOOLTIP")); sensimask->set_tooltip_text(M("TP_LOCALLAB_SENSIMASK_TOOLTIP")); blendmask->set_tooltip_text(M("TP_LOCALLAB_BLENDMASKMASK_TOOLTIP")); + blendmaskab->set_tooltip_text(M("TP_LOCALLAB_BLENDMASKMASK_TOOLTIP")); CCmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); LLmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); HHmask_shape->setTooltip(M("TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP")); @@ -5009,6 +5013,7 @@ void LocallabMask::read(const rtengine::procparams::ProcParams* pp, const Params blurmask->setValue(spot.blurmask); blendmask->setValue(spot.blendmask); + blendmaskab->setValue(spot.blendmaskab); softradiusmask->setValue(spot.softradiusmask); enamask->set_active(spot.enamask); CCmask_shape->setCurve(spot.CCmask_curve); @@ -5055,6 +5060,7 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.sensimask = sensimask->getIntValue(); spot.blendmask = blendmask->getIntValue(); + spot.blendmaskab = blendmaskab->getIntValue(); spot.softradiusmask = softradiusmask->getValue(); spot.enamask = enamask->get_active(); spot.CCmask_curve = CCmask_shape->getCurve(); @@ -5112,6 +5118,7 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams strumaskmask->setDefault(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); blendmask->setDefault((double)defSpot.blendmask); + blendmaskab->setDefault((double)defSpot.blendmaskab); softradiusmask->setDefault((double)defSpot.softradiusmask); radmask->setDefault(defSpot.radmask); lapmask->setDefault(defSpot.lapmask); @@ -5341,6 +5348,13 @@ void LocallabMask::adjusterChanged(Adjuster* a, double newval) } } + if (a == blendmaskab) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskab, + blendmaskab->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + if (a == softradiusmask) { if (listener) { listener->panelChanged(Evlocallabsoftradiusmask, diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 2031a7e8a..5e4b25042 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1483,6 +1483,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).expmask = locallab.spots.at(j).expmask && pSpot.expmask == otherSpot.expmask; locallab.spots.at(j).sensimask = locallab.spots.at(j).sensimask && pSpot.sensimask == otherSpot.sensimask; locallab.spots.at(j).blendmask = locallab.spots.at(j).blendmask && pSpot.blendmask == otherSpot.blendmask; + locallab.spots.at(j).blendmaskab = locallab.spots.at(j).blendmaskab && pSpot.blendmaskab == otherSpot.blendmaskab; locallab.spots.at(j).softradiusmask = locallab.spots.at(j).softradiusmask && pSpot.softradiusmask == otherSpot.softradiusmask; locallab.spots.at(j).enamask = locallab.spots.at(j).enamask && pSpot.enamask == otherSpot.enamask; locallab.spots.at(j).fftmask = locallab.spots.at(j).fftmask && pSpot.fftmask == otherSpot.fftmask; @@ -4831,6 +4832,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).blendmask = mods.locallab.spots.at(i).blendmask; } + if (locallab.spots.at(i).blendmaskab) { + toEdit.locallab.spots.at(i).blendmaskab = mods.locallab.spots.at(i).blendmaskab; + } + if (locallab.spots.at(i).softradiusmask) { toEdit.locallab.spots.at(i).softradiusmask = mods.locallab.spots.at(i).softradiusmask; } @@ -6494,6 +6499,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : expmask(v), sensimask(v), blendmask(v), + blendmaskab(v), softradiusmask(v), enamask(v), fftmask(v), @@ -6983,6 +6989,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) expmask = v; sensimask = v; blendmask = v; + blendmaskab = v; softradiusmask = v; enamask = v; fftmask = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index e6e6a2b2b..327f3a370 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -841,6 +841,7 @@ public: bool expmask; bool sensimask; bool blendmask; + bool blendmaskab; bool softradiusmask; bool enamask; bool fftmask; From f02c4e1fd129fad1af01c68386e74667bd59cc5b Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 27 Jun 2020 14:30:58 +0200 Subject: [PATCH 051/114] use LUT_CLIP_OFF instead of 0 --- rtengine/improccoordinator.cc | 38 +++++++++++++++++------------------ rtengine/simpleprocess.cc | 38 +++++++++++++++++------------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 2b83a661b..386de6310 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -187,25 +187,25 @@ ImProcCoordinator::ImProcCoordinator() : // Locallab locallListener(nullptr), - lllocalcurve(65536, 0), - cllocalcurve(65536, 0), - lclocalcurve(65536, 0), - cclocalcurve(65536, 0), - rgblocalcurve(65536, 0), - exlocalcurve(65536, 0), - hltonecurveloc(65536, 0), //32768 - shtonecurveloc(65536, 0), - tonecurveloc(65536, 0), - lightCurveloc(32770, 0), - lmasklocalcurve(65536, 0), - lmaskexplocalcurve(65536, 0), - lmaskSHlocalcurve(65536, 0), - lmaskviblocalcurve(65536, 0), - lmasktmlocalcurve(65536, 0), - lmaskretilocalcurve(65536, 0), - lmaskcblocalcurve(65536, 0), - lmaskbllocalcurve(65536, 0), - lmasklclocalcurve(65536, 0), + lllocalcurve(65536, LUT_CLIP_OFF), + cllocalcurve(65536, LUT_CLIP_OFF), + lclocalcurve(65536, LUT_CLIP_OFF), + cclocalcurve(65536, LUT_CLIP_OFF), + rgblocalcurve(65536, LUT_CLIP_OFF), + exlocalcurve(65536, LUT_CLIP_OFF), + hltonecurveloc(65536, LUT_CLIP_OFF), //32768 + shtonecurveloc(65536, LUT_CLIP_OFF), + tonecurveloc(65536, LUT_CLIP_OFF), + lightCurveloc(32770, LUT_CLIP_OFF), + lmasklocalcurve(65536, LUT_CLIP_OFF), + lmaskexplocalcurve(65536, LUT_CLIP_OFF), + lmaskSHlocalcurve(65536, LUT_CLIP_OFF), + lmaskviblocalcurve(65536, LUT_CLIP_OFF), + lmasktmlocalcurve(65536, LUT_CLIP_OFF), + lmaskretilocalcurve(65536, LUT_CLIP_OFF), + lmaskcblocalcurve(65536, LUT_CLIP_OFF), + lmaskbllocalcurve(65536, LUT_CLIP_OFF), + lmasklclocalcurve(65536, LUT_CLIP_OFF), lastspotdup(false), previewDeltaE(false), locallColorMask(0), diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index dae1973d5..6ab39b771 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1126,25 +1126,25 @@ private: LocwavCurve loccomprewavCurve; LocwavCurve locedgwavCurve; LocwavCurve locwavCurveden; - LUTf lllocalcurve(65536, 0); - LUTf lclocalcurve(65536, 0); - LUTf cllocalcurve(65536, 0); - LUTf cclocalcurve(65536, 0); - LUTf rgblocalcurve(65536, 0); - LUTf hltonecurveloc(65536, 0); - LUTf shtonecurveloc(65536, 0); - LUTf tonecurveloc(65536, 0); - LUTf lightCurveloc(32770, 0); - LUTf exlocalcurve(65536, 0); - LUTf lmasklocalcurve(65536, 0); - LUTf lmaskexplocalcurve(65536, 0); - LUTf lmaskSHlocalcurve(65536, 0); - LUTf lmaskviblocalcurve(65536, 0); - LUTf lmasktmlocalcurve(65536, 0); - LUTf lmaskretilocalcurve(65536, 0); - LUTf lmaskcblocalcurve(65536, 0); - LUTf lmaskbllocalcurve(65536, 0); - LUTf lmasklclocalcurve(65536, 0); + LUTf lllocalcurve(65536, LUT_CLIP_OFF); + LUTf lclocalcurve(65536, LUT_CLIP_OFF); + LUTf cllocalcurve(65536, LUT_CLIP_OFF); + LUTf cclocalcurve(65536, LUT_CLIP_OFF); + LUTf rgblocalcurve(65536, LUT_CLIP_OFF); + LUTf hltonecurveloc(65536, LUT_CLIP_OFF); + LUTf shtonecurveloc(65536, LUT_CLIP_OFF); + LUTf tonecurveloc(65536, LUT_CLIP_OFF); + LUTf lightCurveloc(32770, LUT_CLIP_OFF); + LUTf exlocalcurve(65536, LUT_CLIP_OFF); + LUTf lmasklocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskexplocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskSHlocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskviblocalcurve(65536, LUT_CLIP_OFF); + LUTf lmasktmlocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskretilocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskcblocalcurve(65536, LUT_CLIP_OFF); + LUTf lmaskbllocalcurve(65536, LUT_CLIP_OFF); + LUTf lmasklclocalcurve(65536, LUT_CLIP_OFF); array2D shbuffer; for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { From 67ca4011acdc54a81db6d2792fda56da1aaefbca Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 27 Jun 2020 14:35:14 +0200 Subject: [PATCH 052/114] Suppress bad code --- rtdata/languages/default | 1 - 1 file changed, 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index c43e895bb..7179e75f4 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1196,7 +1196,6 @@ HISTORY_MSG_BLSHAPE;Blur by level HISTORY_MSG_BLURCWAV;Blur chroma HISTORY_MSG_BLURWAV;Blur luminance HISTORY_MSG_BLUWAV;Attenuation Response ->>>>>>> dev HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction From 90005b06639bf7697bc141feae873435ff81c841 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 27 Jun 2020 15:21:50 +0200 Subject: [PATCH 053/114] Fix more relicts from merge conflict --- rtdata/languages/default | 3 --- 1 file changed, 3 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 7179e75f4..f68a97b39 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2858,7 +2858,6 @@ TP_LOCALLAB_USEMASK;Use mask TP_LOCALLAB_VART;Variance (contrast) TP_LOCALLAB_VIBRANCE;Vibrance - Warm & Cool TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Warm & Cool - 3 -<<<<<<< HEAD TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 TP_LOCALLAB_BLUR_TOOLNAME;Smooth Blur Gain & Denoise - 1 TP_LOCALLAB_TONE_TOOLNAME;Tone Mapping - 4 @@ -2868,9 +2867,7 @@ TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2 TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0 TP_LOCALLAB_MASKCOM_TOOLNAME;Common Color Mask - 13 -======= TP_LOCALLAB_VIS_TOOLTIP;Click to show/hide selected Control Spot.\nCtrl+click to show/hide all Control Spot. ->>>>>>> dev TP_LOCALLAB_WAMASKCOL;Ψ Mask Wavelet level TP_LOCALLAB_WARM;Warm - Cool & Color artifacts TP_LOCALLAB_WARM_TOOLTIP;This slider use Ciecam algorithm and acts as White Balance, it can warm or cool the area selected.\nIt can also in some cases reduce color artifacts. From 3bab9ec6881069ef141b906e94d6908d52cc9fee Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 27 Jun 2020 16:52:53 +0200 Subject: [PATCH 054/114] copy constructor for labimage, further cleanup for locallab periphery --- rtengine/dcrop.cc | 12 ++++-------- rtengine/improccoordinator.cc | 16 ++++------------ rtengine/labimage.cc | 10 ++++++++-- rtengine/labimage.h | 5 +++-- rtengine/simpleprocess.cc | 12 ++++-------- 5 files changed, 23 insertions(+), 32 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 28e5afc1f..81457940d 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -856,10 +856,8 @@ void Crop::update(int todo) labnCrop->CopyFrom(laboCrop); if (params.locallab.enabled && !params.locallab.spots.empty()) { - const std::unique_ptr reservCrop(new LabImage(laboCrop->W, laboCrop->H)); - reservCrop->CopyFrom(laboCrop); - const std::unique_ptr lastorigCrop(new LabImage(laboCrop->W, laboCrop->H)); - lastorigCrop->CopyFrom(laboCrop); + const std::unique_ptr reservCrop(new LabImage(*laboCrop, true)); + const std::unique_ptr lastorigCrop(new LabImage(*laboCrop, true)); auto& lllocalcurve2 = parent->lllocalcurve; auto& cllocalcurve2 = parent->cllocalcurve; auto& lclocalcurve2 = parent->lclocalcurve; @@ -1235,19 +1233,17 @@ void Crop::update(int todo) if (WaveParams.softrad > 0.f) { - provradius = new LabImage(labnCrop->W, labnCrop->H); - provradius->CopyFrom(labnCrop); + provradius = new LabImage(*labnCrop, true); } if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { - unshar = new LabImage(labnCrop->W, labnCrop->H); provis = params.wavelet.CLmethod; params.wavelet.CLmethod = "all"; parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); - unshar->CopyFrom(labnCrop); + unshar = new LabImage(*labnCrop, true); params.wavelet.CLmethod = provis; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 386de6310..2839f483f 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1040,10 +1040,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) * 2017 2018 Jacques Desmis * 2019 Pierre Cabrera */ - const std::unique_ptr reserv(new LabImage(oprevl->W, oprevl->H)); - reserv->CopyFrom(oprevl); - const std::unique_ptr lastorigimp(new LabImage(oprevl->W, oprevl->H)); - lastorigimp->CopyFrom(oprevl); + const std::unique_ptr reserv(new LabImage(*oprevl, true)); + const std::unique_ptr lastorigimp(new LabImage(*oprevl, true)); float **shbuffer = nullptr; int sca = 1; double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; @@ -1287,20 +1285,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } if (WaveParams.softrad > 0.f) { - provradius = new LabImage(pW, pH); - provradius->CopyFrom(nprevl); + provradius = new LabImage(*nprevl, true); } - - - if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { - unshar = new LabImage(pW, pH); provis = params->wavelet.CLmethod; params->wavelet.CLmethod = "all"; ipf.ip_wavelet(nprevl, nprevl, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, scale); - - unshar->CopyFrom(nprevl); + unshar = new LabImage(*nprevl, true); params->wavelet.CLmethod = provis; diff --git a/rtengine/labimage.cc b/rtengine/labimage.cc index 319103d64..68d3857e5 100644 --- a/rtengine/labimage.cc +++ b/rtengine/labimage.cc @@ -32,12 +32,18 @@ LabImage::LabImage (int w, int h, bool initZero, bool multiThread) : W(w), H(h) } } +LabImage::LabImage (const LabImage& source, bool multiThread) : W(source.W), H(source.H) +{ + allocLab(W, H); + CopyFrom(&source, multiThread); +} + LabImage::~LabImage () { deleteLab(); } -void LabImage::CopyFrom(LabImage *Img, bool multiThread) +void LabImage::CopyFrom(const LabImage *Img, bool multiThread) { #ifdef _OPENMP #pragma omp parallel sections if(multiThread) @@ -54,7 +60,7 @@ void LabImage::CopyFrom(LabImage *Img, bool multiThread) #endif } -void LabImage::getPipetteData (float &v1, float &v2, float &v3, int posX, int posY, int squareSize) +void LabImage::getPipetteData (float &v1, float &v2, float &v3, int posX, int posY, int squareSize) const { float accumulator_L = 0.f; float accumulator_a = 0.f; diff --git a/rtengine/labimage.h b/rtengine/labimage.h index b4b974c29..4d1c05aed 100644 --- a/rtengine/labimage.h +++ b/rtengine/labimage.h @@ -36,11 +36,12 @@ public: float** b; LabImage (int w, int h, bool initZero = false, bool multiThread = true); + LabImage (const LabImage& source, bool multiThread); ~LabImage (); //Copies image data in Img into this instance. - void CopyFrom(LabImage *Img, bool multiThread = true); - void getPipetteData (float &L, float &a, float &b, int posX, int posY, int squareSize); + void CopyFrom(const LabImage *Img, bool multiThread = true); + void getPipetteData (float &L, float &a, float &b, int posX, int posY, int squareSize) const; void deleteLab(); void reallocLab(); void clear(bool multiThread = false); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 6ab39b771..fc5c78997 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1081,10 +1081,8 @@ private: if (params.locallab.enabled && params.locallab.spots.size() > 0) { MyTime t1, t2; t1.set(); - const std::unique_ptr reservView(new LabImage(fw, fh)); - reservView->CopyFrom(labView); - const std::unique_ptr lastorigView(new LabImage(fw, fh)); - lastorigView->CopyFrom(labView); + const std::unique_ptr reservView(new LabImage(*labView, true)); + const std::unique_ptr lastorigView(new LabImage(*labView, true)); LocretigainCurve locRETgainCurve; LocretitransCurve locRETtransCurve; LocLHCurve loclhCurve; @@ -1370,8 +1368,7 @@ private: } */ if (WaveParams.softrad > 0.f) { - provradius = new LabImage(fw, fh); - provradius->CopyFrom(labView); + provradius = new LabImage(*labView, true); } params.wavelet.getCurves(wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL); @@ -1379,11 +1376,10 @@ private: CurveFactory::curveWavContL(wavcontlutili, params.wavelet.wavclCurve, wavclCurve,/* hist16C, dummy,*/ 1); if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { - unshar = new LabImage(fw, fh); provis = params.wavelet.CLmethod; params.wavelet.CLmethod = "all"; ipf.ip_wavelet(labView, labView, 2, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, 1); - unshar->CopyFrom(labView); + unshar = new LabImage(*labView, true); params.wavelet.CLmethod = provis; WaveParams.expcontrast = false; From 466099b68bfe4c86e981d4f029ae9802e1563fcd Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 27 Jun 2020 17:11:49 +0200 Subject: [PATCH 055/114] change blendmask blendmaskab from int to double --- rtengine/procparams.cc | 4 ++-- rtengine/procparams.h | 4 ++-- rtgui/locallabtools2.cc | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 689f4dacf..6b67726a0 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3738,8 +3738,8 @@ LocallabParams::LocallabSpot::LocallabSpot() : complexmask(0), expmask(false), sensimask(60), - blendmask(-10), - blendmaskab(-10), + blendmask(-10.), + blendmaskab(-10.), softradiusmask(1.0), enamask(false), fftmask(true), diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 1d08049cd..a82e87dd1 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1426,8 +1426,8 @@ struct LocallabParams { int complexmask; bool expmask; int sensimask; - int blendmask; - int blendmaskab; + double blendmask; + double blendmaskab; double softradiusmask; bool enamask; bool fftmask; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index fee80a664..f2f17917e 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4731,8 +4731,8 @@ LocallabMask::LocallabMask(): // Comon mask specific widgets sensimask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), - blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASK"), -100, 100, 1, -10))), - blendmaskab(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASKAB"), -100, 100, 1, -10))), + blendmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASK"), -100., 100., 0.1, -10.))), + blendmaskab(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKMASKAB"), -100., 100., 0.1, -10.))), softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 1.))), showmask_Method(Gtk::manage(new MyComboBoxText())), @@ -5059,8 +5059,8 @@ void LocallabMask::write(rtengine::procparams::ProcParams* pp, ParamsEdited* ped spot.complexmask = complexity->get_active_row_number(); spot.sensimask = sensimask->getIntValue(); - spot.blendmask = blendmask->getIntValue(); - spot.blendmaskab = blendmaskab->getIntValue(); + spot.blendmask = blendmask->getValue(); + spot.blendmaskab = blendmaskab->getValue(); spot.softradiusmask = softradiusmask->getValue(); spot.enamask = enamask->get_active(); spot.CCmask_curve = CCmask_shape->getCurve(); @@ -5117,8 +5117,8 @@ void LocallabMask::setDefaults(const rtengine::procparams::ProcParams* defParams sensimask->setDefault((double)defSpot.sensimask); strumaskmask->setDefault(defSpot.strumaskmask); toolmask->set_active(defSpot.toolmask); - blendmask->setDefault((double)defSpot.blendmask); - blendmaskab->setDefault((double)defSpot.blendmaskab); + blendmask->setDefault(defSpot.blendmask); + blendmaskab->setDefault(defSpot.blendmaskab); softradiusmask->setDefault((double)defSpot.softradiusmask); radmask->setDefault(defSpot.radmask); lapmask->setDefault(defSpot.lapmask); From bad0c0b1e72d040a99b99b06e6e1fdb4f8b78c23 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 28 Jun 2020 13:24:00 +0200 Subject: [PATCH 056/114] Some cppcheck cleanups --- rtengine/curves.h | 4 ++-- rtengine/improcfun.h | 6 ------ rtengine/simpleprocess.cc | 12 ++++++------ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/rtengine/curves.h b/rtengine/curves.h index 71875fa94..5769cada1 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -485,7 +485,7 @@ protected: void NURBS_set(); public: - DiagonalCurve(const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); + explicit DiagonalCurve(const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); ~DiagonalCurve() override; double getVal(double t) const override; @@ -510,7 +510,7 @@ private: public: - FlatCurve(const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); + explicit FlatCurve(const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); ~FlatCurve() override; double getVal(double t) const override; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 3af1e0ec6..57c8c4c64 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -110,12 +110,6 @@ class ImProcFunctions double scale; bool multiThread; - bool lastcutpast; - int lastcxbuf; - int lastcybuf; - int lastcount; - LabImage *spotbuffer; - void calcVignettingParams(int oW, int oH, const procparams::VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul); void transformLuminanceOnly(Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index bf382f3d9..8eae9daba 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1226,14 +1226,14 @@ private: //provisory double ecomp = params.locallab.spots.at(sp).expcomp; - double black = params.locallab.spots.at(sp).black; - double hlcompr = params.locallab.spots.at(sp).hlcompr; - double hlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh; + double lblack = params.locallab.spots.at(sp).black; + double lhlcompr = params.locallab.spots.at(sp).hlcompr; + double lhlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh; double shcompr = params.locallab.spots.at(sp).shcompr; double br = params.locallab.spots.at(sp).lightness; double cont = params.locallab.spots.at(sp).contrast; - if(black < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) { - black *= 1.5; + if (lblack < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) { + lblack *= 1.5; } // Reference parameters computation @@ -1245,7 +1245,7 @@ private: } else { ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); } - CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, + CurveFactory::complexCurvelocal(ecomp, lblack / 65535., lhlcompr, lhlcomprthresh, shcompr, br, cont, lumare, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avge, 1); float minCD; From 192afd24265a2b4ac3045df0ff8be8f1c725d862 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 28 Jun 2020 13:56:03 +0200 Subject: [PATCH 057/114] iplocallab.cc: deltaEforLaplace(): reduce memory usage --- rtengine/iplocallab.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 2656766fd..2dd3210aa 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -249,20 +249,20 @@ float calcreducdE(float dE, float maxdE, float mindE, float maxdElim, float mind void deltaEforLaplace(float *dE, const float lap, int bfw, int bfh, rtengine::LabImage* bufexporig, const float hueref, const float chromaref, const float lumaref) { - const float refa = chromaref * cos(hueref); - const float refb = chromaref * sin(hueref); + const float refa = chromaref * std::cos(hueref); + const float refb = chromaref * std::sin(hueref); const float refL = lumaref; float maxdE = 5.f + MAXSCOPE * lap; - std::unique_ptr dEforLaplace(new float [bfw * bfh]); float maxC = std::sqrt((rtengine::SQR(refa - bufexporig->a[0][0]) + rtengine::SQR(refb - bufexporig->b[0][0])) + rtengine::SQR(refL - bufexporig->L[0][0])) / 327.68f; #ifdef _OPENMP #pragma omp parallel for reduction(max:maxC) #endif for (int y = 0; y < bfh; y++) { for (int x = 0; x < bfw; x++) { - dEforLaplace[y * bfw + x] = std::sqrt((rtengine::SQR(refa - bufexporig->a[y][x]) + rtengine::SQR(refb - bufexporig->b[y][x])) + rtengine::SQR(refL - bufexporig->L[y][x])) / 327.68f; - maxC = rtengine::max(maxC, dEforLaplace[y * bfw + x]); + const float val = std::sqrt((rtengine::SQR(refa - bufexporig->a[y][x]) + rtengine::SQR(refb - bufexporig->b[y][x])) + rtengine::SQR(refL - bufexporig->L[y][x])) / 327.68f; + dE[y * bfw + x] = val; + maxC = rtengine::max(maxC, val); } } @@ -278,7 +278,7 @@ void deltaEforLaplace(float *dE, const float lap, int bfw, int bfh, rtengine::La #endif for (int y = 0; y < bfh; y++) { for (int x = 0; x < bfw; x++) { - dE[y * bfw + x] = dEforLaplace[y * bfw + x] >= maxdE ? ade * dEforLaplace[y * bfw + x] + bde : 1.f; + dE[y * bfw + x] = dE[y * bfw + x] >= maxdE ? ade * dE[y * bfw + x] + bde : 1.f; } } } From 4010b4a336b978f1e9171d2d2f239cb7a011e24b Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 28 Jun 2020 13:59:52 +0200 Subject: [PATCH 058/114] removed a stopwatch --- rtengine/iplocallab.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 2dd3210aa..4ce51f1f1 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7276,7 +7276,6 @@ BENCHFUN int H_Lm = wdspot->level_H(maxlvl - 1); if (lp.strwav != 0.f && lp.wavgradl) { -StopWatch Stop1("test"); array2D factorwav(W_Lm, H_Lm); calclocalGradientParams(lp, gpwav, 0, 0, W_Lm, H_Lm, 10); const float mult = lp.strwav < 0.f ? -1.f : 1.f; From bfdd936f677341d4f0a373ccfc939eb635da0d65 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 29 Jun 2020 08:05:44 +0200 Subject: [PATCH 059/114] Setlogscale for blendmask and blendmaskab --- rtgui/locallabtools2.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index f2f17917e..fbc05d2c9 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -4773,6 +4773,8 @@ LocallabMask::LocallabMask(): const LocallabParams::LocallabSpot defSpot; sensimask->setAdjusterListener(this); + blendmask->setLogScale(10, 0); + blendmaskab->setLogScale(10, 0); blendmask->setAdjusterListener(this); blendmaskab->setAdjusterListener(this); // softradiusmask->setLogScale(10, -10); From 5998a25a333c3464ac7fa44872dd725c4d30a409 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 29 Jun 2020 16:22:30 +0200 Subject: [PATCH 060/114] Enable exposure when shadows slider different zero --- rtengine/iplocallab.cc | 15 ++++++++++----- rtgui/locallabtools.cc | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 4ce51f1f1..d90d5b590 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2420,7 +2420,7 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* b const float hlcompthr = lp.hlcompthr / 200.f; const float hlcomp = lp.hlcomp / 100.f; if (lp.linear > 0.f && lp.expcomp == 0.f) { - lp.expcomp = 0.01f; + lp.expcomp = 0.001f; } if (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex) { @@ -12920,7 +12920,7 @@ void ImProcFunctions::Lab_Local( enablefat = true;; } - bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); + bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.shadex > 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); if (!lp.invex && execex) { int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); @@ -13085,7 +13085,7 @@ void ImProcFunctions::Lab_Local( } if (lp.expcomp == 0.f) { - lp.expcomp = 0.011f; // to enabled + lp.expcomp = 0.001f; // to enabled } ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); @@ -13211,16 +13211,21 @@ void ImProcFunctions::Lab_Local( } } } + if (lp.shadex > 0) { + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + } //shadows with ipshadowshighlight - if ((lp.expcomp != 0.f && lp.expcomp != 0.01f) || (exlocalcurve && localexutili)) { + if ((lp.expcomp > 0.f) || (exlocalcurve && localexutili)) { if (lp.shadex > 0) { ImProcFunctions::shadowsHighlights(bufexpfin.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); } } if (lp.expchroma != 0.f) { - if ((lp.expcomp != 0.f && lp.expcomp != 0.01f) || (exlocalcurve && localexutili) || lp.laplacexp > 0.1f) { + if ((lp.expcomp != 0.f && lp.expcomp != 0.001f) || (exlocalcurve && localexutili) || lp.laplacexp > 0.1f) { constexpr float ampli = 70.f; const float ch = (1.f + 0.02f * lp.expchroma); const float chprosl = ch <= 1.f ? 99.f * ch - 99.f : clipChro(ampli * ch - ampli); diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 0242e0a89..81b70d7b9 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -2189,7 +2189,7 @@ LocallabExposure::LocallabExposure(): structexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUCCOL"), 0, 100, 1, 0))), blurexpde(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURDE"), 2, 100, 1, 5))), exptoolexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPTOOL")))), - expcomp(Gtk::manage(new Adjuster(M("TP_EXPOSURE_EXPCOMP"), MINEXP, MAXEXP, 0.02, 0.))), + expcomp(Gtk::manage(new Adjuster(M("TP_EXPOSURE_EXPCOMP"), MINEXP, MAXEXP, 0.01, 0.))), black(Gtk::manage(new Adjuster(M("TP_EXPOSURE_BLACKLEVEL"), -16384, 32768, 10, 0))), hlcompr(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTS"), 0, 500, 1, 0))), hlcomprthresh(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD"), 0, 100, 1, 0))), From 7a3958e9760fe840f27cc6335be8986d6af1189b Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 30 Jun 2020 06:36:19 +0200 Subject: [PATCH 061/114] Small change to enable shadows --- rtengine/iplocallab.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d90d5b590..d0934dceb 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -13218,7 +13218,7 @@ void ImProcFunctions::Lab_Local( } //shadows with ipshadowshighlight - if ((lp.expcomp > 0.f) || (exlocalcurve && localexutili)) { + if ((lp.expcomp != 0.f) || (exlocalcurve && localexutili)) { if (lp.shadex > 0) { ImProcFunctions::shadowsHighlights(bufexpfin.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); } From 2ac10c0502545ec4038aa1d3b1c0da3ad26e2237 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 30 Jun 2020 08:45:15 +0200 Subject: [PATCH 062/114] Some chnages to exposure : hlcomp softradius --- rtengine/iplocallab.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d0934dceb..d69be9de8 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -12920,7 +12920,7 @@ void ImProcFunctions::Lab_Local( enablefat = true;; } - bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.shadex > 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); + bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.shadex > 0 || lp.hlcomp > 0.f || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); if (!lp.invex && execex) { int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); @@ -12942,6 +12942,7 @@ void ImProcFunctions::Lab_Local( const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + const std::unique_ptr buforig(new LabImage(bfw, bfh)); std::unique_ptr bufmaskblurexp; std::unique_ptr originalmaskexp; @@ -12960,6 +12961,7 @@ void ImProcFunctions::Lab_Local( for (int y = ystart; y < yend; y++) { for (int x = xstart; x < xend; x++) { bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + buforig->L[y - ystart][x - xstart] = original->L[y][x]; } } @@ -13217,6 +13219,12 @@ void ImProcFunctions::Lab_Local( } } + if (lp.hlcomp > 0.f) { + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + } + //shadows with ipshadowshighlight if ((lp.expcomp != 0.f) || (exlocalcurve && localexutili)) { if (lp.shadex > 0) { @@ -13244,7 +13252,7 @@ void ImProcFunctions::Lab_Local( } if (lp.softradiusexp > 0.f && lp.expmet == 0) { - softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); + softproc(buforig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.1, 0.001, 0.5f, sk, multiThread, 1); } float meansob = 0.f; transit_shapedetect2(call, 1, bufexporig.get(), bufexpfin.get(), originalmaskexp.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); From 39d185a586f454584261dfa6180648ef12532330 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 30 Jun 2020 11:19:35 +0200 Subject: [PATCH 063/114] Improvment to exposure inverse and tooltips --- rtdata/languages/default | 1 + rtengine/iplocallab.cc | 23 +++++++++++++++++++++-- rtgui/locallabtools.cc | 29 +++++------------------------ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index f68a97b39..18dc589ca 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2480,6 +2480,7 @@ TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), TP_LOCALLAB_EXPCHROMA;Chroma compensation TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE weakening'\nIncrease 'Balance ΔE ab-L' TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_EXPCURV;Curves diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d69be9de8..9b982cd84 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -1351,7 +1351,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall float proexp = lp.expcomp; if (std::fabs(proexp) < 0.6f) { float interm = std::fabs(proexp) / 0.6f; - interm = SQR(interm); + interm = pow(interm, 3.f); lp.expcomp = proexp * interm; } lp.expchroma = locallab.spots.at(sp).expchroma / 100.; @@ -5267,6 +5267,12 @@ void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp } } + if ((lp.expcomp != 0.f) || (exlocalcurve)) { + if (lp.shadex > 0) { + ImProcFunctions::shadowsHighlights(temp.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); + } + } + if (lp.expchroma != 0.f) { const float ch = (1.f + 0.02f * lp.expchroma) ; float chprosl; @@ -13268,7 +13274,7 @@ void ImProcFunctions::Lab_Local( } //inverse - else if (lp.invex && (lp.expcomp != 0.0 || lp.laplacexp > 0.1f || params->locallab.spots.at(sp).fatamount > 1.f || (exlocalcurve && localexutili) || lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) && lp.exposena) { + else if (lp.invex && (lp.expcomp != 0.0 || lp.laplacexp > 0.1f || lp.blac != 0 || lp.hlcomp > 0.f || lp.shadex > 0 || params->locallab.spots.at(sp).fatamount > 1.f || (exlocalcurve && localexutili) || lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) && lp.exposena) { constexpr float adjustr = 2.f; std::unique_ptr bufmaskblurexp; std::unique_ptr originalmaskexp; @@ -13333,6 +13339,19 @@ void ImProcFunctions::Lab_Local( return; } + if (lp.shadex > 0) { + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + } + + + if (lp.hlcomp > 0.f) { + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + } + InverseColorLight_Local(false, false, sp, 1, lp, originalmaskexp.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); if (params->locallab.spots.at(sp).recurs) { diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 81b70d7b9..1dd5513da 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -2266,6 +2266,8 @@ LocallabExposure::LocallabExposure(): setExpandAlignProperties(exptoolexp, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + // expcomp->setLogScale(10, 0); + expcomp->setAdjusterListener(this); black->setAdjusterListener(this); @@ -2467,6 +2469,7 @@ void LocallabExposure::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { exp->set_tooltip_text(M("TP_LOCALLAB_EXPOSURE_TOOLTIP")); + expcomp->set_tooltip_text(M("TP_LOCALLAB_EXPCOMP_TOOLTIP")); expMethod->set_tooltip_text(M("TP_LOCALLAB_EXPMETHOD_TOOLTIP")); structexp->set_tooltip_text(M("TP_LOCALLAB_STRUCT_TOOLTIP")); pdeFrame->set_tooltip_text(M("TP_LOCALLAB_PDEFRAME_TOOLTIP")); @@ -2492,29 +2495,6 @@ void LocallabExposure::updateAdviceTooltips(const bool showTooltips) expchroma->set_tooltip_text(M("TP_LOCALLAB_EXPCHROMA_TOOLTIP")); } else { exp->set_tooltip_text(""); - expMethod->set_tooltip_text(""); - structexp->set_tooltip_text(""); - pdeFrame->set_tooltip_text(""); - exnoiseMethod->set_tooltip_text(""); - laplacexp->set_tooltip_text(M("")); - balanexp->set_tooltip_text(M("")); - gamm->set_tooltip_text(M("")); - linear->set_tooltip_text(M("")); - fatFrame->set_tooltip_text(""); - sensiex->set_tooltip_text(""); - shapeexpos->setTooltip(""); - strexp->set_tooltip_text(""); - expmaskexp->set_tooltip_text(""); - CCmaskexpshape->setTooltip(""); - LLmaskexpshape->setTooltip(""); - HHmaskexpshape->setTooltip(""); - radmaskexp->set_tooltip_text(""); - lapmaskexp->set_tooltip_text(""); - strmaskexp->set_tooltip_text(""); - Lmaskexpshape->setTooltip(""); - blendmaskexp->set_tooltip_text(M("")); - mask2expCurveEditorG->set_tooltip_text(M("")); - expchroma->set_tooltip_text(M("")); } } @@ -3231,7 +3211,8 @@ void LocallabExposure::updateExposureGUI3() } structexp->hide(); - shadex->hide(); + // shadex->hide(); + shadex->show(); softradiusexp->hide(); expgradexp->hide(); showmaskexpMethod->hide(); From 15c4f7c2f81d73dc8014ceb831ee19a27af2fa9c Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Tue, 30 Jun 2020 15:29:21 +0200 Subject: [PATCH 064/114] First mockup to improve pyramids --- rtengine/LUT.h | 33 +++++++++++++++++++++++++++++++++ rtengine/iplocallab.cc | 30 ++++-------------------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/rtengine/LUT.h b/rtengine/LUT.h index 6ba7d570f..933fdba3a 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -61,6 +61,7 @@ #include #include #include +#include #ifndef NDEBUG #include @@ -138,6 +139,38 @@ public: clear(); } } + + LUT(const std::vector input, int flags = LUT_CLIP_BELOW | LUT_CLIP_ABOVE) + { +#ifndef NDEBUG + + if (input.size() <= 0) { + printf("s<=0!\n"); + } + + assert (input.size() > 0); +#endif + dirty = true; + clip = flags; + // Add a few extra elements so [](vfloat) won't access out-of-bounds memory. + // The routine would still produce the right answer, but might cause issues + // with address/heap checking programs. + data = new T[input.size() + 3]; + owner = 1; + size = input.size(); + upperBound = size - 1; + maxs = size - 2; + maxsf = (float)maxs; +#ifdef __SSE2__ + maxsv = F2V( maxs ); + sizeiv = _mm_set1_epi32( (int)(size - 1) ); + sizev = F2V( size - 1 ); +#endif + for (size_t i = 0; i < input.size(); ++i) { + data[i] = input[i]; + } + } + void operator ()(int s, int flags = LUT_CLIP_BELOW | LUT_CLIP_ABOVE, bool initZero = false) { #ifndef NDEBUG diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d0934dceb..cb21ac353 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7010,6 +7010,7 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); if (process == 1 && loclevwavCurve && loclevwavutili) { //blur + StopWatch Stop1("blur"); array2D templevel(W_L, H_L); for (int dir = 1; dir < 4; ++dir) { for (int level = level_bl; level < maxlvl; ++level) { @@ -7018,36 +7019,13 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel constexpr float offs = 1.f; float mea[10]; calceffect(level, mean, sigma, mea, effect, offs); - + LUTf meaLut({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); + const float lutFactor = (meaLut.getSize() - 1) / mea[9]; #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(WavL[co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.5f; - } else if (WavCL < mea[7]) { - beta[co] = 0.3f; - } else if (WavCL < mea[8]) { - beta[co] = 0.2f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.1f; - } else { - beta[co] = 0.05f; - } + beta[co] = meaLut[std::fabs(WavL[co]) * lutFactor]; } const float klev = 0.25f * loclevwavCurve[level * 55.5f]; From 0ce8c04ad102e6358cb098a4d3d1fafa90a0ff94 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 1 Jul 2020 17:47:00 +0200 Subject: [PATCH 065/114] Next try to improve pyramids --- rtengine/iplocallab.cc | 33 +- rtengine/iplocallab.cc.save-failed | 14814 +++++++++++++++++++++++++++ 2 files changed, 14845 insertions(+), 2 deletions(-) create mode 100644 rtengine/iplocallab.cc.save-failed diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index cb21ac353..c07bdee61 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7019,8 +7019,37 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel constexpr float offs = 1.f; float mea[10]; calceffect(level, mean, sigma, mea, effect, offs); - LUTf meaLut({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); - const float lutFactor = (meaLut.getSize() - 1) / mea[9]; + constexpr int lutSize = 100; + const float lutMax = ceil(mea[9]); + const float lutDiff = lutMax / lutSize; + std::vector lutVals(lutSize); + std::vector inVals({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); + int jStart = 1; + for (int i = 0; i < 100; ++i) { + const float val = i * lutDiff; + if (val < mea[0]) { + // still < first value => no interpolation + lutVals[i] = inVals[0]; + } else { + for (int j = jStart; j < 10; ++j) { + if (val == mea[j]) { + // exact match => no interpolation + lutVals[i] = inVals[j]; + ++jStart; + break; + } else if (val < mea[j]) { + // interpolate + const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); + lutVals[i] = intp(dist, inVals[j], inVals[j - 1]); + break; + } else { + lutVals[i] = inVals[10]; + } + } + } + } + const LUTf meaLut(lutVals); + const float lutFactor = 1.f / lutDiff; #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif diff --git a/rtengine/iplocallab.cc.save-failed b/rtengine/iplocallab.cc.save-failed new file mode 100644 index 000000000..e0dcd1659 --- /dev/null +++ b/rtengine/iplocallab.cc.save-failed @@ -0,0 +1,14814 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * + * 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 . + * 2016 - 2020 Jacques Desmis + * 2016 - 2020 Ingo Weyrich + + */ +#include +#include + +#include "improcfun.h" +#include "colortemp.h" +#include "curves.h" +#include "gauss.h" +#include "iccstore.h" +#include "imagefloat.h" +#include "labimage.h" +#include "color.h" +#include "rt_math.h" +#include "jaggedarray.h" +#include "rt_algo.h" +#include "settings.h" +#include "../rtgui/options.h" + +#include "utils.h" +#ifdef _OPENMP +#include +#endif +#include "../rtgui/thresholdselector.h" +#include "imagesource.h" + +#include "cplx_wavelet_dec.h" +#include "ciecam02.h" + +#define BENCHMARK +#include "StopWatch.h" +#include "guidedfilter.h" + + +#pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wextra" + +namespace +{ +constexpr int limscope = 80; +constexpr int mSPsharp = 39; //minimum size Spot Sharp due to buildblendmask +constexpr int mSPwav = 32; //minimum size Spot Wavelet +constexpr int mDEN = 64; //minimum size Spot Denoise +constexpr int mSP = 5; //minimum size Spot +constexpr float MAXSCOPE = 1.25f; +constexpr float MINSCOPE = 0.025f; +constexpr int TS = 64; // Tile size +constexpr float epsilonw = 0.001f / (TS * TS); //tolerance +constexpr int offset = 25; // shift between tiles + +constexpr float clipLoc(float x) { + return rtengine::LIM(x, 0.f, 32767.f); +} + +constexpr float clipDE(float x) { + return rtengine::LIM(x, 0.3f, 1.f); +} + +constexpr float clipC(float x) { + return rtengine::LIM(x, -42000.f, 42000.f); +} + +constexpr float clipChro(float x) { + return rtengine::LIM(x, 0.f, 140.f); +} + +float softlig(float a, float b, float minc, float maxc) +{ + // as Photoshop + if (2.f * b <= maxc - minc) { + return a * (2.f * b + a * (maxc - 2.f * b)); + } else { + return 2.f * a * (maxc - b) + std::sqrt(rtengine::LIM(a, 0.f, 2.f)) * (2.f * b - maxc); + } +} + +float softlig3(float a, float b) +{ + // as w3C + if (2.f * b <= 1.f) { + return a - (1.f - 2.f * b) * a * (1.f - a); + } else { + if (4.f * a <= 1.f) { + return a + (2.f * b - 1.f) * (4.f * a * (4.f * a + 1.f) * (a - 1.f) + 7.f * a); + } else { + return a + (2.f * a - 1.f) * (std::sqrt(a) - a); + } + } +} + +float softlig2(float a, float b) +{ + // illusions.hu + return pow_F(b, pow_F(2.f, (2.f * (0.5f - a)))); +} + +constexpr float colburn(float a, float b) +{ + // w3C + return b == 0.f ? 0.f : 1.f - rtengine::min(1.f, (1.f - a) / b); +} + +constexpr float coldodge(float a, float b) +{ + // w3C + return b == 1.f ? 1.f : rtengine::min(1.f, a / (1.f - b)); +} + +float overlay(float a, float b, float minc, float maxc) +{ + if (2.f * b <= maxc - minc) { + return 2.f * b * a; + } else { + return maxc - 2.f * (1.f - a) * (maxc - b); + } +} + +constexpr float screen(float a, float b, float maxc) +{ + return 1.f - (1.f - a) * (maxc - b); +} + +constexpr float exclusion(float a, float b) +{ + return a + b - 2.f * a * b; +} + +void calcGammaLut(double gamma, double ts, LUTf &gammaLut) +{ + double pwr = 1.0 / gamma; + double gamm = gamma; + const double gamm2 = gamma; + rtengine::GammaValues g_a; + + if (gamm2 < 1.0) { + std::swap(pwr, gamm); + } + + rtengine::Color::calcGamma(pwr, ts, 0, g_a); // call to calcGamma with selected gamma and slope + + const double start = gamm2 < 1. ? g_a[2] : g_a[3]; + const double add = g_a[4]; + const double mul = 1.0 + g_a[4]; + + if (gamm2 < 1.) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::igammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } else { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::gammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } +} + +float calcLocalFactor(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) +{ + //ellipse x2/a2 + y2/b2=1 + //transition ellipsoidal + const float kelip = dx / dy; + const float belip = rtengine::max(0.0001f, std::sqrt((rtengine::SQR((lox - lcx) / kelip) + rtengine::SQR(loy - lcy)))); //determine position ellipse ==> a and b + + //gradient allows differentiation between transition x and y + const float rapy = std::fabs((loy - lcy) / belip); + const float aelip = belip * kelip; + const float degrad = aelip / dx; + const float gradreal = gradient * rapy + 1.f; + const float ap = rtengine::RT_PI_F / (1.f - ach); + const float bp = rtengine::RT_PI_F - ap; + return pow(0.5f * (1.f + xcosf(degrad * ap + bp)), rtengine::SQR(gradreal)); // trigo cos transition +} + +float calcLocalFactorrect(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) +{ + constexpr float eps = 0.0001f; + const float krap = std::fabs(dx / dy); + const float kx = lox - lcx; + const float ky = loy - lcy; + + float ref; + //gradient allows differentiation between transition x and y + if (std::fabs(kx / (ky + eps)) < krap) { + ref = std::sqrt(rtengine::SQR(dy) * (1.f + rtengine::SQR(kx / (ky + eps)))); + } else { + ref = std::sqrt(rtengine::SQR(dx) * (1.f + rtengine::SQR(ky / (kx + eps)))); + } + + const float rad = rtengine::max(eps, std::sqrt(rtengine::SQR(kx) + rtengine::SQR(ky))); + const float rapy = std::fabs((loy - lcy) / rad); + const float gradreal = gradient * rapy + 1.f; + + const float coef = rad / ref; + const float fact = (coef - 1.f) / (ach - 1.f); + return pow(fact, rtengine::SQR(gradreal)); +} + +float calcreducdE(float dE, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, int limscope, int scope) +{ + if (scope > limscope) {//80 arbitrary value, if we change we must change limscope + if (dE > maxdElim) { + return 0.f; + } else if (dE > mindElim) { + const float reducdElim = std::pow((dE - maxdElim) / (mindElim - maxdElim), iterat); + const float aalim = (1.f - reducdElim) / 20.f; + const float bblim = 1.f - 100.f * aalim; + return aalim * scope + bblim; + } else { + return 1.f; + } + } else { + if (dE > maxdE) { + return 0.f; + } else if (dE > mindE) { + return std::pow((dE - maxdE) / (mindE - maxdE), iterat); + } else { + return 1.f; + } + } +} + +void deltaEforLaplace(float *dE, const float lap, int bfw, int bfh, rtengine::LabImage* bufexporig, const float hueref, const float chromaref, const float lumaref) +{ + + const float refa = chromaref * std::cos(hueref); + const float refb = chromaref * std::sin(hueref); + const float refL = lumaref; + float maxdE = 5.f + MAXSCOPE * lap; + + float maxC = std::sqrt((rtengine::SQR(refa - bufexporig->a[0][0]) + rtengine::SQR(refb - bufexporig->b[0][0])) + rtengine::SQR(refL - bufexporig->L[0][0])) / 327.68f; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + const float val = std::sqrt((rtengine::SQR(refa - bufexporig->a[y][x]) + rtengine::SQR(refb - bufexporig->b[y][x])) + rtengine::SQR(refL - bufexporig->L[y][x])) / 327.68f; + dE[y * bfw + x] = val; + maxC = rtengine::max(maxC, val); + } + } + + if (maxdE > maxC) { + maxdE = maxC - 1.f; + } + + const float ade = 1.f / (maxdE - maxC); + const float bde = -ade * maxC; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + dE[y * bfw + x] = dE[y * bfw + x] >= maxdE ? ade * dE[y * bfw + x] + bde : 1.f; + } + } +} + +float calclight(float lum, const LUTf &lightCurveloc) +{ + return clipLoc(lightCurveloc[lum]); +} + +float calclightinv(float lum, float koef, const LUTf &lightCurveloc) +{ + return koef != -100.f ? clipLoc(lightCurveloc[lum]) : 0.f; +} + +float balancedeltaE(float kL) +{ + constexpr float mincurs = 0.3f; // minimum slider balan_ + constexpr float maxcurs = 1.7f; // maximum slider balan_ + constexpr float maxkab = 1.35; // 0.5 * (3 - 0.3) + constexpr float minkab = 0.65; // 0.5 * (3 - 1.7) + constexpr float abal = (maxkab - minkab) / (mincurs - maxcurs); + constexpr float bbal = maxkab - mincurs * abal; + return abal * kL + bbal; +} + +void SobelCannyLuma(float **sobelL, float **luma, int bfw, int bfh, float radius) +{ + // base of the process to detect shape in complement of deltaE + // use for calculate Spot reference + // and for structure of the shape + // actually , as the program don't use these function, I just create a simple "Canny" near of Sobel. This can be completed after with teta, etc. + array2D tmL(bfw, bfh); + + //inspired from Chen Guanghua Zhang Xiaolong + //Sobel Horizontal + constexpr float GX[3][3] = { + {1.f, 0.f, -1.f}, + {2.f, 0.f, -2.f}, + {1.f, 0.f, -1.f} + }; + + //Sobel Vertical + constexpr float GY[3][3] = { + {1.f, 2.f, 1.f}, + {0.f, 0.f, 0.f}, + {-1.f, -2.f, -1.f} + }; + + if (radius > 0.f) { + gaussianBlur(luma, tmL, bfw, bfh, rtengine::max(radius / 2.f, 0.5f)); + } else { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw ; x++) { + tmL[y][x] = luma[y][x]; + } + } + } + + for (int x = 0; x < bfw; x++) { + sobelL[0][x] = 0.f; + } + for (int y = 1; y < bfh - 1; y++) { + sobelL[y][0] = 0.f; + for (int x = 1; x < bfw - 1; x++) { + float sumXL = 0.f; + float sumYL = 0.f; + for (int i = -1; i < 2; i += 2) { + for (int j = -1; j < 2; j += 1) { + sumXL += GX[j + 1][i + 1] * tmL[y + i][x + j]; + sumYL += GY[j + 1][i + 1] * tmL[y + i][x + j]; + } + } + //Edge strength + //we can add if need teta = atan2 (sumYr, sumXr) + sobelL[y][x] = rtengine::min(std::sqrt(rtengine::SQR(sumXL) + rtengine::SQR(sumYL)), 32767.f); + } + sobelL[y][bfw - 1] = 0.f; + } + for (int x = 0; x < bfw; x++) { + sobelL[bfh - 1][x] = 0.f; + } +} + +} + +namespace rtengine + +{ +extern MyMutex *fftwMutex; + +using namespace procparams; + +struct local_params { + float yc, xc; + float lx, ly; + float lxL, lyT; + float transweak; + float transgrad; + float iterat; + float balance; + float balanceh; + int colorde; + int cir; + float thr; + float stru; + int chro, cont, sens, sensh, senscb, sensbn, senstm, sensex, sensexclu, sensden, senslc, senssf, senshs, senscolor; + float clarityml; + float contresid; + float blurcbdl; + bool deltaem; + float struco; + float strengrid; + float struexc; + float blendmacol; + float radmacol; + float chromacol; + float gammacol; + float slomacol; + float blendmalc; + float radmalc; + float chromalc; + float radmaexp; + float chromaexp; + float gammaexp; + float slomaexp; + float strmaexp; + float angmaexp; + float str_mas; + float ang_mas; + float strexp; + float angexp; + float strSH; + float angSH; + float strcol; + float strcolab; + float strcolh; + float angcol; + float strvib; + float strvibab; + float strvibh; + float angvib; + float angwav; + float strwav; + + float strengthw; + float radiusw; + float detailw; + float gradw; + float tloww; + float thigw; + float edgw; + float basew; + + float anglog; + float strlog; + float softradiusexp; + float softradiuscol; + float softradiuscb; + float softradiusret; + float softradiustm; + float blendmaexp; + float radmaSH; + float blendmaSH; + float chromaSH; + float gammaSH; + float slomaSH; + float radmavib; + float blendmavib; + float chromavib; + float gammavib; + float slomavib; + float radmacb; + float blendmacb; + float chromacbm; + float gammacb; + float slomacb; + float radmatm; + float blendmatm; + float chromatm; + float gammatm; + float slomatm; + + float radmabl; + float blendmabl; + float chromabl; + float gammabl; + float slomabl; + + float struexp; + float blurexp; + float blurcol; + float blurcolmask; + float contcolmask; + float blurSH; + float ligh; + float lowA, lowB, highA, highB; + float lowBmerg, highBmerg, lowAmerg, highAmerg; + int shamo, shdamp, shiter, senssha, sensv; + float neig; + float strng; + float lap; + float lcamount; + double shrad; + double shblurr; + double rad; + double stren; + int it; + int guidb; + float strbl; + float epsb; + float trans; + float feath; + int dehaze; + int depth; + bool inv; + bool invex; + bool invsh; + bool curvact; + bool invrad; + bool invret; + bool equret; + bool equtm; + bool invshar; + bool actsp; + bool ftwlc; + bool ftwreti; + float str; + int qualmet; + int qualcurvemet; + int gridmet; + bool prevdE; + int showmaskcolmet; + int showmaskcolmetinv; + int showmaskexpmet; + int showmaskexpmetinv; + int showmaskSHmet; + int showmaskSHmetinv; + int showmaskvibmet; + int showmasklcmet; + int showmasksharmet; + int showmaskcbmet; + int showmaskretimet; + int showmasksoftmet; + int showmasktmmet; + int showmaskblmet; + int showmask_met; + bool fftbl; + float laplacexp; + float balanexp; + float linear; + int expmet; + int softmet; + int blurmet; + int blmet; + int smasktyp; + int chromet; + int shmeth; + int medmet; + int locmet; + float noiself; + float noiself0; + float noiself2; + float noiseldetail; + int detailthr; + int noiselequal; + float noisechrodetail; + float bilat; + float noiselc; + float noisecf; + float noisecc; + float mulloc[6]; + int mullocsh[5]; + int detailsh; + float threshol; + float chromacb; + float strengt; + float gamm; + float esto; + float scalt; + float rewe; + float amo; + bool colorena; + bool blurena; + bool tonemapena; + bool retiena; + bool sharpena; + bool lcena; + bool sfena; + bool cbdlena; + bool denoiena; + bool expvib; + bool exposena; + bool hsena; + bool vibena; + bool logena; + bool maskena; + bool cut_past; + float past; + float satur; + int blac; + int shcomp; + int shadex; + int hlcomp; + int hlcompthr; + float expcomp; + float expchroma; + int excmet; + int mergemet; + int mergecolMethod; + float opacol; + int war; + float adjch; + int shapmet; + int edgwmet; + int neiwmet; + bool enaColorMask; + bool fftColorMask; + bool enaColorMaskinv; + bool enaExpMask; + bool enaExpMaskinv; + bool enaSHMask; + bool enaSHMaskinv; + bool enavibMask; + bool enalcMask; + bool enasharMask; + bool enacbMask; + bool enaretiMask; + bool enaretiMasktmap; + bool enatmMask; + bool enablMask; + bool ena_Mask; + int highlihs; + int shadowhs; + int radiushs; + int hltonalhs; + int shtonalhs; + int scalereti; + float sourcegray; + float targetgray; + float blackev; + float whiteev; + float detail; + int sensilog; + int sensimas; + bool Autogray; + bool autocompute; + float baselog; + bool wavgradl; + bool edgwena; + bool lip3; + int daubLen; + float sigmadr; + float sigmabl; + float sigmaed; + float sigmalc; + float sigmalc2; + float residsha; + float residshathr; + float residhi; + float residhithr; + bool blwh; + bool fftma; + float blurma; + float contma; + +}; + +static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locallab, struct local_params& lp, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, const LocwavCurve & locwavCurveden, bool locwavdenutili) +{ + int w = oW; + int h = oH; + int circr = locallab.spots.at(sp).circrad; + float streng = ((float)locallab.spots.at(sp).stren); + float gam = ((float)locallab.spots.at(sp).gamma); + float est = ((float)locallab.spots.at(sp).estop); + float scal_tm = ((float)locallab.spots.at(sp).scaltm); + float rewe = ((float)locallab.spots.at(sp).rewei); + float amo = ((float)locallab.spots.at(sp).amount); + float strlight = ((float)locallab.spots.at(sp).streng); + float strucc = locallab.spots.at(sp).struc; + float laplac = ((float)locallab.spots.at(sp).laplace); + float thre = locallab.spots.at(sp).thresh; + + if (thre > 8.f || thre < 0.f) {//to avoid artifacts if user does not clear cache with new settings. Can be suppressed after + thre = 2.f; + } + + double local_x = locallab.spots.at(sp).loc.at(0) / 2000.0; + double local_y = locallab.spots.at(sp).loc.at(2) / 2000.0; + double local_xL = locallab.spots.at(sp).loc.at(1) / 2000.0; + double local_yT = locallab.spots.at(sp).loc.at(3) / 2000.0; + double local_center_x = locallab.spots.at(sp).centerX / 2000.0 + 0.5; + double local_center_y = locallab.spots.at(sp).centerY / 2000.0 + 0.5; + float iterati = (float) locallab.spots.at(sp).iter; + float balanc = (float) locallab.spots.at(sp).balan; + float balanch = (float) locallab.spots.at(sp).balanh; + int colorde = (int) locallab.spots.at(sp).colorde; + + if (iterati > 4.f || iterati < 0.2f) {//to avoid artifacts if user does not clear cache with new settings Can be suppressed after + iterati = 2.f; + } + + float neigh = float (locallab.spots.at(sp).neigh); + float chromaPastel = float (locallab.spots.at(sp).pastels) / 100.0f; + float chromaSatur = float (locallab.spots.at(sp).saturated) / 100.0f; + int local_sensiv = locallab.spots.at(sp).sensiv; + int local_sensiex = locallab.spots.at(sp).sensiex; + + if (locallab.spots.at(sp).qualityMethod == "enh") { + lp.qualmet = 1; + } else if (locallab.spots.at(sp).qualityMethod == "enhden") { + lp.qualmet = 2; + } + + if (locallab.spots.at(sp).qualitycurveMethod == "none") { + lp.qualcurvemet = 0; + } else if (locallab.spots.at(sp).qualitycurveMethod == "std") { + lp.qualcurvemet = 1; + } + + if (locallab.spots.at(sp).gridMethod == "one") { + lp.gridmet = 0; + } else if (locallab.spots.at(sp).gridMethod == "two") { + lp.gridmet = 1; + } + + if (locallab.spots.at(sp).expMethod == "std") { + lp.expmet = 0; + } else if (locallab.spots.at(sp).expMethod == "pde") { + lp.expmet = 1; + } + + if (locallab.spots.at(sp).localcontMethod == "loc") { + lp.locmet = 0; + } else if (locallab.spots.at(sp).localcontMethod == "wav") { + lp.locmet = 1; + } + + lp.laplacexp = locallab.spots.at(sp).laplacexp; + lp.balanexp = locallab.spots.at(sp).balanexp; + lp.linear = locallab.spots.at(sp).linear; + + lp.fftColorMask = locallab.spots.at(sp).fftColorMask; + lp.prevdE = prevDeltaE; + lp.showmaskcolmet = llColorMask; + lp.showmaskcolmetinv = llColorMaskinv; + lp.showmaskexpmet = llExpMask; + lp.showmaskexpmetinv = llExpMaskinv; + lp.showmaskSHmet = llSHMask; + lp.showmaskSHmetinv = llSHMaskinv; + lp.showmaskvibmet = llvibMask; + lp.showmasklcmet = lllcMask; + lp.showmasksharmet = llsharMask; + lp.showmaskcbmet = llcbMask; + lp.showmaskretimet = llretiMask; + lp.showmasksoftmet = llsoftMask; + + lp.showmasktmmet = lltmMask; + lp.showmaskblmet = llblMask; + lp.showmask_met = ll_Mask; + // printf("mask=%i \n", lp.showmask_met); + + lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMask = locallab.spots.at(sp).enaExpMask && llExpMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMaskinv = locallab.spots.at(sp).enaExpMask && llExpMaskinv == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaSHMask = locallab.spots.at(sp).enaSHMask && llSHMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enaSHMaskinv = locallab.spots.at(sp).enaSHMask && llSHMaskinv == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enacbMask = locallab.spots.at(sp).enacbMask && llcbMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enaretiMask = locallab.spots.at(sp).enaretiMask && lllcMask == 0 && llsharMask == 0 && llsoftMask == 0 && llretiMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enatmMask = locallab.spots.at(sp).enatmMask && lltmMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enablMask = locallab.spots.at(sp).enablMask && llblMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.enavibMask = locallab.spots.at(sp).enavibMask && llvibMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.enalcMask = locallab.spots.at(sp).enalcMask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0 ; + lp.enasharMask = lllcMask == 0 && llcbMask == 0 && llsharMask == 0 && llsoftMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.ena_Mask = locallab.spots.at(sp).enamask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + + // printf("llColorMask=%i lllcMask=%i llExpMask=%i llSHMask=%i llcbMask=%i llretiMask=%i lltmMask=%i llblMask=%i llvibMask=%i\n", llColorMask, lllcMask, llExpMask, llSHMask, llcbMask, llretiMask, lltmMask, llblMask, llvibMask); + if (locallab.spots.at(sp).softMethod == "soft") { + lp.softmet = 0; + } else if (locallab.spots.at(sp).softMethod == "reti") { + lp.softmet = 1; + } + + if (locallab.spots.at(sp).blMethod == "blur") { + lp.blmet = 0; + } else if (locallab.spots.at(sp).blMethod == "med") { + lp.blmet = 1; + } else if (locallab.spots.at(sp).blMethod == "guid") { + lp.blmet = 2; + } + + if (locallab.spots.at(sp).chroMethod == "lum") { + lp.chromet = 0; + } else if (locallab.spots.at(sp).chroMethod == "chr") { + lp.chromet = 1; + } else if (locallab.spots.at(sp).chroMethod == "all") { + lp.chromet = 2; + } + + if (locallab.spots.at(sp).shMethod == "std") { + lp.shmeth = 0; + } else if (locallab.spots.at(sp).shMethod == "tone") { + lp.shmeth = 1; + } + + + if (locallab.spots.at(sp).medMethod == "none") { + lp.medmet = -1; + } else if (locallab.spots.at(sp).medMethod == "33") { + lp.medmet = 0; + } else if (locallab.spots.at(sp).medMethod == "55") { + lp.medmet = 1; + } else if (locallab.spots.at(sp).medMethod == "77") { + lp.medmet = 2; + } else if (locallab.spots.at(sp).medMethod == "99") { + lp.medmet = 3; + } + + if (locallab.spots.at(sp).blurMethod == "norm") { + lp.blurmet = 0; + } else if (locallab.spots.at(sp).blurMethod == "inv") { + lp.blurmet = 1; + } + + if (locallab.spots.at(sp).showmaskblMethodtyp == "blur") { + lp.smasktyp = 0; + } else if (locallab.spots.at(sp).showmaskblMethodtyp == "nois") { + lp.smasktyp = 1; + } else if (locallab.spots.at(sp).showmaskblMethodtyp == "all") { + lp.smasktyp = 2; + } + + + if (locallab.spots.at(sp).spotMethod == "norm") { + lp.excmet = 0; + } else if (locallab.spots.at(sp).spotMethod == "exc") { + lp.excmet = 1; + } + + if (locallab.spots.at(sp).merMethod == "mone") { + lp.mergemet = 0; + } else if (locallab.spots.at(sp).merMethod == "mtwo") { + lp.mergemet = 1; + } else if (locallab.spots.at(sp).merMethod == "mthr") { + lp.mergemet = 2; + } else if (locallab.spots.at(sp).merMethod == "mfou") { + lp.mergemet = 3; + } else if (locallab.spots.at(sp).merMethod == "mfiv") { + lp.mergemet = 4; + } + + if (locallab.spots.at(sp).mergecolMethod == "one") { + lp.mergecolMethod = 0; + } else if (locallab.spots.at(sp).mergecolMethod == "two") { + lp.mergecolMethod = 1; + } else if (locallab.spots.at(sp).mergecolMethod == "thr") { + lp.mergecolMethod = 2; + } else if (locallab.spots.at(sp).mergecolMethod == "fou") { + lp.mergecolMethod = 3; + } else if (locallab.spots.at(sp).mergecolMethod == "fiv") { + lp.mergecolMethod = 4; + } else if (locallab.spots.at(sp).mergecolMethod == "six") { + lp.mergecolMethod = 5; + } else if (locallab.spots.at(sp).mergecolMethod == "sev") { + lp.mergecolMethod = 6; + } else if (locallab.spots.at(sp).mergecolMethod == "sev0") { + lp.mergecolMethod = 7; + } else if (locallab.spots.at(sp).mergecolMethod == "sev1") { + lp.mergecolMethod = 8; + } else if (locallab.spots.at(sp).mergecolMethod == "sev2") { + lp.mergecolMethod = 9; + } else if (locallab.spots.at(sp).mergecolMethod == "hei") { + lp.mergecolMethod = 10; + } else if (locallab.spots.at(sp).mergecolMethod == "nin") { + lp.mergecolMethod = 11; + } else if (locallab.spots.at(sp).mergecolMethod == "ten") { + lp.mergecolMethod = 12; + } else if (locallab.spots.at(sp).mergecolMethod == "ele") { + lp.mergecolMethod = 13; + } else if (locallab.spots.at(sp).mergecolMethod == "twe") { + lp.mergecolMethod = 14; + } else if (locallab.spots.at(sp).mergecolMethod == "thi") { + lp.mergecolMethod = 15; + } else if (locallab.spots.at(sp).mergecolMethod == "for") { + lp.mergecolMethod = 16; + } else if (locallab.spots.at(sp).mergecolMethod == "hue") { + lp.mergecolMethod = 17; + } else if (locallab.spots.at(sp).mergecolMethod == "sat") { + lp.mergecolMethod = 18; + } else if (locallab.spots.at(sp).mergecolMethod == "col") { + lp.mergecolMethod = 19; + } else if (locallab.spots.at(sp).mergecolMethod == "lum") { + lp.mergecolMethod = 20; + } + + if (locallab.spots.at(sp).localedgMethod == "fir") { + lp.edgwmet = 0; + } else if (locallab.spots.at(sp).localedgMethod == "sec") { + lp.edgwmet = 1; + } else if (locallab.spots.at(sp).localedgMethod == "thr") { + lp.edgwmet = 2; + } + + if (locallab.spots.at(sp).localneiMethod == "none") { + lp.neiwmet = -1; + lp.lip3 = false; + } else if (locallab.spots.at(sp).localneiMethod == "low") { + lp.neiwmet = 0; + lp.lip3 = true; + } else if (locallab.spots.at(sp).localneiMethod == "high") { + lp.lip3 = true; + lp.neiwmet = 1; + } + + + if (locallab.spots.at(sp).wavMethod == "D2") { + lp.daubLen = 4; + } else if (locallab.spots.at(sp).wavMethod == "D4") { + lp.daubLen = 6; + } else if (locallab.spots.at(sp).wavMethod == "D6") { + lp.daubLen = 8; + } else if (locallab.spots.at(sp).wavMethod == "D10") { + lp.daubLen = 12; + } else if (locallab.spots.at(sp).wavMethod == "D14") { + lp.daubLen = 16; + } + + + lp.edgwena = locallab.spots.at(sp).wavedg; + + lp.opacol = 0.01 * locallab.spots.at(sp).opacol; + + if (locallab.spots.at(sp).shape == "ELI") { + lp.shapmet = 0; + } else /*if (locallab.spots.at(sp).shape == "RECT")*/ { + lp.shapmet = 1; + } + + lp.denoiena = locallab.spots.at(sp).expblur; + + bool wavcurveden = false; + float local_noiself = 0.f; + float local_noiself0 = 0.f; + float local_noiself2 = 0.f; + float local_noiselc = 0.f; + + if (locwavCurveden && locwavdenutili) { + if (lp.denoiena) { + for (int i = 0; i < 500; i++) { + if (locwavCurveden[i] != 0.f) { + wavcurveden = true; + } + } + } + } + + if (wavcurveden) { + if (lp.denoiena) { + local_noiself0 = 250.f * locwavCurveden[0]; + local_noiself = 250.f * locwavCurveden[166]; + local_noiself2 = 250.f * locwavCurveden[323]; + local_noiselc = 200.f * locwavCurveden[500]; + } + } + + float local_noiseldetail = (float)locallab.spots.at(sp).noiselumdetail; + int local_noiselequal = locallab.spots.at(sp).noiselequal; + float local_noisechrodetail = (float)locallab.spots.at(sp).noisechrodetail; + int local_sensiden = locallab.spots.at(sp).sensiden; + float local_detailthr = (float)locallab.spots.at(sp).detailthr; + + float local_noisecf = ((float)locallab.spots.at(sp).noisechrof) / 10.f; + float local_noisecc = ((float)locallab.spots.at(sp).noisechroc) / 10.f; + float multi[6]; + + for (int y = 0; y < 6; y++) { + multi[y] = ((float) locallab.spots.at(sp).mult[y]); + } + + float multish[5]; + + for (int y = 0; y < 5; y++) { + multish[y] = ((float) locallab.spots.at(sp).multsh[y]); + } + + float thresho = ((float)locallab.spots.at(sp).threshold); + float chromcbdl = (float)locallab.spots.at(sp).chromacbdl; + + int local_chroma = locallab.spots.at(sp).chroma; + int local_sensi = locallab.spots.at(sp).sensi; + int local_sensibn = locallab.spots.at(sp).sensibn; + int local_sensitm = locallab.spots.at(sp).sensitm; + int local_sensiexclu = locallab.spots.at(sp).sensiexclu; + float structexclude = (float) locallab.spots.at(sp).structexclu; + int local_sensilc = locallab.spots.at(sp).sensilc; + int local_warm = locallab.spots.at(sp).warm; + int local_sensih = locallab.spots.at(sp).sensih; + int local_dehaze = locallab.spots.at(sp).dehaz; + int local_depth = locallab.spots.at(sp).depth; + int local_sensicb = locallab.spots.at(sp).sensicb; + float local_clarityml = (float) locallab.spots.at(sp).clarityml; + float local_contresid = (float) locallab.spots.at(sp).contresid; + int local_blurcbdl = 0; //(float) locallab.spots.at(sp).blurcbdl; + int local_contrast = locallab.spots.at(sp).contrast; + float local_lightness = (float) locallab.spots.at(sp).lightness; + float labgridALowloc = locallab.spots.at(sp).labgridALow; + float labgridBLowloc = locallab.spots.at(sp).labgridBLow; + float labgridBHighloc = locallab.spots.at(sp).labgridBHigh; + float labgridAHighloc = locallab.spots.at(sp).labgridAHigh; + float strengthgrid = (float) locallab.spots.at(sp).strengthgrid; + float labgridBLowlocmerg = locallab.spots.at(sp).labgridBLowmerg; + float labgridBHighlocmerg = locallab.spots.at(sp).labgridBHighmerg; + float labgridALowlocmerg = locallab.spots.at(sp).labgridALowmerg; + float labgridAHighlocmerg = locallab.spots.at(sp).labgridAHighmerg; + + float blendmasklc = ((float) locallab.spots.at(sp).blendmasklc) / 100.f ; + float radmasklc = ((float) locallab.spots.at(sp).radmasklc); + float chromasklc = ((float) locallab.spots.at(sp).chromasklc); + float structcolor = (float) locallab.spots.at(sp).structcol; + float blendmaskcolor = ((float) locallab.spots.at(sp).blendmaskcol) / 100.f ; + float radmaskcolor = ((float) locallab.spots.at(sp).radmaskcol); + float chromaskcolor = ((float) locallab.spots.at(sp).chromaskcol); + float gammaskcolor = ((float) locallab.spots.at(sp).gammaskcol); + float slomaskcolor = ((float) locallab.spots.at(sp).slomaskcol); + float blendmaskexpo = ((float) locallab.spots.at(sp).blendmaskexp) / 100.f ; + float radmaskexpo = ((float) locallab.spots.at(sp).radmaskexp); + float chromaskexpo = ((float) locallab.spots.at(sp).chromaskexp); + float gammaskexpo = ((float) locallab.spots.at(sp).gammaskexp); + float slomaskexpo = ((float) locallab.spots.at(sp).slomaskexp); + float strmaskexpo = ((float) locallab.spots.at(sp).strmaskexp); + float angmaskexpo = ((float) locallab.spots.at(sp).angmaskexp); + float strmask = ((float) locallab.spots.at(sp).str_mask); + float angmask = ((float) locallab.spots.at(sp).ang_mask); + float strexpo = ((float) locallab.spots.at(sp).strexp); + float angexpo = ((float) locallab.spots.at(sp).angexp); + float strSH = ((float) locallab.spots.at(sp).strSH); + float angSH = ((float) locallab.spots.at(sp).angSH); + float strcol = ((float) locallab.spots.at(sp).strcol); + float strcolab = ((float) locallab.spots.at(sp).strcolab); + float strcolh = ((float) locallab.spots.at(sp).strcolh); + float angcol = ((float) locallab.spots.at(sp).angcol); + float strvib = ((float) locallab.spots.at(sp).strvib); + float strvibab = ((float) locallab.spots.at(sp).strvibab); + float strvibh = ((float) locallab.spots.at(sp).strvibh); + float angvib = ((float) locallab.spots.at(sp).angvib); + float strwav = ((float) locallab.spots.at(sp).strwav); + float angwav = ((float) locallab.spots.at(sp).angwav); + float strlog = ((float) locallab.spots.at(sp).strlog); + float anglog = ((float) locallab.spots.at(sp).anglog); + float softradiusexpo = ((float) locallab.spots.at(sp).softradiusexp); + float softradiuscolor = ((float) locallab.spots.at(sp).softradiuscol); + float softradiusreti = ((float) locallab.spots.at(sp).softradiusret); + float softradiustma = ((float) locallab.spots.at(sp).softradiustm); + float softradiuscbdl = ((float) locallab.spots.at(sp).softradiuscb); + float blendmaskSH = ((float) locallab.spots.at(sp).blendmaskSH) / 100.f ; + float radmaskSH = ((float) locallab.spots.at(sp).radmaskSH); + float chromaskSH = ((float) locallab.spots.at(sp).chromaskSH); + float gammaskSH = ((float) locallab.spots.at(sp).gammaskSH); + float slomaskSH = ((float) locallab.spots.at(sp).slomaskSH); + float blendmaskvib = ((float) locallab.spots.at(sp).blendmaskvib) / 100.f ; + float radmaskvib = ((float) locallab.spots.at(sp).radmaskvib); + float chromaskvib = ((float) locallab.spots.at(sp).chromaskvib); + float gammaskvib = ((float) locallab.spots.at(sp).gammaskvib); + float slomaskvib = ((float) locallab.spots.at(sp).slomaskvib); + float structexpo = (float) locallab.spots.at(sp).structexp; + float blurexpo = (float) locallab.spots.at(sp).blurexpde; + float blurcolor = (float) locallab.spots.at(sp).blurcolde; + float blurcolmask = (float) locallab.spots.at(sp).blurcol; + float contcolmask = (float) locallab.spots.at(sp).contcol; + float blurSH = (float) locallab.spots.at(sp).blurSHde; + float local_transit = locallab.spots.at(sp).transit; + float local_feather = locallab.spots.at(sp).feather; + float local_transitweak = (float)locallab.spots.at(sp).transitweak; + float local_transitgrad = (float)locallab.spots.at(sp).transitgrad; + float radius = (float) locallab.spots.at(sp).radius; + int itera = locallab.spots.at(sp).itera; + int guidbl = locallab.spots.at(sp).guidbl; + float epsbl = (float) locallab.spots.at(sp).epsbl; + float sharradius = LIM(locallab.spots.at(sp).sharradius, 0.42, 3.5); + float lcamount = ((float) locallab.spots.at(sp).lcamount); + lcamount = LIM01(lcamount); //to prevent crash with old pp3 integer + float sharblurr = LIM(locallab.spots.at(sp).sharblur, 0.2, 3.); //to prevent crash with old pp3 integer + int local_sensisha = locallab.spots.at(sp).sensisha; + int local_sharamount = locallab.spots.at(sp).sharamount; + int local_shardamping = locallab.spots.at(sp).shardamping; + int local_shariter = locallab.spots.at(sp).shariter; + bool inverse = locallab.spots.at(sp).invers; + bool curvacti = locallab.spots.at(sp).curvactiv; + bool acti = locallab.spots.at(sp).activlum; + bool cupas = false; // Provision + int local_sensisf = locallab.spots.at(sp).sensisf; + bool inverseex = locallab.spots.at(sp).inversex; + bool inversesh = locallab.spots.at(sp).inverssh; + bool equiltm = locallab.spots.at(sp).equiltm; + bool fftwlc = locallab.spots.at(sp).fftwlc; + bool fftwreti = locallab.spots.at(sp).fftwreti; + + bool equilret = locallab.spots.at(sp).equilret; + bool inverserad = false; // Provision + bool inverseret = locallab.spots.at(sp).inversret; + bool inversesha = locallab.spots.at(sp).inverssha; + double strength = (double) locallab.spots.at(sp).strength; + float str = (float)locallab.spots.at(sp).str; + int scaleret = (float)locallab.spots.at(sp).scalereti; + + int local_sensihs = locallab.spots.at(sp).sensihs; + int highhs = locallab.spots.at(sp).highlights; + int hltonahs = locallab.spots.at(sp).h_tonalwidth; + int shadhs = locallab.spots.at(sp).shadows; + int shtonals = locallab.spots.at(sp).s_tonalwidth; + int radhs = locallab.spots.at(sp).sh_radius; + float blendmaskcb = ((float) locallab.spots.at(sp).blendmaskcb) / 100.f ; + float radmaskcb = ((float) locallab.spots.at(sp).radmaskcb); + float chromaskcb = ((float) locallab.spots.at(sp).chromaskcb); + float gammaskcb = ((float) locallab.spots.at(sp).gammaskcb); + float slomaskcb = ((float) locallab.spots.at(sp).slomaskcb); + bool enaretiMasktm = locallab.spots.at(sp).enaretiMasktmap; + lp.enaretiMasktmap = enaretiMasktm; + float blendmasktm = ((float) locallab.spots.at(sp).blendmasktm) / 100.f ; + float radmasktm = ((float) locallab.spots.at(sp).radmasktm); + float chromasktm = ((float) locallab.spots.at(sp).chromasktm); + float gammasktm = ((float) locallab.spots.at(sp).gammasktm); + float slomasktm = ((float) locallab.spots.at(sp).slomasktm); + bool wavgradl = locallab.spots.at(sp).wavgradl; + + float blendmaskbl = ((float) locallab.spots.at(sp).blendmaskbl) / 100.f ; + float radmaskbl = ((float) locallab.spots.at(sp).radmaskbl); + float chromaskbl = ((float) locallab.spots.at(sp).chromaskbl); + float gammaskbl = ((float) locallab.spots.at(sp).gammaskbl); + float slomaskbl = ((float) locallab.spots.at(sp).slomaskbl); + bool fftbl = locallab.spots.at(sp).fftwbl; + + + lp.sourcegray = (float) locallab.spots.at(sp).sourceGray; + lp.targetgray = (float) locallab.spots.at(sp).targetGray; + lp.blackev = (float) locallab.spots.at(sp).blackEv; + lp.whiteev = (float) locallab.spots.at(sp).whiteEv; + lp.detail = locallab.spots.at(sp).detail; + lp.sensilog = locallab.spots.at(sp).sensilog; + lp.Autogray = locallab.spots.at(sp).Autogray; + lp.autocompute = locallab.spots.at(sp).autocompute; + lp.baselog = (float) locallab.spots.at(sp).baselog; + lp.sensimas = locallab.spots.at(sp).sensimask; + + lp.deltaem = locallab.spots.at(sp).deltae; + lp.scalereti = scaleret; + lp.cir = circr; + lp.actsp = acti; + lp.xc = w * local_center_x; + lp.yc = h * local_center_y; + lp.lx = w * local_x; + lp.ly = h * local_y; + lp.lxL = w * local_xL; + lp.lyT = h * local_yT; + lp.chro = local_chroma; + lp.struco = structcolor; + lp.strengrid = strengthgrid; + lp.blendmalc = blendmasklc; + lp.radmalc = radmasklc; + lp.chromalc = chromasklc; + lp.blendmacol = blendmaskcolor; + lp.radmacol = radmaskcolor; + lp.chromacol = chromaskcolor; + lp.gammacol = gammaskcolor; + lp.slomacol = slomaskcolor; + lp.radmaexp = radmaskexpo; + lp.chromaexp = chromaskexpo; + lp.gammaexp = gammaskexpo; + lp.slomaexp = slomaskexpo; + lp.strmaexp = strmaskexpo; + lp.angmaexp = angmaskexpo; + lp.str_mas = strmask; + lp.ang_mas = angmask; + + lp.strexp = strexpo; + lp.angexp = angexpo; + lp.strSH = strSH; + lp.angSH = angSH; + lp.strcol = strcol; + lp.strcolab = strcolab; + lp.strcolh = strcolh; + lp.angcol = angcol; + lp.strvib = strvib; + lp.strvibab = strvibab; + lp.strvibh = strvibh; + lp.angvib = angvib; + lp.strwav = strwav; + lp.angwav = angwav; + lp.strlog = strlog; + lp.anglog = anglog; + lp.softradiusexp = softradiusexpo; + lp.softradiuscol = softradiuscolor; + lp.softradiusret = softradiusreti; + lp.softradiuscb = softradiuscbdl; + lp.softradiustm = softradiustma; + lp.struexc = structexclude; + lp.blendmaexp = blendmaskexpo; + lp.blendmaSH = blendmaskSH; + lp.radmaSH = radmaskSH; + lp.chromaSH = chromaskSH; + lp.gammaSH = gammaskSH; + lp.slomaSH = slomaskSH; + lp.blendmavib = blendmaskvib; + lp.radmavib = radmaskvib; + lp.chromavib = chromaskvib; + lp.gammavib = gammaskvib; + lp.slomavib = slomaskvib; + lp.blendmacb = blendmaskcb; + lp.radmacb = radmaskcb; + lp.chromacbm = chromaskcb; + lp.gammacb = gammaskcb; + lp.slomacb = slomaskcb; + lp.blendmatm = blendmasktm; + lp.radmatm = radmasktm; + lp.chromatm = chromasktm; + lp.gammatm = gammasktm; + lp.slomatm = slomasktm; + lp.wavgradl = wavgradl; + + lp.strengthw = ((float) locallab.spots.at(sp).strengthw); + lp.radiusw = ((float) locallab.spots.at(sp).radiusw); + lp.detailw = ((float) locallab.spots.at(sp).detailw); + lp.gradw = ((float) locallab.spots.at(sp).gradw); + lp.tloww = ((float) locallab.spots.at(sp).tloww); + lp.thigw = ((float) locallab.spots.at(sp).thigw); + lp.edgw = ((float) locallab.spots.at(sp).edgw); + lp.basew = ((float) locallab.spots.at(sp).basew); + + lp.blendmabl = blendmaskbl; + lp.radmabl = radmaskbl; + lp.chromabl = chromaskbl; + lp.gammabl = gammaskbl; + lp.slomabl = slomaskbl; + lp.fftbl = fftbl; + lp.it = itera; + lp.guidb = guidbl; + lp.strbl = 0.01f * (float) locallab.spots.at(sp).strbl; + + lp.epsb = epsbl; + lp.struexp = structexpo; + lp.blurexp = blurexpo; + lp.blurcol = blurcolor; + lp.blurcolmask = blurcolmask; + lp.contcolmask = 0.01f * contcolmask; + lp.blurSH = blurSH; + lp.sens = local_sensi; + lp.sensh = local_sensih; + lp.dehaze = local_dehaze; + lp.depth = local_depth; + lp.senscb = local_sensicb; + lp.clarityml = local_clarityml; + lp.contresid = local_contresid; + lp.blurcbdl = local_blurcbdl; + lp.cont = local_contrast; + lp.ligh = local_lightness; + lp.lowA = labgridALowloc; + lp.lowB = labgridBLowloc; + lp.highB = labgridBHighloc; + lp.highA = labgridAHighloc; + lp.lowBmerg = labgridBLowlocmerg; + lp.highBmerg = labgridBHighlocmerg; + lp.lowAmerg = labgridALowlocmerg; + lp.highAmerg = labgridAHighlocmerg; + + lp.senssf = local_sensisf; + lp.strng = strlight; + lp.neig = neigh; + lp.lap = laplac; + + if (lp.ligh >= -2.f && lp.ligh <= 2.f) { + lp.ligh /= 5.f; + } + + lp.trans = local_transit; + lp.feath = local_feather; + lp.transweak = local_transitweak; + lp.transgrad = local_transitgrad; + lp.rad = radius; + lp.stren = strength; + lp.sensbn = local_sensibn; + lp.sensexclu = local_sensiexclu; + lp.senslc = local_sensilc; + lp.lcamount = lcamount; + lp.inv = inverse; + lp.invex = inverseex; + lp.invsh = inversesh; + lp.curvact = curvacti; + lp.invrad = inverserad; + lp.invret = inverseret; + lp.equret = equilret; + lp.equtm = equiltm; + lp.invshar = inversesha; + lp.str = str; + lp.shrad = sharradius; + lp.shblurr = sharblurr; + lp.senssha = local_sensisha; + lp.shamo = local_sharamount; + lp.shdamp = local_shardamping; + lp.shiter = local_shariter; + lp.iterat = iterati; + lp.balance = balanc; + lp.balanceh = balanch; + lp.colorde = colorde; + lp.thr = thre; + lp.stru = strucc; + lp.noiself = local_noiself; + lp.noiself0 = local_noiself0; + lp.noiself2 = local_noiself2; + lp.noiseldetail = local_noiseldetail; + lp.detailthr = local_detailthr; + lp.noiselequal = local_noiselequal; + lp.noisechrodetail = local_noisechrodetail; + lp.noiselc = local_noiselc; + lp.noisecf = local_noisecf; + lp.noisecc = local_noisecc; + lp.sensden = local_sensiden; + lp.bilat = locallab.spots.at(sp).bilateral; + lp.adjch = (float) locallab.spots.at(sp).adjblur; + lp.strengt = streng; + lp.gamm = gam; + lp.esto = est; + lp.scalt = scal_tm; + lp.rewe = rewe; + lp.senstm = local_sensitm; + lp.amo = amo; + lp.blurma = (float) locallab.spots.at(sp).blurmask; + lp.fftma = locallab.spots.at(sp).fftmask; + lp.contma = (float) locallab.spots.at(sp).contmask; + + for (int y = 0; y < 6; y++) { + lp.mulloc[y] = LIM(multi[y], 0.f, 4.f);//to prevent crash with old pp3 integer + } + + for (int y = 0; y < 5; y++) { + lp.mullocsh[y] = multish[y]; + } + + lp.logena = locallab.spots.at(sp).explog; + + lp.detailsh = locallab.spots.at(sp).detailSH; + lp.threshol = thresho; + lp.chromacb = chromcbdl; + lp.expvib = locallab.spots.at(sp).expvibrance; + lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask + lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible + lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible + lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && ll_Mask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.maskena = locallab.spots.at(sp).expmask && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + + lp.sensv = local_sensiv; + lp.past = chromaPastel; + lp.satur = chromaSatur; + + lp.cut_past = cupas; + lp.blac = locallab.spots.at(sp).black; + lp.shcomp = locallab.spots.at(sp).shcompr; + lp.shadex = locallab.spots.at(sp).shadex; + lp.hlcomp = locallab.spots.at(sp).hlcompr; + lp.hlcompthr = locallab.spots.at(sp).hlcomprthresh; + lp.expcomp = LIM(locallab.spots.at(sp).expcomp, -2.0, 4.0); //to prevent crash with Old pp3 with integer + //increase sensitivity for low values + float proexp = lp.expcomp; + if (std::fabs(proexp) < 0.6f) { + float interm = std::fabs(proexp) / 0.6f; + interm = SQR(interm); + lp.expcomp = proexp * interm; + } + lp.expchroma = locallab.spots.at(sp).expchroma / 100.; + lp.sensex = local_sensiex; + lp.war = local_warm; + lp.highlihs = highhs; + lp.shadowhs = shadhs; + lp.radiushs = radhs; + lp.hltonalhs = hltonahs; + lp.shtonalhs = shtonals; + lp.senshs = local_sensihs; + lp.ftwlc = fftwlc; + lp.ftwreti = fftwreti; + lp.sigmadr = locallab.spots.at(sp).sigmadr; + lp.sigmabl = locallab.spots.at(sp).sigmabl; + lp.sigmaed = locallab.spots.at(sp).sigmaed; + lp.sigmalc = locallab.spots.at(sp).sigmalc; + lp.sigmalc2 = locallab.spots.at(sp).sigmalc2; + lp.residsha = locallab.spots.at(sp).residsha; + lp.residshathr = locallab.spots.at(sp).residshathr; + lp.residhi = locallab.spots.at(sp).residhi; + lp.residhithr = locallab.spots.at(sp).residhithr; + lp.blwh = locallab.spots.at(sp).blwh; + lp.senscolor = (int) locallab.spots.at(sp).colorscope; + //replace scope color exposure vibrance shadows + lp.sens = lp.senscolor; + lp.sensv = lp.senscolor; + lp.senshs = lp.senscolor; + if(lp.expmet == 0){ + lp.sensex = lp.senscolor; + } +} + +static void calcTransitionrect(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) +{ + zone = 0; + + if (lox >= lp.xc && lox < lp.xc + lp.lx) { + if (loy >= lp.yc && loy < lp.yc + lp.ly) { + if (lox < lp.xc + lp.lx * ach && loy < lp.yc + lp.ly * ach) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { + if (lox < lp.xc + lp.lx * ach && loy > lp.yc - lp.lyT * ach) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { + if (loy <= lp.yc && loy > lp.yc - lp.lyT) { + if (lox > (lp.xc - lp.lxL * ach) && loy > (lp.yc - lp.lyT * ach)) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } else if (loy > lp.yc && loy < lp.yc + lp.ly) { + if (lox > (lp.xc - lp.lxL * ach) && loy < (lp.yc + lp.ly * ach)) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } +} + +static void calcTransition(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) +{ + // returns the zone (0 = outside selection, 1 = transition zone between outside and inside selection, 2 = inside selection) + // and a factor to calculate the transition in case zone == 1 + + zone = 0; + + if (lox >= lp.xc && lox < lp.xc + lp.lx) { + if (loy >= lp.yc && loy < lp.yc + lp.ly) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.ly)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.lyT)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } + } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { + if (loy <= lp.yc && loy > lp.yc - lp.lyT) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.lyT)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } else if (loy > lp.yc && loy < lp.yc + lp.ly) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.ly)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } + } +} + +// Copyright 2018 Alberto Griggio +//J.Desmis 12 2019 - I will try to port a raw process in local adjustments +// I choose this one because, it is "new" +// Perhaps - probably no result, but perhaps ?? + +float find_gray(float source_gray, float target_gray) +{ + // find a base such that log2lin(base, source_gray) = target_gray + // log2lin is (base^source_gray - 1) / (base - 1), so we solve + // + // (base^source_gray - 1) / (base - 1) = target_gray, that is + // + // base^source_gray - 1 - base * target_gray + target_gray = 0 + // + // use a bisection method (maybe later change to Netwon) + + if (source_gray <= 0.f) { + return 0.f; + } + + const auto f = + [ = ](float x) -> float { + return std::pow(x, source_gray) - 1 - target_gray * x + target_gray; + }; + + // first find the interval we are interested in + + float lo = 1.f; + + while (f(lo) <= 0.f) { + lo *= 2.f; + } + + float hi = lo * 2.f; + + while (f(hi) >= 0.f) { + hi *= 2.f; + } + + if (std::isinf(hi)) { + return 0.f; + } + + // now search for a zero + for (int iter = 0; iter < 100; ++iter) { + float mid = lo + (hi - lo) / 2.f; + float v = f(mid); + + if (std::abs(v) < 1e-4f || (hi - lo) / lo <= 1e-4f) { + return mid; + } + + if (v > 0.f) { + lo = mid; + } else { + hi = mid; + } + } + + return 0.f; // not found +} + + +// basic log encoding taken from ACESutil.Lin_to_Log2, from +// https://github.com/ampas/aces-dev +// (as seen on pixls.us) +void ImProcFunctions::log_encode(Imagefloat *rgb, const struct local_params & lp, bool multiThread, int bfw, int bfh) +{ + /* J.Desmis 12 2019 + small adaptations to local adjustments + replace log2 by log(lp.baselog) allows diferentiation between low and high lights + */ + BENCHFUN + const float gray = lp.sourcegray / 100.f; + const float shadows_range = lp.blackev; + const float dynamic_range = lp.whiteev - lp.blackev; + const float noise = pow_F(2.f, -16.f); + const float log2 = xlogf(lp.baselog); + const float base = lp.targetgray > 1 && lp.targetgray < 100 && dynamic_range > 0 ? find_gray(std::abs(lp.blackev) / dynamic_range, lp.targetgray / 100.f) : 0.f; + const float linbase = rtengine::max(base, 0.f); + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); + + const auto apply = + [ = ](float x, bool scale = true) -> float { + if (scale) + { + x /= 65535.f; + } + + x = rtengine::max(x, noise); + x = rtengine::max(x / gray, noise); + x = rtengine::max((xlogf(x) / log2 - shadows_range) / dynamic_range, noise); + assert(x == x); + + if (linbase > 0.f) + { + x = xlog2lin(x, linbase); + } + + if (scale) + { + return x * 65535.f; + } else { + return x; + } + }; + + const auto norm = + [&](float r, float g, float b) -> float { + return Color::rgbLuminance(r, g, b, ws); + + // other possible alternatives (so far, luminance seems to work + // fine though). See also + // https://discuss.pixls.us/t/finding-a-norm-to-preserve-ratios-across-non-linear-operations + // + // MAX + //return max(r, g, b); + // + // Euclidean + //return std::sqrt(SQR(r) + SQR(g) + SQR(b)); + + // weighted yellow power norm from https://youtu.be/Z0DS7cnAYPk + // float rr = 1.22f * r / 65535.f; + // float gg = 1.20f * g / 65535.f; + // float bb = 0.58f * b / 65535.f; + // float rr4 = SQR(rr) * SQR(rr); + // float gg4 = SQR(gg) * SQR(gg); + // float bb4 = SQR(bb) * SQR(bb); + // float den = (rr4 + gg4 + bb4); + // if (den > 0.f) { + // return 0.8374319f * ((rr4 * rr + gg4 * gg + bb4 * bb) / den) * 65535.f; + // } else { + // return 0.f; + // } + }; + + const float detail = lp.detail; + const int W = rgb->getWidth(), H = rgb->getHeight(); + + if (detail == 0.f) {//no local contrast +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float r = rgb->r(y, x); + float g = rgb->g(y, x); + float b = rgb->b(y, x); + float m = norm(r, g, b); + + if (m > noise) { + float mm = apply(m); + float f = mm / m; + r *= f; + b *= f; + g *= f; + } + + assert(r == r); + assert(g == g); + assert(b == b); + + rgb->r(y, x) = r; + rgb->g(y, x) = g; + rgb->b(y, x) = b; + } + } + } else {//local contrast + + array2D Y(W, H); + { + constexpr float base_posterization = 20.f; + array2D Y2(W, H); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + Y2[y][x] = norm(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x)) / 65535.f; + float l = xlogf(rtengine::max(Y2[y][x], 1e-9f)); + float ll = round(l * base_posterization) / base_posterization; + Y[y][x] = xexpf(ll); + assert(std::isfinite(Y[y][x])); + } + } + const float radius = rtengine::max(rtengine::max(bfw, W), rtengine::max(bfh, H)) / 30.f; + const float epsilon = 0.005f; + rtengine::guidedFilter(Y2, Y, Y, radius, epsilon, multiThread); + } + const float blend = detail; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float &r = rgb->r(y, x); + float &g = rgb->g(y, x); + float &b = rgb->b(y, x); + float t = Y[y][x]; + float t2; + + if (t > noise && (t2 = norm(r, g, b)) > noise) { + float c = apply(t, false); + float f = c / t; + // float t2 = norm(r, g, b); + float f2 = apply(t2) / t2; + f = intp(blend, f, f2); + assert(std::isfinite(f)); + r *= f; + g *= f; + b *= f; + assert(std::isfinite(r)); + assert(std::isfinite(g)); + assert(std::isfinite(b)); + } + } + } + } +} + +void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, float *blackev, float *whiteev, bool *Autogr, int fw, int fh, float xsta, float xend, float ysta, float yend, int SCALE) +{ + BENCHFUN +//adpatation to local adjustments Jacques Desmis 12 2019 + const PreviewProps pp(0, 0, fw, fh, SCALE); + + Imagefloat img(int(fw / SCALE + 0.5), int(fh / SCALE + 0.5)); + const ProcParams neutral; + imgsrc->getImage(imgsrc->getWB(), TR_NONE, &img, pp, params->toneCurve, neutral.raw); + imgsrc->convertColorSpace(&img, params->icm, imgsrc->getWB()); + float minVal = RT_INFINITY; + float maxVal = -RT_INFINITY; + const float ec = std::pow(2.f, params->toneCurve.expcomp); + + constexpr float noise = 1e-5; + const int h = fh / SCALE; + const int w = fw / SCALE; + + const int hsta = ysta * h; + const int hend = yend * h; + + const int wsta = xsta * w; + const int wend = xend * w; + + double mean = 0.0; + int nc = 0; + for (int y = hsta; y < hend; ++y) { + for (int x = wsta; x < wend; ++x) { + const float r = img.r(y, x), g = img.g(y, x), b = img.b(y, x); + mean += static_cast(0.2126f * Color::gamma_srgb(r) + 0.7152f * Color::gamma_srgb(g) + 0.0722f * Color::gamma_srgb(b)); + nc++; + + const float m = rtengine::max(0.f, r, g, b) / 65535.f * ec; + if (m > noise) { + const float l = rtengine::min(r, g, b) / 65535.f * ec; + minVal = rtengine::min(minVal, l > noise ? l : m); + maxVal = rtengine::max(maxVal, m); + } + } + } + + //approximation sourcegray yb source = 0.4 * yb + + if (maxVal > minVal) { + const float log2 = std::log(2.f); + const float dynamic_range = -xlogf(minVal / maxVal) / log2; + + if (settings->verbose) { + std::cout << "AutoLog: min = " << minVal << ", max = " << maxVal + << ", DR = " << dynamic_range << std::endl; + } + + if (Autogr[sp]) { + double tot = 0.0; + int n = 0; + const float gmax = rtengine::min(maxVal / 2.f, 0.25f); + const float gmin = rtengine::max(minVal * std::pow(2.f, rtengine::max((dynamic_range - 1.f) / 2.f, 1.f)), 0.05f); + + if (settings->verbose) { + std::cout << " gray boundaries: " << gmin << ", " << gmax << std::endl; + } + + for (int y = ysta; y < yend; ++y) { + for (int x = wsta; x < wend; ++x) { + const float l = img.g(y, x) / 65535.f; + + if (l >= gmin && l <= gmax) { + tot += static_cast(l); + ++n; + } + } + } + + if (n > 0) { + sourceg[sp] = tot / n * 100.0; + + if (settings->verbose) { + std::cout << " computed gray point from " << n << " samples: " << sourceg[sp] << std::endl; + } + } else { + mean /= (nc * 65535.0); + float yb; + + if (mean < 0.15) { + yb = 3.0f; + } else if (mean < 0.3) { + yb = 5.0f; + } else if (mean < 0.4) { + yb = 10.0f; + } else if (mean < 0.45) { + yb = 15.0f; + } else if (mean < 0.5) { + yb = 18.0f; + } else if (mean < 0.55) { + yb = 23.0f; + } else if (mean < 0.6) { + yb = 30.0f; + } else { + yb = 45.f; + } + sourceg[sp] = 0.4f * yb; + if (settings->verbose) { + std::cout << " no samples found in range, resorting to Yb gray point value " << sourceg[sp] << std::endl; + } + } + } + + const float gray = sourceg[sp] / 100.f; + whiteev[sp] = xlogf(maxVal / gray) / log2; + blackev[sp] = whiteev[sp] - dynamic_range; + } +} + +void tone_eq(array2D &R, array2D &G, array2D &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 + Small adaptation to Local Adjustment 10 2019 Jacques Desmis + 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 . +*/ + +{ + BENCHFUN + + const int W = R.width(); + const int H = R.height(); + array2D 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 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; + } + } + +} + + +void ImProcFunctions::ciecamloc_02float(int sp, LabImage* lab) +{ + //be careful quasi duplicate with branch cat02wb + BENCHFUN + + int width = lab->W, height = lab->H; + float Yw; + Yw = 1.0f; + double Xw, Zw; + float f = 0.f, nc = 0.f, la, c = 0.f, xw, yw, zw, f2 = 1.f, c2 = 1.f, nc2 = 1.f, yb2; + float fl, n, nbb, ncb, aw; //d + float xwd, ywd, zwd, xws, yws, zws; + // int alg = 0; + double Xwout, Zwout; + double Xwsc, Zwsc; + + int tempo; + + if (params->locallab.spots.at(sp).warm > 0) { + tempo = 5000 - 30 * params->locallab.spots.at(sp).warm; + } else { + tempo = 5000 - 49 * params->locallab.spots.at(sp).warm; + } + + ColorTemp::temp2mulxyz(params->wb.temperature, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB + ColorTemp::temp2mulxyz(tempo, "Custom", Xwout, Zwout); + ColorTemp::temp2mulxyz(5000, "Custom", Xwsc, Zwsc); + + //viewing condition for surrsrc + f = 1.00f; + c = 0.69f; + nc = 1.00f; + //viewing condition for surround + f2 = 1.0f, c2 = 0.69f, nc2 = 1.0f; + //with which algorithm + // alg = 0; + + + xwd = 100.0 * Xwout; + zwd = 100.0 * Zwout; + ywd = 100.f; + + xws = 100.0 * Xwsc; + zws = 100.0 * Zwsc; + yws = 100.f; + + + yb2 = 18; + //La and la2 = ambiant luminosity scene and viewing + la = 400.f; + const float la2 = 400.f; + const float pilot = 2.f; + const float pilotout = 2.f; + + //algoritm's params + // const float rstprotection = 100. ;//- params->colorappearance.rstprotection; + LUTu hist16J; + LUTu hist16Q; + float yb = 18.f; + float d, dj; + + // const int gamu = 0; //(params->colorappearance.gamut) ? 1 : 0; + xw = 100.0 * Xw; + yw = 100.f * Yw; + zw = 100.0 * Zw; + float xw1 = xws, yw1 = yws, zw1 = zws, xw2 = xwd, yw2 = ywd, zw2 = zwd; + + float cz, wh, pfl; + Ciecam02::initcam1float(yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); +// const float chr = 0.f; + const float pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f); + float nj, nbbj, ncbj, czj, awj, flj; + Ciecam02::initcam2float(yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); +#ifdef __SSE2__ + const float reccmcz = 1.f / (c2 * czj); +#endif + const float pow1n = pow_F(1.64f - pow_F(0.29f, nj), 0.73f); + +#ifdef __SSE2__ + int bufferLength = ((width + 3) / 4) * 4; // bufferLength has to be a multiple of 4 +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + // one line buffer per channel and thread + float Jbuffer[bufferLength] ALIGNED16; + float Cbuffer[bufferLength] ALIGNED16; + float hbuffer[bufferLength] ALIGNED16; + float Qbuffer[bufferLength] ALIGNED16; + float Mbuffer[bufferLength] ALIGNED16; + float sbuffer[bufferLength] ALIGNED16; +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + for (int i = 0; i < height; i++) { +#ifdef __SSE2__ + // vectorized conversion from Lab to jchqms + int k; + vfloat x, y, z; + vfloat J, C, h, Q, M, s; + + vfloat c655d35 = F2V(655.35f); + + for (k = 0; k < width - 3; k += 4) { + Color::Lab2XYZ(LVFU(lab->L[i][k]), LVFU(lab->a[i][k]), LVFU(lab->b[i][k]), x, y, z); + x = x / c655d35; + y = y / c655d35; + z = z / c655d35; + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, F2V(aw), F2V(fl), F2V(wh), + x, y, z, + F2V(xw1), F2V(yw1), F2V(zw1), + F2V(c), F2V(nc), F2V(pow1), F2V(nbb), F2V(ncb), F2V(pfl), F2V(cz), F2V(d)); + STVF(Jbuffer[k], J); + STVF(Cbuffer[k], C); + STVF(hbuffer[k], h); + STVF(Qbuffer[k], Q); + STVF(Mbuffer[k], M); + STVF(sbuffer[k], s); + } + + for (; k < width; k++) { + float L = lab->L[i][k]; + float a = lab->a[i][k]; + float b = lab->b[i][k]; + float x, y, z; + //convert Lab => XYZ + Color::Lab2XYZ(L, a, b, x, y, z); + x = x / 655.35f; + y = y / 655.35f; + z = z / 655.35f; + float J, C, h, Q, M, s; + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, + c, nc, pow1, nbb, ncb, pfl, cz, d); + Jbuffer[k] = J; + Cbuffer[k] = C; + hbuffer[k] = h; + Qbuffer[k] = Q; + Mbuffer[k] = M; + sbuffer[k] = s; + } + +#endif // __SSE2__ + + for (int j = 0; j < width; j++) { + float J, C, h, Q, M, s; + +#ifdef __SSE2__ + // use precomputed values from above + J = Jbuffer[j]; + C = Cbuffer[j]; + h = hbuffer[j]; + Q = Qbuffer[j]; + M = Mbuffer[j]; + s = sbuffer[j]; +#else + float x, y, z; + float L = lab->L[i][j]; + float a = lab->a[i][j]; + float b = lab->b[i][j]; + float x1, y1, z1; + //convert Lab => XYZ + Color::Lab2XYZ(L, a, b, x1, y1, z1); + x = x1 / 655.35f; + y = y1 / 655.35f; + z = z1 / 655.35f; + //process source==> normal + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, + c, nc, pow1, nbb, ncb, pfl, cz, d); +#endif + float Jpro, Cpro, hpro, Qpro, Mpro, spro; + Jpro = J; + Cpro = C; + hpro = h; + Qpro = Q; + Mpro = M; + spro = s; + /* + */ + + + //retrieve values C,J...s + C = Cpro; + J = Jpro; + Q = Qpro; + M = Mpro; + h = hpro; + s = spro; + +#ifdef __SSE2__ + // write to line buffers + Jbuffer[j] = J; + Cbuffer[j] = C; + hbuffer[j] = h; +#else + float xx, yy, zz; + //process normal==> viewing + + Ciecam02::jch2xyz_ciecam02float(xx, yy, zz, + J, C, h, + xw2, yw2, zw2, + c2, nc2, pow1n, nbbj, ncbj, flj, czj, dj, awj); + x = xx * 655.35f; + y = yy * 655.35f; + z = zz * 655.35f; + float Ll, aa, bb; + //convert xyz=>lab + Color::XYZ2Lab(x, y, z, Ll, aa, bb); + lab->L[i][j] = Ll; + lab->a[i][j] = aa; + lab->b[i][j] = bb; +#endif + } + +#ifdef __SSE2__ + // process line buffers + float *xbuffer = Qbuffer; + float *ybuffer = Mbuffer; + float *zbuffer = sbuffer; + + for (k = 0; k < bufferLength; k += 4) { + Ciecam02::jch2xyz_ciecam02float(x, y, z, + LVF(Jbuffer[k]), LVF(Cbuffer[k]), LVF(hbuffer[k]), + F2V(xw2), F2V(yw2), F2V(zw2), + F2V(nc2), F2V(pow1n), F2V(nbbj), F2V(ncbj), F2V(flj), F2V(dj), F2V(awj), F2V(reccmcz)); + STVF(xbuffer[k], x * c655d35); + STVF(ybuffer[k], y * c655d35); + STVF(zbuffer[k], z * c655d35); + } + + // XYZ2Lab uses a lookup table. The function behind that lut is a cube root. + // SSE can't beat the speed of that lut, so it doesn't make sense to use SSE + for (int j = 0; j < width; j++) { + float Ll, aa, bb; + //convert xyz=>lab + Color::XYZ2Lab(xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); + + lab->L[i][j] = Ll; + lab->a[i][j] = aa; + lab->b[i][j] = bb; + } + +#endif + } + + } +} + +void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufcolfin, float rad, int bfh, int bfw, float epsilmax, float epsilmin, float thres, int sk, bool multiThread, int flag) +{ + if (rad != 0.f) { + array2D ble(bfw, bfh); + array2D guid(bfw, bfh); + if (flag == 0) { + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; + ble[ir][jr] = Color::L2Y(bufcolfin->L[ir][jr]) / 32768.f; + } + } + + const float aepsil = (epsilmax - epsilmin) / 100.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + // const float epsil = aepsil * 0.1f * rad + bepsil; + const float epsil = aepsil * rad + bepsil; + const float blur = 10.f / sk * (thres + 0.8f * rad); + + rtengine::guidedFilter(guid, ble, ble, blur, epsil, multiThread, 4); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = Color::computeXYZ2LabY(32768.f * ble[ir][jr]); + } + } + } else if (flag == 1) { + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + ble[ir][jr] = bufcolfin->L[ir][jr] / 32768.f; + guid[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; + } + + const float aepsil = (epsilmax - epsilmin) / 1000.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.0001f : aepsil * rad + bepsil; + const float blur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r2 = rtengine::max(int(25 / sk * blur + 0.5f), 1); + + rtengine::guidedFilter(guid, ble, ble, r2, epsil, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = 32768.f * ble[ir][jr]; + } + } + } + } +} + + +void ImProcFunctions::softprocess(const LabImage* bufcolorig, array2D &buflight, float rad, int bfh, int bfw, double epsilmax, double epsilmin, float thres, int sk, bool multiThread) +{ + float minlig = buflight[0][0]; + +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minlig) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + minlig = rtengine::min(buflight[ir][jr], minlig); + } + } + + array2D guidsoft(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = LIM01((buflight[ir][jr] - minlig) / (100.f - minlig)); + guidsoft[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; + } + } + + double aepsil = (epsilmax - epsilmin) / 90.f; + double bepsil = epsilmax - 100.f * aepsil; + double epsil = aepsil * rad + bepsil; + float blur = 1.f / sk * (thres + 0.8f * rad); + guidedFilter(guidsoft, buflight, buflight, blur, epsil, multiThread, 4); + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = (100.f - minlig) * buflight[ir][jr] + minlig; + } + } +} + +void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve) +{ + BENCHFUN + //exposure local + + constexpr float maxran = 65536.f; + const float cexp_scale = std::pow(2.f, lp.expcomp); + const float ccomp = (rtengine::max(0.f, lp.expcomp) + 1.f) * lp.hlcomp / 100.f; + const float cshoulder = ((maxran / rtengine::max(1.0f, cexp_scale)) * (lp.hlcompthr / 200.f)) + 0.1f; + const float chlrange = maxran - cshoulder; + const float linear = lp.linear; + constexpr float kl = 1.f; + const float hlcompthr = lp.hlcompthr / 200.f; + const float hlcomp = lp.hlcomp / 100.f; + if (lp.linear > 0.f && lp.expcomp == 0.f) { + lp.expcomp = 0.001f; + } + + if (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float L = bufexporig->L[ir][jr]; + const float Llin = LIM01(L / 32768.f); + const float addcomp = linear * (-kl * Llin + kl);//maximum about 1. IL + const float exp_scale = pow_F(2.0, (lp.expcomp + addcomp)); + const float shoulder = ((maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr) + 0.1f; + const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; + const float hlrange = maxran - shoulder; + + //highlight + const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(exp_scale, comp, hlrange, 2 * L)); + L *= hlfactor * pow_F(2.f, addcomp);//approximation but pretty good with Laplacian and L < mean, hl aren't call + //shadow tone curve + L *= shtonecurve[2 * L]; + //tonecurve + lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float L = bufexporig->L[ir][jr]; + //highlight + const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(cexp_scale, ccomp, chlrange, 2 * L)); + L *= hlfactor;//approximation but pretty good with Laplacian and L < mean, hl aren't call + //shadow tone curve + L *= shtonecurve[2 * L]; + //tonecurve + lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; + } + } + } +} + + +void ImProcFunctions::addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk) +{ +// BENCHFUN +//Box-Muller method. +// add luma noise to image + + srand(1); + + const float variaFactor = SQR(variance) / sk; + constexpr float randFactor1 = 1.f / RAND_MAX; + constexpr float randFactor2 = (2.f * rtengine::RT_PI_F) / RAND_MAX; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + float z0, z1; + bool generate = false; +#ifdef _OPENMP + #pragma omp for schedule(static) // static scheduling is important to avoid artefacts +#endif + for (int y = 0; y < lab->H; y++) { + for (int x = 0; x < lab->W; x++) { + generate = !generate; + float kvar = 1.f; + + if (lab->L[y][x] < 12000.f) { + constexpr float ah = -0.5f / 12000.f; + constexpr float bh = 1.5f; + kvar = ah * lab->L[y][x] + bh; //increase effect for low lights < 12000.f + } else if (lab->L[y][x] > 20000.f) { + constexpr float ah = -0.5f / 12768.f; + constexpr float bh = 1.f - 20000.f * ah; + kvar = ah * lab->L[y][x] + bh; //decrease effect for high lights > 20000.f + kvar = kvar < 0.5f ? 0.5f : kvar; + } + + float varia = SQR(kvar) * variaFactor; + + if (!generate) { + dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z1, 0.f, 32768.f); + continue; + } + + int u1 = 0; + int u2; + + while (u1 == 0) { + u1 = rand(); + u2 = rand(); + } + + float u1f = u1 * randFactor1; + float u2f = u2 * randFactor2; + + float2 sincosval = xsincosf(2.f * rtengine::RT_PI_F * u2f); + float factor = std::sqrt(-2.f * xlogf(u1f)); + z0 = factor * sincosval.y; + z1 = factor * sincosval.x; + + dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z0, 0.f, 32768.f); + + } + } + } +} + +void ImProcFunctions::DeNoise_Local(int call, const struct local_params& lp, LabImage* originalmask, int levred, float hueref, float lumaref, float chromaref, LabImage* original, LabImage* transformed, const LabImage &tmp1, int cx, int cy, int sk) +{ + //warning, but I hope used it next + // local denoise and impulse + //simple algo , perhaps we can improve as the others, but noise is here and not good for hue detection + // BENCHFUN + lumaref *= 327.68f; + const float ach = lp.trans / 100.f; + + const float factnoise1 = 1.f + (lp.noisecf) / 500.f; + const float factnoise2 = 1.f + (lp.noisecc) / 500.f; + const float factnoise = factnoise1 * factnoise2; + + const int GW = transformed->W; + const int GH = transformed->H; + + const float colorde = lp.colorde == 0 ? -1.f : lp.colorde; // -1.f to avoid black + const float amplabL = 2.f * colorde; + constexpr float darklim = 5000.f; + + const float refa = chromaref * std::cos(hueref) * 327.68f; + const float refb = chromaref * std::sin(hueref) * 327.68f; + const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; + const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; + const bool previewbl = lp.showmaskblmet == 4; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float radius = 3.f / sk; + + if (usemaskbl) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblur->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblur->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblur->b, GW, GH, radius); + } + } else { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + } + + const int begx = lp.xc - lp.lxL; + const int begy = lp.yc - lp.lyT; + constexpr float r327d68 = 1.f / 327.68f; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage* maskptr = origblur.get(); + const float mindE = 2.f + MINSCOPE * lp.sensden * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensden * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + for (int x = 0, lox = cx + x; x < transformed->W; x++, lox++) { + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float reducdEL = 1.f; + float reducdEa = 1.f; + float reducdEb = 1.f; + + if (levred == 7) { + const float dEL = std::sqrt(0.9f * SQR(refa - maskptr->a[y][x]) + 0.9f * SQR(refb - maskptr->b[y][x]) + 1.2f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + const float dEa = std::sqrt(1.2f * SQR(refa - maskptr->a[y][x]) + 1.f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + const float dEb = std::sqrt(1.f * SQR(refa - maskptr->a[y][x]) + 1.2f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + reducdEL = SQR(calcreducdE(dEL, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + reducdEa = SQR(calcreducdE(dEa, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + reducdEb = SQR(calcreducdE(dEb, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + } + float difL, difa, difb; + + if (call == 2 /*|| call == 1 || call == 3 */) { //simpleprocess + difL = tmp1.L[loy - begy][lox - begx] - original->L[y][x]; + difa = tmp1.a[loy - begy][lox - begx] - original->a[y][x]; + difb = tmp1.b[loy - begy][lox - begx] - original->b[y][x]; + } else { //dcrop + difL = tmp1.L[y][x] - original->L[y][x]; + difa = tmp1.a[y][x] - original->a[y][x]; + difb = tmp1.b[y][x] - original->b[y][x]; + } + + difL *= localFactor * reducdEL; + difa *= localFactor * reducdEa; + difb *= localFactor * reducdEb; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + transformed->a[y][x] = clipC((original->a[y][x] + difa) * factnoise); + transformed->b[y][x] = clipC((original->b[y][x] + difb) * factnoise) ; + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + amplabL * difL);// * 10.f empirical to can visualize modifications + transformed->a[y][x] = clipC(amplabL * difa);// * 10.f empirical to can visualize modifications + transformed->b[y][x] = clipC(amplabL * difb);// * 10.f empirical to can visualize modifications + } else if (previewbl || lp.prevdE) { + const float difbdisp = (reducdEL + reducdEa + reducdEb) * 10000.f * colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + transformed->L[y][x] = darklim - transformed->L[y][x]; + } + + if (colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::InverseReti_Local(const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int chro, int sk) +{ + // BENCHFUN +//inverse local retinex + float ach = lp.trans / 100.f; + int GW = transformed->W; + int GH = transformed->H; + float refa = chromaref * cos(hueref); + float refb = chromaref * sin(hueref); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + float radius = 3.f / sk; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + + int zone; + float localFactor; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + float rL = origblur->L[y][x] / 327.68f; + float dE = std::sqrt(kab * SQR(refa - origblur->a[y][x] / 327.68f) + kab * SQR(refb - origblur->b[y][x] / 327.68f) + kL * SQR(lumaref - rL)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensh); + + switch (zone) { + case 0: { // outside selection and outside transition zone => full effect, no transition + if (chro == 0) { + float difL = tmp1->L[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + } + + if (chro == 1) { + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + + transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); + transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); + } + break; + } + + case 1: { // inside transition zone + float factorx = 1.f - localFactor; + + if (chro == 0) { + float difL = tmp1->L[y][x] - original->L[y][x]; + difL *= factorx; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + } + + if (chro == 1) { + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + + difa *= factorx; + difb *= factorx; + + transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); + transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); + } + break; + } + + case 2: { // inside selection => no effect, keep original values + if (chro == 0) { + transformed->L[y][x] = original->L[y][x]; + } + + if (chro == 1) { + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + } + } + } + } + } + } +} + + + + +void ImProcFunctions::InverseBlurNoise_Local(LabImage * originalmask, float **bufchro, const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int sk) +{ + // BENCHFUN +//inverse local blur and noise + float ach = lp.trans / 100.f; + int GW = transformed->W; + int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + + const bool blshow = (lp.showmaskblmet == 1 || lp.showmaskblmet == 2); + const bool previewbl = (lp.showmaskblmet == 4); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + std::unique_ptr origblurmask; + const bool usemaskbl = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4); + const bool usemaskall = usemaskbl; + + float radius = 3.f / sk; + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * lp.sensbn * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + + int zone; + float localFactor; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + const float clc = (previewbl) ? settings->previewselection * 100.f : bufchro[y][x]; + float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + + float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); + const float realstrchdE = reducdE * clc; + + switch (zone) { + + case 0: { // outside selection and outside transition zone => full effect, no transition + float difL = tmp1->L[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + float flia = 1.f, flib = 1.f; + flia = flib = ((100.f + realstrchdE) / 100.f); + const float chra = tmp1->a[y][x]; + const float chrb = tmp1->b[y][x]; + + if (!lp.actsp) { + difa = chra * flia - original->a[y][x]; + difb = chrb * flib - original->b[y][x]; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewbl || lp.prevdE) { + transformed->a[y][x] = 0.f; + transformed->b[y] [x] = (difb); + } + + break; + } + + case 1: { // inside transition zone + float difL = tmp1->L[y][x] - original->L[y][x]; + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + float flia = 1.f, flib = 1.f; + flia = flib = ((100.f + realstrchdE) / 100.f); + const float chra = tmp1->a[y][x]; + const float chrb = tmp1->b[y][x]; + + float factorx = 1.f - localFactor; + difL *= factorx; + + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (!lp.actsp) { + difa = chra * flia - original->a[y][x]; + difb = chrb * flib - original->b[y][x]; + difa *= factorx; + difb *= factorx; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + } + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewbl) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = (difb); + } + + break; + } + + case 2: { // inside selection => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + + if (!lp.actsp) { + + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + } + } + } + } + } + } +} + +static void mean_fab(int xstart, int ystart, int bfw, int bfh, LabImage* bufexporig, const LabImage* original, float &fab, float &meanfab, float chrom, bool multiThread) +{ + const int nbfab = bfw * bfh; + + meanfab = 0.f; + fab = 50.f; + + if (nbfab > 0) { + double sumab = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:sumab) if(multiThread) +#else + static_cast(multiThread); +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; + bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; + sumab += std::fabs(bufexporig->a[y][x]); + sumab += std::fabs(bufexporig->b[y][x]); + } + } + + meanfab = sumab / (2.f * nbfab); + + double som = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:som) if(multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + som += SQR(std::fabs(bufexporig->a[y][x]) - meanfab) + SQR(std::fabs(bufexporig->b[y][x]) - meanfab); + } + } + + const float multsigma = (chrom >= 0.f ? 0.035f : 0.018f) * chrom + 1.f; + + const float stddv = std::sqrt(som / nbfab); + fab = meanfab + multsigma * stddv; + + if (fab <= 0.f) { + fab = 50.f; + } + } +} + +struct grad_params { + bool angle_is_zero, transpose, bright_top; + float ta, yc, xc; + float ys, ys_inv; + float scale, botmul, topmul; + float top_edge_0; + int h; +}; + +void calclocalGradientParams(const struct local_params& lp, struct grad_params& gp, float ystart, float xstart, int bfw, int bfh, int indic) +{ + int w = bfw; + int h = bfh; + float stops = 0.f; + float angs = 0.f; + + if (indic == 0) { + stops = -lp.strmaexp; + angs = lp.angmaexp; + } else if (indic == 1) { + stops = lp.strexp; + angs = lp.angexp; + } else if (indic == 2) { + stops = lp.strSH; + angs = lp.angSH; + } else if (indic == 3) { + stops = lp.strcol; + angs = lp.angcol; + } else if (indic == 4) { + float redu = 1.f; + + if (lp.strcolab > 0.f) { + redu = 0.6f; + } else { + redu = 0.15f; + } + + stops = redu * lp.strcolab; + angs = lp.angcol; + } else if (indic == 5) { + stops = lp.strcolab; + angs = lp.angcol; + } else if (indic == 6) { + stops = lp.strcolh; + angs = lp.angcol; + } else if (indic == 7) { + stops = lp.strvib; + angs = lp.angvib; + } else if (indic == 8) { + float redu = 1.f; + + if (lp.strvibab > 0.f) { + redu = 0.7f; + } else { + redu = 0.5f; + } + + stops = redu * lp.strvibab; + angs = lp.angvib; + } else if (indic == 9) { + stops = lp.strvibh; + angs = lp.angvib; + } else if (indic == 10) { + stops = std::fabs(lp.strwav); + angs = lp.angwav; + } else if (indic == 11) { + stops = lp.strlog; + angs = lp.anglog; + } else if (indic == 12) { + stops = -lp.str_mas; + angs = lp.ang_mas; + } + + + double gradient_stops = stops; + double gradient_center_x = LIM01((lp.xc - xstart) / bfw); + double gradient_center_y = LIM01((lp.yc - ystart) / bfh); + double gradient_angle = angs / 180.0 * rtengine::RT_PI; + double varfeath = 0.01 * lp.feath; + + //printf("xstart=%f ysta=%f lpxc=%f lpyc=%f stop=%f bb=%f cc=%f ang=%f ff=%d gg=%d\n", xstart, ystart, lp.xc, lp.yc, gradient_stops, gradient_center_x, gradient_center_y, gradient_angle, w, h); + + // make 0.0 <= gradient_angle < 2 * rtengine::RT_PI + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); + + if (gradient_angle < 0.0) { + gradient_angle += 2.0 * rtengine::RT_PI; + } + + gp.bright_top = false; + gp.transpose = false; + gp.angle_is_zero = false; + gp.h = h; + double cosgrad = cos(gradient_angle); + + if (std::fabs(cosgrad) < 0.707) { + // we transpose to avoid division by zero at 90 degrees + // (actually we could transpose only for 90 degrees, but this way we avoid + // division with extremely small numbers + gp.transpose = true; + gradient_angle += 0.5 * rtengine::RT_PI; + double gxc = gradient_center_x; + gradient_center_x = 1.0 - gradient_center_y; + gradient_center_y = gxc; + } + + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); + + if (gradient_angle > 0.5 * rtengine::RT_PI && gradient_angle < rtengine::RT_PI) { + gradient_angle += rtengine::RT_PI; + gp.bright_top = true; + } else if (gradient_angle >= rtengine::RT_PI && gradient_angle < 1.5 * rtengine::RT_PI) { + gradient_angle -= rtengine::RT_PI; + gp.bright_top = true; + } + + if (std::fabs(gradient_angle) < 0.001 || std::fabs(gradient_angle - 2 * rtengine::RT_PI) < 0.001) { + gradient_angle = 0; + gp.angle_is_zero = true; + } + + if (gp.transpose) { + gp.bright_top = !gp.bright_top; + std::swap(w, h); + } + + gp.scale = 1.0 / pow(2, gradient_stops); + + if (gp.bright_top) { + gp.topmul = 1.0; + gp.botmul = gp.scale; + } else { + gp.topmul = gp.scale; + gp.botmul = 1.0; + } + + gp.ta = tan(gradient_angle); + gp.xc = w * gradient_center_x; + gp.yc = h * gradient_center_y; + gp.ys = std::sqrt((float)h * h + (float)w * w) * (varfeath / cos(gradient_angle)); + gp.ys_inv = 1.0 / gp.ys; + gp.top_edge_0 = gp.yc - gp.ys / 2.0; + + if (gp.ys < 1.0 / h) { + gp.ys_inv = 0; + gp.ys = 0; + } +} + +void ImProcFunctions::blendstruc(int bfw, int bfh, LabImage* bufcolorig, float radius, float stru, array2D & blend2, int sk, bool multiThread) +{ + SobelCannyLuma(blend2, bufcolorig->L, bfw, bfh, radius); + float rm = 20.f / sk; + + if (rm > 0) { + float **mb = blend2; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + array2D ble(bfw, bfh); + array2D guid(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float X, Y, Z; + float L = bufcolorig->L[ir][jr]; + float a = bufcolorig->a[ir][jr]; + float b = bufcolorig->b[ir][jr]; + Color::Lab2XYZ(L, a, b, X, Y, Z); + + guid[ir][jr] = Y / 32768.f; + + blend2[ir][jr] /= 32768.f; + } + } + + const float blur = 25 / sk * (10.f + 1.2f * stru); + + rtengine::guidedFilter(guid, blend2, ble, blur, 0.001, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + blend2[ir][jr] = 32768.f * ble[ir][jr]; + } + } +} + + +static void blendmask(const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* original, LabImage* bufmaskor, LabImage* originalmas, float bl, float blab, int inv) +{ +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh ; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (inv == 0) { + if (zone > 0) { + bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); + bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); + + bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); + bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); + bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); + + originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); + originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); + originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); + + original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + + } + } else if (inv == 1) { + localFactor = 1.f - localFactor; + + if (zone < 2) { + bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); + bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); + + bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); + bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); + bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); + + originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); + originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); + originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); + + switch (zone) { + case 0: { + original->L[y + ystart][x + xstart] += (bl * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + break; + } + + case 1: { + original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + } + + } + } + + } + } + } +} + +void ImProcFunctions::deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh) +{ + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + const float refL = lumaref; + + const float kL = balance; + const float kab = balancedeltaE(kL); + const float kH = balanceh; + const float kch = balancedeltaE(kH); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + const float abdelta2 = SQR(refa - bufcolorig->a[y][x] / 327.68f) + SQR(refb - bufcolorig->b[y][x] / 327.68f); + const float chrodelta2 = SQR(std::sqrt(SQR(bufcolorig->a[y][x]) + SQR(bufcolorig->b[y][x])) / 327.68f - chromaref); + const float huedelta2 = abdelta2 - chrodelta2; + const float tempdE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - bufcolorig->L[y][x] / 327.68f)); + + float reducdE; + if (tempdE > maxdE) { + reducdE = 0.f; + } else if (tempdE > mindE && tempdE <= maxdE) { + const float ar = 1.f / (mindE - maxdE); + const float br = - ar * maxdE; + reducdE = pow(ar * tempdE + br, iterat); + } else { + reducdE = 1.f; + } + + if (scope > limscope) { + if (tempdE > maxdElim) { + reducdE = 0.f; + } else if (tempdE > mindElim && tempdE <= maxdElim) { + const float arlim = 1.f / (mindElim - maxdElim); + const float brlim = - arlim * maxdElim; + const float reducdElim = pow(arlim * tempdE + brlim, iterat); + const float aalim = (1.f - reducdElim) / 20.f; + const float bblim = 1.f - 100.f * aalim; + reducdE = aalim * scope + bblim; + } else { + reducdE = 1.f; + } + } + + rdE[y][x] = reducdE ; + } + } +} + +static void showmask(int lumask, const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* transformed, LabImage* bufmaskorigSH, int inv) +{ +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (inv == 0) { + if (zone > 0) {//normal + transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); + transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; + transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; + } + } else if (inv == 1) { //inverse + if (zone == 0) { + transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); + transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; + transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; + } + } + } + } +} + +void ImProcFunctions::discrete_laplacian_threshold(float * data_out, const float * data_in, size_t nx, size_t ny, float t) +{ + BENCHFUN + + if (!data_in || !data_out) { + fprintf(stderr, "a pointer is NULL and should not be so\n"); + abort(); + } + + /* pointers to the data and neighbour values */ + /* + * y-1 + * x-1 ptr x+1 + * y+1 + * <---------------------nx-------> + */ + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (size_t j = 0; j < ny; j++) { + const float* ptr_in = &data_in[j * nx]; + float* ptr_out = &data_out[j * nx]; + for (size_t i = 0; i < nx; i++) { + float val = 0.f; + /* row differences */ + if (0 < i) { + const float diff = ptr_in[i] - ptr_in[i - 1]; + val += std::fabs(diff) > t ? diff : 0.f; + } + + if (nx - 1 > i) { + const float diff = ptr_in[i] - ptr_in[i + 1];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + /* column differences */ + if (0 < j) { + const float diff = ptr_in[i] - ptr_in[i - nx];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + if (ny - 1 > j) { + const float diff = ptr_in[i] - ptr_in[i + nx];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + ptr_out[i] = val; + } + } + +} + +float *ImProcFunctions::cos_table(size_t size) +{ + float *table = NULL; + + /* allocate the cosinus table */ + if (NULL == (table = (float *) malloc(sizeof(*table) * size))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + /* + * fill the cosinus table, + * table[i] = cos(i Pi / n) for i in [0..n[ + */ + const double pi_size = rtengine::RT_PI / size; + + for (size_t i = 0; i < size; i++) { + table[i] = std::cos(pi_size * i); + } + + return table; +} + + +void ImProcFunctions::rex_poisson_dct(float * data, size_t nx, size_t ny, double m) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * some adaptations for Rawtherapee + */ + BENCHFUN + + /* + * get the cosinus tables + * cosx[i] = cos(i Pi / nx) for i in [0..nx[ + * cosy[i] = cos(i Pi / ny) for i in [0..ny[ + */ + + float* cosx = cos_table(nx); + float* cosy = cos_table(ny); + + /* + * we will now multiply data[i, j] by + * m / (4 - 2 * cosx[i] - 2 * cosy[j])) + * and set data[0, 0] to 0 + */ + float m2 = m / 2.; + /* + * after that, by construction, we always have + * cosx[] + cosy[] != 2. + */ + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (size_t i = 0; i < ny; ++i) { + for (size_t j = 0; j < nx; ++j) { + data[i * nx + j] *= m2 / (2.f - cosx[j] - cosy[i]); + } + } + // handle the first value, data[0, 0] = 0 + data[0] = 0.f; + + free(cosx); + free(cosy); + +} + +void ImProcFunctions::mean_dt(const float* data, size_t size, double& mean_p, double& dt_p) +{ + + double mean = 0.; + double dt = 0.; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:mean,dt) if(multiThread) +#endif + for (size_t i = 0; i < size; i++) { + mean += data[i]; + dt += SQR(data[i]); + } + + mean /= size; + dt /= size; + dt -= SQR(mean); + mean_p = mean; + dt_p = std::sqrt(dt); +} + +void ImProcFunctions::normalize_mean_dt(float * data, const float * ref, size_t size, float mod, float sigm) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * adapted for Rawtherapee - jacques Desmis july 2019 + */ + + if (NULL == data || NULL == ref) { + fprintf(stderr, "a pointer is NULL and should not be so\n"); + abort(); + } + + double mean_ref, mean_data, dt_ref, dt_data; + + /* compute mean and variance of the two arrays */ + mean_dt(ref, size, mean_ref, dt_ref); + mean_dt(data, size, mean_data, dt_data); + + /* compute the normalization coefficients */ + const double a = dt_ref / dt_data; + const double b = mean_ref - a * mean_data; + + const float modma = mod * a; + const float sigmmmodmb = sigm * mod * b; + const float onesmod = 1.f - mod; + /* normalize the array */ + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (size_t i = 0; i < size; i++) { + data[i] = (modma * data[i] + sigmmmodmb) + onesmod * ref[i];//normalize mean and stdv and balance PDE + } + +} + +void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw, int bfh, float thresh, float multy, float * dE, int show, int dEenable, int normalize) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * adapted for Rawtherapee by Jacques Desmis 6-2019 + */ + + BENCHFUN +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } +#endif + float *data_fft, *data_fft04, *data_tmp, *data, *data_tmp04; + float *datashow = nullptr; + + if (show != 0) { + if (NULL == (datashow = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + } + + if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data_tmp04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + //first call to laplacian with plein strength + ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); + + if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (show == 1) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_tmp[y * bfw + x]; + } + } + } + + //second call to laplacian with 40% strength ==> reduce effect if we are far from ref (deltaE) + ImProcFunctions::discrete_laplacian_threshold(data_tmp04, datain, bfw, bfh, 0.4f * thresh); + + if (NULL == (data_fft04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + //execute first + const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw); + + //execute second + if (dEenable == 1) { + const auto dct_fw04 = fftwf_plan_r2r_2d(bfh, bfw, data_tmp04, data_fft04, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw04); + fftwf_destroy_plan(dct_fw04); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) {//mix two fftw Laplacian : plein if dE near ref + for (int x = 0; x < bfw; x++) { + float prov = pow(dE[y * bfw + x], 4.5f); + data_fft[y * bfw + x] = prov * data_fft[y * bfw + x] + (1.f - prov) * data_fft04[y * bfw + x]; + } + } + } + + if (show == 2) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_fft[y * bfw + x]; + } + } + } + + fftwf_free(data_fft04); + fftwf_free(data_tmp); + fftwf_free(data_tmp04); + + /* solve the Poisson PDE in Fourier space */ + /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ + ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); + + if (show == 3) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_fft[y * bfw + x]; + } + } + } + + const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_bw); + fftwf_destroy_plan(dct_fw); + fftwf_destroy_plan(dct_bw); + fftwf_free(data_fft); + fftwf_cleanup(); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif + if (show != 4 && normalize == 1) { + normalize_mean_dt(data, datain, bfw * bfh, 1.f, 1.f); + } + + if (show == 0 || show == 4) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * data[y * bfw + x]); + } + } + } else if (show == 1 || show == 2 || show == 3) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * datashow[y * bfw + x]); + } + } + + fftwf_free(datashow); + } + + fftwf_free(data); + +} + +void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int xstart, int ystart, int sk, int cx, int cy, LabImage* bufcolorig, LabImage* bufmaskblurcol, LabImage* originalmaskcol, LabImage* original, LabImage* reserved, int inv, struct local_params & lp, + float strumask, bool astool, + const LocCCmaskCurve & locccmasCurve, bool lcmasutili, + const LocLLmaskCurve & locllmasCurve, bool llmasutili, + const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, + bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, float blendmab, int shado, float amountcd, float anchorcd, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, + int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, + bool fftt, float blu_ma, float cont_ma, int indic + ) + + +{ + array2D ble(bfw, bfh); + array2D blechro(bfw, bfh); + array2D hue(bfw, bfh); + array2D guid(bfw, bfh); + const std::unique_ptr bufreserv(new LabImage(bfw, bfh)); + float meanfab, fab; + mean_fab(xstart, ystart, bfw, bfh, bufcolorig, original, fab, meanfab, chrom, multiThread); + float chromult = 1.f - 0.01f * chrom; + float kinv = 1.f; + float kneg = 1.f; + + if (invmask) { + kinv = 0.f; + kneg = -1.f; + } + + if (deltaE || modmask || enaMask || showmaske) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufmaskblurcol->L[y][x] = original->L[y + ystart][x + xstart]; + bufmaskblurcol->a[y][x] = original->a[y + ystart][x + xstart]; + bufmaskblurcol->b[y][x] = original->b[y + ystart][x + xstart]; + bufreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; + } + } + + JaggedArray blendstru(bfw, bfh); + + if (blu_ma >= 0.25f && strumask == 0.f) { + strumask = 0.1f; // to enable a small mask make FFT good ...why ?? + } + + if (strumask > 0.f) { + float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 + buildBlendMask(bufcolorig->L, blendstru, bfw, bfh, delstrumask); + float radblur = 0.02f * std::fabs(0.1f * rad);//empirical value + float rm = radblur / sk; + + if (rm > 0) { + float **mb = blendstru; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + } + + JaggedArray blendblur(bfw, bfh); + + JaggedArray blur(bfw, bfh); + + if (cont_ma > 0.f) { + float contra = cont_ma; + buildBlendMask(bufcolorig->L, blendblur, bfw, bfh, contra); + + + float radblur = 0.25f + 0.002f * std::fabs(rad);//empirical value + float rm = radblur / sk; + + if (fftt) { + if (rm < 0.3f) { + rm = 0.3f; + } + } + + if (rm > 0) { + float **mb = blendblur; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + if (blu_ma >= 0.25f) { + if (!fftt) { // || (lp.fftColorMask && call != 2)) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufcolorig->L, blur, bfw, bfh, blu_ma / sk); + } + } else { + ImProcFunctions::fftw_convol_blur2(bufcolorig->L, blur, bfw, bfh, blu_ma / sk, 0, 0); + } + + for (int i = 0; i < bfh; i++) { + for (int j = 0; j < bfw; j++) { + blur[i][j] = intp(blendblur[i][j], bufcolorig->L[i][j], rtengine::max(blur[i][j], 0.0f)); + } + } + } + } + + bool HHmaskcurve = false; + + if (lochhhmasCurve && lhhmasutili) { + for (int i = 0; i < 500; i++) { + if (lochhhmasCurve[i] != 0.5) { + HHmaskcurve = true; + } + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[bfw] ALIGNED64; +// float atan2BufferH[bfw] ALIGNED64; +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + for (int ir = 0; ir < bfh; ir++) { +#ifdef __SSE2__ + + if (lochhmasCurve && lhmasutili) { + int i = 0; + + for (; i < bfw - 3; i += 4) { + STVF(atan2Buffer[i], xatan2f(LVFU(bufcolorig->b[ir][i]), LVFU(bufcolorig->a[ir][i]))); + } + + for (; i < bfw; i++) { + atan2Buffer[i] = xatan2f(bufcolorig->b[ir][i], bufcolorig->a[ir][i]); + } + } + +#endif + + for (int jr = 0; jr < bfw; jr++) { + float kmaskL = 0.f; + float kmaskC = 0.f; + float kmaskHL = 0.f; + float kmaskH = 0.f; + float kmasstru = 0.f; + float kmasblur = 0.f; + + if (strumask > 0.f && !astool) { + kmasstru = bufcolorig->L[ir][jr] * blendstru[ir][jr]; + } + + if (cont_ma > 0.f) { + + if (blu_ma >= 0.25f) { + + float prov = intp(blendstru[ir][jr], bufcolorig->L[ir][jr], rtengine::max(blur[ir][jr], 0.0f)); + kmasblur = bufcolorig->L[ir][jr] - prov ; + + } + } + + if (locllmasCurve && llmasutili) { + // printf("s"); + kmaskL = 32768.f * LIM01(kinv - kneg * locllmasCurve[(500.f / 32768.f) * bufcolorig->L[ir][jr]]); + + } + + if (!deltaE && locccmasCurve && lcmasutili) { + kmaskC = LIM01(kinv - kneg * locccmasCurve[500.f * (0.0001f + std::sqrt(SQR(bufcolorig->a[ir][jr]) + SQR(bufcolorig->b[ir][jr])) / fab)]); + } + + if (lochhmasCurve && lhmasutili) { +#ifdef __SSE2__ + const float huema = atan2Buffer[jr]; +#else + const float huema = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); +#endif + float h = Color::huelab_to_huehsv2(huema); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + const float valHH = LIM01(kinv - kneg * lochhmasCurve[500.f * h]); + + if (!deltaE) { + kmaskH = valHH; + } + + kmaskHL = 32768.f * valHH; + } + + /* + //keep here in case of...but !! + if (lochhhmasCurve && HHmaskcurve) { + + #ifdef __SSE2__ + huemah = atan2BufferH[jr]; + #else + huemah = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); + #endif + + float hh = Color::huelab_to_huehsv2(huemah); + hh += 1.f / 6.f; + + if (hh > 1.f) { + hh -= 1.f; + } + + const float val_HH = float (LIM01(((0.5f - lochhhmasCurve[500.f * hh])))); + kmaskHH = 2.f * val_HH; + const float hhro = kmaskHH; + + if (hhro != 0) { + newhr = huemah + hhro; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + } + sincosval = xsincosf(newhr); + + } + */ + bufmaskblurcol->L[ir][jr] = clipLoc(kmaskL + kmaskHL + kmasstru + kmasblur); + bufmaskblurcol->a[ir][jr] = clipC((kmaskC + chromult * kmaskH)); + bufmaskblurcol->b[ir][jr] = clipC((kmaskC + chromult * kmaskH)); + + if (shortcu == 1) { //short circuit all L curve + bufmaskblurcol->L[ir][jr] = 32768.f - bufcolorig->L[ir][jr]; + } + + ble[ir][jr] = bufmaskblurcol->L[ir][jr] / 32768.f; + hue[ir][jr] = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); + const float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); + + blechro[ir][jr] = chromah / 32768.f;//must be good perhaps more or less, only incidence on LIM blea bleb + guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; + + } + } + } + + std::unique_ptr bufprov; + if (delt) { + bufprov.reset(new LabImage(bfw, bfh)); + bufprov->CopyFrom(bufmaskblurcol, multiThread); + } + + if (rad != 0.f) { + const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); + const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); + + constexpr float epsilmax = 0.005f; + constexpr float epsilmin = 0.00001f; + + constexpr float aepsil = (epsilmax - epsilmin) / 100.f; + constexpr float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); + } + + LUTf lutTonemaskexp(65536); + calcGammaLut(gamma, slope, lutTonemaskexp); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float2 sincosval = xsincosf(hue[ir][jr]); + bufmaskblurcol->L[ir][jr] = lutTonemaskexp[ble[ir][jr] * 65536.f]; + bufmaskblurcol->a[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.y; + bufmaskblurcol->b[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.x; + } + } + + + if (strumask > 0.f && astool) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] *= (1.f + blendstru[ir][jr]); + } + } + } + + if (lmasklocalcurve && localmaskutili) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] = 0.5f * lmasklocalcurve[2.f * bufmaskblurcol->L[ir][jr]]; + } + } + + if (shado > 0) { + ImProcFunctions::shadowsHighlights(bufmaskblurcol, true, 1, 0, shado, 40, sk, 0, 60); + } + + int wavelet_level = level_br; + + int minwin = rtengine::min(bfw, bfh); + int maxlevelspot = 9; + + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + int maxlvl = wavelet_level; +// float contrast = 0.f; + bool wavcurvemask = false; + + if (loclmasCurvecolwav && lmasutilicolwav) { + for (int i = 0; i < 500; i++) { + if (loclmasCurvecolwav[i] != 0.5) { + wavcurvemask = true; + } + } + } + + if (wavcurvemask) { +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + wavelet_decomposition *wdspot = new wavelet_decomposition(bufmaskblurcol->L[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + if (wdspot->memory_allocation_failed()) { + return; + } + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alow = 1.f; + float blow = 0.f; + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspot->level_W(level); + int H_L = wdspot->level_H(level); + float* const* wav_L = wdspot->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + float insigma = 0.666f; //SD + float logmax = log(MaxP[level]); //log Max + float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max + float inx = log(insigma); + float iny = log(rapX); + float rap = inx / iny; //koef + float asig = 0.166f / (sigma[level]); + float bsig = 0.5f - asig * mean[level]; + float amean = 0.5f / mean[level]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + if(loclmasCurvecolwav && lmasutilicolwav) { + float absciss; + float &val = wav_L[dir][i]; + + if (fabsf(val) >= (mean[level] + sigma[level])) { //for max + float valcour = xlogf(fabsf(val)); + float valc = valcour - logmax; + float vald = valc * rap; + absciss = xexpf(vald); + } else if (fabsf(val) >= mean[level]) { + absciss = asig * fabsf(val) + bsig; + } else { + absciss = amean * fabsf(val); + } + + float klev = 1.f; + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + + float kc = klev * (loclmasCurvecolwav[absciss * 500.f] - 0.5f); + float amplieffect = kc <= 0.f ? 1.f : 4.f; + + float kinterm = 1.f + amplieffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + val *= kinterm; + + } + } + } + + } + } + + wdspot->reconstruct(bufmaskblurcol->L[0], 1.f); + delete wdspot; + + } + + if (lochhhmasCurve && HHmaskcurve) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float huemah = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); + float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); + + + float hh = Color::huelab_to_huehsv2(huemah); + hh += 1.f / 6.f; + + if (hh > 1.f) { + hh -= 1.f; + } + + const float val_HH = float ((0.5f - lochhhmasCurve[500.f * hh])); + const float hhro = 1.5f * val_HH; + float newhr = 0.f; + + if (hhro != 0) { + newhr = huemah + hhro;//we add radians and other dim between 0 1.. always radians but addition "false" + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + } + + float2 sincosval = xsincosf(newhr); + bufmaskblurcol->a[ir][jr] = clipC(chromah * sincosval.y); + bufmaskblurcol->b[ir][jr] = clipC(chromah * sincosval.x); + + } + } + + if (amountcd > 1.f) { //dynamic range compression for Mask + FattalToneMappingParams fatParams; + fatParams.enabled = true; + fatParams.threshold = 100.f; + fatParams.amount = amountcd; + fatParams.anchor = anchorcd; + int nlev = 1; + Imagefloat *tmpImagefat = nullptr; + tmpImagefat = new Imagefloat(bfw, bfh); + lab2rgb(*bufmaskblurcol, *tmpImagefat, params->icm.workingProfile); + ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0); + rgb2lab(*tmpImagefat, *bufmaskblurcol, params->icm.workingProfile); + delete tmpImagefat; + } + + if (delt) { + const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); + float** rdE = *(rdEBuffer.get()); + + deltaEforMask(rdE, bfw, bfh, bufreserv.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, lp.balance, lp.balanceh); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float rdEval = rdE[ir][jr]; + bufmaskblurcol->L[ir][jr] = bufprov->L[ir][jr] + rdEval * (bufmaskblurcol->L[ir][jr] - bufprov->L[ir][jr]); + bufmaskblurcol->a[ir][jr] = bufprov->a[ir][jr] + rdEval * (bufmaskblurcol->a[ir][jr] - bufprov->a[ir][jr]); + bufmaskblurcol->b[ir][jr] = bufprov->b[ir][jr] + rdEval * (bufmaskblurcol->b[ir][jr] - bufprov->b[ir][jr]); + } + } + } + + struct grad_params gp; + + if ((indic == 0 && lp.strmaexp != 0.f) || (indic ==12 && lp.str_mas != 0.f)) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, indic); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); + } + } + } + + if (lap > 0.f) { + const float *datain = bufmaskblurcol->L[0]; + const std::unique_ptr data_tmp(new float[bfh * bfw]); + + if (!pde) { + ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, bfw, bfh, 200.f * lap); + } else { + ImProcFunctions::retinex_pde(datain, data_tmp.get(), bfw, bfh, 12.f * lap, 1.f, nullptr, 0, 0, 1); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufmaskblurcol->L[y][x] = data_tmp[y * bfw + x]; + } + } + } + } + + const float radiusb = 1.f / sk; + + if (deltaE || modmask || enaMask || showmaske) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufmaskblurcol->L, bufmaskblurcol->L, bfw, bfh, radiusb); + gaussianBlur(bufmaskblurcol->a, bufmaskblurcol->a, bfw, bfh, 1.f + (0.5f * rad) / sk); + gaussianBlur(bufmaskblurcol->b, bufmaskblurcol->b, bfw, bfh, 1.f + (0.5f * rad) / sk); + } + + if (zero || modif || modmask || deltaE || enaMask) { + originalmaskcol->CopyFrom(bufcolorig, multiThread); + blendmask(lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig, original, bufmaskblurcol, originalmaskcol, blendm, blendmab, inv); + } + } +} + +void ImProcFunctions::InverseSharp_Local(float **loctemp, const float hueref, const float lumaref, const float chromaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ +//local sharp + // BENCHFUN + const float ach = lp.trans / 100.f; + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + const bool sharshow = lp.showmasksharmet == 1; + const bool previewshar = lp.showmasksharmet == 2; + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + constexpr float aadark = -1.f; + constexpr float bbdark = 5000.f; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + float radius = 3.f / sk; +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * lp.senssha * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.senssha * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.senssha); + + switch (zone) { + case 0: { // outside selection and outside transition zone => full effect, no transition + const float difL = loctemp[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar) { + float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! + float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + + } + + break; + } + + case 1: { // inside transition zone + const float difL = (loctemp[y][x] - original->L[y][x]) * (1.f - localFactor); + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar || lp.prevdE) { + const float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! + const float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + break; + } + + case 2: { // inside selection => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + } + } + } + } + } +} + +void ImProcFunctions::Sharp_Local(int call, float **loctemp, int senstype, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + BENCHFUN + const float ach = lp.trans / 100.f; + const float varsens = senstype == 1 ? lp.senslc : lp.senssha; + const bool sharshow = (lp.showmasksharmet == 1); + const bool previewshar = (lp.showmasksharmet == 2); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + float darklim = 5000.f; + float aadark = -1.f; + float bbdark = darklim; + + const int GW = transformed->W; + const int GH = transformed->H; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const float radius = 3.f / sk; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const int begy = int (lp.yc - lp.lyT); + const int begx = int (lp.xc - lp.lxL); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + for (int x = 0; x < transformed->W; x++) { + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + //deltaE + const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + + float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + const float reducview = reducdE; + reducdE *= localFactor; + + float difL; + + if (call == 2) { + difL = loctemp[loy - begy][lox - begx] - original->L[y][x]; + } else { + difL = loctemp[y][x] - original->L[y][x]; + } + + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar || lp.prevdE) { + float difbdisp = reducview * 10000.f * lp.colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + transformed->L[y][x] = transformed->L[y][x] * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::Exclude_Local(float **deltaso, float hueref, float chromaref, float lumaref, float sobelref, float meansobel, const struct local_params & lp, const LabImage * original, LabImage * transformed, const LabImage * rsv, const LabImage * reserv, int cx, int cy, int sk) +{ + + BENCHFUN { + const float ach = lp.trans / 100.f; + const float varsens = lp.sensexclu; + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + const int GW = transformed->W; + const int GH = transformed->H; + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + // lumaref *= 327.68f; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + //sobel + sobelref = rtengine::min(sobelref / 100.f, 60.f); + + const bool recip = sobelref < meansobel && sobelref < lp.stru; + + sobelref = log1p(sobelref); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + const float radius = 3.f / sk; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(reserv->L, origblur->L, GW, GH, radius); + gaussianBlur(reserv->a, origblur->a, GW, GH, radius); + gaussianBlur(reserv->b, origblur->b, GW, GH, radius); + + +#ifdef _OPENMP + #pragma omp barrier + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) + { + const int loy = cy + y; + const bool isZone0 = loy > (lp.yc + lp.ly - 1) || loy < lp.yc - lp.lyT; // // -1 fix issue 5554 + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + for (int x = 0; x < transformed->W; x++) { + transformed->L[y][x] = original->L[y][x]; + } + + continue; + } + + for (int x = 0; x < transformed->W; x++) { + const int lox = cx + x; + const bool isZone0x = lox > (lp.xc + lp.lx - 1) || lox < lp.xc - lp.lxL; // -1 fix issue 5554 + + if (isZone0x) { // outside selection and outside transition zone => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + continue; + } + + const int begx = int (lp.xc - lp.lxL); + const int begy = int (lp.yc - lp.lyT); + + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + continue; + } + + float rs = 0.f; + + const float csob = xlogf(1.f + rtengine::min(deltaso[loy - begy][lox - begx] / 100.f, 60.f) + 0.001f); + + if (!recip) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + float affsob = 1.f; + + if (lp.struexc > 0.f && rs > 0.f) { + const float rsob = 0.002f * lp.struexc * rs; + const float minrs = 1.3f + 0.05f * lp.stru; + + if (rs < minrs) { + affsob = 1.f; + } else { + affsob = 1.f / pow_F((1.f + rsob), SQR(SQR(rs - minrs))); + } + } + + float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + const float rL = origblur->L[y][x]; + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + + if (rL > 32.768f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (zone > 0) { + + const float difL = (rsv->L[loy - begy][lox - begx] - original->L[y][x]) * localFactor; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * affsob * reducdE); + + const float difa = (rsv->a[loy - begy][lox - begx] - original->a[y][x]) * localFactor; + transformed->a[y][x] = clipC(original->a[y][x] + difa * affsob * reducdE); + + const float difb = (rsv->b[loy - begy][lox - begx] - original->b[y][x]) * localFactor; + transformed->b[y][x] = clipC(original->b[y][x] + difb * affsob * reducdE); + + } + } + } + } + } + } +} + + + +void ImProcFunctions::transit_shapedetect_retinex(int call, int senstype, LabImage * bufexporig, LabImage * bufmask, LabImage * buforigmas, float **buflight, float **bufchro, const float hueref, const float chromaref, const float lumaref, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + + BENCHFUN { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + + + const float ach = lp.trans / 100.f; + const float varsens = lp.sensh; + + int GW = transformed->W; + int GH = transformed->H; + + // const float refa = chromaref * cos(hueref); + // const float refb = chromaref * sin(hueref); + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + const bool retishow = ((lp.showmaskretimet == 1 || lp.showmaskretimet == 2)); + const bool previewreti = ((lp.showmaskretimet == 4)); + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const bool showmas = lp.showmaskretimet == 3 ; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float radius = 3.f / sk; + const bool usemaskreti = lp.enaretiMask && senstype == 4 && !lp.enaretiMasktmap; + float strcli = 0.03f * lp.str; + + if (lp.scalereti == 1) + { + strcli = 0.015 * lp.str; + } + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float previewint = settings->previewselection; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) + { + const int loy = cy + y; + + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float rL = origblur->L[y][x] / 327.68f; + float dE; + float abdelta2 = 0.f; + float chrodelta2 = 0.f; + float huedelta2 = 0.f; + + if (!usemaskreti) { + abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + } else { + if (call == 2) { + abdelta2 = SQR(refa - buforigmas->a[y - ystart][x - xstart]) + SQR(refb - buforigmas->b[y - ystart][x - xstart]); + chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y - ystart][x - xstart]) + SQR(buforigmas->b[y - ystart][x - xstart])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y - ystart][x - xstart])); + + } else { + abdelta2 = SQR(refa - buforigmas->a[y][x]) + SQR(refb - buforigmas->b[y][x]); + chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y][x]) + SQR(buforigmas->b[y][x])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y][x])); + } + } + + float cli, clc; + + if (call == 2) { + cli = buflight[y - ystart][x - xstart]; + clc = previewreti ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + } else { + cli = buflight[y][x]; + clc = previewreti ? settings->previewselection * 100.f : bufchro[y][x]; + + } + + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens) / 100.f; + + cli *= reducdE; + clc *= reducdE; + cli *= (1.f + strcli); + + if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (senstype == 4) {//all except color and light (TODO) and exposure + float lightc; + + if (call == 2) { + lightc = bufexporig->L[y - ystart][x - xstart]; + } else { + lightc = bufexporig->L[y][x]; + } + + float fli = 1.f + cli; + float diflc = lightc * fli - original->L[y][x]; + diflc *= localFactor; + + if (!showmas) { + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + } else { + if (call == 2) { + + transformed->L[y][x] = bufmask->L[y - ystart][x - xstart]; + } else { + transformed->L[y][x] = bufmask->L[y][x]; + } + } ; + + if (retishow) { + transformed->L[y][x] = CLIP(12000.f + diflc); + } + } + + float fliab = 1.f; + float chra, chrb; + + if (call == 2) { + chra = bufexporig->a[y - ystart][x - xstart]; + chrb = bufexporig->b[y - ystart][x - xstart]; + } else { + chra = bufexporig->a[y][x]; + chrb = bufexporig->b[y][x]; + + } + + if (senstype == 5) { + fliab = 1.f + clc; + } + + const float difa = (chra * fliab - original->a[y][x]) * localFactor; + float difb = (chrb * fliab - original->b[y][x]) * localFactor; + + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + if (showmas) { + if (call == 2) { + transformed->a[y][x] = bufmask->a[y - ystart][x - xstart]; + transformed->b[y][x] = bufmask->b[y - ystart][x - xstart]; + } else { + transformed->a[y][x] = bufmask->a[y][x]; + transformed->b[y][x] = bufmask->b[y][x]; + + } + } + + if (retishow) { + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } + + if (previewreti) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = previewint * difb; + } + } + } + } + } + + if (showmas || retishow || previewreti) + { + return; + } + + } +} + + +void ImProcFunctions::transit_shapedetect(int senstype, const LabImage * bufexporig, LabImage * originalmask, float **bufchro, bool HHutili, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + + BENCHFUN + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfw = xend - xstart; + const int bfh = yend - ystart; + // printf("h=%f l=%f c=%f s=%f\n", hueref, lumaref, chromaref, sobelref); + const float ach = lp.trans / 100.f; + float varsens = lp.sensex; + + if (senstype == 6 || senstype == 7) //cbdl + { + varsens = lp.senscb; + } else if (senstype == 8) //TM + { + varsens = lp.senstm; + } else if (senstype == 10) //local contrast + { + varsens = lp.senslc; + } + + //sobel //keep in case of, not used + sobelref /= 100.f; + meansobel /= 100.f; + + sobelref = rtengine::min(sobelref, 60.f); + + const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images + + sobelref = log1p(sobelref); + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const float previewint = settings->previewselection; + + const bool cbshow = ((lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2) && senstype == 6); + const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); + const bool previewcb = ((lp.showmaskcbmet == 4) && senstype == 6); + const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); + + const std::unique_ptr origblur(new LabImage(bfw, bfh)); + std::unique_ptr origblurmask; + + float radius = 3.f / sk; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const bool usemaskcb = (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 4) && senstype == 6; + const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; + const bool usemaskall = (usemaskcb || usemasktm); + + if (usemaskall) + { + origblurmask.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); + gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); + gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); + } + } + if (lp.equtm && senstype == 8) //normalize luminance for Tone mapping , at this place we can use for others senstype! + { + float *datain = new float[bfh * bfw]; + float *data = new float[bfh * bfw]; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; + data[(y - ystart)* bfw + (x - xstart)] = bufexporig->L[y - ystart][x - xstart]; + } + + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; + } + + delete [] datain; + delete [] data; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) + { + for (int x = 0; x < bfw; x++) { + origblur->L[y][x] = original->L[y + ystart][x + xstart]; + origblur->a[y][x] = original->a[y + ystart][x + xstart]; + origblur->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); + gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); + gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); + + } + + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[transformed->W] ALIGNED16; +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) + { + const int loy = cy + y; + +#ifdef __SSE2__ + + if (HHutili || senstype == 7) { + int i = xstart; + + for (; i < xend - 3; i += 4) { + vfloat av = LVFU(origblur->a[y - ystart][i - xstart]); + vfloat bv = LVFU(origblur->b[y - ystart][i - xstart]); + STVFU(atan2Buffer[i], xatan2f(bv, av)); + } + + for (; i < xend; i++) { + atan2Buffer[i] = xatan2f(origblur->b[y - ystart][i - xstart], origblur->a[y - ystart][i - xstart]); + } + } + +#endif + + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float rhue = 0; + + if (HHutili || senstype == 7) { +#ifdef __SSE2__ + rhue = atan2Buffer[x]; +#else + rhue = xatan2f(origblur->b[y - ystart][x - xstart], origblur->a[y - ystart][x - xstart]); +#endif + } + + const float rL = origblur->L[y - ystart][x - xstart] / 327.68f; + float rsob = 0.f; + + if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0 || senstype == 100) && lp.struco > 0.f))) {//keep in case of, not used + const float csob = xlogf(1.f + rtengine::min(blend2[y - ystart][x - xstart] / 100.f, 60.f) + 0.001f); + + float rs; + + if (k) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + if (rs > 0.f && senstype == 1) { + rsob = 1.1f * lp.struexp * rs; + } else if (rs > 0.f && (senstype == 0 || senstype == 100)) { + rsob = 1.1f * lp.struco * rs; + } + } + + const float dE = rsob + std::sqrt(kab * (SQR(refa - maskptr->a[y - ystart][x - xstart]) + SQR(refb - maskptr->b[y - ystart][x - xstart])) + kL * SQR(refL - maskptr->L[y - ystart][x - xstart])); + const float clc = (previewcb) ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + const float realstrchdE = reducdE * clc; + + if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (zone > 0) { + float factorx = localFactor; + float difL = 0.f; + + if (senstype == 6 || senstype == 8 || senstype == 10) { + difL = (bufexporig->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + } + + if (senstype == 7) { + float difab = bufexporig->L[y - ystart][x - xstart] - std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); + float2 sincosval = xsincosf(rhue); + float difa = difab * sincosval.y; + float difb = difab * sincosval.x; + difa *= factorx * (100.f + realstrchdE) / 100.f; + difb *= factorx * (100.f + realstrchdE) / 100.f; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } else { + float flia = 1.f; + float flib = 1.f; + float chra = bufexporig->a[y - ystart][x - xstart]; + float chrb = bufexporig->b[y - ystart][x - xstart]; + + if (senstype == 3 || senstype == 30 || senstype == 8 || senstype == 6 || senstype == 10) { + flia = flib = ((100.f + realstrchdE) / 100.f); + } + + + float difa = chra * flia - original->a[y][x]; + float difb = chrb * flib - original->b[y][x]; + difa *= factorx; + difb *= factorx; + + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + + if (cbshow || tmshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewcb || previewtm || lp.prevdE) { + if (std::fabs(difb) < 500.f) { + difb += difL; + } + + transformed->a[y][x] = 0.f; + transformed->b[y][x] = previewint * difb; + } + } + } + } + } + } + } +} + +void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp, int senstype, struct local_params & lp, LabImage * originalmask, const LUTf& lightCurveloc, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& exlocalcurve, const LUTf& cclocalcurve, float adjustr, bool localcutili, const LUTf& lllocalcurve, bool locallutili, LabImage * original, LabImage * transformed, int cx, int cy, const float hueref, const float chromaref, const float lumaref, int sk) +{ + // BENCHFUN + const float ach = lp.trans / 100.f; + const float facc = (100.f + lp.chro) / 100.f; //chroma factor transition + float varsens = lp.sens; + + if (senstype == 0) { //Color and Light + varsens = lp.sens; + } else if (senstype == 1) { //exposure + varsens = lp.sensex; + } else if (senstype == 2) { //shadows highlight + varsens = lp.senshs; + } + + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + const std::unique_ptr temp(new LabImage(GW, GH)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + temp->L[y][x] = original->L[y][x]; + temp->a[y][x] = original->a[y][x]; + temp->b[y][x] = original->b[y][x]; + } + } + + if (senstype == 2) { // Shadows highlight + if (lp.shmeth == 0) { + ImProcFunctions::shadowsHighlights(temp.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); + } else if (lp.shmeth == 1) { + const std::unique_ptr tmpImage(new Imagefloat(GW, GH)); + + lab2rgb(*temp, *tmpImage, params->icm.workingProfile); + + if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB + const float gamtone = params->locallab.spots.at(sp).gamSH; + const float slotone = params->locallab.spots.at(sp).sloSH; + cmsHTRANSFORM dummy = nullptr; + workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); + workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); + } + + if (tonequ) { + tmpImage->normalizeFloatTo1(); + array2D Rtemp(GW, GH, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); + array2D Gtemp(GW, GH, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); + array2D Btemp(GW, GH, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); + tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, sk, multiThread); + tmpImage->normalizeFloatTo65535(); + } + + rgb2lab(*tmpImage, *temp, params->icm.workingProfile); + } + + } else if (senstype == 1) { //exposure + ImProcFunctions::exlabLocal(lp, GH, GW, original, temp.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + + if (exlocalcurve) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < temp->H; y++) { + for (int x = 0; x < temp->W; x++) { + const float lh = 0.5f * exlocalcurve[2.f * temp->L[y][x]]; // / ((lighn) / 1.9f) / 3.61f; //lh between 0 and 0 50 or more + temp->L[y][x] = lh; + } + } + } + + if (lp.expchroma != 0.f) { + const float ch = (1.f + 0.02f * lp.expchroma) ; + float chprosl; + + if (ch <= 1.f) {//convert data curve near values of slider -100 + 100, to be used after to detection shape + chprosl = 99.f * ch - 99.f; + } else { + constexpr float ampli = 70.f; + chprosl = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + const float epsi = original->L[y][x] == 0.f ? 0.001f : 0.f; + const float rapexp = temp->L[y][x] / (original->L[y][x] + epsi); + temp->a[y][x] *= (1.f + chprosl * rapexp); + temp->b[y][x] *= (1.f + chprosl * rapexp); + } + } + } + } else if (senstype == 0) { //Color and Light curves L C + if (cclocalcurve && localcutili) { // C=f(C) curve +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + //same as in "normal" + const float chromat = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); + constexpr float ampli = 25.f; + const float ch = (cclocalcurve[chromat * adjustr ]) / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more + const float chprocu = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 + temp->a[y][x] = original->a[y][x] * (1.f + 0.01f * chprocu); + temp->b[y][x] = original->b[y][x] * (1.f + 0.01f * chprocu); + + } + } + } + + if (lllocalcurve && locallutili) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + temp->L[y][x] = 0.5f * lllocalcurve[2.f * original->L[y][x]]; + } + } + } + } + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + std::unique_ptr origblurmask; + const bool usemaskcol = (lp.enaColorMaskinv) && senstype == 0; + const bool usemaskexp = (lp.enaExpMaskinv) && senstype == 1; + const bool usemasksh = (lp.enaSHMaskinv) && senstype == 2; + const bool usemaskall = (usemaskcol || usemaskexp || usemasksh); + + float radius = 3.f / sk; + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + + if (senstype == 1) { + radius = (2.f + 0.2f * lp.blurexp) / sk; + } else if (senstype == 0) { + radius = (2.f + 0.2f * lp.blurcol) / sk; + } else if (senstype == 2) { + radius = (2.f + 0.2f * lp.blurSH) / sk; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + const float rL = origblur->L[y][x] / 327.68f; + + if (std::fabs(origblur->b[y][x]) < 0.01f) { + origblur->b[y][x] = 0.01f; + } + + constexpr float th_r = 0.01f; + + if (rL > th_r) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor);//rect not good + } + + //deltaE + float reducdE; + if (zone != 2) { + const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + } + + switch (zone) { + case 2: { // outside selection and outside transition zone => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + break; + } + + case 1: { // inside transition zone + const float factorx = 1.f - localFactor; + + if (senstype == 0) { + const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; + const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; + float lumnew = original->L[y][x]; + const float difL = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); + const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); + const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); + const float facCa = 1.f + (difa / (original->a[y][x] + epsia)); + const float facCb = 1.f + (difb / (original->b[y][x] + epsib)); + + if (lp.sens < 75.f) { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve + } + + const float fac = (100.f + factorx * lp.chro * reducdE) / 100.f; //chroma factor transition + const float diflc = (lumnew - original->L[y][x]) * (reducdE * factorx); + + transformed->L[y][x] = CLIP(1.f * (original->L[y][x] + diflc + difL)); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } else { + const float fac = (100.f + factorx * lp.chro) / 100.f; //chroma factor transition + + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); + } + + const float diflc = (lumnew - original->L[y][x]) * factorx; + transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa); + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } + } else if (senstype == 1 || senstype == 2) { + const float diflc = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); + const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); + const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + transformed->a[y][x] = clipC(original->a[y][x] + difa) ; + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + break; + } + + case 0: { // inside selection => full effect, no transition + if (senstype == 0) { + const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; + const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; + float lumnew = original->L[y][x]; + const float difL = (temp->L[y][x] - original->L[y][x]) * reducdE; + const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; + const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; + const float facCa = 1.f + difa / (original->a[y][x] + epsia); + const float facCb = 1.f + difb / (original->b[y][x] + epsib); + + if (lp.sens < 75.f) { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve + } + + const float fac = (100.f + lp.chro * reducdE) / 100.f; //chroma factor transition + const float diflc = (lumnew - original->L[y][x]) * reducdE; + + transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } else { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); + } + + transformed->L[y][x] = CLIP(lumnew + difL) ; + transformed->a[y][x] = clipC(original->a[y][x] * facc * facCa); + transformed->b[y][x] = clipC(original->b[y][x] * facc * facCb); + } + } else if (senstype == 1 || senstype == 2) { + const float diflc = (temp->L[y][x] - original->L[y][x]) * reducdE; + const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; + const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + transformed->a[y][x] = clipC(original->a[y][x] + difa) ; + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + } + } + } + } + } + } +} + +void ImProcFunctions::calc_ref(int sp, LabImage * original, LabImage * transformed, int cx, int cy, int oW, int oH, int sk, double & huerefblur, double & chromarefblur, double & lumarefblur, double & hueref, double & chromaref, double & lumaref, double & sobelref, float & avg, const LocwavCurve & locwavCurveden, bool locwavdenutili) +{ + if (params->locallab.enabled) { + //always calculate hueref, chromaref, lumaref before others operations use in normal mode for all modules exceprt denoise + struct local_params lp; + calcLocalParams(sp, oW, oH, params->locallab, lp, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, locwavCurveden, locwavdenutili); + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + float avg2 = 0.f; + int nc2 = 0; + + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + avg2 += original->L[y][x]; + nc2++; + } + } + + avg2 /= 32768.f; + avg = avg2 / nc2; +// double precision for large summations + double aveA = 0.; + double aveB = 0.; + double aveL = 0.; + double aveChro = 0.; + double aveAblur = 0.; + double aveBblur = 0.; + double aveLblur = 0.; + double aveChroblur = 0.; + + double avesobel = 0.; +// int precision for the counters + int nab = 0; + int nso = 0; + int nsb = 0; +// single precision for the result + float avA, avB, avL; + int spotSize = 0.88623f * rtengine::max(1, lp.cir / sk); //18 + //O.88623 = std::sqrt(PI / 4) ==> sqare equal to circle + int spotSise2; // = 0.88623f * max (1, lp.cir / sk); //18 + + // very small region, don't use omp here + LabImage *sobelL; + LabImage *deltasobelL; + LabImage *origsob; + LabImage *origblur = nullptr; + LabImage *blurorig = nullptr; + + int spotSi = 1 + 2 * rtengine::max(1, lp.cir / sk); + + if (spotSi < 5) { + spotSi = 5; + } + + spotSise2 = (spotSi - 1) / 2; + + JaggedArray blend3(spotSi, spotSi); + + origsob = new LabImage(spotSi, spotSi); + sobelL = new LabImage(spotSi, spotSi); + deltasobelL = new LabImage(spotSi, spotSi); + bool isdenoise = false; + + if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f) && lp.denoiena) { + isdenoise = true; + } + + if (isdenoise) { + origblur = new LabImage(spotSi, spotSi); + blurorig = new LabImage(spotSi, spotSi); + + for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { + int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); + + int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); + + int z = y - yb; + int u = x - xb; + origblur->L[z][u] = original->L[y - cy][x - cx]; + origblur->a[z][u] = original->a[y - cy][x - cx]; + origblur->b[z][u] = original->b[y - cy][x - cx]; + + } + } + + float radius = 3.f / sk; + { + //No omp + gaussianBlur(origblur->L, blurorig->L, spotSi, spotSi, radius); + gaussianBlur(origblur->a, blurorig->a, spotSi, spotSi, radius); + gaussianBlur(origblur->b, blurorig->b, spotSi, spotSi, radius); + + } + + for (int y = 0; y < spotSi; y++) { + for (int x = 0; x < spotSi; x++) { + aveLblur += blurorig->L[y][x]; + aveAblur += blurorig->a[y][x]; + aveBblur += blurorig->b[y][x]; + aveChroblur += std::sqrt(SQR(blurorig->b[y - cy][x - cx]) + SQR(blurorig->a[y - cy][x - cx])); + nsb++; + + } + } + } + + //ref for luma, chroma, hue + for (int y = rtengine::max(cy, (int)(lp.yc - spotSize)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSize + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSize)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSize + 1)); x++) { + aveL += original->L[y - cy][x - cx]; + aveA += original->a[y - cy][x - cx]; + aveB += original->b[y - cy][x - cx]; + aveChro += std::sqrt(SQR(original->b[y - cy][x - cx]) + SQR(original->a[y - cy][x - cx])); + nab++; + } + } + + //ref for sobel + for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { + int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); + + int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); + + int z = y - yb; + int u = x - xb; + origsob->L[z][u] = original->L[y - cy][x - cx]; + nso++; + } + } + + const float radius = 3.f / (sk * 1.4f); //0 to 70 ==> see skip + + SobelCannyLuma(sobelL->L, origsob->L, spotSi, spotSi, radius); + int nbs = 0; + + for (int y = 0; y < spotSi ; y ++) + for (int x = 0; x < spotSi ; x ++) { + avesobel += sobelL->L[y][x]; + nbs++; + } + + sobelref = avesobel / nbs; + + delete sobelL; + + delete deltasobelL; + delete origsob; + aveL = aveL / nab; + aveA = aveA / nab; + aveB = aveB / nab; + aveChro = aveChro / nab; + aveChro /= 327.68f; + avA = aveA / 327.68f; + avB = aveB / 327.68f; + avL = aveL / 327.68f; + hueref = xatan2f(avB, avA); //mean hue + + if (isdenoise) { + aveLblur = aveLblur / nsb; + aveChroblur = aveChroblur / nsb; + aveChroblur /= 327.68f; + aveAblur = aveAblur / nsb; + aveBblur = aveBblur / nsb; + float avAblur = aveAblur / 327.68f; + float avBblur = aveBblur / 327.68f; + float avLblur = aveLblur / 327.68f; + huerefblur = xatan2f(avBblur, avAblur); + chromarefblur = aveChroblur; + lumarefblur = avLblur; + } else { + huerefblur = 0.f; + chromarefblur = 0.f; + lumarefblur = 0.f; + } + + chromaref = aveChro; + lumaref = avL; + + // printf("Calcref => sp=%i befend=%i huere=%2.1f chromare=%2.1f lumare=%2.1f sobelref=%2.1f\n", sp, befend, hueref, chromaref, lumaref, sobelref / 100.f); + + if (isdenoise) { + delete origblur; + delete blurorig; + } + + if (lumaref > 95.f) {//to avoid crash + lumaref = 95.f; + } + } +} +//doc fftw3 says optimum is with size 2^a * 3^b * 5^c * 7^d * 11^e * 13^f with e+f = 0 or 1 +//number for size between 18144 and 1 ==> 18000 pixels cover 99% all sensor +const int fftw_size[] = {18144, 18000, 17920, 17836, 17820, 17640, 17600, 17550, 17500, 17496, 17472, 17325, 17280, 17248, 17199, 17150, 17010, 16896, 16875, 16848, 16807, + 16800, 16640, 16632, 16500, 16464, 16384, 16380, 16250, 16200, 16170, 16128, 16038, 16000, 15925, 15876, 15840, 15795, 15750, 15680, 15625, 15600, 15552, 15435, 15400, + 15360, 15309, 15288, 15120, 15092, 15000, 14976, 14850, 14784, 14742, 14700, 14625, 14580, 14560, 14553, 14336, 14406, 14400, 14256, 14175, 14112, 14080, 14040, 14000, 13860, + 13824, 13750, 13720, 13650, 13608, 13500, 13475, 13440, 13377, 13365, 13312, 13230, 13200, 13125, 13122, 13104, 13000, 12960, 12936, 12800, 12740, 12672, 12636, 12600, + 12544, 12500, 12480, 12474, 12375, 12348, 12320, 12288, 12285, 12250, 12150, 12096, 12005, 12000, 11907, 11880, 11760, 11700, 11664, 11648, 11550, 11520, 11466, 11375, + 11340, 11319, 11264, 11250, 11232, 11200, 11088, 11025, 11000, 10976, 10935, 10920, 10800, 10780, 10752, 10692, 10584, 10560, 10530, 10400, 10395, 10368, 10290, 10240, + 10206, 10192, 10125, 10080, 10000, 9984, 9900, 9604, 9856, 9828, 9800, 9750, 9720, 9702, 9625, 9600, 9555, 9504, 9477, 9450, 9408, 9375, 9360, 9261, 9240, + 9216, 9100, 9072, 9000, 8960, 8918, 8910, 8820, 8800, 8775, 8750, 8748, 8736, 8640, 8624, 8575, 8505, 8448, 8424, 8400, 8320, 8316, 8250, 8232, 8192, 8190, 8125, + 8100, 8085, 8064, 8019, 8000, 7938, 7920, 7875, 7840, 7800, 7776, 7700, 7680, 7644, 7560, 7546, 7500, 7488, 7425, 7392, 7371, 7350, 7290, 7280, 7203, 7200, 7168, + 7128, 7056, 7040, 7020, 7000, 6930, 6912, 6875, 6860, 6825, 6804, 6750, 6720, 6656, 6615, 6600, 6561, 6552, 6500, 6480, 6468, 6400, 6370, 6336, 6318, 6300, + 6272, 6250, 6240, 6237, 6174, 6160, 6144, 6125, 6075, 6048, 6000, 5940, 5880, 5850, 5832, 5824, 5775, 5760, 5670, 5632, 5625, 5616, 5600, 5544, 5500, 5488, + 5460, 5400, 5390, 5376, 5346, 5292, 5280, 5265, 5250, 5200, 5184, 5145, 5120, 5103, 5096, 5040, 5000, 4992, 4950, 4928, 4914, 4900, 4875, 4860, 4851, 4802, + 4800, 4752, 4725, 4704, 4680, 4620, 4608, 4550, 4536, 4500, 4480, 4459, 4455, 4410, 4400, 4375, 4374, 4368, 4320, 4312, 4224, 4212, 4200, 4160, 4158, 4125, + 4116, 4096, 4095, 4050, 4032, 4000, 3969, 3960, 3920, 3900, 3888, 3850, 3840, 3822, 3780, 3773, 3750, 3744, 3696, 3675, 3645, 3640, 3600, 3584, 3564, 3528, + 3520, 3510, 3500, 3465, 3456, 3430, 3402, 3375, 3360, 3328, 3300, 3276, 3250, 3240, 3234, 3200, 3185, 3168, 3159, 3150, 3136, 3125, 3120, 3087, 3080, 3072, + 3024, 3000, 2970, 2940, 2925, 2916, 2912, 2880, 2835, 2816, 2808, 2800, 2772, 2750, 2744, 2730, 2700, 2695, 2688, 2673, 2646, 2640, 2625, 2600, 2592, 2560, + 2548, 2520, 2500, 2496, 2475, 2464, 2457, 2450, 2430, 2401, 2400, 2376, 2352, 2340, 2310, 2304, 2275, 2268, 2250, 2240, 2205, 2200, 2187, 2184, 2160, 2156, + 2112, 2106, 2100, 2080, 2079, 2058, 2048, 2025, 2016, 2000, 1980, 1960, 1950, 1944, 1936, 1925, 1920, 1911, 1890, 1875, 1872, 1848, 1820, 1800, 1792, 1782, + 1764, 1760, 1755, 1750, 1728, 1715, 1701, 1680, 1664, 1650, 1638, 1625, 1620, 1617, 1600, 1584, 1575, 1568, 1560, 1540, 1536, 1512, 1500, 1485, 1470, 1458, + 1456, 1440, 1408, 1404, 1400, 1386, 1375, 1372, 1365, 1350, 1344, 1323, 1320, 1300, 1296, 1280, 1274, 1260, 1250, 1248, 1232, 1225, 1215, 1200, 1188, 1176, + 1170, 1155, 1152, 1134, 1125, 1120, 1100, 1092, 1080, 1078, 1056, 1053, 1050, 1040, 1029, 1024, 1008, 1000, 990, 980, 975, 972, 960, 945, 936, 924, 910, 900, + 896, 891, 882, 880, 875, 864, 840, 832, 825, 819, 810, 800, 792, 784, 780, 770, 768, 756, 750, 735, 729, 728, 720, 704, 702, 700, 693, 686, 675, 672, 660, + 650, 648, 640, 637, 630, 625, 624, 616, 600, 594, 588, 585, 576, 567, 560, 550, 546, 540, 539, 528, 525, 520, 512, 504, 500, 495, 490, 486, 480, 468, 462, 455, + 450, 448, 441, 440, 432, 420, 416, 405, 400, 396, 392, 390, 385, 384, 378, 375, 364, 360, 352, 351, 350, 343, 336, 330, 325, 324, 320, 315, 312, 308, 300, 297, + 294, 288, 280, 275, 273, 270, 264, 260, 256, 252, 250, 245, 243, 240, 234, 231, 225, 224, 220, 216, 210, 208, 200, 198, 196, 195, 192, 189, 182, 180, 176, 175, + 168, 165, 162, 160, 156, 154, 150, 147, 144, 143, 140, 135, 132, 130, 128, 126, 125, 120, 117, 112, 110, 108, 105, 104, 100, 99, 98, 96, 91, 90, 88, 84, 81, + 80, 78, 77, 75, 72, 70, 66, 65, 64, 63, 60, 56, 55, 54, 52, 50, 49, 48, 45, 44, 42, 40, 39, 36, 35, 33, 32, 30, 28, 27, 26, 25, 24, 22, 21, 20, 18, 16, 15, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + }; + +int N_fftwsize = sizeof(fftw_size) / sizeof(fftw_size[0]); + + +void optfft(int N_fftwsize, int &bfh, int &bfw, int &bfhr, int &bfwr, struct local_params& lp, int H, int W, int &xstart, int &ystart, int &xend, int ¥d, int cx, int cy) +{ + int ftsizeH = 1; + int ftsizeW = 1; + + for (int ft = 0; ft < N_fftwsize; ft++) { //find best values + if (fftw_size[ft] <= bfh) { + ftsizeH = fftw_size[ft]; + break; + } + } + + for (int ft = 0; ft < N_fftwsize; ft++) { + if (fftw_size[ft] <= bfw) { + ftsizeW = fftw_size[ft]; + break; + } + } + + //optimize with size fftw + bool reduW = false; + bool reduH = false; + + if (ystart == 0 && yend < H) { + lp.ly -= (bfh - ftsizeH); + } else if (ystart != 0 && yend == H) { + lp.lyT -= (bfh - ftsizeH); + } else if (ystart != 0 && yend != H) { + if (lp.ly <= lp.lyT) { + lp.lyT -= (bfh - ftsizeH); + } else { + lp.ly -= (bfh - ftsizeH); + } + } else if (ystart == 0 && yend == H) { + bfhr = ftsizeH; + reduH = true; + } + + if (xstart == 0 && xend < W) { + lp.lx -= (bfw - ftsizeW); + } else if (xstart != 0 && xend == W) { + lp.lxL -= (bfw - ftsizeW); + } else if (xstart != 0 && xend != W) { + if (lp.lx <= lp.lxL) { + lp.lxL -= (bfw - ftsizeW); + } else { + lp.lx -= (bfw - ftsizeW); + } + } else if (xstart == 0 && xend == W) { + bfwr = ftsizeW; + reduW = true; + } + + //new values optimized + ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, H); + xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, W); + bfh = bfhr = yend - ystart; + bfw = bfwr = xend - xstart; + + if (reduH) { + bfhr = ftsizeH; + } + + if (reduW) { + bfwr = ftsizeW; + } + + if (settings->verbose) { + printf("Nyst=%i Nyen=%i lp.yc=%f lp.lyT=%f lp.ly=%f bfh=%i bfhr=%i origH=%i ftsizeH=%i\n", ystart, yend, lp.yc, lp.lyT, lp.ly, bfh, bfhr, H, ftsizeH); + printf("Nxst=%i Nxen=%i lp.xc=%f lp.lxL=%f lp.lx=%f bfw=%i bfwr=%i origW=%i ftsizeW=%i\n", xstart, xend, lp.xc, lp.lxL, lp.lx, bfw, bfwr, W, ftsizeW); + } +} + +void ImProcFunctions::BlurNoise_Local(LabImage *tmp1, LabImage * originalmask, float **bufchro, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ +//local BLUR + BENCHFUN + + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + + const float ach = lp.trans / 100.f; + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; + const bool previewbl = lp.showmaskblmet == 4; + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + const float ampli = 1.5f + 0.5f * std::fabs(lp.colorde); + + constexpr float darklim = 5000.f; + constexpr float aadark = -1.f; + + const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; + const bool usemaskall = usemaskbl; + const float radius = 3.f / sk; + std::unique_ptr origblurmask; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 4.f + MINSCOPE * lp.sensbn * lp.thr;//best usage ?? with blurnoise + const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) { + const int loy = cy + y; + + for (int x = xstart, lox = cx + x; x < xend; x++, lox++) { + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - chromaref * 327.68f); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); + const float clc = previewbl ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + const float realstrchdE = reducdE * clc; + + float difL = (tmp1->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + const float fli = (100.f + realstrchdE) / 100.f; + const float difa = tmp1->a[y - ystart][x - xstart] * fli - original->a[y][x] * localFactor; + const float difb = tmp1->b[y - ystart][x - xstart] * fli - original->b[y][x] * localFactor; + + if (!lp.actsp) { + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + const float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); + + if (blshow && lp.colorde < 0) { //show modifications with use "b" + // (origshow && lp.colorde < 0) { //original Retinex + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 8.f * difL * reducdE; + transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); + + } else if (blshow && lp.colorde > 0) {//show modifications without use "b" + if (difL < 1000.f) {//if too low to be view use ab + difL += 0.5f * maxdifab; + } + + transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); + transformed->a[y][x] = clipC(ampli * difa); + transformed->b[y][x] = clipC(ampli * difb); + } else if (previewbl || lp.prevdE) {//show deltaE + const float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + darklim; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImage * bufexporig, const LabImage * bufexpfin, LabImage * originalmask, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + //initialize coordinates + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfw = xend - xstart; + int bfh = yend - ystart; + + int bfhr = bfh; + int bfwr = bfw; + if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + bfh = bfhr; + bfw = bfwr; + + //initialize scope + float varsens = lp.sensex;//exposure + + if (senstype == 0) { //Color and light + varsens = lp.sens; + } else if (senstype == 2) { //vibrance + varsens = lp.sensv; + } else if (senstype == 9) { //shadowshighlight + varsens = lp.senshs; + } else if (senstype == 3) { //softlight + varsens = lp.senssf; + } else if (senstype == 30) { //dehaze + varsens = lp.sensh; + } else if (senstype == 8) { //TM + varsens = lp.senstm; + } else if (senstype == 10) { //local contrast + varsens = lp.senslc; + } else if (senstype == 11) { //encoding log + varsens = lp.sensilog; + } else if (senstype == 20) { //common mask + varsens = lp.sensimas; + } + bool delt = lp.deltaem; + + //sobel + sobelref /= 100.f; + meansobel /= 100.f; + + sobelref = rtengine::min(sobelref, 60.f); + + const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images + + sobelref = log1p(sobelref); + + //references Spot + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + //to preview modifications, scope, mask + const bool expshow = ((lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2) && senstype == 1); + const bool vibshow = ((lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2) && senstype == 2); + const bool colshow = ((lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2) && senstype == 0); + const bool SHshow = ((lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2) && senstype == 9); + const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); + const bool lcshow = ((lp.showmasklcmet == 1 || lp.showmasklcmet == 2) && senstype == 10); + const bool origshow = ((lp.showmasksoftmet == 5) && senstype == 3 && lp.softmet == 1); + + const bool masshow = ((lp.showmask_met == 1) && senstype == 20); + + const bool previewvib = ((lp.showmaskvibmet == 4) && senstype == 2); + const bool previewexp = ((lp.showmaskexpmet == 5) && senstype == 1); + const bool previewcol = ((lp.showmaskcolmet == 5) && senstype == 0); + const bool previewSH = ((lp.showmaskSHmet == 4) && senstype == 9); + const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); + const bool previewlc = ((lp.showmasklcmet == 4) && senstype == 10); + const bool previeworig = ((lp.showmasksoftmet == 6) && senstype == 3 && lp.softmet == 1); + const bool previewmas = ((lp.showmask_met == 3) && senstype == 20); + + float radius = 3.f / sk; + + if (senstype == 1) { + radius = (2.f + 0.2f * lp.blurexp) / sk; + } else if (senstype == 0) { + radius = (2.f + 0.2f * lp.blurcol) / sk; + } else if (senstype == 9) { + radius = (2.f + 0.2f * lp.blurSH) / sk; + } + + const std::unique_ptr origblur(new LabImage(bfw, bfh)); + std::unique_ptr origblurmask; + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + float darklim = 5000.f; + float aadark = -1.f; + float bbdark = darklim; + + const bool usemaskvib = (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 4) && senstype == 2; + const bool usemaskexp = (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 5) && senstype == 1; + const bool usemaskcol = (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 5) && senstype == 0; + const bool usemaskSH = (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 4) && senstype == 9; + const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; + const bool usemasklc = (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 4) && senstype == 10; + const bool usemaskmas = (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 3) && senstype == 20; + const bool usemaskall = (usemaskexp || usemaskvib || usemaskcol || usemaskSH || usemasktm || usemasklc || usemaskmas); + + //blur a little mask + if (usemaskall) { + origblurmask.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); + gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); + gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); + } + } + + if (lp.equtm && senstype == 8) { //normalize luminance for Tone mapping , at this place we can use for others senstype! + float *datain = new float[bfh * bfw]; + float *data = new float[bfh * bfw]; + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; + data[(y - ystart)* bfw + (x - xstart)] = bufexpfin->L[y - ystart][x - xstart]; + } + + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + bufexpfin->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; + } + + delete [] datain; + delete [] data; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + origblur->L[y][x] = original->L[y + ystart][x + xstart]; + origblur->a[y][x] = original->a[y + ystart][x + xstart]; + origblur->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); + gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); + gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); + + } + + + //choice between original and mask + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + + //parameters deltaE + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ +// float atan2Buffer[transformed->W] ALIGNED16;//keep in case of +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + + const int loy = y + ystart + cy; +#ifdef __SSE2__ + /* //keep in case of + int i = 0; + + for (; i < bfw - 3; i += 4) { + vfloat av = LVFU(maskptr->a[y][i]); + vfloat bv = LVFU(maskptr->b[y][i]); + STVFU(atan2Buffer[i], xatan2f(bv, av)); + } + + for (; i < bfw; i++) { + atan2Buffer[i] = xatan2f(maskptr->b[y][i], maskptr->a[y][i]); + } + */ +#endif + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + //calculate transition + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + +// float hueh = 0; +#ifdef __SSE2__ +// hueh = atan2Buffer[x]; +#else +// hueh = xatan2f(maskptr->b[y][x], maskptr->a[y][x]); +#endif + + float rsob = 0.f; + + //calculate additive sobel to deltaE + if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0) && lp.struco > 0.f))) { + const float csob = xlogf(1.f + rtengine::min(blend2[y][x] / 100.f, 60.f) + 0.001f); + + float rs; + + if (k) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + if (rs > 0.f && senstype == 1) { + rsob = 1.1f * lp.struexp * rs; + } else if (rs > 0.f && (senstype == 0)) { + rsob = 1.1f * lp.struco * rs; + } + } + + //deltaE + float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + + const float dE = rsob + std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + //reduction action with deltaE + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + + float cli = (bufexpfin->L[y][x] - bufexporig->L[y][x]); + float cla = (bufexpfin->a[y][x] - bufexporig->a[y][x]); + float clb = (bufexpfin->b[y][x] - bufexporig->b[y][x]); + + if (delt) { + cli = bufexpfin->L[y][x] - original->L[y + ystart][x + xstart]; + cla = bufexpfin->a[y][x] - original->a[y + ystart][x + xstart]; + clb = bufexpfin->b[y][x] - original->b[y + ystart][x + xstart]; + } + if(lp.blwh) { + cla = 0.f; + clb = 0.f; + } + + // const float previewint = settings->previewselection; + + const float realstrdE = reducdE * cli; + const float realstradE = reducdE * cla; + const float realstrbdE = reducdE * clb; + + float factorx = localFactor; + + if (zone > 0) { + //simplified transformed with deltaE and transition + transformed->L[y + ystart][x + xstart] = clipLoc(original->L[y + ystart][x + xstart] + factorx * realstrdE); + float diflc = factorx * realstrdE; + transformed->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart] + factorx * realstradE); + const float difa = factorx * realstradE; + transformed->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart] + factorx * realstrbdE); + const float difb = factorx * realstrbdE; + float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); + + if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde < 0) { //show modifications with use "b" + // (origshow && lp.colorde < 0) { //original Retinex + transformed->a[y + ystart][x + xstart] = 0.f; + transformed->b[y + ystart][x + xstart] = ampli * 8.f * diflc * reducdE; + transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); + + } else if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde > 0) {//show modifications without use "b" + if (diflc < 1000.f) {//if too low to be view use ab + diflc += 0.5f * maxdifab; + } + + transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); + transformed->a[y + ystart][x + xstart] = clipC(ampli * difa); + transformed->b[y + ystart][x + xstart] = clipC(ampli * difb); + } else if (previewexp || previewvib || previewcol || previewSH || previewtm || previewlc || previeworig || previewmas || lp.prevdE) {//show deltaE + float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y + ystart][x + xstart] < darklim) { //enhance dark luminance as user can see! + float dark = transformed->L[y + ystart][x + xstart]; + transformed->L[y + ystart][x + xstart] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y + ystart][x + xstart] = 0.f; + transformed->b[y + ystart][x + xstart] = difbdisp; + } else { + transformed->a[y + ystart][x + xstart] = -difbdisp; + transformed->b[y + ystart][x + xstart] = 0.f; + } + } + } + } + } + } +} + + + + +void ImProcFunctions::exposure_pde(float * dataor, float * datain, float * dataout, int bfw, int bfh, float thresh, float mod) +/* Jacques Desmis July 2019 +** adapted from Ipol Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ +*/ +{ + + BENCHFUN +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } + +#endif + float *data_fft, *data_tmp, *data; + + if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); + + if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw); + + fftwf_free(data_tmp); + + /* solve the Poisson PDE in Fourier space */ + /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ + ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); + + const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_bw); + fftwf_destroy_plan(dct_fw); + fftwf_destroy_plan(dct_bw); + fftwf_free(data_fft); + fftwf_cleanup(); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif + + normalize_mean_dt(data, dataor, bfw * bfh, mod, 1.f); + { + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(data[y * bfw + x]); + } + } + } + + fftwf_free(data); +} + +void ImProcFunctions::fftw_convol_blur(float * input, float * output, int bfw, int bfh, float radius, int fftkern, int algo) +{ + /* + ** Jacques Desmis june 2019 - inspired by Copyright 2013 IPOL Image Processing On Line http://www.ipol.im/ + ** when I read documentation on various FFT blur we found 2 possibilities + ** 0) kernel gauss is used with "normal" data + ** 1) kernel gauss is used with FFT + ** fftkern allows to change 0) or 1) and test It seems the good solution is with 0, but I keep the code in case of ?? + + ** input real data to blur + ** output real data blurred with radius + ** bfw bfh width and high area + ** radius = sigma for kernel + ** n_x n_y relative width and high for kernel + ** Gaussian blur is given by G(x,y) = (1/2*PI*sigma) * exp(-(x2 + y2) / 2* sigma2) + ** its traduction in Fourier transform is G(x,y) = exp((-sigma)*(PI * x2 + PI * y2)), for some authors it is not sigma but sigma^2..I have tried...huge differences with Gaussianblur + ** after several test the only result that works very well is with fftkern = 0 and algo = 0, and as there is differences with Gaussianblur, I put an empirical correction in Ipretinex and Iplocalcontrast + ** you can enabled or disabled this function with rtsettings.fftwsigma in options. By default empirical formula is disabled + ** in fact no importance....if it is this function (for sigma) or another... we are not in research :) + */ + BENCHFUN + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } +#endif + + + float *out; //for FFT data + float *kern = nullptr;//for kernel gauss + float *outkern = nullptr;//for FFT kernel + fftwf_plan p; + fftwf_plan pkern;//plan for FFT + int image_size, image_sizechange; + float n_x = 1.f; + float n_y = 1.f;//relative coordinates for kernel Gauss + float radsig = 1.f; + + out = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + + if (fftkern == 1) { //allocate memory FFT if kernel fft = 1 + // kern = new float[bfw * bfh]; + kern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + outkern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + } + + /*compute the Fourier transform of the input data*/ + + p = fftwf_plan_r2r_2d(bfh, bfw, input, out, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE);//FFT 2 dimensions forward FFTW_MEASURE FFTW_ESTIMATE + + fftwf_execute(p); + fftwf_destroy_plan(p); + + /*define the gaussian constants for the convolution kernel*/ + if (algo == 0) { + n_x = rtengine::RT_PI / (double) bfw; //ipol + n_y = rtengine::RT_PI / (double) bfh; + } else if (algo == 1) { + n_x = 1.f / bfw; //gauss + n_y = 1.f / bfh; + radsig = 1.f / (2.f * rtengine::RT_PI * radius * radius);//gauss + } + + n_x = n_x * n_x; + n_y = n_y * n_y; + + image_size = bfw * bfh; + image_sizechange = 4 * image_size; + + if (fftkern == 1) { //convolution with FFT kernel +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) + if (algo == 0) { + kern[ i + index] = exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //calculate Gauss kernel Ipol formula + } else if (algo == 1) { + kern[ i + index] = radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula + } + } + + /*compute the Fourier transform of the kernel data*/ + pkern = fftwf_plan_r2r_2d(bfh, bfw, kern, outkern, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE); //FFT 2 dimensions forward + fftwf_execute(pkern); + fftwf_destroy_plan(pkern); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= outkern[i + index]; //apply Gauss kernel with FFT + } + } + + fftwf_free(outkern); + fftwf_free(kern); + + // delete [] kern; + + } else if (fftkern == 0) {//without FFT kernel + if (algo == 0) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //apply Gauss kernel without FFT - some authors says radius*radius but differences with Gaussianblur + } + } + } else if (algo == 1) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula + } + } + } + } + + p = fftwf_plan_r2r_2d(bfh, bfw, out, output, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE);//FFT 2 dimensions backward + fftwf_execute(p); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int index = 0; index < image_size; index++) { //restore data + output[index] /= image_sizechange; + } + + fftwf_destroy_plan(p); + fftwf_free(out); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif +} + +void ImProcFunctions::fftw_convol_blur2(float **input2, float **output2, int bfw, int bfh, float radius, int fftkern, int algo) +{ + MyMutex::MyLock lock(*fftwMutex); + + float *input = nullptr; + + if (NULL == (input = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + float *output = nullptr; + + if (NULL == (output = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + input[y * bfw + x] = input2[y][x]; + } + } + + ImProcFunctions::fftw_convol_blur(input, output, bfw, bfh, radius, fftkern, algo); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + output2[y][x] = output[y * bfw + x]; + } + } + + fftwf_free(input); + fftwf_free(output); +} + + +void ImProcFunctions::fftw_tile_blur(int GW, int GH, int tilssize, int max_numblox_W, int min_numblox_W, float **tmp1, int numThreads, double radius) +{ + BENCHFUN + float epsil = 0.001f / (tilssize * tilssize); + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + + array2D tilemask_in(tilssize, tilssize); + array2D tilemask_out(tilssize, tilssize); + + float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + + int nfwd[2] = {tilssize, tilssize}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + const int border = rtengine::max(2, tilssize / 16); + + for (int i = 0; i < tilssize; ++i) { + float i1 = abs((i > tilssize / 2 ? i - tilssize + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < tilssize; ++j) { + float j1 = abs((j > tilssize / 2 ? j - tilssize + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; + + } + } + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; + + const int numblox_W = ceil((static_cast(GW)) / offset) + 2; + const int numblox_H = ceil((static_cast(GH)) / offset) + 2; + + array2D Lresult(GW, GH, ARRAY2D_CLEAR_DATA); + array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[GW + tilssize + 2 * offset] ALIGNED16; +#ifdef _OPENMP + #pragma omp for +#endif + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - 1) * offset; + float * datarow = pBuf + offset; + + for (int i = 0; i < tilssize; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = rtengine::min(-row, GH - 1); + } else if (row >= GH) { + rr = rtengine::max(0, 2 * GH - 2 - row); + } + + for (int j = 0; j < GW; ++j) { + datarow[j] = (tmp1[rr][j]); + } + + for (int j = -1 * offset; j < 0; ++j) { + datarow[j] = datarow[rtengine::min(-j, GW - 1)]; + } + + for (int j = GW; j < GW + tilssize + offset; ++j) { + datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; + }//now we have a padded data row + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int indx = (hblk) * tilssize; //index of block in malloc + + if (top + i >= 0 && top + i < GH) { + int j; + + for (j = 0; j < rtengine::min((-left), tilssize); ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < rtengine::min(tilssize, GW - left); ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < tilssize; ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < tilssize; ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + const float n_xy = rtengine::SQR(rtengine::RT_PI / tilssize); + + //radius = 30.f; + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int blkstart = hblk * tilssize * tilssize; + + for (int j = 0; j < tilssize; j++) { + int index = j * tilssize; + + for (int i = 0; i < tilssize; i++) { + fLblox[blkstart + index + i] *= exp((float)(-radius) * (n_xy * rtengine::SQR(i) + n_xy * rtengine::SQR(j))); + } + } + }//end of horizontal block loop + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - 1) * offset; + const int numblox_W = ceil((static_cast(GW)) / offset); + const float DCTnorm = 1.0f / (4 * tilssize * tilssize); //for DCT + + int imin = rtengine::max(0, - topproc); + int bottom = rtengine::min(topproc + tilssize, GH); + int imax = bottom - topproc; + + for (int i = imin; i < imax; ++i) { + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int right = rtengine::min(left + tilssize, GW); + int jmin = rtengine::max(0, -left); + int jmax = right - left; + int indx = hblk * tilssize; + + for (int j = jmin; j < jmax; ++j) { + Lresult[topproc + i][left + j] += tilemask_out[i][j] * Lblox[(indx + i) * tilssize + j] * DCTnorm; //for DCT + } + } + } + }//end of vertical block loop + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + tmp1[i][j] = Lresult[i][j] / totwt[i][j]; + tmp1[i][j] = clipLoc(tmp1[i][j]); + } + } + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); +} + +void ImProcFunctions::wavcbd(wavelet_decomposition &wdspot, int level_bl, int maxlvl, + const LocwavCurve& locconwavCurve, bool locconwavutili, float sigm, float offs, float chromalev, int sk) +{ + if (locconwavCurve && locconwavutili) { + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; +#endif + Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + const int W_L = wdspot.level_W(level); + const int H_L = wdspot.level_H(level); + float mea[9]; + + float* const* wav_L = wdspot.level_coeffs(level); + //offset + float rap = offs * mean[level] - 2.f * sigm * sigma[level]; + + if (rap > 0.f) { + mea[0] = rap; + } else { + mea[0] = mean[level] / 6.f; + } + + rap = offs * mean[level] - sigm * sigma[level]; + + if (rap > 0.f) { + mea[1] = rap; + } else { + mea[1] = mean[level] / 2.f; + } + + mea[2] = offs * mean[level]; // 50% data + mea[3] = offs * mean[level] + sigm * sigma[level] / 2.f; + mea[4] = offs * mean[level] + sigm * sigma[level]; //66% + mea[5] = offs * mean[level] + sigm * 1.2f * sigma[level]; + mea[6] = offs * mean[level] + sigm * 1.5f * sigma[level]; // + mea[7] = offs * mean[level] + sigm * 2.f * sigma[level]; //95% + mea[8] = offs * mean[level] + sigm * 2.5f * sigma[level]; //99% + + float cpMul = 200.f * (locconwavCurve[level * 55.5f] - 0.5f); + + if (cpMul > 0.f) { + cpMul *= 3.5f; + } + + cpMul /= sk; + + for (int i = 0; i < W_L * H_L; i++) { + const float WavCL = std::fabs(wav_L[dir][i]); + float beta; + + //reduction amplification: max action between mean / 2 and mean + sigma + // arbitrary coefficient, we can add a slider !! + if (WavCL < mea[0]) { + beta = 0.6f; //preserve very low contrast (sky...) + } else if (WavCL < mea[1]) { + beta = 0.8f; + } else if (WavCL < mea[2]) { + beta = 1.f; //standard + } else if (WavCL < mea[3]) { + beta = 1.f; + } else if (WavCL < mea[4]) { + beta = 0.8f; //+sigma + } else if (WavCL < mea[5]) { + beta = 0.6f; + } else if (WavCL < mea[6]) { + beta = 0.4f; + } else if (WavCL < mea[7]) { + beta = 0.2f; // + 2 sigma + } else if (WavCL < mea[8]) { + beta = 0.1f; + } else { + beta = 0.0f; + } + + const float alpha = rtengine::max((1024.f + 15.f * cpMul * beta) / 1024.f, 0.02f) ; + wav_L[dir][i] *= alpha * chromalev; + } + } + } + } +} + +void ImProcFunctions::Compresslevels(float **Source, int W_L, int H_L, float compression, float detailattenuator, float thres, float mean, float maxp, float meanN, float maxN, float madL) +{ + //J.Desmis 12-2019 + + float exponent; + + if (detailattenuator > 0.f && detailattenuator < 0.05f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; //0.69315 = log(2) + exponent = 1.2f * xlogf(-betemp); + exponent /= 20.f; + } else if (detailattenuator >= 0.05f && detailattenuator < 0.25f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; + exponent = 1.2f * xlogf(-betemp); + exponent /= (-75.f * detailattenuator + 23.75f); + } else if (detailattenuator >= 0.25f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; + exponent = 1.2f * xlogf(-betemp); + exponent /= (-2.f * detailattenuator + 5.5f); + } else { + exponent = (compression - 1.0f) / 20.f; + } + + float ap = (thres - 1.f) / (maxp - mean); + float bp = 1.f - ap * mean; + ap *= exponent; + bp *= exponent; + + float a0 = (1.33f * thres - 1.f) / (1.f - mean); + float b0 = 1.f - a0 * mean; + a0 *= exponent; + b0 *= exponent; + + float apn = (thres - 1.f) / (maxN - meanN); + float bpn = 1.f - apn * meanN; + apn *= -exponent; + bpn *= exponent; + + float a0n = (1.33f * thres - 1.f) / (1.f - meanN); + float b0n = 1.f - a0n * meanN; + a0n *= -exponent; + b0n *= exponent; + + madL *= 0.05f; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + const vfloat apv = F2V(ap); + const vfloat bpv = F2V(bp); + const vfloat a0v = F2V(a0); + const vfloat b0v = F2V(b0); + const vfloat apnv = F2V(apn); + const vfloat bpnv = F2V(bpn); + const vfloat a0nv = F2V(a0n); + const vfloat b0nv = F2V(b0n); + const vfloat madLv = F2V(madL); + const vfloat meanv = F2V(mean); + const vfloat onev = F2V(1.f); +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < H_L; y++) { + int x = 0; +#ifdef __SSE2__ + for (; x < W_L - 3; x += 4) { + vfloat exponev = onev; + vfloat valv = LVFU(Source[y][x]); + const vmask mask1v = vmaskf_ge(valv, ZEROV); + const vmask mask2v = vmaskf_gt(vself(mask1v, valv, -valv), meanv); + const vfloat av = vself(mask2v, vself(mask1v, apv, apnv), vself(mask1v, a0v, a0nv)); + const vfloat bv = vself(mask2v, vself(mask1v, bpv, bpnv), vself(mask1v, b0v, b0nv)); + exponev += av * valv + bv; + valv = vself(mask1v, valv, -valv); + const vfloat multv = vself(mask1v, onev, -onev); + const vfloat resultv = multv * xexpf(xlogf(valv + madLv) * exponev); + STVFU(Source[y][x], resultv); + } +#endif + for (; x < W_L; x++) { + float expone = 1.f; + + if (Source[y][x] >= 0.f) { + if (Source[y][x] > mean) { + expone += ap * Source[y][x] + bp; + } else { + expone += a0 * Source[y][x] + b0; + } + + Source[y][x] = xexpf(xlogf(Source[y][x] + madL) * expone); + } else { + if (-Source[y][x] > mean) { + expone += apn * Source[y][x] + bpn; + } else { + expone += a0n * Source[y][x] + b0n; + } + + Source[y][x] = -xexpf(xlogf(-Source[y][x] + madL) * expone); + } + } + } + } +} + +void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavelet_decomposition& wdspot, int level_bl, int maxlvl, + const LocwavCurve & loclevwavCurve, bool loclevwavutili, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, + float radlevblur, int process, float chromablu, float thres, float sigmadc, float deltad) +{ + BENCHFUN + const int W_L = wdspot.level_W(0); + const int H_L = wdspot.level_H(0); + + const std::unique_ptr beta(new float[W_L * H_L]); + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; +#endif + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + + if (process == 1 && loclevwavCurve && loclevwavutili) { //blur + StopWatch Stop1("blur"); + array2D templevel(W_L, H_L); + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = lp.sigmabl; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); + constexpr int lutSize = 100; + const float lutMax = ceil(mea[9]); + const float lutDiff = lutMax / lutSize; + std::vector lutVals(lutSize); + std::vector inVals({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); + int jStart = 1; + for (int i = 0; i < 100; ++i) { + const float val = i * lutDiff; + if (val < mea[0]) { + // still < first value => no interpolation + lutVals[i] = inVals[0]; + } else { + for (int j = jStart; j < 10; ++j) { + if (val == mea[j]) { + // exact match => no interpolation + lutVals[i] = inVals[j]; + ++jStart; + break; + } else if (val < mea[j]) { + // interpolate + const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); + lutVals[i] = intp(dist, inVals[j], inVals[j - 1]); + break; + } else { + lutVals[i] = inVals[10]; + } + } + } + } + const LUTf meaLut(lutVals); + constexpr float lutFactor = 1.f / lutDiff; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + beta[co] = meaLut[std::fabs(WavL[co]) * lutFactor]; + } + + const float klev = 0.25f * loclevwavCurve[level * 55.5f]; + float* src[H_L]; + for (int i = 0; i < H_L; ++i) { + src[i] = &wdspot.level_coeffs(level)[dir][i * W_L]; + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(src, templevel, W_L, H_L, radlevblur * klev * chromablu); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + WavL[j] = intp(beta[j], templevel[y][x], WavL[j]); + } + } + } + } + } else if (process == 2 && loccompwavCurve && loccompwavutili) { //Directional contrast + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = sigmadc; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(WavL[co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.7f; + } else if (WavCL < mea[7]) { + beta[co] = 0.5f; + } else if (WavCL < mea[8]) { + beta[co] = 0.3f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.2f; + } else { + beta[co] = 0.1f; + } + } + + const int iteration = deltad; + const int itplus = 7 + iteration; + const int itmoins = 7 - iteration; + const int med = maxlvl / 2; + int it; + + if (level < med) { + it = itmoins; + } else if (level == med) { + it = 7; + } else { + it = itplus; + } + + const float itf = it; + const float factor = dir < 3 ? 0.3f : -0.6f; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + const vfloat c327d68v = F2V(327.68f); + const vfloat factorv = F2V(factor); + const vfloat sixv = F2V(6.f); + const vfloat zd5v = F2V(0.5f); + const vfloat onev = F2V(1.f); + const vfloat itfv = F2V(itf); +#endif +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H_L; ++i) { + int j = 0; +#ifdef __SSE2__ + for (; j < W_L - 3; j += 4) { + const vfloat LL100v = LC2VFU(tmp[i * 2][j * 2]) / c327d68v; + const vfloat kbav = factorv * (loccompwavCurve[sixv * LL100v] - zd5v); //k1 between 0 and 0.5 0.5==> 1/6=0.16 + STVFU(WavL[i * W_L + j], LVFU(WavL[i * W_L + j]) * pow_F(onev + kbav * LVFU(beta[i * W_L + j]), itfv)); + } +#endif + for (; j < W_L; ++j) { + const float LL100 = tmp[i * 2][j * 2] / 327.68f; + const float kba = factor * (loccompwavCurve[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16 + WavL[i * W_L + j] *= pow_F(1.f + kba * beta[i * W_L + j], itf); + } + } + } + } + } + } else if (process == 3 && loccomprewavCurve && loccomprewavutili) { //Dynamic compression wavelet + float madL[10][3]; + array2D templevel(W_L, H_L); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + const int W_L = wdspot.level_W(level); + const int H_L = wdspot.level_H(level); + const auto wav_L = wdspot.level_coeffs(level)[dir]; + madL[level][dir - 1] = Mad(wav_L, W_L * H_L);//evaluate noise by level + } + } + + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = lp.sigmadr; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(WavL[co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.65f; + } else if (WavCL < mea[7]) { + beta[co] = 0.5f; + } else if (WavCL < mea[8]) { + beta[co] = 0.4f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.25f; + } else { + beta[co] = 0.1f; + } + } + + float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); + if (klev < 0.f) { + klev *= 2.6666f;//compression increase contraste + } else { + klev *= 4.f;//dilatation reduce contraste - detailattenuator + } + const float compression = expf(-klev); + const float detailattenuator = std::max(klev, 0.f); + + const auto wav_L = wdspot.level_coeffs(level)[dir]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + templevel[y][x] = wav_L[j]; + } + } + + Compresslevels(templevel, W_L, H_L, compression, detailattenuator, thres, mean[level], MaxP[level], meanN[level], MaxN[level], madL[level][dir - 1]); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + wav_L[j] = intp(beta[j], templevel[y][x], wav_L[j]); + } + } + } + } + } +} + + +void ImProcFunctions::wavcontrast4(struct local_params& lp, float ** tmp, float ** tmpa, float ** tmpb, float contrast, float radblur, float radlevblur, int bfw, int bfh, int level_bl, int level_hl, int level_br, int level_hr, int sk, int numThreads, + const LocwavCurve & locwavCurve, bool locwavutili, bool wavcurve, const LocwavCurve& loclevwavCurve, bool loclevwavutili, bool wavcurvelev, + const LocwavCurve & locconwavCurve, bool locconwavutili, bool wavcurvecon, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, bool wavcurvecomp, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, bool wavcurvecompre, + const LocwavCurve & locedgwavCurve, bool locedgwavutili, + float sigm, float offs, int & maxlvl, float sigmadc, float deltad, float chromalev, float chromablu, bool blurlc, bool blurena, bool levelena, bool comprena, bool compreena, float compress, float thres) +{ +BENCHFUN + std::unique_ptr wdspot(new wavelet_decomposition(tmp[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + + //first decomposition for compress dynamic range positive values and other process + if (wdspot->memory_allocation_failed()) { + return; + } + + struct grad_params gpwav; + + maxlvl = wdspot->maxlevel(); + + int W_Lm = wdspot->level_W(maxlvl - 1); //I assume all decomposition have same W and H + + int H_Lm = wdspot->level_H(maxlvl - 1); + + if (lp.strwav != 0.f && lp.wavgradl) { + array2D factorwav(W_Lm, H_Lm); + calclocalGradientParams(lp, gpwav, 0, 0, W_Lm, H_Lm, 10); + const float mult = lp.strwav < 0.f ? -1.f : 1.f; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_Lm; y++) { + for (int x = 0; x < W_Lm; x++) { + factorwav[y][x] = mult * (1.f - ImProcFunctions::calcGradientFactor(gpwav, x, y)); + } + } + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alowg = 1.f; + float blowg = 0.f; + + if (level_hl != level_bl) { + alowg = 1.f / (level_hl - level_bl); + blowg = -alowg * level_bl; + } + + float ahighg = 1.f; + float bhighg = 0.f; + + if (level_hr != level_br) { + ahighg = 1.f / (level_hr - level_br); + bhighg = -ahighg * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + const int W_L = wdspot->level_W(level); + const int H_L = wdspot->level_H(level); + auto wav_L = wdspot->level_coeffs(level)[dir]; + const float effect = lp.sigmalc2; + constexpr float offset = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offset); + constexpr float insigma = 0.666f; //SD + const float logmax = std::log(MaxP[level]); //log Max + const float rapX = (mean[level] + lp.sigmalc2 * sigma[level]) / MaxP[level]; //rapport between sD / max + const float inx = std::log(insigma); + const float iny = std::log(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / (sigma[level] * lp.sigmalc2); + const float bsig = 0.5f - asig * mean[level]; + const float amean = 0.5f / mean[level]; + float klev = 1.f; + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alowg * level + blowg; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahighg * level + bhighg; + } + } + klev *= 0.8f; + const float threshold = mean[level] + lp.sigmalc2 * sigma[level]; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) if (multiThread) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + const float WavCL = std::fabs(wav_L[y * W_L + x]); + float beta; + + if (WavCL < mea[0]) { + beta = 0.05f; + } else if (WavCL < mea[1]) { + beta = 0.2f; + } else if (WavCL < mea[2]) { + beta = 0.7f; + } else if (WavCL < mea[3]) { + beta = 1.f; //standard + } else if (WavCL < mea[4]) { + beta = 1.f; + } else if (WavCL < mea[5]) { + beta = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta = 0.6f; + } else if (WavCL < mea[7]) { + beta = 0.5f; + } else if (WavCL < mea[8]) { + beta = 0.4f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta = 0.3f; + } else { + beta = 0.1f; + } + + float absciss; + if (WavCL >= threshold) { //for max + absciss = pow_F(WavCL - logmax, rap); + } else if (WavCL >= mean[level]) { + absciss = asig * WavCL + bsig; + } else { + absciss = amean * WavCL; + } + + const float kc = klev * factorwav[y][x] * absciss; + const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; + + float kinterm = 1.f + reduceeffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + wav_L[y * W_L + x] *= (1.f + (kinterm - 1.f) * beta); + } + } + } + } + } + } + + int W_L = wdspot->level_W(0); + int H_L = wdspot->level_H(0); + float *wav_L0 = wdspot->get_coeff0(); + + if (radblur > 0.f && blurena) { + array2D bufl(W_L, H_L); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + bufl[y][x] = wav_L0[y * W_L + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufl, bufl, W_L, H_L, radblur); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + wav_L0[y * W_L + x] = bufl[y][x]; + } + } + } + + if (compress != 0.f && compreena) { + + float Compression = expf(-compress); + float DetailBoost = compress; + + if (compress < 0.0f) { + DetailBoost = 0.0f; + } + + CompressDR(wav_L0, W_L, H_L, Compression, DetailBoost); + + } + + if ((lp.residsha != 0.f || lp.residhi != 0.f)) { + float tran = 5.f;//transition shadow + + if (lp.residshathr > (100.f - tran)) { + tran = 100.f - lp.residshathr; + } + constexpr float alp = 3.f; + const float aalp = (1.f - alp) / lp.residshathr; + const float ath = -lp.residsha / tran; + const float bth = lp.residsha - ath * lp.residshathr; + + //highlight + const float tranh = rtengine::min(5.f, lp.residhithr); + const float athH = lp.residhi / tranh; + const float bthH = lp.residhi - athH * lp.residhithr; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + const float LL100 = wav_L0[i] / 327.68f; + + if (LL100 < lp.residshathr) { + const float kk = aalp * LL100 + alp; + wav_L0[i] *= (1.f + kk * lp.residsha / 200.f); + } else if (LL100 < lp.residshathr + tran) { + wav_L0[i] *= (1.f + (LL100 * ath + bth) / 200.f); + } + + if (LL100 > lp.residhithr) { + wav_L0[i] *= (1.f + lp.residhi / 200.f); + } else if (LL100 > (lp.residhithr - tranh)) { + wav_L0[i] *= (1.f + (LL100 * athH + bthH) / 200.f); + } + } + } + + if (contrast != 0.) { + + double avedbl = 0.0; // use double precision for large summations + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:avedbl) if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + avedbl += wav_L0[i]; + } + + float ave = avedbl / double(W_L * H_L); + + float avg = ave / 32768.f; + avg = LIM01(avg); + double contreal = 0.6 * contrast; + DiagonalCurve resid_contrast({ + DCT_NURBS, + 0, 0, + avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), + avg + (1. - avg) * (0.6 - contreal / 250.0), avg + (1. - avg) * (0.6 + contreal / 250.0), + 1, 1 + }); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + float buf = LIM01(wav_L0[i] / 32768.f); + buf = resid_contrast.getVal(buf); + buf *= 32768.f; + wav_L0[i] = buf; + } + + } + + float alow = 1.f; + float blow = 0.f; + + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + if (wavcurvelev || wavcurvecomp || wavcurvecompre) {//compress dynamic and blur + if (wavcurvelev && radlevblur > 0.f && blurena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, 1.f, 0.f, 0.f, 0.f); + } + + if (wavcurvecomp && comprena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 2, 1.f, 0.f, sigmadc, deltad); + } + + if (wavcurvecompre && compreena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 3, 1.f, thres, 0.f, 0.f); + } + } + + if (wavcurvecon && levelena) {//contrast by levels for luminance + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, 1.f, sk); + } + +//edge sharpness begin + if (lp.edgwena && level_bl == 0 && level_br >= 3 && locedgwavCurve && locedgwavutili && lp.strengthw > 0) { //needs the first levels to work! + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float edd = 3.f; + float eddlow = 15.f; + float eddlipinfl = 0.005f * lp.edgw + 0.4f; + float eddlipampl = 1.f + lp.basew / 50.f; + int W_L = wdspot->level_W(0);//provisory W_L H_L + int H_L = wdspot->level_H(0); + float *koeLi[12]; + float maxkoeLi[12] = {0.f}; + float *beta = new float[W_L * H_L]; + + float *koeLibuffer = new float[12 * H_L * W_L]; //12 + + for (int i = 0; i < 12; i++) { + koeLi[i] = &koeLibuffer[i * W_L * H_L]; + } + + for (int j = 0; j < 12; j++) { + for (int i = 0; i < W_L * H_L; i++) { + koeLi[j][i] = 0.f; + } + } + + array2D tmC(W_L, H_L); + + float gradw = lp.gradw; + float tloww = lp.tloww; +//StopWatch Stop1("test"); + for (int lvl = 0; lvl < 4; lvl++) { + for (int dir = 1; dir < 4; dir++) { + const int W_L = wdspot->level_W(lvl); + const int H_L = wdspot->level_H(lvl); + float* const* wav_L = wdspot->level_coeffs(lvl); + if (lvl == 3 && dir == 3) { + const float effect = lp.sigmaed; + constexpr float offset = 1.f; + float mea[10]; + calceffect(lvl, mean, sigma, mea, effect, offset); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(wav_L[dir][co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.5f; + } else if (WavCL < mea[7]) { + beta[co] = 0.3f; + } else if (WavCL < mea[8]) { + beta[co] = 0.2f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.1f; + } else { + beta[co] = 0.05f; + } + } + } + calckoe(wav_L, gradw, tloww, koeLi, lvl, dir, W_L, H_L, edd, maxkoeLi[lvl * 3 + dir - 1], tmC); + // return convolution KoeLi and maxkoeLi of level 0 1 2 3 and Dir Horiz, Vert, Diag + } + } + tmC.free(); +//Stop1.stop(); + float aamp = 1.f + lp.thigw / 100.f; + + const float alipinfl = (eddlipampl - 1.f) / (1.f - eddlipinfl); + const float blipinfl = eddlipampl - alipinfl; + + for (int lvl = 0; lvl < 4; lvl++) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int i = 1; i < H_L - 1; i++) { + for (int j = 1; j < W_L - 1; j++) { + //treatment of koeLi and maxkoeLi + if (lp.lip3) {//Sobel Canny algo improve with parameters + // comparison between pixel and neighbors + const auto neigh = lp.neiwmet == 1; + const auto kneigh = neigh ? 28.f : 38.f; + const auto somm = neigh ? 40.f : 50.f; + + for (int dir = 1; dir < 4; dir++) { //neighbors proxi + koeLi[lvl * 3 + dir - 1][i * W_L + j] = (kneigh * koeLi[lvl * 3 + dir - 1][i * W_L + j] + + 2.f * koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j + 1] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j - 1] + + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j + 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j + 1]) / somm; + } + } + + float interm = 0.f; + for (int dir = 1; dir < 4; dir++) { + //here I evaluate combination of vert / diag / horiz...we are with multiplicators of the signal + interm += SQR(koeLi[lvl * 3 + dir - 1][i * W_L + j]); + } + + interm = std::sqrt(interm) * 0.57736721f; + + constexpr float eps = 0.0001f; + // I think this double ratio (alph, beta) is better than arctg + + float alph = koeLi[lvl * 3][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between horizontal and vertical + float beta = koeLi[lvl * 3 + 2][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between diagonal and horizontal + + //alph evaluate the direction of the gradient regularity Lipschitz + // if = 1 we are on an edge + // if 0 we are not + // we can change and use log..or Arctg but why ?? we can change if need ... + //Liamp=1 for eddlipinfl + //liamp > 1 for alp >eddlipinfl and alph < 1 + //Liamp < 1 for alp < eddlipinfl and alph > 0 + if (alph > 1.f) { + alph = 1.f / alph; + } + + if (beta > 1.f) { + beta = 1.f / beta; + } + + //take into account diagonal + //if in same value OK + //if not no edge or reduction + float bet = 1.f; + + if (alph > eddlipinfl && beta < 0.85f * eddlipinfl) { //0.85 arbitrary value ==> eliminate from edge if H V D too different + bet = beta; + } + + float kampli; + if (alph > eddlipinfl) { + kampli = alipinfl * alph + blipinfl; //If beta low reduce kampli + kampli = SQR(bet) * kampli * aamp; + } else { + kampli = SQR(SQR(alph * bet)) / eddlipinfl; //Strong Reduce if beta low + kampli = kampli / aamp; + } + + + interm *= kampli; + + if (interm * eddlow < lp.tloww) { + interm = 0.01f; //eliminate too low values + } + + //we can change this part of algo==> not equal but ponderate + koeLi[lvl * 3][i * W_L + j] = koeLi[lvl * 3 + 1][i * W_L + j] = koeLi[lvl * 3 + 2][i * W_L + j] = interm; //new value + //here KoeLi contains values where gradient is high and coef high, and eliminate low values... + } + } + } + + constexpr float scales[10] = {1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, 512.f}; + float scaleskip[10]; + + for (int sc = 0; sc < 10; sc++) { + scaleskip[sc] = scales[sc] / sk; + } + + const float rad = lp.radiusw / 60.f; //radius ==> not too high value to avoid artifacts + float value = lp.strengthw / 8.f; //strength + + if (scaleskip[1] < 1.f) { + constexpr float atten01234 = 0.80f; + value *= atten01234 * scaleskip[1]; //for zoom < 100% reduce strength...I choose level 1...but!! + } + + constexpr float lim0 = 20.f; //arbitrary limit for low radius and level between 2 or 3 to 30 maxi + float repart = lp.detailw; + + if (lp.edgwmet != 1) { + float brepart; + if (lp.edgwmet == 0) { + brepart = 3.f; + } else /*if (lp.edgwmet == 2)*/ { + brepart = 0.5f; //arbitrary value to increase / decrease repart, between 1 and 0 + } + if (rad < lim0 / 60.f) { + const float arepart = - (brepart - 1.f) / (lim0 / 60.f); + repart *= arepart * rad + brepart; //linear repartition of repart + } + } + + const float bk = 1.f + repart / 50.f; + constexpr float al10 = 1.0f; //arbitrary value ==> less = take into account high levels + const float ak = - (bk - al10) / 10.f; //10 = maximum levels + + for (int lvl = 0; lvl < maxlvl; lvl++) { + if (MaxP[lvl] > 0.f) { //curve + const int W_L = wdspot->level_W(lvl); + const int H_L = wdspot->level_H(lvl); + float* const* wav_L = wdspot->level_coeffs(lvl); + const float koef = ak * lvl + bk; //modulate for levels : more levels high, more koef low ==> concentrated action on low levels, without or near for high levels + float expkoef = -pow_F(std::fabs(rad - lvl), koef); //reduce effect for high levels + if (lp.edgwmet == 2) { + if (rad < lim0 / 60.f && lvl == 0) { + expkoef *= abs(repart); //reduce effect for low values of rad and level=0==> quasi only level 1 is effective + } + } else if (lp.edgwmet == 0) { + if (rad < lim0 / 60.f && lvl == 1) { + expkoef /= repart; //increase effect for low values of rad and level=1==> quasi only level 0 is effective + } + } + //take into account local contrast + const float refin = value * xexpf(expkoef); + const float edgePrecalc = 1.f + refin; //estimate edge "pseudo variance" + constexpr float insigma = 0.666f; //SD + const float logmax = xlogf(MaxP[lvl]); //log Max + const float rapX = (mean[lvl] + sigma[lvl]) / MaxP[lvl]; //rapport between sD / max + const float inx = xlogf(insigma); + const float iny = xlogf(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / sigma[lvl]; + const float bsig = 0.5f - asig * mean[lvl]; + const float amean = 0.5f / mean[lvl]; + constexpr int borderL = 1; + constexpr float abssd = 4.f; //amplification reference + constexpr float bbssd = 2.f; //mini ampli + constexpr float maxamp = 2.5f; //maxi ampli at end + constexpr float maxampd = 10.f; //maxi ampli at end + constexpr float a_abssd = (maxamp - abssd) / 0.333f; + constexpr float b_abssd = maxamp - a_abssd; + constexpr float da_abssd = (maxampd - abssd) / 0.333f; + constexpr float db_abssd = maxampd - da_abssd; + constexpr float am = (abssd - bbssd) / 0.666f; + for (int dir = 1; dir < 4; dir++) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) if(multiThread) +#endif + for (int i = borderL; i < H_L - borderL; i++) { + for (int j = borderL; j < W_L - borderL; j++) { + const int k = i * W_L + j; + + float edge; + if (lvl < 4) { + edge = 1.f + (edgePrecalc - 1.f) * (koeLi[lvl * 3][k]) / (1.f + 0.9f * maxkoeLi[lvl * 3 + dir - 1]); + } else { + edge = edgePrecalc; + } + + float absciss = 0.f; + if (std::fabs(wav_L[dir][k]) >= mean[lvl] + sigma[lvl]) { //for max + absciss = xexpf((xlogf(std::fabs(wav_L[dir][k])) - logmax) * rap); + } else if (std::fabs(wav_L[dir][k]) >= mean[lvl]) { + absciss = asig * std::fabs(wav_L[dir][k]) + bsig; + } else /*if (std::fabs(wav_L[dir][k]) < mean[lvl])*/ { + absciss = amean * std::fabs(wav_L[dir][k]); + } + + // Threshold adjuster settings==> approximative for curve + //kmul about average cbrt(3--40 / 10)==>1.5 to 2.5 + //kmul about SD 10--60 / 35 ==> 2 + // kmul about low cbrt((5.f+cp.edg_low)/5.f);==> 1.5 + // kmul about max ==> 9 + // we can change these values + // result is different not best or bad than threshold slider...but similar + float kmul; + float kmuld; + + if (absciss > 0.666f && absciss < 1.f) { + kmul = a_abssd * absciss + b_abssd; //about max ==> kinterm + kmuld = da_abssd * absciss + db_abssd; + } else { + kmul = kmuld = absciss * am + bbssd; + } + + const float kc = kmul * (locedgwavCurve[absciss * 500.f] - 0.5f); + + float kinterm; + if (kc >= 0.f) { + constexpr float reduceeffect = 0.6f; + kinterm = 1.f + reduceeffect * kc; //about 1 to 3 general and big amplification for max (under 0) + } else { + const float kcd = kmuld * (locedgwavCurve[absciss * 500.f] - 0.5f); + kinterm = 1.f - SQR(kcd) / 10.f; + } + + if (kinterm < 0.f) { + kinterm = 0.01f; + } + + edge = std::max(edge * kinterm, 1.f); + wav_L[dir][k] *= 1.f + (edge - 1.f) * beta[k]; + } + } + } + } + } + + if (koeLibuffer) { + delete [] koeLibuffer; + } + + delete[] beta; + } + +//edge sharpness end + + if (locwavCurve && locwavutili && wavcurve) {//simple local contrast in function luminance + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspot->level_W(level); + int H_L = wdspot->level_H(level); + float klev = 1.f; + + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + float* const* wav_L = wdspot->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + constexpr float insigma = 0.666f; //SD + const float logmax = log(MaxP[level]); //log Max + const float rapX = (mean[level] + lp.sigmalc * sigma[level]) / MaxP[level]; //rapport between sD / max + const float inx = log(insigma); + const float iny = log(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / (sigma[level] * lp.sigmalc); + const float bsig = 0.5f - asig * mean[level]; + const float amean = 0.5f / mean[level]; + const float limit1 = mean[level] + lp.sigmalc * sigma[level]; + const float limit2 = mean[level]; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16 * W_L) if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + const float val = std::fabs(wav_L[dir][i]); + + float absciss; + if (val >= limit1) { //for max + const float valcour = xlogf(val); + absciss = xexpf((valcour - logmax) * rap); + } else if (val >= limit2) { + absciss = asig * val + bsig; + } else { + absciss = amean * val; + } + + const float kc = klev * (locwavCurve[absciss * 500.f] - 0.5f); + const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; + + float kinterm = 1.f + reduceeffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + wav_L[dir][i] *= kinterm <= 0.f ? 0.01f : kinterm; + } + } + } + } + } + //reconstruct all for L + wdspot->reconstruct(tmp[0], 1.f); + + bool reconstruct = false; + if (wavcurvecon && (chromalev != 1.f) && levelena) { // a if need ) {//contrast by levels for chroma a + // a + wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { + return; + } + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + reconstruct = true; + } + if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need + // a + if (!reconstruct) { + wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { + return; + } + } + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + reconstruct = true; + } + if (reconstruct) { + wdspot->reconstruct(tmpa[0], 1.f); + } + + reconstruct = false; + if (wavcurvecon && (chromalev != 1.f) && levelena) { // b if need ) {//contrast by levels for chroma b + //b + wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { + return; + } + //b + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + reconstruct = true; + } + + if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need + //b + if (!reconstruct) { + wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); + if (wdspot->memory_allocation_failed()) { + return; + } + } + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + reconstruct = true; + } + if (reconstruct) { + wdspot->reconstruct(tmpb[0], 1.f); + } +} + + +void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_numblox_W, float **tmp1, array2D *Lin, int numThreads, const struct local_params & lp, int chrom) +{ + BENCHFUN + + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + + array2D tilemask_in(TS, TS); + array2D tilemask_out(TS, TS); + + float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + float params_Ldetail = 0.f; + + int nfwd[2] = {TS, TS}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + const int border = rtengine::max(2, TS / 16); + + for (int i = 0; i < TS; ++i) { + float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < TS; ++j) { + float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; + + } + } + + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; + + + + const int numblox_W = ceil((static_cast(GW)) / offset) + 2; + const int numblox_H = ceil((static_cast(GH)) / offset) + 2; + + + //residual between input and denoised L channel + array2D Ldetail(GW, GH, ARRAY2D_CLEAR_DATA); + array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + array2D prov(GW, GH, ARRAY2D_CLEAR_DATA); + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[GW + TS + 2 * offset] ALIGNED16; +#ifdef _OPENMP + #pragma omp for +#endif + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - 1) * offset; + float * datarow = pBuf + offset; + + for (int i = 0; i < TS; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = rtengine::min(-row, GH - 1); + } else if (row >= GH) { + rr = rtengine::max(0, 2 * GH - 2 - row); + } + + for (int j = 0; j < GW; ++j) { + datarow[j] = ((*Lin)[rr][j] - tmp1[rr][j]); + prov[rr][j] = std::fabs(tmp1[rr][j]); + + } + + for (int j = -1 * offset; j < 0; ++j) { + datarow[j] = datarow[rtengine::min(-j, GW - 1)]; + } + + for (int j = GW; j < GW + TS + offset; ++j) { + datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; + }//now we have a padded data row + + //now fill this row of the blocks with Lab high pass data + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int indx = (hblk) * TS; //index of block in malloc + + if (top + i >= 0 && top + i < GH) { + int j; + + for (j = 0; j < rtengine::min((-left), TS); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < rtengine::min(TS, GW - left); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + // now process the vblk row of blocks for noise reduction + + float noisevar_Ldetail = 1.f; + + if (chrom == 0) { + params_Ldetail = rtengine::min(float(lp.noiseldetail), 99.9f); // max out to avoid div by zero when using noisevar_Ldetail as divisor + noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + } else if (chrom == 1) { + params_Ldetail = rtengine::min(float(lp.noisechrodetail), 99.9f); + // noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? + noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? + } + + // float noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + + + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + ImProcFunctions::RGBtile_denoise(fLblox, hblk, noisevar_Ldetail); + + }//end of horizontal block loop + + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - 1) * offset; + + //add row of blocks to output image tile + ImProcFunctions::RGBoutput_tile_row(Lblox, Ldetail, tilemask_out, GH, GW, topproc); + + }//end of vertical block loop + } + + //Threshold DCT from Alberto Grigio + const int detail_thresh = lp.detailthr; + array2D mask; + + if (detail_thresh > 0) { + mask(GW, GH); + float thr = log2lin(float(detail_thresh) / 200.f, 100.f); + buildBlendMask(prov, mask, GW, GH, thr); +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mask, mask, GW, GH, 20.0); + } + array2D m2(GW, GH); + constexpr float alfa = 0.856f; + const float beta = 1.f + std::sqrt(log2lin(thr, 100.f)); + buildGradientsMask(GW, GH, prov, m2, params_Ldetail / 100.f, 7, 3, alfa, beta, multiThread); + + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + mask[i][j] *= m2[i][j]; + } + } + } + + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + float d = Ldetail[i][j] / totwt[i][j]; + + if (detail_thresh > 0) { + d *= mask[i][j]; + } + + //may want to include masking threshold for large hipass data to preserve edges/detail + tmp1[i][j] += d; + } + } + + mask.free(); +//end Threshold DCT + + + delete Lin; + + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); + + +} + +void ImProcFunctions::DeNoise(int call, int del, float * slidL, float * slida, float * slidb, int aut, bool noiscfactiv, const struct local_params & lp, LabImage * originalmaskbl, int levred, float huerefblur, float lumarefblur, float chromarefblur, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + +//local denoise + //all these variables are to prevent use of denoise when non necessary + // but with qualmet = 2 (default for best quality) we must denoise chroma with little values to prevent artifacts due to variations of Hue + // but if user select voluntary denoise, it is that choice the good (prioritary) + bool execcolor = (lp.chro != 0.f || lp.ligh != 0.f || lp.cont != 0); // only if one slider or more is engaged + bool execbdl = (lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f) ;//only if user want cbdl + bool execdenoi = noiscfactiv && ((lp.colorena && execcolor) || (lp.tonemapena && lp.strengt != 0.f) || (lp.cbdlena && execbdl) || (lp.sfena && lp.strng > 0.f) || (lp.lcena && lp.lcamount > 0.f) || (lp.sharpena && lp.shrad > 0.42) || (lp.retiena && lp.str > 0.f) || (lp.exposena && lp.expcomp != 0.f) || (lp.expvib && lp.past != 0.f)); + bool execmaskden = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 0; + + if (((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f +// || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4 || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? + || execmaskden || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? + + StopWatch Stop1("locallab Denoise called"); + + if (aut == 0) { + MyMutex::MyLock lock(*fftwMutex); + } + + + if (lp.noisecf >= 0.01f || lp.noisecc >= 0.01f || aut == 1 || aut == 2) { + noiscfactiv = false; + levred = 7; + } + + int GW = transformed->W; + int GH = transformed->H; + + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + + if (call == 1 && GW >= mDEN && GH >= mDEN) { + + + LabImage tmp1(transformed->W, transformed->H); + LabImage tmp2(transformed->W, transformed->H); + tmp2.clear(); + + array2D *Lin = nullptr; + array2D *Ain = nullptr; + array2D *Bin = nullptr; + + int max_numblox_W = ceil((static_cast(GW)) / offset) + 2; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(GW)) / offset) + 2; + + + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + tmp1.L[ir][jr] = original->L[ir][jr]; + tmp1.a[ir][jr] = original->a[ir][jr]; + tmp1.b[ir][jr] = original->b[ir][jr]; + } + + // int DaubLen = 6; + + int levwavL = levred; + int skip = 1; + + wavelet_decomposition Ldecomp(tmp1.L[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition adecomp(tmp1.a[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition bdecomp(tmp1.b[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + + float madL[10][3]; + int edge = 2; + + if (!Ldecomp.memory_allocation_failed()) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int lvl = 0; lvl < levred; lvl++) { + for (int dir = 1; dir < 4; dir++) { + int Wlvl_L = Ldecomp.level_W(lvl); + int Hlvl_L = Ldecomp.level_H(lvl); + const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); + + madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); + } + } + + float vari[levred]; + float mxsl = 0.f; + // float mxsfl = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); + + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + } else if (levred == 4) { + edge = 3; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + vari[0] = SQR(slidL[0]); + vari[1] = SQR(slidL[1]); + vari[2] = SQR(slidL[2]); + vari[3] = SQR(slidL[3]); + vari[4] = SQR(slidL[4]); + vari[5] = SQR(slidL[5]); + vari[6] = SQR(slidL[6]); + float mxslid34 = rtengine::max(slidL[3], slidL[4]); + float mxslid56 = rtengine::max(slidL[5], slidL[6]); + mxsl = rtengine::max(mxslid34, mxslid56); + + } + + { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + } else if (aut == 2) { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + + vari[0] = rtengine::max(0.000001f, vari[0]); + vari[1] = rtengine::max(0.000001f, vari[1]); + vari[2] = rtengine::max(0.000001f, vari[2]); + vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); + + if (levred == 7) { + vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); + vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); + vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); + } + + float* noisevarlum = new float[GH * GW]; + int GW2 = (GW + 1) / 2; + + float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value + float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value + + float seuillow = 3000.f;//low + float seuilhigh = 18000.f;//high + int i = 10 - lp.noiselequal; + float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); + float bc = nvlh[i] - seuillow * ac; + //ac and bc for transition +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + float lN = tmp1.L[ir][jr]; + + if (lN < seuillow) { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvlh[i]; + } else if (lN < seuilhigh) { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = ac * lN + bc; + } else { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvll[i]; + } + } + + if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + } else { + + WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + } + + delete[] noisevarlum; + + } + } + + + float variC[levred]; + float variCb[levred]; + + float noisecfr = lp.noisecf; + float noiseccr = lp.noisecc; + + if (lp.adjch > 0.f) { + noisecfr = lp.noisecf + 0.1f * lp.adjch; + noiseccr = lp.noisecc + 0.1f * lp.adjch; + } + + float noisecfb = lp.noisecf; + float noiseccb = lp.noisecc; + + if (lp.adjch < 0.f) { + noisecfb = lp.noisecf - 0.1f * lp.adjch; + noiseccb = lp.noisecc - 0.1f * lp.adjch; + } + + + if (noisecfr < 0.f) { + noisecfr = 0.00001f; + } + + if (noiseccr < 0.f) { + noiseccr = 0.00001f; + } + + if (noisecfb < 0.f) { + noisecfb = 0.00001f; + } + + if (noiseccb < 0.f) { + noiseccb = 0.00001f; + } + + if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { + float maxcfine = 0.f; + float maxccoarse = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + variC[0] = SQR(noisecfr); + variC[1] = SQR(noisecfr); + variC[2] = SQR(noisecfr); + + variC[3] = SQR(noisecfr); + variC[4] = SQR(noisecfr); + variC[5] = SQR(noiseccr); + variC[6] = SQR(noiseccr); + + variCb[0] = SQR(noisecfb); + variCb[1] = SQR(noisecfb); + variCb[2] = SQR(noisecfb); + + variCb[3] = SQR(noisecfb); + variCb[4] = SQR(noisecfb); + variCb[5] = SQR(noiseccb); + variCb[6] = SQR(noiseccb); + + } else if (levred == 4) { + edge = 3; + variC[0] = SQR(lp.noisecf / 10.0); + variC[1] = SQR(lp.noisecf / 10.0); + variC[2] = SQR(lp.noisecf / 10.0); + variC[3] = SQR(lp.noisecf / 10.0); + + variCb[0] = SQR(lp.noisecf / 10.0); + variCb[1] = SQR(lp.noisecf / 10.0); + variCb[2] = SQR(lp.noisecf / 10.0); + variCb[3] = SQR(lp.noisecf / 10.0); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + variC[0] = SQR(slida[0]); + variC[1] = SQR(slida[1]); + variC[2] = SQR(slida[2]); + variC[3] = SQR(slida[3]); + variC[4] = SQR(slida[4]); + variC[5] = SQR(slida[5]); + variC[6] = SQR(slida[6]); + float maxc01 = rtengine::max(slida[0], slida[1]); + float maxc23 = rtengine::max(slida[2], slida[3]); + float max03 = rtengine::max(maxc01, maxc23); + float maxrf = rtengine::max(max03, slida[4]); + float maxrc = rtengine::max(slida[5], slida[6]); + + variCb[0] = SQR(slidb[0]); + variCb[1] = SQR(slidb[1]); + variCb[2] = SQR(slidb[2]); + variCb[3] = SQR(slidb[3]); + variCb[4] = SQR(slidb[4]); + variCb[5] = SQR(slidb[5]); + variCb[6] = SQR(slidb[6]); + float maxb01 = rtengine::max(slidb[0], slidb[1]); + float maxb23 = rtengine::max(slidb[2], slidb[3]); + float maxb03 = rtengine::max(maxb01, maxb23); + float maxbf = rtengine::max(maxb03, slidb[4]); + maxcfine = rtengine::max(maxrf, maxbf); + + float maxbc = rtengine::max(slidb[5], slidb[6]); + maxccoarse = rtengine::max(maxrc, maxbc); + + } + + { + float minic = 0.000001f; + + if (noiscfactiv) { + minic = 0.1f;//only for artifact shape detection + } + + float k1 = 0.f; + float k2 = 0.f; + float k3 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { + k1 = 0.05f; + k2 = 0.f; + k3 = 0.f; + } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { + k1 = 0.1f; + k2 = 0.0f; + k3 = 0.f; + } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { + k1 = 0.2f; + k2 = 0.1f; + k3 = 0.f; + } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { + k1 = 0.3f; + k2 = 0.25f; + k3 = 0.f; + } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { + k1 = 0.4f; + k2 = 0.25f; + k3 = 0.1f; + } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { + k1 = 0.5f; + k2 = 0.3f; + k3 = 0.15f; + } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { + k1 = 0.6f; + k2 = 0.45f; + k3 = 0.3f; + } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { + k1 = 0.7f; + k2 = 0.5f; + k3 = 0.4f; + } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { + k1 = 0.8f; + k2 = 0.6f; + k3 = 0.5f; + } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { + k1 = 0.85f; + k2 = 0.7f; + k3 = 0.6f; + } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { + k1 = 0.9f; + k2 = 0.8f; + k3 = 0.7f; + } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { + k1 = 1.f; + k2 = 1.f; + k3 = 0.9f; + + } else { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + } else if (aut == 2) { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + + + variC[0] = rtengine::max(minic, variC[0]); + variC[1] = rtengine::max(minic, k1 * variC[1]); + variC[2] = rtengine::max(minic, k2 * variC[2]); + variC[3] = rtengine::max(minic, k3 * variC[3]); + + variCb[0] = rtengine::max(minic, variCb[0]); + variCb[1] = rtengine::max(minic, k1 * variCb[1]); + variCb[2] = rtengine::max(minic, k2 * variCb[2]); + variCb[3] = rtengine::max(minic, k3 * variCb[3]); + + if (levred == 7) { + float k4 = 0.f; + float k5 = 0.f; + float k6 = 0.f; + + if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { + k4 = 0.1f; + k5 = 0.02f; + } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { + k4 = 0.15f; + k5 = 0.05f; + } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { + k4 = 0.15f; + k5 = 0.1f; + } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { + k4 = 0.3f; + k5 = 0.15f; + } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k4 = 0.6f; + k5 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k4 = 0.8f; + k5 = 0.6f; + } else { + k4 = 1.f; + k5 = 1.f; + } + + variC[4] = rtengine::max(0.000001f, k4 * variC[4]); + variC[5] = rtengine::max(0.000001f, k5 * variC[5]); + variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); + variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); + + if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { + k6 = 0.f; + } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k6 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k6 = 0.7f; + } else { + k6 = 1.f; + } + + variC[6] = rtengine::max(0.00001f, k6 * variC[6]); + variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); + + } + + float* noisevarchrom = new float[GH * GW]; + //noisevarchrom in function chroma + int GW2 = (GW + 1) / 2; + float nvch = 0.6f;//high value + float nvcl = 0.1f;//low value + + if ((lp.noisecf > 100.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { + nvch = 0.8f; + nvcl = 0.4f; + } + + float seuil = 4000.f;//low + float seuil2 = 15000.f;//high + //ac and bc for transition + float ac = (nvch - nvcl) / (seuil - seuil2); + float bc = nvch - seuil * ac; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + float cN = std::sqrt(SQR(tmp1.a[ir][jr]) + SQR(tmp1.b[ir][jr])); + + if (cN < seuil) { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvch; + } else if (cN < seuil2) { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = ac * cN + bc; + } else { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvcl; + } + } + + + float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); + + if ((lp.noisecc < 2.f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } else { + WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + + WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } + + delete[] noisevarchrom; + + } + } + + if (!Ldecomp.memory_allocation_failed()) { + Lin = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Lin)[i][j] = tmp1.L[i][j]; + } + } + + Ldecomp.reconstruct(tmp1.L[0]); + } + + if (!Ldecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.L, Lin, numThreads, lp, 0); + } + } + + if (!adecomp.memory_allocation_failed()) { + Ain = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Ain)[i][j] = tmp1.a[i][j]; + } + } + + adecomp.reconstruct(tmp1.a[0]); + } + + + if (!adecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.a, Ain, numThreads, lp, 1); + } + } + + + if (!bdecomp.memory_allocation_failed()) { + + Bin = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Bin)[i][j] = tmp1.b[i][j]; + } + } + + bdecomp.reconstruct(tmp1.b[0]); + } + + + if (!bdecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.b, Bin, numThreads, lp, 1); + } + + } + + // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + if(lp.smasktyp != 0) { + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + } else { + DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + } + + } else if (call == 2) { //simpleprocess + + int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + int bfw = int (lp.lx + lp.lxL) + del; + + if (bfh >= mDEN && bfw >= mDEN) { + LabImage bufwv(bfw, bfh); + bufwv.clear(true); + array2D *Lin = nullptr; + array2D *Ain = nullptr; + array2D *Bin = nullptr; + + int max_numblox_W = ceil((static_cast(bfw)) / offset) + 2; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(bfw)) / offset) + 2; + // these are needed only for creation of the plans and will be freed before entering the parallel loop + + + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + bufwv.L[loy - begy][lox - begx] = original->L[y][x]; + bufwv.a[loy - begy][lox - begx] = original->a[y][x]; + bufwv.b[loy - begy][lox - begx] = original->b[y][x]; + } + + } + + // int DaubLen = 6; + + int levwavL = levred; + int skip = 1; + wavelet_decomposition Ldecomp(bufwv.L[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition adecomp(bufwv.a[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition bdecomp(bufwv.b[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + float madL[10][3]; + int edge = 2; + + if (!Ldecomp.memory_allocation_failed()) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int lvl = 0; lvl < levred; lvl++) { + for (int dir = 1; dir < 4; dir++) { + int Wlvl_L = Ldecomp.level_W(lvl); + int Hlvl_L = Ldecomp.level_H(lvl); + + const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); + + madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); + } + } + + float vari[levred]; + float mxsl = 0.f; + // float mxsfl = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); + + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + } else if (levred == 4) { + edge = 3; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + vari[0] = SQR(slidL[0]); + vari[1] = SQR(slidL[1]); + vari[2] = SQR(slidL[2]); + vari[3] = SQR(slidL[3]); + vari[4] = SQR(slidL[4]); + vari[5] = SQR(slidL[5]); + vari[6] = SQR(slidL[6]); + float mxslid34 = rtengine::max(slidL[3], slidL[4]); + float mxslid56 = rtengine::max(slidL[5], slidL[6]); + mxsl = rtengine::max(mxslid34, mxslid56); + + } + + { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + } else if (aut == 2) { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + + } + + vari[0] = rtengine::max(0.000001f, vari[0]); + vari[1] = rtengine::max(0.000001f, vari[1]); + vari[2] = rtengine::max(0.000001f, vari[2]); + vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); + + if (levred == 7) { + vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); + vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); + vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); + } + + // float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL + float* noisevarlum = new float[bfh * bfw]; + int bfw2 = (bfw + 1) / 2; + + float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value + float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value + + float seuillow = 3000.f;//low + float seuilhigh = 18000.f;//high + int i = 10 - lp.noiselequal; + float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); + float bc = nvlh[i] - seuillow * ac; + //ac and bc for transition +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float lN = bufwv.L[ir][jr]; + + if (lN < seuillow) { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvlh[i]; + } else if (lN < seuilhigh) { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = ac * lN + bc; + } else { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvll[i]; + } + } + + + if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + } else { + WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + } + + delete [] noisevarlum; + + } + } + + + float variC[levred]; + float variCb[levred]; + + float noisecfr = lp.noisecf; + float noiseccr = lp.noisecc; + + if (lp.adjch > 0.f) { + noisecfr = lp.noisecf + 0.1f * lp.adjch; + noiseccr = lp.noisecc + 0.1f * lp.adjch; + } + + float noisecfb = lp.noisecf; + float noiseccb = lp.noisecc; + + if (lp.adjch < 0.f) { + noisecfb = lp.noisecf - 0.1f * lp.adjch; + noiseccb = lp.noisecc - 0.1f * lp.adjch; + } + + + if (noisecfr < 0.f) { + noisecfr = 0.00001f; + } + + if (noiseccr < 0.f) { + noiseccr = 0.00001f; + } + + if (noisecfb < 0.f) { + noisecfb = 0.00001f; + } + + if (noiseccb < 0.f) { + noiseccb = 0.00001f; + } + + + if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { + float maxcfine = 0.f; + float maxccoarse = 0.f; + + if (aut == 0) { + + if (levred == 7) { + edge = 2; + variC[0] = SQR(noisecfr); + variC[1] = SQR(noisecfr); + variC[2] = SQR(noisecfr); + + variC[3] = SQR(noisecfr); + variC[4] = SQR(noisecfr); + variC[5] = SQR(noiseccr); + variC[6] = SQR(noiseccr); + + variCb[0] = SQR(noisecfb); + variCb[1] = SQR(noisecfb); + variCb[2] = SQR(noisecfb); + + variCb[3] = SQR(noisecfb); + variCb[4] = SQR(noisecfb); + variCb[5] = SQR(noiseccb); + variCb[6] = SQR(noiseccb); + + } else if (levred == 4) { + edge = 3; + variC[0] = SQR(lp.noisecf / 10.0); + variC[1] = SQR(lp.noisecf / 10.0); + variC[2] = SQR(lp.noisecf / 10.0); + variC[3] = SQR(lp.noisecf / 10.0); + + variCb[0] = SQR(lp.noisecf / 10.0); + variCb[1] = SQR(lp.noisecf / 10.0); + variCb[2] = SQR(lp.noisecf / 10.0); + variCb[3] = SQR(lp.noisecf / 10.0); + + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + variC[0] = SQR(slida[0]); + variC[1] = SQR(slida[1]); + variC[2] = SQR(slida[2]); + variC[3] = SQR(slida[3]); + variC[4] = SQR(slida[4]); + variC[5] = SQR(slida[5]); + variC[6] = SQR(slida[6]); + float maxc01 = rtengine::max(slida[0], slida[1]); + float maxc23 = rtengine::max(slida[2], slida[3]); + float max03 = rtengine::max(maxc01, maxc23); + float maxrf = rtengine::max(max03, slida[4]); + float maxrc = rtengine::max(slida[5], slida[6]); + + variCb[0] = SQR(slidb[0]); + variCb[1] = SQR(slidb[1]); + variCb[2] = SQR(slidb[2]); + variCb[3] = SQR(slidb[3]); + variCb[4] = SQR(slidb[4]); + variCb[5] = SQR(slidb[5]); + variCb[6] = SQR(slidb[6]); + float maxb01 = rtengine::max(slidb[0], slidb[1]); + float maxb23 = rtengine::max(slidb[2], slidb[3]); + float maxb03 = rtengine::max(maxb01, maxb23); + float maxbf = rtengine::max(maxb03, slidb[4]); + maxcfine = rtengine::max(maxrf, maxbf); + + float maxbc = rtengine::max(slidb[5], slidb[6]); + maxccoarse = rtengine::max(maxrc, maxbc); + + } + + { + float minic = 0.000001f; + + if (noiscfactiv) { + minic = 0.1f;//only for artifact shape detection + } + + float k1 = 0.f; + float k2 = 0.f; + float k3 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { + k1 = 0.05f; + k2 = 0.f; + k3 = 0.f; + } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { + k1 = 0.1f; + k2 = 0.0f; + k3 = 0.f; + } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { + k1 = 0.2f; + k2 = 0.1f; + k3 = 0.f; + } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { + k1 = 0.3f; + k2 = 0.25f; + k3 = 0.f; + } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { + k1 = 0.4f; + k2 = 0.25f; + k3 = 0.1f; + } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { + k1 = 0.5f; + k2 = 0.3f; + k3 = 0.15f; + } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { + k1 = 0.6f; + k2 = 0.45f; + k3 = 0.3f; + } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { + k1 = 0.7f; + k2 = 0.5f; + k3 = 0.4f; + } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { + k1 = 0.8f; + k2 = 0.6f; + k3 = 0.5f; + } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { + k1 = 0.85f; + k2 = 0.7f; + k3 = 0.6f; + } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { + k1 = 0.9f; + k2 = 0.8f; + k3 = 0.7f; + } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { + k1 = 1.f; + k2 = 1.f; + k3 = 0.9f; + + } else { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + } else if (aut == 2) { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + + variC[0] = rtengine::max(minic, variC[0]); + variC[1] = rtengine::max(minic, k1 * variC[1]); + variC[2] = rtengine::max(minic, k2 * variC[2]); + variC[3] = rtengine::max(minic, k3 * variC[3]); + + variCb[0] = rtengine::max(minic, variCb[0]); + variCb[1] = rtengine::max(minic, k1 * variCb[1]); + variCb[2] = rtengine::max(minic, k2 * variCb[2]); + variCb[3] = rtengine::max(minic, k3 * variCb[3]); + + if (levred == 7) { + float k4 = 0.f; + float k5 = 0.f; + float k6 = 0.f; + + if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { + k4 = 0.1f; + k5 = 0.02f; + } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { + k4 = 0.15f; + k5 = 0.05f; + } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { + k4 = 0.15f; + k5 = 0.1f; + } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { + k4 = 0.3f; + k5 = 0.15f; + } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k4 = 0.6f; + k5 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k4 = 0.8f; + k5 = 0.6f; + } else { + k4 = 1.f; + k5 = 1.f; + } + + + variC[4] = rtengine::max(0.000001f, k4 * variC[4]); + variC[5] = rtengine::max(0.000001f, k5 * variC[5]); + variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); + variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); + + if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { + k6 = 0.f; + } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k6 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k6 = 0.7f; + } else { + k6 = 1.f; + } + + variC[6] = rtengine::max(0.00001f, k6 * variC[6]); + variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); + } + + float* noisevarchrom = new float[bfh * bfw]; + int bfw2 = (bfw + 1) / 2; + float nvch = 0.6f;//high value + float nvcl = 0.1f;//low value + + if ((lp.noisecf > 30.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { + nvch = 0.8f; + nvcl = 0.4f; + } + + float seuil = 4000.f;//low + float seuil2 = 15000.f;//high + //ac and bc for transition + float ac = (nvch - nvcl) / (seuil - seuil2); + float bc = nvch - seuil * ac; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float cN = std::sqrt(SQR(bufwv.a[ir][jr]) + SQR(bufwv.b[ir][jr])); + + if (cN < seuil) { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvch; + } else if (cN < seuil2) { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = ac * cN + bc; + } else { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvcl; + } + } + + float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); + + if ((lp.noisecc < 0.02f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } else { + WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + + WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } + + delete[] noisevarchrom; + } + } + + if (!Ldecomp.memory_allocation_failed()) { + Lin = new array2D(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Lin)[i][j] = bufwv.L[i][j]; + } + } + + Ldecomp.reconstruct(bufwv.L[0]); + } + + + if (!Ldecomp.memory_allocation_failed() && aut == 0) { + + + if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.L, Lin, numThreads, lp, 0); + } + } + + + if (!adecomp.memory_allocation_failed()) { + Ain = new array2D(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Ain)[i][j] = bufwv.a[i][j]; + } + } + + adecomp.reconstruct(bufwv.a[0]); + } + + if (!adecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.a, Ain, numThreads, lp, 1); + } + } + + + if (!bdecomp.memory_allocation_failed()) { + Bin = new array2D(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Bin)[i][j] = bufwv.b[i][j]; + } + } + + bdecomp.reconstruct(bufwv.b[0]); + } + + if (!bdecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.b, Bin, numThreads, lp, 1); + } + } + + if(lp.smasktyp != 0) { + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } else { + DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } + + // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } + } + } + +} + +float triangle(float a, float a1, float b) +{ + if (a != b) { + float b1; + float a2 = a1 - a; + + if (b < a) { + b1 = b + a2 * b / a ; + } else { + b1 = b + a2 * (65535.f - b) / (65535.f - a); + } + + return b1; + } + + return a1; +} + +void rgbtone(float& maxval, float& medval, float& minval, const LUTf& lutToneCurve) +{ + float minvalold = minval, medvalold = medval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); +} + +void clarimerge(struct local_params& lp, float &mL, float &mC, bool &exec, LabImage *tmpresid, int wavelet_level, int sk, int numThreads) +{ + if (mL != 0.f && mC == 0.f) { + mC = 0.0001f; + exec = true; + } + + if (mC != 0.f && mL == 0.f) { + mL = 0.0001f; + exec = true; + } + + if (mL != 0.f && mC != 0.f) { + exec = true; + } + + if (mL != 0.f) { + + wavelet_decomposition *wdspotresid = new wavelet_decomposition(tmpresid->L[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresid->memory_allocation_failed()) { + return; + } + + int maxlvlresid = wdspotresid->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresid->level_W(level); + int H_L = wdspotresid->level_H(level); + float* const* wav_Lresid = wdspotresid->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresid[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0resid = wdspotresid->get_coeff0(); + int W_L = wdspotresid->level_W(0); + int H_L = wdspotresid->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0resid[i] = 0.f; + } + } + + wdspotresid->reconstruct(tmpresid->L[0], 1.f); + delete wdspotresid; + } + + + if (mC != 0.f) { + + wavelet_decomposition *wdspotresida = new wavelet_decomposition(tmpresid->a[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresida->memory_allocation_failed()) { + return; + } + + int maxlvlresid = wdspotresida->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresida->level_W(level); + int H_L = wdspotresida->level_H(level); + float* const* wav_Lresida = wdspotresida->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresida[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0resida = wdspotresida->get_coeff0(); + int W_L = wdspotresida->level_W(0); + int H_L = wdspotresida->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0resida[i] = 0.f; + } + } + + wdspotresida->reconstruct(tmpresid->a[0], 1.f); + delete wdspotresida; + + wavelet_decomposition *wdspotresidb = new wavelet_decomposition(tmpresid->b[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresidb->memory_allocation_failed()) { + return; + } + + maxlvlresid = wdspotresidb->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresidb->level_W(level); + int H_L = wdspotresidb->level_H(level); + float* const* wav_Lresidb = wdspotresidb->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresidb[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0residb = wdspotresidb->get_coeff0(); + int W_L = wdspotresidb->level_W(0); + int H_L = wdspotresidb->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0residb[i] = 0.f; + } + } + + wdspotresidb->reconstruct(tmpresid->b[0], 1.f); + delete wdspotresidb; + } +} + +void ImProcFunctions::Lab_Local( + int call, int sp, float** shbuffer, LabImage * original, LabImage * transformed, LabImage * reserved, LabImage * lastorig, int cx, int cy, int oW, int oH, int sk, + const LocretigainCurve& locRETgainCcurve, const LocretitransCurve& locRETtransCcurve, + const LUTf& lllocalcurve, bool locallutili, + const LUTf& cllocalcurve, bool localclutili, + const LUTf& lclocalcurve, bool locallcutili, + const LocLHCurve& loclhCurve, const LocHHCurve& lochhCurve, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LUTf& lmaskexplocalcurve, bool localmaskexputili, + const LUTf& lmaskSHlocalcurve, bool localmaskSHutili, + const LUTf& lmaskviblocalcurve, bool localmaskvibutili, + const LUTf& lmasktmlocalcurve, bool localmasktmutili, + LUTf& lmaskretilocalcurve, bool localmaskretiutili, + const LUTf& lmaskcblocalcurve, bool localmaskcbutili, + const LUTf& lmaskbllocalcurve, bool localmaskblutili, + const LUTf& lmasklclocalcurve, bool localmasklcutili, + const LUTf& lmasklocal_curve, bool localmask_utili, + + const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, + const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, + const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, + const LocCCmaskCurve& locccmasvibCurve, bool lcmasvibutili, const LocLLmaskCurve& locllmasvibCurve, bool llmasvibutili, const LocHHmaskCurve& lochhmasvibCurve, bool lhmasvibutili, + const LocCCmaskCurve& locccmascbCurve, bool lcmascbutili, const LocLLmaskCurve& locllmascbCurve, bool llmascbutili, const LocHHmaskCurve& lochhmascbCurve, bool lhmascbutili, + const LocCCmaskCurve& locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve& locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve& lochhmasretiCurve, bool lhmasretiutili, + const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, + const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, + const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, + const LocCCmaskCurve& locccmas_Curve, bool lcmas_utili, const LocLLmaskCurve& locllmas_Curve, bool llmas_utili, const LocHHmaskCurve& lochhmas_Curve, bool lhmas_utili, + const LocHHmaskCurve& lochhhmas_Curve, bool lhhmas_utili, + const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, + const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, + const LocwavCurve& locwavCurve, bool locwavutili, + const LocwavCurve& loclevwavCurve, bool loclevwavutili, + const LocwavCurve& locconwavCurve, bool locconwavutili, + const LocwavCurve& loccompwavCurve, bool loccompwavutili, + const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, + const LocwavCurve& locwavCurveden, bool locwavdenutili, + const LocwavCurve& locedgwavCurve, bool locedgwavutili, + const LocwavCurve& loclmasCurve_wav, bool lmasutili_wav, + + bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, + double& huerefblur, double& chromarefblur, double& lumarefblur, double& hueref, double& chromaref, double& lumaref, double& sobelref, int &lastsav, + bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, + float& minCD, float& maxCD, float& mini, float& maxi, float& Tmean, float& Tsigma, float& Tmin, float& Tmax + ) +{ + //general call of others functions : important return hueref, chromaref, lumaref + if (!params->locallab.enabled) { + return; + } + + BENCHFUN + + constexpr int del = 3; // to avoid crash with [loy - begy] and [lox - begx] and bfh bfw // with gtk2 [loy - begy-1] [lox - begx -1 ] and del = 1 + struct local_params lp; + calcLocalParams(sp, oW, oH, params->locallab, lp, prevDeltaE, llColorMask, llColorMaskinv, llExpMask, llExpMaskinv, llSHMask, llSHMaskinv, llvibMask, lllcMask, llsharMask, llcbMask, llretiMask, llsoftMask, lltmMask, llblMask, ll_Mask, locwavCurveden, locwavdenutili); + + const float radius = lp.rad / (sk * 1.4f); //0 to 70 ==> see skip + int levred; + bool noiscfactiv; + + if (lp.qualmet == 2) { //suppress artifacts with quality enhanced + levred = 4; + noiscfactiv = true; + } else { + levred = 7; + noiscfactiv = false; + } + +//lastsav for save restore image + lastsav = 0; + + if (lp.excmet == 1 && call <= 3) {//exclude + const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + const int bfw = int (lp.lx + lp.lxL) + del; + const int begy = lp.yc - lp.lyT; + const int begx = lp.xc - lp.lxL; + const int yEn = lp.yc + lp.ly; + const int xEn = lp.xc + lp.lx; + LabImage bufreserv(bfw, bfh); + array2D bufsob(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int y = rtengine::max(begy - cy, 0); y < rtengine::min(yEn - cy, original->H); y++) { + const int loy = cy + y; + + for (int x = rtengine::max(begx - cx, 0); x < rtengine::min(xEn - cx, original->W); x++) { + const int lox = cx + x; + + bufsob[loy - begy][lox - begx] = bufreserv.L[loy - begy][lox - begx] = reserved->L[y][x]; + bufreserv.a[loy - begy][lox - begx] = reserved->a[y][x]; + bufreserv.b[loy - begy][lox - begx] = reserved->b[y][x]; + } + } + + array2D ble(bfw, bfh); + const float radiussob = 1.f / (sk * 1.4f); + SobelCannyLuma(ble, bufsob, bfw, bfh, radiussob); + array2D &guid = bufsob; + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + ble[ir][jr] /= 32768.f; + guid[ir][jr] /= 32768.f; + } + + + const float blur = 25 / sk * (2.f + 2.5f * lp.struexp); + + rtengine::guidedFilter(guid, ble, ble, blur, 0.0001, multiThread); + +// const float blur = 25 / sk * (10.f + 0.8f * lp.struexp); + +// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiThread); + + double sombel = 0.f; + const int ncsobel = bfh * bfw; + + array2D &deltasobelL = guid; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:sombel) if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float val = ble[ir][jr] * 32768.f; + sombel += val; + deltasobelL[ir][jr] = val; + } + } + + const float meansob = sombel / ncsobel; + Exclude_Local(deltasobelL, hueref, chromaref, lumaref, sobelref, meansob, lp, original, transformed, &bufreserv, reserved, cx, cy, sk); + } + +//encoding lab at the beginning + if (lp.logena) { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfh >= mSP && bfw >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); + log_encode(tmpImage.get(), lp, multiThread, bfw, bfh); + rgb2lab(*(tmpImage.get()), *bufexpfin, params->icm.workingProfile); + tmpImage.reset(); + + //here begin graduated filter + //first solution "easy" but we can do other with log_encode...to see the results + if (lp.strlog != 0.f) { + struct grad_params gplog; + calclocalGradientParams(lp, gplog, ystart, xstart, bfw, bfh, 11); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gplog, jr, ir); + } + } + } + + //end graduated + transit_shapedetect2(call, 11, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + +//Prepare mask for Blur and noise and Denoise + bool denoiz = false; + + if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f || lp.bilat > 0.f) && lp.denoiena) { + denoiz = true; + } + + bool blurz = false; + bool delt = params->locallab.spots.at(sp).deltae; + bool astool = params->locallab.spots.at(sp).toolbl; + + if (((radius > 1.5 * GAUSS_SKIP) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 1 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { + blurz = true; + } + + const int GW = transformed->W; + const int GH = transformed->H; + + LabImage * originalmaskbl = nullptr; + std::unique_ptr bufmaskorigbl; + std::unique_ptr bufmaskblurbl; + std::unique_ptr bufgb; + std::unique_ptr bufprov(new LabImage(GW, GH)); + + if (denoiz || blurz || lp.denoiena || lp.blurena) { + bufgb.reset(new LabImage(GW, GH)); + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + bufmaskorigbl.reset(new LabImage(GW, GH)); + bufmaskblurbl.reset(new LabImage(GW, GH)); + originalmaskbl = new LabImage(GW, GH); + } + + array2D ble(GW, GH); + array2D blechro(GW, GH); + array2D hue(GW, GH); + array2D guid(GW, GH); + float meanfab, fab; + mean_fab(0, 0, GW, GH, bufgb.get(), original, fab, meanfab, lp.chromabl, multiThread); + float chromult = 1.f - 0.01f * lp.chromabl; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH; y++) { + for (int x = 0; x < GW; x++) { + bufgb->L[y][x] = original->L[y][x]; + bufgb->a[y][x] = original->a[y][x]; + bufgb->b[y][x] = original->b[y][x]; + } + } + + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskbl; + JaggedArray blendstru(GW, GH); + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + if (strumask > 0.f) { + float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 + buildBlendMask(bufgb->L, blendstru, GW, GH, delstrumask); + const float radblur = 0.02f * 0.1f * std::fabs(lp.radmabl); + const float rm = radblur / sk; + + if (rm > 0) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(blendstru, blendstru, GW, GH, rm); + } + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + float kmaskLexp = 0.f; + float kmaskCH = 0.f; + float kmasstru = 0.f; + + if (strumask > 0.f && !astool) { + kmasstru = bufgb->L[ir][jr] * blendstru[ir][jr]; + } + + if (locllmasblCurve && llmasblutili) { + const float ligh = bufgb->L[ir][jr] / 32768.f; + kmaskLexp = 32768.f * LIM01(1.f - locllmasblCurve[500.f * ligh]); + } + + if (lp.showmaskblmet != 4) { + if (locccmasblCurve && lcmasblutili) { + const float chromask = 0.0001f + std::sqrt(SQR((bufgb->a[ir][jr]) / fab) + SQR((bufgb->b[ir][jr]) / fab)); + kmaskCH = LIM01(1.f - locccmasblCurve[500.f * chromask]); + } + } + + if (lochhmasblCurve && lhmasblutili) { + const float huema = xatan2f(bufgb->b[ir][jr], bufgb->a[ir][jr]); + float h = Color::huelab_to_huehsv2(huema); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + const float valHH = LIM01(1.f - lochhmasblCurve[500.f * h]); + + if (lp.showmaskblmet != 4) { + kmaskCH += chromult * valHH; + } + + kmaskLexp += 32768.f * valHH; + } + + bufmaskblurbl->L[ir][jr] = clipLoc(kmaskLexp + kmasstru); + bufmaskblurbl->a[ir][jr] = kmaskCH; + bufmaskblurbl->b[ir][jr] = kmaskCH; + ble[ir][jr] = bufmaskblurbl->L[ir][jr] / 32768.f; + hue[ir][jr] = xatan2f(bufmaskblurbl->b[ir][jr], bufmaskblurbl->a[ir][jr]); + const float chromah = std::sqrt(SQR(bufmaskblurbl->b[ir][jr]) + SQR(bufmaskblurbl->a[ir][jr])); + blechro[ir][jr] = chromah / 32768.f; + guid[ir][jr] = Color::L2Y(bufgb->L[ir][jr]) / 32768.f; + } + } + + const std::unique_ptr bufprov(new LabImage(GW, GH)); + + bufprov->CopyFrom(bufmaskblurbl.get(), multiThread); + + if (lp.radmabl != 0.f) { + float blur = lp.radmabl; + blur = blur < 0.f ? -1.f / blur : 1.f + blur; + const int r1 = rtengine::max(4 / sk * blur + 0.5f, 1); + const int r2 = rtengine::max(25 / sk * blur + 0.5f, 1); + + constexpr float epsilmax = 0.005f; + constexpr float epsilmin = 0.00001f; + + const float aepsil = (epsilmax - epsilmin) / 100.f; + const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; + const float epsil = lp.radmabl < 0.f ? 0.001f : aepsil * lp.radmabl + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2 * epsil, multiThread); + + // guidedFilter(guid, ble, ble, lp.radmabl * 10.f / sk, 0.001, multiThread, 4); + } + + LUTf lutTonemaskbl(65536); + calcGammaLut(lp.gammabl, lp.slomabl, lutTonemaskbl); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + const float2 sincosval = xsincosf(hue[ir][jr]); + bufmaskblurbl->L[ir][jr] = LIM01(ble[ir][jr]) * 32768.f; + const float L_ = 2.f * bufmaskblurbl->L[ir][jr]; + bufmaskblurbl->L[ir][jr] = lutTonemaskbl[L_]; + bufmaskblurbl->a[ir][jr] = 32768.f * sincosval.y * blechro[ir][jr]; + bufmaskblurbl->b[ir][jr] = 32768.f * sincosval.x * blechro[ir][jr]; + } + } + } + + if (strumask > 0.f && astool && (lp.enablMask || lp.showmaskblmet == 3)) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] *= (1.f + blendstru[ir][jr]); + } + } + } + + if (lmaskbllocalcurve && localmaskblutili && (lp.enablMask || lp.showmaskblmet == 3)) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] = 0.5f * lmaskbllocalcurve[2.f * bufmaskblurbl->L[ir][jr]]; + } + } + + const int highli = params->locallab.spots.at(sp).shadmaskbl; + + if (highli > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, highli, 0, 40, sk, 50, 0); + } + + const int shado = params->locallab.spots.at(sp).shadmaskblsha; + + if (shado > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, 0, shado, 40, sk, 0, 60); + } + + int wavelet_level = params->locallab.spots.at(sp).shadmaskbl; + int maxlvl = wavelet_level; + + int minwin = rtengine::min(GW, GH); + int maxlevelspot = 9; + + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + bool wavcurvemask = false; + + if (loclmasCurveblwav && lmasutiliblwav && (lp.enablMask || lp.showmaskblmet == 3)) { + for (int i = 0; i < 500; i++) { + if (loclmasCurveblwav[i] != 0.5) { + wavcurvemask = true; + } + } + } + + if (wavcurvemask && (lp.enablMask || lp.showmaskblmet == 3)) { + const int level_bl = params->locallab.spots.at(sp).csthresholdblur.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdblur.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdblur.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdblur.getTopRight(); + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + + wavelet_decomposition *wdspotbl = new wavelet_decomposition(bufmaskblurbl->L[0], GW, GH, maxlvl, 1, sk, numThreads, lp.daubLen); + if (wdspotbl->memory_allocation_failed()) { + return; + } + + + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + + Evaluate2(*wdspotbl, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alow = 1.f; + float blow = 0.f; + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspotbl->level_W(level); + int H_L = wdspotbl->level_H(level); + float* const *wav_L = wdspotbl->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + float insigma = 0.666f; //SD + float logmax = log(MaxP[level]); //log Max + float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max + float inx = log(insigma); + float iny = log(rapX); + float rap = inx / iny; //koef + float asig = 0.166f / (sigma[level]); + float bsig = 0.5f - asig * mean[level]; + float amean = 0.5f / mean[level]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + if(loclmasCurveblwav && lmasutiliblwav) { + float absciss; + float &val = wav_L[dir][i]; + + if (fabsf(val) >= (mean[level] + sigma[level])) { //for max + float valcour = xlogf(fabsf(val)); + float valc = valcour - logmax; + float vald = valc * rap; + absciss = xexpf(vald); + } else if (fabsf(val) >= mean[level]) { + absciss = asig * fabsf(val) + bsig; + } else { + absciss = amean * fabsf(val); + } + + float klev = 1.f; + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + float kc = klev * (loclmasCurveblwav[absciss * 500.f] - 0.5f); + float amplieffect = kc <= 0.f ? 1.f : 4.f; + + float kinterm = 1.f + amplieffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + val *= kinterm; + + } + } + } + + } + } + wdspotbl->reconstruct(bufmaskblurbl->L[0], 1.f); + delete wdspotbl; + + } + + + // deltae Mask with scope + int sco = params->locallab.spots.at(sp).scopemask; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + if (delt && lp.blurmet == 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + JaggedArray rdE(GW, GH); + deltaEforMask(rdE, GW, GH, bufgb.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.balance, lp.balanceh); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] = bufprov->L[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->L[ir][jr] - bufprov->L[ir][jr]); + bufmaskblurbl->a[ir][jr] = bufprov->a[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->a[ir][jr] - bufprov->a[ir][jr]); + bufmaskblurbl->b[ir][jr] = bufprov->b[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->b[ir][jr] - bufprov->b[ir][jr]); + } + } + } + + const float lap = params->locallab.spots.at(sp).lapmaskbl; + const bool pde = params->locallab.spots.at(sp).laplac; + const float lumask = params->locallab.spots.at(sp).lumask; + + if (lap > 0.f && (lp.enablMask || lp.showmaskblmet == 3)) { + const float *datain = bufmaskblurbl->L[0]; + const std::unique_ptr data_tmp(new float[GH * GW]); + + if (!pde) { + ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, GW, GH, 200.f * lap); + } else { + ImProcFunctions::retinex_pde(datain, data_tmp.get(), GW, GH, 12.f * lap, 1.f, nullptr, 0, 0, 1); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < GH; y++) { + for (int x = 0; x < GW; x++) { + bufmaskblurbl->L[y][x] = data_tmp[y * GW + x]; + } + } + } + + const float radiusb = 1.f / sk; + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + const int invers = lp.blurmet == 1 ? 1 : 0; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufmaskblurbl->L, bufmaskorigbl->L, GW, GH, radiusb); + gaussianBlur(bufmaskblurbl->a, bufmaskorigbl->a, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); + gaussianBlur(bufmaskblurbl->b, bufmaskorigbl->b, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); + } + + if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { + blendmask(lp, 0, 0, cx, cy, GW, GH, bufgb.get(), original, bufmaskorigbl.get(), originalmaskbl, lp.blendmabl, lp.blendmabl, invers); + } else if (lp.showmaskblmet == 3) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufgb.get(), transformed, bufmaskorigbl.get(), invers); + return; + } + } + +//end mask + } + + bool execmaskblur = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 1; + if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || execmaskblur) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image + // if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image + std::unique_ptr tmp1; + std::unique_ptr tmp2; + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + int bfhr = bfh; + int bfwr = bfw; + + bool fft = params->locallab.spots.at(sp).fftwbl; + int isogr = params->locallab.spots.at(sp).isogr; + int strengr = params->locallab.spots.at(sp).strengr; + int scalegr = params->locallab.spots.at(sp).scalegr; + + + + if (bfw >= mSP && bfh >= mSP) { + if (lp.blurmet == 0 && (fft || lp.rad > 30.f)) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufgbi(new LabImage(GW, GH)); + + //here mask is used with plain image for normal and inverse + //if it is possible to optimize with maskcalccol(), I don't to preserve visibility + if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { + + if (lp.blurmet == 0) { + if (bfw > 0 && bfh > 0) { + tmp1.reset(new LabImage(bfw, bfh)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + } + } else if (lp.blurmet == 1) { + tmp1.reset(new LabImage(transformed->W, transformed->H)); + tmp2.reset(new LabImage(transformed->W, transformed->H)); + + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + tmp2->L[y][x] = original->L[y][x]; + tmp2->a[y][x] = original->a[y][x]; + tmp2->b[y][x] = original->b[y][x]; + bufgbi->L[y][x] = original->L[y][x]; + bufgbi->a[y][x] = original->a[y][x]; + bufgbi->b[y][x] = original->b[y][x]; + } + } + + } + + + if (lp.blurmet == 0 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { + if (fft || lp.rad > 30.f) { + if (lp.chromet == 0) { + ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); + } else if (lp.chromet == 1) { + ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); + } else if (lp.chromet == 2) { + ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); + } + } else { + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + if (lp.chromet == 0) + { + gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); + } + + else if (lp.chromet == 1) + { + gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); + gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); + } else if (lp.chromet == 2) + { + gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); + gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); + gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); + } + } + } + + } else if (lp.blurmet == 1 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { + if (fft || lp.rad > 30.f) { + if (lp.chromet == 0) { + ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); + } + + else if (lp.chromet == 1) { + ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); + } else if (lp.chromet == 2) { + ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); + } + + } else { + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + if (lp.chromet == 0) + { + gaussianBlur(original->L, tmp1->L, GW, GH, radius); + } else if (lp.chromet == 1) + + { + gaussianBlur(original->a, tmp1->a, GW, GH, radius); + gaussianBlur(original->b, tmp1->b, GW, GH, radius); + } else if (lp.chromet == 2) + + { + gaussianBlur(original->L, tmp1->L, GW, GH, radius); + gaussianBlur(original->a, tmp1->a, GW, GH, radius); + gaussianBlur(original->b, tmp1->b, GW, GH, radius); + } + } + } + } + + + //add noise + if (tmp1.get() && lp.stren > 0.1f && lp.blmet == 0) { + float mean = 0.f;//0 best result + float variance = lp.stren ; + addGaNoise(tmp1.get(), tmp1.get(), mean, variance, sk) ; + } + + //add grain + if (lp.blmet == 0 && strengr > 0) { + int wi = bfw; + int he = bfh; + + if (lp.blurmet == 1) { + wi = GW; + he = GH; + } + + if (tmp1.get()) { + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(wi, he); + + for (int y = 0; y < he ; y++) { + for (int x = 0; x < wi; x++) { + tmpImage->g(y, x) = tmp1->L[y][x]; + tmpImage->r(y, x) = tmp1->a[y][x]; + tmpImage->b(y, x) = tmp1->b[y][x]; + } + } + + + filmGrain(tmpImage, isogr, strengr, scalegr, wi, he); + + for (int y = 0; y < he ; y++) { + for (int x = 0; x < wi; x++) { + tmp1->L[y][x] = tmpImage->g(y, x); + tmp1->a[y][x] = tmpImage->r(y, x); + tmp1->b[y][x] = tmpImage->b(y, x); + } + } + + delete tmpImage; + } + } + + Median medianTypeL = Median::TYPE_3X3_STRONG; + Median medianTypeAB = Median::TYPE_3X3_STRONG; + + if (lp.medmet == 0) { + medianTypeL = medianTypeAB = Median::TYPE_3X3_STRONG; + } else if (lp.medmet == 1) { + medianTypeL = medianTypeAB = Median::TYPE_5X5_STRONG; + } else if (lp.medmet == 2) { + medianTypeL = medianTypeAB = Median::TYPE_7X7; + } else if (lp.medmet == 3) { + medianTypeL = medianTypeAB = Median::TYPE_9X9; + } + + if (lp.blurmet == 0 && lp.blmet == 1 && lp.medmet != -1) { + float** tmL; + int wid = bfw; + int hei = bfh; + tmL = new float*[hei]; + + for (int i = 0; i < hei; ++i) { + tmL[i] = new float[wid]; + } + + if (lp.chromet == 0) { + Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); + } + + else if (lp.chromet == 1) { + Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + } else if (lp.chromet == 2) { + Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); + Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + } + + for (int i = 0; i < hei; ++i) { + delete[] tmL[i]; + } + + delete[] tmL; + + } else if (lp.blurmet == 1 && lp.blmet == 1) { + float** tmL; + int wid = GW; + int hei = GH; + tmL = new float*[hei]; + + for (int i = 0; i < hei; ++i) { + tmL[i] = new float[wid]; + } + + if (lp.chromet == 0) { + Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); + } else if (lp.chromet == 1) { + Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + } else if (lp.chromet == 2) { + Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); + Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + } + + for (int i = 0; i < hei; ++i) { + delete[] tmL[i]; + } + + delete[] tmL; + } + + if (lp.blurmet == 0 && lp.blmet == 2) { + + if (lp.guidb > 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + } + } + + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(bfw, bfh); + lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); + array2D LL(bfw, bfh); + array2D rr(bfw, bfh); + array2D gg(bfw, bfh); + array2D bb(bfw, bfh); + array2D guide(bfw, bfh); + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + LL[y][x] = tmp1->L[y][x]; + float ll = LL[y][x] / 32768.f; + guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); + rr[y][x] = tmpImage->r(y, x); + gg[y][x] = tmpImage->g(y, x); + bb[y][x] = tmpImage->b(y, x); + + } + } + array2D iR(bfw, bfh, rr, 0); + array2D iG(bfw, bfh, gg, 0); + array2D iB(bfw, bfh, bb, 0); + array2D iL(bfw, bfh, LL, 0); + + int r = rtengine::max(int(lp.guidb / sk), 1); + + const float epsil = 0.001f * std::pow(2, - lp.epsb); + + if (lp.chromet == 0) { + rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); + } else if (lp.chromet == 1) { + rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); + } else if (lp.chromet == 2) { + rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); + gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); + bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); + tmpImage->r(y, x) = rr[y][x]; + tmpImage->g(y, x) = gg[y][x]; + tmpImage->b(y, x) = bb[y][x]; + + } + } + + rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); + + if (lp.chromet == 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); + tmp1->L[y][x] = LL[y][x]; + } + } + } + + delete tmpImage; + } + + } else if (lp.blurmet == 1 && lp.blmet == 2) { + + if (lp.guidb > 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + tmp1->L[y][x] = original->L[y][x]; + tmp1->a[y][x] = original->a[y][x]; + tmp1->b[y][x] = original->b[y][x]; + tmp2->L[y][x] = original->L[y][x]; + } + } + + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(GW, GH); + lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); + array2D LL(GW, GH); + array2D rr(GW, GH); + array2D gg(GW, GH); + array2D bb(GW, GH); + array2D guide(GW, GH); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + LL[y][x] = tmp1->L[y][x]; + float ll = LL[y][x] / 32768.f; + guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); + rr[y][x] = tmpImage->r(y, x); + gg[y][x] = tmpImage->g(y, x); + bb[y][x] = tmpImage->b(y, x); + + } + } + + array2D iR(GW, GH, rr, 0); + array2D iG(GW, GH, gg, 0); + array2D iB(GW, GH, bb, 0); + array2D iL(GW, GH, LL, 0); + + int r = rtengine::max(int(lp.guidb / sk), 1); + + const float epsil = 0.001f * std::pow(2, - lp.epsb); + + if (lp.chromet == 0) { + rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); + } else if (lp.chromet == 1) { + rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); + } else if (lp.chromet == 2) { + rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); + gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); + bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); + tmpImage->r(y, x) = rr[y][x]; + tmpImage->g(y, x) = gg[y][x]; + tmpImage->b(y, x) = bb[y][x]; + + } + } + + rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); + + if (lp.chromet == 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); + tmp1->L[y][x] = LL[y][x]; + } + } + } + delete tmpImage; + } + } + + if (tmp1.get()) { + JaggedArray bufchro(lp.blurmet == 1 ? GW : bfw, lp.blurmet == 1 ? GH : bfh); + float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufchro[y][x] *= coefC; + } + } + } + + if (lp.blurmet == 0) { //blur and noise (center) +// BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if(lp.smasktyp != 1) { + BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + } else { + BlurNoise_Local(tmp1.get(), original, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } else if (lp.blurmet == 1) { + // InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + if(lp.smasktyp != 1) { + InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + } else { + InverseBlurNoise_Local(original, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + } + + //local impulse + if ((lp.bilat > 0.f) && lp.denoiena) { + const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + const int bfw = int (lp.lx + lp.lxL) + del; + + std::unique_ptr bufwv; + + if (call == 2) {//simpleprocess + bufwv.reset(new LabImage(bfw, bfh)); //buffer for data in zone limit + + const int begy = lp.yc - lp.lyT; + const int begx = lp.xc - lp.lxL; + const int yEn = lp.yc + lp.ly; + const int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = rtengine::max(0, begy - cy); y < rtengine::min(transformed->H, yEn - cy); y++) { + const int loy = cy + y; + + for (int x = rtengine::max(0, begx - cx); x < rtengine::min(transformed->W, xEn - cx); x++) { + const int lox = cx + x; + bufwv->L[loy - begy][lox - begx] = original->L[y][x]; + bufwv->a[loy - begy][lox - begx] = original->a[y][x]; + bufwv->b[loy - begy][lox - begx] = original->b[y][x]; + } + } + } else {//dcrop.cc + bufwv.reset(new LabImage(transformed->W, transformed->H)); + bufwv->CopyFrom(original, multiThread); + } //end dcrop + + const double threshold = lp.bilat / 20.0; + + if (bfh > 8 && bfw > 8) { + ImProcFunctions::impulse_nr(bufwv.get(), threshold); + } + + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, *(bufwv.get()), cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +//local denoise + + if (lp.denoiena) { + float slidL[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float slida[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float slidb[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + constexpr int aut = 0; + DeNoise(call, del, slidL, slida, slidb, aut, noiscfactiv, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (denoiz || blurz || lp.denoiena || lp.blurena) { + delete originalmaskbl; + } + +//begin cbdl + if ((lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f || lp.clarityml != 0.f || lp.contresid != 0.f || lp.enacbMask || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4 || lp.prevdE) && lp.cbdlena) { + if (call <= 3) { //call from simpleprocess dcrop improcc + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + if (bfw > 65 && bfh > 65) { + array2D bufsh(bfw, bfh); + JaggedArray bufchrom(bfw, bfh, true); + const std::unique_ptr loctemp(new LabImage(bfw, bfh)); + const std::unique_ptr origcbdl(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigcb; + std::unique_ptr bufmaskblurcb; + std::unique_ptr originalmaskcb; + + if (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4) { + bufmaskorigcb.reset(new LabImage(bfw, bfh)); + bufmaskblurcb.reset(new LabImage(bfw, bfh)); + originalmaskcb.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + loctemp->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskcbmet == 3) { + showmaske = true; + } + + if (lp.enacbMask) { + enaMask = true; + } + + if (lp.showmaskcbmet == 4) { + deltaE = true; + } + + if (lp.showmaskcbmet == 2) { + modmask = true; + } + + if (lp.showmaskcbmet == 1) { + modif = true; + } + + if (lp.showmaskcbmet == 0) { + zero = true; + } + + float chrom = lp.chromacbm;; + float rad = lp.radmacb; + float gamma = lp.gammacb; + float slope = lp.slomacb; + float blendm = lp.blendmacb; + float lap = params->locallab.spots.at(sp).lapmaskcb; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int lumask = params->locallab.spots.at(sp).lumask; + int shado = 0; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + bool lmasutilicolwav = false; + float amountcd = 0.f; + float anchorcd = 50.f; + int shortcu = 0; //lp.mergemet; //params->locallab.spots.at(sp).shortc; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, loctemp.get(), bufmaskorigcb.get(), originalmaskcb.get(), original, reserved, inv, lp, + 0.f, false, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f, -1 + ); + + if (lp.showmaskcbmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, loctemp.get(), transformed, bufmaskorigcb.get(), 0); + + return; + } + + constexpr float b_l = -5.f; + constexpr float t_l = 25.f; + constexpr float t_r = 120.f; + constexpr float b_r = 170.f; + constexpr double skinprot = 0.; + int choice = 0; + + if (lp.showmaskcbmet == 0 || lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 4 || lp.enacbMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufsh[y - ystart][x - xstart] = origcbdl->L[y - ystart][x - xstart] = original->L[y][x]; + loctemp->a[y - ystart][x - xstart] = origcbdl->a[y - ystart][x - xstart] = original->a[y][x]; + loctemp->b[y - ystart][x - xstart] = origcbdl->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + if (lp.clarityml != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 + lp.mulloc[5] = 1.001f; + } + + if (lp.contresid != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 + lp.mulloc[5] = 1.001f; + } + + ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, lp.mulloc, 1.f, lp.threshol, lp.clarityml, lp.contresid, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); + + if (lp.softradiuscb > 0.f) { + softproc(origcbdl.get(), loctemp.get(), lp.softradiuscb, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); + } + + } + + transit_shapedetect(6, loctemp.get(), originalmaskcb.get(), bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + bool nochroma = (lp.showmaskcbmet == 2 || lp.showmaskcbmet == 1); + + //chroma CBDL begin here + if (lp.chromacb > 0.f && !nochroma) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufsh[ir][jr] = std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr])); + } + } + + float multc[6]; + float clarich = 0.5f * lp.clarityml; + + if (clarich > 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity + lp.mulloc[0] = 1.01f; + } + + if (lp.contresid != 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity + lp.mulloc[0] = 1.01f; + } + + for (int lv = 0; lv < 6; lv++) { + multc[lv] = rtengine::max((lp.chromacb * (lp.mulloc[lv] - 1.f)) + 1.f, 0.01f); + } + + choice = 1; + ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, multc, rtengine::max(lp.chromacb, 1.f), lp.threshol, clarich, 0.f, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); + + + float minC = loctemp->L[0][0] - std::sqrt(SQR(loctemp->a[0][0]) + SQR(loctemp->b[0][0])); + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchrom[ir][jr] = (loctemp->L[ir][jr] - std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr]))); + minC = rtengine::min(minC, bufchrom[ir][jr]); + maxC = rtengine::max(maxC, bufchrom[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchrom[ir][jr] *= coefC; + } + } + } + + transit_shapedetect(7, loctemp.get(), nullptr, bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + bufsh.free(); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + + +//end cbdl_Local + +//vibrance + + if (lp.expvib && (lp.past != 0.f || lp.satur != 0.f || lp.strvib != 0.f || lp.war != 0 || lp.strvibab != 0.f || lp.strvibh != 0.f || lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4 || lp.prevdE) && lp.vibena) { //interior ellipse renforced lightness and chroma //locallutili + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfw >= mSP && bfh >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigvib; + std::unique_ptr bufmaskblurvib; + std::unique_ptr originalmaskvib; + + if (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4) { + bufmaskorigvib.reset(new LabImage(bfw, bfh)); + bufmaskblurvib.reset(new LabImage(bfw, bfh)); + originalmaskvib.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskvibmet == 3) { + showmaske = true; + } + + if (lp.enavibMask) { + enaMask = true; + } + + if (lp.showmaskvibmet == 4) { + deltaE = true; + } + + if (lp.showmaskvibmet == 2) { + modmask = true; + } + + if (lp.showmaskvibmet == 1) { + modif = true; + } + + if (lp.showmaskvibmet == 0) { + zero = true; + } + + float chrom = lp.chromavib; + float rad = lp.radmavib; + float gamma = lp.gammavib; + float slope = lp.slomavib; + float blendm = lp.blendmavib; + float lap = params->locallab.spots.at(sp).lapmaskvib; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + float amountcd = 0.f; + float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigvib.get(), originalmaskvib.get(), original, reserved, inv, lp, + 0.f, false, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + if (lp.showmaskvibmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigvib.get(), 0); + + return; + } + + if (lp.showmaskvibmet == 0 || lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2 || lp.showmaskvibmet == 4 || lp.enavibMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + VibranceParams vibranceParams; + vibranceParams.enabled = params->locallab.spots.at(sp).expvibrance; + vibranceParams.pastels = params->locallab.spots.at(sp).pastels; + vibranceParams.saturated = params->locallab.spots.at(sp).saturated; + vibranceParams.psthreshold = params->locallab.spots.at(sp).psthreshold; + vibranceParams.protectskins = params->locallab.spots.at(sp).protectskins; + vibranceParams.avoidcolorshift = params->locallab.spots.at(sp).avoidcolorshift; + vibranceParams.pastsattog = params->locallab.spots.at(sp).pastsattog; + vibranceParams.skintonescurve = params->locallab.spots.at(sp).skintonescurve; + + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + + if (lp.strvibh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 9); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + float aa = bufexpfin->a[ir][jr]; + float bb = bufexpfin->b[ir][jr]; + float chrm = std::sqrt(SQR(aa) + SQR(bb)); + float HH = xatan2f(bb, aa); + + float newhr = 0.f; + float cor = 0.f; + + if (factor < 1.f) { + cor = - 2.5f * (1.f - factor); + } else if (factor > 1.f) { + cor = 0.03f * (factor - 1.f); + } + + newhr = HH + cor; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + float2 sincosval = xsincosf(newhr); + bufexpfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufexpfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + + if (lp.strvib != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 7); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufexpfin->L[ir][jr] *= factor; + } + } + + if (lp.strvibab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 8); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufexpfin->a[ir][jr] *= factor; + bufexpfin->b[ir][jr] *= factor; + } + } + + ImProcFunctions::vibrance(bufexpfin.get(), vibranceParams, params->toneCurve.hrenabled, params->icm.workingProfile); + + if (params->locallab.spots.at(sp).warm != 0) { + ImProcFunctions::ciecamloc_02float(sp, bufexpfin.get()); + } + + + transit_shapedetect2(call, 2, bufexporig.get(), bufexpfin.get(), originalmaskvib.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + + } + + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + + +//Tone mapping + + if ((lp.strengt != 0.f || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4 || lp.prevdE) && lp.tonemapena && !params->epd.enabled) { + if (call <= 3) { //simpleprocess dcrop improcc + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfw >= mSP && bfh >= mSP) { + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + std::unique_ptr bufgb(new LabImage(bfw, bfh)); + const std::unique_ptr tmp1(new LabImage(bfw, bfh)); + const std::unique_ptr bufgbm(new LabImage(bfw, bfh)); + const std::unique_ptr tmp1m(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigtm; + std::unique_ptr bufmaskblurtm; + std::unique_ptr originalmasktm; + + // if (lp.showmasktmmet == 0 || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { + if (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { + bufmaskorigtm.reset(new LabImage(bfw, bfh)); + bufmaskblurtm.reset(new LabImage(bfw, bfh)); + originalmasktm.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + bufgb->a[y - ystart][x - xstart] = original->a[y][x]; + bufgb->b[y - ystart][x - xstart] = original->b[y][x]; + bufgbm->L[y - ystart][x - xstart] = original->L[y][x]; + bufgbm->a[y - ystart][x - xstart] = original->a[y][x]; + bufgbm->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmasktmmet == 3) { + showmaske = true; + } + + if (lp.enatmMask) { + enaMask = true; + } + + if (lp.showmasktmmet == 4) { + deltaE = true; + } + + if (lp.showmasktmmet == 2) { + modmask = true; + } + + if (lp.showmasktmmet == 1) { + modif = true; + } + + if (lp.showmasktmmet == 0) { + zero = true; + } + + float chrom = lp.chromatm;; + float rad = lp.radmatm; + float gamma = lp.gammatm; + float slope = lp.slomatm; + float blendm = lp.blendmatm; + float lap = params->locallab.spots.at(sp).lapmasktm; + bool pde = params->locallab.spots.at(sp).laplac; + int lumask = params->locallab.spots.at(sp).lumask; + + if (!params->locallab.spots.at(sp).enatmMaskaft) { + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0; //lp.mergemet;// params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = 0.f; + float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgbm.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, + 0.f, false, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + if (lp.showmasktmmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgbm.get(), transformed, bufmaskorigtm.get(), 0); + + return; + } + } + + if (lp.showmasktmmet == 0 || lp.showmasktmmet == 1 || lp.showmasktmmet == 2 || lp.showmasktmmet == 4 || lp.showmasktmmet == 3 || lp.enatmMask) { + constexpr int itera = 0; + ImProcFunctions::EPDToneMaplocal(sp, bufgb.get(), tmp1.get(), itera, sk);//iterate to 0 calculate with edgstopping, improve result, call=1 dcrop we can put iterate to 5 + + tmp1m->CopyFrom(tmp1.get(), multiThread); //save current result + bool enatmMasktmap = params->locallab.spots.at(sp).enatmMaskaft; + + if (enatmMasktmap) { + //calculate new values for original, originalmasktm, bufmaskorigtm...in function of tmp1 + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + int lumask = params->locallab.spots.at(sp).lumask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = 0.f; + float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, tmp1.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, + 0.f, false, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + if (lp.showmasktmmet == 3) {//display mask + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, tmp1.get(), transformed, bufmaskorigtm.get(), 0); + + return; + } + + } + + tmp1->CopyFrom(tmp1m.get(), multiThread); //restore current result + + + float minL = tmp1->L[0][0] - bufgb->L[0][0]; + float maxL = minL; + float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); + float maxC = minC; + +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxL) reduction(min:minL) reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = tmp1->L[ir][jr] - bufgb->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coef == 0.f) { + coef = 1.f; + } else { + coef = 1.f / coef; + } + + if (coefC == 0.f) { + coefC = 1.f; + } else { + coefC = 1.f / coefC; + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + buflight[y][x] *= coef; + bufchro[y][x] *= coefC; + } + } + + // transit_shapedetect_retinex(call, 4, bufgb.get(),bufmaskorigtm.get(), originalmasktm.get(), buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + transit_shapedetect2(call, 8, bufgb.get(), tmp1.get(), originalmasktm.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + // transit_shapedetect(8, tmp1.get(), originalmasktm.get(), bufchro, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + bufgb.reset(); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + +//end TM + + +//shadow highlight + bool tonequ = false; + + if (lp.mullocsh[0] != 0 || lp.mullocsh[1] != 0 || lp.mullocsh[2] != 0 || lp.mullocsh[3] != 0 || lp.mullocsh[4] != 0) { + tonequ = true; + } + + bool tonecurv = false; + + if (params->locallab.spots.at(sp).gamSH != 2.4 || params->locallab.spots.at(sp).sloSH != 12.92) { + tonecurv = true; + } + + if (! lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.strSH != 0.f || lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4 || lp.prevdE) && call < 3 && lp.hsena) { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + + if (bfw >= mSP && bfh >= mSP) { + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigSH; + std::unique_ptr bufmaskblurSH; + std::unique_ptr originalmaskSH; + + if (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4) { + bufmaskorigSH.reset(new LabImage(bfw, bfh)); + bufmaskblurSH.reset(new LabImage(bfw, bfh)); + originalmaskSH.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskSHmet == 3) { + showmaske = true; + } + + if (lp.enaSHMask) { + enaMask = true; + } + + if (lp.showmaskSHmet == 4) { + deltaE = true; + } + + if (lp.showmaskSHmet == 2) { + modmask = true; + } + + if (lp.showmaskSHmet == 1) { + modif = true; + } + + if (lp.showmaskSHmet == 0) { + zero = true; + } + + float chrom = lp.chromaSH; + float rad = lp.radmaSH; + float gamma = lp.gammaSH; + float slope = lp.slomaSH; + float blendm = lp.blendmaSH; + float lap = params->locallab.spots.at(sp).lapmaskSH; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = params->locallab.spots.at(sp).fatamountSH; + float anchorcd = params->locallab.spots.at(sp).fatanchorSH; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigSH.get(), originalmaskSH.get(), original, reserved, inv, lp, + 0.f, false, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + if (lp.showmaskSHmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigSH.get(), 0); + + return; + } + + if (lp.showmaskSHmet == 0 || lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2 || lp.showmaskSHmet == 4 || lp.enaSHMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; + bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; + bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + if (lp.shmeth == 0) { + ImProcFunctions::shadowsHighlights(bufexpfin.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); + } + +//gradient + struct grad_params gp; + + if (lp.strSH != 0.f) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 2); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufexpfin->L[ir][jr] *= factor; + } + } + + if (lp.shmeth == 1) { + double scal = (double)(sk); + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(bfw, bfh); + lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); + + if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB + float gamtone = params->locallab.spots.at(sp).gamSH; + float slotone = params->locallab.spots.at(sp).sloSH; + cmsHTRANSFORM dummy = nullptr; + workingtrc(tmpImage, tmpImage, bfw, bfh, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); + workingtrc(tmpImage, tmpImage, bfw, bfh, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); + } + + if (tonequ) { + tmpImage->normalizeFloatTo1(); + array2D Rtemp(bfw, bfh, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); + array2D Gtemp(bfw, bfh, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); + array2D Btemp(bfw, bfh, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); + tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, scal, multiThread); + tmpImage->normalizeFloatTo65535(); + } + + rgb2lab(*tmpImage, *bufexpfin, params->icm.workingProfile); + + delete tmpImage; + } + } + + transit_shapedetect2(call, 9, bufexporig.get(), bufexpfin.get(), originalmaskSH.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } else if (lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.showmaskSHmetinv == 1 || lp.enaSHMaskinv) && call < 3 && lp.hsena) { + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskSH; + const std::unique_ptr bufcolorig(new LabImage(GW, GH)); + + if (lp.enaSHMaskinv || lp.showmaskSHmetinv == 1) { + bufmaskblurcol.reset(new LabImage(GW, GH, true)); + originalmaskSH.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufcolorig->L[y][x] = original->L[y][x]; + } + } + + int inv = 1; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskSHmetinv == 1) { + showmaske = true; + } + + if (lp.enaSHMaskinv) { + enaMask = true; + } + + if (lp.showmaskSHmetinv == 0) { + zero = true; + } + + float chrom = lp.chromaSH; + float rad = lp.radmaSH; + float gamma = lp.gammaSH; + float slope = lp.slomaSH; + float blendm = lp.blendmaSH; + float lap = params->locallab.spots.at(sp).lapmaskSH; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + // bool delt = params->locallab.spots.at(sp).deltae; + bool delt = false; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = params->locallab.spots.at(sp).fatamountSH; + float anchorcd = params->locallab.spots.at(sp).fatanchorSH; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskSH.get(), original, reserved, inv, lp, + 0.f, false, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + + if (lp.showmaskSHmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); + + return; + } + + float adjustr = 2.f; + InverseColorLight_Local(tonequ, tonecurv, sp, 2, lp, originalmaskSH.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +// soft light and retinex_pde + if (lp.strng > 0.f && call <= 3 && lp.sfena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + //variable for fast FFTW + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.softmet == 1) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + SoftLightParams softLightParams; + softLightParams.enabled = true; + softLightParams.strength = lp.strng; + + if (lp.softmet == 0) { + ImProcFunctions::softLight(bufexpfin.get(), softLightParams); + } else if (lp.softmet == 1) { + + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); + const std::unique_ptr dE(new float[bfwr * bfhr]); + + deltaEforLaplace(dE.get(), lp.lap, bfwr, bfhr, bufexpfin.get(), hueref, chromaref, lumaref); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + datain[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + const int showorig = lp.showmasksoftmet >= 5 ? 0 : lp.showmasksoftmet; + MyMutex::MyLock lock(*fftwMutex); + ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, 8.f * lp.strng, 1.f, dE.get(), showorig, 1, 1); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexpfin->L[y][x] = dataout[y * bfwr + x]; + } + } + } + + transit_shapedetect2(call, 3, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + //local contrast + bool wavcurve = false; + bool wavcurvelev = false; + bool wavcurvecon = false; + bool wavcurvecomp = false; + bool wavcurvecompre = false; + + if (lp.locmet == 1) { + if (locwavCurve && locwavutili) { + for (int i = 0; i < 500; i++) { + if (locwavCurve[i] != 0.5) { + wavcurve = true; + break; + } + } + } + if (loclevwavCurve && loclevwavutili) { + for (int i = 0; i < 500; i++) { + if (loclevwavCurve[i] != 0.) { + wavcurvelev = true; + break; + } + } + } + if (locconwavCurve && locconwavutili) { + for (int i = 0; i < 500; i++) { + if (locconwavCurve[i] != 0.5) { + wavcurvecon = true; + break; + } + } + } + if (loccompwavCurve && loccompwavutili) { + for (int i = 0; i < 500; i++) { + if (loccompwavCurve[i] != 0.) { + wavcurvecomp = true; + break; + } + } + } + if (loccomprewavCurve && loccomprewavutili) { + for (int i = 0; i < 500; i++) { + if (loccomprewavCurve[i] != 0.75) { + wavcurvecompre = true; + break; + } + } + } + } + + if ((lp.lcamount > 0.f || wavcurve || lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4 || lp.prevdE || lp.strwav != 0.f || wavcurvelev || wavcurvecon || wavcurvecomp || wavcurvecompre || lp.edgwena || params->locallab.spots.at(sp).residblur > 0.f || params->locallab.spots.at(sp).levelblur > 0.f || params->locallab.spots.at(sp).residcont != 0.f || params->locallab.spots.at(sp).clarilres != 0.f || params->locallab.spots.at(sp).claricres != 0.f) && call < 3 && lp.lcena) { + + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSPwav && bfh >= mSPwav) {//avoid too small spot for wavelet + if (lp.ftwlc) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + std::unique_ptr bufmaskblurlc; + std::unique_ptr originalmasklc; + std::unique_ptr bufmaskoriglc; + + if (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4) { + bufmaskblurlc.reset(new LabImage(bfw, bfh)); + originalmasklc.reset(new LabImage(bfw, bfh)); + bufmaskoriglc.reset(new LabImage(bfw, bfh)); + } + + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + const std::unique_ptr bufgb(new LabImage(bfw, bfh)); + std::unique_ptr tmp1(new LabImage(bfw, bfh)); + const std::unique_ptr tmpresid(new LabImage(bfw, bfh)); + const std::unique_ptr tmpres(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + bufgb->a[y - ystart][x - xstart] = original->a[y][x]; + bufgb->b[y - ystart][x - xstart] = original->b[y][x]; + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + tmpresid->L[y - ystart][x - xstart] = original->L[y][x]; + tmpresid->a[y - ystart][x - xstart] = original->a[y][x]; + tmpresid->b[y - ystart][x - xstart] = original->b[y][x]; + tmpres->L[y - ystart][x - xstart] = original->L[y][x]; + tmpres->a[y - ystart][x - xstart] = original->a[y][x]; + tmpres->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufgb->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmasklcmet == 3) { + showmaske = true; + } + + if (lp.enalcMask) { + enaMask = true; + } + + if (lp.showmasklcmet == 4) { + deltaE = true; + } + + if (lp.showmasklcmet == 2) { + modmask = true; + } + + if (lp.showmasklcmet == 1) { + modif = true; + } + + if (lp.showmasklcmet == 0) { + zero = true; + } + + + float chrom = lp.chromalc; + float rad = lp.radmalc; + float blendm = lp.blendmalc; + float gamma = 1.f; + float slope = 0.f; + float lap = 0.f; //params->locallab.spots.at(sp).lapmaskexp; + bool pde = false; //params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shado = 0; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + float amountcd = 0.f; + float anchorcd = 50.f; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgb.get(), bufmaskoriglc.get(), originalmasklc.get(), original, reserved, inv, lp, + 0.f, false, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 + ); + + if (lp.showmasklcmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgb.get(), transformed, bufmaskoriglc.get(), 0); + + return; + } + + if (lp.showmasklcmet == 0 || lp.showmasklcmet == 1 || lp.showmasklcmet == 2 || lp.showmasklcmet == 4 || lp.enalcMask) { + + if (lp.locmet == 0) { + LocalContrastParams localContrastParams; + LocallabParams locallabparams; + localContrastParams.enabled = true; + localContrastParams.radius = params->locallab.spots.at(sp).lcradius; + localContrastParams.amount = params->locallab.spots.at(sp).lcamount; + localContrastParams.darkness = params->locallab.spots.at(sp).lcdarkness; + localContrastParams.lightness = params->locallab.spots.at(sp).lightness; + bool fftwlc = false; + + if (!lp.ftwlc) { // || (lp.ftwlc && call != 2)) { + ImProcFunctions::localContrast(tmp1.get(), tmp1->L, localContrastParams, fftwlc, sk); + } else { + const std::unique_ptr tmpfftw(new LabImage(bfwr, bfhr)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmpfftw->L[y][x] = tmp1->L[y][x]; + tmpfftw->a[y][x] = tmp1->a[y][x]; + tmpfftw->b[y][x] = tmp1->b[y][x]; + } + } + + fftwlc = true; + ImProcFunctions::localContrast(tmpfftw.get(), tmpfftw->L, localContrastParams, fftwlc, sk); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmp1->L[y][x] = tmpfftw->L[y][x]; + tmp1->a[y][x] = tmpfftw->a[y][x]; + tmp1->b[y][x] = tmpfftw->b[y][x]; + } + } + + } + } else if (lp.locmet == 1) { //wavelet && sk ==1 + int wavelet_level = 1 + params->locallab.spots.at(sp).csthreshold.getBottomRight();//retrieve with +1 maximum wavelet_level + float mL = params->locallab.spots.at(sp).clarilres / 100.f; + float mC = params->locallab.spots.at(sp).claricres / 100.f; + float softr = params->locallab.spots.at(sp).clarisoft; + float mL0 = 0.f; + float mC0 = 0.f; +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + // adap maximum level wavelet to size of RT-spot + int minwin = rtengine::min(bfw, bfh); + int maxlevelspot = 10;//maximum possible + + // adap maximum level wavelet to size of crop + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + // printf("minwin=%i maxlevelavant=%i maxlespot=%i\n", minwin, wavelet_level, maxlevelspot); + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + // printf("maxlevel=%i\n", wavelet_level); + bool exec = false; + bool origlc = params->locallab.spots.at(sp).origlc; + + if (origlc) {//merge only with original + clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); + } + + int maxlvl = wavelet_level; + const float contrast = params->locallab.spots.at(sp).residcont; + int level_bl = params->locallab.spots.at(sp).csthreshold.getBottomLeft(); + int level_hl = params->locallab.spots.at(sp).csthreshold.getTopLeft(); + int level_br = params->locallab.spots.at(sp).csthreshold.getBottomRight(); + int level_hr = params->locallab.spots.at(sp).csthreshold.getTopRight(); + const float radblur = (params->locallab.spots.at(sp).residblur) / sk; + const bool blurlc = params->locallab.spots.at(sp).blurlc; + const float radlevblur = (params->locallab.spots.at(sp).levelblur) / sk; + const float sigma = params->locallab.spots.at(sp).sigma; + const float offs = params->locallab.spots.at(sp).offset; + const float sigmadc = params->locallab.spots.at(sp).sigmadc; + const float deltad = params->locallab.spots.at(sp).deltad; + // const float fatres = params->locallab.spots.at(sp).fatres; + const float chrol = params->locallab.spots.at(sp).chromalev; + const float chrobl = params->locallab.spots.at(sp).chromablu; + const bool blurena = params->locallab.spots.at(sp).wavblur; + const bool levelena = params->locallab.spots.at(sp).wavcont; + const bool comprena = params->locallab.spots.at(sp).wavcomp; + const bool compreena = params->locallab.spots.at(sp).wavcompre; + const float compress = params->locallab.spots.at(sp).residcomp; + const float thres = params->locallab.spots.at(sp).threswav; + + wavcontrast4(lp, tmp1->L, tmp1->a, tmp1->b, contrast, radblur, radlevblur, tmp1->W, tmp1->H, level_bl, level_hl, level_br, level_hr, sk, numThreads, locwavCurve, locwavutili, wavcurve, loclevwavCurve, loclevwavutili, wavcurvelev, locconwavCurve, locconwavutili, wavcurvecon, loccompwavCurve, loccompwavutili, wavcurvecomp, loccomprewavCurve, loccomprewavutili, wavcurvecompre, locedgwavCurve, locedgwavutili, sigma, offs, maxlvl, sigmadc, deltad, chrol, chrobl, blurlc, blurena, levelena, comprena, compreena, compress, thres); + + const float satur = params->locallab.spots.at(sp).residchro; + + + if (satur != 0.f || radblur > 0.f) {//blur residual a and satur + + wavelet_decomposition *wdspota = new wavelet_decomposition(tmp1->a[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspota->memory_allocation_failed()) { + return; + } + + float *wav_ab0a = wdspota->get_coeff0(); + // int maxlvla = wdspota->maxlevel(); + int W_La = wdspota->level_W(0); + int H_La = wdspota->level_H(0); + + if (radblur > 0.f && !blurlc && blurena) { + array2D bufa(W_La, H_La); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_La; y++) { + for (int x = 0; x < W_La; x++) { + bufa[y][x] = wav_ab0a [y * W_La + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufa, bufa, W_La, H_La, radblur); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_La; y++) { + for (int x = 0; x < W_La; x++) { + wav_ab0a[y * W_La + x] = bufa[y][x]; + } + } + + } + + if (satur != 0.f) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_La * H_La; i++) { + wav_ab0a[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f)));//more progressive than linear + wav_ab0a[i] = clipC(wav_ab0a[i]); + } + } + + wdspota->reconstruct(tmp1->a[0], 1.f); + delete wdspota; + + wavelet_decomposition *wdspotb = new wavelet_decomposition(tmp1->b[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotb->memory_allocation_failed()) { + return; + } + + float *wav_ab0b = wdspotb->get_coeff0(); + int W_Lb = wdspotb->level_W(0); + int H_Lb = wdspotb->level_H(0); + + if (radblur > 0.f && !blurlc && blurena) { + array2D bufb(W_Lb, H_Lb); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_Lb; y++) { + for (int x = 0; x < W_Lb; x++) { + bufb[y][x] = wav_ab0b [y * W_Lb + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufb, bufb, W_Lb, H_Lb, radblur); + } + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_Lb; y++) { + for (int x = 0; x < W_Lb; x++) { + wav_ab0b[y * W_Lb + x] = bufb[y][x]; + } + } + + } + + if (satur != 0.f) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_Lb * H_Lb; i++) { + wav_ab0b[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f))); + wav_ab0b[i] = clipC(wav_ab0b[i]); + } + } + + wdspotb->reconstruct(tmp1->b[0], 1.f); + delete wdspotb; + } + + if (!origlc) {//merge all files + exec = false; + //copy previous calculation in merge possibilities +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmpresid->L[y][x] = tmp1->L[y][x]; + tmpresid->a[y][x] = tmp1->a[y][x]; + tmpresid->b[y][x] = tmp1->b[y][x]; + } + } + clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); + } + + float thr = 0.001f; + int flag = 0; + + if (maxlvl <= 4) { + mL0 = 0.f; + mC0 = 0.f; + mL = -1.5f * mL;//increase only for sharpen + mC = -mC; + thr = 1.f; + flag = 0; + + } else { + mL0 = mL; + mC0 = mC; + thr = 1.f; + flag = 1; + } + + if (exec || compreena || comprena || levelena || blurena || lp.wavgradl || locwavCurve || lp.edgwena) { + LabImage *mergfile = tmp1.get(); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int x = 0; x < bfh; x++) + for (int y = 0; y < bfw; y++) { + tmp1->L[x][y] = clipLoc((1.f + mL0) * mergfile->L[x][y] - mL * tmpresid->L[x][y]); + tmp1->a[x][y] = clipC((1.f + mC0) * mergfile->a[x][y] - mC * tmpresid->a[x][y]); + tmp1->b[x][y] = clipC((1.f + mC0) * mergfile->b[x][y] - mC * tmpresid->b[x][y]); + } + + if (softr != 0.f && (compreena || locwavCurve || comprena || blurena || levelena || lp.wavgradl || lp.edgwena || std::fabs(mL) > 0.001f)) { + softproc(tmpres.get(), tmp1.get(), softr, bfh, bfw, 0.001, 0.00001, thr, sk, multiThread, flag); + } + } + } + + + transit_shapedetect2(call, 10, bufgb.get(), tmp1.get(), originalmasklc.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + tmp1.reset(); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + if (!lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { //interior ellipse for sharpening, call = 1 and 2 only with Dcrop and simpleprocess + int bfh = call == 2 ? int (lp.ly + lp.lyT) + del : original->H; //bfw bfh real size of square zone + int bfw = call == 2 ? int (lp.lx + lp.lxL) + del : original->W; + JaggedArray loctemp(bfw, bfh); + + if (call == 2) { //call from simpleprocess + // printf("bfw=%i bfh=%i\n", bfw, bfh); + + if (bfw < mSPsharp || bfh < mSPsharp) { + printf("too small RT-spot - minimum size 39 * 39\n"); + return; + } + + JaggedArray bufsh(bfw, bfh, true); + JaggedArray hbuffer(bfw, bfh); + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) { + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + bufsh[loy - begy][lox - begx] = original->L[y][x]; + } + } + } + + //sharpen only square area instead of all image + ImProcFunctions::deconvsharpeningloc(bufsh, hbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, 1); + } else { //call from dcrop.cc + ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); + } + + //sharpen ellipse and transition + Sharp_Local(call, loctemp, 0, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + } else if (lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { + int GW = original->W; + int GH = original->H; + JaggedArray loctemp(GW, GH); + + ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, GW, GH, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); + + InverseSharp_Local(loctemp, hueref, lumaref, chromaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (lp.dehaze != 0 && lp.retiena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + if (bfh >= mSP && bfw >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + //calc dehaze + const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + + DehazeParams dehazeParams; + dehazeParams.enabled = true; + dehazeParams.strength = lp.dehaze; + dehazeParams.showDepthMap = false; + dehazeParams.depth = lp.depth; + dehazeParams.luminance = params->locallab.spots.at(sp).lumonly; + lab2rgb(*bufexpfin, *tmpImage.get(), params->icm.workingProfile); + dehazeloc(tmpImage.get(), dehazeParams); + rgb2lab(*tmpImage.get(), *bufexpfin, params->icm.workingProfile); + + transit_shapedetect2(call, 30, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + lp.invret = false;//always disabled inverse RETI too complex todo !! + + if (lp.str >= 0.2f && lp.retiena && call != 2) { + LabImage *bufreti = nullptr; + LabImage *bufmask = nullptr; + LabImage *buforig = nullptr; + LabImage *buforigmas = nullptr; + + if (GW >= mSP && GH >= mSP) + + { + + array2D buflight(GW, GH); + JaggedArray bufchro(GW, GH); + + int Hd, Wd; + Hd = GH; + Wd = GW; + + bufreti = new LabImage(GW, GH); + bufmask = new LabImage(GW, GH); + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig = new LabImage(GW, GH); + buforigmas = new LabImage(GW, GH); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) //fill with 0 + for (int jr = 0; jr < GW; jr++) { + bufreti->L[ir][jr] = 0.f; + bufreti->a[ir][jr] = 0.f; + bufreti->b[ir][jr] = 0.f; + buflight[ir][jr] = 0.f; + bufchro[ir][jr] = 0.f; + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + bufreti->L[y][x] = original->L[y][x]; + bufreti->a[y][x] = original->a[y][x]; + bufreti->b[y][x] = original->b[y][x]; + bufmask->L[y][x] = original->L[y][x]; + bufmask->a[y][x] = original->a[y][x]; + bufmask->b[y][x] = original->b[y][x]; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig->L[y][x] = original->L[y][x]; + buforig->a[y][x] = original->a[y][x]; + buforig->b[y][x] = original->b[y][x]; + } + + } + + float raddE = params->locallab.spots.at(sp).softradiusret; + + //calc dE and reduction to use in MSR to reduce artifacts + const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + + const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); + float** reducDE = *(reducDEBuffer.get()); + + float ade = 0.01f * raddE; + float bde = 100.f - raddE; + float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) + for (int x = 0; x < transformed->W; x++) { + float dE = std::sqrt(SQR(refa - bufreti->a[y][x] / 327.68f) + SQR(refb - bufreti->b[y][x] / 327.68f) + SQR(lumaref - bufreti->b[y][x] / 327.68f)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); + reducDE[y][x] = clipDE(reducdE); + } + + const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); + float** orig = *(origBuffer.get()); + + const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); + float** orig1 = *(origBuffer1.get()); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = bufreti->L[ir][jr]; + orig1[ir][jr] = bufreti->L[ir][jr]; + } + + LabImage *tmpl = new LabImage(Wd, Hd); + + // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; + bool fftw = lp.ftwreti; + //fftw = false; + //for Retinex Mask are incorporated in MSR + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + float lumask = params->locallab.spots.at(sp).lumask; + + const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, + Wd, Hd, Wd, Hd, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, + lmaskretilocalcurve, localmaskretiutili, + transformed, lp.enaretiMasktmap, lp.enaretiMask, + delt, hueref, chromaref, lumaref, + maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = orig[ir][jr]; + } + } + + if (lp.equret) { //equilibrate luminance before / after MSR + float *datain = new float[Hd * Wd]; + float *data = new float[Hd * Wd]; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + datain[ir * Wd + jr] = orig1[ir][jr]; + data[ir * Wd + jr] = orig[ir][jr]; + } + + normalize_mean_dt(data, datain, Hd * Wd, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = data[ir * Wd + jr]; + } + + delete [] datain; + delete [] data; + } + + + float minL = tmpl->L[0][0] - bufreti->L[0][0]; + float maxL = minL; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + } + } + + const float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] /= coef; + } + } + + transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + if (params->locallab.spots.at(sp).chrrt > 0) { + + if (call == 1) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + + orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + } + + } + + float maxChro = orig1[0][0]; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + maxChro = rtengine::max(maxChro, orig1[ir][jr]); + } + } + + float divchro = maxChro; + + //first step change saturation without Retinex ==> gain of time and memory + float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; + + if (params->locallab.spots.at(sp).chrrt <= 0.2f) { + satreal /= 10.f; + } + + DiagonalCurve reti_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + if (call == 1) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + const float Chprov = orig1[ir][jr]; + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; + + if (params->locallab.spots.at(sp).chrrt <= 100.f) { //first step + float buf = LIM01(orig[ir][jr] / divchro); + buf = reti_satur.getVal(buf); + buf *= divchro; + orig[ir][jr] = buf; + } + + tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; + tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; + } + + float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] *= coefC; + } + } + } + } + + transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + delete tmpl; + delete bufmask; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + if (buforig) { + delete buforig; + } + + if (buforigmas) { + delete buforigmas; + } + } + delete bufreti; + } + } + + + + if (lp.str >= 0.2f && lp.retiena && call == 2) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + LabImage *bufreti = nullptr; + LabImage *bufmask = nullptr; + LabImage *buforig = nullptr; + LabImage *buforigmas = nullptr; + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSP && bfh > mSP) { + if (lp.ftwreti) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + + int Hd, Wd; + Hd = GH; + Wd = GW; + + if (!lp.invret && call == 2) { + + Hd = bfh; + Wd = bfw; + bufreti = new LabImage(bfw, bfh); + bufmask = new LabImage(bfw, bfh); + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig = new LabImage(bfw, bfh); + buforigmas = new LabImage(bfw, bfh); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) //fill with 0 + for (int jr = 0; jr < bfw; jr++) { + bufreti->L[ir][jr] = 0.f; + bufreti->a[ir][jr] = 0.f; + bufreti->b[ir][jr] = 0.f; + buflight[ir][jr] = 0.f; + bufchro[ir][jr] = 0.f; + } + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufreti->L[y - ystart][x - xstart] = original->L[y][x]; + bufreti->a[y - ystart][x - xstart] = original->a[y][x]; + bufreti->b[y - ystart][x - xstart] = original->b[y][x]; + bufmask->L[y - ystart][x - xstart] = original->L[y][x]; + bufmask->a[y - ystart][x - xstart] = original->a[y][x]; + bufmask->b[y - ystart][x - xstart] = original->b[y][x]; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig->L[y - ystart][x - xstart] = original->L[y][x]; + buforig->a[y - ystart][x - xstart] = original->a[y][x]; + buforig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + } + } + + float raddE = params->locallab.spots.at(sp).softradiusret; + + //calc dE and reduction to use in MSR to reduce artifacts + const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + + const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); + float** reducDE = *(reducDEBuffer.get()); + float ade = 0.01f * raddE; + float bde = 100.f - raddE; + float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const float dE = std::sqrt(SQR(refa - bufreti->a[y - ystart][x - xstart] / 327.68f) + SQR(refb - bufreti->b[y - ystart][x - xstart] / 327.68f) + SQR(lumaref - bufreti->b[y - ystart][x - xstart] / 327.68f)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); + reducDE[y - ystart][x - xstart] = clipDE(reducdE); + } + } + + const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); + float** orig = *(origBuffer.get()); + + const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); + float** orig1 = *(origBuffer1.get()); + + LabImage *tmpl = nullptr; + + if (!lp.invret && call == 2) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = bufreti->L[ir][jr]; + orig1[ir][jr] = bufreti->L[ir][jr]; + } + } + + tmpl = new LabImage(Wd, Hd); + } + + // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; + bool fftw = lp.ftwreti; + //for Retinex Mask are incorporated in MSR + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + float lumask = params->locallab.spots.at(sp).lumask; + + const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, + Wd, Hd, bfwr, bfhr, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, + lmaskretilocalcurve, localmaskretiutili, + transformed, lp.enaretiMasktmap, lp.enaretiMask, + delt, hueref, chromaref, lumaref, + maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = orig[ir][jr]; + } + + + if (lp.equret) { //equilibrate luminance before / after MSR + const std::unique_ptr datain(new float[Hd * Wd]); + const std::unique_ptr data(new float[Hd * Wd]); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + datain[ir * Wd + jr] = orig1[ir][jr]; + data[ir * Wd + jr] = orig[ir][jr]; + } + } + + normalize_mean_dt(data.get(), datain.get(), Hd * Wd, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = data[ir * Wd + jr]; + } + } + } + + if (!lp.invret) { + float minL = tmpl->L[0][0] - bufreti->L[0][0]; + float maxL = minL; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + } + } + + float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + + if (coef > 0.f) { + coef = 1.f / coef; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] *= coef; + } + } + } + + transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (params->locallab.spots.at(sp).chrrt > 0) { + if (!lp.invret && call == 2) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + } + } + } + + float maxChro = orig1[0][0]; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + maxChro = rtengine::max(maxChro, orig1[ir][jr]); + } + } + + //first step change saturation without Retinex ==> gain of time and memory + float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; + + if (params->locallab.spots.at(sp).chrrt <= 0.2f) { + satreal /= 10.f; + } + + DiagonalCurve reti_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + if (!lp.invret && call == 2) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + const float Chprov = orig1[ir][jr]; + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; + + if (params->locallab.spots.at(sp).chrrt <= 40.f) { //first step + orig[ir][jr] = reti_satur.getVal(LIM01(orig[ir][jr] / maxChro)) * maxChro; + } + + tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; + tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; + } + } + + float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] *= coefC; + } + } + } + } + + if (!lp.invret) { + transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + delete tmpl; + delete bufmask; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + if (buforig) { + delete buforig; + } + + if (buforigmas) { + delete buforigmas; + } + } + delete bufreti; + } + } + + bool enablefat = false; + + if (params->locallab.spots.at(sp).fatamount > 1.f) { + enablefat = true;; + } + + bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.shadex > 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); + + if (!lp.invex && execex) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + //variable for fast FFTW + int bfhr = bfh; + int bfwr = bfw; + + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.expmet == 1) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + + std::unique_ptr bufmaskblurexp; + std::unique_ptr originalmaskexp; + + array2D blend2; + + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + if (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 5) { + bufmaskblurexp.reset(new LabImage(bfw, bfh)); + originalmaskexp.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + } + } + + const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); + + if (bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struexp > 0.f) { + blend2(bfw, bfh); + ImProcFunctions::blendstruc(bfw, bfh, bufexporig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struexp, blend2, sk, multiThread); + + if (lp.showmaskexpmet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + const int loy = cy + y; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); + transformed->a[y][x] = 0.f; + transformed->b[y][x] = 0.f; + } + } + } + + return; + } + } + + int inv = 0; + bool showmaske = false; + const bool enaMask = lp.enaExpMask; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskexpmet == 3) { + showmaske = true; + } else if (lp.showmaskexpmet == 5) { + deltaE = true; + } else if (lp.showmaskexpmet == 2) { + modmask = true; + } else if (lp.showmaskexpmet == 1) { + modif = true; + } else if (lp.showmaskexpmet == 0) { + zero = true; + } + + float chrom = lp.chromaexp; + float rad = lp.radmaexp; + float gamma = lp.gammaexp; + float slope = lp.slomaexp; + float blendm = lp.blendmaexp; + float lap = params->locallab.spots.at(sp).lapmaskexp; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shado = 0; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + float amountcd = 0.f; + float anchorcd = 50.f; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, + 0.f, false, + locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 + ); + + if (lp.showmaskexpmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskblurexp.get(), 0); + + return; + } + + if (lp.showmaskexpmet == 4) { + return; + } + + if (lp.showmaskexpmet == 0 || lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2 || lp.showmaskexpmet == 5 || lp.enaExpMask) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + + + if (exlocalcurve && localexutili) {// L=f(L) curve enhanced +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] = 0.5f * exlocalcurve[2.f * bufexporig->L[ir][jr]]; + } + + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + + ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + + + } else { + + ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexporig.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + } + +//gradient + struct grad_params gp; + + if (lp.strexp != 0.f) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 1); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); + } + } + } + +//exposure_pde + if (lp.expmet == 1) { + if (enablefat) { + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + datain[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + FattalToneMappingParams fatParams; + fatParams.enabled = true; + fatParams.threshold = params->locallab.spots.at(sp).fatdetail; + fatParams.amount = params->locallab.spots.at(sp).fatamount; + fatParams.anchor = 50.f; //params->locallab.spots.at(sp).fatanchor; + const float sigm = params->locallab.spots.at(sp).fatlevel; + const float mean = params->locallab.spots.at(sp).fatanchor; + const std::unique_ptr tmpImagefat(new Imagefloat(bfwr, bfhr)); + lab2rgb(*bufexpfin, *(tmpImagefat.get()), params->icm.workingProfile); + ToneMapFattal02(tmpImagefat.get(), fatParams, 3, 0, nullptr, 0, 0, 1);//last parameter = 1 ==>ART algorithm + rgb2lab(*(tmpImagefat.get()), *bufexpfin, params->icm.workingProfile); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + dataout[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + normalize_mean_dt(dataout.get(), datain.get(), bfwr * bfhr, mean, sigm); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexpfin->L[y][x] = dataout[y * bfwr + x]; + } + } + } + + if (lp.laplacexp > 0.1f) { + MyMutex::MyLock lock(*fftwMutex); + std::unique_ptr datain(new float[bfwr * bfhr]); + std::unique_ptr dataout(new float[bfwr * bfhr]); + const float gam = params->locallab.spots.at(sp).gamm; + const float igam = 1.f / gam; + + if (params->locallab.spots.at(sp).exnoiseMethod == "med" || params->locallab.spots.at(sp).exnoiseMethod == "medhi") { + if (lp.blac < -100.f && lp.linear > 0.01f) { + float evnoise = lp.blac - lp.linear * 2000.f; + if (params->locallab.spots.at(sp).exnoiseMethod == "med") { + evnoise *= 0.4f; + } + + //soft denoise, user must use Local Denoise to best result + Median med; + if (evnoise < -18000.f) { + med = Median::TYPE_5X5_STRONG; + } else if (evnoise < -15000.f) { + med = Median::TYPE_5X5_SOFT; + } else if (evnoise < -10000.f) { + med = Median::TYPE_3X3_STRONG; + } else { + med = Median:: TYPE_3X3_SOFT; + } + + Median_Denoise(bufexpfin->L, bufexpfin->L, bfwr, bfhr, med, 1, multiThread); + Median_Denoise(bufexpfin->a, bufexpfin->a, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); + Median_Denoise(bufexpfin->b, bufexpfin->b, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + float L = LIM01(bufexpfin->L[y][x] / 32768.f);//change gamma for Laplacian + datain[y * bfwr + x] = pow_F(L, gam) * 32768.f; + } + } + + //call PDE equation - with Laplacian threshold + ImProcFunctions::exposure_pde(datain.get(), datain.get(), dataout.get(), bfwr, bfhr, 12.f * lp.laplacexp, lp.balanexp); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + const float Y = dataout[y * bfwr + x] / 32768.f;//inverse Laplacian gamma + bufexpfin->L[y][x] = pow_F(Y, igam) * 32768.f; + } + } + } + } + if (lp.shadex > 0) { + if (lp.expcomp == 0.f) { + lp.expcomp = 0.001f; // to enabled + } + } + + //shadows with ipshadowshighlight + if ((lp.expcomp != 0.f) || (exlocalcurve && localexutili)) { + if (lp.shadex > 0) { + ImProcFunctions::shadowsHighlights(bufexpfin.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); + } + } + + if (lp.expchroma != 0.f) { + if ((lp.expcomp != 0.f && lp.expcomp != 0.001f) || (exlocalcurve && localexutili) || lp.laplacexp > 0.1f) { + constexpr float ampli = 70.f; + const float ch = (1.f + 0.02f * lp.expchroma); + const float chprosl = ch <= 1.f ? 99.f * ch - 99.f : clipChro(ampli * ch - ampli); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float epsi = bufexporig->L[ir][jr] == 0.f ? 0.001f : 0.f; + const float rapexp = bufexpfin->L[ir][jr] / (bufexporig->L[ir][jr] + epsi); + bufexpfin->a[ir][jr] *= 1.f + chprosl * rapexp; + bufexpfin->b[ir][jr] *= 1.f + chprosl * rapexp; + } + } + } + } + + if (lp.softradiusexp > 0.f && lp.expmet == 0) { + softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 1, bufexporig.get(), bufexpfin.get(), originalmaskexp.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } +//inverse + + else if (lp.invex && (lp.expcomp != 0.0 || lp.laplacexp > 0.1f || params->locallab.spots.at(sp).fatamount > 1.f || (exlocalcurve && localexutili) || lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) && lp.exposena) { + constexpr float adjustr = 2.f; + std::unique_ptr bufmaskblurexp; + std::unique_ptr originalmaskexp; + const std::unique_ptr bufexporig(new LabImage(GW, GH)); + + if (lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) { + bufmaskblurexp.reset(new LabImage(GW, GH, true)); + originalmaskexp.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufexporig->L[y][x] = original->L[y][x]; + } + } + + constexpr int inv = 1; + const bool showmaske = lp.showmaskexpmetinv == 1; + const bool enaMask = lp.enaExpMaskinv; + constexpr bool deltaE = false; + constexpr bool modmask = false; + const bool zero = lp.showmaskexpmetinv == 0; + constexpr bool modif = false; + const float chrom = lp.chromaexp; + const float rad = lp.radmaexp; + const float gamma = lp.gammaexp; + const float slope = lp.slomaexp; + const float blendm = lp.blendmaexp; + const float lap = params->locallab.spots.at(sp).lapmaskexp; + const bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + const bool lmasutilicolwav = false; + // bool delt = params->locallab.spots.at(sp).deltae; + const bool delt = false; + const int sco = params->locallab.spots.at(sp).scopemask; + constexpr int shado = 0; + constexpr int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + constexpr float amountcd = 0.f; + constexpr float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + constexpr bool lhhmasutili = false; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, + 0.f, false, + locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 + ); + + if (lp.showmaskexpmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufexporig.get(), transformed, bufmaskblurexp.get(), inv); + return; + } + + InverseColorLight_Local(false, false, sp, 1, lp, originalmaskexp.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +//local color and light + const float factor = LocallabParams::LABGRIDL_CORR_MAX * 3.276f; + const float scaling = LocallabParams::LABGRIDL_CORR_SCALE; + const float scaledirect = LocallabParams::LABGRIDL_DIRECT_SCALE; + const float a_scale = (lp.highA - lp.lowA) / factor / scaling; + const float a_base = lp.lowA / scaling; + const float b_scale = (lp.highB - lp.lowB) / factor / scaling; + const float b_base = lp.lowB / scaling; + const bool ctoning = (a_scale != 0.f || b_scale != 0.f || a_base != 0.f || b_base != 0.f); + const float a_scalemerg = (lp.highAmerg - lp.lowAmerg) / factor / scaling; + const float a_basemerg = lp.lowAmerg / scaling; + const float b_scalemerg = (lp.highBmerg - lp.lowBmerg) / factor / scaling; + const float b_basemerg = lp.lowBmerg / scaling; + const bool ctoningmerg = (a_scalemerg != 0.f || b_scalemerg != 0.f || a_basemerg != 0.f || b_basemerg != 0.f); + + if (!lp.inv && (lp.chro != 0 || lp.ligh != 0.f || lp.cont != 0 || ctoning || lp.mergemet > 0 || lp.strcol != 0.f || lp.strcolab != 0.f || lp.qualcurvemet != 0 || lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 4 || lp.showmaskcolmet == 5 || lp.prevdE) && lp.colorena) { // || lllocalcurve)) { //interior ellipse renforced lightness and chroma //locallutili + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + const bool spez = params->locallab.spots.at(sp).special; + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + std::unique_ptr bufcolorig; + std::unique_ptr bufcolfin; + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + std::unique_ptr bufcolreserv; + std::unique_ptr buftemp; + array2D blend2; + + float adjustr = 1.0f; + + //adapt chroma to working profile + if (params->icm.workingProfile == "ProPhoto") { + adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. + } else if (params->icm.workingProfile == "Adobe RGB") { + adjustr = 1.8f; + } else if (params->icm.workingProfile == "sRGB") { + adjustr = 2.0f; + } else if (params->icm.workingProfile == "WideGamut") { + adjustr = 1.2f; + } else if (params->icm.workingProfile == "Beta RGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BestRGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BruceRGB") { + adjustr = 1.8f; + } + + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + bufcolorig.reset(new LabImage(bfw, bfh)); + bufcolfin.reset(new LabImage(bfw, bfh)); + buftemp.reset(new LabImage(bfw, bfh)); + + if (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 5) { + bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); + originalmaskcol.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; + bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; + buftemp->L[y][x] = original->L[y + ystart][x + xstart]; + buftemp->a[y][x] = original->a[y + ystart][x + xstart]; + buftemp->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); + const bool blends = bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struco > 0.f; + + if (blends) { + blend2(bfw, bfh); + ImProcFunctions::blendstruc(bfw, bfh, bufcolorig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struco, blend2, sk, multiThread); + + if (lp.showmaskcolmet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + const int loy = cy + y; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); + transformed->a[y][x] = 0.f; + transformed->b[y][x] = 0.f; + } + } + } + return; + } + } + + const int inv = 0; + const bool showmaske = lp.showmaskcolmet == 3; + const bool enaMask = lp.enaColorMask; + const bool deltaE = lp.showmaskcolmet == 5; + const bool modmask = lp.showmaskcolmet == 2; + const bool zero = lp.showmaskcolmet == 0; + const bool modif = lp.showmaskcolmet == 1; + const float chrom = lp.chromacol; + const float rad = lp.radmacol; + const float gamma = lp.gammacol; + const float slope = lp.slomacol; + const float blendm = lp.blendmacol; + const float lap = params->locallab.spots.at(sp).lapmaskcol; + const bool pde = params->locallab.spots.at(sp).laplac; + const int shado = params->locallab.spots.at(sp).shadmaskcol; + const int sco = params->locallab.spots.at(sp).scopemask; + const int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); + const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; + float conthr = 0.01f * params->locallab.spots.at(sp).conthrcol; + const float mercol = params->locallab.spots.at(sp).mercol; + const float merlucol = params->locallab.spots.at(sp).merlucol; + + int tonemod = 0; + if (params->locallab.spots.at(sp).toneMethod == "one") { + tonemod = 0; + } else if (params->locallab.spots.at(sp).toneMethod == "two") { + tonemod = 1; + } else if (params->locallab.spots.at(sp).toneMethod == "thr") { + tonemod = 2; + } else if (params->locallab.spots.at(sp).toneMethod == "fou") { + tonemod = 3; + } + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float amountcd = 0.f; + const float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, astool, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + level_bl, level_hl, level_br, level_hr, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 + ); + + if (lp.showmaskcolmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); + return; + } else if (lp.showmaskcolmet == 4) { + return; + } + + if (lp.showmaskcolmet == 0 || lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2 || lp.showmaskcolmet == 5 || lp.enaColorMask) { + //RGB Curves + bool usergb = false; + + if (rgblocalcurve && localrgbutili && lp.qualcurvemet != 0) { + usergb = true; + const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + + lab2rgb(*buftemp, *(tmpImage.get()), params->icm.workingProfile); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) + for (int x = 0; x < bfw; x++) { + + //std + if (tonemod == 0) { + curves::setLutVal(rgblocalcurve, tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x)); + } else { + float r = CLIP(tmpImage->r(y, x)); + float g = CLIP(tmpImage->g(y, x)); + float b = CLIP(tmpImage->b(y, x)); + + if (tonemod == 1) { // weightstd + const float r1 = rgblocalcurve[r]; + const float g1 = triangle(r, r1, g); + const float b1 = triangle(r, r1, b); + + const float g2 = rgblocalcurve[g]; + const float r2 = triangle(g, g2, r); + const float b2 = triangle(g, g2, b); + + const float b3 = rgblocalcurve[b]; + const float r3 = triangle(b, b3, r); + const float g3 = triangle(b, b3, g); + r = CLIP(r1 * 0.50f + r2 * 0.25f + r3 * 0.25f); + g = CLIP(g1 * 0.25f + g2 * 0.50f + g3 * 0.25f); + b = CLIP(b1 * 0.25f + b2 * 0.25f + b3 * 0.50f); + } else if (tonemod == 2) { // Luminance + float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; + + const float newLuminance = rgblocalcurve[currLuminance]; + currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; + const float coef = newLuminance / currLuminance; + r = LIM(r * coef, 0.f, 65535.f); + g = LIM(g * coef, 0.f, 65535.f); + b = LIM(b * coef, 0.f, 65535.f); + } else if (tonemod == 3) { // Film like Adobe + if (r >= g) { + if (g > b) { + rgbtone(r, g, b, rgblocalcurve); // Case 1: r >= g > b + } else if (b > r) { + rgbtone(b, r, g, rgblocalcurve); // Case 2: b > r >= g + } else if (b > g) { + rgbtone(r, b, g, rgblocalcurve); // Case 3: r >= b > g + } else { // Case 4: r == g == b + r = rgblocalcurve[r]; + g = rgblocalcurve[g]; + b = g; + } + } else { + if (r >= b) { + rgbtone(g, r, b, rgblocalcurve); // Case 5: g > r >= b + } else if (b > g) { + rgbtone(b, g, r, rgblocalcurve); // Case 6: b > g > r + } else { + rgbtone(g, b, r, rgblocalcurve); // Case 7: g >= b > r + } + } + } + + setUnlessOOG(tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x), r, g, b); + } + } + + rgb2lab(*(tmpImage.get()), *buftemp, params->icm.workingProfile); + + // end rgb curves + } + + if (usergb && spez) {//special use of rgb curves ex : negative + const float achm = lp.trans / 100.f; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y + ystart][x + xstart] = buftemp->L[y][x] * localFactor + (1.f - localFactor) * original->L[y + ystart][x + xstart]; + transformed->a[y + ystart][x + xstart] = buftemp->a[y][x] * localFactor + (1.f - localFactor) * original->a[y + ystart][x + xstart]; + transformed->b[y + ystart][x + xstart] = buftemp->b[y][x] * localFactor + (1.f - localFactor) * original->b[y + ystart][x + xstart]; + } + } + } + } + + //others curves + + const LabImage *origptr = usergb ? buftemp.get() : bufcolorig.get(); + + bool execcolor = false; + + if (localcutili || HHutili || locallutili || lp.ligh != 0.f || lp.cont != 0 || lp.chro != 0 || LHutili || ctoning) { + execcolor = true; + } + + bool HHcurve = false; + if (lochhCurve && HHutili) { + for (int i = 0; i < 500; i++) { + if (lochhCurve[i] != 0.5) { + HHcurve = true; + break; + } + } + } + + const float kd = 10.f * 0.01f * lp.strengrid;//correction to ctoning + + //chroma slider with curve instead of linear + const float satreal = lp.chro; + + DiagonalCurve color_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + DiagonalCurve color_saturmoins({ + DCT_NURBS, + 0, 0, + 0.1 - satreal / 150., 0.1, + rtengine::min(1.0, 0.7 - satreal / 300.), 0.7, + 1, 1 + }); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float bufcolcalca = origptr->a[ir][jr]; + float bufcolcalcb = origptr->b[ir][jr]; + float bufcolcalcL = origptr->L[ir][jr]; + + if (lp.chro != 0.f) {//slider chroma with curve DCT_NURBS + float Chprov = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufcolcalca / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufcolcalcb / Chprov; + + // 35000 must be globally good, more than 32768...and less than !! to avoid calculation min max + if (lp.chro > 0.f) { + Chprov = color_satur.getVal(LIM01(Chprov / 35000.f)) * 35000.f; + } else { + Chprov = color_saturmoins.getVal(LIM01(Chprov / 35000.f)) * 35000.f; + } + + if (lp.chro == -100.f) { + Chprov = 0.f; + } + + bufcolcalca = Chprov * sincosval.y; + bufcolcalcb = Chprov * sincosval.x; + } + + if (cclocalcurve && lp.qualcurvemet != 0 && localcutili) { // C=f(C) curve + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + const float ch = cclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more + bufcolcalca *= ch; + bufcolcalcb *= ch; + } + + if (cllocalcurve && lp.qualcurvemet != 0 && localclutili) { // C=f(L) curve + float chromaCfactor = (cllocalcurve[bufcolcalcL * 2.f]) / (bufcolcalcL * 2.f); + bufcolcalca *= chromaCfactor; + bufcolcalcb *= chromaCfactor; + } + + if (lclocalcurve && lp.qualcurvemet != 0 && locallcutili) { // L=f(C) curve + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + float Lc = lclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); + + if (Lc > 1.f) { + Lc = (Lc - 1.0f) * 0.1f + 1.0f; //reduct action + } else { + Lc = (Lc - 1.0f) * 0.3f + 1.0f; + } + + bufcolcalcL *= Lc; + } + + if (lochhCurve && HHcurve && lp.qualcurvemet != 0 && !ctoning) { // H=f(H) + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + const float hhforcurv = xatan2f(bufcolcalcb, bufcolcalca); + const float valparam = float ((lochhCurve[500.f * Color::huelab_to_huehsv2(hhforcurv)] - 0.5f)); //get H=f(H) + float2 sincosval = xsincosf(valparam); + bufcolcalca = chromat * sincosval.y; + bufcolcalcb = chromat * sincosval.x; + } + + if (lp.ligh != 0.f || lp.cont != 0) {//slider luminance or slider contrast with curve + bufcolcalcL = calclight(bufcolcalcL, lightCurveloc); + } + + if (lllocalcurve && locallutili && lp.qualcurvemet != 0) {// L=f(L) curve + bufcolcalcL = 0.5f * lllocalcurve[bufcolcalcL * 2.f]; + } + + if (loclhCurve && LHutili && lp.qualcurvemet != 0) {//L=f(H) curve + const float rhue = xatan2f(bufcolcalcb, bufcolcalca); + float l_r = bufcolcalcL / 32768.f; //Luminance Lab in 0..1 + const float valparam = loclhCurve[500.f * Color::huelab_to_huehsv2(rhue)] - 0.5f; //get l_r=f(H) + + if (valparam > 0.f) { + l_r = (1.f - valparam) * l_r + valparam * (1.f - SQR(((SQR(1.f - rtengine::min(l_r, 1.0f)))))); + } else { + constexpr float khu = 1.9f; //in reserve in case of! + //for negative + l_r *= (1.f + khu * valparam); + } + + bufcolcalcL = l_r * 32768.f; + + } + + if (ctoning) {//color toning and direct change color + if (lp.gridmet == 0) { + bufcolcalca += kd * bufcolcalcL * a_scale + a_base; + bufcolcalcb += kd * bufcolcalcL * b_scale + b_base; + } else if (lp.gridmet == 1) { + bufcolcalca += kd * scaledirect * a_scale; + bufcolcalcb += kd * scaledirect * b_scale; + } + + bufcolcalca = clipC(bufcolcalca); + bufcolcalcb = clipC(bufcolcalcb); + + } + + bufcolfin->L[ir][jr] = bufcolcalcL; + bufcolfin->a[ir][jr] = bufcolcalca; + bufcolfin->b[ir][jr] = bufcolcalcb; + } + } + + if (HHcurve && ctoning) {//not use ctoning and H(H) simultaneous but priority to ctoning + HHcurve = false; + } + + if (!execcolor) {//if we don't use color and light sliders, curves except RGB +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = origptr->L[ir][jr]; + bufcolfin->a[ir][jr] = origptr->a[ir][jr]; + bufcolfin->b[ir][jr] = origptr->b[ir][jr]; + } + } + + bool nottransit = false; + if (lp.mergemet >= 2) { //merge result with original + nottransit = true; + bufcolreserv.reset(new LabImage(bfw, bfh)); + JaggedArray lumreserv(bfw, bfh); + const std::unique_ptr bufreser(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + lumreserv[y][x] = 32768.f - reserved->L[y + ystart][x + xstart]; + bufreser->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufreser->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufreser->b[y][x] = reserved->b[y + ystart][x + xstart]; + + if (lp.mergemet == 2) { + bufcolreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufcolreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufcolreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; + } else if (lp.mergemet == 3) { + bufcolreserv->L[y][x] = lastorig->L[y + ystart][x + xstart]; + bufcolreserv->a[y][x] = lastorig->a[y + ystart][x + xstart]; + bufcolreserv->b[y][x] = lastorig->b[y + ystart][x + xstart]; + } else if (lp.mergemet == 4 && ctoningmerg) { + bufcolreserv->L[y][x] = merlucol * 327.68f; + bufcolreserv->a[y][x] = 9.f * scaledirect * a_scalemerg; + bufcolreserv->b[y][x] = 9.f * scaledirect * b_scalemerg; + } + } + } + + if (lp.strcol != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufcolfin->L[ir][jr] *= corrFactor; + } + } + } + + if (lp.strcolab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 4); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufcolfin->a[ir][jr] *= corrFactor; + bufcolfin->b[ir][jr] *= corrFactor; + } + } + } + + if (lp.strcolh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + const float aa = bufcolfin->a[ir][jr]; + const float bb = bufcolfin->b[ir][jr]; + const float chrm = std::sqrt(SQR(aa) + SQR(bb)); + const float HH = xatan2f(bb, aa); + + float cor = 0.f; + if (corrFactor < 1.f) { + cor = - 2.5f * (1.f - corrFactor); + } else if (corrFactor > 1.f) { + cor = 0.03f * (corrFactor - 1.f); + } + + float newhr = HH + cor; + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + const float2 sincosval = xsincosf(newhr); + bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + } + + JaggedArray blend(bfw, bfh); + buildBlendMask(lumreserv, blend, bfw, bfh, conthr); + const float rm = 20.f / sk; + + if (rm > 0) { + float **mb = blend; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); + float** rdE = *(rdEBuffer.get()); + + deltaEforMask(rdE, bfw, bfh, bufreser.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, mercol, lp.balance, lp.balanceh); + + if (lp.mergecolMethod == 0) { //normal + + if (lp.mergemet == 4) { + bufprov.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rdE[y][x] *= SQR(rdE[y][x]); + bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); + + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); + } + } + } else { + bufprov.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); + } + } + } + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufreser->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufreser->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufreser->b[y][x]); + } + } + } + } + + if (lp.mergecolMethod > 16) { //hue sat chroma luma + bufprov.reset(new LabImage(bfw, bfh)); + + if (lp.mergemet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rdE[y][x] *= SQR(rdE[y][x]); + bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + if (lp.mergecolMethod == 17) { + const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); + const float2 sincosval1 = xsincosf(huefin); + const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); + buftemp->a[y][x] = chrores * sincosval1.y; + buftemp->b[y][x] = chrores * sincosval1.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 18) { + const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); + const float2 sincosval2 = xsincosf(hueres); + const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); + buftemp->a[y][x] = chrofin * sincosval2.y; + buftemp->b[y][x] = chrofin * sincosval2.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 19) { + const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); + const float2 sincosval3 = xsincosf(huefin); + const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); + buftemp->a[y][x] = chrofin * sincosval3.y; + buftemp->b[y][x] = chrofin * sincosval3.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 20) { + const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); + const float2 sincosval4 = xsincosf(hueres); + const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); + buftemp->a[y][x] = chrores * sincosval4.y; + buftemp->b[y][x] = chrores * sincosval4.x; + buftemp->L[y][x] = bufprov->L[y][x]; + } + + if (lp.mergemet == 4) { + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); + } else { + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); + } + } + } + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + } + + + if (lp.mergecolMethod > 0 && lp.mergecolMethod <= 16) { + //first deltaE +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + + //prepare RGB values in 0 1(or more)for current image and reserved + std::unique_ptr tmpImageorig(new Imagefloat(bfw, bfh)); + lab2rgb(*bufcolfin, *(tmpImageorig.get()), params->icm.workingProfile); + tmpImageorig->normalizeFloatTo1(); + + std::unique_ptr tmpImagereserv(new Imagefloat(bfw, bfh)); + lab2rgb(*bufcolreserv, *(tmpImagereserv.get()), params->icm.workingProfile); + tmpImagereserv->normalizeFloatTo1(); + + float minR = tmpImagereserv->r(0, 0); + float maxR = minR; + float minG = tmpImagereserv->g(0, 0); + float maxG = minG; + float minB = tmpImagereserv->b(0, 0); + float maxB = minB; + if (lp.mergecolMethod == 6 || lp.mergecolMethod == 9 || lp.mergecolMethod == 10 || lp.mergecolMethod == 11) { +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxR,maxG,maxB) reduction(min:minR,minG,minB) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + minR = rtengine::min(minR, tmpImagereserv->r(ir, jr)); + maxR = rtengine::max(maxR, tmpImagereserv->r(ir, jr)); + minG = rtengine::min(minG, tmpImagereserv->g(ir, jr)); + maxG = rtengine::max(maxG, tmpImagereserv->g(ir, jr)); + minB = rtengine::min(minB, tmpImagereserv->b(ir, jr)); + maxB = rtengine::max(maxB, tmpImagereserv->b(ir, jr)); + } + } + } + + //various combinations subtract, multiply, difference, etc + if (lp.mergecolMethod == 1) { //subtract +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) {//LIM(x 0 2) 2 arbitrary value but limit... + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) - tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) - tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) - tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 2) { //difference +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->r(y, x) - tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->g(y, x) - tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->b(y, x) - tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 3) { //multiply +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) * tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) * tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) * tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 4) { //addition +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) + tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) + tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) + tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 5) { //divide +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) / (tmpImagereserv->r(y, x) + 0.00001f), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) / (tmpImagereserv->g(y, x) + 0.00001f), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) / (tmpImagereserv->b(y, x) + 0.00001f), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 6) { //soft light as Photoshop +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 7) { //soft light as illusions.hu +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImageorig->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImageorig->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImageorig->b(y, x))), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 8) { //soft light as W3C +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->r(y, x)), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->g(y, x)), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->b(y, x)), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 9) { //hard light overlay (float &b, float &a) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImagereserv->r(y, x), tmpImageorig->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImagereserv->g(y, x), tmpImageorig->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImagereserv->b(y, x), tmpImageorig->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 10) { //overlay overlay(float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 11) { //screen screen (float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, screen(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, screen(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, screen(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 12) { //darken only +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 13) { //lighten only +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 14) { //exclusion exclusion (float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, exclusion(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, exclusion(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, exclusion(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + + } else if (lp.mergecolMethod == 15) { //Color burn +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 16) { //Color dodge +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); + } + } + } + + tmpImageorig->normalizeFloatTo65535(); + rgb2lab(*tmpImageorig, *bufcolfin, params->icm.workingProfile); + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + } + + if (lp.softradiuscol > 0.f) { + softproc(bufcolreserv.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 0, bufcolreserv.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + if (!nottransit) { +//gradient + if (lp.strcol != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufcolfin->L[ir][jr] *= corrFactor; + } + } + + if (lp.strcolab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 5); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufcolfin->a[ir][jr] *= corrFactor; + bufcolfin->b[ir][jr] *= corrFactor; + } + } + + if (lp.strcolh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + const float aa = bufcolfin->a[ir][jr]; + const float bb = bufcolfin->b[ir][jr]; + const float chrm = std::sqrt(SQR(aa) + SQR(bb)); + const float HH = xatan2f(bb, aa); + + float cor = 0.f; + + if (corrFactor < 1.f) { + cor = - 2.5f * (1.f - corrFactor); + } else if (corrFactor > 1.f) { + cor = 0.03f * (corrFactor - 1.f); + } + + float newhr = HH + cor; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + const float2 sincosval = xsincosf(newhr); + bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + + + if (lp.softradiuscol > 0.f) { + softproc(bufcolorig.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 0, bufcolorig.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + } + } + } + +//inverse + else if (lp.inv && (lp.chro != 0 || lp.ligh != 0 || exlocalcurve || lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) && lp.colorena) { + float adjustr = 1.0f; + +//adapt chroma to working profile + if (params->icm.workingProfile == "ProPhoto") { + adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. + } else if (params->icm.workingProfile == "Adobe RGB") { + adjustr = 1.8f; + } else if (params->icm.workingProfile == "sRGB") { + adjustr = 2.0f; + } else if (params->icm.workingProfile == "WideGamut") { + adjustr = 1.2f; + } else if (params->icm.workingProfile == "Beta RGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BestRGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BruceRGB") { + adjustr = 1.8f; + } + + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + const std::unique_ptr bufcolorig(new LabImage(GW, GH)); + + if (lp.enaColorMaskinv || lp.showmaskcolmetinv == 1) { + bufmaskblurcol.reset(new LabImage(GW, GH, true)); + originalmaskcol.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufcolorig->L[y][x] = original->L[y][x]; + } + } + + constexpr int inv = 1; + const bool showmaske = lp.showmaskcolmetinv == 1; + const bool enaMask = lp.enaColorMaskinv; + constexpr bool deltaE = false; + constexpr bool modmask = false; + const bool zero = lp.showmaskcolmetinv == 0; + constexpr bool modif = false; + + const float chrom = lp.chromacol; + const float rad = lp.radmacol; + const float gamma = lp.gammacol; + const float slope = lp.slomacol; + const float blendm = lp.blendmacol; + const float lap = params->locallab.spots.at(sp).lapmaskcol; + const bool pde = params->locallab.spots.at(sp).laplac; + int shado = params->locallab.spots.at(sp).shadmaskcol; + int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); + int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); + int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); + int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + int lumask = params->locallab.spots.at(sp).lumask; + float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + constexpr float amountcd = 0.f; + constexpr float anchorcd = 50.f; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, params->locallab.spots.at(sp).toolcol, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + level_bl, level_hl, level_br, level_hr, + shortcu, false, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 + ); + + if (lp.showmaskcolmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); + return; + } + + if (lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) { + InverseColorLight_Local(false, false, sp, 0, lp, originalmaskcol.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + +//begin common mask + if(lp.maskena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + if (bfw >= mSP && bfh >= mSP) { + + if (lp.blurma >= 0.25f && lp.fftma && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + array2D blechro(bfw, bfh); + array2D ble(bfw, bfh); + array2D hue(bfw, bfh); + array2D guid(bfw, bfh); + + std::unique_ptr bufcolorigsav; + std::unique_ptr bufcolorig; + std::unique_ptr bufcolfin; + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + std::unique_ptr bufcolreserv; + + int wo = original->W; + int ho = original->H; + LabImage *origsav = nullptr; + origsav = new LabImage(wo, ho); + origsav->CopyFrom(original); + + if (call <= 3) { + bufcolorig.reset(new LabImage(bfw, bfh)); + bufcolfin.reset(new LabImage(bfw, bfh)); + bufcolorigsav.reset(new LabImage(bfw, bfh)); + + if (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 2 || lp.showmask_met == 3) { + bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); + originalmaskcol.reset(new LabImage(bfw, bfh)); + } +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; + + bufcolorigsav->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorigsav->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorigsav->b[y][x] = original->b[y + ystart][x + xstart]; + + bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + const int inv = 0; + const bool showmaske = lp.showmask_met == 2; + const bool enaMask = lp.ena_Mask; + const bool deltaE = lp.showmask_met == 3; + const bool modmask = lp.showmask_met == 1; + const bool zero = lp.showmask_met == 0; + const bool modif = lp.showmask_met == 1; + const float chrom = params->locallab.spots.at(sp).chromask; + const float rad = params->locallab.spots.at(sp).radmask; + const float gamma = params->locallab.spots.at(sp).gammask; + const float slope = params->locallab.spots.at(sp).slopmask; + float blendm = params->locallab.spots.at(sp).blendmask; + float blendmab = params->locallab.spots.at(sp).blendmaskab; + if (lp.showmask_met == 2) { + blendm = 0.f;//normalize behavior mask with others no action of blend + blendmab = 0.f; + } + const float lap = params->locallab.spots.at(sp).lapmask; + const bool pde = params->locallab.spots.at(sp).laplac; + const int shado = params->locallab.spots.at(sp).shadmask; + const int sco = params->locallab.spots.at(sp).scopemask; + const int level_bl = params->locallab.spots.at(sp).csthresholdmask.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdmask.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdmask.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdmask.getTopRight(); + const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskmask; + const float softr = params->locallab.spots.at(sp).softradiusmask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float amountcd = 0.f; + const float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, astool, + locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, lochhhmas_Curve, lhhmas_utili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendmab, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, + level_bl, level_hl, level_br, level_hr, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma, 12 + ); + + + if (lp.showmask_met == 2) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); + return; + } +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = bufcolorig->L[y][x]; + bufcolfin->a[y][x] = bufcolorig->a[y][x]; + bufcolfin->b[y][x] = bufcolorig->b[y][x]; + hue[y][x] = xatan2f(bufcolfin->b[y][x], bufcolfin->a[y][x]); + const float chromah = std::sqrt(SQR(bufcolfin->b[y][x]) + SQR(bufcolfin->a[y][x])); + ble[y][x] = bufcolfin->L[y][x] / 32768.f; + blechro[y][x] = chromah / 32768.f; + guid[y][x] = bufcolorigsav->L[y][x] / 32768.f; + } + } + if (softr != 0.f) {//soft for L a b because we change color... + float rad = softr; + const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); + const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); + + constexpr float epsilmax = 0.005f; + constexpr float epsilmin = 0.00001f; + + constexpr float aepsil = (epsilmax - epsilmin) / 100.f; + constexpr float bepsil = epsilmin; + const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + float2 sincosval = xsincosf(hue[y][x]); + bufcolfin->L[y][x] = 32768.f * ble[y][x]; + bufcolfin->a[y][x] = 32768.f * blechro[y][x] * sincosval.y; + bufcolfin->b[y][x] = 32768.f * blechro[y][x] * sincosval.x; + } + } + } + + + + float meansob = 0.f; + transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); + delete origsav; + origsav = NULL; + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + } + } + } + +//end common mask + +// Gamut and Munsell control - very important do not deactivated to avoid crash + if (params->locallab.spots.at(sp).avoid) { + const float ach = lp.trans / 100.f; + + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile); + const float wip[3][3] = { + {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, + {static_cast(wiprof[1][0]), static_cast(wiprof[1][1]), static_cast(wiprof[1][2])}, + {static_cast(wiprof[2][0]), static_cast(wiprof[2][1]), static_cast(wiprof[2][2])} + }; + const bool highlight = params->toneCurve.hrenabled; + const bool needHH = (lp.chro != 0.f); +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[transformed->W] ALIGNED16; + float sqrtBuffer[transformed->W] ALIGNED16; + float sincosyBuffer[transformed->W] ALIGNED16; + float sincosxBuffer[transformed->W] ALIGNED16; + vfloat c327d68v = F2V(327.68f); + vfloat onev = F2V(1.f); +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + +#ifdef __SSE2__ + int i = 0; + + for (; i < transformed->W - 3; i += 4) { + vfloat av = LVFU(transformed->a[y][i]); + vfloat bv = LVFU(transformed->b[y][i]); + + if (needHH) { // only do expensive atan2 calculation if needed + STVF(atan2Buffer[i], xatan2f(bv, av)); + } + + vfloat Chprov1v = vsqrtf(SQRV(bv) + SQRV(av)); + STVF(sqrtBuffer[i], Chprov1v / c327d68v); + vfloat sincosyv = av / Chprov1v; + vfloat sincosxv = bv / Chprov1v; + vmask selmask = vmaskf_eq(Chprov1v, ZEROV); + sincosyv = vself(selmask, onev, sincosyv); + sincosxv = vselfnotzero(selmask, sincosxv); + STVF(sincosyBuffer[i], sincosyv); + STVF(sincosxBuffer[i], sincosxv); + } + + for (; i < transformed->W; i++) { + float aa = transformed->a[y][i]; + float bb = transformed->b[y][i]; + + if (needHH) { // only do expensive atan2 calculation if needed + atan2Buffer[i] = xatan2f(bb, aa); + } + + float Chprov1 = std::sqrt(SQR(bb) + SQR(aa)); + sqrtBuffer[i] = Chprov1 / 327.68f; + + if (Chprov1 == 0.0f) { + sincosyBuffer[i] = 1.f; + sincosxBuffer[i] = 0.0f; + } else { + sincosyBuffer[i] = aa / Chprov1; + sincosxBuffer[i] = bb / Chprov1; + } + } + +#endif + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float Lprov1 = transformed->L[y][x] / 327.68f; + float2 sincosval; +#ifdef __SSE2__ + float HH = atan2Buffer[x]; // reading HH from line buffer even if line buffer is not filled is faster than branching + float Chprov1 = sqrtBuffer[x]; + sincosval.y = sincosyBuffer[x]; + sincosval.x = sincosxBuffer[x]; + float chr = 0.f; + +#else + const float aa = transformed->a[y][x]; + const float bb = transformed->b[y][x]; + float HH = 0.f, chr = 0.f; + + if (needHH) { // only do expensive atan2 calculation if needed + HH = xatan2f(bb, aa); + } + + float Chprov1 = std::sqrt(SQR(aa) + SQR(bb)) / 327.68f; + + if (Chprov1 == 0.0f) { + sincosval.y = 1.f; + sincosval.x = 0.0f; + } else { + sincosval.y = aa / (Chprov1 * 327.68f); + sincosval.x = bb / (Chprov1 * 327.68f); + } +#endif + + Color::pregamutlab(Lprov1, HH, chr); + Chprov1 = rtengine::min(Chprov1, chr); + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.92f); + transformed->L[y][x] = Lprov1 * 327.68f; + transformed->a[y][x] = 327.68f * Chprov1 * sincosval.y; + transformed->b[y][x] = 327.68f * Chprov1 * sincosval.x; + + if (needHH) { + const float Lprov2 = original->L[y][x] / 327.68f; + float correctionHue = 0.f; // Munsell's correction + float correctlum = 0.f; + const float memChprov = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])) / 327.68f; + float Chprov = std::sqrt(SQR(transformed->a[y][x]) + SQR(transformed->b[y][x])) / 327.68f; + Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum); + + if (std::fabs(correctionHue) < 0.015f) { + HH += correctlum; // correct only if correct Munsell chroma very little. + } + + sincosval = xsincosf(HH + correctionHue); + transformed->a[y][x] = 327.68f * Chprov * sincosval.y; // apply Munsell + transformed->b[y][x] = 327.68f * Chprov * sincosval.x; + } + } + } + } + } + +} + +} From 55a823897117ccb3b00d0826a4d7379863cfccd2 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 1 Jul 2020 11:15:06 -0700 Subject: [PATCH 066/114] Fix temp image dimensions in transform (#5594) The temporary image should be the same size as the transformed image, not the original image. --- rtengine/iptransform.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 47153c93c..292d96383 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -695,7 +695,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, // steps, using an intermediate temporary image. There's room for // optimization of course... if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) { - tmpimg.reset(new Imagefloat(original->getWidth(), original->getHeight())); + tmpimg.reset(new Imagefloat(transformed->getWidth(), transformed->getHeight())); dest = tmpimg.get(); } } From 01acba4c84a7e8964f8db70bdf22b00e86077001 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Wed, 1 Jul 2020 23:36:31 +0200 Subject: [PATCH 067/114] make a helper function to create the pyramid LUT --- rtengine/iplocallab.cc | 77 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index c07bdee61..0574e4fa4 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -68,6 +68,41 @@ constexpr int TS = 64; // Tile size constexpr float epsilonw = 0.001f / (TS * TS); //tolerance constexpr int offset = 25; // shift between tiles +std::unique_ptr buildMeaLut(const float inVals[11], const float mea[10], float &lutFactor) { + + constexpr int lutSize = 100; + const float lutMax = ceil(mea[9]); + const float lutDiff = lutMax / lutSize; + std::vector lutVals(lutSize); + int jStart = 1; + for (int i = 0; i < 100; ++i) { + const float val = i * lutDiff; + if (val < mea[0]) { + // still < first value => no interpolation + lutVals[i] = inVals[0]; + } else { + for (int j = jStart; j < 10; ++j) { + if (val == mea[j]) { + // exact match => no interpolation + lutVals[i] = inVals[j]; + ++jStart; + break; + } else if (val < mea[j]) { + // interpolate + const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); + lutVals[i] = rtengine::intp(dist, inVals[j], inVals[j - 1]); + break; + } else { + lutVals[i] = inVals[10]; + } + } + } + } + lutFactor = 1.f / lutDiff; + return std::unique_ptr(new LUTf(lutVals)); + +} + constexpr float clipLoc(float x) { return rtengine::LIM(x, 0.f, 32767.f); } @@ -7019,43 +7054,9 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel constexpr float offs = 1.f; float mea[10]; calceffect(level, mean, sigma, mea, effect, offs); - constexpr int lutSize = 100; - const float lutMax = ceil(mea[9]); - const float lutDiff = lutMax / lutSize; - std::vector lutVals(lutSize); - std::vector inVals({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); - int jStart = 1; - for (int i = 0; i < 100; ++i) { - const float val = i * lutDiff; - if (val < mea[0]) { - // still < first value => no interpolation - lutVals[i] = inVals[0]; - } else { - for (int j = jStart; j < 10; ++j) { - if (val == mea[j]) { - // exact match => no interpolation - lutVals[i] = inVals[j]; - ++jStart; - break; - } else if (val < mea[j]) { - // interpolate - const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); - lutVals[i] = intp(dist, inVals[j], inVals[j - 1]); - break; - } else { - lutVals[i] = inVals[10]; - } - } - } - } - const LUTf meaLut(lutVals); - const float lutFactor = 1.f / lutDiff; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - beta[co] = meaLut[std::fabs(WavL[co]) * lutFactor]; - } + float lutFactor; + const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}; + const auto meaLut = buildMeaLut(inVals, mea, lutFactor); const float klev = 0.25f * loclevwavCurve[level * 55.5f]; float* src[H_L]; @@ -7075,7 +7076,7 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { int j = y * W_L + x; - WavL[j] = intp(beta[j], templevel[y][x], WavL[j]); + WavL[j] = intp((*meaLut)[std::fabs(WavL[j]) * lutFactor], templevel[y][x], WavL[j]); } } } From ccbabb84984f255ad2449ef968aa4a4b98ddd1ca Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 08:12:34 +0200 Subject: [PATCH 068/114] Change label weakening decay - suppress f to DCR label - change order add tool --- rtdata/languages/default | 24 ++++++++++++------------ rtengine/procparams.cc | 2 +- rtgui/locallab.cc | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 18dc589ca..7ce5b4f3f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -761,7 +761,7 @@ HISTORY_MSG_508;Local Spot circrad HISTORY_MSG_509;Local Spot quality method HISTORY_MSG_510;Local Spot transition HISTORY_MSG_511;Local Spot thresh -HISTORY_MSG_512;Local Spot ΔE -weakening +HISTORY_MSG_512;Local Spot ΔE -decay HISTORY_MSG_513;Local Spot scope HISTORY_MSG_514;Local Spot structure HISTORY_MSG_515;Local Adjustments @@ -907,7 +907,7 @@ HISTORY_MSG_655;Local - SH mask slope HISTORY_MSG_656;Local - Color soft radius HISTORY_MSG_657;Local - Retinex Reduce artifacts HISTORY_MSG_658;Local - CBDL soft radius -HISTORY_MSG_659;Local Spot transition-weakening +HISTORY_MSG_659;Local Spot transition-decay HISTORY_MSG_660;Local - cbdl clarity HISTORY_MSG_661;Local - cbdl contrast residual HISTORY_MSG_662;Local - deNoise lum f 0 @@ -2332,7 +2332,7 @@ TP_LOCALLAB_ADJ;Equalizer Blue-yellow Red-green TP_LOCALLAB_ALL;All rubrics TP_LOCALLAB_AMOUNT;Amount TP_LOCALLAB_ARTIF;Shape detection -TP_LOCALLAB_ARTIF_TOOLTIP;Threshold deltaE-scope increase the range of scope-deltaE - high values are for very wide gamut images.\nIncrease deltaE Weakening improve shape detection, but can reduce the scope of detection. +TP_LOCALLAB_ARTIF_TOOLTIP;Threshold deltaE-scope increase the range of scope-deltaE - high values are for very wide gamut images.\nIncrease deltaE decay improve shape detection, but can reduce the scope of detection. TP_LOCALLAB_AUTOGRAY;Automatic TP_LOCALLAB_AVOID;Avoid color shift TP_LOCALLAB_BALAN;Balance ΔE ab-L @@ -2476,13 +2476,13 @@ TP_LOCALLAB_EXCLUTYPE;Spot method TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Normal spot use recursive data.\n\nExcluding spot reinitialize data to origin.\nCan be used to totally or partially cancel a previous action or to perform a inverse mode TP_LOCALLAB_EXECLU;Excluding spot TP_LOCALLAB_EXNORM;Normal spot -TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition weakening (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition decay (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise) TP_LOCALLAB_EXPCHROMA;Chroma compensation TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) -TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE weakening'\nIncrease 'Balance ΔE ab-L' +TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE decay'\nIncrease 'Balance ΔE ab-L' TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. -TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high decay transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. TP_LOCALLAB_EXPCURV;Curves TP_LOCALLAB_EXPGRAD;Graduated Filter TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balances the action between the original image and the Laplace transform. @@ -2495,7 +2495,7 @@ TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to pr TP_LOCALLAB_EXPOSE;Exposure - PDE algorithms TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools -TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high weakening transition values and scope to simulate small RT-spot. +TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high decay transition values and scope to simulate small RT-spot. TP_LOCALLAB_EXPTOOL;Tools exposure TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10 @@ -2535,7 +2535,7 @@ TP_LOCALLAB_GRADSTRLUM;Gradient strength Luminance TP_LOCALLAB_GRADSTR_TOOLTIP;Filter strength in stops TP_LOCALLAB_GRAINFRA;Film Grain 1:1 TP_LOCALLAB_GRALWFRA;Graduated Filter Local contrast -TP_LOCALLAB_GRIDFRAME_TOOLTIP;You can use this tool as a brush. Use small spot and adapt transition and transition weakening\nOnly mode NORMAL and eventually Hue, Saturation, Color, Luminosity are concerned by Merge background (ΔE) +TP_LOCALLAB_GRIDFRAME_TOOLTIP;You can use this tool as a brush. Use small spot and adapt transition and transition decay\nOnly mode NORMAL and eventually Hue, Saturation, Color, Luminosity are concerned by Merge background (ΔE) TP_LOCALLAB_GRIDONE;Color Toning TP_LOCALLAB_GRIDTWO;Direct TP_LOCALLAB_GUIDBL;Soft radius @@ -2684,7 +2684,7 @@ TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n TP_LOCALLAB_PREVIEW;Preview ΔE -TP_LOCALLAB_PROXI;ΔE weakening +TP_LOCALLAB_PROXI;ΔE decay TP_LOCALLAB_QUALCURV_METHOD;Curves type TP_LOCALLAB_QUAL_METHOD;Global quality TP_LOCALLAB_RADIUS;Radius @@ -2849,8 +2849,8 @@ TP_LOCALLAB_TRANSIT;Transition Gradient TP_LOCALLAB_TRANSITGRAD;Transition differentiation XY TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Changes the transition of the abscissa to that of the ordinate TP_LOCALLAB_TRANSITVALUE;Transition value -TP_LOCALLAB_TRANSITWEAK;Transition weakening -TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Adjust transition weakening : change smoothness process - 1 linear - 2 parabolic - 3 cubic - ^25.\nCan be used in conjunction with very low transition values to reduce defects (CBDL, Wavelet, Color & Light) +TP_LOCALLAB_TRANSITWEAK;Transition decay (linear-log) +TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Adjust transition decay : change smoothness process - 1 linear - 2 parabolic - 3 cubic - ^25.\nCan be used in conjunction with very low transition values to reduce defects (CBDL, Wavelet, Color & Light) TP_LOCALLAB_TRANSIT_TOOLTIP;Adjust smoothness of transition between affected and unaffected areas, as a percentage of the "radius" TP_LOCALLAB_TRANSMISSIONGAIN;Transmission gain TP_LOCALLAB_TRANSMISSIONMAP;Transmission map @@ -3186,7 +3186,7 @@ TP_SOFTLIGHT_LABEL;Soft Light TP_SOFTLIGHT_STRENGTH;Strength TP_TM_FATTAL_AMOUNT;Amount TP_TM_FATTAL_ANCHOR;Anchor -TP_TM_FATTAL_LABEL;Dynamic Range Compression ƒ +TP_TM_FATTAL_LABEL;Dynamic Range Compression TP_TM_FATTAL_THRESHOLD;Detail TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 6b67726a0..deeae9c54 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2964,7 +2964,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : visishadhigh(false), expshadhigh(false), complexshadhigh(0), - shMethod("std"), + shMethod("tone"), multsh{0, 0, 0, 0, 0}, highlights(0), h_tonalwidth(70), diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 12de07536..80b92122a 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -188,9 +188,10 @@ Locallab::Locallab(): ToolVBox* const toolpanel = Gtk::manage(new ToolVBox()); toolpanel->set_name("LocallabToolPanel"); addTool(toolpanel, expcolor); - addTool(toolpanel, expexpose); addTool(toolpanel, expshadhigh); addTool(toolpanel, expvibrance); + addTool(toolpanel, explog); + addTool(toolpanel, expexpose); addTool(toolpanel, expmask); addTool(toolpanel, expsoft); addTool(toolpanel, expblur); @@ -199,7 +200,6 @@ Locallab::Locallab(): addTool(toolpanel, expsharp); addTool(toolpanel, expcontrast); addTool(toolpanel, expcbdl); - addTool(toolpanel, explog); panel->pack_start(*toolpanel, false, false); // Add separator From 3118d4d5de186696dd401f08026d1c2c29d2d458 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 13:10:43 +0200 Subject: [PATCH 069/114] first step french langauge local adjustments --- rtdata/languages/Francais | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index a98b6eded..1dfc22a06 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -904,6 +904,8 @@ MAIN_TAB_FAVORITES_TOOLTIP;Raccourci: Alt-u MAIN_TAB_FILTER; Filtrer MAIN_TAB_INSPECT; Inspecter MAIN_TAB_IPTC;IPTC +MAIN_TAB_LOCALLAB;Local +MAIN_TAB_LOCALLAB_TOOLTIP;Raccourci: Alt-o MAIN_TAB_METADATA;Métadonnées MAIN_TAB_METADATA_TOOLTIP;Raccourci:Alt-m MAIN_TAB_RAW;RAW @@ -987,6 +989,8 @@ PARTIALPASTE_LABCURVE;Courbes Lab PARTIALPASTE_LENSGROUP;Réglages de l'objectif PARTIALPASTE_LENSPROFILE;Profil de correction d'Objectif PARTIALPASTE_LOCALCONTRAST;Contraste local +PARTIALPASTE_LOCALLAB;Ajustements locauc +PARTIALPASTE_LOCALLABGROUP;Réglages Ajustements locaux PARTIALPASTE_METADATA;Mode des Metadonnées PARTIALPASTE_METAGROUP;Réglages des Métadonnées PARTIALPASTE_PCVIGNETTE;Filtre Vignettage @@ -1747,6 +1751,53 @@ TP_LOCALCONTRAST_DARKNESS;Niveau des ombres TP_LOCALCONTRAST_LABEL;Contraste Local TP_LOCALCONTRAST_LIGHTNESS;Niveau des hautes-lumières TP_LOCALCONTRAST_RADIUS;Rayon +TP_LOCALLAB_ACTIV;Luminosité seulement +TP_LOCALLAB_ADJ;Egalisateur Bleu-jaune Rouge-vert +TP_LOCALLAB_ALL;Toutes les rubriques +TP_LOCALLAB_AMOUNT;Quantité +TP_LOCALLAB_ARTIF;Détection de forme +TP_LOCALLAB_ARTIF_TOOLTIP;Le seuil deltaE-étendue accroit la plage of étendue-deltaE - les valeurs élévées sont pour les images à gamut élévé.\nAugmenter l'affaiblissement deltaE améliore la détection de forme, mais peu réduire la capacité de détection. +TP_LOCALLAB_AUTOGRAY;Automatique +TP_LOCALLAB_AVOID;Evite les dérives de couleurs +TP_LOCALLAB_BALAN;Balance ΔE ab-L +TP_LOCALLAB_BALANEXP;ΔØ EPD balance +TP_LOCALLAB_BALANH;Balance ΔE C-H +TP_LOCALLAB_BALAN_TOOLTIP;Change l'algorithme des paramètres ΔE.\nPlus ou moins ab-L, plus ou moins C - H.\nPas pour le Debruitage +TP_LOCALLAB_BASELOG;Base Logarithme +TP_LOCALLAB_BILATERAL;Filtre Bilateral +TP_LOCALLAB_BLACK_EV;Noir Ev +TP_LOCALLAB_BLCO;Chrominance seulement +TP_LOCALLAB_BLENDMASKCOL;Mélange - fusion +TP_LOCALLAB_BLENDMASKMASK;Ajout / soustrait le masque Luminance +TP_LOCALLAB_BLENDMASKMASKAB;Ajout / soustrait le masque Chrominance +TP_LOCALLAB_BLENDMASK_TOOLTIP;Si fusion = 0 seule la détection de forme est améliorée.\nSi fusion > 0 le masque est ajouté à l'image. Si fusion < 0 le masque est soustrait à l'image +TP_LOCALLAB_BLENDMASKMASK_TOOLTIP;Si ce curseur = 0 pas d'action.\nAjoute ou soustrait le masque de l'image originale +TP_LOCALLAB_BLGUID;Filtre guidé +TP_LOCALLAB_BLINV;Inverse +TP_LOCALLAB_BLLC;Luminance & Chrominance +TP_LOCALLAB_BLLO;Luminance seulement +TP_LOCALLAB_BLMED;Median +TP_LOCALLAB_BLMETHOD_TOOLTIP;Normal - direct floute et bruite avec tous les réglages.\nInverse floute et bruite avec tous les réglages. Soyez prudents certains resultats peuvent être curieux +TP_LOCALLAB_BLNOI_EXP;Blur & Noise +TP_LOCALLAB_BLNORM;Normal +TP_LOCALLAB_BLSYM;Symétrique +TP_LOCALLAB_BLUFR;Doux - Floute - Grain - Debruitage +TP_LOCALLAB_BLUMETHOD_TOOLTIP;To blur the background and isolate the foreground:\n*Blur the background by a RT-spot fully covering the image (high values for scope and transition) - normal or inverse.\n*Isolate the foreground by one or more excluding RT-spot with the tools you want (increse scope).\n\nThis module can be used in additional noise reduction,including "median" and "Guided filter" +TP_LOCALLAB_BLUR;Flou Gaussien Blur - Bruit - Grain +TP_LOCALLAB_BLURCBDL;Flou niveaux 0-1-2-3-4 +TP_LOCALLAB_BLURCOL;Rayon floutage masque +TP_LOCALLAB_BLURDE;Floute la détection de forme +TP_LOCALLAB_BLURLC;Luminance seulement +TP_LOCALLAB_BLURLEVELFRA;Floute niveaux +TP_LOCALLAB_BLURMASK_TOOLTIP;Génère un masque flou, prend en compte la structure avec le curseur de seuil de contraste du Masque flou. +TP_LOCALLAB_BLURRESIDFRA;Floute image Résiduelle +TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Floute Grain & Débruitage - 1 +TP_LOCALLAB_BLWH;Tous les chnagements forcés en noir et blanc +TP_LOCALLAB_BLWH_TOOLTIP;Force le changement de la composante "a" et "b" à zéro.\nUtile quand l'utilisateur choisit un processus noir et blanc, ou un film. +TP_LOCALLAB_BUTTON_ADD;Ajout +TP_LOCALLAB_BUTTON_DEL;Efface +TP_LOCALLAB_BUTTON_DUPL;Duplique +TP_LOCALLAB_BUTTON_REN;Renomme TP_METADATA_EDIT;Appliquer les modifications TP_METADATA_MODE;Mode de copie des métadonnées TP_METADATA_STRIP;Retirer toutes les métadonnées From 237d97671ccd1603f4d651b6744f2149b79123fb Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 2 Jul 2020 13:12:46 +0200 Subject: [PATCH 070/114] wavcont(): always use meaLut --- rtengine/iplocallab.cc | 78 ++++++------------------------------------ 1 file changed, 11 insertions(+), 67 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 0574e4fa4..15e40be5b 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7029,8 +7029,6 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel const int W_L = wdspot.level_W(0); const int H_L = wdspot.level_H(0); - const std::unique_ptr beta(new float[W_L * H_L]); - #ifdef _OPENMP const int numThreads = omp_get_max_threads(); #else @@ -7089,37 +7087,9 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel constexpr float offs = 1.f; float mea[10]; calceffect(level, mean, sigma, mea, effect, offs); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(WavL[co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.7f; - } else if (WavCL < mea[7]) { - beta[co] = 0.5f; - } else if (WavCL < mea[8]) { - beta[co] = 0.3f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.2f; - } else { - beta[co] = 0.1f; - } - } - + float lutFactor; + const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.7f, 0.5f, 0.3f, 0.2f, 0.1f}; + const auto meaLut = buildMeaLut(inVals, mea, lutFactor); const int iteration = deltad; const int itplus = 7 + iteration; const int itmoins = 7 - iteration; @@ -7147,6 +7117,7 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel const vfloat zd5v = F2V(0.5f); const vfloat onev = F2V(1.f); const vfloat itfv = F2V(itf); + const vfloat lutFactorv = F2V(lutFactor); #endif #ifdef _OPENMP #pragma omp for @@ -7157,13 +7128,14 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel for (; j < W_L - 3; j += 4) { const vfloat LL100v = LC2VFU(tmp[i * 2][j * 2]) / c327d68v; const vfloat kbav = factorv * (loccompwavCurve[sixv * LL100v] - zd5v); //k1 between 0 and 0.5 0.5==> 1/6=0.16 - STVFU(WavL[i * W_L + j], LVFU(WavL[i * W_L + j]) * pow_F(onev + kbav * LVFU(beta[i * W_L + j]), itfv)); + const vfloat valv = LVFU(WavL[i * W_L + j]); + STVFU(WavL[i * W_L + j], valv * pow_F(onev + kbav * (*meaLut)[valv * lutFactorv], itfv)); } #endif for (; j < W_L; ++j) { const float LL100 = tmp[i * 2][j * 2] / 327.68f; const float kba = factor * (loccompwavCurve[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16 - WavL[i * W_L + j] *= pow_F(1.f + kba * beta[i * W_L + j], itf); + WavL[i * W_L + j] *= pow_F(1.f + kba * (*meaLut)[WavL[i * W_L + j] * lutFactor], itf); } } } @@ -7186,41 +7158,13 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel for (int dir = 1; dir < 4; ++dir) { for (int level = level_bl; level < maxlvl; ++level) { - const auto WavL = wdspot.level_coeffs(level)[dir]; const float effect = lp.sigmadr; constexpr float offs = 1.f; float mea[10]; calceffect(level, mean, sigma, mea, effect, offs); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(WavL[co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.65f; - } else if (WavCL < mea[7]) { - beta[co] = 0.5f; - } else if (WavCL < mea[8]) { - beta[co] = 0.4f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.25f; - } else { - beta[co] = 0.1f; - } - } + float lutFactor; + const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.65f, 0.5f, 0.4f, 0.25f, 0.1f}; + const auto meaLut = buildMeaLut(inVals, mea, lutFactor); float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); if (klev < 0.f) { @@ -7250,7 +7194,7 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { int j = y * W_L + x; - wav_L[j] = intp(beta[j], templevel[y][x], wav_L[j]); + wav_L[j] = intp((*meaLut)[wav_L[j]], templevel[y][x], wav_L[j]); } } } From a329b5f5d258930ec05db19933af02d7e012b751 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 14:16:29 +0200 Subject: [PATCH 071/114] First french --- rtdata/languages/Francais | 40 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 1dfc22a06..120759609 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1797,7 +1797,45 @@ TP_LOCALLAB_BLWH_TOOLTIP;Force le changement de la composante "a" et "b" à z TP_LOCALLAB_BUTTON_ADD;Ajout TP_LOCALLAB_BUTTON_DEL;Efface TP_LOCALLAB_BUTTON_DUPL;Duplique -TP_LOCALLAB_BUTTON_REN;Renomme +TP_LOCALLAB_BUTTON_REN;Renomme +TP_LOCALLAB_BUTTON_VIS;Montre/Cache +TP_LOCALLAB_CBDL;Contraste par niveaux de détail - Défauts +TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. +TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. +TP_LOCALLAB_CBDL_THRES_TOOLTIP;Empêche d'augmenter le bruit +TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defauts) - 2 +TP_LOCALLAB_CENTER_X;Centre X +TP_LOCALLAB_CENTER_Y;Centre Y +TP_LOCALLAB_CH;Courbes CL - LC +TP_LOCALLAB_CHROMA;Chrominance +TP_LOCALLAB_CHROMABLU;Niveaux Chroma +TP_LOCALLAB_CHROMABLU_TOOLTIP;Agit comme un amplificateur-reducteur d'action en comparant aux réglages de luma.\nEn dessous de 1 reduit, au dessus de 1 amplifie +TP_LOCALLAB_CHROMACBDL;Chroma +TP_LOCALLAB_CHROMACB_TOOLTIP;Agit comme un amplificateur-reducteur d'action en comparant aux curseurs de luminance.\nEn dessous de 100 reduit, au dessus de 100 amplifie +TP_LOCALLAB_CHROMALEV;Niveaux de Chroma +TP_LOCALLAB_CHROMASKCOL;Masque Chroma +TP_LOCALLAB_CHROMASK_TOOLTIP;Vous pouvez utiliser ce curseur pour désaturer l'arrière plan (inverse masque - courbe proche de 0).\nEgalement pour atténier ou accroître l'action du masque sur la chroma +TP_LOCALLAB_CHRRT;Chroma +TP_LOCALLAB_CIRCRADIUS;Taille Spot +TP_LOCALLAB_CIRCRAD_TOOLTIP;Contient les références du RT-spot, utile pour la détection de forme (couleur, luma, chroma, Sobel).\nLes faibles valeurs peuvent être utiles pour les feuillages.\nLes valeurs élevées peuvent être utile pour la peau +TP_LOCALLAB_CLARICRES;Fusion Chroma +TP_LOCALLAB_CLARIFRA;Clarté & Masque de betteté - Fusion & adoucir images +TP_LOCALLAB_CLARILRES;Fusion Luma +TP_LOCALLAB_CLARISOFT;rayon adoucir +TP_LOCALLAB_CLARISOFT_TOOLTIP;Actif pour Clarté et Masque de netteté si différent de zéro.\n\nActif pour toutes les pyramides ondelettes.\nInactif si rayon = 0 +TP_LOCALLAB_CLARITYML;Claté +TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' +TP_LOCALLAB_CLIPTM;Clip Recupère données (gain) +TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts +TP_LOCALLAB_COLORDE;Coleurr previsualisation sélection ΔE - Intensité +TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Previsualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Previsualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE +TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. +TP_LOCALLAB_COLORSCOPE;Outils Etendue Couleur +TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. +TP_LOCALLAB_COLOR_TOOLNAME;Couleur&Lumière (Défauts) - 11 + + + TP_METADATA_EDIT;Appliquer les modifications TP_METADATA_MODE;Mode de copie des métadonnées TP_METADATA_STRIP;Retirer toutes les métadonnées From cf0ccc26caa8bf5681b2f47c047a2e704081f8b8 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Thu, 2 Jul 2020 14:17:32 +0200 Subject: [PATCH 072/114] wavcont(): fixed two bugs and added SSE code for one loop in blur mode --- rtengine/iplocallab.cc | 33 +- rtengine/iplocallab.cc.save-failed | 14814 --------------------------- 2 files changed, 23 insertions(+), 14824 deletions(-) delete mode 100644 rtengine/iplocallab.cc.save-failed diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 15e40be5b..74be48e09 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7043,7 +7043,6 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); if (process == 1 && loclevwavCurve && loclevwavutili) { //blur - StopWatch Stop1("blur"); array2D templevel(W_L, H_L); for (int dir = 1; dir < 4; ++dir) { for (int level = level_bl; level < maxlvl; ++level) { @@ -7067,14 +7066,28 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel { gaussianBlur(src, templevel, W_L, H_L, radlevblur * klev * chromablu); } - #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel if (multiThread) #endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - int j = y * W_L + x; - WavL[j] = intp((*meaLut)[std::fabs(WavL[j]) * lutFactor], templevel[y][x], WavL[j]); + { +#ifdef __SSE2__ + const vfloat lutFactorv = F2V(lutFactor); +#endif +#ifdef _OPENMP + #pragma omp for +#endif + for (int y = 0; y < H_L; y++) { + int x = 0; + int j = y * W_L; +#ifdef __SSE2__ + for (; x < W_L - 3; x += 4, j += 4) { + const vfloat valv = LVFU(WavL[j]); + STVFU(WavL[j], intp((*meaLut)[vabsf(valv) * lutFactorv], LVFU(templevel[y][x]), valv)); + } +#endif + for (; x < W_L; x++, j++) { + WavL[j] = intp((*meaLut)[std::fabs(WavL[j]) * lutFactor], templevel[y][x], WavL[j]); + } } } } @@ -7129,13 +7142,13 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel const vfloat LL100v = LC2VFU(tmp[i * 2][j * 2]) / c327d68v; const vfloat kbav = factorv * (loccompwavCurve[sixv * LL100v] - zd5v); //k1 between 0 and 0.5 0.5==> 1/6=0.16 const vfloat valv = LVFU(WavL[i * W_L + j]); - STVFU(WavL[i * W_L + j], valv * pow_F(onev + kbav * (*meaLut)[valv * lutFactorv], itfv)); + STVFU(WavL[i * W_L + j], valv * pow_F(onev + kbav * (*meaLut)[vabsf(valv) * lutFactorv], itfv)); } #endif for (; j < W_L; ++j) { const float LL100 = tmp[i * 2][j * 2] / 327.68f; const float kba = factor * (loccompwavCurve[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16 - WavL[i * W_L + j] *= pow_F(1.f + kba * (*meaLut)[WavL[i * W_L + j] * lutFactor], itf); + WavL[i * W_L + j] *= pow_F(1.f + kba * (*meaLut)[std::fabs(WavL[i * W_L + j]) * lutFactor], itf); } } } @@ -7194,7 +7207,7 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { int j = y * W_L + x; - wav_L[j] = intp((*meaLut)[wav_L[j]], templevel[y][x], wav_L[j]); + wav_L[j] = intp((*meaLut)[std::fabs(wav_L[j]) * lutFactor], templevel[y][x], wav_L[j]); } } } diff --git a/rtengine/iplocallab.cc.save-failed b/rtengine/iplocallab.cc.save-failed deleted file mode 100644 index e0dcd1659..000000000 --- a/rtengine/iplocallab.cc.save-failed +++ /dev/null @@ -1,14814 +0,0 @@ -/* - * This file is part of RawTherapee. - * - * Copyright (c) 2004-2010 Gabor Horvath - * - * - * 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 . - * 2016 - 2020 Jacques Desmis - * 2016 - 2020 Ingo Weyrich - - */ -#include -#include - -#include "improcfun.h" -#include "colortemp.h" -#include "curves.h" -#include "gauss.h" -#include "iccstore.h" -#include "imagefloat.h" -#include "labimage.h" -#include "color.h" -#include "rt_math.h" -#include "jaggedarray.h" -#include "rt_algo.h" -#include "settings.h" -#include "../rtgui/options.h" - -#include "utils.h" -#ifdef _OPENMP -#include -#endif -#include "../rtgui/thresholdselector.h" -#include "imagesource.h" - -#include "cplx_wavelet_dec.h" -#include "ciecam02.h" - -#define BENCHMARK -#include "StopWatch.h" -#include "guidedfilter.h" - - -#pragma GCC diagnostic warning "-Wall" -#pragma GCC diagnostic warning "-Wextra" - -namespace -{ -constexpr int limscope = 80; -constexpr int mSPsharp = 39; //minimum size Spot Sharp due to buildblendmask -constexpr int mSPwav = 32; //minimum size Spot Wavelet -constexpr int mDEN = 64; //minimum size Spot Denoise -constexpr int mSP = 5; //minimum size Spot -constexpr float MAXSCOPE = 1.25f; -constexpr float MINSCOPE = 0.025f; -constexpr int TS = 64; // Tile size -constexpr float epsilonw = 0.001f / (TS * TS); //tolerance -constexpr int offset = 25; // shift between tiles - -constexpr float clipLoc(float x) { - return rtengine::LIM(x, 0.f, 32767.f); -} - -constexpr float clipDE(float x) { - return rtengine::LIM(x, 0.3f, 1.f); -} - -constexpr float clipC(float x) { - return rtengine::LIM(x, -42000.f, 42000.f); -} - -constexpr float clipChro(float x) { - return rtengine::LIM(x, 0.f, 140.f); -} - -float softlig(float a, float b, float minc, float maxc) -{ - // as Photoshop - if (2.f * b <= maxc - minc) { - return a * (2.f * b + a * (maxc - 2.f * b)); - } else { - return 2.f * a * (maxc - b) + std::sqrt(rtengine::LIM(a, 0.f, 2.f)) * (2.f * b - maxc); - } -} - -float softlig3(float a, float b) -{ - // as w3C - if (2.f * b <= 1.f) { - return a - (1.f - 2.f * b) * a * (1.f - a); - } else { - if (4.f * a <= 1.f) { - return a + (2.f * b - 1.f) * (4.f * a * (4.f * a + 1.f) * (a - 1.f) + 7.f * a); - } else { - return a + (2.f * a - 1.f) * (std::sqrt(a) - a); - } - } -} - -float softlig2(float a, float b) -{ - // illusions.hu - return pow_F(b, pow_F(2.f, (2.f * (0.5f - a)))); -} - -constexpr float colburn(float a, float b) -{ - // w3C - return b == 0.f ? 0.f : 1.f - rtengine::min(1.f, (1.f - a) / b); -} - -constexpr float coldodge(float a, float b) -{ - // w3C - return b == 1.f ? 1.f : rtengine::min(1.f, a / (1.f - b)); -} - -float overlay(float a, float b, float minc, float maxc) -{ - if (2.f * b <= maxc - minc) { - return 2.f * b * a; - } else { - return maxc - 2.f * (1.f - a) * (maxc - b); - } -} - -constexpr float screen(float a, float b, float maxc) -{ - return 1.f - (1.f - a) * (maxc - b); -} - -constexpr float exclusion(float a, float b) -{ - return a + b - 2.f * a * b; -} - -void calcGammaLut(double gamma, double ts, LUTf &gammaLut) -{ - double pwr = 1.0 / gamma; - double gamm = gamma; - const double gamm2 = gamma; - rtengine::GammaValues g_a; - - if (gamm2 < 1.0) { - std::swap(pwr, gamm); - } - - rtengine::Color::calcGamma(pwr, ts, 0, g_a); // call to calcGamma with selected gamma and slope - - const double start = gamm2 < 1. ? g_a[2] : g_a[3]; - const double add = g_a[4]; - const double mul = 1.0 + g_a[4]; - - if (gamm2 < 1.) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 1024) -#endif - for (int i = 0; i < 65536; i++) { - const double x = rtengine::Color::igammareti(i / 65535.0, gamm, start, ts, mul, add); - gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values - } - } else { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 1024) -#endif - for (int i = 0; i < 65536; i++) { - const double x = rtengine::Color::gammareti(i / 65535.0, gamm, start, ts, mul, add); - gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values - } - } -} - -float calcLocalFactor(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) -{ - //ellipse x2/a2 + y2/b2=1 - //transition ellipsoidal - const float kelip = dx / dy; - const float belip = rtengine::max(0.0001f, std::sqrt((rtengine::SQR((lox - lcx) / kelip) + rtengine::SQR(loy - lcy)))); //determine position ellipse ==> a and b - - //gradient allows differentiation between transition x and y - const float rapy = std::fabs((loy - lcy) / belip); - const float aelip = belip * kelip; - const float degrad = aelip / dx; - const float gradreal = gradient * rapy + 1.f; - const float ap = rtengine::RT_PI_F / (1.f - ach); - const float bp = rtengine::RT_PI_F - ap; - return pow(0.5f * (1.f + xcosf(degrad * ap + bp)), rtengine::SQR(gradreal)); // trigo cos transition -} - -float calcLocalFactorrect(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) -{ - constexpr float eps = 0.0001f; - const float krap = std::fabs(dx / dy); - const float kx = lox - lcx; - const float ky = loy - lcy; - - float ref; - //gradient allows differentiation between transition x and y - if (std::fabs(kx / (ky + eps)) < krap) { - ref = std::sqrt(rtengine::SQR(dy) * (1.f + rtengine::SQR(kx / (ky + eps)))); - } else { - ref = std::sqrt(rtengine::SQR(dx) * (1.f + rtengine::SQR(ky / (kx + eps)))); - } - - const float rad = rtengine::max(eps, std::sqrt(rtengine::SQR(kx) + rtengine::SQR(ky))); - const float rapy = std::fabs((loy - lcy) / rad); - const float gradreal = gradient * rapy + 1.f; - - const float coef = rad / ref; - const float fact = (coef - 1.f) / (ach - 1.f); - return pow(fact, rtengine::SQR(gradreal)); -} - -float calcreducdE(float dE, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, int limscope, int scope) -{ - if (scope > limscope) {//80 arbitrary value, if we change we must change limscope - if (dE > maxdElim) { - return 0.f; - } else if (dE > mindElim) { - const float reducdElim = std::pow((dE - maxdElim) / (mindElim - maxdElim), iterat); - const float aalim = (1.f - reducdElim) / 20.f; - const float bblim = 1.f - 100.f * aalim; - return aalim * scope + bblim; - } else { - return 1.f; - } - } else { - if (dE > maxdE) { - return 0.f; - } else if (dE > mindE) { - return std::pow((dE - maxdE) / (mindE - maxdE), iterat); - } else { - return 1.f; - } - } -} - -void deltaEforLaplace(float *dE, const float lap, int bfw, int bfh, rtengine::LabImage* bufexporig, const float hueref, const float chromaref, const float lumaref) -{ - - const float refa = chromaref * std::cos(hueref); - const float refb = chromaref * std::sin(hueref); - const float refL = lumaref; - float maxdE = 5.f + MAXSCOPE * lap; - - float maxC = std::sqrt((rtengine::SQR(refa - bufexporig->a[0][0]) + rtengine::SQR(refb - bufexporig->b[0][0])) + rtengine::SQR(refL - bufexporig->L[0][0])) / 327.68f; -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxC) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - const float val = std::sqrt((rtengine::SQR(refa - bufexporig->a[y][x]) + rtengine::SQR(refb - bufexporig->b[y][x])) + rtengine::SQR(refL - bufexporig->L[y][x])) / 327.68f; - dE[y * bfw + x] = val; - maxC = rtengine::max(maxC, val); - } - } - - if (maxdE > maxC) { - maxdE = maxC - 1.f; - } - - const float ade = 1.f / (maxdE - maxC); - const float bde = -ade * maxC; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - dE[y * bfw + x] = dE[y * bfw + x] >= maxdE ? ade * dE[y * bfw + x] + bde : 1.f; - } - } -} - -float calclight(float lum, const LUTf &lightCurveloc) -{ - return clipLoc(lightCurveloc[lum]); -} - -float calclightinv(float lum, float koef, const LUTf &lightCurveloc) -{ - return koef != -100.f ? clipLoc(lightCurveloc[lum]) : 0.f; -} - -float balancedeltaE(float kL) -{ - constexpr float mincurs = 0.3f; // minimum slider balan_ - constexpr float maxcurs = 1.7f; // maximum slider balan_ - constexpr float maxkab = 1.35; // 0.5 * (3 - 0.3) - constexpr float minkab = 0.65; // 0.5 * (3 - 1.7) - constexpr float abal = (maxkab - minkab) / (mincurs - maxcurs); - constexpr float bbal = maxkab - mincurs * abal; - return abal * kL + bbal; -} - -void SobelCannyLuma(float **sobelL, float **luma, int bfw, int bfh, float radius) -{ - // base of the process to detect shape in complement of deltaE - // use for calculate Spot reference - // and for structure of the shape - // actually , as the program don't use these function, I just create a simple "Canny" near of Sobel. This can be completed after with teta, etc. - array2D tmL(bfw, bfh); - - //inspired from Chen Guanghua Zhang Xiaolong - //Sobel Horizontal - constexpr float GX[3][3] = { - {1.f, 0.f, -1.f}, - {2.f, 0.f, -2.f}, - {1.f, 0.f, -1.f} - }; - - //Sobel Vertical - constexpr float GY[3][3] = { - {1.f, 2.f, 1.f}, - {0.f, 0.f, 0.f}, - {-1.f, -2.f, -1.f} - }; - - if (radius > 0.f) { - gaussianBlur(luma, tmL, bfw, bfh, rtengine::max(radius / 2.f, 0.5f)); - } else { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw ; x++) { - tmL[y][x] = luma[y][x]; - } - } - } - - for (int x = 0; x < bfw; x++) { - sobelL[0][x] = 0.f; - } - for (int y = 1; y < bfh - 1; y++) { - sobelL[y][0] = 0.f; - for (int x = 1; x < bfw - 1; x++) { - float sumXL = 0.f; - float sumYL = 0.f; - for (int i = -1; i < 2; i += 2) { - for (int j = -1; j < 2; j += 1) { - sumXL += GX[j + 1][i + 1] * tmL[y + i][x + j]; - sumYL += GY[j + 1][i + 1] * tmL[y + i][x + j]; - } - } - //Edge strength - //we can add if need teta = atan2 (sumYr, sumXr) - sobelL[y][x] = rtengine::min(std::sqrt(rtengine::SQR(sumXL) + rtengine::SQR(sumYL)), 32767.f); - } - sobelL[y][bfw - 1] = 0.f; - } - for (int x = 0; x < bfw; x++) { - sobelL[bfh - 1][x] = 0.f; - } -} - -} - -namespace rtengine - -{ -extern MyMutex *fftwMutex; - -using namespace procparams; - -struct local_params { - float yc, xc; - float lx, ly; - float lxL, lyT; - float transweak; - float transgrad; - float iterat; - float balance; - float balanceh; - int colorde; - int cir; - float thr; - float stru; - int chro, cont, sens, sensh, senscb, sensbn, senstm, sensex, sensexclu, sensden, senslc, senssf, senshs, senscolor; - float clarityml; - float contresid; - float blurcbdl; - bool deltaem; - float struco; - float strengrid; - float struexc; - float blendmacol; - float radmacol; - float chromacol; - float gammacol; - float slomacol; - float blendmalc; - float radmalc; - float chromalc; - float radmaexp; - float chromaexp; - float gammaexp; - float slomaexp; - float strmaexp; - float angmaexp; - float str_mas; - float ang_mas; - float strexp; - float angexp; - float strSH; - float angSH; - float strcol; - float strcolab; - float strcolh; - float angcol; - float strvib; - float strvibab; - float strvibh; - float angvib; - float angwav; - float strwav; - - float strengthw; - float radiusw; - float detailw; - float gradw; - float tloww; - float thigw; - float edgw; - float basew; - - float anglog; - float strlog; - float softradiusexp; - float softradiuscol; - float softradiuscb; - float softradiusret; - float softradiustm; - float blendmaexp; - float radmaSH; - float blendmaSH; - float chromaSH; - float gammaSH; - float slomaSH; - float radmavib; - float blendmavib; - float chromavib; - float gammavib; - float slomavib; - float radmacb; - float blendmacb; - float chromacbm; - float gammacb; - float slomacb; - float radmatm; - float blendmatm; - float chromatm; - float gammatm; - float slomatm; - - float radmabl; - float blendmabl; - float chromabl; - float gammabl; - float slomabl; - - float struexp; - float blurexp; - float blurcol; - float blurcolmask; - float contcolmask; - float blurSH; - float ligh; - float lowA, lowB, highA, highB; - float lowBmerg, highBmerg, lowAmerg, highAmerg; - int shamo, shdamp, shiter, senssha, sensv; - float neig; - float strng; - float lap; - float lcamount; - double shrad; - double shblurr; - double rad; - double stren; - int it; - int guidb; - float strbl; - float epsb; - float trans; - float feath; - int dehaze; - int depth; - bool inv; - bool invex; - bool invsh; - bool curvact; - bool invrad; - bool invret; - bool equret; - bool equtm; - bool invshar; - bool actsp; - bool ftwlc; - bool ftwreti; - float str; - int qualmet; - int qualcurvemet; - int gridmet; - bool prevdE; - int showmaskcolmet; - int showmaskcolmetinv; - int showmaskexpmet; - int showmaskexpmetinv; - int showmaskSHmet; - int showmaskSHmetinv; - int showmaskvibmet; - int showmasklcmet; - int showmasksharmet; - int showmaskcbmet; - int showmaskretimet; - int showmasksoftmet; - int showmasktmmet; - int showmaskblmet; - int showmask_met; - bool fftbl; - float laplacexp; - float balanexp; - float linear; - int expmet; - int softmet; - int blurmet; - int blmet; - int smasktyp; - int chromet; - int shmeth; - int medmet; - int locmet; - float noiself; - float noiself0; - float noiself2; - float noiseldetail; - int detailthr; - int noiselequal; - float noisechrodetail; - float bilat; - float noiselc; - float noisecf; - float noisecc; - float mulloc[6]; - int mullocsh[5]; - int detailsh; - float threshol; - float chromacb; - float strengt; - float gamm; - float esto; - float scalt; - float rewe; - float amo; - bool colorena; - bool blurena; - bool tonemapena; - bool retiena; - bool sharpena; - bool lcena; - bool sfena; - bool cbdlena; - bool denoiena; - bool expvib; - bool exposena; - bool hsena; - bool vibena; - bool logena; - bool maskena; - bool cut_past; - float past; - float satur; - int blac; - int shcomp; - int shadex; - int hlcomp; - int hlcompthr; - float expcomp; - float expchroma; - int excmet; - int mergemet; - int mergecolMethod; - float opacol; - int war; - float adjch; - int shapmet; - int edgwmet; - int neiwmet; - bool enaColorMask; - bool fftColorMask; - bool enaColorMaskinv; - bool enaExpMask; - bool enaExpMaskinv; - bool enaSHMask; - bool enaSHMaskinv; - bool enavibMask; - bool enalcMask; - bool enasharMask; - bool enacbMask; - bool enaretiMask; - bool enaretiMasktmap; - bool enatmMask; - bool enablMask; - bool ena_Mask; - int highlihs; - int shadowhs; - int radiushs; - int hltonalhs; - int shtonalhs; - int scalereti; - float sourcegray; - float targetgray; - float blackev; - float whiteev; - float detail; - int sensilog; - int sensimas; - bool Autogray; - bool autocompute; - float baselog; - bool wavgradl; - bool edgwena; - bool lip3; - int daubLen; - float sigmadr; - float sigmabl; - float sigmaed; - float sigmalc; - float sigmalc2; - float residsha; - float residshathr; - float residhi; - float residhithr; - bool blwh; - bool fftma; - float blurma; - float contma; - -}; - -static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locallab, struct local_params& lp, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, const LocwavCurve & locwavCurveden, bool locwavdenutili) -{ - int w = oW; - int h = oH; - int circr = locallab.spots.at(sp).circrad; - float streng = ((float)locallab.spots.at(sp).stren); - float gam = ((float)locallab.spots.at(sp).gamma); - float est = ((float)locallab.spots.at(sp).estop); - float scal_tm = ((float)locallab.spots.at(sp).scaltm); - float rewe = ((float)locallab.spots.at(sp).rewei); - float amo = ((float)locallab.spots.at(sp).amount); - float strlight = ((float)locallab.spots.at(sp).streng); - float strucc = locallab.spots.at(sp).struc; - float laplac = ((float)locallab.spots.at(sp).laplace); - float thre = locallab.spots.at(sp).thresh; - - if (thre > 8.f || thre < 0.f) {//to avoid artifacts if user does not clear cache with new settings. Can be suppressed after - thre = 2.f; - } - - double local_x = locallab.spots.at(sp).loc.at(0) / 2000.0; - double local_y = locallab.spots.at(sp).loc.at(2) / 2000.0; - double local_xL = locallab.spots.at(sp).loc.at(1) / 2000.0; - double local_yT = locallab.spots.at(sp).loc.at(3) / 2000.0; - double local_center_x = locallab.spots.at(sp).centerX / 2000.0 + 0.5; - double local_center_y = locallab.spots.at(sp).centerY / 2000.0 + 0.5; - float iterati = (float) locallab.spots.at(sp).iter; - float balanc = (float) locallab.spots.at(sp).balan; - float balanch = (float) locallab.spots.at(sp).balanh; - int colorde = (int) locallab.spots.at(sp).colorde; - - if (iterati > 4.f || iterati < 0.2f) {//to avoid artifacts if user does not clear cache with new settings Can be suppressed after - iterati = 2.f; - } - - float neigh = float (locallab.spots.at(sp).neigh); - float chromaPastel = float (locallab.spots.at(sp).pastels) / 100.0f; - float chromaSatur = float (locallab.spots.at(sp).saturated) / 100.0f; - int local_sensiv = locallab.spots.at(sp).sensiv; - int local_sensiex = locallab.spots.at(sp).sensiex; - - if (locallab.spots.at(sp).qualityMethod == "enh") { - lp.qualmet = 1; - } else if (locallab.spots.at(sp).qualityMethod == "enhden") { - lp.qualmet = 2; - } - - if (locallab.spots.at(sp).qualitycurveMethod == "none") { - lp.qualcurvemet = 0; - } else if (locallab.spots.at(sp).qualitycurveMethod == "std") { - lp.qualcurvemet = 1; - } - - if (locallab.spots.at(sp).gridMethod == "one") { - lp.gridmet = 0; - } else if (locallab.spots.at(sp).gridMethod == "two") { - lp.gridmet = 1; - } - - if (locallab.spots.at(sp).expMethod == "std") { - lp.expmet = 0; - } else if (locallab.spots.at(sp).expMethod == "pde") { - lp.expmet = 1; - } - - if (locallab.spots.at(sp).localcontMethod == "loc") { - lp.locmet = 0; - } else if (locallab.spots.at(sp).localcontMethod == "wav") { - lp.locmet = 1; - } - - lp.laplacexp = locallab.spots.at(sp).laplacexp; - lp.balanexp = locallab.spots.at(sp).balanexp; - lp.linear = locallab.spots.at(sp).linear; - - lp.fftColorMask = locallab.spots.at(sp).fftColorMask; - lp.prevdE = prevDeltaE; - lp.showmaskcolmet = llColorMask; - lp.showmaskcolmetinv = llColorMaskinv; - lp.showmaskexpmet = llExpMask; - lp.showmaskexpmetinv = llExpMaskinv; - lp.showmaskSHmet = llSHMask; - lp.showmaskSHmetinv = llSHMaskinv; - lp.showmaskvibmet = llvibMask; - lp.showmasklcmet = lllcMask; - lp.showmasksharmet = llsharMask; - lp.showmaskcbmet = llcbMask; - lp.showmaskretimet = llretiMask; - lp.showmasksoftmet = llsoftMask; - - lp.showmasktmmet = lltmMask; - lp.showmaskblmet = llblMask; - lp.showmask_met = ll_Mask; - // printf("mask=%i \n", lp.showmask_met); - - lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaExpMask = locallab.spots.at(sp).enaExpMask && llExpMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaExpMaskinv = locallab.spots.at(sp).enaExpMask && llExpMaskinv == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0;// Exposure mask is deactivated if Color & Light mask is visible - lp.enaSHMask = locallab.spots.at(sp).enaSHMask && llSHMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enaSHMaskinv = locallab.spots.at(sp).enaSHMask && llSHMaskinv == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enacbMask = locallab.spots.at(sp).enacbMask && llcbMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enaretiMask = locallab.spots.at(sp).enaretiMask && lllcMask == 0 && llsharMask == 0 && llsoftMask == 0 && llretiMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enatmMask = locallab.spots.at(sp).enatmMask && lltmMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enablMask = locallab.spots.at(sp).enablMask && llblMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.enavibMask = locallab.spots.at(sp).enavibMask && llvibMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llSHMask == 0 && ll_Mask == 0; - lp.enalcMask = locallab.spots.at(sp).enalcMask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0 ; - lp.enasharMask = lllcMask == 0 && llcbMask == 0 && llsharMask == 0 && llsoftMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.ena_Mask = locallab.spots.at(sp).enamask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; - - // printf("llColorMask=%i lllcMask=%i llExpMask=%i llSHMask=%i llcbMask=%i llretiMask=%i lltmMask=%i llblMask=%i llvibMask=%i\n", llColorMask, lllcMask, llExpMask, llSHMask, llcbMask, llretiMask, lltmMask, llblMask, llvibMask); - if (locallab.spots.at(sp).softMethod == "soft") { - lp.softmet = 0; - } else if (locallab.spots.at(sp).softMethod == "reti") { - lp.softmet = 1; - } - - if (locallab.spots.at(sp).blMethod == "blur") { - lp.blmet = 0; - } else if (locallab.spots.at(sp).blMethod == "med") { - lp.blmet = 1; - } else if (locallab.spots.at(sp).blMethod == "guid") { - lp.blmet = 2; - } - - if (locallab.spots.at(sp).chroMethod == "lum") { - lp.chromet = 0; - } else if (locallab.spots.at(sp).chroMethod == "chr") { - lp.chromet = 1; - } else if (locallab.spots.at(sp).chroMethod == "all") { - lp.chromet = 2; - } - - if (locallab.spots.at(sp).shMethod == "std") { - lp.shmeth = 0; - } else if (locallab.spots.at(sp).shMethod == "tone") { - lp.shmeth = 1; - } - - - if (locallab.spots.at(sp).medMethod == "none") { - lp.medmet = -1; - } else if (locallab.spots.at(sp).medMethod == "33") { - lp.medmet = 0; - } else if (locallab.spots.at(sp).medMethod == "55") { - lp.medmet = 1; - } else if (locallab.spots.at(sp).medMethod == "77") { - lp.medmet = 2; - } else if (locallab.spots.at(sp).medMethod == "99") { - lp.medmet = 3; - } - - if (locallab.spots.at(sp).blurMethod == "norm") { - lp.blurmet = 0; - } else if (locallab.spots.at(sp).blurMethod == "inv") { - lp.blurmet = 1; - } - - if (locallab.spots.at(sp).showmaskblMethodtyp == "blur") { - lp.smasktyp = 0; - } else if (locallab.spots.at(sp).showmaskblMethodtyp == "nois") { - lp.smasktyp = 1; - } else if (locallab.spots.at(sp).showmaskblMethodtyp == "all") { - lp.smasktyp = 2; - } - - - if (locallab.spots.at(sp).spotMethod == "norm") { - lp.excmet = 0; - } else if (locallab.spots.at(sp).spotMethod == "exc") { - lp.excmet = 1; - } - - if (locallab.spots.at(sp).merMethod == "mone") { - lp.mergemet = 0; - } else if (locallab.spots.at(sp).merMethod == "mtwo") { - lp.mergemet = 1; - } else if (locallab.spots.at(sp).merMethod == "mthr") { - lp.mergemet = 2; - } else if (locallab.spots.at(sp).merMethod == "mfou") { - lp.mergemet = 3; - } else if (locallab.spots.at(sp).merMethod == "mfiv") { - lp.mergemet = 4; - } - - if (locallab.spots.at(sp).mergecolMethod == "one") { - lp.mergecolMethod = 0; - } else if (locallab.spots.at(sp).mergecolMethod == "two") { - lp.mergecolMethod = 1; - } else if (locallab.spots.at(sp).mergecolMethod == "thr") { - lp.mergecolMethod = 2; - } else if (locallab.spots.at(sp).mergecolMethod == "fou") { - lp.mergecolMethod = 3; - } else if (locallab.spots.at(sp).mergecolMethod == "fiv") { - lp.mergecolMethod = 4; - } else if (locallab.spots.at(sp).mergecolMethod == "six") { - lp.mergecolMethod = 5; - } else if (locallab.spots.at(sp).mergecolMethod == "sev") { - lp.mergecolMethod = 6; - } else if (locallab.spots.at(sp).mergecolMethod == "sev0") { - lp.mergecolMethod = 7; - } else if (locallab.spots.at(sp).mergecolMethod == "sev1") { - lp.mergecolMethod = 8; - } else if (locallab.spots.at(sp).mergecolMethod == "sev2") { - lp.mergecolMethod = 9; - } else if (locallab.spots.at(sp).mergecolMethod == "hei") { - lp.mergecolMethod = 10; - } else if (locallab.spots.at(sp).mergecolMethod == "nin") { - lp.mergecolMethod = 11; - } else if (locallab.spots.at(sp).mergecolMethod == "ten") { - lp.mergecolMethod = 12; - } else if (locallab.spots.at(sp).mergecolMethod == "ele") { - lp.mergecolMethod = 13; - } else if (locallab.spots.at(sp).mergecolMethod == "twe") { - lp.mergecolMethod = 14; - } else if (locallab.spots.at(sp).mergecolMethod == "thi") { - lp.mergecolMethod = 15; - } else if (locallab.spots.at(sp).mergecolMethod == "for") { - lp.mergecolMethod = 16; - } else if (locallab.spots.at(sp).mergecolMethod == "hue") { - lp.mergecolMethod = 17; - } else if (locallab.spots.at(sp).mergecolMethod == "sat") { - lp.mergecolMethod = 18; - } else if (locallab.spots.at(sp).mergecolMethod == "col") { - lp.mergecolMethod = 19; - } else if (locallab.spots.at(sp).mergecolMethod == "lum") { - lp.mergecolMethod = 20; - } - - if (locallab.spots.at(sp).localedgMethod == "fir") { - lp.edgwmet = 0; - } else if (locallab.spots.at(sp).localedgMethod == "sec") { - lp.edgwmet = 1; - } else if (locallab.spots.at(sp).localedgMethod == "thr") { - lp.edgwmet = 2; - } - - if (locallab.spots.at(sp).localneiMethod == "none") { - lp.neiwmet = -1; - lp.lip3 = false; - } else if (locallab.spots.at(sp).localneiMethod == "low") { - lp.neiwmet = 0; - lp.lip3 = true; - } else if (locallab.spots.at(sp).localneiMethod == "high") { - lp.lip3 = true; - lp.neiwmet = 1; - } - - - if (locallab.spots.at(sp).wavMethod == "D2") { - lp.daubLen = 4; - } else if (locallab.spots.at(sp).wavMethod == "D4") { - lp.daubLen = 6; - } else if (locallab.spots.at(sp).wavMethod == "D6") { - lp.daubLen = 8; - } else if (locallab.spots.at(sp).wavMethod == "D10") { - lp.daubLen = 12; - } else if (locallab.spots.at(sp).wavMethod == "D14") { - lp.daubLen = 16; - } - - - lp.edgwena = locallab.spots.at(sp).wavedg; - - lp.opacol = 0.01 * locallab.spots.at(sp).opacol; - - if (locallab.spots.at(sp).shape == "ELI") { - lp.shapmet = 0; - } else /*if (locallab.spots.at(sp).shape == "RECT")*/ { - lp.shapmet = 1; - } - - lp.denoiena = locallab.spots.at(sp).expblur; - - bool wavcurveden = false; - float local_noiself = 0.f; - float local_noiself0 = 0.f; - float local_noiself2 = 0.f; - float local_noiselc = 0.f; - - if (locwavCurveden && locwavdenutili) { - if (lp.denoiena) { - for (int i = 0; i < 500; i++) { - if (locwavCurveden[i] != 0.f) { - wavcurveden = true; - } - } - } - } - - if (wavcurveden) { - if (lp.denoiena) { - local_noiself0 = 250.f * locwavCurveden[0]; - local_noiself = 250.f * locwavCurveden[166]; - local_noiself2 = 250.f * locwavCurveden[323]; - local_noiselc = 200.f * locwavCurveden[500]; - } - } - - float local_noiseldetail = (float)locallab.spots.at(sp).noiselumdetail; - int local_noiselequal = locallab.spots.at(sp).noiselequal; - float local_noisechrodetail = (float)locallab.spots.at(sp).noisechrodetail; - int local_sensiden = locallab.spots.at(sp).sensiden; - float local_detailthr = (float)locallab.spots.at(sp).detailthr; - - float local_noisecf = ((float)locallab.spots.at(sp).noisechrof) / 10.f; - float local_noisecc = ((float)locallab.spots.at(sp).noisechroc) / 10.f; - float multi[6]; - - for (int y = 0; y < 6; y++) { - multi[y] = ((float) locallab.spots.at(sp).mult[y]); - } - - float multish[5]; - - for (int y = 0; y < 5; y++) { - multish[y] = ((float) locallab.spots.at(sp).multsh[y]); - } - - float thresho = ((float)locallab.spots.at(sp).threshold); - float chromcbdl = (float)locallab.spots.at(sp).chromacbdl; - - int local_chroma = locallab.spots.at(sp).chroma; - int local_sensi = locallab.spots.at(sp).sensi; - int local_sensibn = locallab.spots.at(sp).sensibn; - int local_sensitm = locallab.spots.at(sp).sensitm; - int local_sensiexclu = locallab.spots.at(sp).sensiexclu; - float structexclude = (float) locallab.spots.at(sp).structexclu; - int local_sensilc = locallab.spots.at(sp).sensilc; - int local_warm = locallab.spots.at(sp).warm; - int local_sensih = locallab.spots.at(sp).sensih; - int local_dehaze = locallab.spots.at(sp).dehaz; - int local_depth = locallab.spots.at(sp).depth; - int local_sensicb = locallab.spots.at(sp).sensicb; - float local_clarityml = (float) locallab.spots.at(sp).clarityml; - float local_contresid = (float) locallab.spots.at(sp).contresid; - int local_blurcbdl = 0; //(float) locallab.spots.at(sp).blurcbdl; - int local_contrast = locallab.spots.at(sp).contrast; - float local_lightness = (float) locallab.spots.at(sp).lightness; - float labgridALowloc = locallab.spots.at(sp).labgridALow; - float labgridBLowloc = locallab.spots.at(sp).labgridBLow; - float labgridBHighloc = locallab.spots.at(sp).labgridBHigh; - float labgridAHighloc = locallab.spots.at(sp).labgridAHigh; - float strengthgrid = (float) locallab.spots.at(sp).strengthgrid; - float labgridBLowlocmerg = locallab.spots.at(sp).labgridBLowmerg; - float labgridBHighlocmerg = locallab.spots.at(sp).labgridBHighmerg; - float labgridALowlocmerg = locallab.spots.at(sp).labgridALowmerg; - float labgridAHighlocmerg = locallab.spots.at(sp).labgridAHighmerg; - - float blendmasklc = ((float) locallab.spots.at(sp).blendmasklc) / 100.f ; - float radmasklc = ((float) locallab.spots.at(sp).radmasklc); - float chromasklc = ((float) locallab.spots.at(sp).chromasklc); - float structcolor = (float) locallab.spots.at(sp).structcol; - float blendmaskcolor = ((float) locallab.spots.at(sp).blendmaskcol) / 100.f ; - float radmaskcolor = ((float) locallab.spots.at(sp).radmaskcol); - float chromaskcolor = ((float) locallab.spots.at(sp).chromaskcol); - float gammaskcolor = ((float) locallab.spots.at(sp).gammaskcol); - float slomaskcolor = ((float) locallab.spots.at(sp).slomaskcol); - float blendmaskexpo = ((float) locallab.spots.at(sp).blendmaskexp) / 100.f ; - float radmaskexpo = ((float) locallab.spots.at(sp).radmaskexp); - float chromaskexpo = ((float) locallab.spots.at(sp).chromaskexp); - float gammaskexpo = ((float) locallab.spots.at(sp).gammaskexp); - float slomaskexpo = ((float) locallab.spots.at(sp).slomaskexp); - float strmaskexpo = ((float) locallab.spots.at(sp).strmaskexp); - float angmaskexpo = ((float) locallab.spots.at(sp).angmaskexp); - float strmask = ((float) locallab.spots.at(sp).str_mask); - float angmask = ((float) locallab.spots.at(sp).ang_mask); - float strexpo = ((float) locallab.spots.at(sp).strexp); - float angexpo = ((float) locallab.spots.at(sp).angexp); - float strSH = ((float) locallab.spots.at(sp).strSH); - float angSH = ((float) locallab.spots.at(sp).angSH); - float strcol = ((float) locallab.spots.at(sp).strcol); - float strcolab = ((float) locallab.spots.at(sp).strcolab); - float strcolh = ((float) locallab.spots.at(sp).strcolh); - float angcol = ((float) locallab.spots.at(sp).angcol); - float strvib = ((float) locallab.spots.at(sp).strvib); - float strvibab = ((float) locallab.spots.at(sp).strvibab); - float strvibh = ((float) locallab.spots.at(sp).strvibh); - float angvib = ((float) locallab.spots.at(sp).angvib); - float strwav = ((float) locallab.spots.at(sp).strwav); - float angwav = ((float) locallab.spots.at(sp).angwav); - float strlog = ((float) locallab.spots.at(sp).strlog); - float anglog = ((float) locallab.spots.at(sp).anglog); - float softradiusexpo = ((float) locallab.spots.at(sp).softradiusexp); - float softradiuscolor = ((float) locallab.spots.at(sp).softradiuscol); - float softradiusreti = ((float) locallab.spots.at(sp).softradiusret); - float softradiustma = ((float) locallab.spots.at(sp).softradiustm); - float softradiuscbdl = ((float) locallab.spots.at(sp).softradiuscb); - float blendmaskSH = ((float) locallab.spots.at(sp).blendmaskSH) / 100.f ; - float radmaskSH = ((float) locallab.spots.at(sp).radmaskSH); - float chromaskSH = ((float) locallab.spots.at(sp).chromaskSH); - float gammaskSH = ((float) locallab.spots.at(sp).gammaskSH); - float slomaskSH = ((float) locallab.spots.at(sp).slomaskSH); - float blendmaskvib = ((float) locallab.spots.at(sp).blendmaskvib) / 100.f ; - float radmaskvib = ((float) locallab.spots.at(sp).radmaskvib); - float chromaskvib = ((float) locallab.spots.at(sp).chromaskvib); - float gammaskvib = ((float) locallab.spots.at(sp).gammaskvib); - float slomaskvib = ((float) locallab.spots.at(sp).slomaskvib); - float structexpo = (float) locallab.spots.at(sp).structexp; - float blurexpo = (float) locallab.spots.at(sp).blurexpde; - float blurcolor = (float) locallab.spots.at(sp).blurcolde; - float blurcolmask = (float) locallab.spots.at(sp).blurcol; - float contcolmask = (float) locallab.spots.at(sp).contcol; - float blurSH = (float) locallab.spots.at(sp).blurSHde; - float local_transit = locallab.spots.at(sp).transit; - float local_feather = locallab.spots.at(sp).feather; - float local_transitweak = (float)locallab.spots.at(sp).transitweak; - float local_transitgrad = (float)locallab.spots.at(sp).transitgrad; - float radius = (float) locallab.spots.at(sp).radius; - int itera = locallab.spots.at(sp).itera; - int guidbl = locallab.spots.at(sp).guidbl; - float epsbl = (float) locallab.spots.at(sp).epsbl; - float sharradius = LIM(locallab.spots.at(sp).sharradius, 0.42, 3.5); - float lcamount = ((float) locallab.spots.at(sp).lcamount); - lcamount = LIM01(lcamount); //to prevent crash with old pp3 integer - float sharblurr = LIM(locallab.spots.at(sp).sharblur, 0.2, 3.); //to prevent crash with old pp3 integer - int local_sensisha = locallab.spots.at(sp).sensisha; - int local_sharamount = locallab.spots.at(sp).sharamount; - int local_shardamping = locallab.spots.at(sp).shardamping; - int local_shariter = locallab.spots.at(sp).shariter; - bool inverse = locallab.spots.at(sp).invers; - bool curvacti = locallab.spots.at(sp).curvactiv; - bool acti = locallab.spots.at(sp).activlum; - bool cupas = false; // Provision - int local_sensisf = locallab.spots.at(sp).sensisf; - bool inverseex = locallab.spots.at(sp).inversex; - bool inversesh = locallab.spots.at(sp).inverssh; - bool equiltm = locallab.spots.at(sp).equiltm; - bool fftwlc = locallab.spots.at(sp).fftwlc; - bool fftwreti = locallab.spots.at(sp).fftwreti; - - bool equilret = locallab.spots.at(sp).equilret; - bool inverserad = false; // Provision - bool inverseret = locallab.spots.at(sp).inversret; - bool inversesha = locallab.spots.at(sp).inverssha; - double strength = (double) locallab.spots.at(sp).strength; - float str = (float)locallab.spots.at(sp).str; - int scaleret = (float)locallab.spots.at(sp).scalereti; - - int local_sensihs = locallab.spots.at(sp).sensihs; - int highhs = locallab.spots.at(sp).highlights; - int hltonahs = locallab.spots.at(sp).h_tonalwidth; - int shadhs = locallab.spots.at(sp).shadows; - int shtonals = locallab.spots.at(sp).s_tonalwidth; - int radhs = locallab.spots.at(sp).sh_radius; - float blendmaskcb = ((float) locallab.spots.at(sp).blendmaskcb) / 100.f ; - float radmaskcb = ((float) locallab.spots.at(sp).radmaskcb); - float chromaskcb = ((float) locallab.spots.at(sp).chromaskcb); - float gammaskcb = ((float) locallab.spots.at(sp).gammaskcb); - float slomaskcb = ((float) locallab.spots.at(sp).slomaskcb); - bool enaretiMasktm = locallab.spots.at(sp).enaretiMasktmap; - lp.enaretiMasktmap = enaretiMasktm; - float blendmasktm = ((float) locallab.spots.at(sp).blendmasktm) / 100.f ; - float radmasktm = ((float) locallab.spots.at(sp).radmasktm); - float chromasktm = ((float) locallab.spots.at(sp).chromasktm); - float gammasktm = ((float) locallab.spots.at(sp).gammasktm); - float slomasktm = ((float) locallab.spots.at(sp).slomasktm); - bool wavgradl = locallab.spots.at(sp).wavgradl; - - float blendmaskbl = ((float) locallab.spots.at(sp).blendmaskbl) / 100.f ; - float radmaskbl = ((float) locallab.spots.at(sp).radmaskbl); - float chromaskbl = ((float) locallab.spots.at(sp).chromaskbl); - float gammaskbl = ((float) locallab.spots.at(sp).gammaskbl); - float slomaskbl = ((float) locallab.spots.at(sp).slomaskbl); - bool fftbl = locallab.spots.at(sp).fftwbl; - - - lp.sourcegray = (float) locallab.spots.at(sp).sourceGray; - lp.targetgray = (float) locallab.spots.at(sp).targetGray; - lp.blackev = (float) locallab.spots.at(sp).blackEv; - lp.whiteev = (float) locallab.spots.at(sp).whiteEv; - lp.detail = locallab.spots.at(sp).detail; - lp.sensilog = locallab.spots.at(sp).sensilog; - lp.Autogray = locallab.spots.at(sp).Autogray; - lp.autocompute = locallab.spots.at(sp).autocompute; - lp.baselog = (float) locallab.spots.at(sp).baselog; - lp.sensimas = locallab.spots.at(sp).sensimask; - - lp.deltaem = locallab.spots.at(sp).deltae; - lp.scalereti = scaleret; - lp.cir = circr; - lp.actsp = acti; - lp.xc = w * local_center_x; - lp.yc = h * local_center_y; - lp.lx = w * local_x; - lp.ly = h * local_y; - lp.lxL = w * local_xL; - lp.lyT = h * local_yT; - lp.chro = local_chroma; - lp.struco = structcolor; - lp.strengrid = strengthgrid; - lp.blendmalc = blendmasklc; - lp.radmalc = radmasklc; - lp.chromalc = chromasklc; - lp.blendmacol = blendmaskcolor; - lp.radmacol = radmaskcolor; - lp.chromacol = chromaskcolor; - lp.gammacol = gammaskcolor; - lp.slomacol = slomaskcolor; - lp.radmaexp = radmaskexpo; - lp.chromaexp = chromaskexpo; - lp.gammaexp = gammaskexpo; - lp.slomaexp = slomaskexpo; - lp.strmaexp = strmaskexpo; - lp.angmaexp = angmaskexpo; - lp.str_mas = strmask; - lp.ang_mas = angmask; - - lp.strexp = strexpo; - lp.angexp = angexpo; - lp.strSH = strSH; - lp.angSH = angSH; - lp.strcol = strcol; - lp.strcolab = strcolab; - lp.strcolh = strcolh; - lp.angcol = angcol; - lp.strvib = strvib; - lp.strvibab = strvibab; - lp.strvibh = strvibh; - lp.angvib = angvib; - lp.strwav = strwav; - lp.angwav = angwav; - lp.strlog = strlog; - lp.anglog = anglog; - lp.softradiusexp = softradiusexpo; - lp.softradiuscol = softradiuscolor; - lp.softradiusret = softradiusreti; - lp.softradiuscb = softradiuscbdl; - lp.softradiustm = softradiustma; - lp.struexc = structexclude; - lp.blendmaexp = blendmaskexpo; - lp.blendmaSH = blendmaskSH; - lp.radmaSH = radmaskSH; - lp.chromaSH = chromaskSH; - lp.gammaSH = gammaskSH; - lp.slomaSH = slomaskSH; - lp.blendmavib = blendmaskvib; - lp.radmavib = radmaskvib; - lp.chromavib = chromaskvib; - lp.gammavib = gammaskvib; - lp.slomavib = slomaskvib; - lp.blendmacb = blendmaskcb; - lp.radmacb = radmaskcb; - lp.chromacbm = chromaskcb; - lp.gammacb = gammaskcb; - lp.slomacb = slomaskcb; - lp.blendmatm = blendmasktm; - lp.radmatm = radmasktm; - lp.chromatm = chromasktm; - lp.gammatm = gammasktm; - lp.slomatm = slomasktm; - lp.wavgradl = wavgradl; - - lp.strengthw = ((float) locallab.spots.at(sp).strengthw); - lp.radiusw = ((float) locallab.spots.at(sp).radiusw); - lp.detailw = ((float) locallab.spots.at(sp).detailw); - lp.gradw = ((float) locallab.spots.at(sp).gradw); - lp.tloww = ((float) locallab.spots.at(sp).tloww); - lp.thigw = ((float) locallab.spots.at(sp).thigw); - lp.edgw = ((float) locallab.spots.at(sp).edgw); - lp.basew = ((float) locallab.spots.at(sp).basew); - - lp.blendmabl = blendmaskbl; - lp.radmabl = radmaskbl; - lp.chromabl = chromaskbl; - lp.gammabl = gammaskbl; - lp.slomabl = slomaskbl; - lp.fftbl = fftbl; - lp.it = itera; - lp.guidb = guidbl; - lp.strbl = 0.01f * (float) locallab.spots.at(sp).strbl; - - lp.epsb = epsbl; - lp.struexp = structexpo; - lp.blurexp = blurexpo; - lp.blurcol = blurcolor; - lp.blurcolmask = blurcolmask; - lp.contcolmask = 0.01f * contcolmask; - lp.blurSH = blurSH; - lp.sens = local_sensi; - lp.sensh = local_sensih; - lp.dehaze = local_dehaze; - lp.depth = local_depth; - lp.senscb = local_sensicb; - lp.clarityml = local_clarityml; - lp.contresid = local_contresid; - lp.blurcbdl = local_blurcbdl; - lp.cont = local_contrast; - lp.ligh = local_lightness; - lp.lowA = labgridALowloc; - lp.lowB = labgridBLowloc; - lp.highB = labgridBHighloc; - lp.highA = labgridAHighloc; - lp.lowBmerg = labgridBLowlocmerg; - lp.highBmerg = labgridBHighlocmerg; - lp.lowAmerg = labgridALowlocmerg; - lp.highAmerg = labgridAHighlocmerg; - - lp.senssf = local_sensisf; - lp.strng = strlight; - lp.neig = neigh; - lp.lap = laplac; - - if (lp.ligh >= -2.f && lp.ligh <= 2.f) { - lp.ligh /= 5.f; - } - - lp.trans = local_transit; - lp.feath = local_feather; - lp.transweak = local_transitweak; - lp.transgrad = local_transitgrad; - lp.rad = radius; - lp.stren = strength; - lp.sensbn = local_sensibn; - lp.sensexclu = local_sensiexclu; - lp.senslc = local_sensilc; - lp.lcamount = lcamount; - lp.inv = inverse; - lp.invex = inverseex; - lp.invsh = inversesh; - lp.curvact = curvacti; - lp.invrad = inverserad; - lp.invret = inverseret; - lp.equret = equilret; - lp.equtm = equiltm; - lp.invshar = inversesha; - lp.str = str; - lp.shrad = sharradius; - lp.shblurr = sharblurr; - lp.senssha = local_sensisha; - lp.shamo = local_sharamount; - lp.shdamp = local_shardamping; - lp.shiter = local_shariter; - lp.iterat = iterati; - lp.balance = balanc; - lp.balanceh = balanch; - lp.colorde = colorde; - lp.thr = thre; - lp.stru = strucc; - lp.noiself = local_noiself; - lp.noiself0 = local_noiself0; - lp.noiself2 = local_noiself2; - lp.noiseldetail = local_noiseldetail; - lp.detailthr = local_detailthr; - lp.noiselequal = local_noiselequal; - lp.noisechrodetail = local_noisechrodetail; - lp.noiselc = local_noiselc; - lp.noisecf = local_noisecf; - lp.noisecc = local_noisecc; - lp.sensden = local_sensiden; - lp.bilat = locallab.spots.at(sp).bilateral; - lp.adjch = (float) locallab.spots.at(sp).adjblur; - lp.strengt = streng; - lp.gamm = gam; - lp.esto = est; - lp.scalt = scal_tm; - lp.rewe = rewe; - lp.senstm = local_sensitm; - lp.amo = amo; - lp.blurma = (float) locallab.spots.at(sp).blurmask; - lp.fftma = locallab.spots.at(sp).fftmask; - lp.contma = (float) locallab.spots.at(sp).contmask; - - for (int y = 0; y < 6; y++) { - lp.mulloc[y] = LIM(multi[y], 0.f, 4.f);//to prevent crash with old pp3 integer - } - - for (int y = 0; y < 5; y++) { - lp.mullocsh[y] = multish[y]; - } - - lp.logena = locallab.spots.at(sp).explog; - - lp.detailsh = locallab.spots.at(sp).detailSH; - lp.threshol = thresho; - lp.chromacb = chromcbdl; - lp.expvib = locallab.spots.at(sp).expvibrance; - lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask - lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; - lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; - lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible - lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible - lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && ll_Mask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible - lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.maskena = locallab.spots.at(sp).expmask && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible - - lp.sensv = local_sensiv; - lp.past = chromaPastel; - lp.satur = chromaSatur; - - lp.cut_past = cupas; - lp.blac = locallab.spots.at(sp).black; - lp.shcomp = locallab.spots.at(sp).shcompr; - lp.shadex = locallab.spots.at(sp).shadex; - lp.hlcomp = locallab.spots.at(sp).hlcompr; - lp.hlcompthr = locallab.spots.at(sp).hlcomprthresh; - lp.expcomp = LIM(locallab.spots.at(sp).expcomp, -2.0, 4.0); //to prevent crash with Old pp3 with integer - //increase sensitivity for low values - float proexp = lp.expcomp; - if (std::fabs(proexp) < 0.6f) { - float interm = std::fabs(proexp) / 0.6f; - interm = SQR(interm); - lp.expcomp = proexp * interm; - } - lp.expchroma = locallab.spots.at(sp).expchroma / 100.; - lp.sensex = local_sensiex; - lp.war = local_warm; - lp.highlihs = highhs; - lp.shadowhs = shadhs; - lp.radiushs = radhs; - lp.hltonalhs = hltonahs; - lp.shtonalhs = shtonals; - lp.senshs = local_sensihs; - lp.ftwlc = fftwlc; - lp.ftwreti = fftwreti; - lp.sigmadr = locallab.spots.at(sp).sigmadr; - lp.sigmabl = locallab.spots.at(sp).sigmabl; - lp.sigmaed = locallab.spots.at(sp).sigmaed; - lp.sigmalc = locallab.spots.at(sp).sigmalc; - lp.sigmalc2 = locallab.spots.at(sp).sigmalc2; - lp.residsha = locallab.spots.at(sp).residsha; - lp.residshathr = locallab.spots.at(sp).residshathr; - lp.residhi = locallab.spots.at(sp).residhi; - lp.residhithr = locallab.spots.at(sp).residhithr; - lp.blwh = locallab.spots.at(sp).blwh; - lp.senscolor = (int) locallab.spots.at(sp).colorscope; - //replace scope color exposure vibrance shadows - lp.sens = lp.senscolor; - lp.sensv = lp.senscolor; - lp.senshs = lp.senscolor; - if(lp.expmet == 0){ - lp.sensex = lp.senscolor; - } -} - -static void calcTransitionrect(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) -{ - zone = 0; - - if (lox >= lp.xc && lox < lp.xc + lp.lx) { - if (loy >= lp.yc && loy < lp.yc + lp.ly) { - if (lox < lp.xc + lp.lx * ach && loy < lp.yc + lp.ly * ach) { - zone = 2; - } else { - zone = 1; - localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); - } - } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { - if (lox < lp.xc + lp.lx * ach && loy > lp.yc - lp.lyT * ach) { - zone = 2; - } else { - zone = 1; - localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); - } - } - } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { - if (loy <= lp.yc && loy > lp.yc - lp.lyT) { - if (lox > (lp.xc - lp.lxL * ach) && loy > (lp.yc - lp.lyT * ach)) { - zone = 2; - } else { - zone = 1; - localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); - } - } else if (loy > lp.yc && loy < lp.yc + lp.ly) { - if (lox > (lp.xc - lp.lxL * ach) && loy < (lp.yc + lp.ly * ach)) { - zone = 2; - } else { - zone = 1; - localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); - } - } - } -} - -static void calcTransition(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) -{ - // returns the zone (0 = outside selection, 1 = transition zone between outside and inside selection, 2 = inside selection) - // and a factor to calculate the transition in case zone == 1 - - zone = 0; - - if (lox >= lp.xc && lox < lp.xc + lp.lx) { - if (loy >= lp.yc && loy < lp.yc + lp.ly) { - const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.ly)); - zone = zoneVal < 1.f ? 2 : 0; - - if (!zone) { - zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; - if (zone == 1) { - localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); - } - } - } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { - const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.lyT)); - zone = zoneVal < 1.f ? 2 : 0; - - if (!zone) { - zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; - if (zone == 1) { - localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); - } - } - } - } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { - if (loy <= lp.yc && loy > lp.yc - lp.lyT) { - const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.lyT)); - zone = zoneVal < 1.f ? 2 : 0; - - if (!zone) { - zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; - if (zone == 1) { - localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); - } - } - } else if (loy > lp.yc && loy < lp.yc + lp.ly) { - const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.ly)); - zone = zoneVal < 1.f ? 2 : 0; - - if (!zone) { - zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; - if (zone == 1) { - localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); - } - } - } - } -} - -// Copyright 2018 Alberto Griggio -//J.Desmis 12 2019 - I will try to port a raw process in local adjustments -// I choose this one because, it is "new" -// Perhaps - probably no result, but perhaps ?? - -float find_gray(float source_gray, float target_gray) -{ - // find a base such that log2lin(base, source_gray) = target_gray - // log2lin is (base^source_gray - 1) / (base - 1), so we solve - // - // (base^source_gray - 1) / (base - 1) = target_gray, that is - // - // base^source_gray - 1 - base * target_gray + target_gray = 0 - // - // use a bisection method (maybe later change to Netwon) - - if (source_gray <= 0.f) { - return 0.f; - } - - const auto f = - [ = ](float x) -> float { - return std::pow(x, source_gray) - 1 - target_gray * x + target_gray; - }; - - // first find the interval we are interested in - - float lo = 1.f; - - while (f(lo) <= 0.f) { - lo *= 2.f; - } - - float hi = lo * 2.f; - - while (f(hi) >= 0.f) { - hi *= 2.f; - } - - if (std::isinf(hi)) { - return 0.f; - } - - // now search for a zero - for (int iter = 0; iter < 100; ++iter) { - float mid = lo + (hi - lo) / 2.f; - float v = f(mid); - - if (std::abs(v) < 1e-4f || (hi - lo) / lo <= 1e-4f) { - return mid; - } - - if (v > 0.f) { - lo = mid; - } else { - hi = mid; - } - } - - return 0.f; // not found -} - - -// basic log encoding taken from ACESutil.Lin_to_Log2, from -// https://github.com/ampas/aces-dev -// (as seen on pixls.us) -void ImProcFunctions::log_encode(Imagefloat *rgb, const struct local_params & lp, bool multiThread, int bfw, int bfh) -{ - /* J.Desmis 12 2019 - small adaptations to local adjustments - replace log2 by log(lp.baselog) allows diferentiation between low and high lights - */ - BENCHFUN - const float gray = lp.sourcegray / 100.f; - const float shadows_range = lp.blackev; - const float dynamic_range = lp.whiteev - lp.blackev; - const float noise = pow_F(2.f, -16.f); - const float log2 = xlogf(lp.baselog); - const float base = lp.targetgray > 1 && lp.targetgray < 100 && dynamic_range > 0 ? find_gray(std::abs(lp.blackev) / dynamic_range, lp.targetgray / 100.f) : 0.f; - const float linbase = rtengine::max(base, 0.f); - TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); - - const auto apply = - [ = ](float x, bool scale = true) -> float { - if (scale) - { - x /= 65535.f; - } - - x = rtengine::max(x, noise); - x = rtengine::max(x / gray, noise); - x = rtengine::max((xlogf(x) / log2 - shadows_range) / dynamic_range, noise); - assert(x == x); - - if (linbase > 0.f) - { - x = xlog2lin(x, linbase); - } - - if (scale) - { - return x * 65535.f; - } else { - return x; - } - }; - - const auto norm = - [&](float r, float g, float b) -> float { - return Color::rgbLuminance(r, g, b, ws); - - // other possible alternatives (so far, luminance seems to work - // fine though). See also - // https://discuss.pixls.us/t/finding-a-norm-to-preserve-ratios-across-non-linear-operations - // - // MAX - //return max(r, g, b); - // - // Euclidean - //return std::sqrt(SQR(r) + SQR(g) + SQR(b)); - - // weighted yellow power norm from https://youtu.be/Z0DS7cnAYPk - // float rr = 1.22f * r / 65535.f; - // float gg = 1.20f * g / 65535.f; - // float bb = 0.58f * b / 65535.f; - // float rr4 = SQR(rr) * SQR(rr); - // float gg4 = SQR(gg) * SQR(gg); - // float bb4 = SQR(bb) * SQR(bb); - // float den = (rr4 + gg4 + bb4); - // if (den > 0.f) { - // return 0.8374319f * ((rr4 * rr + gg4 * gg + bb4 * bb) / den) * 65535.f; - // } else { - // return 0.f; - // } - }; - - const float detail = lp.detail; - const int W = rgb->getWidth(), H = rgb->getHeight(); - - if (detail == 0.f) {//no local contrast -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - float r = rgb->r(y, x); - float g = rgb->g(y, x); - float b = rgb->b(y, x); - float m = norm(r, g, b); - - if (m > noise) { - float mm = apply(m); - float f = mm / m; - r *= f; - b *= f; - g *= f; - } - - assert(r == r); - assert(g == g); - assert(b == b); - - rgb->r(y, x) = r; - rgb->g(y, x) = g; - rgb->b(y, x) = b; - } - } - } else {//local contrast - - array2D Y(W, H); - { - constexpr float base_posterization = 20.f; - array2D Y2(W, H); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - Y2[y][x] = norm(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x)) / 65535.f; - float l = xlogf(rtengine::max(Y2[y][x], 1e-9f)); - float ll = round(l * base_posterization) / base_posterization; - Y[y][x] = xexpf(ll); - assert(std::isfinite(Y[y][x])); - } - } - const float radius = rtengine::max(rtengine::max(bfw, W), rtengine::max(bfh, H)) / 30.f; - const float epsilon = 0.005f; - rtengine::guidedFilter(Y2, Y, Y, radius, epsilon, multiThread); - } - const float blend = detail; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - float &r = rgb->r(y, x); - float &g = rgb->g(y, x); - float &b = rgb->b(y, x); - float t = Y[y][x]; - float t2; - - if (t > noise && (t2 = norm(r, g, b)) > noise) { - float c = apply(t, false); - float f = c / t; - // float t2 = norm(r, g, b); - float f2 = apply(t2) / t2; - f = intp(blend, f, f2); - assert(std::isfinite(f)); - r *= f; - g *= f; - b *= f; - assert(std::isfinite(r)); - assert(std::isfinite(g)); - assert(std::isfinite(b)); - } - } - } - } -} - -void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, float *blackev, float *whiteev, bool *Autogr, int fw, int fh, float xsta, float xend, float ysta, float yend, int SCALE) -{ - BENCHFUN -//adpatation to local adjustments Jacques Desmis 12 2019 - const PreviewProps pp(0, 0, fw, fh, SCALE); - - Imagefloat img(int(fw / SCALE + 0.5), int(fh / SCALE + 0.5)); - const ProcParams neutral; - imgsrc->getImage(imgsrc->getWB(), TR_NONE, &img, pp, params->toneCurve, neutral.raw); - imgsrc->convertColorSpace(&img, params->icm, imgsrc->getWB()); - float minVal = RT_INFINITY; - float maxVal = -RT_INFINITY; - const float ec = std::pow(2.f, params->toneCurve.expcomp); - - constexpr float noise = 1e-5; - const int h = fh / SCALE; - const int w = fw / SCALE; - - const int hsta = ysta * h; - const int hend = yend * h; - - const int wsta = xsta * w; - const int wend = xend * w; - - double mean = 0.0; - int nc = 0; - for (int y = hsta; y < hend; ++y) { - for (int x = wsta; x < wend; ++x) { - const float r = img.r(y, x), g = img.g(y, x), b = img.b(y, x); - mean += static_cast(0.2126f * Color::gamma_srgb(r) + 0.7152f * Color::gamma_srgb(g) + 0.0722f * Color::gamma_srgb(b)); - nc++; - - const float m = rtengine::max(0.f, r, g, b) / 65535.f * ec; - if (m > noise) { - const float l = rtengine::min(r, g, b) / 65535.f * ec; - minVal = rtengine::min(minVal, l > noise ? l : m); - maxVal = rtengine::max(maxVal, m); - } - } - } - - //approximation sourcegray yb source = 0.4 * yb - - if (maxVal > minVal) { - const float log2 = std::log(2.f); - const float dynamic_range = -xlogf(minVal / maxVal) / log2; - - if (settings->verbose) { - std::cout << "AutoLog: min = " << minVal << ", max = " << maxVal - << ", DR = " << dynamic_range << std::endl; - } - - if (Autogr[sp]) { - double tot = 0.0; - int n = 0; - const float gmax = rtengine::min(maxVal / 2.f, 0.25f); - const float gmin = rtengine::max(minVal * std::pow(2.f, rtengine::max((dynamic_range - 1.f) / 2.f, 1.f)), 0.05f); - - if (settings->verbose) { - std::cout << " gray boundaries: " << gmin << ", " << gmax << std::endl; - } - - for (int y = ysta; y < yend; ++y) { - for (int x = wsta; x < wend; ++x) { - const float l = img.g(y, x) / 65535.f; - - if (l >= gmin && l <= gmax) { - tot += static_cast(l); - ++n; - } - } - } - - if (n > 0) { - sourceg[sp] = tot / n * 100.0; - - if (settings->verbose) { - std::cout << " computed gray point from " << n << " samples: " << sourceg[sp] << std::endl; - } - } else { - mean /= (nc * 65535.0); - float yb; - - if (mean < 0.15) { - yb = 3.0f; - } else if (mean < 0.3) { - yb = 5.0f; - } else if (mean < 0.4) { - yb = 10.0f; - } else if (mean < 0.45) { - yb = 15.0f; - } else if (mean < 0.5) { - yb = 18.0f; - } else if (mean < 0.55) { - yb = 23.0f; - } else if (mean < 0.6) { - yb = 30.0f; - } else { - yb = 45.f; - } - sourceg[sp] = 0.4f * yb; - if (settings->verbose) { - std::cout << " no samples found in range, resorting to Yb gray point value " << sourceg[sp] << std::endl; - } - } - } - - const float gray = sourceg[sp] / 100.f; - whiteev[sp] = xlogf(maxVal / gray) / log2; - blackev[sp] = whiteev[sp] - dynamic_range; - } -} - -void tone_eq(array2D &R, array2D &G, array2D &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 - Small adaptation to Local Adjustment 10 2019 Jacques Desmis - 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 . -*/ - -{ - BENCHFUN - - const int W = R.width(); - const int H = R.height(); - array2D 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 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; - } - } - -} - - -void ImProcFunctions::ciecamloc_02float(int sp, LabImage* lab) -{ - //be careful quasi duplicate with branch cat02wb - BENCHFUN - - int width = lab->W, height = lab->H; - float Yw; - Yw = 1.0f; - double Xw, Zw; - float f = 0.f, nc = 0.f, la, c = 0.f, xw, yw, zw, f2 = 1.f, c2 = 1.f, nc2 = 1.f, yb2; - float fl, n, nbb, ncb, aw; //d - float xwd, ywd, zwd, xws, yws, zws; - // int alg = 0; - double Xwout, Zwout; - double Xwsc, Zwsc; - - int tempo; - - if (params->locallab.spots.at(sp).warm > 0) { - tempo = 5000 - 30 * params->locallab.spots.at(sp).warm; - } else { - tempo = 5000 - 49 * params->locallab.spots.at(sp).warm; - } - - ColorTemp::temp2mulxyz(params->wb.temperature, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB - ColorTemp::temp2mulxyz(tempo, "Custom", Xwout, Zwout); - ColorTemp::temp2mulxyz(5000, "Custom", Xwsc, Zwsc); - - //viewing condition for surrsrc - f = 1.00f; - c = 0.69f; - nc = 1.00f; - //viewing condition for surround - f2 = 1.0f, c2 = 0.69f, nc2 = 1.0f; - //with which algorithm - // alg = 0; - - - xwd = 100.0 * Xwout; - zwd = 100.0 * Zwout; - ywd = 100.f; - - xws = 100.0 * Xwsc; - zws = 100.0 * Zwsc; - yws = 100.f; - - - yb2 = 18; - //La and la2 = ambiant luminosity scene and viewing - la = 400.f; - const float la2 = 400.f; - const float pilot = 2.f; - const float pilotout = 2.f; - - //algoritm's params - // const float rstprotection = 100. ;//- params->colorappearance.rstprotection; - LUTu hist16J; - LUTu hist16Q; - float yb = 18.f; - float d, dj; - - // const int gamu = 0; //(params->colorappearance.gamut) ? 1 : 0; - xw = 100.0 * Xw; - yw = 100.f * Yw; - zw = 100.0 * Zw; - float xw1 = xws, yw1 = yws, zw1 = zws, xw2 = xwd, yw2 = ywd, zw2 = zwd; - - float cz, wh, pfl; - Ciecam02::initcam1float(yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); -// const float chr = 0.f; - const float pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f); - float nj, nbbj, ncbj, czj, awj, flj; - Ciecam02::initcam2float(yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); -#ifdef __SSE2__ - const float reccmcz = 1.f / (c2 * czj); -#endif - const float pow1n = pow_F(1.64f - pow_F(0.29f, nj), 0.73f); - -#ifdef __SSE2__ - int bufferLength = ((width + 3) / 4) * 4; // bufferLength has to be a multiple of 4 -#endif -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - // one line buffer per channel and thread - float Jbuffer[bufferLength] ALIGNED16; - float Cbuffer[bufferLength] ALIGNED16; - float hbuffer[bufferLength] ALIGNED16; - float Qbuffer[bufferLength] ALIGNED16; - float Mbuffer[bufferLength] ALIGNED16; - float sbuffer[bufferLength] ALIGNED16; -#endif -#ifdef _OPENMP - #pragma omp for schedule(dynamic, 16) -#endif - for (int i = 0; i < height; i++) { -#ifdef __SSE2__ - // vectorized conversion from Lab to jchqms - int k; - vfloat x, y, z; - vfloat J, C, h, Q, M, s; - - vfloat c655d35 = F2V(655.35f); - - for (k = 0; k < width - 3; k += 4) { - Color::Lab2XYZ(LVFU(lab->L[i][k]), LVFU(lab->a[i][k]), LVFU(lab->b[i][k]), x, y, z); - x = x / c655d35; - y = y / c655d35; - z = z / c655d35; - Ciecam02::xyz2jchqms_ciecam02float(J, C, h, - Q, M, s, F2V(aw), F2V(fl), F2V(wh), - x, y, z, - F2V(xw1), F2V(yw1), F2V(zw1), - F2V(c), F2V(nc), F2V(pow1), F2V(nbb), F2V(ncb), F2V(pfl), F2V(cz), F2V(d)); - STVF(Jbuffer[k], J); - STVF(Cbuffer[k], C); - STVF(hbuffer[k], h); - STVF(Qbuffer[k], Q); - STVF(Mbuffer[k], M); - STVF(sbuffer[k], s); - } - - for (; k < width; k++) { - float L = lab->L[i][k]; - float a = lab->a[i][k]; - float b = lab->b[i][k]; - float x, y, z; - //convert Lab => XYZ - Color::Lab2XYZ(L, a, b, x, y, z); - x = x / 655.35f; - y = y / 655.35f; - z = z / 655.35f; - float J, C, h, Q, M, s; - Ciecam02::xyz2jchqms_ciecam02float(J, C, h, - Q, M, s, aw, fl, wh, - x, y, z, - xw1, yw1, zw1, - c, nc, pow1, nbb, ncb, pfl, cz, d); - Jbuffer[k] = J; - Cbuffer[k] = C; - hbuffer[k] = h; - Qbuffer[k] = Q; - Mbuffer[k] = M; - sbuffer[k] = s; - } - -#endif // __SSE2__ - - for (int j = 0; j < width; j++) { - float J, C, h, Q, M, s; - -#ifdef __SSE2__ - // use precomputed values from above - J = Jbuffer[j]; - C = Cbuffer[j]; - h = hbuffer[j]; - Q = Qbuffer[j]; - M = Mbuffer[j]; - s = sbuffer[j]; -#else - float x, y, z; - float L = lab->L[i][j]; - float a = lab->a[i][j]; - float b = lab->b[i][j]; - float x1, y1, z1; - //convert Lab => XYZ - Color::Lab2XYZ(L, a, b, x1, y1, z1); - x = x1 / 655.35f; - y = y1 / 655.35f; - z = z1 / 655.35f; - //process source==> normal - Ciecam02::xyz2jchqms_ciecam02float(J, C, h, - Q, M, s, aw, fl, wh, - x, y, z, - xw1, yw1, zw1, - c, nc, pow1, nbb, ncb, pfl, cz, d); -#endif - float Jpro, Cpro, hpro, Qpro, Mpro, spro; - Jpro = J; - Cpro = C; - hpro = h; - Qpro = Q; - Mpro = M; - spro = s; - /* - */ - - - //retrieve values C,J...s - C = Cpro; - J = Jpro; - Q = Qpro; - M = Mpro; - h = hpro; - s = spro; - -#ifdef __SSE2__ - // write to line buffers - Jbuffer[j] = J; - Cbuffer[j] = C; - hbuffer[j] = h; -#else - float xx, yy, zz; - //process normal==> viewing - - Ciecam02::jch2xyz_ciecam02float(xx, yy, zz, - J, C, h, - xw2, yw2, zw2, - c2, nc2, pow1n, nbbj, ncbj, flj, czj, dj, awj); - x = xx * 655.35f; - y = yy * 655.35f; - z = zz * 655.35f; - float Ll, aa, bb; - //convert xyz=>lab - Color::XYZ2Lab(x, y, z, Ll, aa, bb); - lab->L[i][j] = Ll; - lab->a[i][j] = aa; - lab->b[i][j] = bb; -#endif - } - -#ifdef __SSE2__ - // process line buffers - float *xbuffer = Qbuffer; - float *ybuffer = Mbuffer; - float *zbuffer = sbuffer; - - for (k = 0; k < bufferLength; k += 4) { - Ciecam02::jch2xyz_ciecam02float(x, y, z, - LVF(Jbuffer[k]), LVF(Cbuffer[k]), LVF(hbuffer[k]), - F2V(xw2), F2V(yw2), F2V(zw2), - F2V(nc2), F2V(pow1n), F2V(nbbj), F2V(ncbj), F2V(flj), F2V(dj), F2V(awj), F2V(reccmcz)); - STVF(xbuffer[k], x * c655d35); - STVF(ybuffer[k], y * c655d35); - STVF(zbuffer[k], z * c655d35); - } - - // XYZ2Lab uses a lookup table. The function behind that lut is a cube root. - // SSE can't beat the speed of that lut, so it doesn't make sense to use SSE - for (int j = 0; j < width; j++) { - float Ll, aa, bb; - //convert xyz=>lab - Color::XYZ2Lab(xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); - - lab->L[i][j] = Ll; - lab->a[i][j] = aa; - lab->b[i][j] = bb; - } - -#endif - } - - } -} - -void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufcolfin, float rad, int bfh, int bfw, float epsilmax, float epsilmin, float thres, int sk, bool multiThread, int flag) -{ - if (rad != 0.f) { - array2D ble(bfw, bfh); - array2D guid(bfw, bfh); - if (flag == 0) { - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; - ble[ir][jr] = Color::L2Y(bufcolfin->L[ir][jr]) / 32768.f; - } - } - - const float aepsil = (epsilmax - epsilmin) / 100.f; - const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; - // const float epsil = aepsil * 0.1f * rad + bepsil; - const float epsil = aepsil * rad + bepsil; - const float blur = 10.f / sk * (thres + 0.8f * rad); - - rtengine::guidedFilter(guid, ble, ble, blur, epsil, multiThread, 4); - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufcolfin->L[ir][jr] = Color::computeXYZ2LabY(32768.f * ble[ir][jr]); - } - } - } else if (flag == 1) { - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - ble[ir][jr] = bufcolfin->L[ir][jr] / 32768.f; - guid[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; - } - - const float aepsil = (epsilmax - epsilmin) / 1000.f; - const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; - const float epsil = rad < 0.f ? 0.0001f : aepsil * rad + bepsil; - const float blur = rad < 0.f ? -1.f / rad : 1.f + rad; - const int r2 = rtengine::max(int(25 / sk * blur + 0.5f), 1); - - rtengine::guidedFilter(guid, ble, ble, r2, epsil, multiThread); - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufcolfin->L[ir][jr] = 32768.f * ble[ir][jr]; - } - } - } - } -} - - -void ImProcFunctions::softprocess(const LabImage* bufcolorig, array2D &buflight, float rad, int bfh, int bfw, double epsilmax, double epsilmin, float thres, int sk, bool multiThread) -{ - float minlig = buflight[0][0]; - -#ifdef _OPENMP - #pragma omp parallel for reduction(min:minlig) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - minlig = rtengine::min(buflight[ir][jr], minlig); - } - } - - array2D guidsoft(bfw, bfh); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - buflight[ir][jr] = LIM01((buflight[ir][jr] - minlig) / (100.f - minlig)); - guidsoft[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; - } - } - - double aepsil = (epsilmax - epsilmin) / 90.f; - double bepsil = epsilmax - 100.f * aepsil; - double epsil = aepsil * rad + bepsil; - float blur = 1.f / sk * (thres + 0.8f * rad); - guidedFilter(guidsoft, buflight, buflight, blur, epsil, multiThread, 4); - - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - buflight[ir][jr] = (100.f - minlig) * buflight[ir][jr] + minlig; - } - } -} - -void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve) -{ - BENCHFUN - //exposure local - - constexpr float maxran = 65536.f; - const float cexp_scale = std::pow(2.f, lp.expcomp); - const float ccomp = (rtengine::max(0.f, lp.expcomp) + 1.f) * lp.hlcomp / 100.f; - const float cshoulder = ((maxran / rtengine::max(1.0f, cexp_scale)) * (lp.hlcompthr / 200.f)) + 0.1f; - const float chlrange = maxran - cshoulder; - const float linear = lp.linear; - constexpr float kl = 1.f; - const float hlcompthr = lp.hlcompthr / 200.f; - const float hlcomp = lp.hlcomp / 100.f; - if (lp.linear > 0.f && lp.expcomp == 0.f) { - lp.expcomp = 0.001f; - } - - if (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float L = bufexporig->L[ir][jr]; - const float Llin = LIM01(L / 32768.f); - const float addcomp = linear * (-kl * Llin + kl);//maximum about 1. IL - const float exp_scale = pow_F(2.0, (lp.expcomp + addcomp)); - const float shoulder = ((maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr) + 0.1f; - const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; - const float hlrange = maxran - shoulder; - - //highlight - const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(exp_scale, comp, hlrange, 2 * L)); - L *= hlfactor * pow_F(2.f, addcomp);//approximation but pretty good with Laplacian and L < mean, hl aren't call - //shadow tone curve - L *= shtonecurve[2 * L]; - //tonecurve - lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; - } - } - } else { -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float L = bufexporig->L[ir][jr]; - //highlight - const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(cexp_scale, ccomp, chlrange, 2 * L)); - L *= hlfactor;//approximation but pretty good with Laplacian and L < mean, hl aren't call - //shadow tone curve - L *= shtonecurve[2 * L]; - //tonecurve - lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; - } - } - } -} - - -void ImProcFunctions::addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk) -{ -// BENCHFUN -//Box-Muller method. -// add luma noise to image - - srand(1); - - const float variaFactor = SQR(variance) / sk; - constexpr float randFactor1 = 1.f / RAND_MAX; - constexpr float randFactor2 = (2.f * rtengine::RT_PI_F) / RAND_MAX; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - float z0, z1; - bool generate = false; -#ifdef _OPENMP - #pragma omp for schedule(static) // static scheduling is important to avoid artefacts -#endif - for (int y = 0; y < lab->H; y++) { - for (int x = 0; x < lab->W; x++) { - generate = !generate; - float kvar = 1.f; - - if (lab->L[y][x] < 12000.f) { - constexpr float ah = -0.5f / 12000.f; - constexpr float bh = 1.5f; - kvar = ah * lab->L[y][x] + bh; //increase effect for low lights < 12000.f - } else if (lab->L[y][x] > 20000.f) { - constexpr float ah = -0.5f / 12768.f; - constexpr float bh = 1.f - 20000.f * ah; - kvar = ah * lab->L[y][x] + bh; //decrease effect for high lights > 20000.f - kvar = kvar < 0.5f ? 0.5f : kvar; - } - - float varia = SQR(kvar) * variaFactor; - - if (!generate) { - dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z1, 0.f, 32768.f); - continue; - } - - int u1 = 0; - int u2; - - while (u1 == 0) { - u1 = rand(); - u2 = rand(); - } - - float u1f = u1 * randFactor1; - float u2f = u2 * randFactor2; - - float2 sincosval = xsincosf(2.f * rtengine::RT_PI_F * u2f); - float factor = std::sqrt(-2.f * xlogf(u1f)); - z0 = factor * sincosval.y; - z1 = factor * sincosval.x; - - dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z0, 0.f, 32768.f); - - } - } - } -} - -void ImProcFunctions::DeNoise_Local(int call, const struct local_params& lp, LabImage* originalmask, int levred, float hueref, float lumaref, float chromaref, LabImage* original, LabImage* transformed, const LabImage &tmp1, int cx, int cy, int sk) -{ - //warning, but I hope used it next - // local denoise and impulse - //simple algo , perhaps we can improve as the others, but noise is here and not good for hue detection - // BENCHFUN - lumaref *= 327.68f; - const float ach = lp.trans / 100.f; - - const float factnoise1 = 1.f + (lp.noisecf) / 500.f; - const float factnoise2 = 1.f + (lp.noisecc) / 500.f; - const float factnoise = factnoise1 * factnoise2; - - const int GW = transformed->W; - const int GH = transformed->H; - - const float colorde = lp.colorde == 0 ? -1.f : lp.colorde; // -1.f to avoid black - const float amplabL = 2.f * colorde; - constexpr float darklim = 5000.f; - - const float refa = chromaref * std::cos(hueref) * 327.68f; - const float refb = chromaref * std::sin(hueref) * 327.68f; - const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; - const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; - const bool previewbl = lp.showmaskblmet == 4; - - const std::unique_ptr origblur(new LabImage(GW, GH)); - const float radius = 3.f / sk; - - if (usemaskbl) { -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblur->L, GW, GH, radius); - gaussianBlur(originalmask->a, origblur->a, GW, GH, radius); - gaussianBlur(originalmask->b, origblur->b, GW, GH, radius); - } - } else { -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - } - - const int begx = lp.xc - lp.lxL; - const int begy = lp.yc - lp.lyT; - constexpr float r327d68 = 1.f / 327.68f; - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const LabImage* maskptr = origblur.get(); - const float mindE = 2.f + MINSCOPE * lp.sensden * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.sensden * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - const int loy = cy + y; - const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing - - if (isZone0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - for (int x = 0, lox = cx + x; x < transformed->W; x++, lox++) { - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - float reducdEL = 1.f; - float reducdEa = 1.f; - float reducdEb = 1.f; - - if (levred == 7) { - const float dEL = std::sqrt(0.9f * SQR(refa - maskptr->a[y][x]) + 0.9f * SQR(refb - maskptr->b[y][x]) + 1.2f * SQR(lumaref - maskptr->L[y][x])) * r327d68; - const float dEa = std::sqrt(1.2f * SQR(refa - maskptr->a[y][x]) + 1.f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; - const float dEb = std::sqrt(1.f * SQR(refa - maskptr->a[y][x]) + 1.2f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; - reducdEL = SQR(calcreducdE(dEL, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); - reducdEa = SQR(calcreducdE(dEa, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); - reducdEb = SQR(calcreducdE(dEb, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); - } - float difL, difa, difb; - - if (call == 2 /*|| call == 1 || call == 3 */) { //simpleprocess - difL = tmp1.L[loy - begy][lox - begx] - original->L[y][x]; - difa = tmp1.a[loy - begy][lox - begx] - original->a[y][x]; - difb = tmp1.b[loy - begy][lox - begx] - original->b[y][x]; - } else { //dcrop - difL = tmp1.L[y][x] - original->L[y][x]; - difa = tmp1.a[y][x] - original->a[y][x]; - difb = tmp1.b[y][x] - original->b[y][x]; - } - - difL *= localFactor * reducdEL; - difa *= localFactor * reducdEa; - difb *= localFactor * reducdEb; - transformed->L[y][x] = CLIP(original->L[y][x] + difL); - transformed->a[y][x] = clipC((original->a[y][x] + difa) * factnoise); - transformed->b[y][x] = clipC((original->b[y][x] + difb) * factnoise) ; - - if (blshow) { - transformed->L[y][x] = CLIP(12000.f + amplabL * difL);// * 10.f empirical to can visualize modifications - transformed->a[y][x] = clipC(amplabL * difa);// * 10.f empirical to can visualize modifications - transformed->b[y][x] = clipC(amplabL * difb);// * 10.f empirical to can visualize modifications - } else if (previewbl || lp.prevdE) { - const float difbdisp = (reducdEL + reducdEa + reducdEb) * 10000.f * colorde; - - if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! - transformed->L[y][x] = darklim - transformed->L[y][x]; - } - - if (colorde <= 0) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = difbdisp; - } else { - transformed->a[y][x] = -difbdisp; - transformed->b[y][x] = 0.f; - } - } - } - } - } -} - -void ImProcFunctions::InverseReti_Local(const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int chro, int sk) -{ - // BENCHFUN -//inverse local retinex - float ach = lp.trans / 100.f; - int GW = transformed->W; - int GH = transformed->H; - float refa = chromaref * cos(hueref); - float refb = chromaref * sin(hueref); - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - - const std::unique_ptr origblur(new LabImage(GW, GH)); - - float radius = 3.f / sk; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - - } -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const float mindE = 2.f + MINSCOPE * lp.sensh * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - int loy = cy + y; - - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - - int zone; - float localFactor; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - float rL = origblur->L[y][x] / 327.68f; - float dE = std::sqrt(kab * SQR(refa - origblur->a[y][x] / 327.68f) + kab * SQR(refb - origblur->b[y][x] / 327.68f) + kL * SQR(lumaref - rL)); - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensh); - - switch (zone) { - case 0: { // outside selection and outside transition zone => full effect, no transition - if (chro == 0) { - float difL = tmp1->L[y][x] - original->L[y][x]; - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - } - - if (chro == 1) { - float difa = tmp1->a[y][x] - original->a[y][x]; - float difb = tmp1->b[y][x] - original->b[y][x]; - - transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); - transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); - } - break; - } - - case 1: { // inside transition zone - float factorx = 1.f - localFactor; - - if (chro == 0) { - float difL = tmp1->L[y][x] - original->L[y][x]; - difL *= factorx; - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - } - - if (chro == 1) { - float difa = tmp1->a[y][x] - original->a[y][x]; - float difb = tmp1->b[y][x] - original->b[y][x]; - - difa *= factorx; - difb *= factorx; - - transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); - transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); - } - break; - } - - case 2: { // inside selection => no effect, keep original values - if (chro == 0) { - transformed->L[y][x] = original->L[y][x]; - } - - if (chro == 1) { - transformed->a[y][x] = original->a[y][x]; - transformed->b[y][x] = original->b[y][x]; - } - } - } - } - } - } -} - - - - -void ImProcFunctions::InverseBlurNoise_Local(LabImage * originalmask, float **bufchro, const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int sk) -{ - // BENCHFUN -//inverse local blur and noise - float ach = lp.trans / 100.f; - int GW = transformed->W; - int GH = transformed->H; - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - - - const bool blshow = (lp.showmaskblmet == 1 || lp.showmaskblmet == 2); - const bool previewbl = (lp.showmaskblmet == 4); - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - const std::unique_ptr origblur(new LabImage(GW, GH)); - std::unique_ptr origblurmask; - const bool usemaskbl = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4); - const bool usemaskall = usemaskbl; - - float radius = 3.f / sk; - - if (usemaskall) { - origblurmask.reset(new LabImage(GW, GH)); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); - gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); - gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); - } - } - - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - - } -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); - const float mindE = 2.f + MINSCOPE * lp.sensbn * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - int loy = cy + y; - - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - - int zone; - float localFactor; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - const float clc = (previewbl) ? settings->previewselection * 100.f : bufchro[y][x]; - float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); - float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); - float huedelta2 = abdelta2 - chrodelta2; - - float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); - const float realstrchdE = reducdE * clc; - - switch (zone) { - - case 0: { // outside selection and outside transition zone => full effect, no transition - float difL = tmp1->L[y][x] - original->L[y][x]; - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - float difa = tmp1->a[y][x] - original->a[y][x]; - float difb = tmp1->b[y][x] - original->b[y][x]; - float flia = 1.f, flib = 1.f; - flia = flib = ((100.f + realstrchdE) / 100.f); - const float chra = tmp1->a[y][x]; - const float chrb = tmp1->b[y][x]; - - if (!lp.actsp) { - difa = chra * flia - original->a[y][x]; - difb = chrb * flib - original->b[y][x]; - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - } - - if (blshow) { - transformed->L[y][x] = CLIP(12000.f + difL); - transformed->a[y][x] = clipC(difa); - transformed->b[y][x] = clipC(difb); - } else if (previewbl || lp.prevdE) { - transformed->a[y][x] = 0.f; - transformed->b[y] [x] = (difb); - } - - break; - } - - case 1: { // inside transition zone - float difL = tmp1->L[y][x] - original->L[y][x]; - float difa = tmp1->a[y][x] - original->a[y][x]; - float difb = tmp1->b[y][x] - original->b[y][x]; - float flia = 1.f, flib = 1.f; - flia = flib = ((100.f + realstrchdE) / 100.f); - const float chra = tmp1->a[y][x]; - const float chrb = tmp1->b[y][x]; - - float factorx = 1.f - localFactor; - difL *= factorx; - - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - - if (!lp.actsp) { - difa = chra * flia - original->a[y][x]; - difb = chrb * flib - original->b[y][x]; - difa *= factorx; - difb *= factorx; - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - - } - - if (blshow) { - transformed->L[y][x] = CLIP(12000.f + difL); - transformed->a[y][x] = clipC(difa); - transformed->b[y][x] = clipC(difb); - } else if (previewbl) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = (difb); - } - - break; - } - - case 2: { // inside selection => no effect, keep original values - transformed->L[y][x] = original->L[y][x]; - - if (!lp.actsp) { - - transformed->a[y][x] = original->a[y][x]; - transformed->b[y][x] = original->b[y][x]; - } - } - } - } - } - } -} - -static void mean_fab(int xstart, int ystart, int bfw, int bfh, LabImage* bufexporig, const LabImage* original, float &fab, float &meanfab, float chrom, bool multiThread) -{ - const int nbfab = bfw * bfh; - - meanfab = 0.f; - fab = 50.f; - - if (nbfab > 0) { - double sumab = 0.0; - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:sumab) if(multiThread) -#else - static_cast(multiThread); -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; - bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; - sumab += std::fabs(bufexporig->a[y][x]); - sumab += std::fabs(bufexporig->b[y][x]); - } - } - - meanfab = sumab / (2.f * nbfab); - - double som = 0.0; - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:som) if(multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - som += SQR(std::fabs(bufexporig->a[y][x]) - meanfab) + SQR(std::fabs(bufexporig->b[y][x]) - meanfab); - } - } - - const float multsigma = (chrom >= 0.f ? 0.035f : 0.018f) * chrom + 1.f; - - const float stddv = std::sqrt(som / nbfab); - fab = meanfab + multsigma * stddv; - - if (fab <= 0.f) { - fab = 50.f; - } - } -} - -struct grad_params { - bool angle_is_zero, transpose, bright_top; - float ta, yc, xc; - float ys, ys_inv; - float scale, botmul, topmul; - float top_edge_0; - int h; -}; - -void calclocalGradientParams(const struct local_params& lp, struct grad_params& gp, float ystart, float xstart, int bfw, int bfh, int indic) -{ - int w = bfw; - int h = bfh; - float stops = 0.f; - float angs = 0.f; - - if (indic == 0) { - stops = -lp.strmaexp; - angs = lp.angmaexp; - } else if (indic == 1) { - stops = lp.strexp; - angs = lp.angexp; - } else if (indic == 2) { - stops = lp.strSH; - angs = lp.angSH; - } else if (indic == 3) { - stops = lp.strcol; - angs = lp.angcol; - } else if (indic == 4) { - float redu = 1.f; - - if (lp.strcolab > 0.f) { - redu = 0.6f; - } else { - redu = 0.15f; - } - - stops = redu * lp.strcolab; - angs = lp.angcol; - } else if (indic == 5) { - stops = lp.strcolab; - angs = lp.angcol; - } else if (indic == 6) { - stops = lp.strcolh; - angs = lp.angcol; - } else if (indic == 7) { - stops = lp.strvib; - angs = lp.angvib; - } else if (indic == 8) { - float redu = 1.f; - - if (lp.strvibab > 0.f) { - redu = 0.7f; - } else { - redu = 0.5f; - } - - stops = redu * lp.strvibab; - angs = lp.angvib; - } else if (indic == 9) { - stops = lp.strvibh; - angs = lp.angvib; - } else if (indic == 10) { - stops = std::fabs(lp.strwav); - angs = lp.angwav; - } else if (indic == 11) { - stops = lp.strlog; - angs = lp.anglog; - } else if (indic == 12) { - stops = -lp.str_mas; - angs = lp.ang_mas; - } - - - double gradient_stops = stops; - double gradient_center_x = LIM01((lp.xc - xstart) / bfw); - double gradient_center_y = LIM01((lp.yc - ystart) / bfh); - double gradient_angle = angs / 180.0 * rtengine::RT_PI; - double varfeath = 0.01 * lp.feath; - - //printf("xstart=%f ysta=%f lpxc=%f lpyc=%f stop=%f bb=%f cc=%f ang=%f ff=%d gg=%d\n", xstart, ystart, lp.xc, lp.yc, gradient_stops, gradient_center_x, gradient_center_y, gradient_angle, w, h); - - // make 0.0 <= gradient_angle < 2 * rtengine::RT_PI - gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); - - if (gradient_angle < 0.0) { - gradient_angle += 2.0 * rtengine::RT_PI; - } - - gp.bright_top = false; - gp.transpose = false; - gp.angle_is_zero = false; - gp.h = h; - double cosgrad = cos(gradient_angle); - - if (std::fabs(cosgrad) < 0.707) { - // we transpose to avoid division by zero at 90 degrees - // (actually we could transpose only for 90 degrees, but this way we avoid - // division with extremely small numbers - gp.transpose = true; - gradient_angle += 0.5 * rtengine::RT_PI; - double gxc = gradient_center_x; - gradient_center_x = 1.0 - gradient_center_y; - gradient_center_y = gxc; - } - - gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); - - if (gradient_angle > 0.5 * rtengine::RT_PI && gradient_angle < rtengine::RT_PI) { - gradient_angle += rtengine::RT_PI; - gp.bright_top = true; - } else if (gradient_angle >= rtengine::RT_PI && gradient_angle < 1.5 * rtengine::RT_PI) { - gradient_angle -= rtengine::RT_PI; - gp.bright_top = true; - } - - if (std::fabs(gradient_angle) < 0.001 || std::fabs(gradient_angle - 2 * rtengine::RT_PI) < 0.001) { - gradient_angle = 0; - gp.angle_is_zero = true; - } - - if (gp.transpose) { - gp.bright_top = !gp.bright_top; - std::swap(w, h); - } - - gp.scale = 1.0 / pow(2, gradient_stops); - - if (gp.bright_top) { - gp.topmul = 1.0; - gp.botmul = gp.scale; - } else { - gp.topmul = gp.scale; - gp.botmul = 1.0; - } - - gp.ta = tan(gradient_angle); - gp.xc = w * gradient_center_x; - gp.yc = h * gradient_center_y; - gp.ys = std::sqrt((float)h * h + (float)w * w) * (varfeath / cos(gradient_angle)); - gp.ys_inv = 1.0 / gp.ys; - gp.top_edge_0 = gp.yc - gp.ys / 2.0; - - if (gp.ys < 1.0 / h) { - gp.ys_inv = 0; - gp.ys = 0; - } -} - -void ImProcFunctions::blendstruc(int bfw, int bfh, LabImage* bufcolorig, float radius, float stru, array2D & blend2, int sk, bool multiThread) -{ - SobelCannyLuma(blend2, bufcolorig->L, bfw, bfh, radius); - float rm = 20.f / sk; - - if (rm > 0) { - float **mb = blend2; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(mb, mb, bfw, bfh, rm); - } - } - - array2D ble(bfw, bfh); - array2D guid(bfw, bfh); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float X, Y, Z; - float L = bufcolorig->L[ir][jr]; - float a = bufcolorig->a[ir][jr]; - float b = bufcolorig->b[ir][jr]; - Color::Lab2XYZ(L, a, b, X, Y, Z); - - guid[ir][jr] = Y / 32768.f; - - blend2[ir][jr] /= 32768.f; - } - } - - const float blur = 25 / sk * (10.f + 1.2f * stru); - - rtengine::guidedFilter(guid, blend2, ble, blur, 0.001, multiThread); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - blend2[ir][jr] = 32768.f * ble[ir][jr]; - } - } -} - - -static void blendmask(const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* original, LabImage* bufmaskor, LabImage* originalmas, float bl, float blab, int inv) -{ -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh ; y++) { - const int loy = y + ystart + cy; - - for (int x = 0; x < bfw; x++) { - const int lox = x + xstart + cx; - int zone; - - float localFactor = 1.f; - const float achm = lp.trans / 100.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - - if (inv == 0) { - if (zone > 0) { - bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); - bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); - bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); - - bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); - bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); - bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); - - originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); - originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); - originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); - - original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); - original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); - original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); - original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); - - } - } else if (inv == 1) { - localFactor = 1.f - localFactor; - - if (zone < 2) { - bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); - bufexporig->a[y][x] *= (1.f + blab * bufmaskor->a[y][x]); - bufexporig->b[y][x] *= (1.f + blab * bufmaskor->b[y][x]); - - bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); - bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); - bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); - - originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); - originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); - originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); - - switch (zone) { - case 0: { - original->L[y + ystart][x + xstart] += (bl * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + blab * bufmaskor->b[y][x]); - original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); - original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); - original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); - break; - } - - case 1: { - original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); - original->a[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->a[y][x]); - original->b[y + ystart][x + xstart] *= (1.f + blab * localFactor * bufmaskor->b[y][x]); - original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); - original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); - original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); - } - - } - } - - } - } - } -} - -void ImProcFunctions::deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, - float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh) -{ - const float refa = chromaref * cos(hueref); - const float refb = chromaref * sin(hueref); - const float refL = lumaref; - - const float kL = balance; - const float kab = balancedeltaE(kL); - const float kH = balanceh; - const float kch = balancedeltaE(kH); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - const float abdelta2 = SQR(refa - bufcolorig->a[y][x] / 327.68f) + SQR(refb - bufcolorig->b[y][x] / 327.68f); - const float chrodelta2 = SQR(std::sqrt(SQR(bufcolorig->a[y][x]) + SQR(bufcolorig->b[y][x])) / 327.68f - chromaref); - const float huedelta2 = abdelta2 - chrodelta2; - const float tempdE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - bufcolorig->L[y][x] / 327.68f)); - - float reducdE; - if (tempdE > maxdE) { - reducdE = 0.f; - } else if (tempdE > mindE && tempdE <= maxdE) { - const float ar = 1.f / (mindE - maxdE); - const float br = - ar * maxdE; - reducdE = pow(ar * tempdE + br, iterat); - } else { - reducdE = 1.f; - } - - if (scope > limscope) { - if (tempdE > maxdElim) { - reducdE = 0.f; - } else if (tempdE > mindElim && tempdE <= maxdElim) { - const float arlim = 1.f / (mindElim - maxdElim); - const float brlim = - arlim * maxdElim; - const float reducdElim = pow(arlim * tempdE + brlim, iterat); - const float aalim = (1.f - reducdElim) / 20.f; - const float bblim = 1.f - 100.f * aalim; - reducdE = aalim * scope + bblim; - } else { - reducdE = 1.f; - } - } - - rdE[y][x] = reducdE ; - } - } -} - -static void showmask(int lumask, const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* transformed, LabImage* bufmaskorigSH, int inv) -{ -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh; y++) { - const int loy = y + ystart + cy; - - for (int x = 0; x < bfw; x++) { - const int lox = x + xstart + cx; - int zone; - float localFactor = 1.f; - const float achm = lp.trans / 100.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - - if (inv == 0) { - if (zone > 0) {//normal - transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); - transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; - transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; - } - } else if (inv == 1) { //inverse - if (zone == 0) { - transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); - transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; - transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; - } - } - } - } -} - -void ImProcFunctions::discrete_laplacian_threshold(float * data_out, const float * data_in, size_t nx, size_t ny, float t) -{ - BENCHFUN - - if (!data_in || !data_out) { - fprintf(stderr, "a pointer is NULL and should not be so\n"); - abort(); - } - - /* pointers to the data and neighbour values */ - /* - * y-1 - * x-1 ptr x+1 - * y+1 - * <---------------------nx-------> - */ - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (size_t j = 0; j < ny; j++) { - const float* ptr_in = &data_in[j * nx]; - float* ptr_out = &data_out[j * nx]; - for (size_t i = 0; i < nx; i++) { - float val = 0.f; - /* row differences */ - if (0 < i) { - const float diff = ptr_in[i] - ptr_in[i - 1]; - val += std::fabs(diff) > t ? diff : 0.f; - } - - if (nx - 1 > i) { - const float diff = ptr_in[i] - ptr_in[i + 1];; - val += std::fabs(diff) > t ? diff : 0.f; - } - - /* column differences */ - if (0 < j) { - const float diff = ptr_in[i] - ptr_in[i - nx];; - val += std::fabs(diff) > t ? diff : 0.f; - } - - if (ny - 1 > j) { - const float diff = ptr_in[i] - ptr_in[i + nx];; - val += std::fabs(diff) > t ? diff : 0.f; - } - - ptr_out[i] = val; - } - } - -} - -float *ImProcFunctions::cos_table(size_t size) -{ - float *table = NULL; - - /* allocate the cosinus table */ - if (NULL == (table = (float *) malloc(sizeof(*table) * size))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - /* - * fill the cosinus table, - * table[i] = cos(i Pi / n) for i in [0..n[ - */ - const double pi_size = rtengine::RT_PI / size; - - for (size_t i = 0; i < size; i++) { - table[i] = std::cos(pi_size * i); - } - - return table; -} - - -void ImProcFunctions::rex_poisson_dct(float * data, size_t nx, size_t ny, double m) -{ - /* - * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ - * - - * @file retinex_pde_lib.c discrete Poisson equation - * @brief laplacian, DFT and Poisson routines - * - * @author Nicolas Limare - * some adaptations for Rawtherapee - */ - BENCHFUN - - /* - * get the cosinus tables - * cosx[i] = cos(i Pi / nx) for i in [0..nx[ - * cosy[i] = cos(i Pi / ny) for i in [0..ny[ - */ - - float* cosx = cos_table(nx); - float* cosy = cos_table(ny); - - /* - * we will now multiply data[i, j] by - * m / (4 - 2 * cosx[i] - 2 * cosy[j])) - * and set data[0, 0] to 0 - */ - float m2 = m / 2.; - /* - * after that, by construction, we always have - * cosx[] + cosy[] != 2. - */ - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (size_t i = 0; i < ny; ++i) { - for (size_t j = 0; j < nx; ++j) { - data[i * nx + j] *= m2 / (2.f - cosx[j] - cosy[i]); - } - } - // handle the first value, data[0, 0] = 0 - data[0] = 0.f; - - free(cosx); - free(cosy); - -} - -void ImProcFunctions::mean_dt(const float* data, size_t size, double& mean_p, double& dt_p) -{ - - double mean = 0.; - double dt = 0.; - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:mean,dt) if(multiThread) -#endif - for (size_t i = 0; i < size; i++) { - mean += data[i]; - dt += SQR(data[i]); - } - - mean /= size; - dt /= size; - dt -= SQR(mean); - mean_p = mean; - dt_p = std::sqrt(dt); -} - -void ImProcFunctions::normalize_mean_dt(float * data, const float * ref, size_t size, float mod, float sigm) -{ - /* - * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ - * - - * @file retinex_pde_lib.c discrete Poisson equation - * @brief laplacian, DFT and Poisson routines - * - * @author Nicolas Limare - * adapted for Rawtherapee - jacques Desmis july 2019 - */ - - if (NULL == data || NULL == ref) { - fprintf(stderr, "a pointer is NULL and should not be so\n"); - abort(); - } - - double mean_ref, mean_data, dt_ref, dt_data; - - /* compute mean and variance of the two arrays */ - mean_dt(ref, size, mean_ref, dt_ref); - mean_dt(data, size, mean_data, dt_data); - - /* compute the normalization coefficients */ - const double a = dt_ref / dt_data; - const double b = mean_ref - a * mean_data; - - const float modma = mod * a; - const float sigmmmodmb = sigm * mod * b; - const float onesmod = 1.f - mod; - /* normalize the array */ - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (size_t i = 0; i < size; i++) { - data[i] = (modma * data[i] + sigmmmodmb) + onesmod * ref[i];//normalize mean and stdv and balance PDE - } - -} - -void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw, int bfh, float thresh, float multy, float * dE, int show, int dEenable, int normalize) -{ - /* - * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ - * - - * @file retinex_pde_lib.c discrete Poisson equation - * @brief laplacian, DFT and Poisson routines - * - * @author Nicolas Limare - * adapted for Rawtherapee by Jacques Desmis 6-2019 - */ - - BENCHFUN -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_init_threads(); - fftwf_plan_with_nthreads(omp_get_max_threads()); - } -#endif - float *data_fft, *data_fft04, *data_tmp, *data, *data_tmp04; - float *datashow = nullptr; - - if (show != 0) { - if (NULL == (datashow = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - } - - if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (NULL == (data_tmp04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - //first call to laplacian with plein strength - ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); - - if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (show == 1) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_tmp[y * bfw + x]; - } - } - } - - //second call to laplacian with 40% strength ==> reduce effect if we are far from ref (deltaE) - ImProcFunctions::discrete_laplacian_threshold(data_tmp04, datain, bfw, bfh, 0.4f * thresh); - - if (NULL == (data_fft04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - //execute first - const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); - fftwf_execute(dct_fw); - - //execute second - if (dEenable == 1) { - const auto dct_fw04 = fftwf_plan_r2r_2d(bfh, bfw, data_tmp04, data_fft04, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); - fftwf_execute(dct_fw04); - fftwf_destroy_plan(dct_fw04); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) {//mix two fftw Laplacian : plein if dE near ref - for (int x = 0; x < bfw; x++) { - float prov = pow(dE[y * bfw + x], 4.5f); - data_fft[y * bfw + x] = prov * data_fft[y * bfw + x] + (1.f - prov) * data_fft04[y * bfw + x]; - } - } - } - - if (show == 2) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_fft[y * bfw + x]; - } - } - } - - fftwf_free(data_fft04); - fftwf_free(data_tmp); - fftwf_free(data_tmp04); - - /* solve the Poisson PDE in Fourier space */ - /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ - ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); - - if (show == 3) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_fft[y * bfw + x]; - } - } - } - - const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); - fftwf_execute(dct_bw); - fftwf_destroy_plan(dct_fw); - fftwf_destroy_plan(dct_bw); - fftwf_free(data_fft); - fftwf_cleanup(); - -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_cleanup_threads(); - } -#endif - if (show != 4 && normalize == 1) { - normalize_mean_dt(data, datain, bfw * bfh, 1.f, 1.f); - } - - if (show == 0 || show == 4) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - dataout[y * bfw + x] = clipLoc(multy * data[y * bfw + x]); - } - } - } else if (show == 1 || show == 2 || show == 3) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - dataout[y * bfw + x] = clipLoc(multy * datashow[y * bfw + x]); - } - } - - fftwf_free(datashow); - } - - fftwf_free(data); - -} - -void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int xstart, int ystart, int sk, int cx, int cy, LabImage* bufcolorig, LabImage* bufmaskblurcol, LabImage* originalmaskcol, LabImage* original, LabImage* reserved, int inv, struct local_params & lp, - float strumask, bool astool, - const LocCCmaskCurve & locccmasCurve, bool lcmasutili, - const LocLLmaskCurve & locllmasCurve, bool llmasutili, - const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, - bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, float blendmab, int shado, float amountcd, float anchorcd, - const LUTf& lmasklocalcurve, bool localmaskutili, - const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, - int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, - float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, - bool fftt, float blu_ma, float cont_ma, int indic - ) - - -{ - array2D ble(bfw, bfh); - array2D blechro(bfw, bfh); - array2D hue(bfw, bfh); - array2D guid(bfw, bfh); - const std::unique_ptr bufreserv(new LabImage(bfw, bfh)); - float meanfab, fab; - mean_fab(xstart, ystart, bfw, bfh, bufcolorig, original, fab, meanfab, chrom, multiThread); - float chromult = 1.f - 0.01f * chrom; - float kinv = 1.f; - float kneg = 1.f; - - if (invmask) { - kinv = 0.f; - kneg = -1.f; - } - - if (deltaE || modmask || enaMask || showmaske) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufmaskblurcol->L[y][x] = original->L[y + ystart][x + xstart]; - bufmaskblurcol->a[y][x] = original->a[y + ystart][x + xstart]; - bufmaskblurcol->b[y][x] = original->b[y + ystart][x + xstart]; - bufreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; - bufreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; - bufreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; - } - } - - JaggedArray blendstru(bfw, bfh); - - if (blu_ma >= 0.25f && strumask == 0.f) { - strumask = 0.1f; // to enable a small mask make FFT good ...why ?? - } - - if (strumask > 0.f) { - float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 - buildBlendMask(bufcolorig->L, blendstru, bfw, bfh, delstrumask); - float radblur = 0.02f * std::fabs(0.1f * rad);//empirical value - float rm = radblur / sk; - - if (rm > 0) { - float **mb = blendstru; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(mb, mb, bfw, bfh, rm); - } - } - - } - - JaggedArray blendblur(bfw, bfh); - - JaggedArray blur(bfw, bfh); - - if (cont_ma > 0.f) { - float contra = cont_ma; - buildBlendMask(bufcolorig->L, blendblur, bfw, bfh, contra); - - - float radblur = 0.25f + 0.002f * std::fabs(rad);//empirical value - float rm = radblur / sk; - - if (fftt) { - if (rm < 0.3f) { - rm = 0.3f; - } - } - - if (rm > 0) { - float **mb = blendblur; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(mb, mb, bfw, bfh, rm); - } - } - - if (blu_ma >= 0.25f) { - if (!fftt) { // || (lp.fftColorMask && call != 2)) { -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufcolorig->L, blur, bfw, bfh, blu_ma / sk); - } - } else { - ImProcFunctions::fftw_convol_blur2(bufcolorig->L, blur, bfw, bfh, blu_ma / sk, 0, 0); - } - - for (int i = 0; i < bfh; i++) { - for (int j = 0; j < bfw; j++) { - blur[i][j] = intp(blendblur[i][j], bufcolorig->L[i][j], rtengine::max(blur[i][j], 0.0f)); - } - } - } - } - - bool HHmaskcurve = false; - - if (lochhhmasCurve && lhhmasutili) { - for (int i = 0; i < 500; i++) { - if (lochhhmasCurve[i] != 0.5) { - HHmaskcurve = true; - } - } - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - float atan2Buffer[bfw] ALIGNED64; -// float atan2BufferH[bfw] ALIGNED64; -#endif -#ifdef _OPENMP - #pragma omp for schedule(dynamic, 16) -#endif - for (int ir = 0; ir < bfh; ir++) { -#ifdef __SSE2__ - - if (lochhmasCurve && lhmasutili) { - int i = 0; - - for (; i < bfw - 3; i += 4) { - STVF(atan2Buffer[i], xatan2f(LVFU(bufcolorig->b[ir][i]), LVFU(bufcolorig->a[ir][i]))); - } - - for (; i < bfw; i++) { - atan2Buffer[i] = xatan2f(bufcolorig->b[ir][i], bufcolorig->a[ir][i]); - } - } - -#endif - - for (int jr = 0; jr < bfw; jr++) { - float kmaskL = 0.f; - float kmaskC = 0.f; - float kmaskHL = 0.f; - float kmaskH = 0.f; - float kmasstru = 0.f; - float kmasblur = 0.f; - - if (strumask > 0.f && !astool) { - kmasstru = bufcolorig->L[ir][jr] * blendstru[ir][jr]; - } - - if (cont_ma > 0.f) { - - if (blu_ma >= 0.25f) { - - float prov = intp(blendstru[ir][jr], bufcolorig->L[ir][jr], rtengine::max(blur[ir][jr], 0.0f)); - kmasblur = bufcolorig->L[ir][jr] - prov ; - - } - } - - if (locllmasCurve && llmasutili) { - // printf("s"); - kmaskL = 32768.f * LIM01(kinv - kneg * locllmasCurve[(500.f / 32768.f) * bufcolorig->L[ir][jr]]); - - } - - if (!deltaE && locccmasCurve && lcmasutili) { - kmaskC = LIM01(kinv - kneg * locccmasCurve[500.f * (0.0001f + std::sqrt(SQR(bufcolorig->a[ir][jr]) + SQR(bufcolorig->b[ir][jr])) / fab)]); - } - - if (lochhmasCurve && lhmasutili) { -#ifdef __SSE2__ - const float huema = atan2Buffer[jr]; -#else - const float huema = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); -#endif - float h = Color::huelab_to_huehsv2(huema); - h += 1.f / 6.f; - - if (h > 1.f) { - h -= 1.f; - } - - const float valHH = LIM01(kinv - kneg * lochhmasCurve[500.f * h]); - - if (!deltaE) { - kmaskH = valHH; - } - - kmaskHL = 32768.f * valHH; - } - - /* - //keep here in case of...but !! - if (lochhhmasCurve && HHmaskcurve) { - - #ifdef __SSE2__ - huemah = atan2BufferH[jr]; - #else - huemah = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); - #endif - - float hh = Color::huelab_to_huehsv2(huemah); - hh += 1.f / 6.f; - - if (hh > 1.f) { - hh -= 1.f; - } - - const float val_HH = float (LIM01(((0.5f - lochhhmasCurve[500.f * hh])))); - kmaskHH = 2.f * val_HH; - const float hhro = kmaskHH; - - if (hhro != 0) { - newhr = huemah + hhro; - - if (newhr > rtengine::RT_PI_F) { - newhr -= 2 * rtengine::RT_PI_F; - } else if (newhr < -rtengine::RT_PI_F) { - newhr += 2 * rtengine::RT_PI_F; - } - } - sincosval = xsincosf(newhr); - - } - */ - bufmaskblurcol->L[ir][jr] = clipLoc(kmaskL + kmaskHL + kmasstru + kmasblur); - bufmaskblurcol->a[ir][jr] = clipC((kmaskC + chromult * kmaskH)); - bufmaskblurcol->b[ir][jr] = clipC((kmaskC + chromult * kmaskH)); - - if (shortcu == 1) { //short circuit all L curve - bufmaskblurcol->L[ir][jr] = 32768.f - bufcolorig->L[ir][jr]; - } - - ble[ir][jr] = bufmaskblurcol->L[ir][jr] / 32768.f; - hue[ir][jr] = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); - const float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); - - blechro[ir][jr] = chromah / 32768.f;//must be good perhaps more or less, only incidence on LIM blea bleb - guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; - - } - } - } - - std::unique_ptr bufprov; - if (delt) { - bufprov.reset(new LabImage(bfw, bfh)); - bufprov->CopyFrom(bufmaskblurcol, multiThread); - } - - if (rad != 0.f) { - const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; - const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); - const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); - - constexpr float epsilmax = 0.005f; - constexpr float epsilmin = 0.00001f; - - constexpr float aepsil = (epsilmax - epsilmin) / 100.f; - constexpr float bepsil = epsilmin; //epsilmax - 100.f * aepsil; - const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; - - rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); - rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); - } - - LUTf lutTonemaskexp(65536); - calcGammaLut(gamma, slope, lutTonemaskexp); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float2 sincosval = xsincosf(hue[ir][jr]); - bufmaskblurcol->L[ir][jr] = lutTonemaskexp[ble[ir][jr] * 65536.f]; - bufmaskblurcol->a[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.y; - bufmaskblurcol->b[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.x; - } - } - - - if (strumask > 0.f && astool) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufmaskblurcol->L[ir][jr] *= (1.f + blendstru[ir][jr]); - } - } - } - - if (lmasklocalcurve && localmaskutili) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - bufmaskblurcol->L[ir][jr] = 0.5f * lmasklocalcurve[2.f * bufmaskblurcol->L[ir][jr]]; - } - } - - if (shado > 0) { - ImProcFunctions::shadowsHighlights(bufmaskblurcol, true, 1, 0, shado, 40, sk, 0, 60); - } - - int wavelet_level = level_br; - - int minwin = rtengine::min(bfw, bfh); - int maxlevelspot = 9; - - while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { - --maxlevelspot ; - } - - wavelet_level = rtengine::min(wavelet_level, maxlevelspot); - int maxlvl = wavelet_level; -// float contrast = 0.f; - bool wavcurvemask = false; - - if (loclmasCurvecolwav && lmasutilicolwav) { - for (int i = 0; i < 500; i++) { - if (loclmasCurvecolwav[i] != 0.5) { - wavcurvemask = true; - } - } - } - - if (wavcurvemask) { -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; - -#endif - wavelet_decomposition *wdspot = new wavelet_decomposition(bufmaskblurcol->L[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); - if (wdspot->memory_allocation_failed()) { - return; - } - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - float alow = 1.f; - float blow = 0.f; - if (level_hl != level_bl) { - alow = 1.f / (level_hl - level_bl); - blow = -alow * level_bl; - } - - float ahigh = 1.f; - float bhigh = 0.f; - - if (level_hr != level_br) { - ahigh = 1.f / (level_hr - level_br); - bhigh = -ahigh * level_br; - } - - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - int W_L = wdspot->level_W(level); - int H_L = wdspot->level_H(level); - float* const* wav_L = wdspot->level_coeffs(level); - - if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { - float insigma = 0.666f; //SD - float logmax = log(MaxP[level]); //log Max - float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max - float inx = log(insigma); - float iny = log(rapX); - float rap = inx / iny; //koef - float asig = 0.166f / (sigma[level]); - float bsig = 0.5f - asig * mean[level]; - float amean = 0.5f / mean[level]; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - if(loclmasCurvecolwav && lmasutilicolwav) { - float absciss; - float &val = wav_L[dir][i]; - - if (fabsf(val) >= (mean[level] + sigma[level])) { //for max - float valcour = xlogf(fabsf(val)); - float valc = valcour - logmax; - float vald = valc * rap; - absciss = xexpf(vald); - } else if (fabsf(val) >= mean[level]) { - absciss = asig * fabsf(val) + bsig; - } else { - absciss = amean * fabsf(val); - } - - float klev = 1.f; - if (level >= level_hl && level <= level_hr) { - klev = 1.f; - } - - if (level_hl != level_bl) { - if (level >= level_bl && level < level_hl) { - klev = alow * level + blow; - } - } - - if (level_hr != level_br) { - if (level > level_hr && level <= level_br) { - klev = ahigh * level + bhigh; - } - } - - float kc = klev * (loclmasCurvecolwav[absciss * 500.f] - 0.5f); - float amplieffect = kc <= 0.f ? 1.f : 4.f; - - float kinterm = 1.f + amplieffect * kc; - kinterm = kinterm <= 0.f ? 0.01f : kinterm; - - val *= kinterm; - - } - } - } - - } - } - - wdspot->reconstruct(bufmaskblurcol->L[0], 1.f); - delete wdspot; - - } - - if (lochhhmasCurve && HHmaskcurve) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - float huemah = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); - float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); - - - float hh = Color::huelab_to_huehsv2(huemah); - hh += 1.f / 6.f; - - if (hh > 1.f) { - hh -= 1.f; - } - - const float val_HH = float ((0.5f - lochhhmasCurve[500.f * hh])); - const float hhro = 1.5f * val_HH; - float newhr = 0.f; - - if (hhro != 0) { - newhr = huemah + hhro;//we add radians and other dim between 0 1.. always radians but addition "false" - - if (newhr > rtengine::RT_PI_F) { - newhr -= 2 * rtengine::RT_PI_F; - } else if (newhr < -rtengine::RT_PI_F) { - newhr += 2 * rtengine::RT_PI_F; - } - } - - float2 sincosval = xsincosf(newhr); - bufmaskblurcol->a[ir][jr] = clipC(chromah * sincosval.y); - bufmaskblurcol->b[ir][jr] = clipC(chromah * sincosval.x); - - } - } - - if (amountcd > 1.f) { //dynamic range compression for Mask - FattalToneMappingParams fatParams; - fatParams.enabled = true; - fatParams.threshold = 100.f; - fatParams.amount = amountcd; - fatParams.anchor = anchorcd; - int nlev = 1; - Imagefloat *tmpImagefat = nullptr; - tmpImagefat = new Imagefloat(bfw, bfh); - lab2rgb(*bufmaskblurcol, *tmpImagefat, params->icm.workingProfile); - ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0); - rgb2lab(*tmpImagefat, *bufmaskblurcol, params->icm.workingProfile); - delete tmpImagefat; - } - - if (delt) { - const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); - float** rdE = *(rdEBuffer.get()); - - deltaEforMask(rdE, bfw, bfh, bufreserv.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, lp.balance, lp.balanceh); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float rdEval = rdE[ir][jr]; - bufmaskblurcol->L[ir][jr] = bufprov->L[ir][jr] + rdEval * (bufmaskblurcol->L[ir][jr] - bufprov->L[ir][jr]); - bufmaskblurcol->a[ir][jr] = bufprov->a[ir][jr] + rdEval * (bufmaskblurcol->a[ir][jr] - bufprov->a[ir][jr]); - bufmaskblurcol->b[ir][jr] = bufprov->b[ir][jr] + rdEval * (bufmaskblurcol->b[ir][jr] - bufprov->b[ir][jr]); - } - } - } - - struct grad_params gp; - - if ((indic == 0 && lp.strmaexp != 0.f) || (indic ==12 && lp.str_mas != 0.f)) { - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, indic); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufmaskblurcol->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); - } - } - } - - if (lap > 0.f) { - const float *datain = bufmaskblurcol->L[0]; - const std::unique_ptr data_tmp(new float[bfh * bfw]); - - if (!pde) { - ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, bfw, bfh, 200.f * lap); - } else { - ImProcFunctions::retinex_pde(datain, data_tmp.get(), bfw, bfh, 12.f * lap, 1.f, nullptr, 0, 0, 1); - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufmaskblurcol->L[y][x] = data_tmp[y * bfw + x]; - } - } - } - } - - const float radiusb = 1.f / sk; - - if (deltaE || modmask || enaMask || showmaske) { -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufmaskblurcol->L, bufmaskblurcol->L, bfw, bfh, radiusb); - gaussianBlur(bufmaskblurcol->a, bufmaskblurcol->a, bfw, bfh, 1.f + (0.5f * rad) / sk); - gaussianBlur(bufmaskblurcol->b, bufmaskblurcol->b, bfw, bfh, 1.f + (0.5f * rad) / sk); - } - - if (zero || modif || modmask || deltaE || enaMask) { - originalmaskcol->CopyFrom(bufcolorig, multiThread); - blendmask(lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig, original, bufmaskblurcol, originalmaskcol, blendm, blendmab, inv); - } - } -} - -void ImProcFunctions::InverseSharp_Local(float **loctemp, const float hueref, const float lumaref, const float chromaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ -//local sharp - // BENCHFUN - const float ach = lp.trans / 100.f; - const int GW = transformed->W; - const int GH = transformed->H; - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - const bool sharshow = lp.showmasksharmet == 1; - const bool previewshar = lp.showmasksharmet == 2; - - if (lp.colorde == 0) { - lp.colorde = -1;//to avoid black - } - - float ampli = 1.f + std::fabs(lp.colorde); - ampli = 2.f + 0.5f * (ampli - 2.f); - - constexpr float aadark = -1.f; - constexpr float bbdark = 5000.f; - - const std::unique_ptr origblur(new LabImage(GW, GH)); - - float radius = 3.f / sk; -#ifdef _OPENMP - #pragma omp parallel -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const float mindE = 2.f + MINSCOPE * lp.senssha * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.senssha * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - int loy = cy + y; - - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); - const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); - const float huedelta2 = abdelta2 - chrodelta2; - const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); - - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.senssha); - - switch (zone) { - case 0: { // outside selection and outside transition zone => full effect, no transition - const float difL = loctemp[y][x] - original->L[y][x]; - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - - if (sharshow) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = ampli * 5.f * difL * reducdE; - } else if (previewshar) { - float difbdisp = reducdE * 10000.f * lp.colorde; - - if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! - float dark = transformed->L[y][x]; - transformed->L[y][x] = dark * aadark + bbdark; - } - - if (lp.colorde <= 0) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = difbdisp; - } else { - transformed->a[y][x] = -difbdisp; - transformed->b[y][x] = 0.f; - } - - } - - break; - } - - case 1: { // inside transition zone - const float difL = (loctemp[y][x] - original->L[y][x]) * (1.f - localFactor); - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - - if (sharshow) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = ampli * 5.f * difL * reducdE; - } else if (previewshar || lp.prevdE) { - const float difbdisp = reducdE * 10000.f * lp.colorde; - - if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! - const float dark = transformed->L[y][x]; - transformed->L[y][x] = dark * aadark + bbdark; - } - - if (lp.colorde <= 0) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = difbdisp; - } else { - transformed->a[y][x] = -difbdisp; - transformed->b[y][x] = 0.f; - } - } - break; - } - - case 2: { // inside selection => no effect, keep original values - transformed->L[y][x] = original->L[y][x]; - } - } - } - } - } -} - -void ImProcFunctions::Sharp_Local(int call, float **loctemp, int senstype, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ - BENCHFUN - const float ach = lp.trans / 100.f; - const float varsens = senstype == 1 ? lp.senslc : lp.senssha; - const bool sharshow = (lp.showmasksharmet == 1); - const bool previewshar = (lp.showmasksharmet == 2); - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - if (lp.colorde == 0) { - lp.colorde = -1;//to avoid black - } - - float ampli = 1.f + std::fabs(lp.colorde); - ampli = 2.f + 0.5f * (ampli - 2.f); - - float darklim = 5000.f; - float aadark = -1.f; - float bbdark = darklim; - - const int GW = transformed->W; - const int GH = transformed->H; - - const std::unique_ptr origblur(new LabImage(GW, GH)); - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - const float radius = 3.f / sk; - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const int begy = int (lp.yc - lp.lyT); - const int begx = int (lp.xc - lp.lxL); - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - const int loy = cy + y; - const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing - - if (isZone0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - for (int x = 0; x < transformed->W; x++) { - const int lox = cx + x; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - //deltaE - const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); - const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); - const float huedelta2 = abdelta2 - chrodelta2; - const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); - - float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - const float reducview = reducdE; - reducdE *= localFactor; - - float difL; - - if (call == 2) { - difL = loctemp[loy - begy][lox - begx] - original->L[y][x]; - } else { - difL = loctemp[y][x] - original->L[y][x]; - } - - transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); - - if (sharshow) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = ampli * 5.f * difL * reducdE; - } else if (previewshar || lp.prevdE) { - float difbdisp = reducview * 10000.f * lp.colorde; - - if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! - transformed->L[y][x] = transformed->L[y][x] * aadark + bbdark; - } - - if (lp.colorde <= 0) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = difbdisp; - } else { - transformed->a[y][x] = -difbdisp; - transformed->b[y][x] = 0.f; - } - } - } - } - } -} - -void ImProcFunctions::Exclude_Local(float **deltaso, float hueref, float chromaref, float lumaref, float sobelref, float meansobel, const struct local_params & lp, const LabImage * original, LabImage * transformed, const LabImage * rsv, const LabImage * reserv, int cx, int cy, int sk) -{ - - BENCHFUN { - const float ach = lp.trans / 100.f; - const float varsens = lp.sensexclu; - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - - const int GW = transformed->W; - const int GH = transformed->H; - - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - // lumaref *= 327.68f; - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - //sobel - sobelref = rtengine::min(sobelref / 100.f, 60.f); - - const bool recip = sobelref < meansobel && sobelref < lp.stru; - - sobelref = log1p(sobelref); - - const std::unique_ptr origblur(new LabImage(GW, GH)); - - const float radius = 3.f / sk; - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(reserv->L, origblur->L, GW, GH, radius); - gaussianBlur(reserv->a, origblur->a, GW, GH, radius); - gaussianBlur(reserv->b, origblur->b, GW, GH, radius); - - -#ifdef _OPENMP - #pragma omp barrier - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) - { - const int loy = cy + y; - const bool isZone0 = loy > (lp.yc + lp.ly - 1) || loy < lp.yc - lp.lyT; // // -1 fix issue 5554 - - if (isZone0) { // outside selection and outside transition zone => no effect, keep original values - for (int x = 0; x < transformed->W; x++) { - transformed->L[y][x] = original->L[y][x]; - } - - continue; - } - - for (int x = 0; x < transformed->W; x++) { - const int lox = cx + x; - const bool isZone0x = lox > (lp.xc + lp.lx - 1) || lox < lp.xc - lp.lxL; // -1 fix issue 5554 - - if (isZone0x) { // outside selection and outside transition zone => no effect, keep original values - transformed->L[y][x] = original->L[y][x]; - continue; - } - - const int begx = int (lp.xc - lp.lxL); - const int begy = int (lp.yc - lp.lyT); - - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - transformed->L[y][x] = original->L[y][x]; - continue; - } - - float rs = 0.f; - - const float csob = xlogf(1.f + rtengine::min(deltaso[loy - begy][lox - begx] / 100.f, 60.f) + 0.001f); - - if (!recip) { - rs = sobelref / csob; - } else { - rs = csob / sobelref; - } - - float affsob = 1.f; - - if (lp.struexc > 0.f && rs > 0.f) { - const float rsob = 0.002f * lp.struexc * rs; - const float minrs = 1.3f + 0.05f * lp.stru; - - if (rs < minrs) { - affsob = 1.f; - } else { - affsob = 1.f / pow_F((1.f + rsob), SQR(SQR(rs - minrs))); - } - } - - float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); - float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); - float huedelta2 = abdelta2 - chrodelta2; - const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); - const float rL = origblur->L[y][x]; - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - - if (rL > 32.768f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 - if (zone > 0) { - - const float difL = (rsv->L[loy - begy][lox - begx] - original->L[y][x]) * localFactor; - transformed->L[y][x] = CLIP(original->L[y][x] + difL * affsob * reducdE); - - const float difa = (rsv->a[loy - begy][lox - begx] - original->a[y][x]) * localFactor; - transformed->a[y][x] = clipC(original->a[y][x] + difa * affsob * reducdE); - - const float difb = (rsv->b[loy - begy][lox - begx] - original->b[y][x]) * localFactor; - transformed->b[y][x] = clipC(original->b[y][x] + difb * affsob * reducdE); - - } - } - } - } - } - } -} - - - -void ImProcFunctions::transit_shapedetect_retinex(int call, int senstype, LabImage * bufexporig, LabImage * bufmask, LabImage * buforigmas, float **buflight, float **bufchro, const float hueref, const float chromaref, const float lumaref, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ - - BENCHFUN { - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - - - const float ach = lp.trans / 100.f; - const float varsens = lp.sensh; - - int GW = transformed->W; - int GH = transformed->H; - - // const float refa = chromaref * cos(hueref); - // const float refb = chromaref * sin(hueref); - - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - - const bool retishow = ((lp.showmaskretimet == 1 || lp.showmaskretimet == 2)); - const bool previewreti = ((lp.showmaskretimet == 4)); - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - const bool showmas = lp.showmaskretimet == 3 ; - - const std::unique_ptr origblur(new LabImage(GW, GH)); - const float radius = 3.f / sk; - const bool usemaskreti = lp.enaretiMask && senstype == 4 && !lp.enaretiMasktmap; - float strcli = 0.03f * lp.str; - - if (lp.scalereti == 1) - { - strcli = 0.015 * lp.str; - } - -#ifdef _OPENMP - #pragma omp parallel -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - const float previewint = settings->previewselection; - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = ystart; y < yend; y++) - { - const int loy = cy + y; - - for (int x = xstart; x < xend; x++) { - const int lox = cx + x; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - float rL = origblur->L[y][x] / 327.68f; - float dE; - float abdelta2 = 0.f; - float chrodelta2 = 0.f; - float huedelta2 = 0.f; - - if (!usemaskreti) { - abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); - chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); - huedelta2 = abdelta2 - chrodelta2; - dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); - } else { - if (call == 2) { - abdelta2 = SQR(refa - buforigmas->a[y - ystart][x - xstart]) + SQR(refb - buforigmas->b[y - ystart][x - xstart]); - chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y - ystart][x - xstart]) + SQR(buforigmas->b[y - ystart][x - xstart])) - (chromaref * 327.68f)); - huedelta2 = abdelta2 - chrodelta2; - dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y - ystart][x - xstart])); - - } else { - abdelta2 = SQR(refa - buforigmas->a[y][x]) + SQR(refb - buforigmas->b[y][x]); - chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y][x]) + SQR(buforigmas->b[y][x])) - (chromaref * 327.68f)); - huedelta2 = abdelta2 - chrodelta2; - dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y][x])); - } - } - - float cli, clc; - - if (call == 2) { - cli = buflight[y - ystart][x - xstart]; - clc = previewreti ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; - } else { - cli = buflight[y][x]; - clc = previewreti ? settings->previewselection * 100.f : bufchro[y][x]; - - } - - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens) / 100.f; - - cli *= reducdE; - clc *= reducdE; - cli *= (1.f + strcli); - - if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 - if (senstype == 4) {//all except color and light (TODO) and exposure - float lightc; - - if (call == 2) { - lightc = bufexporig->L[y - ystart][x - xstart]; - } else { - lightc = bufexporig->L[y][x]; - } - - float fli = 1.f + cli; - float diflc = lightc * fli - original->L[y][x]; - diflc *= localFactor; - - if (!showmas) { - transformed->L[y][x] = CLIP(original->L[y][x] + diflc); - } else { - if (call == 2) { - - transformed->L[y][x] = bufmask->L[y - ystart][x - xstart]; - } else { - transformed->L[y][x] = bufmask->L[y][x]; - } - } ; - - if (retishow) { - transformed->L[y][x] = CLIP(12000.f + diflc); - } - } - - float fliab = 1.f; - float chra, chrb; - - if (call == 2) { - chra = bufexporig->a[y - ystart][x - xstart]; - chrb = bufexporig->b[y - ystart][x - xstart]; - } else { - chra = bufexporig->a[y][x]; - chrb = bufexporig->b[y][x]; - - } - - if (senstype == 5) { - fliab = 1.f + clc; - } - - const float difa = (chra * fliab - original->a[y][x]) * localFactor; - float difb = (chrb * fliab - original->b[y][x]) * localFactor; - - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - - if (showmas) { - if (call == 2) { - transformed->a[y][x] = bufmask->a[y - ystart][x - xstart]; - transformed->b[y][x] = bufmask->b[y - ystart][x - xstart]; - } else { - transformed->a[y][x] = bufmask->a[y][x]; - transformed->b[y][x] = bufmask->b[y][x]; - - } - } - - if (retishow) { - transformed->a[y][x] = clipC(difa); - transformed->b[y][x] = clipC(difb); - } - - if (previewreti) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = previewint * difb; - } - } - } - } - } - - if (showmas || retishow || previewreti) - { - return; - } - - } -} - - -void ImProcFunctions::transit_shapedetect(int senstype, const LabImage * bufexporig, LabImage * originalmask, float **bufchro, bool HHutili, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ - - BENCHFUN - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - const int bfw = xend - xstart; - const int bfh = yend - ystart; - // printf("h=%f l=%f c=%f s=%f\n", hueref, lumaref, chromaref, sobelref); - const float ach = lp.trans / 100.f; - float varsens = lp.sensex; - - if (senstype == 6 || senstype == 7) //cbdl - { - varsens = lp.senscb; - } else if (senstype == 8) //TM - { - varsens = lp.senstm; - } else if (senstype == 10) //local contrast - { - varsens = lp.senslc; - } - - //sobel //keep in case of, not used - sobelref /= 100.f; - meansobel /= 100.f; - - sobelref = rtengine::min(sobelref, 60.f); - - const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images - - sobelref = log1p(sobelref); - - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - const float previewint = settings->previewselection; - - const bool cbshow = ((lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2) && senstype == 6); - const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); - const bool previewcb = ((lp.showmaskcbmet == 4) && senstype == 6); - const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); - - const std::unique_ptr origblur(new LabImage(bfw, bfh)); - std::unique_ptr origblurmask; - - float radius = 3.f / sk; - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const bool usemaskcb = (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 4) && senstype == 6; - const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; - const bool usemaskall = (usemaskcb || usemasktm); - - if (usemaskall) - { - origblurmask.reset(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); - gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); - gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); - } - } - if (lp.equtm && senstype == 8) //normalize luminance for Tone mapping , at this place we can use for others senstype! - { - float *datain = new float[bfh * bfw]; - float *data = new float[bfh * bfw]; - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - for (int y = ystart; y < yend; y++) - for (int x = xstart; x < xend; x++) { - datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; - data[(y - ystart)* bfw + (x - xstart)] = bufexporig->L[y - ystart][x - xstart]; - } - - normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = ystart; y < yend; y++) - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; - } - - delete [] datain; - delete [] data; - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh; y++) - { - for (int x = 0; x < bfw; x++) { - origblur->L[y][x] = original->L[y + ystart][x + xstart]; - origblur->a[y][x] = original->a[y + ystart][x + xstart]; - origblur->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - - gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); - gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); - gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); - - } - - const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - float atan2Buffer[transformed->W] ALIGNED16; -#endif - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = ystart; y < yend; y++) - { - const int loy = cy + y; - -#ifdef __SSE2__ - - if (HHutili || senstype == 7) { - int i = xstart; - - for (; i < xend - 3; i += 4) { - vfloat av = LVFU(origblur->a[y - ystart][i - xstart]); - vfloat bv = LVFU(origblur->b[y - ystart][i - xstart]); - STVFU(atan2Buffer[i], xatan2f(bv, av)); - } - - for (; i < xend; i++) { - atan2Buffer[i] = xatan2f(origblur->b[y - ystart][i - xstart], origblur->a[y - ystart][i - xstart]); - } - } - -#endif - - for (int x = xstart; x < xend; x++) { - const int lox = cx + x; - - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - float rhue = 0; - - if (HHutili || senstype == 7) { -#ifdef __SSE2__ - rhue = atan2Buffer[x]; -#else - rhue = xatan2f(origblur->b[y - ystart][x - xstart], origblur->a[y - ystart][x - xstart]); -#endif - } - - const float rL = origblur->L[y - ystart][x - xstart] / 327.68f; - float rsob = 0.f; - - if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0 || senstype == 100) && lp.struco > 0.f))) {//keep in case of, not used - const float csob = xlogf(1.f + rtengine::min(blend2[y - ystart][x - xstart] / 100.f, 60.f) + 0.001f); - - float rs; - - if (k) { - rs = sobelref / csob; - } else { - rs = csob / sobelref; - } - - if (rs > 0.f && senstype == 1) { - rsob = 1.1f * lp.struexp * rs; - } else if (rs > 0.f && (senstype == 0 || senstype == 100)) { - rsob = 1.1f * lp.struco * rs; - } - } - - const float dE = rsob + std::sqrt(kab * (SQR(refa - maskptr->a[y - ystart][x - xstart]) + SQR(refb - maskptr->b[y - ystart][x - xstart])) + kL * SQR(refL - maskptr->L[y - ystart][x - xstart])); - const float clc = (previewcb) ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - const float realstrchdE = reducdE * clc; - - if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 - if (zone > 0) { - float factorx = localFactor; - float difL = 0.f; - - if (senstype == 6 || senstype == 8 || senstype == 10) { - difL = (bufexporig->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; - transformed->L[y][x] = CLIP(original->L[y][x] + difL); - } - - if (senstype == 7) { - float difab = bufexporig->L[y - ystart][x - xstart] - std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); - float2 sincosval = xsincosf(rhue); - float difa = difab * sincosval.y; - float difb = difab * sincosval.x; - difa *= factorx * (100.f + realstrchdE) / 100.f; - difb *= factorx * (100.f + realstrchdE) / 100.f; - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - } else { - float flia = 1.f; - float flib = 1.f; - float chra = bufexporig->a[y - ystart][x - xstart]; - float chrb = bufexporig->b[y - ystart][x - xstart]; - - if (senstype == 3 || senstype == 30 || senstype == 8 || senstype == 6 || senstype == 10) { - flia = flib = ((100.f + realstrchdE) / 100.f); - } - - - float difa = chra * flia - original->a[y][x]; - float difb = chrb * flib - original->b[y][x]; - difa *= factorx; - difb *= factorx; - - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - - - if (cbshow || tmshow) { - transformed->L[y][x] = CLIP(12000.f + difL); - transformed->a[y][x] = clipC(difa); - transformed->b[y][x] = clipC(difb); - } else if (previewcb || previewtm || lp.prevdE) { - if (std::fabs(difb) < 500.f) { - difb += difL; - } - - transformed->a[y][x] = 0.f; - transformed->b[y][x] = previewint * difb; - } - } - } - } - } - } - } -} - -void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp, int senstype, struct local_params & lp, LabImage * originalmask, const LUTf& lightCurveloc, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& exlocalcurve, const LUTf& cclocalcurve, float adjustr, bool localcutili, const LUTf& lllocalcurve, bool locallutili, LabImage * original, LabImage * transformed, int cx, int cy, const float hueref, const float chromaref, const float lumaref, int sk) -{ - // BENCHFUN - const float ach = lp.trans / 100.f; - const float facc = (100.f + lp.chro) / 100.f; //chroma factor transition - float varsens = lp.sens; - - if (senstype == 0) { //Color and Light - varsens = lp.sens; - } else if (senstype == 1) { //exposure - varsens = lp.sensex; - } else if (senstype == 2) { //shadows highlight - varsens = lp.senshs; - } - - const int GW = transformed->W; - const int GH = transformed->H; - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - - const std::unique_ptr temp(new LabImage(GW, GH)); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H; y++) { - for (int x = 0; x < transformed->W; x++) { - temp->L[y][x] = original->L[y][x]; - temp->a[y][x] = original->a[y][x]; - temp->b[y][x] = original->b[y][x]; - } - } - - if (senstype == 2) { // Shadows highlight - if (lp.shmeth == 0) { - ImProcFunctions::shadowsHighlights(temp.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); - } else if (lp.shmeth == 1) { - const std::unique_ptr tmpImage(new Imagefloat(GW, GH)); - - lab2rgb(*temp, *tmpImage, params->icm.workingProfile); - - if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB - const float gamtone = params->locallab.spots.at(sp).gamSH; - const float slotone = params->locallab.spots.at(sp).sloSH; - cmsHTRANSFORM dummy = nullptr; - workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); - workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); - } - - if (tonequ) { - tmpImage->normalizeFloatTo1(); - array2D Rtemp(GW, GH, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); - array2D Gtemp(GW, GH, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); - array2D Btemp(GW, GH, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); - tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, sk, multiThread); - tmpImage->normalizeFloatTo65535(); - } - - rgb2lab(*tmpImage, *temp, params->icm.workingProfile); - } - - } else if (senstype == 1) { //exposure - ImProcFunctions::exlabLocal(lp, GH, GW, original, temp.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); - - if (exlocalcurve) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < temp->H; y++) { - for (int x = 0; x < temp->W; x++) { - const float lh = 0.5f * exlocalcurve[2.f * temp->L[y][x]]; // / ((lighn) / 1.9f) / 3.61f; //lh between 0 and 0 50 or more - temp->L[y][x] = lh; - } - } - } - - if (lp.expchroma != 0.f) { - const float ch = (1.f + 0.02f * lp.expchroma) ; - float chprosl; - - if (ch <= 1.f) {//convert data curve near values of slider -100 + 100, to be used after to detection shape - chprosl = 99.f * ch - 99.f; - } else { - constexpr float ampli = 70.f; - chprosl = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H; y++) { - for (int x = 0; x < transformed->W; x++) { - const float epsi = original->L[y][x] == 0.f ? 0.001f : 0.f; - const float rapexp = temp->L[y][x] / (original->L[y][x] + epsi); - temp->a[y][x] *= (1.f + chprosl * rapexp); - temp->b[y][x] *= (1.f + chprosl * rapexp); - } - } - } - } else if (senstype == 0) { //Color and Light curves L C - if (cclocalcurve && localcutili) { // C=f(C) curve -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H; y++) { - for (int x = 0; x < transformed->W; x++) { - //same as in "normal" - const float chromat = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); - constexpr float ampli = 25.f; - const float ch = (cclocalcurve[chromat * adjustr ]) / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more - const float chprocu = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 - temp->a[y][x] = original->a[y][x] * (1.f + 0.01f * chprocu); - temp->b[y][x] = original->b[y][x] * (1.f + 0.01f * chprocu); - - } - } - } - - if (lllocalcurve && locallutili) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H; y++) { - for (int x = 0; x < transformed->W; x++) { - temp->L[y][x] = 0.5f * lllocalcurve[2.f * original->L[y][x]]; - } - } - } - } - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - const std::unique_ptr origblur(new LabImage(GW, GH)); - std::unique_ptr origblurmask; - const bool usemaskcol = (lp.enaColorMaskinv) && senstype == 0; - const bool usemaskexp = (lp.enaExpMaskinv) && senstype == 1; - const bool usemasksh = (lp.enaSHMaskinv) && senstype == 2; - const bool usemaskall = (usemaskcol || usemaskexp || usemasksh); - - float radius = 3.f / sk; - - if (usemaskall) { - origblurmask.reset(new LabImage(GW, GH)); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); - gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); - gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); - } - } - - if (senstype == 1) { - radius = (2.f + 0.2f * lp.blurexp) / sk; - } else if (senstype == 0) { - radius = (2.f + 0.2f * lp.blurcol) / sk; - } else if (senstype == 2) { - radius = (2.f + 0.2f * lp.blurSH) / sk; - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - const int loy = cy + y; - - for (int x = 0; x < transformed->W; x++) { - const float rL = origblur->L[y][x] / 327.68f; - - if (std::fabs(origblur->b[y][x]) < 0.01f) { - origblur->b[y][x] = 0.01f; - } - - constexpr float th_r = 0.01f; - - if (rL > th_r) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 - const int lox = cx + x; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor);//rect not good - } - - //deltaE - float reducdE; - if (zone != 2) { - const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); - const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); - const float huedelta2 = abdelta2 - chrodelta2; - const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); - reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - } - - switch (zone) { - case 2: { // outside selection and outside transition zone => no effect, keep original values - transformed->L[y][x] = original->L[y][x]; - transformed->a[y][x] = original->a[y][x]; - transformed->b[y][x] = original->b[y][x]; - break; - } - - case 1: { // inside transition zone - const float factorx = 1.f - localFactor; - - if (senstype == 0) { - const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; - const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; - float lumnew = original->L[y][x]; - const float difL = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); - const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); - const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); - const float facCa = 1.f + (difa / (original->a[y][x] + epsia)); - const float facCb = 1.f + (difb / (original->b[y][x] + epsib)); - - if (lp.sens < 75.f) { - if ((lp.ligh != 0.f || lp.cont != 0)) { - lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve - } - - const float fac = (100.f + factorx * lp.chro * reducdE) / 100.f; //chroma factor transition - const float diflc = (lumnew - original->L[y][x]) * (reducdE * factorx); - - transformed->L[y][x] = CLIP(1.f * (original->L[y][x] + diflc + difL)); - transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; - transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); - } else { - const float fac = (100.f + factorx * lp.chro) / 100.f; //chroma factor transition - - if ((lp.ligh != 0.f || lp.cont != 0)) { - lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); - } - - const float diflc = (lumnew - original->L[y][x]) * factorx; - transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); - transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa); - transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); - } - } else if (senstype == 1 || senstype == 2) { - const float diflc = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); - const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); - const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); - transformed->L[y][x] = CLIP(original->L[y][x] + diflc); - transformed->a[y][x] = clipC(original->a[y][x] + difa) ; - transformed->b[y][x] = clipC(original->b[y][x] + difb); - } - - break; - } - - case 0: { // inside selection => full effect, no transition - if (senstype == 0) { - const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; - const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; - float lumnew = original->L[y][x]; - const float difL = (temp->L[y][x] - original->L[y][x]) * reducdE; - const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; - const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; - const float facCa = 1.f + difa / (original->a[y][x] + epsia); - const float facCb = 1.f + difb / (original->b[y][x] + epsib); - - if (lp.sens < 75.f) { - if ((lp.ligh != 0.f || lp.cont != 0)) { - lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve - } - - const float fac = (100.f + lp.chro * reducdE) / 100.f; //chroma factor transition - const float diflc = (lumnew - original->L[y][x]) * reducdE; - - transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); - transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; - transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); - } else { - if ((lp.ligh != 0.f || lp.cont != 0)) { - lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); - } - - transformed->L[y][x] = CLIP(lumnew + difL) ; - transformed->a[y][x] = clipC(original->a[y][x] * facc * facCa); - transformed->b[y][x] = clipC(original->b[y][x] * facc * facCb); - } - } else if (senstype == 1 || senstype == 2) { - const float diflc = (temp->L[y][x] - original->L[y][x]) * reducdE; - const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; - const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; - transformed->L[y][x] = CLIP(original->L[y][x] + diflc); - transformed->a[y][x] = clipC(original->a[y][x] + difa) ; - transformed->b[y][x] = clipC(original->b[y][x] + difb); - } - } - } - } - } - } - } -} - -void ImProcFunctions::calc_ref(int sp, LabImage * original, LabImage * transformed, int cx, int cy, int oW, int oH, int sk, double & huerefblur, double & chromarefblur, double & lumarefblur, double & hueref, double & chromaref, double & lumaref, double & sobelref, float & avg, const LocwavCurve & locwavCurveden, bool locwavdenutili) -{ - if (params->locallab.enabled) { - //always calculate hueref, chromaref, lumaref before others operations use in normal mode for all modules exceprt denoise - struct local_params lp; - calcLocalParams(sp, oW, oH, params->locallab, lp, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, locwavCurveden, locwavdenutili); - int begy = lp.yc - lp.lyT; - int begx = lp.xc - lp.lxL; - int yEn = lp.yc + lp.ly; - int xEn = lp.xc + lp.lx; - float avg2 = 0.f; - int nc2 = 0; - - for (int y = 0; y < transformed->H ; y++) //{ - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - int loy = cy + y; - - if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { - avg2 += original->L[y][x]; - nc2++; - } - } - - avg2 /= 32768.f; - avg = avg2 / nc2; -// double precision for large summations - double aveA = 0.; - double aveB = 0.; - double aveL = 0.; - double aveChro = 0.; - double aveAblur = 0.; - double aveBblur = 0.; - double aveLblur = 0.; - double aveChroblur = 0.; - - double avesobel = 0.; -// int precision for the counters - int nab = 0; - int nso = 0; - int nsb = 0; -// single precision for the result - float avA, avB, avL; - int spotSize = 0.88623f * rtengine::max(1, lp.cir / sk); //18 - //O.88623 = std::sqrt(PI / 4) ==> sqare equal to circle - int spotSise2; // = 0.88623f * max (1, lp.cir / sk); //18 - - // very small region, don't use omp here - LabImage *sobelL; - LabImage *deltasobelL; - LabImage *origsob; - LabImage *origblur = nullptr; - LabImage *blurorig = nullptr; - - int spotSi = 1 + 2 * rtengine::max(1, lp.cir / sk); - - if (spotSi < 5) { - spotSi = 5; - } - - spotSise2 = (spotSi - 1) / 2; - - JaggedArray blend3(spotSi, spotSi); - - origsob = new LabImage(spotSi, spotSi); - sobelL = new LabImage(spotSi, spotSi); - deltasobelL = new LabImage(spotSi, spotSi); - bool isdenoise = false; - - if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f) && lp.denoiena) { - isdenoise = true; - } - - if (isdenoise) { - origblur = new LabImage(spotSi, spotSi); - blurorig = new LabImage(spotSi, spotSi); - - for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { - for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { - int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); - - int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); - - int z = y - yb; - int u = x - xb; - origblur->L[z][u] = original->L[y - cy][x - cx]; - origblur->a[z][u] = original->a[y - cy][x - cx]; - origblur->b[z][u] = original->b[y - cy][x - cx]; - - } - } - - float radius = 3.f / sk; - { - //No omp - gaussianBlur(origblur->L, blurorig->L, spotSi, spotSi, radius); - gaussianBlur(origblur->a, blurorig->a, spotSi, spotSi, radius); - gaussianBlur(origblur->b, blurorig->b, spotSi, spotSi, radius); - - } - - for (int y = 0; y < spotSi; y++) { - for (int x = 0; x < spotSi; x++) { - aveLblur += blurorig->L[y][x]; - aveAblur += blurorig->a[y][x]; - aveBblur += blurorig->b[y][x]; - aveChroblur += std::sqrt(SQR(blurorig->b[y - cy][x - cx]) + SQR(blurorig->a[y - cy][x - cx])); - nsb++; - - } - } - } - - //ref for luma, chroma, hue - for (int y = rtengine::max(cy, (int)(lp.yc - spotSize)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSize + 1)); y++) { - for (int x = rtengine::max(cx, (int)(lp.xc - spotSize)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSize + 1)); x++) { - aveL += original->L[y - cy][x - cx]; - aveA += original->a[y - cy][x - cx]; - aveB += original->b[y - cy][x - cx]; - aveChro += std::sqrt(SQR(original->b[y - cy][x - cx]) + SQR(original->a[y - cy][x - cx])); - nab++; - } - } - - //ref for sobel - for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { - for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { - int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); - - int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); - - int z = y - yb; - int u = x - xb; - origsob->L[z][u] = original->L[y - cy][x - cx]; - nso++; - } - } - - const float radius = 3.f / (sk * 1.4f); //0 to 70 ==> see skip - - SobelCannyLuma(sobelL->L, origsob->L, spotSi, spotSi, radius); - int nbs = 0; - - for (int y = 0; y < spotSi ; y ++) - for (int x = 0; x < spotSi ; x ++) { - avesobel += sobelL->L[y][x]; - nbs++; - } - - sobelref = avesobel / nbs; - - delete sobelL; - - delete deltasobelL; - delete origsob; - aveL = aveL / nab; - aveA = aveA / nab; - aveB = aveB / nab; - aveChro = aveChro / nab; - aveChro /= 327.68f; - avA = aveA / 327.68f; - avB = aveB / 327.68f; - avL = aveL / 327.68f; - hueref = xatan2f(avB, avA); //mean hue - - if (isdenoise) { - aveLblur = aveLblur / nsb; - aveChroblur = aveChroblur / nsb; - aveChroblur /= 327.68f; - aveAblur = aveAblur / nsb; - aveBblur = aveBblur / nsb; - float avAblur = aveAblur / 327.68f; - float avBblur = aveBblur / 327.68f; - float avLblur = aveLblur / 327.68f; - huerefblur = xatan2f(avBblur, avAblur); - chromarefblur = aveChroblur; - lumarefblur = avLblur; - } else { - huerefblur = 0.f; - chromarefblur = 0.f; - lumarefblur = 0.f; - } - - chromaref = aveChro; - lumaref = avL; - - // printf("Calcref => sp=%i befend=%i huere=%2.1f chromare=%2.1f lumare=%2.1f sobelref=%2.1f\n", sp, befend, hueref, chromaref, lumaref, sobelref / 100.f); - - if (isdenoise) { - delete origblur; - delete blurorig; - } - - if (lumaref > 95.f) {//to avoid crash - lumaref = 95.f; - } - } -} -//doc fftw3 says optimum is with size 2^a * 3^b * 5^c * 7^d * 11^e * 13^f with e+f = 0 or 1 -//number for size between 18144 and 1 ==> 18000 pixels cover 99% all sensor -const int fftw_size[] = {18144, 18000, 17920, 17836, 17820, 17640, 17600, 17550, 17500, 17496, 17472, 17325, 17280, 17248, 17199, 17150, 17010, 16896, 16875, 16848, 16807, - 16800, 16640, 16632, 16500, 16464, 16384, 16380, 16250, 16200, 16170, 16128, 16038, 16000, 15925, 15876, 15840, 15795, 15750, 15680, 15625, 15600, 15552, 15435, 15400, - 15360, 15309, 15288, 15120, 15092, 15000, 14976, 14850, 14784, 14742, 14700, 14625, 14580, 14560, 14553, 14336, 14406, 14400, 14256, 14175, 14112, 14080, 14040, 14000, 13860, - 13824, 13750, 13720, 13650, 13608, 13500, 13475, 13440, 13377, 13365, 13312, 13230, 13200, 13125, 13122, 13104, 13000, 12960, 12936, 12800, 12740, 12672, 12636, 12600, - 12544, 12500, 12480, 12474, 12375, 12348, 12320, 12288, 12285, 12250, 12150, 12096, 12005, 12000, 11907, 11880, 11760, 11700, 11664, 11648, 11550, 11520, 11466, 11375, - 11340, 11319, 11264, 11250, 11232, 11200, 11088, 11025, 11000, 10976, 10935, 10920, 10800, 10780, 10752, 10692, 10584, 10560, 10530, 10400, 10395, 10368, 10290, 10240, - 10206, 10192, 10125, 10080, 10000, 9984, 9900, 9604, 9856, 9828, 9800, 9750, 9720, 9702, 9625, 9600, 9555, 9504, 9477, 9450, 9408, 9375, 9360, 9261, 9240, - 9216, 9100, 9072, 9000, 8960, 8918, 8910, 8820, 8800, 8775, 8750, 8748, 8736, 8640, 8624, 8575, 8505, 8448, 8424, 8400, 8320, 8316, 8250, 8232, 8192, 8190, 8125, - 8100, 8085, 8064, 8019, 8000, 7938, 7920, 7875, 7840, 7800, 7776, 7700, 7680, 7644, 7560, 7546, 7500, 7488, 7425, 7392, 7371, 7350, 7290, 7280, 7203, 7200, 7168, - 7128, 7056, 7040, 7020, 7000, 6930, 6912, 6875, 6860, 6825, 6804, 6750, 6720, 6656, 6615, 6600, 6561, 6552, 6500, 6480, 6468, 6400, 6370, 6336, 6318, 6300, - 6272, 6250, 6240, 6237, 6174, 6160, 6144, 6125, 6075, 6048, 6000, 5940, 5880, 5850, 5832, 5824, 5775, 5760, 5670, 5632, 5625, 5616, 5600, 5544, 5500, 5488, - 5460, 5400, 5390, 5376, 5346, 5292, 5280, 5265, 5250, 5200, 5184, 5145, 5120, 5103, 5096, 5040, 5000, 4992, 4950, 4928, 4914, 4900, 4875, 4860, 4851, 4802, - 4800, 4752, 4725, 4704, 4680, 4620, 4608, 4550, 4536, 4500, 4480, 4459, 4455, 4410, 4400, 4375, 4374, 4368, 4320, 4312, 4224, 4212, 4200, 4160, 4158, 4125, - 4116, 4096, 4095, 4050, 4032, 4000, 3969, 3960, 3920, 3900, 3888, 3850, 3840, 3822, 3780, 3773, 3750, 3744, 3696, 3675, 3645, 3640, 3600, 3584, 3564, 3528, - 3520, 3510, 3500, 3465, 3456, 3430, 3402, 3375, 3360, 3328, 3300, 3276, 3250, 3240, 3234, 3200, 3185, 3168, 3159, 3150, 3136, 3125, 3120, 3087, 3080, 3072, - 3024, 3000, 2970, 2940, 2925, 2916, 2912, 2880, 2835, 2816, 2808, 2800, 2772, 2750, 2744, 2730, 2700, 2695, 2688, 2673, 2646, 2640, 2625, 2600, 2592, 2560, - 2548, 2520, 2500, 2496, 2475, 2464, 2457, 2450, 2430, 2401, 2400, 2376, 2352, 2340, 2310, 2304, 2275, 2268, 2250, 2240, 2205, 2200, 2187, 2184, 2160, 2156, - 2112, 2106, 2100, 2080, 2079, 2058, 2048, 2025, 2016, 2000, 1980, 1960, 1950, 1944, 1936, 1925, 1920, 1911, 1890, 1875, 1872, 1848, 1820, 1800, 1792, 1782, - 1764, 1760, 1755, 1750, 1728, 1715, 1701, 1680, 1664, 1650, 1638, 1625, 1620, 1617, 1600, 1584, 1575, 1568, 1560, 1540, 1536, 1512, 1500, 1485, 1470, 1458, - 1456, 1440, 1408, 1404, 1400, 1386, 1375, 1372, 1365, 1350, 1344, 1323, 1320, 1300, 1296, 1280, 1274, 1260, 1250, 1248, 1232, 1225, 1215, 1200, 1188, 1176, - 1170, 1155, 1152, 1134, 1125, 1120, 1100, 1092, 1080, 1078, 1056, 1053, 1050, 1040, 1029, 1024, 1008, 1000, 990, 980, 975, 972, 960, 945, 936, 924, 910, 900, - 896, 891, 882, 880, 875, 864, 840, 832, 825, 819, 810, 800, 792, 784, 780, 770, 768, 756, 750, 735, 729, 728, 720, 704, 702, 700, 693, 686, 675, 672, 660, - 650, 648, 640, 637, 630, 625, 624, 616, 600, 594, 588, 585, 576, 567, 560, 550, 546, 540, 539, 528, 525, 520, 512, 504, 500, 495, 490, 486, 480, 468, 462, 455, - 450, 448, 441, 440, 432, 420, 416, 405, 400, 396, 392, 390, 385, 384, 378, 375, 364, 360, 352, 351, 350, 343, 336, 330, 325, 324, 320, 315, 312, 308, 300, 297, - 294, 288, 280, 275, 273, 270, 264, 260, 256, 252, 250, 245, 243, 240, 234, 231, 225, 224, 220, 216, 210, 208, 200, 198, 196, 195, 192, 189, 182, 180, 176, 175, - 168, 165, 162, 160, 156, 154, 150, 147, 144, 143, 140, 135, 132, 130, 128, 126, 125, 120, 117, 112, 110, 108, 105, 104, 100, 99, 98, 96, 91, 90, 88, 84, 81, - 80, 78, 77, 75, 72, 70, 66, 65, 64, 63, 60, 56, 55, 54, 52, 50, 49, 48, 45, 44, 42, 40, 39, 36, 35, 33, 32, 30, 28, 27, 26, 25, 24, 22, 21, 20, 18, 16, 15, - 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 - }; - -int N_fftwsize = sizeof(fftw_size) / sizeof(fftw_size[0]); - - -void optfft(int N_fftwsize, int &bfh, int &bfw, int &bfhr, int &bfwr, struct local_params& lp, int H, int W, int &xstart, int &ystart, int &xend, int ¥d, int cx, int cy) -{ - int ftsizeH = 1; - int ftsizeW = 1; - - for (int ft = 0; ft < N_fftwsize; ft++) { //find best values - if (fftw_size[ft] <= bfh) { - ftsizeH = fftw_size[ft]; - break; - } - } - - for (int ft = 0; ft < N_fftwsize; ft++) { - if (fftw_size[ft] <= bfw) { - ftsizeW = fftw_size[ft]; - break; - } - } - - //optimize with size fftw - bool reduW = false; - bool reduH = false; - - if (ystart == 0 && yend < H) { - lp.ly -= (bfh - ftsizeH); - } else if (ystart != 0 && yend == H) { - lp.lyT -= (bfh - ftsizeH); - } else if (ystart != 0 && yend != H) { - if (lp.ly <= lp.lyT) { - lp.lyT -= (bfh - ftsizeH); - } else { - lp.ly -= (bfh - ftsizeH); - } - } else if (ystart == 0 && yend == H) { - bfhr = ftsizeH; - reduH = true; - } - - if (xstart == 0 && xend < W) { - lp.lx -= (bfw - ftsizeW); - } else if (xstart != 0 && xend == W) { - lp.lxL -= (bfw - ftsizeW); - } else if (xstart != 0 && xend != W) { - if (lp.lx <= lp.lxL) { - lp.lxL -= (bfw - ftsizeW); - } else { - lp.lx -= (bfw - ftsizeW); - } - } else if (xstart == 0 && xend == W) { - bfwr = ftsizeW; - reduW = true; - } - - //new values optimized - ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, H); - xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, W); - bfh = bfhr = yend - ystart; - bfw = bfwr = xend - xstart; - - if (reduH) { - bfhr = ftsizeH; - } - - if (reduW) { - bfwr = ftsizeW; - } - - if (settings->verbose) { - printf("Nyst=%i Nyen=%i lp.yc=%f lp.lyT=%f lp.ly=%f bfh=%i bfhr=%i origH=%i ftsizeH=%i\n", ystart, yend, lp.yc, lp.lyT, lp.ly, bfh, bfhr, H, ftsizeH); - printf("Nxst=%i Nxen=%i lp.xc=%f lp.lxL=%f lp.lx=%f bfw=%i bfwr=%i origW=%i ftsizeW=%i\n", xstart, xend, lp.xc, lp.lxL, lp.lx, bfw, bfwr, W, ftsizeW); - } -} - -void ImProcFunctions::BlurNoise_Local(LabImage *tmp1, LabImage * originalmask, float **bufchro, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ -//local BLUR - BENCHFUN - - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - - const float ach = lp.trans / 100.f; - const int GW = transformed->W; - const int GH = transformed->H; - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; - const bool previewbl = lp.showmaskblmet == 4; - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - if (lp.colorde == 0) { - lp.colorde = -1;//to avoid black - } - - const float ampli = 1.5f + 0.5f * std::fabs(lp.colorde); - - constexpr float darklim = 5000.f; - constexpr float aadark = -1.f; - - const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; - const bool usemaskall = usemaskbl; - const float radius = 3.f / sk; - std::unique_ptr origblurmask; - - const std::unique_ptr origblur(new LabImage(GW, GH)); - - if (usemaskall) { - origblurmask.reset(new LabImage(GW, GH)); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); - gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); - gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); - } - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(original->L, origblur->L, GW, GH, radius); - gaussianBlur(original->a, origblur->a, GW, GH, radius); - gaussianBlur(original->b, origblur->b, GW, GH, radius); - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); - const float mindE = 4.f + MINSCOPE * lp.sensbn * lp.thr;//best usage ?? with blurnoise - const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = ystart; y < yend; y++) { - const int loy = cy + y; - - for (int x = xstart, lox = cx + x; x < xend; x++, lox++) { - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); - const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - chromaref * 327.68f); - const float huedelta2 = abdelta2 - chrodelta2; - const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); - const float clc = previewbl ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; - const float realstrchdE = reducdE * clc; - - float difL = (tmp1->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; - transformed->L[y][x] = CLIP(original->L[y][x] + difL); - const float fli = (100.f + realstrchdE) / 100.f; - const float difa = tmp1->a[y - ystart][x - xstart] * fli - original->a[y][x] * localFactor; - const float difb = tmp1->b[y - ystart][x - xstart] * fli - original->b[y][x] * localFactor; - - if (!lp.actsp) { - transformed->a[y][x] = clipC(original->a[y][x] + difa); - transformed->b[y][x] = clipC(original->b[y][x] + difb); - } - - const float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); - - if (blshow && lp.colorde < 0) { //show modifications with use "b" - // (origshow && lp.colorde < 0) { //original Retinex - transformed->a[y][x] = 0.f; - transformed->b[y][x] = ampli * 8.f * difL * reducdE; - transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); - - } else if (blshow && lp.colorde > 0) {//show modifications without use "b" - if (difL < 1000.f) {//if too low to be view use ab - difL += 0.5f * maxdifab; - } - - transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); - transformed->a[y][x] = clipC(ampli * difa); - transformed->b[y][x] = clipC(ampli * difb); - } else if (previewbl || lp.prevdE) {//show deltaE - const float difbdisp = reducdE * 10000.f * lp.colorde; - - if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! - float dark = transformed->L[y][x]; - transformed->L[y][x] = dark * aadark + darklim; - } - - if (lp.colorde <= 0) { - transformed->a[y][x] = 0.f; - transformed->b[y][x] = difbdisp; - } else { - transformed->a[y][x] = -difbdisp; - transformed->b[y][x] = 0.f; - } - } - } - } - } -} - -void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImage * bufexporig, const LabImage * bufexpfin, LabImage * originalmask, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ - //initialize coordinates - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfw = xend - xstart; - int bfh = yend - ystart; - - int bfhr = bfh; - int bfwr = bfw; - if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - bfh = bfhr; - bfw = bfwr; - - //initialize scope - float varsens = lp.sensex;//exposure - - if (senstype == 0) { //Color and light - varsens = lp.sens; - } else if (senstype == 2) { //vibrance - varsens = lp.sensv; - } else if (senstype == 9) { //shadowshighlight - varsens = lp.senshs; - } else if (senstype == 3) { //softlight - varsens = lp.senssf; - } else if (senstype == 30) { //dehaze - varsens = lp.sensh; - } else if (senstype == 8) { //TM - varsens = lp.senstm; - } else if (senstype == 10) { //local contrast - varsens = lp.senslc; - } else if (senstype == 11) { //encoding log - varsens = lp.sensilog; - } else if (senstype == 20) { //common mask - varsens = lp.sensimas; - } - bool delt = lp.deltaem; - - //sobel - sobelref /= 100.f; - meansobel /= 100.f; - - sobelref = rtengine::min(sobelref, 60.f); - - const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images - - sobelref = log1p(sobelref); - - //references Spot - const float refa = chromaref * cos(hueref) * 327.68f; - const float refb = chromaref * sin(hueref) * 327.68f; - const float refL = lumaref * 327.68f; - - //to preview modifications, scope, mask - const bool expshow = ((lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2) && senstype == 1); - const bool vibshow = ((lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2) && senstype == 2); - const bool colshow = ((lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2) && senstype == 0); - const bool SHshow = ((lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2) && senstype == 9); - const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); - const bool lcshow = ((lp.showmasklcmet == 1 || lp.showmasklcmet == 2) && senstype == 10); - const bool origshow = ((lp.showmasksoftmet == 5) && senstype == 3 && lp.softmet == 1); - - const bool masshow = ((lp.showmask_met == 1) && senstype == 20); - - const bool previewvib = ((lp.showmaskvibmet == 4) && senstype == 2); - const bool previewexp = ((lp.showmaskexpmet == 5) && senstype == 1); - const bool previewcol = ((lp.showmaskcolmet == 5) && senstype == 0); - const bool previewSH = ((lp.showmaskSHmet == 4) && senstype == 9); - const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); - const bool previewlc = ((lp.showmasklcmet == 4) && senstype == 10); - const bool previeworig = ((lp.showmasksoftmet == 6) && senstype == 3 && lp.softmet == 1); - const bool previewmas = ((lp.showmask_met == 3) && senstype == 20); - - float radius = 3.f / sk; - - if (senstype == 1) { - radius = (2.f + 0.2f * lp.blurexp) / sk; - } else if (senstype == 0) { - radius = (2.f + 0.2f * lp.blurcol) / sk; - } else if (senstype == 9) { - radius = (2.f + 0.2f * lp.blurSH) / sk; - } - - const std::unique_ptr origblur(new LabImage(bfw, bfh)); - std::unique_ptr origblurmask; - - //balance deltaE - const float kL = lp.balance / SQR(327.68f); - const float kab = balancedeltaE(lp.balance) / SQR(327.68f); - const float kH = lp.balanceh; - const float kch = balancedeltaE(kH); - - if (lp.colorde == 0) { - lp.colorde = -1;//to avoid black - } - - float ampli = 1.f + std::fabs(lp.colorde); - ampli = 2.f + 0.5f * (ampli - 2.f); - - float darklim = 5000.f; - float aadark = -1.f; - float bbdark = darklim; - - const bool usemaskvib = (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 4) && senstype == 2; - const bool usemaskexp = (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 5) && senstype == 1; - const bool usemaskcol = (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 5) && senstype == 0; - const bool usemaskSH = (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 4) && senstype == 9; - const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; - const bool usemasklc = (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 4) && senstype == 10; - const bool usemaskmas = (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 3) && senstype == 20; - const bool usemaskall = (usemaskexp || usemaskvib || usemaskcol || usemaskSH || usemasktm || usemasklc || usemaskmas); - - //blur a little mask - if (usemaskall) { - origblurmask.reset(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); - gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); - gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); - } - } - - if (lp.equtm && senstype == 8) { //normalize luminance for Tone mapping , at this place we can use for others senstype! - float *datain = new float[bfh * bfw]; - float *data = new float[bfh * bfw]; - -#ifdef _OPENMP - #pragma omp parallel for -#endif - for (int y = ystart; y < yend; y++) - for (int x = xstart; x < xend; x++) { - datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; - data[(y - ystart)* bfw + (x - xstart)] = bufexpfin->L[y - ystart][x - xstart]; - } - - normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); -#ifdef _OPENMP - #pragma omp parallel for -#endif - for (int y = ystart; y < yend; y++) - for (int x = xstart; x < xend; x++) { - bufexpfin->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; - } - - delete [] datain; - delete [] data; - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - origblur->L[y][x] = original->L[y + ystart][x + xstart]; - origblur->a[y][x] = original->a[y + ystart][x + xstart]; - origblur->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - - gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); - gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); - gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); - - } - - - //choice between original and mask - const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); - - //parameters deltaE - const float mindE = 2.f + MINSCOPE * varsens * lp.thr; - const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ -// float atan2Buffer[transformed->W] ALIGNED16;//keep in case of -#endif - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < bfh; y++) { - - const int loy = y + ystart + cy; -#ifdef __SSE2__ - /* //keep in case of - int i = 0; - - for (; i < bfw - 3; i += 4) { - vfloat av = LVFU(maskptr->a[y][i]); - vfloat bv = LVFU(maskptr->b[y][i]); - STVFU(atan2Buffer[i], xatan2f(bv, av)); - } - - for (; i < bfw; i++) { - atan2Buffer[i] = xatan2f(maskptr->b[y][i], maskptr->a[y][i]); - } - */ -#endif - - for (int x = 0; x < bfw; x++) { - const int lox = x + xstart + cx; - int zone; - float localFactor = 1.f; - const float achm = lp.trans / 100.f; - - //calculate transition - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - -// float hueh = 0; -#ifdef __SSE2__ -// hueh = atan2Buffer[x]; -#else -// hueh = xatan2f(maskptr->b[y][x], maskptr->a[y][x]); -#endif - - float rsob = 0.f; - - //calculate additive sobel to deltaE - if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0) && lp.struco > 0.f))) { - const float csob = xlogf(1.f + rtengine::min(blend2[y][x] / 100.f, 60.f) + 0.001f); - - float rs; - - if (k) { - rs = sobelref / csob; - } else { - rs = csob / sobelref; - } - - if (rs > 0.f && senstype == 1) { - rsob = 1.1f * lp.struexp * rs; - } else if (rs > 0.f && (senstype == 0)) { - rsob = 1.1f * lp.struco * rs; - } - } - - //deltaE - float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); - float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); - float huedelta2 = abdelta2 - chrodelta2; - - const float dE = rsob + std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); - //reduction action with deltaE - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - - float cli = (bufexpfin->L[y][x] - bufexporig->L[y][x]); - float cla = (bufexpfin->a[y][x] - bufexporig->a[y][x]); - float clb = (bufexpfin->b[y][x] - bufexporig->b[y][x]); - - if (delt) { - cli = bufexpfin->L[y][x] - original->L[y + ystart][x + xstart]; - cla = bufexpfin->a[y][x] - original->a[y + ystart][x + xstart]; - clb = bufexpfin->b[y][x] - original->b[y + ystart][x + xstart]; - } - if(lp.blwh) { - cla = 0.f; - clb = 0.f; - } - - // const float previewint = settings->previewselection; - - const float realstrdE = reducdE * cli; - const float realstradE = reducdE * cla; - const float realstrbdE = reducdE * clb; - - float factorx = localFactor; - - if (zone > 0) { - //simplified transformed with deltaE and transition - transformed->L[y + ystart][x + xstart] = clipLoc(original->L[y + ystart][x + xstart] + factorx * realstrdE); - float diflc = factorx * realstrdE; - transformed->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart] + factorx * realstradE); - const float difa = factorx * realstradE; - transformed->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart] + factorx * realstrbdE); - const float difb = factorx * realstrbdE; - float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); - - if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde < 0) { //show modifications with use "b" - // (origshow && lp.colorde < 0) { //original Retinex - transformed->a[y + ystart][x + xstart] = 0.f; - transformed->b[y + ystart][x + xstart] = ampli * 8.f * diflc * reducdE; - transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); - - } else if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow || masshow) && lp.colorde > 0) {//show modifications without use "b" - if (diflc < 1000.f) {//if too low to be view use ab - diflc += 0.5f * maxdifab; - } - - transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); - transformed->a[y + ystart][x + xstart] = clipC(ampli * difa); - transformed->b[y + ystart][x + xstart] = clipC(ampli * difb); - } else if (previewexp || previewvib || previewcol || previewSH || previewtm || previewlc || previeworig || previewmas || lp.prevdE) {//show deltaE - float difbdisp = reducdE * 10000.f * lp.colorde; - - if (transformed->L[y + ystart][x + xstart] < darklim) { //enhance dark luminance as user can see! - float dark = transformed->L[y + ystart][x + xstart]; - transformed->L[y + ystart][x + xstart] = dark * aadark + bbdark; - } - - if (lp.colorde <= 0) { - transformed->a[y + ystart][x + xstart] = 0.f; - transformed->b[y + ystart][x + xstart] = difbdisp; - } else { - transformed->a[y + ystart][x + xstart] = -difbdisp; - transformed->b[y + ystart][x + xstart] = 0.f; - } - } - } - } - } - } -} - - - - -void ImProcFunctions::exposure_pde(float * dataor, float * datain, float * dataout, int bfw, int bfh, float thresh, float mod) -/* Jacques Desmis July 2019 -** adapted from Ipol Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ -*/ -{ - - BENCHFUN -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_init_threads(); - fftwf_plan_with_nthreads(omp_get_max_threads()); - } - -#endif - float *data_fft, *data_tmp, *data; - - if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); - - if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); - fftwf_execute(dct_fw); - - fftwf_free(data_tmp); - - /* solve the Poisson PDE in Fourier space */ - /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ - ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); - - const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); - fftwf_execute(dct_bw); - fftwf_destroy_plan(dct_fw); - fftwf_destroy_plan(dct_bw); - fftwf_free(data_fft); - fftwf_cleanup(); - -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_cleanup_threads(); - } -#endif - - normalize_mean_dt(data, dataor, bfw * bfh, mod, 1.f); - { - -#ifdef _OPENMP - #pragma omp parallel for -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - dataout[y * bfw + x] = clipLoc(data[y * bfw + x]); - } - } - } - - fftwf_free(data); -} - -void ImProcFunctions::fftw_convol_blur(float * input, float * output, int bfw, int bfh, float radius, int fftkern, int algo) -{ - /* - ** Jacques Desmis june 2019 - inspired by Copyright 2013 IPOL Image Processing On Line http://www.ipol.im/ - ** when I read documentation on various FFT blur we found 2 possibilities - ** 0) kernel gauss is used with "normal" data - ** 1) kernel gauss is used with FFT - ** fftkern allows to change 0) or 1) and test It seems the good solution is with 0, but I keep the code in case of ?? - - ** input real data to blur - ** output real data blurred with radius - ** bfw bfh width and high area - ** radius = sigma for kernel - ** n_x n_y relative width and high for kernel - ** Gaussian blur is given by G(x,y) = (1/2*PI*sigma) * exp(-(x2 + y2) / 2* sigma2) - ** its traduction in Fourier transform is G(x,y) = exp((-sigma)*(PI * x2 + PI * y2)), for some authors it is not sigma but sigma^2..I have tried...huge differences with Gaussianblur - ** after several test the only result that works very well is with fftkern = 0 and algo = 0, and as there is differences with Gaussianblur, I put an empirical correction in Ipretinex and Iplocalcontrast - ** you can enabled or disabled this function with rtsettings.fftwsigma in options. By default empirical formula is disabled - ** in fact no importance....if it is this function (for sigma) or another... we are not in research :) - */ - BENCHFUN - -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_init_threads(); - fftwf_plan_with_nthreads(omp_get_max_threads()); - } -#endif - - - float *out; //for FFT data - float *kern = nullptr;//for kernel gauss - float *outkern = nullptr;//for FFT kernel - fftwf_plan p; - fftwf_plan pkern;//plan for FFT - int image_size, image_sizechange; - float n_x = 1.f; - float n_y = 1.f;//relative coordinates for kernel Gauss - float radsig = 1.f; - - out = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT - - if (fftkern == 1) { //allocate memory FFT if kernel fft = 1 - // kern = new float[bfw * bfh]; - kern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT - outkern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT - } - - /*compute the Fourier transform of the input data*/ - - p = fftwf_plan_r2r_2d(bfh, bfw, input, out, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE);//FFT 2 dimensions forward FFTW_MEASURE FFTW_ESTIMATE - - fftwf_execute(p); - fftwf_destroy_plan(p); - - /*define the gaussian constants for the convolution kernel*/ - if (algo == 0) { - n_x = rtengine::RT_PI / (double) bfw; //ipol - n_y = rtengine::RT_PI / (double) bfh; - } else if (algo == 1) { - n_x = 1.f / bfw; //gauss - n_y = 1.f / bfh; - radsig = 1.f / (2.f * rtengine::RT_PI * radius * radius);//gauss - } - - n_x = n_x * n_x; - n_y = n_y * n_y; - - image_size = bfw * bfh; - image_sizechange = 4 * image_size; - - if (fftkern == 1) { //convolution with FFT kernel -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int j = 0; j < bfh; j++) { - int index = j * bfw; - - for (int i = 0; i < bfw; i++) - if (algo == 0) { - kern[ i + index] = exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //calculate Gauss kernel Ipol formula - } else if (algo == 1) { - kern[ i + index] = radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula - } - } - - /*compute the Fourier transform of the kernel data*/ - pkern = fftwf_plan_r2r_2d(bfh, bfw, kern, outkern, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE); //FFT 2 dimensions forward - fftwf_execute(pkern); - fftwf_destroy_plan(pkern); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int j = 0; j < bfh; j++) { - int index = j * bfw; - - for (int i = 0; i < bfw; i++) { - out[i + index] *= outkern[i + index]; //apply Gauss kernel with FFT - } - } - - fftwf_free(outkern); - fftwf_free(kern); - - // delete [] kern; - - } else if (fftkern == 0) {//without FFT kernel - if (algo == 0) { -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int j = 0; j < bfh; j++) { - int index = j * bfw; - - for (int i = 0; i < bfw; i++) { - out[i + index] *= exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //apply Gauss kernel without FFT - some authors says radius*radius but differences with Gaussianblur - } - } - } else if (algo == 1) { -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int j = 0; j < bfh; j++) { - int index = j * bfw; - - for (int i = 0; i < bfw; i++) { - out[i + index] *= radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula - } - } - } - } - - p = fftwf_plan_r2r_2d(bfh, bfw, out, output, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE);//FFT 2 dimensions backward - fftwf_execute(p); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int index = 0; index < image_size; index++) { //restore data - output[index] /= image_sizechange; - } - - fftwf_destroy_plan(p); - fftwf_free(out); - -#ifdef RT_FFTW3F_OMP - if (multiThread) { - fftwf_cleanup_threads(); - } -#endif -} - -void ImProcFunctions::fftw_convol_blur2(float **input2, float **output2, int bfw, int bfh, float radius, int fftkern, int algo) -{ - MyMutex::MyLock lock(*fftwMutex); - - float *input = nullptr; - - if (NULL == (input = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - float *output = nullptr; - - if (NULL == (output = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - input[y * bfw + x] = input2[y][x]; - } - } - - ImProcFunctions::fftw_convol_blur(input, output, bfw, bfh, radius, fftkern, algo); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - output2[y][x] = output[y * bfw + x]; - } - } - - fftwf_free(input); - fftwf_free(output); -} - - -void ImProcFunctions::fftw_tile_blur(int GW, int GH, int tilssize, int max_numblox_W, int min_numblox_W, float **tmp1, int numThreads, double radius) -{ - BENCHFUN - float epsil = 0.001f / (tilssize * tilssize); - fftwf_plan plan_forward_blox[2]; - fftwf_plan plan_backward_blox[2]; - - array2D tilemask_in(tilssize, tilssize); - array2D tilemask_out(tilssize, tilssize); - - float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); - float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); - - int nfwd[2] = {tilssize, tilssize}; - - //for DCT: - fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; - fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; - - // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit - plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - fftwf_free(Lbloxtmp); - fftwf_free(fLbloxtmp); - const int border = rtengine::max(2, tilssize / 16); - - for (int i = 0; i < tilssize; ++i) { - float i1 = abs((i > tilssize / 2 ? i - tilssize + 1 : i)); - float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); - float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); - - for (int j = 0; j < tilssize; ++j) { - float j1 = abs((j > tilssize / 2 ? j - tilssize + 1 : j)); - tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; - tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; - - } - } - - float *LbloxArray[numThreads]; - float *fLbloxArray[numThreads]; - - const int numblox_W = ceil((static_cast(GW)) / offset) + 2; - const int numblox_H = ceil((static_cast(GH)) / offset) + 2; - - array2D Lresult(GW, GH, ARRAY2D_CLEAR_DATA); - array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks - - for (int i = 0; i < numThreads; ++i) { - LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); - fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); - } - -#ifdef _OPENMP - int masterThread = omp_get_thread_num(); -#endif -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef _OPENMP - int subThread = masterThread * 1 + omp_get_thread_num(); -#else - int subThread = 0; -#endif - float *Lblox = LbloxArray[subThread]; - float *fLblox = fLbloxArray[subThread]; - float pBuf[GW + tilssize + 2 * offset] ALIGNED16; -#ifdef _OPENMP - #pragma omp for -#endif - for (int vblk = 0; vblk < numblox_H; ++vblk) { - - int top = (vblk - 1) * offset; - float * datarow = pBuf + offset; - - for (int i = 0; i < tilssize; ++i) { - int row = top + i; - int rr = row; - - if (row < 0) { - rr = rtengine::min(-row, GH - 1); - } else if (row >= GH) { - rr = rtengine::max(0, 2 * GH - 2 - row); - } - - for (int j = 0; j < GW; ++j) { - datarow[j] = (tmp1[rr][j]); - } - - for (int j = -1 * offset; j < 0; ++j) { - datarow[j] = datarow[rtengine::min(-j, GW - 1)]; - } - - for (int j = GW; j < GW + tilssize + offset; ++j) { - datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; - }//now we have a padded data row - - for (int hblk = 0; hblk < numblox_W; ++hblk) { - int left = (hblk - 1) * offset; - int indx = (hblk) * tilssize; //index of block in malloc - - if (top + i >= 0 && top + i < GH) { - int j; - - for (j = 0; j < rtengine::min((-left), tilssize); ++j) { - Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - - for (; j < rtengine::min(tilssize, GW - left); ++j) { - Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; - } - - for (; j < tilssize; ++j) { - Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - } else { - for (int j = 0; j < tilssize; ++j) { - Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - } - - } - - }//end of filling block row - - //fftwf_print_plan (plan_forward_blox); - if (numblox_W == max_numblox_W) { - fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles - } else { - fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles - } - - const float n_xy = rtengine::SQR(rtengine::RT_PI / tilssize); - - //radius = 30.f; - for (int hblk = 0; hblk < numblox_W; ++hblk) { - int blkstart = hblk * tilssize * tilssize; - - for (int j = 0; j < tilssize; j++) { - int index = j * tilssize; - - for (int i = 0; i < tilssize; i++) { - fLblox[blkstart + index + i] *= exp((float)(-radius) * (n_xy * rtengine::SQR(i) + n_xy * rtengine::SQR(j))); - } - } - }//end of horizontal block loop - - //now perform inverse FT of an entire row of blocks - if (numblox_W == max_numblox_W) { - fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT - } else { - fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT - } - - int topproc = (vblk - 1) * offset; - const int numblox_W = ceil((static_cast(GW)) / offset); - const float DCTnorm = 1.0f / (4 * tilssize * tilssize); //for DCT - - int imin = rtengine::max(0, - topproc); - int bottom = rtengine::min(topproc + tilssize, GH); - int imax = bottom - topproc; - - for (int i = imin; i < imax; ++i) { - for (int hblk = 0; hblk < numblox_W; ++hblk) { - int left = (hblk - 1) * offset; - int right = rtengine::min(left + tilssize, GW); - int jmin = rtengine::max(0, -left); - int jmax = right - left; - int indx = hblk * tilssize; - - for (int j = jmin; j < jmax; ++j) { - Lresult[topproc + i][left + j] += tilemask_out[i][j] * Lblox[(indx + i) * tilssize + j] * DCTnorm; //for DCT - } - } - } - }//end of vertical block loop - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - tmp1[i][j] = Lresult[i][j] / totwt[i][j]; - tmp1[i][j] = clipLoc(tmp1[i][j]); - } - } - - for (int i = 0; i < numThreads; ++i) { - fftwf_free(LbloxArray[i]); - fftwf_free(fLbloxArray[i]); - } - - fftwf_destroy_plan(plan_forward_blox[0]); - fftwf_destroy_plan(plan_backward_blox[0]); - fftwf_destroy_plan(plan_forward_blox[1]); - fftwf_destroy_plan(plan_backward_blox[1]); - fftwf_cleanup(); -} - -void ImProcFunctions::wavcbd(wavelet_decomposition &wdspot, int level_bl, int maxlvl, - const LocwavCurve& locconwavCurve, bool locconwavutili, float sigm, float offs, float chromalev, int sk) -{ - if (locconwavCurve && locconwavutili) { - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; -#endif - Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) -#endif - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - const int W_L = wdspot.level_W(level); - const int H_L = wdspot.level_H(level); - float mea[9]; - - float* const* wav_L = wdspot.level_coeffs(level); - //offset - float rap = offs * mean[level] - 2.f * sigm * sigma[level]; - - if (rap > 0.f) { - mea[0] = rap; - } else { - mea[0] = mean[level] / 6.f; - } - - rap = offs * mean[level] - sigm * sigma[level]; - - if (rap > 0.f) { - mea[1] = rap; - } else { - mea[1] = mean[level] / 2.f; - } - - mea[2] = offs * mean[level]; // 50% data - mea[3] = offs * mean[level] + sigm * sigma[level] / 2.f; - mea[4] = offs * mean[level] + sigm * sigma[level]; //66% - mea[5] = offs * mean[level] + sigm * 1.2f * sigma[level]; - mea[6] = offs * mean[level] + sigm * 1.5f * sigma[level]; // - mea[7] = offs * mean[level] + sigm * 2.f * sigma[level]; //95% - mea[8] = offs * mean[level] + sigm * 2.5f * sigma[level]; //99% - - float cpMul = 200.f * (locconwavCurve[level * 55.5f] - 0.5f); - - if (cpMul > 0.f) { - cpMul *= 3.5f; - } - - cpMul /= sk; - - for (int i = 0; i < W_L * H_L; i++) { - const float WavCL = std::fabs(wav_L[dir][i]); - float beta; - - //reduction amplification: max action between mean / 2 and mean + sigma - // arbitrary coefficient, we can add a slider !! - if (WavCL < mea[0]) { - beta = 0.6f; //preserve very low contrast (sky...) - } else if (WavCL < mea[1]) { - beta = 0.8f; - } else if (WavCL < mea[2]) { - beta = 1.f; //standard - } else if (WavCL < mea[3]) { - beta = 1.f; - } else if (WavCL < mea[4]) { - beta = 0.8f; //+sigma - } else if (WavCL < mea[5]) { - beta = 0.6f; - } else if (WavCL < mea[6]) { - beta = 0.4f; - } else if (WavCL < mea[7]) { - beta = 0.2f; // + 2 sigma - } else if (WavCL < mea[8]) { - beta = 0.1f; - } else { - beta = 0.0f; - } - - const float alpha = rtengine::max((1024.f + 15.f * cpMul * beta) / 1024.f, 0.02f) ; - wav_L[dir][i] *= alpha * chromalev; - } - } - } - } -} - -void ImProcFunctions::Compresslevels(float **Source, int W_L, int H_L, float compression, float detailattenuator, float thres, float mean, float maxp, float meanN, float maxN, float madL) -{ - //J.Desmis 12-2019 - - float exponent; - - if (detailattenuator > 0.f && detailattenuator < 0.05f) { - const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; //0.69315 = log(2) - exponent = 1.2f * xlogf(-betemp); - exponent /= 20.f; - } else if (detailattenuator >= 0.05f && detailattenuator < 0.25f) { - const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; - exponent = 1.2f * xlogf(-betemp); - exponent /= (-75.f * detailattenuator + 23.75f); - } else if (detailattenuator >= 0.25f) { - const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; - exponent = 1.2f * xlogf(-betemp); - exponent /= (-2.f * detailattenuator + 5.5f); - } else { - exponent = (compression - 1.0f) / 20.f; - } - - float ap = (thres - 1.f) / (maxp - mean); - float bp = 1.f - ap * mean; - ap *= exponent; - bp *= exponent; - - float a0 = (1.33f * thres - 1.f) / (1.f - mean); - float b0 = 1.f - a0 * mean; - a0 *= exponent; - b0 *= exponent; - - float apn = (thres - 1.f) / (maxN - meanN); - float bpn = 1.f - apn * meanN; - apn *= -exponent; - bpn *= exponent; - - float a0n = (1.33f * thres - 1.f) / (1.f - meanN); - float b0n = 1.f - a0n * meanN; - a0n *= -exponent; - b0n *= exponent; - - madL *= 0.05f; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - const vfloat apv = F2V(ap); - const vfloat bpv = F2V(bp); - const vfloat a0v = F2V(a0); - const vfloat b0v = F2V(b0); - const vfloat apnv = F2V(apn); - const vfloat bpnv = F2V(bpn); - const vfloat a0nv = F2V(a0n); - const vfloat b0nv = F2V(b0n); - const vfloat madLv = F2V(madL); - const vfloat meanv = F2V(mean); - const vfloat onev = F2V(1.f); -#endif -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < H_L; y++) { - int x = 0; -#ifdef __SSE2__ - for (; x < W_L - 3; x += 4) { - vfloat exponev = onev; - vfloat valv = LVFU(Source[y][x]); - const vmask mask1v = vmaskf_ge(valv, ZEROV); - const vmask mask2v = vmaskf_gt(vself(mask1v, valv, -valv), meanv); - const vfloat av = vself(mask2v, vself(mask1v, apv, apnv), vself(mask1v, a0v, a0nv)); - const vfloat bv = vself(mask2v, vself(mask1v, bpv, bpnv), vself(mask1v, b0v, b0nv)); - exponev += av * valv + bv; - valv = vself(mask1v, valv, -valv); - const vfloat multv = vself(mask1v, onev, -onev); - const vfloat resultv = multv * xexpf(xlogf(valv + madLv) * exponev); - STVFU(Source[y][x], resultv); - } -#endif - for (; x < W_L; x++) { - float expone = 1.f; - - if (Source[y][x] >= 0.f) { - if (Source[y][x] > mean) { - expone += ap * Source[y][x] + bp; - } else { - expone += a0 * Source[y][x] + b0; - } - - Source[y][x] = xexpf(xlogf(Source[y][x] + madL) * expone); - } else { - if (-Source[y][x] > mean) { - expone += apn * Source[y][x] + bpn; - } else { - expone += a0n * Source[y][x] + b0n; - } - - Source[y][x] = -xexpf(xlogf(-Source[y][x] + madL) * expone); - } - } - } - } -} - -void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavelet_decomposition& wdspot, int level_bl, int maxlvl, - const LocwavCurve & loclevwavCurve, bool loclevwavutili, - const LocwavCurve & loccompwavCurve, bool loccompwavutili, - const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, - float radlevblur, int process, float chromablu, float thres, float sigmadc, float deltad) -{ - BENCHFUN - const int W_L = wdspot.level_W(0); - const int H_L = wdspot.level_H(0); - - const std::unique_ptr beta(new float[W_L * H_L]); - -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; -#endif - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - - if (process == 1 && loclevwavCurve && loclevwavutili) { //blur - StopWatch Stop1("blur"); - array2D templevel(W_L, H_L); - for (int dir = 1; dir < 4; ++dir) { - for (int level = level_bl; level < maxlvl; ++level) { - const auto WavL = wdspot.level_coeffs(level)[dir]; - const float effect = lp.sigmabl; - constexpr float offs = 1.f; - float mea[10]; - calceffect(level, mean, sigma, mea, effect, offs); - constexpr int lutSize = 100; - const float lutMax = ceil(mea[9]); - const float lutDiff = lutMax / lutSize; - std::vector lutVals(lutSize); - std::vector inVals({0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.5f, 0.3f, 0.2f, 0.1f, 0.05f}); - int jStart = 1; - for (int i = 0; i < 100; ++i) { - const float val = i * lutDiff; - if (val < mea[0]) { - // still < first value => no interpolation - lutVals[i] = inVals[0]; - } else { - for (int j = jStart; j < 10; ++j) { - if (val == mea[j]) { - // exact match => no interpolation - lutVals[i] = inVals[j]; - ++jStart; - break; - } else if (val < mea[j]) { - // interpolate - const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); - lutVals[i] = intp(dist, inVals[j], inVals[j - 1]); - break; - } else { - lutVals[i] = inVals[10]; - } - } - } - } - const LUTf meaLut(lutVals); - constexpr float lutFactor = 1.f / lutDiff; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - beta[co] = meaLut[std::fabs(WavL[co]) * lutFactor]; - } - - const float klev = 0.25f * loclevwavCurve[level * 55.5f]; - float* src[H_L]; - for (int i = 0; i < H_L; ++i) { - src[i] = &wdspot.level_coeffs(level)[dir][i * W_L]; - } -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(src, templevel, W_L, H_L, radlevblur * klev * chromablu); - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - int j = y * W_L + x; - WavL[j] = intp(beta[j], templevel[y][x], WavL[j]); - } - } - } - } - } else if (process == 2 && loccompwavCurve && loccompwavutili) { //Directional contrast - for (int dir = 1; dir < 4; ++dir) { - for (int level = level_bl; level < maxlvl; ++level) { - const auto WavL = wdspot.level_coeffs(level)[dir]; - const float effect = sigmadc; - constexpr float offs = 1.f; - float mea[10]; - calceffect(level, mean, sigma, mea, effect, offs); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(WavL[co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.7f; - } else if (WavCL < mea[7]) { - beta[co] = 0.5f; - } else if (WavCL < mea[8]) { - beta[co] = 0.3f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.2f; - } else { - beta[co] = 0.1f; - } - } - - const int iteration = deltad; - const int itplus = 7 + iteration; - const int itmoins = 7 - iteration; - const int med = maxlvl / 2; - int it; - - if (level < med) { - it = itmoins; - } else if (level == med) { - it = 7; - } else { - it = itplus; - } - - const float itf = it; - const float factor = dir < 3 ? 0.3f : -0.6f; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - const vfloat c327d68v = F2V(327.68f); - const vfloat factorv = F2V(factor); - const vfloat sixv = F2V(6.f); - const vfloat zd5v = F2V(0.5f); - const vfloat onev = F2V(1.f); - const vfloat itfv = F2V(itf); -#endif -#ifdef _OPENMP - #pragma omp for -#endif - for (int i = 0; i < H_L; ++i) { - int j = 0; -#ifdef __SSE2__ - for (; j < W_L - 3; j += 4) { - const vfloat LL100v = LC2VFU(tmp[i * 2][j * 2]) / c327d68v; - const vfloat kbav = factorv * (loccompwavCurve[sixv * LL100v] - zd5v); //k1 between 0 and 0.5 0.5==> 1/6=0.16 - STVFU(WavL[i * W_L + j], LVFU(WavL[i * W_L + j]) * pow_F(onev + kbav * LVFU(beta[i * W_L + j]), itfv)); - } -#endif - for (; j < W_L; ++j) { - const float LL100 = tmp[i * 2][j * 2] / 327.68f; - const float kba = factor * (loccompwavCurve[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16 - WavL[i * W_L + j] *= pow_F(1.f + kba * beta[i * W_L + j], itf); - } - } - } - } - } - } else if (process == 3 && loccomprewavCurve && loccomprewavutili) { //Dynamic compression wavelet - float madL[10][3]; - array2D templevel(W_L, H_L); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) -#endif - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - const int W_L = wdspot.level_W(level); - const int H_L = wdspot.level_H(level); - const auto wav_L = wdspot.level_coeffs(level)[dir]; - madL[level][dir - 1] = Mad(wav_L, W_L * H_L);//evaluate noise by level - } - } - - for (int dir = 1; dir < 4; ++dir) { - for (int level = level_bl; level < maxlvl; ++level) { - const auto WavL = wdspot.level_coeffs(level)[dir]; - const float effect = lp.sigmadr; - constexpr float offs = 1.f; - float mea[10]; - calceffect(level, mean, sigma, mea, effect, offs); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(WavL[co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.65f; - } else if (WavCL < mea[7]) { - beta[co] = 0.5f; - } else if (WavCL < mea[8]) { - beta[co] = 0.4f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.25f; - } else { - beta[co] = 0.1f; - } - } - - float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); - if (klev < 0.f) { - klev *= 2.6666f;//compression increase contraste - } else { - klev *= 4.f;//dilatation reduce contraste - detailattenuator - } - const float compression = expf(-klev); - const float detailattenuator = std::max(klev, 0.f); - - const auto wav_L = wdspot.level_coeffs(level)[dir]; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - int j = y * W_L + x; - templevel[y][x] = wav_L[j]; - } - } - - Compresslevels(templevel, W_L, H_L, compression, detailattenuator, thres, mean[level], MaxP[level], meanN[level], MaxN[level], madL[level][dir - 1]); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - int j = y * W_L + x; - wav_L[j] = intp(beta[j], templevel[y][x], wav_L[j]); - } - } - } - } - } -} - - -void ImProcFunctions::wavcontrast4(struct local_params& lp, float ** tmp, float ** tmpa, float ** tmpb, float contrast, float radblur, float radlevblur, int bfw, int bfh, int level_bl, int level_hl, int level_br, int level_hr, int sk, int numThreads, - const LocwavCurve & locwavCurve, bool locwavutili, bool wavcurve, const LocwavCurve& loclevwavCurve, bool loclevwavutili, bool wavcurvelev, - const LocwavCurve & locconwavCurve, bool locconwavutili, bool wavcurvecon, - const LocwavCurve & loccompwavCurve, bool loccompwavutili, bool wavcurvecomp, - const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, bool wavcurvecompre, - const LocwavCurve & locedgwavCurve, bool locedgwavutili, - float sigm, float offs, int & maxlvl, float sigmadc, float deltad, float chromalev, float chromablu, bool blurlc, bool blurena, bool levelena, bool comprena, bool compreena, float compress, float thres) -{ -BENCHFUN - std::unique_ptr wdspot(new wavelet_decomposition(tmp[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); - - //first decomposition for compress dynamic range positive values and other process - if (wdspot->memory_allocation_failed()) { - return; - } - - struct grad_params gpwav; - - maxlvl = wdspot->maxlevel(); - - int W_Lm = wdspot->level_W(maxlvl - 1); //I assume all decomposition have same W and H - - int H_Lm = wdspot->level_H(maxlvl - 1); - - if (lp.strwav != 0.f && lp.wavgradl) { - array2D factorwav(W_Lm, H_Lm); - calclocalGradientParams(lp, gpwav, 0, 0, W_Lm, H_Lm, 10); - const float mult = lp.strwav < 0.f ? -1.f : 1.f; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < H_Lm; y++) { - for (int x = 0; x < W_Lm; x++) { - factorwav[y][x] = mult * (1.f - ImProcFunctions::calcGradientFactor(gpwav, x, y)); - } - } - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - float alowg = 1.f; - float blowg = 0.f; - - if (level_hl != level_bl) { - alowg = 1.f / (level_hl - level_bl); - blowg = -alowg * level_bl; - } - - float ahighg = 1.f; - float bhighg = 0.f; - - if (level_hr != level_br) { - ahighg = 1.f / (level_hr - level_br); - bhighg = -ahighg * level_br; - } - - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { - const int W_L = wdspot->level_W(level); - const int H_L = wdspot->level_H(level); - auto wav_L = wdspot->level_coeffs(level)[dir]; - const float effect = lp.sigmalc2; - constexpr float offset = 1.f; - float mea[10]; - calceffect(level, mean, sigma, mea, effect, offset); - constexpr float insigma = 0.666f; //SD - const float logmax = std::log(MaxP[level]); //log Max - const float rapX = (mean[level] + lp.sigmalc2 * sigma[level]) / MaxP[level]; //rapport between sD / max - const float inx = std::log(insigma); - const float iny = std::log(rapX); - const float rap = inx / iny; //koef - const float asig = 0.166f / (sigma[level] * lp.sigmalc2); - const float bsig = 0.5f - asig * mean[level]; - const float amean = 0.5f / mean[level]; - float klev = 1.f; - - if (level_hl != level_bl) { - if (level >= level_bl && level < level_hl) { - klev = alowg * level + blowg; - } - } - - if (level_hr != level_br) { - if (level > level_hr && level <= level_br) { - klev = ahighg * level + bhighg; - } - } - klev *= 0.8f; - const float threshold = mean[level] + lp.sigmalc2 * sigma[level]; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) if (multiThread) -#endif - - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - const float WavCL = std::fabs(wav_L[y * W_L + x]); - float beta; - - if (WavCL < mea[0]) { - beta = 0.05f; - } else if (WavCL < mea[1]) { - beta = 0.2f; - } else if (WavCL < mea[2]) { - beta = 0.7f; - } else if (WavCL < mea[3]) { - beta = 1.f; //standard - } else if (WavCL < mea[4]) { - beta = 1.f; - } else if (WavCL < mea[5]) { - beta = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta = 0.6f; - } else if (WavCL < mea[7]) { - beta = 0.5f; - } else if (WavCL < mea[8]) { - beta = 0.4f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta = 0.3f; - } else { - beta = 0.1f; - } - - float absciss; - if (WavCL >= threshold) { //for max - absciss = pow_F(WavCL - logmax, rap); - } else if (WavCL >= mean[level]) { - absciss = asig * WavCL + bsig; - } else { - absciss = amean * WavCL; - } - - const float kc = klev * factorwav[y][x] * absciss; - const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; - - float kinterm = 1.f + reduceeffect * kc; - kinterm = kinterm <= 0.f ? 0.01f : kinterm; - wav_L[y * W_L + x] *= (1.f + (kinterm - 1.f) * beta); - } - } - } - } - } - } - - int W_L = wdspot->level_W(0); - int H_L = wdspot->level_H(0); - float *wav_L0 = wdspot->get_coeff0(); - - if (radblur > 0.f && blurena) { - array2D bufl(W_L, H_L); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - bufl[y][x] = wav_L0[y * W_L + x]; - } - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufl, bufl, W_L, H_L, radblur); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - wav_L0[y * W_L + x] = bufl[y][x]; - } - } - } - - if (compress != 0.f && compreena) { - - float Compression = expf(-compress); - float DetailBoost = compress; - - if (compress < 0.0f) { - DetailBoost = 0.0f; - } - - CompressDR(wav_L0, W_L, H_L, Compression, DetailBoost); - - } - - if ((lp.residsha != 0.f || lp.residhi != 0.f)) { - float tran = 5.f;//transition shadow - - if (lp.residshathr > (100.f - tran)) { - tran = 100.f - lp.residshathr; - } - constexpr float alp = 3.f; - const float aalp = (1.f - alp) / lp.residshathr; - const float ath = -lp.residsha / tran; - const float bth = lp.residsha - ath * lp.residshathr; - - //highlight - const float tranh = rtengine::min(5.f, lp.residhithr); - const float athH = lp.residhi / tranh; - const float bthH = lp.residhi - athH * lp.residhithr; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - const float LL100 = wav_L0[i] / 327.68f; - - if (LL100 < lp.residshathr) { - const float kk = aalp * LL100 + alp; - wav_L0[i] *= (1.f + kk * lp.residsha / 200.f); - } else if (LL100 < lp.residshathr + tran) { - wav_L0[i] *= (1.f + (LL100 * ath + bth) / 200.f); - } - - if (LL100 > lp.residhithr) { - wav_L0[i] *= (1.f + lp.residhi / 200.f); - } else if (LL100 > (lp.residhithr - tranh)) { - wav_L0[i] *= (1.f + (LL100 * athH + bthH) / 200.f); - } - } - } - - if (contrast != 0.) { - - double avedbl = 0.0; // use double precision for large summations - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:avedbl) if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - avedbl += wav_L0[i]; - } - - float ave = avedbl / double(W_L * H_L); - - float avg = ave / 32768.f; - avg = LIM01(avg); - double contreal = 0.6 * contrast; - DiagonalCurve resid_contrast({ - DCT_NURBS, - 0, 0, - avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), - avg + (1. - avg) * (0.6 - contreal / 250.0), avg + (1. - avg) * (0.6 + contreal / 250.0), - 1, 1 - }); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - float buf = LIM01(wav_L0[i] / 32768.f); - buf = resid_contrast.getVal(buf); - buf *= 32768.f; - wav_L0[i] = buf; - } - - } - - float alow = 1.f; - float blow = 0.f; - - if (level_hl != level_bl) { - alow = 1.f / (level_hl - level_bl); - blow = -alow * level_bl; - } - - float ahigh = 1.f; - float bhigh = 0.f; - - if (level_hr != level_br) { - ahigh = 1.f / (level_hr - level_br); - bhigh = -ahigh * level_br; - } - - if (wavcurvelev || wavcurvecomp || wavcurvecompre) {//compress dynamic and blur - if (wavcurvelev && radlevblur > 0.f && blurena) { - wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, 1.f, 0.f, 0.f, 0.f); - } - - if (wavcurvecomp && comprena) { - wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 2, 1.f, 0.f, sigmadc, deltad); - } - - if (wavcurvecompre && compreena) { - wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 3, 1.f, thres, 0.f, 0.f); - } - } - - if (wavcurvecon && levelena) {//contrast by levels for luminance - wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, 1.f, sk); - } - -//edge sharpness begin - if (lp.edgwena && level_bl == 0 && level_br >= 3 && locedgwavCurve && locedgwavutili && lp.strengthw > 0) { //needs the first levels to work! - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - float edd = 3.f; - float eddlow = 15.f; - float eddlipinfl = 0.005f * lp.edgw + 0.4f; - float eddlipampl = 1.f + lp.basew / 50.f; - int W_L = wdspot->level_W(0);//provisory W_L H_L - int H_L = wdspot->level_H(0); - float *koeLi[12]; - float maxkoeLi[12] = {0.f}; - float *beta = new float[W_L * H_L]; - - float *koeLibuffer = new float[12 * H_L * W_L]; //12 - - for (int i = 0; i < 12; i++) { - koeLi[i] = &koeLibuffer[i * W_L * H_L]; - } - - for (int j = 0; j < 12; j++) { - for (int i = 0; i < W_L * H_L; i++) { - koeLi[j][i] = 0.f; - } - } - - array2D tmC(W_L, H_L); - - float gradw = lp.gradw; - float tloww = lp.tloww; -//StopWatch Stop1("test"); - for (int lvl = 0; lvl < 4; lvl++) { - for (int dir = 1; dir < 4; dir++) { - const int W_L = wdspot->level_W(lvl); - const int H_L = wdspot->level_H(lvl); - float* const* wav_L = wdspot->level_coeffs(lvl); - if (lvl == 3 && dir == 3) { - const float effect = lp.sigmaed; - constexpr float offset = 1.f; - float mea[10]; - calceffect(lvl, mean, sigma, mea, effect, offset); - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int co = 0; co < H_L * W_L; co++) { - const float WavCL = std::fabs(wav_L[dir][co]); - - if (WavCL < mea[0]) { - beta[co] = 0.05f; - } else if (WavCL < mea[1]) { - beta[co] = 0.2f; - } else if (WavCL < mea[2]) { - beta[co] = 0.7f; - } else if (WavCL < mea[3]) { - beta[co] = 1.f; //standard - } else if (WavCL < mea[4]) { - beta[co] = 1.f; - } else if (WavCL < mea[5]) { - beta[co] = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta[co] = 0.5f; - } else if (WavCL < mea[7]) { - beta[co] = 0.3f; - } else if (WavCL < mea[8]) { - beta[co] = 0.2f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta[co] = 0.1f; - } else { - beta[co] = 0.05f; - } - } - } - calckoe(wav_L, gradw, tloww, koeLi, lvl, dir, W_L, H_L, edd, maxkoeLi[lvl * 3 + dir - 1], tmC); - // return convolution KoeLi and maxkoeLi of level 0 1 2 3 and Dir Horiz, Vert, Diag - } - } - tmC.free(); -//Stop1.stop(); - float aamp = 1.f + lp.thigw / 100.f; - - const float alipinfl = (eddlipampl - 1.f) / (1.f - eddlipinfl); - const float blipinfl = eddlipampl - alipinfl; - - for (int lvl = 0; lvl < 4; lvl++) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - for (int i = 1; i < H_L - 1; i++) { - for (int j = 1; j < W_L - 1; j++) { - //treatment of koeLi and maxkoeLi - if (lp.lip3) {//Sobel Canny algo improve with parameters - // comparison between pixel and neighbors - const auto neigh = lp.neiwmet == 1; - const auto kneigh = neigh ? 28.f : 38.f; - const auto somm = neigh ? 40.f : 50.f; - - for (int dir = 1; dir < 4; dir++) { //neighbors proxi - koeLi[lvl * 3 + dir - 1][i * W_L + j] = (kneigh * koeLi[lvl * 3 + dir - 1][i * W_L + j] + - 2.f * koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j + 1] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j - 1] - + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j + 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j + 1]) / somm; - } - } - - float interm = 0.f; - for (int dir = 1; dir < 4; dir++) { - //here I evaluate combination of vert / diag / horiz...we are with multiplicators of the signal - interm += SQR(koeLi[lvl * 3 + dir - 1][i * W_L + j]); - } - - interm = std::sqrt(interm) * 0.57736721f; - - constexpr float eps = 0.0001f; - // I think this double ratio (alph, beta) is better than arctg - - float alph = koeLi[lvl * 3][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between horizontal and vertical - float beta = koeLi[lvl * 3 + 2][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between diagonal and horizontal - - //alph evaluate the direction of the gradient regularity Lipschitz - // if = 1 we are on an edge - // if 0 we are not - // we can change and use log..or Arctg but why ?? we can change if need ... - //Liamp=1 for eddlipinfl - //liamp > 1 for alp >eddlipinfl and alph < 1 - //Liamp < 1 for alp < eddlipinfl and alph > 0 - if (alph > 1.f) { - alph = 1.f / alph; - } - - if (beta > 1.f) { - beta = 1.f / beta; - } - - //take into account diagonal - //if in same value OK - //if not no edge or reduction - float bet = 1.f; - - if (alph > eddlipinfl && beta < 0.85f * eddlipinfl) { //0.85 arbitrary value ==> eliminate from edge if H V D too different - bet = beta; - } - - float kampli; - if (alph > eddlipinfl) { - kampli = alipinfl * alph + blipinfl; //If beta low reduce kampli - kampli = SQR(bet) * kampli * aamp; - } else { - kampli = SQR(SQR(alph * bet)) / eddlipinfl; //Strong Reduce if beta low - kampli = kampli / aamp; - } - - - interm *= kampli; - - if (interm * eddlow < lp.tloww) { - interm = 0.01f; //eliminate too low values - } - - //we can change this part of algo==> not equal but ponderate - koeLi[lvl * 3][i * W_L + j] = koeLi[lvl * 3 + 1][i * W_L + j] = koeLi[lvl * 3 + 2][i * W_L + j] = interm; //new value - //here KoeLi contains values where gradient is high and coef high, and eliminate low values... - } - } - } - - constexpr float scales[10] = {1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, 512.f}; - float scaleskip[10]; - - for (int sc = 0; sc < 10; sc++) { - scaleskip[sc] = scales[sc] / sk; - } - - const float rad = lp.radiusw / 60.f; //radius ==> not too high value to avoid artifacts - float value = lp.strengthw / 8.f; //strength - - if (scaleskip[1] < 1.f) { - constexpr float atten01234 = 0.80f; - value *= atten01234 * scaleskip[1]; //for zoom < 100% reduce strength...I choose level 1...but!! - } - - constexpr float lim0 = 20.f; //arbitrary limit for low radius and level between 2 or 3 to 30 maxi - float repart = lp.detailw; - - if (lp.edgwmet != 1) { - float brepart; - if (lp.edgwmet == 0) { - brepart = 3.f; - } else /*if (lp.edgwmet == 2)*/ { - brepart = 0.5f; //arbitrary value to increase / decrease repart, between 1 and 0 - } - if (rad < lim0 / 60.f) { - const float arepart = - (brepart - 1.f) / (lim0 / 60.f); - repart *= arepart * rad + brepart; //linear repartition of repart - } - } - - const float bk = 1.f + repart / 50.f; - constexpr float al10 = 1.0f; //arbitrary value ==> less = take into account high levels - const float ak = - (bk - al10) / 10.f; //10 = maximum levels - - for (int lvl = 0; lvl < maxlvl; lvl++) { - if (MaxP[lvl] > 0.f) { //curve - const int W_L = wdspot->level_W(lvl); - const int H_L = wdspot->level_H(lvl); - float* const* wav_L = wdspot->level_coeffs(lvl); - const float koef = ak * lvl + bk; //modulate for levels : more levels high, more koef low ==> concentrated action on low levels, without or near for high levels - float expkoef = -pow_F(std::fabs(rad - lvl), koef); //reduce effect for high levels - if (lp.edgwmet == 2) { - if (rad < lim0 / 60.f && lvl == 0) { - expkoef *= abs(repart); //reduce effect for low values of rad and level=0==> quasi only level 1 is effective - } - } else if (lp.edgwmet == 0) { - if (rad < lim0 / 60.f && lvl == 1) { - expkoef /= repart; //increase effect for low values of rad and level=1==> quasi only level 0 is effective - } - } - //take into account local contrast - const float refin = value * xexpf(expkoef); - const float edgePrecalc = 1.f + refin; //estimate edge "pseudo variance" - constexpr float insigma = 0.666f; //SD - const float logmax = xlogf(MaxP[lvl]); //log Max - const float rapX = (mean[lvl] + sigma[lvl]) / MaxP[lvl]; //rapport between sD / max - const float inx = xlogf(insigma); - const float iny = xlogf(rapX); - const float rap = inx / iny; //koef - const float asig = 0.166f / sigma[lvl]; - const float bsig = 0.5f - asig * mean[lvl]; - const float amean = 0.5f / mean[lvl]; - constexpr int borderL = 1; - constexpr float abssd = 4.f; //amplification reference - constexpr float bbssd = 2.f; //mini ampli - constexpr float maxamp = 2.5f; //maxi ampli at end - constexpr float maxampd = 10.f; //maxi ampli at end - constexpr float a_abssd = (maxamp - abssd) / 0.333f; - constexpr float b_abssd = maxamp - a_abssd; - constexpr float da_abssd = (maxampd - abssd) / 0.333f; - constexpr float db_abssd = maxampd - da_abssd; - constexpr float am = (abssd - bbssd) / 0.666f; - for (int dir = 1; dir < 4; dir++) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16) if(multiThread) -#endif - for (int i = borderL; i < H_L - borderL; i++) { - for (int j = borderL; j < W_L - borderL; j++) { - const int k = i * W_L + j; - - float edge; - if (lvl < 4) { - edge = 1.f + (edgePrecalc - 1.f) * (koeLi[lvl * 3][k]) / (1.f + 0.9f * maxkoeLi[lvl * 3 + dir - 1]); - } else { - edge = edgePrecalc; - } - - float absciss = 0.f; - if (std::fabs(wav_L[dir][k]) >= mean[lvl] + sigma[lvl]) { //for max - absciss = xexpf((xlogf(std::fabs(wav_L[dir][k])) - logmax) * rap); - } else if (std::fabs(wav_L[dir][k]) >= mean[lvl]) { - absciss = asig * std::fabs(wav_L[dir][k]) + bsig; - } else /*if (std::fabs(wav_L[dir][k]) < mean[lvl])*/ { - absciss = amean * std::fabs(wav_L[dir][k]); - } - - // Threshold adjuster settings==> approximative for curve - //kmul about average cbrt(3--40 / 10)==>1.5 to 2.5 - //kmul about SD 10--60 / 35 ==> 2 - // kmul about low cbrt((5.f+cp.edg_low)/5.f);==> 1.5 - // kmul about max ==> 9 - // we can change these values - // result is different not best or bad than threshold slider...but similar - float kmul; - float kmuld; - - if (absciss > 0.666f && absciss < 1.f) { - kmul = a_abssd * absciss + b_abssd; //about max ==> kinterm - kmuld = da_abssd * absciss + db_abssd; - } else { - kmul = kmuld = absciss * am + bbssd; - } - - const float kc = kmul * (locedgwavCurve[absciss * 500.f] - 0.5f); - - float kinterm; - if (kc >= 0.f) { - constexpr float reduceeffect = 0.6f; - kinterm = 1.f + reduceeffect * kc; //about 1 to 3 general and big amplification for max (under 0) - } else { - const float kcd = kmuld * (locedgwavCurve[absciss * 500.f] - 0.5f); - kinterm = 1.f - SQR(kcd) / 10.f; - } - - if (kinterm < 0.f) { - kinterm = 0.01f; - } - - edge = std::max(edge * kinterm, 1.f); - wav_L[dir][k] *= 1.f + (edge - 1.f) * beta[k]; - } - } - } - } - } - - if (koeLibuffer) { - delete [] koeLibuffer; - } - - delete[] beta; - } - -//edge sharpness end - - if (locwavCurve && locwavutili && wavcurve) {//simple local contrast in function luminance - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - int W_L = wdspot->level_W(level); - int H_L = wdspot->level_H(level); - float klev = 1.f; - - if (level >= level_hl && level <= level_hr) { - klev = 1.f; - } - - if (level_hl != level_bl) { - if (level >= level_bl && level < level_hl) { - klev = alow * level + blow; - } - } - - if (level_hr != level_br) { - if (level > level_hr && level <= level_br) { - klev = ahigh * level + bhigh; - } - } - float* const* wav_L = wdspot->level_coeffs(level); - - if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { - constexpr float insigma = 0.666f; //SD - const float logmax = log(MaxP[level]); //log Max - const float rapX = (mean[level] + lp.sigmalc * sigma[level]) / MaxP[level]; //rapport between sD / max - const float inx = log(insigma); - const float iny = log(rapX); - const float rap = inx / iny; //koef - const float asig = 0.166f / (sigma[level] * lp.sigmalc); - const float bsig = 0.5f - asig * mean[level]; - const float amean = 0.5f / mean[level]; - const float limit1 = mean[level] + lp.sigmalc * sigma[level]; - const float limit2 = mean[level]; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic, 16 * W_L) if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - const float val = std::fabs(wav_L[dir][i]); - - float absciss; - if (val >= limit1) { //for max - const float valcour = xlogf(val); - absciss = xexpf((valcour - logmax) * rap); - } else if (val >= limit2) { - absciss = asig * val + bsig; - } else { - absciss = amean * val; - } - - const float kc = klev * (locwavCurve[absciss * 500.f] - 0.5f); - const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; - - float kinterm = 1.f + reduceeffect * kc; - kinterm = kinterm <= 0.f ? 0.01f : kinterm; - - wav_L[dir][i] *= kinterm <= 0.f ? 0.01f : kinterm; - } - } - } - } - } - //reconstruct all for L - wdspot->reconstruct(tmp[0], 1.f); - - bool reconstruct = false; - if (wavcurvecon && (chromalev != 1.f) && levelena) { // a if need ) {//contrast by levels for chroma a - // a - wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); - if (wdspot->memory_allocation_failed()) { - return; - } - wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); - reconstruct = true; - } - if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need - // a - if (!reconstruct) { - wdspot.reset(new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); - if (wdspot->memory_allocation_failed()) { - return; - } - } - wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); - reconstruct = true; - } - if (reconstruct) { - wdspot->reconstruct(tmpa[0], 1.f); - } - - reconstruct = false; - if (wavcurvecon && (chromalev != 1.f) && levelena) { // b if need ) {//contrast by levels for chroma b - //b - wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); - if (wdspot->memory_allocation_failed()) { - return; - } - //b - wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); - reconstruct = true; - } - - if (wavcurvelev && radlevblur > 0.f && blurena && chromablu > 0.f && !blurlc) {//chroma blur if need - //b - if (!reconstruct) { - wdspot.reset(new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen)); - if (wdspot->memory_allocation_failed()) { - return; - } - } - wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); - reconstruct = true; - } - if (reconstruct) { - wdspot->reconstruct(tmpb[0], 1.f); - } -} - - -void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_numblox_W, float **tmp1, array2D *Lin, int numThreads, const struct local_params & lp, int chrom) -{ - BENCHFUN - - fftwf_plan plan_forward_blox[2]; - fftwf_plan plan_backward_blox[2]; - - array2D tilemask_in(TS, TS); - array2D tilemask_out(TS, TS); - - float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); - float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); - float params_Ldetail = 0.f; - - int nfwd[2] = {TS, TS}; - - //for DCT: - fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; - fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; - - // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit - plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); - fftwf_free(Lbloxtmp); - fftwf_free(fLbloxtmp); - const int border = rtengine::max(2, TS / 16); - - for (int i = 0; i < TS; ++i) { - float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); - float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); - float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); - - for (int j = 0; j < TS; ++j) { - float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); - tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; - tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; - - } - } - - - float *LbloxArray[numThreads]; - float *fLbloxArray[numThreads]; - - - - const int numblox_W = ceil((static_cast(GW)) / offset) + 2; - const int numblox_H = ceil((static_cast(GH)) / offset) + 2; - - - //residual between input and denoised L channel - array2D Ldetail(GW, GH, ARRAY2D_CLEAR_DATA); - array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks - array2D prov(GW, GH, ARRAY2D_CLEAR_DATA); - - for (int i = 0; i < numThreads; ++i) { - LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); - fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); - } - -#ifdef _OPENMP - int masterThread = omp_get_thread_num(); -#endif -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef _OPENMP - int subThread = masterThread * 1 + omp_get_thread_num(); -#else - int subThread = 0; -#endif - float *Lblox = LbloxArray[subThread]; - float *fLblox = fLbloxArray[subThread]; - float pBuf[GW + TS + 2 * offset] ALIGNED16; -#ifdef _OPENMP - #pragma omp for -#endif - for (int vblk = 0; vblk < numblox_H; ++vblk) { - - int top = (vblk - 1) * offset; - float * datarow = pBuf + offset; - - for (int i = 0; i < TS; ++i) { - int row = top + i; - int rr = row; - - if (row < 0) { - rr = rtengine::min(-row, GH - 1); - } else if (row >= GH) { - rr = rtengine::max(0, 2 * GH - 2 - row); - } - - for (int j = 0; j < GW; ++j) { - datarow[j] = ((*Lin)[rr][j] - tmp1[rr][j]); - prov[rr][j] = std::fabs(tmp1[rr][j]); - - } - - for (int j = -1 * offset; j < 0; ++j) { - datarow[j] = datarow[rtengine::min(-j, GW - 1)]; - } - - for (int j = GW; j < GW + TS + offset; ++j) { - datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; - }//now we have a padded data row - - //now fill this row of the blocks with Lab high pass data - for (int hblk = 0; hblk < numblox_W; ++hblk) { - int left = (hblk - 1) * offset; - int indx = (hblk) * TS; //index of block in malloc - - if (top + i >= 0 && top + i < GH) { - int j; - - for (j = 0; j < rtengine::min((-left), TS); ++j) { - Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - - for (; j < rtengine::min(TS, GW - left); ++j) { - Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; - } - - for (; j < TS; ++j) { - Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - } else { - for (int j = 0; j < TS; ++j) { - Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data - } - } - - } - - }//end of filling block row - - //fftwf_print_plan (plan_forward_blox); - if (numblox_W == max_numblox_W) { - fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles - } else { - fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles - } - - // now process the vblk row of blocks for noise reduction - - float noisevar_Ldetail = 1.f; - - if (chrom == 0) { - params_Ldetail = rtengine::min(float(lp.noiseldetail), 99.9f); // max out to avoid div by zero when using noisevar_Ldetail as divisor - noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); - } else if (chrom == 1) { - params_Ldetail = rtengine::min(float(lp.noisechrodetail), 99.9f); - // noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? - noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? - } - - // float noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); - - - - for (int hblk = 0; hblk < numblox_W; ++hblk) { - ImProcFunctions::RGBtile_denoise(fLblox, hblk, noisevar_Ldetail); - - }//end of horizontal block loop - - - //now perform inverse FT of an entire row of blocks - if (numblox_W == max_numblox_W) { - fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT - } else { - fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT - } - - int topproc = (vblk - 1) * offset; - - //add row of blocks to output image tile - ImProcFunctions::RGBoutput_tile_row(Lblox, Ldetail, tilemask_out, GH, GW, topproc); - - }//end of vertical block loop - } - - //Threshold DCT from Alberto Grigio - const int detail_thresh = lp.detailthr; - array2D mask; - - if (detail_thresh > 0) { - mask(GW, GH); - float thr = log2lin(float(detail_thresh) / 200.f, 100.f); - buildBlendMask(prov, mask, GW, GH, thr); -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(mask, mask, GW, GH, 20.0); - } - array2D m2(GW, GH); - constexpr float alfa = 0.856f; - const float beta = 1.f + std::sqrt(log2lin(thr, 100.f)); - buildGradientsMask(GW, GH, prov, m2, params_Ldetail / 100.f, 7, 3, alfa, beta, multiThread); - - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - mask[i][j] *= m2[i][j]; - } - } - } - - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - float d = Ldetail[i][j] / totwt[i][j]; - - if (detail_thresh > 0) { - d *= mask[i][j]; - } - - //may want to include masking threshold for large hipass data to preserve edges/detail - tmp1[i][j] += d; - } - } - - mask.free(); -//end Threshold DCT - - - delete Lin; - - - for (int i = 0; i < numThreads; ++i) { - fftwf_free(LbloxArray[i]); - fftwf_free(fLbloxArray[i]); - } - - fftwf_destroy_plan(plan_forward_blox[0]); - fftwf_destroy_plan(plan_backward_blox[0]); - fftwf_destroy_plan(plan_forward_blox[1]); - fftwf_destroy_plan(plan_backward_blox[1]); - fftwf_cleanup(); - - -} - -void ImProcFunctions::DeNoise(int call, int del, float * slidL, float * slida, float * slidb, int aut, bool noiscfactiv, const struct local_params & lp, LabImage * originalmaskbl, int levred, float huerefblur, float lumarefblur, float chromarefblur, LabImage * original, LabImage * transformed, int cx, int cy, int sk) -{ - -//local denoise - //all these variables are to prevent use of denoise when non necessary - // but with qualmet = 2 (default for best quality) we must denoise chroma with little values to prevent artifacts due to variations of Hue - // but if user select voluntary denoise, it is that choice the good (prioritary) - bool execcolor = (lp.chro != 0.f || lp.ligh != 0.f || lp.cont != 0); // only if one slider or more is engaged - bool execbdl = (lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f) ;//only if user want cbdl - bool execdenoi = noiscfactiv && ((lp.colorena && execcolor) || (lp.tonemapena && lp.strengt != 0.f) || (lp.cbdlena && execbdl) || (lp.sfena && lp.strng > 0.f) || (lp.lcena && lp.lcamount > 0.f) || (lp.sharpena && lp.shrad > 0.42) || (lp.retiena && lp.str > 0.f) || (lp.exposena && lp.expcomp != 0.f) || (lp.expvib && lp.past != 0.f)); - bool execmaskden = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 0; - - if (((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f -// || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4 || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? - || execmaskden || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? - - StopWatch Stop1("locallab Denoise called"); - - if (aut == 0) { - MyMutex::MyLock lock(*fftwMutex); - } - - - if (lp.noisecf >= 0.01f || lp.noisecc >= 0.01f || aut == 1 || aut == 2) { - noiscfactiv = false; - levred = 7; - } - - int GW = transformed->W; - int GH = transformed->H; - - -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; - -#endif - - if (call == 1 && GW >= mDEN && GH >= mDEN) { - - - LabImage tmp1(transformed->W, transformed->H); - LabImage tmp2(transformed->W, transformed->H); - tmp2.clear(); - - array2D *Lin = nullptr; - array2D *Ain = nullptr; - array2D *Bin = nullptr; - - int max_numblox_W = ceil((static_cast(GW)) / offset) + 2; - // calculate min size of numblox_W. - int min_numblox_W = ceil((static_cast(GW)) / offset) + 2; - - - for (int ir = 0; ir < GH; ir++) - for (int jr = 0; jr < GW; jr++) { - tmp1.L[ir][jr] = original->L[ir][jr]; - tmp1.a[ir][jr] = original->a[ir][jr]; - tmp1.b[ir][jr] = original->b[ir][jr]; - } - - // int DaubLen = 6; - - int levwavL = levred; - int skip = 1; - - wavelet_decomposition Ldecomp(tmp1.L[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); - wavelet_decomposition adecomp(tmp1.a[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); - wavelet_decomposition bdecomp(tmp1.b[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); - - float madL[10][3]; - int edge = 2; - - if (!Ldecomp.memory_allocation_failed()) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) -#endif - for (int lvl = 0; lvl < levred; lvl++) { - for (int dir = 1; dir < 4; dir++) { - int Wlvl_L = Ldecomp.level_W(lvl); - int Hlvl_L = Ldecomp.level_H(lvl); - const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); - - madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); - } - } - - float vari[levred]; - float mxsl = 0.f; - // float mxsfl = 0.f; - - if (aut == 0) { - if (levred == 7) { - edge = 2; - vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); - vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); - - vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - } else if (levred == 4) { - edge = 3; - vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); - vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - - } - } else if (aut == 1 || aut == 2) { - edge = 2; - vari[0] = SQR(slidL[0]); - vari[1] = SQR(slidL[1]); - vari[2] = SQR(slidL[2]); - vari[3] = SQR(slidL[3]); - vari[4] = SQR(slidL[4]); - vari[5] = SQR(slidL[5]); - vari[6] = SQR(slidL[6]); - float mxslid34 = rtengine::max(slidL[3], slidL[4]); - float mxslid56 = rtengine::max(slidL[5], slidL[6]); - mxsl = rtengine::max(mxslid34, mxslid56); - - } - - { - float kr3 = 0.f; - float kr4 = 0.f; - float kr5 = 0.f; - - if (aut == 0 || aut == 1) { - if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { - kr3 = 0.f; - kr4 = 0.f; - kr5 = 0.f; - } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { - kr3 = 0.5f; - kr4 = 0.3f; - kr5 = 0.2f; - } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { - kr3 = 0.7f; - kr4 = 0.5f; - kr5 = 0.3f; - } else { - kr3 = 1.f; - kr4 = 1.f; - kr5 = 1.f; - } - } else if (aut == 2) { - kr3 = 1.f; - kr4 = 1.f; - kr5 = 1.f; - } - - vari[0] = rtengine::max(0.000001f, vari[0]); - vari[1] = rtengine::max(0.000001f, vari[1]); - vari[2] = rtengine::max(0.000001f, vari[2]); - vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); - - if (levred == 7) { - vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); - vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); - vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); - } - - float* noisevarlum = new float[GH * GW]; - int GW2 = (GW + 1) / 2; - - float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value - float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value - - float seuillow = 3000.f;//low - float seuilhigh = 18000.f;//high - int i = 10 - lp.noiselequal; - float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); - float bc = nvlh[i] - seuillow * ac; - //ac and bc for transition -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) - for (int jr = 0; jr < GW; jr++) { - float lN = tmp1.L[ir][jr]; - - if (lN < seuillow) { - noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvlh[i]; - } else if (lN < seuilhigh) { - noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = ac * lN + bc; - } else { - noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvll[i]; - } - } - - if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { - WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - - } else { - - WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - - WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - - } - - delete[] noisevarlum; - - } - } - - - float variC[levred]; - float variCb[levred]; - - float noisecfr = lp.noisecf; - float noiseccr = lp.noisecc; - - if (lp.adjch > 0.f) { - noisecfr = lp.noisecf + 0.1f * lp.adjch; - noiseccr = lp.noisecc + 0.1f * lp.adjch; - } - - float noisecfb = lp.noisecf; - float noiseccb = lp.noisecc; - - if (lp.adjch < 0.f) { - noisecfb = lp.noisecf - 0.1f * lp.adjch; - noiseccb = lp.noisecc - 0.1f * lp.adjch; - } - - - if (noisecfr < 0.f) { - noisecfr = 0.00001f; - } - - if (noiseccr < 0.f) { - noiseccr = 0.00001f; - } - - if (noisecfb < 0.f) { - noisecfb = 0.00001f; - } - - if (noiseccb < 0.f) { - noiseccb = 0.00001f; - } - - if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { - float maxcfine = 0.f; - float maxccoarse = 0.f; - - if (aut == 0) { - if (levred == 7) { - edge = 2; - variC[0] = SQR(noisecfr); - variC[1] = SQR(noisecfr); - variC[2] = SQR(noisecfr); - - variC[3] = SQR(noisecfr); - variC[4] = SQR(noisecfr); - variC[5] = SQR(noiseccr); - variC[6] = SQR(noiseccr); - - variCb[0] = SQR(noisecfb); - variCb[1] = SQR(noisecfb); - variCb[2] = SQR(noisecfb); - - variCb[3] = SQR(noisecfb); - variCb[4] = SQR(noisecfb); - variCb[5] = SQR(noiseccb); - variCb[6] = SQR(noiseccb); - - } else if (levred == 4) { - edge = 3; - variC[0] = SQR(lp.noisecf / 10.0); - variC[1] = SQR(lp.noisecf / 10.0); - variC[2] = SQR(lp.noisecf / 10.0); - variC[3] = SQR(lp.noisecf / 10.0); - - variCb[0] = SQR(lp.noisecf / 10.0); - variCb[1] = SQR(lp.noisecf / 10.0); - variCb[2] = SQR(lp.noisecf / 10.0); - variCb[3] = SQR(lp.noisecf / 10.0); - - } - } else if (aut == 1 || aut == 2) { - edge = 2; - variC[0] = SQR(slida[0]); - variC[1] = SQR(slida[1]); - variC[2] = SQR(slida[2]); - variC[3] = SQR(slida[3]); - variC[4] = SQR(slida[4]); - variC[5] = SQR(slida[5]); - variC[6] = SQR(slida[6]); - float maxc01 = rtengine::max(slida[0], slida[1]); - float maxc23 = rtengine::max(slida[2], slida[3]); - float max03 = rtengine::max(maxc01, maxc23); - float maxrf = rtengine::max(max03, slida[4]); - float maxrc = rtengine::max(slida[5], slida[6]); - - variCb[0] = SQR(slidb[0]); - variCb[1] = SQR(slidb[1]); - variCb[2] = SQR(slidb[2]); - variCb[3] = SQR(slidb[3]); - variCb[4] = SQR(slidb[4]); - variCb[5] = SQR(slidb[5]); - variCb[6] = SQR(slidb[6]); - float maxb01 = rtengine::max(slidb[0], slidb[1]); - float maxb23 = rtengine::max(slidb[2], slidb[3]); - float maxb03 = rtengine::max(maxb01, maxb23); - float maxbf = rtengine::max(maxb03, slidb[4]); - maxcfine = rtengine::max(maxrf, maxbf); - - float maxbc = rtengine::max(slidb[5], slidb[6]); - maxccoarse = rtengine::max(maxrc, maxbc); - - } - - { - float minic = 0.000001f; - - if (noiscfactiv) { - minic = 0.1f;//only for artifact shape detection - } - - float k1 = 0.f; - float k2 = 0.f; - float k3 = 0.f; - - if (aut == 0 || aut == 1) { - if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { - k1 = 0.05f; - k2 = 0.f; - k3 = 0.f; - } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { - k1 = 0.1f; - k2 = 0.0f; - k3 = 0.f; - } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { - k1 = 0.2f; - k2 = 0.1f; - k3 = 0.f; - } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { - k1 = 0.3f; - k2 = 0.25f; - k3 = 0.f; - } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { - k1 = 0.4f; - k2 = 0.25f; - k3 = 0.1f; - } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { - k1 = 0.5f; - k2 = 0.3f; - k3 = 0.15f; - } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { - k1 = 0.6f; - k2 = 0.45f; - k3 = 0.3f; - } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { - k1 = 0.7f; - k2 = 0.5f; - k3 = 0.4f; - } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { - k1 = 0.8f; - k2 = 0.6f; - k3 = 0.5f; - } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { - k1 = 0.85f; - k2 = 0.7f; - k3 = 0.6f; - } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { - k1 = 0.9f; - k2 = 0.8f; - k3 = 0.7f; - } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { - k1 = 1.f; - k2 = 1.f; - k3 = 0.9f; - - } else { - k1 = 1.f; - k2 = 1.f; - k3 = 1.f; - } - } else if (aut == 2) { - k1 = 1.f; - k2 = 1.f; - k3 = 1.f; - } - - - variC[0] = rtengine::max(minic, variC[0]); - variC[1] = rtengine::max(minic, k1 * variC[1]); - variC[2] = rtengine::max(minic, k2 * variC[2]); - variC[3] = rtengine::max(minic, k3 * variC[3]); - - variCb[0] = rtengine::max(minic, variCb[0]); - variCb[1] = rtengine::max(minic, k1 * variCb[1]); - variCb[2] = rtengine::max(minic, k2 * variCb[2]); - variCb[3] = rtengine::max(minic, k3 * variCb[3]); - - if (levred == 7) { - float k4 = 0.f; - float k5 = 0.f; - float k6 = 0.f; - - if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { - k4 = 0.1f; - k5 = 0.02f; - } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { - k4 = 0.15f; - k5 = 0.05f; - } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { - k4 = 0.15f; - k5 = 0.1f; - } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { - k4 = 0.3f; - k5 = 0.15f; - } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { - k4 = 0.6f; - k5 = 0.4f; - } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { - k4 = 0.8f; - k5 = 0.6f; - } else { - k4 = 1.f; - k5 = 1.f; - } - - variC[4] = rtengine::max(0.000001f, k4 * variC[4]); - variC[5] = rtengine::max(0.000001f, k5 * variC[5]); - variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); - variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); - - if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { - k6 = 0.f; - } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { - k6 = 0.4f; - } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { - k6 = 0.7f; - } else { - k6 = 1.f; - } - - variC[6] = rtengine::max(0.00001f, k6 * variC[6]); - variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); - - } - - float* noisevarchrom = new float[GH * GW]; - //noisevarchrom in function chroma - int GW2 = (GW + 1) / 2; - float nvch = 0.6f;//high value - float nvcl = 0.1f;//low value - - if ((lp.noisecf > 100.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { - nvch = 0.8f; - nvcl = 0.4f; - } - - float seuil = 4000.f;//low - float seuil2 = 15000.f;//high - //ac and bc for transition - float ac = (nvch - nvcl) / (seuil - seuil2); - float bc = nvch - seuil * ac; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) - for (int jr = 0; jr < GW; jr++) { - float cN = std::sqrt(SQR(tmp1.a[ir][jr]) + SQR(tmp1.b[ir][jr])); - - if (cN < seuil) { - noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvch; - } else if (cN < seuil2) { - noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = ac * cN + bc; - } else { - noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvcl; - } - } - - - float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); - - if ((lp.noisecc < 2.f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - } else { - WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - - WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - } - - delete[] noisevarchrom; - - } - } - - if (!Ldecomp.memory_allocation_failed()) { - Lin = new array2D(GW, GH); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - (*Lin)[i][j] = tmp1.L[i][j]; - } - } - - Ldecomp.reconstruct(tmp1.L[0]); - } - - if (!Ldecomp.memory_allocation_failed() && aut == 0) { - if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { - fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.L, Lin, numThreads, lp, 0); - } - } - - if (!adecomp.memory_allocation_failed()) { - Ain = new array2D(GW, GH); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - (*Ain)[i][j] = tmp1.a[i][j]; - } - } - - adecomp.reconstruct(tmp1.a[0]); - } - - - if (!adecomp.memory_allocation_failed() && aut == 0) { - if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { - fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.a, Ain, numThreads, lp, 1); - } - } - - - if (!bdecomp.memory_allocation_failed()) { - - Bin = new array2D(GW, GH); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < GH; ++i) { - for (int j = 0; j < GW; ++j) { - (*Bin)[i][j] = tmp1.b[i][j]; - } - } - - bdecomp.reconstruct(tmp1.b[0]); - } - - - if (!bdecomp.memory_allocation_failed() && aut == 0) { - if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { - fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.b, Bin, numThreads, lp, 1); - } - - } - - // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); - if(lp.smasktyp != 0) { - DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); - } else { - DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); - } - - } else if (call == 2) { //simpleprocess - - int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone - int bfw = int (lp.lx + lp.lxL) + del; - - if (bfh >= mDEN && bfw >= mDEN) { - LabImage bufwv(bfw, bfh); - bufwv.clear(true); - array2D *Lin = nullptr; - array2D *Ain = nullptr; - array2D *Bin = nullptr; - - int max_numblox_W = ceil((static_cast(bfw)) / offset) + 2; - // calculate min size of numblox_W. - int min_numblox_W = ceil((static_cast(bfw)) / offset) + 2; - // these are needed only for creation of the plans and will be freed before entering the parallel loop - - - int begy = lp.yc - lp.lyT; - int begx = lp.xc - lp.lxL; - int yEn = lp.yc + lp.ly; - int xEn = lp.xc + lp.lx; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H ; y++) //{ - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - int loy = cy + y; - - if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { - bufwv.L[loy - begy][lox - begx] = original->L[y][x]; - bufwv.a[loy - begy][lox - begx] = original->a[y][x]; - bufwv.b[loy - begy][lox - begx] = original->b[y][x]; - } - - } - - // int DaubLen = 6; - - int levwavL = levred; - int skip = 1; - wavelet_decomposition Ldecomp(bufwv.L[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); - wavelet_decomposition adecomp(bufwv.a[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); - wavelet_decomposition bdecomp(bufwv.b[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); - float madL[10][3]; - int edge = 2; - - if (!Ldecomp.memory_allocation_failed()) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) -#endif - for (int lvl = 0; lvl < levred; lvl++) { - for (int dir = 1; dir < 4; dir++) { - int Wlvl_L = Ldecomp.level_W(lvl); - int Hlvl_L = Ldecomp.level_H(lvl); - - const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); - - madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); - } - } - - float vari[levred]; - float mxsl = 0.f; - // float mxsfl = 0.f; - - if (aut == 0) { - if (levred == 7) { - edge = 2; - vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); - vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); - - vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - } else if (levred == 4) { - edge = 3; - vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); - vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); - - } - } else if (aut == 1 || aut == 2) { - edge = 2; - vari[0] = SQR(slidL[0]); - vari[1] = SQR(slidL[1]); - vari[2] = SQR(slidL[2]); - vari[3] = SQR(slidL[3]); - vari[4] = SQR(slidL[4]); - vari[5] = SQR(slidL[5]); - vari[6] = SQR(slidL[6]); - float mxslid34 = rtengine::max(slidL[3], slidL[4]); - float mxslid56 = rtengine::max(slidL[5], slidL[6]); - mxsl = rtengine::max(mxslid34, mxslid56); - - } - - { - float kr3 = 0.f; - float kr4 = 0.f; - float kr5 = 0.f; - - if (aut == 0 || aut == 1) { - if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { - kr3 = 0.f; - kr4 = 0.f; - kr5 = 0.f; - } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { - kr3 = 0.5f; - kr4 = 0.3f; - kr5 = 0.2f; - } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { - kr3 = 0.7f; - kr4 = 0.5f; - kr5 = 0.3f; - } else { - kr3 = 1.f; - kr4 = 1.f; - kr5 = 1.f; - } - } else if (aut == 2) { - kr3 = 1.f; - kr4 = 1.f; - kr5 = 1.f; - - } - - vari[0] = rtengine::max(0.000001f, vari[0]); - vari[1] = rtengine::max(0.000001f, vari[1]); - vari[2] = rtengine::max(0.000001f, vari[2]); - vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); - - if (levred == 7) { - vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); - vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); - vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); - } - - // float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL - float* noisevarlum = new float[bfh * bfw]; - int bfw2 = (bfw + 1) / 2; - - float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value - float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value - - float seuillow = 3000.f;//low - float seuilhigh = 18000.f;//high - int i = 10 - lp.noiselequal; - float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); - float bc = nvlh[i] - seuillow * ac; - //ac and bc for transition -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - float lN = bufwv.L[ir][jr]; - - if (lN < seuillow) { - noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvlh[i]; - } else if (lN < seuilhigh) { - noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = ac * lN + bc; - } else { - noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvll[i]; - } - } - - - if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { - WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - } else { - WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); - } - - delete [] noisevarlum; - - } - } - - - float variC[levred]; - float variCb[levred]; - - float noisecfr = lp.noisecf; - float noiseccr = lp.noisecc; - - if (lp.adjch > 0.f) { - noisecfr = lp.noisecf + 0.1f * lp.adjch; - noiseccr = lp.noisecc + 0.1f * lp.adjch; - } - - float noisecfb = lp.noisecf; - float noiseccb = lp.noisecc; - - if (lp.adjch < 0.f) { - noisecfb = lp.noisecf - 0.1f * lp.adjch; - noiseccb = lp.noisecc - 0.1f * lp.adjch; - } - - - if (noisecfr < 0.f) { - noisecfr = 0.00001f; - } - - if (noiseccr < 0.f) { - noiseccr = 0.00001f; - } - - if (noisecfb < 0.f) { - noisecfb = 0.00001f; - } - - if (noiseccb < 0.f) { - noiseccb = 0.00001f; - } - - - if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { - float maxcfine = 0.f; - float maxccoarse = 0.f; - - if (aut == 0) { - - if (levred == 7) { - edge = 2; - variC[0] = SQR(noisecfr); - variC[1] = SQR(noisecfr); - variC[2] = SQR(noisecfr); - - variC[3] = SQR(noisecfr); - variC[4] = SQR(noisecfr); - variC[5] = SQR(noiseccr); - variC[6] = SQR(noiseccr); - - variCb[0] = SQR(noisecfb); - variCb[1] = SQR(noisecfb); - variCb[2] = SQR(noisecfb); - - variCb[3] = SQR(noisecfb); - variCb[4] = SQR(noisecfb); - variCb[5] = SQR(noiseccb); - variCb[6] = SQR(noiseccb); - - } else if (levred == 4) { - edge = 3; - variC[0] = SQR(lp.noisecf / 10.0); - variC[1] = SQR(lp.noisecf / 10.0); - variC[2] = SQR(lp.noisecf / 10.0); - variC[3] = SQR(lp.noisecf / 10.0); - - variCb[0] = SQR(lp.noisecf / 10.0); - variCb[1] = SQR(lp.noisecf / 10.0); - variCb[2] = SQR(lp.noisecf / 10.0); - variCb[3] = SQR(lp.noisecf / 10.0); - - - } - } else if (aut == 1 || aut == 2) { - edge = 2; - variC[0] = SQR(slida[0]); - variC[1] = SQR(slida[1]); - variC[2] = SQR(slida[2]); - variC[3] = SQR(slida[3]); - variC[4] = SQR(slida[4]); - variC[5] = SQR(slida[5]); - variC[6] = SQR(slida[6]); - float maxc01 = rtengine::max(slida[0], slida[1]); - float maxc23 = rtengine::max(slida[2], slida[3]); - float max03 = rtengine::max(maxc01, maxc23); - float maxrf = rtengine::max(max03, slida[4]); - float maxrc = rtengine::max(slida[5], slida[6]); - - variCb[0] = SQR(slidb[0]); - variCb[1] = SQR(slidb[1]); - variCb[2] = SQR(slidb[2]); - variCb[3] = SQR(slidb[3]); - variCb[4] = SQR(slidb[4]); - variCb[5] = SQR(slidb[5]); - variCb[6] = SQR(slidb[6]); - float maxb01 = rtengine::max(slidb[0], slidb[1]); - float maxb23 = rtengine::max(slidb[2], slidb[3]); - float maxb03 = rtengine::max(maxb01, maxb23); - float maxbf = rtengine::max(maxb03, slidb[4]); - maxcfine = rtengine::max(maxrf, maxbf); - - float maxbc = rtengine::max(slidb[5], slidb[6]); - maxccoarse = rtengine::max(maxrc, maxbc); - - } - - { - float minic = 0.000001f; - - if (noiscfactiv) { - minic = 0.1f;//only for artifact shape detection - } - - float k1 = 0.f; - float k2 = 0.f; - float k3 = 0.f; - - if (aut == 0 || aut == 1) { - if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { - k1 = 0.05f; - k2 = 0.f; - k3 = 0.f; - } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { - k1 = 0.1f; - k2 = 0.0f; - k3 = 0.f; - } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { - k1 = 0.2f; - k2 = 0.1f; - k3 = 0.f; - } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { - k1 = 0.3f; - k2 = 0.25f; - k3 = 0.f; - } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { - k1 = 0.4f; - k2 = 0.25f; - k3 = 0.1f; - } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { - k1 = 0.5f; - k2 = 0.3f; - k3 = 0.15f; - } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { - k1 = 0.6f; - k2 = 0.45f; - k3 = 0.3f; - } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { - k1 = 0.7f; - k2 = 0.5f; - k3 = 0.4f; - } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { - k1 = 0.8f; - k2 = 0.6f; - k3 = 0.5f; - } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { - k1 = 0.85f; - k2 = 0.7f; - k3 = 0.6f; - } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { - k1 = 0.9f; - k2 = 0.8f; - k3 = 0.7f; - } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { - k1 = 1.f; - k2 = 1.f; - k3 = 0.9f; - - } else { - k1 = 1.f; - k2 = 1.f; - k3 = 1.f; - } - } else if (aut == 2) { - k1 = 1.f; - k2 = 1.f; - k3 = 1.f; - } - - variC[0] = rtengine::max(minic, variC[0]); - variC[1] = rtengine::max(minic, k1 * variC[1]); - variC[2] = rtengine::max(minic, k2 * variC[2]); - variC[3] = rtengine::max(minic, k3 * variC[3]); - - variCb[0] = rtengine::max(minic, variCb[0]); - variCb[1] = rtengine::max(minic, k1 * variCb[1]); - variCb[2] = rtengine::max(minic, k2 * variCb[2]); - variCb[3] = rtengine::max(minic, k3 * variCb[3]); - - if (levred == 7) { - float k4 = 0.f; - float k5 = 0.f; - float k6 = 0.f; - - if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { - k4 = 0.1f; - k5 = 0.02f; - } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { - k4 = 0.15f; - k5 = 0.05f; - } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { - k4 = 0.15f; - k5 = 0.1f; - } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { - k4 = 0.3f; - k5 = 0.15f; - } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { - k4 = 0.6f; - k5 = 0.4f; - } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { - k4 = 0.8f; - k5 = 0.6f; - } else { - k4 = 1.f; - k5 = 1.f; - } - - - variC[4] = rtengine::max(0.000001f, k4 * variC[4]); - variC[5] = rtengine::max(0.000001f, k5 * variC[5]); - variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); - variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); - - if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { - k6 = 0.f; - } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { - k6 = 0.4f; - } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { - k6 = 0.7f; - } else { - k6 = 1.f; - } - - variC[6] = rtengine::max(0.00001f, k6 * variC[6]); - variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); - } - - float* noisevarchrom = new float[bfh * bfw]; - int bfw2 = (bfw + 1) / 2; - float nvch = 0.6f;//high value - float nvcl = 0.1f;//low value - - if ((lp.noisecf > 30.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { - nvch = 0.8f; - nvcl = 0.4f; - } - - float seuil = 4000.f;//low - float seuil2 = 15000.f;//high - //ac and bc for transition - float ac = (nvch - nvcl) / (seuil - seuil2); - float bc = nvch - seuil * ac; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - float cN = std::sqrt(SQR(bufwv.a[ir][jr]) + SQR(bufwv.b[ir][jr])); - - if (cN < seuil) { - noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvch; - } else if (cN < seuil2) { - noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = ac * cN + bc; - } else { - noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvcl; - } - } - - float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); - - if ((lp.noisecc < 0.02f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - } else { - WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); - - WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); - } - - delete[] noisevarchrom; - } - } - - if (!Ldecomp.memory_allocation_failed()) { - Lin = new array2D(bfw, bfh); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < bfh; ++i) { - for (int j = 0; j < bfw; ++j) { - (*Lin)[i][j] = bufwv.L[i][j]; - } - } - - Ldecomp.reconstruct(bufwv.L[0]); - } - - - if (!Ldecomp.memory_allocation_failed() && aut == 0) { - - - if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { - fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.L, Lin, numThreads, lp, 0); - } - } - - - if (!adecomp.memory_allocation_failed()) { - Ain = new array2D(bfw, bfh); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < bfh; ++i) { - for (int j = 0; j < bfw; ++j) { - (*Ain)[i][j] = bufwv.a[i][j]; - } - } - - adecomp.reconstruct(bufwv.a[0]); - } - - if (!adecomp.memory_allocation_failed() && aut == 0) { - if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { - fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.a, Ain, numThreads, lp, 1); - } - } - - - if (!bdecomp.memory_allocation_failed()) { - Bin = new array2D(bfw, bfh); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < bfh; ++i) { - for (int j = 0; j < bfw; ++j) { - (*Bin)[i][j] = bufwv.b[i][j]; - } - } - - bdecomp.reconstruct(bufwv.b[0]); - } - - if (!bdecomp.memory_allocation_failed() && aut == 0) { - if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { - fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.b, Bin, numThreads, lp, 1); - } - } - - if(lp.smasktyp != 0) { - DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); - } else { - DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); - } - - // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); - } - } - } - -} - -float triangle(float a, float a1, float b) -{ - if (a != b) { - float b1; - float a2 = a1 - a; - - if (b < a) { - b1 = b + a2 * b / a ; - } else { - b1 = b + a2 * (65535.f - b) / (65535.f - a); - } - - return b1; - } - - return a1; -} - -void rgbtone(float& maxval, float& medval, float& minval, const LUTf& lutToneCurve) -{ - float minvalold = minval, medvalold = medval, maxvalold = maxval; - - maxval = lutToneCurve[maxvalold]; - minval = lutToneCurve[minvalold]; - medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); -} - -void clarimerge(struct local_params& lp, float &mL, float &mC, bool &exec, LabImage *tmpresid, int wavelet_level, int sk, int numThreads) -{ - if (mL != 0.f && mC == 0.f) { - mC = 0.0001f; - exec = true; - } - - if (mC != 0.f && mL == 0.f) { - mL = 0.0001f; - exec = true; - } - - if (mL != 0.f && mC != 0.f) { - exec = true; - } - - if (mL != 0.f) { - - wavelet_decomposition *wdspotresid = new wavelet_decomposition(tmpresid->L[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); - - if (wdspotresid->memory_allocation_failed()) { - return; - } - - int maxlvlresid = wdspotresid->maxlevel(); - - if (maxlvlresid > 4) {//Clarity -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) -#endif - for (int dir = 1; dir < 4; dir++) { - for (int level = 0; level < maxlvlresid; ++level) { - int W_L = wdspotresid->level_W(level); - int H_L = wdspotresid->level_H(level); - float* const* wav_Lresid = wdspotresid->level_coeffs(level); - - for (int i = 0; i < W_L * H_L; i++) { - wav_Lresid[dir][i] = 0.f; - } - } - } - } else {//Sharp - float *wav_L0resid = wdspotresid->get_coeff0(); - int W_L = wdspotresid->level_W(0); - int H_L = wdspotresid->level_H(0); - - for (int i = 0; i < W_L * H_L; i++) { - wav_L0resid[i] = 0.f; - } - } - - wdspotresid->reconstruct(tmpresid->L[0], 1.f); - delete wdspotresid; - } - - - if (mC != 0.f) { - - wavelet_decomposition *wdspotresida = new wavelet_decomposition(tmpresid->a[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); - - if (wdspotresida->memory_allocation_failed()) { - return; - } - - int maxlvlresid = wdspotresida->maxlevel(); - - if (maxlvlresid > 4) {//Clarity -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) -#endif - for (int dir = 1; dir < 4; dir++) { - for (int level = 0; level < maxlvlresid; ++level) { - int W_L = wdspotresida->level_W(level); - int H_L = wdspotresida->level_H(level); - float* const* wav_Lresida = wdspotresida->level_coeffs(level); - - for (int i = 0; i < W_L * H_L; i++) { - wav_Lresida[dir][i] = 0.f; - } - } - } - } else {//Sharp - float *wav_L0resida = wdspotresida->get_coeff0(); - int W_L = wdspotresida->level_W(0); - int H_L = wdspotresida->level_H(0); - - for (int i = 0; i < W_L * H_L; i++) { - wav_L0resida[i] = 0.f; - } - } - - wdspotresida->reconstruct(tmpresid->a[0], 1.f); - delete wdspotresida; - - wavelet_decomposition *wdspotresidb = new wavelet_decomposition(tmpresid->b[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); - - if (wdspotresidb->memory_allocation_failed()) { - return; - } - - maxlvlresid = wdspotresidb->maxlevel(); - - if (maxlvlresid > 4) {//Clarity -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic) collapse(2) -#endif - for (int dir = 1; dir < 4; dir++) { - for (int level = 0; level < maxlvlresid; ++level) { - int W_L = wdspotresidb->level_W(level); - int H_L = wdspotresidb->level_H(level); - float* const* wav_Lresidb = wdspotresidb->level_coeffs(level); - - for (int i = 0; i < W_L * H_L; i++) { - wav_Lresidb[dir][i] = 0.f; - } - } - } - } else {//Sharp - float *wav_L0residb = wdspotresidb->get_coeff0(); - int W_L = wdspotresidb->level_W(0); - int H_L = wdspotresidb->level_H(0); - - for (int i = 0; i < W_L * H_L; i++) { - wav_L0residb[i] = 0.f; - } - } - - wdspotresidb->reconstruct(tmpresid->b[0], 1.f); - delete wdspotresidb; - } -} - -void ImProcFunctions::Lab_Local( - int call, int sp, float** shbuffer, LabImage * original, LabImage * transformed, LabImage * reserved, LabImage * lastorig, int cx, int cy, int oW, int oH, int sk, - const LocretigainCurve& locRETgainCcurve, const LocretitransCurve& locRETtransCcurve, - const LUTf& lllocalcurve, bool locallutili, - const LUTf& cllocalcurve, bool localclutili, - const LUTf& lclocalcurve, bool locallcutili, - const LocLHCurve& loclhCurve, const LocHHCurve& lochhCurve, - const LUTf& lmasklocalcurve, bool localmaskutili, - const LUTf& lmaskexplocalcurve, bool localmaskexputili, - const LUTf& lmaskSHlocalcurve, bool localmaskSHutili, - const LUTf& lmaskviblocalcurve, bool localmaskvibutili, - const LUTf& lmasktmlocalcurve, bool localmasktmutili, - LUTf& lmaskretilocalcurve, bool localmaskretiutili, - const LUTf& lmaskcblocalcurve, bool localmaskcbutili, - const LUTf& lmaskbllocalcurve, bool localmaskblutili, - const LUTf& lmasklclocalcurve, bool localmasklcutili, - const LUTf& lmasklocal_curve, bool localmask_utili, - - const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, - const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, - const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, - const LocCCmaskCurve& locccmasvibCurve, bool lcmasvibutili, const LocLLmaskCurve& locllmasvibCurve, bool llmasvibutili, const LocHHmaskCurve& lochhmasvibCurve, bool lhmasvibutili, - const LocCCmaskCurve& locccmascbCurve, bool lcmascbutili, const LocLLmaskCurve& locllmascbCurve, bool llmascbutili, const LocHHmaskCurve& lochhmascbCurve, bool lhmascbutili, - const LocCCmaskCurve& locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve& locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve& lochhmasretiCurve, bool lhmasretiutili, - const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, - const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, - const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, - const LocCCmaskCurve& locccmas_Curve, bool lcmas_utili, const LocLLmaskCurve& locllmas_Curve, bool llmas_utili, const LocHHmaskCurve& lochhmas_Curve, bool lhmas_utili, - const LocHHmaskCurve& lochhhmas_Curve, bool lhhmas_utili, - const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, - const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, - const LocwavCurve& locwavCurve, bool locwavutili, - const LocwavCurve& loclevwavCurve, bool loclevwavutili, - const LocwavCurve& locconwavCurve, bool locconwavutili, - const LocwavCurve& loccompwavCurve, bool loccompwavutili, - const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, - const LocwavCurve& locwavCurveden, bool locwavdenutili, - const LocwavCurve& locedgwavCurve, bool locedgwavutili, - const LocwavCurve& loclmasCurve_wav, bool lmasutili_wav, - - bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, - double& huerefblur, double& chromarefblur, double& lumarefblur, double& hueref, double& chromaref, double& lumaref, double& sobelref, int &lastsav, - bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, int ll_Mask, - float& minCD, float& maxCD, float& mini, float& maxi, float& Tmean, float& Tsigma, float& Tmin, float& Tmax - ) -{ - //general call of others functions : important return hueref, chromaref, lumaref - if (!params->locallab.enabled) { - return; - } - - BENCHFUN - - constexpr int del = 3; // to avoid crash with [loy - begy] and [lox - begx] and bfh bfw // with gtk2 [loy - begy-1] [lox - begx -1 ] and del = 1 - struct local_params lp; - calcLocalParams(sp, oW, oH, params->locallab, lp, prevDeltaE, llColorMask, llColorMaskinv, llExpMask, llExpMaskinv, llSHMask, llSHMaskinv, llvibMask, lllcMask, llsharMask, llcbMask, llretiMask, llsoftMask, lltmMask, llblMask, ll_Mask, locwavCurveden, locwavdenutili); - - const float radius = lp.rad / (sk * 1.4f); //0 to 70 ==> see skip - int levred; - bool noiscfactiv; - - if (lp.qualmet == 2) { //suppress artifacts with quality enhanced - levred = 4; - noiscfactiv = true; - } else { - levred = 7; - noiscfactiv = false; - } - -//lastsav for save restore image - lastsav = 0; - - if (lp.excmet == 1 && call <= 3) {//exclude - const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone - const int bfw = int (lp.lx + lp.lxL) + del; - const int begy = lp.yc - lp.lyT; - const int begx = lp.xc - lp.lxL; - const int yEn = lp.yc + lp.ly; - const int xEn = lp.xc + lp.lx; - LabImage bufreserv(bfw, bfh); - array2D bufsob(bfw, bfh); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if(multiThread) -#endif - for (int y = rtengine::max(begy - cy, 0); y < rtengine::min(yEn - cy, original->H); y++) { - const int loy = cy + y; - - for (int x = rtengine::max(begx - cx, 0); x < rtengine::min(xEn - cx, original->W); x++) { - const int lox = cx + x; - - bufsob[loy - begy][lox - begx] = bufreserv.L[loy - begy][lox - begx] = reserved->L[y][x]; - bufreserv.a[loy - begy][lox - begx] = reserved->a[y][x]; - bufreserv.b[loy - begy][lox - begx] = reserved->b[y][x]; - } - } - - array2D ble(bfw, bfh); - const float radiussob = 1.f / (sk * 1.4f); - SobelCannyLuma(ble, bufsob, bfw, bfh, radiussob); - array2D &guid = bufsob; - -#ifdef _OPENMP - #pragma omp parallel for if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - ble[ir][jr] /= 32768.f; - guid[ir][jr] /= 32768.f; - } - - - const float blur = 25 / sk * (2.f + 2.5f * lp.struexp); - - rtengine::guidedFilter(guid, ble, ble, blur, 0.0001, multiThread); - -// const float blur = 25 / sk * (10.f + 0.8f * lp.struexp); - -// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiThread); - - double sombel = 0.f; - const int ncsobel = bfh * bfw; - - array2D &deltasobelL = guid; - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:sombel) if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float val = ble[ir][jr] * 32768.f; - sombel += val; - deltasobelL[ir][jr] = val; - } - } - - const float meansob = sombel / ncsobel; - Exclude_Local(deltasobelL, hueref, chromaref, lumaref, sobelref, meansob, lp, original, transformed, &bufreserv, reserved, cx, cy, sk); - } - -//encoding lab at the beginning - if (lp.logena) { - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - const int bfh = yend - ystart; - const int bfw = xend - xstart; - - if (bfh >= mSP && bfw >= mSP) { - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if(multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; - bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; - bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - bufexpfin->CopyFrom(bufexporig.get(), multiThread); - std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); - lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); - log_encode(tmpImage.get(), lp, multiThread, bfw, bfh); - rgb2lab(*(tmpImage.get()), *bufexpfin, params->icm.workingProfile); - tmpImage.reset(); - - //here begin graduated filter - //first solution "easy" but we can do other with log_encode...to see the results - if (lp.strlog != 0.f) { - struct grad_params gplog; - calclocalGradientParams(lp, gplog, ystart, xstart, bfw, bfh, 11); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if(multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gplog, jr, ir); - } - } - } - - //end graduated - transit_shapedetect2(call, 11, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - -//Prepare mask for Blur and noise and Denoise - bool denoiz = false; - - if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f || lp.bilat > 0.f) && lp.denoiena) { - denoiz = true; - } - - bool blurz = false; - bool delt = params->locallab.spots.at(sp).deltae; - bool astool = params->locallab.spots.at(sp).toolbl; - - if (((radius > 1.5 * GAUSS_SKIP) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 1 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { - blurz = true; - } - - const int GW = transformed->W; - const int GH = transformed->H; - - LabImage * originalmaskbl = nullptr; - std::unique_ptr bufmaskorigbl; - std::unique_ptr bufmaskblurbl; - std::unique_ptr bufgb; - std::unique_ptr bufprov(new LabImage(GW, GH)); - - if (denoiz || blurz || lp.denoiena || lp.blurena) { - bufgb.reset(new LabImage(GW, GH)); - - if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { - bufmaskorigbl.reset(new LabImage(GW, GH)); - bufmaskblurbl.reset(new LabImage(GW, GH)); - originalmaskbl = new LabImage(GW, GH); - } - - array2D ble(GW, GH); - array2D blechro(GW, GH); - array2D hue(GW, GH); - array2D guid(GW, GH); - float meanfab, fab; - mean_fab(0, 0, GW, GH, bufgb.get(), original, fab, meanfab, lp.chromabl, multiThread); - float chromult = 1.f - 0.01f * lp.chromabl; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH; y++) { - for (int x = 0; x < GW; x++) { - bufgb->L[y][x] = original->L[y][x]; - bufgb->a[y][x] = original->a[y][x]; - bufgb->b[y][x] = original->b[y][x]; - } - } - - const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskbl; - JaggedArray blendstru(GW, GH); - - if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { - if (strumask > 0.f) { - float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 - buildBlendMask(bufgb->L, blendstru, GW, GH, delstrumask); - const float radblur = 0.02f * 0.1f * std::fabs(lp.radmabl); - const float rm = radblur / sk; - - if (rm > 0) { -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(blendstru, blendstru, GW, GH, rm); - } - } - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) { - for (int jr = 0; jr < GW; jr++) { - float kmaskLexp = 0.f; - float kmaskCH = 0.f; - float kmasstru = 0.f; - - if (strumask > 0.f && !astool) { - kmasstru = bufgb->L[ir][jr] * blendstru[ir][jr]; - } - - if (locllmasblCurve && llmasblutili) { - const float ligh = bufgb->L[ir][jr] / 32768.f; - kmaskLexp = 32768.f * LIM01(1.f - locllmasblCurve[500.f * ligh]); - } - - if (lp.showmaskblmet != 4) { - if (locccmasblCurve && lcmasblutili) { - const float chromask = 0.0001f + std::sqrt(SQR((bufgb->a[ir][jr]) / fab) + SQR((bufgb->b[ir][jr]) / fab)); - kmaskCH = LIM01(1.f - locccmasblCurve[500.f * chromask]); - } - } - - if (lochhmasblCurve && lhmasblutili) { - const float huema = xatan2f(bufgb->b[ir][jr], bufgb->a[ir][jr]); - float h = Color::huelab_to_huehsv2(huema); - h += 1.f / 6.f; - - if (h > 1.f) { - h -= 1.f; - } - - const float valHH = LIM01(1.f - lochhmasblCurve[500.f * h]); - - if (lp.showmaskblmet != 4) { - kmaskCH += chromult * valHH; - } - - kmaskLexp += 32768.f * valHH; - } - - bufmaskblurbl->L[ir][jr] = clipLoc(kmaskLexp + kmasstru); - bufmaskblurbl->a[ir][jr] = kmaskCH; - bufmaskblurbl->b[ir][jr] = kmaskCH; - ble[ir][jr] = bufmaskblurbl->L[ir][jr] / 32768.f; - hue[ir][jr] = xatan2f(bufmaskblurbl->b[ir][jr], bufmaskblurbl->a[ir][jr]); - const float chromah = std::sqrt(SQR(bufmaskblurbl->b[ir][jr]) + SQR(bufmaskblurbl->a[ir][jr])); - blechro[ir][jr] = chromah / 32768.f; - guid[ir][jr] = Color::L2Y(bufgb->L[ir][jr]) / 32768.f; - } - } - - const std::unique_ptr bufprov(new LabImage(GW, GH)); - - bufprov->CopyFrom(bufmaskblurbl.get(), multiThread); - - if (lp.radmabl != 0.f) { - float blur = lp.radmabl; - blur = blur < 0.f ? -1.f / blur : 1.f + blur; - const int r1 = rtengine::max(4 / sk * blur + 0.5f, 1); - const int r2 = rtengine::max(25 / sk * blur + 0.5f, 1); - - constexpr float epsilmax = 0.005f; - constexpr float epsilmin = 0.00001f; - - const float aepsil = (epsilmax - epsilmin) / 100.f; - const float bepsil = epsilmin; //epsilmax - 100.f * aepsil; - const float epsil = lp.radmabl < 0.f ? 0.001f : aepsil * lp.radmabl + bepsil; - - rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); - rtengine::guidedFilter(guid, ble, ble, r2, 0.2 * epsil, multiThread); - - // guidedFilter(guid, ble, ble, lp.radmabl * 10.f / sk, 0.001, multiThread, 4); - } - - LUTf lutTonemaskbl(65536); - calcGammaLut(lp.gammabl, lp.slomabl, lutTonemaskbl); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) { - for (int jr = 0; jr < GW; jr++) { - const float2 sincosval = xsincosf(hue[ir][jr]); - bufmaskblurbl->L[ir][jr] = LIM01(ble[ir][jr]) * 32768.f; - const float L_ = 2.f * bufmaskblurbl->L[ir][jr]; - bufmaskblurbl->L[ir][jr] = lutTonemaskbl[L_]; - bufmaskblurbl->a[ir][jr] = 32768.f * sincosval.y * blechro[ir][jr]; - bufmaskblurbl->b[ir][jr] = 32768.f * sincosval.x * blechro[ir][jr]; - } - } - } - - if (strumask > 0.f && astool && (lp.enablMask || lp.showmaskblmet == 3)) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) { - for (int jr = 0; jr < GW; jr++) { - bufmaskblurbl->L[ir][jr] *= (1.f + blendstru[ir][jr]); - } - } - } - - if (lmaskbllocalcurve && localmaskblutili && (lp.enablMask || lp.showmaskblmet == 3)) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) - for (int jr = 0; jr < GW; jr++) { - bufmaskblurbl->L[ir][jr] = 0.5f * lmaskbllocalcurve[2.f * bufmaskblurbl->L[ir][jr]]; - } - } - - const int highli = params->locallab.spots.at(sp).shadmaskbl; - - if (highli > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { - ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, highli, 0, 40, sk, 50, 0); - } - - const int shado = params->locallab.spots.at(sp).shadmaskblsha; - - if (shado > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { - ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, 0, shado, 40, sk, 0, 60); - } - - int wavelet_level = params->locallab.spots.at(sp).shadmaskbl; - int maxlvl = wavelet_level; - - int minwin = rtengine::min(GW, GH); - int maxlevelspot = 9; - - while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { - --maxlevelspot ; - } - - wavelet_level = rtengine::min(wavelet_level, maxlevelspot); - bool wavcurvemask = false; - - if (loclmasCurveblwav && lmasutiliblwav && (lp.enablMask || lp.showmaskblmet == 3)) { - for (int i = 0; i < 500; i++) { - if (loclmasCurveblwav[i] != 0.5) { - wavcurvemask = true; - } - } - } - - if (wavcurvemask && (lp.enablMask || lp.showmaskblmet == 3)) { - const int level_bl = params->locallab.spots.at(sp).csthresholdblur.getBottomLeft(); - const int level_hl = params->locallab.spots.at(sp).csthresholdblur.getTopLeft(); - const int level_br = params->locallab.spots.at(sp).csthresholdblur.getBottomRight(); - const int level_hr = params->locallab.spots.at(sp).csthresholdblur.getTopRight(); - -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; - -#endif - - wavelet_decomposition *wdspotbl = new wavelet_decomposition(bufmaskblurbl->L[0], GW, GH, maxlvl, 1, sk, numThreads, lp.daubLen); - if (wdspotbl->memory_allocation_failed()) { - return; - } - - - float mean[10]; - float meanN[10]; - float sigma[10]; - float sigmaN[10]; - float MaxP[10]; - float MaxN[10]; - - Evaluate2(*wdspotbl, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); - float alow = 1.f; - float blow = 0.f; - if (level_hl != level_bl) { - alow = 1.f / (level_hl - level_bl); - blow = -alow * level_bl; - } - - float ahigh = 1.f; - float bhigh = 0.f; - - if (level_hr != level_br) { - ahigh = 1.f / (level_hr - level_br); - bhigh = -ahigh * level_br; - } - - for (int dir = 1; dir < 4; dir++) { - for (int level = level_bl; level < maxlvl; ++level) { - int W_L = wdspotbl->level_W(level); - int H_L = wdspotbl->level_H(level); - float* const *wav_L = wdspotbl->level_coeffs(level); - - if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { - float insigma = 0.666f; //SD - float logmax = log(MaxP[level]); //log Max - float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max - float inx = log(insigma); - float iny = log(rapX); - float rap = inx / iny; //koef - float asig = 0.166f / (sigma[level]); - float bsig = 0.5f - asig * mean[level]; - float amean = 0.5f / mean[level]; - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_L * H_L; i++) { - if(loclmasCurveblwav && lmasutiliblwav) { - float absciss; - float &val = wav_L[dir][i]; - - if (fabsf(val) >= (mean[level] + sigma[level])) { //for max - float valcour = xlogf(fabsf(val)); - float valc = valcour - logmax; - float vald = valc * rap; - absciss = xexpf(vald); - } else if (fabsf(val) >= mean[level]) { - absciss = asig * fabsf(val) + bsig; - } else { - absciss = amean * fabsf(val); - } - - float klev = 1.f; - if (level >= level_hl && level <= level_hr) { - klev = 1.f; - } - - if (level_hl != level_bl) { - if (level >= level_bl && level < level_hl) { - klev = alow * level + blow; - } - } - - if (level_hr != level_br) { - if (level > level_hr && level <= level_br) { - klev = ahigh * level + bhigh; - } - } - float kc = klev * (loclmasCurveblwav[absciss * 500.f] - 0.5f); - float amplieffect = kc <= 0.f ? 1.f : 4.f; - - float kinterm = 1.f + amplieffect * kc; - kinterm = kinterm <= 0.f ? 0.01f : kinterm; - - val *= kinterm; - - } - } - } - - } - } - wdspotbl->reconstruct(bufmaskblurbl->L[0], 1.f); - delete wdspotbl; - - } - - - // deltae Mask with scope - int sco = params->locallab.spots.at(sp).scopemask; - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - - if (delt && lp.blurmet == 0 && (lp.enablMask || lp.showmaskblmet == 3)) { - JaggedArray rdE(GW, GH); - deltaEforMask(rdE, GW, GH, bufgb.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.balance, lp.balanceh); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) { - for (int jr = 0; jr < GW; jr++) { - bufmaskblurbl->L[ir][jr] = bufprov->L[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->L[ir][jr] - bufprov->L[ir][jr]); - bufmaskblurbl->a[ir][jr] = bufprov->a[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->a[ir][jr] - bufprov->a[ir][jr]); - bufmaskblurbl->b[ir][jr] = bufprov->b[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->b[ir][jr] - bufprov->b[ir][jr]); - } - } - } - - const float lap = params->locallab.spots.at(sp).lapmaskbl; - const bool pde = params->locallab.spots.at(sp).laplac; - const float lumask = params->locallab.spots.at(sp).lumask; - - if (lap > 0.f && (lp.enablMask || lp.showmaskblmet == 3)) { - const float *datain = bufmaskblurbl->L[0]; - const std::unique_ptr data_tmp(new float[GH * GW]); - - if (!pde) { - ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, GW, GH, 200.f * lap); - } else { - ImProcFunctions::retinex_pde(datain, data_tmp.get(), GW, GH, 12.f * lap, 1.f, nullptr, 0, 0, 1); - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < GH; y++) { - for (int x = 0; x < GW; x++) { - bufmaskblurbl->L[y][x] = data_tmp[y * GW + x]; - } - } - } - - const float radiusb = 1.f / sk; - - if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { - const int invers = lp.blurmet == 1 ? 1 : 0; - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufmaskblurbl->L, bufmaskorigbl->L, GW, GH, radiusb); - gaussianBlur(bufmaskblurbl->a, bufmaskorigbl->a, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); - gaussianBlur(bufmaskblurbl->b, bufmaskorigbl->b, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); - } - - if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { - blendmask(lp, 0, 0, cx, cy, GW, GH, bufgb.get(), original, bufmaskorigbl.get(), originalmaskbl, lp.blendmabl, lp.blendmabl, invers); - } else if (lp.showmaskblmet == 3) { - showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufgb.get(), transformed, bufmaskorigbl.get(), invers); - return; - } - } - -//end mask - } - - bool execmaskblur = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 1; - if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || execmaskblur) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image - // if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image - std::unique_ptr tmp1; - std::unique_ptr tmp2; - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - int bfhr = bfh; - int bfwr = bfw; - - bool fft = params->locallab.spots.at(sp).fftwbl; - int isogr = params->locallab.spots.at(sp).isogr; - int strengr = params->locallab.spots.at(sp).strengr; - int scalegr = params->locallab.spots.at(sp).scalegr; - - - - if (bfw >= mSP && bfh >= mSP) { - if (lp.blurmet == 0 && (fft || lp.rad > 30.f)) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - const std::unique_ptr bufgbi(new LabImage(GW, GH)); - - //here mask is used with plain image for normal and inverse - //if it is possible to optimize with maskcalccol(), I don't to preserve visibility - if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { - - if (lp.blurmet == 0) { - if (bfw > 0 && bfh > 0) { - tmp1.reset(new LabImage(bfw, bfh)); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend ; y++) { - for (int x = xstart; x < xend; x++) { - tmp1->L[y - ystart][x - xstart] = original->L[y][x]; - tmp1->a[y - ystart][x - xstart] = original->a[y][x]; - tmp1->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - } - } else if (lp.blurmet == 1) { - tmp1.reset(new LabImage(transformed->W, transformed->H)); - tmp2.reset(new LabImage(transformed->W, transformed->H)); - - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - tmp2->L[y][x] = original->L[y][x]; - tmp2->a[y][x] = original->a[y][x]; - tmp2->b[y][x] = original->b[y][x]; - bufgbi->L[y][x] = original->L[y][x]; - bufgbi->a[y][x] = original->a[y][x]; - bufgbi->b[y][x] = original->b[y][x]; - } - } - - } - - - if (lp.blurmet == 0 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { - if (fft || lp.rad > 30.f) { - if (lp.chromet == 0) { - ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); - } else if (lp.chromet == 1) { - ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); - } else if (lp.chromet == 2) { - ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); - } - } else { - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - if (lp.chromet == 0) - { - gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); - } - - else if (lp.chromet == 1) - { - gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); - gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); - } else if (lp.chromet == 2) - { - gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); - gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); - gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); - } - } - } - - } else if (lp.blurmet == 1 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { - if (fft || lp.rad > 30.f) { - if (lp.chromet == 0) { - ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); - } - - else if (lp.chromet == 1) { - ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); - } else if (lp.chromet == 2) { - ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); - ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); - } - - } else { - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - if (lp.chromet == 0) - { - gaussianBlur(original->L, tmp1->L, GW, GH, radius); - } else if (lp.chromet == 1) - - { - gaussianBlur(original->a, tmp1->a, GW, GH, radius); - gaussianBlur(original->b, tmp1->b, GW, GH, radius); - } else if (lp.chromet == 2) - - { - gaussianBlur(original->L, tmp1->L, GW, GH, radius); - gaussianBlur(original->a, tmp1->a, GW, GH, radius); - gaussianBlur(original->b, tmp1->b, GW, GH, radius); - } - } - } - } - - - //add noise - if (tmp1.get() && lp.stren > 0.1f && lp.blmet == 0) { - float mean = 0.f;//0 best result - float variance = lp.stren ; - addGaNoise(tmp1.get(), tmp1.get(), mean, variance, sk) ; - } - - //add grain - if (lp.blmet == 0 && strengr > 0) { - int wi = bfw; - int he = bfh; - - if (lp.blurmet == 1) { - wi = GW; - he = GH; - } - - if (tmp1.get()) { - Imagefloat *tmpImage = nullptr; - tmpImage = new Imagefloat(wi, he); - - for (int y = 0; y < he ; y++) { - for (int x = 0; x < wi; x++) { - tmpImage->g(y, x) = tmp1->L[y][x]; - tmpImage->r(y, x) = tmp1->a[y][x]; - tmpImage->b(y, x) = tmp1->b[y][x]; - } - } - - - filmGrain(tmpImage, isogr, strengr, scalegr, wi, he); - - for (int y = 0; y < he ; y++) { - for (int x = 0; x < wi; x++) { - tmp1->L[y][x] = tmpImage->g(y, x); - tmp1->a[y][x] = tmpImage->r(y, x); - tmp1->b[y][x] = tmpImage->b(y, x); - } - } - - delete tmpImage; - } - } - - Median medianTypeL = Median::TYPE_3X3_STRONG; - Median medianTypeAB = Median::TYPE_3X3_STRONG; - - if (lp.medmet == 0) { - medianTypeL = medianTypeAB = Median::TYPE_3X3_STRONG; - } else if (lp.medmet == 1) { - medianTypeL = medianTypeAB = Median::TYPE_5X5_STRONG; - } else if (lp.medmet == 2) { - medianTypeL = medianTypeAB = Median::TYPE_7X7; - } else if (lp.medmet == 3) { - medianTypeL = medianTypeAB = Median::TYPE_9X9; - } - - if (lp.blurmet == 0 && lp.blmet == 1 && lp.medmet != -1) { - float** tmL; - int wid = bfw; - int hei = bfh; - tmL = new float*[hei]; - - for (int i = 0; i < hei; ++i) { - tmL[i] = new float[wid]; - } - - if (lp.chromet == 0) { - Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); - } - - else if (lp.chromet == 1) { - Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); - Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); - } else if (lp.chromet == 2) { - Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); - Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); - Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); - } - - for (int i = 0; i < hei; ++i) { - delete[] tmL[i]; - } - - delete[] tmL; - - } else if (lp.blurmet == 1 && lp.blmet == 1) { - float** tmL; - int wid = GW; - int hei = GH; - tmL = new float*[hei]; - - for (int i = 0; i < hei; ++i) { - tmL[i] = new float[wid]; - } - - if (lp.chromet == 0) { - Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); - } else if (lp.chromet == 1) { - Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); - Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); - } else if (lp.chromet == 2) { - Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); - Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); - Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); - } - - for (int i = 0; i < hei; ++i) { - delete[] tmL[i]; - } - - delete[] tmL; - } - - if (lp.blurmet == 0 && lp.blmet == 2) { - - if (lp.guidb > 0) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend ; y++) { - for (int x = xstart; x < xend; x++) { - tmp1->L[y - ystart][x - xstart] = original->L[y][x]; - tmp1->a[y - ystart][x - xstart] = original->a[y][x]; - tmp1->b[y - ystart][x - xstart] = original->b[y][x]; - bufgb->L[y - ystart][x - xstart] = original->L[y][x]; - } - } - - Imagefloat *tmpImage = nullptr; - tmpImage = new Imagefloat(bfw, bfh); - lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); - array2D LL(bfw, bfh); - array2D rr(bfw, bfh); - array2D gg(bfw, bfh); - array2D bb(bfw, bfh); - array2D guide(bfw, bfh); - - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - LL[y][x] = tmp1->L[y][x]; - float ll = LL[y][x] / 32768.f; - guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); - rr[y][x] = tmpImage->r(y, x); - gg[y][x] = tmpImage->g(y, x); - bb[y][x] = tmpImage->b(y, x); - - } - } - array2D iR(bfw, bfh, rr, 0); - array2D iG(bfw, bfh, gg, 0); - array2D iB(bfw, bfh, bb, 0); - array2D iL(bfw, bfh, LL, 0); - - int r = rtengine::max(int(lp.guidb / sk), 1); - - const float epsil = 0.001f * std::pow(2, - lp.epsb); - - if (lp.chromet == 0) { - rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); - } else if (lp.chromet == 1) { - rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); - rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); - } else if (lp.chromet == 2) { - rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); - rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); - rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); - gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); - bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); - tmpImage->r(y, x) = rr[y][x]; - tmpImage->g(y, x) = gg[y][x]; - tmpImage->b(y, x) = bb[y][x]; - - } - } - - rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); - - if (lp.chromet == 0) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); - tmp1->L[y][x] = LL[y][x]; - } - } - } - - delete tmpImage; - } - - } else if (lp.blurmet == 1 && lp.blmet == 2) { - - if (lp.guidb > 0) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - tmp1->L[y][x] = original->L[y][x]; - tmp1->a[y][x] = original->a[y][x]; - tmp1->b[y][x] = original->b[y][x]; - tmp2->L[y][x] = original->L[y][x]; - } - } - - Imagefloat *tmpImage = nullptr; - tmpImage = new Imagefloat(GW, GH); - lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); - array2D LL(GW, GH); - array2D rr(GW, GH); - array2D gg(GW, GH); - array2D bb(GW, GH); - array2D guide(GW, GH); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - LL[y][x] = tmp1->L[y][x]; - float ll = LL[y][x] / 32768.f; - guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); - rr[y][x] = tmpImage->r(y, x); - gg[y][x] = tmpImage->g(y, x); - bb[y][x] = tmpImage->b(y, x); - - } - } - - array2D iR(GW, GH, rr, 0); - array2D iG(GW, GH, gg, 0); - array2D iB(GW, GH, bb, 0); - array2D iL(GW, GH, LL, 0); - - int r = rtengine::max(int(lp.guidb / sk), 1); - - const float epsil = 0.001f * std::pow(2, - lp.epsb); - - if (lp.chromet == 0) { - rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); - } else if (lp.chromet == 1) { - rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); - rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); - } else if (lp.chromet == 2) { - rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); - rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); - rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); - gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); - bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); - tmpImage->r(y, x) = rr[y][x]; - tmpImage->g(y, x) = gg[y][x]; - tmpImage->b(y, x) = bb[y][x]; - - } - } - - rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); - - if (lp.chromet == 0) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); - tmp1->L[y][x] = LL[y][x]; - } - } - } - delete tmpImage; - } - } - - if (tmp1.get()) { - JaggedArray bufchro(lp.blurmet == 1 ? GW : bfw, lp.blurmet == 1 ? GH : bfh); - float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); - float maxC = minC; -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); - minC = rtengine::min(minC, bufchro[ir][jr]); - maxC = rtengine::max(maxC, bufchro[ir][jr]); - } - } - - float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); - - if (coefC > 0.f) { - coefC = 1.f / coefC; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufchro[y][x] *= coefC; - } - } - } - - if (lp.blurmet == 0) { //blur and noise (center) -// BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if(lp.smasktyp != 1) { - BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - } else { - BlurNoise_Local(tmp1.get(), original, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - } - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } else if (lp.blurmet == 1) { - // InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); - if(lp.smasktyp != 1) { - InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); - } else { - InverseBlurNoise_Local(original, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); - } - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - } - } - } - - //local impulse - if ((lp.bilat > 0.f) && lp.denoiena) { - const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone - const int bfw = int (lp.lx + lp.lxL) + del; - - std::unique_ptr bufwv; - - if (call == 2) {//simpleprocess - bufwv.reset(new LabImage(bfw, bfh)); //buffer for data in zone limit - - const int begy = lp.yc - lp.lyT; - const int begx = lp.xc - lp.lxL; - const int yEn = lp.yc + lp.ly; - const int xEn = lp.xc + lp.lx; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = rtengine::max(0, begy - cy); y < rtengine::min(transformed->H, yEn - cy); y++) { - const int loy = cy + y; - - for (int x = rtengine::max(0, begx - cx); x < rtengine::min(transformed->W, xEn - cx); x++) { - const int lox = cx + x; - bufwv->L[loy - begy][lox - begx] = original->L[y][x]; - bufwv->a[loy - begy][lox - begx] = original->a[y][x]; - bufwv->b[loy - begy][lox - begx] = original->b[y][x]; - } - } - } else {//dcrop.cc - bufwv.reset(new LabImage(transformed->W, transformed->H)); - bufwv->CopyFrom(original, multiThread); - } //end dcrop - - const double threshold = lp.bilat / 20.0; - - if (bfh > 8 && bfw > 8) { - ImProcFunctions::impulse_nr(bufwv.get(), threshold); - } - - DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, *(bufwv.get()), cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - -//local denoise - - if (lp.denoiena) { - float slidL[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float slida[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float slidb[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - constexpr int aut = 0; - DeNoise(call, del, slidL, slida, slidb, aut, noiscfactiv, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - - if (denoiz || blurz || lp.denoiena || lp.blurena) { - delete originalmaskbl; - } - -//begin cbdl - if ((lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f || lp.clarityml != 0.f || lp.contresid != 0.f || lp.enacbMask || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4 || lp.prevdE) && lp.cbdlena) { - if (call <= 3) { //call from simpleprocess dcrop improcc - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - - if (bfw > 65 && bfh > 65) { - array2D bufsh(bfw, bfh); - JaggedArray bufchrom(bfw, bfh, true); - const std::unique_ptr loctemp(new LabImage(bfw, bfh)); - const std::unique_ptr origcbdl(new LabImage(bfw, bfh)); - std::unique_ptr bufmaskorigcb; - std::unique_ptr bufmaskblurcb; - std::unique_ptr originalmaskcb; - - if (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4) { - bufmaskorigcb.reset(new LabImage(bfw, bfh)); - bufmaskblurcb.reset(new LabImage(bfw, bfh)); - originalmaskcb.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - loctemp->L[y][x] = original->L[y + ystart][x + xstart]; - } - } - - int inv = 0; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmaskcbmet == 3) { - showmaske = true; - } - - if (lp.enacbMask) { - enaMask = true; - } - - if (lp.showmaskcbmet == 4) { - deltaE = true; - } - - if (lp.showmaskcbmet == 2) { - modmask = true; - } - - if (lp.showmaskcbmet == 1) { - modif = true; - } - - if (lp.showmaskcbmet == 0) { - zero = true; - } - - float chrom = lp.chromacbm;; - float rad = lp.radmacb; - float gamma = lp.gammacb; - float slope = lp.slomacb; - float blendm = lp.blendmacb; - float lap = params->locallab.spots.at(sp).lapmaskcb; - bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int lumask = params->locallab.spots.at(sp).lumask; - int shado = 0; - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - bool lmasutilicolwav = false; - float amountcd = 0.f; - float anchorcd = 50.f; - int shortcu = 0; //lp.mergemet; //params->locallab.spots.at(sp).shortc; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, loctemp.get(), bufmaskorigcb.get(), originalmaskcb.get(), original, reserved, inv, lp, - 0.f, false, - locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.0f, 0.f, -1 - ); - - if (lp.showmaskcbmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, loctemp.get(), transformed, bufmaskorigcb.get(), 0); - - return; - } - - constexpr float b_l = -5.f; - constexpr float t_l = 25.f; - constexpr float t_r = 120.f; - constexpr float b_r = 170.f; - constexpr double skinprot = 0.; - int choice = 0; - - if (lp.showmaskcbmet == 0 || lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 4 || lp.enacbMask) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufsh[y - ystart][x - xstart] = origcbdl->L[y - ystart][x - xstart] = original->L[y][x]; - loctemp->a[y - ystart][x - xstart] = origcbdl->a[y - ystart][x - xstart] = original->a[y][x]; - loctemp->b[y - ystart][x - xstart] = origcbdl->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - if (lp.clarityml != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 - lp.mulloc[5] = 1.001f; - } - - if (lp.contresid != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 - lp.mulloc[5] = 1.001f; - } - - ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, lp.mulloc, 1.f, lp.threshol, lp.clarityml, lp.contresid, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); - - if (lp.softradiuscb > 0.f) { - softproc(origcbdl.get(), loctemp.get(), lp.softradiuscb, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); - } - - } - - transit_shapedetect(6, loctemp.get(), originalmaskcb.get(), bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - bool nochroma = (lp.showmaskcbmet == 2 || lp.showmaskcbmet == 1); - - //chroma CBDL begin here - if (lp.chromacb > 0.f && !nochroma) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufsh[ir][jr] = std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr])); - } - } - - float multc[6]; - float clarich = 0.5f * lp.clarityml; - - if (clarich > 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity - lp.mulloc[0] = 1.01f; - } - - if (lp.contresid != 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity - lp.mulloc[0] = 1.01f; - } - - for (int lv = 0; lv < 6; lv++) { - multc[lv] = rtengine::max((lp.chromacb * (lp.mulloc[lv] - 1.f)) + 1.f, 0.01f); - } - - choice = 1; - ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, multc, rtengine::max(lp.chromacb, 1.f), lp.threshol, clarich, 0.f, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); - - - float minC = loctemp->L[0][0] - std::sqrt(SQR(loctemp->a[0][0]) + SQR(loctemp->b[0][0])); - float maxC = minC; -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufchrom[ir][jr] = (loctemp->L[ir][jr] - std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr]))); - minC = rtengine::min(minC, bufchrom[ir][jr]); - maxC = rtengine::max(maxC, bufchrom[ir][jr]); - } - } - - float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); - - if (coefC > 0.f) { - coefC = 1.f / coefC; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufchrom[ir][jr] *= coefC; - } - } - } - - transit_shapedetect(7, loctemp.get(), nullptr, bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - bufsh.free(); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - } - } - - -//end cbdl_Local - -//vibrance - - if (lp.expvib && (lp.past != 0.f || lp.satur != 0.f || lp.strvib != 0.f || lp.war != 0 || lp.strvibab != 0.f || lp.strvibh != 0.f || lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4 || lp.prevdE) && lp.vibena) { //interior ellipse renforced lightness and chroma //locallutili - if (call <= 3) { //simpleprocess, dcrop, improccoordinator - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - const int bfh = yend - ystart; - const int bfw = xend - xstart; - - if (bfw >= mSP && bfh >= mSP) { - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); - std::unique_ptr bufmaskorigvib; - std::unique_ptr bufmaskblurvib; - std::unique_ptr originalmaskvib; - - if (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4) { - bufmaskorigvib.reset(new LabImage(bfw, bfh)); - bufmaskblurvib.reset(new LabImage(bfw, bfh)); - originalmaskvib.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; - } - } - - int inv = 0; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmaskvibmet == 3) { - showmaske = true; - } - - if (lp.enavibMask) { - enaMask = true; - } - - if (lp.showmaskvibmet == 4) { - deltaE = true; - } - - if (lp.showmaskvibmet == 2) { - modmask = true; - } - - if (lp.showmaskvibmet == 1) { - modif = true; - } - - if (lp.showmaskvibmet == 0) { - zero = true; - } - - float chrom = lp.chromavib; - float rad = lp.radmavib; - float gamma = lp.gammavib; - float slope = lp.slomavib; - float blendm = lp.blendmavib; - float lap = params->locallab.spots.at(sp).lapmaskvib; - bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - int shado = 0; - int lumask = params->locallab.spots.at(sp).lumask; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - float amountcd = 0.f; - float anchorcd = 50.f; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigvib.get(), originalmaskvib.get(), original, reserved, inv, lp, - 0.f, false, - locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - if (lp.showmaskvibmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigvib.get(), 0); - - return; - } - - if (lp.showmaskvibmet == 0 || lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2 || lp.showmaskvibmet == 4 || lp.enavibMask) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; - bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; - bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - VibranceParams vibranceParams; - vibranceParams.enabled = params->locallab.spots.at(sp).expvibrance; - vibranceParams.pastels = params->locallab.spots.at(sp).pastels; - vibranceParams.saturated = params->locallab.spots.at(sp).saturated; - vibranceParams.psthreshold = params->locallab.spots.at(sp).psthreshold; - vibranceParams.protectskins = params->locallab.spots.at(sp).protectskins; - vibranceParams.avoidcolorshift = params->locallab.spots.at(sp).avoidcolorshift; - vibranceParams.pastsattog = params->locallab.spots.at(sp).pastsattog; - vibranceParams.skintonescurve = params->locallab.spots.at(sp).skintonescurve; - - - bufexpfin->CopyFrom(bufexporig.get(), multiThread); - - if (lp.strvibh != 0.f) { - struct grad_params gph; - calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 9); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - double factor = 1.0; - factor = ImProcFunctions::calcGradientFactor(gph, jr, ir); - float aa = bufexpfin->a[ir][jr]; - float bb = bufexpfin->b[ir][jr]; - float chrm = std::sqrt(SQR(aa) + SQR(bb)); - float HH = xatan2f(bb, aa); - - float newhr = 0.f; - float cor = 0.f; - - if (factor < 1.f) { - cor = - 2.5f * (1.f - factor); - } else if (factor > 1.f) { - cor = 0.03f * (factor - 1.f); - } - - newhr = HH + cor; - - if (newhr > rtengine::RT_PI_F) { - newhr -= 2 * rtengine::RT_PI_F; - } else if (newhr < -rtengine::RT_PI_F) { - newhr += 2 * rtengine::RT_PI_F; - } - - float2 sincosval = xsincosf(newhr); - bufexpfin->a[ir][jr] = clipC(chrm * sincosval.y); - bufexpfin->b[ir][jr] = clipC(chrm * sincosval.x); - } - } - - if (lp.strvib != 0.f) { - struct grad_params gp; - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 7); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - double factor = 1.0; - factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); - bufexpfin->L[ir][jr] *= factor; - } - } - - if (lp.strvibab != 0.f) { - struct grad_params gpab; - calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 8); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - double factor = 1.0; - factor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); - bufexpfin->a[ir][jr] *= factor; - bufexpfin->b[ir][jr] *= factor; - } - } - - ImProcFunctions::vibrance(bufexpfin.get(), vibranceParams, params->toneCurve.hrenabled, params->icm.workingProfile); - - if (params->locallab.spots.at(sp).warm != 0) { - ImProcFunctions::ciecamloc_02float(sp, bufexpfin.get()); - } - - - transit_shapedetect2(call, 2, bufexporig.get(), bufexpfin.get(), originalmaskvib.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - - } - - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - } - - -//Tone mapping - - if ((lp.strengt != 0.f || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4 || lp.prevdE) && lp.tonemapena && !params->epd.enabled) { - if (call <= 3) { //simpleprocess dcrop improcc - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - const int bfh = yend - ystart; - const int bfw = xend - xstart; - - if (bfw >= mSP && bfh >= mSP) { - array2D buflight(bfw, bfh); - JaggedArray bufchro(bfw, bfh); - std::unique_ptr bufgb(new LabImage(bfw, bfh)); - const std::unique_ptr tmp1(new LabImage(bfw, bfh)); - const std::unique_ptr bufgbm(new LabImage(bfw, bfh)); - const std::unique_ptr tmp1m(new LabImage(bfw, bfh)); - std::unique_ptr bufmaskorigtm; - std::unique_ptr bufmaskblurtm; - std::unique_ptr originalmasktm; - - // if (lp.showmasktmmet == 0 || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { - if (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { - bufmaskorigtm.reset(new LabImage(bfw, bfh)); - bufmaskblurtm.reset(new LabImage(bfw, bfh)); - originalmasktm.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufgb->L[y - ystart][x - xstart] = original->L[y][x]; - bufgb->a[y - ystart][x - xstart] = original->a[y][x]; - bufgb->b[y - ystart][x - xstart] = original->b[y][x]; - bufgbm->L[y - ystart][x - xstart] = original->L[y][x]; - bufgbm->a[y - ystart][x - xstart] = original->a[y][x]; - bufgbm->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - int inv = 0; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmasktmmet == 3) { - showmaske = true; - } - - if (lp.enatmMask) { - enaMask = true; - } - - if (lp.showmasktmmet == 4) { - deltaE = true; - } - - if (lp.showmasktmmet == 2) { - modmask = true; - } - - if (lp.showmasktmmet == 1) { - modif = true; - } - - if (lp.showmasktmmet == 0) { - zero = true; - } - - float chrom = lp.chromatm;; - float rad = lp.radmatm; - float gamma = lp.gammatm; - float slope = lp.slomatm; - float blendm = lp.blendmatm; - float lap = params->locallab.spots.at(sp).lapmasktm; - bool pde = params->locallab.spots.at(sp).laplac; - int lumask = params->locallab.spots.at(sp).lumask; - - if (!params->locallab.spots.at(sp).enatmMaskaft) { - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = 0; //lp.mergemet;// params->locallab.spots.at(sp).shortc; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - int shado = 0; - float amountcd = 0.f; - float anchorcd = 50.f; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgbm.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, - 0.f, false, - locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - if (lp.showmasktmmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgbm.get(), transformed, bufmaskorigtm.get(), 0); - - return; - } - } - - if (lp.showmasktmmet == 0 || lp.showmasktmmet == 1 || lp.showmasktmmet == 2 || lp.showmasktmmet == 4 || lp.showmasktmmet == 3 || lp.enatmMask) { - constexpr int itera = 0; - ImProcFunctions::EPDToneMaplocal(sp, bufgb.get(), tmp1.get(), itera, sk);//iterate to 0 calculate with edgstopping, improve result, call=1 dcrop we can put iterate to 5 - - tmp1m->CopyFrom(tmp1.get(), multiThread); //save current result - bool enatmMasktmap = params->locallab.spots.at(sp).enatmMaskaft; - - if (enatmMasktmap) { - //calculate new values for original, originalmasktm, bufmaskorigtm...in function of tmp1 - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - int lumask = params->locallab.spots.at(sp).lumask; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - int shado = 0; - float amountcd = 0.f; - float anchorcd = 50.f; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, tmp1.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, - 0.f, false, - locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - if (lp.showmasktmmet == 3) {//display mask - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, tmp1.get(), transformed, bufmaskorigtm.get(), 0); - - return; - } - - } - - tmp1->CopyFrom(tmp1m.get(), multiThread); //restore current result - - - float minL = tmp1->L[0][0] - bufgb->L[0][0]; - float maxL = minL; - float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); - float maxC = minC; - -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxL) reduction(min:minL) reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - buflight[ir][jr] = tmp1->L[ir][jr] - bufgb->L[ir][jr]; - minL = rtengine::min(minL, buflight[ir][jr]); - maxL = rtengine::max(maxL, buflight[ir][jr]); - bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); - minC = rtengine::min(minC, bufchro[ir][jr]); - maxC = rtengine::max(maxC, bufchro[ir][jr]); - } - } - - float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); - float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); - - if (coef == 0.f) { - coef = 1.f; - } else { - coef = 1.f / coef; - } - - if (coefC == 0.f) { - coefC = 1.f; - } else { - coefC = 1.f / coefC; - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - buflight[y][x] *= coef; - bufchro[y][x] *= coefC; - } - } - - // transit_shapedetect_retinex(call, 4, bufgb.get(),bufmaskorigtm.get(), originalmasktm.get(), buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - transit_shapedetect2(call, 8, bufgb.get(), tmp1.get(), originalmasktm.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - // transit_shapedetect(8, tmp1.get(), originalmasktm.get(), bufchro, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - bufgb.reset(); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - } - } - -//end TM - - -//shadow highlight - bool tonequ = false; - - if (lp.mullocsh[0] != 0 || lp.mullocsh[1] != 0 || lp.mullocsh[2] != 0 || lp.mullocsh[3] != 0 || lp.mullocsh[4] != 0) { - tonequ = true; - } - - bool tonecurv = false; - - if (params->locallab.spots.at(sp).gamSH != 2.4 || params->locallab.spots.at(sp).sloSH != 12.92) { - tonecurv = true; - } - - if (! lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.strSH != 0.f || lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4 || lp.prevdE) && call < 3 && lp.hsena) { - const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - const int bfh = yend - ystart; - const int bfw = xend - xstart; - - - if (bfw >= mSP && bfh >= mSP) { - - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); - std::unique_ptr bufmaskorigSH; - std::unique_ptr bufmaskblurSH; - std::unique_ptr originalmaskSH; - - if (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4) { - bufmaskorigSH.reset(new LabImage(bfw, bfh)); - bufmaskblurSH.reset(new LabImage(bfw, bfh)); - originalmaskSH.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; - } - } - - int inv = 0; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmaskSHmet == 3) { - showmaske = true; - } - - if (lp.enaSHMask) { - enaMask = true; - } - - if (lp.showmaskSHmet == 4) { - deltaE = true; - } - - if (lp.showmaskSHmet == 2) { - modmask = true; - } - - if (lp.showmaskSHmet == 1) { - modif = true; - } - - if (lp.showmaskSHmet == 0) { - zero = true; - } - - float chrom = lp.chromaSH; - float rad = lp.radmaSH; - float gamma = lp.gammaSH; - float slope = lp.slomaSH; - float blendm = lp.blendmaSH; - float lap = params->locallab.spots.at(sp).lapmaskSH; - bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - int shado = 0; - float amountcd = params->locallab.spots.at(sp).fatamountSH; - float anchorcd = params->locallab.spots.at(sp).fatanchorSH; - int lumask = params->locallab.spots.at(sp).lumask; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigSH.get(), originalmaskSH.get(), original, reserved, inv, lp, - 0.f, false, - locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - if (lp.showmaskSHmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigSH.get(), 0); - - return; - } - - if (lp.showmaskSHmet == 0 || lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2 || lp.showmaskSHmet == 4 || lp.enaSHMask) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; - bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; - bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; - bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; - bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; - bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - - if (lp.shmeth == 0) { - ImProcFunctions::shadowsHighlights(bufexpfin.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); - } - -//gradient - struct grad_params gp; - - if (lp.strSH != 0.f) { - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 2); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - double factor = 1.0; - factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); - bufexpfin->L[ir][jr] *= factor; - } - } - - if (lp.shmeth == 1) { - double scal = (double)(sk); - Imagefloat *tmpImage = nullptr; - tmpImage = new Imagefloat(bfw, bfh); - lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); - - if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB - float gamtone = params->locallab.spots.at(sp).gamSH; - float slotone = params->locallab.spots.at(sp).sloSH; - cmsHTRANSFORM dummy = nullptr; - workingtrc(tmpImage, tmpImage, bfw, bfh, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); - workingtrc(tmpImage, tmpImage, bfw, bfh, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); - } - - if (tonequ) { - tmpImage->normalizeFloatTo1(); - array2D Rtemp(bfw, bfh, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); - array2D Gtemp(bfw, bfh, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); - array2D Btemp(bfw, bfh, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); - tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, scal, multiThread); - tmpImage->normalizeFloatTo65535(); - } - - rgb2lab(*tmpImage, *bufexpfin, params->icm.workingProfile); - - delete tmpImage; - } - } - - transit_shapedetect2(call, 9, bufexporig.get(), bufexpfin.get(), originalmaskSH.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } else if (lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.showmaskSHmetinv == 1 || lp.enaSHMaskinv) && call < 3 && lp.hsena) { - std::unique_ptr bufmaskblurcol; - std::unique_ptr originalmaskSH; - const std::unique_ptr bufcolorig(new LabImage(GW, GH)); - - if (lp.enaSHMaskinv || lp.showmaskSHmetinv == 1) { - bufmaskblurcol.reset(new LabImage(GW, GH, true)); - originalmaskSH.reset(new LabImage(GW, GH)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - bufcolorig->L[y][x] = original->L[y][x]; - } - } - - int inv = 1; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmaskSHmetinv == 1) { - showmaske = true; - } - - if (lp.enaSHMaskinv) { - enaMask = true; - } - - if (lp.showmaskSHmetinv == 0) { - zero = true; - } - - float chrom = lp.chromaSH; - float rad = lp.radmaSH; - float gamma = lp.gammaSH; - float slope = lp.slomaSH; - float blendm = lp.blendmaSH; - float lap = params->locallab.spots.at(sp).lapmaskSH; - bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool lmasutilicolwav = false; - // bool delt = params->locallab.spots.at(sp).deltae; - bool delt = false; - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = params->locallab.spots.at(sp).shortc; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - int shado = 0; - float amountcd = params->locallab.spots.at(sp).fatamountSH; - float anchorcd = params->locallab.spots.at(sp).fatanchorSH; - int lumask = params->locallab.spots.at(sp).lumask; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - - maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskSH.get(), original, reserved, inv, lp, - 0.f, false, - locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - - if (lp.showmaskSHmetinv == 1) { - showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); - - return; - } - - float adjustr = 2.f; - InverseColorLight_Local(tonequ, tonecurv, sp, 2, lp, originalmaskSH.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - -// soft light and retinex_pde - if (lp.strng > 0.f && call <= 3 && lp.sfena) { - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - //variable for fast FFTW - int bfhr = bfh; - int bfwr = bfw; - - if (bfw >= mSP && bfh >= mSP) { - - if (lp.softmet == 1) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; - bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; - bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - bufexpfin->CopyFrom(bufexporig.get(), multiThread); - SoftLightParams softLightParams; - softLightParams.enabled = true; - softLightParams.strength = lp.strng; - - if (lp.softmet == 0) { - ImProcFunctions::softLight(bufexpfin.get(), softLightParams); - } else if (lp.softmet == 1) { - - const std::unique_ptr datain(new float[bfwr * bfhr]); - const std::unique_ptr dataout(new float[bfwr * bfhr]); - const std::unique_ptr dE(new float[bfwr * bfhr]); - - deltaEforLaplace(dE.get(), lp.lap, bfwr, bfhr, bufexpfin.get(), hueref, chromaref, lumaref); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - datain[y * bfwr + x] = bufexpfin->L[y][x]; - } - } - - const int showorig = lp.showmasksoftmet >= 5 ? 0 : lp.showmasksoftmet; - MyMutex::MyLock lock(*fftwMutex); - ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, 8.f * lp.strng, 1.f, dE.get(), showorig, 1, 1); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - bufexpfin->L[y][x] = dataout[y * bfwr + x]; - } - } - } - - transit_shapedetect2(call, 3, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - - //local contrast - bool wavcurve = false; - bool wavcurvelev = false; - bool wavcurvecon = false; - bool wavcurvecomp = false; - bool wavcurvecompre = false; - - if (lp.locmet == 1) { - if (locwavCurve && locwavutili) { - for (int i = 0; i < 500; i++) { - if (locwavCurve[i] != 0.5) { - wavcurve = true; - break; - } - } - } - if (loclevwavCurve && loclevwavutili) { - for (int i = 0; i < 500; i++) { - if (loclevwavCurve[i] != 0.) { - wavcurvelev = true; - break; - } - } - } - if (locconwavCurve && locconwavutili) { - for (int i = 0; i < 500; i++) { - if (locconwavCurve[i] != 0.5) { - wavcurvecon = true; - break; - } - } - } - if (loccompwavCurve && loccompwavutili) { - for (int i = 0; i < 500; i++) { - if (loccompwavCurve[i] != 0.) { - wavcurvecomp = true; - break; - } - } - } - if (loccomprewavCurve && loccomprewavutili) { - for (int i = 0; i < 500; i++) { - if (loccomprewavCurve[i] != 0.75) { - wavcurvecompre = true; - break; - } - } - } - } - - if ((lp.lcamount > 0.f || wavcurve || lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4 || lp.prevdE || lp.strwav != 0.f || wavcurvelev || wavcurvecon || wavcurvecomp || wavcurvecompre || lp.edgwena || params->locallab.spots.at(sp).residblur > 0.f || params->locallab.spots.at(sp).levelblur > 0.f || params->locallab.spots.at(sp).residcont != 0.f || params->locallab.spots.at(sp).clarilres != 0.f || params->locallab.spots.at(sp).claricres != 0.f) && call < 3 && lp.lcena) { - - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - int bfhr = bfh; - int bfwr = bfw; - - if (bfw >= mSPwav && bfh >= mSPwav) {//avoid too small spot for wavelet - if (lp.ftwlc) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - std::unique_ptr bufmaskblurlc; - std::unique_ptr originalmasklc; - std::unique_ptr bufmaskoriglc; - - if (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4) { - bufmaskblurlc.reset(new LabImage(bfw, bfh)); - originalmasklc.reset(new LabImage(bfw, bfh)); - bufmaskoriglc.reset(new LabImage(bfw, bfh)); - } - - array2D buflight(bfw, bfh); - JaggedArray bufchro(bfw, bfh); - const std::unique_ptr bufgb(new LabImage(bfw, bfh)); - std::unique_ptr tmp1(new LabImage(bfw, bfh)); - const std::unique_ptr tmpresid(new LabImage(bfw, bfh)); - const std::unique_ptr tmpres(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufgb->L[y - ystart][x - xstart] = original->L[y][x]; - bufgb->a[y - ystart][x - xstart] = original->a[y][x]; - bufgb->b[y - ystart][x - xstart] = original->b[y][x]; - tmp1->L[y - ystart][x - xstart] = original->L[y][x]; - tmp1->a[y - ystart][x - xstart] = original->a[y][x]; - tmp1->b[y - ystart][x - xstart] = original->b[y][x]; - tmpresid->L[y - ystart][x - xstart] = original->L[y][x]; - tmpresid->a[y - ystart][x - xstart] = original->a[y][x]; - tmpresid->b[y - ystart][x - xstart] = original->b[y][x]; - tmpres->L[y - ystart][x - xstart] = original->L[y][x]; - tmpres->a[y - ystart][x - xstart] = original->a[y][x]; - tmpres->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufgb->L[y][x] = original->L[y + ystart][x + xstart]; - } - } - - int inv = 0; - bool showmaske = false; - bool enaMask = false; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmasklcmet == 3) { - showmaske = true; - } - - if (lp.enalcMask) { - enaMask = true; - } - - if (lp.showmasklcmet == 4) { - deltaE = true; - } - - if (lp.showmasklcmet == 2) { - modmask = true; - } - - if (lp.showmasklcmet == 1) { - modif = true; - } - - if (lp.showmasklcmet == 0) { - zero = true; - } - - - float chrom = lp.chromalc; - float rad = lp.radmalc; - float blendm = lp.blendmalc; - float gamma = 1.f; - float slope = 0.f; - float lap = 0.f; //params->locallab.spots.at(sp).lapmaskexp; - bool pde = false; //params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shado = 0; - int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - float amountcd = 0.f; - float anchorcd = 50.f; - int lumask = params->locallab.spots.at(sp).lumask; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgb.get(), bufmaskoriglc.get(), originalmasklc.get(), original, reserved, inv, lp, - 0.f, false, - locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, -1 - ); - - if (lp.showmasklcmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgb.get(), transformed, bufmaskoriglc.get(), 0); - - return; - } - - if (lp.showmasklcmet == 0 || lp.showmasklcmet == 1 || lp.showmasklcmet == 2 || lp.showmasklcmet == 4 || lp.enalcMask) { - - if (lp.locmet == 0) { - LocalContrastParams localContrastParams; - LocallabParams locallabparams; - localContrastParams.enabled = true; - localContrastParams.radius = params->locallab.spots.at(sp).lcradius; - localContrastParams.amount = params->locallab.spots.at(sp).lcamount; - localContrastParams.darkness = params->locallab.spots.at(sp).lcdarkness; - localContrastParams.lightness = params->locallab.spots.at(sp).lightness; - bool fftwlc = false; - - if (!lp.ftwlc) { // || (lp.ftwlc && call != 2)) { - ImProcFunctions::localContrast(tmp1.get(), tmp1->L, localContrastParams, fftwlc, sk); - } else { - const std::unique_ptr tmpfftw(new LabImage(bfwr, bfhr)); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - tmpfftw->L[y][x] = tmp1->L[y][x]; - tmpfftw->a[y][x] = tmp1->a[y][x]; - tmpfftw->b[y][x] = tmp1->b[y][x]; - } - } - - fftwlc = true; - ImProcFunctions::localContrast(tmpfftw.get(), tmpfftw->L, localContrastParams, fftwlc, sk); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - tmp1->L[y][x] = tmpfftw->L[y][x]; - tmp1->a[y][x] = tmpfftw->a[y][x]; - tmp1->b[y][x] = tmpfftw->b[y][x]; - } - } - - } - } else if (lp.locmet == 1) { //wavelet && sk ==1 - int wavelet_level = 1 + params->locallab.spots.at(sp).csthreshold.getBottomRight();//retrieve with +1 maximum wavelet_level - float mL = params->locallab.spots.at(sp).clarilres / 100.f; - float mC = params->locallab.spots.at(sp).claricres / 100.f; - float softr = params->locallab.spots.at(sp).clarisoft; - float mL0 = 0.f; - float mC0 = 0.f; -#ifdef _OPENMP - const int numThreads = omp_get_max_threads(); -#else - const int numThreads = 1; - -#endif - // adap maximum level wavelet to size of RT-spot - int minwin = rtengine::min(bfw, bfh); - int maxlevelspot = 10;//maximum possible - - // adap maximum level wavelet to size of crop - while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { - --maxlevelspot ; - } - - // printf("minwin=%i maxlevelavant=%i maxlespot=%i\n", minwin, wavelet_level, maxlevelspot); - - wavelet_level = rtengine::min(wavelet_level, maxlevelspot); - // printf("maxlevel=%i\n", wavelet_level); - bool exec = false; - bool origlc = params->locallab.spots.at(sp).origlc; - - if (origlc) {//merge only with original - clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); - } - - int maxlvl = wavelet_level; - const float contrast = params->locallab.spots.at(sp).residcont; - int level_bl = params->locallab.spots.at(sp).csthreshold.getBottomLeft(); - int level_hl = params->locallab.spots.at(sp).csthreshold.getTopLeft(); - int level_br = params->locallab.spots.at(sp).csthreshold.getBottomRight(); - int level_hr = params->locallab.spots.at(sp).csthreshold.getTopRight(); - const float radblur = (params->locallab.spots.at(sp).residblur) / sk; - const bool blurlc = params->locallab.spots.at(sp).blurlc; - const float radlevblur = (params->locallab.spots.at(sp).levelblur) / sk; - const float sigma = params->locallab.spots.at(sp).sigma; - const float offs = params->locallab.spots.at(sp).offset; - const float sigmadc = params->locallab.spots.at(sp).sigmadc; - const float deltad = params->locallab.spots.at(sp).deltad; - // const float fatres = params->locallab.spots.at(sp).fatres; - const float chrol = params->locallab.spots.at(sp).chromalev; - const float chrobl = params->locallab.spots.at(sp).chromablu; - const bool blurena = params->locallab.spots.at(sp).wavblur; - const bool levelena = params->locallab.spots.at(sp).wavcont; - const bool comprena = params->locallab.spots.at(sp).wavcomp; - const bool compreena = params->locallab.spots.at(sp).wavcompre; - const float compress = params->locallab.spots.at(sp).residcomp; - const float thres = params->locallab.spots.at(sp).threswav; - - wavcontrast4(lp, tmp1->L, tmp1->a, tmp1->b, contrast, radblur, radlevblur, tmp1->W, tmp1->H, level_bl, level_hl, level_br, level_hr, sk, numThreads, locwavCurve, locwavutili, wavcurve, loclevwavCurve, loclevwavutili, wavcurvelev, locconwavCurve, locconwavutili, wavcurvecon, loccompwavCurve, loccompwavutili, wavcurvecomp, loccomprewavCurve, loccomprewavutili, wavcurvecompre, locedgwavCurve, locedgwavutili, sigma, offs, maxlvl, sigmadc, deltad, chrol, chrobl, blurlc, blurena, levelena, comprena, compreena, compress, thres); - - const float satur = params->locallab.spots.at(sp).residchro; - - - if (satur != 0.f || radblur > 0.f) {//blur residual a and satur - - wavelet_decomposition *wdspota = new wavelet_decomposition(tmp1->a[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); - - if (wdspota->memory_allocation_failed()) { - return; - } - - float *wav_ab0a = wdspota->get_coeff0(); - // int maxlvla = wdspota->maxlevel(); - int W_La = wdspota->level_W(0); - int H_La = wdspota->level_H(0); - - if (radblur > 0.f && !blurlc && blurena) { - array2D bufa(W_La, H_La); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_La; y++) { - for (int x = 0; x < W_La; x++) { - bufa[y][x] = wav_ab0a [y * W_La + x]; - } - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufa, bufa, W_La, H_La, radblur); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_La; y++) { - for (int x = 0; x < W_La; x++) { - wav_ab0a[y * W_La + x] = bufa[y][x]; - } - } - - } - - if (satur != 0.f) { -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_La * H_La; i++) { - wav_ab0a[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f)));//more progressive than linear - wav_ab0a[i] = clipC(wav_ab0a[i]); - } - } - - wdspota->reconstruct(tmp1->a[0], 1.f); - delete wdspota; - - wavelet_decomposition *wdspotb = new wavelet_decomposition(tmp1->b[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); - - if (wdspotb->memory_allocation_failed()) { - return; - } - - float *wav_ab0b = wdspotb->get_coeff0(); - int W_Lb = wdspotb->level_W(0); - int H_Lb = wdspotb->level_H(0); - - if (radblur > 0.f && !blurlc && blurena) { - array2D bufb(W_Lb, H_Lb); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_Lb; y++) { - for (int x = 0; x < W_Lb; x++) { - bufb[y][x] = wav_ab0b [y * W_Lb + x]; - } - } - -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(bufb, bufb, W_Lb, H_Lb, radblur); - } - - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_Lb; y++) { - for (int x = 0; x < W_Lb; x++) { - wav_ab0b[y * W_Lb + x] = bufb[y][x]; - } - } - - } - - if (satur != 0.f) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int i = 0; i < W_Lb * H_Lb; i++) { - wav_ab0b[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f))); - wav_ab0b[i] = clipC(wav_ab0b[i]); - } - } - - wdspotb->reconstruct(tmp1->b[0], 1.f); - delete wdspotb; - } - - if (!origlc) {//merge all files - exec = false; - //copy previous calculation in merge possibilities -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - tmpresid->L[y][x] = tmp1->L[y][x]; - tmpresid->a[y][x] = tmp1->a[y][x]; - tmpresid->b[y][x] = tmp1->b[y][x]; - } - } - clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); - } - - float thr = 0.001f; - int flag = 0; - - if (maxlvl <= 4) { - mL0 = 0.f; - mC0 = 0.f; - mL = -1.5f * mL;//increase only for sharpen - mC = -mC; - thr = 1.f; - flag = 0; - - } else { - mL0 = mL; - mC0 = mC; - thr = 1.f; - flag = 1; - } - - if (exec || compreena || comprena || levelena || blurena || lp.wavgradl || locwavCurve || lp.edgwena) { - LabImage *mergfile = tmp1.get(); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int x = 0; x < bfh; x++) - for (int y = 0; y < bfw; y++) { - tmp1->L[x][y] = clipLoc((1.f + mL0) * mergfile->L[x][y] - mL * tmpresid->L[x][y]); - tmp1->a[x][y] = clipC((1.f + mC0) * mergfile->a[x][y] - mC * tmpresid->a[x][y]); - tmp1->b[x][y] = clipC((1.f + mC0) * mergfile->b[x][y] - mC * tmpresid->b[x][y]); - } - - if (softr != 0.f && (compreena || locwavCurve || comprena || blurena || levelena || lp.wavgradl || lp.edgwena || std::fabs(mL) > 0.001f)) { - softproc(tmpres.get(), tmp1.get(), softr, bfh, bfw, 0.001, 0.00001, thr, sk, multiThread, flag); - } - } - } - - - transit_shapedetect2(call, 10, bufgb.get(), tmp1.get(), originalmasklc.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - tmp1.reset(); - } - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - - if (!lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { //interior ellipse for sharpening, call = 1 and 2 only with Dcrop and simpleprocess - int bfh = call == 2 ? int (lp.ly + lp.lyT) + del : original->H; //bfw bfh real size of square zone - int bfw = call == 2 ? int (lp.lx + lp.lxL) + del : original->W; - JaggedArray loctemp(bfw, bfh); - - if (call == 2) { //call from simpleprocess - // printf("bfw=%i bfh=%i\n", bfw, bfh); - - if (bfw < mSPsharp || bfh < mSPsharp) { - printf("too small RT-spot - minimum size 39 * 39\n"); - return; - } - - JaggedArray bufsh(bfw, bfh, true); - JaggedArray hbuffer(bfw, bfh); - int begy = lp.yc - lp.lyT; - int begx = lp.xc - lp.lxL; - int yEn = lp.yc + lp.ly; - int xEn = lp.xc + lp.lx; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H ; y++) { - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - int loy = cy + y; - - if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { - bufsh[loy - begy][lox - begx] = original->L[y][x]; - } - } - } - - //sharpen only square area instead of all image - ImProcFunctions::deconvsharpeningloc(bufsh, hbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, 1); - } else { //call from dcrop.cc - ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); - } - - //sharpen ellipse and transition - Sharp_Local(call, loctemp, 0, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - - } else if (lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { - int GW = original->W; - int GH = original->H; - JaggedArray loctemp(GW, GH); - - ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, GW, GH, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); - - InverseSharp_Local(loctemp, hueref, lumaref, chromaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - - if (lp.dehaze != 0 && lp.retiena) { - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - - if (bfh >= mSP && bfw >= mSP) { - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; - bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; - bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - - bufexpfin->CopyFrom(bufexporig.get(), multiThread); - //calc dehaze - const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); - - DehazeParams dehazeParams; - dehazeParams.enabled = true; - dehazeParams.strength = lp.dehaze; - dehazeParams.showDepthMap = false; - dehazeParams.depth = lp.depth; - dehazeParams.luminance = params->locallab.spots.at(sp).lumonly; - lab2rgb(*bufexpfin, *tmpImage.get(), params->icm.workingProfile); - dehazeloc(tmpImage.get(), dehazeParams); - rgb2lab(*tmpImage.get(), *bufexpfin, params->icm.workingProfile); - - transit_shapedetect2(call, 30, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - - lp.invret = false;//always disabled inverse RETI too complex todo !! - - if (lp.str >= 0.2f && lp.retiena && call != 2) { - LabImage *bufreti = nullptr; - LabImage *bufmask = nullptr; - LabImage *buforig = nullptr; - LabImage *buforigmas = nullptr; - - if (GW >= mSP && GH >= mSP) - - { - - array2D buflight(GW, GH); - JaggedArray bufchro(GW, GH); - - int Hd, Wd; - Hd = GH; - Wd = GW; - - bufreti = new LabImage(GW, GH); - bufmask = new LabImage(GW, GH); - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - buforig = new LabImage(GW, GH); - buforigmas = new LabImage(GW, GH); - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < GH; ir++) //fill with 0 - for (int jr = 0; jr < GW; jr++) { - bufreti->L[ir][jr] = 0.f; - bufreti->a[ir][jr] = 0.f; - bufreti->b[ir][jr] = 0.f; - buflight[ir][jr] = 0.f; - bufchro[ir][jr] = 0.f; - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H ; y++) //{ - for (int x = 0; x < transformed->W; x++) { - bufreti->L[y][x] = original->L[y][x]; - bufreti->a[y][x] = original->a[y][x]; - bufreti->b[y][x] = original->b[y][x]; - bufmask->L[y][x] = original->L[y][x]; - bufmask->a[y][x] = original->a[y][x]; - bufmask->b[y][x] = original->b[y][x]; - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - buforig->L[y][x] = original->L[y][x]; - buforig->a[y][x] = original->a[y][x]; - buforig->b[y][x] = original->b[y][x]; - } - - } - - float raddE = params->locallab.spots.at(sp).softradiusret; - - //calc dE and reduction to use in MSR to reduce artifacts - const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - const float refa = chromaref * cos(hueref); - const float refb = chromaref * sin(hueref); - - const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); - float** reducDE = *(reducDEBuffer.get()); - - float ade = 0.01f * raddE; - float bde = 100.f - raddE; - float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < transformed->H ; y++) - for (int x = 0; x < transformed->W; x++) { - float dE = std::sqrt(SQR(refa - bufreti->a[y][x] / 327.68f) + SQR(refb - bufreti->b[y][x] / 327.68f) + SQR(lumaref - bufreti->b[y][x] / 327.68f)); - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); - reducDE[y][x] = clipDE(reducdE); - } - - const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); - float** orig = *(origBuffer.get()); - - const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); - float** orig1 = *(origBuffer1.get()); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - orig[ir][jr] = bufreti->L[ir][jr]; - orig1[ir][jr] = bufreti->L[ir][jr]; - } - - LabImage *tmpl = new LabImage(Wd, Hd); - - // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; - bool fftw = lp.ftwreti; - //fftw = false; - //for Retinex Mask are incorporated in MSR - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - float lumask = params->locallab.spots.at(sp).lumask; - - const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, - Wd, Hd, Wd, Hd, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, - locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, - lmaskretilocalcurve, localmaskretiutili, - transformed, lp.enaretiMasktmap, lp.enaretiMask, - delt, hueref, chromaref, lumaref, - maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - tmpl->L[ir][jr] = orig[ir][jr]; - } - } - - if (lp.equret) { //equilibrate luminance before / after MSR - float *datain = new float[Hd * Wd]; - float *data = new float[Hd * Wd]; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - datain[ir * Wd + jr] = orig1[ir][jr]; - data[ir * Wd + jr] = orig[ir][jr]; - } - - normalize_mean_dt(data, datain, Hd * Wd, 1.f, 1.f); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - tmpl->L[ir][jr] = data[ir * Wd + jr]; - } - - delete [] datain; - delete [] data; - } - - - float minL = tmpl->L[0][0] - bufreti->L[0][0]; - float maxL = minL; -#ifdef _OPENMP - #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; - minL = rtengine::min(minL, buflight[ir][jr]); - maxL = rtengine::max(maxL, buflight[ir][jr]); - } - } - - const float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); - - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - buflight[ir][jr] /= coef; - } - } - - transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - - if (params->locallab.spots.at(sp).chrrt > 0) { - - if (call == 1) { - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - - orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); - orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); - } - - } - - float maxChro = orig1[0][0]; -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - maxChro = rtengine::max(maxChro, orig1[ir][jr]); - } - } - - float divchro = maxChro; - - //first step change saturation without Retinex ==> gain of time and memory - float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; - - if (params->locallab.spots.at(sp).chrrt <= 0.2f) { - satreal /= 10.f; - } - - DiagonalCurve reti_satur({ - DCT_NURBS, - 0, 0, - 0.2, 0.2 + satreal / 250.0, - 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), - 1, 1 - }); - - if (call == 1) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - const float Chprov = orig1[ir][jr]; - float2 sincosval; - sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; - sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; - - if (params->locallab.spots.at(sp).chrrt <= 100.f) { //first step - float buf = LIM01(orig[ir][jr] / divchro); - buf = reti_satur.getVal(buf); - buf *= divchro; - orig[ir][jr] = buf; - } - - tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; - tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; - } - - float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; - float maxC = minC; -#ifdef _OPENMP - #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; - minC = rtengine::min(minC, bufchro[ir][jr]); - maxC = rtengine::max(maxC, bufchro[ir][jr]); - } - } - - float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); - - if (coefC > 0.f) { - coefC = 1.f / coefC; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - bufchro[ir][jr] *= coefC; - } - } - } - } - - transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - - delete tmpl; - delete bufmask; - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - if (buforig) { - delete buforig; - } - - if (buforigmas) { - delete buforigmas; - } - } - delete bufreti; - } - } - - - - if (lp.str >= 0.2f && lp.retiena && call == 2) { - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - - LabImage *bufreti = nullptr; - LabImage *bufmask = nullptr; - LabImage *buforig = nullptr; - LabImage *buforigmas = nullptr; - int bfhr = bfh; - int bfwr = bfw; - - if (bfw >= mSP && bfh > mSP) { - if (lp.ftwreti) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - array2D buflight(bfw, bfh); - JaggedArray bufchro(bfw, bfh); - - int Hd, Wd; - Hd = GH; - Wd = GW; - - if (!lp.invret && call == 2) { - - Hd = bfh; - Wd = bfw; - bufreti = new LabImage(bfw, bfh); - bufmask = new LabImage(bfw, bfh); - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - buforig = new LabImage(bfw, bfh); - buforigmas = new LabImage(bfw, bfh); - } - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) //fill with 0 - for (int jr = 0; jr < bfw; jr++) { - bufreti->L[ir][jr] = 0.f; - bufreti->a[ir][jr] = 0.f; - bufreti->b[ir][jr] = 0.f; - buflight[ir][jr] = 0.f; - bufchro[ir][jr] = 0.f; - } - - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufreti->L[y - ystart][x - xstart] = original->L[y][x]; - bufreti->a[y - ystart][x - xstart] = original->a[y][x]; - bufreti->b[y - ystart][x - xstart] = original->b[y][x]; - bufmask->L[y - ystart][x - xstart] = original->L[y][x]; - bufmask->a[y - ystart][x - xstart] = original->a[y][x]; - bufmask->b[y - ystart][x - xstart] = original->b[y][x]; - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - buforig->L[y - ystart][x - xstart] = original->L[y][x]; - buforig->a[y - ystart][x - xstart] = original->a[y][x]; - buforig->b[y - ystart][x - xstart] = original->b[y][x]; - } - } - } - } - - float raddE = params->locallab.spots.at(sp).softradiusret; - - //calc dE and reduction to use in MSR to reduce artifacts - const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; - const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - const float refa = chromaref * cos(hueref); - const float refb = chromaref * sin(hueref); - - const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); - float** reducDE = *(reducDEBuffer.get()); - float ade = 0.01f * raddE; - float bde = 100.f - raddE; - float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend ; y++) { - for (int x = xstart; x < xend; x++) { - const float dE = std::sqrt(SQR(refa - bufreti->a[y - ystart][x - xstart] / 327.68f) + SQR(refb - bufreti->b[y - ystart][x - xstart] / 327.68f) + SQR(lumaref - bufreti->b[y - ystart][x - xstart] / 327.68f)); - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); - reducDE[y - ystart][x - xstart] = clipDE(reducdE); - } - } - - const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); - float** orig = *(origBuffer.get()); - - const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); - float** orig1 = *(origBuffer1.get()); - - LabImage *tmpl = nullptr; - - if (!lp.invret && call == 2) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - orig[ir][jr] = bufreti->L[ir][jr]; - orig1[ir][jr] = bufreti->L[ir][jr]; - } - } - - tmpl = new LabImage(Wd, Hd); - } - - // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; - bool fftw = lp.ftwreti; - //for Retinex Mask are incorporated in MSR - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - float lumask = params->locallab.spots.at(sp).lumask; - - const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - - ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, - Wd, Hd, bfwr, bfhr, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, - locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, - lmaskretilocalcurve, localmaskretiutili, - transformed, lp.enaretiMasktmap, lp.enaretiMask, - delt, hueref, chromaref, lumaref, - maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) - for (int jr = 0; jr < Wd; jr += 1) { - tmpl->L[ir][jr] = orig[ir][jr]; - } - - - if (lp.equret) { //equilibrate luminance before / after MSR - const std::unique_ptr datain(new float[Hd * Wd]); - const std::unique_ptr data(new float[Hd * Wd]); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - datain[ir * Wd + jr] = orig1[ir][jr]; - data[ir * Wd + jr] = orig[ir][jr]; - } - } - - normalize_mean_dt(data.get(), datain.get(), Hd * Wd, 1.f, 1.f); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - tmpl->L[ir][jr] = data[ir * Wd + jr]; - } - } - } - - if (!lp.invret) { - float minL = tmpl->L[0][0] - bufreti->L[0][0]; - float maxL = minL; -#ifdef _OPENMP - #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; - minL = rtengine::min(minL, buflight[ir][jr]); - maxL = rtengine::max(maxL, buflight[ir][jr]); - } - } - - float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); - - if (coef > 0.f) { - coef = 1.f / coef; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - buflight[ir][jr] *= coef; - } - } - } - - transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - - if (params->locallab.spots.at(sp).chrrt > 0) { - if (!lp.invret && call == 2) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); - orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); - } - } - } - - float maxChro = orig1[0][0]; -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - maxChro = rtengine::max(maxChro, orig1[ir][jr]); - } - } - - //first step change saturation without Retinex ==> gain of time and memory - float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; - - if (params->locallab.spots.at(sp).chrrt <= 0.2f) { - satreal /= 10.f; - } - - DiagonalCurve reti_satur({ - DCT_NURBS, - 0, 0, - 0.2, 0.2 + satreal / 250.0, - 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), - 1, 1 - }); - - if (!lp.invret && call == 2) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir += 1) { - for (int jr = 0; jr < Wd; jr += 1) { - const float Chprov = orig1[ir][jr]; - float2 sincosval; - sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; - sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; - - if (params->locallab.spots.at(sp).chrrt <= 40.f) { //first step - orig[ir][jr] = reti_satur.getVal(LIM01(orig[ir][jr] / maxChro)) * maxChro; - } - - tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; - tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; - } - } - - float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; - float maxC = minC; -#ifdef _OPENMP - #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; - minC = rtengine::min(minC, bufchro[ir][jr]); - maxC = rtengine::max(maxC, bufchro[ir][jr]); - } - } - - float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); - - if (coefC > 0.f) { - coefC = 1.f / coefC; -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < Hd; ir++) { - for (int jr = 0; jr < Wd; jr++) { - bufchro[ir][jr] *= coefC; - } - } - } - } - - if (!lp.invret) { - transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - - delete tmpl; - delete bufmask; - - if (!lp.enaretiMasktmap && lp.enaretiMask) { - if (buforig) { - delete buforig; - } - - if (buforigmas) { - delete buforigmas; - } - } - delete bufreti; - } - } - - bool enablefat = false; - - if (params->locallab.spots.at(sp).fatamount > 1.f) { - enablefat = true;; - } - - bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.shadex > 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); - - if (!lp.invex && execex) { - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - //variable for fast FFTW - int bfhr = bfh; - int bfwr = bfw; - - - if (bfw >= mSP && bfh >= mSP) { - - if (lp.expmet == 1) { - optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); - const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); - - std::unique_ptr bufmaskblurexp; - std::unique_ptr originalmaskexp; - - array2D blend2; - - if (call <= 3) { //simpleprocess, dcrop, improccoordinator - if (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 5) { - bufmaskblurexp.reset(new LabImage(bfw, bfh)); - originalmaskexp.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend; y++) { - for (int x = xstart; x < xend; x++) { - bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; - } - } - - const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); - - if (bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struexp > 0.f) { - blend2(bfw, bfh); - ImProcFunctions::blendstruc(bfw, bfh, bufexporig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struexp, blend2, sk, multiThread); - - if (lp.showmaskexpmet == 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend ; y++) { - for (int x = xstart; x < xend; x++) { - const int lox = cx + x; - const int loy = cy + y; - int zone; - float localFactor = 1.f; - const float achm = lp.trans / 100.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - - if (zone > 0) { - transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); - transformed->a[y][x] = 0.f; - transformed->b[y][x] = 0.f; - } - } - } - - return; - } - } - - int inv = 0; - bool showmaske = false; - const bool enaMask = lp.enaExpMask; - bool deltaE = false; - bool modmask = false; - bool zero = false; - bool modif = false; - - if (lp.showmaskexpmet == 3) { - showmaske = true; - } else if (lp.showmaskexpmet == 5) { - deltaE = true; - } else if (lp.showmaskexpmet == 2) { - modmask = true; - } else if (lp.showmaskexpmet == 1) { - modif = true; - } else if (lp.showmaskexpmet == 0) { - zero = true; - } - - float chrom = lp.chromaexp; - float rad = lp.radmaexp; - float gamma = lp.gammaexp; - float slope = lp.slomaexp; - float blendm = lp.blendmaexp; - float lap = params->locallab.spots.at(sp).lapmaskexp; - bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - bool lmasutilicolwav = false; - bool delt = params->locallab.spots.at(sp).deltae; - int sco = params->locallab.spots.at(sp).scopemask; - int shado = 0; - int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - float amountcd = 0.f; - float anchorcd = 50.f; - int lumask = params->locallab.spots.at(sp).lumask; - LocHHmaskCurve lochhhmasCurve; - bool lhhmasutili = false; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, - 0.f, false, - locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 - ); - - if (lp.showmaskexpmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskblurexp.get(), 0); - - return; - } - - if (lp.showmaskexpmet == 4) { - return; - } - - if (lp.showmaskexpmet == 0 || lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2 || lp.showmaskexpmet == 5 || lp.enaExpMask) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; - bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; - bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - - - - if (exlocalcurve && localexutili) {// L=f(L) curve enhanced -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - bufexpfin->L[ir][jr] = 0.5f * exlocalcurve[2.f * bufexporig->L[ir][jr]]; - } - - if (lp.expcomp == 0.f) { - lp.expcomp = 0.001f; // to enabled - } - - ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); - - - } else { - - ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexporig.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); - } - -//gradient - struct grad_params gp; - - if (lp.strexp != 0.f) { - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 1); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); - } - } - } - -//exposure_pde - if (lp.expmet == 1) { - if (enablefat) { - const std::unique_ptr datain(new float[bfwr * bfhr]); - const std::unique_ptr dataout(new float[bfwr * bfhr]); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - datain[y * bfwr + x] = bufexpfin->L[y][x]; - } - } - - FattalToneMappingParams fatParams; - fatParams.enabled = true; - fatParams.threshold = params->locallab.spots.at(sp).fatdetail; - fatParams.amount = params->locallab.spots.at(sp).fatamount; - fatParams.anchor = 50.f; //params->locallab.spots.at(sp).fatanchor; - const float sigm = params->locallab.spots.at(sp).fatlevel; - const float mean = params->locallab.spots.at(sp).fatanchor; - const std::unique_ptr tmpImagefat(new Imagefloat(bfwr, bfhr)); - lab2rgb(*bufexpfin, *(tmpImagefat.get()), params->icm.workingProfile); - ToneMapFattal02(tmpImagefat.get(), fatParams, 3, 0, nullptr, 0, 0, 1);//last parameter = 1 ==>ART algorithm - rgb2lab(*(tmpImagefat.get()), *bufexpfin, params->icm.workingProfile); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - dataout[y * bfwr + x] = bufexpfin->L[y][x]; - } - } - - normalize_mean_dt(dataout.get(), datain.get(), bfwr * bfhr, mean, sigm); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - bufexpfin->L[y][x] = dataout[y * bfwr + x]; - } - } - } - - if (lp.laplacexp > 0.1f) { - MyMutex::MyLock lock(*fftwMutex); - std::unique_ptr datain(new float[bfwr * bfhr]); - std::unique_ptr dataout(new float[bfwr * bfhr]); - const float gam = params->locallab.spots.at(sp).gamm; - const float igam = 1.f / gam; - - if (params->locallab.spots.at(sp).exnoiseMethod == "med" || params->locallab.spots.at(sp).exnoiseMethod == "medhi") { - if (lp.blac < -100.f && lp.linear > 0.01f) { - float evnoise = lp.blac - lp.linear * 2000.f; - if (params->locallab.spots.at(sp).exnoiseMethod == "med") { - evnoise *= 0.4f; - } - - //soft denoise, user must use Local Denoise to best result - Median med; - if (evnoise < -18000.f) { - med = Median::TYPE_5X5_STRONG; - } else if (evnoise < -15000.f) { - med = Median::TYPE_5X5_SOFT; - } else if (evnoise < -10000.f) { - med = Median::TYPE_3X3_STRONG; - } else { - med = Median:: TYPE_3X3_SOFT; - } - - Median_Denoise(bufexpfin->L, bufexpfin->L, bfwr, bfhr, med, 1, multiThread); - Median_Denoise(bufexpfin->a, bufexpfin->a, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); - Median_Denoise(bufexpfin->b, bufexpfin->b, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); - } - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - float L = LIM01(bufexpfin->L[y][x] / 32768.f);//change gamma for Laplacian - datain[y * bfwr + x] = pow_F(L, gam) * 32768.f; - } - } - - //call PDE equation - with Laplacian threshold - ImProcFunctions::exposure_pde(datain.get(), datain.get(), dataout.get(), bfwr, bfhr, 12.f * lp.laplacexp, lp.balanexp); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfhr; y++) { - for (int x = 0; x < bfwr; x++) { - const float Y = dataout[y * bfwr + x] / 32768.f;//inverse Laplacian gamma - bufexpfin->L[y][x] = pow_F(Y, igam) * 32768.f; - } - } - } - } - if (lp.shadex > 0) { - if (lp.expcomp == 0.f) { - lp.expcomp = 0.001f; // to enabled - } - } - - //shadows with ipshadowshighlight - if ((lp.expcomp != 0.f) || (exlocalcurve && localexutili)) { - if (lp.shadex > 0) { - ImProcFunctions::shadowsHighlights(bufexpfin.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); - } - } - - if (lp.expchroma != 0.f) { - if ((lp.expcomp != 0.f && lp.expcomp != 0.001f) || (exlocalcurve && localexutili) || lp.laplacexp > 0.1f) { - constexpr float ampli = 70.f; - const float ch = (1.f + 0.02f * lp.expchroma); - const float chprosl = ch <= 1.f ? 99.f * ch - 99.f : clipChro(ampli * ch - ampli); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float epsi = bufexporig->L[ir][jr] == 0.f ? 0.001f : 0.f; - const float rapexp = bufexpfin->L[ir][jr] / (bufexporig->L[ir][jr] + epsi); - bufexpfin->a[ir][jr] *= 1.f + chprosl * rapexp; - bufexpfin->b[ir][jr] *= 1.f + chprosl * rapexp; - } - } - } - } - - if (lp.softradiusexp > 0.f && lp.expmet == 0) { - softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); - } - float meansob = 0.f; - transit_shapedetect2(call, 1, bufexporig.get(), bufexpfin.get(), originalmaskexp.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); - } - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - } -//inverse - - else if (lp.invex && (lp.expcomp != 0.0 || lp.laplacexp > 0.1f || params->locallab.spots.at(sp).fatamount > 1.f || (exlocalcurve && localexutili) || lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) && lp.exposena) { - constexpr float adjustr = 2.f; - std::unique_ptr bufmaskblurexp; - std::unique_ptr originalmaskexp; - const std::unique_ptr bufexporig(new LabImage(GW, GH)); - - if (lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) { - bufmaskblurexp.reset(new LabImage(GW, GH, true)); - originalmaskexp.reset(new LabImage(GW, GH)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - bufexporig->L[y][x] = original->L[y][x]; - } - } - - constexpr int inv = 1; - const bool showmaske = lp.showmaskexpmetinv == 1; - const bool enaMask = lp.enaExpMaskinv; - constexpr bool deltaE = false; - constexpr bool modmask = false; - const bool zero = lp.showmaskexpmetinv == 0; - constexpr bool modif = false; - const float chrom = lp.chromaexp; - const float rad = lp.radmaexp; - const float gamma = lp.gammaexp; - const float slope = lp.slomaexp; - const float blendm = lp.blendmaexp; - const float lap = params->locallab.spots.at(sp).lapmaskexp; - const bool pde = params->locallab.spots.at(sp).laplac; - LocwavCurve dummy; - const bool lmasutilicolwav = false; - // bool delt = params->locallab.spots.at(sp).deltae; - const bool delt = false; - const int sco = params->locallab.spots.at(sp).scopemask; - constexpr int shado = 0; - constexpr int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; - const int lumask = params->locallab.spots.at(sp).lumask; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - constexpr float amountcd = 0.f; - constexpr float anchorcd = 50.f; - LocHHmaskCurve lochhhmasCurve; - constexpr bool lhhmasutili = false; - - maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, - 0.f, false, - locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, false, 0.f, 0.f, 0 - ); - - if (lp.showmaskexpmetinv == 1) { - showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufexporig.get(), transformed, bufmaskblurexp.get(), inv); - return; - } - - InverseColorLight_Local(false, false, sp, 1, lp, originalmaskexp.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - -//local color and light - const float factor = LocallabParams::LABGRIDL_CORR_MAX * 3.276f; - const float scaling = LocallabParams::LABGRIDL_CORR_SCALE; - const float scaledirect = LocallabParams::LABGRIDL_DIRECT_SCALE; - const float a_scale = (lp.highA - lp.lowA) / factor / scaling; - const float a_base = lp.lowA / scaling; - const float b_scale = (lp.highB - lp.lowB) / factor / scaling; - const float b_base = lp.lowB / scaling; - const bool ctoning = (a_scale != 0.f || b_scale != 0.f || a_base != 0.f || b_base != 0.f); - const float a_scalemerg = (lp.highAmerg - lp.lowAmerg) / factor / scaling; - const float a_basemerg = lp.lowAmerg / scaling; - const float b_scalemerg = (lp.highBmerg - lp.lowBmerg) / factor / scaling; - const float b_basemerg = lp.lowBmerg / scaling; - const bool ctoningmerg = (a_scalemerg != 0.f || b_scalemerg != 0.f || a_basemerg != 0.f || b_basemerg != 0.f); - - if (!lp.inv && (lp.chro != 0 || lp.ligh != 0.f || lp.cont != 0 || ctoning || lp.mergemet > 0 || lp.strcol != 0.f || lp.strcolab != 0.f || lp.qualcurvemet != 0 || lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 4 || lp.showmaskcolmet == 5 || lp.prevdE) && lp.colorena) { // || lllocalcurve)) { //interior ellipse renforced lightness and chroma //locallutili - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - const bool spez = params->locallab.spots.at(sp).special; - - if (bfw >= mSP && bfh >= mSP) { - - if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { - optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - - std::unique_ptr bufcolorig; - std::unique_ptr bufcolfin; - std::unique_ptr bufmaskblurcol; - std::unique_ptr originalmaskcol; - std::unique_ptr bufcolreserv; - std::unique_ptr buftemp; - array2D blend2; - - float adjustr = 1.0f; - - //adapt chroma to working profile - if (params->icm.workingProfile == "ProPhoto") { - adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. - } else if (params->icm.workingProfile == "Adobe RGB") { - adjustr = 1.8f; - } else if (params->icm.workingProfile == "sRGB") { - adjustr = 2.0f; - } else if (params->icm.workingProfile == "WideGamut") { - adjustr = 1.2f; - } else if (params->icm.workingProfile == "Beta RGB") { - adjustr = 1.4f; - } else if (params->icm.workingProfile == "BestRGB") { - adjustr = 1.4f; - } else if (params->icm.workingProfile == "BruceRGB") { - adjustr = 1.8f; - } - - if (call <= 3) { //simpleprocess, dcrop, improccoordinator - bufcolorig.reset(new LabImage(bfw, bfh)); - bufcolfin.reset(new LabImage(bfw, bfh)); - buftemp.reset(new LabImage(bfw, bfh)); - - if (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 5) { - bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); - originalmaskcol.reset(new LabImage(bfw, bfh)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; - bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; - bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; - bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; - bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; - bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; - buftemp->L[y][x] = original->L[y + ystart][x + xstart]; - buftemp->a[y][x] = original->a[y + ystart][x + xstart]; - buftemp->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - - const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); - const bool blends = bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struco > 0.f; - - if (blends) { - blend2(bfw, bfh); - ImProcFunctions::blendstruc(bfw, bfh, bufcolorig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struco, blend2, sk, multiThread); - - if (lp.showmaskcolmet == 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = ystart; y < yend ; y++) { - for (int x = xstart; x < xend; x++) { - const int lox = cx + x; - const int loy = cy + y; - int zone; - float localFactor = 1.f; - const float achm = lp.trans / 100.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - - if (zone > 0) { - transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); - transformed->a[y][x] = 0.f; - transformed->b[y][x] = 0.f; - } - } - } - return; - } - } - - const int inv = 0; - const bool showmaske = lp.showmaskcolmet == 3; - const bool enaMask = lp.enaColorMask; - const bool deltaE = lp.showmaskcolmet == 5; - const bool modmask = lp.showmaskcolmet == 2; - const bool zero = lp.showmaskcolmet == 0; - const bool modif = lp.showmaskcolmet == 1; - const float chrom = lp.chromacol; - const float rad = lp.radmacol; - const float gamma = lp.gammacol; - const float slope = lp.slomacol; - const float blendm = lp.blendmacol; - const float lap = params->locallab.spots.at(sp).lapmaskcol; - const bool pde = params->locallab.spots.at(sp).laplac; - const int shado = params->locallab.spots.at(sp).shadmaskcol; - const int sco = params->locallab.spots.at(sp).scopemask; - const int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); - const int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); - const int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); - const int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); - const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; - const int lumask = params->locallab.spots.at(sp).lumask; - const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; - float conthr = 0.01f * params->locallab.spots.at(sp).conthrcol; - const float mercol = params->locallab.spots.at(sp).mercol; - const float merlucol = params->locallab.spots.at(sp).merlucol; - - int tonemod = 0; - if (params->locallab.spots.at(sp).toneMethod == "one") { - tonemod = 0; - } else if (params->locallab.spots.at(sp).toneMethod == "two") { - tonemod = 1; - } else if (params->locallab.spots.at(sp).toneMethod == "thr") { - tonemod = 2; - } else if (params->locallab.spots.at(sp).toneMethod == "fou") { - tonemod = 3; - } - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - const float amountcd = 0.f; - const float anchorcd = 50.f; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, - strumask, astool, - locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, - level_bl, level_hl, level_br, level_hr, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 - ); - - if (lp.showmaskcolmet == 3) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); - return; - } else if (lp.showmaskcolmet == 4) { - return; - } - - if (lp.showmaskcolmet == 0 || lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2 || lp.showmaskcolmet == 5 || lp.enaColorMask) { - //RGB Curves - bool usergb = false; - - if (rgblocalcurve && localrgbutili && lp.qualcurvemet != 0) { - usergb = true; - const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); - - lab2rgb(*buftemp, *(tmpImage.get()), params->icm.workingProfile); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) - for (int x = 0; x < bfw; x++) { - - //std - if (tonemod == 0) { - curves::setLutVal(rgblocalcurve, tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x)); - } else { - float r = CLIP(tmpImage->r(y, x)); - float g = CLIP(tmpImage->g(y, x)); - float b = CLIP(tmpImage->b(y, x)); - - if (tonemod == 1) { // weightstd - const float r1 = rgblocalcurve[r]; - const float g1 = triangle(r, r1, g); - const float b1 = triangle(r, r1, b); - - const float g2 = rgblocalcurve[g]; - const float r2 = triangle(g, g2, r); - const float b2 = triangle(g, g2, b); - - const float b3 = rgblocalcurve[b]; - const float r3 = triangle(b, b3, r); - const float g3 = triangle(b, b3, g); - r = CLIP(r1 * 0.50f + r2 * 0.25f + r3 * 0.25f); - g = CLIP(g1 * 0.25f + g2 * 0.50f + g3 * 0.25f); - b = CLIP(b1 * 0.25f + b2 * 0.25f + b3 * 0.50f); - } else if (tonemod == 2) { // Luminance - float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; - - const float newLuminance = rgblocalcurve[currLuminance]; - currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; - const float coef = newLuminance / currLuminance; - r = LIM(r * coef, 0.f, 65535.f); - g = LIM(g * coef, 0.f, 65535.f); - b = LIM(b * coef, 0.f, 65535.f); - } else if (tonemod == 3) { // Film like Adobe - if (r >= g) { - if (g > b) { - rgbtone(r, g, b, rgblocalcurve); // Case 1: r >= g > b - } else if (b > r) { - rgbtone(b, r, g, rgblocalcurve); // Case 2: b > r >= g - } else if (b > g) { - rgbtone(r, b, g, rgblocalcurve); // Case 3: r >= b > g - } else { // Case 4: r == g == b - r = rgblocalcurve[r]; - g = rgblocalcurve[g]; - b = g; - } - } else { - if (r >= b) { - rgbtone(g, r, b, rgblocalcurve); // Case 5: g > r >= b - } else if (b > g) { - rgbtone(b, g, r, rgblocalcurve); // Case 6: b > g > r - } else { - rgbtone(g, b, r, rgblocalcurve); // Case 7: g >= b > r - } - } - } - - setUnlessOOG(tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x), r, g, b); - } - } - - rgb2lab(*(tmpImage.get()), *buftemp, params->icm.workingProfile); - - // end rgb curves - } - - if (usergb && spez) {//special use of rgb curves ex : negative - const float achm = lp.trans / 100.f; -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - const int loy = y + ystart + cy; - - for (int x = 0; x < bfw; x++) { - const int lox = x + xstart + cx; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, achm, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, achm, lp, zone, localFactor); - } - - if (zone > 0) { - transformed->L[y + ystart][x + xstart] = buftemp->L[y][x] * localFactor + (1.f - localFactor) * original->L[y + ystart][x + xstart]; - transformed->a[y + ystart][x + xstart] = buftemp->a[y][x] * localFactor + (1.f - localFactor) * original->a[y + ystart][x + xstart]; - transformed->b[y + ystart][x + xstart] = buftemp->b[y][x] * localFactor + (1.f - localFactor) * original->b[y + ystart][x + xstart]; - } - } - } - } - - //others curves - - const LabImage *origptr = usergb ? buftemp.get() : bufcolorig.get(); - - bool execcolor = false; - - if (localcutili || HHutili || locallutili || lp.ligh != 0.f || lp.cont != 0 || lp.chro != 0 || LHutili || ctoning) { - execcolor = true; - } - - bool HHcurve = false; - if (lochhCurve && HHutili) { - for (int i = 0; i < 500; i++) { - if (lochhCurve[i] != 0.5) { - HHcurve = true; - break; - } - } - } - - const float kd = 10.f * 0.01f * lp.strengrid;//correction to ctoning - - //chroma slider with curve instead of linear - const float satreal = lp.chro; - - DiagonalCurve color_satur({ - DCT_NURBS, - 0, 0, - 0.2, 0.2 + satreal / 250.0, - 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), - 1, 1 - }); - - DiagonalCurve color_saturmoins({ - DCT_NURBS, - 0, 0, - 0.1 - satreal / 150., 0.1, - rtengine::min(1.0, 0.7 - satreal / 300.), 0.7, - 1, 1 - }); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float bufcolcalca = origptr->a[ir][jr]; - float bufcolcalcb = origptr->b[ir][jr]; - float bufcolcalcL = origptr->L[ir][jr]; - - if (lp.chro != 0.f) {//slider chroma with curve DCT_NURBS - float Chprov = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); - float2 sincosval; - sincosval.y = Chprov == 0.0f ? 1.f : bufcolcalca / Chprov; - sincosval.x = Chprov == 0.0f ? 0.f : bufcolcalcb / Chprov; - - // 35000 must be globally good, more than 32768...and less than !! to avoid calculation min max - if (lp.chro > 0.f) { - Chprov = color_satur.getVal(LIM01(Chprov / 35000.f)) * 35000.f; - } else { - Chprov = color_saturmoins.getVal(LIM01(Chprov / 35000.f)) * 35000.f; - } - - if (lp.chro == -100.f) { - Chprov = 0.f; - } - - bufcolcalca = Chprov * sincosval.y; - bufcolcalcb = Chprov * sincosval.x; - } - - if (cclocalcurve && lp.qualcurvemet != 0 && localcutili) { // C=f(C) curve - const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); - const float ch = cclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more - bufcolcalca *= ch; - bufcolcalcb *= ch; - } - - if (cllocalcurve && lp.qualcurvemet != 0 && localclutili) { // C=f(L) curve - float chromaCfactor = (cllocalcurve[bufcolcalcL * 2.f]) / (bufcolcalcL * 2.f); - bufcolcalca *= chromaCfactor; - bufcolcalcb *= chromaCfactor; - } - - if (lclocalcurve && lp.qualcurvemet != 0 && locallcutili) { // L=f(C) curve - const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); - float Lc = lclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); - - if (Lc > 1.f) { - Lc = (Lc - 1.0f) * 0.1f + 1.0f; //reduct action - } else { - Lc = (Lc - 1.0f) * 0.3f + 1.0f; - } - - bufcolcalcL *= Lc; - } - - if (lochhCurve && HHcurve && lp.qualcurvemet != 0 && !ctoning) { // H=f(H) - const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); - const float hhforcurv = xatan2f(bufcolcalcb, bufcolcalca); - const float valparam = float ((lochhCurve[500.f * Color::huelab_to_huehsv2(hhforcurv)] - 0.5f)); //get H=f(H) - float2 sincosval = xsincosf(valparam); - bufcolcalca = chromat * sincosval.y; - bufcolcalcb = chromat * sincosval.x; - } - - if (lp.ligh != 0.f || lp.cont != 0) {//slider luminance or slider contrast with curve - bufcolcalcL = calclight(bufcolcalcL, lightCurveloc); - } - - if (lllocalcurve && locallutili && lp.qualcurvemet != 0) {// L=f(L) curve - bufcolcalcL = 0.5f * lllocalcurve[bufcolcalcL * 2.f]; - } - - if (loclhCurve && LHutili && lp.qualcurvemet != 0) {//L=f(H) curve - const float rhue = xatan2f(bufcolcalcb, bufcolcalca); - float l_r = bufcolcalcL / 32768.f; //Luminance Lab in 0..1 - const float valparam = loclhCurve[500.f * Color::huelab_to_huehsv2(rhue)] - 0.5f; //get l_r=f(H) - - if (valparam > 0.f) { - l_r = (1.f - valparam) * l_r + valparam * (1.f - SQR(((SQR(1.f - rtengine::min(l_r, 1.0f)))))); - } else { - constexpr float khu = 1.9f; //in reserve in case of! - //for negative - l_r *= (1.f + khu * valparam); - } - - bufcolcalcL = l_r * 32768.f; - - } - - if (ctoning) {//color toning and direct change color - if (lp.gridmet == 0) { - bufcolcalca += kd * bufcolcalcL * a_scale + a_base; - bufcolcalcb += kd * bufcolcalcL * b_scale + b_base; - } else if (lp.gridmet == 1) { - bufcolcalca += kd * scaledirect * a_scale; - bufcolcalcb += kd * scaledirect * b_scale; - } - - bufcolcalca = clipC(bufcolcalca); - bufcolcalcb = clipC(bufcolcalcb); - - } - - bufcolfin->L[ir][jr] = bufcolcalcL; - bufcolfin->a[ir][jr] = bufcolcalca; - bufcolfin->b[ir][jr] = bufcolcalcb; - } - } - - if (HHcurve && ctoning) {//not use ctoning and H(H) simultaneous but priority to ctoning - HHcurve = false; - } - - if (!execcolor) {//if we don't use color and light sliders, curves except RGB -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - bufcolfin->L[ir][jr] = origptr->L[ir][jr]; - bufcolfin->a[ir][jr] = origptr->a[ir][jr]; - bufcolfin->b[ir][jr] = origptr->b[ir][jr]; - } - } - - bool nottransit = false; - if (lp.mergemet >= 2) { //merge result with original - nottransit = true; - bufcolreserv.reset(new LabImage(bfw, bfh)); - JaggedArray lumreserv(bfw, bfh); - const std::unique_ptr bufreser(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - lumreserv[y][x] = 32768.f - reserved->L[y + ystart][x + xstart]; - bufreser->L[y][x] = reserved->L[y + ystart][x + xstart]; - bufreser->a[y][x] = reserved->a[y + ystart][x + xstart]; - bufreser->b[y][x] = reserved->b[y + ystart][x + xstart]; - - if (lp.mergemet == 2) { - bufcolreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; - bufcolreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; - bufcolreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; - } else if (lp.mergemet == 3) { - bufcolreserv->L[y][x] = lastorig->L[y + ystart][x + xstart]; - bufcolreserv->a[y][x] = lastorig->a[y + ystart][x + xstart]; - bufcolreserv->b[y][x] = lastorig->b[y + ystart][x + xstart]; - } else if (lp.mergemet == 4 && ctoningmerg) { - bufcolreserv->L[y][x] = merlucol * 327.68f; - bufcolreserv->a[y][x] = 9.f * scaledirect * a_scalemerg; - bufcolreserv->b[y][x] = 9.f * scaledirect * b_scalemerg; - } - } - } - - if (lp.strcol != 0.f) { - struct grad_params gp; - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); - bufcolfin->L[ir][jr] *= corrFactor; - } - } - } - - if (lp.strcolab != 0.f) { - struct grad_params gpab; - calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 4); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); - bufcolfin->a[ir][jr] *= corrFactor; - bufcolfin->b[ir][jr] *= corrFactor; - } - } - } - - if (lp.strcolh != 0.f) { - struct grad_params gph; - calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); - const float aa = bufcolfin->a[ir][jr]; - const float bb = bufcolfin->b[ir][jr]; - const float chrm = std::sqrt(SQR(aa) + SQR(bb)); - const float HH = xatan2f(bb, aa); - - float cor = 0.f; - if (corrFactor < 1.f) { - cor = - 2.5f * (1.f - corrFactor); - } else if (corrFactor > 1.f) { - cor = 0.03f * (corrFactor - 1.f); - } - - float newhr = HH + cor; - if (newhr > rtengine::RT_PI_F) { - newhr -= 2 * rtengine::RT_PI_F; - } else if (newhr < -rtengine::RT_PI_F) { - newhr += 2 * rtengine::RT_PI_F; - } - - const float2 sincosval = xsincosf(newhr); - bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); - bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); - } - } - } - - JaggedArray blend(bfw, bfh); - buildBlendMask(lumreserv, blend, bfw, bfh, conthr); - const float rm = 20.f / sk; - - if (rm > 0) { - float **mb = blend; -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { - gaussianBlur(mb, mb, bfw, bfh, rm); - } - } - - const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); - float** rdE = *(rdEBuffer.get()); - - deltaEforMask(rdE, bfw, bfh, bufreser.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, mercol, lp.balance, lp.balanceh); - - if (lp.mergecolMethod == 0) { //normal - - if (lp.mergemet == 4) { - bufprov.reset(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - rdE[y][x] *= SQR(rdE[y][x]); - bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); - bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); - bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); - - bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); - bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); - bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); - } - } - } else { - bufprov.reset(new LabImage(bfw, bfh)); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); - bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); - bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); - - bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); - bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); - bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); - } - } - } - - if (conthr > 0.f && lp.mergemet != 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufreser->L[y][x]); - bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufreser->a[y][x]); - bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufreser->b[y][x]); - } - } - } - } - - if (lp.mergecolMethod > 16) { //hue sat chroma luma - bufprov.reset(new LabImage(bfw, bfh)); - - if (lp.mergemet == 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - rdE[y][x] *= SQR(rdE[y][x]); - bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); - bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); - bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); - } - } - } else { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); - bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); - bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); - } - } - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - if (lp.mergecolMethod == 17) { - const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); - const float2 sincosval1 = xsincosf(huefin); - const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); - buftemp->a[y][x] = chrores * sincosval1.y; - buftemp->b[y][x] = chrores * sincosval1.x; - buftemp->L[y][x] = bufcolreserv->L[y][x]; - } else if (lp.mergecolMethod == 18) { - const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); - const float2 sincosval2 = xsincosf(hueres); - const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); - buftemp->a[y][x] = chrofin * sincosval2.y; - buftemp->b[y][x] = chrofin * sincosval2.x; - buftemp->L[y][x] = bufcolreserv->L[y][x]; - } else if (lp.mergecolMethod == 19) { - const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); - const float2 sincosval3 = xsincosf(huefin); - const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); - buftemp->a[y][x] = chrofin * sincosval3.y; - buftemp->b[y][x] = chrofin * sincosval3.x; - buftemp->L[y][x] = bufcolreserv->L[y][x]; - } else if (lp.mergecolMethod == 20) { - const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); - const float2 sincosval4 = xsincosf(hueres); - const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); - buftemp->a[y][x] = chrores * sincosval4.y; - buftemp->b[y][x] = chrores * sincosval4.x; - buftemp->L[y][x] = bufprov->L[y][x]; - } - - if (lp.mergemet == 4) { - bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); - bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); - bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); - } else { - bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); - bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); - bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); - } - } - } - - if (conthr > 0.f && lp.mergemet != 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); - bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); - bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); - } - } - } - } - - - if (lp.mergecolMethod > 0 && lp.mergecolMethod <= 16) { - //first deltaE -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolfin->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); - bufcolfin->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); - bufcolfin->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); - } - } - - //prepare RGB values in 0 1(or more)for current image and reserved - std::unique_ptr tmpImageorig(new Imagefloat(bfw, bfh)); - lab2rgb(*bufcolfin, *(tmpImageorig.get()), params->icm.workingProfile); - tmpImageorig->normalizeFloatTo1(); - - std::unique_ptr tmpImagereserv(new Imagefloat(bfw, bfh)); - lab2rgb(*bufcolreserv, *(tmpImagereserv.get()), params->icm.workingProfile); - tmpImagereserv->normalizeFloatTo1(); - - float minR = tmpImagereserv->r(0, 0); - float maxR = minR; - float minG = tmpImagereserv->g(0, 0); - float maxG = minG; - float minB = tmpImagereserv->b(0, 0); - float maxB = minB; - if (lp.mergecolMethod == 6 || lp.mergecolMethod == 9 || lp.mergecolMethod == 10 || lp.mergecolMethod == 11) { -#ifdef _OPENMP - #pragma omp parallel for reduction(max:maxR,maxG,maxB) reduction(min:minR,minG,minB) schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - minR = rtengine::min(minR, tmpImagereserv->r(ir, jr)); - maxR = rtengine::max(maxR, tmpImagereserv->r(ir, jr)); - minG = rtengine::min(minG, tmpImagereserv->g(ir, jr)); - maxG = rtengine::max(maxG, tmpImagereserv->g(ir, jr)); - minB = rtengine::min(minB, tmpImagereserv->b(ir, jr)); - maxB = rtengine::max(maxB, tmpImagereserv->b(ir, jr)); - } - } - } - - //various combinations subtract, multiply, difference, etc - if (lp.mergecolMethod == 1) { //subtract -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) {//LIM(x 0 2) 2 arbitrary value but limit... - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) - tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) - tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) - tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 2) { //difference -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->r(y, x) - tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->g(y, x) - tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->b(y, x) - tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 3) { //multiply -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) * tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) * tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) * tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 4) { //addition -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) + tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) + tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) + tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 5) { //divide -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) / (tmpImagereserv->r(y, x) + 0.00001f), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) / (tmpImagereserv->g(y, x) + 0.00001f), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) / (tmpImagereserv->b(y, x) + 0.00001f), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 6) { //soft light as Photoshop -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, softlig(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, softlig(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, softlig(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 7) { //soft light as illusions.hu -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImageorig->r(y, x))), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImageorig->g(y, x))), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImageorig->b(y, x))), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 8) { //soft light as W3C -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->r(y, x)), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->g(y, x)), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->b(y, x)), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 9) { //hard light overlay (float &b, float &a) -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImagereserv->r(y, x), tmpImageorig->r(y, x), minR, maxR), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImagereserv->g(y, x), tmpImageorig->g(y, x), minG, maxG), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImagereserv->b(y, x), tmpImageorig->b(y, x), minB, maxB), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 10) { //overlay overlay(float &a, float &b) -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 11) { //screen screen (float &a, float &b) -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, screen(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), maxR), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, screen(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), maxG), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, screen(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), maxB), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 12) { //darken only -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 13) { //lighten only -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 14) { //exclusion exclusion (float &a, float &b) -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, exclusion(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, exclusion(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, exclusion(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); - } - } - - } else if (lp.mergecolMethod == 15) { //Color burn -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); - } - } - } else if (lp.mergecolMethod == 16) { //Color dodge -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - tmpImageorig->r(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); - tmpImageorig->g(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); - tmpImageorig->b(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); - } - } - } - - tmpImageorig->normalizeFloatTo65535(); - rgb2lab(*tmpImageorig, *bufcolfin, params->icm.workingProfile); - - if (conthr > 0.f && lp.mergemet != 4) { -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); - bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); - bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); - } - } - } - } - - if (lp.softradiuscol > 0.f) { - softproc(bufcolreserv.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); - } - float meansob = 0.f; - transit_shapedetect2(call, 0, bufcolreserv.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); - } - - if (!nottransit) { -//gradient - if (lp.strcol != 0.f) { - struct grad_params gp; - calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); - bufcolfin->L[ir][jr] *= corrFactor; - } - } - - if (lp.strcolab != 0.f) { - struct grad_params gpab; - calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 5); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); - bufcolfin->a[ir][jr] *= corrFactor; - bufcolfin->b[ir][jr] *= corrFactor; - } - } - - if (lp.strcolh != 0.f) { - struct grad_params gph; - calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) - for (int jr = 0; jr < bfw; jr++) { - const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); - const float aa = bufcolfin->a[ir][jr]; - const float bb = bufcolfin->b[ir][jr]; - const float chrm = std::sqrt(SQR(aa) + SQR(bb)); - const float HH = xatan2f(bb, aa); - - float cor = 0.f; - - if (corrFactor < 1.f) { - cor = - 2.5f * (1.f - corrFactor); - } else if (corrFactor > 1.f) { - cor = 0.03f * (corrFactor - 1.f); - } - - float newhr = HH + cor; - - if (newhr > rtengine::RT_PI_F) { - newhr -= 2 * rtengine::RT_PI_F; - } else if (newhr < -rtengine::RT_PI_F) { - newhr += 2 * rtengine::RT_PI_F; - } - - const float2 sincosval = xsincosf(newhr); - bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); - bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); - } - } - - - if (lp.softradiuscol > 0.f) { - softproc(bufcolorig.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.001, 0.00001, 0.5f, sk, multiThread, 1); - } - float meansob = 0.f; - transit_shapedetect2(call, 0, bufcolorig.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); - } - - } - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - - } - } - } - -//inverse - else if (lp.inv && (lp.chro != 0 || lp.ligh != 0 || exlocalcurve || lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) && lp.colorena) { - float adjustr = 1.0f; - -//adapt chroma to working profile - if (params->icm.workingProfile == "ProPhoto") { - adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. - } else if (params->icm.workingProfile == "Adobe RGB") { - adjustr = 1.8f; - } else if (params->icm.workingProfile == "sRGB") { - adjustr = 2.0f; - } else if (params->icm.workingProfile == "WideGamut") { - adjustr = 1.2f; - } else if (params->icm.workingProfile == "Beta RGB") { - adjustr = 1.4f; - } else if (params->icm.workingProfile == "BestRGB") { - adjustr = 1.4f; - } else if (params->icm.workingProfile == "BruceRGB") { - adjustr = 1.8f; - } - - std::unique_ptr bufmaskblurcol; - std::unique_ptr originalmaskcol; - const std::unique_ptr bufcolorig(new LabImage(GW, GH)); - - if (lp.enaColorMaskinv || lp.showmaskcolmetinv == 1) { - bufmaskblurcol.reset(new LabImage(GW, GH, true)); - originalmaskcol.reset(new LabImage(GW, GH)); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < GH ; y++) { - for (int x = 0; x < GW; x++) { - bufcolorig->L[y][x] = original->L[y][x]; - } - } - - constexpr int inv = 1; - const bool showmaske = lp.showmaskcolmetinv == 1; - const bool enaMask = lp.enaColorMaskinv; - constexpr bool deltaE = false; - constexpr bool modmask = false; - const bool zero = lp.showmaskcolmetinv == 0; - constexpr bool modif = false; - - const float chrom = lp.chromacol; - const float rad = lp.radmacol; - const float gamma = lp.gammacol; - const float slope = lp.slomacol; - const float blendm = lp.blendmacol; - const float lap = params->locallab.spots.at(sp).lapmaskcol; - const bool pde = params->locallab.spots.at(sp).laplac; - int shado = params->locallab.spots.at(sp).shadmaskcol; - int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); - int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); - int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); - int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); - int sco = params->locallab.spots.at(sp).scopemask; - int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; - int lumask = params->locallab.spots.at(sp).lumask; - float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - constexpr float amountcd = 0.f; - constexpr float anchorcd = 50.f; - - maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, - strumask, params->locallab.spots.at(sp).toolcol, - locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, - level_bl, level_hl, level_br, level_hr, - shortcu, false, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftColorMask, lp.blurcolmask, lp.contcolmask, -1 - ); - - if (lp.showmaskcolmetinv == 1) { - showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); - return; - } - - if (lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) { - InverseColorLight_Local(false, false, sp, 0, lp, originalmaskcol.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - } - } - -//begin common mask - if(lp.maskena) { - int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); - int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); - int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); - int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); - int bfh = yend - ystart; - int bfw = xend - xstart; - if (bfw >= mSP && bfh >= mSP) { - - if (lp.blurma >= 0.25f && lp.fftma && call == 2) { - optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); - } - array2D blechro(bfw, bfh); - array2D ble(bfw, bfh); - array2D hue(bfw, bfh); - array2D guid(bfw, bfh); - - std::unique_ptr bufcolorigsav; - std::unique_ptr bufcolorig; - std::unique_ptr bufcolfin; - std::unique_ptr bufmaskblurcol; - std::unique_ptr originalmaskcol; - std::unique_ptr bufcolreserv; - - int wo = original->W; - int ho = original->H; - LabImage *origsav = nullptr; - origsav = new LabImage(wo, ho); - origsav->CopyFrom(original); - - if (call <= 3) { - bufcolorig.reset(new LabImage(bfw, bfh)); - bufcolfin.reset(new LabImage(bfw, bfh)); - bufcolorigsav.reset(new LabImage(bfw, bfh)); - - if (lp.showmask_met == 1 || lp.ena_Mask || lp.showmask_met == 2 || lp.showmask_met == 3) { - bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); - originalmaskcol.reset(new LabImage(bfw, bfh)); - } -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; - bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; - bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; - - bufcolorigsav->L[y][x] = original->L[y + ystart][x + xstart]; - bufcolorigsav->a[y][x] = original->a[y + ystart][x + xstart]; - bufcolorigsav->b[y][x] = original->b[y + ystart][x + xstart]; - - bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; - bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; - bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; - } - } - const int inv = 0; - const bool showmaske = lp.showmask_met == 2; - const bool enaMask = lp.ena_Mask; - const bool deltaE = lp.showmask_met == 3; - const bool modmask = lp.showmask_met == 1; - const bool zero = lp.showmask_met == 0; - const bool modif = lp.showmask_met == 1; - const float chrom = params->locallab.spots.at(sp).chromask; - const float rad = params->locallab.spots.at(sp).radmask; - const float gamma = params->locallab.spots.at(sp).gammask; - const float slope = params->locallab.spots.at(sp).slopmask; - float blendm = params->locallab.spots.at(sp).blendmask; - float blendmab = params->locallab.spots.at(sp).blendmaskab; - if (lp.showmask_met == 2) { - blendm = 0.f;//normalize behavior mask with others no action of blend - blendmab = 0.f; - } - const float lap = params->locallab.spots.at(sp).lapmask; - const bool pde = params->locallab.spots.at(sp).laplac; - const int shado = params->locallab.spots.at(sp).shadmask; - const int sco = params->locallab.spots.at(sp).scopemask; - const int level_bl = params->locallab.spots.at(sp).csthresholdmask.getBottomLeft(); - const int level_hl = params->locallab.spots.at(sp).csthresholdmask.getTopLeft(); - const int level_br = params->locallab.spots.at(sp).csthresholdmask.getBottomRight(); - const int level_hr = params->locallab.spots.at(sp).csthresholdmask.getTopRight(); - const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; - const int lumask = params->locallab.spots.at(sp).lumask; - const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskmask; - const float softr = params->locallab.spots.at(sp).softradiusmask; - - const float mindE = 2.f + MINSCOPE * sco * lp.thr; - const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); - const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; - const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); - const float amountcd = 0.f; - const float anchorcd = 50.f; - - maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, - strumask, astool, - locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, lochhhmas_Curve, lhhmas_utili, multiThread, - enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, blendmab, shado, amountcd, anchorcd, lmasklocal_curve, localmask_utili, loclmasCurve_wav, lmasutili_wav, - level_bl, level_hl, level_br, level_hr, - shortcu, delt, hueref, chromaref, lumaref, - maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.fftma, lp.blurma, lp.contma, 12 - ); - - - if (lp.showmask_met == 2) { - showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); - return; - } -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - bufcolfin->L[y][x] = bufcolorig->L[y][x]; - bufcolfin->a[y][x] = bufcolorig->a[y][x]; - bufcolfin->b[y][x] = bufcolorig->b[y][x]; - hue[y][x] = xatan2f(bufcolfin->b[y][x], bufcolfin->a[y][x]); - const float chromah = std::sqrt(SQR(bufcolfin->b[y][x]) + SQR(bufcolfin->a[y][x])); - ble[y][x] = bufcolfin->L[y][x] / 32768.f; - blechro[y][x] = chromah / 32768.f; - guid[y][x] = bufcolorigsav->L[y][x] / 32768.f; - } - } - if (softr != 0.f) {//soft for L a b because we change color... - float rad = softr; - const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; - const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); - const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); - - constexpr float epsilmax = 0.005f; - constexpr float epsilmin = 0.00001f; - - constexpr float aepsil = (epsilmax - epsilmin) / 100.f; - constexpr float bepsil = epsilmin; - const float epsil = rad < 0.f ? 0.001f : aepsil * rad + bepsil; - - rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); - rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < bfh; y++) { - for (int x = 0; x < bfw; x++) { - float2 sincosval = xsincosf(hue[y][x]); - bufcolfin->L[y][x] = 32768.f * ble[y][x]; - bufcolfin->a[y][x] = 32768.f * blechro[y][x] * sincosval.y; - bufcolfin->b[y][x] = 32768.f * blechro[y][x] * sincosval.x; - } - } - } - - - - float meansob = 0.f; - transit_shapedetect2(call, 20, bufcolorigsav.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, nullptr, lp, origsav, transformed, cx, cy, sk); - delete origsav; - origsav = NULL; - - if (params->locallab.spots.at(sp).recurs) { - original->CopyFrom(transformed, multiThread); - float avge; - calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); - } - - } - } - } - -//end common mask - -// Gamut and Munsell control - very important do not deactivated to avoid crash - if (params->locallab.spots.at(sp).avoid) { - const float ach = lp.trans / 100.f; - - TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile); - const float wip[3][3] = { - {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, - {static_cast(wiprof[1][0]), static_cast(wiprof[1][1]), static_cast(wiprof[1][2])}, - {static_cast(wiprof[2][0]), static_cast(wiprof[2][1]), static_cast(wiprof[2][2])} - }; - const bool highlight = params->toneCurve.hrenabled; - const bool needHH = (lp.chro != 0.f); -#ifdef _OPENMP - #pragma omp parallel if (multiThread) -#endif - { -#ifdef __SSE2__ - float atan2Buffer[transformed->W] ALIGNED16; - float sqrtBuffer[transformed->W] ALIGNED16; - float sincosyBuffer[transformed->W] ALIGNED16; - float sincosxBuffer[transformed->W] ALIGNED16; - vfloat c327d68v = F2V(327.68f); - vfloat onev = F2V(1.f); -#endif - -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif - for (int y = 0; y < transformed->H; y++) { - const int loy = cy + y; - const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing - - if (isZone0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - -#ifdef __SSE2__ - int i = 0; - - for (; i < transformed->W - 3; i += 4) { - vfloat av = LVFU(transformed->a[y][i]); - vfloat bv = LVFU(transformed->b[y][i]); - - if (needHH) { // only do expensive atan2 calculation if needed - STVF(atan2Buffer[i], xatan2f(bv, av)); - } - - vfloat Chprov1v = vsqrtf(SQRV(bv) + SQRV(av)); - STVF(sqrtBuffer[i], Chprov1v / c327d68v); - vfloat sincosyv = av / Chprov1v; - vfloat sincosxv = bv / Chprov1v; - vmask selmask = vmaskf_eq(Chprov1v, ZEROV); - sincosyv = vself(selmask, onev, sincosyv); - sincosxv = vselfnotzero(selmask, sincosxv); - STVF(sincosyBuffer[i], sincosyv); - STVF(sincosxBuffer[i], sincosxv); - } - - for (; i < transformed->W; i++) { - float aa = transformed->a[y][i]; - float bb = transformed->b[y][i]; - - if (needHH) { // only do expensive atan2 calculation if needed - atan2Buffer[i] = xatan2f(bb, aa); - } - - float Chprov1 = std::sqrt(SQR(bb) + SQR(aa)); - sqrtBuffer[i] = Chprov1 / 327.68f; - - if (Chprov1 == 0.0f) { - sincosyBuffer[i] = 1.f; - sincosxBuffer[i] = 0.0f; - } else { - sincosyBuffer[i] = aa / Chprov1; - sincosxBuffer[i] = bb / Chprov1; - } - } - -#endif - - for (int x = 0; x < transformed->W; x++) { - int lox = cx + x; - int zone; - float localFactor = 1.f; - - if (lp.shapmet == 0) { - calcTransition(lox, loy, ach, lp, zone, localFactor); - } else /*if (lp.shapmet == 1)*/ { - calcTransitionrect(lox, loy, ach, lp, zone, localFactor); - } - - if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values - continue; - } - - float Lprov1 = transformed->L[y][x] / 327.68f; - float2 sincosval; -#ifdef __SSE2__ - float HH = atan2Buffer[x]; // reading HH from line buffer even if line buffer is not filled is faster than branching - float Chprov1 = sqrtBuffer[x]; - sincosval.y = sincosyBuffer[x]; - sincosval.x = sincosxBuffer[x]; - float chr = 0.f; - -#else - const float aa = transformed->a[y][x]; - const float bb = transformed->b[y][x]; - float HH = 0.f, chr = 0.f; - - if (needHH) { // only do expensive atan2 calculation if needed - HH = xatan2f(bb, aa); - } - - float Chprov1 = std::sqrt(SQR(aa) + SQR(bb)) / 327.68f; - - if (Chprov1 == 0.0f) { - sincosval.y = 1.f; - sincosval.x = 0.0f; - } else { - sincosval.y = aa / (Chprov1 * 327.68f); - sincosval.x = bb / (Chprov1 * 327.68f); - } -#endif - - Color::pregamutlab(Lprov1, HH, chr); - Chprov1 = rtengine::min(Chprov1, chr); - Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.92f); - transformed->L[y][x] = Lprov1 * 327.68f; - transformed->a[y][x] = 327.68f * Chprov1 * sincosval.y; - transformed->b[y][x] = 327.68f * Chprov1 * sincosval.x; - - if (needHH) { - const float Lprov2 = original->L[y][x] / 327.68f; - float correctionHue = 0.f; // Munsell's correction - float correctlum = 0.f; - const float memChprov = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])) / 327.68f; - float Chprov = std::sqrt(SQR(transformed->a[y][x]) + SQR(transformed->b[y][x])) / 327.68f; - Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum); - - if (std::fabs(correctionHue) < 0.015f) { - HH += correctlum; // correct only if correct Munsell chroma very little. - } - - sincosval = xsincosf(HH + correctionHue); - transformed->a[y][x] = 327.68f * Chprov * sincosval.y; // apply Munsell - transformed->b[y][x] = 327.68f * Chprov * sincosval.x; - } - } - } - } - } - -} - -} From 450af240c8eae2f5a76bb76f7c21bcef7bb618a9 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 17:07:04 +0200 Subject: [PATCH 073/114] second french --- rtdata/languages/Francais | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 120759609..01a96904b 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1821,7 +1821,7 @@ TP_LOCALLAB_CIRCRAD_TOOLTIP;Contient les références du RT-spot, utile pour la TP_LOCALLAB_CLARICRES;Fusion Chroma TP_LOCALLAB_CLARIFRA;Clarté & Masque de betteté - Fusion & adoucir images TP_LOCALLAB_CLARILRES;Fusion Luma -TP_LOCALLAB_CLARISOFT;rayon adoucir +TP_LOCALLAB_CLARISOFT;Rayon adoucir TP_LOCALLAB_CLARISOFT_TOOLTIP;Actif pour Clarté et Masque de netteté si différent de zéro.\n\nActif pour toutes les pyramides ondelettes.\nInactif si rayon = 0 TP_LOCALLAB_CLARITYML;Claté TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' @@ -1833,6 +1833,43 @@ TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif TP_LOCALLAB_COLORSCOPE;Outils Etendue Couleur TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. TP_LOCALLAB_COLOR_TOOLNAME;Couleur&Lumière (Défauts) - 11 +TP_LOCALLAB_COL_NAME;Nom +TP_LOCALLAB_COL_VIS;Statut +TP_LOCALLAB_COMPFRA;Niveaux Contraste directionnel +TP_LOCALLAB_COMPFRAME_TOOLTIP;Autorise des effets spéciaux. Vous pouvez réduire les artéfacts avec 'Clarté & Masque netteté - Fusion & Images douces".\nUtilise des ressources +TP_LOCALLAB_COMPLEX_METHOD;Complexitée logicielle +TP_LOCALLAB_COMPLEX_TOOLTIP; Autorise l'utilisateur à sélectionner des rubriques Ajustements locaux. +TP_LOCALLAB_COMPREFRA;Niveaux de (de)compression dynamique +TP_LOCALLAB_COMPRESS_TOOLTIP;Utilisesi nécessaire le module 'Clarté & Masque de netteté - Fusion & Images douces' en ajustant 'Rayon doux' pour réduire les artéfacts. +TP_LOCALLAB_CONTCOL;Seuil de Contraste Masque flou +TP_LOCALLAB_CONTFRA;Contraste par niveau +TP_LOCALLAB_CONTRAST;Contraste +TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;Contrôle de contraste du masque. +TP_LOCALLAB_CONTRESID;Contraste +TP_LOCALLAB_CONTTHR;Seuil contraste +TP_LOCALLAB_CSTHRESHOLD;Ψ Ondelettes niveaux +TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Masque Ondelettes niveau +TP_LOCALLAB_CURV;Luminosité - Contraste - Chrominance "Super" +TP_LOCALLAB_CURVCURR;Normal +TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;Si la courbe est au sommet, le masque est compétement noir aucune transformation n'est réalisée par le masque sur l'image.\nQuand vous descendez la courbe, progressivement le masque va se colorer et s'éclaicir, l'image change de plus en plus.\n\nIl est recommendé (pas obligatoire) de positionner le sommet des courbes curves sur la ligne de transition grise qui représnte les références (chroma, luma, couleur). +TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP;Si la courbe est au sommet,le masque est compétement noir aucune transformation n'est réalisée par le masque sur l'image.\nQuand vous descendez la courbe, progressivement le masque va se colorer et s'éclaicir, l'image change de plus en plus.\nVous pouvez choisir ou non de positionner le sommet de la courbe sur la transition. +TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;Pour être actif, vous devez activer la combobox 'Curves type' +TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Curve tonale +TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), peut être utilisée avec L(H) dans Couleur et lumière +TP_LOCALLAB_CURVEMETHOD_TOOLTIP;'Normal', la courbe L=f(L) a le même algorithme que le curseur luminosité.\n'Super' the curve L=f(L) has an new improved algorithm, which can leeds in some cases to artifacts. +TP_LOCALLAB_CURVENCONTRAST;Super+Contrast threshold (experimental) +TP_LOCALLAB_CURVENH;Super +TP_LOCALLAB_CURVENHSU;Combined HueChroma (experimental) +TP_LOCALLAB_CURVENSOB2;Combined HueChroma + Contrast threshold (experimental) +TP_LOCALLAB_CURVNONE;Désactive courbes +TP_LOCALLAB_DARKRETI;Obscuirité +TP_LOCALLAB_DEHAFRA;Désembuer +TP_LOCALLAB_DEHAZ;Force +TP_LOCALLAB_DEHAZ_TOOLTIP;Valeurs Négatives ajoute de la brûme +TP_LOCALLAB_DELTAD;Delta balance +TP_LOCALLAB_DELTAEC;Masque ΔE Image +TP_LOCALLAB_DENOIS;Ψ Débruitage +TP_LOCALLAB_DENOI_EXP;Débruitage From af4b17ac844dfcc7b3ecbb315a86a468af7244ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Thu, 2 Jul 2020 17:38:59 +0200 Subject: [PATCH 074/114] Minor cleanups --- rtengine/LUT.h | 60 ++++++++++++++++++++---------------------- rtengine/iplocallab.cc | 30 ++++++++++++--------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/rtengine/LUT.h b/rtengine/LUT.h index 933fdba3a..c426bbaaa 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -58,6 +58,7 @@ #pragma once +#include #include #include #include @@ -98,8 +99,8 @@ protected: unsigned int size; unsigned int upperBound; // always equals size-1, parameter created for performance reason private: - unsigned int owner; #ifdef __SSE2__ + unsigned int owner; alignas(16) vfloat maxsv; alignas(16) vfloat sizev; alignas(16) vint sizeiv; @@ -140,35 +141,30 @@ public: } } - LUT(const std::vector input, int flags = LUT_CLIP_BELOW | LUT_CLIP_ABOVE) + LUT(const std::vector& input, int flags = LUT_CLIP_BELOW | LUT_CLIP_ABOVE) : + maxs(input.size() - 2), + maxsf(maxs), + data(new T[input.size() + 3]), // Add a few extra elements so [](vfloat) won't access out-of-bounds memory. + clip(flags), + size(input.size()), + upperBound(size - 1), + owner(1), +#ifdef __SSE2__ + maxsv(F2V(maxs)), + sizev(F2V(size - 1)), + sizeiv(_mm_set1_epi32(size - 1)), +#endif + dirty(true) { #ifndef NDEBUG - if (input.size() <= 0) { - printf("s<=0!\n"); + if (input.empty()) { + printf("s=0!\n"); } - assert (input.size() > 0); + assert(!input.empty()); #endif - dirty = true; - clip = flags; - // Add a few extra elements so [](vfloat) won't access out-of-bounds memory. - // The routine would still produce the right answer, but might cause issues - // with address/heap checking programs. - data = new T[input.size() + 3]; - owner = 1; - size = input.size(); - upperBound = size - 1; - maxs = size - 2; - maxsf = (float)maxs; -#ifdef __SSE2__ - maxsv = F2V( maxs ); - sizeiv = _mm_set1_epi32( (int)(size - 1) ); - sizev = F2V( size - 1 ); -#endif - for (size_t i = 0; i < input.size(); ++i) { - data[i] = input[i]; - } + std::copy_n(input.begin(), input.size(), data); } void operator ()(int s, int flags = LUT_CLIP_BELOW | LUT_CLIP_ABOVE, bool initZero = false) @@ -256,7 +252,7 @@ public: return size > 0 ? upperBound : 0; } - LUT & operator=(const LUT& rhs) + LUT& operator=(const LUT& rhs) { if (this != &rhs) { if (rhs.size > this->size) { @@ -287,7 +283,7 @@ public: } // handy to sum up per thread histograms. #pragma omp simd speeds up the loop by about factor 3 for LUTu (uint32_t). - LUT & operator+=(const LUT& rhs) + LUT& operator+=(const LUT& rhs) { if (rhs.size == this->size) { #ifdef _OPENMP @@ -304,7 +300,7 @@ public: // multiply all elements of LUT with a constant float value template::value>::type> - LUT & operator*=(float factor) + LUT& operator*=(float factor) { #ifdef _OPENMP #pragma omp simd @@ -319,7 +315,7 @@ public: // divide all elements of LUT by a constant float value template::value>::type> - LUT & operator/=(float divisor) + LUT& operator/=(float divisor) { #ifdef _OPENMP #pragma omp simd @@ -489,7 +485,7 @@ public: // Return the value for "index" that is in the [0-1] range. template::value>::type> - T getVal01 (float index) const + T getVal01(float index) const { index *= (float)upperBound; int idx = (int)index; // don't use floor! The difference in negative space is no problems here @@ -514,19 +510,19 @@ public: return (p1 + p2 * diff); } - operator bool (void) const + operator bool() const // FIXME: Should be explicit { return size > 0; } - void clear(void) + void clear() { if (data && size) { memset(data, 0, size * sizeof(T)); } } - void reset(void) + void reset() { if (data) { delete[] data; diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 74be48e09..11e2dcad7 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -57,6 +57,7 @@ namespace { + constexpr int limscope = 80; constexpr int mSPsharp = 39; //minimum size Spot Sharp due to buildblendmask constexpr int mSPwav = 32; //minimum size Spot Wavelet @@ -68,14 +69,16 @@ constexpr int TS = 64; // Tile size constexpr float epsilonw = 0.001f / (TS * TS); //tolerance constexpr int offset = 25; // shift between tiles -std::unique_ptr buildMeaLut(const float inVals[11], const float mea[10], float &lutFactor) { - +std::unique_ptr buildMeaLut(const float inVals[11], const float mea[10], float& lutFactor) +{ constexpr int lutSize = 100; - const float lutMax = ceil(mea[9]); + + const float lutMax = std::ceil(mea[9]); const float lutDiff = lutMax / lutSize; + std::vector lutVals(lutSize); int jStart = 1; - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < lutSize; ++i) { const float val = i * lutDiff; if (val < mea[0]) { // still < first value => no interpolation @@ -87,35 +90,38 @@ std::unique_ptr buildMeaLut(const float inVals[11], const float mea[10], f lutVals[i] = inVals[j]; ++jStart; break; - } else if (val < mea[j]) { + } + if (val < mea[j]) { // interpolate const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]); lutVals[i] = rtengine::intp(dist, inVals[j], inVals[j - 1]); break; - } else { - lutVals[i] = inVals[10]; } + lutVals[i] = inVals[10]; } } } lutFactor = 1.f / lutDiff; return std::unique_ptr(new LUTf(lutVals)); - } -constexpr float clipLoc(float x) { +constexpr float clipLoc(float x) +{ return rtengine::LIM(x, 0.f, 32767.f); } -constexpr float clipDE(float x) { +constexpr float clipDE(float x) +{ return rtengine::LIM(x, 0.3f, 1.f); } -constexpr float clipC(float x) { +constexpr float clipC(float x) +{ return rtengine::LIM(x, -42000.f, 42000.f); } -constexpr float clipChro(float x) { +constexpr float clipChro(float x) +{ return rtengine::LIM(x, 0.f, 140.f); } From a61c0311ccc948a53905def0d4da55e742a34c29 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 18:15:53 +0200 Subject: [PATCH 075/114] 3 french --- rtdata/languages/Francais | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 01a96904b..e3f3a9d95 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1756,13 +1756,13 @@ TP_LOCALLAB_ADJ;Egalisateur Bleu-jaune Rouge-vert TP_LOCALLAB_ALL;Toutes les rubriques TP_LOCALLAB_AMOUNT;Quantité TP_LOCALLAB_ARTIF;Détection de forme -TP_LOCALLAB_ARTIF_TOOLTIP;Le seuil deltaE-étendue accroit la plage of étendue-deltaE - les valeurs élévées sont pour les images à gamut élévé.\nAugmenter l'affaiblissement deltaE améliore la détection de forme, mais peu réduire la capacité de détection. +TP_LOCALLAB_ARTIF_TOOLTIP;Le seuil deltaE étendue accroit la plage of étendue-deltaE - les valeurs élévées sont pour les images à gamut élévé.\nAugmenter l'affaiblissement deltaE améliore la détection de forme, mais peu réduire la capacité de détection. TP_LOCALLAB_AUTOGRAY;Automatique TP_LOCALLAB_AVOID;Evite les dérives de couleurs TP_LOCALLAB_BALAN;Balance ΔE ab-L TP_LOCALLAB_BALANEXP;ΔØ EPD balance TP_LOCALLAB_BALANH;Balance ΔE C-H -TP_LOCALLAB_BALAN_TOOLTIP;Change l'algorithme des paramètres ΔE.\nPlus ou moins ab-L, plus ou moins C - H.\nPas pour le Debruitage +TP_LOCALLAB_BALAN_TOOLTIP;Change l'algorithme des paramètres ΔE.\nPlus ou moins ab-L, plus ou moins C-H.\nPas pour le Debruitage TP_LOCALLAB_BASELOG;Base Logarithme TP_LOCALLAB_BILATERAL;Filtre Bilateral TP_LOCALLAB_BLACK_EV;Noir Ev @@ -1819,7 +1819,7 @@ TP_LOCALLAB_CHRRT;Chroma TP_LOCALLAB_CIRCRADIUS;Taille Spot TP_LOCALLAB_CIRCRAD_TOOLTIP;Contient les références du RT-spot, utile pour la détection de forme (couleur, luma, chroma, Sobel).\nLes faibles valeurs peuvent être utiles pour les feuillages.\nLes valeurs élevées peuvent être utile pour la peau TP_LOCALLAB_CLARICRES;Fusion Chroma -TP_LOCALLAB_CLARIFRA;Clarté & Masque de betteté - Fusion & adoucir images +TP_LOCALLAB_CLARIFRA;Clarté & Masque de netteté - Fusion & adoucir images TP_LOCALLAB_CLARILRES;Fusion Luma TP_LOCALLAB_CLARISOFT;Rayon adoucir TP_LOCALLAB_CLARISOFT_TOOLTIP;Actif pour Clarté et Masque de netteté si différent de zéro.\n\nActif pour toutes les pyramides ondelettes.\nInactif si rayon = 0 @@ -1863,13 +1863,43 @@ TP_LOCALLAB_CURVENHSU;Combined HueChroma (experimental) TP_LOCALLAB_CURVENSOB2;Combined HueChroma + Contrast threshold (experimental) TP_LOCALLAB_CURVNONE;Désactive courbes TP_LOCALLAB_DARKRETI;Obscuirité -TP_LOCALLAB_DEHAFRA;Désembuer +TP_LOCALLAB_DEHAFRA;Elimination de la brume TP_LOCALLAB_DEHAZ;Force -TP_LOCALLAB_DEHAZ_TOOLTIP;Valeurs Négatives ajoute de la brûme +TP_LOCALLAB_DEHAZ_TOOLTIP;Valeurs Négatives ajoute de la brume TP_LOCALLAB_DELTAD;Delta balance TP_LOCALLAB_DELTAEC;Masque ΔE Image -TP_LOCALLAB_DENOIS;Ψ Débruitage -TP_LOCALLAB_DENOI_EXP;Débruitage +TP_LOCALLAB_DENOIS;Ψ Réduction du bruit +TP_LOCALLAB_DENOI_EXP;Réduction du bruit +TP_LOCALLAB_DENOI_TOOLTIP;Ce module peut être utilisé seul (à la fin du processus), ou en complément de Réduction du bruit (au début).\nEtendue(deltaE)permet de différencier l'action.\nVous pouvez compléter avec "median" ou "Filtre guidé" (Adoucir Flou...).\nVous pouvez compléter l'action avec "Flou niveaux" "Ondelette pyramide" +TP_LOCALLAB_DEPTH;Profondeur +TP_LOCALLAB_DETAIL;Contrast local +TP_LOCALLAB_DETAILSH;Details +TP_LOCALLAB_DETAILTHR;Seuil Detail Luminance Chroma (DCT ƒ) +TP_LOCALLAB_DUPLSPOTNAME;Copier +TP_LOCALLAB_EDGFRA;Netteté des bords +TP_LOCALLAB_EDGSHOW;Montre tous les outils +TP_LOCALLAB_ELI;Ellipse +TP_LOCALLAB_ENABLE_AFTER_MASK;Utilise Tone Mapping +TP_LOCALLAB_ENABLE_MASK;Active masque +TP_LOCALLAB_ENABLE_MASKAFT;Utilise tous les algorithmes Exposition +TP_LOCALLAB_ENARETIMASKTMAP_TOOLTIP;Si activé, le Masque utilise les données recupérées après 'Transmission Map' au lieu des données originales +TP_LOCALLAB_ENH;Amélioré +TP_LOCALLAB_ENHDEN;Amélioré + chroma réduction bruit +TP_LOCALLAB_EPSBL;Detail +TP_LOCALLAB_EQUIL;Normalise Luminance +TP_LOCALLAB_EQUILTM_TOOLTIP;Reconstruit la luminance de telle manière que la moyenne et la variance de l'image traitée soient identiques à celle d'origine +TP_LOCALLAB_ESTOP;Arrêt des bords +TP_LOCALLAB_EV_DUPL;Copier de +TP_LOCALLAB_EV_NVIS;Cacher +TP_LOCALLAB_EV_NVIS_ALL;Cacher tout +TP_LOCALLAB_EV_VIS;Montrer +TP_LOCALLAB_EV_VIS_ALL;Montrer tout +TP_LOCALLAB_EXCLUF;Exclure +TP_LOCALLAB_EXCLUF_TOOLTIP;Peut être utilsé pour exclure une partie des données - agir sur Etendue pour prendre en compte plus de couleurs.\n Vous pouvez utiliser tous les réglages pour ce type de RT-spot. +TP_LOCALLAB_EXCLUTYPE;Spot méthode +TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Spot Normal utilise les données récursives.\n\nSpot exclusion réinitialise les données d'origine.\nPeut être utilsé pour annuler totalement ou partiellement une action précédente ou pour réaliser un mode inverse +TP_LOCALLAB_EXECLU;Spot Exclusion +TP_LOCALLAB_EXNORM;Spot Normal From 3e4165985e4856278beb7ed884710a98c9480478 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 2 Jul 2020 19:31:11 +0200 Subject: [PATCH 076/114] 4 french --- rtdata/languages/Francais | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index e3f3a9d95..d78442b40 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1778,27 +1778,27 @@ TP_LOCALLAB_BLLC;Luminance & Chrominance TP_LOCALLAB_BLLO;Luminance seulement TP_LOCALLAB_BLMED;Median TP_LOCALLAB_BLMETHOD_TOOLTIP;Normal - direct floute et bruite avec tous les réglages.\nInverse floute et bruite avec tous les réglages. Soyez prudents certains resultats peuvent être curieux -TP_LOCALLAB_BLNOI_EXP;Blur & Noise +TP_LOCALLAB_BLNOI_EXP;Flouter & Bruit TP_LOCALLAB_BLNORM;Normal TP_LOCALLAB_BLSYM;Symétrique -TP_LOCALLAB_BLUFR;Doux - Floute - Grain - Debruitage -TP_LOCALLAB_BLUMETHOD_TOOLTIP;To blur the background and isolate the foreground:\n*Blur the background by a RT-spot fully covering the image (high values for scope and transition) - normal or inverse.\n*Isolate the foreground by one or more excluding RT-spot with the tools you want (increse scope).\n\nThis module can be used in additional noise reduction,including "median" and "Guided filter" -TP_LOCALLAB_BLUR;Flou Gaussien Blur - Bruit - Grain -TP_LOCALLAB_BLURCBDL;Flou niveaux 0-1-2-3-4 +TP_LOCALLAB_BLUFR;Adoucir - Flouter - Grain - Debruiter +TP_LOCALLAB_BLUMETHOD_TOOLTIP;Pour flouter l'arrère plan et isoler le premier plan:\n*Flouter l'arrière plan avec un RT-spot couvrant totalement l'image (valeurs élevées Etendue et transition) - normal ou inverse.\n*Isoler le premier plan avec un ou plusieurs RT-spot Exclusion avec l'outils que vous voulez (accroître Etendue).\n\nCe module peut être utilisé en réduction de bruit additionnelle,incluant un "median" et un "Filtre Guidé" +TP_LOCALLAB_BLUR;Flou Gaussien - Bruit - Grain +TP_LOCALLAB_BLURCBDL;Flouter niveaux 0-1-2-3-4 TP_LOCALLAB_BLURCOL;Rayon floutage masque -TP_LOCALLAB_BLURDE;Floute la détection de forme +TP_LOCALLAB_BLURDE;Flouter la détection de forme TP_LOCALLAB_BLURLC;Luminance seulement -TP_LOCALLAB_BLURLEVELFRA;Floute niveaux +TP_LOCALLAB_BLURLEVELFRA;Flouter niveaux TP_LOCALLAB_BLURMASK_TOOLTIP;Génère un masque flou, prend en compte la structure avec le curseur de seuil de contraste du Masque flou. -TP_LOCALLAB_BLURRESIDFRA;Floute image Résiduelle -TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Floute Grain & Débruitage - 1 +TP_LOCALLAB_BLURRESIDFRA;Flouter image Résiduelle +TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Flouter Grain & Réduction du Bruit - 1 TP_LOCALLAB_BLWH;Tous les chnagements forcés en noir et blanc TP_LOCALLAB_BLWH_TOOLTIP;Force le changement de la composante "a" et "b" à zéro.\nUtile quand l'utilisateur choisit un processus noir et blanc, ou un film. -TP_LOCALLAB_BUTTON_ADD;Ajout -TP_LOCALLAB_BUTTON_DEL;Efface -TP_LOCALLAB_BUTTON_DUPL;Duplique -TP_LOCALLAB_BUTTON_REN;Renomme -TP_LOCALLAB_BUTTON_VIS;Montre/Cache +TP_LOCALLAB_BUTTON_ADD;Ajouter +TP_LOCALLAB_BUTTON_DEL;Effacer +TP_LOCALLAB_BUTTON_DUPL;Dupliquer +TP_LOCALLAB_BUTTON_REN;Renommer +TP_LOCALLAB_BUTTON_VIS;Montrer/Cacher TP_LOCALLAB_CBDL;Contraste par niveaux de détail - Défauts TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. @@ -1889,7 +1889,7 @@ TP_LOCALLAB_EPSBL;Detail TP_LOCALLAB_EQUIL;Normalise Luminance TP_LOCALLAB_EQUILTM_TOOLTIP;Reconstruit la luminance de telle manière que la moyenne et la variance de l'image traitée soient identiques à celle d'origine TP_LOCALLAB_ESTOP;Arrêt des bords -TP_LOCALLAB_EV_DUPL;Copier de +TP_LOCALLAB_EV_DUPL;Copier vers TP_LOCALLAB_EV_NVIS;Cacher TP_LOCALLAB_EV_NVIS_ALL;Cacher tout TP_LOCALLAB_EV_VIS;Montrer @@ -1900,6 +1900,15 @@ TP_LOCALLAB_EXCLUTYPE;Spot méthode TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Spot Normal utilise les données récursives.\n\nSpot exclusion réinitialise les données d'origine.\nPeut être utilsé pour annuler totalement ou partiellement une action précédente ou pour réaliser un mode inverse TP_LOCALLAB_EXECLU;Spot Exclusion TP_LOCALLAB_EXNORM;Spot Normal +TP_LOCALLAB_EXPCBDL_TOOLTIP;Dans le cas d'un capteur contaminé ("graisse"), et qaund la surface est importante ou pourune série de petits défauts.\n\na) Mettre la sélection du spot sur un défaut prononcé (adapter la taille si nécessaire), utilise run spot suffisament grand qui autorise les ondelettes; b) choisir une aire suffisament grande pour couvrir largement la zone affectée par les défauts; c) Choisir une valeur de transition (basse) et une transition affaiblissement (haute); d) agir sur les niveaux 2, 3, 4 or 5 ou^plus bas en réduisant le the contrast (valeurs en dessous de 100) agir sur le curseur chroma si nécessaire. e)possibilité d'agir sur "Etendue" pour réduire la zone d'action.\n\nVous pouvez aussi compléter avec "Flou niveaux" et Flouu Gaussien (Adoucir Flou et bruit) +TP_LOCALLAB_EXPCHROMA;Chroma compensation +TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors +TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE decay'\nIncrease 'Balance ΔE ab-L' +TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high decay transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. +TP_LOCALLAB_EXPCURV;Courbes +TP_LOCALLAB_EXPGRAD;Filtre gradué From 0d2b682e6cb204c61ba7e9b8467b5de19fbc92c0 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 3 Jul 2020 08:43:33 +0200 Subject: [PATCH 077/114] French 5 --- rtdata/languages/Francais | 65 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index d78442b40..82e08cb09 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1902,13 +1902,68 @@ TP_LOCALLAB_EXECLU;Spot Exclusion TP_LOCALLAB_EXNORM;Spot Normal TP_LOCALLAB_EXPCBDL_TOOLTIP;Dans le cas d'un capteur contaminé ("graisse"), et qaund la surface est importante ou pourune série de petits défauts.\n\na) Mettre la sélection du spot sur un défaut prononcé (adapter la taille si nécessaire), utilise run spot suffisament grand qui autorise les ondelettes; b) choisir une aire suffisament grande pour couvrir largement la zone affectée par les défauts; c) Choisir une valeur de transition (basse) et une transition affaiblissement (haute); d) agir sur les niveaux 2, 3, 4 or 5 ou^plus bas en réduisant le the contrast (valeurs en dessous de 100) agir sur le curseur chroma si nécessaire. e)possibilité d'agir sur "Etendue" pour réduire la zone d'action.\n\nVous pouvez aussi compléter avec "Flou niveaux" et Flouu Gaussien (Adoucir Flou et bruit) TP_LOCALLAB_EXPCHROMA;Chroma compensation -TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors -TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) -TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE decay'\nIncrease 'Balance ΔE ab-L' -TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. -TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high decay transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. +TP_LOCALLAB_EXPCHROMA_TOOLTIP;Seulement en association avec compensation d'exposition et PDE Ipol.\nEvite la desaturation des couleurs +TP_LOCALLAB_EXPCOLOR_TOOLTIP;Dans le cas de petits défauts.\n\nYeux-rouges : sélecteur centré sur la partie rouge, délimiteurs du spot près de l'oeil, adaptez Etendue, "luminosité" -100, "chrominance" -100.\n\nSpotIR :sélection Circulaire centrée sur le défaut, délimiteurs du spot proches du default - reduire "chrominance", possibilté d'agir sur Etendue pour réduire la zone de l'action.\n\nPoussières - graisse (petit) :Sélection circulaire centrée sur le défaut (adapter la taille du spot), délimiteurs du spot pas trop près du défaut pour permettre une transition quasi invisible. a) "Transition" (faibles valeurs) and "Transition affaiblissement" (hautes valeurs); b) agir sur "luminosité" et aussi sur "chrominance" ou sur "Grille de correction couleur - direct" de telle manière que le rendu de la zone polluée soit proche de celui de la zone saine; c) agir modérement sur "Etendue" pour moduler l'action.\n\nVous pouvez aussi compléter avec Flouter Gaussien (Adoucir Flouter et bruit) +TP_LOCALLAB_EXPCOMP_TOOLTIP;Pour les portraits et les images à faible gradient, vous pouvez changer "Détection de forme" dans "Réglages":\n\nAugmentez 'Seuil ΔE Etendue'\nRéduire 'ΔE affaiblissement'\nAugmenter 'Balance ΔE ab-L' +TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;Voir la documentation de ondelettes niveaux.\nCependant il y a des différences: plus d'outils et plus proches des détails .\nEx: Tone mapping pour ondelettes. +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Evitez les spots trop petitssmall(< 32x32 pixels).\nUtilisez de faibles valeurs de transition et de hautes valeurs de transition affaiblissement et d'Etendue pour simuler un petit RT-spot et s'adapter aux défauts.\nUtimiser si nécessaire le module 'Clarté & Maqsue netteté' et 'Fusion d'images' en ajustant 'Rayon adoucir' pour réduire les artéfacts. TP_LOCALLAB_EXPCURV;Courbes TP_LOCALLAB_EXPGRAD;Filtre gradué +TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balance l'action entre l'iamge originale image et la transformée de Laplace. +TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Applique un gamma avant et après la transformée de Laplace +TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Ajoute une exposition linéaire avant l'application de la transformée de Laplace +TP_LOCALLAB_EXPLAP_TOOLTIP;Plus vous agissez sur ce curseur de seuil, plus grande sera l'action de reduire le contraste. +TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Autorise de nombreuses possibilités de fusionner les images (comme les calques dans Photosshop) : difference, multiply, soft light, overlay...avec opacité...\nOriginale Image : fusionne le RT-spot en cours avec Originale.\nSpot Précédent : fusionne le RT-spot en cours avec le précédent - si il n'y a qu'un spot précédent = original.\nArrière plan : fusionne le RT-spot en cours avec la couleur et la luminance de l'arrière plan (moins de possibilités) +TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : utilise un algorithme similaire à Exposure principal mais en L*a*b* et en prenant en compte le deltaE.\n\nLaplacian & PDE : utilise un autre algorithme aussi avec deltaE et avec l'équation de Poisson pour résoudre le Laplacien dans l'espace de Fourier.\nPDE IPOL, PDE Fattal et Standard peuvent être combinés.\nFFTW La transformée de Fourier est optimisée en taille pour réduire les temps de traitement.\nPDE reduit les artéfacts et le bruit. +TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. +TP_LOCALLAB_EXPOSE;Exposition - PDE algorithmes +TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez utiliser le module "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPRETITOOLS;Outils Retinex avancés +TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUtiliser de basses valeurs de transition et de hautes valeurs de transition affaiblissement et Etendue pour simuler un petit RT-spot. +TP_LOCALLAB_EXPTOOL;Outils exposition +TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC +TP_LOCALLAB_EXP_TOOLNAME;Exposition - Compression Dynamique - 10 +TP_LOCALLAB_FATAMOUNT;Quantité +TP_LOCALLAB_FATANCHOR;Ancre +TP_LOCALLAB_FATANCHORA;Décalage +TP_LOCALLAB_FATDETAIL;Detail +TP_LOCALLAB_FATFRA;Compression Dynamique ƒ +TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal - utilise Fattal Tone mapping algorithme. +TP_LOCALLAB_FATLEVEL;Sigma +TP_LOCALLAB_FATRES;Quantité de Residual Image +TP_LOCALLAB_FATSHFRA;Compression Dynamique Masque ƒ +TP_LOCALLAB_FEATH_TOOLTIP;Largeur du Gradiant en porcentage de la diagonale du Spot\nUtilisé par tous les Filtres Gradués dans tous les outils.\nPas d'action si les filtres gradués ne sont pas utilisés. +TP_LOCALLAB_FEATVALUE;Adoucissement gradiant (Filtres Gradués) +TP_LOCALLAB_FFTCOL_MASK;FFTW ƒ +TP_LOCALLAB_FFTW;ƒ - Utilise Fast Fourier Transform +TP_LOCALLAB_FFTW2;ƒ - Utilise Fast Fourier Transform (TIF, JPG,..) +TP_LOCALLAB_FFTWBLUR;ƒ - Utilise toujours Fast Fourier Transform +TP_LOCALLAB_FULLIMAGE;Calcule les valeurs DarkEv - WhiteEv - sur l'image entière +TP_LOCALLAB_GAM;Gamma +TP_LOCALLAB_GAMFRA;Tone response curve (TRC) +TP_LOCALLAB_GAMM;Gamma +TP_LOCALLAB_GAMMASKCOL;Gamma masque +TP_LOCALLAB_GAMSH;Gamma +TP_LOCALLAB_GRADANG;Angle du Gradiant +TP_LOCALLAB_GRADANG_TOOLTIP;Angle de Rotation en degrés : -180 0 +180 +TP_LOCALLAB_GRADFRA;Filtre gradué Masque +TP_LOCALLAB_GRADGEN_TOOLTIP;Filtre Gradué est fourni avec Couleur et Lumière & Fusion fichier, Exposition & masque, Shadows Highlight, Vibrance, Encoding log.\n\nVibrance, Couleur et Lumière & Fusion fichier, sont fournis avec GF luminance, chrominance, teinte.\nAdoucissement est situé dans "réglages". +TP_LOCALLAB_GRADLOGFRA;Filtre Gradué Luminance +TP_LOCALLAB_GRADSTR;Force du Gradiant +TP_LOCALLAB_GRADSTRAB_TOOLTIP;Filtre chroma force +TP_LOCALLAB_GRADSTRCHRO;Force Gradiant Chrominance +TP_LOCALLAB_GRADSTRHUE;Force Gradiant Teinte +TP_LOCALLAB_GRADSTRHUE2;Force Gradiant Teinte +TP_LOCALLAB_GRADSTRHUE_TOOLTIP;Filttre Teinte force +TP_LOCALLAB_GRADSTRLUM;Force Gradiant Luminance +TP_LOCALLAB_GRADSTR_TOOLTIP;Force Filtre en Ev +TP_LOCALLAB_GRAINFRA;Film Grain 1:1 +TP_LOCALLAB_GRALWFRA;Filtre Gradué Local contraste +TP_LOCALLAB_GRIDFRAME_TOOLTIP;Vous pouvez utiliser cet outil comme une brosse. Utiliser un petit Spot et adaptez transition et transition affaiblissement\nSeulement en mode NORMAL et éventuellement Teinte, Saturation, Couleur, Luminosité sont concernés par Fusion arrire plan (ΔE) +TP_LOCALLAB_GRIDONE;Virage partiel +TP_LOCALLAB_GRIDTWO;Direct +TP_LOCALLAB_GUIDBL;Rayon adoucir +TP_LOCALLAB_GUIDFILTER;Rayon Filtre Guidé From d7c073e41c6e32904d142b5802c31c6a30da56df Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 3 Jul 2020 18:13:56 +0200 Subject: [PATCH 078/114] Localfrench - second pack of french datas - label - tooltips (#5829) * French six * French sept * french 8 * french 9 * french 9 --- rtdata/languages/Francais | 269 +++++++++++++++++++++++++++++++++++++- 1 file changed, 266 insertions(+), 3 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 82e08cb09..01a5bf317 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1830,7 +1830,7 @@ TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts TP_LOCALLAB_COLORDE;Coleurr previsualisation sélection ΔE - Intensité TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Previsualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Previsualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. -TP_LOCALLAB_COLORSCOPE;Outils Etendue Couleur +TP_LOCALLAB_COLORSCOPE;Etendue Outils Couleur TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. TP_LOCALLAB_COLOR_TOOLNAME;Couleur&Lumière (Défauts) - 11 TP_LOCALLAB_COL_NAME;Nom @@ -1906,7 +1906,7 @@ TP_LOCALLAB_EXPCHROMA_TOOLTIP;Seulement en association avec compensation d'expos TP_LOCALLAB_EXPCOLOR_TOOLTIP;Dans le cas de petits défauts.\n\nYeux-rouges : sélecteur centré sur la partie rouge, délimiteurs du spot près de l'oeil, adaptez Etendue, "luminosité" -100, "chrominance" -100.\n\nSpotIR :sélection Circulaire centrée sur le défaut, délimiteurs du spot proches du default - reduire "chrominance", possibilté d'agir sur Etendue pour réduire la zone de l'action.\n\nPoussières - graisse (petit) :Sélection circulaire centrée sur le défaut (adapter la taille du spot), délimiteurs du spot pas trop près du défaut pour permettre une transition quasi invisible. a) "Transition" (faibles valeurs) and "Transition affaiblissement" (hautes valeurs); b) agir sur "luminosité" et aussi sur "chrominance" ou sur "Grille de correction couleur - direct" de telle manière que le rendu de la zone polluée soit proche de celui de la zone saine; c) agir modérement sur "Etendue" pour moduler l'action.\n\nVous pouvez aussi compléter avec Flouter Gaussien (Adoucir Flouter et bruit) TP_LOCALLAB_EXPCOMP_TOOLTIP;Pour les portraits et les images à faible gradient, vous pouvez changer "Détection de forme" dans "Réglages":\n\nAugmentez 'Seuil ΔE Etendue'\nRéduire 'ΔE affaiblissement'\nAugmenter 'Balance ΔE ab-L' TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;Voir la documentation de ondelettes niveaux.\nCependant il y a des différences: plus d'outils et plus proches des détails .\nEx: Tone mapping pour ondelettes. -TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Evitez les spots trop petitssmall(< 32x32 pixels).\nUtilisez de faibles valeurs de transition et de hautes valeurs de transition affaiblissement et d'Etendue pour simuler un petit RT-spot et s'adapter aux défauts.\nUtimiser si nécessaire le module 'Clarté & Maqsue netteté' et 'Fusion d'images' en ajustant 'Rayon adoucir' pour réduire les artéfacts. +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Evitez les spots trop petits(< 32x32 pixels).\nUtilisez de faibles valeurs de transition et de hautes valeurs de transition affaiblissement et d'Etendue pour simuler un petit RT-spot et s'adapter aux défauts.\nUtimiser si nécessaire le module 'Clarté & Maqsue netteté' et 'Fusion d'images' en ajustant 'Rayon adoucir' pour réduire les artéfacts. TP_LOCALLAB_EXPCURV;Courbes TP_LOCALLAB_EXPGRAD;Filtre gradué TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balance l'action entre l'iamge originale image et la transformée de Laplace. @@ -1915,7 +1915,7 @@ TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Ajoute une exposition linéaire avant l'applicatio TP_LOCALLAB_EXPLAP_TOOLTIP;Plus vous agissez sur ce curseur de seuil, plus grande sera l'action de reduire le contraste. TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Autorise de nombreuses possibilités de fusionner les images (comme les calques dans Photosshop) : difference, multiply, soft light, overlay...avec opacité...\nOriginale Image : fusionne le RT-spot en cours avec Originale.\nSpot Précédent : fusionne le RT-spot en cours avec le précédent - si il n'y a qu'un spot précédent = original.\nArrière plan : fusionne le RT-spot en cours avec la couleur et la luminance de l'arrière plan (moins de possibilités) TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : utilise un algorithme similaire à Exposure principal mais en L*a*b* et en prenant en compte le deltaE.\n\nLaplacian & PDE : utilise un autre algorithme aussi avec deltaE et avec l'équation de Poisson pour résoudre le Laplacien dans l'espace de Fourier.\nPDE IPOL, PDE Fattal et Standard peuvent être combinés.\nFFTW La transformée de Fourier est optimisée en taille pour réduire les temps de traitement.\nPDE reduit les artéfacts et le bruit. -TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. +TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Applique un median avant la transformée de Laplace pour éviter les artéfacts (bruit).\nVous pouvez aussi utiliser l'outil "Réduction du bruit". TP_LOCALLAB_EXPOSE;Exposition - PDE algorithmes TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez utiliser le module "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... TP_LOCALLAB_EXPRETITOOLS;Outils Retinex avancés @@ -1964,6 +1964,269 @@ TP_LOCALLAB_GRIDONE;Virage partiel TP_LOCALLAB_GRIDTWO;Direct TP_LOCALLAB_GUIDBL;Rayon adoucir TP_LOCALLAB_GUIDFILTER;Rayon Filtre Guidé +TP_LOCALLAB_GUIDFILTER_TOOLTIP;Adapter cette valeur en fonction des images - peut réduire ou accroître les artéfacts. +TP_LOCALLAB_HHMASK_TOOLTIP;Ajustements fin de la teinte par exemple pour la peau. +TP_LOCALLAB_HIGHMASKCOL;Hautes lumières masque +TP_LOCALLAB_HLH;Courbes H +TP_LOCALLAB_IND;Independant (souris) +TP_LOCALLAB_INDSL;Independant (souris + curseurs) +TP_LOCALLAB_INVERS;Inverse +TP_LOCALLAB_INVERS_TOOLTIP;Si sélectionné (inverse) moins de possibilités.\n\nAlternative\nPremier Spot:\n image entière - delimiteurs en dehors de la prévisualisation\n RT-spot forme sélection : rectangle. Transition 100\n\nDeuxième spot : Spot Exclusion +TP_LOCALLAB_ISOGR;Plus gros (ISO) +TP_LOCALLAB_LABBLURM;Masque Flouter +TP_LOCALLAB_LABEL;Ajustements Locaux +TP_LOCALLAB_LABGRID;Grille correction couleurs +TP_LOCALLAB_LABGRIDMERG;Arrière plan +TP_LOCALLAB_LABGRID_VALUES;Haut(a)=%1 Haut(b)=%2\nBas(a)=%3 Bas(b)=%4 +TP_LOCALLAB_LABSTRUM;Masque Structure +TP_LOCALLAB_LAPLACC;ΔØ Masque Laplacien résoud PDE +TP_LOCALLAB_LAPLACE;Δ - Laplacien seuil ΔE +TP_LOCALLAB_LAPLACEXP;∆ - Laplacian seuil +TP_LOCALLAB_LAPMASKCOL;∆ - Laplacian seuil masque +TP_LOCALLAB_LAPRAD_TOOLTIP;Eviter d'utiliser Radius and Laplace Seuil en même temps.\nLaplacien seuil reduit le contraste, artéfacts, adoucit le résultat (si PDE réglages activé). +TP_LOCALLAB_LAP_MASK_TOOLTIP;Résoud PDE (Equation aux dérivées partielles) pour tous les masques Laplacien.\nSi activé Laplacien masque seuil reduit les artéfacts et adoucit les résultats.\nSi désactivé réponse linaire. +TP_LOCALLAB_LC_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface devant être traitée.\nA utiliser de préférences pour de grands rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\n +TP_LOCALLAB_LC_TOOLNAME;Constrast Local & Ondelettes (Défauts) - 7 +TP_LOCALLAB_LEVELBLUR;Maximum Flouter +TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;En abscisse le contraste local (proche du concept de luminance). En ordonnée, amplification ou reduction du contraste local. +TP_LOCALLAB_LEVELWAV;Ψ Ondelettes Niveaux +TP_LOCALLAB_LEVELWAV_TOOLTIP;Le niveau est automatiquement adapté à la taille du spot et de la prévisualisation.\nDu niveau 9 taille max 512 jusqu'au niveau 1 taille max = 4 +TP_LOCALLAB_LIGHTNESS;Luminosité +TP_LOCALLAB_LIGHTN_TOOLTIP;En mode inverse: selection = -100 force la luminance à zero +TP_LOCALLAB_LIGHTRETI;Luminosité +TP_LOCALLAB_LINEAR;Linéarité +TP_LOCALLAB_LIST_NAME;Ajoute un outil au spot courant... +TP_LOCALLAB_LIST_TOOLTIP;Choisir un outil et ensuite son niveau de complexité "Normal" ou "Expert".\nLe nombre traduit la place de l'outil dans le processus de chaque RT-Spot +TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Donne priorité à l'action sur les tons moyens et hautes lumières en choisissant les niveaux concernés d'ondelettes +TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights +TP_LOCALLAB_LOCCONT;Masque Flou +TP_LOCALLAB_LOC_CONTRAST;Contraste Local-Ondelettes-défauts +TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramide 1: +TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramide 2: +TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contraste par niveaux- Tone Mapping - Contraste Dir. +TP_LOCALLAB_LOC_CONTRASTPYRLAB; Filtre Gradué - Netteté bords - Flouter +TP_LOCALLAB_LOC_RESIDPYR;Image Residuelle +TP_LOCALLAB_LOG;Codage log +TP_LOCALLAB_LOGAUTO;Automatique +TP_LOCALLAB_LOGAUTO_TOOLTIP;Presser ce bouton va amner une évaluation an evaluation de l'amplitude dynamique et du point gris "source" (Si "Automatique" Source gris activé).\nPour être autorisé à retoucher les valeurs automatiques, presser le bouton à nouveau +TP_LOCALLAB_LOGBASE_TOOLTIP;Défaut = 2.\nValeurs inférieures à 2 réduisent l'action de l'algorithme, les ombres sont plus sombres, les hautes lumières plus brillantes.\nValeurs supérieures à 2 changent l'action de l'algorithme, les ombres sont plus grises, les hautes lumières lavées +TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;Valeurs estimées de la plage Dynamique - Noir Ev et Blanc Ev +TP_LOCALLAB_LOGENCOD_TOOLTIP;Autorise 'Tone Mapping' avec codage Logarithmique (ACES).\nUtile pour images ous-exposées, ou avec une plage dynamique élévée.\n\nDeux étapes dans le processus : 1) Calculer Plage Dynamique 2) Adaptation par utilisateur +TP_LOCALLAB_LOGFRA;Point gris source +TP_LOCALLAB_LOGFRAME_TOOLTIP;Calcule ou utilise le niveau d'Exposition de l'image tôt dans le processus:\n Noir Ev, Blanc Ev et Point gris source.\n Prend en compte la compensation d'exposition principale. +TP_LOCALLAB_LOGLIN;Logarithme mode +TP_LOCALLAB_LOGPFRA;Niveaux d'Exposition relatif +TP_LOCALLAB_LOGSRCGREY_TOOLTIP;Estime la valeur du point gris de l'image, tôt dans le processus +TP_LOCALLAB_LOGTARGGREY_TOOLTIP;Vous pouvez changer cette valeur pour l'adapter à votre goût. +TP_LOCALLAB_LOG_TOOLNAME;Codage log - 0 +TP_LOCALLAB_LUM;Courbes LL - CC +TP_LOCALLAB_LUMADARKEST;Plus Sombre +TP_LOCALLAB_LUMASK;Maqsue Luminance arrière plan +TP_LOCALLAB_LUMASK_TOOLTIP;Ajuste le gris de l'arrière plan du masque dans Montrer Masque (Masque et modifications) +TP_LOCALLAB_LUMAWHITESEST;Plus blanc +TP_LOCALLAB_LUMONLY;Luminance seulemnt +TP_LOCALLAB_MASKCOM;Masque couleur Commun +TP_LOCALLAB_MASKCOM_TOOLTIP;Ces masques travaillent comme les autres outils, ils prennet en compte Etendue.\nIls sont différents des autres masques qui complètent un outil (Couleur et Lumière, Exposition...) +TP_LOCALLAB_MASFRAME;Masque et Fusion +TP_LOCALLAB_MASFRAME_TOOLTIP;For all masks.\nTake into account deltaE image to avoid retouching the selection area when sliders gamma mask, slope mask, chroma mask and curves contrast , levels contrasts, and mask blur, structure(if enabled tool) are used.\nDisabled in Inverse +TP_LOCALLAB_MASK;Masque +TP_LOCALLAB_MASK2;Courbe de Contraste masque +TP_LOCALLAB_MASKCOL;Masque Courbes +TP_LOCALLAB_MASKH;Courbe teinte masque +TP_LOCALLAB_MASK_TOOLTIP;Vous pouvez activer plusieurs masques pour un simple outil, ceci nécessite d'activer un autre outil (mais sans utilser l'outil : curseurs à 0,...)où est le masque que vous souhaitez activer.\n\nVous pouvez aussi dupliquer le RT-spot et le placer juste à côté de l'autre,les variations de références autorisent un travail fin sur les images. +TP_LOCALLAB_MED;Medium +TP_LOCALLAB_MEDIAN;Median Bas +TP_LOCALLAB_MEDNONE;Rien +TP_LOCALLAB_MERCOL;Couleur +TP_LOCALLAB_MERDCOL;Fusion arrière plan (ΔE) +TP_LOCALLAB_MERELE;Eclaicit seulement +TP_LOCALLAB_MERFIV;Addition +TP_LOCALLAB_MERFOR;Couleur esquiver +TP_LOCALLAB_MERFOU;Multiplier +TP_LOCALLAB_MERGE1COLFRA;Fusion avec Original ou Précédent ou arrière plan +TP_LOCALLAB_MERGECOLFRA;Masque: LCH & Structure +TP_LOCALLAB_MERGEFIV;Previous Spot(Mask 7) + Mask LCH +TP_LOCALLAB_MERGEFOU;Previous Spot(Mask 7) +TP_LOCALLAB_MERGEMER_TOOLTIP;Prend en compte ΔE pour fusionner les fichiers (équivalent de Etendue pour cet usage) +TP_LOCALLAB_MERGENONE;Rien +TP_LOCALLAB_MERGEONE;Short Curves 'L' Mask +TP_LOCALLAB_MERGEOPA_TOOLTIP;Opacité fusion % Spot courant avec original ou Spot précédent.\nContraste seuil : ajuste le résulat en fonction du contraste original +TP_LOCALLAB_MERGETHR;Original(Mask 7) + Mask LCH +TP_LOCALLAB_MERGETWO;Original(Mask 7) +TP_LOCALLAB_MERGETYPE;Fusion image et masque +TP_LOCALLAB_MERGETYPE_TOOLTIP;Rien, use all mask in LCH mode.\nShort curves 'L' mask, use a short circuit for mask 2, 3, 4, 6, 7.\nOriginal mask 8, blend current image with original +TP_LOCALLAB_MERHEI;Overlay +TP_LOCALLAB_MERHUE;Teite +TP_LOCALLAB_MERLUCOL;Luminance +TP_LOCALLAB_MERLUM;Luminosité +TP_LOCALLAB_MERNIN;Ecran +TP_LOCALLAB_MERONE;Normal +TP_LOCALLAB_MERSAT;Saturation +TP_LOCALLAB_MERSEV;Soft Light Photshop +TP_LOCALLAB_MERSEV0;Soft Light Illusion +TP_LOCALLAB_MERSEV1;Soft Light W3C +TP_LOCALLAB_MERSEV2;Lumière dure +TP_LOCALLAB_MERSIX;Divise +TP_LOCALLAB_MERTEN;Assombrit seulement +TP_LOCALLAB_MERTHI;Couleur Brûlé +TP_LOCALLAB_MERTHR;Difference +TP_LOCALLAB_MERTWE;Exclusion +TP_LOCALLAB_MERTWO;Soustrait +TP_LOCALLAB_METHOD_TOOLTIP;'Enhanced + chroma denoise' significantly increases processing times.\nBut reduce artifacts. +TP_LOCALLAB_MLABEL;Récupère les données Min=%1 Max=%2 (Clip - décalage) +TP_LOCALLAB_MLABEL_TOOLTIP;'Doit être' près de min=0 max=32768 (log mode) mais d'autres valeurs sont possibles.\nVous pouvez agir sur les données récupérées (CLIP) et décalage pour normaliser.\n\nRécupère les données image sans mélange. +TP_LOCALLAB_MODE_EXPERT;Expert +TP_LOCALLAB_MODE_NORMAL;Normal +TP_LOCALLAB_MRFIV;Arrière plan +TP_LOCALLAB_MRFOU;Spot précédent +TP_LOCALLAB_MRONE;Rien +TP_LOCALLAB_MRTHR;Image Originale +TP_LOCALLAB_MRTWO;Short Curves 'L' Mask +TP_LOCALLAB_MULTIPL_TOOLTIP;Autorise la retouche des tons sur une large plage : -18EV +4EV. Le remier curseur agit sur -18EV and -6EV. Le dernier curseur agit sur les tons au-dessus de 4EV +TP_LOCALLAB_NEIGH;Rayon +TP_LOCALLAB_NOISECHROCOARSE;Chroma gros (Ond) +TP_LOCALLAB_NOISECHROC_TOOLTIP;Si supérieur à zéro, algorithme haute qualité est activé.\nGros est sélectionné si curseur >=0.2 +TP_LOCALLAB_NOISECHRODETAIL;Récupération des détails Chroma (DCT ƒ) +TP_LOCALLAB_NOISECHROFINE;Chroma fin (Ond) +TP_LOCALLAB_NOISEDETAIL_TOOLTIP;Désactivé si curseur = 100 +TP_LOCALLAB_NOISELEQUAL;Egalisateurs balnc-noir +TP_LOCALLAB_NOISELUMCOARSE;Luminance gros (ond) +TP_LOCALLAB_NOISELUMDETAIL;Récupération Luminance fin(DCT ƒ) +TP_LOCALLAB_NOISELUMFINE;Luminance fin 1 (ond) +TP_LOCALLAB_NOISELUMFINETWO;Luminance fin 2 (ond) +TP_LOCALLAB_NOISELUMFINEZERO;Luminance fin 0 (ond) +TP_LOCALLAB_NOISEMETH;Réduction du bruit +TP_LOCALLAB_NONENOISE;Rien +TP_LOCALLAB_OFFS;Décalage +TP_LOCALLAB_OFFSETWAV;Décalage +TP_LOCALLAB_OPACOL;Opacité +TP_LOCALLAB_ORIGLC;Fusion seulement avec image originale +TP_LOCALLAB_ORRETILAP_TOOLTIP;Agit sur un deuxième seuil Laplacien, pour prendre en compte ΔE pour différencier l'action nottament avec l'arrière plan (différent de Etendue) +TP_LOCALLAB_ORRETISTREN_TOOLTIP;Aagit sur un seuil Laplacien, plus grande est l'action, plus les différences de contraste seront réduites +TP_LOCALLAB_PASTELS2;Vibrance +TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard +TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ +TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n +TP_LOCALLAB_PREVIEW;Previsualisation ΔE +TP_LOCALLAB_PROXI;ΔE Affaiblissement +TP_LOCALLAB_QUALCURV_METHOD;Types de Courbes +TP_LOCALLAB_QUAL_METHOD;Qualité globale +TP_LOCALLAB_RADIUS;Rayon +TP_LOCALLAB_RADIUS_TOOLTIP;Above Radius 30 Use Fast Fourier Transform +TP_LOCALLAB_RADMASKCOL;Smooth Radius Mask +TP_LOCALLAB_RECT;Rectangle +TP_LOCALLAB_RECURS;Réferences Récursives +TP_LOCALLAB_RECURS_TOOLTIP;Recalcule les références pour teinte, luma, chroma après chaque module et après chaque RT-spot.\nAussi utile pour le travail avec les masques. +TP_LOCALLAB_REFLABEL;Ref. (0..1) Chroma=%1 Luma=%2 teinte=%3 +TP_LOCALLAB_REN_DIALOG_LAB;Entrer le nouveau nom de Spot +TP_LOCALLAB_REN_DIALOG_NAME;Renomme le Controle Spot +TP_LOCALLAB_RESETSHOW;Annuler Montrer Toutes les Modifications +TP_LOCALLAB_RESID;Image Résiduelle +TP_LOCALLAB_RESIDBLUR;Flouter Image Résiduelle +TP_LOCALLAB_RESIDCHRO;Image Résiduelle Chroma +TP_LOCALLAB_RESIDCOMP;Image Résiduelle Compression +TP_LOCALLAB_RESIDCONT;Image Résiduelle Contraste +TP_LOCALLAB_RESIDHI;Hautes lumières +TP_LOCALLAB_RESIDHITHR;Hautes lumières seuil +TP_LOCALLAB_RESIDSHA;Ombres +TP_LOCALLAB_RESIDSHATHR;Ombres seuil +TP_LOCALLAB_RETI;De-brume - Retinex Fort contraste +TP_LOCALLAB_RETIFRA;Retinex +TP_LOCALLAB_RETIM;Original Retinex +TP_LOCALLAB_RETITOOLFRA;Retinex Outils +TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface traitée\nLe temps de traitements dépend de "scale" (échelle) (soyez prudent avec les hautes valeurs ).\nA utiliser de préférence avec de grand rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\nOptimisation pas utilsée en prévisualisation +TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast. +TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible. +TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo. +TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP;Adapt these values according to images - if misty images and depending on whether you want to act on the front or the background +TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times +TP_LOCALLAB_RET_TOOLNAME;De-brume & Retinex - 9 +TP_LOCALLAB_REWEI;Repondération iterations +TP_LOCALLAB_RGB;RGB Courbe de tonalité +TP_LOCALLAB_ROW_NVIS;Pas visible +TP_LOCALLAB_ROW_VIS;Visible +TP_LOCALLAB_SATUR;Saturation +TP_LOCALLAB_SAVREST;Sauve - Récupère Image Courante +TP_LOCALLAB_SCALEGR;Echelle +TP_LOCALLAB_SCALERETI;Echelle +TP_LOCALLAB_SCALTM;Echelle +TP_LOCALLAB_SCOPEMASK;Etendue Masque ΔE Image +TP_LOCALLAB_SCOPEMASK_TOOLTIP;Actif si Masque DeltaE Image est activé.\nLes faibles valeurs évitent de retoucher l'aire sélectionnée +TP_LOCALLAB_SENSI;Etendue +TP_LOCALLAB_SENSIBN;Etendue +TP_LOCALLAB_SENSICB;Etendue +TP_LOCALLAB_SENSIDEN;Etendue +TP_LOCALLAB_SENSIEXCLU;Etendue +TP_LOCALLAB_SENSIEXCLU_TOOLTIP;Ajuste les couleurs pour les inclure dans exclusion! +TP_LOCALLAB_SENSIH;Etendue +TP_LOCALLAB_SENSIH_TOOLTIP;Ajuste Etendue de l'action:\nLes petites valeurs limitent l'action aux couleurs très similaires à celles sous le centre du spot.\nHautes valeurs laissent l'outil agir sur une large plage de couleurs. +TP_LOCALLAB_SENSILOG;Etendue +TP_LOCALLAB_SENSIS;Etendue +TP_LOCALLAB_SENSIS_TOOLTIP;Ajuste Etendue de l'action:\nLes petites valeurs limitent l'action aux couleurs très similaires à celles sous le centre du spot.\nHautes valeurs laissent l'outil agir sur une large plage de couleurs.\nValeurs inférieures à 20 conduisent à un meilleur algorithme. +TP_LOCALLAB_SENSI_TOOLTIP;Ajuste Etendue de l'action:\nLes petites valeurs limitent l'action aux couleurs très similaires à celles sous le centre du spot.\nHautes valeurs laissent l'outil agir sur une large plage de couleurs.\nValeurs inférieures à 20 conduisent à un meilleur algorithme. +TP_LOCALLAB_SENSIMASK_TOOLTIP;Ajuste Etendue pour ce masque commun.\nAgit sur l'écart entre l'image originale et le masque.\nLes références (luma, chroma, teinte) sont celles du centre du RT-spot\n\nVous pouvez aussi agir sur le deltaE interne au masque avec 'Etendue Masque deltaE image' dans 'Réglages' +TP_LOCALLAB_SETTINGS;Réglages +TP_LOCALLAB_SH1;Ombres Lumières +TP_LOCALLAB_SH2;Egaliseur +TP_LOCALLAB_SHADEX;Ombres +TP_LOCALLAB_SHADEXCOMP;Compression ombres & profondeur tonale +TP_LOCALLAB_SHADHIGH;OmbresLumières - Egaliseur tonal +TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP;Peut être utilisé - ou en complement - du module Exposition dans les cas difficiles.\nUtiliser réduction du bruit Denoise peut être nécessaire : éclaicir les ombres.\n\nPeut être utilisé comme un filtre gradué (augmenter Etendue) +TP_LOCALLAB_SHAMASKCOL;masque ombres +TP_LOCALLAB_SHAPETYPE;Forme aire RT-spot +TP_LOCALLAB_SHAPE_TOOLTIP;Ellipse est le mode normal.\nRectangle peut être utilé dans certains cas, par exemple pour travailler en image complète en conjonction avec les délimiteurs en dehors de la prévisualisation, transition = 100.\n\nPolygone - Beziers sont en attente de GUI... +TP_LOCALLAB_SHARAMOUNT;Quantité +TP_LOCALLAB_SHARBLUR;Rayon flouter +TP_LOCALLAB_SHARDAMPING;Amortissement +TP_LOCALLAB_SHARFRAME;Modifications +TP_LOCALLAB_SHARITER;Iterations +TP_LOCALLAB_SHARP;Netteté +TP_LOCALLAB_SHARP_TOOLNAME;Netteté - 8 +TP_LOCALLAB_SHARRADIUS;Rayon +TP_LOCALLAB_SHORTC;Short Curves 'L' Mask +TP_LOCALLAB_SHORTCMASK_TOOLTIP;Short circuit the 2 curves L(L) and L(H).\nAllows you to mix the current image with the original image modified by the mask job.\nUsable with masks 2, 3, 4, 6, 7 +TP_LOCALLAB_SHOWC;Masque et modifications +TP_LOCALLAB_SHOWC1;Fusion fichier +TP_LOCALLAB_SHOWCB;Masque et modifications +TP_LOCALLAB_SHOWDCT;Montrer processus Fourier ƒ +TP_LOCALLAB_SHOWE;Masque et modifications +TP_LOCALLAB_SHOWFOURIER;Fourier ƒ(dct) +TP_LOCALLAB_SHOWLAPLACE;∆ Laplacien (premier) +TP_LOCALLAB_SHOWLC;Masque et modifications +TP_LOCALLAB_SHOWMASK;Montrer masque +TP_LOCALLAB_SHOWMASKCOL_TOOLTIP;Affiche masque modifications.\nAttention, vous ne pouvez voir qu'un seul masque à la fois.\n\nNote: Utilisation du Masque est avant l'algorihtme de détection de forme. +TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP;Montre le processus Fourier:\nMontre les différentes étapes du processus.\nLaplace - construit la dérivée seconde the second dérivée associée au seuil (Premiére étape).\nFourier -montre la transformée de Laplace avec DCT.\nPoisson - montre la solution de Poisson DCE.\nNormalise - montre le résultat sans normalisation de la luminance. +TP_LOCALLAB_SHOWMASKTYP1;Flouter & Bruit +TP_LOCALLAB_SHOWMASKTYP2;Réduction du bruit +TP_LOCALLAB_SHOWMASKTYP3;Flouter & Bruit + De-bruite +TP_LOCALLAB_SHOWMASKTYP_TOOLTIP;Masque et modifications peuvent être choisis.\nFlouter et bruit: dans ce cas il n'est pas utilisé pour 'Réduction du bruit'.\nRéduction du bruit : dans ce cas il n'est pas utilisé pour 'flouter et bruit'.\n\nFlouter et bruit + Réduction du bruit : le masque est partagé, faire attention à 'montrer modifications' et 'Etendue' +TP_LOCALLAB_SHOWMNONE;Rien +TP_LOCALLAB_SHOWMODIF;Montrer modifications sans masque +TP_LOCALLAB_SHOWMODIFMASK;Montrer modifications avec masque +TP_LOCALLAB_SHOWNORMAL;Normalise luminance (non) +TP_LOCALLAB_SHOWPLUS;Masque et modifications - Adoucir-flouter & De-bruite +TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ) +TP_LOCALLAB_SHOWR;Masque et modifications +TP_LOCALLAB_SHOWREF;Prévisualisation ΔE +TP_LOCALLAB_SHOWS;Masque et modifications +TP_LOCALLAB_SHOWSTRUC;Montrer Spot structure +TP_LOCALLAB_SHOWSTRUCEX;Montrer Spot structure +TP_LOCALLAB_SHOWT;Masque et modifications +TP_LOCALLAB_SHOWVI;Masque et modifications +TP_LOCALLAB_SHRESFRA;Ombres/Lumières +TP_LOCALLAB_SHTRC_TOOLTIP;Modifie les tons de l'image en agissant sur la TRC (Tone Response Curve).\nGamma agit principalement sur les tons lumineux.\nSlope (pente) agit principalement sur les tons sombres. +TP_LOCALLAB_SH_TOOLNAME;Ombres-lumières & Egaliser tonal - 5 +TP_LOCALLAB_SIGMAWAV;Attenuation Réponse +TP_LOCALLAB_SIM;Simple +TP_LOCALLAB_SLOMASKCOL;Pente (slope) masque +TP_LOCALLAB_SLOSH;Pente +TP_LOCALLAB_SOFT;Lumière douce - Original Retinex +TP_LOCALLAB_SOFTM;Lumière douce (soft light) +TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres méthodes Retinex.\nIl agit sur les gris et équilibre la luminance.\nC'est une émulation de "Dodge" and "Burn" +TP_LOCALLAB_SOFTRADIUSCOL;Radius adoucir +TP_LOCALLAB_SOFTRETI;Reduire artefact ΔE +TP_LOCALLAB_SOFTRETI_TOOLTIP;Prend en compte ΔE pour améliorer Transmission map +TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 From 736e67b7bb84b45d300baadc3d5258c1928e63c0 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 4 Jul 2020 10:00:16 +0200 Subject: [PATCH 079/114] french A --- rtdata/languages/Francais | 106 ++++++++++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 01a5bf317..ef281b97b 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1827,8 +1827,8 @@ TP_LOCALLAB_CLARITYML;Claté TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' TP_LOCALLAB_CLIPTM;Clip Recupère données (gain) TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts -TP_LOCALLAB_COLORDE;Coleurr previsualisation sélection ΔE - Intensité -TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Previsualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Previsualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE +TP_LOCALLAB_COLORDE;Couleur prévisualisation sélection ΔE - Intensité +TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Prévisualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Prévisualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. TP_LOCALLAB_COLORSCOPE;Etendue Outils Couleur TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. @@ -2107,13 +2107,13 @@ TP_LOCALLAB_PASTELS2;Vibrance TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n -TP_LOCALLAB_PREVIEW;Previsualisation ΔE +TP_LOCALLAB_PREVIEW;Prévisualisation ΔE TP_LOCALLAB_PROXI;ΔE Affaiblissement TP_LOCALLAB_QUALCURV_METHOD;Types de Courbes TP_LOCALLAB_QUAL_METHOD;Qualité globale TP_LOCALLAB_RADIUS;Rayon TP_LOCALLAB_RADIUS_TOOLTIP;Above Radius 30 Use Fast Fourier Transform -TP_LOCALLAB_RADMASKCOL;Smooth Radius Mask +TP_LOCALLAB_RADMASKCOL;Rayon adoucir Masque TP_LOCALLAB_RECT;Rectangle TP_LOCALLAB_RECURS;Réferences Récursives TP_LOCALLAB_RECURS_TOOLTIP;Recalcule les références pour teinte, luma, chroma après chaque module et après chaque RT-spot.\nAussi utile pour le travail avec les masques. @@ -2216,20 +2216,108 @@ TP_LOCALLAB_SHOWVI;Masque et modifications TP_LOCALLAB_SHRESFRA;Ombres/Lumières TP_LOCALLAB_SHTRC_TOOLTIP;Modifie les tons de l'image en agissant sur la TRC (Tone Response Curve).\nGamma agit principalement sur les tons lumineux.\nSlope (pente) agit principalement sur les tons sombres. TP_LOCALLAB_SH_TOOLNAME;Ombres-lumières & Egaliser tonal - 5 -TP_LOCALLAB_SIGMAWAV;Attenuation Réponse +TP_LOCALLAB_SIGMAWAV;Atténuation Réponse TP_LOCALLAB_SIM;Simple TP_LOCALLAB_SLOMASKCOL;Pente (slope) masque TP_LOCALLAB_SLOSH;Pente TP_LOCALLAB_SOFT;Lumière douce - Original Retinex TP_LOCALLAB_SOFTM;Lumière douce (soft light) TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres méthodes Retinex.\nIl agit sur les gris et équilibre la luminance.\nC'est une émulation de "Dodge" and "Burn" -TP_LOCALLAB_SOFTRADIUSCOL;Radius adoucir +TP_LOCALLAB_SOFTRADIUSCOL;Rayon adoucir TP_LOCALLAB_SOFTRETI;Reduire artefact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Prend en compte ΔE pour améliorer Transmission map TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 - - - +TP_LOCALLAB_SOURCE_GRAY;Valeur +TP_LOCALLAB_SPECCASE; Cas spécifiques +TP_LOCALLAB_SPECIAL;Usage Special des courbes RGB +TP_LOCALLAB_SPECIAL_TOOLTIP;Seulement pour cette courbe RGB, désactive (ou réduit les effecs) de Etendue, masque...par exemple, si vous voulez rendre un effet "négatif". +TP_LOCALLAB_SPOTNAME;Nouveau Spot +TP_LOCALLAB_STD;Standard +TP_LOCALLAB_STR;Force +TP_LOCALLAB_STRBL;Force +TP_LOCALLAB_STREN;Compression Force +TP_LOCALLAB_STRENG;Force +TP_LOCALLAB_STRENGR;Force +TP_LOCALLAB_STRENGTH;Bruit +TP_LOCALLAB_STRGRID;Force +TP_LOCALLAB_STRRETI_TOOLTIP;Si force Retinex < 0.2 seul Dehaze est activé.\nSi force Retinex >= 0.1 Dehaze est en mode luminance. +TP_LOCALLAB_STRUC;Structure +TP_LOCALLAB_STRUCCOL;Structure +TP_LOCALLAB_STRUCCOL1;Spot Structure +TP_LOCALLAB_STRUCT_TOOLTIP;Utilise l'algorithme de Sobel pour prendre en compte la structure dans la détection de forme.\nvous pouvez prévisualiser avec "masque et modifications - Montrer structure spot".\n\nPeut être utilisé avec masques (expert) structure, flouter, ondelettes pour améliorer la détection de bords.\n\nA besoin de réglages sans-masque pour êtrre activé (luminosité, exposition...) +TP_LOCALLAB_STRUMASKCOL;Structure masque force +TP_LOCALLAB_STRUMASK_TOOLTIP;Génère un masque structure qui va différencier les aplats et reliefs.\nSi structure masque comme outil est activé, ce masque est untilisé en plus des autres outils (gamma, slope, courbe contraste ...) +TP_LOCALLAB_STYPE;Forme méthode +TP_LOCALLAB_STYPE_TOOLTIP;Vous pouvez choisir entre:\nSymétrique - gauche et droite sont liés, haut et bas sont liés.\nIndépendent - toutes les saisies sont indépendantes. +TP_LOCALLAB_SYM;Symétrique (souris) +TP_LOCALLAB_SYMSL;Symétrique (souris + curseurs) +TP_LOCALLAB_TARGET_GRAY;Point Gris Cible +TP_LOCALLAB_THRES;Seuil structure +TP_LOCALLAB_THRESDELTAE;Seuil ΔE-Etendue +TP_LOCALLAB_THRESRETI;Seuil +TP_LOCALLAB_THRESWAV;Balance Seuil +TP_LOCALLAB_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 (Seuil) +TP_LOCALLAB_TLABEL2;TM Effectif Tm=%1 TM=%2 +TP_LOCALLAB_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.\nYou can act on Threshold to normalize +TP_LOCALLAB_TM;Compression tonale - Texture +TP_LOCALLAB_TM_MASK;Utilise transmission map +TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;Ce paramètre affecte la sensibilité aux bords.\n Plus grand il est, plus la luminosité change peut être considéré comme un bord.\n Si réglé à zéro 'compression tonale' va avoir un effet similaire à masque flou. +TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma déplace l'action de 'compression tonale' des ombres vers les lumières. +TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;Dans certains 'compression tonal' peut amener des effets de perspective, et dans de rares cas des halos peuvent apparaître.\n Accroître le nombre d'itérations peut aider à résoudre ces problèmes. +TP_LOCALLAB_TONEMAP_TOOLTIP;Compression tonal - menu principal doit être désactivé +TP_LOCALLAB_TONEMASCALE_TOOLTIP;Ce contrôle donne le pouvoir de différencier le contraste "local" et "global".\nPlus il est important, plus un détail sera accentué. +TP_LOCALLAB_TONE_TOOLNAME;Compression tonale - 4 +TP_LOCALLAB_TOOLCOL;Masque Structure comme outil +TP_LOCALLAB_TOOLMASK;Outils +TP_LOCALLAB_TRANSIT;Transition Gradiant +TP_LOCALLAB_TRANSITGRAD;Transition différentiation XY +TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Change la transition des abscisses vers les ordonnées +TP_LOCALLAB_TRANSITVALUE;Transition valeur +TP_LOCALLAB_TRANSITWEAK;Transition affaiblissement (linéaire-log) +TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Ajuste l'affaiblissement de la transition : change le processus d'affaiblissement - 1 linéaire - 2 parabolique - 3 cubique - ^25.\nPeut être utilisé en conjonction avec de très faibles valeurs de transition pour traiter/réduire les défauts (CBDL, Ondelettes, Couleur et lumière) +TP_LOCALLAB_TRANSIT_TOOLTIP;Ajuste la progressions de la transition enttre les zones affectées ou non affectées, comme un pourcentage du "rayon" +TP_LOCALLAB_TRANSMISSIONGAIN;Transmission gain +TP_LOCALLAB_TRANSMISSIONMAP;Transmission map +TP_LOCALLAB_TRANSMISSION_TOOLTIP;Transmission en accord à transmission.\nAbscisse: transmission pour les valeurs négatives (min), mean, et les valeurs positives (max).\nOrdonnée: amplification ou réduction.\nVous pouvez agir sur cette courbe pour chnager la Transmission et réduire les artefacts +TP_LOCALLAB_USEMASK;Utiliser masque +TP_LOCALLAB_VART;Variance (contraste) +TP_LOCALLAB_VIBRANCE;Vibrance - Chaud & Froid +TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Chaud & Froid - 3 +TP_LOCALLAB_SOFT_TOOLNAME;Lumière douce & Original Retinex - 6 +TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Flouter Grain & De-bruite - 1 +TP_LOCALLAB_TONE_TOOLNAME;Compression tonale - 4 +TP_LOCALLAB_RET_TOOLNAME;De-brume & Retinex - 9 +TP_LOCALLAB_SHARP_TOOLNAME;Netteté - 8 +TP_LOCALLAB_LC_TOOLNAME;Constraste local & Ondelettes (Défauts) - 7 +TP_LOCALLAB_CBDL_TOOLNAME;Contraste par Niveau détail (Défauts) - 2 +TP_LOCALLAB_LOG_TOOLNAME;Codage log - 0 +TP_LOCALLAB_MASKCOM_TOOLNAME;Masque Commun Couleur - 13 +TP_LOCALLAB_VIS_TOOLTIP;Click pour montrer/cacher le Spot sélectionné.\nCtrl+click pour montrer/cacher tous les Spot. +TP_LOCALLAB_WAMASKCOL;Ψ Masque Niveau Ondelettes +TP_LOCALLAB_WARM;Chaud - Froid & Artefacts de couleur +TP_LOCALLAB_WARM_TOOLTIP;Ce curseur utilise l'algorithme Ciecam et agit comme une Balance des blancs, il prut réchauffer ou refroidir cool la zone concernée.\nIl peut aussi dans certains réduire les artefacts colorés. +TP_LOCALLAB_WASDEN_TOOLTIP;De-bruite luminance pour les 3 premiers niveaux (fin).\nLa limite droite de la courbe correspond à gros : niveau 3 et au delà. +TP_LOCALLAB_WAV;Contrast local niveau +TP_LOCALLAB_WAVBLUR_TOOLTIP;Réalise un flou pour chaque niveau de décomposition, également pour l'image résiduelle. +TP_LOCALLAB_WAVCOMP;Compression par niveau +TP_LOCALLAB_WAVCOMPRE;(de)Compression par niveau +TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Réalise un 'Tone-mapping' ou une réduction du contraste local par niveau.\nEn abscisse: niveaux +TP_LOCALLAB_WAVCOMP_TOOLTIP;Réalise un contrast local en fonction de la direction de la décomposition en ondelettes : horizontal, vertical, diagonal +TP_LOCALLAB_WAVCON;Contraste par niveau +TP_LOCALLAB_WAVCONTF_TOOLTIP;Similaire à Contrast By Detail Levels : en abscisse niveaux. +TP_LOCALLAB_WAVDEN;de-bruite luminance par niveau (0 1 2 + 3 et plus) +TP_LOCALLAB_WAVE;Ψ Ondelette +TP_LOCALLAB_WAVEDG;Contrast Local +TP_LOCALLAB_WAVEEDG_TOOLTIP;Améliore la netteté prenant en compte la notion de "ondelettes bords".\nNécessite au moins que les 4 premiers niveaux sont utilisables +TP_LOCALLAB_WAVGRAD_TOOLTIP;Filtre gradué pour Contraste local "luminance" +TP_LOCALLAB_WAVHIGH;Ψ Ondelette haut +TP_LOCALLAB_WAVLEV;Flouter par niveau +TP_LOCALLAB_WAVLOW;Ψ Ondelette bas +TP_LOCALLAB_WAVMASK;Ψ Masque Niveau contraste local +TP_LOCALLAB_WAVMASK_TOOLTIP;Autorise un travail fin sur les masques niveaux de contraste (structure) +TP_LOCALLAB_WAVMED;Ψ Ondelette normal +TP_LOCALLAB_WEDIANHI;Median Haut +TP_LOCALLAB_WHITE_EV;Blanc Ev TP_METADATA_EDIT;Appliquer les modifications TP_METADATA_MODE;Mode de copie des métadonnées TP_METADATA_STRIP;Retirer toutes les métadonnées From b251b7318b208b050b693e55aa27ca2ac598df4b Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 4 Jul 2020 10:13:42 +0200 Subject: [PATCH 080/114] French corrections --- rtdata/languages/Francais | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index ef281b97b..abb657063 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1799,11 +1799,11 @@ TP_LOCALLAB_BUTTON_DEL;Effacer TP_LOCALLAB_BUTTON_DUPL;Dupliquer TP_LOCALLAB_BUTTON_REN;Renommer TP_LOCALLAB_BUTTON_VIS;Montrer/Cacher -TP_LOCALLAB_CBDL;Contraste par niveaux de détail - Défauts +TP_LOCALLAB_CBDL;Contraste par niveaux détail-Défauts TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Empêche d'augmenter le bruit -TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defauts) - 2 +TP_LOCALLAB_CBDL_TOOLNAME;Contraste par niveaux de détails(Défauts) - 2 TP_LOCALLAB_CENTER_X;Centre X TP_LOCALLAB_CENTER_Y;Centre Y TP_LOCALLAB_CH;Courbes CL - LC @@ -2131,7 +2131,7 @@ TP_LOCALLAB_RESIDHITHR;Hautes lumières seuil TP_LOCALLAB_RESIDSHA;Ombres TP_LOCALLAB_RESIDSHATHR;Ombres seuil TP_LOCALLAB_RETI;De-brume - Retinex Fort contraste -TP_LOCALLAB_RETIFRA;Retinex +TP_LOCALLAB_RETIFRA;Retinexfr TP_LOCALLAB_RETIM;Original Retinex TP_LOCALLAB_RETITOOLFRA;Retinex Outils TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface traitée\nLe temps de traitements dépend de "scale" (échelle) (soyez prudent avec les hautes valeurs ).\nA utiliser de préférence avec de grand rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\nOptimisation pas utilsée en prévisualisation @@ -2226,7 +2226,7 @@ TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres TP_LOCALLAB_SOFTRADIUSCOL;Rayon adoucir TP_LOCALLAB_SOFTRETI;Reduire artefact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Prend en compte ΔE pour améliorer Transmission map -TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 +TP_LOCALLAB_SOFT_TOOLNAME;Lumière douce & Original Retinex - 6 TP_LOCALLAB_SOURCE_GRAY;Valeur TP_LOCALLAB_SPECCASE; Cas spécifiques TP_LOCALLAB_SPECIAL;Usage Special des courbes RGB From ee7bf149ffd56e93caa1c172b8669bd7a6e7caf2 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 4 Jul 2020 10:38:07 +0200 Subject: [PATCH 081/114] Local french Third package datas (#5830) * french A * French corrections --- rtdata/languages/Francais | 114 +++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 01a5bf317..abb657063 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1799,11 +1799,11 @@ TP_LOCALLAB_BUTTON_DEL;Effacer TP_LOCALLAB_BUTTON_DUPL;Dupliquer TP_LOCALLAB_BUTTON_REN;Renommer TP_LOCALLAB_BUTTON_VIS;Montrer/Cacher -TP_LOCALLAB_CBDL;Contraste par niveaux de détail - Défauts +TP_LOCALLAB_CBDL;Contraste par niveaux détail-Défauts TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Empêche d'augmenter le bruit -TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defauts) - 2 +TP_LOCALLAB_CBDL_TOOLNAME;Contraste par niveaux de détails(Défauts) - 2 TP_LOCALLAB_CENTER_X;Centre X TP_LOCALLAB_CENTER_Y;Centre Y TP_LOCALLAB_CH;Courbes CL - LC @@ -1827,8 +1827,8 @@ TP_LOCALLAB_CLARITYML;Claté TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' TP_LOCALLAB_CLIPTM;Clip Recupère données (gain) TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts -TP_LOCALLAB_COLORDE;Coleurr previsualisation sélection ΔE - Intensité -TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Previsualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Previsualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE +TP_LOCALLAB_COLORDE;Couleur prévisualisation sélection ΔE - Intensité +TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Prévisualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Prévisualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. TP_LOCALLAB_COLORSCOPE;Etendue Outils Couleur TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. @@ -2107,13 +2107,13 @@ TP_LOCALLAB_PASTELS2;Vibrance TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n -TP_LOCALLAB_PREVIEW;Previsualisation ΔE +TP_LOCALLAB_PREVIEW;Prévisualisation ΔE TP_LOCALLAB_PROXI;ΔE Affaiblissement TP_LOCALLAB_QUALCURV_METHOD;Types de Courbes TP_LOCALLAB_QUAL_METHOD;Qualité globale TP_LOCALLAB_RADIUS;Rayon TP_LOCALLAB_RADIUS_TOOLTIP;Above Radius 30 Use Fast Fourier Transform -TP_LOCALLAB_RADMASKCOL;Smooth Radius Mask +TP_LOCALLAB_RADMASKCOL;Rayon adoucir Masque TP_LOCALLAB_RECT;Rectangle TP_LOCALLAB_RECURS;Réferences Récursives TP_LOCALLAB_RECURS_TOOLTIP;Recalcule les références pour teinte, luma, chroma après chaque module et après chaque RT-spot.\nAussi utile pour le travail avec les masques. @@ -2131,7 +2131,7 @@ TP_LOCALLAB_RESIDHITHR;Hautes lumières seuil TP_LOCALLAB_RESIDSHA;Ombres TP_LOCALLAB_RESIDSHATHR;Ombres seuil TP_LOCALLAB_RETI;De-brume - Retinex Fort contraste -TP_LOCALLAB_RETIFRA;Retinex +TP_LOCALLAB_RETIFRA;Retinexfr TP_LOCALLAB_RETIM;Original Retinex TP_LOCALLAB_RETITOOLFRA;Retinex Outils TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface traitée\nLe temps de traitements dépend de "scale" (échelle) (soyez prudent avec les hautes valeurs ).\nA utiliser de préférence avec de grand rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\nOptimisation pas utilsée en prévisualisation @@ -2216,20 +2216,108 @@ TP_LOCALLAB_SHOWVI;Masque et modifications TP_LOCALLAB_SHRESFRA;Ombres/Lumières TP_LOCALLAB_SHTRC_TOOLTIP;Modifie les tons de l'image en agissant sur la TRC (Tone Response Curve).\nGamma agit principalement sur les tons lumineux.\nSlope (pente) agit principalement sur les tons sombres. TP_LOCALLAB_SH_TOOLNAME;Ombres-lumières & Egaliser tonal - 5 -TP_LOCALLAB_SIGMAWAV;Attenuation Réponse +TP_LOCALLAB_SIGMAWAV;Atténuation Réponse TP_LOCALLAB_SIM;Simple TP_LOCALLAB_SLOMASKCOL;Pente (slope) masque TP_LOCALLAB_SLOSH;Pente TP_LOCALLAB_SOFT;Lumière douce - Original Retinex TP_LOCALLAB_SOFTM;Lumière douce (soft light) TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres méthodes Retinex.\nIl agit sur les gris et équilibre la luminance.\nC'est une émulation de "Dodge" and "Burn" -TP_LOCALLAB_SOFTRADIUSCOL;Radius adoucir +TP_LOCALLAB_SOFTRADIUSCOL;Rayon adoucir TP_LOCALLAB_SOFTRETI;Reduire artefact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Prend en compte ΔE pour améliorer Transmission map -TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 - - - +TP_LOCALLAB_SOFT_TOOLNAME;Lumière douce & Original Retinex - 6 +TP_LOCALLAB_SOURCE_GRAY;Valeur +TP_LOCALLAB_SPECCASE; Cas spécifiques +TP_LOCALLAB_SPECIAL;Usage Special des courbes RGB +TP_LOCALLAB_SPECIAL_TOOLTIP;Seulement pour cette courbe RGB, désactive (ou réduit les effecs) de Etendue, masque...par exemple, si vous voulez rendre un effet "négatif". +TP_LOCALLAB_SPOTNAME;Nouveau Spot +TP_LOCALLAB_STD;Standard +TP_LOCALLAB_STR;Force +TP_LOCALLAB_STRBL;Force +TP_LOCALLAB_STREN;Compression Force +TP_LOCALLAB_STRENG;Force +TP_LOCALLAB_STRENGR;Force +TP_LOCALLAB_STRENGTH;Bruit +TP_LOCALLAB_STRGRID;Force +TP_LOCALLAB_STRRETI_TOOLTIP;Si force Retinex < 0.2 seul Dehaze est activé.\nSi force Retinex >= 0.1 Dehaze est en mode luminance. +TP_LOCALLAB_STRUC;Structure +TP_LOCALLAB_STRUCCOL;Structure +TP_LOCALLAB_STRUCCOL1;Spot Structure +TP_LOCALLAB_STRUCT_TOOLTIP;Utilise l'algorithme de Sobel pour prendre en compte la structure dans la détection de forme.\nvous pouvez prévisualiser avec "masque et modifications - Montrer structure spot".\n\nPeut être utilisé avec masques (expert) structure, flouter, ondelettes pour améliorer la détection de bords.\n\nA besoin de réglages sans-masque pour êtrre activé (luminosité, exposition...) +TP_LOCALLAB_STRUMASKCOL;Structure masque force +TP_LOCALLAB_STRUMASK_TOOLTIP;Génère un masque structure qui va différencier les aplats et reliefs.\nSi structure masque comme outil est activé, ce masque est untilisé en plus des autres outils (gamma, slope, courbe contraste ...) +TP_LOCALLAB_STYPE;Forme méthode +TP_LOCALLAB_STYPE_TOOLTIP;Vous pouvez choisir entre:\nSymétrique - gauche et droite sont liés, haut et bas sont liés.\nIndépendent - toutes les saisies sont indépendantes. +TP_LOCALLAB_SYM;Symétrique (souris) +TP_LOCALLAB_SYMSL;Symétrique (souris + curseurs) +TP_LOCALLAB_TARGET_GRAY;Point Gris Cible +TP_LOCALLAB_THRES;Seuil structure +TP_LOCALLAB_THRESDELTAE;Seuil ΔE-Etendue +TP_LOCALLAB_THRESRETI;Seuil +TP_LOCALLAB_THRESWAV;Balance Seuil +TP_LOCALLAB_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 (Seuil) +TP_LOCALLAB_TLABEL2;TM Effectif Tm=%1 TM=%2 +TP_LOCALLAB_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.\nYou can act on Threshold to normalize +TP_LOCALLAB_TM;Compression tonale - Texture +TP_LOCALLAB_TM_MASK;Utilise transmission map +TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;Ce paramètre affecte la sensibilité aux bords.\n Plus grand il est, plus la luminosité change peut être considéré comme un bord.\n Si réglé à zéro 'compression tonale' va avoir un effet similaire à masque flou. +TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma déplace l'action de 'compression tonale' des ombres vers les lumières. +TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;Dans certains 'compression tonal' peut amener des effets de perspective, et dans de rares cas des halos peuvent apparaître.\n Accroître le nombre d'itérations peut aider à résoudre ces problèmes. +TP_LOCALLAB_TONEMAP_TOOLTIP;Compression tonal - menu principal doit être désactivé +TP_LOCALLAB_TONEMASCALE_TOOLTIP;Ce contrôle donne le pouvoir de différencier le contraste "local" et "global".\nPlus il est important, plus un détail sera accentué. +TP_LOCALLAB_TONE_TOOLNAME;Compression tonale - 4 +TP_LOCALLAB_TOOLCOL;Masque Structure comme outil +TP_LOCALLAB_TOOLMASK;Outils +TP_LOCALLAB_TRANSIT;Transition Gradiant +TP_LOCALLAB_TRANSITGRAD;Transition différentiation XY +TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Change la transition des abscisses vers les ordonnées +TP_LOCALLAB_TRANSITVALUE;Transition valeur +TP_LOCALLAB_TRANSITWEAK;Transition affaiblissement (linéaire-log) +TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Ajuste l'affaiblissement de la transition : change le processus d'affaiblissement - 1 linéaire - 2 parabolique - 3 cubique - ^25.\nPeut être utilisé en conjonction avec de très faibles valeurs de transition pour traiter/réduire les défauts (CBDL, Ondelettes, Couleur et lumière) +TP_LOCALLAB_TRANSIT_TOOLTIP;Ajuste la progressions de la transition enttre les zones affectées ou non affectées, comme un pourcentage du "rayon" +TP_LOCALLAB_TRANSMISSIONGAIN;Transmission gain +TP_LOCALLAB_TRANSMISSIONMAP;Transmission map +TP_LOCALLAB_TRANSMISSION_TOOLTIP;Transmission en accord à transmission.\nAbscisse: transmission pour les valeurs négatives (min), mean, et les valeurs positives (max).\nOrdonnée: amplification ou réduction.\nVous pouvez agir sur cette courbe pour chnager la Transmission et réduire les artefacts +TP_LOCALLAB_USEMASK;Utiliser masque +TP_LOCALLAB_VART;Variance (contraste) +TP_LOCALLAB_VIBRANCE;Vibrance - Chaud & Froid +TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Chaud & Froid - 3 +TP_LOCALLAB_SOFT_TOOLNAME;Lumière douce & Original Retinex - 6 +TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Flouter Grain & De-bruite - 1 +TP_LOCALLAB_TONE_TOOLNAME;Compression tonale - 4 +TP_LOCALLAB_RET_TOOLNAME;De-brume & Retinex - 9 +TP_LOCALLAB_SHARP_TOOLNAME;Netteté - 8 +TP_LOCALLAB_LC_TOOLNAME;Constraste local & Ondelettes (Défauts) - 7 +TP_LOCALLAB_CBDL_TOOLNAME;Contraste par Niveau détail (Défauts) - 2 +TP_LOCALLAB_LOG_TOOLNAME;Codage log - 0 +TP_LOCALLAB_MASKCOM_TOOLNAME;Masque Commun Couleur - 13 +TP_LOCALLAB_VIS_TOOLTIP;Click pour montrer/cacher le Spot sélectionné.\nCtrl+click pour montrer/cacher tous les Spot. +TP_LOCALLAB_WAMASKCOL;Ψ Masque Niveau Ondelettes +TP_LOCALLAB_WARM;Chaud - Froid & Artefacts de couleur +TP_LOCALLAB_WARM_TOOLTIP;Ce curseur utilise l'algorithme Ciecam et agit comme une Balance des blancs, il prut réchauffer ou refroidir cool la zone concernée.\nIl peut aussi dans certains réduire les artefacts colorés. +TP_LOCALLAB_WASDEN_TOOLTIP;De-bruite luminance pour les 3 premiers niveaux (fin).\nLa limite droite de la courbe correspond à gros : niveau 3 et au delà. +TP_LOCALLAB_WAV;Contrast local niveau +TP_LOCALLAB_WAVBLUR_TOOLTIP;Réalise un flou pour chaque niveau de décomposition, également pour l'image résiduelle. +TP_LOCALLAB_WAVCOMP;Compression par niveau +TP_LOCALLAB_WAVCOMPRE;(de)Compression par niveau +TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Réalise un 'Tone-mapping' ou une réduction du contraste local par niveau.\nEn abscisse: niveaux +TP_LOCALLAB_WAVCOMP_TOOLTIP;Réalise un contrast local en fonction de la direction de la décomposition en ondelettes : horizontal, vertical, diagonal +TP_LOCALLAB_WAVCON;Contraste par niveau +TP_LOCALLAB_WAVCONTF_TOOLTIP;Similaire à Contrast By Detail Levels : en abscisse niveaux. +TP_LOCALLAB_WAVDEN;de-bruite luminance par niveau (0 1 2 + 3 et plus) +TP_LOCALLAB_WAVE;Ψ Ondelette +TP_LOCALLAB_WAVEDG;Contrast Local +TP_LOCALLAB_WAVEEDG_TOOLTIP;Améliore la netteté prenant en compte la notion de "ondelettes bords".\nNécessite au moins que les 4 premiers niveaux sont utilisables +TP_LOCALLAB_WAVGRAD_TOOLTIP;Filtre gradué pour Contraste local "luminance" +TP_LOCALLAB_WAVHIGH;Ψ Ondelette haut +TP_LOCALLAB_WAVLEV;Flouter par niveau +TP_LOCALLAB_WAVLOW;Ψ Ondelette bas +TP_LOCALLAB_WAVMASK;Ψ Masque Niveau contraste local +TP_LOCALLAB_WAVMASK_TOOLTIP;Autorise un travail fin sur les masques niveaux de contraste (structure) +TP_LOCALLAB_WAVMED;Ψ Ondelette normal +TP_LOCALLAB_WEDIANHI;Median Haut +TP_LOCALLAB_WHITE_EV;Blanc Ev TP_METADATA_EDIT;Appliquer les modifications TP_METADATA_MODE;Mode de copie des métadonnées TP_METADATA_STRIP;Retirer toutes les métadonnées From d74e525c119a9392972536c8ddd81d65d2559b75 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 4 Jul 2020 10:51:40 +0200 Subject: [PATCH 082/114] Small changes to french --- rtdata/languages/Francais | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index abb657063..af1682fea 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1799,7 +1799,7 @@ TP_LOCALLAB_BUTTON_DEL;Effacer TP_LOCALLAB_BUTTON_DUPL;Dupliquer TP_LOCALLAB_BUTTON_REN;Renommer TP_LOCALLAB_BUTTON_VIS;Montrer/Cacher -TP_LOCALLAB_CBDL;Contraste par niveaux détail-Défauts +TP_LOCALLAB_CBDL;Contraste niveaux détail-Défauts TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Empêche d'augmenter le bruit @@ -2000,7 +2000,7 @@ TP_LOCALLAB_LIST_TOOLTIP;Choisir un outil et ensuite son niveau de complexité " TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Donne priorité à l'action sur les tons moyens et hautes lumières en choisissant les niveaux concernés d'ondelettes TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights TP_LOCALLAB_LOCCONT;Masque Flou -TP_LOCALLAB_LOC_CONTRAST;Contraste Local-Ondelettes-défauts +TP_LOCALLAB_LOC_CONTRAST;Contraste Local-Ondelettes-déf. TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramide 1: TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramide 2: TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contraste par niveaux- Tone Mapping - Contraste Dir. @@ -2170,7 +2170,7 @@ TP_LOCALLAB_SH1;Ombres Lumières TP_LOCALLAB_SH2;Egaliseur TP_LOCALLAB_SHADEX;Ombres TP_LOCALLAB_SHADEXCOMP;Compression ombres & profondeur tonale -TP_LOCALLAB_SHADHIGH;OmbresLumières - Egaliseur tonal +TP_LOCALLAB_SHADHIGH;Ombres/Lumières - Egaliseur tonal TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP;Peut être utilisé - ou en complement - du module Exposition dans les cas difficiles.\nUtiliser réduction du bruit Denoise peut être nécessaire : éclaicir les ombres.\n\nPeut être utilisé comme un filtre gradué (augmenter Etendue) TP_LOCALLAB_SHAMASKCOL;masque ombres TP_LOCALLAB_SHAPETYPE;Forme aire RT-spot From 8728aee0cee59984c41ed53a5e2f70fd105d5e3f Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 4 Jul 2020 11:35:37 +0200 Subject: [PATCH 083/114] fix broken non SSE build and add SSE code for one loop in wavcont() --- rtengine/LUT.h | 2 +- rtengine/iplocallab.cc | 44 ++++++++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/rtengine/LUT.h b/rtengine/LUT.h index c426bbaaa..416ae689a 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -99,8 +99,8 @@ protected: unsigned int size; unsigned int upperBound; // always equals size-1, parameter created for performance reason private: -#ifdef __SSE2__ unsigned int owner; +#ifdef __SSE2__ alignas(16) vfloat maxsv; alignas(16) vfloat sizev; alignas(16) vint sizeiv; diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 11e2dcad7..689054a5d 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7184,16 +7184,6 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel float lutFactor; const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.65f, 0.5f, 0.4f, 0.25f, 0.1f}; const auto meaLut = buildMeaLut(inVals, mea, lutFactor); - - float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); - if (klev < 0.f) { - klev *= 2.6666f;//compression increase contraste - } else { - klev *= 4.f;//dilatation reduce contraste - detailattenuator - } - const float compression = expf(-klev); - const float detailattenuator = std::max(klev, 0.f); - const auto wav_L = wdspot.level_coeffs(level)[dir]; #ifdef _OPENMP @@ -7206,14 +7196,38 @@ void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavel } } + float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); + if (klev < 0.f) { + klev *= 2.6666f;//compression increase contraste + } else { + klev *= 4.f;//dilatation reduce contraste - detailattenuator + } + const float compression = expf(-klev); + const float detailattenuator = std::max(klev, 0.f); + Compresslevels(templevel, W_L, H_L, compression, detailattenuator, thres, mean[level], MaxP[level], meanN[level], MaxN[level], madL[level][dir - 1]); #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel if (multiThread) #endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - int j = y * W_L + x; - wav_L[j] = intp((*meaLut)[std::fabs(wav_L[j]) * lutFactor], templevel[y][x], wav_L[j]); + { +#ifdef __SSE2__ + const vfloat lutFactorv = F2V(lutFactor); +#endif +#ifdef _OPENMP + #pragma omp for +#endif + for (int y = 0; y < H_L; y++) { + int x = 0; + int j = y * W_L; +#ifdef __SSE2__ + for (; x < W_L - 3; x += 4, j += 4) { + const vfloat valv = LVFU(wav_L[j]); + STVFU(wav_L[j], intp((*meaLut)[vabsf(valv) * lutFactorv], LVFU(templevel[y][x]), valv)); + } +#endif + for (; x < W_L; x++, j++) { + wav_L[j] = intp((*meaLut)[std::fabs(wav_L[j]) * lutFactor], templevel[y][x], wav_L[j]); + } } } } From 510810626cd60444cb5509ffdf98589352c5147e Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 4 Jul 2020 13:04:30 +0200 Subject: [PATCH 084/114] Various change typo -spelling French --- rtdata/languages/Francais | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index af1682fea..cbef2e5ef 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1792,7 +1792,7 @@ TP_LOCALLAB_BLURLEVELFRA;Flouter niveaux TP_LOCALLAB_BLURMASK_TOOLTIP;Génère un masque flou, prend en compte la structure avec le curseur de seuil de contraste du Masque flou. TP_LOCALLAB_BLURRESIDFRA;Flouter image Résiduelle TP_LOCALLAB_BLUR_TOOLNAME;Adoucir Flouter Grain & Réduction du Bruit - 1 -TP_LOCALLAB_BLWH;Tous les chnagements forcés en noir et blanc +TP_LOCALLAB_BLWH;Tous les changements forcés en noir et blanc TP_LOCALLAB_BLWH_TOOLTIP;Force le changement de la composante "a" et "b" à zéro.\nUtile quand l'utilisateur choisit un processus noir et blanc, ou un film. TP_LOCALLAB_BUTTON_ADD;Ajouter TP_LOCALLAB_BUTTON_DEL;Effacer @@ -1823,7 +1823,7 @@ TP_LOCALLAB_CLARIFRA;Clarté & Masque de netteté - Fusion & adoucir images TP_LOCALLAB_CLARILRES;Fusion Luma TP_LOCALLAB_CLARISOFT;Rayon adoucir TP_LOCALLAB_CLARISOFT_TOOLTIP;Actif pour Clarté et Masque de netteté si différent de zéro.\n\nActif pour toutes les pyramides ondelettes.\nInactif si rayon = 0 -TP_LOCALLAB_CLARITYML;Claté +TP_LOCALLAB_CLARITYML;Clarté TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' TP_LOCALLAB_CLIPTM;Clip Recupère données (gain) TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts @@ -1854,7 +1854,7 @@ TP_LOCALLAB_CURVCURR;Normal TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;Si la courbe est au sommet, le masque est compétement noir aucune transformation n'est réalisée par le masque sur l'image.\nQuand vous descendez la courbe, progressivement le masque va se colorer et s'éclaicir, l'image change de plus en plus.\n\nIl est recommendé (pas obligatoire) de positionner le sommet des courbes curves sur la ligne de transition grise qui représnte les références (chroma, luma, couleur). TP_LOCALLAB_CURVEEDITORM_CC_TOOLTIP;Si la courbe est au sommet,le masque est compétement noir aucune transformation n'est réalisée par le masque sur l'image.\nQuand vous descendez la courbe, progressivement le masque va se colorer et s'éclaicir, l'image change de plus en plus.\nVous pouvez choisir ou non de positionner le sommet de la courbe sur la transition. TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;Pour être actif, vous devez activer la combobox 'Curves type' -TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Curve tonale +TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Courbe tonale TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), peut être utilisée avec L(H) dans Couleur et lumière TP_LOCALLAB_CURVEMETHOD_TOOLTIP;'Normal', la courbe L=f(L) a le même algorithme que le curseur luminosité.\n'Super' the curve L=f(L) has an new improved algorithm, which can leeds in some cases to artifacts. TP_LOCALLAB_CURVENCONTRAST;Super+Contrast threshold (experimental) @@ -1921,7 +1921,7 @@ TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez TP_LOCALLAB_EXPRETITOOLS;Outils Retinex avancés TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUtiliser de basses valeurs de transition et de hautes valeurs de transition affaiblissement et Etendue pour simuler un petit RT-spot. TP_LOCALLAB_EXPTOOL;Outils exposition -TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC +TP_LOCALLAB_EXPTRC;Courbe de réponse Tonale - TRC TP_LOCALLAB_EXP_TOOLNAME;Exposition - Compression Dynamique - 10 TP_LOCALLAB_FATAMOUNT;Quantité TP_LOCALLAB_FATANCHOR;Ancre @@ -1932,30 +1932,30 @@ TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal - utilise Fattal Tone mapping algorithme TP_LOCALLAB_FATLEVEL;Sigma TP_LOCALLAB_FATRES;Quantité de Residual Image TP_LOCALLAB_FATSHFRA;Compression Dynamique Masque ƒ -TP_LOCALLAB_FEATH_TOOLTIP;Largeur du Gradiant en porcentage de la diagonale du Spot\nUtilisé par tous les Filtres Gradués dans tous les outils.\nPas d'action si les filtres gradués ne sont pas utilisés. -TP_LOCALLAB_FEATVALUE;Adoucissement gradiant (Filtres Gradués) +TP_LOCALLAB_FEATH_TOOLTIP;Largeur du Gradient en porcentage de la diagonale du Spot\nUtilisé par tous les Filtres Gradués dans tous les outils.\nPas d'action si les filtres gradués ne sont pas utilisés. +TP_LOCALLAB_FEATVALUE;Adoucissement gradient (Filtres Gradués) TP_LOCALLAB_FFTCOL_MASK;FFTW ƒ TP_LOCALLAB_FFTW;ƒ - Utilise Fast Fourier Transform TP_LOCALLAB_FFTW2;ƒ - Utilise Fast Fourier Transform (TIF, JPG,..) TP_LOCALLAB_FFTWBLUR;ƒ - Utilise toujours Fast Fourier Transform -TP_LOCALLAB_FULLIMAGE;Calcule les valeurs DarkEv - WhiteEv - sur l'image entière +TP_LOCALLAB_FULLIMAGE;Calcule les valeurs Noir Ev - Blanc Ev - sur l'image entière TP_LOCALLAB_GAM;Gamma -TP_LOCALLAB_GAMFRA;Tone response curve (TRC) +TP_LOCALLAB_GAMFRA;Courbe Réponse Tonale (TRC) TP_LOCALLAB_GAMM;Gamma TP_LOCALLAB_GAMMASKCOL;Gamma masque TP_LOCALLAB_GAMSH;Gamma -TP_LOCALLAB_GRADANG;Angle du Gradiant +TP_LOCALLAB_GRADANG;Angle du Gradient TP_LOCALLAB_GRADANG_TOOLTIP;Angle de Rotation en degrés : -180 0 +180 TP_LOCALLAB_GRADFRA;Filtre gradué Masque TP_LOCALLAB_GRADGEN_TOOLTIP;Filtre Gradué est fourni avec Couleur et Lumière & Fusion fichier, Exposition & masque, Shadows Highlight, Vibrance, Encoding log.\n\nVibrance, Couleur et Lumière & Fusion fichier, sont fournis avec GF luminance, chrominance, teinte.\nAdoucissement est situé dans "réglages". TP_LOCALLAB_GRADLOGFRA;Filtre Gradué Luminance -TP_LOCALLAB_GRADSTR;Force du Gradiant +TP_LOCALLAB_GRADSTR;Force du Gradient TP_LOCALLAB_GRADSTRAB_TOOLTIP;Filtre chroma force -TP_LOCALLAB_GRADSTRCHRO;Force Gradiant Chrominance -TP_LOCALLAB_GRADSTRHUE;Force Gradiant Teinte -TP_LOCALLAB_GRADSTRHUE2;Force Gradiant Teinte +TP_LOCALLAB_GRADSTRCHRO;Force Gradient Chrominance +TP_LOCALLAB_GRADSTRHUE;Force Gradient Teinte +TP_LOCALLAB_GRADSTRHUE2;Force Gradient Teinte TP_LOCALLAB_GRADSTRHUE_TOOLTIP;Filttre Teinte force -TP_LOCALLAB_GRADSTRLUM;Force Gradiant Luminance +TP_LOCALLAB_GRADSTRLUM;Force Gradient Luminance TP_LOCALLAB_GRADSTR_TOOLTIP;Force Filtre en Ev TP_LOCALLAB_GRAINFRA;Film Grain 1:1 TP_LOCALLAB_GRALWFRA;Filtre Gradué Local contraste @@ -2003,7 +2003,7 @@ TP_LOCALLAB_LOCCONT;Masque Flou TP_LOCALLAB_LOC_CONTRAST;Contraste Local-Ondelettes-déf. TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramide 1: TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramide 2: -TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contraste par niveaux- Tone Mapping - Contraste Dir. +TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contr. par niveaux- Tone Mapping - Cont.Dir. TP_LOCALLAB_LOC_CONTRASTPYRLAB; Filtre Gradué - Netteté bords - Flouter TP_LOCALLAB_LOC_RESIDPYR;Image Residuelle TP_LOCALLAB_LOG;Codage log @@ -2131,7 +2131,7 @@ TP_LOCALLAB_RESIDHITHR;Hautes lumières seuil TP_LOCALLAB_RESIDSHA;Ombres TP_LOCALLAB_RESIDSHATHR;Ombres seuil TP_LOCALLAB_RETI;De-brume - Retinex Fort contraste -TP_LOCALLAB_RETIFRA;Retinexfr +TP_LOCALLAB_RETIFRA;Retinex TP_LOCALLAB_RETIM;Original Retinex TP_LOCALLAB_RETITOOLFRA;Retinex Outils TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface traitée\nLe temps de traitements dépend de "scale" (échelle) (soyez prudent avec les hautes valeurs ).\nA utiliser de préférence avec de grand rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\nOptimisation pas utilsée en prévisualisation @@ -2269,7 +2269,7 @@ TP_LOCALLAB_TONEMASCALE_TOOLTIP;Ce contrôle donne le pouvoir de différencier l TP_LOCALLAB_TONE_TOOLNAME;Compression tonale - 4 TP_LOCALLAB_TOOLCOL;Masque Structure comme outil TP_LOCALLAB_TOOLMASK;Outils -TP_LOCALLAB_TRANSIT;Transition Gradiant +TP_LOCALLAB_TRANSIT;Transition - Gradient TP_LOCALLAB_TRANSITGRAD;Transition différentiation XY TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Change la transition des abscisses vers les ordonnées TP_LOCALLAB_TRANSITVALUE;Transition valeur From bc2ec7c39e3da7516c13b5a993c24eb05065fdd2 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sat, 4 Jul 2020 18:02:26 +0200 Subject: [PATCH 085/114] wavcontrast4(): some cleanups --- rtengine/iplocallab.cc | 74 +++++++----------------------------------- 1 file changed, 12 insertions(+), 62 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 4e36f3806..0c529e88d 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -7335,6 +7335,9 @@ BENCHFUN } klev *= 0.8f; const float threshold = mean[level] + lp.sigmalc2 * sigma[level]; + float lutFactor; + const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.6f, 0.5f, 0.4f, 0.3f, 0.1f}; + const auto meaLut = buildMeaLut(inVals, mea, lutFactor); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) if (multiThread) @@ -7343,31 +7346,6 @@ BENCHFUN for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { const float WavCL = std::fabs(wav_L[y * W_L + x]); - float beta; - - if (WavCL < mea[0]) { - beta = 0.05f; - } else if (WavCL < mea[1]) { - beta = 0.2f; - } else if (WavCL < mea[2]) { - beta = 0.7f; - } else if (WavCL < mea[3]) { - beta = 1.f; //standard - } else if (WavCL < mea[4]) { - beta = 1.f; - } else if (WavCL < mea[5]) { - beta = 0.8f; //+sigma - } else if (WavCL < mea[6]) { - beta = 0.6f; - } else if (WavCL < mea[7]) { - beta = 0.5f; - } else if (WavCL < mea[8]) { - beta = 0.4f; // + 2 sigma - } else if (WavCL < mea[9]) { - beta = 0.3f; - } else { - beta = 0.1f; - } float absciss; if (WavCL >= threshold) { //for max @@ -7383,7 +7361,7 @@ BENCHFUN float kinterm = 1.f + reduceeffect * kc; kinterm = kinterm <= 0.f ? 0.01f : kinterm; - wav_L[y * W_L + x] *= (1.f + (kinterm - 1.f) * beta); + wav_L[y * W_L + x] *= (1.f + (kinterm - 1.f) * (*meaLut)[WavCL * lutFactor]); } } } @@ -7396,44 +7374,24 @@ BENCHFUN float *wav_L0 = wdspot->get_coeff0(); if (radblur > 0.f && blurena) { - array2D bufl(W_L, H_L); -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - bufl[y][x] = wav_L0[y * W_L + x]; - } + float* src[H_L]; + for (int i = 0; i < H_L; ++i) { + src[i] = &wav_L0[i * W_L]; } #ifdef _OPENMP #pragma omp parallel if (multiThread) #endif { - gaussianBlur(bufl, bufl, W_L, H_L, radblur); - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) -#endif - for (int y = 0; y < H_L; y++) { - for (int x = 0; x < W_L; x++) { - wav_L0[y * W_L + x] = bufl[y][x]; - } + gaussianBlur(src, src, W_L, H_L, radblur); } } if (compress != 0.f && compreena) { - - float Compression = expf(-compress); - float DetailBoost = compress; - - if (compress < 0.0f) { - DetailBoost = 0.0f; - } + const float Compression = expf(-compress); + const float DetailBoost = std::max(compress, 0.f); CompressDR(wav_L0, W_L, H_L, Compression, DetailBoost); - } if ((lp.residsha != 0.f || lp.residhi != 0.f)) { @@ -7474,7 +7432,6 @@ BENCHFUN } if (contrast != 0.) { - double avedbl = 0.0; // use double precision for large summations #ifdef _OPENMP @@ -7484,10 +7441,7 @@ BENCHFUN avedbl += wav_L0[i]; } - float ave = avedbl / double(W_L * H_L); - - float avg = ave / 32768.f; - avg = LIM01(avg); + const float avg = LIM01(avedbl / (32768.f * W_L * H_L)); double contreal = 0.6 * contrast; DiagonalCurve resid_contrast({ DCT_NURBS, @@ -7500,12 +7454,8 @@ BENCHFUN #pragma omp parallel for if (multiThread) #endif for (int i = 0; i < W_L * H_L; i++) { - float buf = LIM01(wav_L0[i] / 32768.f); - buf = resid_contrast.getVal(buf); - buf *= 32768.f; - wav_L0[i] = buf; + wav_L0[i] = resid_contrast.getVal(LIM01(wav_L0[i] / 32768.f)) * 32768.f; } - } float alow = 1.f; From fd43ed28fe055d9e125b83fb5d1630ebad45b708 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 5 Jul 2020 08:33:40 +0200 Subject: [PATCH 086/114] change exposure with Laplacian --- rtdata/languages/default | 4 ++-- rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 52 +++++++++++++++++++++++++++++++++------- rtengine/procparams.cc | 2 +- rtgui/locallabtools.cc | 2 +- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 7ce5b4f3f..2457b83ee 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2492,13 +2492,13 @@ TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greate TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties) TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise. TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. -TP_LOCALLAB_EXPOSE;Exposure - PDE algorithms +TP_LOCALLAB_EXPOSE;PDE algorithms & Exposure TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high decay transition values and scope to simulate small RT-spot. TP_LOCALLAB_EXPTOOL;Tools exposure TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC -TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10 +TP_LOCALLAB_EXP_TOOLNAME;Laplacian PDE -Dynamic Range Compression & Exposure- 10 TP_LOCALLAB_FATAMOUNT;Amount TP_LOCALLAB_FATANCHOR;Anchor TP_LOCALLAB_FATANCHORA;Offset diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 57c8c4c64..66ad4dc57 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -351,7 +351,7 @@ public: void transit_shapedetect_retinex(int call, int senstype, LabImage * bufexporig, LabImage * bufmask, LabImage * buforigmas, float **buflight, float **bufchro, const float hueref, const float chromaref, const float lumaref, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); void transit_shapedetect(int senstype, const LabImage *bufexporig, LabImage * originalmask, float **bufchro, bool HHutili, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); - void exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve); + void exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, int bfwr, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve, const float hueref, const float lumaref, const float chromaref); void Exclude_Local(float **deltaso, float hueref, float chromaref, float lumaref, float sobelref, float meansobel, const struct local_params & lp, const LabImage * original, LabImage * transformed, const LabImage * rsv, const LabImage * reserv, int cx, int cy, int sk); void DeNoise_Local(int call, const struct local_params& lp, LabImage* originalmask, int levred, float hueref, float lumaref, float chromaref, LabImage* original, LabImage* transformed, const LabImage &tmp1, int cx, int cy, int sk); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 9b982cd84..ca65f3779 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2405,7 +2405,7 @@ void ImProcFunctions::softprocess(const LabImage* bufcolorig, array2D &bu } } -void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve) +void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, int bfwr, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve, const float hueref, const float lumaref, const float chromaref) { BENCHFUN //exposure local @@ -2422,8 +2422,42 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* b if (lp.linear > 0.f && lp.expcomp == 0.f) { lp.expcomp = 0.001f; } + bool exec = (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex); - if (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex) { + //Laplacian PDE before exposure to smooth L, algorithm exposure leads to increase L differences + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); + const std::unique_ptr dE(new float[bfwr * bfhr]); + + + if(!exec) { + float diffde = 100.f - lp.sensex;//the more scope, the less take into account dE for Laplace + deltaEforLaplace(dE.get(), diffde, bfwr, bfhr, bufexporig, hueref, chromaref, lumaref); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + datain[y * bfwr + x] = bufexporig->L[y][x]; + } + } + + MyMutex::MyLock lock(*fftwMutex); + ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, 360.f, 1.f, dE.get(), 0, 1, 1);//350 arbitrary value about 45% strength Laplacian +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexporig->L[y][x] = dataout[y * bfwr + x]; + } + } + } + + + + if (exec) { #ifdef _OPENMP #pragma omp parallel for if (multiThread) @@ -2432,7 +2466,7 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* b for (int jr = 0; jr < bfw; jr++) { float L = bufexporig->L[ir][jr]; const float Llin = LIM01(L / 32768.f); - const float addcomp = linear * (-kl * Llin + kl);//maximum about 1. IL + const float addcomp = linear * (-kl * Llin + kl);//maximum about 1 . IL const float exp_scale = pow_F(2.0, (lp.expcomp + addcomp)); const float shoulder = ((maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr) + 0.1f; const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; @@ -2451,8 +2485,8 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* b #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { + for (int ir = 0; ir < bfhr; ir++) { + for (int jr = 0; jr < bfwr; jr++) { float L = bufexporig->L[ir][jr]; //highlight const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(cexp_scale, ccomp, chlrange, 2 * L)); @@ -5253,7 +5287,7 @@ void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp } } else if (senstype == 1) { //exposure - ImProcFunctions::exlabLocal(lp, GH, GW, original, temp.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + ImProcFunctions::exlabLocal(lp, GH, GW, GW, GH, original, temp.get(), hltonecurveloc, shtonecurveloc, tonecurveloc, hueref, lumaref, chromaref); if (exlocalcurve) { #ifdef _OPENMP @@ -12942,7 +12976,7 @@ void ImProcFunctions::Lab_Local( if (bfw >= mSP && bfh >= mSP) { - if (lp.expmet == 1) { + if (lp.expmet == 1 || lp.expmet == 0) { optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); } @@ -13096,12 +13130,12 @@ void ImProcFunctions::Lab_Local( lp.expcomp = 0.001f; // to enabled } - ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + ImProcFunctions::exlabLocal(lp, bfh, bfw, bfhr, bfwr, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc, hueref, lumaref, chromaref); } else { - ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexporig.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + ImProcFunctions::exlabLocal(lp, bfh, bfw, bfhr, bfwr, bufexporig.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc, hueref, lumaref, chromaref); } //gradient diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index deeae9c54..d177e44a2 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2869,7 +2869,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : expexpose(false), complexexpose(0), expcomp(0.0), - hlcompr(0), + hlcompr(20), hlcomprthresh(0), black(0), shadex(0), diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 1dd5513da..8deff0170 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -2191,7 +2191,7 @@ LocallabExposure::LocallabExposure(): exptoolexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPTOOL")))), expcomp(Gtk::manage(new Adjuster(M("TP_EXPOSURE_EXPCOMP"), MINEXP, MAXEXP, 0.01, 0.))), black(Gtk::manage(new Adjuster(M("TP_EXPOSURE_BLACKLEVEL"), -16384, 32768, 10, 0))), - hlcompr(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTS"), 0, 500, 1, 0))), + hlcompr(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTS"), 0, 500, 1, 20))), hlcomprthresh(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD"), 0, 100, 1, 0))), shadex(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEX"), 0, 100, 1, 0))), shcompr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEXCOMP"), 0, 100, 1, 50))), From 78ab54780126dd619db44f34ef13eec51812f334 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 5 Jul 2020 09:44:34 +0200 Subject: [PATCH 087/114] change scope exposure --- rtdata/languages/default | 2 +- rtengine/iplocallab.cc | 4 ++-- rtgui/locallabtools.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2457b83ee..56c5edd2b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2407,7 +2407,7 @@ TP_LOCALLAB_COLORDE;Color preview selection ΔE - Intensity TP_LOCALLAB_COLORDEPREV_TOOLTIP;Button Preview ΔE needs that only one tool is enabled (expander).\nTo be able to have an Preview ΔE with several enable tools use Mask and modifications - Preview ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative. TP_LOCALLAB_COLORSCOPE;Scope Color Tools -TP_LOCALLAB_COLORSCOPE_TOOLTIP;Use a common Scope for Color and light, Exposure (Standard), Shadows highlight, Vibrance.\nOthers tools have their specific scope. +TP_LOCALLAB_COLORSCOPE_TOOLTIP;Use a common Scope for Color and light, Shadows highlight, Vibrance.\nOthers tools have their specific scope. TP_LOCALLAB_COLOR_TOOLNAME;Color&Light (Defects) - 11 TP_LOCALLAB_COL_NAME;Name TP_LOCALLAB_COL_VIS;Status diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index ca65f3779..d443a9464 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -1376,12 +1376,12 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.residhithr = locallab.spots.at(sp).residhithr; lp.blwh = locallab.spots.at(sp).blwh; lp.senscolor = (int) locallab.spots.at(sp).colorscope; - //replace scope color exposure vibrance shadows + //replace scope color vibrance shadows lp.sens = lp.senscolor; lp.sensv = lp.senscolor; lp.senshs = lp.senscolor; if(lp.expmet == 0){ - lp.sensex = lp.senscolor; +// lp.sensex = lp.senscolor; } } diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 8deff0170..49b6feb56 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -3185,7 +3185,7 @@ void LocallabExposure::updateExposureGUI2() pdeFrame->hide(); fatFrame->hide(); softradiusexp->set_sensitive(true); - sensiex->set_sensitive(false); + sensiex->set_sensitive(true); } else if (expMethod->get_active_row_number() == 1) { pdeFrame->show(); fatFrame->show(); From 20e86dac98d9552f17b80f9dc3342e0a1c3e71ee Mon Sep 17 00:00:00 2001 From: Desmis Date: Sun, 5 Jul 2020 11:29:17 +0200 Subject: [PATCH 088/114] Improve algo laplace --- rtengine/iplocallab.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d443a9464..f080feb1f 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -2433,7 +2433,21 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, i if(!exec) { float diffde = 100.f - lp.sensex;//the more scope, the less take into account dE for Laplace deltaEforLaplace(dE.get(), diffde, bfwr, bfhr, bufexporig, hueref, chromaref, lumaref); + float lap = 1.f; + float alap = 600.f; + float blap = 100.f; + if(diffde > 80.f) { + lap = alap; + } + if(diffde < 30.f) { + lap = blap; + } + float aa = (alap - blap) / 50.f; + float bb = 100.f - 30.f * aa; + if(diffde >= 30.f && diffde <= 80.f) { + lap = aa * diffde + bb; + } #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif @@ -2444,7 +2458,7 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, i } MyMutex::MyLock lock(*fftwMutex); - ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, 360.f, 1.f, dE.get(), 0, 1, 1);//350 arbitrary value about 45% strength Laplacian + ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, lap, 1.f, dE.get(), 0, 1, 1);//350 arbitrary value about 45% strength Laplacian #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif From a393a500a1e6afd82c38c1441132eb85722803ef Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 5 Jul 2020 12:46:29 +0200 Subject: [PATCH 089/114] retinex_pde(): cleanup and speedup --- rtengine/iplocallab.cc | 141 ++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index f080feb1f..bce27f8dd 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -3642,30 +3642,27 @@ void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw fftwf_plan_with_nthreads(omp_get_max_threads()); } #endif - float *data_fft, *data_fft04, *data_tmp, *data, *data_tmp04; - float *datashow = nullptr; + float *datashow = nullptr; if (show != 0) { - if (NULL == (datashow = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + datashow = (float *) fftwf_malloc(sizeof(float) * bfw * bfh); + if (!datashow) { fprintf(stderr, "allocation error\n"); abort(); } } - if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (NULL == (data_tmp04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + float *data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh); + if (!data_tmp) { fprintf(stderr, "allocation error\n"); abort(); } //first call to laplacian with plein strength - ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); + discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); - if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + float *data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh); + if (!data_fft) { fprintf(stderr, "allocation error\n"); abort(); } @@ -3673,74 +3670,100 @@ void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw if (show == 1) { for (int y = 0; y < bfh ; y++) { for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_tmp[y * bfw + x]; + datashow[y * bfw + x] = data_tmp[y * bfw + x]; } } } - //second call to laplacian with 40% strength ==> reduce effect if we are far from ref (deltaE) - ImProcFunctions::discrete_laplacian_threshold(data_tmp04, datain, bfw, bfh, 0.4f * thresh); - - if (NULL == (data_fft04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - - if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { - fprintf(stderr, "allocation error\n"); - abort(); - } - //execute first const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); fftwf_execute(dct_fw); + fftwf_destroy_plan(dct_fw); //execute second if (dEenable == 1) { + float* data_fft04 = (float *)fftwf_malloc(sizeof(float) * bfw * bfh); + float* data_tmp04 = (float *)fftwf_malloc(sizeof(float) * bfw * bfh); + if (!data_fft04 || !data_tmp04) { + fprintf(stderr, "allocation error\n"); + abort(); + } + //second call to laplacian with 40% strength ==> reduce effect if we are far from ref (deltaE) + discrete_laplacian_threshold(data_tmp04, datain, bfw, bfh, 0.4f * thresh); const auto dct_fw04 = fftwf_plan_r2r_2d(bfh, bfw, data_tmp04, data_fft04, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); fftwf_execute(dct_fw04); fftwf_destroy_plan(dct_fw04); + constexpr float exponent = 4.5f; #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel if (multiThread) #endif - for (int y = 0; y < bfh ; y++) {//mix two fftw Laplacian : plein if dE near ref - for (int x = 0; x < bfw; x++) { - float prov = pow(dE[y * bfw + x], 4.5f); - data_fft[y * bfw + x] = prov * data_fft[y * bfw + x] + (1.f - prov) * data_fft04[y * bfw + x]; + { +#ifdef __SSE2__ + const vfloat exponentv = F2V(exponent); +#endif +#ifdef _OPENMP + #pragma omp for +#endif + for (int y = 0; y < bfh ; y++) {//mix two fftw Laplacian : plein if dE near ref + int x = 0; +#ifdef __SSE2__ + for (; x < bfw - 3; x += 4) { + STVFU(data_fft[y * bfw + x], intp(pow_F(LVFU(dE[y * bfw + x]), exponentv), LVFU(data_fft[y * bfw + x]), LVFU(data_fft04[y * bfw + x]))); + } +#endif + for (; x < bfw; x++) { + data_fft[y * bfw + x] = intp(pow_F(dE[y * bfw + x], exponent), data_fft[y * bfw + x], data_fft04[y * bfw + x]); + } } } + fftwf_free(data_fft04); + fftwf_free(data_tmp04); } - if (show == 2) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_fft[y * bfw + x]; - } - } - } - - fftwf_free(data_fft04); - fftwf_free(data_tmp); - fftwf_free(data_tmp04); - /* solve the Poisson PDE in Fourier space */ /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ - ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); + rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); if (show == 3) { for (int y = 0; y < bfh ; y++) { for (int x = 0; x < bfw; x++) { - datashow[y * bfw + x] = data_fft[y * bfw + x]; + datashow[y * bfw + x] = data_fft[y * bfw + x]; } } } - const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data_tmp, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); fftwf_execute(dct_bw); - fftwf_destroy_plan(dct_fw); fftwf_destroy_plan(dct_bw); fftwf_free(data_fft); + + if (show != 4 && normalize == 1) { + normalize_mean_dt(data_tmp, datain, bfw * bfh, 1.f, 1.f); + } + + if (show == 0 || show == 4) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * data_tmp[y * bfw + x]); + } + } + } else if (show == 1 || show == 2 || show == 3) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * datashow[y * bfw + x]); + } + } + } + + fftwf_free(data_tmp); + if (datashow) { + fftwf_free(datashow); + } fftwf_cleanup(); #ifdef RT_FFTW3F_OMP @@ -3748,32 +3771,6 @@ void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw fftwf_cleanup_threads(); } #endif - if (show != 4 && normalize == 1) { - normalize_mean_dt(data, datain, bfw * bfh, 1.f, 1.f); - } - - if (show == 0 || show == 4) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - dataout[y * bfw + x] = clipLoc(multy * data[y * bfw + x]); - } - } - } else if (show == 1 || show == 2 || show == 3) { - for (int y = 0; y < bfh ; y++) { - for (int x = 0; x < bfw; x++) { - dataout[y * bfw + x] = clipLoc(multy * datashow[y * bfw + x]); - } - } - - fftwf_free(datashow); - } - - fftwf_free(data); - } void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int xstart, int ystart, int sk, int cx, int cy, LabImage* bufcolorig, LabImage* bufmaskblurcol, LabImage* originalmaskcol, LabImage* original, LabImage* reserved, int inv, struct local_params & lp, From 10334f5c81ba501a506dfc54fbb6aee00e6fcee8 Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Sun, 5 Jul 2020 13:30:19 +0200 Subject: [PATCH 090/114] exlabLocal(): cleanup --- rtengine/iplocallab.cc | 116 +++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 63 deletions(-) diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index bce27f8dd..aa06dcda3 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -1380,9 +1380,6 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.sens = lp.senscolor; lp.sensv = lp.senscolor; lp.senshs = lp.senscolor; - if(lp.expmet == 0){ -// lp.sensex = lp.senscolor; - } } static void calcTransitionrect(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) @@ -2411,45 +2408,41 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, i //exposure local constexpr float maxran = 65536.f; - const float cexp_scale = std::pow(2.f, lp.expcomp); - const float ccomp = (rtengine::max(0.f, lp.expcomp) + 1.f) * lp.hlcomp / 100.f; - const float cshoulder = ((maxran / rtengine::max(1.0f, cexp_scale)) * (lp.hlcompthr / 200.f)) + 0.1f; - const float chlrange = maxran - cshoulder; const float linear = lp.linear; - constexpr float kl = 1.f; - const float hlcompthr = lp.hlcompthr / 200.f; - const float hlcomp = lp.hlcomp / 100.f; - if (lp.linear > 0.f && lp.expcomp == 0.f) { - lp.expcomp = 0.001f; + if (linear > 0.f && lp.expcomp == 0.f) { + lp.expcomp = 0.001f; } - bool exec = (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex); - - //Laplacian PDE before exposure to smooth L, algorithm exposure leads to increase L differences - const std::unique_ptr datain(new float[bfwr * bfhr]); - const std::unique_ptr dataout(new float[bfwr * bfhr]); - const std::unique_ptr dE(new float[bfwr * bfhr]); - + const bool exec = (lp.expmet == 1 && linear > 0.f && lp.laplacexp > 0.1f && !lp.invex); if(!exec) { - float diffde = 100.f - lp.sensex;//the more scope, the less take into account dE for Laplace - deltaEforLaplace(dE.get(), diffde, bfwr, bfhr, bufexporig, hueref, chromaref, lumaref); - float lap = 1.f; - float alap = 600.f; - float blap = 100.f; + //Laplacian PDE before exposure to smooth L, algorithm exposure leads to increase L differences + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); + const std::unique_ptr dE(new float[bfwr * bfhr]); + const float cexp_scale = std::pow(2.f, lp.expcomp); + const float ccomp = (rtengine::max(0.f, lp.expcomp) + 1.f) * lp.hlcomp / 100.f; + const float cshoulder = ((maxran / rtengine::max(1.0f, cexp_scale)) * (lp.hlcompthr / 200.f)) + 0.1f; + const float chlrange = maxran - cshoulder; + const float diffde = 100.f - lp.sensex;//the more scope, the less take into account dE for Laplace - if(diffde > 80.f) { + deltaEforLaplace(dE.get(), diffde, bfwr, bfhr, bufexporig, hueref, chromaref, lumaref); + + constexpr float alap = 600.f; + constexpr float blap = 100.f; + constexpr float aa = (alap - blap) / 50.f; + constexpr float bb = 100.f - 30.f * aa; + + float lap; + if (diffde > 80.f) { lap = alap; - } - if(diffde < 30.f) { + } else if (diffde < 30.f) { lap = blap; - } - float aa = (alap - blap) / 50.f; - float bb = 100.f - 30.f * aa; - if(diffde >= 30.f && diffde <= 80.f) { + } else { lap = aa * diffde + bb; } + #ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) + #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif for (int y = 0; y < bfhr; y++) { for (int x = 0; x < bfwr; x++) { @@ -2460,42 +2453,13 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, i MyMutex::MyLock lock(*fftwMutex); ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, lap, 1.f, dE.get(), 0, 1, 1);//350 arbitrary value about 45% strength Laplacian #ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) if (multiThread) + #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif for (int y = 0; y < bfhr; y++) { for (int x = 0; x < bfwr; x++) { bufexporig->L[y][x] = dataout[y * bfwr + x]; } } - } - - - - if (exec) { - -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif - for (int ir = 0; ir < bfh; ir++) { - for (int jr = 0; jr < bfw; jr++) { - float L = bufexporig->L[ir][jr]; - const float Llin = LIM01(L / 32768.f); - const float addcomp = linear * (-kl * Llin + kl);//maximum about 1 . IL - const float exp_scale = pow_F(2.0, (lp.expcomp + addcomp)); - const float shoulder = ((maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr) + 0.1f; - const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; - const float hlrange = maxran - shoulder; - - //highlight - const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(exp_scale, comp, hlrange, 2 * L)); - L *= hlfactor * pow_F(2.f, addcomp);//approximation but pretty good with Laplacian and L < mean, hl aren't call - //shadow tone curve - L *= shtonecurve[2 * L]; - //tonecurve - lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; - } - } - } else { #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif @@ -2511,10 +2475,36 @@ void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, int bfhr, i lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; } } + } else { + constexpr float kl = 1.f; + const float hlcompthr = lp.hlcompthr / 200.f; + const float hlcomp = lp.hlcomp / 100.f; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float L = bufexporig->L[ir][jr]; + const float Llin = LIM01(L / 32768.f); + const float addcomp = linear * (-kl * Llin + kl);//maximum about 1 . IL + const float exp_scale = pow_F(2.f, lp.expcomp + addcomp); + const float shoulder = (maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr + 0.1f; + const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; + const float hlrange = maxran - shoulder; + + //highlight + const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(exp_scale, comp, hlrange, 2 * L)); + L *= hlfactor * pow_F(2.f, addcomp);//approximation but pretty good with Laplacian and L < mean, hl aren't call + //shadow tone curve + L *= shtonecurve[2 * L]; + //tonecurve + lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; + } + } } } - void ImProcFunctions::addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk) { // BENCHFUN From 11d68a71360cea97a14ad353df0765116503ca16 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sun, 5 Jul 2020 11:41:12 -0700 Subject: [PATCH 091/114] Fix autofill amount bug (#5826) I forgot to change some values when fixing the order of profiled distortion correction in the processing pipeline (#5595). --- rtengine/iptransform.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 292d96383..8a1142c17 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -516,7 +516,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, } if (pLCPMap && params->lensProf.useDist) { - pLCPMap->correctDistortion(x_d, y_d, 0, 0); + pLCPMap->correctDistortion(x_d, y_d, w2, h2); } // rotate From 67bfc351b24e92cd24f4fb6d85a079dfbab19aec Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 6 Jul 2020 07:59:37 +0200 Subject: [PATCH 092/114] Change french labels exposure --- rtdata/languages/Francais | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index dd44ab8d6..86df147e1 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1831,7 +1831,7 @@ TP_LOCALLAB_COLORDE;Couleur prévisualisation sélection ΔE - Intensité TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Prévisualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Prévisualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. TP_LOCALLAB_COLORSCOPE;Etendue Outils Couleur -TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Exposition (Standard), Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. +TP_LOCALLAB_COLORSCOPE_TOOLTIP;Utilise une étendue commune pour Couleur et lumière, Ombres Lumières, Vibrance.\nLes autres outils ont leur étendue spécifique. TP_LOCALLAB_COLOR_TOOLNAME;Couleur&Lumière (Défauts) - 11 TP_LOCALLAB_COL_NAME;Nom TP_LOCALLAB_COL_VIS;Statut @@ -1916,13 +1916,13 @@ TP_LOCALLAB_EXPLAP_TOOLTIP;Plus vous agissez sur ce curseur de seuil, plus grand TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Autorise de nombreuses possibilités de fusionner les images (comme les calques dans Photosshop) : difference, multiply, soft light, overlay...avec opacité...\nOriginale Image : fusionne le RT-spot en cours avec Originale.\nSpot Précédent : fusionne le RT-spot en cours avec le précédent - si il n'y a qu'un spot précédent = original.\nArrière plan : fusionne le RT-spot en cours avec la couleur et la luminance de l'arrière plan (moins de possibilités) TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : utilise un algorithme similaire à Exposure principal mais en L*a*b* et en prenant en compte le deltaE.\n\nLaplacian & PDE : utilise un autre algorithme aussi avec deltaE et avec l'équation de Poisson pour résoudre le Laplacien dans l'espace de Fourier.\nPDE IPOL, PDE Fattal et Standard peuvent être combinés.\nFFTW La transformée de Fourier est optimisée en taille pour réduire les temps de traitement.\nPDE reduit les artéfacts et le bruit. TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Applique un median avant la transformée de Laplace pour éviter les artéfacts (bruit).\nVous pouvez aussi utiliser l'outil "Réduction du bruit". -TP_LOCALLAB_EXPOSE;Exposition - PDE algorithmes +TP_LOCALLAB_EXPOSE;PDE algorithmes - Exposition TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez utiliser le module "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... TP_LOCALLAB_EXPRETITOOLS;Outils Retinex avancés TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUtiliser de basses valeurs de transition et de hautes valeurs de transition affaiblissement et Etendue pour simuler un petit RT-spot. TP_LOCALLAB_EXPTOOL;Outils exposition TP_LOCALLAB_EXPTRC;Courbe de réponse Tonale - TRC -TP_LOCALLAB_EXP_TOOLNAME;Exposition - Compression Dynamique - 10 +TP_LOCALLAB_EXP_TOOLNAME;Laplacien PDE -Compression Dynamique & Exposition- 10 TP_LOCALLAB_FATAMOUNT;Quantité TP_LOCALLAB_FATANCHOR;Ancre TP_LOCALLAB_FATANCHORA;Décalage From 4d49d142aa1eb3cd394001fe3583537428922171 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 6 Jul 2020 12:26:09 +0200 Subject: [PATCH 093/114] Simplify interface settings with hide/show tools --- rtdata/languages/default | 3 ++ rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 ++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 +- rtgui/controlspotpanel.cc | 102 +++++++++++++++++++++++++++++++++++++- rtgui/controlspotpanel.h | 7 +++ rtgui/locallab.cc | 26 ++++++++++ rtgui/paramsedited.cc | 7 +++ rtgui/paramsedited.h | 1 + 10 files changed, 153 insertions(+), 2 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 56c5edd2b..6105f36da 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1192,6 +1192,7 @@ HISTORY_MSG_950;Local - Mask Common GF strength HISTORY_MSG_951;Local - Mask Common GF angle HISTORY_MSG_952;Local - Mask Common soft radius HISTORY_MSG_953;Local - Mask Common blend chroma +HISTORY_MSG_954;Local - Show-hide tools HISTORY_MSG_BLSHAPE;Blur by level HISTORY_MSG_BLURCWAV;Blur chroma HISTORY_MSG_BLURWAV;Blur luminance @@ -2684,6 +2685,8 @@ TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n TP_LOCALLAB_PREVIEW;Preview ΔE +TP_LOCALLAB_PREVHIDE;Hide all tools +TP_LOCALLAB_PREVSHOW;Show all tools TP_LOCALLAB_PROXI;ΔE decay TP_LOCALLAB_QUALCURV_METHOD;Curves type TP_LOCALLAB_QUAL_METHOD;Global quality diff --git a/rtengine/procevents.h b/rtengine/procevents.h index afc6036f2..a9fb56172 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -976,6 +976,7 @@ enum ProcEventCode { Evlocallabang_mask = 950, Evlocallabsoftradiusmask = 951, Evlocallabblendmaskab = 952, + EvLocallabSpotprevMethod = 953, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index d177e44a2..99731c469 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2615,6 +2615,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : // Control spot settings name(""), isvisible(true), + prevMethod("hide"), shape("ELI"), spotMethod("norm"), wavMethod("D4"), @@ -3844,6 +3845,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const // Control spot settings name == other.name && isvisible == other.isvisible + && prevMethod == other.prevMethod && shape == other.shape && spotMethod == other.spotMethod && wavMethod == other.wavMethod @@ -5364,6 +5366,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo // Control spot settings saveToKeyfile(!pedited || spot_edited->name, "Locallab", "Name_" + index_str, spot.name, keyFile); saveToKeyfile(!pedited || spot_edited->isvisible, "Locallab", "Isvisible_" + index_str, spot.isvisible, keyFile); + saveToKeyfile(!pedited || spot_edited->prevMethod, "Locallab", "PrevMethod_" + index_str, spot.prevMethod, keyFile); saveToKeyfile(!pedited || spot_edited->shape, "Locallab", "Shape_" + index_str, spot.shape, keyFile); saveToKeyfile(!pedited || spot_edited->spotMethod, "Locallab", "SpotMethod_" + index_str, spot.spotMethod, keyFile); saveToKeyfile(!pedited || spot_edited->wavMethod, "Locallab", "WavMethod_" + index_str, spot.wavMethod, keyFile); @@ -7029,6 +7032,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) // Control spot settings assignFromKeyfile(keyFile, "Locallab", "Isvisible_" + index_str, pedited, spot.isvisible, spotEdited.isvisible); + assignFromKeyfile(keyFile, "Locallab", "PrevMethod_" + index_str, pedited, spot.prevMethod, spotEdited.prevMethod); assignFromKeyfile(keyFile, "Locallab", "Shape_" + index_str, pedited, spot.shape, spotEdited.shape); assignFromKeyfile(keyFile, "Locallab", "SpotMethod_" + index_str, pedited, spot.spotMethod, spotEdited.spotMethod); assignFromKeyfile(keyFile, "Locallab", "wavMethod_" + index_str, pedited, spot.wavMethod, spotEdited.wavMethod); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index a82e87dd1..f6ec6cb19 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -976,6 +976,7 @@ struct LocallabParams { // Control spot settings Glib::ustring name; bool isvisible; + Glib::ustring prevMethod; // show, hide Glib::ustring shape; // ELI, RECT Glib::ustring spotMethod; // norm, exc Glib::ustring wavMethod; // D2, D4, D6, D10, D14 diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 855f09572..cc71a78cf 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -979,7 +979,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabstr_mask LUMINANCECURVE, // Evlocallabang_mask LUMINANCECURVE, // Evlocallabsoftradiusmask - LUMINANCECURVE // Evlocallabblendmaskab + LUMINANCECURVE, // Evlocallabblendmaskab + LUMINANCECURVE // EvLocallabSpotprevMethod }; diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc index 5a1c76690..521026f19 100644 --- a/rtgui/controlspotpanel.cc +++ b/rtgui/controlspotpanel.cc @@ -48,6 +48,7 @@ ControlSpotPanel::ControlSpotPanel(): button_rename_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_REN")))), button_visibility_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_VIS")))), + prevMethod_(Gtk::manage(new MyComboBoxText())), shape_(Gtk::manage(new MyComboBoxText())), spotMethod_(Gtk::manage(new MyComboBoxText())), shapeMethod_(Gtk::manage(new MyComboBoxText())), @@ -92,6 +93,7 @@ ControlSpotPanel::ControlSpotPanel(): expMaskMerge_(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_MASFRAME")))), preview_(Gtk::manage(new Gtk::ToggleButton(M("TP_LOCALLAB_PREVIEW")))), + ctboxshape(Gtk::manage(new Gtk::HBox())), controlPanelListener(nullptr), lastObject_(-1), @@ -105,6 +107,18 @@ ControlSpotPanel::ControlSpotPanel(): { const bool showtooltip = options.showtooltip; + Gtk::HBox* const ctboxprevmethod = Gtk::manage(new Gtk::HBox()); + prevMethod_->append(M("TP_LOCALLAB_PREVHIDE")); + prevMethod_->append(M("TP_LOCALLAB_PREVSHOW")); + prevMethod_->set_active(0); + prevMethodconn_ = prevMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::prevMethodChanged)); + + ctboxprevmethod->pack_start(*prevMethod_); + pack_start(*ctboxprevmethod); + + Gtk::HBox* const hbox1_ = Gtk::manage(new Gtk::HBox(true, 4)); buttonaddconn_ = button_add_->signal_clicked().connect( sigc::mem_fun(*this, &ControlSpotPanel::on_button_add)); @@ -176,7 +190,8 @@ ControlSpotPanel::ControlSpotPanel(): scrolledwindow_->set_min_content_height(150); pack_start(*scrolledwindow_); - Gtk::HBox* const ctboxshape = Gtk::manage(new Gtk::HBox()); +// Gtk::HBox* const ctboxshape = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelshape = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_SHAPETYPE") + ":")); ctboxshape->pack_start(*labelshape, Gtk::PACK_SHRINK, 4); shape_->append(M("TP_LOCALLAB_ELI")); @@ -208,6 +223,7 @@ ControlSpotPanel::ControlSpotPanel(): ctboxspotmethod->pack_start(*spotMethod_); pack_start(*ctboxspotmethod); + excluFrame->set_label_align(0.025, 0.5); if (showtooltip) { @@ -782,6 +798,7 @@ void ControlSpotPanel::load_ControlSpot_param() const Gtk::TreeModel::Row row = *iter; // Load param in selected control spot + prevMethod_->set_active(row[spots_.prevMethod]); shape_->set_active(row[spots_.shape]); spotMethod_->set_active(row[spots_.spotMethod]); sensiexclu_->setValue((double)row[spots_.sensiexclu]); @@ -869,6 +886,55 @@ void ControlSpotPanel::shapeChanged() } } +void ControlSpotPanel::prevMethodChanged() +{ + // printf("prevMethodChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.prevMethod] = prevMethod_->get_active_row_number(); + + // Update Control Spot GUI according to spotMethod_ combobox state (to be compliant with updateParamVisibility function) + if (multiImage && prevMethod_->get_active_text() == M("GENERAL_UNCHANGED")) { + expTransGrad_->show(); + expShapeDetect_->show(); + expSpecCases_->show(); + expMaskMerge_->show(); + circrad_->show(); + ctboxshape->show(); + } else if (prevMethod_->get_active_row_number() == 0) { // Normal case + expTransGrad_->hide(); + expShapeDetect_->hide(); + expSpecCases_->hide(); + expMaskMerge_->hide(); + circrad_->hide(); + ctboxshape->hide(); + + } else { // Excluding case + expTransGrad_->show(); + expShapeDetect_->show(); + expSpecCases_->show(); + expMaskMerge_->show(); + circrad_->show(); + ctboxshape->show(); + } + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotprevMethod, prevMethod_->get_active_text()); + } +} + + + void ControlSpotPanel::spotMethodChanged() { // printf("spotMethodChanged\n"); @@ -1113,6 +1179,32 @@ void ControlSpotPanel::updateParamVisibility() } else { // Excluding case excluFrame->show(); } + + + if (multiImage && prevMethod_->get_active_text() == M("GENERAL_UNCHANGED")) { + expTransGrad_->show(); + expShapeDetect_->show(); + expSpecCases_->show(); + expMaskMerge_->show(); + circrad_->show(); + ctboxshape->show(); + } else if (prevMethod_->get_active_row_number() == 0) { // Normal case + expTransGrad_->hide(); + expShapeDetect_->hide(); + expSpecCases_->hide(); + expMaskMerge_->hide(); + circrad_->hide(); + ctboxshape->hide(); + } else { // Excluding case + expTransGrad_->show(); + expShapeDetect_->show(); + expSpecCases_->show(); + expMaskMerge_->show(); + circrad_->show(); + ctboxshape->show(); + } + + } void ControlSpotPanel::adjusterChanged(Adjuster* a, double newval) @@ -1341,6 +1433,9 @@ void ControlSpotPanel::adjusterChanged(Adjuster* a, double newval) } } + + + void ControlSpotPanel::avoidChanged() { // printf("avoidChanged\n"); @@ -1532,6 +1627,7 @@ void ControlSpotPanel::disableParamlistener(bool cond) buttonduplicateconn_.block(cond); buttonrenameconn_.block(cond); buttonvisibilityconn_.block(cond); + prevMethodconn_.block(cond); shapeconn_.block(cond); spotMethodconn_.block(cond); sensiexclu_->block(cond); @@ -1573,6 +1669,7 @@ void ControlSpotPanel::setParamEditable(bool cond) { // printf("setParamEditable: %d\n", cond); + prevMethod_->set_sensitive(cond); shape_->set_sensitive(cond); spotMethod_->set_sensitive(cond); sensiexclu_->set_sensitive(cond); @@ -2229,6 +2326,7 @@ ControlSpotPanel::SpotRow* ControlSpotPanel::getSpot(const int index) if (i == index) { r->name = row[spots_.name]; r->isvisible = row[spots_.isvisible]; + r->prevMethod = row[spots_.prevMethod]; r->shape = row[spots_.shape]; r->spotMethod = row[spots_.spotMethod]; // r->mergeMethod = row[spots_.mergeMethod]; @@ -2361,6 +2459,7 @@ void ControlSpotPanel::addControlSpot(SpotRow* newSpot) row[spots_.name] = newSpot->name; row[spots_.isvisible] = newSpot->isvisible; row[spots_.curveid] = 0; // No associated curve + row[spots_.prevMethod] = newSpot->prevMethod; row[spots_.shape] = newSpot->shape; row[spots_.spotMethod] = newSpot->spotMethod; row[spots_.sensiexclu] = newSpot->sensiexclu; @@ -2473,6 +2572,7 @@ ControlSpotPanel::ControlSpots::ControlSpots() add(name); add(isvisible); add(curveid); + add(prevMethod); add(shape); add(spotMethod); add(sensiexclu); diff --git a/rtgui/controlspotpanel.h b/rtgui/controlspotpanel.h index f2bfa1fd0..01d939aec 100644 --- a/rtgui/controlspotpanel.h +++ b/rtgui/controlspotpanel.h @@ -50,6 +50,7 @@ public: struct SpotRow { Glib::ustring name; bool isvisible; + int prevMethod; // 0 = Normal, 1 = Excluding int shape; // 0 = Ellipse, 1 = Rectangle int spotMethod; // 0 = Normal, 1 = Excluding int sensiexclu; @@ -231,6 +232,7 @@ private: void controlspotChanged(); + void prevMethodChanged(); void shapeChanged(); void spotMethodChanged(); void shapeMethodChanged(); @@ -276,6 +278,7 @@ private: Gtk::TreeModelColumn name; Gtk::TreeModelColumn isvisible; Gtk::TreeModelColumn curveid; // Associated curve id + Gtk::TreeModelColumn prevMethod; // 0 = hide, 1 = show Gtk::TreeModelColumn shape; // 0 = Ellipse, 1 = Rectangle Gtk::TreeModelColumn spotMethod; // 0 = Normal, 1 = Excluding Gtk::TreeModelColumn sensiexclu; @@ -349,6 +352,8 @@ private: Gtk::Button* const button_visibility_; sigc::connection buttonvisibilityconn_; + MyComboBoxText* const prevMethod_; + sigc::connection prevMethodconn_; MyComboBoxText* const shape_; sigc::connection shapeconn_; MyComboBoxText* const spotMethod_; @@ -408,6 +413,8 @@ private: Gtk::ToggleButton* const preview_; sigc::connection previewConn_; + Gtk::HBox* const ctboxshape; + // Internal variables ControlPanelListener* controlPanelListener; int lastObject_; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 80b92122a..274b5b43c 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -261,6 +261,12 @@ void Locallab::read(const rtengine::procparams::ProcParams* pp, const ParamsEdit r->shape = 1; } + if (pp->locallab.spots.at(i).prevMethod == "hide") { + r->prevMethod = 0; + } else { + r->prevMethod = 1; + } + if (pp->locallab.spots.at(i).spotMethod == "norm") { r->spotMethod = 0; } else { @@ -409,6 +415,13 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited r->shape = 1; } + if (newSpot->prevMethod == "hide") { + r->prevMethod = 0; + } else { + r->prevMethod = 1; + } + + if (newSpot->spotMethod == "norm") { r->spotMethod = 0; } else { @@ -686,6 +699,12 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited r->shape = 1; } + if (newSpot->prevMethod == "hide") { + r->prevMethod = 0; + } else { + r->prevMethod = 1; + } + if (newSpot->spotMethod == "norm") { r->spotMethod = 0; } else { @@ -845,6 +864,13 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited pp->locallab.spots.at(pp->locallab.selspot).shape = "RECT"; } + if (r->prevMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).prevMethod = "hide"; + } else { + pp->locallab.spots.at(pp->locallab.selspot).prevMethod = "show"; + } + + if (r->spotMethod == 0) { pp->locallab.spots.at(pp->locallab.selspot).spotMethod = "norm"; } else { diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 5e4b25042..bfcd97c46 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1024,6 +1024,7 @@ void ParamsEdited::initFrom(const std::vector& // Control spot settings locallab.spots.at(j).name = locallab.spots.at(j).name && pSpot.name == otherSpot.name; locallab.spots.at(j).isvisible = locallab.spots.at(j).isvisible && pSpot.isvisible == otherSpot.isvisible; + locallab.spots.at(j).prevMethod = locallab.spots.at(j).prevMethod && pSpot.prevMethod == otherSpot.prevMethod; locallab.spots.at(j).shape = locallab.spots.at(j).shape && pSpot.shape == otherSpot.shape; locallab.spots.at(j).spotMethod = locallab.spots.at(j).spotMethod && pSpot.spotMethod == otherSpot.spotMethod; locallab.spots.at(j).wavMethod = locallab.spots.at(j).wavMethod && pSpot.wavMethod == otherSpot.wavMethod; @@ -3062,6 +3063,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).isvisible = mods.locallab.spots.at(i).isvisible; } + if (locallab.spots.at(i).prevMethod) { + toEdit.locallab.spots.at(i).prevMethod = mods.locallab.spots.at(i).prevMethod; + } + if (locallab.spots.at(i).shape) { toEdit.locallab.spots.at(i).shape = mods.locallab.spots.at(i).shape; } @@ -6048,6 +6053,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : // Control spot settings name(v), isvisible(v), + prevMethod(v), shape(v), spotMethod(v), wavMethod(v), @@ -6530,6 +6536,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) { name = v; isvisible = v; + prevMethod = v; shape = v; spotMethod = v; wavMethod = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 327f3a370..a388db616 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -390,6 +390,7 @@ public: // Control spot settings bool name; bool isvisible; + bool prevMethod; bool shape; bool spotMethod; bool wavMethod; From 80fdb3e7c03da6c1b9b4acd10840ee4e6ab26ffa Mon Sep 17 00:00:00 2001 From: Ingo Weyrich Date: Mon, 6 Jul 2020 18:05:21 +0200 Subject: [PATCH 094/114] Update AUTHORS.txt Added Wayne Sutton for his contribution to RawPedia --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index aae24ff63..b2d1333d2 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -65,6 +65,7 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati Alberto Righetto Kostia (Kildor) Romanov Kalle Söderman + Wayne Sutton Johan Thor Vitalis Tiknius TooWaBoo From 9be94d145650d342d36e2401253e4f1aa4fbbf07 Mon Sep 17 00:00:00 2001 From: Desmis Date: Mon, 6 Jul 2020 19:23:18 +0200 Subject: [PATCH 095/114] Some changes in french datas --- rtdata/languages/Francais | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 86df147e1..3335533ec 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2108,6 +2108,8 @@ TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n TP_LOCALLAB_PREVIEW;Prévisualisation ΔE +TP_LOCALLAB_PREVHIDE;Cacher tous les outils +TP_LOCALLAB_PREVSHOW;Montrer tous les outils TP_LOCALLAB_PROXI;ΔE Affaiblissement TP_LOCALLAB_QUALCURV_METHOD;Types de Courbes TP_LOCALLAB_QUAL_METHOD;Qualité globale From 89a9d1125c3616484cfc2b528d1c25a417b5dfb7 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 7 Jul 2020 08:20:34 +0200 Subject: [PATCH 096/114] Add in settings enable disable Spot --- rtdata/languages/Francais | 1 + rtdata/languages/default | 2 ++ rtengine/iplocallab.cc | 31 ++++++++++++++++------------- rtengine/procevents.h | 1 + rtengine/procparams.cc | 4 ++++ rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/controlspotpanel.cc | 42 ++++++++++++++++++++++++++++++++++++++- rtgui/controlspotpanel.h | 5 +++++ rtgui/locallab.cc | 4 ++++ rtgui/paramsedited.cc | 7 +++++++ rtgui/paramsedited.h | 1 + 12 files changed, 86 insertions(+), 16 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 3335533ec..1f160acea 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1752,6 +1752,7 @@ TP_LOCALCONTRAST_LABEL;Contraste Local TP_LOCALCONTRAST_LIGHTNESS;Niveau des hautes-lumières TP_LOCALCONTRAST_RADIUS;Rayon TP_LOCALLAB_ACTIV;Luminosité seulement +TP_LOCALLAB_ACTIVSPOT;Activer le Spot TP_LOCALLAB_ADJ;Egalisateur Bleu-jaune Rouge-vert TP_LOCALLAB_ALL;Toutes les rubriques TP_LOCALLAB_AMOUNT;Quantité diff --git a/rtdata/languages/default b/rtdata/languages/default index 6105f36da..9f6c2c170 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1193,6 +1193,7 @@ HISTORY_MSG_951;Local - Mask Common GF angle HISTORY_MSG_952;Local - Mask Common soft radius HISTORY_MSG_953;Local - Mask Common blend chroma HISTORY_MSG_954;Local - Show-hide tools +HISTORY_MSG_955;Local - Enable Spot HISTORY_MSG_BLSHAPE;Blur by level HISTORY_MSG_BLURCWAV;Blur chroma HISTORY_MSG_BLURWAV;Blur luminance @@ -2329,6 +2330,7 @@ TP_LOCALCONTRAST_LABEL;Local Contrast TP_LOCALCONTRAST_LIGHTNESS;Lightness level TP_LOCALCONTRAST_RADIUS;Radius TP_LOCALLAB_ACTIV;Luminance only +TP_LOCALLAB_ACTIVSPOT;Enable Spot TP_LOCALLAB_ADJ;Equalizer Blue-yellow Red-green TP_LOCALLAB_ALL;All rubrics TP_LOCALLAB_AMOUNT;Amount diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index e138444a2..b9d070e14 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -684,6 +684,7 @@ struct local_params { bool fftma; float blurma; float contma; + bool activspot; }; @@ -1357,25 +1358,26 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall for (int y = 0; y < 5; y++) { lp.mullocsh[y] = multish[y]; } + lp.activspot = locallab.spots.at(sp).activ; - lp.logena = locallab.spots.at(sp).explog; + lp.logena = locallab.spots.at(sp).explog && lp.activspot; lp.detailsh = locallab.spots.at(sp).detailSH; lp.threshol = thresho; lp.chromacb = chromcbdl; - lp.expvib = locallab.spots.at(sp).expvibrance; - lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask - lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; - lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; - lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible - lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible - lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && ll_Mask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible - lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; - lp.maskena = locallab.spots.at(sp).expmask && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.expvib = locallab.spots.at(sp).expvibrance && lp.activspot ; + lp.colorena = locallab.spots.at(sp).expcolor && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask + lp.blurena = locallab.spots.at(sp).expblur && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.tonemapena = locallab.spots.at(sp).exptonemap && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.retiena = locallab.spots.at(sp).expreti && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.lcena = locallab.spots.at(sp).expcontrast && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0 && ll_Mask == 0; + lp.cbdlena = locallab.spots.at(sp).expcbdl && lp.activspot && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.exposena = locallab.spots.at(sp).expexpose && lp.activspot && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible + lp.hsena = locallab.spots.at(sp).expshadhigh && lp.activspot && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0 && ll_Mask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible + lp.vibena = locallab.spots.at(sp).expvibrance && lp.activspot && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && ll_Mask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.sharpena = locallab.spots.at(sp).expsharp && lp.activspot && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.sfena = locallab.spots.at(sp).expsoft && lp.activspot && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0 && ll_Mask == 0; + lp.maskena = locallab.spots.at(sp).expmask && lp.activspot && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible lp.sensv = local_sensiv; lp.past = chromaPastel; @@ -1421,6 +1423,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall lp.sens = lp.senscolor; lp.sensv = lp.senscolor; lp.senshs = lp.senscolor; + } static void calcTransitionrect(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index a9fb56172..efe793696 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -977,6 +977,7 @@ enum ProcEventCode { Evlocallabsoftradiusmask = 951, Evlocallabblendmaskab = 952, EvLocallabSpotprevMethod = 953, + Evlocallabactiv = 954, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 99731c469..bbc80fa3a 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2639,6 +2639,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : colorscope(30.0), transitweak(1.0), transitgrad(0.0), + activ(true), avoid(false), blwh(false), recurs(false), @@ -3869,6 +3870,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && colorscope == other.colorscope && transitweak == other.transitweak && transitgrad == other.transitgrad + && activ == other.activ && avoid == other.avoid && blwh == other.blwh && recurs == other.recurs @@ -5390,6 +5392,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->colorscope, "Locallab", "Colorscope_" + index_str, spot.colorscope, keyFile); saveToKeyfile(!pedited || spot_edited->transitweak, "Locallab", "Transitweak_" + index_str, spot.transitweak, keyFile); saveToKeyfile(!pedited || spot_edited->transitgrad, "Locallab", "Transitgrad_" + index_str, spot.transitgrad, keyFile); + saveToKeyfile(!pedited || spot_edited->activ, "Locallab", "Activ_" + index_str, spot.activ, keyFile); saveToKeyfile(!pedited || spot_edited->avoid, "Locallab", "Avoid_" + index_str, spot.avoid, keyFile); saveToKeyfile(!pedited || spot_edited->blwh, "Locallab", "Blwh_" + index_str, spot.blwh, keyFile); saveToKeyfile(!pedited || spot_edited->recurs, "Locallab", "Recurs_" + index_str, spot.recurs, keyFile); @@ -7056,6 +7059,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Colorscope_" + index_str, pedited, spot.colorscope, spotEdited.colorscope); assignFromKeyfile(keyFile, "Locallab", "Transitweak_" + index_str, pedited, spot.transitweak, spotEdited.transitweak); assignFromKeyfile(keyFile, "Locallab", "Transitgrad_" + index_str, pedited, spot.transitgrad, spotEdited.transitgrad); + assignFromKeyfile(keyFile, "Locallab", "Activ_" + index_str, pedited, spot.activ, spotEdited.activ); assignFromKeyfile(keyFile, "Locallab", "Avoid_" + index_str, pedited, spot.avoid, spotEdited.avoid); assignFromKeyfile(keyFile, "Locallab", "Blwh_" + index_str, pedited, spot.blwh, spotEdited.blwh); assignFromKeyfile(keyFile, "Locallab", "Recurs_" + index_str, pedited, spot.recurs, spotEdited.recurs); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index f6ec6cb19..a88ef61cb 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1000,6 +1000,7 @@ struct LocallabParams { double colorscope; double transitweak; double transitgrad; + bool activ; bool avoid; bool blwh; bool recurs; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index cc71a78cf..b7e70fda3 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -980,7 +980,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabang_mask LUMINANCECURVE, // Evlocallabsoftradiusmask LUMINANCECURVE, // Evlocallabblendmaskab - LUMINANCECURVE // EvLocallabSpotprevMethod + LUMINANCECURVE, // EvLocallabSpotprevMethod + LUMINANCECURVE // Evlocallabactiv }; diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc index 521026f19..33936324e 100644 --- a/rtgui/controlspotpanel.cc +++ b/rtgui/controlspotpanel.cc @@ -79,6 +79,7 @@ ControlSpotPanel::ControlSpotPanel(): scopemask_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCOPEMASK"), 0, 100, 1, 60))), lumask_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LUMASK"), 0, 30, 1, 10))), + activ_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ACTIVSPOT")))), avoid_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_AVOID")))), blwh_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_BLWH")))), recurs_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_RECURS")))), @@ -190,8 +191,11 @@ ControlSpotPanel::ControlSpotPanel(): scrolledwindow_->set_min_content_height(150); pack_start(*scrolledwindow_); -// Gtk::HBox* const ctboxshape = Gtk::manage(new Gtk::HBox()); + Gtk::HBox* const ctboxactivmethod = Gtk::manage(new Gtk::HBox()); + ctboxactivmethod->pack_start(*activ_); + pack_start(*ctboxactivmethod); +// Gtk::HBox* const ctboxshape = Gtk::manage(new Gtk::HBox()); Gtk::Label* const labelshape = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_SHAPETYPE") + ":")); ctboxshape->pack_start(*labelshape, Gtk::PACK_SHRINK, 4); shape_->append(M("TP_LOCALLAB_ELI")); @@ -245,6 +249,7 @@ ControlSpotPanel::ControlSpotPanel(): excluFrame->add(*excluBox); pack_start(*excluFrame); + Gtk::HBox* const ctboxshapemethod = Gtk::manage(new Gtk::HBox()); Gtk::Label* const labelshapemethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_STYPE") + ":")); ctboxshapemethod->pack_start(*labelshapemethod, Gtk::PACK_SHRINK, 4); @@ -387,6 +392,9 @@ ControlSpotPanel::ControlSpotPanel(): pack_start(*artifBox2); ToolParamBlock* const specCaseBox = Gtk::manage(new ToolParamBlock()); + activConn_ = activ_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::activChanged)); + avoidConn_ = avoid_->signal_toggled().connect( sigc::mem_fun(*this, &ControlSpotPanel::avoidChanged)); specCaseBox->pack_start(*avoid_); @@ -823,6 +831,7 @@ void ControlSpotPanel::load_ControlSpot_param() balanh_->setValue((double)row[spots_.balanh]); colorde_->setValue((double)row[spots_.colorde]); colorscope_->setValue((double)row[spots_.colorscope]); + activ_->set_active(row[spots_.activ]); avoid_->set_active(row[spots_.avoid]); blwh_->set_active(row[spots_.blwh]); recurs_->set_active(row[spots_.recurs]); @@ -1461,6 +1470,32 @@ void ControlSpotPanel::avoidChanged() } } +void ControlSpotPanel::activChanged() +{ + // printf("activChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.activ] = activ_->get_active(); + + // Raise event + if (listener) { + if (activ_->get_active()) { + listener->panelChanged(Evlocallabactiv, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabactiv, M("GENERAL_DISABLED")); + } + } +} + + void ControlSpotPanel::blwhChanged() { // printf("blwhChanged\n"); @@ -1652,6 +1687,7 @@ void ControlSpotPanel::disableParamlistener(bool cond) balanh_->block(cond); colorde_->block(cond); colorscope_->block(cond); + activConn_.block(cond); avoidConn_.block(cond); blwhConn_.block(cond); recursConn_.block(cond); @@ -1694,6 +1730,7 @@ void ControlSpotPanel::setParamEditable(bool cond) balanh_->set_sensitive(cond); colorde_->set_sensitive(cond); colorscope_->set_sensitive(cond); + activ_->set_sensitive(cond); avoid_->set_sensitive(cond); blwh_->set_sensitive(cond); recurs_->set_sensitive(cond); @@ -2355,6 +2392,7 @@ ControlSpotPanel::SpotRow* ControlSpotPanel::getSpot(const int index) r->transitgrad = row[spots_.transitgrad]; r->scopemask = row[spots_.scopemask]; r->lumask = row[spots_.lumask]; + r->activ = row[spots_.activ]; r->avoid = row[spots_.avoid]; r->blwh = row[spots_.blwh]; r->recurs = row[spots_.recurs]; @@ -2484,6 +2522,7 @@ void ControlSpotPanel::addControlSpot(SpotRow* newSpot) row[spots_.balanh] = newSpot->balanh; row[spots_.colorde] = newSpot->colorde; row[spots_.colorscope] = newSpot->colorscope; + row[spots_.activ] = newSpot->activ; row[spots_.avoid] = newSpot->avoid; row[spots_.blwh] = newSpot->blwh; row[spots_.recurs] = newSpot->recurs; @@ -2597,6 +2636,7 @@ ControlSpotPanel::ControlSpots::ControlSpots() add(balanh); add(colorde); add(colorscope); + add(activ); add(avoid); add(blwh); add(recurs); diff --git a/rtgui/controlspotpanel.h b/rtgui/controlspotpanel.h index 01d939aec..81a79fb61 100644 --- a/rtgui/controlspotpanel.h +++ b/rtgui/controlspotpanel.h @@ -75,6 +75,7 @@ public: double balanh; double colorde; double colorscope; + bool activ; bool avoid; bool blwh; bool recurs; @@ -244,6 +245,7 @@ private: void adjusterChanged(Adjuster* a, double newval) override; + void activChanged(); void avoidChanged(); void blwhChanged(); void recursChanged(); @@ -303,6 +305,7 @@ private: Gtk::TreeModelColumn balanh; Gtk::TreeModelColumn colorde; Gtk::TreeModelColumn colorscope; + Gtk::TreeModelColumn activ; Gtk::TreeModelColumn avoid; Gtk::TreeModelColumn blwh; Gtk::TreeModelColumn recurs; @@ -390,6 +393,8 @@ private: Adjuster* const scopemask_; Adjuster* const lumask_; + Gtk::CheckButton* const activ_; + sigc::connection activConn_; Gtk::CheckButton* const avoid_; sigc::connection avoidConn_; Gtk::CheckButton* const blwh_; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 274b5b43c..2f25490fe 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -311,6 +311,7 @@ void Locallab::read(const rtengine::procparams::ProcParams* pp, const ParamsEdit r->balanh = pp->locallab.spots.at(i).balanh; r->colorde = pp->locallab.spots.at(i).colorde; r->colorscope = pp->locallab.spots.at(i).colorscope; + r->activ = pp->locallab.spots.at(i).activ; r->avoid = pp->locallab.spots.at(i).avoid; r->blwh = pp->locallab.spots.at(i).blwh; r->recurs = pp->locallab.spots.at(i).recurs; @@ -485,6 +486,7 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited r->balanh = newSpot->balanh; r->colorde = newSpot->colorde; r->colorscope = newSpot->colorscope; + r->activ = newSpot->activ; r->avoid = newSpot->avoid; r->blwh = newSpot->blwh; r->recurs = newSpot->recurs; @@ -768,6 +770,7 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited r->balanh = newSpot->balanh; r->colorde = newSpot->colorde; r->colorscope = newSpot->colorscope; + r->activ = newSpot->activ; r->avoid = newSpot->avoid; r->blwh = newSpot->blwh; r->recurs = newSpot->recurs; @@ -915,6 +918,7 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited pp->locallab.spots.at(pp->locallab.selspot).balanh = r->balanh; pp->locallab.spots.at(pp->locallab.selspot).colorde = r->colorde; pp->locallab.spots.at(pp->locallab.selspot).colorscope = r->colorscope; + pp->locallab.spots.at(pp->locallab.selspot).activ = r->activ; pp->locallab.spots.at(pp->locallab.selspot).avoid = r->avoid; pp->locallab.spots.at(pp->locallab.selspot).blwh = r->blwh; pp->locallab.spots.at(pp->locallab.selspot).recurs = r->recurs; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index bfcd97c46..39b89dbf3 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1048,6 +1048,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).colorscope = locallab.spots.at(j).colorscope && pSpot.colorscope == otherSpot.colorscope; locallab.spots.at(j).transitweak = locallab.spots.at(j).transitweak && pSpot.transitweak == otherSpot.transitweak; locallab.spots.at(j).transitgrad = locallab.spots.at(j).transitgrad && pSpot.transitgrad == otherSpot.transitgrad; + locallab.spots.at(j).activ = locallab.spots.at(j).activ && pSpot.activ == otherSpot.activ; locallab.spots.at(j).avoid = locallab.spots.at(j).avoid && pSpot.avoid == otherSpot.avoid; locallab.spots.at(j).blwh = locallab.spots.at(j).blwh && pSpot.blwh == otherSpot.blwh; locallab.spots.at(j).recurs = locallab.spots.at(j).recurs && pSpot.recurs == otherSpot.recurs; @@ -3159,6 +3160,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).transitgrad = mods.locallab.spots.at(i).transitgrad; } + if (locallab.spots.at(i).activ) { + toEdit.locallab.spots.at(i).activ = mods.locallab.spots.at(i).activ; + } + if (locallab.spots.at(i).avoid) { toEdit.locallab.spots.at(i).avoid = mods.locallab.spots.at(i).avoid; } @@ -6077,6 +6082,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : colorscope(v), transitweak(v), transitgrad(v), + activ(v), avoid(v), blwh(v), recurs(v), @@ -6560,6 +6566,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) colorscope = v; transitweak = v; transitgrad = v; + activ = v; avoid = v; blwh = v; recurs = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index a388db616..923b07039 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -414,6 +414,7 @@ public: bool colorscope; bool transitweak; bool transitgrad; + bool activ; bool avoid; bool blwh; bool recurs; From 63633a61c4b99560b5867e9862d6625328765683 Mon Sep 17 00:00:00 2001 From: Desmis Date: Tue, 7 Jul 2020 09:36:44 +0200 Subject: [PATCH 097/114] Chnage labels show hide settings --- rtdata/languages/Francais | 4 ++-- rtdata/languages/default | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 1f160acea..b85a969b1 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2109,8 +2109,8 @@ TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n TP_LOCALLAB_PREVIEW;Prévisualisation ΔE -TP_LOCALLAB_PREVHIDE;Cacher tous les outils -TP_LOCALLAB_PREVSHOW;Montrer tous les outils +TP_LOCALLAB_PREVHIDE;Cacher tous les réglages +TP_LOCALLAB_PREVSHOW;Montrer tous les réglages TP_LOCALLAB_PROXI;ΔE Affaiblissement TP_LOCALLAB_QUALCURV_METHOD;Types de Courbes TP_LOCALLAB_QUAL_METHOD;Qualité globale diff --git a/rtdata/languages/default b/rtdata/languages/default index 9f6c2c170..dea86eba4 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2456,7 +2456,7 @@ TP_LOCALLAB_DETAILSH;Details TP_LOCALLAB_DETAILTHR;Detail threshold Luminance Chroma (DCT ƒ) TP_LOCALLAB_DUPLSPOTNAME;Copy TP_LOCALLAB_EDGFRA;Edge Sharpness -TP_LOCALLAB_EDGSHOW;Show all tools +TP_LOCALLAB_EDGSHOW;Show all tolls TP_LOCALLAB_ELI;Elipse TP_LOCALLAB_ENABLE_AFTER_MASK;Use Tone Mapping TP_LOCALLAB_ENABLE_MASK;Enable mask @@ -2687,8 +2687,8 @@ TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n TP_LOCALLAB_PREVIEW;Preview ΔE -TP_LOCALLAB_PREVHIDE;Hide all tools -TP_LOCALLAB_PREVSHOW;Show all tools +TP_LOCALLAB_PREVHIDE;Hide all settings +TP_LOCALLAB_PREVSHOW;Show all settings TP_LOCALLAB_PROXI;ΔE decay TP_LOCALLAB_QUALCURV_METHOD;Curves type TP_LOCALLAB_QUAL_METHOD;Global quality From f07653354ba18f254b9868d6156061f76f5ce513 Mon Sep 17 00:00:00 2001 From: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com> Date: Tue, 7 Jul 2020 13:33:03 +0200 Subject: [PATCH 098/114] Add Canon EOS 90D dual-illuminant profile #5835 --- rtdata/dcpprofiles/Canon EOS 90D.dcp | Bin 0 -> 1045662 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 rtdata/dcpprofiles/Canon EOS 90D.dcp diff --git a/rtdata/dcpprofiles/Canon EOS 90D.dcp b/rtdata/dcpprofiles/Canon EOS 90D.dcp new file mode 100644 index 0000000000000000000000000000000000000000..b65a8e7ac6aa6feba90351b612d63ebc101ac1f6 GIT binary patch literal 1045662 zcmZ6z1yoes`#p|;pD-VPPU726i9{)17pOgp?SlpeS>WA}F?^f&nTv zsMsz4^M2m%TL0IdYt6csVYm*P=RRjYd+#$-rpy^^Xn*C~RbA z$&X+7qv!vfJJm5WTU=Prj33Ne^2d_@JGbKJ-}ujV;YS(&=J&$-X7%`a3P1l**w`$o zo0-{Be*W|S&Rqm%X21A(UH;$R{9|VJo4@{_YyR^yfB1DK^5ea(b(H-|2?+s z@bB^d|NZ=vU;n-?x9Z&oq(tGloA z?LL0o%8j$OZd%iQr|nRiq5Pe4-1&q5m;d|S{`uO>On`qLjztgX!wD&HX&{8T!zo%f zJqeZ;B511OX+upS9GiN)?t(K0!Auk3_50YV6ot zM1z(@;K5!E?oBA6){DbY8?HsI`$H=08HT>WIyeq^LhHqbV9qQ(y6k*FT`hyr@Rc4p zJBw+MCJ>EP2HdVaK^<-x5H!XJw-;%&(oTEAor69kT}zk@<=@SBZjJ^H@(;`3QldLxZvD^c3csKpTlHmEi$Kr z+eP8UJUK?(wV+j15%|4cfwd31QbdH|iKi0OYdjqx4My+DD*SfXOszH;5j;c<%Y9m! zpRR{PZw

jiW2#b;#%D zZGjT4S{l&Kw4a{rpn%|z0S(@%>BMRo%;p(!?vO9-T~CIIjRUd!&Q4m~LyCZxf#`Qp zNUh(AvExY)K5Nu;L?PG^fa9w$Q9t$%y2v} zI7*A}1i)iU1U{>CY4!+zc&H=s^pb?0d@Y3U&G5f)zVT=oz1krPCu#(ky1Os6*%XcR zM?%zO{xNOX6@&4mBHXkqFx{FIi*xtHa4ZcqeRvuRr&kiZZ@<>mczhgKT^a88w>QO2 zibHTOIjZ6ZnBHZ_LhPbIlh#vBsva@WPvy0u2{!%qj>N4YD!41}nr1nL;l>{o9DcT- zPi%v6>#iE2#1XXjW+S@hYtS>qjkI}S zLre-BI;_VBp@90_m&0n90Y^5t)AH5|cwRQ3pTdp?ixo)oHsbvIw$yl7fw&fduupAC zJ?bi;Di6e`Go9#$3K@=E2*S_WLDZy^qP!p&!NpFrkF^9jS3*!fZ#K=nCBnAyP*k_` zpk7KL#(W4vb;=eRy-GWO9rvBSUfGYS{C9Z0xYTKPO#-f1DKWdX$Z7H0I9OSz;M3&DnY_2rxPC>2p(W;~ z3l}5csa4~^5`ihHBorwNH5ib1#&n}wFisBD;=`*?rjaQ|jI66exTFQ$xKfWYs)I}Y zZglHHEqs>g(J;0bJ#bi!#Bx2xcJ4?OQ7W`qYCy=Hy0qR175v{AaQ@~M)7efcn8}Tp z=MZD+{$2@5|3E-2F}+-_MDoKx6cV{<&wK^K4hA9XOSnnZRE{V^Fvh$;Vp3;H@k|hc z7W_J^CQ7hvPbkhty)xZ@Awr687!>a2bY83o`q5#3)$`SxA^EdfC!*&)0a{dSbL!DI z3311TxH6_S;y)+BKSG2Sn(1f}l#B%mF-CUY1(c@~TqkW@M`cjR&J@Jl{7Du2@ss`(h#plmFABy~~T3oy{&=kHV7|~;NaI4&8 z+CT!a|E&(%rD{`Ca|70D^cb+!U@H5h1?gZw?LfJy%2k8pI0Ni|tuW=aQlsA>BN`v? zY1(J4LZ-=x7Z=XuFW#&~sCyvti*55arYg|1UJ$I_&p+dHLXP7ng0MaG>*mY0LQhq9K}^MXNQ<-Bw{S@6#5s=GN}*hwK;&TR|Tk8mJXk~ zsW=iV#Lku%@H8tGy?61yQ*0T2_De(a#bO-(R*Br5X=pcD0^x+$2#QUEoJbKOeT&;M zX;|be!_wUMn6x1cttQFwK7@IWEqG#g_oV=`xI!F5D4*OOBBzMqZxv5X5awqA1=ePr$NX8 zL0Jnao*RR4;`THwek_J?atLntS2|S>lAvN}$X_@&as7&x?K80NtN^`&n~^?`GjK&F zM7Kfh$$PI%T$&@otks>#irh?i_ZMUE+AgH@UM4zONbotUGYP+yiMsD4$eiAZOiIke zt4C6-Z`6^DotTLpB{DQz+<`bB&A`C(a$K0(fqcJ_j*<)oek64uihXIA;itruy&Z^+ z?f`c7Q6X!o6?r^78KaM?ut(dH+-Cbh#;P&tM+4GuMLdKhYMA0bz{@`d32F`Uy>G+D zH4`^SWBo@FIt*|oQDYBd$T=~ziH_uP?IE0tl%R2hJxK{Wgvwn~ zZ2LHZIM+Rd3vMzr+cliX#%AG*t(^DzVPy80gZNcPflE#7$efxCM4VAz(!^1u+njVb zx+}4F)mXB5eJZ>kD)DZq6G>W;g6ioiBtCZ}ac2|ZQ>?#%Jw?nZdM+?#N=48~LAUp`tA$OxWS?*>;#s*&VVZSk? zLWfQx46r@^1-4-t^r$w#bm1cg?@>V%VMKT7N6e5YpmYvITA$B&dqjrp=Yg>9^bJ#M zB{-@Ig1Y7>3~R+G7#fVw!aAgDgarQnL4V;~vuYlhJ2)G8X#%))T~2yrWTW#uAu436 z$mz;#w5cz``u!`(^@rK$lqbUJCd){R_-w5Amh2->5{u(CVPuBLAaloh4m~=@+L80QY78H5z?PfVBsxrq8I6qy+c}tQ4wJ*?k`bN94J4t* zr5Gy>MBu0XWFhB=ug3%-Xxbps<*o!}zJY(?e4YA`Esi;O8!fw3)(c6i8vmAUJD8`Wfn+Wwjfl>Fx_}b2!Y%v^%Tqi*j zc#yNwWBBAO#g1LeNc{9{c)pinRM---`pprfgvnrByM%n%a2WT7%0X4j$n(2dSaw8? zWlo-CqT@kK@1($!1sloD*J)^}RN$rkPC}-n;QdDhW{(dbV=wJT-z7@q=Znc2|2V|* zwRKgAjPzL*jr(?-E1r=PqfZ2K&Zux&FD2b0L;0GahL^R7M7<2ctH&sMf9DG zzdU#z6vAPEggExfLje&%87v|p>Qkt>D?+CU0&+6bZaTavc`VIRdE~zYAH@LU$SFsHsb2YF#LxviR*9(4OB90 z+U`e=oJ_~~R=m!q_>&)zDfk-7>)%C4-f(_i+)jb6pT(rf+&DA{I;Gh~g zJ@jPE6Ai{s)!?OAN4yJFu&&YIh*3*wnF9ABwOHe+Ax^hs=;x}#E2zk1j`PNEI_%ge zAw!a-5I)fTh4W1df70J@4mY;*dQOv&N7{2}UMWD;4LLbJ<{Vng6ym*JNfKhu!uFI9 zujZ(T9b>TTBf^TsDl#{fLTnJB@FsuFA`>S55@Giv1rgmpgY|rEIG-dZMI%llql9yB zA|uVWo!8v5EkDuE5et zCCQ&1isoySu)nM(?M?>a+)E{Dr)o*dNk-J!tis!+IwJj|!@^%G6gSWl-8l{3^SP;b zmY)1b=Ny}B0u__>1nCNlq8gn0r6cwx8Tzi!!o0hVOyW3qGSlH%k%|n?lfwCp_Ai_} z6>K7_P86YXjQ|h6>?7?Wi?FLefQM8}#t$mO7~Vr_M$5_I?S(kCRfzg$mE^&`^JrBl z#3Q+yxXmoUsP-aUjn|Mi@6N(%od~@;YDu%z4B@9mcwDO?n@*TuY#|0#X-Mt6Ge}t@ z#_H8-qKwFcbAcEZJ}Q#wcLI*%B#7qszUz`BP!~(kAY4K0rJ3mC#(U07IkC4)#p6mT zT=a6{|2h%rTV$A(Cnp8{d35*8fbG=NYB!dFy1#BxvDw7 zI3NNI3K4YzNIou;C<%mOdXlPaemi=_nAT!Y0LX!*5kkHxj1DZIraDwj_~>1 zB4QnBpLGd!lLfHqu$y%3cL}Cf0>m1GWT0O$l7|Wr*-J*m#TVfr5F))oN$iU+!0wh1 znwtc#Pix7JvNZV1`0P1dOSG}cuxla1 zg0@=H-;{u)Xc^kI)RNz}v4|Wb$Jt?8a<4iPd8c`AU!x^4&%zKfQ-SB6ra_nELK^+S%8P9QkR#%Ix6%x|(j}()R>Hn%{^EewK7MJ1AegT@j98VTym*K`# zA*QsOPbx>2;qf;i7JXYq-jGs^PZJ@&$tLo#>JA3FbG_8tmwcFf8%JA6u=b~bykC6_ z56?^BIY~mwrrtpC9x3{&WMoaFYbY2ZgU?wx30QO)uee^bpq+v|{Cpl4;^lCuqabc7 z6O^kJ_>wLs-K8fX9iYT6YdL>SHrBpV;%1_Z%?XQ^Q9mB}4Bf z!*QYp?V3r+v-=5neFVrGpnQz1qb0Z#n6cW33df41BV%97nX&0oy;b^Y)B?w3}TNV1R4MLae0pxeJ z9QxtGaF{A2<2cTT>W5(Jv@N8sjSN>N{JjpY4XuV_+xytc`E#FNjmf|<_aR#(gjcJs zq&n{&lA4Jyy3bJJcDW39invA^?m)byrEummykPSbvi|EGd~lJVMmCT99&#Hu>Pit6 zzm%j8yM^ep9B1)r!kXQ{Pk{_>zt)nJomb)HAjg!a>qzC#A`E*cM^Myy60()yRjdN# z{_Dw*8F_Ga+Np%y^AxgtJr3Yzg7l)QET8!VklDtccg6yRh3xk%E6Yk*<=yXWRTuQD?2!T1* zsjbc|Aq$oVqFtFD8_q8xo=JM-?lVAMxPXN6eo)oJh<$86=@75NG0xw1=gcMf{QhX` z&GC;}L|iz|J6i`KX2DD%^Wt-G*55d94_b|>M<1a2kN|^R;*od#0Va6~(f3pl;^#cT zy51r_vsB@O_kGNNE<*i24ai%Yd+3oVMvp&j$f=xC+}tj~!5%${mE~QyK#DD{Lx^DT zZM1GALt(#>MA`2aviaKF(anK8io1?ioa@^;gUA{dgN#vNUlJkJOU_~UO$7p95|Y>Y z43@1{qHH!H^GOa?a7}GtdyvA4BiO{}v?Omw;`%xh4rXfDdygd|w^Ym*t0A?uCoelD zp%2$X-=XoSE9QN6l^yx2h{jN^Ym_~hq@A?^4li`@%PT7c>j2aVPizN zp1TGP?H}S4=WqGV!?3mLK91cIBP=HeYRNrxO_AX6&l0?OQ;N&HSH#bH3-yq@m_18| z(11EbFyS_;ddqSCjRn^_ZsJ9a96DnY(*5%#MDQ7K-O6S}()T&p%p$?=wXl!@@Im zx6ARprx+PFu1+pT9-`uh7zZc4bBbT^fX`17L=}&K>E=EBIVMF#gAF*;uMEau84mB^ z9*EmrY~3bD!<=Z83rbKif!}WrlHeJ41;vjQh`yPMb;k=4v{8x69%=Z|mtxFYC6tQ} zppC^Tv{|Eqvp5koOOD~s9Tk=;ViDf;5H@kGP53YzA(PV~PE%vgwLrYMo{V};HLx&f zustgQ@{Jm7fDDnPG1zxW1D9t3TrZ5on0{Ks`)tFfkD;h5)Z)-!FI>MHgoeDI-yFCE z0u-3t5^Gx*Qx|ENE>mYT;V*~#iHZRq2&M_LHgOu3Hy@t&LIG5r$d&=~faAqR9 z70KYw{$2;)ooh}Vt~`e19OulI)uyPL$GBkNzSZ!2Qy1UITpJdlb%S7&ckClHbQWXR z^2Me${uPk+kRU?Y+Vo~XIht}!V^FWud_m>|xWC~Xc-ZqZ9*yqf-EA4l=PMmAJD1_g z89CZ2Cp$f8cLy(m6_A!noyIh~hHfL3u!#(IQq~q>P@xiSa|4~S2}6@9DmW%eo!-AY zg+^z&j&y0Ylf35L+!2>HcRX?~KGXy%xs=6=%%ag~6#>i*E5<^H01DLhDJ~ zBeR{BfA9y#nd=^2Uc2+p+36tXGe~-jF+U(&jg>R`4Ct1U|B26_os#u9KPA<{iQ_z> zKG$7)ZO$({E5nWpf8%U%d?76ftwe`n0b-O+^!V9I9OC-3+rWNw$n;8V@e`rtnU=J{ zmM6HqOpHU*ewhYLe+=0q2~NK#HC_E!0c$%c<|$5^Ms6yHM=u%b$YM>6&pp7F)^emQ zSD4;b-9vxwt+;>MVse{)7me>L(Ai^^X=2Ok5CkX@9<$~#I);Y4CZpL(W5rfWH&DoM!qI~bB;ItcoPb{$vTX5Pd1e^ zJ}0K=uzp&)>CQz1X4cc=Max5`SU!JPEY~9~GusrkQjN@$Tx;r*YbtoDz>L-gG$~M- z{=s?U4g(G@NHc9sm!Zwrzx&3?g@N?ux@X9~F2E04DLt3)4AGfFw79>O9vS=$Rz?vP ztzJrBPkD+3d&QXWX$sxZp%P~|NbodfG+lo1F-$9@C=M7vkMRDZ=UV8_g3eUrRF3x3 zp+xTimh@-N1&p1bLiZMp=+a<@ z7hLntPHapgYfr(z`-8i&Dedw77@r-~IB~E!UAyxTCiK_fN<~Y$dtEw`Lp0ddz71V? zAsMszdN*uFJK9c?0J>HSE5G)%aZn7VUem%Wsv|As91ZNn^Ss7r}m~LwJLn%9?e9HzVwtr!MzFtoJmXS z$Z>A@(11hEU1=Mk3^S7d#(7!ANotz>0`u+);CwZcW@uhO%jfIzb_q1K?F*!&h)}vC zlos`Q4!;O7tlnzqm4{E!p6eB^8VP+eqZ0mHqiDtU(8z$t@R7;5zPyQU-%x=L0emj& zvxfez{}9G43Pg2UN!P!)i)T=xey8PhyTCPNT5ch(+U%-qF+$*eJM*Utujg1SJ2kuj$y=8HMTmfqPbQ{8AT(t zxD>L6X5WuT%S0^>Hd#xqd>QobgTje@;h#OBaim!e0F9&Er?h~XPHr_?mN+m2uMbZ3j*O11!1WS*kkzGNXg-0w=vy<))NBeC?YR*sJ&j5t0&f{x`l_en7F z{vJ&Qjb%`7{2OQg^9`7L_bMbl6~JF(#-rQrLVB< zrWi8S6Ph;WB@#HtC^>PDx(*Dg^edtBk5!@**EYvhUZr~q9%241ISy78Qs<@pzyH(GCUl%GFGiC36p@O#yYTH~`vJ3;)`4RPCOKKOvkOe>+F7 zkB-B=#yaHvK1Um_jY5DxhvrQR=!c3hH061Pn>`BX{V?w5a&38?V*y>FFv6i!57E*B z+ABkcpOX#vDkz}WeyH&**MPez1vGD~66XE+oOrc>zGx zbMzjS;xnHm|C^J^zuTF8Z~q3fssu29(vBGwZ{Yq^2za(+=AWt|=d(%GnkFnHtqKF* zijnxKA?r~53W;AOc-5vpldpV%KYX6oUNB=*Hb2F+TCU++{i4=i9>M1`_f68jQjx_& z2p%Y~h<&1q47Xtstc1_$57e*xGR6#1;X%*$)U%`jHm6m%R{D+()1E=)cwWO--cqyL z6Y#sJM#kqibYr_C0M{xHPkKWW^E2?FSc65UtLdPpDQGxFi>T?`?Q2!iZ;9RkY(5?)k`!SktbG3L<34`C-J~_Eq$fy972qf#{L( zn#TJ`G12~SKjrGd;mo_kJJk8W{qDv?*`3gLaQVRJl+*p0_RU-P{uJTBtzK-*<~NvW zDM9~L-IxhgSZ>Ar*7cp(H?Nm?(_IGrYb)06?Nj{fE=TO0HY~aF5gc0cEJ43ktZMHA z^y2St_pBD|z`_#TJfMWzyJoDh@)9hZRj|6!l-=2T4#iv-lRj+1YF$p_&{8#)wr|2N z6(5K3xf=018?!$@55b%B;f@~~G2i@jWOE;Cue1@n{VN%%JGEFgxDor4lmM^qJmXbq z$$F&5pbpQ~d`YonAHPRH{FCd$J1p4}LnyxO(SuI2WM5VVVFT|EGY46+v+E3awbOuy z%`MsO1TFkN8nE)a1?yH%jRBjuSMuC~d8-tt``U<~cP-c_?tM2|9SC~Kf~kIqF`H*U z%zj$1M(w$el=L^w<0ejJeR{md@oxg;G?>UG^Q(S**6(uQnjcV+Le8-j7NW4j_u7Z zZ0+}GROIT=Rn~L9Z+71GxHF5a)^%1y>y6~~xyFW_=J^P#(}9>a#fJHCe`<;J}Cbz(0I82=f)8VEs_?Peb;KI2R$5p?2hOmz7ZK93e-w2u#q+4up~ zZW4G8^JcYGZ;{4(km<4qTkg%XGQKjjbY8{+dOkxJK40e_U&vkyD$rnw0()cUGNHPR z=L(bj^BVi5^EqMtrXw z&qBEt(vWBHYVM3@FV8Enfa{`(+2dLEBEGiw3xadVc(%W(6m5?Np_AWucDY=H@Uc9j z1}E0%1J6Sy{y#XIt6284=P3Fiz{cqcb}H!w9@G;dv%8F~X3ufGwHUoK#B6!gQw;6R zb&MYZHrD+yzwf2E(S0uq>Gu$~2FpH!|gGgZ%kdrZm&6omgOqM=D4%BBagu;K#jSgCG2K$ z7SB8I9I19O`yH8vwRzm9E?mU+)=fsRn-&9lE@G1}b6t*WJ?kPCGV7{n+?MOGv+qJi z=0(7|s~(Q$7qBgVLNFv-j{{p5u~i<8BuxK zjWy?9mB&vb8XDbL=~x9Gssa(b-i?*Lm113oAY@E+V;@aov^fw2jg1?V^V#k>*P?~m zscZ^=-ty+(I5+TDw1d&|J&KBIkk@vg@ zQ5CZ)yoKDi67IXp+1Cfxu$O0$!dgpM5V?ej=VVB56R_lQg;>u$Q-{I6%wiftmAe8r zCv9a&$KmO$L94rj#gY-_MW(wdDJ}&pXq8`{#PuKjT@PmK7zMgZhv;P87`?uh~Qx~*cXxfW94Yeb9cm8{B# z>&(2K*RqvN#@Cs|lt4U=S;;zbtv7dU5QKu2>|GrReqIa0JpOlVGD!;aLCe6RcTp{>MX*7rSSixWbH^k%s0reHD1cD2cN_}J3glj5i++Y$FTi9?`yAp z*)92D*d9<|men@4ur?EIRw(hI`6l)xGaZlnsi5%Mz&<@o#r7{MJTx*pjV5`JX~h`UH<;xs zb3rz9|6q}UmDnD~;3-nL-dD5PqmE#DTNwtF%2?vVOjL1g-!WXs^0ueqvrLZK7QU>W zBpGJI6(~vB%zFDKV$352hGnd0))xEGocp0g+O=%jlXzIJ;P>sqHEhrWo*C|^#=@hk zS@Mfm40*4{6oUtw@ID$}(=~W=VihY)h=j;pi(i(jm}p5jbbL0c@L$RL3=G8&&Np(J ztz_jbf?@5cN6fJm?0Iuur+kjSyI}=e($|1XrwrI`w}O4Fsl$G9(yY0{3MlIJuIX+qe9hDT7kM2v~bK<0Jp z5W;OGa0u72m|j_M`or^)l}aXRlZgQ)Dek9AnB}E3WNhOe%1(c#-EsgvUAWHQbUT}D zmkceHLo{(cOBnU`N-K?s;yt?MutjY6a07~B4M?xQi0OiLSU%o}k53k| z7=GV)z2aG~OADE;LXC25AY^A3u~0o%(GpZJrW>i-FiYcNKeoQws5qzw7zT zA0sPha2Vb^FZ!rFhz&Y-nAemL`fEY#@c?Qte3N1&*nbK!rYHy zEWD&deD6jgpTSqWoz0r_*pb>!E6@F zb&(u1BitQkv$o|L$l{D#C!EWUrD$;bIM1PeaAP%mpG(Hpz`yGJzx;W0w1!npJ_P$5 z0lY`+nf^KVDRv66JzLMl79GNl(IN~9)G=YlLzwklgpZvyY}?m^*m|669ZCfY-I|G~ z-VzLLE@7_|({ZSc6tbuL*zD9)%sei|b>4@6#q-ShbQ$``^1gaB2}`)&{ZYT1h48G) zku7paD;6@xlmzr^q`<{3^O$p3JUn?GW%BOX%=%ay8qQLpj{8hD>18b5ed8RfWIB6V z6@wb?iTjcr}&n+7Jn+2Rzf|IF)ri8jgOuHK;l2%EB9jVOwi0+K+Q( zSRVqHEG=fen!>(R1R;s*&-M0CVcq#`m-vKxg6pQR@-JNP+{^RoWC|M}rpG>>*f!&DQWt&62FY^$oXIGWJTFg;tpY zB-(N=(d-b0E*IkMKs8f8%z~he2qV|1SmfX=?h%MEaD;-fRtNEEml$QYC9FI#1G~FR zuth3hnV-|Jgi0{K?JhQ&=Ywq)NU>qx1~zJ33OYUKb4|ZhY_DGu+HaO2yu%_k^VNRb z`!2)C9&=co_ypJ}(PDWEXVys{gktUks;?6^&M^>AdGDAXN0`@F1G4++5wggI z1^qH0%TbT%x5lyZ#vJb)Jsu66&a91ERQEOFAfE-U-qoU<`(FR8=iV;_tl;)Re2C}! zfG$f|k$o1-CkxSUw2YN~JIL2lAv#`^vRUH~q9RO$q~G?aqE2 z5b=Dx`Bb)u<2<~f9EC*_*o?$@yf?`4r-=)jb}bIx-T1oc&gG#taaegofsj>>%ym&L zZ28`cg5P7=mJiYBbW6$iCXHdP`=T(Iuc70cj$!I%k*KX!p@q9WD@qT?VPBqgxjdTL z&ke)G1{zo`9L=6L426l$@^(K)u@x7BVbx8G$%jU|p!4AH=;70VcQcXYt;= zR&9iEeJEg^I~+vkD?BrEM!@tdGokeo;Y~Mx=D#EZ?f!^hKY2G>*C8D(^kNi?HnWh- zRBY}b!N_G^Y*pU_sEL-KuisKu8j*}~9eEZyc`jSpB?*2(d|o~4%Djs9qnc~A6Zee! zpZ-(IJ%TYcj;!smcziOKA@MkwWY`+(2mBja$TxdP^Kc5IF@78k=6*ie5s zd*>E|Yn+qUoVR748b`x(|3CR6K1UZuq8aCkJAYa;p)vwDGE{gz&zfODIMVpunl@L4 zvRT$)&>UAIXxUIUr)4NMkI`U(`A`=7D;Sfx|K*%NgoShNW-(riQ+;fiv`sLkxoXj5 z;wToondhiS>d-M}G{wWbveRxLkk_+=awIAXTDTe!uWg{{Z zP~Jd>ZYd+!?F;b;@snZHcv}|QFdmg3Wyt$FgxM~ORdH&trzR)8_K;rH9G$2!xpRxg@${Pg7|(cGAsnXOEg#; zF^t&{2|~&lEi{vD*==1AT5)gp+a`Oa=Q*0-@W1nQ&ATmTO0MP700Ev>uVyE1^IEMZ zM8G?5Hg0VOR?CE#bABD`$LEMc4Y{|nW-S|-mxd&%2o^>UwlO6Yn`(JR_nA8@-*W&l z+r@}@HIKn11@)eZQQc%3i>jZD)3YQv|Ja2kUP(j_-xIgTVGO&GwI7qaNzwZ1Fs472 zfW~s}o8KDDvMb^t=ecz8kp3)mKs?$nm!TrEH!D=e!RML`XG?pq7T;rG?jpz4v)!0a z&sew}lf%%xD{~wkgOa`+^J|@%YEm?+BNXsH(}|g_ih{hU5?S{f#YqbE~p18BGq3M|H z!S_&DFJt|9hOld?5G$v+vzia7cr`_YYrhsS`sn~h<#LVs{cPs-Dh1^{<8dI#mDT5& zH>MP$p@$1QVM;<`HP7Yh?b-9xMC4DEz@n}#o1C~GZPFw-DH*`>PxE!Kp%e>N^kM_Q z#zW=BYhKuu@xTgH#XL)Pq$8`yiGvx>uH|G~v5Piwknw#1Q&zNP6|z`d=Kew>MQdhP z9D}U2a@??J#Y$Rm?st>V{#RSD(eBYW>Y%{Yi_O{6%qWENnY6+CX6%C*-v`}_XVe{< zG4-NIMEENal-!h^JRSl0YbDR5v}CvUaGk_Vg$5<VYm*{Fth{P!T0%^b8n); zr#o977z!O#qhyc`d&O(}uHbK+%}+Y8FJ9>=^cEn{VhXFz_u~ZL7GQOonGD5gm`jA{ zdSw=?ZIp%!sX{DYJd+u%Q?ZBV?&?NNWl56`V9^#4zK@v5-YrRiKli!3IKi1ROFYa*N^z=h3pVma98MXyNA{@+Te>a|Q+ZaTl~*H{&iP({?tQLUXu)h& z#iAki^;@SkU>+5`wk>%^eY`odToHq{>*Q$KsXhy@jOKX|IiB^X#}r&UUCZ^0kXd!v zviDJl)+_LoJjA@+AL4R-7R9o5AMvS$oZ^&n%8+m8`kzh0&*Kja0$)W!d?lmn9F_5)|M{3!3y7GOJ7Xp9N{$u0NFI0+*Hs9&eyRn%5n(q%; z`kAKfip47M%*wbA^iIcEObFq8GW8v;Ph(J4CBxnwZ>VBT4C;=PBPYCyK5iO=lt?)m zCp@R~W=7#~eJNIrcujk^;v9zewEx!mo%kQr=V1go$>ngH(3*{{42P8Ce@WVm*?kPd zP_B>t7w0)0+p(d2V==5Q*G1L6*v^HqXlpLO+>-;@2J2WjHxQus%n-JrG6uc*^VU~~ zup7DS3G8-Vs{59ra(QTSCi0L63f(#t)f_?~Njcvsw{Z3jgG zmHwFA{TlTe6@~k^{c-TqCHi7Q6yGcDk64$B^u_!r*qQv%=Sv~Iy(S8=$Nf>EJVobq zjKZ}aBFx!&lG|HR+^-O0UCUzH!66a`YblD(-JydU@pJBt{@K@p^_d!hW1QP$C;g_U zQ#pq`#`n(r@87?l3*l?`gl3%z#+PjYI4t^2Yla0OI>sO6x&~~seGqcX_aSI?BUUpz z2=#jIgLX?}7R2+v>Xm!3Q_+akQS&_rv3_VV-hx%<2ciG{J@{d1&i2&{#(t|kxcJqK z9iJVHhjV@Lt;bh-rA`R0)%At>-WRmb)etQ1?Tc+s@6vrdPpIDHi-)a?>5YD2IDXL= zZ5HO!gNMT4+jkFUYmQOcBOKQQ_P}LrCY>(~hyATRFi%LP2X2JJvb!G^e2$|DZ6mO4 zjUNWsMA0)mr|~<*4{Q2`(OO{yCOq*&>B%7a{UHAgiuQXk`ig8qj$%)2Q0tIprQ zzCypcM!;*M{4bolZwRKH-tcwyVF0eBr_*@bFtpzufIk&EbYYWF9C+=IZTCHYWL!(M=l*& z!8M#kd*Ocb2<^Wj6su?MMQz{#YW+GC`*nLUI3kL^;+(3F#XjtrWuO!8h2ccxJ}g`+ zqo0O_!)la2o<{r8Cp*KD#cQ&x&PF<&*G&G_0Ibrkpr0;=V=BMzF1(sglgq;~VYUEW zu1%vqUWQ|2q=4V66R33+U%ws;aB{X2ZTTV`!}|(h7Cf5%<~c}TZy{2Whtt>B!_hiJ zi2KpbbnusO-Y2=Q1Xi}?d9bJJ=;zbn zaQ+>D34OLuJ?A{FuLfY{m~FIQd>AID1i)LgmG;{d2BHbTjKWQ{@5V5^mIR<@hxIf{ z7lxl90mxH&(7hM9_Fl~Uz_9Hq?S+Wl-=1c39L8`y@8sNsx|eW`sH2EyZR^nIMd7%n7UAcnccw1~!||T)MVb{{ zZkny+IByi=_mgWTCC`>mdmzTA{^v|=KsYR1B}lN%HAV2xby#tNXYq@&Og6b;aOa=R zu~$)S+Q>5=3w}so)}YYjcrFYtxqp^y$rgJ#vT>Km7XU;24KgK6u^8x{8#-y4a^^QZePJlNJicQL$F?e)L0F&mS z=}OON1m6%~N9_w!4ewp~uLY?2|EN0exSId}{fq3(va@G|%F3wgNfZ)7NMzvv)Z1s=iIPl zd9yr?yv*}%leg$`qdcxO7%P9cA<3k#BC;+R{k+^UDyp}_hPVpXXixCp`+4dCCDiOn zz$#A%C@CiD2MmmZ1!)ZOcRVZYGcg7ss~w>>ue*G!XB4#K9ihV($nR^>d-DTncf;1n zm#ak}lxBK|<2J~@leX%$vlGtuUL*f14uhELjrjhH>Don{^22z zopFKgky8Dg3qw#qURoFT&-%6Z=$xBqFS(;1dRoe`ID-7M&!(coBbptpb4PQR#i(B; zMO0s!@Acn=-j%_yt?__M&3+h655`ZCCz=5!8Dn!z(A z2+ga#kfb#csW*Z!bgmb+be{}u@-i>A@q+YD2iRN*!uc>SSgCe|W^oW&vT0xbpZB22 zq8iw_r{E#ga#NnAqN;Ne{%&=|58V)qAT9XHkB&HWk93gVVxfA>35pdycw`cTD)Q8q zlg2vfVHD=>cLpQ`*VdzPp!708`NL&|&3ABHy?;VeBUqX@XOCB5b6tsGTqO+MBG(KixLBCL(8R3qr zn+ws&Cj|E7r*Q2iz~HkCzLz~{*Itc9Mluw{dt$?sJE-uLLYaIMDx~RtK+jv^R&RVG zE!(z!#F3cigVMgAaDw`{j{Qhau<$!-;{wrvG#s}!JcaGQKx_*0Mbo|}JWmM3-}Vx; zp1FoA^?^`4^2G>a+S{iE;wSB8q+Lqzlx9#SG{4hbTY}Shfp{^x?YwT`;lWH1mx1o2 zNAf@1gH8REic-=sh92q6+`N);!`lh%QaZ9X)`_@F8in4Wo!HI8@%Z$eW=GSzGK;)e zl*Ws2N20>S<74n_r5K~1^kP1BQE+PKf~QV>*}uX_sGV~`z!)|5=SBqLPr2g5eKq!R zKm>G6+|d7<8asZReA$!SQI)60el><+uo`Khm#8rls=<<#JP|rvjZKK6{S@u3`VH^L zGG2thy2%Us_YGuY`V+_Lkv9$}4rTsyS2PfVAWma6i)T`Ne(Vd=XXBZ}ieTz}i089? z5*uI@gnqYZ-x@WU-L?kIxElRkwFk0)JjK5=_0#;|pL0^z$n0E2#vVO5C%FeYtu zu5>WlN-U*F?*L*&3}&Y@18|Wv8(UuYVvA|NZJ7~(6OVeaSLcY2Qq^{!Vj$ILyU*p| z&KO5bP@2yC?_|PxsuNniPGyF3)A4QsdHvdHF{kC2cB z`5%-#h?UfD8B5BIKxfkDv(Tk%Q8RgmiI3!vx|q2R2uI#S(kK=hGNZX+&>#=U$4iUY zD`WDhk=EtHm&HsrHw1r4w|OgRIm@QLIAD`Mipp2B+~#27d(e(*?K<{`2cf%HAP#Ao zv$W(uMEM0lV~aT(9~FRQqyzKUTF+V%{Bhlj7+*`5vz4ULU|q>`xZ8w9X8L2bh~9bk z7OLyQ!G@M+_U+)vTLO zG6scXEVkJp%?9F-(;R) zYhMIFgLVL+YNwb&?vLjiLSfS7IO|3FBkzr2nEQ4wn?iq&X;~ z9P}||#{&xSiS|=dPc3JigYs~Cg9y9ttYH0US6h`O#>=~FnE&uhTqB>aCsH@cgNnC|-+ z1dy)#i-(9c(9HQ0v2;?KM6Aal>Z1ewaVSH~>idzeL@y9C`na;Ph;Y2WAA~ED-C02A zFlgCGp%m)LdK3H3m1@38yS-WeR4LSYh2m~IAEwkL7;{F2VY$9HJMJ8arh(y@Y2?Ws zjShfHmk3BK+}Qn>|2v;Z{JAJ%lSo^)se2U4($6w4(s6X!7Ky9NPBU}bjhuTIiBJ0u zv86$@Gm%81_{jmLO!~Wz)aTcK(I*ao1k1?5pRHY zuZ2rl$nNv>xpRiApCLOqzX%UoMC461V&RMP(MqEgT<^WAQpe$d{Yrn4-95-4I&MEBt9+=WUY^hZ{$pV`(gWLYd+TAf(?3lhxu5QA4&9hgW<0y)iMqT5-sqsvGq zCXUB1^L0#_yt8{YCSudzL5z{6sU7ijoO=#rVe84W@;R%`IeX_BuuFC1Zz2s+2%pLR z>B%ukaE6MV7W?s@JPTsdcg>y3Tu%4T*!sv=#8Usf$;zFLJQ|G++rqGQfrMqy9OjE-1n$U#*s`)P#L@g{ zSAdK;P`&+YTQuIc%Gl;EQlt-#L6(t}?dwSIyYn$PDhps1>*!tPPIFjSA9kB`wci8c z;CJ1PB_+_=UXDl50B82gmbgg+60w6A4hqud&s(1W?IkB!N7~8y{Yikb)PQ9+k&bL> z3f@*}F!O^x$ftSzuHYY79z}KBZsHv2zJU&Dx5q!sZgbAN?u}!HH5c$m-3e7b{h3qG za#WFL?L(g)tbLRmBL}(Q&x0;ZVN-%#&hz4&cTlM&hw3Xv6)r{sHR=Ry-kDI zAMac&fA5V1qj4|d2bI}NAo>(sy{}KaA5UeF|h0ug=b0b43SYN){ns|J72bi=J`*;V_`bc zpM{5nqFgTykKO&5!%&)4#>L@exi71D76hBE@t|c9JNqzzJUat`1I44!YkhHOpopRco^YZuKFy!cUtTu5%X6NhJw51vjP9l7NRDgo6 zm#{j`1u0(>V1^28y5@#CgQ8LSLV@tF9{9UI9#cP+V&FJ$eA$u@#R7kuy-{x?3|7%`(jbv&4xYMPCPfmcqHo`VkX<^^Ge>=33E;`+2%OtZlN>0 zeVnZikA|93BC(hbu`$CUF@0bXh8^3<9#)0H(;^u$!e+KW8iJGOQZR3e8H;t2qRRy0 zk?k>I{vJU{O-aYG^;22pRzDajrK8=^A?)*R^3;cBVCni#qS z)N6@vwep1i+T2QPQgTOl{!6`2;Mhk@zwe#b>dl!fN28rLOb0pZr5-MUPMt4KY#O3J zzH2dZmk`_VO{9MP<3h}T9f<86y5QrZd^qGuA+cHk!&kYO8y1QS^ZcRyE*m+L2#o7p z4y*f_P}oJ|diH0Gh)&0|NpYAyyEj`pAeH=336P6MG7Y^X|RkYL=V$2 zE@dRMvZFg}LI&RGsk12-#EWpE8uvflu#}EApeKX2XM+hOfSe zG1)MOI5y7kI!n*#<+kVC^1)AU(D|Ei+Dcm7H!^t#Q7!qTL{R@ZSfSqa3hutA-O%by zia^ziIG{zozjSwnhFdv2NN?|w5TO{fUIA2lV|GKb!t-<~)Tc{u*fv@5_fs*vslGH= z6sqW*TZHNRg3vShtfDZY0K!5UF4^cR#sueK1kHuDVlT-1xRAz-*cmtbjVW!nB@07` z#6awNL4P1=MRcyj!Rz@#6#As%X*+sWLrEv9NJ5Nj66R!QK%;v+YIl+6YT89qSVtqQ zH|^MV)uY#!a4b2Wg0L@-pezr;LZ?)Gc=-gsX)fWlJq<&~KY{c$-6t05`1duoVbHKGcoq(O}yLWg&U{odvdIW(ohfdk0hPkf1caP=0mW6iLos z=-Eh`^O3o2?hUid{fcSEx9R;$wOpq(g~8uzn5ZBgU1ftpV}CX5d$_`GvXY=?MZU9* z^x3$mD!fxv;2!Buc887@*4he~G=O+D2d4`4C(7_#BB7tnbRnenJWgo@;IFB+@cQUE zoOvCDCYlXKt}Mg}niv0`p(d;(owHwU7)&+)DyH_%#n7vfxYe^((S^LOlk#Gax++xB z`A7yvZjOiFTNA}jMJjrRCZcZaclpZ{;%(BsT9LC|Czb$0HK$hNi-1 zhDg8Pg;0E>T7SJ)gT8DY-BsRc*!Q_Rg!chZH%dov+9;IP6DPA<22RY>!MG2kKPA7M z?Z|oPHO&+I(lQ~Bn-3l0RooP3VRM=RUXj-O<@RhW`nmvpY5o_oEC+^@7=F=C&u?BX zW@^pSGfZ$sM{(P8{u}p4QFy%`qYgXa*@eNvshu~_^q#3T$}r7UczOT?s#WSI7CRP-*0MgOj;_%ys&F<&6AZ{IX@ zHJ2)`whP1dFKI|NvQ@0Jlj4aY9WzH8C@Lle!p4bsh35w=+{s5(Xpo89bHB*vjQ4>_ z=PYy<3-XOSsW+|7LhB)!y!N^q3S`;H{cS6Mypc5ZHl&MHHkPmHN;~A)x%e|hTfUO^ zc%G_xIQFAOe$mSrFC>U2mE#KZF7-H!aLLnDN+>}+^H zj)nd)Uttol1jjoi;OSXM;l6qrx)da#TgGl-&X+`}u1kTnWVJB=PAqoike9wuPhc-1 zv5IP~@h>%m3lqbz;09@@o(~XO6Q!7#n}OIbodtX1VS3WtYdZRiVib8;Tb5+u#-ay` z@1&a^*&`cwlrJluQ_p(8HXCbt7b!YY4I3@Vp&lVcktKG)iPgF2)iF|$s71`Wfq8fu z6r}h^+?{n#^Dw_xj3SD>QxkXOwYfL$NnC{F;=7pX6NdtC~JiSMr9TCF=qOD_s0y)PkWIq4?f+!n^USD=oxe_ltP3A2~|kNrhD*H!O? z)f>y8zL}WgDlNjb`ty*QOYv#-J3;^KIjpb^#e><+Ld1wd^2~AZlJNe6jMw z>;c(Gs)cav1o@92Wn-SNj&P88j6u;k7}{&R&|$bcK2tq~8AFB6DK6+u&%~AYYQh5A z6(4?(2PK`JLM81S^vN4GM^#OjJ>3~SM&!3S=e|GFg!G~Jk>l=!=dq>2)up%bh`3MA z53UMb)?dd#^7V|%Z4{RLsm2=O6wOchC^)KK#;;SPNwZbrLC%#JH^LYDx^?AO7jWWS z`QgWjp8VxnIi6h##Fx0+FMX%KHxrEXrmLsF6bjC!K`$>Etxb1@z4MZgu`(5xKUWJ? z{o|;HNrP>mT=1hk1$&iFKFu8AHR=3+wq~GgPNE>DJ&4MeOk6$^COnNGZFoZ#1||Cm zjv_w@nWRg;?Iwsrd@$=&4&Hrs5PIDuoiEG9g-=I>o#aQ~)Sf)gMLUE6Rix?Wd6+hS zlkoDe2>0ysNl&mwct4wT;(7&`X}Cd9HFL&Rq3t;@x33fC9)Exv#Gtdh`b_XXSr1e4 z5fxASCUo6<1H(zf`Jzghm$$nLiyp)fkL}4H_o%|YL@#_FFpw`kbpf9@_~L;12p+3n zPWp3y>{vFAhf^KWq7{TGYczPfqEe*LUh2aw4IVS$JgWV|@TbcJzR%{$c8p?l6%EkWZc-%%me&}EpszxVaeWMB=TAW5Y^b{2J=*Yk9NP?X1ip<7e zLf+gs9POEoPeyNr6^1m+S&)Ix&h#1aq?yp#OgQG;5cYJHp~*B02OBR4DP@7snVSvE zJ|)8Jct3n1pY2+M9AQ+k4_>^?LG|8b;p7ibywA(UUgv1ReX~25T^=@{2@xJNyC708 zA8S_!2#frP6Z|h9f$e>STT~ZYl^0x^*I@{GGfKM*<^z9TB~~tZS4o30Kt~Cmmf{879*{uPDrbpX*QpyJ6voUb2ASH#vvT>!aYQ zrqBDj7Qn+g7W?|n;*4g7OC}^hac45`_?lS0vLrl_kKy0Gry=@y3TA1kbK{UCtZ7X} z^D9;UbUS&vP0~@h(OfV-cT#t1*h{pAbs`>qA^a)}g#H1}xSrGYoRf|F@bfB-=uWKj>FWpc zE7=WLMmj@F=P|rw-%U6Yb6}k3R9@9wi%ScLJ@R}Gk9~gyL%tBNuyzq=HkYvQiZ7zf zR&Z6lO2}&b5tFu-S8e3j_B#lN&sgyKNMdH1grHr>Ccd$;1e24)vDSAZU)i%5*Iz{; z_Uk(SL$eT#A7bJ8c?AzK%!7VR0#uGH;*WP`qxO3e(mm($6q<*>ElPod2y;D>|aFzHY_MtvN?U8PaDDb0Z2*g<^#oiJ=l%tYzuK3r>+48^fo7`dT4pFsRG zkzY0>t2^=^G_SKdmV^5_{{)NQKG0p0i*J=*1q0HJu9%#Mvp3!f&5`bK>W~kYD^0@X zag;sLkdKpz4~6Vn5sqdQ(4OXwkQqR_PEjHLT)QW9&vnMl&V_BB=Q$n6ap{uBm=f=V z4I`%Vf9LO_g>)cONMBUH=@x8DU7@;U5noh$4Uro>&~ue3zjUV>p9gr8r)VRevEecn z{_}-m;tt+(-UV0;2tdQS1H8ea9BVcctEIeQ-0hz9UUK%4|b3d zf7v}5ul3R}JXw$btRl_ciFBNQGMyV;qW7RI14pYhc~OUOoR7D%c-}%@eEuGU*`y)tzk+XAcpH64hbEb4!5cHKqwXMa-QVxvfn_zAG=~_E%$6@Q zsUr5F1ml*TtLQ)@*m< zs=nuu^o7p(pNQL>Dnj%w;*On~gxdNqm8>KR)iyL7$|t+{E#8+I{%CIw#Vt6=6A3;YE{0cu)PkWj6J#e_hp1+B{iuS}GOL-~g zMC`$5^6>V5=*jQQxP&b7G7Wts;qzBjqRS8|{M^ZVxQAnZawxVL2J=h1<@l=+iJ{s- ze5wiE)jMM__@abwA9)UwrcyuB&Vz4hR{*=yiCF02#FOMXh_j)c&5aX$s(J=y+)lym z+St_IXgNtHVKQF*7*I_)^fpi}>ZO>UH&4#Pg zK0|%J6V%om;GZHMkl$U5MKU}7>PI~;(Z0T#yb>ce)nR0a2Sz^f;4V(J$g?A6OPwDd zqFs$n!4kaHmvINJ%kX^{fYXH${OOVlcyUw;MPUqQX63Xa3PbLJIDQfeOp1ua-LM$G zqDv_puf!ngW*DD$f$D0Tcr0xR;5@JZqdXFE^P?xX7?leh(_|D&#N2aF2GlZBaJJ$s zA9*VozRjsv8E}+OCq3$^?&;`pa4(;|CmL_bpI|o6ntuojhX?s;9z7&2akmguXlLQR z-da9CF9<89W#jYWmHfFc)ko8E@ZtSZ-ap(I&!^>L#VA9*>n5>1X6E73o%y_)SeJd~ z<>P55eeRM+j6=f$7$nc(bIio3F)PGGEnR+TunYbU&PSNrG_LkQgf*3I>$;S0r}>N5 zO}Ja^gy0w_{?_RsDpwKjV3iwxfB6muw7BBR5MO>{%1xRFc%Z#qFz;k_4M#%getsOm zF{%brFA`7eN<44YsKP*lheLNmE+0N2AG<%0Z>=SnYgy&M{7f{u=SK0pvP|5piXo1xj9Z$g0vBTO!PB3A z8<~ih#5fGo_U3UTVo`819y&+e_@#}J_-c@Ve)B~<`Zj4lx+cQg$${T@mEvM`B5tg; zmpfOlGA#QzTC)9#ib{VQo&)Z@6PeaZiMK$7mI^5?O| z_(6QRJ{h^(yxTc69`MG;PsRM;nL>P}eahjt3O8`!8KNNdsCULXgw8tDEc~vy(i)?w(8q$osiN;j%LB5c*IpcjOFYW1G z-iLJSm2+b8>B27lqdT4Rr&#P&x8l#pTk~jM9DbA6eqT2+tPhM zqjLhd?eTLM8tQ}x?WBCtuma+`k~YRZoS!bt#eM2uqBq2G_4YaV?dF0j6Vv$JD_Kxz zy1{W<9-rBeiT}Mbo*9?$=Fb_JLbIl0y~_E-&*?Zw-sy!w7kOYw8s#QQ5cvKg&!?aN zNZN_!+A8=l`(&twldiX80sl%ntY0R9s63j=HU7qjb)UYkj%wwhaHC2VuOPCr@l243}}im^j^)-`z-DfnCAm#d78! zpGa^eI~XrcInevm2UGq9!|uQ-{&Wj*fYwT39C3`FH}$}}b5gvqJ41L+;=$5ncER_Rmqun1rnV4>rK8d zd;YzG`jJUKc)aNZcYaTrxQ@P<`u;F4AisXu1IpSn-OsyH1`~2=|9WgUH@HMIco#o7 zEZf0v(EK^p+#i=Kt@*1c(h5zYXXAh+Ul=B)ycH>K8gJtU7b$b2wXIHO`CB(W_InEc zDTqVW-i@d1Ohm#)N1Q+6&ULKful^8HXed+mxfYf|}&F`+QONuITuNUjw`y3;gQOne^5OL9q5*MZn~=e>D5 z(nU+gdcgLvD=*#_jBQgq(c`NVA9*y8*o4G#w>!iAooM$=1Q$Pfqv!Jj zJSU%gme+}!=Cqryr@VtP>C~V6+|Fx;d*Zl@1Yxr*`3Pdl#?y|lamp56O&Z&wbN$g! zyOFERaKV>;0l3=Pfy=XQ?T`t z1Ma2R^B2?~H1BuB#B_VE=@^I3e;u*h)RFJ)8iVi0N#Cw3=C^M};yH0@KK`Uy_jWkO z(j2hS#GmIXhr#%yGsZiI^159i_(c7uzjZ9{KziU~Ox|$QFupyC@@3k)B4vvozq^}syu00CU*g6qQ$vtYK}@zdC;oGa z4Ej%r>uyh<_jSQI)Z>&kz z<42h_{!V;hVi;x;<1W(Ao$u{SS}J;;RqQ4FRfP=wB_do+3E~<}QZ#-O;S}*G9>hqo zn*2Kr`vQ1mXBiIo(7$1f7cUd&oNu`z=#4XD<8Ci^oP#g#P!&~wbuBf*H#}GmzeRu?Gm(~?TdTsP5E>; z;tEqPK~mKF*#pY|9rTHB!tw&5*&K80$OL zow>t)I%o3zb^2n*pSH-*DT}gEV)pT+>uAT-#{(BTS@L&u=HEAZqGy*4d=%v$R?7ahSJtP`)bDXhC-++%k9f@t^yL_Ut zl>d80`ZxMJu03AOBld*B$U+QLhs|7jcqmRjAg|x>9sIQxG20KhU{}Fz-n)`Kzq4Fn zQ@n#$-IQU;0yj8QU1~Tw1V268(Rg(Yu?0f#@~sELYmE5{4ay>%>V=Yji?{2;EJAR@~_*5>D%MU=tO_M8WOTF5UgOvW(cBSqTl z@;(<5;7K`;rOH}7r-HOmr-{k0n8*iok0lL0?FphL@bc5qsQ5~Lz$OjuMH)?jW zY-1icna*$k@dw%&@*?t36i`;L@n=2W*NpV;6`okQU>2YLk^D-nH1|%J#!ar17v>4+ zArESiFE|J)aXzG5(ByCO0+77f7wt{5KryP<@M3fCvR6}Tp#|A^vo4XVobCmkPu2$CkKuw@35Q`;GL%-2SA0Sg6Y5f<17Ea+F&nBvQY}ZHy^4-~`6udyb^VD?y?Y?n>=Oz-XLop; z58>Y;Lr@yyffF|e^Czom7C}tWmBcJv@>zyoGl}t@(2w^SCWCA#WuR26@pDzdcuBm_ z4w-$p^?lm+Mf$?>uL>W2%OBH-bK7x67e10S4(};Xt?0Wl7ZIo7{6*4+dUW8;N4;^G zvNC5p>%dE!>E}s&6C|>xm4VkN4tr?s+QgIus6AxWU!_ix5APG!?q^j(MfTzb&I)L)!z} zZ*}6|sAgMk>WMk^U3l#n(v5pj-ZAk}N1Tx%w}PI(ye>R$yA%;cKD4jt%+FCC$40sz zau;^sU9&`r$DS+-6B{T5<|OB5Qb0xE)4tRhUW3X$a4KFJWQsHN<%4@+-T1) z(0jY&O!{sqgjtT0_hq4K)ey}k3u?W#`Dy@F(~ny;7XpU;u$^&_4_G!dIe*U3VCoQe-mOm z(~KmQa!5^I38A|JaL`Z!4ar@>g>(()K1xudepR?gdhbr8(QmnSNmx(5pA{~|CDE)B zG%tGLzzJggov#)4kRE23Lm&)n>xIjdgIq+<@PogPgnswkV0=CpefBjAaj}$H^h}CV zY9EBl2VBsxX9($8zX~y=^=g_CijNzV`F>(5sI|&q__8B^O6RZV)%Kiqyhia+(=MZG zmJ^2hjN;?HFW}G#s{dAv$ZK zOO4M^Cm)Y5dCOAOc-=NX6bGea?iDq#7A^zr*fPTFF z;&k{Icwv{7Dqlf*=0M78IA__1+mJ3Rk=C0;jR*So}bR|E3vydaq0z`KrQ45y#@t&n&3- z=)sqhm-PA59K4*_B&d(J!y4jtMeKbnl(rtn{A*b>@2VG4f^AW=w(a~`^?6tB^D`BF z{y5ToOU&xgDcIIQgx+1d@lEPUaPQ@UZK+*|Nf?g@#NZlP)rtGMk*|kl3noFzT>o1X zn(3Xre|meqX)rWt^*xnBg` zkHlH8B@a}?Cqd=B1c?WNpVcWXc3NH7NgRS{6+g(gmr5~sNWHb{i|ApkR~TE(ywnf zZV_~dZ?U0gBC_e%PpCRTg}C!2*7S9Dp21z(*SxlG5mpu25r-fRw<24F`aUNRPClq_ zyhTvh9YyuvEMz@z5k`JIOuM^mn05Xrs2n|rbu`l~i7pdnyB&w+!^}4K^SZJ}!lxu+ zgEvx!(7|%$8RaB|qHQWJ4ual`RFBKkVp25M$ zRE(hCKVsr3yf#h8-r`c>NA?NQ;$_0Cs#G{@a17>@os*!OAnbOv!v;&z_x|TScwl9^ z5HcVg>&hL`qkon#-69oM#1Wm_JzL00qdmhGF?dy$u=htI3}zC~G%{0IJ0}5}J;_s< zmM%=(6^E|RD68&MilB8e1_Lv_@Y^g==(#Zp8ryx)csE9{>P5ZzUtc8up?rtTP&A$P zL)IRd;BGF%+gJWLDhU*#h6W>Lc_6Z+e!{#z0SL;b-u9HQz`s#eMqeq$srd*uyO76) zm<7|uc?*4~6X%T1JoSv1u$(k#SBz)|-N8$^LbIi^vTz(Y=P6|FamTWSk+6|?3cfwu zV0||VM^1VQGvzL*I7V}XU7o^+V`B2&#X-s3Q+S{wf^9)OJ}mYWW`80tzA4Qh=6VXl z3yI71CJ7(3J%#J`4u}#H({qxiP%E>?sbOi5Pw*6)^iCtXFr9oC-a?h^H2AZ$Hs`#> z@TA}R>E2SGD14wI>72TSf@PYi+EfVsk1>OoXmL(!xC96_S*MS8Pv$Y;(LG;e#L zULJwp`MSb0N8&qb6R+){u3$&`IZEl&-pJt`J3j z-wE0Yh#amfOt2%56xCsa9+1|19gkg(_L-01a%{iMa z94=IocRI_}5pyNu1pO@;Xz1^ZgD#VVl)GuPj}l=)!4x6NG!=UmyP)Nemav?3$K9G; zvFx#y@OV-Z3SHdsyLGDY!8L(0rpQ|zFj)}Q#i4zv7bcF;5W?EWLdnpFwDu!~vC7fN z&htf~QGa3Lg9v2(mSDd_H=$i|7^1ZNF<$wf!Z<5Z| zkAm=?W;Lgl-B6Sa48-!;R7*+}T{n})^F|!%LP`{VeVmawlVDJZC5r4CM|2J+FOx&LVkdb7 zNB$))@PFJkH;3~ zjcFZo6`R-6?kU~}P3cjJ_dBD})a;Ac5w42!){!Xe=7)bjEfl&3!r?#E9}d-;iuQig zr%wpL(rFzO6<1`~GBpr8%ej2yV9Msu4Z@sMA9=#^AXFOB+-BScdES% B%buB;_* z7(hNLu?#VH2Fg=aeQ`63_5@Zv<@?9dO!{#sDr&pRcdYP4N&j$csAwJ@t*JT$df1vDY6ta3Ym9=|F45vQCak%@WtlKW?+t$ZI^TE@yB}z{C z7ZZn9&4cBk=}x#jxb1#$lykCP&x<*jXhIB)=^AC@)?`B?nluKgz2!ddGSTU$Gc<>8 zmcuy%ebB{73tXu7jgZd|O@#^NJ9}MAknbFn0{Ky53m=Y^D`=l=cf$jN za{c6HKN8S!9L+H0TjcUp@nHMCp*rtD**RG(M5Iv{A6c(=r#c!*rM`H%>V&@5U-C}~ z5}e)tm@3Q&C>PP3!gvHe28Cf2d9depFs9td5VZOS;IPA9EIlEGk7ppR(p=!wh#)!} z+7&u^V=84yj3e)M#JE5#Nt0l4q!fFH$jB!|xncRlk{B9_Iv>i&x*mcjcSDg9OZq|L zldgFhiV;`c@PqubZ<6TeLEe$&3y2qH9g3M&Vhq_IiL--4VOs5s{^3#hOWv0{1IjZe ze*IIbpH%x2zj-Nf%Kq~_zb1y3@svEYPIbf{8O^?{a?oV$g!(FHR36C2wMd%L-8%yH zK3Py9{m0pu?U1Bppwke_d%e67TlLe?eFL$|?aZNEm5K>cHyqa7h^D0}=z5*z^9kEw zRFQf- zYs6p{xSlc%LL=IobMBvhY;RCL9`<#_@8X{9>%2S^YdE2QVHbAq9ciu>(mQdEG7AgI z#)EAl9GtDhjP$cmaaN4q>;EC(eg;w~Yk2dlf3PRbW&zE%f(I$FvF*}ur<&fCP-YKZ ziG6pMdV|qj+1aki81;zq4r+R_CYMB33~>IXi- zjCH>dh?m?SH|AKdk2JsUK->q*-CNm=aWpG+r@c~)H8XEdIe4}~I9)%{j;Eu4CS<1-SOr0mp4PinGZ+a!WT$Rf7-Xcp8O#kf~u$hr(9f8}i#+}gc_)fc2=3+*9V<4oB7 z6=_Hz&TamN)hwksg|u4kusOJaS)WaY?j8>~Wmz&lC<*s$o*L2z?(0Gto>p^dbg=M}m}DYO~ z5wv3+7sKut$6yT2OZkp?wu|nNImC+Zel&?`4hX|Wnspj4No6bN5>G~-a#|bHnUZ@D z!n#tPgmxBN`PU!r^}e{Ko5M^)Bp941LBEz3gSM93fLSS z%G#p$Z{@K<=1+M$zm)@Vd3F&?+wX$3XT+VkS;RJ_5u3I$2th{Y*yEqHmrf#1jXa+v zkuEo>p|Lme72uDkzre4lls*FO+*e zBaZ!P%EN#xXPk_SXJb-wQNNwI@s|@>^}ZZP2Z%ApCxvyJkqsxB6~4cl&aSp2uh=dZ zbob3>Wpx>N+u0R?KKX1~dOG1lUEz1`9Mkn7eVh)>$hMZUAy1QWpZe+<{t9ME^QBil z?%3>I&W4i4X`W%>an&;km7Qa-P}vr zaZ~(|w&@nTa?}l~fBkS<WQ57RXLBzlSGtd##u_yIS3uZIPtoIwd^ax(MMw39&?H9CJ%uv&H9>BudoMS@^K-Q zc+M8JY)C~O@$cQ}4zFWBgDK~Z_R1-dx0&+QZ0Ihf{6U)r7Tl5!*TuxUe|?Xg>5_sv z%4n0#e8{*_0(!6V#>nN3OuLx;^&5OB$My-kwkZ+|tbOtO#Z$I_S{POyAjbQiCU$uy zvFuLy5o`K6TWcMJH?IEZ^!^2lIY-$Aw7=f^v6&qp=0iekAgr&wWa*c^;Xr-pTF+N3 z!`>7A@?e~q@R~ha;f@XyesWayM2!-{` zd(3t*-P@UM?`NZzoDDy44W0C;wtmN1zrQtTIN^j#3FR!J;0gv65Qks8l9{_-CQiEu z2XN81HPqpR4mLzTFHm>8QgYnZ7+IVKU~ee&sR%qmJibgMfq->73}uFCQM zs5;BAs_p%Be?MLB zm%HUrA6#cK_gZs~@f#{j$@e(uKC|qVizU>=i4k_Q1A2{`hwNH7ou}en(vq zE-AiY!&f?EavCvq9o{hKOb0wBk6hc=H*EWII~>^=jG61&SPh-4^I}6V*`tlsW?JIQ zAL`+jwXubs#K~J9PX3W*7I1@@C8Y8D?|xo#=nmWARSl1&rs!On&7LY&Lfgs=sj)fi zP;wbO>3laWOH6gC6mu0VL1R+3ZdnOzX&$LNxq$7|D1sm&4(j4!HhD?`T3c!yCeiZaK+mdhN1VMO!s#v5OwZm9--xdgZBWg6OtQfN;%AKssAf~1 zTOz}e=Fl0{EdCbtJ=}tk;#|q*70|4yt?N2FTZFOhP4#fvNIJTw5zJ*mEu!hnJ?=s@ zGZj_g)<<&~?1*EVr&nOcGD{?1NM!y`?m>rGnuQ)I%roRJI@_)BAUK^pxKWI0qM_b6+LIDVs}*n&}ak$8zXRAn)& zpHwkq>E69jKA!o0Ex;$zx2zNrSc*(OWSt#}g`LQ<|8Tr~P5G@46PZ~~HtIT@G52L6 zTemM07oWSJ;&md6*G@rosVj27B(eiH$OjbbhDgOE)|X~p)6Lz{Z&nhU)fk3X+dWXR zCy6P4p?x&o6YDLKSja{Zes>U$A~uOB5z}=^mKWU{lUQlGCq@}~IPW=$WQ z>z2&IA37uB7uA=3$!rDXJl1B=eSB;(8x>4v57J{FpPtOd&{^7cEa^3=4qR!Y>^AzD zDbh@40e8$X%1#8srHSkX>9E{GyVmFVjb`lY(1+Nw&lGistyszI1{mKVEohT13o@uf z4)w%G9&=CMcFevWT=U@4eW96`|?R5-f|Le%yilWg@oTxd1;)9dRf^#Ll+~h#2Y&O$QP4JCO7L*{s4b5o>SC z#HgXJxS=CrL*j`YLiuW&=ZV;8^2Bd@PyMBtBIZSYt8~&6`pp$F+kWAYBPLxRCSuwX zi5q6<3FWmS_SJxLRfrFGYo~}U@8b*o;oi7=M8qs=J&{8`Wb5-HW>!k`uT&q{TobYA zc4zoqCF-fMh`pKb2+xInNU#vGglOWIQ?IqkTEub|*x)?%7Kho2*bdsyuKFU}wh^(< z>E?)@7JzRde$0t0g@x|y_Z4U9{H;5pi-#_#z-4AHSIrxJW+a6^O^ZNnV zHrSF?OUPe;wd*>M96FN~Z)rm3NmB^J7qP&3k5Ex-M)S+1OnY-9GO1&>bmtmo=|TSb zSWC>+-^_OWsKKy-q=mBH$tK^d!e}cSj9$Nwc@C|B=@(ntEI>Y$2E_-i98OOFXBh=MpS;e>T+RhdCKObO6 zPNZVLxf?P<4zL5{x7wiZ4%cG`*t61Td_L%bXNwQ88Y9XWTJ4E7W9aKf!Puzbg+HnX z*r|LGoI3c@3~Tv$VvEhJ!MIo(v|gRc&XiVCPt6YPsngh7i*h89Zod}O+5P?ZP$cb$ zEA(~U4R`VCvJ;kk)L`=J1sELTj6JJ0*mxN+NzWfq|wI-IRojUV>D8j|_ z-q>WN&T>hoSG$!w-t?SF{6PJAzG$^kXRq4b;4r`sJ`U>4M9Kv#zmlfJMV-yiBtKb| zKO`RNY!SWB5lgc$A9c2Aqz!WD>=ELx&g}CnabQOvRs^cEb*ARfni7N)<0mmAe+&G{ z>$=W+h~>P^{28tjpZS+IalvLZBjg?Bh5gRM&Jm9xA)RfzX)WRwJwV+f%AMC}fttyE zIBxx)*61y?+G=r*dbN6$A8GbhjrVk>-*Dm!&P}O=d#*kB&@a&ZN&eZ{j@YsP6ABwj zksITLgAY4!D!%|3_0CZA|A5d-Vhnmm^S_Yy7)^|h=|8Cd_O=~^ex+hiryGRJ?db6| z0q02%Dyz|sDHCHbni!$V?HG8- z7Z1cfxID5Q3QELBiXk2RjCLeby|1BJg4I&``~MIt_o_d%wzi{cCguO^5yAIxJLQqs zA!2a=_FZhp%RZEqO}!NZ!*;aCS>nfU%D1p-N9HkexHkqNA*B`NMix*#-Su1-G%!{x z+UEsg3`~*QPa4isT1Yc!h8cY(VW~_LKI&UQhONfcK97h+Om$=H5u9Jzh;DQTJhtEp zj)m3}_tyqfh-vWoY7#kEb_)ecRkGR5aAi=36;zAU`N`>@l>Nf$mo;K z&kL`H_+jCEeW*O8x}o3?*W>zBzntOoi@f))`UoTKM`Npqw6pq@DNDL&;{0x{(}xC~ zVGC~uV$^$mETIgFsNf)6lDR@UadWg=24nNh6Uf>|?=-cpeSV+wLnJriTTyLh3fZ~_ z@$)gy(Ou3QQrE1-X)l}6VQqo8%JSk(O^4B>Y z-4-QnjKMD_Iwx<;UNr1!IAyDOAwTcsq7ytAe%{^~G`5FkD%H_T)VKd^J4kcXCSN3w zwv2^M(ro+W31eqJ@)*z4)Gi^W8@=Nc?X@+#m(fh#NCaoAHJW#SI$*`+02E!=tQotG z7+=Q%vDbWu=F$S{Z|n$y>CfGoPNd(AUlEK-g?*a8`Vix5b_kaD8j;&N!UCbBhx_mO z*bt7F8J@_TmXuc@C3zAe9wLs!)fx0`+lJ$vA$RBNm0mc@Rypl&W zQ?Z7ji-Bam@&j1TvW5NGYmz{N`9DKYlRP4 za=gFY6Xa44KXA4Tf13XgX5=yWVI|F5M>L|AGNIPxNb}xwKB+nB0IyLpeEyJH{P;w^ zh8;3|?aFeTp_z!yUKu`Kz6g8BpA)}WhF>J#_n`z=>Y>YU!_F+E)wsdES(-v>5QmW9NI3;CFg^QElFOi<@D?zLKNMv zI~Pjx?`=NtKk0`zbENrAbJFMNk)D#Cr)(sjgrNv?7D)3K^nG=+pqv;c%~dZs&>1-p zxvQjk0qJnuXqIzkn>4>cta^Q?V5A?C=1asDXeR&W*o)Hq&}lQYIMPfuS&kc!PJDXr z&@TJgQ|2On(EJVqJxvkqbeK!azCr6mb12H};@*}o@sRYlGAB3j_%+WEOf#DUb!+*v zRn2fBt$3BNn*Y807-}?ot?>9bSwg-SYQU z{J?JF|D1Qm-pp0J<3k}uJJCD-^eTQ@UP5zrSICcD#Yf)GLiZk2lUsH8v=6C>pX!04 z3?05QC;`hhdSbAv4xf`9jYFgp>vv6u%Mp+99BK2cPwH@2(k<|C@*N$~;r)k+;FC?6 z8OL<^fveQFtMbEu^E!N0z!#W*4H=6YPV?*%%y$;u%Lz&N0L!sY`{w<{Ex;Q%wo1#{6%RuVc zAL_c!z7YZ3{{4H5@~87giWjf^)`n#AsU;qD<;M@d!sH5Kx^hSUVBiZ(J5KAIV$WTA zv_SD6We^nG@sJTs(DAfI+(|ngv*pqM>qt+k+wsOg$^<7b)2=tRyg{}e8<&%QBhHp@ zm{m@U5oh$fV9V=+3Nf*XiE(LD`l+Uz&x<^zN(Zu;JqKsn~Md1Az~1 z_`-n+WS#WH&T<<*QG+u0GQCh-Y{LhWe)!J=Z={ykaJzFs^e&@pj|v-popSLW4D`d* z2R7XCp*OzH_Q#f2HvGyNnn}}LBjdXbA3DnwyN{EWqn9mLo8*L7hJn~RjK1ep_GoYm zg5C^UzSq?TOCp2uc)2Yf{D(3bvO+Lpw=M5)ZI1gTp_qKmmb=p&dpzC$<4Z00`dq3Z zH@dEKPLh~=Z*4)nCFMDm+~Rj`KcVa`n!P%v^7}U*!rGB?jo&5m0VWNISGL04;&?vd zRvpsZi2s}z$3s`CNq5dSi)y|*P>;u1+uLR`H|Vxm~e+YQ|I&f=*9|c zAx=>E+&tc4Ld-_`y!9y}FMehzw13&+geB)epNnyg?t_Jy5*~cF5PGHLo&F`}_iyDx zd6pA`PKf!l=NzA1ol&JK=7vYI@t}lw=8tmu!tWVyC0(5)CzqEzNTwdOJM>a=`Dx0n z)?G@y(2QK397e2GnvtXz?;XQZMGi-Z_Ae3xvCrgwLW` z<=lBe#2c6Jq15L$Um1+~8zp>skR`IWhhV*)gs&vc%p2lpRh)|FSyRl>Fs17{x1DR{ z9V;8KV2>%H!k+R?rFBpv=85Ib$2{#~4dy9WVE2XxTx&`d0uNh4vbTY6Z7)YV_3NJW zzt6KW?%{!=4LW4&c~9%RIB>`oU*6Snr(?xvO}B&8gBmW{RftJF93Z@`=G_nH)Q@E zQx|~3c{MzLs~x612}JMXHT%PHrs{v(&Ot%U|L10DOl=Ps+OVN05uOGn@Gl_M*mL|nT{uQ%LUsvIVi6CJSP zHP?MzO4@sCw8_2ViyoBVd?|VRqh9jE4MouIVTY@$U+~0N1-MIdtNcOF`7MQfNK72? zZ`w0%zFdH_*bzx_EqwF+9GEy}2d5%Nh*Z{d>+i5b|> z4aJLFc=OU&d>ctz&Alz$coXSBCVN23wuO%i4n^5aPpC^;c;TcVx=VQ>>vsz;c};yF zEz%rlKI6X|e2}B<19jtPyot`zN!0t;mG_KqoZ}9km6Ww9`Akct z-K`E@(@iltSxT6-q6W+N6I*cjU!FLs3JrFYJu3dqb=4|xJ=20Ruzqo0l``~dvBci2 zA3T3xDfadzHq+YgeC(tWoTWba+32tQ=kg-h?y`k^=Vu;$xd59@?2vBri6^Az;aU{s zuAcqKSN9Zfie^}A?{@H0gL9GjoN~<1b?_MNEbM&igsa*eyvipH^F9!(eL)9bH#-S# z9i-nPt@!Z)ap?TuidmOB_+?@#)KH&765YXN>7BTWSi^yB9o#}Yn0Uq>SUme9H>TX8 ztY-4Gn0@4<`uX8iqZbTnKk`rT#uU1nCyo8YN4R>xqlEN$dY||xIX7rXh*2hd;@@b_ z(>;T-bL2nsZ>tRYN0*E z6siF|guC6Uv2zyj2WEE{zJ9B~@YUu}n z(UVvl&tFIjL05{gi{{$CAG!$>QwwoBjx_fjQo_E@e28=C45}g}r0>o{zY04HuKLTp z?{cU=wMXxsf4S54Tr7C$fV}~oTqBA4NKKBIeyWpusHa0z=Y$mpJNfvwB-|?@kM^}r zZu2${H>mG4HKvmnO^wF;c=G$a?c}}V!?7iR*iwuB@@@x1Q0m|gCC|UyW@R9b-z0tY z=fAxB9)B!8>4`sE|Is_h2fMaYmR06IzUznQ|JXUiCYKWC+@!uBdG*s5NC|n=56Brr z_ess){P-c_oGlarClzjytGH?Sy^FL|J%4u`#fT!te}>gj;pt*ceYkmco3M3?t#Q5 z*()p9^LUt=Q@{9ztng_>42GR>L2Ib2kd+mIA8TB3q*hjFwG73CX>_loJj8G_$~>hR z?vEvMf*tkV?tFH~dILEj@TV_k)O+A|rkpVUj5o$+dQv`$oG|Ynv3Vb8ut3@XTQs!v5?-y~ z)JL*|@s?hKVrDLq$QSTJzLyYk^){Tu_PF=9r?6vn2F5zYTbT8p}zgV0hAEw#XUP5STBtW%&x^pj~JtYj$WiF_y>?NcWQ6^ESD}K@5 zxF<`w_Sf8yysNjcGQ%x~$YJZ{b)IHp@HbdO0eu8r8U6k)JhvBWh!pp@aNY@}w_ZKxG zhZiA8ihQ=8RfUi13*laEiF9Wbp|~|40U@OEkX9B(*ymx+MQg|q19mtBC@-XUZgcDRuFs?Pah$nFL_fmDCeuK z53&A&@b8-oRxDN&;$BeRpM-SR&Wb`0V?TT+EzSL>ih}e!^2cp-#}VTGHIDH?ZUgye z#>)%+i9_J<)dPnkdkXoc)T0gY!VB#l!aeE@OecTi4ry6oZ2{%i4D71=@{1TEq-9d= zEhOL3>A}MHrhCvMZGV~1Afa(yDZUX0toYeLAupxm|ND5cQwIv0w2SdWFo(zf0fIgC zf_@ni?_+g;VTddB?H5qimAJ2vzcL>isgIiXQ&srVi{?`alyP%HML7J3)4ReNM;9my z_p>D^8b$Ntn@WQ5r(Aq}Y=fSom4qpi?qHiY^``zR3RCuE;?X8M9G6iNa*6NirC^Vg z@k)Z64rM|W+apX@Nf@9I4|g*M$dZTdy%c2<(afn@N?9l+zL4BcNBr5REX14*!xrMm zERrY-kDdf0jPCv~MyUut&j+G?GVzkkRfJBOk56uO!Pw_2!oc^w*b?H3Y;7gMrkfve zA>1%Ot&h+|^}Bn!dzXFwAY5MfNi3$~1`p)e_av61JLNZY1wUyqq#CVJZ>20KU%vxwQyXkLqAXO>OxAOXE!JO97MxF}A?7}@w}O;~mt%-` zYi37%I%VOKVgjxbx2IBDMW`Mhi_VAk#QanduAZcGh&5?I2dfJAYQpiHW(^M{06 z%&{kRkg!@lAG`;#fhq?G(fCP;8$ z3Dt9T6~VTr7*lH~>#a~(c$P;wL?^9@b*n6_J#Y)li5=09qbz6+p&TO0BzW~pS-2*h zf^Wo`dA&_V(CwcH4_bfSLKWfn);Qe1YzJ#?RbdGAngUep@wr%4xU`(ubxHQvut`lQ z>IlQW^$r;RNKF_|c|lX#9bkK?uW*-gaArA>?zB=>C8uRmU@+&dAV+IL#we!$;j@WnZ z1B74O1RNv1ba2mpg4QLDopdHlYE%_e10~34pnMih6(OuqjE;?@ouYT^BAO4z+@n2T zt0b6q&!+rUE3{=R3E7WtL7FrS6I+yo&a!m$Ic$y3GnIwaFH&&nJ#mY|m4)E3Noc!4 zv*4*J!WoBnXcKFoC0|AOHZYdBK(=^yNL3h79ECq)?XXQwP59y-fiZMfRgY2=QVqh8 zM0cp&_Nv0#&@kMzwnzF8RUv13DCUfDz`A#8!qhu~@IFGbHY*ju^JyRk5|?bybyeZ= zW)WtLAST#o6=AapY0CN#E1^J1c(v9S|CYOTId9bb9WMMDT|#|p6UbE!6VwBXu!!D? zeQ0gHw-#b4>F(}m4i!q~6i{B0DJEzQ5nj#7$7)4lQvDeu>{^$HEtkw-dU&8P;ferW zZw6(R{z7LI$M*T==&P+Je0V0o7V?guNm(eLDnaERsxu>%1Osa^=IdI(MMY6q)t-x7 z(n}dc^${AEX2X9jt-q*`Fl$E^UdCIZqrQ(Y-Z%p$)YEyx6ot0Csi+9D!iY*mL2)55 zS%=V^LtjbQRgr)hKGv8tSy?!4L7oEA@N}cva%C6g|9JdwzNRAFTO9>Z?+b@jg%{f* zQ0Qihgf+^-vHcOm;G%O8ov+3XB^F#S+b;XO^qi8g`*|?7(&uuOsR_SUkwrl=W8APnZ?KaaEIMi^LK+@l^s@y1#FtfA5_Q z5~$JofAHudOui#V(kOEjWyuS+v&7IjPZ`94y@l?#bDLMN4oQO37%OC4 z>?0`sBVM$N6&g1x3TOVtVt*&~3Z^RwE(2n4>9jRQD<})&b)qopp*3DA_7bvM$d_4f zj?ZSjgo%_V_LwwpBh`Bd_oKowXc3*A=#HRl7Y6sU);LXm|C{QR|Mr9C_>J;Ht4k0r zJhF!^aio*NC?AJdrT?uP$N%;dhI(evd&(FArhNsKl3UnbWQ?(PYQmTqw@@EIIr46* zf=_-X*hOO~>#7J3h+X=Ai7|TKR}#_|XQEQs7_Xo85%dOT!l20rAGgR0m47qvJ=O@G zqk0O%+cSVGMz}%#wl}{NkRWicmt=$B@p2SgZHpGT7DIqDG^mTU( zDd$p3xJ1nD)-*#@w@3+vKU3hEVTg}6y9ui%C*xX>y{3o4fqv8&h+1Lw;Ku{Gog zt~W%{B3WS<Il=K^3}k*9663GCaM6ggOnrro9oq4&RB#A8q&V;tC1dO6$1S*CY&1y@Fe7 z>Uj4nN%%JK3YI*s;|*GgSbtp~t0vv&i@PNt>7E`&{%PQ=tK+ahg=XWE9`eYHScunL zM!551eu4a{GYl`GSgo1o3enJyx`?*m79RUL3fs#rKz{rS{%}kbT-(oMN#6(D;dT_f zQw^|#J>>spN8z)(0Tg*P&nGYHT3Vm*tu;LObtImu7~`V(6Ry`I0(!CLl$rOMukB5K zWoC(A%ZOibGze?+NC)}<|C!MLO<>_&!TV35bN&%yc>gTrALJN zVg$XbMZESS@hTh*F@1Oe=Y8XF^Q!?$X9_%gTO5|3Fu*|bT;44u4yWJU#JRUwTw!!P z@nvt~l5QGz4UH#8$4!XOCh*M@5+Lb+6LXZKxpa5}ZhX4|rJrHkjApE3s&8P^HS&Yg z{#@&O0|u_a{Dfa3WvJf3$@js$f#z0uFRo+XxKJK%6OV?o*RkPy7+-%i4qsnf!^oMD z+)6(dPu5<8VPZ5dvWmf`=&Pt$7ssE(MWf~C6?A@2;QNRN^ zyPr=Vl7K5VMiBD$^UV?Q*n5GVWe@T(`EhusX@uWtNBFL$SX};Sh~+y@@=h6IT(}t` z(&ZeFpC5zh(S|rPP>(knMk6`W03Dgv`K8(@EFWb6IT-W6`BB*9c@vqnCVaV36k-$& zNULPVmF7hu;NDFvls4rBzbWHshcQkjnewSMk&xCl#{7Q9ytic}(sNDlNo>qb&e47U zjtNwIhj7WIaLRfhrul#N^Rf@y`4+nbv`CvEf64}4B9nj;Srcq^SjFv);vuHz#IMV^ zW}kSRl`+98cgBZj#37Y_f5Dwa9M|G-T+Rg7UeDnxx5r_C68ZT{G`KF+6a6tJ7_)Xf zA5VMUX}Jkz)ehxBC*rW~iV4+sHLhtGhc~Gv;JLl|S-Uv=_+kR--LgEGp0j3ApZ19q zUqqj8+fkYiU6bMpTjKDC&Y59O(!4Sy7UNt@v1fO8?k^XMh4z#=&gA*F12M26AJhCw zWxl*18m~zQ+nU^;Pn;W#jD@C{9XXV%XHZR0F~!pxqq!N2g2_wzx1Su#-**!8v$rW8 zteL=@g-GoEW`eNzNqqc{NSwQDijV!K@MCi*FYYw4g&QaHy$^_$LHFgqHdDA^Q3SRu zGsF06JGege3_EsqUFVChzDsg)5~)5=9)0dB$*LZSxUtOyF$W(>cC1f;jUn;Yc2!I2 z!{d<|W`b#3N+f~G@i=|g1j9#4ByApXnE25IUoz7reP+es&~Q43Y>1Y~wZ)>}HdAE# z`bvIN&3xixij|*jB&%Jh&c7wCwbXgZ%?q)xTWf}auh&UNu8)PxEi)W*n<>#89Sij_ z=BS@EN}~8W1__qbGfW#QQ3{GdRt=pMH;$F)k{;gx+Be;=fBNlZ0k2YZk8+AJdP=DK}Fr){_y(WXBZ+Bpx#Zvj-ydnKzzJ~okL*$ED~SS ztT1eR5BMF6#M@`YdtBA5W%DHho4ON!JU2n>gF^(YM_FUlm8)7ar$t~KaUeBj_t$D8 z&6xcvYbcKYy?Di*a4eW*jhd?giwBJhhnW^Jdz)WtDoDa$HJfxnze*Ol28UtuBJx6B z+LEiJ6NYzdt#PoMN$!z(VfeX`_-i$F&TrI>mo8jG%w7o)rZH=3eg zKc4R0J6^zcC;2p6tZ?7_J*4RGG76wO;Lp$KoDqq$t8GX>{Skl6=zdX5^SRhJR9nN* zdy*}T|{m*`8%iiMF?o>>UFhNq#AKa!jUqGIqe5LMe zaB~t8+fAXHqrg7;CnD5|?jQ!L%w9JEk?Q2Hzd3+qPp2%(Vsi}sG=v>cruQ9rz{}M~ zGGBThefVR6Yj$Iqj$#aDhgzamcM|JAlUSx}tdK3yV9QQNp>I!XG;f&AJd-1_vCbM> zZZ2SBe-K}vw7%UO7qb%!BM@M0iz@+OMHh)}PI^?6?qHh(!*I3D4sv@nnYffPpD){^ z;LLosKq?d~-rA#Q`Yd)?I|Mg&5)0ZygSEQ`lV{EWTB?)S>2}g>jd8^Os4*;Qa}au4 zIiffD${!1)t6kv;2SxZ#M=r9;>c=OA399FwgEgW0%~r0KZgh=J|>SSRsp zqmDbGVog656BGn9^5ou1UdL+BQZDVhuJ>-A9-7R`H4RD?Cb%51jLFSOMS+MkjX`VK zh@xcJoHBz#<`%YOdlJUZrCjfKyV&nH3Gn=DjiJK6 zbDXiqbkC(8QJlwV))*Fzw0~CEc=$Zaw2Fd$5%Iy}F0kD1%T zXQlz$5fhHZ#76YiHfFdLhV9$!vHX+?^Am^S!Bhu$Z!=-r@qe zG2-V<*5i2)PA_yq;=pSxsYei=Ry!fh<1+iOFc9*`oDr6Cj(Oe)!0extm6CIk-6Sn~ zFGm+l$T`l6mXcYix3k008 z5Wox!!=Oz4{OIk0Z0Q#2=ge|M*3clq6RY7HI+GQXFB5YnBI zSQ5b2H3eYhbr<}ZDq>P?B4}w5yFC1`GgJ!FvzId}$>ZEZlr>yiePd0qFAC$dm z&h6>JM!oh!6S0GaS-3NILqDj|y46x{_!*jY&ogkxL&2GSR}oQv*c~2cLYPM;F*x@7 zAjK)3jR^6D3H>_iWDUz$nwIoeAxcrkKV`Ac180|!sdL{uWrB;}nmCR}r;_&LJHRY(Lvg@CT zk=||t)q&}33-NfPKG-5_b_R1xio{}?32r=@$=a4jz)6~BQpva2^_O9I-`|mXZMWF~ zlTc`q9?y_Gq{sS)0OS>Nb}`etz7lpTocJfy+uWa# z$J|O%;6t+#Rlh>^>U9zh*jZt_RtYoio=Ew1)=04^Wo2{+)ERDzN1A2KkKTjB=zY*{H-w1f`Sa^-u*%mW{w{nrqu0tYkO$hLJXg7=Qn&Sg*to%u*!% z`Gi`g+ZlvS@~)7%QqOG2qcHS0X&py4unDyysC2lY;y68%pS7}^2R?+}XFi+!u#q^} z8@ARn_ZhyBJK%*fp_UoY9O;CUH!7ypuzYE66yGNQ;L|FW)$WOLFMToJpoAIC7GVPE zOAFh`pC0OuvmZ!ndZB#I*1D0&|pcw5dY$sae6 z^rGHVD_DR|Y;EvBQJ$k6G@{G<>YJhsE^A zEOliH>}NP4TX@8Z=OiJTc*2TxkJzJ`2~fD>j0S}#EO2QY-jZis)852`xy)&h#AwQTg8yGZu8Ldt?V zrkq?1VZ9Az*3~hC^M!Dbwu9yDdS)>uA6HZDVg9C$b>?#9Z*jz0^IB#Z=Nu45ZoGx0dv4P)IK*=b2SZapFf=z%Bfk0h0RW*)dp zKKj8_yP8LPp=iu=mNqX5N9kwz`=%FcqC54Rd->v`%1dVRF%FL{{D|TClC9rLIn+P> z@%86RW+jdWKNElrt6JHa=~0k*9*7-RTbX)H1b*)g#LJ=lXuWL zGYH?Js@PqsAI55&eyjHkFV`bNU8+{~~b$7SJoTXt%_0DF=zijZyz3V!!JX*xQ zT&=<7SX0z)C}Q1d%^&SE$LQfj?0UZnn%hz?NN^#us=S9g4{65HUckPZ-Gyu@u`&i1 zFqi4YXggyEukrZ|^#uqeR)}uBz?u)`;rVw*+}4(`wJ8#Oa&|^(dk%~Il7lzWuGo4h zhn?Sj2YdIqp>VyJrN6y}ej)ARUA)&Xq-37 z*HkgB&_vwXMKfuYTK1SUGj`-no2gUJlT?a2>X1nZmO; zi%AaEAnt}aR<60ldMj6|nIMVKgm*>*hkz2&5B5tPo%q*7wEe?Ui z0iL}#hn1d+#ux>v%bz4{npy;!$N0m%cRove8-h6yLFslOYxod|nBC+zQ7B<=hlt>! zM@(?VQWkT@7tN&AKah8iDK>jyet0ml#+I|O7d)^#JETj!y>6Jn)J75KrYw{^u<7j5 zA{%_$6o!>6QdqN9nyB7h{%6hl6R%*G zt|!dp9hhH729A}xq6^!O*S?u5pCumLyLBX3lY)K|%T#OFIZ(_e(BF&)Dz^?mN5)sMP z54J?+!EkgR8_9O(6Q8xU>pDl9`mq&X8!@Af?ttCBnV!vkY$mVr*FSDdO1ciaN#mP0 z&50>e_VINO(p}uLVYf>vFpp+xMJZyzUphhO6csZuIh)W^DWqLmk`|h>4*JgwoE4~ z0PauyA#^yg2pP&>C>EiApgTJs;D!Fl0qD5z&2rDWV~%ei(nI}O;5ipOAx)TG?;zHf z?m8)Ff>GT+l;yp*!-4G~=usKY-W|0;CPFcHeiU2VX^H5GVQAkP!=y-`wOA#b?$B}U zsHrJ@eumSX+@F1}Fvq4VU3rXO&Ra8=QI9a;8D&hoF=Oxi8>!z;oV%qrnca_i6wnO1 zpzIveTT+V;Rn%9BC6DmcDmXUT;?BfP?4nOOR(23$Cz7#AqEd8p(jB&R8p|;*#+~2J z5Gjsi<2M%|=BX>rsP$#jW(rsk=Zxti_83AS`+{o&udKe5V2s>1Bvfv%6e!JPbM@7O=m3G-N~-_;}Q(pHAhob`Jo5-oNs$n(wI?)b(8wasZ#Gff$N!dkFa?GdY9(*W&=iQIzctgB` zhbLX2)`O$p2GTUr`|d!u2y7gbhpdI<%OJ4vfn8H2v@*>E6UUnjjwEf-~C zX%2B5_P)fSuf(ga^u-b{8J6E89vZcNxFpq=&C{n$?@E8=pg7wZ)GDsc_5Gc$p;qhW82zXk>?%)+Z#uj zz9RXp?8y7@_zat|o7i6_VHl@(nHAh6U&xhk*lfPe9_^wmowE^GZfeB5Ny|C!a3t|) zOxgM>Gwj$Egv;6F@rURLeQwlKyW}XshaWFtsv3^b&H?1X>&C_m zqcegt@#U)&S;uJ+);I-0+i@^U`tE}xj=^{9&}1Kq&h)wL zAo(=4?CD)LS&QkaIl|vA91(TP*dZf3G}%TVHh(o!>rFjEn@Bp#Ze;GncYSP4pSQ&h zwrMQseSSq^e&8V{E+M}6x~MMe9J57+4UTWY2nEWc`S%(#>zlA^0`c!&7ec4}5ms%m zB0gjQ8iN`!gtS<2P5}=U`{tzIjco#7hyd zz!fc4N|M`}#n`*j9Tq7%lF}y9^DptloEhsSm2}45NlXk~gMZ>cZ2Z_t% zvY`H%vIcCIY3&hGv9zy0nh#6kVO0X@GKlriZ7ufDOiG>Z5!!855Pb^AVA415n;4EI zKZ0ROJvfbiVx%q!z|4Kb=T)u8t72dB7KdQG@f*Zj5bxw7aUK(YJ#mf@H*s}Gw+TA0($)a#E|V_LJxi{KMU9-l#MYPJpXL!xlWYB*EWwZf^`Xp*On zW|u}#zd@C}{n6@dFfrAwFLvb;iz$~P?$L9MB0hg%&IP<^eTpyCE3O$a8WCAfaOno+ zvJ7?BQrZ6y-R_VNWx$i%H?+64JMGXqPDYYGzZPprH)vXMQnJyk5(bx@A@&QG{4T!- zJAYSf-drY0nOTD6l+$DR?uBG&VF6UB7cijZp+q%Azz5iNCLtv^9j3@H*Av-vbGy@?p@HmY1e#9_cNc&dJ5>q#MLzd2Ce;0aT`~nX&9}CAs z>j+#Y-?OGc1U^hBErTt2!dxS%E=Z8tMp-TqQRt~z3YRms_?Hz8mlx=W1I>VCZF zVHx)Rbj4!330!Fk@i&Oq+%HLk?@KSlrV-??%pSq3J@T;Zhc}{bbmJl0CD8GwOrXw6 z$v!%J?0D%1&4y6Pujq7qrt|LYuq%=+$w|;y9e`==GbEXP;xLrnd6xR`#F06X2=EU= z(NPQW*33}&k(Yi$Cnd3c4#dsY5bPc!T5NyTA7^@p;nd>2S}TY9z>MYr+S?Pf)=Lpv zW(cUSlYhdF<`=g0Wbifm~&Q?sS5+Ir z0)i4mMMXtHg>KAXL_o|r=d75{zwRp6&3^Xto^{@D=fk+x-v2AqG~K_xy1J*QyULFS z;5-w&H0thnCFBgZ`4_b%N-8JmBFzSRoAIc>BO_4ESW z7!pMO{iZ5A8)jj=V!Xd+iQl!8PSftip%k@pka9WT1bJc%E=-)NqzzZ-><^6V5^bWK zdz(f_25+GtlVZw~=tHE0Y^5FjZ)a@VzK@3GV63wv$1}1EchSdkQFMECOolbyaWm

RIPOkIup7kNOjNj*@s8d1=e@`|e&=$+MzMb&xIitc$oCnH+ZKPp_0p`Zq z)+vs9*Ljq_592I$pAt{bx|H;b7>gq%D1k2CUY~wo1ja~D-$_pSL(>iL&c}#niPY1u z)$w(Bmvi8VL<%uzz#3g$O}EuVdV%+B*G^nT&By+89^U))Zf5wpy`U!et#;>xG0 zPpK8&ogZOfrdUpXNP*v1QhJR}O0xT1{MNLZO1chLtViLunG+bp_v#$wNP}xM*}|W+ zb5|&_KXb^yEr5EQ^jEw-U!=Tv%nP$INbzl)O;fTr;#u+aiXrZe*k*;2;ZPri0#DL2 z%(bd(;H21%R52FcW(u_GrG&gWN;yZj(80ECmC;*LD6(P%ZNP8Sd7=Ahl|v+TA6iWr z^>jBa!L`}9_GOe}tMS|G(`__i&W{X-`IyVQNem4)eV#Er7UMKb-%g*N6lAQcg8R~m zJ80votc)`$;pFi&mUbOJp0N+lh8y5nnbSR!Gkh@r(oaY9t5p&*KBNWEf{>k5zgSep zXw0P&bUu*^3^rwq_F7A>#dp)#*fANOaF4u%F@BFPel7jteEjx>`|m~Tyl6dJzG3C zoe1TvTLG=d7<9_aoyyEQxzr~WbI)zrrR|RZ~vS z3#65s6ER1sk&>3~Pt$JiB9qP)lsQe;QJ%#fGC%MkqYd7-YW*6&Aq@`Cc;~s6+Tht2 zP5hdS=(%|Q6wHfq?yGnx!57|8)A5+&`0i@OxbaJxeq}kvBHyHRHhoGq{a4ZQ67fpA zV-Ls>*O9ARq$u{C?vRx&?iZ;il=!{Z=_$sboA&sE(%&wR&XmA>*bX_$C6g;;*CUvY zZMmrAce#MytT6B7$V{c0a*kpgF{g0lWW_w}G&N!2RHFQLCE4Kw4X(0<4rs!aC(n;l zee@N!bpjQ(<_PsI6G^oXtx@b29iZOsQ4~F7g;M-z5)HYDv5*ewl=6cTX(WrGG2<62 zf12a@!-(zlq0d~U+~64MT8Q5ie@|Be_D9m?p>ed?X^OI@&lWnH7*9PNCMavGhLO{s z1oF7+s9Y--Lgxo#&YH)gltxX1aL;lV&2S#B9G$&^?!MbiA&!79C zq+)z9f4u9os`@8IZIVO%@J^X=;45WP--{HJ5JF2H-d6f&XVLSRO*DG;1*Oy0GgQYv z9OJoXC|w=UhhUt_YPl)OphxLcr%VJ@sk%=o;hjpx>msRHXrj`{8NaWU!Tj1^W0gA5 zdug~gz8C+DR=iv6qMWxew81M<(Ugv-_PB?$dGZ!Twc1Xf@%?;AbeK}t8{@t-kEh&5 zp-OWj++z;JJP{v*mAScL^zuD^`}rBD)H@wQ)9o=20R<>OFwV^3-MgvYQGcaJod7C} z=UUbkELX~G#`uK`aE(#STj^aF@67f|{70RaY}i}5dwo5fxcATg**x*2GVR7gI)L{h ze6L+rPQJQF$IX`0%FH`T?$X=z%X%fXPkpV7I(eOnjr5}?eSRv9ZsgH1+zaj4yQJFd z#TEMMj`wn#mr+|(zerK8{`4fajOwk+!kBp&yYhZXb$-V)v=YCAjOqGAx!3I^c?V;R z(5x3q-B6XrnT62Uyj#j~vkY3A7)oFEUR54dI7%NY;N6=M7nJSRDdajRoV~+*Mo(db3Nt-v|J#366SKkDlrI|l4XYE=eHEHH43c&O5ug)5&v5PZlVjnp^kCWfB4dfGAs6@>?O5+{`(2+Z@6tmB%WP$g@>SjGua#tOuk&`!4 zywx40>8t~E>rx19Qm!k(Bll8FQ{3CHp09XL+eMGO!zk@_j$*by9&=lS(|(J~N~vZ$ z=%w)%(rvz=94OdE*%(Wz$H;6Y?mz@Bj*K9aLFbefshi2p2;U-}*qZTpdSdl%bR#fVohc z#M77wdlg@di&OS7z61V>RhrD-fccL8`8}mu;R~g4zw4MM0pGjBKPme!=F!d&Upg}7 zk5b|O70iLQf|5cE)OANLP_a8JspfP;bxYtmsy@t*YB(CH;}cKe`L)$#HM**L$~u!S zl)yYS_Epsr&KcBa(mJw_ucW^0aGdr(!ToW(_t(PjDE-8Hyv^K-sr@mI^g%bw_p|?t zGIYri(sjppp%33EF-aIx9eri3#V-`!dw7rDFO;q|d8+()l|){+|7_{%NvygBQx5?y8o<)B~qapAVo;LJ8!>>5E8G`E!d81Eq}Ba$wSyP-@v zwVCWNPL$X40%hdyO*CY3G<}Y`rX-9Gp|Io_@>!Oz%)@+<`Mv-_|5p2 zU9Mslk1=J(#FBFUs&aS^=K98OX%lx}R$dteQu@a@j8Au2=`|m9j_22lo(CJ9DpVd_ zzeax7@JzmQF*SKsF3rUlyZ+}4)O%rf)|x z7#Apj)>Jf5jgyYhxvzmVh5jh4)M2WZi}&$Le^+AK9->zfA(Xu5v!ZL7jNiHNE}6?m zW$vUT>VWaeyL!G?<|HT5qx!gRNPny3H;t!KnDfJ>&KsrF)$MerSOm?8ex;nl*d+OG zku;iKDwB3Z(C@q`dh+wRl6-bES!<)|)y-#0j8Pa3iHV^Gcb+P2aR?PN*g@4WCcu+F zxK^4RORopx`R$khx{2o+sn%nq>*YY|R4R^Q(jO`Qh)+KlM{~0uC~pYg$r{Jgo~;j* zwF!7nbLl@h9}X<3&UVeGHusj&i-OYXs>)ZXE5?3}9$=_C55_yl_?=*T=}PL_5!v*m z(<%x(UPZk>>{{CPH_u7`*eK0samYMLT)G5(&S2}>cuw4DW=--PQ{V`w5_z=>W8w+ z5AX6k!x$P>3YF0L+sGNue2=dDU5UiCVe|Xj$gJKs<+FiiC;scVJRxK~qMeK0GNc1EnBLTwH8R|$M2 zx59kH!PV7k)s9iUeRv)t)mV*6Or^>rHqh#icuz3tFlE&aq!8@K)^88dg)+f3>3Avi zMg4g-B^6co6V zCY{E69C%;c5$^^(-LFymps%>$yOTO!_@k8A7D#1{chYyfkKeg+AhpCbNzs1ew_>E0 zT6>i?zF$gjPgYT5M_#56_k3vy=H%^v@;uEwxq=3Ho2bTL&eCB#19{H5x_ZX+6m9Oi znyP=Rp>}alDdy!GdcUKlIu`Ft4O+U6a<0`-4L+vP`&#~V%GOjJA+b&yJ|4W_eK%c@`ZB~xEr2({Q&MjaTkkGl59ct?1*H)M7aT`V0= zTThiy%ih>Ua}RH(A?M1dZ*jjtJ7OzM#k{c|5wVo_DT1!zUDTF4W5{-66y0zrqXy+i zQD2NHXV$Qc>QFC&=I6%HqR*w(WdZ0n+;Ok|Txs<&p6f8G8b>W-ORJ7YLulTS`bj zn1s1PGYr(@eFACxxqn{Q6{Z=h)o|awUm?ab?_{bz`EikQK7#X@>gpQrY?}XY1=%gC zq1HZqhJGlkXx*lo>dtQ`C}q`Znx?CznsisFg7sSR$*85Cyn2i*?qGhUYcE_HJ@rhZH_4z|E7sD+)n;l= z%27(O#x?r7T54GI5z2h+PXm|KP!D}MM6>q=P?DR8IuU(urL{ry=3W&wBplbnUYH95 z?`_ehz0?uo?aZoFNe!K_haCEc(eKrj)CkL6^Z~zRudHIEe*d08k1@9D7Z)S7y-NqVfJ0?&0q?QcbT!(hNKol#+;d17~i==c zrv}ku{2u%?r;=)MJdh>~+e1TrFg`#NJcAU3cXCEnRMU3_QuB}a4*s8i;ys|#lg!nl z-7nD@LyVDI(L&wb^E_?G@8XtKEYvoi&eH5UcqaM2xmse%DQZ*8j|L{0tAmfIWL9hq z)mmt-_O6sp#d6kC%im^dgU~ek6O8AMuwTB-K1>UT;ySWsP1PdlAl)?&#GJ*Z>fG+h z^x#JjwLD%;EmdtFeR+lPiDp(&n}6CvW)DJXQg&t4AMduj$M05Mx>iwxaqr@Z?`9&r z|N8~+I5-X1Nho#~eRYl{!_=y3!;9f`p$nFErm9+9#khBscVe!Es%lTnIpgyL@3*|JsvgY` zrUq%4x3L7~G{-ej)pbc^qN}1#z7s^|)_bVw^U7-EjX<(}w}&QsS5{xMAfowuXss>Y z2gjIrQ!)2&QO<_tE!7bP7ie8IjA1_AQnh-WMZwLMlVLGSb=uxDG|_e?W%k26;O#LM zL2p00;BTSs2vul^7V{?FF;^>mI7V*fxYuE7u8y3OO1EFFr&V8TsgpXV;8@>4K_hCa z{l*=jZ>#Wrs9kk+LEb)ca0#Xa6BBj!iX`egGK6}~sHR5r-c6akHqrC%Rn$MY5Yja5He%p1S2D&UJ&Bq)u)nH$1<$7W zt-|;eN!X{o@Vs;DBubx9RXu?F?w|2YZBfq0pVm>&&A_t(=F4bvrMhbOjpt}3VIH!9 zb=34}r>U>gN?PB{Qe9LMV`95wjb7|q)MzoDPX_1aIY+62%Q|Xt(Mj<^) z;ZLW-YpFwi9i)S%0d%QOP4#3c%!l$OkiKH9kD*R`Y0l+fvdb}1r@qB_4#z^se4nv8 zG&GUACSWd@nZ{~UToXN8fpOkdWAzi>8ENORh33>VQC}(;+o5d)jhtqpUfGK|Q;hLk zc$kTrcqoee|KK}anu&VhJf2~GgRwTQo2d8k?BRyHJIL}Y-h)P8k&Jhxei@sppT>pJ z;nNA2ht*X5xC7(trX^DNNK@72(?+_Iw41IhGF4yn52m}(NwlJgiCWeQ>w9JoZ89=Z zx8OWjHUaNFHZfKom|~7T%*osHLpAkyyC52icktZps$nksAPT?uPtKD*)>W&P%%-{x zmQkJWb=49!XUS{Ga*F&`N3GQ06wU=JX|~Q%jm=TXIogk^y|hq&3{0m0yob)_n5+Hv zrqSj7>u7mRGu5NUVR{fiaxxx5M?@ zH52vO;9b-a^TNiMnV^rwe32!?Xhca9b;!av(%r{%w7pH#Wes;=ULnl4yVgWKTNq6@ zBO=H=#Y7$dDvIpaL{YJ;ChE?Q5j4&#no7O~6GNQu=5436GNx)`%gr?14ShvJQ?=Ik zFnZ=3Py2eAs_t7t=?UJ;`r~S<8e-hRccXXF+jXYum{!5GZRj5Qlw_)wSdF>I`|qWx z&rQ^okAtXb9n8h})I|N#Fo>oa?fplcr{h{;LSsBz{WyspR5Vs?tK%M2^}YY7=b25d z)HYkr(Z?1Ti=dvBI_%MDTHw5#EPm8cyWBrPZ!qWGi|&@{`6z`_FW^~mCku6AqvPcB z7SBHgn5lpKQYi}eF$_M|R2?55qVqWSd3)mh`WeY&gJ+Nutg5Rim`5*eUl198GEs** z?50J*8%bBuL`~g;`!4uxW$G4VHNQ?gc{_&TJB+dVc=rxE+6m7h<{GQJ9b)L5=~hbl zYOGqc*+w=cagV5!iQ1x3B&Ghqv*xHVi#A*7=(lJJw8VbtyP5L7Zl{tRF@eXdFxvGM z@39OqQ5QyqA|6joF|o&n8ym^=+fH1kny95PHd}{pyQtuhi8^CeAbtG0hf?!RR9B35 zRqyLws^7y@Rq;$j3Hv0<8)mASV?29*@PF%TqTU&WbDxq#)y81Es$vYEhI?u8abxux z_QfE)-z)kJ{(oKP7mNCLhSA0J@D-?Sm)l_$T@ddw|Lg$hwl2NxJ)ugM@S~y7dtJgK z_dzu_rR$%E8reJ=_#A3!V_LDKjYiYT#(8!%sIASIi}j)QHm129q2p~l^ZG((+iXf0 z2i4h38!!**XHz&~IW)kg?wk$KFq>w%o1xomYOalgCfK|PNrvvTiTIQbJz~>2C>yHS zr1=&=&)U>G_!xTGMpyA8w7|w`oJNcFv{~4tcx~-YP2O@ zSwTx`zt}c~meD>;Yy&N?J=3@|)JPk9yeqVt_Rp9;(CXT}l7pdU+8vprpq5&BoJKZN>eUt~RnuF+O=d<9*nP5b%|>aQIi{23acU1nVf4btwo`y0AZyX;gkrqP6I zbIeOZ!?enxQqaxX0r_R1TeV9%7(yep@u3x<+q9K`R)WT0JE}l;Xjh)A2947Ov^Iq% zXbU21Kohl=<;|eGwWU2Rph?>GZ!Dqvw8@jKpvl^WH|s+WYP$?=1WnOKU1$P5qK)p` z9Ga%xai%5onAWaW8)!QIPCKZgoieZ^G*kOH&l-ACJ9(@P^t5)_QwBY&jhfRHnx*}z z=>a{j9Tm_MdQp3@b|2_v?ZAWmpgG#-y$3>bwRP{=L-Vzc3x`4rwCyU7fZovV-ZKh% zOPkq$4D^on`pdDbA~?AwsUcTKGT*jJ{|f(`*_O?=qv5< z_OqdHv|rE9g}&1UPM8nw|vKlFZ@rYN!Eg+I}sx6#L=64qBRd9as-7 z!;;?lL(8&z%>$t2Sf*YiQDIxI1FH?%I>_+t;$ ziZ!pZ7g~=kvfBr(&*r)8hc;j{*Cs<7vKo61KpV077Y;%jv!$;NL7TAkhKHd|S>MJ- zpv_pRUa8RLY~G|aXbTqTeH7Y~IfWgAwqnhbk3(Ct-C60-Htfdz3}{=H_EmwlW8oDt zq3v1Yx+kC=SZarp(2neTpHt9I?8TVVP;0id9zjR4!po1LqnPpO zC(zNX)zPO=2i9-jGw2w0I_5dlk#!4s0UgWISG|OeV=orHf;zD?F0Y~E*|3pspcB}A zySLDZ%%bf(=p^Q7`5ro%^)3Ga>dc;f;W~w_y8jV6mE~o9g1WGJ2R}nyS-q`apwn3H zvaitT%-8iB)QvqF_#HZfeQHw(oyiPLe?Vuk)8DzyW~*-fgwA2#kN<+sWwj!IL+3HA z_aCS`Yt>SN^Ync7K7#84cA$JQ=tAc1!F3Vyf6jF=(~K+*UBc#`U^wrj&)hf{nib)vRPa+LeR95_JgGtYXF*L-?y$ zqBB(E$5=M^{aDjh<>9Ypvo}I@t5Ij%U&DSlR)D{Ty~}`V)-r#qityL6n=7E2b?nMB z?yqAD`&NR#o}Jna)vaeIDj31{XRfoLI)A2I;{FDf-l{VE4QxgLR1?6=-f=&GWeutV zKagEXg6aZUL4~UDgIGs5s4j?Y%Hn=78`!8C{9tCV5~|tAj307;Bdc#~3_pami-77v z*r#vY4`q(SP2h(zv;9!bCf2^ZDf~@rz*MLvj5%j=KaAy@RfiwWq831P;jGdX?r&zT zn$&>5nGN=ZYPPVsx46HB1-GvWe=Dot57ljDcOG*;g6-4Rf*-+_hCnruY}YI9N3!PK z&EQ9|5u2g9C|2-}``cJ@PjmR&*!rzdO*EVOf&0-cyq5+17}PaX7sE!p=l*saC$-^k zXD7ph<97Vty`A-7e;Ng!{YM>@xM??`FqF zLN$9>*?8{nVZ*<0KZzCgXaGNnMFm4Od)e0e+~3Q>S~i5gkA-+ab^F+hEbi}Tw$&TK z-_Kr8hH8>ow`A@ov);eCe}H}N+Zg@<_9GOkLErz7`v=*$7ERzEV#gLkb%$7uQ`}Es zmn$`epTbr;KsATi#vR;0%=*6N{t-6Hx*7Z+j2%47{o^d#s3rX4tn(m%eiUG#k{V9sD!wvNu$BhS_Iw|18c2?ctwg#d||F z=h)3P+&{+-T;P5ddsL|d{48cX1ggo#e&l{OyPU`U^K7(fNBHO2gHce;1=J%>8_pQPBo|J{#R1s=3Dc zujc+W_VhIO3)qbkTKEO*ye(97on?4&|2iv|#{CD^@DuUVe=~Yg8!Da z>IK!kWj#H)|BjVN=KecY<0<#wqh5N$f6unsLp2{*xfR_1z!s%)|08?+n)@Hw(7Ju# ze_}O8Ky{zk-PPRx%wmso|1+EZp8H?e>Uw?Qe_;tDp_;Gk^cwDeWsiTG|+G$X>2vP9}LxLsJ|Oj(NMjuP)#xV zk7(2J78kS{|Xt)WU8nmiV&DNFrVLv71a`$IfmmKNOO`Lg6xemL^wXj@09Z8^F> z5vnUkb^W1AIhvHp^M(vCW{ms`PCrRH;gR zJ)oLu^f{X6t6{7up07rI-|~Dl`ef{gyfJ<40>!me%tWZpn0EL<6=RA|;&~IyL&EbW zw5O2gO)&n@SmaH}uqRYwN)_Cowx;9~2-TU=i4>kUC5u};U!8I^ph|7> z7zou^(uUbkTTA*8$n%y|=^)Qrl4l;z*P&-$c)kt|uRaO+I;2`dl{(aX3{+E>HhV&C z>(aOgp07*U={#Q-_vv}wiiT+>BX333>p*o@bh|55v7*8WP)$9`SjO}9=w=Mh*Q0Nl zJYSEDAM$*CJTvQze0>^Z1=ZCjx9(7-K8`o2rUCt0#`6tmUJTDSpl2%2H=wcidA=d` z?-b-4QdDiIt|3*!(>)rcA>AJf)ik0JUOe9j{Tl zrQM;LX7qbJ)V3LUdh>iUD%`^J&1g|7&o`$5`8?m8vOn^CbBZ={MZP&LY!215pwE4w zwk^nNDiqg(-+g($1zp<4^DQa%IM27FtJitHCGGse^DXIGm1)SgqEaoPwymgLKd7!1 zO`HlaqZOExHRM&}l zrY(+ho^MN6LV3O|?cLAwZOQ*U&$pu)PkFu_^(*d%d^>7l4prJw*-lVRdwMzqYTKSH zX7GG_`n8JZ+f%z3p6@`@j`4g4iq7Zx4pi`t=Q~g(!x_kTq{wWDn1^E^=%U!J$6#LYZ!OPlxeye+Lc%k#EW_a4u8r7B-}zAF{4I0yNz^r0S9 z>5Bdbs_8~ehCpq*QB7B#??#h+c)lC$3gP+gv}HHXcc+J$Jl~y~Ug!Dlhr-|Sd|zBQxFg?}y3~N``ck_VP^B;C{)B4!QT_;???*n< zc)lN9_U8G17-y8{`%_jN&-bTVM|r+KoxI5N{b|Mno*zIaUwD21DP`v)KY(1#p~?WN z(i*B6NJo1>Z3m*Rd43>Gna=YAsge)R4=@4vrEZsaekg6d&+|iRz(<}RM)3v0g#0MVH-p-aqI=Dux>59kL6uR|e-O`)rjg@$el(@d=K0ZdVkys$rusoV z??9_!c;1164)DAKeLBJO4!FMM`7u-(gf=J|0r-}C%9YJ7m_$5EF|o*zeF zuJF7Q<~`zhCpz(t=bb4356?T%^NLH5A5TBcp|<0xVsof&Jk_y*D&uiJ;Q0yE%Yo-7 z&}dhlpFk%U^85t4wvy*3QnwJEpGd=Z@ccwll6ih2HC1_j5`|vk`AK*tn&&5x+bf=* zMAr&=elm3{>w)}aGBAbeCe!TtP-QZ;Z41>nQ)E}3ccux0c;1=nkL7u13YgCGQ)uX7 zo}WS{t9X72UEj#_Q)p`p&rhWzdwG5;c^v2Ysl>8*ekwU&u3y~0CG#gd??RnE^1KVK zS3Hq-p;;B68dq9d6Kd;82&$WjF(aVLO!Du-^RvjI7thb4219v%7JV4U^Rp;@ z8qd$BH4AutHjP-u^Rw~&h399}>u{c*gMNeO=g^&fJU@quV=QfrGKVgn;rY4rA&2MZ zq95V;xzzbN&(EcCpLl*AE&Ids^C+&IH}dl+%NVN6qqmk&jXOGA}gpO5dSJU^e#E#UbD{QY_47m&J^=NAwQ<@p7awvFc(q95Y< zg=Bb$=NHnd44z*|C9`;b5fxnJ`9(AwUE4o?k@2-}C%p%K5?b7;m|h5Auu2 z3S*dS6pW9Iaf@-koi12GZI{sZW<0-yE_UGgCA8g^=RN3BAD;KX^&-!E&=p6X_n_D* zJnxDAjORV+&mx}pq}*jZkA1w3=e?+T2+w7I`^38edx;@p7$Y-uRO2&&wt%#&0aj$b*hVdGfQ_DXUj=* zohP`@nrk_03S8HDmb2&1n&Rr_Y}sh4^GxTt6WpCATTWbT*`xb}8TQCnE@<7NRf`Ud zaWi3M4Sb_)h`nwgG`W<}UgdqakmXXDBpW z=40e?ipkI4lZee589;@cD3mqwAfMD>!*59 z>kh)sp6x-~D+s#*+HZZ}{hwuicJ-h;2ZbGr_6?Q`n>l+>LwjNSqRr~+!p@rFLC}9*XDdm^2N0#@YvXjMn-ks<} zJ+2FT;sGCeKHi5WHd$jGT$eAK?{EI^zx#b9I1hg$_!NVkct+U4;2gSB*rDL|d$q9p zgL9^fu=Bv}gq^UDgY!*GVfRM+d*8)6%%A2#ahHXC745(65cUdi-nu~8L(pcZov_o; zCdWwF-e_|g+t1hd+(ZwWC7Cw|+mtaPz6Cc=K{ z3p{CJJJH@@oENp2BWx@DojV7FT^pQDCEF(8{Az$$pBCW!Dqh&x;Cu`|U$?Q~Jl9*; zAHQ?`H|Gt!o}Y*|)nFH73wtm)H`*s`Cva;KENowJ&T$uZEf){^IZW7l!P&p9u=}IE ztC6tZf#LL*V%m z?8e|`vsKt>;G8J?5GOFXGfBi_z&TUa_8_!p4Mcn@+IKb(_CvG}m;Ff+7)Z)# z{%jKVU9?G;%zL1=2gy3OL2diWzGdJf^gqu9pGd^BQY1sfZI%lAd@PQ&6~g}3(~~|Y z3;WzPPcnHZ?1tbxz(oAr3E>q-ww_@&j|ayp%2-~8r}=eQ6t254F~6i z@xuObkL$lV$AImC7otrU*cQ3MzBJc^OpXitlbZ)Mi52!+FzLTe*fC(@Hdojo;Ose4 z*h6?dcM0Ze-z57MhW%38UbLTs z8ct~_>_dpRmB)Az;*%~5hVOTH(9(QiJAw1%Kf<;K=jBbsXM-HQC}*^=%Y*axHNx%> z&XghSS>XKci?DBi^VBwCTXukRS-I^)!MU%jNxLgN|8LH_z_!;b@!5f}I}`|e130_O zb7BTKf7>JCRKyzEx)n+_p(24f%fLh#rFE3hI1zfdn4k%wZcAvxR`LIA5hCmp*e3&3VOIy|j&FtC9h`U97VCT(oF5Jm_7-riCC^#*;JhVM z#J8Q|`ftt$!Iu8hr)V@2IP+Vg{UC5w&k1`iSjEZxH4^r_7!j`rCY$_)?Fr7oi-kQJ zoNvnha`IxV;?Z7!BPT1i%m-fjPHn078?}hyW`^2$^ zSZ6bEw(KtKL*V>qrm#1HbGGbD2ZFQaf{2?RQS6ShG?@I_E%Zklfa>~{9CiZxlrz3M{sVvP_(ZJ`-WtG0}NlwI)4j3&a!WrgJoLF z{@fjHCdzZ@O4!%Fi*4t9d!VfONvPo!St4$Ubx7PV>}805lKhRqc~F#y$Mo=|m&b&C zE6!5fR^pZ4BNi?8)H#EnL{+ z!1lXj?gcwg);Yf>JSO{=+4$@N*`KFinMdu!XD@*5%C^F`M*Az4!rqDYVHJd}<+UyO zUqO87W5H?w;+tgs_ld?i_nL?|0%wx7>IKfbz>9Oc;^0M3M!&V}7XJQAGWI|=(H zIA8M;_GWx8Mb?!cIB)q+?P@grz&Yf$XftLP*MD=q1hy8RL>qqXVK1*eLcqD_EfN2W z?fofh`xThXmHqZDY~v2upNt0QK*@Xp*mjiba~t+X*|+d(l^wD_e}&IZk>`(GpKUAoEI|B$u~^Tth&Px0&-!iX^CizC-95>^j%d>W zoP#?GdkHu@4;1#pJTJ<27PdDyXG^v!I8P51aepi?d9Sd2!8!Jvu=|4Z09l_kB6;lJ zoD0CV(ihR5pA&n^bK)p)9`ZoMPhxx9$@8NbxILGB5${W9OXdT>IZM|0WVHDxkEzZx z@jWg^EQg;%lQ#*QbL%0mTRNhBwB$brf9tHfXyc3ahh_a&McmC%#Lt1}W7+>0Azrt; zh#x`RQtpepQ67{b`z3pDju;@?T+8*Od|9i_z}Yqs=2p8q%J`(XR$tN1MMtJ}%G+6=$ijBO=a(kD6^NPEoO>b~s{8rdaR&xC}=NDl6`nzbu z`w%nPhujA@?HduFjBObxnJ>n++dmNT)!43XvTx~x_Vx2c+!bxU$@O^!&S~=a;(d;r ztpDyGE&C#Vo%2|7XoSzUko$#St2oQ=Vjr>7b z`{*n_+ejWS@rXZ66YZBF9xC@&EpRqU74e?n{8YBD0M1#bMcf&j(=G^m063?}^{D~Q z88=0I1~})*x^e^OZ_h=19ym98FKo`)`J1qZ&*A!S&OgES)GyJ7GrumGPY1V`vd&9m z{eMdSSHZ;ai)jA^oU6-yVTJbfK8W~Cv}qytOE5ThkjF(F;##?ns(?>7*{9V6PdmB4 z{1NZ}hUes;_Zhzy~!Sk>E2ix`6ZVLW??V3f}&o0vD0XY9{Q}na$VDdLU06hQN7mCC? z{2xC1wn&?RB7bW|k+?ZH|6OJ>IRCXH!1=HJeH8yLE|^pU=fC#H?)-XC#0P@&U)u?s z|JpOb`LAsU&VOwKr~ld_CYDnSoS*;ZIgREw>bb78`+%E+tn-)P94zCI?$@M7& z=NodJhk$d86FkS>XIZGU1#DOK!Kv{_mVQTPxY-E9?;Y+4103N-py^IM0{d zIIBl;eKg=aM6PFDw9k<1&-;xga=T){DnxGk5yXGX^KyMKnJSOXdff%84t{wz3emdmsP=MnPn zHUZ~qa@~rd-^i8g>;TRoa=Rvj^Juy4yx*uT`SX6`iR5NA=6~m02)2&0Zr{Q#E!i#t zx4m+o@@v##k|(dLuae;l@DG#g(*W%|$n~6oHaT+rmxA+j*$;D8#brPI3(E|BESOXR z&qjawfYlM%mqsAoGvd0iOCf$UU)V3P%(1z`J^;>tzYCYedGNPv ze;1t1pUKun@NVe z&R^UW+m(xD#@-TkA>s~_gEJT!N^UOTd?ZJ-sf#vF@|vUs+7v%8;(4&QofUQpmf87~ zu%Cf5lh-PZz&S&<{{qgv#THka3R1HrkaJQiw# z^8mTd{JL(jta*N2mt5q!?z?2duj>Zj*stGbn|1GQ{O|mmGw&YtmWr)M1&Uvc$GuMBG-^ zVGD4xP(=JE;_oEGJ%}HX3|HW@^Y)20{9a$ZUBd2(_*wZKa|AeNCWyETIGgVmc0+Jp zlPYW+H`4fF8>9tr6=Nt=e1?v@S`*u9` zZ_cm4)>iiCIj~R4W8oUOb&>t8Bi4V1+>cAK?SCZOAaEWa*>*tt^#$VZ^6!J`^4Kgy zP2QKs>`yTHB5P?lmibfiJdE}qB~N}`edC~5&Pl|RlY~7R@gznkoDc^~^u z{w)u1o*?VCGumfJwk~L6DA``)=Q3HVyf1Z?$9rQe)8nxCTieink*rUCZ*am+5nqFN zd&#E_KKp62h!;bf$Y5cYLA=d6VPD4j>|HMGEN~9+6ZRf(z9!o{fU|9si0=pI3yH#B z0M1^Lc{^|(oGIeI;N173u;+ub>kVP^@7;SP6VAC7xDAah)_LxS|6Xqk-h%mKu-%Wo zg>ya$d#mg#O5;A#T)Dsa_u1C6hWUB_x;&1Yv0c-0#b*heA4#@T(Z)`)JqXVJ$3+`{ z>>roM{x5v?>K+kajrN!2@m_$xwLe0{?Gaxj>+>VxR_jIl7(TmS^2|ZJo~MWhBffZ^ zuuI`S(%FT=z6{Q5W&8Qy>=7v9=fOEFQrIiN`H}2@7&tr0zGW>qmy_qyMd18YZtr4n z{_;S4mfuI}1a7H5RXS%S|L>gnx$l`g_Z@>>TK4U=!MWTsv7AtB%X?YF{9JZSp34Se zyIf^|%kS5H|9gHxo9VJ{1Hn06vgO~`KI|6DGOrX8Sy>Sg*^=M>UZQ|Gn252+loxMf@o^f0utN7@S{h z6Y<{QY$A`Zjo_@y5OGg%ZY;N#-><7GnQ+d8KK64htIo#%`K|2V{l;yuwUnF>!JaLz zTRMVs)MK&yo!FM$@*1oIwtdr85$}lY8Yla)Uf`@zMcf%}yblZ84V?33-8zDEX~{MQ zpEZ(f`R|GEHi-6H@wehv2)lsy2_C|pjd<{EVYk3%Pdf{H0^->Y!mf&V^dMoc0B2>G zuxsJ|v)^Q4CxLT_yRb{*{__i2=i9;APaZo%z!CL|UyT)U-p3Y7w)}U%_mZtG+Na3v^~B%mvskp}-woqu z2)h~Lmt?Iz!u@<@xQI7Ed{G}^Uq<|kjj+#vvwJsTcLV2BLxp`FoL%KUs>6RzSt#NM z!1?HEVUGo8x2?jC1!u3l!uAE{x0%BB0_W5D!seU}Uiy&ZLfg)h%m443^TF2tvuGa& z+ZOFPa~E*VyeI6-*cM~C%t~O=Fx|j~QcLZYS*Li0|tt>~)BrY%FX; z{BE_irLd=g^CHRoB{-+aePj#HlU+rd6X3kWOV}>ptO*i!5;zyi+Fk|D(~gO_4>*sI z=Y7suMIZa6O5e`APyhG+RndCp*K(&miO)vCj(aZbrQmEYuh|}9+fT@4@@uo7vLAkj z?fNcjcsMwx$!oBQ;ARsemh%g?w>%#2U^$+$zcmJ*4zf?Eh&DTA-P)kdRmrvn{#No} zv7Bva->ti_`{C~{YbWeoh|j7oY&*nDnFxC@IJYtr_C|2t(n{Ed_zCe74a{5q}NNq2q*I8Etd}g`E!jj#k*6z%#Cy zu=Bw4LQP@!M!dJ=c?t1ypZRr_MspsVI~EH29ys5vB--}@XZr@ijsoYca{1rEIe)l_ z`-5}kS;D>q&d#fa9RkkJU#7DglY`=oD%Nt?;0_US8MEeQgTu$zzG;p5X zTExrZ`3*z)cQ=FcZ&}+nz<3t8+Gt_( z?|Myo3p*TkMtfnG1fNBi%xk6j6#N6U)12JPE^6g=l4&g3&)ZNYih zZ4s{z&h!8CeOROM1!o`G&)fs&kF~{eEWx?BJa%@0^TVMc{sNqH<#{9;oGk-Hd>uH~ zNESAK?yK%KVHagv)c&8Gi^+QCoKuT%t}Z!029wG~IENMC994vKb;#zyj zYDvx+;I_;~#BX7lMk9s&4czwi5cYDk??Qk%6IcG(3=HK7* z7m4_BeAY^Gz6s8OBSicwIG2^2m!kcgRwDia?RV7@Hm}=F2Eu-WcxBlSk45}_uHZQU zoOep*9l*K0dn{ z+Jkexm@DE%*%r0`zj8J%!r8D0=XFImuPeg2un6bVML6Fn!ns8e&fAJ`HZQ_?O%cx7 zlCwK_W=PJw&bt-i+&oWk?hnqdBL*)(f&EDNIln-ocVdsSaRn5b7T?D2}Syi`e9-{`8{L<**}M& zK06fYH)18{+gK)({l<6H^GnHjCE5qe8sp!IYMP5>dV=$$(!&0Nc)xdo?G(g!%V$Kp zfphY85pN03qvgI>2hIaaiqAd=XI1XI2H^a(orv!R=bwXw&HIg>@;lUa)U%2FF2MWe zti7TQujecOm;FZ3>y7jxoUMv*ZdinKMiI`pi*Q~bIiCjSB}LAIHzns2;Ipd8d2sqD zvCjN{se4ahZvf}DZH4_4?Zf0bi(gmh%87Uhv~Tc9@LY(v%Pq+koIlBTcItxjIeD&J z3eE-c9C{m^`_&Q4u>j{GorRqU&X&W4{S2Hx&k;614;lvwdo4I$mFGczy|M0^h!^cE zirW9P-za+hd{T1e-&0x_;oMzv=GS#^{~y}kJ1mN&Tl>a{Ie-xrvm&S%QJCpj=75Mf z2aFg{%t}z{8S(^BOqes~gb`-CG3SUGbHa>SL=4~EyLx|5UEAk5?>XOh-aq!W?_Im- zsrvP*RjXE2cdMM0xUN1I*S%Fap8;nVb*!z%buOxLopR>$syZH@z@BTXaa3N^+1H7+ zS;^f^@?g3+c($s^^lI>QD9Lm=lwVL|ws|P;^MrAB1n2dcOqT@bL+W|*Dd7CFH2d3g z;M}hs(`CTftqs%bz&U;}(~rP;Ul7wP!8v^~(@I=d?;z6(=R6rq^K5yoaOT(D_Agj{ zWu0=K&VxMe>$A63UoL2&j~=e|U6cJgQSZ-H}fJ=06U*?ASy$~t9(jcH{)Sm7SiJX>D> zlk?xXeTDPi`_xK)W1z}eVX{!=d=sqZa-1DEv)@wUXWwN^r$g6MW1;Q1&Z;@A{0{zh zQy|k>xTfI8v~te5e>bL&LBH2Atpm@W4VbJIA@ja@K}h;5=KMJNDr^ z!Ri|JIsUex+Ly1m<`T49$xAq+&i-C1Pv{7BP8Kqpb&OYFxTvIvEKS1@v%6a~0Em(OlIDhtK`Y|}isx~|aoRj9V^26ZVeF@XbdH&el zOdHVN-e;Ls@<{uiGR?E)wZi%5CsrpBx-`c*OXZx6{w`EGzXFp&>V3>Wzy5x1PT@Rc z3%ibD&+S(*{Q$Z^Jkv^EXstR1mGwoX>8$)0t{JTMQ8^o#rrMRV4*lfK>V$)5MSp(9uM^XK!P!?GM;E}^eI_dp1n0V{ zZR`POw+*a32AtikOe^{4+UZOy`RCs%H=Zr870$mn&gInliLmFND(A20?;(}*YcT1i za=wRtojSmNt3NoqY-L*E9I}$>=g=qAwdxsM$1#GHD{(^LET#*g&cpFcd!o+tp-flC zZ?)*j^bXW--ht^c_}x&|Z=Xkb1(kUa$}84lbxMQt8P(7DgR_4-R{jWi{-*<(b_Zv7 zbxa)q=Z4X&T-i_g`scyk(ka%WLI%tD+xSok-}) zPno_B&abaCT^IcgJH@o}yw#&aOs_+~DyeKog0shRR_+IGr26DI=+>%F9u0;*s!v{o zzYSDv=_0Oqusi$PkKop_4b$^cKffB&I-$H_9aerGe)wDkrj@f9nW)*0*MVI|Q7w2QjVK^QRe12Y_=obqwwR=jZDD9*OHMN@I2YdOqa_ z(+cNRubJlA@>+Q&rQCZ~XPUxU_2-Uwe(f|!4ei-Vn%FlE? z@C;D%%t{VCTaDi)qI|fzHYtg??$~oS4}J#cvG(k5JAw02m3bUEuT}R}lsx~nk*rP# zIJ>C+Ed`v%t9^_G=TWMSDSqSG6;@y2eCQ?9JX>BX&%;*BVs(Z=m%Yn$4{%<3hG~WE zLDfGf%xAA><*rH})%zX-&R(-wxw3!VZ!FVSQGZl#rvEx$q0aBy!6dmpEANduZWWmx z2+joxGCdZ0q`JOPVvMnB&T|f)#}qCzhCR@RB8Qn)o@LJ6&2%C--&LO@ZjJbPn0hb5 zz&WM}yZ#YyF4K@{JH&NI)G?*R8(pTd@)O`ZF@b4i9(=N!X=UGP`~{|!dGP3Srg^r! z-U_xZZ&;nK(Dt{Oo(InTRX;3)LtWK|6(3?qWOd%4@3Rd|M}c#_2}~=`cdzclv~phS zyBeF-0=NDyth^EaHlsAtj;NEQt|OE@^Aq*COC?8rSmoIi^(Uy$w4TQE#ZFro&l4!O zTf%f(aPAYubUi!|dub`t3&45SF{V?&dA}NCXM?jxS=NS1;JiW0^m1_C430{yk`B%< zqL@wq=VIzyuk2eLJUwz}>U2EL zm{ffZG-|PCE=H-&Ef<0ewvJXMej2oG*Gat(@1gsN-=R>KAcfz@o73kYA-2uOcUzVTF$icE{KM+nLY@HwTCkO z9SldcW?Fe~l5ZWR-{Lymi!)8AbN#DwPQ}iyEZ)Cy(Sen30_WXbnbw1IP4yinyTCb0 z_2IL?xv`ojxdzU`s-5J5^MPTkFYCa$vpOHeg7fMWR<3aNdcZW#meN>H;TcD!WANUXC0`g%E$XD-X4)V8?;U3P zA~=_P$aE>Z=VU~6_P6K3x%UXBKY??#bxbb>=N#3xUxIV#O04}wVf!moAF>&o??$q6 zC0|!(AJa-6>4h3U@N9WK8*E!VX7%3=g6~y*&NXl@yPlORd#Q6&Ur_~|SF7uYwcxza zi`6*+&LQfY(h>DT)xD%1V0h*<>&r#d->CWoGq?>1Vdcs>>ejuOZVt{T>N8y%42Re= z-2n{a)%Wd$jzEs<2xHh9*EzC|>4D&1Kbq-)jj)X^OoxGU&5ul1#`|Onc(H2=c+bfm zk!cB>8=PSJH#i&An5PmrTidce?gZ!TP^Ojrlu2rApl}v5SUJy@*GkOX{2{Az8=Rfh zIJx}vP@1UvWILSQatmU0o`K0&_5Kb7H!n9^^E+{xO%0rmCa zO#6UO$^J|~M4dw_+Zm|yH4iJFgF5}x_kGm{L(koeRm0)%|3;=8fq(h2Ol!b@b!Vmv zC*i#4B&MH&vzW&8RB--Uf?eMP&iRKh{S=&&HZxtW{ao7pp6UAFoTnM<*FJEbr>-C4 z!8vF%D_1z@yTvrmme=FKx$%8g=Oj4aOJ#ZxIPYD=bW3nPsLlo3!8yAtMrouQ4V|!LVx*({;gc zb1>6i{BTdZFXTX%T zNqyC~B!lxJbv?2g^)utxb*6%Iwb7~uXK!`B>i}-E@Y~829l-hc9oEiT;H*5atCVNJ z4}__6y*KL5ZOh7!gW*xtu9SE0Tv2`OF!2ArfiYPJhMm>-%T)lwBx2>4hay){n(2io zH)XPS(@Eg(eT3<8n{ZA(i|Iw+yxN!5FNgQ4IqzcH6Yu-_q0XUR;JketYnwMXpHkO8 zJ;C{)8s{HDU(2YmtMcBS{mWV3YayR^I2~t6#f+4!lQaKcwmj#4;9N`fEvvw}eKPyo zXW(of!}NA=9;IGC8k~oz>$uV2W}nO2wja;?%s9pLUik2<^O^1ih9d?t?WEinbv_CO z=ji9GzsftN9&Ts4D9T4pWqJi{xTq)7*VBWmQ7-pgT%qphKfPdr+rWc+ASjf%C-e44*vZRi8e3;%j%z|Kww&Y^1ja2j>$X<3~Y@Co04vVOgWt+tk#egqqin#1&Z zu)V8gT6x!HyPu5JQ83I(W4ayM>=nhd3(9x&V46_=q9)UMw_yAZV0r}J$9PhGPvcf_ z-mQ*{ci=3l@3mVA&I45&hy>?`st=h1&V^sI_Fo3)!fjaJ^E%>u9oB2wAGcH9f1UBy z^KmmhyQQXtPW-o=l{S}F^G-9sIZln`rhs$F5O&Qk;5?@z)5<(}UL8lu`$B)HXHh<( zj?+rk9wnCRtj4__sB^M1E8hpsZ`FCNIyhfVU_AA(;YD4Tt_8N$@-sbX7@n<@7^_*Z z;W5EX-yVoMo=gu!`{iGXPqedJh;q;KOdkPf_X4cW4RCI0VEP9*msiI{TfFzOa2Wfo zYv7zOhv_76ejC8rybzqvTxa?=I8VTw=s(s!CAoA6wTKGC*|I9BLuyW;8fU|3c>B=Q z1LX=TnvQ9B}Tjjq&UQ&Ua@sZ4Xj-=R9S7@dIb4HB3JPXX^l_qrf@Mf$7!YT;n|Ba|oO(MKkSyb~<70 zp|DkQxwq|^-VV;)uQ1L>!TEE3R{j8-Pt0bz$nDwGAe-p{;Ji=u|1ZIL^Go)-hrl^= z5Nq>Ba9(hd>4)GvSzRNnf#2A!@_ASoe|G}!hVZAjto$=&Grbgb>NH{chGNg}SzlIw zbC(@VcY@!zHiqe!;5@iG(@(%z%3w@#z&YO{rhU-P$*Ql|i}veFv+`5m?0S=NJ`Bz) zRC~?@XOAFOryMw!Qs>7&aJC!Ge(NVVCucBy2AuQyur?n6=hg?AegV$)omk(M_c30Y z%5)FJ8%b(B99%^y`}3T6H?-5J`sX;bKV6NztAO)F1G~;$aPH8H>79yCsLr$qh5>I_ z8~ng|>prI2gL6rB4RRKB_N)Fm6P(ZfV12m_&SO%U-UiP1W-#po|NPCJX%{e`@RIS- zzzDW%6o^bm6*;1=i~PnXB#-zQSJNluC7-uqEYm%~xmYUG?=qCSe{$}Hb`HMH z>MTb4=c_r5JmB0!U1OdAXIC{pzXF^`)?xLhgW*N>9Hs%BN84C?l;=9#)b&U%>NM=g z%Co_FULmH-!B+&HV*M(P@|)pI?*!+0I;JOrv+Fm;WG*<5+{5$29oC z@x67_+)ENTI~-^2QO+$lGc)amxNd$ArVF7ym0`LkIQyyVkxAfuWIpR-DcE^UweQK` z{H-7>SK^K9YP^vjZ4OrbzyxqU-K_2-k!pP21e~MSv)?L;IQo{lhROlw z!@;c0MZo!#niJax&V@QL4$6Cpmu+IY{9ktTCucvjIbG$v6YV^?pVi+9&Xd)Az6j0- zab2bFW58K#&U6oO+gE_;*Wj$X%G#s6Z=}R3rj@gB75tfYLHz-4Oh<$B&9|&yO;A2c zofEHtv*&nLF2IKzti`ksI5)h<_$V<(p@mF;2j}$8YB}0J>Nn$@0M2>Jv2ulTHI;K~ zaGraCm7f6T*^SiS0_V0%n63oQQ|(wgi)_a`PsTIt2+sOzOs9Z{SGtp^QGJ=-kM>8XJ~d0uPXA0A^!1-|k z)4jmCrOy5#C=Q2LUV8c6iGo1j=n@2MJ0`2!M!*mEZFRQ_{G7sje z^Po34e>lm??}76`HHPc~&hJI`Tb|%t=NZ#(;GE&Z+F1*n534cWQE)!0?#mAV=LD5i zfo{s*{mEI$*R|*JbsyCDWH2}{R`YdQaQ=JmW+OQFZ^zgwc?pjSO!oxm1ut29MuD@> zA*RcYL*6Wc>HetSTiw^V0G=b&ynPSci(be}D&ryt<*ODkT>?I2Q4gk_5lc5vzYWm? zoc+`}@i90zQyD7zZbz#!h7-WKx+~MK!TFpT*ZF|6^9fe|0-PIGV%HxC&Oc@{-4UFJ zmXux@zj5`&fnF!Y;ker@BZW*h_;n`z*t>`J#4ly-5Q)b zB{E$HoHNwCs}-EJ>K;l>aPx6yb*6#yb9F9O&LO-|^IdJhxo#Y5+Zfaj?a%a6@U*IP zVmvrYY7Tz@V(C0;zOFYoFC51DI~trvRb+YzICoL!#AdMbI$@04MD*ph8`DF;c}5ea zpMi6v8lQ9p=XS?g`Fn8wuFkU~!1dWgcKz<)+(M0|y}@~EBi82T;9Pb&)5`ZIO5wki zdodWCD}*!M@~`#lpPZ+nZJ8?P8{j-dR@s8{-|uQr-p93g1}j(A-4)cFl?L4Usbgm$ zI5+&p+K>#+Q_nE1yyrQ43DdJs-#n7(@8JAXofDPwMUEd>A5+1(e=^g{6wZ^F-T}^g zRbP4^oF}StVhlJ3Ss1qi=u1pXrhULUUc>Z#aIQUvX@&D>m2);YA1%$UKOUS@)V1LN za6WmMl@9>tmo-?Mb>RBJ%=Be&4pw6=<+~p5LKvT-Cgpej%; zX~}dE)amz&^~(t{-g>o<55alyTvq-CoXfXlx(rj#Br;Gr{@95mx^__6NJEvC0#0&O3*dD`&S-)wSVNaPw8? z+{@rRzaXn)hx$F$*h4wDKM*mf(*DJ$KVFSRUBFpr$I3gRPE=8*L%{jbMU^LPezApV z0kO2DBh!<>x!n)Owgvdt+s3rww-b6XT?w4?c4zt=I2Vm&x-&R0JjJw~AI?7)W7nJs z&h7d$JqDZysrm33;5@G|Yg;#P9zTWY3~+YHWO^7l-xYS*2cS+lwwKE)b)>U9y`R-D|d#t|`z^9OqDu^F5BUH^;dh$9V6vmM9TqH~9A{UK^AL{nH^j`L@gbE`N1|2XgD{Kjm~o||*_yoIx8o^vM0 zxe>>?3CDQ}$N3w_IfCPC$8o;TaUR2QKEZKr%yD+(I7e`t%X6GRah&}*&X+mPRXNUm zIL@C`&bp8P_c?FoIIrXE`5DK#A;-Bf$9WOQ`76g+&vE|M_h0ez9gg!zj`INJadzZ5zvnoQ;5c94I9KL4_u@Ez1n0)dy*;(x{^y)ObMxSi|6tF}ID6(fujM#D zUuoB1Fh2vb4<2;+=T$1DbhU4tVaX!Ow zcH%g9=Qw`=XKnqVo?gHH_c(`geuL+{iL>W59Oow-=lUGyh8*Vvj`K&3^Bj(I4#)Yr z%18MQ?_iE|3dgw?$GIxUIf&z2l;iw@<2;Dte3Ii_p5yGxaefERPxWIwJNy|5A99>sIL@vd zXA8&qEysB#$N2-t`8>zDC&zgQ$GHN>*@5F6z;U+YIN#$q_ux279Osf8=T02wH{jf4 zwBEDLUwgm*-k$%boP#-g=D(A^;XiPGz;Uk2adzQ2n>fy|InL8L&RHDiGaP4Mj`J3d zvm?j31jl&{$N7i47pCOvZgZTwa-35*&c!&+9XQUf!8zn&q-Xcaf7)~JfD_i%nP$rH z5UGqyw)N{d6P{BOsm{^bwi-K)6csO0i5=dy*(+kH^B$3ss*koge~Y1JS48^dJ;gTS zcMLTtq^I2Q>9*1tF*r}Fr}bi>Eox;94W6v0;p6>m4F|_i>UupTHtb{@|2vwD*Ys4q zxT`H^O*94D8E8su0oyI#XsYCHpfSZ#tZ7A~>E&<(wQJPD+VWNuB`z~iGyndnb8ke_ z_;m&vGSS;>%KZrPn_{4laVNccl!~BEZ4C4&cePj8$Z*qxva{)F_C!5Bip=M=xTBsr*3(m|kgJ|Q?~2sto=8%g&7N5yB2|hJ zsalS~(`$f8&BrnO{6Ql1@6B|FdLm8KGF`QdNVaO?U*Dq-?lGGV|6=}u`Du|xhGbZ~ z|1wi(Q*ayZY8(F9L`R2-bogGN&Ht&93gWj0CM~p0dl*Zlwup3d+*aE_-&k67TBNJh z4%nIwilv4>MG{Y2Z9TkVX@1ouWeYSdOF|=r&o|g37Y}@4*Lk_Z@ zE>~M(^ZFT0noK<<6_$Bz7!^*%ZtLmynQ*T-aDKN%PlImwdffx( zRWtQ;YIiNKV&J?=r>DT=d|peyIV6vs-nLEmybI1Gi`1`ilII<8&OcqGU6V!6-r$_k zpYbf#N~F0RnDzzd*NvI34$hmMm=?Z=(S?6-9`-HE`l5n`iZ>ETpEJPbSuIEKe(7RhERE=E>)by?*)K7I`6tCU4SH`_Zp#5o2O#?s+}6aldNY zTr`F%f7DZ_!Ut?4Y|&JsqJfHhSYQjB5smjY8^{Uc$cLh6MQa27oLkLiXb?$Gr3_SZ zYO=Lss|fmVS5ITg-%tHGJ)F|P_Un?WUZ%4KdNE5+{`q@*-3RB=o%9rzzm8WeaNbl_ zPb22$^GXEgLJvfmmVDXsFgT~Khh4r}?|BZKYmXEu-{erw0B|1Ag)wvj=Z+etdxLW# z{!#9=!ueKNruV=5lk?c2d2HEDEtFUvW8gxBt(A|N%)K#Yh8(kX7;K`7L9l2499#NK zBUM-;(qtb8X=Di_dF;o1cd916sb{35ry?CXQcpTs#z=V_^mL@LyJYt`mLx$>?jssW z%QnQ)gt2;>mefGn)jF1XDz@FbuJjsy=6SN7o;|NDWrfC2+v|E7mQ+I8UMGfXebdv- z`#H9Ni_z4gvVlI^->`j+i6R^L2VLA@vzr)6*Iwyq&Z9ZD>C+;pZHk`CPII;`SQ<{= zQF@xtB*E(Yz(B6OaNk2cQnk20{a^#*`?`2F2j@w!jd?4WJ!c&j>Fd{vo}0k=hFK)d z)U}=xIG+TEqHAY(&H?9q;H&VgjeqBB!t_8ezFy;Bobx#_9gLXbpZ5G>KoMKfZWgM9 zziC=!g>7<>ncDfn2labt>zrt!)JY<>)0dNCjv8sKMWkZGn@G-+jilcyQsJiUC7*>x zs&`YQ`HOo=UQtGRgE8nkdx(_P-$=FH^t9&c2`eAr!LierLYw-7ihzt@1CYObdyx*5j+WkB zHPW2&deVV(IpBJvp{9 zOO2{PgZb?iQBvxn7+MWGpWaH8eA-7-^i|AjJtj-vD@W0Z9eP^2rJq!*R3w#+(335@ zrSz^^1a0l7$C>qV(mJ1TO2!=b(e0^i`+Nhf{4P>*@Cw_~EIs8t2V0!q+-AcZGGVz$ zE1E2_j(aB3&ncchN|~wIP?45Lb&~7{m?#nVx<>h_(moFp zeNGUmce5DDGSNiyQbhXVx=7kP$wb!sm`}4-OV4|lXlq_QZJoYZItV`>SzAv7^Y4;! zWg~rSuczb(d!@-kjTA6KPkmb+kV@q-Qb{l`W`95mgrEPB2pjIaR~lX@mb&iMQ&yWj zQiTK2v=V-%h5HUE%NRuq6EGI;u9unwN78~o%zXiirF@16N`}wPJRd1d+Yn9%ol$4& zXvy`yfl7TAsmmsSVS#P`;&8AFDIM(8Vk*IMxE)mZR38J zDaA{q7FHKY>}sYGec-oe_(|>?Of+kvNE7U1q@T-R=TRc<*DaG)-Z7E?7PMjfR%t_~ zi43rlh6#rxk1Hlx^hTuT+m1`Ulfb{2o~o9)Bo*&uqI0f#T3PLuA#uK*^U@1 zx^o|M^?H#U%z7ya^W+!!i|r+PN!iClO6Vcdk7~80Yq28vXhm9C>V<9AK#^*>!JoVo zZBG!Rb#)TyUX2CT&zJ*utDG0&eBwX-#^SP0w)hkaT}J&yC12X^*0oR${@(W1L+WHS z(}pf0jdGeUwR&fwd&4oO9$h4b{x(qB?+bIx*Bt?gvH6t$$fM z(!)$GuEYP7d?+O~K-~Bd{%&oS^!}=e@{~Z#I^u`qKi5P{>%iU$<(JF5;C|`!bm?3n zc?9XZ)wJuwzWOBMf_H-ZLRcXq?z2tV}AURBHd3hQ*YSz0PAIG?=3T(Su5h3k8bwVs&e6}CUR`7 zrzSCV8uS@e#eIr!a#eeLB5bHnL6+9NbFP!t&1SvDQh5m zw7=L%54qcHJ-J4}7kq3em#V9$S<^&HY33}~PZ#OIFwFbH59z@ik$f?inWoN`M!$5fC4ASNSZQXonL2kC zY1Z6r((BV^syzrXcKAtYk1Ko*+HiaDL#cgl3w6Q$ep)(5${b*!?duQ&^(rKnX>GxK zDnvS+;V2jWY^K?l5U0+qD%)=}lm3ZF=U2MQ1BRKYayH^BkLL2qG8iL8^c2vEQ9iZxc=ziE_baIK?`n!i78UP*DstZpMnh-qGYeN1{$zeq)k~- z@|QgDEvrQOy*yIZEW^5HzDQg0hsZPQ>*-+_I8^eLOB@m@a)e0bqtm4{*kc!W#<}p4 zKRF+pU}r1x(?T;!igco_opf!Bh4Nwygn#TVy=!Hm>#*&D^W&tJu+MJrQ3dtM(wc@A znh1YV`rRdI%L)ta^%KcH?u}%)VxeR(&uyMx9{k8cfzcvGoh~gGzht3T%MeHPsw(e@ zwa|tgBDJ9ga#SbGziGHHnO<_s{1)=Kj5)V!C;5tGrdp41-)HubyKFVlZH)KA&&J3e zQ;nnnx9sdWvd5TM+VD-JPupYUpQB0VcITff zFDYrFWH71o)+|?XFj5%CMgAvCWO|Nk8h6dW? zDWjq&1od~7IweQMMN)?g@O9x=W22e~!bvgt@YC1Np$=IGQ<1q$ZhVKDGf1X-j>mF`((tH%TeER ziQFj-bLCp(7d$t~%@BvT*n}~@?|{7VJveVi%y9RF+@e$r4ci0%cK@2Zpm{Xq;NJas z{zTs37lr*7k?Pj{Bww~f(kd|AH7-wDMKDhXTh9W8)Ap3X8WYSv4=9$ladbF6hjwUU zpH^`p@=@^TGv_~&Paxj6EfvXsm7jda2;c9`IJ*b^$$6}6FRP_h99_#Nl52LRZ6o?P zs*s3x^mLG}eX@{H9I?8%N=4+CvGHVq&zbkEl0487PxWyxO7CbOKN}TKU*LacN3@bB>f-4JY(T!%OTJh- zo(gsqsepBiT=HQY8T(*f*Ugr9i540&6zjF^COK}fnb!LwX3be4yJ4;jL9Bb(ahKd4 zYtkaK5bsVpBLBpCGp~s8e&ebf{49nDW2b0vrfdcC8@OMRF-N|jji&o6aGkXU(}s_W zqCniEu^&pMg)WMujmd~hmpP@?&y1i-65`mG&S_trBIqFa*V9x?yHqWl>Vfl|(!b=T zANBN0x$l*OWFZOT4|b)*(vv3r$@#{R0jWLu#o_-ec7NFx-#LyJL2vbJDRpfSM@e}^ z>f7HS#TSgDD!3P47jBaJoR7nE(;_X-Jte)H8c+E!78VtLDZRWKPm>D6pG+; z+F~NDy--db?KY1BONzAMql;X-#p{18+E2m8*9^I<$Mb>$!aIckMb^Av0wsV#W!f3!~0jf*GG61eXJQl-Gv;1B=f z(=J^awIQC?mli3g`fI6qSUeR5XUmDaa^j$Ps)=>f8oyHVt;X?qw=nuU!C7ARJ&w|# zSLD=}2V*YqDu)==zLnf!s+pFdypOKGoDhTD3$D{>-xN7+3 zWV~}*&MX;8`JabT)VT+;4*9^scuwkY^PBQ6T{ykE9!ABpcFVc>4HS7Jj843+Cm-7e zdwjui&PjM5%s=x;P6Vvq&+AMo2~5$J6E-$iGYqllr%d zr;V`5N`^RTCF=BdMV#-vN?QIF;|2D4yTneZt{g|!M(|f7WvQJpj+Qiq->82{3Tt4Y z7O>lL6Q4=x1HVjO9_g5)c4 zm>+lRJonQ=7D3u9z-u5Dd%9CgJQ zcMyw8`NHF9O?$+v<10#qeB)?0=8+J6T}fXyjzYWP9$jrNJwmLWw>M(NpPi&Kt4!3Q zFZw%mxFl>d(vv>8U&n%_Oyt1tqfXUrG18uUF;uV@=1tu~sRZ_JCUnO-LRu}&uMCf9rK1s%GyrqNXSZWga7qL{(TU_1a8Y_(I)b{jL_Dcamuim) zr{Ko$W1Y`Px$&3>U9d*(w^3@b7&$83i-Yf+rPERP9r#SeKTpU0)j#c7^T4FtQ7w+_ zQ73Glz4pmH3)UHEe1pg0SSz;$}miPq$6ZlUh5&*koZni6|07NcbE>RgM1!3+)*PpFFLG=7_(U?ja^Axz|PG)6zuwreVEsxQXT^ z8R_^`k#a%>%_r>d%$kBZ&fH1!Y-9{oNBtL%dT9>nG2aDX9UbDQDYhw!-O9>yG%j6a1L#^(or7 zCoJS$4sqk08QKlwEVLElXz%wJ?ZpCM4z|nYE!TQX$Nmd!_0+BH+UJOY;|V_JtE_zq z-@XPmxikKXb}GTQfM=aaPqpoEFCzTl6JBI%e|)gu9mR-0si0syXrUM2vtd;!K^J79 zP9d0!OH>j@d042e0d_vGw(#8EOt)i28t>aoID6elt1VcMeCiwe62~Vy4-}>?v`~rJ@PUs5gkY@G+hV+(ojykhEe*dE59Z^dg{$Yx zkw}@TOqugj=Xo0NS&%~6Shu>rJ)-{8Z_&WaBE@=y!2 zLe!9OO2ZhLl$k3$N;QxU%wO;NE>!g}&_ay6x5r-!XV>Uy8OG+E;#T2EE^lqgQ{BSOns4F zMpY2pHk;{_C-xG8nhANnndxH(>?eNfBJ}ganhs;lhvBO*;LS7zJc|wAB9z*0rtaXDS!ACOi#*k;o$yzK zWx=Dni3T4+%;R=Js9DxXLv6@G+3pK_3dB;q6vUP{-U@s3#8B>D`2WY>g}OzespbyM z3l;L|T&qRVJzVoh-a-Vmf7?Yv(-px}v3YLw1Ez{z&-h!H&9!uLc?y zB$6(SUpj!;$lBZWLOzv5+0?p?vfbVQL5DM!@z-yA8s4Hw(Q+zofL? z!pFO2db0{PhN*1x0y9lVJk)*U86mNgnY<4oKah4!xbnk9(~e^O8}LAI$u`o5^O&1# zS%Pp7e)ux{s<)l4%-Wd0a>(am>~$p)qp8Gk?7t5wuJc+IMbA>P*8WjiR}eN_Z#V4L zTvnHMGlCq!{JnbxUE3lNba^q(0OWPjweb!oLk#lm&Xsieml&vhFzhYONmndKPdmmU z_rAG|uD?l783Pe76)d1@TTxF*opFChoDnW#oV{y|HlM32w1povJHW;!-tvsYGnD^4 zSFp&M})y&ciHJxGsEb%?3ikbu$HF&UFy_2o7D$BzJ;OD>O%_JI72( z@DIt6aY6>_+`?S*`tnM_Dcg)a5sc-|+lB65%rpRgAo@;<5CETZcs0ht)04vUa5FuH zt#-_~DjaNXrXH}*+#3%Bhvz2BI*pv_u~&i@_9boC5HkdQ7WS=1Ui1O>5&GuQZ5N)g-O`98~IP2NKG~d2wN*-K7tMVEiwu2$n6w|4SzeZLby2J zOqnwf$6nYjtisq|f;sC*id87R(M(NNU@hi6SkraUO?6=K9_x*JQ6~%A$m{?l(4EYKT#*WjO z61u{^;iO3vsoRqxIuSP9HUe{fMgd*#_t?*z1PS(_5Vmm?OkR6zYv z)rAWf;|1I?pZ4e~TripGKwGRAT;~XLvrKfPFXl?iB4Kbo5yb{iZnP^Q0 z;)hqC1gCl?staz@tLF*_t{G{;XN=9tcDhzyV(HUw#1&KW>#A*zp|R+*W2M5nixJUu z;S=&3#fs{Zr$^D)r^sn17ts~NUg6nv_zJ&5x)Rt=8IuO)ee&xLN#V3?E7r~lKZS3( z21-Ud9WTEVE_mZC4eYYD`vW2WR-Ehc2mev0gu-R@l&33lo%WlBCa2Lq#B$GG%oPS2 z;8$S7nbjSH%I(3nJp5$E^4gW)_q8DSJgebV_6Oec|BrcaYPh4&dY^?3I$_+!*AvW` z_dhfd=~9FK!cWAmZtz7FN<<3p4`Y9D2-dT!R|rujP4s#)_H+Vv3RKigCu2lfH$fJz zRy0$o)yN5(&I_{~>l78^K)1M1{e&_KZ)$ z$@4}!`~|Vf>uh243?u2VhuiFSj&NmNEG;dL{n6B4!ropnw51r%+nmXxOK5=k?kCQT zbj_nHR3(ZIyu#Y*)DNL+%}6S81N)LUKMH$v5wsSZ`;2=gBm^Sowh4aB?z%8M8T+vC zX?q-x3ghh!c+VL8fc-YXaRTgl5bkw_I3e*q)|3QxlLra~Fn^Y5h`iIVQbH~Gl5p7W z`zm_v)Ml95OCnF=(o!=RbKvv*$p1$)@p^~%(;WQ==WlDu3$~4jF)Aa^X>u2Q;A1ag zTztCZhk4CR;h0+d9U&T!t)l`%k3`GtCtOhm9P=lrdT5gnOZaE#&I<;Ur!3q=J+5t38n$%Vl>6; zgBg6q&nHGtp$z6FaMribdFB0%cjo+q^PVIpAqu&gH@HX5XEqfakndgVg|i%EM+wWS zAotr#$rYP~&!bJ`JsvsMx9fy@T}>1ohIRM$y~5{66Xjq|F}yq?1cU#WWULXQE(k6- zZ}1)CZNbPJf=+9q^n2)U#$BPsEhBY=zqN1wNJyTA^EST`JI#A6gjY0DW7zG>{SSpA z$g_olZK(rygmlDjX6#k@1>X|3?T;o?H9eVYToe2)QSI$Ve3m znj+`|I4`faS-7B6d(2C9x&$g?^L%dsa_0QOV#Y9S#6 z_jVwBLCuh@+8yvK2VJmdO0~6n)GTbHkzT!&>+>vm`i8HqqErW z_`FcBCh#Ro%Q2oBHn7 z?W|jbZ#X9tgxDrVEb|KRDa4f9)X(-l(4opWM4(giyDU zi5k5^>{KU8NIZx0kiXyquSE&k@kY9Wv05`AQm9eXNWGnL*6Y1qNZJ)k&tbzJZ-NAE zZVWy3!a1&wlZ0Q$ZT^H0J=1QqFx`OMCVWV4?_R?3X_1tT^9j|TXaxT`5%d`Mw5Xr6 z@OVWy^*V#N)FVf`?}~xSZibC+*sI-$bCLbf{@j?+T8A(_)g6s|Z}un6d2sFvzpIsT5*nMOQ;vetV|>9q0WA)3r61Tj(P= z*LrqN+vBFJ|=pKc5e1}7Y0O_=rK4K z9^@tbj55)6a9*;ajc{$8iGE#!E#>PZ6h@wH@oVI?Uw8|9aqjLLd~)lgE%KRz+Yfy;f-WE8I?~e=zh=Ud{H`RX{>!|!6^ji1aR)+n^Q@lQY9m0*TkAOe^EiiJXjFE;?3>+LXRS` zbgnIIKKQrxQc5(9YJ<75+b8X!c~SHN=Z>m{J<#41Bk3mYSKj(3v|ple*6TCQY~kWLWEx$_uJ6>#p^1+lSR zlGjCW*12JiV0s_VHQ@Zfnepri&c^agj{)aarI@Y*&fbNX{&)856R=eKVx@(?fb)ax zleC^s%`_97uk@d*y;j>yRl#}R=s4{>aQgwymJ2Jj{ou1>3axQn3`&K~Kx~Y|vg#i>9eKo3tTrg|_YDC~|ez zQ_!4vZSkl`YK=Lx_RUc38N?d}zQK3p@2(9>3a3SgJ$l`&s-1lkXQB6Fy?Oe9W)nES zLo6EEJ66*boWBKPy*a6n#u1$JVQo^}+Q{n&I1fSGrr7F6aK2HM>HgsCUxDcWaDJ(B zt_RM${|nBa7l>N7)fV~+&dxhrwX^Z8%3N@M6kkL8%f(Dh!FkqTH*M5(#JyACn*v&B zyWsr!R@n26WSusAk%{tez}yhlUOQ+h;td(=`~%)v7x-;w^zoyutF{dMj9(Uf-2q>% z(+eXR^XVzyy{_7p(MGa?Pu1eyS~nLX{lr$vWq$UTP%Pl|-$iJYb6F6s2Lf&KBRIe=f=d<9fu-yjEPgTxI;M@&- z>TemCG8mk%shlS%_FS6PZvf7j{{`os&V96l*C0oS>vW2Kq)B{XrYLY;aAJq%qnnvJ zfb&Stm73;=CE9>ET5 zov~;(AjZ1`&TUGXHOX&{_(=G`p_gnU|KZ zGsh{K@i+rx0B1|v(VDhr5Z8fo>j(Wbu3tikDvqK z>@zN(X3myydT>jmJ0omfPr=y*oWBnm>y@XPf#d|levwx_%Y*X=jU7Jr%VOs*5JH+aa2kpaDM$?aGp}fT^qa> z`+d01sL7F<`LFQY1UR>^^2BRTLo@XS=lLaaJy(UAXgD}$3VT!XVa-xJ20Sx^QeS~> z1US3w-jnLP-b4k`lw9|X)ClB-u7LBK6IrQy`kJWPJDh>p{3})dXr!*-y!d@S>s_41 zoes`FHx;n1Zo)YCx|ZKM9(&ebV9!};d8}T}v6KzA?YsX-y>vdBj=+Z;IP)>J-?}Io z0egO8|1@>ayhzMVdWv6tDRl*M8b03;Bkeno+IVX?wL%+y2P{v`f7?KT`!K#Li>YT| z&qe0Lhd1y|J&bwK34Y_#p<1bzk=WRxG^QADLAM77o3-DFR6`K$2f0jUqCbFjhPa_*>p!p)iyShKRBOT zu6zdVj2mnuGJQbqlP;z&072_s-vB-L%O>O~JWk&;8bXW)poy9}`_= zYuiC4Y7EZDs~xkpLq1sp&KrLpxAs|Vq=w*=JL8x&8++DyVb8aY%T`MU&Y64Y$*$o+ zYrpETG!<;?*4}Mhb19lmwbN6Pl+PtV%4SL{?5!;*Dj_SQF+JNgV^uW6NXR7mznqSr4`Z=cE>2Qar%f@4w)@+cQfu zXFc-#u;&`SeY`4V;XExkzw*szUDDJ{)4(}9B*eO1Z=wa@e4y+W>sZ7d{lNLm*wfal zNjOgn&ZaT zMl!%ZXS#p49>?<(nV1LT$9=L^zaL9J%@MPedTG5-GnRfM-Uwa%zq;6iZ@tV-J%n}2oiXr%U3aD~$NlNj5&mu0%v1+(KIVd4+_$!=@!(tzaoyAr z?y2L!xneo2%@b><&H-m#N$eSIteolv&Nu9t|M_?EhQrBon$g(fQ#j9l@!oUyJ2R~W z=i_G!SdSqO9|q2&+$LH3AWq&4&a?j?zTP@2s`dTf7gQ`TuoVpK?wCD;kAaHa*ok5n zwxY-YGcc6c-Kf}sD0|QBjf&Xa-J)W5;_n*tpYQSad7rh;I6(z%+I%jl)rM^F9d#{F{FWO74B)KA-lUne1_eRS1jc~2V{r79M91Q2$ zC5d@|_LRrMc{WU&IdzbO;G8QjG10r`vRy~2{y#V`x^ht81ikJ%oIeZ+l`6bLM}qUC z-0kGUjnJ0i++xHV`3Nyc5}Y&4m6Zk5wprlZd{jqe@Qw)a1n zxt2tT4RGFbajw$CkGcyuFY6twsGnXs7@C zJeLrOdZ$f)IJ>!5)X#YzDdOOK{K^Muu`W_9fb)i=2hvyStd7GuNq=9`Pl*sK;5;hf zf>e8Vg!l^QxdqNj-SA-gAFmCZle(Oi48(?^uh14^PDsbH@n?urlzc~} zfg3EMG4VxKgCaF+fxlNeNE|4)UApyzdUE3KO*xlJ>nnw$H4>Y&376)=*`ITsebhuL z4$jTdLkc`-B`fGvKh|$SQo=S&hQ&J5AY~#cz%FfiAp>YS;7##5`W_=_s%GH zL+WT^cAjNx&QgYQIU+>jZ{p&EZieVMiztld<}#s}p+QTFnBj&-)V+WqJdOSc?!?)P za~N*GITimc`gw-F3!Ec~0Sm;v)YpUaP1hjt@$5N$H|+CV?ywuzw&iL=Aub zvOELDT@2^Y`5(i1_Eo*%5_!PFeR%e7jx=0~CCB8CZ`LKu5P@EFIvj0w%K}3ZdVc#& zO!5vAdrA}=#Iy$YvB6dUp)#xT5ExY&SS(J|U=@VI6c_2C~lJHX(-)x@5) zAW>xPKx!dEMUHnogYG&*4$kuu?mlZ57efy?x7YF}q#D~9hgyedMU&SL5c&Kxq#YE5p1c=qn?Q-)8?iC-?D*-p7&n26@QJ)PKf z!9_!(rxtMyo80@tIYaK*7BLB5uK1x7hUL^_48q6opP?Bh?g@i4P!i7ms zU*FL+hL)SdL{|I_Q+$l!M;H1~up#5M0K@8vCb5y&?mMFZRo{pi!& zdyczr;yL{#uV7JVEB8pQUi!)*^i^^9rH$$-x!mJt4CRcv`O25Oao^%&=vLj9%VKZV z)e&kc0>IioJTf) zpm+7BUam6rD&IO8yjM|Q!S_2Rb&=uMkqB{hH2$80&2Z$CMI1Mi%gKAo@NAbwtl~~7 zRx{nu`>I9EJwnW$_R( za(_|x4X4cEqOXo#wxF|y@%V3(&@L98KVYbx5+>3+k~i^MW!P6cOgQ?`vm6>?(BCx) zZ(^2Xt$Yj}52A%~52`cV42O4zh}5stFS#Gqryma%H960LIVi?-2Z<`fs6jgFplssj_2={U@&7E3xXt;jk4>AEF7JSIFm|Kx=LeabcW_=~ z^*1s(-@tB6ta>4na}VrB?Oi7_IamK5b|Z4=UA@gGO2k*AucXR&gWm*VZR}OEVY>`B z@G*K$CZ_v#+pucp7i9L zb=zsE+A>V|4JHN&w-}s>wfBxd=WpH3P+>OO5r5|N*@A|V14G4TY_-laPVY4oTY=3F zo{=I|jSLpWuJAq|&X>nylS^VZz78v-l%)RfR3vxUa%ZKWHAu{xK;7E+tV%qb-Ft9{ z_WUkSfU`+Lqj!HJ+xxp+u%$C*Jdy8mp34@s*H_$@eL2rz`R&@~W!Z?`=$RYM>gDN7 z&a3|i=kTsK^sDtzqI@mB$Fre^tX||^xqmk=Of|G>i=H1qJW}(UVSQhV$hLwwrF>q` z3EWeGiR2xw7xoPML5$4%(~1@IJVSiZoX_i8tFUJZGYaC}gXqU`@tig&Tnxt_YrNIL zv(IN{5pY%;R()c~7C~OMKly88vf*--FtKwadVYx|24C*Gq)E(nc-OKzfm$&EaO?IslTa9tQ{f_<3CIK9LoJs!Q#yWG_?lRmBGy4Si`;i z#J{AXF&AR!YJ6Ppd`f9JhtI)}t?i`jfb*Dv^jNjbkZ15Ye4C*;oO&wQE@aVF^P%=zZ8Ap%D>rIAb zXrA4;`@d`$Wk~ZU?lTc57H~3Dsbm&qV}rz+Zyofv--e2J_`CCWy2+=T7gH7uvH zhxgTuJJIUqpcvu2-kW$K__Le@=Uv#+)O9a2IS+NCR%-d9OwMjFE$VeElk-&W#2l7O zvKRl)@;^8~jZ4x0a*7gJ@n^>Qtu$C)k~1BO_SZVypk1_x7CdXqZWr*hri6>0*ox!X z%6axDb_hF;Uy)qNvru*Nr|+3FbEdp!aqK{6a(4CZmhkjGLLVjPZS&#Wo*4nu5n*GC z2BjNJ#lu7c*tUq34Zp}=Um_m6FgwKXcCc9lMKKrRWO0LihD@6kob!B5^*5iHMAvxc zy%e}Fe?Lke_fl+2f1}dsN{IM5oLMn`GnM1?ai=!nJ{{LZxd`V3?t(7<4V3+9BH`SwGk1N<@3IQ#bJeM{YV}sW ziQVuhjXzx>O}6Vu*@{rZfADT5=Y`yfo1S0I4SU~&JG^boLFCEa?VnW@2>GfIT-tFf9I7;?$Dy8?yR}(AMT4`_gJi+#KlF{34QA@ID3PKR&%?YO@dh*XE%~RCj~Vaa z=~B^xeIoXM;O1E$8+++6de?=@p7S}Mk$3TJ4pi{8Im6r~NSG%S^L%j2Eav0ix~_Nh z+`rr`()-aTGwZaWMt^KC`eUOV5r*h|X3=C-ka#9s4S}g9k!3%6X0u(=y@m7$pT$=8 zUZ!-$uW)}ABo?-Gunwem_>L=e-ri}-FF4mNXcR9S>{RZ<`N%`+3zvl{gW&veC$l@G zK}rUk7fr|he-Vm(4pp{h zgL7e?xfZs%W1jc&FgWSl<_fny&oen!E*~l&QR}L!S*(@$hj68u>Vk_tQh&yZj zV|1!*?w(urp*a-hOgOrDIvCAjTT?#o$~1#NJ-^R}6R&;UVlXl%?M@_m{%LPR@Ns-I zm>4gZ^t;Gek37lQo8DNdI)a+dbaaoXm&$@6CeaR_X*MUT^Hh^K$LxkD6J9At;M}~G zQ7nz!qa1+qo&rX3Gkb(m3(h5PaQ@%;D)pK7b(HV%+qdS*IygrRVs6sw>WV9z>vFaW zU36DQ!+HKw>PU+hRF1&;;5KY^nLNq?IBS#eGj3#4F2FfKkSA*OTRsNoVEo>cBcC$; z=MkmQrGLE4VQ+~DQ6dOmqTV+{ zj>K?r9RG7{d?C+ugTh1%an{WiB|Rm2)yux%cXfC5%;2m}aKqQ_`OC1nx|!N2@_Io> z4bR@0gzQiKrAx5kZL&#RSx)R$_p|mri*d} z&bjgT{H{1D*Wm2Y68$08K{*U(C;Z-(=5Mk+Z?(G&I!XLnIRyKB2;Jk_#AlhD7r@!R zkKVS8nVkQrM>>4aN#0Ft@~sVi`^rUzZSUxLG+`^!(+xl7;7cSBw_Edg_H_>vt?`i@ zWAb^HaW^xofLg(P**p((Rv&nfoBn;vAm1{v-Hbolw)cl*pbgWV38@EJDO zZ?FH^mAQYHgT$SSQRNPJlJL0QM^Y4w;**U8c9*p~k5Nk)~!=^?-*qkoZTBT%Pz-g#RJYO-Vl5I_EsLlS>42p zpz3v%@^CiyoYof2JT$9f zg>w>q?-QS2a(_4%Do4(7@W)KfWeUUk622dIV3}O#?d+p3ubcKyKkT>dm6UqD$ZL1N z?&mWZ?h`x5M^OKia^0}NJ6t5m{j$t)*F%zHnZ|otU%Y z*G$e4_;~hx^jDV865HWy-$(!SmA|`=NaO7_J`qL?O9jF z(5bXZRA0zUvtEt#J#w4GQ0(x?vE!7@zd}U{{kq>?K3DQ)gbE*QuXWx>C2vWSaGTHh zESbgHw7yB?e_#|pd*rgNYGV=wY6go7rq9ZRjiKUW{a{hRBrAS!-Ut7;%@!!9^M#1J zYnhoj&tEylJ@~W(_UCw~On+m_7yQGYjg&HQ?y?5oV?iaQHJnSqx2dC>a+?39;fIt9 zEvTf!c_8<`E!|mp0q2E1;Wj6SvJ1|~B>FER9Tj{1Mg#7|Z6ANh>$2iYBP}FULtgm6%GABDNc`{fk-#7xc7Ei?B`Q*BZ*CF)tC{zU~04 zq2>s)xctaoQo%vLO?|7Pc){5lfBt|caUz`4o$xug6jr{&`5E`o zuU~nUcW{m!K%H1{E@caxt8pie2+gMSfb-|7*l_mIe`>*w(c+wQ_R(Kz>o2au*}jjy z@Y|8%_J261EtRab>qd%`JR=`%vHE+9EFxn*@qYVehOW#sdx3H<|G@ zIa=xRGF0qG|IH|WTp0o92}5Af>V$GWuZcbtqd2|ytYSvYNAA@q8(M_ zdkm?jm?gCR6XX=Clu|y*#POrZDWEUSZxtkNM0p68rVvA?KlV@!%6~Xd{@`!T z-HiT`PShluZL-Xa+}7BQ15IP~>#HzF6uS{W{)PSyGl-hqM@Q2C(&yb^60^RMcQ12W zpH98^269QUtCs5TVv{8}*Ap4iRPME#_*bKE&6U+cCXxFZ^)S&Rl;T-T^rSNf=ki9S z<=0RV>_flPvrWnXY<1yyqquQllTx@UcH=j3@|>;8F;5eH%E979_zK03**p1QKEe>F zG>3Ef(!s)WXm2G5&QCAG+15na0q2{6XrObdDba9lRhd5FG44tToO@%dv%GLsKErv* z1boi(xidL0AQzn2A)9g!&f)kTEjIs>Z^L=faM0(EP1}MV?n(S_AJ>XJ{cFJXFY9liuD?8Q{bMApQc&7AQ*={%VWzqV3K9yhc`I3*3E9`Seegfw;=nvOjugDYNe3o-qble%a zHk?N`=I-m0Do=*<0(`ubV_GKXA^HDsF17Vv&SSS9vIdTg6m$8${M^E=BfnY1Su=k4 z@9EaWsBjT*fbX%>7;9Vd8kQUAjhp&gkJdJe$Depl-P%|$;7hNqMIBeA+E$lzlbARR z9jRnN>&x9Hk+F>0m5elHU=Z=hIqb>zt%`FO=ALE8ex4kol;*xm;eOibSwTs|KN&d3 zDB|66DNpK|M5pscao6#w+z21Wv0|`r{BTz;4d=W%=HVWGCohJxKena8?60x~&W%;0 zaB=!7Z-aA(sYX%h{s%b_&Tp|B8>YOHC&GEvRq{2*p2&8)QE)EzpXH7m3+J+xc%OzV z@_RUMK0{4Ltuyj-IJ-~5R!1F`N5c7TS8PXsDp!ZIi-EKI_JHgU=Rp;TA#d%;Q4m;=Z)&J$}a68RfkABQ!*m-@6)7F9X zojoD$&YyC{+Mc@2f_unGeYtHNgFf$Z8UEvLT9pN6(ds?g)|%7SPr33bfB7RXcJ9JAFZ zvaZ-JZ-(E-kedlBose`cNJ#T?N&#NgJS)Ck0}YkFPfh-_*`hWM}jZ zV!$F^xg;H&i{uIxYnHA{+6w3O_;PbQbdtxwdB_^RqX>(<70x4wDGmF4fVzBn}Dd;zej$ebswkw`ihjk|L zy8`#qE~~O>Eca<|w76M|%K7RqnnTolfovn`aP z;QX#WT1VPEIV+6c;!`#68!3Naew%w#pZPRq_*EywaWgA<>%1!8a?`znG z(T}t0B()1O>e(Ebo%Q?$@rzv3)|(mczssXzEcCF||483iUus>_^V#0-Mr)c!-2M5D zHQzYq=Nv&#&YEOhiVbh@iD!2A9P6VK+=;}72N&hFE{mdfxEubnp|RpV(j)?+jN+tu zt5mr^eO36zb?#o3+QGSano*dp{?aXhb5{8e^U-jAZ!(JZJ|$%t&Wk$IBQc=6Z1S#z)a3i+wJnY_i=SwryIVc7x_2^*T<@t7trKUx``IL% z(UII7J6IF|V?wgN~6a(jZ*kt>? zGYigXGw?C?oJdNAb8a+~fpsd#%kAemr%`lk-bNk^=Q7x?5i9!0>2RJ9gl~Uopd870 zUdDNLIM6?n^ZWET&}N|rwxuU^FDKk>SK=c?Q-3tp4UKJk(K=k$PcU4&V$+gVP}2;&i5K~{*S*++RC3T zoy92jv@Ih~f^+(zATcMlzWf5tt7lV>>te{{{E>LpOV(#{UIOQ3^&82*;JiEx4fINF z*&EI~hokM-s>+4n+#8L;!MTF$2j>foscT9qoyqy0`yb9(_6Lab|A|v_e zy}fp^ylo*hNI%B$_p3CwrJ~(=tU#w)*T<%#cBsF`ce#Irt=gq9QG)j~G<~FPIPiSrkVX%~|o6^-Vvs zm|KK+xWWNz3_7;TIc)H3u(i`^ldw%gbKY@C8M~H#AN0=|jkC!a`{+ZFjY5BRv*d6l zM0~T*b8;%Hv434i18(c+n$uV6PeZYq+=`8Av$1Q(E7!+HG(>b_!}<$`cd?ux#VoWHs%RIvr#i4faH!*hKbTdox9wU+Q4eHd!{ zu`gWc6m*gYQ*2A}go~DUcxD1;*rw+T7we1g9O@?9e3|E2zXLV2tNd&`HiU^90-;$FHGnXsgsc!+T6KAtEUk!j3{DDUqoMzEAepqr`D>%EZQ~Ye3o0q97r@Me{>1Q z8rJ+NXd~E%Q-;}!!|_nj?XSP*beWwg#Mc#PHI(+lxn}1-oL|EEdznA4&%ycBOP(XI z4N1XpZoZHD7^llgPvIOm3D#~elI(V)S`GaCU2l@?_u#;L=OtPP68=Mz(dz{n? z&ZS49U+lV_voF{)OY1^NTSJV&O&^}yLbR|Fa zHpphC_I|lHby+8-+xo$L3D0I~RzKUDRbk@r9(>nR?QKhSVdBa|bexEqwgVT{rv2CzBS2ipIzbHH!3cZa}7ARsk+`x&TN z>bN+Q^8vID`|mRUm?d8P$8IE^&Skqt&GS3#`H0j4ww{mab;TB4T3_9!p0kKEXm{Zg zyle}OhKuNZ=-o}a+d5K{98Ju!v_@~+gIeLj;!GYk-N!bX-c^t0)UF-vVDmj4CZQ4a`#QM9vy9Fey354aNcU&>|quU zhcojfsh73zb9OD^8+)WYPzK%)6}dJUsn^V<3}ZH0GdR137LX3WITp?Z?p)Osh4Y-h zoIk+1*I&+y;QXHVVCV20&Yj?Fe{GKk-QaA0odeD_;cS1M75(`e_n`fCC(d(0INM*B zgmWV}+h31|a|bxvUw7d=ulmc`(;+~l{D*V8?^~-sHTaN>!k+~q9mNJ?$2ZE z&5V~#aL#tv-DYIAWIH&|zE{PjdW4JGa85PXu(hB**%i(MmsGPo&CcGdX3Q1RmA4h8 zmgedde2jBNZTUmkK@(3+ms<|o9 zBMpIboflQDSJTjx;JmH$E@eMCg5z=IMS@f0N^q_K=bK(-r6f4VzaxMVpAAdRbhjWp?oU6iF_m}f* zINN8u+c^(}^P0b$-#Puu`Gj=NT9lrY=WyQN_K=cxf(mxsL7;H);Y1HK=e`?mODeSIWMG=X#flFzKo z=7xzPaGpBqthHE~Fi{82(Gk0>ySWou$aZ-=wTU(OzIuJ@O7DL7~R(BbXoFm}evJtk?3m1ct7Dt2^8&)etSv$G zuq?D*!S|R?two+zv#if#vsjSbD7=5Qwq6{HUk&HV&+}XRp#hGD^MS>Cl>tvoVhWt= z$5&PQ-L=nTHj1b6hop^ge#u=KaIo;rz|!naSA%XLsKUQYSb^z2<2k1J3>6{58+A?Ktb=oe=W?9{=d5sUdOB7Y24{CT zzbs?Xb%%3PIN!<}tlJD{A2{D}nxPAa^QOO?^A`J;^Y|%~t^U+8K7g|nUc&nEdW3iY z=k|}Ct!pW!^Jnv3=G-f%Gj&S^XITD{4GKfB2H;^}M+;cQQU^Qz%FtbXhQTQ8U~ zl$xP*Iv6I_;yd|$PE(?$go(azzL5Krvb0E;7zJlbnRq3ddeI4RwkSqr)F89?QJ3>M zsGjn%IC==2Hx4}{zkO&D2UekPe!HLK0Oz9{u@!?7bj9Jk3(l5$Ei&y!Fr1I|tts_~ zb2OZtY(*pq&LiP0y~r-zf^*hwJX7B9b+zGa_cvY#-_>1(b2T`ZK7Ll$9?k|hPdjo* zXP+sX9nP^4iMl0lw)=HuT5Q+(!nrw|TOVGp+Yjena31)6nQjT3xBTUN*!^G5AD*|e z4x-oi9-POT4l8-C(JKMx!g(T;c$-D!fU{q&k;)OYXdRprns-*#<)t?V&TdyFC6%*X z6wVW#RaSPt3lk0D>@ly1VoeC+p2C02>Zs`bx%)OzLw@;+yrv+vj_AO}TsO!W%q@AA zl{>3>Pr2;?v=KPJKa!f%t{8JH;cP#*wg)EB2hO|KU)6QJO+6BvOW&*}oxjiB2j|}f zy(JmDF$d1J-VLSEaE^uZ%Nyk-Z#a*Ev;Jfe=>?pf;GB?=TWSJlUpS9>nMHaG=UQ-{ z+wHBc51a*@bDz4e^MP|tIG;LkQ5Oei4>7KXcU*x0bdJN@vD0oOfy+lxA1xrGm4|(SC9r#UftE&|6eM(b=|h55hTi zeUwy%IK^HExFGt3)Tm&%NXdo1ktJQK@H$LfY{EXlBj2S?d&0yXf9i}6{E~8y4->uM zZ2taPI>bJL{&049{!9u&YZ?aUQFX3J4Tuf1z*$>X!qVh`b;anHad9-ZOa5y)I^ZFiNbiQ!T4d*H5r@DP`w$DX8=WtCI z2l7m|z7!#jz&UpM zap}Q9i?|NwkuzKApTviYr)d6-twH*SIoO*C=RuVg=&utGPKWccN6YocUWB3PQg_m1 zg+BMrFwqju*H%69>xWUuyPUl43{wR;3|FeNi+0CcCrW7k-XIdAAQX}W)WTo zfA>Nyecr-m(GbqgKb`ag$;)hl^QwWV(tGMavcb9g@+hexoIAjI?CS2)emGBobKLM& zQaGHK!1+>Ny)+EY{%{ToswFuQ1G&I?{r+-NA2^SMbMgAcq%5uJw}Nv-Wfy4*oW0;Y zVXvb!5zh9R3^k9w(;bF$B{&~neNPt!=eBSjaQ3Y3KAZ=_`F<@;XRou{31|C0`g=A1 z<($0hl(P7Fq__oV`1pH{o{r>NV1kLF+6 z@sNJAGj}4K8@xZJUr1d|X*h@XPt~Vyh37=<#!j0)5Bkz%I4f0l>AU9%6Jy}~({ZI< z-DVb(;GFU~L|?a;Srmoy&E-D&8SJY%>x;duQdVD=9;=RU&LcgM1e}i$8%E?`DLuIt zBC5i#_J2so0r4{ER#C|v@yfNq#6D(pMoI~D3=xrO( zG2r}u$QJ!0{N%%M&X?t&eq%0}=OhL>Wz$DK4io1+v3t)H{fEtAA_UGI*6(8oSD0{t zb0N1)`Xf2RM8*~L-w_M-)8fq{E1VwwOBUA*zd3?k=DHhI7aIQFFoU{YZBjJ2;ct2?~oL9s7QG5r<2mYEchy&CZtW5{cdIu-f8Hrv9E9_+@e}mB9)yW-I1g$( zUhhG@bS*fq-QuT@Lc80!ks8isz4Yy!sQ-uadGD6`W}C^mI2gsfRWJV(=QYC$ z>-&>i%wgyJ@so6u8q~FLj?b4WUAP`91e~i%t0f(r?@vVE8*Y>~!`TwmBIA0l& z_UAmy7n0?IXVL56+(zjnm)~O%OW_<{-d%3Jmi|gO&vbT{H~tJ06XBem^;J?;Y)B}a zi{-zV^z&|*@P@O;gPloJ)? z+u__9&IP|c)5XKND4b)DpV0k)b8R>e&AvmI3g>Qc?mcY2t{9w0aGoFanWXy;XS3fZav(J)5rTyw~(Fe|tik?>5 zeh(9!;q3bUva%Z+(hJVr_FPpa-3}A^;C#QydFADrFtGy8a|a(%%;a3M!TH{et;#xP zyx+Y{{^)F!l5GREk?8Opd-YJZc4p=SoZD}7QrfvtuMX#o(p}{#H%#LFa_T=Fr|3Fd z3l#})K3r#xG!4#CaL&_bi?kHZ-QZlbVzhJ#&YR#IpdTykgtHOO=Zbqv9pUT-=S{<_ zNbBG{1J0K9`J`5G_JZ>%-3Ofv=dN)6eEPI55zfWoTxj`Loqe`kJvcuuAECPp=N@p5 zE8I_43C^S8Y(FzO3-PSO*}jiHq}{)J(p?)owUwr?Dy|s!Z24Wb0n8Bas>_Um_WNw> z#<7Q#pZO#7jO|uJgc!>2UitIAEe|^%m37Roxa6$f8WSmY!#r(tIknp;;>zFL8Mh^M z1-Y;h=;wW_byd4;jueH4VynxHR?n|yUNJpZC5r^9N0@D1@i4V*lOxr0W$533M{ksM ziTZ~5(xXbC0X12tPG=@wWLx68$MI_ILJ{KHcw(02yVZ`I0r&aLh)GUR^SCfy;-FDf zubHUsl-TWif%)$(64V);$fI4Q#;M?Lwc&WPs6)}OJY+Yw`+815S=-t-@8&E7Cj z)U6&ULL;N(@8N-BdO)BUQg*d&biM%C|Kb1VzwkNr+7>$BBGSl-hb=g6+rPph+SevG zcH)*T`%q?>bf%}^mZRE@*+&z}8(t`0N}bJoDyNnBncH>hg`tr`e+u8jueX{$B2wh} z&iCcTUaAY!>`~Y8Au>w6X^j-ahfvS&O?PSvoaa*)`(l@BBmdj%WRTdkBUv3bobLh~ za_#B`wfGQr)DnY-xIR#=^zwHZ$Mdgyt7c~|&_~X*bnutDhuJB6Vfdv-Hm%5Gi^!fY zSlqgvQ=4DkBGTE7l;q&7oo82j-GRYk{Rd|)Jv>ZQrq(p*Y97ry!z8l#)1O>Fr}lsu zR!a&5i_I+@v{MbKYg%g*=Vm=u-#^1YA=mS3s;0h=4-&^Y|5gvPx@ajjgy(wvoO0?I z>ghh}u-oc-+bec4Rm$^+?fq}|+UNh5b6&SkwgU4knX??*uDx$_US<)+YY_7o9MpEc z>@sdg?q<2W>amIb&9UTe4mDMmHe~#<>fp#c-md8X>yE11*}v%=ijJ3ZQ$0|hT~0Rc{?2bz*B_Ch^=GtezpPqy zwpE3cd(N})s6w!)G`XIZ zi+Mp7dMBJ7>$REGuqn%fMgOKPvWPT&>3V(;AAof5qDPe%L0ngVjEp)cER)?JM=qY1o}lPYSBn2BZ16q9?sYBh^0F z>)tTp`OAaVBFsOjg6)b-3spPMX5Z*uay^Nw)LBc|dxCw7Ymun7sS+h7pi9?CJ*(bl z?@T=UQbW^ob#OqG@E{(K@paIqGpBbk+Rmard9|v{OxXAcU&Yx?o4YqkbaTU(3#zOQ zWY^@(zRVntYoNWH6eXrDqrPi&bFIPfC^7v8`INjJwJn91C+Qw6{0sEaCM}B;J?yzF z*Wp?xcXl_d3lT(~b2zdNaZ*pS4E)Yb%6P$LJj1HCDFzE3ALfjM<0sma;i zeXd%}5Gk_GAwHV0R85#R#W}kstygPgPH0nJ@jn&3oV@^xfVBy*&K-i_xm*j}*1f28)y%%e2wd#75E!H?PT7ZKab%^frVD+nR$~#LF=8svUhVbyKv> zYs_M>XNYJx_Ov#-7Ci;@z*wK2*FKzLZd*z)I|Wj;Ax(qDv}WuLni{V4r!S|G;~!0C z@cDl^-`VA;&SL(geXm@R5(U)8(H0R}4nA8NsCAid=}(?9p~o=w&e90c$A@0RPSI*Z z<|d_ZH>@lhuQ~-r3LClFCtk zPn*6hS~zgFt3RKnHQNy_nsT-+--ER+6QaeqoA}2|BDLJE(PAe(7fXjP*8KNJi6rcE zj*08E3ETz7jo6lwJGFsJBE;DAU^dhq(Ka#fw0bq_r}x~{yfVT??ja$GEmiXMr5K%ELdva_&bd~4KM<|k{72=+;a!1?Gb4bd+ zoZEzDQ{M(##Fc!^ohVpBZ60pP+zofk(Mz4(GeQ)?-(T`j@fo~6_Phs;~9)~pvP zX5iQ zB`#E@SD{;NtzUEY81`dU%*SS$PmgF3A4N~`weH$@_P)(XrkB~nPYaWyMcfx+;|tTZ z^6Wx=fbX|AF;rW~e!aj^#M+ahwZ)I3#Vzc1;PWL~tC(n^e@d?W#|k-|vy^^EO17T84-Xhwo~O$+2&Y|HJvB{4eJe zYj*WvAbt^d=eo^h)vhpicPHMd(^++9E@%S2N8Uvt>LX^JT*6ilI=@lfRE7I!AbyDD zh&ps+q*xiiJ$Ua>8pD)=F_Mr6}cJFxFYLz~)BSSvI=O8kC;Z?mkj7Vi+vs0eE9 z`bk=Rr)cq6Pn;mP*LwIzi(RAOJZg}3jGcOesGZ(C#$OA57A>w|Pd<;rQ|us&7$&g;-ch6$v4>{vU>piqTfGgq6fOmwm+QLB>cd&3{jk8_#cP?!e%9ga1nzZdDwOvEm$AsO^owb(M?NgL6zQZRk&`c}V zI$HcLPM+&PH?8vIXp!v2ow#eb=JhFBJRVOD{UL*sIK$ajQL}f0z-28dk z5cYDe_z@(2A6}-F^@$NX@lWJ~8?=PHG2;CoYTMh%pPC z*5+(r|4Z&*ku&X?cCl=v7~DNrw94m{{63tsy){@I-d!@eDmmz#)P-C>TR&O56D}4{ z4iQ&-dnb>=C!f0^MC`B7GPx*uhfl3SM6KN!TAdl>+wte^c0-N-mvcPl`NS9pGfHXgxD&VUqffs}UCrw%d)%-mmpr|+gO#I& z^o9EQy*^rlKJ0mPL+2SiO7mk6uhxPbdg}mf)N}R%k71_7F|+32%?{s%=ziT|wbrv@ zM1BoTTUxHw=)un47u0lJ+o0XgA0u*7BO3o=rY)PqF5gcAeU&R(!v9&ia-AaxU2}oBD0MMGVVDO~||&YBgr& zPcBI8BZjKuYO}w9Sn}tMSaooZ2r;J?IfknV>ge|oq5^)%8tIC9x^$#C-VM#_!B@2c z%ri#VHPyV@B68@)x%6(#FRk^(@0z=udWdm#wPDQ4E0%~(d#Z(I`pmwk%hcPf>Y=q@ zxAnM>)LpL_p*il3X2%@0-&@VrVo!4BZ)DG z?7XifmtdZ1IQG1iWAajBgqhdbOBzu;`Fb&CEbD`XC9z?0mXX|9d${k0bW9FrZo}j6 z!J_v2Hp#QwnMA^VdcM}Y*V;zWbHjaY=e#lIU(Wfg4r&}f=Tr{jj!HGu7-nwh^RruL z&`@=D*$7d$BwFG1Shc)h?@T4?6^se0CppAd4e(FAFRM>}ad%_4R~G%EzAME2g68t| zqO+DQg?Mlb`s_q^ZOHvd@iqv%6i`d+H;z4z%cz}tO6l9dDA5Do`^whN+SX@LA{(~F zYwi%uXDqhjIdg#f`D@u`MvHk)ob7}lt-^z7;ZcD;hg=rzRf!m}3p+D$bF5b8N3>{3 zT<>ybnO0Yc7X5;mO;K@!_IMIIXE)Fn^K^$6(3jnasnnN0m9=-hBSrj6at)oXXoElS zxyl>aQ~5=!AHy??ouBi=HMu#QKc1w9!LfF7vpLK}H4x)S-pQNE*?q)UOfoi4&KO89 zSSL6qKhPGM>BFO5B87eQFM|K&JnZQYTVr2##5mD+QM;--yF2ryT+m<-4N?;dkdrOU zoP^#n>d{IOqB>{WJMSLVWMytPcDwz?b82_;<32j-fF0kd4Vn2<%9}dX_c^u03(+WG zzIshD?aEgA$4Aj?y0D6Nlo+tpZ2Xq1jkQR2@M*E=*xkLg+?%7s*v;gam0p@J_C%<} zRvyDOpL*P7SJ81|r)o2}*HT`S!#ifw4qb{Cp6FLi=7npo5~GEi2fEzZd78_tXtBk> z%%#%HvldVH-Iz~n zazcZ;mMhsO4|Br%5fkmHkX**d%<7}$W_o%i`;`tCmC!+I)@_(vVi+~>_sP4>IjvQm zMGlRhX+O_Z*^~a?xI0h$J6q2F7O^ZFdDZFV)yOs$aSj`Dq;zk!J~LNWa<&Ib7WH0! zbi88du|8YX-RmOg9p=2%J*K`sfX-8$*$DHVssnh>K8>-@>m0Sa=oKAXqK{W9pv7^& z55Ts#o-3n`_{}WMA)M_)HMP6VqL>{)jC0+f9Uc=UE(J5IaDO{Zdda?fY`b|~FRkvE zC=t1ZTAguz+SmTQW~>p@vDT))If?D|!D(QQG8e z{F!`c;I|iPt*E*BU6He0Z>{F>HA3`m&YZVliCVu^#EEDZ4I(aUCGf8%VSla!|I%I& zv)kSiH?1p{d;s5|R2Oy#O{$q(xGZy`R^Z$0sF1v=FY|@)nfpcW(^?Hj^X~MA^S*Ka za$aP9Y#YE{9*3;xpT|q8UIO2Pv+XW*P=9=2ULyD7>K;b*-Vci~5X;E**Q#^5mm9(S z!$n!G%)Hh-ob#>m*VLB8=3gtLM8EE%lRMIw3`;+AXK--2*h+aW0@u-oS6a3fX+bo+oSqra-&n%ONj-9Jrcp4@8o}wr1p;bIHlckz`?*xRNR51y^B>Or+28%&^X%n)**3VVMT~M_ zK2mx<^;-jr7yP4%;JV(y)>W=5kO5&Wq3!JZ(VP5WH{OuL}wyJ*k`6~D3 zp^7Q$Wizv&@d;x~-ceoH31WqRiSZxRo9rC?iEZy8J87%-Mv5l*+BwVuPG#j%m( zLmPM2e7i-9#*^WHvbVO8xN=A!^+UUcYG?1VA1#79mgW<+jc3^f!@baJ%v|m0HFm7? zy%YG+9h7QArY5-qHbf$3C*890-tL!pV0TcR%NC|jGTe4J119icQ@*? z_H!>>`l*@p%nVshp0`AR7K^@E=iDD1sd&GCIlnKr#rB~G`_UZ9LH50Fdx{TZ!-mAY z&ZB#iGKV{|JW>AjptFK zE5B>=zMPu>cbIcWCk7VKj&moT&yL?xpoCW8NVKTU`+Rk`tmc{!E#~n4`*y9Wxv;}} zbQbKAM}4iIfqm;g0!5w>p{+#As`x2T^cdPf8_Rnu@+wfwygE|*_98;;V%PlYr)I4$ z@wn}Hptx{-3IdCx~f=G(q7zun5`4H(?xVmw>JxTJeByq*Sjj{6M28&38Lm`9uO7v2?{eesF6yR+(VM@sH2#Bile_#<`dTDoaY(4RE_X~XBGNSXKqwS-HsADs&PKw?o^Au zLhHl+PiiHrpGIJNYv51zII4Es6D^A1XUfCRsSDOei!3}N2YTLAy=P!gcx@=0rjBEu z^(J1|e)37}UOh^*<8}RCS+)82pA-1IK68s`6R1Oe#?LQWwwAUQZNZiI@N;!1&B(of zgZCWiGEFn1t!>3l%H=FtFX~n5aW?vPoUJ9$PuhU*@n!$7Y700o{QQS=CH8m!*KQQM zQ^8h~exH$?nVYl9*{WloHP{vjFxc+Gc@aE^g$%Lv`(hEzVR&G-*%k)pi@b;W1yPfy?=+(VD zx#RlsJ|BCj8`nmQ?E}c;_;penPD9hf&cEE=SJl^#7SD#Ft9%=&Ua!TT@!>pc<7cU> zIRiOI@g4$W)VI&*OC7`Pl02K$rwcj5+zZ1>rK*#wL{PWDxjK`sK4&jR_K7_2@3LyU z3WSSNe%Sm*AJi^7ld!ah`KhsLNKK>g<6PO#^WYx;avqg;x3wAm#$C?v%Hp-G?Yx+! zhP_SQcS!mD+9Il=b#y&HP+9iVB4$@4AAIA9d@Ph1+MI_f8`UJg75GKi*ufROr2mhv zw}6UkYqqwDKp;v&+}+&+y;l);;t7!xcXtE2fd+y!4aD8uU7&k+JVc3yxVyV2|HC2w zxRUp~cZ@Uc9=se5%wD@z)vQ^wF0G1^ImFnK6YhJuwy;R67Q~!t6}>9JT?7fKT69kzSwKk?PqG|D`F*y~h3%Ev1H`?I|4ZwGY1ZC4b|Xuq3Zbwxu7jhMK5jN5j=m@Ecpub+4FWSiXcF^giCHF|!QisL|#e=9~=N zVK~Nljy=U)FDDF(vqXsN5j31h35II5!X(FH&S!Wn!w+)s)eX7h*hinY_1`#uKRz;H zckf8)l$pPATZkbYUbrcHSYo&QFw}TQ{lYsw@?=e|3w7vO-n&Jf!?d@N^s@}a&x}^> zIyuJDW}K6AYqa58qa=_!t9+UR+PjVxsoR_P?pd<-9Ij~#7=E1hP8&#m)pQcQ#hd9> zMXzWXA51)%@2;|t*F{CchvX=vyr>gr&*kUmDyuqVv`WPl=vRN$P@4kb?l#fu*)6K! zNvkC8;qQuSp;|h~Lk{qBI(AmBbF6ay1iWsB{^~#ukG}1!f+*x$(C} z$SZ24DLH?rO(jzNEAXD0KSG7O2g~jO)VA!Sf2{t-*|~LRL+Ad?M-VGK3YXD-w271l zoZ%+>&DxERkutR+?{&c)nw~LAyu9$eD)T^l8Oy!{&T2wqSCw`%XWJL=509eCm-@xd zyV)eLj+))aA~PqV8N1zD9hqX0TA}0&SqG{uPULbkIX`dx)X!GYGJQEUQSk_MBY^$C z+u-`#W~t6#3rjDR7A;Xr$R7evlmFz|poY!1%J}Q#W}|kisrRgs?I}I}ulv=gD^|(z z30>=$Q>wxit6WUWOz6R@svNoBJo4wPRiCN})S-*uPr@&JS2p;r5pc8VGP<*ejrfL! zs$gsxDH$Ink2{lNlx#xH5G6;@pRBJiR8ECPN`VOcI{cVbM%#8I1RiF{P&8|JjPUQ? ze|kv`BG>jabC0l({y^2gaZZa@_Ul3H)F3zB+B-;lLGJO5*gNFN0c`@gdM#pXoi%Ax zPJ9gX#(1aaEvf3yCbuU})}PQ&x$lh$!T^=Y2olxMaGF7z>AVP7&s^?sMOPVem1KaXT>6z91* z`-FNQlA9AlrPv;31Ck3$3C{TKrQDnBqrd#p_*cH+zj&}Vb0oE1ZaBJxB<%ySx@INv z)pog5@!!M~V*8Lzjn!8C_S1Fe{#r3v9j8`JKaLrSCo`0dGrT#H`fko96>=*|8WT&~ zRz0RXg6PlM;5bfXfeDy z>Xej!>cU-s=Z&dVWjnn9myXOhoX{izj;QZ&G>C33Wlj1RIRRgkZ%P+2fpZt?g|%4+ z$kxxAHkIC;Een`f1)$EYb{%wmd*X0JW7nITqLv(UnhTKajvmP^N~VMcZusDP9HH^Fog-Pu=w18JVr_H5tu;$0cc+CRA3ZXfm4? z`d1&6yomdeee@MJ{f)EZWwaJHCQ|ls_wAYTM!P#8Qhcj(&)zDp9yy^UXo)r{a~~B} z0=+J0c<{<-m40cIBnQIzM66Zw(1~qYK(6xih|2vKZ&L22#Af%^w=j!1f$iC|zg3z| z772YreljteR3^vxnFU|qV}<0ADO%EUzf=sYD9zb+Xcom?&pvuvx4&_I zIBk_yYkZ{CDFB}q?xOm`Kbz{{3)Ejz&(pv^ci>DOo2)vJx6c?&4lsG4%FXX{+)57M z8Lx(%NB_SOUz7{i)ZUj-BIG|GLcXh(krwv5QkQMYBHy-Ar%?M(&rn3Zq2Jh4iFY|; z6}e_aKi~sDZx$KCZoa-F>5r6dCw(edrGl0CnY)i%qR01hBYK1WV`cUtc>7cIt>Vi+N`ol&eHJq1r~{6GI@0)bdqlCB=>}92}1)A&}ffL zcG^TmandFSJ*5;3_P9A@4gFVqnD z|6F(B9NuM+wcxxxJ=|Brd@>$RV`f=6u`(4Uv1GJ_ayNx7)a2CoXt_HY@4M-3q=psl z2#vwlGkxW7Zu;Py)ofMA%Ncl~C0Frq2*bYwUat2KW)h#F)7eih!TauRpT$l-tI$=D zn|+r^X%lgQzDBV{>*WeD=UOmxb2IkH*^bdNk#jim$5HXdqqYIV6Up?9Oimc}f( zcBk>4;f=?59ejaGWcOY~T^!QYEGKL^y;J-F#^DLsEXPmT%8dEuK>R(pt2O>R?Kvko zF3wdxYRlc4T%WplO;`1j^E{>>wO&iB zDqfMj#$nXR=eDaGXgR*GC!T-2sK$WvO769W)^DoS$0#XIel@s;yZBAB$bdr3O>ZnN zOEz2NOCvO3=WEO6+|hE780$8LOA6~#`h?mOPqJGK^nL~d~V?~aRaJ?3K2NmgyzNZt-4*C1X@?NUUYPe)x?0xp+*^!vB@ z{dJztC*9Fr!AI>Pmn%7~lsYpZQZhDW{w2Jh%8`OI^~ILqGnH>8c#Jq|u@UiVB{iT% z{?IzlEtL z!uAr}f}9!d>)O77(zZ*qWaOSYr1{IlOYqWe%=;xxldzl7a-aKsVw>qwp}1B2;NH*e zTPW#zfNdzAV53*duO?O*xRIWe>o)nB6&~pVK2)9TV!RqHZ@&ji^0<={{f3+eZJ2M( z>$3PEy+0qbY%TUtKGcemGXZ9)chK28q$2xYX0o%;+r|4yALdDWpf~t+M^3K}#S4ml z3GZ}P;`eDIS~cr4Re@TnNCZ0Dw$-%{YfLgQE&bE@2A=Nwru>C-&6}^Z+a|bH>b}Rx z<&opd?;W{Wr)EyF zg+91nRx|($bI258#Xog|q-VvFlJ742$a)bgUe}hBEu*Dw82-SCO{E37T*3x&V5_fq z?unK$#N8E1L!>7$IpYUrqJ~VCY|X4PDHreiDzhZVS>;wu&T0?(Kjg_{;NG>pG2%ts z9>868b^lD+?PHZqCN$Lp=gGj=a7QuBxp*v;&D_P^XTTMEEt2W2qr_}Qli;&j^3I5q z34BkbVVmU!nhl}<^s2=wxy*b=k^$Z6w~;a#P5USRAUUx-vmD?vuehHY>TGva)t??+ z?Nm;q1bm+76O;6t?&Yc9ohU_rq~y>k|CjQ=!_|Miu0;k#%2IHC^2S}&M!zu^oVO;4 zDxWP%T)^2ic!E0C5REQ4|Ew@q9j5L}=__67yF)EWYmu|y?3?wR8bmI-3Y=pHzfy^3 zEwTfg$BMJuVV_04-rUP|aS8Eb_s}%@bZd)=mK-f@Hld|YS49r}fKwumEw#%ab)v1( z^D}<79hyn~mE<01bf*>UD93+UWvUmwPQ9nBGQ>!$Zp=zF8zh-ZP*)H)>+?k`aEwEfw<= zT$lW>IrG=)6c#Bn!TCq0f@*6{@?>!K{im_|*)sJ!m$^1pttDq} z4$e7@R`ouIMLvP^e)B3du_^QT;OyVjrrt7>SgkIPnjPgMYYz;4uE zzUh9coLQ(ZwxFx;;3ji8!*ed76S(bx4~|u`qs?eQ{$H53Ey8WCU zm8H$b7-`&(Iqr=$C3s;BULWY3cY8|LelcOqo(5g~)!l9uX#<}y zI!BDU#~Hp1&Up+=R0ra4zP`*>jM%E4CXrXb75H6qsMTQGWgGiwN++peQ>?O-Iav?K zEmg2UjO6%D-Iwu&%H1?ZGSeq2KlH20rpL&z=JrV^dkZ+3^~{0# zWs@SEBjpM8$gY{`#E6&0E&f{*uI*PFOrf%BHgO|Mf3=REaSVQ7_tHddF@Luc^+oN6 z`wctX;l?ujzj3}3zgfErPcj>v$GWW2GFc)e8k`?mb!{LuU`pHwtMyPD)s3DkIKLg_ zrb6kj8Ns>lk;2Meh5c^)_Z9}$R;$qVp9JTNnS9i&!t8*nM?O5OzdDA7wsAkS<+J?N z-VgM(xd$s|i%<`T(D$eQtzny?g0{e2Tu0A8cd^>(8zWuNi!Q#kMs10X5qo)NhRbhL zUhc87uq|`CpH{b zU+B>cIRMVV4wcl>A|ZTF#|;P7uHI47moq;h*CBPG7kwpgPTOj$`sm00mxlN?Z46X7 z{X=CwdC|Ppc~x(IMk42U){SYJ&s#WhaQ-|Z*l-WtqT&C6^T@(0wSwqHXMl63M*TTmRW7dWqIvrOB@@3RG*uTHROVUMU2!MWC;%UV4H8ZdA+zk9Ep@JB=C z4wqC9LU6P!fPdJLzqERv%PLKx*%KXKLtW)w^WBZ^%e%fZ5m(2N zZ_NDdqcX+D$khzgT>HBzx6?7Qm|n$6<3P2pYpgu#!r4wgMjc3qm6Aq$2_{cb3l79e z$O?R>Mw`_8fLM7+E|>m^NyTvfGms}AcrsN*kcV_S#y*7^W7TQ6#JusG{{!Py;;<;0 zznNLlTI1AkaDFg{dB${IRq>(WV$ztKn_Ec5j}Dc4{LafB#A^ll*}I4vM}CynGQ+11 z2j_12iy6x229N&(=i(j*0B6@u9kl~!`y;^lWKtt-7_ln{I2#AG)p`u11_Wp4 zghAT!SM-I!*(JuL1rd`yAETEZYSnt-pP4N?x!iz7+K8snG8c~G&ZiC9xcS5la?P_R z6SQ45tmyrj|8+T`E#GC8%H+&l49Qx<%rR2yKD_0=M_T6tF)}N2%H8)~8|WM>>uWQ2 zJmI%ia(=AT=|!Ga(^+M4ij!5;blalbR1w!WX~>xzTRO8^xGz?Y9p|hr$)utl$4EPJ zyveUKsPbj3%!uGi6P#A{9|8Yt!$0tBT4iMh=N34ha7m*+`$o#WS?sOfdS46d6wW

J&%)>eTKXA64oUJt{CP#yF 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

|!=Mk>$sFh~p{nBiGOnKWP*f?uU+A=yRtv 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�n#uAC5`!3#2} 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

1!cG~@*-*h=qf7D%0 zy_!Xno1I3dgf@b;ZB5)(0FO{6t9d58i)HpT&rj-rdxajX`pjnzK@a#37WCTe01Ael zQT5k7Wd6vnH8H<}{>A1?O%DGzKPRnq5YFdnNr5=`alXL!G1XE!;{52_Uj7yC!HovgkBhC}%XY#^UHT6TBTh7kmeV}(&fH)`CJ&)K?y{#h_@>`61A7m> zRQ;BVBxSSUd4ao{R6@!iaXfw+tMeu1aeRV(o0BIq9a zxLL(5gbtX^FFXQI;jH$;CqEhP0eGB$=_Evb2_;V>@Tq$@7DiZL{xeBRb-&%+hzF*Drn6KFRPppZv056q>(Cn-J9*J!LeSK2%teD_WY4D1u{`#v+0aPlf z$>IOTxqh~VFttESA&B#n(lFklftHpb&SQ4l^Yuhq~Y6JUPI$XsCqta~ zMS){}z(n`l0zP_I5c8|8b1N`wN#lT&watM@i_biS4V zu8NO@W~^G^Y}*_h3P)&~^!IQs0N36&N=i!#%$)Cy2CsIKj2`@WHF!_!2y(~%rUNr4 zcLDYbPh7zP>r|9mbQYR(p74fQ@;q0y6`vja;H%@a4Y}ySqMGLd$7HVl8;`~@; z$q%frrLVXLjo5a&o`b=S#XV?06#FvVrPhJC4|drw7oyd);0}7dp&rb=!_eg(3@!ax zGNuTcKik1S9oTCJqj?fTgHdCMi%&3BZ=-3tvxIb=FERNap~DsmO)jg4jL};q?S;lx z(wp~;Xm=#FeTn{Jm=Qa%1$;0opvB{C&K`y4?F^lSTApvhCajmymNn3^R5fRRSYgjH z8K0e7UA7hQ|JQ-pIOAT(y!HkjCR0X&`CO*`*rs%`Gwn=*QhI;lD$nKOasy;&3I4$vepPp^rU9y5j{=oVFfPx!My+T>mnPcQ7d zFGtkW+w|LyDuIQr{+^~s{OS2W-~Vr%zZ_2Gci=qtM4W49oYoDmt%c(xu=M-uG97xv zk!u@h2=l>A>@?IV==)C0JjP6ijzLfK%bv1lOnO2r=Fsq$KWfH4z$|>aQcO*{wqrB$ zVyHFllISM=*u7_>saYQhF$0FP&!E>kauc)^Ts_!3`AYJ~-N)XZ&(1g=Nw#m`HSg`i z_S_H55_AWOCvIRr#3*Rs2q`&v`LiWc;J3RFGyK`X?6|tf+rR-_dm6 m1{^+!Fm> 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%0DbB1t;>I@A-S?p- z5^R%9B~Hkh_d2kp_Y!II66AhSD)!`19EIVGlgT_;1&=1XuE@jH{>(r-k}fwyU$kF1 zn*y&=K?gPa`Zy-r8$!1Iai29fh1Gc-NTG+(12H_zI++E~Oyr{n!V6f~1b@nmgN}_w z3A=U851N(EG+_g0{nz-BtVBhhe~4_#17DgAzSqs2BAeCTm)7@lr>&7{)^>#t<&O5G z)!z!3SD80i|3Lq_OD0>e%$uHOfD6*RjyenVq6U+q4bMt0UXpNb;Sf}^1$VZZ=Ur=s z74ID^tu?US>L=mcZ^x)&9cZuq4^PjU{`3E^{oiv9#+v_q(Hg*Y&5zIk)W4OXr&%7$ z`O6Uvu#IPRCZjK^rI{%UV|f;7;H7BzsNt;6-efAo=l5=iWviO(r}6KU@IE`pUO$K< z7ku~M$#U50x6w4`4DJgyFt)}l5*p@8`m*30TfQ@#P9&lpG$>~k@b}TmLqE{-F$1T8 z8rB0ZbnZJQ>IG0MoGXu~{bcL@_)$ruBUvq~!+j$BsHurFE#Fj++irjtVS5+4Vyn$t zn)}iH@ouEPr_Dd1zcIqigJ#Fq=hsZ3SMc78My%7~9WLNZ?C(co9{pg6m@R(%IfxR$ zlT6o^a89Yz7u|M9IJdSOCb}m}*tYLvCQ{p){d{DQn2h;24TdLo4-#zxC7k!K9we^X zA?@e?`O*LXah~tkpF1ZVp=(XhZ=ZCDeY4CZUEGIGam!@zsAQ}o@vGM*ke8&OkFI|TQ$U#H?W-R7< zk2+E#^Pc?A9n^iew_4S16TX zABnRuGv|P~?yfZ7-QWL!=mq^P4W6ew4~UAz63!YPku@mekZ@^mcaUxP=dX=8rvPZTBHMjZHKz_(e$=k^XbQk}Z-4)C3sbRT@$ z$bNiTWiUMjr)T8~bNcKC zUmMTkBNzIUO_4h-pKije|6@0=~63%uNwxWrFg!A6zDWYdu%^t42D(Y5AI3LcqD=x^DaQ-&;u6Sa+g!AF+x5YV= zC7g9vT>DR)9R~N~KdF$s2Z96N=pJ*Pn@4Ye?e>PHZ03@~B#**=t0-et2^nNn3cZ51 zPgwizsWkU9JnM)2WEr;((5H5oSFF;2a@;j!^N?nf`Pj(VYqeH(uQ-fCO-?Uo3U!s|N&9 z#qK~_X?%{iK>X3{y|Ymb#m`ryyQ-)RQ!xm-5t`?1)^8K5TSz!t{LB&$KqEr)+;a9+ zaqW2tXRVxD;$!ILYo14^l#4TZNH}|nSH*Y3B%IHUDE&{I=USQYB;2RmKuj<=@R-$m zl}GiwF~feMoZW1f4PQ}k&WAi<-QWRK+|-fszyDu<*+cXgyTmn`@j-B^0@1vOp0NnAENihk)i(JIUNJO!TVI?bRP z^9&w^mqSTxiWzk+aDf^G)6#aBGwJBg3*7?97re|h5BzxlmwuSNcBXn;Lb)S+rI)`@ z(W@cB{3d1)@_u+wqwTT0{X>7!Uh7RPC!X(LjDD0DNNXRx=0`3_I4h=f5Ca1woNZ^3 zxT8?Qx#JK=F%Mb+8Vu74vcw}boDH`v=fw9J63(q}To#R%OE|kGo)_J9C7d%4o)SA^ z-a_+tX;blk;+)%~FCWmPhze&o(2cb(*xR6d`k3iJ%2|)t{CC+@ULTri-9EFbEtzCH z+mRLq>u{YO>13OX`)HT8{N#^=bni8Exp^NR{}z77CQj5CRvZ@K7~ENk{!iXCzR4_( zng=;iSGJU!?vAFg9Qb{2+ria0BWUqyC)zpLg*RUjPS%x9bmoT-A2>XeUbk>2+iszJ z^>XODxH`k5H=57U3!77yxf5T zd%a=(dKb{UtLOu-d&N>tL$_ZB&U$bi{%Knl9Zhs33)kknmXJZpy3hc8WyB5prIM*F z?nw`s^TUIZsReS%k6owoK33SCL04 zkGnH@91P;Mo%T^>H0IMTM)Mm+VYJ|=GdXQa=6hO)(8|W(%dN@cC)PmAF;hjg!;Wyb zae)*J?rd5q=Re_zYz1G&O?OZ7cF<3I6z@%I2cP4smj%-e^r!kCZ6szTN;rEMn~4*( zr1)(8VVY=I6E|{8w}=hTNjNK}#fS_%Mh$LvPG^cePy=b!D>Kr>_5-E;94X%~hGM2u z^SrlSl-M7+l4d=EP8D&hbiQ&*IK2;2 zk>SOso=xBsB?^WYaUFWao_x8@_GKlCXlAShfb-(4IT!K`Dz`cQHYq|ZxWa>9oMN=!Cc`|hM%?`u!XG8$ctc=IZuM6%g zV)($Wv80-a88+>7o-!wjI`4~F@&tp^PO5z&P&|`X-k2M zrddAa4BU*@+f)=->k~f^1-`&*6%FWJOVoYhO^G?KG%2*MxYxmlz8&?X-Tkj5|O{lwk6nA)1b`bev#-U)&OP=}BpN7dXC+zv1UqtUH#a2a&?`etog+8?Z zy^1DP>4@s*@Q0l1O4jlw;+L<`sRRGkE26o$?1c|ay$W8z^v%+?s?XbZ+ulk^>Q?KMju!s>mn*&<4B%*wYcBW0b;CN8W}@c;MjNlbA^ZQ+(#ESVfJ4Bgb!aHLUrM1 zY1ZTm*S8C#{Zm|Mu)UU;G|r!TOvc>uFI_Pr#+QyKLA&I4Q_&s1Sh>&$ySczX+^2&+ zQY{Z!f3Jgh8h6|-%Y0~0qwb>SK30S6lbB(mm8BFzCVFF}8k!86=YxKli3`mnobz7x z5KEbabC|NNxNo(Dv(i{s9AATTp86TD!ae8T_w|MGWikKeeNFGykq_RjCZki(VMu(# znsqIvykN{YXZ~W26-Us!h5m3@Q~o>&bJ}Hl_>3(X@KST7C4+r=yFaOvvB!;O{EFtvxd*9os5_1Oa)_^ey`RDo zpt}@xl>Z8kqgZ!$`fYrJ=Y>Vm&Rft!jD5ksPY$E9PHt4U^cUaN23*2}uCymYTg%LP{&{k64tb0_NVKsTDQtrK!K=ySVzkaJffaWQ&#Vk>CT7a5C2 zaw*3fTG3B@dr`vqK%$YD94FyyUca?iFi66AI%yQWmcAmK4CJdHe5@Vr}SYx2q41{==@~uA9E}5IT(w`u7lZm-&-koIkzwF&4+8#?fFh_+wYGyCB7o z4vU(L9ZpC%yXn*sy@yFSe@m$16TuVFe7>{w1wLcDw4Zr!8XpfXkLLOBbphPyr^Lk# zjNQTgg17t&=gpQaxk);sA+OMXnD~IrxK=`+o
  • &uccIM=|)bPGnlvfNxP2(!8(G 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

    nvY)nRL%m%bT(SYeImbb+sP358!c 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 Date: Tue, 7 Jul 2020 18:09:26 +0200 Subject: [PATCH 099/114] Simplify in normal color Light and exposure with slider structure --- rtdata/languages/Francais | 4 ++-- rtdata/languages/default | 4 ++-- rtgui/locallabtools.cc | 12 ++++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index b85a969b1..1d79c7628 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2212,8 +2212,8 @@ TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ) TP_LOCALLAB_SHOWR;Masque et modifications TP_LOCALLAB_SHOWREF;Prévisualisation ΔE TP_LOCALLAB_SHOWS;Masque et modifications -TP_LOCALLAB_SHOWSTRUC;Montrer Spot structure -TP_LOCALLAB_SHOWSTRUCEX;Montrer Spot structure +TP_LOCALLAB_SHOWSTRUC;Montrer Spot structure (expert) +TP_LOCALLAB_SHOWSTRUCEX;Montrer Spot structure (expert) TP_LOCALLAB_SHOWT;Masque et modifications TP_LOCALLAB_SHOWVI;Masque et modifications TP_LOCALLAB_SHRESFRA;Ombres/Lumières diff --git a/rtdata/languages/default b/rtdata/languages/default index dea86eba4..84128387f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2790,8 +2790,8 @@ TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ) TP_LOCALLAB_SHOWR;Mask and modifications TP_LOCALLAB_SHOWREF;Preview ΔE TP_LOCALLAB_SHOWS;Mask and modifications -TP_LOCALLAB_SHOWSTRUC;Show structure Spot -TP_LOCALLAB_SHOWSTRUCEX;Show structure Spot +TP_LOCALLAB_SHOWSTRUC;Show structure Spot(expert) +TP_LOCALLAB_SHOWSTRUCEX;Show structure Spot(expert) TP_LOCALLAB_SHOWT;Mask and modifications TP_LOCALLAB_SHOWVI;Mask and modifications TP_LOCALLAB_SHRESFRA;Shadows/Highlights diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 49b6feb56..5310a2d2f 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -1680,6 +1680,7 @@ void LocallabColor::convertParamToNormal() // Set hidden GUI widgets in Normal mode to default spot values blurcolde->setValue((double)defSpot.blurcolde); + structcol->setValue((double)defSpot.structcol); // softradiuscol->setValue(defSpot.softradiuscol); strcolab->setValue(defSpot.strcolab); strcolh->setValue(defSpot.strcolh); @@ -1811,9 +1812,11 @@ void LocallabColor::updateGUIToMode(const modeType new_type) maskHCurveEditorG->hide(); mask2CurveEditorGwav->hide(); csThresholdcol->hide(); + structcol->hide(); } else { // Advanced widgets are shown in Expert mode blurcolde->show(); + structcol->show(); if (!invers->get_active()) { // Keep widget hidden when invers is toggled softradiuscol->show(); @@ -2077,6 +2080,7 @@ void LocallabColor::updateColorGUI1() if (mode == Normal) { // Keep widget hidden in Normal mode softradiuscol->show(); + structcol->hide(); } expgradcol->show(); @@ -3012,6 +3016,8 @@ void LocallabExposure::convertParamToNormal() slomaskexp->setValue(defSpot.slomaskexp); strmaskexp->setValue(defSpot.strmaskexp); angmaskexp->setValue(defSpot.angmaskexp); + structexp->setValue((double)defSpot.structexp); + // laplacexp->setValue(defSpot.laplacexp); // linear->setValue(defSpot.linear); // balanexp->setValue(defSpot.balanexp); @@ -3030,6 +3036,7 @@ void LocallabExposure::updateGUIToMode(const modeType new_type) slomaskexp->hide(); gradFramemask->hide(); blurexpde->hide(); + structexp->hide(); // pdeFrame->hide(); } else { // Advanced widgets are shown in Expert mode @@ -3038,6 +3045,7 @@ void LocallabExposure::updateGUIToMode(const modeType new_type) slomaskexp->show(); gradFramemask->show(); blurexpde->show(); + structexp->show(); // pdeFrame->show(); } } @@ -3210,7 +3218,7 @@ void LocallabExposure::updateExposureGUI3() updateExposureGUI2(); } - structexp->hide(); + //structexp->hide(); // shadex->hide(); shadex->show(); softradiusexp->hide(); @@ -3223,7 +3231,7 @@ void LocallabExposure::updateExposureGUI3() showmaskexpMethodinv->show(); } else { expMethod->show(); - structexp->show(); + //structexp->show(); shadex->show(); softradiusexp->show(); expgradexp->show(); From 4e9b554f7fdc17ed51c73bb96186ad8806dd3082 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 8 Jul 2020 07:33:22 +0200 Subject: [PATCH 100/114] Change length of some labels --- rtdata/languages/default | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 84128387f..0eef7b003 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2378,7 +2378,7 @@ TP_LOCALLAB_BUTTON_DEL;Delete TP_LOCALLAB_BUTTON_DUPL;Duplicate TP_LOCALLAB_BUTTON_REN;Rename TP_LOCALLAB_BUTTON_VIS;Show/Hide -TP_LOCALLAB_CBDL;Contrast by detail levels - Defects +TP_LOCALLAB_CBDL;Contrast by detail levels-Defects TP_LOCALLAB_CBDLCLARI_TOOLTIP;Takes the midtones and enhance them. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Prevent the sharpening of noise @@ -2579,11 +2579,11 @@ TP_LOCALLAB_LIST_TOOLTIP;Choose a tool and then its level of complexity "Normal" TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Give priority to action on midtones and high lights and by choosing the concerned wavelet levels TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights TP_LOCALLAB_LOCCONT;Unsharp Mask -TP_LOCALLAB_LOC_CONTRAST;Local contrast -Wavelet - defects +TP_LOCALLAB_LOC_CONTRAST;Local contrast -Wavelet-defects TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramid 1: TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramid 2: -TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contrast by Levels- Tone Mapping - Dir. Contrast -TP_LOCALLAB_LOC_CONTRASTPYRLAB; Graduated Filter - Edge Sharpness - Blur +TP_LOCALLAB_LOC_CONTRASTPYR2LAB;Contrast by Levels- Tone Mapping - Dir.Contrast +TP_LOCALLAB_LOC_CONTRASTPYRLAB;Graduated Filter - Edge Sharpness - Blur TP_LOCALLAB_LOC_RESIDPYR;Residual Image Main TP_LOCALLAB_LOG;Encoding log TP_LOCALLAB_LOGAUTO;Automatic @@ -2683,7 +2683,7 @@ TP_LOCALLAB_ORIGLC;Merge only with original image TP_LOCALLAB_ORRETILAP_TOOLTIP;Acts on a second Laplacian threshold, to take into account ΔE to differentiate the action especially with the background (different from Scope) TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced TP_LOCALLAB_PASTELS2;Vibrance -TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard +TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n TP_LOCALLAB_PREVIEW;Preview ΔE @@ -2886,7 +2886,7 @@ TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Achieve a Tone-mapping or reduction local contrast TP_LOCALLAB_WAVCOMP_TOOLTIP;Achive local contrast in function of the direction wavelet decomposition : horizontal, vertical, diagonal TP_LOCALLAB_WAVCON;Contrast by Level TP_LOCALLAB_WAVCONTF_TOOLTIP;Similar to Contrast By Detail Levels : on abscissa levels. -TP_LOCALLAB_WAVDEN;Luminance denoise by level (0 1 2 + 3 and more) +TP_LOCALLAB_WAVDEN;Luminance denoise by level (0 1 2 +3 and more) TP_LOCALLAB_WAVE;Ψ Wavelet TP_LOCALLAB_WAVEDG;Local contrast TP_LOCALLAB_WAVEEDG_TOOLTIP;Achieves a sharpness taking into account the notion of edges wavelet.\nRequires that at least the first 4 levels are usable From e2d04832506de64b8ca3e439bc03a5024c033bf5 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 8 Jul 2020 17:52:46 +0200 Subject: [PATCH 101/114] Suppress forgotten in settings Laplace checkbox --- rtgui/controlspotpanel.cc | 5 +++-- rtgui/locallab.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc index 33936324e..5aa501476 100644 --- a/rtgui/controlspotpanel.cc +++ b/rtgui/controlspotpanel.cc @@ -469,7 +469,7 @@ ControlSpotPanel::ControlSpotPanel(): laplac_->set_tooltip_text(M("TP_LOCALLAB_LAP_MASK_TOOLTIP")); } - maskBox->pack_start(*laplac_); +// maskBox->pack_start(*laplac_); maskBox->pack_start(*deltae_); maskBox->pack_start(*scopemask_); // maskBox->pack_start(*shortc_); @@ -835,7 +835,8 @@ void ControlSpotPanel::load_ControlSpot_param() avoid_->set_active(row[spots_.avoid]); blwh_->set_active(row[spots_.blwh]); recurs_->set_active(row[spots_.recurs]); - laplac_->set_active(row[spots_.laplac]); + // laplac_->set_active(row[spots_.laplac]); + laplac_->set_active(true); deltae_->set_active(row[spots_.deltae]); scopemask_->setValue((double)row[spots_.scopemask]); shortc_->set_active(row[spots_.shortc]); diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 2f25490fe..883ef9aa2 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -315,7 +315,7 @@ void Locallab::read(const rtengine::procparams::ProcParams* pp, const ParamsEdit r->avoid = pp->locallab.spots.at(i).avoid; r->blwh = pp->locallab.spots.at(i).blwh; r->recurs = pp->locallab.spots.at(i).recurs; - r->laplac = pp->locallab.spots.at(i).laplac; + r->laplac = true; //pp->locallab.spots.at(i).laplac; r->deltae = pp->locallab.spots.at(i).deltae; r->scopemask = pp->locallab.spots.at(i).scopemask; r->shortc = pp->locallab.spots.at(i).shortc; From 89a9fcce0de8050ce1df45df052de37d69370ebe Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 08:14:01 +0200 Subject: [PATCH 102/114] Some changes to labels fr and color light --- rtdata/languages/Francais | 2 +- rtdata/languages/default | 1 + rtgui/locallabtools.cc | 15 +++++++++++---- rtgui/locallabtools.h | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 1d79c7628..d3e365f8b 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2134,7 +2134,7 @@ TP_LOCALLAB_RESIDHITHR;Hautes lumières seuil TP_LOCALLAB_RESIDSHA;Ombres TP_LOCALLAB_RESIDSHATHR;Ombres seuil TP_LOCALLAB_RETI;De-brume - Retinex Fort contraste -TP_LOCALLAB_RETIFRA;Retinexfr +TP_LOCALLAB_RETIFRA;Retinex TP_LOCALLAB_RETIM;Original Retinex TP_LOCALLAB_RETITOOLFRA;Retinex Outils TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT améliore la qualité et autorise de grands rayons, mais accroît les temps de traitement.\nCe temps dépends de la surface traitée\nLe temps de traitements dépend de "scale" (échelle) (soyez prudent avec les hautes valeurs ).\nA utiliser de préférence avec de grand rayons.\n\nLes Dimensions peuvent être réduites de quelques pixels pour optimiser FFTW.\nCette optimisation peut réduire le temps de traitement d'un facteur de 1.5 à 10.\nOptimisation pas utilsée en prévisualisation diff --git a/rtdata/languages/default b/rtdata/languages/default index 0eef7b003..1bdbd3047 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2603,6 +2603,7 @@ TP_LOCALLAB_LUMADARKEST;Darkest TP_LOCALLAB_LUMASK;Luminance Background Mask TP_LOCALLAB_LUMASK_TOOLTIP;Adjust the gray of the mask background in Show Mask (Mask and modifications) TP_LOCALLAB_LUMAWHITESEST;Whiteest +TP_LOCALLAB_LUMFRA;L*a*b* standard TP_LOCALLAB_LUMONLY;Luminance only TP_LOCALLAB_MASKCOM;Common Color Mask TP_LOCALLAB_MASKCOM_TOOLTIP;These masks works as all tools, they take into account scope color.\nThey are different from others masks which complete a tool (Color and Light, Exposure...) diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 5310a2d2f..663735520 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -388,6 +388,7 @@ LocallabColor::LocallabColor(): LocallabTool(this, M("TP_LOCALLAB_COLOR_TOOLNAME"), M("TP_LOCALLAB_COFR"), false), // Color & Light specific widgets + lumFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LUMFRA")))), curvactiv(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_CURV")))), lightness(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LIGHTNESS"), -100, 500, 1, 0))), contrast(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTRAST"), -100, 100, 1, 0))), @@ -477,6 +478,7 @@ LocallabColor::LocallabColor(): // Parameter Color & Light specific widgets curvactivConn = curvactiv->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::curvactivChanged)); + lumFrame->set_label_align(0.025, 0.5); lightness->setAdjusterListener(this); @@ -730,10 +732,14 @@ LocallabColor::LocallabColor(): Gtk::Frame* const superFrame = Gtk::manage(new Gtk::Frame()); superFrame->set_label_align(0.025, 0.5); // superFrame->set_label_widget(*curvactiv); + ToolParamBlock* const lumBox = Gtk::manage(new ToolParamBlock()); + lumBox->pack_start(*lightness); + lumBox->pack_start(*contrast); + lumBox->pack_start(*chroma); + lumFrame->add(*lumBox); + pack_start(*lumFrame); ToolParamBlock* const superBox = Gtk::manage(new ToolParamBlock()); - superBox->pack_start(*lightness); - superBox->pack_start(*contrast); - superBox->pack_start(*chroma); + ToolParamBlock* const gridBox = Gtk::manage(new ToolParamBlock()); gridBox->pack_start(*labgrid); gridBox->pack_start(*gridMethod); @@ -875,7 +881,7 @@ void LocallabColor::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, void LocallabColor::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { - exp->set_tooltip_text(M("TP_LOCALLAB_EXPCOLOR_TOOLTIP")); + lumFrame->set_tooltip_text(M("TP_LOCALLAB_EXPCOLOR_TOOLTIP")); lightness->set_tooltip_text(M("TP_LOCALLAB_LIGHTN_TOOLTIP")); structcol->set_tooltip_text(M("TP_LOCALLAB_STRUCT_TOOLTIP")); sensi->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); @@ -904,6 +910,7 @@ void LocallabColor::updateAdviceTooltips(const bool showTooltips) special->set_tooltip_text(M("TP_LOCALLAB_SPECIAL_TOOLTIP")); } else { exp->set_tooltip_text(""); + lumFrame->set_tooltip_text(M("")); lightness->set_tooltip_text(""); structcol->set_tooltip_text(""); sensi->set_tooltip_text(""); diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index d3672b819..9ec9322d4 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -174,6 +174,7 @@ class LocallabColor: { private: // Color & Light specific widgets + Gtk::Frame* const lumFrame; Gtk::CheckButton* const curvactiv; Adjuster* const lightness; Adjuster* const contrast; From 2a361536ef6542f485a90430b9a6a8c050df229b Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 08:51:01 +0200 Subject: [PATCH 103/114] Change label frame wavelet --- rtdata/languages/default | 1 + rtgui/locallabtools.cc | 4 ++++ rtgui/locallabtools.h | 1 + rtgui/locallabtools2.cc | 21 +++++++++++++++++---- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 1bdbd3047..942958d96 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2426,6 +2426,7 @@ TP_LOCALLAB_CONTRAST;Contrast TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;Main mask contrast control. TP_LOCALLAB_CONTRESID;Contrast TP_LOCALLAB_CONTTHR;Contrast Threshold +TP_LOCALLAB_CONTWFRA;Local contrast TP_LOCALLAB_CSTHRESHOLD;Ψ Wavelets Levels TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Mask Wavelet level TP_LOCALLAB_CURV;Lightness - Contrast - Chrominance "Super" diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 663735520..ed4f7ed93 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -883,6 +883,8 @@ void LocallabColor::updateAdviceTooltips(const bool showTooltips) if (showTooltips) { lumFrame->set_tooltip_text(M("TP_LOCALLAB_EXPCOLOR_TOOLTIP")); lightness->set_tooltip_text(M("TP_LOCALLAB_LIGHTN_TOOLTIP")); + contrast->set_tooltip_text(M(" ")); + chroma->set_tooltip_text(M(" ")); structcol->set_tooltip_text(M("TP_LOCALLAB_STRUCT_TOOLTIP")); sensi->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); strcol->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); @@ -912,6 +914,8 @@ void LocallabColor::updateAdviceTooltips(const bool showTooltips) exp->set_tooltip_text(""); lumFrame->set_tooltip_text(M("")); lightness->set_tooltip_text(""); + contrast->set_tooltip_text(M("")); + chroma->set_tooltip_text(M("")); structcol->set_tooltip_text(""); sensi->set_tooltip_text(""); angcol->set_tooltip_text(M("")); diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 9ec9322d4..df396af82 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -946,6 +946,7 @@ private: Adjuster* const lcamount; Adjuster* const lcdarkness; Adjuster* const lclightness; + Gtk::Frame* const contFrame; Adjuster* const sigmalc; CurveEditorGroup* const LocalcurveEditorwav; FlatCurveEditor* const wavshape; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index fbc05d2c9..c40ce97e8 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -2074,6 +2074,7 @@ LocallabContrast::LocallabContrast(): lcamount(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_AMOUNT"), 0, 1.0, 0.01, 0))), lcdarkness(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_DARKNESS"), 0, 3.0, 0.01, 1.0))), lclightness(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_LIGHTNESS"), 0, 3.0, 0.01, 1.0))), + contFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CONTWFRA")))), sigmalc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), LocalcurveEditorwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), wavshape(static_cast(LocalcurveEditorwav->addCurve(CT_Flat, "", nullptr, false, false))), @@ -2169,6 +2170,9 @@ LocallabContrast::LocallabContrast(): lcdarkness->setAdjusterListener(this); lclightness->setAdjusterListener(this); + + contFrame->set_label_align(0.025, 0.5); + sigmalc->setAdjusterListener(this); LocalcurveEditorwav->setCurveListener(this); @@ -2396,15 +2400,23 @@ LocallabContrast::LocallabContrast(): mask2lcCurveEditorG->curveListComplete(); // Add Local contrast specific widgets to GUI + ToolParamBlock* const coBox = Gtk::manage(new ToolParamBlock()); + coBox->pack_start(*sigmalc); + coBox->pack_start(*LocalcurveEditorwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + // pack_start(*levelwav); + coBox->pack_start(*csThreshold); + contFrame->add(*coBox); + pack_start(*localcontMethod); pack_start(*lcradius); pack_start(*lcamount); pack_start(*lcdarkness); pack_start(*lclightness); - pack_start(*sigmalc); - pack_start(*LocalcurveEditorwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + pack_start(*contFrame); +// pack_start(*sigmalc); +// pack_start(*LocalcurveEditorwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor // pack_start(*levelwav); - pack_start(*csThreshold); +// pack_start(*csThreshold); Gtk::Frame* const shresFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_SHRESFRA"))); shresFrame->set_label_align(0.025, 0.5); ToolParamBlock* const resiBox = Gtk::manage(new ToolParamBlock()); @@ -2571,7 +2583,7 @@ void LocallabContrast::getMaskView(int &colorMask, int &colorMaskinv, int &expMa void LocallabContrast::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { - exp->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRAST_TOOLTIP")); + contFrame->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRAST_TOOLTIP")); levelwav->set_tooltip_markup(M("TP_LOCALLAB_LEVELWAV_TOOLTIP")); LocalcurveEditorwav->set_tooltip_markup(M("TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP")); wavgradl->set_tooltip_text(M("TP_LOCALLAB_WAVGRAD_TOOLTIP")); @@ -2596,6 +2608,7 @@ void LocallabContrast::updateAdviceTooltips(const bool showTooltips) mask2lcCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { exp->set_tooltip_text(""); + contFrame->set_tooltip_text(M("")); levelwav->set_tooltip_text(""); LocalcurveEditorwav->set_tooltip_markup(M("")); wavgradl->set_tooltip_text(""); From bc596090ecef59bb53b2c3fd340a008f714c961e Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 08:55:55 +0200 Subject: [PATCH 104/114] French label contrsat W --- rtdata/languages/Francais | 1 + rtgui/locallabtools2.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index d3e365f8b..fd255f02a 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1848,6 +1848,7 @@ TP_LOCALLAB_CONTRAST;Contraste TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;Contrôle de contraste du masque. TP_LOCALLAB_CONTRESID;Contraste TP_LOCALLAB_CONTTHR;Seuil contraste +TP_LOCALLAB_CONTWFRA;Contrast Local TP_LOCALLAB_CSTHRESHOLD;Ψ Ondelettes niveaux TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Masque Ondelettes niveau TP_LOCALLAB_CURV;Luminosité - Contraste - Chrominance "Super" diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index c40ce97e8..c5684c090 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -3668,6 +3668,7 @@ void LocallabContrast::updateContrastGUI1() lcamount->show(); lcdarkness->show(); lclightness->show(); + contFrame->hide(); sigmalc->hide(); LocalcurveEditorwav->hide(); levelwav->hide(); @@ -3682,6 +3683,7 @@ void LocallabContrast::updateContrastGUI1() lcamount->hide(); lcdarkness->hide(); lclightness->hide(); + contFrame->show(); sigmalc->show(); LocalcurveEditorwav->show(); levelwav->show(); From b7fa705580c8fb32abbc90cd4888f41ff543c37b Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 10:26:09 +0200 Subject: [PATCH 105/114] Change label CBDL tooltip - simplify GUI tooltip --- rtdata/languages/Francais | 1 + rtdata/languages/default | 1 + rtgui/locallabtools.h | 1 + rtgui/locallabtools2.cc | 112 +++++--------------------------------- 4 files changed, 16 insertions(+), 99 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index fd255f02a..8b71f05df 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1993,6 +1993,7 @@ TP_LOCALLAB_LEVELBLUR;Maximum Flouter TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;En abscisse le contraste local (proche du concept de luminance). En ordonnée, amplification ou reduction du contraste local. TP_LOCALLAB_LEVELWAV;Ψ Ondelettes Niveaux TP_LOCALLAB_LEVELWAV_TOOLTIP;Le niveau est automatiquement adapté à la taille du spot et de la prévisualisation.\nDu niveau 9 taille max 512 jusqu'au niveau 1 taille max = 4 +TP_LOCALLAB_LEVFRA;Niveaux TP_LOCALLAB_LIGHTNESS;Luminosité TP_LOCALLAB_LIGHTN_TOOLTIP;En mode inverse: selection = -100 force la luminance à zero TP_LOCALLAB_LIGHTRETI;Luminosité diff --git a/rtdata/languages/default b/rtdata/languages/default index 942958d96..c46cc69be 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2571,6 +2571,7 @@ TP_LOCALLAB_LEVELBLUR;Maximum Blur levels TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;On the abscissa local contrast (near concept luminance). On the ordinate, amplification or reduction local contrast. TP_LOCALLAB_LEVELWAV;Ψ Wavelets Levels TP_LOCALLAB_LEVELWAV_TOOLTIP;The Level is automatically adapted to the size of the spot and the preview.\nFrom level 9 size max 512 to level 1 size max = 4 +TP_LOCALLAB_LEVFRA;Levels TP_LOCALLAB_LIGHTNESS;Lightness TP_LOCALLAB_LIGHTN_TOOLTIP;In inverse mode: selection = -100 force luminance to zero TP_LOCALLAB_LIGHTRETI;Lightness diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index df396af82..5b64e4edb 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -1088,6 +1088,7 @@ class LocallabCBDL: { private: const std::array multiplier; + Gtk::Frame* const levFrame; Adjuster* const chromacbdl; Adjuster* const threshold; Adjuster* const blurcbdl; diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index c5684c090..34d51504b 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -292,21 +292,6 @@ void LocallabTone::updateAdviceTooltips(const bool showTooltips) mask2tmCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { exp->set_tooltip_text(""); - estop->set_tooltip_text(""); - rewei->set_tooltip_text(""); - scaltm->set_tooltip_text(M("")); - gamma->set_tooltip_text(M("")); - equiltm->set_tooltip_text(M("")); - sensitm->set_tooltip_text(""); - expmasktm->set_tooltip_text(""); - CCmasktmshape->setTooltip(""); - LLmasktmshape->setTooltip(""); - HHmasktmshape->setTooltip(""); - lapmasktm->set_tooltip_text(""); - radmasktm->set_tooltip_text(""); - Lmasktmshape->setTooltip(""); - blendmasktm->set_tooltip_text(M("")); - mask2tmCurveEditorG->set_tooltip_text(M("")); } } @@ -1047,33 +1032,8 @@ void LocallabRetinex::updateAdviceTooltips(const bool showTooltips) blendmaskreti->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); mask2retiCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { - sensih->set_tooltip_text(""); - fftwreti->set_tooltip_text(""); - neigh->set_tooltip_text(""); - vart->set_tooltip_text(""); - loglin->set_tooltip_text(M("")); - scalereti->set_tooltip_text(M("")); - limd->set_tooltip_text(M("")); - offs->set_tooltip_text(M("")); - lightnessreti->set_tooltip_text(M("")); - darkness->set_tooltip_text(M("")); - equilret->set_tooltip_text(M("")); - softradiusret->set_tooltip_text(""); - cTtransshape->setTooltip(""); - mMLabels->set_tooltip_text(""); - transLabels->set_tooltip_text(""); - cTgainshape->setTooltip(""); - expmaskreti->set_tooltip_text(""); - enaretiMasktmap->set_tooltip_text(""); - CCmaskretishape->setTooltip(""); - LLmaskretishape->setTooltip(""); - HHmaskretishape->setTooltip(""); - radmaskreti->set_tooltip_text(""); - lapmaskreti->set_tooltip_text(""); - Lmaskretishape->setTooltip(""); - blendmaskreti->set_tooltip_text(M("")); - mask2retiCurveEditorG->set_tooltip_text(M("")); - } + exp->set_tooltip_text(""); + } } void LocallabRetinex::setDefaultExpanderVisibility() @@ -2608,30 +2568,7 @@ void LocallabContrast::updateAdviceTooltips(const bool showTooltips) mask2lcCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { exp->set_tooltip_text(""); - contFrame->set_tooltip_text(M("")); - levelwav->set_tooltip_text(""); - LocalcurveEditorwav->set_tooltip_markup(M("")); - wavgradl->set_tooltip_text(""); - clariFrame->set_tooltip_text(""); - clarisoft->set_tooltip_text(""); - wavedg->set_tooltip_text(""); - wavblur->set_tooltip_text(M("")); - wavcont->set_tooltip_text(M("")); - wavcompre->set_tooltip_text(M("")); - wavcomp->set_tooltip_text(M("")); - chromablu->set_tooltip_text(""); - chromalev->set_tooltip_text(""); - fftwlc->set_tooltip_text(""); - expmasklc->set_tooltip_text(""); - CCmasklcshape->setTooltip(""); - LLmasklcshape->setTooltip(""); - HHmasklcshape->setTooltip(""); - Lmasklcshape->setTooltip(""); - expcontrastpyr->set_tooltip_text(M("")); - expcontrastpyr2->set_tooltip_text(M("")); - blendmasklc->set_tooltip_text(M("")); - mask2lcCurveEditorG->set_tooltip_text(M("")); - } + } } void LocallabContrast::setDefaultExpanderVisibility() @@ -3744,6 +3681,7 @@ LocallabCBDL::LocallabCBDL(): return res; } ()), + levFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LEVFRA")))), chromacbdl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMACBDL"), 0., 1.5, 0.01, 0.))), threshold(Gtk::manage(new Adjuster(M("TP_DIRPYREQUALIZER_THRESHOLD"), 0, 1., 0.01, 0.2))), blurcbdl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCBDL"), 0., 100., 0.1, 0.))), @@ -3849,20 +3787,23 @@ LocallabCBDL::LocallabCBDL(): lumacontrastPlusPressedConn = lumacontrastPlusButton->signal_pressed().connect(sigc::mem_fun(*this, &LocallabCBDL::lumacontrastPlusPressed)); // Add CBDL specific widgets to GUI + ToolParamBlock* const levBox = Gtk::manage(new ToolParamBlock()); Gtk::HBox* buttonBox = Gtk::manage(new Gtk::HBox(true, 10)); buttonBox->pack_start(*lumacontrastMinusButton); buttonBox->pack_start(*lumaneutralButton); buttonBox->pack_start(*lumacontrastPlusButton); - pack_start(*buttonBox); + levBox->pack_start(*buttonBox); for (const auto adj : multiplier) { - pack_start(*adj); + levBox->pack_start(*adj); } Gtk::HSeparator* const separator = Gtk::manage(new Gtk::HSeparator()); - pack_start(*separator, Gtk::PACK_SHRINK, 2); - pack_start(*chromacbdl); - pack_start(*threshold); + levBox->pack_start(*separator, Gtk::PACK_SHRINK, 2); + levBox->pack_start(*chromacbdl); + levBox->pack_start(*threshold); + levFrame->add(*levBox); + pack_start(*levFrame); // pack_start(*blurcbdl); Gtk::Frame* const residFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_RESID"))); residFrame->set_label_align(0.025, 0.5); @@ -3915,7 +3856,7 @@ void LocallabCBDL::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, void LocallabCBDL::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { - exp->set_tooltip_text(M("TP_LOCALLAB_EXPCBDL_TOOLTIP")); + levFrame->set_tooltip_text(M("TP_LOCALLAB_EXPCBDL_TOOLTIP")); for (const auto adj : multiplier) { adj->set_tooltip_text(M("TP_LOCALLAB_CBDL_ADJ_TOOLTIP")); } @@ -3934,22 +3875,6 @@ void LocallabCBDL::updateAdviceTooltips(const bool showTooltips) mask2cbCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { exp->set_tooltip_text(""); - for (const auto adj : multiplier) { - adj->set_tooltip_text(M("")); - } - threshold->set_tooltip_text(M("")); - chromacbdl->set_tooltip_text(""); - clarityml->set_tooltip_text(M("")); - sensicb->set_tooltip_text(""); - expmaskcb->set_tooltip_text(""); - CCmaskcbshape->setTooltip(""); - LLmaskcbshape->setTooltip(""); - HHmaskcbshape->setTooltip(""); - radmaskcb->set_tooltip_text(""); - lapmaskcb->set_tooltip_text(""); - Lmaskcbshape->setTooltip(""); - blendmaskcb->set_tooltip_text(M("")); - mask2cbCurveEditorG->set_tooltip_text(M("")); } } @@ -4452,17 +4377,6 @@ void LocallabLog::updateAdviceTooltips(const bool showTooltips) anglog->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); } else { exp->set_tooltip_text(M("")); - logPFrame->set_tooltip_text(""); - autocompute->set_tooltip_text(M("")); - blackEv->set_tooltip_text(M("")); - whiteEv->set_tooltip_text(M("")); - Autogray->set_tooltip_text(M("")); - sourceGray->set_tooltip_text(M("")); - targetGray->set_tooltip_text(M("")); - detail->set_tooltip_text(M("")); - baselog->set_tooltip_text(M("")); - strlog->set_tooltip_text(M("")); - anglog->set_tooltip_text(M("")); } } From 37615dfdb78c4effdba32cbaf80a7af36109d9eb Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 10:36:55 +0200 Subject: [PATCH 106/114] Simplify 2 GUI tooltip --- rtgui/locallabtools.cc | 78 ++---------------------------------------- 1 file changed, 3 insertions(+), 75 deletions(-) diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index ed4f7ed93..d032f26b1 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -912,35 +912,6 @@ void LocallabColor::updateAdviceTooltips(const bool showTooltips) special->set_tooltip_text(M("TP_LOCALLAB_SPECIAL_TOOLTIP")); } else { exp->set_tooltip_text(""); - lumFrame->set_tooltip_text(M("")); - lightness->set_tooltip_text(""); - contrast->set_tooltip_text(M("")); - chroma->set_tooltip_text(M("")); - structcol->set_tooltip_text(""); - sensi->set_tooltip_text(""); - angcol->set_tooltip_text(M("")); - strcol->set_tooltip_text(""); - qualitycurveMethod->set_tooltip_text(""); - mercol->set_tooltip_text(""); - opacol->set_tooltip_text(""); - conthrcol->set_tooltip_text(""); - gridmerFrame->set_tooltip_text(""); - expmaskcol->set_tooltip_text(""); - CCmaskshape->setTooltip(""); - LLmaskshape->setTooltip(""); - HHmaskshape->setTooltip(""); - radmaskcol->set_tooltip_text(""); - lapmaskcol->set_tooltip_text(""); - Lmaskshape->setTooltip(""); - LLmaskcolshapewav->setTooltip(""); - expmaskcol1->set_tooltip_text(M("")); - blendmaskcol->set_tooltip_text(M("")); - struFrame->set_tooltip_text(M("")); - blurFrame->set_tooltip_text(M("")); - maskHCurveEditorG->set_tooltip_text(M("")); - mask2CurveEditorGwav->set_tooltip_text(M("")); - mask2CurveEditorG->set_tooltip_text(M("")); - special->set_tooltip_text(M("")); } } @@ -3519,21 +3490,6 @@ void LocallabShadow::updateAdviceTooltips(const bool showTooltips) mask2SHCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { exp->set_tooltip_text(""); - strSH->set_tooltip_text(""); - expmasksh->set_tooltip_text(""); - CCmaskSHshape->setTooltip(""); - LLmaskSHshape->setTooltip(""); - HHmaskSHshape->setTooltip(""); - radmaskSH->set_tooltip_text(""); - lapmaskSH->set_tooltip_text(""); - LmaskSHshape->setTooltip(""); - for (unsigned int i = 0; i < multipliersh.size(); i++) { - multipliersh[i]->set_tooltip_text(M("")); - } - gamSH->set_tooltip_text(M("")); - sloSH->set_tooltip_text(M("")); - blendmaskSH->set_tooltip_text(M("")); - mask2SHCurveEditorG->set_tooltip_text(M("")); } } @@ -4331,15 +4287,7 @@ void LocallabVibrance::updateAdviceTooltips(const bool showTooltips) blendmaskvib->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); mask2vibCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); } else { - warm->set_tooltip_text(""); - strvib->set_tooltip_text(""); - expmaskvib->set_tooltip_text(""); - CCmaskvibshape->setTooltip(""); - LLmaskvibshape->setTooltip(""); - HHmaskvibshape->setTooltip(""); - Lmaskvibshape->setTooltip(""); - blendmaskvib->set_tooltip_text(M("")); - mask2vibCurveEditorG->set_tooltip_text(M("")); + exp->set_tooltip_text(""); } } @@ -4953,11 +4901,7 @@ void LocallabSoft::updateAdviceTooltips(const bool showTooltips) streng->set_tooltip_text(M("TP_LOCALLAB_ORRETISTREN_TOOLTIP")); laplace->set_tooltip_text(M("TP_LOCALLAB_ORRETILAP_TOOLTIP")); } else { - softMethod->set_tooltip_markup(M("")); - // ctboxsoftmethod->set_tooltip_markup(M("")); - showmasksoftMethod->set_tooltip_markup(M("")); - streng->set_tooltip_text(M("")); - laplace->set_tooltip_text(M("")); + exp->set_tooltip_text(""); } } @@ -5526,23 +5470,7 @@ void LocallabBlur::updateAdviceTooltips(const bool showTooltips) blendmaskbl->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); showmaskblMethodtyp->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKTYP_TOOLTIP")); } else { - expblnoise->set_tooltip_text(""); - radius->set_tooltip_text(""); - sensibn->set_tooltip_text(""); - blurMethod->set_tooltip_text(""); - expdenoise->set_tooltip_text(""); - wavshapeden->setTooltip(""); - noiselumc->set_tooltip_text(""); - expmaskbl->set_tooltip_text(""); - CCmaskblshape->setTooltip(""); - LLmaskblshape->setTooltip(""); - HHmaskblshape->setTooltip(""); - radmaskbl->set_tooltip_text(""); - lapmaskbl->set_tooltip_text(""); - Lmaskblshape->setTooltip(""); - LLmaskblshapewav->setTooltip(""); - blendmaskbl->set_tooltip_text(M("")); - showmaskblMethodtyp->set_tooltip_markup(M("")); + exp->set_tooltip_text(""); } } From 17a4a0c5f647f7d894736b6e1244eac0f5ae737d Mon Sep 17 00:00:00 2001 From: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com> Date: Thu, 9 Jul 2020 16:30:00 +0200 Subject: [PATCH 107/114] Add slightly better white-levels for Fuji X-T2 --- rtengine/camconst.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rtengine/camconst.json b/rtengine/camconst.json index 6e686c2f1..a10e19bc8 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -1370,13 +1370,21 @@ Camera constants: }, { // Quality B - "make_model": [ "FUJIFILM X-T2", "FUJIFILM X-T20", "FUJIFILM X-E3", "FUJIFILM X100F", "FUJIFILM X-PRO2", "FUJIFILM X-H1" ], + "make_model": [ "FUJIFILM X-T2", "FUJIFILM X-T20", "FUJIFILM X-E3", "FUJIFILM X100F", "FUJIFILM X-H1" ], "dcraw_matrix": [ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 ], // DNG_v9.4 D65 // "raw_crop": [ 0, 5, 6032, 4032 ], // full raw 6160,4032, Usable 6032,4032 - for lossless compressed files // "raw_crop": [ 0, 0, 6032, 4032 ], // full raw 6160,4032, Usable 6032,4032 - for uncompressed files "raw_crop": [ 0, 5, 6032, 4026 ], // full raw 6160,4032, Usable 6032,4026 - for uncompressed and lossless compressed files (but reduces height by 6 pixels) "ranges": { "white": 16100 } }, + + { // Quality B, samples provided by Claes + "make_model": "FUJIFILM X-PRO2", + "dcraw_matrix": [ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 ], // DNG_v9.4 D65 + "raw_crop": [ 0, 5, 6032, 4026 ], // see X-T2 + "ranges": { "white": [ 16105, 16270, 16082 ] } // These values are the lowest pixel values >16000 for all ISOs. LENR has a negligble effect. + // No aperture scaling data provided, but likely negligible + }, { // Quality C, only raw crop "make_model": [ "FUJIFILM X-T3", "FUJIFILM X-T30", "FUJIFILM X-PRO3", "FUJIFILM X100V", "FUJIFILM X-T4" ], From 4c59a0916b8301e8bc6808f0f8d4dc1a3d3f7227 Mon Sep 17 00:00:00 2001 From: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com> Date: Thu, 9 Jul 2020 16:53:54 +0200 Subject: [PATCH 108/114] Add slightly better white-levels for Fuji X-T1 --- rtengine/camconst.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rtengine/camconst.json b/rtengine/camconst.json index a10e19bc8..a960596c1 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -1344,12 +1344,20 @@ Camera constants: }, { // Quality B - "make_model": [ "FUJIFILM X-T1", "FUJIFILM X-T10", "FUJIFILM X-E2" ], + "make_model": [ "FUJIFILM X-T10", "FUJIFILM X-E2" ], "dcraw_matrix": [ 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 ], // DNG D65 //"dcraw_matrix": [ 9289,-3279,-632,-3539,11137,2758,-1049,1950,6544 ], // X-RITE D55 - //"raw_crop": [ 4, 0, 4936, 3296 ], // full raw 4992,3296, fuji official 4936,3296 - experimental crop + //"raw_crop": [ 4, 0, 4936, 3296 ], // full raw 4992,3296, fuji official 4936,3296 "ranges": { "white": 16100 } }, + + { // Quality B, samples provided by Claes + "make_model": "FUJIFILM X-T1", + "dcraw_matrix": [ 8458,-2451,-855,-4597,12447,2407,-1475,2482,6526 ], // DNG D65 + "raw_crop": [ 4, 0, 4936, 3296 ], // full raw 4992,3296, fuji official 4936,3296 + "ranges": { "white": [ 16202, 16277, 16232 ] } // LENR on from ISO4000+ negligibly underestimates white level + // No aperture scaling data provided, but likely negligible + }, { // Quality C "make_model": [ "FUJIFILM X-T100" ], From 434274bbc9b7178c2f0f50e840287b86d24f4cc3 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 9 Jul 2020 18:31:40 +0200 Subject: [PATCH 109/114] Take into account expert in Mask Laplace --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index c46cc69be..ec81eb3eb 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2563,7 +2563,7 @@ TP_LOCALLAB_LAPLACC;ΔØ Mask Laplacian solve PDE TP_LOCALLAB_LAPLACE;Δ - Laplacian threshold ΔE TP_LOCALLAB_LAPLACEXP;∆ - Laplacian threshold TP_LOCALLAB_LAPMASKCOL;∆ - Laplacian threshold mask -TP_LOCALLAB_LAPRAD_TOOLTIP;Avoid using Radius and Laplace Threshold simultaneously.\nLaplacian threshold reduce contrast, artifacts, smooth result (if PDE settings enabled). +TP_LOCALLAB_LAPRAD_TOOLTIP;Avoid using Radius and Laplace Threshold (expert) simultaneously.\nLaplacian threshold reduce contrast, artifacts, smooth result. TP_LOCALLAB_LAP_MASK_TOOLTIP;Solve PDE for all Laplacian masks.\nIf enabled Laplacian threshold mask reduce artifacts and smooth result.\nIf disabled linear response. TP_LOCALLAB_LC_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated.\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\n TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 From 41712058fda432ae15f05c4f4a3ad98ea6960d02 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 10 Jul 2020 08:56:41 +0200 Subject: [PATCH 110/114] Clean code locallabtools --- rtgui/locallabtools.cc | 4 ++-- rtgui/locallabtools2.cc | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index d032f26b1..79985428c 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -910,7 +910,7 @@ void LocallabColor::updateAdviceTooltips(const bool showTooltips) mask2CurveEditorGwav->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); mask2CurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); special->set_tooltip_text(M("TP_LOCALLAB_SPECIAL_TOOLTIP")); - } else { + } else { exp->set_tooltip_text(""); } } @@ -4286,7 +4286,7 @@ void LocallabVibrance::updateAdviceTooltips(const bool showTooltips) Lmaskvibshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); blendmaskvib->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); mask2vibCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); - } else { + } else { exp->set_tooltip_text(""); } } diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc index 34d51504b..8370843ff 100644 --- a/rtgui/locallabtools2.cc +++ b/rtgui/locallabtools2.cc @@ -1031,9 +1031,9 @@ void LocallabRetinex::updateAdviceTooltips(const bool showTooltips) Lmaskretishape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); blendmaskreti->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); mask2retiCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); - } else { + } else { exp->set_tooltip_text(""); - } + } } void LocallabRetinex::setDefaultExpanderVisibility() @@ -1792,7 +1792,6 @@ void LocallabSharp::updateAdviceTooltips(const bool showTooltips) sensisha->set_tooltip_text(M("TP_LOCALLAB_SENSIS_TOOLTIP")); } else { exp->set_tooltip_text(""); - sensisha->set_tooltip_text(""); } } @@ -2566,9 +2565,9 @@ void LocallabContrast::updateAdviceTooltips(const bool showTooltips) expcontrastpyr2->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP")); blendmasklc->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); mask2lcCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); - } else { + } else { exp->set_tooltip_text(""); - } + } } void LocallabContrast::setDefaultExpanderVisibility() @@ -4872,7 +4871,6 @@ void LocallabMask::updateAdviceTooltips(const bool showTooltips) Lmask_shape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); mask2CurveEditorGwav->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); LLmask_shapewav->setTooltip(M("TP_LOCALLAB_LMASK_LEVEL_TOOLTIP")); - } else { exp->set_tooltip_text(M("")); } From 64c6bcfabbcbe5cd2d1acdc95bc3f572861d348b Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 10 Jul 2020 20:08:07 +0200 Subject: [PATCH 111/114] Change some labels and tooltip --- rtdata/languages/default | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index ec81eb3eb..2d4254a85 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2378,7 +2378,7 @@ TP_LOCALLAB_BUTTON_DEL;Delete TP_LOCALLAB_BUTTON_DUPL;Duplicate TP_LOCALLAB_BUTTON_REN;Rename TP_LOCALLAB_BUTTON_VIS;Show/Hide -TP_LOCALLAB_CBDL;Contrast by detail levels-Defects +TP_LOCALLAB_CBDL;Contrast by detail levels TP_LOCALLAB_CBDLCLARI_TOOLTIP;Takes the midtones and enhance them. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Prevent the sharpening of noise @@ -2405,7 +2405,7 @@ TP_LOCALLAB_CLARISOFT_TOOLTIP;Enabled for Clarity and Sharp mask if Merge Luma d TP_LOCALLAB_CLARITYML;Clarity TP_LOCALLAB_CLARI_TOOLTIP;Under or equal level wavelet 4, 'Sharp mask' is enabled.\nAbove level wavelet 5 'Clarity' is enabled.\nUsefull if you use 'Level dynamic Range Compression' TP_LOCALLAB_CLIPTM;Clip Restored datas (gain) -TP_LOCALLAB_COFR;Color & Light - Small defects +TP_LOCALLAB_COFR;Color & Light TP_LOCALLAB_COLORDE;Color preview selection ΔE - Intensity TP_LOCALLAB_COLORDEPREV_TOOLTIP;Button Preview ΔE needs that only one tool is enabled (expander).\nTo be able to have an Preview ΔE with several enable tools use Mask and modifications - Preview ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative. @@ -2480,10 +2480,12 @@ TP_LOCALLAB_EXCLUTYPE;Spot method TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Normal spot use recursive data.\n\nExcluding spot reinitialize data to origin.\nCan be used to totally or partially cancel a previous action or to perform a inverse mode TP_LOCALLAB_EXECLU;Excluding spot TP_LOCALLAB_EXNORM;Normal spot -TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition decay (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise) +//TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition decay (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCBDL_TOOLTIP;Can be used to remove marks on the sensor or lens. TP_LOCALLAB_EXPCHROMA;Chroma compensation TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors -TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) +//TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCOLOR_TOOLTIP;Adjust color, lightness, contrast and correct small defects such as red-eye, sensor dust etc. TP_LOCALLAB_EXPCOMP_TOOLTIP;For portrait or images with low color gradient, you can change "Shape detection" in "settings":\n\nIncrease 'Threshold ΔE scope'\nReduce 'ΔE decay'\nIncrease 'Balance ΔE ab-L' TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high decay transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. @@ -2496,8 +2498,9 @@ TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greate TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties) TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise. TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. -TP_LOCALLAB_EXPOSE;PDE algorithms & Exposure -TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPOSE;Dynamic Range Compr. & Exposure +//TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPOSURE_TOOLTIP;Modify exposure in L*a*b space using Laplacian PDE algorithms to take into account dE and minimize artifacts. TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high decay transition values and scope to simulate small RT-spot. TP_LOCALLAB_EXPTOOL;Tools exposure From 53aec5e1346a78d2e172b93693883b4c57751a30 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 10 Jul 2020 20:23:37 +0200 Subject: [PATCH 112/114] Others chnages to labels --- rtdata/languages/default | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2d4254a85..0715f1846 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2496,7 +2496,7 @@ TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Apply a gamma before and after Laplace transform TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Add linear exposure component before application Laplace transform TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greater the action of reducing contrast. TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties) -TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise. +TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nContrast attenuator : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nContrast attenuator, Dynamic range compression and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nReduce artifacts and noise. TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. TP_LOCALLAB_EXPOSE;Dynamic Range Compr. & Exposure //TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... @@ -2505,7 +2505,7 @@ TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high decay transition values and scope to simulate small RT-spot. TP_LOCALLAB_EXPTOOL;Tools exposure TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC -TP_LOCALLAB_EXP_TOOLNAME;Laplacian PDE -Dynamic Range Compression & Exposure- 10 +TP_LOCALLAB_EXP_TOOLNAME;Contrast attenuator -Dynamic Range Compression & Exposure- 10 TP_LOCALLAB_FATAMOUNT;Amount TP_LOCALLAB_FATANCHOR;Anchor TP_LOCALLAB_FATANCHORA;Offset @@ -2689,8 +2689,8 @@ TP_LOCALLAB_ORIGLC;Merge only with original image TP_LOCALLAB_ORRETILAP_TOOLTIP;Acts on a second Laplacian threshold, to take into account ΔE to differentiate the action especially with the background (different from Scope) TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced TP_LOCALLAB_PASTELS2;Vibrance -TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression -TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ +TP_LOCALLAB_PDE;Contrast attenuator - Dynamic Range compression +TP_LOCALLAB_PDEFRA;Contrast attenuator ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n TP_LOCALLAB_PREVIEW;Preview ΔE TP_LOCALLAB_PREVHIDE;Hide all settings From 738ad15af451e9d2ec6bc0973d66e7dc949d35b2 Mon Sep 17 00:00:00 2001 From: Desmis Date: Sat, 11 Jul 2020 07:44:30 +0200 Subject: [PATCH 113/114] Others changes to labels and french --- rtdata/languages/Francais | 24 ++++++++++++++---------- rtdata/languages/default | 3 ++- rtgui/locallabtools.cc | 3 ++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 8b71f05df..10dabb4a4 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1800,7 +1800,7 @@ TP_LOCALLAB_BUTTON_DEL;Effacer TP_LOCALLAB_BUTTON_DUPL;Dupliquer TP_LOCALLAB_BUTTON_REN;Renommer TP_LOCALLAB_BUTTON_VIS;Montrer/Cacher -TP_LOCALLAB_CBDL;Contraste niveaux détail-Défauts +TP_LOCALLAB_CBDL;Contraste niveaux détail TP_LOCALLAB_CBDLCLARI_TOOLTIP;Ajuste les tons moyens et les réhausse. TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Agit comme un outil ondelettes.\nLe premier niveau (0) agit sur des détails de 2x2.\nLe dernier niveau (5) agit sur des détails de 64x64. TP_LOCALLAB_CBDL_THRES_TOOLTIP;Empêche d'augmenter le bruit @@ -1827,7 +1827,7 @@ TP_LOCALLAB_CLARISOFT_TOOLTIP;Actif pour Clarté et Masque de netteté si diffé TP_LOCALLAB_CLARITYML;Clarté TP_LOCALLAB_CLARI_TOOLTIP;En dessous ou égal à 4, 'Masque netteté' est actif.\nAu dessus du niveau ondelettes 5 'Clarté' est actif.\nUtilesu=i vous utilisez 'Compression dynamique des niveaux' TP_LOCALLAB_CLIPTM;Clip Recupère données (gain) -TP_LOCALLAB_COFR;Couleur & Lumière - Petits défauts +TP_LOCALLAB_COFR;Couleur & Lumière TP_LOCALLAB_COLORDE;Couleur prévisualisation sélection ΔE - Intensité TP_LOCALLAB_COLORDEPREV_TOOLTIP;Bouton Prévisualisation ΔE a besoin qu'un seul outil soit activé (expander).\nPour pouvoir avoir une Prévisualisation ΔE avec plusieurs outils activés utiliser Masque et modifications - Prévisualisation ΔE TP_LOCALLAB_COLORDE_TOOLTIP;Affiche la prévisualisation ΔE en bleu si négatif et en vert si positif.\n\nMasque et modifications (montre modifications sans masque): montre les modifications réelles si positf, montre les modifications améliorées (luminance seule) en bleu et jaune si négatif. @@ -1902,10 +1902,12 @@ TP_LOCALLAB_EXCLUTYPE;Spot méthode TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Spot Normal utilise les données récursives.\n\nSpot exclusion réinitialise les données d'origine.\nPeut être utilsé pour annuler totalement ou partiellement une action précédente ou pour réaliser un mode inverse TP_LOCALLAB_EXECLU;Spot Exclusion TP_LOCALLAB_EXNORM;Spot Normal -TP_LOCALLAB_EXPCBDL_TOOLTIP;Dans le cas d'un capteur contaminé ("graisse"), et qaund la surface est importante ou pourune série de petits défauts.\n\na) Mettre la sélection du spot sur un défaut prononcé (adapter la taille si nécessaire), utilise run spot suffisament grand qui autorise les ondelettes; b) choisir une aire suffisament grande pour couvrir largement la zone affectée par les défauts; c) Choisir une valeur de transition (basse) et une transition affaiblissement (haute); d) agir sur les niveaux 2, 3, 4 or 5 ou^plus bas en réduisant le the contrast (valeurs en dessous de 100) agir sur le curseur chroma si nécessaire. e)possibilité d'agir sur "Etendue" pour réduire la zone d'action.\n\nVous pouvez aussi compléter avec "Flou niveaux" et Flouu Gaussien (Adoucir Flou et bruit) +//TP_LOCALLAB_EXPCBDL_TOOLTIP;Dans le cas d'un capteur contaminé ("graisse"), et qaund la surface est importante ou pourune série de petits défauts.\n\na) Mettre la sélection du spot sur un défaut prononcé (adapter la taille si nécessaire), utilise run spot suffisament grand qui autorise les ondelettes; b) choisir une aire suffisament grande pour couvrir largement la zone affectée par les défauts; c) Choisir une valeur de transition (basse) et une transition affaiblissement (haute); d) agir sur les niveaux 2, 3, 4 or 5 ou^plus bas en réduisant le the contrast (valeurs en dessous de 100) agir sur le curseur chroma si nécessaire. e)possibilité d'agir sur "Etendue" pour réduire la zone d'action.\n\nVous pouvez aussi compléter avec "Flou niveaux" et Flouu Gaussien (Adoucir Flou et bruit) +TP_LOCALLAB_EXPCBDL_TOOLTIP;Peut être utilisé pour retirer les marques sur le capteur ou la lentille. TP_LOCALLAB_EXPCHROMA;Chroma compensation TP_LOCALLAB_EXPCHROMA_TOOLTIP;Seulement en association avec compensation d'exposition et PDE Ipol.\nEvite la desaturation des couleurs -TP_LOCALLAB_EXPCOLOR_TOOLTIP;Dans le cas de petits défauts.\n\nYeux-rouges : sélecteur centré sur la partie rouge, délimiteurs du spot près de l'oeil, adaptez Etendue, "luminosité" -100, "chrominance" -100.\n\nSpotIR :sélection Circulaire centrée sur le défaut, délimiteurs du spot proches du default - reduire "chrominance", possibilté d'agir sur Etendue pour réduire la zone de l'action.\n\nPoussières - graisse (petit) :Sélection circulaire centrée sur le défaut (adapter la taille du spot), délimiteurs du spot pas trop près du défaut pour permettre une transition quasi invisible. a) "Transition" (faibles valeurs) and "Transition affaiblissement" (hautes valeurs); b) agir sur "luminosité" et aussi sur "chrominance" ou sur "Grille de correction couleur - direct" de telle manière que le rendu de la zone polluée soit proche de celui de la zone saine; c) agir modérement sur "Etendue" pour moduler l'action.\n\nVous pouvez aussi compléter avec Flouter Gaussien (Adoucir Flouter et bruit) +//TP_LOCALLAB_EXPCOLOR_TOOLTIP;Dans le cas de petits défauts.\n\nYeux-rouges : sélecteur centré sur la partie rouge, délimiteurs du spot près de l'oeil, adaptez Etendue, "luminosité" -100, "chrominance" -100.\n\nSpotIR :sélection Circulaire centrée sur le défaut, délimiteurs du spot proches du default - reduire "chrominance", possibilté d'agir sur Etendue pour réduire la zone de l'action.\n\nPoussières - graisse (petit) :Sélection circulaire centrée sur le défaut (adapter la taille du spot), délimiteurs du spot pas trop près du défaut pour permettre une transition quasi invisible. a) "Transition" (faibles valeurs) and "Transition affaiblissement" (hautes valeurs); b) agir sur "luminosité" et aussi sur "chrominance" ou sur "Grille de correction couleur - direct" de telle manière que le rendu de la zone polluée soit proche de celui de la zone saine; c) agir modérement sur "Etendue" pour moduler l'action.\n\nVous pouvez aussi compléter avec Flouter Gaussien (Adoucir Flouter et bruit) +TP_LOCALLAB_EXPCOLOR_TOOLTIP;Ajuste les couleurs, la luminosité, le contrast et corrige les petits défauts tels que teux-rouges, poussières sur le capteur, etc. TP_LOCALLAB_EXPCOMP_TOOLTIP;Pour les portraits et les images à faible gradient, vous pouvez changer "Détection de forme" dans "Réglages":\n\nAugmentez 'Seuil ΔE Etendue'\nRéduire 'ΔE affaiblissement'\nAugmenter 'Balance ΔE ab-L' TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;Voir la documentation de ondelettes niveaux.\nCependant il y a des différences: plus d'outils et plus proches des détails .\nEx: Tone mapping pour ondelettes. TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Evitez les spots trop petits(< 32x32 pixels).\nUtilisez de faibles valeurs de transition et de hautes valeurs de transition affaiblissement et d'Etendue pour simuler un petit RT-spot et s'adapter aux défauts.\nUtimiser si nécessaire le module 'Clarté & Maqsue netteté' et 'Fusion d'images' en ajustant 'Rayon adoucir' pour réduire les artéfacts. @@ -1916,15 +1918,16 @@ TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Applique un gamma avant et après la transformée TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Ajoute une exposition linéaire avant l'application de la transformée de Laplace TP_LOCALLAB_EXPLAP_TOOLTIP;Plus vous agissez sur ce curseur de seuil, plus grande sera l'action de reduire le contraste. TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Autorise de nombreuses possibilités de fusionner les images (comme les calques dans Photosshop) : difference, multiply, soft light, overlay...avec opacité...\nOriginale Image : fusionne le RT-spot en cours avec Originale.\nSpot Précédent : fusionne le RT-spot en cours avec le précédent - si il n'y a qu'un spot précédent = original.\nArrière plan : fusionne le RT-spot en cours avec la couleur et la luminance de l'arrière plan (moins de possibilités) -TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : utilise un algorithme similaire à Exposure principal mais en L*a*b* et en prenant en compte le deltaE.\n\nLaplacian & PDE : utilise un autre algorithme aussi avec deltaE et avec l'équation de Poisson pour résoudre le Laplacien dans l'espace de Fourier.\nPDE IPOL, PDE Fattal et Standard peuvent être combinés.\nFFTW La transformée de Fourier est optimisée en taille pour réduire les temps de traitement.\nPDE reduit les artéfacts et le bruit. +TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : utilise un algorithme similaire à Exposure principal mais en L*a*b* et en prenant en compte le deltaE.\n\nCompression dynamique et atténuateur de contraste : utilise un autre algorithme aussi avec deltaE et avec l'équation de Poisson pour résoudre le Laplacien dans l'espace de Fourier.\nAtténuateur, Compression dynamqiue et Standard peuvent être combinés.\nFFTW La transformée de Fourier est optimisée en taille pour réduire les temps de traitement.\nRéduit les artéfacts et le bruit. TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Applique un median avant la transformée de Laplace pour éviter les artéfacts (bruit).\nVous pouvez aussi utiliser l'outil "Réduction du bruit". -TP_LOCALLAB_EXPOSE;PDE algorithmes - Exposition -TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez utiliser le module "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPOSE;Compression dynamique - Exposition +//TP_LOCALLAB_EXPOSURE_TOOLTIP;Dans certains cases (fortes ombres ..) vous pouvez utiliser le module "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPOSURE_TOOLTIP;Modifie l'exposition dans l'espace L*a*b* en utilisant un Laplacien et les algorithmes PDE en prenant en compte dE, minimise les artéfacts. TP_LOCALLAB_EXPRETITOOLS;Outils Retinex avancés TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUtiliser de basses valeurs de transition et de hautes valeurs de transition affaiblissement et Etendue pour simuler un petit RT-spot. TP_LOCALLAB_EXPTOOL;Outils exposition TP_LOCALLAB_EXPTRC;Courbe de réponse Tonale - TRC -TP_LOCALLAB_EXP_TOOLNAME;Laplacien PDE -Compression Dynamique & Exposition- 10 +TP_LOCALLAB_EXP_TOOLNAME;Atténuation contraste -Compression Dynamique & Exposition- 10 TP_LOCALLAB_FATAMOUNT;Quantité TP_LOCALLAB_FATANCHOR;Ancre TP_LOCALLAB_FATANCHORA;Décalage @@ -2108,7 +2111,7 @@ TP_LOCALLAB_ORRETILAP_TOOLTIP;Agit sur un deuxième seuil Laplacien, pour prendr TP_LOCALLAB_ORRETISTREN_TOOLTIP;Aagit sur un seuil Laplacien, plus grande est l'action, plus les différences de contraste seront réduites TP_LOCALLAB_PASTELS2;Vibrance TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Compression dynamique + Standard -TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuation ƒ +TP_LOCALLAB_PDEFRA;Contrast atténuation ƒ TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - algorithme personnel adapté de IPOL à Rawtherapee: conduit à des résultats très variés et a besoin de différents réglages que Standard (Noir négatif, gamma < 1,...)\nPeut être utils pour des iamges sous-exposées ou avec une étendue dynamique importante.\n TP_LOCALLAB_PREVIEW;Prévisualisation ΔE TP_LOCALLAB_PREVHIDE;Cacher tous les réglages @@ -2227,7 +2230,8 @@ TP_LOCALLAB_SLOMASKCOL;Pente (slope) masque TP_LOCALLAB_SLOSH;Pente TP_LOCALLAB_SOFT;Lumière douce - Original Retinex TP_LOCALLAB_SOFTM;Lumière douce (soft light) -TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres méthodes Retinex.\nIl agit sur les gris et équilibre la luminance.\nC'est une émulation de "Dodge" and "Burn" +//TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex est très différent des autres méthodes Retinex.\nIl agit sur les gris et équilibre la luminance.\nC'est une émulation de "Dodge" and "Burn" +TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Applique un mélange Lumière douce. Effectue une émulation de "dodge and burn" en utilisant l'algorithme original de retinex. TP_LOCALLAB_SOFTRADIUSCOL;Rayon adoucir TP_LOCALLAB_SOFTRETI;Reduire artefact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Prend en compte ΔE pour améliorer Transmission map diff --git a/rtdata/languages/default b/rtdata/languages/default index 0715f1846..908a0b4e3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2809,7 +2809,8 @@ TP_LOCALLAB_SLOMASKCOL;Slope mask TP_LOCALLAB_SLOSH;Slope TP_LOCALLAB_SOFT;Soft Light - Original Retinex TP_LOCALLAB_SOFTM;Soft Light -TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex is very different from others Retinex method.\nIts acts on grey and balance luminance.\nIt is an emulation of "Dodge" and "Burn" +//TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex is very different from others Retinex method.\nIts acts on grey and balance luminance.\nIt is an emulation of "Dodge" and "Burn" +TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Apply a Soft-light blend (identical to the global adjustment). Carry out dodge and burn using the original Retinex algorithm. TP_LOCALLAB_SOFTRADIUSCOL;Soft radius TP_LOCALLAB_SOFTRETI;Reduce artifact ΔE TP_LOCALLAB_SOFTRETI_TOOLTIP;Take into account deltaE to improve Transmission map diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 79985428c..f66625df9 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -4895,7 +4895,8 @@ void LocallabSoft::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, void LocallabSoft::updateAdviceTooltips(const bool showTooltips) { if (showTooltips) { - softMethod->set_tooltip_markup(M("TP_LOCALLAB_SOFTMETHOD_TOOLTIP")); + exp->set_tooltip_markup(M("TP_LOCALLAB_SOFTMETHOD_TOOLTIP")); + //softMethod->set_tooltip_markup(M("TP_LOCALLAB_SOFTMETHOD_TOOLTIP")); // ctboxsoftmethod->set_tooltip_markup(M("TP_LOCALLAB_ORRETISTEP_TOOLTIP")); showmasksoftMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP")); streng->set_tooltip_text(M("TP_LOCALLAB_ORRETISTREN_TOOLTIP")); From 3b4d470b4bd95e88d75edc92b1a0bea8ed95752d Mon Sep 17 00:00:00 2001 From: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com> Date: Sat, 11 Jul 2020 09:16:16 +0200 Subject: [PATCH 114/114] Add slightly better white-levels for Fuji X-T2 (#5824) --- rtengine/camconst.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rtengine/camconst.json b/rtengine/camconst.json index a960596c1..19e25264c 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -1378,7 +1378,7 @@ Camera constants: }, { // Quality B - "make_model": [ "FUJIFILM X-T2", "FUJIFILM X-T20", "FUJIFILM X-E3", "FUJIFILM X100F", "FUJIFILM X-H1" ], + "make_model": [ "FUJIFILM X-T20", "FUJIFILM X-E3", "FUJIFILM X100F", "FUJIFILM X-H1" ], "dcraw_matrix": [ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 ], // DNG_v9.4 D65 // "raw_crop": [ 0, 5, 6032, 4032 ], // full raw 6160,4032, Usable 6032,4032 - for lossless compressed files // "raw_crop": [ 0, 0, 6032, 4032 ], // full raw 6160,4032, Usable 6032,4032 - for uncompressed files @@ -1386,6 +1386,14 @@ Camera constants: "ranges": { "white": 16100 } }, + { // Quality B, samples provided by Daniel Catalina #5824 + "make_model": "FUJIFILM X-T2", + "dcraw_matrix": [ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 ], // DNG_v9.4 D65 + "raw_crop": [ 0, 5, 6032, 4026 ], // xee X-T20 + "ranges": { "white": [ 16195, 16270, 16195 ] } // With LENR on and ISO4000+ starts to overestimate white level, more realistic would be 16090 + // Negligible aperture scaling effect + }, + { // Quality B, samples provided by Claes "make_model": "FUJIFILM X-PRO2", "dcraw_matrix": [ 11434,-4948,-1210,-3746,12042,1903,-666,1479,5235 ], // DNG_v9.4 D65