/* * 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 "dcrop.h" #include "curves.h" #include "mytime.h" #include "refreshmap.h" #include "rt_math.h" // "ceil" rounding #define SKIPS(a,b) ((a) / (b) + ((a) % (b) > 0)) namespace rtengine { extern const Settings* settings; Crop::Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow) : EditBuffer(editDataProvider), origCrop(NULL), laboCrop(NULL), labnCrop(NULL), cropImg(NULL), cbuf_real(NULL), cshmap(NULL), transCrop(NULL), cieCrop(NULL), cbuffer(NULL), 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(NULL), 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() { EditSubscriber *subscriber = EditBuffer::dataProvider ? EditBuffer::dataProvider->getCurrSubscriber() : NULL; 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 EditSubscriber *oldSubscriber = EditBuffer::dataProvider ? EditBuffer::dataProvider->getCurrSubscriber() : NULL; if (newSubscriber == NULL || (oldSubscriber != NULL && oldSubscriber->getEditBufferType() != newSubscriber->getEditBufferType())) { if (EditBuffer::imgFloatBuffer!=NULL) { delete EditBuffer::imgFloatBuffer; EditBuffer::imgFloatBuffer = NULL; } if (EditBuffer::LabBuffer!=NULL) { delete EditBuffer::LabBuffer; EditBuffer::LabBuffer = NULL; } if (EditBuffer::singlePlaneBuffer.getW()!=-1) { EditBuffer::singlePlaneBuffer.flushData(); } } if (newSubscriber == NULL && oldSubscriber != NULL && oldSubscriber->getEditingType() == ET_OBJECTS) { printf("Free object buffers\n"); EditBuffer::resize(0, 0); // This will delete the objects buffer } else if (newSubscriber && newSubscriber->getEditingType() == ET_OBJECTS) { EditBuffer::resize(cropw, croph, newSubscriber); } // If oldSubscriber == NULL && newSubscriber != NULL -> the image will be allocated when necessary } 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; bool overrideWindow = false; if (cropImageListener) 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; bool needstransform = parent->ipf.needsTransform(); if (todo & (M_INIT|M_LINDENOISE)) { 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 = NULL;//for Luminance denoise curve NoiseCurve noiseLCurve; NoiseCurve noiseCCurve; float autoNR = (float) settings->nrauto;// float autoNRmax = (float) settings->nrautomax;// params.dirpyrDenoise.getCurves(noiseLCurve, noiseCCurve); int tilesize; int overlap; if(settings->leveldnti ==0) { tilesize = 1024; overlap = 128; } if(settings->leveldnti ==1) { tilesize = 768; overlap = 96; } int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; int kall=2; parent->ipf.Tile_calc (tilesize, overlap, kall, widIm, heiIm, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); kall=0; float *ch_M = new float [9];//allocate memory float *max_r = new float [9]; float *max_b = new float [9]; 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;cXleveldnautsimpl==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.icm, 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.icm, 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.icm, 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;ccleveldnv ==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.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=="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(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;iir(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, nresi, highresi, 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,nresi, highresi, 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 && ((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((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];//coordonate of part of image to mesure 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.icm, params.raw ); // we only need image reduced to 1/4 here for(int ii=0;iir(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, nresi=0.f, highresi=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, nresi, highresi, 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; ch_M[hcr*3 + wcr]=pondcorrec*chaut; max_r[hcr*3 + wcr]=pondcorrec*maxredaut; 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 fot gamma=1 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=="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;} 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]); parent->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]) { 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 { 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] 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; 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.icm, params.raw ); } 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;iir(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_LINDENOISE) { if (skip==1 && denoiseParams.enabled) { int kall=0; float chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi; parent->ipf.RGB_denoise(kall, origCrop, origCrop, calclum, ch_M, max_r, max_b, parent->imgsrc->isRAW(), /*Roffset,*/ denoiseParams, parent->imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, 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); } } } parent->imgsrc->convertColorSpace(origCrop, params.icm, parent->currWB); 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; 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); // transform if (needstransform) { if (!transCrop) transCrop = new Imagefloat (cropw, croph); if ((todo & M_TRANSFORM) && 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()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), parent->imgsrc->getMetaData()->getFocusDist(), parent->imgsrc->getRotateDegree(), false); if (transCrop) baseCrop = transCrop; } else { if (transCrop) delete transCrop; transCrop = NULL; } // blurmap for shadow & highlights if ((todo & M_BLURMAP) && params.sh.enabled) { double radius = sqrt (double(SKIPS(parent->fw,skip)*SKIPS(parent->fw,skip)+SKIPS(parent->fh,skip)*SKIPS(parent->fh,skip))) / 2.0; double shradius = params.sh.radius; if (!params.sh.hq) shradius *= radius / 1800.0; if(!cshmap) cshmap = new SHMap (cropw, croph, true); cshmap->update (baseCrop, shradius, parent->ipf.lumimul, params.sh.hq, skip); if(parent->shmap->min_f < 65535.f) // don't call forceStat with wrong values cshmap->forceStat (parent->shmap->max_f, parent->shmap->min_f, parent->shmap->avg); } // shadows & highlights & tone curve & convert to cielab /*int xref,yref; xref=000;yref=000; if (colortest && cropw>115 && croph>115) for(int j=1;j<5;j++){ xref+=j*30;yref+=j*30; if (settings->verbose) printf("before rgbProc RGB Xr%i Yr%i Skip=%d R=%f G=%f B=%f gamma=%f \n",xref,yref,skip, baseCrop->r[(int)(xref/skip)][(int)(yref/skip)]/256, baseCrop->g[(int)(xref/skip)][(int)(yref/skip)]/256, baseCrop->b[(int)(xref/skip)][(int)(yref/skip)]/256, parent->imgsrc->getGamma()); }*/ 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; parent->ipf.moyeqt (baseCrop, moyS, eqty);//return image : mean saturation and standard dev of saturation //printf("moy=%f ET=%f\n", moyS,eqty); 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 } if (todo & M_RGBCURVE) { double rrm, ggm, bbm; DCPProfile *dcpProf = parent->imgsrc->getDCP(params.icm, parent->currWB); parent->ipf.rgbProc (baseCrop, laboCrop, this, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, cshmap, params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, satLimit ,satLimitOpacity, 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); } /*xref=000;yref=000; if (colortest && cropw>115 && croph>115) for(int j=1;j<5;j++){ xref+=j*30;yref+=j*30; if (settings->verbose) { printf("after rgbProc RGB Xr%i Yr%i Skip=%d R=%f G=%f B=%f \n",xref,yref,skip, baseCrop->r[(int)(xref/skip)][(int)(yref/skip)]/256, baseCrop->g[(int)(xref/skip)][(int)(yref/skip)]/256, baseCrop->b[(int)(xref/skip)][(int)(yref/skip)]/256); printf("after rgbProc Lab Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip, laboCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327, laboCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327, laboCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327); } }*/ // 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); //parent->ipf.luminanceCurve (labnCrop, labnCrop, parent->lumacurve); bool utili=parent->utili; bool autili=parent->autili; bool butili=parent->butili; bool ccutili=parent->ccutili; bool clcutili=parent->clcutili; bool cclutili=parent->cclutili; bool wavcontlutili=parent->wavcontlutili; LUTu dummy; 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, dummy, dummy); parent->ipf.vibrance (labnCrop); if((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) parent->ipf.EPDToneMap(labnCrop,5,1); //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)) { parent->ipf.impulsedenoise (labnCrop);} if((params.colorappearance.enabled && !settings->autocielab) ||(!params.colorappearance.enabled) ) {parent->ipf.defringe (labnCrop);} parent->ipf.MLsharpen (labnCrop); if((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { parent->ipf.MLmicrocontrast (labnCrop); parent->ipf.sharpening (labnCrop, (float**)cbuffer, params.sharpening); } } // if (skip==1) { WaveletParams WaveParams = params.wavelet; if((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { parent->ipf.dirpyrequalizer (labnCrop, skip); // parent->ipf.Lanczoslab (labnCrop,labnCrop , 1.f/skip); } 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; if(params.wavelet.Tilesmethod=="lit") realtile=12; int tilesize; int overlap; tilesize = 1024; overlap = 128; tilesize=128*realtile; //overlap=(int) tilesize*params->wavelet.overl; overlap=(int) tilesize*0.125f; // printf("overl=%d\n",overlap); 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)); if((params.wavelet.enabled)) { WavCurve wavCLVCurve; WavOpacityCurveRG waOpacityCurveRG; WavOpacityCurveBY waOpacityCurveBY; WavOpacityCurveW waOpacityCurveW; WavOpacityCurveWL waOpacityCurveWL; LUTf wavclCurve; LUTu dummy; params.wavelet.getCurves(wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL); parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, wavcontlutili, skip); } // } // } 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))); 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= pow(2., E_V-3.);// cd / m2 // end calculation adaptation scene luminosity } int begh = 0, endh = labnCrop->H; bool execsharp=false; if(skip==1) execsharp=true; if (!cieCrop) { cieCrop = new CieImage (cropw, croph); } if(settings->ciecamfloat) { float d; // not used after this block parent->ipf.ciecam_02float (cieCrop, float(adap), begh, endh, 1, 2, labnCrop, ¶ms, parent->customColCurve1, parent->customColCurve2, parent->customColCurve3, dummy, dummy, parent->CAMBrightCurveJ, parent->CAMBrightCurveQ, parent->CAMMean, 5, 1, execsharp, d, skip, 1); } else { double dd; // not used after this block parent->ipf.ciecam_02 (cieCrop,adap, begh, endh, 1, 2, labnCrop, ¶ms, parent->customColCurve1, parent->customColCurve2, parent->customColCurve3, dummy, dummy, parent->CAMBrightCurveJ, parent->CAMBrightCurveQ, parent->CAMMean, 5, 1, execsharp, dd, skip, 1); } } else { // CIECAM is disbaled, we free up its image buffer to save some space if (cieCrop) delete cieCrop; cieCrop=NULL; } } // all pipette buffer processing should be finished now EditBuffer::setReady(); // switch back to rgb parent->ipf.lab2monitorRgb (labnCrop, cropImg); //parent->ipf.lab2monitorRgb (laboCrop, cropImg); //cropImg = baseCrop->to8(); /* // int xref,yref; xref=000;yref=000; if (colortest && cropw>115 && croph>115) for(int j=1;j<5;j++){ xref+=j*30;yref+=j*30; int rlin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))]/255.0) * 255.0); int glin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+1]/255.0) * 255.0); int blin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+2]/255.0) * 255.0); printf("after lab2rgb RGB lab2 Xr%i Yr%i Skip=%d R=%d G=%d B=%d \n",xref,yref,skip, rlin,glin,blin); //cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))], //cropImg->data[(3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+1)], //cropImg->data[(3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+2)]); //printf("after lab2rgb Lab lab2 Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip, labnCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327,labnCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327,labnCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327); printf("after lab2rgb Lab Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip, labnCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327, labnCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327, labnCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327)q; } */ /* if (colortest && cropImg->height>115 && cropImg->width>115) {//for testing xref=000;yref=000; printf("dcrop final R= %d G= %d B= %d \n", cropImg->data[3*xref/(skip)*(cropImg->width+1)], cropImg->data[3*xref/(skip)*(cropImg->width+1)+1], cropImg->data[3*xref/(skip)*(cropImg->width+1)+2]); } */ if (cropImageListener) { // this in output space held in parallel to allow analysis like shadow/highlight Glib::ustring outProfile=params.icm.output; Glib::ustring workProfile=params.icm.working; Image8 *cropImgtrue; if(settings->HistogramWorking) cropImgtrue = parent->ipf.lab2rgb (labnCrop, 0,0,cropw,croph, workProfile, false); else { if (params.icm.output=="" || params.icm.output==ColorManagementParams::NoICMString) outProfile="sRGB"; cropImgtrue = parent->ipf.lab2rgb (labnCrop, 0,0,cropw,croph, outProfile, false); } 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; idata + 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 (settings->verbose) printf ("freeallcrop starts %d\n", (int)cropAllocated); if (cropAllocated) { if (origCrop ) { delete origCrop; origCrop=NULL; } if (transCrop) { delete transCrop; transCrop=NULL; } if (laboCrop ) { delete laboCrop; laboCrop=NULL; } if (labnCrop ) { delete labnCrop; labnCrop=NULL; } if (cropImg ) { delete cropImg; cropImg=NULL; } if (cieCrop ) { delete cieCrop; cieCrop=NULL; } if (cbuf_real) { delete [] cbuf_real; cbuf_real=NULL; } if (cbuffer ) { delete [] cbuffer; cbuffer=NULL; } if (cshmap ) { delete cshmap; cshmap=NULL; } EditBuffer::flush(); } cropAllocated = false; } /** @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 rcx, int rcy, int rcw, int rch, int skip, bool internal) { if (settings->verbose) printf ("setcropsizes before lock\n"); if (!internal) cropMutex.lock (); bool changed = false; rqcropx = rcx; rqcropy = rcy; rqcropw = rcw; rqcroph = rch; // 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; ProcParams& params = parent->params; parent->ipf.transCoord (parent->fw, parent->fh, bx1, by1, bw, bh, orx, ory, orw, orh); int tr = getCoarseBitMask(params.coarse); PreviewProps cp (orx, ory, orw, orh, skip); int orW, orH; parent->imgsrc->getSize (tr, cp, orW, orH); int cw = SKIPS(bw,skip); int ch = SKIPS(bh,skip); leftBorder = SKIPS(rqx1-bx1,skip); upperBorder = SKIPS(rqy1-by1,skip); if (settings->verbose) printf ("setsizes starts (%d, %d, %d, %d, %d, %d)\n", orW, orH, trafw, trafh,cw,ch); 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 (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=NULL; } if (cbuffer ) delete [] cbuffer; if (cbuf_real) delete [] cbuf_real; if (cshmap ) { delete cshmap; cshmap = NULL;} cbuffer = new float*[croph]; cbuf_real= new float[(croph+2)*cropw]; for (int i=0; iverbose) printf ("setsizes ends\n"); 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 (); } }