/* * 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 * 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 #include #include "cieimage.h" #include "color.h" #include "curves.h" #include "dcp.h" #include "dcrop.h" #include "guidedfilter.h" #include "image8.h" #include "imagefloat.h" #include "improccoordinator.h" #include "labimage.h" #include "mytime.h" #include "procparams.h" #include "refreshmap.h" #include "rt_math.h" #include "utils.h" #include "rtgui/editcallbacks.h" #pragma GCC diagnostic warning "-Wall" #pragma GCC diagnostic warning "-Wextra" namespace { // "ceil" rounding template constexpr T skips(T a, T b) { return a / b + static_cast(a % b); } } namespace rtengine { Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow) : PipetteBuffer(editDataProvider), origCrop(nullptr), spotCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr), cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr), updating(false), newUpdatePending(false), skip(10), cropx(0), cropy(0), cropw(-1), croph(-1), trafx(0), trafy(0), trafw(-1), trafh(-1), rqcropx(0), rqcropy(0), rqcropw(-1), rqcroph(-1), borderRequested(32), upperBorder(0), leftBorder(0), cropAllocated(false), cropImageListener(nullptr), parent(parent), isDetailWindow(isDetailWindow) { parent->crops.push_back(this); } Crop::~Crop() { MyMutex::MyLock cropLock(cropMutex); std::vector::iterator i = std::find(parent->crops.begin(), parent->crops.end(), this); if (i != parent->crops.end()) { parent->crops.erase(i); } MyMutex::MyLock processingLock(parent->mProcessing); freeAll(); } void Crop::destroy() { MyMutex::MyLock lock(cropMutex); MyMutex::MyLock processingLock(parent->mProcessing); freeAll(); } void Crop::setListener(DetailedCropListener* il) { // We can make reads in the IF, because the mProcessing lock is only needed for change if (cropImageListener != il) { MyMutex::MyLock lock(cropMutex); cropImageListener = il; } } EditUniqueID Crop::getCurrEditID() const { const EditSubscriber *subscriber = PipetteBuffer::dataProvider ? PipetteBuffer::dataProvider->getCurrSubscriber() : nullptr; return subscriber ? subscriber->getEditID() : EUID_None; } /* * Delete the edit image buffer if there's no subscriber anymore. * If allocation has to be done, it is deferred to Crop::update */ void Crop::setEditSubscriber(EditSubscriber* newSubscriber) { MyMutex::MyLock lock(cropMutex); // At this point, editCrop.dataProvider->currSubscriber is the old subscriber const EditSubscriber *oldSubscriber = PipetteBuffer::dataProvider ? PipetteBuffer::dataProvider->getCurrSubscriber() : nullptr; if (newSubscriber == nullptr || (oldSubscriber != nullptr && oldSubscriber->getPipetteBufferType() != newSubscriber->getPipetteBufferType())) { if (PipetteBuffer::imgFloatBuffer != nullptr) { delete PipetteBuffer::imgFloatBuffer; PipetteBuffer::imgFloatBuffer = nullptr; } if (PipetteBuffer::LabBuffer != nullptr) { delete PipetteBuffer::LabBuffer; PipetteBuffer::LabBuffer = nullptr; } if (PipetteBuffer::singlePlaneBuffer.getWidth() != -1) { PipetteBuffer::singlePlaneBuffer.flushData(); } } // If oldSubscriber == NULL && newSubscriber != NULL && newSubscriber->getEditingType() == ET_PIPETTE-> the image will be allocated when necessary } bool Crop::hasListener() { MyMutex::MyLock cropLock(cropMutex); return cropImageListener; } void Crop::update(int todo) { MyMutex::MyLock cropLock(cropMutex); ProcParams& params = *parent->params; // CropGUIListener* cropgl; // No need to update todo here, since it has already been changed in ImprocCoordinator::updatePreviewImage, // and Crop::update ask to do ALL anyway // give possibility to the listener to modify crop window (as the full image dimensions are already known at this point) int wx, wy, ww, wh, ws; const bool overrideWindow = cropImageListener; bool spotsDone = false; if (overrideWindow) { cropImageListener->getWindow(wx, wy, ww, wh, ws); } // re-allocate sub-images and arrays if their dimensions changed bool needsinitupdate = false; if (!overrideWindow) { needsinitupdate = setCropSizes(rqcropx, rqcropy, rqcropw, rqcroph, skip, true); } else { needsinitupdate = setCropSizes(wx, wy, ww, wh, ws, true); // this set skip=ws } // it something has been reallocated, all processing steps have to be performed if (needsinitupdate || (todo & M_HIGHQUAL)) { todo = ALL; } // Tells to the ImProcFunctions' tool what is the preview scale, which may lead to some simplifications parent->ipf.setScale(skip); Imagefloat* baseCrop = origCrop; int widIm = parent->fw;//full image int heiIm = parent->fh; if (todo & (M_INIT | M_LINDENOISE | M_HDR)) { MyMutex::MyLock lock(parent->minit); // Also used in improccoord int tr = getCoarseBitMask(params.coarse); if (!needsinitupdate) { setCropSizes(rqcropx, rqcropy, rqcropw, rqcroph, skip, true); } // printf("x=%d y=%d crow=%d croh=%d skip=%d\n",rqcropx, rqcropy, rqcropw, rqcroph, skip); // printf("trafx=%d trafyy=%d trafwsk=%d trafHs=%d \n",trafx, trafy, trafw*skip, trafh*skip); Imagefloat *calclum = nullptr;//for Luminance denoise curve NoiseCurve noiseLCurve; NoiseCurve noiseCCurve; float autoNR = (float) settings->nrauto;// float autoNRmax = (float) settings->nrautomax;// params.dirpyrDenoise.getCurves(noiseLCurve, noiseCCurve); const int tilesize = settings->leveldnti == 0 ? 1024 : 768; const int overlap = settings->leveldnti == 0 ? 128 : 96; int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; parent->ipf.Tile_calc(tilesize, overlap, 2, widIm, heiIm, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); float *min_b = new float [9]; float *min_r = new float [9]; float *lumL = new float [9]; float *chromC = new float [9]; float *ry = new float [9]; float *sk = new float [9]; float *pcsk = new float [9]; int *centerTile_X = new int [numtiles_W]; int *centerTile_Y = new int [numtiles_H]; for (int cX = 0; cX < numtiles_W; cX++) { centerTile_X[cX] = tileWskip / 2 + tileWskip * cX; } for (int cY = 0; cY < numtiles_H; cY++) { centerTile_Y[cY] = tileHskip / 2 + tileHskip * cY; } if (settings->leveldnautsimpl == 1) { if (params.dirpyrDenoise.Cmethod == "MAN" || params.dirpyrDenoise.Cmethod == "PON") { PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); } } else { if (params.dirpyrDenoise.C2method == "MANU") { PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); } } if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "PRE") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "PREV")) { PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); if ((!isDetailWindow) && parent->adnListener && skip == 1 && params.dirpyrDenoise.enabled) { float lowdenoise = 1.f; int levaut = settings->leveldnaut; if (levaut == 1) { //Standard lowdenoise = 0.7f; } int CenterPreview_X = trafx + (trafw * skip) / 2; int CenterPreview_Y = trafy + (trafh * skip) / 2; int minimuX = 20000; int minimuY = 20000; int poscenterX = 0; int poscenterY = 0; for (int cc = 0; cc < numtiles_W; cc++) { if (abs(centerTile_X[cc] - CenterPreview_X) < minimuX) { minimuX = abs(centerTile_X[cc] - CenterPreview_X); poscenterX = cc; } } for (int cc = 0; cc < numtiles_H; cc++) { if (abs(centerTile_Y[cc] - CenterPreview_Y) < minimuY) { minimuY = abs(centerTile_Y[cc] - CenterPreview_Y); poscenterY = cc; } } // printf("TileCX=%d TileCY=%d prevX=%d prevY=%d \n",centerTile_X[poscenterX],centerTile_Y[poscenterY],CenterPreview_X,CenterPreview_Y); int crW; if (settings->leveldnv == 0) { crW = 100; } if (settings->leveldnv == 1) { crW = 250; } // if (settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int((tileWskip/2));}//adapted to scale of preview if (settings->leveldnv == 2) { crW = int (tileWskip / 2); } if (settings->leveldnv == 3) { crW = tileWskip - 10; } float adjustr = 1.f; if (params.icm.workingProfile == "ProPhoto") { adjustr = 1.f; } else if (params.icm.workingProfile == "Adobe RGB") { adjustr = 1.f / 1.3f; } else if (params.icm.workingProfile == "sRGB") { adjustr = 1.f / 1.3f; } else if (params.icm.workingProfile == "WideGamut") { adjustr = 1.f / 1.1f; } else if (params.icm.workingProfile == "Beta RGB") { adjustr = 1.f / 1.2f; } else if (params.icm.workingProfile == "BestRGB") { adjustr = 1.f / 1.2f; } else if (params.icm.workingProfile == "BruceRGB") { adjustr = 1.f / 1.2f; } if (parent->adnListener) { parent->adnListener->noiseTilePrev(centerTile_X[poscenterX], centerTile_Y[poscenterY], CenterPreview_X, CenterPreview_Y, crW, trafw * skip); } // I have tried "blind" some solutions..to move review ...but GUI is not my truc ! // int W,H; // cropgl->cropMoved (centerTile_X[poscenterX],centerTile_Y[poscenterY] , W, H); // cropImageListener->setPosition (int x, int y, bool update=true); // bool update; // cropImageListener->setPosition (centerTile_X[poscenterX],centerTile_Y[poscenterY] , true); //setCropSizes (centerTile_X[poscenterX], centerTile_Y[poscenterY], trafw*skip,trafh*skip , skip, true); // we only need image reduced to 1/4 here int W = origCrop->getWidth(); int H = origCrop->getHeight(); Imagefloat *provicalc = new Imagefloat((W + 1) / 2, (H + 1) / 2); //for denoise curves for (int ii = 0; ii < H; ii += 2) { for (int jj = 0; jj < W; jj += 2) { provicalc->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj); provicalc->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj); provicalc->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj); } } parent->imgsrc->convertColorSpace(provicalc, params.icm, parent->currWB); //for denoise luminance curve float maxr = 0.f; float maxb = 0.f; 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; minredaut = 0.f; minblueaut = 0.f; LUTf gamcurve(65536, 0); float gam, gamthresh, gamslope; parent->ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope); parent->ipf.RGB_denoise_info(origCrop, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc, true); // printf("redy=%f skin=%f pcskin=%f\n",redyel, skinc,nsknc); // printf("DCROP skip=%d cha=%4.0f Nb=%d red=%4.0f bl=%4.0f redM=%4.0f bluM=%4.0f L=%4.0f sigL=%4.0f Ch=%4.0f Si=%4.0f\n",skip, chaut,Nb, redaut,blueaut, maxredaut, maxblueaut, lumema, sigma_L, chromina, sigma); float multip = 1.f; if (!parent->imgsrc->isRAW()) { multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good for gamma=1 } float maxmax = max(maxredaut, maxblueaut); float delta; int mode = 0; // float redyel, skinc, nsknc; int lissage = settings->leveldnliss; parent->ipf.calcautodn_info(chaut, delta, Nb, levaut, maxmax, lumema, chromina, mode, lissage, redyel, skinc, nsknc); if (maxredaut > maxblueaut) { // maxr=(maxredaut-chaut)/((autoNRmax*multip*adjustr)/2.f); maxr = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); if (minblueaut <= minredaut && minblueaut < chaut) { maxb = (-chaut + minblueaut) / (autoNRmax * multip * adjustr * lowdenoise); } } else { // maxb=(maxblueaut-chaut)/((autoNRmax*multip*adjustr)/2.f); 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 params.dirpyrDenoise.chroma = chaut / (autoNR * multip * adjustr * lowdenoise); params.dirpyrDenoise.redchro = maxr; params.dirpyrDenoise.bluechro = maxb; parent->adnListener->chromaChanged(params.dirpyrDenoise.chroma, params.dirpyrDenoise.redchro, params.dirpyrDenoise.bluechro); delete provicalc; } } if (skip == 1 && params.dirpyrDenoise.enabled && !parent->denoiseInfoStore.valid && ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO"))) { MyTime t1aue, t2aue; t1aue.set(); int crW = 100; // settings->leveldnv == 0 int crH = 100; // settings->leveldnv == 0 if (settings->leveldnv == 1) { crW = 250; crH = 250; } // if (settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int((tileWskip/2));}//adapted to scale of preview if (settings->leveldnv == 2) { crW = int (tileWskip / 2); crH = int (tileHskip / 2); } 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; } LUTf gamcurve(65536, 0); float gam, gamthresh, gamslope; parent->ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope); int Nb[9]; #ifdef _OPENMP #pragma omp parallel #endif { Imagefloat *origCropPart = new Imagefloat(crW, crH); //allocate memory Imagefloat *provicalc = new Imagefloat((crW + 1) / 2, (crH + 1) / 2); //for denoise curves int coordW[3];//coordinate of part of image to measure noise int coordH[3]; int begW = 50; int begH = 50; coordW[0] = begW; coordW[1] = widIm / 2 - crW / 2; coordW[2] = widIm - crW - begW; coordH[0] = begH; coordH[1] = heiIm / 2 - crH / 2; coordH[2] = heiIm - crH - begH; #ifdef _OPENMP #pragma omp for schedule(dynamic) collapse(2) nowait #endif for (int wcr = 0; wcr <= 2; wcr++) { for (int hcr = 0; hcr <= 2; hcr++) { PreviewProps ppP(coordW[wcr], coordH[hcr], crW, crH, 1); parent->imgsrc->getImage(parent->currWB, tr, origCropPart, ppP, params.toneCurve, 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); } } parent->imgsrc->convertColorSpace(provicalc, params.icm, parent->currWB); //for denoise luminance curve float pondcorrec = 1.0f; 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; int nb = 0; parent->ipf.RGB_denoise_info(origCropPart, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc); //printf("DCROP skip=%d cha=%f red=%f bl=%f redM=%f bluM=%f chrom=%f sigm=%f lum=%f\n",skip, chaut,redaut,blueaut, maxredaut, maxblueaut, chromina, sigma, lumema); Nb[hcr * 3 + wcr] = nb; parent->denoiseInfoStore.ch_M[hcr * 3 + wcr] = pondcorrec * chaut; parent->denoiseInfoStore.max_r[hcr * 3 + wcr] = pondcorrec * maxredaut; parent->denoiseInfoStore.max_b[hcr * 3 + wcr] = pondcorrec * maxblueaut; min_r[hcr * 3 + wcr] = pondcorrec * minredaut; min_b[hcr * 3 + wcr] = pondcorrec * 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 = 100000000000.f; float MinB = 100000000000.f; float maxr = 0.f; float maxb = 0.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; float multip = 1.f; if (!parent->imgsrc->isRAW()) { multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good for gamma=1 } float adjustr = 1.f; if (params.icm.workingProfile == "ProPhoto") { adjustr = 1.f; // } else if (params.icm.workingProfile == "Adobe RGB") { adjustr = 1.f / 1.3f; } else if (params.icm.workingProfile == "sRGB") { adjustr = 1.f / 1.3f; } else if (params.icm.workingProfile == "WideGamut") { adjustr = 1.f / 1.1f; } else if (params.icm.workingProfile == "Beta RGB") { adjustr = 1.f / 1.2f; } else if (params.icm.workingProfile == "BestRGB") { adjustr = 1.f / 1.2f; } else if (params.icm.workingProfile == "BruceRGB") { adjustr = 1.f / 1.2f; } float delta[9]; int mode = 1; int lissage = settings->leveldnliss; for (int k = 0; k < 9; k++) { float maxmax = max(parent->denoiseInfoStore.max_r[k], parent->denoiseInfoStore.max_b[k]); parent->ipf.calcautodn_info(parent->denoiseInfoStore.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 (parent->denoiseInfoStore.max_r[k] > parent->denoiseInfoStore.max_b[k]) { Max_R[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); Min_B[k] = - (parent->denoiseInfoStore.ch_M[k] - min_b[k]) / (autoNRmax * multip * adjustr * lowdenoise); Max_B[k] = 0.f; Min_R[k] = 0.f; } else { Max_B[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f); Min_R[k] = - (parent->denoiseInfoStore.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 += parent->denoiseInfoStore.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 = MinRMoy + (MinR - MinRMoy) * 0.66f; } // printf("DCROP skip=%d cha=%f red=%f bl=%f \n",skip, chM,maxr,maxb); params.dirpyrDenoise.chroma = chM / (autoNR * multip * adjustr); params.dirpyrDenoise.redchro = maxr; params.dirpyrDenoise.bluechro = maxb; parent->denoiseInfoStore.valid = true; if (parent->adnListener) { parent->adnListener->chromaChanged(params.dirpyrDenoise.chroma, params.dirpyrDenoise.redchro, params.dirpyrDenoise.bluechro); } if (settings->verbose) { t2aue.set(); printf("Info denoise auto performed in %d usec:\n", t2aue.etime(t1aue)); } //end evaluate noise } // if (params.dirpyrDenoise.Cmethod=="AUT" || params.dirpyrDenoise.Cmethod=="PON") {//reinit origCrop after Auto if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { //reinit origCrop after Auto PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); } if ((todo & M_SPOT) && params.spot.enabled && !params.spot.entries.empty()) { spotsDone = true; PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); //parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, nullptr, tr); } DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise; if (params.dirpyrDenoise.Lmethod == "CUR") { if (noiseLCurve) { denoiseParams.luma = 0.5f; //very small value to init process - select curve or slider } else { denoiseParams.luma = 0.0f; } } else if (denoiseParams.Lmethod == "SLI") { noiseLCurve.Reset(); } if ((noiseLCurve || noiseCCurve) && skip == 1 && denoiseParams.enabled) { //only allocate memory if enabled and skip // we only need image reduced to 1/4 here int W = origCrop->getWidth(); int H = origCrop->getHeight(); calclum = new Imagefloat((W + 1) / 2, (H + 1) / 2); //for denoise curves for (int ii = 0; ii < H; ii += 2) { for (int jj = 0; jj < W; jj += 2) { calclum->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj); calclum->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj); calclum->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj); } } parent->imgsrc->convertColorSpace(calclum, params.icm, parent->currWB); //for denoise luminance curve } if (skip != 1) if (parent->adnListener) { parent->adnListener->noiseChanged(0.f, 0.f); } if (todo & (M_INIT | M_LINDENOISE | M_HDR)) { if (skip == 1 && denoiseParams.enabled) { float nresi, highresi; parent->ipf.RGB_denoise(0, origCrop, origCrop, calclum, parent->denoiseInfoStore.ch_M, parent->denoiseInfoStore.max_r, parent->denoiseInfoStore.max_b, parent->imgsrc->isRAW(), /*Roffset,*/ denoiseParams, parent->imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, nresi, highresi); if (parent->adnListener) { parent->adnListener->noiseChanged(nresi, highresi); } if (settings->leveldnautsimpl == 1) { if ((denoiseParams.Cmethod == "AUT" || denoiseParams.Cmethod == "PRE") && (parent->adnListener)) { // force display value of sliders parent->adnListener->chromaChanged(denoiseParams.chroma, denoiseParams.redchro, denoiseParams.bluechro); } } else { if ((denoiseParams.C2method == "AUTO" || denoiseParams.C2method == "PREV") && (parent->adnListener)) { // force display value of sliders parent->adnListener->chromaChanged(denoiseParams.chroma, denoiseParams.redchro, denoiseParams.bluechro); } } } } if (params.filmNegative.enabled && params.filmNegative.colorSpace == FilmNegativeParams::ColorSpace::INPUT) { parent->ipf.filmNegativeProcess(baseCrop, baseCrop, params.filmNegative); } parent->imgsrc->convertColorSpace(origCrop, params.icm, parent->currWB); if (params.filmNegative.enabled && params.filmNegative.colorSpace != FilmNegativeParams::ColorSpace::INPUT) { parent->ipf.filmNegativeProcess(baseCrop, baseCrop, params.filmNegative); } if (params.cg.enabled) {//gamut compression parent->ipf.gamutcompr(baseCrop, baseCrop); } delete [] min_r; delete [] min_b; delete [] lumL; delete [] chromC; delete [] ry; delete [] sk; delete [] pcsk; delete [] centerTile_X; delete [] centerTile_Y; } // has to be called after setCropSizes! Tools prior to this point can't handle the Edit mechanism, but that shouldn't be a problem. createBuffer(cropw, croph); // Apply Spot removal if ((todo & M_SPOT) && !spotsDone) { if (params.spot.enabled && !params.spot.entries.empty()) { if(!spotCrop) { spotCrop = new Imagefloat (cropw, croph); } baseCrop->copyData (spotCrop); PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip); int tr = getCoarseBitMask(params.coarse); parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, ¶ms.icm, tr); } else { if (spotCrop) { delete spotCrop; spotCrop = nullptr; } } } if (spotCrop) { baseCrop = spotCrop; } std::unique_ptr fattalCrop; if ((todo & M_HDR) && (params.fattal.enabled || params.dehaze.enabled)) { Imagefloat *f = origCrop; int fw = skips(parent->fw, skip); int fh = skips(parent->fh, skip); bool need_cropping = false; bool need_fattal = true; if (trafx || trafy || trafw != fw || trafh != fh) { need_cropping = true; // fattal needs to work on the full image. So here we get the full // image from imgsrc, and replace the denoised crop in case if (!params.dirpyrDenoise.enabled && skip == 1 && parent->fattal_11_dcrop_cache) { f = parent->fattal_11_dcrop_cache; need_fattal = false; } else { f = new Imagefloat(fw, fh); fattalCrop.reset(f); PreviewProps pp(0, 0, parent->fw, parent->fh, skip); int tr = getCoarseBitMask(params.coarse); parent->imgsrc->getImage(parent->currWB, tr, f, pp, params.toneCurve, params.raw); parent->imgsrc->convertColorSpace(f, params.icm, parent->currWB); if (params.dirpyrDenoise.enabled || params.filmNegative.enabled || params.spot.enabled) { // copy the denoised crop int oy = trafy / skip; int ox = trafx / skip; #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < baseCrop->getHeight(); ++y) { int dy = oy + y; for (int x = 0; x < baseCrop->getWidth(); ++x) { int dx = ox + x; f->r(dy, dx) = baseCrop->r(y, x); f->g(dy, dx) = baseCrop->g(y, x); f->b(dy, dx) = baseCrop->b(y, x); } } } else if (skip == 1) { parent->fattal_11_dcrop_cache = f; // cache this globally fattalCrop.release(); } } } if (need_fattal) { parent->ipf.dehaze(f, params.dehaze); parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0, false); } // crop back to the size expected by the rest of the pipeline if (need_cropping) { Imagefloat *c = origCrop; int oy = trafy / skip; int ox = trafx / skip; #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < trafh; ++y) { int cy = y + oy; for (int x = 0; x < trafw; ++x) { int cx = x + ox; c->r(y, x) = f->r(cy, cx); c->g(y, x) = f->g(cy, cx); c->b(y, x) = f->b(cy, cx); } } baseCrop = c; } else { baseCrop = f; } } const bool needstransform = parent->ipf.needsTransform(skips(parent->fw, skip), skips(parent->fh, skip), parent->imgsrc->getRotateDegree(), parent->imgsrc->getMetaData()); const bool cam02 = params.colorappearance.modelmethod == "02" && params.colorappearance.enabled; // transform // if (needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled)) { if (needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !cam02)) { if (!transCrop) { transCrop = new Imagefloat(cropw, croph); } if (needstransform) parent->ipf.transform(baseCrop, transCrop, cropx / skip, cropy / skip, trafx / skip, trafy / skip, skips(parent->fw, skip), skips(parent->fh, skip), parent->getFullWidth(), parent->getFullHeight(), parent->imgsrc->getMetaData(), parent->imgsrc->getRotateDegree(), false); else { baseCrop->copyData(transCrop); } if (transCrop) { baseCrop = transCrop; } } else { delete transCrop; transCrop = nullptr; } // if ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled) { if ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !cam02) { const int W = baseCrop->getWidth(); const int H = baseCrop->getHeight(); LabImage labcbdl(W, H); parent->ipf.rgb2lab(*baseCrop, labcbdl, params.icm.workingProfile); parent->ipf.dirpyrequalizer(&labcbdl, skip); parent->ipf.lab2rgb(labcbdl, *baseCrop, params.icm.workingProfile); } if ((todo & (M_AUTOEXP | M_RGBCURVE)) && params.locallab.enabled && !params.locallab.spots.empty()) { //I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here. parent->ipf.rgb2lab(*baseCrop, *laboCrop, params.icm.workingProfile); labnCrop->CopyFrom(laboCrop); const std::unique_ptr reservCrop(new LabImage(*laboCrop, true)); const std::unique_ptr lastorigCrop(new LabImage(*laboCrop, true)); std::unique_ptr savenormtmCrop; std::unique_ptr savenormretiCrop; auto& lllocalcurve2 = parent->lllocalcurve; auto& cllocalcurve2 = parent->cllocalcurve; auto& lclocalcurve2 = parent->lclocalcurve; auto& cclocalcurve2 = parent->cclocalcurve; auto& rgblocalcurve2 = parent->rgblocalcurve; auto& exlocalcurve2 = parent->exlocalcurve; auto& lmasklocalcurve2 = parent->lmasklocalcurve; auto& lmaskexplocalcurve2 = parent->lmaskexplocalcurve; auto& lmaskSHlocalcurve2 = parent->lmaskSHlocalcurve; auto& lmaskviblocalcurve2 = parent->lmaskviblocalcurve; auto& lmasktmlocalcurve2 = parent->lmasktmlocalcurve; auto& lmaskretilocalcurve2 = parent->lmaskretilocalcurve; auto& lmaskcblocalcurve2 = parent->lmaskcblocalcurve; auto& lmaskbllocalcurve2 = parent->lmaskbllocalcurve; auto& lmasklclocalcurve2 = parent->lmasklclocalcurve; auto& lmaskloglocalcurve2 = parent->lmaskloglocalcurve; auto& lmaskcielocalcurve2 = parent->lmaskcielocalcurve; auto& cielocalcurve2 = parent->cielocalcurve; auto& cielocalcurve22 = parent->cielocalcurve2; auto& jzlocalcurve2 = parent->jzlocalcurve; auto& czlocalcurve2 = parent->czlocalcurve; auto& czjzlocalcurve2 = parent->czjzlocalcurve; auto& hltonecurveloc2 = parent->hltonecurveloc; auto& shtonecurveloc2 = parent->shtonecurveloc; auto& tonecurveloc2 = parent->tonecurveloc; auto& lightCurveloc2 = parent->lightCurveloc; auto& locRETgainCurve = parent->locRETgainCurve; auto& locRETtransCurve = parent->locRETtransCurve; auto& loclhCurve = parent->loclhCurve; auto& lochhCurve = parent->lochhCurve; auto& locchCurve = parent->locchCurve; auto& lochhCurvejz = parent->lochhCurvejz; auto& locchCurvejz = parent->locchCurvejz; auto& loclhCurvejz = parent->loclhCurvejz; auto& locccmasCurve = parent->locccmasCurve; auto& locllmasCurve = parent->locllmasCurve; auto& lochhmasCurve = parent->lochhmasCurve; auto& lochhhmasCurve = parent->lochhhmasCurve; auto& lochhhmascieCurve = parent->lochhhmascieCurve; auto& locccmasexpCurve = parent->locccmasexpCurve; auto& locllmasexpCurve = parent->locllmasexpCurve; auto& lochhmasexpCurve = parent->lochhmasexpCurve; auto& locccmasSHCurve = parent->locccmasSHCurve; auto& locllmasSHCurve = parent->locllmasSHCurve; auto& lochhmasSHCurve = parent->lochhmasSHCurve; auto& locccmasvibCurve = parent->locccmasvibCurve; auto& locllmasvibCurve = parent->locllmasvibCurve; auto& lochhmasvibCurve = parent->lochhmasvibCurve; auto& locccmaslcCurve = parent->locccmaslcCurve; auto& locllmaslcCurve = parent->locllmaslcCurve; auto& lochhmaslcCurve = parent->lochhmaslcCurve; auto& locccmascbCurve = parent->locccmascbCurve; auto& locllmascbCurve = parent->locllmascbCurve; auto& lochhmascbCurve = parent->lochhmascbCurve; auto& locccmasretiCurve = parent->locccmasretiCurve; auto& locllmasretiCurve = parent->locllmasretiCurve; auto& lochhmasretiCurve = parent->lochhmasretiCurve; auto& locccmastmCurve = parent->locccmastmCurve; auto& locllmastmCurve = parent->locllmastmCurve; auto& lochhmastmCurve = parent->lochhmastmCurve; auto& locccmasblCurve = parent->locccmasblCurve; auto& locllmasblCurve = parent->locllmasblCurve; auto& lochhmasblCurve = parent->lochhmasblCurve; auto& locccmaslogCurve = parent->locccmaslogCurve; auto& locllmaslogCurve = parent->locllmaslogCurve; auto& lochhmaslogCurve = parent->lochhmaslogCurve; auto& locccmascieCurve = parent->locccmascieCurve; auto& locllmascieCurve = parent->locllmascieCurve; auto& lochhmascieCurve = parent->lochhmascieCurve; auto& locccmas_Curve = parent->locccmas_Curve; auto& locllmas_Curve = parent->locllmas_Curve; auto& lochhmas_Curve = parent->lochhmas_Curve; auto& lochhhmas_Curve = parent->lochhhmas_Curve; auto& locwavCurve = parent->locwavCurve; auto& locwavCurvejz = parent->locwavCurvejz; auto& loclmasCurveblwav = parent->loclmasCurveblwav; auto& loclmasCurvecolwav = parent->loclmasCurvecolwav; auto& loclmasCurveciewav = parent->loclmasCurveciewav; auto& loclevwavCurve = parent->loclevwavCurve; auto& locconwavCurve = parent->locconwavCurve; auto& loccompwavCurve = parent->loccompwavCurve; auto& loccomprewavCurve = parent->loccomprewavCurve; auto& locedgwavCurve = parent->locedgwavCurve; auto& locwavCurvehue = parent->locwavCurvehue; auto& locwavCurveden = parent->locwavCurveden; auto& lmasklocal_curve2 = parent->lmasklocal_curve; auto& loclmasCurve_wav = parent->loclmasCurve_wav; for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) { locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); const bool LHutili = loclhCurve.Set(params.locallab.spots.at(sp).LHcurve); const bool HHutili = lochhCurve.Set(params.locallab.spots.at(sp).HHcurve); const bool CHutili = locchCurve.Set(params.locallab.spots.at(sp).CHcurve); const bool HHutilijz = lochhCurvejz.Set(params.locallab.spots.at(sp).HHcurvejz); const bool CHutilijz = locchCurvejz.Set(params.locallab.spots.at(sp).CHcurvejz); const bool LHutilijz = loclhCurvejz.Set(params.locallab.spots.at(sp).LHcurvejz); const bool lcmasutili = locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve); const bool llmasutili = locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve); const bool lhmasutili = lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve); const bool lhhmasutili = lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve); const bool lhhmascieutili = lochhhmascieCurve.Set(params.locallab.spots.at(sp).HHhmaskciecurve); const bool lcmasexputili = locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve); const bool llmasexputili = locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve); const bool lhmasexputili = lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve); const bool lcmasSHutili = locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve); const bool llmasSHutili = locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve); const bool lhmasSHutili = lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve); const bool lcmasvibutili = locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve); const bool llmasvibutili = locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve); const bool lhmasvibutili = lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve); const bool lcmascbutili = locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve); const bool llmascbutili = locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve); const bool lhmascbutili = lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve); const bool lcmasretiutili = locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve); const bool llmasretiutili = locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve); const bool lhmasretiutili = lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve); const bool lcmastmutili = locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve); const bool llmastmutili = locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve); const bool lhmastmutili = lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve); const bool lcmasblutili = locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve); const bool llmasblutili = locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve); const bool lhmasblutili = lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve); const bool lcmaslogutili = locccmaslogCurve.Set(params.locallab.spots.at(sp).CCmaskcurveL); const bool llmaslogutili = locllmaslogCurve.Set(params.locallab.spots.at(sp).LLmaskcurveL); const bool lhmaslogutili = lochhmaslogCurve.Set(params.locallab.spots.at(sp).HHmaskcurveL); const bool lcmascieutili = locccmascieCurve.Set(params.locallab.spots.at(sp).CCmaskciecurve); const bool llmascieutili = locllmascieCurve.Set(params.locallab.spots.at(sp).LLmaskciecurve); const bool lhmascieutili = lochhmascieCurve.Set(params.locallab.spots.at(sp).HHmaskciecurve); const bool lcmas_utili = locccmas_Curve.Set(params.locallab.spots.at(sp).CCmask_curve); const bool llmas_utili = locllmas_Curve.Set(params.locallab.spots.at(sp).LLmask_curve); const bool lhmas_utili = lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmask_curve); const bool lhhmas_utili = lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmask_curve); const bool lmasutili_wav = loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav); const bool lmasutiliblwav = loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav); const bool lmasutilicolwav = loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav); const bool lmasutiliciewav = loclmasCurveciewav.Set(params.locallab.spots.at(sp).LLmaskciecurvewav); const bool lcmaslcutili = locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve); const bool llmaslcutili = locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve); const bool lhmaslcutili = lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve); const bool locwavutili = locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve); const bool locwavutilijz = locwavCurvejz.Set(params.locallab.spots.at(sp).locwavcurvejz); const bool locwavhueutili = locwavCurvehue.Set(params.locallab.spots.at(sp).locwavcurvehue); const bool locwavdenutili = locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden); const bool loclevwavutili = loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve); const bool locconwavutili = locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve); const bool loccompwavutili = loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve); const bool loccomprewavutili = loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve); const bool locedgwavutili = locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve); const bool locallutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).llcurve, lllocalcurve2, skip); const bool localclutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).clcurve, cllocalcurve2, skip); const bool locallcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).lccurve, lclocalcurve2, skip); const bool localrgbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).rgbcurve, rgblocalcurve2, skip); const bool localcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).cccurve, cclocalcurve2, skip); const bool localexutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).excurve, exlocalcurve2, skip); const bool localmaskutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve2, skip); const bool localmaskexputili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve2, skip); const bool localmaskSHutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve2, skip); const bool localmaskvibutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve2, skip); const bool localmasktmutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve2, skip); const bool localmaskretiutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve2, skip); const bool localmaskcbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve2, skip); const bool localmasklcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, skip); const bool localmaskblutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, skip); const bool localmasklogutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskcurveL, lmaskloglocalcurve2, skip); const bool localmask_utili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmask_curve, lmasklocal_curve2, skip); const bool localmaskcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskciecurve, lmaskcielocalcurve2, skip); const bool localcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve, cielocalcurve2, skip); const bool localcieutili2 = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve2, cielocalcurve22, skip); const bool localjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).jzcurve, jzlocalcurve2, skip); const bool localczutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czcurve, czlocalcurve2, skip); const bool localczjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czjzcurve, czjzlocalcurve2, skip); double ecomp = params.locallab.spots.at(sp).expcomp; double black = params.locallab.spots.at(sp).black; double hlcompr = params.locallab.spots.at(sp).hlcompr; double hlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh; double shcompr = params.locallab.spots.at(sp).shcompr; double br = params.locallab.spots.at(sp).lightness; if (black < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) { black *= 1.5; } std::vector localldenoiselc; double cont = params.locallab.spots.at(sp).contrast; double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; huerefblu = parent->huerefblurs[sp]; chromarefblu = parent->chromarefblurs[sp]; lumarefblu = parent->lumarefblurs[sp]; huere = parent->huerefs[sp]; chromare = parent->chromarefs[sp]; lumare = parent->lumarefs[sp]; sobelre = parent->sobelrefs[sp]; const float avge = parent->avgs[sp]; float meantme = parent->meantms[sp]; float stdtme = parent->stdtms[sp]; float meanretie = parent->meanretis[sp]; float stdretie = parent->stdretis[sp]; float fab = 1.f; float maxicam = -1000.f; float rdx, rdy, grx, gry, blx, bly = 0.f; float meanx, meany, meanxe, meanye = 0.f; int ill = 2; int prim = 3; float minCD; float maxCD; float mini; float maxi; float Tmean; float Tsigma; float Tmin; float Tmax; int lastsav; float highresi = 0.f; float nresi = 0.f; float highresi46 =0.f; float nresi46 = 0.f; float Lhighresi = 0.f; float Lnresi = 0.f; float Lhighresi46 = 0.f; float Lnresi46 = 0.f; float contsig = params.locallab.spots.at(sp).contsigqcie; float slopeg = 1.f; bool linkrgb = true; float lightsig = params.locallab.spots.at(sp).lightsigqcie; /* huerefp[sp] = huere; chromarefp[sp] = chromare; lumarefp[sp] = lumare; */ CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, avge, skip); // Locallab mask are only shown for selected spot int fh = parent->fh; int fw = parent->fw; if (sp == params.locallab.selspot) { parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), savenormtmCrop.get(), savenormretiCrop.get(), lastorigCrop.get(), fw, fh, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, lllocalcurve2,locallutili, cllocalcurve2, localclutili, lclocalcurve2, locallcutili, loclhCurve, lochhCurve, locchCurve, lochhCurvejz, locchCurvejz, loclhCurvejz, lmasklocalcurve2, localmaskutili, lmaskexplocalcurve2, localmaskexputili, lmaskSHlocalcurve2, localmaskSHutili, lmaskviblocalcurve2, localmaskvibutili, lmasktmlocalcurve2, localmasktmutili, lmaskretilocalcurve2, localmaskretiutili, lmaskcblocalcurve2, localmaskcbutili, lmaskbllocalcurve2, localmaskblutili, lmasklclocalcurve2, localmasklcutili, lmaskloglocalcurve2, localmasklogutili, lmasklocal_curve2, localmask_utili, lmaskcielocalcurve2, localmaskcieutili, cielocalcurve2,localcieutili, cielocalcurve22,localcieutili2, jzlocalcurve2,localjzutili, czlocalcurve2,localczutili, czjzlocalcurve2,localczjzutili, locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, lochhhmascieCurve, lhhmascieutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, locccmaslogCurve, lcmaslogutili, locllmaslogCurve, llmaslogutili, lochhmaslogCurve, lhmaslogutili, locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, locccmascieCurve, lcmascieutili, locllmascieCurve, llmasSHutili, lochhmascieCurve, lhmascieutili, lochhhmas_Curve, lhhmas_utili, loclmasCurveblwav,lmasutiliblwav, loclmasCurvecolwav,lmasutilicolwav, loclmasCurveciewav,lmasutiliciewav, locwavCurve, locwavutili, locwavCurvejz, locwavutilijz, loclevwavCurve, loclevwavutili, locconwavCurve, locconwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, locwavCurvehue, locwavhueutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, CHutili, HHutilijz, CHutilijz, LHutilijz, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask, parent->localllogMask, parent->locall_Mask, parent->locallcieMask, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, meantme, stdtme, meanretie, stdretie, fab, maxicam,rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, prim, ill, contsig, lightsig, highresi, nresi, highresi46, nresi46, Lhighresi, Lnresi, Lhighresi46, Lnresi46, slopeg, linkrgb); LocallabListener::locallabDenoiseLC denoiselc; denoiselc.highres = highresi; denoiselc.nres = nresi; denoiselc.highres46 = highresi46; denoiselc.nres46 = nresi46; denoiselc.Lhighres = Lhighresi; denoiselc.Lnres = Lnresi; denoiselc.Lhighres46 = Lhighresi46; denoiselc.Lnres46 = Lnresi46; localldenoiselc.push_back(denoiselc); if (parent->previewDeltaE || parent->locallColorMask == 5 || parent->locallvibMask == 4 || parent->locallExpMask == 5 || parent->locallSHMask == 4 || parent->localllcMask == 4 || parent->localltmMask == 4 || parent->localllogMask == 4 || parent->locallsoftMask == 6 || parent->localllcMask == 4 || parent->locallcieMask == 4) { params.blackwhite.enabled = false; params.colorToning.enabled = false; params.rgbCurves.enabled = false; params.chmixer.enabled = false; params.hsvequalizer.enabled = false; params.filmSimulation.enabled = false; params.toneCurve.black = 0.f; params.toneCurve.saturation = 0.f; params.toneCurve.brightness= 0.f; params.toneCurve.contrast = 0.f; params.toneCurve.hlcompr = 0.f; //these 3 are "before" LA //params.toneCurve.expcomp = 0; //params.toneCurve.curve = { 0 }; //params.toneCurve.curve2 = { 0 }; params.colorappearance.enabled = false; params.vibrance.enabled = false; params.labCurve.enabled = false; params.wavelet.enabled = false; params.epd.enabled = false; params.softlight.enabled = false; } /* if (parent->locallListener) { parent->locallListener->refChanged2(huerefp, chromarefp, lumarefp, fabrefp, params.locallab.selspot); } */ denoiselc.highres = highresi; denoiselc.nres = nresi; denoiselc.highres46 = highresi46; denoiselc.nres46 = nresi46; denoiselc.Lhighres = Lhighresi; denoiselc.Lnres = Lnresi; denoiselc.Lhighres46 = Lhighresi46; denoiselc.Lnres46 = Lnresi46; localldenoiselc.push_back(denoiselc); if (parent->locallListener) { parent->locallListener->denChanged(localldenoiselc, params.locallab.selspot); } } else { parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), savenormtmCrop.get(), savenormretiCrop.get(), lastorigCrop.get(), fw, fh, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, lllocalcurve2,locallutili, cllocalcurve2, localclutili, lclocalcurve2, locallcutili, loclhCurve, lochhCurve, locchCurve, lochhCurvejz, locchCurvejz, loclhCurvejz, lmasklocalcurve2, localmaskutili, lmaskexplocalcurve2, localmaskexputili, lmaskSHlocalcurve2, localmaskSHutili, lmaskviblocalcurve2, localmaskvibutili, lmasktmlocalcurve2, localmasktmutili, lmaskretilocalcurve2, localmaskretiutili, lmaskcblocalcurve2, localmaskcbutili, lmaskbllocalcurve2, localmaskblutili, lmasklclocalcurve2, localmasklcutili, lmaskloglocalcurve2, localmasklogutili, lmasklocal_curve2, localmask_utili, lmaskcielocalcurve2, localmaskcieutili, cielocalcurve2,localcieutili, cielocalcurve22,localcieutili2, jzlocalcurve2,localjzutili, czlocalcurve2,localczutili, czjzlocalcurve2,localczjzutili, locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili,lochhhmasCurve, lhhmasutili, lochhhmascieCurve, lhhmascieutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, locccmaslogCurve, lcmaslogutili, locllmaslogCurve, llmaslogutili, lochhmaslogCurve, lhmaslogutili, locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili, locccmascieCurve, lcmascieutili, locllmascieCurve, llmascieutili, lochhmascieCurve, lhmascieutili, lochhhmas_Curve, lhhmas_utili, loclmasCurveblwav,lmasutiliblwav, loclmasCurvecolwav,lmasutilicolwav, loclmasCurveciewav,lmasutiliciewav, locwavCurve, locwavutili, locwavCurvejz, locwavutilijz, loclevwavCurve, loclevwavutili, locconwavCurve, locconwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, locwavCurvehue, locwavhueutili, locwavCurveden, locwavdenutili, locedgwavCurve, locedgwavutili, loclmasCurve_wav,lmasutili_wav, LHutili, HHutili, CHutili, HHutilijz, CHutilijz, LHutilijz, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, meantme, stdtme, meanretie, stdretie, fab, maxicam, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, prim, ill, contsig, lightsig, highresi, nresi, highresi46, nresi46, Lhighresi, Lnresi, Lhighresi46, Lnresi46, slopeg, linkrgb); } if (sp + 1u < params.locallab.spots.size()) { // do not copy for last spot as it is not needed anymore lastorigCrop->CopyFrom(labnCrop); } if (skip <= 2) { Glib::usleep(settings->cropsleep); //wait to avoid crash when crop 100% and move window } } /* delete [] huerefp; delete [] chromarefp; delete [] lumarefp; delete [] fabrefp; */ parent->ipf.lab2rgb(*labnCrop, *baseCrop, params.icm.workingProfile); } if (todo & M_RGBCURVE) { double rrm, ggm, bbm; DCPProfileApplyState as; DCPProfile *dcpProf = parent->imgsrc->getDCP(params.icm, as); LUTu histToneCurve; parent->ipf.rgbProc (baseCrop, laboCrop, this, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, parent->colourToningSatLimit, parent->colourToningSatLimitOpacity, parent->ctColorCurve, parent->ctOpacityCurve, parent->opautili, parent->clToningcurve, parent->cl2Toningcurve, parent->customToneCurve1, parent->customToneCurve2, parent->beforeToneCurveBW, parent->afterToneCurveBW, rrm, ggm, bbm, parent->bwAutoR, parent->bwAutoG, parent->bwAutoB, dcpProf, as, histToneCurve); } // apply luminance operations if (todo & (M_LUMINANCE + M_COLOR)) { // //I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here. labnCrop->CopyFrom(laboCrop); bool utili = parent->utili; bool autili = parent->autili; bool butili = parent->butili; bool ccutili = parent->ccutili; bool clcutili = parent->clcutili; bool cclutili = parent->cclutili; LUTu dummy; if (params.colorToning.enabled && params.colorToning.method == "LabGrid") { parent->ipf.colorToningLabGrid(labnCrop, 0,labnCrop->W , 0, labnCrop->H, false); } parent->ipf.shadowsHighlights(labnCrop, params.sh.enabled, params.sh.lab,params.sh.highlights ,params.sh.shadows, params.sh.radius, skip, params.sh.htonalwidth, params.sh.stonalwidth); if (params.localContrast.enabled) { // Alberto's local contrast parent->ipf.localContrast(labnCrop, labnCrop->L, params.localContrast, false, skip); } parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); parent->ipf.vibrance(labnCrop, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); parent->ipf.labColorCorrectionRegions(labnCrop); // if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!cam02)) { parent->ipf.EPDToneMap(labnCrop, 0, skip); } //parent->ipf.EPDToneMap(labnCrop, 5, 1); //Go with much fewer than normal iterates for fast redisplay. // for all treatments Defringe, Sharpening, Contrast detail , Microcontrast they are activated if "CIECAM" function are disabled if (skip == 1) { // if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) { parent->ipf.impulsedenoise(labnCrop); parent->ipf.defringe(labnCrop); } parent->ipf.MLsharpen(labnCrop); // if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) { parent->ipf.MLmicrocontrast(labnCrop); parent->ipf.sharpening(labnCrop, params.sharpening, parent->sharpMask); } } // if (skip==1) { if (params.dirpyrequalizer.cbdlMethod == "aft") { // if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled))) { if (((params.colorappearance.enabled && !settings->autocielab) || (!cam02))) { parent->ipf.dirpyrequalizer(labnCrop, skip); // parent->ipf.Lanczoslab (labnCrop,labnCrop , 1.f/skip); } } if ((params.wavelet.enabled)) { WaveletParams WaveParams = params.wavelet; int kall = 0; int minwin = min(labnCrop->W, labnCrop->H); int maxlevelcrop = 10; // if (cp.mul[9]!=0)maxlevelcrop=10; // adap maximum level wavelet to size of crop if (minwin * skip < 1024) { maxlevelcrop = 9; //sampling wavelet 512 } if (minwin * skip < 512) { maxlevelcrop = 8; //sampling wavelet 256 } if (minwin * skip < 256) { maxlevelcrop = 7; //sampling 128 } if (minwin * skip < 128) { maxlevelcrop = 6; } if (minwin < 64) { maxlevelcrop = 5; } int realtile; if (params.wavelet.Tilesmethod == "big") { realtile = 22; } else /*if (params.wavelet.Tilesmethod == "lit")*/ { realtile = 12; } int tilesize = 128 * realtile; int overlap = (int) tilesize * 0.125f; int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; parent->ipf.Tile_calc(tilesize, overlap, kall, labnCrop->W, labnCrop->H, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); //now we have tile dimensions, overlaps //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int minsizetile = min(tilewidth, tileheight); int maxlev2 = 10; if (minsizetile < 1024 && maxlevelcrop == 10) { maxlev2 = 9; } if (minsizetile < 512) { maxlev2 = 8; } if (minsizetile < 256) { maxlev2 = 7; } if (minsizetile < 128) { maxlev2 = 6; } int maxL = min(maxlev2, maxlevelcrop); if (parent->awavListener) { parent->awavListener->wavChanged(float (maxL)); } WavCurve wavCLVCurve; WavCurve wavdenoise; WavCurve wavdenoiseh; Wavblcurve wavblcurve; WavOpacityCurveRG waOpacityCurveRG; WavOpacityCurveSH waOpacityCurveSH; WavOpacityCurveBY waOpacityCurveBY; WavOpacityCurveW waOpacityCurveW; WavOpacityCurveWL waOpacityCurveWL; LUTf wavclCurve; params.wavelet.getCurves(wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL); LabImage *unshar = nullptr; Glib::ustring provis; LabImage *provradius = nullptr; bool procont = WaveParams.expcontrast; bool prochro = WaveParams.expchroma; bool proedge = WaveParams.expedge; bool profin = WaveParams.expfinal; bool proton = WaveParams.exptoning; bool pronois = WaveParams.expnoise; if (WaveParams.showmask) { // WaveParams.showmask = false; // WaveParams.expclari = true; } if (WaveParams.softrad > 0.f) { provradius = new LabImage(*labnCrop, true); } if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { provis = params.wavelet.CLmethod; params.wavelet.CLmethod = "all"; parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); unshar = new LabImage(*labnCrop, true); params.wavelet.CLmethod = provis; WaveParams.expcontrast = false; WaveParams.expchroma = false; WaveParams.expedge = false; WaveParams.expfinal = false; WaveParams.exptoning = false; WaveParams.expnoise = false; } // parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { WaveParams.expcontrast = procont; WaveParams.expchroma = prochro; WaveParams.expedge = proedge; WaveParams.expfinal = profin; WaveParams.exptoning = proton; WaveParams.expnoise = pronois; if (WaveParams.softrad > 0.f) { array2D ble(labnCrop->W, labnCrop->H); array2D guid(labnCrop->W, labnCrop->H); Imagefloat *tmpImage = nullptr; tmpImage = new Imagefloat(labnCrop->W, labnCrop->H); #ifdef _OPENMP #pragma omp parallel for #endif for (int ir = 0; ir < labnCrop->H ; ir++) for (int jr = 0; jr < labnCrop->W; jr++) { float X, Y, Z; float L = provradius->L[ir][jr]; float a = provradius->a[ir][jr]; float b = provradius->b[ir][jr]; Color::Lab2XYZ(L, a, b, X, Y, Z); guid[ir][jr] = Y / 32768.f; float La = labnCrop->L[ir][jr]; float aa = labnCrop->a[ir][jr]; float ba = labnCrop->b[ir][jr]; Color::Lab2XYZ(La, aa, ba, X, Y, Z); tmpImage->r(ir, jr) = X; tmpImage->g(ir, jr) = Y; tmpImage->b(ir, jr) = Z; ble[ir][jr] = Y / 32768.f; } double epsilmax = 0.0001; double epsilmin = 0.00001; double aepsil = (epsilmax - epsilmin) / 100.f; double bepsil = epsilmin; //epsilmax - 100.f * aepsil; double epsil = aepsil * WaveParams.softrad + bepsil; float blur = 10.f / skip * (0.5f + 0.8f * WaveParams.softrad); rtengine::guidedFilter(guid, ble, ble, blur, epsil, false); #ifdef _OPENMP #pragma omp parallel for #endif for (int ir = 0; ir < labnCrop->H; ir++) for (int jr = 0; jr < labnCrop->W; jr++) { float X = tmpImage->r(ir, jr); float Y = 32768.f * ble[ir][jr]; float Z = tmpImage->b(ir, jr); float L, a, b; Color::XYZ2Lab(X, Y, Z, L, a, b); labnCrop->L[ir][jr] = L; } delete tmpImage; } } if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { float mL = (float)(WaveParams.mergeL / 100.f); float mC = (float)(WaveParams.mergeC / 100.f); float mL0; float mC0; float background = 0.f; int show = 0; if ((WaveParams.CLmethod == "one" || WaveParams.CLmethod == "inf") && WaveParams.Backmethod == "black") { mL0 = mC0 = 0.f; mL = -1.5f * mL; mC = -mC; background = 12000.f; show = 0; } else if (WaveParams.CLmethod == "sup" && WaveParams.Backmethod == "resid") { mL0 = mL; mC0 = mC; background = 0.f; show = 0; } else { mL0 = mL = mC0 = mC = 0.f; background = 0.f; show = 0; } float indic = 1.f; if (WaveParams.showmask){ mL0 = mC0 = -1.f; indic = -1.f; mL = fabs(mL); mC = fabs(mC); show = 1; } #ifdef _OPENMP #pragma omp parallel for #endif for (int x = 0; x < labnCrop->H; x++) for (int y = 0; y < labnCrop->W; y++) { labnCrop->L[x][y] = LIM((1.f + mL0) * (unshar->L[x][y]) + show * background - mL * indic * labnCrop->L[x][y], 0.f, 32768.f); labnCrop->a[x][y] = (1.f + mC0) * (unshar->a[x][y]) - mC * indic * labnCrop->a[x][y]; labnCrop->b[x][y] = (1.f + mC0) * (unshar->b[x][y]) - mC * indic * labnCrop->b[x][y]; } delete unshar; unshar = NULL; if (WaveParams.softrad > 0.f) { delete provradius; provradius = NULL; } } } parent->ipf.softLight(labnCrop, params.softlight); if (params.icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params.icm.trcExp) { const int GW = labnCrop->W; const int GH = labnCrop->H; if(params.icm.trcExp) {//local contrast int level_hr = 7; int maxlevpo = 9; bool wavcurvecont = false; WaveletParams WaveParams = params.wavelet; ColorManagementParams Colparams = params.icm; WavOpacityCurveWL icmOpacityCurveWL; Colparams.getCurves(icmOpacityCurveWL); parent->ipf.complete_local_contrast(labnCrop, labnCrop, WaveParams, Colparams, icmOpacityCurveWL, skip, level_hr, maxlevpo, wavcurvecont); bool enall = false; enall = wavcurvecont && Colparams.wavExp;//enable message only if curve enable and Expander on if (parent->primListener) { parent->primListener->wavlocChanged(float (maxlevpo), float (level_hr), enall); } } std::unique_ptr provis; const float pres = 0.01f * params.icm.preser; if (pres > 0.f && params.icm.wprim != ColorManagementParams::Primaries::DEFAULT) { provis.reset(new LabImage(GW, GH)); provis->CopyFrom(labnCrop); } const std::unique_ptr tmpImage1(new Imagefloat(GW, GH)); parent->ipf.lab2rgb(*labnCrop, *tmpImage1, params.icm.workingProfile); const float gamtone = parent->params->icm.wGamma; const float slotone = parent->params->icm.wSlope; int illum = rtengine::toUnderlying(params.icm.will); const int prim = rtengine::toUnderlying(params.icm.wprim); Glib::ustring prof = params.icm.workingProfile; cmsHTRANSFORM cmsDummy = nullptr; int ill = 0; int locprim = 0; bool gamutcontrol = params.icm.gamut; int catc = rtengine::toUnderlying(params.icm.wcat); float rdx, rdy, grx, gry, blx, bly = 0.f; float meanx, meany, meanxe, meanye = 0.f; parent->ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, -5, prof, 2.4, 12.92310, 0, ill, 0, 0, rdx, rdy, grx, gry, blx, bly,meanx, meany, meanxe, meanye, cmsDummy, true, false, false, false); parent->ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, 5, prof, gamtone, slotone, catc, illum, prim, locprim, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, cmsDummy, false, true, true, gamutcontrol); const int midton = params.icm.wmidtcie; if(midton != 0) { ToneEqualizerParams params; params.enabled = true; params.regularization = 0.f; params.pivot = 0.f; params.bands[0] = 0; params.bands[2] = midton; params.bands[4] = 0; params.bands[5] = 0; int mid = abs(midton); int threshmid = 50; if(mid > threshmid) { params.bands[1] = sign(midton) * (mid - threshmid); params.bands[3] = sign(midton) * (mid - threshmid); } parent->ipf.toneEqualizer(tmpImage1.get(), params, prof, skip, false); } const bool smoothi = params.icm.wsmoothcie; if(smoothi) { ToneEqualizerParams params; params.enabled = true; params.regularization = 0.f; params.pivot = 0.f; params.bands[0] = 0; params.bands[1] = 0; params.bands[2] = 0; params.bands[3] = 0; params.bands[4] = -40;//arbitrary value to adapt with WhiteEvjz - here White Ev # 10 params.bands[5] = -80;//8 Ev and above bool Evsix = true; if(Evsix) {//EV = 6 majority of images params.bands[4] = -15; } parent->ipf.toneEqualizer(tmpImage1.get(), params, prof, skip, false); } parent->ipf.rgb2lab(*tmpImage1, *labnCrop, params.icm.workingProfile); //labnCrop and provis if (provis) { parent->ipf.preserv(labnCrop, provis.get(), GW, GH); } if (params.icm.fbw) { #ifdef _OPENMP #pragma omp parallel for #endif for (int x = 0; x < GH; x++) for (int y = 0; y < GW; y++) { labnCrop->a[x][y] = 0.f; labnCrop->b[x][y] = 0.f; } } } if (params.colorappearance.enabled) { float fnum = parent->imgsrc->getMetaData()->getFNumber(); // F number float fiso = parent->imgsrc->getMetaData()->getISOSpeed() ; // ISO float fspeed = parent->imgsrc->getMetaData()->getShutterSpeed() ; // Speed double fcomp = parent->imgsrc->getMetaData()->getExpComp(); // Compensation +/- double adap; // Scene's luminosity adaptation factor if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) { //if no exif data or wrong adap = 2000.; } else { double E_V = fcomp + log2(double ((fnum * fnum) / fspeed / (fiso / 100.f))); double kexp = 0.; E_V += kexp * params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV E_V += 0.5 * log2(params.raw.expos); // exposure raw white point ; log2 ==> linear to EV adap = pow(2., E_V - 3.); // cd / m2 // end calculation adaptation scene luminosity } bool execsharp = false; if (skip == 1) { execsharp = true; } if (!cieCrop) { cieCrop = new CieImage(cropw, croph); } float d, dj, yb; // not used after this block parent->ipf.ciecam_02float(cieCrop, float (adap), 1, 2, labnCrop, ¶ms, parent->customColCurve1, parent->customColCurve2, parent->customColCurve3, dummy, dummy, parent->CAMBrightCurveJ, parent->CAMBrightCurveQ, parent->CAMMean, 0, skip, execsharp, d, dj, yb, 1, parent->sharpMask); } else { // CIECAM is disabled, we free up its image buffer to save some space if (cieCrop) { delete cieCrop; } cieCrop = nullptr; } } // all pipette buffer processing should be finished now PipetteBuffer::setReady(); // Computing the preview image, i.e. converting from lab->Monitor color space (soft-proofing disabled) or lab->Output profile->Monitor color space (soft-proofing enabled) parent->ipf.lab2monitorRgb(labnCrop, cropImg); if (cropImageListener) { // Computing the internal image for analysis, i.e. conversion from lab->Output profile (rtSettings.HistogramWorking disabled) or lab->WCS (rtSettings.HistogramWorking enabled) // internal image in output color space for analysis Image8 *cropImgtrue = parent->ipf.lab2rgb(labnCrop, 0, 0, cropw, croph, params.icm); int finalW = rqcropw; if (cropImg->getWidth() - leftBorder < finalW) { finalW = cropImg->getWidth() - leftBorder; } int finalH = rqcroph; if (cropImg->getHeight() - upperBorder < finalH) { finalH = cropImg->getHeight() - upperBorder; } Image8* final = new Image8(finalW, finalH); Image8* finaltrue = new Image8(finalW, finalH); for (int i = 0; i < finalH; i++) { memcpy(final->data + 3 * i * finalW, cropImg->data + 3 * (i + upperBorder)*cropw + 3 * leftBorder, 3 * finalW); memcpy(finaltrue->data + 3 * i * finalW, cropImgtrue->data + 3 * (i + upperBorder)*cropw + 3 * leftBorder, 3 * finalW); } cropImageListener->setDetailedCrop(final, finaltrue, params.icm, params.crop, rqcropx, rqcropy, rqcropw, rqcroph, skip); delete final; delete finaltrue; delete cropImgtrue; } } void Crop::freeAll() { if (cropAllocated) { if (origCrop) { delete origCrop; origCrop = nullptr; } if (transCrop) { delete transCrop; transCrop = nullptr; } if (laboCrop) { delete laboCrop; laboCrop = nullptr; } if (labnCrop) { delete labnCrop; labnCrop = nullptr; } if (cropImg) { delete cropImg; cropImg = nullptr; } if (cieCrop) { delete cieCrop; cieCrop = nullptr; } if (shbuffer) { delete [] shbuffer; shbuffer = nullptr; } if (shbuf_real) { delete [] shbuf_real; shbuf_real = nullptr; } PipetteBuffer::flush(); } cropAllocated = false; } namespace { bool check_need_larger_crop_for_lcp_distortion(int fw, int fh, int x, int y, int w, int h, const procparams::ProcParams ¶ms) { if (x == 0 && y == 0 && w == fw && h == fh) { return false; } return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp() || params.lensProf.useMetadata())); } } // namespace /** @brief Handles crop's image buffer reallocation and trigger sizeChanged of SizeListener[s] * If the scale changes, this method will free all buffers and reallocate ones of the new size. * It will then tell to the SizeListener that size has changed (sizeChanged) */ bool Crop::setCropSizes(int cropX, int cropY, int cropW, int cropH, int skip, bool internal) { if (!internal) { cropMutex.lock(); } bool changed = false; rqcropx = cropX; rqcropy = cropY; rqcropw = cropW; rqcroph = cropH; // store and set requested crop size int rqx1 = LIM(rqcropx, 0, parent->fullw - 1); int rqy1 = LIM(rqcropy, 0, parent->fullh - 1); int rqx2 = rqx1 + rqcropw - 1; int rqy2 = rqy1 + rqcroph - 1; rqx2 = LIM(rqx2, 0, parent->fullw - 1); rqy2 = LIM(rqy2, 0, parent->fullh - 1); this->skip = skip; // add border, if possible int bx1 = rqx1 - skip * borderRequested; int by1 = rqy1 - skip * borderRequested; int bx2 = rqx2 + skip * borderRequested; int by2 = rqy2 + skip * borderRequested; // clip it to fit into image area bx1 = LIM(bx1, 0, parent->fullw - 1); by1 = LIM(by1, 0, parent->fullh - 1); bx2 = LIM(bx2, 0, parent->fullw - 1); by2 = LIM(by2, 0, parent->fullh - 1); int bw = bx2 - bx1 + 1; int bh = by2 - by1 + 1; // determine which part of the source image is required to compute the crop rectangle int orx, ory, orw, orh; orx = bx1; ory = by1; orw = bw; orh = bh; parent->ipf.transCoord(parent->fw, parent->fh, bx1, by1, bw, bh, orx, ory, orw, orh); if (parent->ipf.needsTransform(skips(parent->fw, skip), skips(parent->fh, skip), parent->imgsrc->getRotateDegree(), parent->imgsrc->getMetaData())) { if (check_need_larger_crop_for_lcp_distortion(parent->fw, parent->fh, orx, ory, orw, orh, *parent->params)) { // TODO - this is an estimate of the max distortion relative to the image size. ATM it is hardcoded to be 15%, which seems enough. If not, need to revise int dW = int (double (parent->fw) * 0.15 / (2 * skip)); int dH = int (double (parent->fh) * 0.15 / (2 * skip)); int x1 = orx - dW; int x2 = orx + orw + dW; int y1 = ory - dH; int y2 = ory + orh + dH; if (x1 < 0) { x2 += -x1; x1 = 0; } if (x2 > parent->fw) { x1 -= x2 - parent->fw; x2 = parent->fw; } if (y1 < 0) { y2 += -y1; y1 = 0; } if (y2 > parent->fh) { y1 -= y2 - parent->fh; y2 = parent->fh; } orx = max(x1, 0); ory = max(y1, 0); orw = min(x2 - x1, parent->fw - orx); orh = min(y2 - y1, parent->fh - ory); } } leftBorder = skips(rqx1 - bx1, skip); upperBorder = skips(rqy1 - by1, skip); PreviewProps cp(orx, ory, orw, orh, skip); int orW, orH; parent->imgsrc->getSize(cp, orW, orH); if (trafx != orx || trafy != ory) { trafx = orx; trafy = ory; changed = true; } int cw = skips(bw, skip); int ch = skips(bh, skip); EditType editType = ET_PIPETTE; if (const auto editProvider = PipetteBuffer::getDataProvider()) { if (const auto editSubscriber = editProvider->getCurrSubscriber()) { editType = editSubscriber->getEditingType(); } } if (cw != cropw || ch != croph || orW != trafw || orH != trafh) { cropw = cw; croph = ch; trafw = orW; trafh = orH; if (!origCrop) { origCrop = new Imagefloat; } origCrop->allocate(trafw, trafh); // Resizing the buffer (optimization) // if transCrop doesn't exist yet, it'll be created where necessary if (transCrop) { transCrop->allocate(cropw, croph); } if (laboCrop) { delete laboCrop; // laboCrop can't be resized } laboCrop = new LabImage(cropw, croph); // if (translabCrop) translabCrop->reallocLab(); if (labnCrop) { delete labnCrop; // labnCrop can't be resized } labnCrop = new LabImage(cropw, croph); if (!cropImg) { cropImg = new Image8; } cropImg->allocate(cropw, croph); // Resizing the buffer (optimization) //cieCrop is only used in Crop::update, it is destroyed now but will be allocated on first use if (cieCrop) { delete cieCrop; cieCrop = nullptr; } if (shbuffer) { delete [] shbuffer; } if (shbuf_real) { delete [] shbuf_real; } shbuffer = new float*[croph]; shbuf_real = new float[(croph + 2)*cropw]; for (int i = 0; i < croph; i++) { shbuffer[i] = shbuf_real + cropw * i + cropw; } if (editType == ET_PIPETTE) { PipetteBuffer::resize(cropw, croph); } else if (PipetteBuffer::bufferCreated()) { PipetteBuffer::flush(); } cropAllocated = true; changed = true; } cropx = bx1; cropy = by1; if (!internal) { cropMutex.unlock(); } return changed; } /** @brief Look out if a new thread has to be started to process the update * * @return If true, a new updating thread has to be created. If false, the current updating thread will be used */ bool Crop::tryUpdate() { bool needsNewThread = true; if (updating) { // tells to the updater thread that a new update is pending newUpdatePending = true; // no need for a new thread, the current one will do the job needsNewThread = false; } else // the crop is now being updated ...well, when fullUpdate will be called { updating = true; } return needsNewThread; } /* @brief Handles Crop updating in its own thread * * This method will cycle updates as long as Crop::newUpdatePending will be true. During the processing, * intermediary update will be automatically flushed by Crop::tryUpdate. * * This method is called when the visible part of the crop has changed (resize, zoom, etc..), so it needs a full update */ void Crop::fullUpdate() { parent->updaterThreadStart.lock(); if (parent->updaterRunning && parent->thread) { // Do NOT reset changes here, since in a long chain of events it will lead to chroma_scale not being updated, // causing Color::lab2rgb to return a black image on some opens //parent->changeSinceLast = 0; parent->thread->join(); } if (parent->plistener) { parent->plistener->setProgressState(true); } // If there are more update request, the following WHILE will collect it newUpdatePending = true; while (newUpdatePending) { newUpdatePending = false; update(ALL); } updating = false; // end of crop update if (parent->plistener) { parent->plistener->setProgressState(false); } parent->updaterThreadStart.unlock(); } int Crop::get_skip() { MyMutex::MyLock lock(cropMutex); return skip; } int Crop::getLeftBorder() { MyMutex::MyLock lock(cropMutex); return leftBorder; } int Crop::getUpperBorder() { MyMutex::MyLock lock(cropMutex); return upperBorder; } }