/* * 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 . */ #include "rtengine.h" #include "colortemp.h" #include "imagesource.h" #include "improcfun.h" #include "curves.h" #include "iccstore.h" #include "clutstore.h" #include "processingjob.h" #include #include "../rtgui/options.h" #include "rawimagesource.h" #include "../rtgui/multilangmgr.h" #include "mytime.h" #include #include #include #undef THREAD_PRIORITY_NORMAL namespace rtengine { extern const Settings* settings; IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData, bool flush) { errorCode = 0; ProcessingJobImpl* job = static_cast (pjob); if (pl) { pl->setProgressStr ("PROGRESSBAR_PROCESSING"); pl->setProgress (0.0); } InitialImage* ii = job->initialImage; if (!ii) { ii = InitialImage::load (job->fname, job->isRaw, &errorCode); if (errorCode) { delete job; return nullptr; } } procparams::ProcParams& params = job->pparams; // acquire image from imagesource ImageSource* imgsrc = ii->getImageSource (); int tr = getCoarseBitMask (params.coarse); int fw, fh; imgsrc->getFullSize (fw, fh, tr); // check the crop params if (params.crop.x > fw || params.crop.y > fh) { // the crop is completely out of the image, so we disable the crop params.crop.enabled = false; // and we set the values to the defaults params.crop.x = 0; params.crop.y = 0; params.crop.w = fw; params.crop.h = fh; } else { if (params.crop.x < 0) { params.crop.x = 0; } if (params.crop.y < 0) { params.crop.y = 0; } if ((params.crop.x + params.crop.w) > fw) { // crop overflow in the width dimension ; we trim it params.crop.w = fw - params.crop.x; } if ((params.crop.y + params.crop.h) > fh) { // crop overflow in the height dimension ; we trim it params.crop.h = fh - params.crop.y; } } // MyTime t1,t2; // t1.set(); ImProcFunctions ipf (¶ms, true); PreviewProps pp (0, 0, fw, fh, 1); imgsrc->preprocess ( params.raw, params.lensProf, params.coarse, params.dirpyrDenoise.enabled); if (params.toneCurve.autoexp) {// this enabled HLRecovery LUTu histRedRaw (256), histGreenRaw (256), histBlueRaw (256); imgsrc->getRAWHistogram (histRedRaw, histGreenRaw, histBlueRaw); if (ToneCurveParams::HLReconstructionNecessary (histRedRaw, histGreenRaw, histBlueRaw) && !params.toneCurve.hrenabled) { params.toneCurve.hrenabled = true; // WARNING: Highlight Reconstruction is being forced 'on', should we force a method here too? } } if (pl) { pl->setProgress (0.20); } imgsrc->demosaic ( params.raw); if (pl) { pl->setProgress (0.30); } if (params.retinex.enabled) { //enabled Retinex LUTf cdcurve (65536, 0); LUTf mapcurve (65536, 0); LUTu dummy; RetinextransmissionCurve dehatransmissionCurve; RetinexgaintransmissionCurve dehagaintransmissionCurve; bool dehacontlutili = false; bool mapcontlutili = false; bool useHsl = false; // multi_array2D conversionBuffer(1, 1); multi_array2D conversionBuffer (1, 1); imgsrc->retinexPrepareBuffers (params.icm, params.retinex, conversionBuffer, dummy); imgsrc->retinexPrepareCurves (params.retinex, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, dehacontlutili, mapcontlutili, useHsl, dummy, dummy ); float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; imgsrc->retinex ( params.icm, params.retinex, params.toneCurve, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, conversionBuffer, dehacontlutili, mapcontlutili, useHsl, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, dummy); } if (pl) { pl->setProgress (0.40); } imgsrc->HLRecovery_Global ( params.toneCurve ); if (pl) { pl->setProgress (0.45); } // set the color temperature ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.equal, params.wb.method); if (params.wb.method == "Camera") { currWB = imgsrc->getWB (); } else if (params.wb.method == "Auto") { double rm, gm, bm; imgsrc->getAutoWBMultipliers (rm, gm, bm); currWB.update (rm, gm, bm, params.wb.equal); } NoiseCurve noiseLCurve; NoiseCurve noiseCCurve; Imagefloat *calclum = nullptr ; params.dirpyrDenoise.getCurves (noiseLCurve, noiseCCurve); float autoNR = (float) settings->nrauto;// float autoNRmax = (float) settings->nrautomax;// int tilesize; int overlap; if (settings->leveldnti == 0) { tilesize = 1024; overlap = 128; } if (settings->leveldnti == 1) { tilesize = 768; overlap = 96; } // const int tilesize = 768; // const int overlap = 96; int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; ipf.Tile_calc (tilesize, overlap, 2, fw, fh, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); int nbtl = numtiles_W * numtiles_H; if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { nbtl = 9; } float *ch_M = new float [nbtl];//allocate memory float *max_r = new float [nbtl]; float *max_b = new float [nbtl]; float *min_b = new float [9]; float *min_r = new float [9]; float *lumL = new float [nbtl]; float *chromC = new float [nbtl]; float *ry = new float [nbtl]; float *sk = new float [nbtl]; float *pcsk = new float [nbtl]; // printf("expert=%d\n",settings->leveldnautsimpl); if (settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "PON") { MyTime t1pone, t2pone; t1pone.set(); int crW, crH; if (settings->leveldnv == 0) { crW = 100; crH = 100; } if (settings->leveldnv == 1) { crW = 250; crH = 250; } if (settings->leveldnv == 2) { crW = int (tileWskip / 2); crH = int (tileHskip / 2); } // if(settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int(1.15f*(tileWskip/2));}//adapted to scale of preview if (settings->leveldnv == 3) { crW = tileWskip - 10; crH = tileHskip - 10; } float lowdenoise = 1.f; int levaut = settings->leveldnaut; if (levaut == 1) { //Standard lowdenoise = 0.7f; } // int crW=tileWskip-10;//crop noise width // int crH=tileHskip-10;//crop noise height // Imagefloat *origCropPart;//init auto noise // origCropPart = new Imagefloat (crW, crH);//allocate memory if (params.dirpyrDenoise.enabled) {//evaluate Noise LUTf gamcurve (65536, 0); float gam, gamthresh, gamslope; ipf.RGB_denoise_infoGamCurve (params.dirpyrDenoise, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope); #pragma omp parallel { Imagefloat *origCropPart;//init auto noise origCropPart = new Imagefloat (crW, crH);//allocate memory Imagefloat *provicalc = new Imagefloat ((crW + 1) / 2, (crH + 1) / 2); //for denoise curves int skipP = 1; #pragma omp for schedule(dynamic) collapse(2) nowait for (int wcr = 0; wcr < numtiles_W; wcr++) { for (int hcr = 0; hcr < numtiles_H; hcr++) { int beg_tileW = wcr * tileWskip + tileWskip / 2.f - crW / 2.f; int beg_tileH = hcr * tileHskip + tileHskip / 2.f - crH / 2.f; PreviewProps ppP (beg_tileW , beg_tileH, crW, crH, skipP); imgsrc->getImage (currWB, tr, origCropPart, ppP, params.toneCurve, params.icm, params.raw ); // we only need image reduced to 1/4 here for (int ii = 0; ii < crH; ii += 2) { for (int jj = 0; jj < crW; jj += 2) { provicalc->r (ii >> 1, jj >> 1) = origCropPart->r (ii, jj); provicalc->g (ii >> 1, jj >> 1) = origCropPart->g (ii, jj); provicalc->b (ii >> 1, jj >> 1) = origCropPart->b (ii, jj); } } imgsrc->convertColorSpace (provicalc, params.icm, currWB); //for denoise luminance curve float maxr = 0.f; float maxb = 0.f; float pondcorrec = 1.0f; float chaut, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc; int Nb; chaut = 0.f; redaut = 0.f; blueaut = 0.f; maxredaut = 0.f; maxblueaut = 0.f; chromina = 0.f; sigma = 0.f; ipf.RGB_denoise_info (origCropPart, provicalc, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc); float multip = 1.f; float adjustr = 1.f; if (params.icm.working == "ProPhoto") { adjustr = 1.f; // } else if (params.icm.working == "Adobe RGB") { adjustr = 1.f / 1.3f; } else if (params.icm.working == "sRGB") { adjustr = 1.f / 1.3f; } else if (params.icm.working == "WideGamut") { adjustr = 1.f / 1.1f; } else if (params.icm.working == "Rec2020") { adjustr = 1.f / 1.1f; } else if (params.icm.working == "Beta RGB") { adjustr = 1.f / 1.2f; } else if (params.icm.working == "BestRGB") { adjustr = 1.f / 1.2f; } else if (params.icm.working == "BruceRGB") { adjustr = 1.f / 1.2f; } if (!imgsrc->isRAW()) { multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good fot gamma=1 } float maxmax = max (maxredaut, maxblueaut); float delta; int mode = 2; int lissage = settings->leveldnliss; ipf.calcautodn_info (chaut, delta, Nb, levaut, maxmax, lumema, chromina, mode, lissage, redyel, skinc, nsknc); // printf("PROCESS cha=%f red=%f bl=%f redM=%f bluM=%f chrom=%f sigm=%f lum=%f sigL=%f\n",chaut,redaut,blueaut, maxredaut, maxblueaut, chromina, sigma, lumema, sigma_L); if (maxredaut > maxblueaut) { maxr = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); if (minblueaut <= minredaut && minblueaut < chaut) { maxb = (-chaut + minblueaut) / (autoNRmax * multip * adjustr * lowdenoise); } } else { maxb = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); if (minredaut <= minblueaut && minredaut < chaut) { maxr = (-chaut + minredaut) / (autoNRmax * multip * adjustr * lowdenoise); } }//maxb mxr - empirical evaluation red / blue ch_M[hcr * numtiles_W + wcr] = pondcorrec * chaut / (autoNR * multip * adjustr * lowdenoise); max_r[hcr * numtiles_W + wcr] = pondcorrec * maxr; max_b[hcr * numtiles_W + wcr] = pondcorrec * maxb; lumL[hcr * numtiles_W + wcr] = lumema; chromC[hcr * numtiles_W + wcr] = chromina; ry[hcr * numtiles_W + wcr] = redyel; sk[hcr * numtiles_W + wcr] = skinc; pcsk[hcr * numtiles_W + wcr] = nsknc; } } delete provicalc; delete origCropPart; } int liss = settings->leveldnliss; //smooth result around mean if (liss == 2 || liss == 3) { // I smooth only mean and not delta (max) float nchm = 0.f; float koef = 0.4f; //between 0.1 to 0.9 if (liss == 3) { koef = 0.0f; //quasi auto for mean Ch } for (int wcr = 0; wcr < numtiles_W; wcr++) { for (int hcr = 0; hcr < numtiles_H; hcr++) { nchm += ch_M[hcr * numtiles_W + wcr]; } } nchm /= (numtiles_H * numtiles_W); for (int wcr = 0; wcr < numtiles_W; wcr++) { for (int hcr = 0; hcr < numtiles_H; hcr++) { ch_M[hcr * numtiles_W + wcr] = nchm + (ch_M[hcr * numtiles_W + wcr] - nchm) * koef; } } } if (liss == 3) { //same as auto but with much cells float MaxR = 0.f; float MaxB = 0.f; float MaxRMoy = 0.f; float MaxBMoy = 0.f; for (int k = 0; k < nbtl; k++) { MaxBMoy += max_b[k]; MaxRMoy += max_r[k]; if (max_r[k] > MaxR) { MaxR = max_r[k]; } if (max_b[k] > MaxB) { MaxB = max_b[k]; } } MaxBMoy /= nbtl; MaxRMoy /= nbtl; for (int k = 0; k < nbtl; k++) { if (MaxR > MaxB) { max_r[k] = MaxRMoy + (MaxR - MaxRMoy) * 0.66f; //#std Dev //max_b[k]=MinB; max_b[k] = MaxBMoy + (MaxB - MaxBMoy) * 0.66f; } else { max_b[k] = MaxBMoy + (MaxB - MaxBMoy) * 0.66f; //max_r[k]=MinR; max_r[k] = MaxRMoy + (MaxR - MaxRMoy) * 0.66f; } } } if (settings->verbose) { t2pone.set(); printf ("Info denoise ponderated performed in %d usec:\n", t2pone.etime (t1pone)); } } } if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { MyTime t1aue, t2aue; t1aue.set(); int crW, crH; if (settings->leveldnv == 0) { crW = 100; crH = 100; } if (settings->leveldnv == 1) { crW = 250; crH = 250; } if (settings->leveldnv == 2) { crW = int (tileWskip / 2); crH = int (tileHskip / 2); } // if(settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int(1.15f*(tileWskip/2));}//adapted to scale of preview if (settings->leveldnv == 3) { crW = tileWskip - 10; crH = tileHskip - 10; } float lowdenoise = 1.f; int levaut = settings->leveldnaut; if (levaut == 1) { //Standard lowdenoise = 0.7f; } if (params.dirpyrDenoise.enabled) {//evaluate Noise LUTf gamcurve (65536, 0); float gam, gamthresh, gamslope; ipf.RGB_denoise_infoGamCurve (params.dirpyrDenoise, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope); int Nb[9]; int coordW[3];//coordonate of part of image to mesure noise int coordH[3]; int begW = 50; int begH = 50; coordW[0] = begW; coordW[1] = fw / 2 - crW / 2; coordW[2] = fw - crW - begW; coordH[0] = begH; coordH[1] = fh / 2 - crH / 2; coordH[2] = fh - crH - begH; #pragma omp parallel { Imagefloat *origCropPart;//init auto noise origCropPart = new Imagefloat (crW, crH);//allocate memory Imagefloat *provicalc = new Imagefloat ((crW + 1) / 2, (crH + 1) / 2); //for denoise curves #pragma omp for schedule(dynamic) collapse(2) nowait for (int wcr = 0; wcr <= 2; wcr++) { for (int hcr = 0; hcr <= 2; hcr++) { PreviewProps ppP (coordW[wcr] , coordH[hcr], crW, crH, 1); imgsrc->getImage (currWB, tr, origCropPart, ppP, params.toneCurve, params.icm, params.raw); // we only need image reduced to 1/4 here for (int ii = 0; ii < crH; ii += 2) { for (int jj = 0; jj < crW; jj += 2) { provicalc->r (ii >> 1, jj >> 1) = origCropPart->r (ii, jj); provicalc->g (ii >> 1, jj >> 1) = origCropPart->g (ii, jj); provicalc->b (ii >> 1, jj >> 1) = origCropPart->b (ii, jj); } } imgsrc->convertColorSpace (provicalc, params.icm, currWB); //for denoise luminance curve int nb = 0; float chaut = 0.f, redaut = 0.f, blueaut = 0.f, maxredaut = 0.f, maxblueaut = 0.f, minredaut = 0.f, minblueaut = 0.f, chromina = 0.f, sigma = 0.f, lumema = 0.f, sigma_L = 0.f, redyel = 0.f, skinc = 0.f, nsknc = 0.f; ipf.RGB_denoise_info (origCropPart, provicalc, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, imgsrc->getDirPyrDenoiseExpComp(), chaut, nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc); Nb[hcr * 3 + wcr] = nb; ch_M[hcr * 3 + wcr] = chaut; max_r[hcr * 3 + wcr] = maxredaut; max_b[hcr * 3 + wcr] = maxblueaut; min_r[hcr * 3 + wcr] = minredaut; min_b[hcr * 3 + wcr] = minblueaut; lumL[hcr * 3 + wcr] = lumema; chromC[hcr * 3 + wcr] = chromina; ry[hcr * 3 + wcr] = redyel; sk[hcr * 3 + wcr] = skinc; pcsk[hcr * 3 + wcr] = nsknc; } } delete provicalc; delete origCropPart; } float chM = 0.f; float MaxR = 0.f; float MaxB = 0.f; float MinR = 100000000.f; float MinB = 100000000.f; float maxr = 0.f; float maxb = 0.f; float multip = 1.f; float adjustr = 1.f; float Max_R[9] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; float Max_B[9] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; float Min_R[9]; float Min_B[9]; float MaxRMoy = 0.f; float MaxBMoy = 0.f; float MinRMoy = 0.f; float MinBMoy = 0.f; if (params.icm.working == "ProPhoto") { adjustr = 1.f; } else if (params.icm.working == "Adobe RGB") { adjustr = 1.f / 1.3f; } else if (params.icm.working == "sRGB") { adjustr = 1.f / 1.3f; } else if (params.icm.working == "WideGamut") { adjustr = 1.f / 1.1f; } else if (params.icm.working == "Rec2020") { adjustr = 1.f / 1.1f; } else if (params.icm.working == "Beta RGB") { adjustr = 1.f / 1.2f; } else if (params.icm.working == "BestRGB") { adjustr = 1.f / 1.2f; } else if (params.icm.working == "BruceRGB") { adjustr = 1.f / 1.2f; } if (!imgsrc->isRAW()) { multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good fot gamma=1 } float delta[9]; int mode = 1; int lissage = settings->leveldnliss; for (int k = 0; k < 9; k++) { float maxmax = max (max_r[k], max_b[k]); ipf.calcautodn_info (ch_M[k], delta[k], Nb[k], levaut, maxmax, lumL[k], chromC[k], mode, lissage, ry[k], sk[k], pcsk[k] ); // printf("ch_M=%f delta=%f\n",ch_M[k], delta[k]); } for (int k = 0; k < 9; k++) { if (max_r[k] > max_b[k]) { //printf("R delta=%f koef=%f\n",delta[k],autoNRmax*multip*adjustr*lowdenoise); Max_R[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); Min_B[k] = - (ch_M[k] - min_b[k]) / (autoNRmax * multip * adjustr * lowdenoise); Max_B[k] = 0.f; Min_R[k] = 0.f; } else { //printf("B delta=%f koef=%f\n",delta[k],autoNRmax*multip*adjustr*lowdenoise); Max_B[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); Min_R[k] = - (ch_M[k] - min_r[k]) / (autoNRmax * multip * adjustr * lowdenoise); Min_B[k] = 0.f; Max_R[k] = 0.f; } } for (int k = 0; k < 9; k++) { // printf("ch_M= %f Max_R=%f Max_B=%f min_r=%f min_b=%f\n",ch_M[k],Max_R[k], Max_B[k],Min_R[k], Min_B[k]); chM += ch_M[k]; MaxBMoy += Max_B[k]; MaxRMoy += Max_R[k]; MinRMoy += Min_R[k]; MinBMoy += Min_B[k]; if (Max_R[k] > MaxR) { MaxR = Max_R[k]; } if (Max_B[k] > MaxB) { MaxB = Max_B[k]; } if (Min_R[k] < MinR) { MinR = Min_R[k]; } if (Min_B[k] < MinB) { MinB = Min_B[k]; } } chM /= 9; MaxBMoy /= 9; MaxRMoy /= 9; MinBMoy /= 9; MinRMoy /= 9; if (MaxR > MaxB) { maxr = MaxRMoy + (MaxR - MaxRMoy) * 0.66f; //#std Dev // maxb=MinB; maxb = MinBMoy + (MinB - MinBMoy) * 0.66f; } else { maxb = MaxBMoy + (MaxB - MaxBMoy) * 0.66f; // maxr=MinR; maxr = MinRMoy + (MinR - MinRMoy) * 0.66f; } // printf("SIMPL cha=%f red=%f bl=%f \n",chM,maxr,maxb); params.dirpyrDenoise.chroma = chM / (autoNR * multip * adjustr); params.dirpyrDenoise.redchro = maxr; params.dirpyrDenoise.bluechro = maxb; } if (settings->verbose) { t2aue.set(); printf ("Info denoise auto performed in %d usec:\n", t2aue.etime (t1aue)); } //end evaluate noise } Imagefloat* baseImg = new Imagefloat (fw, fh); imgsrc->getImage (currWB, tr, baseImg, pp, params.toneCurve, params.icm, params.raw); if (pl) { pl->setProgress (0.50); } // LUTf Noisecurve (65536,0); //!!!// auto exposure!!! double expcomp = params.toneCurve.expcomp; int bright = params.toneCurve.brightness; int contr = params.toneCurve.contrast; int black = params.toneCurve.black; int hlcompr = params.toneCurve.hlcompr; int hlcomprthresh = params.toneCurve.hlcomprthresh; if (params.toneCurve.autoexp) { LUTu aehist; int aehistcompr; imgsrc->getAutoExpHistogram (aehist, aehistcompr); ipf.getAutoExp (aehist, aehistcompr, imgsrc->getDefGain(), params.toneCurve.clip, expcomp, bright, contr, black, hlcompr, hlcomprthresh); } // at this stage, we can flush the raw data to free up quite an important amount of memory // commented out because it makes the application crash when batch processing... // TODO: find a better place to flush rawData and rawRGB if (flush) { imgsrc->flushRawData(); imgsrc->flushRGB(); } // perform luma/chroma denoise // CieImage *cieView; // NoisCurve noiseLCurve; // bool lldenoiseutili=false; // Imagefloat *calclum ; // params.dirpyrDenoise.getCurves(noiseLCurve, lldenoiseutili); // if (params.dirpyrDenoise.enabled && lldenoiseutili) { DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise; // make a copy because we cheat here if (denoiseParams.Lmethod == "CUR") { if (noiseLCurve) { denoiseParams.luma = 0.5f; } else { denoiseParams.luma = 0.0f; } } else if (denoiseParams.Lmethod == "SLI") { noiseLCurve.Reset(); } if (denoiseParams.enabled && (noiseLCurve || noiseCCurve )) { // we only need image reduced to 1/4 here calclum = new Imagefloat ((fw + 1) / 2, (fh + 1) / 2); //for luminance denoise curve #pragma omp parallel for for (int ii = 0; ii < fh; ii += 2) { for (int jj = 0; jj < fw; jj += 2) { calclum->r (ii >> 1, jj >> 1) = baseImg->r (ii, jj); calclum->g (ii >> 1, jj >> 1) = baseImg->g (ii, jj); calclum->b (ii >> 1, jj >> 1) = baseImg->b (ii, jj); } } imgsrc->convertColorSpace (calclum, params.icm, currWB); } if (denoiseParams.enabled) { // CurveFactory::denoiseLL(lldenoiseutili, denoiseParams.lcurve, Noisecurve,1); //denoiseParams.getCurves(noiseLCurve); // ipf.RGB_denoise(baseImg, baseImg, calclum, imgsrc->isRAW(), denoiseParams, params.defringe, imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, lldenoiseutili); float chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi; int kall = 2; ipf.RGB_denoise (kall, baseImg, baseImg, calclum, ch_M, max_r, max_b, imgsrc->isRAW(), denoiseParams, imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi); } // delete calclum; delete [] ch_M; delete [] max_r; delete [] max_b; delete [] min_r; delete [] min_b; delete [] lumL; delete [] chromC; delete [] ry; delete [] sk; delete [] pcsk; imgsrc->convertColorSpace (baseImg, params.icm, currWB); // perform first analysis LUTu hist16 (65536); ipf.firstAnalysis (baseImg, params, hist16); // perform transform (excepted resizing) if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getRotateDegree(), true); delete baseImg; baseImg = trImg; } if (params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled) { const int W = baseImg->getWidth(); const int H = baseImg->getHeight(); LabImage labcbdl (W, H); ipf.rgb2lab (*baseImg, labcbdl, params.icm.working); ipf.dirpyrequalizer (&labcbdl, 1); ipf.lab2rgb (labcbdl, *baseImg, params.icm.working); } // update blurmap SHMap* shmap = nullptr; if (params.sh.enabled) { shmap = new SHMap (fw, fh, true); double radius = sqrt (double (fw * fw + fh * fh)) / 2.0; double shradius = params.sh.radius; if (!params.sh.hq) { shradius *= radius / 1800.0; } shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 1); } // RGB processing LUTf curve1 (65536); LUTf curve2 (65536); LUTf curve (65536, 0); LUTf satcurve (65536, 0); LUTf lhskcurve (65536, 0); LUTf lumacurve (32770, 0); // lumacurve[32768] and lumacurve[32769] will be set to 32768 and 32769 later to allow linear interpolation LUTf clcurve (65536, 0); LUTf clToningcurve; LUTf cl2Toningcurve; LUTf wavclCurve (65536, 0); LUTf rCurve; LUTf gCurve; LUTf bCurve; LUTu dummy; ToneCurve customToneCurve1, customToneCurve2; ColorGradientCurve ctColorCurve; OpacityCurve ctOpacityCurve; ColorAppearance customColCurve1, customColCurve2, customColCurve3 ; ToneCurve customToneCurvebw1; ToneCurve customToneCurvebw2; //if(params.blackwhite.enabled) params.toneCurve.hrenabled=false; CurveFactory::complexCurve (expcomp, black / 65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr, params.toneCurve.curveMode, params.toneCurve.curve, params.toneCurve.curveMode2, params.toneCurve.curve2, hist16, curve1, curve2, curve, dummy, customToneCurve1, customToneCurve2 ); CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, 1); CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, 1); CurveFactory::RGBCurve (params.rgbCurves.bcurve, bCurve, 1); bool opautili = false; if (params.colorToning.enabled) { TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working); double wp[3][3] = { {wprof[0][0], wprof[0][1], wprof[0][2]}, {wprof[1][0], wprof[1][1], wprof[1][2]}, {wprof[2][0], wprof[2][1], wprof[2][2]} }; TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params.icm.working); double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, {wiprof[1][0], wiprof[1][1], wiprof[1][2]}, {wiprof[2][0], wiprof[2][1], wiprof[2][2]} }; params.colorToning.getCurves (ctColorCurve, ctOpacityCurve, wp, wip, opautili); clToningcurve (65536, 0); CurveFactory::curveToning (params.colorToning.clcurve, clToningcurve, 1); cl2Toningcurve (65536, 0); CurveFactory::curveToning (params.colorToning.cl2curve, cl2Toningcurve, 1); } LabImage* labView = new LabImage (fw, fh); if (params.blackwhite.enabled) { CurveFactory::curveBW (params.blackwhite.beforeCurve, params.blackwhite.afterCurve, hist16, dummy, customToneCurvebw1, customToneCurvebw2, 1); } double rrm, ggm, bbm; float autor, autog, autob; float satLimit = float (params.colorToning.satProtectionThreshold) / 100.f * 0.7f + 0.3f; float satLimitOpacity = 1.f - (float (params.colorToning.saturatedOpacity) / 100.f); if (params.colorToning.enabled && params.colorToning.autosat) { //for colortoning evaluation of saturation settings float moyS = 0.f; float eqty = 0.f; ipf.moyeqt (baseImg, moyS, eqty);//return image : mean saturation and standard dev of saturation float satp = ((moyS + 1.5f * eqty) - 0.3f) / 0.7f; //1.5 sigma ==> 93% pixels with high saturation -0.3 / 0.7 convert to Hombre scale if (satp >= 0.92f) { satp = 0.92f; //avoid values too high (out of gamut) } if (satp <= 0.15f) { satp = 0.15f; //avoid too low values } satLimit = 100.f * satp; satLimitOpacity = 100.f * (moyS - 0.85f * eqty); //-0.85 sigma==>20% pixels with low saturation } autor = -9000.f; // This will ask to compute the "auto" values for the B&W tool (have to be inferior to -5000) DCPProfile::ApplyState as; DCPProfile *dcpProf = imgsrc->getDCP (params.icm, currWB, as); LUTu histToneCurve; ipf.rgbProc (baseImg, labView, nullptr, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit , satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf, as, histToneCurve); if (settings->verbose) { printf ("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", autor, autog, autob); } // if clut was used and size of clut cache == 1 we free the memory used by the clutstore (default clut cache size = 1 for 32 bit OS) if ( params.filmSimulation.enabled && !params.filmSimulation.clutFilename.empty() && options.clutCacheSize == 1) { CLUTStore::getInstance().clearCache(); } // freeing up some memory customToneCurve1.Reset(); customToneCurve2.Reset(); ctColorCurve.Reset(); ctOpacityCurve.Reset(); noiseLCurve.Reset(); noiseCCurve.Reset(); customToneCurvebw1.Reset(); customToneCurvebw2.Reset(); // Freeing baseImg because not used anymore delete baseImg; baseImg = nullptr; if (shmap) { delete shmap; } shmap = nullptr; if (pl) { pl->setProgress (0.55); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // start tile processing...??? if (params.labCurve.contrast != 0) { //only use hist16 for contrast hist16.clear(); #ifdef _OPENMP #pragma omp parallel #endif { LUTu hist16thr (hist16.getSize()); // one temporary lookup table per thread hist16thr.clear(); #ifdef _OPENMP #pragma omp for schedule(static) nowait #endif for (int i = 0; i < fh; i++) for (int j = 0; j < fw; j++) { hist16thr[ (int) ((labView->L[i][j]))]++; } #pragma omp critical { hist16 += hist16thr; } } } bool utili; CurveFactory::complexLCurve (params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve, hist16, lumacurve, dummy, 1, utili); bool clcutili; CurveFactory::curveCL (clcutili, params.labCurve.clcurve, clcurve, 1); bool autili, butili, ccutili, cclutili; CurveFactory::complexsgnCurve (autili, butili, ccutili, cclutili, params.labCurve.acurve, params.labCurve.bcurve, params.labCurve.cccurve, params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 1); bool locutili = false; bool locallutili = false; bool localcutili = false; if (params.locallab.enabled) { MyTime t1, t2; t1.set(); //Glib::ustring datalab = imgsrc->getFileName() + ".mip"; // Glib::ustring pop = options.getUserProfilePath() + "/"; Glib::ustring pop = options.cacheBaseDir + "/mip/"; Glib::ustring datalab; if (options.mip == MI_opt) { datalab = pop + Glib::path_get_basename (imgsrc->getFileName () + ".mip"); } if (options.mip == MI_prev) { datalab = imgsrc->getFileName() + ".mip"; } LocretigainCurve locRETgainCurve; LocLHCurve loclhCurve; LocretigainCurverab locRETgainCurverab; LUTf lllocalcurve (65536, 0); LUTf cclocalcurve (65536, 0); int realspot = params.locallab.nbspot; int maxspot = settings->nspot + 1; ifstream fic0 (datalab, ios::in); float** shbuffer; int versionmip = 0; std::string delim[69] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "&", "#", "{", "[", "]", "}", "$", "*", "?", ">", "!", ";", "<"",(", ")", "+", "-" }; if (params.locallab.inverssha) { shbuffer = new float*[fh]; for (int i = 0; i < fh; i++) { shbuffer[i] = new float[fw]; } } if (fic0) {//normally we don't use here but ?? //find the version mip string line; string spotline; int cont = 0; while (getline (fic0, line)) { spotline = line; std::size_t pos = spotline.find ("="); std::size_t posend = spotline.find ("@"); //in case of for futur use if (spotline.substr (0, pos) == "Mipversion") { string strversion = spotline.substr (pos + 1, (posend - pos)); versionmip = std::stoi (strversion.c_str()); } if (spotline.substr (0, pos) == "Spot") { cont = 0; } } fic0.close(); } ifstream fich (datalab, ios::in); if (fich && versionmip != 0) { std::string inser; int **dataspots; dataspots = new int*[61]; for (int i = 0; i < 61; i++) { dataspots[i] = new int[maxspot]; } std::string *retistrs; retistrs = new std::string[maxspot]; std::string *llstrs; llstrs = new std::string[maxspot]; std::string *lhstrs; lhstrs = new std::string[maxspot]; std::string *ccstrs; ccstrs = new std::string[maxspot]; { dataspots[2][0] = params.locallab.circrad; dataspots[3][0] = params.locallab.locX; dataspots[4][0] = params.locallab.locY; dataspots[5][0] = params.locallab.locYT; dataspots[6][0] = params.locallab.locXL; dataspots[7][0] = params.locallab.centerX; dataspots[8][0] = params.locallab.centerY; dataspots[9][0] = params.locallab.lightness; dataspots[10][0] = params.locallab.contrast; dataspots[11][0] = params.locallab.chroma; dataspots[12][0] = params.locallab.sensi; dataspots[13][0] = params.locallab.transit; if (!params.locallab.invers) { dataspots[14][0] = 0; } else { dataspots[14][0] = 1; } if (params.locallab.Smethod == "IND") { dataspots[15][0] = 0; } else if (params.locallab.Smethod == "SYM") { dataspots[15][0] = 1; } else if (params.locallab.Smethod == "INDSL") { dataspots[15][0] = 2; } else if (params.locallab.Smethod == "SYMSL") { dataspots[15][0] = 3; } dataspots[17][0] = params.locallab.radius; dataspots[18][0] = params.locallab.strength; dataspots[19][0] = params.locallab.sensibn; if (!params.locallab.inversrad) { dataspots[20][0] = 0; } else { dataspots[20][0] = 1; } dataspots[21][0] = params.locallab.str; dataspots[22][0] = params.locallab.chrrt; dataspots[23][0] = params.locallab.neigh; dataspots[24][0] = params.locallab.vart; dataspots[25][0] = params.locallab.sensih; if (!params.locallab.inversret) { dataspots[26][0] = 0; } else { dataspots[26][0] = 1; } if (params.locallab.retinexMethod == "low") { dataspots[27][0] = 0; } else if (params.locallab.retinexMethod == "uni") { dataspots[27][0] = 1; } else if (params.locallab.retinexMethod == "high") { dataspots[27][0] = 2; } dataspots[28][0] = params.locallab.sharradius; dataspots[29][0] = params.locallab.sharamount; dataspots[30][0] = params.locallab.shardamping; dataspots[31][0] = params.locallab.shariter; dataspots[32][0] = params.locallab.sensisha; if (!params.locallab.inverssha) { dataspots[33][0] = 0; } else { dataspots[33][0] = 1; } if (params.locallab.qualityMethod == "std") { dataspots[34][0] = 0; } else if (params.locallab.qualityMethod == "enh") { dataspots[34][0] = 1; } else if (params.locallab.qualityMethod == "enhden") { dataspots[34][0] = 2; } dataspots[35][0] = params.locallab.thres; dataspots[36][0] = params.locallab.proxi; dataspots[37][0] = params.locallab.noiselumf; dataspots[38][0] = params.locallab.noiselumc; dataspots[39][0] = params.locallab.noisechrof; dataspots[40][0] = params.locallab.noisechroc; dataspots[41][0] = params.locallab.mult[0]; dataspots[42][0] = params.locallab.mult[1]; dataspots[43][0] = params.locallab.mult[2]; dataspots[44][0] = params.locallab.mult[3]; dataspots[45][0] = params.locallab.mult[4]; dataspots[46][0] = params.locallab.threshold; dataspots[47][0] = params.locallab.sensicb; if (!params.locallab.activlum) { dataspots[48][0] = 0; } else { dataspots[48][0] = 1; } dataspots[49][0] = params.locallab.stren; dataspots[50][0] = params.locallab.gamma; dataspots[51][0] = params.locallab.estop; dataspots[52][0] = params.locallab.scaltm; dataspots[53][0] = params.locallab.rewei; dataspots[54][0] = params.locallab.sensitm; dataspots[55][0] = params.locallab.retrab; if (!params.locallab.curvactiv) { dataspots[56][0] = 0; } else { dataspots[56][0] = 1; } if (params.locallab.qualitycurveMethod == "none") { dataspots[57][0] = 0; } else if (params.locallab.qualitycurveMethod == "std") { dataspots[57][0] = 1; } else if (params.locallab.qualitycurveMethod == "enh") { dataspots[57][0] = 2; } dataspots[58][0] = 100.f * params.locallab.hueref; dataspots[59][0] = params.locallab.chromaref; dataspots[60][0] = params.locallab.lumaref; //curve Reti local int siz = params.locallab.localTgaincurve.size(); if (siz > 69) { siz = 69;//avoid crash } int s_cur[siz + 1]; int s_datcur[siz + 1]; for (int j = 0; j < siz; j++) { s_datcur[j] = (int) (1000. * params.locallab.localTgaincurve[j]); } std::string cur_str = ""; for (int j = 0; j < siz; j++) { cur_str = cur_str + std::to_string (s_datcur[j]) + delim[j]; } inser = retistrs[0] = cur_str + "@"; int sizl = params.locallab.llcurve.size(); if (sizl > 69) { sizl = 69; } int s_curl[sizl + 1]; int s_datcurl[sizl + 1]; for (int j = 0; j < sizl; j++) { s_datcurl[j] = (int) (1000. * params.locallab.llcurve[j]); } std::string ll_str = ""; for (int j = 0; j < sizl; j++) { ll_str = ll_str + std::to_string (s_datcurl[j]) + delim[j]; } llstrs[0] = ll_str + "@"; int sizc = params.locallab.cccurve.size(); if (sizc > 69) { sizc = 69; } int s_curc[sizc + 1]; int s_datcurc[sizc + 1]; for (int j = 0; j < sizc; j++) { s_datcurc[j] = (int) (1000. * params.locallab.cccurve[j]); } std::string cc_str = ""; for (int j = 0; j < sizc; j++) { cc_str = cc_str + std::to_string (s_datcurc[j]) + delim[j]; } ccstrs[0] = cc_str + "@"; // int sizh = params.locallab.LHcurve.size(); if (sizh > 69) { sizh = 69; } int s_curh[sizh + 1]; int s_datcurh[sizh + 1]; for (int j = 0; j < sizh; j++) { s_datcurh[j] = (int) (1000. * params.locallab.LHcurve[j]); } std::string lh_str = ""; for (int j = 0; j < sizl; j++) { lh_str = lh_str + std::to_string (s_datcurh[j]) + delim[j]; } lhstrs[0] = lh_str + "@"; } locallutili = false; int ns; int realsp = params.locallab.nbspot; if (fich) { std::string line; std::string spotline; int cont = 0; int sizecu; int sizell; int sizelh; int sizecc; while (getline (fich, line)) { spotline = line; std::size_t pos = spotline.find ("="); std::size_t posend = spotline.find ("@"); //in case of for futur use if (spotline.substr (0, pos) == "Mipversion") { std::string strversion = spotline.substr (pos + 1, (posend - pos)); versionmip = std::stoi (strversion.c_str()); } if (spotline.substr (0, pos) == "Spot") { cont = 0; } cont++; std::string str3 = spotline.substr (pos + 1, (posend - pos)); if (cont == 1) { ns = std::stoi (str3.c_str()); } if (cont >= 2 && cont < 16) { dataspots[cont][ns] = std::stoi (str3.c_str()); } if (spotline.substr (0, pos) == "Currentspot") { dataspots[16][0] = std::stoi (str3.c_str()); } if (cont > 16 && cont < 61) { dataspots[cont][ns] = std::stoi (str3.c_str()); } if (spotline.substr (0, pos) == "curveReti") { std::string curstr; int longecur; std::string strend = spotline.substr (posend - 1, 1); std::size_t posz = spotline.find (strend); int longe; for (int sl = 0; sl < 69; sl++) { if (delim[sl] == strend) { longe = sl + 1; } } retistrs[ns] = str3; sizecu = longe; // printf("lec simpl str=%s ns=%i si=%i\n", retistrs[ns].c_str(), ns, sizecu); } if (spotline.substr (0, pos) == "curveLL") { std::string curstrl; int longecurl; std::string strendl = spotline.substr (posend - 1, 1); std::size_t poszl = spotline.find (strendl); int longel; for (int sl = 0; sl < 69; sl++) { if (delim[sl] == strendl) { longel = sl + 1; } } llstrs[ns] = str3; sizell = longel; // printf("lecture strLL=%s ns=%i si=%i\n", llstr[ns].c_str(), ns, sizell); } if (spotline.substr (0, pos) == "curveLH") { std::string curstrh; int longecurh; std::string strendh = spotline.substr (posend - 1, 1); std::size_t poszh = spotline.find (strendh); int longeh; for (int sh = 0; sh < 69; sh++) { if (delim[sh] == strendh) { longeh = sh + 1; } } lhstrs[ns] = str3; sizelh = longeh; //printf("lecture strLH=%s ns=%i si=%i\n", lhstr[ns].c_str(), ns, sizelh); } if (spotline.substr (0, pos) == "curveCC") { std::string curstrc; int longecurc; std::string strendc = spotline.substr (posend - 1, 1); std::size_t poszc = spotline.find (strendc); int longec; for (int sh = 0; sh < 69; sh++) { if (delim[sh] == strendc) { longec = sh + 1; } } ccstrs[ns] = str3; sizecc = longec; //printf("lecture strCC=%s ns=%i si=%i\n", ccstr[ns].c_str(), ns, sizecc); } } fich.close(); } for (int sp = 1; sp < maxspot; sp++) { //spots default params.locallab.hueref = INFINITY; params.locallab.chromaref = INFINITY; params.locallab.lumaref = INFINITY; params.locallab.circrad = dataspots[2][sp]; params.locallab.locX = dataspots[3][sp]; params.locallab.locY = dataspots[4][sp]; params.locallab.locYT = dataspots[5][sp]; params.locallab.locXL = dataspots[6][sp]; params.locallab.centerX = dataspots[7][sp]; params.locallab.centerY = dataspots[8][sp]; params.locallab.lightness = dataspots[9][sp]; params.locallab.contrast = dataspots[10][sp]; params.locallab.chroma = dataspots[11][sp]; params.locallab.sensi = dataspots[12][sp]; params.locallab.transit = dataspots[13][sp]; if (dataspots[14][sp] == 0) { params.locallab.invers = false; } else { params.locallab.invers = true; } if (dataspots[15][sp] == 0) { params.locallab.Smethod = "IND" ; } else if (dataspots[15][sp] == 1) { params.locallab.Smethod = "SYM" ; } else if (dataspots[15][sp] == 2) { params.locallab.Smethod = "INDSL"; } else if (dataspots[15][sp] == 3) { params.locallab.Smethod = "SYMSL"; } params.locallab.radius = dataspots[17][sp]; params.locallab.strength = dataspots[18][sp]; params.locallab.sensibn = dataspots[19][sp]; if (dataspots[20][sp] == 0) { params.locallab.inversrad = false; } else { params.locallab.inversrad = true; } params.locallab.str = dataspots[21][sp]; params.locallab.chrrt = dataspots[22][sp]; params.locallab.neigh = dataspots[23][sp]; params.locallab.vart = dataspots[24][sp]; params.locallab.sensih = dataspots[25][sp]; if (dataspots[26][sp] == 0) { params.locallab.inversret = false; } else { params.locallab.inversret = true; } if (dataspots[27][sp] == 0) { params.locallab.retinexMethod = "low" ; } else if (dataspots[27][sp] == 1) { params.locallab.retinexMethod = "uni" ; } else if (dataspots[27][sp] == 2) { params.locallab.retinexMethod = "high"; } params.locallab.sharradius = dataspots[28][sp]; params.locallab.sharamount = dataspots[29][sp]; params.locallab.shardamping = dataspots[30][sp]; params.locallab.shariter = dataspots[31][sp]; params.locallab.sensisha = dataspots[32][sp]; if (dataspots[33][sp] == 0) { params.locallab.inverssha = false; } else { params.locallab.inverssha = true; } if (dataspots[34][sp] == 0) { params.locallab.qualityMethod = "std" ; } else if (dataspots[34][sp] == 1) { params.locallab.qualityMethod = "enh" ; } else if (dataspots[34][sp] == 2) { params.locallab.qualityMethod = "enhden" ; } params.locallab.thres = dataspots[35][sp]; params.locallab.proxi = dataspots[36][sp]; params.locallab.noiselumf = dataspots[37][sp]; params.locallab.noiselumc = dataspots[38][sp]; params.locallab.noisechrof = dataspots[39][sp]; params.locallab.noisechroc = dataspots[40][sp]; params.locallab.mult[0] = dataspots[41][sp]; params.locallab.mult[1] = dataspots[42][sp]; params.locallab.mult[2] = dataspots[43][sp]; params.locallab.mult[3] = dataspots[44][sp]; params.locallab.mult[4] = dataspots[45][sp]; params.locallab.threshold = dataspots[46][sp]; params.locallab.sensicb = dataspots[47][sp]; if (dataspots[48][sp] == 0) { params.locallab.activlum = false; } else { params.locallab.activlum = true; } params.locallab.stren = dataspots[49][sp]; params.locallab.gamma = dataspots[50][sp]; params.locallab.estop = dataspots[51][sp]; params.locallab.scaltm = dataspots[52][sp]; params.locallab.rewei = dataspots[53][sp]; params.locallab.sensitm = dataspots[54][sp]; params.locallab.retrab = dataspots[55][sp]; if (dataspots[56][sp] == 0) { params.locallab.curvactiv = false; } else { params.locallab.curvactiv = true; } if (dataspots[57][sp] == 0) { params.locallab.qualitycurveMethod = "none" ; } else if (dataspots[57][sp] == 1) { params.locallab.qualitycurveMethod = "std" ; } else if (dataspots[57][sp] == 2) { params.locallab.qualitycurveMethod = "enh" ; } params.locallab.hueref = ((float) dataspots[58][sp]) / 100.f; params.locallab.chromaref = dataspots[59][sp]; params.locallab.lumaref = dataspots[60][sp]; int *s_datc; s_datc = new int[70]; int siz; ipf.strcurv_data (retistrs[sp], s_datc, siz); std::vector cretiend; for (int j = 0; j < siz; j++) { cretiend.push_back ((double) (s_datc[j]) / 1000.); } delete [] s_datc; int *s_datcl; s_datcl = new int[70]; int sizl; ipf.strcurv_data (llstrs[sp], s_datcl, sizl); std::vector cllend; for (int j = 0; j < sizl; j++) { cllend.push_back ((double) (s_datcl[j]) / 1000.); } delete [] s_datcl; int *s_datcc; s_datcc = new int[70]; int sizc; ipf.strcurv_data (ccstrs[sp], s_datcc, sizc); std::vector cccend; for (int j = 0; j < sizc; j++) { cccend.push_back ((double) (s_datcc[j]) / 1000.); } delete [] s_datcc; int *s_datch; s_datch = new int[70]; int sizh; ipf.strcurv_data (lhstrs[sp], s_datch, sizh); std::vector clhend; for (int j = 0; j < sizh; j++) { clhend.push_back ((double) (s_datch[j]) / 1000.); } params.locallab.localTgaincurve.clear(); params.locallab.llcurve.clear(); params.locallab.LHcurve.clear(); params.locallab.cccurve.clear(); params.locallab.localTgaincurve = cretiend; params.locallab.llcurve = cllend; params.locallab.LHcurve = clhend; params.locallab.cccurve = cccend; params.locallab.getCurves (locRETgainCurve, locRETgainCurverab, loclhCurve); bool locallutili = false; bool localcutili = false; CurveFactory::curveLocal (locallutili, params.locallab.llcurve, lllocalcurve, 1); CurveFactory::curveCCLocal (localcutili, params.locallab.cccurve, cclocalcurve, 1); ipf.Lab_Local (2, sp, (float**)shbuffer, labView, labView, 0, 0, 0, 0, fw, fh, fw, fh, locutili, 1, locRETgainCurve, locallutili, lllocalcurve, loclhCurve, cclocalcurve, params.locallab.hueref, params.locallab.chromaref, params.locallab.lumaref); lllocalcurve.clear(); cclocalcurve.clear(); } for (int i = 0; i < 61; i++) { delete [] dataspots[i]; } delete [] dataspots; delete [] retistrs; delete [] llstrs; delete [] lhstrs; delete [] ccstrs; if (params.locallab.inverssha) { for (int i = 0; i < fh; i++) { delete [] shbuffer[i]; } delete [] shbuffer; } } t2.set(); if ( settings->verbose ) { printf ("Total local:- %d usec\n", t2.etime (t1)); } } ipf.chromiLuminanceCurve (nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { ipf.EPDToneMap (labView, 5, 1); } ipf.vibrance (labView); if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { ipf.impulsedenoise (labView); } // for all treatments Defringe, Sharpening, Contrast detail ,Microcontrast they are activated if "CIECAM" function are disabled if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { ipf.defringe (labView); } if (params.sharpenEdge.enabled) { ipf.MLsharpen (labView); } if (params.sharpenMicro.enabled) { if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { ipf.MLmicrocontrast (labView); //!params.colorappearance.sharpcie } } if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) && params.sharpening.enabled) { float **buffer = new float*[fh]; for (int i = 0; i < fh; i++) { buffer[i] = new float[fw]; } ipf.sharpening (labView, (float**)buffer, params.sharpening); for (int i = 0; i < fh; i++) { delete [] buffer[i]; } delete [] buffer; } WaveletParams WaveParams = params.wavelet; WavCurve wavCLVCurve; WavOpacityCurveRG waOpacityCurveRG; WavOpacityCurveBY waOpacityCurveBY; WavOpacityCurveW waOpacityCurveW; WavOpacityCurveWL waOpacityCurveWL; params.wavelet.getCurves (wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL ); // directional pyramid wavelet if (params.dirpyrequalizer.cbdlMethod == "aft") { if ((params.colorappearance.enabled && !settings->autocielab) || !params.colorappearance.enabled) { ipf.dirpyrequalizer (labView, 1); //TODO: this is the luminance tonecurve, not the RGB one } } bool wavcontlutili = false; CurveFactory::curveWavContL (wavcontlutili, params.wavelet.wavclCurve, wavclCurve,/* hist16C, dummy,*/ 1); if (params.wavelet.enabled) { ipf.ip_wavelet (labView, labView, 2, WaveParams, wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, wavcontlutili, 1); } wavCLVCurve.Reset(); //Colorappearance and tone-mapping associated int f_w = 1, f_h = 1; int begh = 0, endh = fh; if (params.colorappearance.tonecie || params.colorappearance.enabled) { f_w = fw; f_h = fh; } CieImage *cieView = new CieImage (f_w, (f_h)); begh = 0; endh = fh; CurveFactory::curveLightBrightColor ( params.colorappearance.curve, params.colorappearance.curve2, params.colorappearance.curve3, hist16, dummy, dummy, dummy, customColCurve1, customColCurve2, customColCurve3, 1); if (params.colorappearance.enabled) { double adap; float fnum = imgsrc->getMetaData()->getFNumber ();// F number float fiso = imgsrc->getMetaData()->getISOSpeed () ;// ISO float fspeed = imgsrc->getMetaData()->getShutterSpeed () ;//speed float fcomp = imgsrc->getMetaData()->getExpComp ();//compensation + - if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) { adap = 2000.; }//if no exif data or wrong else { float E_V = fcomp + log2 ((fnum * fnum) / fspeed / (fiso / 100.f)); E_V += params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV E_V += log2 (params.raw.expos); // exposure raw white point ; log2 ==> linear to EV adap = powf (2.f, E_V - 3.f); //cd / m2 } LUTf CAMBrightCurveJ; LUTf CAMBrightCurveQ; float CAMMean = NAN; if (params.sharpening.enabled) { if (settings->ciecamfloat) { float d; ipf.ciecam_02float (cieView, float (adap), begh, endh, 1, 2, labView, ¶ms, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, 1, true, d, 1, 1); } else { double dd; ipf.ciecam_02 (cieView, adap, begh, endh, 1, 2, labView, ¶ms, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, 1, true, dd, 1, 1); } } else { if (settings->ciecamfloat) { float d; ipf.ciecam_02float (cieView, float (adap), begh, endh, 1, 2, labView, ¶ms, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, 1, true, d, 1, 1); } else { double dd; ipf.ciecam_02 (cieView, adap, begh, endh, 1, 2, labView, ¶ms, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, 1, true, dd, 1, 1); } } } delete cieView; cieView = nullptr; // end tile processing...??? //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if (pl) { pl->setProgress (0.60); } int imw, imh; double tmpScale = ipf.resizeScale (¶ms, fw, fh, imw, imh); bool labResize = params.resize.enabled && params.resize.method != "Nearest" && tmpScale != 1.0; LabImage *tmplab; // crop and convert to rgb16 int cx = 0, cy = 0, cw = labView->W, ch = labView->H; if (params.crop.enabled) { cx = params.crop.x; cy = params.crop.y; cw = params.crop.w; ch = params.crop.h; if (labResize) { // crop lab data tmplab = new LabImage (cw, ch); for (int row = 0; row < ch; row++) { for (int col = 0; col < cw; col++) { tmplab->L[row][col] = labView->L[row + cy][col + cx]; tmplab->a[row][col] = labView->a[row + cy][col + cx]; tmplab->b[row][col] = labView->b[row + cy][col + cx]; } } delete labView; labView = tmplab; cx = 0; cy = 0; } } if (labResize) { // resize lab data // resize image tmplab = new LabImage (imw, imh); ipf.Lanczos (labView, tmplab, tmpScale); delete labView; labView = tmplab; cw = labView->W; ch = labView->H; if (params.prsharpening.enabled) { for (int i = 0; i < ch; i++) for (int j = 0; j < cw; j++) { labView->L[i][j] = labView->L[i][j] < 0.f ? 0.f : labView->L[i][j]; } float **buffer = new float*[ch]; for (int i = 0; i < ch; i++) { buffer[i] = new float[cw]; } ipf.sharpening (labView, (float**)buffer, params.prsharpening); for (int i = 0; i < ch; i++) { delete [] buffer[i]; } delete [] buffer; } } Image16* readyImg = nullptr; cmsHPROFILE jprof = nullptr; bool customGamma = false; bool useLCMS = false; bool bwonly = params.blackwhite.enabled && !params.colorToning.enabled && !autili && !butili ; if (params.icm.gamma != "default" || params.icm.freegamma) { // if select gamma output between BT709, sRGB, linear, low, high, 2.2 , 1.8 GammaValues ga; // if(params.blackwhite.enabled) params.toneCurve.hrenabled=false; readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm, bwonly, &ga); customGamma = true; //or selected Free gamma useLCMS = false; if ((jprof = iccStore->createCustomGammaOutputProfile (params.icm, ga)) == nullptr) { useLCMS = true; } } else { // if Default gamma mode: we use the profile selected in the "Output profile" combobox; // gamma come from the selected profile, otherwise it comes from "Free gamma" tool readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm, bwonly); if (settings->verbose) { printf ("Output profile_: \"%s\"\n", params.icm.output.c_str()); } } delete labView; labView = nullptr; if (bwonly) { //force BW r=g=b if (settings->verbose) { printf ("Force BW\n"); } for (int ccw = 0; ccw < cw; ccw++) { for (int cch = 0; cch < ch; cch++) { readyImg->r (cch, ccw) = readyImg->g (cch, ccw); readyImg->b (cch, ccw) = readyImg->g (cch, ccw); } } } if (pl) { pl->setProgress (0.70); } if (tmpScale != 1.0 && params.resize.method == "Nearest") { // resize rgb data (gamma applied) Image16* tempImage = new Image16 (imw, imh); ipf.resize (readyImg, tempImage, tmpScale); delete readyImg; readyImg = tempImage; } if (tunnelMetaData) { readyImg->setMetadata (ii->getMetaData()->getExifData ()); } else { readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc); } // Setting the output curve to readyImg if (customGamma) { if (!useLCMS) { // use corrected sRGB profile in order to apply a good TRC if present, otherwise use LCMS2 profile generated by lab2rgb16 w/ gamma ProfileContent pc (jprof); readyImg->setOutputProfile (pc.data, pc.length); } } else { // use the selected output profile if present, otherwise use LCMS2 profile generate by lab2rgb16 w/ gamma if (params.icm.output != "" && params.icm.output != ColorManagementParams::NoICMString) { // if iccStore->getProfile send back an object, then iccStore->getContent will do too cmsHPROFILE jprof = iccStore->getProfile (params.icm.output); //get outProfile if (jprof == nullptr) { if (settings->verbose) { printf ("\"%s\" ICC output profile not found!\n - use LCMS2 substitution\n", params.icm.output.c_str()); } } else { if (settings->verbose) { printf ("Using \"%s\" output profile\n", params.icm.output.c_str()); } ProfileContent pc = iccStore->getContent (params.icm.output); readyImg->setOutputProfile (pc.data, pc.length); } } else { // No ICM readyImg->setOutputProfile (nullptr, 0); } } // t2.set(); // if( settings->verbose ) // printf("Total:- %d usec\n", t2.etime(t1)); if (!job->initialImage) { ii->decreaseRef (); } delete job; if (pl) { pl->setProgress (0.75); } /* curve1.reset();curve2.reset(); curve.reset(); satcurve.reset(); lhskcurve.reset(); rCurve.reset(); gCurve.reset(); bCurve.reset(); hist16.reset(); hist16C.reset(); */ return readyImg; } void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) { ProcessingJob* currentJob = job; while (currentJob) { int errorCode; IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData, true); if (errorCode) { bpl->error (M ("MAIN_MSG_CANNOTLOAD")); currentJob = nullptr; } else { try { currentJob = bpl->imageReady (img); } catch (Glib::Exception& ex) { bpl->error (ex.what()); currentJob = nullptr; } } } } void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) { if (bpl) #if __GNUC__ == 4 && __GNUC_MINOR__ == 8 && defined( WIN32 ) && defined(__x86_64__) // See Issue 2384 "Very bad response time on win7/64 using gcc 4.8 when queue is running" Glib::Thread::create (sigc::bind (sigc::ptr_fun (batchProcessingThread), job, bpl, tunnelMetaData), 0, true, true, Glib::THREAD_PRIORITY_NORMAL); #else Glib::Thread::create (sigc::bind (sigc::ptr_fun (batchProcessingThread), job, bpl, tunnelMetaData), 0, true, true, Glib::THREAD_PRIORITY_LOW); #endif } }