//////////////////////////////////////////////////////////////// // // 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 "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 "cplx_wavelet_dec.h" #ifdef _OPENMP #include #endif #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 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 = 0; int high = n-1; int 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] ; } int 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]) ; int ll = low + 1; int 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) { // Order elements (only half of them) for (int i = 0; i < (N >> 1) + 1; ++i) { // Find position of minimum element int min = i; for (int j = i + 1; j < N; ++j) if (elements[j] < elements[min]) min = j; // Put found minimum element in its place float temp = elements[i]; elements[i] = elements[min]; elements[min] = temp; } // Get result - the middle element return elements[N >> 1]; } void ImProcFunctions::Median_Denoise( float **src, float **dst, const int width, const int height, const mediantype medianType, const int iterations, const int numThreads, float **buffer) { int border=1, numElements, middleElement; switch(medianType) { case MED_3X3SOFT: case MED_3X3STRONG: border = 1; break; case MED_5X5SOFT: border = 2; break; case MED_5X5STRONG: border = 2; break; case MED_7X7: border = 3; numElements = 49; middleElement = 24; break; default: // includes MED_9X9 border = 4; numElements = 81; middleElement = 40; } float **allocBuffer = NULL; float **medBuffer[2]; medBuffer[0] = src; // we need a buffer if src == dst or if (src != dst && iterations > 1) if(src == dst || (src != dst && iterations>1)) { if(buffer == NULL) { // we didn't get a buufer => create one allocBuffer = new float*[height]; for (int i=0; i use it medBuffer[1] = buffer; } } else { // we can write directly into destination medBuffer[1] = dst; } float ** medianIn, ** medianOut; int BufferIndex = 0; for(int iteration=1;iteration<=iterations;iteration++){ medianIn = medBuffer[BufferIndex]; medianOut = medBuffer[BufferIndex^1]; if(iteration == 1) { // upper border for (int i=0; i1) #endif for (int i=border; i1) #endif for(int i = border; i < height-border; i++ ) { for(int j = border; j < width-border; j++) { dst[i][j] = medianOut[i][j]; } } } if(allocBuffer != NULL) { // we allocated memory, so let's free it now for (int i=0; icopyData(dst); if(calclum) { delete calclum; calclum = NULL; } return; } static MyMutex FftwMutex; MyMutex::MyLock lock(FftwMutex); const nrquality nrQuality = (dnparams.smethod == "shal") ? QUALITY_STANDARD : QUALITY_HIGH;//shrink method const float qhighFactor = (nrQuality == QUALITY_HIGH) ? 1.f/(float) settings->nrhigh : 1.0f; const bool useNoiseCCurve = (noiseCCurve && noiseCCurve.getSum() > 5.f ); const bool useNoiseLCurve = (noiseLCurve && noiseLCurve.getSum() >= 7.f ); const bool autoch = (settings->leveldnautsimpl==1 && (dnparams.Cmethod=="AUT" || dnparams.Cmethod=="PRE")) || (settings->leveldnautsimpl==0 && (dnparams.C2method=="AUTO" || dnparams.C2method=="PREV")); float** lumcalc; float* lumcalcBuffer; float** ccalc; float* ccalcBuffer; bool ponder=false; float ponderCC=1.f; if(settings->leveldnautsimpl==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;} 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; const bool denoiseMethodRgb = (dnparams.dmethod == "RGB"); // init luma noisevarL const float noiseluma=(float) dnparams.luma; const float noisevarL = (useNoiseLCurve && (denoiseMethodRgb || !isRAW)) ? (float) (SQR(((noiseluma+1.0)/125.0)*(10.+ (noiseluma+1.0)/25.0))) : (float) (SQR((noiseluma/125.0)*(1.0+ noiseluma/25.0))); const bool denoiseLuminance = (noisevarL > 0.00001f); //printf("NL=%f \n",noisevarL); if(useNoiseLCurve || useNoiseCCurve) { int hei=calclum->height; int wid=calclum->width; TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); const float wpi[3][3] = { {static_cast(wprofi[0][0]),static_cast(wprofi[0][1]),static_cast(wprofi[0][2])}, {static_cast(wprofi[1][0]),static_cast(wprofi[1][1]),static_cast(wprofi[1][2])}, {static_cast(wprofi[2][0]),static_cast(wprofi[2][1]),static_cast(wprofi[2][2])} }; lumcalcBuffer = new float[hei*wid]; 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 and chrominance for noisecurves float XL,YL,ZL; Color::rgbxyz(RL,GL,BL,XL,YL,ZL,wpi); Color::XYZ2Lab(XL, YL, ZL, LLum, AAum, BBum); if(useNoiseLCurve) { float epsi = 0.01f; if(LLum<2.f) LLum = 2.f;//avoid divided by zero if(LLum>32768.f) LLum = 32768.f; // not strictly necessary float kinterm = epsi + noiseLCurve[xdivf(LLum,15)*500.f]; kinterm *= 100.f; kinterm += noiseluma; lumcalc[ii][jj] = SQR((kinterm/125.f)*(1.f+kinterm/25.f)); } if(useNoiseCCurve) { float cN = sqrtf(SQR(AAum)+SQR(BBum)); if(cN > 100) ccalc[ii][jj] = SQR(1.f + ponderCC*(4.f*noiseCCurve[cN / 60.f])); else ccalc[ii][jj] = cn100Precalc; } } } delete calclum; calclum = NULL; } const short int imheight=src->height, imwidth=src->width; if (dnparams.luma!=0 || dnparams.chroma!=0 || dnparams.methodmed=="Lab" || dnparams.methodmed=="Lonly" ) { // 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,LUT_CLIP_BELOW); if(denoiseMethodRgb) { 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,LUT_CLIP_BELOW); if(denoiseMethodRgb) { 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); } } const float gain = pow (2.0f, float(expcomp)); float noisevar_Ldetail = SQR((float)(SQR(100.-dnparams.Ldetail) + 50.*(100.-dnparams.Ldetail)) * TS * 0.5f); if(settings->verbose) printf("Denoise Lab=%i\n",settings->denoiselabgamma); // To avoid branches in loops we access the gammatabs by pointers // 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 LUTf *denoisegamtab; LUTf *denoiseigamtab; switch(settings->denoiselabgamma) { case 0: denoisegamtab = &(Color::gammatab_26_11); denoiseigamtab = &(Color::igammatab_26_11); break; case 1: denoisegamtab = &(Color::gammatab_4); denoiseigamtab = &(Color::igammatab_4); break; default: denoisegamtab = &(Color::gammatab_55); denoiseigamtab = &(Color::igammatab_55); break; } array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); if(denoiseLuminance) { 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 * (j1leveldnti ==0) { tilesize = 1024; overlap = 128; } if(settings->leveldnti ==1) { tilesize = 768; overlap = 96; } int numTries = 0; if(ponder) printf("Tiled denoise processing caused by Automatic Multizone mode\n"); bool memoryAllocationFailed = false; do { numTries++; if(numTries == 2) printf("1st denoise pass failed due to insufficient memory, starting 2nd (tiled) pass now...\n"); int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; Tile_calc (tilesize, overlap, (options.rgbDenoiseThreadLimit == 0 && !ponder) ? (numTries == 1 ? 0 : 2) : 2, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); memoryAllocationFailed = false; const int numtiles = numtiles_W * numtiles_H; //output buffer Imagefloat * dsttmp; if(numtiles == 1) dsttmp = dst; else { dsttmp = new Imagefloat(imwidth,imheight); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0;ir(i,j) = 0.f; dsttmp->g(i,j) = 0.f; dsttmp->b(i,j) = 0.f; } } //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 fftwf_plan plan_forward_blox[2]; fftwf_plan plan_backward_blox[2]; if(denoiseLuminance) { float *Lbloxtmp = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof (float)); float *fLbloxtmp = (float*) fftwf_malloc(max_numblox_W*TS*TS*sizeof (float)); int nfwd[2]={TS,TS}; //for DCT: fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, NULL, 1, TS*TS, fLbloxtmp, NULL, 1, TS*TS, fwdkind, FFTW_MEASURE || FFTW_DESTROY_INPUT ); 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 || FFTW_DESTROY_INPUT ); 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 || FFTW_DESTROY_INPUT ); 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 || FFTW_DESTROY_INPUT ); fftwf_free ( Lbloxtmp ); fftwf_free ( fLbloxtmp ); } #ifndef _OPENMP int numthreads = 1; #else // Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles int numthreads = MIN(numtiles,omp_get_max_threads()); if(options.rgbDenoiseThreadLimit > 0) numthreads = MIN(numthreads,options.rgbDenoiseThreadLimit); #ifdef _RT_NESTED_OPENMP denoiseNestedLevels = omp_get_max_threads() / numthreads; bool oldNested = omp_get_nested(); if(denoiseNestedLevels < 2) denoiseNestedLevels = 1; else omp_set_nested(true); if(options.rgbDenoiseThreadLimit > 0) while(denoiseNestedLevels*numthreads > options.rgbDenoiseThreadLimit) denoiseNestedLevels--; #endif if(settings->verbose) printf("RGB_denoise uses %d main thread(s) and up to %d nested thread(s) for each main thread\n",numthreads,denoiseNestedLevels); #endif float *LbloxArray[denoiseNestedLevels*numthreads]; float *fLbloxArray[denoiseNestedLevels*numthreads]; if(numtiles > 1 && denoiseLuminance) for(int i=0;iworkingSpaceInverseMatrix (params->icm.working); //inverse matrix user select const float wip[3][3] = { {static_cast(wiprof[0][0]),static_cast(wiprof[0][1]),static_cast(wiprof[0][2])}, {static_cast(wiprof[1][0]),static_cast(wiprof[1][1]),static_cast(wiprof[1][2])}, {static_cast(wiprof[2][0]),static_cast(wiprof[2][1]),static_cast(wiprof[2][2])} }; TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); const float wp[3][3] = { {static_cast(wprof[0][0]),static_cast(wprof[0][1]),static_cast(wprof[0][2])}, {static_cast(wprof[1][0]),static_cast(wprof[1][1]),static_cast(wprof[1][2])}, {static_cast(wprof[2][0]),static_cast(wprof[2][1]),static_cast(wprof[2][2])} }; // begin tile processing of image #ifdef _OPENMP #pragma omp parallel num_threads(numthreads) if(numthreads>1) #endif { int pos; float* noisevarlum; float* noisevarchrom; if(numtiles == 1 && isRAW && (useNoiseCCurve || useNoiseLCurve)) { noisevarlum = lumcalcBuffer; noisevarchrom = ccalcBuffer; } else { noisevarlum = new float[((tileheight+1)/2)*((tilewidth+1)/2)]; noisevarchrom = new float[((tileheight+1)/2)*((tilewidth+1)/2)]; } #ifdef _OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int tiletop=0; tiletop 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; const float noisevarab_r = SQR(realred); const float noisevarab_b = SQR(realblue); //input L channel array2D *Lin; //wavelet denoised image LabImage * labdn = new LabImage(width,height); //fill tile from image; convert RGB to "luma/chroma" const float maxNoiseVarab = max(noisevarab_b,noisevarab_r); if (isRAW) {//image is raw; use channel differences for chroma channels if(!denoiseMethodRgb){//lab mode //modification Jacques feb 2013 and july 2014 #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif for (int i=tiletop; ir(i,j); float G_ = gain*src->g(i,j); float B_ = gain*src->b(i,j); R_ = (*denoiseigamtab)[R_]; G_ = (*denoiseigamtab)[G_]; B_ = (*denoiseigamtab)[B_]; //apply gamma noise standard (slider) R_ = R_<65535.0f ? gamcurve[R_] : (Color::gammanf(R_/65535.f, gam)*32768.0f); G_ = G_<65535.0f ? gamcurve[G_] : (Color::gammanf(G_/65535.f, gam)*32768.0f); B_ = B_<65535.0f ? gamcurve[B_] : (Color::gammanf(B_/65535.f, gam)*32768.0f); //true conversion xyz=>Lab float X,Y,Z; Color::rgbxyz(R_,G_,B_,X,Y,Z,wp); //convert to Lab float L,a,b; Color::XYZ2Lab(X, Y, Z, L, a, b); labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; if(((i1|j1)&1) == 0) { if(numTries == 1) { noisevarlum[(i1>>1)*width2+(j1>>1)] = useNoiseLCurve ? lumcalc[i>>1][j>>1] : noisevarL; noisevarchrom[(i1>>1)*width2+(j1>>1)] = useNoiseCCurve ? maxNoiseVarab*ccalc[i>>1][j>>1] : 1.f; } else { noisevarlum[(i1>>1)*width2+(j1>>1)] = lumcalc[i>>1][j>>1]; noisevarchrom[(i1>>1)*width2+(j1>>1)] = ccalc[i>>1][j>>1]; } } //end chroma } } } else {//RGB mode #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif for (int i=tiletop; 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 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); //end chroma labdn->L[i1][j1] = Y; labdn->a[i1][j1] = (X-Y); labdn->b[i1][j1] = (Y-Z); if(((i1|j1)&1) == 0) { if(numTries == 1) { noisevarlum[(i1>>1)*width2+(j1>>1)] = useNoiseLCurve ? lumcalc[i>>1][j>>1] : noisevarL; noisevarchrom[(i1>>1)*width2+(j1>>1)] = useNoiseCCurve ? maxNoiseVarab*ccalc[i>>1][j>>1] : 1.f; } else { noisevarlum[(i1>>1)*width2+(j1>>1)] = lumcalc[i>>1][j>>1]; noisevarchrom[(i1>>1)*width2+(j1>>1)] = ccalc[i>>1][j>>1]; } } } } } } else {//image is not raw; use Lab parametrization #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif for (int i=tiletop; ir(i,j) ;//for denoise curves 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); float X,Y,Z; Color::rgbxyz(rtmp,gtmp,btmp,X,Y,Z,wp); //convert Lab Color::XYZ2Lab(X, Y, Z, L, a, b); labdn->L[i1][j1] = L; labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; if(((i1|j1)&1) == 0) { float Llum,alum,blum; if(useNoiseLCurve || useNoiseCCurve) { float XL,YL,ZL; Color::rgbxyz(rLum,gLum,bLum,XL,YL,ZL,wp); Color::XYZ2Lab(XL, YL, ZL, Llum, alum, blum); } if(useNoiseLCurve) { 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[xdivf(kN,15)*500.f]; float ki=kinterm*100.f; ki+=noiseluma; noisevarlum[(i1>>1)*width2+(j1>>1)]=SQR((ki/125.f)*(1.f+ki/25.f)); } else { noisevarlum[(i1>>1)*width2+(j1>>1)] = noisevarL; } if(useNoiseCCurve) { float aN=alum; float bN=blum; float cN=sqrtf(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero ??? float Cinterm=1.f + ponderCC*4.f*noiseCCurve[cN/60.f]; noisevarchrom[(i1>>1)*width2+(j1>>1)]= maxNoiseVarab*SQR(Cinterm); } else { noisevarchrom[(i1>>1)*width2+(j1>>1)] = 1.f; } } } } } //now perform basic wavelet denoise //arguments 4 and 5 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. //actual implementation only works with subsampling set to 1 float interm_medT = (float) dnparams.chroma/10.0; bool execwavelet = true; if(!denoiseLuminance && 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(execwavelet) {//gain time if user choose only median sliders L <=1 slider chrom master < 1 wavelet_decomposition* Ldecomp; wavelet_decomposition* adecomp; 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(nrQuality == QUALITY_HIGH) levwav += settings->nrwavlevel;//increase level for enhanced mode if(levwav>8) levwav=8; int minsizetile=min(tilewidth, tileheight); int maxlev2=8; if(minsizetile < 256) maxlev2 = 7; if(minsizetile < 128) maxlev2 = 6; if(minsizetile < 64) maxlev2 = 5; levwav=min(maxlev2,levwav); // if (settings->verbose) printf("levwavelet=%i noisevarA=%f noisevarB=%f \n",levwav, noisevarab_r, noisevarab_b ); Ldecomp = new wavelet_decomposition (labdn->L[0], labdn->W, labdn->H, levwav, 1, 1, max(1,denoiseNestedLevels)); if(Ldecomp->memoryAllocationFailed) { memoryAllocationFailed = true; } float madL[8][3]; if(!memoryAllocationFailed) { // precalculate madL, because it's used in adecomp and bdecomp int maxlvl = Ldecomp->maxlevel(); #ifdef _RT_NESTED_OPENMP #pragma omp parallel for schedule(dynamic) collapse(2) num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif for (int lvl=0; lvllevel_W(lvl); int Hlvl_L = Ldecomp->level_H(lvl); float ** WavCoeffs_L = Ldecomp->level_coeffs(lvl); if(!denoiseMethodRgb) { madL[lvl][dir-1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L*Hlvl_L)); } else { madL[lvl][dir-1] = SQR(MadRgb(WavCoeffs_L[dir], Wlvl_L*Hlvl_L)); } } } } float chresid = 0.f; float chresidtemp=0.f; float chmaxresid = 0.f; float chmaxresidtemp = 0.f; adecomp = new wavelet_decomposition (labdn->a[0], labdn->W, labdn->H,levwav, 1, 1, max(1,denoiseNestedLevels)); if(adecomp->memoryAllocationFailed) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { if(nrQuality==QUALITY_STANDARD) { if(!WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, noisevarab_r, useNoiseCCurve, autoch, denoiseMethodRgb ))//enhance mode memoryAllocationFailed = true; } else /*if(nrQuality==QUALITY_HIGH)*/ { if(!WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *adecomp, noisevarchrom, madL, noisevarab_r, useNoiseCCurve, autoch, denoiseMethodRgb ))//enhance mode memoryAllocationFailed = true; if(!memoryAllocationFailed) if(!WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, noisevarab_r, useNoiseCCurve, autoch, denoiseMethodRgb )) memoryAllocationFailed = true; } } if(!memoryAllocationFailed) { if(kall == 0) { Noise_residualAB(*adecomp, chresid, chmaxresid, denoiseMethodRgb); chresidtemp=chresid; chmaxresidtemp= chmaxresid; } adecomp->reconstruct(labdn->a[0]); } delete adecomp; if(!memoryAllocationFailed) { wavelet_decomposition* bdecomp = new wavelet_decomposition (labdn->b[0], labdn->W, labdn->H, levwav, 1, 1, max(1,denoiseNestedLevels)); if(bdecomp->memoryAllocationFailed) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { if(nrQuality==QUALITY_STANDARD) { if(!WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, noisevarab_b, useNoiseCCurve, autoch, denoiseMethodRgb ))//enhance mode memoryAllocationFailed = true; } else /*if(nrQuality==QUALITY_HIGH)*/ { if(!WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *bdecomp, noisevarchrom, madL, noisevarab_b, useNoiseCCurve, autoch, denoiseMethodRgb ))//enhance mode memoryAllocationFailed = true; if(!memoryAllocationFailed) if(!WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, noisevarab_b, useNoiseCCurve, autoch, denoiseMethodRgb )) memoryAllocationFailed = true; } } if(!memoryAllocationFailed) { if(kall == 0) { Noise_residualAB(*bdecomp, chresid, chmaxresid, denoiseMethodRgb); chresid += chresidtemp; chmaxresid += chmaxresidtemp; chresid = sqrt(chresid/(6*(levwav))); highresi = chresid + 0.66f*(sqrt(chmaxresid) - chresid);//evaluate sigma nresi = chresid; } bdecomp->reconstruct(labdn->b[0]); } delete bdecomp; if(!memoryAllocationFailed) { if(denoiseLuminance) { int edge=0; if(nrQuality==QUALITY_STANDARD) { if(!WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, NULL, edge))//enhance mode memoryAllocationFailed = true; } else /*if(nrQuality==QUALITY_HIGH)*/ { if(!WaveletDenoiseAll_BiShrinkL(*Ldecomp, noisevarlum, madL))//enhance mode memoryAllocationFailed = true; if(!memoryAllocationFailed) if(!WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, NULL, edge)) memoryAllocationFailed = true; } if(!memoryAllocationFailed) { // copy labdn->L to Lin before it gets modified by reconstruction Lin = new array2D(width,height); #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif for(int i=0;iL[i][j]; Ldecomp->reconstruct(labdn->L[0]); } } } } delete Ldecomp; } if(!memoryAllocationFailed) { if( (metchoice==1 || metchoice==2 || metchoice==3 || metchoice==4) && dnparams.median) { float** tmL; int wid=labdn->W; int hei=labdn->H; tmL = new float*[hei]; for (int i=0; iL, labdn->L, wid, hei, medianTypeL, dnparams.passes, denoiseNestedLevels, tmL); if(metchoice==2 || metchoice==3 || metchoice==4) { Median_Denoise( labdn->a, labdn->a, wid, hei, medianTypeAB, dnparams.passes, denoiseNestedLevels, tmL); Median_Denoise( labdn->b, labdn->b, wid, hei, medianTypeAB, dnparams.passes, denoiseNestedLevels, tmL); } 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 if(numtiles == 1) for(int i=0;i1) #endif { #ifdef _RT_NESTED_OPENMP int subThread = masterThread * denoiseNestedLevels + omp_get_thread_num(); #else int subThread = 0; #endif float blurbuffer[TS*TS] ALIGNED64; float *Lblox = LbloxArray[subThread]; float *fLblox = fLbloxArray[subThread]; float pBuf[width + TS + 2*blkrad*offset] ALIGNED16; float nbrwt[TS*TS] ALIGNED64; #ifdef _RT_NESTED_OPENMP #pragma omp for #endif for (int vblk=0; vblk=height) { rr = MAX(0,2*height-2-row); } for (int j=0; jW; j++) { datarow[j] = ((*Lin)[rr][j]-labdn->L[rr][j]); } for (int j=-blkrad*offset; j<0; j++) { datarow[j] = datarow[MIN(-j,width-1)]; } for (int j=width; j=0 && top+i1) #endif for (int i=0; iL[i][j] += Ldetail[i][j]/totwt[i][j]; //note that labdn initially stores the denoised hipass data } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform denoised "Lab" to output RGB //calculate mask for feathering output tile overlaps float Vmask[height+1] ALIGNED16; float Hmask[width+1] ALIGNED16; float newGain; if(numtiles > 1) { for (int i=0; i0) Vmask[i] = mask; if (tilebottom0) Hmask[i] = mask/newGain; if (tilerightxyz float L = labdn->L[i1][j1]; float a = labdn->a[i1][j1]; float b = labdn->b[i1][j1]; float c_h=SQR(a)+SQR(b); if(c_h>9000000.f){ a *= 1.f + qhighFactor*realred; b *= 1.f + qhighFactor*realblue; } //convert XYZ float X,Y,Z; 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.f ? igamcurve[r_] : (Color::gammanf(r_/32768.f, igam) * 65535.f); g_ = g_<32768.f ? igamcurve[g_] : (Color::gammanf(g_/32768.f, igam) * 65535.f); b_ = b_<32768.f ? igamcurve[b_] : (Color::gammanf(b_/32768.f, igam) * 65535.f); //readapt arbitrary gamma (inverse from beginning) r_ = (*denoisegamtab)[r_]; g_ = (*denoisegamtab)[g_]; b_ = (*denoisegamtab)[b_]; if(numtiles == 1) { dsttmp->r(i,j) = newGain*r_; dsttmp->g(i,j) = newGain*g_; dsttmp->b(i,j) = newGain*b_; } else { float factor = Vmask[i1]*Hmask[j1]; dsttmp->r(i,j) += factor*r_; dsttmp->g(i,j) += factor*g_; dsttmp->b(i,j) += factor*b_; } } } } else {//RGB mode #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) #endif for (int i=tiletop; ia[i1][j1])+SQR(labdn->b[i1][j1])); if(c_h>3000.f){ labdn->a[i1][j1]*=1.f + qhighFactor*realred/100.f; labdn->b[i1][j1]*=1.f + qhighFactor*realblue/100.f; } float Y = labdn->L[i1][j1]; float X = (labdn->a[i1][j1]) + Y; float 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); if(numtiles == 1) { dsttmp->r(i,j) = newGain*X; dsttmp->g(i,j) = newGain*Y; dsttmp->b(i,j) = newGain*Z; } else { float factor = Vmask[i1]*Hmask[j1]; dsttmp->r(i,j) += factor*X; dsttmp->g(i,j) += factor*Y; dsttmp->b(i,j) += factor*Z; } } } } } else { #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(denoiseNestedLevels) #endif for (int i=tiletop; iL[i1][j1]; float a = labdn->a[i1][j1]; float b = labdn->b[i1][j1]; float c_h=sqrt(SQR(a)+SQR(b)); if(c_h>3000.f){ a*=1.f + qhighFactor*realred/100.f; b*=1.f + qhighFactor*realblue/100.f; } float X,Y,Z; Color::Lab2XYZ(L, a, b, X, Y, Z); 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); if(numtiles == 1) { dsttmp->r(i,j) = newGain*r_; dsttmp->g(i,j) = newGain*g_; dsttmp->b(i,j) = newGain*b_; } else { float factor = Vmask[i1]*Hmask[j1]; dsttmp->r(i,j) += factor*r_; dsttmp->g(i,j) += factor*g_; dsttmp->b(i,j) += factor*b_; } } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% } delete labdn; if(denoiseLuminance) delete Lin; }//end of tile row }//end of tile loop if(numtiles > 1 || !isRAW || (!useNoiseCCurve && !useNoiseLCurve)) { delete [] noisevarlum; delete [] noisevarchrom; } } if(denoiseLuminance) { for(int i=0;i1) { if(!memoryAllocationFailed) dsttmp->copyData(dst); else if(dst != src) src->copyData(dst); delete dsttmp; } if (!isRAW && !memoryAllocationFailed) {//restore original image gamma #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0;iheight;i++) for(int j=0;jwidth;j++) { dst->r(i,j) = Color::gammatab_srgb[ dst->r(i,j) ]; dst->g(i,j) = Color::gammatab_srgb[ dst->g(i,j) ]; dst->b(i,j) = Color::gammatab_srgb[ dst->b(i,j) ]; } } if(denoiseLuminance) { // 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(); } } while(memoryAllocationFailed && numTries < 2 && (options.rgbDenoiseThreadLimit == 0) && !ponder); if(memoryAllocationFailed) printf("tiled denoise failed due to isufficient memory. Output is not denoised!\n"); } //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; nmaxresid ) maxresid=madC; } } chresid = resid; chmaxresid = maxresid; } SSEFUNCTION bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3]) { int maxlvl = min(WaveletCoeffs_L.maxlevel(),5); const float eps = 0.01f; int maxWL = 0, maxHL = 0; for (int lvl=0; lvl maxWL) maxWL = WaveletCoeffs_L.level_W(lvl); if(WaveletCoeffs_L.level_H(lvl) > maxHL) maxHL = WaveletCoeffs_L.level_H(lvl); } bool memoryAllocationFailed = false; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif { float *buffer[3]; buffer[0] = new (std::nothrow) float[maxWL*maxHL+32]; buffer[1] = new (std::nothrow) float[maxWL*maxHL+64]; buffer[2] = new (std::nothrow) float[maxWL*maxHL+96]; if(buffer[0] == NULL || buffer[1] == NULL || buffer[2] == NULL) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int lvl=maxlvl-1; lvl>=0; lvl--) {//for levels less than max, use level diff to make edge mask for (int dir=1; dir<4; dir++) { int Wlvl_L = WaveletCoeffs_L.level_W(lvl); int Hlvl_L = WaveletCoeffs_L.level_H(lvl); float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); if (lvl==maxlvl-1) { float vari[3]; int edge=0; ShrinkAllL(WaveletCoeffs_L, buffer, lvl, dir, noisevarlum, madL[lvl], NULL, edge ); } else { //simple wavelet shrinkage float * sfave = buffer[0]+32; float * sfaved = buffer[2]+96; float * blurBuffer = buffer[1]+64; float mad_Lr = madL[lvl][dir-1]; float levelFactor = mad_Lr*5.f/(lvl+1); #ifdef __SSE2__ __m128 mad_Lv; __m128 ninev = _mm_set1_ps( 9.0f ); __m128 epsv = _mm_set1_ps(eps); __m128 mag_Lv; __m128 levelFactorv = _mm_set1_ps(levelFactor); int coeffloc_L; for (coeffloc_L=0; coeffloc_L=0;i--) if(buffer[i] != NULL) delete [] buffer[i]; } return (!memoryAllocationFailed); } SSEFUNCTION bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(wavelet_decomposition &WaveletCoeffs_L, wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb) { int maxlvl = WaveletCoeffs_L.maxlevel(); if(autoch && noisevar_ab <=0.001f) noisevar_ab=0.02f; float madab[8][3]; int maxWL = 0, maxHL = 0; for (int lvl=0; lvl maxWL) maxWL = WaveletCoeffs_L.level_W(lvl); if(WaveletCoeffs_L.level_H(lvl) > maxHL) maxHL = WaveletCoeffs_L.level_H(lvl); } bool memoryAllocationFailed = false; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif { float *buffer[3]; buffer[0] = new (std::nothrow) float[maxWL*maxHL+32]; buffer[1] = new (std::nothrow) float[maxWL*maxHL+64]; buffer[2] = new (std::nothrow) float[maxWL*maxHL+96]; if(buffer[0] == NULL || buffer[1] == NULL || buffer[2] == NULL) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int lvl=0; lvl=0; lvl--) {//for levels less than max, use level diff to make edge mask for (int dir=1; dir<4; dir++) { int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl); int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl); float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); if (lvl==maxlvl-1) { ShrinkAllAB(WaveletCoeffs_L, WaveletCoeffs_ab, buffer, lvl, dir, noisevarchrom, noisevar_ab, useNoiseCCurve, autoch, denoiseMethodRgb, madL[lvl], madab[lvl], true); } else { //simple wavelet shrinkage float mad_Lr = madL[lvl][dir-1]; float mad_abr = useNoiseCCurve ? noisevar_ab*madab[lvl][dir-1] : SQR(noisevar_ab)*madab[lvl][dir-1]; if (noisevar_ab>0.001f) { #ifdef __SSE2__ __m128 onev = _mm_set1_ps(1.f); __m128 mad_abrv = _mm_set1_ps(mad_abr); __m128 rmad_Lm9v = onev / _mm_set1_ps(mad_Lr * 9.f); __m128 mad_abv; __m128 mag_Lv, mag_abv; __m128 tempabv; int coeffloc_ab; for (coeffloc_ab=0; coeffloc_ab=0;i--) if(buffer[i] != NULL) delete [] buffer[i]; } return (!memoryAllocationFailed); } bool ImProcFunctions::WaveletDenoiseAllL(wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge)//mod JD { int maxlvl = min(WaveletCoeffs_L.maxlevel(),5); if(edge==1) maxlvl=3;//for refine denoise edge wavelet int maxWL = 0, maxHL = 0; for (int lvl=0; lvl maxWL) maxWL = WaveletCoeffs_L.level_W(lvl); if(WaveletCoeffs_L.level_H(lvl) > maxHL) maxHL = WaveletCoeffs_L.level_H(lvl); } bool memoryAllocationFailed = false; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif { float *buffer[3]; buffer[0] = new (std::nothrow) float[maxWL*maxHL+32]; buffer[1] = new (std::nothrow) float[maxWL*maxHL+64]; buffer[2] = new (std::nothrow) float[maxWL*maxHL+96]; if(buffer[0] == NULL || buffer[1] == NULL || buffer[2] == NULL) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int lvl=0; lvl=0;i--) if(buffer[i] != NULL) delete [] buffer[i]; } return (!memoryAllocationFailed); } bool ImProcFunctions::WaveletDenoiseAllAB(wavelet_decomposition &WaveletCoeffs_L, wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb)//mod JD { int maxlvl = WaveletCoeffs_L.maxlevel(); int maxWL = 0, maxHL = 0; for (int lvl=0; lvl maxWL) maxWL = WaveletCoeffs_L.level_W(lvl); if(WaveletCoeffs_L.level_H(lvl) > maxHL) maxHL = WaveletCoeffs_L.level_H(lvl); } bool memoryAllocationFailed = false; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(denoiseNestedLevels) if(denoiseNestedLevels>1) #endif { float *buffer[3]; buffer[0] = new (std::nothrow) float[maxWL*maxHL+32]; buffer[1] = new (std::nothrow) float[maxWL*maxHL+64]; buffer[2] = new (std::nothrow) float[maxWL*maxHL+96]; if(buffer[0] == NULL || buffer[1] == NULL || buffer[2] == NULL) { memoryAllocationFailed = true; } if(!memoryAllocationFailed) { #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int lvl=0; lvl=0;i--) if(buffer[i] != NULL) delete [] buffer[i]; } return (!memoryAllocationFailed); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SSEFUNCTION void ImProcFunctions::ShrinkAllL(wavelet_decomposition &WaveletCoeffs_L, float **buffer, int level, int dir, float *noisevarlum, float * madL, float * vari, int edge ) { //simple wavelet shrinkage const float eps = 0.01f; float * sfave = buffer[0]+32; float * sfaved = buffer[1]+64; float * blurBuffer = buffer[2]+96; int W_L = WaveletCoeffs_L.level_W(level); int H_L = WaveletCoeffs_L.level_H(level); float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level); float mad_L = madL[dir-1] ; if(edge==1) { noisevarlum = blurBuffer; // we need one buffer, but fortunately we don't have to allocate a new one because we can use blurBuffer for (int i=0; i0.001f) { madab = useNoiseCCurve ? madab : madab * noisevar_ab; #ifdef __SSE2__ __m128 onev = _mm_set1_ps(1.f); __m128 mad_abrv = _mm_set1_ps(madab); __m128 rmadLm9v = onev / _mm_set1_ps(mad_L * 9.f); __m128 mad_abv ; __m128 mag_Lv, mag_abv; int coeffloc_ab; for (coeffloc_ab=0; coeffloc_ab -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++; } lume += noisevarlum[i][j]; nL++; devL += SQR(noisevarlum[i][j]-(lume/nL)); } } if(nc>0) { 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; } const float reduc = (schoice == 2) ? (float) settings->nrhigh : 1.f; for (int dir=1; dir<4; dir++) { float mada, madb; if(!denoiseMethodRgb) mada = SQR(Mad(WavCoeffs_a[dir], W_ab*H_ab)); else mada = SQR(MadRgb(WavCoeffs_a[dir], W_ab*H_ab)); chred += mada; if(mada > maxchred) maxchred = mada; if(mada < minchred) minchred = mada; maxredaut = sqrt(reduc*maxchred); minredaut = sqrt(reduc*minchred); if(!denoiseMethodRgb) madb = SQR(Mad(WavCoeffs_b[dir], W_ab*H_ab)); else madb = SQR(MadRgb(WavCoeffs_b[dir], W_ab*H_ab)); chblue += madb; if(madb > maxchblue) maxchblue = madb; if(madb < minchblue) minchblue = madb; maxblueaut = sqrt(reduc*maxchblue); minblueaut = sqrt(reduc*minchblue); chau += (mada+madb); nb++; //here evaluation of automatic chaut = sqrt(reduc*chau/(nb + nb)); redaut = sqrt(reduc*chred/nb); blueaut = sqrt(reduc*chblue/nb); Nb = nb; } } void ImProcFunctions::WaveletDenoiseAll_info(int levwav, wavelet_decomposition &WaveletCoeffs_a, wavelet_decomposition &WaveletCoeffs_b, float **noisevarlum, float **noisevarchrom, float **noisevarhue, int width, int height, float noisevar_abr, float noisevar_abb, LabImage * noi, 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, bool denoiseMethodRgb, bool multiThread ){ int maxlvl = levwav; 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 denoiseMethodRgb = (dnparams.dmethod=="RGB"); if(denoiseMethodRgb) { 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) { 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; } } } SSEFUNCTION void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat * provicalc, const bool isRAW, LUTf &gamcurve, float gam, float gamthresh, float gamslope, const procparams::DirPyrDenoiseParams & dnparams, const double expcomp, 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, bool multiThread) { if ((settings->leveldnautsimpl==1 && dnparams.Cmethod == "MAN") || (settings->leveldnautsimpl==0 && dnparams.C2method == "MANU")) { //nothing to do return; } int hei,wid; float** lumcalc; float** acalc; float** bcalc; hei=provicalc->height; wid=provicalc->width; TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); const float wpi[3][3] = { {static_cast(wprofi[0][0]),static_cast(wprofi[0][1]),static_cast(wprofi[0][2])}, {static_cast(wprofi[1][0]),static_cast(wprofi[1][1]),static_cast(wprofi[1][2])}, {static_cast(wprofi[2][0]),static_cast(wprofi[2][1]),static_cast(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 int imheight=src->height, imwidth=src->width; bool denoiseMethodRgb = (dnparams.dmethod == "RGB"); const float gain = pow (2.0f, float(expcomp)); 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); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); const float wp[3][3] = { {static_cast(wprof[0][0]),static_cast(wprof[0][1]),static_cast(wprof[0][2])}, {static_cast(wprof[1][0]),static_cast(wprof[1][1]),static_cast(wprof[1][2])}, {static_cast(wprof[2][0]),static_cast(wprof[2][1]),static_cast(wprof[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; int comptlevel=0; // To avoid branches in loops we access the gammatabs by pointers LUTf *denoiseigamtab; switch(settings->denoiselabgamma) { case 0: denoiseigamtab = &(Color::igammatab_26_11); break; case 1: denoiseigamtab = &(Color::igammatab_4); break; default: denoiseigamtab = &(Color::igammatab_55); break; } for (int tiletop=0; tiletop 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 #ifdef _RT_NESTED_OPENMP #pragma omp parallel for if(multiThread) #endif for (int i=tiletop; i>1][j>>1]); bNv = LVFU(bcalc[i>>1][j>>1]); _mm_storeu_ps(&noisevarhue[i1>>1][j1>>1], xatan2f(bNv,aNv)); _mm_storeu_ps(&noisevarchrom[i1>>1][j1>>1], _mm_max_ps(c100v,_mm_sqrt_ps(SQRV(aNv)+SQRV(bNv)))); } for (; j>1][j>>1]; float bN = bcalc[i>>1][j>>1]; float cN = sqrtf(SQR(aN)+SQR(bN)); noisevarhue[i1>>1][j1>>1] = xatan2f(bN,aN); if(cN < 100.f) cN=100.f;//avoid divided by zero noisevarchrom[i1>>1][j1>>1] = cN; } #else for (int j=tileleft; j>1][j>>1]; float bN = bcalc[i>>1][j>>1]; float cN = sqrtf(SQR(aN)+SQR(bN)); float hN = xatan2f(bN,aN); if(cN < 100.f) cN = 100.f;//avoid divided by zero noisevarchrom[i1>>1][j1>>1] = cN; noisevarhue[i1>>1][j1>>1] = hN; } #endif } #ifdef _RT_NESTED_OPENMP #pragma omp parallel for if(multiThread) #endif for (int i=tiletop; i>1][j>>1]; Llum = Llum < 2.f ? 2.f : Llum; //avoid divided by zero ? Llum = Llum > 32768.f ? 32768.f : Llum; // not strictly necessary noisevarlum[i1>>1][j1>>1]= Llum; } } if (!denoiseMethodRgb){//lab mode, modification Jacques feb 2013 and july 2014 #ifdef _RT_NESTED_OPENMP #pragma omp parallel for if(multiThread) #endif for (int i=tiletop; ir(i,j); float G_ = gain*src->g(i,j); float B_ = gain*src->b(i,j); R_ = (*denoiseigamtab)[R_]; G_ = (*denoiseigamtab)[G_]; B_ = (*denoiseigamtab)[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 X,Y,Z; Color::rgbxyz(R_,G_,B_,X,Y,Z,wp); //convert to Lab float L,a,b; Color::XYZ2Lab(X, Y, Z, L, a, b); labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; } } } 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); 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); labdn->a[i1][j1] = (X-Y); labdn->b[i1][j1] = (Y-Z); } } } } 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); float X,Y,Z; Color::rgbxyz(rtmp,gtmp,btmp,X,Y,Z,wp); //convert Lab Color::XYZ2Lab(X, Y, Z, L, a, b); if(((i1|j1)&1) == 0) { float Llum,alum,blum; float XL,YL,ZL; Color::rgbxyz(rLum,gLum,bLum,XL,YL,ZL,wp); Color::XYZ2Lab(XL, YL, ZL, Llum, alum, blum); float kN=Llum; if(kN<2.f) kN=2.f; if(kN>32768.f) kN=32768.f; noisevarlum[i1>>1][j1>>1]=kN; float aN=alum; float bN=blum; float hN=xatan2f(bN,aN); float cN=sqrt(SQR(aN)+SQR(bN)); if(cN < 100.f) cN=100.f;//avoid divided by zero noisevarchrom[i1>>1][j1>>1]=cN; noisevarhue[i1>>1][j1>>1]=hN; } labdn->a[i1][j1] = a; labdn->b[i1][j1] = b; } } } 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; wavelet_decomposition* adecomp; wavelet_decomposition* bdecomp; int schoice = 0;//shrink method if (dnparams.smethod == "shalbi") schoice=2; const int levwav=5; #ifdef _RT_NESTED_OPENMP #pragma omp parallel sections if(multiThread) #endif { #ifdef _RT_NESTED_OPENMP #pragma omp section #endif { adecomp = new wavelet_decomposition (labdn->data+datalen, labdn->W, labdn->H, levwav, 1 ); } #ifdef _RT_NESTED_OPENMP #pragma omp section #endif { bdecomp = new wavelet_decomposition (labdn->data+2*datalen, labdn->W, labdn->H, levwav, 1 ); } } bool autoch = dnparams.autochroma; if(comptlevel==0) WaveletDenoiseAll_info(levwav, *adecomp, *bdecomp, noisevarlum, noisevarchrom, noisevarhue, width, height, noisevarab_r, noisevarab_b, labdn, 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, denoiseMethodRgb, multiThread);//enhance mode comptlevel+=1; float chresid,chmaxredresid,chmaxblueresid; nresi=chresid; highresi=chresid + 0.66f*(max(chmaxredresid,chmaxblueresid) - chresid);//evaluate sigma delete adecomp; delete bdecomp; delete labdn; for (int i=0; i<(height+1)/2; i++) delete [] noisevarlum[i]; delete [] noisevarlum; for (int i=0; i<(height+1)/2; i++) delete [] noisevarchrom[i]; delete [] noisevarchrom; for (int i=0; i<(height+1)/2; i++) delete [] noisevarhue[i]; delete [] noisevarhue; }//end of tile row }//end of tile loop for (int i=0; i