//////////////////////////////////////////////////////////////// // // // // // code dated: December , 2014 // // Ipwaveletcc is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // * 2014 Jacques Desmis // * 2014 Ingo Weyrich // //////////////////////////////////////////////////////////////// #include #include "../rtgui/threadutils.h" #include "rtengine.h" #include "improcfun.h" #include "LUT.h" #include "array2D.h" #include "boxblur.h" #include "rt_math.h" #include "mytime.h" #include "sleef.c" #include "opthelper.h" #include "StopWatch.h" #include "EdgePreservingDecomposition.h" #ifdef _OPENMP #include #endif #include "cplx_wavelet_dec.h" #define TS 64 // Tile size #define offset 25 // shift between tiles #define fTS ((TS/2+1)) // second dimension of Fourier tiles #define blkrad 1 // radius of block averaging #define PIX_SORT(a,b) { if ((a)>(b)) {temp=(a);(a)=(b);(b)=temp;} } #define med3(a0,a1,a2,a3,a4,a5,a6,a7,a8,median) { \ pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; pp[5]=a5; pp[6]=a6; pp[7]=a7; pp[8]=a8; \ PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \ PIX_SORT(pp[0],pp[1]); PIX_SORT(pp[3],pp[4]); PIX_SORT(pp[6],pp[7]); \ PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \ PIX_SORT(pp[0],pp[3]); PIX_SORT(pp[5],pp[8]); PIX_SORT(pp[4],pp[7]); \ PIX_SORT(pp[3],pp[6]); PIX_SORT(pp[1],pp[4]); PIX_SORT(pp[2],pp[5]); \ PIX_SORT(pp[4],pp[7]); PIX_SORT(pp[4],pp[2]); PIX_SORT(pp[6],pp[4]); \ PIX_SORT(pp[4],pp[2]); median=pp[4];} //pp4 = median #define med2(a0,a1,a2,a3,a4,median) { \ pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; \ PIX_SORT(pp[0],pp[1]) ; PIX_SORT(pp[3],pp[4]) ; PIX_SORT(pp[0],pp[3]) ;\ PIX_SORT(pp[1],pp[4]) ; PIX_SORT(pp[1],pp[2]) ; PIX_SORT(pp[2],pp[3]) ;\ PIX_SORT(pp[1],pp[2]) ; median=pp[2] ;} #define epsilon 0.001f/(TS*TS) //tolerance namespace rtengine { extern const Settings* settings; struct cont_params { float mul[10]; int chrom; int chro; int contrast; float th; float thH; float conres; float conresH; float chrores; float hueres; float sky; float b_l,t_l,b_r,t_r; float b_ly,t_ly,b_ry,t_ry; float b_lsl,t_lsl,b_rsl,t_rsl; float b_lhl,t_lhl,b_rhl,t_rhl; float edg_low, edg_mean, edg_sd, edg_max; float lev0s, lev0n, lev1s, lev1n, lev2s, lev2n; float b_lpast,t_lpast,b_rpast,t_rpast; float b_lsat,t_lsat,b_rsat,t_rsat; int rad; int val; int til; int numlevH, numlevS; float mulC[9]; float mulopaRG[9]; float mulopaBY[9]; bool curv; bool opaBY; bool opaRG; bool edgcurv; bool diagcurv; int CHmet; int CHSLmet; int EDmet; bool HSmet; bool avoi; float strength; int reinforce; bool detectedge; int backm; float eddet; float eddetthr; bool lips; float eddetthrHi; bool link; bool lip3; bool tonemap; bool diag; int TMmeth; float tmstrength; float balan; int ite; int contmet; bool opaW; int BAmet; bool bam; float blhigh; float grhigh; float blmed; float grmed; float bllow; float grlow; bool cbena; }; int wavNestedLevels = 1; SSEFUNCTION void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const procparams::WaveletParams & waparams, const WavCurve & wavCLVCcurve, const WavOpacityCurveRG & waOpacityCurveRG, const WavOpacityCurveBY & waOpacityCurveBY, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveWL & waOpacityCurveWL, LUTf &wavclCurve, bool wavcontlutili, int skip) { MyTime t1e,t2e ; t1e.set(); #ifdef _DEBUG // init variables to display Munsell corrections MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo(); #endif TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); double wip[3][3] = { {wiprof[0][0],wiprof[0][1],wiprof[0][2]}, {wiprof[1][0],wiprof[1][1],wiprof[1][2]}, {wiprof[2][0],wiprof[2][1],wiprof[2][2]} }; const short int imheight=lab->H, imwidth=lab->W; struct cont_params cp; cp.avoi = params->wavelet.avoid; if(params->wavelet.Medgreinf=="more") cp.reinforce = 1; if(params->wavelet.Medgreinf=="none") cp.reinforce = 2; if(params->wavelet.Medgreinf=="less") cp.reinforce = 3; cp.lip3 = params->wavelet.lipst; cp.diag = params->wavelet.tmr; cp.balan = (float)params->wavelet.balance; cp.ite = params->wavelet.iter; cp.tonemap=false; cp.bam=false; if(params->wavelet.tmrs==0) cp.tonemap=false; else cp.tonemap=true; /*else if(params->wavelet.TMmethod=="std") {cp.TMmeth=1;cp.tonemap=true;} else if(params->wavelet.TMmethod=="high") {cp.TMmeth=2;cp.tonemap=true;} else if(params->wavelet.TMmethod=="lowhigh") {cp.TMmeth=3;cp.tonemap=true;} */ if(params->wavelet.TMmethod=="cont") cp.contmet=1; else if(params->wavelet.TMmethod=="tm") cp.contmet=2; if(params->wavelet.BAmethod!="none") cp.bam=true; if(params->wavelet.BAmethod=="sli") cp.BAmet=1; if(params->wavelet.BAmethod=="cur") cp.BAmet=2; cp.tmstrength=params->wavelet.tmrs; //cp.tonemap = params->wavelet.tmr; if(params->wavelet.Backmethod=="black") cp.backm= 0; if(params->wavelet.Backmethod=="grey") cp.backm = 1; if(params->wavelet.Backmethod=="resid") cp.backm = 2; cp.link=params->wavelet.linkedg; cp.eddet=(float) params->wavelet.edgedetect; cp.eddetthr=(float) params->wavelet.edgedetectthr; cp.eddetthrHi=(float) params->wavelet.edgedetectthr2; int N=imheight*imwidth; int maxmul=params->wavelet.thres; static const float scales[10] = {1.f,2.f,4.f,8.f,16.f,32.f,64.f,128.f,256.f,512.f}; float scaleskip[10]; for(int sc=0;sc<10;sc++) scaleskip[sc]=scales[sc]/skip; float atten0 = 0.40f; float atten123=0.90f; //int DaubLen = settings->daubech ? 8 : 6; int DaubLen; if(params->wavelet.daubcoeffmethod=="2_") DaubLen=4; if(params->wavelet.daubcoeffmethod=="4_") DaubLen=6; if(params->wavelet.daubcoeffmethod=="6_") DaubLen=8; if(params->wavelet.daubcoeffmethod=="10_") DaubLen=12; if(params->wavelet.daubcoeffmethod=="14_") DaubLen=16; cp.CHSLmet=1; // if(params->wavelet.CHSLmethod=="SL") cp.CHSLmet=1; // if(params->wavelet.CHSLmethod=="CU") cp.CHSLmet=2; cp.EDmet=1; if(params->wavelet.EDmethod=="SL") cp.EDmet=1; if(params->wavelet.EDmethod=="CU") cp.EDmet=2; cp.cbena= params->wavelet.cbenab; cp.blhigh=(float)params->wavelet.bluehigh; cp.grhigh=(float)params->wavelet.greenhigh; cp.blmed=(float)params->wavelet.bluemed; cp.grmed=(float)params->wavelet.greenmed; cp.bllow=(float)params->wavelet.bluelow; cp.grlow=(float)params->wavelet.greenlow; cp.curv=false; cp.edgcurv=false; cp.diagcurv=false; cp.opaRG=false; cp.opaBY=false; cp.opaW=false; cp.CHmet=0; cp.HSmet=false; if(params->wavelet.CHmethod=="with") cp.CHmet=1; if(params->wavelet.CHmethod=="link") cp.CHmet=2; if(params->wavelet.HSmethod=="with") cp.HSmet=true; cp.strength = min(1.f,max(0.f,((float)params->wavelet.strength / 100.f))); for(int m=0;mverbose) printf("Wav mul 0=%f 1=%f 2=%f 3=%f 4=%f 5=%f 6=%f 7=%f 8=%f 9=%f\n",cp.mul[0],cp.mul[1],cp.mul[2],cp.mul[3],cp.mul[4],cp.mul[5],cp.mul[6],cp.mul[7],cp.mul[8],cp.mul[9]); for(int sc=0;sc<9;sc++) {//reduce strength if zoom < 100% for chroma and tuning if(sc==0) {if(scaleskip[sc] < 1.f) {cp.mulC[sc]*=(atten0*scaleskip[sc]);cp.mulopaRG[sc]*=(atten0*scaleskip[sc]);cp.mulopaBY[sc]*=(atten0*scaleskip[sc]);}} else {if(scaleskip[sc] < 1.f) {cp.mulC[sc]*=(atten123*scaleskip[sc]);cp.mulopaRG[sc]*=(atten123*scaleskip[sc]);cp.mulopaBY[sc]*=(atten123*scaleskip[sc]);}} } cp.chro=waparams.chro; cp.chrom=waparams.chroma; cp.contrast=waparams.contrast; cp.rad=waparams.edgrad; cp.val=waparams.edgval; cp.til=waparams.edgthresh; cp.conres=waparams.rescon; cp.conresH=waparams.resconH; cp.chrores=waparams.reschro; //cp.hueres=waparams.reshue; cp.hueres=2.f; cp.th=float(waparams.thr); cp.thH=float(waparams.thrH); cp.sky=waparams.sky; //skin cp.b_l = static_cast(params->wavelet.hueskin.value[0]) / 100.0f; cp.t_l = static_cast(params->wavelet.hueskin.value[1]) / 100.0f; cp.b_r = static_cast(params->wavelet.hueskin.value[2]) / 100.0f; cp.t_r = static_cast(params->wavelet.hueskin.value[3]) / 100.0f; cp.b_ly = static_cast(params->wavelet.hueskin2.value[0]) / 100.0f; cp.t_ly = static_cast(params->wavelet.hueskin2.value[1]) / 100.0f; cp.b_ry = static_cast(params->wavelet.hueskin2.value[2]) / 100.0f; cp.t_ry = static_cast(params->wavelet.hueskin2.value[3]) / 100.0f; cp.numlevH=params->wavelet.threshold; //shadows cp.b_lsl = static_cast(params->wavelet.bllev.value[0]); cp.t_lsl = static_cast(params->wavelet.bllev.value[1]); cp.b_rsl = static_cast(params->wavelet.bllev.value[2]); cp.t_rsl = static_cast(params->wavelet.bllev.value[3]); cp.numlevS=params->wavelet.threshold2; int maxlevS=9-cp.numlevH; cp.numlevS = MIN(cp.numlevS,maxlevS); //printf("levHigh=%d levShad=%d\n",cp.numlevH,cp.numlevS); //highlight cp.b_lhl = static_cast(params->wavelet.hllev.value[0]); cp.t_lhl = static_cast(params->wavelet.hllev.value[1]); cp.b_rhl = static_cast(params->wavelet.hllev.value[2]); cp.t_rhl = static_cast(params->wavelet.hllev.value[3]); //printf("BL=%f TL=%f BR=%f TR=%f\n",cp.b_lhl,cp.t_lhl,cp.b_rhl,cp.t_rhl); //pastel cp.b_lpast = static_cast(params->wavelet.pastlev.value[0]); cp.t_lpast = static_cast(params->wavelet.pastlev.value[1]); cp.b_rpast = static_cast(params->wavelet.pastlev.value[2]); cp.t_rpast = static_cast(params->wavelet.pastlev.value[3]); //saturated cp.b_lsat = static_cast(params->wavelet.satlev.value[0]); cp.t_lsat = static_cast(params->wavelet.satlev.value[1]); cp.b_rsat = static_cast(params->wavelet.satlev.value[2]); cp.t_rsat = static_cast(params->wavelet.satlev.value[3]); //edge local contrast cp.edg_low = static_cast(params->wavelet.edgcont.value[0]); cp.edg_mean = static_cast(params->wavelet.edgcont.value[1]); cp.edg_max = static_cast(params->wavelet.edgcont.value[2]); cp.edg_sd = static_cast(params->wavelet.edgcont.value[3]); //level noise cp.lev0s =static_cast(params->wavelet.level0noise.value[0]); cp.lev0n =static_cast(params->wavelet.level0noise.value[1]); cp.lev1s =static_cast(params->wavelet.level1noise.value[0]); cp.lev1n =static_cast(params->wavelet.level1noise.value[1]); cp.lev2s =static_cast(params->wavelet.level2noise.value[0]); cp.lev2n =static_cast(params->wavelet.level2noise.value[1]); cp.detectedge = false; cp.detectedge = params->wavelet.medianlev; //printf("low=%f mean=%f sd=%f max=%f\n",cp.edg_low,cp.edg_mean,cp.edg_sd,cp.edg_max); int minwin=min(imwidth,imheight); int maxlevelcrop=9; if(cp.mul[9]!=0) maxlevelcrop=10; // adap maximum level wavelet to size of crop if(minwin*skip < 1024) maxlevelcrop = 9;//sampling wavelet 512 if(minwin*skip < 512) maxlevelcrop = 8;//sampling wavelet 256 if(minwin*skip < 256) maxlevelcrop = 7;//sampling 128 if(minwin*skip < 128) maxlevelcrop = 6; if(minwin < 64) maxlevelcrop = 5; // printf("minwin=%d maxcrop=%d\n",minwin, maxlevelcrop); int levwav=params->wavelet.thres; if(levwav==9 && cp.mul[9]!=0) levwav=10; levwav=min(maxlevelcrop,levwav); // determine number of levels to process. // for(levwav=min(maxlevelcrop,levwav);levwav>0;levwav--) // if(cp.mul[levwav-1]!=0.f || cp.curv) // if(cp.mul[levwav-1]!=0.f) // break; // I suppress this fonctionality ==> crash for level < 3 if(levwav<1) return; // nothing to do //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // begin tile processing of image //output buffer int realtile; if(params->wavelet.Tilesmethod=="big") realtile=22; if(params->wavelet.Tilesmethod=="lit") realtile=12; int tilesize; int overlap; tilesize = 1024; overlap = 128; //tilesize=128*params->wavelet.tiles; tilesize=128*realtile; //overlap=(int) tilesize*params->wavelet.overl; overlap=(int) tilesize*0.125f; // printf("overl=%d\n",overlap); int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; if(params->wavelet.Tilesmethod=="full") kall=0; Tile_calc (tilesize, overlap, kall, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); const int numtiles = numtiles_W*numtiles_H; LabImage * dsttmp; if(numtiles == 1) { dsttmp = dst; } else { dsttmp = new LabImage(imwidth,imheight); for (int n=0; n<3*imwidth*imheight; n++) dsttmp->data[n] = 0; } //now we have tile dimensions, overlaps //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int minsizetile=min(tilewidth, tileheight); int maxlev2=10; if(minsizetile < 1024 && levwav==10) maxlev2 = 9; if(minsizetile < 512) maxlev2 = 8; if(minsizetile < 256) maxlev2 = 7; if(minsizetile < 128) maxlev2 = 6; levwav=min(maxlev2,levwav); //printf("levwav = %d\n",levwav); int numthreads = 1; int maxnumberofthreadsforwavelet =0; //reduce memory for big tile size if(kall!=0) { if(realtile <= 22) maxnumberofthreadsforwavelet=2; if(realtile <= 20) maxnumberofthreadsforwavelet=3; if(realtile <= 18) maxnumberofthreadsforwavelet=4; if(realtile <= 16) maxnumberofthreadsforwavelet=6; if(realtile <= 14) maxnumberofthreadsforwavelet=8; //printf("maxNRT=%d\n",maxnumberofthreadsforwavelet); if((maxnumberofthreadsforwavelet==6 || maxnumberofthreadsforwavelet==8) && levwav==10) maxnumberofthreadsforwavelet-=2; if(levwav <=7 && maxnumberofthreadsforwavelet ==8) maxnumberofthreadsforwavelet=0; } //printf("maxthre=%d\n",maxnumberofthreadsforwavelet); #ifdef _OPENMP // Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles if( options.rgbDenoiseThreadLimit>0) maxnumberofthreadsforwavelet = min(max(options.rgbDenoiseThreadLimit / 2, 1), maxnumberofthreadsforwavelet); numthreads = MIN(numtiles,omp_get_max_threads()); if(maxnumberofthreadsforwavelet > 0) numthreads = MIN(numthreads,maxnumberofthreadsforwavelet); #ifdef _RT_NESTED_OPENMP wavNestedLevels = omp_get_max_threads() / numthreads; bool oldNested = omp_get_nested(); if(wavNestedLevels < 2) wavNestedLevels = 1; else omp_set_nested(true); if(maxnumberofthreadsforwavelet > 0) while(wavNestedLevels*numthreads > maxnumberofthreadsforwavelet) wavNestedLevels--; #endif if(settings->verbose) printf("Ip Wavelet uses %d main thread(s) and up to %d nested thread(s) for each main thread\n",numthreads,wavNestedLevels); #endif #ifdef _OPENMP #pragma omp parallel num_threads(numthreads) #endif { float *mean = new float [9]; float *meanN = new float [9]; float *sigma = new float [9]; float *sigmaN = new float [9]; float *MaxP = new float [9]; float *MaxN = new float [9]; float** varhue = new float*[tileheight]; for (int i=0; i we can use output buffer for labco labco = dst; if(cp.avoi) { // we need a buffer to hold a copy of the L channel Lold = new float*[tileheight]; LoldBuffer = new float[tilewidth*tileheight]; memcpy(LoldBuffer, lab->L[0], tilewidth*tileheight*sizeof(float)); for (int i=0; iL; } #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif for (int i=tiletop; ia[i][j]); bv = LVFU(lab->b[i][j]); huev = xatan2f(bv,av); chrov = _mm_sqrt_ps(SQRV(av)+SQRV(bv)) / c327d68v; _mm_storeu_ps(&varhue[i1][j1],huev); _mm_storeu_ps(&varchro[i1][j1], chrov); if(labco != lab) { _mm_storeu_ps(&(labco->L[i1][j1]),LVFU(lab->L[i][j])); _mm_storeu_ps(&(labco->a[i1][j1]),av); _mm_storeu_ps(&(labco->b[i1][j1]),bv); } } #else j=tileleft; #endif for (; ja[i][j]; float b=lab->b[i][j]; varhue[i1][j1]=xatan2f(b,a); varchro[i1][j1]=(sqrtf(a*a+b*b))/327.68f; if(labco != lab) { labco->L[i1][j1] = lab->L[i][j]; labco->a[i1][j1] = a; labco->b[i1][j1] = b; } } } //to avoid artifacts in blue sky if(params->wavelet.median) { float** tmL; int wid=labco->W; int hei=labco->H; int borderL = 1; tmL = new float*[hei]; for (int i=0; iL[i][j]; } } #ifdef _RT_NESTED_OPENMP #pragma omp parallel for num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif for (int i=1; i - 2.5f) && (varchro[i][j] > 15.f && varchro[i][j] < 55.f) && labco->L[i][j] > 6000.f) //blue sky + med3x3 ==> after for more effect use denoise med3(labco->L[i][j] ,labco->L[i-1][j], labco->L[i+1][j] ,labco->L[i][j+1],labco->L[i][j-1], labco->L[i-1][j-1],labco->L[i-1][j+1],labco->L[i+1][j-1],labco->L[i+1][j+1],tmL[i][j]);//3x3 } } for(int i = borderL; i < hei-borderL; i++ ) { for(int j = borderL; j < wid-borderL; j++) { labco->L[i][j] = tmL[i][j]; } } for (int i=0; iW * labco->H; int levwavL = levwav; bool ref0=false; if(cp.lev0s > 0.f || cp.lev1s > 0.f || cp.lev2s > 0.f) ref0=true; // printf("LevwavL before: %d\n",levwavL); if(cp.contrast == 0.f && cp.tonemap==false && cp.conres == 0.f && cp.conresH == 0.f && cp.val ==0 && !ref0 && params->wavelet.CLmethod=="all") { // no processing of residual L or edge=> we probably can reduce the number of levels while(levwavL > 0 && cp.mul[levwavL-1] == 0.f) // cp.mul[level] == 0.f means no changes to level levwavL--; } // printf("LevwavL after: %d\n",levwavL); if(levwavL < 3) levwavL=3;//to allow edge => I always allocate 3 levels..because if user select wavelet it is to do something !! if(levwavL > 0) { wavelet_decomposition* Ldecomp = new wavelet_decomposition (labco->data, labco->W, labco->H, levwavL, 1, skip, max(1,wavNestedLevels), DaubLen ); if(!Ldecomp->memoryAllocationFailed) { float madL[8][3]; bool memoryAllocationFailed = false; #ifdef _RT_NESTED_OPENMP #pragma omp parallel for schedule(dynamic) collapse(2) num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif for (int lvl=0; lvl<3; lvl++) { for (int dir=1; dir<4; dir++) { int Wlvl_L = Ldecomp->level_W(lvl); int Hlvl_L = Ldecomp->level_H(lvl); float ** WavCoeffs_L = Ldecomp->level_coeffs(lvl); madL[lvl][dir-1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L*Hlvl_L)); } } int ind=0; bool ref=false; if(cp.lev0s > 0.f || cp.lev1s > 0.f || cp.lev2s > 0.f) ref=true; bool contr=false; for(int f=0;f 0 || ref || contr) {//edge Evaluate2(*Ldecomp, cp, ind, mean, meanN, sigma, sigmaN, MaxP, MaxN, madL); } //init for edge and denoise float vari[3]; vari[0]=8.f*SQR((cp.lev0n/125.0)*(1.0+ cp.lev0n/25.0)); vari[1]=8.f*SQR((cp.lev1n/125.0)*(1.0+ cp.lev1n/25.0)); vari[2]=8.f*SQR((cp.lev2n/125.0)*(1.0+ cp.lev2n/25.0)); int edge=1; if(cp.lev0n > 0.1f || cp.lev1n > 0.1f || cp.lev2n > 0.1f) { vari[0] = max(0.0001f,vari[0]); vari[1] = max(0.0001f,vari[1]); vari[2] = max(0.0001f,vari[2]); float* noisevarlum = NULL; // we need a dummy to pass it to WaveletDenoiseAllL if(!WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, vari, edge))// memoryAllocationFailed = true; } ind=1; //Flat curve for Contrast=f(H) in levels FlatCurve* ChCurve = NULL;//curve C=f(H) bool Chutili = false; ChCurve = new FlatCurve(params->wavelet.Chcurve); if (!ChCurve || ChCurve->isIdentity()) { if (ChCurve) { delete ChCurve; ChCurve = NULL; } } else Chutili = true; WaveletcontAllL(labco, varhue, varchro, *Ldecomp, cp, skip, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveWL, ChCurve, Chutili); if(cp.val > 0 || ref || contr || cp.diagcurv) {//edge Evaluate2(*Ldecomp, cp, ind, mean, meanN, sigma, sigmaN, MaxP, MaxN, madL); } WaveletcontAllLfinal(labco, varhue, varchro, *Ldecomp, cp, skip, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavCLVCcurve, waOpacityCurveWL, ChCurve, Chutili); //Evaluate2(*Ldecomp, cp, ind, mean, meanN, sigma, sigmaN, MaxP, MaxN, madL); Ldecomp->reconstruct(labco->data, cp.strength); } delete Ldecomp; } //Flat curve for H=f(H) in residual image FlatCurve* hhCurve = NULL;//curve H=f(H) bool hhutili = false; hhCurve = new FlatCurve(params->wavelet.hhcurve); if (!hhCurve || hhCurve->isIdentity()) { if (hhCurve) { delete hhCurve; hhCurve = NULL; } } else hhutili = true; if(!hhutili) {//always a or b int levwava = levwav; // printf("Levwava before: %d\n",levwava); if(cp.chrores == 0.f && params->wavelet.CLmethod=="all" && !cp.cbena) { // no processing of residual ab => we probably can reduce the number of levels while(levwava > 0 && !cp.diag &&(((cp.CHmet==2 && (cp.chro == 0.f || cp.mul[levwava-1] == 0.f )) || (cp.CHmet!=2 && (levwava == 10 || (!cp.curv || (cp.curv && cp.mulC[levwava-1] == 0.f)))))) && (!cp.opaRG || levwava == 10 || (cp.opaRG && cp.mulopaRG[levwava-1] == 0.f)) && ((levwava == 10 ||(cp.CHSLmet==1 && cp.mulC[levwava-1] == 0.f)))) { levwava--; } } //printf("Levwava after: %d\n",levwava); if(levwava > 0) { wavelet_decomposition* adecomp = new wavelet_decomposition (labco->data+datalen, labco->W, labco->H,levwava, 1, skip, max(1,wavNestedLevels), DaubLen ); if(!adecomp->memoryAllocationFailed) { WaveletcontAllAB(labco, varhue, varchro, *adecomp, waOpacityCurveW, cp, true); adecomp->reconstruct(labco->data+datalen, cp.strength); } delete adecomp; } int levwavb = levwav; //printf("Levwavb before: %d\n",levwavb); if(cp.chrores == 0.f && params->wavelet.CLmethod=="all" && !cp.cbena) { // no processing of residual ab => we probably can reduce the number of levels while(levwavb > 0 && !cp.diag && (((cp.CHmet==2 && (cp.chro == 0.f || cp.mul[levwavb-1] == 0.f )) || (cp.CHmet!=2 && (levwavb == 10 || (!cp.curv || (cp.curv && cp.mulC[levwavb-1] == 0.f)))))) && (!cp.opaBY || levwavb == 10 || (cp.opaBY && cp.mulopaBY[levwavb-1] == 0.f)) && ((levwavb == 10 ||(cp.CHSLmet==1 && cp.mulC[levwavb-1] == 0.f)))) { levwavb--; } } // printf("Levwavb after: %d\n",levwavb); if(levwavb > 0) { wavelet_decomposition* bdecomp = new wavelet_decomposition (labco->data+2*datalen, labco->W, labco->H, levwavb, 1, skip, max(1,wavNestedLevels), DaubLen ); if(!bdecomp->memoryAllocationFailed) { WaveletcontAllAB(labco, varhue, varchro, *bdecomp, waOpacityCurveW, cp, false); bdecomp->reconstruct(labco->data+2*datalen, cp.strength); } delete bdecomp; } } else {// a and b int levwavab = levwav; // printf("Levwavab before: %d\n",levwavab); if(cp.chrores == 0.f && !hhutili && params->wavelet.CLmethod=="all") { // no processing of residual ab => we probably can reduce the number of levels while(levwavab > 0 && (((cp.CHmet==2 && (cp.chro == 0.f || cp.mul[levwavab-1] == 0.f )) || (cp.CHmet!=2 && (levwavab == 10 || (!cp.curv || (cp.curv && cp.mulC[levwavab-1] == 0.f)))))) && (!cp.opaRG || levwavab == 10 || (cp.opaRG && cp.mulopaRG[levwavab-1] == 0.f)) && ((levwavab == 10 ||(cp.CHSLmet==1 && cp.mulC[levwavab-1] == 0.f)))) { levwavab--; } } // printf("Levwavab after: %d\n",levwavab); if(levwavab > 0) { wavelet_decomposition* adecomp = new wavelet_decomposition (labco->data+datalen, labco->W, labco->H,levwavab, 1, skip, max(1,wavNestedLevels), DaubLen ); wavelet_decomposition* bdecomp = new wavelet_decomposition (labco->data+2*datalen, labco->W, labco->H, levwavab, 1, skip, max(1,wavNestedLevels), DaubLen ); if(!adecomp->memoryAllocationFailed && !bdecomp->memoryAllocationFailed) { WaveletcontAllAB(labco, varhue, varchro, *adecomp, waOpacityCurveW, cp, true); WaveletcontAllAB(labco, varhue, varchro, *bdecomp, waOpacityCurveW, cp, false); WaveletAandBAllAB(labco, varhue, varchro, *adecomp, *bdecomp, cp, waOpacityCurveW, hhCurve, hhutili ); adecomp->reconstruct(labco->data+datalen, cp.strength); bdecomp->reconstruct(labco->data+2*datalen, cp.strength); } delete adecomp; delete bdecomp; } } if (hhCurve) delete hhCurve; if(numtiles > 1 || (numtiles == 1 /*&& cp.avoi*/)) {//in all case since I add contrast curve //calculate mask for feathering output tile overlaps float Vmask[height+overlap] ALIGNED16; float Hmask[width+overlap] ALIGNED16; if(numtiles > 1) { for (int i=0; i0) Vmask[i] = mask; if (tilebottom0) Hmask[i] = mask; if (tilerighttoneCurve.hrenabled; #ifdef _RT_NESTED_OPENMP #pragma omp parallel for schedule(dynamic,16) num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif for (int i=tiletop; ia[i1][col]); bv = LVFU(labco->b[i1][col]); _mm_store_ps(&atan2Buffer[col],xatan2f(bv,av)); cv = _mm_sqrt_ps(SQRV(av)+SQRV(bv)); yv = av / cv; xv = bv / cv; xyMask = vmaskf_eq(zerov, cv); yv = vself(xyMask, onev, yv); xv = vself(xyMask, zerov, xv); _mm_store_ps(&yBuffer[col],yv); _mm_store_ps(&xBuffer[col],xv); _mm_store_ps(&chprovBuffer[col], cv / c327d68v); } for(;cola[i1][col]; float b = labco->b[i1][col]; atan2Buffer[col] = xatan2f(b,a); float Chprov1=sqrtf(SQR(a) + SQR(b)); yBuffer[col] = (Chprov1 == 0.f) ? 1.f : a/Chprov1; xBuffer[col] = (Chprov1 == 0.f) ? 0.f : b/Chprov1; chprovBuffer[col] = Chprov1/327.68; } } #endif for (int j=tileleft; ja[i1][j1]; b = labco->b[i1][j1]; float HH=xatan2f(b,a); float Chprov1=sqrtf(SQR(a) + SQR(b)); float2 sincosv; sincosv.y = (Chprov1==0.0f) ? 1.f : a/(Chprov1); sincosv.x = (Chprov1==0.0f) ? 0.f : b/(Chprov1); Chprov1 /= 327.68f; #endif L = labco->L[i1][j1]; const float Lin=labco->L[i1][j1]; if(wavclCurve) {labco->L[i1][j1] =(0.5f*Lin + 1.5f*wavclCurve[Lin])/2.f;}//apply contrast curve L = labco->L[i1][j1]; float Lprov1=L/327.68f; float Lprov2 = Lold[i][j]/327.68f; float memChprov=varchro[i1][j1]; float R,G,B; #ifdef _DEBUG bool neg=false; bool more_rgb=false; Color::gamutLchonly(HH,sincosv, Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb); #else Color::gamutLchonly(HH,sincosv, Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f); #endif L=Lprov1*327.68f; a=327.68f*Chprov1*sincosv.y;//gamut b=327.68f*Chprov1*sincosv.x;//gamut float correctionHue=0.0f; // Munsell's correction float correctlum=0.0f; Lprov1=L/327.68f; float Chprov=sqrtf(SQR(a)+ SQR(b))/327.68f; #ifdef _DEBUG Color::AllMunsellLch(true, Lprov1,Lprov2,HH,Chprov,memChprov,correctionHue,correctlum, MunsDebugInfo); #else Color::AllMunsellLch(true, Lprov1,Lprov2,HH,Chprov,memChprov,correctionHue,correctlum); #endif if(correctionHue!=0.f || correctlum!=0.f) { // only calculate sin and cos if HH changed if(fabs(correctionHue) < 0.015f) HH+=correctlum; // correct only if correct Munsell chroma very little. sincosv = xsincosf(HH+correctionHue); } a=327.68f*Chprov*sincosv.y;// apply Munsell b=327.68f*Chprov*sincosv.x;//aply Munsell } else {//general case L = labco->L[i1][j1]; const float Lin=labco->L[i1][j1]; if(wavclCurve) {labco->L[i1][j1] = (0.5f*Lin + 1.5f*wavclCurve[Lin])/2.f;}//apply contrast curve L = labco->L[i1][j1]; a = labco->a[i1][j1]; b = labco->b[i1][j1]; } if(numtiles > 1) { float factor = Vmask[i1]*Hmask[j1]; dsttmp->L[i][j]+= factor*L; dsttmp->a[i][j]+= factor*a; dsttmp->b[i][j]+= factor*b; } else { dsttmp->L[i][j] = L; dsttmp->a[i][j] = a; dsttmp->b[i][j] = b; } } } } if(LoldBuffer != NULL) { delete [] LoldBuffer; delete [] Lold; } if(numtiles>1) delete labco; } } for (int i=0; i 1) { dst->CopyFrom(dsttmp); delete dsttmp; } if (settings->verbose) { t2e.set(); printf("Wavelet performed in %d usec:\n", t2e.etime(t1e)); } }//end o #undef TS #undef fTS #undef offset #undef epsilon void ImProcFunctions::Aver( float * RESTRICT DataList, int datalen, float &averagePlus, float &averageNeg, float &max, float &min) { //find absolute mean int countP = 0, countN = 0; float averaP = 0.f, averaN = 0.f; float thres = 5.f;//different fom zero to take into account only data large enough max=0.f; min=0.f; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif { float lmax = 0.f, lmin = 0.f; #ifdef _RT_NESTED_OPENMP #pragma omp for reduction(+:averaP,averaN,countP,countN) nowait #endif for(int i=0;i= thres) { averaP += DataList[i]; if(DataList[i]> lmax) lmax = DataList[i]; countP++; } else if(DataList[i] < -thres) { averaN += DataList[i]; if(DataList[i] < lmin) lmin = DataList[i]; countN++; } } #ifdef _RT_NESTED_OPENMP #pragma omp critical #endif { max = max > lmax ? max : lmax; min = min < lmin ? min : lmin; } } averagePlus=averaP/countP; averageNeg=averaN/countN; } void ImProcFunctions::Sigma( float * RESTRICT DataList, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg) { int countP = 0, countN = 0; float variP = 0.f, variN = 0.f; float thres = 5.f;//different fom zero to take into account only data large enough #ifdef _RT_NESTED_OPENMP #pragma omp parallel for reduction(+:variP,variN,countP,countN) num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif for(int i=0;i= thres) { variP += SQR(DataList[i] - averagePlus); countP++; } else if(DataList[i] <= -thres) { variN += SQR(DataList[i] - averageNeg); countN++; } } sigmaPlus=sqrt(variP/countP); sigmaNeg=sqrt(variN/countN); } void ImProcFunctions::Evaluate2(wavelet_decomposition &WaveletCoeffs_L, struct cont_params cp, int ind, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, float madL[8][3]){ //StopWatch Stop1("Evaluate2"); int maxlvl = WaveletCoeffs_L.maxlevel(); for (int lvl=0; lvlwavelet.thres; for (int dir=1; dir<4; dir++) { Aver(WavCoeffs_L[dir], W_L*H_L, avLP[dir], avLN[dir], maxL[dir], minL[dir]); Sigma(WavCoeffs_L[dir], W_L*H_L, avLP[dir], avLN[dir], sigP[dir], sigN[dir]); // printf("dir=%d level=%d avLP=%f max=%f avLN=%f min=%f sigP=%f sigN=%f\n",dir,level,avLP[dir] ,maxL[dir], avLN[dir] ,minL[dir], sigP[dir], sigN[dir]); } AvL=0.f;AvN=0.f;SL=0.f;SN=0.f;maxLP=0.f;maxLN=0.f;MADL=0.f; for (int dir=1; dir<4; dir++) { AvL +=avLP[dir]; AvN +=avLN[dir]; SL +=sigP[dir]; SN +=sigN[dir]; maxLP += maxL[dir]; maxLN += minL[dir]; MADL += madL[dir]; } AvL/=3; AvN/=3; SL/=3; SN/=3; maxLP/=3; maxLN/=3; MADL/=3; if(level < 3) MADL=sqrt(MADL);else MADL=0.f; mean[level]=AvL; meanN[level]=AvN; sigma[level]=SL; sigmaN[level]=SN; MaxP[level]=maxLP; MaxN[level]=maxLN; // if(params->wavelet.CLmethod!="all") {//display only if user choose different from all // printf("Ind=%d Level=%d MadL=%f AvL=%f AvN=%f SL=%f SN=%f maxP=%f maxN=%f\n",ind, level,MADL,mean[level],meanN[level],sigma[level],sigmaN[level],MaxP[level],MaxN[level]); // } } float *ImProcFunctions::ContrastDR(float *Source, int skip, struct cont_params cp, int W_L, int H_L, float Compression,float DetailBoost,float max0, float min0, float ave, float ah, float bh, float al, float bl, float factorx, float *Contrast){ int n=W_L*H_L; if(Contrast == NULL) Contrast = new float[n]; memcpy(Contrast, Source, n*sizeof(float)); #ifdef _RT_NESTED_OPENMP #pragma omp parallel for #endif for (int i=0; i ave) { float kh = ah*(Source[i]*100.f)+bh; prov=32768.f*Source[i]; Contrast[i]=ave+kh*(Source[i]*32768.f-ave); } else { float kl = al*(Source[i]*100.f)+bl; prov=32768.f*Source[i]; Contrast[i]=ave-kl*(ave-Source[i]*32768.f); } float diflc=Contrast[i]-prov; diflc*=factorx; Contrast[i] = (prov + diflc)/32768.f; //contrast between 0 and 1 } */ Contrast[i] = Source[i] ; } return Contrast; } SSEFUNCTION float *ImProcFunctions::CompressDR(float *Source, int skip, struct cont_params cp, int W_L, int H_L, float Compression,float DetailBoost,float max0, float min0, float ave, float ah, float bh, float al, float bl, float factorx, float *Compressed){ const float eps = 0.000001f; int n=W_L*H_L; #ifdef __SSE2__ #ifdef _RT_NESTED_OPENMP #pragma omp parallel #endif { __m128 epsv = _mm_set1_ps( eps ); #ifdef _RT_NESTED_OPENMP #pragma omp for #endif for(int ii = 0; ii < n-3; ii+=4) _mm_storeu_ps( &Source[ii], xlogf(LVFU(Source[ii]) + epsv)); } for(int ii = n-(n%4); ii < n; ii++) Source[ii] = xlogf(Source[ii] + eps); #else #ifdef _RT_NESTED_OPENMP #pragma omp parallel for #endif for(int ii = 0; ii < n; ii++) Source[ii] = xlogf(Source[ii] + eps); #endif float *ucr =ContrastDR(Source, skip, cp, W_L, H_L, Compression,DetailBoost,max0, min0, ave, ah, bh, al, bl, factorx); if(Compressed == NULL) Compressed = ucr; float temp; if(DetailBoost>0.f && DetailBoost < 0.05f ) { float betemp=expf(-(2.f-DetailBoost+0.694f))-1.f;//0.694 = log(2) temp = 1.2f*xlogf( -betemp); temp /= 20.f; } else if(DetailBoost>=0.05f && DetailBoost < 0.25f ) { float betemp=expf(-(2.f-DetailBoost+0.694f))-1.f;//0.694 = log(2) temp = 1.2f*xlogf( -betemp); temp /= (-75.f*DetailBoost+23.75f); } else if(DetailBoost>=0.25f) { float betemp=expf(-(2.f-DetailBoost+0.694f))-1.f;//0.694 = log(2) temp = 1.2f*xlogf( -betemp); temp /= (-2.f*DetailBoost + 5.5f); } else temp= (Compression - 1.0f)/20.f; // printf("temp=%f \n", temp); #ifdef __SSE2__ #ifdef _RT_NESTED_OPENMP #pragma omp parallel #endif { __m128 cev, uev, sourcev; __m128 epsv = _mm_set1_ps( eps ); __m128 DetailBoostv = _mm_set1_ps( DetailBoost ); __m128 tempv = _mm_set1_ps( temp ); #ifdef _RT_NESTED_OPENMP #pragma omp for #endif for(int i = 0; i < n-3; i+=4){ cev = xexpf(LVFU(Source[i]) + LVFU(ucr[i])*(tempv)) - epsv; uev = xexpf(LVFU(ucr[i])) - epsv; sourcev = xexpf(LVFU(Source[i])) - epsv; _mm_storeu_ps( &Source[i], sourcev); _mm_storeu_ps( &Compressed[i], cev + DetailBoostv * (sourcev - uev) ); } } for(int i=n-(n%4); i < n; i++){ float ce = xexpf(Source[i] + ucr[i]*(temp)) - eps; float ue = xexpf(ucr[i]) - eps; Source[i] = xexpf(Source[i]) - eps; Compressed[i] = ce + DetailBoost*(Source[i] - ue); } #else #ifdef _RT_NESTED_OPENMP #pragma omp parallel for #endif for(int i = 0; i < n; i++){ float ce = xexpf(Source[i] + ucr[i]*(temp)) - eps; float ue = xexpf(ucr[i]) - eps; Source[i] = xexpf(Source[i]) - eps; Compressed[i] = ce + DetailBoost*(Source[i] - ue); } #endif if(Compressed != ucr) delete[] ucr; return Compressed; } void ImProcFunctions::ContrastResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, struct cont_params cp, int W_L, int H_L, float max0, float min0, float ave, float ah, float bh, float al, float bl, float factorx) { float stren=cp.tmstrength; float gamm=params->wavelet.gamma; cp.TMmeth=2;//default after testing if(cp.TMmeth ==1) {min0 = 0.0f;max0=32768.f;} else if (cp.TMmeth ==2) {min0 = 0.0f;} #ifdef _RT_NESTED_OPENMP #pragma omp parallel for #endif for(int i = 0; i < W_L*H_L; i++) { WavCoeffs_L0[i]= (WavCoeffs_L0[i] - min0)/max0; WavCoeffs_L0[i]*=gamm; } float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; if(stren < 0.0f) DetailBoost = 0.0f; //Go with effect of exponent only if uncompressing. CompressDR(WavCoeffs_L0, skip, cp, W_L, H_L, Compression,DetailBoost,max0, min0, ave, ah, bh, al, bl, factorx, WavCoeffs_L0); #ifdef _RT_NESTED_OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif for(int ii = 0; ii < W_L*H_L; ii++) WavCoeffs_L0[ii] = WavCoeffs_L0[ii]*max0*(1.f/gamm) + min0; } void ImProcFunctions::EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, struct cont_params cp, int W_L, int H_L, float max0, float min0) { float stren=cp.tmstrength; float edgest=params->epd.edgeStopping; float sca=params->epd.scale; float gamm=params->wavelet.gamma; float rew=params->epd.reweightingIterates; EdgePreservingDecomposition epd = EdgePreservingDecomposition(W_L, H_L); cp.TMmeth=2;//default after testing if(cp.TMmeth ==1) {min0 = 0.0f;max0=32768.f;} else if (cp.TMmeth ==2) {min0 = 0.0f;} // max0=32768.f; #ifdef _RT_NESTED_OPENMP #pragma omp parallel for #endif for(int i = 0; i < W_L*H_L; i++) { WavCoeffs_L0[i]= (WavCoeffs_L0[i] - min0)/max0; WavCoeffs_L0[i]*=gamm; } float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; if(stren < 0.0f) DetailBoost = 0.0f; //Go with effect of exponent only if uncompressing. //Auto select number of iterates. Note that p->EdgeStopping = 0 makes a Gaussian blur. if(Iterates == 0) Iterates = (unsigned int)(edgest*15.0f); epd.CompressDynamicRange(WavCoeffs_L0, sca/float(skip), edgest, Compression, DetailBoost, Iterates, rew, WavCoeffs_L0); //Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping. #ifdef _RT_NESTED_OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif for(int ii = 0; ii < W_L*H_L; ii++) WavCoeffs_L0[ii] = WavCoeffs_L0[ii]*max0*(1.f/gamm) + min0; } void ImProcFunctions::WaveletcontAllLfinal(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition &WaveletCoeffs_L, struct cont_params cp, int skip, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveWL & waOpacityCurveWL, FlatCurve* ChCurve, bool Chutili){ int maxlvl = WaveletCoeffs_L.maxlevel(); int W_L = WaveletCoeffs_L.level_W(0); int H_L = WaveletCoeffs_L.level_H(0); float * WavCoeffs_L0 = WaveletCoeffs_L.coeff0; #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int dir=1; dir<4; dir++) { for (int lvl=0; lvl1) #endif for (int i=0; i1) #endif { float lminL = FLT_MAX; float lmaxL = 0.f; #ifdef _RT_NESTED_OPENMP #pragma omp for #endif for(int i = 0; i < W_L*H_L; i++) { if(WavCoeffs_L0[i] < lminL) lminL = WavCoeffs_L0[i]; if(WavCoeffs_L0[i] > lmaxL) lmaxL = WavCoeffs_L0[i]; } #ifdef _RT_NESTED_OPENMP #pragma omp critical #endif { if(lminL < min0) min0 = lminL; if(lmaxL > max0) max0 = lmaxL; } } } // printf("MAXmax0=%f MINmin0=%f\n",max0,min0); //tone mapping if(cp.tonemap && cp.contmet==2) { //iterate = 5 EPDToneMapResid(WavCoeffs_L0, 5, skip, cp, W_L, H_L, max0, min0); } //end tonemapping max0/=327.68f; min0/=327.68f; float ave = avedbl / (double)(W_L*H_L); float av=ave/327.68f; float ah=(multH-1.f)/(av-max0);// float bh=1.f-max0*ah; float al=(multL-1.f)/(av-min0); float bl=1.f-min0*al; float factorx=1.f; float *koeLi[9]; float maxkoeLi[9]; float *koeLibuffer = NULL; bool lipschitz =false; lipschitz=true; if(lipschitz==true) { for(int y=0;y<9;y++) maxkoeLi[y]=0.f; koeLibuffer = new float[9*H_L*W_L]; for (int i=0; i<9; i++) { koeLi[i] = &koeLibuffer[i*W_L*H_L]; } for(int j=0;j<9;j++) for (int i=0; i1) #endif { if(contrast != 0.f) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step { #ifdef _RT_NESTED_OPENMP #pragma omp for #endif for (int i=0; i ave) { float kh = ah*(WavCoeffs_L0[i]/327.68f)+bh; prov=WavCoeffs_L0[i]; WavCoeffs_L0[i]=ave+kh*(WavCoeffs_L0[i]-ave); } else { float kl = al*(WavCoeffs_L0[i]/327.68f)+bl; prov=WavCoeffs_L0[i]; WavCoeffs_L0[i]=ave-kl*(ave-WavCoeffs_L0[i]); } float diflc=WavCoeffs_L0[i]-prov; diflc*=factorx; WavCoeffs_L0[i] = prov + diflc; } } } } if(cp.tonemap && cp.contmet==1) { float maxp=max0*256.f; float minp=min0*256.f; #ifdef _RT_NESTED_OPENMP #pragma omp single #endif ContrastResid(WavCoeffs_L0, 5, skip, cp, W_L, H_L, maxp, minp, ave, ah, bh, al, bl, factorx ); } #ifdef _RT_NESTED_OPENMP #pragma omp barrier #endif if(cp.conres != 0.f || cp.conresH != 0.f) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step #ifdef _RT_NESTED_OPENMP #pragma omp for nowait #endif for (int i=0; i (100.f-tran)) tran=100.f-cp.th; if(LL100 < cp.th){ float aalp=(1.f-alp)/cp.th;//no changes for LL100 = cp.th float kk=aalp*LL100+alp; WavCoeffs_L0[i] *= (1.f+kk*cp.conres/200.f); } else if(LL100 < cp.th + tran) { float ath = -cp.conres/tran; float bth = cp.conres-ath*cp.th; WavCoeffs_L0[i] *= (1.f+(LL100*ath+bth)/200.f); } //highlight tran=5.f; if(cp.thH < (tran)) tran = cp.thH; if(LL100 > cp.thH) WavCoeffs_L0[i] *= (1.f+cp.conresH/200.f); else if(LL100 > (cp.thH - tran)) { float athH = cp.conresH/tran; float bthH = cp.conresH-athH*cp.thH; WavCoeffs_L0[i] *= (1.f+(LL100*athH+bthH)/200.f); } } } //enabled Lipschitz..replace simple by complex edge detection // I found this concept on the web (doctoral thesis on medical Imaging) // I was inspired by the principle of Canny and Lipschitz (continuity and derivability) // I adapted the principle but have profoundly changed the algorithm // One can 1) change all parameters and found good parameters; //one can also chnage in calckoe float edd=settings->ed_detec; float eddlow=settings->ed_low; float eddlipinfl=settings->ed_lipinfl; float eddlipampl=settings->ed_lipampl; if(cp.detectedge && lipschitz==true) { //enabled Lipschitz control...more memory..more time... float *tmCBuffer = new float[H_L*W_L]; float *tmC[H_L]; for (int i=0; i 1 for alp >eddlipinfl and alph < 1 //Liamp < 1 for alp < eddlipinfl and alph > 0 if(alph > 1.f) {alph = 1.f/ alph;} if(beta > 1.f) {beta=1.f/beta;} //take into account diagonal //if in same value OK //if not no edge or reduction float bet=1.f; //if(cp.lip3) {//enhance algorithm if(alph > eddlipinfl && beta < 0.85f*eddlipinfl) {//0.85 arbitrary value ==> eliminate from edge if H V D too different bet=beta; } //} float AmpLip=1.f; if(alph > eddlipinfl) {AmpLip=alipinfl*alph+blipinfl;kampli=SQR(bet)*AmpLip*aamp;}//If beta low reduce kampli else {AmpLip=(1.f/eddlipinfl)*SQR(SQR(alph*bet));kampli=AmpLip/aamp;}//Strong Reduce if beta low // comparaison betwwen pixel and neighbours to do ==> I think 3 dir above is better //koeLi[lvl*3][i*W_L + j] koeLi[lvl*3][(i-1)*W_L + j] koeLi[lvl*3][(i+1)*W_L + j] // koeLi[lvl*3][i*W_L + j+1] koeLi[lvl*3][i*W_L + j-1]) koeLi[lvl*3][(i-1)*W_L + j-1] // koeLi[lvl*3][(i-1)*W_L + j+1] koeLi[lvl*3][(i+1)*W_L + j-1] koeLi[lvl*3][(i+1)*W_L + j+1]) // apply to each direction Wavelet level : horizontal / vertiacle / diagonal interm*=kampli; if(interm < cp.eddetthr/eddlow) interm = 0.01f;//eliminate too low values //we can change this part of algo==> not equal but ponderate koeLi[lvl*3][i*W_L + j]=koeLi[lvl*3 + 1][i*W_L + j]=koeLi[lvl*3 + 2][i*W_L + j]=interm;//new value //here KoeLi contains values where gradient is high and coef high, and eliminate low values... } } } // end } #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int dir=1; dir<4; dir++) { for (int lvl=0; lvl1) #endif { #ifdef __SSE2__ float huebuffer[W_L] ALIGNED64; float chrbuffer[W_L] ALIGNED64; #endif // __SSE2__ #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic,16) #endif for (int i=0; iv(i,j) = valpar; } */ float valparam = float((hhCurve->getVal(Color::huelab_to_huehsv2(hueR))-0.5f) * 1.7f) +hueR;//get H=f(H) 1.7 optimisation ! float2 sincosval = xsincosf(valparam); WavCoeffs_a0[i*W_L+j]=chR*sincosval.y; WavCoeffs_b0[i*W_L+j]=chR*sincosval.x; } } } } } void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition &WaveletCoeffs_ab,const WavOpacityCurveW & waOpacityCurveW, struct cont_params cp, const bool useChannelA){ int maxlvl = WaveletCoeffs_ab.maxlevel(); int W_L = WaveletCoeffs_ab.level_W(0); int H_L = WaveletCoeffs_ab.level_H(0); float * WavCoeffs_ab0 = WaveletCoeffs_ab.coeff0; #ifdef _RT_NESTED_OPENMP #pragma omp parallel num_threads(wavNestedLevels) if(wavNestedLevels>1) #endif { if(cp.chrores != 0.f) { // cp.chrores == 0.f means all will be multiplied by 1.f, so we can skip the processing of residual #ifdef _RT_NESTED_OPENMP #pragma omp for nowait #endif for (int i=0; i 0.f){ if((modhue < cp.t_ry && modhue > cp.t_ly)) { scale=(100.f-cp.sky)/100.1f; } else if((modhue >= cp.t_ry && modhue < cp.b_ry)) { scale=(100.f-cp.sky)/100.1f; float ar=(scale-1.f)/(cp.t_ry- cp.b_ry); float br=scale-cp.t_ry*ar; scale=ar*modhue+br; } else if((modhue > cp.b_ly && modhue < cp.t_ly)) { scale=(100.f-cp.sky)/100.1f; float al=(scale-1.f)/(-cp.b_ly + cp.t_ly); float bl=scale-cp.t_ly*al; scale=al*modhue+bl; } } else if(skyprot < 0.f){ if((modhue > cp.t_ry || modhue < cp.t_ly)){ scale=(100.f+cp.sky)/100.1f; } /* else if((modhue >= cp.t_ry && modhue < cp.b_ry)) { scale=(100.f+cp.sky)/100.1f; float ar=(scale-1.f)/(cp.t_ry- cp.b_ry); float br=scale-cp.t_ry*ar; scale=ar*modhue+br; } else if((modhue > cp.b_ly && modhue < cp.t_ly)) { scale=(100.f+cp.sky)/100.1f; float al=(scale-1.f)/(-cp.b_ly + cp.t_ly); float bl=scale-cp.t_ly*al; scale=al*modhue+bl; } */ } WavCoeffs_ab0[i]*=(1.f+cp.chrores*(scale)/100.f); } } if(cp.cbena) {//if user select Toning and color balance #ifdef _RT_NESTED_OPENMP #pragma omp for nowait #endif for (int i=0; iL[ii*2][jj*2])/327.68f;//I use labco but I can use also WavCoeffs_L0 (more exact but more memory) float sca=1.f;//amplifer - reducter...about 1, but perhaps 0.6 or 1.3 if(useChannelA) {//green red (little magenta) //transition to avoid artifacts with 6 between 30 to 36 and 63 to 69 float aa=(cp.grmed-cp.grlow)/6.f; float bb= cp.grlow-30.f*aa; float aaa=(cp.grhigh-cp.grmed)/6.f; float bbb= cp.grmed-63.f*aaa; if(LL < 30.f)//shadows WavCoeffs_ab0[i]+=cp.grlow*(sca)*300.f; else if(LL >= 30.f && LL < 36.f) {//transition float tr=aa*LL+bb; WavCoeffs_ab0[i]+= tr*(sca)*300.f; } else if(LL >= 36.f && LL < 63.f)//midtones WavCoeffs_ab0[i]+=cp.grmed*(sca)*300.f; else if(LL >= 63.f && LL < 69.f) {//transition float trh=aaa*LL+bbb; WavCoeffs_ab0[i]+=trh*(sca)*300.f; } else if(LL >= 69.f)//highlights WavCoeffs_ab0[i]+=cp.grhigh*(sca)*300.f; } else {//blue yellow //transition with 6 between 30 to 36 and 63 to 69 float aa1=(cp.blmed-cp.bllow)/6.f; float bb1= cp.bllow-30.f*aa1; float aaa1=(cp.blhigh-cp.blmed)/6.f; float bbb1= cp.blmed-63.f*aaa1; if(LL < 30.f) WavCoeffs_ab0[i]+=cp.bllow*(sca)*300.f; else if(LL >= 30.f && LL < 36.f) { float tr1=aa1*LL+bb1; WavCoeffs_ab0[i]+=tr1*(sca)*300.f; } else if(LL >= 36.f && LL < 63.f) WavCoeffs_ab0[i]+=cp.blmed*(sca)*300.f; else if(LL >= 63.f && LL < 69.f) { float trh1=aaa1*LL+bbb1; WavCoeffs_ab0[i]+=trh1*(sca)*300.f; } else if(LL >= 69.f) WavCoeffs_ab0[i]+=cp.blhigh*(sca)*300.f; } } } #ifdef _RT_NESTED_OPENMP #pragma omp for schedule(dynamic) collapse(2) #endif for (int dir=1; dir<4; dir++) { for (int lvl=0; lvl= 30.f && cp.eddetthr < 50.f) { borderL = 1; for (int i=1; i= 50.f && cp.eddetthr < 75.f) { borderL = 1; for (int i=1; i= 75.f) { borderL = 2; for (int i=2; i lees good results==> probably because structure data different and also I compare to original value which have + and - for(int i = borderL; i < H_L-borderL; i++ ) {//[-1 0 1] x==>j for(int j = borderL; j < W_L-borderL; j++) { tmC[i][j]=- WavCoeffs_LL[dir][(i)*W_L + j-1] + WavCoeffs_LL[dir][(i)*W_L + j+1]; } } for(int i = borderL; i < H_L-borderL; i++ ) {//[1 0 -1] y==>i for(int j = borderL; j < W_L-borderL; j++) { tmC[i][j]= - WavCoeffs_LL[dir][(i-1)*W_L + j] + WavCoeffs_LL[dir][(i+1)*W_L + j]; } } */ float thr=40.f;//avoid artifact eg. noise...to test float thr2=1.5f*edd;//edd can be modified in option ed_detect thr2+=cp.eddet/30.f;//to test float diffFactor = (cp.eddet/100.f); for(int i = 0; i < H_L; i++ ) { for(int j = 0; j < W_L; j++) { koeLi[level*3 + dir-1][i*W_L + j]=1.f; } } for(int i = borderL; i < H_L-borderL; i++ ) { for(int j = borderL; j < W_L-borderL; j++) { // my own algo : probably a little false, but simpler as Lipschitz ! // Thr2 = maximum of the function ==> Lipsitch says = probably edge // float temp = WavCoeffs_LL[dir][i*W_L + j]; // if(temp>=0.f && temp < thr) temp = thr; // if(temp < 0.f && temp > -thr) temp = -thr; float temp = max(fabsf(WavCoeffs_LL[dir][i*W_L + j]), thr ); koeLi[level*3 + dir-1][i*W_L + j]= min(thr2,fabs(tmC[i][j]/temp));// limit maxi //it will be more complicated to calculate both Wh and Wv, but we have also Wd==> pseudo Lipschitz if(koeLi[level*3+ dir-1][i*W_L + j] > maxkoeLi[level*3+ dir-1]) maxkoeLi[level*3+ dir-1] = koeLi[level*3 + dir-1][i*W_L + j]; float diff = maxkoeLi[level*3+ dir-1] - koeLi[level*3 + dir-1][i*W_L + j]; diff *= diffFactor; koeLi[level*3 + dir-1][i*W_L + j] = maxkoeLi[level*3 + dir-1] - diff; } } } void ImProcFunctions::finalContAllL (int maxlvl, LabImage * labco, float ** varhue, float **varchrom, float ** WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params cp, int W_L, int H_L, int skip, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveWL & waOpacityCurveWL, FlatCurve* ChCurve, bool Chutili) { bool lipschitz=true; float edge=1.f; bool curvdiag=true; if(curvdiag) {//curve float insigma=0.666f;//SD float logmax=log(MaxP[level]);//log Max float rapX=(mean[level]+sigma[level])/MaxP[level];//rapport between sD / max float inx=log(insigma); float iny=log(rapX); float rap=inx/iny;//koef float asig=0.166f/sigma[level]; float bsig=0.5f-asig*mean[level]; float amean=0.5f/mean[level]; float absciss; float kinterm; float kmul; for (int i=0; i= (mean[level]+sigma[level])){//for max float valcour=log(fabs(WavCoeffs_L[dir][i])); float valc=valcour-logmax; float vald=valc*rap; absciss=exp(vald); } else if(fabs(WavCoeffs_L[dir][i])>=mean[level] && fabs(WavCoeffs_L[dir][i]) < (mean[level]+sigma[level])){ absciss=asig*fabs(WavCoeffs_L[dir][i])+bsig; } else if(fabs(WavCoeffs_L[dir][i]) < mean[level]){ absciss=amean*fabs(WavCoeffs_L[dir][i]); } kinterm=1.f; kmul=1.f; float kc = kmul*(waOpacityCurveWL[absciss*500.f]-0.5f); float reduceeffect=1.5f; if(kc <=0.f) reduceeffect=1.f; kinterm = 1.f + reduceeffect*kmul*(waOpacityCurveWL[absciss*500.f]-0.5f); if(kinterm<0.f) kinterm=0.01f; } WavCoeffs_L[dir][i] *= kinterm; } } int choicelevel = atoi(params->wavelet.Lmethod.data())-1; choicelevel = choicelevel == -1 ? 4 : choicelevel; int choiceClevel=0; if(params->wavelet.CLmethod=="one") choiceClevel=0; else if(params->wavelet.CLmethod=="inf") choiceClevel=1; else if(params->wavelet.CLmethod=="sup") choiceClevel=2; else if(params->wavelet.CLmethod=="all") choiceClevel=3; int choiceDir=0; if(params->wavelet.Dirmethod=="one") choiceDir=1; else if(params->wavelet.Dirmethod=="two") choiceDir=2; else if(params->wavelet.Dirmethod=="thr") choiceDir=3; else if(params->wavelet.Dirmethod=="all") choiceDir=0; int dir1 = (choiceDir == 2) ? 1 : 2; int dir2 = (choiceDir == 3) ? 1 : 3; if(choiceClevel<3) { // not all levels visible, paint residual if(level == 0) { if(cp.backm!=2) { // nothing to change when residual is used as background float backGroundColor = (cp.backm==1) ? 12000.f : 0.f; for (int i=0; i= choicelevel) { for (int dir=1; dir<4; dir++) { for (int i=0; i= choicelevel if(level >= choicelevel) { for (int i=0; i= choicelevel if(level <= choicelevel) { for (int i=0; itop_right; float t_l=settings->top_left; float b_r=settings->bot_right; float b_l=settings->bot_left; float edd=settings->ed_detec; float eddlow=settings->ed_low; float eddstrength=settings->ed_detecStr; float aedstr=(eddstrength-1.f)/90.f; float bedstr=1.f-10.f*aedstr; bool refi=false; // if(cp.lev0s > 0.f || cp.lev1s > 0.f || cp.lev2s > 0.f) refi=true; // if(cp.val > 0 || refi) {//edge if(cp.val > 0) {//edge float * koe; float maxkoe=0.f; if(lipschitz==false) { koe = new float [H_L*W_L]; for (int i=0; i=0.f && temp < thr) temp= thr; if(temp < 0.f && temp > -thr) temp= -thr; koe[i*W_L + j]= min(thr2,fabs(tmC[i][j]/temp)); if(koe[i*W_L + j] > maxkoe) maxkoe=koe[i*W_L + j]; float diff=maxkoe-koe[i*W_L + j]; diff *=(cp.eddet/100.f); float interm=maxkoe-diff; if(interm < cp.eddetthr/30.f) interm = 0.01f; koe[i*W_L + j]=interm; } } //printf("maxkoe=%f \n",maxkoe); for (int i=0; i not too high value to avoid artifacts float value = ((float)cp.val)/8.f;//strength if (scaleskip[1] < 1.f) value *= (atten01234*scaleskip[1]);//for zoom < 100% reduce strength...I choose level 1...but!! float edge=1.f; float lim0=20.f;//arbitrary limit for low radius and level between 2 or 3 to 30 maxi float lev = float (level); float repart=(float)cp.til; float brepart; if(cp.reinforce==1) brepart=3.f; if(cp.reinforce==3) brepart=0.5f; //arbitrary value to increase / decrease repart, between 1 and 0 float arepart=-(brepart-1.f)/(lim0/60.f); if (cp.reinforce !=2) {if(rad < lim0/60.f) repart *= (arepart*rad + brepart);}//linear repartition of repart float al0 = 1.f + (repart)/50.f; float al10 =1.0f;//arbitrary value ==> less = take into account high levels // float ak =-(al0-al10)/10.f;//10 = maximum levels float ak =-(al0-al10)/10.f;//10 = maximum levels float bk =al0; float koef = ak*level+bk;//modulate for levels : more levels high, more koef low ==> concentrated action on low levels, without or near for high levels float expkoef= -pow(fabs(rad - lev),koef);//reduce effect for high levels if (cp.reinforce==3) {if(rad < lim0/60.f && level==0) expkoef *= repart;}//reduce effect for low values of rad and level=0==> quasi only level 1 is effective if (cp.reinforce==1) {if(rad < lim0/60.f && level==1) expkoef /= repart;}//increase effect for low values of rad and level=1==> quasi only level 0 is effective float coefsd=0.85f;//arbitray value to reduce effect after sigma in all case float coefmean=0.85f;//arbitray value to reduce effect after sigma in all case // edge = 1.f + value * exp (expkoef);//estimate edge "pseudo variance" //take into account local contrast float refin= value * exp (expkoef); if(cp.link==true){//combi { if(level==0) refin *= (1.f + cp.lev0s/50.f);// we can change this sensibility! if(level==1) refin *= (1.f + cp.lev1s/50.f); if(level==2) refin *= (1.f + cp.lev2s/50.f); } } float edgePrecalc = 1.f + refin; //estimate edge "pseudo variance" //bool exa=false; if(cp.EDmet==2) {//curve // if(exa) {//curve float insigma=0.666f;//SD float logmax=log(MaxP[level]);//log Max float rapX=(mean[level]+sigma[level])/MaxP[level];//rapport between sD / max float inx=log(insigma); float iny=log(rapX); float rap=inx/iny;//koef float asig=0.166f/sigma[level]; float bsig=0.5f-asig*mean[level]; float amean=0.5f/mean[level]; float absciss; float kinterm; float kmul; for (int i=0; i 10.f) edge=(aedstr*cp.eddet+bedstr)*(edgePrecalc*(1.f+koe[i]))/(1.f+0.9f*maxkoe); else edge=(edgePrecalc*(1.f+koe[i]))/(1.f+0.9f*maxkoe); } if(lipschitz==true) { if(level < 3) edge = 1.f +(edgePrecalc-1.f)*(koeLi[level*3][i])/(1.f+0.9f*maxkoeLi[level*3+ dir-1]); else edge = edgePrecalc; } } else edge = edgePrecalc; if(cp.edgcurv) { if(fabs(WavCoeffs_L[dir][i])>= (mean[level]+sigma[level])){//for max float valcour=log(fabs(WavCoeffs_L[dir][i])); float valc=valcour-logmax; float vald=valc*rap; absciss=exp(vald); } else if(fabs(WavCoeffs_L[dir][i])>=mean[level] && fabs(WavCoeffs_L[dir][i]) < (mean[level]+sigma[level])){ absciss=asig*fabs(WavCoeffs_L[dir][i])+bsig; } else if(fabs(WavCoeffs_L[dir][i]) < mean[level]){ absciss=amean*fabs(WavCoeffs_L[dir][i]); } // Threshold adjuster settings==> approximative for curve //kmul about average cbrt(3--40 / 10)==>1.5 to 2.5 //kmul about SD 10--60 / 35 ==> 2 // kmul about low cbrt((5.f+cp.edg_low)/5.f);==> 1.5 // kmul about max ==> 9 // we can change these values // result is different not best or bad than threshold slider...but similar float abssd=4.f;//amplification reference float bbssd=2.f;//mini ampli float maxamp=2.5f;//maxi ampli at end float maxampd=10.f;//maxi ampli at end float a_abssd=(maxamp - abssd)/0.333f; float b_abssd=maxamp-a_abssd; float da_abssd=(maxampd - abssd)/0.333f; float db_abssd=maxampd-da_abssd; float am=(abssd-bbssd)/0.666f; float kmuld=0.f; if(absciss>0.666f && absciss < 1.f) {kmul=a_abssd*absciss + b_abssd;kmuld=da_abssd*absciss + db_abssd;}//about max ==> kinterm else kmul = kmuld = absciss*am+bbssd; kinterm=1.f; float kc= kmul*(wavCLVCcurve[absciss*500.f]-0.5f); float kcd= kmuld*(wavCLVCcurve[absciss*500.f]-0.5f); float reduceeffect=0.6f; if(kc >=0.f) kinterm = 1.f + reduceeffect*kmul*(wavCLVCcurve[absciss*500.f]-0.5f);//about 1 to 3 general and big amplification for max (under 0) else kinterm = 1.f - (SQR(kcd))/10.f; if(kinterm<0.f) kinterm=0.01f; edge *= kinterm; if(edge < 1.f) edge=1.f; } WavCoeffs_L[dir][i] *= edge; } } else if(cp.EDmet==1) {//threshold adjuster float MaxPCompare = MaxP[level]*SQR(cp.edg_max/100.f);//100 instead of b_r...case if b_r < 100 float MaxNCompare = MaxN[level]*SQR(cp.edg_max/100.f);//always rduce a little edge for near max values float edgeSdCompare = (mean[level]+1.5f*sigma[level])*SQR(cp.edg_sd/t_r);// 1.5 standard deviation #80% range between mean 50% and 80% float edgeMeanCompare = mean[level]*SQR(cp.edg_mean/t_l); float edgeLowCompare = (5.f+SQR(cp.edg_low)); float edgeMeanFactor = cbrt(cp.edg_mean/t_l); float interm; if(cp.edg_low < 10.f) interm= cbrt((5.f+cp.edg_low)/5.f); else interm=1.437f;//cbrt(3); float edgeLowFactor = interm; float edgeSdFactor = cp.edg_sd/t_r; float edgeMaxFactor = SQR(cp.edg_max/b_r); float edgMaxFsup=(cp.edg_max/b_r);//reduce increase of effect for high values contrast..if slider > b_r for (int i=0; i 10.f) edge=(aedstr*cp.eddet+bedstr)*(edgePrecalc*(1.f+koe[i]))/(1.f+0.9f*maxkoe); else edge=(edgePrecalc*(1.f+koe[i]))/(1.f+0.9f*maxkoe); } if(lipschitz==true) { if(level < 3) edge = 1.f +(edgePrecalc-1.f)*(koeLi[level*3][i])/(1.f+0.9f*maxkoeLi[level*3+ dir-1]); else edge = edgePrecalc; } } else edge = edgePrecalc; //algorithm that take into account local contrast // I use a thresholdadjuster with // Bottom left ==> minimal low value for local contrast (not 0, but 5...we can change) // 0 10*10 35*35 100*100 substantially correspond to the true distribution of low value, mean, standard-deviation and max (ed 5, 50, 400, 4000 // Top left ==> mean reference value (for each level), we can change cbrt(cp.edg_mean/10.f) // Top Right==> standard deviation (for each level) we can change (cp.edg_sd/35.f) // bottom right ==> Max for positif and negatif contrast we can change cp.edg_max/100.f // If we move sliders to the left, local contrast is reduced // if we move sliders to the right local contrast is increased // MaxP, MaxN, mean, sigma are calculated if necessary (val > 0) by evaluate2(), eval2(), aver() , sigma() if(b_r < 100.f && cp.edg_max/b_r > 1.f) {//in case of b_r < 100 and slider move to right if (WavCoeffs_L[dir][i] > MaxPCompare*cp.edg_max/b_r) { edge *= edgMaxFsup; if(edge < 1.f) edge=1.f; } else if (WavCoeffs_L[dir][i] < MaxNCompare*cp.edg_max/b_r) { edge *= edgMaxFsup; if(edge < 1.f) edge=1.f; } } if (WavCoeffs_L[dir][i] > MaxPCompare) { edge *= edgeMaxFactor; if(edge < 1.f) edge=1.f; }//reduce edge if > new max else if (WavCoeffs_L[dir][i] < MaxNCompare) { edge *= edgeMaxFactor; if(edge < 1.f) edge=1.f; } if (fabs(WavCoeffs_L[dir][i]) >= edgeMeanCompare && fabs(WavCoeffs_L[dir][i]) < edgeSdCompare) { //if (fabs(WavCoeffs_L[dir][i]) > edgeSdCompare) { edge *= edgeSdFactor; if(edge < 1.f) edge=1.f; }//mofify effect if sd change if (fabs(WavCoeffs_L[dir][i]) < edgeMeanCompare) { edge *= edgeMeanFactor; if(edge < 1.f) edge=1.f; } // modify effect if mean change if (fabs(WavCoeffs_L[dir][i]) < edgeLowCompare) { edge *= edgeLowFactor; if(edge < 1.f) edge=1.f; } WavCoeffs_L[dir][i] *= edge; } } if(lipschitz==false) { delete [] koe; } } if(cp.link==false) { //used both with denoise 1 2 3 float refine=0.f; for (int i=0; iwavelet.skinprotect; const float skinprotneg = -skinprot; const float factorHard = (1.f - skinprotneg/100.f); //to adjust increase contrast with local contrast //for each pixel and each level float beta; float mea[9]; mea[0]=mean[level]/6.f; mea[1]=mean[level]/2.f; mea[2]=mean[level];// 50% data mea[3]=mean[level] + sigma[level]/2.f; mea[4]=mean[level] + sigma[level];//66% mea[5]=mean[level] + 1.2f*sigma[level]; mea[6]=mean[level] + 1.5f*sigma[level];// mea[7]=mean[level] + 2.f*sigma[level];//95% mea[8]=mean[level] + 2.5f*sigma[level];//99% bool skinControl = (skinprot != 0.f); bool useChromAndHue = (skinprot != 0.f || cp.HSmet); float modchro, kLlev; for (int i=0; iL[ii*2][jj*2]; LL100=LL100init=LL/327.68f; LL100res=WavCoeffs_L0[i]/327.68f; float delta=fabs(LL100init-LL100res)/(maxlvl/2); for(int ml=0;ml 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); //0 for skin and extand } else if(skinprot < 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); if (scale == 1.f) scale=factorHard; else scale=1.f; } } if(Chutili){ int i_i=i/W_L; int j_j=i-i_i*W_L; double lr; float modhue2 = varhue[i_i][j_j]; float valparam = float((ChCurve->getVal(lr=Color::huelab_to_huehsv2(modhue2))-0.5f));//get valparam=f(H) if(valparam > 0.f) scale2=1.f + 3.f* valparam;//arbitrary value else scale2 = 1.f + 1.9f*valparam;//near 0 but not zero if curve # 0 //curve Contrast / hue } // //linear transition HL float diagacc=1.f; /* if(cp.diag) { if(dir <=2) diagacc=0.75f; if(dir ==3) diagacc=1.5f; } */ float alpha = (1024.f + 15.f *(float) cpMul*scale*scale2*beta*diagacc)/1024.f ; if(cp.HSmet){ float aaal=(1.f-alpha)/((cp.b_lhl-cp.t_lhl)*kH[level]); float bbal=1.f-aaal*cp.b_lhl*kH[level]; float aaar=(alpha-1.f)/(cp.t_rhl-cp.b_rhl)*kH[level]; float bbbr=1.f-cp.b_rhl*aaar*kH[level]; //linear transition Shadows float aaalS=(1.f-alpha)/(cp.b_lsl-cp.t_lsl); float bbalS=1.f-aaalS*cp.b_lsl; float aaarS=(alpha-1.f)/(cp.t_rsl-cp.b_rsl); float bbbrS=1.f-cp.b_rsl*aaarS; if(level <=cp.numlevH) {//in function of levels if((LL100 > cp.t_lhl*kH[level] && LL100 < cp.t_rhl*kH[level])) {kLlev=alpha;} else if((LL100 > cp.b_lhl*kH[level] && LL100 <= cp.t_lhl*kH[level])) kLlev=aaal*LL100+bbal; else if((LL100 > cp.t_rhl*kH[level] && LL100 <= cp.b_rhl*kH[level])) kLlev=aaar*LL100+bbbr; else kLlev=1.f; } if(level >=(9-cp.numlevS)) { if((LL100 > cp.t_lsl && LL100 < cp.t_rsl)) kLlev=alpha; else if((LL100 > cp.b_lsl && LL100 <= cp.t_lsl)) kLlev=aaalS*LL100+bbalS; else if((LL100 > cp.t_rsl && LL100 <= cp.b_rsl)) kLlev=aaarS*LL100+bbbrS; else kLlev=1.f; } } else kLlev=alpha; WavCoeffs_L[dir][i]*=(kLlev); } } if(waOpacityCurveW) cp.opaW=true; if(cp.bam) { if(cp.opaW && cp.BAmet==2){ int iteration = cp.ite; int itplus=7+iteration; int itmoins= 7-iteration; int med = maxlvl/2; int it; if(level < med) {it=itmoins; } else if(level == med) it=7; else if(level > med) it=itplus; for(int j=0; j < it; j++) { //float bal = cp.balan;//-100 +100 float kba=1.f; float k1; float k2; // if(dir <3) kba= 1.f + bal/600.f; // if(dir==3) kba = 1.f - bal/300.f; for (int i=0; iL[ii*2][jj*2]/327.68f; k1=600.f; k2=300.f; k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16 k2=k1*2.f; if(dir <3) kba= 1.f + k1; if(dir==3) kba = 1.f - k2; WavCoeffs_L[dir][i] *=(kba); } } } if(cp.BAmet==1){ int iteration = cp.ite; int itplus=7+iteration; int itmoins= 7-iteration; int med = maxlvl/2; int it; if(level < med) {it=itmoins; } else if(level == med) it=7; else if(level > med) it=itplus; for(int j=0; j < it; j++) { float bal = cp.balan;//-100 +100 float kba=1.f; float k1; float k2; // if(dir <3) kba= 1.f + bal/600.f; // if(dir==3) kba = 1.f - bal/300.f; for (int i=0; iL[ii*2][jj*2]/327.68f; float aa=4970.f; float bb=-397000.f; float b0=100000.f; float a0=-4970.f; if(LL100> 80.f) {k1=aa*LL100 + bb;k2=0.5f*k1;} if(LL100< 20.f) {k1=a0*LL100 + b0;k2=0.5f*k1;} //k1=600.f; //k2=300.f; //k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16 //k2=k1*2.f; if(dir <3) kba= 1.f + bal/k1; if(dir==3) kba = 1.f - bal/k2; WavCoeffs_L[dir][i] *=(kba); } } } } // to see each level of wavelet ...level from 0 to 8 int choicelevel = atoi(params->wavelet.Lmethod.data())-1; choicelevel = choicelevel == -1 ? 4 : choicelevel; } void ImProcFunctions::ContAllAB (LabImage * labco, int maxlvl, float ** varhue, float **varchrom, float ** WavCoeffs_ab, float * WavCoeffs_ab0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params cp, int W_ab, int H_ab, const bool useChannelA) { float cpMul = cp.mul[level]; if(cpMul != 0.f && cp.CHmet==2 && cp.chro != 0.f) { // cpMul == 0.f or cp.chro = 0.f means all will be multiplied by 1.f, so we can skip this const float skinprot = params->wavelet.skinprotect; const float skinprotneg = -skinprot; const float factorHard = (1.f - skinprotneg/100.f); const float cpChrom = cp.chro; //to adjust increase contrast with local contrast bool useSkinControl = (skinprot != 0.f); float alphaC =(1024.f + 15.f *cpMul*cpChrom/50.f)/1024.f ; for (int i=0; iL[ii*2][jj*2]/327.68f; float modhue = varhue[ii][jj]; float modchro = varchrom[ii*2][jj*2]; // hue chroma skin with initial lab datas float scale=1.f; if(skinprot > 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); //0 for skin and extand } else if(skinprot < 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); scale = (scale == 1.f) ? factorHard : 1.f; } alphaC =(1024.f + 15.f *cpMul*cpChrom*scale/50.f)/1024.f ; } WavCoeffs_ab[dir][i] *= alphaC; } } //Curve chro float cpMulC = cp.mulC[level]; // if( (cp.curv || cp.CHSLmet==1) && cp.CHmet!=2 && level < 9 && cpMulC != 0.f) { // cpMulC == 0.f means all will be multiplied by 1.f, so we can skip if( cp.CHmet!=2 && level < 9 && cpMulC != 0.f) { // cpMulC == 0.f means all will be multiplied by 1.f, so we can skip float modchro, modhue, kClev; const float skinprot = params->wavelet.skinprotect; const float skinprotneg = -skinprot; const float factorHard = (1.f - skinprotneg/100.f); bool useSkinControl = (skinprot != 0.f); for (int i=0; iL[ii*2][jj*2]; float LL100=LL/327.68f; float scale=1.f; modchro = varchrom[ii*2][jj*2]; if(useSkinControl) { // hue chroma skin with initial lab datas modhue = varhue[ii][jj]; scale=1.f; if(skinprot > 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 1); //1 for curve } else if(skinprot < 0.f){ Color::SkinSatCbdl2 (LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 1); scale = (scale == 1.f) ? factorHard : 1.f; } } float beta = (1024.f + 20.f * cpMulC * scale)/1024.f ; if(beta < 0.02f) beta=0.02f; kClev = beta; if(cp.CHmet==1){ if(level < cp.chrom) { //linear for saturated if((modchro > cp.t_lsat && modchro < cp.t_rsat)) kClev=beta; else if((modchro > cp.b_lsat && modchro <= cp.t_lsat)) { float aaal=(1.f-beta)/(cp.b_lsat-cp.t_lsat); float bbal=1.f-aaal*cp.b_lsat; kClev=aaal*modchro+bbal; } else if((modchro > cp.t_rsat && modchro <= cp.b_rsat)) { float aaar=(beta-1.f)/(cp.t_rsat-cp.b_rsat); float bbbr=1.f-cp.b_rsat*aaar; kClev=aaar*modchro+bbbr; } else kClev=1.f; } else { //linear for pastel if((modchro > cp.t_lpast && modchro < cp.t_rpast)) kClev=beta; else if((modchro > cp.b_lpast && modchro <= cp.t_lpast)) { float aaalS=(1.f-beta)/(cp.b_lpast-cp.t_lpast); float bbalS=1.f-aaalS*cp.b_lpast; kClev=aaalS*modchro+bbalS; } else if((modchro > cp.t_rpast && modchro <= cp.b_rpast)) { float aaarS=(beta-1.f)/(cp.t_rpast-cp.b_rpast); float bbbrS=1.f-cp.b_rpast*aaarS; kClev=aaarS*modchro+bbbrS; } else kClev=1.f; } } else if(cp.CHmet==0) kClev=beta; WavCoeffs_ab[dir][i] *= kClev; } } bool useOpacity; float mulOpacity; if(useChannelA) { useOpacity = cp.opaRG; mulOpacity = cp.mulopaRG[level]; } else { useOpacity = cp.opaBY; mulOpacity = cp.mulopaBY[level]; } if(useOpacity && level < 9 && mulOpacity != 0.f) { //toning float beta = (1024.f + 20.f * mulOpacity)/1024.f ; //float beta = (1000.f * mulOpacity); for (int i=0; i med) it=itplus; for(int j=0; j < it; j++) { //float bal = cp.balan;//-100 +100 float kba=1.f; float k1; float k2; // if(dir <3) kba= 1.f + bal/600.f; // if(dir==3) kba = 1.f - bal/300.f; for (int i=0; iL[ii*2][jj*2]/327.68f; k1=600.f; k2=300.f; k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16 k2=k1*2.f; if(dir <3) kba= 1.f + k1; if(dir==3) kba = 1.f - k2; WavCoeffs_ab[dir][i] *=(kba); } } } if(cp.BAmet==1){ int iteration = cp.ite; int itplus=7+iteration; int itmoins= 7-iteration; int med = maxlvl/2; int it; if(level < med) {it=itmoins; } else if(level == med) it=7; else if(level > med) it=itplus; for(int j=0; j < it; j++) { float bal = cp.balan;//-100 +100 float kba=1.f; float k1; float k2; // if(dir <3) kba= 1.f + bal/600.f; // if(dir==3) kba = 1.f - bal/300.f; for (int i=0; iL[ii*2][jj*2]/327.68f; float aa=4970.f; float bb=-397000.f; float b0=100000.f; float a0=-4970.f; if(LL100> 80.f) {k1=aa*LL100 + bb;k2=0.5f*k1;} if(LL100< 20.f) {k1=a0*LL100 + b0;k2=0.5f*k1;} //k1=600.f; //k2=300.f; //k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16 //k2=k1*2.f; if(dir <3) kba= 1.f + bal/k1; if(dir==3) kba = 1.f - bal/k2; WavCoeffs_ab[dir][i] *=(kba); } } } } // to see each level of wavelet ...level from 0 to 8 int choicelevel = atoi(params->wavelet.Lmethod.data())-1; choicelevel = choicelevel == -1 ? 4 : choicelevel; int choiceClevel=0; if(params->wavelet.CLmethod=="one") choiceClevel=0; else if(params->wavelet.CLmethod=="inf") choiceClevel=1; else if(params->wavelet.CLmethod=="sup") choiceClevel=2; else if(params->wavelet.CLmethod=="all") choiceClevel=3; int choiceDir=0; if(params->wavelet.Dirmethod=="one") choiceDir=1; else if(params->wavelet.Dirmethod=="two") choiceDir=2; else if(params->wavelet.Dirmethod=="thr") choiceDir=3; else if(params->wavelet.Dirmethod=="all") choiceDir=0; int dir1 = (choiceDir == 2) ? 1 : 2; int dir2 = (choiceDir == 3) ? 1 : 3; if(choiceClevel<3) { // not all levels visible, paint residual if(level == 0) { if(cp.backm!=2) { // nothing to change when residual is used as background float backGroundChroma = (cp.backm==1) ? 0.f : 0.f;//we can change first to colorized... for (int i=0; i= choicelevel) { for (int dir=1; dir<4; dir++) { for (int i=0; i= choicelevel if(level >= choicelevel) { for (int i=0; i= choicelevel if(level <= choicelevel) { for (int i=0; i