diff --git a/rtdata/languages/default b/rtdata/languages/default index 8585a8c70..9e6e1e710 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -983,8 +983,8 @@ HISTORY_MSG_741;Local - Local contrast Residual C HISTORY_MSG_742;Local - Exp Laplacian gamma HISTORY_MSG_743;Local - Exp Fattal Amount HISTORY_MSG_744;Local - Exp Fattal Detail -HISTORY_MSG_745;Local - Exp Fattal Anchor -HISTORY_MSG_746;Local - Exp Fattal levels +HISTORY_MSG_745;Local - Exp Fattal Offset +HISTORY_MSG_746;Local - Exp Fattal Sigma HISTORY_MSG_747;Local Spot created HISTORY_MSG_748;Local - Exp Denoise HISTORY_MSG_749;Local - Reti Depth @@ -2316,11 +2316,11 @@ TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values a TP_LOCALLAB_EXPTOOL;Tools exposure TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC TP_LOCALLAB_FATAMOUNT;Amount -TP_LOCALLAB_FATANCHOR;Anchor +TP_LOCALLAB_FATANCHOR;Offset TP_LOCALLAB_FATDETAIL;Detail TP_LOCALLAB_FATFRA;Dynamic Range Compression ƒ TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal - use Fattal Tone mapping algorithm.\nAnchor allows selection according to the images over or under exposed.\nUseful to increase Luminance for a Second spot near the current and using mask -TP_LOCALLAB_FATLEVEL;Detail levels +TP_LOCALLAB_FATLEVEL;Sigma TP_LOCALLAB_FATRES;Amount Residual Image TP_LOCALLAB_FATSHFRA;Dynamic Range Compression Mask ƒ TP_LOCALLAB_FEATH_TOOLTIP;Gradient width in percent of the Spot diagonal\n.. diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 90e8db9bb..3336778d6 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -737,7 +737,7 @@ void Crop::update(int todo) if (need_fattal) { parent->ipf.dehaze(f, params.dehaze); - parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0); + parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0); } // crop back to the size expected by the rest of the pipeline diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 16263e61a..a58b56325 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -643,7 +643,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } ipf.dehaze(orig_prev, params->dehaze); - ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0); + ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0, 0); if (oprevi != orig_prev) { delete oprevi; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index c2468fc65..1a1be8c8f 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -241,7 +241,7 @@ public: void mean_dt(const float * data, size_t size, double * mean_p, double * dt_p); double *cos_table(size_t size); - void normalize_mean_dt(float *data, const float *ref, size_t size, float mod); + void normalize_mean_dt(float *data, float *ref, size_t size, float mod, float sigm); void retinex_pde(float *datain, float * dataout, int bfw, int bfh, float thresh, float multy, float *dE, int show, int dEenable, int normalize); void exposure_pde(float *dataor, float *datain, float * dataout, int bfw, int bfh, float thresh, float mod); void fftw_convol_blur(float *input, float *output, int bfw, int bfh, float radius, int fftkern, int algo); @@ -422,7 +422,7 @@ public: void dehaze(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams); void dehazeloc(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams); - void ToneMapFattal02(Imagefloat *rgb, const procparams::FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH); + void ToneMapFattal02(Imagefloat *rgb, const procparams::FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo); void localContrast(LabImage *lab, float **destination, const procparams::LocalContrastParams &localContrastParams, bool fftwlc, double scale); void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); // void shadowsHighlights(LabImage *lab); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index d8afea4a3..5518ed3c6 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -1383,6 +1383,7 @@ void ImProcFunctions::log_encode(Imagefloat *rgb, struct local_params & lp, floa const int detail2 = float(max(lp.detail, 0)) / scale + 0.5f; const int detail = lp.detail; const int W = rgb->getWidth(), H = rgb->getHeight(); + if (detail == 0) {//no preser contrast #ifdef _OPENMP # pragma omp parallel for if (multiThread) @@ -1451,15 +1452,16 @@ void ImProcFunctions::log_encode(Imagefloat *rgb, struct local_params & lp, floa } } } else if (detail == 1) {//preserve local contrast - + array2D Y(W, H); { constexpr float base_posterization = 20.f; array2D Y2(W, H); - + #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) { Y2[y][x] = norm(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x)) / 65535.f; @@ -1468,20 +1470,23 @@ void ImProcFunctions::log_encode(Imagefloat *rgb, struct local_params & lp, floa Y[y][x] = xexpf(ll); } } + const float radius = max(max(bfw, W), max(bfh, H)) / 30.f; const float epsilon = 0.005f; rtengine::guidedFilter(Y2, Y, Y, radius, epsilon, multiThread); } - + #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) { float &r = rgb->r(y, x); float &g = rgb->g(y, x); float &b = rgb->b(y, x); float t = Y[y][x]; + if (t > noise) { float c = apply(t, false); float f = c / t; @@ -3543,7 +3548,7 @@ void ImProcFunctions::mean_dt(const float * data, size_t size, double * mean_p, } -void ImProcFunctions::normalize_mean_dt(float * data, const float * ref, size_t size, float mod) +void ImProcFunctions::normalize_mean_dt(float * data, float * ref, size_t size, float mod, float sigm) { /* * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ @@ -3577,10 +3582,10 @@ void ImProcFunctions::normalize_mean_dt(float * data, const float * ref, size_t /* normalize the array */ ptr_data = data; - ptr_dataold = data; + ptr_dataold = ref;//data; for (i = 0; i < size; i++) { - *ptr_data = mod * (a * *ptr_data + b) + (1.f - mod) * *ptr_dataold;//normalize mean and stdv and balance PDE + *ptr_data = mod * (a * *ptr_data + sigm * b) + (1.f - mod) * *ptr_dataold;//normalize mean and stdv and balance PDE ptr_data++; } @@ -3721,7 +3726,7 @@ void ImProcFunctions::retinex_pde(float * datain, float * dataout, int bfw, int } if (show != 4 && normalize == 1) { - normalize_mean_dt(data, datain, bfw * bfh, 1.f); + normalize_mean_dt(data, datain, bfw * bfh, 1.f, 1.f); } if (show == 0 || show == 4) { @@ -4176,7 +4181,7 @@ void ImProcFunctions::maskcalccol(int call, bool invmask, bool pde, int bfw, int Imagefloat *tmpImagefat = nullptr; tmpImagefat = new Imagefloat(bfw, bfh); lab2rgb(*bufmaskblurcol, *tmpImagefat, params->icm.workingProfile); - ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0); + ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0); rgb2lab(*tmpImagefat, *bufmaskblurcol, params->icm.workingProfile); delete tmpImagefat; } @@ -4961,7 +4966,7 @@ void ImProcFunctions::transit_shapedetect(int senstype, const LabImage * bufexpo data[(y - ystart)* bfw + (x - xstart)] = bufexporig->L[y - ystart][x - xstart]; } - normalize_mean_dt(data, datain, bfh * bfw, 1.f); + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -6302,7 +6307,7 @@ void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImag data[(y - ystart)* bfw + (x - xstart)] = bufexporig->L[y - ystart][x - xstart]; } - normalize_mean_dt(data, datain, bfh * bfw, 1.f); + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -6543,7 +6548,7 @@ void ImProcFunctions::exposure_pde(float * dataor, float * datain, float * datao fftwf_cleanup_threads(); } - normalize_mean_dt(data, dataor, bfw * bfh, mod); + normalize_mean_dt(data, dataor, bfw * bfh, mod, 1.f); { #ifdef _OPENMP @@ -7126,7 +7131,7 @@ void ImProcFunctions::wavcont(wavelet_decomposition &wdspot, float ****templevel float klev = (loccompwavCurve[level * 55.5f]); fatParams.amount = 50.f * klev; { - ToneMapFattal02(nullptr, fatParams, 3, 1, templevel[dir - 1][level], W_L, H_L); + ToneMapFattal02(nullptr, fatParams, 3, 1, templevel[dir - 1][level], W_L, H_L, 0); } } } @@ -7194,7 +7199,7 @@ void ImProcFunctions::wavcontrast4(float ** tmp, float ** tmpa, float ** tmpb, f } } - ToneMapFattal02(nullptr, fatParams, 3, 1, bufl, W_L, H_L); + ToneMapFattal02(nullptr, fatParams, 3, 1, bufl, W_L, H_L, 0); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) @@ -7326,7 +7331,7 @@ void ImProcFunctions::wavcontrast4(float ** tmp, float ** tmpa, float ** tmpb, f if (wavcurvelev && radlevblur > 0.f && blurena) { wavcont(*wdspot, templevel, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, radlevblur, 1, fatParams, 1.f); } - + if (wavcurvecomp && comprena) { wavcont(*wdspot, templevel, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, radlevblur, 2, fatParams, 1.f); } @@ -11496,7 +11501,7 @@ void ImProcFunctions::Lab_Local(int call, int sp, float** shbuffer, LabImage * o // printf("minwin=%i maxlevelavant=%i maxlespot=%i\n", minwin, wavelet_level, maxlevelspot); wavelet_level = min(wavelet_level, maxlevelspot); - // printf("maxlevel=%i\n", wavelet_level); + // printf("maxlevel=%i\n", wavelet_level); bool exec = false; bool origlc = params->locallab.spots.at(sp).origlc; @@ -12009,7 +12014,7 @@ void ImProcFunctions::Lab_Local(int call, int sp, float** shbuffer, LabImage * o data[ir * Wd + jr] = orig[ir][jr]; } - normalize_mean_dt(data, datain, Hd * Wd, 1.f); + normalize_mean_dt(data, datain, Hd * Wd, 1.f, 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -12384,7 +12389,7 @@ void ImProcFunctions::Lab_Local(int call, int sp, float** shbuffer, LabImage * o data[ir * Wd + jr] = orig[ir][jr]; } - normalize_mean_dt(data, datain, Hd * Wd, 1.f); + normalize_mean_dt(data, datain, Hd * Wd, 1.f, 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -12823,17 +12828,56 @@ void ImProcFunctions::Lab_Local(int call, int sp, float** shbuffer, LabImage * o if (enablefat) { + float *datain = new float[bfwr * bfhr]; + float *dataout = new float[bfwr * bfhr]; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + float L = bufexpfin->L[y][x]; + datain[y * bfwr + x] = L; + } + } + FattalToneMappingParams fatParams; fatParams.enabled = true; fatParams.threshold = params->locallab.spots.at(sp).fatdetail; fatParams.amount = params->locallab.spots.at(sp).fatamount; - fatParams.anchor = params->locallab.spots.at(sp).fatanchor; - int nlev = params->locallab.spots.at(sp).fatlevel; + fatParams.anchor = 50.f; //params->locallab.spots.at(sp).fatanchor; + float sigm = params->locallab.spots.at(sp).fatlevel; + float mean = params->locallab.spots.at(sp).fatanchor; tmpImagefat = new Imagefloat(bfwr, bfhr); lab2rgb(*bufexpfin, *tmpImagefat, params->icm.workingProfile); - ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0); + ToneMapFattal02(tmpImagefat, fatParams, 3, 0, nullptr, 0, 0, 1);//last parameter = 1 ==>ART algorithm rgb2lab(*tmpImagefat, *bufexpfin, params->icm.workingProfile); delete tmpImagefat; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + float L = bufexpfin->L[y][x]; + dataout[y * bfwr + x] = L; + } + } + + normalize_mean_dt(dataout, datain, bfwr * bfhr, mean, sigm); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexpfin->L[y][x] = dataout[y * bfwr + x]; + } + } + + + delete [] datain; + delete [] dataout; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index fdbc77c29..bc5927032 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2545,13 +2545,13 @@ LocallabParams::LocallabSpot::LocallabSpot() : expMethod("std"), exnoiseMethod("none"), laplacexp(0.0), - balanexp(0.75), + balanexp(1.0), linear(0.3), gamm(0.4), fatamount(1.0), fatdetail(40.0), - fatanchor(50.0), - fatlevel(2), + fatanchor(1.0), + fatlevel(1.), // Shadow highlight shMethod("std"), multsh{0, 0, 0, 0, 0}, diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 05ae5b772..0b108fbc7 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1094,7 +1094,7 @@ struct LocallabParams { double fatamount; double fatdetail; double fatanchor; - int fatlevel; + double fatlevel; // Shadow highlight Glib::ustring shMethod; int multsh[5]; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 0a2d21d9d..11e75a097 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1244,7 +1244,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.firstAnalysis (baseImg, params, hist16); ipf.dehaze(baseImg, params.dehaze); - ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); // perform transform if (ipf.needsTransform()) { diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 953ab61da..e4a56bc0c 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -872,7 +872,7 @@ private: ipf.firstAnalysis(baseImg, params, hist16); ipf.dehaze(baseImg, params.dehaze); - ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); // perform transform (excepted resizing) if (ipf.needsTransform()) { diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index 79a2e1335..045f6f7e3 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -416,7 +416,7 @@ void calculateFiMatrix(Array2Df* FI, Array2Df* gradients[], delete[] fi; } -void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread); +void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, int algo); void tmo_fattal02(size_t width, size_t height, @@ -426,7 +426,7 @@ void tmo_fattal02(size_t width, float beta, float noise, int detail_level, - bool multithread) + bool multithread, int algo) { // #ifdef TIMER_PROFILING // msec_timer stop_watch; @@ -435,6 +435,7 @@ void tmo_fattal02(size_t width, // static const float black_point = 0.1f; // static const float white_point = 0.5f; static const float gamma = 1.0f; // 0.8f; +//paramet // static const int detail_level = 3; if (detail_level < 0) { @@ -466,7 +467,7 @@ void tmo_fattal02(size_t width, int size = width * height; - +//paramet // find max value, normalize to range 0..100 and take logarithm // float minLum = Y (0, 0); float maxLum = Y(0, 0); @@ -481,6 +482,11 @@ void tmo_fattal02(size_t width, Array2Df* H = new Array2Df(width, height); float temp = 100.f / maxLum; + + if (algo == 1) { + temp = 1.f; + } + #ifdef _OPENMP #pragma omp parallel if(multithread) #endif @@ -649,7 +655,7 @@ void tmo_fattal02(size_t width, // solve pde and exponentiate (ie recover compressed image) { MyMutex::MyLock lock(*fftwMutex); - solve_pde_fft(FI, &L, Gx, multithread); + solve_pde_fft(FI, &L, Gx, multithread, algo); } delete Gx; delete FI; @@ -868,7 +874,7 @@ std::vector get_lambda(int n) // not modified and the equation might not have a solution but an // approximate solution with a minimum error is then calculated // double precision version -void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/*, pfs::Progress &ph, +void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, int algo)/*, pfs::Progress &ph, bool adjust_bound)*/ { // ph.setValue(20); @@ -933,21 +939,23 @@ void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/*, // a solution which has no positive values: U_new(x,y)=U(x,y)-max // (not really needed but good for numerics as we later take exp(U)) //DEBUG_STR << "solve_pde_fft: removing constant from solution" << std::endl; - float maxVal = 0.f; + if (algo == 0) { + float maxVal = 0.f; #ifdef _OPENMP - #pragma omp parallel for reduction(max:maxVal) if(multithread) + #pragma omp parallel for reduction(max:maxVal) if(multithread) #endif - for (int i = 0; i < width * height; i++) { - maxVal = std::max(maxVal, (*U)(i)); - } + for (int i = 0; i < width * height; i++) { + maxVal = std::max(maxVal, (*U)(i)); + } #ifdef _OPENMP - #pragma omp parallel for if(multithread) + #pragma omp parallel for if(multithread) #endif - for (int i = 0; i < width * height; i++) { - (*U)(i) -= maxVal; + for (int i = 0; i < width * height; i++) { + (*U)(i) -= maxVal; + } } } @@ -1056,7 +1064,9 @@ inline int find_fast_dim(int dim) } // namespace -void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH) +void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo) +//algo allows to use ART algorithme algo = 0 RT, algo = 1 ART +//Lalone allows to use L without RGB values in RT mode { if (!fatParams.enabled) { return; @@ -1082,12 +1092,13 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa int w; int h; - if(Lalone != 0) { + + if (Lalone != 0) { w = WW; h = HH; } else { - w = rgb->getWidth(); - h = rgb->getHeight(); + w = rgb->getWidth(); + h = rgb->getHeight(); } Array2Df Yr(w, h); @@ -1103,7 +1114,7 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - if(Lalone != 0) { + if (Lalone != 0) { Yr(x, y) = std::max(2.f * Lum[y][x], min_luminance); // clip really black pixels } else { Yr(x, y) = std::max(luminance(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x), ws), min_luminance); // clip really black pixels @@ -1112,8 +1123,13 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa } float oldMedian; - const float percentile = float(LIM(fatParams.anchor, 1, 100)) / 100.f; - findMinMaxPercentile (Yr.data(), static_cast(Yr.getRows()) * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); + float percentile = 1.f; + + if (algo == 0) { + percentile = float(LIM(fatParams.anchor, 1, 100)) / 100.f; + findMinMaxPercentile(Yr.data(), static_cast(Yr.getRows()) * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); + } + // median filter on the deep shadows, to avoid boosting noise // because w2 >= w and h2 >= h, we can use the L buffer as temporary buffer for Median_Denoise() int w2 = find_fast_dim(w) + 1; @@ -1150,14 +1166,65 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa rescale_nearest(Yr, L, multiThread); - tmo_fattal02(w2, h2, L, L, alpha, beta, noise, detail_level, multiThread); + tmo_fattal02(w2, h2, L, L, alpha, beta, noise, detail_level, multiThread, 0); const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); - float newMedian; - findMinMaxPercentile (L.data(), static_cast(L.getRows()) * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); - const float scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + float offset = 0.f; + float scale = 65535.f; + + if (algo == 0) { + float newMedian; + findMinMaxPercentile(L.data(), static_cast(L.getRows()) * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); + scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + } else { + + scale = 65535.f; + { + float ratio = 0.f; + int ww, hh; + + if (w >= h) { + ratio = 200.f / w; + ww = 200; + hh = ratio * h; + } else { + ratio = 200.f / h; + hh = 200; + ww = ratio * w; + } + + Array2Df tmp(ww, hh); + int sz = ww * hh; + int idx = sz / 2; + int oidx = LIM(int(sz * 0.05f + 0.5f), 1, sz - 1); + rescale_nearest(Yr, tmp, multiThread); + std::sort(tmp.data(), tmp.data() + sz); + float oldMedian = tmp(idx); + float old_min = 0.f; + + for (int i = 0; i <= oidx; ++i) { + old_min += tmp(i); + } + + old_min /= oidx; + rescale_nearest(L, tmp, multiThread); + std::sort(tmp.data(), tmp.data() + sz); + float newMedian = tmp(idx); + scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + float new_min = 0.f; + + for (int i = 0; i <= oidx; ++i) { + new_min += tmp(i); + } + + new_min /= oidx; + offset = old_min - new_min; + } + + + } #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if(multiThread) @@ -1171,18 +1238,25 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa float Y = std::max(Yr(x, y), epsilon); float l = std::max(L(xx, yy), epsilon) * (scale / Y); - if(Lalone == 0) { - rgb->r(y, x) *= l; - rgb->g(y, x) *= l; - rgb->b(y, x) *= l; - assert(std::isfinite(rgb->r(y, x))); - assert(std::isfinite(rgb->g(y, x))); - assert(std::isfinite(rgb->b(y, x))); + if (Lalone == 0) { +// rgb->r(y, x) *= l; +// rgb->g(y, x) *= l; +// rgb->b(y, x) *= l; + float &r = rgb->r(y, x); + float &g = rgb->g(y, x); + float &b = rgb->b(y, x); + r = max(r * l - offset, 0.f); + g = max(g * l - offset, 0.f); + b = max(b * l - offset, 0.f); + + assert(std::isfinite(rgb->r(y, x))); + assert(std::isfinite(rgb->g(y, x))); + assert(std::isfinite(rgb->b(y, x))); } else { - if(Lalone == 1) { - Lum[y][x] *= (0.5f * l); - } else if(Lalone == -1){ + if (Lalone == 1) { + Lum[y][x] *= (0.5f * l - offset); + } else if (Lalone == -1) { Lum[y][x] *= (-0.5f * l); } } diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 8596b1f31..d4698d7c7 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -428,15 +428,15 @@ Locallab::Locallab(): lapmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), softradiusexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), -10.0, 1000.0, 0.5, 0.))), laplacexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPLACEXP"), 0.0, 100.0, 0.1, 0.))), - balanexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BALANEXP"), 0.2, 1.2, 0.01, 0.75))), + balanexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BALANEXP"), 0.5, 1.5, 0.01, 1.0))), linear(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LINEAR"), 0., 1., 0.01, 0.3))), gamm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMM"), 0.2, 1.3, 0.01, 0.4))), fatamount(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), fatdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATDETAIL"), -100., 300., 1., 0.))), - fatanchor(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + fatanchor(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 0.25, 2.5, 0.05, 1., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), strmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2., 2., 0.05, 0.))), angmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180., 180., 0.1, 0.))), - fatlevel(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATLEVEL"), 0, 3, 1, 2))), + fatlevel(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATLEVEL"), 0.25, 2.5, 0.05, 1.))), multipliersh( [this]() -> std::array { @@ -1618,9 +1618,9 @@ pe(nullptr) fatBox->pack_start(*fatamount); fatBox->pack_start(*fatdetail); - if (complexsoft < 2) { +// if (complexsoft < 2) { fatBox->pack_start(*fatlevel); - } +// } fatBox->pack_start(*fatanchor); pdeFrame->add(*pdeBox); @@ -5068,7 +5068,7 @@ void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited pp->locallab.spots.at(pp->locallab.selspot).fatamount = fatamount->getValue(); pp->locallab.spots.at(pp->locallab.selspot).fatdetail = fatdetail->getValue(); pp->locallab.spots.at(pp->locallab.selspot).fatanchor = fatanchor->getValue(); - pp->locallab.spots.at(pp->locallab.selspot).fatlevel = fatlevel->getIntValue(); + pp->locallab.spots.at(pp->locallab.selspot).fatlevel = fatlevel->getValue(); // Shadow highlight pp->locallab.spots.at(pp->locallab.selspot).expshadhigh = expshadhigh->getEnabled();