Sharpening: Blur contrast blend mask to get smoother transitions.

This commit is contained in:
heckflosse 2018-05-23 23:49:11 +02:00
parent e1643418cb
commit fcfd813714
5 changed files with 48 additions and 85 deletions

View File

@ -42,7 +42,7 @@ extern const Settings* settings;
Crop::Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow) Crop::Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow)
: PipetteBuffer (editDataProvider), origCrop (nullptr), laboCrop (nullptr), labnCrop (nullptr), : PipetteBuffer (editDataProvider), origCrop (nullptr), laboCrop (nullptr), labnCrop (nullptr),
cropImg (nullptr), cbuf_real (nullptr), transCrop (nullptr), cieCrop (nullptr), cbuffer (nullptr), cropImg (nullptr), transCrop (nullptr), cieCrop (nullptr),
updating (false), newUpdatePending (false), skip (10), updating (false), newUpdatePending (false), skip (10),
cropx (0), cropy (0), cropw (-1), croph (-1), cropx (0), cropy (0), cropw (-1), croph (-1),
trafx (0), trafy (0), trafw (-1), trafh (-1), trafx (0), trafy (0), trafw (-1), trafh (-1),
@ -865,7 +865,7 @@ void Crop::update (int todo)
if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) { if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
parent->ipf.MLmicrocontrast (labnCrop); parent->ipf.MLmicrocontrast (labnCrop);
parent->ipf.sharpening (labnCrop, (float**)cbuffer, params.sharpening); parent->ipf.sharpening (labnCrop, params.sharpening);
} }
} }
@ -1082,16 +1082,6 @@ void Crop::freeAll ()
cieCrop = nullptr; cieCrop = nullptr;
} }
if (cbuf_real) {
delete [] cbuf_real;
cbuf_real = nullptr;
}
if (cbuffer ) {
delete [] cbuffer;
cbuffer = nullptr;
}
PipetteBuffer::flush(); PipetteBuffer::flush();
} }
@ -1269,21 +1259,6 @@ bool Crop::setCropSizes (int rcx, int rcy, int rcw, int rch, int skip, bool inte
cieCrop = nullptr; cieCrop = nullptr;
} }
if (cbuffer ) {
delete [] cbuffer;
}
if (cbuf_real) {
delete [] cbuf_real;
}
cbuffer = new float*[croph];
cbuf_real = new float[ (croph + 2)*cropw];
for (int i = 0; i < croph; i++) {
cbuffer[i] = cbuf_real + cropw * i + cropw;
}
if (editType == ET_PIPETTE) { if (editType == ET_PIPETTE) {
PipetteBuffer::resize (cropw, croph); PipetteBuffer::resize (cropw, croph);
} else if (PipetteBuffer::bufferCreated()) { } else if (PipetteBuffer::bufferCreated()) {

View File

@ -44,13 +44,11 @@ protected:
LabImage* laboCrop; // "one chunk" allocation LabImage* laboCrop; // "one chunk" allocation
LabImage* labnCrop; // "one chunk" allocation LabImage* labnCrop; // "one chunk" allocation
Image8* cropImg; // "one chunk" allocation ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not Image8* cropImg; // "one chunk" allocation ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not
float * cbuf_real; // "one chunk" allocation
// --- automatically allocated and deleted when necessary, and only renewed on size changes // --- automatically allocated and deleted when necessary, and only renewed on size changes
Imagefloat* transCrop; // "one chunk" allocation, allocated if necessary Imagefloat* transCrop; // "one chunk" allocation, allocated if necessary
CieImage* cieCrop; // allocating 6 images, each in "one chunk" allocation CieImage* cieCrop; // allocating 6 images, each in "one chunk" allocation
// ----------------------------------------------------------------- // -----------------------------------------------------------------
float** cbuffer;
bool updating; /// Flag telling if an updater thread is currently processing bool updating; /// Flag telling if an updater thread is currently processing
bool newUpdatePending; /// Flag telling the updater thread that a new update is pending bool newUpdatePending; /// Flag telling the updater thread that a new update is pending

View File

@ -226,7 +226,7 @@ public:
void chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW, LabImage* lold, LabImage* lnew, LUTf &acurve, LUTf &bcurve, LUTf & satcurve, LUTf & satclcurve, LUTf &clcurve, LUTf &curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histLurve); void chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW, LabImage* lold, LabImage* lnew, LUTf &acurve, LUTf &bcurve, LUTf & satcurve, LUTf & satclcurve, LUTf &clcurve, LUTf &curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histLurve);
void vibrance (LabImage* lab);//Jacques' vibrance void vibrance (LabImage* lab);//Jacques' vibrance
// void colorCurve (LabImage* lold, LabImage* lnew); // void colorCurve (LabImage* lold, LabImage* lnew);
void sharpening (LabImage* lab, float** buffer, SharpeningParams &sharpenParam); void sharpening (LabImage* lab, const SharpeningParams &sharpenParam);
void sharpeningcam (CieImage* ncie, float** buffer); void sharpeningcam (CieImage* ncie, float** buffer);
void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const FramesMetaData *metadata, int rawRotationDeg, bool fullImage); void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const FramesMetaData *metadata, int rawRotationDeg, bool fullImage);
float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh); float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh);

View File

@ -24,17 +24,19 @@
#include "rt_math.h" #include "rt_math.h"
#include "sleef.c" #include "sleef.c"
#include "opthelper.h" #include "opthelper.h"
//#define BENCHMARK #define BENCHMARK
#include "StopWatch.h" #include "StopWatch.h"
using namespace std; using namespace std;
namespace { namespace {
float calcBlendFactor(float val, float threshold) { float calcBlendFactor(float val, float threshold) {
// sigmoid function // sigmoid function
// result is in ]0;1] range // result is in ]0;1] range
// inflexion point is at (x, y) (threshold, 0.5) // inflexion point is at (x, y) (threshold, 0.5)
return threshold == 0.f ? 1.f : 1.f / (1.f + xexpf(16.f - 16.f * val / threshold)); return threshold == 0.f ? 1.f : 1.f / (1.f + xexpf(16.f - 16.f * val / threshold));
} }
#ifdef __SSE2__ #ifdef __SSE2__
vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) { vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) {
// sigmoid function // sigmoid function
@ -48,31 +50,33 @@ vfloat calcBlendFactor(vfloat valv, vfloat thresholdv) {
#endif #endif
void buildBlendMask(float** luminance, rtengine::JaggedArray<float> &blend, int W, int H, float contrastThreshold, float amount = 1.f) { void buildBlendMask(float** luminance, rtengine::JaggedArray<float> &blend, int W, int H, float contrastThreshold, float amount = 1.f) {
BENCHFUN
// upper border // upper border
for(int j = 0; j < 2; j++) for(int j = 0; j < 2; j++)
for(int i = 0; i < W; ++i) { for(int i = 0; i < W; ++i) {
blend[j][i] = 0.f; blend[j][i] = 0.f;
} }
constexpr float scale = 0.0625f / 327.68f;
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
{ {
#ifdef __SSE2__ #ifdef __SSE2__
vfloat contrastThresholdv = F2V(contrastThreshold); const vfloat contrastThresholdv = F2V(contrastThreshold);
vfloat scalev = F2V(0.0625f / 327.68f); const vfloat scalev = F2V(scale);
vfloat amountv = F2V(amount); const vfloat amountv = F2V(amount);
#endif #endif
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp for schedule(dynamic,16) nowait #pragma omp for schedule(dynamic,16) nowait
#endif #endif
for(int j = 2; j < H - 2; ++j) { for(int j = 2; j < H - 2; ++j) {
// left two pixels
blend[j][0] = blend[j][1] = 0.f; blend[j][0] = blend[j][1] = 0.f;
int i = 2; int i = 2;
#ifdef __SSE2__ #ifdef __SSE2__
for(; i < W - 5; i += 4) { 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])) + 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; SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev;
@ -82,10 +86,11 @@ void buildBlendMask(float** luminance, rtengine::JaggedArray<float> &blend, int
for(; i < W - 2; ++i) { 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]) + 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; SQR(luminance[j][i+2] - luminance[j][i-2]) + SQR(luminance[j+2][i] - luminance[j-2][i])) * scale;
blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold); blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold);
} }
// right two pixels
blend[j][W - 2] = blend[j][W - 1] = 0.f; blend[j][W - 2] = blend[j][W - 1] = 0.f;
} }
#ifdef _OPENMP #ifdef _OPENMP
@ -98,14 +103,17 @@ void buildBlendMask(float** luminance, rtengine::JaggedArray<float> &blend, int
blend[j][i] = 0.f; blend[j][i] = 0.f;
} }
} }
// blur blend mask to smooth transitions
gaussianBlur(blend, blend, W, H, 2.0);
} }
} }
void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, float** blend, int W, int H, const SharpeningParams &sharpenParam) void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, float** blend, int W, int H, const SharpeningParams &sharpenParam)
{ {
float scale = (100.f - sharpenParam.halocontrol_amount) * 0.01f; const float scale = (100.f - sharpenParam.halocontrol_amount) * 0.01f;
float sharpFac = sharpenParam.amount * 0.01f; const float sharpFac = sharpenParam.amount * 0.01f;
float** nL = base; float** nL = base;
#ifdef _OPENMP #ifdef _OPENMP
@ -170,12 +178,12 @@ void dcdamping (float** aI, float** aO, float damping, int W, int H)
const float dampingFac = -2.0 / (damping * damping); const float dampingFac = -2.0 / (damping * damping);
#ifdef __SSE2__ #ifdef __SSE2__
__m128 Iv, Ov, Uv, zerov, onev, fourv, fivev, dampingFacv, Tv, Wv, Lv; vfloat Iv, Ov, Uv, zerov, onev, fourv, fivev, dampingFacv, Tv, Wv, Lv;
zerov = _mm_setzero_ps( ); zerov = _mm_setzero_ps();
onev = F2V( 1.0f ); onev = F2V(1.f);
fourv = F2V( 4.0f ); fourv = F2V(4.f);
fivev = F2V( 5.0f ); fivev = F2V(5.f);
dampingFacv = F2V( dampingFac ); dampingFacv = F2V(dampingFac);
#endif #endif
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp for #pragma omp for
@ -186,8 +194,8 @@ void dcdamping (float** aI, float** aO, float damping, int W, int H)
#ifdef __SSE2__ #ifdef __SSE2__
for (; j < W - 3; j += 4) { for (; j < W - 3; j += 4) {
Iv = LVFU( aI[i][j] ); Iv = LVFU(aI[i][j]);
Ov = LVFU( aO[i][j] ); Ov = LVFU(aO[i][j]);
Lv = xlogf(Iv / Ov); Lv = xlogf(Iv / Ov);
Wv = Ov - Iv; Wv = Ov - Iv;
Uv = (Ov * Lv + Wv) * dampingFacv; Uv = (Ov * Lv + Wv) * dampingFacv;
@ -198,7 +206,7 @@ void dcdamping (float** aI, float** aO, float damping, int W, int H)
Uv = (Wv / Iv) * Uv + onev; Uv = (Wv / Iv) * Uv + onev;
Uv = vselfzero(vmaskf_gt(Iv, zerov), Uv); Uv = vselfzero(vmaskf_gt(Iv, zerov), Uv);
Uv = vselfzero(vmaskf_gt(Ov, zerov), Uv); Uv = vselfzero(vmaskf_gt(Ov, zerov), Uv);
STVFU( aI[i][j], Uv ); STVFU(aI[i][j], Uv);
} }
#endif #endif
@ -221,6 +229,7 @@ void dcdamping (float** aI, float** aO, float damping, int W, int H)
} }
} }
namespace rtengine namespace rtengine
{ {
@ -247,9 +256,9 @@ BENCHFUN
JaggedArray<float> blend(W, H); JaggedArray<float> blend(W, H);
buildBlendMask(luminance, blend, W, H, sharpenParam.contrast / 100.f, sharpenParam.deconvamount / 100.0); buildBlendMask(luminance, blend, W, H, sharpenParam.contrast / 100.f, sharpenParam.deconvamount / 100.0);
float damping = sharpenParam.deconvdamping / 5.0; const float damping = sharpenParam.deconvdamping / 5.0;
bool needdamp = sharpenParam.deconvdamping > 0; const bool needdamp = sharpenParam.deconvdamping > 0;
double sigma = sharpenParam.deconvradius / scale; const double sigma = sharpenParam.deconvradius / scale;
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp parallel #pragma omp parallel
@ -289,20 +298,23 @@ BENCHFUN
} // end parallel } // end parallel
} }
void ImProcFunctions::sharpening (LabImage* lab, float** b2, SharpeningParams &sharpenParam) void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpenParam)
{ {
if ((!sharpenParam.enabled) || sharpenParam.amount < 1 || lab->W < 8 || lab->H < 8) { if ((!sharpenParam.enabled) || sharpenParam.amount < 1 || lab->W < 8 || lab->H < 8) {
return; return;
} }
int W = lab->W, H = lab->H;
JaggedArray<float> b2(W, H);
if (sharpenParam.method == "rld") { if (sharpenParam.method == "rld") {
deconvsharpening (lab->L, b2, lab->W, lab->H, sharpenParam); deconvsharpening (lab->L, b2, lab->W, lab->H, sharpenParam);
return; return;
} }
// Rest is UNSHARP MASK // Rest is UNSHARP MASK
int W = lab->W, H = lab->H;
float** b3 = nullptr; float** b3 = nullptr;
if (sharpenParam.edgesonly) { if (sharpenParam.edgesonly) {

View File

@ -1053,20 +1053,8 @@ private:
} }
if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) && params.sharpening.enabled) { if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) && params.sharpening.enabled) {
ipf.sharpening (labView, params.sharpening);
float **buffer = new float*[fh];
for (int i = 0; i < fh; i++) {
buffer[i] = new float[fw];
}
ipf.sharpening (labView, (float**)buffer, params.sharpening);
for (int i = 0; i < fh; i++) {
delete [] buffer[i];
}
delete [] buffer;
} }
WaveletParams WaveParams = params.wavelet; WaveletParams WaveParams = params.wavelet;
@ -1165,7 +1153,7 @@ private:
int imw, imh; int imw, imh;
double tmpScale = ipf.resizeScale (&params, fw, fh, imw, imh); double tmpScale = ipf.resizeScale (&params, fw, fh, imw, imh);
bool labResize = params.resize.enabled && params.resize.method != "Nearest" && tmpScale != 1.0; bool labResize = params.resize.enabled && params.resize.method != "Nearest" && (tmpScale != 1.0 || params.prsharpening.enabled);
LabImage *tmplab; LabImage *tmplab;
// crop and convert to rgb16 // crop and convert to rgb16
@ -1196,33 +1184,23 @@ private:
} }
if (labResize) { // resize lab data if (labResize) { // resize lab data
// resize image if(labView->W != imw || labView->H != imh) {
tmplab = new LabImage (imw, imh); // resize image
ipf.Lanczos (labView, tmplab, tmpScale); tmplab = new LabImage (imw, imh);
delete labView; ipf.Lanczos (labView, tmplab, tmpScale);
labView = tmplab; delete labView;
labView = tmplab;
}
cw = labView->W; cw = labView->W;
ch = labView->H; ch = labView->H;
if (params.prsharpening.enabled) { if (params.prsharpening.enabled) {
for (int i = 0; i < ch; i++) for (int i = 0; i < ch; i++) {
for (int j = 0; j < cw; j++) { for (int j = 0; j < cw; j++) {
labView->L[i][j] = labView->L[i][j] < 0.f ? 0.f : labView->L[i][j]; labView->L[i][j] = labView->L[i][j] < 0.f ? 0.f : labView->L[i][j];
} }
float **buffer = new float*[ch];
for (int i = 0; i < ch; i++) {
buffer[i] = new float[cw];
} }
ipf.sharpening (labView, params.prsharpening);
ipf.sharpening (labView, (float**)buffer, params.prsharpening);
for (int i = 0; i < ch; i++) {
delete [] buffer[i];
}
delete [] buffer;
} }
} }