Desmis b73840cf3c
Gamut compression - reduce artifacts resulting from out of gamut (#7205)
* First functions needs for ACES

* New file GUI compressgamut

* GUI first step

* GUI first step

* Gui step 2

* GUI procparams and paramsedit

* GUI read

* GUI step 4

* GUI step 5

* First tooltip

* Gamut compression tooltips

* Various GUI improvment

* History msg

* Comment code with Aces remarks

* First change improccoordinator and events

* Save work on matrix

* Compress gamut next work

* First try gamut compress

* Replace cout by printf in invertmatrix

* Change tooltips and events

* Added namespace std to iplab2rgb

* Comment code

* Active rtthumbnail

* Change tooltip

* Various improvment GUI and rolloff

* Added adobeRGB gamut

* Appimage windows yml

* Remove rttumbnail gamutcompr

* Change event to COMPR

* Change tooltip and verbose

* Restore raw de-haze history message

* Refactor ACES gamut compression functions

* Fix gamut compression color space history message

Correctly display the color space name.

* Add "unchanged" for gamut compression color space

Allow "unchanged" in batch mode.

* Fix gamut compression yellow distance for batch

* Update copyright for gamut compression GUI

* Fix gamut compression color space names

* Refactor gamut compression code

* Remove comment rtthumnail.cc - Acesp1 default

* Change matrix DCI-P3 - threshold maximum to 1 in GUI and in gamut compression - tooltips

* Change tooltip

* Clean code - change tooltips

* Remove appimage windows yml

---------

Co-authored-by: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
2024-11-14 13:08:52 +01:00

2135 lines
96 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
* 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/ustring.h>
#include <glibmm/timer.h>
#include "cieimage.h"
#include "color.h"
#include "curves.h"
#include "dcp.h"
#include "dcrop.h"
#include "guidedfilter.h"
#include "image8.h"
#include "imagefloat.h"
#include "improccoordinator.h"
#include "labimage.h"
#include "mytime.h"
#include "procparams.h"
#include "refreshmap.h"
#include "rt_math.h"
#include "utils.h"
#include "../rtgui/editcallbacks.h"
#pragma GCC diagnostic warning "-Wall"
#pragma GCC diagnostic warning "-Wextra"
namespace
{
// "ceil" rounding
template<typename T>
constexpr T skips(T a, T b)
{
return a / b + static_cast<bool>(a % b);
}
}
namespace rtengine
{
Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow)
: PipetteBuffer(editDataProvider), origCrop(nullptr), spotCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr),
cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr),
updating(false), newUpdatePending(false), skip(10),
cropx(0), cropy(0), cropw(-1), croph(-1),
trafx(0), trafy(0), trafw(-1), trafh(-1),
rqcropx(0), rqcropy(0), rqcropw(-1), rqcroph(-1),
borderRequested(32), upperBorder(0), leftBorder(0),
cropAllocated(false),
cropImageListener(nullptr), parent(parent), isDetailWindow(isDetailWindow)
{
parent->crops.push_back(this);
}
Crop::~Crop()
{
MyMutex::MyLock cropLock(cropMutex);
std::vector<Crop*>::iterator i = std::find(parent->crops.begin(), parent->crops.end(), this);
if (i != parent->crops.end()) {
parent->crops.erase(i);
}
MyMutex::MyLock processingLock(parent->mProcessing);
freeAll();
}
void Crop::destroy()
{
MyMutex::MyLock lock(cropMutex);
MyMutex::MyLock processingLock(parent->mProcessing);
freeAll();
}
void Crop::setListener(DetailedCropListener* il)
{
// We can make reads in the IF, because the mProcessing lock is only needed for change
if (cropImageListener != il) {
MyMutex::MyLock lock(cropMutex);
cropImageListener = il;
}
}
EditUniqueID Crop::getCurrEditID() const
{
const EditSubscriber *subscriber = PipetteBuffer::dataProvider ? PipetteBuffer::dataProvider->getCurrSubscriber() : nullptr;
return subscriber ? subscriber->getEditID() : EUID_None;
}
/*
* Delete the edit image buffer if there's no subscriber anymore.
* If allocation has to be done, it is deferred to Crop::update
*/
void Crop::setEditSubscriber(EditSubscriber* newSubscriber)
{
MyMutex::MyLock lock(cropMutex);
// At this point, editCrop.dataProvider->currSubscriber is the old subscriber
const EditSubscriber *oldSubscriber = PipetteBuffer::dataProvider ? PipetteBuffer::dataProvider->getCurrSubscriber() : nullptr;
if (newSubscriber == nullptr || (oldSubscriber != nullptr && oldSubscriber->getPipetteBufferType() != newSubscriber->getPipetteBufferType())) {
if (PipetteBuffer::imgFloatBuffer != nullptr) {
delete PipetteBuffer::imgFloatBuffer;
PipetteBuffer::imgFloatBuffer = nullptr;
}
if (PipetteBuffer::LabBuffer != nullptr) {
delete PipetteBuffer::LabBuffer;
PipetteBuffer::LabBuffer = nullptr;
}
if (PipetteBuffer::singlePlaneBuffer.getWidth() != -1) {
PipetteBuffer::singlePlaneBuffer.flushData();
}
}
// If oldSubscriber == NULL && newSubscriber != NULL && newSubscriber->getEditingType() == ET_PIPETTE-> the image will be allocated when necessary
}
bool Crop::hasListener()
{
MyMutex::MyLock cropLock(cropMutex);
return cropImageListener;
}
void Crop::update(int todo)
{
MyMutex::MyLock cropLock(cropMutex);
ProcParams& params = *parent->params;
// CropGUIListener* cropgl;
// No need to update todo here, since it has already been changed in ImprocCoordinator::updatePreviewImage,
// and Crop::update ask to do ALL anyway
// give possibility to the listener to modify crop window (as the full image dimensions are already known at this point)
int wx, wy, ww, wh, ws;
const bool overrideWindow = cropImageListener;
bool spotsDone = false;
if (overrideWindow) {
cropImageListener->getWindow(wx, wy, ww, wh, ws);
}
// re-allocate sub-images and arrays if their dimensions changed
bool needsinitupdate = false;
if (!overrideWindow) {
needsinitupdate = setCropSizes(rqcropx, rqcropy, rqcropw, rqcroph, skip, true);
} else {
needsinitupdate = setCropSizes(wx, wy, ww, wh, ws, true); // this set skip=ws
}
// it something has been reallocated, all processing steps have to be performed
if (needsinitupdate || (todo & M_HIGHQUAL)) {
todo = ALL;
}
// Tells to the ImProcFunctions' tool what is the preview scale, which may lead to some simplifications
parent->ipf.setScale(skip);
Imagefloat* baseCrop = origCrop;
int widIm = parent->fw;//full image
int heiIm = parent->fh;
if (todo & (M_INIT | M_LINDENOISE | M_HDR)) {
MyMutex::MyLock lock(parent->minit); // Also used in improccoord
int tr = getCoarseBitMask(params.coarse);
if (!needsinitupdate) {
setCropSizes(rqcropx, rqcropy, rqcropw, rqcroph, skip, true);
}
// printf("x=%d y=%d crow=%d croh=%d skip=%d\n",rqcropx, rqcropy, rqcropw, rqcroph, skip);
// printf("trafx=%d trafyy=%d trafwsk=%d trafHs=%d \n",trafx, trafy, trafw*skip, trafh*skip);
Imagefloat *calclum = nullptr;//for Luminance denoise curve
NoiseCurve noiseLCurve;
NoiseCurve noiseCCurve;
float autoNR = (float) settings->nrauto;//
float autoNRmax = (float) settings->nrautomax;//
params.dirpyrDenoise.getCurves(noiseLCurve, noiseCCurve);
const int tilesize = settings->leveldnti == 0 ? 1024 : 768;
const int overlap = settings->leveldnti == 0 ? 128 : 96;
int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip;
parent->ipf.Tile_calc(tilesize, overlap, 2, widIm, heiIm, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip);
float *min_b = new float [9];
float *min_r = new float [9];
float *lumL = new float [9];
float *chromC = new float [9];
float *ry = new float [9];
float *sk = new float [9];
float *pcsk = new float [9];
int *centerTile_X = new int [numtiles_W];
int *centerTile_Y = new int [numtiles_H];
for (int cX = 0; cX < numtiles_W; cX++) {
centerTile_X[cX] = tileWskip / 2 + tileWskip * cX;
}
for (int cY = 0; cY < numtiles_H; cY++) {
centerTile_Y[cY] = tileHskip / 2 + tileHskip * cY;
}
if (settings->leveldnautsimpl == 1) {
if (params.dirpyrDenoise.Cmethod == "MAN" || params.dirpyrDenoise.Cmethod == "PON") {
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
}
} else {
if (params.dirpyrDenoise.C2method == "MANU") {
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
}
}
if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "PRE") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "PREV")) {
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
if ((!isDetailWindow) && parent->adnListener && skip == 1 && params.dirpyrDenoise.enabled) {
float lowdenoise = 1.f;
int levaut = settings->leveldnaut;
if (levaut == 1) { //Standard
lowdenoise = 0.7f;
}
int CenterPreview_X = trafx + (trafw * skip) / 2;
int CenterPreview_Y = trafy + (trafh * skip) / 2;
int minimuX = 20000;
int minimuY = 20000;
int poscenterX = 0;
int poscenterY = 0;
for (int cc = 0; cc < numtiles_W; cc++) {
if (abs(centerTile_X[cc] - CenterPreview_X) < minimuX) {
minimuX = abs(centerTile_X[cc] - CenterPreview_X);
poscenterX = cc;
}
}
for (int cc = 0; cc < numtiles_H; cc++) {
if (abs(centerTile_Y[cc] - CenterPreview_Y) < minimuY) {
minimuY = abs(centerTile_Y[cc] - CenterPreview_Y);
poscenterY = cc;
}
}
// printf("TileCX=%d TileCY=%d prevX=%d prevY=%d \n",centerTile_X[poscenterX],centerTile_Y[poscenterY],CenterPreview_X,CenterPreview_Y);
int crW;
if (settings->leveldnv == 0) {
crW = 100;
}
if (settings->leveldnv == 1) {
crW = 250;
}
// if (settings->leveldnv ==2) {crW=int(tileWskip/2);crH=int((tileWskip/2));}//adapted to scale of preview
if (settings->leveldnv == 2) {
crW = int (tileWskip / 2);
}
if (settings->leveldnv == 3) {
crW = tileWskip - 10;
}
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 == "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 (parent->adnListener) {
parent->adnListener->noiseTilePrev(centerTile_X[poscenterX], centerTile_Y[poscenterY], CenterPreview_X, CenterPreview_Y, crW, trafw * skip);
}
// I have tried "blind" some solutions..to move review ...but GUI is not my truc !
// int W,H;
// cropgl->cropMoved (centerTile_X[poscenterX],centerTile_Y[poscenterY] , W, H);
// cropImageListener->setPosition (int x, int y, bool update=true);
// bool update;
// cropImageListener->setPosition (centerTile_X[poscenterX],centerTile_Y[poscenterY] , true);
//setCropSizes (centerTile_X[poscenterX], centerTile_Y[poscenterY], trafw*skip,trafh*skip , skip, true);
// we only need image reduced to 1/4 here
int W = origCrop->getWidth();
int H = origCrop->getHeight();
Imagefloat *provicalc = new Imagefloat((W + 1) / 2, (H + 1) / 2); //for denoise curves
for (int ii = 0; ii < H; ii += 2) {
for (int jj = 0; jj < W; jj += 2) {
provicalc->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj);
provicalc->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj);
provicalc->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj);
}
}
parent->imgsrc->convertColorSpace(provicalc, params.icm, parent->currWB); //for denoise luminance curve
float maxr = 0.f;
float maxb = 0.f;
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;
minredaut = 0.f;
minblueaut = 0.f;
LUTf gamcurve(65536, 0);
float gam, gamthresh, gamslope;
parent->ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope);
parent->ipf.RGB_denoise_info(origCrop, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc, true);
// printf("redy=%f skin=%f pcskin=%f\n",redyel, skinc,nsknc);
// printf("DCROP skip=%d cha=%4.0f Nb=%d red=%4.0f bl=%4.0f redM=%4.0f bluM=%4.0f L=%4.0f sigL=%4.0f Ch=%4.0f Si=%4.0f\n",skip, chaut,Nb, redaut,blueaut, maxredaut, maxblueaut, lumema, sigma_L, chromina, sigma);
float multip = 1.f;
if (!parent->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 = 0;
// float redyel, skinc, nsknc;
int lissage = settings->leveldnliss;
parent->ipf.calcautodn_info(chaut, delta, Nb, levaut, maxmax, lumema, chromina, mode, lissage, redyel, skinc, nsknc);
if (maxredaut > maxblueaut) {
// maxr=(maxredaut-chaut)/((autoNRmax*multip*adjustr)/2.f);
maxr = (delta) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
if (minblueaut <= minredaut && minblueaut < chaut) {
maxb = (-chaut + minblueaut) / (autoNRmax * multip * adjustr * lowdenoise);
}
} else {
// maxb=(maxblueaut-chaut)/((autoNRmax*multip*adjustr)/2.f);
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
params.dirpyrDenoise.chroma = chaut / (autoNR * multip * adjustr * lowdenoise);
params.dirpyrDenoise.redchro = maxr;
params.dirpyrDenoise.bluechro = maxb;
parent->adnListener->chromaChanged(params.dirpyrDenoise.chroma, params.dirpyrDenoise.redchro, params.dirpyrDenoise.bluechro);
delete provicalc;
}
}
if (skip == 1 && params.dirpyrDenoise.enabled && !parent->denoiseInfoStore.valid && ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO"))) {
MyTime t1aue, t2aue;
t1aue.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((tileWskip/2));}//adapted to scale of preview
if (settings->leveldnv == 2) {
crW = int (tileWskip / 2);
crH = int (tileHskip / 2);
}
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;
}
LUTf gamcurve(65536, 0);
float gam, gamthresh, gamslope;
parent->ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope);
int Nb[9];
#ifdef _OPENMP
#pragma omp parallel
#endif
{
Imagefloat *origCropPart = new Imagefloat(crW, crH); //allocate memory
Imagefloat *provicalc = new Imagefloat((crW + 1) / 2, (crH + 1) / 2); //for denoise curves
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] = widIm / 2 - crW / 2;
coordW[2] = widIm - crW - begW;
coordH[0] = begH;
coordH[1] = heiIm / 2 - crH / 2;
coordH[2] = heiIm - crH - begH;
#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);
parent->imgsrc->getImage(parent->currWB, tr, origCropPart, ppP, params.toneCurve, params.raw);
// 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);
}
}
parent->imgsrc->convertColorSpace(provicalc, params.icm, parent->currWB); //for denoise luminance curve
float pondcorrec = 1.0f;
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;
int nb = 0;
parent->ipf.RGB_denoise_info(origCropPart, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc);
//printf("DCROP skip=%d cha=%f red=%f bl=%f redM=%f bluM=%f chrom=%f sigm=%f lum=%f\n",skip, chaut,redaut,blueaut, maxredaut, maxblueaut, chromina, sigma, lumema);
Nb[hcr * 3 + wcr] = nb;
parent->denoiseInfoStore.ch_M[hcr * 3 + wcr] = pondcorrec * chaut;
parent->denoiseInfoStore.max_r[hcr * 3 + wcr] = pondcorrec * maxredaut;
parent->denoiseInfoStore.max_b[hcr * 3 + wcr] = pondcorrec * maxblueaut;
min_r[hcr * 3 + wcr] = pondcorrec * minredaut;
min_b[hcr * 3 + wcr] = pondcorrec * 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 = 100000000000.f;
float MinB = 100000000000.f;
float maxr = 0.f;
float maxb = 0.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;
float multip = 1.f;
if (!parent->imgsrc->isRAW()) {
multip = 2.f; //take into account gamma for TIF / JPG approximate value...not good for gamma=1
}
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 == "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;
}
float delta[9];
int mode = 1;
int lissage = settings->leveldnliss;
for (int k = 0; k < 9; k++) {
float maxmax = max(parent->denoiseInfoStore.max_r[k], parent->denoiseInfoStore.max_b[k]);
parent->ipf.calcautodn_info(parent->denoiseInfoStore.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 (parent->denoiseInfoStore.max_r[k] > parent->denoiseInfoStore.max_b[k]) {
Max_R[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
Min_B[k] = - (parent->denoiseInfoStore.ch_M[k] - min_b[k]) / (autoNRmax * multip * adjustr * lowdenoise);
Max_B[k] = 0.f;
Min_R[k] = 0.f;
} else {
Max_B[k] = (delta[k]) / ((autoNRmax * multip * adjustr * lowdenoise) / 2.f);
Min_R[k] = - (parent->denoiseInfoStore.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 += parent->denoiseInfoStore.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 = MinRMoy + (MinR - MinRMoy) * 0.66f;
}
// printf("DCROP skip=%d cha=%f red=%f bl=%f \n",skip, chM,maxr,maxb);
params.dirpyrDenoise.chroma = chM / (autoNR * multip * adjustr);
params.dirpyrDenoise.redchro = maxr;
params.dirpyrDenoise.bluechro = maxb;
parent->denoiseInfoStore.valid = true;
if (parent->adnListener) {
parent->adnListener->chromaChanged(params.dirpyrDenoise.chroma, params.dirpyrDenoise.redchro, params.dirpyrDenoise.bluechro);
}
if (settings->verbose) {
t2aue.set();
printf("Info denoise auto performed in %d usec:\n", t2aue.etime(t1aue));
}
//end evaluate noise
}
// if (params.dirpyrDenoise.Cmethod=="AUT" || params.dirpyrDenoise.Cmethod=="PON") {//reinit origCrop after Auto
if ((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { //reinit origCrop after Auto
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
}
if ((todo & M_SPOT) && params.spot.enabled && !params.spot.entries.empty()) {
spotsDone = true;
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
//parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, nullptr, tr);
}
DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise;
if (params.dirpyrDenoise.Lmethod == "CUR") {
if (noiseLCurve) {
denoiseParams.luma = 0.5f; //very small value to init process - select curve or slider
} else {
denoiseParams.luma = 0.0f;
}
} else if (denoiseParams.Lmethod == "SLI") {
noiseLCurve.Reset();
}
if ((noiseLCurve || noiseCCurve) && skip == 1 && denoiseParams.enabled) { //only allocate memory if enabled and skip
// we only need image reduced to 1/4 here
int W = origCrop->getWidth();
int H = origCrop->getHeight();
calclum = new Imagefloat((W + 1) / 2, (H + 1) / 2); //for denoise curves
for (int ii = 0; ii < H; ii += 2) {
for (int jj = 0; jj < W; jj += 2) {
calclum->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj);
calclum->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj);
calclum->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj);
}
}
parent->imgsrc->convertColorSpace(calclum, params.icm, parent->currWB); //for denoise luminance curve
}
if (skip != 1) if (parent->adnListener) {
parent->adnListener->noiseChanged(0.f, 0.f);
}
if (todo & (M_INIT | M_LINDENOISE | M_HDR)) {
if (skip == 1 && denoiseParams.enabled) {
float nresi, highresi;
parent->ipf.RGB_denoise(0, origCrop, origCrop, calclum, parent->denoiseInfoStore.ch_M, parent->denoiseInfoStore.max_r, parent->denoiseInfoStore.max_b, parent->imgsrc->isRAW(), /*Roffset,*/ denoiseParams, parent->imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, nresi, highresi);
if (parent->adnListener) {
parent->adnListener->noiseChanged(nresi, highresi);
}
if (settings->leveldnautsimpl == 1) {
if ((denoiseParams.Cmethod == "AUT" || denoiseParams.Cmethod == "PRE") && (parent->adnListener)) { // force display value of sliders
parent->adnListener->chromaChanged(denoiseParams.chroma, denoiseParams.redchro, denoiseParams.bluechro);
}
} else {
if ((denoiseParams.C2method == "AUTO" || denoiseParams.C2method == "PREV") && (parent->adnListener)) { // force display value of sliders
parent->adnListener->chromaChanged(denoiseParams.chroma, denoiseParams.redchro, denoiseParams.bluechro);
}
}
}
}
if (params.filmNegative.enabled && params.filmNegative.colorSpace == FilmNegativeParams::ColorSpace::INPUT) {
parent->ipf.filmNegativeProcess(baseCrop, baseCrop, params.filmNegative);
}
parent->imgsrc->convertColorSpace(origCrop, params.icm, parent->currWB);
if (params.filmNegative.enabled && params.filmNegative.colorSpace != FilmNegativeParams::ColorSpace::INPUT) {
parent->ipf.filmNegativeProcess(baseCrop, baseCrop, params.filmNegative);
}
if (params.cg.enabled) {//gamut compression
parent->ipf.gamutcompr(baseCrop, baseCrop);
}
delete [] min_r;
delete [] min_b;
delete [] lumL;
delete [] chromC;
delete [] ry;
delete [] sk;
delete [] pcsk;
delete [] centerTile_X;
delete [] centerTile_Y;
}
// has to be called after setCropSizes! Tools prior to this point can't handle the Edit mechanism, but that shouldn't be a problem.
createBuffer(cropw, croph);
// Apply Spot removal
if ((todo & M_SPOT) && !spotsDone) {
if (params.spot.enabled && !params.spot.entries.empty()) {
if(!spotCrop) {
spotCrop = new Imagefloat (cropw, croph);
}
baseCrop->copyData (spotCrop);
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
int tr = getCoarseBitMask(params.coarse);
parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, &params.icm, tr);
} else {
if (spotCrop) {
delete spotCrop;
spotCrop = nullptr;
}
}
}
if (spotCrop) {
baseCrop = spotCrop;
}
std::unique_ptr<Imagefloat> fattalCrop;
if ((todo & M_HDR) && (params.fattal.enabled || params.dehaze.enabled)) {
Imagefloat *f = origCrop;
int fw = skips(parent->fw, skip);
int fh = skips(parent->fh, skip);
bool need_cropping = false;
bool need_fattal = true;
if (trafx || trafy || trafw != fw || trafh != fh) {
need_cropping = true;
// fattal needs to work on the full image. So here we get the full
// image from imgsrc, and replace the denoised crop in case
if (!params.dirpyrDenoise.enabled && skip == 1 && parent->fattal_11_dcrop_cache) {
f = parent->fattal_11_dcrop_cache;
need_fattal = false;
} else {
f = new Imagefloat(fw, fh);
fattalCrop.reset(f);
PreviewProps pp(0, 0, parent->fw, parent->fh, skip);
int tr = getCoarseBitMask(params.coarse);
parent->imgsrc->getImage(parent->currWB, tr, f, pp, params.toneCurve, params.raw);
parent->imgsrc->convertColorSpace(f, params.icm, parent->currWB);
if (params.dirpyrDenoise.enabled || params.filmNegative.enabled || params.spot.enabled) {
// copy the denoised crop
int oy = trafy / skip;
int ox = trafx / skip;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < baseCrop->getHeight(); ++y) {
int dy = oy + y;
for (int x = 0; x < baseCrop->getWidth(); ++x) {
int dx = ox + x;
f->r(dy, dx) = baseCrop->r(y, x);
f->g(dy, dx) = baseCrop->g(y, x);
f->b(dy, dx) = baseCrop->b(y, x);
}
}
} else if (skip == 1) {
parent->fattal_11_dcrop_cache = f; // cache this globally
fattalCrop.release();
}
}
}
if (need_fattal) {
parent->ipf.dehaze(f, params.dehaze);
parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0, false);
}
// crop back to the size expected by the rest of the pipeline
if (need_cropping) {
Imagefloat *c = origCrop;
int oy = trafy / skip;
int ox = trafx / skip;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < trafh; ++y) {
int cy = y + oy;
for (int x = 0; x < trafw; ++x) {
int cx = x + ox;
c->r(y, x) = f->r(cy, cx);
c->g(y, x) = f->g(cy, cx);
c->b(y, x) = f->b(cy, cx);
}
}
baseCrop = c;
} else {
baseCrop = f;
}
}
const bool needstransform = parent->ipf.needsTransform(skips(parent->fw, skip), skips(parent->fh, skip), parent->imgsrc->getRotateDegree(), parent->imgsrc->getMetaData());
const bool cam02 = params.colorappearance.modelmethod == "02" && params.colorappearance.enabled;
// transform
// if (needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled)) {
if (needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !cam02)) {
if (!transCrop) {
transCrop = new Imagefloat(cropw, croph);
}
if (needstransform)
parent->ipf.transform(baseCrop, transCrop, cropx / skip, cropy / skip, trafx / skip, trafy / skip, skips(parent->fw, skip), skips(parent->fh, skip), parent->getFullWidth(), parent->getFullHeight(),
parent->imgsrc->getMetaData(),
parent->imgsrc->getRotateDegree(), false);
else {
baseCrop->copyData(transCrop);
}
if (transCrop) {
baseCrop = transCrop;
}
} else {
delete transCrop;
transCrop = nullptr;
}
// if ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled) {
if ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !cam02) {
const int W = baseCrop->getWidth();
const int H = baseCrop->getHeight();
LabImage labcbdl(W, H);
parent->ipf.rgb2lab(*baseCrop, labcbdl, params.icm.workingProfile);
parent->ipf.dirpyrequalizer(&labcbdl, skip);
parent->ipf.lab2rgb(labcbdl, *baseCrop, params.icm.workingProfile);
}
if ((todo & (M_AUTOEXP | M_RGBCURVE)) && params.locallab.enabled && !params.locallab.spots.empty()) {
//I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here.
parent->ipf.rgb2lab(*baseCrop, *laboCrop, params.icm.workingProfile);
labnCrop->CopyFrom(laboCrop);
const std::unique_ptr<LabImage> reservCrop(new LabImage(*laboCrop, true));
const std::unique_ptr<LabImage> lastorigCrop(new LabImage(*laboCrop, true));
std::unique_ptr<LabImage> savenormtmCrop;
std::unique_ptr<LabImage> savenormretiCrop;
auto& lllocalcurve2 = parent->lllocalcurve;
auto& cllocalcurve2 = parent->cllocalcurve;
auto& lclocalcurve2 = parent->lclocalcurve;
auto& cclocalcurve2 = parent->cclocalcurve;
auto& rgblocalcurve2 = parent->rgblocalcurve;
auto& exlocalcurve2 = parent->exlocalcurve;
auto& lmasklocalcurve2 = parent->lmasklocalcurve;
auto& lmaskexplocalcurve2 = parent->lmaskexplocalcurve;
auto& lmaskSHlocalcurve2 = parent->lmaskSHlocalcurve;
auto& lmaskviblocalcurve2 = parent->lmaskviblocalcurve;
auto& lmasktmlocalcurve2 = parent->lmasktmlocalcurve;
auto& lmaskretilocalcurve2 = parent->lmaskretilocalcurve;
auto& lmaskcblocalcurve2 = parent->lmaskcblocalcurve;
auto& lmaskbllocalcurve2 = parent->lmaskbllocalcurve;
auto& lmasklclocalcurve2 = parent->lmasklclocalcurve;
auto& lmaskloglocalcurve2 = parent->lmaskloglocalcurve;
auto& lmaskcielocalcurve2 = parent->lmaskcielocalcurve;
auto& cielocalcurve2 = parent->cielocalcurve;
auto& cielocalcurve22 = parent->cielocalcurve2;
auto& jzlocalcurve2 = parent->jzlocalcurve;
auto& czlocalcurve2 = parent->czlocalcurve;
auto& czjzlocalcurve2 = parent->czjzlocalcurve;
auto& hltonecurveloc2 = parent->hltonecurveloc;
auto& shtonecurveloc2 = parent->shtonecurveloc;
auto& tonecurveloc2 = parent->tonecurveloc;
auto& lightCurveloc2 = parent->lightCurveloc;
auto& locRETgainCurve = parent->locRETgainCurve;
auto& locRETtransCurve = parent->locRETtransCurve;
auto& loclhCurve = parent->loclhCurve;
auto& lochhCurve = parent->lochhCurve;
auto& locchCurve = parent->locchCurve;
auto& lochhCurvejz = parent->lochhCurvejz;
auto& locchCurvejz = parent->locchCurvejz;
auto& loclhCurvejz = parent->loclhCurvejz;
auto& locccmasCurve = parent->locccmasCurve;
auto& locllmasCurve = parent->locllmasCurve;
auto& lochhmasCurve = parent->lochhmasCurve;
auto& lochhhmasCurve = parent->lochhhmasCurve;
auto& lochhhmascieCurve = parent->lochhhmascieCurve;
auto& locccmasexpCurve = parent->locccmasexpCurve;
auto& locllmasexpCurve = parent->locllmasexpCurve;
auto& lochhmasexpCurve = parent->lochhmasexpCurve;
auto& locccmasSHCurve = parent->locccmasSHCurve;
auto& locllmasSHCurve = parent->locllmasSHCurve;
auto& lochhmasSHCurve = parent->lochhmasSHCurve;
auto& locccmasvibCurve = parent->locccmasvibCurve;
auto& locllmasvibCurve = parent->locllmasvibCurve;
auto& lochhmasvibCurve = parent->lochhmasvibCurve;
auto& locccmaslcCurve = parent->locccmaslcCurve;
auto& locllmaslcCurve = parent->locllmaslcCurve;
auto& lochhmaslcCurve = parent->lochhmaslcCurve;
auto& locccmascbCurve = parent->locccmascbCurve;
auto& locllmascbCurve = parent->locllmascbCurve;
auto& lochhmascbCurve = parent->lochhmascbCurve;
auto& locccmasretiCurve = parent->locccmasretiCurve;
auto& locllmasretiCurve = parent->locllmasretiCurve;
auto& lochhmasretiCurve = parent->lochhmasretiCurve;
auto& locccmastmCurve = parent->locccmastmCurve;
auto& locllmastmCurve = parent->locllmastmCurve;
auto& lochhmastmCurve = parent->lochhmastmCurve;
auto& locccmasblCurve = parent->locccmasblCurve;
auto& locllmasblCurve = parent->locllmasblCurve;
auto& lochhmasblCurve = parent->lochhmasblCurve;
auto& locccmaslogCurve = parent->locccmaslogCurve;
auto& locllmaslogCurve = parent->locllmaslogCurve;
auto& lochhmaslogCurve = parent->lochhmaslogCurve;
auto& locccmascieCurve = parent->locccmascieCurve;
auto& locllmascieCurve = parent->locllmascieCurve;
auto& lochhmascieCurve = parent->lochhmascieCurve;
auto& locccmas_Curve = parent->locccmas_Curve;
auto& locllmas_Curve = parent->locllmas_Curve;
auto& lochhmas_Curve = parent->lochhmas_Curve;
auto& lochhhmas_Curve = parent->lochhhmas_Curve;
auto& locwavCurve = parent->locwavCurve;
auto& locwavCurvejz = parent->locwavCurvejz;
auto& loclmasCurveblwav = parent->loclmasCurveblwav;
auto& loclmasCurvecolwav = parent->loclmasCurvecolwav;
auto& loclmasCurveciewav = parent->loclmasCurveciewav;
auto& loclevwavCurve = parent->loclevwavCurve;
auto& locconwavCurve = parent->locconwavCurve;
auto& loccompwavCurve = parent->loccompwavCurve;
auto& loccomprewavCurve = parent->loccomprewavCurve;
auto& locedgwavCurve = parent->locedgwavCurve;
auto& locwavCurvehue = parent->locwavCurvehue;
auto& locwavCurveden = parent->locwavCurveden;
auto& lmasklocal_curve2 = parent->lmasklocal_curve;
auto& loclmasCurve_wav = parent->loclmasCurve_wav;
for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) {
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 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 lmasutili_wav = loclmasCurve_wav.Set(params.locallab.spots.at(sp).LLmask_curvewav);
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 lhmaslcutili = lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve);
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, lllocalcurve2, skip);
const bool localclutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).clcurve, cllocalcurve2, skip);
const bool locallcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).lccurve, lclocalcurve2, skip);
const bool localrgbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).rgbcurve, rgblocalcurve2, skip);
const bool localcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).cccurve, cclocalcurve2, skip);
const bool localexutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).excurve, exlocalcurve2, skip);
const bool localmaskutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve2, skip);
const bool localmaskexputili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve2, skip);
const bool localmaskSHutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve2, skip);
const bool localmaskvibutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve2, skip);
const bool localmasktmutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve2, skip);
const bool localmaskretiutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve2, skip);
const bool localmaskcbutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve2, skip);
const bool localmasklcutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, skip);
const bool localmaskblutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, skip);
const bool localmasklogutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).LmaskcurveL, lmaskloglocalcurve2, skip);
const bool localmask_utili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmask_curve, lmasklocal_curve2, skip);
const bool localmaskcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).Lmaskciecurve, lmaskcielocalcurve2, skip);
const bool localcieutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve, cielocalcurve2, skip);
const bool localcieutili2 = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).ciecurve2, cielocalcurve22, skip);
const bool localjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).jzcurve, jzlocalcurve2, skip);
const bool localczutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czcurve, czlocalcurve2, skip);
const bool localczjzutili = CurveFactory::diagonalCurve2Lut(params.locallab.spots.at(sp).czjzcurve, czjzlocalcurve2, skip);
double ecomp = params.locallab.spots.at(sp).expcomp;
double black = params.locallab.spots.at(sp).black;
double hlcompr = params.locallab.spots.at(sp).hlcompr;
double hlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh;
double shcompr = params.locallab.spots.at(sp).shcompr;
double br = params.locallab.spots.at(sp).lightness;
if (black < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) {
black *= 1.5;
}
std::vector<LocallabListener::locallabDenoiseLC> localldenoiselc;
double cont = params.locallab.spots.at(sp).contrast;
double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre;
huerefblu = parent->huerefblurs[sp];
chromarefblu = parent->chromarefblurs[sp];
lumarefblu = parent->lumarefblurs[sp];
huere = parent->huerefs[sp];
chromare = parent->chromarefs[sp];
lumare = parent->lumarefs[sp];
sobelre = parent->sobelrefs[sp];
const float avge = parent->avgs[sp];
float meantme = parent->meantms[sp];
float stdtme = parent->stdtms[sp];
float meanretie = parent->meanretis[sp];
float stdretie = parent->stdretis[sp];
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;
float minCD;
float maxCD;
float mini;
float maxi;
float Tmean;
float Tsigma;
float Tmin;
float Tmax;
int lastsav;
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 contsig = params.locallab.spots.at(sp).contsigqcie;
float lightsig = params.locallab.spots.at(sp).lightsigqcie;
/* huerefp[sp] = huere;
chromarefp[sp] = chromare;
lumarefp[sp] = lumare;
*/
CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare,
hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, avge,
skip);
// Locallab mask are only shown for selected spot
int fh = parent->fh;
int fw = parent->fw;
if (sp == params.locallab.selspot) {
parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), savenormtmCrop.get(), savenormretiCrop.get(), lastorigCrop.get(), fw, fh, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve,
lllocalcurve2,locallutili,
cllocalcurve2, localclutili,
lclocalcurve2, locallcutili,
loclhCurve, lochhCurve, locchCurve,
lochhCurvejz, locchCurvejz, loclhCurvejz,
lmasklocalcurve2, localmaskutili,
lmaskexplocalcurve2, localmaskexputili,
lmaskSHlocalcurve2, localmaskSHutili,
lmaskviblocalcurve2, localmaskvibutili,
lmasktmlocalcurve2, localmasktmutili,
lmaskretilocalcurve2, localmaskretiutili,
lmaskcblocalcurve2, localmaskcbutili,
lmaskbllocalcurve2, localmaskblutili,
lmasklclocalcurve2, localmasklcutili,
lmaskloglocalcurve2, localmasklogutili,
lmasklocal_curve2, localmask_utili,
lmaskcielocalcurve2, localmaskcieutili,
cielocalcurve2,localcieutili,
cielocalcurve22,localcieutili2,
jzlocalcurve2,localjzutili,
czlocalcurve2,localczutili,
czjzlocalcurve2,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, llmasSHutili, 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, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2,
huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav,
parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask,
parent->localllogMask, parent->locall_Mask, parent->locallcieMask, 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);
LocallabListener::locallabDenoiseLC denoiselc;
denoiselc.highres = highresi;
denoiselc.nres = nresi;
denoiselc.highres46 = highresi46;
denoiselc.nres46 = nresi46;
denoiselc.Lhighres = Lhighresi;
denoiselc.Lnres = Lnresi;
denoiselc.Lhighres46 = Lhighresi46;
denoiselc.Lnres46 = Lnresi46;
localldenoiselc.push_back(denoiselc);
if (parent->previewDeltaE || parent->locallColorMask == 5 || parent->locallvibMask == 4 || parent->locallExpMask == 5 || parent->locallSHMask == 4 || parent->localllcMask == 4 || parent->localltmMask == 4 || parent->localllogMask == 4 || parent->locallsoftMask == 6 || parent->localllcMask == 4 || parent->locallcieMask == 4) {
params.blackwhite.enabled = false;
params.colorToning.enabled = false;
params.rgbCurves.enabled = false;
params.chmixer.enabled = false;
params.hsvequalizer.enabled = false;
params.filmSimulation.enabled = false;
params.toneCurve.black = 0.f;
params.toneCurve.saturation = 0.f;
params.toneCurve.brightness= 0.f;
params.toneCurve.contrast = 0.f;
params.toneCurve.hlcompr = 0.f;
//these 3 are "before" LA
//params.toneCurve.expcomp = 0;
//params.toneCurve.curve = { 0 };
//params.toneCurve.curve2 = { 0 };
params.colorappearance.enabled = false;
params.vibrance.enabled = false;
params.labCurve.enabled = false;
params.wavelet.enabled = false;
params.epd.enabled = false;
params.softlight.enabled = false;
}
/*
if (parent->locallListener) {
parent->locallListener->refChanged2(huerefp, chromarefp, lumarefp, fabrefp, params.locallab.selspot);
}
*/
denoiselc.highres = highresi;
denoiselc.nres = nresi;
denoiselc.highres46 = highresi46;
denoiselc.nres46 = nresi46;
denoiselc.Lhighres = Lhighresi;
denoiselc.Lnres = Lnresi;
denoiselc.Lhighres46 = Lhighresi46;
denoiselc.Lnres46 = Lnresi46;
localldenoiselc.push_back(denoiselc);
if (parent->locallListener) {
parent->locallListener->denChanged(localldenoiselc, params.locallab.selspot);
}
} else {
parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), savenormtmCrop.get(), savenormretiCrop.get(), lastorigCrop.get(), fw, fh, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve,
lllocalcurve2,locallutili,
cllocalcurve2, localclutili,
lclocalcurve2, locallcutili,
loclhCurve, lochhCurve, locchCurve,
lochhCurvejz, locchCurvejz, loclhCurvejz,
lmasklocalcurve2, localmaskutili,
lmaskexplocalcurve2, localmaskexputili,
lmaskSHlocalcurve2, localmaskSHutili,
lmaskviblocalcurve2, localmaskvibutili,
lmasktmlocalcurve2, localmasktmutili,
lmaskretilocalcurve2, localmaskretiutili,
lmaskcblocalcurve2, localmaskcbutili,
lmaskbllocalcurve2, localmaskblutili,
lmasklclocalcurve2, localmasklcutili,
lmaskloglocalcurve2, localmasklogutili,
lmasklocal_curve2, localmask_utili,
lmaskcielocalcurve2, localmaskcieutili,
cielocalcurve2,localcieutili,
cielocalcurve22,localcieutili2,
jzlocalcurve2,localjzutili,
czlocalcurve2,localczutili,
czjzlocalcurve2,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, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2,
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);
}
if (sp + 1u < params.locallab.spots.size()) {
// do not copy for last spot as it is not needed anymore
lastorigCrop->CopyFrom(labnCrop);
}
if (skip <= 2) {
Glib::usleep(settings->cropsleep); //wait to avoid crash when crop 100% and move window
}
}
/*
delete [] huerefp;
delete [] chromarefp;
delete [] lumarefp;
delete [] fabrefp;
*/
parent->ipf.lab2rgb(*labnCrop, *baseCrop, params.icm.workingProfile);
}
if (todo & M_RGBCURVE) {
double rrm, ggm, bbm;
DCPProfileApplyState as;
DCPProfile *dcpProf = parent->imgsrc->getDCP(params.icm, as);
LUTu histToneCurve;
parent->ipf.rgbProc (baseCrop, laboCrop, this, parent->hltonecurve, parent->shtonecurve, parent->tonecurve,
params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, parent->colourToningSatLimit, parent->colourToningSatLimitOpacity, parent->ctColorCurve, parent->ctOpacityCurve, parent->opautili, parent->clToningcurve, parent->cl2Toningcurve,
parent->customToneCurve1, parent->customToneCurve2, parent->beforeToneCurveBW, parent->afterToneCurveBW, rrm, ggm, bbm,
parent->bwAutoR, parent->bwAutoG, parent->bwAutoB, dcpProf, as, histToneCurve);
}
// apply luminance operations
if (todo & (M_LUMINANCE + M_COLOR)) { //
//I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here.
labnCrop->CopyFrom(laboCrop);
bool utili = parent->utili;
bool autili = parent->autili;
bool butili = parent->butili;
bool ccutili = parent->ccutili;
bool clcutili = parent->clcutili;
bool cclutili = parent->cclutili;
LUTu dummy;
if (params.colorToning.enabled && params.colorToning.method == "LabGrid") {
parent->ipf.colorToningLabGrid(labnCrop, 0,labnCrop->W , 0, labnCrop->H, false);
}
parent->ipf.shadowsHighlights(labnCrop, params.sh.enabled, params.sh.lab,params.sh.highlights ,params.sh.shadows, params.sh.radius, skip, params.sh.htonalwidth, params.sh.stonalwidth);
if (params.localContrast.enabled) {
// Alberto's local contrast
parent->ipf.localContrast(labnCrop, labnCrop->L, params.localContrast, false, skip);
}
parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy);
parent->ipf.vibrance(labnCrop, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile);
parent->ipf.labColorCorrectionRegions(labnCrop);
// if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!cam02)) {
parent->ipf.EPDToneMap(labnCrop, 0, skip);
}
//parent->ipf.EPDToneMap(labnCrop, 5, 1); //Go with much fewer than normal iterates for fast redisplay.
// for all treatments Defringe, Sharpening, Contrast detail , Microcontrast they are activated if "CIECAM" function are disabled
if (skip == 1) {
// if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) {
parent->ipf.impulsedenoise(labnCrop);
parent->ipf.defringe(labnCrop);
}
parent->ipf.MLsharpen(labnCrop);
// if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
if ((params.colorappearance.enabled && !settings->autocielab) || (!cam02)) {
parent->ipf.MLmicrocontrast(labnCrop);
parent->ipf.sharpening(labnCrop, params.sharpening, parent->sharpMask);
}
}
// if (skip==1) {
if (params.dirpyrequalizer.cbdlMethod == "aft") {
// if (((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled))) {
if (((params.colorappearance.enabled && !settings->autocielab) || (!cam02))) {
parent->ipf.dirpyrequalizer(labnCrop, skip);
// parent->ipf.Lanczoslab (labnCrop,labnCrop , 1.f/skip);
}
}
if ((params.wavelet.enabled)) {
WaveletParams WaveParams = params.wavelet;
int kall = 0;
int minwin = min(labnCrop->W, labnCrop->H);
int maxlevelcrop = 10;
// if (cp.mul[9]!=0)maxlevelcrop=10;
// adap maximum level wavelet to size of crop
if (minwin * skip < 1024) {
maxlevelcrop = 9; //sampling wavelet 512
}
if (minwin * skip < 512) {
maxlevelcrop = 8; //sampling wavelet 256
}
if (minwin * skip < 256) {
maxlevelcrop = 7; //sampling 128
}
if (minwin * skip < 128) {
maxlevelcrop = 6;
}
if (minwin < 64) {
maxlevelcrop = 5;
}
int realtile;
if (params.wavelet.Tilesmethod == "big") {
realtile = 22;
} else /*if (params.wavelet.Tilesmethod == "lit")*/ {
realtile = 12;
}
int tilesize = 128 * realtile;
int overlap = (int) tilesize * 0.125f;
int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip;
parent->ipf.Tile_calc(tilesize, overlap, kall, labnCrop->W, labnCrop->H, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip);
//now we have tile dimensions, overlaps
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int minsizetile = min(tilewidth, tileheight);
int maxlev2 = 10;
if (minsizetile < 1024 && maxlevelcrop == 10) {
maxlev2 = 9;
}
if (minsizetile < 512) {
maxlev2 = 8;
}
if (minsizetile < 256) {
maxlev2 = 7;
}
if (minsizetile < 128) {
maxlev2 = 6;
}
int maxL = min(maxlev2, maxlevelcrop);
if (parent->awavListener) {
parent->awavListener->wavChanged(float (maxL));
}
WavCurve wavCLVCurve;
WavCurve wavdenoise;
WavCurve wavdenoiseh;
Wavblcurve wavblcurve;
WavOpacityCurveRG waOpacityCurveRG;
WavOpacityCurveSH waOpacityCurveSH;
WavOpacityCurveBY waOpacityCurveBY;
WavOpacityCurveW waOpacityCurveW;
WavOpacityCurveWL waOpacityCurveWL;
LUTf wavclCurve;
params.wavelet.getCurves(wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL);
LabImage *unshar = nullptr;
Glib::ustring provis;
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 (WaveParams.showmask) {
// WaveParams.showmask = false;
// WaveParams.expclari = true;
}
if (WaveParams.softrad > 0.f) {
provradius = new LabImage(*labnCrop, true);
}
if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") {
provis = params.wavelet.CLmethod;
params.wavelet.CLmethod = "all";
parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip);
unshar = new LabImage(*labnCrop, true);
params.wavelet.CLmethod = provis;
WaveParams.expcontrast = false;
WaveParams.expchroma = false;
WaveParams.expedge = false;
WaveParams.expfinal = false;
WaveParams.exptoning = false;
WaveParams.expnoise = false;
}
// parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip);
parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip);
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(labnCrop->W, labnCrop->H);
array2D<float> guid(labnCrop->W, labnCrop->H);
Imagefloat *tmpImage = nullptr;
tmpImage = new Imagefloat(labnCrop->W, labnCrop->H);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < labnCrop->H ; ir++)
for (int jr = 0; jr < labnCrop->W; 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 = labnCrop->L[ir][jr];
float aa = labnCrop->a[ir][jr];
float ba = labnCrop->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 / skip * (0.5f + 0.8f * WaveParams.softrad);
rtengine::guidedFilter(guid, ble, ble, blur, epsil, false);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < labnCrop->H; ir++)
for (int jr = 0; jr < labnCrop->W; 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);
labnCrop->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;
float background = 0.f;
int show = 0;
if ((WaveParams.CLmethod == "one" || WaveParams.CLmethod == "inf") && WaveParams.Backmethod == "black") {
mL0 = mC0 = 0.f;
mL = -1.5f * mL;
mC = -mC;
background = 12000.f;
show = 0;
} else if (WaveParams.CLmethod == "sup" && WaveParams.Backmethod == "resid") {
mL0 = mL;
mC0 = mC;
background = 0.f;
show = 0;
} else {
mL0 = mL = mC0 = mC = 0.f;
background = 0.f;
show = 0;
}
float indic = 1.f;
if (WaveParams.showmask){
mL0 = mC0 = -1.f;
indic = -1.f;
mL = fabs(mL);
mC = fabs(mC);
show = 1;
}
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int x = 0; x < labnCrop->H; x++)
for (int y = 0; y < labnCrop->W; y++) {
labnCrop->L[x][y] = LIM((1.f + mL0) * (unshar->L[x][y]) + show * background - mL * indic * labnCrop->L[x][y], 0.f, 32768.f);
labnCrop->a[x][y] = (1.f + mC0) * (unshar->a[x][y]) - mC * indic * labnCrop->a[x][y];
labnCrop->b[x][y] = (1.f + mC0) * (unshar->b[x][y]) - mC * indic * labnCrop->b[x][y];
}
delete unshar;
unshar = NULL;
if (WaveParams.softrad > 0.f) {
delete provradius;
provradius = NULL;
}
}
}
parent->ipf.softLight(labnCrop, params.softlight);
if (params.icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params.icm.trcExp) {
const int GW = labnCrop->W;
const int GH = labnCrop->H;
std::unique_ptr<LabImage> provis;
const float pres = 0.01f * params.icm.preser;
if (pres > 0.f && params.icm.wprim != ColorManagementParams::Primaries::DEFAULT) {
provis.reset(new LabImage(GW, GH));
provis->CopyFrom(labnCrop);
}
const std::unique_ptr<Imagefloat> tmpImage1(new Imagefloat(GW, GH));
parent->ipf.lab2rgb(*labnCrop, *tmpImage1, params.icm.workingProfile);
const float gamtone = parent->params->icm.workingTRCGamma;
const float slotone = parent->params->icm.workingTRCSlope;
int illum = rtengine::toUnderlying(params.icm.will);
const int prim = rtengine::toUnderlying(params.icm.wprim);
Glib::ustring prof = params.icm.workingProfile;
cmsHTRANSFORM cmsDummy = nullptr;
int ill = 0;
int locprim = 0;
bool gamutcontrol = params.icm.gamut;
int catc = rtengine::toUnderlying(params.icm.wcat);
float rdx, rdy, grx, gry, blx, bly = 0.f;
float meanx, meany, meanxe, meanye = 0.f;
parent->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, cmsDummy, true, false, false, false);
parent->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, cmsDummy, 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);
}
parent->ipf.toneEqualizer(tmpImage1.get(), params, prof, skip, 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;
}
parent->ipf.toneEqualizer(tmpImage1.get(), params, prof, skip, false);
}
parent->ipf.rgb2lab(*tmpImage1, *labnCrop, params.icm.workingProfile);
//labnCrop and provis
if (provis) {
parent->ipf.preserv(labnCrop, 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++) {
labnCrop->a[x][y] = 0.f;
labnCrop->b[x][y] = 0.f;
}
}
}
if (params.colorappearance.enabled) {
float fnum = parent->imgsrc->getMetaData()->getFNumber(); // F number
float fiso = parent->imgsrc->getMetaData()->getISOSpeed() ; // ISO
float fspeed = parent->imgsrc->getMetaData()->getShutterSpeed() ; // Speed
double fcomp = parent->imgsrc->getMetaData()->getExpComp(); // Compensation +/-
double adap; // Scene's luminosity adaptation factor
if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) { //if no exif data or wrong
adap = 2000.;
} else {
double E_V = fcomp + log2(double ((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 = pow(2., E_V - 3.); // cd / m2
// end calculation adaptation scene luminosity
}
bool execsharp = false;
if (skip == 1) {
execsharp = true;
}
if (!cieCrop) {
cieCrop = new CieImage(cropw, croph);
}
float d, dj, yb; // not used after this block
parent->ipf.ciecam_02float(cieCrop, float (adap), 1, 2, labnCrop, &params, parent->customColCurve1, parent->customColCurve2, parent->customColCurve3,
dummy, dummy, parent->CAMBrightCurveJ, parent->CAMBrightCurveQ, parent->CAMMean, 0, skip, execsharp, d, dj, yb, 1, parent->sharpMask);
} else {
// CIECAM is disabled, we free up its image buffer to save some space
if (cieCrop) {
delete cieCrop;
}
cieCrop = nullptr;
}
}
// all pipette buffer processing should be finished now
PipetteBuffer::setReady();
// Computing the preview image, i.e. converting from lab->Monitor color space (soft-proofing disabled) or lab->Output profile->Monitor color space (soft-proofing enabled)
parent->ipf.lab2monitorRgb(labnCrop, cropImg);
if (cropImageListener) {
// Computing the internal image for analysis, i.e. conversion from lab->Output profile (rtSettings.HistogramWorking disabled) or lab->WCS (rtSettings.HistogramWorking enabled)
// internal image in output color space for analysis
Image8 *cropImgtrue = parent->ipf.lab2rgb(labnCrop, 0, 0, cropw, croph, params.icm);
int finalW = rqcropw;
if (cropImg->getWidth() - leftBorder < finalW) {
finalW = cropImg->getWidth() - leftBorder;
}
int finalH = rqcroph;
if (cropImg->getHeight() - upperBorder < finalH) {
finalH = cropImg->getHeight() - upperBorder;
}
Image8* final = new Image8(finalW, finalH);
Image8* finaltrue = new Image8(finalW, finalH);
for (int i = 0; i < finalH; i++) {
memcpy(final->data + 3 * i * finalW, cropImg->data + 3 * (i + upperBorder)*cropw + 3 * leftBorder, 3 * finalW);
memcpy(finaltrue->data + 3 * i * finalW, cropImgtrue->data + 3 * (i + upperBorder)*cropw + 3 * leftBorder, 3 * finalW);
}
cropImageListener->setDetailedCrop(final, finaltrue, params.icm, params.crop, rqcropx, rqcropy, rqcropw, rqcroph, skip);
delete final;
delete finaltrue;
delete cropImgtrue;
}
}
void Crop::freeAll()
{
if (cropAllocated) {
if (origCrop) {
delete origCrop;
origCrop = nullptr;
}
if (transCrop) {
delete transCrop;
transCrop = nullptr;
}
if (laboCrop) {
delete laboCrop;
laboCrop = nullptr;
}
if (labnCrop) {
delete labnCrop;
labnCrop = nullptr;
}
if (cropImg) {
delete cropImg;
cropImg = nullptr;
}
if (cieCrop) {
delete cieCrop;
cieCrop = nullptr;
}
if (shbuffer) {
delete [] shbuffer;
shbuffer = nullptr;
}
if (shbuf_real) {
delete [] shbuf_real;
shbuf_real = nullptr;
}
PipetteBuffer::flush();
}
cropAllocated = false;
}
namespace
{
bool check_need_larger_crop_for_lcp_distortion(int fw, int fh, int x, int y, int w, int h, const procparams::ProcParams &params)
{
if (x == 0 && y == 0 && w == fw && h == fh) {
return false;
}
return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp() || params.lensProf.useMetadata()));
}
} // namespace
/** @brief Handles crop's image buffer reallocation and trigger sizeChanged of SizeListener[s]
* If the scale changes, this method will free all buffers and reallocate ones of the new size.
* It will then tell to the SizeListener that size has changed (sizeChanged)
*/
bool Crop::setCropSizes(int cropX, int cropY, int cropW, int cropH, int skip, bool internal)
{
if (!internal) {
cropMutex.lock();
}
bool changed = false;
rqcropx = cropX;
rqcropy = cropY;
rqcropw = cropW;
rqcroph = cropH;
// store and set requested crop size
int rqx1 = LIM(rqcropx, 0, parent->fullw - 1);
int rqy1 = LIM(rqcropy, 0, parent->fullh - 1);
int rqx2 = rqx1 + rqcropw - 1;
int rqy2 = rqy1 + rqcroph - 1;
rqx2 = LIM(rqx2, 0, parent->fullw - 1);
rqy2 = LIM(rqy2, 0, parent->fullh - 1);
this->skip = skip;
// add border, if possible
int bx1 = rqx1 - skip * borderRequested;
int by1 = rqy1 - skip * borderRequested;
int bx2 = rqx2 + skip * borderRequested;
int by2 = rqy2 + skip * borderRequested;
// clip it to fit into image area
bx1 = LIM(bx1, 0, parent->fullw - 1);
by1 = LIM(by1, 0, parent->fullh - 1);
bx2 = LIM(bx2, 0, parent->fullw - 1);
by2 = LIM(by2, 0, parent->fullh - 1);
int bw = bx2 - bx1 + 1;
int bh = by2 - by1 + 1;
// determine which part of the source image is required to compute the crop rectangle
int orx, ory, orw, orh;
orx = bx1;
ory = by1;
orw = bw;
orh = bh;
parent->ipf.transCoord(parent->fw, parent->fh, bx1, by1, bw, bh, orx, ory, orw, orh);
if (parent->ipf.needsTransform(skips(parent->fw, skip), skips(parent->fh, skip), parent->imgsrc->getRotateDegree(), parent->imgsrc->getMetaData())) {
if (check_need_larger_crop_for_lcp_distortion(parent->fw, parent->fh, orx, ory, orw, orh, *parent->params)) {
// TODO - this is an estimate of the max distortion relative to the image size. ATM it is hardcoded to be 15%, which seems enough. If not, need to revise
int dW = int (double (parent->fw) * 0.15 / (2 * skip));
int dH = int (double (parent->fh) * 0.15 / (2 * skip));
int x1 = orx - dW;
int x2 = orx + orw + dW;
int y1 = ory - dH;
int y2 = ory + orh + dH;
if (x1 < 0) {
x2 += -x1;
x1 = 0;
}
if (x2 > parent->fw) {
x1 -= x2 - parent->fw;
x2 = parent->fw;
}
if (y1 < 0) {
y2 += -y1;
y1 = 0;
}
if (y2 > parent->fh) {
y1 -= y2 - parent->fh;
y2 = parent->fh;
}
orx = max(x1, 0);
ory = max(y1, 0);
orw = min(x2 - x1, parent->fw - orx);
orh = min(y2 - y1, parent->fh - ory);
}
}
leftBorder = skips(rqx1 - bx1, skip);
upperBorder = skips(rqy1 - by1, skip);
PreviewProps cp(orx, ory, orw, orh, skip);
int orW, orH;
parent->imgsrc->getSize(cp, orW, orH);
if (trafx != orx || trafy != ory) {
trafx = orx;
trafy = ory;
changed = true;
}
int cw = skips(bw, skip);
int ch = skips(bh, skip);
EditType editType = ET_PIPETTE;
if (const auto editProvider = PipetteBuffer::getDataProvider()) {
if (const auto editSubscriber = editProvider->getCurrSubscriber()) {
editType = editSubscriber->getEditingType();
}
}
if (cw != cropw || ch != croph || orW != trafw || orH != trafh) {
cropw = cw;
croph = ch;
trafw = orW;
trafh = orH;
if (!origCrop) {
origCrop = new Imagefloat;
}
origCrop->allocate(trafw, trafh); // Resizing the buffer (optimization)
// if transCrop doesn't exist yet, it'll be created where necessary
if (transCrop) {
transCrop->allocate(cropw, croph);
}
if (laboCrop) {
delete laboCrop; // laboCrop can't be resized
}
laboCrop = new LabImage(cropw, croph);
// if (translabCrop) translabCrop->reallocLab();
if (labnCrop) {
delete labnCrop; // labnCrop can't be resized
}
labnCrop = new LabImage(cropw, croph);
if (!cropImg) {
cropImg = new Image8;
}
cropImg->allocate(cropw, croph); // Resizing the buffer (optimization)
//cieCrop is only used in Crop::update, it is destroyed now but will be allocated on first use
if (cieCrop) {
delete cieCrop;
cieCrop = nullptr;
}
if (shbuffer) {
delete [] shbuffer;
}
if (shbuf_real) {
delete [] shbuf_real;
}
shbuffer = new float*[croph];
shbuf_real = new float[(croph + 2)*cropw];
for (int i = 0; i < croph; i++) {
shbuffer[i] = shbuf_real + cropw * i + cropw;
}
if (editType == ET_PIPETTE) {
PipetteBuffer::resize(cropw, croph);
} else if (PipetteBuffer::bufferCreated()) {
PipetteBuffer::flush();
}
cropAllocated = true;
changed = true;
}
cropx = bx1;
cropy = by1;
if (!internal) {
cropMutex.unlock();
}
return changed;
}
/** @brief Look out if a new thread has to be started to process the update
*
* @return If true, a new updating thread has to be created. If false, the current updating thread will be used
*/
bool Crop::tryUpdate()
{
bool needsNewThread = true;
if (updating) {
// tells to the updater thread that a new update is pending
newUpdatePending = true;
// no need for a new thread, the current one will do the job
needsNewThread = false;
} else
// the crop is now being updated ...well, when fullUpdate will be called
{
updating = true;
}
return needsNewThread;
}
/* @brief Handles Crop updating in its own thread
*
* This method will cycle updates as long as Crop::newUpdatePending will be true. During the processing,
* intermediary update will be automatically flushed by Crop::tryUpdate.
*
* This method is called when the visible part of the crop has changed (resize, zoom, etc..), so it needs a full update
*/
void Crop::fullUpdate()
{
parent->updaterThreadStart.lock();
if (parent->updaterRunning && parent->thread) {
// Do NOT reset changes here, since in a long chain of events it will lead to chroma_scale not being updated,
// causing Color::lab2rgb to return a black image on some opens
//parent->changeSinceLast = 0;
parent->thread->join();
}
if (parent->plistener) {
parent->plistener->setProgressState(true);
}
// If there are more update request, the following WHILE will collect it
newUpdatePending = true;
while (newUpdatePending) {
newUpdatePending = false;
update(ALL);
}
updating = false; // end of crop update
if (parent->plistener) {
parent->plistener->setProgressState(false);
}
parent->updaterThreadStart.unlock();
}
int Crop::get_skip()
{
MyMutex::MyLock lock(cropMutex);
return skip;
}
int Crop::getLeftBorder()
{
MyMutex::MyLock lock(cropMutex);
return leftBorder;
}
int Crop::getUpperBorder()
{
MyMutex::MyLock lock(cropMutex);
return upperBorder;
}
}