Fix new color propagation method (#6109)

* Fixed artifacts thanks to Alberto

* Added blur to Color propagation

* Clean format code - small improvments

* color propagation: Enable old mode at blur = 0

* Improve GUI

* color propagation: smooth progress bar for blur > 0

* change label

* Some cleanups

* color propagation: small speedup for blur > 0

* color propagation: speedup for blur > 0 when region with clipped highlights is small

* Speed-up for blur=1 - clean GUI code

* color propagation: cleanups

* Harmonize events in tonecurve.cc

* tonecurve.cc : cleanup

* Highlight reconstruction: small changes to gui

* Use Gtk::Box instead of Gtk::VBox

* Change maximum number of blur levels to 4

* Suppress BENCHFUN

* Suppress bad commit locallabtools

Co-authored-by: Desmis <jdesmis@gmail.com>
Co-authored-by: Ingo Weyrich <heckflosse67@gmx.de>
This commit is contained in:
Thanatomanic 2021-02-18 13:36:54 +01:00 committed by GitHub
parent d29d5e144b
commit de15da1d59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 527 additions and 449 deletions

View File

@ -1322,6 +1322,7 @@ HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative
HISTORY_MSG_FILMNEGATIVE_REF_SPOT;FN - Reference input HISTORY_MSG_FILMNEGATIVE_REF_SPOT;FN - Reference input
HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values
HISTORY_MSG_HISTMATCHING;Auto-matched tone curve HISTORY_MSG_HISTMATCHING;Auto-matched tone curve
HISTORY_MSG_HLBL;Color propagation - blur
HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Output - Primaries HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Output - Primaries
HISTORY_MSG_ICM_OUTPUT_TEMP;Output - ICC-v4 illuminant D HISTORY_MSG_ICM_OUTPUT_TEMP;Output - ICC-v4 illuminant D
HISTORY_MSG_ICM_OUTPUT_TYPE;Output - Type HISTORY_MSG_ICM_OUTPUT_TYPE;Output - Type
@ -2367,6 +2368,7 @@ TP_GRADIENT_STRENGTH_TOOLTIP;Filter strength in stops.
TP_HLREC_BLEND;Blend TP_HLREC_BLEND;Blend
TP_HLREC_CIELAB;CIELab Blending TP_HLREC_CIELAB;CIELab Blending
TP_HLREC_COLOR;Color Propagation TP_HLREC_COLOR;Color Propagation
TP_HLREC_HLBLUR;Blur
TP_HLREC_ENA_TOOLTIP;Could be activated by Auto Levels. TP_HLREC_ENA_TOOLTIP;Could be activated by Auto Levels.
TP_HLREC_LABEL;Highlight reconstruction TP_HLREC_LABEL;Highlight reconstruction
TP_HLREC_LUMINANCE;Luminance Recovery TP_HLREC_LUMINANCE;Luminance Recovery

View File

@ -124,6 +124,26 @@ public:
} }
} }
// creator type 3
array2D(int w, int h, int startx, int starty, T ** source, unsigned int flags = 0) : width(w)
{
rows.resize(h);
if (!(flags & ARRAY2D_BYREFERENCE)) {
buffer.resize(h * width);
T* start = buffer.data();
for (ssize_t i = 0; i < h; ++i) {
rows[i] = start + i * width;
for (ssize_t j = 0; j < width; ++j) {
rows[i][j] = source[i + starty][j + startx];
}
}
} else {
for (ssize_t i = 0; i < h; ++i) {
rows[i] = source[i + starty] + startx;
}
}
}
array2D(const array2D& other) : array2D(const array2D& other) :
width(other.width), width(other.width),
buffer(other.buffer) buffer(other.buffer)

View File

@ -206,6 +206,11 @@ public:
return static_cast<double>(r) * workingspace[1][0] + static_cast<double>(g) * workingspace[1][1] + static_cast<double>(b) * workingspace[1][2]; return static_cast<double>(r) * workingspace[1][0] + static_cast<double>(g) * workingspace[1][1] + static_cast<double>(b) * workingspace[1][2];
} }
static float rgbLuminance(float r, float g, float b, const float workingspace[3])
{
return r * workingspace[0] + g * workingspace[1] + b * workingspace[2];
}
#ifdef __SSE2__ #ifdef __SSE2__
static vfloat rgbLuminance(vfloat r, vfloat g, vfloat b, const vfloat workingspace[3]) static vfloat rgbLuminance(vfloat r, vfloat g, vfloat b, const vfloat workingspace[3])
{ {

View File

@ -32,8 +32,8 @@
#include "opthelper.h" #include "opthelper.h"
#include "rawimagesource.h" #include "rawimagesource.h"
#include "rt_math.h" #include "rt_math.h"
#define BENCHMARK //#define BENCHMARK
#include "StopWatch.h" //#include "StopWatch.h"
#include "guidedfilter.h" #include "guidedfilter.h"
#include "settings.h" #include "settings.h"
#include "gauss.h" #include "gauss.h"
@ -299,9 +299,9 @@ extern const Settings *settings;
using namespace procparams; using namespace procparams;
const ProcParams params; const ProcParams params;
void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue) void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue, int blur)
{ {
BENCHFUN // BENCHFUN
double progress = 0.0; double progress = 0.0;
if (plistener) { if (plistener) {
@ -318,7 +318,7 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
constexpr float threshpct = 0.25f; constexpr float threshpct = 0.25f;
constexpr float maxpct = 0.95f; constexpr float maxpct = 0.95f;
constexpr float epsilon = 0.00001f; constexpr float epsilon = 0.00001f;
//%%%%%%%%%%%%%%%%%%%%
//for blend algorithm: //for blend algorithm:
constexpr float blendthresh = 1.0; constexpr float blendthresh = 1.0;
// Transform matrixes rgb>lab and back // Transform matrixes rgb>lab and back
@ -424,11 +424,6 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
return; return;
} }
// if (plistener) {
// progress += 0.05;
// plistener->setProgress(progress);
// }
constexpr int blurBorder = 256; constexpr int blurBorder = 256;
minx = std::max(0, minx - blurBorder); minx = std::max(0, minx - blurBorder);
miny = std::max(0, miny - blurBorder); miny = std::max(0, miny - blurBorder);
@ -442,21 +437,8 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
array2D<float> temp(bufferWidth, blurHeight); // allocate temporary buffer array2D<float> temp(bufferWidth, blurHeight); // allocate temporary buffer
// blur RGB channels // blur RGB channels
boxblur2(red, channelblur[0], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4); boxblur2(red, channelblur[0], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4);
// if (plistener) {
// progress += 0.07;
// plistener->setProgress(progress);
// }
boxblur2(green, channelblur[1], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4); boxblur2(green, channelblur[1], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4);
// if (plistener) {
// progress += 0.07;
// plistener->setProgress(progress);
// }
boxblur2(blue, channelblur[2], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4); boxblur2(blue, channelblur[2], temp, miny, minx, blurHeight, blurWidth, bufferWidth, 4);
if (plistener) { if (plistener) {
@ -470,7 +452,7 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
#endif #endif
for (int i = 0; i < blurHeight; ++i) { for (int i = 0; i < blurHeight; ++i) {
for (int j = 0; j < blurWidth; ++j) { for (int j = 0; j < blurWidth; ++j) {
channelblur[0][i][j] = fabsf(channelblur[0][i][j] - red[i + miny][j + minx]) + fabsf(channelblur[1][i][j] - green[i + miny][j + minx]) + fabsf(channelblur[2][i][j] - blue[i + miny][j + minx]); channelblur[0][i][j] = std::fabs(channelblur[0][i][j] - red[i + miny][j + minx]) + std::fabs(channelblur[1][i][j] - green[i + miny][j + minx]) + std::fabs(channelblur[2][i][j] - blue[i + miny][j + minx]);
} }
} }
@ -529,7 +511,7 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
} }
array2D<float> hilite_full4(bufferWidth, blurHeight); array2D<float> hilite_full4(bufferWidth, blurHeight);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//blur highlight data //blur highlight data
boxblur2(hilite_full[3], hilite_full4, temp, 0, 0, blurHeight, blurWidth, bufferWidth, 1); boxblur2(hilite_full[3], hilite_full4, temp, 0, 0, blurHeight, blurWidth, bufferWidth, 1);
@ -566,9 +548,7 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
multi_array2D<float, 4> hilite(hfw + 1, hfh + 1, ARRAY2D_CLEAR_DATA, 48); multi_array2D<float, 4> hilite(hfw + 1, hfh + 1, ARRAY2D_CLEAR_DATA, 48);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// blur and resample highlight data; range=size of blur, pitch=sample spacing // blur and resample highlight data; range=size of blur, pitch=sample spacing
array2D<float> temp2(blurWidth / pitch + (blurWidth % pitch == 0 ? 0 : 1), blurHeight); array2D<float> temp2(blurWidth / pitch + (blurWidth % pitch == 0 ? 0 : 1), blurHeight);
for (int m = 0; m < 4; ++m) { for (int m = 0; m < 4; ++m) {
@ -651,11 +631,11 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
} }
if (hilite[3][2][j] <= epsilon) { if (hilite[3][2][j] <= epsilon) {
hilite_dir[0 + c][0][j] = hilite_dir0[c][j][2]; hilite_dir[0 + c][0][j] = hilite_dir0[c][j][2];
} }
if (hilite[3][3][j] <= epsilon) { if (hilite[3][3][j] <= epsilon) {
hilite_dir[0 + c][1][j] = hilite_dir0[c][j][3]; hilite_dir[0 + c][1][j] = hilite_dir0[c][j][3];
} }
if (hilite[3][hfh - 3][j] <= epsilon) { if (hilite[3][hfh - 3][j] <= epsilon) {
@ -938,38 +918,43 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
hilite[c].free(); hilite[c].free();
} }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// now reconstruct clipped channels using color ratios // now reconstruct clipped channels using color ratios
//using code from ART - thanks to Alberto Griggio //using code from ART - thanks to Alberto Griggio
const int W2 = float(W) / 2.f + 0.5f; const int W2 = blur > 0 ? blurWidth / 2.f + 0.5f : 0;
const int H2 = float(H) / 2.f + 0.5f; const int H2 = blur > 0 ? blurHeight / 2.f + 0.5f : 0;
array2D<float> mask(W2, H2, ARRAY2D_CLEAR_DATA); array2D<float> mask(W2, H2, ARRAY2D_CLEAR_DATA);
array2D<float> rbuf(W2, H2); array2D<float> rbuf(W2, H2);
array2D<float> gbuf(W2, H2); array2D<float> gbuf(W2, H2);
array2D<float> bbuf(W2, H2); array2D<float> bbuf(W2, H2);
array2D<float> guide(W2, H2); array2D<float> guide(W2, H2);
using rtengine::TMatrix; if (blur > 0) {
TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params.icm.workingProfile); array2D<float> rbuffer(blurWidth, blurHeight, minx, miny, red, ARRAY2D_BYREFERENCE);
rescaleNearest(rbuffer, rbuf, true);
{ array2D<float> gbuffer(blurWidth, blurHeight, minx, miny, green, ARRAY2D_BYREFERENCE);
array2D<float> rsrc(W, H, red, ARRAY2D_BYREFERENCE); rescaleNearest(gbuffer, gbuf, true);
array2D<float> gsrc(W, H, green, ARRAY2D_BYREFERENCE); array2D<float> bbuffer(blurWidth, blurHeight, minx, miny, blue, ARRAY2D_BYREFERENCE);
array2D<float> bsrc(W, H, blue, ARRAY2D_BYREFERENCE); rescaleNearest(bbuffer, bbuf, true);
rescaleNearest(rsrc, rbuf, true);
rescaleNearest(gsrc, gbuf, true);
rescaleNearest(bsrc, bbuf, true);
LUTf gamma(65536);
#ifdef _OPENMP #ifdef _OPENMP
# pragma omp parallel for #pragma omp parallel for
#endif
for (int i = 0; i < 65536; ++i) {
gamma[i] = pow_F(i / 65535.f, 2.2f);
}
const float xyzcam[3] = {static_cast<float>(imatrices.xyz_cam[1][0]), static_cast<float>(imatrices.xyz_cam[1][1]), static_cast<float>(imatrices.xyz_cam[1][2])};
#ifdef _OPENMP
#pragma omp parallel for
#endif #endif
for (int y = 0; y < H2; ++y) { for (int y = 0; y < H2; ++y) {
for (int x = 0; x < W2; ++x) { for (int x = 0; x < W2; ++x) {
guide[y][x] = Color::igamma_srgb(Color::rgbLuminance(static_cast<double>(rbuf[y][x]), static_cast<double>(gbuf[y][x]), static_cast<double>(bbuf[y][x]), ws)); guide[y][x] = gamma[Color::rgbLuminance(rbuf[y][x], gbuf[y][x], bbuf[y][x], xyzcam)];
} }
} }
} }
//end addind code ART //end adding code ART
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16) #pragma omp parallel for schedule(dynamic,16)
@ -1050,21 +1035,20 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
// Copy converted pixel back // Copy converted pixel back
if (pixel[0] > blendpt) { if (pixel[0] > blendpt) {
const float rfrac = LIM01(medFactor[0] * (pixel[0] - blendpt)); const float rfrac = LIM01(medFactor[0] * (pixel[0] - blendpt));
rgb_blend[0] = rfrac * rgb[0] + (1.f - rfrac) * pixel[0]; rgb_blend[0] = intp(rfrac, rgb[0], pixel[0]);
} }
if (pixel[1] > blendpt) { if (pixel[1] > blendpt) {
const float gfrac = LIM01(medFactor[1] * (pixel[1] - blendpt)); const float gfrac = LIM01(medFactor[1] * (pixel[1] - blendpt));
rgb_blend[1] = gfrac * rgb[1] + (1.f - gfrac) * pixel[1]; rgb_blend[1] = intp(gfrac, rgb[1], pixel[1]);
} }
if (pixel[2] > blendpt) { if (pixel[2] > blendpt) {
const float bfrac = LIM01(medFactor[2] * (pixel[2] - blendpt)); const float bfrac = LIM01(medFactor[2] * (pixel[2] - blendpt));
rgb_blend[2] = bfrac * rgb[2] + (1.f - bfrac) * pixel[2]; rgb_blend[2] = intp(bfrac, rgb[2], pixel[2]);
} }
//end of HLRecovery_blend estimation //end of HLRecovery_blend estimation
//%%%%%%%%%%%%%%%%%%%%%%%
//there are clipped highlights //there are clipped highlights
//first, determine weighted average of unclipped extensions (weighting is by 'hue' proximity) //first, determine weighted average of unclipped extensions (weighting is by 'hue' proximity)
@ -1090,7 +1074,7 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
} }
for (int dir = 0; dir < 2; ++dir) { for (int dir = 0; dir < 2; ++dir) {
const float Yhi2 = 1.f / ( hilite_dir[dir * 4 + 0][i1][j1] + hilite_dir[dir * 4 + 1][i1][j1] + hilite_dir[dir * 4 + 2][i1][j1]); const float Yhi2 = 1.f / (hilite_dir[dir * 4 + 0][i1][j1] + hilite_dir[dir * 4 + 1][i1][j1] + hilite_dir[dir * 4 + 2][i1][j1]);
if (Yhi2 < 2.f) { if (Yhi2 < 2.f) {
const float dirwt = 1.f / ((1.f + 65535.f * (SQR(rgb_blend[0] - hilite_dir[dir * 4 + 0][i1][j1] * Yhi2) + const float dirwt = 1.f / ((1.f + 65535.f * (SQR(rgb_blend[0] - hilite_dir[dir * 4 + 0][i1][j1] * Yhi2) +
@ -1119,10 +1103,11 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
if (UNLIKELY(!totwt)) { if (UNLIKELY(!totwt)) {
continue; continue;
} }
//using code from ART - thanks to Alberto Griggio //using code from ART - thanks to Alberto Griggio
float maskval = 1.f; float maskval = 1.f;
int yy = i + miny; const int yy = i + miny;
int xx = j + minx; const int xx = j + minx;
//now correct clipped channels //now correct clipped channels
if (pixel[0] > max_f[0] && pixel[1] > max_f[1] && pixel[2] > max_f[2]) { if (pixel[0] > max_f[0] && pixel[1] > max_f[1] && pixel[2] > max_f[2]) {
@ -1160,63 +1145,81 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue
if (Y > whitept) { if (Y > whitept) {
const float mult = whitept / Y; const float mult = whitept / Y;
red[yy][xx] *= mult;
red[yy][xx] *= mult;
green[yy][xx] *= mult; green[yy][xx] *= mult;
blue[yy][xx] *= mult; blue[yy][xx] *= mult;
} }
int ii = (yy) / 2; if (blur > 0) {
int jj = (xx) / 2; const int ii = i / 2;
rbuf[ii][jj] = red[yy][xx]; const int jj = j / 2;
gbuf[ii][jj] = green[yy][xx]; rbuf[ii][jj] = red[yy][xx];
bbuf[ii][jj] = blue[yy][xx]; gbuf[ii][jj] = green[yy][xx];
mask[ii][jj] = maskval; bbuf[ii][jj] = blue[yy][xx];
mask[ii][jj] = maskval;
}
} }
} }
if (plistener) { if (blur > 0) {
progress += 0.05; if (plistener) {
plistener->setProgress(progress); progress += 0.05;
} plistener->setProgress(progress);
}
blur = rtengine::LIM(blur - 1, 0, 3);
// #ifdef _OPENMP constexpr float vals[4][3] = {{4.0f, 0.3f, 0.3f},
// #pragma omp parallel // {3.5f, 0.5f, 0.2f},
// #endif {3.0f, 1.0f, 0.1f},
{ {3.0f, 2.0f, 0.01f},
//gaussianBlur(mask, mask, W/2, H/2, 5); {2.0f, 3.0f, 0.001f}
// gaussianBlur(rbuf, rbuf, W/2, H/2, 1); };
// gaussianBlur(gbuf, gbuf, W/2, H/2, 1);
// gaussianBlur(bbuf, bbuf, W/2, H/2, 1);
guidedFilter(guide, mask, mask, 2, 0.001f, true, 1);
guidedFilter(guide, rbuf, rbuf, 3, 0.01f * 65535.f, true, 1);
guidedFilter(guide, gbuf, gbuf, 3, 0.01f * 65535.f, true, 1);
guidedFilter(guide, bbuf, bbuf, 3, 0.01f * 65535.f, true, 1);
}
{ const float rad1 = vals[blur][0];
const float rad2 = vals[blur][1];
const float th = vals[blur][2];
guidedFilter(guide, mask, mask, rad1, th, true, 1);
if (plistener) {
progress += 0.03;
plistener->setProgress(progress);
}
if (blur > 0) { //no use of 2nd guidedFilter if Blur = 0 (slider to 1)..speed-up and very small differences.
guidedFilter(guide, rbuf, rbuf, rad2, 0.01f * 65535.f, true, 1);
if (plistener) {
progress += 0.03;
plistener->setProgress(progress);
}
guidedFilter(guide, gbuf, gbuf, rad2, 0.01f * 65535.f, true, 1);
if (plistener) {
progress += 0.03;
plistener->setProgress(progress);
}
guidedFilter(guide, bbuf, bbuf, rad2, 0.01f * 65535.f, true, 1);
if (plistener) {
progress += 0.03;
plistener->setProgress(progress);
}
}
#ifdef _OPENMP #ifdef _OPENMP
#pragma omp parallel for #pragma omp parallel for schedule(dynamic,16)
#endif #endif
for (int y = 0; y < H; ++y) { for (int y = 0; y < blurHeight; ++y) {
float fy = y * 0.5f; const float fy = y * 0.5f;
int yy = y / 2; const int yy = y / 2;
for (int x = 0; x < W; ++x) { for (int x = 0; x < blurWidth; ++x) {
float fx = x * 0.5f; const int xx = x / 2;
int xx = x / 2; const float m = mask[yy][xx];
float m = mask[yy][xx];
if (m > 0.f) { if (m > 0.f) {
red[y][x] = intp(m, getBilinearValue(rbuf, fx, fy), red[y][x]); const float fx = x * 0.5f;
green[y][x] = intp(m, getBilinearValue(gbuf, fx, fy), green[y][x]); red[y + miny][x + minx] = intp(m, getBilinearValue(rbuf, fx, fy), red[y + miny][x + minx]);
blue[y][x] = intp(m, getBilinearValue(bbuf, fx, fy), blue[y][x]); green[y + miny][x + minx] = intp(m, getBilinearValue(gbuf, fx, fy), green[y + miny][x + minx]);
blue[y + miny][x + minx] = intp(m, getBilinearValue(bbuf, fx, fy), blue[y + miny][x + minx]);
} }
} }
} }
} }
if (plistener) { if (plistener) {
plistener->setProgress(1.00); plistener->setProgress(1.00);
} }

View File

@ -98,7 +98,6 @@ public:
virtual void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D<float, 4> &conversionBuffer, LUTu &lhist16RETI) {}; virtual void retinexPrepareBuffers (const procparams::ColorManagementParams& cmp, const procparams::RetinexParams &retinexParams, multi_array2D<float, 4> &conversionBuffer, LUTu &lhist16RETI) {};
virtual void flush () = 0; virtual void flush () = 0;
virtual void HLRecovery_Global (const procparams::ToneCurveParams &hrp) {}; virtual void HLRecovery_Global (const procparams::ToneCurveParams &hrp) {};
virtual void HLRecovery_inpaint (float** red, float** green, float** blue) {};
virtual bool isRGBSourceModified () const = 0; // tracks whether cached rgb output of demosaic has been modified virtual bool isRGBSourceModified () const = 0; // tracks whether cached rgb output of demosaic has been modified

View File

@ -385,6 +385,7 @@ ToneCurveParams::ToneCurveParams() :
saturation(0), saturation(0),
shcompr(50), shcompr(50),
hlcompr(0), hlcompr(0),
hlbl(0),
hlcomprthresh(0), hlcomprthresh(0),
histmatching(false), histmatching(false),
fromHistMatching(false), fromHistMatching(false),
@ -410,6 +411,7 @@ bool ToneCurveParams::isPanningRelatedChange(const ToneCurveParams& other) const
&& saturation == other.saturation && saturation == other.saturation
&& shcompr == other.shcompr && shcompr == other.shcompr
&& hlcompr == other.hlcompr && hlcompr == other.hlcompr
&& hlbl == other.hlbl
&& hlcomprthresh == other.hlcomprthresh && hlcomprthresh == other.hlcomprthresh
&& histmatching == other.histmatching && histmatching == other.histmatching
&& clampOOG == other.clampOOG); && clampOOG == other.clampOOG);
@ -433,6 +435,7 @@ bool ToneCurveParams::operator ==(const ToneCurveParams& other) const
&& saturation == other.saturation && saturation == other.saturation
&& shcompr == other.shcompr && shcompr == other.shcompr
&& hlcompr == other.hlcompr && hlcompr == other.hlcompr
&& hlbl == other.hlbl
&& hlcomprthresh == other.hlcomprthresh && hlcomprthresh == other.hlcomprthresh
&& histmatching == other.histmatching && histmatching == other.histmatching
&& fromHistMatching == other.fromHistMatching && fromHistMatching == other.fromHistMatching
@ -5399,6 +5402,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
// Highlight recovery // Highlight recovery
saveToKeyfile(!pedited || pedited->toneCurve.hrenabled, "HLRecovery", "Enabled", toneCurve.hrenabled, keyFile); saveToKeyfile(!pedited || pedited->toneCurve.hrenabled, "HLRecovery", "Enabled", toneCurve.hrenabled, keyFile);
saveToKeyfile(!pedited || pedited->toneCurve.method, "HLRecovery", "Method", toneCurve.method, keyFile); saveToKeyfile(!pedited || pedited->toneCurve.method, "HLRecovery", "Method", toneCurve.method, keyFile);
saveToKeyfile(!pedited || pedited->toneCurve.hlbl, "HLRecovery", "Hlbl", toneCurve.hlbl, keyFile);
const std::map<ToneCurveMode, const char*> tc_mapping = { const std::map<ToneCurveMode, const char*> tc_mapping = {
{ToneCurveMode::STD, "Standard"}, {ToneCurveMode::STD, "Standard"},
@ -6924,6 +6928,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
if (keyFile.has_group("HLRecovery")) { if (keyFile.has_group("HLRecovery")) {
assignFromKeyfile(keyFile, "HLRecovery", "Enabled", pedited, toneCurve.hrenabled, pedited->toneCurve.hrenabled); assignFromKeyfile(keyFile, "HLRecovery", "Enabled", pedited, toneCurve.hrenabled, pedited->toneCurve.hrenabled);
assignFromKeyfile(keyFile, "HLRecovery", "Method", pedited, toneCurve.method, pedited->toneCurve.method); assignFromKeyfile(keyFile, "HLRecovery", "Method", pedited, toneCurve.method, pedited->toneCurve.method);
assignFromKeyfile(keyFile, "HLRecovery", "Hlbl", pedited, toneCurve.hlbl, pedited->toneCurve.hlbl);
} }
if (keyFile.has_group("Channel Mixer")) { if (keyFile.has_group("Channel Mixer")) {

View File

@ -297,6 +297,7 @@ struct ToneCurveParams {
int saturation; int saturation;
int shcompr; int shcompr;
int hlcompr; // Highlight Recovery's compression int hlcompr; // Highlight Recovery's compression
int hlbl; // Highlight Recovery's compression
int hlcomprthresh; // Highlight Recovery's threshold int hlcomprthresh; // Highlight Recovery's threshold
bool histmatching; // histogram matching bool histmatching; // histogram matching
bool fromHistMatching; bool fromHistMatching;

View File

@ -2438,7 +2438,7 @@ void RawImageSource::HLRecovery_Global(const ToneCurveParams &hrp)
printf ("Applying Highlight Recovery: Color propagation...\n"); printf ("Applying Highlight Recovery: Color propagation...\n");
} }
HLRecovery_inpaint (red, green, blue); HLRecovery_inpaint (red, green, blue, hrp.hlbl);
rgbSourceModified = true; rgbSourceModified = true;
} }
} }

View File

@ -195,7 +195,7 @@ public:
static void inverse33(const double (*coeff)[3], double (*icoeff)[3]); static void inverse33(const double (*coeff)[3], double (*icoeff)[3]);
void MSR(float** luminance, float **originalLuminance, float **exLuminance, const LUTf& mapcurve, bool mapcontlutili, int width, int height, const procparams::RetinexParams &deh, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax); void MSR(float** luminance, float **originalLuminance, float **exLuminance, const LUTf& mapcurve, bool mapcontlutili, int width, int height, const procparams::RetinexParams &deh, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax);
void HLRecovery_inpaint (float** red, float** green, float** blue) override; void HLRecovery_inpaint (float** red, float** green, float** blue, int blur);
static void HLRecovery_Luminance (float* rin, float* gin, float* bin, float* rout, float* gout, float* bout, int width, float maxval); static void HLRecovery_Luminance (float* rin, float* gin, float* bin, float* rout, float* gout, float* bout, int width, float maxval);
static void HLRecovery_CIELab (float* rin, float* gin, float* bin, float* rout, float* gout, float* bout, int width, float maxval, double cam[3][3], double icam[3][3]); static void HLRecovery_CIELab (float* rin, float* gin, float* bin, float* rout, float* gout, float* bout, int width, float maxval, double cam[3][3], double icam[3][3]);
static void HLRecovery_blend (float* rin, float* gin, float* bin, int width, float maxval, float* hlmax); static void HLRecovery_blend (float* rin, float* gin, float* bin, int width, float maxval, float* hlmax);

View File

@ -35,23 +35,22 @@ inline float getBilinearValue(const array2D<float> &src, float x, float y)
const int H = src.getHeight(); const int H = src.getHeight();
// Get integer and fractional parts of numbers // Get integer and fractional parts of numbers
int xi = x; const int xi = x;
int yi = y; const int yi = y;
float xf = x - xi; const float xf = x - xi;
float yf = y - yi; const float yf = y - yi;
int xi1 = std::min(xi + 1, W - 1); const int xi1 = std::min(xi + 1, W - 1);
int yi1 = std::min(yi + 1, H - 1); const int yi1 = std::min(yi + 1, H - 1);
float bl = src[yi][xi]; const float bl = src[yi][xi];
float br = src[yi][xi1]; const float br = src[yi][xi1];
float tl = src[yi1][xi]; const float tl = src[yi1][xi];
float tr = src[yi1][xi1]; const float tr = src[yi1][xi1];
// interpolate // interpolate
float b = xf * br + (1.f - xf) * bl; const float b = intp(xf, br, bl);
float t = xf * tr + (1.f - xf) * tl; const float t = intp(xf, tr, tl);
float pxf = yf * t + (1.f - yf) * b; return intp(yf, t, b);
return pxf;
} }

View File

@ -47,6 +47,7 @@ void ParamsEdited::set(bool v)
toneCurve.saturation = v; toneCurve.saturation = v;
toneCurve.shcompr = v; toneCurve.shcompr = v;
toneCurve.hlcompr = v; toneCurve.hlcompr = v;
toneCurve.hlbl = v;
toneCurve.hlcomprthresh = v; toneCurve.hlcomprthresh = v;
toneCurve.autoexp = v; toneCurve.autoexp = v;
toneCurve.clip = v; toneCurve.clip = v;
@ -720,6 +721,7 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
toneCurve.saturation = toneCurve.saturation && p.toneCurve.saturation == other.toneCurve.saturation; toneCurve.saturation = toneCurve.saturation && p.toneCurve.saturation == other.toneCurve.saturation;
toneCurve.shcompr = toneCurve.shcompr && p.toneCurve.shcompr == other.toneCurve.shcompr; toneCurve.shcompr = toneCurve.shcompr && p.toneCurve.shcompr == other.toneCurve.shcompr;
toneCurve.hlcompr = toneCurve.hlcompr && p.toneCurve.hlcompr == other.toneCurve.hlcompr; toneCurve.hlcompr = toneCurve.hlcompr && p.toneCurve.hlcompr == other.toneCurve.hlcompr;
toneCurve.hlbl = toneCurve.hlbl && p.toneCurve.hlbl == other.toneCurve.hlbl;
toneCurve.hlcomprthresh = toneCurve.hlcomprthresh && p.toneCurve.hlcomprthresh == other.toneCurve.hlcomprthresh; toneCurve.hlcomprthresh = toneCurve.hlcomprthresh && p.toneCurve.hlcomprthresh == other.toneCurve.hlcomprthresh;
toneCurve.autoexp = toneCurve.autoexp && p.toneCurve.autoexp == other.toneCurve.autoexp; toneCurve.autoexp = toneCurve.autoexp && p.toneCurve.autoexp == other.toneCurve.autoexp;
toneCurve.clip = toneCurve.clip && p.toneCurve.clip == other.toneCurve.clip; toneCurve.clip = toneCurve.clip && p.toneCurve.clip == other.toneCurve.clip;
@ -2008,6 +2010,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.toneCurve.method = mods.toneCurve.method; toEdit.toneCurve.method = mods.toneCurve.method;
} }
if (toneCurve.hlbl) {
toEdit.toneCurve.hlbl = mods.toneCurve.hlbl;
}
if (toneCurve.histmatching) { if (toneCurve.histmatching) {
toEdit.toneCurve.histmatching = mods.toneCurve.histmatching; toEdit.toneCurve.histmatching = mods.toneCurve.histmatching;
} }

View File

@ -51,6 +51,7 @@ struct ToneCurveParamsEdited {
bool saturation; bool saturation;
bool shcompr; bool shcompr;
bool hlcompr; bool hlcompr;
bool hlbl;
bool hlcomprthresh; bool hlcomprthresh;
bool autoexp; bool autoexp;
bool clip; bool clip;

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@ protected:
sigc::connection methconn; sigc::connection methconn;
sigc::connection enaconn; sigc::connection enaconn;
bool lasthrEnabled; bool lasthrEnabled;
Adjuster* hlbl;
Gtk::Box* abox; Gtk::Box* abox;
Gtk::Box* hlrbox; Gtk::Box* hlrbox;
@ -80,6 +81,7 @@ protected:
rtengine::ProcEvent EvHistMatching; rtengine::ProcEvent EvHistMatching;
rtengine::ProcEvent EvHistMatchingBatch; rtengine::ProcEvent EvHistMatchingBatch;
rtengine::ProcEvent EvClampOOG; rtengine::ProcEvent EvClampOOG;
rtengine::ProcEvent EvHLbl;
// used temporarily in eventing // used temporarily in eventing
double nextExpcomp; double nextExpcomp;