diff --git a/rtdata/languages/default b/rtdata/languages/default index dcf626948..facf89498 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -728,7 +728,10 @@ HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction +HISTORY_MSG_DEHAZE_DEPTH;Dehaze - Depth +HISTORY_MSG_DEHAZE_DETAIL;Dehaze - Detail HISTORY_MSG_DEHAZE_ENABLED;Haze Removal +HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Dehaze - Show depth map HISTORY_MSG_DEHAZE_STRENGTH;Dehaze - Strength HISTORY_MSG_DUALDEMOSAIC_CONTRAST;AMaZE+VNG4 - Contrast threshold HISTORY_MSG_HISTMATCHING;Auto-matched tone curve @@ -1508,7 +1511,10 @@ TP_DARKFRAME_LABEL;Dark-Frame TP_DEFRINGE_LABEL;Defringe TP_DEFRINGE_RADIUS;Radius TP_DEFRINGE_THRESHOLD;Threshold +TP_DEHAZE_DEPTH;Depth +TP_DEHAZE_DETAIL;Detail TP_DEHAZE_LABEL;Haze Removal +TP_DEHAZE_SHOW_DEPTH_MAP;Show Depth Map TP_DEHAZE_STRENGTH;Strength TP_DIRPYRDENOISE_CHROMINANCE_AMZ;Auto multi-zones TP_DIRPYRDENOISE_CHROMINANCE_AUTOGLOBAL;Automatic global diff --git a/rtengine/ipdehaze.cc b/rtengine/ipdehaze.cc index aed647417..7fa1988ce 100644 --- a/rtengine/ipdehaze.cc +++ b/rtengine/ipdehaze.cc @@ -218,6 +218,59 @@ void get_luminance(Imagefloat *img, array2D &Y, TMatrix ws, bool multithr } +void apply_contrast(array2D &dark, int contrast, double scale, bool multithread) +{ + if (contrast) { + const int W = dark.width(); + const int H = dark.height(); + + double tot = 0.0; +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + double ytot = 0.0; + for (int x = 0; x < W; ++x) { + ytot += dark[y][x]; + } +#ifdef _OPENMP + #pragma omp critical +#endif + { + tot += ytot; + } + } + + float avg = tot / (W * H); + + std::vector pts = { + DCT_NURBS, + 0, //black point. Value in [0 ; 1] range + 0, //black point. Value in [0 ; 1] range + + avg - avg * (0.6 - contrast / 250.0), //toe point + avg - avg * (0.6 + contrast / 250.0), //value at toe point + + avg + (1 - avg) * (0.6 - contrast / 250.0), //shoulder point + avg + (1 - avg) * (0.6 + contrast / 250.0), //value at shoulder point + + 1., // white point + 1. // value at white point + }; + + const DiagonalCurve curve(pts, CURVES_MIN_POLY_POINTS / scale); + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + dark[y][x] = curve.getVal(dark[y][x]); + } + } + } +} + } // namespace @@ -231,14 +284,14 @@ void ImProcFunctions::dehaze(Imagefloat *img) const int W = img->getWidth(); const int H = img->getHeight(); - const float strength = LIM01(float(params->dehaze.strength) / 100.f * 0.9f); + float strength = LIM01(float(params->dehaze.strength) / 100.f * 0.9f); if (options.rtSettings.verbose) { std::cout << "dehaze: strength = " << strength << std::endl; } array2D dark(W, H); - const int patchsize = std::max(W / 200, 2); + const int patchsize = std::max(W / (200 + max(params->dehaze.detail, 0)), 2); int npatches = get_dark_channel(*img, dark, patchsize, nullptr, multiThread); DEBUG_DUMP(dark); @@ -268,18 +321,27 @@ void ImProcFunctions::dehaze(Imagefloat *img) array2D &t_tilde = dark; get_dark_channel(*img, dark, patchsize, ambient, multiThread); + apply_contrast(dark, params->dehaze.depth, scale, multiThread); DEBUG_DUMP(t_tilde); - + + if (!params->dehaze.showDepthMap) { #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel for if (multiThread) #endif - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - dark[y][x] = 1.f - strength * dark[y][x]; + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + dark[y][x] = 1.f - strength * dark[y][x]; + } } } - const int radius = patchsize * 2; + float mult = 2.f; + if (params->dehaze.detail > 0) { + mult -= (params->dehaze.detail / 100.f) * 1.9f; + } else { + mult -= params->dehaze.detail / 10.f; + } + const int radius = max(int(patchsize * mult), 1); const float epsilon = 2.5e-4; array2D &t = t_tilde; @@ -287,6 +349,19 @@ void ImProcFunctions::dehaze(Imagefloat *img) DEBUG_DUMP(t); + + if (params->dehaze.showDepthMap) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + img->r(y, x) = img->g(y, x) = img->b(y, x) = t[y][x] * 65535.f; + } + } + return; + } + const float t0 = 0.01; #ifdef _OPENMP #pragma omp parallel for if (multiThread) @@ -313,7 +388,7 @@ void ImProcFunctions::dehaze(Imagefloat *img) if (newmed > 1e-5f) { const float f1 = oldmed / newmed; - const float f = f1 * 65535.f; + const float f = /* f1 * */ 65535.f; #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif @@ -328,6 +403,8 @@ void ImProcFunctions::dehaze(Imagefloat *img) Color::hsl2rgbfloat(h, s, l, img->r(y, x), img->g(y, x), img->b(y, x)); } } + } else { + img->normalizeFloatTo65535(); } } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index ceace7c3b..17afb3371 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2376,7 +2376,10 @@ bool SoftLightParams::operator !=(const SoftLightParams& other) const DehazeParams::DehazeParams() : enabled(false), - strength(50) + strength(50), + showDepthMap(false), + depth(0), + detail(0) { } @@ -2384,7 +2387,10 @@ bool DehazeParams::operator ==(const DehazeParams& other) const { return enabled == other.enabled - && strength == other.strength; + && strength == other.strength + && showDepthMap == other.showDepthMap + && depth == other.depth + && detail == other.detail; } bool DehazeParams::operator !=(const DehazeParams& other) const @@ -3062,6 +3068,9 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo // Dehaze saveToKeyfile(!pedited || pedited->dehaze.enabled, "Dehaze", "Enabled", dehaze.enabled, keyFile); saveToKeyfile(!pedited || pedited->dehaze.strength, "Dehaze", "Strength", dehaze.strength, keyFile); + saveToKeyfile(!pedited || pedited->dehaze.showDepthMap, "Dehaze", "ShowDepthMap", dehaze.showDepthMap, keyFile); + saveToKeyfile(!pedited || pedited->dehaze.depth, "Dehaze", "Depth", dehaze.depth, keyFile); + saveToKeyfile(!pedited || pedited->dehaze.detail, "Dehaze", "Detail", dehaze.detail, keyFile); // Directional pyramid denoising saveToKeyfile(!pedited || pedited->dirpyrDenoise.enabled, "Directional Pyramid Denoising", "Enabled", dirpyrDenoise.enabled, keyFile); @@ -4647,6 +4656,9 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) if (keyFile.has_group("Dehaze")) { assignFromKeyfile(keyFile, "Dehaze", "Enabled", pedited, dehaze.enabled, pedited->dehaze.enabled); assignFromKeyfile(keyFile, "Dehaze", "Strength", pedited, dehaze.strength, pedited->dehaze.strength); + assignFromKeyfile(keyFile, "Dehaze", "ShowDepthMap", pedited, dehaze.showDepthMap, pedited->dehaze.showDepthMap); + assignFromKeyfile(keyFile, "Dehaze", "Depth", pedited, dehaze.depth, pedited->dehaze.depth); + assignFromKeyfile(keyFile, "Dehaze", "Detail", pedited, dehaze.detail, pedited->dehaze.detail); } if (keyFile.has_group("Film Simulation")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 19fe0a376..143fedbe8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1230,6 +1230,9 @@ struct SoftLightParams { struct DehazeParams { bool enabled; int strength; + bool showDepthMap; + int depth; + int detail; DehazeParams(); diff --git a/rtgui/dehaze.cc b/rtgui/dehaze.cc index 6f4814e55..ef5075ab7 100644 --- a/rtgui/dehaze.cc +++ b/rtgui/dehaze.cc @@ -30,12 +30,30 @@ Dehaze::Dehaze(): FoldableToolPanel(this, "dehaze", M("TP_DEHAZE_LABEL"), false, auto m = ProcEventMapper::getInstance(); EvDehazeEnabled = m->newEvent(HDR, "HISTORY_MSG_DEHAZE_ENABLED"); EvDehazeStrength = m->newEvent(HDR, "HISTORY_MSG_DEHAZE_STRENGTH"); + EvDehazeShowDepthMap = m->newEvent(HDR, "HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP"); + EvDehazeDepth = m->newEvent(HDR, "HISTORY_MSG_DEHAZE_DEPTH"); + EvDehazeDetail = m->newEvent(HDR, "HISTORY_MSG_DEHAZE_DETAIL"); strength = Gtk::manage(new Adjuster(M("TP_DEHAZE_STRENGTH"), 0., 100., 1., 50.)); strength->setAdjusterListener(this); strength->show(); + depth = Gtk::manage(new Adjuster(M("TP_DEHAZE_DEPTH"), -100., 100., 1., 0.)); + depth->setAdjusterListener(this); + depth->show(); + + detail = Gtk::manage(new Adjuster(M("TP_DEHAZE_DETAIL"), -100, 100, 1, 0)); + detail->setAdjusterListener(this); + detail->show(); + + showDepthMap = Gtk::manage(new Gtk::CheckButton(M("TP_DEHAZE_SHOW_DEPTH_MAP"))); + showDepthMap->signal_toggled().connect(sigc::mem_fun(*this, &Dehaze::showDepthMapChanged)); + showDepthMap->show(); + pack_start(*strength); + pack_start(*depth); + pack_start(*detail); + pack_start(*showDepthMap); } @@ -45,11 +63,17 @@ void Dehaze::read(const ProcParams *pp, const ParamsEdited *pedited) if (pedited) { strength->setEditedState(pedited->dehaze.strength ? Edited : UnEdited); + depth->setEditedState(pedited->dehaze.depth ? Edited : UnEdited); + detail->setEditedState(pedited->dehaze.detail ? Edited : UnEdited); set_inconsistent(multiImage && !pedited->dehaze.enabled); + showDepthMap->set_inconsistent(!pedited->dehaze.showDepthMap); } setEnabled(pp->dehaze.enabled); strength->setValue(pp->dehaze.strength); + depth->setValue(pp->dehaze.depth); + detail->setValue(pp->dehaze.detail); + showDepthMap->set_active(pp->dehaze.showDepthMap); enableListener(); } @@ -58,22 +82,34 @@ void Dehaze::read(const ProcParams *pp, const ParamsEdited *pedited) void Dehaze::write(ProcParams *pp, ParamsEdited *pedited) { pp->dehaze.strength = strength->getValue(); + pp->dehaze.depth = depth->getValue(); + pp->dehaze.detail = detail->getValue(); pp->dehaze.enabled = getEnabled(); + pp->dehaze.showDepthMap = showDepthMap->get_active(); if (pedited) { pedited->dehaze.strength = strength->getEditedState(); + pedited->dehaze.depth = depth->getEditedState(); + pedited->dehaze.detail = detail->getEditedState(); pedited->dehaze.enabled = !get_inconsistent(); + pedited->dehaze.showDepthMap = !showDepthMap->get_inconsistent(); } } void Dehaze::setDefaults(const ProcParams *defParams, const ParamsEdited *pedited) { strength->setDefault(defParams->dehaze.strength); + depth->setDefault(defParams->dehaze.depth); + detail->setDefault(defParams->dehaze.detail); if (pedited) { strength->setDefaultEditedState(pedited->dehaze.strength ? Edited : UnEdited); + depth->setDefaultEditedState(pedited->dehaze.depth ? Edited : UnEdited); + detail->setDefaultEditedState(pedited->dehaze.detail ? Edited : UnEdited); } else { strength->setDefaultEditedState(Irrelevant); + depth->setDefaultEditedState(Irrelevant); + detail->setDefaultEditedState(Irrelevant); } } @@ -81,7 +117,13 @@ void Dehaze::setDefaults(const ProcParams *defParams, const ParamsEdited *pedite void Dehaze::adjusterChanged(Adjuster* a, double newval) { if (listener && getEnabled()) { - listener->panelChanged(EvDehazeStrength, a->getTextValue()); + if (a == strength) { + listener->panelChanged(EvDehazeStrength, a->getTextValue()); + } else if (a == depth) { + listener->panelChanged(EvDehazeDepth, a->getTextValue()); + } else if (a == detail) { + listener->panelChanged(EvDehazeDetail, a->getTextValue()); + } } } @@ -100,11 +142,21 @@ void Dehaze::enabledChanged () } +void Dehaze::showDepthMapChanged() +{ + if (listener) { + listener->panelChanged(EvDehazeShowDepthMap, showDepthMap->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + void Dehaze::setBatchMode(bool batchMode) { ToolPanel::setBatchMode(batchMode); strength->showEditedCB(); + depth->showEditedCB(); + detail->showEditedCB(); } diff --git a/rtgui/dehaze.h b/rtgui/dehaze.h index 3617f13ea..ae6097c86 100644 --- a/rtgui/dehaze.h +++ b/rtgui/dehaze.h @@ -27,9 +27,15 @@ class Dehaze: public ToolParamBlock, public AdjusterListener, public FoldableToo { private: Adjuster *strength; + Adjuster *depth; + Adjuster *detail; + Gtk::CheckButton *showDepthMap; rtengine::ProcEvent EvDehazeEnabled; rtengine::ProcEvent EvDehazeStrength; + rtengine::ProcEvent EvDehazeDepth; + rtengine::ProcEvent EvDehazeDetail; + rtengine::ProcEvent EvDehazeShowDepthMap; public: @@ -42,6 +48,7 @@ public: void adjusterChanged(Adjuster *a, double newval); void enabledChanged(); + void showDepthMapChanged(); void setAdjusterBehavior(bool strengthAdd); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index fef8da8e6..ffc1bd19c 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -568,6 +568,9 @@ void ParamsEdited::set(bool v) softlight.strength = v; dehaze.enabled = v; dehaze.strength = v; + dehaze.showDepthMap = v; + dehaze.depth = v; + dehaze.detail = v; metadata.mode = v; exif = v; @@ -1123,6 +1126,9 @@ void ParamsEdited::initFrom(const std::vector& softlight.strength = softlight.strength && p.softlight.strength == other.softlight.strength; dehaze.enabled = dehaze.enabled && p.dehaze.enabled == other.dehaze.enabled; dehaze.strength = dehaze.strength && p.dehaze.strength == other.dehaze.strength; + dehaze.showDepthMap = dehaze.showDepthMap && p.dehaze.showDepthMap == other.dehaze.showDepthMap; + dehaze.depth = dehaze.depth && p.dehaze.depth == other.dehaze.depth; + dehaze.detail = dehaze.detail && p.dehaze.detail == other.dehaze.detail; metadata.mode = metadata.mode && p.metadata.mode == other.metadata.mode; // How the hell can we handle that??? @@ -3125,6 +3131,18 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.dehaze.strength = dontforceSet && options.baBehav[ADDSET_DEHAZE_STRENGTH] ? toEdit.dehaze.strength + mods.dehaze.strength : mods.dehaze.strength; } + if (dehaze.depth) { + toEdit.dehaze.depth = mods.dehaze.depth; + } + + if (dehaze.detail) { + toEdit.dehaze.detail = mods.dehaze.detail; + } + + if (dehaze.showDepthMap) { + toEdit.dehaze.showDepthMap = mods.dehaze.showDepthMap; + } + if (metadata.mode) { toEdit.metadata.mode = mods.metadata.mode; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 6a2076032..73a19db88 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -729,6 +729,9 @@ class DehazeParamsEdited public: bool enabled; bool strength; + bool showDepthMap; + bool depth; + bool detail; };