diff --git a/rtdata/languages/default b/rtdata/languages/default index ecaff0909..6e201e2f6 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -839,6 +839,7 @@ HISTORY_MSG_589;Local - Spot method HISTORY_MSG_590;Local - Spot Excluding scope HISTORY_MSG_591;Local - Spot Excluding struc HISTORY_MSG_592;Local - Warm Cool +HISTORY_MSG_593;Local - Noise lum detail HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1850,10 +1851,11 @@ TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Normal spot use recursive data.\nExcluding spot re TP_LOCALLAB_EXNORM;Normal spot TP_LOCALLAB_EXECLU;Excluding spot TP_LOCALLAB_EXPOSE;Exposure -TP_LOCALLAB_NOISELUMFINE;Luminance fine -TP_LOCALLAB_NOISELUMCOARSE;Luminance coarse -TP_LOCALLAB_NOISECHROFINE;Chroma fine -TP_LOCALLAB_NOISECHROCOARSE;Chroma coarse +TP_LOCALLAB_NOISELUMFINE;Luminance fine (Wav) +TP_LOCALLAB_NOISELUMCOARSE;Luminance coarse (Wav) +TP_LOCALLAB_NOISELUMDETAIL;Luminance detail (DCT) +TP_LOCALLAB_NOISECHROFINE;Chroma fine (Wav) +TP_LOCALLAB_NOISECHROCOARSE;Chroma coarse (Wav) TP_LOCALLAB_QUAL_METHOD;Global quality TP_LOCALLAB_QUALCURV_METHOD;Curves type TP_LOCALLAB_GAM;Gamma diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index e746aeb50..988706c19 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1147,6 +1147,7 @@ void Crop::update(int todo) params.locallab.sensiexclu = parent->sensiexclus[sp]; params.locallab.struc = parent->strucs[sp]; params.locallab.warm = parent->warms[sp]; + params.locallab.noiselumdetail = parent->noiselumdetails[sp]; std::vector cretie; @@ -1526,6 +1527,7 @@ void Crop::update(int todo) parent->sensiexclus[sp] = params.locallab.sensiexclu = parent->sensiexclus[0]; parent->strucs[sp] = params.locallab.struc = parent->strucs[0]; parent->warms[sp] = params.locallab.warm = parent->warms[0]; + parent->noiselumdetails[sp] = params.locallab.noiselumdetail = parent->noiselumdetails[0]; std::vector ccret; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 819f78935..6b666c35d 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -155,6 +155,7 @@ ImProcCoordinator::ImProcCoordinator() proxis(500, -10000), noiselumfs(500, -10000), noiselumcs(500, -10000), + noiselumdetails(500, -10000), noisechrofs(500, -10000), noisechrocs(500, -10000), mult0s(500, -10000), @@ -413,34 +414,34 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) } if (todo & (M_INIT | M_LINDENOISE | M_HDR)) { - MyMutex::MyLock initLock (minit); // Also used in crop window + MyMutex::MyLock initLock(minit); // Also used in crop window - imgsrc->HLRecovery_Global ( params.toneCurve); // this handles Color HLRecovery + imgsrc->HLRecovery_Global(params.toneCurve); // this handles Color HLRecovery if (settings->verbose) { - printf ("Applying white balance, color correction & sRBG conversion...\n"); + printf("Applying white balance, color correction & sRBG conversion...\n"); } - currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.equal, params.wb.method); + currWB = ColorTemp(params.wb.temperature, params.wb.green, params.wb.equal, params.wb.method); if (!params.wb.enabled) { currWB = ColorTemp(); } else if (params.wb.method == "Camera") { - currWB = imgsrc->getWB (); + currWB = imgsrc->getWB(); } else if (params.wb.method == "Auto") { if (lastAwbEqual != params.wb.equal || lastAwbTempBias != params.wb.tempBias) { double rm, gm, bm; - imgsrc->getAutoWBMultipliers (rm, gm, bm); + imgsrc->getAutoWBMultipliers(rm, gm, bm); if (rm != -1.) { - autoWB.update (rm, gm, bm, params.wb.equal, params.wb.tempBias); + autoWB.update(rm, gm, bm, params.wb.equal, params.wb.tempBias); lastAwbEqual = params.wb.equal; lastAwbTempBias = params.wb.tempBias; } else { lastAwbEqual = -1.; lastAwbTempBias = 0.0; - autoWB.useDefaults (params.wb.equal); + autoWB.useDefaults(params.wb.equal); } //double rr,gg,bb; @@ -451,25 +452,25 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) } if (params.wb.enabled) { - params.wb.temperature = currWB.getTemp (); - params.wb.green = currWB.getGreen (); + params.wb.temperature = currWB.getTemp(); + params.wb.green = currWB.getGreen(); } if (params.wb.method == "Auto" && awbListener && params.wb.enabled) { - awbListener->WBChanged (params.wb.temperature, params.wb.green); + awbListener->WBChanged(params.wb.temperature, params.wb.green); } - int tr = getCoarseBitMask (params.coarse); + int tr = getCoarseBitMask(params.coarse); - imgsrc->getFullSize (fw, fh, tr); + imgsrc->getFullSize(fw, fh, tr); // Will (re)allocate the preview's buffers - setScale (scale); - PreviewProps pp (0, 0, fw, fh, scale); + setScale(scale); + PreviewProps pp(0, 0, fw, fh, scale); // Tells to the ImProcFunctions' tools what is the preview scale, which may lead to some simplifications - ipf.setScale (scale); + ipf.setScale(scale); - imgsrc->getImage (currWB, tr, orig_prev, pp, params.toneCurve, params.raw); + imgsrc->getImage(currWB, tr, orig_prev, pp, params.toneCurve, params.raw); denoiseInfoStore.valid = false; //ColorTemp::CAT02 (orig_prev, ¶ms) ; // printf("orig_prevW=%d\n scale=%d",orig_prev->width, scale); @@ -514,9 +515,9 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) } } */ - imgsrc->convertColorSpace (orig_prev, params.icm, currWB); + imgsrc->convertColorSpace(orig_prev, params.icm, currWB); - ipf.firstAnalysis (orig_prev, params, vhist16); + ipf.firstAnalysis(orig_prev, params, vhist16); } readyphase++; @@ -857,7 +858,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) }; - int maxdata = 86;//85 10016;// 82 10015//78;//73 for 10011 + int maxdata = 87;//86 10017 //85 10016;// 82 10015//78;//73 for 10011 if (fic0) { //find current version mip @@ -901,7 +902,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) //initilize newues when first utilisation of Locallab. Prepare creation of Mip files for (int sp = 1; sp < maxspot; sp++) { // spots default int t_sp = sp; - int t_mipversion = 10017;//new value for each change + int t_mipversion = 10018;//new value for each change int t_circrad = 18; int t_locX = 250; int t_locY = 250; @@ -1022,6 +1023,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) //10017 int t_warm = 0; + //10018 + int t_noiselumdetail = 0; //all variables except locRETgainCurve 'coomon for all) fic << "Mipversion=" << t_mipversion << '@' << endl; @@ -1115,6 +1118,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) fic << "Sensiexclu=" << t_sensiexclu << '@' << endl; fic << "Struc=" << t_struc << '@' << endl; fic << "Warm=" << t_warm << '@' << endl; + fic << "Noiselumdetail=" << t_noiselumdetail << '@' << endl; fic << "curveReti=" << t_curvret << '@' << endl; fic << "curveLL=" << t_curvll << '@' << endl; @@ -1367,6 +1371,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) dataspot[79][0] = sensiexclus[0] = params.locallab.sensiexclu; dataspot[80][0] = strucs[0] = params.locallab.struc; dataspot[81][0] = warms[0] = params.locallab.warm; + dataspot[82][0] = noiselumdetails[0] = params.locallab.noiselumdetail; // for all curves work around - I do not know how to do with params curves... //curve Reti local @@ -1653,6 +1658,10 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) maxind = 80; } + if (versionmip == 10017) { + maxind = 81; + } + while (getline(fich, line)) { spotline = line; std::size_t pos = spotline.find("="); @@ -1874,6 +1883,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) } } + if (versionmip <= 10017) {// + for (int sp = 1; sp < maxspot; sp++) { // spots default + dataspot[82][sp] = 0; + } + } //here we change the number of spot @@ -1883,7 +1897,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) for (int sp = ns + 1 ; sp < maxspot; sp++) { // spots default int t_sp = sp; - int t_mipversion = 10017; + int t_mipversion = 10018; int t_circrad = 18; int t_locX = 250; int t_locY = 250; @@ -1996,6 +2010,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) //10017 int t_warm = 0; + //10018 + int t_noiselumdetail = 0; fic << "Mipversion=" << t_mipversion << '@' << endl; fic << "Spot=" << t_sp << '@' << endl; @@ -2085,6 +2101,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) fic << "Sensiexclu=" << t_sensiexclu << '@' << endl; fic << "Struc=" << t_struc << '@' << endl; fic << "Warm=" << t_warm << '@' << endl; + fic << "Noiselumdetail=" << t_noiselumdetail << '@' << endl; fic << "curveReti=" << t_curvret << '@' << endl; fic << "curveLL=" << t_curvll << '@' << endl; @@ -2434,6 +2451,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) params.locallab.sensiexclu = sensiexclus[sp] = dataspot[79][sp]; params.locallab.struc = strucs[sp] = dataspot[80][sp]; params.locallab.warm = warms[sp] = dataspot[81][sp]; + params.locallab.noiselumdetail = noiselumdetails[sp] = dataspot[82][sp]; int *s_datc; @@ -2975,6 +2993,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) dataspot[79][sp] = sensiexclus[sp] = params.locallab.sensiexclu = dataspot[79][0]; dataspot[80][sp] = strucs[sp] = params.locallab.struc = dataspot[80][0]; dataspot[81][sp] = warms[sp] = params.locallab.warm = dataspot[81][0]; + dataspot[82][sp] = noiselumdetails[sp] = params.locallab.noiselumdetail = dataspot[82][0]; int *s_datc; @@ -3006,7 +3025,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) sizellcs[sp] = sizl; std::vector cllend; - llstr[sp] = llstr[0]; + llstr[sp] = llstr[0]; for (int j = 0; j < sizl; j++) { llcurvs[sp * 500 + j] = s_datcl[j]; @@ -3216,7 +3235,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) for (int spe = 1; spe < maxspot; spe++) { int t_sp = spe; - int t_mipversion = 10017; + int t_mipversion = 10018; int t_circrad = dataspot[2][spe]; int t_locX = dataspot[3][spe]; int t_locY = dataspot[4][spe]; @@ -3304,6 +3323,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) int t_sensiexclu = dataspot[79][spe]; int t_struc = dataspot[80][spe]; int t_warm = dataspot[81][spe]; + int t_noiselumdetail = dataspot[82][spe]; int t_hueref = dataspot[maxdata - 4][spe]; int t_chromaref = dataspot[maxdata - 3][spe]; @@ -3410,6 +3430,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, Crop* cropCall) fou << "Sensiexclu=" << t_sensiexclu << '@' << endl; fou << "Struc=" << t_struc << '@' << endl; fou << "Warm=" << t_warm << '@' << endl; + fou << "Noiselumdetail=" << t_noiselumdetail << '@' << endl; fou << "hueref=" << t_hueref << '@' << endl; fou << "chromaref=" << t_chromaref << '@' << endl; diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 28ab288ea..7667a3b70 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -306,6 +306,7 @@ protected: LUTi proxis; LUTi noiselumfs; LUTi noiselumcs; + LUTi noiselumdetails; LUTi noisechrofs; LUTi noisechrocs; LUTi mult0s; diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index 2224af8e5..3760a0117 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include "rtengine.h" #include "improcfun.h" @@ -49,6 +50,14 @@ #define cliploc( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) +#define TSD 64 // Tile size +#define offset 25 // shift between tiles +#define fTS ((TS/2+1)) // second dimension of Fourier tiles +#define blkrad 1 // radius of block averaging + +#define epsilon 0.001f/(TS*TS) //tolerance + + #define CLIPC(a) ((a)>-42000?((a)<42000?(a):42000):-42000) // limit a and b to 130 probably enough ? #define CLIPL(x) LIM(x,0.f,40000.f) // limit L to about L=120 probably enough ? #define CLIPLOC(x) LIM(x,0.f,32767.f) @@ -134,6 +143,7 @@ struct local_params { int qualcurvemet; int blurmet; float noiself; + float noiseldetail; float noiselc; float noisecf; float noisecc; @@ -368,6 +378,7 @@ static void calcLocalParams(int oW, int oH, const LocallabParams& locallab, stru float local_noiself = locallab.noiselumf; float local_noiselc = locallab.noiselumc; + float local_noiseldetail = locallab.noiselumdetail; float local_noisecf = locallab.noisechrof; float local_noisecc = locallab.noisechroc; float multi[5]; @@ -450,7 +461,7 @@ static void calcLocalParams(int oW, int oH, const LocallabParams& locallab, stru lp.dyy = h * local_dyy; lp.thr = thre; lp.noiself = local_noiself; - lp.noiself = local_noiself; + lp.noiseldetail = local_noiseldetail; lp.noiselc = local_noiselc; lp.noisecf = local_noisecf; lp.noisecc = local_noisecc; @@ -7235,6 +7246,10 @@ void ImProcFunctions::calc_ref(LabImage * original, LabImage * transformed, int hueref = xatan2f(avB, avA); //mean hue chromaref = aveChro; lumaref = avL; + + if (lumaref > 95.f) {//to avoid crash + lumaref = 95.f; + } } } @@ -8487,8 +8502,58 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, if (call == 1) { LabImage tmp1(transformed->W, transformed->H); + array2D *Lin = nullptr; + + int GW = transformed->W; int GH = transformed->H; + int max_numblox_W = ceil((static_cast(GW)) / (offset)) + 2 * blkrad; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(GW)) / (offset)) + 2 * blkrad; + + // these are needed only for creation of the plans and will be freed before entering the parallel loop + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + array2D tilemask_in(TS, TS); + array2D tilemask_out(TS, TS); + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + 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))); + + int nfwd[2] = {TS, TS}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + + const int border = MAX(2, TS / 16); + + for (int i = 0; i < TS; ++i) { + float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < TS; ++j) { + float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + + } + } + + } + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; for (int ir = 0; ir < GH; ir++) for (int jr = 0; jr < GW; jr++) { @@ -8507,6 +8572,7 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, wavelet_decomposition bdecomp(tmp1.b[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, DaubLen); float madL[8][3]; + float madC[8][3]; int edge = 2; if (!Ldecomp.memoryAllocationFailed) { @@ -8529,7 +8595,7 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, edge = 2; vari[0] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); vari[1] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); vari[3] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); vari[4] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); @@ -8539,21 +8605,43 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, edge = 3; vari[0] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); vari[1] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[3] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[2] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); } if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (lp.noiselc < 30.f) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if (lp.noiselc < 50.f) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if (lp.noiselc < 70.f) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + vari[0] = max(0.0001f, vari[0]); vari[1] = max(0.0001f, vari[1]); vari[2] = max(0.0001f, vari[2]); - vari[3] = max(0.0001f, vari[3]); + vari[3] = max(0.0001f, kr3 * vari[3]); if (levred == 7) { - vari[4] = max(0.0001f, vari[4]); - vari[5] = max(0.0001f, vari[5]); - vari[6] = max(0.0001f, vari[6]); + vari[4] = max(0.0001f, kr4 * vari[4]); + vari[5] = max(0.0001f, kr5 * vari[5]); + vari[6] = max(0.0001f, kr5 * vari[6]); } float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL @@ -8565,6 +8653,23 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, float variC[levred]; if (!adecomp.memoryAllocationFailed && !bdecomp.memoryAllocationFailed) { + #pragma omp parallel for collapse(2) schedule(dynamic,1) + + for (int lvl = 0; lvl < levred; ++lvl) { + // compute median absolute deviation (MAD) of detail coefficients as robust noise estimator + for (int dir = 1; dir < 4; ++dir) { + + int Wlvl_ab = adecomp.level_W(lvl);//approximation with only "a" (better than L + int Hlvl_ab = adecomp.level_H(lvl); + + float ** WavCoeffs_ab = adecomp.level_coeffs(lvl); + madC[lvl][dir - 1] = SQR(Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab)); + } + + } + + + if (levred == 7) { edge = 2; variC[0] = SQR(lp.noisecf / 10.0); @@ -8610,18 +8715,191 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, } float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, false, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variC, edge, noisevarab_r, false, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madC, variC, edge, noisevarab_r, false, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madC, variC, edge, noisevarab_r, false, false, false, numThreads); delete[] noisevarchrom; } } if (!Ldecomp.memoryAllocationFailed) { + Lin = new array2D(GW, GH); + + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Lin)[i][j] = tmp1.L[i][j]; + } + } Ldecomp.reconstruct(tmp1.L[0]); } + if (!Ldecomp.memoryAllocationFailed) { + + const int numblox_W = ceil((static_cast(GW)) / (offset)) + 2 * blkrad; + const int numblox_H = ceil((static_cast(GH)) / (offset)) + 2 * blkrad; + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + //residual between input and denoised L channel + array2D Ldetail(GW, GH, ARRAY2D_CLEAR_DATA); + array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel //num_threads(denoiseNestedLevels) if (denoiseNestedLevels>1) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float blurbuffer[TS * TS] ALIGNED64; + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[GW + TS + 2 * blkrad * offset] ALIGNED16; + float nbrwt[TS * TS] ALIGNED64; +#ifdef _OPENMP + #pragma omp for +#endif + + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - blkrad) * offset; + float * datarow = pBuf + blkrad * offset; + + for (int i = 0; i < TS; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = MIN(-row, GH - 1); + } else if (row >= GH) { + rr = MAX(0, 2 * GH - 2 - row); + } + + for (int j = 0; j < tmp1.W; ++j) { + datarow[j] = ((*Lin)[rr][j] - tmp1.L[rr][j]); + } + + for (int j = -blkrad * offset; j < 0; ++j) { + datarow[j] = datarow[MIN(-j, GW - 1)]; + } + + for (int j = GW; j < GW + TS + blkrad * offset; ++j) { + datarow[j] = datarow[MAX(0, 2 * GW - 2 - j)]; + }//now we have a padded data row + + //now fill this row of the blocks with Lab high pass data + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - blkrad) * offset; + int indx = (hblk) * TS; //index of block in malloc + + if (top + i >= 0 && top + i < GH) { + int j; + + for (j = 0; j < min((-left), TS); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < min(TS, GW - left); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // now process the vblk row of blocks for noise reduction + float params_Ldetail = min(float(lp.noiseldetail), 99.9f); // max out to avoid div by zero when using noisevar_Ldetail as divisor + float noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + + + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + ImProcFunctions::RGBtile_denoise(fLblox, hblk, noisevar_Ldetail, nbrwt, blurbuffer); + }//end of horizontal block loop + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - blkrad) * offset; + + //add row of blocks to output image tile + ImProcFunctions::RGBoutput_tile_row(Lblox, Ldetail, tilemask_out, GH, GW, topproc); + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + }//end of vertical block loop + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#ifdef _OPENMP + + #pragma omp parallel for //num_threads(denoiseNestedLevels) if (denoiseNestedLevels>1) +#endif + + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + //may want to include masking threshold for large hipass data to preserve edges/detail + tmp1.L[i][j] += Ldetail[i][j] / totwt[i][j]; //note that labdn initially stores the denoised hipass data + } + } + + } + + delete Lin; + } + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); + + } + if (!adecomp.memoryAllocationFailed) { adecomp.reconstruct(tmp1.a[0]); @@ -8640,6 +8918,54 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, int bfw = int (lp.lx + lp.lxL) + del; LabImage bufwv(bfw, bfh); bufwv.clear(true); + array2D *Lin = nullptr; + int max_numblox_W = ceil((static_cast(bfw)) / (offset)) + 2 * blkrad; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(bfw)) / (offset)) + 2 * blkrad; + // these are needed only for creation of the plans and will be freed before entering the parallel loop + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + array2D tilemask_in(TS, TS); + array2D tilemask_out(TS, TS); + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + 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))); + + int nfwd[2] = {TS, TS}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + + const int border = MAX(2, TS / 16); + + for (int i = 0; i < TS; ++i) { + float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < TS; ++j) { + float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + + } + } + + } + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; + int begy = lp.yc - lp.lyT; int begx = lp.xc - lp.lxL; @@ -8694,7 +9020,7 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, edge = 2; vari[0] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); vari[1] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); vari[3] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); vari[4] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); @@ -8704,23 +9030,44 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, edge = 3; vari[0] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); vari[1] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[2] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); - vari[3] = 8.f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[2] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 8.f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); } if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (lp.noiselc < 30.f) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if (lp.noiselc < 50.f) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if (lp.noiselc < 70.f) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + vari[0] = max(0.0001f, vari[0]); vari[1] = max(0.0001f, vari[1]); vari[2] = max(0.0001f, vari[2]); - vari[3] = max(0.0001f, vari[3]); + vari[3] = max(0.0001f, kr3 * vari[3]); if (levred == 7) { - - vari[4] = max(0.0001f, vari[4]); - vari[5] = max(0.0001f, vari[5]); - vari[6] = max(0.0001f, vari[6]); + vari[4] = max(0.0001f, kr4 * vari[4]); + vari[5] = max(0.0001f, kr5 * vari[5]); + vari[6] = max(0.0001f, kr5 * vari[6]); } float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL @@ -8733,6 +9080,21 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, float variC[levred]; if (!adecomp.memoryAllocationFailed && !bdecomp.memoryAllocationFailed) { + float madC[8][3]; + #pragma omp parallel for collapse(2) schedule(dynamic,1) + + for (int lvl = 0; lvl < levred; ++lvl) { + // compute median absolute deviation (MAD) of detail coefficients as robust noise estimator + for (int dir = 1; dir < 4; ++dir) { + + int Wlvl_ab = adecomp.level_W(lvl);//approximation with only "a" (better than L + int Hlvl_ab = adecomp.level_H(lvl); + + float ** WavCoeffs_ab = adecomp.level_coeffs(lvl); + madC[lvl][dir - 1] = SQR(Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab)); + } + + } if (levred == 7) { edge = 2; @@ -8779,18 +9141,195 @@ void ImProcFunctions::Lab_Local(int call, float** shbuffer, LabImage * original, float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); - WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, false, false, false, numThreads); - WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variC, edge, noisevarab_r, false, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madC, variC, edge, noisevarab_r, false, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madC, variC, edge, noisevarab_r, false, false, false, numThreads); delete[] noisevarchrom; } } if (!Ldecomp.memoryAllocationFailed) { + Lin = new array2D(bfw, bfh); + // #pragma omp parallel for num_threads(numThreads) if (numThreads>1) + + + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Lin)[i][j] = bufwv.L[i][j]; + } + } Ldecomp.reconstruct(bufwv.L[0]); } + + if (!Ldecomp.memoryAllocationFailed) { + + const int numblox_W = ceil((static_cast(bfw)) / (offset)) + 2 * blkrad; + const int numblox_H = ceil((static_cast(bfh)) / (offset)) + 2 * blkrad; + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + //residual between input and denoised L channel + array2D Ldetail(bfw, bfh, ARRAY2D_CLEAR_DATA); + //pixel weight + array2D totwt(bfw, bfh, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel //num_threads(denoiseNestedLevels) if (denoiseNestedLevels>1) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float blurbuffer[TS * TS] ALIGNED64; + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[bfw + TS + 2 * blkrad * offset] ALIGNED16; + float nbrwt[TS * TS] ALIGNED64; +#ifdef _OPENMP + #pragma omp for +#endif + + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - blkrad) * offset; + float * datarow = pBuf + blkrad * offset; + + for (int i = 0; i < TS; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = MIN(-row, bfh - 1); + } else if (row >= bfh) { + rr = MAX(0, 2 * bfh - 2 - row); + } + + for (int j = 0; j < bufwv.W; ++j) { + datarow[j] = ((*Lin)[rr][j] - bufwv.L[rr][j]); + } + + for (int j = -blkrad * offset; j < 0; ++j) { + datarow[j] = datarow[MIN(-j, bfw - 1)]; + } + + for (int j = bfw; j < bfw + TS + blkrad * offset; ++j) { + datarow[j] = datarow[MAX(0, 2 * bfw - 2 - j)]; + }//now we have a padded data row + + //now fill this row of the blocks with Lab high pass data + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - blkrad) * offset; + int indx = (hblk) * TS; //index of block in malloc + + if (top + i >= 0 && top + i < bfh) { + int j; + + for (j = 0; j < min((-left), TS); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < min(TS, bfw - left); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // now process the vblk row of blocks for noise reduction + float params_Ldetail = min(float(lp.noiseldetail), 99.9f); // max out to avoid div by zero when using noisevar_Ldetail as divisor + float noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + + + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + ImProcFunctions::RGBtile_denoise(fLblox, hblk, noisevar_Ldetail, nbrwt, blurbuffer); + }//end of horizontal block loop + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - blkrad) * offset; + + //add row of blocks to output image tile + ImProcFunctions::RGBoutput_tile_row(Lblox, Ldetail, tilemask_out, bfh, bfw, topproc); + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + }//end of vertical block loop + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#ifdef _OPENMP + + #pragma omp parallel for //num_threads(denoiseNestedLevels) if (denoiseNestedLevels>1) +#endif + + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + //may want to include masking threshold for large hipass data to preserve edges/detail + bufwv.L[i][j] += Ldetail[i][j] / totwt[i][j]; //note that labdn initially stores the denoised hipass data + } + } + + } + + delete Lin; + } + + if ((lp.noiself > 0.1f || lp.noiselc > 0.1f)) { + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); + + } + if (!adecomp.memoryAllocationFailed) { adecomp.reconstruct(bufwv.a[0]); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 479374b08..75e150483 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -619,6 +619,7 @@ enum ProcEvent { Evlocallabsensiexclu = 589, Evlocallabstruc = 590, Evlocallabwarm = 591, + Evlocallabnoiselumdetail = 592, NUMOFEVENTS diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index fb2a0c998..c1e311f9e 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1784,6 +1784,7 @@ bool ChannelMixerParams::operator ==(const ChannelMixerParams& other) const if (enabled != other.enabled) { return false; } + for (unsigned int i = 0; i < 3; ++i) { if ( red[i] != other.red[i] @@ -2373,6 +2374,7 @@ pastsattog(true), sensiv(19), noiselumf(0), noiselumc(0), +noiselumdetail(0), noisechrof(0), noisechroc(0), sharradius(40), @@ -2505,6 +2507,7 @@ bool LocallabParams::operator ==(const LocallabParams& other) const && excurve == other.excurve && noiselumf == other.noiselumf && noiselumc == other.noiselumc + && noiselumdetail == other.noiselumdetail && noisechrof == other.noisechrof && noisechroc == other.noisechroc && sharradius == other.sharradius @@ -3116,6 +3119,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo // Channel mixer saveToKeyfile(!pedited || pedited->chmixer.enabled, "Channel Mixer", "Enabled", chmixer.enabled, keyFile); + if (!pedited || pedited->chmixer.red[0] || pedited->chmixer.red[1] || pedited->chmixer.red[2]) { Glib::ArrayHandle rmix(chmixer.red, 3, Glib::OWNERSHIP_NONE); keyFile.set_integer_list("Channel Mixer", "Red", rmix); @@ -3478,6 +3482,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->locallab.excurve, "Locallab", "ExCurve", locallab.excurve, keyFile); saveToKeyfile(!pedited || pedited->locallab.noiselumf, "Locallab", "noiselumf", locallab.noiselumf, keyFile); saveToKeyfile(!pedited || pedited->locallab.noiselumc, "Locallab", "noiselumc", locallab.noiselumc, keyFile); + saveToKeyfile(!pedited || pedited->locallab.noiselumdetail, "Locallab", "noiselumdetail", locallab.noiselumdetail, keyFile); saveToKeyfile(!pedited || pedited->locallab.noisechrof, "Locallab", "noisechrof", locallab.noisechrof, keyFile); saveToKeyfile(!pedited || pedited->locallab.noisechroc, "Locallab", "noisechroc", locallab.noisechroc, keyFile); saveToKeyfile(!pedited || pedited->locallab.sharradius, "Locallab", "Sharradius", locallab.sharradius, keyFile); @@ -3941,19 +3946,21 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "HLRecovery", "Method", pedited, toneCurve.method, pedited->toneCurve.method); } - if (keyFile.has_group ("Channel Mixer")) { + if (keyFile.has_group("Channel Mixer")) { if (ppVersion >= 329) { assignFromKeyfile(keyFile, "Channel Mixer", "Enabled", pedited, chmixer.enabled, pedited->chmixer.enabled); } else { chmixer.enabled = true; + if (pedited) { pedited->chmixer.enabled = true; } } - if (keyFile.has_key ("Channel Mixer", "Red") && keyFile.has_key ("Channel Mixer", "Green") && keyFile.has_key ("Channel Mixer", "Blue")) { - const std::vector rmix = keyFile.get_integer_list ("Channel Mixer", "Red"); - const std::vector gmix = keyFile.get_integer_list ("Channel Mixer", "Green"); - const std::vector bmix = keyFile.get_integer_list ("Channel Mixer", "Blue"); + + if (keyFile.has_key("Channel Mixer", "Red") && keyFile.has_key("Channel Mixer", "Green") && keyFile.has_key("Channel Mixer", "Blue")) { + const std::vector rmix = keyFile.get_integer_list("Channel Mixer", "Red"); + const std::vector gmix = keyFile.get_integer_list("Channel Mixer", "Green"); + const std::vector bmix = keyFile.get_integer_list("Channel Mixer", "Blue"); if (rmix.size() == 3 && gmix.size() == 3 && bmix.size() == 3) { memcpy(chmixer.red, rmix.data(), 3 * sizeof(int)); @@ -4192,7 +4199,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Vibrance", "SkinTonesCurve", pedited, vibrance.skintonescurve, pedited->vibrance.skintonescurve); } - if (keyFile.has_group ("White Balance")) { + if (keyFile.has_group("White Balance")) { assignFromKeyfile(keyFile, "White Balance", "Enabled", pedited, wb.enabled, pedited->wb.enabled); assignFromKeyfile(keyFile, "White Balance", "Setting", pedited, wb.method, pedited->wb.method); assignFromKeyfile(keyFile, "White Balance", "Temperature", pedited, wb.temperature, pedited->wb.temperature); @@ -4529,6 +4536,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Shcompr", pedited, locallab.shcompr, pedited->locallab.shcompr); assignFromKeyfile(keyFile, "Locallab", "noiselumf", pedited, locallab.noiselumf, pedited->locallab.noiselumf); assignFromKeyfile(keyFile, "Locallab", "noiselumc", pedited, locallab.noiselumc, pedited->locallab.noiselumc); + assignFromKeyfile(keyFile, "Locallab", "noiselumdetail", pedited, locallab.noiselumdetail, pedited->locallab.noiselumdetail); assignFromKeyfile(keyFile, "Locallab", "noisechrof", pedited, locallab.noisechrof, pedited->locallab.noisechrof); assignFromKeyfile(keyFile, "Locallab", "noisechroc", pedited, locallab.noisechroc, pedited->locallab.noisechroc); assignFromKeyfile(keyFile, "Locallab", "Sharradius", pedited, locallab.sharradius, pedited->locallab.sharradius); @@ -5023,15 +5031,17 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } } - if (keyFile.has_group ("HSV Equalizer")) { + if (keyFile.has_group("HSV Equalizer")) { if (ppVersion >= 329) { assignFromKeyfile(keyFile, "HSV Equalizer", "Enabled", pedited, hsvequalizer.enabled, pedited->hsvequalizer.enabled); } else { hsvequalizer.enabled = true; + if (pedited) { pedited->hsvequalizer.enabled = true; } } + if (ppVersion >= 300) { assignFromKeyfile(keyFile, "HSV Equalizer", "HCurve", pedited, hsvequalizer.hcurve, pedited->hsvequalizer.hcurve); assignFromKeyfile(keyFile, "HSV Equalizer", "SCurve", pedited, hsvequalizer.scurve, pedited->hsvequalizer.scurve); @@ -5039,15 +5049,17 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } } - if (keyFile.has_group ("RGB Curves")) { + if (keyFile.has_group("RGB Curves")) { if (ppVersion >= 329) { assignFromKeyfile(keyFile, "RGB Curves", "Enabled", pedited, rgbCurves.enabled, pedited->rgbCurves.enabled); } else { rgbCurves.enabled = true; + if (pedited) { pedited->rgbCurves.enabled = true; } } + assignFromKeyfile(keyFile, "RGB Curves", "LumaMode", pedited, rgbCurves.lumamode, pedited->rgbCurves.lumamode); assignFromKeyfile(keyFile, "RGB Curves", "rCurve", pedited, rgbCurves.rcurve, pedited->rgbCurves.rcurve); assignFromKeyfile(keyFile, "RGB Curves", "gCurve", pedited, rgbCurves.gcurve, pedited->rgbCurves.gcurve); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 924352dfe..85f56de64 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -914,6 +914,7 @@ struct LocallabParams { int sensiv; int noiselumf; int noiselumc; + int noiselumdetail; int noisechrof; int noisechroc; int sharradius; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index cdb5449d7..ba24c7314 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -618,6 +618,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // Evlocallabexclumethod LUMINANCECURVE, // Evlocallabsensiexclu LUMINANCECURVE, // Evlocallabstruc - LUMINANCECURVE // Evlocallabwarm + LUMINANCECURVE, // Evlocallabwarm + LUMINANCECURVE // Evlocallabnoiselumdetail + }; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 83d8ab09e..3fcfe7e9e 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -233,7 +233,7 @@ private: if (!params.wb.enabled) { currWB = ColorTemp(); } else if (params.wb.method == "Camera") { - currWB = imgsrc->getWB (); + currWB = imgsrc->getWB(); } else if (params.wb.method == "Auto") { double rm, gm, bm; imgsrc->getAutoWBMultipliers(rm, gm, bm); @@ -1103,7 +1103,7 @@ private: } ifstream fich(datalab, ios::in); - int maxdata = 86; //85 10016 //82;//78;//73 10011 + int maxdata = 87; //86 10017//85 10016 //82;//78;//73 10011 if (fich && versionmip != 0) { std::string inser; @@ -1338,6 +1338,7 @@ private: dataspots[79][0] = params.locallab.sensiexclu; dataspots[80][0] = params.locallab.struc; dataspots[81][0] = params.locallab.warm; + dataspots[82][0] = params.locallab.noiselumdetail; dataspots[maxdata - 4][0] = 100.f * params.locallab.hueref; dataspots[maxdata - 3][0] = params.locallab.chromaref; @@ -1807,6 +1808,7 @@ private: params.locallab.sensiexclu = dataspots[79][sp]; params.locallab.struc = dataspots[80][sp]; params.locallab.warm = dataspots[81][sp]; + params.locallab.noiselumdetail = dataspots[82][sp]; params.locallab.hueref = ((float) dataspots[maxdata - 4][sp]) / 100.f; params.locallab.chromaref = dataspots[maxdata - 3][sp]; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 3ee4c1b23..82b1247ee 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -106,6 +106,7 @@ Locallab::Locallab(): sensisha(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIS"), 0, 100, 1, 19))), noiselumf(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINE"), 0, 100, 1, 0))), noiselumc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMCOARSE"), 0, 100, 1, 0))), + noiselumdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMDETAIL"), 0, 100, 1, 50))), noisechrof(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROFINE"), 0, 100, 1, 0))), noisechroc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROCOARSE"), 0, 100, 1, 0))), hueref(Gtk::manage(new Adjuster(M("TP_LOCALLAB_HUEREF"), -3.15, 3.15, 0.01, 0))), @@ -640,6 +641,7 @@ Locallab::Locallab(): noiselumf->setAdjusterListener(this); noiselumc->setAdjusterListener(this); + noiselumdetail->setAdjusterListener(this); noisechrof->setAdjusterListener(this); @@ -648,6 +650,7 @@ Locallab::Locallab(): denoisBox->pack_start(*noiselumf); denoisBox->pack_start(*noiselumc); + denoisBox->pack_start(*noiselumdetail); denoisBox->pack_start(*noisechrof); denoisBox->pack_start(*noisechroc); @@ -1301,6 +1304,7 @@ void Locallab::neutral_pressed() inverssha->set_active(false); noiselumf->resetValue(false); noiselumc->resetValue(false); + noiselumdetail->resetValue(false); noisechrof->resetValue(false); noisechroc->resetValue(false); @@ -1806,12 +1810,13 @@ bool Locallab::localComputed_() sensiexclu->setValue(nextdatasp[79]); struc->setValue(nextdatasp[80]); warm->setValue(nextdatasp[81]); + noiselumdetail->setValue(nextdatasp[82]); - double intermed = 0.01 * (double) nextdatasp[82]; + double intermed = 0.01 * (double) nextdatasp[83]; hueref->setValue(intermed); - chromaref->setValue(nextdatasp[83]); - lumaref->setValue(nextdatasp[84]); - sobelref->setValue(nextdatasp[85]); + chromaref->setValue(nextdatasp[84]); + lumaref->setValue(nextdatasp[85]); + sobelref->setValue(nextdatasp[86]); int *s_datc; s_datc = new int[70]; @@ -2095,7 +2100,7 @@ bool Locallab::localComputed_() void Locallab::localChanged(int **datasp, std::string datastr, std::string ll_str, std::string lh_str, std::string cc_str, std::string hh_str, std::string sk_str, std::string ps_str, std::string ex_str, int sp, int maxdat) { - for (int i = 2; i < 86; i++) { + for (int i = 2; i < 87; i++) { nextdatasp[i] = datasp[i][sp]; } @@ -2181,6 +2186,7 @@ void Locallab::read(const ProcParams* pp, const ParamsEdited* pedited) sensisha->setEditedState(pedited->locallab.sensisha ? Edited : UnEdited); noiselumf->setEditedState(pedited->locallab.noiselumf ? Edited : UnEdited); noiselumc->setEditedState(pedited->locallab.noiselumc ? Edited : UnEdited); + noiselumdetail->setEditedState(pedited->locallab.noiselumdetail ? Edited : UnEdited); noisechrof->setEditedState(pedited->locallab.noisechrof ? Edited : UnEdited); noisechroc->setEditedState(pedited->locallab.noisechroc ? Edited : UnEdited); @@ -2390,6 +2396,7 @@ void Locallab::read(const ProcParams* pp, const ParamsEdited* pedited) lastanbspot = pp->locallab.anbspot; noiselumf->setValue(pp->locallab.noiselumf); noiselumc->setValue(pp->locallab.noiselumc); + noiselumdetail->setValue(pp->locallab.noiselumdetail); noisechrof->setValue(pp->locallab.noisechrof); noisechroc->setValue(pp->locallab.noisechroc); expcolor->setEnabled(pp->locallab.expcolor); @@ -2815,6 +2822,7 @@ void Locallab::write(ProcParams* pp, ParamsEdited* pedited) pp->locallab.hlcomprthresh = (int)hlcomprthresh->getValue(); pp->locallab.shcompr = (int)shcompr->getValue(); pp->locallab.noiselumc = noiselumc->getIntValue(); + pp->locallab.noiselumdetail = noiselumdetail->getIntValue(); pp->locallab.noiselumf = noiselumf->getIntValue(); pp->locallab.noisechrof = noisechrof->getIntValue(); pp->locallab.noisechroc = noisechroc->getIntValue(); @@ -2927,6 +2935,7 @@ void Locallab::write(ProcParams* pp, ParamsEdited* pedited) pedited->locallab.noiselumf = noiselumf->getEditedState(); pedited->locallab.noiselumc = noiselumc->getEditedState(); + pedited->locallab.noiselumdetail = noiselumdetail->getEditedState(); pedited->locallab.noisechrof = noisechrof->getEditedState(); pedited->locallab.noisechroc = noisechroc->getEditedState(); pedited->locallab.sharradius = sharradius->getEditedState(); @@ -3450,7 +3459,7 @@ void Locallab::inversChanged() } else { sensi->show(); - warm->show(); + warm->show(); llCurveEditorG->show(); curvactiv->show(); qualitycurveMethod->show(); @@ -3698,6 +3707,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe noiselumf->setDefault(defParams->locallab.noiselumf); noiselumc->setDefault(defParams->locallab.noiselumc); + noiselumdetail->setDefault(defParams->locallab.noiselumdetail); noisechrof->setDefault(defParams->locallab.noisechrof); noisechroc->setDefault(defParams->locallab.noisechroc); sharradius->setDefault(defParams->locallab.sharradius); @@ -3772,6 +3782,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe noiselumf->setDefaultEditedState(pedited->locallab.noiselumf ? Edited : UnEdited); noiselumc->setDefaultEditedState(pedited->locallab.noiselumc ? Edited : UnEdited); + noiselumdetail->setDefaultEditedState(pedited->locallab.noiselumdetail ? Edited : UnEdited); noisechrof->setDefaultEditedState(pedited->locallab.noisechrof ? Edited : UnEdited); noisechroc->setDefaultEditedState(pedited->locallab.noisechroc ? Edited : UnEdited); sharradius->setDefaultEditedState(pedited->locallab.sharradius ? Edited : UnEdited); @@ -3845,6 +3856,7 @@ void Locallab::setDefaults(const ProcParams * defParams, const ParamsEdited * pe noiselumf->setDefaultEditedState(Irrelevant); noiselumc->setDefaultEditedState(Irrelevant); + noiselumdetail->setDefaultEditedState(Irrelevant); noisechrof->setDefaultEditedState(Irrelevant); noisechroc->setDefaultEditedState(Irrelevant); sharradius->setDefaultEditedState(Irrelevant); @@ -4018,6 +4030,8 @@ void Locallab::adjusterChanged(Adjuster * a, double newval) listener->panelChanged(Evlocallabnoiselumf, noiselumf->getTextValue()); } else if (a == noiselumc) { listener->panelChanged(Evlocallabnoiselumc, noiselumc->getTextValue()); + } else if (a == noiselumdetail) { + listener->panelChanged(Evlocallabnoiselumdetail, noiselumdetail->getTextValue()); } else if (a == noisechrof) { listener->panelChanged(Evlocallabnoisechrof, noisechrof->getTextValue()); } else if (a == noisechroc) { @@ -4204,6 +4218,7 @@ void Locallab::trimValues(rtengine::procparams::ProcParams * pp) noiselumf->trimValue(pp->locallab.noiselumf); noiselumc->trimValue(pp->locallab.noiselumc); + noiselumdetail->trimValue(pp->locallab.noiselumdetail); noisechrof->trimValue(pp->locallab.noisechrof); noisechroc->trimValue(pp->locallab.noisechroc); sharradius->trimValue(pp->locallab.sharradius); @@ -4288,6 +4303,7 @@ void Locallab::setBatchMode(bool batchMode) noiselumf->showEditedCB(); noiselumc->showEditedCB(); + noiselumdetail->showEditedCB(); noisechroc->showEditedCB(); noiselumf->showEditedCB(); sharradius->showEditedCB(); diff --git a/rtgui/locallab.h b/rtgui/locallab.h index d9bbff9ed..3834a6e02 100644 --- a/rtgui/locallab.h +++ b/rtgui/locallab.h @@ -115,6 +115,8 @@ private: Adjuster* const sensisha; Adjuster* const noiselumf; Adjuster* const noiselumc; + Adjuster* const noiselumdetail; + Adjuster* const noisechrof; Adjuster* const noisechroc; Adjuster* const hueref; @@ -231,7 +233,7 @@ private: - int nextdatasp[86]; + int nextdatasp[87]; int nextlength; std::string nextstr; std::string nextstr2; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 7adcea9a3..384ab789e 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -347,6 +347,7 @@ void ParamsEdited::set(bool v) locallab.noiselumf = v; locallab.noiselumc = v; + locallab.noiselumdetail = v; locallab.noisechrof = v; locallab.noisechroc = v; locallab.sharradius = v; @@ -1010,6 +1011,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.noiselumf = locallab.noiselumf && p.locallab.noiselumf == other.locallab.noiselumf; locallab.noiselumc = locallab.noiselumc && p.locallab.noiselumc == other.locallab.noiselumc; + locallab.noiselumdetail = locallab.noiselumdetail && p.locallab.noiselumdetail == other.locallab.noiselumdetail; locallab.noisechrof = locallab.noisechrof && p.locallab.noisechrof == other.locallab.noisechrof; locallab.noisechroc = locallab.noisechroc && p.locallab.noisechroc == other.locallab.noisechroc; locallab.sharradius = locallab.sharradius && p.locallab.sharradius == other.locallab.sharradius; @@ -1308,7 +1310,7 @@ void ParamsEdited::initFrom(const std::vector& dirpyrequalizer.skinprotect = dirpyrequalizer.skinprotect && p.dirpyrequalizer.skinprotect == other.dirpyrequalizer.skinprotect; // dirpyrequalizer.algo = dirpyrequalizer.algo && p.dirpyrequalizer.algo == other.dirpyrequalizer.algo; dirpyrequalizer.hueskin = dirpyrequalizer.hueskin && p.dirpyrequalizer.hueskin == other.dirpyrequalizer.hueskin; - hsvequalizer.enabled = hsvequalizer.enabled && p.hsvequalizer.enabled == other.hsvequalizer.enabled; + hsvequalizer.enabled = hsvequalizer.enabled && p.hsvequalizer.enabled == other.hsvequalizer.enabled; hsvequalizer.hcurve = hsvequalizer.hcurve && p.hsvequalizer.hcurve == other.hsvequalizer.hcurve; hsvequalizer.scurve = hsvequalizer.scurve && p.hsvequalizer.scurve == other.hsvequalizer.scurve; hsvequalizer.vcurve = hsvequalizer.vcurve && p.hsvequalizer.vcurve == other.hsvequalizer.vcurve; @@ -1896,7 +1898,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (wb.enabled) { toEdit.wb.enabled = mods.wb.enabled; } - + if (wb.method) { toEdit.wb.method = mods.wb.method; } @@ -2616,6 +2618,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.noiselumc = mods.locallab.noiselumc; } + if (locallab.noiselumdetail) { + toEdit.locallab.noiselumdetail = mods.locallab.noiselumdetail; + } + if (locallab.noisechrof) { toEdit.locallab.noisechrof = mods.locallab.noisechrof; } @@ -2837,7 +2843,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (chmixer.enabled) { toEdit.chmixer.enabled = mods.chmixer.enabled; } - + for (int i = 0; i < 3; i++) { if (chmixer.red[i]) { toEdit.chmixer.red[i] = dontforceSet && options.baBehav[ADDSET_CHMIXER] ? toEdit.chmixer.red[i] + mods.chmixer.red[i] : mods.chmixer.red[i]; @@ -3660,7 +3666,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (hsvequalizer.enabled) { toEdit.hsvequalizer.enabled = mods.hsvequalizer.enabled; } - + if (hsvequalizer.hcurve) { toEdit.hsvequalizer.hcurve = mods.hsvequalizer.hcurve; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 84d02c1a0..15d45d470 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -466,6 +466,7 @@ public: bool noiselumf; bool noiselumc; + bool noiselumdetail; bool noisechrof; bool noisechroc; bool sharradius;