/* * 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 namespace rtengine { Curve::Curve (const char* iname, const char* descr) : islinear(false), isempty(false) { ypp = NULL; name = iname; char* buffer = new char[strlen(descr)+1]; strcpy (buffer, descr); char* token = strtok (buffer, ",; \t\n"); std::vector xv; std::vector yv; while (token) { double xd = atof (token); token = strtok (NULL, ",; \t\n"); if (token) { double yd = atof (token); xv.push_back (xd); yv.push_back (yd); } token = strtok (NULL, ",; \t\n"); } N = xv.size (); x = new double[N]; y = new double[N]; for (int i=0; i& p) { x = NULL; y = NULL; ypp = NULL; name = "custom"; isempty = true; N = p.size()/2; if (N<2) return; int ix = 0; islinear = p[ix++]<0; x = new double[N]; y = new double[N]; for (int i=0; i= 0; --k) ypp[k] = ypp[k] * ypp[k + 1] + u[k]; delete [] u; } double Curve::getVal (double t) { // // Determine the interval [ T(I), T(I+1) ] that contains TVAL. // Values below T[0] or above T[N-1] use extrapolation. // if (isempty) return t; if (t>x[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 (islinear) return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; else { double a = (x[k_hi] - t) / h; double b = (t - x[k_lo]) / h; return 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 (t>x[N-1]) return y[N-1]; else if (t CurveFactory::curves; /*double CurveFactory::centercontrast (double x, double b, double m) { if (b==0) return x; if (b>0) { if (x>m) return m + (1.0-m) * tanh (b*(x-m)/(1.0-m)) / tanh (b); else return m - m * tanh (b*(m-x)/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*(m-x)/m) / tanh (b); } } */ double CurveFactory::centercontrast (double x, double b, double m) { if (b==0) return x; if (b>0) { 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); } } double CurveFactory::contrast (double x, double a) { if (a==0) return x; else if (a>0) { double s = (1.0+exp(-0.5*a)) / (1.0+exp(-(x-0.5)*a)) * (exp(0.5*a)-exp(-(x-0.5)*a)) / (exp(0.5*a)-exp(-0.5*a)); return s; } else { double s = (1.0+exp(-0.5*a)) / (1.0+exp(-(x-0.5)*a)) * (exp(0.5*a)-exp(-(x-0.5)*a)) / (exp(0.5*a)-exp(-0.5*a)); return 2*x - s; } } double CurveFactory::brightness (double x, double a, double bd1, double bd2) { if (a==1) return x; else if (a<1) return a*x; else { if (x < 1.0/a-bd1) return a*x; else if (x > 1.0/a+bd2) return 1; else { double d = bd1+bd2; double s = - (-a*d*(1.0+a*(bd2-x))*(1.0+a*(bd2-x))*(-1.0+a*(bd1+x))-(2.0+a*(bd1+3.0*bd2-2.0*x))*(-1.0+a*(bd1+x))*(-1.0+a*(bd1+x)) + (-1.0+a*bd1)*(1.0+a*(bd2-x))*(1.0+a*(bd2-x))*(-2.0+a*(3.0*bd1+bd2+2.0*x))) / (a*a*a*d*d*d); return s; } } } double CurveFactory::softClip (double x, double d1, double d2, double a, double b, double c, double d) { if (x<1.0-d1) return x; else if (x>1.0+d2) return 1.0; else return a*x*x*x + b*x*x + c*x + d; } double CurveFactory::dlower (const double b, const double m, const double c) { return b / (tanh(b) * 2.0 * m); } double CurveFactory::dupper (const double b, const double m, const double c) { return b / (tanh(b) * 2.0 * (c-m)); } double CurveFactory::solve_lower (double m, double c, double deriv) { double b_u = 2.0*m*deriv; double b_l = 0.0; double b; while (b_u-b_l > 0.0000001) { b = (b_u+b_l) / 2.0; if (dlower(b,m,c) 0.0000001) { b = (b_u+b_l) / 2.0; if (dupper(b,m,c)& 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; 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; } /* 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::updateCurve2 (int* curve, int* ohistogram, const std::vector& points, double ecomp, double br, int black, double hlcompr, double shcompr, double contr, double gamma_, bool igamma, int skip) { double ec_mul = pow (2, ecomp); double bl = black / 65535.0; double hi = pow (2.0,-br) + bl; // 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; // compute parameters of the "levels" curve shcompr /= 100.0; hlcompr /= 100.0; double correction = hlcompr<0.85 ? 1.0 : hlcompr+0.15; correction = 1.0; double d = pow (2.0,br); double m = 0.5 / d + bl; double c = (gamma_>0 ? gamma (ec_mul, gamma_, start, slope, mul, add) : ec_mul) * correction; // double c = (gamma_>0 ? gamma (ec_mul, gamma_, start, slope, mul, add) : gamma2(ec_mul)) * correction; double b_upper = solve_upper (m, c, d); double b_lower = solve_lower (m, c, d); // generate curve without contrast (in double) // Curve* tcurve = curves[type]; Curve* tcurve = new Curve (points); if (tcurve->isEmpty()) { delete tcurve; tcurve = NULL; } double* dcurve = new double[65536]; double bltanh = tanh (b_lower); double butanh = tanh (b_upper); if (d * (c - bl) < 1) hlcompr = 0; bool needcontrast = contr>0.00001 || contr<-0.00001; bool needigamma = !needcontrast && igamma && gamma_>0; for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { double val = (double)i / 65535.0; val *= ec_mul; if (gamma_>0) val = gamma (val, gamma_, start, slope, mul, add); // double sval = levels (val, b_lower, b_upper, m, c); // Acceleration: double sval; if (val <= m) { double ttag = 2.0 / (1.0 + exp (-2.0*b_lower*(val-m)/m)) - 1.0; // sval = (1.0 + tanh (b_lower*(val-m)/m) / bltanh) / 2.0; sval = (1.0 + ttag / bltanh) / 2.0; } else { double ttag = 2.0 / (1.0 + exp (-2.0*b_upper*(val-m)/(c-m))) - 1.0; // sval = (1.0 + tanh (b_upper*(val-m)/(c-m)) / butanh) / 2.0; sval = (1.0 + ttag / butanh) / 2.0; } if (valhi) val = (1.0 - hlcompr) + hlcompr * sval; else if (valgetVal (val); if (needigamma) val = igamma2 (val); if (val>1.0) val = 1.0; else if (val<0.0) val = 0.0; dcurve[i] = val; } 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]); //if (igamma) { // FILE* f = fopen ("curve.txt","wt"); // for (int i=0; i<65536; i++) // fprintf (f, "%d\t%d\n", i, curve[i]); // fclose (f); //} delete [] dcurve; } int CurveFactory::gammatab [65536]; int CurveFactory::igammatab_srgb [65536]; int CurveFactory::gammatab_srgb [65536]; void CurveFactory::loadCurves (Glib::ustring fname) { 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 = g_fopen (fname.c_str(), "rt"); if (!f) return; setlocale (LC_ALL, "C"); char* buffer = new char[1024]; while (buffer = fgets(buffer, 1024, f)) { int es = 0; int llen = strlen(buffer); for (es = 0; esgetName()] = c; } } delete buffer; setlocale (LC_ALL, ""); } std::vector CurveFactory::curveNames () { std::vector ret; int ix = 0; for (std::map::iterator i = curves.begin(); i!=curves.end(); i++) ret.push_back (i->second->getName()); return ret; } }