/* -*- 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 Christophe Glinel 07-2005
 * @author Yannick Remy - 04-01-2006
 * @author Nael Ouedraogo - 04-01-2006
 */

/**
 * @file mca_threshold.cpp
 *
 * This operator was written to allow us to threshold the result
 * of Fast Analysis Transform.Two kinds of threshold are available,
 * softthresh and hardthresh
 *
 * Cet opérateur permet de faire un seuillage sur le résultat de
 * l'opérateur Fast Analysis Transform. Deux sortes de seuillage
 * sont présents, un seuillage doux et un seuillage dur.
 */

#include "wutils.cpp"

#ifdef MAIN
#undef MAIN

namespace Utility{
#include "utility/pextractsubimage.cpp"
#include "utility/pinsertsubimage.cpp"
}

#define MAIN
#endif

typedef enum ThdType {Hard = 0,Soft};

/**
 * This function thresholds an image. 
 * Inputs :
 * 	ims : The input image to threshold.
 * 	imd : The output image.
 * 	lambda : The threshold.
 *	type   : The type of threshold.
 */
Errc Thresh(Img2dsf & ims,Img2dsf &imd, Img2dsf::ValueType lambda,ThdType type = Hard) {
   int register y,x;
   if (type == Soft) {
      for (y=0;y<ims.Height();y++)
	 for (x=0;x<ims.Width();x++)
	    if ( fabs(ims[y][x]) >= lambda ) {
	       if(ims[y][x] >= 0.0)
		  imd[y][x] = ims[y][x] - lambda;
	       else
		  imd[y][x] = ims[y][x] + lambda;
	    }
	    else
	       imd[y][x] = 0;
   } else if (type == Hard) {
	 for (y=0;y<ims.Height();y++)
	    for (x=0;x<ims.Width();x++)
	       imd[y][x] = fabs(ims[y][x]) >= lambda ? ims[y][x] : 0 ;
      }

   return SUCCESS;
}

/**
 * This function thresholds an image. 
 * Inputs :
 * 	ims : The input image to threshold.
 * 	imd : The output image.
 * 	lambda : The threshold.
 *	type   : The type of threshold.
 */
Errc Thresh(Img3dsf & ims,Img3dsf &imd, Img3dsf::ValueType lambda,ThdType type = Hard){
   int register z,y,x;
   if (type == Soft) {
      for (z=0;z<ims.Depth();z++)
	 for (y=0;y<ims.Height();y++)
	    for (x=0;x<ims.Width();x++)
	       if ( fabs(ims[z][y][x]) >= lambda ) {
		  if(ims[z][y][x] >= 0.0)
		     imd[z][y][x] = ims[z][y][x] - lambda;
		  else
		     imd[z][y][x] = ims[z][y][x] + lambda;
	       }
	       else
		  imd[z][y][x] = 0;
   } else if (type == Hard) {
      for (z=0;z<ims.Depth();z++)
	 for (y=0;y<ims.Height();y++)
	    for (x=0;x<ims.Width();x++)
	       imd[z][y][x] = fabs(ims[z][y][x]) >= lambda ? ims[z][y][x] : 0 ;
   }
   
   return SUCCESS;
}

/**
 * This function thresholds an image after having normalized it. The target Image is 
 * denormalized. 
 * Implementation : the plane i is threshold with lambda*coeff[i]
 * Inputs :
 * 	ims : The input image to threshold.
 * 	imd : The output image.
 * 	lambda : The threshold.
 *	type   : The type of threshold.
 *      coeff : Normalization Coefficients. The plane i is normalized by coeff[i].
 */
Errc Thresh(Img3dsf & ims,Img3dsf &imd, Img3dsf::ValueType lambda, Float * & coeff, ThdType type = Hard){
   int register z,y,x;

   if (type == Soft) {
      for (z=0;z<ims.Depth();z++) {
	 lambda = lambda*coeff[z];
	 for (y=0;y<ims.Height();y++)
	    for (x=0;x<ims.Width();x++)
	       if ( fabs(ims[z][y][x]) >= lambda ) {
		  if(ims[z][y][x] >= 0.0)
		     imd[z][y][x] = ims[z][y][x] - lambda;
		  else
		     imd[z][y][x] = ims[z][y][x] + lambda;
	       }
	       else
		  imd[z][y][x] = 0;
      }
   } else if (type == Hard) {
      for (z=0;z<ims.Depth();z++) {
	 for (y=0;y<ims.Height();y++)
	    for (x=0;x<ims.Width();x++) {
	       imd[z][y][x] = (fabs(ims[z][y][x]) >= (lambda * coeff[z])) ? ims[z][y][x] : 0 ;
	    }
      }
   }
   
   return SUCCESS;
}

/**
 * This function is for the applications.
 * Inputs :
 *	col : The input and output collection. 
 *	lambda : The threshold.
 *	type   : The type of threshold. 
 */
Errc Thresh(Collection & col,Dictionary &dict, Imx3dsf::ValueType lambda,ThdType type = Hard) {
   Errc result = FAILURE;
   Char * name = dict.GetNameofTransform();
   
   if (!col.Exists("Collection")) {
      Imx3dsf * ims = (Imx3dsf *)col.GETPOBJECT("Image",Imx3dsf);
      // col contains an image
      if (!strcmp(name,"DWT") || !strcmp(name,"DCT") || !strcmp(name,"DST") || !strcmp(name,"LDCT") || !strcmp(name,"LDST") || !strcmp(name,"LDST") || !strcmp(name,"LDCTIV") || !strcmp(name,"DCTIV")){
	 
	 if (!strcmp(name,"DWT")) {
	    // Do not threshold the low-frequency.
	    Img2dsf imtemp;
	    Img2dsf imtemp2(ims->Height(),ims->Width(),ims->Vector(0));
	    
	    Utility::PExtractSubImage(imtemp2,imtemp,0,0,ims->Height()>>(int)dict.GetPar1(),ims->Width()>>(int)dict.GetPar1());
	    result = Thresh(imtemp2,imtemp2,lambda,type);
	    Utility::PInsertSubImage(imtemp2,imtemp,imtemp2,0,0);
	    imtemp.Delete();
	    imtemp2.Delete();
	 } else {
	    Img2dsf imtemp2(ims->Height(),ims->Width(),ims->Vector(0));
	    result = Thresh(imtemp2,imtemp2,lambda,type);
	    imtemp2.Delete();
	 }
      } else if (!strcmp(name,"UDWT") || !strcmp(name,"ISOUDWT")){
	 // Do not threshold the low-frequency.
	 Img3dsf imtemp(ims->Depth()-1,ims->Height(),ims->Width(),ims->Vector(0) + ims->Height() * ims->Width());
	 result = Thresh(imtemp,imtemp,lambda,type);
	 imtemp.Delete();
      } else {
	 std::cerr<<"Threshold: "<< name << " specified in the dictionary is not implemented."<<std::endl;
      }
   } else {
      // col contains a Collection.
      if (!strcmp(name,"FCT")){
	 // Do not threshold the low-frequency.
	 Collection * collec = (Collection *)(col.GETPOBJECT("Collection",Collection));
	 Long nbscales = collec->GETVALUE("nbscales", Long);
	 Img1dsl* nbangles = (Img1dsl*)(collec->GETPOBJECT("nbangles",Img1dsl));
	 Collection** les_curvelets = collec->GETPARRAY("curvelets",Collection);
	 Img2dsf**  curvdata_tmp=NULL;
	 
	 for(int i=0; i<nbscales-1; i++) {
	    // The low-frequency is the last image of listtemp array
	    curvdata_tmp=les_curvelets[i]->GETPARRAY("curvangles",Img2dsf);
	    for(int j=0; j<(*nbangles)[i]; j++)
	       Thresh(*(curvdata_tmp[j]), *(curvdata_tmp[j]),lambda,type);
	 }
      } else {
	 std::cerr<<"Threshold: "<< name << "specified in the dictionary is not implemented."<<std::endl;
      }
   }
   return result;
}

/**
 * This function is for the applications.
 * Inputs :
 *	col : The input and output collection. 
 *	lambda : The threshold.
 *	type   : The type of threshold. 
 */
Errc NormThresh(Collection & col,Dictionary &dict, Imx3dsf::ValueType lambda,Float ** &E, ThdType type = Hard) {
   Errc result = FAILURE;
   Char * name = dict.GetNameofTransform();
   if(!strcmp(name,"FCT")) {
      // Do not threshold the low-frequency.
      Collection ** les_curvelets;
      Collection * collec = (Collection *)(col.GETPOBJECT("Collection",Collection));
      Long nbscales = collec->GETVALUE("nbscales", Long);
      Img1dsl* nbangles = (Img1dsl*)(collec->GETPOBJECT("nbangles",Img1dsl));
      les_curvelets = collec->GETPARRAY("curvelets",Collection);
      Img2dsf**  curvdata_tmp=NULL;
      
      for(int i=0; i<nbscales-1; i++) {
	 // The low-frequency is the last image of listtemp array
	 curvdata_tmp=les_curvelets[i]->GETPARRAY("curvangles",Img2dsf);
	 for(int j=0; j<(*nbangles)[i]; j++)
	    Thresh(*(curvdata_tmp[j]), *(curvdata_tmp[j]),lambda*E[i][j],type);
      }
   } else {
      result = Thresh(col,dict, lambda, type);
   }
   
   return result;
}

/**
 *  This operator is for Main
 * Inputs :
 *	cols    : The input collection to threshold.
 *	cold    : The output collection.
 *	dict   : The dictionary.
 *	lambda : The threshold.
 *	type   : The type of threshold. 
 */
Errc Thresh(Collection & cols,Collection &cold,Dictionary &dict, Img2dsf::ValueType lambda,ThdType type = Hard) {	
   Errc result = FAILURE;
   if (!cols.Exists("Collection")) {
      // cols contains a Image 
      if(cols.GETPOBJECT("Image",Imx3dsf)->Type() == Po_Img2dsf) {
	 cold.SETPOBJECT("Image",Img2dsf,(Img2dsf *)cols.GETPOBJECT("Image",Img2dsf));
      } else if (cols.GETPOBJECT("Image",Imx3dsf)->Type() == Po_Img3dsf) {
	 cold.SETPOBJECT("Image",Img2dsf,(Img3dsf *)cols.GETPOBJECT("Image",Img3dsf));
      }
      result  = Thresh(cold,dict,lambda,type);
   } else {
      // cols contains a collection
      cold.SETPOBJECT("Collection",Collection,(Collection *)cols.GETPOBJECT("Collection",Collection));
      result = Thresh(cold,dict,lambda,type);
   }
   return result;   
}

#ifdef MAIN
#define USAGE	"USAGE : %s  type threshold num [dict_in|-] [col_in|-] [col_out|-]"
#define PARC	3
#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_Collection){
	
      Collection * const colin = (Collection*)objs[0];
      Collection * const cols = (Collection*)objs[1];
      objd[0] = new Collection();
      Collection* const cold = (Collection*)objd[0];
      Dictionary dict(*colin);
	
      ThdType type;
      
      if(atoi(parv[0]) == 1)
	 type = Soft;
      else
	 type = Hard;
      result = Thresh(*cols,*cold,dict[atoi(parv[2])],(Img2dsf::ValueType)atof(parv[1]),type);
      goto end;
   }
   {
      PrintErrorFormat(objin,FINC);
      result = FAILURE;
   }	

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