//////////////////////////////////////////////////////////////// // // 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" #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 0 // radius of block averaging //#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, 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; 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; } //srand((unsigned)time(0));//test with random data //int max; //float median = MadMax(src->data, max, src->width*src->height); //gain = sqrt(MAX(1.0f,(0.15f*65535.0f/median))*(65535.0f/max));//'gain' is public float allocated in improcfun.h; const float gain = pow (2.0, dnparams.expcomp); #ifdef _OPENMP #pragma omp parallel for #endif for (int i=0; iheight; i++) for (int j=0; jwidth; j++) { float X = gain*src->r[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); dst->L[i][j] = Y; dst->a[i][j] = 0.2f*(X-Y); dst->b[i][j] = 0.2f*(Y-Z); //Y = 0.05+0.1*((float)rand()/(float)RAND_MAX);//test with random data //dst->L[i][j] = gamcurve[65535.0f*Y]; } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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; 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); } const float gain = pow (2.0, dnparams.expcomp); #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 = 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 dst->r[i][j] = X/gain;//prophoto_xyz[0][0]*X + prophoto_xyz[0][1]*Y + prophoto_xyz[0][2]*Z; dst->g[i][j] = Y/gain;//prophoto_xyz[1][0]*X + prophoto_xyz[1][1]*Y + prophoto_xyz[1][2]*Z; dst->b[i][j] = Z/gain;//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 ("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; if (dnparams.luma==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; } //const int blkrad=2; float noisevar_Ldetail = SQR((100-dnparams.Ldetail) * TS * 100.0f); array2D tilemask_in(TS,TS); array2D tilemask_out(TS,TS); array2D totwt(width,height,ARRAY2D_CLEAR_DATA); 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 * (j1tilesize and height>tilesize) { const int numtiles_W = ceil(((float)(width))/(tilesize-overlap)); const int numtiles_H = ceil(((float)(height))/(tilesize-overlap)); int tilewidth = ceil(((float)(width))/(numtiles_W)); int tileheight = ceil(((float)(height))/(numtiles_H)); tilewidth = tilewidth + (tilewidth&1); tileheight = tileheight + (tileheight&1); for (int tiletop=0; tiletop Ldn(width,height); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform RGB input to ersatz Lab RGB_InputTransf(src, labin, dnparams, defringe); memcpy (labdn->data, labin->data, 3*width*height*sizeof(float)); //initial impulse denoise impulse_nr (labdn, 50.0f/20.0f); int datalen = labin->W * labin->H; //now perform basic wavelet denoise wavelet_decomposition Ldecomp(labin->data, labin->W, labin->H, 5/*maxlevels*/, 0/*subsampling*/ ); wavelet_decomposition adecomp(labin->data+datalen, labin->W, labin->H, 5, 1 );//last args are maxlevels, subsampling wavelet_decomposition bdecomp(labin->data+2*datalen, labin->W, labin->H, 5, 1 );//last args are maxlevels, subsampling 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); float * Ldnptr = Ldn; memcpy (Ldnptr, labdn->data, width*height*sizeof(float)); for (int i=0; iW*labdn->H; i++) { labdn->data[i] = 0.0f; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // now do detail recovery using block DCT to detect patterns // missed by wavelet denoise // allocate DCT data structures // calculation for detail recovery 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 float ** Lblox = new float *[8] ; float ** fLblox = new float *[8] ; //for DCT for( int i = 0 ; i < 8 ; i++ ) { Lblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); fLblox[i] = (float *) fftwf_malloc (numblox_W*TS*TS * sizeof (float)); //for DCT } //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[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; vblk=height) { rr = MAX(0,2*height-2-row); } for (int j=0; jW; j++) { datarow[j] = (labin->L[rr][j]-Ldn[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]/totwt[i][j];//note that labdn initially stores the denoised hipass data labdn->L[i][j] = Ldn[i][j] + hpdn; } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // transform denoised "Lab" to output RGB RGB_OutputTransf(labdn, dst, dnparams); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% delete labin; delete labdn; }//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 { int vblprocmod=vblproc%8; float * nbrwt = new float[TS*TS]; //for DCT int blkstart = hblproc*TS*TS; boxabsblur(fLblox[vblprocmod]+blkstart, nbrwt, 3, 3, TS, TS);//blur neighbor weights for more robust estimation //for DCT for (int n=0; nL[top+i][left+j] += tilemask_out[i][j]*bloxrow_L[(indx + i)*TS+j]*DCTnorm; //for DCT } } } #undef TS #undef fTS #undef offset //#undef eps //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* 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); } */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::WaveletDenoise(cplx_wavelet_decomposition &DualTreeCoeffs, float noisevar ) { int maxlvl = DualTreeCoeffs.maxlevel(); int rad_stage1[8] = {3,2,1,1,1,1,1,1}; int rad_stage2[8] = {2,1,1,1,1,1,1,1}; for (int lvl=0; lvl wiener1(Wlvl,Hlvl); for (int m=0; m<2; m++) { float ** ReCoeffs = DualTreeCoeffs.level_coeffs(lvl,0+m); float ** ImCoeffs = DualTreeCoeffs.level_coeffs(lvl,2+m); float ** ReParents = DualTreeCoeffs.level_coeffs(lvl+1,0+m); float ** ImParents = DualTreeCoeffs.level_coeffs(lvl+1,2+m); int ParentPadding = DualTreeCoeffs.level_pad(lvl+1,0+m); for (int dir=1; dir<4; dir++) { //FirstStageWiener (ReCoeffs[dir],ImCoeffs[dir],wiener1,Wlvl,Hlvl,rad_stage1[lvl], noisevar); //SecondStageWiener(ReCoeffs[dir],ImCoeffs[dir],wiener1,Wlvl,Hlvl,rad_stage2[lvl], noisevar); BiShrink(ReCoeffs[dir], ImCoeffs[dir], ReParents[dir], ImParents[dir], Wlvl, Hlvl, lvl, ParentPadding, noisevar); } } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void ImProcFunctions::WaveletDenoise(wavelet_decomposition &WaveletCoeffs, float noisevar ) { int maxlvl = WaveletCoeffs.maxlevel(); int rad_stage1[8] = {3,2,1,1,1,1,1,1}; int rad_stage2[8] = {2,1,1,1,1,1,1,1}; for (int lvl=0; lvl wiener1(Wlvl,Hlvl); int ParentPadding; float ** WavParents; float ** WavCoeffs = WaveletCoeffs.level_coeffs(lvl); if (lvl2*thresh_a ? 1 : (coeff_a2*thresh_b ? 1 : (coeff_b2*thresh_L ? 1 : (coeff_L2*thresh_L ? 1 : (coeff_L=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; 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 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; i