Files
ANSCORE/modules/ANSLPR/src/utils_alpr_detect.cpp

2902 lines
140 KiB
C++

#include "utils_alpr_detect.h"
#include "utils_image_file.h"
#include"utils_opencv.h"
#include <filesystem>
#include <iostream>
#include <fstream>
#define NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE 36
#define NUMBER_OF_COUNTRIES 61
//returns the iou (intersection over union) of two boxes
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;
}
//rearrange detected bounding boxes from left to right
void sort_from_left_to_right(std::list<cv::Rect>& boxes, std::list<float>& confidences, std::list<int>& classIds)
{
std::list<cv::Rect> l_tri_left;
std::list<float> confidences_tri_left;
std::list<int> classIds_tri_left;
while (!boxes.empty() && !confidences.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();
std::list<float>::iterator it_confidences = confidences_tri_left.begin();
while (it != l_tri_left.end() && it_confidences != confidences_tri_left.end() &&
it_classIds != classIds_tri_left.end()) {
if (is_on_the_left(boxes.front(), *it))
break;
else {
it++; it_confidences++; it_classIds++;
}
}
l_tri_left.splice(it, boxes, boxes.begin());
confidences_tri_left.splice(it_confidences, confidences, confidences.begin());
classIds_tri_left.splice(it_classIds, classIds, classIds.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);
classIds_tri_left.swap(classIds);
}//return the intersection rect of the rectangles
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);
}
//return true if the intersection of the first argument box and the second has an interect area that is at least 90% of the box (which means box is nearly entirely in the second argument)
bool is_in_rect_if(const cv::Rect& box, const cv::Rect& rect_im, const float min_area_ratio)
{
cv::Rect inter(get_inter(box, rect_im));
if (inter.width > 0 && inter.height > 0 && box.width > 0 && box.height > 0 && rect_im.width > 0 && rect_im.height > 0) {
return (static_cast<float>(inter.area()) / static_cast<float>(box.area()) > min_area_ratio);
}
else return false;
}
//return true if the intersection of the first argument box and the second has an interect area that is at least 90% of the box (which means box is nearly entirely in the second argument)
bool is_in_rect_if(const cv::Rect& box, const cv::Rect& rect_im)
{
const float min_area_ratio = 0.9f;
return is_in_rect_if(box, rect_im, min_area_ratio);
}
///from all heights of the boxes, get the median value
int get_median_height(const std::list<cv::Rect>& boxes)
{
if (boxes.empty()) return -1;
else if (boxes.size() == 1) return boxes.front().height;
else {
std::list<int> list_tri;
std::list<cv::Rect>::const_iterator it(boxes.begin());
while (it != boxes.end()) {
list_tri.push_back(it->y + it->height - it->y);
it++;
}
list_tri.sort();
std::list<int>::const_iterator it_median = list_tri.begin();
int index = 0;
while (it_median != list_tri.end()
&& index < (list_tri.size() >> 1)) {
index++;
it_median++;
}
if (it_median == list_tri.end())
return list_tri.back();
else return *it_median;
}
}
//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
void filter_iou2(
std::list<cv::Rect>& boxes,
std::list<float>& confidences,
std::list<int>& classIds, float nmsThreshold) {
//filter out adjacent boxes with iou>nmsThreshold
//***************************************************
// FILTER
//***************************************************
std::list<float>::iterator it_confidences = (confidences.begin());
std::list<cv::Rect>::iterator it_boxes = (boxes.begin());
std::list<int>::iterator it_out_classes_ = (classIds.begin());
///from all heights of the boxes, get the median value
int median_height = get_median_height(boxes);
while (it_out_classes_ != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
std::list<float>::iterator it_confidences_next(it_confidences);
std::list<cv::Rect>::iterator it_boxes_next(it_boxes);
std::list<int>::iterator it_out_classes_next(it_out_classes_);
it_out_classes_next++;
it_confidences_next++;
it_boxes_next++;
bool must_erase_current_element = false;
while (it_out_classes_next != classIds.end()
&& it_confidences_next != confidences.end() && it_boxes_next != boxes.end()) {
bool next_element_erased = false;
float current_iou = iou(*it_boxes, *it_boxes_next);
if (current_iou > nmsThreshold) {
if (*it_confidences_next > * it_confidences) {
//eliminate it_boxes
must_erase_current_element = true;
break;
}
else {//eliminate it_boxes_next
//we dont move current iterator
it_out_classes_next = classIds.erase(it_out_classes_next);
it_confidences_next = confidences.erase(it_confidences_next);
it_boxes_next = boxes.erase(it_boxes_next);
next_element_erased = true;
}
}
else {
if (is_in_rect_if(*it_boxes, *it_boxes_next) || is_in_rect_if(*it_boxes_next, *it_boxes)) {
if (abs(it_boxes->height - median_height) > abs(it_boxes_next->height - median_height)) {
//eliminate it_boxes
must_erase_current_element = true;
break;
}
else {//eliminate it_boxes_next
//we dont move current iterator
it_out_classes_next = classIds.erase(it_out_classes_next);
it_confidences_next = confidences.erase(it_confidences_next);
it_boxes_next = boxes.erase(it_boxes_next);
next_element_erased = true;
}
}
}
if (!next_element_erased) {
it_out_classes_next++;
it_confidences_next++;
it_boxes_next++;
}
}
if (//eliminate it_boxes
must_erase_current_element
) {
it_out_classes_ = classIds.erase(it_out_classes_);
it_confidences = confidences.erase(it_confidences);
it_boxes = boxes.erase(it_boxes);
}
else {
it_out_classes_++;
it_confidences++;
it_boxes++;
}
}
}
//get barycenters of a list of bounding boxes.
void get_barycenters(const std::list<cv::Rect>& boxes, std::list<cv::Point2f>& les_points)
{
#ifdef _DEBUG
assert(les_points.empty());
#endif //_DEBUG
std::list<cv::Rect>::const_iterator it(boxes.begin());
while (it != boxes.end()) {
float x_d;
//update des sommes
int somme_x(it->x + it->x + it->width);
if (((somme_x >> 1) << 1) < somme_x) {
#ifdef _DEBUG
assert(somme_x - ((somme_x >> 1) << 1) == 1);
#endif //_DEBUG
somme_x = (somme_x >> 1);
x_d = static_cast<float>(somme_x);
x_d += 0.5f;
}
else {
somme_x = (somme_x >> 1);
x_d = static_cast<float>(somme_x);
}
float y_d;
//update des sommes
int somme_y(it->y + it->y + it->height);
if (((somme_y >> 1) << 1) < somme_y) {
#ifdef _DEBUG
assert(somme_y - ((somme_y >> 1) << 1) == 1);
#endif //_DEBUG
somme_y = (somme_y >> 1);
y_d = static_cast<float>(somme_y);
y_d += 0.5f;
}
else {
somme_y = (somme_y >> 1);
y_d = static_cast<float>(somme_y);
}
#ifdef _DEBUG
assert(y_d + FLT_EPSILON > it->y&& y_d - FLT_EPSILON<it->y + it->height && x_d + FLT_EPSILON>it->x && x_d - FLT_EPSILON < it->x + it->width);
#endif //_DEBUG
les_points.push_back(cv::Point2f(x_d, y_d));
it++;
}
}
//checks if the bounding boxes are ordered from left to right
#ifdef _DEBUG
//cette fonction verifie que la liste est triee de gauche a droite
bool debug_left(const std::list<cv::Rect>& boxes) {
if (boxes.size() < 2) return true;
else {
//VERIFICATION DU TRI CROISSANT
std::list<cv::Rect>::const_iterator it_verif = boxes.begin();
//int pred = it_verif->x;
cv::Rect pred_box = *it_verif;
it_verif++;
while (it_verif != boxes.end()) {
#ifdef _DEBUG
//assert(it_verif->x >= pred);
assert(is_on_the_left(pred_box, *it_verif));
#endif //_DEBUG
if (!is_on_the_left(pred_box, *it_verif) && pred_box != *it_verif) return false;
pred_box = *it_verif;
it_verif++;
}
return true;
}
}
#endif //_DEBUG
//examines how boxes are disposed and filter out boxes with a position that are incompatible with the positions of other boxes
void filtre_grubbs_sides(const std::list<cv::Rect>& boxes, std::list<float>& angles_with_horizontal_line,
float& mean_angles_par_rapport_horiz,
float& standard_deviation_consecutives_angles,
std::list<int>& interdistances,
float& mean_interdistances,
float& standard_deviation_interdistances,
float& mean_produit_interdistance_avec_angle, float& standard_deviation_produit_interdistance_avec_angle)
{
//cette fonction verifie que la liste est triee de gauche a droite
#ifdef _DEBUG
assert(debug_left(boxes));
#endif //_DEBUG
if (boxes.size() < 3) {
standard_deviation_interdistances = -1.0f; standard_deviation_consecutives_angles = -1.0f;
}
else {
std::list<cv::Point2f> les_points;
//retourne la liste forme de centres des ROI
get_barycenters(boxes, les_points);
#ifdef _DEBUG
assert(boxes.size() == les_points.size());
#endif //_DEBUG
//********************************************************************
// standard_deviation_consecutives_angles
//********************************************************************
float somme_produit_interdistance_avec_angle(.0f);
float somme_carre_produit_interdistance_avec_angle(.0f);
float somme_interdistances(.0f);
float somme_carre_interdistances(.0f);
float somme_angles_par_rapport_horiz(.0f);
float somme_carre_angles_par_rapport_horiz(.0f);
std::list<cv::Point2f>::const_iterator it_centre(les_points.begin());
//calcul du premier angle
cv::Point2f pt_pred(*it_centre);
it_centre++;
while (it_centre != les_points.end()) {
cv::Point2f pt_suivant(*it_centre);
//constructeur a partir de deux pts
C_Line current_line(pt_pred, pt_suivant);
//retour l'angle dee la droite par rapport a l'horizontale
float angle_pred = current_line.get_skew_angle();
somme_angles_par_rapport_horiz += angle_pred;
somme_carre_angles_par_rapport_horiz += (angle_pred * angle_pred);
//retour l'angle dee la droite par rapport a l'horizontale
angles_with_horizontal_line.push_back(angle_pred);
somme_interdistances += (pt_suivant.x - pt_pred.x);
somme_carre_interdistances +=
((pt_suivant.x - pt_pred.x) * (pt_suivant.x - pt_pred.x));
interdistances.push_back(static_cast<int>(pt_suivant.x - pt_pred.x));
float produit_courant = (pt_suivant.x - pt_pred.x) * angle_pred;
somme_produit_interdistance_avec_angle += produit_courant;
somme_carre_produit_interdistance_avec_angle += produit_courant * produit_courant;
pt_pred = pt_suivant;
it_centre++;
}
#ifdef _DEBUG
assert(angles_with_horizontal_line.size() + 1 == les_points.size());
#endif //_DEBUG
standard_deviation_consecutives_angles = (somme_carre_angles_par_rapport_horiz)
*angles_with_horizontal_line.size();
standard_deviation_consecutives_angles -= (somme_angles_par_rapport_horiz * somme_angles_par_rapport_horiz);
#ifdef _DEBUG
assert(standard_deviation_consecutives_angles > -FLT_EPSILON);
#endif //_DEBUG
standard_deviation_consecutives_angles /= (angles_with_horizontal_line.size() * angles_with_horizontal_line.size());
standard_deviation_consecutives_angles = sqrtf(standard_deviation_consecutives_angles);
mean_angles_par_rapport_horiz = somme_angles_par_rapport_horiz / (angles_with_horizontal_line.size());
#ifdef _DEBUG
assert(interdistances.size() + 1 == les_points.size());
#endif //_DEBUG
standard_deviation_interdistances = (somme_carre_interdistances)*interdistances.size();
standard_deviation_interdistances -= (somme_interdistances * somme_interdistances);
#ifdef _DEBUG
assert(standard_deviation_interdistances > -FLT_EPSILON);
#endif //_DEBUG
standard_deviation_interdistances /= (interdistances.size() * interdistances.size());
standard_deviation_interdistances = sqrtf(standard_deviation_interdistances);
mean_interdistances = somme_interdistances / (interdistances.size());
standard_deviation_produit_interdistance_avec_angle = (somme_carre_produit_interdistance_avec_angle)*interdistances.size();
standard_deviation_produit_interdistance_avec_angle -= (somme_produit_interdistance_avec_angle * somme_produit_interdistance_avec_angle);
#ifdef _DEBUG
//assert(standard_deviation_produit_interdistance_avec_angle > -FLT_EPSILON);
#endif //_DEBUG
standard_deviation_produit_interdistance_avec_angle /= (interdistances.size() * interdistances.size());
standard_deviation_produit_interdistance_avec_angle = sqrtf(standard_deviation_produit_interdistance_avec_angle);
mean_produit_interdistance_avec_angle = somme_produit_interdistance_avec_angle / (interdistances.size());
}
}
//retourne la liste forme de centres des ROI
//characters on a license plate can be disposed on two lines (bi level) or on just one unique line (single level).
//anycase the ascii characters and there bouding boxes must nbe ordered in the inthe same way of the registration ascii chain.
void is_bi_level_plate(const std::list<cv::Rect>& boxes, const std::list<int>& l_classIds,
std::list<cv::Rect>& l_reordered, std::list<int>& l_reordered_classIds, std::list<int>& levels)
{//tous les characteres encore disponibles
//initialisation des sommes partielles
if (boxes.size() > 1) {
//cette fonction trie la liste de gauche a droite
std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_reordered));
std::copy(l_classIds.begin(), l_classIds.end(), std::back_inserter(l_reordered_classIds));
//***************************************************
// FILTER
//***************************************************
filter_out_everything_but_characters(l_reordered,
l_reordered_classIds);
sort_from_left_to_right(l_reordered, l_reordered_classIds);
//la difference des angles des n-1 premiers characteres par rapport l'horizontale
//l'interdistance de chaque caractere avec son suivant pour les n-1 premiers characteres
std::list<float> angles_with_horizontal_line;
float mean_angles_par_rapport_horiz;
float standard_deviation_consecutives_angles;
std::list<int> interdistances;
float mean_interdistances;
float standard_deviation_interdistances;
float mean_produit_des_2; float standard_deviation_produit_des_2;
filtre_grubbs_sides(l_reordered, angles_with_horizontal_line,
mean_angles_par_rapport_horiz, standard_deviation_consecutives_angles, interdistances,
mean_interdistances, standard_deviation_interdistances, mean_produit_des_2,
standard_deviation_produit_des_2);
if (standard_deviation_consecutives_angles < 0.0f || standard_deviation_interdistances < 0.0f || standard_deviation_produit_des_2 < 0.0f) {
levels = std::list<int>(l_reordered.size(), 0);
}
else {
std::list<cv::Rect>::const_iterator it = l_reordered.begin();
std::list<float>::const_iterator it_angles(angles_with_horizontal_line.begin());
while (it_angles != angles_with_horizontal_line.end() && it != l_reordered.end()) {
if (fabs(*it_angles) > 0.785398f) {
//3.141593/4 pi/4
if (levels.size()) {
std::list<cv::Rect>::const_iterator it_next = it;
it_next++;
cv::Rect box_left(*it);
cv::Rect box_right(*it_next);
#ifdef _DEBUG
////assert(iou(box_left, box_right) < 0.54);
#endif //_DEBUG
if (2 * box_left.y + box_left.height < 2 * box_right.y + box_right.height) {
//in this case box_left is on first line and box_right is on second line
std::list<int>::reverse_iterator it_levels(levels.rbegin());
while (it_levels != levels.rend()) {
if (*it_levels == 0) {
*it_levels = -1;
it_levels++;
}
else break;
}
if ((levels.back() == 1 || levels.back() == 0) && it_angles != angles_with_horizontal_line.begin()) {
std::list<float>::const_iterator it_angles_pred(it_angles); it_angles_pred--;
if (fabs(*it_angles_pred) < fabs(*it_angles)) {
levels.back() = -1;
}
}
levels.push_back(1);
}
else {
std::list<int>::reverse_iterator it_levels(levels.rbegin());
while (it_levels != levels.rend()) {
if (*it_levels == 0) {
*it_levels = 1;
it_levels++;
}
else break;
}
if ((levels.back() == -1 || levels.back() == 0) && it_angles != angles_with_horizontal_line.begin()) {
std::list<float>::const_iterator it_angles_pred(it_angles); it_angles_pred--;
if (fabs(*it_angles_pred) < fabs(*it_angles)) {
levels.back() = 1;
}
}
//in this case box_right is on first line and box_left is on second line
levels.push_back(-1);
}
}
else {
std::list<cv::Rect>::const_iterator it_next = it;
it_next++;
cv::Rect box_left(*it);
cv::Rect box_right(*it_next);
#ifdef _DEBUG
//assert(iou(box_left, box_right) < 0.54);
#endif //_DEBUG
if (2 * box_left.y + box_left.height < 2 * box_right.y + box_right.height) {
//in this case box_left is on first line and box_right is on second line
levels.push_back(-1);
levels.push_back(1);
}
else {
//in this case box_right is on first line and box_left is on second line
levels.push_back(1);
levels.push_back(-1);
}
}
}
else {
//3.141593/4 pi/4
if (levels.size()) {
levels.push_back(levels.back());
}
else {
//in this case box_right is on first line and box_left is on second line
levels.push_back(0);
levels.push_back(levels.back());
}
}
it++;
it_angles++;
}
#ifdef _DEBUG
assert(levels.size() == angles_with_horizontal_line.size() + 1 && levels.size() == l_reordered.size() && levels.size() == boxes.size());
#endif //_DEBUG
//now
std::list<char> lpn_minus_1;
std::list<char> lpn_0;
std::list<char> lpn_plus_1;
//C_OCROutputs availableAlpha(LATIN_LETTERS_NO_I_O_LATIN_DIGITS);
std::list<int>::const_iterator it_levels(levels.begin());
std::list<cv::Rect>::const_iterator it_box(l_reordered.begin());//list of characters l_boxes ranged from left to right
std::list<int>::const_iterator it_out_classes(l_reordered_classIds.begin());
std::list<cv::Rect> l_minus_1;//list of characters l_boxes ranged from left to right
std::list<cv::Rect> l_0;//list of characters l_boxes ranged from left to right
std::list<cv::Rect> l_plus_1;//list of characters l_boxes ranged from left to right
while (it_out_classes != l_reordered_classIds.end() && it_levels != levels.end() &&
it_box != l_reordered.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
if (*it_levels == -1) {
lpn_minus_1.push_back(get_char(*it_out_classes));
l_minus_1.push_back(*it_box);//list of characters l_boxes ranged from left to right
}
else {
if (*it_levels == 0) {
lpn_0.push_back(get_char(*it_out_classes));
l_0.push_back(*it_box);//list of characters l_boxes ranged from left to right
}
else if (*it_levels == 1) {
lpn_plus_1.push_back(get_char(*it_out_classes));
l_plus_1.push_back(*it_box);//list of characters l_boxes ranged from left to right
}
}
}
it_out_classes++; it_levels++; it_box++;
}
//returns true if we can merge two list of characters bounding boxes on a single row
if (merge_is_acceptable(l_0, l_plus_1) || merge_is_acceptable(l_minus_1, l_0) || merge_is_acceptable(l_minus_1, l_plus_1)) {
std::list<int>::iterator it_levels(levels.begin());
while (it_levels != levels.end()) {
*it_levels = 0;
it_levels++;
}
}
}
}
}
//returns true if we can merge two list of characters bounding boxes on a single row
bool merge_is_acceptable(const std::list<cv::Rect>& boxes_on_one_line, const std::list<cv::Rect>& boxes_on_another_line)
{//tous les characteres encore disponibles
//initialisation des sommes partielles
if (boxes_on_one_line.size() > 1 && boxes_on_another_line.size() > 1) {
C_Line line_sup_boxes_on_one_line; C_Line line_inf_boxes_on_one_line;
if (get_upperand_lower_lines
(boxes_on_one_line, line_sup_boxes_on_one_line, line_inf_boxes_on_one_line)) {
C_Line line_sup_boxes_on_another_line; C_Line line_inf_boxes_on_another_line;
if (get_upperand_lower_lines
(boxes_on_another_line, line_sup_boxes_on_another_line, line_inf_boxes_on_another_line)) {
#ifdef _DEBUG
float difference_y_from_center_debug = (line_sup_boxes_on_another_line.difference_y_from_center(boxes_on_one_line));
difference_y_from_center_debug = line_inf_boxes_on_another_line.difference_y_from_center(boxes_on_one_line);
difference_y_from_center_debug = line_sup_boxes_on_one_line.difference_y_from_center(boxes_on_another_line);
difference_y_from_center_debug = line_inf_boxes_on_one_line.difference_y_from_center(boxes_on_another_line);
difference_y_from_center_debug = get_median_height(boxes_on_one_line);
difference_y_from_center_debug = get_median_height(boxes_on_another_line);
#endif //_DEBUG
float difference_y_from_center = ((line_sup_boxes_on_another_line.difference_y_from_center(boxes_on_one_line) +
line_inf_boxes_on_another_line.difference_y_from_center(boxes_on_one_line)) / static_cast<float>(boxes_on_one_line.size())
-
(line_sup_boxes_on_one_line.difference_y_from_center(boxes_on_another_line) +
line_inf_boxes_on_one_line.difference_y_from_center(boxes_on_another_line)) / static_cast<float>(boxes_on_another_line.size())) / 4.0f;
return (fabs(difference_y_from_center) < get_median_height(boxes_on_one_line) && fabs(difference_y_from_center) < get_median_height(boxes_on_another_line));
}
else return false;
}
else return false;
}
else return false;
}
//characters on a license plate can be disposed on two lines (bi level) or on just one unique line (single level).
//anycase the ascii characters and there bouding boxes must nbe ordered in the inthe same way of the registration ascii chain.
void is_bi_level_plate(const std::list<cv::Rect>& boxes, const std::list<float>& l_confidences, const std::list<int>& l_classIds,
std::list<cv::Rect>& l_reordered, std::list<float>& l_reordered_confidences, std::list<int>& l_reordered_classIds, std::list<int>& levels)
{
if (boxes.size() > 1) {
//cette fonction trie la liste de gauche a droite
std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_reordered));
std::copy(l_confidences.begin(), l_confidences.end(), std::back_inserter(l_reordered_confidences));
std::copy(l_classIds.begin(), l_classIds.end(), std::back_inserter(l_reordered_classIds));
sort_from_left_to_right(l_reordered, l_reordered_confidences, l_reordered_classIds);
//la difference des angles des n-1 premiers characteres par rapport l'horizontale
//l'interdistance de chaque caractere avec son suivant pour les n-1 premiers characteres
std::list<float> angles_par_rapport_horiz;
float moy_angles_par_rapport_horiz;
float ecart_type_angles_consecutifs;
std::list<int> interdistances;
float moy_interdistances;
float ecart_type_interdistances;
float moy_produit_des_2; float ecart_type_produit_des_2;
filtre_grubbs_sides(l_reordered, angles_par_rapport_horiz,
moy_angles_par_rapport_horiz, ecart_type_angles_consecutifs, interdistances,
moy_interdistances, ecart_type_interdistances, moy_produit_des_2,
ecart_type_produit_des_2);
std::list<cv::Rect>::const_iterator it = l_reordered.begin();
std::list<float>::const_iterator it_angles(angles_par_rapport_horiz.begin());
while (it_angles != angles_par_rapport_horiz.end() && it != l_reordered.end()) {
if (fabs(*it_angles) > 0.785398f) {
//3.141593/4 pi/4
if (levels.size()) {
std::list<cv::Rect>::const_iterator it_next = it;
it_next++;
cv::Rect box_left(*it);
cv::Rect box_right(*it_next);
#ifdef _DEBUG
assert(iou(box_left, box_right) < 0.54);
#endif //_DEBUG
if (2 * box_left.y + box_left.height < 2 * box_right.y + box_right.height) {
//in this case box_left is on first line and box_right is on second line
std::list<int>::reverse_iterator it_levels(levels.rbegin());
while (it_levels != levels.rend()) {
if (*it_levels == 0) {
*it_levels = -1;
it_levels++;
}
else break;
}
if ((levels.back() == 1 || levels.back() == 0) && it_angles != angles_par_rapport_horiz.begin()) {
std::list<float>::const_iterator it_angles_pred(it_angles); it_angles_pred--;
if (fabs(*it_angles_pred) < fabs(*it_angles)) {
levels.back() = -1;
}
}
levels.push_back(1);
}
else {
std::list<int>::reverse_iterator it_levels(levels.rbegin());
while (it_levels != levels.rend()) {
if (*it_levels == 0) {
*it_levels = 1;
it_levels++;
}
else break;
}
if ((levels.back() == -1 || levels.back() == 0) && it_angles != angles_par_rapport_horiz.begin()) {
std::list<float>::const_iterator it_angles_pred(it_angles); it_angles_pred--;
if (fabs(*it_angles_pred) < fabs(*it_angles)) {
levels.back() = 1;
}
}
//in this case box_right is on first line and box_left is on second line
levels.push_back(-1);
}
}
else {
std::list<cv::Rect>::const_iterator it_next = it;
it_next++;
cv::Rect box_left(*it);
cv::Rect box_right(*it_next);
#ifdef _DEBUG
assert(iou(box_left, box_right) < 0.54);
#endif //_DEBUG
if (2 * box_left.y + box_left.height < 2 * box_right.y + box_right.height) {
//in this case box_left is on first line and box_right is on second line
levels.push_back(-1);
levels.push_back(1);
}
else {
//in this case box_right is on first line and box_left is on second line
levels.push_back(1);
levels.push_back(-1);
}
}
}
else {
//3.141593/4 pi/4
if (levels.size()) {
levels.push_back(levels.back());
}
else {
//in this case box_right is on first line and box_left is on second line
levels.push_back(0);
levels.push_back(levels.back());
}
}
it++;
it_angles++;
}
#ifdef _DEBUG
assert(levels.size() == angles_par_rapport_horiz.size() + 1 && levels.size() == l_reordered.size() && levels.size() == boxes.size());
#endif //_DEBUG
}
}//checks if the character of the lpn is a digit or a letter or something else (by ex examples misread carac or license plate bounding box, ...)
int is_digit(const char input)
{
if (
input == 'A' ||
input == 'B' ||
input == 'C' ||
input == 'D' ||
input == 'E' ||
input == 'F' ||
input == 'G' ||
input == 'H' ||
input == 'I' ||
input == 'J' ||
input == 'K' ||
input == 'L' ||
input == 'M' ||
input == 'N' ||
input == 'O' ||
input == 'P' ||
input == 'Q' ||
input == 'R' ||
input == 'S' ||
input == 'T' ||
input == 'U' ||
input == 'V' ||
input == 'W' ||
input == 'X' ||
input == 'Y' ||
input == 'Z') return 0;
else if (
input == '0' ||
input == '1' ||
input == '2' ||
input == '3' ||
input == '4' ||
input == '5' ||
input == '6' ||
input == '7' ||
input == '8' ||
input == '9') return 1;
else if (input == '0' ||
input == 'O') return 2;
else return -1;
}
void filter_out_everything_but_characters(std::list<cv::Rect>& boxes,
std::list<int>& l_classIds)
{
//***************************************************
// FILTER
//***************************************************
std::list<cv::Rect>::iterator it_boxes(boxes.begin());
std::list<int>::iterator it_out_classes(l_classIds.begin());
while (it_out_classes != l_classIds.end()
&& it_boxes != boxes.end()) {
if (!(*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& *it_out_classes >= 0)) {
it_out_classes = l_classIds.erase(it_out_classes);
it_boxes = boxes.erase(it_boxes);
}
else {
it_out_classes++;
it_boxes++;
}
}
}
void filter_out_everything_but_characters(std::list<cv::Rect>& boxes,
std::list<float>& l_confidences, std::list<int>& l_classIds)
{
//***************************************************
// FILTER
//***************************************************
std::list<float>::iterator it_confidences(l_confidences.begin());
std::list<cv::Rect>::iterator it_boxes(boxes.begin());
std::list<int>::iterator it_out_classes(l_classIds.begin());
while (it_out_classes != l_classIds.end()
&& it_confidences != l_confidences.end() && it_boxes != boxes.end()) {
if (!(*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& *it_out_classes >= 0)) {
it_out_classes = l_classIds.erase(it_out_classes);
it_confidences = l_confidences.erase(it_confidences);
it_boxes = boxes.erase(it_boxes);
}
else {
it_out_classes++;
it_confidences++;
it_boxes++;
}
}
}
//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
//vehicle--> returns 3
//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.
int is_this_box_a_character_a_license_plate_or_a_vehicle(const int classId, const int classId_last_country)
{
if (classId == -1) return 3;
else if (classId < 0) return 0;
else if (classId < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) return 1;
else if (classId <= classId_last_country) return 2;
else return 3;
}
//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.
int is_this_box_a_character(const int classId, const int number_of_characters_latin_numberplate)
{
if (classId == -1) return 3;
else if (classId < 0) return 0;
else if (classId < number_of_characters_latin_numberplate //- 1
) return 1;
else return 2;
}
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//it can deal with license pates that have two lines of charcaters
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void group_characters_in_the_same_license_plate(
//raw detections
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//first find license plates
std::list<cv::Rect> license_plates;
std::list<float> license_plates_confidences;
std::list<int> license_plates_classIds;
std::vector<float>::const_iterator it_confidences(confidences.begin());
std::vector<cv::Rect>::const_iterator it_boxes(boxes.begin());
std::vector<int>::const_iterator it_out_classes(classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.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
//vehicle--> returns 3
//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_a_license_plate_or_a_vehicle(*it_out_classes, classId_last_country) == 2) {
license_plates_confidences.push_back(*it_confidences);
license_plates.push_back(*it_boxes);
license_plates_classIds.push_back(*it_out_classes);
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
//second associate to each license plate a vehicle
std::list<cv::Rect> vehicles;
std::list<float> vehicles_confidences;
std::list<int> vehicles_classIds;
std::list<cv::Rect>::const_iterator it_license_plates(license_plates.begin());
std::list<float>::const_iterator it_license_plates_confidence(license_plates_confidences.begin());
std::list<int>::const_iterator it_license_plates_classIds(license_plates_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
) {
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
if (is_in_rect_if(*it_license_plates, *it_boxes) && (*it_license_plates_classIds != *it_out_classes)
&& *it_out_classes > classId_last_country) {
if (vehicles_confidences.empty()) {
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes);
vehicles_classIds.push_back(*it_out_classes);
}
else {
if (*it_confidences > vehicles_confidences.back()) {
vehicles_confidences.pop_back();
vehicles.pop_back(); vehicles_classIds.pop_back();
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes); vehicles_classIds.push_back(*it_out_classes);
}
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
if (vehicles_confidences.empty()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
vehicles_confidences.push_back(0.0f);
vehicles.push_back(cv::Rect(0, 0, 0, 0));
vehicles_classIds.push_back(-1);//index ==-1 suggests that there is in fact no vehicle for this plate
}
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
}
//now creates a vect of characters for each license plate that have been detected
it_license_plates = (license_plates.begin());
it_license_plates_confidence = (license_plates_confidences.begin());
it_license_plates_classIds = (license_plates_classIds.begin());
std::list<cv::Rect>::const_iterator it_vehicles(vehicles.begin());
std::list<float>::const_iterator it_vehicles_confidence(vehicles_confidences.begin());
std::list<int>::const_iterator it_vehicles_classIds(vehicles_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
&& it_vehicles != vehicles.end() && it_vehicles_confidence != vehicles_confidences.end()
&& it_vehicles_classIds != vehicles_classIds.end()
) {
l_vect_of_boxes_in_a_license_plate.push_back(std::vector<cv::Rect>());
l_vect_of_confidences_in_a_license_plate.push_back(std::vector<float>());
l_vect_of_classIds_in_a_license_plate.push_back(std::vector<int>());
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_license_plates);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_license_plates_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_license_plates_classIds);
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_vehicles);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_vehicles_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_vehicles_classIds);
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
it_vehicles_confidence++;
it_vehicles++;
it_vehicles_classIds++;
}
//now associate each character to license plates
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
std::list < std::vector<cv::Rect>>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::vector<float>>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::vector<int>>::iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_boxes_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size() && it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size()
&& it_l_vect_of_boxes_in_a_license_plate->size() >= 2);
#endif //_DEBUG
//front is license plate
if (is_in_rect_if(*it_boxes, it_l_vect_of_boxes_in_a_license_plate->front(), 0.5f)) {
it_l_vect_of_boxes_in_a_license_plate->push_back(*it_boxes);
it_l_vect_of_confidences_in_a_license_plate->push_back(*it_confidences);
it_l_vect_of_classIds_in_a_license_plate->push_back(*it_out_classes);
}
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
}
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
//it can deal with license pates that have two lines of charcaters
void group_characters_in_the_same_license_plate(
//raw detections
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::list<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//first find license plates
std::list<cv::Rect> license_plates;
std::list<float> license_plates_confidences;
std::list<int> license_plates_classIds;
std::vector<float>::const_iterator it_confidences(confidences.begin());
std::vector<cv::Rect>::const_iterator it_boxes(boxes.begin());
std::vector<int>::const_iterator it_out_classes(classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.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
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_out_classes, classId_last_country) == 2) {
license_plates_confidences.push_back(*it_confidences);
license_plates.push_back(*it_boxes);
license_plates_classIds.push_back(*it_out_classes);
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
//second associate to each license plate a vehicle
std::list<cv::Rect> vehicles;
std::list<float> vehicles_confidences;
std::list<int> vehicles_classIds;
std::list<cv::Rect>::const_iterator it_license_plates(license_plates.begin());
std::list<float>::const_iterator it_license_plates_confidence(license_plates_confidences.begin());
std::list<int>::const_iterator it_license_plates_classIds(license_plates_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
) {
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
if (is_in_rect_if(*it_license_plates, *it_boxes) && (*it_license_plates_classIds != *it_out_classes)
&& *it_out_classes > classId_last_country) {
if (vehicles_confidences.empty()) {
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes);
vehicles_classIds.push_back(*it_out_classes);
}
else {
if (*it_confidences > vehicles_confidences.back()) {
vehicles_confidences.pop_back();
vehicles.pop_back(); vehicles_classIds.pop_back();
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes); vehicles_classIds.push_back(*it_out_classes);
}
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
if (vehicles_confidences.empty()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
vehicles_confidences.push_back(0.0f);
vehicles.push_back(cv::Rect(0, 0, 0, 0));
vehicles_classIds.push_back(-1);//index ==-1 suggests that there is in fact no vehicle for this plate
}
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
}
//now creates a vect of characters for each license plate that have been detected
it_license_plates = (license_plates.begin());
it_license_plates_confidence = (license_plates_confidences.begin());
it_license_plates_classIds = (license_plates_classIds.begin());
std::list<cv::Rect>::const_iterator it_vehicles(vehicles.begin());
std::list<float>::const_iterator it_vehicles_confidence(vehicles_confidences.begin());
std::list<int>::const_iterator it_vehicles_classIds(vehicles_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
&& it_vehicles != vehicles.end() && it_vehicles_confidence != vehicles_confidences.end()
&& it_vehicles_classIds != vehicles_classIds.end()
) {
l_vect_of_boxes_in_a_license_plate.push_back(std::list<cv::Rect>());
l_vect_of_confidences_in_a_license_plate.push_back(std::list<float>());
l_vect_of_classIds_in_a_license_plate.push_back(std::list<int>());
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_license_plates);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_license_plates_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_license_plates_classIds);
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_vehicles);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_vehicles_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_vehicles_classIds);
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
it_vehicles_confidence++;
it_vehicles++;
it_vehicles_classIds++;
}
//now associate each character to license plates
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
std::list < std::list<cv::Rect>>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::list<float>>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::list<int>>::iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_boxes_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size() && it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size()
&& it_l_vect_of_boxes_in_a_license_plate->size() >= 2);
#endif //_DEBUG
//front is license plate
if (is_in_rect_if(*it_boxes, it_l_vect_of_boxes_in_a_license_plate->front(), 0.5f)) {
it_l_vect_of_boxes_in_a_license_plate->push_back(*it_boxes);
it_l_vect_of_confidences_in_a_license_plate->push_back(*it_confidences);
it_l_vect_of_classIds_in_a_license_plate->push_back(*it_out_classes);
}
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
}
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
//it can deal with license pates that have two lines of charcaters
void group_characters_in_the_same_license_plate(
//raw detections
const std::list<cv::Rect>& boxes,
const std::list<float>& confidences, const std::list<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::list<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//first find license plates
std::list<cv::Rect> license_plates;
std::list<float> license_plates_confidences;
std::list<int> license_plates_classIds;
std::list<float>::const_iterator it_confidences(confidences.begin());
std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
std::list<int>::const_iterator it_out_classes(classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.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
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_out_classes, classId_last_country) == 2) {
license_plates_confidences.push_back(*it_confidences);
license_plates.push_back(*it_boxes);
license_plates_classIds.push_back(*it_out_classes);
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
//second associate to each license plate a vehicle
std::list<cv::Rect> vehicles;
std::list<float> vehicles_confidences;
std::list<int> vehicles_classIds;
std::list<cv::Rect>::const_iterator it_license_plates(license_plates.begin());
std::list<float>::const_iterator it_license_plates_confidence(license_plates_confidences.begin());
std::list<int>::const_iterator it_license_plates_classIds(license_plates_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
) {
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
if (is_in_rect_if(*it_license_plates, *it_boxes) && (*it_license_plates_classIds != *it_out_classes)
&& *it_out_classes > classId_last_country) {
if (vehicles_confidences.empty()) {
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes);
vehicles_classIds.push_back(*it_out_classes);
}
else {
if (*it_confidences > vehicles_confidences.back()) {
vehicles_confidences.pop_back();
vehicles.pop_back(); vehicles_classIds.pop_back();
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes); vehicles_classIds.push_back(*it_out_classes);
}
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
if (vehicles_confidences.empty()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
vehicles_confidences.push_back(0.0f);
vehicles.push_back(cv::Rect(0, 0, 0, 0));
vehicles_classIds.push_back(-1);//index ==-1 suggests that there is in fact no vehicle for this plate
}
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
}
//now creates a vect of characters for each license plate that have been detected
it_license_plates = (license_plates.begin());
it_license_plates_confidence = (license_plates_confidences.begin());
it_license_plates_classIds = (license_plates_classIds.begin());
std::list<cv::Rect>::const_iterator it_vehicles(vehicles.begin());
std::list<float>::const_iterator it_vehicles_confidence(vehicles_confidences.begin());
std::list<int>::const_iterator it_vehicles_classIds(vehicles_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
&& it_vehicles != vehicles.end() && it_vehicles_confidence != vehicles_confidences.end()
&& it_vehicles_classIds != vehicles_classIds.end()
) {
l_vect_of_boxes_in_a_license_plate.push_back(std::list<cv::Rect>());
l_vect_of_confidences_in_a_license_plate.push_back(std::list<float>());
l_vect_of_classIds_in_a_license_plate.push_back(std::list<int>());
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_license_plates);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_license_plates_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_license_plates_classIds);
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_vehicles);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_vehicles_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_vehicles_classIds);
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
it_vehicles_confidence++;
it_vehicles++;
it_vehicles_classIds++;
}
//now associate each character to license plates
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
std::list < std::list<cv::Rect>>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::list<float>>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::list<int>>::iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_boxes_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size() && it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size()
&& it_l_vect_of_boxes_in_a_license_plate->size() >= 2);
#endif //_DEBUG
//front is license plate
if (is_in_rect_if(*it_boxes, it_l_vect_of_boxes_in_a_license_plate->front(), 0.5f)) {
it_l_vect_of_boxes_in_a_license_plate->push_back(*it_boxes);
it_l_vect_of_confidences_in_a_license_plate->push_back(*it_confidences);
it_l_vect_of_classIds_in_a_license_plate->push_back(*it_out_classes);
}
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
}
//the dnn has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void group_characters_in_the_same_license_plate(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector<int>>& l_vect_of_classIds_in_a_license_plate, const int classId_last_country
//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//first find license plates
std::list<cv::Rect> license_plates;
std::list<float> license_plates_confidences;
std::list<int> license_plates_classIds;
std::list<float>::const_iterator it_confidences(confidences.begin());
std::list<cv::Rect>::const_iterator it_boxes(boxes.begin());
std::list<int>::const_iterator it_out_classes(classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.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
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_out_classes, classId_last_country) == 2) {
license_plates_confidences.push_back(*it_confidences);
license_plates.push_back(*it_boxes);
license_plates_classIds.push_back(*it_out_classes);
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
//second associate to each license plate a vehicle
std::list<cv::Rect> vehicles;
std::list<float> vehicles_confidences;
std::list<int> vehicles_classIds;
std::list<cv::Rect>::const_iterator it_license_plates(license_plates.begin());
std::list<float>::const_iterator it_license_plates_confidence(license_plates_confidences.begin());
std::list<int>::const_iterator it_license_plates_classIds(license_plates_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
) {
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
if (is_in_rect_if(*it_license_plates, *it_boxes) && (*it_license_plates_classIds != *it_out_classes)
&& *it_out_classes > classId_last_country) {
if (vehicles_confidences.empty()) {
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes);
vehicles_classIds.push_back(*it_out_classes);
}
else {
if (*it_confidences > vehicles_confidences.back()) {
vehicles_confidences.pop_back();
vehicles.pop_back(); vehicles_classIds.pop_back();
vehicles_confidences.push_back(*it_confidences);
vehicles.push_back(*it_boxes); vehicles_classIds.push_back(*it_out_classes);
}
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
if (vehicles_confidences.empty()) {
#ifdef _DEBUG
assert(vehicles_confidences.size() == vehicles.size() && vehicles_confidences.size() == vehicles_classIds.size());
#endif //_DEBUG
vehicles_confidences.push_back(0.0f);
vehicles.push_back(cv::Rect(0, 0, 0, 0));
vehicles_classIds.push_back(-1);//index ==-1 suggests that there is in fact no vehicle for this plate
}
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
}
//now creates a vect of characters for each license plate that have been detected
it_license_plates = (license_plates.begin());
it_license_plates_confidence = (license_plates_confidences.begin());
it_license_plates_classIds = (license_plates_classIds.begin());
std::list<cv::Rect>::const_iterator it_vehicles(vehicles.begin());
std::list<float>::const_iterator it_vehicles_confidence(vehicles_confidences.begin());
std::list<int>::const_iterator it_vehicles_classIds(vehicles_classIds.begin());
while (it_license_plates != license_plates.end() && it_license_plates_confidence != license_plates_confidences.end()
&& it_license_plates_classIds != license_plates_classIds.end()
&& it_vehicles != vehicles.end() && it_vehicles_confidence != vehicles_confidences.end()
&& it_vehicles_classIds != vehicles_classIds.end()
) {
l_vect_of_boxes_in_a_license_plate.push_back(std::vector<cv::Rect>());
l_vect_of_confidences_in_a_license_plate.push_back(std::vector<float>());
l_vect_of_classIds_in_a_license_plate.push_back(std::vector<int>());
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_license_plates);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_license_plates_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_license_plates_classIds);
l_vect_of_boxes_in_a_license_plate.back().push_back(*it_vehicles);
l_vect_of_confidences_in_a_license_plate.back().push_back(*it_vehicles_confidence);
l_vect_of_classIds_in_a_license_plate.back().push_back(*it_vehicles_classIds);
it_license_plates_confidence++;
it_license_plates++;
it_license_plates_classIds++;
it_vehicles_confidence++;
it_vehicles++;
it_vehicles_classIds++;
}
//now associate each character to license plates
it_confidences = (confidences.begin());
it_boxes = (boxes.begin());
it_out_classes = (classIds.begin());
while (it_out_classes != classIds.end()
&& it_confidences != confidences.end() && it_boxes != boxes.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
std::list < std::vector<cv::Rect>>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::vector<float>>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::vector<int>>::iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_boxes_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size() && it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size()
&& it_l_vect_of_boxes_in_a_license_plate->size() >= 2);
#endif //_DEBUG
//front is license plate
if (is_in_rect_if(*it_boxes, it_l_vect_of_boxes_in_a_license_plate->front(), 0.5f)) {
it_l_vect_of_boxes_in_a_license_plate->push_back(*it_boxes);
it_l_vect_of_confidences_in_a_license_plate->push_back(*it_confidences);
it_l_vect_of_classIds_in_a_license_plate->push_back(*it_out_classes);
}
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
}
it_out_classes++;
it_confidences++;
it_boxes++;
}
}
void substitute(const int index, const int classId, std::list<int >& classes) {
int current_index = 0;
std::list<int>::iterator it_classe(classes.begin());
while (it_classe != classes.end()) {
if (current_index == index) {
*it_classe = classId;
break;
}
current_index++;
it_classe++;
}
}
//compares two characters return true if there is an admissible switch between the two characters (by ex O eand 0). classId is the class index of the character that belongs to the true lpn
//(later erroneous_character will be changed to classId)
bool substitution_admissible(const char VraiLPN_character, const char erroneous_character, int& classId)
{
classId = -1;
if (VraiLPN_character == erroneous_character) return false;
else {
//we dont accept any substitution
return false;
}
}
int index_character_substitution(const std::string& ExactLPN, const std::string& lpn, char& VraiLPN_character, char& erroneous_character) {
if (ExactLPN.length() == lpn.length()) {
std::string::const_iterator it_VraiLPN(ExactLPN.begin());
std::string::const_iterator it(lpn.begin());
int index = 0;
while (it != lpn.end() && it_VraiLPN != ExactLPN.end()) {
if (*it != *it_VraiLPN) {
VraiLPN_character = *it_VraiLPN;
erroneous_character = *it;
return index;
}
else { it++; it_VraiLPN++; index++; }
}return -1;
}
else return -1;
}
//we know the true license plate number that come from a training image and we want to find the detections boxes to aautomatically annotate the image.
//We also have run the nn that produces detections, the goal of this func is to find the detections that are closest to the true lpn
int find_nearest_plate_substitutions_allowed(const std::string& ExactLPN,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, const std::list <int>& lp_country_class, const std::list < cv::Rect>& lp_rois, const
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, const std::list < std::list<cv::Rect>>& boxes,
//output = nearest lpn + its class + its bounding box
std::string& best_lpn, int& best_country_class, cv::Rect& best_lpn_roi,
//output = characters in nearest lpn
std::list<float>& best_confidences, std::list<int>& best_classes, std::list<cv::Rect>& best_boxes)
{
int min_editdistance = SHRT_MAX;
Levenshtein lev;
std::list <std::string>::iterator it_lpns(lpns.begin());
std::list <int>::const_iterator it_country_class(lp_country_class.begin());
std::list < cv::Rect>::const_iterator it_lpn_roi(lp_rois.begin());
std::list < std::list<float>>::const_iterator it_confidences(confidences.begin());
std::list < std::list<int>>::iterator it_classes(classes.begin());
std::list < std::list<cv::Rect>>::const_iterator it_boxes(boxes.begin());
while (it_lpns != lpns.end() && it_country_class != lp_country_class.end() &&
it_lpn_roi != lp_rois.end() &&
it_confidences != confidences.end() && it_classes != classes.end() && it_boxes != boxes.end())
{
int editdistance = lev.Get(ExactLPN.c_str(), ExactLPN.length(), it_lpns->c_str(), it_lpns->length());
if (min_editdistance > editdistance) {
min_editdistance = editdistance;
best_lpn = *it_lpns;
best_country_class = *it_country_class;
best_lpn_roi = *it_lpn_roi;
best_confidences = *it_confidences;
best_classes = *it_classes;
best_boxes = *it_boxes;
}
it_lpns++;
it_country_class++;
it_lpn_roi++;
it_confidences++;
it_classes++;
it_boxes++;
}
return min_editdistance;
}
//For each plate in the image, the detections have been separated. From these, we select the detections of the plates that have have the best detection score.
void get_best_plate(
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp)
{
std::list < std::list<cv::Rect>>::const_iterator it_boxes(boxes.begin());
std::list < std::list<float>>::const_iterator it_confidences(confidences.begin());
std::list < std::list<int>>::const_iterator it_classIds(classIds.begin());
float best_score = 0.0f;
while (it_boxes != boxes.end()
&& it_confidences != confidences.end() && it_classIds != classIds.end()) {
#ifdef _DEBUG
assert(it_classIds->size() == it_confidences->size() && it_classIds->size() == it_boxes->size() && it_classIds->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_classIds) == 1);
#endif //_DEBUG
float current_score = //(float)(it_boxes->size() - 2) *
//From confidences of detections of all boxes of a plate, we get the average confidence.
(it_boxes->front().width * it_boxes->front().height) * get_average_confidence_of_license_plate(*it_classIds,
*it_confidences);
if (best_score < current_score)
{
best_score = current_score;
confidence_one_lp.clear();
one_lp.clear();
classIds_one_lp.clear();
std::copy(it_confidences->begin(), it_confidences->end(), std::back_inserter(confidence_one_lp));
std::copy(it_boxes->begin(), it_boxes->end(), std::back_inserter(one_lp));
std::copy(it_classIds->begin(), it_classIds->end(), std::back_inserter(classIds_one_lp));
}
it_boxes++;
it_confidences++;
it_classIds++;
}
}
//For each plate in the image, the detections have been separated. From these, we select the detections of the plates that have have the best detection score.
void get_best_plate(const std::string& ExactLPN,
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp)
{
std::list < std::list<cv::Rect>>::const_iterator it_boxes(boxes.begin());
std::list < std::list<float>>::const_iterator it_confidences(confidences.begin());
std::list < std::list<int>>::const_iterator it_classIds(classIds.begin());
float best_score = 0.0f;
Levenshtein lev;
int best_editdistance = SHRT_MAX;
size_t best_lpn_length = -1;
while (it_boxes != boxes.end()
&& it_confidences != confidences.end() && it_classIds != classIds.end()) {
#ifdef _DEBUG
assert(it_classIds->size() == it_confidences->size() && it_classIds->size() == it_boxes->size() && it_classIds->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_classIds) == 1);
#endif //_DEBUG
float current_score = //(float)(it_boxes->size() - 2) *
//From confidences of detections of all boxes of a plate, we get the average confidence.
(it_boxes->front().width * it_boxes->front().height) * get_average_confidence_of_license_plate(*it_classIds,
*it_confidences);
std::vector<cv::Rect> tri_left_vect_of_detected_boxes; std::vector<float> tri_left_confidences; std::vector<int> tri_left_classIds;
std::string lpn = get_lpn(*it_boxes, *it_confidences, *it_classIds, tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds);
int editdistance = lev.Get(ExactLPN.c_str(), ExactLPN.length(), lpn.c_str(), lpn.length());
if (best_editdistance > editdistance || (best_lpn_length != ExactLPN.length() && lpn.length() == ExactLPN.length() && best_editdistance == editdistance)
|| (best_score < current_score && best_editdistance == editdistance && !(best_lpn_length == ExactLPN.length() && lpn.length() != ExactLPN.length()))) {
best_lpn_length = lpn.length();
best_editdistance = editdistance;
best_score = current_score;
confidence_one_lp.clear();
one_lp.clear();
classIds_one_lp.clear();
std::copy(it_confidences->begin(), it_confidences->end(), std::back_inserter(confidence_one_lp));
std::copy(it_boxes->begin(), it_boxes->end(), std::back_inserter(one_lp));
std::copy(it_classIds->begin(), it_classIds->end(), std::back_inserter(classIds_one_lp));
}
it_boxes++;
it_confidences++;
it_classIds++;
}
}
//For each plate in the image, the detections have been separated. From these, we select the detections of the plates that have have the best detection score.
void get_best_plate(const std::string& ExactLPN,
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::vector<float>& confidence_one_lp, std::vector < cv::Rect>& one_lp, std::vector<int>& classIds_one_lp)
{
std::list < std::list<cv::Rect>>::const_iterator it_boxes(boxes.begin());
std::list < std::list<float>>::const_iterator it_confidences(confidences.begin());
std::list < std::list<int>>::const_iterator it_classIds(classIds.begin());
float best_score = 0.0f;
Levenshtein lev;
int best_editdistance = SHRT_MAX;
size_t best_lpn_length = -1;
while (it_boxes != boxes.end()
&& it_confidences != confidences.end() && it_classIds != classIds.end()) {
#ifdef _DEBUG
assert(it_classIds->size() == it_confidences->size() && it_classIds->size() == it_boxes->size() && it_classIds->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_classIds) == 1);
#endif //_DEBUG
float current_score = //(float)(it_boxes->size() - 2) *
//From confidences of detections of all boxes of a plate, we get the average confidence.
(it_boxes->front().width * it_boxes->front().height) * get_average_confidence_of_license_plate(*it_classIds,
*it_confidences);
std::vector<cv::Rect> tri_left_vect_of_detected_boxes; std::vector<float> tri_left_confidences; std::vector<int> tri_left_classIds;
std::string lpn = get_lpn(*it_boxes, *it_confidences, *it_classIds, tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds);
int editdistance = lev.Get(ExactLPN.c_str(), ExactLPN.length(), lpn.c_str(), lpn.length());
if (best_editdistance > editdistance || (best_lpn_length != ExactLPN.length() && lpn.length() == ExactLPN.length() && best_editdistance == editdistance)
|| (best_score < current_score && best_editdistance == editdistance && !(best_lpn_length == ExactLPN.length() && lpn.length() != ExactLPN.length()))) {
best_lpn_length = lpn.length();
best_editdistance = editdistance;
best_score = current_score;
confidence_one_lp.clear();
one_lp.clear();
classIds_one_lp.clear();
std::copy(it_confidences->begin(), it_confidences->end(), std::back_inserter(confidence_one_lp));
std::copy(it_boxes->begin(), it_boxes->end(), std::back_inserter(one_lp));
std::copy(it_classIds->begin(), it_classIds->end(), std::back_inserter(classIds_one_lp));
}
it_boxes++;
it_confidences++;
it_classIds++;
}
}
//the nnet has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_lpn(
const std::list<cv::Rect>& l_of_detected_boxes,
const std::list<float>& l_confidences, const std::list<int>& l_classIds,
//list of characters inside the lp
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds,
float nmsThreshold
) {
std::vector<cv::Rect> vect_of_detected_boxes;
std::vector<float> confidences;
std::vector<int> classIds;
std::copy(l_of_detected_boxes.begin(), l_of_detected_boxes.end(), std::back_inserter(vect_of_detected_boxes));
std::copy(l_confidences.begin(), l_confidences.end(), std::back_inserter(confidences));
std::copy(l_classIds.begin(), l_classIds.end(), std::back_inserter(classIds));
return get_single_lpn(
vect_of_detected_boxes, confidences, classIds,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds,
nmsThreshold);
}
//For each plate in the image, the detections have been separated. From these, we select the detections of the plates that have have the best detection score.
//Uses double linked lists : inside list is for characters and outside list is for plates.
void get_best_plate(
//detections when they are separated license plates by license plates
const std::list < std::vector<int>>& classIds, const std::list < std::vector<float>>& confidences, const std::list < std::vector<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp)
{
std::list < std::vector<cv::Rect>>::const_iterator it_boxes(boxes.begin());
std::list < std::vector<float>>::const_iterator it_confidences(confidences.begin());
std::list < std::vector<int>>::const_iterator it_classIds(classIds.begin());
float best_score = 0.0f;
while (it_boxes != boxes.end()
&& it_confidences != confidences.end() && it_classIds != classIds.end()) {
#ifdef _DEBUG
assert(it_classIds->size() == it_confidences->size() && it_classIds->size() == it_boxes->size() && it_classIds->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_classIds) == 1);
#endif //_DEBUG
float current_score = //(float)(it_boxes->size() - 2) *
//From confidences of detections of all boxes of a plate, we get the average confidence.
(it_boxes->front().width * it_boxes->front().height) * get_average_confidence_of_license_plate(*it_classIds,
*it_confidences);
if (best_score < current_score)
{
best_score = current_score;
confidence_one_lp.clear();
one_lp.clear();
classIds_one_lp.clear();
std::copy(it_confidences->begin(), it_confidences->end(), std::back_inserter(confidence_one_lp));
std::copy(it_boxes->begin(), it_boxes->end(), std::back_inserter(one_lp));
std::copy(it_classIds->begin(), it_classIds->end(), std::back_inserter(classIds_one_lp));
}
it_boxes++;
it_confidences++;
it_classIds++;
}
}
//For each plate in the image, the detections have been separated. From these, we select the detections of the plates that have have the best detection score.
void add_lp_and_vehicle(const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes, std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds
, const int classId_last_country
//classId_last_country : is the class index of the last country in the list of detected classes.
)
{
std::list<cv::Rect> l_tri_left_vect_of_detected_boxes;
std::list<float> l_tri_left_confidences;
std::list<int> l_tri_left_classIds;
std::copy(tri_left_vect_of_detected_boxes.begin(), tri_left_vect_of_detected_boxes.end(), std::back_inserter(l_tri_left_vect_of_detected_boxes));
std::copy(tri_left_confidences.begin(), tri_left_confidences.end(), std::back_inserter(l_tri_left_confidences));
std::copy(tri_left_classIds.begin(), tri_left_classIds.end(), std::back_inserter(l_tri_left_classIds));
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
add_lp_and_vehicle(boxes, confidences, classIds,
l_tri_left_vect_of_detected_boxes, l_tri_left_confidences, l_tri_left_classIds
, classId_last_country);
std::copy(l_tri_left_vect_of_detected_boxes.begin(), l_tri_left_vect_of_detected_boxes.end(), std::back_inserter(tri_left_vect_of_detected_boxes));
std::copy(l_tri_left_confidences.begin(), l_tri_left_confidences.end(), std::back_inserter(tri_left_confidences));
std::copy(l_tri_left_classIds.begin(), l_tri_left_classIds.end(), std::back_inserter(tri_left_classIds));
}
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
void add_lp_and_vehicle(const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes, std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
)
{
std::list<cv::Rect> l_tri_left_vect_of_detected_boxes;
std::list<float> l_tri_left_confidences;
std::list<int> l_tri_left_classIds;
std::copy(tri_left_vect_of_detected_boxes.begin(), tri_left_vect_of_detected_boxes.end(), std::back_inserter(l_tri_left_vect_of_detected_boxes));
std::copy(tri_left_confidences.begin(), tri_left_confidences.end(), std::back_inserter(l_tri_left_confidences));
std::copy(tri_left_classIds.begin(), tri_left_classIds.end(), std::back_inserter(l_tri_left_classIds));
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
add_lp_and_vehicle(boxes, confidences, classIds,
l_tri_left_vect_of_detected_boxes, l_tri_left_confidences, l_tri_left_classIds
, classId_last_country);
std::copy(l_tri_left_vect_of_detected_boxes.begin(), l_tri_left_vect_of_detected_boxes.end(), std::back_inserter(tri_left_vect_of_detected_boxes));
std::copy(l_tri_left_confidences.begin(), l_tri_left_confidences.end(), std::back_inserter(tri_left_confidences));
std::copy(l_tri_left_classIds.begin(), l_tri_left_classIds.end(), std::back_inserter(tri_left_classIds));
}
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
void add_lp_and_vehicle(const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
std::list<cv::Rect>& tri_left_vect_of_detected_boxes, std::list<float>& tri_left_confidences, std::list<int>& tri_left_classIds
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
)
{
std::list < cv::Rect>::const_iterator it_boxes(boxes.begin());
std::list < float>::const_iterator it_confidences(confidences.begin());
std::list < int>::const_iterator it_classIds(classIds.begin());
cv::Rect lp(0, 0, 0, 0);
int lp_classIds = -2;
float lp_confidence = .0f;
//first scan raw detections to find lp that can encapsulate tri_left_vect_of_detected_boxes
while (it_boxes != boxes.end() && it_confidences != confidences.end() && it_classIds != classIds.end()) {//single character--> returns 1
//license plate--> returns 2
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_classIds, classId_last_country) == 2)
{//for each box in the container, check that it is nearly entirely contained in the second argument
if (is_in_rect_if(tri_left_vect_of_detected_boxes, *it_boxes)) {
if (lp.area() > it_boxes->area() || (lp_classIds == -2)) {
lp_classIds = *it_classIds;
lp = *it_boxes;
lp_confidence = *it_confidences;
}
}
}
it_boxes++;
it_confidences++;
it_classIds++;
}
if (is_this_box_a_character_a_license_plate_or_a_vehicle(lp_classIds, classId_last_country) == 2) {
#ifdef _DEBUG
assert(lp.area() > 0 && lp_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& lp_confidence > 0.0f);
#endif //_DEBUG
}
else {
#ifdef _DEBUG
assert(lp == cv::Rect(0, 0, 0, 0) && lp_classIds == -2 && lp_confidence == 0.0f);
#endif //_DEBUG
//be carefull there, we cannot just invoke lp = get_global_rect(boxes); since boxes may contains the vehicle box
std::list<cv::Rect> l_boxes;
std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes));
std::list<float> l_confidences;
std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences));
std::list<int> l_classIds;
std::copy(classIds.begin(), classIds.end(), std::back_inserter(l_classIds));
sort_from_left_to_right(l_boxes, l_confidences, l_classIds);//sorts all the boxes from left to right
//filter out lpn box
//***************************************************
// FILTER
//***************************************************
filter_out_everything_but_characters(l_boxes,
l_confidences, l_classIds);
lp = get_global_rect(l_boxes);
lp_classIds = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
;
lp_confidence = 0.8f;
}
it_boxes = (boxes.begin());
it_confidences = (confidences.begin());
it_classIds = (classIds.begin());
cv::Rect vehicle(0, 0, 0, 0);
int vehicle_classIds = -1;
float vehicle_confidence = .0f;
//first scan raw detections to find vehicle that can encapsulate tri_left_vect_of_detected_boxes
while (it_boxes != boxes.end() && it_confidences != confidences.end() && it_classIds != classIds.end()) {//single character--> returns 1
//license plate--> returns 2
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_classIds, classId_last_country) == 3)
{//for each box in the container, check that it is nearly entirely contained in the second argument
if (is_in_rect_if(lp, *it_boxes)) {
if (vehicle.area() > it_boxes->area() || (vehicle_classIds == -1)) {
vehicle_classIds = *it_classIds;
vehicle = *it_boxes;
vehicle_confidence = *it_confidences;
}
}
}
it_boxes++;
it_confidences++;
it_classIds++;
}
#ifdef _DEBUG
assert((vehicle.area() > 0 && vehicle_classIds > classId_last_country&& vehicle_confidence > 0.0f) ||
(vehicle == cv::Rect(0, 0, 0, 0) && vehicle_classIds == -1 && vehicle_confidence == 0.0f)
);
#endif //_DEBUG
#ifdef _DEBUG
assert(lp.area() > 0 && lp_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& lp_confidence > 0.0f);
#endif //_DEBUG
//now add vehicle at the beginning of the detections
tri_left_vect_of_detected_boxes.push_front(vehicle);
tri_left_confidences.push_front(vehicle_confidence);
tri_left_classIds.push_front(vehicle_classIds);
//now add lp at the beginning of the detections
tri_left_vect_of_detected_boxes.push_front(lp);
tri_left_confidences.push_front(lp_confidence);
tri_left_classIds.push_front(lp_classIds);
}
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
void add_lp_and_vehicle(const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
std::list<cv::Rect>& tri_left_vect_of_detected_boxes, std::list<float>& tri_left_confidences, std::list<int>& tri_left_classIds
, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
)
{
std::vector < cv::Rect>::const_iterator it_boxes(boxes.begin());
std::vector < float>::const_iterator it_confidences(confidences.begin());
std::vector < int>::const_iterator it_classIds(classIds.begin());
cv::Rect lp(0, 0, 0, 0);
int lp_classIds = -2;
float lp_confidence = .0f;
//first scan raw detections to find lp that can encapsulate tri_left_vect_of_detected_boxes
while (it_boxes != boxes.end() && it_confidences != confidences.end() && it_classIds != classIds.end()) {//single character--> returns 1
//license plate--> returns 2
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_classIds, classId_last_country) == 2)
{//for each box in the container, check that it is nearly entirely contained in the second argument
if (is_in_rect_if(tri_left_vect_of_detected_boxes, *it_boxes)) {
if (lp.area() > it_boxes->area() || (lp_classIds == -2)) {
lp_classIds = *it_classIds;
lp = *it_boxes;
lp_confidence = *it_confidences;
}
}
}
it_boxes++;
it_confidences++;
it_classIds++;
}
if (is_this_box_a_character_a_license_plate_or_a_vehicle(lp_classIds, classId_last_country) == 2) {
#ifdef _DEBUG
assert(lp.area() > 0 && lp_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& lp_confidence > 0.0f);
#endif //_DEBUG
}
else {
#ifdef _DEBUG
assert(lp == cv::Rect(0, 0, 0, 0) && lp_classIds == -2 && lp_confidence == 0.0f);
#endif //_DEBUG
std::list<cv::Rect> l_boxes;
std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes));
std::list<float> l_confidences;
std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences));
std::list<int> l_classIds;
std::copy(classIds.begin(), classIds.end(), std::back_inserter(l_classIds));
sort_from_left_to_right(l_boxes, l_confidences, l_classIds);//sorts all the boxes from left to right
//filter out lpn box
//***************************************************
// FILTER
//***************************************************
filter_out_everything_but_characters(l_boxes,
l_confidences, l_classIds);
lp = get_global_rect(l_boxes); lp_classIds = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
;
lp_confidence = 0.8f;
}
it_boxes = (boxes.begin());
it_confidences = (confidences.begin());
it_classIds = (classIds.begin());
cv::Rect vehicle(0, 0, 0, 0);
int vehicle_classIds = -1;
float vehicle_confidence = .0f;
//first scan raw detections to find vehicle that can encapsulate tri_left_vect_of_detected_boxes
while (it_boxes != boxes.end() && it_confidences != confidences.end() && it_classIds != classIds.end()) {//single character--> returns 1
//license plate--> returns 2
//vehicle--> returns 3
//negative index--> returns 0 must be an error
if (is_this_box_a_character_a_license_plate_or_a_vehicle(*it_classIds, classId_last_country) == 3)
{//for each box in the container, check that it is nearly entirely contained in the second argument
if (is_in_rect_if(lp, *it_boxes)) {
if (vehicle.area() > it_boxes->area() || (vehicle_classIds == -1)) {
vehicle_classIds = *it_classIds;
vehicle = *it_boxes;
vehicle_confidence = *it_confidences;
}
}
}
it_boxes++;
it_confidences++;
it_classIds++;
}
#ifdef _DEBUG
assert((vehicle.area() > 0 && vehicle_classIds > classId_last_country&& vehicle_confidence > 0.0f) ||
(vehicle == cv::Rect(0, 0, 0, 0) && vehicle_classIds == -1 && vehicle_confidence == 0.0f)
);
#endif //_DEBUG
#ifdef _DEBUG
assert(lp.area() > 0 && lp_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
&& lp_confidence > 0.0f);
#endif //_DEBUG
//now add vehicle at the beginning of the detections
tri_left_vect_of_detected_boxes.push_front(vehicle);
tri_left_confidences.push_front(vehicle_confidence);
tri_left_classIds.push_front(vehicle_classIds);
//now add lp at the beginning of the detections
tri_left_vect_of_detected_boxes.push_front(lp);
tri_left_confidences.push_front(lp_confidence);
tri_left_classIds.push_front(lp_classIds);
}
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
int is_detections_of_a_unique_license_plate(const std::vector<int>& classIds)
{
if (classIds.size() >= 2) {
const int classId_last_country = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE + NUMBER_OF_COUNTRIES - 1;
std::vector<int>::const_iterator it_classIds(classIds.begin());
if (*it_classIds < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
|| *it_classIds >= classId_last_country) return 4;//first detection is not a plate
it_classIds++;
if (*it_classIds < classId_last_country && *it_classIds>0) return 4;//second detection is not a vehicle
it_classIds++;
while (it_classIds != classIds.end()) {
if (*it_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
|| *it_classIds < 0)
return 6;//detection after first two ones, is not a character
it_classIds++;
} return 1;//ok
}
else return 2;//size too small
}
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
int is_detections_of_a_unique_license_plate(const std::list<int>& classIds)
{
if (classIds.size() >= 2) {
//classId_last_country : is the class index of the last country in the list of detected classes.
const int classId_last_country = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE + NUMBER_OF_COUNTRIES - 1;
std::list<int>::const_iterator it_classIds(classIds.begin());
if (*it_classIds < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
|| *it_classIds > classId_last_country) return 4;//first detection is not a plate
it_classIds++;
if (*it_classIds < classId_last_country && *it_classIds>0) return 4;//second detection is not a vehicle
it_classIds++;
while (it_classIds != classIds.end()) {
if (*it_classIds >= NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
|| *it_classIds < 0)
return 6;//detection after first two ones, is not a character
it_classIds++;
} return 1;//ok
}
else return 2;//size too small
}
//From confidences of detections of all boxes of a plate, we get the average confidence.
float get_average_confidence_of_license_plate(const std::vector<int>& classIds,
const std::vector<float>& confidences)
{
#ifdef _DEBUG
assert(classIds.size() == confidences.size() && confidences.size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(classIds) == 1);
#endif //_DEBUG
std::vector<int>::const_iterator it_classIds(classIds.begin());
std::vector<float>::const_iterator it_confidences(confidences.begin());
float score = confidences.front();
while (it_confidences != confidences.end() && it_classIds != classIds.end()) {
if (*it_classIds < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
)
score += *it_confidences;
it_confidences++;
it_classIds++;
}
return (score / static_cast<float>(confidences.size() - 1));
}
//From confidences of detections of all boxes of a plate, we get the average confidence.
float get_average_confidence_of_license_plate(const std::list<int>& classIds,
const std::list<float>& confidences)
{
#ifdef _DEBUG
assert(classIds.size() == confidences.size() && confidences.size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(classIds) == 1);
#endif //_DEBUG
std::list<int>::const_iterator it_classIds(classIds.begin());
std::list<float>::const_iterator it_confidences(confidences.begin());
float score = confidences.front();
while (it_confidences != confidences.end() && it_classIds != classIds.end()) {
if (*it_classIds < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
)
score += *it_confidences;
it_confidences++;
it_classIds++;
}
return (score / static_cast<float>(confidences.size() - 1));
}
//
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country,//classId_last_country : is the class index of the last country in the list of detected classes.
const float nmsThreshold)
{
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
group_characters_in_the_same_license_plate(
boxes,
confidences, classIds, l_vect_of_boxes_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate, classId_last_country
);
std::list < std::vector<cv::Rect>>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::vector<float>>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::vector<int>>::const_iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_l_vect_of_classIds_in_a_license_plate) == 1);
#endif //_DEBUG
std::vector<cv::Rect> tri_left_vect_of_detected_boxes;
std::vector<float> tri_left_confidences;
std::vector<int> tri_left_classIds;
std::string lpn = get_single_lpn(
*it_l_vect_of_boxes_in_a_license_plate,
*it_l_vect_of_confidences_in_a_license_plate, *it_l_vect_of_classIds_in_a_license_plate,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds, nmsThreshold
);
l_vect_of_boxes_in_a_license_plate_tri_left.push_back(tri_left_vect_of_detected_boxes);
l_vect_of_confidences_in_a_license_plate_tri_left.push_back(tri_left_confidences);
l_vect_of_classIds_in_a_license_plate_tri_left.push_back(tri_left_classIds);
lpns.push_back(lpn);
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
#ifdef _DEBUG
assert(boxes.size() ==
confidences.size() && boxes.size() ==
classIds.size());
assert(l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_classIds_in_a_license_plate.size() && l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_confidences_in_a_license_plate.size()
&& l_vect_of_boxes_in_a_license_plate.size() ==
lpns.size());
assert(l_vect_of_boxes_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size() && l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size()
&& l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
lpns.size());
#endif //_DEBUG
}
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::list<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, //classId_last_country : is the class index of the last country in the list of detected classes.
const float nmsThreshold
) {
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
group_characters_in_the_same_license_plate(
boxes,
confidences, classIds, l_vect_of_boxes_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate, classId_last_country
);
std::list < std::list<cv::Rect>>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::list<float>>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::list<int>>::const_iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_l_vect_of_classIds_in_a_license_plate) == 1);
#endif //_DEBUG
std::vector<cv::Rect> tri_left_vect_of_detected_boxes;
std::vector<float> tri_left_confidences;
std::vector<int> tri_left_classIds;
std::string lpn = get_lpn(
*it_l_vect_of_boxes_in_a_license_plate,
*it_l_vect_of_confidences_in_a_license_plate, *it_l_vect_of_classIds_in_a_license_plate,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds, nmsThreshold
);
l_vect_of_boxes_in_a_license_plate_tri_left.push_back(tri_left_vect_of_detected_boxes);
l_vect_of_confidences_in_a_license_plate_tri_left.push_back(tri_left_confidences);
l_vect_of_classIds_in_a_license_plate_tri_left.push_back(tri_left_classIds);
lpns.push_back(lpn);
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
#ifdef _DEBUG
assert(boxes.size() ==
confidences.size() && boxes.size() ==
classIds.size());
assert(l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_classIds_in_a_license_plate.size() && l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_confidences_in_a_license_plate.size()
&& l_vect_of_boxes_in_a_license_plate.size() ==
lpns.size());
assert(l_vect_of_boxes_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size() && l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size()
&& l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
lpns.size());
#endif //_DEBUG
}
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::list<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, //classId_last_country : is the class index of the last country in the list of detected classes.
const float nmsThreshold
) {
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
group_characters_in_the_same_license_plate(
boxes,
confidences, classIds, l_vect_of_boxes_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate, classId_last_country
);
std::list < std::list<cv::Rect>>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::list<float>>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::list<int>>::const_iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
#ifdef _DEBUG
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_confidences_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() == it_l_vect_of_boxes_in_a_license_plate->size());
assert(it_l_vect_of_classIds_in_a_license_plate->size() >= 2);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
assert(is_detections_of_a_unique_license_plate(*it_l_vect_of_classIds_in_a_license_plate) == 1);
#endif //_DEBUG
std::vector<cv::Rect> tri_left_vect_of_detected_boxes;
std::vector<float> tri_left_confidences;
std::vector<int> tri_left_classIds;
std::string lpn = get_lpn(
*it_l_vect_of_boxes_in_a_license_plate,
*it_l_vect_of_confidences_in_a_license_plate, *it_l_vect_of_classIds_in_a_license_plate,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds, nmsThreshold
);
l_vect_of_boxes_in_a_license_plate_tri_left.push_back(tri_left_vect_of_detected_boxes);
l_vect_of_confidences_in_a_license_plate_tri_left.push_back(tri_left_confidences);
l_vect_of_classIds_in_a_license_plate_tri_left.push_back(tri_left_classIds);
lpns.push_back(lpn);
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
#ifdef _DEBUG
assert(boxes.size() ==
confidences.size() && boxes.size() ==
classIds.size());
assert(l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_classIds_in_a_license_plate.size() && l_vect_of_boxes_in_a_license_plate.size() ==
l_vect_of_confidences_in_a_license_plate.size()
&& l_vect_of_boxes_in_a_license_plate.size() ==
lpns.size());
assert(l_vect_of_boxes_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size() && l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
l_vect_of_confidences_in_a_license_plate_tri_left.size()
&& l_vect_of_classIds_in_a_license_plate_tri_left.size() ==
lpns.size());
#endif //_DEBUG
}
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters.
//Produces double linked lists : inside list is for characters and outside list is for plates.
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, //classId_last_country : is the class index of the last country in the list of detected classes.
const float nmsThreshold
) {
//groups detected boxes that correspond to the same vehicle. The separation is based on raw detections of license plates from the dnn
//output lists look like : first box = license plate (either a detected box either the global rect englobing characters boxes, second element = vehicle (either a detected vehicle either (0,0,0,0)
//and remaining elements are characters
group_characters_in_the_same_license_plate(
boxes,
confidences, classIds, l_vect_of_boxes_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate, classId_last_country
);
std::list < std::vector<cv::Rect>>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin());
std::list < std::vector<float>>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin());
std::list < std::vector<int>>::const_iterator it_l_vect_of_classIds_in_a_license_plate(l_vect_of_classIds_in_a_license_plate.begin());
while (it_l_vect_of_boxes_in_a_license_plate != l_vect_of_boxes_in_a_license_plate.end()
&& it_l_vect_of_confidences_in_a_license_plate != l_vect_of_confidences_in_a_license_plate.end() && it_l_vect_of_classIds_in_a_license_plate != l_vect_of_classIds_in_a_license_plate.end()) {
std::vector<cv::Rect> tri_left_vect_of_detected_boxes;
std::vector<float> tri_left_confidences;
std::vector<int> tri_left_classIds;
std::string lpn = get_single_lpn(
*it_l_vect_of_boxes_in_a_license_plate,
*it_l_vect_of_confidences_in_a_license_plate, *it_l_vect_of_classIds_in_a_license_plate,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds, nmsThreshold
);
l_vect_of_boxes_in_a_license_plate_tri_left.push_back(tri_left_vect_of_detected_boxes);
l_vect_of_confidences_in_a_license_plate_tri_left.push_back(tri_left_confidences);
l_vect_of_classIds_in_a_license_plate_tri_left.push_back(tri_left_classIds);
lpns.push_back(lpn);
it_l_vect_of_boxes_in_a_license_plate++;
it_l_vect_of_confidences_in_a_license_plate++;
it_l_vect_of_classIds_in_a_license_plate++;
}
}
//the nnet has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_lpn(const std::list<int>& l_classIds) {
std::string lpn;
std::list<int>::const_iterator it_out_classes(l_classIds.begin());
while (it_out_classes != l_classIds.end()) {
lpn += get_char(*it_out_classes);
it_out_classes++;
}
return lpn;
}
std::string get_lpn(const std::vector<int>& l_classIds) {
std::string lpn;
std::vector<int>::const_iterator it_out_classes(l_classIds.begin());
while (it_out_classes != l_classIds.end()) {
lpn += get_char(*it_out_classes);
it_out_classes++;
}
return lpn;
}
//the nnet has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_lpn(
const std::list<cv::Rect>& l_of_detected_boxes,
const std::list<int>& l_classIds,
//list of characters inside the lp
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<int>& tri_left_classIds,
float nmsThreshold
) {
std::vector<cv::Rect> vect_of_detected_boxes;
std::vector<int> classIds;
std::copy(l_of_detected_boxes.begin(), l_of_detected_boxes.end(), std::back_inserter(vect_of_detected_boxes));
std::copy(l_classIds.begin(), l_classIds.end(), std::back_inserter(classIds));
return get_single_lpn(
vect_of_detected_boxes, classIds,
tri_left_vect_of_detected_boxes,
tri_left_classIds,
nmsThreshold);
}
int horizontal_dist(const cv::Rect& r1, const cv::Rect& r2)
{
return abs((r1.x - r2.x + r1.x - r2.x + r1.width - r2.width) / 2);
}
//the dnn has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_single_lpn(
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, float nmsThreshold
) {
//first from left to right
//cette fonction trie la liste de gauche a droite
//change from vect to list
std::list<cv::Rect> l_boxes;
std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes));
std::list<float> l_confidences;
std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences));
std::list<int> l_classIds;
std::copy(classIds.begin(), classIds.end(), std::back_inserter(l_classIds));
sort_from_left_to_right(l_boxes, l_confidences, l_classIds);//sorts all the boxes from left to right
//filter out lpn box
//***************************************************
// FILTER
//***************************************************
filter_out_everything_but_characters(l_boxes,
l_confidences, l_classIds);
//filter out adjacent l_boxes with iou>nmsThreshold
//***************************************************
// FILTER
//***************************************************
//if two l_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(l_boxes, l_confidences, l_classIds, nmsThreshold);
std::list<int> levels;//levels of each character box of l_tri_left
std::list<char> lpn;
if (l_boxes.size() > 3) {
std::list<cv::Rect> l_tri_left;//list of characters l_boxes ranged from left to right
std::list<float> l_tri_left_confidences;
std::list<int> l_tri_left_classIds;
is_bi_level_plate(l_boxes, l_confidences, l_classIds, l_tri_left, l_tri_left_confidences, 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(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_levels(levels.begin());
std::list<cv::Rect>::const_iterator it_box(l_tri_left.begin());//list of characters l_boxes ranged from left to right
std::list<float>::const_iterator it_confidence(l_tri_left_confidences.begin());
std::list<cv::Rect> l_minus_1;//list of characters l_boxes ranged from left to right
std::list<float> l_confidences_minus_1;
std::list<int> l_classIds_minus_1;
std::list<cv::Rect> l_0;//list of characters l_boxes ranged from left to right
std::list<float> l_confidences_0;
std::list<int> l_classIds_0;
std::list<cv::Rect> l_plus_1;//list of characters l_boxes ranged from left to right
std::list<float> l_confidences_plus_1;
std::list<int> l_classIds_plus_1;
while (it_out_classes != l_tri_left_classIds.end() && it_levels != levels.end() &&
it_box != l_tri_left.end() && it_confidence != l_tri_left_confidences.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
if (*it_levels == -1) {
lpn_minus_1.push_back(get_char(*it_out_classes));
l_minus_1.push_back(*it_box);//list of characters l_boxes ranged from left to right
l_confidences_minus_1.push_back(*it_confidence);
l_classIds_minus_1.push_back(*it_out_classes);
}
else {
if (*it_levels == 0) {
lpn_0.push_back(get_char(*it_out_classes));
l_0.push_back(*it_box);//list of characters l_boxes ranged from left to right
l_confidences_0.push_back(*it_confidence);
l_classIds_0.push_back(*it_out_classes);
}
else if (*it_levels == 1) {
lpn_plus_1.push_back(get_char(*it_out_classes));
l_plus_1.push_back(*it_box);//list of characters l_boxes ranged from left to right
l_confidences_plus_1.push_back(*it_confidence);
l_classIds_plus_1.push_back(*it_out_classes);
}
}
}
it_out_classes++; it_levels++; it_box++; it_confidence++;
}
lpn.splice(lpn.end(), lpn_minus_1);
lpn.splice(lpn.end(), lpn_0);
lpn.splice(lpn.end(), lpn_plus_1);
//std::cout << "get_lpn lpn : " << lpn<< std::endl;
l_tri_left.clear();//list of characters l_boxes ranged from left to right
l_tri_left_confidences.clear();
l_tri_left_classIds.clear();
l_tri_left.splice(l_tri_left.end(), l_minus_1);
l_tri_left.splice(l_tri_left.end(), l_0);
l_tri_left.splice(l_tri_left.end(), l_plus_1);
l_tri_left_confidences.splice(l_tri_left_confidences.end(), l_confidences_minus_1);
l_tri_left_confidences.splice(l_tri_left_confidences.end(), l_confidences_0);
l_tri_left_confidences.splice(l_tri_left_confidences.end(), l_confidences_plus_1);
l_tri_left_classIds.splice(l_tri_left_classIds.end(), l_classIds_minus_1);
l_tri_left_classIds.splice(l_tri_left_classIds.end(), l_classIds_0);
l_tri_left_classIds.splice(l_tri_left_classIds.end(), l_classIds_plus_1);
#ifdef _DEBUG
assert(lpn.size() > 3 && lpn.size() == l_tri_left.size()
&& lpn.size() == l_tri_left_confidences.size()
&& lpn.size() == l_tri_left_classIds.size());
#endif //_DEBUG
//bool european_plate = true;
if (lpn.size() > 3 && lpn.size() == l_tri_left.size()
&& lpn.size() == l_tri_left_confidences.size()
&& lpn.size() == l_tri_left_classIds.size()) {
}
std::string lpn_corrected;
std::list<char>::iterator it_char(lpn.begin());
while (it_char != lpn.end()) {
lpn_corrected.push_back(*it_char);
it_char++;
}
std::copy(l_tri_left.begin(), l_tri_left.end(), std::back_inserter(tri_left_vect_of_detected_boxes));
std::copy(l_tri_left_confidences.begin(), l_tri_left_confidences.end(), std::back_inserter(tri_left_confidences));
std::copy(l_tri_left_classIds.begin(), l_tri_left_classIds.end(), std::back_inserter(tri_left_classIds));
#ifdef _DEBUG
//std::cout << "read lpn by engine : " << lpn_corrected << std::endl;
#endif //_DEBUG
return lpn_corrected;
}
else {
std::string lpn_corrected;
//C_OCROutputs availableAlpha(LATIN_LETTERS_NO_I_O_LATIN_DIGITS);
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_levels != levels.end()) {
if (*it_out_classes < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE //- 1
) {
lpn.push_back(get_char(*it_out_classes));
lpn_corrected.push_back(get_char(*it_out_classes));
}
it_out_classes++; it_levels++;
}
std::copy(boxes.begin(), boxes.end(), std::back_inserter(tri_left_vect_of_detected_boxes));
std::copy(confidences.begin(), confidences.end(), std::back_inserter(tri_left_confidences));
std::copy(classIds.begin(), classIds.end(), std::back_inserter(tri_left_classIds));
#ifdef _DEBUG
//std::cout << "read lpn by engine : " << lpn_corrected << std::endl;
#endif //_DEBUG
return lpn_corrected;
}
}
//the dnn has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_single_lpn(
const std::vector<cv::Rect>& boxes,
const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<int>& tri_left_classIds, float nmsThreshold
) {
float no_importance = 0.7f;
std::vector<float> confidences(boxes.size(), no_importance);
std::vector<float> tri_left_confidences;
return get_single_lpn(
boxes,
confidences, classIds,
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds, nmsThreshold);
}
//the dnn has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_best_lpn(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//characters inside the best lpn that have been chosen from the above double linked list
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, const float nmsThreshold, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//detections when they are separated license plates by license plates
std::list<std::string> lpns; std::list < std::vector<cv::Rect>> l_vect_of_boxes_in_a_license_plate;
std::list < std::vector<float>> l_vect_of_confidences_in_a_license_plate; std::list <std::vector<int>> l_vect_of_classIds_in_a_license_plate;
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>> l_vect_of_boxes_in_a_license_plate_tri_left;
std::list < std::vector<float>> l_vect_of_confidences_in_a_license_plate_tri_left; std::list <std::vector<int>> l_vect_of_classIds_in_a_license_plate_tri_left;
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters
separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
boxes, confidences, classIds,
//detections when they are separated license plates by license plates
lpns, l_vect_of_boxes_in_a_license_plate,
l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
l_vect_of_boxes_in_a_license_plate_tri_left,
l_vect_of_confidences_in_a_license_plate_tri_left, l_vect_of_classIds_in_a_license_plate_tri_left,
classId_last_country);
//check if we have a lpn
bool no_lpn = true;
std::list <std::string>::iterator it_lpns(lpns.begin());
while (it_lpns != lpns.end())
{
if (it_lpns->size()) {
no_lpn = false;
break;
}
else it_lpns++;
}
if (no_lpn) {
lpns.clear();
}
if (lpns.empty()) {
//std::vector<cv::Rect> tri_left_vect_of_detected_boxes; std::vector<float> tri_left_confidences; std::vector<int> tri_left_classIds;
std::string lpn = get_single_lpn(
boxes, confidences, classIds,
//characters inside lp
tri_left_vect_of_detected_boxes, tri_left_confidences, tri_left_classIds,
nmsThreshold
);
return lpn;
}
else {
std::list<float> confidence_one_lp; std::list < cv::Rect> one_lp; std::list<int> classIds_one_lp;
get_best_plate(
//detections when they are separated license plates by license plates
l_vect_of_classIds_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_boxes_in_a_license_plate
//l_vect_of_classIds_in_a_license_plate_tri_left, l_vect_of_confidences_in_a_license_plate_tri_left,l_vect_of_boxes_in_a_license_plate_tri_left
//output the list of the best (most probable/readable) lp
, confidence_one_lp, one_lp, classIds_one_lp);
return get_lpn(one_lp,
confidence_one_lp, classIds_one_lp,
//list of characters inside the lp
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds,
nmsThreshold
);
}
}
//the dnn has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::string get_best_lpn(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//characters inside the best lpn that have been chosen from the above double linked list
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, const float nmsThreshold, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
) {
//detections when they are separated license plates by license plates
std::list<std::string> lpns; std::list < std::vector<cv::Rect>> l_vect_of_boxes_in_a_license_plate;
std::list < std::vector<float>> l_vect_of_confidences_in_a_license_plate; std::list <std::vector<int>> l_vect_of_classIds_in_a_license_plate;
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>> l_vect_of_boxes_in_a_license_plate_tri_left;
std::list < std::vector<float>> l_vect_of_confidences_in_a_license_plate_tri_left; std::list <std::vector<int>> l_vect_of_classIds_in_a_license_plate_tri_left;
//the dnn has detected boxes that represent characters of the license plate, this function now groups characters in the same license plate and then rearranged from left to right.
//it can deal with license pates that have two lines of charcaters
separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
boxes, confidences, classIds,
//detections when they are separated license plates by license plates
lpns, l_vect_of_boxes_in_a_license_plate,
l_vect_of_confidences_in_a_license_plate, l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
l_vect_of_boxes_in_a_license_plate_tri_left,
l_vect_of_confidences_in_a_license_plate_tri_left, l_vect_of_classIds_in_a_license_plate_tri_left,
classId_last_country);
//check if we have a lpn
bool no_lpn = true;
std::list <std::string>::iterator it_lpns(lpns.begin());
while (it_lpns != lpns.end())
{
if (it_lpns->size()) {
no_lpn = false;
break;
}
else it_lpns++;
}
if (no_lpn) {
lpns.clear();
}
if (lpns.empty()) {
//std::vector<cv::Rect> tri_left_vect_of_detected_boxes; std::vector<float> tri_left_confidences; std::vector<int> tri_left_classIds;
std::string lpn = get_lpn(
boxes, confidences, classIds,
//characters inside lp
tri_left_vect_of_detected_boxes, tri_left_confidences, tri_left_classIds,
nmsThreshold
);
return lpn;
}
else {
std::list<float> confidence_one_lp; std::list < cv::Rect> one_lp; std::list<int> classIds_one_lp;
get_best_plate(
//detections when they are separated license plates by license plates
l_vect_of_classIds_in_a_license_plate, l_vect_of_confidences_in_a_license_plate, l_vect_of_boxes_in_a_license_plate
//l_vect_of_classIds_in_a_license_plate_tri_left, l_vect_of_confidences_in_a_license_plate_tri_left,l_vect_of_boxes_in_a_license_plate_tri_left
//output the list of the best (most probable/readable) lp
, confidence_one_lp, one_lp, classIds_one_lp);
return get_lpn(one_lp,
confidence_one_lp, classIds_one_lp,
//list of characters inside the lp
tri_left_vect_of_detected_boxes,
tri_left_confidences, tri_left_classIds,
nmsThreshold
);
}
}
void get_best_lpn(const std::list<cv::Rect>& one_lp, const std::list<float>& confidence_one_lp, const std::list<int>& classIds_one_lp,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes)
{
//the nnet has detected boxes that represent characters of the license plate, this function now etracts from these boxes the license plate number.
//it can deal with license pates that have two lines of charcaters
std::vector<cv::Rect> tri_left_vect_of_detected_boxes;
std::vector<float> tri_left_confidences; std::vector<int> tri_left_classIds;
const float nmsThreshold_lpn = 0.5;
//rearange boxes from left to right
std::string lpn = get_best_lpn(one_lp, confidence_one_lp, classIds_one_lp, tri_left_vect_of_detected_boxes, tri_left_confidences, tri_left_classIds, nmsThreshold_lpn);
std::vector<float>::iterator it_confidences(tri_left_confidences.begin());
std::vector<cv::Rect>::iterator it_boxes(tri_left_vect_of_detected_boxes.begin());
std::vector<int>::iterator it_out_classes_(tri_left_classIds.begin());
while (it_out_classes_ != tri_left_classIds.end()
&& it_confidences != tri_left_confidences.end() && it_boxes != tri_left_vect_of_detected_boxes.end()) {
if (
(*it_out_classes_ < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE) ||
(lpn.empty())
) {
//now we must change the coordinates of the box to fit global image
cv::Rect box_in_global_image(it_boxes->x, it_boxes->y, it_boxes->width, it_boxes->height);
chosen_lp_boxes.push_back(box_in_global_image);
chosen_lp_classIds.push_back(*it_out_classes_);
chosen_lp_confidences.push_back(*it_confidences);
}
it_out_classes_++;
it_confidences++;
it_boxes++;
}
#ifdef _DEBUG
assert(chosen_lp_confidences.size() == chosen_lp_classIds.size() && chosen_lp_boxes.size() == chosen_lp_classIds.size());
assert(lpn.length() == chosen_lp_classIds.size() || lpn.length() == 0);
#endif //_DEBUG
lpns.push_back(lpn);
lp_country_class.push_back(classIds_one_lp.front());
lp_rois.push_back(one_lp.front());
}
//Padded resize
std::vector<float> LetterboxImage(const cv::Mat& src, cv::Mat& dst, const cv::Size& out_size) {
auto in_h = static_cast<float>(src.rows);
auto in_w = static_cast<float>(src.cols);
float out_h = static_cast<float>(out_size.height);
float out_w = static_cast<float>(out_size.width);
float scale = std::min(out_w / in_w, out_h / in_h);
int mid_h = static_cast<int>(in_h * scale);
int mid_w = static_cast<int>(in_w * scale);
cv::resize(src, dst, cv::Size(mid_w, mid_h));
int top = (static_cast<int>(out_h) - mid_h) / 2;
int down = (static_cast<int>(out_h) - mid_h + 1) / 2;
int left = (static_cast<int>(out_w) - mid_w) / 2;
int right = (static_cast<int>(out_w) - mid_w + 1) / 2;
cv::copyMakeBorder(dst, dst, top, down, left, right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
std::vector<float> pad_info{ static_cast<float>(left), static_cast<float>(top), scale };
return pad_info;
}
//pad_w is the left (and also right) border width in the square image feeded to the model
void ScaleCoordinates(std::vector<Detection>& data, float pad_w, float pad_h,
float scale, const cv::Size& img_shape) {
auto clip = [](float n, float lower, float upper) {
return std::max(lower, std::min(n, upper));
};
std::vector<Detection> detections;
for (auto& i : data) {
//pad_w is the left (and also right) border width in the square image feeded to the model
float x1 = (i.bbox.tl().x - pad_w) / scale; // x padding
float y1 = (i.bbox.tl().y - pad_h) / scale; // y padding
float x2 = (i.bbox.br().x - pad_w) / scale; // x padding
float y2 = (i.bbox.br().y - pad_h) / scale; // y padding
x1 = clip(x1, 0, static_cast<float>(img_shape.width));
y1 = clip(y1, 0, static_cast<float>(img_shape.height));
x2 = clip(x2, 0, static_cast<float>(img_shape.width));
y2 = clip(y2, 0, static_cast<float>(img_shape.height));
i.bbox = cv::Rect(cv::Point(lround(x1), lround(y1)), cv::Point(lround(x2), lround(y2)));
}
}
std::vector<std::vector<Detection>> PostProcessing(
float* output, // output of onnx runtime ->>> 1,25200,85
size_t dimensionsCount,
size_t size, // 1x25200x85=2142000
int dimensions,
//pad_w is the left (and also right) border width in the square image feeded to the model
float modelWidth, float modelHeight, const cv::Size& img_shape,
float conf_threshold, float iou_threshold) {
constexpr int item_attr_size = 5;
int batch_size = 1;
// number of classes, e.g. 80 for coco dataset
// get candidates which object confidence > threshold
int rows = static_cast<int>(size) / dimensions; //25200
int confidenceIndex = 4;
int labelStartIndex = 5;
float xGain = modelWidth / static_cast<float>(img_shape.width);
float yGain = modelHeight / static_cast<float>(img_shape.height);
std::vector<cv::Vec4f> locations;
std::vector<int> labels_;
std::vector<float> confidences;
std::vector<cv::Rect> src_rects;
std::vector<Detection> det_vec;//batch 1 one image at a time
cv::Rect rect;
cv::Vec4f location;
for (int i = 0; i < rows; ++i) {
int index = i * dimensions;
if (output[index + confidenceIndex] <= conf_threshold) continue;
//each class score of the current bb is multiplied by the bb confidence
for (int j = labelStartIndex; j < dimensions; ++j) {
output[index + j] = output[index + j] * output[index + confidenceIndex];
}
for (int k = labelStartIndex; k < dimensions; ++k) {
if (output[index + k] <= conf_threshold) continue;
else {
//xywh2xyxy
location[0] = (output[index] - output[index + 2] / 2) / xGain;//top left x
location[1] = (output[index + 1] - output[index + 3] / 2) / yGain;//top left y
location[2] = (output[index] + output[index + 2] / 2) / xGain;//bottom right x
location[3] = (output[index + 1] + output[index + 3] / 2) / yGain;//bottom right y
locations.emplace_back(location);
rect = cv::Rect(lround(location[0]), lround(location[1]),
lround(location[2] - location[0]), lround(location[3] - location[1]));
src_rects.push_back(rect);
labels_.emplace_back(k - labelStartIndex);
confidences.emplace_back(output[index + k]);
}
}
}
// run NMS
std::vector<int> nms_indices;
cv::dnn::NMSBoxes(src_rects, confidences, conf_threshold, iou_threshold, nms_indices);
for (size_t i = 0; i < nms_indices.size(); i++) {
Detection detection;
detection.bbox = src_rects[nms_indices[i]];
detection.score = confidences[nms_indices[i]];
detection.class_idx = labels_[nms_indices[i]];
det_vec.push_back(detection);
}
std::vector<std::vector<Detection>> detections;
detections.reserve(batch_size);
// save final detection for the current image
detections.emplace_back(det_vec);
return detections;
}
std::vector<std::vector<Detection>> PostProcessing(
float* output, // output of onnx runtime ->>> 1,25200,85
size_t dimensionsCount,
size_t size, // 1x25200x85=2142000
int dimensions,
//pad_w is the left (and also right) border width in the square image feeded to the model
float pad_w, float pad_h, float scale, const cv::Size& img_shape,
float conf_threshold, float iou_threshold) {
constexpr int item_attr_size = 5;
int batch_size = 1;
// number of classes, e.g. 80 for coco dataset
// get candidates which object confidence > threshold
int rows = static_cast<int>(size) / dimensions; //25200
int confidenceIndex = 4;
int labelStartIndex = 5;
//float xGain = modelWidth / static_cast<float>(img_shape.width);
//float yGain = modelHeight / static_cast<float>(img_shape.height);
std::vector<cv::Vec4f> locations;
std::vector<int> labels_;
std::vector<float> confidences;
std::vector<cv::Rect> src_rects;
std::vector<Detection> det_vec;//batch 1 one image at a time
cv::Rect rect;
cv::Vec4f location;
for (int i = 0; i < rows; ++i) {
int index = i * dimensions;
if (output[index + confidenceIndex] <= conf_threshold) continue;
//each class score of the current bb is multiplied by the bb confidence
for (int j = labelStartIndex; j < dimensions; ++j) {
output[index + j] = output[index + j] * output[index + confidenceIndex];
}
for (int k = labelStartIndex; k < dimensions; ++k) {
if (output[index + k] <= conf_threshold) continue;
else {
//xywh2xyxy
location[0] = (output[index] - output[index + 2] / 2);//top left x
location[1] = (output[index + 1] - output[index + 3] / 2);//top left y
location[2] = (output[index] + output[index + 2] / 2);//bottom right x
location[3] = (output[index + 1] + output[index + 3] / 2);//bottom right y
locations.emplace_back(location);
rect = cv::Rect(lround(location[0]), lround(location[1]),
lround(location[2] - location[0]), lround(location[3] - location[1]));
src_rects.push_back(rect);
labels_.emplace_back(k - labelStartIndex);
confidences.emplace_back(output[index + k]);
}
}
}
// run NMS
std::vector<int> nms_indices;
cv::dnn::NMSBoxes(src_rects, confidences, conf_threshold, iou_threshold, nms_indices);
for (size_t i = 0; i < nms_indices.size(); i++) {
Detection detection;
detection.bbox = src_rects[nms_indices[i]];
detection.score = confidences[nms_indices[i]];
detection.class_idx = labels_[nms_indices[i]];
det_vec.push_back(detection);
}
//pad_w is the left (and also right) border width in the square image feeded to the model
ScaleCoordinates(det_vec, pad_w, pad_h, scale, img_shape);
std::vector<std::vector<Detection>> detections;
detections.reserve(batch_size);
// save final detection for the current image
detections.emplace_back(det_vec);
return detections;
}
//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
void filter_iou(std::vector<int>& classIds,
std::vector<float>& confidences,
std::vector<cv::Rect>& vect_of_detected_boxes, const float& nmsThreshold
)
{
#ifdef _DEBUG
assert(classIds.size() == confidences.size() && classIds.size() == vect_of_detected_boxes.size());
#endif //_DEBUG
std::list<int> l_classIds;
std::list<float> l_confidences;
std::list<cv::Rect> list_of_detected_boxes;
//first copy vector to list which are faster when we need to delete element
std::copy(classIds.begin(), classIds.end(), std::back_inserter(l_classIds));
std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences));
std::copy(vect_of_detected_boxes.begin(), vect_of_detected_boxes.end(), std::back_inserter(list_of_detected_boxes));
//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(list_of_detected_boxes, l_confidences,
l_classIds, nmsThreshold);
if (classIds.size() > l_classIds.size()) {
classIds.clear(); confidences.clear(); vect_of_detected_boxes.clear();
std::copy(l_classIds.begin(), l_classIds.end(), std::back_inserter(classIds));
std::copy(l_confidences.begin(), l_confidences.end(), std::back_inserter(confidences));
std::copy(list_of_detected_boxes.begin(), list_of_detected_boxes.end(), std::back_inserter(vect_of_detected_boxes));
}
}
//this func extract all info from the filenames of the platesmania dataset :
bool plates_types_differ_with_one_character(const std::string& type1, std::string& lpn
, const std::string& type2)
{
if (type1.length() == type2.length() && type1.length() == lpn.length() && type1.length()) {
Levenshtein lev;
int editdistance = lev.Get(type1.c_str(), type1.length(), type2.c_str(), type2.length());
if (editdistance == 1) {
std::string::const_iterator it2(type2.begin());
std::string::const_iterator it1(type1.begin());
int index = 0;
while (it1 != type1.end() && it2 != type2.end())
{
if (*it2 != *it1) {
if (lpn[index] == 'O') {
lpn[index] = '0';
return true;
}
if (lpn[index] == '0') {
lpn[index] = 'O';
return true;
}
if (lpn[index] == 'B') {
lpn[index] = '8';
return true;
}
if (lpn[index] == '8') {
lpn[index] = 'B';
return true;
}
}
it1++;
it2++;
index++;
}
return false;
}
else return false;
}
else return false;
}
//this func extract all info from the filenames of the platesmania dataset :
std::string get_plate_sub_type(const std::string& lpn)
{
std::string sub_type;
std::string::const_iterator it(lpn.begin());
while (it != lpn.end()) {
if (is_digit(*it) == 1) { sub_type += 'D'; }
else if (is_digit(*it) == 0) { sub_type += 'L'; }
else if (is_digit(*it) == 2) {
if (*it == '0') sub_type += 'D';
else if (*it == 'O') sub_type += 'L';
}
it++;
}
return sub_type;
}
//this func extract all info from the filenames of the platesmania dataset :
std::string get_plate_sub_type(const std::list<char>& lpn)
{
std::string sub_type;
std::list<char>::const_iterator it(lpn.begin());
while (it != lpn.end()) {
if (is_digit(*it) == 1) { sub_type += 'D'; }
else if (is_digit(*it) == 0) { sub_type += 'L'; }
else if (is_digit(*it) == 2) {
if (*it == '0') sub_type += 'D';
else if (*it == 'O') sub_type += 'L';
}
it++;
}
return sub_type;
}
std::vector<std::string> get_plates_types_labels(const std::string& filename) {
std::ifstream ifs(filename.c_str());
std::string line;
std::vector<std::string> labels;
while (std::getline(ifs, line))
{
labels.push_back(line);
}
return labels;
}