/*!
\file skinmodel.hpp

\author Juraj Blaho
\modified by Michal Hradis 17.11.2008

\brief Skin model.


*/

#ifndef SKINMODEL_INCLUDED
#define SKINMODEL_INCLUDED

#include <cxcore.h>

#include <iostream>

using namespace std;

/*!
\brief Represents the skin color model.

This class accumulates data about skin and background (non-skin) colors
*/
class SkinModel{
	public:
		static const int COLOR_DIMENSIONS=2;/*!< Number of color dimensions */
		
	private:
		int _hist_resolution[COLOR_DIMENSIONS];/*!< Histogram resolutions. */
		int _hist_bin_count;/*!< Total number of histogram bins */
	
		/*these are used for evaluation*/
		double **_p_diff;/*!< Redundant histogram used for evaluation. Represents p(skin)/p(bg) */
		
		/*these are trained values*/
		double _skin_mean[COLOR_DIMENSIONS];/*!< Means of the skin color. */
		double _skin_var[COLOR_DIMENSIONS];/*!< Variances of the skin color. */
		double _skin_prior;/*!< Skin prior probability. */
		
		double **_bg;/*!< Background probability histogram */
		
		/*these are used for accumulation of new data*/
		double _acc_skin_sum[COLOR_DIMENSIONS];
		double _acc_skin_sqrsum[COLOR_DIMENSIONS];
		double _acc_skin_count;
		
		double **_acc_bg;
		double _acc_bg_count;
		
		/*!
		\brief Transform color to the dimension values.
		
		\return Return false if the color couldnt be transformed
		*/
		static bool TransformColor(int r, int g, int b, double *output);
		
		/*static double NormalDistribution(double x, double mean, double var);*/
		
		static double NormalDistribution2D(double const *x, double const *mean, double const *var);
		
		/*!
		\brief Update difference of probabilities
		*/
		void ProbabilityReset();
		void ProbabilityAddHist(double **hist, double ratio);
		void ProbabilityAddGauss(double *mean, double *var, double ratio);
		void ProbabilityUpdateMinMax();
		
		void ProbabilityUpdateRetrained();
		void ProbabilityUpdateAdapted(double tau);
	
		/*!
		\brief Init all data. This is only used in constructor.
		*/
		void Init(int r_resolution, int g_resolution);
		
		/*!
		\brief Init all data from the saved file.
		*/
		void Init(istream &s);
		
		/*!
		\brief Init all data from the saved file.
		*/
		void Init(char const *filename);
	public:
		/*!
		\brief The constructor.
		
		\param r_resolution The resolution of all histograms in dimension R.
		\param g_resolution The resolution of all histograms in dimension G.
		*/
		SkinModel(int r_resolution=100, int g_resolution=100);
		
		/*!
		\brief Construct the skin model from the saved stream.
		*/
		SkinModel(istream &s);
		
		/*!
		\brief Construct the skin model from the saved stream.
		*/
		SkinModel(char const *filename);
		
		/*!
		\brief The destructor.
		*/
		~SkinModel();
	
		/*!
		\brief Set skin prior
		*/
		void SkinPrior(double value);
		
		/*!
		\brief Get skin prior
		*/
		void SkinPrior();
	
		/*!
		\brief Add skin color.
		*/
		void AddSkin(int r, int g, int b, double weight=1.0);
		
		/*!
		\brief Add background (non-skin) color.
		*/
		void AddBackground(int r, int g, int b, double weight=1.0);

		/*!
		\brief Reduce added training data to the given count.
		
		This function is used to parialy forget current accumulated data.
		
		\param final_count Says how many samples are remembered. 0.0 means all data are forgotten.
		*/
		void RememberOnly(double final_count=0.0);
		
		/*!
		\brief Reduce added training data by the given ratio.
		
		This function is used to parialy forget current accumulated data.
		
		\param ratio Says how much data is remembered. 0.0 means all data are forgotten. The value should be less than 1.0
		*/
		void RememberRatio(double ratio);
		
		/*!
		\brief Retrain the model with accumulated samples.
		
		Retrain the model accordingly to the added skin and non-skin colors.
		*/
		void Retrain();
		
		/*!
		\brief Adapt the previously trained model.
		
		Adapt the model accordingly to the added skin and non-skin colors.
		*/
		void Adapt(double tau=1000.0);
		
		/*!
		\brief Evaluate single color.
		
		\return Positive for skin, negative for background.
		*/
		double Eval(int r, int g, int b) const;
		
		/*!
		\brief Evaluate image color and store result to destination image.
		
		\param src Source BGR/BGRA image.
		\param dst Destination image. May be same as src.
		*/
		void Eval(IplImage const *src, IplImage *dst);
		
		/*!
		\brief Save the skin model.
		*/
		void Save(ostream &s) const;
		void Save(char const *filename) const;
};

#endif

