/* -*- 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 iudwt.cpp
 *
 * 2D Inverse Undecimated Discrete Wavelet Transform (IUDWT).
 * The UDWT is implemented only with the 
 * 2D gray level images.
 *
 * 2D Inverse Undecimated Discrete Wavelet Transform (IUDWT).
 * La IUDWT implémentée ici ne traite que le cas des images
 * 2D de niveaux de gris.
 */

#include "wutils.cpp"

/**
 * Reconstruction of the approximation x_out by a convolution
 * of vectors of detail and approximation x_inl and x_inh by
 * a pair of reconstruction qmf filters hr and gr.
 * Inputs :
 *	lh    : The length of the filter h.
 *	x_inl : Approximation.
 *	x_inh : Detail.
 */
void bpconv(Double *x_out, int lx, Float *hr, Float *gr, int lh,
	    Double *x_inl, Double *x_inh) {
   int i, j;
   Double x0;
 
   for (i=lh-2; i > -1; i--){
      x_inl[i] = x_inl[lx+i];
      x_inh[i] = x_inh[lx+i];
   }
   for (i=0; i<lx; i++){
      x0 = 0;
      for (j=0; j<lh; j++)
	 x0 = x0 + x_inl[j+i]*hr[lh-1-j] +
	    x_inh[j+i]*gr[lh-1-j];
      x_out[i] = x0;
   }
}


/**
 * 2D Inverse Undecimated Wavelet Transform
 * Inputs
 *	ims    : The input image.
 *	lh     : The length of the filter h.
 *	x_outl : Approximation.
 *	x_outh : Detail.
 */
template<typename U, typename V>
void mir2dwt(Img2d<U> &imd, int m, int n, Filter &filter, int L,
	     Img3d<V> &ims){
	
   Double *ydummyll, *ydummylh, *ydummyhl;
   Double *ydummyhh, *xdummyl , *xdummyh, *xh;
   Long i;
   int actual_L, actual_m, actual_n, c_o_a, ir, n_c, n_cb, lhm1;
   int ic, n_r, n_rb, c_o_a_p2n, sample_f;
   xh = new Double[m*n];
   xdummyl  = new Double[MAX(m,n)];
   xdummyh  = new Double[MAX(m,n)];
   ydummyll = new Double[MAX(m,n)+filter.Size-1];
   ydummylh = new Double[MAX(m,n)+filter.Size-1];
   ydummyhl = new Double[MAX(m,n)+filter.Size-1];
   ydummyhh = new Double[MAX(m,n)+filter.Size-1];
	
   lhm1 = filter.Size - 1;
	
   /* 2^L */
   sample_f = 1;
   for (i=1; i<L; i++)
      sample_f = sample_f*2;
	
   actual_m = m/sample_f;
   actual_n = n/sample_f;
	
   /* restore the LL component in the output image. */
   copy(imd.Vector(),ims.Vector(),m*n);
	
   /* main loop */
   for (actual_L=L; actual_L >= 1; actual_L--){
	
      /* actual (level dependent) column offset */
      if (m==1)
	 c_o_a = n*(actual_L-1);
      else
	 c_o_a = 3*n*(actual_L-1);
      c_o_a_p2n = c_o_a + 2*n;
	
      /* go by columns of 2D images*/
      n_rb = m/actual_m;                 /* # of row blocks per column */
      for (ic=0; ic<n; ic++){            /* loop over column */
	 for (n_r=0; n_r<n_rb; n_r++){    /* loop within one column */
	    /* store in dummy variables */
	    ir = -sample_f + n_r;
	    for (i=0; i<actual_m; i++){    
	       ir = ir + sample_f;
	       ydummyll[i+lhm1] = imd[ir][ic];  
	       ydummylh[i+lhm1] = ims[3*(actual_L-1)+1][ir][ic] ; 
	       ydummyhl[i+lhm1] = ims[3*(actual_L-1)+2][ir][ic];
	       ydummyhh[i+lhm1] = ims[3*(actual_L-1)+3][ir][ic]; 
	    }
	    /* perform filtering and adding: first LL/LH, then HL/HH */
	    bpconv(xdummyl, actual_m, filter.Hr, filter.Gr, filter.Size, ydummyll, ydummylh); 
	    bpconv(xdummyh, actual_m, filter.Hr, filter.Gr, filter.Size, ydummyhl, ydummyhh); 
	    /* store dummy variables in matrices */
	    ir = -sample_f + n_r;
	    for (i=0; i<actual_m; i++){    
	       ir = ir + sample_f;
	       imd[ir][ic] = (Float)xdummyl[i];  
	       mat(xh, ir, ic) = xdummyh[i];  
	    }
	 }
      }
	
      /* go by rows */
      n_cb = n/actual_n;                 /* # of column blocks per row */
      for (ir=0; ir<m; ir++){            /* loop over rows */
	 for (n_c=0; n_c<n_cb; n_c++){    /* loop within one row */      
	    /* store in dummy variable */
	    ic = -sample_f + n_c;
	    for  (i=0; i<actual_n; i++){    
	       ic = ic + sample_f;
	       ydummyll[i+lhm1] = imd[ir][ic];  
	       ydummyhh[i+lhm1] = mat(xh, ir, ic);  
	    } 
		
	    /* perform filtering lowpass/highpass */
	    bpconv(xdummyl, actual_n, filter.Hr, filter.Gr, filter.Size, ydummyll, ydummyhh); 
	    /* restore dummy variables in matrices */
	    ic = -sample_f + n_c;
	    for (i=0; i<actual_n; i++){    
	       ic = ic + sample_f;
	       imd[ir][ic] = (Float)xdummyl[i];  
	    }
	 }
      }
      sample_f = sample_f/2;
      actual_m = actual_m*2;
      actual_n = actual_n*2;
   }
	
   delete []xh;
   delete []xdummyl;
   delete []xdummyh;
   delete []ydummyll;
   delete []ydummylh;
   delete []ydummyhl;
   delete []ydummyhh;
}


/*     
 * Undecimated Wavelet Transform
 * Integer 2D gray image. (Img3dsf)
 * 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.
 * 	scale : The scale of the transform.
 */
Errc IUDWT(Img3dsf & ims, Qmf & q, Img2dsf & imd, Long scale){

   Long n  = ims.Width();
   Long m  = ims.Height();
   UdwtFilter filter;
	
   if(ims.Depth() != (3*scale+1)){
      std::cerr<<"ERROR: Output image do not have the proper depth\n";
      Exit(FAILURE);
   }
   if(scale >= nextpow2(MIN(m,n))){
      std::cerr<<"Incorrect scale : scale >= nexpow2(N)"<<std::endl;
      Exit(FAILURE);
   }

   filter.LoadRecFilter(q);
   // Reconstruction
   mir2dwt(imd, m, n, filter, scale, ims);
   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_Img3dsf ){
	
   Collection * const colin = (Collection *)objs[0];
   Img3dsf* const ims   = (Img3dsf *)objs[1];	
   Check(*ims);
   objd[0] = new Img2dsf(ims->Height(),ims->Width());
   Img2dsf* const imd = (Img2dsf*)objd[0];
   Qmf q(*colin);
   result = IUDWT(*ims, q, *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
