diff --git a/rtdata/languages/default b/rtdata/languages/default index 319a8689e..fd2f62ae3 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1002,6 +1002,7 @@ HISTORY_MSG_761;Local - SH Laplacian mask HISTORY_MSG_762;Local - cbdl Laplacian mask HISTORY_MSG_763;Local - Blur Laplacian mask HISTORY_MSG_764;Local - Solve PDE Laplacian mask +HISTORY_MSG_765;Local - deNoise Detail threshold HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction @@ -2182,11 +2183,12 @@ TP_LOCALLAB_NOISELUMFINE;Luminance fine 1 (Wav) TP_LOCALLAB_NOISELUMFINEZERO;Luminance fine 0 (Wav) TP_LOCALLAB_NOISELUMFINETWO;Luminance fine 2 (Wav) TP_LOCALLAB_NOISELUMCOARSE;Luminance coarse (Wav) -TP_LOCALLAB_NOISELUMDETAIL;Luminance detail (DCT) +TP_LOCALLAB_NOISELUMDETAIL;Luminance detail recovery(DCT) TP_LOCALLAB_NOISELEQUAL;Equalizer white-black TP_LOCALLAB_NOISECHROFINE;Chroma fine (Wav) TP_LOCALLAB_NOISECHROCOARSE;Chroma coarse (Wav) -TP_LOCALLAB_NOISECHRODETAIL;Chroma detail (DCT) +TP_LOCALLAB_NOISECHRODETAIL;Chroma detail recovery(DCT) +TP_LOCALLAB_DETAILTHR;Detail threshold Luminance Chroma (DCT) TP_LOCALLAB_NOISECHROC_TOOLTIP;If superior to zero, high quality algorithm is enabled.\nCoarse is for slider >=2 TP_LOCALLAB_PREVIEWSEL;Preview selection deltaE TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 216ad7c2d..070b01735 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -30,6 +30,8 @@ #include "color.h" #include "rt_math.h" #include "jaggedarray.h" +#include "rt_algo.h" + #ifdef _OPENMP #include #endif @@ -298,6 +300,7 @@ struct local_params { float noiself0; float noiself2; float noiseldetail; + int detailthr; int noiselequal; float noisechrodetail; float bilat; @@ -593,6 +596,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall 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; @@ -832,6 +836,7 @@ static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locall 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; @@ -2574,7 +2579,7 @@ void ImProcFunctions::rex_poisson_dct(float * data, size_t nx, size_t ny, double * cosx[i] = cos(i Pi / nx) for i in [0..nx[ * cosy[i] = cos(i Pi / ny) for i in [0..ny[ */ - + cosx = cos_table(nx); cosy = cos_table(ny); @@ -5256,6 +5261,7 @@ void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_nu 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}; @@ -5393,7 +5399,7 @@ void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_nu //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // now process the vblk row of blocks for noise reduction - float params_Ldetail = 0.f; +// float params_Ldetail = 0.f; float noisevar_Ldetail = 1.f; if (chrom == 0) { @@ -5434,6 +5440,34 @@ void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_nu //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% } + //Threshold DCT from Alberto Grigio + const int detail_thresh = lp.detailthr; + array2D mask; + float scalea = 1.f; + if (detail_thresh > 0) { + mask(GW, GH); + float thr = log2lin(float(detail_thresh) / 200.f, 100.f); + buildBlendMask(tmp1, mask, GW, GH, thr); + float r = 20.f / scalea; + + if (r > 0) { + float **m = mask; + gaussianBlur(m, m, GW, GH, r); + } + + array2D m2(GW, GH); + const float alfa = 0.856f; + const float beta = 1.f + std::sqrt(log2lin(thr, 100.f)); + buildGradientsMask(GW, GH, tmp1, 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 @@ -5442,11 +5476,20 @@ void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_nu 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] += Ldetail[i][j] / totwt[i][j]; //note that labdn initially stores the denoised hipass data + tmp1[i][j] += d; } } + mask.free(); +//end Threshold DCT + delete Lin; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index cc9dc8701..db7bc57fb 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -791,6 +791,7 @@ enum ProcEventCode { Evlocallablapmaskcb = 761, Evlocallablapmaskbl = 762, Evlocallablaplac = 763, + Evlocallabdetailthr = 764, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index eca46aee4..eb2adef92 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2670,14 +2670,15 @@ LocallabParams::LocallabSpot::LocallabSpot() : noiselumf0(0), noiselumf2(0), noiselumc(0), - noiselumdetail(80), + noiselumdetail(0), noiselequal(7), noisechrof(0), noisechroc(0), - noisechrodetail(80), + noisechrodetail(0), adjblur(0), bilateral(0), - sensiden(20) + sensiden(20), + detailthr(0) { } @@ -2963,7 +2964,8 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && noisechrodetail == other.noisechrodetail && adjblur == other.adjblur && bilateral == other.bilateral - && sensiden == other.sensiden; + && sensiden == other.sensiden + && detailthr == other.detailthr; } bool LocallabParams::LocallabSpot::operator !=(const LocallabSpot& other) const @@ -4222,6 +4224,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->locallab.spots.at(i).adjblur, "Locallab", "Adjblur_" + std::to_string(i), spot.adjblur, keyFile); saveToKeyfile(!pedited || pedited->locallab.spots.at(i).bilateral, "Locallab", "Bilateral_" + std::to_string(i), spot.bilateral, keyFile); saveToKeyfile(!pedited || pedited->locallab.spots.at(i).sensiden, "Locallab", "Sensiden_" + std::to_string(i), spot.sensiden, keyFile); + saveToKeyfile(!pedited || pedited->locallab.spots.at(i).detailthr, "Locallab", "Detailthr_" + std::to_string(i), spot.detailthr, keyFile); } } @@ -5612,6 +5615,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Adjblur_" + std::to_string(i), pedited, spot.adjblur, spotEdited.adjblur); assignFromKeyfile(keyFile, "Locallab", "Bilateral_" + std::to_string(i), pedited, spot.bilateral, spotEdited.bilateral); assignFromKeyfile(keyFile, "Locallab", "Sensiden_" + std::to_string(i), pedited, spot.sensiden, spotEdited.sensiden); + assignFromKeyfile(keyFile, "Locallab", "Detailthr_" + std::to_string(i), pedited, spot.detailthr, spotEdited.detailthr); locallab.spots.at(i) = spot; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 71c3aeda9..ce7fd008c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1227,6 +1227,7 @@ struct LocallabParams { int adjblur; int bilateral; int sensiden; + int detailthr; LocallabSpot(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 8b49823c4..1a1bf1008 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -790,7 +790,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, //EvlocallablapmaskSH LUMINANCECURVE, //Evlocallablapmaskcb LUMINANCECURVE, //Evlocallablapmaskbl - LUMINANCECURVE //Evlocallablaplac + LUMINANCECURVE, //Evlocallablaplac + LUMINANCECURVE //Evlocallabdetailthr }; namespace rtengine diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index 5485346c7..46a1aaa44 100644 --- a/rtengine/rt_algo.h +++ b/rtengine/rt_algo.h @@ -25,4 +25,8 @@ namespace rtengine { void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multiThread = true); void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false, float ** clipmask = nullptr); +// implemented in tmo_fattal02 +void buildGradientsMask(int W, int H, float **luminance, float **out, + float amount, int nlevels, int detail_level, + float alfa, float beta, bool multithread); } diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index db557ddf9..13082354e 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -96,60 +96,62 @@ class Array2Df: public array2D typedef array2D Super; public: Array2Df(): Super() {} - Array2Df (int w, int h): Super (w, h) {} + Array2Df(int w, int h): Super(w, h) {} + Array2Df(int w, int h, float **data): + Super(w, h, data, ARRAY2D_BYREFERENCE) {} - float &operator() (int w, int h) + float &operator()(int w, int h) { return (*this)[h][w]; } - const float &operator() (int w, int h) const + const float &operator()(int w, int h) const { return (*this)[h][w]; } - float &operator() (int i) + float &operator()(int i) { - return static_cast (*this)[i]; + return static_cast(*this)[i]; } - const float &operator() (int i) const + const float &operator()(int i) const { - return const_cast (*this).operator() (i); + return const_cast(*this).operator()(i); } int getRows() const { - return const_cast (*this).height(); + return const_cast(*this).height(); } int getCols() const { - return const_cast (*this).width(); + return const_cast(*this).width(); } float *data() { - return static_cast (*this); + return static_cast(*this); } const float *data() const { - return const_cast (*this).data(); + return const_cast(*this).data(); } }; // upper bound on image dimension used in tmo_fattal02 -- see the comment there const int RT_dimension_cap = 1920; -void rescale_bilinear (const Array2Df &src, Array2Df &dst, bool multithread); +void rescale_bilinear(const Array2Df &src, Array2Df &dst, bool multithread); /****************************************************************************** * Luminance HDR code (modifications are marked with an RT comment) ******************************************************************************/ -void downSample (const Array2Df& A, Array2Df& B) +void downSample(const Array2Df& A, Array2Df& B) { const int width = B.getCols(); const int height = B.getRows(); @@ -159,18 +161,18 @@ void downSample (const Array2Df& A, Array2Df& B) // speed improvements. The main issue is the pde solver and in case of the // fft solver uses optimised threaded fftw routines. //#pragma omp parallel for - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 0 ; x < width ; x++ ) { - float p = A (2 * x, 2 * y); - p += A (2 * x + 1, 2 * y); - p += A (2 * x, 2 * y + 1); - p += A (2 * x + 1, 2 * y + 1); - B (x, y) = p * 0.25f; // p / 4.0f; + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { + float p = A(2 * x, 2 * y); + p += A(2 * x + 1, 2 * y); + p += A(2 * x, 2 * y + 1); + p += A(2 * x + 1, 2 * y + 1); + B(x, y) = p * 0.25f; // p / 4.0f; } } } -void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) +void gaussianBlur(const Array2Df& I, Array2Df& L, bool multithread) { const int width = I.getCols(); const int height = I.getRows(); @@ -178,30 +180,30 @@ void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) if (width < 3 || height < 3) { if (&I != &L) { for (int i = 0, n = width * height; i < n; ++i) { - L (i) = I (i); + L(i) = I(i); } } return; } - Array2Df T (width, height); + Array2Df T(width, height); //--- X blur #ifdef _OPENMP #pragma omp parallel for shared(I, T) if(multithread) #endif - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 1 ; x < width - 1 ; x++ ) { - float t = 2.f * I (x, y); - t += I (x - 1, y); - t += I (x + 1, y); - T (x, y) = t * 0.25f; // t / 4.f; + for (int y = 0 ; y < height ; y++) { + for (int x = 1 ; x < width - 1 ; x++) { + float t = 2.f * I(x, y); + t += I(x - 1, y); + t += I(x + 1, y); + T(x, y) = t * 0.25f; // t / 4.f; } - T (0, y) = ( 3.f * I (0, y) + I (1, y) ) * 0.25f; // / 4.f; - T (width - 1, y) = ( 3.f * I (width - 1, y) + I (width - 2, y) ) * 0.25f; // / 4.f; + T(0, y) = (3.f * I(0, y) + I(1, y)) * 0.25f; // / 4.f; + T(width - 1, y) = (3.f * I(width - 1, y) + I(width - 2, y)) * 0.25f; // / 4.f; } //--- Y blur @@ -209,66 +211,66 @@ void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) #pragma omp parallel for if(multithread) #endif - for ( int x = 0 ; x < width - 7 ; x += 8 ) { - for ( int y = 1 ; y < height - 1 ; y++ ) { + for (int x = 0 ; x < width - 7 ; x += 8) { + for (int y = 1 ; y < height - 1 ; y++) { for (int xx = 0; xx < 8; ++xx) { - float t = 2.f * T (x + xx, y); - t += T (x + xx, y - 1); - t += T (x + xx, y + 1); - L (x + xx, y) = t * 0.25f; // t/4.0f; + float t = 2.f * T(x + xx, y); + t += T(x + xx, y - 1); + t += T(x + xx, y + 1); + L(x + xx, y) = t * 0.25f; // t/4.0f; } } for (int xx = 0; xx < 8; ++xx) { - L (x + xx, 0) = ( 3.f * T (x + xx, 0) + T (x + xx, 1) ) * 0.25f; // / 4.0f; - L (x + xx, height - 1) = ( 3.f * T (x + xx, height - 1) + T (x + xx, height - 2) ) * 0.25f; // / 4.0f; + L(x + xx, 0) = (3.f * T(x + xx, 0) + T(x + xx, 1)) * 0.25f; // / 4.0f; + L(x + xx, height - 1) = (3.f * T(x + xx, height - 1) + T(x + xx, height - 2)) * 0.25f; // / 4.0f; } } - for ( int x = width - (width % 8) ; x < width ; x++ ) { - for ( int y = 1 ; y < height - 1 ; y++ ) { - float t = 2.f * T (x, y); - t += T (x, y - 1); - t += T (x, y + 1); - L (x, y) = t * 0.25f; // t/4.0f; + for (int x = width - (width % 8) ; x < width ; x++) { + for (int y = 1 ; y < height - 1 ; y++) { + float t = 2.f * T(x, y); + t += T(x, y - 1); + t += T(x, y + 1); + L(x, y) = t * 0.25f; // t/4.0f; } - L (x, 0) = ( 3.f * T (x, 0) + T (x, 1) ) * 0.25f; // / 4.0f; - L (x, height - 1) = ( 3.f * T (x, height - 1) + T (x, height - 2) ) * 0.25f; // / 4.0f; + L(x, 0) = (3.f * T(x, 0) + T(x, 1)) * 0.25f; // / 4.0f; + L(x, height - 1) = (3.f * T(x, height - 1) + T(x, height - 2)) * 0.25f; // / 4.0f; } } -void createGaussianPyramids (Array2Df** pyramids, int nlevels, bool multithread) +void createGaussianPyramids(Array2Df** pyramids, int nlevels, bool multithread) { // pyramids[0] is already set int width = pyramids[0]->getCols(); int height = pyramids[0]->getRows(); - Array2Df* L = new Array2Df (width, height); - gaussianBlur ( *pyramids[0], *L, multithread ); + Array2Df* L = new Array2Df(width, height); + gaussianBlur(*pyramids[0], *L, multithread); - for ( int k = 1 ; k < nlevels ; k++ ) { + for (int k = 1 ; k < nlevels ; k++) { if (width > 2 && height > 2) { width /= 2; height /= 2; - pyramids[k] = new Array2Df (width, height); - downSample (*L, *pyramids[k]); + pyramids[k] = new Array2Df(width, height); + downSample(*L, *pyramids[k]); } else { // RT - now nlevels is fixed in tmo_fattal02 (see the comment in // there), so it might happen that we have to add some padding to // the gaussian pyramids - pyramids[k] = new Array2Df (width, height); + pyramids[k] = new Array2Df(width, height); for (int j = 0, n = width * height; j < n; ++j) { - (*pyramids[k]) (j) = (*L) (j); + (*pyramids[k])(j) = (*L)(j); } } if (k < nlevels - 1) { delete L; - L = new Array2Df (width, height); - gaussianBlur ( *pyramids[k], *L, multithread ); + L = new Array2Df(width, height); + gaussianBlur(*pyramids[k], *L, multithread); } } @@ -277,37 +279,37 @@ void createGaussianPyramids (Array2Df** pyramids, int nlevels, bool multithread) //-------------------------------------------------------------------- -float calculateGradients (Array2Df* H, Array2Df* G, int k, bool multithread) +float calculateGradients(Array2Df* H, Array2Df* G, int k, bool multithread) { const int width = H->getCols(); const int height = H->getRows(); - const float divider = pow ( 2.0f, k + 1 ); + const float divider = pow(2.0f, k + 1); double avgGrad = 0.0; // use double precision for large summations #ifdef _OPENMP #pragma omp parallel for reduction(+:avgGrad) if(multithread) #endif - for ( int y = 0 ; y < height ; y++ ) { + for (int y = 0 ; y < height ; y++) { int n = (y == 0 ? 0 : y - 1); int s = (y + 1 == height ? y : y + 1); - for ( int x = 0 ; x < width ; x++ ) { + for (int x = 0 ; x < width ; x++) { float gx, gy; int w, e; w = (x == 0 ? 0 : x - 1); e = (x + 1 == width ? x : x + 1); - gx = ((*H) (w, y) - (*H) (e, y)); + gx = ((*H)(w, y) - (*H)(e, y)); - gy = ((*H) (x, s) - (*H) (x, n)); + gy = ((*H)(x, s) - (*H)(x, n)); // note this implicitly assumes that H(-1)=H(0) // for the fft-pde slover this would need adjustment as H(-1)=H(1) // is assumed, which means gx=0.0, gy=0.0 at the boundaries // however, the impact is not visible so we ignore this here - (*G) (x, y) = sqrt (gx * gx + gy * gy) / divider; - avgGrad += (*G) (x, y); + (*G)(x, y) = sqrt(gx * gx + gy * gy) / divider; + avgGrad += (*G)(x, y); } } @@ -316,7 +318,7 @@ float calculateGradients (Array2Df* H, Array2Df* G, int k, bool multithread) //-------------------------------------------------------------------- -void upSample (const Array2Df& A, Array2Df& B) +void upSample(const Array2Df& A, Array2Df& B) { const int width = B.getCols(); const int height = B.getRows(); @@ -324,14 +326,14 @@ void upSample (const Array2Df& A, Array2Df& B) const int aheight = A.getRows(); //#pragma omp parallel for shared(A, B) - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 0 ; x < width ; x++ ) { - int ax = static_cast (x * 0.5f); //x / 2.f; - int ay = static_cast (y * 0.5f); //y / 2.f; + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { + int ax = static_cast(x * 0.5f); //x / 2.f; + int ay = static_cast(y * 0.5f); //y / 2.f; ax = (ax < awidth) ? ax : awidth - 1; ay = (ay < aheight) ? ay : aheight - 1; - B (x, y) = A (ax, ay); + B(x, y) = A(ax, ay); } } @@ -351,24 +353,25 @@ void upSample (const Array2Df& A, Array2Df& B) } -void calculateFiMatrix (Array2Df* FI, Array2Df* gradients[], - float avgGrad[], int nlevels, int detail_level, - float alfa, float beta, float noise, bool multithread) +void calculateFiMatrix(Array2Df* FI, Array2Df* gradients[], + float avgGrad[], int nlevels, int detail_level, + float alfa, float beta, float noise, bool multithread) { int width = gradients[nlevels - 1]->getCols(); int height = gradients[nlevels - 1]->getRows(); Array2Df** fi = new Array2Df*[nlevels]; - fi[nlevels - 1] = new Array2Df (width, height); + fi[nlevels - 1] = new Array2Df(width, height); #ifdef _OPENMP #pragma omp parallel for shared(fi) if(multithread) #endif - for ( int k = 0 ; k < width * height ; k++ ) { - (*fi[nlevels - 1]) (k) = 1.0f; + + for (int k = 0 ; k < width * height ; k++) { + (*fi[nlevels - 1])(k) = 1.0f; } - for ( int k = nlevels - 1; k >= 0 ; k-- ) { + for (int k = nlevels - 1; k >= 0 ; k--) { width = gradients[k]->getCols(); height = gradients[k]->getRows(); @@ -378,50 +381,51 @@ void calculateFiMatrix (Array2Df* FI, Array2Df* gradients[], #ifdef _OPENMP #pragma omp parallel for shared(fi,avgGrad) if(multithread) #endif - for ( int y = 0; y < height; y++ ) { - for ( int x = 0; x < width; x++ ) { - float grad = ((*gradients[k]) (x, y) < 1e-4f) ? 1e-4 : (*gradients[k]) (x, y); - float a = alfa * avgGrad[k]; - float value = pow ((grad + noise) / a, beta - 1.0f); - (*fi[k]) (x, y) *= value; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float grad = ((*gradients[k])(x, y) < 1e-4f) ? 1e-4 : (*gradients[k])(x, y); + float a = alfa * avgGrad[k]; + float value = pow((grad + noise) / a, beta - 1.0f); + + (*fi[k])(x, y) *= value; } } } // create next level - if ( k > 1 ) { + if (k > 1) { width = gradients[k - 1]->getCols(); height = gradients[k - 1]->getRows(); - fi[k - 1] = new Array2Df (width, height); + fi[k - 1] = new Array2Df(width, height); } else { fi[0] = FI; // highest level -> result } if (k > 0) { - upSample (*fi[k], *fi[k - 1]); // upsample to next level - gaussianBlur (*fi[k - 1], *fi[k - 1], multithread); + upSample(*fi[k], *fi[k - 1]); // upsample to next level + gaussianBlur(*fi[k - 1], *fi[k - 1], multithread); } } - for ( int k = 1 ; k < nlevels ; k++ ) { + for (int k = 1 ; k < nlevels ; k++) { delete fi[k]; } 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); -void tmo_fattal02 (size_t width, - size_t height, - const Array2Df& Y, - Array2Df& L, - float alfa, - float beta, - float noise, - int detail_level, - bool multithread) +void tmo_fattal02(size_t width, + size_t height, + const Array2Df& Y, + Array2Df& L, + float alfa, + float beta, + float noise, + int detail_level, + bool multithread) { // #ifdef TIMER_PROFILING // msec_timer stop_watch; @@ -432,11 +436,11 @@ void tmo_fattal02 (size_t width, static const float gamma = 1.0f; // 0.8f; // static const int detail_level = 3; - if ( detail_level < 0 ) { + if (detail_level < 0) { detail_level = 0; } - if ( detail_level > 3 ) { + if (detail_level > 3) { detail_level = 3; } @@ -464,17 +468,17 @@ void tmo_fattal02 (size_t width, // find max value, normalize to range 0..100 and take logarithm // float minLum = Y (0, 0); - float maxLum = Y (0, 0); + float maxLum = Y(0, 0); #ifdef _OPENMP #pragma omp parallel for reduction(max:maxLum) if(multithread) #endif - for ( int i = 0 ; i < size ; i++ ) { - maxLum = std::max (maxLum, Y (i)); + for (int i = 0 ; i < size ; i++) { + maxLum = std::max(maxLum, Y(i)); } - Array2Df* H = new Array2Df (width, height); + Array2Df* H = new Array2Df(width, height); float temp = 100.f / maxLum; #ifdef _OPENMP #pragma omp parallel if(multithread) @@ -494,13 +498,13 @@ void tmo_fattal02 (size_t width, #ifdef __SSE2__ for (; j < width - 3; j += 4) { - STVFU ((*H)[i][j], xlogf (tempv * LVFU (Y[i][j]) + epsv)); + STVFU((*H)[i][j], xlogf(tempv * LVFU(Y[i][j]) + epsv)); } #endif for (; j < width; ++j) { - (*H)[i][j] = xlogf (temp * Y[i][j] + eps); + (*H)[i][j] = xlogf(temp * Y[i][j] + eps); } } } @@ -527,13 +531,13 @@ void tmo_fattal02 (size_t width, * improved */ int fullwidth = width; int fullheight = height; - int dim = std::max (width, height); + int dim = std::max(width, height); Array2Df *fullH = nullptr; if (dim > RT_dimension_cap) { float s = float (RT_dimension_cap) / float (dim); - Array2Df *HH = new Array2Df (width * s, height * s); - rescale_bilinear (*H, *HH, multithread); + Array2Df *HH = new Array2Df(width * s, height * s); + rescale_bilinear(*H, *HH, multithread); fullH = H; H = HH; width = H->getCols(); @@ -546,25 +550,27 @@ void tmo_fattal02 (size_t width, Array2Df* pyramids[nlevels]; pyramids[0] = H; - createGaussianPyramids (pyramids, nlevels, multithread); + createGaussianPyramids(pyramids, nlevels, multithread); // calculate gradients and its average values on pyramid levels Array2Df* gradients[nlevels]; float avgGrad[nlevels]; - for ( int k = 0 ; k < nlevels ; k++ ) { - gradients[k] = new Array2Df (pyramids[k]->getCols(), pyramids[k]->getRows()); - avgGrad[k] = calculateGradients (pyramids[k], gradients[k], k, multithread); - if(k != 0) // pyramids[0] is H. Will be deleted later + for (int k = 0 ; k < nlevels ; k++) { + gradients[k] = new Array2Df(pyramids[k]->getCols(), pyramids[k]->getRows()); + avgGrad[k] = calculateGradients(pyramids[k], gradients[k], k, multithread); + + if (k != 0) { // pyramids[0] is H. Will be deleted later delete pyramids[k]; + } } // calculate fi matrix - Array2Df* FI = new Array2Df (width, height); - calculateFiMatrix (FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); + Array2Df* FI = new Array2Df(width, height); + calculateFiMatrix(FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); - for ( int i = 0 ; i < nlevels ; i++ ) { + for (int i = 0 ; i < nlevels ; i++) { delete gradients[i]; } @@ -572,8 +578,8 @@ void tmo_fattal02 (size_t width, if (fullH) { delete H; H = fullH; - Array2Df *FI2 = new Array2Df (fullwidth, fullheight); - rescale_bilinear (*FI, *FI2, multithread); + Array2Df *FI2 = new Array2Df(fullwidth, fullheight); + rescale_bilinear(*FI, *FI2, multithread); delete FI; FI = FI2; width = fullwidth; @@ -583,7 +589,7 @@ void tmo_fattal02 (size_t width, /** RT */ // attenuate gradients - Array2Df* Gx = new Array2Df (width, height); + Array2Df* Gx = new Array2Df(width, height); Array2Df* Gy = &L; // use L as buffer for Gy // the fft solver solves the Poisson pde but with slightly different @@ -594,16 +600,16 @@ void tmo_fattal02 (size_t width, #pragma omp parallel for if(multithread) #endif - for ( size_t y = 0 ; y < height ; y++ ) { + for (size_t y = 0 ; y < height ; y++) { // sets index+1 based on the boundary assumption H(N+1)=H(N-1) unsigned int yp1 = (y + 1 >= height ? height - 2 : y + 1); - for ( size_t x = 0 ; x < width ; x++ ) { + for (size_t x = 0 ; x < width ; x++) { // sets index+1 based on the boundary assumption H(N+1)=H(N-1) unsigned int xp1 = (x + 1 >= width ? width - 2 : x + 1); // forward differences in H, so need to use between-points approx of FI - (*Gx) (x, y) = ((*H) (xp1, y) - (*H) (x, y)) * 0.5 * ((*FI) (xp1, y) + (*FI) (x, y)); - (*Gy) (x, y) = ((*H) (x, yp1) - (*H) (x, y)) * 0.5 * ((*FI) (x, yp1) + (*FI) (x, y)); + (*Gx)(x, y) = ((*H)(xp1, y) - (*H)(x, y)) * 0.5 * ((*FI)(xp1, y) + (*FI)(x, y)); + (*Gy)(x, y) = ((*H)(x, yp1) - (*H)(x, y)) * 0.5 * ((*FI)(x, yp1) + (*FI)(x, y)); } } @@ -614,24 +620,24 @@ void tmo_fattal02 (size_t width, #pragma omp parallel for if(multithread) #endif - for ( size_t y = 0; y < height; ++y ) { - for ( size_t x = 0; x < width; ++x ) { - (*FI) (x, y) = (*Gx) (x, y) + (*Gy) (x, y); + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + (*FI)(x, y) = (*Gx)(x, y) + (*Gy)(x, y); - if ( x > 0 ) { - (*FI) (x, y) -= (*Gx) (x - 1, y); + if (x > 0) { + (*FI)(x, y) -= (*Gx)(x - 1, y); } - if ( y > 0 ) { - (*FI) (x, y) -= (*Gy) (x, y - 1); + if (y > 0) { + (*FI)(x, y) -= (*Gy)(x, y - 1); } if (x == 0) { - (*FI) (x, y) += (*Gx) (x, y); + (*FI)(x, y) += (*Gx)(x, y); } if (y == 0) { - (*FI) (x, y) += (*Gy) (x, y); + (*FI)(x, y) += (*Gy)(x, y); } } @@ -641,8 +647,8 @@ 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); + MyMutex::MyLock lock(*fftwMutex); + solve_pde_fft(FI, &L, Gx, multithread); } delete Gx; delete FI; @@ -652,7 +658,7 @@ void tmo_fattal02 (size_t width, #endif { #ifdef __SSE2__ - vfloat gammav = F2V (gamma); + vfloat gammav = F2V(gamma); #endif #ifdef _OPENMP #pragma omp for schedule(dynamic,16) @@ -663,13 +669,13 @@ void tmo_fattal02 (size_t width, #ifdef __SSE2__ for (; j < width - 3; j += 4) { - STVFU (L[i][j], xexpf (gammav * LVFU (L[i][j]))); + STVFU(L[i][j], xexpf(gammav * LVFU(L[i][j]))); } #endif for (; j < width; j++) { - L[i][j] = xexpf ( gamma * L[i][j]); + L[i][j] = xexpf(gamma * L[i][j]); } } } @@ -723,11 +729,11 @@ void tmo_fattal02 (size_t width, // returns T = EVy A EVx^tr // note, modifies input data -void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) +void transform_ev2normal(Array2Df *A, Array2Df *T, bool multithread) { int width = A->getCols(); int height = A->getRows(); - assert ((int)T->getCols() == width && (int)T->getRows() == height); + assert((int)T->getCols() == width && (int)T->getRows() == height); // the discrete cosine transform is not exactly the transform needed // need to scale input values to get the right transformation @@ -735,19 +741,19 @@ void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) #pragma omp parallel for if(multithread) #endif - for (int y = 1 ; y < height - 1 ; y++ ) - for (int x = 1 ; x < width - 1 ; x++ ) { - (*A) (x, y) *= 0.25f; + for (int y = 1 ; y < height - 1 ; y++) + for (int x = 1 ; x < width - 1 ; x++) { + (*A)(x, y) *= 0.25f; } - for (int x = 1 ; x < width - 1 ; x++ ) { - (*A) (x, 0) *= 0.5f; - (*A) (x, height - 1) *= 0.5f; + for (int x = 1 ; x < width - 1 ; x++) { + (*A)(x, 0) *= 0.5f; + (*A)(x, height - 1) *= 0.5f; } - for (int y = 1 ; y < height - 1 ; y++ ) { - (*A) (0, y) *= 0.5; - (*A) (width - 1, y) *= 0.5f; + for (int y = 1 ; y < height - 1 ; y++) { + (*A)(0, y) *= 0.5; + (*A)(width - 1, y) *= 0.5f; } // note, fftw provides its own memory allocation routines which @@ -761,26 +767,26 @@ void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) // executes 2d discrete cosine transform fftwf_plan p; - p = fftwf_plan_r2r_2d (height, width, A->data(), T->data(), - FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); - fftwf_execute (p); - fftwf_destroy_plan (p); + p = fftwf_plan_r2r_2d(height, width, A->data(), T->data(), + FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); + fftwf_execute(p); + fftwf_destroy_plan(p); } // returns T = EVy^-1 * A * (EVx^-1)^tr -void transform_normal2ev (Array2Df *A, Array2Df *T, bool multithread) +void transform_normal2ev(Array2Df *A, Array2Df *T, bool multithread) { int width = A->getCols(); int height = A->getRows(); - assert ((int)T->getCols() == width && (int)T->getRows() == height); + assert((int)T->getCols() == width && (int)T->getRows() == height); // executes 2d discrete cosine transform fftwf_plan p; - p = fftwf_plan_r2r_2d (height, width, A->data(), T->data(), - FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); - fftwf_execute (p); - fftwf_destroy_plan (p); + p = fftwf_plan_r2r_2d(height, width, A->data(), T->data(), + FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); + fftwf_execute(p); + fftwf_destroy_plan(p); // need to scale the output matrix to get the right transform float factor = (1.0f / ((height - 1) * (width - 1))); @@ -788,30 +794,30 @@ void transform_normal2ev (Array2Df *A, Array2Df *T, bool multithread) #pragma omp parallel for if(multithread) #endif - for (int y = 0 ; y < height ; y++ ) - for (int x = 0 ; x < width ; x++ ) { - (*T) (x, y) *= factor; + for (int y = 0 ; y < height ; y++) + for (int x = 0 ; x < width ; x++) { + (*T)(x, y) *= factor; } - for (int x = 0 ; x < width ; x++ ) { - (*T) (x, 0) *= 0.5f; - (*T) (x, height - 1) *= 0.5f; + for (int x = 0 ; x < width ; x++) { + (*T)(x, 0) *= 0.5f; + (*T)(x, height - 1) *= 0.5f; } - for (int y = 0 ; y < height ; y++ ) { - (*T) (0, y) *= 0.5f; - (*T) (width - 1, y) *= 0.5f; + for (int y = 0 ; y < height ; y++) { + (*T)(0, y) *= 0.5f; + (*T)(width - 1, y) *= 0.5f; } } // returns the eigenvalues of the 1d laplace operator -std::vector get_lambda (int n) +std::vector get_lambda(int n) { - assert (n > 1); - std::vector v (n); + assert(n > 1); + std::vector v(n); for (int i = 0; i < n; i++) { - v[i] = -4.0 * SQR (sin ((double)i / (2 * (n - 1)) * RT_PI)); + v[i] = -4.0 * SQR(sin((double)i / (2 * (n - 1)) * RT_PI)); } return v; @@ -861,22 +867,22 @@ 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)/*, pfs::Progress &ph, bool adjust_bound)*/ { // ph.setValue(20); //DEBUG_STR << "solve_pde_fft: solving Laplace U = F ..." << std::endl; int width = F->getCols(); int height = F->getRows(); - assert ((int)U->getCols() == width && (int)U->getRows() == height); - assert (buf->getCols() == width && buf->getRows() == height); + assert((int)U->getCols() == width && (int)U->getRows() == height); + assert(buf->getCols() == width && buf->getRows() == height); // activate parallel execution of fft routines #ifdef RT_FFTW3F_OMP if (multithread) { fftwf_init_threads(); - fftwf_plan_with_nthreads ( omp_get_max_threads() ); + fftwf_plan_with_nthreads(omp_get_max_threads()); } // #else @@ -896,29 +902,29 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* // transforms F into eigenvector space: Ftr = //DEBUG_STR << "solve_pde_fft: transform F to ev space (fft)" << std::endl; Array2Df* F_tr = buf; - transform_normal2ev (F, F_tr, multithread); + transform_normal2ev(F, F_tr, multithread); // TODO: F no longer needed so could release memory, but as it is an // input parameter we won't do that // in the eigenvector space the solution is very simple - std::vector l1 = get_lambda (height); - std::vector l2 = get_lambda (width); + std::vector l1 = get_lambda(height); + std::vector l2 = get_lambda(width); #ifdef _OPENMP #pragma omp parallel for if(multithread) #endif - for (int y = 0 ; y < height ; y++ ) { - for (int x = 0 ; x < width ; x++ ) { - (*F_tr) (x, y) = (*F_tr) (x, y) / (l1[y] + l2[x]); + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { + (*F_tr)(x, y) = (*F_tr)(x, y) / (l1[y] + l2[x]); } } - (*F_tr) (0, 0) = 0.f; // any value ok, only adds a const to the solution + (*F_tr)(0, 0) = 0.f; // any value ok, only adds a const to the solution // transforms F_tr back to the normal space - transform_ev2normal (F_tr, U, multithread); + transform_ev2normal(F_tr, U, multithread); // the solution U as calculated will satisfy something like int U = 0 // since for any constant c, U-c is also a solution and we are mainly @@ -940,7 +946,7 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* #endif for (int i = 0; i < width * height; i++) { - (*U) (i) -= maxVal; + (*U)(i) -= maxVal; } } @@ -974,27 +980,27 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* * RT code from here on *****************************************************************************/ -inline void rescale_bilinear (const Array2Df &src, Array2Df &dst, bool multithread) +inline void rescale_bilinear(const Array2Df &src, Array2Df &dst, bool multithread) { rescaleBilinear(src, dst, multithread); } -inline void rescale_nearest (const Array2Df &src, Array2Df &dst, bool multithread) +inline void rescale_nearest(const Array2Df &src, Array2Df &dst, bool multithread) { rescaleNearest(src, dst, multithread); } -inline float luminance (float r, float g, float b, TMatrix ws) +inline float luminance(float r, float g, float b, TMatrix ws) { return Color::rgbLuminance(r, g, b, ws); } -inline int round_up_pow2 (int dim) +inline int round_up_pow2(int dim) { // from https://graphics.stanford.edu/~seander/bithacks.html - assert (dim > 0); + assert(dim > 0); unsigned int v = dim; v--; v |= v >> 1; @@ -1006,7 +1012,7 @@ inline int round_up_pow2 (int dim) return v; } -inline int find_fast_dim (int dim) +inline int find_fast_dim(int dim) { // as per the FFTW docs: // @@ -1018,7 +1024,7 @@ inline int find_fast_dim (int dim) // the above form. This is not exhaustive, but should be ok for pictures // up to 100MPix at least - int d1 = round_up_pow2 (dim); + int d1 = round_up_pow2(dim); std::vector d = { d1 / 128 * 65, d1 / 64 * 33, @@ -1040,18 +1046,21 @@ inline int find_fast_dim (int dim) } } - assert (false); + assert(false); return dim; } + + } // namespace -void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level) +void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level) { if (!fatParams.enabled) { return; } + BENCHFUN // const int detail_level = 3; @@ -1073,38 +1082,39 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb, const FattalToneMappingP int w = rgb->getWidth(); int h = rgb->getHeight(); - Array2Df Yr (w, h); + Array2Df Yr(w, h); constexpr float epsilon = 1e-4f; constexpr float luminance_noise_floor = 65.535f; constexpr float min_luminance = 1.f; - TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix (params->icm.workingProfile); + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); #ifdef _OPENMP #pragma omp parallel for if(multiThread) #endif + for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - 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 + 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 } } float oldMedian; const float percentile = float(LIM(fatParams.anchor, 1, 100)) / 100.f; - findMinMaxPercentile (Yr.data(), Yr.getRows() * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); + findMinMaxPercentile(Yr.data(), 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; - int h2 = find_fast_dim (h) + 1; - Array2Df L (w2, h2); + int w2 = find_fast_dim(w) + 1; + int h2 = find_fast_dim(h) + 1; + Array2Df L(w2, h2); { #ifdef _OPENMP int num_threads = multiThread ? omp_get_max_threads() : 1; #else int num_threads = 1; #endif - float r = float (std::max (w, h)) / float (RT_dimension_cap); + float r = float (std::max(w, h)) / float (RT_dimension_cap); Median med; if (r >= 3) { @@ -1117,7 +1127,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb, const FattalToneMappingP med = Median::TYPE_3X3_STRONG; } - Median_Denoise (Yr, Yr, luminance_noise_floor, w, h, med, 1, num_threads, L); + Median_Denoise(Yr, Yr, luminance_noise_floor, w, h, med, 1, num_threads, L); } float noise = alpha * 0.01f; @@ -1126,20 +1136,22 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb, const FattalToneMappingP std::cout << "ToneMapFattal02: alpha = " << alpha << ", beta = " << beta << ", detail_level = " << detail_level << std::endl; } - rescale_nearest (Yr, L, multiThread); - - tmo_fattal02 (w2, h2, L, L, alpha, beta, noise, detail_level, multiThread); + + rescale_nearest(Yr, L, multiThread); + + tmo_fattal02(w2, h2, L, L, alpha, beta, noise, detail_level, multiThread); const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); float newMedian; - findMinMaxPercentile (L.data(), L.getRows() * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); + findMinMaxPercentile(L.data(), L.getRows() * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); const float scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if(multiThread) #endif + for (int y = 0; y < h; y++) { int yy = y * hr + 1; @@ -1157,8 +1169,80 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb, const FattalToneMappingP assert(std::isfinite(rgb->b(y, x))); } } - + } +void buildGradientsMask(int W, int H, float **luminance, float **out, + float amount, int nlevels, int detail_level, + float alfa, float beta, bool multithread) +{ + Array2Df Y(W, H, luminance); + const float noise = alfa * 0.01f; + + Array2Df *pyramids[nlevels]; + pyramids[0] = &Y; + createGaussianPyramids(pyramids, nlevels, multithread); + + // calculate gradients and its average values on pyramid levels + Array2Df *gradients[nlevels]; + float avgGrad[nlevels]; + + for (int k = 0 ; k < nlevels ; k++) { + gradients[k] = new Array2Df(pyramids[k]->getCols(), pyramids[k]->getRows()); + avgGrad[k] = calculateGradients(pyramids[k], gradients[k], k, multithread); + + if (k != 0) { // pyramids[0] is Y + delete pyramids[k]; + } + } + + + // calculate fi matrix + Array2Df FI(W, H, out); + calculateFiMatrix(&FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); + + for (int i = 0 ; i < nlevels ; i++) { + delete gradients[i]; + } + + // rescale the mask + float m = out[0][0]; +#ifdef _OPENMP + # pragma omp parallel for reduction(max:m) if (multithread) +#endif + + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float v = std::abs(out[y][x]); + out[y][x] = v; + m = std::max(v, m); + } + } + + if (m > 0.f) { + const float f = amount / m; +#ifdef _OPENMP + # pragma omp parallel for reduction(max:m) if (multithread) +#endif + + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + out[y][x] *= f; + } + } + } + + // { + // Imagefloat tmp(W, H); + // for (int y = 0; y < H; ++y) { + // for (int x = 0; x < W; ++x) { + // tmp.r(y, x) = tmp.g(y, x) = tmp.b(y, x) = out[y][x] * 65535.f; + // } + // } + // std::ostringstream name; + // name << "/tmp/FI-" << W << "x" << H << ".tif"; + // tmp.saveAsTIFF(name.str(), 16); + // } +} } // namespace rtengine diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index fcc4d6b28..24f583a62 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -331,14 +331,15 @@ Locallab::Locallab(): noiselumf0(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINEZERO"), MINCHRO, MAXCHRO, 1, 0))), noiselumf2(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINETWO"), MINCHRO, MAXCHRO, 1, 0))), noiselumc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMCOARSE"), MINCHRO, MAXCHROCC, 1, 0))), - noiselumdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMDETAIL"), 0, 100, 1, 80))), + noiselumdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMDETAIL"), 0, 100, 1, 0))), noiselequal(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELEQUAL"), -2, 10, 1, 7, Gtk::manage(new RTImage("circle-white-small.png")), Gtk::manage(new RTImage("circle-black-small.png"))))), noisechrof(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROFINE"), MINCHRO, MAXCHRO, 1, 0))), noisechroc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROCOARSE"), MINCHRO, MAXCHROCC, 1, 0))), - noisechrodetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHRODETAIL"), 0, 100, 1, 80))), + noisechrodetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHRODETAIL"), 0, 100, 1, 0))), adjblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_ADJ"), -100., 100., 1., 0., Gtk::manage(new RTImage("circle-blue-small.png")), Gtk::manage(new RTImage("circle-red-small.png"))))), bilateral(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BILATERAL"), 0, 100, 1, 0))), sensiden(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIDEN"), 0, 100, 1, 20))), + detailthr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAILTHR"), 0, 100, 1, 0))), // ButtonCheck widgets // Color & Light @@ -2122,7 +2123,7 @@ Locallab::Locallab(): noiselumdetail->setAdjusterListener(this); if (showtooltip) { - noiselumdetail->set_tooltip_text(M("TP_LOCALLAB_NOISEDETAIL_TOOLTIP")); +// noiselumdetail->set_tooltip_text(M("TP_LOCALLAB_NOISEDETAIL_TOOLTIP")); } noiselequal->setAdjusterListener(this); @@ -2138,7 +2139,7 @@ Locallab::Locallab(): noisechrodetail->setAdjusterListener(this); if (showtooltip) { - noisechrodetail->set_tooltip_text(M("TP_LOCALLAB_NOISEDETAIL_TOOLTIP")); +// noisechrodetail->set_tooltip_text(M("TP_LOCALLAB_NOISEDETAIL_TOOLTIP")); } adjblur->setAdjusterListener(this); @@ -2146,6 +2147,7 @@ Locallab::Locallab(): bilateral->setAdjusterListener(this); sensiden->setAdjusterListener(this); + detailthr->setAdjusterListener(this); ToolParamBlock* const denoisBox = Gtk::manage(new ToolParamBlock()); Gtk::Frame* const wavFrame = Gtk::manage(new Gtk::Frame()); @@ -2158,7 +2160,8 @@ Locallab::Locallab(): wavBox->pack_start(*noiselequal); wavBox->pack_start(*noisechrof); wavBox->pack_start(*noisechroc); - wavBox->pack_start(*noisechrodetail); // Uncomment this line to use the noisechrodetail adjuster + wavBox->pack_start(*noisechrodetail); + wavBox->pack_start(*detailthr); wavBox->pack_start(*adjblur); wavFrame->add(*wavBox); denoisBox->pack_start(*wavFrame); @@ -3387,6 +3390,7 @@ void Locallab::write(ProcParams* pp, ParamsEdited* pedited) pp->locallab.spots.at(pp->locallab.selspot).adjblur = adjblur->getIntValue(); pp->locallab.spots.at(pp->locallab.selspot).bilateral = bilateral->getIntValue(); pp->locallab.spots.at(pp->locallab.selspot).sensiden = sensiden->getIntValue(); + pp->locallab.spots.at(pp->locallab.selspot).detailthr = detailthr->getIntValue(); } ControlSpotPanel::SpotEdited* const se = expsettings->getEditedStates(); @@ -3666,6 +3670,7 @@ void Locallab::write(ProcParams* pp, ParamsEdited* pedited) pe->locallab.spots.at(pp->locallab.selspot).adjblur = pe->locallab.spots.at(pp->locallab.selspot).adjblur || adjblur->getEditedState(); pe->locallab.spots.at(pp->locallab.selspot).bilateral = pe->locallab.spots.at(pp->locallab.selspot).bilateral || bilateral->getEditedState(); pe->locallab.spots.at(pp->locallab.selspot).sensiden = pe->locallab.spots.at(pp->locallab.selspot).sensiden || sensiden->getEditedState(); + pe->locallab.spots.at(pp->locallab.selspot).detailthr = pe->locallab.spots.at(pp->locallab.selspot).detailthr || detailthr->getEditedState(); } } @@ -3948,6 +3953,7 @@ void Locallab::write(ProcParams* pp, ParamsEdited* pedited) pedited->locallab.spots.at(pp->locallab.selspot).adjblur = pedited->locallab.spots.at(pp->locallab.selspot).adjblur || adjblur->getEditedState(); pedited->locallab.spots.at(pp->locallab.selspot).bilateral = pedited->locallab.spots.at(pp->locallab.selspot).bilateral || bilateral->getEditedState(); pedited->locallab.spots.at(pp->locallab.selspot).sensiden = pedited->locallab.spots.at(pp->locallab.selspot).sensiden || sensiden->getEditedState(); + pedited->locallab.spots.at(pp->locallab.selspot).detailthr = pedited->locallab.spots.at(pp->locallab.selspot).detailthr || detailthr->getEditedState(); } } } @@ -5742,6 +5748,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe adjblur->setDefault((double)defSpot->adjblur); bilateral->setDefault((double)defSpot->bilateral); sensiden->setDefault((double)defSpot->sensiden); + detailthr->setDefault((double)defSpot->detailthr); // Set default edited states for adjusters and threshold adjusters if (!pedited) { @@ -5914,6 +5921,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe adjblur->setDefaultEditedState(Irrelevant); bilateral->setDefaultEditedState(Irrelevant); sensiden->setDefaultEditedState(Irrelevant); + detailthr->setDefaultEditedState(Irrelevant); } else { const LocallabParamsEdited::LocallabSpotEdited* defSpotState = new LocallabParamsEdited::LocallabSpotEdited(true); @@ -6091,6 +6099,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe adjblur->setDefaultEditedState(defSpotState->adjblur ? Edited : UnEdited); bilateral->setDefaultEditedState(defSpotState->bilateral ? Edited : UnEdited); sensiden->setDefaultEditedState(defSpotState->sensiden ? Edited : UnEdited); + detailthr->setDefaultEditedState(defSpotState->detailthr ? Edited : UnEdited); } } @@ -7130,6 +7139,13 @@ void Locallab::adjusterChanged(Adjuster * a, double newval) listener->panelChanged(Evlocallabsensiden, sensiden->getTextValue()); } } + + if (a == detailthr) { + if (listener) { + listener->panelChanged(Evlocallabdetailthr, detailthr->getTextValue()); + } + } + } } @@ -7327,6 +7343,7 @@ void Locallab::setBatchMode(bool batchMode) adjblur->showEditedCB(); bilateral->showEditedCB(); sensiden->showEditedCB(); + detailthr->showEditedCB(); // Set batch mode for comboBoxText // Color & Light @@ -7997,6 +8014,7 @@ void Locallab::updateLocallabGUI(const rtengine::procparams::ProcParams* pp, con adjblur->setValue(pp->locallab.spots.at(index).adjblur); bilateral->setValue(pp->locallab.spots.at(index).bilateral); sensiden->setValue(pp->locallab.spots.at(index).sensiden); + detailthr->setValue(pp->locallab.spots.at(index).detailthr); if (pedited) { if (index < (int)pedited->locallab.spots.size()) { @@ -8329,6 +8347,7 @@ void Locallab::updateLocallabGUI(const rtengine::procparams::ProcParams* pp, con adjblur->setEditedState(spotState->adjblur ? Edited : UnEdited); bilateral->setEditedState(spotState->bilateral ? Edited : UnEdited); sensiden->setEditedState(spotState->sensiden ? Edited : UnEdited); + detailthr->setEditedState(spotState->detailthr ? Edited : UnEdited); } } } diff --git a/rtgui/locallab.h b/rtgui/locallab.h index 9663e51c9..0aa59f35d 100644 --- a/rtgui/locallab.h +++ b/rtgui/locallab.h @@ -291,6 +291,7 @@ private: Adjuster* const adjblur; Adjuster* const bilateral; Adjuster* const sensiden; + Adjuster* const detailthr; // ButtonCheck widgets // Color & Light diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 9205c6322..590961fbc 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1223,6 +1223,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).adjblur = locallab.spots.at(j).adjblur && pSpot.adjblur == otherSpot.adjblur; locallab.spots.at(j).bilateral = locallab.spots.at(j).bilateral && pSpot.bilateral == otherSpot.bilateral; locallab.spots.at(j).sensiden = locallab.spots.at(j).sensiden && pSpot.sensiden == otherSpot.sensiden; + locallab.spots.at(j).detailthr = locallab.spots.at(j).detailthr && pSpot.detailthr == otherSpot.detailthr; } } @@ -3724,6 +3725,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (locallab.spots.at(i).sensiden) { toEdit.locallab.spots.at(i).sensiden = mods.locallab.spots.at(i).sensiden; } + + if (locallab.spots.at(i).detailthr) { + toEdit.locallab.spots.at(i).detailthr = mods.locallab.spots.at(i).detailthr; + } } @@ -4961,7 +4966,8 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : noisechrodetail(v), adjblur(v), bilateral(v), - sensiden(v) + sensiden(v), + detailthr(v) { } @@ -5240,6 +5246,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) adjblur = v; bilateral = v; sensiden = v; + detailthr = v; } bool CaptureSharpeningParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 7a0052f95..bef35607d 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -638,6 +638,7 @@ public: bool adjblur; bool bilateral; bool sensiden; + bool detailthr; LocallabSpotEdited(bool v);