/* -*- c-basic-offset: 3 -*-
 * @(#)dwt.cpp		PANDORE		2005-02-02	
 *
 * 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:
 * Regis.Clouard@greyc.ismra.fr
 * http://www.greyc.ismra.fr/~regis
 */

/*
 * (C)SOLTYS Ludovic - 02-02-2003
 * (R)Glinel Christophe - 20-07-2005
 */

/**
 * Discrete Wavelet Transform (DWT)
 * This DWT is only implemented with
 * 2D gray images.
 */ 
 
/**
 * Transformée discrète par ondelettes.
 * La DWT ne traite que les images 2D de niveaux
 * de gris.
 */  

#include "wutils.cpp"
 /**
  * Convolution-decimation of a signal by a filter.
  *
  * This function applies a step of decomposition
  * of the pyramidal algorithm.
  * It convolutes the input signal by the low-pass filter
  * ~h (high pass ~g) for the approximation and the detail,
  * then sub-samples  by a factor of 2. The coefficients of the
  * approximation are saved in the lower middle of the output
  * and the details in the upper middle of the output.
  * The processing of edge effects  is done by a periodisation
  * of the motif of the convoluted signal.
  */
void ConvDecim(Float* input, Float* output,Filter &Filt, int length){
	
        int i, ii, jf, jr, k, n, n1, ni, nj, nh, nmod;

        n = length ;
        if (n < 4 || !output || !input) { return;}
		
        nmod=Filt.Size*n;
        n1=n-1;
        nh=n >> 1;

	memset(output,0,sizeof(Float)*(n+1));
	for (ii=1, i=1; i<=n; i+=2, ii++)
	{
		ni = i + nmod + Filt.Shift;
		nj = i + nmod + Filt.Shift;
		for (k=1;k<=Filt.Size;k++)
		{
			jf=(n1 & (ni+k));
	        	jr=(n1 & (nj+k));
			output[ii]    += Filt.H[k]*input[jf+1];
            		output[ii+nh] += Filt.G[k]*input[jr+1];
		}
	}
}


/*    
 * DWT
 * 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.
 *	q     : The Qmf  to use.
 *	imd   : The output image.
 *	scale : The scale of the transform.
 */
Errc DWT(Img2duc & ims, Qmf & q, Img2dsf & imd, Long scale){

	Long i, j, k;
	Long width  = imd.Width();
	Long height = imd.Height();
	DwtFilter filter;
	
	if(scale >= nextpow2(MIN(imd.Width(),imd.Height()))){
		std::cerr<<"Incorret scale : scale >= nexpow2(N)."<<std::endl;
		Exit(FAILURE);
	}
	
	Float *data = new Float[MAX(width,height)+1]; 
	Float *work = new Float[MAX(width,height)+1];
	
	// We load the filter.
	filter.LoadFilter(q);
	
	// We copy the input image.
	imd = ims;

	// Decomposition			
	for(k = 1; k <= scale; k++){
		// Horizontal decomposition 
		for(i = 0; i < height; i++){
			for( j = 0; j < width; j++)
				data[j+1] =(Float) imd[i][j];
			
			ConvDecim(data, work, filter, width);
			
			for( j = 0; j < width; j++)
				imd[i][j] = (Img2dsf::ValueType)work[j+1];
		}
		// Vertical decomposition
		for(j = 0; j < width; j++){
			for( i = 0; i < height; i++)
				data[i+1] = (Float)imd[i][j];
			
			ConvDecim(data, work, filter, height);
			
			for( i = 0; i < height; i++)
				imd[i][j] = (Img2dsf::ValueType)work[i+1];
		}
		height = height >> 1;
		width  = width  >> 1;
	}

	delete []work;
	delete []data;
	return SUCCESS;
}

/*    
 * DWT
 * 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.
 *	q     : The Qmf  to use.
 *	imd   : The output image.
 *	scale : The scale of the transform.
 */
Errc DWT(Img2dsl & ims, Qmf & q, Img2dsf & imd, Long scale){

	Long i, j, k;
	Long width  = imd.Width();
	Long height = imd.Height();
	DwtFilter filter;
	
	if(scale >= nextpow2(MIN(imd.Width(),imd.Height()))){
		std::cerr<<"Incorret scale : scale >= nexpow2(N)."<<std::endl;
		Exit(FAILURE);
	}
	
	Float *data = new Float[MAX(width,height)+1]; 
	Float *work = new Float[MAX(width,height)+1];
	
	// We load the filter.
	filter.LoadFilter(q);
	
	// We copy the input image.
	imd = ims;

	// Decomposition			
	for(k = 1; k <= scale; k++){
		// Horizontal decomposition 
		for(i = 0; i < height; i++){
			for( j = 0; j < width; j++)
				data[j+1] =(Float) imd[i][j];
			
			ConvDecim(data, work, filter, width);
			
			for( j = 0; j < width; j++)
				imd[i][j] = (Img2dsf::ValueType)work[j+1];
		}
		// Vertical decomposition
		for(j = 0; j < width; j++){
			for( i = 0; i < height; i++)
				data[i+1] = (Float)imd[i][j];
			
			ConvDecim(data, work, filter, height);
			
			for( i = 0; i < height; i++)
				imd[i][j] = (Img2dsf::ValueType)work[i+1];
		}
		height = height >> 1;
		width  = width  >> 1;
	}

	delete []work;
	delete []data;
	return SUCCESS;
}

/*    
 * DWT
 * 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.
 *	q     : The Qmf  to use.
 *	imd   : The output image.
 *	scale : The scale of the transform.
 */
Errc DWT(Img2dsf & ims, Qmf & q, Img2dsf & imd, Long scale){

	Long i, j, k;
	Long width  = imd.Width();
	Long height = imd.Height();
	DwtFilter filter;
	
	if(scale >= nextpow2(MIN(imd.Width(),imd.Height()))){
		std::cerr<<"Incorret scale : scale >= nexpow2(N)."<<std::endl;
		Exit(FAILURE);
	}
	
	Float *data = new Float[MAX(width,height)+1]; 
	Float *work = new Float[MAX(width,height)+1];
	
	// We load the filter.
	filter.LoadFilter(q);
	
	// We copy the input image.
	imd = ims;

	// Decomposition			
	for(k = 1; k <= scale; k++){
		// Horizontal decomposition 
		for(i = 0; i < height; i++){
			for( j = 0; j < width; j++)
				data[j+1] =(Float) imd[i][j];
			
			ConvDecim(data, work, filter, width);
			
			for( j = 0; j < width; j++)
				imd[i][j] = (Img2dsf::ValueType)work[j+1];
		}
		// Vertical decomposition
		for(j = 0; j < width; j++){
			for( i = 0; i < height; i++)
				data[i+1] = (Float)imd[i][j];
			
			ConvDecim(data, work, filter, height);
			
			for( i = 0; i < height; i++)
				imd[i][j] = (Img2dsf::ValueType)work[i+1];
		}
		height = height >> 1;
		width  = width  >> 1;
	}

	delete []work;
	delete []data;
	return SUCCESS;
}

#ifdef MAIN
#define USAGE   "USAGE : %s  scale [col_in|-] [im_in|-] [im_out|-]"
#define PARC    1
#define FINC    2
#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_Collection && objs[1]->Type()==Po_Img2duc){
	
	Collection * const colin = (Collection *)objs[0];
	Img2duc * const ims = (Img2duc*)objs[1];
	Check(*ims);
	objd[0] = new Img2dsf(ims->Size());
	Img2dsf * const imd = (Img2dsf*)objd[0];
	Qmf q(*colin);
	result = DWT(*ims, q, *imd, atol(parv[0]));
	goto end;
   }
   else
  if (objs[0]->Type()==Po_Collection && objs[1]->Type()==Po_Img2dsl){
	
	Collection * const colin = (Collection *)objs[0];
	Img2dsl * const ims = (Img2dsl*)objs[1];
	Check(*ims);
	objd[0] = new Img2dsf(ims->Size());
	Img2dsf * const imd = (Img2dsf*)objd[0];
	Qmf q(*colin);
	result = DWT(*ims, q, *imd, atol(parv[0]));
	goto end;
   }
   else
  if (objs[0]->Type()==Po_Collection && objs[1]->Type()==Po_Img2dsf){
	
	Collection * const colin = (Collection *)objs[0];
	Img2dsf * const ims = (Img2dsf*)objs[1];
	Check(*ims);
	objd[0] = new Img2dsf(ims->Size());
	Img2dsf * const imd = (Img2dsf*)objd[0];
	Qmf q(*colin);
	result = DWT(*ims, q, *imd, atol(parv[0]));
	goto end;
   }
   else
  {
     PrintErrorFormat(objin,FINC);
     result = FAILURE;
  }	

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