diff --git a/rtdata/languages/default b/rtdata/languages/default index c45a3cac3..b0a128467 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -578,6 +578,7 @@ HISTORY_MSG_356;Wavelet edgedetect thresholdHi HISTORY_MSG_357;Wavelet Denoise link HISTORY_MSG_358;Wavelet Contrast Hue curve HISTORY_MSG_359;Hot/Dead - Threshold +HISTORY_MSG_360;TM Gamma HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOTS;Snapshots @@ -1337,6 +1338,7 @@ TP_EPD_EDGESTOPPING;Edge stopping TP_EPD_LABEL;Tone Mapping TP_EPD_REWEIGHTINGITERATES;Reweighting iterates TP_EPD_SCALE;Scale +TP_EPD_GAMMA;Gamma TP_EPD_STRENGTH;Strength TP_EPD_TOOLTIP;Tone mapping is possible via Lab mode (standard) and CIECAM02 mode.\n\nTo engage CIECAM02 tone mapping mode enable the following settings:\n1. CIECAM02\n2. Algorithm="Brightness + Colorfulness (QM)"\n3. "Tone mapping using CIECAM02 brightness (Q)" TP_EXPOSURE_AUTOLEVELS;Auto Levels diff --git a/rtengine/EdgePreservingDecomposition.cc b/rtengine/EdgePreservingDecomposition.cc index de2ca0622..6f8a30033 100644 --- a/rtengine/EdgePreservingDecomposition.cc +++ b/rtengine/EdgePreservingDecomposition.cc @@ -4,13 +4,13 @@ #ifdef _OPENMP #include #endif -#include "sleef.c" -#include "opthelper.h" - -#define pow_F(a,b) (xexpf(b*xlogf(a))) - -#define DIAGONALS 5 -#define DIAGONALSP1 6 +#include "sleef.c" +#include "opthelper.h" + +#define pow_F(a,b) (xexpf(b*xlogf(a))) + +#define DIAGONALS 5 +#define DIAGONALSP1 6 /* Solves A x = b by the conjugate gradient method, where instead of feeding it the matrix A you feed it a function which calculates A x where x is some vector. Stops when rms residual < RMSResidual or when maximum iterates is reached. @@ -21,12 +21,12 @@ Takes less memory with OkToModify_b = true, and Preconditioner = NULL. */ float *SparseConjugateGradient(void Ax(float *Product, float *x, void *Pass), float *b, int n, bool OkToModify_b, float *x, float RMSResidual, void *Pass, int MaximumIterates, void Preconditioner(float *Product, float *x, void *Pass)){ int iterate, i; - - char* buffer = (char*)malloc(2*n*sizeof(float)+128); + + char* buffer = (char*)malloc(2*n*sizeof(float)+128); float *r = (float*)(buffer+64); //Start r and x. if(x == NULL){ - x = new float[n]; + x = new float[n]; memset(x, 0, sizeof(float)*n); //Zero initial guess if x == NULL. memcpy(r, b, sizeof(float)*n); @@ -35,25 +35,25 @@ float *SparseConjugateGradient(void Ax(float *Product, float *x, void *Pass), fl #ifdef _OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif - for(int ii = 0; ii < n; ii++) + for(int ii = 0; ii < n; ii++) r[ii] = b[ii] - r[ii]; //r = b - A x. } //s is preconditionment of r. Without, direct to r. float *s = r, rs = 0.0f; if(Preconditioner != NULL){ - s = new float[n]; + s = new float[n]; Preconditioner(s, r, Pass); - } + } #ifdef _OPENMP #pragma omp parallel for reduction(+:rs) // removed schedule(dynamic,10) #endif for(int ii = 0; ii < n; ii++) { - rs += r[ii]*s[ii]; + rs += r[ii]*s[ii]; } //Search direction d. - float *d = (float*)(buffer + n*sizeof(float) + 128); - + float *d = (float*)(buffer + n*sizeof(float) + 128); + memcpy(d, s, sizeof(float)*n); //Store calculations of Ax in this. @@ -61,7 +61,7 @@ float *SparseConjugateGradient(void Ax(float *Product, float *x, void *Pass), fl if(!OkToModify_b) ax = new float[n]; //Start iterating! - if(MaximumIterates == 0) MaximumIterates = n; + if(MaximumIterates == 0) MaximumIterates = n; for(iterate = 0; iterate < MaximumIterates; iterate++){ //Get step size alpha, store ax while at it. float ab = 0.0f; @@ -69,7 +69,7 @@ float *SparseConjugateGradient(void Ax(float *Product, float *x, void *Pass), fl #ifdef _OPENMP #pragma omp parallel for reduction(+:ab) #endif - for(int ii = 0; ii < n; ii++) + for(int ii = 0; ii < n; ii++) ab += d[ii]*ax[ii]; if(ab == 0.0f) break; //So unlikely. It means perfectly converged or singular, stop either way. @@ -93,90 +93,90 @@ float *SparseConjugateGradient(void Ax(float *Product, float *x, void *Pass), fl //Get beta. ab = rs; - rs = 0.0f; - + rs = 0.0f; + #ifdef _OPENMP -#pragma omp parallel +#pragma omp parallel #endif -{ - float c = 0.0f; - float t; +{ + float c = 0.0f; + float t; float temp; #ifdef _OPENMP -#pragma omp for reduction(+:rs) // Summation with error correction +#pragma omp for reduction(+:rs) // Summation with error correction #endif - for(int ii = 0; ii < n; ii++) { - temp = r[ii]*s[ii]; - t = rs + temp; - if( fabsf(rs) >= fabsf(temp) ) - c += ((rs-t) + temp); - else - c += ((temp-t)+rs); - rs = t; - } + for(int ii = 0; ii < n; ii++) { + temp = r[ii]*s[ii]; + t = rs + temp; + if( fabsf(rs) >= fabsf(temp) ) + c += ((rs-t) + temp); + else + c += ((temp-t)+rs); + rs = t; + } #ifdef _OPENMP -#pragma omp critical +#pragma omp critical #endif - rs += c; -} - + rs += c; +} + ab = rs/ab; - //Update search direction p. + //Update search direction p. #ifdef _OPENMP #pragma omp parallel for #endif - for(int ii = 0; ii < n; ii++) + for(int ii = 0; ii < n; ii++) d[ii] = s[ii] + ab*d[ii]; - - + + } - + if(iterate == MaximumIterates) if(iterate != n && RMSResidual != 0.0f) printf("Warning: MaximumIterates (%u) reached in SparseConjugateGradient.\n", MaximumIterates); if(ax != b) delete[] ax; if(s != r) delete[] s; - free(buffer); + free(buffer); return x; } MultiDiagonalSymmetricMatrix::MultiDiagonalSymmetricMatrix(int Dimension, int NumberOfDiagonalsInLowerTriangle){ n = Dimension; - m = NumberOfDiagonalsInLowerTriangle; + m = NumberOfDiagonalsInLowerTriangle; IncompleteCholeskyFactorization = NULL; Diagonals = new float *[m]; StartRows = new int [m+1]; memset(Diagonals, 0, sizeof(float *)*m); - memset(StartRows, 0, sizeof(int)*(m+1)); + memset(StartRows, 0, sizeof(int)*(m+1)); StartRows[m] = n+1; } MultiDiagonalSymmetricMatrix::~MultiDiagonalSymmetricMatrix(){ - if(DiagBuffer != NULL) - free(buffer); - else - for(int i=0;i try to allocate smaller blocks - DiagBuffer = NULL; - else { - DiagBuffer = (char*)( ( uintptr_t(buffer) + uintptr_t(63)) / 64 * 64); - } +bool MultiDiagonalSymmetricMatrix::CreateDiagonal(int index, int StartRow){ + // Changed memory allocation for diagonals to avoid L1 conflict misses + // Falls back to original version if big block could not be allocated + int padding = 4096 - ((n*m*sizeof(float)) % 4096); + if(index == 0){ + buffer = (char*)calloc( (n+padding) * m * sizeof(float)+ (m+16)*64 + 63,1); + if(buffer == NULL) + // no big memory block available => try to allocate smaller blocks + DiagBuffer = NULL; + else { + DiagBuffer = (char*)( ( uintptr_t(buffer) + uintptr_t(63)) / 64 * 64); + } } if(index >= m){ printf("Error in MultiDiagonalSymmetricMatrix::CreateDiagonal: invalid index.\n"); @@ -187,32 +187,32 @@ bool MultiDiagonalSymmetricMatrix::CreateDiagonal(int index, int StartRow){ printf("Error in MultiDiagonalSymmetricMatrix::CreateDiagonal: each StartRow must exceed the previous.\n"); return false; } - + if(DiagBuffer != NULL) - Diagonals[index] = (float*)(DiagBuffer+(index*(n+padding)*sizeof(float))+((index+16)*64)); - else { - Diagonals[index] = new float[DiagonalLength(StartRow)]; - if(Diagonals[index] == NULL) { + Diagonals[index] = (float*)(DiagBuffer+(index*(n+padding)*sizeof(float))+((index+16)*64)); + else { + Diagonals[index] = new float[DiagonalLength(StartRow)]; + if(Diagonals[index] == NULL) { printf("Error in MultiDiagonalSymmetricMatrix::CreateDiagonal: memory allocation failed. Out of memory?\n"); - return false; + return false; } memset(Diagonals[index], 0, sizeof(float)*DiagonalLength(StartRow)); - } - + } + StartRows[index] = StartRow; return true; } inline int MultiDiagonalSymmetricMatrix::FindIndex(int StartRow) { - //There's GOT to be a better way to do this. "Bidirectional map?" - // Issue 1895 : Changed start of loop from zero to one + //There's GOT to be a better way to do this. "Bidirectional map?" + // Issue 1895 : Changed start of loop from zero to one // m is small (5 or 6) for(int i = 1; i < m; i++) if(StartRows[i] == StartRow) return i; return -1; } - + bool MultiDiagonalSymmetricMatrix::LazySetEntry(float value, int row, int column){ //On the strict upper triangle? Swap, this is ok due to symmetry. int i, sr; @@ -232,90 +232,90 @@ bool MultiDiagonalSymmetricMatrix::LazySetEntry(float value, int row, int column } SSEFUNCTION void MultiDiagonalSymmetricMatrix::VectorProduct(float* RESTRICT Product, float* RESTRICT x){ - - int srm = StartRows[m-1]; - int lm = DiagonalLength(srm); -#ifdef _OPENMP -#ifdef __SSE2__ - const int chunkSize = (lm-srm)/(omp_get_num_procs()*32); -#else - const int chunkSize = (lm-srm)/(omp_get_num_procs()*8); -#endif -#endif -#pragma omp parallel -{ - // First fill the big part in the middle - // This can be done without intermediate stores to memory and it can be parallelized too -#ifdef _OPENMP -#pragma omp for schedule(dynamic,chunkSize) nowait -#endif -#ifdef __SSE2__ - for(int j=srm;j0;i--) { - int s = StartRows[i]; - prodv += (LVFU(Diagonals[i][j - s])*LVFU(x[j - s])) + (LVFU(Diagonals[i][j])*LVFU(x[j + s])); - } - _mm_storeu_ps(&Product[j],prodv); - } -#else - for(int j=srm;j0;i--) { - int s = StartRows[i]; - prod += (Diagonals[i][j - s]*x[j - s]) + (Diagonals[i][j]*x[j + s]); - } - Product[j] = prod; - } - -#endif -#pragma omp single -{ -#ifdef __SSE2__ - for(int j=lm-((lm-srm)%4);j0;i--) { - int s = StartRows[i]; - prod += (Diagonals[i][j - s]*x[j - s]) + (Diagonals[i][j]*x[j + s]); - } - Product[j] = prod; - } -#endif - // Fill remaining area + + int srm = StartRows[m-1]; + int lm = DiagonalLength(srm); +#ifdef _OPENMP +#ifdef __SSE2__ + const int chunkSize = (lm-srm)/(omp_get_num_procs()*32); +#else + const int chunkSize = (lm-srm)/(omp_get_num_procs()*8); +#endif +#endif +#pragma omp parallel +{ + // First fill the big part in the middle + // This can be done without intermediate stores to memory and it can be parallelized too +#ifdef _OPENMP +#pragma omp for schedule(dynamic,chunkSize) nowait +#endif +#ifdef __SSE2__ + for(int j=srm;j0;i--) { + int s = StartRows[i]; + prodv += (LVFU(Diagonals[i][j - s])*LVFU(x[j - s])) + (LVFU(Diagonals[i][j])*LVFU(x[j + s])); + } + _mm_storeu_ps(&Product[j],prodv); + } +#else + for(int j=srm;j0;i--) { + int s = StartRows[i]; + prod += (Diagonals[i][j - s]*x[j - s]) + (Diagonals[i][j]*x[j + s]); + } + Product[j] = prod; + } + +#endif +#pragma omp single +{ +#ifdef __SSE2__ + for(int j=lm-((lm-srm)%4);j0;i--) { + int s = StartRows[i]; + prod += (Diagonals[i][j - s]*x[j - s]) + (Diagonals[i][j]*x[j + s]); + } + Product[j] = prod; + } +#endif + // Fill remaining area // Loop over the stored diagonals. for(int i = 0; i < m; i++){ int sr = StartRows[i]; float *a = Diagonals[i]; //One fewer dereference. - int l = DiagonalLength(sr); + int l = DiagonalLength(sr); if(sr == 0) { for(int j = 0; j < srm; j++) Product[j] = a[j]*x[j]; //Separate, fairly simple treatment for the main diagonal. for(int j = lm; j < l; j++) Product[j] = a[j]*x[j]; //Separate, fairly simple treatment for the main diagonal. } else { -// Split the loop in 3 parts, so now the big one in the middle can be parallelized without race conditions - // updates 0 to sr - 1. Because sr is small (in the range of image-width) no benefit by omp - for(int j=0;jCreateDiagonal(0, 0); //There's always a main diagonal in this type of decomposition. - mic=1; + mic=1; for(int ii = 1; ii < m; ii++){ //Set j to the number of diagonals to be created corresponding to a diagonal on this source matrix... - j = rtengine::min(StartRows[ii] - StartRows[ii - 1], MaxFillAbove); - + j = rtengine::min(StartRows[ii] - StartRows[ii - 1], MaxFillAbove); + //...and create those diagonals. I want to take a moment to tell you about how much I love minimalistic loops: very much. while(j-- != 0) if(!ic->CreateDiagonal(mic++, StartRows[ii] - j)){ @@ -350,101 +350,101 @@ bool MultiDiagonalSymmetricMatrix::CreateIncompleteCholeskyFactorization(int Max delete ic; return false; } - } + } //It's all initialized? Uhkay. Do the actual math then. int sss, ss, s; int k, MaxStartRow = StartRows[m - 1]; //Handy number. float **l = ic->Diagonals; - float *d = ic->Diagonals[0]; //Describes D in LDLt. - int icm = ic->m; - int icn = ic->n; - int* RESTRICT icStartRows = ic->StartRows; - - //Loop over the columns. - - // create array for quicker access to ic->StartRows - struct s_diagmap { - int sss; - int ss; - int k; - }; - - - // Pass one: count number of needed entries - int entrycount = 0; - for(int i=1;iFindIndex( icStartRows[i] + icStartRows[j]) > 0) - entrycount ++; - } - } - - // now we can create the array - struct s_diagmap* RESTRICT DiagMap = new s_diagmap[entrycount]; - // we also need the maxvalues - int entrynumber = 0; - int index; - int* RESTRICT MaxIndizes = new int[icm]; - - for(int i=1;iFindIndex( icStartRows[i] + icStartRows[j]); - if(index > 0) { - DiagMap[entrynumber].ss = j; - DiagMap[entrynumber].sss = index; - DiagMap[entrynumber].k = icStartRows[j]; - entrynumber ++; - } - } - MaxIndizes[i] = entrynumber - 1; - } - - int* RESTRICT findmap = new int[icm]; - for(int j=0;jDiagonals[0]; //Describes D in LDLt. + int icm = ic->m; + int icn = ic->n; + int* RESTRICT icStartRows = ic->StartRows; + + //Loop over the columns. + + // create array for quicker access to ic->StartRows + struct s_diagmap { + int sss; + int ss; + int k; + }; + + + // Pass one: count number of needed entries + int entrycount = 0; + for(int i=1;iFindIndex( icStartRows[i] + icStartRows[j]) > 0) + entrycount ++; + } + } + + // now we can create the array + struct s_diagmap* RESTRICT DiagMap = new s_diagmap[entrycount]; + // we also need the maxvalues + int entrynumber = 0; + int index; + int* RESTRICT MaxIndizes = new int[icm]; + + for(int i=1;iFindIndex( icStartRows[i] + icStartRows[j]); + if(index > 0) { + DiagMap[entrynumber].ss = j; + DiagMap[entrynumber].sss = index; + DiagMap[entrynumber].k = icStartRows[j]; + entrynumber ++; + } + } + MaxIndizes[i] = entrynumber - 1; + } + + int* RESTRICT findmap = new int[icm]; + for(int j=0;j= jMax) - break; //Possible values of j are limited - - float temp = 0.0f; - while(mapindex <= MaxIndizes[s] && ( k = DiagMap[mapindex].k) <= j) { - temp -= l[DiagMap[mapindex].sss][j - k]*l[DiagMap[mapindex].ss][j - k]*d[j - k]; - mapindex ++; - } - sss = findmap[s]; + if(icStartRows[s] >= jMax) + break; //Possible values of j are limited + + float temp = 0.0f; + while(mapindex <= MaxIndizes[s] && ( k = DiagMap[mapindex].k) <= j) { + temp -= l[DiagMap[mapindex].sss][j - k]*l[DiagMap[mapindex].ss][j - k]*d[j - k]; + mapindex ++; + } + sss = findmap[s]; l[s][j] = id * (sss < 0 ? temp : (Diagonals[sss][j] + temp)); - } - } - delete[] DiagMap; - delete[] MaxIndizes; + } + } + delete[] DiagMap; + delete[] MaxIndizes; delete[] findmap; IncompleteCholeskyFactorization = ic; return true; @@ -454,86 +454,86 @@ void MultiDiagonalSymmetricMatrix::KillIncompleteCholeskyFactorization(void){ delete IncompleteCholeskyFactorization; } -void MultiDiagonalSymmetricMatrix::CholeskyBackSolve(float* RESTRICT x, float* RESTRICT b){ +void MultiDiagonalSymmetricMatrix::CholeskyBackSolve(float* RESTRICT x, float* RESTRICT b){ //We want to solve L D Lt x = b where D is a diagonal matrix described by Diagonals[0] and L is a unit lower triagular matrix described by the rest of the diagonals. //Let D Lt x = y. Then, first solve L y = b. float* RESTRICT *d = IncompleteCholeskyFactorization->Diagonals; int* RESTRICT s = IncompleteCholeskyFactorization->StartRows; int M = IncompleteCholeskyFactorization->m, N = IncompleteCholeskyFactorization->n; - int i, j; - + int i, j; + if(M != DIAGONALSP1){ // can happen in theory for(j = 0; j < N; j++){ - float sub = b[j]; // using local var to reduce memory writes, gave a big speedup - i = 1; - int c = j - s[i]; + float sub = b[j]; // using local var to reduce memory writes, gave a big speedup + i = 1; + int c = j - s[i]; while(c >= 0) { sub -= d[i][c]*x[c]; - i++; + i++; c = j - s[i]; } x[j] = sub; // only one memory-write per j - } - } else { // that's the case almost every time + } + } else { // that's the case almost every time for(j = 0; j <= s[M-1]; j++){ - float sub = b[j]; // using local var to reduce memory writes, gave a big speedup - i = 1; - int c = j - s[1]; + float sub = b[j]; // using local var to reduce memory writes, gave a big speedup + i = 1; + int c = j - s[1]; while(c >= 0) { sub -= d[i][c]*x[c]; - i++; + i++; c = j - s[i]; } x[j] = sub; // only one memory-write per j } for(j = s[M-1]+1; j0;i--){ // using a constant upperbound allows the compiler to unroll this loop (gives a good speedup) + float sub = b[j]; // using local var to reduce memory writes, gave a big speedup + for(int i=DIAGONALSP1-1;i>0;i--){ // using a constant upperbound allows the compiler to unroll this loop (gives a good speedup) sub -= d[i][j-s[i]]*x[j-s[i]]; - } + } x[j] = sub; // only one memory-write per j - } + } } - + //Now, solve x from D Lt x = y -> Lt x = D^-1 y // Took this one out of the while, so it can be parallelized now, which speeds up, because division is expensive #ifdef _OPENMP #pragma omp parallel for -#endif +#endif for(j = 0; j < N; j++) x[j] = x[j]/d[0][j]; - + if(M != DIAGONALSP1){ // can happen in theory while(j-- > 0){ - float sub = x[j]; // using local var to reduce memory writes, gave a big speedup - i=1; - int c = j+s[1]; - while(c < N) { - sub -= d[i][j]*x[c]; - i++; + float sub = x[j]; // using local var to reduce memory writes, gave a big speedup + i=1; + int c = j+s[1]; + while(c < N) { + sub -= d[i][j]*x[c]; + i++; c = j+s[i]; - } + } x[j] = sub; // only one memory-write per j - } - } else { // that's the case almost every time - for(j=N-1;j>=(N-1)-s[M-1];j--) { - float sub = x[j]; // using local var to reduce memory writes, gave a big speedup - i=1; - int c = j+s[1]; - while(c < N) { - sub -= d[i][j]*x[j+s[i]]; - i++; + } + } else { // that's the case almost every time + for(j=N-1;j>=(N-1)-s[M-1];j--) { + float sub = x[j]; // using local var to reduce memory writes, gave a big speedup + i=1; + int c = j+s[1]; + while(c < N) { + sub -= d[i][j]*x[j+s[i]]; + i++; c = j+s[i]; - } + } x[j] = sub; // only one memory-write per j - } - for(j=(N-2)-s[M-1];j>=0;j--) { - float sub = x[j]; // using local var to reduce memory writes, gave a big speedup - for(int i=DIAGONALSP1-1;i>0;i--){ // using a constant upperbound allows the compiler to unroll this loop (gives a good speedup) - sub -= d[i][j]*x[j + s[i]]; - } + } + for(j=(N-2)-s[M-1];j>=0;j--) { + float sub = x[j]; // using local var to reduce memory writes, gave a big speedup + for(int i=DIAGONALSP1-1;i>0;i--){ // using a constant upperbound allows the compiler to unroll this loop (gives a good speedup) + sub -= d[i][j]*x[j + s[i]]; + } x[j] = sub; // only one memory-write per j - } + } } } @@ -566,19 +566,19 @@ EdgePreservingDecomposition::~EdgePreservingDecomposition(){ delete A; } -SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float Scale, float EdgeStopping, int Iterates, float *Blur, bool UseBlurForEdgeStop){ - +SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float Scale, float EdgeStopping, int Iterates, float *Blur, bool UseBlurForEdgeStop){ + if(Blur == NULL) UseBlurForEdgeStop = false, //Use source if there's no supplied Blur. - Blur = new float[n]; - + Blur = new float[n]; + if(Scale == 0.0f){ memcpy(Blur, Source, n*sizeof(float)); return Blur; } //Create the edge stopping function a, rotationally symmetric and just one instead of (ax, ay). Maybe don't need Blur yet, so use its memory. - float* RESTRICT a; + float* RESTRICT a; float* RESTRICT g; if(UseBlurForEdgeStop) a = new float[n], g = Blur; else a = Blur, g = Source; @@ -586,41 +586,41 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float int i; int w1 = w - 1, h1 = h - 1; // float eps = 0.02f; - const float sqreps = 0.0004f; // removed eps*eps from inner loop - - -#ifdef _OPENMP -#pragma omp parallel -#endif -{ -#ifdef __SSE2__ - int x; - __m128 gxv,gyv; - __m128 Scalev = _mm_set1_ps( Scale ); - __m128 sqrepsv = _mm_set1_ps( sqreps ); - __m128 EdgeStoppingv = _mm_set1_ps( -EdgeStopping ); - __m128 zd5v = _mm_set1_ps( 0.5f ); -#endif + const float sqreps = 0.0004f; // removed eps*eps from inner loop + + +#ifdef _OPENMP +#pragma omp parallel +#endif +{ +#ifdef __SSE2__ + int x; + __m128 gxv,gyv; + __m128 Scalev = _mm_set1_ps( Scale ); + __m128 sqrepsv = _mm_set1_ps( sqreps ); + __m128 EdgeStoppingv = _mm_set1_ps( -EdgeStopping ); + __m128 zd5v = _mm_set1_ps( 0.5f ); +#endif #ifdef _OPENMP #pragma omp for -#endif +#endif for(int y = 0; y < h1; y++){ - float *rg = &g[w*y]; -#ifdef __SSE2__ + float *rg = &g[w*y]; +#ifdef __SSE2__ for(x = 0; x < w1-3; x+=4){ - //Estimate the central difference gradient in the center of a four pixel square. (gx, gy) is actually 2*gradient. - gxv = (LVFU(rg[x + 1]) - LVFU(rg[x])) + (LVFU(rg[x + w + 1]) - LVFU(rg[x + w])); + //Estimate the central difference gradient in the center of a four pixel square. (gx, gy) is actually 2*gradient. + gxv = (LVFU(rg[x + 1]) - LVFU(rg[x])) + (LVFU(rg[x + w + 1]) - LVFU(rg[x + w])); gyv = (LVFU(rg[x + w]) - LVFU(rg[x])) + (LVFU(rg[x + w + 1]) - LVFU(rg[x + 1])); //Apply power to the magnitude of the gradient to get the edge stopping function. _mm_storeu_ps( &a[x + w*y], Scalev * pow_F((zd5v*_mm_sqrt_ps(gxv*gxv + gyv*gyv + sqrepsv)), EdgeStoppingv) ); - } + } for(; x < w1; x++){ //Estimate the central difference gradient in the center of a four pixel square. (gx, gy) is actually 2*gradient. float gx = (rg[x + 1] - rg[x]) + (rg[x + w + 1] - rg[x + w]); float gy = (rg[x + w] - rg[x]) + (rg[x + w + 1] - rg[x + 1]); //Apply power to the magnitude of the gradient to get the edge stopping function. a[x + w*y] = Scale*pow_F(0.5f*sqrtf(gx*gx + gy*gy + sqreps), -EdgeStopping); - } + } #else for(int x = 0; x < w1; x++){ //Estimate the central difference gradient in the center of a four pixel square. (gx, gy) is actually 2*gradient. @@ -629,12 +629,12 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float //Apply power to the magnitude of the gradient to get the edge stopping function. a[x + w*y] = Scale*pow_F(0.5f*sqrtf(gx*gx + gy*gy + sqreps), -EdgeStopping); - } + } #endif - } -} - - + } +} + + /* Now setup the linear problem. I use the Maxima CAS, here's code for making an FEM formulation for the smoothness term: p(x, y) := (1 - x)*(1 - y); P(m, n) := A[m][n]*p(x, y) + A[m + 1][n]*p(1 - x, y) + A[m + 1][n + 1]*p(1 - x, 1 - y) + A[m][n + 1]*p(x, 1 - y); @@ -644,13 +644,13 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float Integrate(diff(P(u - 1, v), x)*diff(p(1 - x, y), x) + diff(P(u - 1, v), y)*diff(p(1 - x, y), y)); Integrate(diff(P(u - 1, v - 1), x)*diff(p(1 - x, 1 - y), x) + diff(P(u - 1, v - 1), y)*diff(p(1 - x, 1 - y), y)); Integrate(diff(P(u, v - 1), x)*diff(p(x, 1 - y), x) + diff(P(u, v - 1), y)*diff(p(x, 1 - y), y)); - So yeah. Use the numeric results of that to fill the matrix A.*/ - + So yeah. Use the numeric results of that to fill the matrix A.*/ + memset(a_1, 0, A->DiagonalLength(1)*sizeof(float)); memset(a_w1, 0, A->DiagonalLength(w - 1)*sizeof(float)); memset(a_w, 0, A->DiagonalLength(w)*sizeof(float)); - memset(a_w_1, 0, A->DiagonalLength(w + 1)*sizeof(float)); - + memset(a_w_1, 0, A->DiagonalLength(w + 1)*sizeof(float)); + // checked for race condition here // a0[] is read and write but adressed by i only @@ -659,10 +659,10 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float // a_w is write only // a_w1 is write only // a_1 is write only -// So, there should be no race conditions - -#ifdef _OPENMP -#pragma omp parallel for +// So, there should be no race conditions + +#ifdef _OPENMP +#pragma omp parallel for #endif for(int y = 0; y < h; y++){ int i = y*w; @@ -673,31 +673,31 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float //Remember, only fill the lower triangle. Memory for upper is never made. It's symmetric. Trust. if(x > 0 && y > 0) { ac = a[i - w - 1]/6.0f; - a_w_1[i - w - 1] -= 2.0f*ac; + a_w_1[i - w - 1] -= 2.0f*ac; a_w[i - w] -= ac; - a_1[i - 1] -= ac; + a_1[i - 1] -= ac; a0temp += ac; } if(x < w1 && y > 0) { ac = a[i - w]/6.0f; - a_w[i - w] -= ac; + a_w[i - w] -= ac; a_w1[i - w + 1] -= 2.0f*ac; a0temp += ac; } if(x > 0 && y < h1) { ac = a[i - 1]/6.0f; - a_1[i - 1] -= ac; + a_1[i - 1] -= ac; a0temp += ac; } if(x < w1 && y < h1) - a0temp += a[i]/6.0f; - a0[i] = 4.0f*a0temp; + a0temp += a[i]/6.0f; + a0[i] = 4.0f*a0temp; } - } - + } + if(UseBlurForEdgeStop) delete[] a; - //Solve & return. - bool success=A->CreateIncompleteCholeskyFactorization(1); //Fill-in of 1 seems to work really good. More doesn't really help and less hurts (slightly). + //Solve & return. + bool success=A->CreateIncompleteCholeskyFactorization(1); //Fill-in of 1 seems to work really good. More doesn't really help and less hurts (slightly). if(!success) { fprintf(stderr,"Error: Tonemapping has failed.\n"); memset(Blur, 0, sizeof(float)*n); // On failure, set the blur to zero. This is subsequently exponentiated in CompressDynamicRange. @@ -705,7 +705,7 @@ SSEFUNCTION float *EdgePreservingDecomposition::CreateBlur(float *Source, float } if(!UseBlurForEdgeStop) memcpy(Blur, Source, n*sizeof(float)); SparseConjugateGradient(A->PassThroughVectorProduct, Source, n, false, Blur, 0.0f, (void *)A, Iterates, A->PassThroughCholeskyBackSolve); - A->KillIncompleteCholeskyFactorization(); + A->KillIncompleteCholeskyFactorization(); return Blur; } @@ -725,35 +725,35 @@ float *EdgePreservingDecomposition::CreateIteratedBlur(float *Source, float Scal return Blur; } -SSEFUNCTION float *EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scale, float EdgeStopping, float CompressionExponent, float DetailBoost, int Iterates, int Reweightings, float *Compressed){ - if(w<300 && h<300) // set number of Reweightings to zero for small images (thumbnails). We could try to find a better solution here. - Reweightings = 0; - - //Small number intended to prevent division by zero. This is different from the eps in CreateBlur. +SSEFUNCTION float *EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scale, float EdgeStopping, float CompressionExponent, float DetailBoost, int Iterates, int Reweightings, float *Compressed){ + if(w<300 && h<300) // set number of Reweightings to zero for small images (thumbnails). We could try to find a better solution here. + Reweightings = 0; + + //Small number intended to prevent division by zero. This is different from the eps in CreateBlur. const float eps = 0.0001f; //We're working with luminance, which does better logarithmic. -#ifdef __SSE2__ +#ifdef __SSE2__ #ifdef _OPENMP #pragma omp parallel -#endif -{ - __m128 epsv = _mm_set1_ps( eps ); +#endif +{ + __m128 epsv = _mm_set1_ps( eps ); #ifdef _OPENMP #pragma omp for -#endif +#endif for(int ii = 0; ii < n-3; ii+=4) - _mm_storeu_ps( &Source[ii], xlogf(LVFU(Source[ii]) + epsv)); -} + _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); - + Source[ii] = xlogf(Source[ii] + eps); + #else #ifdef _OPENMP #pragma omp parallel for #endif for(int ii = 0; ii < n; ii++) - Source[ii] = xlogf(Source[ii] + eps); + Source[ii] = xlogf(Source[ii] + eps); #endif //Blur. Also setup memory for Compressed (we can just use u since each element of u is used in one calculation). @@ -761,35 +761,40 @@ SSEFUNCTION float *EdgePreservingDecomposition::CompressDynamicRange(float *Sour if(Compressed == NULL) Compressed = u; //Apply compression, detail boost, unlogging. Compression is done on the logged data and detail boost on unlogged. - float temp = CompressionExponent - 1.0f; - -#ifdef __SSE2__ +// float temp = CompressionExponent - 1.0f; + float temp; + if(DetailBoost>0.f) { + float betemp=expf(-(2.f-DetailBoost+0.694f))-1.f;//0.694 = log(2) + temp = 1.2f*xlogf( -betemp); + } + else temp= CompressionExponent - 1.0f; +#ifdef __SSE2__ #ifdef _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 ); +#endif +{ + __m128 cev, uev, sourcev; + __m128 epsv = _mm_set1_ps( eps ); + __m128 DetailBoostv = _mm_set1_ps( DetailBoost ); + __m128 tempv = _mm_set1_ps( temp ); #ifdef _OPENMP -#pragma omp for -#endif +#pragma omp for +#endif for(int i = 0; i < n-3; i+=4){ cev = xexpf(LVFU(Source[i]) + LVFU(u[i])*(tempv)) - epsv; - uev = xexpf(LVFU(u[i])) - epsv; - sourcev = xexpf(LVFU(Source[i])) - epsv; - _mm_storeu_ps( &Source[i], sourcev); + uev = xexpf(LVFU(u[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] + u[i]*(temp)) - eps; float ue = xexpf(u[i]) - eps; Source[i] = xexpf(Source[i]) - eps; Compressed[i] = ce + DetailBoost*(Source[i] - ue); - } - + } + #else #ifdef _OPENMP #pragma omp parallel for @@ -799,10 +804,10 @@ SSEFUNCTION float *EdgePreservingDecomposition::CompressDynamicRange(float *Sour float ue = xexpf(u[i]) - eps; Source[i] = xexpf(Source[i]) - eps; Compressed[i] = ce + DetailBoost*(Source[i] - ue); - } -#endif + } +#endif - if(Compressed != u) delete[] u; + if(Compressed != u) delete[] u; return Compressed; } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 387f26dc1..37110a83a 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -5092,6 +5092,7 @@ if(!params->epd.enabled) return; float stren=params->epd.strength; float edgest=params->epd.edgeStopping; float sca=params->epd.scale; + float gamm=params->epd.gamma; float rew=params->epd.reweightingIterates; unsigned int i, N = Wid*Hei; float Qpro= ( 4.0 / c_) * ( a_w + 4.0 ) ;//estimate Q max if J=100.0 @@ -5106,7 +5107,7 @@ if(!params->epd.enabled) return; #pragma omp parallel for for (int i=0; iQ_p[i][j] = ncie->Q_p[i][j]/(Qpro); + ncie->Q_p[i][j] = gamm*ncie->Q_p[i][j]/(Qpro); float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; @@ -5125,7 +5126,7 @@ if(!params->epd.enabled) return; #endif for (int i=0; iQ_p[i][j]=ncie->Q_p[i][j]*Qpro; + ncie->Q_p[i][j]=(ncie->Q_p[i][j]*Qpro)/gamm; ncie->M_p[i][j]*=s; } /* @@ -5180,31 +5181,39 @@ if(!params->epd.enabled) return; float stren=params->epd.strength; float edgest=params->epd.edgeStopping; float sca=params->epd.scale; +float gamm=params->epd.gamma; float rew=params->epd.reweightingIterates; //Pointers to whole data and size of it. float *L = lab->L[0]; float *a = lab->a[0]; float *b = lab->b[0]; unsigned int i, N = lab->W*lab->H; - EdgePreservingDecomposition epd = EdgePreservingDecomposition(lab->W, lab->H); //Due to the taking of logarithms, L must be nonnegative. Further, scale to 0 to 1 using nominal range of L, 0 to 15 bit. float minL = FLT_MAX; + float maxL = 0.f; #pragma omp parallel { float lminL = FLT_MAX; + float lmaxL = 0.f; #pragma omp for - for(i = 0; i < N; i++) + for(i = 0; i < N; i++) { if(L[i] < lminL) lminL = L[i]; + if(L[i] > lmaxL) lmaxL = L[i]; + } #pragma omp critical if(lminL < minL) minL = lminL; + if(lmaxL > maxL) maxL = lmaxL; + } if(minL > 0.0f) minL = 0.0f; //Disable the shift if there are no negative numbers. I wish there were just no negative numbers to begin with. #pragma omp parallel for for(i = 0; i < N; i++) - L[i] = (L[i] - minL)/32767.0f; - + //{L[i] = (L[i] - minL)/32767.0f; + {L[i] = (L[i] - minL)/maxL; + L[i]*=gamm; + } //Some interpretations. float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; @@ -5230,7 +5239,8 @@ fclose(f);*/ for(int ii = 0; ii < N; ii++) a[ii] *= s, b[ii] *= s, - L[ii] = L[ii]*32767.0f + minL; + //L[ii] = L[ii]*32767.0f*(1.f/gamm) + minL; + L[ii] = L[ii]*maxL*(1.f/gamm) + minL; } diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 9ca678e94..ec45a0130 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -384,6 +384,8 @@ enum ProcEvent { EvWavlinkedg=356, EvWavCHCurve=357, EvPreProcessHotDeadThresh=358, + EvEPDgamma=359, + NUMOFEVENTS }; } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 20d283b6c..e10a8ab49 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -845,7 +845,8 @@ void ProcParams::setDefaults () { dirpyrDenoise.setDefaults(); epd.enabled = false; - epd.strength = 0.25; + epd.strength = 0.8; + epd.gamma = 1.0; epd.edgeStopping = 1.4; epd.scale = 1.0; epd.reweightingIterates = 0; @@ -1427,6 +1428,7 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol //Save epd. if (!pedited || pedited->epd.enabled) keyFile.set_boolean ("EPD", "Enabled", epd.enabled); if (!pedited || pedited->epd.strength) keyFile.set_double ("EPD", "Strength", epd.strength); + if (!pedited || pedited->epd.gamma) keyFile.set_double ("EPD", "Gamma", epd.gamma); if (!pedited || pedited->epd.edgeStopping) keyFile.set_double ("EPD", "EdgeStopping", epd.edgeStopping); if (!pedited || pedited->epd.scale) keyFile.set_double ("EPD", "Scale", epd.scale); if (!pedited || pedited->epd.reweightingIterates) keyFile.set_integer ("EPD", "ReweightingIterates", epd.reweightingIterates); @@ -2228,6 +2230,7 @@ if (keyFile.has_group ("Directional Pyramid Denoising")) {//TODO: No longer an a if (keyFile.has_group ("EPD")) { if(keyFile.has_key("EPD", "Enabled")) { epd.enabled = keyFile.get_boolean ("EPD", "Enabled"); if (pedited) pedited->epd.enabled = true; } if(keyFile.has_key("EPD", "Strength")) { epd.strength = keyFile.get_double ("EPD", "Strength"); if (pedited) pedited->epd.strength = true; } + if(keyFile.has_key("EPD", "Gamma")) { epd.gamma = keyFile.get_double ("EPD", "Gamma"); if (pedited) pedited->epd.gamma = true; } if(keyFile.has_key("EPD", "EdgeStopping")) { epd.edgeStopping = keyFile.get_double ("EPD", "EdgeStopping"); if (pedited) pedited->epd.edgeStopping = true; } if(keyFile.has_key("EPD", "Scale")) { epd.scale = keyFile.get_double ("EPD", "Scale"); if (pedited) pedited->epd.scale = true; } if(keyFile.has_key("EPD", "ReweightingIterates")) { epd.reweightingIterates = keyFile.get_integer ("EPD", "ReweightingIterates"); if (pedited) pedited->epd.reweightingIterates = true; } @@ -2880,6 +2883,7 @@ bool ProcParams::operator== (const ProcParams& other) { && dirpyrDenoise.passes == other.dirpyrDenoise.passes && epd.enabled == other.epd.enabled && epd.strength == other.epd.strength + && epd.gamma == other.epd.gamma && epd.edgeStopping == other.epd.edgeStopping && epd.scale == other.epd.scale && epd.reweightingIterates == other.epd.reweightingIterates diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 21eb89f51..e5d273c57 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -588,6 +588,7 @@ class EPDParams{ public: bool enabled; double strength; + double gamma; double edgeStopping; double scale; int reweightingIterates; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index f5da261b4..fd9bfefdb 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -380,6 +380,8 @@ DIRPYREQUALIZER, //EvWavedgedetectthr DIRPYREQUALIZER, //EvWavedgedetectthr2 DIRPYREQUALIZER, //EvWavlinkedg DIRPYREQUALIZER, //EvWavCHCurve -DARKFRAME //EvPreProcessHotDeadThresh +DARKFRAME, //EvPreProcessHotDeadThresh +SHARPENING //EvEPDgamma + }; diff --git a/rtgui/epd.cc b/rtgui/epd.cc index e9ebba139..f20a66c13 100644 --- a/rtgui/epd.cc +++ b/rtgui/epd.cc @@ -27,22 +27,26 @@ EdgePreservingDecompositionUI::EdgePreservingDecompositionUI () : FoldableToolPa setEnabledTooltipMarkup(M("TP_EPD_TOOLTIP")); - strength = Gtk::manage(new Adjuster (M("TP_EPD_STRENGTH"), -2.0, 2.0, 0.01, 0.25)); + strength = Gtk::manage(new Adjuster (M("TP_EPD_STRENGTH"), -1.0, 2.0, 0.01, 0.8)); + gamma = Gtk::manage(new Adjuster (M("TP_EPD_GAMMA"), 0.8, 1.5, 0.01, 1.)); edgeStopping = Gtk::manage(new Adjuster (M("TP_EPD_EDGESTOPPING"), 0.1, 4.0, 0.01, 1.4)); scale = Gtk::manage(new Adjuster (M("TP_EPD_SCALE"), 0.1, 10.0, 0.01, 1.0)); reweightingIterates = Gtk::manage(new Adjuster (M("TP_EPD_REWEIGHTINGITERATES"), 0, 9, 1, 0)); strength->setAdjusterListener(this); + gamma->setAdjusterListener(this); edgeStopping->setAdjusterListener(this); scale->setAdjusterListener(this); reweightingIterates->setAdjusterListener(this); strength->show(); + gamma->show(); edgeStopping->show(); scale->show(); reweightingIterates->show(); pack_start(*strength); + pack_start(*gamma); pack_start(*edgeStopping); pack_start(*scale); pack_start(*reweightingIterates); @@ -53,6 +57,7 @@ void EdgePreservingDecompositionUI::read(const ProcParams *pp, const ParamsEdite if(pedited){ strength->setEditedState(pedited->epd.strength ? Edited : UnEdited); + gamma->setEditedState(pedited->epd.gamma ? Edited : UnEdited); edgeStopping->setEditedState(pedited->epd.edgeStopping ? Edited : UnEdited); scale->setEditedState(pedited->epd.scale ? Edited : UnEdited); reweightingIterates->setEditedState(pedited->epd.reweightingIterates ? Edited : UnEdited); @@ -62,6 +67,7 @@ void EdgePreservingDecompositionUI::read(const ProcParams *pp, const ParamsEdite setEnabled(pp->epd.enabled); strength->setValue(pp->epd.strength); + gamma->setValue(pp->epd.gamma); edgeStopping->setValue(pp->epd.edgeStopping); scale->setValue(pp->epd.scale); reweightingIterates->setValue(pp->epd.reweightingIterates); @@ -71,6 +77,7 @@ void EdgePreservingDecompositionUI::read(const ProcParams *pp, const ParamsEdite void EdgePreservingDecompositionUI::write(ProcParams *pp, ParamsEdited *pedited){ pp->epd.strength = strength->getValue(); + pp->epd.gamma = gamma->getValue(); pp->epd.edgeStopping = edgeStopping->getValue(); pp->epd.scale = scale->getValue(); pp->epd.reweightingIterates = reweightingIterates->getValue(); @@ -78,6 +85,7 @@ void EdgePreservingDecompositionUI::write(ProcParams *pp, ParamsEdited *pedited) if(pedited){ pedited->epd.strength = strength->getEditedState(); + pedited->epd.gamma = gamma->getEditedState(); pedited->epd.edgeStopping = edgeStopping->getEditedState(); pedited->epd.scale = scale->getEditedState(); pedited->epd.reweightingIterates = reweightingIterates->getEditedState(); @@ -87,17 +95,20 @@ void EdgePreservingDecompositionUI::write(ProcParams *pp, ParamsEdited *pedited) void EdgePreservingDecompositionUI::setDefaults(const ProcParams *defParams, const ParamsEdited *pedited){ strength->setDefault(defParams->epd.strength); + gamma->setDefault(defParams->epd.gamma); edgeStopping->setDefault(defParams->epd.edgeStopping); scale->setDefault(defParams->epd.scale); reweightingIterates->setDefault(defParams->epd.reweightingIterates); if(pedited){ strength->setDefaultEditedState(pedited->epd.strength ? Edited : UnEdited); + gamma->setDefaultEditedState(pedited->epd.gamma ? Edited : UnEdited); edgeStopping->setDefaultEditedState(pedited->epd.edgeStopping ? Edited : UnEdited); scale->setDefaultEditedState(pedited->epd.scale ? Edited : UnEdited); reweightingIterates->setDefaultEditedState(pedited->epd.reweightingIterates ? Edited : UnEdited); }else{ strength->setDefaultEditedState(Irrelevant); + gamma->setDefaultEditedState(Irrelevant); edgeStopping->setDefaultEditedState(Irrelevant); scale->setDefaultEditedState(Irrelevant); reweightingIterates->setDefaultEditedState(Irrelevant); @@ -108,6 +119,8 @@ void EdgePreservingDecompositionUI::adjusterChanged(Adjuster* a, double newval){ if(listener && getEnabled()){ if(a == strength) listener->panelChanged(EvEPDStrength, Glib::ustring::format(std::setw(2), std::fixed, std::setprecision(2), a->getValue())); + else if(a == gamma) + listener->panelChanged(EvEPDgamma, Glib::ustring::format(std::setw(2), std::fixed, std::setprecision(2), a->getValue())); else if(a == edgeStopping) listener->panelChanged(EvEPDEdgeStopping, Glib::ustring::format(std::setw(2), std::fixed, std::setprecision(2), a->getValue())); else if(a == scale) @@ -132,6 +145,7 @@ void EdgePreservingDecompositionUI::setBatchMode(bool batchMode){ ToolPanel::setBatchMode(batchMode); strength->showEditedCB(); + gamma->showEditedCB(); edgeStopping->showEditedCB(); scale->showEditedCB(); reweightingIterates->showEditedCB(); diff --git a/rtgui/epd.h b/rtgui/epd.h index c4bb0545e..c0559a10a 100644 --- a/rtgui/epd.h +++ b/rtgui/epd.h @@ -26,6 +26,7 @@ class EdgePreservingDecompositionUI : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel { protected: Adjuster *strength; + Adjuster *gamma; Adjuster *edgeStopping; Adjuster *scale; Adjuster *reweightingIterates; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index d1638a227..83fe14d82 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -201,6 +201,7 @@ void ParamsEdited::set (bool v) { dirpyrDenoise.rgbmethod = v; epd.enabled = v; epd.strength = v; + epd.gamma = v; epd.edgeStopping = v; epd.scale = v; epd.reweightingIterates = v; @@ -600,6 +601,7 @@ void ParamsEdited::initFrom (const std::vector epd.enabled = epd.enabled && p.epd.enabled == other.epd.enabled; epd.strength = epd.strength && p.epd.strength == other.epd.strength; + epd.gamma = epd.gamma && p.epd.gamma == other.epd.gamma; epd.edgeStopping = epd.edgeStopping && p.epd.edgeStopping == other.epd.edgeStopping; epd.scale = epd.scale && p.epd.scale == other.epd.scale; epd.reweightingIterates = epd.reweightingIterates && p.epd.reweightingIterates == other.epd.reweightingIterates; @@ -999,6 +1001,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten if (epd.enabled) toEdit.epd.enabled = mods.epd.enabled; if (epd.strength) toEdit.epd.strength = mods.epd.strength; + if (epd.gamma) toEdit.epd.gamma = mods.epd.gamma; if (epd.edgeStopping) toEdit.epd.edgeStopping = mods.epd.edgeStopping; if (epd.scale) toEdit.epd.scale = mods.epd.scale; if (epd.reweightingIterates) toEdit.epd.reweightingIterates = mods.epd.reweightingIterates; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index c95d2fea9..1cad0cbbb 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -291,6 +291,7 @@ class EPDParamsEdited{ public: bool enabled; bool strength; + bool gamma; bool edgeStopping; bool scale; bool reweightingIterates;