* GHS GUI first step * GHS GUI first step * GUI step 3 * Hide show ghsmethod * Siril functions for ghs * Init ghs in iplocallab * ghs step 2 * ghs step 4 * Comment copyright Pixlnsight 2021 * Smooth highlights and tooltips * Enabled saturation and luminance ghs mode * First tooltip * Ghs tooltips * Remove wrong packstart inverssh * Change labels and tooltips * Disabled mask in global and other improvments * Comment code * appimage and windows yml ghs * Change tooltip * Ghsmethod hue and change tolltips * Change tooltip * Inverse Stretch step 1 * Inverse GHS * GHS tooltips * Change tooltips * Change tooltip * Linear black point * Small change to blackpoint * Change tooltip * Clean and comment code * forgotten GHS highlight attenuation msg in history * Comment code with Transformation equations * Change tooltip * Wrong default value balck point * Comment code iplocallab * Ghs curve step 1 * GHS curve step 2 * Show GHS setting in a Curve Box * Tooltip for ghs S curve * Disabled warning in compilation simpleprocess * Simplified code part 1 * Simplified code GHS graph part 2 * Improve black point with negatives values * Improve BP and tooltip * Listener enable only with GHS * White point for GHS * Change label and tooltip * Improve behavior white point and black point * Link sliders ghs_SP ghs_LP ghs_HP and change code to avoid balck screen * hide unused button in diagonal curve GHS * prevents the GHS representation in S from displaying artifacts if ghs-d=0 * Change tooltips * Improvment and tooltips * Forgotten tooltip * Improve GUI GHS S curve - change tooltips * Set transition gray areas in S curve GHS with values of the RT-spot * Change label GHS * setlogscale blackpoint and symmetry * Set recursive reference enable by default in controlspotpanel * Change lastlocalCurvesDir Dirghs in locallabtools and options - change labels * Added in converttonormal ghsMode.. not sure if this is useful * DIY to make GHS curve work without the choices * Change RGB calculation with luminance function working profile * 5 modes GHS method * Label to show datas clipped Black point and White point * Various change white point tooltips * Bad behavior wp bp labels * Small change to improccordinator call to ghschanged ghsbwchanged * Set log scale ghs_D * Hide Graduated filter if GHS enable * Luminance Lab in ghsmethod * Lab slope factor step 1 * Slope and Chromaticity GHS improvments * Fixed bad sqrt line 17477 iplocallab * Workaround linear GHS - re-enable Graduated filer GHS * Change limits slope lab factor * Ghs chromaticity Lab (Lch) * Improve ghs chromaticity * Change labels and tooltips Lab chroma * Slope Lab to 100 * Noise and saturation RGB * Saturation RGB standard and labels * Change histogram and navigator panel without gamma when using working profile * Remove gray in GHS curve * Local contrast a minima * Regularization stretch * Improve Graduated Filter in all cases GHS Color and Light etc. * Improves nlmeans to reduce noise after GHS * Change to GF - tooltip Nlmeans * Added oW oH tW tH etc. * Added call GF * tX tY for previewProps * Comment code GF * Improve local contrast ghs * Change norm to norm2 * Improve GUI mode complexity and Lab GHS * Show values BP WP in GUI * Labgrid ghs step 1 * Labgrid for simulation GHS - step 2 * More points for Labgrid ghs * Clean and comment code * Fixed crash in inverse GHS white point - set to 10 points for GSH simulation * Change to black point in inverse GHS * Intilialize simulation with nothing if new spot * Remove curve GHS - optimize code simulation - improve GUI * set ghs default - fixed crash is case HP SP LP * Fixed crash - I hope in inverse GHS * Simplify WP and BP limits to avoid crash in inverse GHS * Clean code with ghscurve - ghsshape * Change tooltips * Change to D - GUI - comment code * Simulation with 4 more points * Best simulation with point 0.05 and 0.95 * Clean code - change for crsah in Inverse GHS * Show values WP and BP
3445 lines
144 KiB
C++
3445 lines
144 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <fstream>
|
|
|
|
#include <glibmm/thread.h>
|
|
|
|
#include "improccoordinator.h"
|
|
|
|
#include "array2D.h"
|
|
#include "cieimage.h"
|
|
#include "color.h"
|
|
#include "colortemp.h"
|
|
#include "curves.h"
|
|
#include "dcp.h"
|
|
#include "guidedfilter.h"
|
|
#include "iccstore.h"
|
|
#include "image8.h"
|
|
#include "imagefloat.h"
|
|
#include "improcfun.h"
|
|
#include "metadata.h"
|
|
#include "labimage.h"
|
|
#include "lcp.h"
|
|
#include "procparams.h"
|
|
#include "tweakoperator.h"
|
|
#include "refreshmap.h"
|
|
#include "utils.h"
|
|
|
|
#include "../rtgui/options.h"
|
|
|
|
#ifdef _OPENMP
|
|
#include <omp.h>
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
|
|
constexpr int VECTORSCOPE_SIZE = 128;
|
|
|
|
}
|
|
|
|
namespace rtengine
|
|
{
|
|
|
|
ImProcCoordinator::ImProcCoordinator() :
|
|
orig_prev(nullptr),
|
|
oprevi(nullptr),
|
|
spotprev(nullptr),
|
|
oprevl(nullptr),
|
|
nprevl(nullptr),
|
|
fattal_11_dcrop_cache(nullptr),
|
|
previmg(nullptr),
|
|
workimg(nullptr),
|
|
ncie(nullptr),
|
|
imgsrc(nullptr),
|
|
lastAwbEqual(0.),
|
|
lastAwbTempBias(0.0),
|
|
lastAwbauto(""),
|
|
monitorIntent(RI_RELATIVE),
|
|
softProof(false),
|
|
gamutCheck(false),
|
|
sharpMask(false),
|
|
sharpMaskChanged(false),
|
|
scale(10),
|
|
highDetailPreprocessComputed(false),
|
|
highDetailRawComputed(false),
|
|
allocated(false),
|
|
bwAutoR(-9000.f),
|
|
bwAutoG(-9000.f),
|
|
bwAutoB(-9000.f),
|
|
CAMMean(NAN),
|
|
hltonecurve(65536),
|
|
shtonecurve(65536),
|
|
tonecurve(65536, 0), //,1);
|
|
lumacurve(32770, 0), // lumacurve[32768] and lumacurve[32769] will be set to 32768 and 32769 later to allow linear interpolation
|
|
chroma_acurve(65536, 0),
|
|
chroma_bcurve(65536, 0),
|
|
satcurve(65536, 0),
|
|
lhskcurve(65536, 0),
|
|
clcurve(65536, 0),
|
|
conversionBuffer(1, 1),
|
|
wavclCurve(65536, 0),
|
|
clToningcurve(65536, 0),
|
|
cl2Toningcurve(65536, 0),
|
|
Noisecurve(65536, 0),
|
|
NoiseCCcurve(65536, 0),
|
|
vhist16(65536), vhist16bw(65536),
|
|
lhist16CAM(65536),
|
|
lhist16CCAM(65536),
|
|
lhist16RETI(),
|
|
lhist16LClad(65536),
|
|
histRed(256), histRedRaw(256),
|
|
histGreen(256), histGreenRaw(256),
|
|
histBlue(256), histBlueRaw(256),
|
|
histLuma(256),
|
|
histToneCurve(256),
|
|
histToneCurveBW(256),
|
|
histLCurve(256),
|
|
histCCurve(256),
|
|
histLLCurve(256),
|
|
|
|
histLCAM(256),
|
|
histCCAM(256),
|
|
histClad(256),
|
|
bcabhist(256),
|
|
histChroma(256),
|
|
|
|
histLRETI(256),
|
|
|
|
hist_lrgb_dirty(false),
|
|
hist_raw_dirty(false),
|
|
|
|
vectorscopeScale(0),
|
|
vectorscope_hc_dirty(false),
|
|
vectorscope_hs_dirty(false),
|
|
vectorscope_hc(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
|
|
vectorscope_hs(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
|
|
waveformScale(0),
|
|
waveform_dirty(false),
|
|
waveformRed(0, 0),
|
|
waveformGreen(0, 0),
|
|
waveformBlue(0, 0),
|
|
waveformLuma(0, 0),
|
|
|
|
CAMBrightCurveJ(), CAMBrightCurveQ(),
|
|
|
|
rCurve(),
|
|
gCurve(),
|
|
bCurve(),
|
|
ctColorCurve(),
|
|
rcurvehist(256), rcurvehistCropped(256), rbeforehist(256),
|
|
gcurvehist(256), gcurvehistCropped(256), gbeforehist(256),
|
|
bcurvehist(256), bcurvehistCropped(256), bbeforehist(256),
|
|
fw(0), fh(0), tr(0),
|
|
fullw(1), fullh(1),
|
|
pW(-1), pH(-1),
|
|
plistener(nullptr),
|
|
imageListener(nullptr),
|
|
aeListener(nullptr),
|
|
acListener(nullptr),
|
|
ablListener(nullptr),
|
|
abwListener(nullptr),
|
|
awbListener(nullptr),
|
|
flatFieldAutoClipListener(nullptr),
|
|
bayerAutoContrastListener(nullptr),
|
|
xtransAutoContrastListener(nullptr),
|
|
pdSharpenAutoContrastListener(nullptr),
|
|
pdSharpenAutoRadiusListener(nullptr),
|
|
frameCountListener(nullptr),
|
|
imageTypeListener(nullptr),
|
|
filmNegListener(nullptr),
|
|
actListener(nullptr),
|
|
primListener(nullptr),
|
|
adnListener(nullptr),
|
|
awavListener(nullptr),
|
|
dehaListener(nullptr),
|
|
hListener(nullptr),
|
|
resultValid(false),
|
|
params(new procparams::ProcParams),
|
|
tweakOperator(nullptr),
|
|
lastOutputProfile("BADFOOD"),
|
|
lastOutputIntent(RI__COUNT),
|
|
lastOutputBPC(false),
|
|
thread(nullptr),
|
|
changeSinceLast(0),
|
|
updaterRunning(false),
|
|
nextParams(new procparams::ProcParams),
|
|
destroying(false),
|
|
utili(false),
|
|
autili(false),
|
|
butili(false),
|
|
ccutili(false),
|
|
cclutili(false),
|
|
clcutili(false),
|
|
opautili(false),
|
|
wavcontlutili(false),
|
|
colourToningSatLimit(0.f),
|
|
colourToningSatLimitOpacity(0.f),
|
|
highQualityComputed(false),
|
|
customTransformIn(nullptr),
|
|
customTransformOut(nullptr),
|
|
ipf(params.get(), true),
|
|
|
|
// Locallab
|
|
locallListener(nullptr),
|
|
lllocalcurve(65536, LUT_CLIP_OFF),
|
|
cllocalcurve(65536, LUT_CLIP_OFF),
|
|
lclocalcurve(65536, LUT_CLIP_OFF),
|
|
cclocalcurve(65536, LUT_CLIP_OFF),
|
|
rgblocalcurve(65536, LUT_CLIP_OFF),
|
|
exlocalcurve(65536, LUT_CLIP_OFF),
|
|
hltonecurveloc(65536, LUT_CLIP_OFF), //32768
|
|
shtonecurveloc(65536, LUT_CLIP_OFF),
|
|
tonecurveloc(65536, LUT_CLIP_OFF),
|
|
lightCurveloc(32770, LUT_CLIP_OFF),
|
|
lmasklocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskexplocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskSHlocalcurve(65536, LUT_CLIP_OFF),
|
|
ghslocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskviblocalcurve(65536, LUT_CLIP_OFF),
|
|
lmasktmlocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskretilocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskcblocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskbllocalcurve(65536, LUT_CLIP_OFF),
|
|
lmasklclocalcurve(65536, LUT_CLIP_OFF),
|
|
lmaskloglocalcurve(65536, LUT_CLIP_OFF),
|
|
lmasklocal_curve(65536, LUT_CLIP_OFF),
|
|
lmaskcielocalcurve(65536, LUT_CLIP_OFF),
|
|
cielocalcurve(65536, LUT_CLIP_OFF),
|
|
cielocalcurve2(65536, LUT_CLIP_OFF),
|
|
jzlocalcurve(65536, LUT_CLIP_OFF),
|
|
czlocalcurve(65536, LUT_CLIP_OFF),
|
|
czjzlocalcurve(65536, LUT_CLIP_OFF),
|
|
lastspotdup(false),
|
|
previewDeltaE(false),
|
|
locallColorMask(0),
|
|
locallColorMaskinv(0),
|
|
locallExpMask(0),
|
|
locallExpMaskinv(0),
|
|
locallSHMask(0),
|
|
locallSHMaskinv(0),
|
|
locallvibMask(0),
|
|
localllcMask(0),
|
|
locallcbMask(0),
|
|
locallretiMask(0),
|
|
locallsoftMask(0),
|
|
localltmMask(0),
|
|
locallblMask(0),
|
|
locallsharMask(0),
|
|
localllogMask(0),
|
|
locall_Mask(0),
|
|
locallcieMask(0),
|
|
retistrsav(nullptr)
|
|
{
|
|
}
|
|
|
|
ImProcCoordinator::~ImProcCoordinator()
|
|
{
|
|
|
|
destroying = true;
|
|
updaterThreadStart.lock();
|
|
|
|
if (updaterRunning && thread) {
|
|
thread->join();
|
|
}
|
|
|
|
mProcessing.lock();
|
|
mProcessing.unlock();
|
|
freeAll();
|
|
|
|
if (fattal_11_dcrop_cache) {
|
|
delete fattal_11_dcrop_cache;
|
|
fattal_11_dcrop_cache = nullptr;
|
|
}
|
|
|
|
std::vector<Crop*> toDel = crops;
|
|
|
|
for (size_t i = 0; i < toDel.size(); i++) {
|
|
delete toDel[i];
|
|
}
|
|
|
|
imgsrc->decreaseRef();
|
|
|
|
if (customTransformIn) {
|
|
cmsDeleteTransform(customTransformIn);
|
|
customTransformIn = nullptr;
|
|
}
|
|
|
|
if (customTransformOut) {
|
|
cmsDeleteTransform(customTransformOut);
|
|
customTransformOut = nullptr;
|
|
}
|
|
|
|
updaterThreadStart.unlock();
|
|
}
|
|
|
|
void ImProcCoordinator::assign(ImageSource* imgsrc)
|
|
{
|
|
this->imgsrc = imgsrc;
|
|
}
|
|
|
|
void ImProcCoordinator::getParams(procparams::ProcParams* dst, bool tweaked)
|
|
{
|
|
if (!tweaked && paramsBackup.operator bool()) {
|
|
*dst = *paramsBackup;
|
|
} else {
|
|
*dst = *params;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::backupParams()
|
|
{
|
|
if (!params) {
|
|
return;
|
|
}
|
|
|
|
if (!paramsBackup) {
|
|
paramsBackup.reset(new ProcParams());
|
|
}
|
|
|
|
*paramsBackup = *params;
|
|
}
|
|
|
|
void ImProcCoordinator::restoreParams()
|
|
{
|
|
if (!paramsBackup || !params) {
|
|
return;
|
|
}
|
|
|
|
*params = *paramsBackup;
|
|
}
|
|
|
|
DetailedCrop* ImProcCoordinator::createCrop(::EditDataProvider *editDataProvider, bool isDetailWindow)
|
|
{
|
|
|
|
return new Crop(this, editDataProvider, isDetailWindow);
|
|
}
|
|
|
|
|
|
// todo: bitmask containing desired actions, taken from changesSinceLast
|
|
void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
|
{
|
|
// TODO Locallab printf
|
|
MyMutex::MyLock processingLock(mProcessing);
|
|
|
|
bool highDetailNeeded = options.prevdemo == PD_Sidecar ? true : (todo & M_HIGHQUAL);
|
|
// printf("metwb=%s \n", params->wb.method.c_str());
|
|
|
|
// Check if any detail crops need high detail. If not, take a fast path short cut
|
|
if (!highDetailNeeded) {
|
|
for (size_t i = 0; i < crops.size(); i++) {
|
|
if (crops[i]->get_skip() == 1) { // skip=1 -> full resolution
|
|
highDetailNeeded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((todo & ALL) == ALL) || (todo & M_MONITOR) || panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar)) {
|
|
bwAutoR = bwAutoG = bwAutoB = -9000.f;
|
|
|
|
if (todo == CROP && ipf.needsPCVignetting()) {
|
|
todo |= TRANSFORM; // Change about Crop does affect TRANSFORM
|
|
}
|
|
|
|
RAWParams rp = params->raw;
|
|
ColorManagementParams cmp = params->icm;
|
|
LCurveParams lcur = params->labCurve;
|
|
bool spotsDone = false;
|
|
|
|
if (!highDetailNeeded) {
|
|
// if below 100% magnification, take a fast path
|
|
if (rp.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::NONE) && rp.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::MONO)) {
|
|
rp.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
|
|
}
|
|
|
|
//bayerrp.all_enhance = false;
|
|
|
|
if (rp.xtranssensor.method != RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::NONE) && rp.xtranssensor.method != RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::MONO)) {
|
|
rp.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
|
|
}
|
|
|
|
rp.bayersensor.ccSteps = 0;
|
|
rp.xtranssensor.ccSteps = 0;
|
|
//rp.deadPixelFilter = rp.hotPixelFilter = false;
|
|
}
|
|
|
|
if (frameCountListener) {
|
|
frameCountListener->FrameCountChanged(imgsrc->getFrameCount(), params->raw.bayersensor.imageNum);
|
|
}
|
|
|
|
// raw auto CA is bypassed if no high detail is needed, so we have to compute it when high detail is needed
|
|
float reddeha = 0.f;//initialize black red, blue, green for dehaze
|
|
float greendeha = 0.f;
|
|
float bluedeha = 0.f;
|
|
|
|
if ((todo & M_PREPROC) || (!highDetailPreprocessComputed && highDetailNeeded)) {
|
|
imgsrc->setCurrentFrame(params->raw.bayersensor.imageNum);
|
|
imgsrc->preprocess(rp, params->lensProf, params->coarse, reddeha, greendeha, bluedeha, true);
|
|
if(imgsrc->getSensorType() == ST_BAYER) {//Bayer
|
|
if (ablListener) {
|
|
if(rp.bayersensor.Dehablack) {
|
|
ablListener->autoBlackChanged(reddeha, greendeha, bluedeha);
|
|
}
|
|
}
|
|
} else if(imgsrc->getSensorType() == ST_FUJI_XTRANS) {//X trans
|
|
if (ablxListener) {
|
|
if(rp.xtranssensor.Dehablackx) {
|
|
ablxListener->autoBlackxChanged(reddeha, greendeha, bluedeha);
|
|
}
|
|
}
|
|
}
|
|
if (flatFieldAutoClipListener && rp.ff_AutoClipControl) {
|
|
flatFieldAutoClipListener->flatFieldAutoClipValueChanged(imgsrc->getFlatFieldAutoClipValue());
|
|
}
|
|
|
|
imgsrc->getRAWHistogram(histRedRaw, histGreenRaw, histBlueRaw);
|
|
hist_raw_dirty = !(hListener && hListener->updateHistogramRaw());
|
|
|
|
highDetailPreprocessComputed = highDetailNeeded;
|
|
}
|
|
|
|
/*
|
|
Demosaic is kicked off only when
|
|
Detail considerations:
|
|
accurate detail is not displayed yet needed based on preview specifics (driven via highDetailNeeded flag)
|
|
OR
|
|
HLR considerations:
|
|
Color HLR alters rgb output of demosaic, so re-demosaic is needed when Color HLR is being turned off;
|
|
if HLR is enabled and changing method *from* Color to any other method
|
|
OR HLR gets disabled when Color method was selected
|
|
If white balance changed with inpaint opposed, because inpaint opposed depends on the white balance
|
|
*/
|
|
// If high detail (=100%) is newly selected, do a demosaic update, since the last was just with FAST
|
|
|
|
if (imageTypeListener) {
|
|
imageTypeListener->imageTypeChanged(imgsrc->isRAW(), imgsrc->getSensorType() == ST_BAYER, imgsrc->getSensorType() == ST_FUJI_XTRANS, imgsrc->isMono(), imgsrc->isGainMapSupported());
|
|
}
|
|
|
|
bool iscolor = (params->toneCurve.method == "Color" || params->toneCurve.method == "Coloropp");
|
|
if ((todo & M_WB) && params->toneCurve.hrenabled && params->toneCurve.method == "Coloropp") {
|
|
todo |= DEMOSAIC;
|
|
}
|
|
|
|
if ((todo & M_RAW)
|
|
|| (!highDetailRawComputed && highDetailNeeded)
|
|
// || (params->toneCurve.hrenabled && params->toneCurve.method != "Color" && imgsrc->isRGBSourceModified())
|
|
// || (!params->toneCurve.hrenabled && params->toneCurve.method == "Color" && imgsrc->isRGBSourceModified())) {
|
|
|| (params->toneCurve.hrenabled && !iscolor && imgsrc->isRGBSourceModified())
|
|
|| (!params->toneCurve.hrenabled && iscolor && imgsrc->isRGBSourceModified())) {
|
|
|
|
if (settings->verbose) {
|
|
if (imgsrc->getSensorType() == ST_BAYER) {
|
|
printf("Demosaic Bayer image n.%d using method: %s\n", rp.bayersensor.imageNum + 1, rp.bayersensor.method.c_str());
|
|
} else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
|
|
printf("Demosaic X-Trans image with using method: %s\n", rp.xtranssensor.method.c_str());
|
|
}
|
|
}
|
|
|
|
if (imgsrc->getSensorType() == ST_BAYER) {
|
|
if (params->raw.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT)) {
|
|
imgsrc->setBorder(params->raw.bayersensor.border);
|
|
} else {
|
|
imgsrc->setBorder(std::max(params->raw.bayersensor.border, 2));
|
|
}
|
|
} else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
|
|
imgsrc->setBorder(params->raw.xtranssensor.border);
|
|
}
|
|
|
|
bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicAutoContrast : params->raw.xtranssensor.dualDemosaicAutoContrast;
|
|
double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast;
|
|
imgsrc->demosaic(rp, autoContrast, contrastThreshold, params->pdsharpening.enabled);
|
|
|
|
if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) {
|
|
bayerAutoContrastListener->autoContrastChanged(contrastThreshold);
|
|
} else if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) {
|
|
|
|
xtransAutoContrastListener->autoContrastChanged(contrastThreshold);
|
|
}
|
|
|
|
// if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag
|
|
todo |= (M_INIT | M_CSHARP);
|
|
|
|
}
|
|
|
|
if ((todo & (M_RAW | M_CSHARP)) && params->pdsharpening.enabled) {
|
|
double pdSharpencontrastThreshold = params->pdsharpening.contrast;
|
|
double pdSharpenRadius = params->pdsharpening.deconvradius;
|
|
imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold, pdSharpenRadius);
|
|
|
|
if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) {
|
|
pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold);
|
|
}
|
|
|
|
if (pdSharpenAutoRadiusListener && params->pdsharpening.autoRadius) {
|
|
pdSharpenAutoRadiusListener->autoRadiusChanged(pdSharpenRadius);
|
|
}
|
|
}
|
|
|
|
|
|
if ((todo & M_RAW)
|
|
|| (!highDetailRawComputed && highDetailNeeded)
|
|
// || (params->toneCurve.hrenabled && params->toneCurve.method != "Color" && imgsrc->isRGBSourceModified())
|
|
// || (!params->toneCurve.hrenabled && params->toneCurve.method == "Color" && imgsrc->isRGBSourceModified())) {
|
|
|| (params->toneCurve.hrenabled && !iscolor && imgsrc->isRGBSourceModified())
|
|
|| (!params->toneCurve.hrenabled && iscolor && imgsrc->isRGBSourceModified())) {
|
|
if (highDetailNeeded) {
|
|
highDetailRawComputed = true;
|
|
} else {
|
|
highDetailRawComputed = false;
|
|
}
|
|
|
|
if (params->retinex.enabled) {
|
|
lhist16RETI(32768);
|
|
lhist16RETI.clear();
|
|
|
|
imgsrc->retinexPrepareBuffers(params->icm, params->retinex, conversionBuffer, lhist16RETI);
|
|
}
|
|
}
|
|
|
|
if (todo & (M_INIT | M_LINDENOISE | M_HDR)) {
|
|
if (params->wb.method == "autitcgreen") {
|
|
imgsrc->getrgbloc(0, 0, fh, fw, 0, 0, fh, fw, params->wb);
|
|
}
|
|
}
|
|
|
|
if ((todo & (M_RETINEX | M_INIT)) && params->retinex.enabled) {
|
|
bool dehacontlutili = false;
|
|
bool mapcontlutili = false;
|
|
bool useHsl = false;
|
|
LUTf cdcurve(65536, 0);
|
|
LUTf mapcurve(65536, 0);
|
|
|
|
imgsrc->retinexPrepareCurves(params->retinex, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, dehacontlutili, mapcontlutili, useHsl, lhist16RETI, histLRETI);
|
|
float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax;
|
|
imgsrc->retinex(params->icm, params->retinex, params->toneCurve, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, conversionBuffer, dehacontlutili, mapcontlutili, useHsl, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, histLRETI); //enabled Retinex
|
|
|
|
if (dehaListener) {
|
|
dehaListener->minmaxChanged(maxCD, minCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax);
|
|
}
|
|
}
|
|
|
|
// const bool autowb = (params->wb.method == "autold" || params->wb.method == "autitcgreen");
|
|
|
|
if (settings->verbose) {
|
|
printf("automethod=%s \n", params->wb.method.c_str());
|
|
}
|
|
|
|
if (todo & (M_INIT | M_LINDENOISE | M_HDR)) {
|
|
MyMutex::MyLock initLock(minit); // Also used in crop window
|
|
//imgsrc->HLRecovery_Global(params->toneCurve); // this handles Color HLRecovery
|
|
|
|
|
|
if (settings->verbose) {
|
|
printf("Applying white balance, color correction & sRBG conversion...\n");
|
|
}
|
|
|
|
currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method, params->wb.observer);
|
|
int tempnotisraw = 6501;//D65 with Observer 2° - 6473 with Observer 10°
|
|
double greennotisraw = 1.;//D65 with Observer 2° - 0.967 with Observer 10°
|
|
|
|
if (!imgsrc->isRAW() && params->wb.method == "autitcgreen") {
|
|
if (params->wb.compat_version == 1) {
|
|
// ITCWB compatibility version 1 used 5000 K and observer 10
|
|
// degrees for non-raw files.
|
|
auto currWBitc = ColorTemp(5000., 1., 1., params->wb.method, StandardObserver::TEN_DEGREES);
|
|
currWBitc = currWBitc.convertObserver(params->wb.observer);
|
|
tempnotisraw = currWBitc.getTemp();
|
|
greennotisraw = currWBitc.getGreen();
|
|
} else {
|
|
auto currWBitc = imgsrc->getWB();//if jpg TIF with another illuminant
|
|
currWBitc = currWBitc.convertObserver(params->wb.observer);
|
|
tempnotisraw = currWBitc.getTemp();
|
|
greennotisraw = currWBitc.getGreen();
|
|
}
|
|
}
|
|
|
|
int dread = 0;
|
|
int bia = 1;
|
|
float studgood = 1000.f;
|
|
int nocam = 0;
|
|
int kcam = 0;
|
|
float minchrom = 1000.f;
|
|
float delta = 0.f;
|
|
int kmin = 20;
|
|
float minhist = 1000000000.f;
|
|
float maxhist = -1000.f;
|
|
double greenitc = 1.;
|
|
float temp0 = 5000.f;
|
|
bool extra = false;
|
|
bool forcewbgrey = false;
|
|
|
|
if (!params->wb.enabled) {
|
|
currWB = ColorTemp();
|
|
} else if (params->wb.method == "Camera") {
|
|
currWB = imgsrc->getWB();
|
|
lastAwbauto = ""; //reinitialize auto
|
|
|
|
} else if (params->wb.method == "autold") {
|
|
if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias || lastAwbauto != params->wb.method) {
|
|
double rm, gm, bm;
|
|
if (params->wb.compat_version == 1 && !imgsrc->isRAW()) {
|
|
// RGB grey compatibility version 1 used the identity
|
|
// multipliers plus temperature bias for non-raw files.
|
|
rm = gm = bm = 1.;
|
|
} else {
|
|
imgsrc->getAutoWBMultipliers(rm, gm, bm);
|
|
}
|
|
|
|
if (rm != -1.) {
|
|
double bias = params->wb.tempBias;
|
|
|
|
autoWB.update(rm, gm, bm, params->wb.equal, params->wb.observer, bias);
|
|
lastAwbEqual = params->wb.equal;
|
|
lastAwbObserver = params->wb.observer;
|
|
lastAwbTempBias = params->wb.tempBias;
|
|
lastAwbauto = params->wb.method;
|
|
} else {
|
|
lastAwbEqual = -1.;
|
|
lastAwbObserver = ColorTemp::DEFAULT_OBSERVER;
|
|
lastAwbTempBias = 0.0;
|
|
lastAwbauto = "";
|
|
autoWB.useDefaults(params->wb.equal, params->wb.observer);
|
|
|
|
}
|
|
|
|
//double rr,gg,bb;
|
|
//autoWB.getMultipliers(rr,gg,bb);
|
|
}
|
|
currWB = autoWB;
|
|
// lastAwbauto = ""; //reinitialize auto
|
|
} else if (params->wb.method == "autitcgreen") { //(// autowb) {
|
|
double rm;
|
|
double gm;
|
|
double bm;
|
|
imgsrc->getAutoWBMultipliersItcGreen(
|
|
*params,
|
|
forcewbgrey,
|
|
kcam,
|
|
greenitc,
|
|
extra,
|
|
temp0,
|
|
delta,
|
|
bia,
|
|
dread,
|
|
nocam,
|
|
studgood,
|
|
minchrom,
|
|
kmin,
|
|
minhist,
|
|
maxhist,
|
|
fh,
|
|
fw,
|
|
currWB,
|
|
tempnotisraw,
|
|
greennotisraw,
|
|
lastAwbEqual == params->wb.equal && lastAwbObserver == params->wb.observer && lastAwbTempBias == params->wb.tempBias && lastAwbauto == params->wb.method,
|
|
autoWB,
|
|
rm,
|
|
gm,
|
|
bm);
|
|
|
|
if (imgsrc->isRAW() || lastAwbEqual != params->wb.equal || lastAwbObserver != params->wb.observer || lastAwbTempBias != params->wb.tempBias || lastAwbauto != params->wb.method) {
|
|
if (rm != -1.) {
|
|
autoWB.update(rm, gm, bm, params->wb.equal, params->wb.observer);
|
|
lastAwbEqual = params->wb.equal;
|
|
lastAwbObserver = params->wb.observer;
|
|
lastAwbTempBias = params->wb.tempBias;
|
|
lastAwbauto = params->wb.method;
|
|
} else {
|
|
lastAwbEqual = -1.;
|
|
lastAwbObserver = ColorTemp::DEFAULT_OBSERVER;
|
|
lastAwbTempBias = 0.0;
|
|
lastAwbauto = "";
|
|
autoWB.useDefaults(params->wb.equal, params->wb.observer);
|
|
}
|
|
}
|
|
|
|
currWB = autoWB;
|
|
}
|
|
|
|
double rw = 1.;
|
|
double gw = 1.;
|
|
double bw = 1.;
|
|
if (params->wb.enabled) {
|
|
currWB = currWB.convertObserver(params->wb.observer);
|
|
params->wb.temperature = static_cast<int>(currWB.getTemp());
|
|
params->wb.green = currWB.getGreen();
|
|
|
|
currWB.getMultipliers(rw, gw, bw);
|
|
imgsrc->wbMul2Camera(rw, gw, bw);
|
|
// params->wb.itcwb_sampling = false;
|
|
/*
|
|
printf("ra=%f ga=%f ba=%f\n", rw, gw, bw);
|
|
//recalculate temp and green with wb multipliers.
|
|
imgsrc->wbCamera2Mul(rw, gw, bw);
|
|
ColorTemp ct(rw, gw, bw, 1.0, currWB.getObserver());
|
|
//allows to calculate temp and green with multipliers in case of we want in GUI
|
|
float tem = ct.getTemp();
|
|
float gre = ct.getGreen();
|
|
printf("tem=%f gre=%f \n", (double) tem, (double) gre);
|
|
*/
|
|
}
|
|
|
|
int met = 0;
|
|
|
|
if (awbListener && params->wb.enabled) {
|
|
if (params->wb.method == "autitcgreen" && imgsrc->isRAW()) {//Raw files
|
|
if (params->wb.itcwb_sampling) {
|
|
dread = 1;
|
|
studgood = 1.f;
|
|
awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, 0, 1, 0, dread, studgood, 0, 0, 0, 0, AutoWBListener::AWBMode::TEMP_CORRELATION_RAW);
|
|
|
|
} else {
|
|
minchrom = LIM(minchrom, 0.f, 0.9f);
|
|
delta = LIM(delta, 0.f, 0.9f);
|
|
minhist = std::max(minhist, 100.f);
|
|
maxhist = std::max(maxhist, 1000.f);
|
|
kmin = std::max(kmin, 18);
|
|
dread = LIM(dread, 10, 239);
|
|
awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, temp0, delta, bia, dread, studgood, minchrom, kmin, minhist, maxhist, AutoWBListener::AWBMode::TEMP_CORRELATION_RAW);
|
|
}
|
|
} else if (params->wb.method == "autitcgreen" && !imgsrc->isRAW()) {//non raw files
|
|
params->wb.temperature = tempnotisraw;
|
|
params->wb.green = greennotisraw;
|
|
currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method, params->wb.observer);
|
|
|
|
awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::TEMP_CORRELATION_NON_RAW);//false => hide settings
|
|
|
|
} else if (params->wb.method == "autold"){
|
|
awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::RGB_GREY);
|
|
} else {
|
|
awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::NONE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
GammaValues g_a;
|
|
double pwr = 1.0 / params->icm.gampos;
|
|
double ts = params->icm.slpos;
|
|
|
|
|
|
int mode = 0;
|
|
Color::calcGamma(pwr, ts, mode, g_a); // call to calcGamma with selected gamma and slope
|
|
printf("ga[0]=%f ga[1]=%f ga[2]=%f ga[3]=%f ga[4]=%f\n", g_a[0],g_a[1],g_a[2],g_a[3],g_a[4]);
|
|
|
|
Glib::ustring datal;
|
|
datal = "lutsrgb.txt";
|
|
ofstream fou(datal, ios::out | ios::trunc);
|
|
|
|
for(int i=0; i < 212; i++) {
|
|
//printf("igamma2=%i\n", (int) 65535.f*Color::igamma2(i/212.0));
|
|
float gam = Color::igamma2(i/211.0);
|
|
int lutga = nearbyint(65535.f* gam);
|
|
// fou << 65535*(int)Color::igamma2(i/212.0) << endl;
|
|
fou << i << " " << lutga << endl;
|
|
|
|
}
|
|
fou.close();
|
|
*/
|
|
int tr = getCoarseBitMask(params->coarse);
|
|
|
|
imgsrc->getFullSize(fw, fh, tr);
|
|
|
|
// Will (re)allocate the preview's buffers
|
|
setScale(scale);
|
|
PreviewProps pp(0, 0, fw, fh, scale);
|
|
// Tells to the ImProcFunctions' tools what is the preview scale, which may lead to some simplifications
|
|
ipf.setScale(scale);
|
|
imgsrc->getImage(currWB, tr, orig_prev, pp, params->toneCurve, params->raw);
|
|
|
|
if ((todo & M_SPOT) && params->spot.enabled && !params->spot.entries.empty()) {
|
|
spotsDone = true;
|
|
PreviewProps pp(0, 0, fw, fh, scale);
|
|
ipf.removeSpots(orig_prev, imgsrc, params->spot.entries, pp, currWB, nullptr, tr);
|
|
}
|
|
|
|
denoiseInfoStore.valid = false;
|
|
//ColorTemp::CAT02 (orig_prev, ¶ms) ;
|
|
// printf("orig_prevW=%d\n scale=%d",orig_prev->width, scale);
|
|
/* Issue 2785, disabled some 1:1 tools
|
|
if (todo & M_LINDENOISE) {
|
|
DirPyrDenoiseParams denoiseParams = params->dirpyrDenoise;
|
|
if (denoiseParams.enabled && (scale==1)) {
|
|
Imagefloat *calclum = NULL ;
|
|
|
|
denoiseParams.getCurves(noiseLCurve,noiseCCurve);
|
|
int nbw=6;//nb tile W
|
|
int nbh=4;//
|
|
|
|
float ch_M[nbw*nbh];
|
|
float max_r[nbw*nbh];
|
|
float max_b[nbw*nbh];
|
|
|
|
if (denoiseParams.Lmethod == "CUR") {
|
|
if (noiseLCurve)
|
|
denoiseParams.luma = 0.5f;
|
|
else
|
|
denoiseParams.luma = 0.0f;
|
|
} else if (denoiseParams.Lmethod == "SLI")
|
|
noiseLCurve.Reset();
|
|
|
|
|
|
if (noiseLCurve || noiseCCurve){//only allocate memory if enabled and scale=1
|
|
// we only need image reduced to 1/4 here
|
|
calclum = new Imagefloat ((pW+1)/2, (pH+1)/2);//for luminance denoise curve
|
|
for(int ii=0;ii<pH;ii+=2){
|
|
for(int jj=0;jj<pW;jj+=2){
|
|
calclum->r(ii>>1,jj>>1) = orig_prev->r(ii,jj);
|
|
calclum->g(ii>>1,jj>>1) = orig_prev->g(ii,jj);
|
|
calclum->b(ii>>1,jj>>1) = orig_prev->b(ii,jj);
|
|
}
|
|
}
|
|
imgsrc->convertColorSpace(calclum, params->icm, currWB);//calculate values after colorspace conversion
|
|
}
|
|
|
|
int kall=1;
|
|
ipf.RGB_denoise(kall, orig_prev, orig_prev, calclum, ch_M, max_r, max_b, imgsrc->isRAW(), denoiseParams, imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi);
|
|
}
|
|
}
|
|
*/
|
|
|
|
if (params->filmNegative.enabled) {
|
|
|
|
// Process film negative AFTER colorspace conversion
|
|
if (params->filmNegative.colorSpace != FilmNegativeParams::ColorSpace::INPUT) {
|
|
imgsrc->convertColorSpace(orig_prev, params->icm, currWB);
|
|
}
|
|
|
|
// Perform negative inversion. If needed, upgrade filmNegative params for backwards compatibility with old profiles
|
|
if (ipf.filmNegativeProcess(orig_prev, orig_prev, params->filmNegative, params->raw, imgsrc, currWB) && filmNegListener) {
|
|
filmNegListener->filmRefValuesChanged(params->filmNegative.refInput, params->filmNegative.refOutput);
|
|
}
|
|
|
|
// Process film negative BEFORE colorspace conversion (legacy mode)
|
|
if (params->filmNegative.colorSpace == FilmNegativeParams::ColorSpace::INPUT) {
|
|
imgsrc->convertColorSpace(orig_prev, params->icm, currWB);
|
|
}
|
|
|
|
} else {
|
|
imgsrc->convertColorSpace(orig_prev, params->icm, currWB);
|
|
}
|
|
|
|
ipf.firstAnalysis(orig_prev, *params, vhist16);
|
|
}
|
|
|
|
oprevi = orig_prev;
|
|
|
|
if ((todo & M_SPOT) && !spotsDone) {
|
|
if (params->spot.enabled && !params->spot.entries.empty()) {
|
|
allocCache(spotprev);
|
|
orig_prev->copyData(spotprev);
|
|
PreviewProps pp(0, 0, fw, fh, scale);
|
|
ipf.removeSpots(spotprev, imgsrc, params->spot.entries, pp, currWB, ¶ms->icm, tr);
|
|
} else {
|
|
if (spotprev) {
|
|
delete spotprev;
|
|
spotprev = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (spotprev) {
|
|
spotprev->copyData(orig_prev);
|
|
}
|
|
|
|
if ((todo & M_HDR) && (params->fattal.enabled || params->dehaze.enabled)) {
|
|
if (fattal_11_dcrop_cache) {
|
|
delete fattal_11_dcrop_cache;
|
|
fattal_11_dcrop_cache = nullptr;
|
|
}
|
|
|
|
ipf.dehaze(orig_prev, params->dehaze);
|
|
ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0, 0, false);
|
|
|
|
if (oprevi != orig_prev) {
|
|
delete oprevi;
|
|
}
|
|
}
|
|
|
|
// Remove transformation if unneeded
|
|
bool needstransform = ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData());
|
|
|
|
const bool cam02 = params->colorappearance.modelmethod == "02" && params->colorappearance.enabled;
|
|
// 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))) {
|
|
// Forking the image
|
|
assert(oprevi);
|
|
Imagefloat *op = oprevi;
|
|
oprevi = new Imagefloat(pW, pH);
|
|
|
|
if (needstransform)
|
|
ipf.transform(op, oprevi, 0, 0, 0, 0, pW, pH, fw, fh,
|
|
imgsrc->getMetaData(), imgsrc->getRotateDegree(), false);
|
|
else {
|
|
op->copyData(oprevi);
|
|
}
|
|
}
|
|
|
|
for (int sp = 0; sp < (int)params->locallab.spots.size(); sp++) {
|
|
if(params->locallab.spots.at(sp).expsharp && params->dirpyrequalizer.cbdlMethod == "bef") {
|
|
if(params->locallab.spots.at(sp).shardamping < 1) {
|
|
params->locallab.spots.at(sp).shardamping = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 = oprevi->getWidth();
|
|
const int H = oprevi->getHeight();
|
|
LabImage labcbdl(W, H);
|
|
ipf.rgb2lab(*oprevi, labcbdl, params->icm.workingProfile);
|
|
ipf.dirpyrequalizer(&labcbdl, scale);
|
|
ipf.lab2rgb(labcbdl, *oprevi, params->icm.workingProfile);
|
|
}
|
|
|
|
if (todo & M_AUTOEXP) {
|
|
if (params->toneCurve.autoexp) {
|
|
LUTu aehist;
|
|
int aehistcompr;
|
|
imgsrc->getAutoExpHistogram(aehist, aehistcompr);
|
|
ipf.getAutoExp(aehist, aehistcompr, params->toneCurve.clip, params->toneCurve.expcomp,
|
|
params->toneCurve.brightness, params->toneCurve.contrast, params->toneCurve.black, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh);
|
|
|
|
if (aeListener)
|
|
aeListener->autoExpChanged(params->toneCurve.expcomp, params->toneCurve.brightness, params->toneCurve.contrast,
|
|
params->toneCurve.black, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, params->toneCurve.hrenabled);
|
|
}
|
|
|
|
if (params->toneCurve.histmatching ) {
|
|
bool exectrcexp = false;//store if Abstract profile enabled
|
|
exectrcexp = params->icm.trcExp;
|
|
if (!params->toneCurve.fromHistMatching) {
|
|
if(params->icm.trcExp) {
|
|
params->icm.trcExp = false;//disabled Abstract profile, if hismatching
|
|
}
|
|
imgsrc->getAutoMatchedToneCurve(params->icm, params->raw, params->wb.observer, params->toneCurve.curve);
|
|
params->icm.trcExp = exectrcexp;//restore Abstract profile
|
|
}
|
|
|
|
if (params->toneCurve.autoexp) {
|
|
params->toneCurve.expcomp = 0.0;
|
|
}
|
|
|
|
params->toneCurve.autoexp = false;
|
|
params->toneCurve.curveMode = ToneCurveMode::FILMLIKE;
|
|
params->toneCurve.curve2 = { 0 };
|
|
params->toneCurve.brightness = 0;
|
|
params->toneCurve.contrast = 0;
|
|
params->toneCurve.black = 0;
|
|
params->toneCurve.fromHistMatching = true;
|
|
|
|
if (aeListener) {
|
|
aeListener->autoMatchedToneCurveChanged(params->toneCurve.curveMode, params->toneCurve.curve);
|
|
}
|
|
}
|
|
|
|
// Encoding log with locallab
|
|
if (params->locallab.enabled && !params->locallab.spots.empty()) {
|
|
const int sizespot = (int)params->locallab.spots.size();
|
|
const LocallabParams::LocallabSpot defSpot;
|
|
std::vector<LocallabListener::locallabcieBEF> locallciebef;
|
|
|
|
float *sourceg = nullptr;
|
|
sourceg = new float[sizespot];
|
|
float *sourceab = nullptr;
|
|
sourceab = new float[sizespot];
|
|
float *targetg = nullptr;
|
|
targetg = new float[sizespot];
|
|
bool *log = nullptr;
|
|
log = new bool[sizespot];
|
|
bool *cie = nullptr;
|
|
cie = new bool[sizespot];
|
|
bool *autocomput = nullptr;
|
|
autocomput = new bool[sizespot];
|
|
float *blackev = nullptr;
|
|
blackev = new float[sizespot];
|
|
float *whiteev = nullptr;
|
|
whiteev = new float[sizespot];
|
|
bool *Autogr = nullptr;
|
|
Autogr = new bool[sizespot];
|
|
bool *autocie = nullptr;
|
|
autocie = new bool[sizespot];
|
|
int *whits = nullptr;
|
|
whits = new int[sizespot];
|
|
int *blacks = nullptr;
|
|
blacks = new int[sizespot];
|
|
int *whitslog = nullptr;
|
|
whitslog = new int[sizespot];
|
|
int *blackslog = nullptr;
|
|
blackslog = new int[sizespot];
|
|
|
|
|
|
float *locx = nullptr;
|
|
locx = new float[sizespot];
|
|
float *locy = nullptr;
|
|
locy = new float[sizespot];
|
|
float *locxL = nullptr;
|
|
locxL = new float[sizespot];
|
|
float *locyT = nullptr;
|
|
locyT = new float[sizespot];
|
|
float *centx = nullptr;
|
|
centx = new float[sizespot];
|
|
float *centy = nullptr;
|
|
centy = new float[sizespot];
|
|
|
|
for (int sp = 0; sp < sizespot; sp++) {
|
|
log[sp] = params->locallab.spots.at(sp).explog;
|
|
cie[sp] = params->locallab.spots.at(sp).expcie;
|
|
autocomput[sp] = params->locallab.spots.at(sp).autocompute;
|
|
autocie[sp] = params->locallab.spots.at(sp).Autograycie;
|
|
blackev[sp] = params->locallab.spots.at(sp).blackEv;
|
|
whiteev[sp] = params->locallab.spots.at(sp).whiteEv;
|
|
sourceg[sp] = params->locallab.spots.at(sp).sourceGray;
|
|
sourceab[sp] = params->locallab.spots.at(sp).sourceabs;
|
|
whits[sp] = params->locallab.spots.at(sp).whitescie;
|
|
blacks[sp] = params->locallab.spots.at(sp).blackscie;
|
|
whitslog[sp] = params->locallab.spots.at(sp).whiteslog;
|
|
blackslog[sp] = params->locallab.spots.at(sp).blackslog;
|
|
Autogr[sp] = params->locallab.spots.at(sp).Autogray;
|
|
targetg[sp] = params->locallab.spots.at(sp).targetGray;
|
|
locx[sp] = params->locallab.spots.at(sp).loc.at(0) / 2000.0;
|
|
locy[sp] = params->locallab.spots.at(sp).loc.at(2) / 2000.0;
|
|
locxL[sp] = params->locallab.spots.at(sp).loc.at(1) / 2000.0;
|
|
locyT[sp] = params->locallab.spots.at(sp).loc.at(3) / 2000.0;
|
|
centx[sp] = params->locallab.spots.at(sp).centerX / 2000.0 + 0.5;
|
|
centy[sp] = params->locallab.spots.at(sp).centerY / 2000.0 + 0.5;
|
|
|
|
const bool fullimstd = params->locallab.spots.at(sp).fullimage;//for log encoding standard
|
|
const bool fullimjz = true;//always force fullimage in log encoding Jz - always possible to put a checkbox if need
|
|
|
|
if ((log[sp] && autocomput[sp]) || (cie[sp] && autocie[sp])) {
|
|
constexpr int SCALE = 10;
|
|
int fw, fh, tr = TR_NONE;
|
|
imgsrc->getFullSize(fw, fh, tr);
|
|
PreviewProps pp(0, 0, fw, fh, SCALE);
|
|
|
|
float ysta = std::max(static_cast<float>(centy[sp] - locyT[sp]), 0.f);
|
|
float yend = std::min(static_cast<float>(centy[sp] + locy[sp]), 1.f);
|
|
float xsta = std::max(static_cast<float>(centx[sp] - locxL[sp]), 0.f);
|
|
float xend = std::min(static_cast<float>(centx[sp] + locx[sp]), 1.f);
|
|
|
|
if (fullimstd && (log[sp] && autocomput[sp])) {
|
|
ysta = 0.f;
|
|
yend = 1.f;
|
|
xsta = 0.f;
|
|
xend = 1.f;
|
|
}
|
|
|
|
if (fullimjz && (cie[sp] && autocie[sp])) {
|
|
ysta = 0.f;
|
|
yend = 1.f;
|
|
xsta = 0.f;
|
|
xend = 1.f;
|
|
}
|
|
ipf.getAutoLogloc(sp, imgsrc, sourceg, blackev, whiteev, Autogr, sourceab, whits, blacks, whitslog, blackslog, fw, fh, xsta, xend, ysta, yend, SCALE);
|
|
params->locallab.spots.at(sp).blackEv = blackev[sp];
|
|
params->locallab.spots.at(sp).whiteEv = whiteev[sp];
|
|
params->locallab.spots.at(sp).blackEvjz = blackev[sp];
|
|
params->locallab.spots.at(sp).whiteEvjz = whiteev[sp];
|
|
params->locallab.spots.at(sp).sourceGray = sourceg[sp];
|
|
params->locallab.spots.at(sp).sourceabs = sourceab[sp];
|
|
params->locallab.spots.at(sp).sourceGraycie = sourceg[sp];
|
|
params->locallab.spots.at(sp).sourceabscie = sourceab[sp];
|
|
params->locallab.spots.at(sp).whitescie = whits[sp];
|
|
params->locallab.spots.at(sp).blackscie = blacks[sp];
|
|
params->locallab.spots.at(sp).whiteslog = whitslog[sp];
|
|
params->locallab.spots.at(sp).blackslog = blackslog[sp];
|
|
float jz1 = defSpot.jz100;
|
|
|
|
LocallabListener::locallabcieBEF locciebef;
|
|
locciebef.blackevbef = blackev[sp];
|
|
locciebef.whiteevbef = whiteev[sp];
|
|
locciebef.sourcegbef = sourceg[sp];
|
|
locciebef.sourceabbef = sourceab[sp];
|
|
locciebef.targetgbef = targetg[sp];
|
|
locciebef.autocomputbef = autocomput[sp];
|
|
locciebef.autociebef = autocie[sp];
|
|
locciebef.jz1bef = jz1;
|
|
locallciebef.push_back(locciebef);
|
|
|
|
if (locallListener) {
|
|
locallListener->ciebefChanged(locallciebef,params->locallab.selspot);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] locx;
|
|
delete [] locy;
|
|
delete [] locxL;
|
|
delete [] locyT;
|
|
delete [] centx;
|
|
delete [] centy;
|
|
|
|
delete [] autocie;
|
|
delete [] Autogr;
|
|
delete [] whiteev;
|
|
delete [] blackev;
|
|
delete [] targetg;
|
|
delete [] sourceab;
|
|
delete [] whits;
|
|
delete [] blacks;
|
|
delete [] whitslog;
|
|
delete [] blackslog;
|
|
delete [] sourceg;
|
|
delete [] cie;
|
|
delete [] log;
|
|
delete [] autocomput;
|
|
}
|
|
}
|
|
|
|
|
|
if ((todo & (M_AUTOEXP | M_RGBCURVE | M_CROP)) && params->locallab.enabled && !params->locallab.spots.empty()) {
|
|
|
|
ipf.rgb2lab(*oprevi, *oprevl, params->icm.workingProfile);
|
|
|
|
nprevl->CopyFrom(oprevl);
|
|
// int maxspot = 1;
|
|
//*************************************************************
|
|
// locallab
|
|
//*************************************************************
|
|
|
|
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
* 2017 2018 Jacques Desmis <jdesmis@gmail.com>
|
|
* 2019 Pierre Cabrera <pierre.cab@gmail.com>
|
|
*/
|
|
const std::unique_ptr<LabImage> reserv(new LabImage(*oprevl, true));
|
|
const std::unique_ptr<LabImage> lastorigimp(new LabImage(*oprevl, true));
|
|
std::unique_ptr<LabImage> savenormdr;
|
|
std::unique_ptr<LabImage> savenormtm;
|
|
std::unique_ptr<LabImage> savenormreti;
|
|
float **shbuffer = nullptr;
|
|
int sca = 1;
|
|
double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre;
|
|
float avge, meantme, stdtme, meanretie, stdretie;
|
|
//std::vector<LocallabListener::locallabRef> locallref;
|
|
std::vector<LocallabListener::locallabRetiMinMax> locallretiminmax;
|
|
std::vector<LocallabListener::locallabcieLC> locallcielc;
|
|
std::vector<LocallabListener::locallabshGHS> locallshgsh;
|
|
std::vector<LocallabListener::locallabshGHSbw> locallshgshbw;
|
|
std::vector<LocallabListener::locallabsetLC> locallsetlc;
|
|
std::vector<LocallabListener::locallabcieSIG> locallciesig;
|
|
huerefs.resize(params->locallab.spots.size());
|
|
huerefblurs.resize(params->locallab.spots.size());
|
|
chromarefblurs.resize(params->locallab.spots.size());
|
|
lumarefblurs.resize(params->locallab.spots.size());
|
|
chromarefs.resize(params->locallab.spots.size());
|
|
lumarefs.resize(params->locallab.spots.size());
|
|
sobelrefs.resize(params->locallab.spots.size());
|
|
avgs.resize(params->locallab.spots.size());
|
|
meantms.resize(params->locallab.spots.size());
|
|
stdtms.resize(params->locallab.spots.size());
|
|
meanretis.resize(params->locallab.spots.size());
|
|
stdretis.resize(params->locallab.spots.size());
|
|
const int sizespot = (int)params->locallab.spots.size();
|
|
|
|
float *huerefp = nullptr;
|
|
huerefp = new float[sizespot];
|
|
float *chromarefp = nullptr;
|
|
chromarefp = new float[sizespot];
|
|
float *lumarefp = nullptr;
|
|
lumarefp = new float[sizespot];
|
|
float *fabrefp = nullptr;
|
|
fabrefp = new float[sizespot];
|
|
//new controls mainfp and scopefp with multi spots
|
|
int *mainfp = nullptr;
|
|
mainfp = new int[sizespot];
|
|
int *scopefp = nullptr;
|
|
scopefp = new int[sizespot];
|
|
|
|
for (int sp = 0; sp < (int)params->locallab.spots.size(); sp++) {
|
|
|
|
if (params->locallab.spots.at(sp).equiltm && params->locallab.spots.at(sp).exptonemap) {
|
|
savenormtm.reset(new LabImage(*oprevl, true));
|
|
}
|
|
|
|
if (params->locallab.spots.at(sp).equilret && params->locallab.spots.at(sp).expreti) {
|
|
savenormreti.reset(new LabImage(*oprevl, true));
|
|
}
|
|
|
|
// if(params->locallab.spots.at(sp).colorscope != 30) {//compatibility with old method in controlspotpanel to change scope - default value 30
|
|
// scopefp[sp]= params->locallab.spots.at(sp).colorscope;
|
|
// }
|
|
|
|
// Set local curves of current spot to LUT
|
|
locRETgainCurve.Set(params->locallab.spots.at(sp).localTgaincurve);
|
|
locRETtransCurve.Set(params->locallab.spots.at(sp).localTtranscurve);
|
|
const bool LHutili = loclhCurve.Set(params->locallab.spots.at(sp).LHcurve);
|
|
const bool HHutili = lochhCurve.Set(params->locallab.spots.at(sp).HHcurve);
|
|
const bool CHutili = locchCurve.Set(params->locallab.spots.at(sp).CHcurve);
|
|
const bool HHutilijz = lochhCurvejz.Set(params->locallab.spots.at(sp).HHcurvejz);
|
|
const bool CHutilijz = locchCurvejz.Set(params->locallab.spots.at(sp).CHcurvejz);
|
|
const bool LHutilijz = loclhCurvejz.Set(params->locallab.spots.at(sp).LHcurvejz);
|
|
const bool lcmasutili = locccmasCurve.Set(params->locallab.spots.at(sp).CCmaskcurve);
|
|
const bool llmasutili = locllmasCurve.Set(params->locallab.spots.at(sp).LLmaskcurve);
|
|
const bool lhmasutili = lochhmasCurve.Set(params->locallab.spots.at(sp).HHmaskcurve);
|
|
const bool lhhmasutili = lochhhmasCurve.Set(params->locallab.spots.at(sp).HHhmaskcurve);
|
|
const bool lhhmascieutili = lochhhmascieCurve.Set(params->locallab.spots.at(sp).HHhmaskciecurve);
|
|
const bool llmasexputili = locllmasexpCurve.Set(params->locallab.spots.at(sp).LLmaskexpcurve);
|
|
const bool lcmasexputili = locccmasexpCurve.Set(params->locallab.spots.at(sp).CCmaskexpcurve);
|
|
const bool lhmasexputili = lochhmasexpCurve.Set(params->locallab.spots.at(sp).HHmaskexpcurve);
|
|
const bool llmasSHutili = locllmasSHCurve.Set(params->locallab.spots.at(sp).LLmaskSHcurve);
|
|
const bool lcmasSHutili = locccmasSHCurve.Set(params->locallab.spots.at(sp).CCmaskSHcurve);
|
|
const bool lhmasSHutili = lochhmasSHCurve.Set(params->locallab.spots.at(sp).HHmaskSHcurve);
|
|
const bool llmasvibutili = locllmasvibCurve.Set(params->locallab.spots.at(sp).LLmaskvibcurve);
|
|
const bool lcmasvibutili = locccmasvibCurve.Set(params->locallab.spots.at(sp).CCmaskvibcurve);
|
|
const bool lhmasvibutili = lochhmasvibCurve.Set(params->locallab.spots.at(sp).HHmaskvibcurve);
|
|
const bool llmascbutili = locllmascbCurve.Set(params->locallab.spots.at(sp).LLmaskcbcurve);
|
|
const bool lcmascbutili = locccmascbCurve.Set(params->locallab.spots.at(sp).CCmaskcbcurve);
|
|
const bool lhmascbutili = lochhmascbCurve.Set(params->locallab.spots.at(sp).HHmaskcbcurve);
|
|
const bool llmaslcutili = locllmaslcCurve.Set(params->locallab.spots.at(sp).LLmasklccurve);
|
|
const bool lcmaslcutili = locccmaslcCurve.Set(params->locallab.spots.at(sp).CCmasklccurve);
|
|
const bool lhmaslcutili = lochhmaslcCurve.Set(params->locallab.spots.at(sp).HHmasklccurve);
|
|
const bool llmasretiutili = locllmasretiCurve.Set(params->locallab.spots.at(sp).LLmaskreticurve);
|
|
const bool lcmasretiutili = locccmasretiCurve.Set(params->locallab.spots.at(sp).CCmaskreticurve);
|
|
const bool lhmasretiutili = lochhmasretiCurve.Set(params->locallab.spots.at(sp).HHmaskreticurve);
|
|
const bool llmastmutili = locllmastmCurve.Set(params->locallab.spots.at(sp).LLmasktmcurve);
|
|
const bool lcmastmutili = locccmastmCurve.Set(params->locallab.spots.at(sp).CCmasktmcurve);
|
|
const bool lhmastmutili = lochhmastmCurve.Set(params->locallab.spots.at(sp).HHmasktmcurve);
|
|
const bool llmasblutili = locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve);
|
|
const bool lcmasblutili = locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve);
|
|
const bool lhmasblutili = lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve);
|
|
const bool llmaslogutili = locllmaslogCurve.Set(params->locallab.spots.at(sp).LLmaskcurveL);
|
|
const bool lcmaslogutili = locccmaslogCurve.Set(params->locallab.spots.at(sp).CCmaskcurveL);
|
|
const bool lhmaslogutili = lochhmaslogCurve.Set(params->locallab.spots.at(sp).HHmaskcurveL);
|
|
const bool llmascieutili = locllmascieCurve.Set(params->locallab.spots.at(sp).LLmaskciecurve);
|
|
const bool lcmascieutili = locccmascieCurve.Set(params->locallab.spots.at(sp).CCmaskciecurve);
|
|
const bool lhmascieutili = lochhmascieCurve.Set(params->locallab.spots.at(sp).HHmaskciecurve);
|
|
const bool lcmas_utili = locccmas_Curve.Set(params->locallab.spots.at(sp).CCmask_curve);
|
|
const bool llmas_utili = locllmas_Curve.Set(params->locallab.spots.at(sp).LLmask_curve);
|
|
const bool lhmas_utili = lochhmas_Curve.Set(params->locallab.spots.at(sp).HHmask_curve);
|
|
const bool lhhmas_utili = lochhhmas_Curve.Set(params->locallab.spots.at(sp).HHhmask_curve);
|
|
const bool lmasutiliblwav = loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav);
|
|
const bool lmasutilicolwav = loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav);
|
|
const bool lmasutiliciewav = loclmasCurveciewav.Set(params->locallab.spots.at(sp).LLmaskciecurvewav);
|
|
const bool locwavutili = locwavCurve.Set(params->locallab.spots.at(sp).locwavcurve);
|
|
const bool locwavutilijz = locwavCurvejz.Set(params->locallab.spots.at(sp).locwavcurvejz);
|
|
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 locwavhueutili = locwavCurvehue.Set(params->locallab.spots.at(sp).locwavcurvehue);
|
|
const bool locwavdenutili = locwavCurveden.Set(params->locallab.spots.at(sp).locwavcurveden);
|
|
const bool locedgwavutili = locedgwavCurve.Set(params->locallab.spots.at(sp).locedgwavcurve);
|
|
const bool lmasutili_wav = loclmasCurve_wav.Set(params->locallab.spots.at(sp).LLmask_curvewav);
|
|
const bool locallutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).llcurve, lllocalcurve, sca);
|
|
const bool localclutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).clcurve, cllocalcurve, sca);
|
|
const bool locallcutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).lccurve, lclocalcurve, sca);
|
|
const bool localcutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).cccurve, cclocalcurve, sca);
|
|
const bool localrgbutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).rgbcurve, rgblocalcurve, sca);
|
|
const bool localexutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).excurve, exlocalcurve, sca);
|
|
const bool localmaskutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, sca);
|
|
const bool localmaskexputili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, sca);
|
|
const bool localmaskSHutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, sca);
|
|
// const bool localghsutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).ghscurve, ghslocalcurve, sca);
|
|
const bool localmaskvibutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, sca);
|
|
const bool localmasktmutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, sca);
|
|
const bool localmaskretiutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, sca);
|
|
const bool localmaskcbutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, sca);
|
|
const bool localmaskblutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, sca);
|
|
const bool localmasklcutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, sca);
|
|
const bool localmasklogutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).LmaskcurveL, lmaskloglocalcurve, sca);
|
|
const bool localmask_utili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmask_curve, lmasklocal_curve, sca);
|
|
const bool localmaskcieutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).Lmaskciecurve, lmaskcielocalcurve, sca);
|
|
const bool localcieutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).ciecurve, cielocalcurve, sca);
|
|
const bool localcieutili2 = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).ciecurve2, cielocalcurve2, sca);
|
|
const bool localjzutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).jzcurve, jzlocalcurve, sca);
|
|
const bool localczutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).czcurve, czlocalcurve, sca);
|
|
const bool localczjzutili = CurveFactory::diagonalCurve2Lut(params->locallab.spots.at(sp).czjzcurve, czjzlocalcurve, sca);
|
|
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;
|
|
double cont = params->locallab.spots.at(sp).contrast;
|
|
float contsig = params->locallab.spots.at(sp).contsigqcie;
|
|
|
|
float lightsig = params->locallab.spots.at(sp).lightsigqcie;
|
|
|
|
if (black < 0. && params->locallab.spots.at(sp).expMethod == "pde") {
|
|
black *= 1.5;
|
|
}
|
|
|
|
// Reference parameters computation
|
|
if (params->locallab.spots.at(sp).spotMethod == "exc") {
|
|
ipf.calc_ref(sp, reserv.get(), reserv.get(), 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
|
|
} else {
|
|
ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili);
|
|
}
|
|
|
|
meantme = 0.f;
|
|
stdtme = 0.f;
|
|
meanretie = 0.f;
|
|
stdretie = 0.f;
|
|
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;
|
|
bool istm = params->locallab.spots.at(sp).equiltm && params->locallab.spots.at(sp).exptonemap;
|
|
bool isreti = params->locallab.spots.at(sp).equilret && params->locallab.spots.at(sp).expreti;
|
|
//preparation for mean and sigma on current RT-spot
|
|
float locx = 0.f;
|
|
float locy = 0.f;
|
|
float locxl = 0.f;
|
|
float locyt = 0.f;
|
|
float centx = 0.f;
|
|
float centy = 0.f;
|
|
float ysta = 0.f;
|
|
float yend = 1.f;
|
|
float xsta = 0.f;
|
|
float xend = 1.f;
|
|
|
|
if (istm || isreti) {
|
|
locx = params->locallab.spots.at(sp).loc.at(0) / 2000.0;
|
|
locy = params->locallab.spots.at(sp).loc.at(2) / 2000.0;
|
|
locxl = params->locallab.spots.at(sp).loc.at(1) / 2000.0;
|
|
locyt = params->locallab.spots.at(sp).loc.at(3) / 2000.0;
|
|
centx = params->locallab.spots.at(sp).centerX / 2000.0 + 0.5;
|
|
centy = params->locallab.spots.at(sp).centerY / 2000.0 + 0.5;
|
|
ysta = std::max(static_cast<float>(centy - locyt), 0.f);
|
|
yend = std::min(static_cast<float>(centy + locy), 1.f);
|
|
xsta = std::max(static_cast<float>(centx - locxl), 0.f);
|
|
xend = std::min(static_cast<float>(centx + locx), 1.f);
|
|
}
|
|
|
|
int ww = nprevl->W;
|
|
int hh = nprevl->H;
|
|
int xxs = xsta * ww;
|
|
int xxe = xend * ww;
|
|
int yys = ysta * hh;
|
|
int yye = yend * hh;
|
|
|
|
if (istm) { //calculate mean and sigma on full image for RT-spot use by normalize_mean_dt
|
|
ipf.mean_sig(nprevl->L, meantme, stdtme, xxs, xxe, yys, yye);
|
|
}
|
|
|
|
if (isreti) { //calculate mean and sigma on full image for RT-spot use by normalize_mean_dt
|
|
ipf.mean_sig(nprevl->L, meanretie, stdretie, xxs, xxe, yys, yye) ;
|
|
}
|
|
|
|
double huerblu = huerefblurs[sp] = huerefblu;
|
|
double chromarblu = chromarefblurs[sp] = chromarefblu;
|
|
double lumarblu = lumarefblurs[sp] = lumarefblu;
|
|
double huer = huerefs[sp] = huere;
|
|
double chromar = chromarefs[sp] = chromare;
|
|
double lumar = lumarefs[sp] = lumare ;
|
|
double sobeler = sobelrefs[sp] = sobelre;
|
|
float avg = avgs[sp] = avge;
|
|
float meantm = meantms[sp] = meantme;
|
|
float stdtm = stdtms[sp] = stdtme;
|
|
float meanreti = meanretis[sp] = meanretie;
|
|
float stdreti = stdretis[sp] = stdretie;
|
|
huerefp[sp] = huer;
|
|
chromarefp[sp] = chromar;
|
|
lumarefp[sp] = lumar;
|
|
|
|
CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumar,
|
|
hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avg,
|
|
sca);
|
|
|
|
// Save Locallab mask curve references for current spot
|
|
// Locallab tools computation
|
|
/* Notes:
|
|
* - shbuffer is used as nullptr
|
|
*/
|
|
|
|
// Locallab mask is only showed in detailed image
|
|
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 ghscur[24];//42 +4 12 11
|
|
int ghsbpwp[2];
|
|
ghsbpwp[0] = 0;
|
|
ghsbpwp[1] = 0;
|
|
float ghsbpwpvalue[2];
|
|
ghsbpwpvalue[0] = 0.f;
|
|
ghsbpwpvalue[1] = 1.f;;
|
|
|
|
|
|
Glib::ustring prof = params->icm.workingProfile;
|
|
if(params->locallab.spots.at(sp).complexcie == 2) {
|
|
params->locallab.spots.at(sp).primMethod = prof;//in Basic mode set to Working profile
|
|
}
|
|
|
|
ipf.Lab_Local(3, sp, (float**)shbuffer, nprevl, nprevl, reserv.get(), savenormtm.get(), savenormreti.get(), lastorigimp.get(), fw, fh, 0, 0, pW, pH, pW, pH, pW, pH, scale, locRETgainCurve, locRETtransCurve,
|
|
lllocalcurve, locallutili,
|
|
cllocalcurve, localclutili,
|
|
lclocalcurve, locallcutili,
|
|
loclhCurve, lochhCurve, locchCurve,
|
|
lochhCurvejz, locchCurvejz, loclhCurvejz,
|
|
lmasklocalcurve, localmaskutili,
|
|
lmaskexplocalcurve, localmaskexputili,
|
|
lmaskSHlocalcurve, localmaskSHutili,
|
|
// ghslocalcurve, localghsutili,
|
|
lmaskviblocalcurve, localmaskvibutili,
|
|
lmasktmlocalcurve, localmasktmutili,
|
|
lmaskretilocalcurve, localmaskretiutili,
|
|
lmaskcblocalcurve, localmaskcbutili,
|
|
lmaskbllocalcurve, localmaskblutili,
|
|
lmasklclocalcurve, localmasklcutili,
|
|
lmaskloglocalcurve, localmasklogutili,
|
|
lmasklocal_curve, localmask_utili,
|
|
lmaskcielocalcurve, localmaskcieutili,
|
|
cielocalcurve, localcieutili,
|
|
cielocalcurve2, localcieutili2,
|
|
jzlocalcurve, localjzutili,
|
|
czlocalcurve, localczutili,
|
|
czjzlocalcurve, localczjzutili,
|
|
|
|
locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, lochhhmascieCurve, lhhmascieutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili,
|
|
locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili,
|
|
locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili,
|
|
locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili,
|
|
locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili,
|
|
locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili,
|
|
locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili,
|
|
locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili,
|
|
locccmaslogCurve, lcmaslogutili, locllmaslogCurve, llmaslogutili, lochhmaslogCurve, lhmaslogutili,
|
|
|
|
locccmas_Curve, lcmas_utili, locllmas_Curve, llmas_utili, lochhmas_Curve, lhmas_utili,
|
|
locccmascieCurve, lcmascieutili, locllmascieCurve, llmascieutili, lochhmascieCurve, lhmascieutili,
|
|
|
|
lochhhmas_Curve, lhhmas_utili,
|
|
loclmasCurveblwav, lmasutiliblwav,
|
|
loclmasCurvecolwav, lmasutilicolwav,
|
|
loclmasCurveciewav, lmasutiliciewav,
|
|
locwavCurve, locwavutili,
|
|
locwavCurvejz, locwavutilijz,
|
|
loclevwavCurve, loclevwavutili,
|
|
locconwavCurve, locconwavutili,
|
|
loccompwavCurve, loccompwavutili,
|
|
loccomprewavCurve, loccomprewavutili,
|
|
locwavCurvehue, locwavhueutili,
|
|
locwavCurveden, locwavdenutili,
|
|
locedgwavCurve, locedgwavutili,
|
|
loclmasCurve_wav, lmasutili_wav,
|
|
LHutili, HHutili, CHutili, HHutilijz, CHutilijz, LHutilijz, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc,
|
|
huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, 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,
|
|
meantm, stdtm, meanreti, stdreti, fab, maxicam, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, prim, ill, contsig, lightsig,
|
|
highresi, nresi, highresi46, nresi46, Lhighresi, Lnresi, Lhighresi46, Lnresi46,
|
|
ghscur, ghsbpwp, ghsbpwpvalue
|
|
);
|
|
|
|
|
|
fabrefp[sp] = fab;
|
|
|
|
//Illuminant
|
|
float w_x = 0.3f;
|
|
float w_y = 0.3f;
|
|
if(ill == 2) {
|
|
w_x = 0.3457f;
|
|
w_y = 0.3585f;
|
|
} else if(ill == 4) {
|
|
w_x = 0.3217f;
|
|
w_y = 0.3377f;
|
|
} else if(ill == 5) {
|
|
w_x = 0.3127f;
|
|
w_y = 0.3290f;
|
|
} else if(ill == 1) {
|
|
w_x = 0.376137f;
|
|
w_y = 0.374021f;
|
|
} else if(ill == 3) {
|
|
w_x = 0.332424f;
|
|
w_y = 0.347426f;
|
|
} else if(ill == 6) {
|
|
w_x = 0.293756f;
|
|
w_y = 0.309185f;
|
|
} else if(ill == 7) {//D120
|
|
w_x = 0.269669f;
|
|
w_y = 0.28078f;
|
|
} else if(ill == 8) {//stdA
|
|
w_x = 0.447573f;
|
|
w_y = 0.407440f;
|
|
} else if(ill == 9) {//2000K
|
|
w_x = 0.526591f;
|
|
w_y = 0.41331f;
|
|
} else if(ill == 10) {//1500K
|
|
w_x = 0.585703f;
|
|
w_y = 0.393157f;
|
|
} else if(ill == 20) {
|
|
w_x = 0.333333f;
|
|
w_y = 0.333333f;
|
|
}
|
|
//move white-point in GUI
|
|
double refin = params->locallab.spots.at(sp).refi;
|
|
double arefi = (w_y - meany) / (w_x - meanx);
|
|
double brefi = w_y - arefi * w_x;
|
|
double scalrefi = meanx - w_x;
|
|
w_x = w_x + scalrefi * refin;
|
|
w_y = w_x * arefi + brefi;
|
|
|
|
|
|
|
|
if (istm) { //calculate mean and sigma on full image for use by normalize_mean_dt
|
|
float meanf = 0.f;
|
|
float stdf = 0.f;
|
|
ipf.mean_sig(savenormtm->L, meanf, stdf, xxs, xxe, yys, yye);
|
|
|
|
//using 2 unused variables noiselumc and softradiustm
|
|
params->locallab.spots.at(sp).noiselumc = (int) meanf;
|
|
params->locallab.spots.at(sp).softradiustm = stdf ;
|
|
}
|
|
|
|
if (isreti) { //calculate mean and sigma on full image for use by normalize_mean_dt
|
|
float meanf = 0.f;
|
|
float stdf = 0.f;
|
|
ipf.mean_sig(savenormreti->L, meanf, stdf, xxs, xxe, yys, yye);
|
|
//using 2 unused variables sensihs and sensiv
|
|
params->locallab.spots.at(sp).sensihs = (int) meanf;
|
|
params->locallab.spots.at(sp).sensiv = (int) stdf;
|
|
}
|
|
|
|
|
|
if (sp + 1u < params->locallab.spots.size()) {
|
|
// do not copy for last spot as it is not needed anymore
|
|
lastorigimp->CopyFrom(nprevl);
|
|
}
|
|
|
|
// Save Locallab Retinex min/max for current spot
|
|
LocallabListener::locallabRetiMinMax retiMinMax;
|
|
retiMinMax.cdma = maxCD;
|
|
retiMinMax.cdmin = minCD;
|
|
retiMinMax.mini = mini;
|
|
retiMinMax.maxi = maxi;
|
|
retiMinMax.Tmean = Tmean;
|
|
retiMinMax.Tsigma = Tsigma;
|
|
retiMinMax.Tmin = Tmin;
|
|
retiMinMax.Tmax = Tmax;
|
|
locallretiminmax.push_back(retiMinMax);
|
|
|
|
//save Locallab CIE primaries and white for current spot
|
|
LocallabListener::locallabcieLC loccielc;
|
|
loccielc.redxlc = rdx;
|
|
loccielc.redylc = rdy;
|
|
loccielc.grexlc = grx;
|
|
loccielc.greylc = gry;
|
|
loccielc.bluxlc = blx;
|
|
loccielc.bluylc = bly;
|
|
loccielc.wxlc = w_x;
|
|
loccielc.wylc = w_y;
|
|
loccielc.meanxlc = meanx;
|
|
loccielc.meanylc = meany;
|
|
loccielc.meanxelc = meanxe;
|
|
loccielc.meanyelc = meanye;
|
|
locallcielc.push_back(loccielc);
|
|
|
|
LocallabListener::locallabcieSIG locciesig;
|
|
locciesig.contsigq = contsig;
|
|
locciesig.lightsigq = lightsig;
|
|
locallciesig.push_back(locciesig);
|
|
/*
|
|
int reset = 0;
|
|
if(params->locallab.spots.at(sp).ghsMode == "lin") {
|
|
reset = 0;
|
|
} else {
|
|
reset = 1;
|
|
}
|
|
*/
|
|
LocallabListener::locallabshGHS locshghs;//ghs S curve 42 or labgrid 12
|
|
for(int j = 0; j < 24; j++) {//+4 12 11
|
|
locshghs.ghsc[j] = ghscur[j];
|
|
}
|
|
// locshghs.licur = reset;
|
|
/*
|
|
printf("ghsc0=%f ghsc1=%f\n", (double) locshghs.ghsc[0], (double) locshghs.ghsc[1]);
|
|
printf("ghsc20=%f ghsc21=%f\n", (double) locshghs.ghsc[20], (double) locshghs.ghsc[21]);
|
|
printf("ghsc40=%f ghsc41=%f\n", (double) locshghs.ghsc[40], (double) locshghs.ghsc[41]);
|
|
printf("OK 2 impro\n");
|
|
*/
|
|
locallshgsh.push_back(locshghs);
|
|
|
|
LocallabListener::locallabshGHSbw locshghsbw;//ghs S curve
|
|
for(int j = 0; j < 2; j++) {
|
|
locshghsbw.ghsbw[j] = ghsbpwp[j];
|
|
locshghsbw.ghsbwvalue[j] = ghsbpwpvalue[j];
|
|
}
|
|
locallshgshbw.push_back(locshghsbw);
|
|
|
|
|
|
// Recalculate references after
|
|
if (params->locallab.spots.at(sp).spotMethod == "exc") {
|
|
ipf.calc_ref(sp, reserv.get(), reserv.get(), 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili);
|
|
} else {
|
|
ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili);
|
|
}
|
|
|
|
// Update Locallab reference values according to recurs parameter
|
|
if (params->locallab.spots.at(sp).recurs) {
|
|
huerefp[sp] = huer;
|
|
chromarefp[sp] = chromar;
|
|
lumarefp[sp] = lumar;
|
|
fabrefp[sp] = fab;
|
|
|
|
}
|
|
|
|
|
|
|
|
// new used linked to global and scope
|
|
mainfp[sp] = 0;
|
|
if (params->locallab.spots.at(sp).spotMethod == "main") {
|
|
mainfp[sp] = 3;
|
|
} else if (params->locallab.spots.at(sp).spotMethod == "full") {
|
|
mainfp[sp] = 2;
|
|
}
|
|
//keep using tools
|
|
bool iscolor = params->locallab.spots.at(sp).expcolor;
|
|
bool issh = params->locallab.spots.at(sp).expshadhigh;
|
|
bool isvib = params->locallab.spots.at(sp).expvibrance;
|
|
bool isexpos = params->locallab.spots.at(sp).expexpose;
|
|
bool issoft = params->locallab.spots.at(sp).expsoft;
|
|
bool isblur = params->locallab.spots.at(sp).expblur;
|
|
bool istom = params->locallab.spots.at(sp).exptonemap;
|
|
bool isret = params->locallab.spots.at(sp).expreti;
|
|
bool issharp = params->locallab.spots.at(sp).expsharp;
|
|
bool iscont = params->locallab.spots.at(sp).expcontrast;
|
|
bool iscbdl = params->locallab.spots.at(sp).expcbdl;
|
|
bool islog = params->locallab.spots.at(sp).explog;
|
|
bool ismas = params->locallab.spots.at(sp).expmask;
|
|
bool iscie = params->locallab.spots.at(sp).expcie;
|
|
// bool isset = iscolor || issh || isvib;
|
|
|
|
//set select spot settings
|
|
LocallabListener::locallabsetLC locsetlc;
|
|
locsetlc.mainf = mainfp[sp];
|
|
locsetlc.iscolo = iscolor;
|
|
locsetlc.iss = issh;
|
|
locsetlc.isvi = isvib;
|
|
locsetlc.isexpo = isexpos;
|
|
locsetlc.issof = issoft;
|
|
locsetlc.isblu = isblur;
|
|
locsetlc.isto = istom;
|
|
locsetlc.isre = isret;
|
|
locsetlc.isshar = issharp;
|
|
locsetlc.iscon = iscont;
|
|
locsetlc.iscbd = iscbdl;
|
|
locsetlc.islo = islog;
|
|
locsetlc.isma = ismas;
|
|
locsetlc.isci = iscie;
|
|
locallsetlc.push_back(locsetlc);
|
|
|
|
if (locallListener) {
|
|
locallListener->refChanged2(huerefp, chromarefp, lumarefp, fabrefp, params->locallab.selspot);
|
|
locallListener->minmaxChanged(locallretiminmax, params->locallab.selspot);
|
|
if (params->locallab.spots.at(sp).expprecam) {
|
|
locallListener->cieChanged(locallcielc,params->locallab.selspot);
|
|
}
|
|
locallListener->sigChanged(locallciesig,params->locallab.selspot);
|
|
if (params->locallab.spots.at(sp).expshadhigh && params->locallab.spots.at(sp).shMethod == "ghs") {
|
|
locallListener->ghsChanged(locallshgsh,params->locallab.selspot);//curve
|
|
locallListener->ghsbwChanged(locallshgshbw,params->locallab.selspot);//Black and White point
|
|
}
|
|
|
|
/*
|
|
if(params->locallab.spots.at(sp).colorscope != 0) {//compatibility with old method in controlspotpanel
|
|
locallListener->scopeChangedcol(scopefp[sp], params->locallab.selspot, iscolor);
|
|
locallListener->scopeChangedsh(scopefp[sp], params->locallab.selspot, issh);
|
|
locallListener->scopeChangedvib(scopefp[sp], params->locallab.selspot, isvib);
|
|
locallListener->scopeChangedset(scopefp[sp], params->locallab.selspot, isset);
|
|
//params->locallab.spots.at(sp).colorscope = 30;
|
|
}
|
|
*/
|
|
// if (mainfp[sp] >= 0) {//minimize call to idle register
|
|
//used by Global fullimage.
|
|
locallListener->maiChanged(locallsetlc,params->locallab.selspot);
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete [] huerefp;
|
|
delete [] chromarefp;
|
|
delete [] lumarefp;
|
|
delete [] fabrefp;
|
|
delete [] mainfp;
|
|
delete [] scopefp;
|
|
ipf.lab2rgb(*nprevl, *oprevi, params->icm.workingProfile);
|
|
//*************************************************************
|
|
// end locallab
|
|
//*************************************************************
|
|
|
|
}
|
|
|
|
if ((todo & M_RGBCURVE) || (todo & M_CROP)) {
|
|
//complexCurve also calculated pre-curves histogram depending on crop
|
|
CurveFactory::complexCurve(params->toneCurve.expcomp, params->toneCurve.black / 65535.0,
|
|
params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh,
|
|
params->toneCurve.shcompr, params->toneCurve.brightness, params->toneCurve.contrast,
|
|
params->toneCurve.curve, params->toneCurve.curve2,
|
|
vhist16, hltonecurve, shtonecurve, tonecurve, histToneCurve, customToneCurve1, customToneCurve2, 1);
|
|
|
|
CurveFactory::RGBCurve(params->rgbCurves.rcurve, rCurve, 1);
|
|
CurveFactory::RGBCurve(params->rgbCurves.gcurve, gCurve, 1);
|
|
CurveFactory::RGBCurve(params->rgbCurves.bcurve, bCurve, 1);
|
|
|
|
|
|
opautili = false;
|
|
|
|
if (params->colorToning.enabled) {
|
|
TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile);
|
|
double wp[3][3] = {
|
|
{wprof[0][0], wprof[0][1], wprof[0][2]},
|
|
{wprof[1][0], wprof[1][1], wprof[1][2]},
|
|
{wprof[2][0], wprof[2][1], wprof[2][2]}
|
|
};
|
|
params->colorToning.getCurves(ctColorCurve, ctOpacityCurve, wp, opautili);
|
|
CurveFactory::diagonalCurve2Lut(params->colorToning.clcurve, clToningcurve, scale == 1 ? 1 : 16);
|
|
CurveFactory::diagonalCurve2Lut(params->colorToning.cl2curve, cl2Toningcurve, scale == 1 ? 1 : 16);
|
|
}
|
|
|
|
if (params->blackwhite.enabled) {
|
|
CurveFactory::curveBW(params->blackwhite.beforeCurve, params->blackwhite.afterCurve, vhist16bw, histToneCurveBW, beforeToneCurveBW, afterToneCurveBW, 1);
|
|
}
|
|
|
|
colourToningSatLimit = float (params->colorToning.satProtectionThreshold) / 100.f * 0.7f + 0.3f;
|
|
colourToningSatLimitOpacity = 1.f - (float (params->colorToning.saturatedOpacity) / 100.f);
|
|
|
|
int satTH = 80;
|
|
int satPR = 30;
|
|
int indi = 0;
|
|
|
|
if (params->colorToning.enabled && params->colorToning.autosat && params->colorToning.method != "LabGrid") { //for colortoning evaluation of saturation settings
|
|
float moyS = 0.f;
|
|
float eqty = 0.f;
|
|
ipf.moyeqt(oprevi, moyS, eqty); //return image : mean saturation and standard dev of saturation
|
|
//printf("moy=%f ET=%f\n", moyS,eqty);
|
|
float satp = ((moyS + 1.5f * eqty) - 0.3f) / 0.7f; //1.5 sigma ==> 93% pixels with high saturation -0.3 / 0.7 convert to Hombre scale
|
|
|
|
if (satp >= 0.92f) {
|
|
satp = 0.92f; //avoid values too high (out of gamut)
|
|
}
|
|
|
|
if (satp <= 0.15f) {
|
|
satp = 0.15f; //avoid too low values
|
|
}
|
|
|
|
//satTH=(int) 100.f*satp;
|
|
//satPR=(int) 100.f*(moyS-0.85f*eqty);//-0.85 sigma==>20% pixels with low saturation
|
|
colourToningSatLimit = 100.f * satp;
|
|
satTH = (int) 100.f * satp;
|
|
|
|
colourToningSatLimitOpacity = 100.f * (moyS - 0.85f * eqty); //-0.85 sigma==>20% pixels with low saturation
|
|
satPR = (int) 100.f * (moyS - 0.85f * eqty);
|
|
}
|
|
|
|
if (actListener && params->colorToning.enabled) {
|
|
if (params->blackwhite.enabled && params->colorToning.autosat) {
|
|
actListener->autoColorTonChanged(0, satTH, satPR); //hide sliders only if autosat
|
|
indi = 0;
|
|
} else {
|
|
if (params->colorToning.autosat) {
|
|
if (params->colorToning.method == "Lab") {
|
|
indi = 1;
|
|
} else if (params->colorToning.method == "RGBCurves") {
|
|
indi = 1;
|
|
} else if (params->colorToning.method == "RGBSliders") {
|
|
indi = 1;
|
|
} else if (params->colorToning.method == "Splico") {
|
|
indi = 2;
|
|
} else if (params->colorToning.method == "Splitlr") {
|
|
indi = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if it's just crop we just need the histogram, no image updates
|
|
if (todo & M_RGBCURVE) {
|
|
//initialize rrm bbm ggm different from zero to avoid black screen in some cases
|
|
double rrm = 33.;
|
|
double ggm = 33.;
|
|
double bbm = 33.;
|
|
|
|
DCPProfileApplyState as;
|
|
DCPProfile *dcpProf = imgsrc->getDCP(params->icm, as);
|
|
|
|
ipf.rgbProc(oprevi, oprevl, nullptr, hltonecurve, shtonecurve, tonecurve, params->toneCurve.saturation,
|
|
rCurve, gCurve, bCurve, colourToningSatLimit, colourToningSatLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, beforeToneCurveBW, afterToneCurveBW, rrm, ggm, bbm, bwAutoR, bwAutoG, bwAutoB, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, dcpProf, as, histToneCurve);
|
|
|
|
if (params->blackwhite.enabled && params->blackwhite.autoc && abwListener) {
|
|
if (settings->verbose) {
|
|
printf("ImProcCoordinator / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", static_cast<double>(bwAutoR), static_cast<double>(bwAutoG), static_cast<double>(bwAutoB));
|
|
}
|
|
|
|
abwListener->BWChanged((float) rrm, (float) ggm, (float) bbm);
|
|
}
|
|
|
|
if (params->colorToning.enabled && params->colorToning.autosat && actListener) {
|
|
actListener->autoColorTonChanged(indi, (int) colourToningSatLimit, (int)colourToningSatLimitOpacity); //change sliders autosat
|
|
}
|
|
|
|
// correct GUI black and white with value
|
|
}
|
|
|
|
// ipf.Lab_Tile(oprevl, oprevl, scale);
|
|
|
|
// compute L channel histogram
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
}
|
|
|
|
// lhist16(32768);
|
|
if (todo & (M_LUMACURVE | M_CROP)) {
|
|
LUTu lhist16(32768);
|
|
lhist16.clear();
|
|
#ifdef _OPENMP
|
|
const int numThreads = min(max(pW * pH / (int)lhist16.getSize(), 1), omp_get_max_threads());
|
|
#pragma omp parallel num_threads(numThreads) if (numThreads>1)
|
|
#endif
|
|
{
|
|
LUTu lhist16thr(lhist16.getSize());
|
|
lhist16thr.clear();
|
|
#ifdef _OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
|
|
for (int x = 0; x < pH; x++)
|
|
for (int y = 0; y < pW; y++) {
|
|
int pos = (int)(oprevl->L[x][y]);
|
|
lhist16thr[pos]++;
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
lhist16 += lhist16thr;
|
|
}
|
|
#ifdef _OPENMP
|
|
static_cast<void>(numThreads); // to silence cppcheck warning
|
|
#endif
|
|
CurveFactory::complexLCurve(params->labCurve.brightness, params->labCurve.contrast, params->labCurve.lcurve, lhist16, lumacurve, histLCurve, scale == 1 ? 1 : 16, utili);
|
|
}
|
|
|
|
if (todo & M_LUMACURVE) {
|
|
|
|
clcutili = CurveFactory::diagonalCurve2Lut(params->labCurve.clcurve, clcurve, scale == 1 ? 1 : 16);
|
|
|
|
CurveFactory::complexsgnCurve(autili, butili, ccutili, cclutili, params->labCurve.acurve, params->labCurve.bcurve, params->labCurve.cccurve,
|
|
params->labCurve.lccurve, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, scale == 1 ? 1 : 16);
|
|
}
|
|
|
|
//scale = 1;
|
|
|
|
if ((todo & (M_LUMINANCE + M_COLOR)) || (todo & M_AUTOEXP)) {
|
|
nprevl->CopyFrom(oprevl);
|
|
histCCurve.clear();
|
|
histLCurve.clear();
|
|
|
|
if (params->colorToning.enabled && params->colorToning.method == "LabGrid") {
|
|
ipf.colorToningLabGrid(nprevl, 0, nprevl->W, 0, nprevl->H, false);
|
|
}
|
|
|
|
ipf.shadowsHighlights(nprevl, params->sh.enabled, params->sh.lab, params->sh.highlights, params->sh.shadows, params->sh.radius, scale, params->sh.htonalwidth, params->sh.stonalwidth);
|
|
|
|
if (params->localContrast.enabled) {
|
|
// Alberto's local contrast
|
|
ipf.localContrast(nprevl, nprevl->L, params->localContrast, false, scale);
|
|
}
|
|
|
|
ipf.chromiLuminanceCurve(nullptr, pW, nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, histCCurve, histLCurve);
|
|
ipf.vibrance(nprevl, params->vibrance, params->toneCurve.hrenabled, params->icm.workingProfile);
|
|
ipf.labColorCorrectionRegions(nprevl);
|
|
|
|
// if ((params->colorappearance.enabled && !params->colorappearance.tonecie) || (!params->colorappearance.enabled)) {
|
|
if ((params->colorappearance.enabled && !params->colorappearance.tonecie) || (!cam02)) {
|
|
ipf.EPDToneMap(nprevl, 0, scale);
|
|
}
|
|
|
|
if (params->dirpyrequalizer.cbdlMethod == "aft") {
|
|
// if (((params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled))) {
|
|
if (((params->colorappearance.enabled && !settings->autocielab) || (!cam02))) {
|
|
ipf.dirpyrequalizer(nprevl, scale);
|
|
}
|
|
}
|
|
|
|
wavcontlutili = CurveFactory::diagonalCurve2Lut(params->wavelet.wavclCurve, wavclCurve, scale == 1 ? 1 : 16);
|
|
|
|
if ((params->wavelet.enabled)) {
|
|
WaveletParams WaveParams = params->wavelet;
|
|
WaveParams.getCurves(wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL);
|
|
int kall = 0;
|
|
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(*nprevl, true);
|
|
}
|
|
|
|
if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") {
|
|
provis = params->wavelet.CLmethod;
|
|
params->wavelet.CLmethod = "all";
|
|
ipf.ip_wavelet(nprevl, nprevl, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, scale);
|
|
unshar = new LabImage(*nprevl, true);
|
|
|
|
params->wavelet.CLmethod = provis;
|
|
|
|
WaveParams.expcontrast = false;
|
|
WaveParams.expchroma = false;
|
|
WaveParams.expedge = false;
|
|
WaveParams.expfinal = false;
|
|
WaveParams.exptoning = false;
|
|
WaveParams.expnoise = false;
|
|
}
|
|
|
|
ipf.ip_wavelet(nprevl, nprevl, kall, WaveParams, wavCLVCurve, wavdenoise, wavdenoiseh, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, scale);
|
|
|
|
|
|
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(pW, pH);
|
|
array2D<float> guid(pW, pH);
|
|
Imagefloat *tmpImage = nullptr;
|
|
tmpImage = new Imagefloat(pW, pH);
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int ir = 0; ir < pH; ir++)
|
|
for (int jr = 0; jr < pW; 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 = nprevl->L[ir][jr];
|
|
float aa = nprevl->a[ir][jr];
|
|
float ba = nprevl->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 / scale * (0.5f + 0.8f * WaveParams.softrad);
|
|
// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiTh);
|
|
rtengine::guidedFilter(guid, ble, ble, blur, epsil, false);
|
|
|
|
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int ir = 0; ir < pH; ir++)
|
|
for (int jr = 0; jr < pW; 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);
|
|
nprevl->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 < pH; x++)
|
|
for (int y = 0; y < pW; y++) {
|
|
nprevl->L[x][y] = LIM((1.f + mL0) * (unshar->L[x][y]) + show * background - mL * indic * nprevl->L[x][y], 0.f, 32768.f);
|
|
nprevl->a[x][y] = (1.f + mC0) * (unshar->a[x][y]) - mC * indic * nprevl->a[x][y];
|
|
nprevl->b[x][y] = (1.f + mC0) * (unshar->b[x][y]) - mC * indic * nprevl->b[x][y];
|
|
}
|
|
|
|
delete unshar;
|
|
unshar = NULL;
|
|
|
|
|
|
/*
|
|
if (WaveParams.softrad > 0.f) {
|
|
array2D<float> ble(pW, pH);
|
|
array2D<float> guid(pW, pH);
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int ir = 0; ir < pH; ir++)
|
|
for (int jr = 0; jr < pW; jr++) {
|
|
ble[ir][jr] = (nprevl->L[ir][jr] - provradius->L[ir][jr]) / 32768.f;
|
|
guid[ir][jr] = provradius->L[ir][jr] / 32768.f;
|
|
}
|
|
double epsilmax = 0.001;
|
|
double epsilmin = 0.0001;
|
|
double aepsil = (epsilmax - epsilmin) / 90.f;
|
|
double bepsil = epsilmax - 100.f * aepsil;
|
|
double epsil = aepsil * WaveParams.softrad + bepsil;
|
|
|
|
float blur = 10.f / scale * (0.001f + 0.8f * WaveParams.softrad);
|
|
// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiTh);
|
|
rtengine::guidedFilter(guid, ble, ble, blur, epsil, false);
|
|
|
|
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int ir = 0; ir < pH; ir++)
|
|
for (int jr = 0; jr < pW; jr++) {
|
|
nprevl->L[ir][jr] = provradius->L[ir][jr] + 32768.f * ble[ir][jr];
|
|
}
|
|
}
|
|
*/
|
|
if (WaveParams.softrad > 0.f) {
|
|
|
|
delete provradius;
|
|
provradius = NULL;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ipf.softLight(nprevl, params->softlight);
|
|
|
|
if (params->icm.workingTRC != ColorManagementParams::WorkingTrc::NONE && params->icm.trcExp) {
|
|
const int GW = nprevl->W;
|
|
const int GH = nprevl->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(nprevl);
|
|
}
|
|
|
|
std::unique_ptr<Imagefloat> tmpImage1(new Imagefloat(GW, GH));
|
|
|
|
ipf.lab2rgb(*nprevl, *tmpImage1, params->icm.workingProfile);
|
|
|
|
const float gamtone = params->icm.workingTRCGamma;
|
|
const float slotone = params->icm.workingTRCSlope;
|
|
int illum = toUnderlying(params->icm.will);
|
|
const int prim = toUnderlying(params->icm.wprim);
|
|
|
|
Glib::ustring prof = params->icm.workingProfile;
|
|
|
|
cmsHTRANSFORM dummy = nullptr;
|
|
int ill = 0;
|
|
bool gamutcontrol = params->icm.gamut;
|
|
int catc = toUnderlying(params->icm.wcat);
|
|
int locprim = 0;
|
|
float rdx, rdy, grx, gry, blx, bly = 0.f;
|
|
float meanx, meany, meanxe, meanye = 0.f;
|
|
|
|
ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, -5, prof, 2.4, 12.92310, 0, ill, 0, 0, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, dummy, true, false, false, false);
|
|
ipf.workingtrc(0, tmpImage1.get(), tmpImage1.get(), GW, GH, 5, prof, gamtone, slotone, catc, illum, prim, locprim, rdx, rdy, grx, gry, blx, bly, meanx, meany, meanxe, meanye, dummy, false, true, true, gamutcontrol);
|
|
const int midton = params->icm.wmidtcie;
|
|
if(midton != 0) {
|
|
ToneEqualizerParams params;
|
|
params.enabled = true;
|
|
params.regularization = 0.f;
|
|
params.pivot = 0.f;
|
|
params.bands[0] = 0;
|
|
params.bands[2] = midton;
|
|
params.bands[4] = 0;
|
|
params.bands[5] = 0;
|
|
int mid = abs(midton);
|
|
int threshmid = 50;
|
|
if(mid > threshmid) {
|
|
params.bands[1] = sign(midton) * (mid - threshmid);
|
|
params.bands[3] = sign(midton) * (mid - threshmid);
|
|
}
|
|
ipf.toneEqualizer(tmpImage1.get(), params, prof, scale, false);
|
|
}
|
|
const bool smoothi = params->icm.wsmoothcie;
|
|
if(smoothi) {
|
|
ToneEqualizerParams params;
|
|
params.enabled = true;
|
|
params.regularization = 0.f;
|
|
params.pivot = 0.f;
|
|
params.bands[0] = 0;
|
|
params.bands[1] = 0;
|
|
params.bands[2] = 0;
|
|
params.bands[3] = 0;
|
|
params.bands[4] = -40;//arbitrary value to adapt with WhiteEvjz - here White Ev # 10
|
|
params.bands[5] = -80;//8 Ev and above
|
|
bool Evsix = true;
|
|
if(Evsix) {//EV = 6 majority of images
|
|
params.bands[4] = -15;
|
|
}
|
|
|
|
ipf.toneEqualizer(tmpImage1.get(), params, prof, scale, false);
|
|
}
|
|
|
|
ipf.rgb2lab(*tmpImage1, *nprevl, params->icm.workingProfile);
|
|
|
|
//nprevl and provis
|
|
if (provis) {
|
|
ipf.preserv(nprevl, 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++) {
|
|
nprevl->a[x][y] = 0.f;
|
|
nprevl->b[x][y] = 0.f;
|
|
}
|
|
}
|
|
|
|
tmpImage1.reset();
|
|
|
|
if (prim == 14) {//pass red gre blue xy in function of area dats Ciexy
|
|
float redgraphx = params->icm.labgridcieALow;
|
|
float redgraphy = params->icm.labgridcieBLow;
|
|
float blugraphx = params->icm.labgridcieAHigh;
|
|
float blugraphy = params->icm.labgridcieBHigh;
|
|
float gregraphx = params->icm.labgridcieGx;
|
|
float gregraphy = params->icm.labgridcieGy;
|
|
float redxx = 0.55f * (redgraphx + 1.f) - 0.1f;
|
|
redxx = rtengine::LIM(redxx, 0.41f, 1.f);
|
|
float redyy = 0.55f * (redgraphy + 1.f) - 0.1f;
|
|
redyy = rtengine::LIM(redyy, 0.f, 0.7f);
|
|
float bluxx = 0.55f * (blugraphx + 1.f) - 0.1f;
|
|
bluxx = rtengine::LIM(bluxx, -0.1f, 0.5f);
|
|
float bluyy = 0.55f * (blugraphy + 1.f) - 0.1f;
|
|
bluyy = rtengine::LIM(bluyy, -0.1f, 0.5f);
|
|
|
|
float grexx = 0.55f * (gregraphx + 1.f) - 0.1f;
|
|
grexx = rtengine::LIM(grexx, -0.1f, 0.4f);
|
|
float greyy = 0.55f * (gregraphy + 1.f) - 0.1f;
|
|
greyy = rtengine::LIM(greyy, 0.5f, 1.f);
|
|
|
|
if (primListener) {
|
|
primListener->primChanged(redxx, redyy, bluxx, bluyy, grexx, greyy);
|
|
}
|
|
} else {//all other cases - pass Cie xy to update graph Ciexy
|
|
float r_x = params->icm.redx;
|
|
float r_y = params->icm.redy;
|
|
float b_x = params->icm.blux;
|
|
float b_y = params->icm.bluy;
|
|
float g_x = params->icm.grex;
|
|
float g_y = params->icm.grey;
|
|
//printf("rx=%f ry=%f \n", (double) r_x, (double) r_y);
|
|
float wx = 0.33f;
|
|
float wy = 0.33f;
|
|
|
|
switch (illum) {
|
|
case 1://D41
|
|
wx = 0.37798f;
|
|
wy = 0.38123f;
|
|
break;
|
|
|
|
case 2://D50
|
|
wx = 0.3457f;
|
|
wy = 0.3585f;
|
|
break;
|
|
|
|
case 3://D55
|
|
wx = 0.3324f;
|
|
wy = 0.3474f;
|
|
break;
|
|
|
|
case 4://D60
|
|
wx = 0.3217f;
|
|
wy = 0.3377f;
|
|
break;
|
|
|
|
case 5://D65
|
|
wx = 0.3127f;
|
|
wy = 0.3290f;
|
|
break;
|
|
|
|
case 6://D80
|
|
wx = 0.2937f;
|
|
wy = 0.3092f;
|
|
break;
|
|
|
|
case 7://D120
|
|
wx = 0.2697f;
|
|
wy = 0.2808f;
|
|
break;
|
|
|
|
case 8://stdA
|
|
wx = 0.4476f;
|
|
wy = 0.4074f;
|
|
break;
|
|
|
|
case 9://2000K
|
|
wx = 0.5266f;
|
|
wy = 0.4133f;
|
|
break;
|
|
|
|
case 10://1500K
|
|
wx = 0.5857f;
|
|
wy = 0.3932f;
|
|
break;
|
|
}
|
|
|
|
|
|
//move white point in GUI
|
|
double refin = params->icm.refi;
|
|
double arefi = (wy - meany) / (wx - meanx);
|
|
double brefi = wy - arefi * wx;
|
|
double scalrefi = meanx - wx;
|
|
wx = wx + scalrefi * refin;
|
|
wy = wx * arefi + brefi;
|
|
|
|
if (primListener) {
|
|
primListener->iprimChanged(r_x, r_y, b_x, b_y, g_x, g_y, wx, wy, meanx, meany);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (params->colorappearance.enabled) {
|
|
// L histo and Chroma histo for ciecam
|
|
// histogram will be for Lab (Lch) values, because very difficult to do with J,Q, M, s, C
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
lhist16CAM.clear();
|
|
lhist16CCAM.clear();
|
|
|
|
if (!params->colorappearance.datacie) {
|
|
for (int x = 0; x < pH; x++)
|
|
for (int y = 0; y < pW; y++) {
|
|
int pos = CLIP((int)(nprevl->L[x][y]));
|
|
int posc = CLIP((int)sqrt(nprevl->a[x][y] * nprevl->a[x][y] + nprevl->b[x][y] * nprevl->b[x][y]));
|
|
lhist16CAM[pos]++;
|
|
lhist16CCAM[posc]++;
|
|
}
|
|
}
|
|
|
|
CurveFactory::curveLightBrightColor(params->colorappearance.curve, params->colorappearance.curve2, params->colorappearance.curve3,
|
|
lhist16CAM, histLCAM, lhist16CCAM, histCCAM,
|
|
customColCurve1, customColCurve2, customColCurve3, 1);
|
|
|
|
const FramesMetaData* metaData = imgsrc->getMetaData();
|
|
float fnum = metaData->getFNumber(); // F number
|
|
float fiso = metaData->getISOSpeed() ; // ISO
|
|
float fspeed = metaData->getShutterSpeed() ; // Speed
|
|
double fcomp = metaData->getExpComp(); // Compensation +/-
|
|
double adap;
|
|
|
|
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.0, E_V - 3.0); // cd / m2
|
|
// end calculation adaptation scene luminosity
|
|
}
|
|
|
|
if (params->colorappearance.catmethod == "symg") { //force abolute luminance scenescene to 400 in symmetric
|
|
adap = 400.;
|
|
}
|
|
|
|
float d, dj, yb;
|
|
bool execsharp = false;
|
|
|
|
if (!ncie) {
|
|
ncie = new CieImage(pW, pH);
|
|
}
|
|
|
|
if (!CAMBrightCurveJ && (params->colorappearance.algo == "JC" || params->colorappearance.algo == "JS" || params->colorappearance.algo == "ALL")) {
|
|
CAMBrightCurveJ(32768, 0);
|
|
}
|
|
|
|
if (!CAMBrightCurveQ && (params->colorappearance.algo == "QM" || params->colorappearance.algo == "ALL")) {
|
|
CAMBrightCurveQ(32768, 0);
|
|
}
|
|
|
|
// Issue 2785, only float version of ciecam02 for navigator and pan background
|
|
CAMMean = NAN;
|
|
CAMBrightCurveJ.dirty = true;
|
|
CAMBrightCurveQ.dirty = true;
|
|
|
|
ipf.ciecam_02float(ncie, float (adap), pW, 2, nprevl, params.get(), customColCurve1, customColCurve2, customColCurve3, histLCAM, histCCAM, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 0, scale, execsharp, d, dj, yb, 1);
|
|
|
|
//call listener
|
|
if ((params->colorappearance.autodegree || params->colorappearance.autodegreeout) && acListener && params->colorappearance.enabled) {
|
|
if (params->colorappearance.catmethod == "symg") { //force chromatic adaptation to 90 in symmetric
|
|
d = 0.9;
|
|
dj = 0.9;
|
|
}
|
|
|
|
acListener->autoCamChanged(100.* (double)d, 100.* (double)dj);
|
|
}
|
|
|
|
if (params->colorappearance.autoadapscen && acListener && params->colorappearance.enabled) {
|
|
acListener->adapCamChanged(adap); //real value of adapt scene, force to 400 in symmetric
|
|
}
|
|
|
|
if (params->colorappearance.autoybscen && acListener && params->colorappearance.enabled) {
|
|
if (params->colorappearance.catmethod == "symg") { //force yb scene to 18 in symmetric
|
|
yb = 18;
|
|
}
|
|
|
|
acListener->ybCamChanged((int) yb); //real value Yb scene
|
|
}
|
|
|
|
double tempsym = 5003.;
|
|
double greensym = 1.;
|
|
int wmodel = 0;//wmodel allows - arbitrary - choice of illuminant and temp with choice
|
|
|
|
if (params->colorappearance.wbmodel == "RawT") {
|
|
wmodel = 0;
|
|
} else if (params->colorappearance.wbmodel == "RawTCAT02") {
|
|
wmodel = 1;
|
|
} else if (params->colorappearance.wbmodel == "free") {
|
|
wmodel = 2;//force white balance in symmetric
|
|
}
|
|
|
|
if (params->colorappearance.catmethod == "symg" && wmodel == 2) {
|
|
tempsym = params->wb.temperature;//force white balance in symmetric
|
|
} else if(params->colorappearance.autotempout) {
|
|
if (params->colorappearance.illum == "iA") {//otherwise force illuminant source
|
|
tempsym = 2856.;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "i41") {
|
|
tempsym = 4100.;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "i50") {
|
|
tempsym = 5003.;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "i55") {
|
|
tempsym = 5503.;
|
|
} else if (params->colorappearance.illum == "i60") {
|
|
tempsym = 6000. ;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "i65") {
|
|
tempsym = 6504.;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "i75") {
|
|
tempsym = 7504.;
|
|
greensym = 1.;
|
|
} else if (params->colorappearance.illum == "ifree") {
|
|
tempsym = params->wb.temperature;//force white balance in symmetric
|
|
greensym = 1.;
|
|
}
|
|
} else {
|
|
tempsym = params->colorappearance.tempout;
|
|
greensym = params->colorappearance.greenout;
|
|
}
|
|
if (params->colorappearance.enabled && acListener) {
|
|
acListener->wbCamChanged(tempsym, greensym, params->colorappearance.autotempout); //real temp and tint.
|
|
}
|
|
|
|
} else {
|
|
// CIECAM is disabled, we free up its image buffer to save some space
|
|
if (ncie) {
|
|
delete ncie;
|
|
}
|
|
|
|
ncie = nullptr;
|
|
|
|
if (CAMBrightCurveJ) {
|
|
CAMBrightCurveJ.reset();
|
|
}
|
|
|
|
if (CAMBrightCurveQ) {
|
|
CAMBrightCurveQ.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
// if (todo & (M_AUTOEXP | M_RGBCURVE)) {
|
|
|
|
// Update the monitor color transform if necessary
|
|
if ((todo & M_MONITOR) || (lastOutputProfile != params->icm.outputProfile) || lastOutputIntent != params->icm.outputIntent || lastOutputBPC != params->icm.outputBPC) {
|
|
lastOutputProfile = params->icm.outputProfile;
|
|
lastOutputIntent = params->icm.outputIntent;
|
|
lastOutputBPC = params->icm.outputBPC;
|
|
ipf.updateColorProfiles(monitorProfile, monitorIntent, softProof, gamutCheck);
|
|
}
|
|
}
|
|
|
|
// process crop, if needed
|
|
for (size_t i = 0; i < crops.size(); i++)
|
|
if (crops[i]->hasListener() && (panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar) || (todo & (M_MONITOR | M_RGBCURVE | M_LUMACURVE)) || crops[i]->get_skip() == 1)) {
|
|
crops[i]->update(todo); // may call ourselves
|
|
}
|
|
|
|
if (panningRelatedChange || (todo & M_MONITOR)) {
|
|
if ((todo != CROP && todo != MINUPDATE) || (todo & M_MONITOR)) {
|
|
MyMutex::MyLock prevImgLock(previmg->getMutex());
|
|
|
|
try {
|
|
// Computing the preview image, i.e. converting from WCS->Monitor color space (soft-proofing disabled) or WCS->Printer profile->Monitor color space (soft-proofing enabled)
|
|
ipf.lab2monitorRgb(nprevl, previmg);
|
|
|
|
// Computing the internal image for analysis, i.e. conversion from WCS->Output profile
|
|
delete workimg;
|
|
workimg = nullptr;
|
|
|
|
workimg = ipf.lab2rgb(nprevl, 0, 0, pW, pH, params->icm);
|
|
} catch (std::exception&) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!resultValid) {
|
|
resultValid = true;
|
|
|
|
if (imageListener) {
|
|
imageListener->setImage(previmg, scale, params->crop);
|
|
}
|
|
}
|
|
|
|
if (imageListener)
|
|
// TODO: The WB tool should be advertised too in order to get the AutoWB's temp and green values
|
|
{
|
|
imageListener->imageReady(params->crop);
|
|
}
|
|
|
|
hist_lrgb_dirty = vectorscope_hc_dirty = vectorscope_hs_dirty = waveform_dirty = true;
|
|
|
|
if (hListener) {
|
|
if (hListener->updateHistogram()) {
|
|
updateLRGBHistograms();
|
|
}
|
|
|
|
if (hListener->updateVectorscopeHC()) {
|
|
updateVectorscopeHC();
|
|
}
|
|
|
|
if (hListener->updateVectorscopeHS()) {
|
|
updateVectorscopeHS();
|
|
}
|
|
|
|
if (hListener->updateWaveform()) {
|
|
updateWaveforms();
|
|
}
|
|
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
if (orig_prev != oprevi) {
|
|
delete oprevi;
|
|
oprevi = nullptr;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::setTweakOperator(TweakOperator *tOperator)
|
|
{
|
|
if (tOperator) {
|
|
tweakOperator = tOperator;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::unsetTweakOperator(TweakOperator *tOperator)
|
|
{
|
|
if (tOperator && tOperator == tweakOperator) {
|
|
tweakOperator = nullptr;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::freeAll()
|
|
{
|
|
|
|
if (allocated) {
|
|
if (spotprev && spotprev != oprevi) {
|
|
delete spotprev;
|
|
}
|
|
|
|
spotprev = nullptr;
|
|
|
|
if (orig_prev != oprevi) {
|
|
delete oprevi;
|
|
}
|
|
|
|
oprevi = nullptr;
|
|
delete orig_prev;
|
|
orig_prev = nullptr;
|
|
delete oprevl;
|
|
oprevl = nullptr;
|
|
delete nprevl;
|
|
nprevl = nullptr;
|
|
|
|
if (ncie) {
|
|
delete ncie;
|
|
}
|
|
|
|
ncie = nullptr;
|
|
|
|
if (imageListener) {
|
|
imageListener->delImage(previmg);
|
|
} else {
|
|
delete previmg;
|
|
}
|
|
|
|
delete workimg;
|
|
workimg = nullptr;
|
|
|
|
}
|
|
|
|
allocated = false;
|
|
}
|
|
|
|
void ImProcCoordinator::allocCache(Imagefloat* &imgfloat)
|
|
{
|
|
if (imgfloat == nullptr) {
|
|
imgfloat = new Imagefloat(pW, pH);
|
|
} else {
|
|
imgfloat->allocate(pW, pH);
|
|
}
|
|
}
|
|
|
|
/** @brief Handles image buffer (re)allocation and trigger sizeChanged of SizeListener[s]
|
|
* If the scale change, 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)
|
|
*
|
|
* @param prevscale New Preview's scale.
|
|
*/
|
|
void ImProcCoordinator::setScale(int prevscale)
|
|
{
|
|
|
|
tr = getCoarseBitMask(params->coarse);
|
|
|
|
int nW, nH;
|
|
imgsrc->getFullSize(fw, fh, tr);
|
|
|
|
prevscale++;
|
|
|
|
do {
|
|
prevscale--;
|
|
PreviewProps pp(0, 0, fw, fh, prevscale);
|
|
imgsrc->getSize(pp, nW, nH);
|
|
} while (nH < 400 && prevscale > 1 && (nW * nH < 1000000)); // actually hardcoded values, perhaps a better choice is possible
|
|
|
|
if (nW != pW || nH != pH) {
|
|
|
|
freeAll();
|
|
|
|
pW = nW;
|
|
pH = nH;
|
|
|
|
orig_prev = new Imagefloat(pW, pH);
|
|
oprevi = orig_prev;
|
|
oprevl = new LabImage(pW, pH);
|
|
nprevl = new LabImage(pW, pH);
|
|
|
|
//ncie is only used in ImProcCoordinator::updatePreviewImage, it will be allocated on first use and deleted if not used anymore
|
|
previmg = new Image8(pW, pH);
|
|
workimg = new Image8(pW, pH);
|
|
|
|
allocated = true;
|
|
}
|
|
|
|
scale = prevscale;
|
|
resultValid = false;
|
|
fullw = fw;
|
|
fullh = fh;
|
|
|
|
if (!sizeListeners.empty())
|
|
for (size_t i = 0; i < sizeListeners.size(); i++) {
|
|
sizeListeners[i]->sizeChanged(fullw, fullh, fw, fh);
|
|
}
|
|
}
|
|
|
|
|
|
void ImProcCoordinator::notifyHistogramChanged()
|
|
{
|
|
if (hListener) {
|
|
hListener->histogramChanged(
|
|
histRed,
|
|
histGreen,
|
|
histBlue,
|
|
histLuma,
|
|
histToneCurve,
|
|
histLCurve,
|
|
histCCurve,
|
|
histLCAM,
|
|
histCCAM,
|
|
histRedRaw,
|
|
histGreenRaw,
|
|
histBlueRaw,
|
|
histChroma,
|
|
histLRETI,
|
|
vectorscopeScale,
|
|
vectorscope_hc,
|
|
vectorscope_hs,
|
|
waveformScale,
|
|
waveformRed,
|
|
waveformGreen,
|
|
waveformBlue,
|
|
waveformLuma
|
|
);
|
|
}
|
|
}
|
|
|
|
bool ImProcCoordinator::updateLRGBHistograms()
|
|
{
|
|
|
|
if (!hist_lrgb_dirty) {
|
|
return false;
|
|
}
|
|
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel sections
|
|
#endif
|
|
{
|
|
#ifdef _OPENMP
|
|
#pragma omp section
|
|
#endif
|
|
{
|
|
histChroma.clear();
|
|
|
|
for (int i = y1; i < y2; i++)
|
|
for (int j = x1; j < x2; j++)
|
|
{
|
|
histChroma[(int)(sqrtf(SQR(nprevl->a[i][j]) + SQR(nprevl->b[i][j])) / 188.f)]++; //188 = 48000/256
|
|
}
|
|
}
|
|
#ifdef _OPENMP
|
|
#pragma omp section
|
|
#endif
|
|
{
|
|
histLuma.clear();
|
|
|
|
for (int i = y1; i < y2; i++)
|
|
for (int j = x1; j < x2; j++)
|
|
{
|
|
histLuma[(int)(nprevl->L[i][j] / 128.f)]++;
|
|
}
|
|
}
|
|
#ifdef _OPENMP
|
|
#pragma omp section
|
|
#endif
|
|
{
|
|
histRed.clear();
|
|
histGreen.clear();
|
|
histBlue.clear();
|
|
|
|
for (int i = y1; i < y2; i++)
|
|
{
|
|
int ofs = (i * pW + x1) * 3;
|
|
|
|
for (int j = x1; j < x2; j++) {
|
|
int r = workimg->data[ofs++];
|
|
int g = workimg->data[ofs++];
|
|
int b = workimg->data[ofs++];
|
|
|
|
histRed[r]++;
|
|
histGreen[g]++;
|
|
histBlue[b]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hist_lrgb_dirty = false;
|
|
return true;
|
|
|
|
}
|
|
|
|
bool ImProcCoordinator::updateVectorscopeHC()
|
|
{
|
|
if (!workimg || !vectorscope_hc_dirty) {
|
|
return false;
|
|
}
|
|
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
|
|
constexpr int size = VECTORSCOPE_SIZE;
|
|
constexpr float norm_factor = size / (128.f * 655.36f);
|
|
vectorscope_hc.fill(0);
|
|
|
|
vectorscopeScale = (x2 - x1) * (y2 - y1);
|
|
|
|
const std::unique_ptr<float[]> a(new float[vectorscopeScale]);
|
|
const std::unique_ptr<float[]> b(new float[vectorscopeScale]);
|
|
const std::unique_ptr<float[]> L(new float[vectorscopeScale]);
|
|
ipf.rgb2lab(*workimg, x1, y1, x2 - x1, y2 - y1, L.get(), a.get(), b.get(), params->icm);
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel
|
|
#endif
|
|
{
|
|
array2D<int> vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
|
|
#ifdef _OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
|
|
for (int i = y1; i < y2; ++i) {
|
|
for (int j = x1, ofs_lab = (i - y1) * (x2 - x1); j < x2; ++j, ++ofs_lab) {
|
|
const int col = norm_factor * a[ofs_lab] + size / 2 + 0.5f;
|
|
const int row = norm_factor * b[ofs_lab] + size / 2 + 0.5f;
|
|
|
|
if (col >= 0 && col < size && row >= 0 && row < size) {
|
|
vectorscopeThr[row][col]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
{
|
|
vectorscope_hc += vectorscopeThr;
|
|
}
|
|
}
|
|
|
|
vectorscope_hc_dirty = false;
|
|
return true;
|
|
}
|
|
|
|
bool ImProcCoordinator::updateVectorscopeHS()
|
|
{
|
|
if (!workimg || !vectorscope_hs_dirty) {
|
|
return false;
|
|
}
|
|
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
|
|
constexpr int size = VECTORSCOPE_SIZE;
|
|
vectorscope_hs.fill(0);
|
|
|
|
vectorscopeScale = (x2 - x1) * (y2 - y1);
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel
|
|
#endif
|
|
{
|
|
array2D<int> vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
|
|
#ifdef _OPENMP
|
|
#pragma omp for nowait
|
|
#endif
|
|
|
|
for (int i = y1; i < y2; ++i) {
|
|
int ofs = (i * pW + x1) * 3;
|
|
|
|
for (int j = x1; j < x2; ++j) {
|
|
const float red = 257.f * workimg->data[ofs++];
|
|
const float green = 257.f * workimg->data[ofs++];
|
|
const float blue = 257.f * workimg->data[ofs++];
|
|
float h, s, l;
|
|
Color::rgb2hslfloat(red, green, blue, h, s, l);
|
|
const auto sincosval = xsincosf(2.f * RT_PI_F * h);
|
|
const int col = s * sincosval.y * (size / 2) + size / 2;
|
|
const int row = s * sincosval.x * (size / 2) + size / 2;
|
|
|
|
if (col >= 0 && col < size && row >= 0 && row < size) {
|
|
vectorscopeThr[row][col]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
{
|
|
vectorscope_hs += vectorscopeThr;
|
|
}
|
|
}
|
|
|
|
vectorscope_hs_dirty = false;
|
|
return true;
|
|
}
|
|
|
|
bool ImProcCoordinator::updateWaveforms()
|
|
{
|
|
if (!workimg) {
|
|
// free memory
|
|
waveformRed.free();
|
|
waveformGreen.free();
|
|
waveformBlue.free();
|
|
waveformLuma.free();
|
|
return true;
|
|
}
|
|
|
|
if (!waveform_dirty) {
|
|
return false;
|
|
}
|
|
|
|
int x1, y1, x2, y2;
|
|
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
|
|
int waveform_width = waveformRed.getWidth();
|
|
|
|
if (waveform_width != x2 - x1) {
|
|
// Resize waveform arrays.
|
|
waveform_width = x2 - x1;
|
|
waveformRed(waveform_width, 256);
|
|
waveformGreen(waveform_width, 256);
|
|
waveformBlue(waveform_width, 256);
|
|
waveformLuma(waveform_width, 256);
|
|
}
|
|
|
|
// Start with zero.
|
|
waveformRed.fill(0);
|
|
waveformGreen.fill(0);
|
|
waveformBlue.fill(0);
|
|
waveformLuma.fill(0);
|
|
|
|
constexpr float luma_factor = 255.f / 32768.f;
|
|
|
|
for (int i = y1; i < y2; i++) {
|
|
int ofs = (i * pW + x1) * 3;
|
|
float* L_row = nprevl->L[i] + x1;
|
|
|
|
for (int j = 0; j < waveform_width; j++) {
|
|
waveformRed[workimg->data[ofs++]][j]++;
|
|
waveformGreen[workimg->data[ofs++]][j]++;
|
|
waveformBlue[workimg->data[ofs++]][j]++;
|
|
waveformLuma[LIM<int>(L_row[j] * luma_factor, 0, 255)][j]++;
|
|
}
|
|
}
|
|
|
|
waveformScale = y2 - y1;
|
|
waveform_dirty = false;
|
|
return true;
|
|
}
|
|
|
|
bool ImProcCoordinator::getAutoWB(double& temp, double& green, double equal, StandardObserver observer, double tempBias)
|
|
{
|
|
|
|
if (imgsrc) {
|
|
if (lastAwbEqual != equal || lastAwbObserver != observer || lastAwbTempBias != tempBias || lastAwbauto != params->wb.method) {
|
|
// Issue 2500 MyMutex::MyLock lock(minit); // Also used in crop window
|
|
double rm, gm, bm;
|
|
params->wb.method = "autold";//same result as before multiple Auto WB
|
|
|
|
// imgsrc->getAutoWBMultipliers(rm, gm, bm);
|
|
double tempitc = 5000.;
|
|
double greenitc = 1.;
|
|
int dread = 0;
|
|
int bia = 0;
|
|
float temp0 = 5000.f;
|
|
float studgood = 1000.f;
|
|
int nocam = 0;
|
|
int kcam = 0;
|
|
float minchrom = 1000.f;
|
|
float delta = 0.f;
|
|
int kmin = 20;
|
|
float minhist = 10000000.f;
|
|
float maxhist = -1000.f;
|
|
double tempref, greenref;
|
|
bool extra = false;
|
|
imgsrc->getAutoWBMultipliersitc(extra, tempref, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params->wb, params->icm, params->raw, params->toneCurve);
|
|
|
|
if (rm != -1) {
|
|
autoWB.update(rm, gm, bm, equal, observer, tempBias);
|
|
lastAwbEqual = equal;
|
|
lastAwbObserver = observer;
|
|
lastAwbTempBias = tempBias;
|
|
lastAwbauto = params->wb.method;
|
|
} else {
|
|
lastAwbEqual = -1.;
|
|
lastAwbObserver = ColorTemp::DEFAULT_OBSERVER;
|
|
autoWB.useDefaults(equal, observer);
|
|
lastAwbauto = "";
|
|
lastAwbTempBias = 0.0;
|
|
}
|
|
}
|
|
|
|
temp = autoWB.getTemp();
|
|
green = autoWB.getGreen();
|
|
return true;
|
|
} else {
|
|
//temp = autoWB.getTemp();
|
|
temp = -1.0;
|
|
green = -1.0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::getCamWB(double& temp, double& green, StandardObserver observer)
|
|
{
|
|
|
|
if (imgsrc) {
|
|
const ColorTemp color_temp = imgsrc->getWB().convertObserver(observer);
|
|
temp = color_temp.getTemp();
|
|
green = color_temp.getGreen();
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::getSpotWB(int x, int y, int rect, double& temp, double& tgreen)
|
|
{
|
|
|
|
ColorTemp ret;
|
|
|
|
{
|
|
MyMutex::MyLock lock(mProcessing);
|
|
std::vector<Coord2D> points, red, green, blue;
|
|
|
|
for (int i = y - rect; i <= y + rect; i++)
|
|
for (int j = x - rect; j <= x + rect; j++) {
|
|
points.push_back(Coord2D(j, i));
|
|
}
|
|
|
|
ipf.transCoord(fw, fh, points, red, green, blue);
|
|
|
|
int tr = getCoarseBitMask(params->coarse);
|
|
|
|
ret = imgsrc->getSpotWB(red, green, blue, tr, params->wb.equal, params->wb.observer);
|
|
currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method, params->wb.observer);
|
|
//double rr,gg,bb;
|
|
//currWB.getMultipliers(rr,gg,bb);
|
|
|
|
} // end of mutex lockong
|
|
|
|
if (ret.getTemp() > 0) {
|
|
temp = ret.getTemp();
|
|
tgreen = ret.getGreen();
|
|
} else {
|
|
temp = currWB.getTemp();
|
|
tgreen = currWB.getGreen();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ImProcCoordinator::getAutoCrop(double ratio, int &x, int &y, int &w, int &h)
|
|
{
|
|
|
|
MyMutex::MyLock lock(mProcessing);
|
|
|
|
LensCorrection *pLCPMap = nullptr;
|
|
|
|
if (params->lensProf.useLcp() && imgsrc->getMetaData()->getFocalLen() > 0) {
|
|
const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile(params->lensProf.lcpFile);
|
|
|
|
if (pLCPProf) pLCPMap = new LCPMapper(pLCPProf, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(),
|
|
0, false, params->lensProf.useDist, fullw, fullh, params->coarse, imgsrc->getRotateDegree());
|
|
}
|
|
|
|
double fillscale = ipf.getTransformAutoFill(fullw, fullh, pLCPMap);
|
|
|
|
if (ratio > 0) {
|
|
w = fullw * fillscale;
|
|
h = w / ratio;
|
|
|
|
if (h > fullh * fillscale) {
|
|
h = fullh * fillscale;
|
|
w = h * ratio;
|
|
}
|
|
} else {
|
|
w = fullw * fillscale;
|
|
h = fullh * fillscale;
|
|
}
|
|
|
|
x = (fullw - w) / 2;
|
|
y = (fullh - h) / 2;
|
|
}
|
|
|
|
void ImProcCoordinator::setMonitorProfile(const Glib::ustring& profile, RenderingIntent intent)
|
|
{
|
|
monitorProfile = profile;
|
|
monitorIntent = intent;
|
|
}
|
|
|
|
void ImProcCoordinator::getMonitorProfile(Glib::ustring& profile, RenderingIntent& intent) const
|
|
{
|
|
profile = monitorProfile;
|
|
intent = monitorIntent;
|
|
}
|
|
|
|
void ImProcCoordinator::setSoftProofing(bool softProof, bool gamutCheck)
|
|
{
|
|
this->softProof = softProof;
|
|
this->gamutCheck = gamutCheck;
|
|
}
|
|
|
|
void ImProcCoordinator::getSoftProofing(bool &softProof, bool &gamutCheck)
|
|
{
|
|
softProof = this->softProof;
|
|
gamutCheck = this->gamutCheck;
|
|
}
|
|
|
|
ProcEvent ImProcCoordinator::setSharpMask(bool sharpMask)
|
|
{
|
|
if (this->sharpMask != sharpMask) {
|
|
sharpMaskChanged = true;
|
|
this->sharpMask = sharpMask;
|
|
return params->pdsharpening.enabled ? rtengine::EvPdShrMaskToggled : rtengine::EvShrEnabled;
|
|
} else {
|
|
sharpMaskChanged = false;
|
|
return rtengine::EvShrEnabled;
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb)
|
|
{
|
|
|
|
MyMutex::MyLock lock(mProcessing);
|
|
|
|
int fW, fH;
|
|
std::unique_ptr<ProcParams> validParams(new ProcParams());
|
|
getParams(validParams.get());
|
|
|
|
int tr = getCoarseBitMask(validParams->coarse);
|
|
|
|
imgsrc->getFullSize(fW, fH, tr);
|
|
PreviewProps pp(0, 0, fW, fH, 1);
|
|
ProcParams ppar = *validParams;
|
|
ppar.toneCurve.hrenabled = false;
|
|
ppar.icm.inputProfile = "(none)";
|
|
Imagefloat* im = new Imagefloat(fW, fH);
|
|
float reddeha = 0.f;
|
|
float greendeha = 0.f;
|
|
float bluedeha = 0.f;
|
|
imgsrc->preprocess(ppar.raw, ppar.lensProf, ppar.coarse,reddeha, greendeha, bluedeha, true);
|
|
double dummy = 0.0;
|
|
imgsrc->demosaic(ppar.raw, false, dummy);
|
|
ColorTemp currWB = ColorTemp(validParams->wb.temperature, validParams->wb.green, validParams->wb.equal, validParams->wb.method, validParams->wb.observer);
|
|
|
|
if (validParams->wb.method == "Camera") {
|
|
currWB = imgsrc->getWB();
|
|
} else if (validParams->wb.method == "autold") {
|
|
if (lastAwbEqual != validParams->wb.equal || lastAwbObserver != validParams->wb.observer || lastAwbTempBias != validParams->wb.tempBias) {
|
|
double rm, gm, bm;
|
|
imgsrc->getAutoWBMultipliers(rm, gm, bm);
|
|
|
|
if (rm != -1.) {
|
|
autoWB.update(rm, gm, bm, validParams->wb.equal, validParams->wb.observer, validParams->wb.tempBias);
|
|
lastAwbEqual = validParams->wb.equal;
|
|
lastAwbObserver = validParams->wb.observer;
|
|
lastAwbTempBias = validParams->wb.tempBias;
|
|
} else {
|
|
lastAwbEqual = -1.;
|
|
lastAwbObserver = ColorTemp::DEFAULT_OBSERVER;
|
|
lastAwbTempBias = 0.0;
|
|
autoWB.useDefaults(validParams->wb.equal, validParams->wb.observer);
|
|
}
|
|
}
|
|
|
|
currWB = autoWB;
|
|
}
|
|
|
|
if (!apply_wb) {
|
|
currWB = ColorTemp(); // = no white balance
|
|
}
|
|
|
|
imgsrc->getImage(currWB, tr, im, pp, ppar.toneCurve, ppar.raw);
|
|
ImProcFunctions ipf(&ppar, true);
|
|
|
|
if (ipf.needsTransform(fW, fH, imgsrc->getRotateDegree(), imgsrc->getMetaData())) {
|
|
Imagefloat* trImg = new Imagefloat(fW, fH);
|
|
ipf.transform(im, trImg, 0, 0, 0, 0, fW, fH, fW, fH,
|
|
imgsrc->getMetaData(), imgsrc->getRotateDegree(), true);
|
|
delete im;
|
|
im = trImg;
|
|
}
|
|
|
|
if (validParams->crop.enabled) {
|
|
Imagefloat *tmpim = new Imagefloat(validParams->crop.w, validParams->crop.h);
|
|
int cx = validParams->crop.x;
|
|
int cy = validParams->crop.y;
|
|
int cw = validParams->crop.w;
|
|
int ch = validParams->crop.h;
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int i = cy; i < cy + ch; i++) {
|
|
for (int j = cx; j < cx + cw; j++) {
|
|
tmpim->r(i - cy, j - cx) = im->r(i, j);
|
|
tmpim->g(i - cy, j - cx) = im->g(i, j);
|
|
tmpim->b(i - cy, j - cx) = im->b(i, j);
|
|
}
|
|
}
|
|
|
|
delete im;
|
|
im = tmpim;
|
|
}
|
|
|
|
// image may contain out of range samples, clip them to avoid wrap-arounds
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
|
|
for (int i = 0; i < im->getHeight(); i++) {
|
|
for (int j = 0; j < im->getWidth(); j++) {
|
|
im->r(i, j) = CLIP(im->r(i, j));
|
|
im->g(i, j) = CLIP(im->g(i, j));
|
|
im->b(i, j) = CLIP(im->b(i, j));
|
|
}
|
|
}
|
|
|
|
int imw, imh;
|
|
double tmpScale = ipf.resizeScale(validParams.get(), fW, fH, imw, imh);
|
|
|
|
if (tmpScale != 1.0) {
|
|
Imagefloat* tempImage = new Imagefloat(imw, imh);
|
|
ipf.resize(im, tempImage, tmpScale);
|
|
delete im;
|
|
im = tempImage;
|
|
}
|
|
|
|
im->setMetadata(Exiv2Metadata(imgsrc->getFileName(), false));
|
|
|
|
im->saveTIFF(fname, 16, false, true);
|
|
delete im;
|
|
|
|
if (plistener) {
|
|
plistener->setProgressState(false);
|
|
}
|
|
|
|
//im->saveJPEG (fname, 85);
|
|
}
|
|
|
|
void ImProcCoordinator::stopProcessing()
|
|
{
|
|
|
|
updaterThreadStart.lock();
|
|
|
|
if (updaterRunning && thread) {
|
|
changeSinceLast = 0;
|
|
thread->join();
|
|
}
|
|
|
|
updaterThreadStart.unlock();
|
|
}
|
|
|
|
void ImProcCoordinator::startProcessing()
|
|
{
|
|
|
|
#undef THREAD_PRIORITY_NORMAL
|
|
|
|
if (!destroying) {
|
|
if (!updaterRunning) {
|
|
updaterThreadStart.lock();
|
|
thread = nullptr;
|
|
updaterRunning = true;
|
|
updaterThreadStart.unlock();
|
|
|
|
//batchThread->yield(); //the running batch should wait other threads to avoid conflict
|
|
|
|
thread = Glib::Thread::create(sigc::mem_fun(*this, &ImProcCoordinator::process), 0, true, true, Glib::THREAD_PRIORITY_NORMAL);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::startProcessing(int changeCode)
|
|
{
|
|
paramsUpdateMutex.lock();
|
|
changeSinceLast |= changeCode;
|
|
paramsUpdateMutex.unlock();
|
|
|
|
startProcessing();
|
|
}
|
|
|
|
void ImProcCoordinator::process()
|
|
{
|
|
if (plistener) {
|
|
plistener->setProgressState(true);
|
|
}
|
|
|
|
paramsUpdateMutex.lock();
|
|
|
|
while (changeSinceLast) {
|
|
const bool panningRelatedChange =
|
|
params->toneCurve.isPanningRelatedChange(nextParams->toneCurve)
|
|
|| params->labCurve != nextParams->labCurve
|
|
|| params->locallab != nextParams->locallab
|
|
|| params->localContrast != nextParams->localContrast
|
|
|| params->rgbCurves != nextParams->rgbCurves
|
|
|| params->colorToning != nextParams->colorToning
|
|
|| params->vibrance != nextParams->vibrance
|
|
// || params->wb != nextParams->wb //isPanningRelatedChange(nextParams->wb)
|
|
|| params->wb.isPanningRelatedChange(nextParams->wb)
|
|
|| params->colorappearance != nextParams->colorappearance
|
|
|| params->epd != nextParams->epd
|
|
|| params->fattal != nextParams->fattal
|
|
|| params->sh != nextParams->sh
|
|
|| params->toneEqualizer != nextParams->toneEqualizer
|
|
|| params->crop != nextParams->crop
|
|
|| params->coarse != nextParams->coarse
|
|
|| params->commonTrans != nextParams->commonTrans
|
|
|| params->rotate != nextParams->rotate
|
|
|| params->distortion != nextParams->distortion
|
|
|| params->lensProf != nextParams->lensProf
|
|
|| params->perspective != nextParams->perspective
|
|
|| params->gradient != nextParams->gradient
|
|
|| params->pcvignette != nextParams->pcvignette
|
|
|| params->cacorrection != nextParams->cacorrection
|
|
|| params->vignetting != nextParams->vignetting
|
|
|| params->chmixer != nextParams->chmixer
|
|
|| params->blackwhite != nextParams->blackwhite
|
|
|| params->icm != nextParams->icm
|
|
|| params->hsvequalizer != nextParams->hsvequalizer
|
|
|| params->filmSimulation != nextParams->filmSimulation
|
|
|| params->softlight != nextParams->softlight
|
|
|| params->raw != nextParams->raw
|
|
|| params->retinex != nextParams->retinex
|
|
|| params->wavelet != nextParams->wavelet
|
|
|| params->dirpyrequalizer != nextParams->dirpyrequalizer
|
|
|| params->dehaze != nextParams->dehaze
|
|
|| params->pdsharpening != nextParams->pdsharpening
|
|
|| params->filmNegative != nextParams->filmNegative
|
|
|| params->spot.enabled != nextParams->spot.enabled
|
|
|| sharpMaskChanged;
|
|
|
|
sharpMaskChanged = false;
|
|
*params = *nextParams;
|
|
int change = changeSinceLast;
|
|
changeSinceLast = 0;
|
|
|
|
if (tweakOperator) {
|
|
// TWEAKING THE PROCPARAMS FOR THE SPOT ADJUSTMENT MODE
|
|
backupParams();
|
|
tweakOperator->tweakParams(*params);
|
|
} else if (paramsBackup) {
|
|
paramsBackup.release();
|
|
}
|
|
|
|
paramsUpdateMutex.unlock();
|
|
|
|
// M_VOID means no update, and is a bit higher that the rest
|
|
if (change & (~M_VOID)) {
|
|
updatePreviewImage(change, panningRelatedChange);
|
|
}
|
|
|
|
paramsUpdateMutex.lock();
|
|
}
|
|
|
|
paramsUpdateMutex.unlock();
|
|
updaterRunning = false;
|
|
|
|
if (plistener) {
|
|
plistener->setProgressState(false);
|
|
}
|
|
}
|
|
|
|
ProcParams* ImProcCoordinator::beginUpdateParams()
|
|
{
|
|
paramsUpdateMutex.lock();
|
|
|
|
return nextParams.get();
|
|
}
|
|
|
|
void ImProcCoordinator::endUpdateParams(ProcEvent change)
|
|
{
|
|
int action = RefreshMapper::getInstance()->getAction(change);
|
|
endUpdateParams(action);
|
|
}
|
|
|
|
void ImProcCoordinator::endUpdateParams(int changeFlags)
|
|
{
|
|
changeSinceLast |= changeFlags;
|
|
|
|
paramsUpdateMutex.unlock();
|
|
startProcessing();
|
|
}
|
|
|
|
bool ImProcCoordinator::getHighQualComputed()
|
|
{
|
|
// this function may only be called from detail windows
|
|
if (!highQualityComputed) {
|
|
if (options.prevdemo == PD_Sidecar) {
|
|
// we already have high quality preview
|
|
setHighQualComputed();
|
|
} else {
|
|
for (size_t i = 0; i < crops.size() - 1; ++i) { // -1, because last entry is the freshly created detail window
|
|
if (crops[i]->get_skip() == 1) { // there is at least one crop with skip == 1 => we already have high quality preview
|
|
setHighQualComputed();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return highQualityComputed;
|
|
}
|
|
|
|
void ImProcCoordinator::setHighQualComputed()
|
|
{
|
|
highQualityComputed = true;
|
|
}
|
|
|
|
void ImProcCoordinator::requestUpdateWaveform()
|
|
{
|
|
if (!hListener) {
|
|
return;
|
|
}
|
|
|
|
bool updated = updateWaveforms();
|
|
|
|
if (updated) {
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::requestUpdateHistogram()
|
|
{
|
|
if (!hListener) {
|
|
return;
|
|
}
|
|
|
|
bool updated = updateLRGBHistograms();
|
|
|
|
if (updated) {
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::requestUpdateHistogramRaw()
|
|
{
|
|
if (!hListener) {
|
|
return;
|
|
}
|
|
|
|
// Don't need to actually update histogram because it is always
|
|
// up-to-date.
|
|
if (hist_raw_dirty) {
|
|
hist_raw_dirty = false;
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::requestUpdateVectorscopeHC()
|
|
{
|
|
if (!hListener) {
|
|
return;
|
|
}
|
|
|
|
bool updated = updateVectorscopeHC();
|
|
|
|
if (updated) {
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
void ImProcCoordinator::requestUpdateVectorscopeHS()
|
|
{
|
|
if (!hListener) {
|
|
return;
|
|
}
|
|
|
|
bool updated = updateVectorscopeHS();
|
|
|
|
if (updated) {
|
|
notifyHistogramChanged();
|
|
}
|
|
}
|
|
|
|
}
|