/* * 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) : x(NULL), y(NULL), ypp(NULL) { if (p.size()<3) { kind = 0; } else { kind = p[0]; if (kind==-1 || kind==1) { N = (p.size()-1)/2; x = new double[N]; y = new double[N]; int ix = 1; for (int i=0; i= 0; --k) ypp[k] = ypp[k] * ypp[k + 1] + u[k]; delete [] u; } double Curve::getVal (double t) { if (!kind) return t; if (kind==2) { 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]; if (kind==-1) return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; else if (kind==1) { 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; if (r < 0.0) return 0.0; if (r > 1.0) return 1.0; return r; } else 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 (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::updateCurve3 (int* curve, int* ohistogram, const std::vector& points, double defmul, double ecomp, int black, double hlcompr, double shcompr, double br, double contr, double gamma_, bool igamma, int 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 = gamma_>0 ? gamma (def_mul, gamma_, start, slope, mul, add) : def_mul; double a = pow (2.0, ecomp); double b = black / 65535.0; // curve without contrast double* dcurve = new double[65536]; bool needcontrast = contr>0.00001 || contr<-0.00001; bool needigamma = !needcontrast && igamma && gamma_>0; // create a curve if needed Curve* tcurve = NULL; if (points.size()>0 && points[0]!=0) tcurve = new Curve (points); for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { double val = (double)i / 65535.0; val *= def_mul; if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); val = basecurve (val, a, b, D, hlcompr/100.0, shcompr/100.0); val = brightness (val, br/100.0); if (tcurve) val = tcurve->getVal (val); if (needigamma) val = igamma2 (val); if (val>1.0) val = 1.0; else if (val<0.0) val = 0.0; dcurve[i] = val; } delete tcurve; /* if (igamma) { FILE* f = fopen ("curve.txt","wt"); for (int i=0; i<65536; i++) // fprintf (f, "%g\t%g\n", i/65535.0, basel(i/65535.0, 2, 0)); fprintf (f, "%g\t%g\n", i/65535.0, clower(i/65535.0, 0.500015/0.5, 1.5)); // fprintf (f, "%g\t%g\n", i/65535.0, basecurve(i/65535.0, 1.25701, 0, 1.47694, 1.0, 1.0)); // fprintf (f, "%g\t%g\n", i/65535.0, dcurve[i]); fclose (f); } */ /* 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] * ohistogram[i]; sum += ohistogram[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); if (val>1.0) val = 1.0; if (val<0.0) val = 0.0; curve[i] = (int) (65535.0 * val); } } else for (int i=0; i<=0xffff; i++) curve[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, int* outCurve, unsigned int* outBeforeCCurveHistogram, int 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 = gamma_>0 ? gamma (def_mul, gamma_, start, slope, mul, add) : 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); // 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] rage double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) val *= def_mul; // gamma correction if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); // 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); // apply brightness curve val = brightness (val, br/100.0); // apply custom/parametric 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; } 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);*/ } }