diff --git a/rtdata/languages/default b/rtdata/languages/default index 1378baf40..da76c9387 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -568,8 +568,19 @@ TP_CROP_Y;y TP_DETAIL_AMOUNT;Amount TP_DIRPYRDENOISE_CHROMA;Chrominance TP_DIRPYRDENOISE_GAMMA;Gamma -TP_DIRPYRDENOISE_LABEL;Directional Pyramid Noise Reduction +TP_DIRPYRDENOISE_LABEL;Directional pyramid noise reduction TP_DIRPYRDENOISE_LUMA;Luminance +TP_DIRPYREQUALIZER_CHROMACOARSEST;chroma coarsest +TP_DIRPYREQUALIZER_CHROMACONTRAST_MINUS;Contrast- +TP_DIRPYREQUALIZER_CHROMACONTRAST_PLUS;Contrast+ +TP_DIRPYREQUALIZER_CHROMAFINEST;chroma finest +TP_DIRPYREQUALIZER_CHROMANEUTRAL;Neutral +TP_DIRPYREQUALIZER_LUMACOARSEST;luma coarsest +TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS;Contrast- +TP_DIRPYREQUALIZER_LUMACONTRAST_PLUS;Contrast+ +TP_DIRPYREQUALIZER_LUMAFINEST;luma finest +TP_DIRPYREQUALIZER_LUMANEUTRAL;Neutral +TP_DIRPYREQUALIZER_LABEL;Directional pyramid equalizer TP_DISTORTION_AMOUNT;Amount TP_DISTORTION_LABEL;Distortion TP_EQUALIZER_CONTRAST_MINUS;Contrast- diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc index 81fcf13cb..715aa6c2e 100644 --- a/rtengine/CA_correct_RT.cc +++ b/rtengine/CA_correct_RT.cc @@ -105,10 +105,7 @@ void RawImageSource::CA_correct_RT() { #define PIX_SORT(a,b) { if ((a)>(b)) {temp=(a);(a)=(b);(b)=temp;} } #define SQR(x) ((x)*(x)) - - /*static const float pre_mul[3] = {MIN(ri->red_multiplier,ri->green_multiplier), ri->green_multiplier, \ - MIN(ri->blue_multiplier,ri->green_multiplier)};*/ - + const float clip_pt = ri->defgain; // local variables diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 299cf9ed3..5fcf0f6ba 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -13,7 +13,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc dcraw.cc iccstore.cc stdimagesource.cc myfile.cc iccjpeg.c hlmultipliers.cc improccoordinator.cc processingjob.cc rtthumbnail.cc utils.cc labimage.cc iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc - wavelet_dec.cc ipequalizer.cc dirpyrLab_denoise.cc ) + wavelet_dec.cc ipequalizer.cc dirpyrLab_denoise.cc dirpyrLab_equalizer.cc) add_library (rtengine ${RTENGINESOURCEFILES}) #It may be nice to store library version too diff --git a/rtengine/amaze_interpolate_RT.cc b/rtengine/amaze_interpolate_RT.cc index d3ce7a1aa..073543e91 100644 --- a/rtengine/amaze_interpolate_RT.cc +++ b/rtengine/amaze_interpolate_RT.cc @@ -51,7 +51,7 @@ void RawImageSource::amaze_demosaic_RT() { } - static const float clip_pt = 1/ri->defgain; + const float clip_pt = 1/ri->defgain; #define TS 512 // Tile size; the image is processed in square tiles to lower memory requirements and facilitate multi-threading diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 4d29a82d0..a0462594d 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -159,29 +159,7 @@ void Crop::update (int todo, bool internal) { // shadows & highlights & tone curve & convert to cielab if (todo & M_RGBCURVE) parent->ipf.rgbProc (baseCrop, laboCrop, parent->tonecurve, cshmap); -/* - // apply luminance operations - if (todo & M_LUMINANCE) { - parent->ipf.luminanceCurve (laboCrop, labnCrop, parent->lumacurve, 0, croph); - if (skip==1) { - parent->ipf.impulsedenoise (labnCrop); - parent->ipf.lumadenoise (labnCrop, cbuffer); - //parent->ipf.sharpening (labnCrop, (unsigned short**)cbuffer); - parent->ipf.waveletEqualizer(labnCrop, true, false); - } - } - // apply color operations - if (todo & M_COLOR) { - parent->ipf.colorCurve (laboCrop, labnCrop); - if (skip==1) { - parent->ipf.colordenoise (labnCrop, cbuffer); - parent->ipf.dirpyrdenoise (labnCrop); - parent->ipf.sharpening (labnCrop, (unsigned short**)cbuffer); - parent->ipf.waveletEqualizer(labnCrop, false, true); - } - } -*/ // apply luminance operations if (todo & (M_LUMINANCE+M_COLOR)) { @@ -195,8 +173,8 @@ void Crop::update (int todo, bool internal) { parent->ipf.dirpyrdenoise (labnCrop); parent->ipf.sharpening (labnCrop, (unsigned short**)cbuffer); //parent->ipf.impulsedenoise (labnCrop); + parent->ipf.dirpyrequalizer (labnCrop); parent->ipf.waveletEqualizer(labnCrop, true, true); - //parent->ipf.waveletEqualizer(labnCrop, false, true); } } diff --git a/rtengine/dirpyrLab_denoise.cc b/rtengine/dirpyrLab_denoise.cc index d63d35d24..cd07ddcf4 100644 --- a/rtengine/dirpyrLab_denoise.cc +++ b/rtengine/dirpyrLab_denoise.cc @@ -341,7 +341,7 @@ namespace rtengine { //float eps = 0.0; double wtdsum[3], norm; float hipass[3], hpffluct[3], tonefactor, nrfactor; - int i1, j1; + int i, j, ix, jx; // c[0] noise_L // c[1] noise_ab (relative to noise_L) @@ -366,9 +366,7 @@ namespace rtengine { }*/ //float domker[5][5] = {{1,1,1,1,1},{1,2,2,2,1},{1,2,4,2,1},{1,2,2,2,1},{1,1,1,1,1}}; - LabImage* smooth; - smooth = new LabImage(width, height); // for coarsest level, take non-subsampled lopass image and subtract from lopass_fine to generate hipass image @@ -386,28 +384,64 @@ namespace rtengine { // step (1) - - for(int i = 0, i1=0; i < height; i+=pitch, i1++) - for(int j = 0, j1=0; j < width; j+=pitch, j1++) { - - //copy common pixels - smooth->L[i][j] = data_coarse->L[i1][j1]; - smooth->a[i][j] = data_coarse->a[i1][j1]; - smooth->b[i][j] = data_coarse->b[i1][j1]; - } - - if (pitch>1) {//pitch=2; expand coarse image, fill in missing data + if (pitch==1) { - for(int i = 0; i < height-1; i+=2) - for(int j = 0; j < width-1; j+=2) { + // step (1-2-3-4) + for( i = 0; i < height; i++) + for( j = 0; j < width; j++) { + + tonefactor = ((NRWT_L(data_coarse->L[i][j]))); + + //Wiener filter + //luma + if (level<2) { + hipass[0] = data_fine->L[i][j]-data_coarse->L[i][j]; + hpffluct[0]=SQR(hipass[0])+0.001; + hipass[0] *= hpffluct[0]/(hpffluct[0]+noisevar_L); + data_fine->L[i][j] = CLIP(hipass[0]+data_coarse->L[i][j]); + } + + //chroma + hipass[1] = data_fine->a[i][j]-data_coarse->a[i][j]; + hipass[2] = data_fine->b[i][j]-data_coarse->b[i][j]; + hpffluct[1]=SQR(hipass[1]*tonefactor)+0.001; + hpffluct[2]=SQR(hipass[2]*tonefactor)+0.001; + nrfactor = (hpffluct[1]+hpffluct[2]) /((hpffluct[1]+hpffluct[2]) + noisevar_ab * NRWT_AB); + + hipass[1] *= nrfactor; + hipass[2] *= nrfactor; + + data_fine->a[i][j] = hipass[1]+data_coarse->a[i][j]; + data_fine->b[i][j] = hipass[2]+data_coarse->b[i][j]; + } + + } else { + + LabImage* smooth; + + smooth = new LabImage(width, height); + + for( i = 0, ix=0; i < height; i+=pitch, ix++) + for( j = 0, jx=0; j < width; j+=pitch, jx++) { + + //copy common pixels + smooth->L[i][j] = data_coarse->L[ix][jx]; + smooth->a[i][j] = data_coarse->a[ix][jx]; + smooth->b[i][j] = data_coarse->b[ix][jx]; + } + + //if (pitch>1) {//pitch=2; step (1) expand coarse image, fill in missing data + + for( i = 0; i < height-1; i+=2) + for( j = 0; j < width-1; j+=2) { //do midpoint first norm=0; wtdsum[0]=wtdsum[1]=wtdsum[2]=0.0; - for(i1=i; i1L[i1][j1]; - wtdsum[1] += smooth->a[i1][j1]; - wtdsum[2] += smooth->b[i1][j1]; + for( ix=i; ixL[ix][jx]; + wtdsum[1] += smooth->a[ix][jx]; + wtdsum[2] += smooth->b[ix][jx]; norm++; } norm = 1/norm; @@ -416,22 +450,22 @@ namespace rtengine { smooth->b[i+1][j+1]=wtdsum[2]*norm; } - for(int i = 0; i < height-1; i+=2) - for(int j = 0; j < width-1; j+=2) { + for( i = 0; i < height-1; i+=2) + for( j = 0; j < width-1; j+=2) { //now right neighbor if (j+1==width) continue; norm=0; wtdsum[0]=wtdsum[1]=wtdsum[2]=0.0; - for (j1=j; j1L[i][j1]; - wtdsum[1] += smooth->a[i][j1]; - wtdsum[2] += smooth->b[i][j1]; + for (jx=j; jxL[i][jx]; + wtdsum[1] += smooth->a[i][jx]; + wtdsum[2] += smooth->b[i][jx]; norm++; } - for (i1=MAX(0,i-1); i1L[i1][j+1]; - wtdsum[1] += smooth->a[i1][j+1]; - wtdsum[2] += smooth->b[i1][j+1]; + for (ix=MAX(0,i-1); ixL[ix][j+1]; + wtdsum[1] += smooth->a[ix][j+1]; + wtdsum[2] += smooth->b[ix][j+1]; norm++; } norm = 1/norm; @@ -443,16 +477,16 @@ namespace rtengine { if (i+1==height) continue; norm=0; wtdsum[0]=wtdsum[1]=wtdsum[2]=0.0; - for (i1=i; i1L[i1][j]; - wtdsum[1] += smooth->a[i1][j]; - wtdsum[2] += smooth->b[i1][j]; + for (ix=i; ixL[ix][j]; + wtdsum[1] += smooth->a[ix][j]; + wtdsum[2] += smooth->b[ix][j]; norm++; } - for (j1=j-1; j1L[i+1][j1]; - wtdsum[1] += smooth->a[i+1][j1]; - wtdsum[2] += smooth->b[i+1][j1]; + for (jx=MAX(0,j-1); jxL[i+1][jx]; + wtdsum[1] += smooth->a[i+1][jx]; + wtdsum[2] += smooth->b[i+1][jx]; norm++; } norm=1/norm; @@ -461,42 +495,40 @@ namespace rtengine { smooth->b[i+1][j]=wtdsum[2]*norm; } - } - - // step (2-3-4) - for(int i = 0; i < height; i++) - for(int j = 0; j < width; j++) { - - tonefactor = ((NRWT_L(smooth->L[i][j]))); - - //Wiener filter - //luma - if (level<2) { - hipass[0] = data_fine->L[i][j]-smooth->L[i][j]; - hpffluct[0]=SQR(hipass[0])+0.001; - hipass[0] *= hpffluct[0]/(hpffluct[0]+noisevar_L); - data_fine->L[i][j] = CLIP(hipass[0]+smooth->L[i][j]); + + + // step (2-3-4) + for( i = 0; i < height; i++) + for( j = 0; j < width; j++) { + + tonefactor = ((NRWT_L(smooth->L[i][j]))); + + //Wiener filter + //luma + if (level<2) { + hipass[0] = data_fine->L[i][j]-smooth->L[i][j]; + hpffluct[0]=SQR(hipass[0])+0.001; + hipass[0] *= hpffluct[0]/(hpffluct[0]+noisevar_L); + data_fine->L[i][j] = CLIP(hipass[0]+smooth->L[i][j]); + } + + //chroma + hipass[1] = data_fine->a[i][j]-smooth->a[i][j]; + hipass[2] = data_fine->b[i][j]-smooth->b[i][j]; + hpffluct[1]=SQR(hipass[1]*tonefactor)+0.001; + hpffluct[2]=SQR(hipass[2]*tonefactor)+0.001; + nrfactor = (hpffluct[1]+hpffluct[2]) /((hpffluct[1]+hpffluct[2]) + noisevar_ab * NRWT_AB); + + hipass[1] *= nrfactor; + hipass[2] *= nrfactor; + + data_fine->a[i][j] = hipass[1]+smooth->a[i][j]; + data_fine->b[i][j] = hipass[2]+smooth->b[i][j]; } - - //chroma - hipass[1] = data_fine->a[i][j]-smooth->a[i][j]; - hipass[2] = data_fine->b[i][j]-smooth->b[i][j]; - hpffluct[1]=SQR(hipass[1]*tonefactor)+0.001; - hpffluct[2]=SQR(hipass[2]*tonefactor)+0.001; - nrfactor = (hpffluct[1]+hpffluct[2]) /((hpffluct[1]+hpffluct[2]) + noisevar_ab * NRWT_AB); - //nrfactor *= resaturate; - /*if (level) { - hipass[0] *= recontrast; - nrfactor *= resaturate; - }*/ - hipass[1] *= nrfactor; - hipass[2] *= nrfactor; - - data_fine->a[i][j] = hipass[1]+smooth->a[i][j]; - data_fine->b[i][j] = hipass[2]+smooth->b[i][j]; - } + + delete smooth; + }//end of pitch>1 - delete smooth; }; diff --git a/rtengine/dirpyrLab_equalizer.cc b/rtengine/dirpyrLab_equalizer.cc new file mode 100644 index 000000000..2dbe045ea --- /dev/null +++ b/rtengine/dirpyrLab_equalizer.cc @@ -0,0 +1,541 @@ +/* + * This file is part of RawTherapee. + * + * RawTherapee 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. + * + * RawTherapee 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 RawTherapee. If not, see . + * + * © 2010 Emil Martinec + * + */ + +//#include +#include +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#define SQR(x) ((x)*(x)) +#define CLIPTO(a,b,c) ((a)>(b)?((a)<(c)?(a):(c)):(b)) +#define CLIPC(a) ((a)>-32000?((a)<32000?(a):32000):-32000) +#define CLIP(a) (CLIPTO(a,0,65535)) + + + + +//#define IDIRWT(i1,j1,i,j) ( irangefn[abs((int)data_fine->L[i1][j1]-data_fine->L[i][j]) + \ +abs((int)data_fine->a[i1][j1]-data_fine->a[i][j])+abs((int)data_fine->b[i1][j1]-data_fine->b[i][j])] ) + +#define DIRWT(i1,j1,i,j) ( /*domker[(i1-i)/scale+halfwin][(j1-j)/scale+halfwin] */ rangefn[abs((int)data_fine->L[i1][j1]-data_fine->L[i][j])+abs((int)data_fine->a[i1][j1]-data_fine->a[i][j])+abs((int)data_fine->b[i1][j1]-data_fine->b[i][j])] ) + +namespace rtengine { + + static const int maxlevel = 4; + + //sequence of scales + static const int scales[8] = {1,2,4,8,16,32,64,128}; + //sequence of pitches + static const int pitches[8] = {1,1,1,1,1,1,1,1}; + + //sequence of scales + //static const int scales[8] = {1,1,1,1,1,1,1,1}; + //sequence of pitches + //static const int pitches[8] = {2,2,2,2,2,2,2,2}; + + //sequence of scales + //static const int scales[8] = {1,3,6,10,15,21,28,36}; + //sequence of pitches + //static const int pitches[8] = {1,1,1,1,1,1,1,1}; + + //sequence of scales + //static const int scales[8] = {1,1,2,4,8,16,32,64}; + //sequence of pitches + //static const int pitches[8] = {2,1,1,1,1,1,1,1}; + + //pitch is spacing of subsampling + //scale is spacing of directional averaging weights + //example 1: no subsampling at any level -- pitch=1, scale=2^n + //example 2: subsampling by 2 every level -- pitch=2, scale=1 at each level + //example 3: no subsampling at first level, subsampling by 2 thereafter -- + // pitch =1, scale=1 at first level; pitch=2, scale=2 thereafter + + + + + void ImProcFunctions :: dirpyrLab_equalizer(LabImage * src, LabImage * dst, /*float luma, float chroma, float gamma*/ const double * mult ) + { + /*float gam = 2.0;//MIN(3.0, 0.1*fabs(c[4])/3.0+0.001); + float gamthresh = 0.03; + float gamslope = exp(log((double)gamthresh)/gam)/gamthresh; + unsigned short gamcurve[65536]; + for (int i=0; i<65536; i++) { + int g = (int)(CurveFactory::gamma((double)i/65535.0, gam, gamthresh, gamslope, 1.0, 0.0) * 65535.0); + //if (i<500) printf("%d %d \n",i,g); + gamcurve[i] = CLIP(g); + } + + + //#pragma omp parallel for if (multiThread) + for (int i=0; iH; i++) { + for (int j=0; jW; j++) { + src->L[i][j] = gamcurve[src->L[i][j] ]; + } + }*/ + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + int * rangefn = new int [0x20000]; + + //int * irangefn = new int [0x20000]; + + int intfactor = 1024;//16384; + + + //set up weights + float noise = 1500; + + + //set up range functions + + for (int i=0; i<0x20000; i++) + rangefn[i] = (int)((noise/((double)i + noise))*intfactor); + + /*for (int i=0; i<0x20000; i++) + //irangefn[i] = 1+(int)( exp(-(double)fabs(i-0x10000) / (1+16*noise) )*intfactor); + irangefn[i] = intfactor*(int)(SQR(noise)/((float)SQR(noise)+SQR(i)));*/ + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + int level; + int ** buffer[3]; + + + LabImage * dirpyrLablo[maxlevel]; + + int w = src->W; + int h = src->H; + + buffer[0] = allocArray (w+128, h+128); + buffer[1] = allocArray (w+128, h+128); + buffer[2] = allocArray (w+128, h+128); + + for (int i=0; iH; i+=totalpitch, i1++) + for(int j = 0, j1=0; j < src->W; j+=totalpitch, j1++) { + + //copy pixels + buffer[0][i][j] = dirpyrLablo[maxlevel-1]->L[i1][j1]; + buffer[1][i][j] = dirpyrLablo[maxlevel-1]->a[i1][j1]; + buffer[2][i][j] = dirpyrLablo[maxlevel-1]->b[i1][j1]; + + } + + //if we are not subsampling, this is lots faster but does the typecasting work??? + //memcpy(buffer[0],dirpyrLablo[maxlevel-1]->L,sizeof(buffer[0])); + //memcpy(buffer[1],dirpyrLablo[maxlevel-1]->a,sizeof(buffer[1])); + //memcpy(buffer[2],dirpyrLablo[maxlevel-1]->b,sizeof(buffer[2])); + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + for(int level = maxlevel - 1; level > 0; level--) + { + + //int scale = scales[level]; + int pitch = pitches[level]; + + totalpitch /= pitch; + + idirpyr_eq(dirpyrLablo[level], dirpyrLablo[level-1], buffer, /*i*/ rangefn, level, pitch, totalpitch, mult ); + + } + + + scale = scales[0]; + pitch = pitches[0]; + totalpitch /= pitch; + + idirpyr_eq(dirpyrLablo[0], dst, buffer, /*i*/ rangefn, 0, pitch, totalpitch, mult ); + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + /*float igam = 1/gam; + float igamthresh = gamthresh*gamslope; + float igamslope = 1/gamslope; + for (int i=0; i<65536; i++) { + int g = (int)(CurveFactory::gamma((float)i/65535.0, igam, igamthresh, igamslope, 1.0, 0.0) * 65535.0); + gamcurve[i] = CLIP(g); + }*/ + + + for (int i=0; iH; i++) + for (int j=0; jW; j++) { + + dst->L[i][j] = CLIP((int)( buffer[0][i][j] )); + dst->a[i][j] = CLIPC((int)( buffer[1][i][j] )); + dst->b[i][j] = CLIPC((int)( buffer[2][i][j] )); + + + //dst->L[i][j] = gamcurve[ dst->L[i][j] ]; + + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + for(int i = 0; i < maxlevel; i++) + { + delete dirpyrLablo[i]; + } + + for (int c=0;c<3;c++) + freeArray(buffer[c], h+128); + + //delete [] rangefn_L; + //delete [] rangefn_ab; + delete [] rangefn; + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + }; + + void ImProcFunctions::dirpyr_eq(LabImage* data_fine, LabImage* data_coarse, int * rangefn, int level, int pitch, int scale, const double * mult ) + { + + //pitch is spacing of subsampling + //scale is spacing of directional averaging weights + //example 1: no subsampling at any level -- pitch=1, scale=2^n + //example 2: subsampling by 2 every level -- pitch=2, scale=1 at each level + //example 3: no subsampling at first level, subsampling by 2 thereafter -- + // pitch =1, scale=1 at first level; pitch=2, scale=2 thereafter + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // calculate weights, compute directionally weighted average + + int width = data_fine->W; + int height = data_fine->H; + + float Lout, aout, bout; + float dirwt, norm; + + //generate domain kernel + int halfwin = 1;//MIN(ceil(2*sig),3); + int scalewin = halfwin*scale; + //int intfactor = 16384; + + /*float domker[7][7]; + for (int i=-halfwin; i<=halfwin; i++) + for (int j=-halfwin; j<=halfwin; j++) { + domker[i+halfwin][j+halfwin] = (int)(exp(-(i*i+j*j)/(2*sig*sig))*intfactor); //or should we use a value that depends on sigma??? + }*/ + //float domker[5][5] = {{1,1,1,1,1},{1,2,2,2,1},{1,2,4,2,1},{1,2,2,2,1},{1,1,1,1,1}}; + + //float domker[3][3] = {{1,1,1},{1,2,1},{1,1,1}}; + + + for(int i = 0, i1=0; i < height; i+=pitch, i1++) { + for(int j = 0, j1=0; j < width; j+=pitch, j1++) + { + norm = 0;//if we do want to include the input pixel in the sum + Lout = 0; + aout = 0; + bout = 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) { + dirwt = DIRWT(inbr, jnbr, i, j); + Lout += dirwt*data_fine->L[inbr][jnbr]; + aout += dirwt*data_fine->a[inbr][jnbr]; + bout += dirwt*data_fine->b[inbr][jnbr]; + norm += dirwt; + } + } + data_coarse->L[i1][j1]=Lout/norm;//low pass filter + data_coarse->a[i1][j1]=aout/norm; + data_coarse->b[i1][j1]=bout/norm; + } + } + + + + + }; + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + void ImProcFunctions::idirpyr_eq(LabImage* data_coarse, LabImage* data_fine, int *** buffer, int * irangefn, int level, int pitch, int scale, const double * mult ) + { + + int width = data_fine->W; + int height = data_fine->H; + + float lumamult[4], chromamult[4]; + for (int i=0; i<4; i++) { + lumamult[i] = mult[i]; + chromamult[i] = mult[i+4]; + } + + float wtdsum[6], norm, dirwt; + float hipass[3]; + int i1, j1; + + + //int halfwin = 3;//MIN(ceil(2*sig),3); + //int intfactor= 16384; + //int winwidth=1+2*halfwin;//this belongs in calling function + /*float domker[7][7]; + for (int i=-halfwin; i<=halfwin; i++) + for (int j=-halfwin; j<=halfwin; j++) { + domker[i][j] = (int)(exp(-(i*i+j*j)/(2*sig*sig))*intfactor); //or should we use a value that depends on sigma??? + }*/ + //float domker[5][5] = {{1,1,1,1,1},{1,2,2,2,1},{1,2,4,2,1},{1,2,2,2,1},{1,1,1,1,1}}; + + + // for coarsest level, take non-subsampled lopass image and subtract from lopass_fine to generate hipass image + + // denoise hipass image, add back into lopass_fine to generate denoised image at fine scale + + // now iterate: + // (1) take denoised image at level n, expand and smooth using gradient weights from lopass image at level n-1 + // the result is the smoothed image at level n-1 + // (2) subtract smoothed image at level n-1 from lopass image at level n-1 to make hipass image at level n-1 + // (3) denoise the hipass image at level n-1 + // (4) add the denoised image at level n-1 to the smoothed image at level n-1 to make the denoised image at level n-1 + + // note that the coarsest level amounts to skipping step (1) and doing (2,3,4). + // in other words, skip step one if pitch=1 + + + + if (pitch==1) { + // step (1-2-3-4) + for(int i = 0; i < height; i++) + for(int j = 0; j < width; j++) { + + //luma + hipass[0] = (float)data_fine->L[i][j]-data_coarse->L[i][j]; + buffer[0][i*scale][j*scale] += hipass[0] * lumamult[level];//*luma; + + //chroma + hipass[1] = data_fine->a[i][j]-data_coarse->a[i][j]; + hipass[2] = data_fine->b[i][j]-data_coarse->b[i][j]; + buffer[1][i*scale][j*scale] += hipass[1] * chromamult[level]; //*chroma; + buffer[2][i*scale][j*scale] += hipass[2] * chromamult[level]; //*chroma; + } + + } else { + + // step (1) + //if (pitch>1), pitch=2; expand coarse image, fill in missing data + + LabImage* smooth; + + smooth = new LabImage(width, height); + + for(int i = 0, i2=0; i < height; i+=pitch, i2++) + for(int j = 0, j2=0; j < width; j+=pitch, j2++) { + + //copy common pixels + smooth->L[i][j] = data_coarse->L[i2][j2]; + smooth->a[i][j] = data_coarse->a[i2][j2]; + smooth->b[i][j] = data_coarse->b[i2][j2]; + } + //} + + + for(int i = 0; i < height-1; i+=2) + for(int j = 0; j < width-1; j+=2) { + //do midpoint first + norm=dirwt=0; + wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0; + for(i1=i; i1L[i1][j1]; + wtdsum[1] += dirwt*smooth->a[i1][j1]; + wtdsum[2] += dirwt*smooth->b[i1][j1]; + wtdsum[3] += dirwt*buffer[0][i1*scale][j1*scale];// not completely right if j1*scale or i1*scale is out of bounds of original image ??? + wtdsum[4] += dirwt*buffer[1][i1*scale][j1*scale];// also should we use directional average? + wtdsum[5] += dirwt*buffer[2][i1*scale][j1*scale]; + norm+=dirwt; + } + norm = 1/norm; + smooth->L[i+1][j+1]=wtdsum[0]*norm; + smooth->a[i+1][j+1]=wtdsum[1]*norm; + smooth->b[i+1][j+1]=wtdsum[2]*norm; + buffer[0][(i+1)*scale][(j+1)*scale]=wtdsum[3]*norm; + buffer[1][(i+1)*scale][(j+1)*scale]=wtdsum[4]*norm; + buffer[2][(i+1)*scale][(j+1)*scale]=wtdsum[5]*norm; + } + + for(int i = 0; i < height-1; i+=2) + for(int j = 0; j < width-1; j+=2) { + //now right neighbor + if (j+1==width) continue; + norm=dirwt=0; + wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0; + for (j1=j; j1L[i][j1]; + wtdsum[1] += dirwt*smooth->a[i][j1]; + wtdsum[2] += dirwt*smooth->b[i][j1]; + wtdsum[3] += dirwt*buffer[0][i*scale][j1*scale]; + wtdsum[4] += dirwt*buffer[1][i*scale][j1*scale]; + wtdsum[5] += dirwt*buffer[2][i*scale][j1*scale]; + norm+=dirwt; + } + for (i1=MAX(0,i-1); i1L[i1][j+1]; + wtdsum[1] += dirwt*smooth->a[i1][j+1]; + wtdsum[2] += dirwt*smooth->b[i1][j+1]; + wtdsum[3] += dirwt*buffer[0][i1*scale][(j+1)*scale]; + wtdsum[4] += dirwt*buffer[1][i1*scale][(j+1)*scale]; + wtdsum[5] += dirwt*buffer[2][i1*scale][(j+1)*scale]; + norm+=dirwt; + } + norm = 1/norm; + smooth->L[i][j+1]=wtdsum[0]*norm; + smooth->a[i][j+1]=wtdsum[1]*norm; + smooth->b[i][j+1]=wtdsum[2]*norm; + buffer[0][i][(j+1)*scale]=wtdsum[3]*norm; + buffer[1][i][(j+1)*scale]=wtdsum[4]*norm; + buffer[2][i][(j+1)*scale]=wtdsum[5]*norm; + + //now down neighbor + if (i+1==height) continue; + norm=0; + wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0; + for (i1=i; i1L[i1][j]; + wtdsum[1] += dirwt*smooth->a[i1][j]; + wtdsum[2] += dirwt*smooth->b[i1][j]; + wtdsum[3] += dirwt*buffer[0][i1*scale][j*scale]; + wtdsum[4] += dirwt*buffer[1][i1*scale][j*scale]; + wtdsum[5] += dirwt*buffer[2][i1*scale][j*scale]; + norm+=dirwt; + } + for (j1=MAX(0,j-1); j1L[i+1][j1]; + wtdsum[1] += dirwt*smooth->a[i+1][j1]; + wtdsum[2] += dirwt*smooth->b[i+1][j1]; + wtdsum[3] += dirwt*buffer[0][(i+1)*scale][j1*scale]; + wtdsum[4] += dirwt*buffer[1][(i+1)*scale][j1*scale]; + wtdsum[5] += dirwt*buffer[2][(i+1)*scale][j1*scale]; + norm+=dirwt; + } + norm=1/norm; + smooth->L[i+1][j]=wtdsum[0]*norm; + smooth->a[i+1][j]=wtdsum[1]*norm; + smooth->b[i+1][j]=wtdsum[2]*norm; + buffer[0][(i+1)*scale][j*scale]=wtdsum[3]*norm; + buffer[1][(i+1)*scale][j*scale]=wtdsum[4]*norm; + buffer[2][(i+1)*scale][j*scale]=wtdsum[5]*norm; + + } + + + // step (2-3-4) + for(int i = 0; i < height; i++) + for(int j = 0; j < width; j++) { + + //luma + hipass[0] = (float)data_fine->L[i][j]-smooth->L[i][j]; + buffer[0][i*scale][j*scale] += hipass[0] * lumamult[level]; //*luma; + + //chroma + hipass[1] = data_fine->a[i][j]-smooth->a[i][j]; + hipass[2] = data_fine->b[i][j]-smooth->b[i][j]; + buffer[1][i*scale][j*scale] += hipass[1] * chromamult[level]; //*chroma; + buffer[2][i*scale][j*scale] += hipass[2] * chromamult[level]; //*chroma; + } + + delete smooth; + + } + }; + + +#undef DIRWT_L +#undef DIRWT_AB + +#undef NRWT_L +#undef NRWT_AB + +} + diff --git a/rtengine/fast_demo.cc b/rtengine/fast_demo.cc index 19973d540..0e64df13a 100644 --- a/rtengine/fast_demo.cc +++ b/rtengine/fast_demo.cc @@ -27,7 +27,9 @@ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void RawImageSource::fast_demo() { +void RawImageSource::fast_demo() { + int winx=0, winy=0; + int winw=W, winh=H; if (plistener) { plistener->setProgressStr ("Fast demosaicing..."); @@ -52,7 +54,8 @@ void RawImageSource::fast_demo() { #define bord 4 - + + int clip_pt = 4*65535*ri->defgain; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -190,6 +193,7 @@ void RawImageSource::fast_demo() { #pragma omp for // interpolate G using gradient weights for (int i=bord; i< H-bord; i++) { + float wtu, wtd, wtl, wtr; for (int j=bord; j < W-bord; j++) { if (FC(i,j)==1) { @@ -199,14 +203,13 @@ void RawImageSource::fast_demo() { } else { //compute directional weights using image gradients - float wt1, wt2, wt3, wt4; - wt1=dirwt[(abs(ri->data[i+1][j]-ri->data[i-1][j])+abs(ri->data[i][j]-ri->data[i-2][j])+abs(ri->data[i-1][j]-ri->data[i-3][j])) >>4]; - wt2=dirwt[(abs(ri->data[i-1][j]-ri->data[i+1][j])+abs(ri->data[i][j]-ri->data[i+2][j])+abs(ri->data[i+1][j]-ri->data[i+3][j])) >>4]; - wt3=dirwt[(abs(ri->data[i][j+1]-ri->data[i][j-1])+abs(ri->data[i][j]-ri->data[i][j-2])+abs(ri->data[i][j-1]-ri->data[i][j-3])) >>4]; - wt4=dirwt[(abs(ri->data[i][j-1]-ri->data[i][j+1])+abs(ri->data[i][j]-ri->data[i][j+2])+abs(ri->data[i][j+1]-ri->data[i][j+3])) >>4]; + wtu=dirwt[(abs(ri->data[i+1][j]-ri->data[i-1][j])+abs(ri->data[i][j]-ri->data[i-2][j])+abs(ri->data[i-1][j]-ri->data[i-3][j])) >>4]; + wtd=dirwt[(abs(ri->data[i-1][j]-ri->data[i+1][j])+abs(ri->data[i][j]-ri->data[i+2][j])+abs(ri->data[i+1][j]-ri->data[i+3][j])) >>4]; + wtl=dirwt[(abs(ri->data[i][j+1]-ri->data[i][j-1])+abs(ri->data[i][j]-ri->data[i][j-2])+abs(ri->data[i][j-1]-ri->data[i][j-3])) >>4]; + wtr=dirwt[(abs(ri->data[i][j-1]-ri->data[i][j+1])+abs(ri->data[i][j]-ri->data[i][j+2])+abs(ri->data[i][j+1]-ri->data[i][j+3])) >>4]; //store in rgb array the interpolated G value at R/B grid points using directional weighted average - green[i][j]=(int)((wt1*ri->data[i-1][j]+wt2*ri->data[i+1][j]+wt3*ri->data[i][j-1]+wt4*ri->data[i][j+1])/(wt1+wt2+wt3+wt4)); + green[i][j]=(int)((wtu*ri->data[i-1][j]+wtd*ri->data[i+1][j]+wtl*ri->data[i][j-1]+wtr*ri->data[i][j+1])/(wtu+wtd+wtl+wtr)); //red[i][j] = green[i][j]; //blue[i][j] = green[i][j]; @@ -220,16 +223,18 @@ void RawImageSource::fast_demo() { #pragma omp for for (int i=bord; i< H-bord; i++) { - for (int j=bord+(FC(i,2)&1), c=FC(i,j); j < W-bord; j+=2) { - + for (int j=bord+(FC(i,2)&1); j < W-bord; j+=2) { + + int c=FC(i,j); //interpolate B/R colors at R/B sites - unsigned int rb = CLIP((int)(green[i][j] - 0.25*((green[i-1][j-1]-ri->data[i-1][j-1])+(green[i-1][j+1]-ri->data[i-1][j+1])+ \ - (green[i+1][j+1]-ri->data[i+1][j+1])+(green[i+1][j-1]-ri->data[i+1][j-1])))); + if (c==0) {//R site red[i][j] = ri->data[i][j]; - blue[i][j] = rb; + blue[i][j] = CLIP((int)(green[i][j] - 0.25*((green[i-1][j-1]+green[i-1][j+1]+green[i+1][j+1]+green[i+1][j-1]) - \ + MIN(clip_pt,ri->data[i-1][j-1]+ri->data[i-1][j+1]+ri->data[i+1][j+1]+ri->data[i+1][j-1])))); } else {//B site - red[i][j] = rb; + red[i][j] = CLIP((int)(green[i][j] - 0.25*((green[i-1][j-1]+green[i-1][j+1]+green[i+1][j+1]+green[i+1][j-1]) - \ + MIN(clip_pt,ri->data[i-1][j-1]+ri->data[i-1][j+1]+ri->data[i+1][j+1]+ri->data[i+1][j-1])))); blue[i][j] = ri->data[i][j]; } } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index ba78140fc..3f12e2cd8 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -239,13 +239,13 @@ void ImProcCoordinator::updatePreviewImage (int todo) { // ipf.impulsedenoise (nprevl); //} if (scale==1) { + progress ("Pyramid equalizer...",100*readyphase/numofphases); + ipf.dirpyrequalizer (nprevl); + } + if (scale==1) { progress ("Wavelet...",100*readyphase/numofphases); ipf.waveletEqualizer (nprevl, true, true); } - //if (scale==1) { - // progress ("Wavelet...",100*readyphase/numofphases); - // ipf.waveletEqualizer (nprevl, false, true); - //} } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 83962643a..26db61a73 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -434,8 +434,7 @@ void ImProcFunctions::colorCurve (LabImage* lold, LabImage* lnew) { if (params->impulseDenoise.enabled && lab->W>=8 && lab->H>=8) - impulse_nr (lab->L, lab->L, lab->W, lab->H, (float)params->impulseDenoise.thresh/20.0 /*1024*/); - impulse_nr (lab->L, lab->L, lab->W, lab->H, 1024); + impulse_nr (lab->L, lab->L, lab->W, lab->H, (float)params->impulseDenoise.thresh/20.0 ); } void ImProcFunctions::dirpyrdenoise (LabImage* lab) { @@ -444,6 +443,14 @@ void ImProcFunctions::colorCurve (LabImage* lold, LabImage* lnew) { dirpyrLab_denoise(lab, lab, params->dirpyrDenoise.luma, params->dirpyrDenoise.chroma, params->dirpyrDenoise.gamma/3.0 ); } + + void ImProcFunctions::dirpyrequalizer (LabImage* lab) { + + if (params->dirpyrequalizer.enabled && lab->W>=8 && lab->H>=8) { + + dirpyrLab_equalizer(lab, lab, params->dirpyrequalizer.mult); + } + } void ImProcFunctions::lumadenoise (LabImage* lab, int** b2) { diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 5b4e587fb..bfbdb98f0 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -94,12 +94,16 @@ class ImProcFunctions { void impulsedenoise (LabImage* lab);//Emil's impulse denoise void dirpyrdenoise (LabImage* lab);//Emil's impulse denoise - - void dirpyrLab_denoise(LabImage * src, LabImage * dst, const int luma, const int chroma, float gamma );//Emil's directional pyramid denoise - void dirpyr(LabImage* data_fine, LabImage* data_coarse, int level, int * rangefn_L, int * rangefn_ab, int pitch, int scale, const int luma, const int chroma ); - void idirpyr(LabImage* data_coarse, LabImage* data_fine, int level, float * nrwt_l, float * nrwt_ab, int pitch, int scale, const int luma, const int chroma ); - + void dirpyrequalizer (LabImage* lab);//Emil's equalizer + void dirpyrLab_denoise(LabImage * src, LabImage * dst, int luma, int chroma, float gamma );//Emil's directional pyramid denoise + void dirpyr(LabImage* data_fine, LabImage* data_coarse, int level, int * rangefn_L, int * rangefn_ab, int pitch, int scale, const int luma, int chroma ); + void idirpyr(LabImage* data_coarse, LabImage* data_fine, int level, float * nrwt_l, float * nrwt_ab, int pitch, int scale, const int luma, int chroma ); + + void dirpyrLab_equalizer(LabImage * src, LabImage * dst, const double * mult );//Emil's directional pyramid equalizer + void dirpyr_eq(LabImage* data_coarse, LabImage* data_fine, int * rangefn, int level, int pitch, int scale, const double * mult ); + void idirpyr_eq(LabImage* data_coarse, LabImage* data_fine, int *** buffer, int * irangefn, int level, int pitch, int scale, const double * mult ); + Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, Glib::ustring profile); Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, Glib::ustring profile); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index ee4c056ed..65e1d79ba 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -21,7 +21,7 @@ #include -#define NUMOFEVENTS 92 +#define NUMOFEVENTS 94 namespace rtengine { @@ -118,7 +118,9 @@ enum ProcEvent { EvDPDNEnabled=88, EvDPDNLuma=89, EvDPDNChroma=90, - EvDPDNGamma=91 + EvDPDNGamma=91, + EvDirPyrEqualizer=92, + EvDirPyrEqlEnabled=93 }; } #endif diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index b0c5bf55d..6f32aaa90 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -172,6 +172,11 @@ void ProcParams::setDefaults () { { equalizer.c[i] = 0; } + dirpyrequalizer.enabled = false; + for(int i = 0; i < 8; i ++) + { + dirpyrequalizer.mult[i] = 1.0; + } exif.clear (); iptc.clear (); @@ -338,6 +343,15 @@ int ProcParams::save (Glib::ustring fname) const { ss << "C" << i; keyFile.set_integer("Equalizer", ss.str(), equalizer.c[i]); } + + // save directional pyramid equalizer parameters + keyFile.set_boolean ("Directional Pyramid Equalizer", "Enabled", dirpyrequalizer.enabled); + for(int i = 0; i < 8; i++) + { + std::stringstream ss; + ss << "Mult" << i; + keyFile.set_integer("Directional Pyramid Equalizer", ss.str(), dirpyrequalizer.mult[i]); + } // save exif change list for (int i=0; i<(int)exif.size(); i++) @@ -589,6 +603,17 @@ if (keyFile.has_group ("Equalizer")) { if(keyFile.has_key ("Equalizer", ss.str())) equalizer.c[i] = keyFile.get_integer ("Equalizer", ss.str()); } } + + // load directional pyramid equalizer parameters +if (keyFile.has_group ("Directional Pyramid Equalizer")) { + if (keyFile.has_key ("Directional Pyramid Equalizer", "Enabled")) equalizer.enabled = keyFile.get_boolean ("Directional Pyramid Equalizer", "Enabled"); + for(int i = 0; i < 8; i ++) + { + std::stringstream ss; + ss << "Mult" << i; + if(keyFile.has_key ("Directional Pyramid Equalizer", ss.str())) dirpyrequalizer.mult[i] = keyFile.get_integer ("Directional Pyramid Equalizer", ss.str()); + } +} // load exif change settings if (keyFile.has_group ("Exif")) { @@ -633,6 +658,17 @@ bool operator==(const EqualizerParams & a, const EqualizerParams & b) { } return true; } + +bool operator==(const DirPyrEqualizerParams & a, const DirPyrEqualizerParams & b) { + if(a.enabled != b.enabled) + return false; + + for(int i = 0; i < 8; i++) { + if(a.mult[i] != b.mult[i]) + return false; + } + return true; +} bool operator==(const ExifPair& a, const ExifPair& b) { @@ -739,6 +775,7 @@ bool ProcParams::operator== (const ProcParams& other) { && icm.working == other.icm.working && icm.output == other.icm.output && equalizer == other.equalizer + && dirpyrequalizer == other.dirpyrequalizer && exif==other.exif && iptc==other.iptc; } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 9b647db70..3a6f4ea28 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -334,6 +334,16 @@ class EqualizerParams { bool enabled; int c[8]; }; + +/** + * Directional pyramid equalizer params + */ + class DirPyrEqualizerParams { + + public: + bool enabled; + double mult[8]; + }; /** * This class holds all the processing parameters applied on the images @@ -365,6 +375,7 @@ class ProcParams { ResizeParams resize; ///< Resize parameters ColorManagementParams icm; ///< profiles/color spaces used during the image processing EqualizerParams equalizer; ///< wavelet equalizer parameters + DirPyrEqualizerParams dirpyrequalizer;///< directional pyramid equalizer parameters std::vector exif; ///< List of modifications appplied on the exif tags of the input image std::vector iptc; ///< The IPTC tags and values to be saved to the output image int version; ///< Version of the file from which the parameters have been read diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 0e4994adb..a0c478b61 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -110,6 +110,8 @@ IMPULSEDENOISE, // EvIDNThresh, DIRPYRDENOISE, // EvDPDNEnabled, DIRPYRDENOISE, // EvDPDNLuma, DIRPYRDENOISE, // EvDPDNChroma, -DIRPYRDENOISE // EvDPDNGamma, +DIRPYRDENOISE, // EvDPDNGamma, +DIRPYREQUALIZER, // EvDirPyrEqualizer +DIRPYREQUALIZER // EvDirPyrEqlEnabled }; diff --git a/rtengine/refreshmap.h b/rtengine/refreshmap.h index 90073c563..0364f3a32 100644 --- a/rtengine/refreshmap.h +++ b/rtengine/refreshmap.h @@ -39,6 +39,7 @@ #define EXIF 32768 #define IPTC 32768 #define EQUALIZER 3 +#define DIRPYREQUALIZER 3 #define NONE 0 #define M_INIT 128 diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index de7ed1584..6a65aac59 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -163,6 +163,9 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p // wavelet equalizer ipf.waveletEqualizer (labView, true, true); + + // directional pyramid equalizer + ipf.dirpyrequalizer (labView); for (int i=0; i. + * + * © 2010 Emil Martinec + */ + +#include + +using namespace rtengine; +using namespace rtengine::procparams; + +DirPyrEqualizer::DirPyrEqualizer () : ToolPanel() { + + enabled = Gtk::manage (new Gtk::CheckButton (M("GENERAL_ENABLED"))); + enabled->set_active (true); + pack_start(*enabled); + enaConn = enabled->signal_toggled().connect( sigc::mem_fun(*this, &DirPyrEqualizer::enabledToggled) ); + + Gtk::HSeparator *separator1 = Gtk::manage (new Gtk::HSeparator()); + pack_start(*separator1, Gtk::PACK_SHRINK, 2); + + Gtk::HBox * buttonBox1 = Gtk::manage (new Gtk::HBox()); + pack_start(*buttonBox1, Gtk::PACK_SHRINK, 2); + + Gtk::Button * lumacontrastMinusButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS"))); + buttonBox1->pack_start(*lumacontrastMinusButton, Gtk::PACK_SHRINK, 2); + lumacontrastMinusPressedConn = lumacontrastMinusButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::lumacontrastMinusPressed)); + + Gtk::Button * lumaneutralButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMANEUTRAL"))); + buttonBox1->pack_start(*lumaneutralButton, Gtk::PACK_SHRINK, 2); + lumaneutralPressedConn = lumaneutralButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::lumaneutralPressed)); + + Gtk::Button * lumacontrastPlusButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMACONTRAST_PLUS"))); + buttonBox1->pack_start(*lumacontrastPlusButton, Gtk::PACK_SHRINK, 2); + lumacontrastPlusPressedConn = lumacontrastPlusButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::lumacontrastPlusPressed)); + + buttonBox1->show_all_children(); + + Gtk::HSeparator *separator2 = Gtk::manage (new Gtk::HSeparator()); + pack_start(*separator2, Gtk::PACK_SHRINK, 2); + + for(int i = 0; i < 4; i++) + { + std::stringstream ss; + ss << i; + if(i == 0) + ss << " (" << M("TP_DIRPYREQUALIZER_LUMAFINEST") << ")"; + if(i == 3) + ss << " (" << M("TP_DIRPYREQUALIZER_LUMACOARSEST") << ")"; + + multiplier[i] = new Adjuster (ss.str(), 0, 4, 0.01, 1.0); + multiplier[i]->setAdjusterListener(this); + pack_start(*multiplier[i]); + } + + show_all_children (); + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Gtk::HSeparator *separator3 = Gtk::manage (new Gtk::HSeparator()); + pack_start(*separator3, Gtk::PACK_SHRINK, 2); + + Gtk::HBox * buttonBox2 = Gtk::manage (new Gtk::HBox()); + pack_start(*buttonBox2, Gtk::PACK_SHRINK, 2); + + Gtk::Button * chromacontrastMinusButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_CHROMACONTRAST_MINUS"))); + buttonBox2->pack_start(*chromacontrastMinusButton, Gtk::PACK_SHRINK, 2); + chromacontrastMinusPressedConn = chromacontrastMinusButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::chromacontrastMinusPressed)); + + Gtk::Button * chromaneutralButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_CHROMANEUTRAL"))); + buttonBox2->pack_start(*chromaneutralButton, Gtk::PACK_SHRINK, 2); + chromaneutralPressedConn = chromaneutralButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::chromaneutralPressed)); + + Gtk::Button * chromacontrastPlusButton = Gtk::manage (new Gtk::Button(M("TP_DIRPYREQUALIZER_CHROMACONTRAST_PLUS"))); + buttonBox2->pack_start(*chromacontrastPlusButton, Gtk::PACK_SHRINK, 2); + chromacontrastPlusPressedConn = chromacontrastPlusButton->signal_pressed().connect( sigc::mem_fun(*this, &DirPyrEqualizer::chromacontrastPlusPressed)); + + buttonBox2->show_all_children(); + + Gtk::HSeparator *separator4 = Gtk::manage (new Gtk::HSeparator()); + pack_start(*separator4, Gtk::PACK_SHRINK, 2); + + for(int i = 0; i < 4; i++) + { + std::stringstream ss; + ss << i; + if(i == 0) + ss << " (" << M("TP_DIRPYREQUALIZER_CHROMAFINEST") << ")"; + if(i == 3) + ss << " (" << M("TP_DIRPYREQUALIZER_CHROMACOARSEST") << ")"; + + multiplier[i+4] = new Adjuster (ss.str(), 0, 4, 0.01, 1.0); + multiplier[i+4]->setAdjusterListener(this); + pack_start(*multiplier[i+4]); + } + + show_all_children (); + + +} + +DirPyrEqualizer::~DirPyrEqualizer () { + +} + +void DirPyrEqualizer::read (const ProcParams* pp, const ParamsEdited* pedited) { + + disableListener (); + + if (pedited) { + + enabled->set_inconsistent (!pedited->dirpyrequalizer.enabled); + + for(int i = 0; i < 8; i++) { + multiplier[i]->setEditedState (pedited->dirpyrequalizer.mult[i] ? Edited : UnEdited); + } + } + + enaConn.block (true); + enabled->set_active (pp->dirpyrequalizer.enabled); + enaConn.block (false); + lastEnabled = pp->dirpyrequalizer.enabled; + + for (int i = 0; i < 8; i++) { + multiplier[i]->setValue(pp->dirpyrequalizer.mult[i]); + } + + enableListener (); +} + +void DirPyrEqualizer::write (ProcParams* pp, ParamsEdited* pedited) { + + pp->dirpyrequalizer.enabled = enabled->get_active (); + + for (int i = 0; i < 8; i++) { + pp->dirpyrequalizer.mult[i] = (int) multiplier[i]->getValue(); + } + + if (pedited) { + + pedited->dirpyrequalizer.enabled = !enabled->get_inconsistent(); + + for(int i = 0; i < 8; i++) { + pedited->dirpyrequalizer.mult[i] = multiplier[i]->getEditedState(); + } + } +} + +void DirPyrEqualizer::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { + + for (int i = 0; i < 8; i++) { + multiplier[i]->setDefault(defParams->dirpyrequalizer.mult[i]); + } + + if (pedited) { + for (int i = 0; i < 8; i++) { + multiplier[i]->setDefaultEditedState(pedited->dirpyrequalizer.mult[i] ? Edited : UnEdited); + } + } + else { + for (int i = 0; i < 8; i++) { + multiplier[i]->setDefaultEditedState(Irrelevant); + } + } +} + +void DirPyrEqualizer::setBatchMode (bool batchMode) { + + ToolPanel::setBatchMode (batchMode); + + for (int i = 0; i < 8; i++) { + multiplier[i]->showEditedCB(); + } +} + +void DirPyrEqualizer::adjusterChanged (Adjuster* a, double newval) { + + if (listener && enabled->get_active()) { + std::stringstream ss; + ss << "("; + int i; + for (i = 0; i < 8; i++) { + if (i > 0) { + ss << ", "; + } + ss << static_cast(multiplier[i]->getValue()); + } + ss << ")"; + listener->panelChanged (EvDirPyrEqualizer, ss.str()); + } +} + +void DirPyrEqualizer::enabledToggled () { + + if (batchMode) { + if (enabled->get_inconsistent()) { + enabled->set_inconsistent (false); + enaConn.block (true); + enabled->set_active (false); + enaConn.block (false); + } + else if (lastEnabled) + enabled->set_inconsistent (true); + + lastEnabled = enabled->get_active (); + } + + if (listener) { + if (enabled->get_active ()) + listener->panelChanged (EvDirPyrEqlEnabled, M("GENERAL_ENABLED")); + else + listener->panelChanged (EvDirPyrEqlEnabled, M("GENERAL_DISABLED")); + } +} + + +void DirPyrEqualizer::lumaneutralPressed () { + + for (int i = 0; i < 4; i++) { + multiplier[i]->setValue(1.0); + adjusterChanged(multiplier[i], 1.0); + } +} + +void DirPyrEqualizer::chromaneutralPressed () { + + for (int i = 4; i < 8; i++) { + multiplier[i]->setValue(1.0); + adjusterChanged(multiplier[i], 1.0); + } +} + + +void DirPyrEqualizer::lumacontrastPlusPressed () { + + for (int i = 0; i < 4; i++) { + float inc = 0.05 * (4 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + inc); + adjusterChanged(multiplier[i], multiplier[i]->getValue()); + } +} + +void DirPyrEqualizer::chromacontrastPlusPressed () { + + for (int i = 4; i < 8; i++) { + float inc = 0.05 * (8 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + inc); + adjusterChanged(multiplier[i], multiplier[i]->getValue()); + } +} + +void DirPyrEqualizer::lumacontrastMinusPressed () { + + for (int i = 0; i < 4; i++) { + float inc = -0.05 * (4 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + inc); + adjusterChanged(multiplier[i], multiplier[i]->getValue()); + } +} + +void DirPyrEqualizer::chromacontrastMinusPressed () { + + for (int i = 4; i < 8; i++) { + float inc = -0.05 * (8 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + inc); + adjusterChanged(multiplier[i], multiplier[i]->getValue()); + } +} diff --git a/rtgui/dirpyrequalizer.h b/rtgui/dirpyrequalizer.h new file mode 100644 index 000000000..ba7976a96 --- /dev/null +++ b/rtgui/dirpyrequalizer.h @@ -0,0 +1,65 @@ +/* + * This file is part of RawTherapee. + * + * RawTherapee 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. + * + * RawTherapee 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 RawTherapee. If not, see . + * + * © 2010 Emil Martinec + */ + +#ifndef DIRPYREQUALIZER_H_INCLUDED +#define DIRPYREQUALIZER_H_INCLUDED + +#include +#include +#include + +class DirPyrEqualizer : public Gtk::VBox, public AdjusterListener, public ToolPanel +{ + +protected: + + Gtk::CheckButton * enabled; + Adjuster* multiplier[8]; + + sigc::connection enaConn; + sigc::connection lumaneutralPressedConn; + sigc::connection lumacontrastPlusPressedConn; + sigc::connection lumacontrastMinusPressedConn; + sigc::connection chromaneutralPressedConn; + sigc::connection chromacontrastPlusPressedConn; + sigc::connection chromacontrastMinusPressedConn; + + bool lastEnabled; + +public: + + DirPyrEqualizer (); + virtual ~DirPyrEqualizer (); + + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); + void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); + void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); + void setBatchMode (bool batchMode); + + void adjusterChanged (Adjuster* a, double newval); + void enabledToggled (); + void lumaneutralPressed (); + void lumacontrastPlusPressed (); + void lumacontrastMinusPressed (); + void chromaneutralPressed (); + void chromacontrastPlusPressed (); + void chromacontrastMinusPressed (); +}; + +#endif diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index bcdd2899e..4b5ace20c 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -851,8 +851,8 @@ bool EditorPanel::idle_sentToGimp(ProgressConnector *pc,rtengine::IImage16* void EditorPanel::saveOptions () { - options.historyPanelWidth = hpanedl->get_position (); - options.toolPanelWidth = vboxright->get_width ();//older code + //options.historyPanelWidth = hpanedl->get_position ();//older code + //options.toolPanelWidth = vboxright->get_width ();//older code //options.toolPanelWidth = hpanedr->get_position ();//Hombre's change which screws up OSX build } diff --git a/rtgui/filepanel.cc b/rtgui/filepanel.cc index fd12b3f36..43349670b 100644 --- a/rtgui/filepanel.cc +++ b/rtgui/filepanel.cc @@ -161,9 +161,9 @@ bool FilePanel::imageLoaded( Thumbnail* thm, ProgressConnectorget_position (); - options.dirBrowserHeight = placespaned->get_position (); - options.browserToolPanelWidth = get_position(); + //options.dirBrowserWidth = dirpaned->get_position (); + //options.dirBrowserHeight = placespaned->get_position (); + //options.browserToolPanelWidth = get_position(); if (options.startupDir==STARTUPDIR_LAST && fileCatalog->lastSelectedDir ()!="") options.startupPath = fileCatalog->lastSelectedDir (); fileCatalog->closeDir (); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 68c76b3d3..8bd081335 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -126,9 +126,12 @@ void ParamsEdited::set (bool v) { icm.working = v; icm.output = v; equalizer.enabled = v; + dirpyrequalizer.enabled = v; + for(int i = 0; i < 8; i++) { equalizer.c[i] = v; + dirpyrequalizer.mult[i] = v; } exif.clear (); iptc.clear (); @@ -250,6 +253,10 @@ void ParamsEdited::initFrom (const std::vector for(int i = 0; i < 8; i++) { equalizer.c[i] = equalizer.c[i] && p.equalizer.c[i] == other.equalizer.c[i]; } + dirpyrequalizer.enabled = dirpyrequalizer.enabled && p.dirpyrequalizer.enabled == other.dirpyrequalizer.enabled; + for(int i = 0; i < 8; i++) { + dirpyrequalizer.mult[i] = dirpyrequalizer.mult[i] && p.dirpyrequalizer.mult[i] == other.dirpyrequalizer.mult[i]; + } // exif = exif && p.exif==other.exif // iptc = other.iptc; } @@ -361,6 +368,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten for(int i = 0; i < 8; i++) { if(equalizer.c[i]) toEdit.equalizer.c[i] = mods.equalizer.c[i]; } + if (dirpyrequalizer.enabled) toEdit.dirpyrequalizer.enabled = mods.dirpyrequalizer.enabled; + for(int i = 0; i < 8; i++) { + if(dirpyrequalizer.mult[i]) toEdit.dirpyrequalizer.mult[i] = mods.dirpyrequalizer.mult[i]; + } // if (exif) toEdit.exif==mo.exif = mods.exif==other.exif; // if (iptc;) toEdit.iptc==other.iptc; = mods.iptc==other.iptc;; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 8f9cd9569..f7c28385c 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -239,6 +239,13 @@ class EqualizerParamsEdited { bool c[8]; }; +class DirPyrEqualizerParamsEdited { + +public: + bool enabled; + bool mult[8]; +}; + class ExifPairEdited { public: @@ -282,6 +289,7 @@ class ParamsEdited { ResizeParamsEdited resize; ColorManagementParamsEdited icm; EqualizerParamsEdited equalizer; + DirPyrEqualizerParamsEdited dirpyrequalizer; std::vector exif; std::vector iptc; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 99e69a870..e1f7059b4 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -56,6 +56,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(NULL) { exifpanel = Gtk::manage (new ExifPanel ()); iptcpanel = Gtk::manage (new IPTCPanel ()); equalizer = Gtk::manage (new Equalizer ()); + dirpyrequalizer = Gtk::manage (new DirPyrEqualizer ()); addPanel (colorPanel, whitebalance, M("TP_WBALANCE_LABEL")); toolPanels.push_back (whitebalance); addPanel (exposurePanel, curve, M("TP_EXPOSURE_LABEL")); toolPanels.push_back (curve); @@ -70,6 +71,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(NULL) { addPanel (detailsPanel, lumadenoise, M("TP_LUMADENOISE_LABEL")); toolPanels.push_back (lumadenoise); addPanel (detailsPanel, colordenoise, M("TP_COLORDENOISE_LABEL")); toolPanels.push_back (colordenoise); addPanel (detailsPanel, dirpyrdenoise, M("TP_DIRPYRDENOISE_LABEL")); toolPanels.push_back (dirpyrdenoise); + addPanel (detailsPanel, dirpyrequalizer, M("TP_DIRPYREQUALIZER_LABEL")); toolPanels.push_back (dirpyrequalizer); addPanel (detailsPanel, equalizer, M("TP_EQUALIZER_LABEL")); toolPanels.push_back (equalizer); addPanel (transformPanel, crop, M("TP_CROP_LABEL")); toolPanels.push_back (crop); addPanel (transformPanel, resize, M("TP_RESIZE_LABEL")); toolPanels.push_back (resize); diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 363c4a358..d9ba10a1e 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -54,6 +54,7 @@ #include #include #include +#include class ImageEditorCoordinator; @@ -92,6 +93,7 @@ class ToolPanelCoordinator : public ToolPanelListener, Sharpening* sharpening; LCurve* lcurve; Equalizer * equalizer; + DirPyrEqualizer * dirpyrequalizer; std::vector paramcListeners;