//////////////////////////////////////////////////////////////// // // CFA denoise by wavelet transform, FT filtering // // copyright (c) 2008-2012 Emil Martinec // // // code dated: March 9, 2012 // // FTblockDN.cc is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program 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 this program. If not, see . // //////////////////////////////////////////////////////////////// #include #include #include "../rtgui/threadutils.h" #include "gauss.h" #include "rtengine.h" #include "improcfun.h" #include "LUT.h" #include "array2D.h" #include "iccmatrices.h" #include "boxblur.h" #include "rt_math.h" #include "mytime.h" #include "sleef.c" #include "opthelper.h" #include "StopWatch.h" #ifdef _OPENMP #include #endif #include "cplx_wavelet_dec.h" //#define MIN(a,b) ((a) < (b) ? (a) : (b)) //#define MAX(a,b) ((a) > (b) ? (a) : (b)) //#define LIM(x,min,max) MAX(min,MIN(x,max)) //#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) //#define CLIP(x) LIM(x,0,65535) #define TS 64 // Tile size #define offset 25 // shift between tiles #define fTS ((TS/2+1)) // second dimension of Fourier tiles #define blkrad 1 // radius of block averaging #define epsilon 0.001f/(TS*TS) //tolerance #define PIX_SORT(a,b) { if ((a)>(b)) {temp=(a);(a)=(b);(b)=temp;} } #define med3(a0,a1,a2,a3,a4,a5,a6,a7,a8,median) { \ pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; pp[5]=a5; pp[6]=a6; pp[7]=a7; pp[8]=a8; \ PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \ PIX_SORT(pp[0],pp[1]); PIX_SORT(pp[3],pp[4]); PIX_SORT(pp[6],pp[7]); \ PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \ PIX_SORT(pp[0],pp[3]); PIX_SORT(pp[5],pp[8]); PIX_SORT(pp[4],pp[7]); \ PIX_SORT(pp[3],pp[6]); PIX_SORT(pp[1],pp[4]); PIX_SORT(pp[2],pp[5]); \ PIX_SORT(pp[4],pp[7]); PIX_SORT(pp[4],pp[2]); PIX_SORT(pp[6],pp[4]); \ PIX_SORT(pp[4],pp[2]); median=pp[4];} //pp4 = median #define med2(a0,a1,a2,a3,a4,median) { \ pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; \ PIX_SORT(pp[0],pp[1]) ; PIX_SORT(pp[3],pp[4]) ; PIX_SORT(pp[0],pp[3]) ;\ PIX_SORT(pp[1],pp[4]) ; PIX_SORT(pp[1],pp[2]) ; PIX_SORT(pp[2],pp[3]) ;\ PIX_SORT(pp[1],pp[2]) ; median=pp[2] ;} #define med5(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,median) { \ pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; pp[5]=a5; pp[6]=a6; pp[7]=a7; pp[8]=a8; pp[9]=a9; pp[10]=a10; pp[11]=a11; pp[12]=a12; \ pp[13]=a13; pp[14]=a14; pp[15]=a15; pp[16]=a16; pp[17]=a17; pp[18]=a18; pp[19]=a19; pp[20]=a20; pp[21]=a21; pp[22]=a22; pp[23]=a23; pp[24]=a24; \ PIX_SORT(pp[0], pp[1]) ; PIX_SORT(pp[3], pp[4]) ; PIX_SORT(pp[2], pp[4]) ;\ PIX_SORT(pp[2], pp[3]) ; PIX_SORT(pp[6], pp[7]) ; PIX_SORT(pp[5], pp[7]) ;\ PIX_SORT(pp[5], pp[6]) ; PIX_SORT(pp[9], pp[10]) ; PIX_SORT(pp[8], pp[10]) ;\ PIX_SORT(pp[8], pp[9]) ; PIX_SORT(pp[12], pp[13]) ; PIX_SORT(pp[11], pp[13]) ;\ PIX_SORT(pp[11], pp[12]) ; PIX_SORT(pp[15], pp[16]) ; PIX_SORT(pp[14], pp[16]) ;\ PIX_SORT(pp[14], pp[15]) ; PIX_SORT(pp[18], pp[19]) ; PIX_SORT(pp[17], pp[19]) ;\ PIX_SORT(pp[17], pp[18]) ; PIX_SORT(pp[21], pp[22]) ; PIX_SORT(pp[20], pp[22]) ;\ PIX_SORT(pp[20], pp[21]) ; PIX_SORT(pp[23], pp[24]) ; PIX_SORT(pp[2], pp[5]) ;\ PIX_SORT(pp[3], pp[6]) ; PIX_SORT(pp[0], pp[6]) ; PIX_SORT(pp[0], pp[3]) ;\ PIX_SORT(pp[4], pp[7]) ; PIX_SORT(pp[1], pp[7]) ; PIX_SORT(pp[1], pp[4]) ;\ PIX_SORT(pp[11], pp[14]) ; PIX_SORT(pp[8], pp[14]) ; PIX_SORT(pp[8], pp[11]) ;\ PIX_SORT(pp[12], pp[15]) ; PIX_SORT(pp[9], pp[15]) ; PIX_SORT(pp[9], pp[12]) ;\ PIX_SORT(pp[13], pp[16]) ; PIX_SORT(pp[10], pp[16]) ; PIX_SORT(pp[10], pp[13]) ;\ PIX_SORT(pp[20], pp[23]) ; PIX_SORT(pp[17], pp[23]) ; PIX_SORT(pp[17], pp[20]) ;\ PIX_SORT(pp[21], pp[24]) ; PIX_SORT(pp[18], pp[24]) ; PIX_SORT(pp[18], pp[21]) ;\ PIX_SORT(pp[19], pp[22]) ; PIX_SORT(pp[8], pp[17]) ; PIX_SORT(pp[9], pp[18]) ;\ PIX_SORT(pp[0], pp[18]) ; PIX_SORT(pp[0], pp[9]) ; PIX_SORT(pp[10], pp[19]) ;\ PIX_SORT(pp[1], pp[19]) ; PIX_SORT(pp[1], pp[10]) ; PIX_SORT(pp[11], pp[20]) ;\ PIX_SORT(pp[2], pp[20]) ; PIX_SORT(pp[2], pp[11]) ; PIX_SORT(pp[12], pp[21]) ;\ PIX_SORT(pp[3], pp[21]) ; PIX_SORT(pp[3], pp[12]) ; PIX_SORT(pp[13], pp[22]) ;\ PIX_SORT(pp[4], pp[22]) ; PIX_SORT(pp[4], pp[13]) ; PIX_SORT(pp[14], pp[23]) ;\ PIX_SORT(pp[5], pp[23]) ; PIX_SORT(pp[5], pp[14]) ; PIX_SORT(pp[15], pp[24]) ;\ PIX_SORT(pp[6], pp[24]) ; PIX_SORT(pp[6], pp[15]) ; PIX_SORT(pp[7], pp[16]) ;\ PIX_SORT(pp[7], pp[19]) ; PIX_SORT(pp[13], pp[21]) ; PIX_SORT(pp[15], pp[23]) ;\ PIX_SORT(pp[7], pp[13]) ; PIX_SORT(pp[7], pp[15]) ; PIX_SORT(pp[1], pp[9]) ;\ PIX_SORT(pp[3], pp[11]) ; PIX_SORT(pp[5], pp[17]) ; PIX_SORT(pp[11], pp[17]) ;\ PIX_SORT(pp[9], pp[17]) ; PIX_SORT(pp[4], pp[10]) ; PIX_SORT(pp[6], pp[12]) ;\ PIX_SORT(pp[7], pp[14]) ; PIX_SORT(pp[4], pp[6]) ; PIX_SORT(pp[4], pp[7]) ;\ PIX_SORT(pp[12], pp[14]) ; PIX_SORT(pp[10], pp[14]) ; PIX_SORT(pp[6], pp[7]) ;\ PIX_SORT(pp[10], pp[12]) ; PIX_SORT(pp[6], pp[10]) ; PIX_SORT(pp[6], pp[17]) ;\ PIX_SORT(pp[12], pp[17]) ; PIX_SORT(pp[7], pp[17]) ; PIX_SORT(pp[7], pp[10]) ;\ PIX_SORT(pp[12], pp[18]) ; PIX_SORT(pp[7], pp[12]) ; PIX_SORT(pp[10], pp[18]) ;\ PIX_SORT(pp[12], pp[20]) ; PIX_SORT(pp[10], pp[20]) ; PIX_SORT(pp[10], pp[12]) ;\ median=pp[12];} #define ELEM_FLOAT_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; } namespace rtengine { // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* Structure of the algorithm: 1. Compute an initial denoise of the image via undecimated wavelet transform and universal thresholding modulated by user input. 2. Decompose the residual image into TSxTS size tiles, shifting by 'offset' each step (so roughly each pixel is in (TS/offset)^2 tiles); Discrete Cosine transform the tiles. 3. Filter the DCT data to pick out patterns missed by the wavelet denoise 4. Inverse DCT the denoised tile data and combine the tiles into a denoised output image. */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% extern const Settings* settings; // Median calculation using quicksort float fq_sort2(float arr[], int n) { int low, high ; int median; int middle, ll, hh; low = 0 ; high = n-1 ; median = (low + high) / 2; for (;;) { if (high <= low) return arr[median] ; if (high == low + 1) { if (arr[low] > arr[high]) ELEM_FLOAT_SWAP(arr[low], arr[high]) ; return arr[median] ; } middle = (low + high) / 2; if (arr[middle] > arr[high]) ELEM_FLOAT_SWAP(arr[middle], arr[high]) ; if (arr[low] > arr[high]) ELEM_FLOAT_SWAP(arr[low], arr[high]) ; if (arr[middle] > arr[low]) ELEM_FLOAT_SWAP(arr[middle], arr[low]) ; ELEM_FLOAT_SWAP(arr[middle], arr[low+1]) ; ll = low + 1; hh = high; for (;;) { do ll++; while (arr[low] > arr[ll]) ; do hh--; while (arr[hh] > arr[low]) ; if (hh < ll) break; ELEM_FLOAT_SWAP(arr[ll], arr[hh]) ; } ELEM_FLOAT_SWAP(arr[low], arr[hh]) ; if (hh <= median) low = ll; if (hh >= median) high = hh - 1; } } float media(float *elements, int N) { int i,j,min; float temp; // Order elements (only half of them) for (i = 0; i < (N >> 1) + 1; ++i) { // Find position of minimum element min = i; for (j = i + 1; j < N; ++j) if (elements[j] < elements[min]) min = j; // Put found minimum element in its place temp = elements[i]; elements[i] = elements[min]; elements[min] = temp; } // Get result - the middle element return elements[N >> 1]; } void ImProcFunctions::Tile_calc (int tilesize, int overlap, int kall, int imwidth, int imheight, int &numtiles_W, int &numtiles_H, int &tilewidth, int &tileheight, int &tileWskip, int &tileHskip) { if(kall==2) { if (imwidthdata,src->data,dst->width*dst->height*3*sizeof(float)); if(calclum) { delete calclum; calclum = NULL; } return; } static MyMutex FftwMutex; MyMutex::MyLock lock(FftwMutex); int hei,wid; // float LLum,AAum,BBum; float** lumcalc; float** acalc; float** bcalc; if(noiseLCurve || noiseCCurve) { hei=calclum->height; wid=calclum->width; TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); double wpi[3][3] = { {wprofi[0][0],wprofi[0][1],wprofi[0][2]}, {wprofi[1][0],wprofi[1][1],wprofi[1][2]}, {wprofi[2][0],wprofi[2][1],wprofi[2][2]} }; lumcalc = new float*[hei]; for (int i=0; ir(ii,jj); float GL = calclum->g(ii,jj); float BL = calclum->b(ii,jj); // determine luminance for noisecurve float XL,YL,ZL; Color::rgbxyz(RL,GL,BL,XL,YL,ZL,wpi); Color::XYZ2Lab(XL, YL, ZL, LLum, AAum, BBum); lumcalc[ii][jj]=LLum; acalc[ii][jj]=AAum; bcalc[ii][jj]=BBum; } } delete calclum; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /*if (plistener) { plistener->setProgressStr ("Denoise..."); plistener->setProgress (0.0); }*/ // volatile double progress = 0.0; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% const short int imheight=src->height, imwidth=src->width; // printf("imW=%d imH=%d\n",imwidth,imheight); // printf("Chroma=%f\n", dnparams.chroma); Qhigh=1.0f; if(dnparams.smethod=="shalbi") Qhigh=1.f/(float) settings->nrhigh; if (dnparams.luma!=0 || dnparams.chroma!=0 || dnparams.methodmed=="Lab" || dnparams.methodmed=="Lonly" ) { const bool perf = (dnparams.dmethod=="RGB"); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // gamma transform for input data float gam = dnparams.gamma; float gamthresh = 0.001f; if(!isRAW) {//reduce gamma under 1 for Lab mode ==> TIF and JPG if(gam <1.9f) gam=1.f - (1.9f-gam)/3.f;//minimum gamma 0.7 else if (gam >= 1.9f && gam <= 3.f) gam=(1.4f/1.1f)*gam - 1.41818f; } float gamslope = exp(log((double)gamthresh)/gam)/gamthresh; LUTf gamcurve(65536,0); if(perf) { for (int i=0; i<65536; i++) { gamcurve[i] = (Color::gamma((double)i/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)) * 32768.0f; } } else { for (int i=0; i<65536; i++) { gamcurve[i] = (Color::gamman((double)i/65535.0,gam)) * 32768.0f; } } // inverse gamma transform for output data float igam = 1.f/gam; float igamthresh = gamthresh*gamslope; float igamslope = 1.f/gamslope; LUTf igamcurve(65536,0); if(perf) { for (int i=0; i<65536; i++) { igamcurve[i] = (Color::gamma((float)i/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); } } else { for (int i=0; i<65536; i++) { igamcurve[i] = (Color::gamman((float)i/32768.0f,igam) * 65535.0f); } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //srand((unsigned)time(0));//test with random data const float gain = pow (2.0f, float(expcomp)); float incr=1.f; float noisevar_Ldetail = SQR((float)(SQR(100.-dnparams.Ldetail) + 50.*(100.-dnparams.Ldetail)) * TS * 0.5f * incr); bool enhance_denoise = dnparams.enhance; // bool median_denoise = dnparams.median; int gamlab = settings->denoiselabgamma;//gamma lab essentialy for Luminance detail if(gamlab > 2) gamlab=2; if(settings->verbose) printf("Denoise Lab=%i\n",gamlab); array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); const int border = MAX(2,TS/16); #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; iTS/2 ? i-TS+1 : i)); float vmask = (i1TS/2 ? j-TS+1 : j)); tilemask_in[i][j] = (vmask * (j1data[n] = 0; 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; Tile_calc (tilesize, overlap, 2, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); //now we have tile dimensions, overlaps //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // According to FFTW-Doc 'it is safe to execute the same plan in parallel by multiple threads', so we now create 4 plans // outside the parallel region and use them inside the parallel region. // calculate max size of numblox_W. int max_numblox_W = ceil(((float)(MIN(imwidth,tilewidth)))/(offset))+2*blkrad; // calculate min size of numblox_W. int min_numblox_W = ceil(((float)((MIN(imwidth,((numtiles_W - 1) * tileWskip) + tilewidth) ) - ((numtiles_W - 1) * tileWskip)))/(offset))+2*blkrad; // these are needed only for creation of the plans and will be freed before entering the parallel loop float * Lbloxtmp; float * fLbloxtmp; Lbloxtmp = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof (float)); fLbloxtmp = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof (float)); int nfwd[2]={TS,TS}; //for DCT: const fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; const fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; fftwf_plan plan_forward_blox[2]; fftwf_plan plan_backward_blox[2]; // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, NULL, 1, TS*TS, fLbloxtmp, NULL, 1, TS*TS, fwdkind, FFTW_MEASURE ); plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, NULL, 1, TS*TS, Lbloxtmp, NULL, 1, TS*TS, bwdkind, FFTW_MEASURE ); plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, NULL, 1, TS*TS, fLbloxtmp, NULL, 1, TS*TS, fwdkind, FFTW_MEASURE ); plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, NULL, 1, TS*TS, Lbloxtmp, NULL, 1, TS*TS, bwdkind, FFTW_MEASURE ); fftwf_free ( Lbloxtmp ); fftwf_free ( fLbloxtmp ); #ifdef _OPENMP // Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles int numtiles = numtiles_W * numtiles_H; int numthreads = MIN(numtiles,omp_get_max_threads()); if(options.rgbDenoiseThreadLimit > 0) numthreads = MIN(numthreads,options.rgbDenoiseThreadLimit); // Issue 1887, overide setting of 1, if more than one thread is available. This way the inner omp-directives should become inactive if(numthreads == 1 && omp_get_max_threads() > 1) numthreads = 2; #pragma omp parallel num_threads(numthreads) #endif { float resid=0.f; float nbresid=0; float maxredresid=0.f; float maxblueresid=0.f; float residred=0.f; float residblue=0.f; //DCT block data storage float * Lblox; float * fLblox; int pos; TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); double wp[3][3] = { {wprof[0][0],wprof[0][1],wprof[0][2]}, {wprof[1][0],wprof[1][1],wprof[1][2]}, {wprof[2][0],wprof[2][1],wprof[2][2]} }; TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); //inverse matrix user select double wip[3][3] = { {wiprof[0][0],wiprof[0][1],wiprof[0][2]}, {wiprof[1][0],wiprof[1][1],wiprof[1][2]}, {wiprof[2][0],wiprof[2][1],wiprof[2][2]} }; // int wcr,hcr; #ifdef _OPENMP #pragma omp critical #endif { Lblox = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof(float)); fLblox = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof(float)); } float * nbrwt = new float[TS*TS]; float * blurbuffer = new float[TS*TS]; #ifdef _OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int tiletop=0; tiletopleveldnautsimpl==1 && params->dirpyrDenoise.Cmethod=="PON") {ponder=true;ponderCC=0.5f;} if(settings->leveldnautsimpl==1 && params->dirpyrDenoise.Cmethod=="PRE") {ponderCC=0.5f;} if(settings->leveldnautsimpl==0 && params->dirpyrDenoise.Cmethod=="PREV") {ponderCC=0.5f;} float realred2, realred, realblue, realblue2; float interm_med =(float) dnparams.chroma/10.0; float intermred, intermblue; if(dnparams.redchro > 0.) intermred=(dnparams.redchro/10.); else intermred= (float) dnparams.redchro/7.0;//increase slower than linear for more sensit if(dnparams.bluechro > 0.) intermblue=(dnparams.bluechro/10.); else intermblue= (float) dnparams.bluechro/7.0;//increase slower than linear for more sensit if(ponder && kall==2){interm_med=ch_M[pos]/10.f; intermred=max_r[pos]/10.f;intermblue=max_b[pos]/10.f;} if(ponder && kall==0){interm_med=0.01f; intermred=0.f;intermblue=0.f;} realred = interm_med + intermred; if (realred < 0.f) realred=0.001f; realblue = interm_med + intermblue; if (realblue < 0.f) realblue=0.001f; // printf("Ch=%f red=%f blu=%f\n",interm_med, intermred,intermblue ); //input L channel array2D Lin(width,height); //wavelet denoised image LabImage * labdn = new LabImage(width,height); float* mad_LL = new float [height*width]; float** noisevarlum; noisevarlum = new float*[height]; for (int i=0; i Ldetail(width,height,ARRAY2D_CLEAR_DATA); //pixel weight array2D totwt(width,height,ARRAY2D_CLEAR_DATA);//weight for combining DCT blocks // init luma noisevarL float noisevarL = (float) (SQR((dnparams.luma/125.0)*(1.+ dnparams.luma/25.0))); // printf("nova=%f\n",noisevarL); //TODO: implement using AlignedBufferMP //fill tile from image; convert RGB to "luma/chroma" if (isRAW) {//image is raw; use channel differences for chroma channels if(!perf){//lab mode //modification Jacques feb 2013 and july 2014 for (int i=tiletop/*, i1=0*/; ir(i,j); float G_ = gain*src->g(i,j); float B_ = gain*src->b(i,j); float Llum,alum,blum; if(noiseLCurve) { Llum=lumcalc[i][j]; } if(noiseCCurve) { alum=acalc[i][j]; blum=bcalc[i][j]; } //modify arbitrary data for Lab..I have test : nothing, gamma 2.6 11 - gamma 4 5 - gamma 5.5 10 //we can put other as gamma g=2.6 slope=11, etc. // but noting to do with real gamma !!!: it's only for data Lab # data RGB //finally I opted fot gamma55 and with options we can change if (gamlab == 0) {// options 12/2013 R_ = Color::igammatab_26_11[R_]; G_ = Color::igammatab_26_11[G_]; B_ = Color::igammatab_26_11[B_]; } else if (gamlab == 1) { //other new gamma 4 5 R_ = Color::igammatab_4[R_]; G_ = Color::igammatab_4[G_]; B_ = Color::igammatab_4[B_]; } else if (gamlab == 2) { //new gamma 5.5 10 better for detail luminance..it is a compromise...which depends on the image (distribution BL, ML, HL ...) R_ = Color::igammatab_55[R_]; G_ = Color::igammatab_55[G_]; B_ = Color::igammatab_55[B_]; } //apply gamma noise standard (slider) R_ = R_<65535.0f ? gamcurve[R_] : (Color::gamman((double)R_/65535.0, gam)*32768.0f); G_ = G_<65535.0f ? gamcurve[G_] : (Color::gamman((double)G_/65535.0, gam)*32768.0f); B_ = B_<65535.0f ? gamcurve[B_] : (Color::gamman((double)B_/65535.0, gam)*32768.0f); //true conversion xyz=>Lab float L,a,b; float X,Y,Z; Color::rgbxyz(R_,G_,B_,X,Y,Z,wp); //convert to Lab Color::XYZ2Lab(X, Y, Z, L, a, b); float noiseluma=(float) dnparams.luma; if(noiseLCurve) { float kN=Llum;//with no gamma and take into account working profile float epsi=0.01f; if(kN<2.f) kN=2.f;//avoid divided by zero if(kN>32768.f) kN=32768.f; // not strictly necessary float kinterm=epsi+ noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; noisevarlum[i1][j1]= SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float kN=Llum;//with no gamma and take into account working profile if(kN<2.f) kN=2.f;//avoid divided by zero if(kN>32768.f) kN=32768.f; // not strictly necessary float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + ponderCC*4.f*noiseCCurve[(cN/30000.f)*500.f];//C=f(C) noisevarchrom[i1][j1]= max(noisevarab_b,noisevarab_r)*SQR(Cinterm); // printf("NC=%f ",noisevarchrom[i1][j1]); } //end chroma labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; Lin[i1][j1] = L; // totwt[i1][j1] = 0; } } } else {//RGB mode for (int i=tiletop/*, i1=0*/; ir(i,j); float Y = gain*src->g(i,j); float Z = gain*src->b(i,j); //conversion colorspace to determine luminance with no gamma float Llum,alum,blum; if(noiseLCurve) { Llum=lumcalc[i][j]; } if(noiseCCurve) { alum=acalc[i][j]; blum=bcalc[i][j]; } X = X<65535.0f ? gamcurve[X] : (Color::gamma((double)X/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Y = Y<65535.0f ? gamcurve[Y] : (Color::gamma((double)Y/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Z = Z<65535.0f ? gamcurve[Z] : (Color::gamma((double)Z/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); float noiseluma=(float) dnparams.luma; if(noiseLCurve) { // float noiseluma=(float) dnparams.luma; float kN=Llum; float epsi=0.01f; if(kN<2.f) kN=2.f; if(kN>32768.f) kN=32768.f; float kinterm=epsi + noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; noisevarlum[i1][j1]= SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + ponderCC*4.f*noiseCCurve[(cN/30000.f)*500.f]; noisevarchrom[i1][j1]=max(noisevarab_b,noisevarab_r)*SQR(Cinterm); } //end chroma labdn->L[i1][j1] = Y; labdn->a[i1][j1] = (X-Y); labdn->b[i1][j1] = (Y-Z); // Ldetail[i1][j1] = 0; Lin[i1][j1] = Y; // totwt[i1][j1] = 0; } } } } else {//image is not raw; use Lab parametrization for (int i=tiletop/*, i1=0*/; ir(i,j) ;//for luminance denoise curve float gLum=src->g(i,j) ; float bLum=src->b(i,j) ; //use gamma sRGB, not good if TIF (JPG) Output profil not with gamma sRGB (eg : gamma =1.0, or 1.8...) //very difficult to solve ! // solution ==> save TIF with gamma sRGB and re open float rtmp = Color::igammatab_srgb[ src->r(i,j) ]; float gtmp = Color::igammatab_srgb[ src->g(i,j) ]; float btmp = Color::igammatab_srgb[ src->b(i,j) ]; //modification Jacques feb 2013 // gamma slider different from raw rtmp = rtmp<65535.0f ? gamcurve[rtmp] : (Color::gamman((double)rtmp/65535.0, gam)*32768.0f); gtmp = gtmp<65535.0f ? gamcurve[gtmp] : (Color::gamman((double)gtmp/65535.0, gam)*32768.0f); btmp = btmp<65535.0f ? gamcurve[btmp] : (Color::gamman((double)btmp/65535.0, gam)*32768.0f); noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); float X,Y,Z; Color::rgbxyz(rtmp,gtmp,btmp,X,Y,Z,wp); //convert Lab Color::XYZ2Lab(X, Y, Z, L, a, b); float Llum,alum,blum; if(noiseLCurve || noiseCCurve) { float XL,YL,ZL; Color::rgbxyz(rLum,gLum,bLum,XL,YL,ZL,wp); Color::XYZ2Lab(XL, YL, ZL, Llum, alum, blum); } float noiseluma=(float) dnparams.luma; if(noiseLCurve) { // float noiseluma=(float) dnparams.luma; float kN=Llum; float epsi=0.01f; if(kN<2.f) kN=2.f; if(kN>32768.f) kN=32768.f; float kinterm=epsi + noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; // noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); noisevarlum[i1][j1]=SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + ponderCC*4.f*noiseCCurve[(cN/30000.f)*500.f]; noisevarchrom[i1][j1]=max(noisevarab_b,noisevarab_r)*SQR(Cinterm); } labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; // Ldetail[i1][j1] = 0; Lin[i1][j1] = L; // totwt[i1][j1] = 0; } } } //initial impulse denoise, removed in Issue 2557 // if (dnparams.luma>0.01) { // impulse_nr (labdn, float(MIN(50.0,dnparams.luma))/20.0f); // } int datalen = labdn->W * labdn->H; //now perform basic wavelet denoise //last two arguments of wavelet decomposition are max number of wavelet decomposition levels; //and whether to subsample the image after wavelet filtering. Subsampling is coded as //binary 1 or 0 for each level, eg subsampling = 0 means no subsampling, 1 means subsample //the first level only, 7 means subsample the first three levels, etc. // float noisevarL = (float) (SQR((dnparams.luma/125.0)*(1.+ dnparams.luma/25.0))); float interm_medT= (float) dnparams.chroma/10.0; bool execwavelet=true; bool autoch=false; if(noisevarL < 0.000007f && interm_medT < 0.05f && dnparams.median && (dnparams.methodmed=="Lab" || dnparams.methodmed=="Lonly")) execwavelet=false;//do not exec wavelet if sliders luminance and chroma are very small and median need //we considered user don't want wavelet if(settings->leveldnautsimpl==1 && dnparams.Cmethod!="MAN") execwavelet=true; if(settings->leveldnautsimpl==0 && dnparams.C2method!="MANU") execwavelet=true; if(settings->leveldnautsimpl==1 && (dnparams.Cmethod=="AUT" || dnparams.Cmethod=="PRE")) autoch=true; if(settings->leveldnautsimpl==0 && dnparams.C2method=="AUTO" || dnparams.C2method=="PREV") autoch=true; if(execwavelet) {//gain time if user choose only median sliders L <=1 slider chrom master < 1 { // enclosing this code in a block frees about 120 MB before allocating 20 MB after this block (measured with D700 NEF) wavelet_decomposition* Ldecomp; wavelet_decomposition* adecomp; wavelet_decomposition* bdecomp; int schoice=0;//shrink method if(dnparams.smethod=="shal") schoice=0; else if(dnparams.smethod=="shalbi") schoice=2; int levwav=5; float maxreal = max(realred, realblue); //increase the level of wavelet if user increase much or very much sliders if( maxreal < 8.f) levwav=5; else if( maxreal < 10.f)levwav=6; else if( maxreal < 15.f)levwav=7; else levwav=8;//maximum ==> I have increase Maxlevel in cplx_wavelet_dec.h from 8 to 9 if(schoice==2) levwav+=settings->nrwavlevel;//increase level for enhanced mode if(levwav>8) levwav=8; // if (settings->verbose) printf("levwavelet=%i noisevarA=%f noisevarB=%f \n",levwav, noisevarab_r, noisevarab_b ); Ldecomp = new wavelet_decomposition (labdn->data, labdn->W, labdn->H, levwav/*maxlevels*/, 0/*subsampling*/ ); adecomp = new wavelet_decomposition (labdn->data+datalen, labdn->W, labdn->H,levwav, 1 ); bdecomp = new wavelet_decomposition (labdn->data+2*datalen, labdn->W, labdn->H, levwav, 1 ); if(schoice==0) WaveletDenoiseAll(*Ldecomp, *adecomp, *bdecomp, noisevarL, noisevarlum, noisevarchrom, width, height, mad_LL, mad_aa, mad_bb, noisevarab_r, noisevarab_b,labdn, noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, schoice, autoch);//enhance mode if(schoice==2) { WaveletDenoiseAll_BiShrink(*Ldecomp, *adecomp, *bdecomp, noisevarL, noisevarlum, noisevarchrom, width, height, mad_LL, mad_aa, mad_bb, noisevarab_r, noisevarab_b,labdn, noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, schoice, autoch);//enhance mode WaveletDenoiseAll(*Ldecomp, *adecomp, *bdecomp, noisevarL, noisevarlum, noisevarchrom, width, height, mad_LL, mad_aa, mad_bb, noisevarab_r, noisevarab_b,labdn, noiseLCurve, noiseCCurve, chaut ,redaut, blueaut, maxredaut, maxblueaut, schoice, autoch); } float chresid,chmaxredresid,chmaxblueresid,chresidred, chresidblue; //kall=0 call by Dcrop resid=0.f;residred=0.f;residblue=0.f; if(kall==0) Noise_residual(*Ldecomp, *adecomp, *bdecomp, width, height, chresid, chmaxredresid,chmaxblueresid, chresidred, chresidblue, resid, residblue, residred, maxredresid, maxblueresid, nbresid); //printf("NoiRESID=%3.1f maxR=%3.1f maxB=%3.1f red=%3.1f blue=%3.1f\n",chresid, chmaxredresid,chmaxblueresid, chresidred, chresidblue); nresi=chresid; highresi=chresid + 0.66f*(max(chmaxredresid,chmaxblueresid) - chresid);//evaluate sigma Ldecomp->reconstruct(labdn->data); delete Ldecomp; adecomp->reconstruct(labdn->data+datalen); delete adecomp; bdecomp->reconstruct(labdn->data+2*datalen); delete bdecomp; } } //TODO: at this point wavelet coefficients storage can be freed //Issue 1680: Done now //second impulse denoise, removed in Issue 2557 // if (dnparams.luma>0.01) { // impulse_nr (labdn, MIN(50.0f,(float)dnparams.luma)/20.0f); // } //PF_correct_RT(dst, dst, defringe.radius, defringe.threshold); int metchoice=0; if(dnparams.methodmed=="Lonly") metchoice=1; else if(dnparams.methodmed=="Lab") metchoice=2; else if(dnparams.methodmed=="ab") metchoice=3; else if(dnparams.methodmed=="Lpab") metchoice=4; //median on Luminance Lab only if( (metchoice==1 || metchoice==2 || metchoice==3 || metchoice==4) && dnparams.median) { //printf("Lab et Lonly \n"); for(int iteration=1;iteration<=dnparams.passes;iteration++){ //printf("pas=%i\n",iteration); int wid=labdn->W; int hei=labdn->H; float** tmL; tmL = new float*[hei]; for (int i=0; iL[i][j] ,labdn->L[i-1][j], labdn->L[i+1][j] ,labdn->L[i][j+1],labdn->L[i][j-1], tmL[i][j]);//3x3 soft } else for (int j=1; j impluse denoise !I let here to get trace /* pp[0] = labdn->L[i][j-1] ; pp[1] = labdn->L[i-1][j] ; pp[2] = labdn->L[i][j] ; pp[3] = labdn->L[i+1][j] ; pp[4] = labdn->L[i][j+1] ; // Get median results[0] = media(pp, 5); // Pick up x-window elements pp[0] = labdn->L[i-1][j-1] ; pp[1] = labdn->L[i+1][j-1] ; pp[2] = labdn->L[i][j] ; pp[3] = labdn->L[i-1][j+1] ; pp[4] = labdn->L[i+1][j+1] ; // Get median results[1] = media(pp, 5); // Pick up leading element results[2] = labdn->L[i][j] ;; // Get result tmL[i][j] = media(results, 3); */ med3(labdn->L[i][j] ,labdn->L[i-1][j], labdn->L[i+1][j] ,labdn->L[i][j+1],labdn->L[i][j-1], labdn->L[i-1][j-1],labdn->L[i-1][j+1],labdn->L[i+1][j-1],labdn->L[i+1][j+1],tmL[i][j]);//3x3 soft } } } else { for (int i=borderL; iL[i+ii][j+jj]; } } fq_sort2(pp,49); tmL[i][j]=pp[24];//7x7 } else if(methmedL == 5) for (int j=borderL; jL[i+ii][j+jj]; } } fq_sort2(pp,81); tmL[i][j]=pp[40];//9x9 } else if(methmedL == 3) for (int j=2; jL[i][j],labdn->L[i-1][j],labdn->L[i+1][j],labdn->L[i][j+1],labdn->L[i][j-1],labdn->L[i-1][j-1],labdn->L[i-1][j+1], labdn->L[i+1][j-1],labdn->L[i+1][j+1], labdn->L[i-2][j],labdn->L[i+2][j],labdn->L[i][j+2],labdn->L[i][j-2],labdn->L[i-2][j-2],labdn->L[i-2][j+2],labdn->L[i+2][j-2],labdn->L[i+2][j+2], labdn->L[i-2][j+1],labdn->L[i+2][j+1],labdn->L[i-1][j+2],labdn->L[i-1][j-2],labdn->L[i-2][j-1],labdn->L[i+2][j-1],labdn->L[i+1][j+2],labdn->L[i+1][j-2], tmL[i][j]);//5x5 } else for (int j=2; jL[i][j];pp[1]=labdn->L[i-1][j]; pp[2]=labdn->L[i+1][j];pp[3]=labdn->L[i][j+1];pp[4]=labdn->L[i][j-1];pp[5]=labdn->L[i-1][j-1];pp[6]=labdn->L[i-1][j+1]; pp[7]=labdn->L[i+1][j-1];pp[8]=labdn->L[i+1][j+1];pp[9]=labdn->L[i+2][j];pp[10]=labdn->L[i-2][j];pp[11]=labdn->L[i][j+2];pp[12]=labdn->L[i][j-2]; fq_sort2(pp,13); tmL[i][j]=pp[6];//5x5 soft } } } for(int i = borderL; i < hei-borderL; i++ ) { for(int j = borderL; j < wid-borderL; j++) { labdn->L[i][j] = tmL[i][j]; } } } if(metchoice==2 || metchoice==3 || metchoice==4) {/*printf(" AB methmedL=%d\n", methmedL);*/ if(methmedAB < 2) { for (int i=1; ia[i][j] ,labdn->a[i-1][j], labdn->a[i+1][j] ,labdn->a[i][j+1],labdn->a[i][j-1], tmL[i][j]);//3x3 soft } else for (int j=1; ja[i][j] ,labdn->a[i-1][j], labdn->a[i+1][j] ,labdn->a[i][j+1],labdn->a[i][j-1], labdn->a[i-1][j-1],labdn->a[i-1][j+1],labdn->a[i+1][j-1],labdn->a[i+1][j+1],tmL[i][j]);//3x3 soft } } } else { for (int i=borderL; ia[i+ii][j+jj]; } } fq_sort2(pp,49); tmL[i][j]=pp[24];//7x7 } else if(methmedAB == 5) for (int j=borderL; ja[i+ii][j+jj]; } } fq_sort2(pp,81); tmL[i][j]=pp[40];//9 } else if(methmedAB == 3) for (int j=2; ja[i][j],labdn->a[i-1][j],labdn->a[i+1][j],labdn->a[i][j+1],labdn->a[i][j-1],labdn->a[i-1][j-1],labdn->a[i-1][j+1], labdn->a[i+1][j-1],labdn->a[i+1][j+1], labdn->a[i-2][j],labdn->a[i+2][j],labdn->a[i][j+2],labdn->a[i][j-2],labdn->a[i-2][j-2],labdn->a[i-2][j+2],labdn->a[i+2][j-2],labdn->a[i+2][j+2], labdn->a[i-2][j+1],labdn->a[i+2][j+1],labdn->a[i-1][j+2],labdn->a[i-1][j-2],labdn->a[i-2][j-1],labdn->a[i+2][j-1],labdn->a[i+1][j+2],labdn->a[i+1][j-2], tmL[i][j]);//5x5 } else for (int j=2; ja[i][j];pp[1]=labdn->a[i-1][j]; pp[2]=labdn->a[i+1][j];pp[3]=labdn->a[i][j+1];pp[4]=labdn->a[i][j-1];pp[5]=labdn->a[i-1][j-1];pp[6]=labdn->a[i-1][j+1]; pp[7]=labdn->a[i+1][j-1];pp[8]=labdn->a[i+1][j+1];pp[9]=labdn->a[i+2][j];pp[10]=labdn->a[i-2][j];pp[11]=labdn->a[i][j+2];pp[12]=labdn->a[i][j-2]; fq_sort2(pp,13); tmL[i][j]=pp[6];//5x5 soft } /* for (int j=3; ja[i+ii][j+jj]; } pp[kk+1]=labdn->a[i-3][j-3];pp[kk+2]=labdn->a[i-3][j+3];pp[kk+3]=labdn->a[i+3][j-3];pp[kk+4]=labdn->a[i+3][j+3]; pp[kk+5]=labdn->a[i-3][j];pp[kk+6]=labdn->a[i+3][j];pp[kk+7]=labdn->a[i][j-3];pp[kk+8]=labdn->a[i][j+3]; } fq_sort2(pp,33); tmL[i][j]=pp[16];//7x7 } */ } } for(int i = borderL; i < hei-borderL; i++ ) { for(int j = borderL; j < wid-borderL; j++) { labdn->a[i][j] = tmL[i][j]; } } //b if(methmedAB < 2) { for (int i=1; ib[i][j] ,labdn->b[i-1][j], labdn->b[i+1][j] ,labdn->b[i][j+1],labdn->b[i][j-1], tmL[i][j]);//3x3 soft } else for (int j=1; jb[i][j] ,labdn->b[i-1][j], labdn->b[i+1][j] ,labdn->b[i][j+1],labdn->b[i][j-1], labdn->b[i-1][j-1],labdn->b[i-1][j+1],labdn->b[i+1][j-1],labdn->b[i+1][j+1],tmL[i][j]);//3x3 soft } } } else { for (int i=borderL; ib[i+ii][j+jj]; } } fq_sort2(pp,49); tmL[i][j]=pp[24];//7x7 } else if(methmedAB == 5) for (int j=borderL; jb[i+ii][j+jj]; } } fq_sort2(pp,81); tmL[i][j]=pp[40];//9 } else if(methmedAB == 3) for (int j=2; jb[i][j],labdn->b[i-1][j],labdn->b[i+1][j],labdn->b[i][j+1],labdn->b[i][j-1],labdn->b[i-1][j-1],labdn->b[i-1][j+1], labdn->b[i+1][j-1],labdn->b[i+1][j+1], labdn->b[i-2][j],labdn->b[i+2][j],labdn->b[i][j+2],labdn->b[i][j-2],labdn->b[i-2][j-2],labdn->b[i-2][j+2],labdn->b[i+2][j-2],labdn->b[i+2][j+2], labdn->b[i-2][j+1],labdn->b[i+2][j+1],labdn->b[i-1][j+2],labdn->b[i-1][j-2],labdn->b[i-2][j-1],labdn->b[i+2][j-1],labdn->b[i+1][j+2],labdn->b[i+1][j-2], tmL[i][j]);//5x5 } else for (int j=2; jb[i+ii][j+jj]; } pp[kk+1]=labdn->b[i-3][j-3];pp[kk+2]=labdn->b[i-3][j+3];pp[kk+3]=labdn->b[i+3][j-3];pp[kk+4]=labdn->b[i+3][j+3]; pp[kk+5]=labdn->b[i-3][j];pp[kk+6]=labdn->b[i+3][j];pp[kk+7]=labdn->b[i][j-3];pp[kk+8]=labdn->b[i][j+3]; } fq_sort2(pp,33); tmL[i][j]=pp[16];//7x7 */ pp[0]=labdn->b[i][j];pp[1]=labdn->b[i-1][j]; pp[2]=labdn->b[i+1][j];pp[3]=labdn->b[i][j+1];pp[4]=labdn->b[i][j-1];pp[5]=labdn->b[i-1][j-1];pp[6]=labdn->b[i-1][j+1]; pp[7]=labdn->b[i+1][j-1];pp[8]=labdn->b[i+1][j+1];pp[9]=labdn->b[i+2][j];pp[10]=labdn->b[i-2][j];pp[11]=labdn->b[i][j+2];pp[12]=labdn->b[i][j-2]; fq_sort2(pp,13); tmL[i][j]=pp[6];//5x5 soft } } } for(int i = borderL; i < hei-borderL; i++ ) { for(int j = borderL; j < wid-borderL; j++) { labdn->b[i][j] = tmL[i][j]; } } } for (int i=0; i Lwavdn(width,height); float * Lwavdnptr = Lwavdn; memcpy (Lwavdnptr, labdn->data, width*height*sizeof(float)); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // now do detail recovery using block DCT to detect // patterns missed by wavelet denoise // blocks are not the same thing as tiles! // calculation for detail recovery blocks const int numblox_W = ceil(((float)(width))/(offset))+2*blkrad; const int numblox_H = ceil(((float)(height))/(offset))+2*blkrad; //const int nrtiles = numblox_W*numblox_H; // end of tiling calc { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Main detail recovery algorithm: Block loop AlignedBuffer pBuf(width + TS + 2*blkrad*offset); for (int vblk=0; vblk=height) { rr = MAX(0,2*height-2-row); } for (int j=0; jW; j++) { datarow[j] = (Lin[rr][j]-Lwavdn[rr][j]); } for (int j=-blkrad*offset; j<0; j++) { datarow[j] = datarow[MIN(-j,width-1)]; } for (int j=width; j=0 && top+i=0 && left+jL[i][j] = Lwavdn[i][j] + hpdn; } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform denoised "Lab" to output RGB //calculate mask for feathering output tile overlaps float * Vmask = new float [height+1]; float * Hmask = new float [width+1]; for (int i=0; i0) Vmask[i] = mask; if (tilebottom0) Hmask[i] = mask; if (tilerightxyz L = labdn->L[i1][j1]; a = labdn->a[i1][j1]; b = labdn->b[i1][j1]; float c_h=sqrt(SQR(a)+SQR(b)); if(c_h>3000.f){ a*=1.f + Qhigh*realred/100.f; b*=1.f + Qhigh*realblue/100.f; } //convert XYZ Color::Lab2XYZ(L, a, b, X, Y, Z); //apply inverse gamma noise float r_,g_,b_; Color::xyz2rgb(X,Y,Z,r_,g_,b_,wip); //inverse gamma standard (slider) r_ = r_<32768.0f ? igamcurve[r_] : (Color::gamman((float)r_/32768.0f, igam) * 65535.0f); g_ = g_<32768.0f ? igamcurve[g_] : (Color::gamman((float)g_/32768.0f, igam) * 65535.0f); b_ = b_<32768.0f ? igamcurve[b_] : (Color::gamman((float)b_/32768.0f, igam) * 65535.0f); //readapt arbitrary gamma (inverse from beginning) if (gamlab == 0) { r_ = Color::gammatab_26_11[r_]; g_ = Color::gammatab_26_11[g_]; b_ = Color::gammatab_26_11[b_]; } else if (gamlab == 1) { r_ = Color::gammatab_4[r_]; g_ = Color::gammatab_4[g_]; b_ = Color::gammatab_4[b_]; } else if (gamlab == 2) { r_ = Color::gammatab_55[r_]; g_ = Color::gammatab_55[g_]; b_ = Color::gammatab_55[b_]; } float factor = Vmask[i1]*Hmask[j1]/gain; dsttmp->r(i,j) += factor*r_; dsttmp->g(i,j) += factor*g_; dsttmp->b(i,j) += factor*b_; } } } else {//RGB mode for (int i=tiletop; ia[i1][j1])+SQR(labdn->b[i1][j1])); if(c_h>3000.f){ labdn->a[i1][j1]*=1.f + Qhigh*realred/100.f; labdn->b[i1][j1]*=1.f + Qhigh*realblue/100.f; } Y = labdn->L[i1][j1]; X = (labdn->a[i1][j1]) + Y; Z = Y - (labdn->b[i1][j1]); X = X<32768.0f ? igamcurve[X] : (Color::gamma((float)X/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); Y = Y<32768.0f ? igamcurve[Y] : (Color::gamma((float)Y/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); Z = Z<32768.0f ? igamcurve[Z] : (Color::gamma((float)Z/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); float factor = Vmask[i1]*Hmask[j1]/gain; dsttmp->r(i,j) += factor*X; dsttmp->g(i,j) += factor*Y; dsttmp->b(i,j) += factor*Z; } } } } else { for (int i=tiletop; iL[i1][j1]; a = labdn->a[i1][j1]; b = labdn->b[i1][j1]; float c_h=sqrt(SQR(a)+SQR(b)); if(c_h>3000.f){ a*=1.f + Qhigh*realred/100.f; b*=1.f + Qhigh*realblue/100.f; } Color::Lab2XYZ(L, a, b, X, Y, Z); float factor = Vmask[i1]*Hmask[j1]; float r_,g_,b_; Color::xyz2rgb(X,Y,Z,r_,g_,b_,wip); //gamma slider is different from Raw r_ = r_<32768.0f ? igamcurve[r_] : (Color::gamman((float)r_/32768.0f, igam) * 65535.0f); g_ = g_<32768.0f ? igamcurve[g_] : (Color::gamman((float)g_/32768.0f, igam) * 65535.0f); b_ = b_<32768.0f ? igamcurve[b_] : (Color::gamman((float)b_/32768.0f, igam) * 65535.0f); dsttmp->r(i,j) += factor*r_; dsttmp->g(i,j) += factor*g_; dsttmp->b(i,j) += factor*b_; } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% delete labdn; // delete noiseh; for (int i=0; idata, dsttmp->data, 3*dst->width*dst->height*sizeof(float)); if (!isRAW) {//restore original image gamma #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; i<3*dst->width*dst->height; i++) { dst->data[i] = Color::gammatab_srgb[ dst->data[i] ]; } } delete dsttmp; // destroy the plans fftwf_destroy_plan( plan_forward_blox[0] ); fftwf_destroy_plan( plan_backward_blox[0] ); fftwf_destroy_plan( plan_forward_blox[1] ); fftwf_destroy_plan( plan_backward_blox[1] ); fftwf_cleanup(); } //median 3x3 in complement on RGB if(dnparams.methodmed=="RGB" && dnparams.median) { //printf("RGB den\n"); int wid=dst->width, hei=dst->height; float** tm; tm = new float*[hei]; for (int i=0; ir(i,j),source->r(i-1,j),source->r(i+1,j),source->r(i,j+1),source->r(i,j-1),tm[i][j]);//3x3 soft } else for (int j=1; jr(i,j),source->r(i-1,j),source->r(i+1,j),source->r(i,j+1),source->r(i,j-1),source->r(i-1,j-1),source->r(i-1,j+1),source->r(i+1,j-1),source->r(i+1,j+1),tm[i][j]);//3x3 } } } else { #pragma omp for for (int i=2; ir(i,j),source->r(i-1,j),source->r(i+1,j),source->r(i,j+1),source->r(i,j-1),source->r(i-1,j-1),source->r(i-1,j+1),source->r(i+1,j-1),source->r(i+1,j+1), source->r(i-2,j),source->r(i+2,j),source->r(i,j+2),source->r(i,j-2),source->r(i-2,j-2),source->r(i-2,j+2),source->r(i+2,j-2),source->r(i+2,j+2), source->r(i-2,j+1),source->r(i+2,j+1),source->r(i-1,j+2),source->r(i-1,j-2),source->r(i-2,j-1),source->r(i+2,j-1),source->r(i+1,j+2),source->r(i+1,j-2), tm[i][j]);//5x5 } else for (int j=2; jr(i,j);pp[1]=source->r(i-1,j); pp[2]=source->r(i+1,j);pp[3]=source->r(i,j+1);pp[4]=source->r(i,j-1);pp[5]=source->r(i-1,j-1);pp[6]=source->r(i-1,j+1); pp[7]=source->r(i+1,j-1);pp[8]=source->r(i+1,j+1);pp[9]=source->r(i+2,j);pp[10]=source->r(i-2,j);pp[11]=source->r(i,j+2);pp[12]=source->r(i,j-2); fq_sort2(pp,13); tm[i][j]=pp[6];//5x5 soft } } } #ifdef _OPENMP #pragma omp for nowait #endif for(int i = border; i < hei-border; i++ ) { for(int j = border; j < wid-border; j++) { dst->r(i,j) = tm[i][j]; } } if(methmed < 2) { #pragma omp for for (int i=1; ib(i,j),source->b(i-1,j),source->b(i+1,j),source->b(i,j+1),source->b(i,j-1),tm[i][j]); } else for (int j=1; jb(i,j),source->b(i-1,j),source->b(i+1,j),source->b(i,j+1),source->b(i,j-1),source->b(i-1,j-1),source->b(i-1,j+1),source->b(i+1,j-1),source->b(i+1,j+1),tm[i][j]); } } } else { #pragma omp for for (int i=2; ib(i,j),source->b(i-1,j),source->b(i+1,j),source->b(i,j+1),source->b(i,j-1),source->b(i-1,j-1),source->b(i-1,j+1),source->b(i+1,j-1),source->b(i+1,j+1), source->b(i-2,j),source->b(i+2,j),source->b(i,j+2),source->b(i,j-2),source->b(i-2,j-2),source->b(i-2,j+2),source->b(i+2,j-2),source->b(i+2,j+2), source->b(i-2,j+1),source->b(i+2,j+1),source->b(i-1,j+2),source->b(i-1,j-2),source->b(i-2,j-1),source->b(i+2,j-1),source->b(i+1,j+2),source->b(i+1,j-2), tm[i][j]);//5x5 } else for (int j=2; jb(i,j);pp[1]=source->b(i-1,j); pp[2]=source->b(i+1,j);pp[3]=source->b(i,j+1);pp[4]=source->b(i,j-1);pp[5]=source->b(i-1,j-1);pp[6]=source->b(i-1,j+1); pp[7]=source->b(i+1,j-1);pp[8]=source->b(i+1,j+1);pp[9]=source->b(i+2,j);pp[10]=source->b(i-2,j);pp[11]=source->b(i,j+2);pp[12]=source->b(i,j-2); fq_sort2(pp,13); tm[i][j]=pp[6];//5x5 soft } } } #ifdef _OPENMP #pragma omp for nowait #endif for(int i = border; i < hei-border; i++ ) { for(int j = border; j < wid-border; j++) { dst->b(i,j) = tm[i][j]; } } if(methmed < 2) { #pragma omp for for (int i=1; ig(i,j),source->g(i-1,j),source->g(i+1,j),source->g(i,j+1),source->g(i,j-1),tm[i][j]); } else for (int j=1; jg(i,j),source->g(i-1,j),source->g(i+1,j),source->g(i,j+1),source->g(i,j-1),source->g(i-1,j-1),source->g(i-1,j+1),source->g(i+1,j-1),source->g(i+1,j+1),tm[i][j]); } } } else { #pragma omp for for (int i=2; ig(i,j),source->g(i-1,j),source->g(i+1,j),source->g(i,j+1),source->g(i,j-1),source->g(i-1,j-1),source->g(i-1,j+1),source->g(i+1,j-1),source->g(i+1,j+1), source->g(i-2,j),source->g(i+2,j),source->g(i,j+2),source->g(i,j-2),source->g(i-2,j-2),source->g(i-2,j+2),source->g(i+2,j-2),source->g(i+2,j+2), source->g(i-2,j+1),source->g(i+2,j+1),source->g(i-1,j+2),source->g(i-1,j-2),source->g(i-2,j-1),source->g(i+2,j-1),source->g(i+1,j+2),source->g(i+1,j-2), tm[i][j]);//5x5 } else for (int j=2; jg(i,j);pp[1]=source->g(i-1,j); pp[2]=source->g(i+1,j);pp[3]=source->g(i,j+1);pp[4]=source->g(i,j-1);pp[5]=source->g(i-1,j-1);pp[6]=source->g(i-1,j+1); pp[7]=source->g(i+1,j-1);pp[8]=source->g(i+1,j+1);pp[9]=source->g(i+2,j);pp[10]=source->g(i-2,j);pp[11]=source->g(i,j+2);pp[12]=source->g(i,j-2); fq_sort2(pp,13); tm[i][j]=pp[6];//5x5 soft } } } #ifdef _OPENMP #pragma omp for #endif for(int i = border; i < hei-border; i++ ) { for(int j = border; j < wid-border; j++) { dst->g(i,j) = tm[i][j]; } } } } for (int i=0; iverbose) { t2e.set(); printf("Denoise performed in %d usec:\n", t2e.etime(t1e)); } //#endif }//end of main RGB_denoise //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SSEFUNCTION void ImProcFunctions::RGBtile_denoise (float * fLblox, int hblproc, float noisevar_Ldetail, float * nbrwt, float * blurbuffer ) //for DCT { int blkstart = hblproc*TS*TS; boxabsblur(fLblox+blkstart, nbrwt, 3, 3, TS, TS, blurbuffer);//blur neighbor weights for more robust estimation //for DCT #ifdef __SSE2__ __m128 tempv; __m128 noisevar_Ldetailv = _mm_set1_ps( noisevar_Ldetail ); __m128 onev = _mm_set1_ps( 1.0f ); for (int n=0; nmaxredresid ) maxredresid=madaC; if(madbC > maxblueresid) maxblueresid=madbC; nbresid++; chresid=sqrt(resid/(2*nbresid)); chmaxredresid=sqrt(maxredresid); chmaxblueresid=sqrt(maxblueresid); chresidred=sqrt(residred/nbresid); chresidblue=sqrt(residblue/nbresid); } } delete [] madHisto; } SSEFUNCTION void ImProcFunctions::WaveletDenoiseAll_BiShrink(wavelet_decomposition &WaveletCoeffs_L, wavelet_decomposition &WaveletCoeffs_a, wavelet_decomposition &WaveletCoeffs_b, float noisevar_L, float **noisevarlum, float **noisevarchrom, int width, int height, float *mad_LL, float *mad_aa, float *mad_bb, float noisevar_abr, float noisevar_abb, LabImage * noi, const NoiseCurve & noiseLCurve, const NoiseCurve & noiseCCurve, float &chaut, float &redaut, float &blueaut, float &maxredaut, float &maxblueaut, int schoice, bool autoch) { int maxlvl = WaveletCoeffs_L.maxlevel(); const float eps = 0.01f; // int max; // float parfrac = 0.05; float madL[8][3], mada[8][3], madb[8][3]; float madaC,madbC; int * madHisto = new int[65536]; for (int lvl=0; lvl=0; lvl--) {//for levels less than max, use level diff to make edge mask //for (int lvl=0; lvl edge(Wlvl_L,Hlvl_L); //printf("\n level=%d \n",lvl); if(autoch && noisevar_abr <=0.001f) noisevar_abr=0.02f; if(autoch && noisevar_abb <=0.001f) noisevar_abb=0.02f; for (int dir=1; dir<4; dir++) { float mad_Lr = madL[lvl][dir-1]; float mad_ar = noisevar_abr*mada[lvl][dir-1]; float mad_br = noisevar_abb*madb[lvl][dir-1]; if (!noiseCCurve || noiseCCurve.getSum() < 5.f ){ for (int i=0; i 5.f ){ // printf("OUI\n"); for (int i=0; i0.001f || noisevar_abb>0.001f) { for(int i=0;i2 ? 1 : (coeff_a<1 ? 0 : (coeff_a - 1))); //WavCoeffs_b[dir][coeffloc_ab] *= edgefactor*(coeff_b>2 ? 1 : (coeff_b<1 ? 0 : (coeff_b - 1))); //float satfactor_a = mad_a/(mad_a+0.5*SQR(WavCoeffs_a[0][coeffloc_ab])); //float satfactor_b = mad_b/(mad_b+0.5*SQR(WavCoeffs_b[0][coeffloc_ab])); WavCoeffs_a[dir][coeffloc_ab] *= SQR(1.f-xexpf(-(mag_a/mad_aa[coeffloc_ab])-(mag_L/(9.f*mad_Lr)))/*satfactor_a*/); WavCoeffs_b[dir][coeffloc_ab] *= SQR(1.f-xexpf(-(mag_b/mad_bb[coeffloc_ab])-(mag_L/(9.f*mad_Lr)))/*satfactor_b*/); } }//now chrominance coefficients are denoised #endif } if (noisevar_L>0.00001f) { if (!noiseLCurve || noiseLCurve.getSum() < 7.f ) { for (int i=0; i= 7.f) { for (int i=0; i (edge, edge, buffer, Wlvl_L, Hlvl_L, 1<<(lvl+1), false); //gaussVertical (edge, edge, buffer, Wlvl_L, Hlvl_L, 1<<(lvl+1), false); boxblur(sfave, sfave, lvl+2, lvl+2, Wlvl_L, Hlvl_L);//increase smoothness by locally averaging shrinkage #ifdef __SSE2__ __m128 tempLv; __m128 tempL2v; __m128 sf_Lv; for (int i=0; i 582=max // float mad_b = madb*noisevar_abb; // printf("noisevarabr=%f\n",noisevar_abr); if(autoch && noisevar_abr <=0.001f) noisevar_abr=0.02f; if(autoch && noisevar_abb <=0.001f) noisevar_abb=0.02f; if (!noiseCCurve || noiseCCurve.getSum() < 5.f ){ // printf("Chroma NON\n"); for (int i=0; i 5.f ){ // printf("chroma OUI\n"); for (int i=0; i0.001f || noisevar_abb>0.001f ) { for(int i=0;i2*thresh_a ? 1 : (coeff_a2*thresh_b ? 1 : (coeff_bverbose) printf("noisevar=%f dnzero=%f \n",noisevar_L, noiseLCurve.sum); if (noisevar_L>0.00001f) { if (!noiseLCurve || noiseLCurve.getSum() < 7.f ){//under 7 quasi no action // printf("Luma sans\n"); for (int i=0; i= 7.f) { // printf("Luma avec\n"); for (int i=0; i maxchro) maxchro= noisevarchrom[i][j]; chro+=noisevarchrom[i][j];nc++; } } for (int i=0; i -0.8f && noisevarhue[i][j] < 2.0f && noisevarchrom[i][j] > 10000.f)//saturated red yellow {red_yel+=noisevarchrom[i][j];nry++;} if(noisevarhue[i][j] > 0.f && noisevarhue[i][j] < 1.6f && noisevarchrom[i][j] < 10000.f)//skin {skin_c+=noisevarchrom[i][j];nsk++;} } } for (int i=0; i0) { chromina=chro/nc; sigma=sqrt(dev/nc); nsknc=(float)nsk/(float)nc; } else { nsknc=(float)nsk; } if(nL>0) { lumema=lume/nL; sigma_L=sqrt(devL/nL); } if(nry>0) redyel=red_yel/nry; if(nsk>0) skinc=skin_c/nsk; // printf("redy=%f ski=%f nsk=%d nc=%d pcsk=%f\n",red_yel/nry,skinc,nsk, nc, (float)nsk/(float)nc ); // chroG=; // chroB=; } for (int dir=1; dir<4; dir++) { float mada, madb, madL; if(madCalculated) { mada = madaa[dir-1]; madb = madab[dir-1]; madL = madaL[dir-1] ; } else { int * madHisto = new int[65536]; mada = SQR(Mad(WavCoeffs_a[dir], W_ab*H_ab, madHisto)); madb = SQR(Mad(WavCoeffs_b[dir], W_ab*H_ab, madHisto)); madL = SQR(Mad(WavCoeffs_L[dir], W_L*H_L, madHisto)); delete [] madHisto; // madCalculated=true; } if(callby==0){ chau+=(mada+madb); chred+=mada; chblue+=madb; if(mada > maxchred) maxchred=mada; if(madb > maxchblue) maxchblue=madb; if(mada < minchred) minchred=mada; if(madb < minchblue) minchblue=madb; nb++; //here evaluation of automatic // printf("WAL dir=%d mada=%4.0f madb=%4.0f skip_ab=%i minR=%4.0f minB=%4.0f nb=%d\n",dir, sqrt(mada),sqrt(madb), skip_ab, sqrt(minchred), sqrt(minchblue), nb); mada=madb=0.f; float reduc=1.f; // if(schoice==2) reduc=0.6f; if(schoice==2) reduc=(float) settings->nrhigh; chaut=sqrt(reduc*chau/(nb + nb)); redaut=sqrt(reduc*chred/nb); blueaut=sqrt(reduc*chblue/nb); maxredaut=sqrt(reduc*maxchred); maxblueaut=sqrt(reduc*maxchblue); minredaut=sqrt(reduc*minchred); minblueaut=sqrt(reduc*minchblue); Nb=nb; } } // } delete[] sfave; delete[] sfavea; delete[] sfaveb; delete[] WavCoeffsLtemp; // delete[] mad_L; } void ImProcFunctions::WaveletDenoiseAll_info(wavelet_decomposition &WaveletCoeffs_L, wavelet_decomposition &WaveletCoeffs_a, wavelet_decomposition &WaveletCoeffs_b, float noisevar_L, float **noisevarlum, float **noisevarchrom, float **noisevarhue, int width, int height, float *mad_LL, float *mad_aa, float *mad_bb, float noisevar_abr, float noisevar_abb, LabImage * noi, const NoiseCurve & noiseLCurve, const NoiseCurve & noiseCCurve, float &chaut, int &Nb, float &redaut, float &blueaut, float &maxredaut, float &maxblueaut, float &minredaut, float &minblueaut,int schoice, bool autoch, float &chromina, float &sigma, float &lumema, float &sigma_L, float &redyel, float &skinc, float &nsknc, float &maxchred, float &maxchblue, float &minchred, float &minchblue, int &nb, float &chau, float &chred, float &chblue ){ int maxlvl = WaveletCoeffs_L.maxlevel(); // printf("maxlevel = %d\n",maxlvl); //omp_set_nested(true); for (int lvl=0; lvl TIF and JPG if(gam <1.9f) gam=1.f - (1.9f-gam)/3.f;//minimum gamma 0.7 else if (gam >= 1.9f && gam <= 3.f) gam=(1.4f/1.1f)*gam - 1.41818f; } gamslope = exp(log((double)gamthresh)/gam)/gamthresh; bool perf = (dnparams.dmethod=="RGB"); if(perf) { for (int i=0; i<65536; i++) { gamcurve[i] = (Color::gamma((double)i/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)) * 32768.0f; } } else { for (int i=0; i<65536; i++) { gamcurve[i] = (Color::gamman((double)i/65535.0,gam)) * 32768.0f; } } } void ImProcFunctions::calcautodn_info (float &chaut, float &delta, int Nb, int levaut, float maxmax, float lumema, float chromina, int mode, int lissage, float redyel, float skinc, float nsknc) {//lissage = 1 more action float reducdelta=1.f; if(params->dirpyrDenoise.smethod=="shalbi") reducdelta=(float) settings->nrhigh; chaut = (chaut*Nb-maxmax)/(Nb-1);//suppress maximum for chaut calcul if(redyel>5000.f || skinc > 1000.f && nsknc < 0.4f && chromina > 3000.f ) chaut *=0.45f;//reduct action in red zone, except skin for high / med chroma else if(redyel>12000.f || skinc > 1200.f && nsknc < 0.3f && chromina > 3000.f ) chaut *=0.3f; if(mode==0 || mode==2) {//Preview or Auto multizone if(chromina > 10000.f) chaut*=0.7f;//decrease action for high chroma (visible noise) else if(chromina > 6000.f) chaut*=0.9f; else if(chromina < 3000.f) chaut*=1.2f;//increase action in low chroma==> 1.2 /==>2.0 ==> curve CC else if(chromina < 2000.f) chaut*=1.5f;//increase action in low chroma==> 1.5 / ==>2.7 if(lumema < 2500.f) chaut*=1.3f;//increase action for low light else if(lumema < 5000.f) chaut*=1.2f; else if(lumema > 20000.f) chaut*=0.9f;//decrease for high light } else if(mode==1){//auto ==> less coefficient because interaction if(chromina > 10000.f) chaut*=0.8f;//decrease action for high chroma (visible noise) else if(chromina > 6000.f) chaut*=0.9f; else if(chromina < 3000.f) chaut*=1.5f;//increase action in low chroma else if(chromina < 2000.f) chaut*=2.2f;//increase action in low chroma if(lumema < 2500.f) chaut*=1.2f;//increase action for low light else if(lumema < 5000.f) chaut*=1.1f; else if(lumema > 20000.f) chaut*=0.9f;//decrease for high light } if(levaut==0) {//Low denoise if(chaut > 300.f) chaut = 0.714286f*chaut + 85.71428f; } delta = maxmax-chaut; delta*=reducdelta; if(lissage==1 || lissage==2) { if(chaut < 200.f && delta < 200.f ) delta*=0.95f; else if(chaut < 200.f && delta < 400.f ) delta*=0.5f; else if(chaut < 200.f && delta >= 400.f ) delta=200.f; else if(chaut < 400.f && delta < 400.f ) delta*=0.4f; else if(chaut < 400.f && delta >= 400.f ) delta=120.f; else if(chaut < 550.f) delta*=0.15f; else if(chaut < 650.f) delta*=0.1f; else if(chaut >= 650.f) delta*=0.07f; if(mode==0 || mode==2) {//Preview or Auto multizone if(chromina < 6000.f) delta*=1.4f;//increase maxi if(lumema < 5000.f) delta*=1.4f; } else if(mode==1) {//Auto if(chromina < 6000.f) delta*=1.2f;//increase maxi if(lumema < 5000.f) delta*=1.2f; } } if(lissage==0) { if(chaut < 200.f && delta < 200.f ) delta*=0.95f; else if(chaut < 200.f && delta < 400.f ) delta*=0.7f; else if(chaut < 200.f && delta >= 400.f ) delta=280.f; else if(chaut < 400.f && delta < 400.f ) delta*=0.6f; else if(chaut < 400.f && delta >= 400.f ) delta=200.f; else if(chaut < 550.f) delta*=0.3f; else if(chaut < 650.f) delta*=0.2f; else if(chaut >= 650.f) delta*=0.15f; if(mode==0 || mode==2) {//Preview or Auto multizone if(chromina < 6000.f) delta*=1.4f;//increase maxi if(lumema < 5000.f) delta*=1.4f; } else if(mode==1) {//Auto if(chromina < 6000.f) delta*=1.2f;//increase maxi if(lumema < 5000.f) delta*=1.2f; } } } void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat * dst,Imagefloat * provicalc, bool isRAW, LUTf &gamcurve, float gam, float gamthresh, float gamslope, const procparams::DirPyrDenoiseParams & dnparams, const double expcomp, const NoiseCurve & noiseLCurve, const NoiseCurve & noiseCCurve, float &chaut, int &Nb, float &redaut, float &blueaut, float &maxredaut, float &maxblueaut, float &minredaut, float &minblueaut, float &nresi, float &highresi, float &chromina, float &sigma, float &lumema, float &sigma_L, float &redyel, float &skinc, float &nsknc) { // StopWatch Stop1("RGB_denoise_info"); if (dnparams.luma==0 && dnparams.chroma==0 && ((settings->leveldnautsimpl==1 && dnparams.Cmethod=="MAN") || (settings->leveldnautsimpl==0 && dnparams.C2method!="MANU")) && !dnparams.median && !noiseLCurve && !noiseCCurve) { //nothing to do; copy src to dst or do nothing in case src == dst return; } int hei,wid; // float LLum,AAum,BBum; float** lumcalc; float** acalc; float** bcalc; if(noiseLCurve || noiseCCurve) { hei=provicalc->height; wid=provicalc->width; TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); double wpi[3][3] = { {wprofi[0][0],wprofi[0][1],wprofi[0][2]}, {wprofi[1][0],wprofi[1][1],wprofi[1][2]}, {wprofi[2][0],wprofi[2][1],wprofi[2][2]} }; lumcalc = new float*[hei]; for (int i=0; ir(ii,jj); float GL = provicalc->g(ii,jj); float BL = provicalc->b(ii,jj); // determine luminance for noisecurve float XL,YL,ZL; Color::rgbxyz(RL,GL,BL,XL,YL,ZL,wpi); Color::XYZ2Lab(XL, YL, ZL, LLum, AAum, BBum); lumcalc[ii][jj]=LLum; acalc[ii][jj]=AAum; bcalc[ii][jj]=BBum; } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% const short int imheight=src->height, imwidth=src->width; if (dnparams.luma!=0 || dnparams.chroma!=0 || dnparams.methodmed=="Lab" || dnparams.methodmed=="Lonly" ) { bool perf = (dnparams.dmethod=="RGB"); // gamma transform for input data // LUTf gamcurve(65536,0); // float gam, gamthresh, gamslope; // RGB_denoise_infoGamCurve(dnparams, isRAW, gamcurve, gam, gamthresh, gamslope); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% const float gain = pow (2.0f, float(expcomp)); float incr=1.f; float noisevar_Ldetail = SQR((float)(SQR(100.-dnparams.Ldetail) + 50.*(100.-dnparams.Ldetail)) * TS * 0.5f * incr); bool enhance_denoise = dnparams.enhance; int gamlab = settings->denoiselabgamma;//gamma lab essentialy for Luminance detail if(gamlab > 2) gamlab=2; /* array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); const int border = MAX(2,TS/16); for (int i=0; iTS/2 ? i-TS+1 : i)); float vmask = (i1TS/2 ? j-TS+1 : j)); tilemask_in[i][j] = (vmask * (j1data[n] = 0; 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; //always no Tiles int kall=0; Tile_calc (tilesize, overlap, kall, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% { //DCT block data storage TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); double wp[3][3] = { {wprof[0][0],wprof[0][1],wprof[0][2]}, {wprof[1][0],wprof[1][1],wprof[1][2]}, {wprof[2][0],wprof[2][1],wprof[2][2]} }; TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); //inverse matrix user select double wip[3][3] = { {wiprof[0][0],wiprof[0][1],wiprof[0][2]}, {wiprof[1][0],wiprof[1][1],wiprof[1][2]}, {wiprof[2][0],wiprof[2][1],wiprof[2][2]} }; float chau=0.f; float chred=0.f; float chblue=0.f; float maxchred=0.f; float maxchblue=0.f; float minchred =100000000.f; float minchblue=100000000.f; int nb=0; // resid=0.f; // nbresid=0; // maxredresid=0.f; // maxblueresid=0.f; // residred=0.f; // residblue=0.f; int comptlevel=0; // static MyMutex FftwMutex; // MyMutex::MyLock lock(FftwMutex); for (int tiletop=0; tiletop Lin(width,height); //wavelet denoised image LabImage * labdn = new LabImage(width,height); float* mad_LL = new float [height*width]; float** noisevarlum; noisevarlum = new float*[height]; for (int i=0; i Ldetail(width,height,ARRAY2D_CLEAR_DATA); //pixel weight // array2D totwt(width,height,ARRAY2D_CLEAR_DATA);//weight for combining DCT blocks // init luma noisevarL float noisevarL = (float) (SQR((dnparams.luma/125.0)*(1.+ dnparams.luma/25.0))); float noisevarab_b, noisevarab_r; bool ponder=false; float realred2, realred, realblue, realblue2; float interm_med =(float) dnparams.chroma/10.0; float intermred, intermblue; if(dnparams.redchro > 0.) intermred=(dnparams.redchro/10.); else intermred= (float) dnparams.redchro/7.0;//increase slower than linear for more sensit if(dnparams.bluechro > 0.) intermblue=(dnparams.bluechro/10.); else intermblue= (float) dnparams.bluechro/7.0;//increase slower than linear for more sensit realred = interm_med + intermred; if (realred < 0.f) realred=0.001f; realblue = interm_med + intermblue; if (realblue < 0.f) realblue=0.001f; //TODO: implement using AlignedBufferMP //fill tile from image; convert RGB to "luma/chroma" if (isRAW) {//image is raw; use channel differences for chroma channels if(!perf){//lab mode //modification Jacques feb 2013 and july 2014 for (int i=tiletop/*, i1=0*/; ir(i,j); float G_ = gain*src->g(i,j); float B_ = gain*src->b(i,j); float Llum,alum,blum; if(noiseLCurve) { Llum=lumcalc[i][j]; } if(noiseCCurve) { alum=acalc[i][j]; blum=bcalc[i][j]; } //finally I opted fot gamma55 and with options we can change if (gamlab == 0) {// options 12/2013 R_ = Color::igammatab_26_11[R_]; G_ = Color::igammatab_26_11[G_]; B_ = Color::igammatab_26_11[B_]; } else if (gamlab == 1) { //other new gamma 4 5 R_ = Color::igammatab_4[R_]; G_ = Color::igammatab_4[G_]; B_ = Color::igammatab_4[B_]; } else if (gamlab == 2) { //new gamma 5.5 10 better for detail luminance..it is a compromise...which depends on the image (distribution BL, ML, HL ...) R_ = Color::igammatab_55[R_]; G_ = Color::igammatab_55[G_]; B_ = Color::igammatab_55[B_]; } //apply gamma noise standard (slider) R_ = R_<65535.0f ? gamcurve[R_] : (Color::gamman((double)R_/65535.0, gam)*32768.0f); G_ = G_<65535.0f ? gamcurve[G_] : (Color::gamman((double)G_/65535.0, gam)*32768.0f); B_ = B_<65535.0f ? gamcurve[B_] : (Color::gamman((double)B_/65535.0, gam)*32768.0f); //true conversion xyz=>Lab float L,a,b; float X,Y,Z; Color::rgbxyz(R_,G_,B_,X,Y,Z,wp); //convert to Lab Color::XYZ2Lab(X, Y, Z, L, a, b); float noiseluma=(float) dnparams.luma; if(noiseLCurve) { float kN=Llum;//with no gamma and take into account working profile float epsi=0.01f; if(kN<2.f) kN=2.f;//avoid divided by zero if(kN>32768.f) kN=32768.f; // not strictly necessary float kinterm=epsi+ noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; noisevarlum[i1][j1]= kN;// SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); // chroma noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float kN=Llum;//with no gamma and take into account working profile if(kN<2.f) kN=2.f;//avoid divided by zero if(kN>32768.f) kN=32768.f; // not strictly necessary float cN=sqrt(SQR(aN)+SQR(bN)); float hN=xatan2f(bN,aN); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + 10.f*noiseCCurve[(cN/48000.f)*500.f];//C=f(C) noisevarchrom[i1][j1]=cN; noisevarhue[i1][j1]=hN; } //end chroma labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; // Lin[i1][j1] = L; // totwt[i1][j1] = 0; } } } else {//RGB mode for (int i=tiletop/*, i1=0*/; ir(i,j); float Y = gain*src->g(i,j); float Z = gain*src->b(i,j); //conversion colorspace to determine luminance with no gamma float Llum,alum,blum; if(noiseLCurve) { Llum=lumcalc[i][j]; } if(noiseCCurve) { alum=acalc[i][j]; blum=bcalc[i][j]; } X = X<65535.0f ? gamcurve[X] : (Color::gamma((double)X/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Y = Y<65535.0f ? gamcurve[Y] : (Color::gamma((double)Y/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Z = Z<65535.0f ? gamcurve[Z] : (Color::gamma((double)Z/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); float noiseluma=(float) dnparams.luma; if(noiseLCurve) { // float noiseluma=(float) dnparams.luma; float kN=Llum; float epsi=0.01f; if(kN<2.f) kN=2.f; if(kN>32768.f) kN=32768.f; float kinterm=epsi + noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; noisevarlum[i1][j1]= kN;//SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float hN=xatan2f(bN,aN); float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + 10.f*noiseCCurve[(cN/48000.f)*500.f]; //noisevarchrom[i1][j1]=0.5f*(noisevarab_b+noisevarab_r)*(Cinterm*Cinterm); noisevarchrom[i1][j1]=cN; noisevarhue[i1][j1]=hN; } //end chroma labdn->L[i1][j1] = Y; labdn->a[i1][j1] = (X-Y); labdn->b[i1][j1] = (Y-Z); // Ldetail[i1][j1] = 0; // Lin[i1][j1] = Y; // totwt[i1][j1] = 0; } } } } else {//image is not raw; use Lab parametrization for (int i=tiletop/*, i1=0*/; ir(i,j) ;//for luminance denoise curve float gLum=src->g(i,j) ; float bLum=src->b(i,j) ; //use gamma sRGB, not good if TIF (JPG) Output profil not with gamma sRGB (eg : gamma =1.0, or 1.8...) //very difficult to solve ! // solution ==> save TIF with gamma sRGB and re open float rtmp = Color::igammatab_srgb[ src->r(i,j) ]; float gtmp = Color::igammatab_srgb[ src->g(i,j) ]; float btmp = Color::igammatab_srgb[ src->b(i,j) ]; //modification Jacques feb 2013 // gamma slider different from raw rtmp = rtmp<65535.0f ? gamcurve[rtmp] : (Color::gamman((double)rtmp/65535.0, gam)*32768.0f); gtmp = gtmp<65535.0f ? gamcurve[gtmp] : (Color::gamman((double)gtmp/65535.0, gam)*32768.0f); btmp = btmp<65535.0f ? gamcurve[btmp] : (Color::gamman((double)btmp/65535.0, gam)*32768.0f); noisevarab_r = SQR(realred); noisevarab_b = SQR(realblue); float X,Y,Z; Color::rgbxyz(rtmp,gtmp,btmp,X,Y,Z,wp); //convert Lab Color::XYZ2Lab(X, Y, Z, L, a, b); float Llum,alum,blum; if(noiseLCurve || noiseCCurve) { float XL,YL,ZL; Color::rgbxyz(rLum,gLum,bLum,XL,YL,ZL,wp); Color::XYZ2Lab(XL, YL, ZL, Llum, alum, blum); } float noiseluma=(float) dnparams.luma; if(noiseLCurve) { // float noiseluma=(float) dnparams.luma; float kN=Llum; float epsi=0.01f; if(kN<2.f) kN=2.f; if(kN>32768.f) kN=32768.f; float kinterm=epsi + noiseLCurve[(kN/32768.f)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noiseluma += 1.f; // noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); noisevarlum[i1][j1]=kN;//SQR((ki/125.f)*(1.f+ki/25.f)); } noisevarL = SQR((noiseluma/125.f)*(1.f+noiseluma/25.f)); if(noiseCCurve) { float aN=alum; float bN=blum; float epsic=0.01f; float hN=xatan2f(bN,aN); float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero float Cinterm=1.f + 10.f*noiseCCurve[(cN/48000.f)*500.f]; //noisevarchrom[i1][j1]=0.5f*(noisevarab_b+noisevarab_r)*(Cinterm*Cinterm); noisevarchrom[i1][j1]=cN; noisevarhue[i1][j1]=hN; } labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; // Ldetail[i1][j1] = 0; // Lin[i1][j1] = L; // totwt[i1][j1] = 0; } } } /* if (dnparams.luma>0.01) { impulse_nr (labdn, float(MIN(50.0,dnparams.luma))/20.0f); } */ int datalen = labdn->W * labdn->H; //now perform basic wavelet denoise //last two arguments of wavelet decomposition are max number of wavelet decomposition levels; //and whether to subsample the image after wavelet filtering. Subsampling is coded as //binary 1 or 0 for each level, eg subsampling = 0 means no subsampling, 1 means subsample //the first level only, 7 means subsample the first three levels, etc. noisevarab_r = SQR(realred) + 0.01f; noisevarab_b = SQR(realblue) + 0.01f; bool execwavelet=true; if(execwavelet) {//gain time if user choose only median sliders L <=1 slider chrom master < 1 { // enclosing this code in a block frees about 120 MB before allocating 20 MB after this block (measured with D700 NEF) wavelet_decomposition* Ldecomp; wavelet_decomposition* adecomp; wavelet_decomposition* bdecomp; int schoice=0;//shrink method if(dnparams.smethod=="shal") schoice=0; else if(dnparams.smethod=="shalbi") schoice=2; int levwav=5; float maxreal = max(realred, realblue); //increase the level of wavelet if user increase much or very much sliders // if (settings->verbose) printf("levwavelet=%i noisevarA=%f noisevarB=%f \n",levwav, noisevarab_r, noisevarab_b ); Ldecomp = new wavelet_decomposition (labdn->data, labdn->W, labdn->H, levwav/*maxlevels*/, 0/*subsampling*/ ); adecomp = new wavelet_decomposition (labdn->data+datalen, labdn->W, labdn->H,levwav, 1 ); bdecomp = new wavelet_decomposition (labdn->data+2*datalen, labdn->W, labdn->H, levwav, 1 ); bool autoch = dnparams.autochroma; if(comptlevel==0) WaveletDenoiseAll_info(*Ldecomp, *adecomp, *bdecomp, noisevarL, noisevarlum, noisevarchrom, noisevarhue, width, height, mad_LL, mad_aa, mad_bb, noisevarab_r, noisevarab_b,labdn, noiseLCurve, noiseCCurve, chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, schoice, autoch, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc,maxchred, maxchblue, minchred, minchblue, nb,chau ,chred, chblue);//enhance mode comptlevel+=1; float chresid,chmaxredresid,chmaxblueresid,chresidred, chresidblue; nresi=chresid; highresi=chresid + 0.66f*(max(chmaxredresid,chmaxblueresid) - chresid);//evaluate sigma delete Ldecomp; delete adecomp; delete bdecomp; } } delete labdn; // delete noiseh; for (int i=0; i