//////////////////////////////////////////////////////////////// // // 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 "bilateral2.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 "sleef.c" #ifdef __SSE2__ #include "sleefsseavx.c" #endif #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 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. */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGB_denoise(Imagefloat * src, Imagefloat * dst, bool isRAW, const procparams::DirPyrDenoiseParams & dnparams, const procparams::DefringeParams & defringe, const double expcomp) { static MyMutex FftwMutex; MyMutex::MyLock lock(FftwMutex); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /*if (plistener) { plistener->setProgressStr ("Denoise..."); plistener->setProgress (0.0); }*/ // volatile double progress = 0.0; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% const short int imheight=src->height, imwidth=src->width; if (dnparams.luma==0 && dnparams.chroma==0) { //nothing to do; copy src to dst memcpy(dst->data,src->data,dst->width*dst->height*3*sizeof(float)); return; } perf=false; if(dnparams.dmethod=="RGB") perf=true;//RGB mode //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // 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, expcomp); float incr=1.f; float noisevar_Ldetail = SQR((SQR(100.f-dnparams.Ldetail) + 50.f*(100.f-dnparams.Ldetail)) * TS * 0.5f * incr); noisered=1.f;//chroma red if(dnparams.redchro<-0.1f) {noisered=0.001f+SQR((100.f + dnparams.redchro)/100.0f);} else if(dnparams.redchro>0.1f) {noisered=1.f+SQR((dnparams.redchro));} else if (dnparams.redchro>= -0.1f && dnparams.redchro<=0.1f) noisered=0.f; noiseblue=1.f;//chroma blue if(dnparams.bluechro<-0.1f) {noiseblue=0.001f+SQR((100.f + dnparams.bluechro)/100.0f);} else if(dnparams.bluechro>0.1f) {noiseblue=1.f+SQR((dnparams.bluechro));} else if (dnparams.bluechro>= -0.1f && dnparams.bluechro<=0.1f) noiseblue=0.f; 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; const int tilesize = 1024; const int overlap = 128; int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; if (imwidth 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 { //DCT block data storage float * Lblox; float * fLblox; 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]} }; #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)); } #ifdef _OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int tiletop=0; tiletop Lin(width,height); //wavelet denoised image LabImage * labdn = new LabImage(width,height); //residual between input and denoised L channel array2D Ldetail(width,height,ARRAY2D_CLEAR_DATA); //pixel weight array2D totwt(width,height,ARRAY2D_CLEAR_DATA);//weight for combining DCT blocks // //#ifdef _OPENMP //#pragma omp parallel for //#endif //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 for (int i=tiletop/*, i1=0*/; ir(i,j); float G_ = gain*src->g(i,j); float B_ = gain*src->b(i,j); //modify arbitrary data for Lab..I have test : nothing, gamma standard, gamma SRGB and GammaBT709... //we can put other as gamma g=2.6 slope=11, etc. // Gamma sRGB is a good compromise, but noting to do with real gamma !!!: it's only for data Lab # data RGB //finally I opted fot gamma_26_11 R_ = Color::igammatab_26_11[R_]; G_ = Color::igammatab_26_11[G_]; B_ = Color::igammatab_26_11[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); 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); 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->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*/; i 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; // Ldetail[i1][j1] = 0; Lin[i1][j1] = L; // totwt[i1][j1] = 0; } } } //initial impulse denoise if (dnparams.luma>0.01) { impulse_nr (labdn, MIN(50.0f,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 = SQR((dnparams.luma/125.0f)*(1+ dnparams.luma/25.0f)); float noisevarab = SQR(dnparams.chroma/10.0f); { // 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 levwav=5; // if(xxxx) levwav=7; 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 ); //WaveletDenoiseAll_BiShrink(Ldecomp, adecomp, bdecomp, noisevarL, noisevarab); WaveletDenoiseAll(*Ldecomp, *adecomp, *bdecomp, noisevarL, noisevarab, labdn);//mod JD 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 if (dnparams.luma>0.01) { impulse_nr (labdn, MIN(50.0f,dnparams.luma)/20.0f); } //PF_correct_RT(dst, dst, defringe.radius, defringe.threshold); //wavelet denoised L channel array2D 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 //OpenMP here //adding omp here leads to artifacts 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]; //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) r_ = Color::gammatab_26_11[r_]; g_ = Color::gammatab_26_11[g_]; b_ = Color::gammatab_26_11[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; iL[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 { #ifdef _OPENMP //#pragma omp parallel for #endif for (int i=tiletop; iL[i1][j1]; a = labdn->a[i1][j1]; b = labdn->b[i1][j1]; 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; delete[] Vmask; delete[] Hmask; }//end of tile row }//end of tile loop #ifdef _OPENMP #pragma omp critical #endif { fftwf_free ( Lblox); fftwf_free ( fLblox); } } //copy denoised image to output memcpy (dst->data, 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(); }//end of main RGB_denoise //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #if defined( __SSE2__ ) && defined( WIN32 ) __attribute__((force_align_arg_pointer)) void ImProcFunctions::RGBtile_denoise (float * fLblox, int vblproc, int hblproc, int numblox_H, int numblox_W, float noisevar_Ldetail ) //for DCT #else void ImProcFunctions::RGBtile_denoise (float * fLblox, int vblproc, int hblproc, int numblox_H, int numblox_W, float noisevar_Ldetail ) //for DCT #endif { float * nbrwt = new float[TS*TS]; //for DCT int blkstart = hblproc*TS*TS; boxabsblur(fLblox+blkstart, nbrwt, 3, 3, TS, TS);//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; n=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); for (int dir=1; dir<4; dir++) { float mad_L = madL[lvl][dir-1]; float mad_a = noisevar_ab*mada[lvl][dir-1]; float mad_b = noisevar_ab*madb[lvl][dir-1]; //float mad_Lpar = madL[lvl+1][dir-1]; //float mad_apar = mada[lvl+1][dir-1]; //float mad_bpar = mada[lvl+1][dir-1]; //float skip_ab_ratio = WaveletCoeffs_a.level_stride(lvl+1)/skip_ab; float skip_L_ratio = WaveletCoeffs_L.level_stride(lvl+1)/skip_L; if (noisevar_ab>0.01) { //printf(" dir=%d mad_L=%f mad_a=%f mad_b=%f \n",dir,sqrt(mad_L),sqrt(mad_a),sqrt(mad_b)); //OpenMP here 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-xexpf(-(mag_a/mad_a)-(mag_L/(9*mad_L)))/*satfactor_a*/); WavCoeffs_b[dir][coeffloc_ab] *= SQR(1-xexpf(-(mag_b/mad_b)-(mag_L/(9*mad_L)))/*satfactor_b*/); } }//now chrominance coefficients are denoised } if (noisevar_L>0.01) { mad_L *= noisevar_L*5/(lvl+1); //OpenMP here for (int i=0; i (edge, edge, buffer, Wlvl_L, Hlvl_L, 1<<(lvl+1), false /*multiThread*/); //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 //OpenMP here for (int i=0; i0.01) { //OpenMP here #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; ib[2*i][2*j],noi->a[2*i][2*j]); //one can also use L or c (chromaticity) if necessary if(hh > -0.4f && hh < 1.6f) reduc=noisered;//red from purple to next yellow if(hh>-2.45f && hh <=-0.4f) bluuc=noiseblue;//blue } mad_a*=reduc; mad_a*=bluuc; mad_b*=reduc; mad_b*=bluuc; float mag_L = SQR(WavCoeffs_L[dir][coeffloc_L ])+eps; float mag_a = SQR(WavCoeffs_a[dir][coeffloc_ab])+eps; float mag_b = SQR(WavCoeffs_b[dir][coeffloc_ab])+eps; sfavea[coeffloc_ab] = (1-xexpf(-(mag_a/mad_a)-(mag_L/(9*madL)))); sfaveb[coeffloc_ab] = (1-xexpf(-(mag_b/mad_b)-(mag_L/(9*madL)))); mad_a=m_a; mad_b=m_b; // 'firm' threshold of chroma coefficients //WavCoeffs_a[dir][coeffloc_ab] *= (1-exp(-(mag_a/mad_a)-(mag_L/(9*madL))));//(coeff_a>2*thresh_a ? 1 : (coeff_a2*thresh_b ? 1 : (coeff_bb[2*i][2*j],noi->a[2*i][2*j]); if(hh > -0.4f && hh < 1.6f) reduc=noisered; if(hh>-2.45f && hh <=-0.4f) bluuc=noiseblue; } mad_a*=reduc; mad_a*=bluuc; mad_b*=reduc; mad_b*=bluuc; float mag_L = SQR(WavCoeffs_L[dir][coeffloc_L ])+eps; float mag_a = SQR(WavCoeffs_a[dir][coeffloc_ab])+eps; float mag_b = SQR(WavCoeffs_b[dir][coeffloc_ab])+eps; float sfa = (1-xexpf(-(mag_a/mad_a)-(mag_L/(9*madL)))); float sfb = (1-xexpf(-(mag_b/mad_b)-(mag_L/(9*madL)))); //use smoothed shrinkage unless local shrinkage is much less WavCoeffs_a[dir][coeffloc_ab] *= (SQR(sfavea[coeffloc_ab])+SQR(sfa))/(sfavea[coeffloc_ab]+sfa+eps); WavCoeffs_b[dir][coeffloc_ab] *= (SQR(sfaveb[coeffloc_ab])+SQR(sfb))/(sfaveb[coeffloc_ab]+sfb+eps); mad_a=m_a; mad_b=m_b; }//now chrominance coefficients are denoised } if (noisevar_L>0.01) { #ifdef __SSE2__ __m128 magv; __m128 mad_Lv = _mm_set1_ps( mad_L ); __m128 ninev = _mm_set1_ps( 9.0f ); __m128 epsv = _mm_set1_ps( eps ); for (int i=0; i