Issue 2850: normalized RGB-pipeline curve gammas to sRGB, make curve algorithms operate on linear image data

This commit is contained in:
torger
2015-07-23 21:34:00 +02:00
parent db306ccfed
commit b2836b388f
10 changed files with 80 additions and 100 deletions

View File

@@ -340,6 +340,11 @@ void CurveFactory::curveBW (
LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,//for Luminance
ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip)
{
const float gamma_ = Color::sRGBGamma;
const float start = expf(gamma_*logf( -0.055 / ((1.0/gamma_-1.0)*1.055 )));
const float slope = 1.055 * powf (start, 1.0/gamma_-1) - 0.055/start;
const float mul = 1.055;
const float add = 0.055;
outBeforeCCurveHistogrambw.clear();
bool histNeeded = false;
@@ -355,7 +360,7 @@ void CurveFactory::curveBW (
}
if (tcurve) {
if (!tcurve->isIdentity())
customToneCurvebw2.Set(tcurve);
customToneCurvebw2.Set(tcurve, gamma_, start, slope, mul, add);
delete tcurve;
tcurve = NULL;
}
@@ -370,7 +375,7 @@ void CurveFactory::curveBW (
}
if (tcurve) {
if (!tcurve->isIdentity())
customToneCurvebw1.Set(tcurve);
customToneCurvebw1.Set(tcurve, gamma_, start, slope, mul, add);
delete tcurve;
tcurve = NULL;
}
@@ -617,7 +622,7 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SSEFUNCTION void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh,
double shcompr, double br, double contr, double gamma_, bool igamma_,
double shcompr, double br, double contr,
procparams::ToneCurveParams::eTCModeId curveMode, const std::vector<double>& curvePoints,
procparams::ToneCurveParams::eTCModeId curveMode2, const std::vector<double>& curvePoints2,
LUTu & histogram, LUTu & histogramCropped,
@@ -627,31 +632,16 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
ToneCurve & customToneCurve2,
int skip) {
//double def_mul = pow (2.0, defmul);
/*printf ("def_mul= %f ecomp= %f black= %f hlcompr= %f shcompr= %f br= %f contr= %f defmul= %f
gamma= %f, skip= %d \n",def_mul,ecomp,black,hlcompr,shcompr,br,contr,defmul,gamma_,skip);*/
// compute parameters of the gamma curve
/*double start = exp(gamma_*log( -0.099 / ((1.0/gamma_-1.0)*1.099 )));
double slope = 1.099 * pow (start, 1.0/gamma_-1) - 0.099/start;
double mul = 1.099;
double add = 0.099;
// gamma BT709*/
//normalize gamma to sRGB
double start = exp(gamma_*log( -0.055 / ((1.0/gamma_-1.0)*1.055 )));
double slope = 1.055 * pow (start, 1.0/gamma_-1) - 0.055/start;
double mul = 1.055;
double add = 0.055;
// the curve shapes are defined in sRGB gamma, but the output curves will operate on linear floating point data,
// hence we do both forward and inverse gamma conversions here.
const float gamma_ = Color::sRGBGamma;
const float start = expf(gamma_*logf( -0.055 / ((1.0/gamma_-1.0)*1.055 )));
const float slope = 1.055 * powf (start, 1.0/gamma_-1) - 0.055/start;
const float mul = 1.055;
const float add = 0.055;
// a: slope of the curve, black: starting point at the x axis
double a = pow (2.0, ecomp);
// check if inverse gamma is needed at the end
bool needigamma = igamma_ && gamma_>1.;
const float a = powf (2.0, ecomp);
// clear array that stores histogram valid before applying the custom curve
outBeforeCCurveHistogram.clear();
@@ -727,9 +717,8 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
float val2 = simplebasecurve (val, black, 0.015*shcompr);
shCurve[0] = CLIPD(val2)/val;
val = 0.0;
// gamma correction
if (gamma_>1.)
val = gamma (val, gamma_, start, slope, mul, add);
// gamma correction
val = gamma (val, gamma_, start, slope, mul, add);
// apply brightness curve
if (brightcurve)
@@ -747,8 +736,7 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
shCurve[i] = CLIPD(val2)/val;
// gamma correction
if (gamma_>1.)
val = gamma (val, gamma_, start, slope, mul, add);
val = gamma (val, gamma_, start, slope, mul, add);
// apply brightness curve
if (brightcurve)
@@ -825,7 +813,7 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
}
if (tcurve) {
if (!tcurve->isIdentity())
customToneCurve2.Set(tcurve);
customToneCurve2.Set(tcurve, gamma_, start, slope, mul, add);
delete tcurve;
tcurve = NULL;
}
@@ -844,7 +832,7 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
}
if (tcurve) {
if (!tcurve->isIdentity())
customToneCurve1.Set(tcurve);
customToneCurve1.Set(tcurve, gamma_, start, slope, mul, add);
delete tcurve;
tcurve = NULL;
}
@@ -904,11 +892,7 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
int hi = (int)(255.0*(hval));
outBeforeCCurveHistogram[hi] += histogram/*Cropped*/[i] ;
}
// if inverse gamma is needed, do it (standard sRGB inverse gamma is applied)
if (needigamma)
val = igamma (val, gamma_, start, slope, mul, add);
val = igamma (val, gamma_, start, slope, mul, add);
outCurve[i] = (65535.f * val);
}
@@ -1137,7 +1121,13 @@ void CurveFactory::curveToningLL ( bool & llctoningutili,const std::vector<doubl
outCurve(65536, 0);
for (int i=0; i<65536; i++) {
// apply custom/parametric/NURBS curve, if any
float val = tcurve->getVal ((float)i/65536.0f);
// RGB curves are defined with sRGB gamma, but operate on linear data
float val = float(i)/65535.f;
val = CurveFactory::gamma2 (val);
val = tcurve->getVal(val);
val = CurveFactory::igamma2 (val);
//float val = tcurve->getVal ((float)i/65536.0f);
outCurve[i] = (65536.0f * val);
}
delete tcurve;
@@ -1165,9 +1155,20 @@ void ToneCurve::Reset() {
}
// Fill a LUT with X/Y, ranged 0xffff
void ToneCurve::Set(Curve *pCurve) {
lutToneCurve(65536);
for (int i=0; i<65536; i++) lutToneCurve[i] = pCurve->getVal(double(i)/65535.) * 65535.;
void ToneCurve::Set(Curve *pCurve, float gamma, float start, float slope, float mul, float add) {
lutToneCurve(65536);
if (gamma <= 0.0 || gamma == 1.) {
for (int i=0; i<65536; i++) lutToneCurve[i] = (float)pCurve->getVal(float(i)/65535.f) * 65535.f;
} else {
// apply gamma, that is 'pCurve' is defined with the given gamma and here we convert it to a curve in linear space
for (int i=0; i<65536; i++) {
float val = float(i)/65535.f;
val = CurveFactory::gamma (val, gamma, start, slope, mul, add);
val = pCurve->getVal(val);
val = CurveFactory::igamma (val, gamma, start, slope, mul, add);
lutToneCurve[i] = val * 65535.f;
}
}
}
void OpacityCurve::Reset() {

View File

@@ -181,6 +181,12 @@ class CurveFactory {
static inline double igamma2 (double x) {
return x <= 0.03928 ? x/12.92 : exp(log((x+0.055)/1.055)*sRGBGammaCurve);
}
static inline float gamma2 (float x) {
return x <= 0.00304 ? x*12.92 : 1.055*expf(logf(x)/sRGBGammaCurve)-0.055;
}
static inline float igamma2 (float x) {
return x <= 0.03928 ? x/12.92 : expf(logf((x+0.055)/1.055)*sRGBGammaCurve);
}
// gamma function with adjustable parameters
static inline double gamma (double x, double gamma, double start, double slope, double mul, double add){
return (x <= start ? x*slope : exp(log(x)/gamma)*mul-add);
@@ -188,6 +194,12 @@ class CurveFactory {
static inline double igamma (double x, double gamma, double start, double slope, double mul, double add){
return (x <= start*slope ? x/slope : exp(log((x+add)/mul)*gamma) );
}
static inline float gamma (float x, float gamma, float start, float slope, float mul, float add){
return (x <= start ? x*slope : expf(logf(x)/gamma)*mul-add);
}
static inline float igamma (float x, float gamma, float start, float slope, float mul, float add){
return (x <= start*slope ? x/slope : expf(logf((x+add)/mul)*gamma) );
}
static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level)
{
@@ -208,7 +220,7 @@ class CurveFactory {
public:
static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr,
double gamma_, bool igamma_, procparams::ToneCurveParams::eTCModeId curveMode, const std::vector<double>& curvePoints, procparams::ToneCurveParams::eTCModeId curveMode2, const std::vector<double>& curvePoints2,
procparams::ToneCurveParams::eTCModeId curveMode, const std::vector<double>& curvePoints, procparams::ToneCurveParams::eTCModeId curveMode2, const std::vector<double>& curvePoints2,
LUTu & histogram, LUTu & histogramCropped,
LUTf & hlCurve, LUTf & shCurve,LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2,
@@ -348,7 +360,7 @@ class ToneCurve {
virtual ~ToneCurve() {};
void Reset();
void Set(Curve *pCurve);
void Set(Curve *pCurve, float gamma=0, float start=0, float slope=0, float mul=0, float add=0);
operator bool (void) const { return lutToneCurve; }
};

View File

@@ -86,8 +86,6 @@ class ImageSource : public InitialImage {
virtual double getDefGain () { return 1.0; }
virtual double getGamma () { return 0.0; }
virtual void getFullSize (int& w, int& h, int tr = TR_NONE) {}
virtual void getSize (int tran, PreviewProps pp, int& w, int& h) {}
virtual int getRotateDegree() const { return 0; }

View File

@@ -354,12 +354,10 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) {
// if (hListener) oprevi->calcCroppedHistogram(params, scale, histCropped);
//complexCurve also calculated pre-curves histogram depending on crop
ipf.g = imgsrc->getGamma();
ipf.iGamma = true;
CurveFactory::complexCurve (params.toneCurve.expcomp, params.toneCurve.black/65535.0,
params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh,
params.toneCurve.shcompr, params.toneCurve.brightness, params.toneCurve.contrast,
ipf.g, !ipf.iGamma, params.toneCurve.curveMode, params.toneCurve.curve, params.toneCurve.curveMode2, params.toneCurve.curve2,
params.toneCurve.curveMode, params.toneCurve.curve, params.toneCurve.curveMode2, params.toneCurve.curve2,
vhist16, histCropped, hltonecurve, shtonecurve, tonecurve, histToneCurve, customToneCurve1, customToneCurve2, scale==1 ? 1 : 1);
CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, scale==1 ? 1 : 1);

View File

@@ -2432,7 +2432,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, float satLimit ,float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, bool opautili, LUTf & clToningcurve,LUTf & cl2Toningcurve,
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, DCPProfile *dcpProf) {
LUTf iGammaLUTf;
LUTf fGammaLUTf;
Imagefloat *tmpImage=NULL;
// NOTE: We're getting all 3 pointers here, but this function may not need them all, so one could optimize this
@@ -2688,20 +2688,11 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
}
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);
fGammaLUTf(65535);
#pragma omp parallel for
for (int i=0; i<65536; i++) {
iGammaLUTf[i] = float(CurveFactory::igamma (double(i)/65535., g, start, slope, mul, add)*65535.);
}
for (int i=0; i<65536; i++) {
fGammaLUTf[i] = CurveFactory::gamma2 (float(i)/65535.f) * 65535.f;
}
if (hasColorToning || blackwhite)
tmpImage = new Imagefloat(working->width,working->height);
@@ -2887,9 +2878,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
if (editID == EUID_ToneCurve1) { // filling the pipette buffer
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editIFloatTmpR[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(btemp[ti*TS+tj]/65535.f);
editIFloatTmpR[ti*TS+tj] = CLIP(fGammaLUTf[rtemp[ti*TS+tj]]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(fGammaLUTf[gtemp[ti*TS+tj]]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(fGammaLUTf[btemp[ti*TS+tj]]/65535.f);
}
}
}
@@ -2949,9 +2940,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
if (editID == EUID_ToneCurve2) { // filling the pipette buffer
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editIFloatTmpR[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(btemp[ti*TS+tj]/65535.f);
editIFloatTmpR[ti*TS+tj] = CLIP(fGammaLUTf[rtemp[ti*TS+tj]]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(fGammaLUTf[gtemp[ti*TS+tj]]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(fGammaLUTf[btemp[ti*TS+tj]]/65535.f);
}
}
}
@@ -2999,35 +2990,24 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
}
}
if (iGammaLUTf) {
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
// apply inverse gamma
rtemp[ti*TS+tj] = iGammaLUTf[ rtemp[ti*TS+tj] ];
gtemp[ti*TS+tj] = iGammaLUTf[ gtemp[ti*TS+tj] ];
btemp[ti*TS+tj] = iGammaLUTf[ btemp[ti*TS+tj] ];
}
}
}
if (editID == EUID_RGB_R) {
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editWhateverTmp[ti*TS+tj] = rtemp[ti*TS+tj]/65536.f;
editWhateverTmp[ti*TS+tj] = fGammaLUTf[rtemp[ti*TS+tj]]/65536.f;
}
}
}
else if (editID == EUID_RGB_G) {
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editWhateverTmp[ti*TS+tj] = gtemp[ti*TS+tj]/65536.f;
editWhateverTmp[ti*TS+tj] = fGammaLUTf[gtemp[ti*TS+tj]]/65536.f;
}
}
}
else if (editID == EUID_RGB_B) {
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editWhateverTmp[ti*TS+tj] = btemp[ti*TS+tj]/65536.f;
editWhateverTmp[ti*TS+tj] = fGammaLUTf[btemp[ti*TS+tj]]/65536.f;
}
}
}
@@ -3379,9 +3359,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
if (editID == EUID_BlackWhiteBeforeCurve) {
for (int i=istart,ti=0; i<tH; i++,ti++) {
for (int j=jstart,tj=0; j<tW; j++,tj++) {
editIFloatTmpR[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(btemp[ti*TS+tj]/65535.f);
editIFloatTmpR[ti*TS+tj] = CLIP(fGammaLUTf[rtemp[ti*TS+tj]]/65535.f);
editIFloatTmpG[ti*TS+tj] = CLIP(fGammaLUTf[gtemp[ti*TS+tj]]/65535.f);
editIFloatTmpB[ti*TS+tj] = CLIP(fGammaLUTf[btemp[ti*TS+tj]]/65535.f);
}
}
}
@@ -3748,7 +3728,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *e
#endif
for (int i=0; i<tH; i++) {
for (int j=0; j<tW; j++) {
editWhatever->v(i,j) = CLIP(tmpImage->r(i,j)/65535.f); // assuming that r=g=b
editWhatever->v(i,j) = CLIP(fGammaLUTf[tmpImage->r(i,j)]/65535.f); // assuming that r=g=b
}
}
}
@@ -5732,7 +5712,7 @@ fclose(f);*/
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;
gavg += histogram[i] * CurveFactory::gamma2((float)(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)

View File

@@ -185,8 +185,6 @@ class ImProcFunctions {
public:
bool iGamma; // true if inverse gamma has to be applied in rgbProc
double g;
static LUTf cachef;
double lumimul[3];
// float chau;
@@ -211,7 +209,7 @@ class ImProcFunctions {
static void cleanupCache ();
ImProcFunctions (const ProcParams* iparams, bool imultiThread=true)
: monitorTransform(NULL), params(iparams), scale(1), multiThread(imultiThread), iGamma(true), g(0.0) {}
: monitorTransform(NULL), params(iparams), scale(1), multiThread(imultiThread) {}
~ImProcFunctions ();
void setScale (double iscale);

View File

@@ -162,8 +162,6 @@ class RawImageSource : public ImageSource {
double getDefGain () { return defGain; }
double getGamma () { return Color::sRGBGamma; }
void getFullSize (int& w, int& h, int tr = TR_NONE);
void getSize (int tran, PreviewProps pp, int& w, int& h);
int getRotateDegree() const { return ri->get_rotateDegree(); }

View File

@@ -910,10 +910,8 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei
ToneCurve customToneCurvebw1;
ToneCurve customToneCurvebw2;
ipf.g = gamma;
ipf.iGamma = true;
CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh,
params.toneCurve.shcompr, bright, contr, ipf.g, !ipf.iGamma,
params.toneCurve.shcompr, bright, contr,
params.toneCurve.curveMode, params.toneCurve.curve,
params.toneCurve.curveMode2, params.toneCurve.curve2,
hist16, dummy, curve1, curve2, curve, dummy, customToneCurve1, customToneCurve2, 16);

View File

@@ -606,9 +606,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
ToneCurve customToneCurvebw2;
//if(params.blackwhite.enabled) params.toneCurve.hrenabled=false;
ipf.g = imgsrc->getGamma();
ipf.iGamma = true;
CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr, ipf.g, !ipf.iGamma,
CurveFactory::complexCurve (expcomp, black/65535.0, hlcompr, hlcomprthresh, params.toneCurve.shcompr, bright, contr,
params.toneCurve.curveMode, params.toneCurve.curve, params.toneCurve.curveMode2, params.toneCurve.curve2,
hist16, dummy, curve1, curve2, curve, dummy, customToneCurve1, customToneCurve2 );

View File

@@ -53,7 +53,6 @@ class StdImageSource : public ImageSource {
void getAutoExpHistogram (LUTu &histogram, int& histcompr);
double getDefGain () { return 0.0; }
double getGamma () { return 0.0; }
void getFullSize (int& w, int& h, int tr = TR_NONE);
void getSize (int tran, PreviewProps pp, int& w, int& h);