Moved debayer and preprocessing parameters to class ProcParams for every single image. Added tab RAW for changing those parameters. Progress bar shows only load step (work to do)
561 lines
16 KiB
C++
561 lines
16 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 <glib.h>
|
|
#include <glib/gstdio.h>
|
|
#include <curves.h>
|
|
#include <math.h>
|
|
#include <vector>
|
|
#include <mytime.h>
|
|
#include <string.h>
|
|
|
|
#undef CLIPD
|
|
#define CLIPD(a) ((a)>0.0?((a)<1.0?(a):1.0):0.0)
|
|
|
|
|
|
namespace rtengine {
|
|
|
|
Curve::Curve (const std::vector<double>& 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<N; i++) {
|
|
x[i] = p[ix++];
|
|
y[i] = p[ix++];
|
|
}
|
|
if (kind==Spline)
|
|
spline_cubic_set ();
|
|
else if (kind==NURBS && N > 2)
|
|
NURBS_set ();
|
|
}
|
|
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;
|
|
}
|
|
|
|
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 () {
|
|
|
|
std::vector<double> sc_x; // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...)
|
|
std::vector<double> sc_y; // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...)
|
|
std::vector<double> sc_length; // Length of the subcurves
|
|
double total_length=0;
|
|
|
|
// Create the list of Bezier sub-curves
|
|
// NURBS_set is called if N > 2 only
|
|
|
|
for (int i = 0; i < N-1;) {
|
|
double length;
|
|
|
|
// first point (on the curve)
|
|
double sc_x2, sc_y2;
|
|
if (!i) {
|
|
sc_x2 = x[i];
|
|
sc_y2 = y[i];
|
|
i++;
|
|
}
|
|
else {
|
|
sc_x2 = (x[i-1] + x[i]) / 2.;
|
|
sc_y2 = (y[i-1] + y[i]) / 2.;
|
|
}
|
|
sc_x.push_back(sc_x2);
|
|
sc_y.push_back(sc_y2);
|
|
|
|
// second point (control point)
|
|
sc_x.push_back(x[i]);
|
|
sc_y.push_back(y[i]);
|
|
length = sqrt(pow(x[i] - sc_x2,2) + pow(y[i] - sc_y2,2));
|
|
i++;
|
|
|
|
// third point (on the curve)
|
|
if (i==N-1) {
|
|
sc_x2 = x[i];
|
|
sc_y2 = y[i];
|
|
i++;
|
|
}
|
|
else {
|
|
sc_x2 = (x[i-1] + x[i]) / 2.;
|
|
sc_y2 = (y[i-1] + y[i]) / 2.;
|
|
}
|
|
sc_x.push_back(sc_x2);
|
|
sc_y.push_back(sc_y2);
|
|
length += sqrt(pow(x[i] - sc_x2,2) + pow(y[i] - sc_y2,2));
|
|
|
|
// Storing the length of all sub-curves and the total length (to have a better distribution
|
|
// of the points along the curve)
|
|
sc_length.push_back(length);
|
|
total_length += length;
|
|
}
|
|
sc_x.begin();
|
|
sc_y.begin();
|
|
sc_length.begin();
|
|
|
|
// create the polyline with the number of points adapted to the X range of the sub-curve
|
|
for (int i=0; i < 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) + (i==0 ? 1 : 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 (int j=1; j<nbr_points-1; j++) {
|
|
double t = j*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 (t<x[2]) {
|
|
// add shadows effect:
|
|
double sc = -log(2.0)/log(x[1]/x[2]);
|
|
double stv = exp(sc*log(stretched/fc));
|
|
double sbase = pfull (stv, x[8], x[7], 0.5);
|
|
double sstretched = fc*(sbase<=1e-14 ? 0.0 : exp(log(sbase)/sc));
|
|
return sstretched;
|
|
}
|
|
else {
|
|
// add highlights effect:
|
|
double hc = -log(2.0)/log((x[3]-x[2])/(1-x[2]));
|
|
double htv = exp(hc*log((stretched-fc)/(1-fc)));
|
|
double hbase = pfull (htv, x[8], 0.5, x[4]);
|
|
double hstretched = fc + (1-fc)*(hbase<=1e-14 ? 0.0 : exp(log(hbase)/hc));
|
|
return hstretched;
|
|
}
|
|
break;
|
|
}
|
|
case Linear :
|
|
case Spline : {
|
|
// values under and over the first and last point
|
|
if (t>x[N-1])
|
|
return y[N-1];
|
|
else if (t<x[0])
|
|
return y[0];
|
|
|
|
// do a binary search for the right interval:
|
|
int k_lo = 0, k_hi = N - 1;
|
|
while (k_hi - k_lo > 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<x[0])
|
|
return y[0];
|
|
else if (N == 2)
|
|
return y[0] + (t - x[0]) * ( y[1] - y[0] ) / (x[1] - x[0]);
|
|
|
|
// do a binary search for the right interval:
|
|
int k_lo = 0, k_hi = poly_x.size() - 1;
|
|
while (k_hi - k_lo > 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<double>& t, std::vector<double>& 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; i<t.size(); i++)
|
|
res[i] = getVal(t[i]);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
/*
|
|
void CurveFactory::updateCurve3 (int* curve, int* ohistogram, const std::vector<double>& 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<double>& 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, CURVES_MIN_POLY_POINTS/skip);
|
|
|
|
// clear array that stores histogram valid before applying the custom curve
|
|
if (outBeforeCCurveHistogram)
|
|
memset (outBeforeCCurveHistogram, 0, 256*sizeof(int));
|
|
|
|
//float atmp=a;
|
|
//a = 1;
|
|
|
|
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;
|
|
|
|
//exposure compensation
|
|
//val *= atmp;
|
|
//black *= atmp;
|
|
//D *= atmp;
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
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);*/
|
|
}
|
|
|
|
}
|
|
|