#include "svm.h"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>

#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
        ( std::ostringstream() << std::dec << x ) ).str()


using namespace std;
using namespace cv;


/*int svm::findBiggestContour(vector<vector<Point> > contours){
    int indexOfBiggestContour = -1;
    int sizeOfBiggestContour = 0;
    for (int i = 0; i < contours.size(); i++){
        if(contours[i].size() > sizeOfBiggestContour){
            sizeOfBiggestContour = contours[i].size();
            indexOfBiggestContour = i;
        }
    }
    return indexOfBiggestContour;

}*/

//calculate Hu moments of binary images and set labels
void svm::setTrainingData()
{
    Mat classes;
    Mat trainingData;
    Mat trainingImages;
    vector<int> trainingLabels;
    int number = 1;
    ifstream stream;
    for(int j = 0; j < 5; j++)
    {
        char path[80];
        stream.open(SSTR( "svm_data/" << number << "/bg.txt").c_str());
        if(!stream)
        {
            cout << "While opening a file an error is encountered" << endl;
        }
        else
        {
            cout << "File is successfully opened" << endl;
        }
        while(!stream.eof())
        {

            stream >> path;
            Mat image1 = imread(SSTR( "svm_data/" << number << "/" << path), 0);
            Mat image2 = Mat::zeros( image1.size(), CV_32FC1 );
            vector<vector<Point> > contours;
            vector<Vec4i> hierarchy;
            cv::findContours(image1, contours, hierarchy,CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
            int indexOfBiggestContour = -1;
            int sizeOfBiggestContour = 0;
            for (int i = 0; i < contours.size(); i++)
            {
                if(contours[i].size() > sizeOfBiggestContour)
                {
                    sizeOfBiggestContour = contours[i].size();
                    indexOfBiggestContour = i;
                }
            }
            drawContours( image2, contours, indexOfBiggestContour, Scalar(255,255,255),1 , 8, vector<Vec4i>(), 0, Point() );
            Moments mom = moments(image2);
            Mat hu;
            HuMoments(mom, hu);
            hu = hu.reshape(1, 1);
            trainingImages.push_back(hu);
            trainingLabels.push_back((number)%10);
        }
        number=number+1;
        stream.close();
    }
    Mat(trainingImages).copyTo(trainingData);
    trainingData.convertTo(trainingData, CV_32FC1);
    Mat(trainingLabels).copyTo(classes);
    FileStorage fs("SVM/SVM_42.xml", FileStorage::WRITE);
    fs << "TrainingData" << trainingData;
    fs << "classes" << classes;
    fs.release();
}


void svm::train()
{
    FileStorage fs;
    fs.open("SVM/SVM_42.xml", FileStorage::READ);
    Mat SVM_TrainingData;
    Mat SVM_Classes;
    fs["TrainingData"] >> SVM_TrainingData;
    fs["classes"] >> SVM_Classes;
    double scale;
    Mat hu_min(7, 1,  DataType<double>::type);
    Mat hu_max(7, 1,  DataType<double>::type);
    //Set SVM params
    CvSVM svmClassifier;
    CvSVMParams SVM_params;
    SVM_params.svm_type = CvSVM::C_SVC;
    SVM_params.kernel_type = CvSVM::RBF;
    SVM_params.degree = 2;
    SVM_params.gamma = 5;
    SVM_params.coef0 = 0;
    SVM_params.C = 128;
    SVM_params.nu = 0;
    SVM_params.p = 0;
    CvParamGrid CvParamGrid_C(pow(2.0,-5), pow(2.0,15), pow(2.0,2));
    CvParamGrid CvParamGrid_gamma(pow(2.0,-15), pow(2.0,5), pow(2.0,2));
    if (!CvParamGrid_C.check() || !CvParamGrid_gamma.check())
        cout<<"The grid is NOT VALID."<<endl;
    for(int l=0; l<7; l++)
    {
        double min, max;
        minMaxLoc(SVM_TrainingData.col(l), &min, &max);
        hu_min.at<double>(l,0)=min;
        hu_max.at<double>(l,0)=max;
        // normalize to range [-1,1]
        SVM_TrainingData.col(l) = -1 + 2*(SVM_TrainingData.col(l)-min)/(max-min);
    }
    SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER +CV_TERMCRIT_EPS, 10000000000, 0.0000000001);//0.01
    //svmClassifier.train_auto(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_params,10, CvParamGrid_C, CvParamGrid_gamma, CvSVM::get_default_grid(CvSVM::P), CvSVM::get_default_grid(CvSVM::NU), CvSVM::get_default_grid(CvSVM::COEF), CvSVM::get_default_grid(CvSVM::DEGREE), true);
    svmClassifier.train(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_params);
    svmClassifier.save("SVM_42.xml");
    FileStorage fs1("scale_42.xml", FileStorage::WRITE);
    fs1 << "hu_min" << hu_min;
    fs1 << "hu_max" << hu_max;
    fs1.release();
}

void svm::test()
{
    int number = 1;
    ifstream stream;
    for(int j = 0; j < 5; j++)
    {
        int false_prediction = 0;
        CvSVM svmClassifier;
        Mat hu_min(7, 1,  DataType<double>::type);
        Mat hu_max(7, 1,  DataType<double>::type);
        char path[80];
        stream.open(SSTR( "svm_data/test_data_" << number << "/bg.txt").c_str());
        if(!stream)
        {
            cout << "While opening a file an error is encountered" << endl;
        }
        else
        {
            cout << "File is successfully opened" << endl;
        }
        while(!stream.eof())
        {
            stream >> path;
            Mat image1 = imread(SSTR( "svm_data/test_data_" << number << "/" << path), 0);
            Mat image2 = Mat::zeros( image1.size(), CV_32FC1 );
            vector<vector<Point> > contours;
            vector<Vec4i> hierarchy;
            cv::findContours(image1, contours, hierarchy,CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
            int indexOfBiggestContour = -1;
            int sizeOfBiggestContour = 0;
            for (int i = 0; i < contours.size(); i++)
            {
                if(contours[i].size() > sizeOfBiggestContour)
                {
                    sizeOfBiggestContour = contours[i].size();
                    indexOfBiggestContour = i;
                }
            }
            drawContours( image2, contours, indexOfBiggestContour, Scalar(255,255,255),1 , 8, vector<Vec4i>(), 0, Point() );
            Moments mom = moments(image2);
            Mat hu;
            HuMoments(mom, hu);
            int response;
            FileStorage fs;
            fs.open("scale_42.xml", FileStorage::READ);
            fs["hu_min"] >> hu_min;
            fs["hu_max"] >> hu_max;
            for(int l=0; l<7; l++)
            {
                hu.at <double >(l ,0) = -1 + 2*(hu.at <double >(l ,0)-hu_min.at<double>(l,0))/(hu_max.at<double>(l,0)-hu_min.at<double>(l,0));
            }
            hu= hu.reshape(1, 1);
            hu.convertTo(hu, CV_32FC1);
            svmClassifier.load("SVM_42.xml");
            response = (int)svmClassifier.predict(hu);
            if(response!=(number%10))
            {
                false_prediction++;
                //save false predictions
                imwrite(SSTR( "svm_data/test_" << number << "/" <<response << "_"<<path), image2);
                waitKey(0);
                fs.release();
            }
        }
        cout << "false_prediction"<<false_prediction << endl;
        number = number + 1;
        stream.close();
    }
}

float svm::testOneSample(Mat image1, int number1)
{
    int number=1;
    int false_predicition=0;
    CvSVM svmClassifier;
    Mat hu_min(7, 1,  DataType<double>::type);
    Mat hu_max(7, 1,  DataType<double>::type);
    Mat image2= Mat::zeros( image1.size(), CV_32FC1 );
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    cv::findContours(image1, contours, hierarchy,CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
    int indexOfBiggestContour = -1;
    int sizeOfBiggestContour = 0;
    for (int i = 0; i < contours.size(); i++)
    {
        if(contours[i].size() > sizeOfBiggestContour)
        {
            sizeOfBiggestContour = contours[i].size();
            indexOfBiggestContour = i;
        }
    }
    drawContours( image2, contours, indexOfBiggestContour, Scalar(255,255,255),1 , 8, vector<Vec4i>(), 0, Point() );
    Moments mom = moments(image2);
    Mat hu;
    HuMoments(mom, hu);
    float response;
    FileStorage fs;
    fs.open(SSTR( "scale_" << number1 << ".xml" ), FileStorage::READ);
    fs["hu_min"] >> hu_min;
    fs["hu_max"] >> hu_max;
    for(int l=0; l<7; l++)
    {
        hu.at <double >(l ,0)= -1 + 2*(hu.at <double >(l ,0)-hu_min.at<double>(l,0))/(hu_max.at<double>(l,0)-hu_min.at<double>(l,0));
    }
    hu= hu.reshape(1, 1);
    hu.convertTo(hu, CV_32FC1);
    svmClassifier.load(SSTR( "SVM_" << number1 << ".xml" ).c_str());
    response = (float)svmClassifier.predict(hu, false);
    return response;
}

