rawTherapee/rtengine/ipvibrance.cc

761 lines
35 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2011 Jacques Desmis <jdesmis@gmail.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rt_math.h"
//#include <algorithm>
#include "rtengine.h"
#include "improcfun.h"
#include "iccstore.h"
#include "mytime.h"
#include "../rtgui/thresholdselector.h"
#include "curves.h"
#include "color.h"
#ifdef _OPENMP
#include <omp.h>
#endif
using namespace std;
namespace rtengine
{
using namespace procparams;
#define SAT(a,b,c) ((float)max(a,b,c)-(float)min(a,b,c))/(float)max(a,b,c)
extern const Settings* settings;
void fillCurveArrayVib(DiagonalCurve* diagCurve, LUTf &outCurve)
{
if (diagCurve) {
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i <= 0xffff; i++ ) {
// change to [0,1] range
// apply custom/parametric/NURBS curve, if any
// and store result in a temporary array
outCurve[i] = 65535.f * diagCurve->getVal( double(i) / 65535.0 );
}
} else {
outCurve.makeIdentity();
}
}
/*
* Vibrance correction
* copyright (c)2011 Jacques Desmis <jdesmis@gmail.com> and Jean-Christophe Frisch <natureh@free.fr>
*
*/
void ImProcFunctions::vibrance (LabImage* lab)
{
if (!params->vibrance.enabled) {
return;
}
// int skip=1; //scale==1 ? 1 : 16;
bool skinCurveIsSet = false;
DiagonalCurve* dcurve = NULL;
dcurve = new DiagonalCurve (params->vibrance.skintonescurve, CURVES_MIN_POLY_POINTS);
if (dcurve) {
if (!dcurve->isIdentity()) {
skinCurveIsSet = true;
} else {
delete dcurve;
dcurve = NULL;
}
}
if (!skinCurveIsSet && !params->vibrance.pastels && !params->vibrance.saturated) {
if (dcurve) {
delete dcurve;
dcurve = NULL;
}
return;
}
const int width = lab->W;
const int height = lab->H;
#ifdef _DEBUG
MyTime t1e, t2e;
t1e.set();
int negat = 0, moreRGB = 0, negsat = 0, moresat = 0;
#endif
// skin hue curve
// I use diagonal because I think it's better
LUTf skin_curve (65536, 0);
if(skinCurveIsSet) {
fillCurveArrayVib(dcurve, skin_curve);
}
if (dcurve) {
delete dcurve;
dcurve = NULL;
}
// skin_curve.dump("skin_curve");
const float chromaPastel = float(params->vibrance.pastels) / 100.0f;
const float chromaSatur = float(params->vibrance.saturated) / 100.0f;
const float p00 = 0.07f;
const float limitpastelsatur = (static_cast<float>(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f) * (1.0f - p00) + p00;
const float maxdp = (limitpastelsatur - p00) / 4.0f;
const float maxds = (1.0 - limitpastelsatur) / 4.0f;
const float p0 = p00 + maxdp;
const float p1 = p00 + 2.0f * maxdp;
const float p2 = p00 + 3.0f * maxdp;
const float s0 = limitpastelsatur + maxds;
const float s1 = limitpastelsatur + 2.0f * maxds;
const float s2 = limitpastelsatur + 3.0f * maxds;
const float transitionweighting = static_cast<float>(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f;
float chromamean = 0.0f;
if(chromaPastel != chromaSatur) {
//if sliders pastels and saturated are different: transition with a double linear interpolation: between p2 and limitpastelsatur, and between limitpastelsatur and s0
//modify the "mean" point in function of double threshold => differential transition
chromamean = maxdp * (chromaSatur - chromaPastel) / (s0 - p2) + chromaPastel;
// move chromaMean up or down depending on transitionCtrl
if (transitionweighting > 0.0f) {
chromamean = (chromaSatur - chromamean) * transitionweighting + chromamean;
} else if (transitionweighting < 0.0f) {
chromamean = (chromamean - chromaPastel) * transitionweighting + chromamean;
}
}
const float chromaPastel_a = (chromaPastel - chromamean) / (p2 - limitpastelsatur);
const float chromaPastel_b = chromaPastel - chromaPastel_a * p2;
const float chromaSatur_a = (chromaSatur - chromamean) / (s0 - limitpastelsatur);
const float chromaSatur_b = chromaSatur - chromaSatur_a * s0;
const float dhue = 0.15f; //hue transition
const float dchr = 20.0f; //chroma transition
const float skbeg = -0.05f; //begin hue skin
const float skend = 1.60f; //end hue skin
const float xx = 0.5f; //soft : between 0.3 and 1.0
const float ask = 65535.0f / (skend - skbeg);
const float bsk = -skbeg * ask;
const bool highlight = params->toneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated
const bool protectskins = params->vibrance.protectskins;
const bool avoidcolorshift = params->vibrance.avoidcolorshift;
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
//inverse matrix user select
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]}
};
#ifdef _DEBUG
MunsellDebugInfo* MunsDebugInfo = NULL;
if (avoidcolorshift) {
MunsDebugInfo = new MunsellDebugInfo();
}
#pragma omp parallel default(shared) firstprivate(lab, MunsDebugInfo) reduction(+: negat, moreRGB, negsat, moresat) if (multiThread)
#else
#pragma omp parallel default(shared) if (multiThread)
#endif
{
float sathue[5], sathue2[4]; // adjust sat in function of hue
/*
// Fitting limitpastelsatur into the real 0.07->1.0 range
// limitpastelsatur = limitpastelsatur*(1.0f-p00) + p00;
float p0,p1,p2;//adapt limit of pyramid to psThreshold
float s0,s1,s2;
*/
#ifdef _OPENMP
if (settings->verbose && omp_get_thread_num() == 0) {
#else
if (settings->verbose) {
#endif
printf("vibrance: p0=%1.2f p1=%1.2f p2=%1.2f s0=%1.2f s1=%1.2f s2=%1.2f\n", p0, p1, p2, s0, s1, s2);
printf(" pastel=%f satur=%f limit= %1.2f chromamean=%0.5f\n", 1.0f + chromaPastel, 1.0f + chromaSatur, limitpastelsatur, chromamean);
}
#pragma omp for schedule(dynamic, 16)
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
float LL = lab->L[i][j] / 327.68f;
float CC = sqrt(SQR(lab->a[i][j]) + SQR(lab->b[i][j])) / 327.68f;
float HH = xatan2f(lab->b[i][j], lab->a[i][j]);
float satredu = 1.0f; //reduct sat in function of skin
if(protectskins) {
Color::SkinSat (LL, HH, CC, satredu);// for skin colors
}
// here we work on Chromaticity and Hue
// variation of Chromaticity ==> saturation via RGB
// Munsell correction, then conversion to Lab
float Lprov = LL;
float Chprov = CC;
float R, G, B;
float2 sincosval;
if(CC == 0.0f) {
sincosval.y = 1.f;
sincosval.x = 0.0f;
} else {
sincosval.y = lab->a[i][j] / (CC * 327.68f);
sincosval.x = lab->b[i][j] / (CC * 327.68f);
}
#ifdef _DEBUG
bool neg = false;
bool more_rgb = false;
//gamut control : Lab values are in gamut
Color::gamutLchonly(HH, sincosval, Lprov, Chprov, R, G, B, wip, highlight, 0.15f, 0.98f, neg, more_rgb);
if(neg) {
negat++;
}
if(more_rgb) {
moreRGB++;
}
#else
//gamut control : Lab values are in gamut
Color::gamutLchonly(HH, sincosval, Lprov, Chprov, R, G, B, wip, highlight, 0.15f, 0.98f);
#endif
if(Chprov > 6.0f) {
const float saturation = SAT(R, G, B);
if(saturation > 0.0f) {
if(satredu != 1.0f) {
// for skin, no differentiation
sathue [0] = sathue [1] = sathue [2] = sathue [3] = sathue[4] = 1.0f;
sathue2[0] = sathue2[1] = sathue2[2] = sathue2[3] = 1.0f;
} else {
//double pyramid: LL and HH
//I try to take into account: Munsell response (human vision) and Gamut..(less response for red): preferably using Prophoto or WideGamut
//blue: -1.80 -3.14 green = 2.1 3.14 green-yellow=1.4 2.1 red:0 1.4 blue-purple:-0.7 -1.4 purple: 0 -0.7
//these values allow a better and differential response
if(LL < 20.0f) {//more for blue-purple, blue and red modulate
if (/*HH> -3.1415f &&*/ HH < -1.5f ) {
sathue[0] = 1.3f; //blue
sathue[1] = 1.2f;
sathue[2] = 1.1f;
sathue[3] = 1.05f;
sathue[4] = 0.4f;
sathue2[0] = 1.05f;
sathue2[1] = 1.1f ;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
} else if(/*HH>=-1.5f &&*/ HH < -0.7f ) {
sathue[0] = 1.6f; //blue purple 1.2 1.1
sathue[1] = 1.4f;
sathue[2] = 1.3f;
sathue[3] = 1.2f ;
sathue[4] = 0.4f;
sathue2[0] = 1.2f ;
sathue2[1] = 1.15f;
sathue2[2] = 1.1f ;
sathue2[3] = 1.0f;
} else if(/*HH>=-0.7f &&*/ HH < 0.0f ) {
sathue[0] = 1.2f; //purple
sathue[1] = 1.0f;
sathue[2] = 1.0f;
sathue[3] = 1.0f ;
sathue[4] = 0.4f;
sathue2[0] = 1.0f ;
sathue2[1] = 1.0f ;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
}
// else if( HH>= 0.0f && HH<= 1.4f ) {sathue[0]=1.1f;sathue[1]=1.1f;sathue[2]=1.1f;sathue[3]=1.0f ;sathue[4]=0.4f;sathue2[0]=1.0f ;sathue2[1]=1.0f ;sathue2[2]=1.0f ;sathue2[3]=1.0f;}//red 0.8 0.7
else if(/*HH>= 0.0f &&*/ HH <= 1.4f ) {
sathue[0] = 1.3f; //red 0.8 0.7
sathue[1] = 1.2f;
sathue[2] = 1.1f;
sathue[3] = 1.0f ;
sathue[4] = 0.4f;
sathue2[0] = 1.0f ;
sathue2[1] = 1.0f ;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
} else if(/*HH> 1.4f &&*/ HH <= 2.1f ) {
sathue[0] = 1.0f; //green yellow 1.2 1.1
sathue[1] = 1.0f;
sathue[2] = 1.0f;
sathue[3] = 1.0f ;
sathue[4] = 0.4f;
sathue2[0] = 1.0f ;
sathue2[1] = 1.0f ;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
} else { /*if(HH> 2.1f && HH<= 3.1415f)*/
sathue[0] = 1.4f; //green
sathue[1] = 1.3f;
sathue[2] = 1.2f;
sathue[3] = 1.15f;
sathue[4] = 0.4f;
sathue2[0] = 1.15f;
sathue2[1] = 1.1f ;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
}
} else if (LL < 50.0f) { //more for blue and green, less for red and green-yellow
if (/*HH> -3.1415f &&*/ HH < -1.5f ) {
sathue[0] = 1.5f; //blue
sathue[1] = 1.4f;
sathue[2] = 1.3f;
sathue[3] = 1.2f ;
sathue[4] = 0.4f;
sathue2[0] = 1.2f ;
sathue2[1] = 1.1f ;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
} else if(/*HH>=-1.5f &&*/ HH < -0.7f ) {
sathue[0] = 1.3f; //blue purple 1.2 1.1
sathue[1] = 1.2f;
sathue[2] = 1.1f;
sathue[3] = 1.05f;
sathue[4] = 0.4f;
sathue2[0] = 1.05f;
sathue2[1] = 1.05f;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
} else if(/*HH>=-0.7f &&*/ HH < 0.0f ) {
sathue[0] = 1.2f; //purple
sathue[1] = 1.0f;
sathue[2] = 1.0f;
sathue[3] = 1.0f ;
sathue[4] = 0.4f;
sathue2[0] = 1.0f ;
sathue2[1] = 1.0f ;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
}
// else if( HH>= 0.0f && HH<= 1.4f ) {sathue[0]=0.8f;sathue[1]=0.8f;sathue[2]=0.8f;sathue[3]=0.8f ;sathue[4]=0.4f;sathue2[0]=0.8f ;sathue2[1]=0.8f ;sathue2[2]=0.8f ;sathue2[3]=0.8f;}//red 0.8 0.7
else if(/*HH>= 0.0f &&*/ HH <= 1.4f ) {
sathue[0] = 1.1f; //red 0.8 0.7
sathue[1] = 1.0f;
sathue[2] = 0.9f;
sathue[3] = 0.8f ;
sathue[4] = 0.4f;
sathue2[0] = 0.8f ;
sathue2[1] = 0.8f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
} else if(/*HH> 1.4f &&*/ HH <= 2.1f ) {
sathue[0] = 1.1f; //green yellow 1.2 1.1
sathue[1] = 1.1f;
sathue[2] = 1.1f;
sathue[3] = 1.05f;
sathue[4] = 0.4f;
sathue2[0] = 0.9f ;
sathue2[1] = 0.8f ;
sathue2[2] = 0.7f ;
sathue2[3] = 0.6f;
} else { /*if(HH> 2.1f && HH<= 3.1415f)*/
sathue[0] = 1.5f; //green
sathue[1] = 1.4f;
sathue[2] = 1.3f;
sathue[3] = 1.2f ;
sathue[4] = 0.4f;
sathue2[0] = 1.2f ;
sathue2[1] = 1.1f ;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
}
} else if (LL < 80.0f) { //more for green, less for red and green-yellow
if (/*HH> -3.1415f &&*/ HH < -1.5f ) {
sathue[0] = 1.3f; //blue
sathue[1] = 1.2f;
sathue[2] = 1.15f;
sathue[3] = 1.1f ;
sathue[4] = 0.3f;
sathue2[0] = 1.1f ;
sathue2[1] = 1.1f ;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
} else if(/*HH>=-1.5f &&*/ HH < -0.7f ) {
sathue[0] = 1.3f; //blue purple 1.2 1.1
sathue[1] = 1.2f;
sathue[2] = 1.15f;
sathue[3] = 1.1f ;
sathue[4] = 0.3f;
sathue2[0] = 1.1f ;
sathue2[1] = 1.05f;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
} else if(/*HH>=-0.7f &&*/ HH < 0.0f ) {
sathue[0] = 1.2f; //purple
sathue[1] = 1.0f;
sathue[2] = 1.0f ;
sathue[3] = 1.0f ;
sathue[4] = 0.3f;
sathue2[0] = 1.0f ;
sathue2[1] = 1.0f ;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
}
// else if( HH>= 0.0f && HH<= 1.4f ) {sathue[0]=0.8f;sathue[1]=0.8f;sathue[2]=0.8f ;sathue[3]=0.8f ;sathue[4]=0.3f;sathue2[0]=0.8f ;sathue2[1]=0.8f ;sathue2[2]=0.8f ;sathue2[3]=0.8f;}//red 0.8 0.7
else if(/*HH>= 0.0f &&*/ HH <= 1.4f ) {
sathue[0] = 1.1f; //red 0.8 0.7
sathue[1] = 1.0f;
sathue[2] = 0.9f ;
sathue[3] = 0.8f ;
sathue[4] = 0.3f;
sathue2[0] = 0.8f ;
sathue2[1] = 0.8f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
} else if(/*HH> 1.4f &&*/ HH <= 2.1f ) {
sathue[0] = 1.3f; //green yellow 1.2 1.1
sathue[1] = 1.2f;
sathue[2] = 1.1f ;
sathue[3] = 1.05f;
sathue[4] = 0.3f;
sathue2[0] = 1.0f ;
sathue2[1] = 0.9f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.7f;
} else { /*if(HH> 2.1f && HH<= 3.1415f)*/
sathue[0] = 1.6f; //green - even with Prophoto green are too "little" 1.5 1.3
sathue[1] = 1.4f;
sathue[2] = 1.3f ;
sathue[3] = 1.25f;
sathue[4] = 0.3f;
sathue2[0] = 1.25f;
sathue2[1] = 1.2f ;
sathue2[2] = 1.15f;
sathue2[3] = 1.05f;
}
} else { /*if (LL>=80.0f)*/ //more for green-yellow, less for red and purple
if (/*HH> -3.1415f &&*/ HH < -1.5f ) {
sathue[0] = 1.0f; //blue
sathue[1] = 1.0f;
sathue[2] = 0.9f;
sathue[3] = 0.8f;
sathue[4] = 0.2f;
sathue2[0] = 0.8f;
sathue2[1] = 0.8f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
} else if(/*HH>=-1.5f &&*/ HH < -0.7f ) {
sathue[0] = 1.0f; //blue purple 1.2 1.1
sathue[1] = 1.0f;
sathue[2] = 0.9f;
sathue[3] = 0.8f;
sathue[4] = 0.2f;
sathue2[0] = 0.8f;
sathue2[1] = 0.8f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
} else if(/*HH>=-0.7f &&*/ HH < 0.0f ) {
sathue[0] = 1.2f; //purple
sathue[1] = 1.0f;
sathue[2] = 1.0f;
sathue[3] = 0.9f;
sathue[4] = 0.2f;
sathue2[0] = 0.9f;
sathue2[1] = 0.9f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
}
// else if( HH>= 0.0f && HH<= 1.4f ) {sathue[0]=0.8f;sathue[1]=0.8f;sathue[2]=0.8f;sathue[3]=0.8f;sathue[4]=0.2f;sathue2[0]=0.8f;sathue2[1]=0.8f ;sathue2[2]=0.8f ;sathue2[3]=0.8f;}//red 0.8 0.7
else if(/*HH>= 0.0f &&*/ HH <= 1.4f ) {
sathue[0] = 1.1f; //red 0.8 0.7
sathue[1] = 1.0f;
sathue[2] = 0.9f;
sathue[3] = 0.8f;
sathue[4] = 0.2f;
sathue2[0] = 0.8f;
sathue2[1] = 0.8f ;
sathue2[2] = 0.8f ;
sathue2[3] = 0.8f;
} else if(/*HH> 1.4f &&*/ HH <= 2.1f ) {
sathue[0] = 1.6f; //green yellow 1.2 1.1
sathue[1] = 1.5f;
sathue[2] = 1.4f;
sathue[3] = 1.2f;
sathue[4] = 0.2f;
sathue2[0] = 1.1f;
sathue2[1] = 1.05f;
sathue2[2] = 1.0f ;
sathue2[3] = 1.0f;
} else { /*if(HH> 2.1f && HH<= 3.1415f)*/
sathue[0] = 1.4f; //green
sathue[1] = 1.3f;
sathue[2] = 1.2f;
sathue[3] = 1.1f;
sathue[4] = 0.2f;
sathue2[0] = 1.1f;
sathue2[1] = 1.05f;
sathue2[2] = 1.05f;
sathue2[3] = 1.0f;
}
}
}
float chmodpastel, chmodsat;
// variables to improve transitions
float pa, pb;// transition = pa*saturation + pb
float chl00 = chromaPastel * satredu * sathue[4];
float chl0 = chromaPastel * satredu * sathue[0];
float chl1 = chromaPastel * satredu * sathue[1];
float chl2 = chromaPastel * satredu * sathue[2];
float chl3 = chromaPastel * satredu * sathue[3];
float chs0 = chromaSatur * satredu * sathue2[0];
float chs1 = chromaSatur * satredu * sathue2[1];
float chs2 = chromaSatur * satredu * sathue2[2];
float chs3 = chromaSatur * satredu * sathue2[3];
float s3 = 1.0f;
// We handle only positive values here ; improve transitions
if (saturation < p00) {
chmodpastel = chl00 ; //neutral tones
} else if (saturation < p0 ) {
pa = (chl00 - chl0) / (p00 - p0);
pb = chl00 - pa * p00;
chmodpastel = pa * saturation + pb;
} else if (saturation < p1) {
pa = (chl0 - chl1) / (p0 - p1);
pb = chl0 - pa * p0;
chmodpastel = pa * saturation + pb;
} else if (saturation < p2) {
pa = (chl1 - chl2) / (p1 - p2);
pb = chl1 - pa * p1;
chmodpastel = pa * saturation + pb;
} else if (saturation < limitpastelsatur) {
pa = (chl2 - chl3) / (p2 - limitpastelsatur);
pb = chl2 - pa * p2;
chmodpastel = pa * saturation + pb;
} else if (saturation < s0) {
pa = (chl3 - chs0) / (limitpastelsatur - s0) ;
pb = chl3 - pa * limitpastelsatur;
chmodsat = pa * saturation + pb;
} else if (saturation < s1) {
pa = (chs0 - chs1) / (s0 - s1);
pb = chs0 - pa * s0;
chmodsat = pa * saturation + pb;
} else if (saturation < s2) {
pa = (chs1 - chs2) / (s1 - s2);
pb = chs1 - pa * s1;
chmodsat = pa * saturation + pb;
} else {
pa = (chs2 - chs3) / (s2 - s3);
pb = chs2 - pa * s2;
chmodsat = pa * saturation + pb;
}
if(chromaPastel != chromaSatur) {
// Pastels
if(saturation > p2 && saturation < limitpastelsatur) {
float newchromaPastel = chromaPastel_a * saturation + chromaPastel_b;
chmodpastel = newchromaPastel * satredu * sathue[3];
}
// Saturated
if(saturation < s0 && saturation >= limitpastelsatur) {
float newchromaSatur = chromaSatur_a * saturation + chromaSatur_b;
chmodsat = newchromaSatur * satredu * sathue2[0];
}
}// end transition
if (saturation <= limitpastelsatur) {
if (chmodpastel > 2.0f ) {
chmodpastel = 2.0f; //avoid too big values
} else if(chmodpastel < -0.93f) {
chmodpastel = -0.93f; //avoid negative values
}
Chprov *= (1.0f + chmodpastel);
if(Chprov < 6.0f) {
Chprov = 6.0f;
}
} else { //if (saturation > limitpastelsatur)
if (chmodsat > 1.8f ) {
chmodsat = 1.8f; //saturated
} else if(chmodsat < -0.93f) {
chmodsat = -0.93f;
}
Chprov *= 1.0f + chmodsat;
if(Chprov < 6.0f) {
Chprov = 6.0f;
}
}
}
}
bool hhModified = false;
// Vibrance's Skin curve
if(skinCurveIsSet) {
if (HH > skbeg && HH < skend) {
if(Chprov < 60.0f) {//skin hue : todo ==> transition
float HHsk = ask * HH + bsk;
float Hn = (skin_curve[HHsk] - bsk) / ask;
float Hc = (Hn * xx + HH * (1.0f - xx));
HH = Hc;
hhModified = true;
} else if(Chprov < (60.0f + dchr)) { //transition chroma
float HHsk = ask * HH + bsk;
float Hn = (skin_curve[HHsk] - bsk) / ask;
float Hc = (Hn * xx + HH * (1.0f - xx));
float aa = (HH - Hc) / dchr ;
float bb = HH - (60.0f + dchr) * aa;
HH = aa * Chprov + bb;
hhModified = true;
}
}
//transition hue
else if(HH > (skbeg - dhue) && HH <= skbeg && Chprov < (60.0f + dchr * 0.5f)) {
float HHsk = ask * skbeg + bsk;
float Hn = (skin_curve[HHsk] - bsk) / ask;
float Hcc = (Hn * xx + skbeg * (1.0f - xx));
float adh = (Hcc - (skbeg - dhue)) / (dhue);
float bdh = Hcc - adh * skbeg;
HH = adh * HH + bdh;
hhModified = true;
} else if(HH >= skend && HH < (skend + dhue) && Chprov < (60.0f + dchr * 0.5f)) {
float HHsk = ask * skend + bsk;
float Hn = (skin_curve[HHsk] - bsk) / ask;
float Hcc = (Hn * xx + skend * (1.0f - xx));
float adh = (skend + dhue - Hcc) / (dhue);
float bdh = Hcc - adh * skend;
HH = adh * HH + bdh;
hhModified = true;
}
} // end skin hue
//Munsell correction
// float2 sincosval;
if(!avoidcolorshift && hhModified) {
sincosval = xsincosf(HH);
}
float aprovn, bprovn;
bool inGamut;
do {
inGamut = true;
if(avoidcolorshift) {
float correctionHue = 0.0f;
float correctlum = 0.0f;
#ifdef _DEBUG
Color::AllMunsellLch(/*lumaMuns*/false, Lprov, Lprov, HH, Chprov, CC, correctionHue, correctlum, MunsDebugInfo);
#else
Color::AllMunsellLch(/*lumaMuns*/false, Lprov, Lprov, HH, Chprov, CC, correctionHue, correctlum);
#endif
if(correctionHue != 0.f || hhModified) {
sincosval = xsincosf(HH + correctionHue);
hhModified = false;
}
}
aprovn = Chprov * sincosval.y;
bprovn = Chprov * sincosval.x;
float fyy = (0.00862069f * Lprov ) + 0.137932f;
float fxx = (0.002f * aprovn) + fyy;
float fzz = fyy - (0.005f * bprovn);
float xx_ = 65535.f * Color::f2xyz(fxx) * Color::D50x;
// float yy_ = 65535.0f * Color::f2xyz(fyy);
float zz_ = 65535.f * Color::f2xyz(fzz) * Color::D50z;
float yy_ = 65535.f * ((Lprov > Color::epskap) ? fyy * fyy*fyy : Lprov / Color::kappa);
Color::xyz2rgb(xx_, yy_, zz_, R, G, B, wip);
if(R < 0.0f || G < 0.0f || B < 0.0f) {
#ifdef _DEBUG
negsat++;
#endif
Chprov *= 0.98f;
inGamut = false;
}
// if "highlight reconstruction" enabled don't control Gamut for highlights
if((!highlight) && (R > 65535.0f || G > 65535.0f || B > 65535.0f)) {
#ifdef _DEBUG
moresat++;
#endif
Chprov *= 0.98f;
inGamut = false;
}
} while (!inGamut);
//put new values in Lab
lab->L[i][j] = Lprov * 327.68f;
lab->a[i][j] = aprovn * 327.68f;
lab->b[i][j] = bprovn * 327.68f;
}
} // end of parallelization
#ifdef _DEBUG
t2e.set();
if (settings->verbose) {
printf("Vibrance (performed in %d usec):\n", t2e.etime(t1e));
printf(" Gamut: G1negat=%iiter G165535=%iiter G2negsat=%iiter G265535=%iiter\n", negat, moreRGB, negsat, moresat);
if (MunsDebugInfo) {
printf(" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad depass=%u\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass);
}
}
if (MunsDebugInfo) {
delete MunsDebugInfo;
}
#endif
}
}