/*
 * bow_generator.hpp
 *
 *  Created on: 12.10.2011
 *      Author: isvoboda
 */

#ifndef BOW_GENERATOR_HPP_
#define BOW_GENERATOR_HPP_

#include <omp.h>
#include "read_dataset.hpp"
#include "opencv2/opencv.hpp"

namespace ppd
{
	/**
	 * Extractor works out detecting keypoints in the image, or in tho ROI of the image and extracts its descriptors.
	 */
	class Extractor
	{
	public:
		/**
		 * NonParametric constructor.
		 * The default feature detector and descriptor extractor is build upon the SURF.
		 */
		Extractor();

		/**
		 * Parametric constructor of class Extractor
		 * @param featureDetector Initialized feature detector from OpenCv
		 * @param descriptorExtractor Initialized descriptor extractor from OpenCv
		 */
		Extractor(const cv::Ptr<cv::FeatureDetector> featureDetector, const cv::Ptr<cv::DescriptorExtractor> descriptorExtractor);

		/**
		 * Setter of the Extractor's components
		 * @param featureDetector Set the feature detector
		 * @param descriptorExtractor Set the descriptor extractor.
		 */
		void set(cv::Ptr<cv::FeatureDetector> featureDetector, cv::Ptr<cv::DescriptorExtractor> descriptorExtractor);

		/**
		 * Returns the descriptor extractor component
		 * @return cv::Ptr<DescriptorExtractor>
		 */
		cv::Ptr<cv::DescriptorExtractor> getDescriptorExtractor(void);

		/**
		 * Return the featureDetector
		 * @return cv::Ptr<cv::FeatureDetector>
		 */
		cv::Ptr<cv::FeatureDetector> getFeatureDetector(void);

		/**
		 * Extracts Descriptors from the whole image, or ROI of image, if mask is defined
		 * @param image Image to extract descriptors from.
		 * @param descriptors Matrix of descriptors (a row is a descriptor).
		 * @param mask	Mask to set the ROI of image.
		 */
		virtual void extract(const cv::Mat& image, cv::Mat& descriptors, const cv::Rect_<int>* mask = NULL) const;

		/**
		 * Detect the keypoints in the image.
		 * @param image Source image to detectk the keypoints in.
		 * @param keypoints Detected keypoints - vector of detected keypoints.
		 * @param mask Mask to set the ROI of image.
		 */
		void detect(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, const cv::Rect_<int>* mask = NULL) const;

		/**
		 * Extracts the descriptors from defined keypoints in the source image
		 * @param image Source image to extract descriptors from.
		 * @param keypoints Defined keypoints to extract the descriptor upon.
		 * @param descriptors Returned descriptors - matrix of dexriptors, where one row is one descriptor.
		 */
		void compute(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors) const;

		/**
		 * Destructor, there is no dynamic allocation in the class.
		 */
		virtual ~Extractor();

	protected:
		cv::Ptr<cv::FeatureDetector> featureDetector;
		cv::Ptr<cv::DescriptorExtractor> descriptorExtractor;

	private:
		void get_masked(const std::vector<cv::KeyPoint>& keypoints, const cv::Rect_<int>* mask, std::vector<cv::KeyPoint>& maskedKeypoints) const;

	};

	/**
	 * Class for computing the Bag Of Words features.
	 */
	class BOW
	{
	public:

		/**
		 * NonParametric constructor, the descriptor matcher has to be set via set_DescriptorMatcher method.
		 */
		BOW();

		/**
		 * Parametric constructor
		 * @param dmatcher dmatcher used to find the most similar word
		 */
		BOW(cv::Ptr<cv::DescriptorMatcher> dmatcher);

	public:

		/**
		 * Computes the Bag Of Word feature.
		 * @param descriptors - computed descriptors
		 * @param dmatcher - Matcher to do matching the descriptors to the words
		 * @param feature - computed BoW descriptor
		 * @param pointIdxOfClusters - Array where on descriptor's index is the word's index pointIdxsOfClusters[descriptor_index] = word_index
		 */
		void extract(const cv::Mat& descriptors, cv::Ptr<cv::DescriptorMatcher> dmatcher, cv::Mat& feature, std::vector<int>* pointIdxOfClusters = NULL)const;

		/**
		 * Computes the Bag Of Word feature. The DMatcher has to be set, otherwise the method doesn't do anything.
		 * @param descriptors - computed descriptors
		 * @param feature - computed BoW descriptor
		 * @param pointIdxOfClusters - Array where on descriptor's index is the word's index pointIdxsOfClusters[descriptor_index] = word_index
		 */
		void extract(const cv::Mat& descriptors, cv::Mat& feature, std::vector<int>* pointIdxOfClusters = NULL)const;

	public:

		/**
		 * Sets the matcher object.
		 * @param dmatcher
		 */
		void set_DescriptorMatcher(cv::Ptr<cv::DescriptorMatcher> dmatcher);

		/**
		 * Returns the size of VOC used for matching
		 * @return size of VOC
		 */
		int get_voc_size(void) const;

	private:
		cv::Ptr<cv::DescriptorMatcher> dmatcher;
	};

	/**
	 * Proceed extracting of descriptor or Bag Of Word from the source image or the whole dataset.
	 */
	class ExtractorManager
	{
	public:

		/**
		 * NonParametric constructor, sets the default Image size to 640 if the width of the image is higher or the height to 480 if the max height of image is higher.
		 */
		ExtractorManager();

		/**
		 * Extract the Descriptors of all images in the dataset.
		 * @param reader - initialized dataset reader object
		 * @param ext - initialized extractor object
		 * @param features - The computed Descriptors over whole dataset
		 */
		void extract_data(DataSetReader& reader, Extractor& ext, cv::Mat& features) const;

		/**
		 * Extract the Bag Of Words from all images in the dataset.
		 * @param reader - initialized dataset reader object
		 * @param ext - initialized extractor object
		 * @param bow_ext - BOW extractor.
		 * @param features - The computed Bag Of Words over the whole dataset
		 */
		void extract_data(DataSetReader& reader, Extractor& ext, BOW& bow_ext, cv::Mat& features) const;

		/**
		 * Method use the ext to extract Descriptors from input image.
		 * @param image - the source image to compute the descriptors from
		 * @param ext - extractor
		 * @param feature - computed descriptors over the image or ROI of the image
		 * @param mask - the mask to define the ROI of the source image
		 */
		void extract_data(const cv::Mat& image, Extractor& ext, cv::Mat& feature, const cv::Rect_<int>* mask = NULL) const;

		/**
		 * Method use the ext to extract Descriptors from input image and after that computes the Bag Of Words feature upon the Descriptors.
		 * @param image- the source image to compute the BOW from
		 * @param ext - extractor
		 * @param bow_ext - BOW extractor
		 * @param features - computed BOW of the source image
		 * @param mask - the mask to define the ROI of the source image
		 */
		void extract_data(const cv::Mat& image, Extractor& ext, BOW& bow_ext, cv::Mat& features, const cv::Rect_<int>* mask = NULL) const;

		/**
		 * Sets the max resolution of the source image. If the source image is bigger, then is interpolated to defined size.
		 * @param x Width of the image
		 * @param y Height of the image
		 */
		void set_resolution(const unsigned int x, const unsigned int y);

		/**
		 * Destructor of ExtractorManager object.
		 */
		virtual ~ExtractorManager();

	protected:
			virtual void resize(const cv::Mat& input, const cv::Point_<int>& size, cv::Mat& output) const;

	protected:
		cv::Point_<int> resolution;
	};

	/**
	 * Creates the Vocabulary used to compute the Bag Of Words - BOW.
	 */
	class VOC
	{
	public:
		/**
		 * NonParametric constructor. The default size of VOC is set to 1024.
		 */
		VOC();
	public:
	//-----Creating VOC
		/**
		 * Setter for the VOC size to compute.
		 * @param number_of_words - the size of the VOC
		 */
		void set_voc_size(const unsigned int number_of_words);

		/**
		 * Compute the Vocabulary from Descriptors from dataset
		 * @param reader - initialized dataset reader object
		 * @param ext - extractor of Descriptors
		 * @param extractorManager
		 */
		void createVOC(DataSetReader& reader, Extractor& ext, ExtractorManager& extractorManager);

		/**
		 * Saves the computed vocabulary to the XML file
		 * @param file_name_voc_storage - file name of the saved vocabulary
		 */
		void saveVOC(const std::string& file_name_voc_storage);
		virtual ~VOC();
	// ToDo Return VOC as Mat and not only store into file?
	protected:
		cv::Mat voc;
		unsigned int voc_size;
	};

	void read_voc(const std::string& voc_path, cv::Mat& voc);
}

#endif /* BOW_GENERATOR_HPP_ */
