rawTherapee/rtengine/simpleprocess.cc
Desmis a578423378
Abstract Profile - Contrast enhancement -- Selective Editing Cam16 and JzCzHz - improvments (#7111)
* Init levels trc GUI

* Levels TRC

* Complete with gamma based attenuation

* Limit RGB channel Slope with checkbox

* Improve GUI and code channel TRC

* Change default values - compexity levels RGB channels

* Relative gamma mode RGB channel TRC

* Change label and ponderation rolloff

* Change rolloff level

* Threshold attenuation

* Threshold attenuation 2 part

* GUI Link R G B

* Linked RGB with Green slope - RGB channels

* Set Freeman TM functions with ImProcFunctions

* First GUI Abstract profile highlight attenuation

* GUI AP part 2

* Restore olg GUI AP

* Expander AP primaries adn illuminant

* Disable RGB channel TRC

* Expander contrast AP

* Slider attenuation response

* Save work GUI local contrast

* Save GUI part 2 AP curve

* Local contrast GUI Abstract Profile

* Move Abstract profile in toolpanel and ICMpanel

* rtengine variable contrast

* Variable contrast 2

* Variable contrast engine 3

* Variable contrast engine 4

* Variable contrast engine

* Detail levels pyramid

* Engine residual contrast

* Residual contrast

* Change settings detail levels

* Expander refinement - new tooltips - low resid contrast

* Change contrast profile and labels

* Remove warning message GUI Gtk

* Gamutcontrol code - disabled

* Improve with calceffect

* Other improvement variable contrast

* Offset variable contrast

* Range offset - comment code

* Parametric inva fot lut

* Clean cmakelist.txt

* Change contrast profiles

* Comment code ipwavelet

* Added orthogonal Daubechies scaling D20

* Change strenght curve - tooltip Daubechies

* Forgotten changes

* Comment code

* Move variable in process - take into account highlight attenuation

* Display label maximum preview and preset selection

* Remove console message

* harmonize levels wavelets iplocallab

* Tooltips contrast enhancement

* Change tooltip Contrast profile

* Chnage tooltip Contrast

* Message warning preview size

* Change gamma TRC values in GUI

* Remove itanium architecture support for windows as PR 7105

* Change windows.yml and appimage.yml

* Windows.yml apseimprov

* Clean and comment ipwavelet

* Clean comment icmpanel.cc

* Harmonize local contrast wavelet Selective editing with Abstract profile

* Harmonize with AP - offset

* vanishing moment D20 - Selective editing wavelet

* Offset only in advanced mode

* GUI expander contrast enable and pyrwavtrc

* Clean and comment code

* merge with dev

* Prepare sigmoid based

* Contrast sigmoid GUI

* Skew sigmoid GUI

* Sigmoid tone mapper in iplocallab

* Change GUI settings

* White-point and black-point auto

* Change EvDCP to ALLNORAW as others events DCP

* Change default skew

* Change settings - enable scale Yb

* Display white point - advanced mode

* Improve GUI

* Clean unused variable

* new sigmoid Q in cam16

* Change tooltips and default sigmoid Q settings

* Sigmoid Jz

* Clean code Jz and sigmoid

* Harmonize Sigmoid Q and Sigmoid RGB

* Harmonize Sigmoid Jz

* Clean code

* Improve labels wit cd/m2

* Slope base Q methode first

* GUI slope based Q

* Change default settings and tooltips

* Change tooltips

* Clean code - change default setting

* Change default local contrast & wavelet to wavelet & basic mode

* Fixed bad assignation slopesmoq

* Improve sigmoid and slope based Q - GUI for Log encoding Color appearance

* Remove wrong change

* various small improvments

* Allows black and white AP and SDA in basic mode

* Change  the writing of wGamma and wSlope - attenuates the effect of the first 2 AP contrast profiles

* Clean code wgamma wslope

* Set curve Cam16 in basic mode

* Change position curve in GUI cam16

* Enable tonecurve1 in colorappearance & lighting in standard mode

* Fixed bug scale yb scene - ciecam curve - change default contrast enhancement

* not reset curve shape ciecam in strandard

* Change label Tone mapping operators and tooltips

* Change some labels and tooltips - Appearance - Mask and Mofifications - Recovery Based On Luminance Mask

* Forgotten changes

* Clean locallabtools2.cc

* Maxlevel wavelet minimum to 5

* Reset mask and modifications in SE wavelet and all tools in Global

* Show modified areas SE wavelet

* Tooltip show wavelets decomposition

* Fixed another bad behavior in Global - changes also in color & light for merge file

* Change behavior fullimage - global as in PR GHS

* Disable all mask and modifications in Global but remain active in fullimage and normal

* Set expander expanded = true

* Chane contrast enhancement coef

* Replace VBox trcWavVBox by ToolParamBlock trcWavFBox

* Forgotten code in icmpanel read pedited opacityShapeWLI - hope solve batch mode

* Change RGB Slope behavior with link

* No access to last level contrast enhancement

* Move Abstract Profile tooltip to title

The tooltip was popping up when the cursor was over almost any part of
the tool which was inconvenient. Now, the tooltip only appears when
hovering over the title.

* Improve Color Management expanders behavior

By default, collapse Abstract Profile and leave it's sub-expanders
expanded.

Keep the expanded state of all the sub-expanders during the editing
session.

Fix the right-click behavior. The clicked expander should be expanded
and all siblings, if any, should be collapsed.

* Fix RGB slope tone mapping RGB linkage

Synchronize the red, green, and blue values before updating the preview
to avoid using incorrect values to generate the preview.

* Fix SE CAM tone mapping slider defocus

Avoid unnecessarily hiding then showing the adjusters in tone mapping so
that focus is not lost while adjusting the adjusters.

* Delete history kslopesmo - remove IcmOpacityCurveWL

* change the tooltips as suggested by Lawrence

* Review L37 - change strengthjz strengthlc - MIDDLE_GREY MIDDLE_GREYjz - artifacts ciecam

* Change name Tone map freeman functions

* Remove gamutcont - rename localcont - change allocation memory wdspot

* Clean procparams

* remove sigmoidsenscie - logcieq

* Added * to three labels 'sigmoid' - change tooltip which shows the incompatibility with 5.11

* Forgotten correction suggested by Lawrence

* Compatibility 5.11 log encoding - sigmoid part 1

* Compatibility 5.11 part 2

* Compatibility 5.11 - step 3

* Compatibility 5.11 - step 4

* Compatibility 5.11 step xx

* Compatibility 5.11 - combobox operators Q and J

* Compatibility 5.11 Cam16 GUI first part

* Improve GUI Cam16 sigmoid compatibility

* Compatibility 5.11 Jz - sigmoid - step 1

* Compatibility 5.11 Jz gui step 2

* Compatibility 5.11 Jz GUI step x

* Compatibility 5.11 Jz - history - etc.

* Various change labels - history ...

* Improve GUI - hide show 5.11 5.12

* Jz 5.11 in iplocallab - step 1

* Compatibility 5.11 iplocallab cam16 step 1

* Improve GUI hide show 511 512

* Solved - I hope - GUI problem with tone mapper Q and J 5.11 and 5.12

* Compatibility 5.11 iplocallab Cam16 step 2

* Improve GUI compatibility 5.11 labels tooltips

* Small improvments GUI - labels - history...

* Fixed typo in paramsedited.cc clcurve issue 7283

* Change tooltips method 5.12 - 5.11 for cam16 and Jz  brightness Q or J

* Clean and refine code

* Various change dafult language  and CAM16 CAM02 replace by Cam16 Cam02

* Change modeQJ method for 5.11 in function ppversion

* Change labels as suggested by Wayne PR 7111

* Others changes suggested for label

* Change tooltips as suggested in PR

* Use unique pointer instead of manual management

* Update rtdata/languages/default

Co-authored-by: Lawrence37 <45837045+Lawrence37@users.noreply.github.com>

* Change all Cam16 references to CAM16

* Change convention uppercase and lowercase in frame - checkbox

* Improve tooltips for Tone Mapping Operators

* Another change CIECAM and uppercase lowercase in checkbox

* Remove appimage and windows yml

---------

Co-authored-by: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
2025-01-19 07:52:32 +01:00

2372 lines
99 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <glibmm/thread.h>
#include <glibmm/ustring.h>
#include "cieimage.h"
#include "clutstore.h"
#include "color.h"
#include "colortemp.h"
#include "curves.h"
#include "dcp.h"
#include "guidedfilter.h"
#include "iccstore.h"
#include "imagefloat.h"
#include "imagesource.h"
#include "improcfun.h"
#include "labimage.h"
#include "metadata.h"
#include "mytime.h"
#include "processingjob.h"
#include "procparams.h"
#include "rawimagesource.h"
#include "rtengine.h"
#include "utils.h"
#include "rtgui/multilangmgr.h"
#include "rtgui/options.h"
#undef THREAD_PRIORITY_NORMAL
namespace rtengine
{
namespace
{
template <typename T>
void adjust_radius(const T &default_param, double scale_factor, T &param)
{
const double delta = (param - default_param) * scale_factor;
param = default_param + delta;
}
class ImageProcessor
{
public:
ImageProcessor(
ProcessingJob* pjob,
int& errorCode,
ProgressListener* pl,
bool flush
) :
job(static_cast<ProcessingJobImpl*>(pjob)),
errorCode(errorCode),
pl(pl),
flush(flush),
// internal state
initialImage(nullptr),
imgsrc(nullptr),
fw(0),
fh(0),
tr(0),
pp(0, 0, 0, 0, 0),
calclum(nullptr),
autoNR(0.f),
autoNRmax(0.f),
tilesize(0),
overlap(0),
ch_M(nullptr),
max_r(nullptr),
max_b(nullptr),
min_b(nullptr),
min_r(nullptr),
lumL(nullptr),
chromC(nullptr),
ry(nullptr),
sk(nullptr),
pcsk(nullptr),
expcomp(0.0),
bright(0),
contr(0),
black(0),
hlcompr(0),
hlcomprthresh(0),
baseImg(nullptr),
labView(nullptr),
ctColorCurve(),
autili(false),
butili(false)
{
}
Imagefloat *operator()()
{
if (!job->fast) {
return normal_pipeline();
} else {
return fast_pipeline();
}
}
private:
Imagefloat *normal_pipeline()
{
if (!stage_init()) {
return nullptr;
}
stage_denoise();
stage_transform();
return stage_finish();
}
Imagefloat *fast_pipeline()
{
if (!job->pparams.resize.enabled) {
return normal_pipeline();
}
pl = nullptr;
if (!stage_init()) {
return nullptr;
}
stage_transform();
stage_early_resize();
stage_denoise();
return stage_finish();
}
bool stage_init()
{
errorCode = 0;
if (pl) {
pl->setProgressStr("PROGRESSBAR_PROCESSING");
pl->setProgress(0.0);
}
initialImage = job->initialImage;
if (!initialImage) {
initialImage = InitialImage::load(job->fname, job->isRaw, &errorCode);
if (errorCode) {
delete job;
return false; //return nullptr;
}
}
procparams::ProcParams& params = job->pparams;
// acquire image from imagesource
imgsrc = initialImage->getImageSource();
tr = getCoarseBitMask(params.coarse);
if (imgsrc->getSensorType() == ST_BAYER) {
if (params.raw.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT)) {
imgsrc->setBorder(params.raw.bayersensor.border);
} else {
imgsrc->setBorder(std::max(params.raw.bayersensor.border, 2));
}
} else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
imgsrc->setBorder(params.raw.xtranssensor.border);
}
imgsrc->getFullSize(fw, fh, tr);
// check the crop params
if (params.crop.x > fw || params.crop.y > fh) {
// the crop is completely out of the image, so we disable the crop
params.crop.enabled = false;
// and we set the values to the defaults
params.crop.x = 0;
params.crop.y = 0;
params.crop.w = fw;
params.crop.h = fh;
} else {
if (params.crop.x < 0) {
params.crop.x = 0;
}
if (params.crop.y < 0) {
params.crop.y = 0;
}
if ((params.crop.x + params.crop.w) > fw) {
// crop overflow in the width dimension ; we trim it
params.crop.w = fw - params.crop.x;
}
if ((params.crop.y + params.crop.h) > fh) {
// crop overflow in the height dimension ; we trim it
params.crop.h = fh - params.crop.y;
}
}
// MyTime t1,t2;
// t1.set();
ipf_p.reset(new ImProcFunctions(&params, true));
ImProcFunctions &ipf = * (ipf_p.get());
imgsrc->setCurrentFrame(params.raw.bayersensor.imageNum);
float reddeha = 0.f;
float greendeha = 0.f;
float bluedeha = 0.f;
imgsrc->preprocess(params.raw, params.lensProf, params.coarse, reddeha, greendeha, bluedeha, params.dirpyrDenoise.enabled);
if (pl) {
pl->setProgress(0.20);
}
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, params.pdsharpening.enabled && pl);
if (params.pdsharpening.enabled) {
imgsrc->captureSharpening(params.pdsharpening, false, params.pdsharpening.contrast, params.pdsharpening.deconvradius);
}
if (pl) {
pl->setProgress(0.30);
}
pp = PreviewProps(0, 0, fw, fh, 1);
if (params.retinex.enabled) { //enabled Retinex
LUTf cdcurve(65536, 0);
LUTf mapcurve(65536, 0);
RetinextransmissionCurve dehatransmissionCurve;
RetinexgaintransmissionCurve dehagaintransmissionCurve;
bool dehacontlutili = false;
bool mapcontlutili = false;
bool useHsl = false;
multi_array2D<float, 4> conversionBuffer(1, 1);
imgsrc->retinexPrepareBuffers(params.icm, params.retinex, conversionBuffer, dummy);
imgsrc->retinexPrepareCurves(params.retinex, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, dehacontlutili, mapcontlutili, useHsl, dummy, dummy);
float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax;
imgsrc->retinex(params.icm, params.retinex, params.toneCurve, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, conversionBuffer, dehacontlutili, mapcontlutili, useHsl, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, dummy);
}
if (pl) {
pl->setProgress(0.40);
}
// imgsrc->HLRecovery_Global(params.toneCurve);
if (pl) {
pl->setProgress(0.45);
}
// set the color temperature
currWB = ColorTemp(params.wb.temperature, params.wb.green, params.wb.equal, params.wb.method, params.wb.observer);
ColorTemp currWBitc;
if (params.wb.method == "autitcgreen" && flush) {
imgsrc->getrgbloc(0, 0, fh, fw, 0, 0, fh, fw, params.wb);
}
const bool autowb = (params.wb.method == "autitcgreen" && imgsrc->isRAW() && flush);
ColorTemp autoWB;
int dread = 0;
int bia = 1;
float studgood = 1000.f;
int nocam = 0;
int kcam = 0;
float minchrom = 1000.f;
float delta = 0.f;
int kmin = 20;
float minhist = 1000000000.f;
float maxhist = -1000.f;
double greenitc = 1.;
float temp0 = 5000.f;
bool extra = false;
bool forcewbgrey = false;
if (!params.wb.enabled) {
currWB = ColorTemp();
} else if (params.wb.method == "Camera" || (params.wb.method == "autitcgreen" && params.wb.compat_version >= 2 && !imgsrc->isRAW() && flush)) {//Use also Camera settings for Temperature correlation and TIF/Jpg
currWB = imgsrc->getWB();
} else if (params.wb.method == "autold") {//for Auto RGB
double rm, gm, bm;
if (params.wb.compat_version == 1 && !imgsrc->isRAW()) {
// RGB grey compatibility version 1 used the identity
// multipliers plus temperature bias for non-raw files.
rm = gm = bm = 1.;
} else {
imgsrc->getAutoWBMultipliers(rm, gm, bm);
}
currWB.update(rm, gm, bm, params.wb.equal, params.wb.observer, params.wb.tempBias);
} else if (autowb) {//for auto Itcwb - flush to enable only when batch only with Raw files
//code similar to that present in improccoordinator.cc
double rm;
double gm;
double bm;
imgsrc->getAutoWBMultipliersItcGreen(
params,
forcewbgrey,
kcam,
greenitc,
extra,
temp0,
delta,
bia,
dread,
nocam,
studgood,
minchrom,
kmin,
minhist,
maxhist,
fh,
fw,
currWB,
0,
0.,
false,
autoWB,
rm,
gm,
bm);
currWB = autoWB;
} else if (params.wb.method == "autitcgreen" && params.wb.compat_version == 1 && !imgsrc->isRAW() && flush) {
// ITCWB compatibility version 1 used 5000 K and observer 10 degrees
// for non-raw files.
currWB = ColorTemp(5000., 1., 1., params.wb.method, StandardObserver::TEN_DEGREES);
currWB.convertObserver(params.wb.observer);
params.wb.temperature = currWB.getTemp();
params.wb.green = currWB.getGreen();
params.wb.equal = currWB.getEqual();
}
//end WB auto
calclum = nullptr ;
params.dirpyrDenoise.getCurves(noiseLCurve, noiseCCurve);
autoNR = (float) settings->nrauto;//
autoNRmax = (float) settings->nrautomax;//
if (settings->leveldnti == 0) {
tilesize = 1024;
overlap = 128;
}
if (settings->leveldnti == 1) {
tilesize = 768;
overlap = 96;
}
// const int tilesize = 768;
// const int overlap = 96;
int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip;
ipf.Tile_calc(tilesize, overlap, 2, fw, fh, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip);
int nbtl = numtiles_W * numtiles_H;
if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) {
nbtl = 9;
}
ch_M = new float [nbtl];//allocate memory
max_r = new float [nbtl];
max_b = new float [nbtl];
min_b = new float [9];
min_r = new float [9];
lumL = new float [nbtl];
chromC = new float [nbtl];
ry = new float [nbtl];
sk = new float [nbtl];
pcsk = new float [nbtl];
// printf("expert=%d\n",settings->leveldnautsimpl);
if (settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "PON") {
MyTime t1pone, t2pone;
t1pone.set();
int crW = 100; // settings->leveldnv == 0
int crH = 100; // settings->leveldnv == 0
if (settings->leveldnv == 1) {
crW = 250;
crH = 250;
}
if (settings->leveldnv == 2) {
crW = int (tileWskip / 2);
crH = int (tileHskip / 2);
}
// if(settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int(1.15f*(tileWskip/2));}//adapted to scale of preview
if (settings->leveldnv == 3) {
crW = tileWskip - 10;
crH = tileHskip - 10;
}
float lowdenoise = 1.f;
int levaut = settings->leveldnaut;
if (levaut == 1) { //Standard
lowdenoise = 0.7f;
}
// int crW=tileWskip-10;//crop noise width
// int crH=tileHskip-10;//crop noise height
// Imagefloat *origCropPart;//init auto noise
// origCropPart = new Imagefloat (crW, crH);//allocate memory
if (params.dirpyrDenoise.enabled) {//evaluate Noise
LUTf gamcurve(65536, 0);
float gam, gamthresh, gamslope;
ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope);
#ifdef _OPENMP
#pragma omp parallel
#endif
{
Imagefloat *origCropPart;//init auto noise
origCropPart = new Imagefloat(crW, crH); //allocate memory
Imagefloat *provicalc = new Imagefloat((crW + 1) / 2, (crH + 1) / 2); //for denoise curves
int skipP = 1;
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2) nowait
#endif
for (int wcr = 0; wcr < numtiles_W; wcr++) {
for (int hcr = 0; hcr < numtiles_H; hcr++) {
int beg_tileW = wcr * tileWskip + tileWskip / 2.f - crW / 2.f;
int beg_tileH = hcr * tileHskip + tileHskip / 2.f - crH / 2.f;
PreviewProps ppP(beg_tileW, beg_tileH, crW, crH, skipP);
imgsrc->getImage(currWB, tr, origCropPart, ppP, params.toneCurve, params.raw);
//baseImg->getStdImage(currWB, tr, origCropPart, ppP, true, params.toneCurve);
// we only need image reduced to 1/4 here
for (int ii = 0; ii < crH; ii += 2) {
for (int jj = 0; jj < crW; jj += 2) {
provicalc->r(ii >> 1, jj >> 1) = origCropPart->r(ii, jj);
provicalc->g(ii >> 1, jj >> 1) = origCropPart->g(ii, jj);
provicalc->b(ii >> 1, jj >> 1) = origCropPart->b(ii, jj);
}
}
imgsrc->convertColorSpace(provicalc, params.icm, currWB); //for denoise luminance curve
float maxr = 0.f;
float maxb = 0.f;
float pondcorrec = 1.0f;
float chaut, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc;
int Nb;
chaut = 0.f;
redaut = 0.f;
blueaut = 0.f;
maxredaut = 0.f;
maxblueaut = 0.f;
chromina = 0.f;
sigma = 0.f;
ipf.RGB_denoise_info(origCropPart, provicalc, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc);
float multip = 1.f;
float adjustr = 1.f;
if (params.icm.workingProfile == "ProPhoto") {
adjustr = 1.f; //
} else if (params.icm.workingProfile == "Adobe RGB") {
adjustr = 1.f / 1.3f;
} else if (params.icm.workingProfile == "sRGB") {
adjustr = 1.f / 1.3f;
} else if (params.icm.workingProfile == "WideGamut") {
adjustr = 1.f / 1.1f;
} else if (params.icm.workingProfile == "Rec2020") {
adjustr = 1.f / 1.1f;
} else if (params.icm.workingProfile == "Beta RGB") {
adjustr = 1.f / 1.2f;
} else if (params.icm.workingProfile == "BestRGB") {
adjustr = 1.f / 1.2f;
} else if (params.icm.workingProfile == "BruceRGB") {
adjustr = 1.f / 1.2f;
}
if (!imgsrc->isRAW()) {
multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good for gamma=1
}
float maxmax = max(maxredaut, maxblueaut);
float delta;
int mode = 2;
int lissage = settings->leveldnliss;
ipf.calcautodn_info(chaut, delta, Nb, levaut, maxmax, lumema, chromina, mode, lissage, redyel, skinc, nsknc);
// printf("PROCESS cha=%f red=%f bl=%f redM=%f bluM=%f chrom=%f sigm=%f lum=%f sigL=%f\n",chaut,redaut,blueaut, maxredaut, maxblueaut, chromina, sigma, lumema, sigma_L);
if (maxredaut > maxblueaut) {
maxr = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
if (minblueaut <= minredaut && minblueaut < chaut) {
maxb = (-chaut + minblueaut) / (autoNRmax * multip * adjustr * lowdenoise);
}
} else {
maxb = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
if (minredaut <= minblueaut && minredaut < chaut) {
maxr = (-chaut + minredaut) / (autoNRmax * multip * adjustr * lowdenoise);
}
}//maxb mxr - empirical evaluation red / blue
ch_M[hcr * numtiles_W + wcr] = pondcorrec * chaut / (autoNR * multip * adjustr * lowdenoise);
max_r[hcr * numtiles_W + wcr] = pondcorrec * maxr;
max_b[hcr * numtiles_W + wcr] = pondcorrec * maxb;
lumL[hcr * numtiles_W + wcr] = lumema;
chromC[hcr * numtiles_W + wcr] = chromina;
ry[hcr * numtiles_W + wcr] = redyel;
sk[hcr * numtiles_W + wcr] = skinc;
pcsk[hcr * numtiles_W + wcr] = nsknc;
}
}
delete provicalc;
delete origCropPart;
}
int liss = settings->leveldnliss; //smooth result around mean
if (liss == 2 || liss == 3) {
// I smooth only mean and not delta (max)
float nchm = 0.f;
float koef = 0.4f; //between 0.1 to 0.9
if (liss == 3) {
koef = 0.0f; //quasi auto for mean Ch
}
for (int wcr = 0; wcr < numtiles_W; wcr++) {
for (int hcr = 0; hcr < numtiles_H; hcr++) {
nchm += ch_M[hcr * numtiles_W + wcr];
}
}
nchm /= (numtiles_H * numtiles_W);
for (int wcr = 0; wcr < numtiles_W; wcr++) {
for (int hcr = 0; hcr < numtiles_H; hcr++) {
ch_M[hcr * numtiles_W + wcr] = nchm + (ch_M[hcr * numtiles_W + wcr] - nchm) * koef;
}
}
}
if (liss == 3) { //same as auto but with much cells
float MaxR = 0.f;
float MaxB = 0.f;
float MaxRMoy = 0.f;
float MaxBMoy = 0.f;
for (int k = 0; k < nbtl; k++) {
MaxBMoy += max_b[k];
MaxRMoy += max_r[k];
if (max_r[k] > MaxR) {
MaxR = max_r[k];
}
if (max_b[k] > MaxB) {
MaxB = max_b[k];
}
}
MaxBMoy /= nbtl;
MaxRMoy /= nbtl;
for (int k = 0; k < nbtl; k++) {
if (MaxR > MaxB) {
max_r[k] = MaxRMoy + (MaxR - MaxRMoy) * 0.66f; //#std Dev
//max_b[k]=MinB;
max_b[k] = MaxBMoy + (MaxB - MaxBMoy) * 0.66f;
} else {
max_b[k] = MaxBMoy + (MaxB - MaxBMoy) * 0.66f;
//max_r[k]=MinR;
max_r[k] = MaxRMoy + (MaxR - MaxRMoy) * 0.66f;
}
}
}
if (settings->verbose) {
t2pone.set();
printf("Info denoise ponderated performed in %d usec:\n", t2pone.etime(t1pone));
}
}
}
if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) {
MyTime t1aue, t2aue;
t1aue.set();
int crW, crH;
if (settings->leveldnv == 0) {
crW = 100;
crH = 100;
}
if (settings->leveldnv == 1) {
crW = 250;
crH = 250;
}
if (settings->leveldnv == 2) {
crW = int (tileWskip / 2);
crH = int (tileHskip / 2);
}
// if(settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int(1.15f*(tileWskip/2));}//adapted to scale of preview
if (settings->leveldnv == 3) {
crW = tileWskip - 10;
crH = tileHskip - 10;
}
float lowdenoise = 1.f;
int levaut = settings->leveldnaut;
if (levaut == 1) { //Standard
lowdenoise = 0.7f;
}
if (params.dirpyrDenoise.enabled) {//evaluate Noise
LUTf gamcurve(65536, 0);
float gam, gamthresh, gamslope;
ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope);
int Nb[9];
int coordW[3];//coordinate of part of image to measure noise
int coordH[3];
int begW = 50;
int begH = 50;
coordW[0] = begW;
coordW[1] = fw / 2 - crW / 2;
coordW[2] = fw - crW - begW;
coordH[0] = begH;
coordH[1] = fh / 2 - crH / 2;
coordH[2] = fh - crH - begH;
#ifdef _OPENMP
#pragma omp parallel
#endif
{
Imagefloat *origCropPart;//init auto noise
origCropPart = new Imagefloat(crW, crH); //allocate memory
Imagefloat *provicalc = new Imagefloat((crW + 1) / 2, (crH + 1) / 2); //for denoise curves
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2) nowait
#endif
for (int wcr = 0; wcr <= 2; wcr++) {
for (int hcr = 0; hcr <= 2; hcr++) {
PreviewProps ppP(coordW[wcr], coordH[hcr], crW, crH, 1);
imgsrc->getImage(currWB, tr, origCropPart, ppP, params.toneCurve, params.raw);
//baseImg->getStdImage(currWB, tr, origCropPart, ppP, true, params.toneCurve);
// we only need image reduced to 1/4 here
for (int ii = 0; ii < crH; ii += 2) {
for (int jj = 0; jj < crW; jj += 2) {
provicalc->r(ii >> 1, jj >> 1) = origCropPart->r(ii, jj);
provicalc->g(ii >> 1, jj >> 1) = origCropPart->g(ii, jj);
provicalc->b(ii >> 1, jj >> 1) = origCropPart->b(ii, jj);
}
}
imgsrc->convertColorSpace(provicalc, params.icm, currWB); //for denoise luminance curve
int nb = 0;
float chaut = 0.f, redaut = 0.f, blueaut = 0.f, maxredaut = 0.f, maxblueaut = 0.f, minredaut = 0.f, minblueaut = 0.f, chromina = 0.f, sigma = 0.f, lumema = 0.f, sigma_L = 0.f, redyel = 0.f, skinc = 0.f, nsknc = 0.f;
ipf.RGB_denoise_info(origCropPart, provicalc, imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, imgsrc->getDirPyrDenoiseExpComp(), chaut, nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc);
Nb[hcr * 3 + wcr] = nb;
ch_M[hcr * 3 + wcr] = chaut;
max_r[hcr * 3 + wcr] = maxredaut;
max_b[hcr * 3 + wcr] = maxblueaut;
min_r[hcr * 3 + wcr] = minredaut;
min_b[hcr * 3 + wcr] = minblueaut;
lumL[hcr * 3 + wcr] = lumema;
chromC[hcr * 3 + wcr] = chromina;
ry[hcr * 3 + wcr] = redyel;
sk[hcr * 3 + wcr] = skinc;
pcsk[hcr * 3 + wcr] = nsknc;
}
}
delete provicalc;
delete origCropPart;
}
float chM = 0.f;
float MaxR = 0.f;
float MaxB = 0.f;
float MinR = 100000000.f;
float MinB = 100000000.f;
float maxr = 0.f;
float maxb = 0.f;
float multip = 1.f;
float adjustr = 1.f;
float Max_R[9] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
float Max_B[9] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
float Min_R[9];
float Min_B[9];
float MaxRMoy = 0.f;
float MaxBMoy = 0.f;
float MinRMoy = 0.f;
float MinBMoy = 0.f;
if (params.icm.workingProfile == "ProPhoto") {
adjustr = 1.f;
} else if (params.icm.workingProfile == "Adobe RGB") {
adjustr = 1.f / 1.3f;
} else if (params.icm.workingProfile == "sRGB") {
adjustr = 1.f / 1.3f;
} else if (params.icm.workingProfile == "WideGamut") {
adjustr = 1.f / 1.1f;
} else if (params.icm.workingProfile == "Rec2020") {
adjustr = 1.f / 1.1f;
} else if (params.icm.workingProfile == "Beta RGB") {
adjustr = 1.f / 1.2f;
} else if (params.icm.workingProfile == "BestRGB") {
adjustr = 1.f / 1.2f;
} else if (params.icm.workingProfile == "BruceRGB") {
adjustr = 1.f / 1.2f;
}
if (!imgsrc->isRAW()) {
multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good for gamma=1
}
float delta[9];
int mode = 1;
int lissage = settings->leveldnliss;
for (int k = 0; k < 9; k++) {
float maxmax = max(max_r[k], max_b[k]);
ipf.calcautodn_info(ch_M[k], delta[k], Nb[k], levaut, maxmax, lumL[k], chromC[k], mode, lissage, ry[k], sk[k], pcsk[k]);
// printf("ch_M=%f delta=%f\n",ch_M[k], delta[k]);
}
for (int k = 0; k < 9; k++) {
if (max_r[k] > max_b[k]) {
//printf("R delta=%f koef=%f\n",delta[k],autoNRmax*multip*adjustr*lowdenoise);
Max_R[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
Min_B[k] = - (ch_M[k] - min_b[k]) / (autoNRmax * multip * adjustr * lowdenoise);
Max_B[k] = 0.f;
Min_R[k] = 0.f;
} else {
//printf("B delta=%f koef=%f\n",delta[k],autoNRmax*multip*adjustr*lowdenoise);
Max_B[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
Min_R[k] = - (ch_M[k] - min_r[k]) / (autoNRmax * multip * adjustr * lowdenoise);
Min_B[k] = 0.f;
Max_R[k] = 0.f;
}
}
for (int k = 0; k < 9; k++) {
// printf("ch_M= %f Max_R=%f Max_B=%f min_r=%f min_b=%f\n",ch_M[k],Max_R[k], Max_B[k],Min_R[k], Min_B[k]);
chM += ch_M[k];
MaxBMoy += Max_B[k];
MaxRMoy += Max_R[k];
MinRMoy += Min_R[k];
MinBMoy += Min_B[k];
if (Max_R[k] > MaxR) {
MaxR = Max_R[k];
}
if (Max_B[k] > MaxB) {
MaxB = Max_B[k];
}
if (Min_R[k] < MinR) {
MinR = Min_R[k];
}
if (Min_B[k] < MinB) {
MinB = Min_B[k];
}
}
chM /= 9;
MaxBMoy /= 9;
MaxRMoy /= 9;
MinBMoy /= 9;
MinRMoy /= 9;
if (MaxR > MaxB) {
maxr = MaxRMoy + (MaxR - MaxRMoy) * 0.66f; //#std Dev
// maxb=MinB;
maxb = MinBMoy + (MinB - MinBMoy) * 0.66f;
} else {
maxb = MaxBMoy + (MaxB - MaxBMoy) * 0.66f;
// maxr=MinR;
maxr = MinRMoy + (MinR - MinRMoy) * 0.66f;
}
// printf("SIMPL cha=%f red=%f bl=%f \n",chM,maxr,maxb);
params.dirpyrDenoise.chroma = chM / (autoNR * multip * adjustr);
params.dirpyrDenoise.redchro = maxr;
params.dirpyrDenoise.bluechro = maxb;
}
if (settings->verbose) {
t2aue.set();
printf("Info denoise auto performed in %d usec:\n", t2aue.etime(t1aue));
}
//end evaluate noise
}
baseImg = new Imagefloat(fw, fh);
imgsrc->getImage(currWB, tr, baseImg, pp, params.toneCurve, params.raw);
if (pl) {
pl->setProgress(0.50);
}
// LUTf Noisecurve (65536,0);
//!!!// auto exposure!!!
expcomp = params.toneCurve.expcomp;
bright = params.toneCurve.brightness;
contr = params.toneCurve.contrast;
black = params.toneCurve.black;
hlcompr = params.toneCurve.hlcompr;
hlcomprthresh = params.toneCurve.hlcomprthresh;
if (params.toneCurve.autoexp) {
LUTu aehist;
int aehistcompr;
imgsrc->getAutoExpHistogram(aehist, aehistcompr);
ipf.getAutoExp(aehist, aehistcompr, params.toneCurve.clip, expcomp, bright, contr, black, hlcompr, hlcomprthresh);
}
if (params.toneCurve.histmatching) {
if (!params.toneCurve.fromHistMatching) {
imgsrc->getAutoMatchedToneCurve(params.icm, params.raw, params.wb.observer, params.toneCurve.curve);
}
if (params.toneCurve.autoexp) {
params.toneCurve.expcomp = 0.0;
}
params.toneCurve.autoexp = false;
params.toneCurve.curveMode = ToneCurveMode::FILMLIKE;
params.toneCurve.curve2 = { 0 };
params.toneCurve.brightness = 0;
params.toneCurve.contrast = 0;
params.toneCurve.black = 0;
}
// Spot Removal
if (params.spot.enabled && !params.spot.entries.empty()) {
ipf.removeSpots(baseImg, imgsrc, params.spot.entries, pp, currWB, nullptr, tr);
}
// at this stage, we can flush the raw data to free up quite an important amount of memory
// commented out because it makes the application crash when batch processing...
// TODO: find a better place to flush rawData and rawRGB
if (flush) {
imgsrc->flush();
}
return true;
}
void stage_denoise()
{
const procparams::ProcParams& params = job->pparams;
DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise; // make a copy because we cheat here
if (denoiseParams.Lmethod == "CUR") {
if (noiseLCurve) {
denoiseParams.luma = 0.5f;
} else {
denoiseParams.luma = 0.0f;
}
} else if (denoiseParams.Lmethod == "SLI") {
noiseLCurve.Reset();
}
if (denoiseParams.enabled && (noiseLCurve || noiseCCurve)) {
// we only need image reduced to 1/4 here
calclum = new Imagefloat((fw + 1) / 2, (fh + 1) / 2); //for luminance denoise curve
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ii = 0; ii < fh; ii += 2) {
for (int jj = 0; jj < fw; jj += 2) {
calclum->r(ii >> 1, jj >> 1) = baseImg->r(ii, jj);
calclum->g(ii >> 1, jj >> 1) = baseImg->g(ii, jj);
calclum->b(ii >> 1, jj >> 1) = baseImg->b(ii, jj);
}
}
imgsrc->convertColorSpace(calclum, params.icm, currWB);
}
if (denoiseParams.enabled) {
ImProcFunctions &ipf = * (ipf_p.get());
float nresi, highresi;
int kall = 2;
ipf.RGB_denoise(kall, baseImg, baseImg, calclum, ch_M, max_r, max_b, imgsrc->isRAW(), denoiseParams, imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, nresi, highresi);
}
// delete calclum;
delete [] ch_M;
delete [] max_r;
delete [] max_b;
delete [] min_r;
delete [] min_b;
delete [] lumL;
delete [] chromC;
delete [] ry;
delete [] sk;
delete [] pcsk;
}
void stage_transform()
{
const procparams::ProcParams& params = job->pparams;
//ImProcFunctions ipf (&params, true);
ImProcFunctions &ipf = * (ipf_p.get());
if (params.filmNegative.enabled) {
// Process film negative AFTER colorspace conversion if camera space is NOT selected
if (params.filmNegative.colorSpace != FilmNegativeParams::ColorSpace::INPUT) {
imgsrc->convertColorSpace(baseImg, params.icm, currWB);
}
FilmNegativeParams copy = params.filmNegative;
ipf.filmNegativeProcess(baseImg, baseImg, copy, params.raw, imgsrc, currWB);
// ... otherwise, process film negative BEFORE colorspace conversion
if (params.filmNegative.colorSpace == FilmNegativeParams::ColorSpace::INPUT) {
imgsrc->convertColorSpace(baseImg, params.icm, currWB);
}
} else {
imgsrc->convertColorSpace(baseImg, params.icm, currWB);
}
// perform first analysis
hist16(65536);
if (params.cg.enabled) {//gamut compression
ipf.gamutcompr(baseImg, baseImg);
}
ipf.firstAnalysis(baseImg, params, hist16);
ipf.dehaze(baseImg, params.dehaze);
ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0, false);
// perform transform (excepted resizing)
if (ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData())) {
Imagefloat* trImg = nullptr;
if (ipf.needsLuminanceOnly()) {
trImg = baseImg;
} else {
trImg = new Imagefloat(fw, fh);
}
ipf.transform(baseImg, trImg, 0, 0, 0, 0, fw, fh, fw, fh,
imgsrc->getMetaData(), imgsrc->getRotateDegree(), true, true);
if (trImg != baseImg) {
delete baseImg;
baseImg = trImg;
}
}
}
Imagefloat *stage_finish()
{
procparams::ProcParams& params = job->pparams;
//ImProcFunctions ipf (&params, true);
ImProcFunctions &ipf = * (ipf_p.get());
for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) {
if (params.locallab.spots.at(sp).expsharp && params.dirpyrequalizer.cbdlMethod == "bef") {
if (params.locallab.spots.at(sp).shardamping < 1) {
params.locallab.spots.at(sp).shardamping = 1;
}
}
}
bool execcam = false;
//execcam => work around for pre-ciecam in LA: about 0.1 second
for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) {
if (params.locallab.spots.at(sp).expprecam) {
execcam = true;
}
}
if ((params.dirpyrequalizer.cbdlMethod == "bef") && (params.dirpyrequalizer.enabled || execcam) && !params.colorappearance.enabled) {
if (execcam && !params.dirpyrequalizer.enabled) {
params.dirpyrequalizer.enabled = true;
if (params.dirpyrequalizer.mult[0] == 1.) {
params.dirpyrequalizer.mult[0] = 1.01;
}
}
const int W = baseImg->getWidth();
const int H = baseImg->getHeight();
LabImage labcbdl(W, H);
ipf.rgb2lab(*baseImg, labcbdl, params.icm.workingProfile);
ipf.dirpyrequalizer(&labcbdl, 1);
ipf.lab2rgb(labcbdl, *baseImg, params.icm.workingProfile);
}
// RGB processing
labView = new LabImage(fw, fh);
if (params.locallab.enabled && params.locallab.spots.size() > 0) {
ipf.rgb2lab(*baseImg, *labView, params.icm.workingProfile);
MyTime t1, t2;
t1.set();
const std::unique_ptr<LabImage> reservView(new LabImage(*labView, true));
const std::unique_ptr<LabImage> lastorigView(new LabImage(*labView, true));
std::unique_ptr<LabImage> savenormtmView;
std::unique_ptr<LabImage> savenormretiView;
LocretigainCurve locRETgainCurve;
LocretitransCurve locRETtransCurve;
LocLHCurve loclhCurve;
LocHHCurve lochhCurve;
LocCHCurve locchCurve;
LocHHCurve lochhCurvejz;
LocCHCurve locchCurvejz;
LocLHCurve loclhCurvejz;
LocCCmaskCurve locccmasCurve;
LocLLmaskCurve locllmasCurve;
LocHHmaskCurve lochhmasCurve;
LocHHmaskCurve lochhhmasCurve;
LocHHmaskCurve lochhhmascieCurve;
LocCCmaskCurve locccmasexpCurve;
LocLLmaskCurve locllmasexpCurve;
LocHHmaskCurve lochhmasexpCurve;
LocCCmaskCurve locccmasSHCurve;
LocLLmaskCurve locllmasSHCurve;
LocHHmaskCurve lochhmasSHCurve;
LocCCmaskCurve locccmasvibCurve;
LocLLmaskCurve locllmasvibCurve;
LocHHmaskCurve lochhmasvibCurve;
LocCCmaskCurve locccmaslcCurve;
LocLLmaskCurve locllmaslcCurve;
LocHHmaskCurve lochhmaslcCurve;
LocCCmaskCurve locccmascbCurve;
LocLLmaskCurve locllmascbCurve;
LocHHmaskCurve lochhmascbCurve;
LocCCmaskCurve locccmasretiCurve;
LocLLmaskCurve locllmasretiCurve;
LocHHmaskCurve lochhmasretiCurve;
LocCCmaskCurve locccmastmCurve;
LocLLmaskCurve locllmastmCurve;
LocHHmaskCurve lochhmastmCurve;
LocCCmaskCurve locccmasblCurve;
LocLLmaskCurve locllmasblCurve;
LocHHmaskCurve lochhmasblCurve;
LocCCmaskCurve locccmaslogCurve;
LocLLmaskCurve locllmaslogCurve;
LocHHmaskCurve lochhmaslogCurve;
LocCCmaskCurve locccmascieCurve;
LocLLmaskCurve locllmascieCurve;
LocHHmaskCurve lochhmascieCurve;
LocCCmaskCurve locccmas_Curve;
LocLLmaskCurve locllmas_Curve;
LocHHmaskCurve lochhmas_Curve;
LocHHmaskCurve lochhhmas_Curve;
LocwavCurve loclmasCurveblwav;
LocwavCurve loclmasCurvecolwav;
LocwavCurve loclmasCurveciewav;
LocwavCurve loclmasCurve_wav;
LocwavCurve locwavCurve;
LocwavCurve locwavCurvejz;
LocwavCurve loclevwavCurve;
LocwavCurve locconwavCurve;
LocwavCurve loccompwavCurve;
LocwavCurve loccomprewavCurve;
LocwavCurve locedgwavCurve;
LocwavCurve locwavCurvehue;
LocwavCurve locwavCurveden;
LUTf lllocalcurve(65536, LUT_CLIP_OFF);
LUTf lclocalcurve(65536, LUT_CLIP_OFF);
LUTf cllocalcurve(65536, LUT_CLIP_OFF);
LUTf cclocalcurve(65536, LUT_CLIP_OFF);
LUTf rgblocalcurve(65536, LUT_CLIP_OFF);
LUTf hltonecurveloc(65536, LUT_CLIP_OFF);
LUTf shtonecurveloc(65536, LUT_CLIP_OFF);
LUTf tonecurveloc(65536, LUT_CLIP_OFF);
LUTf lightCurveloc(32770, LUT_CLIP_OFF);
LUTf exlocalcurve(65536, LUT_CLIP_OFF);
LUTf lmasklocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskexplocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskSHlocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskviblocalcurve(65536, LUT_CLIP_OFF);
LUTf lmasktmlocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskretilocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskcblocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskbllocalcurve(65536, LUT_CLIP_OFF);
LUTf lmasklclocalcurve(65536, LUT_CLIP_OFF);
LUTf lmaskloglocalcurve(65536, LUT_CLIP_OFF);
LUTf lmasklocal_curve(65536, LUT_CLIP_OFF);
LUTf lmaskcielocalcurve(65536, LUT_CLIP_OFF);
LUTf cielocalcurve(65536, LUT_CLIP_OFF);
LUTf cielocalcurve2(65536, LUT_CLIP_OFF);
LUTf jzlocalcurve(65536, LUT_CLIP_OFF);
LUTf czlocalcurve(65536, LUT_CLIP_OFF);
LUTf czjzlocalcurve(65536, LUT_CLIP_OFF);
array2D<float> shbuffer;
for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) {
if (params.locallab.spots.at(sp).inverssha) {
shbuffer(fw, fh);
break;
}
}
for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) {
// Set local curves of current spot to LUT
locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve);
locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve);
const bool LHutili = loclhCurve.Set(params.locallab.spots.at(sp).LHcurve);
const bool HHutili = lochhCurve.Set(params.locallab.spots.at(sp).HHcurve);
const bool CHutili = locchCurve.Set(params.locallab.spots.at(sp).CHcurve);
const bool HHutilijz = lochhCurvejz.Set(params.locallab.spots.at(sp).HHcurvejz);
const bool CHutilijz = locchCurvejz.Set(params.locallab.spots.at(sp).CHcurvejz);
const bool LHutilijz = loclhCurvejz.Set(params.locallab.spots.at(sp).LHcurvejz);
const bool lcmasutili = locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve);
const bool llmasutili = locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve);
const bool lhmasutili = lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve);
const bool lhhmasutili = lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve);
const bool lhhmascieutili = lochhhmascieCurve.Set(params.locallab.spots.at(sp).HHhmaskciecurve);
const bool lcmasexputili = locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve);
const bool llmasexputili = locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve);
const bool lhmasexputili = lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve);
const bool lcmasSHutili = locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve);
const bool llmasSHutili = locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve);
const bool lhmasSHutili = lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve);
const bool lcmasvibutili = locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve);
const bool llmasvibutili = locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve);
const bool lhmasvibutili = lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve);
const bool lcmascbutili = locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve);
const bool llmascbutili = locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve);
const bool lhmascbutili = lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve);
const bool lcmasretiutili = locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve);
const bool llmasretiutili = locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve);
const bool lhmasretiutili = lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve);
const bool lcmastmutili = locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve);
const bool lhmaslcutili = lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve);
const bool llmastmutili = locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve);
const bool lhmastmutili = lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve);
const bool lcmasblutili = locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve);
const bool llmasblutili = locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve);
const bool lhmasblutili = lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve);
const bool lcmaslogutili = locccmaslogCurve.Set(params.locallab.spots.at(sp).CCmaskcurveL);
const bool llmaslogutili = locllmaslogCurve.Set(params.locallab.spots.at(sp).LLmaskcurveL);
const bool lhmaslogutili = lochhmaslogCurve.Set(params.locallab.spots.at(sp).HHmaskcurveL);
const bool lcmascieutili = locccmascieCurve.Set(params.locallab.spots.at(sp).CCmaskciecurve);
const bool llmascieutili = locllmascieCurve.Set(params.locallab.spots.at(sp).LLmaskciecurve);
const bool lhmascieutili = lochhmascieCurve.Set(params.locallab.spots.at(sp).HHmaskciecurve);
const bool lcmas_utili = locccmas_Curve.Set(params.locallab.spots.at(sp).CCmask_curve);
const bool llmas_utili = locllmas_Curve.Set(params.locallab.spots.at(sp).LLmask_curve);
const bool lhmas_utili = lochhmas_Curve.Set(params.locallab.spots.at(sp).HHmask_curve);
const bool lhhmas_utili = lochhhmas_Curve.Set(params.locallab.spots.at(sp).HHhmask_curve);
const bool lmasutiliblwav = loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav);
const bool lmasutilicolwav = loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav);
const bool lmasutiliciewav = loclmasCurveciewav.Set(params.locallab.spots.at(sp).LLmaskciecurvewav);
const bool lcmaslcutili = locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve);
const bool llmaslcutili = locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve);
const bool lmasutili_wav = loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav);
const bool locwavutili = locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve);
const bool locwavutilijz = locwavCurvejz.Set(params.locallab.spots.at(sp).locwavcurvejz);
const bool locwavhueutili = locwavCurvehue.Set(params.locallab.spots.at(sp).locwavcurvehue);
const bool locwavdenutili = locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden);
const bool loclevwavutili = loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve);
const bool locconwavutili = locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve);
const bool loccompwavutili = loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve);
const bool loccomprewavutili = loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve);
const bool locedgwavutili = locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve);
const bool locallutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).llcurve, lllocalcurve, 1);
const bool localclutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).clcurve, cllocalcurve, 1);
const bool locallcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).lccurve, lclocalcurve, 1);
const bool localcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).cccurve, cclocalcurve, 1);
const bool localrgbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).rgbcurve, rgblocalcurve, 1);
const bool localexutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).excurve, exlocalcurve, 1);
const bool localmaskutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, 1);
const bool localmaskexputili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, 1);
const bool localmaskSHutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, 1);
const bool localmaskvibutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, 1);
const bool localmasktmutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, 1);
const bool localmaskretiutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, 1);
const bool localmaskcbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, 1);
const bool localmaskblutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, 1);
const bool localmasklcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, 1);
const bool localmasklogutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskcurveL, lmaskloglocalcurve, 1);
const bool localmask_utili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmask_curve, lmasklocal_curve, 1);
const bool localmaskcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskciecurve, lmaskcielocalcurve, 1);
const bool localcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve, cielocalcurve, 1);
const bool localcieutili2 = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve2, cielocalcurve2, 1);
const bool localjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).jzcurve, jzlocalcurve, 1);
const bool localczutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czcurve, czlocalcurve, 1);
const bool localczjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czjzcurve, czjzlocalcurve, 1);
//provisory
double ecomp = params.locallab.spots.at(sp).expcomp;
double lblack = params.locallab.spots.at(sp).black;
double lhlcompr = params.locallab.spots.at(sp).hlcompr;
double lhlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh;
double shcompr = params.locallab.spots.at(sp).shcompr;
double br = params.locallab.spots.at(sp).lightness;
double cont = params.locallab.spots.at(sp).contrast;
if (lblack < 0. && params.locallab.spots.at(sp).expMethod == "pde") {
lblack *= 1.5;
}
float contsig = params.locallab.spots.at(sp).contsigqcie;
float lightsig = params.locallab.spots.at(sp).lightsigqcie;
// Reference parameters computation
double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre;
int lastsav;
float avge;
float meantme;
float stdtme;
float meanretie;
float stdretie;
float fab = 1.f;
float maxicam = -1000.f;
float rdx, rdy, grx, gry, blx, bly = 0.f;
float meanx, meany, meanxe, meanye = 0.f;
int ill = 2;
int prim = 3;
if (params.locallab.spots.at(sp).spotMethod == "exc") {
ipf.calc_ref(sp, reservView.get(), reservView.get(), 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
} else {
ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
}
CurveFactory::complexCurvelocal(ecomp, lblack / 65535., lhlcompr, lhlcomprthresh, shcompr, br, cont, lumare,
hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avge,
1);
float minCD;
float maxCD;
float mini;
float maxi;
float Tmean;
float Tsigma;
float Tmin;
float Tmax;
float highresi = 0.f;
float nresi = 0.f;
float highresi46 = 0.f;
float nresi46 = 0.f;
float Lhighresi = 0.f;
float Lnresi = 0.f;
float Lhighresi46 = 0.f;
float Lnresi46 = 0.f;
float slopeg = 1.f;
bool linkrgb = true;
// No Locallab mask is shown in exported picture
ipf.Lab_Local(2, sp, shbuffer, labView, labView, reservView.get(), savenormtmView.get(), savenormretiView.get(), lastorigView.get(), fw, fh, 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve,
lllocalcurve, locallutili,
cllocalcurve, localclutili,
lclocalcurve, locallcutili,
loclhCurve, lochhCurve, locchCurve,
lochhCurvejz, locchCurvejz, loclhCurvejz,
lmasklocalcurve, localmaskutili,
lmaskexplocalcurve, localmaskexputili,
lmaskSHlocalcurve, localmaskSHutili,
lmaskviblocalcurve, localmaskvibutili,
lmasktmlocalcurve, localmasktmutili,
lmaskretilocalcurve, localmaskretiutili,
lmaskcblocalcurve, localmaskcbutili,
lmaskbllocalcurve, localmaskblutili,
lmasklclocalcurve, localmasklcutili,
lmaskloglocalcurve, localmasklogutili,
lmasklocal_curve, localmask_utili,
lmaskcielocalcurve, localmaskcieutili,
cielocalcurve, localcieutili,
cielocalcurve2, localcieutili2,
jzlocalcurve, localjzutili,
czlocalcurve, localczutili,
czjzlocalcurve, localczjzutili,
locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, lochhhmascieCurve, lhhmascieutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili,
locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili,
locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili,
locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili,
locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili,
locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili,
locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili,
locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili,
locccmaslogCurve, lcmaslogutili, locllmaslogCurve, llmaslogutili, lochhmaslogCurve, lhmaslogutili,
locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili,
locccmascieCurve, lcmascieutili, locllmascieCurve, llmascieutili, lochhmascieCurve, lhmascieutili,
lochhhmas_Curve, lhhmas_utili,
loclmasCurveblwav, lmasutiliblwav,
loclmasCurvecolwav, lmasutilicolwav,
loclmasCurveciewav, lmasutiliciewav,
locwavCurve, locwavutili,
locwavCurvejz, locwavutilijz,
loclevwavCurve, loclevwavutili,
locconwavCurve, locconwavutili,
loccompwavCurve, loccompwavutili,
loccomprewavCurve, loccomprewavutili,
locwavCurvehue, locwavhueutili,
locwavCurveden, locwavdenutili,
locedgwavCurve, locedgwavutili,
loclmasCurve_wav, lmasutili_wav,
LHutili, HHutili, CHutili, HHutilijz, CHutilijz, LHutilijz, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc,
huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax,
meantme, stdtme, meanretie, stdretie, fab, maxicam, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, prim, ill, contsig, lightsig,
highresi, nresi, highresi46, nresi46, Lhighresi, Lnresi, Lhighresi46, Lnresi46, slopeg, linkrgb
);
if (sp + 1u < params.locallab.spots.size()) {
// do not copy for last spot as it is not needed anymore
lastorigView->CopyFrom(labView);
}
if (params.locallab.spots.at(sp).spotMethod == "exc") {
ipf.calc_ref(sp, reservView.get(), reservView.get(), 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
} else {
ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
}
}
t2.set();
ipf.lab2rgb(*labView, *baseImg, params.icm.workingProfile);
if (settings->verbose) {
printf("Total local:- %d usec\n", t2.etime(t1));
}
}
curve1(65536);
curve2(65536);
curve(65536, 0);
satcurve(65536, 0);
lhskcurve(65536, 0);
lumacurve(32770, 0); // lumacurve[32768] and lumacurve[32769] will be set to 32768 and 32769 later to allow linear interpolation
clcurve(65536, 0);
wavclCurve(65536, 0);
//if(params.blackwhite.enabled) params.toneCurve.hrenabled=false;
CurveFactory::complexCurve(expcomp, black / 65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr,
params.toneCurve.curve, params.toneCurve.curve2,
hist16, curve1, curve2, curve, dummy, customToneCurve1, customToneCurve2);
CurveFactory::RGBCurve(params.rgbCurves.rcurve, rCurve, 1);
CurveFactory::RGBCurve(params.rgbCurves.gcurve, gCurve, 1);
CurveFactory::RGBCurve(params.rgbCurves.bcurve, bCurve, 1);
bool opautili = false;
if (params.colorToning.enabled) {
TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(params.icm.workingProfile);
double wp[3][3] = {
{wprof[0][0], wprof[0][1], wprof[0][2]},
{wprof[1][0], wprof[1][1], wprof[1][2]},
{wprof[2][0], wprof[2][1], wprof[2][2]}
};
params.colorToning.getCurves(ctColorCurve, ctOpacityCurve, wp, opautili);
clToningcurve(65536, 0);
CurveFactory::diagonalCurve2Lut(params.colorToning.clcurve, clToningcurve, 1);
cl2Toningcurve(65536, 0);
CurveFactory::diagonalCurve2Lut(params.colorToning.cl2curve, cl2Toningcurve, 1);
}
// labView = new LabImage(fw, fh);
if (params.blackwhite.enabled) {
CurveFactory::curveBW(params.blackwhite.beforeCurve, params.blackwhite.afterCurve, hist16, dummy, customToneCurvebw1, customToneCurvebw2, 1);
}
double rrm, ggm, bbm;
float autor, autog, autob;
float satLimit = float (params.colorToning.satProtectionThreshold) / 100.f * 0.7f + 0.3f;
float satLimitOpacity = 1.f - (float (params.colorToning.saturatedOpacity) / 100.f);
if (params.colorToning.enabled && params.colorToning.autosat && params.colorToning.method != "LabGrid") { //for colortoning evaluation of saturation settings
float moyS = 0.f;
float eqty = 0.f;
ipf.moyeqt(baseImg, moyS, eqty); //return image : mean saturation and standard dev of saturation
float satp = ((moyS + 1.5f * eqty) - 0.3f) / 0.7f; //1.5 sigma ==> 93% pixels with high saturation -0.3 / 0.7 convert to Hombre scale
if (satp >= 0.92f) {
satp = 0.92f; //avoid values too high (out of gamut)
}
if (satp <= 0.15f) {
satp = 0.15f; //avoid too low values
}
satLimit = 100.f * satp;
satLimitOpacity = 100.f * (moyS - 0.85f * eqty); //-0.85 sigma==>20% pixels with low saturation
}
autor = -9000.f; // This will ask to compute the "auto" values for the B&W tool (have to be inferior to -5000)
DCPProfileApplyState as;
DCPProfile *dcpProf = imgsrc->getDCP(params.icm, as);
LUTu histToneCurve;
ipf.rgbProc(baseImg, labView, nullptr, curve1, curve2, curve, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf, as, histToneCurve, options.chunkSizeRGB, options.measure);
if (settings->verbose) {
printf("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", static_cast<double>(autor), static_cast<double>(autog), static_cast<double>(autob));
}
// if clut was used and size of clut cache == 1 we free the memory used by the clutstore (default clut cache size = 1 for 32 bit OS)
if (params.filmSimulation.enabled && !params.filmSimulation.clutFilename.empty() && options.clutCacheSize == 1) {
CLUTStore::getInstance().clearCache();
}
// freeing up some memory
customToneCurve1.Reset();
customToneCurve2.Reset();
ctColorCurve.Reset();
ctOpacityCurve.Reset();
noiseLCurve.Reset();
noiseCCurve.Reset();
customToneCurvebw1.Reset();
customToneCurvebw2.Reset();
// Freeing baseImg because not used anymore
delete baseImg;
baseImg = nullptr;
if (pl) {
pl->setProgress(0.55);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// start tile processing...???
if (params.labCurve.contrast != 0) { //only use hist16 for contrast
hist16.clear();
#ifdef _OPENMP
#pragma omp parallel
#endif
{
LUTu hist16thr(hist16.getSize()); // one temporary lookup table per thread
hist16thr.clear();
#ifdef _OPENMP
#pragma omp for schedule(static) nowait
#endif
for (int i = 0; i < fh; i++)
for (int j = 0; j < fw; j++) {
hist16thr[(int)((labView->L[i][j]))]++;
}
#ifdef _OPENMP
#pragma omp critical
#endif
{
hist16 += hist16thr;
}
}
}
bool utili;
CurveFactory::complexLCurve(params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve, hist16, lumacurve, dummy, 1, utili);
const bool clcutili = CurveFactory::diagonalCurve2Lut(params.labCurve.clcurve, clcurve, 1);
bool ccutili, cclutili;
CurveFactory::complexsgnCurve(autili, butili, ccutili, cclutili, params.labCurve.acurve, params.labCurve.bcurve, params.labCurve.cccurve,
params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 1);
if (params.colorToning.enabled && params.colorToning.method == "LabGrid") {
ipf.colorToningLabGrid(labView, 0, labView->W, 0, labView->H, false);
}
ipf.shadowsHighlights(labView, params.sh.enabled, params.sh.lab, params.sh.highlights, params.sh.shadows, params.sh.radius, 1, params.sh.htonalwidth, params.sh.stonalwidth);
if (params.localContrast.enabled) {
// Alberto's local contrast
ipf.localContrast(labView, labView->L, params.localContrast, false, 1);//scale);
}
ipf.chromiLuminanceCurve(nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy);
const bool cam02 = params.colorappearance.modelmethod == "02" && params.colorappearance.enabled;
// if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!cam02)) {
ipf.EPDToneMap(labView, 0, 1);
}
ipf.vibrance(labView, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile);
ipf.labColorCorrectionRegions(labView);
// for all treatments Defringe, Sharpening, Contrast detail ,Microcontrast they are activated if "CIECAM" function are disabled
// if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) {
ipf.impulsedenoise(labView);
ipf.defringe(labView);
}
if (params.sharpenEdge.enabled) {
ipf.MLsharpen(labView);
}
if (params.sharpenMicro.enabled) {
// if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) {
ipf.MLmicrocontrast(labView); //!params.colorappearance.sharpcie
}
}
// if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) && params.sharpening.enabled) {
if (((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) && params.sharpening.enabled) {
ipf.sharpening(labView, params.sharpening);
}
// directional pyramid wavelet
if (params.dirpyrequalizer.cbdlMethod == "aft") {
// if ((params.colorappearance.enabled && !settings->autocielab) || !params.colorappearance.enabled) {
if ((params.colorappearance.enabled && !settings->autocielab) || !cam02) {
ipf.dirpyrequalizer(labView, 1); //TODO: this is the luminance tonecurve, not the RGB one
}
}
int savestr = params.wavelet.strength;//work around for abstract profile: time about = 0.1 second
if ((params.wavelet.enabled) || (params.icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params.icm.trcExp)) {
LabImage *unshar = nullptr;
WaveletParams WaveParams = params.wavelet;
WavCurve wavCLVCurve;
WavCurve wavdenoise;
WavCurve wavdenoiseh;
Wavblcurve wavblcurve;
WavOpacityCurveRG waOpacityCurveRG;
WavOpacityCurveSH waOpacityCurveSH;
WavOpacityCurveBY waOpacityCurveBY;
WavOpacityCurveW waOpacityCurveW;
WavOpacityCurveWL waOpacityCurveWL;
LabImage *provradius = nullptr;
bool procont = WaveParams.expcontrast;
bool prochro = WaveParams.expchroma;
bool proedge = WaveParams.expedge;
bool profin = WaveParams.expfinal;
bool proton = WaveParams.exptoning;
bool pronois = WaveParams.expnoise;
if(params.icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params.icm.trcExp) {
params.wavelet.strength = 0;
}
if (WaveParams.softrad > 0.f) {
provradius = new LabImage(*labView, true);
}
params.wavelet.getCurves(wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL);
CurveFactory::diagonalCurve2Lut(params.wavelet.wavclCurve, wavclCurve, 1);
if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") {
const Glib::ustring provis = params.wavelet.CLmethod;
params.wavelet.CLmethod = "all";
ipf.ip_wavelet(labView, labView, 2, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, 1);
unshar = new LabImage(*labView, true);
params.wavelet.CLmethod = provis;
WaveParams.expcontrast = false;
WaveParams.expchroma = false;
WaveParams.expedge = false;
WaveParams.expfinal = false;
WaveParams.exptoning = false;
WaveParams.expnoise = false;
}
ipf.ip_wavelet(labView, labView, 2, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, 1);
if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") {
WaveParams.expcontrast = procont;
WaveParams.expchroma = prochro;
WaveParams.expedge = proedge;
WaveParams.expfinal = profin;
WaveParams.exptoning = proton;
WaveParams.expnoise = pronois;
if (WaveParams.softrad > 0.f) {
array2D<float> ble(fw, fh);
array2D<float> guid(fw, fh);
Imagefloat *tmpImage = nullptr;
tmpImage = new Imagefloat(fw, fh);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < fh; ir++)
for (int jr = 0; jr < fw; jr++) {
float X, Y, Z;
float L = provradius->L[ir][jr];
float a = provradius->a[ir][jr];
float b = provradius->b[ir][jr];
Color::Lab2XYZ(L, a, b, X, Y, Z);
guid[ir][jr] = Y / 32768.f;
float La = labView->L[ir][jr];
float aa = labView->a[ir][jr];
float ba = labView->b[ir][jr];
Color::Lab2XYZ(La, aa, ba, X, Y, Z);
tmpImage->r(ir, jr) = X;
tmpImage->g(ir, jr) = Y;
tmpImage->b(ir, jr) = Z;
ble[ir][jr] = Y / 32768.f;
}
double epsilmax = 0.0001;
double epsilmin = 0.00001;
double aepsil = (epsilmax - epsilmin) / 100.f;
double bepsil = epsilmin; //epsilmax - 100.f * aepsil;
double epsil = aepsil * WaveParams.softrad + bepsil;
float blur = 10.f / 1 * (0.5f + 0.8f * WaveParams.softrad);
// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiTh);
rtengine::guidedFilter(guid, ble, ble, blur, epsil, false);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < fh; ir++)
for (int jr = 0; jr < fw; jr++) {
float X = tmpImage->r(ir, jr);
float Y = 32768.f * ble[ir][jr];
float Z = tmpImage->b(ir, jr);
float L, a, b;
Color::XYZ2Lab(X, Y, Z, L, a, b);
labView->L[ir][jr] = L;
}
delete tmpImage;
}
}
if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") {
float mL = (float)(WaveParams.mergeL / 100.f);
float mC = (float)(WaveParams.mergeC / 100.f);
float mL0;
float mC0;
if ((WaveParams.CLmethod == "one" || WaveParams.CLmethod == "inf") && WaveParams.Backmethod == "black") {
mL0 = mC0 = 0.f;
mL = -1.5f * mL;
mC = -mC;
} else if (WaveParams.CLmethod == "sup" && WaveParams.Backmethod == "resid") {
mL0 = mL;
mC0 = mC;
} else {
mL0 = mL = mC0 = mC = 0.f;
}
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int x = 0; x < fh; x++)
for (int y = 0; y < fw; y++) {
labView->L[x][y] = LIM((1.f + mL0) * (unshar->L[x][y]) - mL * labView->L[x][y], 0.f, 32768.f);
labView->a[x][y] = (1.f + mC0) * (unshar->a[x][y]) - mC * labView->a[x][y];
labView->b[x][y] = (1.f + mC0) * (unshar->b[x][y]) - mC * labView->b[x][y];
}
delete unshar;
unshar = NULL;
if (WaveParams.softrad > 0.f) {
delete provradius;
provradius = NULL;
}
}
wavCLVCurve.Reset();
}
params.wavelet.strength = savestr;
ipf.softLight(labView, params.softlight);
if (params.icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params.icm.trcExp) {
const int GW = labView->W;
const int GH = labView->H;
std::unique_ptr<LabImage> provis;
const float pres = 0.01f * params.icm.preser;
if(params.icm.trcExp) {//local contrast
int level_hr = 7;
int maxlevpo = 9;
bool wavcurvecont = false;
WaveletParams WaveParams = params.wavelet;
ColorManagementParams Colparams = params.icm;
WavOpacityCurveWL icmOpacityCurveWL;
Colparams.getCurves(icmOpacityCurveWL);
ipf.complete_local_contrast(labView, labView, WaveParams, Colparams, icmOpacityCurveWL, 1, level_hr, maxlevpo, wavcurvecont);
}
if (pres > 0.f && params.icm.wprim != ColorManagementParams::Primaries::DEFAULT) {
provis.reset(new LabImage(GW, GH));
provis->CopyFrom(labView);
}
const std::unique_ptr<Imagefloat> tmpImage1(new Imagefloat(GW, GH));
ipf.lab2rgb(*labView, *tmpImage1, params.icm.workingProfile);
const float gamtone = params.icm.wGamma;
const float slotone = params.icm.wSlope;
int illum = toUnderlying(params.icm.will);
const int prim = toUnderlying(params.icm.wprim);
Glib::ustring prof = params.icm.workingProfile;
cmsHTRANSFORM dummy = nullptr;
int ill = 0;
bool gamutcontrol = params.icm.gamut;
int catc = toUnderlying(params.icm.wcat);
int locprim = 0;
float rdx, rdy, grx, gry, blx, bly = 0.f;
float meanx, meany, meanxe, meanye = 0.f;
ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, -5, prof, 2.4, 12.92310, 0, ill, 0, 0, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, dummy, true, false, false, false);
ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, 5, prof, gamtone, slotone, catc, illum, prim, locprim, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, dummy, false, true, true, gamutcontrol);
const int midton = params.icm.wmidtcie;
if(midton != 0) {
ToneEqualizerParams params;
params.enabled = true;
params.regularization = 0.f;
params.pivot = 0.f;
params.bands[0] = 0;
params.bands[2] = midton;
params.bands[4] = 0;
params.bands[5] = 0;
int mid = abs(midton);
int threshmid = 50;
if(mid > threshmid) {
params.bands[1] = sign(midton) * (mid - threshmid);
params.bands[3] = sign(midton) * (mid - threshmid);
}
ipf.toneEqualizer(tmpImage1.get(), params, prof, 1, false);
}
const bool smoothi = params.icm.wsmoothcie;
if(smoothi) {
ToneEqualizerParams params;
params.enabled = true;
params.regularization = 0.f;
params.pivot = 0.f;
params.bands[0] = 0;
params.bands[1] = 0;
params.bands[2] = 0;
params.bands[3] = 0;
params.bands[4] = -40;//arbitrary value to adapt with WhiteEvjz - here White Ev # 10
params.bands[5] = -80;//8 Ev and above
bool Evsix = true;
if(Evsix) {//EV = 6 majority of images
params.bands[4] = -15;
}
ipf.toneEqualizer(tmpImage1.get(), params, prof, 1, false);
}
ipf.rgb2lab(*tmpImage1, *labView, params.icm.workingProfile);
// labView and provis
if (provis) {
ipf.preserv(labView, provis.get(), GW, GH);
}
if (params.icm.fbw) {
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int x = 0; x < GH; x++)
for (int y = 0; y < GW; y++) {
labView->a[x][y] = 0.f;
labView->b[x][y] = 0.f;
}
}
}
//Colorappearance and tone-mapping associated
int f_w = 1, f_h = 1;
if (params.colorappearance.tonecie || params.colorappearance.enabled) {
f_w = fw;
f_h = fh;
}
CieImage *cieView = new CieImage(f_w, (f_h));
CurveFactory::curveLightBrightColor(
params.colorappearance.curve,
params.colorappearance.curve2,
params.colorappearance.curve3,
hist16, dummy,
dummy, dummy,
customColCurve1,
customColCurve2,
customColCurve3,
1);
if (params.colorappearance.enabled) {
double adap;
const float fnum = imgsrc->getMetaData()->getFNumber(); // F number
const float fiso = imgsrc->getMetaData()->getISOSpeed() ; // ISO
const float fspeed = imgsrc->getMetaData()->getShutterSpeed() ; // Speed
const float fcomp = imgsrc->getMetaData()->getExpComp(); // Compensation + -
if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) {
adap = 2000.;
}//if no exif data or wrong
else {
double E_V = fcomp + log2((fnum * fnum) / fspeed / (fiso / 100.f));
double kexp = 0.;
E_V += kexp * params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV
E_V += 0.5 * log2(params.raw.expos); // exposure raw white point ; log2 ==> linear to EV
adap = std::pow(2.0, E_V - 3.0); //cd / m2
}
LUTf CAMBrightCurveJ;
LUTf CAMBrightCurveQ;
float CAMMean = NAN;
float d, dj, yb;
ipf.ciecam_02float(cieView, float (adap), 1, 2, labView, &params, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 0, 1, true, d, dj, yb, 1);
}
delete cieView;
cieView = nullptr;
// end tile processing...???
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if (pl) {
pl->setProgress(0.60);
}
// crop and convert to rgb16
int cx = 0, cy = 0, cw = labView->W, ch = labView->H;
if (params.crop.enabled) {
cx = params.crop.x;
cy = params.crop.y;
cw = params.crop.w;
ch = params.crop.h;
}
ImProcFunctions::FramingArgs framingArgs;
framingArgs.params = &params;
framingArgs.cropWidth = cw;
framingArgs.cropHeight = ch;
{
int imw, imh;
double tmpScale = ipf.resizeScale(&params, fw, fh, imw, imh);
framingArgs.resizeWidth = imw;
framingArgs.resizeHeight = imh;
framingArgs.resizeScale = tmpScale;
// If upscaling is not permitted, keep original sizing
if ((cw < imw || ch < imh) && !params.resize.allowUpscaling) {
framingArgs.resizeWidth = cw;
framingArgs.resizeHeight = ch;
framingArgs.resizeScale = 1.0;
}
}
// If framing is not enabled, resize values simply pass through to output
ImProcFunctions::FramingData framingData = ipf.framing(framingArgs);
if (settings->verbose) {
printf("Framing Parameters (enabled=%s)\n", framingData.enabled ? "yes" : "no");
printf(" Crop: w=%d h=%d\n", cw, ch);
printf(" Original resize: w=%d h=%d s=%f\n",
framingArgs.resizeWidth, framingArgs.resizeHeight, framingArgs.resizeScale);
printf(" Framed image size: w=%d h=%d s=%f\n",
framingData.imgWidth, framingData.imgHeight, framingData.scale);
printf(" Total size: w=%d h=%d\n",
framingData.framedWidth, framingData.framedHeight);
}
bool labResize = params.resize.enabled && params.resize.method != "Nearest" &&
(framingData.scale != 1.0 || params.prsharpening.enabled || framingData.enabled);
LabImage *tmplab = nullptr;
if (params.crop.enabled) {
if (labResize) { // crop lab data
tmplab = new LabImage(cw, ch);
for (int row = 0; row < ch; row++) {
for (int col = 0; col < cw; col++) {
tmplab->L[row][col] = labView->L[row + cy][col + cx];
tmplab->a[row][col] = labView->a[row + cy][col + cx];
tmplab->b[row][col] = labView->b[row + cy][col + cx];
}
}
delete labView;
labView = tmplab;
cx = 0;
cy = 0;
}
}
if (labResize) { // resize lab data
int imw = framingData.imgWidth;
int imh = framingData.imgHeight;
if (labView->W != imw || labView->H != imh) {
// resize image
tmplab = new LabImage(imw, imh);
ipf.Lanczos(labView, tmplab, framingData.scale);
delete labView;
labView = tmplab;
}
cw = labView->W;
ch = labView->H;
if (params.prsharpening.enabled) {
for (int i = 0; i < ch; i++) {
for (int j = 0; j < cw; j++) {
labView->L[i][j] = labView->L[i][j] < 0.f ? 0.f : labView->L[i][j];
}
}
ipf.sharpening(labView, params.prsharpening);
}
}
// bool bwonly = params.blackwhite.enabled && !params.colorToning.enabled && !autili && !butili && !params.colorappearance.enabled;
bool bwonly = params.blackwhite.enabled && !params.colorToning.enabled && !autili && !butili && !cam02;
///////////// Custom output gamma has been removed, the user now has to create
///////////// a new output profile with the ICCProfileCreator
// if Default gamma mode: we use the profile selected in the "Output profile" combobox;
// gamma come from the selected profile, otherwise it comes from "Free gamma" tool
Imagefloat* readyImg = ipf.lab2rgbOut(labView, cx, cy, cw, ch, params.icm);
if (settings->verbose) {
printf("Output profile_: \"%s\"\n", params.icm.outputProfile.c_str());
}
delete labView;
labView = nullptr;
if (bwonly) { //force BW r=g=b
if (settings->verbose) {
printf("Force BW\n");
}
for (int ccw = 0; ccw < cw; ccw++) {
for (int cch = 0; cch < ch; cch++) {
readyImg->r(cch, ccw) = readyImg->g(cch, ccw);
readyImg->b(cch, ccw) = readyImg->g(cch, ccw);
}
}
}
if (pl) {
pl->setProgress(0.70);
}
if (framingData.scale != 1.0 && params.resize.method == "Nearest") {
int imw = framingData.imgWidth;
int imh = framingData.imgHeight;
if (readyImg->getWidth() != imw || readyImg->getHeight() != imh) {
// resize rgb data (gamma applied)
Imagefloat* tempImage = new Imagefloat(imw, imh);
ipf.resize(readyImg, tempImage, framingData.scale);
delete readyImg;
readyImg = tempImage;
}
}
if (framingData.enabled) {
readyImg = ipf.drawFrame(readyImg, params.framing, framingData);
}
Exiv2Metadata info(imgsrc->getFileName());
switch (params.metadata.mode) {
case MetaDataParams::TUNNEL:
readyImg->setMetadata(std::move(info));
break;
case MetaDataParams::EDIT:
info.setExif(params.metadata.exif);
info.setIptc(params.metadata.iptc);
if (!(params.metadata.exifKeys.size() == 1 && params.metadata.exifKeys[0] == "*")) {
info.setExifKeys(&(params.metadata.exifKeys));
}
readyImg->setMetadata(std::move(info));
break;
default: // case MetaDataParams::STRIP
// nothing to do
break;
}
// Setting the output curve to readyImg
// use the selected output profile if present, otherwise use LCMS2 profile generate by lab2rgb16 w/ gamma
if (!params.icm.outputProfile.empty() && params.icm.outputProfile != ColorManagementParams::NoICMString) {
// if ICCStore::getInstance()->getProfile send back an object, then ICCStore::getInstance()->getContent will do too
cmsHPROFILE jprof = ICCStore::getInstance()->getProfile(params.icm.outputProfile); //get outProfile
if (jprof == nullptr) {
if (settings->verbose) {
printf("\"%s\" ICC output profile not found!\n - use LCMS2 substitution\n", params.icm.outputProfile.c_str());
}
} else {
if (settings->verbose) {
printf("Using \"%s\" output profile\n", params.icm.outputProfile.c_str());
}
ProfileContent pc = ICCStore::getInstance()->getContent(params.icm.outputProfile);
readyImg->setOutputProfile(pc.getData());
}
} else {
// No ICM
readyImg->setOutputProfile({});
}
// t2.set();
// if( settings->verbose )
// printf("Total:- %d usec\n", t2.etime(t1));
if (!job->initialImage) {
initialImage->decreaseRef();
}
delete job;
if (pl) {
pl->setProgress(0.75);
}
/* curve1.reset();curve2.reset();
curve.reset();
satcurve.reset();
lhskcurve.reset();
rCurve.reset();
gCurve.reset();
bCurve.reset();
hist16.reset();
hist16C.reset();
*/
return readyImg;
}
void stage_early_resize()
{
procparams::ProcParams& params = job->pparams;
//ImProcFunctions ipf (&params, true);
ImProcFunctions &ipf = * (ipf_p.get());
int imw, imh;
double scale_factor = ipf.resizeScale(&params, fw, fh, imw, imh);
std::unique_ptr<LabImage> tmplab(new LabImage(fw, fh));
ipf.rgb2lab(*baseImg, *tmplab, params.icm.workingProfile);
if (params.crop.enabled) {
int cx = params.crop.x;
int cy = params.crop.y;
int cw = params.crop.w;
int ch = params.crop.h;
std::unique_ptr<LabImage> cropped(new LabImage(cw, ch));
for (int row = 0; row < ch; row++) {
for (int col = 0; col < cw; col++) {
cropped->L[row][col] = tmplab->L[row + cy][col + cx];
cropped->a[row][col] = tmplab->a[row + cy][col + cx];
cropped->b[row][col] = tmplab->b[row + cy][col + cx];
}
}
tmplab = std::move(cropped);
}
assert(params.resize.enabled);
// resize image
if (params.resize.allowUpscaling || (imw <= fw && imh <= fh)) {
std::unique_ptr<LabImage> resized(new LabImage(imw, imh));
ipf.Lanczos(tmplab.get(), resized.get(), scale_factor);
tmplab = std::move(resized);
}
adjust_procparams(scale_factor);
fw = imw;
fh = imh;
delete baseImg;
baseImg = new Imagefloat(fw, fh);
ipf.lab2rgb(*tmplab, *baseImg, params.icm.workingProfile);
}
void adjust_procparams(double scale_factor)
{
procparams::ProcParams &params = job->pparams;
procparams::ProcParams defaultparams;
params.resize.enabled = false;
params.crop.enabled = false;
if (params.prsharpening.enabled) {
params.sharpening = params.prsharpening;
} else {
params.sharpening.radius *= scale_factor;
params.sharpening.deconvradius *= scale_factor;
}
params.impulseDenoise.thresh *= scale_factor;
if (scale_factor < 0.5) {
params.impulseDenoise.enabled = false;
}
params.wavelet.strength *= scale_factor;
double noise_factor = (1.0 - scale_factor);
params.dirpyrDenoise.luma *= noise_factor; // * scale_factor;
//params.dirpyrDenoise.Ldetail += (100 - params.dirpyrDenoise.Ldetail) * scale_factor;
auto &lcurve = params.dirpyrDenoise.lcurve;
for (size_t i = 2; i < lcurve.size(); i += 4) {
lcurve[i] *= min(noise_factor /* * scale_factor*/, 1.0);
}
noiseLCurve.Set(lcurve);
const char *medmethods[] = { "soft", "33", "55soft", "55", "77", "99" };
if (params.dirpyrDenoise.median) {
auto &key = params.dirpyrDenoise.methodmed == "RGB" ? params.dirpyrDenoise.rgbmethod : params.dirpyrDenoise.medmethod;
for (int i = 1; i < int (sizeof(medmethods) / sizeof(const char *)); ++i) {
if (key == medmethods[i]) {
int j = i - int (1.0 / scale_factor);
if (j < 0) {
params.dirpyrDenoise.median = false;
} else {
key = medmethods[j];
}
break;
}
}
}
params.epd.scale *= scale_factor;
//params.epd.edgeStopping *= scale_factor;
const double dirpyreq_scale = min(scale_factor * 1.5, 1.0);
for (int i = 0; i < 6; ++i) {
adjust_radius(defaultparams.dirpyrequalizer.mult[i], dirpyreq_scale,
params.dirpyrequalizer.mult[i]);
}
params.dirpyrequalizer.threshold *= scale_factor;
adjust_radius(defaultparams.defringe.radius, scale_factor,
params.defringe.radius);
params.sh.radius *= scale_factor;
params.localContrast.radius *= scale_factor;
if (params.raw.xtranssensor.method == procparams::RAWParams::XTransSensor::getMethodString(procparams::RAWParams::XTransSensor::Method::THREE_PASS)) {
params.raw.xtranssensor.method = procparams::RAWParams::XTransSensor::getMethodString(procparams::RAWParams::XTransSensor::Method::ONE_PASS);
}
if (params.raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::PIXELSHIFT)) {
params.raw.bayersensor.method = procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::RCD);
}
// Use Rcd instead of Amaze for fast export
if (params.raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::AMAZE)) {
params.raw.bayersensor.method = procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::RCD);
}
}
private:
ProcessingJobImpl* job;
int& errorCode;
ProgressListener* pl;
bool flush;
// internal state
std::unique_ptr<ImProcFunctions> ipf_p;
InitialImage *initialImage;
ImageSource *imgsrc;
int fw;
int fh;
int tr;
PreviewProps pp;
NoiseCurve noiseLCurve;
NoiseCurve noiseCCurve;
Imagefloat *calclum;
float autoNR;
float autoNRmax;
int tilesize;
int overlap;
float *ch_M;
float *max_r;
float *max_b;
float *min_b;
float *min_r;
float *lumL;
float *chromC;
float *ry;
float *sk;
float *pcsk;
double expcomp;
int bright;
int contr;
int black;
int hlcompr;
int hlcomprthresh;
ColorTemp currWB;
Imagefloat *baseImg;
LabImage* labView;
LUTu hist16;
LUTf curve1;
LUTf curve2;
LUTf curve;
LUTf satcurve;
LUTf lhskcurve;
LUTf lumacurve;
LUTf clcurve;
LUTf clToningcurve;
LUTf cl2Toningcurve;
LUTf wavclCurve;
LUTf rCurve;
LUTf gCurve;
LUTf bCurve;
LUTu dummy;
ToneCurve customToneCurve1, customToneCurve2;
ColorGradientCurve ctColorCurve;
OpacityCurve ctOpacityCurve;
ColorAppearance customColCurve1, customColCurve2, customColCurve3 ;
ToneCurve customToneCurvebw1;
ToneCurve customToneCurvebw2;
bool autili, butili;
};
} // namespace
IImagefloat* processImage(ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool flush)
{
ImageProcessor proc(pjob, errorCode, pl, flush);
return proc();
}
void batchProcessingThread(ProcessingJob* job, BatchProcessingListener* bpl)
{
ProcessingJob* currentJob = job;
while (currentJob) {
int errorCode;
IImagefloat* img = processImage(currentJob, errorCode, bpl, true);
if (errorCode) {
bpl->error(M("MAIN_MSG_CANNOTLOAD"));
currentJob = nullptr;
} else {
try {
currentJob = bpl->imageReady(img);
} catch (Glib::Exception& ex) {
bpl->error(ex.what());
currentJob = nullptr;
}
}
}
}
void startBatchProcessing(ProcessingJob* job, BatchProcessingListener* bpl)
{
if (bpl) {
Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl), 0, true, true, Glib::THREAD_PRIORITY_LOW);
}
}
}