//////////////////////////////////////////////////////////////// // // 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 "bilateral2.h" #include "gauss.h" #include "rtengine.h" #include "improcfun.h" #include "LUT.h" #include "array2D.h" #include "iccmatrices.h" #include "boxblur.h" #ifdef _OPENMP #include #endif #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 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, const procparams::DirPyrDenoiseParams & dnparams, const procparams::DefringeParams & defringe) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /*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 for (int i=0; ir[i][j] = src->r[i][j]; dst->r[i][j] = src->r[i][j]; dst->r[i][j] = src->r[i][j]; } } return; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // gamma transform for input data float gam = dnparams.gamma; float gamthresh = 0.03; float gamslope = exp(log((double)gamthresh)/gam)/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; } // inverse gamma transform for output data float igam = 1/gam; float igamthresh = gamthresh*gamslope; float igamslope = 1/gamslope; LUTf igamcurve(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); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //srand((unsigned)time(0));//test with random data const float gain = pow (2.0, dnparams.expcomp); float noisevar_Ldetail = SQR((100-dnparams.Ldetail) * TS * 100.0f); array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); const int border = MAX(2,TS/16); for (int i=0; iTS/2 ? i-TS+1 : i)); float vmask = (i1TS/2 ? j-TS+1 : j)); tilemask_in[i][j] = (vmask * (j1data[n] = 0; } const int tilesize = 1024; const int overlap = 128; int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; if (imwidth Ldetail(width,height); #ifdef _OPENMP #pragma omp parallel for #endif //fill tile from image for (int i=tiletop, i1=0; ir[i][j];//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 = gain*src->g[i][j];//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 = gain*src->b[i][j];//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 = X<65535.0f ? gamcurve[X] : (gamma((double)X/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Y = Y<65535.0f ? gamcurve[Y] : (gamma((double)Y/65535.0, gam, gamthresh, gamslope, 1.0, 0.0)*32768.0f); Z = Z<65535.0f ? gamcurve[Z] : (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; } array2D Lin(width,height); float * Linptr = Lin; memcpy (Linptr, labdn->data, width*height*sizeof(float)); //initial impulse denoise impulse_nr (labdn, 50.0f/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. wavelet_decomposition Ldecomp(labdn->data, labdn->W, labdn->H, 5/*maxlevels*/, 0/*subsampling*/ ); wavelet_decomposition adecomp(labdn->data+datalen, labdn->W, labdn->H, 5, 1 ); wavelet_decomposition bdecomp(labdn->data+2*datalen, labdn->W, labdn->H, 5, 1 ); float noisevarL = SQR(dnparams.luma/25.0f); float noisevarab = SQR(dnparams.chroma/10.0f); WaveletDenoiseAll_BiShrink(Ldecomp, adecomp, bdecomp, noisevarL, noisevarab); Ldecomp.reconstruct(labdn->data); adecomp.reconstruct(labdn->data+datalen); bdecomp.reconstruct(labdn->data+2*datalen); //TODO: at this point wavelet coefficients storage can be freed //second impulse denoise impulse_nr (labdn, 50.0f/20.0f); //PF_correct_RT(dst, dst, defringe.radius, defringe.threshold); array2D Lwavdn(width,height,ARRAY2D_CLEAR_DATA); 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! array2D totwt(width,height,ARRAY2D_CLEAR_DATA);//weight for combining blocks // allocate DCT data structures // 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 float * Lblox = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); float * fLblox = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); //make a plan for FFTW fftwf_plan plan_forward_blox, plan_backward_blox; int nfwd[2]={TS,TS}; //for DCT: const fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; const fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; plan_forward_blox = fftwf_plan_many_r2r(2, nfwd, numblox_W, Lblox, NULL, 1, TS*TS, fLblox, NULL, 1, TS*TS, fwdkind, FFTW_ESTIMATE ); plan_backward_blox = fftwf_plan_many_r2r(2, nfwd, numblox_W, fLblox, NULL, 1, TS*TS, Lblox, NULL, 1, TS*TS, bwdkind, FFTW_ESTIMATE ); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Main detail recovery algorithm: Block loop #pragma omp parallel for schedule(dynamic) 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 float * Vmask = new float [height]; float * Hmask = new float [width]; for (int i=0; i0) Vmask[i] = mask; if (tilebottom0) Hmask[i] = mask; if (tilerightL[i1][j1]; X = (labdn->a[i1][j1]) + Y; Z = Y - (labdn->b[i1][j1]); X = X<32768.0f ? igamcurve[X] : (gamma((float)X/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); Y = Y<32768.0f ? igamcurve[Y] : (gamma((float)Y/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); Z = Z<32768.0f ? igamcurve[Z] : (gamma((float)Z/32768.0f, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0f); //Y = 65535.0f*(0.05+0.1*((float)rand()/(float)RAND_MAX));//test with random data float mask = Vmask[i1]*Hmask[j1]; if (Y<-500) { float xxx=Y; } dsttmp->r[i][j] += mask*X/gain;//prophoto_xyz[0][0]*X + prophoto_xyz[0][1]*Y + prophoto_xyz[0][2]*Z; dsttmp->g[i][j] += mask*Y/gain;//prophoto_xyz[1][0]*X + prophoto_xyz[1][1]*Y + prophoto_xyz[1][2]*Z; dsttmp->b[i][j] += mask*Z/gain;//prophoto_xyz[2][0]*X + prophoto_xyz[2][1]*Y + prophoto_xyz[2][2]*Z; } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //delete labin; delete labdn; }//end of tile row }//end of tile loop //copy denoised image to output memcpy (dst->data, dsttmp->data, 3*imwidth*imheight*sizeof(float)); delete dsttmp; }//end of main RGB_denoise //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::RGBtile_denoise (float * fLblox, int vblproc, int hblproc, int numblox_H, int numblox_W, float noisevar_Ldetail ) //for DCT { 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 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); AlignedBuffer* buffer = new AlignedBuffer (MAX(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)); 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-exp(-(mag_a/mad_a)-(mag_L/(9*mad_L)))/*satfactor_a*/); WavCoeffs_b[dir][coeffloc_ab] *= SQR(1-exp(-(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); 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 for (int i=0; i0.01) { for (int i=0; i2*thresh_a ? 1 : (coeff_a2*thresh_b ? 1 : (coeff_b0.01) { for (int i=0; i2*thresh_L ? 1 : (coeff_L2*thresh_L ? 1 : (coeff_L