diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 4a50da4fb..8d9df5973 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -12,14 +12,13 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc jpeg_memsrc.cc jdatasrc.cc - EdgePreserveLab.cc EdgePreservingDecomposition.cc cplx_wavelet_dec.cc FTblockDNchroma.cc + EdgePreserveLab.cc EdgePreservingDecomposition.cc cplx_wavelet_dec.cc FTblockDN.cc PF_correct_RT.cc dirpyrLab_denoise.cc dirpyr_equalizer.cc calc_distort.cc klt/convolve.cc klt/error.cc klt/klt.cc klt/klt_util.cc klt/pnmio.cc klt/pyramid.cc klt/selectGoodFeatures.cc klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc ) -#FTblockDNchroma.cc add_library (rtengine ${RTENGINESOURCEFILES}) #It may be nice to store library version too diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc new file mode 100644 index 000000000..caaf633ec --- /dev/null +++ b/rtengine/FTblockDN.cc @@ -0,0 +1,850 @@ +//////////////////////////////////////////////////////////////// +// +// 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_Ltilesize and height>tilesize) { const int numtiles_W = ceil(((float)(width))/(tilesize-overlap)); @@ -243,6 +243,17 @@ namespace rtengine { int tileheight = ceil(((float)(height))/(numtiles_H)); tilewidth = tilewidth + (tilewidth&1); tileheight = tileheight + (tileheight&1); + + for (int tiletop=0; tiletopgetImage (currWB, tr, baseImg, pp, params.hlrecovery, params.icm, params.raw); if (pl) pl->setProgress (0.45); - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // start tile processing...??? - // perform luma denoise LabImage* labView = new LabImage (fw,fh); if (params.dirpyrDenoise.enabled) { @@ -160,7 +156,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p } // at this stage, we can flush the raw data to free up quite an important amount of memory - // commented out because it make the application crash when batch processing... + // commented out because it makes the application crash when batch processing... // TODO: find a better place to flush rawData and rawRGB //imgsrc->flushRawData(); //imgsrc->flushRGB(); @@ -194,6 +190,9 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p if (pl) pl->setProgress (0.5); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // start tile processing...??? // luminance histogram update hist16.clear(); @@ -237,6 +236,9 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p // directional pyramid equalizer ipf.dirpyrequalizer (labView);//TODO: this is the luminance tonecurve, not the RGB one + // end tile processing...??? + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if (pl) pl->setProgress (0.60);