<repo_name>zackdove/detecting-dartboards<file_sep>/f1score_darts.cpp
/////////////////////////////////////////////////////////////////////////////
//
// Calculate the F1-Score for the faces.
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
//For printing correct precision
#include <iomanip>

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame, int imgnum );
void printFaces(std::vector<Rect> faces);
vector<Rect> ground_truth(int filenum);
Rect vect_to_rect(vector<int> vect);
float iou(Rect A, Rect B);
int calculate_all();
float f1_score(float FalsePos, float TruePos, float real_pos);

/** Global variables */
String cascade_name = "dart.xml";
CascadeClassifier cascade;

/** @function main */
int main( int argc, const char** argv ){
	string imgnum = argv[1];
	if (imgnum == "all"){
		calculate_all();
	} else {
		string filename =  "dartPictures/dart"+imgnum+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
		detectAndDisplay( frame, stoi(imgnum) );
		imwrite( "detected.jpg", frame );
	}
	return 0;
}

 int calculate_all(){
	 for (int imgnum = 0; imgnum<16; imgnum++){
		string filename =  "dartPictures/dart"+to_string(imgnum)+".jpg";
 		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
 		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
 		detectAndDisplay( frame, imgnum);
 		imwrite( "detected"+to_string(imgnum)+".jpg", frame );
	 }
	 return 0;
 }

float f1_score(float FalsePos, float TruePos, float RealPos){
	float precision;
	if (TruePos == 0) {
		precision = 0;
	} else {
		precision = TruePos/(TruePos+FalsePos);
	}
	float recall;
	if (TruePos == 0) {
		recall = 0;
	} else {
		recall = TruePos/(RealPos);
	}
	if (precision == 0 && recall == 0){
		return 0;
	} else {
		return 2*(precision*recall)/(precision+recall);
	}
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame, int imgnum){
	std::vector<Rect> detected_faces;
	Mat frame_gray;
	cvtColor( frame, frame_gray, CV_BGR2GRAY );
	equalizeHist( frame_gray, frame_gray );
	cascade.detectMultiScale( frame_gray, detected_faces, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE, Size(50, 50), Size(500,500) );
	vector<Rect> truth_faces = ground_truth(imgnum);
	// printFaces(truth_faces);
	float threshold = 0.5;
	float true_positives = 0;
	float false_positives = 0;
	//Calculate IOU for each
	for (int j = 0; j < truth_faces.size(); j++){
		for( int i = 0; i < detected_faces.size(); i++ ){
			float iou_result = iou(detected_faces[i], truth_faces[j]);
			// std::cout << "IOU for ground truth face " << truth_faces[j] << " with detected face " << detected_faces[i] << " = " << iou_result << std::endl;
			if (iou_result >= threshold){
				true_positives++;
				//Prevents TP being higher than 1 for each face. Assumes that there are no faces inside faces. In which cases we would need to double break
				break;
			}
		}
	}
	false_positives += detected_faces.size()-true_positives;
	int real_pos = truth_faces.size();
	float f1 = f1_score(false_positives, true_positives, real_pos);
	std::cout << "Image: " << imgnum << std::endl;
	// cout << "Real Pos = " << real_pos << endl;
	// cout << "Detected # Faces = " << detected_faces.size() << endl;
	// cout << "True Positives = " << true_positives << endl;
	// cout << "Detected # Faces = " << detected_faces.size() << endl;
	float tpr;
	//Handling for div 0
	if (true_positives == 0) {
		tpr = 0;
	} else {
		tpr = true_positives/real_pos;
	}
	std::cout << "TPR " << tpr << std::endl;
	std::cout << "F1: " << f1 << "\n" << std::endl;
	//Draw for groud truthh
	for (int i = 0; i < truth_faces.size(); i++){
		rectangle(frame, Point(truth_faces[i].x, truth_faces[i].y), Point(truth_faces[i].x + truth_faces[i].width, truth_faces[i].y + truth_faces[i].height), Scalar( 0, 0, 255 ), 2);
	}
	for( int i = 0; i < detected_faces.size(); i++ ){
		rectangle(frame, Point(detected_faces[i].x, detected_faces[i].y), Point(detected_faces[i].x + detected_faces[i].width, detected_faces[i].y + detected_faces[i].height), Scalar( 0, 255, 0 ), 2);
	}
}

void printFaces(std::vector<Rect> faces){
	for (int i = 0; i < faces.size(); i++){
		std::cout << faces[i] << std::endl;
	}
}

//Using https://www.geeksforgeeks.org/csv-file-management-using-c/
//Can optimise by storing into an array
vector<Rect> ground_truth(int filenum){
	// std::cout << filenum << std::endl;
	// File pointer
    ifstream fin("dart.csv");
	int file2, count = 0;
	vector<string> row;
    string line, word, temp;
	vector<int> face_coords;
	vector<Rect> face_coords_set;
	while (getline(fin, line)) {
        row.clear();
        istringstream iss(line);
        while (getline(iss, word, ',')) {
            row.push_back(word);
        }
        file2 = stoi(row[0]);
        if (file2 == filenum) {
            count = 1;
			row.erase(row.begin());
			// std::cout << "row size = " << row.size() << std::endl;
			for (int i=0; i<row.size(); i=i+4){
				face_coords_set.push_back(Rect(stoi(row[i]),stoi(row[i+1]),stoi(row[i+2])-stoi(row[i]),stoi(row[i+3])-stoi(row[i+1])));
			}
			return face_coords_set;
            break;
        }
    }
    if (count == 0){
        // cout << "Record not found\n";

	}
	return face_coords_set;
}

Rect vect_to_rect(vector<int> vect){
	return Rect(vect[0], vect[1], vect[2]-vect[0], vect[3]-vect[1]);
}

float iou(Rect A, Rect B){
	// std::cout << "A " << A << std::endl;
	// std::cout << "B " << B << std::endl;
    Rect intersection = A&B;
	// std::cout << "inter " << intersection.area() << std::endl;
    float iou = (float)intersection.area() / ((float)A.area()+(float)B.area()-(float)intersection.area());
	// std::cout << "iou " << std::fixed << std::setprecision(5) << iou << std::endl;
    return iou;
}
<file_sep>/readme.md
# Detecting Dartboards

## Image Processing & Computer Vision project.

In this project we will be exploring, implementing and analysing image processing and computer visio techniques in the context of identifying dartboards in images. We will experiment with the Viola-Jones object detection framework, and combine with other techniques to improve efficiacy. Working in C++ and OpenCV. We also implemented a deep neural network.

See [here](https://github.com/zackdove/detecting-dartboards/blob/master/report/darts_report.pdf) for the results and final report.

Running the detector

compile with "g++ main.cpp /usr/lib64/libopencv_core.so.2.4  /usr/lib64/libopencv_highgui.so.2.4  /usr/lib64/libopencv_imgproc.so.2.4  /usr/lib64/libopencv_objdetect.so.2.4 -std=c++11"

run with a.out "image number"
<file_sep>/hough.h
/////////////////////////////////////////////////////////////////////////////
//
// HOUGH header functions
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <iostream>
#include<string>

using namespace std;
using namespace cv;

/** Function Headers */
void normaliseMatrix( Mat matrix );
void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame,
	                Mat &accu_m, Mat &points);
vector<int> hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame);
void hough_clustered_lines(Mat &frame, vector<int> points_lines, Mat &accu_m);
void hough_combine(Mat &accu_circle, Mat &accu_clustered_lines, Mat& points);
void hough_detect(Mat& points, bool *detected, int center[2]);
/** */

void hough(Mat &frame, Mat &mag, Mat &dir, Mat &points){
  // Hough Circles
	int x = mag.rows;
	int y = mag.cols;
	int radius = min(x,y)/2;
	int sizes_circles[] = { x, y, radius };
	Mat accu_circles(3, sizes_circles, CV_32FC1, cv::Scalar(0));
	Mat points_circle;
	hough_circle( frame, mag, dir, accu_circles, points_circle);

	// Hough Lines
	Mat accu_clustered_lines;
	vector<int> points_lines = hough_line( frame , mag, dir);
	hough_clustered_lines(frame, points_lines, accu_clustered_lines);

	// Combine Circle and Line
	hough_combine(points_circle, accu_clustered_lines, points);
}


void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame,
	Mat &accu_m, Mat &points){
	int minRadius = 15;
	const int x = mag_frame.rows;
	const int y = mag_frame.cols;
	const int radius = min(x,y) / 4;  // can be max half the size of dimensions
  points = Mat(x/2, y/2, CV_32S, cvScalar(0));
	// Look for strong edges
	for( int i = 0; i < x; i++){
		for( int j = 0; j < y; j++){
			// Pixel threshold
			if(mag_frame.at<float>(i, j) > 50) {
				// Vote for circles corresponding to point's +ve and -ve direction
				for( int r = minRadius; r < radius; r++){
					float d = dir_frame.at<float>(i, j);
					int a = i + r*cos(d);
					int b = j + r*sin(d);
					// Check that the radius doesn't exceed the boarder
					if((a > 0) && (b > 0) && (a < x) && (b < y)){
						accu_m.at<int>(a, b, r) += 1;
					}
					a = i - r*cos(d);
					b = j - r*sin(d);
					// Check that the radius doesn't exceed the boarder
					if((a > 0) && (b > 0) && (a < x) && (b < y)){
						accu_m.at<int>(a, b, r) += 1;
					}
				}
			}
		}
	}

	// Condensing points to (x,y) by summing radii
	for( int i = 0; i < mag_frame.rows; i++){
		for( int j = 0; j < mag_frame.cols; j++){
			for( int r = minRadius; r < radius; r++){
				if(i/2 < x/2 && j/2 < y/2){
					points.at<int>(i/2, j/2) += accu_m.at<int>(i,j,r);
				}
			}
		}
	}
	normaliseMatrix( points );
	//imwrite("circle_hough.jpg", points);
}

void hough_ellipse(Mat &frame){
	//Calculate hough space for ellipse
	//Threshold
	//If above threshold, return true
}

vector<int> hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame){

	vector<int> points_lines;
	const int d = sqrt((mag_frame.rows*mag_frame.rows) +
	(mag_frame.cols*mag_frame.cols)); //Diameter of image
	Mat accu_m = Mat(d, 180, CV_32S, cvScalar(0));

	// Look for strong edges
	for( int i = 0; i < mag_frame.rows; i++){
		for( int j = 0; j < mag_frame.cols; j++){
			if(mag_frame.at<float>(i, j) > 45) { // Threshhold
				// If edge is strong, vote for lines corresponding to its point
				for( int r = 0; r < 180; r++){
					int p = (i*cos(r*CV_PI/180) + (j*sin(r*CV_PI/180)));
					if(p > 0) {
						accu_m.at<int>(p, r)++;
					}
				}
			}
		}
	}

	for( int p = 0; p < d; p++) {
		for( int r = 0; r < 180; r++) {
			if(accu_m.at<int>(p, r) > 100) { // Threshold
				float a = cos(r*CV_PI/180);
				float b = sin(r*CV_PI/180);
				int x0 = a*p;
				int y0 = b*p;
				int x1 = int(x0 + 1000*(-b));
				int y1 = int(y0 + 1000*(a));
				int x2 = int(x0 - 1000*(-b));
				int y2 = int(y0 - 1000*(a));
				points_lines.push_back(x1);
				points_lines.push_back(y1);
				points_lines.push_back(x2);
				points_lines.push_back(y2);
			}
		}
	}
  //imwrite("lines_hough.jpg", accu_m);
	return points_lines;
}

void hough_clustered_lines(Mat &frame, vector<int> points_lines, Mat &accu_m){
	vector<int> points_clustered_lines;
	// Reducing the resolution of the image to reduce spread of points
	accu_m = Mat(frame.rows/2, frame.cols/2, CV_32S, cvScalar(0));

	for( int p = 0; p < points_lines.size(); p = p + 4){
		float x1 = (float)points_lines[p]   /2;
		float y1 = (float)points_lines[p+1] /2;
		float x2 = (float)points_lines[p+2] /2;
		float y2 = (float)points_lines[p+3] /2;
		float m = (y1 - y2) / (x1 - x2);
		float c = y1 - (m*x1);

		for(int x = 0; x < frame.rows/2; x ++){
			int y = (int)(x*m) + c;
			if( (y > 0) && (y < frame.cols/2)){
				accu_m.at<int>(x, y)++;
			}
		}
	}
	normaliseMatrix(accu_m);
	//imwrite("clustered_hough.jpg", accu_m);
}

void hough_combine(Mat &accu_circle, Mat &accu_clustered_lines, Mat& points){
	const int x = accu_circle.rows;
	const int y = accu_circle.cols;
	points = Mat(x, y, CV_32S, cvScalar(0));
	for(int i = 0; i < x; i++) {
		for(int j = 0; j < y; j++) {
			points.at<float>(i, j) += accu_circle.at<float>(i, j);
			points.at<float>(i, j) += accu_clustered_lines.at<float>(i, j);
		}
	}
	normaliseMatrix(points);
	//imwrite("both1.jpg", points);
}

// Send in specific boxes, if any value is 255 return true
void hough_detect(Mat& points, bool *detected, int center[2]){
	const int x = points.rows;
	const int y = points.cols;
	for(int i=0; i < x; i++){
		for(int j = 0; j < y; j++){
			if(points.at<int>(i,j) > 235){
				*detected = true;
				// *2 because points is half-sized
				center[0] = i * 2;
				center[1] = j * 2;
			}
		}
	}
}
<file_sep>/intersect.cpp
/////////////////////////////////////////////////////////////////////////////
//
// Detect dartboards
//
// compile with "g++ detect_dart_with_hough.cpp /usr/lib64/libopencv_core.so.2.4  /usr/lib64/libopencv_highgui.so.2.4  /usr/lib64/libopencv_imgproc.so.2.4  /usr/lib64/libopencv_objdetect.so.2.4 -std=c++11"
// run with a.out "image number"
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
//For printing correct precision
#include <iomanip>
#include <iostream>
#include<string>


using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame, int imgnum );
void printFaces(std::vector<Rect> faces);
Rect vect_to_rect(vector<int> vect);
void calculate_all();
void sobel(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo);
void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir);
void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m);
void hough_ellipse(Mat &mag_frame);
vector<vector<int>> hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m);
vector<vector<int>> hough_clustered_lines(vector<vector<int>> points_lines);

/** Global variables */
String cascade_name = "dart.xml";
CascadeClassifier cascade;

/** @function main */
int main( int argc, const char** argv ){
	string imgnum = argv[1];
	if (imgnum == "all"){
		calculate_all();
	} else {
		string filename =  "dart"+imgnum+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
		detectAndDisplay( frame, stoi(imgnum) );
		imwrite( "detected.jpg", frame );
	}
	return 0;
}

int ***malloc3dArray(int dim1, int dim2, int dim3){
	int i, j, k;
	int ***array = (int ***) malloc(dim1 * sizeof(int **));
	for (i = 0; i < dim1; i++) {
		array[i] = (int **) malloc(dim2 * sizeof(int *));
		for (j = 0; j < dim2; j++) {
			array[i][j] = (int *) malloc(dim3 * sizeof(int));
		}
	}
	return array;
}

void calculate_all(){
	for (int imgnum = 0; imgnum<16; imgnum++){
		string filename =  "dart"+to_string(imgnum)+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); };
		detectAndDisplay( frame, imgnum);
		imwrite( "detected"+to_string(imgnum)+".jpg", frame );
	}
}

void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m, Mat &points){
	//Calculate hough space for circles
	//Threshold
	//If above threshold, return true
        points.create(mag_frame.size(), CV_32FC1);
	const int x = mag_frame.rows;
	const int y = mag_frame.cols;
	const int radius = min(x,y) / 2;  // can be max half the size of the smallest dimension

	// Look for strong edges
	for( int i = 0; i < mag_frame.rows; i++){
	  for( int j = 0; j < mag_frame.cols; j++){
	    // Pixel threshold
	    if(mag_frame.at<float>(i, j) > 200) {
	      // If edge is strong, vote for circles corresponding to its direction
	      for( int r = 30; r < radius; r++){ 
		for (int k = -1; k < 2; k++){
		  for (int l = -1; l < 2; l++){
		    // Circle centers around selected point 
		      int d = dir_frame.at<float>(i, j);
		      int a = i + k*r*cos(d*CV_PI/180);
		      int b = j + l*r*sin(d*CV_PI/180);
		      // Check that the radius doesn't exceed the boarder
		      if((a > 0) && (b > 0) && (a < x) && (b < y)){	
			accu_m.at<float>(a, b, r) += 1;
		      }
		  } 
		}
	      }	
	    }
	  }
	}

	// Condensing points to just (x,y)
        for( int i = 0; i < mag_frame.rows; i++){
	  for( int j = 0; j < mag_frame.cols; j++){
	    for( int r = 30; r < radius; r++){ 
	      points.at<int>(i, j) += accu_m.at<float>(i,j,r);
	    }
	    if(points.at<int>(i, j) > 300){
	      std::cout << i << ',' << j << ',' << " points = " << points.at<int>(i, j) << std::endl;
	      circle(frame, Point(j, i), 10 , Scalar( 0, 0, 255 ), 2);
	    }
	  }
	} 
}

void hough_ellipse(Mat &frame){
	//Calculate hough space for ellipse
	//Threshold
	//If above threshold, return true
}

vector<vector<int>> hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m){

  vector<vector<int>> points_lines;
  const int x = mag_frame.rows/2;
  const int y = mag_frame.cols/2;
  const int d = sqrt((mag_frame.rows*mag_frame.rows) + (mag_frame.cols*mag_frame.cols)) / 2;
  const int mini = min(x, y);  
  accu_m = Mat(d, 180, CV_32S, cvScalar(0));

  // Look for strong edges
  for( int i = 0; i < mag_frame.rows; i++){
    for( int j = 0; j < mag_frame.cols; j++){
      if(mag_frame.at<float>(i, j) > 100) { // Threshhold
      	int xd = i-x;
       	int yd = j-y;
	// If edge is strong, vote for lines corresponding to its point 
       	for( int r = 0; r < 180; r++){ 
	  int p = abs((xd*cos(r*CV_PI/180)) + (yd*sin(r*CV_PI/180)));
	  accu_m.at<int>(p, r)++;
      	}
      } 
    }
    for( int k = 0; k < d; k++) {
      for( int l = 0; l < 180; l++) {
	if(accu_m.at<int>(k, l) > 200) { // Threshold
	 float a = cos(l*CV_PI/180);
	 float b = sin(l*CV_PI/180);
	 int x0 = x + a*k;
	 int y0 = y + b*k;
	 int x1 = int(x0 + mini*(-b));
	 int y1 = int(y0 + mini*(a));
	 int x2 = int(x0 - mini*(-b));
	 int y2 = int(y0 - mini*(a));
	 vector<int> points = {x1,y1,x2,y2};
	 points_lines.push_back(points);
	 line(frame, Point(y1,x1), Point(y2,x2), (0,0,255), 2);
	} 
      }		 
    }
  }
  return points_lines;
}

vector<vector<int>> hough_clustered_lines(vector<vector<int>> points_lines){
  vector<vector<int>> points_clustered_lines;

  for( int p = 0; p < points_lines.size(); p++){ 
	 int x1 = points_lines[p][0];
	 int y1 = points_lines[p][1];
	 int x2 = points_lines[p][2];
	 int y2 = points_lines[p][3];
	std::cout << x1 << ',' << x2 << ',' << y1 << ',' << y2 << std::endl;
      	}
  return points_clustered_lines;
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame, int imgnum){
	std::vector<Rect> detected_dartboards;
	Mat frame_grey;
	cvtColor( frame, frame_grey, CV_BGR2GRAY );
	equalizeHist( frame_grey, frame_grey );
	cascade.detectMultiScale( frame_grey,
		detected_dartboards, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE,
		Size(50, 50), Size(500,500) );
	cout << "Original detected dartboards" << endl;
	printFaces(detected_dartboards);

	//for (int i = 0; i < detected_dartboards.size(); i++){
	//Mat dartboard_frame = frame(detected_dartboards[i]);
	//Check if face_frame cotains circle, line, ellipse etc
	//If it doesnt meet the criteria, erase it

	// create the SOBEL kernel in 1D, Y is transpose
	Mat_<int> kernel(3,3);
	Mat_<int>kernelT(3,3);
	kernel << -1, 0, 1, -2, 0, 2, -1, 0, 1;
	kernelT << 1, 2, 1, 0, 0, 0, -1, -2, -1;

	// Sobel filter
	Mat xConvo, yConvo, mag, dir;
	sobel(frame_grey,kernel, xConvo);
	sobel(frame_grey, kernelT, yConvo);
	imwrite( "x.jpg", xConvo ); 
	imwrite( "y.jpg", yConvo );
	magDir( xConvo, yConvo, mag, dir);
	imwrite( "Mag.jpg", mag ); 
	imwrite( "Dir.jpg", dir );

        Mat points_circle;
	int x = mag.rows;
	int y = mag.cols;

	int radius = min(x,y)/2;
	int sizes_circles[] = { x, y, radius };
	Mat accu_circles(3, sizes_circles, CV_32FC1, cv::Scalar(0));
	//hough_circle( frame, mag, dir, accu_circles, points_circle );

	Mat accu_lines;
	vector<vector<int>> points_lines = hough_line( frame, mag, dir, accu_lines);
        vector<vector<int>> points_clustered_lines = hough_clustered_lines(points_lines); 

	//Draw detected dartboards from cascade for comparison
	for (int i = 0; i < detected_dartboards.size(); i++){
		rectangle(frame,
			Point(detected_dartboards[i].x, detected_dartboards[i].y),
			Point(detected_dartboards[i].x + detected_dartboards[i].width, 
			      detected_dartboards[i].y + detected_dartboards[i].height),
			Scalar( 0, 0, 255 ), 2);
	}
}

void printFaces(std::vector<Rect> faces){
	for (int i = 0; i < faces.size(); i++){
		std::cout << faces[i] << std::endl;
	}
}

void sobel(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo){
	// intialise the output using the input
	convo.create(input.size(), CV_32FC1);

	// we need to create a padded version of the input
	// or there will be border effects
	int kernelRadiusX = ( kernel.size[0] - 1 ) / 2;
	int kernelRadiusY = ( kernel.size[1] - 1 ) / 2;

	cv::Mat paddedInput;
	cv::copyMakeBorder( input, paddedInput,
		kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY,
		cv::BORDER_REPLICATE );
		for ( int i = 0; i < input.rows; i++ )
		{
		  for( int j = 0; j < input.cols; j++ )
		    {
		      float sum = 0.0;
		      for( int m = -kernelRadiusX; m <= kernelRadiusX; m++ )
			{
			  for( int n = -kernelRadiusY; n <= kernelRadiusY; n++ ){
			    // find the correct indices we are using
			    int imagex = i + m + kernelRadiusX;
			    int imagey = j + n + kernelRadiusY;
			    int kernelx = m + kernelRadiusX;
			    int kernely = n + kernelRadiusY;
			    
			    // get the values from the padded image and the kernel
			    int imageval = ( int ) paddedInput.at<uchar>( imagex, imagey );
			    int kernalval = kernel.at<int>( kernelx, kernely );
			    
			    // do the multiplication
			    sum = sum + ( imageval * kernalval );
			    
			  }
			}
		      convo.at<float>(i, j) = sum;
		    }
		}
}

void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir)
{
  // intialise the output using the input
  mag.create(inputx.size(), CV_32FC1);
  dir.create(inputx.size(), CV_32FC1);
  
  float magmin = 1000;
  float magmax = -1000;
  
  for ( int i = 0; i < inputx.rows; i++ )
    {	
      for( int j = 0; j < inputx.cols; j++ )
	{
	  
	  float magx = inputx.at<float>(i, j);
	  float magy = inputy.at<float>(i, j);
	  float magTemp = sqrt((magx*magx) + (magy*magy));
	  float dirTemp = atan2(magy,  magx) * 180 / CV_PI;
	  mag.at<float>(i, j) = magTemp;
	  dir.at<float>(i, j) = dirTemp;		           
	  
	  // Finding min & max values (for normalisation)
	  if(magTemp > magmax) {
	    magmax = magTemp;
	  }
	  if(magTemp < magmin) {
	    magmin = magTemp;
	  }  
	}
    }
  
  //Normalising loop
  for ( int i = 0; i < inputx.rows; i++ )
    {	
      for( int j = 0; j < inputx.cols; j++ )
	{
	  //Normalize calculation
	  float normMag = ((mag.at<float>(i, j) - magmin) * 255)/ (magmax - magmin);
	  mag.at<float>(i, j) =  normMag;
	}		
    }
}
<file_sep>/sobel.h
/////////////////////////////////////////////////////////////////////////////
//
// SOBEL header functions
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <iostream>
#include<string>

using namespace std;
using namespace cv;



/** Function Headers */
void normaliseMatrix( Mat matrix );
void direction(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo);
void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir);

void sobel(Mat &frame_grey, Mat &xConvo, Mat &yConvo, Mat &mag, Mat &dir){
	// Create the SOBEL kernel in 1D, Y is transpose
	Mat_<int> kernel(3,3);
	Mat_<int>kernelT(3,3);
	kernel << -1, 0, 1, -2, 0, 2, -1, 0, 1;
	kernelT << 1, 2, 1, 0, 0, 0, -1, -2, -1;

	// Sobel filter
	direction(frame_grey,kernel, xConvo);
	direction(frame_grey, kernelT, yConvo);
	magDir( xConvo, yConvo, mag, dir);
	//imwrite( "x.jpg", xConvo );
	//imwrite( "y.jpg", yConvo );
	//imwrite( "Mag.jpg", mag );
	//imwrite( "Dir.jpg", dir );
}

void direction(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo){

convo.create(input.size(), CV_32FC1);
// we need to create a padded version of the input
// or there will be border effects
int kernelRadiusX = ( kernel.size[0] - 1 ) / 2;
int kernelRadiusY = ( kernel.size[1] - 1 ) / 2;

cv::Mat paddedInput;
cv::copyMakeBorder( input, paddedInput,
	kernelRadiusX, kernelRadiusX, kernelRadiusY, kernelRadiusY,
	cv::BORDER_REPLICATE );
	for ( int i = 0; i < input.rows; i++ )
	{
		for( int j = 0; j < input.cols; j++ )
		{
			float sum = 0.0;
			for( int m = -kernelRadiusX; m <= kernelRadiusX; m++ )
			{
				for( int n = -kernelRadiusY; n <= kernelRadiusY; n++ ){

					// find the correct indices we are using
					int imagex = i + m + kernelRadiusX;
					int imagey = j + n + kernelRadiusY;
					int kernelx = m + kernelRadiusX;
					int kernely = n + kernelRadiusY;

					// get the values from the padded image and the kernel
					int imageval = ( int ) paddedInput.at<uchar>( imagex, imagey );
					int kernalval = kernel.at<int>( kernelx, kernely );

					// do the multiplication
					sum = sum + ( imageval * kernalval );
				}
			}
			convo.at<float>(i, j) = sum;
		}
	}
}

void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir)
{
	mag.create(inputx.size(), CV_32FC1);
	dir.create(inputx.size(), CV_32FC1);

	for ( int i = 0; i < inputx.rows; i++ )
	{
		for( int j = 0; j < inputx.cols; j++ )
		{
			float magx = inputx.at<float>(i, j);
			float magy = inputy.at<float>(i, j);
			float magTemp = sqrt((magx*magx) + (magy*magy));
			float dirTemp = atan2(magy,  magx);
			mag.at<float>(i, j) = magTemp;
			dir.at<float>(i, j) = dirTemp;
		}
	}
	normaliseMatrix( mag );
}
<file_sep>/dart.cpp
/////////////////////////////////////////////////////////////////////////////
//
// COMS30121 - face.cpp
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sstream>

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame , int imageNumber);

/** Global variables */
String cascade_name = "dart.xml";
CascadeClassifier cascade;


/** @function main */
int main( int argc, const char** argv )
{
        // 1. Read Input Image
	Mat frame = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	int imageNumber = atoi(argv[2]);

	// 2. Load the Strong Classifier in a structure called `Cascade'
	if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
	
	// 3. Detect Faces and Display Result
        detectAndDisplay( frame , imageNumber);

	// 4. Save Result Image
	imwrite( "detected.jpg", frame );

	return 0;
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame , int imageNumber)
{
	std::vector<Rect> faces;
	Mat frame_gray;

	// 1. Prepare Image by turning it into Grayscale and normalising lighting
	cvtColor( frame, frame_gray, CV_BGR2GRAY );
	equalizeHist( frame_gray, frame_gray );

	// 2. Perform Viola-Jones Object Detection 
	cascade.detectMultiScale( frame_gray, faces, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE, Size(50, 50), Size(500,500) );

	// Loading true position coordinates into matrix
        vector<double> matrix;
        fstream file;
        file.open("dart.csv");
        string line;
        while (getline( file, line,'\n'))
	{
             istringstream templine(line); 
             string data;
	     while (getline( templine, data,',')) 
	        {
	           matrix.push_back(atof(data.c_str())); 
                }
	}
        vector<double> coordinates;
        for(int i = 0; i < matrix.size(); i = i+5)
             {
             if(matrix[i] == imageNumber)
		{
                   for(int j = 1; j < 5; j++)
		     {
		        coordinates.push_back(matrix[i+j]);
                     }
                }
             }
        file.close();
        
        //IOU
        for(int j = 0; j < coordinates.size(); j = j+4){
             Rect r1 = Rect(Point(coordinates[j], coordinates[j+1]), Point(coordinates[j+2], coordinates[j+3]));
             float maxiou = 0;
             for( int i = 0; i < faces.size(); i++ ) {
                 Rect r2 = Rect(Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height));
                 float is = (r1 & r2).area();
                 float u = (r1 | r2).area();
                 float iou = (double)is/u;
                 if(iou > maxiou){
                     maxiou = iou;
                 }
             }
             // Draw RED box around true position 
             rectangle(frame, Point(coordinates[j], coordinates[j+1]), Point(coordinates[j+2], coordinates[j+3]), Scalar( 0, 0, 255 ), 2);
             // Print IOU
             std::cout << "IOU for dartboard " << (j/4) + 1 << " = "<< maxiou << std::endl;
        }

        // 3. Print number of Faces found
	std::cout << faces.size() << std::endl;

        // 4. Draw box around faces found
	for( int i = 0; i < faces.size(); i++ )
	{
		rectangle(frame, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), Scalar( 0, 255, 0 ), 2);
	}
        

}
<file_sep>/detect_dart_with_hough.cpp
/////////////////////////////////////////////////////////////////////////////
//
// Detect dartboards
//
// compile with "g++ detect_dart_with_hough.cpp /usr/lib64/libopencv_core.so.2.4  /usr/lib64/libopencv_highgui.so.2.4  /usr/lib64/libopencv_imgproc.so.2.4  /usr/lib64/libopencv_objdetect.so.2.4 -std=c++11"
// run with a.out "image number"
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <iostream>
#include<string>

#include "sobel.h"
#include "hough.h"

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame, int imgnum );
void printFaces(std::vector<Rect> faces);
Rect vect_to_rect(vector<int> vect);
void calculate_all();
void sobel(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo);
void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir);
void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m);
void hough_ellipse(Mat &mag_frame);
void normaliseMatrix( Mat matrix );
vector<vector<int> > hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m);
vector<vector<int> > hough_clustered_lines(cv::Mat &frame, vector<vector<int> > points_lines);

/** Global variables */
String cascade_name = "dart.xml";
CascadeClassifier cascade;

/** @function main */
int main( int argc, const char** argv ){
	string imgnum = argv[1];
	if (imgnum == "all"){
		calculate_all();
	} else {
		string filename =  "dart"+imgnum+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
		detectAndDisplay( frame, stoi(imgnum) );
		imwrite( "detected.jpg", frame );
	}
	return 0;
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame, int imgnum){
	std::vector<Rect> detected_dartboards;
	Mat frame_grey;
	cvtColor( frame, frame_grey, CV_BGR2GRAY );
	equalizeHist( frame_grey, frame_grey );
	cascade.detectMultiScale( frame_grey,
		detected_dartboards, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE,
		Size(50, 50), Size(500,500) );
		cout << "Original detected dartboards" << endl;
		printFaces(detected_dartboards);
		//Draw detected dartboards from cascade for comparison
		for (int i = 0; i < detected_dartboards.size(); i++){
			rectangle(frame,
				Point(detected_dartboards[i].x, detected_dartboards[i].y),
				Point(detected_dartboards[i].x + detected_dartboards[i].width,
					detected_dartboards[i].y + detected_dartboards[i].height),
					Scalar( 0, 0, 255 ), 2);
		}

		// create the SOBEL kernel in 1D, Y is transpose
		Mat_<int> kernel(3,3);
		Mat_<int>kernelT(3,3);
		kernel << -1, 0, 1, -2, 0, 2, -1, 0, 1;
		kernelT << 1, 2, 1, 0, 0, 0, -1, -2, -1;

		// Sobel filter
		Mat xConvo, yConvo, mag, dir;
		sobel(frame_grey,kernel, xConvo);
		sobel(frame_grey, kernelT, yConvo);
		imwrite( "x.jpg", xConvo );
		imwrite( "y.jpg", yConvo );
		magDir( xConvo, yConvo, mag, dir);
		imwrite( "Mag.jpg", mag );
		imwrite( "Dir.jpg", dir );

		Mat points_circle;
		int x = mag.rows;
		int y = mag.cols;

		int radius = min(x,y)/2;
		int sizes_circles[] = { x, y, radius };
		Mat accu_circles(3, sizes_circles, CV_32FC1, cv::Scalar(0));
		hough_circle( frame, mag, dir, accu_circles, points_circle );

		Mat accu_lines;
		//vector<vector<int> > points_lines = hough_line( frame, mag, dir, accu_lines);
		//vector<vector<int> > points_clustered_lines = hough_clustered_lines(frame, points_lines);
}

void printFaces(std::vector<Rect> faces){
	for (int i = 0; i < faces.size(); i++){
		std::cout << faces[i] << std::endl;
	}
}

int ***malloc3dArray(int dim1, int dim2, int dim3){
	int i, j, k;
	int ***array = (int ***) malloc(dim1 * sizeof(int **));
	for (i = 0; i < dim1; i++) {
		array[i] = (int **) malloc(dim2 * sizeof(int *));
		for (j = 0; j < dim2; j++) {
			array[i][j] = (int *) malloc(dim3 * sizeof(int));
		}
	}
	return array;
}

void calculate_all(){
	for (int imgnum = 0; imgnum<16; imgnum++){
		string filename =  "dart"+to_string(imgnum)+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); };
		detectAndDisplay( frame, imgnum);
		imwrite( "detected"+to_string(imgnum)+".jpg", frame );
	}
}

void normaliseMatrix( Mat matrix ) {
	double min, max;
	cv::minMaxLoc(matrix, &min, &max);
	for ( int i = 0; i < matrix.rows; i++ )
	{
		for( int j = 0; j < matrix.cols; j++ )
		{
			//Normalize calculation
			matrix.at<float>(i, j) = (((matrix.at<float>(i, j) - min) * 255.0) / (max - min));
			//std::cout << min << ',' << max << ',' << matrix.at<float>(i, j) << std::endl;
		}
	}
}
<file_sep>/main.cpp
/////////////////////////////////////////////////////////////////////////////
//
// Detect dartboards
//
// compile with "g++ main.cpp /usr/lib64/libopencv_core.so.2.4  /usr/lib64/libopencv_highgui.so.2.4  /usr/lib64/libopencv_imgproc.so.2.4  /usr/lib64/libopencv_objdetect.so.2.4 -std=c++11"
// run with a.out "image number"
//
/////////////////////////////////////////////////////////////////////////////

// header inclusion
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <iostream>
#include<string>

#include "sobel.h"
#include "hough.h"

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame, int imgnum );
void printFaces(std::vector<Rect> faces);
Rect vect_to_rect(vector<int> vect);
void calculate_all();
void sobel(cv::Mat &input, Mat_<int> kernel, cv::Mat &convo);
void magDir(cv::Mat &inputx, cv::Mat &inputy, cv::Mat &mag, cv::Mat &dir);
void hough_circle(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame, Mat &accu_m);
void hough_ellipse(Mat &mag_frame);
void normaliseMatrix( Mat matrix );
vector<int> hough_line(cv::Mat &frame, cv::Mat &mag_frame, cv::Mat &dir_frame);
void hough_clustered_lines(Mat &frame, vector<int> points_lines, Mat &accu_m);
void hough_combine(Mat &accu_circle, Mat &accu_clustered_lines, Mat &points);
void hough_detect(Mat& points, bool *detected, int center[2]);
vector<Rect> ground_truth(int filenum);
float iou(Rect A, Rect B);
float f1_score(float FalsePos, float TruePos, float real_pos);

/** Global variables */
String cascade_name = "dart.xml";
CascadeClassifier cascade;

/** @function main */
int main( int argc, const char** argv ){
	string imgnum = argv[1];
	if (imgnum == "all"){
		calculate_all();
	} else {
		string filename =  "dartPictures/dart"+imgnum+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
		detectAndDisplay( frame, stoi(imgnum) );
		imwrite( "detected.jpg", frame );
	}
	return 0;
}

void calculate_all(){
	for (int imgnum = 0; imgnum<16; imgnum++){
		cout << "Image: " << imgnum << endl;
		string filename =  "dartPictures/dart"+to_string(imgnum)+".jpg";
		Mat frame = imread(filename, CV_LOAD_IMAGE_COLOR);
		if( !cascade.load( cascade_name ) ){ printf("--(!)Error loading\n"); };
		detectAndDisplay( frame, imgnum);
		imwrite( "detectedPictures/detected"+to_string(imgnum)+".jpg", frame );
	}
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame, int imgnum){
	std::vector<Rect> detected_dartboards;
	vector<Rect> new_detected_dartboards;
	Mat frame_grey;
	cvtColor( frame, frame_grey, CV_BGR2GRAY );
  // GaussianBlur(frame_grey, frame_grey, Size(3,3), 2, 2); // Does this help?
	vector<Rect> truth_dartboards = ground_truth(imgnum);
	float iou_threshold = 0.5;
	float true_positives = 0;
	float false_positives = 0;
	equalizeHist( frame_grey, frame_grey );
	cascade.detectMultiScale( frame_grey,
		detected_dartboards, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE,
		Size(50, 50), Size(500,500) );
	// cout << "Original detected dartboards" << endl;
	// printFaces(detected_dartboards);

	// Create the SOBEL kernel in 1D, Y is transpose
	Mat_<int> kernel(3,3);
	Mat_<int>kernelT(3,3);
	kernel << -1, 0, 1, -2, 0, 2, -1, 0, 1;
	kernelT << 1, 2, 1, 0, 0, 0, -1, -2, -1;

	// Sobel filter
	Mat xConvo, yConvo, mag, dir;
	sobel(frame_grey,kernel, xConvo);
	sobel(frame_grey, kernelT, yConvo);
	magDir( xConvo, yConvo, mag, dir);
	//imwrite( "x.jpg", xConvo );
	//imwrite( "y.jpg", yConvo );
	//imwrite( "Mag.jpg", mag );
	//imwrite( "Dir.jpg", dir );

	// Hough Circles
	int x = mag.rows;
	int y = mag.cols;
	int radius = min(x,y)/2;
	int sizes_circles[] = { x, y, radius };
	Mat accu_circles(3, sizes_circles, CV_32FC1, cv::Scalar(0));
	Mat points_circle;
	hough_circle( frame, mag, dir, accu_circles, points_circle );

	// Hough Lines
	Mat accu_clustered_lines;
	vector<int> points_lines = hough_line( frame , mag, dir);
	hough_clustered_lines(frame, points_lines, accu_clustered_lines);
	Mat points;
	hough_combine(points_circle, accu_clustered_lines, points);
	// Draw detected dartboards from cascade for comparison
	for (int i = 0; i < detected_dartboards.size(); i++){
		// Create a cropped image of each voilaJones detection
		// '/2' because points is half the size of the frame
		Mat detected_frame = points(Rect((detected_dartboards[i].x / 2)-1,(detected_dartboards[i].y / 2)-1,(detected_dartboards[i].width / 2)-1,(detected_dartboards[i].height / 2)-1));
 		bool detected = false;
 		int center[2] = {0,0};
 		hough_detect(detected_frame, &detected, center);
		if(detected) {
			int oldX = (detected_dartboards[i].x);
			int oldY = (detected_dartboards[i].y);
			new_detected_dartboards.push_back(detected_dartboards[i]);
			rectangle(frame,
								Point(oldX, oldY),
								Point(oldX + detected_dartboards[i].width,
											oldY + detected_dartboards[i].height),
								Scalar( 0, 255, 0 ), 2);

		}
	}
	// cout << "New Detected Dartboards" << endl;
	// printFaces(new_detected_dartboards);
	//Calculate detected dartboard iou
	for (int j = 0; j < truth_dartboards.size(); j++){
		for( int i = 0; i < new_detected_dartboards.size(); i++ ){
			float iou_result = iou(new_detected_dartboards[i], truth_dartboards[j]);
			// std::cout << "IOU for ground truth face " << truth_dartboards[j] << " with detected face " << detected_faces[i] << " = " << iou_result << std::endl;
			if (iou_result >= iou_threshold){
				true_positives++;
				//Prevents TP being higher than 1 for each face. Assumes that there are no faces inside faces. In which cases we would need to double break
				break;
			}
		}
	}
	false_positives += new_detected_dartboards.size()-true_positives;
	int real_pos = truth_dartboards.size();
	float f1 = f1_score(false_positives, true_positives, real_pos);
	float tpr;
	//Handling for div 0
	if (true_positives == 0) {
		tpr = 0;
	} else {
		tpr = true_positives/real_pos;
	}
	std::cout << "TPR " << tpr << std::endl;
	std::cout << "F1: " << f1 << "\n" << std::endl;
	for (int i = 0; i < truth_dartboards.size(); i++){
		rectangle(frame, Point(truth_dartboards[i].x, truth_dartboards[i].y), Point(truth_dartboards[i].x + truth_dartboards[i].width, truth_dartboards[i].y + truth_dartboards[i].height), Scalar( 0, 0, 255 ), 2);
	}
}

void printFaces(std::vector<Rect> faces){
	for (int i = 0; i < faces.size(); i++){
		std::cout << faces[i] << std::endl;
	}
}

int ***malloc3dArray(int dim1, int dim2, int dim3){
	int i, j, k;
	int ***array = (int ***) malloc(dim1 * sizeof(int **));
	for (i = 0; i < dim1; i++) {
		array[i] = (int **) malloc(dim2 * sizeof(int *));
		for (j = 0; j < dim2; j++) {
			array[i][j] = (int *) malloc(dim3 * sizeof(int));
		}
	}
	return array;
}



void normaliseMatrix( Mat matrix ) {
	double min, max;
	cv::minMaxLoc(matrix, &min, &max);
	for ( int i = 0; i < matrix.rows; i++ )
	{
		for( int j = 0; j < matrix.cols; j++ )
		{
			//Normalize calculation
			matrix.at<float>(i, j) = (((matrix.at<float>(i, j) - min) * 255.0) / (max - min));
		}
	}
}

float f1_score(float FalsePos, float TruePos, float RealPos){
	float precision;
	if (TruePos == 0) {
		precision = 0;
	} else {
		precision = TruePos/(TruePos+FalsePos);
	}
	float recall;
	if (TruePos == 0) {
		recall = 0;
	} else {
		recall = TruePos/(RealPos);
	}
	if (precision == 0 && recall == 0){
		return 0;
	} else {
		return 2*(precision*recall)/(precision+recall);
	}
}

//Using https://www.geeksforgeeks.org/csv-file-management-using-c/
//Can optimise by storing into an array
vector<Rect> ground_truth(int filenum){
	// std::cout << filenum << std::endl;
	// File pointer
    ifstream fin("dart.csv");
	int file2, count = 0;
	vector<string> row;
    string line, word, temp;
	vector<int> face_coords;
	vector<Rect> face_coords_set;
	while (getline(fin, line)) {
        row.clear();
        istringstream iss(line);
        while (getline(iss, word, ',')) {
            row.push_back(word);
        }
        file2 = stoi(row[0]);
        if (file2 == filenum) {
            count = 1;
			row.erase(row.begin());
			// std::cout << "row size = " << row.size() << std::endl;
			for (int i=0; i<row.size(); i=i+4){
				face_coords_set.push_back(Rect(stoi(row[i]),stoi(row[i+1]),stoi(row[i+2])-stoi(row[i]),stoi(row[i+3])-stoi(row[i+1])));
			}
			return face_coords_set;
            break;
        }
    }
    if (count == 0){
        // cout << "Record not found\n";

	}
	return face_coords_set;
}


Rect vect_to_rect(vector<int> vect){
	return Rect(vect[0], vect[1], vect[2]-vect[0], vect[3]-vect[1]);
}

float iou(Rect A, Rect B){
	// std::cout << "A " << A << std::endl;
	// std::cout << "B " << B << std::endl;
    Rect intersection = A&B;
	// std::cout << "inter " << intersection.area() << std::endl;
    float iou = (float)intersection.area() / ((float)A.area()+(float)B.area()-(float)intersection.area());
	// std::cout << "iou " << std::fixed << std::setprecision(5) << iou << std::endl;
    return iou;
}
