3966 lines
140 KiB
C++
3966 lines
140 KiB
C++
/*
|
|
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <cmath>
|
|
#include <glib.h>
|
|
#include <glibmm.h>
|
|
|
|
#include "rtengine.h"
|
|
#include "improcfun.h"
|
|
#include "curves.h"
|
|
#include "colorclip.h"
|
|
#include "gauss.h"
|
|
#include "bilateral2.h"
|
|
#include "mytime.h"
|
|
#include "iccstore.h"
|
|
#include "impulse_denoise.h"
|
|
#include "imagesource.h"
|
|
#include "rtthumbnail.h"
|
|
#include "utils.h"
|
|
#include "iccmatrices.h"
|
|
#include "color.h"
|
|
#include "calc_distort.h"
|
|
#include "cplx_wavelet_dec.h"
|
|
#include "boxblur.h"
|
|
#include "rt_math.h"
|
|
#include "EdgePreservingDecomposition.h"
|
|
#include "improccoordinator.h"
|
|
#ifdef _OPENMP
|
|
#include <omp.h>
|
|
#endif
|
|
#undef CLIPD
|
|
#define CLIPD(a) ((a)>0.0f?((a)<1.0f?(a):1.0f):0.0f)
|
|
#define PIX_SORT(a,b) { if ((a)>(b)) {temp=(a);(a)=(b);(b)=temp;} }
|
|
|
|
#define med3(a0,a1,a2,a3,a4,a5,a6,a7,a8,median) { \
|
|
pp[0]=a0; pp[1]=a1; pp[2]=a2; pp[3]=a3; pp[4]=a4; pp[5]=a5; pp[6]=a6; pp[7]=a7; pp[8]=a8; \
|
|
PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \
|
|
PIX_SORT(pp[0],pp[1]); PIX_SORT(pp[3],pp[4]); PIX_SORT(pp[6],pp[7]); \
|
|
PIX_SORT(pp[1],pp[2]); PIX_SORT(pp[4],pp[5]); PIX_SORT(pp[7],pp[8]); \
|
|
PIX_SORT(pp[0],pp[3]); PIX_SORT(pp[5],pp[8]); PIX_SORT(pp[4],pp[7]); \
|
|
PIX_SORT(pp[3],pp[6]); PIX_SORT(pp[1],pp[4]); PIX_SORT(pp[2],pp[5]); \
|
|
PIX_SORT(pp[4],pp[7]); PIX_SORT(pp[4],pp[2]); PIX_SORT(pp[6],pp[4]); \
|
|
PIX_SORT(pp[4],pp[2]); median=pp[4];} //pp4 = median
|
|
|
|
|
|
|
|
|
|
namespace rtengine {
|
|
|
|
using namespace procparams;
|
|
|
|
#undef ABS
|
|
#undef CLIPS
|
|
#undef CLIPC
|
|
|
|
#define ABS(a) ((a)<0?-(a):(a))
|
|
#define CLIPS(a) ((a)>-32768?((a)<32767?(a):32767):-32768)
|
|
#define CLIPC(a) ((a)>-32000?((a)<32000?(a):32000):-32000)
|
|
#define CLIP2(a) ((a)<MAXVAL ? a : MAXVAL )
|
|
#define FCLIP(a) ((a)>0.0?((a)<65535.5?(a):65535.5):0.0)
|
|
|
|
|
|
extern const Settings* settings;
|
|
LUTf ImProcFunctions::cachef;
|
|
LUTf ImProcFunctions::gamma2curve;
|
|
void ImProcFunctions::initCache () {
|
|
|
|
const int maxindex = 65536;
|
|
cachef(maxindex,0/*LUT_CLIP_BELOW*/);
|
|
|
|
gamma2curve(maxindex,0);
|
|
|
|
for (int i=0; i<maxindex; i++) {
|
|
if (i>Color::eps_max) {
|
|
cachef[i] = 327.68*( exp(1.0/3.0 * log((double)i / MAXVALD) ));
|
|
}
|
|
else {
|
|
cachef[i] = 327.68*((Color::kappa*i/MAXVALD+16.0)/116.0);
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<maxindex; i++) {
|
|
gamma2curve[i] = (CurveFactory::gamma2(i/65535.0) * 65535.0);
|
|
}
|
|
}
|
|
|
|
void ImProcFunctions::cleanupCache () {
|
|
|
|
}
|
|
|
|
ImProcFunctions::~ImProcFunctions () {
|
|
|
|
if (monitorTransform!=NULL)
|
|
cmsDeleteTransform (monitorTransform);
|
|
}
|
|
|
|
void ImProcFunctions::setScale (double iscale) {
|
|
scale = iscale;
|
|
}
|
|
|
|
// Called from several threads
|
|
void ImProcFunctions::firstAnalysisThread (Imagefloat* original, Glib::ustring wprofile, unsigned int* histogram, int row_from, int row_to) {
|
|
|
|
TMatrix wprof = iccStore->workingSpaceMatrix (wprofile);
|
|
|
|
lumimul[0] = wprof[1][0];
|
|
lumimul[1] = wprof[1][1];
|
|
lumimul[2] = wprof[1][2];
|
|
|
|
int W = original->width;
|
|
for (int i=row_from; i<row_to; i++) {
|
|
for (int j=0; j<W; j++) {
|
|
|
|
int r = original->r(i,j);
|
|
int g = original->g(i,j);
|
|
int b = original->b(i,j);
|
|
|
|
int y = CLIP((int)(lumimul[0] * r + lumimul[1] * g + lumimul[2] * b)) ;
|
|
|
|
if (histogram) {
|
|
histogram[y]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
void ImProcFunctions::CAT02 (Imagefloat* baseImg, const ProcParams* params)
|
|
{
|
|
const double toxyz[3][3] = {{0.7976749, 0.1351917, 0.0313534},
|
|
{0.2880402, 0.7118741, 0.0000857},
|
|
{0.0000000, 0.0000000, 0.8252100}};
|
|
|
|
const double xyzto[3][3] = {{1.3459433, -0.2556075, -0.0511118},
|
|
{-0.5445989, 1.5081673, 0.0205351},
|
|
{0.0000000, 0.0000000, 1.2118128}};
|
|
int fw = baseImg->width;
|
|
int fh = baseImg->height;
|
|
|
|
double CAM02BB00,CAM02BB01,CAM02BB02,CAM02BB10,CAM02BB11,CAM02BB12,CAM02BB20,CAM02BB21,CAM02BB22;
|
|
double Xxx,Yyy,Zzz;
|
|
// Xxx=1.09844;
|
|
// Yyy=1.0;
|
|
// Zzz=0.355961;
|
|
//params.wb.temperature, params.wb.green, params.wb.method
|
|
double Xxyz, Zxyz;
|
|
// ColorTemp::temp2mulxyz (params->wb.temperature, params->wb.green, params->wb.method, Xxyz, Zxyz);
|
|
ColorTemp::temp2mulxyz (5000.0, 1.0, "Camera", Xxyz, Zxyz);
|
|
|
|
ColorTemp::cieCAT02(Xxx, Yyy, Zzz, CAM02BB00,CAM02BB01,CAM02BB02,CAM02BB10,CAM02BB11,CAM02BB12,CAM02BB20,CAM02BB21,CAM02BB22);
|
|
printf("00=%f 01=%f 11=%f 20=%f 22=%f\n", CAM02BB00,CAM02BB01,CAM02BB11,CAM02BB20,CAM02BB22);
|
|
|
|
|
|
for (int i=0; i<fh; i++) {
|
|
for (int j=0; j<fw; j++) {
|
|
float r = baseImg->r(i,j);
|
|
float g = baseImg->g(i,j);
|
|
float b = baseImg->b(i,j);
|
|
|
|
float x = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b;
|
|
float y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b;
|
|
float z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b;
|
|
float Xcam=CAM02BB00* x +CAM02BB01* y + CAM02BB02* z ;
|
|
float Ycam=CAM02BB10* x +CAM02BB11* y + CAM02BB12* z ;
|
|
float Zcam=CAM02BB20* x +CAM02BB21* y + CAM02BB22* z ;
|
|
baseImg->r(i,j) = xyzto[0][0] * Xcam + xyzto[0][1] * Ycam + xyzto[0][2] * Zcam;
|
|
baseImg->g(i,j) = xyzto[1][0] * Xcam + xyzto[1][1] * Ycam + xyzto[1][2] * Zcam;
|
|
baseImg->b(i,j) = xyzto[2][0] * Xcam + xyzto[2][1] * Ycam + xyzto[2][2] * Zcam;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
void ImProcFunctions::firstAnalysis (Imagefloat* original, const ProcParams* params, LUTu & histogram, double gamma) {
|
|
|
|
// set up monitor transform
|
|
Glib::ustring wprofile = params->icm.working;
|
|
if (monitorTransform)
|
|
cmsDeleteTransform (monitorTransform);
|
|
monitorTransform = NULL;
|
|
|
|
#if !defined(__APPLE__) // No support for monitor profiles on OS X, all data is sRGB
|
|
Glib::ustring monitorProfile=settings->monitorProfile;
|
|
if (settings->autoMonitorProfile) monitorProfile=iccStore->defaultMonitorProfile;
|
|
|
|
cmsHPROFILE monitor = iccStore->getProfile ("file:"+monitorProfile);
|
|
if (monitor) {
|
|
cmsHPROFILE iprof = iccStore->getXYZProfile ();
|
|
lcmsMutex->lock ();
|
|
monitorTransform = cmsCreateTransform (iprof, TYPE_RGB_16, monitor, TYPE_RGB_8, settings->colorimetricIntent,
|
|
cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE ); // NOCACHE is for thread safety, NOOPTIMIZE for precision
|
|
lcmsMutex->unlock ();
|
|
}
|
|
#endif
|
|
// calculate histogram of the y channel needed for contrast curve calculation in exposure adjustments
|
|
|
|
int T = 1;
|
|
#ifdef _OPENMP
|
|
if(multiThread)
|
|
T = omp_get_max_threads();
|
|
#endif
|
|
|
|
unsigned int** hist = new unsigned int* [T];
|
|
for (int i=0; i<T; i++) {
|
|
hist[i] = new unsigned int[65536];
|
|
memset (hist[i], 0, 65536*sizeof(int));
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel if (multiThread)
|
|
{
|
|
int H = original->height;
|
|
int tid = omp_get_thread_num();
|
|
int nthreads = omp_get_num_threads();
|
|
int blk = H/nthreads;
|
|
|
|
if (tid<nthreads-1)
|
|
firstAnalysisThread (original, wprofile, hist[tid], tid*blk, (tid+1)*blk);
|
|
else
|
|
firstAnalysisThread (original, wprofile, hist[tid], tid*blk, H);
|
|
}
|
|
#else
|
|
firstAnalysisThread (original, wprofile, hist[0], 0, original->height);
|
|
#endif
|
|
|
|
histogram.clear();
|
|
for (int j=0; j<T; j++)
|
|
for (int i=0; i<65536; i++)
|
|
histogram[i] += hist[j][i];
|
|
|
|
for (int i=0; i<T; i++)
|
|
delete [] hist[i];
|
|
delete [] hist;
|
|
}
|
|
|
|
// Copyright (c) 2012 Jacques Desmis <jdesmis@gmail.com>
|
|
void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh, int pW, int pwb, LabImage* lab, const ProcParams* params ,
|
|
const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve2,const ColorAppearance & customColCurve3,
|
|
LUTu & histLCAM, LUTu & histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, float** buffer, bool execsharp, double &d)
|
|
{
|
|
if(params->colorappearance.enabled) {
|
|
|
|
#ifdef _DEBUG
|
|
MyTime t1e,t2e;
|
|
t1e.set();
|
|
#endif
|
|
LUTf dLcurve;
|
|
LUTu hist16JCAM;
|
|
bool jp=false;
|
|
float val;
|
|
//preparate for histograms CIECAM
|
|
if(pW!=1){//only with improccoordinator
|
|
dLcurve(65536,0);
|
|
dLcurve.clear();
|
|
hist16JCAM(65536,0);
|
|
hist16JCAM.clear();
|
|
for (int i=0; i<32768; i++) { //# 32768*1.414 approximation maxi for chroma
|
|
val = (double)i / 32767.0;
|
|
dLcurve[i] = CLIPD(val);
|
|
}
|
|
}
|
|
LUTf dCcurve;
|
|
LUTu hist16_CCAM;
|
|
bool chropC=false;
|
|
float valc;
|
|
|
|
if(pW!=1){//only with improccoordinator
|
|
dCcurve(65536,0);
|
|
hist16_CCAM(65536);
|
|
hist16_CCAM.clear();
|
|
for (int i=0; i<48000; i++) { //# 32768*1.414 approximation maxi for chroma
|
|
valc = (double)i / 47999.0;
|
|
dCcurve[i] = CLIPD(valc);
|
|
}
|
|
}
|
|
//end preparate histogram
|
|
int width = lab->W, height = lab->H;
|
|
float minQ=10000.f;
|
|
float minM=10000.f;
|
|
float maxQ= -1000.f;
|
|
float maxM= -1000.f;
|
|
float w_h;
|
|
float a_w;
|
|
float c_;
|
|
float f_l;
|
|
double Yw;
|
|
Yw=1.0;
|
|
double Xw, Zw;
|
|
double f,c,nc,yb,la,xw,yw,zw,f2,c2,nc2,yb2,la2;
|
|
double fl,n,nbb,ncb,aw;
|
|
double xwd,ywd,zwd;
|
|
int alg=0;
|
|
bool algepd=false;
|
|
float sum=0.f;
|
|
|
|
bool ciedata=params->colorappearance.datacie;
|
|
|
|
ColorTemp::temp2mulxyz (params->wb.temperature, params->wb.green, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB
|
|
//viewing condition for surround
|
|
if(params->colorappearance.surround=="Average") { f = 1.00; c = 0.69; nc = 1.00;f2=1.0,c2=0.69,nc2=1.0;}
|
|
else if(params->colorappearance.surround=="Dim"){ f2 = 0.9; c2 = 0.59; nc2 = 0.9;f=1.0,c=0.69,nc=1.0;}
|
|
else if(params->colorappearance.surround=="Dark"){f2 = 0.8; c2 = 0.525;nc2 = 0.8;f=1.0,c=0.69,nc=1.0;}
|
|
else if(params->colorappearance.surround=="ExtremelyDark"){f2 = 0.8; c2 = 0.41;nc2 = 0.8;f=1.0,c=0.69,nc=1.0;}
|
|
|
|
//scene condition for surround
|
|
if(params->colorappearance.surrsource==true) {f = 0.85; c = 0.55; nc = 0.85;}// if user => source image has surround very dark
|
|
//with which algorithme
|
|
if (params->colorappearance.algo=="JC") alg=0;
|
|
else if(params->colorappearance.algo=="JS") alg=1;
|
|
else if(params->colorappearance.algo=="QM") {alg=2;algepd=true;}
|
|
else if(params->colorappearance.algo=="ALL") {alg=3;algepd=true;}
|
|
|
|
bool needJ = (alg==0 || alg==1 || alg==3);
|
|
bool needQ = (alg==2 || alg==3);
|
|
//settings white point of output device - or illuminant viewing
|
|
if(settings->viewingdevice==0) {xwd=96.42;ywd=100.0;zwd=82.52;}//5000K
|
|
else if(settings->viewingdevice==1) {xwd=95.68;ywd=100.0;zwd=92.15;}//5500
|
|
else if(settings->viewingdevice==2) {xwd=95.24;ywd=100.0;zwd=100.81;}//6000
|
|
else if(settings->viewingdevice==3) {xwd=95.04;ywd=100.0;zwd=108.88;}//6500
|
|
else if(settings->viewingdevice==4) {xwd=109.85;ywd=100.0;zwd=35.58;}//tungsten
|
|
else if(settings->viewingdevice==5) {xwd=99.18;ywd=100.0;zwd=67.39;}//fluo F2
|
|
else if(settings->viewingdevice==6) {xwd=95.04;ywd=100.0;zwd=108.75;}//fluo F7
|
|
else if(settings->viewingdevice==7) {xwd=100.96;ywd=100.0;zwd=64.35;}//fluo F11
|
|
|
|
|
|
//settings mean Luminance Y of output device or viewing
|
|
if(settings->viewingdevicegrey==0) {yb2=5.0;}
|
|
else if(settings->viewingdevicegrey==1) {yb2=10.0;}
|
|
else if(settings->viewingdevicegrey==2) {yb2=15.0;}
|
|
else if(settings->viewingdevicegrey==3) {yb2=18.0;}
|
|
else if(settings->viewingdevicegrey==4) {yb2=23.0;}
|
|
else if(settings->viewingdevicegrey==5) {yb2=30.0;}
|
|
else if(settings->viewingdevicegrey==6) {yb2=40.0;}
|
|
|
|
//La and la2 = ambiant luminosity scene and viewing
|
|
la=double(params->colorappearance.adapscen);
|
|
if(pwb==2){
|
|
if(params->colorappearance.autoadapscen) la=adap;
|
|
}
|
|
|
|
la2=double(params->colorappearance.adaplum);
|
|
|
|
// level of adaptation
|
|
double deg=(params->colorappearance.degree)/100.0;
|
|
double pilot=params->colorappearance.autodegree ? 2.0 : deg;
|
|
|
|
//algoritm's params
|
|
float jli=params->colorappearance.jlight;
|
|
float chr=params->colorappearance.chroma;
|
|
float contra=params->colorappearance.contrast;
|
|
float qbri=params->colorappearance.qbright;
|
|
float schr=params->colorappearance.schroma;
|
|
float mchr=params->colorappearance.mchroma;
|
|
float qcontra=params->colorappearance.qcontrast;
|
|
float hue=params->colorappearance.colorh;
|
|
double rstprotection = 100.-params->colorappearance.rstprotection;
|
|
if(schr>0.0) schr=schr/2.0f;//divide sensibility for saturation
|
|
|
|
// extracting datas from 'params' to avoid cache flush (to be confirmed)
|
|
ColorAppearanceParams::eTCModeId curveMode = params->colorappearance.curveMode;
|
|
ColorAppearanceParams::eTCModeId curveMode2 = params->colorappearance.curveMode2;
|
|
bool hasColCurve1 = bool(customColCurve1);
|
|
bool hasColCurve2 = bool(customColCurve2);
|
|
ColorAppearanceParams::eCTCModeId curveMode3 = params->colorappearance.curveMode3;
|
|
bool hasColCurve3 = bool(customColCurve3);
|
|
|
|
|
|
if(CAMBrightCurveJ.dirty || CAMBrightCurveQ.dirty){
|
|
LUTu hist16J;
|
|
LUTu hist16Q;
|
|
if (needJ) {
|
|
hist16J (65536);
|
|
hist16J.clear();
|
|
}
|
|
if (needQ) {
|
|
hist16Q (65536);
|
|
hist16Q.clear();
|
|
}
|
|
float koef=1.0f;//rough correspondence between L and J
|
|
for (int i=0; i<height; i++)
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<width; j++) {//rough correspondence between L and J
|
|
float currL = lab->L[i][j]/327.68f;
|
|
if (currL>95.) koef=1.f;
|
|
else if(currL>85.) koef=0.97f;
|
|
else if(currL>80.) koef=0.93f;
|
|
else if(currL>70.) koef=0.87f;
|
|
else if(currL>60.) koef=0.85f;
|
|
else if(currL>50.) koef=0.8f;
|
|
else if(currL>40.) koef=0.75f;
|
|
else if(currL>30.) koef=0.7f;
|
|
else if(currL>20.) koef=0.7f;
|
|
else if(currL>10.) koef=0.9f;
|
|
else if(currL>0.) koef=1.0f;
|
|
|
|
if (needJ)
|
|
hist16J[CLIP((int)((koef*lab->L[i][j])))]++;//evaluate histogram luminance L # J
|
|
if (needQ)
|
|
hist16Q[CLIP((int) (32768.f*sqrt((koef*(lab->L[i][j]))/32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L
|
|
sum+=koef*lab->L[i][j];//evaluate mean J to calcualte Yb
|
|
}
|
|
//mean=(sum/((endh-begh)*width))/327.68f;//for Yb for all image...if one day "pipette" we can adapt Yb for each zone
|
|
mean=(sum/((height)*width))/327.68f;//for Yb for all image...if one day "pipette" we can adapt Yb for each zone
|
|
|
|
//evaluate lightness, contrast
|
|
if (needJ) {
|
|
if (!CAMBrightCurveJ) {
|
|
CAMBrightCurveJ(65536,0);
|
|
CAMBrightCurveJ.dirty = false;
|
|
}
|
|
ColorTemp::curveJ (jli, contra, 1, CAMBrightCurveJ, hist16J);//lightness and contrast J
|
|
}
|
|
if (needQ) {
|
|
if (!CAMBrightCurveQ) {
|
|
CAMBrightCurveQ(65536,0);
|
|
CAMBrightCurveQ.dirty = false;
|
|
}
|
|
ColorTemp::curveJ (qbri, qcontra, 1, CAMBrightCurveQ, hist16Q);//brightness and contrast Q
|
|
}
|
|
}
|
|
if (mean<15.f) yb=3.0;
|
|
else if(mean<30.f) yb=5.0;
|
|
else if(mean<40.f) yb=10.0;
|
|
else if(mean<45.f) yb=15.0;
|
|
else if(mean<50.f) yb=18.0;
|
|
else if(mean<55.f) yb=23.0;
|
|
else if(mean<60.f) yb=30.0;
|
|
else if(mean<70.f) yb=40.0;
|
|
else if(mean<80.f) yb=60.0;
|
|
else if(mean<90.f) yb=80.0;
|
|
else yb=90.0;
|
|
|
|
int gamu=0;
|
|
bool highlight = params->toneCurve.hrenabled; //Get the value if "highlight reconstruction" is activated
|
|
|
|
if(params->colorappearance.gamut==true) gamu=1;//enabled gamut control
|
|
xw=100.0*Xw;
|
|
yw=100.0*Yw;
|
|
zw=100.0*Zw;
|
|
double xw1,yw1,zw1,xw2,yw2,zw2;
|
|
// settings of WB: scene and viewing
|
|
if(params->colorappearance.wbmodel=="RawT") {xw1=96.46;yw1=100.0;zw1=82.445;xw2=xwd;yw2=ywd;zw2=zwd;} //use RT WB; CAT 02 is used for output device (see prefreneces)
|
|
else if(params->colorappearance.wbmodel=="RawTCAT02") {xw1=xw;yw1=yw;zw1=zw;xw2=xwd;yw2=ywd;zw2=zwd;} // Settings RT WB are used for CAT02 => mix , CAT02 is use for output device (screen: D50 D65, projector: lamp, LED) see preferences
|
|
double cz,wh, pfl;
|
|
ColorTemp::initcam1(gamu, yb, pilot, f, la,xw, yw, zw, n, d, nbb, ncb,cz, aw, wh, pfl, fl, c);
|
|
double nj,dj,nbbj,ncbj,czj,awj,flj;
|
|
ColorTemp::initcam2(gamu,yb2, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj,czj, awj, flj);
|
|
|
|
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(lab,xw1,xw2,yw1,yw2,zw1,zw2,pilot,jli,chr,yb,la,yb2,la2,fl,nc,f,c, height,width,begh, endh,nc2,f2,c2, alg,algepd, gamu, highlight, rstprotection, pW)
|
|
#endif
|
|
{ //matrix for current working space
|
|
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
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]}
|
|
};
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++)
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<width; j++) {
|
|
|
|
float L=lab->L[i][j];
|
|
float a=lab->a[i][j];
|
|
float b=lab->b[i][j];
|
|
float x1,y1,z1;
|
|
double x,y,z;
|
|
double epsil=0.0001;
|
|
//convert Lab => XYZ
|
|
Color::Lab2XYZ(L, a, b, x1, y1, z1);
|
|
// double J, C, h, Q, M, s, aw, fl, wh;
|
|
double J, C, h, Q, M, s;
|
|
|
|
double Jpro,Cpro, hpro, Qpro, Mpro, spro;
|
|
bool t1L=false;
|
|
bool t1B=false;
|
|
bool t2B=false;
|
|
int c1s=0;
|
|
int c1co=0;
|
|
//double n,nbb,ncb,pfl,cz,d;
|
|
x=(double)x1/655.35;
|
|
y=(double)y1/655.35;
|
|
z=(double)z1/655.35;
|
|
//process source==> normal
|
|
ColorTemp::xyz2jchqms_ciecam02( J, C, h,
|
|
Q, M, s, aw, fl, wh,
|
|
x, y, z,
|
|
xw1, yw1, zw1,
|
|
yb, la,
|
|
f, c, nc, pilot, gamu , n, nbb, ncb, pfl, cz, d );
|
|
Jpro=J;
|
|
Cpro=C;
|
|
hpro=h;
|
|
Qpro=Q;
|
|
Mpro=M;
|
|
spro=s;
|
|
w_h=wh+epsil;
|
|
a_w=aw;
|
|
c_=c;
|
|
f_l=fl;
|
|
// we cannot have all algoritms with all chroma curves
|
|
if(alg==1) {
|
|
// Lightness saturation
|
|
Jpro=(CAMBrightCurveJ[(float)(Jpro*327.68)])/327.68;//ligthness CIECAM02 + contrast
|
|
double sres;
|
|
double Sp=spro/100.0;
|
|
double parsat=1.5;
|
|
parsat=1.5;//parsat=1.5 =>saturation ; 1.8 => chroma ; 2.5 => colorfullness (personal evaluation)
|
|
if(schr==-100.0) schr=-99.8;
|
|
ColorTemp::curvecolor(schr, Sp , sres, parsat);
|
|
double coe=pow(fl,0.25);
|
|
float dred=100.f;// in C mode
|
|
float protect_red=80.0f; // in C mode
|
|
dred = 100.0 * sqrt((dred*coe)/Qpro);
|
|
protect_red=100.0 * sqrt((protect_red*coe)/Qpro);
|
|
int sk=0;
|
|
float ko=100.f;
|
|
Color::skinred(Jpro, hpro, sres, Sp, dred, protect_red,sk,rstprotection,ko, spro);
|
|
Qpro= ( 4.0 / c ) * sqrt( Jpro / 100.0 ) * ( aw + 4.0 ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0);
|
|
}
|
|
else if(alg==3 || alg==0 || alg==2) {
|
|
double coef=32760./wh;
|
|
if(alg==3 || alg==2) Qpro=(CAMBrightCurveQ[(float)(Qpro*coef)])/coef;//brightness and contrast
|
|
double Mp, sres;
|
|
double coe=pow(fl,0.25);
|
|
Mp=Mpro/100.0;
|
|
double parsat=2.5;
|
|
if(mchr==-100.0) mchr=-99.8 ;
|
|
if(mchr==100.0) mchr=99.9;
|
|
if(alg==3 || alg==2) ColorTemp::curvecolor(mchr, Mp , sres, parsat); else ColorTemp::curvecolor(0.0, Mp , sres, parsat);//colorfullness
|
|
float dred=100.f;//in C mode
|
|
float protect_red=80.0f;// in C mode
|
|
dred *=coe;//in M mode
|
|
protect_red *=coe;//M mode
|
|
int sk=0;
|
|
float ko=100.f;
|
|
Color::skinred(Jpro, hpro, sres, Mp, dred, protect_red,sk,rstprotection,ko, Mpro);
|
|
Jpro=(100.0* Qpro*Qpro) /(wh*wh);
|
|
Cpro= Mpro/coe;
|
|
spro = 100.0 * sqrt( Mpro / Qpro );
|
|
if(alg!=2) Jpro=(CAMBrightCurveJ[(float)(Jpro*327.68)])/327.68;//ligthness CIECAM02 + contrast
|
|
double Cp;
|
|
double Sp=spro/100.0;
|
|
parsat=1.5;
|
|
if(schr==-100.0) schr=-99.;
|
|
if(schr==100.0) schr=98.;
|
|
if(alg==3) ColorTemp::curvecolor(schr, Sp , sres, parsat); else ColorTemp::curvecolor(0.0, Sp , sres, parsat); //saturation
|
|
dred=100.f;// in C mode
|
|
protect_red=80.0f; // in C mode
|
|
dred = 100.0 * sqrt((dred*coe)/Q);
|
|
protect_red=100.0 * sqrt((protect_red*coe)/Q);
|
|
sk=0;
|
|
Color::skinred(Jpro, hpro, sres, Sp, dred, protect_red,sk,rstprotection,ko, spro);
|
|
//double Q1;
|
|
Qpro= ( 4.0 / c ) * sqrt( Jpro / 100.0 ) * ( aw + 4.0 ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0);
|
|
Cp=Cpro/100.0;
|
|
parsat=1.8;//parsat=1.5 =>saturation ; 1.8 => chroma ; 2.5 => colorfullness (personal evaluation : for not)
|
|
if(chr==-100.0) chr=-99.8;
|
|
if(alg!=2) ColorTemp::curvecolor(chr, Cp , sres, parsat);else ColorTemp::curvecolor(0.0, Cp , sres, parsat); //chroma
|
|
dred=55.f;
|
|
protect_red=30.0f;
|
|
sk=1;
|
|
Color::skinred(Jpro, hpro, sres, Cp, dred, protect_red,sk,rstprotection, ko, Cpro);
|
|
|
|
hpro=hpro+hue;if( hpro < 0.0 ) hpro += 360.0;//hue
|
|
}
|
|
|
|
if (hasColCurve1) {//curve 1 with Lightness and Brightness
|
|
if (curveMode==ColorAppearanceParams::TC_MODE_LIGHT){
|
|
/* float Jj=(float) Jpro*327.68;
|
|
float Jold=Jj;
|
|
const Lightcurve& userColCurve = static_cast<const Lightcurve&>(customColCurve1);
|
|
userColCurve.Apply(Jj);
|
|
Jj=0.7f*(Jj-Jold)+Jold;//divide sensibility
|
|
*/
|
|
float Jj=(float) Jpro*327.68f;
|
|
float Jold=Jj;
|
|
float Jold100=(float) Jpro;
|
|
float redu=25.f;
|
|
float reduc=1.f;
|
|
const Lightcurve& userColCurveJ1 = static_cast<const Lightcurve&>(customColCurve1);
|
|
userColCurveJ1.Apply(Jj);
|
|
if(Jj>Jold) {
|
|
if(Jj<65535.f) {
|
|
if(Jold < 327.68f*redu) Jj=0.3f*(Jj-Jold)+Jold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Jold100)/(100.f-redu),0.f,1.f);
|
|
Jj=0.3f*reduc*(Jj-Jold)+Jold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Jj>10.f) Jj=0.8f*(Jj-Jold)+Jold;
|
|
else if (Jj>=0.f) Jj=0.90f*(Jj-Jold)+Jold;// not zero ==>artifacts
|
|
|
|
|
|
Jpro=(double)(Jj/327.68f);
|
|
t1L=true;
|
|
}
|
|
else if (curveMode==ColorAppearanceParams::TC_MODE_BRIGHT){
|
|
//attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different
|
|
float coef=((aw+4.f)*(4.f/c))/100.f;
|
|
float Qq=(float) Qpro*327.68f*(1.f/coef);
|
|
float Qold100=(float) Qpro/coef;
|
|
|
|
float Qold=Qq;
|
|
float redu=20.f;
|
|
float reduc=1.f;
|
|
|
|
const Brightcurve& userColCurveB1 = static_cast<const Brightcurve&>(customColCurve1);
|
|
userColCurveB1.Apply(Qq);
|
|
if(Qq>Qold) {
|
|
if(Qq<65535.f) {
|
|
if(Qold < 327.68f*redu) Qq=0.25f*(Qq-Qold)+Qold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Qold100)/(100.f-redu),0.f,1.f);
|
|
Qq=0.25f*reduc*(Qq-Qold)+Qold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Qq>10.f) Qq=0.5f*(Qq-Qold)+Qold;
|
|
else if (Qq>=0.f) Qq=0.7f*(Qq-Qold)+Qold;// not zero ==>artifacts
|
|
Qpro=(double)(Qq*(coef)/327.68f);
|
|
Jpro=100.*(Qpro*Qpro)/((4.0/c)*(4.0/c)*(aw+4.0)*(aw+4.0));
|
|
t1B=true;
|
|
}
|
|
}
|
|
|
|
if (hasColCurve2) {//curve 2 with Lightness and Brightness
|
|
if (curveMode2==ColorAppearanceParams::TC_MODE_LIGHT){
|
|
float Jj=(float) Jpro*327.68;
|
|
float Jold=Jj;
|
|
/*
|
|
const Lightcurve& userColCurve = static_cast<const Lightcurve&>(customColCurve2);
|
|
userColCurve.Apply(Jj);
|
|
Jj=0.7f*(Jj-Jold)+Jold;//divide sensibility
|
|
*/
|
|
float Jold100=(float) Jpro;
|
|
float redu=25.f;
|
|
float reduc=1.f;
|
|
const Lightcurve& userColCurveJ2 = static_cast<const Lightcurve&>(customColCurve2);
|
|
userColCurveJ2.Apply(Jj);
|
|
if(Jj>Jold) {
|
|
if(Jj<65535.f) {
|
|
if(Jold < 327.68f*redu) Jj=0.3f*(Jj-Jold)+Jold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Jold100)/(100.f-redu),0.f,1.f);
|
|
Jj=0.3f*reduc*(Jj-Jold)+Jold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Jj>10.f) {if(!t1L)Jj=0.8f*(Jj-Jold)+Jold;else Jj=0.4f*(Jj-Jold)+Jold;}
|
|
else if (Jj>=0.f){if(!t1L)Jj=0.90f*(Jj-Jold)+Jold;else Jj=0.5f*(Jj-Jold)+Jold;}// not zero ==>artifacts
|
|
|
|
Jpro=(double)(Jj/327.68f);
|
|
}
|
|
else if (curveMode2==ColorAppearanceParams::TC_MODE_BRIGHT){ //
|
|
float coef=((aw+4.f)*(4.f/c))/100.f;
|
|
float Qq=(float) Qpro*327.68f*(1.f/coef);
|
|
float Qold100=(float) Qpro/coef;
|
|
|
|
float Qold=Qq;
|
|
float redu=20.f;
|
|
float reduc=1.f;
|
|
|
|
const Brightcurve& userColCurveB2 = static_cast<const Brightcurve&>(customColCurve2);
|
|
userColCurveB2.Apply(Qq);
|
|
if(Qq>Qold) {
|
|
if(Qq<65535.f) {
|
|
if(Qold < 327.68f*redu) Qq=0.25f*(Qq-Qold)+Qold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Qold100)/(100.f-redu),0.f,1.f);
|
|
Qq=0.25f*reduc*(Qq-Qold)+Qold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Qq>10.f) Qq=0.5f*(Qq-Qold)+Qold;
|
|
else if (Qq>=0.f) Qq=0.7f*(Qq-Qold)+Qold;// not zero ==>artifacts
|
|
Qpro=(double)(Qq*(coef)/327.68f);
|
|
Jpro=100.*(Qpro*Qpro)/((4.0/c)*(4.0/c)*(aw+4.0)*(aw+4.0));
|
|
t2B=true;
|
|
|
|
if(t1L){//to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case
|
|
coef=2.f;//adapt Q to J approximation
|
|
Qq=(float) Qpro*coef;
|
|
Qold=Qq;
|
|
const Lightcurve& userColCurveJ1 = static_cast<const Lightcurve&>(customColCurve1);
|
|
userColCurveJ1.Apply(Qq);
|
|
Qq=0.1f*(Qq-Qold)+Qold;//approximative adaptation
|
|
Qpro=(double)(Qq/coef);
|
|
Jpro=100.*(Qpro*Qpro)/((4.0/c)*(4.0/c)*(aw+4.0)*(aw+4.0));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (hasColCurve3) {//curve 3 with chroma saturation colorfullness
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA){
|
|
double parsat=0.8;//0.68;
|
|
double coef=327.68/parsat;
|
|
float Cc=(float) Cpro*coef;
|
|
float Ccold=Cc;
|
|
const Chromacurve& userColCurve = static_cast<const Chromacurve&>(customColCurve3);
|
|
userColCurve.Apply(Cc);
|
|
float dred=55.f;
|
|
float protect_red=30.0f;
|
|
float sk=1;
|
|
float ko=1.f/coef;
|
|
Color::skinred(Jpro, hpro, Cc, Ccold, dred, protect_red,sk,rstprotection,ko, Cpro);
|
|
// Cpro=Cc/coef;
|
|
}
|
|
else if (curveMode3==ColorAppearanceParams::TC_MODE_SATUR){ //
|
|
double parsat=0.8;//0.6
|
|
double coef=327.68/parsat;
|
|
float Ss=(float) spro*coef;
|
|
float Sold=Ss;
|
|
const Saturcurve& userColCurve = static_cast<const Saturcurve&>(customColCurve3);
|
|
userColCurve.Apply(Ss);
|
|
Ss=0.6f*(Ss-Sold)+Sold;//divide sensibility saturation
|
|
double coe=pow(fl,0.25);
|
|
float dred=100.f;// in C mode
|
|
float protect_red=80.0f; // in C mode
|
|
dred = 100.0 * sqrt((dred*coe)/Qpro);
|
|
protect_red=100.0 * sqrt((protect_red*coe)/Qpro);
|
|
int sk=0;
|
|
float ko=1.f/coef;
|
|
Color::skinred(Jpro, hpro, Ss, Sold, dred, protect_red,sk,rstprotection,ko, spro);
|
|
Qpro= ( 4.0 / c ) * sqrt( Jpro / 100.0 ) * ( aw + 4.0 ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0);
|
|
c1s=1;
|
|
|
|
}
|
|
else if (curveMode3==ColorAppearanceParams::TC_MODE_COLORF){ //
|
|
double parsat=0.8;//0.68;
|
|
double coef=327.68/parsat;
|
|
float Mm=(float) Mpro*coef;
|
|
float Mold=Mm;
|
|
const Colorfcurve& userColCurve = static_cast<const Colorfcurve&>(customColCurve3);
|
|
userColCurve.Apply(Mm);
|
|
double coe=pow(fl,0.25);
|
|
float dred=100.f;//in C mode
|
|
float protect_red=80.0f;// in C mode
|
|
dred *=coe;//in M mode
|
|
protect_red *=coe;
|
|
int sk=0;
|
|
float ko=1.f/coef;
|
|
Color::skinred(Jpro, hpro, Mm, Mold, dred, protect_red,sk,rstprotection,ko, Mpro);
|
|
Cpro= Mpro/coe;
|
|
c1co=1;
|
|
}
|
|
}
|
|
//to retrieve the correct values of variables
|
|
if(t2B && t1B) Jpro=(100.0* Qpro*Qpro) /(wh*wh);// for brightness curve
|
|
if(c1s==1) {
|
|
Qpro= ( 4.0 / c ) * sqrt( Jpro / 100.0 ) * ( aw + 4.0 ) ;//for saturation curve
|
|
Cpro=(spro*spro*Qpro)/(10000.0);
|
|
}
|
|
if(c1co==1) { double coe=pow(fl,0.25);Cpro= Mpro/coe;} // for colorfullness curve
|
|
//retrieve values C,J...s
|
|
C=Cpro;
|
|
J=Jpro;
|
|
Q=Qpro;
|
|
M=Mpro;
|
|
h=hpro;
|
|
s=spro;
|
|
|
|
if(params->colorappearance.tonecie || settings->autocielab){//use pointer for tonemapping with CIECAM and also sharpening , defringe, contrast detail
|
|
// if(params->colorappearance.tonecie || params->colorappearance.sharpcie){//use pointer for tonemapping with CIECAM and also sharpening , defringe, contrast detail
|
|
float Qred= ( 4.0 / c) * ( aw + 4.0 );//estimate Q max if J=100.0
|
|
ncie->Q_p[i][j]=(float)Q+epsil;//epsil to avoid Q=0
|
|
ncie->M_p[i][j]=(float)M+epsil;
|
|
ncie->J_p[i][j]=(float)J+epsil;
|
|
ncie->h_p[i][j]=(float)h;
|
|
ncie->C_p[i][j]=(float)C+epsil;
|
|
// ncie->s_p[i][j]=(float)s;
|
|
ncie->sh_p[i][j]=(float) 32768.*(( 4.0 / c )*sqrt( J / 100.0 ) * ( aw + 4.0 ))/Qred ;
|
|
// ncie->ch_p[i][j]=(float) 327.68*C;
|
|
if(ncie->Q_p[i][j]<minQ) minQ=ncie->Q_p[i][j];//minima
|
|
if(ncie->Q_p[i][j]>maxQ) maxQ=ncie->Q_p[i][j];//maxima
|
|
}
|
|
if(!params->colorappearance.tonecie || !settings->autocielab || !params->edgePreservingDecompositionUI.enabled ){
|
|
|
|
// if(!params->edgePreservingDecompositionUI.enabled || !params->colorappearance.tonecie || !settings->autocielab){
|
|
// if(!params->edgePreservingDecompositionUI.enabled || !params->colorappearance.tonecie || !params->colorappearance.sharpcie){
|
|
int posl, posc;
|
|
double brli=327.;
|
|
double chsacol=327.;
|
|
int libr=0;
|
|
int colch=0;
|
|
if(curveMode==ColorAppearanceParams::TC_MODE_BRIGHT) {brli=70.0; libr=1;}
|
|
else if(curveMode==ColorAppearanceParams::TC_MODE_LIGHT) {brli=327.;libr=0;}
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA) {chsacol=327.;colch=0;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_SATUR) {chsacol=450.0;colch=1;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_COLORF) {chsacol=327.0;colch=2;}
|
|
if(ciedata) {
|
|
// Data for J Q M s and C histograms
|
|
//update histogram
|
|
jp=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(libr==1) posl=CLIP((int)(Q*brli));//40.0 to 100.0 approximative factor for Q - 327 for J
|
|
else if(libr==0) posl=CLIP((int)(J*brli));//327 for J
|
|
hist16JCAM[posl]++;
|
|
}
|
|
chropC=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(colch==0) posc=CLIP((int)(C*chsacol));//450.0 approximative factor for s 320 for M
|
|
else if(colch==1) posc=CLIP((int)(s*chsacol));
|
|
else if(colch==2) posc=CLIP((int)(M*chsacol));
|
|
hist16_CCAM[posc]++;
|
|
}
|
|
}
|
|
double xx,yy,zz;
|
|
//double nj, nbbj, ncbj, flj, czj, dj, awj;
|
|
//process normal==> viewing
|
|
ColorTemp::jch2xyz_ciecam02( xx, yy, zz,
|
|
J, C, h,
|
|
xw2, yw2, zw2,
|
|
yb2, la2,
|
|
f2, c2, nc2, gamu, nj, nbbj, ncbj, flj, czj, dj, awj);
|
|
x=(float)xx*655.35;
|
|
y=(float)yy*655.35;
|
|
z=(float)zz*655.35;
|
|
float Ll,aa,bb;
|
|
//convert xyz=>lab
|
|
Color::XYZ2Lab(x, y, z, Ll, aa, bb);
|
|
lab->L[i][j]=Ll;
|
|
lab->a[i][j]=aa;
|
|
lab->b[i][j]=bb;
|
|
// gamut control in Lab mode; I must study how to do with cIECAM only
|
|
if(gamu==1) {
|
|
float R,G,B;
|
|
float HH, Lprov1, Chprov1;
|
|
Lprov1=lab->L[i][j]/327.68f;
|
|
Chprov1=sqrt(SQR(lab->a[i][j]/327.68f) + SQR(lab->b[i][j]/327.68f));
|
|
HH=atan2(lab->b[i][j],lab->a[i][j]);
|
|
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
|
|
lab->L[i][j]=Lprov1*327.68f;
|
|
lab->a[i][j]=327.68f*Chprov1*cos(HH);
|
|
lab->b[i][j]=327.68f*Chprov1*sin(HH);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// End of parallelization
|
|
if(!params->edgePreservingDecompositionUI.enabled || !params->colorappearance.tonecie || !settings->autocielab){//normal
|
|
//if(!params->edgePreservingDecompositionUI.enabled || !params->colorappearance.tonecie || !params->colorappearance.sharpcie){//normal
|
|
|
|
if(ciedata) {
|
|
//update histogram J
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=32768; i++) {//
|
|
if (jp) {
|
|
float hval = dLcurve[i];
|
|
int hi = (int)(255.0*CLIPD(hval)); //
|
|
histLCAM[hi] += hist16JCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=48000; i++) {//
|
|
if (chropC) {
|
|
float hvalc = dCcurve[i];
|
|
int hic = (int)(255.0*CLIPD(hvalc)); //
|
|
histCCAM[hic] += hist16_CCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
if (settings->verbose) {
|
|
t2e.set();
|
|
printf("CIECAM02 performed in %d usec:\n", t2e.etime(t1e));
|
|
// printf("minc=%f maxc=%f minj=%f maxj=%f\n",minc,maxc,minj,maxj);
|
|
}
|
|
#endif
|
|
|
|
if(settings->autocielab) {
|
|
//if(params->colorappearance.sharpcie) {
|
|
|
|
//all this treatments reduce artifacts, but can lead to slightly different results
|
|
if(params->defringe.enabled) if(execsharp) ImProcFunctions::defringecam (ncie);//
|
|
|
|
if(params->colorappearance.badpixsl > 0) if(execsharp){ int mode=params->colorappearance.badpixsl;
|
|
ImProcFunctions::badpixcam (ncie, 3.4, 5, mode);//for bad pixels
|
|
}
|
|
|
|
|
|
if (params->sharpenMicro.enabled)if(execsharp) ImProcFunctions::MLmicrocontrastcam(ncie);
|
|
|
|
if(params->sharpening.enabled) if(execsharp) {ImProcFunctions::sharpeningcam (ncie, (float**)buffer);} //sharpening adapted to CIECAM
|
|
|
|
if(params->dirpyrequalizer.enabled) if(execsharp) dirpyr_equalizercam(ncie, ncie->sh_p, ncie->sh_p, ncie->W, ncie->H, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold, true);//contrast by detail adapted to CIECAM
|
|
|
|
float Qredi= ( 4.0 / c_) * ( a_w + 4.0 );
|
|
float co_e=(pow(f_l,0.25f));
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(height,width, Qredi,a_w,c_)
|
|
#endif
|
|
{
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++) // update CieImages with new values after sharpening, defringe, contrast by detail level
|
|
for (int j=0; j<width; j++) {
|
|
float interm=Qredi*ncie->sh_p[i][j]/(32768.f);
|
|
ncie->J_p[i][j]=100.0* interm*interm/((a_w+4.)*(a_w+4.)*(4./c_)*(4./c_));
|
|
ncie->Q_p[i][j]=( 4.0 / c_) * ( a_w + 4.0 ) * sqrt(ncie->J_p[i][j]/100.f);
|
|
ncie->M_p[i][j]=ncie->C_p[i][j]*co_e;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((params->colorappearance.tonecie || (params->colorappearance.tonecie && params->edgePreservingDecompositionUI.enabled)) || (params->sharpening.enabled && settings->autocielab)
|
|
|| (params->dirpyrequalizer.enabled && settings->autocielab) ||(params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab)
|
|
|| (params->colorappearance.badpixsl > 0 && settings->autocielab)) {
|
|
|
|
if(params->edgePreservingDecompositionUI.enabled && params->colorappearance.tonecie && algepd) ImProcFunctions::EPDToneMapCIE(ncie, a_w, c_, w_h, width, height, begh, endh, minQ, maxQ, Iterates, scale );
|
|
//EPDToneMapCIE adapted to CIECAM
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(lab,xw2,yw2,zw2,chr,yb,la2,yb2, height,width,begh, endh, nc2,f2,c2, gamu, highlight,pW)
|
|
#endif
|
|
{
|
|
TMatrix wiprofa = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
double wipa[3][3] = {
|
|
{wiprofa[0][0],wiprofa[0][1],wiprofa[0][2]},
|
|
{wiprofa[1][0],wiprofa[1][1],wiprofa[1][2]},
|
|
{wiprofa[2][0],wiprofa[2][1],wiprofa[2][2]}
|
|
};
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++) // update CIECAM with new values after tone-mapping
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<width; j++) {
|
|
double xx,yy,zz;
|
|
float x,y,z;
|
|
const float eps=0.0001;
|
|
float co_e=(pow(f_l,0.25f))+eps;
|
|
// if(params->edgePreservingDecompositionUI.enabled) ncie->J_p[i][j]=(100.0* ncie->Q_p[i][j]*ncie->Q_p[i][j])/(w_h*w_h);
|
|
if(params->edgePreservingDecompositionUI.enabled) ncie->J_p[i][j]=(100.0* ncie->Q_p[i][j]*ncie->Q_p[i][j])/SQR((4./c)*(aw+4.));
|
|
|
|
ncie->C_p[i][j] =(ncie->M_p[i][j])/co_e;
|
|
//show histogram in CIECAM mode (Q,J, M,s,C)
|
|
int posl, posc;
|
|
double brli=327.;
|
|
double chsacol=327.;
|
|
int libr=0;
|
|
int colch=0;
|
|
float sa_t;
|
|
if(curveMode==ColorAppearanceParams::TC_MODE_BRIGHT) {brli=70.0; libr=1;}
|
|
else if(curveMode==ColorAppearanceParams::TC_MODE_LIGHT) {brli=327.;libr=0;}
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA) {chsacol=327.;colch=0;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_SATUR) {chsacol=450.0;colch=1;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_COLORF) {chsacol=327.0;colch=2;}
|
|
if(ciedata) {
|
|
// Data for J Q M s and C histograms
|
|
//update histogram
|
|
jp=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(libr==1) posl=CLIP((int)(ncie->Q_p[i][j]*brli));//40.0 to 100.0 approximative factor for Q - 327 for J
|
|
else if(libr==0) posl=CLIP((int)(ncie->J_p[i][j]*brli));//327 for J
|
|
hist16JCAM[posl]++;
|
|
}
|
|
chropC=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(colch==0) posc=CLIP((int)(ncie->C_p[i][j]*chsacol));//450.0 approximative factor for s 320 for M
|
|
else if(colch==1) {sa_t=100.f*sqrt(ncie->C_p[i][j]/ncie->Q_p[i][j]); posc=CLIP((int)(sa_t*chsacol));}//Q_p always > 0
|
|
else if(colch==2) posc=CLIP((int)(ncie->M_p[i][j]*chsacol));
|
|
hist16_CCAM[posc]++;
|
|
}
|
|
}
|
|
//end histograms
|
|
// double nd, nbbd, ncbd, fld, czd, dd, awd;
|
|
ColorTemp::jch2xyz_ciecam02( xx, yy, zz,
|
|
ncie->J_p[i][j], ncie->C_p[i][j], ncie->h_p[i][j],
|
|
xw2, yw2, zw2,
|
|
yb2, la2,
|
|
f2, c2, nc2, gamu, nj, nbbj, ncbj, flj, czj, dj, awj);
|
|
x=(float)xx*655.35;
|
|
y=(float)yy*655.35;
|
|
z=(float)zz*655.35;
|
|
float Ll,aa,bb;
|
|
//convert xyz=>lab
|
|
Color::XYZ2Lab(x, y, z, Ll, aa, bb);
|
|
lab->L[i][j]=Ll;
|
|
lab->a[i][j]=aa;
|
|
lab->b[i][j]=bb;
|
|
if(gamu==1) {
|
|
float R,G,B;
|
|
float HH, Lprov1, Chprov1;
|
|
Lprov1=lab->L[i][j]/327.68f;
|
|
Chprov1=sqrt(SQR(lab->a[i][j]/327.68f) + SQR(lab->b[i][j]/327.68f));
|
|
HH=atan2(lab->b[i][j],lab->a[i][j]);
|
|
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wipa, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wipa, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
|
|
lab->L[i][j]=Lprov1*327.68f;
|
|
lab->a[i][j]=327.68f*Chprov1*cos(HH);
|
|
lab->b[i][j]=327.68f*Chprov1*sin(HH);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
//end parallelization
|
|
//show CIECAM histograms
|
|
if(ciedata) {
|
|
//update histogram J and Q
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=32768; i++) {//
|
|
if (jp) {
|
|
float hval = dLcurve[i];
|
|
int hi = (int)(255.0*CLIPD(hval)); //
|
|
histLCAM[hi] += hist16JCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
//update color histogram M,s,C
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=48000; i++) {//
|
|
if (chropC) {
|
|
float hvalc = dCcurve[i];
|
|
int hic = (int)(255.0*CLIPD(hvalc)); //
|
|
histCCAM[hic] += hist16_CCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
//end CIECAM
|
|
|
|
|
|
// Copyright (c) 2012 Jacques Desmis <jdesmis@gmail.com>
|
|
void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int endh, int pW, int pwb, LabImage* lab, const ProcParams* params,
|
|
const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve2,const ColorAppearance & customColCurve3,
|
|
LUTu & histLCAM, LUTu & histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, float** buffer, bool execsharp, float &d)
|
|
{
|
|
if(params->colorappearance.enabled) {
|
|
//printf("ciecam float\n");
|
|
#ifdef _DEBUG
|
|
MyTime t1e,t2e;
|
|
t1e.set();
|
|
#endif
|
|
LUTf dLcurve;
|
|
LUTu hist16JCAM;
|
|
bool jp=false;
|
|
float val;
|
|
//preparate for histograms CIECAM
|
|
if(pW!=1){//only with improccoordinator
|
|
dLcurve(65536, 0);
|
|
dLcurve.clear();
|
|
hist16JCAM(65536);
|
|
hist16JCAM.clear();
|
|
for (int i=0; i<32768; i++) { //# 32768*1.414 approximation maxi for chroma
|
|
val = (double)i / 32767.0;
|
|
dLcurve[i] = CLIPD(val);
|
|
}
|
|
}
|
|
LUTf dCcurve(65536,0);
|
|
LUTu hist16_CCAM(65536);
|
|
bool chropC=false;
|
|
float valc;
|
|
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<48000; i++) { //# 32768*1.414 approximation maxi for chroma
|
|
valc = (double)i / 47999.0;
|
|
dCcurve[i] = CLIPD(valc);
|
|
}
|
|
hist16_CCAM.clear();
|
|
}
|
|
//end preparate histogram
|
|
int width = lab->W, height = lab->H;
|
|
float minQ=10000.f;
|
|
float minM=10000.f;
|
|
float maxQ= -1000.f;
|
|
float maxM= -1000.f;
|
|
float w_h;
|
|
float a_w;
|
|
float c_;
|
|
float f_l;
|
|
float Yw;
|
|
Yw=1.0;
|
|
double Xw, Zw;
|
|
float f,nc,yb,la,c,xw,yw,zw,f2,c2,nc2,yb2,la2;
|
|
float fl,n,nbb,ncb,aw;//d
|
|
float xwd,ywd,zwd;
|
|
int alg=0;
|
|
bool algepd=false;
|
|
float sum=0.f;
|
|
|
|
bool ciedata=params->colorappearance.datacie;
|
|
|
|
ColorTemp::temp2mulxyz (params->wb.temperature, params->wb.green, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB
|
|
//viewing condition for surround
|
|
if(params->colorappearance.surround=="Average") { f = 1.00f; c = 0.69f; nc = 1.00f;f2=1.0f,c2=0.69f,nc2=1.0f;}
|
|
else if(params->colorappearance.surround=="Dim"){ f2 = 0.9f; c2 = 0.59f; nc2 = 0.9f;f=1.0f,c=0.69f,nc=1.0f;}
|
|
else if(params->colorappearance.surround=="Dark"){f2 = 0.8f; c2 = 0.525f;nc2 = 0.8f;f=1.0f,c=0.69f,nc=1.0f;}
|
|
else if(params->colorappearance.surround=="ExtremelyDark"){f2 = 0.8f; c2 = 0.41f;nc2 = 0.8f;f=1.0f,c=0.69f,nc=1.0f;}
|
|
|
|
//scene condition for surround
|
|
if(params->colorappearance.surrsource==true) {f = 0.85f; c = 0.55f; nc = 0.85f;}// if user => source image has surround very dark
|
|
//with which algorithm
|
|
if (params->colorappearance.algo=="JC") alg=0;
|
|
else if(params->colorappearance.algo=="JS") alg=1;
|
|
else if(params->colorappearance.algo=="QM") {alg=2;algepd=true;}
|
|
else if(params->colorappearance.algo=="ALL") {alg=3;algepd=true;}
|
|
|
|
bool needJ = (alg==0 || alg==1 || alg==3);
|
|
bool needQ = (alg==2 || alg==3);
|
|
//settings white point of output device - or illuminant viewing
|
|
if(settings->viewingdevice==0) {xwd=96.42f;ywd=100.0f;zwd=82.52f;}//5000K
|
|
else if(settings->viewingdevice==1) {xwd=95.68f;ywd=100.0f;zwd=92.15f;}//5500
|
|
else if(settings->viewingdevice==2) {xwd=95.24f;ywd=100.0f;zwd=100.81f;}//6000
|
|
else if(settings->viewingdevice==3) {xwd=95.04f;ywd=100.0f;zwd=108.88f;}//6500
|
|
else if(settings->viewingdevice==4) {xwd=109.85f;ywd=100.0f;zwd=35.58f;}//tungsten
|
|
else if(settings->viewingdevice==5) {xwd=99.18f;ywd=100.0f;zwd=67.39f;}//fluo F2
|
|
else if(settings->viewingdevice==6) {xwd=95.04f;ywd=100.0f;zwd=108.75f;}//fluo F7
|
|
else if(settings->viewingdevice==7) {xwd=100.96f;ywd=100.0f;zwd=64.35f;}//fluo F11
|
|
|
|
|
|
//settings mean Luminance Y of output device or viewing
|
|
if(settings->viewingdevicegrey==0) {yb2=5.0f;}
|
|
else if(settings->viewingdevicegrey==1) {yb2=10.0f;}
|
|
else if(settings->viewingdevicegrey==2) {yb2=15.0f;}
|
|
else if(settings->viewingdevicegrey==3) {yb2=18.0f;}
|
|
else if(settings->viewingdevicegrey==4) {yb2=23.0f;}
|
|
else if(settings->viewingdevicegrey==5) {yb2=30.0f;}
|
|
else if(settings->viewingdevicegrey==6) {yb2=40.0f;}
|
|
|
|
//La and la2 = ambiant luminosity scene and viewing
|
|
la=float(params->colorappearance.adapscen);
|
|
if(pwb==2){
|
|
if(params->colorappearance.autoadapscen) la=adap;
|
|
}
|
|
|
|
la2=float(params->colorappearance.adaplum);
|
|
|
|
// level of adaptation
|
|
float deg=(params->colorappearance.degree)/100.0f;
|
|
float pilot=params->colorappearance.autodegree ? 2.0f : deg;
|
|
|
|
//algoritm's params
|
|
float jli=params->colorappearance.jlight;
|
|
float chr=params->colorappearance.chroma;
|
|
float contra=params->colorappearance.contrast;
|
|
float qbri=params->colorappearance.qbright;
|
|
float schr=params->colorappearance.schroma;
|
|
float mchr=params->colorappearance.mchroma;
|
|
float qcontra=params->colorappearance.qcontrast;
|
|
float hue=params->colorappearance.colorh;
|
|
float rstprotection = 100.-params->colorappearance.rstprotection;
|
|
if(schr>0.0) schr=schr/2.0f;//divide sensibility for saturation
|
|
|
|
// extracting datas from 'params' to avoid cache flush (to be confirmed)
|
|
ColorAppearanceParams::eTCModeId curveMode = params->colorappearance.curveMode;
|
|
ColorAppearanceParams::eTCModeId curveMode2 = params->colorappearance.curveMode2;
|
|
bool hasColCurve1 = bool(customColCurve1);
|
|
bool hasColCurve2 = bool(customColCurve2);
|
|
ColorAppearanceParams::eCTCModeId curveMode3 = params->colorappearance.curveMode3;
|
|
bool hasColCurve3 = bool(customColCurve3);
|
|
|
|
|
|
if(CAMBrightCurveJ.dirty || CAMBrightCurveQ.dirty){
|
|
LUTu hist16J;
|
|
LUTu hist16Q;
|
|
if (needJ) {
|
|
hist16J (65536);
|
|
hist16J.clear();
|
|
}
|
|
if (needQ) {
|
|
hist16Q (65536);
|
|
hist16Q.clear();
|
|
}
|
|
float koef=1.0f;//rough correspondence between L and J
|
|
for (int i=0; i<height; i++)
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<width; j++) {//rough correspondence between L and J
|
|
float currL = lab->L[i][j]/327.68f;
|
|
if (currL>95.) koef=1.f;
|
|
else if(currL>85.) koef=0.97f;
|
|
else if(currL>80.) koef=0.93f;
|
|
else if(currL>70.) koef=0.87f;
|
|
else if(currL>60.) koef=0.85f;
|
|
else if(currL>50.) koef=0.8f;
|
|
else if(currL>40.) koef=0.75f;
|
|
else if(currL>30.) koef=0.7f;
|
|
else if(currL>20.) koef=0.7f;
|
|
else if(currL>10.) koef=0.9f;
|
|
else if(currL>0.) koef=1.0f;
|
|
|
|
if (needJ)
|
|
hist16J[CLIP((int)((koef*lab->L[i][j])))]++;//evaluate histogram luminance L # J
|
|
if (needQ)
|
|
hist16Q[CLIP((int) (32768.f*sqrt((koef*(lab->L[i][j]))/32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L
|
|
sum+=koef*lab->L[i][j];//evaluate mean J to calculate Yb
|
|
}
|
|
//mean=(sum/((endh-begh)*width))/327.68f;//for Yb for all image...if one day "pipette" we can adapt Yb for each zone
|
|
mean=(sum/((height)*width))/327.68f;//for Yb for all image...if one day "pipette" we can adapt Yb for each zone
|
|
|
|
//evaluate lightness, contrast
|
|
if (needJ) {
|
|
if (!CAMBrightCurveJ) {
|
|
CAMBrightCurveJ(65536,0);
|
|
CAMBrightCurveJ.dirty = false;
|
|
}
|
|
ColorTemp::curveJfloat (jli, contra, 1, CAMBrightCurveJ, hist16J);//lightness and contrast J
|
|
}
|
|
if (needQ) {
|
|
if (!CAMBrightCurveQ) {
|
|
CAMBrightCurveQ(65536,0);
|
|
CAMBrightCurveQ.dirty = false;
|
|
}
|
|
ColorTemp::curveJfloat (qbri, qcontra, 1, CAMBrightCurveQ, hist16Q);//brightness and contrast Q
|
|
}
|
|
}
|
|
if (mean<15.f) yb=3.0f;
|
|
else if(mean<30.f) yb=5.0f;
|
|
else if(mean<40.f) yb=10.0f;
|
|
else if(mean<45.f) yb=15.0f;
|
|
else if(mean<50.f) yb=18.0f;
|
|
else if(mean<55.f) yb=23.0f;
|
|
else if(mean<60.f) yb=30.0f;
|
|
else if(mean<70.f) yb=40.0f;
|
|
else if(mean<80.f) yb=60.0f;
|
|
else if(mean<90.f) yb=80.0f;
|
|
else yb=90.0f;
|
|
|
|
int gamu=0;
|
|
bool highlight = params->toneCurve.hrenabled; //Get the value if "highlight reconstruction" is activated
|
|
|
|
if(params->colorappearance.gamut==true) gamu=1;//enabled gamut control
|
|
xw=100.0f*Xw;
|
|
yw=100.0f*Yw;
|
|
zw=100.0f*Zw;
|
|
float xw1,yw1,zw1,xw2,yw2,zw2;
|
|
// settings of WB: scene and viewing
|
|
if(params->colorappearance.wbmodel=="RawT") {xw1=96.46f;yw1=100.0f;zw1=82.445f;xw2=xwd;yw2=ywd;zw2=zwd;} //use RT WB; CAT 02 is used for output device (see prefreneces)
|
|
else if(params->colorappearance.wbmodel=="RawTCAT02") {xw1=xw;yw1=yw;zw1=zw;xw2=xwd;yw2=ywd;zw2=zwd;} // Settings RT WB are used for CAT02 => mix , CAT02 is use for output device (screen: D50 D65, projector: lamp, LED) see preferences
|
|
float cz,wh, pfl;
|
|
ColorTemp::initcam1float(gamu, yb, pilot, f, la,xw, yw, zw, n, d, nbb, ncb,cz, aw, wh, pfl, fl, c);
|
|
float nj,dj,nbbj,ncbj,czj,awj,flj;
|
|
ColorTemp::initcam2float(gamu,yb2, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj,czj, awj, flj);
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(lab,xw1,xw2,yw1,yw2,zw1,zw2,pilot,jli,chr,yb,la,yb2,la2,fl,nc,f,c, height,width,begh, endh,nc2,f2,c2, alg, algepd, gamu, highlight, rstprotection, pW,nj, nbbj, ncbj, flj, czj, dj, awj, n, nbb, ncb, pfl, cz)
|
|
#endif
|
|
{ //matrix for current working space
|
|
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
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]}
|
|
};
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++)
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<width; j++) {
|
|
|
|
float L=lab->L[i][j];
|
|
float a=lab->a[i][j];
|
|
float b=lab->b[i][j];
|
|
float x1,y1,z1;
|
|
float x,y,z;
|
|
float epsil=0.0001f;
|
|
//convert Lab => XYZ
|
|
Color::Lab2XYZ(L, a, b, x1, y1, z1);
|
|
float J, C, h, Q, M, s;
|
|
float Jp;
|
|
float Jpro,Cpro, hpro, Qpro, Mpro, spro;
|
|
bool t1L=false;
|
|
bool t1B=false;
|
|
bool t2B=false;
|
|
int c1s=0;
|
|
int c1co=0;
|
|
|
|
x=(float)x1/655.35f;
|
|
y=(float)y1/655.35f;
|
|
z=(float)z1/655.35f;
|
|
//process source==> normal
|
|
ColorTemp::xyz2jchqms_ciecam02float( J, C, h,
|
|
Q, M, s, aw, fl, wh,
|
|
x, y, z,
|
|
xw1, yw1, zw1,
|
|
yb, la,
|
|
f, c, nc, pilot, gamu, n, nbb, ncb, pfl, cz, d);
|
|
Jpro=J;
|
|
Cpro=C;
|
|
hpro=h;
|
|
Qpro=Q;
|
|
Mpro=M;
|
|
spro=s;
|
|
w_h=wh+epsil;
|
|
a_w=aw;
|
|
c_=c;
|
|
f_l=fl;
|
|
// we cannot have all algoritms with all chroma curves
|
|
if(alg==1) {
|
|
// Lightness saturation
|
|
Jpro=(CAMBrightCurveJ[(float)(Jpro*327.68f)])/327.68f;//ligthness CIECAM02 + contrast
|
|
float sres;
|
|
float Sp=spro/100.0f;
|
|
float parsat=1.5f;
|
|
parsat=1.5f;//parsat=1.5 =>saturation ; 1.8 => chroma ; 2.5 => colorfullness (personal evaluation)
|
|
if(schr==-100.0f) schr=-99.8f;
|
|
ColorTemp::curvecolorfloat(schr, Sp , sres, parsat);
|
|
float coe=pow_F(fl,0.25f);
|
|
float dred=100.f;// in C mode
|
|
float protect_red=80.0f; // in C mode
|
|
dred = 100.0f * sqrt((dred*coe)/Qpro);
|
|
protect_red=100.0f * sqrt((protect_red*coe)/Qpro);
|
|
int sk=0;
|
|
float ko=100.f;
|
|
Color::skinredfloat(Jpro, hpro, sres, Sp, dred, protect_red,sk,rstprotection,ko, spro);
|
|
Qpro= ( 4.0f / c ) * sqrt( Jpro / 100.0f ) * ( aw + 4.0f ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0f);
|
|
}
|
|
else if(alg==3 || alg==0 || alg==2) {
|
|
float coef=32760.f/wh;
|
|
if(alg==3 || alg==2) Qpro=(CAMBrightCurveQ[(float)(Qpro*coef)])/coef;//brightness and contrast
|
|
float Mp, sres;
|
|
float coe=pow_F(fl,0.25f);
|
|
Mp=Mpro/100.0f;
|
|
float parsat=2.5f;
|
|
if(mchr==-100.0f) mchr=-99.8f ;
|
|
if(mchr==100.0f) mchr=99.9f;
|
|
if(alg==3 || alg==2) ColorTemp::curvecolorfloat(mchr, Mp , sres, parsat); else ColorTemp::curvecolorfloat(0.0, Mp , sres, parsat);//colorfullness
|
|
float dred=100.f;//in C mode
|
|
float protect_red=80.0f;// in C mode
|
|
dred *=coe;//in M mode
|
|
protect_red *=coe;//M mode
|
|
int sk=0;
|
|
float ko=100.f;
|
|
Color::skinredfloat(Jpro, hpro, sres, Mp, dred, protect_red,sk,rstprotection,ko, Mpro);
|
|
Jpro=(100.0f* Qpro*Qpro) /(wh*wh);
|
|
Cpro= Mpro/coe;
|
|
spro = 100.0f * sqrt( Mpro / Qpro );
|
|
if(alg!=2) Jpro=(CAMBrightCurveJ[(float)(Jpro*327.68f)])/327.68f;//ligthness CIECAM02 + contrast
|
|
float Cp;
|
|
float Sp=spro/100.0f;
|
|
parsat=1.5f;
|
|
if(schr==-100.0f) schr=-99.f;
|
|
if(schr==100.0f) schr=98.f;
|
|
if(alg==3) ColorTemp::curvecolorfloat(schr, Sp , sres, parsat); else ColorTemp::curvecolorfloat(0.0f, Sp , sres, parsat); //saturation
|
|
dred=100.f;// in C mode
|
|
protect_red=80.0f; // in C mode
|
|
dred = 100.0f * sqrt((dred*coe)/Q);
|
|
protect_red=100.0f * sqrt((protect_red*coe)/Q);
|
|
sk=0;
|
|
Color::skinredfloat(Jpro, hpro, sres, Sp, dred, protect_red,sk,rstprotection,ko, spro);
|
|
Qpro= ( 4.0f / c ) * sqrt( Jpro / 100.0f ) * ( aw + 4.0f ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0f);
|
|
Cp=Cpro/100.0f;
|
|
parsat=1.8f;//parsat=1.5 =>saturation ; 1.8 => chroma ; 2.5 => colorfullness (personal evaluation : for not)
|
|
if(chr==-100.0f) chr=-99.8f;
|
|
if(alg!=2) ColorTemp::curvecolorfloat(chr, Cp , sres, parsat);else ColorTemp::curvecolorfloat(0.0f, Cp , sres, parsat); //chroma
|
|
dred=55.f;
|
|
protect_red=30.0f;
|
|
sk=1;
|
|
Color::skinredfloat(Jpro, hpro, sres, Cp, dred, protect_red,sk,rstprotection, ko, Cpro);
|
|
|
|
hpro=hpro+hue;if( hpro < 0.0f ) hpro += 360.0f;//hue
|
|
}
|
|
|
|
if (hasColCurve1) {//curve 1 with Lightness and Brightness
|
|
if (curveMode==ColorAppearanceParams::TC_MODE_LIGHT){
|
|
float Jj=(float) Jpro*327.68f;
|
|
float Jold=Jj;
|
|
float Jold100=(float) Jpro;
|
|
float redu=25.f;
|
|
float reduc=1.f;
|
|
const Lightcurve& userColCurveJ1 = static_cast<const Lightcurve&>(customColCurve1);
|
|
userColCurveJ1.Apply(Jj);
|
|
if(Jj>Jold) {
|
|
if(Jj<65535.f) {
|
|
if(Jold < 327.68f*redu) Jj=0.3f*(Jj-Jold)+Jold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Jold100)/(100.f-redu),0.f,1.f);
|
|
Jj=0.3f*reduc*(Jj-Jold)+Jold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Jj>10.f) Jj=0.8f*(Jj-Jold)+Jold;
|
|
else if (Jj>=0.f) Jj=0.90f*(Jj-Jold)+Jold;// not zero ==>artifacts
|
|
Jpro=(float)(Jj/327.68f);
|
|
t1L=true;
|
|
}
|
|
else if (curveMode==ColorAppearanceParams::TC_MODE_BRIGHT){
|
|
//attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different
|
|
float coef=((aw+4.f)*(4.f/c))/100.f;
|
|
float Qq=(float) Qpro*327.68f*(1.f/coef);
|
|
float Qold100=(float) Qpro/coef;
|
|
|
|
float Qold=Qq;
|
|
float redu=20.f;
|
|
float reduc=1.f;
|
|
|
|
const Brightcurve& userColCurveB1 = static_cast<const Brightcurve&>(customColCurve1);
|
|
userColCurveB1.Apply(Qq);
|
|
if(Qq>Qold) {
|
|
if(Qq<65535.f) {
|
|
if(Qold < 327.68f*redu) Qq=0.25f*(Qq-Qold)+Qold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Qold100)/(100.f-redu),0.f,1.f);
|
|
Qq=0.25f*reduc*(Qq-Qold)+Qold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Qq>10.f) Qq=0.5f*(Qq-Qold)+Qold;
|
|
else if (Qq>=0.f) Qq=0.7f*(Qq-Qold)+Qold;// not zero ==>artifacts
|
|
Qpro=(float)(Qq*(coef)/327.68f);
|
|
Jpro=100.f*(Qpro*Qpro)/((4.0f/c)*(4.0f/c)*(aw+4.0f)*(aw+4.0f));
|
|
t1B=true;
|
|
}
|
|
}
|
|
|
|
if (hasColCurve2) {//curve 2 with Lightness and Brightness
|
|
if (curveMode2==ColorAppearanceParams::TC_MODE_LIGHT){
|
|
float Jj=(float) Jpro*327.68f;
|
|
float Jold=Jj;
|
|
float Jold100=(float) Jpro;
|
|
float redu=25.f;
|
|
float reduc=1.f;
|
|
const Lightcurve& userColCurveJ2 = static_cast<const Lightcurve&>(customColCurve2);
|
|
userColCurveJ2.Apply(Jj);
|
|
if(Jj>Jold) {
|
|
if(Jj<65535.f) {
|
|
if(Jold < 327.68f*redu) Jj=0.3f*(Jj-Jold)+Jold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Jold100)/(100.f-redu),0.f,1.f);
|
|
Jj=0.3f*reduc*(Jj-Jold)+Jold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Jj>10.f) {if(!t1L)Jj=0.8f*(Jj-Jold)+Jold;else Jj=0.4f*(Jj-Jold)+Jold;}
|
|
else if (Jj>=0.f){if(!t1L)Jj=0.90f*(Jj-Jold)+Jold;else Jj=0.5f*(Jj-Jold)+Jold;}// not zero ==>artifacts
|
|
Jpro=(float)(Jj/327.68f);
|
|
}
|
|
else if (curveMode2==ColorAppearanceParams::TC_MODE_BRIGHT){ //
|
|
float coef=((aw+4.f)*(4.f/c))/100.f;
|
|
float Qq=(float) Qpro*327.68f*(1.f/coef);
|
|
float Qold100=(float) Qpro/coef;
|
|
|
|
float Qold=Qq;
|
|
float redu=20.f;
|
|
float reduc=1.f;
|
|
|
|
const Brightcurve& userColCurveB2 = static_cast<const Brightcurve&>(customColCurve2);
|
|
userColCurveB2.Apply(Qq);
|
|
if(Qq>Qold) {
|
|
if(Qq<65535.f) {
|
|
if(Qold < 327.68f*redu) Qq=0.25f*(Qq-Qold)+Qold;//divide sensibility
|
|
else {
|
|
reduc=LIM((100.f-Qold100)/(100.f-redu),0.f,1.f);
|
|
Qq=0.25f*reduc*(Qq-Qold)+Qold;//reduct sensibility in highlights
|
|
}
|
|
}
|
|
}
|
|
else if(Qq>10.f) Qq=0.5f*(Qq-Qold)+Qold;
|
|
else if (Qq>=0.f) Qq=0.7f*(Qq-Qold)+Qold;// not zero ==>artifacts
|
|
Qpro=(float)(Qq*(coef)/327.68f);
|
|
Jpro=100.f*(Qpro*Qpro)/((4.0f/c)*(4.0f/c)*(aw+4.0f)*(aw+4.0f));
|
|
t2B=true;
|
|
|
|
if(t1L){//to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case
|
|
coef=2.f;//adapt Q to J approximation
|
|
Qq=(float) Qpro*coef;
|
|
Qold=Qq;
|
|
const Lightcurve& userColCurveJ1 = static_cast<const Lightcurve&>(customColCurve1);
|
|
userColCurveJ1.Apply(Qq);
|
|
Qq=0.05f*(Qq-Qold)+Qold;//approximative adaptation
|
|
Qpro=(float)(Qq/coef);
|
|
Jpro=100.f*(Qpro*Qpro)/((4.0f/c)*(4.0f/c)*(aw+4.0f)*(aw+4.0f));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (hasColCurve3) {//curve 3 with chroma saturation colorfullness
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA){
|
|
float parsat=0.8f;//0.68;
|
|
float coef=327.68f/parsat;
|
|
float Cc=(float) Cpro*coef;
|
|
float Ccold=Cc;
|
|
const Chromacurve& userColCurve = static_cast<const Chromacurve&>(customColCurve3);
|
|
userColCurve.Apply(Cc);
|
|
float dred=55.f;
|
|
float protect_red=30.0f;
|
|
float sk=1;
|
|
float ko=1.f/coef;
|
|
Color::skinredfloat(Jpro, hpro, Cc, Ccold, dred, protect_red,sk,rstprotection,ko, Cpro);
|
|
}
|
|
else if (curveMode3==ColorAppearanceParams::TC_MODE_SATUR){ //
|
|
float parsat=0.8f;//0.6
|
|
float coef=327.68f/parsat;
|
|
float Ss=(float) spro*coef;
|
|
float Sold=Ss;
|
|
const Saturcurve& userColCurve = static_cast<const Saturcurve&>(customColCurve3);
|
|
userColCurve.Apply(Ss);
|
|
Ss=0.6f*(Ss-Sold)+Sold;//divide sensibility saturation
|
|
float coe=pow_F(fl,0.25f);
|
|
float dred=100.f;// in C mode
|
|
float protect_red=80.0f; // in C mode
|
|
dred = 100.0f * sqrt((dred*coe)/Qpro);
|
|
protect_red=100.0f * sqrt((protect_red*coe)/Qpro);
|
|
int sk=0;
|
|
float ko=1.f/coef;
|
|
Color::skinredfloat(Jpro, hpro, Ss, Sold, dred, protect_red,sk,rstprotection,ko, spro);
|
|
Qpro= ( 4.0f / c ) * sqrt( Jpro / 100.0f ) * ( aw + 4.0f ) ;
|
|
Cpro=(spro*spro*Qpro)/(10000.0f);
|
|
c1s=1;
|
|
|
|
}
|
|
else if (curveMode3==ColorAppearanceParams::TC_MODE_COLORF){ //
|
|
float parsat=0.8f;//0.68;
|
|
float coef=327.68f/parsat;
|
|
float Mm=(float) Mpro*coef;
|
|
float Mold=Mm;
|
|
const Colorfcurve& userColCurve = static_cast<const Colorfcurve&>(customColCurve3);
|
|
userColCurve.Apply(Mm);
|
|
float coe=pow_F(fl,0.25f);
|
|
float dred=100.f;//in C mode
|
|
float protect_red=80.0f;// in C mode
|
|
dred *=coe;//in M mode
|
|
protect_red *=coe;
|
|
int sk=0;
|
|
float ko=1.f/coef;
|
|
Color::skinredfloat(Jpro, hpro, Mm, Mold, dred, protect_red,sk,rstprotection,ko, Mpro);
|
|
Cpro= Mpro/coe;
|
|
c1co=1;
|
|
}
|
|
}
|
|
//to retrieve the correct values of variables
|
|
|
|
if(c1s==1) {
|
|
Qpro= ( 4.0f / c ) * sqrt( Jpro / 100.0f ) * ( aw + 4.0f ) ;//for saturation curve
|
|
Cpro=(spro*spro*Qpro)/(10000.0f);
|
|
}
|
|
if(c1co==1) { float coe=pow_F(fl,0.25f);Cpro= Mpro/coe;} // for colorfullness curve
|
|
//retrieve values C,J...s
|
|
C=Cpro;
|
|
J=Jpro;
|
|
Q=Qpro;
|
|
M=Mpro;
|
|
h=hpro;
|
|
s=spro;
|
|
|
|
if(params->colorappearance.tonecie || settings->autocielab){//use pointer for tonemapping with CIECAM and also sharpening , defringe, contrast detail
|
|
float Qred= ( 4.0f / c) * ( aw + 4.0f );//estimate Q max if J=100.0
|
|
ncie->Q_p[i][j]=(float)Q+epsil;//epsil to avoid Q=0
|
|
ncie->M_p[i][j]=(float)M+epsil;
|
|
ncie->J_p[i][j]=(float)J+epsil;
|
|
ncie->h_p[i][j]=(float)h;
|
|
ncie->C_p[i][j]=(float)C+epsil;
|
|
ncie->sh_p[i][j]=(float) 32768.f*(( 4.0f / c )*sqrt( J / 100.0f ) * ( aw + 4.0f ))/Qred ;
|
|
if(ncie->Q_p[i][j]<minQ) minQ=ncie->Q_p[i][j];//minima
|
|
if(ncie->Q_p[i][j]>maxQ) maxQ=ncie->Q_p[i][j];//maxima
|
|
}
|
|
if(!params->colorappearance.tonecie || !settings->autocielab || !params->edgePreservingDecompositionUI.enabled){
|
|
int posl, posc;
|
|
float brli=327.f;
|
|
float chsacol=327.f;
|
|
int libr=0;
|
|
int colch=0;
|
|
if(curveMode==ColorAppearanceParams::TC_MODE_BRIGHT) {brli=70.0f; libr=1;}
|
|
else if(curveMode==ColorAppearanceParams::TC_MODE_LIGHT) {brli=327.f;libr=0;}
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA) {chsacol=327.f;colch=0;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_SATUR) {chsacol=450.0f;colch=1;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_COLORF) {chsacol=327.0f;colch=2;}
|
|
|
|
if(ciedata) {
|
|
// Data for J Q M s and C histograms
|
|
//update histogram
|
|
jp=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(libr==1) posl=CLIP((int)(Q*brli));//40.0 to 100.0 approximative factor for Q - 327 for J
|
|
else if(libr==0) posl=CLIP((int)(J*brli));//327 for J
|
|
hist16JCAM[posl]++;
|
|
}
|
|
chropC=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(colch==0) posc=CLIP((int)(C*chsacol));//450.0 approximative factor for s 320 for M
|
|
else if(colch==1) posc=CLIP((int)(s*chsacol));
|
|
else if(colch==2) posc=CLIP((int)(M*chsacol));
|
|
hist16_CCAM[posc]++;
|
|
}
|
|
}
|
|
float xx,yy,zz;
|
|
//process normal==> viewing
|
|
|
|
ColorTemp::jch2xyz_ciecam02float( xx, yy, zz,
|
|
J, C, h,
|
|
xw2, yw2, zw2,
|
|
yb2, la2,
|
|
f2, c2, nc2, gamu, nj, nbbj, ncbj, flj, czj, dj, awj);
|
|
x=(float)xx*655.35f;
|
|
y=(float)yy*655.35f;
|
|
z=(float)zz*655.35f;
|
|
float Ll,aa,bb;
|
|
//convert xyz=>lab
|
|
Color::XYZ2Lab(x, y, z, Ll, aa, bb);
|
|
lab->L[i][j]=Ll;
|
|
lab->a[i][j]=aa;
|
|
lab->b[i][j]=bb;
|
|
// gamut control in Lab mode; I must study how to do with cIECAM only
|
|
if(gamu==1) {
|
|
float R,G,B;
|
|
float HH, Lprov1, Chprov1;
|
|
Lprov1=lab->L[i][j]/327.68f;
|
|
Chprov1=sqrt(SQR(lab->a[i][j]/327.68f) + SQR(lab->b[i][j]/327.68f));
|
|
HH=xatan2f(lab->b[i][j],lab->a[i][j]);
|
|
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
|
|
lab->L[i][j]=Lprov1*327.68f;
|
|
float2 sincosval = xsincosf(HH);
|
|
|
|
lab->a[i][j]=327.68f*Chprov1*sincosval.y;
|
|
lab->b[i][j]=327.68f*Chprov1*sincosval.x;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// End of parallelization
|
|
if(!params->colorappearance.tonecie || !settings->autocielab){//normal
|
|
|
|
if(ciedata) {
|
|
//update histogram J
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=32768; i++) {//
|
|
if (jp) {
|
|
float hval = dLcurve[i];
|
|
int hi = (int)(255.0f*CLIPD(hval)); //
|
|
histLCAM[hi] += hist16JCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=48000; i++) {//
|
|
if (chropC) {
|
|
float hvalc = dCcurve[i];
|
|
int hic = (int)(255.0f*CLIPD(hvalc)); //
|
|
histCCAM[hic] += hist16_CCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
if (settings->verbose) {
|
|
t2e.set();
|
|
printf("CIECAM02 performed in %d usec:\n", t2e.etime(t1e));
|
|
// printf("minc=%f maxc=%f minj=%f maxj=%f\n",minc,maxc,minj,maxj);
|
|
}
|
|
#endif
|
|
|
|
if(settings->autocielab) {
|
|
|
|
|
|
|
|
//all this treatments reduce artefacts, but can leed to slighty different results
|
|
|
|
if(params->defringe.enabled) if(execsharp) ImProcFunctions::defringecam (ncie);//defringe adapted to CIECAM
|
|
|
|
if(params->colorappearance.badpixsl > 0) if(execsharp){ int mode=params->colorappearance.badpixsl;
|
|
ImProcFunctions::badpixcam (ncie, 3.0, 10, mode);//for bad pixels
|
|
}
|
|
|
|
if(params->impulseDenoise.enabled) if(execsharp) ImProcFunctions::impulsedenoisecam (ncie);//impulse adapted to CIECAM
|
|
|
|
if (params->sharpenMicro.enabled)if(execsharp) ImProcFunctions::MLmicrocontrastcam(ncie);
|
|
|
|
if(params->sharpening.enabled) if(execsharp) {ImProcFunctions::sharpeningcam (ncie, (float**)buffer);} //sharpening adapted to CIECAM
|
|
|
|
if(params->dirpyrequalizer.enabled) if(execsharp) dirpyr_equalizercam(ncie, ncie->sh_p, ncie->sh_p, ncie->W, ncie->H, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold, true);//contrast by detail adapted to CIECAM
|
|
|
|
float Qredi= ( 4.0f / c_) * ( a_w + 4.0f );
|
|
float co_e=(pow_F(f_l,0.25f));
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(height,width, Qredi,a_w,c_)
|
|
#endif
|
|
{
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++) // update CieImages with new values after sharpening, defringe, contrast by detail level
|
|
for (int j=0; j<width; j++) {
|
|
float interm=Qredi*ncie->sh_p[i][j]/(32768.f);
|
|
ncie->J_p[i][j]=100.0f* interm*interm/((a_w+4.f)*(a_w+4.f)*(4.f/c_)*(4.f/c_));
|
|
ncie->Q_p[i][j]=( 4.0f / c_) * ( a_w + 4.0f ) * sqrt(ncie->J_p[i][j]/100.f);
|
|
ncie->M_p[i][j]=ncie->C_p[i][j]*co_e;
|
|
}
|
|
}
|
|
}
|
|
if((params->colorappearance.tonecie && (params->edgePreservingDecompositionUI.enabled)) || (params->sharpening.enabled && settings->autocielab)
|
|
|| (params->dirpyrequalizer.enabled && settings->autocielab) ||(params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab)
|
|
|| (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl >0 && settings->autocielab)){
|
|
|
|
if(params->edgePreservingDecompositionUI.enabled && params->colorappearance.tonecie && algepd) ImProcFunctions::EPDToneMapCIE(ncie, a_w, c_, w_h, width, height, begh, endh, minQ, maxQ, Iterates, scale );
|
|
//EPDToneMapCIE adated to CIECAM
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(lab,xw2,yw2,zw2,chr,yb,la2,yb2, height,width,begh, endh, nc2,f2,c2, gamu, highlight,pW,nj, nbbj, ncbj, flj, czj, dj, awj)
|
|
#endif
|
|
{
|
|
TMatrix wiprofa = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
double wipa[3][3] = {
|
|
{wiprofa[0][0],wiprofa[0][1],wiprofa[0][2]},
|
|
{wiprofa[1][0],wiprofa[1][1],wiprofa[1][2]},
|
|
{wiprofa[2][0],wiprofa[2][1],wiprofa[2][2]}
|
|
};
|
|
|
|
|
|
#ifndef _DEBUG
|
|
#pragma omp for schedule(dynamic, 10)
|
|
#endif
|
|
for (int i=0; i<height; i++) // update CIECAM with new values after tone-mapping
|
|
for (int j=0; j<width; j++) {
|
|
float xx,yy,zz;
|
|
float x,y,z;
|
|
float eps=0.0001f;
|
|
float co_e=(pow_F(f_l,0.25f))+eps;
|
|
// if(params->edgePreservingDecompositionUI.enabled) ncie->J_p[i][j]=(100.0f* ncie->Q_p[i][j]*ncie->Q_p[i][j])/(w_h*w_h);
|
|
if(params->edgePreservingDecompositionUI.enabled) ncie->J_p[i][j]=(100.0f* ncie->Q_p[i][j]*ncie->Q_p[i][j])/SQR((4.f/c)*(aw+4.f));
|
|
|
|
ncie->C_p[i][j] =(ncie->M_p[i][j])/co_e;
|
|
//show histogram in CIECAM mode (Q,J, M,s,C)
|
|
int posl, posc;
|
|
float brli=327.f;
|
|
float chsacol=327.f;
|
|
int libr=0;
|
|
int colch=0;
|
|
float sa_t;
|
|
if(curveMode==ColorAppearanceParams::TC_MODE_BRIGHT) {brli=70.0f; libr=1;}
|
|
else if(curveMode==ColorAppearanceParams::TC_MODE_LIGHT) {brli=327.f;libr=0;}
|
|
if (curveMode3==ColorAppearanceParams::TC_MODE_CHROMA) {chsacol=327.f;colch=0;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_SATUR) {chsacol=450.0f;colch=1;}
|
|
else if(curveMode3==ColorAppearanceParams::TC_MODE_COLORF) {chsacol=327.0f;colch=2;}
|
|
if(ciedata) {
|
|
// Data for J Q M s and C histograms
|
|
//update histogram
|
|
jp=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(libr==1) posl=CLIP((int)(ncie->Q_p[i][j]*brli));//40.0 to 100.0 approximative factor for Q - 327 for J
|
|
else if(libr==0) posl=CLIP((int)(ncie->J_p[i][j]*brli));//327 for J
|
|
hist16JCAM[posl]++;
|
|
}
|
|
chropC=true;
|
|
if(pW!=1){//only with improccoordinator
|
|
if(colch==0) posc=CLIP((int)(ncie->C_p[i][j]*chsacol));//450.0 approximative factor for s 320 for M
|
|
else if(colch==1) {sa_t=100.f*sqrt(ncie->C_p[i][j]/ncie->Q_p[i][j]); posc=CLIP((int)(sa_t*chsacol));}//Q_p always > 0
|
|
else if(colch==2) posc=CLIP((int)(ncie->M_p[i][j]*chsacol));
|
|
hist16_CCAM[posc]++;
|
|
}
|
|
}
|
|
//end histograms
|
|
|
|
ColorTemp::jch2xyz_ciecam02float( xx, yy, zz,
|
|
ncie->J_p[i][j], ncie->C_p[i][j], ncie->h_p[i][j],
|
|
xw2, yw2, zw2,
|
|
yb2, la2,
|
|
f2, c2, nc2, gamu, nj, nbbj, ncbj, flj, czj, dj, awj);
|
|
x=(float)xx*655.35f;
|
|
y=(float)yy*655.35f;
|
|
z=(float)zz*655.35f;
|
|
float Ll,aa,bb;
|
|
//convert xyz=>lab
|
|
Color::XYZ2Lab(x, y, z, Ll, aa, bb);
|
|
lab->L[i][j]=Ll;
|
|
lab->a[i][j]=aa;
|
|
lab->b[i][j]=bb;
|
|
if(gamu==1) {
|
|
float R,G,B;
|
|
float HH, Lprov1, Chprov1;
|
|
Lprov1=lab->L[i][j]/327.68f;
|
|
Chprov1=sqrt(SQR(lab->a[i][j]/327.68f) + SQR(lab->b[i][j]/327.68f));
|
|
HH=xatan2f(lab->b[i][j],lab->a[i][j]);
|
|
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wipa, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wipa, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
float2 sincosval = xsincosf(HH);
|
|
|
|
lab->L[i][j]=Lprov1*327.68f;
|
|
lab->a[i][j]=327.68f*Chprov1*sincosval.y;
|
|
lab->b[i][j]=327.68f*Chprov1*sincosval.x;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
//end parallelization
|
|
//show CIECAM histograms
|
|
if(ciedata) {
|
|
//update histogram J and Q
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=32768; i++) {//
|
|
if (jp) {
|
|
float hval = dLcurve[i];
|
|
int hi = (int)(255.0f*CLIPD(hval)); //
|
|
histLCAM[hi] += hist16JCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
//update color histogram M,s,C
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=48000; i++) {//
|
|
if (chropC) {
|
|
float hvalc = dCcurve[i];
|
|
int hic = (int)(255.0f*CLIPD(hvalc)); //
|
|
histCCAM[hic] += hist16_CCAM[i] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
//end CIECAM
|
|
void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve,
|
|
SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve,
|
|
const ToneCurve & customToneCurve1,const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob ) {
|
|
rgbProc (working, lab, hltonecurve, shtonecurve, tonecurve, shmap, sat, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh);
|
|
}
|
|
|
|
// Process RGB image and convert to LAB space
|
|
void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve,
|
|
SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const ToneCurve & customToneCurve1,
|
|
const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2,double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, double expcomp, int hlcompr, int hlcomprthresh) {
|
|
|
|
LUTf iGammaLUTf;
|
|
Imagefloat *tmpImage = working->copy();
|
|
|
|
int h_th, s_th;
|
|
if (shmap) {
|
|
h_th = shmap->max_f - params->sh.htonalwidth * (shmap->max_f - shmap->avg) / 100;
|
|
s_th = params->sh.stonalwidth * (shmap->avg - shmap->min_f) / 100;
|
|
}
|
|
|
|
bool processSH = params->sh.enabled && shmap!=NULL && (params->sh.highlights>0 || params->sh.shadows>0);
|
|
bool processLCE = params->sh.enabled && shmap!=NULL && params->sh.localcontrast>0;
|
|
double lceamount = params->sh.localcontrast / 200.0;
|
|
|
|
TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working);
|
|
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
|
|
double toxyz[3][3] = {
|
|
{
|
|
( wprof[0][0] / Color::D50x),
|
|
( wprof[0][1] / Color::D50x),
|
|
( wprof[0][2] / Color::D50x)
|
|
},{
|
|
( wprof[1][0]),
|
|
( wprof[1][1]),
|
|
( wprof[1][2])
|
|
},{
|
|
( wprof[2][0] / Color::D50z),
|
|
( wprof[2][1] / Color::D50z),
|
|
( wprof[2][2] / Color::D50z)
|
|
}
|
|
};
|
|
|
|
//inverse matrix user select
|
|
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]}
|
|
};
|
|
|
|
double wp[3][3] = {
|
|
{wprof[0][0],wprof[0][1],wprof[0][2]},
|
|
{wprof[1][0],wprof[1][1],wprof[1][2]},
|
|
{wprof[2][0],wprof[2][1],wprof[2][2]}};
|
|
|
|
|
|
bool mixchannels = (params->chmixer.red[0]!=100 || params->chmixer.red[1]!=0 || params->chmixer.red[2]!=0 ||
|
|
params->chmixer.green[0]!=0 || params->chmixer.green[1]!=100 || params->chmixer.green[2]!=0 ||
|
|
params->chmixer.blue[0]!=0 || params->chmixer.blue[1]!=0 || params->chmixer.blue[2]!=100);
|
|
|
|
int tW = working->width;
|
|
int tH = working->height;
|
|
double pi = M_PI;
|
|
FlatCurve* hCurve;
|
|
FlatCurve* sCurve;
|
|
FlatCurve* vCurve;
|
|
FlatCurve* bwlCurve;
|
|
|
|
FlatCurveType hCurveType = (FlatCurveType)params->hsvequalizer.hcurve.at(0);
|
|
FlatCurveType sCurveType = (FlatCurveType)params->hsvequalizer.scurve.at(0);
|
|
FlatCurveType vCurveType = (FlatCurveType)params->hsvequalizer.vcurve.at(0);
|
|
FlatCurveType bwlCurveType = (FlatCurveType)params->blackwhite.luminanceCurve.at(0);
|
|
bool hCurveEnabled = hCurveType > FCT_Linear;
|
|
bool sCurveEnabled = sCurveType > FCT_Linear;
|
|
bool vCurveEnabled = vCurveType > FCT_Linear;
|
|
bool bwlCurveEnabled = bwlCurveType > FCT_Linear;
|
|
|
|
// TODO: We should create a 'skip' value like for CurveFactory::complexsgnCurve (rtengine/curves.cc)
|
|
if (hCurveEnabled) hCurve = new FlatCurve(params->hsvequalizer.hcurve);
|
|
if (sCurveEnabled) sCurve = new FlatCurve(params->hsvequalizer.scurve);
|
|
if (vCurveEnabled) vCurve = new FlatCurve(params->hsvequalizer.vcurve);
|
|
if (bwlCurveEnabled) {
|
|
bwlCurve = new FlatCurve(params->blackwhite.luminanceCurve);
|
|
if (bwlCurve->isIdentity()) {
|
|
delete bwlCurve;
|
|
bwlCurve = NULL;
|
|
bwlCurveEnabled = false;
|
|
}
|
|
}
|
|
|
|
const float exp_scale = pow (2.0, expcomp);
|
|
const float comp = (max(0.0, expcomp) + 1.0)*hlcompr/100.0;
|
|
const float shoulder = ((65536.0/max(1.0f,exp_scale))*(hlcomprthresh/200.0))+0.1;
|
|
const float hlrange = 65536.0-shoulder;
|
|
|
|
// extracting datas from 'params' to avoid cache flush (to be confirmed)
|
|
ToneCurveParams::eTCModeId curveMode = params->toneCurve.curveMode;
|
|
ToneCurveParams::eTCModeId curveMode2 = params->toneCurve.curveMode2;
|
|
bool hasToneCurve1 = bool(customToneCurve1);
|
|
bool hasToneCurve2 = bool(customToneCurve2);
|
|
BlackWhiteParams::eTCModeId beforeCurveMode = params->blackwhite.beforeCurveMode;
|
|
BlackWhiteParams::eTCModeId afterCurveMode = params->blackwhite.afterCurveMode;
|
|
|
|
bool hasToneCurvebw1 = bool(customToneCurvebw1);
|
|
bool hasToneCurvebw2 = bool(customToneCurvebw2);
|
|
|
|
float chMixRR = float(params->chmixer.red[0]);
|
|
float chMixRG = float(params->chmixer.red[1]);
|
|
float chMixRB = float(params->chmixer.red[2]);
|
|
float chMixGR = float(params->chmixer.green[0]);
|
|
float chMixGG = float(params->chmixer.green[1]);
|
|
float chMixGB = float(params->chmixer.green[2]);
|
|
float chMixBR = float(params->chmixer.blue[0]);
|
|
float chMixBG = float(params->chmixer.blue[1]);
|
|
float chMixBB = float(params->chmixer.blue[2]);
|
|
int shHighlights = params->sh.highlights;
|
|
int shShadows = params->sh.shadows;
|
|
bool blackwhite = params->blackwhite.enabled;
|
|
bool complem = params->blackwhite.enabledcc;
|
|
float bwr = float(params->blackwhite.mixerRed);
|
|
float bwg = float(params->blackwhite.mixerGreen);
|
|
float bwb = float(params->blackwhite.mixerBlue);
|
|
float bwrgam = float(params->blackwhite.gammaRed);
|
|
float bwggam = float(params->blackwhite.gammaGreen);
|
|
float bwbgam = float(params->blackwhite.gammaBlue);
|
|
float mixerOrange = float(params->blackwhite.mixerOrange);
|
|
float mixerYellow = float(params->blackwhite.mixerYellow);
|
|
float mixerCyan = float(params->blackwhite.mixerCyan);
|
|
float mixerMagenta = float(params->blackwhite.mixerMagenta);
|
|
float mixerPurple = float(params->blackwhite.mixerPurple);
|
|
int algm=0;
|
|
if (params->blackwhite.method=="Desaturation") algm=0;
|
|
else if(params->blackwhite.method=="LumEqualizer") algm=1;
|
|
else if(params->blackwhite.method=="ChannelMixer") algm=2;
|
|
float kcorec=1.f;
|
|
//gamma correction of each channel
|
|
float gamvalr=125.f;
|
|
float gamvalg=125.f;
|
|
float gamvalb=125.f;
|
|
double nr=0;
|
|
double ng=0;
|
|
double nb=0;
|
|
bool computeMixerAuto = params->blackwhite.autoc && (autor < -5000.f);
|
|
if(bwrgam < 0) gamvalr=100.f;
|
|
if(bwggam < 0) gamvalg=100.f;
|
|
if(bwbgam < 0) gamvalb=100.f;
|
|
float gammabwr=1.f;
|
|
float gammabwg=1.f;
|
|
float gammabwb=1.f;
|
|
//if (params->blackwhite.setting=="Ma" || params->blackwhite.setting=="Mr" || params->blackwhite.setting=="Fr" || params->blackwhite.setting=="Fa") {
|
|
{
|
|
gammabwr=1.f -bwrgam/gamvalr;
|
|
gammabwg=1.f -bwggam/gamvalg;
|
|
gammabwb=1.f -bwbgam/gamvalb;
|
|
}
|
|
bool hasgammabw = gammabwr!=1.f || gammabwg!=1.f || gammabwb!=1.f;
|
|
|
|
//normalize gamma to sRGB
|
|
double start = exp(g*log( -0.055 / ((1.0/g-1.0)*1.055 )));
|
|
double slope = 1.055 * pow (start, 1.0/g-1) - 0.055/start;
|
|
double mul = 1.055;
|
|
double add = 0.055;
|
|
|
|
if (iGamma && g > 1.) {
|
|
iGammaLUTf(65535);
|
|
for (int i=0; i<65536; i++) {
|
|
iGammaLUTf[i] = float(CurveFactory::igamma (double(i)/65535., g, start, slope, mul, add)*65535.);
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel if (multiThread)
|
|
{
|
|
#endif
|
|
|
|
if (mixchannels) {
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
//if (i==100 & j==100) printf("rgbProc input R= %f G= %f B= %f \n",r,g,b);
|
|
float rmix = (r*chMixRR + g*chMixRG + b*chMixRB) / 100.f;
|
|
float gmix = (r*chMixGR + g*chMixGG + b*chMixGB) / 100.f;
|
|
float bmix = (r*chMixBR + g*chMixBG + b*chMixBB) / 100.f;
|
|
|
|
tmpImage->r(i,j) = rmix;
|
|
tmpImage->g(i,j) = gmix;
|
|
tmpImage->b(i,j) = bmix;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (processSH || processLCE) {
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
double mapval = 1.0 + shmap->map[i][j];
|
|
double factor = 1.0;
|
|
|
|
if (processSH) {
|
|
if (mapval > h_th)
|
|
factor = (h_th + (100.0 - shHighlights) * (mapval - h_th) / 100.0) / mapval;
|
|
else if (mapval < s_th)
|
|
factor = (s_th - (100.0 - shShadows) * (s_th - mapval) / 100.0) / mapval;
|
|
}
|
|
if (processLCE) {
|
|
double sub = lceamount*(mapval-factor*(r*lumimul[0] + g*lumimul[1] + b*lumimul[2]));
|
|
tmpImage->r(i,j) = factor*r-sub;
|
|
tmpImage->g(i,j) = factor*g-sub;
|
|
tmpImage->b(i,j) = factor*b-sub;
|
|
}
|
|
else {
|
|
tmpImage->r(i,j) = factor*r;
|
|
tmpImage->g(i,j) = factor*g;
|
|
tmpImage->b(i,j) = factor*b;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
//TODO: proper treatment of out-of-gamut colors
|
|
//float tonefactor = hltonecurve[(0.299f*r+0.587f*g+0.114f*b)];
|
|
float tonefactor=((r<MAXVALF ? hltonecurve[r] : CurveFactory::hlcurve (exp_scale, comp, hlrange, r) ) +
|
|
(g<MAXVALF ? hltonecurve[g] : CurveFactory::hlcurve (exp_scale, comp, hlrange, g) ) +
|
|
(b<MAXVALF ? hltonecurve[b] : CurveFactory::hlcurve (exp_scale, comp, hlrange, b) ) )/3.0;
|
|
|
|
tmpImage->r(i,j) = r*tonefactor;
|
|
tmpImage->g(i,j) = g*tonefactor;
|
|
tmpImage->b(i,j) = b*tonefactor;
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
//shadow tone curve
|
|
float Y = (0.299f*r + 0.587f*g + 0.114f*b);
|
|
float tonefactor = shtonecurve[Y];
|
|
tmpImage->r(i,j) = tmpImage->r(i,j)*tonefactor;
|
|
tmpImage->g(i,j) = tmpImage->g(i,j)*tonefactor;
|
|
tmpImage->b(i,j) = tmpImage->b(i,j)*tonefactor;
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
//brightness/contrast
|
|
tmpImage->r(i,j) = tonecurve[ tmpImage->r(i,j) ];
|
|
tmpImage->g(i,j) = tonecurve[ tmpImage->g(i,j) ];
|
|
tmpImage->b(i,j) = tonecurve[ tmpImage->b(i,j) ];
|
|
}
|
|
}
|
|
|
|
if (hasToneCurve1) {
|
|
if (curveMode==ToneCurveParams::TC_MODE_STD){ // Standard
|
|
printf("STANDARD\n");
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const StandardToneCurve& userToneCurve = static_cast<const StandardToneCurve&>(customToneCurve1);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode==ToneCurveParams::TC_MODE_FILMLIKE){ // Adobe like
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const AdobeToneCurve& userToneCurve = static_cast<const AdobeToneCurve&>(customToneCurve1);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode==ToneCurveParams::TC_MODE_SATANDVALBLENDING){ // apply the curve on the saturation and value channels
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const SatAndValueBlendingToneCurve& userToneCurve = static_cast<const SatAndValueBlendingToneCurve&>(customToneCurve1);
|
|
tmpImage->r(i,j) = CLIP<float>(tmpImage->r(i,j));
|
|
tmpImage->g(i,j) = CLIP<float>(tmpImage->g(i,j));
|
|
tmpImage->b(i,j) = CLIP<float>(tmpImage->b(i,j));
|
|
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode==ToneCurveParams::TC_MODE_WEIGHTEDSTD){ // apply the curve to the rgb channels, weighted
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const WeightedStdToneCurve& userToneCurve = static_cast<const WeightedStdToneCurve&>(customToneCurve1);
|
|
tmpImage->r(i,j) = CLIP<float>(tmpImage->r(i,j));
|
|
tmpImage->g(i,j) = CLIP<float>(tmpImage->g(i,j));
|
|
tmpImage->b(i,j) = CLIP<float>(tmpImage->b(i,j));
|
|
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasToneCurve2) {
|
|
if (curveMode2==ToneCurveParams::TC_MODE_STD){ // Standard
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const StandardToneCurve& userToneCurve = static_cast<const StandardToneCurve&>(customToneCurve2);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode2==ToneCurveParams::TC_MODE_FILMLIKE){ // Adobe like
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const AdobeToneCurve& userToneCurve = static_cast<const AdobeToneCurve&>(customToneCurve2);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode2==ToneCurveParams::TC_MODE_SATANDVALBLENDING){ // apply the curve on the saturation and value channels
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const SatAndValueBlendingToneCurve& userToneCurve = static_cast<const SatAndValueBlendingToneCurve&>(customToneCurve2);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (curveMode2==ToneCurveParams::TC_MODE_WEIGHTEDSTD){ // apply the curve to the rgb channels, weighted
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const WeightedStdToneCurve& userToneCurve = static_cast<const WeightedStdToneCurve&>(customToneCurve2);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iGammaLUTf) {
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
// apply inverse gamma
|
|
tmpImage->r(i,j) = iGammaLUTf[ tmpImage->r(i,j) ];
|
|
tmpImage->g(i,j) = iGammaLUTf[ tmpImage->g(i,j) ];
|
|
tmpImage->b(i,j) = iGammaLUTf[ tmpImage->b(i,j) ];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rCurve || gCurve || bCurve) { // if any of the RGB curves is engaged
|
|
if (!params->rgbCurves.lumamode){ // normal RGB mode
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
// individual R tone curve
|
|
if (rCurve) tmpImage->r(i,j) = rCurve[ tmpImage->r(i,j) ];
|
|
// individual G tone curve
|
|
if (gCurve) tmpImage->g(i,j) = gCurve[ tmpImage->g(i,j) ];
|
|
// individual B tone curve
|
|
if (bCurve) tmpImage->b(i,j) = bCurve[ tmpImage->b(i,j) ];
|
|
}
|
|
}
|
|
}
|
|
else { //params->rgbCurves.lumamode==true (Luminosity mode)
|
|
// rCurve.dump("r_curve");//debug
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
bool highlight = params->toneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated
|
|
|
|
float r1,g1,b1, r2,g2,b2, L_1,L_2, Lfactor,a_1,b_1,x_,y_,z_,R,G,B ;
|
|
float y,fy, yy,fyy,x,z,fx,fz;
|
|
|
|
// rgb values before RGB curves
|
|
r1 = tmpImage->r(i,j) ;
|
|
g1 = tmpImage->g(i,j) ;
|
|
b1 = tmpImage->b(i,j) ;
|
|
//convert to Lab to get a&b before RGB curves
|
|
x = toxyz[0][0] * r1 + toxyz[0][1] * g1 + toxyz[0][2] * b1;
|
|
y = toxyz[1][0] * r1 + toxyz[1][1] * g1 + toxyz[1][2] * b1;
|
|
z = toxyz[2][0] * r1 + toxyz[2][1] * g1 + toxyz[2][2] * b1;
|
|
|
|
fx = (x<65535.0f ? cachef[std::max(x,0.f)] : (327.68f*float(exp(log(x/MAXVALF)/3.0f ))));
|
|
fy = (y<65535.0f ? cachef[std::max(y,0.f)] : (327.68f*float(exp(log(y/MAXVALF)/3.0f ))));
|
|
fz = (z<65535.0f ? cachef[std::max(z,0.f)] : (327.68f*float(exp(log(z/MAXVALF)/3.0f ))));
|
|
|
|
L_1 = (116.0f * fy - 5242.88f); //5242.88=16.0*327.68;
|
|
a_1 = (500.0f * (fx - fy) );
|
|
b_1 = (200.0f * (fy - fz) );
|
|
|
|
// rgb values after RGB curves
|
|
if (rCurve) r2 = rCurve[ tmpImage->r(i,j)]; else r2=r1;
|
|
if (gCurve) g2 = gCurve[ tmpImage->g(i,j)]; else g2=g1;
|
|
if (bCurve) b2 = bCurve[ tmpImage->b(i,j)]; else b2=b1;
|
|
|
|
// Luminosity after
|
|
// only Luminance in Lab
|
|
yy = toxyz[1][0] * r2 + toxyz[1][1] * g2 + toxyz[1][2] * b2;
|
|
fyy = (yy<65535.0f ? cachef[std::max(yy,0.f)] : (327.68f*float(exp(log(yy/MAXVALF)/3.0f ))));
|
|
L_2 = (116.0f * fyy - 5242.88f);
|
|
|
|
//gamut control
|
|
if(settings->rgbcurveslumamode_gamut) {
|
|
float RR,GG,BB;
|
|
float HH, Lpro, Chpro;
|
|
Lpro=L_2/327.68f;
|
|
Chpro=sqrt(SQR(a_1/327.68f) + SQR(b_1/327.68f));
|
|
HH=atan2(b_1,a_1);
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lpro,Chpro, RR, GG, BB, wip, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lpro,Chpro, RR, GG, BB, wip, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
|
|
// Color::gamutLchonly(HH,Lpro,Chpro, RR, GG, BB, wip, highlight, 0.15f, 0.96f);
|
|
L_2=Lpro*327.68f;
|
|
a_1=327.68f*Chpro*cos(HH);
|
|
b_1=327.68f*Chpro*sin(HH);
|
|
} //end of gamut control
|
|
|
|
//calculate RGB with L_2 and old value of a and b
|
|
Color::Lab2XYZ(L_2, a_1, b_1, x_, y_, z_) ;
|
|
Color::xyz2rgb(x_,y_,z_,R,G,B,wip);
|
|
|
|
tmpImage->r(i,j) =R;
|
|
tmpImage->g(i,j) =G;
|
|
tmpImage->b(i,j) =B;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sat!=0 || hCurveEnabled || sCurveEnabled || vCurveEnabled) {
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
float h,s,v;
|
|
Color::rgb2hsv(r,g,b,h,s,v);
|
|
if (sat > 0) {
|
|
s = (1.f-float(sat)/100.f)*s+float(sat)/100.f*(1.f-SQR(SQR(1.f-min(s,1.0f))));
|
|
if (s<0.f) s=0.f;
|
|
} else /*if (sat < 0)*/
|
|
s *= 1.f+float(sat)/100.f;
|
|
|
|
//HSV equalizer
|
|
if (hCurveEnabled) {
|
|
h = (hCurve->getVal(double(h)) - 0.5) * 2.f + h;
|
|
if (h > 1.0f)
|
|
h -= 1.0f;
|
|
else if (h < 0.0f)
|
|
h += 1.0f;
|
|
}
|
|
if (sCurveEnabled) {
|
|
//shift saturation
|
|
float satparam = (sCurve->getVal(double(h))-0.5) * 2;
|
|
if (satparam > 0.00001f) {
|
|
s = (1.f-satparam)*s+satparam*(1.f-SQR(1.f-min(s,1.0f)));
|
|
if (s<0.f) s=0.f;
|
|
} else if (satparam < -0.00001f)
|
|
s *= 1.f+satparam;
|
|
|
|
}
|
|
if (vCurveEnabled) {
|
|
if (v<0) v=0; // important
|
|
|
|
//shift value
|
|
float valparam = vCurve->getVal((double)h)-0.5f;
|
|
valparam *= (1.f-SQR(SQR(1.f-min(s,1.0f))));
|
|
if (valparam > 0.00001f) {
|
|
v = (1.f-valparam)*v+ valparam*(1.f-SQR(1.f-min(v,1.0f)));// SQR (SQR to increase action and avoid artefacts
|
|
if (v<0) v=0;
|
|
} else {
|
|
if (valparam < -0.00001f)
|
|
v *= (1.f+ valparam);//1.99 to increase action
|
|
}
|
|
|
|
}
|
|
Color::hsv2rgb(h, s, v, tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//black and white
|
|
if(blackwhite){
|
|
if (hasToneCurvebw1) {
|
|
if (beforeCurveMode==BlackWhiteParams::TC_MODE_STD_BW){ // Standard
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const StandardToneCurvebw& userToneCurvebw = static_cast<const StandardToneCurvebw&>(customToneCurvebw1);
|
|
userToneCurvebw.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (beforeCurveMode==BlackWhiteParams::TC_MODE_FILMLIKE_BW){ // Adobe like
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const AdobeToneCurvebw& userToneCurvebw = static_cast<const AdobeToneCurvebw&>(customToneCurvebw1);
|
|
userToneCurvebw.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (beforeCurveMode==BlackWhiteParams::TC_MODE_SATANDVALBLENDING_BW){ // apply the curve on the saturation and value channels
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const SatAndValueBlendingToneCurvebw& userToneCurvebw = static_cast<const SatAndValueBlendingToneCurvebw&>(customToneCurvebw1);
|
|
tmpImage->r(i,j) = CLIP<float>(tmpImage->r(i,j));
|
|
tmpImage->g(i,j) = CLIP<float>(tmpImage->g(i,j));
|
|
tmpImage->b(i,j) = CLIP<float>(tmpImage->b(i,j));
|
|
|
|
userToneCurvebw.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (beforeCurveMode==BlackWhiteParams::TC_MODE_WEIGHTEDSTD_BW){ // apply the curve to the rgb channels, weighted
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const WeightedStdToneCurvebw& userToneCurvebw = static_cast<const WeightedStdToneCurvebw&>(customToneCurvebw1);
|
|
tmpImage->r(i,j) = CLIP<float>(tmpImage->r(i,j));
|
|
tmpImage->g(i,j) = CLIP<float>(tmpImage->g(i,j));
|
|
tmpImage->b(i,j) = CLIP<float>(tmpImage->b(i,j));
|
|
|
|
userToneCurvebw.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (algm==0){//lightness
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
// --------------------------------------------------
|
|
|
|
// Method 1: Luminosity (code taken from Gimp)
|
|
/*
|
|
float maxi = max(r, g, b);
|
|
float mini = min(r, g, b);
|
|
r = g = b = (maxi+mini)/2;
|
|
*/
|
|
|
|
// Method 2: Luminance (former RT code)
|
|
r = g = b = (0.299f*r + 0.587f*g + 0.114f*b);
|
|
|
|
// --------------------------------------------------
|
|
|
|
//gamma correction: pseudo TRC curve
|
|
if (hasgammabw) Color::trcGammaBW (r, g, b, gammabwr, gammabwg, gammabwb);
|
|
|
|
tmpImage->r(i,j) = r;
|
|
tmpImage->g(i,j) = g;
|
|
tmpImage->b(i,j) = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (algm==1) {//Luminance mixer in Lab mode to avoid artifacts
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
bool highlight = params->toneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated
|
|
//rgb=>lab
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
float X,Y,Z;
|
|
float L,aa,bb;
|
|
Color::rgbxyz(r,g,b,X,Y,Z,wp);
|
|
//convert Lab
|
|
Color::XYZ2Lab(X, Y, Z, L, aa, bb);
|
|
//end rgb=>lab
|
|
//lab ==> Ch
|
|
float CC=sqrt(SQR(aa/327.68f) + SQR(bb/327.68f));//CC chromaticity in 0..180 or more
|
|
float HH=xatan2f(bb,aa);// HH hue in -3.141 +3.141
|
|
float l_r;//Luminance Lab in 0..1
|
|
l_r = L/32768.f;
|
|
if (bwlCurveEnabled) {
|
|
double hr;
|
|
float valparam = float((bwlCurve->getVal((hr=Color::huelab_to_huehsv2(HH)))-0.5f) * 2.0f);//get l_r=f(H)
|
|
float kcc=(CC/70.f);//take Chroma into account...70 "middle" of chromaticity (arbitrary and simple), one can imagine other algorithme
|
|
//reduct action for low chroma and increase action for high chroma
|
|
valparam *= kcc;
|
|
if(valparam > 0.f) { l_r = (1.f-valparam)*l_r+ valparam*(1.f-SQR(SQR(SQR(SQR(1.f-min(l_r,1.0f))))));}// SQR (SQR((SQR) to increase action in low light
|
|
else l_r *= (1.f + valparam);//for negative
|
|
}
|
|
L=l_r*32768.f;
|
|
float RR,GG,BB;
|
|
float Lr;
|
|
Lr=L/327.68f;//for gamutlch
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lr,CC, RR, GG, BB, wip, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lr,CC, RR, GG, BB, wip, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
//convert CH ==> ab
|
|
L=Lr*327.68f;
|
|
float a_,b_;
|
|
a_=0.f;//grey
|
|
b_=0.f;//grey
|
|
//convert lab=>rgb
|
|
Color::Lab2XYZ(L, a_, b_, X, Y, Z);
|
|
float rr_,gg_,bb_;
|
|
Color::xyz2rgb(X,Y,Z,rr_,gg_,bb_,wip);
|
|
|
|
//gamma correction: pseudo TRC curve
|
|
if (hasgammabw) Color::trcGammaBW (rr_, gg_, bb_, gammabwr, gammabwg, gammabwb);
|
|
tmpImage->r(i,j)=rr_;
|
|
tmpImage->g(i,j)=gg_;
|
|
tmpImage->b(i,j)=bb_;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
else if (algm==1) {//Luminance mixer
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
if (bwlCurveEnabled) {
|
|
float h,s,v;
|
|
Color::rgb2hsv(r,g,b,h,s,v);
|
|
if (v<0) v=0;
|
|
float valparam = bwlCurve->getVal((double)h)-0.5f;
|
|
valparam *= (1.f-(SQR(SQR(1.f-min(s,1.0f)))));
|
|
//float valcor=1.f/(0.501f-valparam);
|
|
|
|
if (valparam < -0.00001f || valparam > 0.00001f)
|
|
v *= (1.f + 4.f*valparam);
|
|
v = CLIPD(v);
|
|
Color::hsv2rgb(h, s, v, r, g, b);
|
|
}
|
|
|
|
// get luminance
|
|
r = g = b = (0.2126f*r + 0.7152f*g + 0.0722f*b); // (constant taken from Gimp, see https://git.gnome.org/browse/gimp/tree/libgimpcolor/gimprgb.h)
|
|
//r = g = b = (0.299f*r + 0.587f*g + 0.114f*b); // (obsolete constant)
|
|
|
|
//gamma correction: pseudo TRC curve
|
|
if (hasgammabw) Color::trcGammaBW (r, g, b, gammabwr, gammabwg, gammabwb);
|
|
|
|
tmpImage->r(i,j) = r;
|
|
tmpImage->g(i,j) = g;
|
|
tmpImage->b(i,j) = b;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
else if (algm==2) {//channel-mixer
|
|
//end auto chmix
|
|
float mix[3][3];
|
|
|
|
if (computeMixerAuto) {
|
|
// auto channel-mixer
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5) reduction(+:nr,ng,nb)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
nr += tmpImage->r(i,j);
|
|
ng += tmpImage->g(i,j);
|
|
nb += tmpImage->b(i,j);
|
|
}
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp single
|
|
#endif
|
|
{
|
|
double srgb = nr+ng+nb;
|
|
double knr = srgb/nr;
|
|
double kng = srgb/ng;
|
|
double knb = srgb/nb;
|
|
double sk = knr+kng+knb;
|
|
autor=(float)(100.0*knr/sk);
|
|
autog=(float)(100.0*kng/sk);
|
|
autob=(float)(100.0*knb/sk);
|
|
}
|
|
|
|
/*
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
float rr = tmpImage->r(i,j);
|
|
float gg = tmpImage->g(i,j);
|
|
float bb = tmpImage->b(i,j);
|
|
nr+=rr;
|
|
ng+=gg;
|
|
nb+=bb;
|
|
float srgb;
|
|
srgb = nr+ng+nb;
|
|
float knr=nr/srgb;
|
|
float kng=ng/srgb;
|
|
float knb=nb/srgb;
|
|
knr = 1.f/knr;
|
|
kng = 1.f/kng;
|
|
knb = 1.f/knb;
|
|
float sk;
|
|
sk=knr+kng+knb;
|
|
knr=100.f*knr/sk;
|
|
kng=100.f*kng/sk;
|
|
knb=100.f*knb/sk;
|
|
autor=knr;
|
|
autog=kng;
|
|
autob=knb;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
if (params->blackwhite.autoc) {
|
|
// auto channel-mixer
|
|
bwr = autor;
|
|
bwg = autog;
|
|
bwb = autob;
|
|
mixerOrange = 33.f;
|
|
mixerYellow = 33.f;
|
|
mixerMagenta = 33.f;
|
|
mixerPurple = 33.f;
|
|
mixerCyan = 33.f;
|
|
}
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp barrier
|
|
#pragma omp single
|
|
#endif
|
|
{
|
|
Color::computeBWMixerConstants(params->blackwhite.setting, params->blackwhite.filter,
|
|
bwr, bwg, bwb, mixerOrange, mixerYellow, mixerCyan, mixerPurple, mixerMagenta,
|
|
params->blackwhite.autoc, complem, kcorec, rrm, ggm, bbm);
|
|
}
|
|
mix[0][0] = bwr;
|
|
mix[1][0] = bwr;
|
|
mix[2][0] = bwr;
|
|
mix[0][1] = bwg;
|
|
mix[1][1] = bwg;
|
|
mix[2][1] = bwg;
|
|
mix[0][2] = bwb;
|
|
mix[1][2] = bwb;
|
|
mix[2][2] = bwb;
|
|
|
|
float in[3], val[3];
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
in[0] = tmpImage->r(i,j);
|
|
in[1] = tmpImage->g(i,j);
|
|
in[2] = tmpImage->b(i,j);
|
|
//mix channel
|
|
for (int end=0; end < 3 ; end++){
|
|
val[end]=0.f;
|
|
for (int beg=0; beg < 3 ; beg++) {
|
|
val[end] += mix[end][beg] *in[beg];
|
|
}
|
|
}
|
|
tmpImage->r(i,j) = tmpImage->g(i,j) = tmpImage->b(i,j) = CLIP(val[0]*kcorec);
|
|
|
|
//gamma correction: pseudo TRC curve
|
|
if (hasgammabw) Color::trcGammaBW (tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j), gammabwr, gammabwg, gammabwb);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasToneCurvebw2) {
|
|
|
|
if (afterCurveMode==BlackWhiteParams::TC_MODE_STD_BW){ // Standard
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
const StandardToneCurvebw& userToneCurve = static_cast<const StandardToneCurvebw&>(customToneCurvebw2);
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
else if (afterCurveMode==BlackWhiteParams::TC_MODE_WEIGHTEDSTD_BW){ // apply the curve to the rgb channels, weighted
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {//for ulterior usage if bw data modified
|
|
for (int j=0; j<tW; j++) {
|
|
const WeightedStdToneCurvebw& userToneCurve = static_cast<const WeightedStdToneCurvebw&>(customToneCurvebw2);
|
|
|
|
tmpImage->r(i,j) = CLIP<float>(tmpImage->r(i,j));
|
|
tmpImage->g(i,j) = CLIP<float>(tmpImage->g(i,j));
|
|
tmpImage->b(i,j) = CLIP<float>(tmpImage->b(i,j));
|
|
|
|
userToneCurve.Apply(tmpImage->r(i,j), tmpImage->g(i,j), tmpImage->b(i,j));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end blackwhite
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp for schedule(dynamic, 5)
|
|
#endif
|
|
for (int i=0; i<tH; i++) {
|
|
for (int j=0; j<tW; j++) {
|
|
float r = tmpImage->r(i,j);
|
|
float g = tmpImage->g(i,j);
|
|
float b = tmpImage->b(i,j);
|
|
|
|
float x = toxyz[0][0] * r + toxyz[0][1] * g + toxyz[0][2] * b;
|
|
float y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b;
|
|
float z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b;
|
|
|
|
float fx,fy,fz;
|
|
|
|
fx = (x<65535.0f ? cachef[std::max(x,0.f)] : (327.68f*float(exp(log(x/MAXVALF)/3.0f ))));
|
|
fy = (y<65535.0f ? cachef[std::max(y,0.f)] : (327.68f*float(exp(log(y/MAXVALF)/3.0f ))));
|
|
fz = (z<65535.0f ? cachef[std::max(z,0.f)] : (327.68f*float(exp(log(z/MAXVALF)/3.0f ))));
|
|
|
|
lab->L[i][j] = (116.0f * fy - 5242.88f); //5242.88=16.0*327.68;
|
|
lab->a[i][j] = (500.0f * (fx - fy) );
|
|
lab->b[i][j] = (200.0f * (fy - fz) );
|
|
|
|
|
|
|
|
//test for color accuracy
|
|
/*float fy = (0.00862069 * lab->L[i][j])/327.68 + 0.137932; // (L+16)/116
|
|
float fx = (0.002 * lab->a[i][j])/327.68 + fy;
|
|
float fz = fy - (0.005 * lab->b[i][j])/327.68;
|
|
|
|
float x_ = 65535*Lab2xyz(fx)*Color::D50x;
|
|
float y_ = 65535*Lab2xyz(fy);
|
|
float z_ = 65535*Lab2xyz(fz)*Color::D50z;
|
|
|
|
int R,G,B;
|
|
xyz2srgb(x_,y_,z_,R,G,B);
|
|
r=(float)R; g=(float)G; b=(float)B;
|
|
float xxx=1;*/
|
|
|
|
}
|
|
}
|
|
#ifdef _OPENMP
|
|
}
|
|
#endif
|
|
|
|
delete tmpImage;
|
|
|
|
if (hCurveEnabled) delete hCurve;
|
|
if (sCurveEnabled) delete sCurve;
|
|
if (vCurveEnabled) delete vCurve;
|
|
}
|
|
|
|
|
|
void ImProcFunctions::luminanceCurve (LabImage* lold, LabImage* lnew, LUTf & curve) {
|
|
|
|
int W = lold->W;
|
|
int H = lold->H;
|
|
|
|
#pragma omp parallel for if (multiThread)
|
|
for (int i=0; i<H; i++)
|
|
for (int j=0; j<W; j++) {
|
|
float Lin=lold->L[i][j];
|
|
//if (Lin>0 && Lin<65535)
|
|
lnew->L[i][j] = curve[Lin];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* lnew, LUTf & acurve, LUTf & bcurve, LUTf & satcurve,LUTf & lhskcurve, LUTf & clcurve, LUTf & curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histCLurve, LUTu &histLLCurve, LUTu &histLCurve) {
|
|
int W = lold->W;
|
|
int H = lold->H;
|
|
// lhskcurve.dump("lh_curve");
|
|
//init Flatcurve for C=f(H)
|
|
FlatCurve* chCurve = NULL;// curve C=f(H)
|
|
bool chutili = false;
|
|
if (params->labCurve.chromaticity > -100) {
|
|
chCurve = new FlatCurve(params->labCurve.chcurve);
|
|
if (chCurve && !chCurve->isIdentity()) {
|
|
chutili=true;
|
|
}//do not use "Munsell" if Chcurve not used
|
|
}
|
|
FlatCurve* lhCurve = NULL;//curve L=f(H)
|
|
bool lhutili = false;
|
|
if (params->labCurve.chromaticity > -100) {
|
|
lhCurve = new FlatCurve(params->labCurve.lhcurve);
|
|
if (lhCurve && !lhCurve->isIdentity()) {
|
|
lhutili=true;
|
|
}
|
|
}
|
|
|
|
FlatCurve* hhCurve = NULL;//curve H=f(H)
|
|
bool hhutili = false;
|
|
if (params->labCurve.chromaticity > -100) {
|
|
hhCurve = new FlatCurve(params->labCurve.hhcurve);
|
|
if (hhCurve && !hhCurve->isIdentity()) {
|
|
hhutili=true;
|
|
}
|
|
}
|
|
|
|
LUTf dCcurve(65536,0);
|
|
LUTf dLcurve(65536,0);
|
|
|
|
LUTu hist16Clad(65536);
|
|
LUTu hist16CLlad(65536);
|
|
LUTu hist16LLClad(65536);
|
|
|
|
bool chrop=false;
|
|
float val;
|
|
//preparate for histograms CIECAM
|
|
if(pW!=1){//only with improccoordinator
|
|
chrop = true;
|
|
for (int i=0; i<48000; i++) { //# 32768*1.414 approximation maxi for chroma
|
|
val = (double)i / 47999.0;
|
|
dCcurve[i] = CLIPD(val);
|
|
}
|
|
for (int i=0; i<65535; i++) { // a
|
|
val = (double)i / 65534.0;
|
|
dLcurve[i] = CLIPD(val);
|
|
|
|
}
|
|
|
|
hist16Clad.clear();
|
|
hist16CLlad.clear();
|
|
hist16LLClad.clear();
|
|
|
|
}
|
|
#ifdef _DEBUG
|
|
MyTime t1e,t2e, t3e, t4e;
|
|
t1e.set();
|
|
// init variables to display Munsell corrections
|
|
MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo();
|
|
#endif
|
|
|
|
unsigned int N = W*H;
|
|
float *L = lold->L[0];
|
|
float *a= lold->a[0];
|
|
float *b= lold->b[0];
|
|
|
|
float* Lold = new float [lold->W*lold->H];//to save L before any used
|
|
float* Cold = new float [lold->W*lold->H];//to save C before any used
|
|
float adjustr=1.0f, adjustbg=1.0f;
|
|
|
|
// if(params->labCurve.avoidclip ){
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for if (multiThread)
|
|
#endif // _OPENMP
|
|
for (unsigned int j=0; j<N; j++){
|
|
Lold[j]=L[j]/327.68f;
|
|
Cold[j]=sqrt(SQR(a[j]/327.68f)+SQR(b[j]/327.68f));
|
|
// Hr=atan2(b[j],a[j]);
|
|
// if(Hr >-0.15f && Hr < 1.5f && Cold[j]>maxCr)
|
|
// maxCr=Cold[j]; // I do not take into account "acurve" and "bcurve" to adjust max
|
|
// else if (Cold[j]>maxCbg)
|
|
// maxCbg=Cold[j];
|
|
}
|
|
// parameter to adapt curve C=f(C) to gamut
|
|
|
|
if (params->icm.working=="ProPhoto") {adjustr = adjustbg = 1.2f;}// 1.2 instead 1.0 because it's very rare to have C>170..
|
|
else if (params->icm.working=="Adobe RGB") {adjustr = 1.8f; adjustbg = 1.4f;}
|
|
else if (params->icm.working=="sRGB") {adjustr = 2.0f; adjustbg = 1.7f;}
|
|
else if (params->icm.working=="WideGamut") {adjustr = adjustbg = 1.2f;}
|
|
else if (params->icm.working=="Beta RGB") {adjustr = adjustbg = 1.4f;}
|
|
else if (params->icm.working=="BestRGB") {adjustr = adjustbg = 1.4f;}
|
|
else if (params->icm.working=="BruceRGB") {adjustr = 1.8f; adjustbg = 1.5f;}
|
|
|
|
|
|
// reference to the params structure has to be done outside of the parallelization to avoid CPU cache problem
|
|
bool highlight = params->toneCurve.hrenabled; //Get the value if "highlight reconstruction" is activated
|
|
int chromaticity = params->labCurve.chromaticity;
|
|
bool bwToning = params->labCurve.chromaticity==-100 /*|| params->blackwhite.method=="Ch" */ || params->blackwhite.enabled;
|
|
//if(chromaticity==-100) chromaticity==-99;
|
|
//if(bwToning) printf("OK bwto\n"); else printf("pas de bw\n");
|
|
bool LCredsk = params->labCurve.lcredsk;
|
|
bool ccut = ccutili;
|
|
bool clut = clcutili;
|
|
double rstprotection = 100.-params->labCurve.rstprotection; // Red and Skin Tones Protection
|
|
// avoid color shift is disabled when bwToning is activated and enabled if gamut is true in colorappearanace
|
|
bool avoidColorShift = (params->labCurve.avoidcolorshift || (params->colorappearance.gamut && params->colorappearance.enabled)) && !bwToning ;
|
|
int protectRed = settings->protectred;
|
|
double protectRedH = settings->protectredh;
|
|
bool gamutLch = settings->gamutLch;
|
|
float amountchroma = (float) settings->amchroma;
|
|
// only if user activate Lab adjustements
|
|
if (avoidColorShift) {
|
|
if(autili || butili || ccutili || cclutili || chutili || lhutili || hhutili || clcutili || utili || chromaticity)
|
|
Color::LabGamutMunsell(lold, Lold, Cold, /*corMunsell*/true, /*lumaMuns*/false, params->toneCurve.hrenabled, /*gamut*/true, params->icm.working, multiThread);
|
|
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#pragma omp parallel default(shared) firstprivate(highlight, ccut, clut, chromaticity, bwToning, rstprotection, avoidColorShift, LCredsk, protectRed, protectRedH, gamutLch, lold, lnew, MunsDebugInfo, pW) if (multiThread)
|
|
#else
|
|
#pragma omp parallel default(shared) firstprivate(highlight, ccut, clut, chromaticity, bwToning, rstprotection, avoidColorShift, LCredsk, protectRed, protectRedH, gamutLch, lold, lnew, pW) if (multiThread)
|
|
#endif
|
|
{
|
|
|
|
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working);
|
|
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]}
|
|
};
|
|
|
|
|
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working);
|
|
|
|
double wp[3][3] = {
|
|
{wprof[0][0],wprof[0][1],wprof[0][2]},
|
|
{wprof[1][0],wprof[1][1],wprof[1][2]},
|
|
{wprof[2][0],wprof[2][1],wprof[2][2]}};
|
|
|
|
#pragma omp for schedule(dynamic, 10)
|
|
for (int i=0; i<H; i++)
|
|
for (int j=0; j<W; j++) {
|
|
float LL=lold->L[i][j]/327.68f;
|
|
float CC=sqrt(SQR(lold->a[i][j]/327.68f) + SQR(lold->b[i][j]/327.68f));
|
|
float HH=xatan2f(lold->b[i][j],lold->a[i][j]);
|
|
float Chprov=CC;
|
|
float Chprov1=CC;
|
|
float memChprov=Chprov;
|
|
float Lprov2=LL;
|
|
float Lin=lold->L[i][j];
|
|
lnew->L[i][j] = curve[Lin];
|
|
float Lprov1=(lnew->L[i][j])/327.68f;
|
|
float chromaChfactor=1.0f;
|
|
float atmp = acurve[lold->a[i][j]+32768.0f]-32768.0f;// curves Lab a
|
|
float btmp = bcurve[lold->b[i][j]+32768.0f]-32768.0f;// curves Lab b
|
|
float Chprov2=Chprov1;
|
|
int poscc,posp,posl;
|
|
bool inRGB;
|
|
const float ClipLevel = 65535.0f;
|
|
|
|
if (lhutili) { // L=f(H)
|
|
float l_r;//Luminance Lab in 0..1
|
|
l_r = Lprov1/100.f;
|
|
{
|
|
double lr;
|
|
float khue=1.9f;//in reserve in case of!
|
|
float valparam = float((lhCurve->getVal(lr=Color::huelab_to_huehsv2(HH))-0.5f));//get l_r=f(H)
|
|
float valparamneg;
|
|
valparamneg=valparam;
|
|
float kcc=(CC/amountchroma);//take Chroma into account...40 "middle low" of chromaticity (arbitrary and simple), one can imagine other algorithme
|
|
//reduct action for low chroma and increase action for high chroma
|
|
valparam *= 2.f*kcc;
|
|
valparamneg*= kcc;//slightly different for negative
|
|
if(valparam > 0.f) { l_r = (1.f-valparam)*l_r+ valparam*(1.f-SQR(((SQR(1.f-min(l_r,1.0f))))));}
|
|
else {l_r *= (1.f+khue*valparamneg);}//for negative
|
|
}
|
|
|
|
Lprov1=l_r*100.f;
|
|
|
|
Chprov2 = sqrt(SQR(atmp/327.68f)+SQR(btmp/327.68f));
|
|
//Gamut control especialy fot negative values slightly different of gamutlchonly
|
|
do {
|
|
inRGB=true;
|
|
float2 sincosval = xsincosf(HH);
|
|
float aprov1=Chprov2*sincosval.y;
|
|
float bprov1=Chprov2*sincosval.x;
|
|
|
|
float fy = (0.00862069f *Lprov1 )+ 0.137932f;
|
|
float fx = (0.002f * aprov1) + fy;
|
|
float fz = fy - (0.005f * bprov1);
|
|
|
|
float x_ = 65535.0f * Color::f2xyz(fx)*Color::D50x;
|
|
float z_ = 65535.0f * Color::f2xyz(fz)*Color::D50z;
|
|
float y_=(Lprov1>Color::epskap) ? 65535.0*fy*fy*fy : 65535.0*Lprov1/Color::kappa;
|
|
float R,G,B;
|
|
Color::xyz2rgb(x_,y_,z_,R,G,B,wip);
|
|
if (R<0.0f || G<0.0f || B<0.0f) {
|
|
if(Lprov1 < 0.1f) Lprov1=0.1f;
|
|
Chprov2*=0.95f;
|
|
inRGB=false;
|
|
}
|
|
else
|
|
if (!highlight && (R>ClipLevel || G>ClipLevel || B>ClipLevel)) {
|
|
if (Lprov1 > 99.999f) Lprov1 = 99.98f;
|
|
Chprov2 *= 0.95f;
|
|
inRGB = false;
|
|
}
|
|
}
|
|
while (!inRGB) ;
|
|
|
|
float2 sincosval = xsincosf(HH);
|
|
atmp=327.68f*Chprov2*sincosval.y;
|
|
btmp=327.68f*Chprov2*sincosval.x;
|
|
|
|
}
|
|
// calculate C=f(H)
|
|
if (chutili) {
|
|
double hr;
|
|
float chparam = float((chCurve->getVal((hr=Color::huelab_to_huehsv2(HH)))-0.5f) * 2.0f);//get C=f(H)
|
|
chromaChfactor=1.0f+chparam;
|
|
}
|
|
|
|
atmp *= chromaChfactor;//apply C=f(H)
|
|
btmp *= chromaChfactor;
|
|
|
|
if (hhutili) { // H=f(H)
|
|
//hue Lab in -PI +PI
|
|
double hr;
|
|
float valparam = float((hhCurve->getVal(hr=Color::huelab_to_huehsv2(HH))-0.5f) * 1.7f) +HH;//get H=f(H) 1.7 optimisation !
|
|
HH= valparam;
|
|
}
|
|
|
|
//simulate very approximative gamut f(L) : with pyramid transition
|
|
float dred=55.0f;//C red value limit
|
|
if (Lprov1<25.0f) dred = 40.0f;
|
|
else if(Lprov1<30.0f) dred = 3.0f*Lprov1 -35.0f;
|
|
else if(Lprov1<70.0f) dred = 55.0f;
|
|
else if(Lprov1<75.0f) dred = -3.0f*Lprov1 +265.0f;
|
|
else dred = 40.0f;
|
|
// end pyramid
|
|
if(params->dirpyrDenoise.enabled && chromaticity ==0) chromaticity = 0.5f;
|
|
|
|
if(!bwToning){
|
|
float chromahist;
|
|
float factorskin, factorsat, factor, factorskinext, interm;
|
|
float scale = 100.0f/100.1f;//reduction in normal zone
|
|
float scaleext=1.0f;//reduction in transition zone
|
|
float protect_red,protect_redh;
|
|
float deltaHH;//HH value transition
|
|
protect_red=float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160
|
|
if(protect_red < 20.0f) protect_red=20.0; // avoid too low value
|
|
if(protect_red > 180.0f) protect_red=180.0; // avoid too high value
|
|
protect_redh=float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0
|
|
if(protect_redh<0.1f) protect_redh=0.1f;//avoid divide by 0 and negatives values
|
|
if(protect_redh>1.0f) protect_redh=1.0f;//avoid too big values
|
|
|
|
deltaHH=protect_redh;//transition hue
|
|
float chromapro = (chromaticity + 100.0f)/100.0f;
|
|
if(chromapro>0.0) Color::scalered ( rstprotection, chromapro, 0.0, HH, deltaHH, scale, scaleext);//1.0
|
|
if(chromapro>1.0) {interm=(chromapro-1.0f)*100.0f;
|
|
factorskin= 1.0f+(interm*scale)/100.0f;
|
|
factorskinext=1.0f+(interm*scaleext)/100.0f;}
|
|
else {
|
|
//factorskin= chromapro*scale;
|
|
//factorskinext= chromapro*scaleext;
|
|
factorskin= chromapro ; // +(chromapro)*scale;
|
|
factorskinext= chromapro ;// +(chromapro)*scaleext;
|
|
|
|
}
|
|
factorsat=chromapro;
|
|
//increase saturation after denoise : ...approximation
|
|
float factnoise=1.f;
|
|
if(params->dirpyrDenoise.enabled) {
|
|
factnoise=(1.f+params->dirpyrDenoise.chroma/500.f);//levels=5
|
|
|
|
|
|
// if(yyyy) factnoise=(1.f+params->dirpyrDenoise.chroma/100.f);//levels=7
|
|
}
|
|
factorsat*=factnoise;
|
|
|
|
factor=factorsat;
|
|
// Test if chroma is in the normal range first
|
|
Color::transitred ( HH, Chprov1, dred, factorskin, protect_red, factorskinext, deltaHH, factorsat, factor);
|
|
atmp *= factor;
|
|
btmp *= factor;
|
|
|
|
}
|
|
if (!bwToning && clut) { // begin C=f(L)
|
|
float factorskin,factorsat,factor,factorskinext,interm;
|
|
float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f);
|
|
float chromaCfactor=(clcurve[Lprov1*327.68f])/(Lprov1*327.68f);//apply C=f(L)
|
|
float curf=0.7f;//empirical coeff because curve is more progressive
|
|
float scale = 100.0f/100.1f;//reduction in normal zone for curve C
|
|
float scaleext=1.0f;//reduction in transition zone for curve C
|
|
float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh
|
|
float deltaHH;//HH value transition for C curve
|
|
protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive
|
|
if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value
|
|
if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value
|
|
protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive
|
|
if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values
|
|
if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values
|
|
|
|
deltaHH=protect_redhcur;//transition hue
|
|
if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0
|
|
if(chromaCfactor>1.0) {
|
|
interm=(chromaCfactor-1.0f)*100.0f;
|
|
factorskin= 1.0f+(interm*scale)/100.0f;
|
|
factorskinext=1.0f+(interm*scaleext)/100.0f;
|
|
}
|
|
else {
|
|
factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale;
|
|
factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext;
|
|
}
|
|
|
|
factorsat=chromaCfactor;
|
|
factor=factorsat;
|
|
Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor);
|
|
atmp *= factor;
|
|
btmp *= factor;
|
|
}
|
|
// end C=f(L)
|
|
|
|
// I have placed C=f(C) after all C treatments to assure maximum amplitude of "C"
|
|
if (!bwToning && ccut) {
|
|
float factorskin,factorsat,factor,factorskinext,interm;
|
|
float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f);
|
|
float chromaCfactor=(satcurve[chroma*adjustr])/(chroma*adjustr);//apply C=f(C)
|
|
float curf=0.7f;//empirical coeff because curve is more progressive
|
|
float scale = 100.0f/100.1f;//reduction in normal zone for curve CC
|
|
float scaleext=1.0f;//reduction in transition zone for curve CC
|
|
float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh
|
|
float deltaHH;//HH value transition for CC curve
|
|
protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive
|
|
if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value
|
|
if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value
|
|
protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive
|
|
if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values
|
|
if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values
|
|
|
|
deltaHH=protect_redhcur;//transition hue
|
|
if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0
|
|
if(chromaCfactor>1.0) {
|
|
interm=(chromaCfactor-1.0f)*100.0f;
|
|
factorskin= 1.0f+(interm*scale)/100.0f;
|
|
factorskinext=1.0f+(interm*scaleext)/100.0f;
|
|
}
|
|
else {
|
|
//factorskin= chromaCfactor*scale;
|
|
//factorskinext=chromaCfactor*scaleext;
|
|
factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale;
|
|
factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext;
|
|
|
|
}
|
|
|
|
factorsat=chromaCfactor;
|
|
factor=factorsat;
|
|
Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor);
|
|
atmp *= factor;
|
|
btmp *= factor;
|
|
}
|
|
// end chroma C=f(C)
|
|
|
|
//update histogram C
|
|
if(pW!=1){//only with improccoordinator
|
|
posp=CLIP((int)sqrt((atmp*atmp + btmp*btmp)));
|
|
hist16Clad[posp]++;
|
|
hist16CLlad[posp]++;
|
|
|
|
}
|
|
|
|
if (!bwToning) { //apply curve L=f(C) for skin and rd...but also for extended color ==> near green and blue (see 'curf')
|
|
|
|
const float xx=0.25f;//soft : between 0.2 and 0.4
|
|
float protect_redhcur;
|
|
float curf=1.0f;
|
|
float deltaHH;
|
|
protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1
|
|
if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values:minimal protection for transition
|
|
if(protect_redhcur>3.5f) protect_redhcur=3.5f;//avoid too big values
|
|
|
|
deltaHH=protect_redhcur;//transition hue
|
|
|
|
float skbeg=-0.05f;//begin hue skin
|
|
float skend=1.60f;//end hue skin
|
|
const float chrmin=20.0f;//to avoid artefact, because L curve is not a real curve for luminance
|
|
float aa,bb;
|
|
float zz=0.0f;
|
|
float yy=0.0f;
|
|
if(Chprov1 < chrmin) yy=(Chprov1/chrmin)*(Chprov1/chrmin)*xx;else yy=xx;//avoid artefact for low C
|
|
if(!LCredsk) {skbeg=-3.1415; skend=3.14159; deltaHH=0.001f;}
|
|
if(HH>skbeg && HH < skend ) zz=yy;
|
|
else if(HH>skbeg-deltaHH && HH<=skbeg) {aa=yy/deltaHH;bb=-aa*(skbeg-deltaHH); zz=aa*HH+bb;}//transition
|
|
else if(HH>=skend && HH < skend+deltaHH) {aa=-yy/deltaHH;bb=-aa*(skend+deltaHH);zz=aa*HH+bb;}//transition
|
|
|
|
float chroma=sqrt(SQR(atmp)+SQR(btmp)+0.001f);
|
|
float Lc = (lhskcurve[chroma*adjustr])/(chroma*adjustr);//apply L=f(C)
|
|
Lc=(Lc-1.0f)*zz+1.0f;//reduct action
|
|
Lprov1*=Lc;//adjust luminance
|
|
}
|
|
//update histo L
|
|
if(pW!=1){//only with improccoordinator
|
|
posl=CLIP((int(Lprov1*327.68f)));
|
|
hist16LLClad[posl]++;
|
|
}
|
|
|
|
Chprov1 = sqrt(SQR(atmp/327.68f)+SQR(btmp/327.68f));
|
|
|
|
// labCurve.bwtoning option allows to decouple modulation of a & b curves by saturation
|
|
// with bwtoning enabled the net effect of a & b curves is visible
|
|
if (bwToning) {
|
|
atmp -= lold->a[i][j];
|
|
btmp -= lold->b[i][j];
|
|
}
|
|
|
|
if (avoidColorShift) {
|
|
//gamutmap Lch ==> preserve Hue,but a little slower than gamutbdy for high values...and little faster for low values
|
|
if(gamutLch) {
|
|
float R,G,B;
|
|
|
|
#ifdef _DEBUG
|
|
bool neg=false;
|
|
bool more_rgb=false;
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb);
|
|
#else
|
|
//gamut control : Lab values are in gamut
|
|
Color::gamutLchonly(HH,Lprov1,Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f);
|
|
#endif
|
|
|
|
lnew->L[i][j]=Lprov1*327.68f;
|
|
lnew->a[i][j]=327.68f*Chprov1*cos(HH);
|
|
lnew->b[i][j]=327.68f*Chprov1*sin(HH);
|
|
}
|
|
else {
|
|
//use gamutbdy
|
|
//Luv limiter
|
|
float Y,u,v;
|
|
Color::Lab2Yuv(lnew->L[i][j],atmp,btmp,Y,u,v);
|
|
//Yuv2Lab includes gamut restriction map
|
|
Color::Yuv2Lab(Y,u,v,lnew->L[i][j],lnew->a[i][j],lnew->b[i][j], wp);
|
|
}
|
|
|
|
if (utili || autili || butili || ccut || clut || cclutili || chutili || lhutili || hhutili || clcutili || chromaticity) {
|
|
float correctionHue=0.0f; // Munsell's correction
|
|
float correctlum=0.0f;
|
|
|
|
Lprov1=lnew->L[i][j]/327.68f;
|
|
Chprov=sqrt(SQR(lnew->a[i][j]/327.68f)+ SQR(lnew->b[i][j]/327.68f));
|
|
|
|
#ifdef _DEBUG
|
|
Color::AllMunsellLch(/*lumaMuns*/true, Lprov1,Lprov2,HH,Chprov,memChprov,correctionHue,correctlum, MunsDebugInfo);
|
|
#else
|
|
Color::AllMunsellLch(/*lumaMuns*/true, Lprov1,Lprov2,HH,Chprov,memChprov,correctionHue,correctlum);
|
|
#endif
|
|
|
|
if(fabs(correctionHue) < 0.015f) HH+=correctlum; // correct only if correct Munsell chroma very little.
|
|
/* if((HH>0.0f && HH < 1.6f) && memChprov < 70.0f) HH+=correctlum;//skin correct
|
|
else if(fabs(correctionHue) < 0.3f) HH+=0.08f*correctlum;
|
|
else if(fabs(correctionHue) < 0.2f) HH+=0.25f*correctlum;
|
|
else if(fabs(correctionHue) < 0.1f) HH+=0.35f*correctlum;
|
|
else if(fabs(correctionHue) < 0.015f) HH+=correctlum; // correct only if correct Munsell chroma very little.
|
|
*/
|
|
float2 sincosval = xsincosf(HH+correctionHue);
|
|
|
|
lnew->a[i][j]=327.68f*Chprov*sincosval.y;// apply Munsell
|
|
lnew->b[i][j]=327.68f*Chprov*sincosval.x;
|
|
}
|
|
}
|
|
else {
|
|
// if(Lprov1 > maxlp) maxlp=Lprov1;
|
|
// if(Lprov1 < minlp) minlp=Lprov1;
|
|
if(!bwToning){
|
|
lnew->L[i][j]=Lprov1*327.68f;
|
|
lnew->a[i][j]=327.68f*Chprov1*cos(HH);
|
|
lnew->b[i][j]=327.68f*Chprov1*sin(HH);
|
|
}
|
|
else {
|
|
//Luv limiter only
|
|
lnew->a[i][j] = atmp;
|
|
lnew->b[i][j] = btmp;
|
|
}
|
|
}
|
|
}
|
|
} // end of parallelization
|
|
//update histogram C with data chromaticity and not with CC curve
|
|
if(pW!=1){//only with improccoordinator
|
|
for (int i=0; i<=48000; i++) {//32768*1.414 + ...
|
|
if (chrop) {
|
|
float hval = dCcurve[i];
|
|
int hi = (int)(255.0*CLIPD(hval)); //
|
|
histCCurve[hi] += hist16Clad[i] ;
|
|
histCLurve[hi] += hist16CLlad[i] ;
|
|
}
|
|
}
|
|
//update histogram L with data luminance
|
|
for (int i=0; i<=65535; i++) {
|
|
if (chrop) {
|
|
float hlval = dLcurve[i];
|
|
int hli = (int)(255.0*CLIPD(hlval));
|
|
histLLCurve[hli] += hist16LLClad[i] ;
|
|
histLCurve[hli] += hist16LLClad[i] ;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (settings->verbose) {
|
|
t3e.set();
|
|
printf("Color::AllMunsellLch (correction performed in %d usec):\n", t3e.etime(t1e));
|
|
printf(" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%i\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass);
|
|
printf(" Munsell luminance : MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%i\n", MunsDebugInfo->maxdhuelum[0], MunsDebugInfo->maxdhuelum[1], MunsDebugInfo->maxdhuelum[2], MunsDebugInfo->maxdhuelum[3], MunsDebugInfo->depassLum);
|
|
}
|
|
delete MunsDebugInfo;
|
|
#endif
|
|
delete [] Lold;
|
|
delete [] Cold;
|
|
|
|
if (chCurve) delete chCurve;
|
|
if (lhCurve) delete lhCurve;
|
|
if (hhCurve) delete hhCurve;
|
|
|
|
}
|
|
|
|
|
|
//#include "cubic.cc"
|
|
|
|
void ImProcFunctions::colorCurve (LabImage* lold, LabImage* lnew) {
|
|
|
|
/* LUT<double> cmultiplier(181021);
|
|
|
|
double boost_a = ((float)params->colorBoost.amount + 100.0) / 100.0;
|
|
double boost_b = ((float)params->colorBoost.amount + 100.0) / 100.0;
|
|
|
|
double c, amul = 1.0, bmul = 1.0;
|
|
if (boost_a > boost_b) {
|
|
c = boost_a;
|
|
if (boost_a > 0)
|
|
bmul = boost_b / boost_a;
|
|
}
|
|
else {
|
|
c = boost_b;
|
|
if (boost_b > 0)
|
|
amul = boost_a / boost_b;
|
|
}
|
|
|
|
if (params->colorBoost.enable_saturationlimiter && c>1.0) {
|
|
// re-generate color multiplier lookup table
|
|
double d = params->colorBoost.saturationlimit / 3.0;
|
|
double alpha = 0.5;
|
|
double threshold1 = alpha * d;
|
|
double threshold2 = c*d*(alpha+1.0) - d;
|
|
for (int i=0; i<=181020; i++) { // lookup table stores multipliers with a 0.25 chrominance resolution
|
|
double chrominance = (double)i/4.0;
|
|
if (chrominance < threshold1)
|
|
cmultiplier[i] = c;
|
|
else if (chrominance < d)
|
|
cmultiplier[i] = (c / (2.0*d*(alpha-1.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance;
|
|
else if (chrominance < threshold2)
|
|
cmultiplier[i] = (1.0 / (2.0*d*(c*(alpha+1.0)-2.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance;
|
|
else
|
|
cmultiplier[i] = 1.0;
|
|
}
|
|
}
|
|
|
|
float eps = 0.001;
|
|
double shift_a = params->colorShift.a + eps, shift_b = params->colorShift.b + eps;
|
|
|
|
float** oa = lold->a;
|
|
float** ob = lold->b;
|
|
|
|
#pragma omp parallel for if (multiThread)
|
|
for (int i=0; i<lold->H; i++)
|
|
for (int j=0; j<lold->W; j++) {
|
|
|
|
double wanted_c = c;
|
|
if (params->colorBoost.enable_saturationlimiter && c>1) {
|
|
float chroma = (float)(4.0 * sqrt((oa[i][j]+shift_a)*(oa[i][j]+shift_a) + (ob[i][j]+shift_b)*(ob[i][j]+shift_b)));
|
|
wanted_c = cmultiplier [chroma];
|
|
}
|
|
|
|
double real_c = wanted_c;
|
|
if (wanted_c >= 1.0 && params->colorBoost.avoidclip) {
|
|
double cclip = 100000.0;
|
|
double cr = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 3.079935, -1.5371515, -0.54278342);
|
|
double cg = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, -0.92123418, 1.87599, 0.04524418);
|
|
double cb = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 0.052889682, -0.20404134, 1.15115166);
|
|
if (cr>1.0 && cr<cclip) cclip = cr;
|
|
if (cg>1.0 && cg<cclip) cclip = cg;
|
|
if (cb>1.0 && cb<cclip) cclip = cb;
|
|
if (cclip<100000.0) {
|
|
real_c = -cclip + 2.0*cclip / (1.0+exp(-2.0*wanted_c/cclip));
|
|
if (real_c<1.0)
|
|
real_c = 1.0;
|
|
}
|
|
}
|
|
|
|
float nna = ((oa[i][j]+shift_a) * real_c * amul);
|
|
float nnb = ((ob[i][j]+shift_b) * real_c * bmul);
|
|
lnew->a[i][j] = LIM(nna,-32000.0f,32000.0f);
|
|
lnew->b[i][j] = LIM(nnb,-32000.0f,32000.0f);
|
|
}
|
|
*/
|
|
//delete [] cmultiplier;
|
|
}
|
|
|
|
void ImProcFunctions::impulsedenoise (LabImage* lab) {
|
|
|
|
if (params->impulseDenoise.enabled && lab->W>=8 && lab->H>=8)
|
|
|
|
impulse_nr (lab, (float)params->impulseDenoise.thresh/20.0 );
|
|
}
|
|
|
|
void ImProcFunctions::impulsedenoisecam (CieImage* ncie) {
|
|
|
|
if (params->impulseDenoise.enabled && ncie->W>=8 && ncie->H>=8)
|
|
|
|
impulse_nrcam (ncie, (float)params->impulseDenoise.thresh/20.0 );
|
|
}
|
|
|
|
void ImProcFunctions::defringe (LabImage* lab) {
|
|
|
|
if (params->defringe.enabled && lab->W>=8 && lab->H>=8)
|
|
|
|
PF_correct_RT(lab, lab, params->defringe.radius, params->defringe.threshold);
|
|
}
|
|
|
|
void ImProcFunctions::defringecam (CieImage* ncie) {
|
|
if (params->defringe.enabled && ncie->W>=8 && ncie->H>=8) PF_correct_RTcam(ncie, ncie, params->defringe.radius, params->defringe.threshold);
|
|
|
|
}
|
|
|
|
void ImProcFunctions::badpixcam(CieImage* ncie, double rad, int thr, int mode){
|
|
if(ncie->W>=8 && ncie->H>=8) Badpixelscam(ncie, ncie, rad, thr, mode);
|
|
}
|
|
|
|
void ImProcFunctions::dirpyrequalizer (LabImage* lab) {
|
|
|
|
if (params->dirpyrequalizer.enabled && lab->W>=8 && lab->H>=8) {
|
|
|
|
//dirpyrLab_equalizer(lab, lab, params->dirpyrequalizer.mult);
|
|
dirpyr_equalizer(lab->L, lab->L, lab->W, lab->H, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold);
|
|
}
|
|
}
|
|
void ImProcFunctions::EPDToneMapCIE(CieImage *ncie, float a_w, float c_, float w_h, int Wid, int Hei, int begh, int endh, float minQ, float maxQ, unsigned int Iterates, int skip){
|
|
|
|
if(!params->edgePreservingDecompositionUI.enabled) return;
|
|
float stren=params->edgePreservingDecompositionUI.Strength;
|
|
float edgest=params->edgePreservingDecompositionUI.EdgeStopping;
|
|
float sca=params->edgePreservingDecompositionUI.Scale;
|
|
float rew=params->edgePreservingDecompositionUI.ReweightingIterates;
|
|
unsigned int i, N = Wid*Hei;
|
|
float Qpro= ( 4.0 / c_) * ( a_w + 4.0 ) ;//estimate Q max if J=100.0
|
|
float *Qpr=ncie->Q_p[0];
|
|
float eps=0.0001;
|
|
if (settings->verbose) printf("minQ=%f maxQ=%f Qpro=%f\n",minQ,maxQ, Qpro);
|
|
if(maxQ>Qpro) Qpro=maxQ;
|
|
for (int i=0; i<Hei; i++)
|
|
for (int j=0; j<Wid; j++) { Qpr[i*Wid+j]=ncie->Q_p[i][j];}
|
|
|
|
EdgePreservingDecomposition epd = EdgePreservingDecomposition(Wid, Hei);
|
|
|
|
for(i = 0; i != N; i++) Qpr[i] = (Qpr[i]+eps)/(Qpro);
|
|
|
|
float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents.
|
|
float DetailBoost = stren;
|
|
if(stren < 0.0f) DetailBoost = 0.0f; //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.0);
|
|
//Jacques Desmis : always Iterates=5 for compatibility images between preview and output
|
|
|
|
epd.CompressDynamicRange(Qpr, (float)sca/skip, (float)edgest, Compression, DetailBoost, Iterates, rew, Qpr);
|
|
|
|
//Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping.
|
|
float s = (1.0f + 38.7889f)*powf(Compression, 1.5856f)/(1.0f + 38.7889f*powf(Compression, 1.5856f));
|
|
#ifndef _DEBUG
|
|
#pragma omp parallel for schedule(dynamic,10)
|
|
#endif
|
|
for (int i=0; i<Hei; i++)
|
|
for (int j=0; j<Wid; j++) {
|
|
ncie->Q_p[i][j]=(Qpr[i*Wid+j]+eps)*Qpro;
|
|
ncie->M_p[i][j]*=s;
|
|
}
|
|
/*
|
|
float *Qpr2 = new float[Wid*((heir)+1)];
|
|
|
|
for (int i=heir; i<Hei; i++)
|
|
for (int j=0; j<Wid; j++) { Qpr2[(i-heir)*Wid+j]=ncie->Q_p[i][j];}
|
|
if(minQ>0.0) minQ=0.0;//normaly minQ always > 0...
|
|
// EdgePreservingDecomposition epd = EdgePreservingDecomposition(Wid, Hei);
|
|
//EdgePreservingDecomposition epd = EdgePreservingDecomposition(Wid, Hei/2);
|
|
for(i = N2; i != N; i++)
|
|
// for(i = begh*Wid; i != N; i++)
|
|
//Qpr[i] = (Qpr[i]-minQ)/(maxQ+1.0);
|
|
Qpr2[i-N2] = (Qpr2[i-N2]-minQ)/(Qpro+1.0);
|
|
|
|
float Compression2 = expf(-stren); //This modification turns numbers symmetric around 0 into exponents.
|
|
float DetailBoost2 = stren;
|
|
if(stren < 0.0f) DetailBoost2 = 0.0f; //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.0);
|
|
|
|
|
|
epd.CompressDynamicRange(Qpr2, (float)sca/skip, (float)edgest, Compression2, DetailBoost2, Iterates, rew, Qpr2);
|
|
|
|
//Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping.
|
|
float s2 = (1.0f + 38.7889f)*powf(Compression, 1.5856f)/(1.0f + 38.7889f*powf(Compression, 1.5856f));
|
|
for (int i=heir; i<Hei; i++)
|
|
// for (int i=begh; i<endh; i++)
|
|
for (int j=0; j<Wid; j++) {
|
|
ncie->Q_p[i][j]=Qpr2[(i-heir)*Wid+j]*Qpro + minQ;
|
|
// Qpr[i*Wid+j]=Qpr[i*Wid+j]*maxQ + minQ;
|
|
// ncie->J_p[i][j]=(100.0* Qpr[i*Wid+j]*Qpr[i*Wid+j]) /(w_h*w_h);
|
|
|
|
ncie->M_p[i][j]*=s2;
|
|
}
|
|
delete [] Qpr2;
|
|
|
|
*/
|
|
}
|
|
|
|
|
|
//Map tones by way of edge preserving decomposition. Is this the right way to include source?
|
|
//#include "EdgePreservingDecomposition.cc"
|
|
void ImProcFunctions::EPDToneMap(LabImage *lab, unsigned int Iterates, int skip){
|
|
//Hasten access to the parameters.
|
|
// EPDParams *p = (EPDParams *)(¶ms->edgePreservingDecompositionUI);
|
|
|
|
//Enabled? Leave now if not.
|
|
// if(!p->enabled) return;
|
|
if(!params->edgePreservingDecompositionUI.enabled) return;
|
|
float stren=params->edgePreservingDecompositionUI.Strength;
|
|
float edgest=params->edgePreservingDecompositionUI.EdgeStopping;
|
|
float sca=params->edgePreservingDecompositionUI.Scale;
|
|
float rew=params->edgePreservingDecompositionUI.ReweightingIterates;
|
|
//Pointers to whole data and size of it.
|
|
float *L = lab->L[0];
|
|
float *a = lab->a[0];
|
|
float *b = lab->b[0];
|
|
unsigned int i, N = lab->W*lab->H;
|
|
|
|
EdgePreservingDecomposition epd = EdgePreservingDecomposition(lab->W, lab->H);
|
|
|
|
//Due to the taking of logarithms, L must be nonnegative. Further, scale to 0 to 1 using nominal range of L, 0 to 15 bit.
|
|
float minL = FLT_MAX;
|
|
#pragma omp parallel
|
|
{
|
|
float lminL = FLT_MAX;
|
|
#pragma omp for
|
|
for(i = 0; i < N; i++)
|
|
if(L[i] < lminL) lminL = L[i];
|
|
#pragma omp critical
|
|
if(lminL < minL) minL = lminL;
|
|
}
|
|
if(minL > 0.0f) minL = 0.0f; //Disable the shift if there are no negative numbers. I wish there were just no negative numbers to begin with.
|
|
#pragma omp parallel for
|
|
for(i = 0; i < N; i++)
|
|
L[i] = (L[i] - minL)/32767.0f;
|
|
|
|
//Some interpretations.
|
|
float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents.
|
|
float DetailBoost = stren;
|
|
if(stren < 0.0f) DetailBoost = 0.0f; //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);
|
|
|
|
/* Debuggery. Saves L for toying with outside of RT.
|
|
char nm[64];
|
|
sprintf(nm, "%ux%ufloat.bin", lab->W, lab->H);
|
|
FILE *f = fopen(nm, "wb");
|
|
fwrite(L, N, sizeof(float), f);
|
|
fclose(f);*/
|
|
|
|
epd.CompressDynamicRange(L, sca/float(skip), edgest, Compression, DetailBoost, Iterates, rew, L);
|
|
|
|
//Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping.
|
|
float s = (1.0f + 38.7889f)*powf(Compression, 1.5856f)/(1.0f + 38.7889f*powf(Compression, 1.5856f));
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for // removed schedule(dynamic,10)
|
|
#endif
|
|
for(int ii = 0; ii < N; ii++)
|
|
a[ii] *= s,
|
|
b[ii] *= s,
|
|
L[ii] = L[ii]*32767.0f + minL;
|
|
}
|
|
|
|
|
|
void ImProcFunctions::getAutoExp (LUTu & histogram, int histcompr, double defgain, double clip,
|
|
double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh) {
|
|
|
|
float scale = 65536.0f;
|
|
float midgray=0.1842f;//middle gray in linear gamma =1 50% luminance
|
|
|
|
int imax=65536>>histcompr;
|
|
int overex=0;
|
|
float sum = 0.f, hisum=0.f, losum=0.f;
|
|
float ave = 0.f, hidev=0.f, lodev=0.f;
|
|
//find average luminance
|
|
for (int i=0; i<imax; i++) {
|
|
sum += histogram[i];
|
|
ave += histogram[i] *(float)i;
|
|
}
|
|
ave /= (sum);
|
|
|
|
//find median of luminance
|
|
int median=0, count=histogram[0];
|
|
while (count<sum/2) {
|
|
median++;
|
|
count += histogram[median];
|
|
}
|
|
if (median==0 || ave<1.f) {//probably the image is a blackframe
|
|
expcomp=0.;
|
|
black=0;
|
|
bright=0;
|
|
contr=0;
|
|
hlcompr=0;
|
|
hlcomprthresh=0;
|
|
return;
|
|
}
|
|
|
|
// compute std dev on the high and low side of median
|
|
// and octiles of histogram
|
|
float octile[8]={0.f,0.f,0.f,0.f,0.f,0.f,0.f,0.f},ospread=0.f;
|
|
count=0;
|
|
for (int i=0; i<imax; i++) {
|
|
if (count<8) {
|
|
octile[count] += histogram[i];
|
|
if (octile[count]>sum/8.f || (count==7 && octile[count]>sum/16.f)) {
|
|
octile[count]=log(1.+(float)i)/log(2.f);
|
|
count++;// = min(count+1,7);
|
|
}
|
|
}
|
|
if (i<ave) {
|
|
//lodev += SQR(ave-i)*histogram[i];
|
|
lodev += (log(ave+1.f)-log((float)i+1.))*histogram[i];
|
|
losum += histogram[i];
|
|
} else {
|
|
//hidev += SQR(i-ave)*histogram[i];
|
|
hidev += (log((float)i+1.)-log(ave+1.f))*histogram[i];
|
|
hisum += histogram[i];
|
|
}
|
|
|
|
}
|
|
if (losum==0 || hisum==0) {//probably the image is a blackframe
|
|
expcomp=0.;
|
|
black=0;
|
|
bright=0;
|
|
contr=0;
|
|
hlcompr=0;
|
|
hlcomprthresh=0;
|
|
return;
|
|
}
|
|
lodev = (lodev/(log(2.f)*losum));
|
|
hidev = (hidev/(log(2.f)*hisum));
|
|
if (octile[6]>log((float)imax+1.f)/log2(2.f)) {//if very overxposed image
|
|
octile[6]=1.5f*octile[5]-0.5f*octile[4];
|
|
overex=2;
|
|
}
|
|
|
|
if (octile[7]>log((float)imax+1.f)/log2(2.f)) {//if overexposed
|
|
octile[7]=1.5f*octile[6]-0.5f*octile[5];
|
|
overex=1;
|
|
|
|
}
|
|
for(int i=1; i<8; i++) {
|
|
if (octile[i] == 0.0f)
|
|
octile[i] = octile[i-1];
|
|
}
|
|
// compute weighted average separation of octiles
|
|
// for future use in contrast setting
|
|
for (int i=1; i<6; i++) {
|
|
ospread += (octile[i+1]-octile[i])/max(0.5f,(i>2 ? (octile[i+1]-octile[3]) : (octile[3]-octile[i])));
|
|
}
|
|
ospread /= 5.f;
|
|
if (ospread<=0.f) {//probably the image is a blackframe
|
|
expcomp=0.;
|
|
black=0;
|
|
bright=0;
|
|
contr=0;
|
|
hlcompr=0;
|
|
hlcomprthresh=0;
|
|
return;
|
|
}
|
|
|
|
|
|
// compute clipping points based on the original histograms (linear, without exp comp.)
|
|
int clipped = 0;
|
|
int rawmax = (imax) - 1;
|
|
while (rawmax>1 && histogram[rawmax]+clipped <= 0) {
|
|
clipped += histogram[rawmax];
|
|
rawmax--;
|
|
}
|
|
|
|
//compute clipped white point
|
|
int clippable = (int)(sum * clip/100.f );
|
|
clipped = 0;
|
|
int whiteclip = (imax) - 1;
|
|
while (whiteclip>1 && histogram[whiteclip]+clipped <= clippable) {
|
|
clipped += histogram[whiteclip];
|
|
whiteclip--;
|
|
}
|
|
|
|
//compute clipped black point
|
|
clipped = 0;
|
|
int shc = 0;
|
|
|
|
while (shc<whiteclip-1 && histogram[shc]+clipped <= clippable) {
|
|
clipped += histogram[shc];
|
|
shc++;
|
|
}
|
|
//rescale to 65535 max
|
|
rawmax <<= histcompr;
|
|
whiteclip <<= histcompr;
|
|
ave = ave*(1<<histcompr);
|
|
median <<= histcompr;
|
|
shc <<= histcompr;
|
|
|
|
//prevent division by 0
|
|
if (lodev==0.f) lodev=1.f;
|
|
|
|
//compute exposure compensation as geometric mean of the amount that
|
|
//sets the mean or median at middle gray, and the amount that sets the estimated top
|
|
//of the histogram at or near clipping.
|
|
float expo=log(midgray*scale/(ave-shc+midgray*shc));
|
|
//float expcomp1 = (log(/*(median/ave)*//*(hidev/lodev)*/midgray*scale/(ave-shc+midgray*shc))+log((hidev/lodev)))/log(2.f);
|
|
float expcomp1 = (log(/*(median/ave)*//*(hidev/lodev)*/midgray*scale/(ave-shc+midgray*shc)))/log(2.f);
|
|
float expcomp2 = 0.5f*( (15.5f-histcompr-(2.f*octile[7]-octile[6])) + log(scale/rawmax)/log(2.f) );
|
|
if(fabs(expcomp1)-fabs(expcomp2)> 1.f) {//for great expcomp
|
|
expcomp = (expcomp1*fabs(expcomp2)+expcomp2*fabs(expcomp1))/(fabs(expcomp1)+fabs(expcomp2));
|
|
if (expcomp<0.f) {
|
|
min(0.0f,max(expcomp1,expcomp2));
|
|
}
|
|
}
|
|
else expcomp = 0.5 * (double)expcomp1 + 0.5 *(double) expcomp2;//for small expcomp
|
|
float gain = exp((float)expcomp*log(2.f));
|
|
|
|
float corr = sqrt(gain*scale/rawmax);
|
|
black = (int) shc*corr;
|
|
|
|
|
|
//now tune hlcompr to bring back rawmax to 65535
|
|
hlcomprthresh = 33;
|
|
//this is a series approximation of the actual formula for comp,
|
|
//which is a transcendental equation
|
|
float comp = (gain*((float)whiteclip)/scale - 1.f)*2.3f;// 2.3 instead of 2 to increase slightly comp
|
|
hlcompr=(int)(100.*comp/(max(0.0,expcomp) + 1.0));
|
|
hlcompr = max(0,min(100,hlcompr));
|
|
|
|
//now find brightness if gain didn't bring ave to midgray using
|
|
//the envelope of the actual 'control cage' brightness curve for simplicity
|
|
float midtmp = gain*sqrt(median*ave)/scale;
|
|
if (midtmp<0.1f) {
|
|
bright = (midgray-midtmp)*15.0/(midtmp);
|
|
} else {
|
|
bright = (midgray-midtmp)*15.0/(0.10833-0.0833*midtmp);
|
|
}
|
|
|
|
bright = 0.25*/*(median/ave)*(hidev/lodev)*/max(0,bright);
|
|
|
|
//compute contrast that spreads the average spacing of octiles
|
|
contr = (int) 50.0f*(1.1f-ospread);
|
|
contr = max(0,min(100,contr));
|
|
//take gamma into account
|
|
double whiteclipg = (int)(CurveFactory::gamma2 (whiteclip * corr / 65536.0) * 65536.0);
|
|
|
|
double gavg = 0.;
|
|
for (int i=0; i<65536>>histcompr; i++)
|
|
gavg += histogram[i] * CurveFactory::gamma2((int)(corr*(i<<histcompr)<65535 ? corr*(i<<histcompr) : 65535)) / sum;
|
|
if (black < gavg) {
|
|
int maxwhiteclip = (gavg - black) * 4 / 3 + black; // dont let whiteclip be such large that the histogram average goes above 3/4
|
|
if (whiteclipg < maxwhiteclip)
|
|
whiteclipg = maxwhiteclip;
|
|
}
|
|
whiteclipg = CurveFactory::igamma2 ((float)(whiteclipg/65535.0)) * 65535.0; //need to inverse gamma transform to get correct exposure compensation parameter
|
|
|
|
//corection with gamma
|
|
black = (int)((65535*black)/whiteclipg);
|
|
//expcomp = log(65535.0 / (whiteclipg)) / log(2.0);
|
|
|
|
//diagnostics
|
|
//printf ("**************** AUTO LEVELS ****************\n");
|
|
/* if (settings->verbose) {
|
|
printf ("expcomp1= %f expcomp2= %f gain= %f expcomp=%f\n",expcomp1,expcomp2,gain,expcomp);
|
|
printf ("expo=%f\n",expo);
|
|
printf ("median: %i average: %f median/average: %f\n",median,ave, median/ave);
|
|
printf ("average: %f\n",ave);
|
|
printf("comp=%f hlcomp: %i\n",comp, hlcompr);
|
|
printf ("median/average: %f\n",median/ave);
|
|
printf ("lodev: %f hidev: %f hidev/lodev: %f\n",lodev,hidev,hidev/lodev);
|
|
printf ("lodev: %f\n",lodev);
|
|
printf ("hidev: %f\n",hidev);
|
|
printf ("imax=%d rawmax= %d whiteclip= %d gain= %f\n",imax,rawmax,whiteclip,gain);
|
|
printf ("octiles: %f %f %f %f %f %f %f %f\n",octile[0],octile[1],octile[2],octile[3],octile[4],octile[5],octile[6],octile[7]);
|
|
printf ("ospread= %f\n",ospread);
|
|
printf ("overexp= %i\n",overex);
|
|
}
|
|
*/
|
|
/*
|
|
// %%%%%%%%%% LEGACY AUTOEXPOSURE CODE %%%%%%%%%%%%%
|
|
// black point selection is based on the linear result (yielding better visual results)
|
|
black = (int)(shc * corr);
|
|
// compute the white point of the exp. compensated gamma corrected image
|
|
double whiteclipg = (int)(CurveFactory::gamma2 (whiteclip * corr / 65536.0) * 65536.0);
|
|
|
|
// compute average intensity of the exp compensated, gamma corrected image
|
|
double gavg = 0;
|
|
for (int i=0; i<65536>>histcompr; i++)
|
|
gavg += histogram[i] * CurveFactory::gamma2((int)(corr*(i<<histcompr)<65535 ? corr*(i<<histcompr) : 65535)) / sum;
|
|
|
|
if (black < gavg) {
|
|
int maxwhiteclip = (gavg - black) * 4 / 3 + black; // dont let whiteclip be such large that the histogram average goes above 3/4
|
|
//double mavg = 65536.0 / (whiteclipg-black) * (gavg - black);
|
|
if (whiteclipg < maxwhiteclip)
|
|
whiteclipg = maxwhiteclip;
|
|
}
|
|
|
|
whiteclipg = CurveFactory::igamma2 ((float)(whiteclipg/65535.0)) * 65535.0; //need to inverse gamma transform to get correct exposure compensation parameter
|
|
|
|
black = (int)((65535*black)/whiteclipg);
|
|
expcomp = log(65535.0 / (whiteclipg)) / log(2.0);
|
|
|
|
if (expcomp<0.0) expcomp = 0.0;*/
|
|
if (expcomp<-5.0) expcomp = -5.0;
|
|
if (expcomp>10.0) expcomp = 10.0;
|
|
}
|
|
|
|
|
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_size) {
|
|
if (fname != "") {
|
|
rtengine::RawMetaDataLocation ri;
|
|
int w_raw=-1, h_raw=thumb_size;
|
|
int w_thumb=-1, h_thumb=thumb_size;
|
|
|
|
Thumbnail* thumb = rtengine::Thumbnail::loadQuickFromRaw (fname, ri, w_thumb, h_thumb, 1, FALSE);
|
|
if (thumb == NULL)
|
|
return 0.0;
|
|
|
|
Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, w_raw, h_raw, 1, 1.0, FALSE);
|
|
if (raw == NULL) {
|
|
delete thumb;
|
|
return 0.0;
|
|
}
|
|
|
|
if (h_thumb != h_raw) {
|
|
delete thumb;
|
|
delete raw;
|
|
return 0.0;
|
|
}
|
|
|
|
int width;
|
|
|
|
if (w_thumb > w_raw)
|
|
width = w_raw;
|
|
else
|
|
width = w_thumb;
|
|
|
|
unsigned char* thumbGray;
|
|
unsigned char* rawGray;
|
|
thumbGray = thumb->getGrayscaleHistEQ (width);
|
|
rawGray = raw->getGrayscaleHistEQ (width);
|
|
|
|
if (thumbGray == NULL || rawGray == NULL) {
|
|
if (thumbGray) delete thumbGray;
|
|
if (rawGray) delete rawGray;
|
|
delete thumb;
|
|
delete raw;
|
|
return 0.0;
|
|
}
|
|
|
|
double dist_amount = calcDistortion (thumbGray, rawGray, width, h_thumb);
|
|
delete thumbGray;
|
|
delete rawGray;
|
|
delete thumb;
|
|
delete raw;
|
|
return dist_amount;
|
|
}
|
|
else
|
|
return 0.0;
|
|
}
|
|
|
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
}
|
|
#undef PIX_SORT
|
|
#undef med3x3
|