/* -*- 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 - 23-05-2005
 */

/**
 * @file contrastenh.cpp
 * 
 * work -- Contrast enhancement function
 * Inputs:	  
 * 	  Image 	  Input Image
 * 	  Wavelet	  kind of wavelet to use (Daubechies, Symmlet)
 * 	  Regularity	  (see MakeONFilter 'Par' parameter for each wavelet
 *                         decomposition)
 * 	  Scale 	  coarse level
 * 	  Gain  	  gain >0 (typically 50).
 * 
 * Description:
 * 	  Apply a contrast enhancement on image Filename, using a DWT, UDWT,
 * ISOUDWT or CURV transform.
 *
 * Le réhaussement de contraste ne traite que le cas des images réelles
 * en niveaux de gris à 2 dimensions.
 */

#undef MAIN
#include "wutils.cpp"
#include "dctutils.cpp"

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

namespace Dwt{
#include "dwt.cpp"
//#include "idwt.cpp"
}
namespace Udwt{
#include "udwt.cpp"
#include "iudwt.cpp"
}
namespace Isoudwt{
#include "isoudwt.cpp"
#include "isoiudwt.cpp"
}
namespace Dct{
#include "dct.cpp"
#include "idct.cpp"
#include "ldct.cpp"
#include "ildct.cpp"
}

namespace Dst{
#include "dst.cpp"
#include "idst.cpp"
#include "ldst.cpp"
#include "ildst.cpp"
}
namespace Dctiv{
#include "dctiv.cpp"
#include "ldctiv.cpp"
#include "ildctiv.cpp"
}
namespace Stat{
#include "imagefeatureextraction/pmedianvalue.cpp"
}
/*
namespace Curvelet{
#include "fftshift2.cpp"
#include "fct.cpp"
#include "fict.cpp"
}
*/
#define MAIN

/**
 * Estimate the noise std by the MAD robust estimator.
 */
Float mad(Img2dsf & ims) {
   Long x,y;
   Float median;
   median = Stat::PMedianValue(ims);
   for(y = 0; y < ims.Height() ;y++)
      for(x = 0; x < ims.Width() ;x++)
	 ims[y][x] = (Float)fabs(ims[y][x] - median);
	
   return Stat::PMedianValue(ims);
}

/**
 * Sigmoidal function.
 */
Float sigm(Float x) { 
   return (Float)(1/(1+exp(-x)));
}

/**
 * Enhancement function
 * Inputs:
 *	x	A vector of float 
 *	n	The length of the vecyor x
 *	Gain	Gain.
 *	Lambda 
 */
Errc Enh_Func(Float* x,int n, Float gain,Float lambda) {
   Float x_max = maxabs(x,n);
   Float b = lambda/x_max;
   Float a = 1/(sigm(gain*(1-b))-sigm(-gain*(1+b)));
   int i;
   for(i = 0; i < n; i++)
      x[i] = x_max*a*(sigm(gain*(x[i]/x_max-b))-sigm(-gain*(x[i]/x_max+b)));
   return SUCCESS;
}

/**
 * ContrastEnh 
 * ims   : The input image.
 * colin : The collection which contains the transforms.
 * gain  : The wanted gain.
 * imd   : The output image.
 */
Errc ContrastEnh( Img2dsf & ims, Collection & colin, Img2dsf::ValueType gain, Img2dsf & imd) {
   // We create the dictionary.
   Dictionary Dict(colin);
   Long n = ims.Width();
   Long m = ims.Height();
   Long band;
	
   //We check some conditions	
   if (Dict.GetNbTransforms()>1)
      std::cerr<<"Multiscale Contrast Enhancement is only implemented with one transform: DWT, UDWT or ISOUDWT."<<std::endl;
	
   std::cout<<"Multiscale Contrast Enhancement with dictionary containing"<<std::endl;
   Dict[0].Display();

   // To estimate the WGN standard deviation using the MAD.
   // The qmf is quite arbitrary, sufficiently regular to approach a good band-pass.
   Qmf qmf("daubechies", 4);
   Img2dsf wc(m,n), hh;
   Dwt::DWT(ims, qmf, wc, 1);
   Utility::PExtractSubImage(wc, hh, m>>1, n>>1,m>>1,n>>1);
   Float sigma = (Float)(mad(hh)/.6745);
   Float lambda = (Float)(sigma*sqrt(2*log((Double)(m*n))));
   wc.Delete();
   hh.Delete();
	
   //Each type of transform is treated separately for consistency with pandore image class conventions.
   if (!strcmp(Dict[0].GetNameofTransform(),"UDWT")) {
      Img3dsf wctemp(3*(Long)Dict[0].GetPar1()+1,m,n);
      Qmf q(Dict[0]);
      Udwt::UDWT(ims, q, wctemp, (Long)Dict[0].GetPar1());
      for(band = 1; band <= 3*(Long)Dict[0].GetPar1(); band++)
	 Enh_Func(wctemp.Vector()+band*m*n,m*n,gain,lambda);
      Udwt::IUDWT(wctemp, q, imd, (Long)Dict[0].GetPar1());
      wctemp.Delete();
   }
	
   return SUCCESS;
}

#ifdef MAIN
#define USAGE   "USAGE : %s gain [dict_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_Img2dsf &&  (float)atof(parv[0])>0){
      Collection* const colin = (Collection *) objs[0];
      Img2dsf* const ims   = (Img2dsf*)objs[1];
      objd[0] = new Img2dsf(ims->Size());
      Img2dsf* const imd = (Img2dsf*)objd[0];
      int I=nextpow2(ims->Height());
      int J=nextpow2(ims->Width());
      Img2dsf* imsbuf=new Img2dsf(1<<I, 1<<J);
      *imsbuf=0;
      Utility::PInsertSubImage(*imsbuf, *ims, *imsbuf, 0, 0);
      Img2dsf* imdbuf=new Img2dsf(imsbuf->Props());
      result = ContrastEnh(*imsbuf, *colin, (Float)atof(parv[0]), *imdbuf);
      Utility::PExtractSubImage(*imdbuf, *imd, 0, 0, imd->Height(), imd->Width());
      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
