Capture Sharpening: automatic radius calculation

This commit is contained in:
Ingo Weyrich 2019-09-11 18:56:07 +02:00
parent ba43437220
commit 4079bb9920
20 changed files with 416 additions and 44 deletions

View File

@ -766,6 +766,7 @@ 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_AUTO_RADIUS;CAS - Auto radius
HISTORY_MSG_PDSHARPEN_GAMMA;CAS - Gamma
HISTORY_MSG_PDSHARPEN_ITERATIONS;CAS - Iterations
HISTORY_MSG_PDSHARPEN_RADIUS;CAS - Radius
@ -1800,6 +1801,7 @@ TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse,
TP_PCVIGNETTE_STRENGTH;Strength
TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners).
TP_PDSHARPENING_LABEL;Capture Sharpening
TP_PDSHARPENING_AUTORADIUS_TOOLTIP;If the checkbox is checked, RawTherapee calculates a value based on the raw data of the image.
TP_PERSPECTIVE_HORIZONTAL;Horizontal
TP_PERSPECTIVE_LABEL;Perspective
TP_PERSPECTIVE_VERTICAL;Vertical

View File

@ -37,6 +37,243 @@
#include "../rtgui/multilangmgr.h"
namespace {
void buildClipMaskBayer(const float * const *rawData, int W, int H, float** clipMask, const float whites[2][2])
{
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 0; row < H; ++row) {
for (int col = 0; col < W; ++col) {
clipMask[row][col] = 1.f;
}
}
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 2; row < H - 2; ++row) {
float clip0 = whites[row & 1][0];
float clip1 = whites[row & 1][1];
for (int col = 2; col < W - 2; ++col) {
if (rawData[row][col] >= clip0) {
clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f;
clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f;
clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f;
clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f;
clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f;
}
std::swap(clip0, clip1);
}
}
}
void buildClipMaskXtrans(const float * const *rawData, int W, int H, float** clipMask, const float whites[6][6])
{
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 0; row < H; ++row) {
for (int col = 0; col < W; ++col) {
clipMask[row][col] = 1.f;
}
}
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 2; row < H - 2; ++row) {
for (int col = 2; col < W - 2; ++col) {
const float clip = whites[row % 6][col % 6];
if (rawData[row][col] >= clip) {
clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f;
clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f;
clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f;
clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f;
clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f;
}
}
}
}
void buildClipMaskMono(const float * const *rawData, int W, int H, float** clipMask, float white)
{
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 0; row < H; ++row) {
for (int col = 0; col < W; ++col) {
clipMask[row][col] = 1.f;
}
}
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 2; row < H - 2; ++row) {
for (int col = 2; col < W - 2; ++col) {
if (rawData[row][col] >= white) {
clipMask[row - 2][col - 1] = clipMask[row - 2][col] = clipMask[row - 2][col + 1] = 0.f;
clipMask[row - 1][col - 2] = clipMask[row - 1][col - 1] = clipMask[row - 1][col] = clipMask[row - 1][col + 1] = clipMask[row - 1][col + 2] = 0.f;
clipMask[row][col - 2] = clipMask[row][col - 1] = clipMask[row][col] = clipMask[row][col + 1] = clipMask[row][col + 2] = 0.f;
clipMask[row + 1][col - 2] = clipMask[row + 1][col - 1] = clipMask[row + 1][col] = clipMask[row + 1][col + 1] = clipMask[row + 1][col + 2] = 0.f;
clipMask[row + 2][col - 1] = clipMask[row + 2][col] = clipMask[row + 2][col + 1] = 0.f;
}
}
}
}
float calcRadiusBayer(const float * const *rawData, int W, int H, float lowerLimit, float upperLimit, const unsigned int fc[2])
{
float maxRatio = 1.f;
#ifdef _OPENMP
#pragma omp parallel for reduction(max:maxRatio) schedule(dynamic, 16)
#endif
for (int row = 4; row < H - 4; ++row) {
for (int col = 5 + (fc[row & 1] & 1); col < W - 4; col += 2) {
const float val00 = rawData[row][col];
if (val00 > 0.f) {
const float val1m1 = rawData[row + 1][col - 1];
const float val1p1 = rawData[row + 1][col + 1];
const float maxVal0 = std::max(val00, val1m1);
if (val1m1 > 0.f && maxVal0 > lowerLimit) {
const float minVal = std::min(val00, val1m1);
if (UNLIKELY(maxVal0 > maxRatio * minVal)) {
bool clipped = false;
if (maxVal0 == val00) { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col - 1], rawData[row - 1][col + 1], val1p1) >= upperLimit) {
clipped = true;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row][col - 2], val00, rawData[row + 2][col - 2], rawData[row + 2][col]) >= upperLimit) {
clipped = true;
}
}
if (!clipped) {
maxRatio = maxVal0 / minVal;
}
}
}
const float maxVal1 = std::max(val00, val1p1);
if (val1p1 > 0.f && maxVal1 > lowerLimit) {
const float minVal = std::min(val00, val1p1);
if (UNLIKELY(maxVal1 > maxRatio * minVal)) {
if (maxVal1 == val00) { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col - 1], rawData[row - 1][col + 1], val1p1) >= upperLimit) {
continue;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(val00, rawData[row][col + 2], rawData[row + 2][col], rawData[row + 2][col + 2]) >= upperLimit) {
continue;
}
}
maxRatio = maxVal1 / minVal;
}
}
}
}
}
return std::sqrt((1.f / (std::log(1.f / maxRatio) / 2.f)) / -2.f);
}
float calcRadiusXtrans(const float * const *rawData, int W, int H, float lowerLimit, float upperLimit, unsigned int starty, unsigned int startx)
{
float maxRatio = 1.f;
#ifdef _OPENMP
#pragma omp parallel for reduction(max:maxRatio) schedule(dynamic, 16)
#endif
for (int row = starty + 3; row < H - 4; row += 3) {
for (int col = startx + 3; col < W - 4; col += 3) {
const float valtl = rawData[row][col];
const float valtr = rawData[row][col + 1];
const float valbl = rawData[row + 1][col];
const float valbr = rawData[row + 1][col + 1];
if (valtl > 1.f) {
const float maxValtltr = std::max(valtl, valtr);
if (valtr > 1.f && maxValtltr > lowerLimit) {
const float minVal = std::min(valtl, valtr);
if (UNLIKELY(maxValtltr > maxRatio * minVal)) {
bool clipped = false;
if (maxValtltr == valtl) { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col - 1], valtr, valbl, valbr) >= upperLimit) {
clipped = true;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col + 2], valtl, valbl, valbr) >= upperLimit) {
clipped = true;
}
}
if (!clipped) {
maxRatio = maxValtltr / minVal;
}
}
}
const float maxValtlbl = std::max(valtl, valbl);
if (valbl > 1.f && maxValtlbl > lowerLimit) {
const float minVal = std::min(valtl, valbl);
if (UNLIKELY(maxValtlbl > maxRatio * minVal)) {
bool clipped = false;
if (maxValtlbl == valtl) { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col - 1], valtr, valbl, valbr) >= upperLimit) {
clipped = true;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(valtl, valtr, rawData[row + 2][col - 1], valbr) >= upperLimit) {
clipped = true;
}
}
if (!clipped) {
maxRatio = maxValtlbl / minVal;
}
}
}
}
if (valbr > 1.f) {
const float maxValblbr = std::max(valbl, valbr);
if (valbl > 1.f && maxValblbr > lowerLimit) {
const float minVal = std::min(valbl, valbr);
if (UNLIKELY(maxValblbr > maxRatio * minVal)) {
bool clipped = false;
if (maxValblbr == valbr) { // check for influence by clipped green in neighborhood
if (rtengine::max(valtl, valtr, valbl, rawData[row + 2][col + 2]) >= upperLimit) {
clipped = true;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(valtl, valtr, rawData[row + 2][col - 1], valbr) >= upperLimit) {
clipped = true;
}
}
if (!clipped) {
maxRatio = maxValblbr / minVal;
}
}
}
const float maxValtrbr = std::max(valtr, valbr);
if (valtr > 1.f && maxValtrbr > lowerLimit) {
const float minVal = std::min(valtr, valbr);
if (UNLIKELY(maxValtrbr > maxRatio * minVal)) {
if (maxValtrbr == valbr) { // check for influence by clipped green in neighborhood
if (rtengine::max(valtl, valtr, valbl, rawData[row + 2][col + 2]) >= upperLimit) {
continue;
}
} else { // check for influence by clipped green in neighborhood
if (rtengine::max(rawData[row - 1][col + 2], valtl, valbl, valbr) >= upperLimit) {
continue;
}
}
maxRatio = maxValtrbr / minVal;
}
}
}
}
}
return std::sqrt((1.f / (std::log(1.f / maxRatio))) / -2.f);
}
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)
{
@ -54,7 +291,7 @@ void CaptureDeconvSharpening (float** luminance, float** tmp, const float * cons
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);
@ -85,9 +322,7 @@ void CaptureDeconvSharpening (float** luminance, float** tmp, const float * cons
namespace rtengine
{
void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold) {
BENCHFUN
void RawImageSource::captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) {
if (plistener) {
plistener->setProgressStr(M("TP_PDSHARPENING_LABEL"));
@ -102,12 +337,62 @@ BENCHFUN
float contrast = conrastThreshold / 100.f;
const float clipVal = (ri->get_white(1) - ri->get_cblack(1)) * scale_mul[1];
array2D<float>& redVals = redCache ? *redCache : red;
array2D<float>& greenVals = greenCache ? *greenCache : green;
array2D<float>& blueVals = blueCache ? *blueCache : blue;
array2D<float> clipMask(W, H);
constexpr float clipLimit = 0.95f;
if (ri->getSensorType() == ST_BAYER) {
const float whites[2][2] = {
{(ri->get_white(FC(0,0)) - c_black[FC(0,0)]) * scale_mul[FC(0,0)] * clipLimit, (ri->get_white(FC(0,1)) - c_black[FC(0,1)]) * scale_mul[FC(0,1)] * clipLimit},
{(ri->get_white(FC(1,0)) - c_black[FC(1,0)]) * scale_mul[FC(1,0)] * clipLimit, (ri->get_white(FC(1,1)) - c_black[FC(1,1)]) * scale_mul[FC(1,1)] * clipLimit}
};
buildClipMaskBayer(rawData, W, H, clipMask, whites);
const unsigned int fc[2] = {FC(0,0), FC(1,0)};
if (sharpeningParams.autoRadius) {
radius = calcRadiusBayer(rawData, W, H, 1000.f, clipVal, fc);
}
} else if (ri->getSensorType() == ST_FUJI_XTRANS) {
float whites[6][6];
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j) {
const auto color = ri->XTRANSFC(i, j);
whites[i][j] = (ri->get_white(color) - c_black[color]) * scale_mul[color] * clipLimit;
}
}
buildClipMaskXtrans(rawData, W, H, clipMask, whites);
bool found = false;
int i, j;
for (i = 6; i < 12 && !found; ++i) {
for (j = 6; j < 12 && !found; ++j) {
if (ri->XTRANSFC(i, j) == 1) {
if (ri->XTRANSFC(i, j - 1) != ri->XTRANSFC(i, j + 1)) {
if (ri->XTRANSFC(i - 1, j) != 1) {
if (ri->XTRANSFC(i, j - 1) != 1) {
found = true;
break;
}
}
}
}
}
}
if (sharpeningParams.autoRadius) {
radius = calcRadiusXtrans(rawData, W, H, 1000.f, clipVal, i, j);
}
} else if (ri->get_colors() == 1) {
buildClipMaskMono(rawData, W, H, clipMask, (ri->get_white(0) - c_black[0]) * scale_mul[0] * clipLimit);
if (sharpeningParams.autoRadius) {
const unsigned int fc[2] = {0, 0};
radius = calcRadiusBayer(rawData, W, H, 1000.f, clipVal, fc);
}
}
if (showMask) {
StopWatch Stop1("Show mask");
array2D<float>& L = blue; // blue will be overridden anyway => we can use its buffer to store L
#ifdef _OPENMP
#pragma omp parallel for
@ -119,8 +404,9 @@ BENCHFUN
if (plistener) {
plistener->setProgress(0.1);
}
array2D<float>& 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);
buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast, clipMask);
if (plistener) {
plistener->setProgress(0.2);
}
@ -157,7 +443,6 @@ BENCHFUN
array2D<float>& YOld = YOldbuffer ? * YOldbuffer : green;
array2D<float>& YNew = YNewbuffer ? * YNewbuffer : blue;
StopWatch Stop1("rgb2YL");
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
@ -170,19 +455,17 @@ BENCHFUN
}
// calculate contrast based blend factors to reduce sharpening in regions with low contrast
JaggedArray<float> blend(W, H);
buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast);
buildBlendMask(L, blend, W, H, contrast, 1.f, sharpeningParams.autoContrast, clipMask);
if (plistener) {
plistener->setProgress(0.2);
}
conrastThreshold = contrast * 100.f;
Stop1.stop();
array2D<float>& 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);
CaptureDeconvSharpening(YNew, tmp, blend, W, H, radius, 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)
@ -206,7 +489,7 @@ BENCHFUN
blue[i][j] = blueVals[i][j] * factor;
}
}
Stop2.stop();
delete Lbuffer;
delete YOldbuffer;
delete YNewbuffer;

View File

@ -182,7 +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;
virtual void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) = 0;
};
}

View File

@ -122,6 +122,7 @@ ImProcCoordinator::ImProcCoordinator() :
bayerAutoContrastListener(nullptr),
xtransAutoContrastListener(nullptr),
pdSharpenAutoContrastListener(nullptr),
pdSharpenAutoRadiusListener(nullptr),
frameCountListener(nullptr),
imageTypeListener(nullptr),
actListener(nullptr),
@ -346,10 +347,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
if ((todo & (M_RAW | M_CSHARP)) && params->pdsharpening.enabled) {
double pdSharpencontrastThreshold = params->pdsharpening.contrast;
imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold);
double pdSharpenRadius = params->pdsharpening.deconvradius;
imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold, pdSharpenRadius);
if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) {
pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold);
}
if (pdSharpenAutoRadiusListener && params->pdsharpening.autoRadius) {
pdSharpenAutoRadiusListener->autoRadiusChanged(pdSharpenRadius);
}
}

View File

@ -162,6 +162,7 @@ protected:
AutoContrastListener *bayerAutoContrastListener;
AutoContrastListener *xtransAutoContrastListener;
AutoContrastListener *pdSharpenAutoContrastListener;
AutoRadiusListener *pdSharpenAutoRadiusListener;
FrameCountListener *frameCountListener;
ImageTypeListener *imageTypeListener;
@ -364,6 +365,11 @@ public:
xtransAutoContrastListener = acl;
}
void setpdSharpenAutoRadiusListener (AutoRadiusListener* acl) override
{
pdSharpenAutoRadiusListener = acl;
}
void setpdSharpenAutoContrastListener (AutoContrastListener* acl) override
{
pdSharpenAutoContrastListener = acl;

View File

@ -1152,6 +1152,7 @@ bool SharpeningParams::operator !=(const SharpeningParams& other) const
CaptureSharpeningParams::CaptureSharpeningParams() :
enabled(false),
autoContrast(true),
autoRadius(true),
contrast(10.0),
gamma(1.00),
deconvradius(0.75),
@ -1166,6 +1167,7 @@ bool CaptureSharpeningParams::operator ==(const CaptureSharpeningParams& other)
&& contrast == other.contrast
&& gamma == other.gamma
&& autoContrast == other.autoContrast
&& autoRadius == other.autoRadius
&& deconvradius == other.deconvradius
&& deconviter == other.deconviter;
}
@ -3370,6 +3372,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
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.autoRadius, "PostDemosaicSharpening", "AutoRadius", pdsharpening.autoRadius, 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);
@ -4458,6 +4461,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
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", "AutoRadius", pedited, pdsharpening.autoRadius, pedited->pdsharpening.autoRadius);
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvGamma", pedited, pdsharpening.gamma, pedited->pdsharpening.gamma);
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "DeconvRadius", pedited, pdsharpening.deconvradius, pedited->pdsharpening.deconvradius);

View File

@ -545,6 +545,7 @@ struct SharpenMicroParams {
struct CaptureSharpeningParams {
bool enabled;
bool autoContrast;
bool autoRadius;
double contrast;
double gamma;
double deconvradius;

View File

@ -311,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;
void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) override;
};
}

View File

@ -521,7 +521,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
RGBCURVE, // EvRGBEnabled
LUMINANCECURVE, // EvLEnabled
DEMOSAIC, // EvPdShrEnabled
ALLNORAW // EvPdShrMaskToggled
CAPTURESHARPEN // EvPdShrMaskToggled
};

View File

@ -156,7 +156,7 @@ float calcContrastThreshold(const float* const * luminance, int tileY, int tileX
}
}
return c / 100.f;
return (c + 1) / 100.f;
}
}
@ -299,7 +299,7 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float&
maxOut = rtengine::LIM(maxOut, minVal, maxVal);
}
void buildBlendMask(const float* const * 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, float ** clipMask) {
if (autoContrast) {
constexpr float minLuminance = 2000.f;
@ -424,11 +424,20 @@ void buildBlendMask(const float* const * luminance, float **blend, int W, int H,
for(int j = 2; j < H - 2; ++j) {
int i = 2;
#ifdef __SSE2__
for(; i < W - 5; i += 4) {
vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) +
SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev;
if (clipMask) {
for(; i < W - 5; i += 4) {
vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) +
SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev;
STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv));
STVFU(blend[j][i], LVFU(clipMask[j][i]) * amountv * calcBlendFactor(contrastv, contrastThresholdv));
}
} else {
for(; i < W - 5; i += 4) {
vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) +
SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev;
STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv));
}
}
#endif
for(; i < W - 2; ++i) {
@ -436,7 +445,7 @@ void buildBlendMask(const float* const * luminance, float **blend, int W, int H,
float contrast = sqrtf(rtengine::SQR(luminance[j][i+1] - luminance[j][i-1]) + rtengine::SQR(luminance[j+1][i] - luminance[j-1][i]) +
rtengine::SQR(luminance[j][i+2] - luminance[j][i-2]) + rtengine::SQR(luminance[j+2][i] - luminance[j-2][i])) * scale;
blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold);
blend[j][i] = (clipMask ? clipMask[j][i] : 1.f) * amount * calcBlendFactor(contrast, contrastThreshold);
}
}

View File

@ -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(const float* const * 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, float ** clipmask = nullptr);
}

View File

@ -415,6 +415,13 @@ public :
virtual void autoContrastChanged (double autoContrast) = 0;
};
class AutoRadiusListener
{
public :
virtual ~AutoRadiusListener() = default;
virtual void autoRadiusChanged (double autoRadius) = 0;
};
class WaveletListener
{
public:
@ -524,6 +531,7 @@ public:
virtual void setBayerAutoContrastListener (AutoContrastListener* l) = 0;
virtual void setXtransAutoContrastListener (AutoContrastListener* l) = 0;
virtual void setpdSharpenAutoContrastListener (AutoContrastListener* l) = 0;
virtual void setpdSharpenAutoRadiusListener (AutoRadiusListener* l) = 0;
virtual void setAutoBWListener (AutoBWListener* l) = 0;
virtual void setAutoWBListener (AutoWBListener* l) = 0;
virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0;

View File

@ -222,7 +222,7 @@ private:
imgsrc->demosaic (params.raw, autoContrast, contrastThreshold, params.pdsharpening.enabled && pl);
if (params.pdsharpening.enabled) {
imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast);
imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast, params.pdsharpening.deconvradius);
}

View File

@ -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 {};
void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) override {};
};
}
#endif

View File

@ -168,6 +168,7 @@ void ParamsEdited::set(bool v)
pdsharpening.enabled = v;
pdsharpening.contrast = v;
pdsharpening.autoContrast = v;
pdsharpening.autoRadius = v;
pdsharpening.gamma = v;
pdsharpening.deconvradius = v;
pdsharpening.deconviter = v;
@ -752,6 +753,7 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
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.autoRadius = pdsharpening.autoRadius && p.pdsharpening.autoRadius == other.pdsharpening.autoRadius;
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;
@ -1732,6 +1734,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.pdsharpening.autoContrast = mods.pdsharpening.autoContrast;
}
if (pdsharpening.autoRadius) {
toEdit.pdsharpening.autoRadius = mods.pdsharpening.autoRadius;
}
if (pdsharpening.gamma) {
toEdit.pdsharpening.gamma = dontforceSet && options.baBehav[ADDSET_SHARP_GAMMA] ? toEdit.pdsharpening.gamma + mods.pdsharpening.gamma : mods.pdsharpening.gamma;
}
@ -3289,5 +3295,5 @@ bool FilmNegativeParamsEdited::isUnchanged() const
bool CaptureSharpeningParamsEdited::isUnchanged() const
{
return enabled && contrast && autoContrast && gamma && deconvradius && deconviter;
return enabled && contrast && autoContrast && autoRadius && gamma && deconvradius && deconviter;
}

View File

@ -202,6 +202,7 @@ struct CaptureSharpeningParamsEdited {
bool enabled;
bool contrast;
bool autoContrast;
bool autoRadius;
bool gamma;
bool deconvradius;
bool deconviter;

View File

@ -35,6 +35,7 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS
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");
EvPdShrAutoRadius = m->newEvent(CAPTURESHARPEN, "HISTORY_MSG_PDSHARPEN_AUTO_RADIUS");
Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
hb->show();
@ -51,6 +52,8 @@ PdSharpening::PdSharpening() : FoldableToolPanel(this, "pdsharpening", M("TP_PDS
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));
dradius->addAutoButton(M("TP_PDSHARPENING_AUTORADIUS_TOOLTIP"));
dradius->setAutoValue(true);
diter = Gtk::manage(new Adjuster(M("TP_SHARPENING_RLD_ITERATIONS"), 1, 100, 1, 20));
rld->pack_start(*gamma);
rld->pack_start(*dradius);
@ -85,6 +88,7 @@ void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited)
if (pedited) {
contrast->setEditedState(pedited->pdsharpening.contrast ? Edited : UnEdited);
contrast->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoContrast);
dradius->setAutoInconsistent(multiImage && !pedited->pdsharpening.autoRadius);
gamma->setEditedState(pedited->pdsharpening.gamma ? Edited : UnEdited);
dradius->setEditedState(pedited->pdsharpening.deconvradius ? Edited : UnEdited);
diter->setEditedState(pedited->pdsharpening.deconviter ? Edited : UnEdited);
@ -98,8 +102,10 @@ void PdSharpening::read(const ProcParams* pp, const ParamsEdited* pedited)
contrast->setAutoValue(pp->pdsharpening.autoContrast);
gamma->setValue(pp->pdsharpening.gamma);
dradius->setValue(pp->pdsharpening.deconvradius);
dradius->setAutoValue(pp->pdsharpening.autoRadius);
diter->setValue(pp->pdsharpening.deconviter);
lastAutoContrast = pp->pdsharpening.autoContrast;
lastAutoRadius = pp->pdsharpening.autoRadius;
enableListener();
}
@ -112,6 +118,7 @@ void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited)
pp->pdsharpening.enabled = getEnabled();
pp->pdsharpening.gamma = gamma->getValue();
pp->pdsharpening.deconvradius = dradius->getValue();
pp->pdsharpening.autoRadius = dradius->getAutoValue();
pp->pdsharpening.deconviter =(int)diter->getValue();
if (pedited) {
@ -119,6 +126,7 @@ void PdSharpening::write(ProcParams* pp, ParamsEdited* pedited)
pedited->pdsharpening.autoContrast = !contrast->getAutoInconsistent();
pedited->pdsharpening.gamma = gamma->getEditedState();
pedited->pdsharpening.deconvradius = dradius->getEditedState();
pedited->pdsharpening.autoRadius = !dradius->getAutoInconsistent();
pedited->pdsharpening.deconviter = diter->getEditedState();
pedited->pdsharpening.enabled = !get_inconsistent();
}
@ -224,26 +232,62 @@ void PdSharpening::autoContrastChanged(double autoContrast)
);
}
void PdSharpening::autoRadiusChanged(double autoRadius)
{
idle_register.add(
[this, autoRadius]() -> bool
{
disableListener();
dradius->setValue(autoRadius);
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);
if (a == contrast) {
if (multiImage) {
if (contrast->getAutoInconsistent()) {
contrast->setAutoInconsistent(false);
contrast->setAutoValue(false);
} else if (lastAutoContrast) {
contrast->setAutoInconsistent(true);
}
lastAutoContrast = contrast->getAutoValue();
}
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"));
}
}
} else { // must be dradius
if (multiImage) {
if (dradius->getAutoInconsistent()) {
dradius->setAutoInconsistent(false);
dradius->setAutoValue(false);
} else if (lastAutoRadius) {
dradius->setAutoInconsistent(true);
}
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"));
lastAutoRadius = dradius->getAutoValue();
}
if (listener) {
if (dradius->getAutoInconsistent()) {
listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_UNCHANGED"));
} else if (dradius->getAutoValue()) {
listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_ENABLED"));
} else {
listener->panelChanged(EvPdShrAutoRadius, M("GENERAL_DISABLED"));
}
}
}
}

View File

@ -21,7 +21,7 @@
#include "adjuster.h"
#include "toolpanel.h"
class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener
class PdSharpening final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoContrastListener, public rtengine::AutoRadiusListener
{
protected:
@ -31,11 +31,13 @@ protected:
Adjuster* diter;
bool lastAutoContrast;
bool lastAutoRadius;
rtengine::ProcEvent EvPdShrContrast;
rtengine::ProcEvent EvPdShrDRadius;
rtengine::ProcEvent EvPdSharpenGamma;
rtengine::ProcEvent EvPdShrDIterations;
rtengine::ProcEvent EvPdShrAutoContrast;
rtengine::ProcEvent EvPdShrAutoRadius;
IdleRegister idle_register;
public:
@ -53,6 +55,7 @@ public:
void enabledChanged () override;
void autoContrastChanged (double autoContrast) override;
void autoRadiusChanged (double autoRadius) override;
void setAdjusterBehavior (bool contrastadd, bool gammaadd, bool radiusadd, bool iteradds);
void trimValues (rtengine::procparams::ProcParams* pp) override;

View File

@ -573,6 +573,7 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool
ipc->setBayerAutoContrastListener (bayerprocess);
ipc->setXtransAutoContrastListener (xtransprocess);
ipc->setpdSharpenAutoContrastListener (pdSharpening);
ipc->setpdSharpenAutoRadiusListener (pdSharpening);
ipc->setAutoWBListener (whitebalance);
ipc->setAutoColorTonListener (colortoning);
ipc->setAutoChromaListener (dirpyrdenoise);

View File

@ -242,7 +242,6 @@ public:
void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false) override;
// void autoContrastChanged (double autoContrast);
// profilechangelistener interface
void profileChange(
const rtengine::procparams::PartialProfile* nparams,