From d62281b86f5e6f30bb3662f3472d7920b207b1ae Mon Sep 17 00:00:00 2001 From: Emil Martinec Date: Mon, 27 Sep 2010 11:04:25 -0500 Subject: [PATCH] Modified Pyramid equalizer to act only on luminance channel, and added a threshold slider to control noise (only local contrast larger than the threshold is amplified). --- rtdata/languages/default | 10 +- rtengine/CMakeLists.txt | 2 +- rtengine/dirpyr_equalizer.cc | 286 +++++++++++++++++++++++++++++++++++ rtengine/improcfun.cc | 4 +- rtengine/improcfun.h | 4 + rtengine/procparams.cc | 5 +- rtgui/dirpyrequalizer.cc | 91 +++-------- rtgui/dirpyrequalizer.h | 8 +- 8 files changed, 319 insertions(+), 91 deletions(-) create mode 100644 rtengine/dirpyr_equalizer.cc diff --git a/rtdata/languages/default b/rtdata/languages/default index da76c9387..a2acbba04 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -570,17 +570,13 @@ TP_DIRPYRDENOISE_CHROMA;Chrominance TP_DIRPYRDENOISE_GAMMA;Gamma 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_LUMACOARSEST;Coarsest TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS;Contrast- TP_DIRPYREQUALIZER_LUMACONTRAST_PLUS;Contrast+ -TP_DIRPYREQUALIZER_LUMAFINEST;luma finest +TP_DIRPYREQUALIZER_LUMAFINEST;Finest TP_DIRPYREQUALIZER_LUMANEUTRAL;Neutral TP_DIRPYREQUALIZER_LABEL;Directional pyramid equalizer +TP_DIRPYREQUALIZER_THRESHOLD;Threshold TP_DISTORTION_AMOUNT;Amount TP_DISTORTION_LABEL;Distortion TP_EQUALIZER_CONTRAST_MINUS;Contrast- diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 5fcf0f6ba..638b21613 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 dirpyrLab_equalizer.cc) + wavelet_dec.cc ipequalizer.cc dirpyrLab_denoise.cc dirpyrLab_equalizer.cc dirpyr_equalizer.cc) add_library (rtengine ${RTENGINESOURCEFILES}) #It may be nice to store library version too diff --git a/rtengine/dirpyr_equalizer.cc b/rtengine/dirpyr_equalizer.cc new file mode 100644 index 000000000..67ce8f1f0 --- /dev/null +++ b/rtengine/dirpyr_equalizer.cc @@ -0,0 +1,286 @@ +/* + * 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 DIRWT(i1,j1,i,j) ( domker[(i1-i)/scale+halfwin][(j1-j)/scale+halfwin] * rangefn[abs((int)data_fine[i1][j1]-data_fine[i][j])] ) + +namespace rtengine { + + static const int maxlevel = 4; + static const float noise = 2000; + static const float thresh = 1000; + + //sequence of scales + static const int scales[8] = {1,2,4,8,16,32,64,128}; + + //sequence of scales + //static const int scales[8] = {1,2,3,6,15,21,28,36}; + //scale is spacing of directional averaging weights + + + void ImProcFunctions :: dirpyr_equalizer(unsigned short ** src, unsigned short ** dst, int srcwidth, int srcheight, const double * mult ) + { + int lastlevel=maxlevel; + + while (fabs(mult[lastlevel-1]-1)<0.001 && lastlevel>0) { + lastlevel--; + //printf("last level to process %d \n",lastlevel); + } + if (lastlevel==0) return; + + /*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[i][j] = gamcurve[src[i][j] ]; + } + }*/ + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + int * rangefn = new int [0x20000]; + + int intfactor = 1024;//16384; + + + //set up range functions + + for (int i=0; i<0x10000; i++) { + rangefn[i] = (int)((thresh/((double)(i) + thresh))*intfactor); + //rangefn[i] = (int)(exp(-(double)abs(i)/(5*thresh))*(thresh/((double)(i) + thresh))*intfactor); + //rangefn[i] = (int)((thresh*thresh/((double)(i)*(double)(i) + thresh*thresh))*intfactor); + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + int level; + int ** buffer; + + unsigned short ** dirpyrlo[maxlevel]; + + + buffer = allocArray (srcwidth, srcheight); + + for (int i=0; i (srcwidth, srcheight); + + dirpyr_channel(src, dirpyrlo[0], srcwidth, srcheight, rangefn, 0, scale, mult ); + + level = 1; + + while(level < lastlevel) + { + scale = scales[level]; + + dirpyrlo[level] = allocArray(srcwidth, srcheight); + + dirpyr_channel(dirpyrlo[level-1], dirpyrlo[level], srcwidth, srcheight, rangefn, level, scale, mult ); + + level ++; + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + //initiate buffer for final image + for(int i = 0; i < srcheight; i++) + for(int j = 0; j < srcwidth; j++) { + + //copy pixels + buffer[i][j] = dirpyrlo[lastlevel-1][i][j]; + + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + for(int level = lastlevel - 1; level > 0; level--) + { + idirpyr_eq_channel(dirpyrlo[level], dirpyrlo[level-1], buffer, srcwidth, srcheight, level, mult ); + } + + + scale = scales[0]; + + idirpyr_eq_channel(dirpyrlo[0], dst, buffer, srcwidth, srcheight, 0, 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; iL[i][j] ]; + + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + for(int i = 0; i < lastlevel; i++) + { + freeArray(dirpyrlo[i], srcheight); + } + + freeArray(buffer, srcheight); + + delete [] rangefn; + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + }; + + void ImProcFunctions::dirpyr_channel(unsigned short ** data_fine, unsigned short ** data_coarse, int width, int height, int * rangefn, int level, int scale, const double * mult ) + { + //scale is spacing of directional averaging weights + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // calculate weights, compute directionally weighted average + + int halfwin=2; + int domker[5][5] = {{1,1,1,1,1},{1,2,2,2,1},{1,2,2,2,1},{1,2,2,2,1},{1,1,1,1,1}}; + + //generate domain kernel + if (level<2) { + halfwin = 1; + domker[1][1]=domker[1][2]=domker[2][1]=domker[2][2]=1; + } + + + int scalewin = halfwin*scale; + +#ifdef _OPENMP +#pragma omp parallel for +#endif + for(int i = 0; i < height; i++) { + for(int j = 0; j < width; j++) + { + float val=0; + float norm=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) { + float dirwt = DIRWT(inbr, jnbr, i, j); + val += dirwt*data_fine[inbr][jnbr]; + norm += dirwt; + } + } + data_coarse[i][j]=val/norm;//low pass filter + } + } + + + + + }; + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + void ImProcFunctions::idirpyr_eq_channel(unsigned short ** data_coarse, unsigned short ** data_fine, int ** buffer, int width, int height, int level, const double * mult ) + { + int hipass; + + float noisehi = 1.33*noise*mult[4]/pow(3,level), noiselo = 0.66*noise*mult[4]/pow(3,level); + float * irangefn = new float [0x20000]; + + for (int i=0; i<0x20000; i++) { + if (abs(i-0x10000)>noisehi || mult[level]<1.0) { + irangefn[i] = mult[level] ; + } else { + if (abs(i-0x10000)dirpyrequalizer.enabled && lab->W>=8 && lab->H>=8) { - dirpyrLab_equalizer(lab, lab, params->dirpyrequalizer.mult); + //dirpyrLab_equalizer(lab, lab, params->dirpyrequalizer.mult); + dirpyr_equalizer(lab->L, lab->L, lab->W, lab->H, params->dirpyrequalizer.mult); + } } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index bfbdb98f0..d065cfac3 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -104,6 +104,10 @@ class ImProcFunctions { 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 ); + void dirpyr_equalizer(unsigned short ** src, unsigned short ** dst, int srcwidth, int srcheight, const double * mult );//Emil's directional pyramid equalizer + void dirpyr_channel(unsigned short ** data_fine, unsigned short ** data_coarse, int width, int height, int * rangefn, int level, int scale, const double * mult ); + void idirpyr_eq_channel(unsigned short ** data_coarse, unsigned short ** data_fine, int ** buffer, int width, int height, int level, 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/procparams.cc b/rtengine/procparams.cc index 449494c49..62c51d30c 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -173,11 +173,12 @@ void ProcParams::setDefaults () { equalizer.c[i] = 0; } dirpyrequalizer.enabled = false; - for(int i = 0; i < 8; i ++) + for(int i = 0; i < 4; i ++) { dirpyrequalizer.mult[i] = 1.0; } - + dirpyrequalizer.mult[4] = 0.0; + exif.clear (); iptc.clear (); diff --git a/rtgui/dirpyrequalizer.cc b/rtgui/dirpyrequalizer.cc index 3cad8853b..06630b951 100644 --- a/rtgui/dirpyrequalizer.cc +++ b/rtgui/dirpyrequalizer.cc @@ -60,56 +60,24 @@ DirPyrEqualizer::DirPyrEqualizer () : ToolPanel() { 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] = 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]); - } + std::stringstream ss; + ss << M("TP_DIRPYREQUALIZER_THRESHOLD") ; + multiplier[4] = new Adjuster (ss.str(), 0, 1, 0.01, 0.0); + multiplier[4]->setAdjusterListener(this); + pack_start(*multiplier[4]); + show_all_children (); - + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% } DirPyrEqualizer::~DirPyrEqualizer () { @@ -124,7 +92,7 @@ void DirPyrEqualizer::read (const ProcParams* pp, const ParamsEdited* pedited) { enabled->set_inconsistent (!pedited->dirpyrequalizer.enabled); - for(int i = 0; i < 8; i++) { + for(int i = 0; i < 5; i++) { multiplier[i]->setEditedState (pedited->dirpyrequalizer.mult[i] ? Edited : UnEdited); } } @@ -134,7 +102,7 @@ void DirPyrEqualizer::read (const ProcParams* pp, const ParamsEdited* pedited) { enaConn.block (false); lastEnabled = pp->dirpyrequalizer.enabled; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { multiplier[i]->setValue(pp->dirpyrequalizer.mult[i]); } @@ -145,7 +113,7 @@ void DirPyrEqualizer::write (ProcParams* pp, ParamsEdited* pedited) { pp->dirpyrequalizer.enabled = enabled->get_active (); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { pp->dirpyrequalizer.mult[i] = multiplier[i]->getValue(); } @@ -153,7 +121,7 @@ void DirPyrEqualizer::write (ProcParams* pp, ParamsEdited* pedited) { pedited->dirpyrequalizer.enabled = !enabled->get_inconsistent(); - for(int i = 0; i < 8; i++) { + for(int i = 0; i < 5; i++) { pedited->dirpyrequalizer.mult[i] = multiplier[i]->getEditedState(); } } @@ -161,17 +129,17 @@ void DirPyrEqualizer::write (ProcParams* pp, ParamsEdited* pedited) { void DirPyrEqualizer::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { multiplier[i]->setDefault(defParams->dirpyrequalizer.mult[i]); } if (pedited) { - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { multiplier[i]->setDefaultEditedState(pedited->dirpyrequalizer.mult[i] ? Edited : UnEdited); } } else { - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { multiplier[i]->setDefaultEditedState(Irrelevant); } } @@ -181,7 +149,7 @@ void DirPyrEqualizer::setBatchMode (bool batchMode) { ToolPanel::setBatchMode (batchMode); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 5; i++) { multiplier[i]->showEditedCB(); } } @@ -192,7 +160,7 @@ void DirPyrEqualizer::adjusterChanged (Adjuster* a, double newval) { std::stringstream ss; ss << "("; int i; - for (i = 0; i < 8; i++) { + for (i = 0; i < 5; i++) { if (i > 0) { ss << ", "; } @@ -235,14 +203,6 @@ void DirPyrEqualizer::lumaneutralPressed () { } } -void DirPyrEqualizer::chromaneutralPressed () { - - for (int i = 4; i < 8; i++) { - multiplier[i]->setValue(1.0); - adjusterChanged(multiplier[i], 1.0); - } -} - void DirPyrEqualizer::lumacontrastPlusPressed () { @@ -253,14 +213,6 @@ void DirPyrEqualizer::lumacontrastPlusPressed () { } } -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 () { @@ -271,11 +223,4 @@ void DirPyrEqualizer::lumacontrastMinusPressed () { } } -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 index ba7976a96..ddad93034 100644 --- a/rtgui/dirpyrequalizer.h +++ b/rtgui/dirpyrequalizer.h @@ -30,15 +30,12 @@ class DirPyrEqualizer : public Gtk::VBox, public AdjusterListener, public ToolPa protected: Gtk::CheckButton * enabled; - Adjuster* multiplier[8]; + Adjuster* multiplier[5]; sigc::connection enaConn; sigc::connection lumaneutralPressedConn; sigc::connection lumacontrastPlusPressedConn; sigc::connection lumacontrastMinusPressedConn; - sigc::connection chromaneutralPressedConn; - sigc::connection chromacontrastPlusPressedConn; - sigc::connection chromacontrastMinusPressedConn; bool lastEnabled; @@ -57,9 +54,6 @@ public: void lumaneutralPressed (); void lumacontrastPlusPressed (); void lumacontrastMinusPressed (); - void chromaneutralPressed (); - void chromacontrastPlusPressed (); - void chromacontrastMinusPressed (); }; #endif