/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * 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 . */ #include #include #include #include #include #include #include #undef CLIPD #define CLIPD(a) ((a)>0.0?((a)<1.0?(a):1.0):0.0) namespace rtengine { Curve::Curve (const std::vector& p, int poly_pn) : x(NULL), y(NULL), ypp(NULL) { ppn = poly_pn; if (p.size()<3) { kind = Empty; } else { kind = (CurveType)p[0]; if (kind==Linear || kind==Spline || kind==NURBS) { N = (p.size()-1)/2; x = new double[N]; y = new double[N]; int ix = 1; for (int i=0; i 2) NURBS_set (); else kind=Linear; } else if (kind==Parametric) { if (p.size()!=8 && p.size()!=9) kind = Empty; else { x = new double[9]; for (int i=0; i<4; i++) x[i] = p[i]; for (int i=4; i<8; i++) x[i] = (p[i]+100.0)/200.0; if (p.size()<9) x[8] = 1.0; else x[8] = p[8]/100.0; } } } } Curve::~Curve () { delete [] x; delete [] y; delete [] ypp; poly_x.clear(); poly_y.clear(); } void Curve::spline_cubic_set () { double* u = new double[N-1]; delete [] ypp; ypp = new double [N]; ypp[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */ for (int i = 1; i < N - 1; ++i) { double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]); double p = sig * ypp[i - 1] + 2.0; ypp[i] = (sig - 1.0) / p; u[i] = ((y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1])); u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; } ypp[N - 1] = 0.0; for (int k = N - 2; k >= 0; --k) ypp[k] = ypp[k] * ypp[k + 1] + u[k]; delete [] u; } void Curve::NURBS_set () { int nbSubCurvesPoints = N + (N-3)*2; std::vector sc_x(nbSubCurvesPoints); // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...) std::vector sc_y(nbSubCurvesPoints); // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...) std::vector sc_length(N+2); // Length of the subcurves double total_length=0.; // Create the list of Bezier sub-curves // NURBS_set is called if N > 2 only int j = 0; int k = 0; for (int i = 0; i < N-1;) { double length; double dx; double dy; // first point (on the curve) if (!i) { sc_x[j] = x[i]; sc_y[j++] = y[i++]; } else { sc_x[j] = (x[i-1] + x[i]) / 2.; sc_y[j++] = (y[i-1] + y[i]) / 2.; } // second point (control point) sc_x[j] = x[i]; sc_y[j] = y[i++]; length = sqrt(pow(sc_x[j] - sc_x[j-1],2) + pow(sc_y[j] - sc_y[j-1],2)); j++; // third point (on the curve) if (i==N-1) { sc_x[j] = x[i]; sc_y[j] = y[i]; } else { sc_x[j] = (x[i-1] + x[i]) / 2.; sc_y[j] = (y[i-1] + y[i]) / 2.; } dx = sc_x[j] - sc_x[j-1]; dy = sc_y[j] - sc_y[j-1]; length += sqrt(dx*dx + dy*dy); j++; // Storing the length of all sub-curves and the total length (to have a better distribution // of the points along the curve) sc_length[k++] = length; total_length += length; } poly_x.clear(); poly_y.clear(); unsigned int sc_xsize=j-1; j = 0; // create the polyline with the number of points adapted to the X range of the sub-curve for (unsigned int i=0; i < sc_xsize /*sc_x.size()*/; i+=3) { // TODO: Speeding-up the interface by caching the polyline, instead of rebuilding it at each action on sliders !!! int nbr_points = (int)(((double)(ppn+N-2) * sc_length[i/3] )/ total_length); if (nbr_points<0){ for(int it=0;it < sc_x.size(); it+=3) printf("sc_length[%d/3]=%f \n",it,sc_length[it/3]); printf("NURBS: error detected!\n i=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,nbr_points,ppn,N,sc_length[i/3],total_length); exit(0); } // increment along the curve, not along the X axis double increment = 1.0 / (double)(nbr_points-1); if (!i) { poly_x.push_back( sc_x[i]); poly_y.push_back(sc_y[i]); } for (k=1; k<(nbr_points-1); k++) { double t = k*increment; double t2 = t*t; double tr = 1.-t; double tr2 = tr*tr; double tr2t = tr*2*t; // adding a point to the polyline poly_x.push_back( tr2*sc_x[i] + tr2t*sc_x[i+1] + t2*sc_x[i+2]); poly_y.push_back( tr2*sc_y[i] + tr2t*sc_y[i+1] + t2*sc_y[i+2]); } // adding the last point of the sub-curve poly_x.push_back( sc_x[i+2]); poly_y.push_back(sc_y[i+2]); } } double Curve::getVal (double t) { switch (kind) { case Empty : return t; break; case Parametric : { if (t<=1e-14) return 0.0; double c = -log(2.0)/log(x[2]); double tv = exp(c*log(t)); double base = pfull (tv, x[8], x[6], x[5]); double stretched = base<=1e-14 ? 0.0 : exp(log(base)/c); base = pfull (0.5, x[8], x[6], x[5]); double fc = base<=1e-14 ? 0.0 : exp(log(base)/c); // value of the curve at the center point if (tx[N-1]) return y[N-1]; else if (t 1){ int k = (k_hi + k_lo) / 2; if (x[k] > t) k_hi = k; else k_lo = k; } double h = x[k_hi] - x[k_lo]; // linear if (kind==Linear) return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; // spline curve else { // if (kind==Spline) { double a = (x[k_hi] - t) / h; double b = (t - x[k_lo]) / h; double r = a*y[k_lo] + b*y[k_hi] + ((a*a*a - a)*ypp[k_lo] + (b*b*b - b)*ypp[k_hi]) * (h*h)/6.0; return CLIPD(r); } break; } case NURBS : { // values under and over the first and last point if (t>x[N-1]) return y[N-1]; else if (t 1){ int k = (k_hi + k_lo) / 2; if (poly_x[k] > t) k_hi = k; else k_lo = k; } double h = poly_x[k_hi] - poly_x[k_lo]; return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h; break; } default: // all other (unknown) kind return t; } } void Curve::getVal (const std::vector& t, std::vector& res) { // TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided res.resize (t.size()); for (unsigned int i=0; i0) { if (x>m) return m + (1.0-m) * tanh (b*(x-m)/(1.0-m)) / tanh (b); else return m + m * tanh (b*(x-m)/m) / tanh (b); } else { if (x>m) return 2.0*x - m - (1.0-m) * tanh (b*(x-m)/(1.0-m)) / tanh (b); else return 2.0*x - m - m * tanh (b*(x-m)/m) / tanh (b); } } /* void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, double shcompr, double br, double contr, double defmul, double gamma_, bool igamma, const std::vector& curvePoints, unsigned int* histogram, int* outCurve, unsigned int* outBeforeCCurveHistogram, int skip) { printf ("ecomp= %f black= %f hlcompr= %f shcompr= %f br= %f contr= %f defmul= %f gamma= %f, skip= %d \n",ecomp,black,hlcompr,shcompr,br,contr,defmul,gamma_,skip); double def_mul = pow (2.0, defmul); // 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; // theoretical maximum of the curve double D = def_mul; // a: slope of the curve, black: starting point at the x axis double a = pow (2.0, ecomp); // curve without contrast double* dcurve = new double[65536]; // check if contrast curve is needed bool needcontrast = contr>0.00001 || contr<-0.00001; // check if inverse gamma is needed at the end bool needigamma = !needcontrast && igamma && gamma_>0; // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) memset (outBeforeCCurveHistogram, 0, 256*sizeof(int)); for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) val *= def_mul; // apply base curve, thus, exposure compensation and black point with shadow and highlight protection val = basecurve (val, a, black, D, hlcompr/100.0, shcompr/100.0); // gamma correction if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); // apply brightness curve val = brightness (val, br/100.0); // apply custom/parametric/NURBS curve, if any if (tcurve) { if (outBeforeCCurveHistogram) { double hval = val; // if (needigamma) // hval = igamma2 (hval); int hi = (int)(255.0*CLIPD(hval)); outBeforeCCurveHistogram[hi]+=histogram[i] ; } val = tcurve->getVal (val); } // if inverse gamma is needed, do it (standard sRGB inverse gamma is applied) if (needigamma) val = igamma2 (val); // store result in a temporary array dcurve[i] = CLIPD(val); } delete tcurve; // if skip>1, let apply linear interpolation in the skipped points of the curve int prev = 0; for (int i=1; i<=0xffff-skip; i++) { if (i%skip==0) { prev+=skip; continue; } dcurve[i] = ( dcurve[prev] * (skip - i%skip) + dcurve[prev+skip] * (i%skip) ) / skip; } if (needcontrast) { // compute mean luminance of the image with the curve applied int sum = 0; double avg = 0; for (int i=0; i<=0xffff; i++) { avg += dcurve[i] * histogram[i]; sum += histogram[i]; } avg /= sum; // compute contrast parameter double contr_b = contr / 20; if (contr_b>=0 && contr_b < 0.00001) contr_b = 0.00001; else if (contr_b<0 && contr_b > -0.00001) contr_b = -0.00001; // apply contrast enhancement for (int i=0; i<=0xffff; i++) { double val = centercontrast (dcurve[i], contr_b, avg); if (igamma && gamma_>0) val = igamma2 (val); outCurve[i] = (int) (65535.0 * CLIPD(val)); } } else for (int i=0; i<=0xffff; i++) outCurve[i] = (int) (65535.0 * dcurve[i]); delete [] dcurve; } */ /* void CurveFactory::complexsgnCurve (double satclip, double satcompr, double saturation, double colormult, const std::vector& curvePoints, int* outCurve, int skip) { //colormult = chroma_scale for Lab manipulations bool needsaturation = (saturation<-0.0001 || saturation>0.0001); // curve without contrast double* dcurve = new double[65536]; // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) val *= colormult; // apply base curve, thus, exposure compensation and black point with shadow and highlight protection //val = basecurve (val, 1.0, 0, colormult, satcompr/100.0, satcompr/100.0); // apply custom/parametric/NURBS curve, if any if (tcurve) { val = tcurve->getVal (val); } // store result in a temporary array dcurve[i] = CLIPD(val); } delete tcurve; // if skip>1, let apply linear interpolation in the skipped points of the curve int prev = 0; for (int i=1; i<=0xffff-skip; i++) { if (i%skip==0) { prev+=skip; continue; } dcurve[i] = ( dcurve[prev] * (skip - i%skip) + dcurve[prev+skip] * (i%skip) ) / skip; } if (needsaturation) { float avg = 0.5; // compute contrast parameter double saturation_b = saturation / 20; if (saturation_b>=0 && saturation_b < 0.00001) saturation_b = 0.00001; else if (saturation_b<0 && saturation_b > -0.00001) saturation_b = -0.00001; // apply contrast enhancement for (int i=0; i<=0xffff; i++) { double val = centercontrast (dcurve[i], saturation_b, avg); outCurve[i] = (int) (65535.0 * CLIPD(val)); } } else for (int i=0; i<=0xffff; i++) outCurve[i] = (int) (65535.0 * dcurve[i]); delete [] dcurve; } */ void CurveFactory::complexsgnCurve (double satclip, double satcompr, double saturation, double colormult, const std::vector& curvePoints, int* outCurve, int skip) { //colormult = chroma_scale for Lab manipulations // check if contrast curve is needed bool needsaturation = (saturation<-0.0001 || saturation>0.0001); // curve without contrast double* dcurve = new double[65536]; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector satcurvePoints; satcurvePoints.push_back((double)((CurveType)NURBS)); if (saturation>0) { satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range satcurvePoints.push_back(0.25+saturation/500.0); //toe point satcurvePoints.push_back(0.25-saturation/500.0); //value at toe point satcurvePoints.push_back(0.75-saturation/500.0); //shoulder point satcurvePoints.push_back(0.75+saturation/500.0); //value at shoulder point satcurvePoints.push_back(1); // white point satcurvePoints.push_back(1); // value at white point } else { satcurvePoints.push_back(0); satcurvePoints.push_back(-0.5*(saturation/100.0)); satcurvePoints.push_back(1); satcurvePoints.push_back(1+saturation/200.0); } Curve* satcurve = NULL; satcurve = new Curve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) val *= colormult; // apply saturation curve if (needsaturation) val = satcurve->getVal (val); // apply custom/parametric/NURBS curve, if any if (tcurve) { val = tcurve->getVal (val); } // store result in a temporary array dcurve[i] = CLIPD(val); } delete tcurve; // if skip>1, let apply linear interpolation in the skipped points of the curve int prev = 0; for (int i=1; i<=0xffff-skip; i++) { if (i%skip==0) { prev+=skip; continue; } dcurve[i] = ( dcurve[prev] * (skip - i%skip) + dcurve[prev+skip] * (i%skip) ) / skip; } for (int i=0; i<=0xffff; i++) outCurve[i] = (int) (65535.0 * dcurve[i]); delete [] dcurve; delete satcurve; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, double shcompr, double br, double contr, double defmul, double gamma_, bool igamma, const std::vector& curvePoints, unsigned int* histogram, float* hlCurve, float* shCurve, int* outCurve, unsigned int* outBeforeCCurveHistogram, 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; // a: slope of the curve, black: starting point at the x axis double a = pow (2.0, ecomp); // curve without contrast double* dcurve = new double[65536]; // check if contrast curve is needed bool needcontrast = contr>0.00001 || contr<-0.00001; // check if inverse gamma is needed at the end bool needigamma = !needcontrast && igamma && gamma_>0; // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) memset (outBeforeCCurveHistogram, 0, 256*sizeof(int)); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector brightcurvePoints; brightcurvePoints.push_back((double)((CurveType)NURBS)); brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range if(br>0) { brightcurvePoints.push_back(0.1); //toe point brightcurvePoints.push_back(0.1+br/150.0); //value at toe point brightcurvePoints.push_back(0.7); //shoulder point brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point } else { brightcurvePoints.push_back(0.1-br/150.0); //toe point brightcurvePoints.push_back(0.1); //value at toe point brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); //shoulder point brightcurvePoints.push_back(0.7); //value at shoulder point } brightcurvePoints.push_back(1); // white point brightcurvePoints.push_back(1); // value at white point Curve* brightcurve = NULL; brightcurve = new Curve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for (int i=0; i<=0xffff; i++) { // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) // val *= def_mul; // apply base curve, thus, exposure compensation and black point with shadow and highlight protection val = basecurve (val*def_mul, a, 0, def_mul, hlcompr/100.0, 0); //val = basecurve (val*def_mul, a, black, def_mul, hlcompr/100.0, 1.5*shcompr/100.0); hlCurve[i] = (65535.0 * CLIPD(val)); //%%%%%%%%%%%%%%%%%%%%%%%%%% // change to [0,1] range val = (double)i / 65535.0; val = basecurve (val, 1, black, 1, 0, 1.5*shcompr/100.0); shCurve[i] = (65535.0 * CLIPD(val)); } //%%%%%%%%%%%%%%%%%%%%%%%%%% for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { // change to [0,1] range double val = (double)i / 65535.0; float val0 = val; // gamma correction if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); // apply brightness curve //val = brightness (val, br/100.0); val = brightcurve->getVal (val); // apply custom/parametric/NURBS curve, if any if (tcurve) { if (outBeforeCCurveHistogram) { float hval = shCurve[(int)(hlCurve[i])]*(float)val/(65535*val0); //if (needigamma) // hval = igamma2 (hval); int hi = (int)(255.0*CLIPD(hval)); outBeforeCCurveHistogram[hi]+=histogram[i] ; } val = tcurve->getVal (val); } // if inverse gamma is needed, do it (standard sRGB inverse gamma is applied) if (needigamma) val = igamma2 (val); // store result in a temporary array dcurve[i] = CLIPD(val); } delete tcurve; delete brightcurve; // if skip>1, let apply linear interpolation in the skipped points of the curve int prev = 0; for (int i=1; i<=0xffff-skip; i++) { if (i%skip==0) { prev+=skip; continue; } dcurve[i] = ( dcurve[prev] * (skip - i%skip) + dcurve[prev+skip] * (i%skip) ) / skip; } if (needcontrast) { // compute mean luminance of the image with the curve applied int sum = 0; double avg = 0; //double sqavg = 0; for (int i=0; i<=0xffff; i++) { avg += dcurve[(int)shCurve[(int)hlCurve[i]]] * histogram[i]; //sqavg += dcurve[i]*dcurve[i] * histogram[i]; sum += histogram[i]; } avg /= sum; //sqavg /= sum; //double stddev = sqrt(sqavg-avg*avg); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector contrastcurvePoints; contrastcurvePoints.push_back((double)((CurveType)NURBS)); contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); //toe point contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); //value at toe point contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); //shoulder point contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); //value at shoulder point contrastcurvePoints.push_back(1); // white point contrastcurvePoints.push_back(1); // value at white point Curve* contrastcurve = NULL; contrastcurve = new Curve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // apply contrast enhancement for (int i=0; i<=0xffff; i++) { //double val = centercontrast (dcurve[i], contr_b, avg); double val = contrastcurve->getVal (dcurve[i]); if (igamma && gamma_>0) val = igamma2 (val); outCurve[i] = (int) (65535.0 * CLIPD(val)); } delete contrastcurve; } else for (int i=0; i<=0xffff; i++) outCurve[i] = (int) (65535.0 * dcurve[i]); delete [] dcurve; } */ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, double shcompr, double br, double contr, double defmul, double gamma_, bool igamma, const std::vector& curvePoints, unsigned int* histogram, float* hlCurve, float* shCurve, int* outCurve, unsigned int* outBeforeCCurveHistogram, 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; // a: slope of the curve, black: starting point at the x axis double a = pow (2.0, ecomp); // curve without contrast double* dcurve = new double[65536]; // check if contrast curve is needed bool needcontrast = contr>0.00001 || contr<-0.00001; // check if inverse gamma is needed at the end bool needigamma = igamma && gamma_>0; // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) memset (outBeforeCCurveHistogram, 0, 256*sizeof(int)); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector brightcurvePoints; brightcurvePoints.push_back((double)((CurveType)NURBS)); brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range if(br>0) { brightcurvePoints.push_back(0.1); //toe point brightcurvePoints.push_back(0.1+br/150.0); //value at toe point brightcurvePoints.push_back(0.7); //shoulder point brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point } else { brightcurvePoints.push_back(0.1-br/150.0); //toe point brightcurvePoints.push_back(0.1); //value at toe point brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); //shoulder point brightcurvePoints.push_back(0.7); //value at shoulder point } brightcurvePoints.push_back(1); // white point brightcurvePoints.push_back(1); // value at white point Curve* brightcurve = NULL; brightcurve = new Curve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for (int i=0; i<=0xffff; i++) { // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) // val *= def_mul; // apply base curve, thus, exposure compensation and black point with shadow and highlight protection val = basecurve (val*def_mul, a, 0, def_mul, hlcompr/100.0, 0); //val = basecurve (val*def_mul, a, black, def_mul, hlcompr/100.0, 1.5*shcompr/100.0); hlCurve[i] = (65535.0 * CLIPD(val)); //%%%%%%%%%%%%%%%%%%%%%%%%%% // change to [0,1] range val = (double)i / 65535.0; val = basecurve (val, 1, black, 1, 0, 1.5*shcompr/100.0); shCurve[i] = (65535.0 * CLIPD(val)); //%%%%%%%%%%%%%%%%%%%%%%%%%% // change to [0,1] range val = (double)i / 65535.0; // gamma correction if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); // apply brightness curve //val = brightness (val, br/100.0); val = brightcurve->getVal (val); // store result in a temporary array dcurve[i] = CLIPD(val); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if (needcontrast) { // compute mean luminance of the image with the curve applied int sum = 0; double avg = 0; //double sqavg = 0; for (int i=0; i<=0xffff; i++) { avg += dcurve[(int)shCurve[(int)hlCurve[i]]] * histogram[i]; //sqavg += dcurve[i]*dcurve[i] * histogram[i]; sum += histogram[i]; } avg /= sum; //sqavg /= sum; //double stddev = sqrt(sqavg-avg*avg); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector contrastcurvePoints; contrastcurvePoints.push_back((double)((CurveType)NURBS)); contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); //toe point contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); //value at toe point contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); //shoulder point contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); //value at shoulder point contrastcurvePoints.push_back(1); // white point contrastcurvePoints.push_back(1); // value at white point Curve* contrastcurve = NULL; contrastcurve = new Curve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // apply contrast enhancement for (int i=0; i<=0xffff; i++) { //double val = centercontrast (dcurve[i], contr_b, avg); dcurve[i] = contrastcurve->getVal (dcurve[i]); } delete contrastcurve; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for (int i=0; i<=0xffff; i++) { float val; // apply custom/parametric/NURBS curve, if any if (tcurve) { if (outBeforeCCurveHistogram) { float hval = dcurve[(int)shCurve[(int)(hlCurve[i])]]; //if (needigamma) // hval = igamma2 (hval); int hi = (int)(255.0*CLIPD(hval)); outBeforeCCurveHistogram[hi]+=histogram[i] ; } val = tcurve->getVal (dcurve[i]); } else { val = (dcurve[i]); } // if inverse gamma is needed, do it (standard sRGB inverse gamma is applied) if (needigamma) val = igamma2 (val); outCurve[i] = (int) (65535.0 * val + 0.5); } delete [] dcurve; delete tcurve; delete brightcurve; if (outBeforeCCurveHistogram) { //for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]); } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int CurveFactory::gammatab [65536]; int CurveFactory::igammatab_srgb [65536]; int CurveFactory::gammatab_srgb [65536]; void CurveFactory::init () { for (int i=0; i<65536; i++) gammatab_srgb[i] = (int)(65535 * gamma2 (i/65535.0)); for (int i=0; i<65536; i++) igammatab_srgb[i] = (int)(65535 * igamma2 (i/65535.0)); for (int i=0; i<65536; i++) gammatab[i] = (int)(65535 * pow (i/65535.0, 0.454545)); /* FILE* f = fopen ("c.txt", "wt"); for (int i=0; i<256; i++) fprintf (f, "%g %g\n", i/255.0, clower (i/255.0, 2.0, 1.0)); fclose (f);*/ } }