/*
* Copyright 2009, 2010 IPOL Image Processing On Line http://www.ipol.im/
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file poisson_lca_lib.c
* @brief gradient, divergence and Poisson routines using DFT.
*
* @author Catalina Sbert <catalina.sbert@uib.es/>
* @author Ana Belén Petro <anabelen.petro@uib.es/>
*/
#include <stdlib.h>
#include <math.h>
#include <fftw3.h>
#include "auxiliary_lib.h"
/*#include "poisson_lca_lib.h"*/
/* M_PI is a POSIX definition */
#ifndef M_PI
/** macro definition for Pi */
#define M_PI 3.14159265358979323846
#endif /* !M_PI */
/**
* @brief compute the gradient of a 2D array using the discrete cosinus transform
*
* We supose that the input image (N X M array) is extended simmetrically across its sides to a
* 2N X 2M array. Then we obtain a function wich is even around (-0.5 , j) ,(N-0.5,j ),
* (i,-0.5) and (i, M-0.5). To compute the gradient we use the following definition
* of cosinus transform, (we write it in dimension 1 for dimension two is similar)
*
* @f$ C(X)_k= 2\sum_{j=0}^{N-1} X_j cos( \frac{\pi k (j+1/2)}{N}) \f$
* Note that with the symmetry C(X)_N=0.
*
* With this definition the sinus transform of the derivative is defined by
*
* @f$ S(X')_k=\frac{-\pi(k+1)}{N } C(X)_{k+1} , k=0,..., N-1 \f$
*
* This new function is odd around -1 and even aroun N-1
* Then we compute X'_k using the invers sinus transform whose definition is
*
* @f$ X'_k=\frac{2\sum_{j=0}^{N-1} S(X')_j sin (\frac{\pi(j+1)(k+1/2)}{N})}{2N} \f$
*
* Note that the result is odd around -0.5 and around N-0.5.
*
* @param data_x partial derivative respect to the first variable
* @param data_y partial derivative respect to the second variable
* @param data input array
* @param nx x-the dimensions of the array
* @param ny y-the dimensions of the array
*
* @return data_x, data_y or NULL if an error occured
*/
static double *gradient(double *data_x, double *data_y, double *data_in, size_t nx, size_t ny)
{
int x,y,dim,dim4;
double *ftout, *ftux, *ftuy;
fftw_plan p;
double normx, normy;
/* pointers to the right and bottom neighbour values */
double *ptr_end, *ptr_in_x1,*ptr_in_y1, *ptr_x, *ptr_y;
/* sanity check */
if (NULL == data_in || NULL == data_x || NULL== data_y)
{
fprintf(stderr, "a pointer is NULL and should not be so\n");
abort();
}
dim=(int)nx*(int)ny;
dim4=4*dim;
/* allocate the memory of fourier transform*/
ftout=(double*)fftw_malloc(sizeof(double)*dim);
ftux=(double*)fftw_malloc(sizeof(double)*dim);
ftuy=(double*)fftw_malloc(sizeof(double)*dim);
/* compute the cosinus transform of the input array*/
p=fftw_plan_r2r_2d((int) ny,(int) nx, data_in, ftout,FFTW_REDFT10,FFTW_REDFT10,FFTW_ESTIMATE);
fftw_execute(p);
normx=M_PI/nx; normy=M_PI/ny;
/* compute the Fourier transform of partial derivatives respect to x and y*/
ptr_in_x1=ftout+1;
ptr_in_y1=ftout+nx;
ptr_x=ftux;
ptr_y=ftuy;
for(y=0;y<(int)ny;y++)
for(x=0;x< (int) nx;x++)
{
if(x < (int) nx-1) *ptr_x=-normx*(x+1)*(*ptr_in_x1);
else *ptr_x=0.;
if(y < (int) ny-1) *ptr_y=-normy*(y+1)*(*ptr_in_y1);
else *ptr_y=0.;
ptr_x++;
ptr_y++;
ptr_in_x1++;
ptr_in_y1++;
}
/* compute the inverse Fourier transform to obtain the gradient of the input array*/
p=fftw_plan_r2r_2d((int) ny,(int) nx, ftux, data_x, FFTW_REDFT01,FFTW_RODFT01,FFTW_ESTIMATE);
fftw_execute(p);
p=fftw_plan_r2r_2d((int) ny, (int) nx, ftuy, data_y, FFTW_RODFT01,FFTW_REDFT01,FFTW_ESTIMATE);
fftw_execute(p);
/* normalize the inverse fourier transform*/
ptr_end=data_x+dim;
ptr_x=data_x;
ptr_y=data_y;
while(ptr_x < ptr_end){
*ptr_x=*ptr_x/dim4;
*ptr_y=*ptr_y/dim4;
ptr_x++; ptr_y++;
}
/*deallocate the memory*/
fftw_destroy_plan(p);
fftw_free(ftout);
fftw_free(ftux); fftw_free(ftuy);
return data_x; return data_y;
}
/**
* @brief compute the Fourier transform of the divergence of a vector field using the
* @brief discrete sinus/cosinus transform
*
* We have two N X M arrays V_1 and V_2. V_1 is odd around (-0.5, j) and (N-0.5, j) and even around
* (i,-0.5) and (i,M-0.5). V_2 is even around (-0.5, j) and (N-0.5, j) and odd around
* (i,-0.5) and (i,M-0.5).
* We compute the Fourier transform of divergence of the vector field V= (V_1, V_2),
*
* @f$ div(V)=\frac{\partial V_1}{\partial x}+\frac{\partial V_2}{\partial y} \f$
*
* Due to the kind of symmetry to compute the Fourier transform of
* @f$ \frac{\partial V_1}{\partial x}\f$ ( respectively @f$ frac{\partial V_2}{\partial y}\f$)
* we use the sinus transform, which in dimension 1 is defined by
*
* @f$ S(X)_k=2\sum_{j=0}^{N-1} X_j sin(\frac{\pi(j+1/2)(k+1)}{N}) \f$
*
* The cosinus transform of the derivative of X is defined by
*
* @f$ C(X')_k=\frac{\pi k}{N} S(X)_{k-1} , k=1,... N-1, C(X')_0=0 \f$
*
* @param div the cosinus transform of the divergence of a vector field
* @param data_x the first component of the vector field
* @param data_y the second component of the vector field
* @param nx x-the dimensions of the array
* @param ny y-the dimensions of the array
*
* @return div or NULL
*/
static double *divergence(double *div, double *data_x, double *data_y, size_t nx, size_t ny)
{
int x,y, dim;
double *ftout,*ptr_div,*ptr_x1;
fftw_plan p;
double normx, normy;
/* sanity check*/
if (NULL == div || NULL == data_x || NULL== data_y)
{
fprintf(stderr, "a pointer is NULL and should not be so\n");
abort();
}
dim=(int)nx*(int)ny;
normx=M_PI/nx; normy=M_PI/ny;
/* allocate the Fourier transform*/
ftout=(double*)fftw_malloc(sizeof(double)*dim);
/* compute the Fourier transform of the first component of the vector field*/
p=fftw_plan_r2r_2d((int) ny,(int) nx, data_x, ftout,FFTW_REDFT10,FFTW_RODFT10,FFTW_ESTIMATE);
fftw_execute(p);
/*compute the fourier transform of the derivative respect to x of the first component*/
ptr_x1=ftout;
ptr_div=div;
for(y=0;y< (int) ny; y++)
for(x=0; x< (int) nx; x++){
if(x==0) *ptr_div=0.;
else {
*ptr_div= normx*x* *(ptr_x1-1);
}
ptr_div++;
ptr_x1++;
}
/* compute the Fourier transform of the second component of the vector field*/
p=fftw_plan_r2r_2d((int) ny, (int) nx,data_y, ftout,FFTW_RODFT10,FFTW_REDFT10,FFTW_ESTIMATE);
fftw_execute(p);
/* compute the fourier transform of the derivative respect to y of the second component and the divergence*/
ptr_x1=ftout;
ptr_div=div+nx;
for(y=1;y< (int) ny; y++)
for(x=0; x< (int) nx; x++){
*ptr_div+=normy*y*(*ptr_x1);
ptr_div++;
ptr_x1++;
}
/* deallocate the memory*/
fftw_destroy_plan(p);
fftw_free(ftout);
return div;
}
/**
* @brief Solve a Poisson equation using FFTW
*
* Given a vector filed V=(ux,uy), this routine solves the Poisson equation
* @f$ \Delta u= div(V)\f$ with homogeneous Neumann boundary conditions
*
*
* @li compute the Fourier transform of the divergence of the vector field V, C(di.
* @li compute the cosinus transform of the laplacian
* @f$ C(\Delta u)_{ij}=-[(\frac{\pi i}{nx})²+(\frac{\pi j}{ny})²] C(u)_{ij}\f$
* @li The cosinus transform of the solution of the Poisson equation is
* @f$ C(u)_{ij}=-\frac{C(div)}{(\frac{\pi i}{nx})²+(\frac{\pi j}{ny})²}
* @li the solution is computed by the inverse cosinus transform
*
* @param ux first component of the vector
* @param uy second component of the vector
* @param nx x-dimension of the array
* @param ny y-dimension of the array
*
* @return data_out
*/
double *solve_poisson_equation(double *data_out, double *ux, double *uy, size_t nx, size_t ny)
{
double *div;
double normx, normy, A;
int x,y,l, dim, dim4;
fftw_plan p;
dim=(int)nx*(int)ny;
dim4=4*dim;
div=(double*)fftw_malloc(sizeof(double)*dim);
/* compute the fourier transform of the divergence of the vector (ux,uy)*/
(void) divergence(div, ux,uy, nx,ny);
normx=M_PI/nx; normy=M_PI/ny;
normx*=normx; normy*=normy;
/* compute the fourier transform of the solution of the Poisson equation*/
for(y=0;y< (int) ny;y++){
l=y*(int)nx;
for(x=0;x< (int) nx;x++){
if( x==0 && y==0) div[0]=0.;
else{
A=(normx*x*x+normy*y*y);
div[l+x]=-div[l+x]/A;
}
}
}
/* compute the inverse fourier transform*/
p=fftw_plan_r2r_2d((int) ny, (int) nx, div, data_out,FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE);
fftw_execute(p);
for(l=0; l< dim; l++) data_out[l]/=dim4;
return data_out;
}
/**
* @brief Linear option for modifying the gradient, solving a Poisson equation using the Fourier Transform
*
* The input array is processed as follow:
*
* @li compute the gradient of the input array
* @li Modify the gradient: amplifying by a factor the gradient in the interior of the dark zones of the image
* @li solve the Poisson equation aplying FFT
*
* @param data_in input array
* @param T threshold for the dark regions
* @param a amplification factor for the gradient
* @param nx x-dimension of the array
* @param ny y-dimension of the array
*
* @return data_out
*/
double *poisson_local(double *data_out, double *data_in, double T, double a, size_t nx, size_t ny)
{
double *ux,*uy;
double *ptr_x,*ptr_y, *ptr_data, *ptr_xr, *ptr_xl,*ptr_yt,*ptr_yb;
int x,y, dim;
if (NULL == data_out || NULL == data_in )
{
fprintf(stderr, "a pointer is NULL and should not be so\n");
abort();
}
dim=(int)nx*(int)ny;
/* allocate the memory for the Fourier transform*/
ux=(double*)fftw_malloc(sizeof(double)*dim);
uy=(double*)fftw_malloc(sizeof(double)*dim);
/* compute the gradient of the input array*/
gradient(ux, uy, data_in, nx,ny);
/* multiply the gradient by a factor a in the interior of the dark zones which are defined by a threshold T*/
ptr_x=ux; ptr_y=uy;
ptr_data=data_in;
ptr_xr=data_in+1;
ptr_xl=data_in-1;
ptr_yt=data_in-nx;
ptr_yb=data_in+nx;
for(y=0; y < (int) ny; y++)
for(x=0; x < (int) nx; x++){
if( x >0 && x < (int) nx-1 && y>0 && y < (int) ny-1)
if(*ptr_xl <= T && *ptr_data <= T && *ptr_xr <= T && *ptr_yt <= T && *ptr_yb <= T){
*ptr_x=a*(*ptr_x); *ptr_y=a* (*ptr_y);
}
ptr_data++; ptr_x++; ptr_y++; ptr_xr++; ptr_xl++; ptr_yt++; ptr_yb++;
}
solve_poisson_equation(data_out,ux,uy, nx,ny);
fftw_free(ux); fftw_free(uy);
return data_out;
}
/**
* @brief Power option for modifying the gradient, solving a Poisson equation using the Fourier Transform
*
* The input array is processed as follow:
*
* @li compute the gradient of the input array
* @li Modify the gradient: power by alpha the gradient
* @li solve the Poisson equation
*
* @param data_in input array
* @param alpha Power function for the gradient
* @param nx x-dimension of the array
* @param ny y-dimension of the array
*
* @return data_out
*/
double *poisson_global(double *data_out, double *data_in, double a, size_t nx, size_t ny)
{
double *ux,*uy;
int l,dim;
double A;
if (NULL == data_out || NULL == data_in )
{
fprintf(stderr, "a pointer is NULL and should not be so\n");
abort();
}
dim=(int)nx*(int)ny;
/* allocate the memory for the Fourier transform*/
ux=(double*)fftw_malloc(sizeof(double)*dim);
uy=(double*)fftw_malloc(sizeof(double)*dim);
/* compute and modify the gradient of the input array*/
gradient(ux, uy, data_in, nx,ny);
for(l=0;l< dim;l++){
A=fabs(ux[l])+fabs(uy[l])+0.1;
A=pow(A, a-1.);
ux[l]*=A;
uy[l]*=A;
}
solve_poisson_equation(data_out, ux, uy, nx,ny);
fftw_free(ux); fftw_free(uy);
return data_out;
}
/**
* @brief The algorithm applied to the gray intensity and the color channels are computed
* such that the ratios R/G/B are preserved.
*
*
* @param data input array
* @param T the threshold to compute the dark regions
* @param a the gradient amplification factor
* @param alpha Power function for the gradient
* @param nx x-dimension of the array
* @param ny y-dimension of the array
* @param channel the number of channels of the input image
* @param s the percentage of pixels saturated to minimum/maximum value
*
* @return data_norm simplest color balance of the input with s% of saturation
* @return local_out the result of the local option of the algorithm
* @return global_out the result of the global option of the algoritm
*/
double *poisson_gray(double *data_norm, double *local_out, double *global_out,
double *data, double T, double a, double alpha, size_t nx, size_t ny, int channel, float s)
{
int dim;
double *data_gray,*data_gray1;
dim=(int)nx*(int)ny;
data_gray = (double*) malloc(dim* sizeof(double));
data_gray1 = (double*) malloc(dim * sizeof(double));
if(channel == 1){
simplest_color_balance(data_norm, data, dim, s);
poisson_local(local_out, data_norm, T, a, nx,ny);
simplest_color_balance(local_out, local_out, dim,s);
poisson_global(global_out, data_norm, alpha, nx,ny);
simplest_color_balance(global_out, global_out, dim,s);
}
else{
gray_intensity(data_gray, data, dim);
simplest_color_balance(data_gray1, data_gray, dim,s);
color(data_norm, data, data_gray, data_gray1, dim);
poisson_local(data_gray, data_gray1, T, a, nx,ny);
simplest_color_balance(data_gray, data_gray, dim,s);
color(local_out, data_norm, data_gray1, data_gray, dim);
poisson_global(data_gray, data_gray1, alpha, nx,ny);
simplest_color_balance(data_gray, data_gray, dim,s);
color(global_out, data_norm, data_gray1, data_gray, dim);
}
free(data_gray); free(data_gray1);
return data_norm;
return local_out;
return global_out;
}
/**
* @brief The algorithm applied to the color channels independently
*
* @param data input array
* @param T the threshold to compute the dark regions
* @param a the gradient amplification factor
* @param alpha Power function for the gradient
* @param nx x-dimension of the array
* @param ny y-dimension of the array
* @param channel the number of channels of the input image
* @param s the percentage of pixels saturated to minimum/maximum value
*
* @return data_norm simplest color balance of the input with s% of saturation
* @return local_out the result of the local option of the algorithm
* @return global_out the result of the global option of the algoritm
*/
double *poisson_rgb(double *data_norm, double *local_out, double *global_out,
double *data, double T, double a, double alpha, size_t nx, size_t ny, int channel, float s)
{
int dim, n;
dim=(int)nx*(int)ny;
for(n=0;n<channel;n++) simplest_color_balance(data_norm+n*dim, data+n*dim, dim, s);
for(n=0;n<channel;n++){
poisson_local(local_out+n*dim, data_norm+n*dim, T, a, nx,ny);
simplest_color_balance(local_out+n*dim, local_out+n*dim, dim, s);
}
for(n=0;n<channel;n++){
poisson_global(global_out+n*dim, data_norm+n*dim, alpha, nx,ny);
simplest_color_balance(global_out+n*dim, global_out+n*dim, dim, s);
}
return data_norm;
return local_out;
return global_out;
}