diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 0738e48b0..818d4d8bb 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -808,6 +808,10 @@ void Crop::update (int todo) 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); + if (params.fattal.enabled) { + parent->ipf.ToneMapFattal02(labnCrop, 3); + } + if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { parent->ipf.EPDToneMap (labnCrop, 5, skip); } @@ -1077,6 +1081,11 @@ void Crop::freeAll () namespace { +bool check_need_full_image(const ProcParams ¶ms) +{ + return params.fattal.enabled; // agriggio - maybe we can do this for wavelets too? +} + bool check_need_larger_crop_for_lcp_distortion (int fw, int fh, int x, int y, int w, int h, const ProcParams ¶ms) { if (x == 0 && y == 0 && w == fw && h == fh) { @@ -1139,6 +1148,14 @@ bool Crop::setCropSizes (int rcx, int rcy, int rcw, int rch, int skip, bool inte ory = by1; orw = bw; orh = bh; + + if (check_need_full_image(parent->params)) { + orx = bx1 = 0; + ory = by1 = 0; + orw = bw = parent->fullw; + orh = bh = parent->fullh; + } + ProcParams& params = parent->params; parent->ipf.transCoord (parent->fw, parent->fh, bx1, by1, bw, bh, orx, ory, orw, orh); @@ -1178,7 +1195,6 @@ bool Crop::setCropSizes (int rcx, int rcy, int rcw, int rch, int skip, bool inte orh = min (y2 - y1, parent->fh - ory); } - PreviewProps cp (orx, ory, orw, orh, skip); int orW, orH; parent->imgsrc->getSize (cp, orW, orH); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 9d265f90b..e3ff874b6 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -636,6 +636,10 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) ipf.chromiLuminanceCurve (nullptr, pW, nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, histCCurve, histLCurve); ipf.vibrance (nprevl); + if (params.fattal.enabled) { + ipf.ToneMapFattal02(nprevl, 3); + } + if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { ipf.EPDToneMap (nprevl, 5, scale); } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 21bc9d9c5..516e0ee9d 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -3119,19 +3119,6 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } - std::unique_ptr fattal; - if (params->fattal.enabled) { - fattal.reset(working->copy()); - int detail_level = 3; - if (scale < 8) { - detail_level = 3; - } else { - detail_level = 0; - } - ToneMapFattal02(fattal.get(), detail_level); - working = fattal.get(); - } - int h_th = 0, s_th = 0; if (shmap) { diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 53de02d3d..c1e4a1e35 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -347,7 +347,7 @@ public: void Badpixelscam (CieImage * src, CieImage * dst, double radius, int thresh, int mode, float b_l, float t_l, float t_r, float b_r, float skinprot, float chrom, int hotbad); void BadpixelsLab (LabImage * src, LabImage * dst, double radius, int thresh, int mode, float b_l, float t_l, float t_r, float b_r, float skinprot, float chrom); - void ToneMapFattal02(Imagefloat *rgb, int detail_level); + void ToneMapFattal02(LabImage *lab, int detail_level); Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm); Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool bw, GammaValues *ga = nullptr); diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 9bdee796b..6cc50a6a8 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1275,6 +1275,10 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.vibrance (labView); + if (params.fattal.enabled) { + ipf.ToneMapFattal02(labView, 0); + } + if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || !params.colorappearance.enabled) { ipf.EPDToneMap (labView, 5, 6); } diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 93d2a3149..de57386dd 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1013,6 +1013,10 @@ private: ipf.chromiLuminanceCurve (nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); + if (params.fattal.enabled) { + ipf.ToneMapFattal02(labView, 3); + } + if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { ipf.EPDToneMap (labView, 5, 1); } diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index d50488554..7d5a866b1 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -68,6 +68,8 @@ #include "array2D.h" #include "improcfun.h" #include "settings.h" +#include "iccstore.h" + namespace rtengine { @@ -155,6 +157,15 @@ void gaussianBlur(const Array2Df& I, Array2Df& L) Array2Df T(width,height); + if (width < 3 || height < 3) { + if (&I != &L) { + for (int i = 0, n = width*height; i < n; ++i) { + L(i) = I(i); + } + } + return; + } + //--- X blur //#pragma omp parallel for shared(I, T) for ( int y=0 ; y 2 && height > 2) { + width /= 2; + height /= 2; + 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); + for (int j = 0, n = width*height; j < n; ++j) { + (*pyramids[k])(j) = (*L)(j); + } + } delete L; L = new Array2Df(width,height); @@ -379,7 +400,7 @@ void findMaxMinPercentile(const Array2Df& I, maxLum = vI.at( int(maxPrct*vI.size()) ); } -void solve_pde_fft(Array2Df *F, Array2Df *U); +void solve_pde_fft(Array2Df *F, Array2Df *U, bool multithread); void tmo_fattal02(size_t width, size_t height, @@ -388,7 +409,8 @@ void tmo_fattal02(size_t width, float alfa, float beta, float noise, - int detail_level) + int detail_level, + bool multithread) { // #ifdef TIMER_PROFILING // msec_timer stop_watch; @@ -404,15 +426,21 @@ void tmo_fattal02(size_t width, // ph.setValue(2); // if (ph.canceled()) return; - int MSIZE = 32; // minimum size of gaussian pyramid - // I believe a smaller value than 32 results in slightly better overall - // quality but I'm only applying this if the newly implemented fft solver - // is used in order not to change behaviour of the old version - // TODO: best let the user decide this value - // if (fftsolver) - { - MSIZE = 8; - } + /* RT -- we use a hardcoded value of 8 for nlevels, to limit the + * dependency of the result on the image size. When using an auto computed + * nlevels value, you would get vastly different results with different + * image sizes, making it essentially impossible to preview the tool + * inside RT. With a hardcoded value, the results for the preview are much + * closer to those for the final image */ + // int MSIZE = 32; // minimum size of gaussian pyramid + // // I believe a smaller value than 32 results in slightly better overall + // // quality but I'm only applying this if the newly implemented fft solver + // // is used in order not to change behaviour of the old version + // // TODO: best let the user decide this value + // // if (fftsolver) + // { + // MSIZE = 8; + // } int size = width*height; // unsigned int x,y; @@ -435,16 +463,17 @@ void tmo_fattal02(size_t width, // ph.setValue(4); // create gaussian pyramids - int mins = (width= MSIZE ) - { - nlevels++; - mins /= 2; - } - // std::cout << "DEBUG: nlevels = " << nlevels << ", mins = " << mins << std::endl; - // The following lines solves a bug with images particularly small - if (nlevels == 0) nlevels = 1; + // int mins = (width= MSIZE ) + // { + // nlevels++; + // mins /= 2; + // } + // // std::cout << "DEBUG: nlevels = " << nlevels << ", mins = " << mins << std::endl; + // // The following lines solves a bug with images particularly small + // if (nlevels == 0) nlevels = 1; + const int nlevels = 7; // RT -- see above Array2Df** pyramids = new Array2Df*[nlevels]; createGaussianPyramids(H, pyramids, nlevels); @@ -551,7 +580,7 @@ void tmo_fattal02(size_t width, Array2Df U(width, height); // if (fftsolver) { - solve_pde_fft(&DivG, &U);//, ph); + solve_pde_fft(&DivG, &U, multithread);//, ph); } // else // { @@ -798,7 +827,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)/*, pfs::Progress &ph, +void solve_pde_fft(Array2Df *F, Array2Df *U, bool multithread)/*, pfs::Progress &ph, bool adjust_bound)*/ { // ph.setValue(20); @@ -809,8 +838,10 @@ void solve_pde_fft(Array2Df *F, Array2Df *U)/*, pfs::Progress &ph, // activate parallel execution of fft routines #ifdef RT_FFTW3F_OMP - fftwf_init_threads(); - fftwf_plan_with_nthreads( omp_get_max_threads() ); + if (multithread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads( omp_get_max_threads() ); + } // #else // fftwf_plan_with_nthreads( 2 ); #endif @@ -883,7 +914,9 @@ void solve_pde_fft(Array2Df *F, Array2Df *U)/*, pfs::Progress &ph, // fft parallel threads cleanup, better handled outside this function? #ifdef RT_FFTW3F_OMP - fftwf_cleanup_threads(); + if (multithread) { + fftwf_cleanup_threads(); + } #endif // ph.setValue(90); @@ -916,10 +949,7 @@ void solve_pde_fft(Array2Df *F, Array2Df *U)/*, pfs::Progress &ph, // } -} // namespace - - -void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, int detail_level) +void tmo_fattal02_RT(Imagefloat *rgb, float alpha, float beta, int detail_level, bool multiThread) { int w = rgb->getWidth(); int h = rgb->getHeight(); @@ -936,8 +966,8 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, int detail_level) } } - float alpha = params->fattal.alpha; - float beta = params->fattal.beta; + // float alpha = params->fattal.alpha; + // float beta = params->fattal.beta; float noise = alpha * 0.01f; if (settings->verbose) { @@ -945,7 +975,7 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, int detail_level) << ", detail_level = " << detail_level << std::endl; } - tmo_fattal02(w, h, Yr, L, alpha, beta, noise, detail_level); + tmo_fattal02(w, h, Yr, L, alpha, beta, noise, detail_level, multiThread); const float epsilon = 1e-4f; for (int y = 0; y < h; y++) { @@ -961,4 +991,15 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, int detail_level) rgb->normalizeFloatTo65535(); } +} // namespace + + +void ImProcFunctions::ToneMapFattal02(LabImage *lab, int detail_level) +{ + Imagefloat tmp(lab->W, lab->H); + lab2rgb(*lab, tmp, params->icm.working); + tmo_fattal02_RT(&tmp, params->fattal.alpha, params->fattal.beta, detail_level, multiThread); + rgb2lab(tmp, *lab, params->icm.working); +} + } // namespace rtengine