Files
rawTherapee/rtengine/EdgePreserveLab.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

230 lines
8.7 KiB
C++

#include "EdgePreserveLab.h"
#include "boxblur.h"
#include <cstdlib>
#ifdef _OPENMP
#include <omp.h>
#endif
//#define MAX(a,b) ((a)<(b)?(b):(a))
//#define MIN(a,b) ((a)>(b)?(b):(a))
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
EdgePreserveLab::EdgePreserveLab(unsigned int width, unsigned int height){
w = width;
h = height;
n = w*h;
//Initialize the matrix just once at construction.
A = new MultiDiagonalSymmetricMatrix(n, 5);
if(!(
A->CreateDiagonal(0, 0) &&
A->CreateDiagonal(1, 1) &&
A->CreateDiagonal(2, w - 1) &&
A->CreateDiagonal(3, w) &&
A->CreateDiagonal(4, w + 1))){
delete A;
A = NULL;
printf("Error in EdgePreserveLab construction: out of memory.\n");
}else{
a0 = A->Diagonals[0];
a_1 = A->Diagonals[1];
a_w1 = A->Diagonals[2];
a_w = A->Diagonals[3];
a_w_1 = A->Diagonals[4];
}
}
EdgePreserveLab::~EdgePreserveLab(){
delete A;
}
float *EdgePreserveLab::CreateBlur(float *Source, float LScale, float abScale, float EdgeStoppingLuma, float EdgeStoppingChroma, unsigned int Iterates, float *Blur, bool UseBlurForEdgeStop){
if(Blur == NULL)
UseBlurForEdgeStop = false, //Use source if there's no supplied Blur.
Blur = new float[3*n];
if(LScale == 0.0f){
memcpy(Blur, Source, 3*n*sizeof(float));
return Blur;
}
//Create the edge stopping function a, rotationally symmetric and just one instead of (ax, ay). Maybe don't need Blur yet, so use its memory.
float *a, *b, *g;
if(UseBlurForEdgeStop) a = new float[n], g = Blur;
else a = Blur, g = Source;
//b = new float[n];
unsigned int x, y, i;
unsigned int w1 = w - 1, h1 = h - 1;
float eps = 0.0001f;
float scL = powf(100.0f,LScale);
float scab = powf(200.0f,abScale);
float * var = new float[w*h];
rtengine::boxvar(g, var, 1, 1, w, h);
#ifdef _OPENMP
#pragma omp parallel for
#endif
for(y = 0; y < h1; y++){
float *rg = &g[w*y];
for(x = 0; x < w1; x++){
//Estimate the central difference gradient in the center of a four pixel square. (gx, gy) is actually 2*gradient.
/*float gx = (fabs((rg[x + 1] - rg[x]) + (rg[x + w + 1] - rg[x + w])));
float gy = (fabs((rg[x + w] - rg[x]) + (rg[x + w + 1] - rg[x + 1])));
//TODO: combine this with gx, gy if not needing separate quantities
float hx = (fabs((rg[x + 1 + n] - rg[x + n]) + (rg[x + w + 1 + n] - rg[x + w + n])) + \
fabs((rg[x + 1 + 2*n] - rg[x + 2*n]) + (rg[x + w + 1 + 2*n] - rg[x + w + 2*n])));
float hy = (fabs((rg[x + w + n] - rg[x + n]) + (rg[x + w + 1 + n] - rg[x + 1 + n])) + \
fabs((rg[x + w + 2*n] - rg[x + 2*n]) + (rg[x + w + 1 + 2*n] - rg[x + 1 + 2*n])));
*/
//float gradtot = (gx+gy+hx+hy);
//gradhisto[MAX(0,MIN(32767,(int)gradtot))] ++;
//Apply power to the magnitude of the gradient to get the edge stopping function.
//a[x + w*y] = scL*expf(-100.0f*(gx + gy + hx + hy)/(EdgeStoppingLuma));
//a[x + w*y] = scL*expf(-var[y*w+x]/SQR(0.02*EdgeStoppingLuma));///(0.1+rg[x]);
a[x + w*y] = scL*expf(-50.0f*sqrt(var[y*w+x])/(EdgeStoppingLuma+eps));///(0.1+rg[x]);
//b[x + w*y] = (scab)*expf(-20.0f*(gx + gy + Lave*(hx + hy))/(EdgeStoppingChroma));
//b[x + w*y] = (scab)*expf(-400.0f*SQR(gx + gy + Lave*(hx + hy))/SQR(EdgeStoppingChroma));;
}
}
/* Now setup the linear problem. I use the Maxima CAS, here's code for making an FEM formulation for the smoothness term:
p(x, y) := (1 - x)*(1 - y);
P(m, n) := A[m][n]*p(x, y) + A[m + 1][n]*p(1 - x, y) + A[m + 1][n + 1]*p(1 - x, 1 - y) + A[m][n + 1]*p(x, 1 - y);
Integrate(f) := integrate(integrate(f, x, 0, 1), y, 0, 1);
Integrate(diff(P(u, v), x)*diff(p(x, y), x) + diff(P(u, v), y)*diff(p(x, y), y));
Integrate(diff(P(u - 1, v), x)*diff(p(1 - x, y), x) + diff(P(u - 1, v), y)*diff(p(1 - x, y), y));
Integrate(diff(P(u - 1, v - 1), x)*diff(p(1 - x, 1 - y), x) + diff(P(u - 1, v - 1), y)*diff(p(1 - x, 1 - y), y));
Integrate(diff(P(u, v - 1), x)*diff(p(x, 1 - y), x) + diff(P(u, v - 1), y)*diff(p(x, 1 - y), y));
So yeah. Use the numeric results of that to fill the matrix A.*/
memset(a_1, 0, A->DiagonalLength(1)*sizeof(float));
memset(a_w1, 0, A->DiagonalLength(w - 1)*sizeof(float));
memset(a_w, 0, A->DiagonalLength(w)*sizeof(float));
memset(a_w_1, 0, A->DiagonalLength(w + 1)*sizeof(float));
//TODO: OMP here?
for(i = y = 0; y != h; y++){
for(x = 0; x != w; x++, i++){
float ac;
a0[i] = 1.0;
//Remember, only fill the lower triangle. Memory for upper is never made. It's symmetric. Trust.
if(x > 0 && y > 0)
ac = a[i - w - 1]/6.0f,
a_w_1[i - w - 1] -= 2.0f*ac, a_w[i - w] -= ac,
a_1[i - 1] -= ac, a0[i] += 4.0f*ac;
if(x < w1 && y > 0)
ac = a[i - w]/6.0f,
a_w[i - w] -= ac, a_w1[i - w + 1] -= 2.0f*ac,
a0[i] += 4.0f*ac;
if(x > 0 && y < h1)
ac = a[i - 1]/6.0f,
a_1[i - 1] -= ac, a0[i] += 4.0f*ac;
if(x < w1 && y < h1)
a0[i] += 4.0f*a[i]/6.0f;
}
}
if(UseBlurForEdgeStop) delete[] a;
//Solve & return.
A->CreateIncompleteCholeskyFactorization(1); //Fill-in of 1 seems to work really good. More doesn't really help and less hurts (slightly).
if(!UseBlurForEdgeStop) memcpy(Blur, Source, 3*n*sizeof(float));
// blur L channel
SparseConjugateGradient(A->PassThroughVectorProduct, Source, n, false, Blur, 0.0f, (void *)A, Iterates, A->PassThroughCholeskyBackSolve);
//reset A for ab channels
/*memset(a_1, 0, A->DiagonalLength(1)*sizeof(float));
memset(a_w1, 0, A->DiagonalLength(w - 1)*sizeof(float));
memset(a_w, 0, A->DiagonalLength(w)*sizeof(float));
memset(a_w_1, 0, A->DiagonalLength(w + 1)*sizeof(float));
for(i = y = 0; y != h; y++){
for(x = 0; x != w; x++, i++){
float ac;
a0[i] = 1.0;
//Remember, only fill the lower triangle. Memory for upper is never made. It's symmetric. Trust.
if(x > 0 && y > 0)
ac = b[i - w - 1]/6.0f,
a_w_1[i - w - 1] -= 2.0f*ac, a_w[i - w] -= ac,
a_1[i - 1] -= ac, a0[i] += 4.0f*ac;
if(x < w1 && y > 0)
ac = b[i - w]/6.0f,
a_w[i - w] -= ac, a_w1[i - w + 1] -= 2.0f*ac,
a0[i] += 4.0f*ac;
if(x > 0 && y < h1)
ac = b[i - 1]/6.0f,
a_1[i - 1] -= ac, a0[i] += 4.0f*ac;
if(x < w1 && y < h1)
a0[i] += 4.0f*b[i]/6.0f;
}
}*/
/*if(UseBlurForEdgeStop)*/ //delete[] b;
// blur ab channels
//A->CreateIncompleteCholeskyFactorization(1); //Fill-in of 1 seems to work really good. More doesn't really help and less hurts (slightly).
//SparseConjugateGradient(A->PassThroughVectorProduct, Source+n, n, false, Blur+n, 0.0f, (void *)A, Iterates, A->PassThroughCholeskyBackSolve);
//SparseConjugateGradient(A->PassThroughVectorProduct, Source+2*n, n, false, Blur+2*n, 0.0f, (void *)A, Iterates, A->PassThroughCholeskyBackSolve);
A->KillIncompleteCholeskyFactorization();
return Blur;
}
float *EdgePreserveLab::CreateIteratedBlur(float *Source, float LScale, float abScale, float EdgeStoppingLuma, float EdgeStoppingChroma, unsigned int Iterates, unsigned int Reweightings, float *Blur){
//Simpler outcome?
if(Reweightings == 0) return CreateBlur(Source, LScale, abScale, EdgeStoppingLuma, EdgeStoppingChroma, Iterates, Blur);
//Create a blur here, initialize.
if(Blur == NULL) Blur = new float[3*n];
memcpy(Blur, Source, 3*n*sizeof(float));
//Iteratively improve the blur.
Reweightings++;
for(unsigned int i = 0; i != Reweightings; i++)
CreateBlur(Source, LScale, abScale, EdgeStoppingLuma, EdgeStoppingChroma, Iterates, Blur, true);
return Blur;
}
float *EdgePreserveLab::CompressDynamicRange(float *Source, float LScale, float abScale, float EdgeStoppingLuma, float EdgeStoppingChroma, float CompressionExponent, float DetailBoost, unsigned int Iterates, unsigned int Reweightings, float *Compressed){
//We're working with luminance, which does better logarithmic.
unsigned int i;
//for(i = 0; i != n; i++)
// Source[i] = logf(Source[i] + 0.0001f);
//Blur. Also setup memory for Compressed (we can just use u since each element of u is used in one calculation).
float *u = CreateIteratedBlur(Source, LScale, abScale, EdgeStoppingLuma, EdgeStoppingChroma, Iterates, Reweightings);
if(Compressed == NULL) Compressed = u;
//Apply compression, detail boost, unlogging. Compression is done on the logged data and detail boost on unlogged.
for(i = 0; i != n; i++){
//float ce = expf(Source[i] + u[i]*(CompressionExponent - 1.0f)) - 0.0001f;
//float ue = expf(u[i]) - 0.0001f;
//Source[i] = expf(Source[i]) - 0.0001f;
//Compressed[i] = ce + DetailBoost*(Source[i] - ue);
Compressed[i] = u[i];//ue;//for testing, to display blur
}
if(Compressed != u) delete[] u;
return Compressed;
}