/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . * adaptation to RawTherapee * 2015 Jacques Desmis * 2015 Ingo Weyrich * D. J. Jobson, Z. Rahman, and G. A. Woodell. A multi-scale * Retinex for bridging the gap between color images and the * human observation of scenes. IEEE Transactions on Image Processing, * 1997, 6(7): 965-976 * Fan Guo Zixing Cai Bin Xie Jin Tang * School of Information Science and Engineering, Central South University Changsha, China * Weixing Wang and Lian Xu * College of Physics and Information Engineering, Fuzhou University, Fuzhou, China * inspired from 2003 Fabien Pelisson * some ideas taken (use of mask) Russell Cottrell - The Retinex .8bf Plugin */ #include #include #include #include #include "color.h" #include "curves.h" #include "gauss.h" #include "improcfun.h" #include "labimage.h" #include "median.h" #include "opthelper.h" #include "procparams.h" #include "rawimagesource.h" #include "rtengine.h" #include "shmap.h" //#define BENCHMARK #include "StopWatch.h" #include "guidedfilter.h" #include "boxblur.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #define CLIPLOC(x) LIM(x,0.f,32767.f) #define CLIPC(a) LIM(a, -42000.f, 42000.f) // limit a and b to 130 probably enough ? namespace { void calcGammaLut(double gamma, double ts, LUTf &gammaLut) { double pwr = 1.0 / gamma; double gamm = gamma; const double gamm2 = gamma; rtengine::GammaValues g_a; if (gamm2 < 1.0) { std::swap(pwr, gamm); } rtengine::Color::calcGamma(pwr, ts, g_a); // call to calcGamma with selected gamma and slope const double start = gamm2 < 1. ? g_a[2] : g_a[3]; const double add = g_a[4]; const double mul = 1.0 + g_a[4]; if (gamm2 < 1.) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 1024) #endif for (int i = 0; i < 65536; i++) { const double x = rtengine::Color::igammareti(i / 65535.0, gamm, start, ts, mul, add); gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values } } else { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 1024) #endif for (int i = 0; i < 65536; i++) { const double x = rtengine::Color::gammareti(i / 65535.0, gamm, start, ts, mul, add); gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values } } } void retinex_scales(float* scales, int nscales, int mode, int s, float high) { if (s < 3) { s = 3; //to avoid crash in MSRlocal if nei small } if (nscales == 1) { scales[0] = (float)s / 2.f; } else if (nscales == 2) { scales[1] = (float) s / 2.f; scales[0] = (float) s; } else { float size_step = (float) s / (float) nscales; if (mode == 0) { for (int i = 0; i < nscales; ++i) { scales[nscales - i - 1] = 2.0f + (float)i * size_step; } } else if (mode == 1) { size_step = (float)log(s - 2.0f) / (float) nscales; for (int i = 0; i < nscales; ++i) { scales[nscales - i - 1] = 2.0f + (float)pow(10.f, (i * size_step) / log(10.f)); } } else if (mode == 2) { size_step = (float) log(s - 2.0f) / (float) nscales; for (int i = 0; i < nscales; ++i) { scales[i] = s - (float)pow(10.f, (i * size_step) / log(10.f)); } } else if (mode == 3) { size_step = (float) log(s - 2.0f) / (float) nscales; for (int i = 0; i < nscales; ++i) { scales[i] = high * s - (float)pow(10.f, (i * size_step) / log(10.f)); } } } } void mean_stddv2(float **dst, float &mean, float &stddv, int W_L, int H_L, float &maxtr, float &mintr) { // summation using double precision to avoid too large summation error for large pictures double vsquared = 0.f; double sum = 0.f; maxtr = -999999.f; mintr = 999999.f; #ifdef _OPENMP #pragma omp parallel #endif { float lmax = -999999.f, lmin = 999999.f; #ifdef _OPENMP #pragma omp for reduction(+:sum,vsquared) nowait // this leads to differences, but parallel summation is more accurate #endif for (int i = 0; i < H_L; i++) for (int j = 0; j < W_L; j++) { sum += static_cast(dst[i][j]); vsquared += rtengine::SQR(dst[i][j]); lmax = dst[i][j] > lmax ? dst[i][j] : lmax; lmin = dst[i][j] < lmin ? dst[i][j] : lmin; } #ifdef _OPENMP #pragma omp critical #endif { maxtr = maxtr > lmax ? maxtr : lmax; mintr = mintr < lmin ? mintr : lmin; } } mean = sum / (double)(W_L * H_L); vsquared /= (double) W_L * H_L; stddv = vsquared - rtengine::SQR(mean); stddv = std::sqrt(stddv); } } namespace rtengine { void RawImageSource::MSR(float** luminance, float** originalLuminance, float **exLuminance, const LUTf& mapcurve, bool mapcontlutili, int width, int height, const procparams::RetinexParams &deh, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax) { BENCHFUN if (!deh.enabled) { return; } constexpr float eps = 2.f; const bool useHsl = deh.retinexcolorspace == "HSLLOG"; const bool useHslLin = deh.retinexcolorspace == "HSLLIN"; const float offse = deh.offs; //def = 0 not use const int iter = deh.iter; const int gradient = deh.scal; int scal = deh.skal; const int nei = 2.8f * deh.neigh; //def = 220 const float vart = deh.vart / 100.f;//variance const float gradvart = deh.grad; const float gradstr = deh.grads; const float strength = deh.str / 100.f; // Blend with original L channel data float limD = deh.limd; limD = pow(limD, 1.7f);//about 2500 enough limD *= useHslLin ? 10.f : 1.f; const float ilimD = 1.f / limD; const float hig = deh.highl / 100.f; const int H_L = height; const int W_L = width; constexpr float elogt = 2.71828f; bool lhutili = false; FlatCurve* shcurve = new FlatCurve(deh.lhcurve); //curve L=f(H) if (!shcurve || shcurve->isIdentity()) { if (shcurve) { delete shcurve; shcurve = nullptr; } } else { lhutili = true; } bool higplus = false ; int moderetinex = 2; // default to 2 ( deh.retinexMethod == "high" ) if (deh.retinexMethod == "highliplus") { higplus = true; moderetinex = 3; } else if (deh.retinexMethod == "uni") { moderetinex = 0; } else if (deh.retinexMethod == "low") { moderetinex = 1; } else { /*if (deh.retinexMethod == "highli") */ moderetinex = 3; } constexpr float aahi = 49.f / 99.f; ////reduce sensibility 50% constexpr float bbhi = 1.f - aahi; float high = bbhi + aahi * (float) deh.highl; for (int it = 1; it < iter + 1; it++) { //iter nb max of iterations float grad = 1.f; float sc = scal; if (gradient == 0) { grad = 1.f; sc = 3.f; } else if (gradient == 1) { grad = 0.25f * it + 0.75f; sc = -0.5f * it + 4.5f; } else if (gradient == 2) { grad = 0.5f * it + 0.5f; sc = -0.75f * it + 5.75f; } else if (gradient == 3) { grad = 0.666f * it + 0.333f; sc = -0.75f * it + 5.75f; } else if (gradient == 4) { grad = 0.8f * it + 0.2f; sc = -0.75f * it + 5.75f; } else if (gradient == 5) { if (moderetinex != 3) { grad = 2.5f * it - 1.5f; } else { float aa = (11.f * high - 1.f) / 4.f; float bb = 1.f - aa; grad = aa * it + bb; } sc = -0.75f * it + 5.75f; } else if (gradient == 6) { if (moderetinex != 3) { grad = 5.f * it - 4.f; } else { float aa = (21.f * high - 1.f) / 4.f; float bb = 1.f - aa; grad = aa * it + bb; } sc = -0.75f * it + 5.75f; } else if (gradient == -1) { grad = -0.125f * it + 1.125f; sc = 3.f; } if (iter == 1) { sc = scal; } else { //adjust sc in function of choice of scale by user if iterations if (scal < 3) { sc -= 1; if (sc < 1.f) {//avoid 0 sc = 1.f; } } else if (scal > 4) { sc += 1; } } float varx = vart; float limdx = limD; float ilimdx = ilimD; if (gradvart != 0) { if (gradvart == 1) { varx = vart * (-0.125f * it + 1.125f); limdx = limD * (-0.125f * it + 1.125f); ilimdx = 1.f / limdx; } else if (gradvart == 2) { varx = vart * (-0.2f * it + 1.2f); limdx = limD * (-0.2f * it + 1.2f); ilimdx = 1.f / limdx; } else if (gradvart == -1) { varx = vart * (0.125f * it + 0.875f); limdx = limD * (0.125f * it + 0.875f); ilimdx = 1.f / limdx; } else if (gradvart == -2) { varx = vart * (0.4f * it + 0.6f); limdx = limD * (0.4f * it + 0.6f); ilimdx = 1.f / limdx; } } scal = round(sc); float ks = 1.f; if (gradstr != 0) { if (gradstr == 1) { if (it <= 3) { ks = -0.3f * it + 1.6f; } else { ks = 0.5f; } } else if (gradstr == 2) { if (it <= 3) { ks = -0.6f * it + 2.2f; } else { ks = 0.3f; } } else if (gradstr == -1) { if (it <= 3) { ks = 0.2f * it + 0.6f; } else { ks = 1.2f; } } else if (gradstr == -2) { if (it <= 3) { ks = 0.4f * it + 0.2f; } else { ks = 1.5f; } } } const float strengthx = ks * strength; constexpr auto maxRetinexScales = 8; float RetinexScales[maxRetinexScales]; retinex_scales(RetinexScales, scal, moderetinex, nei / grad, high); const int shHighlights = deh.highlights; const int shShadows = deh.shadows; int mapmet = 0; if (deh.mapMethod == "map") { mapmet = 2; } else if (deh.mapMethod == "mapT") { mapmet = 3; } else if (deh.mapMethod == "gaus") { mapmet = 4; } const double shradius = mapmet == 4 ? (double) deh.radius : 40.; int viewmet = 0; if (deh.viewMethod == "mask") { viewmet = 1; } else if (deh.viewMethod == "tran") { viewmet = 2; } else if (deh.viewMethod == "tran2") { viewmet = 3; } else if (deh.viewMethod == "unsharp") { viewmet = 4; } std::unique_ptr> srcBuffer(new JaggedArray(W_L, H_L)); float** src = *srcBuffer; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) for (int j = 0; j < W_L; j++) { src[i][j] = luminance[i][j] + eps; luminance[i][j] = 0.f; } JaggedArray out(W_L, H_L); JaggedArray& tran = out; // tran and out can safely use the same buffer const float logBetaGain = xlogf(16384.f); float pond = logBetaGain / (float) scal; if (!useHslLin) { pond /= log(elogt); } std::unique_ptr shmap; if (((mapmet == 2 || mapmet == 3 || mapmet == 4) && it == 1)) { shmap.reset(new SHMap(W_L, H_L)); } std::unique_ptr buffer; if (mapmet > 0) { buffer.reset(new float[W_L * H_L]); } for (int scale = scal - 1; scale >= 0; --scale) { if (scale == scal - 1) { gaussianBlur(src, out, W_L, H_L, RetinexScales[scale], true); } else { // reuse result of last iteration // out was modified in last iteration => restore it if ((((mapmet == 2 && scale > 1) || mapmet == 3 || mapmet == 4) || (mapmet > 0 && mapcontlutili)) && it == 1) { #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { out[i][j] = buffer[i * W_L + j]; } } } gaussianBlur(out, out, W_L, H_L, sqrtf(SQR(RetinexScales[scale]) - SQR(RetinexScales[scale + 1])), true); } if ((((mapmet == 2 && scale > 2) || mapmet == 3 || mapmet == 4) || (mapmet > 0 && mapcontlutili)) && it == 1 && scale > 0) { // out will be modified => store it for use in next iteration. #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { buffer[i * W_L + j] = out[i][j]; } } } int h_th = 0; int s_th = 0; if (((mapmet == 2 && scale > 2) || mapmet == 3 || mapmet == 4) && it == 1) { shmap->updateL(out, shradius, true, 1); h_th = shmap->max_f - deh.htonalwidth * (shmap->max_f - shmap->avg) / 100; s_th = deh.stonalwidth * (shmap->avg - shmap->min_f) / 100; } if (mapmet > 0 && mapcontlutili && it == 1) { #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { out[i][j] = mapcurve[2.f * out[i][j]]; } } } if (((mapmet == 2 && scale > 2) || mapmet == 3 || mapmet == 4) && it == 1) { const float hWeight = (100.f - shHighlights) / 100.f; const float sWeight = (100.f - shShadows) / 100.f; #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { const float mapval = 1.f + shmap->map[i][j]; float factor; if (mapval > h_th) { factor = (h_th + hWeight * (mapval - h_th)) / mapval; } else if (mapval < s_th) { factor = (s_th - sWeight * (s_th - mapval)) / mapval; } else { factor = 1.f; } out[i][j] *= factor; } } } #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { int j = 0; #ifdef __SSE2__ const vfloat pondv = F2V(pond); const vfloat limMinv = F2V(ilimdx); const vfloat limMaxv = F2V(limdx); if (useHslLin) { for (; j < W_L - 3; j += 4) { STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv)); } } else { for (; j < W_L - 3; j += 4) { STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * xlogf(vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv))); } } #endif if (useHslLin) { for (; j < W_L; j++) { luminance[i][j] += pond * LIM(src[i][j] / out[i][j], ilimdx, limdx); } } else { for (; j < W_L; j++) { luminance[i][j] += pond * xlogf(LIM(src[i][j] / out[i][j], ilimdx, limdx)); // /logt ? } } } } srcBuffer.reset(); float mean = 0.f; float stddv = 0.f; // I call mean_stddv2 instead of mean_stddv ==> logBetaGain float maxtr, mintr; mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); //printf("mean=%f std=%f delta=%f maxtr=%f mintr=%f\n", mean, stddv, delta, maxtr, mintr); //mean_stddv( luminance, mean, stddv, W_L, H_L, logBetaGain, maxtr, mintr); if (dehatransmissionCurve && mean != 0.f && stddv != 0.f) { //if curve float asig = 0.166666f / stddv; float bsig = 0.5f - asig * mean; float amax = 0.333333f / (maxtr - mean - stddv); float bmax = 1.f - amax * maxtr; float amin = 0.333333f / (mean - stddv - mintr); float bmin = -amin * mintr; asig *= 500.f; bsig *= 500.f; amax *= 500.f; bmax *= 500.f; amin *= 500.f; bmin *= 500.f; #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { //for mintr to maxtr evalate absciss in function of original transmission float absciss; if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { absciss = asig * luminance[i][j] + bsig; } else if (luminance[i][j] >= mean) { absciss = amax * luminance[i][j] + bmax; } else { /*if(luminance[i][j] <= mean - stddv)*/ absciss = amin * luminance[i][j] + bmin; } //TODO : move multiplication by 4.f and subtraction of 1.f inside the curve luminance[i][j] *= (-1.f + 4.f * dehatransmissionCurve[absciss]); //new transmission if (viewmet == 3 || viewmet == 2) { tran[i][j] = luminance[i][j]; } } } // median filter on transmission ==> reduce artifacts if (deh.medianmap && it == 1) { //only one time JaggedArray tmL(W_L, H_L); constexpr int borderL = 1; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = borderL; i < H_L - borderL; i++) { for (int j = borderL; j < W_L - borderL; j++) { tmL[i][j] = median(luminance[i][j], luminance[i - 1][j], luminance[i + 1][j], luminance[i][j + 1], luminance[i][j - 1], luminance[i - 1][j - 1], luminance[i - 1][j + 1], luminance[i + 1][j - 1], luminance[i + 1][j + 1]); //3x3 } } #ifdef _OPENMP #pragma omp parallel for #endif for (int i = borderL; i < H_L - borderL; i++) { for (int j = borderL; j < W_L - borderL; j++) { luminance[i][j] = tmL[i][j]; } } } // I call mean_stddv2 instead of mean_stddv ==> logBetaGain //mean_stddv( luminance, mean, stddv, W_L, H_L, 1.f, maxtr, mintr); mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); } constexpr float epsil = 0.1f; mini = mean - varx * stddv; if (mini < mintr) { mini = mintr + epsil; } maxi = mean + varx * stddv; if (maxi > maxtr) { maxi = maxtr - epsil; } float delta = maxi - mini; //printf("maxi=%f mini=%f mean=%f std=%f delta=%f maxtr=%f mintr=%f\n", maxi, mini, mean, stddv, delta, maxtr, mintr); if (!delta) { delta = 1.0f; } // coeff for auto "transmission" with 2 sigma #95% data const float aza = 16300.f / (2.f * stddv); const float azb = -aza * (mean - 2.f * stddv); const float bza = 16300.f / (2.f * stddv); const float bzb = 16300.f - bza * (mean); //prepare work for curve gain #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { luminance[i][j] = luminance[i][j] - mini; } } mean = 0.f; stddv = 0.f; // I call mean_stddv2 instead of mean_stddv ==> logBetaGain mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); float asig = 0.f, bsig = 0.f, amax = 0.f, bmax = 0.f, amin = 0.f, bmin = 0.f; if (dehagaintransmissionCurve && mean != 0.f && stddv != 0.f) { //if curve asig = 0.166666f / stddv; bsig = 0.5f - asig * mean; amax = 0.333333f / (maxtr - mean - stddv); bmax = 1.f - amax * maxtr; amin = 0.333333f / (mean - stddv - mintr); bmin = -amin * mintr; asig *= 500.f; bsig *= 500.f; amax *= 500.f; bmax *= 500.f; amin *= 500.f; bmin *= 500.f; } const float cdfactor = 32768.f / delta; maxCD = -9999999.f; minCD = 9999999.f; #ifdef _OPENMP #pragma omp parallel for reduction(max:maxCD) reduction(min:minCD) schedule(dynamic, 16) #endif for (int i = 0; i < H_L; i ++) { for (int j = 0; j < W_L; j++) { float gan; if (dehagaintransmissionCurve && mean != 0.f && stddv != 0.f) { float absciss; if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { absciss = asig * luminance[i][j] + bsig; } else if (luminance[i][j] >= mean) { absciss = amax * luminance[i][j] + bmax; } else { /*if(luminance[i][j] <= mean - stddv)*/ absciss = amin * luminance[i][j] + bmin; } // float cd = cdfactor * ( luminance[i][j] - mini ) + offse; // TODO : move multiplication by 2.f inside the curve gan = 2.f * dehagaintransmissionCurve[absciss]; //new gain function transmission } else { gan = 0.5f; } const float cd = gan * cdfactor * luminance[i][j] + offse; maxCD = cd > maxCD ? cd : maxCD; minCD = cd < minCD ? cd : minCD; float str = strengthx; if (lhutili && it == 1) { // S=f(H) { const float HH = exLuminance[i][j]; float valparam; if (useHsl || useHslLin) { valparam = shcurve->getVal(HH) - 0.5; } else { valparam = shcurve->getVal(Color::huelab_to_huehsv2(HH)) - 0.5; } str *= (1.f + 2.f * valparam); } } if (higplus && exLuminance[i][j] > 65535.f * hig) { str *= hig; } if (viewmet == 0) { luminance[i][j] = intp(str, LIM(cd, 0.f, 32768.f), originalLuminance[i][j]); } else if (viewmet == 1) { luminance[i][j] = out[i][j]; } else if (viewmet == 4) { luminance[i][j] = originalLuminance[i][j] + str * (originalLuminance[i][j] - out[i][j]);//unsharp } else if (viewmet == 2) { if (tran[i][j] <= mean) { luminance[i][j] = azb + aza * tran[i][j]; //auto values } else { luminance[i][j] = bzb + bza * tran[i][j]; } } else { /*if (viewmet == 3) */ luminance[i][j] = 1000.f + tran[i][j] * 700.f; //arbitrary values to help display log values which are between -20 to + 30 - usage values -4 + 5 } } } Tmean = mean; Tsigma = stddv; Tmin = mintr; Tmax = maxtr; if (shcurve) { delete shcurve; shcurve = nullptr; } } } void ImProcFunctions::maskforretinex(int sp, int before, float ** luminance, float ** out, int W_L, int H_L, int skip, const LocCCmaskCurve & locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool lhmasretiutili, int llretiMask, bool retiMasktmap, bool retiMask, float rad, float lap, bool pde, float gamm, float slop, float chro, float blend, const LUTf & lmaskretilocalcurve, bool localmaskretiutili, LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, LabImage * bufmaskorigreti, bool multiThread, bool delt, const float hueref, const float chromaref, const float lumaref, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask) { array2D loctemp(W_L, H_L); array2D ble(W_L, H_L); array2D blechro(W_L, H_L); array2D hue(W_L, H_L); array2D guid(W_L, H_L); std::unique_ptr bufmaskblurreti; bufmaskblurreti.reset(new LabImage(W_L, H_L)); // std::unique_ptr bufmaskorigreti; // bufmaskorigreti.reset(new LabImage(W_L, H_L)); std::unique_ptr bufprov; bufprov.reset(new LabImage(W_L, H_L)); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { if (before == 1 && retiMasktmap) { loctemp[y][x] = LIM(luminance[y][x], 0.f, 32768.f); } else if (before == 0 && retiMasktmap) { loctemp[y][x] = out[y][x]; } else { loctemp[y][x] = bufreti->L[y][x]; } } } float chromult = 1.f - 0.01f * chro; //fab float fab = 50.f; float meanfab = 0.f; const int nbfab = W_L * H_L; double sumab = 0.0; #ifdef _OPENMP #pragma omp parallel for reduction(+:sumab) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { sumab += fabs(bufreti->a[y][x]); sumab += fabs(bufreti->b[y][x]); } } meanfab = sumab / (2.f * nbfab); double som = 0.0; #ifdef _OPENMP #pragma omp parallel for reduction(+:som) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { som += SQR(fabs(bufreti->a[y][x]) - meanfab) + SQR(fabs(bufreti->b[y][x]) - meanfab); } } const float multsigma = (chro >= 0.f ? 0.035f : 0.018f) * chro + 1.f; const float stddv = sqrt(som / nbfab); fab = meanfab + multsigma * stddv; if (fab <= 0.f) { fab = 50.f; } //end fab #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int ir = 0; ir < H_L; ir++) { for (int jr = 0; jr < W_L; jr++) { float kmaskLexp = 0; float kmaskCH = 0; if (locllmasretiCurve && llmasretiutili) { float ligh = loctemp[ir][jr] / 32768.f; kmaskLexp = 32768.f * LIM01(1.f - locllmasretiCurve[500.f * ligh]); } if (locllmasretiCurve && llmasretiutili && retiMasktmap) { } if (llretiMask != 4) { if (locccmasretiCurve && lcmasretiutili) { float chromask = 0.0001f + sqrt(SQR((bufreti->a[ir][jr]) / fab) + SQR((bufreti->b[ir][jr]) / fab)); kmaskCH = LIM01(1.f - locccmasretiCurve[500.f * chromask]); } } if (lochhmasretiCurve && lhmasretiutili) { float huema = xatan2f(bufreti->b[ir][jr], bufreti->a[ir][jr]); float h = Color::huelab_to_huehsv2(huema); h += 1.f / 6.f; if (h > 1.f) { h -= 1.f; } float valHH = LIM01(1.f - lochhmasretiCurve[500.f * h]); if (llretiMask != 4) { kmaskCH += chromult * valHH; } kmaskLexp += 32768.f * valHH; } bufmaskblurreti->L[ir][jr] = kmaskLexp; bufmaskblurreti->a[ir][jr] = kmaskCH; bufmaskblurreti->b[ir][jr] = kmaskCH; ble[ir][jr] = bufmaskblurreti->L[ir][jr] / 32768.f; hue[ir][jr] = xatan2f(bufmaskblurreti->b[ir][jr], bufmaskblurreti->a[ir][jr]); float chromah = sqrt(SQR(bufmaskblurreti->b[ir][jr]) + SQR(bufmaskblurreti->a[ir][jr])); blechro[ir][jr] = chromah / 32768.f; guid[ir][jr] = bufreti->L[ir][jr] / 32768.f; bufprov->L[ir][jr] = bufmaskblurreti->L[ir][jr]; bufprov->a[ir][jr] = bufmaskblurreti->a[ir][jr]; bufprov->b[ir][jr] = bufmaskblurreti->b[ir][jr]; } } if (rad != 0.f) { // guidedFilter(guid, ble, ble, rad * 10.f / skip, 0.001, multiThread, 4); float blur = rad; blur = blur < 0.f ? -1.f / blur : 1.f + blur; int r1 = max(int(4 / skip * blur + 0.5), 1); int r2 = max(int(25 / skip * blur + 0.5), 1); double epsilmax = 0.005; double epsilmin = 0.00001; double aepsil = (epsilmax - epsilmin) / 100.f; double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * rad + bepsil; if (rad < 0.f) { epsil = 0.001; } rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); rtengine::guidedFilter(guid, ble, ble, r2, 0.2 * epsil, multiThread); } LUTf lutTonemaskreti(65536); calcGammaLut(gamm, slop, lutTonemaskreti); float radiusb = 1.f / skip; #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int ir = 0; ir < H_L; ir++) for (int jr = 0; jr < W_L; jr++) { float L_; float2 sincosval = xsincosf(hue[ir][jr]); bufmaskblurreti->L[ir][jr] = LIM01(ble[ir][jr]) * 32768.f; L_ = 2.f * bufmaskblurreti->L[ir][jr]; bufmaskblurreti->L[ir][jr] = lutTonemaskreti[L_]; bufmaskblurreti->a[ir][jr] = 32768.f * sincosval.y * blechro[ir][jr]; bufmaskblurreti->b[ir][jr] = 32768.f * sincosval.x * blechro[ir][jr]; } if (lmaskretilocalcurve && localmaskretiutili) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int ir = 0; ir < H_L; ir++) for (int jr = 0; jr < W_L; jr++) { bufmaskblurreti->L[ir][jr] = 0.5f * lmaskretilocalcurve[2.f * bufmaskblurreti->L[ir][jr]]; } } if (lap > 0.f && pde) { array2D mask; mask(W_L, H_L); float amount = LIM01(float(lap)/100.f); array2D LL(W_L, H_L, bufreti->L, ARRAY2D_BYREFERENCE); ImProcFunctions::laplacian(LL, mask, W_L, H_L, 25.f, 20000.f, amount, false); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif for (int i = 0; i < H_L; ++i) { for (int j = 0; j < W_L; ++j) { mask[i][j] = LIM01(mask[i][j]); } } for (int i = 0; i < 3; ++i) { boxblur(static_cast(mask), static_cast(mask), 5 / skip, W_L, H_L, false); } #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) #endif for (int i = 0; i < H_L; ++i) { for (int j = 0; j < W_L; ++j) { bufmaskblurreti->L[i][j] += CLIPLOC(100000.f * (mask[i][j]));//increase strongly result } } } if (delt) { float *rdE[H_L] ALIGNED16; float *rdEBuffer = new float[H_L * W_L]; for (int i = 0; i < H_L; i++) { rdE[i] = &rdEBuffer[i * W_L]; } deltaEforMask(rdE, W_L, H_L, bufreti, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, balance, balanceh); // printf("rde1=%f rde2=%f\n", rdE[1][1], rdE[100][100]); std::unique_ptr delta(new LabImage(W_L, H_L)); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int ir = 0; ir < H_L; ir++) for (int jr = 0; jr < W_L; jr++) { delta->L[ir][jr] = bufmaskblurreti->L[ir][jr] - bufprov->L[ir][jr]; delta->a[ir][jr] = bufmaskblurreti->a[ir][jr] - bufprov->a[ir][jr]; delta->b[ir][jr] = bufmaskblurreti->b[ir][jr] - bufprov->b[ir][jr]; bufmaskblurreti->L[ir][jr] = bufprov->L[ir][jr] + rdE[ir][jr] * delta->L[ir][jr]; bufmaskblurreti->a[ir][jr] = bufprov->a[ir][jr] + rdE[ir][jr] * delta->a[ir][jr]; bufmaskblurreti->b[ir][jr] = bufprov->b[ir][jr] + rdE[ir][jr] * delta->b[ir][jr]; } delete [] rdEBuffer; } /* if (lap > 0.f) { float *datain = new float[H_L * W_L]; float *data_tmp = new float[H_L * W_L]; #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { datain[y * W_L + x] = bufmaskblurreti->L[y][x]; } } if (!pde) { ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, W_L, H_L, 200.f * lap); } else { ImProcFunctions::retinex_pde(datain, data_tmp, W_L, H_L, 12.f * lap, 1.f, nullptr, 0, 0, 1); } #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { bufmaskblurreti->L[y][x] = data_tmp[y * W_L + x]; } } delete [] datain; delete [] data_tmp; } */ //blend #ifdef _OPENMP #pragma omp parallel #endif { gaussianBlur(bufmaskblurreti->L, bufmaskorigreti->L, W_L, H_L, radiusb); gaussianBlur(bufmaskblurreti->a, bufmaskorigreti->a, W_L, H_L, 1.f + (0.5f * rad) / skip); gaussianBlur(bufmaskblurreti->b, bufmaskorigreti->b, W_L, H_L, 1.f + (0.5f * rad) / skip); } float modr = 0.01f * (float) blend; if (llretiMask != 3 && retiMask) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { if (before == 0 && retiMasktmap) { out[y][x] += fabs(modr) * bufmaskorigreti->L[y][x]; out[y][x] = LIM(out[y][x], 0.f, 100000.f); } else { bufreti->L[y][x] += bufmaskorigreti->L[y][x] * modr; bufreti->L[y][x] = CLIPLOC(bufreti->L[y][x]); } bufreti->a[y][x] *= (1.f + bufmaskorigreti->a[y][x] * modr * (1.f + 0.01f * chro)); bufreti->b[y][x] *= (1.f + bufmaskorigreti->b[y][x] * modr * (1.f + 0.01f * chro)); bufreti->a[y][x] = CLIPC(bufreti->a[y][x]); bufreti->b[y][x] = CLIPC(bufreti->b[y][x]); } } } if (!retiMasktmap && retiMask) { //new original blur mask for deltaE #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { buforig->L[y][x] += (modr * bufmaskorigreti->L[y][x]); buforig->a[y][x] *= (1.f + modr * bufmaskorigreti->a[y][x]); buforig->b[y][x] *= (1.f + modr * bufmaskorigreti->b[y][x]); buforig->L[y][x] = CLIP(buforig->L[y][x]); buforig->a[y][x] = CLIPC(buforig->a[y][x]); buforig->b[y][x] = CLIPC(buforig->b[y][x]); buforig->L[y][x] = CLIP(buforig->L[y][x] - bufmaskorigreti->L[y][x]); buforig->a[y][x] = CLIPC(buforig->a[y][x] * (1.f - bufmaskorigreti->a[y][x])); buforig->b[y][x] = CLIPC(buforig->b[y][x] * (1.f - bufmaskorigreti->b[y][x])); } } float radius = 3.f / skip; #ifdef _OPENMP #pragma omp parallel if (multiThread) #endif { gaussianBlur(buforig->L, buforigmas->L, W_L, H_L, radius); gaussianBlur(buforig->a, buforigmas->a, W_L, H_L, radius); gaussianBlur(buforig->b, buforigmas->b, W_L, H_L, radius); } } if (llretiMask == 3) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H_L; y++) { for (int x = 0; x < W_L; x++) { bufmask->L[y][x] = (lumask * 400.f) + CLIPLOC(bufmaskorigreti->L[y][x]); bufmask->a[y][x] = CLIPC(bufreti->a[y][x] * bufmaskorigreti->a[y][x]); bufmask->b[y][x] = CLIPC(bufreti->b[y][x] * bufmaskorigreti->b[y][x]); } } } } void ImProcFunctions::MSRLocal(int call, int sp, bool fftw, int lum, float** reducDE, LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, LabImage * bufmaskorigreti, float** luminance, const float* const *originalLuminance, const int width, const int height, int bfwr, int bfhr, const procparams::LocallabParams &loc, const int skip, const LocretigainCurve &locRETgainCcurve, const LocretitransCurve &locRETtransCcurve, const int chrome, const int scall, const float krad, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, const LocCCmaskCurve & locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool lhmasretiutili, int llretiMask, const LUTf & lmaskretilocalcurve, bool localmaskretiutili, LabImage * transformed, bool retiMasktmap, bool retiMask, bool delt, const float hueref, const float chromaref, const float lumaref, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask) { BENCHFUN float mean, stddv, maxtr, mintr; mean = 0.f; stddv = 0.f; maxtr = 0.f; mintr = 0.f; constexpr float eps = 2.f; bool useHslLin = false; const float offse = loc.spots.at(sp).offs; const float chrT = (float)(loc.spots.at(sp).chrrt) / 100.f; const int scal = (loc.spots.at(sp).scalereti); float vart = loc.spots.at(sp).vart / 100.f;//variance const float strength = loc.spots.at(sp).str / 100.f; // Blend with original L channel data const float dar = loc.spots.at(sp).darkness; const float lig = loc.spots.at(sp).lightnessreti; float value = pow(strength, 0.4f); float value_1 = pow(strength, 0.3f); bool logli = loc.spots.at(sp).loglin; float limD = loc.spots.at(sp).limd;//10.f limD = pow(limD, 1.7f); //about 2500 enough float ilimD = 1.f / limD; float threslum = loc.spots.at(sp).limd; const float elogt = 2.71828f; if (!logli) { useHslLin = true; } //empirical skip evaluation : very difficult because quasi all parameters interfere //to test on several images int nei = (int)(krad * loc.spots.at(sp).neigh); // printf("neigh=%i\n", nei); //several test to find good values ???!!! //very difficult to do because 4 factor are correlate with skip and cannot been solved easily // size of spots // radius - neigh // scal // variance vart //not too bad proposition float divsca = 1.f; if (scal >= 3) { divsca = sqrt(scal / 3.f); } if (skip >= 4) { //nei = (int)(0.1f * nei + 2.f); //not too bad nei = (int)(nei / (1.5f * skip)) / divsca; vart *= sqrt(skip); } else if (skip > 1) { //nei = (int)(0.3f * nei + 2.f); nei = (int)(nei / skip) / divsca; vart *= sqrt(skip); } int moderetinex = 0; if (loc.spots.at(sp).retinexMethod == "uni") { moderetinex = 0; } else if (loc.spots.at(sp).retinexMethod == "low") { moderetinex = 1; } else if (loc.spots.at(sp).retinexMethod == "high") { moderetinex = 2; } const float high = 0.f; // Dummy to pass to retinex_scales(...) constexpr auto maxRetinexScales = 10; float RetinexScales[maxRetinexScales]; retinex_scales(RetinexScales, scal, moderetinex, nei, high); const int H_L = height; const int W_L = width; std::unique_ptr> srcBuffer(new JaggedArray(W_L, H_L)); float** src = *srcBuffer; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) for (int j = 0; j < W_L; j++) { src[i][j] = luminance[i][j] + eps; luminance[i][j] = 0.f; } JaggedArray out(W_L, H_L); float clipt = loc.spots.at(sp).cliptm; const float logBetaGain = xlogf(16384.f); float pond = logBetaGain / (float) scal; if (!useHslLin) { pond /= log(elogt); } float kr;//on FFTW float kg = 1.f;//on Gaussianblur for (int scale = scal - 1; scale >= 0; --scale) { // printf("retscale=%f scale=%i \n", mulradiusfftw * RetinexScales[scale], scale); //emprical adjustment between FFTW radius and Gaussainblur //under 50 ==> 10.f // 400 ==> 1.f float sigm = 1.f; if (settings->fftwsigma == false) { //empirical formula sigm = RetinexScales[scale]; float ak = -9.f / 350.f; float bk = 10.f - 50.f * ak; kr = ak * sigm + bk; if (sigm < 50.f) { kr = 10.f; } //above 400 at 5000 ==> 20.f if (sigm > 400.f) { //increase ==> 5000 float ka = 19.f / 4600.f; float kb = 1.f - 400 * ka; kr = ka * sigm + kb; float kga = -0.14f / 4600.f;//decrease float kgb = 1.f - 400.f * kga; kg = kga * sigm + kgb; if (sigm > 5000.f) { kr = ka * 5000.f + kb; kg = kga * 5000.f + kgb; } } } else {//sigma *= sigma kg = 1.f; kr = sigm; } if (!fftw) { // || (fftw && call != 2)) { if (scale == scal - 1) { gaussianBlur(src, out, W_L, H_L, kg * RetinexScales[scale], true); } else { // reuse result of last iteration // out was modified in last iteration => restore it gaussianBlur(out, out, W_L, H_L, sqrtf(SQR(kg * RetinexScales[scale]) - SQR(kg * RetinexScales[scale + 1])), true); } } else { if (scale == scal - 1) { if (settings->fftwsigma == false) { //empirical formula ImProcFunctions::fftw_convol_blur2(src, out, bfwr, bfhr, (kr * RetinexScales[scale]), 0, 0); } else { // FFT blur radius fixed in 5.12, resulting in different // blur amount. Here we preserve the original behavior by // multiplying the original sigma SQR(RetinexScales[scale]) // with 2 then taking the square root. const auto gaussianSigma = std::sqrt(2.f) * RetinexScales[scale]; ImProcFunctions::fftw_convol_blur2(src, out, bfwr, bfhr, gaussianSigma, 0, 0); } } else { // reuse result of last iteration // out was modified in last iteration => restore it if (settings->fftwsigma == false) { //empirical formula ImProcFunctions::fftw_convol_blur2(out, out, bfwr, bfhr, sqrtf(SQR(kr * RetinexScales[scale]) - SQR(kr * RetinexScales[scale + 1])), 0, 0); } else { // FFT blur radius fixed in 5.12, resulting in different // blur amount. Here we preserve the original behavior by // multiplying the original sigma // SQR(RetinexScales[scale]) - SQR(RetinexScales[scale + 1]) // with 2 then taking the square root. const auto gaussianSigma = std::sqrt(2 * (SQR(RetinexScales[scale]) - SQR(RetinexScales[scale + 1]))); ImProcFunctions::fftw_convol_blur2(out, out, bfwr, bfhr, gaussianSigma, 0, 0); } } } if (scale == 1) { //equalize last scale with darkness and lightness of course acts on TM! if (dar != 1.f || lig != 1.f) { #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < H_L; ++y) { for (int x = 0; x < W_L; ++x) { float buf = (src[y][x] - out[y][x]) * value; buf *= (buf > 0.f) ? lig : dar; out[y][x] = LIM(out[y][x] + buf, -100000.f, 100000.f); } } } } #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { int j = 0; #ifdef __SSE2__ const vfloat pondv = F2V(pond); const vfloat limMinv = F2V(ilimD); const vfloat limMaxv = F2V(limD); if (useHslLin) { for (; j < W_L - 3; j += 4) { STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv)); } } else { for (; j < W_L - 3; j += 4) { STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * xlogf(vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv))); } } #endif if (useHslLin) { for (; j < W_L; j++) { luminance[i][j] += pond * (LIM(src[i][j] / out[i][j], ilimD, limD)); } } else { for (; j < W_L; j++) { luminance[i][j] += pond * xlogf(LIM(src[i][j] / out[i][j], ilimD, limD)); } } } } if (scal == 1) {//only if user select scal = 1 const float threslow = threslum * 163.f; #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < H_L; ++y) { for (int x = 0; x < W_L; ++x) { const float srcVal = src[y][x]; const float kval = rtengine::min(srcVal / threslow, 1.f); float buf = (srcVal - out[y][x]) * value_1; buf *= (buf > 0.f) ? lig : dar; luminance[y][x] = LIM(srcVal + (1.f + kval) * buf, -32768.f, 32768.f); } } double avg = 0.f; #ifdef _OPENMP #pragma omp parallel for reduction(+:avg) #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { avg += luminance[i][j]; } } avg /= H_L * W_L; avg /= 32768.f; avg = LIM01(avg); float contreal = 0.5f * vart; DiagonalCurve reti_contrast({ DCT_NURBS, 0, 0, avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), avg + (1 - avg) * (0.6 - contreal / 250.0), avg + (1 - avg) * (0.6 + contreal / 250.0), 1, 1 }); #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { float buf = LIM01(luminance[i][j] / 32768.f); buf = reti_contrast.getVal(buf); buf *= 32768.f; luminance[i][j] = buf; } } } srcBuffer.reset(); float str = strength * (chrome == 0 ? 1.f : 0.8f * (chrT - 0.4f)); const float maxclip = (chrome == 0 ? 32768.f : 50000.f); if (scal != 1) { mean = 0.f; stddv = 0.f; mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); if (locRETtransCcurve && mean != 0.f && stddv != 0.f) { //if curve float asig = 0.166666f / stddv; float bsig = 0.5f - asig * mean; float amax = 0.333333f / (maxtr - mean - stddv); float bmax = 1.f - amax * maxtr; float amin = 0.333333f / (mean - stddv - mintr); float bmin = -amin * mintr; asig *= 500.f; bsig *= 500.f; amax *= 500.f; bmax *= 500.f; amin *= 500.f; bmin *= 500.f; #ifdef _OPENMP #pragma omp parallel #endif { float absciss; #ifdef _OPENMP #pragma omp for schedule(dynamic,16) #endif for (int i = 0; i < H_L; i++) for (int j = 0; j < W_L; j++) { //for mintr to maxtr evalate absciss in function of original transmission if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { absciss = asig * luminance[i][j] + bsig; } else if (luminance[i][j] >= mean) { absciss = amax * luminance[i][j] + bmax; } else { /*if(luminance[i][j] <= mean - stddv)*/ absciss = amin * luminance[i][j] + bmin; } //TODO : move multiplication by 4.f and subtraction of 1.f inside the curve luminance[i][j] *= (-1.f + 4.f * locRETtransCcurve[absciss]); //new transmission } } } mean = 0.f; stddv = 0.f; mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr);//new calculation of mean... float epsil = 0.1f; mini = mean - vart * stddv; if (mini < mintr) { mini = mintr + epsil; } maxi = mean + vart * stddv; if (maxi > maxtr) { maxi = maxtr - epsil; } float delta = maxi - mini; if (!delta) { delta = 1.0f; } float *copylum[H_L] ALIGNED16; float *copylumBuffer = new float[H_L * W_L]; for (int i = 0; i < H_L; i++) { copylum[i] = ©lumBuffer[i * W_L]; } float cdfactor = (clipt * 32768.f) / delta; maxCD = -9999999.f; minCD = 9999999.f; //prepare work for curve gain #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { luminance[i][j] = luminance[i][j] - mini; } } mean = 0.f; stddv = 0.f; mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); // printf("meanun=%f stdun=%f maxtr=%f mintr=%f\n", mean, stddv, maxtr, mintr); float asig = 0.f, bsig = 0.f, amax = 0.f, bmax = 0.f, amin = 0.f, bmin = 0.f; const bool hasRetGainCurve = locRETgainCcurve && mean != 0.f && stddv != 0.f; if (hasRetGainCurve) { //if curve asig = 0.166666f / stddv; bsig = 0.5f - asig * mean; amax = 0.333333f / (maxtr - mean - stddv); bmax = 1.f - amax * maxtr; amin = 0.333333f / (mean - stddv - mintr); bmin = -amin * mintr; asig *= 500.f; bsig *= 500.f; amax *= 500.f; bmax *= 500.f; amin *= 500.f; bmin *= 500.f; cdfactor *= 2.f; } #ifdef _OPENMP #pragma omp parallel #endif { // float absciss; float cdmax = -999999.f, cdmin = 999999.f; float gan = 0.5f; #ifdef _OPENMP #pragma omp for schedule(dynamic,16) #endif for (int i = 0; i < H_L; i ++) for (int j = 0; j < W_L; j++) { if (hasRetGainCurve) { float absciss; if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { absciss = asig * luminance[i][j] + bsig; } else if (luminance[i][j] >= mean) { absciss = amax * luminance[i][j] + bmax; } else { absciss = amin * luminance[i][j] + bmin; } gan = locRETgainCcurve[absciss]; //new gain function transmission } //but we don't update mean stddv for display only... copylum[i][j] = gan * luminance[i][j];//update data for display float cd = gan * cdfactor * luminance[i][j] + offse; cdmax = cd > cdmax ? cd : cdmax; cdmin = cd < cdmin ? cd : cdmin; luminance[i][j] = intp(str * reducDE[i][j], clipretinex(cd, 0.f, maxclip), originalLuminance[i][j]); } #ifdef _OPENMP #pragma omp critical #endif { maxCD = maxCD > cdmax ? maxCD : cdmax; minCD = minCD < cdmin ? minCD : cdmin; } } mean = 0.f; stddv = 0.f; mean_stddv2(copylum, mean, stddv, W_L, H_L, maxtr, mintr); delete [] copylumBuffer; copylumBuffer = nullptr; } else { #ifdef _OPENMP #pragma omp for schedule(dynamic,16) #endif for (int i = 0; i < H_L; i ++) for (int j = 0; j < W_L; j++) { luminance[i][j] = LIM(luminance[i][j], 0.f, maxclip) * str + (1.f - str) * originalLuminance[i][j]; } } float rad = loc.spots.at(sp).radmaskreti; float slop = loc.spots.at(sp).slomaskreti; float gamm = loc.spots.at(sp).gammaskreti; float blend = loc.spots.at(sp).blendmaskreti; float chro = loc.spots.at(sp).chromaskreti; float lap = loc.spots.at(sp).lapmaskreti; bool pde = params->locallab.spots.at(sp).laplac; if (lum == 1 && (llretiMask == 3 || llretiMask == 0 || llretiMask == 2 || llretiMask == 4)) { //only mask with luminance on last scale int before = 1; maskforretinex(sp, before, luminance, nullptr, W_L, H_L, skip, locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, retiMasktmap, retiMask, rad, lap, pde, gamm, slop, chro, blend, lmaskretilocalcurve, localmaskretiutili, bufreti, bufmask, buforig, buforigmas, bufmaskorigreti, multiThread, delt, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, balance, balanceh, lumask ); } //mask does not interfered with data displayed Tmean = mean; Tsigma = stddv; Tmin = mintr; Tmax = maxtr; } }