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

* Levels TRC

* Complete with gamma based attenuation

* Limit RGB channel Slope with checkbox

* Improve GUI and code channel TRC

* Change default values - compexity levels RGB channels

* Relative gamma mode RGB channel TRC

* Change label and ponderation rolloff

* Change rolloff level

* Threshold attenuation

* Threshold attenuation 2 part

* GUI Link R G B

* Linked RGB with Green slope - RGB channels

* Set Freeman TM functions with ImProcFunctions

* First GUI Abstract profile highlight attenuation

* GUI AP part 2

* Restore olg GUI AP

* Expander AP primaries adn illuminant

* Disable RGB channel TRC

* Expander contrast AP

* Slider attenuation response

* Save work GUI local contrast

* Save GUI part 2 AP curve

* Local contrast GUI Abstract Profile

* Move Abstract profile in toolpanel and ICMpanel

* rtengine variable contrast

* Variable contrast 2

* Variable contrast engine 3

* Variable contrast engine 4

* Variable contrast engine

* Detail levels pyramid

* Engine residual contrast

* Residual contrast

* Change settings detail levels

* Expander refinement - new tooltips - low resid contrast

* Change contrast profile and labels

* Remove warning message GUI Gtk

* Gamutcontrol code - disabled

* Improve with calceffect

* Other improvement variable contrast

* Offset variable contrast

* Range offset - comment code

* Parametric inva fot lut

* Clean cmakelist.txt

* Change contrast profiles

* Comment code ipwavelet

* Added orthogonal Daubechies scaling D20

* Change strenght curve - tooltip Daubechies

* Forgotten changes

* Comment code

* Move variable in process - take into account highlight attenuation

* Display label maximum preview and preset selection

* Remove console message

* harmonize levels wavelets iplocallab

* Tooltips contrast enhancement

* Change tooltip Contrast profile

* Chnage tooltip Contrast

* Message warning preview size

* Change gamma TRC values in GUI

* Remove itanium architecture support for windows as PR 7105

* Change windows.yml and appimage.yml

* Windows.yml apseimprov

* Clean and comment ipwavelet

* Clean comment icmpanel.cc

* Harmonize local contrast wavelet Selective editing with Abstract profile

* Harmonize with AP - offset

* vanishing moment D20 - Selective editing wavelet

* Offset only in advanced mode

* GUI expander contrast enable and pyrwavtrc

* Clean and comment code

* merge with dev

* Prepare sigmoid based

* Contrast sigmoid GUI

* Skew sigmoid GUI

* Sigmoid tone mapper in iplocallab

* Change GUI settings

* White-point and black-point auto

* Change EvDCP to ALLNORAW as others events DCP

* Change default skew

* Change settings - enable scale Yb

* Display white point - advanced mode

* Improve GUI

* Clean unused variable

* new sigmoid Q in cam16

* Change tooltips and default sigmoid Q settings

* Sigmoid Jz

* Clean code Jz and sigmoid

* Harmonize Sigmoid Q and Sigmoid RGB

* Harmonize Sigmoid Jz

* Clean code

* Improve labels wit cd/m2

* Slope base Q methode first

* GUI slope based Q

* Change default settings and tooltips

* Change tooltips

* Clean code - change default setting

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

* Fixed bad assignation slopesmoq

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

* Remove wrong change

* various small improvments

* Allows black and white AP and SDA in basic mode

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

* Clean code wgamma wslope

* Set curve Cam16 in basic mode

* Change position curve in GUI cam16

* Enable tonecurve1 in colorappearance & lighting in standard mode

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

* not reset curve shape ciecam in strandard

* Change label Tone mapping operators and tooltips

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

* Forgotten changes

* Clean locallabtools2.cc

* Maxlevel wavelet minimum to 5

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

* Show modified areas SE wavelet

* Tooltip show wavelets decomposition

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

* Change behavior fullimage - global as in PR GHS

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

* Set expander expanded = true

* Chane contrast enhancement coef

* Replace VBox trcWavVBox by ToolParamBlock trcWavFBox

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

* Change RGB Slope behavior with link

* No access to last level contrast enhancement

* Move Abstract Profile tooltip to title

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

* Improve Color Management expanders behavior

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

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

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

* Fix RGB slope tone mapping RGB linkage

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

* Fix SE CAM tone mapping slider defocus

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

* Delete history kslopesmo - remove IcmOpacityCurveWL

* change the tooltips as suggested by Lawrence

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

* Change name Tone map freeman functions

* Remove gamutcont - rename localcont - change allocation memory wdspot

* Clean procparams

* remove sigmoidsenscie - logcieq

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

* Forgotten correction suggested by Lawrence

* Compatibility 5.11 log encoding - sigmoid part 1

* Compatibility 5.11 part 2

* Compatibility 5.11 - step 3

* Compatibility 5.11 - step 4

* Compatibility 5.11 step xx

* Compatibility 5.11 - combobox operators Q and J

* Compatibility 5.11 Cam16 GUI first part

* Improve GUI Cam16 sigmoid compatibility

* Compatibility 5.11 Jz - sigmoid - step 1

* Compatibility 5.11 Jz gui step 2

* Compatibility 5.11 Jz GUI step x

* Compatibility 5.11 Jz - history - etc.

* Various change labels - history ...

* Improve GUI - hide show 5.11 5.12

* Jz 5.11 in iplocallab - step 1

* Compatibility 5.11 iplocallab cam16 step 1

* Improve GUI hide show 511 512

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

* Compatibility 5.11 iplocallab Cam16 step 2

* Improve GUI compatibility 5.11 labels tooltips

* Small improvments GUI - labels - history...

* Fixed typo in paramsedited.cc clcurve issue 7283

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

* Clean and refine code

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

* Change modeQJ method for 5.11 in function ppversion

* Change labels as suggested by Wayne PR 7111

* Others changes suggested for label

* Change tooltips as suggested in PR

* Use unique pointer instead of manual management

* Update rtdata/languages/default

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

* Change all Cam16 references to CAM16

* Change convention uppercase and lowercase in frame - checkbox

* Improve tooltips for Tone Mapping Operators

* Another change CIECAM and uppercase lowercase in checkbox

* Remove appimage and windows yml

---------

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

5211 lines
208 KiB
C++

////////////////////////////////////////////////////////////////
//
//
//
//
// code dated: 9 , 2019
//
// Ipwaveletcc 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
// * 2014 - 2019 2020 2024 - Jacques Desmis <jdesmis@gmail.com>
// * 2014 Ingo Weyrich <heckflosse@i-weyrich.de>
//
////////////////////////////////////////////////////////////////
#include <cassert>
#include <cmath>
#include "array2D.h"
#include "color.h"
#include "curves.h"
#include "EdgePreservingDecomposition.h"
#include "iccstore.h"
#include "improcfun.h"
#include "imagefloat.h"
#include "labimage.h"
#include "gauss.h"
#include "boxblur.h"
#include "LUT.h"
#include "median.h"
#include "opthelper.h"
#include "procparams.h"
#include "rt_math.h"
#include "rtengine.h"
#include "sleef.h"
#include "rtgui/options.h"
#include "guidedfilter.h"
#ifdef _OPENMP
#include <omp.h>
#endif
#include "cplx_wavelet_dec.h"
#define BENCHMARK
#include "StopWatch.h"
namespace rtengine
{
struct cont_params {
float mul[10];
float sigm;
int chrom;
int chro;
float chrwav;
int contrast;
float th;
float thH;
float conres;
float conresH;
float blurres;
float blurcres;
float bluwav;
float radius;
float chrores;
bool oldsh;
float hueres;
float sky;
float b_l, t_l, b_r, t_r;
float b_ly, t_ly, b_ry, t_ry;
float b_lsl, t_lsl, b_rsl, t_rsl;
float b_lhl, t_lhl, b_rhl, t_rhl;
float edg_low, edg_mean, edg_sd, edg_max;
float lev0s, lev0n, lev1s, lev1n, lev2s, lev2n, lev3s, lev3n, lev4n, lev4t;
float b_lpast, t_lpast, b_rpast, t_rpast;
float b_lsat, t_lsat, b_rsat, t_rsat;
int rad;
float eff;
int val;
int til;
int numlevH, numlevS;
float mulC[9];
float mulopaRG[9];
float mulopaBY[9];
bool curv;
bool opaBY;
bool opaRG;
bool edgcurv;
bool diagcurv;
bool denoicurv;
bool denoicurvh;
int CHmet;
int CHSLmet;
int EDmet;
bool HSmet;
bool avoi;
float strength;
int reinforce;
bool detectedge;
int backm;
float eddet;
float eddetthr;
float eddetthrHi;
bool link;
bool lip3;
bool tonemap;
bool diag;
float tmstrength;
float balan;
float sigmafin;
float sigmaton;
float sigmacol;
float sigmadir;
int denmet;
int mixmet;
int quamet;
int slimet;
int ite;
int contmet;
bool opaW;
int BAmet;
bool bam;
float blhigh;
float grhigh;
float blmed;
float grmed;
float bllow;
float grlow;
bool cbena;
bool contena;
bool chromena;
bool edgeena;
bool resena;
bool finena;
bool toningena;
bool noiseena;
bool blena;
int maxilev;
float edgsens;
float edgampl;
int neigh;
bool lipp;
float ballum;
float balchrom;
float chromfi;
float chromco;
float factor;
float scaling;
float scaledirect;
float a_scale;
float a_base;
float b_scale;
float b_base;
float a_high;
float a_low;
float b_high;
float b_low;
float rangeab;
float protab;
float sigmm;
float sigmm14;
float sigmm56;
float levden;
float thrden;
float limden;
int complex;
};
int wavNestedLevels = 1;
std::unique_ptr<LUTf> ImProcFunctions::buildMeaLut(const float inVals[11], const float mea[10], float& lutFactor)
{
constexpr int lutSize = 100;
const float lutMax = std::ceil(mea[9]);
const float lutDiff = lutMax / lutSize;
std::vector<float> lutVals(lutSize);
int jStart = 1;
for (int i = 0; i < lutSize; ++i) {
const float val = i * lutDiff;
if (val < mea[0]) {
// still < first value => no interpolation
lutVals[i] = inVals[0];
} else {
for (int j = jStart; j < 10; ++j) {
if (val == mea[j]) {
// exact match => no interpolation
lutVals[i] = inVals[j];
++jStart;
break;
}
if (val < mea[j]) {
// interpolate
const float dist = (val - mea[j - 1]) / (mea[j] - mea[j - 1]);
lutVals[i] = rtengine::intp(dist, inVals[j], inVals[j - 1]);
break;
}
lutVals[i] = inVals[10];
}
}
}
lutFactor = lutDiff == 0.f ? 0.f : 1.f / lutDiff;
return std::unique_ptr<LUTf>(new LUTf(lutVals));
}
void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const procparams::WaveletParams & waparams, const WavCurve & wavCLVCcurve, const WavCurve & wavdenoise, WavCurve & wavdenoiseh, const Wavblcurve & wavblcurve, const WavOpacityCurveRG & waOpacityCurveRG, const WavOpacityCurveSH & waOpacityCurveSH, const WavOpacityCurveBY & waOpacityCurveBY, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveWL & waOpacityCurveWL, const LUTf &wavclCurve, int skip)
{
TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile);
const double wip[3][3] = {
{wiprof[0][0], wiprof[0][1], wiprof[0][2]},
{wiprof[1][0], wiprof[1][1], wiprof[1][2]},
{wiprof[2][0], wiprof[2][1], wiprof[2][2]}
};
const int imheight = lab->H, imwidth = lab->W;
int levwavL = 0;
//Flat curve for H=f(H) in final touchup for guidedfilter
FlatCurve* wavguidCurve = new FlatCurve(params->wavelet.wavguidcurve); //curve H=f(H)
bool wavguidutili = false;
if (!wavguidCurve || wavguidCurve->isIdentity()) {
if (wavguidCurve) {
delete wavguidCurve;
wavguidCurve = nullptr;
}
} else {
wavguidutili = true;
}
//flat curve for equalizer H
FlatCurve* wavhueCurve = new FlatCurve(params->wavelet.wavhuecurve); //curve H=f(H)
bool wavhueutili = false;
if (!wavhueCurve || wavhueCurve->isIdentity()) {
if (wavhueCurve) {
delete wavhueCurve;
wavhueCurve = nullptr;
}
} else {
wavhueutili = true;
}
struct cont_params cp;
cp.avoi = params->wavelet.avoid;
if (params->wavelet.complexmethod == "normal") {
cp.complex = 0;
} else if (params->wavelet.complexmethod == "expert") {
cp.complex = 1;
} else {
cp.complex = 0;
}
if (params->wavelet.Medgreinf == "more") {
cp.reinforce = 1;
} else if (params->wavelet.Medgreinf == "none") {
cp.reinforce = 2;
} else if (params->wavelet.Medgreinf == "less") {
cp.reinforce = 3;
}
if (params->wavelet.NPmethod == "none") {
cp.lip3 = false;
} else if (params->wavelet.NPmethod == "low") {
cp.lip3 = true;
cp.neigh = 0;
} else if (params->wavelet.NPmethod == "high") {
cp.lip3 = true;
cp.neigh = 1;
}
cp.lipp = params->wavelet.lipst;
cp.diag = params->wavelet.tmr;
cp.balan = (float)params->wavelet.balance;
cp.ite = params->wavelet.iter;
cp.tonemap = params->wavelet.tmrs != 0;
cp.bam = false;
cp.sigmafin = params->wavelet.sigmafin;
cp.sigmaton = params->wavelet.sigmaton;
cp.sigmacol = params->wavelet.sigmacol;
cp.sigmadir = params->wavelet.sigmadir;
cp.sigmm = params->wavelet.sigm;
cp.levden = params->wavelet.levden;
cp.thrden = 0.01f * params->wavelet.thrden;
cp.limden = params->wavelet.limden;
if (params->wavelet.TMmethod == "cont") {
cp.contmet = 1;
} else if (params->wavelet.TMmethod == "tm") {
cp.contmet = 2;
}
cp.denmet = 4;
//if (params->wavelet.denmethod == "equ") {
// cp.denmet = 0;
//} else if (params->wavelet.denmethod == "high") {
// cp.denmet = 1;
//} else if (params->wavelet.denmethod == "low") {
// cp.denmet = 2;
//} else if (params->wavelet.denmethod == "12high") {
// cp.denmet = 3;
//} else if (params->wavelet.denmethod == "12low") {
// cp.denmet = 4;
//}
if (params->wavelet.mixmethod == "nois") {
cp.mixmet = 0;
} else if (params->wavelet.mixmethod == "mix") {
cp.mixmet = 1;
} else if (params->wavelet.mixmethod == "mix7") {
cp.mixmet = 2;
} else if (params->wavelet.mixmethod == "den") {
cp.mixmet = 3;
}
if (params->wavelet.quamethod == "cons") {
cp.quamet = 0;
} else if (params->wavelet.quamethod == "agre") {
cp.quamet = 1;
}
if (params->wavelet.slimethod == "sli") {
cp.slimet = 0;
} else if (params->wavelet.slimethod == "cur") {
cp.slimet = 1;
}
if (params->wavelet.BAmethod != "none") {
cp.bam = true;
if (params->wavelet.BAmethod == "sli") {
cp.BAmet = 1;
} else if (params->wavelet.BAmethod == "cur") {
cp.BAmet = 2;
}
}
int minwinnoise = rtengine::min(imwidth, imheight);
cp.sigm = params->wavelet.sigma;
cp.tmstrength = params->wavelet.tmrs;
cp.contena = params->wavelet.expcontrast;
cp.chromena = params->wavelet.expchroma;
cp.edgeena = params->wavelet.expedge;
cp.resena = params->wavelet.expresid;
cp.finena = params->wavelet.expfinal;
cp.toningena = params->wavelet.exptoning;
cp.noiseena = params->wavelet.expnoise && minwinnoise > 128;//128 limit for 6 levels wavelet denoise issue 7146
cp.blena = params->wavelet.expbl;
cp.chrwav = 0.01f * params->wavelet.chrwav;
if (params->wavelet.Backmethod == "black") {
cp.backm = 0;
} else if (params->wavelet.Backmethod == "grey") {
cp.backm = 1;
} else if (params->wavelet.Backmethod == "resid") {
cp.backm = 2;
}
cp.link = params->wavelet.linkedg;
cp.eddet = (float) params->wavelet.edgedetect;
cp.eddetthr = (float) params->wavelet.edgedetectthr;
cp.eddetthrHi = (float) params->wavelet.edgedetectthr2;
cp.edgsens = 60.f;
cp.edgampl = 10.f;
if (cp.lipp) {
cp.edgsens = (float) params->wavelet.edgesensi;
cp.edgampl = (float) params->wavelet.edgeampli;
}
const int maxmul = params->wavelet.thres;
cp.maxilev = maxmul;
static const float scales[10] = {1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, 512.f};
float scaleskip[10];
for (int sc = 0; sc < 10; sc++) {
scaleskip[sc] = scales[sc] / skip;
}
constexpr float atten0 = 0.40f;
constexpr float atten123 = 0.90f;
//int DaubLen = settings->daubech ? 8 : 6;
int DaubLen = 6;
if (params->wavelet.daubcoeffmethod == "2_") {
DaubLen = 4;
} else if (params->wavelet.daubcoeffmethod == "4_") {
DaubLen = 6;
} else if (params->wavelet.daubcoeffmethod == "6_") {
DaubLen = 8;
} else if (params->wavelet.daubcoeffmethod == "10_") {
DaubLen = 12;
} else if (params->wavelet.daubcoeffmethod == "14_") {
DaubLen = 16;
} else if (params->wavelet.daubcoeffmethod == "20_"){
DaubLen = 22;
}
cp.CHSLmet = 1;
cp.EDmet = 2;
/*
if (params->wavelet.EDmethod == "SL") {
cp.EDmet = 1;
} else if (params->wavelet.EDmethod == "CU") {
cp.EDmet = 2;
}
*/
cp.cbena = params->wavelet.cbenab;
cp.blhigh = (float)params->wavelet.bluehigh;
cp.grhigh = (float)params->wavelet.greenhigh;
cp.blmed = (float)params->wavelet.bluemed;
cp.grmed = (float)params->wavelet.greenmed;
cp.bllow = (float)params->wavelet.bluelow;
cp.grlow = (float)params->wavelet.greenlow;
cp.curv = false;
cp.edgcurv = false;
cp.diagcurv = false;
cp.denoicurv = false;
cp.denoicurvh = false;
cp.opaRG = false;
cp.opaBY = false;
cp.opaW = false;
cp.CHmet = 0;
cp.HSmet = false;
if (params->wavelet.CHmethod == "without") {
cp.CHmet = 0;
} else if (params->wavelet.CHmethod == "with") {
cp.CHmet = 1;
} else if (params->wavelet.CHmethod == "link") {
cp.CHmet = 2;
}
if (params->wavelet.HSmethod == "with") {
cp.HSmet = true;
}
cp.strength = rtengine::min(1.f, rtengine::max(0.f, ((float)params->wavelet.strength / 100.f)));
for (int m = 0; m < maxmul; m++) {
cp.mulC[m] = waparams.ch[m];
}
for (int m = maxmul; m < 9; m++) {
cp.mulC[m] = 0.f;
}
//printf("maxmul=%i\n", maxmul);
cp.factor = WaveletParams::LABGRID_CORR_MAX * 3.276f;
cp.scaling = WaveletParams::LABGRID_CORR_SCALE;
cp.scaledirect = WaveletParams::LABGRIDL_DIRECT_SCALE;
cp.a_scale = (params->wavelet.labgridAHigh - params->wavelet.labgridALow) / cp.factor / cp.scaling;
cp.a_base = params->wavelet.labgridALow / cp.scaling;
cp.b_scale = (params->wavelet.labgridBHigh - params->wavelet.labgridBLow) / cp.factor / cp.scaling;
cp.b_base = params->wavelet.labgridBLow / cp.scaling;
cp.a_high = 3.276f * params->wavelet.labgridAHigh;
cp.a_low = 3.276f * params->wavelet.labgridALow;
cp.b_high = 3.276f * params->wavelet.labgridBHigh;
cp.b_low = 3.276f * params->wavelet.labgridBLow;
cp.rangeab = params->wavelet.rangeab;
cp.protab = params->wavelet.protab;
if (waOpacityCurveRG) {
cp.opaRG = true;
}
if (cp.opaRG) {
cp.mulopaRG[0] = 200.f * (waOpacityCurveRG[0] - 0.5f);
cp.mulopaRG[1] = 200.f * (waOpacityCurveRG[62] - 0.5f);
cp.mulopaRG[2] = 200.f * (waOpacityCurveRG[125] - 0.5f);
cp.mulopaRG[3] = 200.f * (waOpacityCurveRG[187] - 0.5f);
cp.mulopaRG[4] = 200.f * (waOpacityCurveRG[250] - 0.5f);
cp.mulopaRG[5] = 200.f * (waOpacityCurveRG[312] - 0.5f);
cp.mulopaRG[6] = 200.f * (waOpacityCurveRG[375] - 0.5f);
cp.mulopaRG[7] = 200.f * (waOpacityCurveRG[438] - 0.5f);
cp.mulopaRG[8] = 200.f * (waOpacityCurveRG[500] - 0.5f);
} else {
for (int level = 0; level < 9; level++) {
cp.mulopaRG[level] = 0.f;
}
}
if (waOpacityCurveBY) {
cp.opaBY = true;
}
if (cp.opaBY) {
cp.mulopaBY[0] = 200.f * (waOpacityCurveBY[0] - 0.5f);
cp.mulopaBY[1] = 200.f * (waOpacityCurveBY[62] - 0.5f);
cp.mulopaBY[2] = 200.f * (waOpacityCurveBY[125] - 0.5f);
cp.mulopaBY[3] = 200.f * (waOpacityCurveBY[187] - 0.5f);
cp.mulopaBY[4] = 200.f * (waOpacityCurveBY[250] - 0.5f);
cp.mulopaBY[5] = 200.f * (waOpacityCurveBY[312] - 0.5f);
cp.mulopaBY[6] = 200.f * (waOpacityCurveBY[375] - 0.5f);
cp.mulopaBY[7] = 200.f * (waOpacityCurveBY[438] - 0.5f);
cp.mulopaBY[8] = 200.f * (waOpacityCurveBY[500] - 0.5f);
} else {
for (int level = 0; level < 9; level++) {
cp.mulopaBY[level] = 0.f;
}
}
if (wavCLVCcurve) {
cp.edgcurv = true;
}
if (waOpacityCurveWL) {
cp.diagcurv = true;
}
if (wavdenoise) {
for (int i = 0; i < 500; i++) {
if (wavdenoise[i] != 1.0) {
cp.denoicurv = true;
break;
}
}
}
if(cp.complex == 0) {
wavdenoiseh = wavdenoise;
}
if (wavdenoiseh) {
for (int i = 0; i < 500; i++) {
if (wavdenoiseh[i] != 1.0) {
cp.denoicurvh = true;
break;
}
}
}
for (int m = 0; m < maxmul; m++) {
cp.mul[m] = waparams.c[m];
}
for (int m = maxmul; m < 10; m++) {
cp.mul[m] = 0.f;
}
cp.mul[9] = (float) waparams.sup;
for (int sc = 0; sc < 10; sc++) { //reduce strength if zoom < 100% for contrast
if (sc == 0) {
if (scaleskip[sc] < 1.f) {
cp.mul[sc] *= (atten0 * scaleskip[sc]);
}
} else {
if (scaleskip[sc] < 1.f) {
cp.mul[sc] *= (atten123 * scaleskip[sc]);
}
}
}
for (int sc = 0; sc < 9; sc++) { //reduce strength if zoom < 100% for chroma and tuning
if (sc == 0) {
if (scaleskip[sc] < 1.f) {
cp.mulC[sc] *= (atten0 * scaleskip[sc]);
cp.mulopaRG[sc] *= (atten0 * scaleskip[sc]);
cp.mulopaBY[sc] *= (atten0 * scaleskip[sc]);
}
} else {
if (scaleskip[sc] < 1.f) {
cp.mulC[sc] *= (atten123 * scaleskip[sc]);
cp.mulopaRG[sc] *= (atten123 * scaleskip[sc]);
cp.mulopaBY[sc] *= (atten123 * scaleskip[sc]);
}
}
}
cp.chro = waparams.chro;
cp.chrom = waparams.chroma;
cp.contrast = waparams.contrast;
cp.rad = waparams.edgrad;
cp.val = waparams.edgval;
cp.til = waparams.edgthresh;
cp.eff = waparams.edgeffect;
cp.balchrom = waparams.balchrom;
cp.chromfi = 0.1f * waparams.chromfi;
cp.chromco = 0.1f * waparams.chromco;
cp.ballum = waparams.ballum;
cp.conres = waparams.rescon;
cp.conresH = waparams.resconH;
cp.radius = waparams.radius;
cp.chrores = waparams.reschro;
cp.oldsh = waparams.oldsh;
cp.blurres = waparams.resblur;
cp.blurcres = waparams.resblurc;
cp.bluwav = waparams.bluwav;
//cp.hueres=waparams.reshue;
cp.hueres = 2.f;
cp.th = float(waparams.thr);
cp.thH = float(waparams.thrH);
cp.sky = waparams.sky;
//skin
cp.b_l = static_cast<float>(params->wavelet.hueskin.getBottomLeft()) / 100.0f;
cp.t_l = static_cast<float>(params->wavelet.hueskin.getTopLeft()) / 100.0f;
cp.b_r = static_cast<float>(params->wavelet.hueskin.getBottomRight()) / 100.0f;
cp.t_r = static_cast<float>(params->wavelet.hueskin.getTopRight()) / 100.0f;
cp.b_ly = static_cast<float>(params->wavelet.hueskin2.getBottomLeft()) / 100.0f;
cp.t_ly = static_cast<float>(params->wavelet.hueskin2.getTopLeft()) / 100.0f;
cp.b_ry = static_cast<float>(params->wavelet.hueskin2.getBottomRight()) / 100.0f;
cp.t_ry = static_cast<float>(params->wavelet.hueskin2.getTopRight()) / 100.0f;
cp.numlevH = params->wavelet.threshold -1;
//shadows
cp.b_lsl = static_cast<float>(params->wavelet.bllev.getBottomLeft());
cp.t_lsl = static_cast<float>(params->wavelet.bllev.getTopLeft());
cp.b_rsl = static_cast<float>(params->wavelet.bllev.getBottomRight());
cp.t_rsl = static_cast<float>(params->wavelet.bllev.getTopRight());
cp.numlevS = params->wavelet.threshold2; //rtengine::max(cp.numlevS, maxlevS);
//highlight
cp.b_lhl = static_cast<float>(params->wavelet.hllev.getBottomLeft());
cp.t_lhl = static_cast<float>(params->wavelet.hllev.getTopLeft());
cp.b_rhl = static_cast<float>(params->wavelet.hllev.getBottomRight());
cp.t_rhl = static_cast<float>(params->wavelet.hllev.getTopRight());
//pastel
cp.b_lpast = static_cast<float>(params->wavelet.pastlev.getBottomLeft());
cp.t_lpast = static_cast<float>(params->wavelet.pastlev.getTopLeft());
cp.b_rpast = static_cast<float>(params->wavelet.pastlev.getBottomRight());
cp.t_rpast = static_cast<float>(params->wavelet.pastlev.getTopRight());
//saturated
cp.b_lsat = static_cast<float>(params->wavelet.satlev.getBottomLeft());
cp.t_lsat = static_cast<float>(params->wavelet.satlev.getTopLeft());
cp.b_rsat = static_cast<float>(params->wavelet.satlev.getBottomRight());
cp.t_rsat = static_cast<float>(params->wavelet.satlev.getTopRight());
//edge local contrast
cp.edg_low = static_cast<float>(params->wavelet.edgcont.getBottomLeft());
cp.edg_mean = static_cast<float>(params->wavelet.edgcont.getTopLeft());
cp.edg_max = static_cast<float>(params->wavelet.edgcont.getBottomRight());
cp.edg_sd = static_cast<float>(params->wavelet.edgcont.getTopRight());
//level noise
cp.lev0s = static_cast<float>(params->wavelet.level0noise.getBottom());
cp.lev0n = static_cast<float>(params->wavelet.level0noise.getTop());
cp.lev1s = static_cast<float>(params->wavelet.level1noise.getBottom());
cp.lev1n = static_cast<float>(params->wavelet.level1noise.getTop());
cp.lev2s = static_cast<float>(params->wavelet.level2noise.getBottom());
cp.lev2n = static_cast<float>(params->wavelet.level2noise.getTop());
cp.lev3s = static_cast<float>(params->wavelet.level3noise.getBottom());
cp.lev3n = static_cast<float>(params->wavelet.level3noise.getTop());
cp.lev4n = static_cast<float>(params->wavelet.leveldenoise.getTop());
cp.lev4t = 0.01f * static_cast<float>(params->wavelet.leveldenoise.getBottom());
cp.sigmm14 = static_cast<float>(params->wavelet.levelsigm.getTop());
cp.sigmm56 = static_cast<float>(params->wavelet.levelsigm.getBottom());
cp.detectedge = params->wavelet.medianlev;
int minwin = rtengine::min(imwidth, imheight);
int maxlevelcrop = 9;
if (cp.mul[9] != 0) {
maxlevelcrop = 10;
}
// adapt maximum level wavelet to size of crop
if (minwin * skip < 1024) {
maxlevelcrop = 9; //sampling wavelet 512
}
if (minwin * skip < 512) {
maxlevelcrop = 8; //sampling wavelet 256
}
if (minwin * skip < 256) {
maxlevelcrop = 7; //sampling 128
}
if (minwin * skip < 128) {
maxlevelcrop = 6;
}
if (minwin * skip < 64) {
maxlevelcrop = 5;
}
if (minwin * skip < 32) {
maxlevelcrop = 4;
}
int levwav = params->wavelet.thres;
if(params->wavelet.expnoise) {
levwav = 6;
}
if (levwav == 9 && cp.mul[9] != 0) {
levwav = 10;
}
levwav = rtengine::min(maxlevelcrop, levwav);
// I suppress this functionality ==> crash for level < 3
if (levwav < 1) {
return; // nothing to do
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// begin tile processing of image
//output buffer
int realtile = 0;
if (params->wavelet.Tilesmethod == "big") {
realtile = 22;
}
/*
if (params->wavelet.Tilesmethod == "lit") {
realtile = 12;
}
*/
int tilesize = 128 * realtile;
int overlap = (int) tilesize * 0.125f;
int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip;
if (params->wavelet.Tilesmethod == "full") {
kall = 0;
}
Tile_calc(tilesize, overlap, kall, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip);
const int numtiles = numtiles_W * numtiles_H;
LabImage * dsttmp;
if (numtiles == 1) {
dsttmp = dst;
} else {
dsttmp = new LabImage(imwidth, imheight);
for (int n = 0; n < 3 * imwidth * imheight; n++) {
dsttmp->data[n] = 0;
}
}
//now we have tile dimensions, overlaps
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int minsizetile = rtengine::min(tilewidth, tileheight);
int maxlev2 = 10;
if (minsizetile < 1024 && levwav == 10) {
maxlev2 = 9;
}
if (minsizetile < 512) {
maxlev2 = 8;
}
if (minsizetile < 256) {
maxlev2 = 7;
}
if (minsizetile < 128) {
maxlev2 = 6;
}
if (minsizetile < 64) {
maxlev2 = 5;
}
if (minsizetile < 32) {
maxlev2 = 4;
}
levwav = rtengine::min(maxlev2, levwav);
#ifdef _OPENMP
int numthreads = 1;
int maxnumberofthreadsforwavelet = 0;
//reduce memory for big tile size
if (kall != 0) {
if (realtile <= 22) {
maxnumberofthreadsforwavelet = 2;
}
if (realtile <= 20) {
maxnumberofthreadsforwavelet = 3;
}
if (realtile <= 18) {
maxnumberofthreadsforwavelet = 4;
}
if (realtile <= 16) {
maxnumberofthreadsforwavelet = 6;
}
if (realtile <= 14) {
maxnumberofthreadsforwavelet = 8;
}
if ((maxnumberofthreadsforwavelet == 6 || maxnumberofthreadsforwavelet == 8) && levwav == 10) {
maxnumberofthreadsforwavelet -= 2;
}
if (levwav <= 7 && maxnumberofthreadsforwavelet == 8) {
maxnumberofthreadsforwavelet = 0;
}
}
// Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles
if (options.rgbDenoiseThreadLimit > 0) {
maxnumberofthreadsforwavelet = rtengine::LIM(options.rgbDenoiseThreadLimit / 2, 1, maxnumberofthreadsforwavelet);
}
numthreads = rtengine::min(numtiles, omp_get_max_threads());
if (maxnumberofthreadsforwavelet > 0) {
numthreads = rtengine::min(numthreads, maxnumberofthreadsforwavelet);
}
#ifdef _OPENMP
wavNestedLevels = omp_get_max_threads() / numthreads;
bool oldNested = omp_get_nested();
if (wavNestedLevels < 2) {
wavNestedLevels = 1;
} else {
omp_set_nested(true);
}
if (maxnumberofthreadsforwavelet > 0)
while (wavNestedLevels * numthreads > maxnumberofthreadsforwavelet) {
wavNestedLevels--;
}
#endif
if (settings->verbose) {
printf("Ip Wavelet uses %d main thread(s) and up to %d nested thread(s) for each main thread\n", numthreads, wavNestedLevels);
}
#pragma omp parallel num_threads(numthreads)
#endif
{
float mean[10];
float meanN[10];
float sigma[10];
float sigmaN[10];
float MaxP[10];
float MaxN[10];
float meand[10];
float meanNd[10];
float sigmad[10];
float sigmaNd[10];
float MaxPd[10];
float MaxNd[10];
float meanab[10];
float meanNab[10];
float sigmaab[10];
float sigmaNab[10];
float MaxPab[10];
float MaxNab[10];
array2D<float> varchro(tilewidth, tileheight);
float** varhue = new float*[tileheight];
for (int i = 0; i < tileheight; i++) {
varhue[i] = new float[tilewidth];
}
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2)
#endif
for (int tiletop = 0; tiletop < imheight; tiletop += tileHskip) {
for (int tileleft = 0; tileleft < imwidth ; tileleft += tileWskip) {
int tileright = rtengine::min(imwidth, tileleft + tilewidth);
int tilebottom = rtengine::min(imheight, tiletop + tileheight);
int width = tileright - tileleft;
int height = tilebottom - tiletop;
LabImage * labco;
float **Lold = nullptr;
float *LoldBuffer = nullptr;
if (numtiles == 1) { // untiled processing => we can use output buffer for labco
labco = dst;
if (cp.avoi) { // we need a buffer to hold a copy of the L channel
Lold = new float*[tileheight];
LoldBuffer = new float[tilewidth * tileheight];
memcpy(LoldBuffer, lab->L[0], tilewidth * tileheight * sizeof(float));
for (int i = 0; i < tileheight; i++) {
Lold[i] = LoldBuffer + i * tilewidth;
}
}
} else {
labco = new LabImage(width, height);
Lold = lab->L;
}
#ifdef _OPENMP
#pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = tiletop; i < tilebottom; i++) {
const int i1 = i - tiletop;
int j = tileleft;
#ifdef __SSE2__
const vfloat c327d68v = F2V(327.68f);
for (; j < tileright - 3; j += 4) {
const int j1 = j - tileleft;
const vfloat av = LVFU(lab->a[i][j]);
const vfloat bv = LVFU(lab->b[i][j]);
STVFU(varhue[i1][j1], xatan2f(bv, av));
STVFU(varchro[i1][j1], vsqrtf(SQRV(av) + SQRV(bv)) / c327d68v);
if (labco != lab) {
STVFU((labco->L[i1][j1]), LVFU(lab->L[i][j]));
STVFU((labco->a[i1][j1]), av);
STVFU((labco->b[i1][j1]), bv);
}
}
#endif
for (; j < tileright; j++) {
const int j1 = j - tileleft;
const float a = lab->a[i][j];
const float b = lab->b[i][j];
varhue[i1][j1] = xatan2f(b, a);
varchro[i1][j1] = (sqrtf(a * a + b * b)) / 327.68f;
if (labco != lab) {
labco->L[i1][j1] = lab->L[i][j];
labco->a[i1][j1] = a;
labco->b[i1][j1] = b;
}
}
}
//to avoid artifacts in blue sky
if (params->wavelet.median) {
float** tmL;
int wid = labco->W;
int hei = labco->H;
int borderL = 1;
tmL = new float*[hei];
for (int i = 0; i < hei; i++) {
tmL[i] = new float[wid];
}
for (int i = borderL; i < hei - borderL; i++) {
for (int j = borderL; j < wid - borderL; j++) {
tmL[i][j] = labco->L[i][j];
}
}
#ifdef _OPENMP
#pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 1; i < hei - 1; i++) {
for (int j = 1; j < wid - 1; j++) {
if ((varhue[i][j] < -1.3f && varhue[i][j] > - 2.5f) && (varchro[i][j] > 15.f && varchro[i][j] < 55.f) && labco->L[i][j] > 6000.f) { //blue sky + med3x3 ==> after for more effect use denoise
tmL[i][j] = median(labco->L[i][j], labco->L[i - 1][j], labco->L[i + 1][j], labco->L[i][j + 1], labco->L[i][j - 1], labco->L[i - 1][j - 1], labco->L[i - 1][j + 1], labco->L[i + 1][j - 1], labco->L[i + 1][j + 1]); //3x3
}
}
}
for (int i = borderL; i < hei - borderL; i++) {
for (int j = borderL; j < wid - borderL; j++) {
labco->L[i][j] = tmL[i][j];
}
}
for (int i = 0; i < hei; i++) {
delete [] tmL[i];
}
delete [] tmL;
// end blue sky
}
if (numtiles == 1) {
// reduce the varhue array to get faster access in following processing and reduce peak memory usage
float temphue[(tilewidth + 1) / 2] ALIGNED64;
for (int i = 0; i < (tileheight + 1) / 2; i++) {
for (int j = 0; j < (tilewidth + 1) / 2; j++) {
temphue[j] = varhue[i * 2][j * 2];
}
delete [] varhue[i];
varhue[i] = new float[(tilewidth + 1) / 2];
memcpy(varhue[i], temphue, ((tilewidth + 1) / 2) * sizeof(float));
}
for (int i = (tileheight + 1) / 2; i < tileheight; i++) {
delete [] varhue[i];
varhue[i] = nullptr;
}
} else { // reduce the varhue array to get faster access in following processing
for (int i = 0; i < (tileheight + 1) / 2; i++) {
for (int j = 0; j < (tilewidth + 1) / 2; j++) {
varhue[i][j] = varhue[i * 2][j * 2];
}
}
}
int datalen = labco->W * labco->H;
levwavL = levwav;
bool ref0 = false;
if ((cp.lev0s > 0.f || cp.lev1s > 0.f || cp.lev2s > 0.f || cp.lev3s > 0.f) && cp.noiseena) {
ref0 = true;
}
bool wavcurvecomp = false;//not enable if 0.75
if (wavblcurve) {
for (int i = 0; i < 500; i++) {
if (wavblcurve[i] != 0.) {
wavcurvecomp = true;
}
}
}
bool exblurL = cp.blena && wavcurvecomp;
if (exblurL) {
if (cp.mul[0] == 0.f) {
cp.mul[0] = 0.01f;//to always enable WaveletcontAllL if no contrast is needed
}
}
if (cp.BAmet != 0) {
if (cp.mul[0] == 0.f) {
cp.mul[0] = 0.01f;
}
}
// printf("cp4=%f cpmul5=%f cp6=%f cp7=%f cp8=%f\n", (double)cp.mul[4], (double)cp.mul[5],(double)cp.mul[6],(double)cp.mul[7],(double)cp.mul[8]);
if (levwavL == 6 && cp.noiseena && cp.chromfi == 0.f) {
cp.chromfi = 0.01f;
}
if (cp.chromfi > 0.f || cp.chromco > 0.f) {
if (levwavL < 7) {
levwavL = 7;
}
}
if (levwavL < 5 && cp.noiseena) {
levwavL = 6; //to allow edge and denoise => I always allocate 3 (4) levels..because if user select wavelet it is to do something !!
}
if (!exblurL && cp.contrast == 0.f && cp.blurres == 0.f && !cp.noiseena && !cp.tonemap && !cp.resena && !cp.chromena && !cp.toningena && !cp.finena && !cp.edgeena && cp.conres == 0.f && cp.conresH == 0.f && cp.val == 0 && !ref0 && params->wavelet.CLmethod == "all") { // no processing of residual L or edge=> we probably can reduce the number of levels
while (levwavL > 0 && cp.mul[levwavL - 1] == 0.f) { // cp.mul[level] == 0.f means no changes to level
levwavL--;
}
}
bool isdenoisL = (cp.lev0n > 0.1f || cp.lev1n > 0.1f || cp.lev2n > 0.1f || cp.lev3n > 0.1f || cp.lev4n > 0.1f);
/*
if(cp.denoicurvh || cp.levdenhigh > 0.01f) {
levwavL = levwav;
}
*/
float th = 0.01f * (float) waparams.thrend;
if(th > 0.f) {
levwavL = levwav;
}
bool usechrom = cp.chromfi > 0.f || cp.chromco > 0.f;
levwavL = rtengine::min(maxlevelcrop, levwavL);
levwavL = rtengine::min(maxlev2, levwavL);
if (settings->verbose) {
printf("Level decomp L=%i\n", levwavL);
}
if (levwavL > 0) {
const std::unique_ptr<wavelet_decomposition> Ldecomp(new wavelet_decomposition(labco->data, labco->W, labco->H, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
// const std::unique_ptr<wavelet_decomposition> Ldecomp2(new wavelet_decomposition(labco->data, labco->W, labco->H, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if (!Ldecomp->memory_allocation_failed()) {
float madL[10][3];
// float madL[8][3];
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic) collapse(2) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int lvl = 0; lvl < levwavL; lvl++) {
for (int dir = 1; dir < 4; dir++) {
int Wlvl_L = Ldecomp->level_W(lvl);
int Hlvl_L = Ldecomp->level_H(lvl);
const float* const* WavCoeffs_L = Ldecomp->level_coeffs(lvl);
madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L));
if (settings->verbose) {
// printf("Luminance noise estimate (sqr) madL=%.0f lvl=%i dir=%i\n", madL[lvl][dir - 1], lvl, dir - 1);
}
}
}
bool ref = false;
if ((cp.lev0s > 0.f || cp.lev1s > 0.f || cp.lev2s > 0.f || cp.lev3s > 0.f) && cp.noiseena) {
ref = true;
}
bool contr = false;
for (int f = 0; f < levwavL; f++) {
if (cp.mul[f] != 0.f) {
contr = true;
}
}
// if (cp.val > 0 || ref || contr || cp.denoicurv || cp.denoicurvh || cp.noiseena || cp.levdenlow > 0.f || cp.thrden > 0.f ) { //edge
if (cp.val > 0 || ref || contr || cp.denoicurv || cp.denoicurvh || cp.noiseena || cp.thrden > 0.f ) { //edge
Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavNestedLevels);
}
//init for edge and denoise
float vari[6];
vari[0] = 0.8f * SQR((cp.lev0n / 125.f) * (1.f + cp.lev0n / 25.f));
vari[1] = 0.8f * SQR((cp.lev1n / 125.f) * (1.f + cp.lev1n / 25.f));
vari[2] = 0.8f * SQR((cp.lev2n / 125.f) * (1.f + cp.lev2n / 25.f));
vari[3] = 0.8f * SQR((cp.lev3n / 125.f) * (1.f + cp.lev3n / 25.f));
vari[4] = 0.8f * SQR((cp.lev4n / 125.f) * (1.f + cp.lev4n / 25.f));
vari[5] = 0.8f * SQR((cp.lev4n / 125.f) * (1.f + cp.lev4n / 25.f));
float kr3 = 1.f;
if (cp.lev3n < 10.f) {
kr3 = 0.3f;
} else if (cp.lev3n < 30.f) {
kr3 = 0.6f;
} else if (cp.lev3n < 70.f) {
kr3 = 0.8f;
} else {
kr3 = 1.f;
}
float kr4 = 1.f;
if (cp.lev4n < 10.f) {
kr4 = 0.6f;
} else if (cp.lev4n < 30.f) {
kr4 = 0.8f;
} else if (cp.lev4n < 70.f) {
kr4 = 0.9f;
} else {
kr4 = 1.f;
}
if ((cp.lev0n > 0.1f || cp.lev1n > 0.1f || cp.lev2n > 0.1f || cp.lev3n > 0.1f || cp.lev4n > 0.1f) && cp.noiseena) {
int edge = 6;
vari[0] = rtengine::max(0.000001f, vari[0]);
vari[1] = rtengine::max(0.000001f, vari[1]);
vari[2] = rtengine::max(0.000001f, vari[2]);
vari[3] = rtengine::max(0.000001f, kr3 * vari[3]);
vari[4] = rtengine::max(0.000001f, kr4 * vari[4]);
vari[5] = rtengine::max(0.000001f, kr4 * vari[5]);
const std::unique_ptr<wavelet_decomposition> Ldecomp2(new wavelet_decomposition(labco->data, labco->W, labco->H, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if(!Ldecomp2->memory_allocation_failed()){
if (settings->verbose) {
printf("LUM var0=%f var1=%f var2=%f var3=%f var4=%f\n", vari[0], vari[1], vari[2], vari[3], vari[4]);
}
// float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL
int GWL = labco->W;
int GHL = labco->H;
float* noisevarlum = new float[GHL * GWL];
float* noisevarhue = new float[GHL * GWL];
int GW2L = (GWL + 1) / 2;
constexpr float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value
constexpr float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value
constexpr float seuillow = 3000.f;//low
constexpr float seuilhigh = 18000.f;//high
const int index = 10 - cp.ballum;
const float ac = (nvlh[index] - nvll[index]) / (seuillow - seuilhigh);
const float bc = nvlh[index] - seuillow * ac;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < GHL; ir++)
for (int jr = 0; jr < GWL; jr++) {
float lN = labco->L[ir][jr];
if (lN < seuillow) {
noisevarlum[(ir >> 1)*GW2L + (jr >> 1)] = nvlh[index];
} else if (lN < seuilhigh) {
noisevarlum[(ir >> 1)*GW2L + (jr >> 1)] = ac * lN + bc;
} else {
noisevarlum[(ir >> 1)*GW2L + (jr >> 1)] = nvll[index];
}
}
if(wavhueutili) {
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int ir = 0; ir < GHL; ir++)
for (int jr = 0; jr < GWL; jr++) {
float hueG = xatan2f(labco->b[ir][jr], labco->a[ir][jr]);
noisevarhue[(ir >> 1)*GW2L + (jr >> 1)] = 1.f + 2.f * (static_cast<float>(wavhueCurve->getVal(Color::huelab_to_huehsv2(hueG))) - 0.5f);
noisevarlum[(ir >> 1)*GW2L + (jr >> 1)] *= noisevarhue[(ir >> 1)*GW2L + (jr >> 1)];
}
}
if(cp.quamet == 0) {
if (settings->verbose) {
printf("denoise standard L\n");
}
WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, vari, edge, 1);
} else {
if (settings->verbose) {
printf("denoise bishrink L\n");
}
WaveletDenoiseAll_BiShrinkL(*Ldecomp, noisevarlum, madL, vari, edge, 1);
WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, vari, edge, 1);
}
delete[] noisevarlum;
//evaluate after denoise
bool exitifzero = true;
Evaluate2(*Ldecomp, meand, meanNd, sigmad, sigmaNd, MaxPd, MaxNd, wavNestedLevels);
for (int dir = 1; dir < 4; dir++) {
for (int level = 0; level < levwavL; level++) {
if(mean[level] < 0.1f || meand[level] < 0.1f || sigma[level] < 0.1f || sigmad[level] < 0.1f) {
printf("near zero - exit\n");
exitifzero = false;
}
}
}
//for level 0 1 2 3
float thr = 0.f;
float thrend = cp.thrden; //cp.levdenlow;
if(thrend < 0.01f) thr = 0.95f;
else if(thrend < 0.02f) thr = 0.9f;
else if(thrend < 0.04f) thr = 0.8f;
else if(thrend < 0.06f) thr = 0.7f;
else if(thrend < 0.08f) thr = 0.6f;
else if(thrend < 0.1f) thr = 0.5f;
else if(thrend < 0.2f) thr = 0.2f;
else thr = 0.f;
FlatCurve wavlow({
FCT_MinMaxCPoints,
0, 1, 0.35, 0.35,thrend, 1.0, 0.35, 0.35, thrend + 0.01f, thr, 0.35, 0.35, 1, thr, 0.35, 0.35
});
//for level 4
float thrhigh = 0.f;
float threndhigh = cp.lev4t; //cp.levdenlow;
if(threndhigh < 0.01f) thrhigh = 0.95f;
else if(threndhigh < 0.02f) thrhigh = 0.9f;
else if(threndhigh < 0.04f) thrhigh = 0.8f;
else if(threndhigh < 0.06f) thrhigh = 0.7f;
else if(threndhigh < 0.08f) thrhigh = 0.6f;
else if(threndhigh < 0.1f) thrhigh = 0.5f;
else thrhigh = 0.f;
FlatCurve wavhigh({
FCT_MinMaxCPoints,
0, 1, 0.35, 0.35,threndhigh, 1.0, 0.35, 0.35, threndhigh + 0.01f, thrhigh, 0.35, 0.35, 1, thrhigh, 0.35, 0.35
});
float thrmed = 0.f;
float threndmed = 1.f - cp.limden;
if(threndmed < 0.02f) thrmed = 0.5f;
else if(threndmed < 0.05f) thrmed = 0.2f;
else thrmed = 0.f;
FlatCurve wavmed({
FCT_MinMaxCPoints,
0, 1, 0.35, 0.35,threndmed, 1.0, 0.35, 0.35, threndmed + 0.01f, thrmed, 0.35, 0.35, 1, thrmed, 0.35, 0.35
});
float siglh[10];
float levref = 6;
//levref = levwavL-1;
if(cp.complex == 1){
for (int level = 0; level < levref; level++) {
if(level > 3) {
siglh[level] = cp.sigmm56;
} else {
siglh[level] = cp.sigmm14;
}
}
} else {
levref = 4;
for (int level = 0; level < levref; level++) {
siglh[level] = cp.sigmm;
}
}
// printf("sig0=%f sig1=%f sig2=%f sig3=%f sig4=%f sig5=%f\n", siglh[0], siglh[1],siglh[2],siglh[3],siglh[4],siglh[5]);
bool execut = false;
if(cp.slimet == 0) {
// if(cp.levdenlow > 0.f) {
if(cp.thrden > 0.f) {
execut = true;
}
} else {
if(cp.denoicurv) {
execut = true;
}
}
// }
if (execut && exitifzero) {
for (int dir = 1; dir < 4; dir++) {
for (int level = 0; level < levref; level++) {
const int Wlvl_L = Ldecomp->level_W(level);
const int Hlvl_L = Ldecomp->level_H(level);
const float* const WavCoeffs_L = Ldecomp->level_coeffs(level)[dir];//first decomp denoised
const float* const WavCoeffs_L2 = Ldecomp2->level_coeffs(level)[dir];//second decomp before denoise
const int k4 = cp.complex == 1 ? 4 : 3;
const int k5 = cp.complex == 1 ? 5 : 3;
auto WavL0 = Ldecomp->level_coeffs(0)[dir];
auto WavL1 = Ldecomp->level_coeffs(1)[dir];
auto WavL2 = Ldecomp->level_coeffs(2)[dir];
auto WavL3 = Ldecomp->level_coeffs(3)[dir];
auto WavL4 = Ldecomp->level_coeffs(k4)[dir];
auto WavL5 = Ldecomp->level_coeffs(k5)[dir];
//not denoise
const auto WavL02 = Ldecomp2->level_coeffs(0)[dir];
const auto WavL12 = Ldecomp2->level_coeffs(1)[dir];
const auto WavL22 = Ldecomp2->level_coeffs(2)[dir];
const auto WavL32 = Ldecomp2->level_coeffs(3)[dir];
const auto WavL42 = Ldecomp2->level_coeffs(k4)[dir];
const auto WavL52 = Ldecomp2->level_coeffs(k5)[dir];
if (settings->verbose) {
printf("level=%i mean=%.0f meanden=%.0f sigma=%.0f sigmaden=%.0f Max=%.0f Maxden=%.0f\n", level, mean[level], meand[level], sigma[level], sigmad[level],MaxP[level], MaxPd[level]);
}
//find local contrast
constexpr float weights[4] = {0.f, 0.5f, 0.7f, 1.f};
const float weightL = weights[rtengine::LIM(cp.mixmet, 0, 3)];
const float tempmean = intp(weightL, meand[level], mean[level]);
const float tempsig = intp(weightL, sigmad[level], sigma[level]);
const float tempmax = intp(weightL, MaxPd[level], MaxP[level]);
if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { //curve
constexpr float insigma = 0.666f; //SD
const float logmax = log(tempmax); //log Max
//cp.sigmm change the "wider" of sigma
const float rapX = (tempmean + siglh[level] * tempsig) / tempmax; //rapport between sD / max
const float inx = log(insigma);
const float iny = log(rapX);
const float rap = inx / iny; //koef
const float asig = 0.166f / (tempsig * siglh[level]);
const float bsig = 0.5f - asig * tempmean;
const float amean = 1.f / tempmean;
//equalizer for levels 0 1 and 3... 1.33 and 0.75 arbitrary values
float kcFactor = 1.f;
if(cp.denmet == 1) {
if(level == 0 || level == 3) {
kcFactor = 1.7f;
}
} else if(cp.denmet == 2) {
if(level == 0 || level == 3) {
kcFactor = 0.3f;
}
} else if(cp.denmet == 3) {
if(level == 0 || level == 1) {
kcFactor = 1.7f;
}
} else if(cp.denmet == 4) {
if(level == 0 || level == 1) {
kcFactor = 0.3f;
}
}
const float k = 1.f / (siglh[level] > 1.f ? SQR(siglh[level]) : siglh[level]);
const float maxVal = tempmean + siglh[level] * tempsig;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, Wlvl_L * 16) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < Wlvl_L * Hlvl_L; i++) {
float absciss;
const float tempwav = std::fabs(intp(weightL, WavCoeffs_L[i], WavCoeffs_L2[i]));
if (tempwav >= maxVal) { //for max
absciss = xexpf((xlogf(tempwav) - logmax) * rap);
} else if (tempwav >= tempmean) {
absciss = asig * tempwav + bsig;
} else {
absciss = 0.5f * pow_F(amean * tempwav, k);
}
float kc;
if (cp.slimet == 0) {
kc = wavlow.getVal(absciss) - 1.f;
} else {
kc = wavdenoise[absciss * 500.f] - 1.f;
}
if (kc < 0) {
kc = -SQR(kc); //approximation to simulate sliders denoise
}
kc *= kcFactor;
const float reduceeffect = kc <= 0.f ? 1.f : 1.2f; //1.2 allows to increase denoise (not used)
if (level < 4) {
float kintermlow = 1.f + reduceeffect * kc;
kintermlow = kintermlow <= 0.f ? 0.01f : kintermlow;
WavL0[i] = WavL02[i] + (WavL0[i] - WavL02[i]) * kintermlow;
WavL1[i] = WavL12[i] + (WavL1[i] - WavL12[i]) * kintermlow;
WavL2[i] = WavL22[i] + (WavL2[i] - WavL22[i]) * kintermlow;
WavL3[i] = WavL32[i] + (WavL3[i] - WavL32[i]) * kintermlow;
}
if (cp.complex == 1){
if (cp.limden > 0.f) {
const float kcmed = -SQR(wavmed.getVal(absciss) - 1.f);
float kintermed = 1.f + reduceeffect * kcmed;
kintermed = kintermed <= 0.f ? 0.01f : kintermed;
WavL0[i] = WavL02[i] + (WavL0[i] - WavL02[i]) * kintermed;
WavL1[i] = WavL12[i] + (WavL1[i] - WavL12[i]) * kintermed;
WavL2[i] = WavL22[i] + (WavL2[i] - WavL22[i]) * kintermed;
WavL3[i] = WavL32[i] + (WavL3[i] - WavL32[i]) * kintermed;
}
const float kchigh = -SQR(wavhigh.getVal(absciss) - 1.f);
float kintermhigh = 1.f + reduceeffect * kchigh;
kintermhigh = kintermhigh <= 0.f ? 0.01f : kintermhigh;
WavL4[i] = WavL42[i] + (WavL4[i] - WavL42[i]) * kintermhigh;
WavL5[i] = WavL52[i] + (WavL5[i] - WavL52[i]) * kintermhigh;
}
}
}
}
}
if (settings->verbose) {
Evaluate2(*Ldecomp, meand, meanNd, sigmad, sigmaNd, MaxPd, MaxNd, wavNestedLevels);
for (int dir = 1; dir < 4; dir++) {
for (int level = 0; level < levref; level++) {
printf("AFTER LC level=%i mean=%.0f meanden=%.0f sigma=%.0f sigmaden=%.0f Max=%.0f Maxden=%.0f\n", level, mean[level], meand[level], sigma[level], sigmad[level],MaxP[level], MaxPd[level]);
}
}
}
}
delete[] noisevarhue;
}
}
//Flat curve for Contrast=f(H) in levels
FlatCurve* ChCurve = new FlatCurve(params->wavelet.Chcurve); //curve C=f(H)
bool Chutili = false;
if (!ChCurve || ChCurve->isIdentity()) {
if (ChCurve) {
delete ChCurve;
ChCurve = nullptr;
}
} else {
Chutili = true;
}
WaveletcontAllL(labco, varhue, varchro, *Ldecomp, wavblcurve, cp, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveSH, ChCurve, Chutili);
if (cp.val > 0 || ref || contr || cp.diagcurv) { //edge
Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavNestedLevels);
}
WaveletcontAllLfinal(*Ldecomp, cp, mean, sigma, MaxP, waOpacityCurveWL);
//Evaluate2(*Ldecomp, cp, ind, mean, meanN, sigma, sigmaN, MaxP, MaxN, madL);
/*
Ldecomp->reconstruct(labco->data, cp.strength);
}
}
*/
if (!usechrom) {
Ldecomp->reconstruct(labco->data, cp.strength);
}
float variC[7];
float variCb[7];
float noisecfr = cp.chromfi;
float noiseccr = cp.chromco;
if (cp.balchrom > 0.f) {
noisecfr = cp.chromfi + 0.1f * cp.balchrom;
noiseccr = cp.chromco + 0.1f * cp.balchrom;
}
float noisecfb = cp.chromfi;
float noiseccb = cp.chromco;
if (cp.balchrom < 0.f) {
noisecfb = cp.chromfi - 0.1f * cp.balchrom;
noiseccb = cp.chromco - 0.1f * cp.balchrom;
}
if (noisecfr < 0.f) {
noisecfr = 0.00001f;
}
if (noiseccr < 0.f) {
noiseccr = 0.00001f;
}
if (noisecfb < 0.f) {
noisecfb = 0.00001f;
}
if (noiseccb < 0.f) {
noiseccb = 0.0001f;
}
int edge = 2;
variC[0] = SQR(noisecfr);
variC[1] = SQR(noisecfr);
variC[2] = SQR(noisecfr);
variC[3] = SQR(noisecfr);
variC[4] = SQR(noisecfr);
variC[5] = SQR(noiseccr);
variC[6] = SQR(noiseccr);
variCb[0] = SQR(noisecfb);
variCb[1] = SQR(noisecfb);
variCb[2] = SQR(noisecfb);
variCb[3] = SQR(noisecfb);
variCb[4] = SQR(noisecfb);
variCb[5] = SQR(noiseccb);
variCb[6] = SQR(noiseccb);
float k1 = 0.f;
float k2 = 0.f;
float k3 = 0.f;
if (cp.chromfi < 0.2f) {
k1 = 0.05f;
k2 = 0.f;
k3 = 0.f;
} else if (cp.chromfi < 0.3f) {
k1 = 0.1f;
k2 = 0.0f;
k3 = 0.f;
} else if (cp.chromfi < 0.5f) {
k1 = 0.2f;
k2 = 0.1f;
k3 = 0.f;
} else if (cp.chromfi < 0.8f) {
k1 = 0.3f;
k2 = 0.25f;
k3 = 0.f;
} else if (cp.chromfi < 1.f) {
k1 = 0.4f;
k2 = 0.25f;
k3 = 0.1f;
} else if (cp.chromfi < 2.f) {
k1 = 0.5f;
k2 = 0.3f;
k3 = 0.15f;
} else if (cp.chromfi < 3.f) {
k1 = 0.6f;
k2 = 0.45f;
k3 = 0.3f;
} else if (cp.chromfi < 4.f) {
k1 = 0.7f;
k2 = 0.5f;
k3 = 0.4f;
} else if (cp.chromfi < 5.f) {
k1 = 0.8f;
k2 = 0.6f;
k3 = 0.5f;
} else if (cp.chromfi < 6.f) {
k1 = 0.85f;
k2 = 0.7f;
k3 = 0.6f;
} else if (cp.chromfi < 8.f) {
k1 = 0.9f;
k2 = 0.8f;
k3 = 0.7f;
} else if (cp.chromfi < 10.f) {
k1 = 1.f;
k2 = 1.f;
k3 = 0.9f;
} else {
k1 = 1.f;
k2 = 1.f;
k3 = 1.f;
}
float minic = 0.000001f;
variC[0] = max(minic, variC[0]);
variC[1] = max(minic, k1 * variC[1]);
variC[2] = max(minic, k2 * variC[2]);
variC[3] = max(minic, k3 * variC[3]);
variCb[0] = max(minic, variCb[0]);
variCb[1] = max(minic, k1 * variCb[1]);
variCb[2] = max(minic, k2 * variCb[2]);
variCb[3] = max(minic, k3 * variCb[3]);
float k4 = 0.f;
float k5 = 0.f;
float k6 = 0.f;
if (cp.chromco < 0.2f) {
k4 = 0.1f;
k5 = 0.02f;
} else if (cp.chromco < 0.5f) {
k4 = 0.15f;
k5 = 0.05f;
} else if (cp.chromco < 1.f) {
k4 = 0.15f;
k5 = 0.1f;
} else if (cp.chromco < 3.f) {
k4 = 0.3f;
k5 = 0.15f;
} else if (cp.chromco < 4.f) {
k4 = 0.6f;
k5 = 0.4f;
} else if (cp.chromco < 6.f) {
k4 = 0.8f;
k5 = 0.6f;
} else {
k4 = 1.f;
k5 = 1.f;
}
variC[4] = max(0.000001f, k4 * variC[4]);
variC[5] = max(0.000001f, k5 * variC[5]);
variCb[4] = max(0.000001f, k4 * variCb[4]);
variCb[5] = max(0.000001f, k5 * variCb[5]);
if (cp.chromco < 4.f) {
k6 = 0.f;
} else if (cp.chromco < 5.f) {
k6 = 0.4f;
} else if (cp.chromco < 6.f) {
k6 = 0.7f;
} else {
k6 = 1.f;
}
variC[6] = max(0.00001f, k6 * variC[6]);
variCb[6] = max(0.00001f, k6 * variCb[6]);
if (settings->verbose) {
printf("CHRO var0=%f va1=%f va2=%f va3=%f va4=%f val5=%f va6=%f\n", variC[0], variC[1], variC[2], variC[3], variC[4], variC[5], variC[6]);
}
/*
for (int y = 0; y < 7; y++) {
printf("y=%i madL=%f varia=%f variab=%f\n", y, madL[y][1], variC[y], variCb[y]);
}
*/
float nvch = 0.6f;//high value
float nvcl = 0.1f;//low value
if (cp.chromco > 30.f) {
nvch = 0.8f;
nvcl = 0.4f;
}
float seuil = 4000.f;//low
float seuil2 = 15000.f;//high
//ac and bc for transition
float ac = (nvch - nvcl) / (seuil - seuil2);
float bc = nvch - seuil * ac;
int GW = labco->W;
int GH = labco->H;
float* noisevarchrom = new float[GH * GW];
//noisevarchrom in function chroma
int GW2 = (GW + 1) / 2;
float noisevarab_r = 100.f;
for (int ir = 0; ir < GH; ir++)
for (int jr = 0; jr < GW; jr++) {
float cN = sqrt(SQR(labco->a[ir][jr]) + SQR(labco->b[ir][jr]));
if (cN < seuil) {
noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvch;
} else if (cN < seuil2) {
noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = ac * cN + bc;
} else {
noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvcl;
}
}
//Flat curve for H=f(H) in residual image
FlatCurve* hhCurve = new FlatCurve(params->wavelet.hhcurve); //curve H=f(H)
bool hhutili = false;
if (!hhCurve || hhCurve->isIdentity()) {
if (hhCurve) {
delete hhCurve;
hhCurve = nullptr;
}
} else {
hhutili = true;
}
bool exblurab = cp.chrwav > 0.f && exblurL;
if (!hhutili) { //always a or b
int levwava = levwav;
if (!exblurab && cp.chrores == 0.f && cp.blurcres == 0.f && !cp.noiseena && !cp.tonemap && !cp.resena && !cp.chromena && !cp.toningena && !cp.finena && !cp.edgeena && params->wavelet.CLmethod == "all" && !cp.cbena) { // no processing of residual ab => we probably can reduce the number of levels
while (levwava > 0 && !cp.diag && (((cp.CHmet == 2 && (cp.chro == 0.f || cp.mul[levwava - 1] == 0.f)) || (cp.CHmet != 2 && (levwava == 10 || (!cp.curv || cp.mulC[levwava - 1] == 0.f))))) && (!cp.opaRG || levwava == 10 || (cp.opaRG && cp.mulopaRG[levwava - 1] == 0.f)) && ((levwava == 10 || (cp.CHSLmet == 1 && cp.mulC[levwava - 1] == 0.f)))) {
levwava--;
}
}
if (cp.chromfi > 0.f || cp.chromco > 0.f) {
if (levwava < 7) {
levwava = 7;
}
}
levwava = rtengine::min(maxlevelcrop, levwava);
levwava = rtengine::min(maxlev2, levwava);
levwava = rtengine::min(levwav, levwava);
if (settings->verbose) {
printf("Leval decomp a=%i\n", levwava);
}
if (levwava > 0) {
const std::unique_ptr<wavelet_decomposition> adecomp(new wavelet_decomposition(labco->data + datalen, labco->W, labco->H, levwava, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if (!adecomp->memory_allocation_failed()) {
if(levwava == 6) {
edge = 1;
}
if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 0 && isdenoisL)) {
if (settings->verbose) {
printf("denoise standard a \n");
}
WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
} else if (cp.noiseena && ((cp.chromfi > 0.f && cp.chromco >= 0.f) && cp.quamet == 1 && isdenoisL)){
if (settings->verbose) {
printf("denoise bishrink a \n");
}
WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
}
Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels);
WaveletcontAllAB(labco, varhue, varchro, *adecomp, wavblcurve, waOpacityCurveW, cp, true, skip, meanab, sigmaab);
adecomp->reconstruct(labco->data + datalen, cp.strength);
}
}
int levwavb = levwav;
if (!exblurab && cp.chrores == 0.f && cp.blurcres == 0.f && !cp.noiseena && !cp.tonemap && !cp.resena && !cp.chromena && !cp.toningena && !cp.finena && !cp.edgeena && params->wavelet.CLmethod == "all" && !cp.cbena) { // no processing of residual ab => we probably can reduce the number of levels
while (levwavb > 0 && !cp.diag && (((cp.CHmet == 2 && (cp.chro == 0.f || cp.mul[levwavb - 1] == 0.f)) || (cp.CHmet != 2 && (levwavb == 10 || (!cp.curv || cp.mulC[levwavb - 1] == 0.f))))) && (!cp.opaBY || levwavb == 10 || (cp.opaBY && cp.mulopaBY[levwavb - 1] == 0.f)) && ((levwavb == 10 || (cp.CHSLmet == 1 && cp.mulC[levwavb - 1] == 0.f)))) {
levwavb--;
}
}
if (cp.chromfi > 0.f || cp.chromco > 0.f) {
if (levwavb < 7) {
levwavb = 7;
}
}
levwavb = rtengine::min(maxlevelcrop, levwavb);
levwavb = rtengine::min(maxlev2, levwavb);
levwavb = rtengine::min(levwav, levwavb);
if (settings->verbose) {
printf("Leval decomp b=%i\n", levwavb);
}
if (levwavb > 0) {
const std::unique_ptr<wavelet_decomposition> bdecomp(new wavelet_decomposition(labco->data + 2 * datalen, labco->W, labco->H, levwavb, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if(levwavb == 6) {
edge = 1;
}
if (!bdecomp->memory_allocation_failed()) {
// if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.chromco < 2.f )) {
if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 0 && isdenoisL)) {
WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
if (settings->verbose) {
printf("Denoise standard b\n");
}
} else if (cp.noiseena && ((cp.chromfi > 0.f && cp.chromco >= 0.f) && cp.quamet == 1 && isdenoisL)){
WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
if (settings->verbose) {
printf("Denoise bishrink b\n");
}
}
Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels);
WaveletcontAllAB(labco, varhue, varchro, *bdecomp, wavblcurve, waOpacityCurveW, cp, false, skip, meanab, sigmaab);
bdecomp->reconstruct(labco->data + 2 * datalen, cp.strength);
}
}
} else {// a and b
int levwavab = levwav;
if (cp.chromfi > 0.f || cp.chromco > 0.f) {
if (levwavab < 7) {
levwavab = 7;
}
}
if (levwavab > 0) {
const std::unique_ptr<wavelet_decomposition> adecomp(new wavelet_decomposition(labco->data + datalen, labco->W, labco->H, levwavab, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
const std::unique_ptr<wavelet_decomposition> bdecomp(new wavelet_decomposition(labco->data + 2 * datalen, labco->W, labco->H, levwavab, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if (!adecomp->memory_allocation_failed() && !bdecomp->memory_allocation_failed()) {
if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 0 && isdenoisL)) {
WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
if (settings->verbose) {
printf("Denoise standard ab\n");
}
} else if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 1 && isdenoisL)) {
WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1);
if (settings->verbose) {
printf("Denoise bishrink ab\n");
}
}
Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels);
WaveletcontAllAB(labco, varhue, varchro, *adecomp, wavblcurve, waOpacityCurveW, cp, true, skip, meanab, sigmaab);
if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 0 && isdenoisL)) {
WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
} else if(cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.quamet == 1 && isdenoisL)) {
WaveletDenoiseAll_BiShrinkAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1);
}
Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels);
WaveletcontAllAB(labco, varhue, varchro, *bdecomp, wavblcurve, waOpacityCurveW, cp, false, skip, meanab, sigmaab);
WaveletAandBAllAB(*adecomp, *bdecomp, cp, hhCurve, hhutili);
adecomp->reconstruct(labco->data + datalen, cp.strength);
bdecomp->reconstruct(labco->data + 2 * datalen, cp.strength);
}
}
}
delete[] noisevarchrom;
if (hhCurve) {
delete hhCurve;
}
if (usechrom) {
Ldecomp->reconstruct(labco->data, cp.strength);
}
}
}
if (numtiles > 1 || (numtiles == 1 /*&& cp.avoi*/)) { //in all case since I add contrast curve
//calculate mask for feathering output tile overlaps
float Vmask[height + overlap] ALIGNED16;
float Hmask[width + overlap] ALIGNED16;
if (numtiles > 1) {
for (int i = 0; i < height; i++) {
Vmask[i] = 1;
}
for (int j = 0; j < width; j++) {
Hmask[j] = 1;
}
for (int i = 0; i < overlap; i++) {
float mask = SQR(sin((rtengine::RT_PI * i) / (2 * overlap)));
if (tiletop > 0) {
Vmask[i] = mask;
}
if (tilebottom < imheight) {
Vmask[height - i] = mask;
}
if (tileleft > 0) {
Hmask[i] = mask;
}
if (tileright < imwidth) {
Hmask[width - i] = mask;
}
}
}
bool highlight = params->toneCurve.hrenabled;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = tiletop; i < tilebottom; i++) {
const int i1 = i - tiletop;
float L, a, b;
#ifdef __SSE2__
const int rowWidth = tileright - tileleft;
float atan2Buffer[rowWidth] ALIGNED64;
float chprovBuffer[rowWidth] ALIGNED64;
float xBuffer[rowWidth] ALIGNED64;
float yBuffer[rowWidth] ALIGNED64;
if (cp.avoi) {
int col = 0;
const vfloat onev = F2V(1.f);
const vfloat c327d68v = F2V(327.68f);
for (; col < rowWidth - 3; col += 4) {
const vfloat av = LVFU(labco->a[i1][col]);
const vfloat bv = LVFU(labco->b[i1][col]);
STVF(atan2Buffer[col], xatan2f(bv, av));
const vfloat cv = vsqrtf(SQRV(av) + SQRV(bv));
vfloat yv = av / cv;
vfloat xv = bv / cv;
const vmask xyMask = vmaskf_eq(ZEROV, cv);
yv = vself(xyMask, onev, yv);
xv = vselfnotzero(xyMask, xv);
STVF(yBuffer[col], yv);
STVF(xBuffer[col], xv);
STVF(chprovBuffer[col], cv / c327d68v);
}
for (; col < rowWidth; col++) {
const float la = labco->a[i1][col];
const float lb = labco->b[i1][col];
atan2Buffer[col] = xatan2f(lb, la);
const float Chprov1 = sqrtf(SQR(la) + SQR(lb));
yBuffer[col] = (Chprov1 == 0.f) ? 1.f : la / Chprov1;
xBuffer[col] = (Chprov1 == 0.f) ? 0.f : lb / Chprov1;
chprovBuffer[col] = Chprov1 / 327.68f;
}
}
#endif
for (int j = tileleft; j < tileright; j++) {
const int j1 = j - tileleft;
if (cp.avoi) { //Gamut and Munsell
#ifdef __SSE2__
float HH = atan2Buffer[j1];
float Chprov1 = chprovBuffer[j1];
float2 sincosv;
sincosv.y = yBuffer[j1];
sincosv.x = xBuffer[j1];
#else
a = labco->a[i1][j1];
b = labco->b[i1][j1];
float HH = xatan2f(b, a);
float Chprov1 = sqrtf(SQR(a) + SQR(b));
float2 sincosv;
sincosv.y = (Chprov1 == 0.0f) ? 1.f : a / (Chprov1);
sincosv.x = (Chprov1 == 0.0f) ? 0.f : b / (Chprov1);
Chprov1 /= 327.68f;
#endif
const float Lin = labco->L[i1][j1];
if (wavclCurve && cp.finena) {
labco->L[i1][j1] = (0.5f * Lin + 1.5f * wavclCurve[Lin]) / 2.f; //apply contrast curve
}
L = labco->L[i1][j1];
float Lprov1 = L / 327.68f;
float Lprov2 = Lold[i][j] / 327.68f;
float memChprov = varchro[i1][j1];
float R, G, B;
Color::gamutLchonly(HH, sincosv, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f);
L = Lprov1 * 327.68f;
a = 327.68f * Chprov1 * sincosv.y; //gamut
b = 327.68f * Chprov1 * sincosv.x; //gamut
float correctionHue = 0.0f; // Munsell's correction
float correctlum = 0.0f;
Lprov1 = L / 327.68f;
const float Chprov = sqrtf(SQR(a) + SQR(b)) / 327.68f;
Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum);
if (correctionHue != 0.f || correctlum != 0.f) { // only calculate sin and cos if HH changed
if (std::fabs(correctionHue) < 0.015f) {
HH += correctlum; // correct only if correct Munsell chroma very little.
}
sincosv = xsincosf(HH + correctionHue);
}
a = 327.68f * Chprov * sincosv.y; // apply Munsell
b = 327.68f * Chprov * sincosv.x; // apply Munsell
} else {//general case
L = labco->L[i1][j1];
const float Lin = std::max(0.f, L);
if (wavclCurve && cp.finena) {
labco->L[i1][j1] = (0.5f * Lin + 1.5f * wavclCurve[Lin]) / 2.f; //apply contrast curve
}
L = labco->L[i1][j1];
a = labco->a[i1][j1];
b = labco->b[i1][j1];
}
if (numtiles > 1) {
float factor = Vmask[i1] * Hmask[j1];
if(L <= 0.f) {
L= 1.f;
}
dsttmp->L[i][j] += factor * L;
dsttmp->a[i][j] += factor * a;
dsttmp->b[i][j] += factor * b;
} else {
if(L <= 0.f) {
L= 1.f;
}
dsttmp->L[i][j] = L;
dsttmp->a[i][j] = a;
dsttmp->b[i][j] = b;
}
}
}
}
if (LoldBuffer != nullptr) {
delete [] LoldBuffer;
delete [] Lold;
}
if (numtiles > 1) {
delete labco;
}
}
}
for (int i = 0; i < tileheight; i++)
if (varhue[i] != nullptr) {
delete [] varhue[i];
}
delete [] varhue;
}
#ifdef _OPENMP
omp_set_nested(oldNested);
#endif
if (numtiles != 1) {
dst->CopyFrom(dsttmp);
delete dsttmp;
}
if (waparams.softradend > 0.f && cp.finena) {
float guid = waparams.softradend;
float strend = waparams.strend;
float detend = (float) waparams.detend;
float thrend = 0.01f * (float) waparams.thrend;
int ww = lab->W;
int hh = lab->H;
array2D<float> LL(ww, hh);
array2D<float> LLbef(ww, hh);
array2D<float> LAbef(ww, hh);
array2D<float> LBbef(ww, hh);
array2D<float> guide(ww, hh);
const float blend = LIM01(float(strend) / 100.f);
float mean[10];
float meanN[10];
float sigma[10];
float sigmaN[10];
float MaxP[10];
float MaxN[10];
float meang[10];
float meanNg[10];
float sigmag[10];
float sigmaNg[10];
float MaxPg[10];
float MaxNg[10];
bool multiTh = false;
#ifdef _OPENMP
if (numthreads > 1) {
multiTh = true;
}
#pragma omp parallel for
#endif
for (int y = 0; y < hh; y++) {
for (int x = 0; x < ww; x++) {
LL[y][x] = dst->L[y][x];
LLbef[y][x] = dst->L[y][x];
LAbef[y][x] = dst->a[y][x];
LBbef[y][x] = dst->b[y][x];
float ll = LL[y][x] / 32768.f;
guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f);
}
}
array2D<float> iL(ww, hh, LL, 0);
int r = rtengine::max(int(guid / skip), 1);
const float epsil = 0.001f * std::pow(2, - detend);
rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiTh);
//take Hue to modulate LL
//LL in function of LLbef and Labef Lbbef
if(wavguidutili) {
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < hh ; y++) {
for (int x = 0; x < ww; x++) {
float hueG = xatan2f(LBbef[y][x], LAbef[y][x]);
float valparam = 1.f * (static_cast<float>(wavguidCurve->getVal(Color::huelab_to_huehsv2(hueG))) - 0.5f);
LL[y][x] = LLbef[y][x] + (LL[y][x] - LLbef[y][x]) * (1.f + valparam);
}
}
}
//end hue
if (thrend > 0.f) {
//2 decomposition LL after guidefilter and dst before (perhaps dst no need)
const std::unique_ptr<wavelet_decomposition> LdecompLL(new wavelet_decomposition(LL[0], ww, hh, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
const std::unique_ptr<wavelet_decomposition> Ldecompdst(new wavelet_decomposition(dst->L[0], ww, hh, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen));
if (!LdecompLL->memory_allocation_failed() && !Ldecompdst->memory_allocation_failed()) {
Evaluate2(*LdecompLL, meang, meanNg, sigmag, sigmaNg, MaxPg, MaxNg, wavNestedLevels);
Evaluate2(*Ldecompdst, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavNestedLevels);
constexpr float sig = 2.f;
// original code for variable sig
float k = sig;
// cppcheck-suppress knownConditionTrueFalse
if (sig > 1.f) {
k = SQR(sig);
}
float thr = 0.f;
if (thrend < 0.02f) thr = 0.5f;
else if (thrend < 0.1f) thr = 0.2f;
else thr = 0.f;
FlatCurve wavguid({
FCT_MinMaxCPoints,
0, 1, 0.35, 0.35,thrend, 1.0, 0.35, 0.35, thrend + 0.01f, thr, 0.35, 0.35, 1, thr, 0.35, 0.35
});
for (int dir = 1; dir < 4; dir++) {
for (int level = 0; level < levwavL-1; level++) {
const int Wlvl_L = LdecompLL->level_W(level);
const int Hlvl_L = LdecompLL->level_H(level);
float* const* WavCoeffs_L = LdecompLL->level_coeffs(level);//first decomp denoised
float* const* WavCoeffs_L2 = Ldecompdst->level_coeffs(level);//second decomp before denoise
if (settings->verbose) {
printf("level=%i mean=%.0f meanden=%.0f sigma=%.0f sigmaden=%.0f Max=%.0f Maxden=%.0f\n", level, mean[level], meang[level], sigma[level], sigmag[level],MaxP[level], MaxPg[level]);
}
//find local contrast
const float tempmean = 0.3f * mean[level] + 0.7f * meang[level];
const float tempsig = 0.3f * sigma[level] + 0.7f * sigmag[level];
const float tempmax = 0.3f * MaxP[level] + 0.7f * MaxPg[level];
if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { //curve
constexpr float insigma = 0.666f; //SD
const float logmax = log(tempmax); //log Max
const float rapX = (tempmean + sig * tempsig) / tempmax; //rapport between sD / max
const float inx = log(insigma);
const float iny = log(rapX);
const float rap = inx / iny; //koef
const float asig = 0.166f / (tempsig * sig);
const float bsig = 0.5f - asig * tempmean;
const float amean = 1.f / tempmean;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, Wlvl_L * 16) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < Wlvl_L * Hlvl_L; i++) {
float absciss;
const float tempwav = std::fabs(0.7f * WavCoeffs_L[dir][i] + 0.3f * WavCoeffs_L2[dir][i]);
if (tempwav >= tempmean + sig * tempsig) { //for max
const float vald = (xlogf(tempwav) - logmax) * rap;
absciss = xexpf(vald);
} else if (tempwav >= tempmean) {
absciss = asig * tempwav + bsig;
} else {
absciss = amean * tempwav;
if (sig == 2.f) { // for sig = 2.f we can use a faster calculation because the exponent in this case is 0.25
absciss = 0.5f * std::sqrt(std::sqrt(absciss));
} else { // original code for variable sig
absciss = 0.5f * pow_F(absciss, 1.f / k);
}
}
float kc = wavguid.getVal(absciss) - 1.f;
kc = kc < 0.f ? -SQR(kc) : kc; // approximation to simulate sliders denoise
const float reduceeffect = kc <= 0.f ? 1.f : 1.2f;//1.2 allows to increase denoise (not used)
const float kinterm = rtengine::max(1.f + reduceeffect * kc, 0.f);
WavCoeffs_L[dir][i] = intp(kinterm, WavCoeffs_L[dir][i], WavCoeffs_L2[dir][i]); // interpolate using kinterm
}
}
}
}
LdecompLL->reconstruct(LL[0], cp.strength);
}
}
//end local contrast
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < hh ; y++) {
for (int x = 0; x < ww; x++) {
LL[y][x] = intp(blend, LL[y][x] , iL[y][x]);
dst->L[y][x] = LL[y][x];
}
}
}
}
void ImProcFunctions::Aver(const float* RESTRICT DataList, int datalen, float &averagePlus, float &averageNeg, float &max, float &min, int numThreads)
{
//find absolute mean
int countP = 0, countN = 0;
double averaP = 0.0, averaN = 0.0; // use double precision for large summations
constexpr float thres = 32.7f;//different from zero to take into account only data large enough 32.7 = 0.1 in range 0..100 very low value
max = 0.f;
min = RT_INFINITY_F;
#ifdef _OPENMP
#pragma omp parallel num_threads(numThreads) if (numThreads>1)
#endif
{
float lmax = 0.f, lmin = 0.f;
#ifdef _OPENMP
#pragma omp for reduction(+:averaP,averaN,countP,countN) nowait
#endif
for (int i = 0; i < datalen; i++) {
if (DataList[i] >= thres) {
averaP += static_cast<double>(DataList[i]);
lmax = rtengine::max(lmax, DataList[i]);
countP++;
} else if (DataList[i] < -thres) {
averaN += static_cast<double>(DataList[i]);
lmin = rtengine::min(lmin, DataList[i]);
countN++;
}
}
#ifdef _OPENMP
#pragma omp critical
#endif
{
max = rtengine::max(max, lmax);
min = rtengine::min(min, lmin);
}
}
if (countP > 0) {
averagePlus = averaP / countP;
} else {
averagePlus = 0;
}
if (countN > 0) {
averageNeg = averaN / countN;
} else {
averageNeg = 0;
}
}
void ImProcFunctions::Sigma(const float* RESTRICT DataList, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg, int numThreads)
{
int countP = 0, countN = 0;
double variP = 0.0, variN = 0.0; // use double precision for large summations
float thres = 32.7f;//different from zero to take into account only data large enough 32.7 = 0.1 in range 0..100
#ifdef _OPENMP
#pragma omp parallel for reduction(+:variP,variN,countP,countN) num_threads(numThreads) if (numThreads>1)
#endif
for (int i = 0; i < datalen; i++) {
if (DataList[i] >= thres) {
variP += static_cast<double>(SQR(DataList[i] - averagePlus));
countP++;
} else if (DataList[i] <= -thres) {
variN += static_cast<double>(SQR(DataList[i] - averageNeg));
countN++;
}
}
if (countP > 0) {
sigmaPlus = sqrt(variP / countP);
} else {
sigmaPlus = 0;
}
if (countN > 0) {
sigmaNeg = sqrt(variN / countN);
} else {
sigmaNeg = 0;
}
}
void ImProcFunctions::Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads)
{
//StopWatch Stop1("Evaluate2");
int maxlvl = WaveletCoeffs_L.maxlevel();
for (int lvl = 0; lvl < maxlvl; lvl++) {
int Wlvl_L = WaveletCoeffs_L.level_W(lvl);
int Hlvl_L = WaveletCoeffs_L.level_H(lvl);
const float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
Eval2(WavCoeffs_L, lvl, Wlvl_L, Hlvl_L, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads);
}
}
void ImProcFunctions::calceffect(int level, float *mean, float *sigma, float *mea, float effect, float offs)
{
float rap = 0.f;
float sig = 1.f;
if (effect < 1.f) {
sig = effect;
}
if (effect <= 1.f) {
rap = offs * mean[level] - sig * sigma[level];
}
if (rap > 0.f) {
mea[0] = rap;
} else {
mea[0] = mean[level] / 6.f;
}
rap = 0.f;
if (effect <= 1.f) {
rap = offs * mean[level] - 0.5f * sig * sigma[level];
}
if (rap > 0.f) {
mea[1] = rap;
} else {
mea[1] = mean[level] / 4.f;
}
rap = 0.f;
if (effect <= 1.f) {
rap = offs * mean[level] - 0.2f * sig * sigma[level];
}
if (rap > 0.f) {
mea[2] = rap;
} else {
mea[2] = mean[level] / 2.f;
}
mea[3] = offs * mean[level]; // 50% data
mea[4] = offs * mean[level] + effect * sigma[level] / 2.f;
mea[5] = offs * mean[level] + effect * sigma[level]; //66%
mea[6] = offs * mean[level] + effect * 1.2f * sigma[level];
mea[7] = offs * mean[level] + effect * 1.5f * sigma[level]; //
mea[8] = offs * mean[level] + effect * 2.f * sigma[level]; //95%
mea[9] = offs * mean[level] + effect * 2.5f * sigma[level]; //99%
}
void ImProcFunctions::Eval2(const float* const* WavCoeffs_L, int level, int W_L, int H_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads)
{
float avLP[4], avLN[4];
float maxL[4], minL[4];
float sigP[4], sigN[4];
float AvL, AvN, SL, SN, maxLP, maxLN;
for (int dir = 1; dir < 4; dir++) {
Aver(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], maxL[dir], minL[dir], numThreads);
Sigma(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], sigP[dir], sigN[dir], numThreads);
}
AvL = 0.f;
AvN = 0.f;
SL = 0.f;
SN = 0.f;
maxLP = 0.f;
maxLN = 0.f;
for (int dir = 1; dir < 4; dir++) {
AvL += avLP[dir];
AvN += avLN[dir];
SL += sigP[dir];
SN += sigN[dir];
maxLP += maxL[dir];
maxLN += minL[dir];
}
AvL /= 3;
AvN /= 3;
SL /= 3;
SN /= 3;
maxLP /= 3;
maxLN /= 3;
mean[level] = AvL;
meanN[level] = AvN;
sigma[level] = SL;
sigmaN[level] = SN;
MaxP[level] = maxLP;
MaxN[level] = maxLN;
}
void ImProcFunctions::CompressDR(float *Source, int W_L, int H_L, float Compression, float DetailBoost)
{
const int n = W_L * H_L;
float exponent;
if (DetailBoost > 0.f && DetailBoost < 0.05f) {
float betemp = expf(-(2.f - DetailBoost + 0.694f)) - 1.f; //0.694 = log(2)
exponent = 1.2f * xlogf(-betemp);
exponent /= 20.f;
} else if (DetailBoost >= 0.05f && DetailBoost < 0.25f) {
float betemp = expf(-(2.f - DetailBoost + 0.694f)) - 1.f; //0.694 = log(2)
exponent = 1.2f * xlogf(-betemp);
exponent /= (-75.f * DetailBoost + 23.75f);
} else if (DetailBoost >= 0.25f) {
float betemp = expf(-(2.f - DetailBoost + 0.694f)) - 1.f; //0.694 = log(2)
exponent = 1.2f * xlogf(-betemp);
exponent /= (-2.f * DetailBoost + 5.5f);
} else {
exponent = (Compression - 1.0f) / 20.f;
}
exponent += 1.f;
// now calculate Source = pow(Source, exponent)
#ifdef __SSE2__
#ifdef _OPENMP
#pragma omp parallel
#endif
{
const vfloat exponentv = F2V(exponent);
#ifdef _OPENMP
#pragma omp for
#endif
for (int i = 0; i < n - 3; i += 4) {
STVFU(Source[i], xexpf(xlogf(LVFU(Source[i])) * exponentv));
}
}
for (int i = n - (n % 4); i < n; i++) {
Source[i] = xexpf(xlogf(Source[i]) * exponent);
}
#else
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < n; i++) {
Source[i] = xexpf(xlogf(Source[i]) * exponent);
}
#endif
}
void ImProcFunctions::ContrastResid(float * WavCoeffs_L0, const cont_params &cp, int W_L, int H_L, float max0)
{
const float stren = cp.tmstrength;
const float gamm = params->wavelet.gamma;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L0[i] *= (gamm / max0);
}
const float Compression = std::exp(-stren); //This modification turns numbers symmetric around 0 into exponents.
const float DetailBoost = std::max(stren, 0.f); //Go with effect of exponent only if uncompressing.
CompressDR(WavCoeffs_L0, W_L, H_L, Compression, DetailBoost);
max0 /= gamm;
#ifdef _OPENMP
#pragma omp parallel for // removed schedule(dynamic,10)
#endif
for (int ii = 0; ii < W_L * H_L; ii++) {
WavCoeffs_L0[ii] *= max0;
}
}
void ImProcFunctions::EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, const cont_params& cp, int W_L, int H_L, float max0)
{
const float stren = cp.tmstrength;
const float edgest = params->wavelet.edgs;
const float sca = params->wavelet.scale;
const float gamm = params->wavelet.gamma;
constexpr int rew = 0; //params->epd.reweightingIterates;
EdgePreservingDecomposition epd2(W_L, H_L);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L0[i] *= (gamm / max0);
}
const float Compression = std::exp(-stren); //This modification turns numbers symmetric around 0 into exponents.
const float DetailBoost = std::max(stren, 0.f); //Go with effect of exponent only if uncompressing.
//Auto select number of iterates. Note that p->EdgeStopping = 0 makes a Gaussian blur.
if (Iterates == 0) {
Iterates = (unsigned int)(edgest * 15.0f);
}
epd2.CompressDynamicRange(WavCoeffs_L0, sca / skip, edgest, Compression, DetailBoost, Iterates, rew);
max0 /= gamm;
//Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping.
#ifdef _OPENMP
#pragma omp parallel for // removed schedule(dynamic,10)
#endif
for (int ii = 0; ii < W_L * H_L; ii++) {
WavCoeffs_L0[ii] *= max0;
}
}
void ImProcFunctions::WaveletcontAllLfinal(wavelet_decomposition& WaveletCoeffs_L, const cont_params &cp, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL)
{
int maxlvl = WaveletCoeffs_L.maxlevel();
float* WavCoeffs_L0 = WaveletCoeffs_L.get_coeff0();
for (int dir = 1; dir < 4; dir++) {
for (int lvl = 0; lvl < maxlvl; lvl++) {
int Wlvl_L = WaveletCoeffs_L.level_W(lvl);
int Hlvl_L = WaveletCoeffs_L.level_H(lvl);
float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
finalContAllL(WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, mean, sigma, MaxP, waOpacityCurveWL);
}
}
}
void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_L, const Wavblcurve & wavblcurve,
struct cont_params &cp, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili)
{
// BENCHFUN
const int maxlvl = WaveletCoeffs_L.maxlevel();
const int W_L = WaveletCoeffs_L.level_W(0);
const int H_L = WaveletCoeffs_L.level_H(0);
float* WavCoeffs_L0 = WaveletCoeffs_L.get_coeff0();
const float contrast = cp.contrast;
double avedbl = 0.0; // use double precision for large summations
float max0 = 0.f;
if (contrast != 0.f || (cp.tonemap && cp.resena)) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step
#ifdef _OPENMP
#pragma omp parallel for reduction(+:avedbl) reduction(max:max0) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < W_L * H_L; i++) {
avedbl += static_cast<double>(WavCoeffs_L0[i]);
max0 = std::max(WavCoeffs_L0[i], max0);
}
}
//tone mapping
if (cp.tonemap && cp.contmet == 2 && cp.resena) {
//iterate = 5
EPDToneMapResid(WavCoeffs_L0, 0, skip, cp, W_L, H_L, max0);
}
//end tonemapping
max0 /= 327.68f;
const float ave = avedbl / (W_L * H_L);
const float avg = LIM01(ave / 32768.f);
const double contreal = 0.6 * contrast;
DiagonalCurve resid_contrast({
DCT_NURBS,
0, 0,
avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0),
avg + (1. - avg) * (0.6 - contreal / 250.0), avg + (1. - avg) * (0.6 + contreal / 250.0),
1, 1
});
if (contrast != 0.f && cp.resena && max0 > 0.f) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step
#ifdef _OPENMP
#pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < W_L * H_L; i++) {
float buf = LIM01(WavCoeffs_L0[i] / 32768.f);
buf = resid_contrast.getVal(buf);
buf *= 32768.f;
WavCoeffs_L0[i] = buf;
}
}
if (cp.tonemap && cp.contmet == 1 && cp.resena) {
const float maxp = max0 * 256.f;
ContrastResid(WavCoeffs_L0, cp, W_L, H_L, maxp);
}
// if ((cp.conres >= 0.f || cp.conresH >= 0.f) && cp.resena && !cp.oldsh) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step
if ((cp.conres >= 0.f || cp.conresH >= 0.f) && cp.resena) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step
const std::unique_ptr<LabImage> temp(new LabImage(W_L, H_L));
#ifdef _OPENMP
#pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < H_L; i++) {
for (int j = 0; j < W_L; j++) {
temp->L[i][j] = WavCoeffs_L0[i * W_L + j];
}
}
ImProcFunctions::shadowsHighlights(temp.get(), true, 1, cp.conresH, cp.conres, cp.radius, skip, cp.thH, cp.th);
#ifdef _OPENMP
#pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < H_L; i++) {
for (int j = 0; j < W_L; j++) {
WavCoeffs_L0[i * W_L + j] = temp->L[i][j];
}
}
}
// if ((cp.conres != 0.f || cp.conresH != 0.f) && cp.resena && cp.oldsh) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step
if ((cp.conres < 0.f || cp.conresH < 0.f) && cp.resena) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < W_L * H_L; i++) {
float LL = WavCoeffs_L0[i];
float LL100 = LL / 327.68f;
float tran = 5.f;//transition
//shadow
if (cp.th > (100.f - tran)) {
tran = 100.f - cp.th;
}
if (LL100 < cp.th) {
constexpr float alp = 3.f; //increase contrast sahdow in lowlights between 1 and ??
float aalp = (1.f - alp) / cp.th; //no changes for LL100 = cp.th
float kk = aalp * LL100 + alp;
WavCoeffs_L0[i] *= (1.f + kk * cp.conres / 200.f);
} else if (LL100 < cp.th + tran) {
float ath = -cp.conres / tran;
float bth = cp.conres - ath * cp.th;
WavCoeffs_L0[i] *= (1.f + (LL100 * ath + bth) / 200.f);
}
//highlight
tran = 5.f;
if (cp.thH < (tran)) {
tran = cp.thH;
}
if (LL100 > cp.thH) {
WavCoeffs_L0[i] *= (1.f + cp.conresH / 200.f);
} else if (LL100 > (cp.thH - tran)) {
float athH = cp.conresH / tran;
float bthH = cp.conresH - athH * cp.thH;
WavCoeffs_L0[i] *= (1.f + (LL100 * athH + bthH) / 200.f);
}
}
}
//Blur luma
if (cp.blurres != 0.f && cp.resena) {
int minWL = min(W_L, H_L);
//printf("skip=%i WL=%i HL=%i min=%i\n", skip, W_L, H_L, minWL);
if (minWL > 140) { //disabled if too low windows
constexpr float k = 0.5f;
float rad = k * cp.blurres / skip;
float * bef = new float[W_L * H_L];
float * aft = new float[W_L * H_L];
for (int i = 0; i < H_L * W_L; i++) {
bef[i] = WavCoeffs_L0[i];
}
boxblur(bef, aft, rad, W_L, H_L, false);
for (int i = 0; i < H_L * W_L; i++) {
WavCoeffs_L0[i] = aft[i];
}
delete[] bef;
delete[] aft;
}
}
float *koeLi[12];
const std::unique_ptr<float[]> koeLibuffer(new float[12 * H_L * W_L]());
for (int i = 0; i < 12; i++) {
koeLi[i] = &koeLibuffer[i * W_L * H_L];
}
float maxkoeLi[12] = {0.f};
#ifdef _OPENMP
#pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
{
//enabled Lipschitz..replace simple by complex edge detection
// I found this concept on the web (doctoral thesis on medical Imaging)
// I was inspired by the principle of Canny and Lipschitz (continuity and derivability)
// I adapted the principle but have profoundly changed the algorithm
// One can 1) change all parameters and found good parameters;
//one can also change in calckoe
constexpr float edd = 3.f;
constexpr float eddlow = 15.f;
float eddlipinfl = 0.005f * cp.edgsens + 0.4f;
float eddlipampl = 1.f + cp.edgampl / 50.f;
if (cp.detectedge && cp.val > 0) { //enabled Lipschitz control...more memory..more time...
const std::unique_ptr<float[]> tmCBuffer(new float[H_L * W_L]);
float *tmC[H_L];
for (int i = 0; i < H_L; i++) {
tmC[i] = &tmCBuffer[i * W_L];
}
float gradw = cp.eddet;
float tloww = cp.eddetthr;
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2)
#endif
for (int lvl = 0; lvl < 4; lvl++) {
for (int dir = 1; dir < 4; dir++) {
const float* const* WavCoeffs_LL = WaveletCoeffs_L.level_coeffs(lvl);
float tempkoeli = 0.f;
calckoe (WavCoeffs_LL[dir], gradw, tloww, koeLi[lvl * 3 + dir - 1], lvl, W_L, H_L, edd, tempkoeli, tmC);
maxkoeLi[lvl * 3 + dir - 1] = tempkoeli ;
// return convolution KoeLi and maxkoeLi of level 0 1 2 3 and Dir Horiz, Vert, Diag
}
}
float aamp = 1.f + cp.eddetthrHi / 100.f;
for (int lvl = 0; lvl < 4; lvl++) {
#ifdef _OPENMP
#pragma omp for schedule(dynamic,16)
#endif
for (int i = 1; i < H_L - 1; i++) {
for (int j = 1; j < W_L - 1; j++) {
//treatment of koeLi and maxkoeLi
float interm = 0.f;
if (cp.lip3 && cp.lipp) {
// comparison between pixel and neighbours
const auto neigh = cp.neigh == 1;
const auto kneigh = neigh ? 28.f : 38.f;
const auto somm = neigh ? 40.f : 50.f;
for (int dir = 1; dir < 4; dir++) { //neighbours proxi
koeLi[lvl * 3 + dir - 1][i * W_L + j] = (kneigh * koeLi[lvl * 3 + dir - 1][i * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j]
+ 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j + 1] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j - 1]
+ koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j + 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j + 1]) / somm;
}
}
for (int dir = 1; dir < 4; dir++) {
//here I evaluate combinaison of vert / diag / horiz...we are with multiplicators of the signal
interm += SQR(koeLi[lvl * 3 + dir - 1][i * W_L + j]);
}
interm = sqrt(interm);
// interm /= 1.732f;//interm = pseudo variance koeLi
interm *= 0.57736721f;
float kampli = 1.f;
float eps = 0.0001f;
// I think this double ratio (alph, beta) is better than arctg
float alph = koeLi[lvl * 3][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between horizontal and vertical
float beta = koeLi[lvl * 3 + 2][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between diagonal and horizontal
float alipinfl = (eddlipampl - 1.f) / (1.f - eddlipinfl);
float blipinfl = eddlipampl - alipinfl;
//alph evaluate the direction of the gradient regularity Lipschitz
// if = 1 we are on an edge
// if 0 we are not
// we can change and use log..or Arctg but why ?? we can change if need ...
//Liamp=1 for eddlipinfl
//liamp > 1 for alp >eddlipinfl and alph < 1
//Liamp < 1 for alp < eddlipinfl and alph > 0
if (alph > 1.f) {
alph = 1.f / alph;
}
if (beta > 1.f) {
beta = 1.f / beta;
}
//take into account diagonal
//if in same value OK
//if not no edge or reduction
float bet = 1.f;
if (alph > eddlipinfl && beta < 0.85f * eddlipinfl) { //0.85 arbitrary value ==> eliminate from edge if H V D too different
bet = beta;
}
float AmpLip = 1.f;
if (alph > eddlipinfl) {
AmpLip = alipinfl * alph + blipinfl; //If beta low reduce kampli
kampli = SQR(bet) * AmpLip * aamp;
} else {
AmpLip = (1.f / eddlipinfl) * SQR(SQR(alph * bet)); //Strong Reduce if beta low
kampli = AmpLip / aamp;
}
interm *= kampli;
if (interm < cp.eddetthr / eddlow) {
interm = 0.01f; //eliminate too low values
}
//we can change this part of algo==> not equal but ponderate
koeLi[lvl * 3][i * W_L + j] = koeLi[lvl * 3 + 1][i * W_L + j] = koeLi[lvl * 3 + 2][i * W_L + j] = interm; //new value
//here KoeLi contains values where gradient is high and coef high, and eliminate low values...
}
}
}
// end
}
bool wavcurvecomp = false;//not enable if 0.75
if (wavblcurve) {
for (int i = 0; i < 500; i++) {
if (wavblcurve[i] != 0.) {
wavcurvecomp = true;
break;
}
}
}
std::unique_ptr<float[]> aft;
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2)
#endif
for (int dir = 1; dir < 4; dir++) {
for (int lvl = 0; lvl < maxlvl; lvl++) {
int Wlvl_L = WaveletCoeffs_L.level_W(lvl);
int Hlvl_L = WaveletCoeffs_L.level_H(lvl);
float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
ContAllL(koeLi, maxkoeLi[lvl * 3 + dir - 1], true, maxlvl, labco, varhue, varchrom, WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveSH, ChCurve, Chutili);
if (std::min(Wlvl_L, Hlvl_L) > 180) {
if (wavblcurve && wavcurvecomp && cp.blena) {
// printf("Blur level L\n");
float mea[10];
const float effect = cp.bluwav;
constexpr float offs = 1.f;
calceffect(lvl, mean, sigma, mea, effect, offs);
float lutFactor;
const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.6f, 0.4f, 0.2f, 0.1f, 0.01f};
const auto meaLut = buildMeaLut(inVals, mea, lutFactor);
if (!aft.get()) {
aft.reset(new float[Wlvl_L * Hlvl_L]);
}
//blur level
const float klev = wavblcurve[lvl * 55.5f] * 80.f / skip;
auto WavL = WavCoeffs_L[dir];
boxblur(WavL, aft.get(), klev, Wlvl_L, Hlvl_L, false);
int co = 0;
#ifdef __SSE2__
const vfloat lutFactorv = F2V(lutFactor);
for (; co < Hlvl_L * Wlvl_L - 3; co += 4) {
const vfloat valv = LVFU(WavL[co]);
STVFU(WavL[co], intp((*meaLut)[vabsf(valv) * lutFactorv], LVFU(aft[co]), valv));
}
#endif
for (; co < Hlvl_L * Wlvl_L; co++) {
WavL[co] = intp((*meaLut)[std::fabs(WavL[co]) * lutFactor], aft[co], WavL[co]);
}
}
}
}
}
}
}
void ImProcFunctions::WaveletAandBAllAB(wavelet_decomposition& WaveletCoeffs_a, wavelet_decomposition& WaveletCoeffs_b,
const cont_params &cp, FlatCurve* hhCurve, bool hhutili)
{
// StopWatch Stop1("WaveletAandBAllAB");
if (hhutili && cp.resena) { // H=f(H)
int W_L = WaveletCoeffs_a.level_W(0);
int H_L = WaveletCoeffs_a.level_H(0);
float* WavCoeffs_a0 = WaveletCoeffs_a.get_coeff0();
float* WavCoeffs_b0 = WaveletCoeffs_b.get_coeff0();
#ifdef _OPENMP
#pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
{
#ifdef __SSE2__
float huebuffer[W_L] ALIGNED64;
float chrbuffer[W_L] ALIGNED64;
#endif // __SSE2__
#ifdef _OPENMP
#pragma omp for schedule(dynamic,16)
#endif
for (int i = 0; i < H_L; i++) {
#ifdef __SSE2__
// precalculate hue and chr
int k;
for (k = 0; k < W_L - 3; k += 4) {
const vfloat av = LVFU(WavCoeffs_a0[i * W_L + k]);
const vfloat bv = LVFU(WavCoeffs_b0[i * W_L + k]);
STVF(huebuffer[k], xatan2f(bv, av));
STVF(chrbuffer[k], vsqrtf(SQRV(av) + SQRV(bv)));
}
for (; k < W_L; k++) {
huebuffer[k] = xatan2f(WavCoeffs_b0[i * W_L + k], WavCoeffs_a0[i * W_L + k]);
chrbuffer[k] = sqrtf(SQR(WavCoeffs_b0[i * W_L + k]) + SQR(WavCoeffs_a0[i * W_L + k])) / 327.68f;
}
#endif // __SSE2__
for (int j = 0; j < W_L; j++) {
#ifdef __SSE2__
float hueR = huebuffer[j];
float chR = chrbuffer[j];
#else
float hueR = xatan2f(WavCoeffs_b0[i * W_L + j], WavCoeffs_a0[i * W_L + j]);
float chR = sqrtf(SQR(WavCoeffs_b0[i * W_L + j]) + SQR(WavCoeffs_a0[i * W_L + j]));
#endif
/* if (editID == EUID_WW_HHCurve) {//H pipette
float valpar =Color::huelab_to_huehsv2(hueR);
editWhatever->v(i,j) = valpar;
}
*/
float valparam = (static_cast<float>(hhCurve->getVal(Color::huelab_to_huehsv2(hueR))) - 0.5f) * 1.7f + hueR; //get H=f(H) 1.7 optimisation !
float2 sincosval = xsincosf(valparam);
WavCoeffs_a0[i * W_L + j] = chR * sincosval.y;
WavCoeffs_b0[i * W_L + j] = chR * sincosval.x;
}
}
}
}
}
void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_ab, const Wavblcurve & wavblcurve, const WavOpacityCurveW & waOpacityCurveW,
struct cont_params &cp, const bool useChannelA, int skip, float *meanab, float *sigmaab)
{
//BENCHFUN
int maxlvl = WaveletCoeffs_ab.maxlevel();
int W_L = WaveletCoeffs_ab.level_W(0);
int H_L = WaveletCoeffs_ab.level_H(0);
float* WavCoeffs_ab0 = WaveletCoeffs_ab.get_coeff0();
#ifdef _OPENMP
#pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
{
if (cp.chrores != 0.f && cp.resena) { // cp.chrores == 0.f means all will be multiplied by 1.f, so we can skip the processing of residual
#ifdef _OPENMP
#pragma omp for nowait
#endif
for (int i = 0; i < W_L * H_L; i++) {
const float skyprot = cp.sky;
//chroma
int ii = i / W_L;
int jj = i - ii * W_L;
float modhue = varhue[ii][jj];
float scale = 1.f;
if (skyprot > 0.f) {
if ((modhue < cp.t_ry && modhue > cp.t_ly)) {
scale = (100.f - cp.sky) / 100.1f;
} else if ((modhue >= cp.t_ry && modhue < cp.b_ry)) {
scale = (100.f - cp.sky) / 100.1f;
float ar = (scale - 1.f) / (cp.t_ry - cp.b_ry);
float br = scale - cp.t_ry * ar;
scale = ar * modhue + br;
} else if ((modhue > cp.b_ly && modhue < cp.t_ly)) {
scale = (100.f - cp.sky) / 100.1f;
float al = (scale - 1.f) / (-cp.b_ly + cp.t_ly);
float bl = scale - cp.t_ly * al;
scale = al * modhue + bl;
}
} else if (skyprot < 0.f) {
if ((modhue > cp.t_ry || modhue < cp.t_ly)) {
scale = (100.f + cp.sky) / 100.1f;
}
}
WavCoeffs_ab0[i] *= (1.f + cp.chrores * (scale) / 100.f);
}
}
if (cp.cbena && cp.resena) { //if user select Toning and color balance
#ifdef _OPENMP
#pragma omp for nowait
#endif
for (int i = 0; i < W_L * H_L; i++) {
int ii = i / W_L;
int jj = i - ii * W_L;
float LL = (labco->L[ii * 2][jj * 2]) / 327.68f; //I use labco but I can use also WavCoeffs_L0 (more exact but more memory)
float sca = 1.f; //amplifier - reducter...about 1, but perhaps 0.6 or 1.3
if (useChannelA) { //green red (little magenta)
//transition to avoid artifacts with 6 between 30 to 36 and 63 to 69
float aa = (cp.grmed - cp.grlow) / 6.f;
float bb = cp.grlow - 30.f * aa;
float aaa = (cp.grhigh - cp.grmed) / 6.f;
float bbb = cp.grmed - 63.f * aaa;
if (LL < 30.f) { //shadows
WavCoeffs_ab0[i] += cp.grlow * (sca) * 300.f;
} else if (LL >= 30.f && LL < 36.f) { //transition
float tr = aa * LL + bb;
WavCoeffs_ab0[i] += tr * (sca) * 300.f;
} else if (LL >= 36.f && LL < 63.f) { //midtones
WavCoeffs_ab0[i] += cp.grmed * (sca) * 300.f;
} else if (LL >= 63.f && LL < 69.f) { //transition
float trh = aaa * LL + bbb;
WavCoeffs_ab0[i] += trh * (sca) * 300.f;
} else if (LL >= 69.f) { //highlights
WavCoeffs_ab0[i] += cp.grhigh * (sca) * 300.f;
}
} else { //blue yellow
//transition with 6 between 30 to 36 and 63 to 69
float aa1 = (cp.blmed - cp.bllow) / 6.f;
float bb1 = cp.bllow - 30.f * aa1;
float aaa1 = (cp.blhigh - cp.blmed) / 6.f;
float bbb1 = cp.blmed - 63.f * aaa1;
if (LL < 30.f) {
WavCoeffs_ab0[i] += cp.bllow * (sca) * 300.f;
} else if (LL >= 30.f && LL < 36.f) {
float tr1 = aa1 * LL + bb1;
WavCoeffs_ab0[i] += tr1 * (sca) * 300.f;
} else if (LL >= 36.f && LL < 63.f) {
WavCoeffs_ab0[i] += cp.blmed * (sca) * 300.f;
} else if (LL >= 63.f && LL < 69.f) {
float trh1 = aaa1 * LL + bbb1;
WavCoeffs_ab0[i] += trh1 * (sca) * 300.f;
} else if (LL >= 69.f) {
WavCoeffs_ab0[i] += cp.blhigh * (sca) * 300.f;
}
}
}
}
//Blur chroma
if (cp.blurcres != 0.f && cp.resena) {
int minWL = min(W_L, H_L);
//printf("skip=%i WL=%i HL=%i min=%i\n", skip, W_L, H_L, minWL);
if (minWL > 140) { //disabled if too low windows
constexpr float k = 0.5f;
float rad = k * cp.blurcres / skip;
float * bef = new float[W_L * H_L];
float * aft = new float[W_L * H_L];
for (int i = 0; i < H_L * W_L; i++) {
bef[i] = WavCoeffs_ab0[i];
}
boxblur(bef, aft, rad, W_L, H_L, false);
for (int i = 0; i < H_L * W_L; i++) {
WavCoeffs_ab0[i] = aft[i];
}
delete[] bef;
delete[] aft;
}
}
bool wavcurvecomp = false;//not enable if 0.75
if (wavblcurve) {
for (int i = 0; i < 500; i++) {
if (wavblcurve[i] != 0.) {
wavcurvecomp = true;
break;
}
}
}
std::unique_ptr<float[]> aft;
#ifdef _OPENMP
#pragma omp for schedule(dynamic) collapse(2)
#endif
for (int dir = 1; dir < 4; dir++) {
for (int lvl = 0; lvl < maxlvl; lvl++) {
int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl);
int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl);
float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
ContAllAB(labco, maxlvl, varhue, varchrom, WavCoeffs_ab, WavCoeffs_ab0, lvl, dir, waOpacityCurveW, cp, Wlvl_ab, Hlvl_ab, useChannelA, meanab, sigmaab);
if(std::min(Wlvl_ab, Hlvl_ab) > 180) {
if (wavblcurve && wavcurvecomp && cp.blena && cp.chrwav > 0.f) {
float mea[10];
const float effect = cp.bluwav;
constexpr float offs = 1.f;
calceffect(lvl, meanab, sigmaab, mea, effect, offs);
float lutFactor;
const float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, 0.8f, 0.6f, 0.4f, 0.2f, 0.1f, 0.00f};
const auto meaLut = buildMeaLut(inVals, mea, lutFactor);
if (!aft.get()) {
aft.reset(new float[Wlvl_ab * Hlvl_ab]);
}
//blur level
const float klev = wavblcurve[lvl * 55.5f] * 80.f / skip;
boxblur(WavCoeffs_ab[dir], aft.get(), klev, Wlvl_ab, Hlvl_ab, false);
auto WavAb = WavCoeffs_ab[dir];
int co = 0;
#ifdef __SSE2__
const vfloat lutFactorv = F2V(lutFactor);
for (; co < Hlvl_ab * Wlvl_ab - 3; co += 4) {
const vfloat valv = LVFU(WavAb[co]);
STVFU(WavAb[co], intp((*meaLut)[vabsf(valv) * lutFactorv], LVFU(aft[co]), valv));
}
#endif
for (; co < Hlvl_ab * Wlvl_ab; co++) {
WavAb[co] = intp((*meaLut)[std::fabs(WavAb[co]) * lutFactor], aft[co], WavAb[co]);
}
}
}
}
}
}
}
void ImProcFunctions::calckoe (const float* WavCoeffs, float gradw, float tloww, float *koeLi, int level, int W_L, int H_L, float edd, float &maxkoeLi, float **tmC, bool multiThread)
{
const int borderL = tloww < 75.f ? 1 : 2;
if (tloww < 75.f) {
// I calculate coefficients with r size matrix 3x3 r=1 ; 5x5 r=2; 7x7 r=3
/*
float k[2*r][2*r];
for (int i=1;i<=(2*r+1);i++) {
for (int j=1;j<=(2*r+1);j++) {
k[i][j]=(1.f/6.283*sigma*sigma)*exp(-SQR(i-r-1)+SQR(j-r-1)/2.f*SQR(sigma));
}
}
//I could also use Gauss.h for 3x3
// If necessary I can put a 7x7 matrix
*/
float c0, c1, c2, mult;
if (tloww < 30.f) { //sigma=0.55
c0 = 8.94f;
c1 = 1.71f;
c2 = 0.33f;
mult = 0.0584795f;
} else if (tloww < 50.f) { //sigma=0.85
c0 = 4.0091f;
c1 = 2.0068f;
c2 = 1.0045f;
mult = 0.062288f;
} else { //sigma=1.1
c0 = 3.025f;
c1 = 2.001f;
c2 = 1.323f;
mult = 0.06127f;
}
c0 *= mult;
c1 *= mult;
c2 *= mult;
#ifdef _OPENMP
#pragma omp parallel for if(multiThread)
#endif
for (int i = 1; i < H_L - 1; i++) {
for (int j = 1; j < W_L - 1; j++) {
tmC[i][j] = c0 * WavCoeffs[i * W_L + j] +
c1 * ((WavCoeffs[(i - 1) * W_L + j] + WavCoeffs[(i + 1) * W_L + j]) + (WavCoeffs[i * W_L + j + 1] + WavCoeffs[i * W_L + j - 1])) +
c2 * ((WavCoeffs[(i - 1) * W_L + j - 1] + WavCoeffs[(i - 1) * W_L + j + 1]) + (WavCoeffs[(i + 1) * W_L + j - 1] + WavCoeffs[(i + 1) * W_L + j + 1]));
}
}
} else {
if (level > 1) { // do not activate 5x5 if level 0 or 1
// Gaussian 1.1
// 0.5 2 3 2 0.5
// 2 7 10 7 2
// 3 10 15 10 3
// 2 7 10 7 2
// 0.5 2 3 2 0.5
// divi 113
//Gaussian 1.4
// 2 4 5 4 2
// 4 9 12 9 4
// 5 12 15 12 5
// 4 9 12 9 4
// 2 4 5 4 2
// divi 159
float c0, c1, c2, c3, c4, c5, mult;
if (tloww < 85.f) { //sigma=1.1
c0 = 15.f;
c1 = 10.f;
c2 = 7.f;
c3 = 3.f;
c4 = 2.f;
c5 = 0.5f;
mult = 0.0088495f;
} else { //sigma=1.4
c0 = 15.f;
c1 = 12.f;
c2 = 9.f;
c3 = 5.f;
c4 = 4.f;
c5 = 2.f;
mult = 0.0062893f;
}
c0 *= mult;
c1 *= mult;
c2 *= mult;
c3 *= mult;
c4 *= mult;
c5 *= mult;
#ifdef _OPENMP
#pragma omp parallel for if(multiThread)
#endif
for (int i = 2; i < H_L - 2; i++) {
for (int j = 2; j < W_L - 2; j++) {
tmC[i][j] = c0 * WavCoeffs[i * W_L + j] +
c1 * ((WavCoeffs[(i - 1) * W_L + j] + WavCoeffs[(i + 1) * W_L + j]) + (WavCoeffs[i * W_L + j + 1] + WavCoeffs[i * W_L + j - 1])) +
c2 * ((WavCoeffs[(i - 1) * W_L + j - 1] + WavCoeffs[(i - 1) * W_L + j + 1]) + (WavCoeffs[(i + 1) * W_L + j - 1] + WavCoeffs[(i + 1) * W_L + j + 1])) +
c3 * ((WavCoeffs[(i - 2) * W_L + j] + WavCoeffs[(i + 2) * W_L + j]) + (WavCoeffs[i * W_L + j - 2] + WavCoeffs[i * W_L + j + 2])) +
c4 * ((WavCoeffs[(i - 2) * W_L + j - 1] + WavCoeffs[(i - 2) * W_L + j + 1]) + (WavCoeffs[(i + 2) * W_L + j + 1] + WavCoeffs[(i + 2) * W_L + j - 1]) +
(WavCoeffs[(i - 1) * W_L + j - 2] + WavCoeffs[(i - 1) * W_L + j + 2]) + (WavCoeffs[(i + 1) * W_L + j + 2] + WavCoeffs[(i + 1) * W_L + j - 2])) +
c5 * ((WavCoeffs[(i - 2) * W_L + j - 2] + WavCoeffs[(i - 2) * W_L + j + 2]) + (WavCoeffs[(i + 2) * W_L + j - 2] + WavCoeffs[(i + 2) * W_L + j + 2]));
}
}
} else {
#ifdef _OPENMP
#pragma omp parallel for if(multiThread)
#endif
for (int i = 0; i < H_L; i++) {
for (int j = 0; j < W_L; j++) {
koeLi[i * W_L + j] = 0.f;
}
}
return;
}
}
// fill borders with 1.f
int ii = 0;
for (; ii < borderL; ii++) {
for (int j = 0; j < W_L; j++) {
koeLi[ii * W_L + j] = 1.f;
}
}
for (; ii < H_L - borderL; ii++) {
for (int j = 0; j < borderL; j++) {
koeLi[ii * W_L + j] = 1.f;
}
for (int j = W_L - borderL; j < W_L; j++) {
koeLi[ii * W_L + j] = 1.f;
}
}
for (; ii < H_L; ii++) {
for (int j = 0; j < W_L; j++) {
koeLi[ii * W_L + j] = 1.f;
}
}
constexpr float thr = 40.f; //avoid artifact eg. noise...to test
const float thr2 = 1.5f * edd + gradw / 30.f; //edd can be modified in option ed_detect
const float diffFactor = gradw / 100.f;
for (int i = borderL; i < H_L - borderL; i++) {
for (int j = borderL; j < W_L - borderL; j++) {
// my own algo : probably a little false, but simpler as Lipschitz !
// Thr2 = maximum of the function ==> Lipsitch says = probably edge
float temp = rtengine::max(std::fabs(WavCoeffs[i * W_L + j]), thr);
koeLi[i * W_L + j] = rtengine::min(thr2, std::fabs(tmC[i][j] / temp)); // limit maxi
//it will be more complicated to calculate both Wh and Wv, but we have also Wd==> pseudo Lipschitz
if (koeLi[i * W_L + j] > maxkoeLi) {
maxkoeLi = koeLi[i * W_L + j];
}
float diff = maxkoeLi - koeLi[i * W_L + j];
diff *= diffFactor;
koeLi[i * W_L + j] = maxkoeLi - diff;
}
}
}
// Copyright 6-2024 - Jacques Desmis <jdesmis@gmail.com>
void ImProcFunctions::complete_local_contrast (LabImage * lab, LabImage * dst, const procparams::WaveletParams & waparams, const procparams::ColorManagementParams & cmparams, const WavOpacityCurveWL & cmOpacityCurveWL, int skip, int &level_hr, int &maxlevpo, bool &wavcurvecont)
{
wavcurvecont = false;
if (cmOpacityCurveWL) {//activate only if one value not equal to 0.5
for (int i = 0; i < 500; i++) {
if (cmOpacityCurveWL[i] != 0.5f) {
wavcurvecont = true;
break;
}
}
}
if(wavcurvecont && cmparams.wavExp) {//enable curve and expander
#ifdef _OPENMP
const int numThreads = omp_get_max_threads();
#else
const int numThreads = 1;
#endif
int width = lab->W;
int height = lab->H;
int wavelet_lev = 7;//default
int DaubLen = 4;//type of wave
if (waparams.daubcoeffmethod == "2_") {
DaubLen = 4;
} else if (waparams.daubcoeffmethod == "4_") {
DaubLen = 6;//default
} else if (waparams.daubcoeffmethod == "6_") {
DaubLen = 8;
} else if (waparams.daubcoeffmethod == "10_") {
DaubLen = 12;
} else if (params->wavelet.daubcoeffmethod == "14_") {
DaubLen = 16;
} else if (params->wavelet.daubcoeffmethod == "20_") {
DaubLen = 22;
}
float sigmafin = cmparams.sigmatrc;//attenuation response signal
int pyrwav = cmparams.pyrwavtrc;//levels contrast profiles
float offset = cmparams.offstrc;//offset signal
int level_bl = 0;//adapted to each levels profile
int level_hl = 1;//adapted to each levels profile
int level_br = wavelet_lev;
level_hr = wavelet_lev;//to adapt if necessary
//6 contrast profiles to change range levels and rolloff for high contrast positive and negative - of course we can add anothers
//I change only values for LUT for high contrast values and not for low levels, but we can...
float inva5 = 0.8f;
float inva6 = 0.7f;
float inva7 = 0.5f;
float inva8 = 0.4f;
float inva9 = 0.3f;
float inva10 = 0.1f;
if(pyrwav == 1) {//low contrast
level_bl = 0;//0
level_hl = 1;//1
level_br = wavelet_lev - 3;//-3
level_hr = wavelet_lev - 2;//-2
} else if(pyrwav == 2) {
level_bl = 0;//0
level_hl = 0;//1
level_br = wavelet_lev - 3;//-2
level_hr = wavelet_lev - 1;//-1
if(!cmparams.wsmoothcie) {
inva5 = 1.f;
inva6 = 0.8f;
inva7 = 0.65f;
inva8 = 0.5f;
inva9 = 0.3f;
inva10 = 0.2f;
}
} else if( pyrwav == 3) {//default
level_bl = 0;
level_hl = 0;
level_br = wavelet_lev - 2;//-1
level_hr = wavelet_lev;//0
if(!cmparams.wsmoothcie) {
inva5 = 1.f; //1
inva6 = 0.9f;//0.9
inva7 = 0.7f;//0.7
inva8 = 0.6f;//0.6
inva9 = 0.4f;//0.4
inva10 = 0.2f;//0.2
}
} else if( pyrwav == 4) {
level_bl = 0;
level_hl = 0;
level_br = wavelet_lev -1;//0
level_hr = wavelet_lev +1;//0
if(!cmparams.wsmoothcie) {
inva5 = 0.9f;
inva6 = 0.8f;
inva7 = 0.6f;
inva8 = 0.5f;
inva9 = 0.3f;
inva10 = 0.1f;
}
} else if( pyrwav == 5) {
level_bl = 0;
level_hl = 0;
level_br = wavelet_lev - 1;//-1
level_hr = wavelet_lev + 2;//+1 //be careful the preview must be big enough to see the changes
if(!cmparams.wsmoothcie) {
inva5 = 0.8f;//0.85
inva6 = 0.6f;//0.75
inva7 = 0.5f;//0.55
inva8 = 0.3f;//0.45
inva9 = 0.2f;//0.3
inva10 = 0.05f;//0.1
}//last choice not used for various reasons
} else if( pyrwav == 6) {//agresive - maximum - in this case LUT are minimal to avoid artifacts -be careful the preview must be big enough to see the changes
level_bl = 0;
level_hl = 0;
level_br = wavelet_lev ;//-1
level_hr = wavelet_lev + 2;//here maximum
}
//find possible max levels in function of windows preview size.
int minwin = rtengine::min(width, height);
int maxlevelspot = 9;//maximum possible
// adapt maximum level wavelet to size of crop
while ((1 << maxlevelspot) >= minwin && maxlevelspot > 1) {
--maxlevelspot ;
}
int wavelet_level = rtengine::min(level_hr, maxlevelspot);
int maxlvl = wavelet_level;
maxlevpo = maxlvl;
//decomposition wavelet , we can change Daublen (moment wavelet) in Tab - Wavelet Levels with subsampling = 1
std::unique_ptr<wavelet_decomposition> wdspot = std::unique_ptr<wavelet_decomposition>(new wavelet_decomposition(lab->L[0], width, height, maxlvl, 1, skip, numThreads, DaubLen));
if (wdspot->memory_allocation_failed()) {//probably if not enough memory.
return;
}
//residual contrast
const float contresid = cmparams.residtrc;
if (contresid != 0) {
int W_L = wdspot->level_W(0);
int H_L = wdspot->level_H(0);
float *wav_L0 = wdspot->get_coeff0();//residual image
float maxh = 1.25f; //amplification contrast above mean, we can change 1.25f
float maxl = 1.25f; //reduction contrast under mean
float multL = contresid * (maxl - 1.f) / 100.f + 1.f;
float multH = contresid * (maxh - 1.f) / 100.f + 1.f;
double avedbl = 0.0; // use double precision for large summations
float max0 = 0.f;
float min0 = FLT_MAX;
#ifdef _OPENMP
# pragma omp parallel for reduction(+:avedbl) if (multiThread)
#endif
for (int i = 0; i < W_L * H_L; i++) {
avedbl += wav_L0[i];
}
#ifdef _OPENMP
# pragma omp parallel if (multiThread)
#endif
{
float lminL = FLT_MAX;
float lmaxL = 0.f;
#ifdef _OPENMP
# pragma omp for
#endif
for (int i = 0; i < W_L * H_L; i++) {
lminL = min(lminL, wav_L0[i]);
lmaxL = max(lmaxL, wav_L0[i]);
}
#ifdef _OPENMP
# pragma omp critical
#endif
{
min0 = min(min0, lminL);
max0 = max(max0, lmaxL);
}
}
max0 /= 327.68f;
min0 /= 327.68f;
float ave = avedbl / double(W_L * H_L);
//transitions
float av = ave / 327.68f;
float ah = (multH - 1.f) / (av - max0);
float bh = 1.f - max0 * ah;
float al = (multL - 1.f) / (av - min0);
float bl = 1.f - min0 * al;
if (max0 > 0.0) {
#ifdef _OPENMP
# pragma omp parallel for if (multiThread)
#endif
for (int i = 0; i < W_L * H_L; i++) {
if (wav_L0[i] < 32768.f) {
float prov;
if (wav_L0[i] > ave) {
float kh = ah * (wav_L0[i] / 327.68f) + bh;
prov = wav_L0[i];
wav_L0[i] = ave + kh * (wav_L0[i] - ave);
} else {
float kl = al * (wav_L0[i] / 327.68f) + bl;
prov = wav_L0[i];
wav_L0[i] = ave - kl * (ave - wav_L0[i]);
}
float diflc = wav_L0[i] - prov;
wav_L0[i] = prov + diflc;
}
}
}
}
//end residual contrast
//begin variable contrast
// declaration with 10 levels to calculate mean , mean negative, sigma, sigma negative, Max et Max negative for each level
float mean[10];
float meanN[10];
float sigma[10];
float sigmaN[10];
float MaxP[10];
float MaxN[10];
Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads);//calculate mean sigma Max for each levels
float alow = 1.f;
float blow = 0.f;
if (level_hl != level_bl) {//transitions low levels
alow = 1.f / (level_hl - level_bl);
blow = -alow * level_bl;
}
float ahigh = 1.f;
float bhigh = 0.f;
if (level_hr != level_br) {//transitions high levels
ahigh = 1.f / (level_hr - level_br);
bhigh = -ahigh * level_br;
}
for (int dir = 1; dir < 4; dir++) {//for each direction
for (int level = level_bl; level < maxlvl; ++level) {//for each levels
int W_L = wdspot->level_W(level);
int H_L = wdspot->level_H(level);
float* const* wav_L = wdspot->level_coeffs(level);
//sigmafin = attenuation response to change signal shape
// I use only positives values to simplify calculations... possible improvment.
if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) {
float insigma = 0.666f; //SD standard deviation (modelisation)
float logmax = log(MaxP[level]); //log Max
float rapX = (offset * mean[level] + sigmafin * sigma[level]) / MaxP[level]; //rapport between SD / max
//offset move mean location in signal
float inx = log(insigma);
float iny = log(rapX);
float rap = inx / iny; //koef
//transitions
float asig = 0.166f / (sigma[level] * sigmafin);
float bsig = 0.5f - asig * (mean[level] * offset);
float amean = 0.5f / (mean[level] * offset);
const float effect = sigmafin;
float mea[10];//simulation using mean and sigma, to evaluate signal
calceffect(level, mean, sigma, mea, effect, offset);
float klev = 1.f;
if (level >= level_hl && level <= level_hr) {
klev = 1.f;
}
//change klev with real change in levels - see contrast profiles
//transition in beginning low levels
if (level_hl != level_bl) {
if (level >= level_bl && level < level_hl) {
klev = alow * level + blow;
}
}
//transition in max levels
if (level_hr != level_br) {
if (level > level_hr && level <= level_br) {
klev = ahigh * level + bhigh;
}
}
const float threshold = offset * mean[level] + sigmafin * sigma[level];//base signal calculation.
float lutFactor;//inva5, inva6, inva7, inva8, inva9, inva10 are define in Contrast profiles.
float inVals[] = {0.05f, 0.2f, 0.7f, 1.f, 1.f, inva5, inva6, inva7, inva8, inva9, inva10};//values to give for calculate LUT along signal : minimal near 0 or MaxP
const auto meaLut = buildMeaLut(inVals, mea, lutFactor);//build LUT
#ifdef _OPENMP
#pragma omp parallel for if (multiThread)
#endif
for (int y = 0; y < H_L; y++) {
for (int x = 0; x < W_L; x++) {//for each pixel
if(cmOpacityCurveWL) {//if curve enable
float absciss;//position in curve and signal
float &val = wav_L[dir][y * W_L + x];
const float WavCL = std::fabs(wav_L[dir][y * W_L + x]);
if (WavCL >= threshold) { //for max take into account attenuation response and offset
float valcour = xlogf(fabsf(val));
float valc = valcour - logmax;
float vald = valc * rap;
absciss = xexpf(vald);
} else if (WavCL >= offset * mean[level]) {//offset only
absciss = asig * WavCL + bsig;
} else {
absciss = amean * WavCL;
}
/*
*/
float kc = klev * (cmOpacityCurveWL[absciss * 500.f] - 0.5f);
float amplieffect = kc <= 0.f ? 1.f : 1.7f;//we can change 1.5 - to 1.7 or more or less
float kinterm = 1.f + amplieffect * kc;
kinterm = kinterm <= 0.f ? 0.01f : kinterm;
val *= (1.f + (kinterm - 1.f) * (*meaLut)[WavCL * lutFactor]);//change signal (contrast) for each level, direction, with LUT.
}
}
}
}
}
}
//reconstruct lab
wdspot->reconstruct(lab->L[0], 1.f);
}
}
void ImProcFunctions::finalContAllL(float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, const cont_params &cp,
int W_L, int H_L, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL)
{
if (cp.diagcurv && cp.finena && MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { //curve
float insigma = 0.666f; //SD
float logmax = log(MaxP[level]); //log Max
float rapX = (mean[level] + cp.sigmafin * sigma[level]) / (MaxP[level]); //rapport between sD / max
float inx = log(insigma);
float iny = log(rapX);
float rap = inx / iny; //koef
float asig = 0.166f / (sigma[level] * cp.sigmafin);
float bsig = 0.5f - asig * mean[level];
float amean = 0.5f / (mean[level]);
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, W_L * 16) num_threads(wavNestedLevels) if (wavNestedLevels>1)
#endif
for (int i = 0; i < W_L * H_L; i++) {
float absciss;
if (std::fabs(WavCoeffs_L[dir][i]) >= (mean[level] + cp.sigmafin * sigma[level])) { //for max
float valcour = xlogf(std::fabs(WavCoeffs_L[dir][i]));
float valc = valcour - logmax;
float vald = valc * rap;
absciss = xexpf(vald);
} else if (std::fabs(WavCoeffs_L[dir][i]) >= mean[level]) {
absciss = asig * std::fabs(WavCoeffs_L[dir][i]) + bsig;
} else {
absciss = amean * std::fabs(WavCoeffs_L[dir][i]);
}
float kc = waOpacityCurveWL[absciss * 500.f] - 0.5f;
float reduceeffect = kc <= 0.f ? 1.f : 1.5f;
float kinterm = 1.f + reduceeffect * kc;
kinterm = kinterm <= 0.f ? 0.01f : kinterm;
WavCoeffs_L[dir][i] *= kinterm;
}
}
int choicelevel = params->wavelet.Lmethod - 1;
choicelevel = choicelevel == -1 ? 4 : choicelevel;
int choiceClevel = 0;
if (params->wavelet.CLmethod == "one") {
choiceClevel = 0;
} else if (params->wavelet.CLmethod == "inf") {
choiceClevel = 1;
} else if (params->wavelet.CLmethod == "sup") {
choiceClevel = 2;
} else if (params->wavelet.CLmethod == "all") {
choiceClevel = 3;
}
int choiceDir = 0;
if (params->wavelet.Dirmethod == "one") {
choiceDir = 1;
} else if (params->wavelet.Dirmethod == "two") {
choiceDir = 2;
} else if (params->wavelet.Dirmethod == "thr") {
choiceDir = 3;
} else if (params->wavelet.Dirmethod == "all") {
choiceDir = 0;
}
int dir1 = (choiceDir == 2) ? 1 : 2;
int dir2 = (choiceDir == 3) ? 1 : 3;
if (choiceClevel < 3) { // not all levels visible, paint residual
if (level == 0) {
if (cp.backm != 2) { // nothing to change when residual is used as background
float backGroundColor = (cp.backm == 1) ? 12000.f : 0.f;
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L0[i] = backGroundColor;
}
}
}
}
if (choiceClevel == 0) { // Only one level
if (choiceDir == 0) { // All directions
if (level != choicelevel) { // zero all for the levels != choicelevel
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level == choicelevel
if (choicelevel >= cp.maxilev) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[d][i] = 0.f;
}
}
} else if (level != choicelevel) { // zero all for the levels != choicelevel
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[dir1][i] = WavCoeffs_L[dir2][i] = 0.f;
}
}
}
} else if (choiceClevel == 1) { // Only below level
if (choiceDir == 0) { // All directions
if (level > choicelevel) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level >= choicelevel
if (level > choicelevel) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[dir1][i] = WavCoeffs_L[dir2][i] = 0.f;
}
}
}
} else if (choiceClevel == 2) { // Only above level
if (choiceDir == 0) { // All directions
if (level <= choicelevel) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level >= choicelevel
if (choicelevel >= cp.maxilev) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[d][i] = 0.f;
}
}
}
else if (level <= choicelevel) {
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[dir1][i] = WavCoeffs_L[dir2][i] = 0.f;
}
}
}
}
}
void ImProcFunctions::ContAllL(float *koeLi[12], float maxkoeLi, bool lipschitz, int maxlvl, LabImage * labco, const float* const* varhue, const float* const* varchrom, float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params &cp,
int W_L, int H_L, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili)
{
assert(level >= 0);
assert(maxlvl > level);
static const float scales[10] = {1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, 512.f};
float scaleskip[10];
for (int sc = 0; sc < 10; sc++) {
scaleskip[sc] = scales[sc] / skip;
}
/*
if (settings->verbose) {
printf("level=%i mean=%f sigma=%f maxp=%f\n", level, mean[level], sigma[level], MaxP[level]);
}
*/
constexpr float t_r = 40.f;
constexpr float t_l = 10.f;
constexpr float b_r = 75.f;
constexpr float edd = 3.f;
constexpr float eddstrength = 1.3f;
constexpr float aedstr = (eddstrength - 1.f) / 90.f;
constexpr float bedstr = 1.f - 10.f * aedstr;
std::unique_ptr<float[]> beta(new float[W_L * H_L]);
for (int co = 0; co < H_L * W_L; co++) {
beta[co] = 1.f;
}
if (cp.eff < 2.5f) {
float effect = cp.eff;
float offs = 1.f;
float mea[10];
calceffect(level, mean, sigma, mea, effect, offs);
for (int co = 0; co < H_L * W_L; co++) {
float WavCL = std::fabs(WavCoeffs_L[dir][co]);
if (WavCL < mea[0]) {
beta[co] = 0.05f;
} else if (WavCL < mea[1]) {
beta[co] = 0.2f;
} else if (WavCL < mea[2]) {
beta[co] = 0.7f;
} else if (WavCL < mea[3]) {
beta[co] = 1.f; //standard
} else if (WavCL < mea[4]) {
beta[co] = 1.f;
} else if (WavCL < mea[5]) {
beta[co] = 0.8f; //+sigma
} else if (WavCL < mea[6]) {
beta[co] = 0.6f;
} else if (WavCL < mea[7]) {
beta[co] = 0.4f;
} else if (WavCL < mea[8]) {
beta[co] = 0.2f; // + 2 sigma
} else if (WavCL < mea[9]) {
beta[co] = 0.1f;
} else {
beta[co] = 0.0f;
}
}
}
if (cp.val > 0 && cp.edgeena) {
float * koe = nullptr;
float maxkoe = 0.f;
if (!lipschitz) {
koe = new float [H_L * W_L];
for (int i = 0; i < W_L * H_L; i++) {
koe[i] = 0.f;
}
maxkoe = 0.f;
if (cp.detectedge) {
float** tmC;
int borderL = 1;
tmC = new float*[H_L];
for (int i = 0; i < H_L; i++) {
tmC[i] = new float[W_L];
}
{
for (int i = 1; i < H_L - 1; i++) {
for (int j = 1; j < W_L - 1; j++) {
//edge detection wavelet TMC Canny
// also possible to detect noise with 5x5 instead of 3x3
tmC[i][j] = (4.f * WavCoeffs_L[dir][i * W_L + j] + 2.f * WavCoeffs_L[dir][(i - 1) * W_L + j] + 2.f * WavCoeffs_L[dir][(i + 1) * W_L + j]
+ 2.f * WavCoeffs_L[dir][i * W_L + j + 1] + 2.f * WavCoeffs_L[dir][i * W_L + j - 1] + WavCoeffs_L[dir][(i - 1) * W_L + j - 1]
+ WavCoeffs_L[dir][(i - 1) * W_L + j + 1] + WavCoeffs_L[dir][(i + 1) * W_L + j - 1] + WavCoeffs_L[dir][(i + 1) * W_L + j + 1]) / 16.f;
// apply to each direction Wavelet level : horizontal / vertiacle / diagonal
}
}
}
for (int i = borderL; i < H_L - borderL; i++) {
for (int j = borderL; j < W_L - borderL; j++) {
// my own algo : probably a little false, but simpler as Lipschitz !
float thr = 40.f; //avoid artifact eg. noise...to test
float thr2 = edd; //edd can be modified in option ed_detect
thr2 += cp.eddet / 30.f; //to test
float temp = WavCoeffs_L[dir][i * W_L + j];
if (temp >= 0.f && temp < thr) {
temp = thr;
}
if (temp < 0.f && temp > -thr) {
temp = -thr;
}
koe[i * W_L + j] = rtengine::min(thr2, std::fabs(tmC[i][j] / temp));
maxkoe = rtengine::max(maxkoe, koe[i * W_L + j]);
float diff = maxkoe - koe[i * W_L + j];
diff *= (cp.eddet / 100.f);
float interm = maxkoe - diff;
if (interm < cp.eddetthr / 30.f) {
interm = 0.01f;
}
koe[i * W_L + j] = interm;
}
}
for (int i = 0; i < H_L; i++) {
delete [] tmC[i];
}
delete [] tmC;
}
}
//end detect edge
float rad = ((float)cp.rad) / 60.f; //radius ==> not too high value to avoid artifacts
float value = ((float)cp.val) / 8.f; //strength
if (scaleskip[1] < 1.f) {
float atten01234 = 0.80f;
value *= (atten01234 * scaleskip[1]); //for zoom < 100% reduce strength...I choose level 1...but!!
}
float edghig = settings->edghi;//increase or reduce "reinforce"
float edglow = settings->edglo;//increase or reduce "reduce"
float limrad = settings->limrad;//threshold action in function radius (rad)
// printf("edghi=%f edglo=%f limrad=%f\n", edghig, edglow, limrad);
// value *= beta;
float edge = 1.f;
float lim0 = limrad; //arbitrary limit for low radius and level between 2 or 3 to 30 maxi
float lev = float (level);
float repart = (float)cp.til;
if (cp.reinforce != 2) {
const float brepart =
cp.reinforce == 1
? edghig
: edglow;
const float arepart = -(brepart - 1.f) / (lim0 / 60.f);
if (rad < (lim0 / 60.f)) {
repart *= (arepart * rad + brepart); //linear repartition of repart
}
}
float al0 = 1.f + (repart) / 50.f;
float al10 = 1.0f; //arbitrary value ==> less = take into account high levels
// float ak =-(al0-al10)/10.f;//10 = maximum levels
float ak = -(al0 - al10) / 10.f; //10 = maximum levels
float bk = al0;
float koef = ak * level + bk; //modulate for levels : more levels high, more koef low ==> concentrated action on low levels, without or near for high levels
float expkoef = -std::pow(std::fabs(rad - lev), koef); //reduce effect for high levels
// printf("repart=%f\n", repart);
if (cp.reinforce == 3) {
if (rad < (lim0 / 60.f) && level == 0) {
expkoef *= abs(repart); //reduce effect for low values of rad and level=0==> quasi only level 1 is effective
}
}
if (cp.reinforce == 1) {
if (rad < (lim0 / 60.f) && level == 1) {
expkoef /= repart; //increase effect for low values of rad and level=1==> quasi only level 0 is effective
}
}
//take into account local contrast
float refin = value * exp(expkoef);
if (cp.link && cp.noiseena) { //combi
{
if (level == 0) {
refin *= (1.f + cp.lev0s / 50.f); // we can change this sensibility!
}
if (level == 1) {
refin *= (1.f + cp.lev1s / 50.f);
}
if (level == 2) {
refin *= (1.f + cp.lev2s / 50.f);
}
if (level == 3) {
refin *= (1.f + cp.lev3s / 50.f);
}
}
}
float edgePrecalc = 1.f + refin; //estimate edge "pseudo variance"
if (cp.EDmet == 2 && MaxP[level] > 0.f) { //curve
// if (exa) {//curve
float insigma = 0.666f; //SD
float logmax = log(MaxP[level]); //log Max
float rapX = (mean[level] + sigma[level]) / (MaxP[level]); //rapport between sD / max
float inx = log(insigma);
float iny = log(rapX);
float rap = inx / iny; //koef
float asig = 0.166f / (sigma[level]);
float bsig = 0.5f - asig * mean[level];
float amean = 0.5f / (mean[level]);
float absciss = 0.f;
float kinterm;
float kmul;
int borderL = 1;
for (int i = borderL; i < H_L - borderL; i++) {
for (int j = borderL; j < W_L - borderL; j++) {
int k = i * W_L + j;
if (cp.detectedge) {
if (!lipschitz) {
if (cp.eddet > 10.f) {
edge = (aedstr * cp.eddet + bedstr) * (edgePrecalc * (1.f + koe[k])) / (1.f + 0.9f * maxkoe);
} else {
edge = (edgePrecalc * (1.f + koe[k])) / (1.f + 0.9f * maxkoe);
}
}
if (lipschitz) {
if (level < 4) {
edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi);
} else {
edge = edgePrecalc;
}
}
} else {
edge = edgePrecalc;
}
if (cp.edgcurv) {
if (std::fabs(WavCoeffs_L[dir][k]) >= (mean[level] + sigma[level])) { //for max
float valcour = xlogf(std::fabs(WavCoeffs_L[dir][k]));
float valc = valcour - logmax;
float vald = valc * rap;
absciss = exp(vald);
} else if (std::fabs(WavCoeffs_L[dir][k]) >= mean[level] && std::fabs(WavCoeffs_L[dir][k]) < (mean[level] + sigma[level])) {
absciss = asig * std::fabs(WavCoeffs_L[dir][k]) + bsig;
} else if (std::fabs(WavCoeffs_L[dir][k]) < mean[level]) {
absciss = amean * std::fabs(WavCoeffs_L[dir][k]);
}
// Threshold adjuster settings==> approximative for curve
//kmul about average cbrt(3--40 / 10)==>1.5 to 2.5
//kmul about SD 10--60 / 35 ==> 2
// kmul about low cbrt((5.f+cp.edg_low)/5.f);==> 1.5
// kmul about max ==> 9
// we can change these values
// result is different not best or bad than threshold slider...but similar
constexpr float abssd = 4.f; //amplification reference
constexpr float bbssd = 2.f; //mini ampli
float kmuld = 0.f;
if (absciss > 0.666f && absciss < 1.f) {
constexpr float maxamp = 2.5f; //maxi ampli at end
constexpr float maxampd = 10.f; //maxi ampli at end
constexpr float a_abssd = (maxamp - abssd) / 0.333f;
constexpr float b_abssd = maxamp - a_abssd;
constexpr float da_abssd = (maxampd - abssd) / 0.333f;
constexpr float db_abssd = maxampd - da_abssd;
kmul = a_abssd * absciss + b_abssd; //about max ==> kinterm
kmuld = da_abssd * absciss + db_abssd;
} else {
constexpr float am = (abssd - bbssd) / 0.666f;
kmul = kmuld = absciss * am + bbssd;
}
const float kc = kmul * (wavCLVCcurve[absciss * 500.f] - 0.5f);
if (kc >= 0.f) {
float reduceeffect = 0.6f;
kinterm = 1.f + reduceeffect * kmul * (wavCLVCcurve[absciss * 500.f] - 0.5f); //about 1 to 3 general and big amplification for max (under 0)
} else {
const float kcd = kmuld * (wavCLVCcurve[absciss * 500.f] - 0.5f);
kinterm = 1.f - (SQR(kcd)) / 10.f;
}
if (kinterm < 0.f) {
kinterm = 0.01f;
}
edge *= kinterm;
edge = rtengine::max(edge, 1.f);
}
WavCoeffs_L[dir][k] *= (1.f + (edge - 1.f) * beta[k]);
}
}
} else if (cp.EDmet == 1) { //threshold adjuster
float MaxPCompare = MaxP[level] * SQR(cp.edg_max / 100.f); //100 instead of b_r...case if b_r < 100
float MaxNCompare = MaxN[level] * SQR(cp.edg_max / 100.f); //always reduce a little edge for near max values
float edgeSdCompare = (mean[level] + 1.5f * sigma[level]) * SQR(cp.edg_sd / t_r); // 1.5 standard deviation #80% range between mean 50% and 80%
float edgeMeanCompare = mean[level] * SQR(cp.edg_mean / t_l);
float edgeLowCompare = (5.f + SQR(cp.edg_low));
float edgeMeanFactor = cbrt(cp.edg_mean / t_l);
float interm;
if (cp.edg_low < 10.f) {
interm = cbrt((5.f + cp.edg_low) / 5.f);
} else {
interm = 1.437f; //cbrt(3);
}
float edgeLowFactor = interm;
float edgeSdFactor = cp.edg_sd / t_r;
float edgeMaxFactor = SQR(cp.edg_max / b_r);
float edgMaxFsup = (cp.edg_max / b_r); //reduce increase of effect for high values contrast..if slider > b_r
//for (int i=0; i<W_L*H_L; i++) {
int borderL = 1;
for (int i = borderL; i < H_L - borderL; i++) {
for (int j = borderL; j < W_L - borderL; j++) {
int k = i * W_L + j;
if (cp.detectedge) {
if (!lipschitz) {
if (cp.eddet > 10.f) {
edge = (aedstr * cp.eddet + bedstr) * (edgePrecalc * (1.f + koe[k])) / (1.f + 0.9f * maxkoe);
} else {
edge = (edgePrecalc * (1.f + koe[k])) / (1.f + 0.9f * maxkoe);
}
}
if (lipschitz) {
if (level < 4) {
edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi);
} else {
edge = edgePrecalc;
}
}
} else {
edge = edgePrecalc;
}
//algorithm that takes into account local contrast
// I use a thresholdadjuster with
// Bottom left ==> minimal low value for local contrast (not 0, but 5...we can change)
// 0 10*10 35*35 100*100 substantially correspond to the true distribution of low value, mean, standard-deviation and max (ed 5, 50, 400, 4000
// Top left ==> mean reference value (for each level), we can change cbrt(cp.edg_mean/10.f)
// Top Right==> standard deviation (for each level) we can change (cp.edg_sd/35.f)
// bottom right ==> Max for positif and negatif contrast we can change cp.edg_max/100.f
// If we move sliders to the left, local contrast is reduced
// if we move sliders to the right local contrast is increased
// MaxP, MaxN, mean, sigma are calculated if necessary (val > 0) by evaluate2(), eval2(), aver() , sigma()
// if (b_r < 100.f && cp.edg_max / b_r > 1.f) { //in case of b_r < 100 and slider move to right
if (cp.edg_max / b_r > 1.f) { //in case of b_r < 100 and slider move to right
if (WavCoeffs_L[dir][k] > MaxPCompare * cp.edg_max / b_r) {
edge *= edgMaxFsup;
if (edge < 1.f) {
edge = 1.f;
}
} else if (WavCoeffs_L[dir][k] < MaxNCompare * cp.edg_max / b_r) {
edge *= edgMaxFsup;
if (edge < 1.f) {
edge = 1.f;
}
}
}
if (WavCoeffs_L[dir][k] > MaxPCompare) {
edge *= edgeMaxFactor;
if (edge < 1.f) {
edge = 1.f;
}
}//reduce edge if > new max
else if (WavCoeffs_L[dir][k] < MaxNCompare) {
edge *= edgeMaxFactor;
if (edge < 1.f) {
edge = 1.f;
}
}
if (std::fabs(WavCoeffs_L[dir][k]) >= edgeMeanCompare && std::fabs(WavCoeffs_L[dir][k]) < edgeSdCompare) {
//if (std::fabs(WavCoeffs_L[dir][i]) > edgeSdCompare) {
edge *= edgeSdFactor;
if (edge < 1.f) {
edge = 1.f;
}
}//modify effect if sd change
if (std::fabs(WavCoeffs_L[dir][k]) < edgeMeanCompare) {
edge *= edgeMeanFactor;
if (edge < 1.f) {
edge = 1.f;
}
} // modify effect if mean change
if (std::fabs(WavCoeffs_L[dir][k]) < edgeLowCompare) {
edge *= edgeLowFactor;
if (edge < 1.f) {
edge = 1.f;
}
}
WavCoeffs_L[dir][k] *= (1.f + (edge - 1.f) * beta[k]);
}
}
}
if (!lipschitz) {
delete [] koe;
}
if (!(cp.bam && cp.finena)) {
beta.reset();
}
}
if (!cp.link && cp.noiseena) { //used both with denoise 1 2 3
float refine = 0.f;
if (level == 0) {
refine = cp.lev0s / 40.f;
} else if (level == 1) {
refine = cp.lev1s / 40.f;
} else if (level == 2) {
refine = cp.lev2s / 40.f;
} else if (level == 3) {
refine = cp.lev3s / 40.f;
}
if (refine != 0.f) {
refine += 1.f;
for (int i = 0; i < W_L * H_L; i++) {
WavCoeffs_L[dir][i] *= refine;
}
}
}
float cpMul = cp.mul[level];
if (cpMul != 0.f && cp.contena) { // cpMul == 0.f means all will be multiplied by 1.f, so we can skip this
const float skinprot = params->wavelet.skinprotect;
const float skinprotneg = -skinprot;
const float factorHard = (1.f - skinprotneg / 100.f);
const float offs = params->wavelet.offset;
const float lowthr = params->wavelet.lowthr;
float mea[10];
float effect = cp.sigm;
float lbeta;
calceffect(level, mean, sigma, mea, effect, offs);
bool useChromAndHue = (skinprot != 0.f || cp.HSmet);
float modchro;
float red0 = 0.005f * (110.f - lowthr);
float red1 = 0.008f * (110.f - lowthr);
float red2 = 0.011f * (110.f - lowthr);
// int n = 0;
// int m = 0;
// int p = 0;
// int q = 0;
for (int i = 0; i < W_L * H_L; i++) {
float kLlev = 1.f;
if (cpMul < 0.f) {
lbeta = 1.f; // disabled for negatives values "less contrast"
} else {
float WavCL = std::fabs(WavCoeffs_L[dir][i]);
//reduction amplification: max action between mean / 2 and mean + sigma
// arbitrary coefficient, we can add a slider !!
if (WavCL < mea[0]) {
lbeta = 0.4f * red0;//preserve very low contrast (sky...)
} else if (WavCL < mea[1]) {
lbeta = 0.5f * red1;
} else if (WavCL < mea[2]) {
lbeta = 0.7f * red2;
} else if (WavCL < mea[3]) {
lbeta = 1.f; //standard
} else if (WavCL < mea[4]) {
lbeta = 1.f;
} else if (WavCL < mea[5]) {
lbeta = 0.8f; //+sigma
} else if (WavCL < mea[6]) {
lbeta = 0.6f;
} else if (WavCL < mea[7]) {
lbeta = 0.4f;
} else if (WavCL < mea[8]) {
lbeta = 0.2f; // + 2 sigma
} else if (WavCL < mea[9]) {
lbeta = 0.1f;
} else {
lbeta = 0.0f;
}
}
float scale = 1.f;
float scale2 = 1.f;
float LL100, LL100res, LL100init, kH[maxlvl];
int ii = i / W_L;
int jj = i - ii * W_L;
float LL = labco->L[ii * 2][jj * 2];
LL100 = LL100init = LL / 327.68f;
LL100res = WavCoeffs_L0[i] / 327.68f;
float delta = std::fabs(LL100init - LL100res) / (maxlvl / 2);
for (int ml = 0; ml < maxlvl; ml++) {
if (ml < maxlvl / 2) {
kH[ml] = (LL100res + ml * delta) / LL100res; // fixed a priori max to level middle
} else {
kH[ml] = (LL100init - ml * delta) / LL100res;
}
}
if (useChromAndHue) {
float modhue = varhue[ii][jj];
modchro = varchrom[ii * 2][jj * 2];
// hue chroma skin with initial lab data
scale = 1.f;
if (skinprot > 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); //0 for skin and extand
} else if (skinprot < 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0);
if (scale == 1.f) {
scale = factorHard;
} else {
scale = 1.f;
}
}
}
if (Chutili) {
int i_i = i / W_L;
int j_j = i - i_i * W_L;
float modhue2 = varhue[i_i][j_j];
float valparam = static_cast<float>(ChCurve->getVal(Color::huelab_to_huehsv2(modhue2))) - 0.5f; //get valparam=f(H)
if (valparam > 0.f) {
scale2 = 1.f + 3.f * valparam; //arbitrary value
} else {
scale2 = 1.f + 1.9f * valparam; //near 0 but not zero if curve # 0
}
}
//linear transition HL
float diagacc = 1.f;
float alpha = (1024.f + 15.f * (float) cpMul * scale * scale2 * lbeta * diagacc) / 1024.f ;
if (cp.HSmet && cp.contena) {
// if (cp.HSmet && cp.contena && waOpacityCurveSH) {//waOpacityCurveSH no long use
float aaal = (1.f - alpha) / ((cp.b_lhl - cp.t_lhl) * kH[level]);
float bbal = 1.f - aaal * cp.b_lhl * kH[level];
float aaar = (alpha - 1.f) / (cp.t_rhl - cp.b_rhl) * kH[level];
float bbbr = 1.f - cp.b_rhl * aaar * kH[level];
//linear transition Shadows
float aaalS = (1.f - alpha) / (cp.b_lsl - cp.t_lsl);
float bbalS = 1.f - aaalS * cp.b_lsl;
float aaarS = (alpha - 1.f) / (cp.t_rsl - cp.b_rsl);
float bbbrS = 1.f - cp.b_rsl * aaarS;
if (level <= cp.numlevH) { //in function of levels
if ((LL100 > cp.t_lhl * kH[level] && LL100 < cp.t_rhl * kH[level])) {
kLlev = alpha;
} else if ((LL100 > cp.b_lhl * kH[level] && LL100 <= cp.t_lhl * kH[level])) {
kLlev = aaal * LL100 + bbal;
} else if ((LL100 > cp.t_rhl * kH[level] && LL100 <= cp.b_rhl * kH[level])) {
kLlev = aaar * LL100 + bbbr;
} else {
kLlev = 1.f;
}
}
if (level >= cp.numlevS - 1) {
// if(klevred < 0.f && level >= 3) {//level > 3 to avoid bad use of the curve if user put positives values negatives
if ((LL100 > cp.t_lsl && LL100 < cp.t_rsl)) {
kLlev = alpha;
// n++;
} else if ((LL100 > cp.b_lsl && LL100 <= cp.t_lsl)) {
kLlev = aaalS * LL100 + bbalS;
// m++;
} else if ((LL100 > cp.t_rsl && LL100 <= cp.b_rsl)) {
kLlev = aaarS * LL100 + bbbrS;
// p++;
} else {
kLlev = 1.f;
// q++;
}
}
} else {
kLlev = alpha;
}
WavCoeffs_L[dir][i] *= (kLlev);
}
// printf("lev=%i n=%i m=%i p=%i q=%i\n", level, n, m, p, q);
}
if (waOpacityCurveW) {
cp.opaW = true;
}
if (cp.bam && cp.finena) {
const float effect = cp.sigmadir;
constexpr float offs = 1.f;
float mea[10];
calceffect(level, mean, sigma, mea, effect, offs);
for (int co = 0; co < H_L * W_L; co++) {
float WavCL = std::fabs(WavCoeffs_L[dir][co]);
if (WavCL < mea[0]) {
beta[co] = 0.05f;
} else if (WavCL < mea[1]) {
beta[co] = 0.2f;
} else if (WavCL < mea[2]) {
beta[co] = 0.7f;
} else if (WavCL < mea[3]) {
beta[co] = 1.f; //standard
} else if (WavCL < mea[4]) {
beta[co] = 1.f;
} else if (WavCL < mea[5]) {
beta[co] = 0.8f; //+sigma
} else if (WavCL < mea[6]) {
beta[co] = 0.6f;
} else if (WavCL < mea[7]) {
beta[co] = 0.4f;
} else if (WavCL < mea[8]) {
beta[co] = 0.2f; // + 2 sigma
} else if (WavCL < mea[9]) {
beta[co] = 0.1f;
} else {
beta[co] = 0.01f;
}
}
if (cp.opaW && cp.BAmet == 2) {
int iteration = cp.ite;
int itplus = 7 + iteration;
int itmoins = 7 - iteration;
int med = maxlvl / 2;
int it;
if (level < med) {
it = itmoins;
} else if (level == med) {
it = 7;
} else { /*if (level > med)*/
it = itplus;
}
for (int j = 0; j < it; j++) {
//float bal = cp.balan;//-100 +100
float kba = 1.f;
// if (dir <3) kba= 1.f + bal/600.f;
// if (dir==3) kba = 1.f - bal/300.f;
for (int i = 0; i < W_L * H_L; i++) {
int ii = i / W_L;
int jj = i - ii * W_L;
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
float k1 = 0.3f * (waOpacityCurveW[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16
float k2 = k1 * 2.f;
if (dir < 3) {
kba = 1.f + k1;
}
if (dir == 3) {
kba = 1.f - k2;
}
WavCoeffs_L[dir][i] *= (1.f + (kba - 1.f) * beta[i]);
}
}
}
if (cp.BAmet == 1) {
int iteration = cp.ite;
int itplus = 7 + iteration;
int itmoins = 7 - iteration;
int med = maxlvl / 2;
int it;
if (level < med) {
it = itmoins;
} else if (level == med) {
it = 7;
} else { /*if (level > med)*/
it = itplus;
}
for (int j = 0; j < it; j++) {
float bal = cp.balan;//-100 +100
float kba = 1.f;
// if (dir <3) kba= 1.f + bal/600.f;
// if (dir==3) kba = 1.f - bal/300.f;
for (int i = 0; i < W_L * H_L; i++) {
int ii = i / W_L;
int jj = i - ii * W_L;
float k1 = 600.f;
float k2 = 300.f;
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
constexpr float aa = 4970.f;
constexpr float bb = -397000.f;
constexpr float b0 = 100000.f;
constexpr float a0 = -4970.f;
if (LL100 > 80.f) {
k1 = aa * LL100 + bb;
k2 = 0.5f * k1;
}
if (LL100 < 20.f) {
k1 = a0 * LL100 + b0;
k2 = 0.5f * k1;
}
//k1=600.f;
//k2=300.f;
//k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16
//k2=k1*2.f;
if (dir < 3) {
kba = 1.f + bal / k1;
}
if (dir == 3) {
kba = 1.f - bal / k2;
}
WavCoeffs_L[dir][i] *= (1.f + (kba - 1.f) * beta[i]);
}
}
}
}
// to see each level of wavelet ...level from 0 to 8
// int choicelevel = params->wavelet.Lmethod - 1;
// choicelevel = choicelevel == -1 ? 4 : choicelevel;
}
void ImProcFunctions::ContAllAB(LabImage * labco, int maxlvl, float ** varhue, float **varchrom, float* const* WavCoeffs_ab, float * WavCoeffs_ab0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp,
int W_ab, int H_ab, const bool useChannelA, float *meanab, float *sigmaab)
{
float cpMul = cp.mul[level];
if (cpMul != 0.f && cp.CHmet == 2 && cp.chro != 0.f && cp.chromena) { // cpMul == 0.f or cp.chro = 0.f means all will be multiplied by 1.f, so we can skip this
const float skinprot = params->wavelet.skinprotect;
const float skinprotneg = -skinprot;
const float factorHard = (1.f - skinprotneg / 100.f);
const float cpChrom = cp.chro;
//to adjust increase contrast with local contrast
bool useSkinControl = (skinprot != 0.f);
float mea[10];
float effect = cp.sigmacol;
float betaab;
float offs = 1.f;
calceffect(level, meanab, sigmaab, mea, effect, offs);
for (int i = 0; i < W_ab * H_ab; i++) {
float WavCab = std::fabs(WavCoeffs_ab[dir][i]);
if (WavCab < mea[0]) {
betaab = 0.05f;
} else if (WavCab < mea[1]) {
betaab = 0.2f;
} else if (WavCab < mea[2]) {
betaab = 0.7f;
} else if (WavCab < mea[3]) {
betaab = 1.f; //standard
} else if (WavCab < mea[4]) {
betaab = 1.f;
} else if (WavCab < mea[5]) {
betaab = 0.8f; //+sigma
} else if (WavCab < mea[6]) {
betaab = 0.6f;
} else if (WavCab < mea[7]) {
betaab = 0.4f;
} else if (WavCab < mea[8]) {
betaab = 0.2f; // + 2 sigma
} else if (WavCab < mea[9]) {
betaab = 0.1f;
} else {
betaab = 0.0f;
}
float scale = 1.f;
if (useSkinControl) {
int ii = i / W_ab;
int jj = i - ii * W_ab;
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
float modhue = varhue[ii][jj];
float modchro = varchrom[ii * 2][jj * 2];
// hue chroma skin with initial lab data
if (skinprot > 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0); //0 for skin and extand
} else if (skinprot < 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 0);
scale = (scale == 1.f) ? factorHard : 1.f;
}
}
const float alphaC = (1024.f + 15.f * cpMul * cpChrom * betaab * scale / 50.f) / 1024.f ;
WavCoeffs_ab[dir][i] *= alphaC;
}
}
//Curve chro
float cpMulC = cp.mulC[level];
// if ( (cp.curv || cp.CHSLmet==1) && cp.CHmet!=2 && level < 9 && cpMulC != 0.f) { // cpMulC == 0.f means all will be multiplied by 1.f, so we can skip
if (cp.CHmet != 2 && level < 9 && cpMulC != 0.f && cp.chromena) { // cpMulC == 0.f means all will be multiplied by 1.f, so we can skip
const float skinprot = params->wavelet.skinprotect;
const float skinprotneg = -skinprot;
const float factorHard = (1.f - skinprotneg / 100.f);
bool useSkinControl = (skinprot != 0.f);
float mea[10];
float effect = cp.sigmacol;
float betaab;
float offs = 1.f;
calceffect(level, meanab, sigmaab, mea, effect, offs);
for (int i = 0; i < W_ab * H_ab; i++) {
float WavCab = std::fabs(WavCoeffs_ab[dir][i]);
if (WavCab < mea[0]) {
betaab = 0.05f;
} else if (WavCab < mea[1]) {
betaab = 0.2f;
} else if (WavCab < mea[2]) {
betaab = 0.7f;
} else if (WavCab < mea[3]) {
betaab = 1.f; //standard
} else if (WavCab < mea[4]) {
betaab = 1.f;
} else if (WavCab < mea[5]) {
betaab = 0.8f; //+sigma
} else if (WavCab < mea[6]) {
betaab = 0.6f;
} else if (WavCab < mea[7]) {
betaab = 0.4f;
} else if (WavCab < mea[8]) {
betaab = 0.2f; // + 2 sigma
} else if (WavCab < mea[9]) {
betaab = 0.1f;
} else {
betaab = 0.0f;
}
int ii = i / W_ab;
int jj = i - ii * W_ab;
//WL and W_ab are identical
float scale = 1.f;
float modchro = varchrom[ii * 2][jj * 2];
if (useSkinControl) {
// hue chroma skin with initial lab data
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
float modhue = varhue[ii][jj];
if (skinprot > 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprot, scale, true, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 1); //1 for curve
} else if (skinprot < 0.f) {
Color::SkinSatCbdl2(LL100, modhue, modchro, skinprotneg, scale, false, cp.b_l, cp.t_l, cp.t_r, cp.b_r, 1);
scale = (scale == 1.f) ? factorHard : 1.f;
}
}
float beta = (1024.f + 20.f * cpMulC * scale * betaab) / 1024.f ;
if (beta < 0.02f) {
beta = 0.02f;
}
float kClev = beta;
if (cp.CHmet == 1) {
if (level < cp.chrom) {
//linear for saturated
if ((modchro > cp.t_lsat && modchro < cp.t_rsat)) {
kClev = beta;
} else if ((modchro > cp.b_lsat && modchro <= cp.t_lsat)) {
float aaal = (1.f - beta) / (cp.b_lsat - cp.t_lsat);
float bbal = 1.f - aaal * cp.b_lsat;
kClev = aaal * modchro + bbal;
} else if ((modchro > cp.t_rsat && modchro <= cp.b_rsat)) {
float aaar = (beta - 1.f) / (cp.t_rsat - cp.b_rsat);
float bbbr = 1.f - cp.b_rsat * aaar;
kClev = aaar * modchro + bbbr;
} else {
kClev = 1.f;
}
} else {
//linear for pastel
if ((modchro > cp.t_lpast && modchro < cp.t_rpast)) {
kClev = beta;
} else if ((modchro > cp.b_lpast && modchro <= cp.t_lpast)) {
float aaalS = (1.f - beta) / (cp.b_lpast - cp.t_lpast);
float bbalS = 1.f - aaalS * cp.b_lpast;
kClev = aaalS * modchro + bbalS;
} else if ((modchro > cp.t_rpast && modchro <= cp.b_rpast)) {
float aaarS = (beta - 1.f) / (cp.t_rpast - cp.b_rpast);
float bbbrS = 1.f - cp.b_rpast * aaarS;
kClev = aaarS * modchro + bbbrS;
} else {
kClev = 1.f;
}
}
} else if (cp.CHmet == 0) {
kClev = beta;
}
WavCoeffs_ab[dir][i] *= kClev;
}
}
bool useOpacity;
float mulOpacity = 0.f;
if (useChannelA) {
useOpacity = cp.opaRG;
if (level < 9) {
mulOpacity = cp.mulopaRG[level];
}
} else {
useOpacity = cp.opaBY;
if (level < 9) {
mulOpacity = cp.mulopaBY[level];
}
}
if ((useOpacity && level < 9 && mulOpacity != 0.f) && cp.toningena) { //toning
// if (settings->verbose) {
// printf("Toning enabled\n");
// }
float mea[10];
float effect = cp.sigmaton;
float betaab;
float offs = 1.f;
float protec = 0.01f * (100.f - cp.protab);
float aref1 = cp.a_high;
float bref1 = cp.b_high;
float aref2 = cp.a_low;
float bref2 = cp.b_low;
float kk = 100.f;
float arefplus1 = aref1 + cp.rangeab * kk;
float arefmoins1 = aref1 - cp.rangeab * kk;
float brefplus1 = bref1 + cp.rangeab * kk;
float brefmoins1 = bref1 - cp.rangeab * kk;
float arefplus2 = aref2 + cp.rangeab * kk;
float arefmoins2 = aref2 - cp.rangeab * kk;
float brefplus2 = bref2 + cp.rangeab * kk;
float brefmoins2 = bref2 - cp.rangeab * kk;
calceffect(level, meanab, sigmaab, mea, effect, offs);
for (int co = 0; co < W_ab * H_ab; co++) {
float WavCab = std::fabs(WavCoeffs_ab[dir][co]);
if (WavCab < mea[0]) {
betaab = 0.05f;
} else if (WavCab < mea[1]) {
betaab = 0.2f;
} else if (WavCab < mea[2]) {
betaab = 0.7f;
} else if (WavCab < mea[3]) {
betaab = 1.f; //standard
} else if (WavCab < mea[4]) {
betaab = 1.f;
} else if (WavCab < mea[5]) {
betaab = 0.8f; //+sigma
} else if (WavCab < mea[6]) {
betaab = 0.6f;
} else if (WavCab < mea[7]) {
betaab = 0.4f;
} else if (WavCab < mea[8]) {
betaab = 0.2f; // + 2 sigma
} else if (WavCab < mea[9]) {
betaab = 0.1f;
} else {
betaab = 0.0f;
}
float kreduc1 = 1.f;
float kreduc2 = 1.f;
int ii = co / W_ab;
int jj = co - ii * W_ab;
// cp.protab = 0.f;// always disabled provisory...
if (cp.protab > 0.f) {
if (useChannelA) {
if ((labco->a[ii * 2][jj * 2] > arefmoins1) && (labco->a[ii * 2][jj * 2] < arefplus1)) {
kreduc1 = 0.5f * protec;
if ((labco->a[ii * 2][jj * 2] > 0.8f * arefmoins1) && (labco->a[ii * 2][jj * 2] < 0.8f * arefplus1)) {
kreduc1 = protec;
}
}
} else {
if ((labco->b[ii * 2][jj * 2] > brefmoins1) && (labco->b[ii * 2][jj * 2] < brefplus1)) {
kreduc1 = 0.5f * protec;
if ((labco->b[ii * 2][jj * 2] > 0.8f * brefmoins1) && (labco->b[ii * 2][jj * 2] < 0.8f * brefplus1)) {
kreduc1 = protec;
}
}
}
if (useChannelA) {
if ((labco->a[ii * 2][jj * 2] > arefmoins2) && (labco->a[ii * 2][jj * 2] < arefplus2)) {
kreduc2 = 0.5f * protec;
if ((labco->a[ii * 2][jj * 2] > 0.8f * arefmoins2) && (labco->a[ii * 2][jj * 2] < 0.8f * arefplus2)) {
kreduc2 = protec;
}
}
} else {
if ((labco->b[ii * 2][jj * 2] > brefmoins2) && (labco->b[ii * 2][jj * 2] < brefplus2)) {
kreduc2 = 0.5f * protec;
if ((labco->b[ii * 2][jj * 2] > brefmoins2) && (labco->b[ii * 2][jj * 2] < brefplus2)) {
kreduc2 = protec;
}
}
}
}
// printf("pa1=%f pa2=%f\n", kreduc1, kredu2);
float beta = (1024.f + 50.f * mulOpacity * betaab * kreduc1 * kreduc2) / 1024.f ;
WavCoeffs_ab[dir][co] *= beta;
}
}
if (waOpacityCurveW) {
cp.opaW = true;
}
if (cp.bam && cp.diag) {
if (cp.opaW && cp.BAmet == 2) {
int iteration = cp.ite;
int itplus = 7 + iteration;
int itmoins = 7 - iteration;
int med = maxlvl / 2;
int it;
if (level < med) {
it = itmoins;
} else if (level == med) {
it = 7;
} else { /*if (level > med)*/
it = itplus;
}
for (int j = 0; j < it; j++) {
//float bal = cp.balan;//-100 +100
float kba = 1.f;
// if (dir <3) kba= 1.f + bal/600.f;
// if (dir==3) kba = 1.f - bal/300.f;
for (int i = 0; i < W_ab * H_ab; i++) {
int ii = i / W_ab;
int jj = i - ii * W_ab;
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
float k1 = 0.3f * (waOpacityCurveW[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16
float k2 = k1 * 2.f;
if (dir < 3) {
kba = 1.f + k1;
}
if (dir == 3) {
kba = 1.f - k2;
}
WavCoeffs_ab[dir][i] *= (kba);
}
}
}
if (cp.BAmet == 1) {
int iteration = cp.ite;
int itplus = 7 + iteration;
int itmoins = 7 - iteration;
int med = maxlvl / 2;
int it;
if (level < med) {
it = itmoins;
} else if (level == med) {
it = 7;
} else { /*if (level > med)*/
it = itplus;
}
for (int j = 0; j < it; j++) {
float bal = cp.balan;//-100 +100
float kba = 1.f;
// if (dir <3) kba= 1.f + bal/600.f;
// if (dir==3) kba = 1.f - bal/300.f;
for (int i = 0; i < W_ab * H_ab; i++) {
int ii = i / W_ab;
int jj = i - ii * W_ab;
float k1 = 600.f;
float k2 = 300.f;
float LL100 = labco->L[ii * 2][jj * 2] / 327.68f;
constexpr float aa = 4970.f;
constexpr float bb = -397000.f;
constexpr float b0 = 100000.f;
constexpr float a0 = -4970.f;
if (LL100 > 80.f) {
k1 = aa * LL100 + bb;
k2 = 0.5f * k1;
}
if (LL100 < 20.f) {
k1 = a0 * LL100 + b0;
k2 = 0.5f * k1;
}
//k1=600.f;
//k2=300.f;
//k1=0.3f*(waOpacityCurveW[6.f*LL100]-0.5f);//k1 between 0 and 0.5 0.5==> 1/6=0.16
//k2=k1*2.f;
if (dir < 3) {
kba = 1.f + bal / k1;
}
if (dir == 3) {
kba = 1.f - bal / k2;
}
WavCoeffs_ab[dir][i] *= (kba);
}
}
}
}
// to see each level of wavelet ...level from 0 to 8
int choicelevel = params->wavelet.Lmethod - 1;
choicelevel = choicelevel == -1 ? 4 : choicelevel;
int choiceClevel = 0;
if (params->wavelet.CLmethod == "one") {
choiceClevel = 0;
} else if (params->wavelet.CLmethod == "inf") {
choiceClevel = 1;
} else if (params->wavelet.CLmethod == "sup") {
choiceClevel = 2;
} else if (params->wavelet.CLmethod == "all") {
choiceClevel = 3;
}
int choiceDir = 0;
if (params->wavelet.Dirmethod == "one") {
choiceDir = 1;
} else if (params->wavelet.Dirmethod == "two") {
choiceDir = 2;
} else if (params->wavelet.Dirmethod == "thr") {
choiceDir = 3;
} else if (params->wavelet.Dirmethod == "all") {
choiceDir = 0;
}
int dir1 = (choiceDir == 2) ? 1 : 2;
int dir2 = (choiceDir == 3) ? 1 : 3;
if (choiceClevel < 3) { // not all levels visible, paint residual
if (level == 0) {
if (cp.backm != 2) { // nothing to change when residual is used as background
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab0[i] = 0.f;
}
}
}
}
if (choiceClevel == 0) { // Only one level
if (choiceDir == 0) { // All directions
if (level != choicelevel) { // zero all for the levels != choicelevel
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level == choicelevel
if (choicelevel >= cp.maxilev) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[d][i] = 0.f;
}
}
} else if (level != choicelevel) { // zero all for the levels != choicelevel
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[dir1][i] = WavCoeffs_ab[dir2][i] = 0.f;
}
}
}
} else if (choiceClevel == 1) { // Only below level
if (choiceDir == 0) { // All directions
if (level > choicelevel) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level >= choicelevel
if (level > choicelevel) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[dir1][i] = WavCoeffs_ab[dir2][i] = 0.f;
}
}
}
} else if (choiceClevel == 2) { // Only above level
if (choiceDir == 0) { // All directions
if (level <= choicelevel) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[d][i] = 0.f;
}
}
}
} else { // zero the unwanted directions for level >= choicelevel
if (choicelevel >= cp.maxilev) {
for (int d = 1; d < 4; d++) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[d][i] = 0.f;
}
}
} else if (level <= choicelevel) {
for (int i = 0; i < W_ab * H_ab; i++) {
WavCoeffs_ab[dir1][i] = WavCoeffs_ab[dir2][i] = 0.f;
}
}
}
}
}
}