/* -*- mode: c++; c-basic-offset: 3 -*-
 *
 * PANDORE (PANTHEON Project)
 *
 * GREYC IMAGE
 * 6 Boulevard Maréchal Juin
 * F-14050 Caen Cedex France
 *
 * This file is free software. You can use it, distribute it
 * and/or modify it. However, the entire risk to the quality
 * and performance of this program is with you.
 *
 * Please email any bugs, comments, and/or additions to this file to:
 * pandore@greyc.ensicaen.fr
 * http://www.greyc.ensicaen.fr/EquipeImage/Pandore/
 */

/**
 * @author Jalal Fadili - 02-02-2005
 * @author Christophe Glinel - 20-07-2005
 */

/**
 * @file isoudwt.cpp
 *
  * 2D Isotropic Undecimated Discrete Wavelet Transform (isoudwt) 
 * associated to the cubic spline scaling function.
 *
 * Transformée discrète par ondelettes non décimées isotropiques.
 * utilisant des cubiques splines.
 * La ISOUDWT implémentée ici ne traite que le cas des images
 * 2D de nveaux de gris.
 */

#include <pandore.h>
using namespace pandore;
#include "wutils.cpp"

// This is an artificial way to normalize the frame representation elements but only up to scale 20.
Float NORMC[21] = {2.734375e-01F,1.085883e-01F,5.145657e-02F,2.600124e-02F,1.349663e-02F,
	7.089505e-03F,3.743773e-03F,1.981668e-03F,1.050050e-03F,5.566641e-04F,
	2.951664e-04F,1.565240e-04F,8.300666e-05F,4.402028e-05F,2.334513e-05F,
	1.238059e-05F,6.565790e-06F,3.482034e-06F,1.846627e-06F,9.793216e-07F,5.193637e-07F};
Float NORMD[21] = {8.907963e-01F,1.996878e-01F,8.559235e-02F,4.272887e-02F,2.229211e-02F,
	1.176224e-02F,6.226405e-03F,3.299607e-03F,1.749331e-03F,9.275954e-04F,
	4.919020e-04F,2.608634e-04F,1.383421e-04F,7.336658e-05F,3.890842e-05F,
	2.063428e-05F,1.094298e-05F,5.803388e-06F,3.077711e-06F,1.632203e-06F,8.656061e-07F};

/**
* Convolution of an input ims by filter h with zeros insertion.
*  (one iteration of the low-pass A trous algorithm at scale j).
* Periodic boundary condition with the modulo operator.
* Inputs
*	ims : The input image.
*	lh  : The length of the filter
*	j   : The scale.
*/
template<typename U>
Float convhj(Img2d<U>& ims, int x, int y, Float h[][5], int lhr, int lhc, int j) {
  int ih, jh, icur, jcur, lhr2, lhc2, j2;
  Float sum=0;

  lhr2 = lhr>>1;
  lhc2 = lhc>>1;
  j2  = 1<<j;
  
  for (ih=-lhr2, icur=x-j2*lhr2; ih<=lhr2; ih++, icur+=j2)
  for (jh=-lhc2, jcur=y-j2*lhc2; jh<=lhc2; jh++, jcur+=j2)
    	sum += ims[mod(icur,ims.Height())][mod(jcur,ims.Width())]*h[ih+lhr2][jh+lhc2];
  
  return sum;
}

/*      
 * Direct Isotropic Undecimated Wavelet Transform
 * Integer 2D gray image. (Img2duc)
 * Images MUST have their dimensions all powers of 2 -> reshape.
 * The dimensions of the input and output must be the same.
 * Inputs :
 *	ims : The input image.
 *	imd : The output image.
 *	scale : The scale of the transform.
 */
Errc ISOUDWT(Img2duc & ims, Img3dsf & imd, int scale)
{
	int n = imd.Width();
	int m = imd.Height();
	int i, j, d;
	Float h[5][5] = {{1./256,     4./256,    6./256,	 4./256,    1./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {6./256,    24./256,   36./256,	24./256,    6./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {1./256,     4./256,    6./256,	 4./256,    1./256}};

	if(imd.Depth() != (scale+1)){
		std::cerr<<"ERROR: Output image do not have the proper depth."<<std::endl;
		Exit(FAILURE);
	}
	if(scale>=nextpow2(MIN(m,n))){
		std::cerr<<"Incorrect scale : scale >= nexpow2(N)"<<std::endl;
		Exit(FAILURE);
	}
	
	Img2d<Img3dsf::ValueType> temp(ims.Height(),ims.Width());
	temp = ims;

	// Decomposition
	for(d = 1; d <= scale; d++){
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
		imd[0][i][j] =(Img3dsf::ValueType) convhj(temp,i,j,h,5,5,d-1);
	  
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++){
		imd[d][i][j] = (temp[i][j]-imd[0][i][j])/NORMD[d-1];
		temp[i][j]   = imd[0][i][j];
	  }
	}
	
	for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
	    	imd[0][i][j] /= NORMC[scale-1];
	
	temp.Delete();
	return SUCCESS;
}

/*      
 * Direct Isotropic Undecimated Wavelet Transform
 * Integer 2D gray image. (Img2dsl)
 * Images MUST have their dimensions all powers of 2 -> reshape.
 * The dimensions of the input and output must be the same.
 * Inputs :
 *	ims : The input image.
 *	imd : The output image.
 *	scale : The scale of the transform.
 */
Errc ISOUDWT(Img2dsl & ims, Img3dsf & imd, int scale)
{
	int n = imd.Width();
	int m = imd.Height();
	int i, j, d;
	Float h[5][5] = {{1./256,     4./256,    6./256,	 4./256,    1./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {6./256,    24./256,   36./256,	24./256,    6./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {1./256,     4./256,    6./256,	 4./256,    1./256}};

	if(imd.Depth() != (scale+1)){
		std::cerr<<"ERROR: Output image do not have the proper depth."<<std::endl;
		Exit(FAILURE);
	}
	if(scale>=nextpow2(MIN(m,n))){
		std::cerr<<"Incorrect scale : scale >= nexpow2(N)"<<std::endl;
		Exit(FAILURE);
	}
	
	Img2d<Img3dsf::ValueType> temp(ims.Height(),ims.Width());
	temp = ims;

	// Decomposition
	for(d = 1; d <= scale; d++){
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
		imd[0][i][j] =(Img3dsf::ValueType) convhj(temp,i,j,h,5,5,d-1);
	  
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++){
		imd[d][i][j] = (temp[i][j]-imd[0][i][j])/NORMD[d-1];
		temp[i][j]   = imd[0][i][j];
	  }
	}
	
	for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
	    	imd[0][i][j] /= NORMC[scale-1];
	
	temp.Delete();
	return SUCCESS;
}

/*      
 * Direct Isotropic Undecimated Wavelet Transform
 * Integer 2D gray image. (Img2dsf)
 * Images MUST have their dimensions all powers of 2 -> reshape.
 * The dimensions of the input and output must be the same.
 * Inputs :
 *	ims : The input image.
 *	imd : The output image.
 *	scale : The scale of the transform.
 */
Errc ISOUDWT(Img2dsf & ims, Img3dsf & imd, int scale)
{
	int n = imd.Width();
	int m = imd.Height();
	int i, j, d;
	Float h[5][5] = {{1./256,     4./256,    6./256,	 4./256,    1./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {6./256,    24./256,   36./256,	24./256,    6./256},
     			 {4./256,    16./256,   24./256,	16./256,    4./256},
     			 {1./256,     4./256,    6./256,	 4./256,    1./256}};

	if(imd.Depth() != (scale+1)){
		std::cerr<<"ERROR: Output image do not have the proper depth."<<std::endl;
		Exit(FAILURE);
	}
	if(scale>=nextpow2(MIN(m,n))){
		std::cerr<<"Incorrect scale : scale >= nexpow2(N)"<<std::endl;
		Exit(FAILURE);
	}
	
	Img2d<Img3dsf::ValueType> temp(ims.Height(),ims.Width());
	temp = ims;

	// Decomposition
	for(d = 1; d <= scale; d++){
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
		imd[0][i][j] =(Img3dsf::ValueType) convhj(temp,i,j,h,5,5,d-1);
	  
	  for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++){
		imd[d][i][j] = (temp[i][j]-imd[0][i][j])/NORMD[d-1];
		temp[i][j]   = imd[0][i][j];
	  }
	}
	
	for(i = 0; i < m; i++)
	    for(j = 0; j < n; j++)
	    	imd[0][i][j] /= NORMC[scale-1];
	
	temp.Delete();
	return SUCCESS;
}

#ifdef MAIN
#define USAGE   "USAGE : %s  scale [im_in|-] [im_out|-]"
#define PARC    1
#define FINC    1
#define FOUTC   1
#define MASK    0
int main( int argc, char *argv[] ) {
   Errc result;              // The result code of the execution.
   Pobject* mask;            // The region map.
   Pobject* objin[FINC+1];   // The input objects;
   Pobject* objs[FINC+1];    // The source objects masked.
   Pobject* objout[FOUTC+1]; // The output object.
   Pobject* objd[FOUTC+1];   // The result object of the execution.
   char* parv[PARC+1];       // The input parameters.

   ReadArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,parv,USAGE,MASK);
     if (objs[0]->Type()==Po_Img2duc){

      Img2duc* const ims = (Img2duc*)objs[0];
      Check(*ims);
      objd[0] = new Img3dsf(atoi(parv[0])+1, ims->Height(), ims->Width());
      Img3dsf* const imd = (Img3dsf*)objd[0];
      result = ISOUDWT(*ims, *imd, atoi(parv[0]));
      goto end;
   
   }
   else
     if (objs[0]->Type()==Po_Img2dsl){

      Img2dsl* const ims = (Img2dsl*)objs[0];
      Check(*ims);
      objd[0] = new Img3dsf(atoi(parv[0])+1, ims->Height(), ims->Width());
      Img3dsf* const imd = (Img3dsf*)objd[0];
      result = ISOUDWT(*ims, *imd, atoi(parv[0]));
      goto end;
   
   }
   else
     if (objs[0]->Type()==Po_Img2dsf){

      Img2dsf* const ims = (Img2dsf*)objs[0];
      Check(*ims);
      objd[0] = new Img3dsf(atoi(parv[0])+1, ims->Height(), ims->Width());
      Img3dsf* const imd = (Img3dsf*)objd[0];
      result = ISOUDWT(*ims, *imd, atoi(parv[0]));
      goto end;
   
   }
   else
  {
     PrintErrorFormat(objin,FINC);
     result = FAILURE;
  }	

end:
  if (result)
	WriteArgs(argc,argv,PARC,FINC,FOUTC,&mask,objin,objs,objout,objd,MASK);
  Exit(result);
  return 0;
}
#endif
