#include"utils_opencv.h"
#include <filesystem>
#include "utils_alpr_detect.h"
#include "../include/StatSommesX_Y_H_dbl.h"
#include "../include/utils_image_file.h"
#define NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE 36
#define MIN_WIDTH_FOR_A_LP 60
#define MIN_HEIGHT_FOR_A_LP 10
#define MAX_SLOPE 0.35
#define RACINE_3_SUR_2 0.85
#define RACINE_2_SUR_2 0.707
#define NB_MIN_PIXELS_IN_PLATE	1000
void indeces_tri(std::list<cv::Rect>& boxes, //  the boxes detected by nn detector, stored in a list of rectagles objects
	std::list<float>& ious)
{
	if (boxes.size() == ious.size() && ious.size()) {
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des listes pour trier (vides au debut puis se remplissant un par un, progressivement)
		std::list<cv::Rect> liste_tri_boxes;
		std::list<float> liste_tri_iou;
		while (!boxes.empty() && !ious.empty()) {
			float _iou = ious.front();
			std::list<float>::iterator it_tri_iou(liste_tri_iou.begin());
			std::list<cv::Rect>::iterator it_tri(liste_tri_boxes.begin());
			while (it_tri != liste_tri_boxes.end() && it_tri_iou != liste_tri_iou.end()) {
				if (_iou <= (*it_tri_iou)) break;
				else {
					it_tri_iou++;
					it_tri++;
				}
			}
			liste_tri_iou.splice(it_tri_iou, ious,
				ious.begin());
			liste_tri_boxes.splice(it_tri, boxes,
				boxes.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(boxes.empty() && ious.empty());
		std::list<float>::const_iterator it_verif(liste_tri_iou.begin());
		float _iou = -1.0f;
		while (it_verif != liste_tri_iou.end()) {
#ifdef _DEBUG
			assert((*it_verif) > -0.1f);
			assert((*it_verif) >= _iou);
#endif //_DEBUG
			_iou = (*it_verif);
			it_verif++;
		}
#endif //_DEBUG
		liste_tri_iou.swap(ious);
		liste_tri_boxes.swap(boxes);
	}
}
std::list<cv::Rect> fiter_out(
	const std::vector<cv::Rect>& true_boxes,  // the true boxes extracted from pascal voc xml file, as a list
	const std::list<cv::Rect>& detected_boxes, //  the boxes detected by nn detector, stored in a list of rectagles objects
	const std::list<float>& ious,
	std::list<float>& out_ious)
{
	std::list<cv::Rect> lboxes;
	vector_to_list(true_boxes, lboxes);
	return fiter_out(
		lboxes,  // the true boxes extracted from pascal voc xml file, as a list
		detected_boxes, //  the boxes detected by nn detector, stored in a list of rectagles objects
		ious,
		out_ious);
}
std::list<cv::Rect> fiter_out(
	const std::list<cv::Rect>& true_boxes,  // the true boxes extracted from pascal voc xml file, as a list
	const std::list<cv::Rect>& detected_boxes, //  the boxes detected by nn detector, stored in a list of rectagles objects
	const std::list<float>& ious,
	std::list<float>& out_ious)
{
	std::list<cv::Rect> return_list;
	if (detected_boxes.size() == ious.size() && true_boxes.size() && ious.size()) {
		std::list<cv::Rect> detected_boxes_copie = detected_boxes;  //  the boxes detected by nn detector, stored in a list of rectagles objects
		std::list<float>  ious_copie = ious;
		indeces_tri(detected_boxes_copie, //  the boxes detected by nn detector, stored in a list of rectagles objects
			ious_copie);
		const float inv_pow = 1.0f / static_cast<float>(true_boxes.size());
		float response = 1.0f;
		std::list<cv::Rect> true_boxes_copy = true_boxes;
		std::list<cv::Rect> detected_boxes_copy = detected_boxes;
		while (!detected_boxes_copy.empty() && !ious_copie.empty()) {
			float carac_iou = 0.0f;
			std::list<cv::Rect>::const_iterator it_true_boxes(true_boxes_copy.begin());
			while (it_true_boxes != true_boxes_copy.end()) {
				float current_iou = iou(detected_boxes_copy.back(), *it_true_boxes);
				if (carac_iou < current_iou) {
					carac_iou = current_iou;
				}
				it_true_boxes++;
			}
			if (carac_iou < 0.1f) {
				out_ious.push_back(ious_copie.back());
				return_list.push_back(detected_boxes_copy.back());
			}
			ious_copie.pop_back();
			detected_boxes_copy.pop_back();
		}
	}
	return return_list;
}
float iou(
	const std::list<cv::Rect>& true_boxes,  // the true boxes extracted from pascal voc xml file, as a list
	const std::list<cv::Rect>& detected_boxes //  the boxes detected by nn detector, stored in a list of rectagles objects
)
{
	if (detected_boxes.size() == true_boxes.size() && true_boxes.size()) {
		const float inv_pow = 1.0f / static_cast<float>(true_boxes.size());
		float response = 1.0f;
		std::list<cv::Rect> true_boxes_copy = true_boxes;
		std::list<cv::Rect> detected_boxes_copy = detected_boxes;
		while (!detected_boxes_copy.empty()) {
			float carac_iou = 0.0f;
			std::list<cv::Rect>::const_iterator it_true_boxes(true_boxes_copy.begin());
			std::list<cv::Rect>::const_iterator it_erase(it_true_boxes);
			while (it_true_boxes != true_boxes_copy.end()) {
				float current_iou = iou(detected_boxes_copy.front(), *it_true_boxes);
				if (carac_iou < current_iou) {
					carac_iou = current_iou;
					it_erase = it_true_boxes;
				}
				it_true_boxes++;
			}
			response *= carac_iou;
			true_boxes_copy.erase(it_erase);
			detected_boxes_copy.pop_front();
		}
		return static_cast<float>(pow(response, inv_pow));
	}
	return 0.0f;
}
float iou(
	const std::list<cv::Rect>& boxes,  // the true boxes extracted from pascal voc xml file, as a list
	const cv::Rect& box //  the boxes detected by nn detector, stored in a list of rectagles objects
)
{
	if (boxes.size()) {
		float carac_iou = 0.0f;
		std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
		while (it_boxes != boxes.end()) {
			float current_iou = iou(box, *it_boxes);
			if (carac_iou < current_iou) {
				carac_iou = current_iou;
			}
			it_boxes++;
		}
		return carac_iou;
	}
	else return 0.0f;
}
//cette fonction trie la liste de gauche a droite 
std::list<cv::Rect> sort_from_left_to_right(const std::list<cv::Rect>& boxes) {
	std::list<cv::Rect> copie = boxes;
	std::list<cv::Rect> l_tri_left;
	while (!copie.empty()) {
		int left_courant(copie.front().x);
		std::list<cv::Rect>::iterator it = l_tri_left.begin();
		while (it != l_tri_left.end()) {
			//if (left_courant <= it->x) break;
			if (is_on_the_left(copie.front(), *it))
				break;
			else it++;
		}
		l_tri_left.splice(it, copie, copie.begin());
	}
#ifdef _DEBUG
	assert(debug_left(l_tri_left));
	assert(copie.empty());
#endif	 //_DEBUG
	return l_tri_left;
}
//cette fonction trie la liste de gauche a droite 
//returns a list of characters of lp sorted from left to right
std::list<cv::Rect> sort_from_left_to_right(const std::list<cv::Rect>& boxes, const std::list<int>& classes, std::list<int>& sorted_classes,
	const int number_of_characters_latin_numberplate//, const int index_first_mark_model
) {
	//const int NUMBER_OF_COUNTRIES = index_first_mark_model - number_of_characters_latin_numberplate;
	//const int classId_last_country = number_of_characters_latin_numberplate + NUMBER_OF_COUNTRIES - 1;
	std::list<cv::Rect>::const_iterator it_box(boxes.begin());
	std::list<int>::const_iterator it_classes(classes.begin());
	std::list<cv::Rect> copie;
	while (it_box != boxes.end() && it_classes != classes.end()
		) {
		//given the index of a bounding box, we can predict if this box is a single character or if it represents the license plate area or if it is the roi of an entire vehicle
		//single character--> returns 1
		//license plate--> returns 2
		//negative index--> returns 0 must be an error
		//classId_last_country : is the class index of the last country in the list of detected classes.
		if (is_this_box_a_character(*it_classes, number_of_characters_latin_numberplate) == 1)//single character--> returns 1
		{
			copie.push_back(*it_box);
			sorted_classes.push_back(*it_classes);
		}
		it_classes++;
		it_box++;
	}
	sort_from_left_to_right(copie, sorted_classes);
	return copie;
}
			//cette fonction trie la liste de gauche a droite 
bool is_on_the_left(const cv::Rect& box1, const cv::Rect& box2)
{
	if (box1.x < box2.x) return true;
	else if (box1.x > box2.x) return false;
	else {
		if (box1.x + box1.width < box2.x + box2.width) return true;
		else if (box1.x + box1.width > box2.x + box2.width) return false;
		else {
			if (box1.y < box2.y) return true;
			else if (box1.y > box2.y) return false;
			else {
				if (box1.y + box1.height < box2.y + box2.height) return true;
				else if (box1.y + box1.height > box2.y + box2.height) return false;
				else return false;
			}
		}
	}
}
void sort_from_left_to_right(std::list<cv::Rect>& boxes, std::list<int>& classIds_)
{
	std::list<cv::Rect> l_tri_left;
	std::list<int> classIds_tri_left;
	while (!boxes.empty() && !classIds_.empty()) {
		//int left_courant(boxes.front().x);
		std::list<cv::Rect>::iterator it = l_tri_left.begin();
		std::list<int>::iterator it_classIds = classIds_tri_left.begin();
		while (it != l_tri_left.end() &&
			it_classIds != classIds_tri_left.end()) {
			if(is_on_the_left(boxes.front(), *it))
			//if (left_courant < it->x || (left_courant == it->x && left_courant + boxes.front().width < it->x + it->width)) 
				break;
			else {
				it++;  it_classIds++;
			}
		}
		l_tri_left.splice(it, boxes, boxes.begin());
		classIds_tri_left.splice(it_classIds, classIds_, classIds_.begin());
	}
#ifdef _DEBUG
	assert(boxes.empty());
	assert(debug_left(l_tri_left));
	//VERIFICATION DU TRI CROISSANT
#endif	  //_DEBUG
	l_tri_left.swap(boxes);
	classIds_tri_left.swap(classIds_);
}
//cette fonction trie la liste de gauche a droite 
void sort_from_left_to_right(std::list<cv::Rect>& boxes, std::list<float>& confidences)
{
	std::list<cv::Rect> l_tri_left;
	std::list<float> confidences_tri_left;
	while (!boxes.empty() && !confidences.empty()) {
		//int left_courant(boxes.front().x);
		std::list<cv::Rect>::iterator it = l_tri_left.begin();
		std::list<float>::iterator it_confidences = confidences_tri_left.begin();
		while (it != l_tri_left.end() && it_confidences != confidences_tri_left.end()) {
			//if (left_courant <= it->x)
			if (is_on_the_left(boxes.front(), *it))
				break;
			else {
				it++; it_confidences++;
			}
		}
		l_tri_left.splice(it, boxes, boxes.begin());
		confidences_tri_left.splice(it_confidences, confidences, confidences.begin());
	}
#ifdef _DEBUG
	assert(boxes.empty());
	//VERIFICATION DU TRI CROISSANT
	assert(debug_left(l_tri_left));
#endif	  //_DEBUG
	l_tri_left.swap(boxes);
	confidences_tri_left.swap(confidences);
}
bool is_in_rect(const cv::Rect& box, const cv::Rect& rect_im)
{
#ifdef _DEBUG
	/*
	assert(!(box.x<rect_im.x || box.y<rect_im.y
		|| box.x + box.width > rect_im.x + rect_im.width
		|| box.y + box.height> rect_im.y + rect_im.height));*/
#endif //_DEBUG
	return (!(box.x<rect_im.x || box.y<rect_im.y
		|| box.x + box.width > rect_im.x + rect_im.width
		|| box.y + box.height> rect_im.y + rect_im.height));
}
bool is_in_rect(const cv::Point& pt, const cv::Rect& rect_im)
{
	return (!(pt.x < rect_im.x || pt.y < rect_im.y || pt.x >= rect_im.x + rect_im.width || pt.y >= rect_im.y + rect_im.height));
}
//for each box in the container, check that it is nearly entirely contained in the second argument
bool is_in_rect_if(const std::list<cv::Rect>& boxes, const cv::Rect& rect_im)
{
	std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
	while (it_boxes != boxes.end()) {//return true if the intersection of the first argument box and the second has an area that is at least 90% of the first argument box (which means box is nearly entirely in the second argument)
		if (!is_in_rect_if(*it_boxes, rect_im))
			return false;
		else it_boxes++;
	}
	return true;
}
//for each box in the container, check that it is nearly entirely contained in the second argument
bool is_in_rect_if(const std::vector<cv::Rect>& boxes, const cv::Rect& rect_im)
{
	std::vector<cv::Rect>::const_iterator it_boxes(boxes.begin());
	while (it_boxes != boxes.end()) {//return true if the intersection of the first argument box and the second has an area that is at least 90% of the first argument box (which means box is nearly entirely in the second argument)
		if (!is_in_rect_if(*it_boxes, rect_im))
			return false;
		else it_boxes++;
	}
	return true;
}
bool is_in_rect(const std::list<cv::Rect>& boxes, const cv::Rect& rect_im)
{
	std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
	while (it_boxes != boxes.end()) {
		if (!is_in_rect(*it_boxes, rect_im))
			return false;
		else it_boxes++;
	}
	return true;
}
void get_mean_std(const cv::Mat& frame, const cv::Rect& box, float& mean, float& standard_deviation)
{
	if (box.x >= 0 && box.y >= 0 && box.width >= 0 && box.height >= 0 && box.x + box.width <= frame.cols && box.y + box.height <= frame.rows) {
		cv::Rect roi(box.x + box.width / 2, box.y, 1, box.height);
		cv::Mat tmp(frame(roi));
		cv::Scalar mean_, dev_;
		cv::meanStdDev(tmp, mean_, dev_);
		mean = static_cast<float>(mean_[0]);
		standard_deviation = static_cast<float>(dev_[0]);
	}
	else {
		mean = 0.0f; standard_deviation = 0.0f;
	}
}
bool get_upperand_lower_lines
(const std::list<cv::Rect>& boxes, C_Line& line_sup, C_Line& line_inf)
{
	if (boxes.size() > 1) {
		C_SumsRegLineXYHDbl return_value;
		//initialisation des sommes partielles
		std::list<cv::Rect>::const_iterator it(boxes.begin());
		while (it != boxes.end()) {
			//update des sommes
			int somme(it->x + it->x + it->width);
			if (((somme >> 1) << 1) < somme) {
#ifdef _DEBUG
				assert(somme - ((somme >> 1) << 1) == 1);
#endif //_DEBUG
				somme = (somme >> 1);
				cv::Point2f Center(somme + 0.5f, intToFloat(it->y));
				return_value += Center;//le centre du bord sup de la region *it.
			}
			else {
				somme = (somme >> 1);
				cv::Point2f Center(intToFloat(somme), intToFloat(it->y));
				return_value += Center;//le centre du bord sup de la region *it.
			}
			it++;
		}
#ifdef _DEBUG
		//initialisation des sommes partielles
		float somme_x = 0.0f;
		float somme_y = 0.0f;
		float produit_xy = 0.0f;
		float somme_carre_x = 0.0f;
		std::list<cv::Rect>::const_iterator it_verif = boxes.begin();
		while (it_verif != boxes.end()) {
			//le centre du bord sup de la region *it.
			cv::Point2f Center;
			Center.y = intToFloat(it_verif->y);
			int somme(it_verif->x + it_verif->x + it_verif->width);
			if (((somme >> 1) << 1) < somme) {
#ifdef _DEBUG
				assert(somme - ((somme >> 1) << 1) == 1);
#endif //_DEBUG
				somme = (somme >> 1);
				Center.x = somme + 0.5f;
			}
			else {
				somme = (somme >> 1);
				Center.x = intToFloat(somme);
			}
			//update des sommes
			somme_x += Center.x;
			somme_y += Center.y;
			produit_xy += Center.x * Center.y;
			somme_carre_x += Center.x * Center.x;
			it_verif++;
		}
		return_value.somme_hauteurs = 1;
#ifdef _DEBUG
		assert(return_value.debug(somme_x, somme_y, produit_xy, somme_carre_x));
#endif //_DEBUG
#endif
		line_sup = return_value.regression_line(boxes.size());
		return_value.clear();
		it = boxes.begin();
		while (it != boxes.end()) {
			//update des sommes
			int somme(it->x + it->x + it->width);
			if (((somme >> 1) << 1) < somme) {
#ifdef _DEBUG
				assert(somme - ((somme >> 1) << 1) == 1);
#endif //_DEBUG
				somme = (somme >> 1);
				cv::Point2f Center(intToFloat(somme) + 0.5f, intToFloat(it->y + it->height));
				return_value += Center;//le centre du bord sup de la region *it.
			}
			else {
				somme = (somme >> 1);
				cv::Point2f Center(intToFloat(somme), intToFloat(it->y + it->height));
				return_value += Center;//le centre du bord sup de la region *it.
			}
			it++;
		}
		line_inf = return_value.regression_line(boxes.size());
		return (line_inf.a > -MAX_SLOPE && line_inf.a<MAX_SLOPE&& line_sup.a>-MAX_SLOPE && line_sup.a < MAX_SLOPE);
	}
	else {
		line_sup.a = 0.0f;
		line_sup.b = 0.0f;
		line_inf.a = 0.0f;
		line_inf.b = 0.0f;
		return false;
	}
}
cv::Rect  get_global_rect(const cv::Point& bottom_right,
	const 	cv::Point& bottom_left, const cv::Point& top_right
	, const cv::Point& top_left)
{
	int left = top_left.x;
	if (left > bottom_left.x)left = bottom_left.x;
	int right = top_right.x;
	if (right < bottom_right.x)right = bottom_right.x;
	int bottom = bottom_right.y;
	if (bottom < bottom_left.y)bottom = bottom_left.y;
	int top = top_right.y;
	if (top > top_left.y)top = top_left.y;
	return cv::Rect(left, top, right - left, bottom - top);
}
void vector_to_list(
	const std::vector<cv::Rect>& lboxes,
	std::list<cv::Rect>& boxes) {
	std::copy(lboxes.begin(), lboxes.end(), std::back_inserter(boxes));
}
//cette fonction retourne le rect englobant la collection
//gets the reunion of all the boxes
cv::Rect get_global_rect(const std::list<cv::Rect>& l)
{
	if (l.empty()) return cv::Rect(0, 0, 0, 0);
	else {
		std::list<cv::Rect>::const_iterator it(l.begin());
		cv::Rect rect(*it);
		it++;
		while (it != l.end()) {
			if (rect.x > it->x) {
				rect.width += rect.x - it->x; rect.x = it->x;
			}
			if (rect.y > it->y) {
				rect.height += rect.y - it->y; rect.y = it->y;
			}
			if (rect.x + rect.width < it->x + it->width) rect.width = it->x + it->width - rect.x;
			if (rect.y + rect.height < it->y + it->height) rect.height = it->y + it->height - rect.y;
			it++;
		}
		return rect;
	}
}
//cette fonction retourne le rect englobant la collection
//gets the reunion of all the boxes
cv::Rect get_global_rect(const std::vector<cv::Rect>& l)
{
	if (l.empty()) return cv::Rect(0, 0, 0, 0);
	else {
		std::vector<cv::Rect>::const_iterator it(l.begin());
		cv::Rect rect(*it);
		it++;
		while (it != l.end()) {
			if (rect.x > it->x) {
				rect.width += rect.x - it->x; rect.x = it->x;
			}
			if (rect.y > it->y) {
				rect.height += rect.y - it->y; rect.y = it->y;
			}
			if (rect.x + rect.width < it->x + it->width) rect.width = it->x + it->width - rect.x;
			if (rect.y + rect.height < it->y + it->height) rect.height = it->y + it->height - rect.y;
			it++;
		}
		return rect;
	}
}
cv::Rect array2CRect(const cv::Point& p0, const cv::Point& p1,
	const cv::Point& p2, const cv::Point& p3,
	cv::Point& top_left,
	cv::Point& top_right,
	cv::Point& bottom_right,
	cv::Point& bottom_left
)
{
	if (p0.x == SHRT_MIN) {
#ifdef _DEBUG
		assert(p0.x == SHRT_MIN && p0.y == SHRT_MIN &&
			p1.x == SHRT_MIN && p1.y == SHRT_MIN &&
			p2.x == SHRT_MIN && p2.y == SHRT_MIN &&
			p3.x == SHRT_MIN && p3.y == SHRT_MIN);
#endif //_DEBUG
		return cv::Rect(-1, -1, -1, -1);
	}
	else {
#ifdef _DEBUG
		assert(p0.x >= 0 && p0.y >= 0 &&
			p1.x >= 0 && p1.y >= 0 &&
			p2.x >= 0 && p2.y >= 0 &&
			p3.x >= 0 && p3.y >= 0);
#endif //_DEBUG
		int resultat[4][2];
		resultat[0][0] = p0.x;
		resultat[0][1] = p0.y;
		resultat[1][0] = p1.x;
		resultat[1][1] = p1.y;
		resultat[2][0] = p2.x;
		resultat[2][1] = p2.y;
		resultat[3][0] = p3.x;
		resultat[3][1] = p3.y;
		//trouver top_left
		//c'est le point dont la somme des corrdonnes est la plus petite
		int somme_coord_min = SHRT_MAX;
		int somme_coord_max = SHRT_MIN;
		int index_top_left(-1);
		int index_bottom_right(-1);
		unsigned int i;
		for (i = 0; i < 4; i++)
		{
			const int somme_coord = resultat[i][0] + resultat[i][1];
			if (somme_coord < somme_coord_min) {
				somme_coord_min = somme_coord;
				index_top_left = i;
			}
			if (somme_coord > somme_coord_max) {
				somme_coord_max = somme_coord;
				index_bottom_right = i;
			}
		}
		top_left.x = resultat[index_top_left][0];
		top_left.y = resultat[index_top_left][1];
		bottom_right.x = resultat[index_bottom_right][0];
		bottom_right.y = resultat[index_bottom_right][1];
		//trouver top_right
		int right(SHRT_MIN);
		int index_top_right(-1);
		for (i = 0; i < 4; i++)
		{
			if (i != index_top_left && i != index_bottom_right) {
				if (resultat[i][0] - resultat[i][1] >= right) {
					right = resultat[i][0] - resultat[i][1];
					top_right.x = resultat[i][0];
					top_right.y = resultat[i][1];
					index_top_right = i;
				}
			}
		}
		//trouver bottom_left
		int left(resultat[index_top_left][0]);
		int index_bottom_left(-1);
		for (i = 0; i < 4; i++)
		{
			if (i != index_top_left && i != index_bottom_right && i != index_top_right) {
				index_bottom_left = i;
				bottom_left.x = resultat[i][0];
				bottom_left.y = resultat[i][1];
				break;
			}
		}
		int gauche = resultat[0][0];
		int droite = resultat[0][0];
		int haut = resultat[0][1];
		int bas = resultat[0][1];
		for (i = 1; i < 4; i++)
		{
#ifdef _DEBUG
			assert(gauche <= droite);
#endif //_DEBUG
			if (resultat[i][0] < gauche)
				gauche = resultat[i][0];
			else if (resultat[i][0] > droite)
				droite = resultat[i][0];
#ifdef _DEBUG
			assert(haut <= bas);
#endif //_DEBUG
			if (resultat[i][1] < haut)
				haut = resultat[i][1];
			else if (resultat[i][1] > bas)
				bas = resultat[i][1];
		}
		return cv::Rect(gauche, haut, droite - gauche + 1, bas - haut + 1);
	}
}
std::vector<cv::Point2f> get_points(const CvSeq* polygone)
{
	std::vector<cv::Point2f> vertVect;
	for (int i = 0; i < polygone->total; i++) {
		CvPoint* pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
		vertVect.push_back(cv::Point2f(pt->x, pt->y));
	}
	return vertVect;
}
cv::RotatedRect get_rect(const CvSeq* polygone)
{
	std::vector<cv::Point2f> vertVect(get_points(polygone));
	cv::RotatedRect calculatedRect = cv::minAreaRect(vertVect);
	return calculatedRect;
}
void swap(CvSeq* polygone, const cv::RotatedRect& RotatedRect)
{
	int total = polygone->total;
	if (total >= 4) {
		cv::Point2f vertices[4];
		RotatedRect.points(vertices);
		std::list<cv::Point2f> list_sommets;
		for (int j = 0; j < 4; j++) {
			list_sommets.push_back(vertices[j]);
		}
		std::list<cv::Point2f> liste_sommets_tri;
		list_sommets.swap(liste_sommets_tri);
		while (!liste_sommets_tri.empty()) {
			std::list<cv::Point2f>::iterator it_sommets(liste_sommets_tri.begin());
			float sum = it_sommets->x + it_sommets->y;
			std::list<cv::Point2f>::iterator it = list_sommets.begin();
			while (it != list_sommets.end()) {
				if (sum <= it->x + it->y) break;
				it++;
			}
			list_sommets.splice(it, liste_sommets_tri, it_sommets);
		}
		cv::Point2f top_left;
		cv::Point2f top_right;
		cv::Point2f bottom_left;
		cv::Point2f bottom_right;
		std::list<cv::Point2f>::iterator it = list_sommets.begin();
		if (it != list_sommets.end()) {
			top_left = *it;
			it++;
		}
		if (it != list_sommets.end()) {
			bottom_left = *it;
			it++;
		}
		if (it != list_sommets.end()) {
			top_right = *it;
			it++;
		}
		if (it != list_sommets.end()) {
			bottom_right = *it;
		}
		float top_left_distance = FLT_MAX;
		float top_right_distance = FLT_MAX;
		float bottom_left_distance = FLT_MAX;
		float bottom_right_distance = FLT_MAX;
		cv::Point2f new_top_left;
		cv::Point2f new_top_right;
		cv::Point2f new_bottom_left;
		cv::Point2f new_bottom_right;
		for (int i = 0; i < total; i++) {
			CvPoint* pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
			float current_distance_top_left_distance
				= (pt->x - top_left.x) * (pt->x - top_left.x) +
				(pt->y - top_left.y) * (pt->y - top_left.y);
			if (top_left_distance > current_distance_top_left_distance) {
				new_top_left = *pt;
			}
			float current_distance_bottom_left_distance
				= (pt->x - bottom_left.x) * (pt->x - bottom_left.x) +
				(pt->y - bottom_left.y) * (pt->y - bottom_left.y);
			if (bottom_left_distance > current_distance_bottom_left_distance) {
				new_bottom_left = *pt;
			}
			float current_distance_top_right_distance
				= (pt->x - top_right.x) * (pt->x - top_right.x) +
				(pt->y - top_right.y) * (pt->y - top_right.y);
			if (top_right_distance > current_distance_top_right_distance) {
				new_top_right = *pt;
			}
			float current_distance_bottom_right_distance
				= (pt->x - bottom_right.x) * (pt->x - bottom_right.x) +
				(pt->y - bottom_right.y) * (pt->y - bottom_right.y);
			if (bottom_right_distance > current_distance_bottom_right_distance) {
				new_bottom_right = *pt;
			}
		}
		for (int i = 0; i < total - 4; i++) {
			cvSeqRemove(polygone, 0);
		}
		int i = 0;
		CvPoint* pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
		pt->x = new_top_left.x;
		pt->y = new_top_left.y;
		i++;
		pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
		pt->x = new_top_right.x;
		pt->y = new_top_right.y;
		i++;
		pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
		pt->x = new_bottom_right.x;
		pt->y = new_bottom_right.y;
		i++;
		pt = CV_GET_SEQ_ELEM(CvPoint, polygone, i);
		pt->x = new_bottom_left.x;
		pt->y = new_bottom_left.y;
	}
}
void init_contours_weight(const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min,
	const int widthimage, const int heightimage,
	CvMemStorage* storage, CvSeq* contours,
	CvSeq* squares,
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus,
	CvSeq* note_rapport_long_cotes_opposes,//means largeur_sur_hauteur
	CvSeq* note_plus_petit_sinus,
	CvSeq* note_plus_grand_sinus,
	CvSeq* notesym,
	float cvApproxPoly_coeff)
{
	int i, index_element_a_zapper;
	//cvApproxPoly_coeff regulation
	if (cvApproxPoly_coeff < .0) cvApproxPoly_coeff = -cvApproxPoly_coeff;
	if (cvApproxPoly_coeff < .0 || cvApproxPoly_coeff>1.0f) cvApproxPoly_coeff = 0.05f;
	CvPoint pt[4];
	const int nb_max_contours = 20;//11
	// teste chaque contour
	while (contours)
	{
		// approximate contour with accuracy proportional
		// to the contour perimeter
		CvSeq* polygone = cvApproxPoly(contours, sizeof(CvContour), storage,
			CV_POLY_APPROX_DP, (cvContourPerimeter(contours
			)) * cvApproxPoly_coeff, 0);
		// le 0.02 est un indice d'approximation. il n'est pas etre judicieux 
		// de le modifier, mieux vaut utiliser la methode transformant toutes
		// sortes de polygones (pentagones ou +) en quadrilatees
		// si il y a max 10 cee (cette valeur est arbitraire)
		// et que le polygone est fermeet a une aire suffisante pour ere inteessant
		if (polygone->total < nb_max_contours && polygone->total>3)
		{
			//The function cvCheckContourConvexity tests whether the input 
			//contour is convex or not. The contour must be simple, i.e. 
			//without self-intersections. 
			double area = fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ)));
			if (area > NB_MIN_PIXELS_IN_PLATE)
			{
				if (
					cvCheckContourConvexity(polygone)
					) {//a remttre
					//tant qu'il y a plus de 4 cee
					while (polygone->total > 4)
					{
						index_element_a_zapper = zappeUnPoint(polygone, storage);//on regarde si un point est inutile
						if (index_element_a_zapper != -1)//si c'est non on arree
						{
							cvSeqRemove(polygone, index_element_a_zapper);
						}
						else//si c'est oui on retire ce point inutile
						{
							cv::RotatedRect RotatedRect(get_rect(polygone));
							double rotated_area = RotatedRect.size.area();
							int angle_degree = abs(static_cast<int>(RotatedRect.angle));
							div_t divresult;
							divresult = div(angle_degree, 90);
							cv::Rect boundingrect(RotatedRect.boundingRect());
							if (rotated_area * 0.85 < area && divresult.rem < 25
								&& boundingrect.width>2.5 * boundingrect.height)
							{
								swap(polygone, RotatedRect);
							}
							else//si c'est oui on retire ce point inutile
							{
								cvClearSeq(polygone);
							}
						}
					}
				}
				else//si c'est oui on retire ce point inutile
				{
					cv::RotatedRect RotatedRect(get_rect(polygone));
					double rotated_area = RotatedRect.size.area();
					int angle_degree = abs(static_cast<int>(RotatedRect.angle));
					div_t divresult;
					divresult = div(angle_degree, 90);
					cv::Rect boundingrect(RotatedRect.boundingRect());
					if (rotated_area * 0.85 < area && divresult.rem < 25
						&& boundingrect.width>2.5 * boundingrect.height)
					{
						swap(polygone, RotatedRect);
					}
					else//si c'est oui on retire ce point inutile
					{
						cvClearSeq(polygone);
					}
					//cvClearSeq(polygone); 
				}
			}
			else//si c'est oui on retire ce point inutile
			{
				cvClearSeq(polygone);
			}
		}
		// square contours should have 4 vertices after approximation
		// relatively large area (to filter out noisy contours)
		// and be convex.
		// Note: absolute value of an area is used because
		// area may be positive or negative - in accordance with the
		// contour orientation
		// si le quadrilatee obtenu a 4 cee
		// et que les conditions preeemment eonces sont veifies
		if (polygone)
		{
			if (polygone->total == 4)
			{
				if ( //The function cvCheckContourConvexity tests whether the input 
					//contour is convex or not. The contour must be simple, i.e. 
					//without self-intersections.
					cvCheckContourConvexity(polygone) &&
					fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ))) > NB_MIN_PIXELS_IN_PLATE
					)
				{
					float plus_grand_cosinus = -1.0;
					float t, plus_petit_sinus = 1.0, plus_grand_sinus = -1.0, //sin between two opposite sides of the lp //the greatest sin between two opposite sides of the lp
						sym,//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						petit//le min de la somme de deux cote oppose
						;
					float rapport_long_cotes_opposes = -1.0;//means largeur_sur_hauteur
					CvPoint p;
					float dist_plus_grand_cote = 0.0f;
					float cos_plus_grand_cote, anglePetitCote;//=cos between width and horizontal
					CvPoint p1;
					bool colleAuxBords = false;
					//************************************************
					//   CALC DES GRANDEURS GEO POUR UNE PLAQUE
					//************************************************
					for (i = 0; i < 5 && !colleAuxBords; i++)
					{
						// store all the contour vertices in the buffer
						// (pt sert estocker le quadrilatee eudie
						// afin de simplifier les expressions)
						pt[i & 3] = *(CvPoint*)cvGetSeqElemLPR(polygone, i, 0);
						//colleAuxBords&=(pt)&&()
						//epartir de i = 2 on peut faire un angle
						if (i >= 2)
						{
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							t = fabsf(cosine(pt[i & 3], pt[(i - 2) & 3], pt[(i - 1) & 3]));
							if (plus_grand_cosinus < t) plus_grand_cosinus = t;
							//plus_grand_cosinus = MAX( plus_grand_cosinus, t );// on garde le + grand cos d'angle du quadrilatee
						}
						// on regarde si le + grand ceeforme 1 angle
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 1)
						{
							//  on regarde si on a pas tout simplement detecte toute l'image
							// c a d si tous les points ne seraient pas dans les coins de l'image
							colleAuxBords = colleAuxBords || (pt[i & 3].x < dist_min_bord) || (pt[i & 3].x > widthimage - dist_min_bord) ||
								(pt[i & 3].y < dist_min_bord) || (pt[i & 3].y > heightimage - dist_min_bord);
							if (dist(pt[i & 3], pt[(i - 1) & 3]) > dist_plus_grand_cote)
							{
								p1.y = pt[i & 3].y;
								p1.x = pt[(i - 1) & 3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								cos_plus_grand_cote = fabsf(cosine(p1, pt[(i - 1) & 3], pt[i & 3]));//pt[(i - 1) & 3]->pt[i & 3]=WIDTH //=cos between width and horizontal
								dist_plus_grand_cote = dist(pt[i & 3], pt[(i - 1) & 3]);
							}
						}
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 3)
						{
							// on garde le + petit sinus d'angle entre 2 cotes opposes
							p.x = pt[(i - 3) & 3].x - pt[(i - 2) & 3].x + pt[(i - 1) & 3].x;
							p.y = pt[(i - 3) & 3].y - pt[(i - 2) & 3].y + pt[(i - 1) & 3].y;
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							float cosinus_courant(cosine(pt[i & 3], p, pt[(i - 1) & 3]));
#ifdef _DEBUG
							assert(cosinus_courant > -1.0 - FLT_EPSILON &&
								cosinus_courant < 1.0f + FLT_EPSILON);
#endif //_DEBUG
							t = sqrtf(1 - (cosinus_courant * cosinus_courant));
							if (plus_petit_sinus > t)plus_petit_sinus = t;//sin between two opposite sides of the lp
							//plus_petit_sinus le +petit sinus (trapee)
							if (plus_grand_sinus < t) plus_grand_sinus = t;//the greatest sin between two opposite sides of the lp
							//plus_grand_sinus le +grand sinus (paralleogramme)
						}
						if (i == 4)
						{
							float d3 = dist(pt[1], pt[0]);
							float d0 = dist(pt[0], pt[3]);
							float d1 = dist(pt[3], pt[2]);
							float d2 = dist(pt[2], pt[1]);
							///verifie le repport des longueurs du quadrilatee
							if ((d0 + d2) > (d1 + d3)) rapport_long_cotes_opposes = (d0 + d2) / (d1 + d3);//means largeur_sur_hauteur
							else rapport_long_cotes_opposes = (d1 + d3) / (d0 + d2);
							//sym mesure le plus petit eart en proportions entre 2 cotes opposes
							sym = MAX(fabsf(d0 - d2) / (d0 + d2),
								fabsf(d1 - d3) / (d1 + d3));//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
							// on utilise petit pour mettre la + petite somme de longueurs de 2 cotes opposes
							// qui doit ere au moins de 36 (soit 2*18)
							petit = MIN((d0 + d2), (d1 + d3));//means hauteur
							//on verifie si les 2 plus petits cote (en fait le plus petit cotedes 2 derniers controles et celui lui faisant face)
							// sont suffisament verticaux
							if (d0 < d1)//d0 and d2 are height and d1 , d3 are width : pt[3]->pt[2]= WIDTH , pt[0]->pt[3]=HEIGHT
							{
								p1.y = pt[0].y;
								p1.x = pt[3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[0], pt[3]));//pt[0]->pt[3]=HEIGHT. p1->pt[3] =  cos of angle betwween vertical and  pt[0]->pt[3]=HEIGHT
								p1.y = pt[2].y;
								p1.x = pt[1].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[2], pt[1])));
							}
							else
							{
								p1.y = pt[3].y;
								p1.x = pt[2].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[3], pt[2]));
								p1.y = pt[1].y;
								p1.x = pt[0].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[1], pt[0])));
							}
							anglePetitCote = sqrtf(1.0f - anglePetitCote * anglePetitCote);
						}
					}
#ifdef _DEBUG
					assert(rapport_long_cotes_opposes >= 1.0 || rapport_long_cotes_opposes < -0.5f);//means largeur_sur_hauteur
#endif //_DEBUG
					cv::Point top_left, top_right, bottom_right, bottom_left;
					cv::Point P0(pt[0].x, pt[0].y);
					cv::Point P1(pt[1].x, pt[1].y);
					cv::Point P2(pt[2].x, pt[2].y);
					cv::Point P3(pt[3].x, pt[3].y);
					// on n'applique une note que si le quadrilatee semble inteessant
					// (pas de forme aberrante)
					if ((rapport_long_cotes_opposes > rapport_largeur_sur_hauteur_min)//means largeur_sur_hauteur always true
						&& ((rapport_long_cotes_opposes < 10) && (rapport_long_cotes_opposes > 0.164))//(rapport_long_cotes_opposes > 0.164) always true since (rapport_long_cotes_opposes >=1)
						//(rapport_long_cotes_opposes < 10) //largeur = 7* hauteur 
						&& (plus_petit_sinus < 0.5f)//sin between two opposite sides of the lp
						&& (plus_grand_sinus < 0.8f)//the greatest sin between two opposite sides of the lp
						&& (sym < 0.5f)//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//le min de la somme de deux cote oppose
						&& (petit > (hauteur_plaque_min << 1))//means hauteur
						&& (cos_plus_grand_cote > RACINE_2_SUR_2)//=cos between width and horizontal so this means that the width must not be inclined of more than 45 degrees 
						&& !colleAuxBords
						&& (anglePetitCote < 0.3)
						&&
						//must be convex quadrilatere
						get_corners(P0, P1, P2, P3,
							top_left, top_right, bottom_right, bottom_left)
						)
						//FILTRE
					{
						// on stocke les quadrilatees
						for (i = 0; i < 4; i++)
						{
							cvSeqPush(squares, cvGetSeqElemLPR(polygone, i, 0));
						}
						// on stocke les notes correspondantes
						cvSeqPush(note_plus_grand_cosinus, &plus_grand_cosinus);
#ifdef _DEBUG
						assert(plus_grand_cosinus > -1.0 - FLT_EPSILON
							&& plus_grand_cosinus < 1.0f + FLT_EPSILON);
						assert(rapport_long_cotes_opposes >= 1.0f);//means largeur_sur_hauteur always true
#endif //_DEBUG
						rapport_long_cotes_opposes = fabsf(rapport_long_cotes_opposes - 5.04f);//means largeur_sur_hauteur always true 5.04 ?????????????
						cvSeqPush(note_rapport_long_cotes_opposes, &rapport_long_cotes_opposes);
#ifdef _DEBUG
						assert(plus_petit_sinus > -1.0 - FLT_EPSILON &&//sin between two opposite sides of the lp
							plus_petit_sinus < 1.0f + FLT_EPSILON);
#endif //_DEBUG
						cvSeqPush(note_plus_petit_sinus, &plus_petit_sinus);//sin between two opposite sides of the lp
						cvSeqPush(note_plus_grand_sinus, &plus_grand_sinus);//the greatest sin between two opposite sides of the lp
						cvSeqPush(notesym, &sym);//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//NOTES
					}
				}
			}
		}
		// on passe au suivant
		contours = contours->h_next;
	}
	/*seqence stock�e ds storage donc m�moire lib�r�e avec storage
	if(contours)
	cvClearSeq(contours);*/
}
void init_contours_weight(const IplImage* im, const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min,
	const int widthimage, const int heightimage,
	CvMemStorage* storage, CvSeq* contours,
	CvSeq* squares,
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus,
	CvSeq* note_rapport_long_cotes_opposes,//means largeur_sur_hauteur
	CvSeq* note_plus_petit_sinus,
	CvSeq* note_plus_grand_sinus,
	CvSeq* notesym,
	float cvApproxPoly_coeff)
{
	int i, index_element_a_zapper;
	//cvApproxPoly_coeff regulation
	if (cvApproxPoly_coeff < .0) cvApproxPoly_coeff = -cvApproxPoly_coeff;
	if (cvApproxPoly_coeff < .0 || cvApproxPoly_coeff>1.0f) cvApproxPoly_coeff = 0.05f;
	CvPoint pt[4];
	const int nb_max_contours = 20;//11
	// teste chaque contour
	while (contours)
	{
		// approximate contour with accuracy proportional
		// to the contour perimeter
		CvSeq* polygone = cvApproxPoly(contours, sizeof(CvContour), storage,
			CV_POLY_APPROX_DP, (cvContourPerimeter(contours
			)) * cvApproxPoly_coeff, 0);
		// le 0.02 est un indice d'approximation. il n'est pas etre judicieux 
		// de le modifier, mieux vaut utiliser la methode transformant toutes
		// sortes de polygones (pentagones ou +) en quadrilatees
		// si il y a max 10 cee (cette valeur est arbitraire)
		// et que le polygone est fermeet a une aire suffisante pour ere inteessant
		if (polygone->total < nb_max_contours && polygone->total>3)
		{
			//The function cvCheckContourConvexity tests whether the input 
			//contour is convex or not. The contour must be simple, i.e. 
			//without self-intersections. 
			double area = fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ)));
			if (area > NB_MIN_PIXELS_IN_PLATE)
			{
				if (
					cvCheckContourConvexity(polygone)
					) {//a remttre
					//tant qu'il y a plus de 4 cee
					while (polygone->total > 4)
					{
						index_element_a_zapper = zappeUnPoint(polygone, storage);//on regarde si un point est inutile
						if (index_element_a_zapper != -1)//si c'est non on arree
						{
							cvSeqRemove(polygone, index_element_a_zapper);
						}
						else//si c'est oui on retire ce point inutile
						{
							cv::RotatedRect RotatedRect(get_rect(polygone));
							double rotated_area = RotatedRect.size.area();
							int angle_degree = abs(static_cast<int>(RotatedRect.angle));
							div_t divresult;
							divresult = div(angle_degree, 90);
							cv::Rect boundingrect(RotatedRect.boundingRect());
							if (rotated_area * 0.85 < area && divresult.rem < 25
								&& boundingrect.width>2.5 * boundingrect.height)
							{
								swap(polygone, RotatedRect);
							}
							else//si c'est oui on retire ce point inutile
							{
								cvClearSeq(polygone);
							}
						}
					}
				}
				else//si c'est oui on retire ce point inutile
				{
					cv::RotatedRect RotatedRect(get_rect(polygone));
					double rotated_area = RotatedRect.size.area();
					int angle_degree = abs(static_cast<int>(RotatedRect.angle));
					div_t divresult;
					divresult = div(angle_degree, 90);
					cv::Rect boundingrect(RotatedRect.boundingRect());
					if (rotated_area * 0.85 < area && divresult.rem < 25
						&& boundingrect.width>2.5 * boundingrect.height)
					{
						swap(polygone, RotatedRect);
					}
					else//si c'est oui on retire ce point inutile
					{
						cvClearSeq(polygone);
					}
					//cvClearSeq(polygone); 
				}
			}
			else//si c'est oui on retire ce point inutile
			{
				cvClearSeq(polygone);
			}
		}
		// square contours should have 4 vertices after approximation
		// relatively large area (to filter out noisy contours)
		// and be convex.
		// Note: absolute value of an area is used because
		// area may be positive or negative - in accordance with the
		// contour orientation
		// si le quadrilatee obtenu a 4 cee
		// et que les conditions preeemment eonces sont veifies
		if (polygone)
		{
			if (polygone->total == 4)
			{
				if ( //The function cvCheckContourConvexity tests whether the input 
					//contour is convex or not. The contour must be simple, i.e. 
						//without self-intersections.
					cvCheckContourConvexity(polygone) &&
					fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ))) > NB_MIN_PIXELS_IN_PLATE
					)
				{
					float plus_grand_cosinus = -1.0;
					float t, plus_petit_sinus = 1.0f, plus_grand_sinus = -1.0f, //sin between two opposite sides of the lp //the greatest sin between two opposite sides of the lp
						sym = -1.0f,//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						petit = 0.0f//le min de la somme de deux cote oppose
						;
					float rapport_long_cotes_opposes = -1.0;//means largeur_sur_hauteur
					CvPoint p;
					float dist_plus_grand_cote = 0.0f;
					float cos_plus_grand_cote = -2.0f, anglePetitCote = -2.0f;//=cos between width and horizontal
					CvPoint p1;
					bool colleAuxBords = false;
					//************************************************
					//   CALC DES GRANDEURS GEO POUR UNE PLAQUE
					//************************************************
					for (i = 0; i < 5// && !colleAuxBords
						; i++)
					{
						// store all the contour vertices in the buffer
						// (pt sert estocker le quadrilatee eudie
						// afin de simplifier les expressions)
						pt[i & 3] = *(CvPoint*)cvGetSeqElemLPR(polygone, i, 0);
						//colleAuxBords&=(pt)&&()
						//epartir de i = 2 on peut faire un angle
						if (i >= 2)
						{
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							t = fabsf(cosine(pt[i & 3], pt[(i - 2) & 3], pt[(i - 1) & 3]));
							if (plus_grand_cosinus < t) plus_grand_cosinus = t;
							//plus_grand_cosinus = MAX( plus_grand_cosinus, t );// on garde le + grand cos d'angle du quadrilatee
						}
						// on regarde si le + grand ceeforme 1 angle
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 1)
						{
							//  on regarde si on a pas tout simplement detecte toute l'image
							// c a d si tous les points ne seraient pas dans les coins de l'image
							colleAuxBords = colleAuxBords || (pt[i & 3].x < dist_min_bord) || (pt[i & 3].x > widthimage - dist_min_bord) ||
								(pt[i & 3].y < dist_min_bord) || (pt[i & 3].y > heightimage - dist_min_bord);
							if (dist(pt[i & 3], pt[(i - 1) & 3]) > dist_plus_grand_cote)
							{
								p1.y = pt[i & 3].y;
								p1.x = pt[(i - 1) & 3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								cos_plus_grand_cote = fabsf(cosine(p1, pt[(i - 1) & 3], pt[i & 3]));//pt[(i - 1) & 3]->pt[i & 3]=WIDTH //=cos between width and horizontal
								dist_plus_grand_cote = dist(pt[i & 3], pt[(i - 1) & 3]);
							}
						}
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 3)
						{
							// on garde le + petit sinus d'angle entre 2 cotes opposes
							p.x = pt[(i - 3) & 3].x - pt[(i - 2) & 3].x + pt[(i - 1) & 3].x;
							p.y = pt[(i - 3) & 3].y - pt[(i - 2) & 3].y + pt[(i - 1) & 3].y;
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							float cosinus_courant(cosine(pt[i & 3], p, pt[(i - 1) & 3]));
#ifdef _DEBUG
							assert(cosinus_courant > -1.0 - FLT_EPSILON &&
								cosinus_courant < 1.0f + FLT_EPSILON);
#endif //_DEBUG
							t = sqrtf(1 - (cosinus_courant * cosinus_courant));
							if (plus_petit_sinus > t)plus_petit_sinus = t;//sin between two opposite sides of the lp
							//plus_petit_sinus le +petit sinus (trapee)
							if (plus_grand_sinus < t) plus_grand_sinus = t;//the greatest sin between two opposite sides of the lp
							//plus_grand_sinus le +grand sinus (paralleogramme)
						}
						if (i == 4)
						{
							float d3 = dist(pt[1], pt[0]);
							float d0 = dist(pt[0], pt[3]);
							float d1 = dist(pt[3], pt[2]);
							float d2 = dist(pt[2], pt[1]);
							///verifie le repport des longueurs du quadrilatee
							if ((d0 + d2) > (d1 + d3)) rapport_long_cotes_opposes = (d0 + d2) / (d1 + d3);//means largeur_sur_hauteur
							else rapport_long_cotes_opposes = (d1 + d3) / (d0 + d2);
							//sym mesure le plus petit eart en proportions entre 2 cotes opposes
							sym = MAX(fabsf(d0 - d2) / (d0 + d2),
								fabsf(d1 - d3) / (d1 + d3));//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
							// on utilise petit pour mettre la + petite somme de longueurs de 2 cotes opposes
							// qui doit ere au moins de 36 (soit 2*18)
							petit = MIN((d0 + d2), (d1 + d3));//means hauteur
							//on verifie si les 2 plus petits cote (en fait le plus petit cotedes 2 derniers controles et celui lui faisant face)
							// sont suffisament verticaux
							if (d0 < d1)//d0 and d2 are height and d1 , d3 are width : pt[3]->pt[2]= WIDTH , pt[0]->pt[3]=HEIGHT
							{
								p1.y = pt[0].y;
								p1.x = pt[3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[0], pt[3]));//pt[0]->pt[3]=HEIGHT. p1->pt[3] =  cos of angle betwween vertical and  pt[0]->pt[3]=HEIGHT
								p1.y = pt[2].y;
								p1.x = pt[1].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[2], pt[1])));
							}
							else
							{
								p1.y = pt[3].y;
								p1.x = pt[2].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[3], pt[2]));
								p1.y = pt[1].y;
								p1.x = pt[0].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[1], pt[0])));
							}
							anglePetitCote = sqrtf(1.0f - anglePetitCote * anglePetitCote);
						}
					}
#ifdef _DEBUG
					assert(rapport_long_cotes_opposes >= 1.0 || rapport_long_cotes_opposes < -0.5f);//means largeur_sur_hauteur
#endif //_DEBUG
					cv::Point top_left, top_right, bottom_right, bottom_left;
					cv::Point P0(pt[0].x, pt[0].y);
					cv::Point P1(pt[1].x, pt[1].y);
					cv::Point P2(pt[2].x, pt[2].y);
					cv::Point P3(pt[3].x, pt[3].y);
					bool corners = (get_corners(im, pt[0], pt[1], pt[2], pt[3], top_left, top_right, bottom_right, bottom_left));
					// on n'applique une note que si le quadrilatee semble inteessant
					// (pas de forme aberrante)
					if (corners &&
						(rapport_long_cotes_opposes > rapport_largeur_sur_hauteur_min)//means largeur_sur_hauteur always true
						&& (rapport_long_cotes_opposes < 10.0f)
						&& (rapport_long_cotes_opposes > 0.164f)//(rapport_long_cotes_opposes > 0.164) always true since (rapport_long_cotes_opposes >=1)
						//(rapport_long_cotes_opposes < 10) //largeur = 7* hauteur 
						&& (plus_petit_sinus < 0.5f)//sin between two opposite sides of the lp
						&& (plus_grand_sinus < 0.8f)//the greatest sin between two opposite sides of the lp
						&& (sym < 0.5f)//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//le min de la somme de deux cote oppose
						&& (petit > (hauteur_plaque_min / 2))//means hauteur
						&& (cos_plus_grand_cote > 0.707f)//=cos between width and horizontal so this means that the width must not be inclined of more than 45 degrees 
						&& (!colleAuxBords)
						&& (anglePetitCote < 0.3f)
						//&& (get_corners(im, pt[0], pt[1], pt[2], pt[3],top_left, top_right, bottom_right, bottom_left))
						)
						//FILTRE
					{
						// on stocke les quadrilatees
						for (i = 0; i < 4; i++)
						{
							cvSeqPush(squares, cvGetSeqElemLPR(polygone, i, 0));
						}
						// on stocke les notes correspondantes
						cvSeqPush(note_plus_grand_cosinus, &plus_grand_cosinus);
#ifdef _DEBUG
						assert(plus_grand_cosinus > -1.0 - FLT_EPSILON
							&& plus_grand_cosinus < 1.0f + FLT_EPSILON);
						assert(rapport_long_cotes_opposes >= 1.0f);//means largeur_sur_hauteur always true
#endif //_DEBUG
						rapport_long_cotes_opposes = fabsf(rapport_long_cotes_opposes - 5.04f);//means largeur_sur_hauteur always true 5.04 ?????????????
						cvSeqPush(note_rapport_long_cotes_opposes, &rapport_long_cotes_opposes);
#ifdef _DEBUG
						assert(plus_petit_sinus > -1.0 - FLT_EPSILON &&//sin between two opposite sides of the lp
							plus_petit_sinus < 1.0f + FLT_EPSILON);
#endif //_DEBUG
						cvSeqPush(note_plus_petit_sinus, &plus_petit_sinus);//sin between two opposite sides of the lp
						cvSeqPush(note_plus_grand_sinus, &plus_grand_sinus);//the greatest sin between two opposite sides of the lp
						cvSeqPush(notesym, &sym);//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//NOTES
					}
				}
			}
		}
		// on passe au suivant
		contours = contours->h_next;
	}
	/*seqence stock�e ds storage donc m�moire lib�r�e avec storage
	if(contours)
	cvClearSeq(contours);*/
}
cv::Rect get_inter_(const cv::Rect& r1, const cv::Rect& r2)
{
	int width_inter = 0;
	int x_inter = r1.x;
	if (x_inter < r2.x) {
		x_inter = r2.x;
	}
	if (r1.x + r1.width < r2.x + r2.width)
		width_inter = r1.x + r1.width - x_inter;
	else width_inter = r2.x + r2.width - x_inter;
	int height_inter = 0;
	int y_inter = r1.y;
	if (y_inter < r2.y) {
		y_inter = r2.y;
	}
	if (r1.y + r1.height < r2.y + r2.height)
		height_inter = r1.y + r1.height - y_inter;
	else height_inter = r2.y + r2.height - y_inter;
	return cv::Rect(x_inter, y_inter, width_inter, height_inter);
}
float iou_(const cv::Rect& r1, const cv::Rect& r2)
{
	cv::Rect inter(get_inter_(r1, r2));
	if (inter.width > 0 && inter.height > 0 && r1.width > 0 && r1.height > 0 && r2.width > 0 && r2.height > 0) {
		return static_cast<float>(inter.area()) / static_cast<float>(r1.area() + r2.area() - inter.area());
	}
	else return 0.0f;
}
void init_contours_weight(const IplImage* im, const cv::Rect& global_rect, const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min,
	const int widthimage, const int heightimage,
	CvMemStorage* storage, CvSeq* contours,
	CvSeq* squares,
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus,
	CvSeq* note_rapport_long_cotes_opposes,//means largeur_sur_hauteur
	CvSeq* note_plus_petit_sinus,
	CvSeq* note_plus_grand_sinus,
	CvSeq* notesym,
	float cvApproxPoly_coeff)
{
	int i, index_element_a_zapper;
	//cvApproxPoly_coeff regulation
	if (cvApproxPoly_coeff < .0) cvApproxPoly_coeff = -cvApproxPoly_coeff;
	if (cvApproxPoly_coeff < .0 || cvApproxPoly_coeff>1.0f) cvApproxPoly_coeff = 0.05f;
	CvPoint pt[4];
	const int nb_max_contours = 20;//11
	// teste chaque contour
	while (contours)
	{
		// approximate contour with accuracy proportional
		// to the contour perimeter
		CvSeq* polygone = cvApproxPoly(contours, sizeof(CvContour), storage,
			CV_POLY_APPROX_DP, (cvContourPerimeter(contours
			)) * cvApproxPoly_coeff, 0);
		// le 0.02 est un indice d'approximation. il n'est pas etre judicieux 
		// de le modifier, mieux vaut utiliser la methode transformant toutes
		// sortes de polygones (pentagones ou +) en quadrilatees
		// si il y a max 10 cee (cette valeur est arbitraire)
		// et que le polygone est fermeet a une aire suffisante pour ere inteessant
		if (polygone->total < nb_max_contours && polygone->total>3)
		{
			//The function cvCheckContourConvexity tests whether the input 
			//contour is convex or not. The contour must be simple, i.e. 
			//without self-intersections. 
			double area = fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ)));
			if (area > NB_MIN_PIXELS_IN_PLATE)
			{
				if (
					cvCheckContourConvexity(polygone)
					) {//a remttre
					//tant qu'il y a plus de 4 cee
					while (polygone->total > 4)
					{
						index_element_a_zapper = zappeUnPoint(polygone, storage);//on regarde si un point est inutile
						if (index_element_a_zapper != -1)//si c'est non on arree
						{
							cvSeqRemove(polygone, index_element_a_zapper);
						}
						else//si c'est oui on retire ce point inutile
						{
							cv::RotatedRect RotatedRect(get_rect(polygone));
							double rotated_area = RotatedRect.size.area();
							int angle_degree = abs(static_cast<int>(RotatedRect.angle));
							div_t divresult;
							divresult = div(angle_degree, 90);
							cv::Rect boundingrect(RotatedRect.boundingRect());
							if (rotated_area * 0.85 < area && divresult.rem < 25
								&& boundingrect.width>2.5 * boundingrect.height)
							{
								swap(polygone, RotatedRect);
							}
							else//si c'est oui on retire ce point inutile
							{
								cvClearSeq(polygone);
							}
						}
					}
				}
				else//si c'est oui on retire ce point inutile
				{
					cv::RotatedRect RotatedRect(get_rect(polygone));
					double rotated_area = RotatedRect.size.area();
					int angle_degree = abs(static_cast<int>(RotatedRect.angle));
					div_t divresult;
					divresult = div(angle_degree, 90);
					cv::Rect boundingrect(RotatedRect.boundingRect());
					if (rotated_area * 0.85 < area && divresult.rem < 25
						&& boundingrect.width>2.5 * boundingrect.height)
					{
						swap(polygone, RotatedRect);
					}
					else//si c'est oui on retire ce point inutile
					{
						cvClearSeq(polygone);
					}
					//cvClearSeq(polygone); 
				}
				if (polygone) {
					if (polygone->total >= 4)
					{
						cv::RotatedRect RotatedRect_(get_rect(polygone));
						cv::Rect global_rotated_rect = RotatedRect_.boundingRect();
						if (iou_(global_rect, global_rotated_rect) < 0.3f) {
							cvClearSeq(polygone);
						}
					}
				}
			}
			else//si c'est oui on retire ce point inutile
			{
				cvClearSeq(polygone);
			}
		}
		// square contours should have 4 vertices after approximation
		// relatively large area (to filter out noisy contours)
		// and be convex.
		// Note: absolute value of an area is used because
		// area may be positive or negative - in accordance with the
		// contour orientation
		// si le quadrilatee obtenu a 4 cee
		// et que les conditions preeemment eonces sont veifies
		if (polygone)
		{
			if (polygone->total == 4)
			{
				if ( //The function cvCheckContourConvexity tests whether the input 
					//contour is convex or not. The contour must be simple, i.e. 
						//without self-intersections.
					cvCheckContourConvexity(polygone) &&
					fabsf(doubleToFloat(cvContourArea(polygone, CV_WHOLE_SEQ))) > NB_MIN_PIXELS_IN_PLATE
					)
				{
					float plus_grand_cosinus = -1.0;
					float t, plus_petit_sinus = 1.0f, plus_grand_sinus = -1.0f, //sin between two opposite sides of the lp //the greatest sin between two opposite sides of the lp
						sym = -1.0f,//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						petit = 0.0f//le min de la somme de deux cote oppose
						;
					float rapport_long_cotes_opposes = -1.0;//means largeur_sur_hauteur
					CvPoint p;
					float dist_plus_grand_cote = 0.0f;
					float cos_plus_grand_cote = -2.0f, anglePetitCote = -2.0f;//=cos between width and horizontal
					CvPoint p1;
					bool colleAuxBords = false;
					//************************************************
					//   CALC DES GRANDEURS GEO POUR UNE PLAQUE
					//************************************************
					for (i = 0; i < 5// && !colleAuxBords
						; i++)
					{
						// store all the contour vertices in the buffer
						// (pt sert estocker le quadrilatee eudie
						// afin de simplifier les expressions)
						pt[i & 3] = *(CvPoint*)cvGetSeqElemLPR(polygone, i, 0);
						//colleAuxBords&=(pt)&&()
						//epartir de i = 2 on peut faire un angle
						if (i >= 2)
						{
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							t = fabsf(cosine(pt[i & 3], pt[(i - 2) & 3], pt[(i - 1) & 3]));
							if (plus_grand_cosinus < t) plus_grand_cosinus = t;
							//plus_grand_cosinus = MAX( plus_grand_cosinus, t );// on garde le + grand cos d'angle du quadrilatee
						}
						// on regarde si le + grand ceeforme 1 angle
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 1)
						{
							//  on regarde si on a pas tout simplement detecte toute l'image
							// c a d si tous les points ne seraient pas dans les coins de l'image
							colleAuxBords = colleAuxBords || (pt[i & 3].x < dist_min_bord) || (pt[i & 3].x > widthimage - dist_min_bord) ||
								(pt[i & 3].y < dist_min_bord) || (pt[i & 3].y > heightimage - dist_min_bord);
							if (dist(pt[i & 3], pt[(i - 1) & 3]) > dist_plus_grand_cote)
							{
								p1.y = pt[i & 3].y;
								p1.x = pt[(i - 1) & 3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								cos_plus_grand_cote = fabsf(cosine(p1, pt[(i - 1) & 3], pt[i & 3]));//pt[(i - 1) & 3]->pt[i & 3]=WIDTH //=cos between width and horizontal
								dist_plus_grand_cote = dist(pt[i & 3], pt[(i - 1) & 3]);
							}
						}
						// infeieur ou eal 45 eseuil'horizontale
						if (i >= 3)
						{
							// on garde le + petit sinus d'angle entre 2 cotes opposes
							p.x = pt[(i - 3) & 3].x - pt[(i - 2) & 3].x + pt[(i - 1) & 3].x;
							p.y = pt[(i - 3) & 3].y - pt[(i - 2) & 3].y + pt[(i - 1) & 3].y;
							//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
							// angle renvoie le cos de l'angle derit par 2 vecteurs
							// pt0->pt1 and pt0->pt2 
							float cosinus_courant(cosine(pt[i & 3], p, pt[(i - 1) & 3]));
#ifdef _DEBUG
							assert(cosinus_courant > -1.0 - FLT_EPSILON &&
								cosinus_courant < 1.0f + FLT_EPSILON);
#endif //_DEBUG
							t = sqrtf(1 - (cosinus_courant * cosinus_courant));
							if (plus_petit_sinus > t)plus_petit_sinus = t;//sin between two opposite sides of the lp
							//plus_petit_sinus le +petit sinus (trapee)
							if (plus_grand_sinus < t) plus_grand_sinus = t;//the greatest sin between two opposite sides of the lp
							//plus_grand_sinus le +grand sinus (paralleogramme)
						}
						if (i == 4)
						{
							float d3 = dist(pt[1], pt[0]);
							float d0 = dist(pt[0], pt[3]);
							float d1 = dist(pt[3], pt[2]);
							float d2 = dist(pt[2], pt[1]);
							///verifie le repport des longueurs du quadrilatee
							if ((d0 + d2) > (d1 + d3)) rapport_long_cotes_opposes = (d0 + d2) / (d1 + d3);//means largeur_sur_hauteur
							else rapport_long_cotes_opposes = (d1 + d3) / (d0 + d2);
							//sym mesure le plus petit eart en proportions entre 2 cotes opposes
							sym = MAX(fabsf(d0 - d2) / (d0 + d2),
								fabsf(d1 - d3) / (d1 + d3));//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
							// on utilise petit pour mettre la + petite somme de longueurs de 2 cotes opposes
							// qui doit ere au moins de 36 (soit 2*18)
							petit = MIN((d0 + d2), (d1 + d3));//means hauteur
							//on verifie si les 2 plus petits cote (en fait le plus petit cotedes 2 derniers controles et celui lui faisant face)
							// sont suffisament verticaux
							if (d0 < d1)//d0 and d2 are height and d1 , d3 are width : pt[3]->pt[2]= WIDTH , pt[0]->pt[3]=HEIGHT
							{
								p1.y = pt[0].y;
								p1.x = pt[3].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[0], pt[3]));//pt[0]->pt[3]=HEIGHT. p1->pt[3] =  cos of angle betwween vertical and  pt[0]->pt[3]=HEIGHT
								p1.y = pt[2].y;
								p1.x = pt[1].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[2], pt[1])));
							}
							else
							{
								p1.y = pt[3].y;
								p1.x = pt[2].x;
								//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
								// angle renvoie le cos de l'angle derit par 2 vecteurs
								// pt0->pt1 and pt0->pt2 
								anglePetitCote = fabsf(cosine(p1, pt[3], pt[2]));
								p1.y = pt[1].y;
								p1.x = pt[0].x;
								anglePetitCote = MIN(anglePetitCote, fabsf(cosine(p1, pt[1], pt[0])));
							}
							anglePetitCote = sqrtf(1.0f - anglePetitCote * anglePetitCote);
						}
					}
#ifdef _DEBUG
					assert(rapport_long_cotes_opposes >= 1.0 || rapport_long_cotes_opposes < -0.5f);//means largeur_sur_hauteur
#endif //_DEBUG
					cv::Point top_left, top_right, bottom_right, bottom_left;
					cv::Point P0(pt[0].x, pt[0].y);
					cv::Point P1(pt[1].x, pt[1].y);
					cv::Point P2(pt[2].x, pt[2].y);
					cv::Point P3(pt[3].x, pt[3].y);
					bool corners = (get_corners(im, pt[0], pt[1], pt[2], pt[3], top_left, top_right, bottom_right, bottom_left));
					// on n'applique une note que si le quadrilatee semble inteessant
					// (pas de forme aberrante)
					if (corners &&
						(rapport_long_cotes_opposes > rapport_largeur_sur_hauteur_min)//means largeur_sur_hauteur always true
						&& (rapport_long_cotes_opposes < 10.0f)
						&& (rapport_long_cotes_opposes > 0.164f)//(rapport_long_cotes_opposes > 0.164) always true since (rapport_long_cotes_opposes >=1)
						//(rapport_long_cotes_opposes < 10) //largeur = 7* hauteur 
						&& (plus_petit_sinus < 0.5f)//sin between two opposite sides of the lp
						&& (plus_grand_sinus < 0.8f)//the greatest sin between two opposite sides of the lp
						&& (sym < 0.5f)//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//le min de la somme de deux cote oppose
						&& (petit > (hauteur_plaque_min / 2))//means hauteur
						&& (cos_plus_grand_cote > 0.707f)//=cos between width and horizontal so this means that the width must not be inclined of more than 45 degrees 
						&& (!colleAuxBords)
						&& (anglePetitCote < 0.3f)
						//&& (get_corners(im, pt[0], pt[1], pt[2], pt[3],top_left, top_right, bottom_right, bottom_left))
						)
						//FILTRE
					{
						// on stocke les quadrilatees
						for (i = 0; i < 4; i++)
						{
							cvSeqPush(squares, cvGetSeqElemLPR(polygone, i, 0));
						}
						// on stocke les notes correspondantes
						cvSeqPush(note_plus_grand_cosinus, &plus_grand_cosinus);
#ifdef _DEBUG
						assert(plus_grand_cosinus > -1.0 - FLT_EPSILON
							&& plus_grand_cosinus < 1.0f + FLT_EPSILON);
						assert(rapport_long_cotes_opposes >= 1.0f);//means largeur_sur_hauteur always true
#endif //_DEBUG
						rapport_long_cotes_opposes = fabsf(rapport_long_cotes_opposes - 5.04f);//means largeur_sur_hauteur always true 5.04 ?????????????
						cvSeqPush(note_rapport_long_cotes_opposes, &rapport_long_cotes_opposes);
#ifdef _DEBUG
						assert(plus_petit_sinus > -1.0 - FLT_EPSILON &&//sin between two opposite sides of the lp
							plus_petit_sinus < 1.0f + FLT_EPSILON);
#endif //_DEBUG
						cvSeqPush(note_plus_petit_sinus, &plus_petit_sinus);//sin between two opposite sides of the lp
						cvSeqPush(note_plus_grand_sinus, &plus_grand_sinus);//the greatest sin between two opposite sides of the lp
						cvSeqPush(notesym, &sym);//means ratio between difference and sum of left and right side or ratio between difference and sum of top and bottom side (in fact max of these two ratio)
							// we want these ratio to be as small as possible
						//NOTES
					}
				}
			}
		}
		// on passe au suivant
		contours = contours->h_next;
	}
	/*seqence stock�e ds storage donc m�moire lib�r�e avec storage
	if(contours)
	cvClearSeq(contours);*/
}
void LPRThreshold(const IplImage* src, IplImage*& dst,
	const int threshold, int& nb_pts_sup_threshold)
{
#ifdef _DEBUG
	assert(dst->tileInfo == nullptr);
	//assert(dst->align == IPL_ALIGN_DWORD);
	assert(dst->depth == IPL_DEPTH_8U && dst->nChannels == 1);
	assert(dst->widthStep >= src->width
		&& dst->widthStep < src->width + 4);
	assert(dst->origin == 0);
#endif //_DEBUG
	// 0 - top-left origin
#ifdef _DEBUG
#ifdef _DEBUG
	assert(src->tileInfo == nullptr);
	//assert(src->align == IPL_ALIGN_DWORD);
	assert(src->depth == IPL_DEPTH_8U && src->nChannels == 1);
	//assert(src->widthStep >= src->width && src->widthStep < src->width + 4);
	assert(src->origin == 0);
#endif //_DEBUG
#endif //_DEBUG
	// 0 - top-left origin
	if (dst->width == src->width &&
		dst->height == src->height
		&& dst->depth == IPL_DEPTH_8U
		&& src->depth == IPL_DEPTH_8U
		&& dst->nChannels == 1
		&& src->nChannels == 1) {
		int width = (dst->width % dst->align == 0) ?
			dst->width :
			((dst->width / dst->align) + 1) * dst->align;
#ifdef _DEBUG
		assert(dst->widthStep == width);
#endif //_DEBUG
		int nb_byte_data = width * dst->height;
		int alignement = width - dst->width;
#ifdef _DEBUG
		assert(alignement >= 0 && alignement < 4);
#endif //_DEBUG
		nb_pts_sup_threshold = 0;
		for (int i = 0; i < src->height; ++i) {	//pour chaque ligne
			unsigned char* ptr_cv_dst = (unsigned char*)dst->imageData + i * dst->widthStep;
			unsigned char* ptr_cv_src = (unsigned char*)src->imageData + i * src->widthStep;
			for (int j = 0; j < src->width; ++j)
			{
				if (*ptr_cv_src > threshold) {
					*ptr_cv_dst = 127;
					nb_pts_sup_threshold++;
				}
				else *ptr_cv_dst = 0;
				ptr_cv_dst++;
				ptr_cv_src++;
			}
		}
		// Save the Output Image
		// int cvSaveImage( const char* filename, const CvArr* image );
	}
	else {
		nb_pts_sup_threshold = -1;
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage 
//peut traiter les images sources couleur ou en niveaux de gris
CvSeq* findSquares4
(IplImage* im_src, const cv::Rect& global_rect, CvMemStorage* storage,
	const int nTresh, const int mean_carac
	, const int mean_fond, //
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	CvSeq* contours = 0;
	CvPoint centre;
	int  i, j, k, l, m,
		un = 1,
		pond, max;
	int thresh_canny = 50;
	CvSize sz = cvSize(im_src->width, im_src->height);
	//quelques images temporaires qui permettront de travailler
	IplImage* gray1 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	IplImage* tgray = cvCreateImage(sz, IPL_DEPTH_8U//im_src->depth
		, 1);
	IplImage* gray2 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	int filterSize = 5;
	IplConvKernel* convKernel = cvCreateStructuringElementEx
	(filterSize, filterSize, (filterSize - 1) / 2, (filterSize - 1) / 2,
		CV_SHAPE_ELLIPSE, nullptr);
#ifdef _DEBUG
	assert(im_src->nChannels == im_src->nChannels);
	assert(im_src->depth == im_src->depth);
	assert(im_src->align == im_src->align);
#endif //_DEBUG
	// squares et classement permettront de travailler sur les listes de quadrilatees
	CvSeq* squares = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	CvSeq* classement = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// centres est une liste de centres de quadrilatees utilisee ela fin
	CvSeq* centres = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_rapport_long_cotes_opposes = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_petit_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_grand_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* notesym = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	// diffeentes seuences contenant les places correspondant aux diffeents critees
	CvSeq* place_plus_grand_cosinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_rapport_long_cotes_opposes = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_petit_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_grand_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* placesym = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// place contiendra la liste de sommes de places
	CvSeq* place = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	//classementplace contiendra la liste des quadrilatees ordonnee selon les sommes de places
	CvSeq* classementplace = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// selectionne le + grand rectangle dans l'image
	// dont les dimensions sont divisibles par 2
#ifdef _DEBUG
	assert(im_src->width == sz.width);
	assert(im_src->height == sz.height);
#endif //_DEBUG
	//		cvSetImageROI( im_src, cvRect( 0, 0, sz.width, sz.height ));
	// pt servira eplusieurs reprises estocker des quadrilatees
	// (tableau de 4 points)
	CvPoint pt[4];
	// on teste toutes les couleurs R,G,B
	//const int sz.width=im_src->width;const int sz.height=im_src->height;
	cvCopy(im_src, tgray, 0);
	//si on est pas en NB
	//on recopie ds une image de travail
	bool prochaine_image_gray1 = true;
	// on teste N niveaux de seuillage
	// apply Canny. Take the upper threshold from slider
	// and set the lower to 0 (which forces edges merging) 
	cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
	);
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
	// trouve les contours et en fait une liste
	float cvApproxPoly_coeff = 0.02f;
	cvFindContours(gray1, storage, &contours, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
	// teste chaque contour
	//calcule le poids des contours
	init_contours_weight(tgray, global_rect, dist_min_bord,
		hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
		sz.width, sz.height,
		storage, contours,
		squares,
		// diffeentes seuences contentant les notes correspondant aux diffeents critees 
		note_plus_grand_cosinus,
		note_rapport_long_cotes_opposes,
		note_plus_petit_sinus,
		note_plus_grand_sinus,
		notesym, cvApproxPoly_coeff);
	if (!squares->total) {
		cv::Mat img = cv::cvarrToMat(tgray);
		cv::Mat image_final = cv::cvarrToMat(gray1);
		int blockSize = (sz.width + sz.height) / 2;
		if (blockSize % 2 == 0) blockSize += 1;
		cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		// teste chaque contour
		//calcule le poids des contours
		int total_number_of_plates = squares->total;
		init_contours_weight(tgray, global_rect, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		if (total_number_of_plates == squares->total) {
			int nb_pts_sup_threshold = (sz.width * sz.height);
			int new_mean_carac = mean_carac;
			int new_mean_fond = mean_fond;
			if (mean_carac > mean_fond) {
				new_mean_carac = mean_fond;
				new_mean_fond = mean_carac;
				new_mean_carac -= (mean_carac - mean_fond) / 10;
				new_mean_fond += (mean_carac - mean_fond) / 10;
			}
			else {
				new_mean_carac -= (mean_fond - mean_carac) / 10;
				new_mean_fond += (mean_fond - mean_carac) / 10;
			}
#ifdef _DEBUG
			assert(new_mean_carac <= new_mean_fond);
#endif //_DEBUG
			if (new_mean_carac < 0) new_mean_carac = 0;
			if (new_mean_fond > 255) new_mean_fond = 255;
			int new_nTresh = nTresh;
			if (new_nTresh > (new_mean_fond - new_mean_carac) >> 1)
				new_nTresh = (new_mean_fond - new_mean_carac) >> 1;
			int seuil;
			for (seuil = 0; seuil < new_nTresh; seuil++)
			{
				// sinon on applique le seuillage
				///((tgray(x,y) = gray(x,y) < (seuil+1)*255/N ? 255 : 0))
				int current_nb_pts_sup_threshold = 0;
				if (new_mean_carac < new_mean_fond)
					LPRThreshold(im_src, gray1,
						new_mean_carac + ((new_mean_fond - new_mean_carac) * seuil) / new_nTresh,
						current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
				else LPRThreshold(im_src, gray1,
					new_mean_fond + ((new_mean_carac - new_mean_fond) * seuil) / new_nTresh,
					current_nb_pts_sup_threshold);
				if (current_nb_pts_sup_threshold >= 0 &&
					current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
					///////////////////////////////////////////////////////////////////////////////////////////
					// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
					assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
					float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
						current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
					assert(percentage_match > -FLT_EPSILON &&
						percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
					if (percentage_match < 0.75) {
						nb_pts_sup_threshold = current_nb_pts_sup_threshold;
						cvFindContours(gray1, storage, &contours, sizeof(CvContour),
							CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
						total_number_of_plates = squares->total;
						init_contours_weight(tgray, global_rect, dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees 
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);
						if (seuil == 0) cvApproxPoly_coeff = 0.02f;
						else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
						else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
						else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
														// teste chaque contour
														//calcule le poids des contours
						if (total_number_of_plates == squares->total) {
							cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
							cvFindContours(gray2, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							init_contours_weight(tgray, global_rect, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
						}
					}
				}
			}
		}
	}
	// (on libee de la meoire)
	cvReleaseImage(&gray1);
	cvReleaseImage(&tgray);
	cvReleaseImage(&gray2);
	cvReleaseStructuringElement(&convKernel);
#ifdef _DEBUG
	assert(note_plus_grand_cosinus->total == note_rapport_long_cotes_opposes->total &&
		note_plus_grand_cosinus->total == note_plus_petit_sinus->total &&
		note_plus_grand_cosinus->total == note_plus_grand_sinus->total &&
		note_plus_grand_cosinus->total == notesym->total);
#endif //_DEBUG
	//si rien n'a passele filtre, on arree le:
	//on renvoie une seuence de -1
	if (notesym->total == 0)
	{
		cvClearSeq(classement);
		centre = cvPoint(SHRT_MIN, SHRT_MIN);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		return classement;
	}
	// CLASSEMENT
	// renvoie la seuence de places epartir de la seuence de notes
	place_plus_grand_cosinus = sort(note_plus_grand_cosinus, storage);
	place_rapport_long_cotes_opposes = sort(note_rapport_long_cotes_opposes, storage);
	place_plus_petit_sinus = sort(note_plus_petit_sinus, storage);
	place_plus_grand_sinus = sort(note_plus_grand_sinus, storage);
	placesym = sort(notesym, storage);
#ifdef _DEBUG
	assert(place_plus_grand_cosinus->total == place_rapport_long_cotes_opposes->total &&
		place_plus_grand_cosinus->total == place_plus_petit_sinus->total &&
		place_plus_grand_cosinus->total == place_plus_grand_sinus->total &&
		place_plus_grand_cosinus->total == placesym->total);
#endif //_DEBUG
	//dans place, on met la somme des places pour chaque quadrilatee
	for (i = 0; i < place_plus_grand_cosinus->total; i++)
	{
		j = (*(int*)cvGetSeqElemLPR(place_plus_grand_cosinus, i, 0) + *(int*)cvGetSeqElemLPR(place_rapport_long_cotes_opposes, i, 0) +
			*(int*)cvGetSeqElemLPR(place_plus_petit_sinus, i, 0) + *(int*)cvGetSeqElemLPR(place_plus_grand_sinus, i, 0) +
			*(int*)cvGetSeqElemLPR(placesym, i, 0));
		cvSeqPush(place, &j);
	}
	// on initialise classementplace et classement
	cvSeqPush(classementplace, cvGetSeqElemLPR(place, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3, 0));
	//dans classement on met les quadrilatees
	//et dans classmentplace leurs sommes de places
	// (le tout de faen ordonnee)
	for (j = 1; j < place->total; j++)
	{
		for (i = 0; i < classementplace->total; i++)
		{
			k = 0;
			if (*(int*)cvGetSeqElemLPR(classementplace, i, 0) >
				*(int*)cvGetSeqElemLPR(place, j, 0) || (i == classementplace->total))
			{
				cvSeqInsert(classementplace, i, cvGetSeqElemLPR(place, j, 0));
				cvSeqInsert(classement, (i << 2), cvGetSeqElemLPR(squares, (j << 2), 0));
				cvSeqInsert(classement, (i << 2) + 1, cvGetSeqElemLPR(squares, (j << 2) + 1, 0));
				cvSeqInsert(classement, (i << 2) + 2, cvGetSeqElemLPR(squares, (j << 2) + 2, 0));
				cvSeqInsert(classement, (i << 2) + 3, cvGetSeqElemLPR(squares, (j << 2) + 3, 0));
				k = 1;
			}
			if (k == 1)
				break;
		}
	}
	// on va se resservir de squares et note_plus_grand_cosinus (on les vide donc)
	cvClearSeq(squares);
	cvClearSeq(note_plus_grand_cosinus);
	//dans squares on stocke les rectangles moyens
	//dans centres leurs centres
	//dans note_plus_grand_cosinus, leurs pondeations
	//on eudie le quart supeieur des quadrilatees
	//(ou 1 si y en a - de 4)
	max = (classement->total) >> 4;
	//s'il n'y a pas 4 quadrilatees on les prends tous
	if (max < 1) max = ((classement->total) >> 2);
	k = 0;
	for (j = 0; j < max; j++)//j est le no du quadrilatee auquel on cherche un sosie
	{
		//pt[((j<<2))&3] est un des quatre points du quadrilatee courant.
		pt[((j << 2)) & 3] = *(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0);
		//centre est le centre du quadrilatee, il nous permet de l'identifier
		centre = moyenne4(pt[((j << 2)) & 3], pt[((j << 2) + 1) & 3], pt[((j << 2) + 2) & 3], pt[((j << 2) + 3) & 3]);
		if (k == 1)
		{
			m = 0;
			for (i = 0; i < centres->total; i++)// i est le no du qudrilatee qui est peut-ere un sosie
			{
				if (dist(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0)) < 10)
					//si les 2 centres sont emoins de 10 pixels d'intervalle
					//on fait la moyenne des centres
					//on fait la moyenne des rectangles 
					//le coef de pond augmente de 1
				{
					m = 1;
					pond = *(int*)cvGetSeqElemLPR(note_plus_grand_cosinus, i, 0) + 1;
					cvSeqRemove(note_plus_grand_cosinus, i);
					cvSeqInsert(note_plus_grand_cosinus, i, &pond);
					//dans centre on met la moyenne pondere des 2 centres
					//puis on stocke ce point dans la seq. centres
					centre = moyennepond(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0), 1, (pond - 1));
					cvSeqRemove(centres, i);
					cvSeqInsert(centres, i, &centre);
					//centre va servir echaque fois estocker les coordonnees de chaque coin du rectangle moyen
					for (l = 0; l < 4; l++)
					{
						//dans centre, on met les nouvelles coordonnees d'un coin du rectangle 
						//moyen c a d la moyenne pondere du point rectangle preedemment stockeet du 
						// point (du nouveau rectangle) le + proche
						centre = moyennepond(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
							trouveleplusproche(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 1, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 2, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 3, 0)), (pond - 1), 1);
						// et echaque fois on stocke le point qui vient d'ere calculedans squares
						cvSeqRemove(squares, (i << 2) + l);
						cvSeqInsert(squares, (i << 2) + l, &centre);
					}
				}
				//si on a associeles 2 rect., on break
				if (m == 1)
					break;
				// si on a pas trouveede sosie ej,
				// on le classe eune nouvelle place dans squares
				// avec une pondeation de 1
				if (i == centres->total)
				{
					cvSeqPush(centres, &centre);
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2), 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 1, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 2, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 3, 0));
					cvSeqPush(note_plus_grand_cosinus, &un);
				}
			}
		}
		if (k == 0)	// la 1 ere fois  qu on fait cette boucle 
					// on compte systematiquement le quadrilatee comme nouveau
					// donc on le stocke eune nouvelle place dans squares
					// et on lui met une pondeation de 1
		{
			cvSeqPush(centres, &centre);
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 0, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 1, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 2, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 3, 0));
			cvSeqPush(note_plus_grand_cosinus, &un);
			k = 1;
		}
	}
	// leon reclasse nos rectangles par ordre de pondeation
	k = 0; l = 0;
	for (j = 1; j < note_plus_grand_cosinus->total; j++)
	{
		i = *cvGetSeqElemLPR(note_plus_grand_cosinus, j, 0);
		if (i > k)
		{
			k = i;
			l = j;
		}
	}
	// et dans ce classement on garde que le meilleur
	cvClearSeq(classement);
	cvSeqPush(classement, cvGetSeqElemLPR(squares, (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3 + (l << 2), 0));
	//il ne reste qu'un quadrilatee. on le renvoie
	return classement;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage 
//peut traiter les images sources couleur ou en niveaux de gris
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage 
//peut traiter les images sources couleur ou en niveaux de gris
CvSeq* findSquares4
(IplImage* im_src, CvMemStorage* storage,
	const int nTresh, const int mean_carac
	, const int mean_fond, //
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	CvSeq* contours = 0;
	CvPoint centre;
	int  i, j, k, l, m,
		un = 1,
		pond, max;
	int thresh_canny = 50;
	CvSize sz = cvSize(im_src->width, im_src->height);
	//quelques images temporaires qui permettront de travailler
	IplImage* gray1 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	IplImage* tgray = cvCreateImage(sz, IPL_DEPTH_8U//im_src->depth
		, 1);
	IplImage* gray2 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	int filterSize = 5;
	IplConvKernel* convKernel = cvCreateStructuringElementEx
	(filterSize, filterSize, (filterSize - 1) / 2, (filterSize - 1) / 2,
		CV_SHAPE_ELLIPSE, nullptr);
#ifdef _DEBUG
	assert(im_src->nChannels == im_src->nChannels);
	assert(im_src->depth == im_src->depth);
	assert(im_src->align == im_src->align);
#endif //_DEBUG
	// squares et classement permettront de travailler sur les listes de quadrilatees
	CvSeq* squares = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	CvSeq* classement = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// centres est une liste de centres de quadrilatees utilisee ela fin
	CvSeq* centres = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_rapport_long_cotes_opposes = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_petit_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_grand_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* notesym = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	// diffeentes seuences contenant les places correspondant aux diffeents critees
	CvSeq* place_plus_grand_cosinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_rapport_long_cotes_opposes = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_petit_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_grand_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* placesym = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// place contiendra la liste de sommes de places
	CvSeq* place = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	//classementplace contiendra la liste des quadrilatees ordonnee selon les sommes de places
	CvSeq* classementplace = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// selectionne le + grand rectangle dans l'image
	// dont les dimensions sont divisibles par 2
#ifdef _DEBUG
	assert(im_src->width == sz.width);
	assert(im_src->height == sz.height);
#endif //_DEBUG
	//		cvSetImageROI( im_src, cvRect( 0, 0, sz.width, sz.height ));
	// pt servira eplusieurs reprises estocker des quadrilatees
	// (tableau de 4 points)
	CvPoint pt[4];
	// on teste toutes les couleurs R,G,B
	//const int sz.width=im_src->width;const int sz.height=im_src->height;
	cvCopy(im_src, tgray, 0);
	//si on est pas en NB
	//on recopie ds une image de travail
	bool prochaine_image_gray1 = true;
	// on teste N niveaux de seuillage
	// apply Canny. Take the upper threshold from slider
	// and set the lower to 0 (which forces edges merging) 
	cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
	);
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
	// trouve les contours et en fait une liste
	float cvApproxPoly_coeff = 0.02f;
	cvFindContours(gray1, storage, &contours, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
	// teste chaque contour
	//calcule le poids des contours
	init_contours_weight(tgray, dist_min_bord,
		hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
		sz.width, sz.height,
		storage, contours,
		squares,
		// diffeentes seuences contentant les notes correspondant aux diffeents critees 
		note_plus_grand_cosinus,
		note_rapport_long_cotes_opposes,
		note_plus_petit_sinus,
		note_plus_grand_sinus,
		notesym, cvApproxPoly_coeff);
	if (!squares->total) {
		cv::Mat img = cv::cvarrToMat(tgray);
		cv::Mat image_final = cv::cvarrToMat(gray1);
		int blockSize = (sz.width + sz.height) / 2;
		if (blockSize % 2 == 0) blockSize += 1;
		cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		// teste chaque contour
		//calcule le poids des contours
		int total_number_of_plates = squares->total;
		init_contours_weight(tgray, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		if (total_number_of_plates == squares->total) {
			int nb_pts_sup_threshold = (sz.width * sz.height);
			int new_mean_carac = mean_carac;
			int new_mean_fond = mean_fond;
			if (mean_carac > mean_fond) {
				new_mean_carac = mean_fond;
				new_mean_fond = mean_carac;
				new_mean_carac -= (mean_carac - mean_fond) / 10;
				new_mean_fond += (mean_carac - mean_fond) / 10;
			}
			else {
				new_mean_carac -= (mean_fond - mean_carac) / 10;
				new_mean_fond += (mean_fond - mean_carac) / 10;
			}
#ifdef _DEBUG
			assert(new_mean_carac <= new_mean_fond);
#endif //_DEBUG
			if (new_mean_carac < 0) new_mean_carac = 0;
			if (new_mean_fond > 255) new_mean_fond = 255;
			int new_nTresh = nTresh;
			if (new_nTresh > (new_mean_fond - new_mean_carac) >> 1)
				new_nTresh = (new_mean_fond - new_mean_carac) >> 1;
			int seuil;
			for (seuil = 0; seuil < new_nTresh; seuil++)
			{
				// sinon on applique le seuillage
				///((tgray(x,y) = gray(x,y) < (seuil+1)*255/N ? 255 : 0))
				int current_nb_pts_sup_threshold = 0;
				if (new_mean_carac < new_mean_fond)
					LPRThreshold(im_src, gray1,
						new_mean_carac + ((new_mean_fond - new_mean_carac) * seuil) / new_nTresh,
						current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
				else LPRThreshold(im_src, gray1,
					new_mean_fond + ((new_mean_carac - new_mean_fond) * seuil) / new_nTresh,
					current_nb_pts_sup_threshold);
				if (current_nb_pts_sup_threshold >= 0 &&
					current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
					///////////////////////////////////////////////////////////////////////////////////////////
					// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
					assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
					float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
						current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
					assert(percentage_match > -FLT_EPSILON &&
						percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
					if (percentage_match < 0.75) {
						nb_pts_sup_threshold = current_nb_pts_sup_threshold;
						cvFindContours(gray1, storage, &contours, sizeof(CvContour),
							CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
						total_number_of_plates = squares->total;
						init_contours_weight(tgray, dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees 
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);
						if (seuil == 0) cvApproxPoly_coeff = 0.02f;
						else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
						else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
						else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
														// teste chaque contour
														//calcule le poids des contours
						if (total_number_of_plates == squares->total) {
							cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
							cvFindContours(gray2, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							init_contours_weight(tgray, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
						}
					}
				}
			}
		}
	}
	// (on libee de la meoire)
	cvReleaseImage(&gray1);
	cvReleaseImage(&tgray);
	cvReleaseImage(&gray2);
	cvReleaseStructuringElement(&convKernel);
#ifdef _DEBUG
	assert(note_plus_grand_cosinus->total == note_rapport_long_cotes_opposes->total &&
		note_plus_grand_cosinus->total == note_plus_petit_sinus->total &&
		note_plus_grand_cosinus->total == note_plus_grand_sinus->total &&
		note_plus_grand_cosinus->total == notesym->total);
#endif //_DEBUG
	//si rien n'a passele filtre, on arree le:
	//on renvoie une seuence de -1
	if (notesym->total == 0)
	{
		cvClearSeq(classement);
		centre = cvPoint(SHRT_MIN, SHRT_MIN);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		return classement;
	}
	// CLASSEMENT
	// renvoie la seuence de places epartir de la seuence de notes
	place_plus_grand_cosinus = sort(note_plus_grand_cosinus, storage);
	place_rapport_long_cotes_opposes = sort(note_rapport_long_cotes_opposes, storage);
	place_plus_petit_sinus = sort(note_plus_petit_sinus, storage);
	place_plus_grand_sinus = sort(note_plus_grand_sinus, storage);
	placesym = sort(notesym, storage);
#ifdef _DEBUG
	assert(place_plus_grand_cosinus->total == place_rapport_long_cotes_opposes->total &&
		place_plus_grand_cosinus->total == place_plus_petit_sinus->total &&
		place_plus_grand_cosinus->total == place_plus_grand_sinus->total &&
		place_plus_grand_cosinus->total == placesym->total);
#endif //_DEBUG
	//dans place, on met la somme des places pour chaque quadrilatee
	for (i = 0; i < place_plus_grand_cosinus->total; i++)
	{
		j = (*(int*)cvGetSeqElemLPR(place_plus_grand_cosinus, i, 0) + *(int*)cvGetSeqElemLPR(place_rapport_long_cotes_opposes, i, 0) +
			*(int*)cvGetSeqElemLPR(place_plus_petit_sinus, i, 0) + *(int*)cvGetSeqElemLPR(place_plus_grand_sinus, i, 0) +
			*(int*)cvGetSeqElemLPR(placesym, i, 0));
		cvSeqPush(place, &j);
	}
	// on initialise classementplace et classement
	cvSeqPush(classementplace, cvGetSeqElemLPR(place, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3, 0));
	//dans classement on met les quadrilatees
	//et dans classmentplace leurs sommes de places
	// (le tout de faen ordonnee)
	for (j = 1; j < place->total; j++)
	{
		for (i = 0; i < classementplace->total; i++)
		{
			k = 0;
			if (*(int*)cvGetSeqElemLPR(classementplace, i, 0) >
				*(int*)cvGetSeqElemLPR(place, j, 0) || (i == classementplace->total))
			{
				cvSeqInsert(classementplace, i, cvGetSeqElemLPR(place, j, 0));
				cvSeqInsert(classement, (i << 2), cvGetSeqElemLPR(squares, (j << 2), 0));
				cvSeqInsert(classement, (i << 2) + 1, cvGetSeqElemLPR(squares, (j << 2) + 1, 0));
				cvSeqInsert(classement, (i << 2) + 2, cvGetSeqElemLPR(squares, (j << 2) + 2, 0));
				cvSeqInsert(classement, (i << 2) + 3, cvGetSeqElemLPR(squares, (j << 2) + 3, 0));
				k = 1;
			}
			if (k == 1)
				break;
		}
	}
	// on va se resservir de squares et note_plus_grand_cosinus (on les vide donc)
	cvClearSeq(squares);
	cvClearSeq(note_plus_grand_cosinus);
	//dans squares on stocke les rectangles moyens
	//dans centres leurs centres
	//dans note_plus_grand_cosinus, leurs pondeations
	//on eudie le quart supeieur des quadrilatees
	//(ou 1 si y en a - de 4)
	max = (classement->total) >> 4;
	//s'il n'y a pas 4 quadrilatees on les prends tous
	if (max < 1) max = ((classement->total) >> 2);
	k = 0;
	for (j = 0; j < max; j++)//j est le no du quadrilatee auquel on cherche un sosie
	{
		//pt[((j<<2))&3] est un des quatre points du quadrilatee courant.
		pt[((j << 2)) & 3] = *(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0);
		//centre est le centre du quadrilatee, il nous permet de l'identifier
		centre = moyenne4(pt[((j << 2)) & 3], pt[((j << 2) + 1) & 3], pt[((j << 2) + 2) & 3], pt[((j << 2) + 3) & 3]);
		if (k == 1)
		{
			m = 0;
			for (i = 0; i < centres->total; i++)// i est le no du qudrilatee qui est peut-ere un sosie
			{
				if (dist(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0)) < 10)
					//si les 2 centres sont emoins de 10 pixels d'intervalle
						//on fait la moyenne des centres
							//on fait la moyenne des rectangles 
								//le coef de pond augmente de 1
				{
					m = 1;
					pond = *(int*)cvGetSeqElemLPR(note_plus_grand_cosinus, i, 0) + 1;
					cvSeqRemove(note_plus_grand_cosinus, i);
					cvSeqInsert(note_plus_grand_cosinus, i, &pond);
					//dans centre on met la moyenne pondere des 2 centres
					//puis on stocke ce point dans la seq. centres
					centre = moyennepond(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0), 1, (pond - 1));
					cvSeqRemove(centres, i);
					cvSeqInsert(centres, i, &centre);
					//centre va servir echaque fois estocker les coordonnees de chaque coin du rectangle moyen
					for (l = 0; l < 4; l++)
					{
						//dans centre, on met les nouvelles coordonnees d'un coin du rectangle 
						//moyen c a d la moyenne pondere du point rectangle preedemment stockeet du 
						// point (du nouveau rectangle) le + proche
						centre = moyennepond(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
							trouveleplusproche(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 1, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 2, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 3, 0)), (pond - 1), 1);
						// et echaque fois on stocke le point qui vient d'ere calculedans squares
						cvSeqRemove(squares, (i << 2) + l);
						cvSeqInsert(squares, (i << 2) + l, &centre);
					}
				}
				//si on a associeles 2 rect., on break
				if (m == 1)
					break;
				// si on a pas trouveede sosie ej,
				// on le classe eune nouvelle place dans squares
				// avec une pondeation de 1
				if (i == centres->total)
				{
					cvSeqPush(centres, &centre);
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2), 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 1, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 2, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 3, 0));
					cvSeqPush(note_plus_grand_cosinus, &un);
				}
			}
		}
		if (k == 0)// la 1 ere fois  qu on fait cette boucle 
			// on compte systematiquement le quadrilatee comme nouveau
				// donc on le stocke eune nouvelle place dans squares
					// et on lui met une pondeation de 1
		{
			cvSeqPush(centres, &centre);
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 0, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 1, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 2, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 3, 0));
			cvSeqPush(note_plus_grand_cosinus, &un);
			k = 1;
		}
	}
	// leon reclasse nos rectangles par ordre de pondeation
	k = 0; l = 0;
	for (j = 1; j < note_plus_grand_cosinus->total; j++)
	{
		i = *cvGetSeqElemLPR(note_plus_grand_cosinus, j, 0);
		if (i > k)
		{
			k = i;
			l = j;
		}
	}
	// et dans ce classement on garde que le meilleur
	cvClearSeq(classement);
	cvSeqPush(classement, cvGetSeqElemLPR(squares, (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3 + (l << 2), 0));
	//il ne reste qu'un quadrilatee. on le renvoie
	return classement;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage 
//peut traiter les images sources couleur ou en niveaux de gris
void resize(CvSeq* fin, const float s)
{
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			pt0->x *= s;
			pt0->y *= s;
			CvPoint* pt1 = CV_GET_SEQ_ELEM(CvPoint, fin, 1);
			pt1->x *= s;
			pt1->y *= s;
			CvPoint* pt2 = CV_GET_SEQ_ELEM(CvPoint, fin, 2);
			pt2->x *= s;
			pt2->y *= s;
			CvPoint* pt3 = CV_GET_SEQ_ELEM(CvPoint, fin, 3);
			pt3->x *= s;
			pt3->y *= s;
		}
	}
}
float dist(const CvPoint* pt, const int i, const int j)
{
	return (pt->x - i) * (pt->x - i) + (pt->y - j) * (pt->y - j);
}
void update(const CvPoint* pt0,
	const CvPoint* pt1,
	const CvPoint* pt2,
	const CvPoint* pt3, const int i, const int j,
	cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
	float& min_d0, float& min_d1, float& min_d2, float& min_d3)
{
	float d0 = dist(pt0, i, j);
	float d1 = dist(pt1, i, j);
	float d2 = dist(pt2, i, j);
	float d3 = dist(pt3, i, j);
	if (d0 < d1) {//d0 and d2 are height and d1 , d3 are width
		if (d0 < d2) {
			if (d0 < d3) {
				if (d0 < min_d0) {
					min_d0 = d0;
					p0 = cv::Point(i, j);
				}
			}
			else {
				if (d3 < min_d3) {
					min_d3 = d3;
					p3 = cv::Point(i, j);
				}
			}
		}
		else {
			if (d2 < d3) {
				if (d2 < min_d2) {
					min_d2 = d2;
					p2 = cv::Point(i, j);
				}
			}
			else {
				if (d3 < min_d3) {
					min_d3 = d3;
					p3 = cv::Point(i, j);
				}
			}
		}
	}
	else {
		if (d1 < d2) {
			if (d1 < d3) {
				if (d1 < min_d1) {
					min_d1 = d1;
					p1 = cv::Point(i, j);
				}
			}
			else {
				if (d2 < min_d2) {
					min_d2 = d2;
					p2 = cv::Point(i, j);
				}
			}
		}
		else {
			if (d2 < d3) {
				if (d2 < min_d2) {
					min_d2 = d2;
					p2 = cv::Point(i, j);
				}
			}
			else {
				if (d3 < min_d3) {
					min_d3 = d3;
					p3 = cv::Point(i, j);
				}
			}
		}
	}
}
/** @function refine_cornerHarris */
void refine_cornerHarris(const IplImage* im_src, const CvSeq* fin, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3)
{
	cv::Mat dst, dst_norm, dst_norm_scaled, src_gray;
	src_gray = cv::cvarrToMat(im_src);
	dst = cv::Mat::zeros(src_gray.size(), CV_32FC1);
	/// Detector parameters
	int blockSize = 5;
	int apertureSize = 7;
	double k = 0.04;
	int thresh = 127;
	int max_thresh = 255;
	/// Detecting corners
	cv::cornerHarris(src_gray, dst, blockSize, apertureSize, k, cv::BORDER_DEFAULT);
	/// Normalizing
	cv::normalize(dst, dst_norm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
	cv::convertScaleAbs(dst_norm, dst_norm_scaled);
	float min_d0 = FLT_MAX, min_d1 = FLT_MAX, min_d2 = FLT_MAX, min_d3 = FLT_MAX;
	CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
	CvPoint* pt1 = CV_GET_SEQ_ELEM(CvPoint, fin, 1);
	CvPoint* pt2 = CV_GET_SEQ_ELEM(CvPoint, fin, 2);
	CvPoint* pt3 = CV_GET_SEQ_ELEM(CvPoint, fin, 3);
	p0.x = pt0->x; p0.y = pt0->y;
	p1.x = pt1->x; p1.y = pt1->y;
	p2.x = pt2->x; p2.y = pt2->y;
	p3.x = pt3->x; p3.y = pt3->y;
	/// Drawing a circle around corners
	std::list<float> median;
	for (int j = 0; j < dst_norm.rows; j++)
	{
		for (int i = 0; i < dst_norm.cols; i++)
		{
			median.push_back(dst_norm.at<float>(j, i));
		}
	}
	median.sort();
	int i = 0;
	std::list<float>::const_reverse_iterator it(median.rbegin());
	const int quartille = median.size() / 40;
	while (it != median.rend() && i < quartille) {
		//operator*(const C_Transform_2d& L, const C_Transform_2d& R)
		i++;
		thresh = *it;
		it++;
	}
	for (int j = 0; j < dst_norm.rows; j++)
	{
		for (int i = 0; i < dst_norm.cols; i++)
		{
			if (static_cast<int>(dst_norm.at<float>(j, i)) > thresh)
			{
				update(pt0, pt1, pt2, pt3, i, j, p0, p1, p2, p3, min_d0, min_d1, min_d2, min_d3);
			}
		}
	}
}
void refine_SIFT(const IplImage* im_src, const CvSeq* fin, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3)
{
	cv::Mat src_gray =
		cv::cvarrToMat(im_src);
	// Create smart pointer for SIFT feature detector.
	cv::Ptr<cv::FeatureDetector> featureDetector = cv::AKAZE::create();//cv::FeatureDetector::create("FAST");
	std::vector<cv::KeyPoint> keypoints;
	keypoints.reserve(10000);
	// Detect the keypoints
	featureDetector->detect(src_gray, keypoints); // NOTE: featureDetector is a pointer hence the '->'.
	float min_d0 = FLT_MAX, min_d1 = FLT_MAX, min_d2 = FLT_MAX, min_d3 = FLT_MAX;
	CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
	CvPoint* pt1 = CV_GET_SEQ_ELEM(CvPoint, fin, 1);
	CvPoint* pt2 = CV_GET_SEQ_ELEM(CvPoint, fin, 2);
	CvPoint* pt3 = CV_GET_SEQ_ELEM(CvPoint, fin, 3);
	p0.x = pt0->x; p0.y = pt0->y;
	p1.x = pt1->x; p1.y = pt1->y;
	p2.x = pt2->x; p2.y = pt2->y;
	p3.x = pt3->x; p3.y = pt3->y;
	std::vector<cv::KeyPoint>::const_iterator it(keypoints.begin());
	while (it != keypoints.end())
	{
		update(pt0, pt1, pt2, pt3, it->pt.x, it->pt.y, p0, p1, p2, p3, min_d0, min_d1, min_d2, min_d3);
		it++;
	}
}
/** @function refine_cornerHarris */
void refine_cornerHarris(const IplImage* im_src, CvSeq* fin)
{
	cv::Point p0, p1, p2, p3;
	refine_SIFT(im_src, fin, p0, p1, p2, p3);
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			pt0->x = p0.x;
			pt0->y = p0.y;
			CvPoint* pt1 = CV_GET_SEQ_ELEM(CvPoint, fin, 1);
			pt1->x = p1.x;
			pt1->y = p1.y;
			CvPoint* pt2 = CV_GET_SEQ_ELEM(CvPoint, fin, 2);
			pt2->x = p2.x;
			pt2->y = p2.y;
			CvPoint* pt3 = CV_GET_SEQ_ELEM(CvPoint, fin, 3);
			pt3->x = p3.x;
			pt3->y = p3.y;
		}
	}
}
CvSeq* findSquares4_multiresolution
(
	IplImage* im_src, CvMemStorage* storage,
	const int nTresh, const int mean_carac
	, const int mean_fond, //
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min
)
{
	CvSeq* fin = findSquares4
	(im_src, storage,
		nTresh, //
		mean_carac, mean_fond, dist_min_bord,
		hauteur_plaque_min,
		rapport_largeur_sur_hauteur_min);
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			if (pt0->x >= 0 && pt0->y >= 0)	return fin;
		}
	}
	if (im_src->width > MIN_WIDTH_FOR_A_LP && im_src->height > MIN_HEIGHT_FOR_A_LP) {
		cv::Point p0, p1, p2, p3;
		IplImage* new_img_2 = cvCreateImage(cvSize(im_src->width * 0.5, im_src->height * 0.5), im_src->depth, im_src->nChannels);
		cvResize(im_src, new_img_2, CV_INTER_CUBIC);
		CvSeq* fin = findSquares4
		(new_img_2, storage,
			nTresh, //
			mean_carac, mean_fond, dist_min_bord,
			hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
		if (fin) {
			if (fin->total == 4) {
				CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
				if (pt0->x >= 0 && pt0->y >= 0) {
					/*
					#ifdef _DEBUG
					cv::Mat tmp=new_img_2;
					imshow("new_img_2",tmp);
					char c = cv::waitKey(0);
					#endif //_DEBUG
					*/
					resize(fin, 2.0);
					refine_cornerHarris(im_src, fin);
					return fin;
				}
			}
		}
		if (new_img_2->width > MIN_WIDTH_FOR_A_LP && new_img_2->height > MIN_HEIGHT_FOR_A_LP) {
			IplImage* new_img_4 = cvCreateImage(cvSize(new_img_2->width * 0.5, new_img_2->height * 0.5), new_img_2->depth, new_img_2->nChannels);
			cvResize(new_img_2, new_img_4, CV_INTER_CUBIC);
			CvSeq* fin = findSquares4
			(new_img_4, storage,
				nTresh, //
				mean_carac, mean_fond, dist_min_bord,
				hauteur_plaque_min,
				rapport_largeur_sur_hauteur_min);
			if (fin) {
				if (fin->total == 4) {
					CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
					if (pt0->x >= 0 && pt0->y >= 0) {
						/*
						#ifdef _DEBUG
						cv::Mat tmp=new_img_4;
						imshow("new_img_4",tmp);
						char c = cv::waitKey(0);
						#endif //_DEBUG
						*/
						resize(fin, 4.0);
						refine_cornerHarris(im_src, fin);
					}
				}
			}
			return fin;
		}
		else {
			return fin;
		}
	}
	else {
		return fin;
	}
}
CvSeq* findSquares4_multiresolution
(
	IplImage* im_src, const cv::Rect& global_rect, CvMemStorage* storage,
	const int nTresh, const int mean_carac
	, const int mean_fond, //
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min
)
{
	CvSeq* fin = findSquares4
	(im_src, global_rect, storage,
		nTresh, //
		mean_carac, mean_fond, dist_min_bord,
		hauteur_plaque_min,
		rapport_largeur_sur_hauteur_min);
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			if (pt0->x >= 0 && pt0->y >= 0) {
#ifdef _DEBUG
				cv::RotatedRect RotatedRect_(get_rect(fin));
				cv::Rect global_rotated_rect = RotatedRect_.boundingRect();
				float iou__ = iou_(global_rect, global_rotated_rect);
				//assert(iou__ >= 0.3f);
#endif //_DEBUG
				return fin;
			}
		}
	}
	if (im_src->width > MIN_WIDTH_FOR_A_LP && im_src->height > MIN_HEIGHT_FOR_A_LP) {
		cv::Point p0, p1, p2, p3;
		IplImage* new_img_2 = cvCreateImage(cvSize(im_src->width * 0.5f, im_src->height * 0.5), im_src->depth, im_src->nChannels);
		cvResize(im_src, new_img_2, CV_INTER_CUBIC);
		CvSeq* fin = findSquares4
		(new_img_2, cv::Rect(global_rect.x * 0.5f, global_rect.x * 0.5f, global_rect.width * 0.5f, global_rect.height * 0.5f), storage,
			nTresh, //
			mean_carac, mean_fond, dist_min_bord,
			hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
		if (fin) {
			if (fin->total == 4) {
				CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
				if (pt0->x >= 0 && pt0->y >= 0) {
					/*
					#ifdef _DEBUG
										cv::Mat tmp=new_img_2;
										imshow("new_img_2",tmp);
										char c = cv::waitKey(0);
					#endif //_DEBUG
										*/
					resize(fin, 2.0);
					refine_cornerHarris(im_src, fin);
#ifdef _DEBUG
					cv::RotatedRect RotatedRect_(get_rect(fin));
					cv::Rect global_rotated_rect = RotatedRect_.boundingRect();
					float iou__ = iou_(cv::Rect(global_rect.x * 0.5f, global_rect.x * 0.5f, global_rect.width * 0.5f, global_rect.height * 0.5f), global_rotated_rect);
					//assert(iou__ >= 0.3f);
#endif //_DEBUG
					return fin;
				}
			}
		}
		if (new_img_2->width > MIN_WIDTH_FOR_A_LP && new_img_2->height > MIN_HEIGHT_FOR_A_LP) {
			IplImage* new_img_4 = cvCreateImage(cvSize(new_img_2->width * 0.5, new_img_2->height * 0.5), new_img_2->depth, new_img_2->nChannels);
			cvResize(new_img_2, new_img_4, CV_INTER_CUBIC);
			CvSeq* fin = findSquares4
			(new_img_4, cv::Rect(global_rect.x * 0.25f, global_rect.x * 0.25f, global_rect.width * 0.25f, global_rect.height * 0.25f), storage,
				nTresh, //
				mean_carac, mean_fond, dist_min_bord,
				hauteur_plaque_min,
				rapport_largeur_sur_hauteur_min);
			if (fin) {
				if (fin->total == 4) {
					CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
					if (pt0->x >= 0 && pt0->y >= 0) {
						/*
						#ifdef _DEBUG
						cv::Mat tmp=new_img_4;
						imshow("new_img_4",tmp);
						char c = cv::waitKey(0);
						#endif //_DEBUG
						*/
#ifdef _DEBUG
						cv::RotatedRect RotatedRect_(get_rect(fin));
						cv::Rect global_rotated_rect = RotatedRect_.boundingRect();
						float iou__ = iou_(cv::Rect(global_rect.x * 0.25f, global_rect.x * 0.25f, global_rect.width * 0.25f, global_rect.height * 0.25f), global_rotated_rect);
						assert(iou__ >= 0.2f);
#endif //_DEBUG
						resize(fin, 4.0);
						refine_cornerHarris(im_src, fin);
					}
				}
			}
			return fin;
		}
		else {
			return fin;
		}
	}
	else {
		return fin;
	}
}
CvSeq* findSquares4
(IplImage* im_src, CvMemStorage* storage,
	const int nTresh, //
	//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
	const int nb_composantes_color,
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	const int mean_carac = 50;
	const int mean_fond = 206;
	if (nb_composantes_color == 1) {
		return findSquares4
		(im_src, storage,
			nTresh, //
			mean_carac
			, mean_fond,
			dist_min_bord,
			hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
	}
	else	if (nb_composantes_color != 3) {
		return nullptr;
	}
	CvSeq* contours = 0;
	CvPoint centre;
	int  i, j, k, l, m,
		un = 1,
		pond, max;
	/*	*/
	int thresh_canny = 50;
	CvSize sz = cvSize(im_src->width, im_src->height);
	//quelques images temporaires qui permettront de travailler
	//		IplImage* im_copy = cvCloneImage( im_src ); // make a copy of input image
	IplImage* gray1 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	IplImage* tgray = cvCreateImage(sz, IPL_DEPTH_8U//im_src->depth
		, 1);
	IplImage* gray2 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	int filterSize = 5;
	IplConvKernel* convKernel = cvCreateStructuringElementEx
	(filterSize, filterSize, (filterSize - 1) / 2, (filterSize - 1) / 2,
		CV_SHAPE_ELLIPSE, nullptr);
#ifdef _DEBUG
	assert(im_src->nChannels == im_src->nChannels);
	assert(im_src->depth == im_src->depth);
	assert(im_src->align == im_src->align);
#endif //_DEBUG
	// squares et classement permettront de travailler sur les listes de quadrilatees
	CvSeq* squares = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	CvSeq* classement = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// centres est une liste de centres de quadrilatees utilisee ela fin
	CvSeq* centres = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_rapport_long_cotes_opposes = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_petit_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_grand_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* notesym = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	// diffeentes seuences contenant les places correspondant aux diffeents critees
	CvSeq* place_plus_grand_cosinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_rapport_long_cotes_opposes = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_petit_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_grand_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* placesym = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// place contiendra la liste de sommes de places
	CvSeq* place = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	//classementplace contiendra la liste des quadrilatees ordonnee selon les sommes de places
	CvSeq* classementplace = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// selectionne le + grand rectangle dans l'image
	// dont les dimensions sont divisibles par 2
	//	cvSetImageROI( im_src, cvRect( 0, 0, sz.width, sz.height ));
	// pt servira eplusieurs reprises estocker des quadrilatees
	// (tableau de 4 points)
	CvPoint pt[4];
	// on teste toutes les couleurs R,G,B
	//const int sz.width=im_src->width;const int sz.height=im_src->height;
	for (int c = 0; c < nb_composantes_color; c++)
	{
		//si on est pas en NB
		if (nb_composantes_color != 1) {
			if (im_src->nChannels == nb_composantes_color && nb_composantes_color != 1)
				//on extrait la composante dans la couleur choisie
				cvSetImageCOI(im_src, c + 1);// evoir s'il ne faut pas mettre c+1 cvSetImageCOI( im_src, c+1 );
			else if (im_src->nChannels == nb_composantes_color + 1)
				cvSetImageCOI(im_src, c + 2);
		}
		//on recopie ds une image de travail
		cvCopy(im_src, tgray, 0);
		// apply Canny. Take the upper threshold from slider
		// and set the lower to 0 (which forces edges merging) 
		cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
		);
		// dilate canny output to remove potential
		// holes between edge segments 
		//cvErode( gray1, gray1, 0, 1 );//a rajouter
		// trouve les contours et en fait une liste
		float cvApproxPoly_coeff = 0.02f;
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		// teste chaque contour
		//calcule le poids des contours
		init_contours_weight(tgray, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		if (!squares->total) {
			/*
			Mat cv::cvarrToMat 	( 	const CvArr *  	arr,
			bool  	copyData = false,
			bool  	allowND = true,
			int  	coiMode = 0,
			AutoBuffer< double > *  	buf = 0
			)
			*/
			cv::Mat img = cv::cvarrToMat(tgray);
			cv::Mat image_final = cv::cvarrToMat(gray1);
			int blockSize = (sz.width + sz.height) / 2;
			if (blockSize % 2 == 0) blockSize += 1;
			cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
			cvFindContours(gray1, storage, &contours, sizeof(CvContour),
				CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
			// teste chaque contour
			//calcule le poids des contours
			int total_number_of_plates = squares->total;
			init_contours_weight(tgray, dist_min_bord,
				hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
				sz.width, sz.height,
				storage, contours,
				squares,
				// diffeentes seuences contentant les notes correspondant aux diffeents critees 
				note_plus_grand_cosinus,
				note_rapport_long_cotes_opposes,
				note_plus_petit_sinus,
				note_plus_grand_sinus,
				notesym, cvApproxPoly_coeff);
			if (total_number_of_plates == squares->total) {
				int nb_pts_sup_threshold = (sz.width * sz.height);
				// on teste N niveaux de seuillage
				int seuil;
				for (seuil = 0; seuil < nTresh; seuil++)
				{
					int current_nb_pts_sup_threshold = 0;
					LPRThreshold(im_src, gray1, ((seuil + 1) * 200 / nTresh) + 25,
						current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
					if (current_nb_pts_sup_threshold >= 0 &&
						current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
						///////////////////////////////////////////////////////////////////////////////////////////
						// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
						assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
						float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
							current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
						assert(percentage_match > -FLT_EPSILON &&
							percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
						if (percentage_match < 0.75) {
							cvFindContours(gray1, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							nb_pts_sup_threshold = current_nb_pts_sup_threshold;
							if (seuil == 0) cvApproxPoly_coeff = 0.02f;
							else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
							else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
							else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
							total_number_of_plates = squares->total;
							init_contours_weight(tgray, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
							if (total_number_of_plates == squares->total) {
								cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
								cvFindContours(gray2, storage, &contours, sizeof(CvContour),
									CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
								init_contours_weight(tgray, dist_min_bord,
									hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
									sz.width, sz.height,
									storage, contours,
									squares,
									// diffeentes seuences contentant les notes correspondant aux diffeents critees 
									note_plus_grand_cosinus,
									note_rapport_long_cotes_opposes,
									note_plus_petit_sinus,
									note_plus_grand_sinus,
									notesym, cvApproxPoly_coeff);
							}
						}
					}
				}
			}
		}
	}
	// (on libee de la meoire)
	cvReleaseImage(&gray1);
	//	cvReleaseImage( &pyr );
	cvReleaseImage(&tgray);
	//		cvReleaseImage( &im_copy );
	cvReleaseImage(&gray2);
	cvReleaseStructuringElement(&convKernel);
#ifdef _DEBUG
	assert(note_plus_grand_cosinus->total == note_rapport_long_cotes_opposes->total &&
		note_plus_grand_cosinus->total == note_plus_petit_sinus->total &&
		note_plus_grand_cosinus->total == note_plus_grand_sinus->total &&
		note_plus_grand_cosinus->total == notesym->total);
#endif //_DEBUG
	//si rien n'a passele filtre, on arree le:
	//on renvoie une seuence de -1
	if (notesym->total == 0)
	{
		cvClearSeq(classement);
		centre = cvPoint(SHRT_MIN, SHRT_MIN);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		return classement;
	}
	// CLASSEMENT
	// renvoie la seuence de places epartir de la seuence de notes
	place_plus_grand_cosinus = sort(note_plus_grand_cosinus, storage);
	place_rapport_long_cotes_opposes = sort(note_rapport_long_cotes_opposes, storage);
	place_plus_petit_sinus = sort(note_plus_petit_sinus, storage);
	place_plus_grand_sinus = sort(note_plus_grand_sinus, storage);
	placesym = sort(notesym, storage);
#ifdef _DEBUG
	assert(place_plus_grand_cosinus->total == place_rapport_long_cotes_opposes->total &&
		place_plus_grand_cosinus->total == place_plus_petit_sinus->total &&
		place_plus_grand_cosinus->total == place_plus_grand_sinus->total &&
		place_plus_grand_cosinus->total == placesym->total);
#endif //_DEBUG
	//dans place, on met la somme des places pour chaque quadrilatee
	for (i = 0; i < place_plus_grand_cosinus->total; i++)
	{
		j = (*(int*)cvGetSeqElemLPR(place_plus_grand_cosinus, i, 0) + *(int*)cvGetSeqElemLPR(place_rapport_long_cotes_opposes, i, 0) +
			*(int*)cvGetSeqElemLPR(place_plus_petit_sinus, i, 0) + *(int*)cvGetSeqElemLPR(place_plus_grand_sinus, i, 0) +
			*(int*)cvGetSeqElemLPR(placesym, i, 0));
		cvSeqPush(place, &j);
	}
	// on initialise classementplace et classement
	cvSeqPush(classementplace, cvGetSeqElemLPR(place, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3, 0));
	//dans classement on met les quadrilatees
	//et dans classmentplace leurs sommes de places
	// (le tout de faen ordonnee)
	for (j = 1; j < place->total; j++)
	{
		for (i = 0; i < classementplace->total; i++)
		{
			k = 0;
			if (*(int*)cvGetSeqElemLPR(classementplace, i, 0) >
				*(int*)cvGetSeqElemLPR(place, j, 0) || (i == classementplace->total))
			{
				cvSeqInsert(classementplace, i, cvGetSeqElemLPR(place, j, 0));
				cvSeqInsert(classement, (i << 2), cvGetSeqElemLPR(squares, (j << 2), 0));
				cvSeqInsert(classement, (i << 2) + 1, cvGetSeqElemLPR(squares, (j << 2) + 1, 0));
				cvSeqInsert(classement, (i << 2) + 2, cvGetSeqElemLPR(squares, (j << 2) + 2, 0));
				cvSeqInsert(classement, (i << 2) + 3, cvGetSeqElemLPR(squares, (j << 2) + 3, 0));
				k = 1;
			}
			if (k == 1)
				break;
		}
	}
	// on va se resservir de squares et note_plus_grand_cosinus (on les vide donc)
	cvClearSeq(squares);
	cvClearSeq(note_plus_grand_cosinus);
	//dans squares on stocke les rectangles moyens
	//dans centres leurs centres
	//dans note_plus_grand_cosinus, leurs pondeations
	//on eudie le quart supeieur des quadrilatees
	//(ou 1 si y en a - de 4)
	max = (classement->total) >> 4;
	//s'il n'y a pas 4 quadrilatees on les prends tous
	if (max < 1) max = ((classement->total) >> 2);
	k = 0;
	for (j = 0; j < max; j++)//j est le no du quadrilatee auquel on cherche un sosie
	{
		//pt[((j<<2))&3] est un des quatre points du quadrilatee courant.
		pt[((j << 2)) & 3] = *(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0);
		//centre est le centre du quadrilatee, il nous permet de l'identifier
		centre = moyenne4(pt[((j << 2)) & 3], pt[((j << 2) + 1) & 3], pt[((j << 2) + 2) & 3], pt[((j << 2) + 3) & 3]);
		if (k == 1)
		{
			m = 0;
			for (i = 0; i < centres->total; i++)// i est le no du qudrilatee qui est peut-ere un sosie
			{
				if (dist(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0)) < 10)
					//si les 2 centres sont emoins de 10 pixels d'intervalle
					//on fait la moyenne des centres
					//on fait la moyenne des rectangles 
					//le coef de pond augmente de 1
				{
					m = 1;
					pond = *(int*)cvGetSeqElemLPR(note_plus_grand_cosinus, i, 0) + 1;
					cvSeqRemove(note_plus_grand_cosinus, i);
					cvSeqInsert(note_plus_grand_cosinus, i, &pond);
					//dans centre on met la moyenne pondere des 2 centres
					//puis on stocke ce point dans la seq. centres
					centre = moyennepond(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0), 1, (pond - 1));
					cvSeqRemove(centres, i);
					cvSeqInsert(centres, i, &centre);
					//centre va servir echaque fois estocker les coordonnees de chaque coin du rectangle moyen
					for (l = 0; l < 4; l++)
					{
						//dans centre, on met les nouvelles coordonnees d'un coin du rectangle 
						//moyen c a d la moyenne pondere du point rectangle preedemment stockeet du 
						// point (du nouveau rectangle) le + proche
						centre = moyennepond(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
							trouveleplusproche(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 1, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 2, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 3, 0)), (pond - 1), 1);
						// et echaque fois on stocke le point qui vient d'ere calculedans squares
						cvSeqRemove(squares, (i << 2) + l);
						cvSeqInsert(squares, (i << 2) + l, &centre);
					}
				}
				//si on a associeles 2 rect., on break
				if (m == 1)
					break;
				// si on a pas trouveede sosie ej,
				// on le classe eune nouvelle place dans squares
				// avec une pondeation de 1
				if (i == centres->total)
				{
					cvSeqPush(centres, &centre);
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2), 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 1, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 2, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 3, 0));
					cvSeqPush(note_plus_grand_cosinus, &un);
				}
			}
		}
		if (k == 0)	// la 1 ere fois  qu on fait cette boucle 
					// on compte systematiquement le quadrilatee comme nouveau
					// donc on le stocke eune nouvelle place dans squares
					// et on lui met une pondeation de 1
		{
			cvSeqPush(centres, &centre);
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 0, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 1, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 2, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 3, 0));
			cvSeqPush(note_plus_grand_cosinus, &un);
			k = 1;
		}
	}
	// leon reclasse nos rectangles par ordre de pondeation
	k = 0; l = 0;
	for (j = 1; j < note_plus_grand_cosinus->total; j++)
	{
		i = *cvGetSeqElemLPR(note_plus_grand_cosinus, j, 0);
		if (i > k)
		{
			k = i;
			l = j;
		}
	}
	// et dans ce classement on garde que le meilleur
	cvClearSeq(classement);
	cvSeqPush(classement, cvGetSeqElemLPR(squares, (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3 + (l << 2), 0));	
	//il ne reste qu'un quadrilatee. on le renvoie
	return classement;
}
CvSeq* findSquares4_multiresolution
(
	IplImage* im_src, CvMemStorage* storage,
	const int nTresh, //
	//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
	const int nb_composantes_color,
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min
)
{
#ifdef _DEBUG	
	//show_cornerHarris(im_src);
#endif //_DEBUG
	CvSeq* fin = findSquares4
	(im_src, storage,
		nTresh, //
		nb_composantes_color, dist_min_bord,
		hauteur_plaque_min,
		rapport_largeur_sur_hauteur_min);
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			if (pt0->x >= 0 && pt0->y >= 0)	return fin;
		}
	}
	if (im_src->width > MIN_WIDTH_FOR_A_LP && im_src->height > MIN_HEIGHT_FOR_A_LP) {
		cv::Point p0, p1, p2, p3;
		IplImage* new_img_2 = cvCreateImage(cvSize(im_src->width * 0.5, im_src->height * 0.5), im_src->depth, im_src->nChannels);
		cvResize(im_src, new_img_2, CV_INTER_CUBIC);
		CvSeq* fin = findSquares4
		(new_img_2, storage,
			nTresh, //
			nb_composantes_color, dist_min_bord,
			hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
		if (fin) {
			if (fin->total == 4) {
				CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
				if (pt0->x >= 0 && pt0->y >= 0) {
					resize(fin, 2.0);
					refine_cornerHarris(im_src, fin);
					return fin;
				}
			}
		}
		if (new_img_2->width > MIN_WIDTH_FOR_A_LP && new_img_2->height > MIN_HEIGHT_FOR_A_LP) {
			IplImage* new_img_4 = cvCreateImage(cvSize(new_img_2->width * 0.5, new_img_2->height * 0.5), new_img_2->depth, new_img_2->nChannels);
			cvResize(new_img_2, new_img_4, CV_INTER_CUBIC);
			CvSeq* fin = findSquares4
			(new_img_4, storage,
				nTresh, //
				nb_composantes_color, dist_min_bord,
				hauteur_plaque_min,
				rapport_largeur_sur_hauteur_min);
			if (fin) {
				if (fin->total == 4) {
					CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
					if (pt0->x >= 0 && pt0->y >= 0) {
						resize(fin, 4.0);
						refine_cornerHarris(im_src, fin);
					}
				}
			}
			return fin;
		}
		else {
			return fin;
		}
	}
	else {
		return fin;
	}
}
CvSeq* findSquares4_multiresolution
(IplImage* imgR, IplImage* imgG, IplImage* imgB, CvMemStorage* storage,
	const int nTresh, //
	//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	CvSeq* fin = findSquares4
	(imgR, imgG, imgB, storage,
		nTresh, //
		dist_min_bord,
		hauteur_plaque_min,
		rapport_largeur_sur_hauteur_min);
	if (fin) {
		if (fin->total == 4) {
			CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
			if (pt0->x >= 0 && pt0->y >= 0)	return fin;
		}
	}
	if (imgR->width > MIN_WIDTH_FOR_A_LP && imgR->height > MIN_HEIGHT_FOR_A_LP) {
		cv::Point p0, p1, p2, p3;
		IplImage* new_img_2R = cvCreateImage(cvSize(imgR->width * 0.5, imgR->height * 0.5), imgR->depth, imgR->nChannels);
		cvResize(imgR, new_img_2R, CV_INTER_CUBIC);
		IplImage* new_img_2G = cvCreateImage(cvSize(imgR->width * 0.5, imgR->height * 0.5), imgR->depth, imgR->nChannels);
		cvResize(imgG, new_img_2G, CV_INTER_CUBIC);
		IplImage* new_img_2B = cvCreateImage(cvSize(imgR->width * 0.5, imgR->height * 0.5), imgR->depth, imgR->nChannels);
		cvResize(imgB, new_img_2B, CV_INTER_CUBIC);
		CvSeq* fin = findSquares4
		(new_img_2R, new_img_2G, new_img_2B
			, storage,
			nTresh, //
			dist_min_bord,
			hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
		if (fin) {
			if (fin->total == 4) {
				CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
				if (pt0->x >= 0 && pt0->y >= 0) {
#ifdef _DEBUG	
					/*cv::Mat tmp=new_img_2R;
					imshow("new_img_2R",tmp);
					char c = cv::waitKey(0);
					tmp=new_img_2G;
					imshow("new_img_2G",tmp);
					c = cv::waitKey(0);
					tmp=new_img_2B;
					imshow("new_img_2B",tmp);
					c = cv::waitKey(0);*/
#endif //_DEBUG
					resize(fin, 2.0);
					refine_cornerHarris(imgG, fin);
					return fin;
				}
			}
		}
		if (new_img_2B->width > MIN_WIDTH_FOR_A_LP && new_img_2B->height > MIN_HEIGHT_FOR_A_LP) {
			IplImage* new_img_4R = cvCreateImage(cvSize(new_img_2B->width * 0.5, new_img_2B->height * 0.5), new_img_2B->depth, new_img_2B->nChannels);
			cvResize(new_img_2R, new_img_4R, CV_INTER_CUBIC);
			IplImage* new_img_4G = cvCreateImage(cvSize(new_img_2B->width * 0.5, new_img_2B->height * 0.5), new_img_2B->depth, new_img_2B->nChannels);
			cvResize(new_img_2G, new_img_4G, CV_INTER_CUBIC);
			IplImage* new_img_4B = cvCreateImage(cvSize(new_img_2B->width * 0.5, new_img_2B->height * 0.5), new_img_2B->depth, new_img_2B->nChannels);
			cvResize(new_img_2B, new_img_4B, CV_INTER_CUBIC);
			CvSeq* fin = findSquares4
			(new_img_4R, new_img_4G, new_img_4B, storage,
				nTresh, //
				dist_min_bord,
				hauteur_plaque_min,
				rapport_largeur_sur_hauteur_min);
			if (fin) {
				if (fin->total == 4) {
					CvPoint* pt0 = CV_GET_SEQ_ELEM(CvPoint, fin, 0);
					if (pt0->x >= 0 && pt0->y >= 0) {
#ifdef _DEBUG	
						/*cv::Mat tmp=new_img_4R;
						imshow("new_img_4R",tmp);
						char c = cv::waitKey(0);
						tmp=new_img_4G;
						imshow("new_img_4G",tmp);
						c = cv::waitKey(0);
						tmp=new_img_4B;
						imshow("new_img_4B",tmp);
						c = cv::waitKey(0);*/
#endif //_DEBUG
						resize(fin, 4.0);
						refine_cornerHarris(imgG, fin);
						return fin;
					}
				}
			}
		}
	}
	return fin;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage 
//peut traiter les images sources couleur ou en niveaux de gris
CvSeq* findSquares4
(IplImage* imgR, IplImage* imgG, IplImage* imgB, CvMemStorage* storage,
	const int nTresh, //
	//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
	const int dist_min_bord,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	if (imgR->width != imgG->width || imgR->width != imgB->width
		|| imgG->width != imgB->width
		||
		imgR->height != imgG->height || imgR->height != imgB->height
		|| imgG->height != imgB->height) {
		return nullptr;
	}
	CvSeq* contours = 0;
	CvPoint centre;
	int  i, j, k, l, m,
		un = 1,
		pond, max;
	/*	*/
	int thresh_canny = 50;
	CvSize sz = cvSize(imgR->width, imgR->height);
	//quelques images temporaires qui permettront de travailler
	IplImage* gray1 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	IplImage* tgray = cvCreateImage(sz, IPL_DEPTH_8U//im_src->depth
		, 1);
	IplImage* gray2 = cvCreateImage(sz, IPL_DEPTH_8U//unsigned 8-bit integers im_src->depth
		, 1);
	int filterSize = 5;
	IplConvKernel* convKernel = cvCreateStructuringElementEx
	(filterSize, filterSize, (filterSize - 1) / 2, (filterSize - 1) / 2,
		CV_SHAPE_ELLIPSE, nullptr);
	// squares et classement permettront de travailler sur les listes de quadrilatees
	CvSeq* squares = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	CvSeq* classement = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// centres est une liste de centres de quadrilatees utilisee ela fin
	CvSeq* centres = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	// diffeentes seuences contentant les notes correspondant aux diffeents critees 
	CvSeq* note_plus_grand_cosinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_rapport_long_cotes_opposes = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_petit_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* note_plus_grand_sinus = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	CvSeq* notesym = cvCreateSeq(CV_32FC1, sizeof(CvSeq), sizeof(float), storage);
	// diffeentes seuences contenant les places correspondant aux diffeents critees
	CvSeq* place_plus_grand_cosinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_rapport_long_cotes_opposes = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_petit_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* place_plus_grand_sinus = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeq* placesym = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// place contiendra la liste de sommes de places
	CvSeq* place = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	//classementplace contiendra la liste des quadrilatees ordonnee selon les sommes de places
	CvSeq* classementplace = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	// selectionne le + grand rectangle dans l'image
	// dont les dimensions sont divisibles par 2
	cvSetImageROI(imgR, cvRect(0, 0, sz.width, sz.height));
	cvSetImageROI(imgG, cvRect(0, 0, sz.width, sz.height));
	cvSetImageROI(imgB, cvRect(0, 0, sz.width, sz.height));
	// pt servira eplusieurs reprises estocker des quadrilatees
	// (tableau de 4 points)
	CvPoint pt[4];
	int nb_pts_sup_threshold = (sz.width * sz.height);
	int seuil;
	// on teste toutes les couleurs R,G,B
	//const int sz.width=imgR->width;const int sz.height=imgR->height;
	cvCopy(imgR, tgray, 0);
	// apply Canny. Take the upper threshold from slider
	// and set the lower to 0 (which forces edges merging) 
	/*cvCanny( tgray, gray1, (thresh_canny>>1), 0, 3//Aperture parameter for Sobel operator
	);*/
	cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
	);
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
	float cvApproxPoly_coeff = 0.02f;
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
	// trouve les contours et en fait une liste
	cvFindContours(gray1, storage, &contours, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
	// teste chaque contour
	//calcule le poids des contours
	init_contours_weight(tgray, dist_min_bord,
		hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
		sz.width, sz.height,
		storage, contours,
		squares,
		// diffeentes seuences contentant les notes correspondant aux diffeents critees 
		note_plus_grand_cosinus,
		note_rapport_long_cotes_opposes,
		note_plus_petit_sinus,
		note_plus_grand_sinus,
		notesym, cvApproxPoly_coeff);
	if (!squares->total) {
		/*
		Mat cv::cvarrToMat 	( 	const CvArr *  	arr,
		bool  	copyData = false,
		bool  	allowND = true,
		int  	coiMode = 0,
		AutoBuffer< double > *  	buf = 0
		)
		*/
		cv::Mat img = cv::cvarrToMat(tgray);
		cv::Mat image_final = cv::cvarrToMat(gray1);
		int blockSize = (sz.width + sz.height) / 2;
		if (blockSize % 2 == 0) blockSize += 1;
		cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		int total_number_of_plates = squares->total;
		init_contours_weight(tgray, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		// teste chaque contour
		//calcule le poids des contours
		if (total_number_of_plates == squares->total) {
			// on teste N niveaux de seuillage
			for (seuil = 0; seuil < nTresh; seuil++)
			{
				int current_nb_pts_sup_threshold = 0;
				LPRThreshold(imgR, gray1, ((seuil + 1) * 200 / nTresh) + 25,
					current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
				if (current_nb_pts_sup_threshold >= 0 &&
					current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
					///////////////////////////////////////////////////////////////////////////////////////////
					// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
					assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
					float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
						current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
					assert(percentage_match > -FLT_EPSILON &&
						percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
					if (percentage_match < 0.75) {
						cvFindContours(gray1, storage, &contours, sizeof(CvContour),
							CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
						nb_pts_sup_threshold = current_nb_pts_sup_threshold;
						if (seuil == 0) cvApproxPoly_coeff = 0.02f;
						else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
						else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
						else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
						int total_number_of_plates = squares->total;
						init_contours_weight(tgray, dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees 
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);
							// teste chaque contour
							//calcule le poids des contours
							/*init_contours_weight(tgray,dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);*/
						if (total_number_of_plates == squares->total) {
							cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
							cvFindContours(gray2, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							init_contours_weight(tgray, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
						}
					}
				}
			}
		}
	}
	cvCopy(imgG, tgray, 0);
	// apply Canny. Take the upper threshold from slider
	// and set the lower to 0 (which forces edges merging) 
	cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
	);
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
	// trouve les contours et en fait une liste
	cvFindContours(gray1, storage, &contours, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
	int total_number_of_plates = squares->total;
	cvApproxPoly_coeff = 0.02f;
	// teste chaque contour
	//calcule le poids des contours
	init_contours_weight(tgray, dist_min_bord,
		hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
		sz.width, sz.height,
		storage, contours,
		squares,
		// diffeentes seuences contentant les notes correspondant aux diffeents critees 
		note_plus_grand_cosinus,
		note_rapport_long_cotes_opposes,
		note_plus_petit_sinus,
		note_plus_grand_sinus,
		notesym, cvApproxPoly_coeff);
	if (!squares->total) {
		/*
		Mat cv::cvarrToMat 	( 	const CvArr *  	arr,
		bool  	copyData = false,
		bool  	allowND = true,
		int  	coiMode = 0,
		AutoBuffer< double > *  	buf = 0
	)
	*/
		cv::Mat img = cv::cvarrToMat(tgray);
		cv::Mat image_final = cv::cvarrToMat(gray1);
		int blockSize = (sz.width + sz.height) / 2;
		if (blockSize % 2 == 0) blockSize += 1;
		cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
#ifdef LPR_SAVE_DISPLAY
		cv::Mat debug = cv::cvarrToMat(gray1);
		imshow("adaptiveThreshold", debug);
		char c = cv::waitKey(0);
#endif //_DEBUG
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		init_contours_weight(tgray, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		// teste chaque contour
		//calcule le poids des contours
		if (total_number_of_plates == squares->total) {
			// on teste N niveaux de seuillage
			nb_pts_sup_threshold = (sz.width * sz.height);
			for (seuil = 0; seuil < nTresh; seuil++)
			{
				int current_nb_pts_sup_threshold = 0;
				LPRThreshold(imgG, gray1, ((seuil + 1) * 200 / nTresh) + 25,
					current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
				if (current_nb_pts_sup_threshold >= 0 &&
					current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
					///////////////////////////////////////////////////////////////////////////////////////////
					// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
					assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
					float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
						current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
					assert(percentage_match > -FLT_EPSILON &&
						percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
					if (percentage_match < 0.75) {
						cvFindContours(gray1, storage, &contours, sizeof(CvContour),
							CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
						nb_pts_sup_threshold = current_nb_pts_sup_threshold;
						if (seuil == 0) cvApproxPoly_coeff = 0.02f;
						else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
						else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
						else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
						int total_number_of_plates = squares->total;
						init_contours_weight(tgray, dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees 
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);
						// teste chaque contour
						//calcule le poids des contours
						/**/
						if (total_number_of_plates == squares->total) {
							cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
							cvFindContours(gray2, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							init_contours_weight(tgray, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
						}
					}
				}
			}
		}
	}
	cvCopy(imgB, tgray, 0);
	// apply Canny. Take the upper threshold from slider
	// and set the lower to 0 (which forces edges merging) 
	cvCanny(tgray, gray1, thresh_canny, 255 - thresh_canny, 5//Aperture parameter for Sobel operator
	);
	// dilate canny output to remove potential
	// holes between edge segments 
	//cvErode( gray1, gray1, 0, 1 );//a rajouter
#ifdef LPR_SAVE_DISPLAY
	debug = cv::cvarrToMat(gray1);
	imshow("canny", debug);
	c = cv::waitKey(0);
#endif //_DEBUG
	// trouve les contours et en fait une liste
	cvFindContours(gray1, storage, &contours, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
	cvApproxPoly_coeff = 0.02f;
	total_number_of_plates = squares->total;
	// teste chaque contour
	//calcule le poids des contours
	init_contours_weight(tgray, dist_min_bord,
		hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
		sz.width, sz.height,
		storage, contours,
		squares,
		// diffeentes seuences contentant les notes correspondant aux diffeents critees 
		note_plus_grand_cosinus,
		note_rapport_long_cotes_opposes,
		note_plus_petit_sinus,
		note_plus_grand_sinus,
		notesym, cvApproxPoly_coeff);
	if (!squares->total) {
		/*
		Mat cv::cvarrToMat 	( 	const CvArr *  	arr,
		bool  	copyData = false,
		bool  	allowND = true,
		int  	coiMode = 0,
		AutoBuffer< double > *  	buf = 0
	)
	*/
		cv::Mat img = cv::cvarrToMat(tgray);
		cv::Mat image_final = cv::cvarrToMat(gray1);
		//cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 17, 0);
		int blockSize = (sz.width + sz.height) / 2;
		if (blockSize % 2 == 0) blockSize += 1;
		cv::adaptiveThreshold(img, image_final, 127, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, 0);
#ifdef LPR_SAVE_DISPLAY
		cv::Mat debug = cv::cvarrToMat(gray1);
		imshow("adaptiveThreshold", debug);
		char c = cv::waitKey(0);
#endif //_DEBUG
		cvFindContours(gray1, storage, &contours, sizeof(CvContour),
			CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
		init_contours_weight(tgray, dist_min_bord,
			hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
			sz.width, sz.height,
			storage, contours,
			squares,
			// diffeentes seuences contentant les notes correspondant aux diffeents critees 
			note_plus_grand_cosinus,
			note_rapport_long_cotes_opposes,
			note_plus_petit_sinus,
			note_plus_grand_sinus,
			notesym, cvApproxPoly_coeff);
		// teste chaque contour
		//calcule le poids des contours
		if (total_number_of_plates == squares->total) {
			// on teste N niveaux de seuillage
			nb_pts_sup_threshold = (sz.width * sz.height);
			for (seuil = 0; seuil < nTresh; seuil++)
			{
				int current_nb_pts_sup_threshold = 0;
				LPRThreshold(imgB, gray1, ((seuil + 1) * 200 / nTresh) + 25,
					current_nb_pts_sup_threshold);// trouve les contours et en fait une liste
				if (current_nb_pts_sup_threshold >= 0 &&
					current_nb_pts_sup_threshold <= nb_pts_sup_threshold) {
					///////////////////////////////////////////////////////////////////////////////////////////
					// retourne le nb de points en commun entre deux images
#ifdef _DEBUG
					assert(current_nb_pts_sup_threshold <= nb_pts_sup_threshold);
#endif //_DEBUG
					float percentage_match = 1.0f - (static_cast<float>(nb_pts_sup_threshold -
						current_nb_pts_sup_threshold)) / (sz.width * sz.height);
#ifdef _DEBUG
					assert(percentage_match > -FLT_EPSILON &&
						percentage_match < 1.0f + FLT_EPSILON);
#endif //_DEBUG
					if (percentage_match < 0.75) {
						cvFindContours(gray1, storage, &contours, sizeof(CvContour),
							CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
						nb_pts_sup_threshold = current_nb_pts_sup_threshold;
						if (seuil == 0) cvApproxPoly_coeff = 0.02f;
						else if (seuil == 1) cvApproxPoly_coeff = 0.03f;
						else if (seuil == 2) cvApproxPoly_coeff = 0.04f;
						else cvApproxPoly_coeff = 0.05f;//if(seuil==3) 
						int total_number_of_plates = squares->total;
						init_contours_weight(tgray, dist_min_bord,
							hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
							sz.width, sz.height,
							storage, contours,
							squares,
							// diffeentes seuences contentant les notes correspondant aux diffeents critees 
							note_plus_grand_cosinus,
							note_rapport_long_cotes_opposes,
							note_plus_petit_sinus,
							note_plus_grand_sinus,
							notesym, cvApproxPoly_coeff);
						// teste chaque contour
						//calcule le poids des contours
						/**/
						if (total_number_of_plates == squares->total) {
							cvMorphologyEx(gray1, gray2, nullptr, convKernel, CV_MOP_CLOSE);
							cvFindContours(gray2, storage, &contours, sizeof(CvContour),
								CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
							init_contours_weight(tgray, dist_min_bord,
								hauteur_plaque_min, rapport_largeur_sur_hauteur_min,
								sz.width, sz.height,
								storage, contours,
								squares,
								// diffeentes seuences contentant les notes correspondant aux diffeents critees 
								note_plus_grand_cosinus,
								note_rapport_long_cotes_opposes,
								note_plus_petit_sinus,
								note_plus_grand_sinus,
								notesym, cvApproxPoly_coeff);
						}
					}
				}
			}
		}
	}
	// (on libee de la meoire)
	cvReleaseImage(&gray1);
	cvReleaseImage(&tgray);
	cvReleaseImage(&gray2);
	cvReleaseStructuringElement(&convKernel);
	/*
	cvReleaseImage( &imgR );
	cvReleaseImage( &imgG );
	cvReleaseImage( &imgB );
	*/
#ifdef _DEBUG
	assert(note_plus_grand_cosinus->total == note_rapport_long_cotes_opposes->total &&
		note_plus_grand_cosinus->total == note_plus_petit_sinus->total &&
		note_plus_grand_cosinus->total == note_plus_grand_sinus->total &&
		note_plus_grand_cosinus->total == notesym->total);
#endif //_DEBUG
	//si rien n'a passele filtre, on arree le:
	//on renvoie une seuence de -1
	if (notesym->total == 0)
	{
		cvClearSeq(classement);
		centre = cvPoint(SHRT_MIN, SHRT_MIN);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		cvSeqPush(classement, &centre);
		return classement;
	}
	// CLASSEMENT
	// renvoie la seuence de places epartir de la seuence de notes
	place_plus_grand_cosinus = sort(note_plus_grand_cosinus, storage);
	place_rapport_long_cotes_opposes = sort(note_rapport_long_cotes_opposes, storage);
	place_plus_petit_sinus = sort(note_plus_petit_sinus, storage);
	place_plus_grand_sinus = sort(note_plus_grand_sinus, storage);
	placesym = sort(notesym, storage);
#ifdef _DEBUG
	assert(place_plus_grand_cosinus->total == place_rapport_long_cotes_opposes->total &&
		place_plus_grand_cosinus->total == place_plus_petit_sinus->total &&
		place_plus_grand_cosinus->total == place_plus_grand_sinus->total &&
		place_plus_grand_cosinus->total == placesym->total);
#endif //_DEBUG
	//dans place, on met la somme des places pour chaque quadrilatee
	for (i = 0; i < place_plus_grand_cosinus->total; i++)
	{
		j = (*(int*)cvGetSeqElemLPR(place_plus_grand_cosinus, i, 0) + *(int*)cvGetSeqElemLPR(place_rapport_long_cotes_opposes, i, 0) +
			*(int*)cvGetSeqElemLPR(place_plus_petit_sinus, i, 0) + *(int*)cvGetSeqElemLPR(place_plus_grand_sinus, i, 0) +
			*(int*)cvGetSeqElemLPR(placesym, i, 0));
		cvSeqPush(place, &j);
	}
	// on initialise classementplace et classement
	cvSeqPush(classementplace, cvGetSeqElemLPR(place, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 0, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2, 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3, 0));
	//dans classement on met les quadrilatees
	//et dans classmentplace leurs sommes de places
	// (le tout de faen ordonnee)
	for (j = 1; j < place->total; j++)
	{
		for (i = 0; i < classementplace->total; i++)
		{
			k = 0;
			if (*(int*)cvGetSeqElemLPR(classementplace, i, 0) >
				*(int*)cvGetSeqElemLPR(place, j, 0) || (i == classementplace->total))
			{
				cvSeqInsert(classementplace, i, cvGetSeqElemLPR(place, j, 0));
				cvSeqInsert(classement, (i << 2), cvGetSeqElemLPR(squares, (j << 2), 0));
				cvSeqInsert(classement, (i << 2) + 1, cvGetSeqElemLPR(squares, (j << 2) + 1, 0));
				cvSeqInsert(classement, (i << 2) + 2, cvGetSeqElemLPR(squares, (j << 2) + 2, 0));
				cvSeqInsert(classement, (i << 2) + 3, cvGetSeqElemLPR(squares, (j << 2) + 3, 0));
				k = 1;
			}
			if (k == 1)
				break;
		}
	}
	// on va se resservir de squares et note_plus_grand_cosinus (on les vide donc)
	cvClearSeq(squares);
	cvClearSeq(note_plus_grand_cosinus);
	//dans squares on stocke les rectangles moyens
	//dans centres leurs centres
	//dans note_plus_grand_cosinus, leurs pondeations
	//on eudie le quart supeieur des quadrilatees
	//(ou 1 si y en a - de 4)
	max = (classement->total) >> 4;
	//s'il n'y a pas 4 quadrilatees on les prends tous
	if (max < 1) max = ((classement->total) >> 2);
	k = 0;
	for (j = 0; j < max; j++)//j est le no du quadrilatee auquel on cherche un sosie
	{
		//pt[((j<<2))&3] est un des quatre points du quadrilatee courant.
		pt[((j << 2)) & 3] = *(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0);
		//centre est le centre du quadrilatee, il nous permet de l'identifier
		centre = moyenne4(pt[((j << 2)) & 3], pt[((j << 2) + 1) & 3], pt[((j << 2) + 2) & 3], pt[((j << 2) + 3) & 3]);
		if (k == 1)
		{
			m = 0;
			for (i = 0; i < centres->total; i++)// i est le no du qudrilatee qui est peut-ere un sosie
			{
				if (dist(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0)) < 10)
					//si les 2 centres sont emoins de 10 pixels d'intervalle
						//on fait la moyenne des centres
							//on fait la moyenne des rectangles 
								//le coef de pond augmente de 1
				{
					m = 1;
					pond = *(int*)cvGetSeqElemLPR(note_plus_grand_cosinus, i, 0) + 1;
					cvSeqRemove(note_plus_grand_cosinus, i);
					cvSeqInsert(note_plus_grand_cosinus, i, &pond);
					//dans centre on met la moyenne pondere des 2 centres
					//puis on stocke ce point dans la seq. centres
					centre = moyennepond(centre, *(CvPoint*)cvGetSeqElemLPR(centres, i, 0), 1, (pond - 1));
					cvSeqRemove(centres, i);
					cvSeqInsert(centres, i, &centre);
					//centre va servir echaque fois estocker les coordonnees de chaque coin du rectangle moyen
					for (l = 0; l < 4; l++)
					{
						//dans centre, on met les nouvelles coordonnees d'un coin du rectangle 
						//moyen c a d la moyenne pondere du point rectangle preedemment stockeet du 
						// point (du nouveau rectangle) le + proche
						centre = moyennepond(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
							trouveleplusproche(*(CvPoint*)cvGetSeqElemLPR(squares, (i << 2) + l, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2), 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 1, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 2, 0),
								*(CvPoint*)cvGetSeqElemLPR(classement, (j << 2) + 3, 0)), (pond - 1), 1);
						// et echaque fois on stocke le point qui vient d'ere calculedans squares
						cvSeqRemove(squares, (i << 2) + l);
						cvSeqInsert(squares, (i << 2) + l, &centre);
					}
				}
				//si on a associeles 2 rect., on break
				if (m == 1)
					break;
				// si on a pas trouveede sosie ej,
				// on le classe eune nouvelle place dans squares
				// avec une pondeation de 1
				if (i == centres->total)
				{
					cvSeqPush(centres, &centre);
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2), 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 1, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 2, 0));
					cvSeqPush(squares, cvGetSeqElemLPR(classement, (j << 2) + 3, 0));
					cvSeqPush(note_plus_grand_cosinus, &un);
				}
			}
		}
		if (k == 0)	// la 1 ere fois  qu on fait cette boucle 
					// on compte systematiquement le quadrilatee comme nouveau
					// donc on le stocke eune nouvelle place dans squares
					// et on lui met une pondeation de 1
		{
			cvSeqPush(centres, &centre);
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 0, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 1, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 2, 0));
			cvSeqPush(squares, cvGetSeqElemLPR(classement, 3, 0));
			cvSeqPush(note_plus_grand_cosinus, &un);
			k = 1;
		}
	}
	// leon reclasse nos rectangles par ordre de pondeation
	k = 0; l = 0;
	for (j = 1; j < note_plus_grand_cosinus->total; j++)
	{
		i = *cvGetSeqElemLPR(note_plus_grand_cosinus, j, 0);
		if (i > k)
		{
			k = i;
			l = j;
		}
	}
	// et dans ce classement on garde que le meilleur
	cvClearSeq(classement);
	cvSeqPush(classement, cvGetSeqElemLPR(squares, (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 1 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 2 + (l << 2), 0));
	cvSeqPush(classement, cvGetSeqElemLPR(squares, 3 + (l << 2), 0));
	//il ne reste qu'un quadrilatee. on le renvoie
	return classement;
}
bool is_valid(const cv::Point& top_left_,
	const cv::Point& top_right_,
	const cv::Point& bottom_right_,
	const cv::Point& bottom_left_) {
	std::vector<cv::Point> points;
	points.push_back(cv::Point(top_left_.x, top_left_.y));
	points.push_back(cv::Point(top_right_.x, top_right_.y));
	points.push_back(cv::Point(bottom_right_.x, bottom_right_.y));
	points.push_back(cv::Point(bottom_left_.x, bottom_left_.y));
	cv::RotatedRect box = cv::minAreaRect(points);
	float rotated_area = box.size.area();
	float contourArea_ = cv::contourArea(points);
	//from -45 to +45 degrees
	float angle_ = box.angle;
	cv::Rect global_rect = box.boundingRect();
	if (angle_ >= 70.0f || angle_ <= -70.0f || (angle_ >= -20.0f && angle_ <= 20.0f)) {
		cv::Point2f pts[4];
		box.points(pts);
#ifdef _DEBUG
		std::list<int> ordonnees;
		ordonnees.push_back(fabs(pts[0].y - pts[1].y));
		ordonnees.push_back(fabs(pts[1].y - pts[2].y));
		ordonnees.push_back(fabs(pts[2].y - pts[3].y));
		ordonnees.push_back(fabs(pts[3].y - pts[0].y));
		std::list<int> abscisses;
		abscisses.push_back(fabs(pts[0].x - pts[1].x));
		abscisses.push_back(fabs(pts[1].x - pts[2].x));
		abscisses.push_back(fabs(pts[2].x - pts[3].x));
		abscisses.push_back(fabs(pts[3].x - pts[0].x));
#endif //_DEBUG
		float width_ = std::max({ fabs(pts[0].x - pts[1].x), fabs(pts[1].x - pts[2].x),
			fabs(pts[2].x - pts[3].x), fabs(pts[3].x - pts[0].x) });
		float height_ = std::max({ fabs(pts[0].y - pts[1].y), fabs(pts[1].y - pts[2].y),
			fabs(pts[2].y - pts[3].y), fabs(pts[3].y - pts[0].y) });
		return (static_cast<float>(global_rect.width * global_rect.height) < contourArea_ * 5.0f//ok
			&& rotated_area < contourArea_ * 2.5f//ok
			&& 1.5f * global_rect.width > global_rect.height//ok
			&& width_ >= height_
			);
	}
	else {
#ifdef _DEBUG
		//bottom left-top left-top right-bottom right
		assert(angle_ <= 90.0f && angle_ >= -90.0f);
		cv::Point2f pts[4];
		box.points(pts);
		//angle entre l'horizontal et la plus grande dimension
		//width et la dimension la plus proche de l'horizontal
		if (angle_ > 45.0f) {
			assert(box.size.width - 1 <= box.size.height);
		}
		else if (angle_ < -45.0f) {
			assert(box.size.width - 1 <= box.size.height);
		}
		else {
			assert(box.size.width >= box.size.height - 1);
		}
#endif //_DEBUG
		/*
		return (static_cast<float>(global_rect.width * global_rect.height) < contourArea_ * 5.0f//ok
			&& rotated_area < contourArea_ * 2.5f//ok
			&& fabs(angle_) < 30.0f//this has to be fixed
			&& 1.5f*global_rect.width > global_rect.height//ok
			);*/
		if (static_cast<float>(global_rect.width * global_rect.height) < contourArea_ * 5.0f//ok
			&& rotated_area < contourArea_ * 2.5f//ok
			&& 1.5f * global_rect.width > global_rect.height//ok
			&& box.size.width >= box.size.height
			)
		{
			return true;
			//first case
			if (box.size.width <= box.size.height) {
				return (angle_ < -60);
			}
			else return (angle_ > -30);
		}
		//first case
		if (box.size.width <= box.size.height) {
			return (static_cast<float>(global_rect.width * global_rect.height) < contourArea_ * 5.0f//ok
				&& rotated_area < contourArea_ * 2.5f//ok
				&& (angle_ < -60)
				&& 1.5f * global_rect.width > global_rect.height//ok
				);
			//return (angle_ < -60);
		}
		else {
			return (static_cast<float>(global_rect.width * global_rect.height) < contourArea_ * 5.0f//ok
				&& rotated_area < contourArea_ * 2.5f//ok
				&& (angle_ > -30)
				&& 1.5f * global_rect.width > global_rect.height//ok
				);
			//return (angle_ > -30);
		}
	}
}
//peut traiter les images sources couleur ou en niveaux de gris
bool trouve_la_plaque(IplImage* etude, const cv::Rect& global_rect, int nTresh
	, const int mean_carac
	, const int mean_fond,
	cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
	cv::Point& top_left_,
	cv::Point& top_right_,
	cv::Point& bottom_right_,
	cv::Point& bottom_left_, cv::Rect& rect_OpenLP,
	const int dist_min_bord
	,
	const int hauteur_plaque_min,
	const float& rapport_largeur_sur_hauteur_min)
{
	CvSeq* fin = nullptr;
	CvMemStorage* storage = cvCreateMemStorage(0);
	// traite l'image
	//peut traiter les images sources couleur ou en niveaux de gris	
	fin = findSquares4_multiresolution(etude, global_rect, storage, nTresh, mean_carac
		, mean_fond
		, dist_min_bord, hauteur_plaque_min,
		rapport_largeur_sur_hauteur_min);
	if (fin) {
		CvPoint* pt =
			CV_GET_SEQ_ELEM(CvPoint, fin, 0);
		if (pt != nullptr)
		{
			p0.x = pt->x;
			p0.y = pt->y;
			pt = CV_GET_SEQ_ELEM(CvPoint, fin, 1);
			if (pt != nullptr)
			{
				p1.x = pt->x;
				p1.y = pt->y;
				pt = CV_GET_SEQ_ELEM(CvPoint, fin, 2);
				if (pt != nullptr)
				{
					p2.x = pt->x;
					p2.y = pt->y;
					pt = CV_GET_SEQ_ELEM(CvPoint, fin, 3);
					if (pt != nullptr)
					{
						p3.x = pt->x;
						p3.y = pt->y;
					}
					else {
						p0.x = p0.y = SHRT_MIN;
						p1.x = p1.y = SHRT_MIN;
						p2.x = p2.y = SHRT_MIN;
						p3.x = p3.y = SHRT_MIN;
					}
				}
				else {
					p0.x = p0.y = SHRT_MIN;
					p1.x = p1.y = SHRT_MIN;
					p2.x = p2.y = SHRT_MIN;
					p3.x = p3.y = SHRT_MIN;
				}
			}
			else {
				p0.x = p0.y = SHRT_MIN;
				p1.x = p1.y = SHRT_MIN;
				p2.x = p2.y = SHRT_MIN;
				p3.x = p3.y = SHRT_MIN;
			}
		}
		else {
			p0.x = p0.y = SHRT_MIN;
			p1.x = p1.y = SHRT_MIN;
			p2.x = p2.y = SHRT_MIN;
			p3.x = p3.y = SHRT_MIN;
		}
	}
	else {
		p0.x = p0.y = SHRT_MIN;
		p1.x = p1.y = SHRT_MIN;
		p2.x = p2.y = SHRT_MIN;
		p3.x = p3.y = SHRT_MIN;
	}
	// on libee la meoire occupe pas les cvseq
	cvClearMemStorage(storage);
	cvReleaseMemStorage(&storage);
	if (p3.x != SHRT_MIN) {
		cv::Point bottom_right;
		cv::Point top_right;
		cv::Point top_left;
		cv::Point bottom_left;
		cv::Rect r(array2CRect(cv::Point(p0.x, p0.y), cv::Point(p1.x, p1.y),
			cv::Point(p2.x, p2.y), cv::Point(p3.x, p3.y),
			top_left,
			top_right,
			bottom_right,
			bottom_left));
		if (r.x >= 0 && r.width > MIN_WIDTH_FOR_A_LP && r.y >= 0 && r.height >= MIN_HEIGHT_FOR_A_LP
			&& top_left.x < top_right.x && bottom_left.x < bottom_right.x && top_left.y < bottom_left.y && top_right.y < bottom_right.y) {
			bottom_right_.x = bottom_right.x;
			top_right_.x = top_right.x;
			top_left_.x = top_left.x;
			bottom_left_.x = bottom_left.x;
			bottom_right_.y = bottom_right.y;
			top_right_.y = top_right.y;
			top_left_.y = top_left.y;
			bottom_left_.y = bottom_left.y;
			rect_OpenLP = r;
			return is_valid(top_left_,
				top_right_,
				bottom_right_,
				bottom_left_);
			//return true;
		}
		else return false;
	}
	else return false;
}
bool trouve_la_plaque(const cv::Mat& frame, const cv::Rect& global_rect
	, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
	cv::Point& top_left_,
	cv::Point& top_right_,
	cv::Point& bottom_right_,
	cv::Point& bottom_left_,
	cv::Rect& rect_OpenLP)
{
	//std::cout << "  starts two_stage_lpr" << std::endl;
	const int dist_min_bord = 3;
	const int hauteur_plaque_min = 9;
	float rapport_largeur_sur_hauteur_min(1.0f);
	int nTresh = 8;
	IplImage iplImage = cvIplImage(frame);
	IplImage* iptemp = &iplImage;
	//convertit une image vissdk 8 bits en une image ipl 8 bits
	if (iptemp) {
		/*
				// convert to binary
				cv::Mat thresh_img;
				int blockSize = 7;
				double C = 0.0;
				adaptiveThreshold(frame, thresh_img, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, C);
				*/
		const int mean_carac = 50;
		const int  mean_fond = 200;
		cv::Point p0_; cv::Point p1_; cv::Point p2_; cv::Point p3_; cv::Point top_left__;
		cv::Point top_right__;
		cv::Point bottom_right__;
		cv::Point bottom_left__; cv::Rect rect_OpenLP_;
		bool return_value = trouve_la_plaque(iptemp, global_rect, nTresh, mean_carac
			, mean_fond, p0_, p1_, p2_, p3_,
			top_left__,
			top_right__,
			bottom_right__,
			bottom_left__, rect_OpenLP_,
			dist_min_bord, hauteur_plaque_min,
			rapport_largeur_sur_hauteur_min);
		p0.x = p0_.x;
		p0.y = p0_.y;
		p1.x = p1_.x;
		p1.y = p1_.y;
		p2.x = p2_.x;
		p2.y = p2_.y;
		p3.x = p3_.x;
		p3.y = p3_.y;
		top_left_.x = top_left__.x;
		top_left_.y = top_left__.y;
		top_right_.x = top_right__.x;
		top_right_.y = top_right__.y;
		bottom_right_.x = bottom_right__.x;
		bottom_right_.y = bottom_right__.y;
		bottom_left_.x = bottom_left__.x;
		bottom_left_.y = bottom_left__.y;
		rect_OpenLP = rect_OpenLP_;
		//cvReleaseImage(&iptemp);
		return return_value;
	}
	return false;
}
cv::Rect fit_in(const cv::Rect& rect_im, const cv::Rect& roi) {
	cv::Rect fit_in_rect(roi);
	if (fit_in_rect.x < rect_im.x) {
		fit_in_rect.width -= rect_im.x - fit_in_rect.x;
		fit_in_rect.x = rect_im.x;
	}
	if (fit_in_rect.y < rect_im.y) {
		fit_in_rect.height -= rect_im.y - fit_in_rect.y;
		fit_in_rect.y = rect_im.y;
	}
	if (fit_in_rect.y + fit_in_rect.height > rect_im.y + rect_im.height) fit_in_rect.height = rect_im.y + rect_im.height - fit_in_rect.y;
	if (fit_in_rect.x + fit_in_rect.width > rect_im.x + rect_im.width) fit_in_rect.width = rect_im.x + rect_im.width - fit_in_rect.x;
#ifdef _DEBUG
	assert(fit_in_rect == get_inter(rect_im, roi));
#endif //_DEBUG
	return fit_in_rect;
}
cv::Rect get_greater_rect(const std::list<cv::Rect>& boxes)
{
	int area = -1;
	cv::Rect return_rect(0, 0, 0, 0);
	std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
	while (it_boxes != boxes.end()) {
		if (area < it_boxes->width * it_boxes->height) {
			area = it_boxes->width * it_boxes->height;
			return_rect = *it_boxes;
		}
		it_boxes++;
	}
	return return_rect;
}
//gets the greater rect of the boxes list and then gets the reunion of all the boxes 
//if these two rects are similar means that the reunion is not too much larger than the greater rect, and thus the iou is near 1, then we consider that the greater rect 
//is license plate bb
bool has_global_rect(const std::list<cv::Rect>& boxes, cv::Rect& greater_rect)
{
	greater_rect = cv::Rect(get_greater_rect(boxes));
	//cette fonction retourne le rect englobant la collection
	//gets the reunion of all the boxes
	cv::Rect global_rect(get_global_rect(boxes));
	float iou_ = iou(global_rect, greater_rect);
	return (iou_ > 0.9f);
}
std::list<cv::Rect> translate(const std::list<cv::Rect>& l, const int x_, const int y_) {
	std::list<cv::Rect> return_value;
	std::list<cv::Rect>::const_iterator it(l.begin());
	while (it != l.end()) {
		return_value.push_back(cv::Rect(it->x + x_, it->y + y_, it->width, it->height));
		it++;
	}
	return return_value;
}
void translate(cv::Point& pt, const int tx, const int ty)
{
	pt.x += tx;
	pt.y += ty;
}
cv::Rect translate(const cv::Rect& r, const int x_, const int y_) {
	return cv::Rect(r.x + x_, r.y + y_, r.width, r.height);
}
bool in_quadrilatere(const cv::Rect& box,
	const cv::Point& top_left, const cv::Point& top_right, const cv::Point& bottom_right, const cv::Point& bottom_left, const float& signed_distance)
{
	std::vector<cv::Point > contours;
	contours.push_back(top_left);
	contours.push_back(top_right);
	contours.push_back(bottom_right);
	contours.push_back(bottom_left);
	contours.push_back(top_left);
	return in_quadrilatere(box, contours, signed_distance);
}
bool in_quadrilatere(const std::list<cv::Rect>& boxes,
	const cv::Point& top_left, const cv::Point& top_right, const cv::Point& bottom_right, const cv::Point& bottom_left, const float& signed_distance)
{
	std::vector<cv::Point > contours;
	contours.push_back(top_left);
	contours.push_back(top_right);
	contours.push_back(bottom_right);
	contours.push_back(bottom_left);
	contours.push_back(top_left);
	return in_quadrilatere(boxes, contours, signed_distance);
}
bool in_quadrilatere(const std::vector<cv::Rect>& boxes, const std::vector<cv::Point >& contours, const float& signed_distance)
{
	std::list<cv::Rect> boxes_;
	std::copy(boxes.begin(), boxes.end(), std::back_inserter(boxes_));
	return in_quadrilatere(boxes_, contours, signed_distance);
}
bool in_quadrilatere(const std::vector<cv::Rect>& boxes,
	const cv::Point& top_left, const cv::Point& top_right, const cv::Point& bottom_right, const cv::Point& bottom_left, const float& signed_distance)
{
	std::list<cv::Rect> boxes_;
	std::copy(boxes.begin(), boxes.end(), std::back_inserter(boxes_));
	return in_quadrilatere(boxes_, top_left, top_right, bottom_right, bottom_left, signed_distance);
}
bool trouve_la_plaque(const cv::Mat& frame,
	const std::list<int>& classes, const std::list<cv::Rect>& boxes
	,
	cv::Point& top_left_,
	cv::Point& top_right_,
	cv::Point& bottom_right_,
	cv::Point& bottom_left_, cv::Rect& rect_OpenLP)
{
	cv::Mat frame_grayscale(frame.size(), CV_8U);
	if (frame.type() == CV_8UC3)
		cv::cvtColor(frame, frame_grayscale, cv::COLOR_BGR2GRAY);
	else if (frame.type() == CV_8UC4)
		cv::cvtColor(frame, frame_grayscale, cv::COLOR_BGRA2GRAY);
	else if (frame.type() == CV_8UC1)
		frame_grayscale = frame.clone();
	cv::Scalar mean_ = cv::mean(frame_grayscale);
	if (frame_grayscale.rows > 0 && frame_grayscale.cols > 0 && mean_[0] > 2.0f && mean_[0] < 250.0f) {
		//now print corners
		cv::Point  p0; cv::Point  p1; cv::Point  p2; cv::Point  p3;
		std::list<cv::Rect> l_boxes;
		std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes));
		std::list<int> l_classIds;
		std::copy(classes.begin(), classes.end(), std::back_inserter(l_classIds));
		sort_from_left_to_right(l_boxes, l_classIds);//sorts all the boxes from left to right
		//filter out lpn box
		//***************************************************
		//                  FILTER
		//***************************************************
		filter_out_everything_but_characters(l_boxes, l_classIds);
		//cette fonction retourne le rect englobant la collection
		//gets the reunion of all the l_boxes
		cv::Rect roi(get_global_rect(l_boxes));
		if (roi.width && roi.height) {
			roi.x -= roi.width / 2;
			roi.width += static_cast<int>(static_cast<float>(roi.width));
			roi.y -= roi.height;
			roi.height += 2 * roi.height;
			const cv::Rect rect_im(0, 0, frame_grayscale.cols, frame_grayscale.rows);
			roi = fit_in(rect_im, roi);
#ifdef _DEBUG		
			assert(roi.width && roi.height);
#endif //_DEBUG
			cv::Mat subimage = frame_grayscale(roi);
			cv::Rect greater_rect;
			const int im_width = frame.cols;
			const int im_height = frame.rows;
			const int classe_lpn_rect = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE;
			if (!has_global_rect(l_boxes, greater_rect) && im_width > 0 && im_height > 0 && classe_lpn_rect >= 0 && l_boxes.size() > 1)
			{
				//now print corners
				cv::Rect global_rect_in_subimage(get_global_rect(translate(l_boxes, -roi.x, -roi.y)));
				if (trouve_la_plaque(subimage, global_rect_in_subimage
					, p0, p1, p2, p3, top_left_, top_right_, bottom_right_, bottom_left_, rect_OpenLP)) {
					translate(top_left_, roi.x, roi.y);
					translate(top_right_, roi.x, roi.y);
					translate(bottom_right_, roi.x, roi.y);
					translate(bottom_left_, roi.x, roi.y);
					rect_OpenLP = translate(rect_OpenLP, roi.x, roi.y);
					if (is_in_rect(top_left_, rect_im) && is_in_rect(top_right_, rect_im) && is_in_rect(bottom_right_, rect_im) && is_in_rect(bottom_left_, rect_im)
						) {
						cv::Rect global_rect = get_global_rect(l_boxes);
						if (in_quadrilatere(l_boxes, top_left_, top_right_, bottom_right_, bottom_left_, static_cast<float>(-get_median_height(l_boxes) / 4))) {
							global_rect = global_rect | (::get_global_rect(bottom_right_, bottom_left_, top_right_, top_left_));//rect = rect1 | rect2 (minimum area rectangle containing rect2 and rect3 )
							return true;
						}
						else return false;
					}
					else return false;
				}
				else return false;
			}
			else return false;
		}
		else return false;
	}
	else return false;
}
bool in_quadrilatere(const cv::Point& pt,
	const cv::Point& top_left, const cv::Point& top_right, const cv::Point& bottom_right, const cv::Point& bottom_left, const float& signed_distance)
{
	std::vector<cv::Point > contours;
	contours.push_back(top_left);
	contours.push_back(top_right);
	contours.push_back(bottom_right);
	contours.push_back(bottom_left);
	contours.push_back(top_left);
	return in_quadrilatere(pt, contours, signed_distance);
}
bool in_quadrilatere(const cv::Point& pt, const std::vector<cv::Point >& contours, const float& signed_distance)
{
	const cv::Point2f point(pt.x, pt.y);
	const double result = cv::pointPolygonTest(contours, point, true);
	return (result >= signed_distance);
}
bool in_quadrilatere(const cv::Rect& box, const std::vector<cv::Point >& contours, const float& signed_distance)
{
	return (in_quadrilatere(cv::Point(box.tl()), contours, signed_distance) && in_quadrilatere(cv::Point(box.x + box.width, box.y), contours, signed_distance)
		&& in_quadrilatere(cv::Point(box.x + box.width, box.y + box.height), contours, signed_distance) && in_quadrilatere(cv::Point(box.x, box.y + box.height), contours, signed_distance)
		);
}
bool in_quadrilatere(const std::list<cv::Rect>& boxes, const std::vector<cv::Point >& contours, const float& signed_distance)
{
	std::list<cv::Rect>::const_iterator it(boxes.begin());
	while (it != boxes.end()) {
		if (!in_quadrilatere(*it, contours, signed_distance))
			return false;
		else it++;
	}
	return true;
}
bool quadrilatere_is_convex(const CvPoint& pt0, const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3)
{
	std::vector<cv::Point > contours;
	contours.push_back(pt0);
	contours.push_back(pt1);
	contours.push_back(pt2);
	contours.push_back(pt3);
	contours.push_back(pt0);
	/*
	std::vector<cv::Point> hull;
	cv::convexHull(contours, hull);
	*/
	std::vector<cv::Vec4i> cx;
	std::vector<int> hull_idx;
	cv::convexHull(contours, hull_idx, false, false);
	convexityDefects(contours, hull_idx, cx);
	return (cx.size() == 0);
}
bool quadrilatere_is_convex(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3)
{
	CvPoint pt0_;  pt0_.x = pt0.x; pt0_.y = pt0.y;
	CvPoint pt1_;  pt1_.x = pt1.x; pt1_.y = pt1.y;
	CvPoint pt2_;  pt2_.x = pt2.x; pt2_.y = pt2.y;
	CvPoint pt3_;  pt3_.x = pt3.x; pt3_.y = pt3.y;
	return quadrilatere_is_convex(pt0_, pt1_, pt2_, pt3_);
}
//must be convex quadrilatere
bool width_is_larger(const CvPoint& pt0, const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3)
{
	if (quadrilatere_is_convex(pt0, pt1, pt2, pt3)) {
		std::vector<cv::Point > contours;
		contours.push_back(pt0);
		contours.push_back(pt1);
		contours.push_back(pt2);
		contours.push_back(pt3);
		cv::RotatedRect rrect = cv::minAreaRect(contours);//! returns width and height of the rectangle
				//Size2f size;
		//returns 4 vertices of the rectangle
		cv::Point2f pts[4];
		rrect.points(pts);
		float dx = (pts[2].x - pts[1].x) * (pts[2].x - pts[1].x) + (pts[2].y - pts[1].y) * (pts[2].y - pts[1].y);
		float dy = (pts[0].x - pts[1].x) * (pts[0].x - pts[1].x) + (pts[0].y - pts[1].y) * (pts[0].y - pts[1].y);
		return (dx > dy);
	}
	else return false;
}
//must be convex quadrilatere
bool width_is_larger(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3)
{
	CvPoint pt0_;  pt0_.x = pt0.x; pt0_.y = pt0.y;
	CvPoint pt1_;  pt1_.x = pt1.x; pt1_.y = pt1.y;
	CvPoint pt2_;  pt2_.x = pt2.x; pt2_.y = pt2.y;
	CvPoint pt3_;  pt3_.x = pt3.x; pt3_.y = pt3.y;
	return width_is_larger(pt0_, pt1_, pt2_, pt3_);
}
//must be convex quadrilatere
bool get_corners(const CvPoint& pt0, const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3,
	cv::Point& top_left, cv::Point& top_right, cv::Point& bottom_right, cv::Point& bottom_left)
{
	if (quadrilatere_is_convex(pt0, pt1, pt2, pt3) && width_is_larger(pt0, pt1, pt2, pt3)) {
		std::vector<cv::Point > contours;
		contours.push_back(pt0);
		contours.push_back(pt1);
		contours.push_back(pt2);
		contours.push_back(pt3);
		cv::RotatedRect rrect = cv::minAreaRect(contours);//! returns width and height of the rectangle
		//returns 4 vertices of the rectangle
		cv::Point2f pts[4];
		rrect.points(pts);
		cv::Point pts_quadrilatere[4];
		pts_quadrilatere[0] = pt0;
		pts_quadrilatere[1] = pt1;
		pts_quadrilatere[2] = pt2;
		pts_quadrilatere[3] = pt3;
		//*****************************
				//TOP_LEFT
				//*****************************
		float d0 = (pts[1].x - pts_quadrilatere[0].x) * (pts[1].x - pts_quadrilatere[0].x) + (pts[1].y - pts_quadrilatere[0].y) * (pts[1].y - pts_quadrilatere[0].y);
		float d1 = (pts[1].x - pts_quadrilatere[1].x) * (pts[1].x - pts_quadrilatere[1].x) + (pts[1].y - pts_quadrilatere[1].y) * (pts[1].y - pts_quadrilatere[1].y);
		float d2 = (pts[1].x - pts_quadrilatere[2].x) * (pts[1].x - pts_quadrilatere[2].x) + (pts[1].y - pts_quadrilatere[2].y) * (pts[1].y - pts_quadrilatere[2].y);
		float d3 = (pts[1].x - pts_quadrilatere[3].x) * (pts[1].x - pts_quadrilatere[3].x) + (pts[1].y - pts_quadrilatere[3].y) * (pts[1].y - pts_quadrilatere[3].y);
		std::list<float> dists; std::list<int> indeces;
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		std::list<float> dists_tri;
		std::list<int> indeces_tri;
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		std::list<float>::iterator it_dists_verif(dists_tri.begin());
		float poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		top_left = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//BOTTOM_RIGHT
		//*****************************
		d0 = (pts[3].x - pts_quadrilatere[0].x) * (pts[3].x - pts_quadrilatere[0].x) + (pts[3].y - pts_quadrilatere[0].y) * (pts[3].y - pts_quadrilatere[0].y);
		d1 = (pts[3].x - pts_quadrilatere[1].x) * (pts[3].x - pts_quadrilatere[1].x) + (pts[3].y - pts_quadrilatere[1].y) * (pts[3].y - pts_quadrilatere[1].y);
		d2 = (pts[3].x - pts_quadrilatere[2].x) * (pts[3].x - pts_quadrilatere[2].x) + (pts[3].y - pts_quadrilatere[2].y) * (pts[3].y - pts_quadrilatere[2].y);
		d3 = (pts[3].x - pts_quadrilatere[3].x) * (pts[3].x - pts_quadrilatere[3].x) + (pts[3].y - pts_quadrilatere[3].y) * (pts[3].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		bottom_right = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//TOP_RIGHT
		//*****************************
		d0 = (pts[2].x - pts_quadrilatere[0].x) * (pts[2].x - pts_quadrilatere[0].x) + (pts[2].y - pts_quadrilatere[0].y) * (pts[2].y - pts_quadrilatere[0].y);
		d1 = (pts[2].x - pts_quadrilatere[1].x) * (pts[2].x - pts_quadrilatere[1].x) + (pts[2].y - pts_quadrilatere[1].y) * (pts[2].y - pts_quadrilatere[1].y);
		d2 = (pts[2].x - pts_quadrilatere[2].x) * (pts[2].x - pts_quadrilatere[2].x) + (pts[2].y - pts_quadrilatere[2].y) * (pts[2].y - pts_quadrilatere[2].y);
		d3 = (pts[2].x - pts_quadrilatere[3].x) * (pts[2].x - pts_quadrilatere[3].x) + (pts[2].y - pts_quadrilatere[3].y) * (pts[2].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		top_right = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//BOTTOM_LEFT
		//*****************************
		d0 = (pts[0].x - pts_quadrilatere[0].x) * (pts[0].x - pts_quadrilatere[0].x) + (pts[0].y - pts_quadrilatere[0].y) * (pts[0].y - pts_quadrilatere[0].y);
		d1 = (pts[0].x - pts_quadrilatere[1].x) * (pts[0].x - pts_quadrilatere[1].x) + (pts[0].y - pts_quadrilatere[1].y) * (pts[0].y - pts_quadrilatere[1].y);
		d2 = (pts[0].x - pts_quadrilatere[2].x) * (pts[0].x - pts_quadrilatere[2].x) + (pts[0].y - pts_quadrilatere[2].y) * (pts[0].y - pts_quadrilatere[2].y);
		d3 = (pts[0].x - pts_quadrilatere[3].x) * (pts[0].x - pts_quadrilatere[3].x) + (pts[0].y - pts_quadrilatere[3].y) * (pts[0].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		bottom_left = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		return (bottom_left != bottom_right && bottom_left != top_right && bottom_left != top_left && top_left != bottom_right && top_left != top_right && top_right != bottom_right);
	}
	else {
		bottom_right = bottom_left = top_right = top_left = cv::Point(SHRT_MIN, SHRT_MIN);
		return false;
	}
}
//must be convex quadrilatere
bool get_corners(const IplImage* im, const CvPoint& pt0, const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3,
	cv::Point& top_left, cv::Point& top_right, cv::Point& bottom_right, cv::Point& bottom_left)
{
	if (quadrilatere_is_convex(pt0, pt1, pt2, pt3) && width_is_larger(pt0, pt1, pt2, pt3)) {
#ifdef _DEBUG
		/*
		cv::Mat frame = cv::cvarrToMat(im);
		cv::line(frame, pt0, pt1, cv::Scalar(0, 255, 0), 1);
		cv::line(frame, pt1, pt2, cv::Scalar(0, 255, 0), 1);
		cv::line(frame, pt2, pt3, cv::Scalar(0, 255, 0), 1);
		cv::line(frame, pt3, pt0, cv::Scalar(0, 255, 0), 1);
		imshow("sqaure", frame);
		char c = cv::waitKey(0);
		*/
#endif //_DEBUG
		std::vector<cv::Point > contours;
		contours.push_back(pt0);
		contours.push_back(pt1);
		contours.push_back(pt2);
		contours.push_back(pt3);
		cv::RotatedRect rrect = cv::minAreaRect(contours);//! returns width and height of the rectangle
		//returns 4 vertices of the rectangle
		cv::Point2f pts[4];
		rrect.points(pts);
		cv::Point pts_quadrilatere[4];
		pts_quadrilatere[0] = pt0;
		pts_quadrilatere[1] = pt1;
		pts_quadrilatere[2] = pt2;
		pts_quadrilatere[3] = pt3;
		//*****************************
		//TOP_LEFT
		//*****************************
		float d0 = (pts[1].x - pts_quadrilatere[0].x) * (pts[1].x - pts_quadrilatere[0].x) + (pts[1].y - pts_quadrilatere[0].y) * (pts[1].y - pts_quadrilatere[0].y);
		float d1 = (pts[1].x - pts_quadrilatere[1].x) * (pts[1].x - pts_quadrilatere[1].x) + (pts[1].y - pts_quadrilatere[1].y) * (pts[1].y - pts_quadrilatere[1].y);
		float d2 = (pts[1].x - pts_quadrilatere[2].x) * (pts[1].x - pts_quadrilatere[2].x) + (pts[1].y - pts_quadrilatere[2].y) * (pts[1].y - pts_quadrilatere[2].y);
		float d3 = (pts[1].x - pts_quadrilatere[3].x) * (pts[1].x - pts_quadrilatere[3].x) + (pts[1].y - pts_quadrilatere[3].y) * (pts[1].y - pts_quadrilatere[3].y);
		std::list<float> dists; std::list<int> indeces;
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		std::list<float> dists_tri;
		std::list<int> indeces_tri;
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		std::list<float>::iterator it_dists_verif(dists_tri.begin());
		float poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		top_left = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//BOTTOM_RIGHT
		//*****************************
		d0 = (pts[3].x - pts_quadrilatere[0].x) * (pts[3].x - pts_quadrilatere[0].x) + (pts[3].y - pts_quadrilatere[0].y) * (pts[3].y - pts_quadrilatere[0].y);
		d1 = (pts[3].x - pts_quadrilatere[1].x) * (pts[3].x - pts_quadrilatere[1].x) + (pts[3].y - pts_quadrilatere[1].y) * (pts[3].y - pts_quadrilatere[1].y);
		d2 = (pts[3].x - pts_quadrilatere[2].x) * (pts[3].x - pts_quadrilatere[2].x) + (pts[3].y - pts_quadrilatere[2].y) * (pts[3].y - pts_quadrilatere[2].y);
		d3 = (pts[3].x - pts_quadrilatere[3].x) * (pts[3].x - pts_quadrilatere[3].x) + (pts[3].y - pts_quadrilatere[3].y) * (pts[3].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		bottom_right = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//TOP_RIGHT
		//*****************************
		d0 = (pts[2].x - pts_quadrilatere[0].x) * (pts[2].x - pts_quadrilatere[0].x) + (pts[2].y - pts_quadrilatere[0].y) * (pts[2].y - pts_quadrilatere[0].y);
		d1 = (pts[2].x - pts_quadrilatere[1].x) * (pts[2].x - pts_quadrilatere[1].x) + (pts[2].y - pts_quadrilatere[1].y) * (pts[2].y - pts_quadrilatere[1].y);
		d2 = (pts[2].x - pts_quadrilatere[2].x) * (pts[2].x - pts_quadrilatere[2].x) + (pts[2].y - pts_quadrilatere[2].y) * (pts[2].y - pts_quadrilatere[2].y);
		d3 = (pts[2].x - pts_quadrilatere[3].x) * (pts[2].x - pts_quadrilatere[3].x) + (pts[2].y - pts_quadrilatere[3].y) * (pts[2].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		top_right = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		//*****************************
		//BOTTOM_LEFT
		//*****************************
		d0 = (pts[0].x - pts_quadrilatere[0].x) * (pts[0].x - pts_quadrilatere[0].x) + (pts[0].y - pts_quadrilatere[0].y) * (pts[0].y - pts_quadrilatere[0].y);
		d1 = (pts[0].x - pts_quadrilatere[1].x) * (pts[0].x - pts_quadrilatere[1].x) + (pts[0].y - pts_quadrilatere[1].y) * (pts[0].y - pts_quadrilatere[1].y);
		d2 = (pts[0].x - pts_quadrilatere[2].x) * (pts[0].x - pts_quadrilatere[2].x) + (pts[0].y - pts_quadrilatere[2].y) * (pts[0].y - pts_quadrilatere[2].y);
		d3 = (pts[0].x - pts_quadrilatere[3].x) * (pts[0].x - pts_quadrilatere[3].x) + (pts[0].y - pts_quadrilatere[3].y) * (pts[0].y - pts_quadrilatere[3].y);
		indeces.push_back(0); indeces.push_back(1); indeces.push_back(2); indeces.push_back(3);
		dists.push_back(d0); dists.push_back(d1); dists.push_back(d2); dists.push_back(d3);
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des  pour trier (vides au debut puis se remplissant un par un, progressivement)
		while (!dists.empty() && !indeces.empty()) {
			float dist(dists.front());
			std::list<int>::iterator it_indeces_tri(indeces_tri.begin());
			std::list<float>::iterator it_dists_tri(dists_tri.begin());
			while (it_dists_tri != dists_tri.end()
				&& it_indeces_tri != indeces_tri.end()) {
				if (dist >= *it_dists_tri) { break; }
				else { it_dists_tri++; it_indeces_tri++; }
			}
			indeces_tri.splice(it_indeces_tri
				, indeces, indeces.begin());
			dists_tri.splice(it_dists_tri, dists, dists.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(indeces.empty());
		assert(dists.empty());
		assert(indeces_tri.size() == dists_tri.size());
		it_dists_verif = (dists_tri.begin());
		poids_ = FLT_MAX;
		while (it_dists_verif != dists_tri.end()) {
#ifdef _DEBUG
			assert(*it_dists_verif < poids_ + FLT_EPSILON);
#endif //_DEBUG
			poids_ = *it_dists_verif;
			it_dists_verif++;
		}
#endif //_DEBUG
		indeces.swap(indeces_tri);
		dists.swap(dists_tri);
		bottom_left = pts_quadrilatere[indeces.back()];
		indeces.clear();
		dists.clear();
		return (bottom_left != bottom_right && bottom_left != top_right && bottom_left != top_left && top_left != bottom_right && top_left != top_right && top_right != bottom_right);
	}
	else {
		bottom_right = bottom_left = top_right = top_left = cv::Point(SHRT_MIN, SHRT_MIN);
		return false;
	}
}
//must be convex quadrilatere
bool get_corners(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3,
	cv::Point& top_left, cv::Point& top_right, cv::Point& bottom_right, cv::Point& bottom_left)
{
	CvPoint pt0_;  pt0_.x = pt0.x; pt0_.y = pt0.y;
	CvPoint pt1_;  pt1_.x = pt1.x; pt1_.y = pt1.y;
	CvPoint pt2_;  pt2_.x = pt2.x; pt2_.y = pt2.y;
	CvPoint pt3_;  pt3_.x = pt3.x; pt3_.y = pt3.y;
	return get_corners(pt0_, pt1_, pt2_, pt3_,
		top_left, top_right, bottom_right, bottom_left);
}
std::string get_plate_type(
	const std::list<cv::Rect>& vect_of_detected_boxes,
	const std::list<int>& classIds, const int number_of_characters_latin_numberplate
)
{
	//first from left to right
	//cette fonction trie la liste de gauche a droite 
	//change from vect to list 
	std::list<cv::Rect> boxes;
	std::copy(vect_of_detected_boxes.begin(), vect_of_detected_boxes.end(), std::back_inserter(boxes));
	std::list<int> l_classIds;
	std::copy(classIds.begin(), classIds.end(), std::back_inserter(l_classIds));
	sort_from_left_to_right(boxes, l_classIds);
	//filter out lpn box
	//***************************************************
	//                  FILTER
	//***************************************************
	filter_out_everything_but_characters(boxes,
		l_classIds);
	//filter out adjacent boxes with iou>nmsThreshold
	//***************************************************
	//                  FILTER
	//***************************************************
	//if two boxes have an iou (intersection over union) that is too large, then they cannot represent two adjacent characters of the license plate 
	//so we discard the one with the lowest confidence rate
	//filter_iou2(boxes, l_classIds, nmsThreshold);
	//std::list<cv::Rect> l_tri_left;
	std::list<int> levels;
	std::list<char> lpn;
	std::list<cv::Rect> l_tri_left;//list of characters boxes ranged from left to right
	std::list<int> l_tri_left_classIds;
	is_bi_level_plate(boxes,
		l_classIds, l_tri_left,
		l_tri_left_classIds, levels);
	//now
	std::list<char> lpn_minus_1;
	std::list<char> lpn_0;
	std::list<char> lpn_plus_1;
	/*
	C_OCROutputs availableAlpha;
	if (number_of_characters_latin_numberplate == 36)availableAlpha = C_OCROutputs(LATIN_LETTERS_LATIN_DIGITS);
	else if (number_of_characters_latin_numberplate == 35)availableAlpha = C_OCROutputs(LATIN_LETTERS_NO_O_LATIN_DIGITS);
	else availableAlpha = C_OCROutputs(LATIN_LETTERS_NO_I_O_LATIN_DIGITS);
	*/
	std::list<int>::const_iterator it_out_classes(l_tri_left_classIds.begin());
	//std::list<int>::const_iterator it_out_classes(l_classIds.begin());
	std::list<int>::const_iterator it_levels(levels.begin());
	while (
		//it_out_classes != l_classIds.end() 
		it_out_classes != l_tri_left_classIds.end()
		&& it_levels != levels.end()) {
		if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE) {
			if (*it_levels == -1)
				lpn_minus_1.push_back(get_char(*it_out_classes));
			else {
				if (*it_levels == 0)
					lpn_0.push_back(get_char(*it_out_classes));
				else if (*it_levels == 1)
					lpn_plus_1.push_back(get_char(*it_out_classes));
			}
		}
		it_out_classes++; it_levels++;
	}
	int levels_count = 0;
	if (lpn_minus_1.size())levels_count++;
	if (lpn_0.size())levels_count++;
	if (lpn_plus_1.size())levels_count++;
	std::string sub_type_minus_1(get_plate_sub_type(lpn_minus_1));
	std::string sub_type_0(get_plate_sub_type(lpn_0));
	std::string sub_type_plus_1(get_plate_sub_type(lpn_plus_1));
	std::string sub_type;
	if (sub_type_minus_1.size()) {
#ifdef _DEBUG		
		assert(sub_type_plus_1.size() == 0 || sub_type_0.size() == 0);
#endif //_DEBUG
		sub_type += sub_type_minus_1;
	}
	if (sub_type_0.size()) {
#ifdef _DEBUG		
		assert(sub_type_plus_1.size() == 0 || sub_type_minus_1.size() == 0);
#endif //_DEBUG
		if (sub_type_minus_1.size()) {
			sub_type += '_';
		}
		sub_type += sub_type_0;
	}
	if (sub_type_plus_1.size()) {
#ifdef _DEBUG		
		assert(sub_type_0.size() == 0 || sub_type_minus_1.size() == 0);
#endif //_DEBUG
		if (sub_type.size()) {
			sub_type += '_';
		}
		sub_type += sub_type_plus_1;
	}
	return sub_type;
}
std::string get_plate_type(
	const std::vector<cv::Rect>& vect_of_detected_boxes,
	const std::vector<int>& classIds, const int number_of_characters_latin_numberplate
)
{
	std::list<cv::Rect> list_of_detected_boxes;
	std::list<int> list_classIds;
	std::copy(vect_of_detected_boxes.begin(), vect_of_detected_boxes.end(), std::back_inserter(list_of_detected_boxes));
	std::copy(classIds.begin(), classIds.end(), std::back_inserter(list_classIds));
	return get_plate_type(list_of_detected_boxes, list_classIds, number_of_characters_latin_numberplate);
}
float cosine(const CvPoint& pt1, const CvPoint& pt2, const CvPoint& summit)//gives the cos in radians of the angle of summit summit formed by pt1 and pt2 and summit
{// dot servira dans la methode angle pour simplifier les calculs
#define dot(a,b) (a.x*b.x + a.y*b.y)
	cv::Point v1(pt1.x - summit.x, pt1.y - summit.y);
	cv::Point v2(pt2.x - summit.x, pt2.y - summit.y);
	int64_t denomin = (v1.x * v1.x + v1.y * v1.y); denomin *= (v2.x * v2.x + v2.y * v2.y);
	float denomin_d = int64ToFloat(denomin);
	//denomin_d+=FLT_EPSILON;
	denomin_d = sqrtf(denomin_d);
	if (denomin_d < FLT_EPSILON) denomin_d = FLT_EPSILON;
#ifdef _DEBUG
	float a = (static_cast<float>(dot(v1, v2))) / denomin_d;
#ifdef _DEBUG
	assert(a > -1.0f - FLT_EPSILON && a < 1.0f + FLT_EPSILON);
#endif //_DEBUG
	return a;
#else
	return (static_cast<float>(dot(v1, v2))) / denomin_d;
#endif
}
//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
// angle renvoie le cos de l'angle derit par 2 vecteurs
// pt0->pt1 and pt0->pt2 
float cosine(const CvPoint* pt1, const CvPoint* pt2, const CvPoint* summit)
{/*
 float dx1 = pt1->x - summit->x;
 float dy1 = pt1->y - summit->y;
 float dx2 = pt2->x - summit->x;
 float dy2 = pt2->y - summit->y;
 return (dx1*dx2 + dy1*dy2)/sqrtf((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + FLT_EPSILON);
 */
	int dx1 = pt1->x - summit->x;
	int dy1 = pt1->y - summit->y;
	int dx2 = pt2->x - summit->x;
	int dy2 = pt2->y - summit->y;
	if (dx1 == 0 && dx2 == 0 && dy1 == 0 && dy2 == 0) return 0.0f;
	else return (static_cast<float>(dx1 * dx2 + dy1 * dy2)) / sqrtf(static_cast<float>((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2)));
}
// la methode zappeUnPoint renvoie l'indice du point qui apparae comme eant le plus inutile
// (c.a.d. dont le sinus de l'angle est le + faible) dans la seuence result.
// cette methode renvoie -1 si aucun point ne semble inutile (aucun sinus <0.5f)
int zappeUnPoint(const CvSeq* polygone, CvMemStorage* storage)
{
	// on cree une seuence contenant les points du polygones, 
	// puis enouveau les 2 premiers points,
	// pour permettre le calcul du sin entre 3 points conseutifs
	if (polygone->total < 3) return -1;
	else {
		int i, j;
		float sinus1, sinusmin = 1.0f;
		//he function finds the element with the given index in the sequence and returns the pointer to
		//it. If the element is not found, the function returns 0. The function supports negative indices, where
		//-1 stands for the last sequence element, -2 stands for the one before last, etc.
		// on cherche l'angle dont le sinus est le + faible, on met l'indice de cet angle dans j
		for (i = -1; i < polygone->total - 1; i++)
		{
			//gives the cos in radians of the angle of summit pt0 formed by pt1 and pt2 and pt0
			// angle renvoie le cos de l'angle derit par 2 vecteurs
			// pt0->pt1 and pt0->pt2 
			sinus1 = cosine(
				(CvPoint*)cvGetSeqElemLPR(polygone, i - 1, 0),
				(CvPoint*)cvGetSeqElemLPR(polygone, i + 1, 0),
				(CvPoint*)cvGetSeqElemLPR(polygone, i, 0)
			);
			sinus1 = sqrtf(1.0f - (sinus1 * sinus1));
			if (sinus1 <= sinusmin)
			{
				sinusmin = sinus1;
				j = i;
			}
		}
		// si le sinus le + faible est en dessous d'un seuil (ici, 0.5f)
		// on renvoie l'indice du point deigne
		// (si j tombe sur un des 2 points rajoute ela fin, 
		// on le replace sur un des 2 points correspondant au deut)
		if (sinusmin < 0.5f)
		{
#ifdef _DEBUG
			assert(j<polygone->total - 1 && j>-2);
#endif
			if (j < 0) j += polygone->total;
			return j;
		}
		else
			// si le sinus le + faible n'est pas en dessous du seuil, on renvoie -1
		{
			return -1;
		}
	}
}
// renvoie la distance entre 2 points
// point pt1
// point pt2
float dist(const CvPoint& pt1, const CvPoint& pt2)
{
#ifdef _DEBUG
	float a(sqrtf(static_cast<float>((pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y))));
	return a;
#else
	//		return sqrtf((pt1.x-pt2.x)*(pt1.x-pt2.x)+(pt1.y-pt2.y)*(pt1.y-pt2.y));
	return sqrtf((static_cast<float>(pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y)));
#endif
}
// renvoie la moyenne pondere de 2 pts
// point pt1, de pondeation a1
// point pt2, de pondeation a2
CvPoint moyennepond(const CvPoint& pt1, const CvPoint& pt2,
	const int a1, const int a2)
{
	CvPoint resultat;
	float inverse(1.0f); inverse /= (a1 + a2);
	resultat.x = floatToInt((a1 * pt1.x + a2 * pt2.x) * inverse);
	resultat.y = floatToInt((a1 * pt1.y + a2 * pt2.y) * inverse);
	return resultat;
}
// renvoie la moyenne de 2 pts
// point pt1
// point pt2
CvPoint moyenne(const CvPoint& pt1, const CvPoint& pt2)
{
	CvPoint resultat;
	resultat.x = ((pt1.x + pt2.x) >> 1);
	resultat.y = ((pt1.y + pt2.y) >> 1);
	return resultat;
}
//renvoie la moyenne de 4 pts
// point pt1
// point pt2
// point pt3
// point pt4
CvPoint moyenne4(const CvPoint& pt1, const CvPoint& pt2,
	const CvPoint& pt3, const CvPoint& pt4)
{
	CvPoint resultat1, resultat2;
	resultat1 = moyenne(pt1, pt2);
	resultat2 = moyenne(pt3, pt4);
	return moyenne(resultat1, resultat2);
}
//trouve le point le + proche d'un point de reference parmis 4 pts
// point de reeence ref
// point pt1
// point pt2
// point pt3
// point pt4
CvPoint trouveleplusproche
(const CvPoint& ref, const CvPoint& pt1,
	const CvPoint& pt2, const CvPoint& pt3, const CvPoint& pt4)
{
	float distance_min = FLT_MAX;
	int index = -1;
	float distance = (dist(ref, pt1));
	if (distance_min > distance) {
		distance_min = distance;
		index = 1;
	}
	distance = (dist(ref, pt2));
	if (distance_min > distance) {
		distance_min = distance;
		index = 2;
	}
	distance = (dist(ref, pt3));
	if (distance_min > distance) {
		distance_min = distance;
		index = 3;
	}
	distance = (dist(ref, pt4));
	if (distance_min > distance) {
		distance_min = distance;
		index = 4;
	}
	if (index == 1) return pt1;
	else if (index == 2) return pt2;
	else if (index == 3) return pt3;
	else if (index == 4) return pt4;
	else return pt1;
	/*
	CvPoint resultat=pt1;
	float distance=dist(ref,pt1);
	if(distance>dist(ref,pt2))
	{resultat=pt2;
	distance=dist(ref,pt2);}
	if(distance>dist(ref,pt3))
	{resultat=pt3;
	distance=dist(ref,pt3);}
	if(distance>dist(ref,pt4))
	{resultat=pt4;}
	return resultat;
	*/
}
// renvoie la seuence de places epartir de la seuence de notes
// seuence de notes not
CvSeq* sort(const CvSeq* notes, CvMemStorage* storage)
{
#ifdef _DEBUG
	assert(notes->elem_size == 4 || notes->elem_size == 8);
#endif //_DEBUG
	CvSeq* cle = cvCreateSeq(CV_32SC1, sizeof(CvSeq), sizeof(int), storage);
	CvSeqReader reader;
	std::list<int> liste_indexs;
	//enrgistrement des notes ds une liste
	int i;
	if (notes->elem_size == 4) {
		std::list<int> liste_notes;
		cvStartReadSeq(notes, &reader, 0);
		for (i = 0; i < notes->total; i++)
		{
			int val;
			CV_READ_SEQ_ELEM(val, reader);
			liste_notes.push_back(val);
			liste_indexs.push_back(i);
		}
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des listes pour trier (vides au deut puis se remplissant un par un, progressivement)
		std::list<int> liste_notes_tri;
		std::list<int> liste_indexs_tri;
		while (!liste_notes.empty() && !liste_indexs.empty()) {
#ifdef _DEBUG
#ifdef _DEBUG
			assert(liste_notes.size() == liste_indexs.size());
#endif //_DEBUG
#endif //_DEBUG
			int note_(liste_notes.front());
			std::list<int>::iterator it_index(liste_indexs_tri.begin());
			std::list<int>::iterator it_tri(liste_notes_tri.begin());
			while (it_tri != liste_notes_tri.end()
				&& it_index != liste_indexs_tri.end()) {
				if (note_ <= *it_tri) break;
				it_tri++;
				it_index++;
			}
			liste_notes_tri.splice(it_tri, liste_notes, liste_notes.begin());
			liste_indexs_tri.splice(it_index, liste_indexs, liste_indexs.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(liste_notes.empty() &&
			liste_indexs.empty());
		std::list<int>::const_iterator it_verif(liste_notes_tri.begin());
		int note_(-1);
		while (it_verif != liste_notes_tri.end()) {
#ifdef _DEBUG
			assert(*it_verif >= note_);
#endif //_DEBUG
			note_ = *it_verif;
			it_verif++;
		}
#endif //_DEBUG
		//////////////////////////////////////////////////////////////////
		//INSCRIPTION DES RANGS
		//////////////////////////////////////////////////////////////////
		//Pushes several elements to the either end of sequence
		int zero = 0;
		cvSeqPushMulti(cle, &zero, liste_notes_tri.size(), CV_BACK);
		int rang(1);
		std::list<int>::iterator it_index(liste_indexs_tri.begin());
		while (it_index != liste_indexs_tri.end()) {
#ifdef _DEBUG
			assert(*it_index >= 0 && *it_index < cle->total);
#endif //_DEBUG
			* cvGetSeqElemLPR(cle, *it_index, nullptr) = rang;
			it_index++;
			rang++;
		}
	}
	else if (notes->elem_size == 8)
	{
		std::list<float> liste_notes;
		cvStartReadSeq(notes, &reader, 0);
		for (i = 0; i < notes->total; i++)
		{
			float val;
			CV_READ_SEQ_ELEM(val, reader);
			liste_notes.push_back(val);
			liste_indexs.push_back(i);
		}
		//////////////////////////////////////////////////////////////////
		//TRI DE LA LISTE
		//////////////////////////////////////////////////////////////////
		//des listes pour trier (vides au deut puis se remplissant un par un, progressivement)
		std::list<float> liste_notes_tri;
		std::list<int> liste_indexs_tri;
		while (!liste_notes.empty() && !liste_indexs.empty()) {
#ifdef _DEBUG
#ifdef _DEBUG
			assert(liste_notes.size() == liste_indexs.size());
#endif //_DEBUG
#endif //_DEBUG
			float note_(liste_notes.front());
			std::list<int>::iterator it_index(liste_indexs_tri.begin());
			std::list<float>::iterator it_tri(liste_notes_tri.begin());
			while (it_tri != liste_notes_tri.end()
				&& it_index != liste_indexs_tri.end()) {
				if (note_ <= *it_tri) break;
				it_tri++;
				it_index++;
			}
			liste_notes_tri.splice(it_tri, liste_notes, liste_notes.begin());
			liste_indexs_tri.splice(it_index, liste_indexs, liste_indexs.begin());
		}
		//VERIF DE LA LISTE
#ifdef _DEBUG
		assert(liste_notes.empty() &&
			liste_indexs.empty());
		std::list<float>::const_iterator it_verif(liste_notes_tri.begin());
		float note_(-1);
		while (it_verif != liste_notes_tri.end()) {
#ifdef _DEBUG
			assert(*it_verif + FLT_EPSILON > note_);
#endif //_DEBUG
			note_ = *it_verif;
			it_verif++;
		}
#endif //_DEBUG
		//////////////////////////////////////////////////////////////////
		//INSCRIPTION DES RANGS
		//////////////////////////////////////////////////////////////////
		//Pushes several elements to the either end of sequence
		int zero = 0;
		cvSeqPushMulti(cle, &zero, liste_notes_tri.size(), CV_BACK);
		int rang(1);
		std::list<int>::iterator it_index(liste_indexs_tri.begin());
		while (it_index != liste_indexs_tri.end()) {
#ifdef _DEBUG
			assert(*it_index >= 0 && *it_index < cle->total);
#endif //_DEBUG
			* cvGetSeqElemLPR(cle, *it_index, nullptr) = rang;
			it_index++;
			rang++;
		}
	}
	return cle;
}