diff --git a/rtdata/languages/default b/rtdata/languages/default
index 499005d32..5bf1863df 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -726,6 +726,7 @@ HISTORY_MSG_490;DRC - Amount
HISTORY_MSG_491;White Balance
HISTORY_MSG_492;RGB Curves
HISTORY_MSG_493;L*a*b* Adjustments
+HISTORY_MSG_494;Capture Sharpening
HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors
HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction
HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction
@@ -763,6 +764,11 @@ HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness
HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius
HISTORY_MSG_METADATA_MODE;Metadata copy mode
HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold
+HISTORY_MSG_PDSHARPEN_CONTRAST;CAS - Contrast threshold
+HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CAS - Auto threshold
+HISTORY_MSG_PDSHARPEN_GAMMA;CAS - Gamma
+HISTORY_MSG_PDSHARPEN_ITERATIONS;CAS - Iterations
+HISTORY_MSG_PDSHARPEN_RADIUS;CAS - Radius
HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion
HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction
HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
@@ -773,6 +779,7 @@ HISTORY_MSG_RAW_BORDER;Raw border
HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling
HISTORY_MSG_SHARPENING_BLUR;Sharpening - Blur radius
HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold
+HISTORY_MSG_SHARPENING_GAMMA;Sharpening - Gamma
HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace
HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light
HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength
@@ -1792,6 +1799,7 @@ TP_PCVIGNETTE_ROUNDNESS;Roundness
TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse,\n100 = circle.
TP_PCVIGNETTE_STRENGTH;Strength
TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners).
+TP_PDSHARPENING_LABEL;Capture Sharpening
TP_PERSPECTIVE_HORIZONTAL;Horizontal
TP_PERSPECTIVE_LABEL;Perspective
TP_PERSPECTIVE_VERTICAL;Vertical
@@ -2020,6 +2028,7 @@ TP_SHARPENING_BLUR;Blur radius
TP_SHARPENING_CONTRAST;Contrast threshold
TP_SHARPENING_EDRADIUS;Radius
TP_SHARPENING_EDTOLERANCE;Edge tolerance
+TP_SHARPENING_GAMMA;Gamma
TP_SHARPENING_HALOCONTROL;Halo control
TP_SHARPENING_HCAMOUNT;Amount
TP_SHARPENING_LABEL;Sharpening
diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt
index c5c8afa82..f52cfa256 100644
--- a/rtengine/CMakeLists.txt
+++ b/rtengine/CMakeLists.txt
@@ -32,6 +32,7 @@ set(CAMCONSTSFILE "camconst.json")
set(RTENGINESOURCEFILES
badpixels.cc
CA_correct_RT.cc
+ capturesharpening.cc
EdgePreservingDecomposition.cc
FTblockDN.cc
PF_correct_RT.cc
diff --git a/rtengine/capturesharpening.cc b/rtengine/capturesharpening.cc
new file mode 100644
index 000000000..eb95d2633
--- /dev/null
+++ b/rtengine/capturesharpening.cc
@@ -0,0 +1,218 @@
+/*
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2019 Ingo Weyrich (heckflosse67@gmx.de)
+ *
+ * RawTherapee is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * RawTherapee is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with RawTherapee. If not, see .
+*/
+#include
+#include
+
+#include "jaggedarray.h"
+#include "rtengine.h"
+#include "rawimagesource.h"
+#include "rt_math.h"
+#include "improcfun.h"
+#include "procparams.h"
+#include "color.h"
+#include "gauss.h"
+#include "rt_algo.h"
+#define BENCHMARK
+#include "StopWatch.h"
+#ifdef _OPENMP
+#include
+#endif
+#include "opthelper.h"
+#include "../rtgui/multilangmgr.h"
+
+namespace {
+void CaptureDeconvSharpening (float** luminance, float** tmp, const float * const * blend, int W, int H, double sigma, int iterations, rtengine::ProgressListener* plistener, double start, double step)
+{
+
+ rtengine::JaggedArray tmpI(W, H);
+
+#ifdef _OPENMP
+ #pragma omp parallel
+#endif
+ {
+#ifdef _OPENMP
+ #pragma omp for
+#endif
+ for (int i = 0; i < H; i++) {
+ for(int j = 0; j < W; j++) {
+ tmpI[i][j] = max(luminance[i][j], 0.f);
+ }
+ }
+
+ for (int k = 0; k < iterations; k++) {
+ // apply gaussian blur and divide luminance by result of gaussian blur
+ gaussianBlur(tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance);
+ gaussianBlur(tmp, tmpI, W, H, sigma, nullptr, GAUSS_MULT);
+ if (plistener) {
+#ifdef _OPENMP
+ #pragma omp single
+#endif
+ start += step;
+ plistener->setProgress(start);
+ }
+ } // end for
+
+#ifdef _OPENMP
+ #pragma omp for
+#endif
+
+ for (int i = 0; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ luminance[i][j] = rtengine::intp(blend[i][j], max(tmpI[i][j], 0.0f), luminance[i][j]);
+ }
+ }
+ } // end parallel
+}
+
+}
+
+namespace rtengine
+{
+
+void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) {
+BENCHFUN
+
+
+ if (plistener) {
+ plistener->setProgressStr(M("TP_PDSHARPENING_LABEL"));
+ plistener->setProgress(0.0);
+ }
+
+ const float xyz_rgb[3][3] = { // XYZ from RGB
+ { 0.412453, 0.357580, 0.180423 },
+ { 0.212671, 0.715160, 0.072169 },
+ { 0.019334, 0.119193, 0.950227 }
+ };
+
+ float contrast = conrastThreshold / 100.f;
+
+ array2D& redVals = redCache ? *redCache : red;
+ array2D& greenVals = greenCache ? *greenCache : green;
+ array2D& blueVals = blueCache ? *blueCache : blue;
+
+ if (showMask) {
+ StopWatch Stop1("Show mask");
+ array2D& L = blue; // blue will be overridden anyway => we can use its buffer to store L
+#ifdef _OPENMP
+ #pragma omp parallel for
+#endif
+
+ for (int i = 0; i < H; ++i) {
+ Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W);
+ }
+ if (plistener) {
+ plistener->setProgress(0.1);
+ }
+ array2D& blend = red; // red will be overridden anyway => we can use its buffer to store the blend mask
+ buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast);
+ if (plistener) {
+ plistener->setProgress(0.2);
+ }
+ conrastThreshold = contrast * 100.f;
+#ifdef _OPENMP
+ #pragma omp parallel for
+#endif
+ for (int i = 0; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ red[i][j] = green[i][j] = blue[i][j] = blend[i][j] * 16384.f;
+ }
+ }
+ if (plistener) {
+ plistener->setProgress(1.0);
+ }
+ return;
+ }
+
+ array2D* Lbuffer = nullptr;
+ if (!redCache) {
+ Lbuffer = new array2D(W, H);
+ }
+
+ array2D* YOldbuffer = nullptr;
+ if (!greenCache) {
+ YOldbuffer = new array2D(W, H);
+ }
+
+ array2D* YNewbuffer = nullptr;
+ if (!blueCache) {
+ YNewbuffer = new array2D(W, H);
+ }
+ array2D& L = Lbuffer ? *Lbuffer : red;
+ array2D& YOld = YOldbuffer ? * YOldbuffer : green;
+ array2D& YNew = YNewbuffer ? * YNewbuffer : blue;
+
+ StopWatch Stop1("rgb2YL");
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic, 16)
+#endif
+ for (int i = 0; i < H; ++i) {
+ Color::RGB2L(redVals[i], greenVals[i], blueVals[i], L[i], xyz_rgb, W);
+ Color::RGB2Y(redVals[i], greenVals[i], blueVals[i], YOld[i], YNew[i], sharpeningParams.gamma, W);
+ }
+ if (plistener) {
+ plistener->setProgress(0.1);
+ }
+ // calculate contrast based blend factors to reduce sharpening in regions with low contrast
+ JaggedArray blend(W, H);
+ buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast);
+ if (plistener) {
+ plistener->setProgress(0.2);
+ }
+ conrastThreshold = contrast * 100.f;
+
+ Stop1.stop();
+ array2D& tmp = L; // L is not used anymore now => we can use its buffer as the needed temporary buffer
+ CaptureDeconvSharpening(YNew, tmp, blend, W, H, sharpeningParams.deconvradius, sharpeningParams.deconviter, plistener, 0.2, (0.9 - 0.2) / sharpeningParams.deconviter);
+ if (plistener) {
+ plistener->setProgress(0.9);
+ }
+ StopWatch Stop2("Y2RGB");
+ const float gamma = sharpeningParams.gamma;
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic, 16)
+#endif
+ for (int i = 0; i < H; ++i) {
+ int j = 0;
+#ifdef __SSE2__
+ const vfloat gammav = F2V(gamma);
+ for (; j < W - 3; j += 4) {
+ const vfloat factor = pow_F(vmaxf(LVFU(YNew[i][j]), ZEROV) / vmaxf(LVFU(YOld[i][j]), F2V(0.00001f)), gammav);
+ STVFU(red[i][j], LVFU(redVals[i][j]) * factor);
+ STVFU(green[i][j], LVFU(greenVals[i][j]) * factor);
+ STVFU(blue[i][j], LVFU(blueVals[i][j]) * factor);
+ }
+
+#endif
+ for (; j < W; ++j) {
+ const float factor = pow_F(std::max(YNew[i][j], 0.f) / std::max(YOld[i][j], 0.00001f), gamma);
+ red[i][j] = redVals[i][j] * factor;
+ green[i][j] = greenVals[i][j] * factor;
+ blue[i][j] = blueVals[i][j] * factor;
+ }
+ }
+ Stop2.stop();
+ delete Lbuffer;
+ delete YOldbuffer;
+ delete YNewbuffer;
+ if (plistener) {
+ plistener->setProgress(1.0);
+ }
+}
+
+} /* namespace */
diff --git a/rtengine/color.cc b/rtengine/color.cc
index 24266e52d..7a91f0073 100644
--- a/rtengine/color.cc
+++ b/rtengine/color.cc
@@ -1835,21 +1835,21 @@ void Color::RGB2L(float *R, float *G, float *B, float *L, const float wp[3][3],
{
#ifdef __SSE2__
- vfloat minvalfv = F2V(0.f);
- vfloat maxvalfv = F2V(MAXVALF);
+ const vfloat maxvalfv = F2V(MAXVALF);
+ const vfloat rmv = F2V(wp[1][0]);
+ const vfloat gmv = F2V(wp[1][1]);
+ const vfloat bmv = F2V(wp[1][2]);
#endif
int i = 0;
#ifdef __SSE2__
- for(;i < width - 3; i+=4) {
+ for(; i < width - 3; i+=4) {
const vfloat rv = LVFU(R[i]);
const vfloat gv = LVFU(G[i]);
const vfloat bv = LVFU(B[i]);
- const vfloat yv = F2V(wp[1][0]) * rv + F2V(wp[1][1]) * gv + F2V(wp[1][2]) * bv;
+ const vfloat yv = rmv * rv + gmv * gv + bmv * bv;
- vmask maxMask = vmaskf_gt(yv, maxvalfv);
- vmask minMask = vmaskf_lt(yv, minvalfv);
- if (_mm_movemask_ps((vfloat)vorm(maxMask, minMask))) {
+ if (_mm_movemask_ps((vfloat)vorm(vmaskf_gt(yv, maxvalfv), vmaskf_lt(yv, ZEROV)))) {
// take slower code path for all 4 pixels if one of the values is > MAXVALF. Still faster than non SSE2 version
for(int k = 0; k < 4; ++k) {
float y = yv[k];
@@ -1860,7 +1860,7 @@ void Color::RGB2L(float *R, float *G, float *B, float *L, const float wp[3][3],
}
}
#endif
- for(;i < width; ++i) {
+ for(; i < width; ++i) {
const float rv = R[i];
const float gv = G[i];
const float bv = B[i];
diff --git a/rtengine/color.h b/rtengine/color.h
index 214f420d7..b859fb0cf 100644
--- a/rtengine/color.h
+++ b/rtengine/color.h
@@ -1804,6 +1804,31 @@ public:
return (hr);
}
+ static inline void RGB2Y(const float* R, const float* G, const float* B, float* Y1, float * Y2, float gamma, int W) {
+ gamma = 1.f / gamma;
+ int i = 0;
+#ifdef __SSE2__
+ const vfloat gammav = F2V(gamma);
+ const vfloat c1v = F2V(0.2627f);
+ const vfloat c2v = F2V(0.6780f);
+ const vfloat c3v = F2V(0.0593f);
+ for (; i < W - 3; i += 4) {
+ const vfloat Rv = vmaxf(LVFU(R[i]), ZEROV);
+ const vfloat Gv = vmaxf(LVFU(G[i]), ZEROV);
+ const vfloat Bv = vmaxf(LVFU(B[i]), ZEROV);
+ vfloat yv = pow_F(c1v * Rv + c2v * Gv + c3v * Bv, gammav);
+ STVFU(Y1[i], yv);
+ STVFU(Y2[i], yv);
+ }
+#endif
+ for (; i < W; ++i) {
+ const float r = std::max(R[i], 0.f);
+ const float g = std::max(G[i], 0.f);
+ const float b = std::max(B[i], 0.f);
+ Y1[i] = Y2[i] = pow_F(0.2627f * r + 0.6780f * g + 0.0593f * b, gamma);
+ }
+ }
+
};
}
diff --git a/rtengine/gauss.cc b/rtengine/gauss.cc
index 40014f211..ca330f9b9 100644
--- a/rtengine/gauss.cc
+++ b/rtengine/gauss.cc
@@ -17,14 +17,56 @@
* along with RawTherapee. If not, see .
*/
#include "gauss.h"
+#include "rt_math.h"
#include
#include
#include "opthelper.h"
#include "boxblur.h"
-
namespace
{
+void compute7x7kernel(float sigma, float kernel[7][7]) {
+ const double temp = -2.f * rtengine::SQR(sigma);
+ float sum = 0.f;
+ for (int i = -3; i <= 3; ++i) {
+ for (int j = -3; j <= 3; ++j) {
+ if((rtengine::SQR(i) + rtengine::SQR(j)) <= rtengine::SQR(3.0 * 1.15)) {
+ kernel[i + 3][j + 3] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp);
+ sum += kernel[i + 3][j + 3];
+ } else {
+ kernel[i + 3][j + 3] = 0.f;
+ }
+ }
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ kernel[i][j] /= sum;
+ }
+ }
+}
+
+void compute5x5kernel(float sigma, float kernel[5][5]) {
+ const double temp = -2.f * rtengine::SQR(sigma);
+ float sum = 0.f;
+ for (int i = -2; i <= 2; ++i) {
+ for (int j = -2; j <= 2; ++j) {
+ if((rtengine::SQR(i) + rtengine::SQR(j)) <= rtengine::SQR(3.0 * 0.84)) {
+ kernel[i + 2][j + 2] = std::exp((rtengine::SQR(i) + rtengine::SQR(j)) / temp);
+ sum += kernel[i + 2][j + 2];
+ } else {
+ kernel[i + 2][j + 2] = 0.f;
+ }
+ }
+ }
+
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ kernel[i][j] /= sum;
+ }
+ }
+}
+
template void calculateYvVFactors( const T sigma, T &b1, T &b2, T &b3, T &B, T M[3][3])
{
// coefficient calculation
@@ -207,6 +249,174 @@ template void gauss3x3div (T** RESTRICT src, T** RESTRICT dst, T** REST
}
}
+template void gauss7x7div (T** RESTRICT src, T** RESTRICT dst, T** RESTRICT divBuffer, const int W, const int H, float sigma)
+{
+
+ float kernel[7][7];
+ compute7x7kernel(sigma, kernel);
+
+ const float c31 = kernel[0][2];
+ const float c30 = kernel[0][3];
+ const float c22 = kernel[1][1];
+ const float c21 = kernel[1][2];
+ const float c20 = kernel[1][3];
+ const float c11 = kernel[2][2];
+ const float c10 = kernel[2][3];
+ const float c00 = kernel[3][3];
+
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic, 16) nowait
+#endif
+
+ for (int i = 3; i < H - 3; ++i) {
+ dst[i][0] = dst[i][1] = dst[i][2] = 1.f;
+ // I tried hand written SSE code but gcc vectorizes better
+ for (int j = 3; j < W - 3; ++j) {
+ const float val = c31 * (src[i - 3][j - 1] + src[i - 3][j + 1] + src[i - 1][j - 3] + src[i - 1][j + 3] + src[i + 1][j - 3] + src[i + 1][j + 3] + src[i + 3][j - 1] + src[i + 3][j + 1]) +
+ c30 * (src[i - 3][j] + src[i][j - 3] + src[i][j + 3] + src[i + 3][j]) +
+ c22 * (src[i - 2][j - 2] + src[i - 2][j + 2] + src[i + 2][j - 2] + src[i + 2][j + 2]) +
+ c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] * c21 + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) +
+ c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) +
+ c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) +
+ c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) +
+ c00 * src[i][j];
+
+ dst[i][j] = divBuffer[i][j] / std::max(val, 0.00001f);
+ }
+ dst[i][W - 3] = dst[i][W - 2] = dst[i][W - 1] = 1.f;
+ }
+
+ // first and last rows
+#ifdef _OPENMP
+ #pragma omp single
+#endif
+ {
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < W; ++j) {
+ dst[i][j] = 1.f;
+ }
+ }
+ for (int i = H - 3 ; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ dst[i][j] = 1.f;
+ }
+ }
+ }
+}
+
+template void gauss5x5div (T** RESTRICT src, T** RESTRICT dst, T** RESTRICT divBuffer, const int W, const int H, float sigma)
+{
+
+ float kernel[5][5];
+ compute5x5kernel(sigma, kernel);
+
+ const float c21 = kernel[0][1];
+ const float c20 = kernel[0][2];
+ const float c11 = kernel[1][1];
+ const float c10 = kernel[1][2];
+ const float c00 = kernel[2][2];
+
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic, 16) nowait
+#endif
+
+ for (int i = 2; i < H - 2; ++i) {
+ dst[i][0] = dst[i][1] = 1.f;
+ // I tried hand written SSE code but gcc vectorizes better
+ for (int j = 2; j < W - 2; ++j) {
+ const float val = c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) +
+ c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) +
+ c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) +
+ c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) +
+ c00 * src[i][j];
+
+ dst[i][j] = divBuffer[i][j] / std::max(val, 0.00001f);
+ }
+ dst[i][W - 2] = dst[i][W - 1] = 1.f;
+ }
+
+ // first and last rows
+#ifdef _OPENMP
+ #pragma omp single
+#endif
+ {
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < W; ++j) {
+ dst[i][j] = 1.f;
+ }
+ }
+ for (int i = H - 2 ; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ dst[i][j] = 1.f;
+ }
+ }
+ }
+}
+
+template void gauss7x7mult (T** RESTRICT src, T** RESTRICT dst, const int W, const int H, float sigma)
+{
+
+ float kernel[7][7];
+ compute7x7kernel(sigma, kernel);
+ const float c31 = kernel[0][2];
+ const float c30 = kernel[0][3];
+ const float c22 = kernel[1][1];
+ const float c21 = kernel[1][2];
+ const float c20 = kernel[1][3];
+ const float c11 = kernel[2][2];
+ const float c10 = kernel[2][3];
+ const float c00 = kernel[3][3];
+
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic, 16)
+#endif
+
+ for (int i = 3; i < H - 3; ++i) {
+ // I tried hand written SSE code but gcc vectorizes better
+ for (int j = 3; j < W - 3; ++j) {
+ const float val = c31 * (src[i - 3][j - 1] + src[i - 3][j + 1] + src[i - 1][j - 3] + src[i - 1][j + 3] + src[i + 1][j - 3] + src[i + 1][j + 3] + src[i + 3][j - 1] + src[i + 3][j + 1]) +
+ c30 * (src[i - 3][j] + src[i][j - 3] + src[i][j + 3] + src[i + 3][j]) +
+ c22 * (src[i - 2][j - 2] + src[i - 2][j + 2] + src[i + 2][j - 2] + src[i + 2][j + 2]) +
+ c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] * c21 + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) +
+ c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) +
+ c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) +
+ c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) +
+ c00 * src[i][j];
+
+ dst[i][j] *= val;
+ }
+ }
+}
+
+template void gauss5x5mult (T** RESTRICT src, T** RESTRICT dst, const int W, const int H, float sigma)
+{
+
+ float kernel[5][5];
+ compute5x5kernel(sigma, kernel);
+
+ const float c21 = kernel[0][1];
+ const float c20 = kernel[0][2];
+ const float c11 = kernel[1][1];
+ const float c10 = kernel[1][2];
+ const float c00 = kernel[2][2];
+
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic, 16)
+#endif
+
+ for (int i = 2; i < H - 2; ++i) {
+ // I tried hand written SSE code but gcc vectorizes better
+ for (int j = 2; j < W - 2; ++j) {
+ const float val = c21 * (src[i - 2][j - 1] + src[i - 2][j + 1] + src[i - 1][j - 2] + src[i - 1][j + 2] + src[i + 1][j - 2] + src[i + 1][j + 2] + src[i + 2][j - 1] + src[i + 2][j + 1]) +
+ c20 * (src[i - 2][j] + src[i][j - 2] + src[i][j + 2] + src[i + 2][j]) +
+ c11 * (src[i - 1][j - 1] + src[i - 1][j + 1] + src[i + 1][j - 1] + src[i + 1][j + 1]) +
+ c10 * (src[i - 1][j] + src[i][j - 1] + src[i][j + 1] + src[i + 1][j]) +
+ c00 * src[i][j];
+
+ dst[i][j] *= val;
+ }
+ }
+}
// use separated filter if the support window is small and src == dst
template void gaussHorizontal3 (T** src, T** dst, int W, int H, const float c0, const float c1)
@@ -1143,6 +1353,8 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int
{
static constexpr auto GAUSS_SKIP = 0.25;
static constexpr auto GAUSS_3X3_LIMIT = 0.6;
+ static constexpr auto GAUSS_5X5_LIMIT = 0.84;
+ static constexpr auto GAUSS_7X7_LIMIT = 1.15;
static constexpr auto GAUSS_DOUBLE = 25.0;
if(buffer) {
@@ -1241,14 +1453,26 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int
if (sigma < GAUSS_DOUBLE) {
switch (gausstype) {
case GAUSS_MULT : {
- gaussHorizontalSse (src, src, W, H, sigma);
- gaussVerticalSsemult (src, dst, W, H, sigma);
+ if (sigma <= GAUSS_5X5_LIMIT && src != dst) {
+ gauss5x5mult(src, dst, W, H, sigma);
+ } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) {
+ gauss7x7mult(src, dst, W, H, sigma);
+ } else {
+ gaussHorizontalSse (src, src, W, H, sigma);
+ gaussVerticalSsemult (src, dst, W, H, sigma);
+ }
break;
}
case GAUSS_DIV : {
- gaussHorizontalSse (src, dst, W, H, sigma);
- gaussVerticalSsediv (dst, dst, buffer2, W, H, sigma);
+ if (sigma <= GAUSS_5X5_LIMIT && src != dst) {
+ gauss5x5div (src, dst, buffer2, W, H, sigma);
+ } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) {
+ gauss7x7div (src, dst, buffer2, W, H, sigma);
+ } else {
+ gaussHorizontalSse (src, dst, W, H, sigma);
+ gaussVerticalSsediv (dst, dst, buffer2, W, H, sigma);
+ }
break;
}
@@ -1268,14 +1492,26 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int
if (sigma < GAUSS_DOUBLE) {
switch (gausstype) {
case GAUSS_MULT : {
- gaussHorizontal (src, src, W, H, sigma);
- gaussVerticalmult (src, dst, W, H, sigma);
+ if (sigma <= GAUSS_5X5_LIMIT && src != dst) {
+ gauss5x5mult(src, dst, W, H, sigma);
+ } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) {
+ gauss7x7mult(src, dst, W, H, sigma);
+ } else {
+ gaussHorizontal (src, src, W, H, sigma);
+ gaussVerticalmult (src, dst, W, H, sigma);
+ }
break;
}
case GAUSS_DIV : {
- gaussHorizontal (src, dst, W, H, sigma);
- gaussVerticaldiv (dst, dst, buffer2, W, H, sigma);
+ if (sigma <= GAUSS_5X5_LIMIT && src != dst) {
+ gauss5x5div (src, dst, buffer2, W, H, sigma);
+ } else if (sigma <= GAUSS_7X7_LIMIT && src != dst) {
+ gauss7x7div (src, dst, buffer2, W, H, sigma);
+ } else {
+ gaussHorizontal (src, dst, W, H, sigma);
+ gaussVerticaldiv (dst, dst, buffer2, W, H, sigma);
+ }
break;
}
diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h
index 705d29d62..c62dfe7d8 100644
--- a/rtengine/imagesource.h
+++ b/rtengine/imagesource.h
@@ -46,7 +46,7 @@ struct LensProfParams;
struct RAWParams;
struct RetinexParams;
struct ToneCurveParams;
-
+struct CaptureSharpeningParams;
}
class ImageMatrices
@@ -83,7 +83,7 @@ public:
virtual void preprocess (const procparams::RAWParams &raw, const procparams::LensProfParams &lensProf, const procparams::CoarseTransformParams& coarse, bool prepareDenoise = true) {};
virtual void filmNegativeProcess (const procparams::FilmNegativeParams ¶ms) {};
virtual bool getFilmNegativeExponents (Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams& currentParams, std::array& newExps) { return false; };
- virtual void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold) {};
+ virtual void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache = false) {};
virtual void retinex (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &deh, const procparams::ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) {};
virtual void retinexPrepareCurves (const procparams::RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI) {};
virtual void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI) {};
@@ -182,6 +182,7 @@ public:
return this;
}
virtual void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) = 0;
+ virtual void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) = 0;
};
}
diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc
index b74c14e2e..c6268576a 100644
--- a/rtengine/improccoordinator.cc
+++ b/rtengine/improccoordinator.cc
@@ -53,6 +53,7 @@ ImProcCoordinator::ImProcCoordinator() :
softProof(false),
gamutCheck(false),
sharpMask(false),
+ sharpMaskChanged(false),
scale(10),
highDetailPreprocessComputed(false),
highDetailRawComputed(false),
@@ -120,6 +121,7 @@ ImProcCoordinator::ImProcCoordinator() :
flatFieldAutoClipListener(nullptr),
bayerAutoContrastListener(nullptr),
xtransAutoContrastListener(nullptr),
+ pdSharpenAutoContrastListener(nullptr),
frameCountListener(nullptr),
imageTypeListener(nullptr),
actListener(nullptr),
@@ -330,18 +332,31 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
}
bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicAutoContrast : params->raw.xtranssensor.dualDemosaicAutoContrast;
double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast;
- imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic
+ imgsrc->demosaic(rp, autoContrast, contrastThreshold, params->pdsharpening.enabled);
if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) {
- bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0);
- }
- if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) {
+ bayerAutoContrastListener->autoContrastChanged(contrastThreshold);
+ } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) {
xtransAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0);
}
-
// if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag
- todo |= M_INIT;
+ todo |= (M_INIT | M_CSHARP);
+ }
+
+ if ((todo & (M_RAW | M_CSHARP)) && params->pdsharpening.enabled) {
+ double pdSharpencontrastThreshold = params->pdsharpening.contrast;
+ imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold);
+ if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) {
+ pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold);
+ }
+ }
+
+
+ if ((todo & M_RAW)
+ || (!highDetailRawComputed && highDetailNeeded)
+ || (params->toneCurve.hrenabled && params->toneCurve.method != "Color" && imgsrc->isRGBSourceModified())
+ || (!params->toneCurve.hrenabled && params->toneCurve.method == "Color" && imgsrc->isRGBSourceModified())) {
if (highDetailNeeded) {
highDetailRawComputed = true;
} else {
@@ -1344,9 +1359,16 @@ void ImProcCoordinator::getSoftProofing(bool &softProof, bool &gamutCheck)
gamutCheck = this->gamutCheck;
}
-void ImProcCoordinator::setSharpMask (bool sharpMask)
+ProcEvent ImProcCoordinator::setSharpMask (bool sharpMask)
{
- this->sharpMask = sharpMask;
+ if (this->sharpMask != sharpMask) {
+ sharpMaskChanged = true;
+ this->sharpMask = sharpMask;
+ return params->pdsharpening.enabled ? rtengine::EvPdShrMaskToggled : rtengine::EvShrEnabled;
+ } else {
+ sharpMaskChanged = false;
+ return rtengine::EvShrEnabled;
+ }
}
void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb)
@@ -1546,8 +1568,11 @@ void ImProcCoordinator::process()
|| params->retinex != nextParams->retinex
|| params->wavelet != nextParams->wavelet
|| params->dirpyrequalizer != nextParams->dirpyrequalizer
- || params->dehaze != nextParams->dehaze;
+ || params->dehaze != nextParams->dehaze
+ || params->pdsharpening != nextParams->pdsharpening
+ || sharpMaskChanged;
+ sharpMaskChanged = false;
*params = *nextParams;
int change = changeSinceLast;
changeSinceLast = 0;
diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h
index fbe233607..df4ec8134 100644
--- a/rtengine/improccoordinator.h
+++ b/rtengine/improccoordinator.h
@@ -77,7 +77,7 @@ protected:
bool softProof;
bool gamutCheck;
bool sharpMask;
-
+ bool sharpMaskChanged;
int scale;
bool highDetailPreprocessComputed;
bool highDetailRawComputed;
@@ -161,6 +161,7 @@ protected:
FlatFieldAutoClipListener *flatFieldAutoClipListener;
AutoContrastListener *bayerAutoContrastListener;
AutoContrastListener *xtransAutoContrastListener;
+ AutoContrastListener *pdSharpenAutoContrastListener;
FrameCountListener *frameCountListener;
ImageTypeListener *imageTypeListener;
@@ -277,7 +278,7 @@ public:
void getMonitorProfile (Glib::ustring& profile, RenderingIntent& intent) const override;
void setSoftProofing (bool softProof, bool gamutCheck) override;
void getSoftProofing (bool &softProof, bool &gamutCheck) override;
- void setSharpMask (bool sharpMask) override;
+ ProcEvent setSharpMask (bool sharpMask) override;
bool updateTryLock () override
{
return updaterThreadStart.trylock();
@@ -363,6 +364,11 @@ public:
xtransAutoContrastListener = acl;
}
+ void setpdSharpenAutoContrastListener (AutoContrastListener* acl) override
+ {
+ pdSharpenAutoContrastListener = acl;
+ }
+
void setImageTypeListener (ImageTypeListener* itl) override
{
imageTypeListener = itl;
diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h
index 8278e2f37..7f159090a 100644
--- a/rtengine/improcfun.h
+++ b/rtengine/improcfun.h
@@ -248,7 +248,7 @@ public:
void Lanczos(const LabImage* src, LabImage* dst, float scale);
void Lanczos(const Imagefloat* src, Imagefloat* dst, float scale);
- void deconvsharpening(float** luminance, float** buffer, int W, int H, const procparams::SharpeningParams &sharpenParam);
+ void deconvsharpening(float** luminance, float** buffer, const float* const * blend, int W, int H, const procparams::SharpeningParams &sharpenParam, double Scale);
void MLsharpen(LabImage* lab); // Manuel's clarity / sharpening
void MLmicrocontrast(float** luminance, int W, int H); //Manuel's microcontrast
void MLmicrocontrast(LabImage* lab); //Manuel's microcontrast
diff --git a/rtengine/ipsharpen.cc b/rtengine/ipsharpen.cc
index 73510fe92..bbd1de155 100644
--- a/rtengine/ipsharpen.cc
+++ b/rtengine/ipsharpen.cc
@@ -158,7 +158,7 @@ namespace rtengine
extern const Settings* settings;
-void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, int W, int H, const SharpeningParams &sharpenParam)
+void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, const float * const * blend, int W, int H, const SharpeningParams &sharpenParam, double Scale)
{
if (sharpenParam.deconvamount == 0 && sharpenParam.blurradius < 0.25f) {
return;
@@ -175,10 +175,6 @@ BENCHFUN
}
}
- // calculate contrast based blend factors to reduce sharpening in regions with low contrast
- JaggedArray blend(W, H);
- float contrast = sharpenParam.contrast / 100.f;
- buildBlendMask(luminance, blend, W, H, contrast, 1.f);
JaggedArray* blurbuffer = nullptr;
if (sharpenParam.blurradius >= 0.25f) {
@@ -201,7 +197,7 @@ BENCHFUN
}
const float damping = sharpenParam.deconvdamping / 5.0;
const bool needdamp = sharpenParam.deconvdamping > 0;
- const double sigma = sharpenParam.deconvradius / scale;
+ const double sigma = sharpenParam.deconvradius / Scale;
const float amount = sharpenParam.deconvamount / 100.f;
#ifdef _OPENMP
@@ -254,11 +250,12 @@ void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpen
int W = lab->W, H = lab->H;
+ // calculate contrast based blend factors to reduce sharpening in regions with low contrast
+ JaggedArray blend(W, H);
+ float contrast = sharpenParam.contrast / 100.f;
+ buildBlendMask(lab->L, blend, W, H, contrast, 1.f);
+
if(showMask) {
- // calculate contrast based blend factors to reduce sharpening in regions with low contrast
- JaggedArray blend(W, H);
- float contrast = sharpenParam.contrast / 100.f;
- buildBlendMask(lab->L, blend, W, H, contrast, 1.f);
#ifdef _OPENMP
#pragma omp parallel for
#endif
@@ -274,7 +271,7 @@ void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpen
JaggedArray b2(W, H);
if (sharpenParam.method == "rld") {
- deconvsharpening (lab->L, b2, lab->W, lab->H, sharpenParam);
+ deconvsharpening (lab->L, b2, blend, lab->W, lab->H, sharpenParam, scale);
return;
}
BENCHFUN
@@ -290,11 +287,6 @@ BENCHFUN
}
}
- // calculate contrast based blend factors to reduce sharpening in regions with low contrast
- JaggedArray blend(W, H);
- float contrast = sharpenParam.contrast / 100.f;
- buildBlendMask(lab->L, blend, W, H, contrast);
-
JaggedArray blur(W, H);
if (sharpenParam.blurradius >= 0.25f) {
@@ -886,11 +878,11 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask)
int W = ncie->W, H = ncie->H;
+ // calculate contrast based blend factors to reduce sharpening in regions with low contrast
+ JaggedArray blend(W, H);
+ float contrast = params->sharpening.contrast / 100.f;
+ buildBlendMask(ncie->sh_p, blend, W, H, contrast);
if(showMask) {
- // calculate contrast based blend factors to reduce sharpening in regions with low contrast
- JaggedArray blend(W, H);
- float contrast = params->sharpening.contrast / 100.f;
- buildBlendMask(ncie->sh_p, blend, W, H, contrast);
#ifdef _OPENMP
#pragma omp parallel for
#endif
@@ -903,9 +895,8 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask)
return;
}
-
if (params->sharpening.method == "rld") {
- deconvsharpening (ncie->sh_p, b2, ncie->W, ncie->H, params->sharpening);
+ deconvsharpening (ncie->sh_p, b2, blend, ncie->W, ncie->H, params->sharpening, scale);
return;
}
@@ -921,11 +912,6 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask)
}
}
- // calculate contrast based blend factors to reduce sharpening in regions with low contrast
- JaggedArray blend(W, H);
- float contrast = params->sharpening.contrast / 100.f;
- buildBlendMask(ncie->sh_p, blend, W, H, contrast);
-
#ifdef _OPENMP
#pragma omp parallel
#endif
diff --git a/rtengine/procevents.h b/rtengine/procevents.h
index 32fdb0f4e..ca098f216 100644
--- a/rtengine/procevents.h
+++ b/rtengine/procevents.h
@@ -520,7 +520,8 @@ enum ProcEventCode {
EvWBEnabled = 490,
EvRGBEnabled = 491,
EvLEnabled = 492,
-// EvPixelShiftOneGreen = 493, can be reused
+ EvPdShrEnabled = 493,
+ EvPdShrMaskToggled = 494,
NUMOFEVENTS
diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 0d7d55286..2fcc5d358 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -1102,7 +1102,9 @@ void ColorToningParams::getCurves(ColorGradientCurve& colorCurveLUT, OpacityCurv
SharpeningParams::SharpeningParams() :
enabled(false),
contrast(20.0),
+ autoContrast(false),
blurradius(0.2),
+ gamma(1.0),
radius(0.5),
amount(200),
threshold(20, 80, 2000, 1200, false),
@@ -1125,9 +1127,11 @@ bool SharpeningParams::operator ==(const SharpeningParams& other) const
enabled == other.enabled
&& contrast == other.contrast
&& blurradius == other.blurradius
+ && gamma == other.gamma
&& radius == other.radius
&& amount == other.amount
&& threshold == other.threshold
+ && autoContrast == other.autoContrast
&& edgesonly == other.edgesonly
&& edges_radius == other.edges_radius
&& edges_tolerance == other.edges_tolerance
@@ -1145,6 +1149,32 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const
return !(*this == other);
}
+CaptureSharpeningParams::CaptureSharpeningParams() :
+ enabled(false),
+ autoContrast(true),
+ contrast(10.0),
+ gamma(1.00),
+ deconvradius(0.75),
+ deconviter(20)
+{
+}
+
+bool CaptureSharpeningParams::operator ==(const CaptureSharpeningParams& other) const
+{
+ return
+ enabled == other.enabled
+ && contrast == other.contrast
+ && gamma == other.gamma
+ && autoContrast == other.autoContrast
+ && deconvradius == other.deconvradius
+ && deconviter == other.deconviter;
+}
+
+bool CaptureSharpeningParams::operator !=(const CaptureSharpeningParams& other) const
+{
+ return !(*this == other);
+}
+
SharpenEdgeParams::SharpenEdgeParams() :
enabled(false),
passes(2),
@@ -2825,6 +2855,8 @@ void ProcParams::setDefaults()
prsharpening.deconviter = 100;
prsharpening.deconvdamping = 0;
+ pdsharpening = {};
+
vibrance = {};
wb = {};
@@ -3334,6 +3366,14 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->resize.height, "Resize", "Height", resize.height, keyFile);
saveToKeyfile(!pedited || pedited->resize.allowUpscaling, "Resize", "AllowUpscaling", resize.allowUpscaling, keyFile);
+// Post demosaic sharpening
+ saveToKeyfile(!pedited || pedited->pdsharpening.enabled, "PostDemosaicSharpening", "Enabled", pdsharpening.enabled, keyFile);
+ saveToKeyfile(!pedited || pedited->pdsharpening.contrast, "PostDemosaicSharpening", "Contrast", pdsharpening.contrast, keyFile);
+ saveToKeyfile(!pedited || pedited->pdsharpening.autoContrast, "PostDemosaicSharpening", "AutoContrast", pdsharpening.autoContrast, keyFile);
+ saveToKeyfile(!pedited || pedited->pdsharpening.gamma, "PostDemosaicSharpening", "DeconvGamma", pdsharpening.gamma, keyFile);
+ saveToKeyfile(!pedited || pedited->pdsharpening.deconvradius, "PostDemosaicSharpening", "DeconvRadius", pdsharpening.deconvradius, keyFile);
+ saveToKeyfile(!pedited || pedited->pdsharpening.deconviter, "PostDemosaicSharpening", "DeconvIterations", pdsharpening.deconviter, keyFile);
+
// Post resize sharpening
saveToKeyfile(!pedited || pedited->prsharpening.enabled, "PostResizeSharpening", "Enabled", prsharpening.enabled, keyFile);
saveToKeyfile(!pedited || pedited->prsharpening.contrast, "PostResizeSharpening", "Contrast", prsharpening.contrast, keyFile);
@@ -4414,6 +4454,16 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
}
}
+ if (keyFile.has_group("PostDemosaicSharpening")) {
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled);
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast);
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "AutoContrast", pedited, pdsharpening.autoContrast, pedited->pdsharpening.autoContrast);
+
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvGamma", pedited, pdsharpening.gamma, pedited->pdsharpening.gamma);
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvRadius", pedited, pdsharpening.deconvradius, pedited->pdsharpening.deconvradius);
+ assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvIterations", pedited, pdsharpening.deconviter, pedited->pdsharpening.deconviter);
+ }
+
if (keyFile.has_group("PostResizeSharpening")) {
assignFromKeyfile(keyFile, "PostResizeSharpening", "Enabled", pedited, prsharpening.enabled, pedited->prsharpening.enabled);
assignFromKeyfile(keyFile, "PostResizeSharpening", "Contrast", pedited, prsharpening.contrast, pedited->prsharpening.contrast);
diff --git a/rtengine/procparams.h b/rtengine/procparams.h
index 253ed26eb..e8f846a1d 100644
--- a/rtengine/procparams.h
+++ b/rtengine/procparams.h
@@ -494,7 +494,9 @@ struct ColorToningParams {
struct SharpeningParams {
bool enabled;
double contrast;
+ bool autoContrast;
double blurradius;
+ double gamma;
double radius;
int amount;
Threshold threshold;
@@ -540,6 +542,20 @@ struct SharpenMicroParams {
bool operator !=(const SharpenMicroParams& other) const;
};
+struct CaptureSharpeningParams {
+ bool enabled;
+ bool autoContrast;
+ double contrast;
+ double gamma;
+ double deconvradius;
+ int deconviter;
+
+ CaptureSharpeningParams();
+
+ bool operator ==(const CaptureSharpeningParams& other) const;
+ bool operator !=(const CaptureSharpeningParams& other) const;
+};
+
/**
* Parameters of the vibrance
*/
@@ -1528,6 +1544,7 @@ public:
ColorToningParams colorToning; ///< Color Toning parameters
SharpeningParams sharpening; ///< Sharpening parameters
SharpeningParams prsharpening; ///< Sharpening parameters for post resize sharpening
+ CaptureSharpeningParams pdsharpening; ///< Sharpening parameters for post demosaic sharpening
SharpenEdgeParams sharpenEdge; ///< Sharpen edge parameters
SharpenMicroParams sharpenMicro; ///< Sharpen microcontrast parameters
VibranceParams vibrance; ///< Vibrance parameters
diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc
index d0fb87313..5f1d13c79 100644
--- a/rtengine/rawimagesource.cc
+++ b/rtengine/rawimagesource.cc
@@ -37,6 +37,9 @@
#include "pdaflinesfilter.h"
#include "camconst.h"
#include "procparams.h"
+#include "color.h"
+//#define BENCHMARK
+//#include "StopWatch.h"
#ifdef _OPENMP
#include
#endif
@@ -453,6 +456,9 @@ RawImageSource::RawImageSource ()
, green(0, 0)
, red(0, 0)
, blue(0, 0)
+ , greenCache(nullptr)
+ , redCache(nullptr)
+ , blueCache(nullptr)
, rawDirty(true)
, histMatchingParams(new procparams::ColorManagementParams)
{
@@ -470,6 +476,9 @@ RawImageSource::~RawImageSource ()
{
delete idata;
+ delete redCache;
+ delete greenCache;
+ delete blueCache;
for(size_t i = 0; i < numFrames; ++i) {
delete riFrames[i];
@@ -1552,7 +1561,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold)
+void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache)
{
MyTime t1, t2;
t1.set();
@@ -1623,7 +1632,49 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c
rgbSourceModified = false;
-
+ if (cache) {
+ if (!redCache) {
+ redCache = new array2D(W, H);
+ greenCache = new array2D(W, H);
+ blueCache = new array2D(W, H);
+ }
+#ifdef _OPENMP
+ #pragma omp parallel sections
+#endif
+ {
+#ifdef _OPENMP
+ #pragma omp section
+#endif
+ for (int i = 0; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ (*redCache)[i][j] = red[i][j];
+ }
+ }
+#ifdef _OPENMP
+ #pragma omp section
+#endif
+ for (int i = 0; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ (*greenCache)[i][j] = green[i][j];
+ }
+ }
+#ifdef _OPENMP
+ #pragma omp section
+#endif
+ for (int i = 0; i < H; ++i) {
+ for (int j = 0; j < W; ++j) {
+ (*blueCache)[i][j] = blue[i][j];
+ }
+ }
+ }
+ } else {
+ delete redCache;
+ redCache = nullptr;
+ delete greenCache;
+ greenCache = nullptr;
+ delete blueCache;
+ blueCache = nullptr;
+ }
if( settings->verbose ) {
if (getSensorType() == ST_BAYER) {
printf("Demosaicing Bayer data: %s - %d usec\n", raw.bayersensor.method.c_str(), t2.etime(t1));
diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h
index decdcef12..e20024f3c 100644
--- a/rtengine/rawimagesource.h
+++ b/rtengine/rawimagesource.h
@@ -88,6 +88,12 @@ protected:
array2D red;
// the interpolated blue plane:
array2D blue;
+ // the interpolated green plane:
+ array2D* greenCache;
+ // the interpolated red plane:
+ array2D* redCache;
+ // the interpolated blue plane:
+ array2D* blueCache;
bool rawDirty;
float psRedBrightness[4];
float psGreenBrightness[4];
@@ -117,7 +123,7 @@ public:
void preprocess (const procparams::RAWParams &raw, const procparams::LensProfParams &lensProf, const procparams::CoarseTransformParams& coarse, bool prepareDenoise = true) override;
void filmNegativeProcess (const procparams::FilmNegativeParams ¶ms) override;
bool getFilmNegativeExponents (Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams ¤tParams, std::array& newExps) override;
- void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold) override;
+ void demosaic (const procparams::RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache = false) override;
void retinex (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &deh, const procparams::ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) override;
void retinexPrepareCurves (const procparams::RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI) override;
void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI) override;
@@ -305,7 +311,7 @@ protected:
void hflip (Imagefloat* im);
void vflip (Imagefloat* im);
void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override;
-
+ void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override;
};
}
diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc
index 94c3e71f8..db56500b8 100644
--- a/rtengine/refreshmap.cc
+++ b/rtengine/refreshmap.cc
@@ -519,7 +519,10 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
HDR, // EvTMFattalAmount
ALLNORAW, // EvWBEnabled
RGBCURVE, // EvRGBEnabled
- LUMINANCECURVE // EvLEnabled
+ LUMINANCECURVE, // EvLEnabled
+ DEMOSAIC, // EvPdShrEnabled
+ ALLNORAW // EvPdShrMaskToggled
+
};
diff --git a/rtengine/refreshmap.h b/rtengine/refreshmap.h
index 2890d8f19..3f4831329 100644
--- a/rtengine/refreshmap.h
+++ b/rtengine/refreshmap.h
@@ -32,6 +32,7 @@
// Elementary functions that can be done to
// the preview image when an event occurs
+#define M_CSHARP (1<<18)
#define M_MONITOR (1<<14)
#define M_RETINEX (1<<13)
#define M_CROP (1<<12)
@@ -56,6 +57,7 @@
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define ALLNORAW (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
+#define CAPTURESHARPEN (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP)
#define HDR (M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define AUTOEXP (M_HDR|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc
index 5c5da6bca..9fdb59652 100644
--- a/rtengine/rt_algo.cc
+++ b/rtengine/rt_algo.cc
@@ -52,7 +52,7 @@ vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) {
}
#endif
-float tileAverage(float **data, size_t tileY, size_t tileX, size_t tilesize) {
+float tileAverage(const float * const *data, size_t tileY, size_t tileX, size_t tilesize) {
float avg = 0.f;
#ifdef __SSE2__
@@ -75,7 +75,7 @@ float tileAverage(float **data, size_t tileY, size_t tileX, size_t tilesize) {
return avg / rtengine::SQR(tilesize);
}
-float tileVariance(float **data, size_t tileY, size_t tileX, size_t tilesize, float avg) {
+float tileVariance(const float * const *data, size_t tileY, size_t tileX, size_t tilesize, float avg) {
float var = 0.f;
#ifdef __SSE2__
@@ -99,7 +99,7 @@ float tileVariance(float **data, size_t tileY, size_t tileX, size_t tilesize, fl
return var / (rtengine::SQR(tilesize) * avg);
}
-float calcContrastThreshold(float** luminance, int tileY, int tileX, int tilesize) {
+float calcContrastThreshold(const float* const * luminance, int tileY, int tileX, int tilesize) {
constexpr float scale = 0.0625f / 327.68f;
std::vector> blend(tilesize - 4, std::vector(tilesize - 4));
@@ -299,7 +299,7 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float&
maxOut = rtengine::LIM(maxOut, minVal, maxVal);
}
-void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount, bool autoContrast) {
+void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, float amount, bool autoContrast) {
if (autoContrast) {
constexpr float minLuminance = 2000.f;
diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h
index bf442b9be..0ea000a26 100644
--- a/rtengine/rt_algo.h
+++ b/rtengine/rt_algo.h
@@ -24,5 +24,5 @@
namespace rtengine
{
void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multiThread = true);
-void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false);
+void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false);
}
diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h
index a0be5a7f6..33cca1aba 100644
--- a/rtengine/rtengine.h
+++ b/rtengine/rtengine.h
@@ -523,6 +523,7 @@ public:
virtual void setFrameCountListener (FrameCountListener* l) = 0;
virtual void setBayerAutoContrastListener (AutoContrastListener* l) = 0;
virtual void setXtransAutoContrastListener (AutoContrastListener* l) = 0;
+ virtual void setpdSharpenAutoContrastListener (AutoContrastListener* l) = 0;
virtual void setAutoBWListener (AutoBWListener* l) = 0;
virtual void setAutoWBListener (AutoWBListener* l) = 0;
virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0;
@@ -535,7 +536,7 @@ public:
virtual void getMonitorProfile (Glib::ustring& monitorProfile, RenderingIntent& intent) const = 0;
virtual void setSoftProofing (bool softProof, bool gamutCheck) = 0;
virtual void getSoftProofing (bool &softProof, bool &gamutCheck) = 0;
- virtual void setSharpMask (bool sharpMask) = 0;
+ virtual ProcEvent setSharpMask (bool sharpMask) = 0;
virtual ~StagedImageProcessor () {}
diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc
index d80abe045..2022d2d15 100644
--- a/rtengine/simpleprocess.cc
+++ b/rtengine/simpleprocess.cc
@@ -220,7 +220,10 @@ private:
bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicAutoContrast : params.raw.xtranssensor.dualDemosaicAutoContrast;
double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicContrast : params.raw.xtranssensor.dualDemosaicContrast;
- imgsrc->demosaic (params.raw, autoContrast, contrastThreshold);
+ imgsrc->demosaic (params.raw, autoContrast, contrastThreshold, params.pdsharpening.enabled && pl);
+ if (params.pdsharpening.enabled) {
+ imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast);
+ }
if (pl) {
diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h
index 8ad8cb8ea..ef1294dea 100644
--- a/rtengine/stdimagesource.h
+++ b/rtengine/stdimagesource.h
@@ -102,7 +102,7 @@ public:
void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override { R = G = B = 0;}
void flushRGB () override;
-
+ void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) override {};
};
}
#endif
diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt
index bdd166938..bc52b64b0 100644
--- a/rtgui/CMakeLists.txt
+++ b/rtgui/CMakeLists.txt
@@ -106,6 +106,7 @@ set(NONCLISOURCEFILES
partialpastedlg.cc
pathutils.cc
pcvignette.cc
+ pdsharpening.cc
perspective.cc
placesbrowser.cc
popupbutton.cc
diff --git a/rtgui/addsetids.h b/rtgui/addsetids.h
index 163bc27ef..6a4ea83d3 100644
--- a/rtgui/addsetids.h
+++ b/rtgui/addsetids.h
@@ -142,6 +142,7 @@ enum {
ADDSET_XTRANS_FALSE_COLOR_SUPPRESSION,
ADDSET_SOFTLIGHT_STRENGTH,
ADDSET_DEHAZE_STRENGTH,
+ ADDSET_SHARP_GAMMA,
ADDSET_PARAM_NUM // THIS IS USED AS A DELIMITER!!
};
diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc
index 592b59a4f..87320892e 100644
--- a/rtgui/batchtoolpanelcoord.cc
+++ b/rtgui/batchtoolpanelcoord.cc
@@ -152,6 +152,7 @@ void BatchToolPanelCoordinator::initSession ()
cacorrection->setAdjusterBehavior (false);
sharpening->setAdjusterBehavior (false, false, false, false, false, false, false);
prsharpening->setAdjusterBehavior (false, false, false, false, false, false, false);
+ pdSharpening->setAdjusterBehavior (false, false, false, false);
sharpenEdge->setAdjusterBehavior (false, false);
sharpenMicro->setAdjusterBehavior (false, false, false);
epd->setAdjusterBehavior (false, false, false, false, false);
diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc
index e0e3ea47b..84860329f 100644
--- a/rtgui/paramsedited.cc
+++ b/rtgui/paramsedited.cc
@@ -152,6 +152,7 @@ void ParamsEdited::set(bool v)
sharpening.contrast = v;
sharpening.radius = v;
sharpening.blurradius = v;
+ sharpening.gamma = v;
sharpening.amount = v;
sharpening.threshold = v;
sharpening.edgesonly = v;
@@ -164,6 +165,12 @@ void ParamsEdited::set(bool v)
sharpening.deconvradius = v;
sharpening.deconviter = v;
sharpening.deconvdamping = v;
+ pdsharpening.enabled = v;
+ pdsharpening.contrast = v;
+ pdsharpening.autoContrast = v;
+ pdsharpening.gamma = v;
+ pdsharpening.deconvradius = v;
+ pdsharpening.deconviter = v;
prsharpening.enabled = v;
prsharpening.contrast = v;
prsharpening.radius = v;
@@ -729,6 +736,7 @@ void ParamsEdited::initFrom(const std::vector&
sharpening.contrast = sharpening.contrast && p.sharpening.contrast == other.sharpening.contrast;
sharpening.radius = sharpening.radius && p.sharpening.radius == other.sharpening.radius;
sharpening.blurradius = sharpening.blurradius && p.sharpening.blurradius == other.sharpening.blurradius;
+ sharpening.gamma = sharpening.gamma && p.sharpening.gamma == other.sharpening.gamma;
sharpening.amount = sharpening.amount && p.sharpening.amount == other.sharpening.amount;
sharpening.threshold = sharpening.threshold && p.sharpening.threshold == other.sharpening.threshold;
sharpening.edgesonly = sharpening.edgesonly && p.sharpening.edgesonly == other.sharpening.edgesonly;
@@ -741,6 +749,12 @@ void ParamsEdited::initFrom(const std::vector&
sharpening.deconvradius = sharpening.deconvradius && p.sharpening.deconvradius == other.sharpening.deconvradius;
sharpening.deconviter = sharpening.deconviter && p.sharpening.deconviter == other.sharpening.deconviter;
sharpening.deconvdamping = sharpening.deconvdamping && p.sharpening.deconvdamping == other.sharpening.deconvdamping;
+ pdsharpening.enabled = pdsharpening.enabled && p.pdsharpening.enabled == other.pdsharpening.enabled;
+ pdsharpening.contrast = pdsharpening.contrast && p.pdsharpening.contrast == other.pdsharpening.contrast;
+ pdsharpening.autoContrast = pdsharpening.autoContrast && p.pdsharpening.autoContrast == other.pdsharpening.autoContrast;
+ pdsharpening.gamma = pdsharpening.gamma && p.pdsharpening.gamma == other.pdsharpening.gamma;
+ pdsharpening.deconvradius = pdsharpening.deconvradius && p.pdsharpening.deconvradius == other.pdsharpening.deconvradius;
+ pdsharpening.deconviter = pdsharpening.deconviter && p.pdsharpening.deconviter == other.pdsharpening.deconviter;
prsharpening.enabled = prsharpening.enabled && p.prsharpening.enabled == other.prsharpening.enabled;
prsharpening.contrast = prsharpening.contrast && p.prsharpening.contrast == other.prsharpening.contrast;
prsharpening.radius = prsharpening.radius && p.prsharpening.radius == other.prsharpening.radius;
@@ -1654,6 +1668,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.sharpening.blurradius = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.sharpening.blurradius + mods.sharpening.blurradius : mods.sharpening.blurradius;
}
+ if (sharpening.gamma) {
+ toEdit.sharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.sharpening.gamma + mods.sharpening.gamma : mods.sharpening.gamma;
+ }
+
if (sharpening.amount) {
toEdit.sharpening.amount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.sharpening.amount + mods.sharpening.amount : mods.sharpening.amount;
}
@@ -1702,6 +1720,30 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.sharpening.deconvdamping = dontforceSet && options.baBehav[ADDSET_SHARP_DAMPING] ? toEdit.sharpening.deconvdamping + mods.sharpening.deconvdamping : mods.sharpening.deconvdamping;
}
+ if (pdsharpening.enabled) {
+ toEdit.pdsharpening.enabled = mods.pdsharpening.enabled;
+ }
+
+ if (pdsharpening.contrast) {
+ toEdit.pdsharpening.contrast = dontforceSet && options.baBehav[ADDSET_SHARP_CONTRAST] ? toEdit.pdsharpening.contrast + mods.pdsharpening.contrast : mods.pdsharpening.contrast;
+ }
+
+ if (pdsharpening.autoContrast) {
+ toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast;
+ }
+
+ if (pdsharpening.gamma) {
+ toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma;
+ }
+
+ if (pdsharpening.deconvradius) {
+ toEdit.pdsharpening.deconvradius = dontforceSet && options.baBehav[ADDSET_SHARP_RADIUS] ? toEdit.pdsharpening.deconvradius + mods.pdsharpening.deconvradius : mods.pdsharpening.deconvradius;
+ }
+
+ if (pdsharpening.deconviter) {
+ toEdit.pdsharpening.deconviter = dontforceSet && options.baBehav[ADDSET_SHARP_ITER] ? toEdit.pdsharpening.deconviter + mods.pdsharpening.deconviter : mods.pdsharpening.deconviter;
+ }
+
if (prsharpening.enabled) {
toEdit.prsharpening.enabled = mods.prsharpening.enabled;
}
@@ -3244,3 +3286,8 @@ bool FilmNegativeParamsEdited::isUnchanged() const
{
return enabled && redRatio && greenExp && blueRatio;
}
+
+bool CaptureSharpeningParamsEdited::isUnchanged() const
+{
+ return enabled && contrast && autoContrast && gamma && deconvradius && deconviter;
+}
\ No newline at end of file
diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h
index 0b765db22..860ffc551 100644
--- a/rtgui/paramsedited.h
+++ b/rtgui/paramsedited.h
@@ -179,7 +179,9 @@ struct SharpenMicroParamsEdited {
struct SharpeningParamsEdited {
bool enabled;
bool contrast;
+ bool autoContrast;
bool blurradius;
+ bool gamma;
bool radius;
bool amount;
bool threshold;
@@ -196,6 +198,16 @@ struct SharpeningParamsEdited {
bool deconvdamping;
};
+struct CaptureSharpeningParamsEdited {
+ bool enabled;
+ bool contrast;
+ bool autoContrast;
+ bool gamma;
+ bool deconvradius;
+ bool deconviter;
+ bool isUnchanged() const;
+};
+
struct VibranceParamsEdited {
bool enabled;
bool pastels;
@@ -684,6 +696,7 @@ struct ParamsEdited {
ColorToningEdited colorToning;
RetinexParamsEdited retinex;
SharpeningParamsEdited sharpening;
+ CaptureSharpeningParamsEdited pdsharpening;
SharpeningParamsEdited prsharpening;
SharpenEdgeParamsEdited sharpenEdge;
SharpenMicroParamsEdited sharpenMicro;
diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc
new file mode 100644
index 000000000..c85b42e95
--- /dev/null
+++ b/rtgui/pdsharpening.cc
@@ -0,0 +1,249 @@
+/*
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2019 Ingo Weyrich (heckflosse67@gmx.de)
+ *
+ * RawTherapee is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * RawTherapee is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with RawTherapee. If not, see .
+*/
+
+#include
+#include "eventmapper.h"
+#include "pdsharpening.h"
+#include "options.h"
+#include "../rtengine/procparams.h"
+
+using namespace rtengine;
+using namespace rtengine::procparams;
+
+PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDSHARPENING_LABEL"), false, true)
+{
+
+ auto m = ProcEventMapper::getInstance();
+ EvPdShrContrast = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_CONTRAST");
+ EvPdSharpenGamma = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_GAMMA");
+ EvPdShrDRadius = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_RADIUS");
+ EvPdShrDIterations = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_ITERATIONS");
+ EvPdShrAutoContrast = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST");
+
+ Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
+ hb->show();
+ contrast = Gtk::manage(new Adjuster(M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 10));
+ contrast->setAdjusterListener(this);
+ contrast->addAutoButton(M("TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP"));
+ contrast->setAutoValue(true);
+
+ pack_start(*contrast);
+ contrast->show();
+
+ pack_start(*hb);
+
+ Gtk::VBox* rld = Gtk::manage(new Gtk::VBox());
+ gamma = Gtk::manage(new Adjuster(M("TP_SHARPENING_GAMMA"), 0.5, 6.0, 0.05, 1.00));
+ dradius = Gtk::manage(new Adjuster(M("TP_SHARPENING_EDRADIUS"), 0.4, 1.15, 0.01, 0.75));
+ diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 1, 100, 1, 20));
+ rld->pack_start(*gamma);
+ rld->pack_start(*dradius);
+ rld->pack_start(*diter);
+ gamma->show();
+ dradius->show();
+ diter->show();
+ rld->show();
+ pack_start(*rld);
+
+ dradius->setAdjusterListener(this);
+ gamma->setAdjusterListener(this);
+ diter->setAdjusterListener(this);
+
+ contrast->delay = std::max(contrast->delay, options.adjusterMaxDelay);
+ dradius->delay = std::max(dradius->delay, options.adjusterMaxDelay);
+ gamma->delay = std::max(gamma->delay, options.adjusterMaxDelay);
+ diter->delay = std::max(diter->delay, options.adjusterMaxDelay);
+}
+
+PdSharpening::~PdSharpening()
+{
+ idle_register.destroy();
+}
+
+
+void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited)
+{
+
+ disableListener();
+
+ if (pedited) {
+ contrast->setEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited);
+ contrast->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoContrast);
+ gamma->setEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited);
+ dradius->setEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited);
+ diter->setEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited);
+
+ set_inconsistent(multiImage && !pedited->pdsharpening.enabled);
+ }
+
+ setEnabled(pp->pdsharpening.enabled);
+
+ contrast->setValue(pp->pdsharpening.contrast);
+ contrast->setAutoValue(pp->pdsharpening.autoContrast);
+ gamma->setValue(pp->pdsharpening.gamma);
+ dradius->setValue(pp->pdsharpening.deconvradius);
+ diter->setValue(pp->pdsharpening.deconviter);
+ lastAutoContrast = pp->pdsharpening.autoContrast;
+
+ enableListener();
+}
+
+void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited)
+{
+
+ pp->pdsharpening.contrast = contrast->getValue();
+ pp->pdsharpening.autoContrast = contrast->getAutoValue();
+ pp->pdsharpening.enabled = getEnabled();
+ pp->pdsharpening.gamma = gamma->getValue();
+ pp->pdsharpening.deconvradius = dradius->getValue();
+ pp->pdsharpening.deconviter =(int)diter->getValue();
+
+ if (pedited) {
+ pedited->pdsharpening.contrast = contrast->getEditedState();
+ pedited->pdsharpening.autoContrast = !contrast->getAutoInconsistent();
+ pedited->pdsharpening.gamma = gamma->getEditedState();
+ pedited->pdsharpening.deconvradius = dradius->getEditedState();
+ pedited->pdsharpening.deconviter = diter->getEditedState();
+ pedited->pdsharpening.enabled = !get_inconsistent();
+ }
+}
+
+void PdSharpening::setDefaults(const ProcParams* defParams, const ParamsEdited* pedited)
+{
+
+ contrast->setDefault(defParams->pdsharpening.contrast);
+ gamma->setDefault(defParams->pdsharpening.gamma);
+ dradius->setDefault(defParams->pdsharpening.deconvradius);
+ diter->setDefault(defParams->pdsharpening.deconviter);
+
+ if (pedited) {
+ contrast->setDefaultEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited);
+ gamma->setDefaultEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited);
+ dradius->setDefaultEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited);
+ diter->setDefaultEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited);
+ } else {
+ contrast->setDefaultEditedState(Irrelevant);
+ gamma->setDefaultEditedState(Irrelevant);
+ dradius->setDefaultEditedState(Irrelevant);
+ diter->setDefaultEditedState(Irrelevant);
+ }
+}
+
+void PdSharpening::adjusterChanged(Adjuster* a, double newval)
+{
+ if (listener && (multiImage || getEnabled())) {
+
+ Glib::ustring costr;
+
+ if (a == gamma || a == dradius) {
+ costr = Glib::ustring::format(std::setw(3), std::fixed, std::setprecision(2), a->getValue());
+ } else {
+ costr = Glib::ustring::format((int)a->getValue());
+ }
+
+ if (a == contrast) {
+ listener->panelChanged(EvPdShrContrast, costr);
+ } else if (a == gamma) {
+ listener->panelChanged(EvPdSharpenGamma, costr);
+ } else if (a == dradius) {
+ listener->panelChanged(EvPdShrDRadius, costr);
+ } else if (a == diter) {
+ listener->panelChanged(EvPdShrDIterations, costr);
+ }
+ }
+}
+
+void PdSharpening::enabledChanged()
+{
+ if (listener) {
+ if (get_inconsistent()) {
+ listener->panelChanged(EvPdShrEnabled, M("GENERAL_UNCHANGED"));
+ } else if (getEnabled()) {
+ listener->panelChanged(EvPdShrEnabled, M("GENERAL_ENABLED"));
+ } else {
+ listener->panelChanged(EvPdShrEnabled, M("GENERAL_DISABLED"));
+ }
+ }
+}
+
+void PdSharpening::setBatchMode(bool batchMode)
+{
+
+ ToolPanel::setBatchMode(batchMode);
+
+ contrast->showEditedCB();
+ gamma->showEditedCB();
+ dradius->showEditedCB();
+ diter->showEditedCB();
+}
+
+void PdSharpening::setAdjusterBehavior(bool contrastadd, bool gammaadd, bool radiusadd, bool iteradd)
+{
+
+ contrast->setAddMode(contrastadd);
+ gamma->setAddMode(gammaadd);
+ dradius->setAddMode(radiusadd);
+ diter->setAddMode(iteradd);
+}
+
+void PdSharpening::trimValues(rtengine::procparams::ProcParams* pp)
+{
+
+ contrast->trimValue(pp->pdsharpening.contrast);
+ gamma->trimValue(pp->pdsharpening.gamma);
+ dradius->trimValue(pp->pdsharpening.deconvradius);
+ diter->trimValue(pp->pdsharpening.deconviter);
+}
+
+void PdSharpening::autoContrastChanged(double autoContrast)
+{
+ idle_register.add(
+ [this, autoContrast]() -> bool
+ {
+ disableListener();
+ contrast->setValue(autoContrast);
+ enableListener();
+ return false;
+ }
+ );
+}
+
+void PdSharpening::adjusterAutoToggled(Adjuster* a, bool newval)
+{
+ if (multiImage) {
+ if (contrast->getAutoInconsistent()) {
+ contrast->setAutoInconsistent(false);
+ contrast->setAutoValue(false);
+ } else if (lastAutoContrast) {
+ contrast->setAutoInconsistent(true);
+ }
+
+ lastAutoContrast = contrast->getAutoValue();
+ }
+
+ if (listener) {
+ if (contrast->getAutoInconsistent()) {
+ listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_UNCHANGED"));
+ } else if (contrast->getAutoValue()) {
+ listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_ENABLED"));
+ } else {
+ listener->panelChanged(EvPdShrAutoContrast, M("GENERAL_DISABLED"));
+ }
+ }
+}
diff --git a/rtgui/pdsharpening.h b/rtgui/pdsharpening.h
new file mode 100644
index 000000000..b2f7b6e57
--- /dev/null
+++ b/rtgui/pdsharpening.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2019 Ingo Weyrich (heckflosse67@gmx.de)
+ *
+ * RawTherapee is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * RawTherapee is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with RawTherapee. If not, see .
+*/
+#pragma once
+
+#include "adjuster.h"
+#include "toolpanel.h"
+
+class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener
+{
+
+protected:
+ Adjuster* contrast;
+ Adjuster* gamma;
+ Adjuster* dradius;
+ Adjuster* diter;
+
+ bool lastAutoContrast;
+ rtengine::ProcEvent EvPdShrContrast;
+ rtengine::ProcEvent EvPdShrDRadius;
+ rtengine::ProcEvent EvPdSharpenGamma;
+ rtengine::ProcEvent EvPdShrDIterations;
+ rtengine::ProcEvent EvPdShrAutoContrast;
+ IdleRegister idle_register;
+
+public:
+
+ PdSharpening ();
+ ~PdSharpening () override;
+
+ void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override;
+ void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override;
+ void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override;
+ void setBatchMode (bool batchMode) override;
+
+ void adjusterAutoToggled (Adjuster* a, bool newval) override;
+ void adjusterChanged (Adjuster* a, double newval) override;
+ void enabledChanged () override;
+
+ void autoContrastChanged (double autoContrast) override;
+
+ void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool iteradds);
+ void trimValues (rtengine::procparams::ProcParams* pp) override;
+};
diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc
index e920e0e97..a664942a1 100644
--- a/rtgui/toolpanelcoord.cc
+++ b/rtgui/toolpanelcoord.cc
@@ -93,7 +93,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
xtransrawexposure = Gtk::manage (new XTransRAWExposure ());
fattal = Gtk::manage (new FattalToneMapping ());
filmNegative = Gtk::manage (new FilmNegative ());
-
+ pdSharpening = Gtk::manage (new PdSharpening());
// So Demosaic, Line noise filter, Green Equilibration, Ca-Correction (garder le nom de section identique!) and Black-Level will be moved in a "Bayer sensor" tool,
// and a separate Demosaic and Black Level tool will be created in an "X-Trans sensor" tool
@@ -156,6 +156,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
addfavoritePanel (rawPanel, darkframe);
addfavoritePanel (rawPanel, flatfield);
addfavoritePanel (rawPanel, filmNegative);
+ addfavoritePanel (rawPanel, pdSharpening);
int favoriteCount = 0;
for(auto it = favorites.begin(); it != favorites.end(); ++it) {
@@ -309,6 +310,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
filmNegative->FoldableToolPanel::show();
+ pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@@ -325,6 +327,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
filmNegative->FoldableToolPanel::show();
+ pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@@ -341,6 +344,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::show();
filmNegative->FoldableToolPanel::hide();
+ pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@@ -356,6 +360,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::hide();
filmNegative->FoldableToolPanel::hide();
+ pdSharpening->FoldableToolPanel::hide();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@@ -368,6 +373,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt
{
rawPanelSW->set_sensitive(false);
filmNegative->FoldableToolPanel::hide();
+ pdSharpening->FoldableToolPanel::hide();
retinex->FoldableToolPanel::setGrayedOut(true);
return false;
@@ -484,7 +490,7 @@ void ToolPanelCoordinator::profileChange(
lParams[1] = *mergedParams;
pe.initFrom (lParams);
- filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.filmNegative.isUnchanged();
+ filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.filmNegative.isUnchanged() && pe.pdsharpening.isUnchanged();
}
*params = *mergedParams;
@@ -566,6 +572,7 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool
ipc->setFlatFieldAutoClipListener (flatfield);
ipc->setBayerAutoContrastListener (bayerprocess);
ipc->setXtransAutoContrastListener (xtransprocess);
+ ipc->setpdSharpenAutoContrastListener (pdSharpening);
ipc->setAutoWBListener (whitebalance);
ipc->setAutoColorTonListener (colortoning);
ipc->setAutoChromaListener (dirpyrdenoise);
@@ -689,8 +696,7 @@ void ToolPanelCoordinator::sharpMaskSelected(bool sharpMask)
return;
}
ipc->beginUpdateParams ();
- ipc->setSharpMask(sharpMask);
- ipc->endUpdateParams (rtengine::EvShrEnabled);
+ ipc->endUpdateParams (ipc->setSharpMask(sharpMask));
}
int ToolPanelCoordinator::getSpotWBRectSize() const
diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h
index fea55990e..390967395 100644
--- a/rtgui/toolpanelcoord.h
+++ b/rtgui/toolpanelcoord.h
@@ -77,6 +77,7 @@
#include "colortoning.h"
#include "filmsimulation.h"
#include "prsharpening.h"
+#include "pdsharpening.h"
#include "fattaltonemap.h"
#include "localcontrast.h"
#include "softlight.h"
@@ -157,7 +158,7 @@ protected:
FattalToneMapping *fattal;
MetaDataPanel* metadata;
FilmNegative* filmNegative;
-
+ PdSharpening* pdSharpening;
std::vector paramcListeners;
rtengine::StagedImageProcessor* ipc;