#include "utils_alpr_detect.h" #include "utils_image_file.h" #include"utils_opencv.h" #include #include #include #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(inter.area()) / static_cast(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& boxes, std::list& confidences, std::list& classIds) { std::list l_tri_left; std::list confidences_tri_left; std::list classIds_tri_left; while (!boxes.empty() && !confidences.empty() && !classIds.empty()) { //int left_courant(boxes.front().x); std::list::iterator it = l_tri_left.begin(); std::list::iterator it_classIds = classIds_tri_left.begin(); std::list::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(inter.area()) / static_cast(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& boxes) { if (boxes.empty()) return -1; else if (boxes.size() == 1) return boxes.front().height; else { std::list list_tri; std::list::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::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& boxes, std::list& confidences, std::list& classIds, float nmsThreshold) { //filter out adjacent boxes with iou>nmsThreshold //*************************************************** // FILTER //*************************************************** std::list::iterator it_confidences = (confidences.begin()); std::list::iterator it_boxes = (boxes.begin()); std::list::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::iterator it_confidences_next(it_confidences); std::list::iterator it_boxes_next(it_boxes); std::list::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& boxes, std::list& les_points) { #ifdef _DEBUG assert(les_points.empty()); #endif //_DEBUG std::list::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(somme_x); x_d += 0.5f; } else { somme_x = (somme_x >> 1); x_d = static_cast(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(somme_y); y_d += 0.5f; } else { somme_y = (somme_y >> 1); y_d = static_cast(somme_y); } #ifdef _DEBUG assert(y_d + FLT_EPSILON > it->y&& y_d - FLT_EPSILONy + 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& boxes) { if (boxes.size() < 2) return true; else { //VERIFICATION DU TRI CROISSANT std::list::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& boxes, std::list& angles_with_horizontal_line, float& mean_angles_par_rapport_horiz, float& standard_deviation_consecutives_angles, std::list& 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 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::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(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& boxes, const std::list& l_classIds, std::list& l_reordered, std::list& l_reordered_classIds, std::list& 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 angles_with_horizontal_line; float mean_angles_par_rapport_horiz; float standard_deviation_consecutives_angles; std::list 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(l_reordered.size(), 0); } else { std::list::const_iterator it = l_reordered.begin(); std::list::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::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::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::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::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::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::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 lpn_minus_1; std::list lpn_0; std::list lpn_plus_1; //C_OCROutputs availableAlpha(LATIN_LETTERS_NO_I_O_LATIN_DIGITS); std::list::const_iterator it_levels(levels.begin()); std::list::const_iterator it_box(l_reordered.begin());//list of characters l_boxes ranged from left to right std::list::const_iterator it_out_classes(l_reordered_classIds.begin()); std::list l_minus_1;//list of characters l_boxes ranged from left to right std::list l_0;//list of characters l_boxes ranged from left to right std::list 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::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& boxes_on_one_line, const std::list& 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(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(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& boxes, const std::list& l_confidences, const std::list& l_classIds, std::list& l_reordered, std::list& l_reordered_confidences, std::list& l_reordered_classIds, std::list& 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 angles_par_rapport_horiz; float moy_angles_par_rapport_horiz; float ecart_type_angles_consecutifs; std::list 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::const_iterator it = l_reordered.begin(); std::list::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::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::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::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::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::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::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& boxes, std::list& l_classIds) { //*************************************************** // FILTER //*************************************************** std::list::iterator it_boxes(boxes.begin()); std::list::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& boxes, std::list& l_confidences, std::list& l_classIds) { //*************************************************** // FILTER //*************************************************** std::list::iterator it_confidences(l_confidences.begin()); std::list::iterator it_boxes(boxes.begin()); std::list::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& boxes, const std::vector& confidences, const std::vector& classIds, //detections of same lp are regrouped in a vector std::list < std::vector>& l_vect_of_boxes_in_a_license_plate, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector>& 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 license_plates; std::list license_plates_confidences; std::list license_plates_classIds; std::vector::const_iterator it_confidences(confidences.begin()); std::vector::const_iterator it_boxes(boxes.begin()); std::vector::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 vehicles; std::list vehicles_confidences; std::list vehicles_classIds; std::list::const_iterator it_license_plates(license_plates.begin()); std::list::const_iterator it_license_plates_confidence(license_plates_confidences.begin()); std::list::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::const_iterator it_vehicles(vehicles.begin()); std::list::const_iterator it_vehicles_confidence(vehicles_confidences.begin()); std::list::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()); l_vect_of_confidences_in_a_license_plate.push_back(std::vector()); l_vect_of_classIds_in_a_license_plate.push_back(std::vector()); 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>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::vector>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::vector>::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& boxes, const std::vector& confidences, const std::vector& classIds, //detections of same lp are regrouped in a vector std::list < std::list>& l_vect_of_boxes_in_a_license_plate, std::list < std::list>& l_vect_of_confidences_in_a_license_plate, std::list < std::list>& 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 license_plates; std::list license_plates_confidences; std::list license_plates_classIds; std::vector::const_iterator it_confidences(confidences.begin()); std::vector::const_iterator it_boxes(boxes.begin()); std::vector::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 vehicles; std::list vehicles_confidences; std::list vehicles_classIds; std::list::const_iterator it_license_plates(license_plates.begin()); std::list::const_iterator it_license_plates_confidence(license_plates_confidences.begin()); std::list::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::const_iterator it_vehicles(vehicles.begin()); std::list::const_iterator it_vehicles_confidence(vehicles_confidences.begin()); std::list::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()); l_vect_of_confidences_in_a_license_plate.push_back(std::list()); l_vect_of_classIds_in_a_license_plate.push_back(std::list()); 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>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::list>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::list>::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& boxes, const std::list& confidences, const std::list& classIds, //detections of same lp are regrouped in a vector std::list < std::list>& l_vect_of_boxes_in_a_license_plate, std::list < std::list>& l_vect_of_confidences_in_a_license_plate, std::list < std::list>& 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 license_plates; std::list license_plates_confidences; std::list license_plates_classIds; std::list::const_iterator it_confidences(confidences.begin()); std::list::const_iterator it_boxes(boxes.begin()); std::list::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 vehicles; std::list vehicles_confidences; std::list vehicles_classIds; std::list::const_iterator it_license_plates(license_plates.begin()); std::list::const_iterator it_license_plates_confidence(license_plates_confidences.begin()); std::list::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::const_iterator it_vehicles(vehicles.begin()); std::list::const_iterator it_vehicles_confidence(vehicles_confidences.begin()); std::list::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()); l_vect_of_confidences_in_a_license_plate.push_back(std::list()); l_vect_of_classIds_in_a_license_plate.push_back(std::list()); 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>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::list>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::list>::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& boxes, const std::list& confidences, const std::list& classIds, //detections of same lp are regrouped in a vector std::list < std::vector>& l_vect_of_boxes_in_a_license_plate, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector>& 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 license_plates; std::list license_plates_confidences; std::list license_plates_classIds; std::list::const_iterator it_confidences(confidences.begin()); std::list::const_iterator it_boxes(boxes.begin()); std::list::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 vehicles; std::list vehicles_confidences; std::list vehicles_classIds; std::list::const_iterator it_license_plates(license_plates.begin()); std::list::const_iterator it_license_plates_confidence(license_plates_confidences.begin()); std::list::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::const_iterator it_vehicles(vehicles.begin()); std::list::const_iterator it_vehicles_confidence(vehicles_confidences.begin()); std::list::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()); l_vect_of_confidences_in_a_license_plate.push_back(std::vector()); l_vect_of_classIds_in_a_license_plate.push_back(std::vector()); 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>::iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::vector>::iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::vector>::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& classes) { int current_index = 0; std::list::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 & lpns, const std::list & lp_country_class, const std::list < cv::Rect>& lp_rois, const std::list < std::list>& confidences, std::list < std::list>& classes, const std::list < std::list>& 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& best_confidences, std::list& best_classes, std::list& best_boxes) { int min_editdistance = SHRT_MAX; Levenshtein lev; std::list ::iterator it_lpns(lpns.begin()); std::list ::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>::const_iterator it_confidences(confidences.begin()); std::list < std::list>::iterator it_classes(classes.begin()); std::list < std::list>::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>& classIds, const std::list < std::list>& confidences, const std::list < std::list>& boxes //output the list of the best (most probable/readable) lp , std::list& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list& classIds_one_lp) { std::list < std::list>::const_iterator it_boxes(boxes.begin()); std::list < std::list>::const_iterator it_confidences(confidences.begin()); std::list < std::list>::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>& classIds, const std::list < std::list>& confidences, const std::list < std::list>& boxes //output the list of the best (most probable/readable) lp , std::list& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list& classIds_one_lp) { std::list < std::list>::const_iterator it_boxes(boxes.begin()); std::list < std::list>::const_iterator it_confidences(confidences.begin()); std::list < std::list>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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>& classIds, const std::list < std::list>& confidences, const std::list < std::list>& boxes //output the list of the best (most probable/readable) lp , std::vector& confidence_one_lp, std::vector < cv::Rect>& one_lp, std::vector& classIds_one_lp) { std::list < std::list>::const_iterator it_boxes(boxes.begin()); std::list < std::list>::const_iterator it_confidences(confidences.begin()); std::list < std::list>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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& l_of_detected_boxes, const std::list& l_confidences, const std::list& l_classIds, //list of characters inside the lp std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& tri_left_classIds, float nmsThreshold ) { std::vector vect_of_detected_boxes; std::vector confidences; std::vector 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>& classIds, const std::list < std::vector>& confidences, const std::list < std::vector>& boxes //output the list of the best (most probable/readable) lp , std::list& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list& classIds_one_lp) { std::list < std::vector>::const_iterator it_boxes(boxes.begin()); std::list < std::vector>::const_iterator it_confidences(confidences.begin()); std::list < std::vector>::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& boxes, const std::list& confidences, const std::list& classIds, std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& 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 l_tri_left_vect_of_detected_boxes; std::list l_tri_left_confidences; std::list 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& boxes, const std::vector& confidences, const std::vector& classIds, std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& 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 l_tri_left_vect_of_detected_boxes; std::list l_tri_left_confidences; std::list 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& boxes, const std::list& confidences, const std::list& classIds, std::list& tri_left_vect_of_detected_boxes, std::list& tri_left_confidences, std::list& 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 l_boxes; std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes)); std::list l_confidences; std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences)); std::list 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& boxes, const std::vector& confidences, const std::vector& classIds, std::list& tri_left_vect_of_detected_boxes, std::list& tri_left_confidences, std::list& 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 l_boxes; std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes)); std::list l_confidences; std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences)); std::list 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& classIds) { if (classIds.size() >= 2) { const int classId_last_country = NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE + NUMBER_OF_COUNTRIES - 1; std::vector::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& 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::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& classIds, const std::vector& 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::const_iterator it_classIds(classIds.begin()); std::vector::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(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& classIds, const std::list& 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::const_iterator it_classIds(classIds.begin()); std::list::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(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& boxes, const std::vector& confidences, const std::vector& classIds, //detections when they are separated license plates by license plates std::list& lpns, std::list < std::vector>& l_vect_of_boxes_in_a_license_plate, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate, std::list >& 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>& l_vect_of_boxes_in_a_license_plate_tri_left, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list >& 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>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::vector>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::vector>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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& boxes, const std::vector& confidences, const std::vector& classIds, //detections when they are separated license plates by license plates std::list& lpns, std::list < std::list>& l_vect_of_boxes_in_a_license_plate, std::list < std::list>& l_vect_of_confidences_in_a_license_plate, std::list >& 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>& l_vect_of_boxes_in_a_license_plate_tri_left, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list >& 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>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::list>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::list>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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& boxes, const std::list& confidences, const std::list& classIds, //detections when they are separated license plates by license plates std::list& lpns, std::list < std::list>& l_vect_of_boxes_in_a_license_plate, std::list < std::list>& l_vect_of_confidences_in_a_license_plate, std::list >& 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>& l_vect_of_boxes_in_a_license_plate_tri_left, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list >& 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>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::list>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::list>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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& boxes, const std::list& confidences, const std::list& classIds, //detections when they are separated license plates by license plates std::list& lpns, std::list < std::vector>& l_vect_of_boxes_in_a_license_plate, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate, std::list >& 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>& l_vect_of_boxes_in_a_license_plate_tri_left, std::list < std::vector>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list >& 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>::const_iterator it_l_vect_of_boxes_in_a_license_plate(l_vect_of_boxes_in_a_license_plate.begin()); std::list < std::vector>::const_iterator it_l_vect_of_confidences_in_a_license_plate(l_vect_of_confidences_in_a_license_plate.begin()); std::list < std::vector>::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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& l_classIds) { std::string lpn; std::list::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& l_classIds) { std::string lpn; std::vector::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& l_of_detected_boxes, const std::list& l_classIds, //list of characters inside the lp std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_classIds, float nmsThreshold ) { std::vector vect_of_detected_boxes; std::vector 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& boxes, const std::vector& confidences, const std::vector& classIds, std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& 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 l_boxes; std::copy(boxes.begin(), boxes.end(), std::back_inserter(l_boxes)); std::list l_confidences; std::copy(confidences.begin(), confidences.end(), std::back_inserter(l_confidences)); std::list 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 levels;//levels of each character box of l_tri_left std::list lpn; if (l_boxes.size() > 3) { std::list l_tri_left;//list of characters l_boxes ranged from left to right std::list l_tri_left_confidences; std::list 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 lpn_minus_1; std::list lpn_0; std::list lpn_plus_1; //C_OCROutputs availableAlpha(LATIN_LETTERS_NO_I_O_LATIN_DIGITS); std::list::const_iterator it_out_classes(l_tri_left_classIds.begin()); std::list::const_iterator it_levels(levels.begin()); std::list::const_iterator it_box(l_tri_left.begin());//list of characters l_boxes ranged from left to right std::list::const_iterator it_confidence(l_tri_left_confidences.begin()); std::list l_minus_1;//list of characters l_boxes ranged from left to right std::list l_confidences_minus_1; std::list l_classIds_minus_1; std::list l_0;//list of characters l_boxes ranged from left to right std::list l_confidences_0; std::list l_classIds_0; std::list l_plus_1;//list of characters l_boxes ranged from left to right std::list l_confidences_plus_1; std::list 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::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::const_iterator it_out_classes(l_classIds.begin()); std::list::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& boxes, const std::vector& classIds, std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_classIds, float nmsThreshold ) { float no_importance = 0.7f; std::vector confidences(boxes.size(), no_importance); std::vector 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& boxes, const std::vector& confidences, const std::vector& classIds, //characters inside the best lpn that have been chosen from the above double linked list std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& 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 lpns; std::list < std::vector> l_vect_of_boxes_in_a_license_plate; std::list < std::vector> l_vect_of_confidences_in_a_license_plate; std::list > 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> l_vect_of_boxes_in_a_license_plate_tri_left; std::list < std::vector> l_vect_of_confidences_in_a_license_plate_tri_left; std::list > 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 ::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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 confidence_one_lp; std::list < cv::Rect> one_lp; std::list 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& boxes, const std::list& confidences, const std::list& classIds, //characters inside the best lpn that have been chosen from the above double linked list std::vector& tri_left_vect_of_detected_boxes, std::vector& tri_left_confidences, std::vector& 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 lpns; std::list < std::vector> l_vect_of_boxes_in_a_license_plate; std::list < std::vector> l_vect_of_confidences_in_a_license_plate; std::list > 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> l_vect_of_boxes_in_a_license_plate_tri_left; std::list < std::vector> l_vect_of_confidences_in_a_license_plate_tri_left; std::list > 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 ::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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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 confidence_one_lp; std::list < cv::Rect> one_lp; std::list 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& one_lp, const std::list& confidence_one_lp, const std::list& 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 & lpns, std::list & lp_country_class, std::list < cv::Rect>& lp_rois, //detection inside the chosen lp std::list& chosen_lp_classIds, std::list& chosen_lp_confidences, std::list& 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 tri_left_vect_of_detected_boxes; std::vector tri_left_confidences; std::vector 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::iterator it_confidences(tri_left_confidences.begin()); std::vector::iterator it_boxes(tri_left_vect_of_detected_boxes.begin()); std::vector::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 LetterboxImage(const cv::Mat& src, cv::Mat& dst, const cv::Size& out_size) { auto in_h = static_cast(src.rows); auto in_w = static_cast(src.cols); float out_h = static_cast(out_size.height); float out_w = static_cast(out_size.width); float scale = std::min(out_w / in_w, out_h / in_h); int mid_h = static_cast(in_h * scale); int mid_w = static_cast(in_w * scale); cv::resize(src, dst, cv::Size(mid_w, mid_h)); int top = (static_cast(out_h) - mid_h) / 2; int down = (static_cast(out_h) - mid_h + 1) / 2; int left = (static_cast(out_w) - mid_w) / 2; int right = (static_cast(out_w) - mid_w + 1) / 2; cv::copyMakeBorder(dst, dst, top, down, left, right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); std::vector pad_info{ static_cast(left), static_cast(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& 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 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(img_shape.width)); y1 = clip(y1, 0, static_cast(img_shape.height)); x2 = clip(x2, 0, static_cast(img_shape.width)); y2 = clip(y2, 0, static_cast(img_shape.height)); i.bbox = cv::Rect(cv::Point(lround(x1), lround(y1)), cv::Point(lround(x2), lround(y2))); } } std::vector> 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(size) / dimensions; //25200 int confidenceIndex = 4; int labelStartIndex = 5; float xGain = modelWidth / static_cast(img_shape.width); float yGain = modelHeight / static_cast(img_shape.height); std::vector locations; std::vector labels_; std::vector confidences; std::vector src_rects; std::vector 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 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> detections; detections.reserve(batch_size); // save final detection for the current image detections.emplace_back(det_vec); return detections; } std::vector> 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(size) / dimensions; //25200 int confidenceIndex = 4; int labelStartIndex = 5; //float xGain = modelWidth / static_cast(img_shape.width); //float yGain = modelHeight / static_cast(img_shape.height); std::vector locations; std::vector labels_; std::vector confidences; std::vector src_rects; std::vector 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 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> 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& classIds, std::vector& confidences, std::vector& 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 l_classIds; std::list l_confidences; std::list 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& lpn) { std::string sub_type; std::list::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 get_plates_types_labels(const std::string& filename) { std::ifstream ifs(filename.c_str()); std::string line; std::vector labels; while (std::getline(ifs, line)) { labels.push_back(line); } return labels; }