From 539c39a92bdfd8ac2f41c104b4c2b9334c0e47d1 Mon Sep 17 00:00:00 2001 From: Emil Martinec Date: Wed, 22 Sep 2010 15:39:00 -0500 Subject: [PATCH] Bugfix for Directional Pyramid Denoising. Adding a Directional Pyramid Equalizer tool. This one serves the same function as the existing Wavelet Equalizer, but has much less artifacting; though it is a little slower to execute and has not yet been adapted for OpenMP implementation. There are also fewer levels on which the tool operates, though of course if there was a demand that could be altered. The controls are similar, though have been given separate luma and chroma controls. Each slider adjusts the factor by which a given detail band is amplified; factors larger than one increase contrast, while values smaller than one decrease contrast. The luma control alters contrast on various scales, each successive one twice as large as the previous one. The chroma control is similar, but does less since there is typically less chroma contrast on fine scales. One might use this to restore some of the color contrast lost in NR, or to remove color fringing by making the fine scale enhancement factor much less than one. --- rtdata/languages/default | 13 +- rtengine/CA_correct_RT.cc | 5 +- rtengine/CMakeLists.txt | 2 +- rtengine/amaze_interpolate_RT.cc | 2 +- rtengine/dcrop.cc | 24 +- rtengine/dirpyrLab_denoise.cc | 178 +++++----- rtengine/dirpyrLab_equalizer.cc | 541 +++++++++++++++++++++++++++++++ rtengine/fast_demo.cc | 33 +- rtengine/improccoordinator.cc | 8 +- rtengine/improcfun.cc | 11 +- rtengine/improcfun.h | 14 +- rtengine/procevents.h | 6 +- rtengine/procparams.cc | 37 +++ rtengine/procparams.h | 11 + rtengine/refreshmap.cc | 4 +- rtengine/refreshmap.h | 1 + rtengine/simpleprocess.cc | 3 + rtgui/CMakeLists.txt | 2 +- rtgui/dirpyrequalizer.cc | 281 ++++++++++++++++ rtgui/dirpyrequalizer.h | 65 ++++ rtgui/editorpanel.cc | 4 +- rtgui/filepanel.cc | 6 +- rtgui/paramsedited.cc | 11 + rtgui/paramsedited.h | 8 + rtgui/toolpanelcoord.cc | 2 + rtgui/toolpanelcoord.h | 2 + 26 files changed, 1137 insertions(+), 137 deletions(-) create mode 100644 rtengine/dirpyrLab_equalizer.cc create mode 100644 rtgui/dirpyrequalizer.cc create mode 100644 rtgui/dirpyrequalizer.h 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;