Added contrast threshold adjuster for sharpening tool, #4551

This commit is contained in:
heckflosse
2018-05-20 17:37:42 +02:00
parent 528167e578
commit 1a5545c461
11 changed files with 136 additions and 123 deletions

View File

@@ -61,7 +61,6 @@ class ImProcFunctions
void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam);
void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam);
void sharpenHaloCtrlcam (CieImage* ncie, float** blurmap, float** base, int W, int H);
void dcdamping (float** aI, float** aO, float damping, int W, int H);
bool needsCA ();
bool needsDistortion ();

View File

@@ -20,6 +20,7 @@
#include "improcfun.h"
#include "gauss.h"
#include "bilateral2.h"
#include "jaggedarray.h"
#include "rt_math.h"
#include "sleef.c"
#include "opthelper.h"
@@ -45,16 +46,61 @@ vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) {
return vself(vmaskf_eq(thresholdv, ZEROV), onev, resultv);
}
#endif
}
namespace rtengine
void buildBlendMask(float** luminance, rtengine::JaggedArray<float> &blend, int W, int H, float contrastThreshold) {
// upper border
for(int j = 0; j < 2; j++)
for(int i = 0; i < W; ++i) {
blend[j][i] = 0.f;
}
#ifdef _OPENMP
#pragma omp parallel
#endif
{
#ifdef __SSE2__
vfloat contrastThresholdv = F2V(contrastThreshold);
vfloat scalev = F2V(0.0625f / 327.68f);
#endif
#ifdef _OPENMP
#pragma omp for schedule(dynamic,16) nowait
#endif
#undef ABS
for(int j = 2; j < H - 2; ++j) {
blend[j][0] = blend[j][1] = 0.f;
int i = 2;
#ifdef __SSE2__
for(; i < W - 5; i += 4) {
#define ABS(a) ((a)<0?-(a):(a))
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;
extern const Settings* settings;
void ImProcFunctions::dcdamping (float** aI, float** aO, float damping, int W, int H)
STVFU(blend[j][i], calcBlendFactor(contrastv, contrastThresholdv));
}
#endif
for(; i < W - 2; ++i) {
float contrast = sqrtf(SQR(luminance[j][i+1] - luminance[j][i-1]) + SQR(luminance[j+1][i] - luminance[j-1][i]) +
SQR(luminance[j][i+2] - luminance[j][i-2]) + SQR(luminance[j+2][i] - luminance[j-2][i])) * 0.0625f / 327.68f;
blend[j][i] = calcBlendFactor(contrast, contrastThreshold);
}
blend[j][W - 2] = blend[j][W - 1] = 0.f;
}
#ifdef _OPENMP
#pragma omp single
#endif
{
// lower border
for(int j = H - 2; j < H; ++j)
for(int i = 0; i < W; ++i) {
blend[j][i] = 0.f;
}
}
}
}
void dcdamping (float** aI, float** aO, float damping, int W, int H)
{
const float dampingFac = -2.0 / (damping * damping);
@@ -103,35 +149,35 @@ void ImProcFunctions::dcdamping (float** aI, float** aO, float damping, int W, i
}
float U = (O * xlogf(I / O) - I + O) * dampingFac;
U = min(U, 1.0f);
U = rtengine::min(U, 1.0f);
U = U * U * U * U * (5.f - U * 4.f);
aI[i][j] = (O - I) / I * U + 1.f;
}
}
}
}
namespace rtengine
{
#undef ABS
#define ABS(a) ((a)<0?-(a):(a))
extern const Settings* settings;
void ImProcFunctions::deconvsharpening (float** luminance, float** tmp, int W, int H, const SharpeningParams &sharpenParam)
{
if (sharpenParam.deconvamount < 1) {
return;
}
BENCHFUN
float *tmpI[H] ALIGNED16;
tmpI[0] = new float[W * H];
for (int i = 1; i < H; i++) {
tmpI[i] = tmpI[i - 1] + W;
}
float *tmpII[H] ALIGNED16;
tmpII[0] = new float[W * H];
for (int i = 1; i < H; i++) {
tmpII[i] = tmpII[i - 1] + W;
}
JaggedArray<float> tmpI(W, H);
JaggedArray<float> tmpII(W, H);
#ifdef _OPENMP
#pragma omp parallel 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);
@@ -140,63 +186,11 @@ BENCHFUN
}
// calculate contrast based blend factors to reduce sharpening in regions with low contrast
const float contrastThreshold = sharpenParam.deconvdamping / 100.f;
float *blend = new float[W * H]; //allocation for blend factor map
// upper border
for(int j = 0; j < 2; j++)
for(int i = 0, offset = j * W + i; i < W; i++, offset++) {
blend[offset] = 0.f;
}
#ifdef _OPENMP
#pragma omp parallel
#endif
{
#ifdef __SSE2__
vfloat contrastThresholdv = F2V(contrastThreshold);
vfloat scalev = F2V(0.0625f / 327.68f);
#endif
#ifdef _OPENMP
#pragma omp for schedule(dynamic,16) nowait
#endif
for(int j = 2; j < H - 2; j++) {
blend[j * W] = blend[j * W + 1] = 0.f;
int i = 2;
int offset = j * W + i;
#ifdef __SSE2__
for(; i < W - 5; i += 4, offset += 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[offset], calcBlendFactor(contrastv, contrastThresholdv));
}
#endif
for(; i < W - 2; i++, offset++) {
float contrast = sqrtf(SQR(luminance[j][i+1] - luminance[j][i-1]) + SQR(luminance[j+1][i] - luminance[j-1][i]) +
SQR(luminance[j][i+2] - luminance[j][i-2]) + SQR(luminance[j+2][i] - luminance[j-2][i])) * 0.0625f / 327.68f;
blend[offset] = calcBlendFactor(contrast, contrastThreshold);
}
blend[j * W + W - 2] = blend[j * W + W - 1] = 0.f;
}
#ifdef _OPENMP
#pragma omp single
#endif
{
// lower border
for(int j = H - 2; j < H; j++)
for(int i = 0, offset = j * W + i; i < W; i++, offset++) {
blend[offset] = 0.f;
}
}
}
JaggedArray<float> blend(W, H);
buildBlendMask(luminance, blend, W, H, sharpenParam.contrast / 100.f);
float damping = sharpenParam.deconvdamping / 5.0;
bool needdamp = false; //sharpenParam.deconvdamping > 0;
bool needdamp = sharpenParam.deconvdamping > 0;
double sigma = sharpenParam.deconvradius / scale;
#ifdef _OPENMP
@@ -206,22 +200,22 @@ BENCHFUN
for (int k = 0; k < sharpenParam.deconviter; k++) {
if (!needdamp) {
// apply gaussian blur and divide luminance by result of gaussian blur
gaussianBlur (tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance);
gaussianBlur(tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance);
#ifdef _OPENMP
#pragma omp for
#endif
for (int i = 0; i < H; i++) {
for(int j = 0; j < W; j++) {
for (int i = 0; i < H; ++i) {
for(int j = 0; j < W; ++j) {
tmp[i][j] = max(tmp[i][j], 0.f);
}
}
} else {
// apply gaussian blur + damping
gaussianBlur (tmpI, tmp, W, H, sigma);
dcdamping (tmp, luminance, damping, W, H);
gaussianBlur(tmpI, tmp, W, H, sigma);
dcdamping(tmp, luminance, damping, W, H);
}
gaussianBlur (tmp, tmpI, W, H, sigma, nullptr, GAUSS_MULT);
gaussianBlur(tmp, tmpI, W, H, sigma, nullptr, GAUSS_MULT);
} // end for
@@ -232,21 +226,17 @@ BENCHFUN
#pragma omp for
#endif
for (int i = 0; i < H; i++)
for (int j = 0, offset = i * W + j; j < W; j++, offset++) {
luminance[i][j] = intp(blend[offset], luminance[i][j] * p1 + max(tmpI[i][j], 0.0f) * p2, tmpII[i][j]);
for (int i = 0; i < H; ++i)
for (int j = 0; j < W; ++j) {
luminance[i][j] = intp(blend[i][j], luminance[i][j] * p1 + max(tmpI[i][j], 0.0f) * p2, tmpII[i][j]);
}
} // end parallel
delete [] blend;
delete [] tmpI[0];
delete [] tmpII[0];
}
void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &sharpenParam)
{
if (!sharpenParam.enabled) {
if ((!sharpenParam.enabled) || sharpenParam.amount < 1 || lab->W < 8 || lab->H < 8) {
return;
}
@@ -255,11 +245,6 @@ void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &s
return;
}
if ((!sharpenParam.enabled) || sharpenParam.amount < 1 || lab->W < 8 || lab->H < 8) {
return;
}
// Rest is UNSHARP MASK
int W = lab->W, H = lab->H;
float** b3 = nullptr;
@@ -272,6 +257,10 @@ void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &s
}
}
// calculate contrast based blend factors to reduce sharpening in regions with low contrast
JaggedArray<float> blend(W, H);
buildBlendMask(lab->L, blend, W, H, sharpenParam.contrast / 100.f);
#ifdef _OPENMP
#pragma omp parallel
#endif
@@ -303,18 +292,12 @@ void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &s
min(ABS(diff), upperBound), // X axis value = absolute value of the difference, truncated to the max value of this field
sharpenParam.amount * diff * 0.01f // Y axis max value
);
lab->L[i][j] = lab->L[i][j] + delta;
lab->L[i][j] = intp(blend[i][j], lab->L[i][j] + delta, lab->L[i][j]);
}
} else {
float** labCopy = nullptr;
if (!sharpenParam.edgesonly) {
// make a deep copy of lab->L
labCopy = new float*[H];
for( int i = 0; i < H; i++ ) {
labCopy[i] = new float[W];
}
JaggedArray<float> labCopy(W, H);
#ifdef _OPENMP
#pragma omp parallel for
@@ -325,18 +308,11 @@ void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &s
labCopy[i][j] = lab->L[i][j];
}
base = labCopy;
sharpenHaloCtrl (lab->L, b2, labCopy, W, H, sharpenParam);
} else {
sharpenHaloCtrl (lab->L, b2, base, W, H, sharpenParam);
}
sharpenHaloCtrl (lab->L, b2, base, W, H, sharpenParam);
if (labCopy) {
for( int i = 0; i < H; i++ ) {
delete[] labCopy[i];
}
delete[] labCopy;
}
}
if (sharpenParam.edgesonly) {

View File

@@ -1007,6 +1007,7 @@ void ColorToningParams::getCurves(ColorGradientCurve& colorCurveLUT, OpacityCurv
SharpeningParams::SharpeningParams() :
enabled(false),
contrast(0.0),
radius(0.5),
amount(200),
threshold(20, 80, 2000, 1200, false),
@@ -1027,6 +1028,7 @@ bool SharpeningParams::operator ==(const SharpeningParams& other) const
{
return
enabled == other.enabled
&& contrast == other.contrast
&& radius == other.radius
&& amount == other.amount
&& threshold == other.threshold
@@ -2853,6 +2855,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
// Sharpening
saveToKeyfile(!pedited || pedited->sharpening.enabled, "Sharpening", "Enabled", sharpening.enabled, keyFile);
saveToKeyfile(!pedited || pedited->sharpening.contrast, "Sharpening", "Contrast", sharpening.contrast, keyFile);
saveToKeyfile(!pedited || pedited->sharpening.method, "Sharpening", "Method", sharpening.method, keyFile);
saveToKeyfile(!pedited || pedited->sharpening.radius, "Sharpening", "Radius", sharpening.radius, keyFile);
saveToKeyfile(!pedited || pedited->sharpening.amount, "Sharpening", "Amount", sharpening.amount, keyFile);
@@ -3670,6 +3673,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
if (keyFile.has_group ("Sharpening")) {
assignFromKeyfile(keyFile, "Sharpening", "Enabled", pedited, sharpening.enabled, pedited->sharpening.enabled);
assignFromKeyfile(keyFile, "Sharpening", "Contrast", pedited, sharpening.contrast, pedited->sharpening.contrast);
assignFromKeyfile(keyFile, "Sharpening", "Radius", pedited, sharpening.radius, pedited->sharpening.radius);
assignFromKeyfile(keyFile, "Sharpening", "Amount", pedited, sharpening.amount, pedited->sharpening.amount);

View File

@@ -473,6 +473,7 @@ struct ColorToningParams {
*/
struct SharpeningParams {
bool enabled;
double contrast;
double radius;
int amount;
Threshold<int> threshold;