/* -*- c-basic-offset: 3 -*-
 * @(#)idwt.cpp		PANDORE 	2003-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
 */

/**
 * Inverse Discrete Wavelet Transform 
 * This IDWT is only implemented with
 * 2D gray image.
 */ 
 
/*
 * L'IDWT ne traite que les images 2D de
 * niveaux de gris.
 */
 
#include "wutils.cpp"
/**
 * Expansion-Convolution of a signal by a filter
 * 
 * This function applies a step of reconstruction of 
 * the pyramidal algorithm.
 * It convolutes the input TOD by the low pass filter h (vice versa  high pass g) for
 * the approximation and the detail, with putting one zero one time out of two samples.
 * The coefficients of approximation are supposed to be stored in the low middle
 * of the input and the details in the high middle. The output
 * contains the approximation at the above scale .
 * The pocessing of edge effects is done by a periodization of the modif
 * of the convoluted signal
 */
void ExpandConv(Float* input, Float* output,Filter &Filt, Long length)
{
        Float ai, ai1;
        Long i, ii, j, jf, jr, k, n, n1, ni, nj, nh, nmod;

        n = length ;
        if (n < 4 || !input || !output) return;

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


/*    
 * IDWT
 * 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.
 *	imd   : The output image.
 *	scale : The scale of the transform.
 */

Errc IDWT(Img2dsf & ims, Qmf & q, Img2dsf & imd, Long scale){
	
	Long i, j,k;
	Long width = ims.Width();
	Long height= ims.Height();
	DwtFilter filter;
	
	if(scale >= nextpow2(MIN(ims.Width(),ims.Height()))){
		std::cerr<<"Incorrect scale : scale >= nexpow2(N)."<<std::endl;
		Exit(FAILURE);
	}
	
	Float *data = new Float[width+1]; 
	Float *work = new Float[width+1];
	
	// We load the QMF filter.
	filter.LoadRecFilter(q);
	// Copy the input image.
	imd = ims;

	// Reconstruction
	for(k = scale-1; k >= 0; k--){
	
		width  = ims.Width()  >> k;
		height = ims.Height() >> k;	
	
		// Vertical reconstruction 
		for(j = 0; j < width; j++){
			
			for( i = 0; i < height; i++)
				data[i+1] = (Float)imd[i][j];
			ExpandConv(data, work, filter, height);
			for( i = 0; i < height; i++)
				imd[i][j] = (Img2dsf::ValueType)work[i+1];
		}
		// Horizontal reconstruction 
		for(i = 0; i < height; i++){
			
			for( j = 0; j < width; j++)
				data[j+1] = (Float)imd[i][j];
			ExpandConv(data, work, filter, width);
			for( j = 0; j < width; j++)
				imd[i][j] = (Img2dsf::ValueType)work[j+1];
		}
	}
	
	delete[] work;
	delete[] data;
	return SUCCESS;
}


#ifdef MAIN
#define USAGE   "USAGE : %s  scale [qmf_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[1]->Type() == Po_Img2dsf && objs[0]->Type() == Po_Collection){
	  
	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 = IDWT(*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
