/*
 * class ImageDenoiser
 *
 * $Date$
 * $Revision$
 *
 */

#ifndef IMAGE_DENOISER_HH__
#define IMAGE_DENOISER_HH__

#include "Wave/PyramidTransform.hh"
#include "WTools/PyramidTree.hh"

/** 
 * @addtogroup WTools 
 * @{ */

/** Include the HL area in the denoising process */
#define DENOISE_HL 0x01
/** Include the LH area in the denoising process */
#define DENOISE_LH 0x02
/** Include the HH area in the denoising process */
#define DENOISE_HH 0x04

#define SIGNIFICANT_COEFF   0
#define SIGNIFICANT_REGION  1
#define SIGNIFICANT_CHANNEL 2

#define REPLACE_SIMPLE  0
#define REPLACE_CHANNEL 1

/**
 * A simple DWT-based image denoiser. It uses the same tree structure as in
 * Shapiro's Zerotree compression algorithm. Denoising means reducing the
 * power of significant coefficients in finer subbands that have insignificant
 * parents in the coarser subbands.
 */
class ImageDenoiser 
{
public:
  /** Constructor, initializes internal structures, sets up preferences.
   * @param img a reference to the image to operate on.
   * @param areas the areas to operate on. Use a binary OR to combine more
   * than one area (see macros above).
   * @param fromSub the subband to start building the trees
   * @param toSub the target subband where the tree terminates
   * @param alpha the scaling factor to the standard deviation for 
   * determining the significance threshold
   * @param filter the filterset for decomposition 
   * @param isSingleSignificant the kind of function used to determine 
   * significance, 0: single values, 1: 3x3 regions, 2: channels in 3x3 
   * environments
   * @param replacementFunction the kind of function used to replace
   * coefficient we consider as noise, 0: +-0.9*threshold, 1: more complicated
   * formula depending on channel (see below)
   * @exception invalid_argument if the areas argument is zero or not at
   * least two subbands are selected or an invalid significance function
   * is selected
   */
  ImageDenoiser (Image &img, unsigned areas, int fromSub, int toSub = 1, 
                 double alpha = 1.0, FilterSet &filter = Daub4, 
                 int isSingleSignificant = 0, int replacementFunction = 0);
  
  /** Destructor, clean up. */
  virtual ~ImageDenoiser (void);

  /** Apply the denoising algorithm to the image. 
   * @exception invalid_argument if some error occurs during decomposition
   * or reconstruction of the image */
  virtual void denoise (void);
  
protected:

  /** Calculate the threshold for a given area/subband.
   * @param channel the area
   * @param subband the subband
   * @return the threshold */
  coeff threshold (int subband, area channel);

  /** Process one tree or subtree by applying the thresholds
   * @param tree the tree structure to process
   * @param thresholds an array holding depth thresholds
   * @param channel which area inside a subband
   * @param level the current position's decomposition level */
  virtual void processFrom (PyramidTree &tree, coeff *thresholds, 
                            area channel, int level);

  /** Return true if the coefficient's value is significant with respect
   * to the threshold.
   * @param c the coefficient
   * @param channel which area inside a subband
   * @param threshold the threshold
   * @param stop set to true if we found an edge and want the processing
   * of this tree to stop (always false here)
   * @return true if the coefficient is significant */
  virtual bool isCoeffSingleSignificant (CoeffInformation &c, area channel, 
                                         coeff threshold, bool &stop);
  /** Return true if the coefficient is significant with respect to the 
   * threshold but the surrounding square region is not.
   * @param c the coefficient
   * @param channel which area inside a subband
   * @param threshold the threshold
   * @param stop set to true if we found an edge and want the processing
   * of this tree to stop
   * @return true if the coefficient's region is significant */
  virtual bool isSingleSignificantInRegion (CoeffInformation &c, area channel, 
                                            coeff threshold, bool &stop);
  /** Return true if the coefficient is significant with respect to the 
   * threshold but the surrounding channel-dependent region is not.
   * @param c the coefficient
   * @param channel which area inside a subband
   * @param threshold the threshold
   * @param stop set to true if we found an edge and want the processing
   * of this tree to stop
   * @return true if the coefficient's region is significant */
  virtual bool isSingleSignificantInChannel (CoeffInformation &c, area channel,
                                             coeff threshold, bool &stop);
  /** Replace a coefficient that we think is noise. This method simply
   * replaces it by the threshold multiplied by the original value's
   * sign and 0.99.
   * @param y the row in the image
   * @param x the column in the image
   * @param channel which area inside a subband
   * @param threshold the threshold */
  virtual void replaceSimple (int y, int x, area channel, coeff threshold);
  /** Replace a coefficient that we think is noise. This method uses the
   * formula found in "Image Denoising Using a Local Gaussian Scale Mixture 
   * Model in the Wavelet Domain" by Strela/Portilla/Simoncelli: I_xy =
   * sqrt (sum_n(I_xnyn^2)/n), however we only count vertical, horizontal
   * or diagonal values depending on which channel a value is in.
   * @param y the row in the image
   * @param x the column in the image
   * @param channel which area inside a subband
   * @param threshold the threshold */
  virtual void replaceChannel (int y, int x, area channel, coeff threshold);

  /** A reference to the image to process. */
  Image *m_image;
  /** The wavelet transform to be used. */
  PyramidTransform *m_transform;
  /** The filterset used for decomposition */
  FilterSet *m_filter;
  /** The subband to start building the trees */
  int m_from;
  /** The target subband where the tree terminates */
  int m_to;
  /** The areas to operate on. Use a binary OR to combine more
   * than one area (see macros above). */
  unsigned m_areas;
  /** The scaling factor to the standard deviation for 
   * determining the significance threshold */
  double m_alpha;
  /** The significance function used. */
  int m_significanceFunction;
  /** The replacement function used. */
  int m_replacementFunction;
  
}; /* class ImageDenoiser */

/** @} */

#endif /* IMAGE_DENOISER_HH__ */

