#include "FeatureExtractor.h"



FeatureExtractor::FeatureExtractor(vector<Point> contour)
{
	FeatureExtractor::contour = contour;

	rect = minAreaRect(contour);
}


FeatureExtractor::~FeatureExtractor()
{
}

void FeatureExtractor::Extract(Mat &ref)
{
	//Bepaal verschillende features
	double ar = AspectRatio();
	double cr = Circularity();
	double bendingEnergy = getBendingEnergy();
	double convexHullBendingEnergy = getConvexHullBendingEnergy();
	double radius = getMinEnclosingCircleRadius();
	double perimeter = getPerimeter();
	double numDefects = convexDefects();

	ref = (Mat_<double>(1, 5) << 1.0, ar, cr / 10.0, convexHullBendingEnergy / 100.0, numDefects / 100.0);
}

//Rotatie onafhankelijke aspect ratio
double FeatureExtractor::AspectRatio()
{
	double ar = 0;

	if(rect.size.width > rect.size.height)
		ar = (rect.size.height / rect.size.width);
	else
		ar = (rect.size.width / rect.size.height);

	return ar;
}


//Oppervlakte van de minimale circle gedeeld door het oppervlakte van de bounding box
double FeatureExtractor::Circularity()
{
	double radius = getMinEnclosingCircleRadius();
	double carea = radius * radius * M_PI;
	double rectarea = rect.size.width * rect.size.height;
	double cir = carea / rectarea;

	return cir;
}

//Rotated bounding box om het object
RotatedRect FeatureExtractor::Rectangle()
{
	return rect;
}

//De radius van de minimale circle
double FeatureExtractor::getMinEnclosingCircleRadius()
{
	float radius;
	Point2f center;
	minEnclosingCircle(contour, center, radius);

	double rad = (double)radius;

	return rad;
}

//De lengte van het contour
double FeatureExtractor::getPerimeter()
{
	double per = arcLength(contour, true);

	return per;
}

//De bending energy van de contour
double FeatureExtractor::getBendingEnergy()
{
	double energy = 0;
	int dir = 0;
	int prevdir = 0;

	Point previousPoint = contour[contour.size() - 1];

	for (Point p : contour)
	{
		dir = discoverNextRelativeDirection(previousPoint, p);
		energy += (dir - prevdir + 8) % 8;
		prevdir = dir;
		previousPoint = p;
	}

	return energy;
}

//De bending energy van de convex hull
double FeatureExtractor::getConvexHullBendingEnergy() {

	vector<Point> convex;
	convexHull(contour, convex);
	double energy = 0;
	int dir = 0;
	int prevdir = 0;

	Point previousPoint = convex[convex.size() - 1];

	for (Point p : convex)
	{
		dir = discoverNextRelativeDirection(previousPoint, p);
		energy += (dir - prevdir + 8) % 8;
		prevdir = dir;
		previousPoint = p;
	}

	return energy;
}

//Bending energy support methode, zoekt naar de hoek tussen twee pixels volgens de freeman code
int FeatureExtractor::discoverNextRelativeDirection(const cv::Point &pos, const cv::Point &target)
{
	for (int i = 0; i < 8; i++) {
		int dir = i;

		int newX = pos.x + rotateX[dir];
		int newY = pos.y + rotateY[dir];

		if (target.x == newX && target.y == newY)
			return dir;
	}

	return -1;
}

//Het aantal defecten die in de convexhull zitten
double FeatureExtractor::convexDefects()
{
	vector<int> hullsI(contour.size()); // Indices to contour points
	vector<Vec4i> defects;
	convexHull(contour, hullsI, false);
	convexityDefects(contour, hullsI, defects);

	return (double)defects.size();
}

//Statische help functie om het contour te bepalen in een plaatje
void FeatureExtractor::findContour(Mat &image, vector<Point> &contour)
{
	Mat canny_output;
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	Canny(image, canny_output, 20, 150, 3);
	findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	int large = 0;
	int contouridx = -1;

	for (int i = 0; i < contours.size(); i++)
	{
		double a = arcLength(contours[i], false);

		if (a > large)
		{
			large = a;
			contouridx = i;
		}
	}

	contour = contours[contouridx];
}