//////////////////////////////////////////////////////////////// // // CFA chroma denoise by FT filtering // // copyright (c) 2008-2012 Emil Martinec // // // code dated: December 24, 2011 // // FTblockDNchroma.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 "bilateral2.h" //#include "gauss.h" #include "rtengine.h" #include "improcfun.h" #include "LUT.h" #include "array2D.h" #include "iccmatrices.h" #ifdef _OPENMP #include #endif #include "EdgePreserveLab.h" #include "cplx_wavelet_dec.h" #define SQR(x) ((x)*(x)) //#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 256 // Tile size #define offset TS/2 // shift between tiles #define fTS ((TS/2+1)) // shift between Fourier tiles //#define eps 0.01f/(TS*TS) //tolerance namespace rtengine { // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* Structure of the algorithm: 1. Compute a high pass filter of the image via bilateral filter with user input range 2. Decompose the image into TSxTS size tiles, shifting by 'offset' each step (so roughly each pixel is in (TS/offset)^2 tiles); Fourier transform the tiles after applying a mask to prevent long range tails in the FT data due to boundary discontinuities. 3. Compute the average size of Fourier coefficients. 4. Damp the FT data of the tile by a Wiener filter factor (image_variance)/(image_variance + noise_control) where noise_control is the user specified noise reduction amount. Noise_control is altered according to neighbor average. 6. Inverse FT the denoised tile data and combine the tiles into a denoised output image. */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGB_InputTransf(Imagefloat * src, LabImage * dst, LabImage * blur, const procparams::DirPyrDenoiseParams & dnparams, const procparams::DefringeParams & defringe) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // gamma transform input channel data float gam = dnparams.gamma; float gamthresh = 0.03; float gamslope = exp(log((double)gamthresh)/gam)/gamthresh; float gam3slope = exp(log((double)gamthresh)/3.0f)/gamthresh; LUTf gamcurve(65536,0); for (int i=0; i<65536; i++) { gamcurve[i] = (gamma((double)i/65535.0, gam, gamthresh, gamslope, 1.0, 0.0));// * 32768.0f; } #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; iheight; i++) for (int j=0; jwidth; j++) { float X = xyz_prophoto[0][0]*src->r[i][j] + xyz_prophoto[0][1]*src->g[i][j] + xyz_prophoto[0][2]*src->b[i][j]; float Y = xyz_prophoto[1][0]*src->r[i][j] + xyz_prophoto[1][1]*src->g[i][j] + xyz_prophoto[1][2]*src->b[i][j]; float Z = xyz_prophoto[2][0]*src->r[i][j] + xyz_prophoto[2][1]*src->g[i][j] + xyz_prophoto[2][2]*src->b[i][j]; X = gamcurve[X]; Y = gamcurve[Y]; Z = gamcurve[Z]; dst->L[i][j] = Y; dst->a[i][j] = 0.2f*(X-Y); dst->b[i][j] = 0.2f*(Y-Z); } //pre-blur L channel to mitigate artifacts /*for (int i=1; iheight-1; i++) for (int j=1; jwidth-1; j++) { dst->L[i][j] = 0.25*(dst->L[i][j]+0.5*(dst->L[i-1][j]+dst->L[i+1][j]+dst->L[i][j-1]+dst->L[i][j+1]) +0.25*(dst->L[i-1][j-1]+dst->L[i-1][j+1]+dst->L[i+1][j-1]+dst->L[i+1][j+1])); }*/ //cplx_wavelet_decomposition Ldecomp(dst->data, dst->W, dst->H, 2 /*maxlvl*/); impulse_nr (dst, 50.0f/20.0f); //PF_correct_RT(dst, dst, defringe.radius, defringe.threshold); //blur the image //dirpyr_ab(dst, blur, dnparams);//use dirpyr here if using it to blur all channels EPDParams *p = (EPDParams *)(¶ms->edgePreservingDecompositionUI); float EdgeStop = (pow(0.15f, 1.0f/dnparams.gamma)/0.15f) * (float)dnparams.Lamt/10.0f;//compensate edgestopping power according to gamma EdgePreserveLab epd = EdgePreserveLab(dst->W, dst->H); blur->data = epd.CreateIteratedBlur(dst->data /*Source*/, (float)p->Scale /*LScale*/, (float)p->EdgeStopping /*abScale*/, \ EdgeStop /*(float)dnparams.Lamt/10.0f*/ /*EdgeStoppingLuma*/, (float)dnparams.chroma/25.0f/*p->EdgeStopping*/ /*EdgeStoppingChroma*/, \ 5/*Iterates*/, 0/*Reweightings*/, blur->data/*Blur*/); //impulsedenoise (blur); /* #ifdef _OPENMP #pragma omp parallel #endif { AlignedBuffer* buffer = new AlignedBuffer (MAX(dst->W,dst->H)); //gaussHorizontal (src->L, tmp1->L, buffer, src->W, src->H, radius, multiThread); //gaussHorizontal (src->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); //gaussVertical (tmp1->a, tmp1->a, buffer, src->W, src->H, radius, multiThread); //gaussVertical (tmp1->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); gaussHorizontal (dst->L, blur->L, buffer, dst->W, dst->H, (double)dnparams.Lamt/25.0f, multiThread); gaussVertical (blur->L, blur->L, buffer, dst->W, dst->H, (double)dnparams.Lamt/25.0f, multiThread); //gaussHorizontal (blur->L, blur->L, buffer, dst->W, dst->H, (double)dnparams.Lamt/25.0f, multiThread); //gaussVertical (blur->L, blur->L, buffer, dst->W, dst->H, (double)dnparams.Lamt/25.0f, multiThread); delete buffer; } */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // gamma transform input channel data /*gam = dnparams.gamma/3.0f; gamthresh = 0.03; gamslope = exp(log((double)gamthresh)/gam)/gamthresh; gam3slope = exp(log((double)gamthresh)/3.0f)/gamthresh; for (int i=0; i<65536; i++) { gamcurve[i] = (gamma((double)i/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)) * 65535.0f; }*/ #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; iheight; i++) { for (int j=0; jwidth; j++) { //float wt = expf(-100.0f*fabs(dst->L[i][j]-blur->L[i][j])/((float)dnparams.luma)); //blur->L[i][j] = wt*dst->L[i][j] + (1-wt)*blur->L[i][j]; blur->a[i][j] /*= 32768.0f*0.2f*(blur->a[i][j]-blur->L[i][j]);/*/*= 32768.0f; blur->b[i][j] /*= 32768.0f*0.2f*(blur->L[i][j]-blur->b[i][j]);/*/*= 32768.0f; blur->L[i][j] *= 32768.0f;//= gamcurve[32768.0f*blur->L[i][j]]; dst->L[i][j] *= 32768.0f;//= gamcurve[32768.0f*dst->L[i][j]]; dst->a[i][j] *= 32768.0f; dst->b[i][j] *= 32768.0f; } } //dirpyr_ab(blur, blur, dnparams);//use dirpyr here if using it to blur ab channels only //dirpyrLab_denoise(blur, blur, dnparams);//use dirpyr here if using it to blur ab channels only } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGB_OutputTransf(LabImage * src, Imagefloat * dst, const procparams::DirPyrDenoiseParams & dnparams) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // gamma transform output channel data float gam = dnparams.gamma; float gamthresh = 0.03; float gamslope = exp(log((double)gamthresh)/gam)/gamthresh; float igam = 1/gam; float igamthresh = gamthresh*gamslope; float igamslope = 1/gamslope; float gamL = dnparams.gamma/3.0f; float gamslopeL = exp(log((double)gamthresh)/gamL)/gamthresh; float igamL = 1/gamL; float igamthreshL = gamthresh*gamslopeL; float igamslopeL = 1/gamslopeL; LUTf gamcurve(65536,0); LUTf igamcurve(65536,0); LUTf igamcurveL(65536,0); for (int i=0; i<65536; i++) { igamcurve[i] = (gamma((float)i/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); /*float L = (gamma((float)i/65535.0f, igamL, igamthreshL, igamslopeL, 1.0, 0.0) * 200.0f); float fy = (0.00862069 * L) + 0.137932; // (L+16)/116 float Y = f2xyz(fy); igamcurveL[i] = (gamma(Y, gam, gamthresh, gamslope, 1.0, 0.0)) * 32768.0f;*/ } #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; iH; i++) { float X,Y,Z; for (int j=0; jW; j++) { //input normalized to (0,1) //Y = igamcurveL[ src->L[i][j] ]; Y = src->L[i][j]; X = (5.0f*(src->a[i][j])) + Y; Z = Y - (5.0f*(src->b[i][j])); X = igamcurve[X]; Y = igamcurve[Y]; Z = igamcurve[Z]; dst->r[i][j] = prophoto_xyz[0][0]*X + prophoto_xyz[0][1]*Y + prophoto_xyz[0][2]*Z; dst->g[i][j] = prophoto_xyz[1][0]*X + prophoto_xyz[1][1]*Y + prophoto_xyz[1][2]*Z; dst->b[i][j] = prophoto_xyz[2][0]*X + prophoto_xyz[2][1]*Y + prophoto_xyz[2][2]*Z; } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGB_denoise(Imagefloat * src, Imagefloat * dst, /*int Roffset,*/ const procparams::DirPyrDenoiseParams & dnparams, const procparams::DefringeParams & defringe) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /*if (plistener) { plistener->setProgressStr ("Block FT Luma Denoise..."); plistener->setProgress (0.0); }*/ volatile double progress = 0.0; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% const short int height=src->height, width=src->width; const short int hfh=(height+1)/2, hfw=(width+1)/2; //const short int hfh=(height+1), hfw=(width+1); const int blkrad=1; float noisevar_L = SQR(dnparams.luma * TS * 40.0f); float noisevar_ab = SQR(dnparams.chroma * TS * 200.0f); //int dxr=Roffset&1, dyr=(Roffset&2)/2, dxb=(1-dxr), dyb=(1-dyr); //int rdx, rdy, bdx, bdy; // calculation for tiling 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 //const float eps = 1.0f; array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); array2D totwt(width,height,ARRAY2D_CLEAR_DATA); for (int i=0; iTS/2 ? i-TS : i)); float vmask = (i1<8 ? SQR(sin(M_PI*i1/16.0f)) : 1.0f); for (int j=0; jTS/2 ? j-TS : j)); //tilemask_in[i][j] = vmask * (j1<4 ? SQR(sin(M_PI*(float)j1/8.0f)) : 1.0f); tilemask_in[i][j] = (exp(-(SQR(i-TS/2-0.5f)+SQR(j-TS/2-0.5f))/(2*SQR(TS/4.0f))) * \ vmask * (j1<8 ? SQR(sin(M_PI*(float)j1/16.0f)) : 1.0f)); tilemask_out[i][j] = (SQR(MAX(0.0f, sin(M_PI*(float)(i-4.0f)/(TS-9) ))) * \ SQR(MAX(0.0f, sin(M_PI*(float)(j-4.0f)/(TS-9) )))); //tilemask_out[i][j] = exp(-(SQR(i-TS/2-0.5f)+SQR(j-TS/2-0.5f))/(SQR(TS/4.0f))); } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LabImage * labin = new LabImage(width,height); LabImage * labblur = new LabImage(width,height); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform RGB input to ersatz Lab RGB_InputTransf(src, labin, labblur, dnparams, defringe); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // initialize FT and output data structures LabImage * labdn = new LabImage(width,height); float ** Lblox = new float *[8] ; float ** RLblox = new float *[8] ; float ** BLblox = new float *[8] ; fftwf_complex ** fLblox = new fftwf_complex *[8] ; //for FT //float ** fLblox = new float *[8] ; //for DCT fftwf_complex ** fRLblox = new fftwf_complex *[8] ; fftwf_complex ** fBLblox = new fftwf_complex *[8] ; for( int i = 0 ; i < 8 ; i++ ) { Lblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); //RLblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); //BLblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); fLblox[i] = (fftwf_complex *) fftwf_malloc (numblox_W*TS*fTS * sizeof (fftwf_complex)); //for FT //fLblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); //for DCT //fRLblox[i] = (fftwf_complex *) fftwf_malloc (numblox_W*TS*fTS * sizeof (fftwf_complex)); //fBLblox[i] = (fftwf_complex *) fftwf_malloc (numblox_W*TS*fTS * sizeof (fftwf_complex)); } //make a plan for FFTW fftwf_plan plan_forward_blox, plan_backward_blox; int nfwd[2]={TS,TS}; //for FT: plan_forward_blox = fftwf_plan_many_dft_r2c(2, nfwd, numblox_W, Lblox[0], NULL, 1, TS*TS, \ fLblox[0], NULL, 1, TS*fTS, FFTW_ESTIMATE ); plan_backward_blox = fftwf_plan_many_dft_c2r(2, nfwd, numblox_W, fLblox[0], NULL, 1, TS*fTS, \ Lblox[0], NULL, 1, TS*TS, FFTW_ESTIMATE ); //for DCT: /*const fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; const fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; plan_forward_blox = fftwf_plan_many_r2r(2, nfwd, numblox_W, Lblox[0], NULL, 1, TS*TS, \ fLblox[0], NULL, 1, TS*TS, fwdkind, FFTW_ESTIMATE ); plan_backward_blox = fftwf_plan_many_r2r(2, nfwd, numblox_W, fLblox[0], NULL, 1, TS*TS, \ Lblox[0], NULL, 1, TS*TS, bwdkind, FFTW_ESTIMATE );*/ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Main algorithm: Tile loop #pragma omp parallel for schedule(dynamic) //int vblock=0, hblock=0; for (int vblk=0; vblkL[top+i][left+j]-labblur->L[top+i][left+j]);// luma data //RLblox[vblkmod][(indx + i)*TS+j] = tilemask_in[i][j]*(labin->a[top+i][left+j]-labblur->a[top+i][left+j]);// high pass chroma data //BLblox[vblkmod][(indx + i)*TS+j] = tilemask_in[i][j]*(labin->b[top+i][left+j]-labblur->b[top+i][left+j]);// high pass chroma data totwt[top+i][left+j] += tilemask_in[i][j]*tilemask_out[i][j]; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //pad the image to size TS on both sides if (imin>0) { for (int i=0; iL[2*imin-i-1][left+j]-labblur->L[2*imin-i-1][left+j]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[2*imin-i-1][left+j]-labblur->a[2*imin-i-1][left+j]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[2*imin-i-1][left+j]-labblur->b[2*imin-i-1][left+j]); } if (jmin>0) { for (int i=0; iL[2*imin-i-1][2*jmin-j-1]-labblur->L[2*imin-i-1][2*jmin-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[2*imin-i-1][2*jmin-j-1]-labblur->a[2*imin-i-1][2*jmin-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[2*imin-i-1][2*jmin-j-1]-labblur->b[2*imin-i-1][2*jmin-j-1]); } } } if (imaxL[height+imax-i-1][left+j]-labblur->L[height+imax-i-1][left+j]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[height+imax-i-1][left+j]-labblur->a[height+imax-i-1][left+j]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[height+imax-i-1][left+j]-labblur->b[height+imax-i-1][left+j]); } if (jmaxL[height+imax-i-1][width+jmax-j-1]-labblur->L[height+imax-i-1][width+jmax-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[height+imax-i-1][width+jmax-j-1]-labblur->a[height+imax-i-1][width+jmax-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[height+imax-i-1][width+jmax-j-1]-labblur->b[height+imax-i-1][width+jmax-j-1]); } } } if (jmin>0) { for (int j=0; jL[top+i][2*jmin-j-1]-labblur->L[top+i][2*jmin-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[top+i][2*jmin-j-1]-labblur->a[top+i][2*jmin-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[top+i][2*jmin-j-1]-labblur->b[top+i][2*jmin-j-1]); } if (imaxL[height+imax-i-1][2*jmin-j-1]-labblur->L[height+imax-i-1][2*jmin-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[height+imax-i-1][2*jmin-j-1]-labblur->a[height+imax-i-1][2*jmin-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[height+imax-i-1][2*jmin-j-1]-labblur->b[height+imax-i-1][2*jmin-j-1]); } } } if (jmaxL[top+i][width+jmax-j-1]-labblur->L[top+i][width+jmax-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[top+i][width+jmax-j-1]-labblur->a[top+i][width+jmax-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[top+i][width+jmax-j-1]-labblur->b[top+i][width+jmax-j-1]); } if (imin>0) { for (int i=0; iL[2*imin-i-1][width+jmax-j-1]-labblur->L[2*imin-i-1][width+jmax-j-1]); //RLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->a[2*imin-i-1][width+jmax-j-1]-labblur->a[2*imin-i-1][width+jmax-j-1]); //BLblox[vblkmod][(indx + i)*TS+j]=tilemask_in[i][j]*(labin->b[2*imin-i-1][width+jmax-j-1]-labblur->b[2*imin-i-1][width+jmax-j-1]); } } } //end of tile padding //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }//end of filling block row //fftwf_print_plan (plan_forward_blox); fftwf_execute_dft_r2c(plan_forward_blox,Lblox[vblkmod],fLblox[vblkmod]); // FT an entire row of tiles //fftwf_execute_r2r(plan_forward_blox,Lblox[vblkmod],fLblox[vblkmod]); // DCT an entire row of tiles //fftwf_execute_dft_r2c(plan_forward_blox,RLblox[vblkmod],fRLblox[vblkmod]);// FT an entire row of tiles //fftwf_execute_dft_r2c(plan_forward_blox,BLblox[vblkmod],fBLblox[vblkmod]);// FT an entire row of tiles if (vblkL[i][j]-labblur->L[i][j]; float hpdn = labdn->L[i][j]/totwt[i][j];//note that labdn initially stores the denoised hipass data float hgratio = 0;//MIN(1.0f,(abs(hpdn)+eps)/(abs(hporig)+eps)); labdn->L[i][j] = labblur->L[i][j] + (hgratio*hporig+(1-hgratio)*hpdn); //labdn->L[i][j] = -(hporig)+0.25; hporig = labin->a[i][j]-labblur->a[i][j]; hpdn = labdn->a[i][j]/totwt[i][j]; labdn->a[i][j] = labblur->a[i][j] ;//+ (hgratio*hporig+(1-hgratio)*hpdn); //labdn->a[i][j] = (hporig); hporig = labin->b[i][j]-labblur->b[i][j]; hpdn = labdn->b[i][j]/totwt[i][j]; labdn->b[i][j] = labblur->b[i][j] ;//+ (hgratio*hporig+(1-hgratio)*hpdn); //labdn->b[i][j] = (hporig); //labdn->a[i][j] = labin->a[i][j]; //labdn->b[i][j] = labin->b[i][j]; } } //dirpyr_ab(labdn, labdn, dnparams);//use dirpyr here if using it to blur ab channels only dirpyrdenoise(labdn);//denoise ab channels using ImProcFns denoise (stripped to ab channels only) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform denoised "Lab" to output RGB RGB_OutputTransf(labdn, dst, dnparams); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% delete labin; delete labdn; delete labblur; }//end of main RB_denoise //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGBtile_denoise (fftwf_complex ** fLblox, fftwf_complex ** fRLblox, fftwf_complex ** fBLblox, \ int vblproc, int hblproc, int blkrad, int numblox_H, int numblox_W, \ float noisevar_L, float noisevar_ab ) //for FT //void ImProcFunctions::RGBtile_denoise (float ** fLblox, fftwf_complex ** fRLblox, fftwf_complex ** fBLblox, \ int vblproc, int hblproc, int blkrad, int numblox_H, int numblox_W, \ float noisevar_L, float noisevar_ab ) //for DCT { int vblprocmod=vblproc%8; float eps = 0.01f/(TS*TS); //tolerance float RLblockvar=eps, BLblockvar=eps, Lblockvar=eps; for (int i=TS/4; i<3*TS/4; i++) for (int j=TS/4; jL[top+i][left+j] += tilemask_out[i][j]*bloxrow_L[(indx + i)*TS+j]/(TS*TS); //labdn->a[top+i][left+j] += tilemask_out[i][j]*bloxrow_a[(indx + i)*TS+j]/(TS*TS); //labdn->b[top+i][left+j] += tilemask_out[i][j]*bloxrow_b[(indx + i)*TS+j]/(TS*TS); } } } #undef TS #undef fTS #undef offset #undef eps //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //experimental dirpyr low-pass filter void ImProcFunctions::dirpyr_ab(LabImage * data_fine, LabImage * data_coarse, const procparams::DirPyrDenoiseParams & dnparams) { int W = data_fine->W; int H = data_fine->H; float thresh_L = 10.0f*dnparams.luma; float threshsq_L = SQR(thresh_L); float thresh_ab = 10.0f*dnparams.chroma; float threshsq_ab = SQR(thresh_ab); LUTf rangefn_L(0x10000); LUTf rangefn_ab(0x10000); LabImage * dirpyrlo[2]; //set up range functions for (int i=0; i<0x10000; i++) { rangefn_L[i] = exp(-((float)i) / (1.0+thresh_L)) ;// * (1.0+thresh_L)/(((float)i) + thresh_L+1.0); rangefn_ab[i] = exp(-SQR((float)i) / (1.0+threshsq_ab)) ;// * (1.0+thresh_ab)/(((float)i) + thresh_ab+1.0); } dirpyrlo[0] = new LabImage (W, H); dirpyrlo[1] = new LabImage (W, H); //int scale[4]={1,3,5,9/*1*/}; int scale[5]={1,2,4,7,13/*1*/}; int level=0; int indx=0; dirpyr_ablevel(data_fine, dirpyrlo[indx], W, H, rangefn_L,rangefn_ab, 0, scale[level] ); level += 1; indx = 1-indx; while (level<3) { dirpyr_ablevel(dirpyrlo[1-indx], dirpyrlo[indx], W, H, rangefn_L,rangefn_ab, level, scale[level] ); level += 1; indx = 1-indx; } dirpyr_ablevel(dirpyrlo[1-indx], data_coarse, W, H, rangefn_L,rangefn_ab, level, scale[level] ); //delete dirpyrlo[0];//TODO: this seems to disable the NR ??? //delete dirpyrlo[1]; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::dirpyr_ablevel(LabImage * data_fine, LabImage * data_coarse, int width, int height, LUTf & rangefn_L, LUTf & rangefn_ab, int level, int scale) { //scale is spacing of directional averaging weights //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // calculate weights, compute directionally weighted average //int domker[5][5] = {{1,1,1,1,1},{1,2,2,2,1},{1,2,2,2,1},{1,2,2,2,1},{1,1,1,1,1}}; //int domker[5][5] = {{1, 2, 4, 2, 1}, {2, 4, 8, 4, 2}, {4, 8, 16, 8, 4}, {2, 4, 8, 4, 2}, {1, 2, 4, 2, 1}}; float domker[5][5] = {{0.129923f, 0.279288f, 0.360448f, 0.279288f, 0.129923f}, \ {0.279288f, 0.600373f, 0.774837f, 0.600373f, 0.279288f}, \ {0.360448f, 0.774837f, 1.0f, 0.774837f, 0.360448f}, \ {0.279288f, 0.600373f, 0.774837f, 0.600373f, 0.279288f}, \ {0.129923f, 0.279288f, 0.360448f, 0.279288f, 0.129923f}};//Gaussian with sigma=1.4 int scalewin = 2*scale; #ifdef _OPENMP #pragma omp parallel for #endif for(int i = 0; i < height; i++) { for(int j = 0; j < width; j++) { float valL=0, vala=0, valb=0; float norm_L=0, norm_ab=0; for(int inbr=MAX(0,(i-scalewin)); inbr<=MIN(height-1,(i+scalewin)); inbr+=scale) { for (int jnbr=MAX(0,(j-scalewin)); jnbr<=MIN(width-1,(j+scalewin)); jnbr+=scale) { //it seems that weighting the blur by L (gamma=3) works better //than using the variable gamma source //float desat = 1-rangefn_ab[data_fine->L[i][j]+abs(data_fine->a[i][j])+abs(data_fine->b[i][j])]; float nbrdiff_L = fabs(data_fine->L[inbr][jnbr]-data_fine->L[i][j])/level; float nbrdiff_ab = (fabs(data_fine->a[inbr][jnbr]-data_fine->a[i][j]) + \ fabs(data_fine->b[inbr][jnbr]-data_fine->b[i][j])); float dirwt_L = ( domker[(inbr-i)/scale+2][(jnbr-j)/scale+2] * rangefn_L[nbrdiff_L] ); float dirwt_ab = ( /*domker[(inbr-i)/scale+2][(jnbr-j)/scale+2] */ rangefn_ab[nbrdiff_ab] ); //valL += dirwt_L *data_fine->L[inbr][jnbr]; vala += dirwt_L*dirwt_ab*data_fine->a[inbr][jnbr]; valb += dirwt_L*dirwt_ab*data_fine->b[inbr][jnbr]; //norm_L += dirwt_L; norm_ab += dirwt_L*dirwt_ab; } } //data_coarse->L[i][j] = valL/norm_L; // low pass filter data_coarse->L[i][j] = data_fine->L[i][j]; data_coarse->a[i][j] = vala/norm_ab; // low pass filter data_coarse->b[i][j] = valb/norm_ab; // low pass filter /*if (level!=3) { data_coarse->L[i][j] = valL/norm_L; // low pass filter } else { float valL=0, vala=0, valb=0; float norm=0; for(int inbr=MAX(0,(i-2)); inbr<=MIN(height-1,(i+2)); inbr++) { for (int jnbr=MAX(0,(j-2)); jnbr<=MIN(width-1,(j+2)); jnbr++) { //it seems that weighting the blur by Lab luminance (~gamma=3) //works better than using the variable gamma source float nbrdiff = (fabs(data_fine->L[inbr][jnbr]-data_fine->L[i][j]) + \ fabs(data_fine->a[inbr][jnbr]-data_fine->a[i][j]) + \ fabs(data_fine->b[inbr][jnbr]-data_fine->b[i][j]))/(level); float dirwt = ( domker[(inbr-i)/scale+2][(jnbr-j)+2] * rangefn_L[nbrdiff] ); valL += dirwt*data_fine->L[inbr][jnbr]; vala += dirwt*data_fine->a[inbr][jnbr]; valb += dirwt*data_fine->b[inbr][jnbr]; norm += dirwt; } } data_coarse->L[i][j] = data_fine->L[i][j];//valL/norm; data_coarse->a[i][j] = vala/norm; data_coarse->b[i][j] = valb/norm; }*/ } } } /* void ImProcFunctions::FixImpulse_ab(LabImage * src, LabImage * dst, double radius, int thresh) { float threshsqr = SQR(thresh); int halfwin = ceil(2*radius)+1; // local variables int width=src->W, height=src->H; //temporary array to store chromaticity float *fringe = float * calloc ((height)*(width), sizeof *fringe); LabImage * tmp1; tmp1 = new LabImage(width, height); #ifdef _OPENMP #pragma omp parallel #endif { AlignedBuffer* buffer = new AlignedBuffer (MAX(src->W,src->H)); gaussHorizontal (src->a, tmp1->a, buffer, src->W, src->H, radius, multiThread); gaussHorizontal (src->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); gaussVertical (tmp1->a, tmp1->a, buffer, src->W, src->H, radius, multiThread); gaussVertical (tmp1->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); //gaussHorizontal (src->L, tmp1->L, buffer, src->W, src->H, radius, multiThread); //gaussVertical (tmp1->L, tmp1->L, buffer, src->W, src->H, radius, multiThread); delete buffer; } //#ifdef _OPENMP //#pragma omp parallel for //#endif float chromave=0; for(int i = 0; i < height; i++ ) { for(int j = 0; j < width; j++) { float chroma = SQR(src->a[i][j]-tmp1->a[i][j])+SQR(src->b[i][j]-tmp1->b[i][j]); chromave += chroma; fringe[i*width+j]=chroma; } } chromave /= (height*width); #ifdef _OPENMP #pragma omp parallel for #endif for(int i = 0; i < height; i++ ) { for(int j = 0; j < width; j++) { tmp1->a[i][j] = src->a[i][j]; tmp1->b[i][j] = src->b[i][j]; if (33*fringe[i*width+j]>thresh*chromave) { float atot=0; float btot=0; float norm=0; float wt; for (int i1=MAX(0,i-halfwin+1); i1a[i1][j1]; btot += wt*src->b[i1][j1]; norm += wt; } tmp1->a[i][j] = (int)(atot/norm); tmp1->b[i][j] = (int)(btot/norm); }//end of ab channel averaging } } #ifdef _OPENMP #pragma omp parallel for #endif for(int i = 0; i < height; i++ ) { for(int j = 0; j < width; j++) { dst->L[i][j] = src->L[i][j]; dst->a[i][j] = tmp1->a[i][j]; dst->b[i][j] = tmp1->b[i][j]; } } delete tmp1; free(fringe); } */ };