Files
rawTherapee/rtengine/dcrop.cc
rom9 e635030650 FilmNegative: non-raw file support and better accuracy (#5798)
* Added support for non-raw files to the film negative tool. The tool is now under the "Color" tab. Moved the entire filmneg code downstream, right after input profile conversion.
Usage changes a bit: White Balance must be set to the _backlight_ color temperature. Added two more sliders to color-balance the picture after negative inversion. Legacy inversion method kept for compatibility with processing profiles saved by RT v5.7 or 5.8 (only if Film Negative was enabled). Should be removed in a future version.
There is still an issue with DCP profiles that need the look table to be active to work properly. Using a simple matrix input profile (or just camera standard) is recommended for now.

* The user can now choose to perform inversion before or after input colorspace conversion.
Seamless backwards compatibility with previous processing profiles; upgrading from a previous version now gives an (almost) identical output as before.
Generalised the concept of film base values: the processing profile now contains a pair of RGB triplets, "reference input" and "reference output", which makes it much more straightforward to compute the output multipliers.

* Added support for `RGB` data type to putToKeyFile, removed the now unused `RGB::toVector()` method. Some cleanup.

* Spot balance picker now stays active indefinitely. Can be disabled by right-clicking, too.

* Removed film negative from `filterRawRefresh` condition, since the new version does not require raw rendering anymore.

* The Output Level slider is now exponential, so it should feel more familiar to use (similar to the exposure compensation slider).

* Removed old `RedBase`, `GreenBase`, `BlueBase` keys from the PP3 file after params upgrade. Keys are only kept if the file only undergoes batch edit (hence no params upgrade was done).

* Made the balance sliders exponential and centered at zero. Now they should be a bit smoother and possibly more user-friendly.

* Changed adjusters' step to more useful values, they were too fine-grained and hard to adjust using the +/- spinbutton.
Increased max ratio value from 3 to 5, as i found an old negative needing a very high blue channel exponent.

* Added an initial processing profile for film negative inversion, to be provided as a bundled profile.

* Removed Output Level and balance sliders when in batch mode: since the effect of these sliders is dependent on the reference input values, those values needed to be copied as well. And, touching any of the sliders needed to flag all three as dirty. All this felt more confusing than useful.
It should be sufficient (and clearer) to copy/paste the params, and then fine-tune the balance on individual pictures when needed.

* Set bayer demosaic method to RCD in the bundled "Film Negative" processing profile. RCD seems to play a bit better with Capture Sharpening in the presence of film grain, compared to Amaze.
This will favor new users starting with all-defaults settings, hence having Capture Sharpening enabled by default.

* Removed incorrect "contrast" term from the "Reference exponent" label. This parameter adjusts the image _gamma_, not its contrast.
2020-11-21 13:29:47 +01:00

1853 lines
82 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 "cieimage.h"
#include "curves.h"
#include "dcp.h"
#include "dcrop.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 "color.h"
#include "../rtgui/editcallbacks.h"
#include "guidedfilter.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), 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 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;
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);
}
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_LINDENOISE) {
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);
}
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);
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) {
// 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);
}
// 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());
// transform
if (needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params.dirpyrequalizer.cbdlMethod == "bef" && params.dirpyrequalizer.enabled && !params.colorappearance.enabled)) {
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) {
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_RGBCURVE) {
Imagefloat *workingCrop = baseCrop;
/*
if (params.icm.workingTRC == "Custom") { //exec TRC IN free
const Glib::ustring profile = params.icm.workingProfile;
if (profile == "sRGB" || profile == "Adobe RGB" || profile == "ProPhoto" || profile == "WideGamut" || profile == "BruceRGB" || profile == "Beta RGB" || profile == "BestRGB" || profile == "Rec2020" || profile == "ACESp0" || profile == "ACESp1") {
const int cw = baseCrop->getWidth();
const int ch = baseCrop->getHeight();
workingCrop = new Imagefloat(cw, ch);
//first put gamma TRC to 1
parent->ipf.workingtrc(baseCrop, workingCrop, cw, ch, -5, params.icm.workingProfile, 2.4, 12.92310, parent->getCustomTransformIn(), true, false, true);
//adjust gamma TRC
parent->ipf.workingtrc(workingCrop, workingCrop, cw, ch, 5, params.icm.workingProfile, params.icm.workingTRCGamma, params.icm.workingTRCSlope, parent->getCustomTransformOut(), false, true, true);
}
}
*/
double rrm, ggm, bbm;
DCPProfileApplyState as;
DCPProfile *dcpProf = parent->imgsrc->getDCP(params.icm, as);
LUTu histToneCurve;
parent->ipf.rgbProc (workingCrop, 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);
if (workingCrop != baseCrop) {
delete workingCrop;
}
}
/*xref=000;yref=000;
if (colortest && cropw>115 && croph>115)
for(int j=1;j<5;j++){
xref+=j*30;yref+=j*30;
if (settings->verbose) {
printf("after rgbProc RGB Xr%i Yr%i Skip=%d R=%f G=%f B=%f \n",xref,yref,skip,
baseCrop->r[(int)(xref/skip)][(int)(yref/skip)]/256,
baseCrop->g[(int)(xref/skip)][(int)(yref/skip)]/256,
baseCrop->b[(int)(xref/skip)][(int)(yref/skip)]/256);
printf("after rgbProc Lab Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip,
laboCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327,
laboCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327,
laboCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327);
}
}*/
// 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);
if (params.locallab.enabled && !params.locallab.spots.empty()) {
const std::unique_ptr<LabImage> reservCrop(new LabImage(*laboCrop, true));
const std::unique_ptr<LabImage> lastorigCrop(new LabImage(*laboCrop, true));
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& 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& locccmasCurve = parent->locccmasCurve;
auto& locllmasCurve = parent->locllmasCurve;
auto& lochhmasCurve = parent->lochhmasCurve;
auto& lochhhmasCurve = parent->lochhhmasCurve;
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& 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& loclmasCurveblwav = parent->loclmasCurveblwav;
auto& loclmasCurvecolwav = parent->loclmasCurvecolwav;
auto& loclevwavCurve = parent->loclevwavCurve;
auto& locconwavCurve = parent->locconwavCurve;
auto& loccompwavCurve = parent->loccompwavCurve;
auto& loccomprewavCurve = parent->loccomprewavCurve;
auto& locedgwavCurve = parent->locedgwavCurve;
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 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 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 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 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 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);
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;
}
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 minCD;
float maxCD;
float mini;
float maxi;
float Tmean;
float Tsigma;
float Tmin;
float Tmax;
int lastsav;
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
if (sp == params.locallab.selspot) {
parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), lastorigCrop.get(), cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve,
lllocalcurve2,locallutili,
cllocalcurve2, localclutili,
lclocalcurve2, locallcutili,
loclhCurve, lochhCurve, locchCurve,
lmasklocalcurve2, localmaskutili,
lmaskexplocalcurve2, localmaskexputili,
lmaskSHlocalcurve2, localmaskSHutili,
lmaskviblocalcurve2, localmaskvibutili,
lmasktmlocalcurve2, localmasktmutili,
lmaskretilocalcurve2, localmaskretiutili,
lmaskcblocalcurve2, localmaskcbutili,
lmaskbllocalcurve2, localmaskblutili,
lmasklclocalcurve2, localmasklcutili,
lmaskloglocalcurve2, localmasklogutili,
lmasklocal_curve2, localmask_utili,
locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, 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,
lochhhmas_Curve, lhhmas_utili,
loclmasCurveblwav,lmasutiliblwav,
loclmasCurvecolwav,lmasutilicolwav,
locwavCurve, locwavutili,
loclevwavCurve, loclevwavutili,
locconwavCurve, locconwavutili,
loccompwavCurve, loccompwavutili,
loccomprewavCurve, loccomprewavutili,
locwavCurveden, locwavdenutili,
locedgwavCurve, locedgwavutili,
loclmasCurve_wav,lmasutili_wav,
LHutili, HHutili, CHutili, 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, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax);
} else {
parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop.get(), lastorigCrop.get(), cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve,
lllocalcurve2,locallutili,
cllocalcurve2, localclutili,
lclocalcurve2, locallcutili,
loclhCurve, lochhCurve, locchCurve,
lmasklocalcurve2, localmaskutili,
lmaskexplocalcurve2, localmaskexputili,
lmaskSHlocalcurve2, localmaskSHutili,
lmaskviblocalcurve2, localmaskvibutili,
lmasktmlocalcurve2, localmasktmutili,
lmaskretilocalcurve2, localmaskretiutili,
lmaskcblocalcurve2, localmaskcbutili,
lmaskbllocalcurve2, localmaskblutili,
lmasklclocalcurve2, localmasklcutili,
lmaskloglocalcurve2, localmasklogutili,
lmasklocal_curve2, localmask_utili,
locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili,lochhhmasCurve, lhhmasutili, 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,
lochhhmas_Curve, lhhmas_utili,
loclmasCurveblwav,lmasutiliblwav,
loclmasCurvecolwav,lmasutilicolwav,
locwavCurve, locwavutili,
loclevwavCurve, loclevwavutili,
locconwavCurve, locconwavutili,
loccompwavCurve, loccompwavutili,
loccomprewavCurve, loccomprewavutili,
locwavCurveden, locwavdenutili,
locedgwavCurve, locedgwavutili,
loclmasCurve_wav,lmasutili_wav,
LHutili, HHutili, CHutili, 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,
minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax);
}
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
}
}
}
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;
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)) {
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)) {
parent->ipf.impulsedenoise(labnCrop);
parent->ipf.defringe(labnCrop);
}
parent->ipf.MLsharpen(labnCrop);
if ((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
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))) {
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.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)));
E_V += params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV
E_V += 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;
}
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()));
}
} // 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 rcx, int rcy, int rcw, int rch, int skip, bool internal)
{
if (!internal) {
cropMutex.lock();
}
bool changed = false;
rqcropx = rcx;
rqcropy = rcy;
rqcropw = rcw;
rqcroph = rch;
// 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;
}
}