Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

View File

@@ -0,0 +1,136 @@
/*
// Line.h: interface for the C_Line class.
//
//////////////////////////////////////////////////////////////////////
*/
// Levenshtein.cpp: implementation of the Levenshtein class.
//
//////////////////////////////////////////////////////////////////////
#include "../include/Levenshtein.h"
#include <cstring>
#include <cstdlib>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Levenshtein::Levenshtein()
{
}
Levenshtein::~Levenshtein()
{
}
//****************************************************
// Compute Levenshtein distance
// This code is based on the work by Michael Gilleland
//
// It uses dynamic arrays which allows any std::string size.
//****************************************************
int Levenshtein::Get2 (char const *s, char const *t)
{
return Get2(s, strlen(s), t, strlen(t) );
}
int Levenshtein::Get2 (const char *s, size_t n, const char* t, size_t dst)
{
int *d; // pointer to matrix
size_t i; // iterates through s
size_t j; // iterates through t
//char s_i; // ith character of s
char t_j; // jth character of t
int cost; // cost
int result; // result
int cell; // contents of target cell
int above; // contents of cell immediately above
int left; // contents of cell immediately to left
int diag; // contents of cell immediately above and to left
size_t sz; // number of cells in matrix
// Step 1
if (n == 0) {
return static_cast<int>(dst);
}
if (dst == 0) {
return static_cast<int>(n);
}
sz = (n+1) * (dst+1) * sizeof (int);
d = static_cast<int*>(malloc(sz));
// Step 2
for (i = 0; i <= n; i++) {
PutAt (d, i, 0, n, static_cast<int>(i));
}
for (j = 0; j <= dst; j++) {
PutAt (d, 0, j, n, static_cast<int>(j));
}
// Step 3
for (i = 1; i <= n; i++) {
char s_i; // ith character of s
s_i = s[i-1];
// Step 4
for (j = 1; j <= dst; j++) {
t_j = t[j-1];
// Step 5
if (s_i == t_j) {
cost = 0;
}
else {
cost = 1;
}
// Step 6
above = GetAt (d,i-1,j, n);
left = GetAt (d,i, j-1, n);
diag = GetAt (d, i-1,j-1, n);
cell = Minimum (above + 1, left + 1, diag + cost);
PutAt (d, i, j, n, cell);
}
}
// Step 7
result = GetAt (d, n, dst, n);
free (d);
return result;
}
/*****************************************************
* Second implementation of the Levenshtein algorithm.
* A static array is used with length MAXLINE. Make
* sure that your strings are no longer! Otherwise use
* the algorithm above.
*/
#define MAXLINE 128
int Levenshtein::Get(const char *a, const char *b)
{
return Get(a, strlen(a), b, strlen(b));
}
int Levenshtein::Get(const char *a, size_t aLen, const char *b, size_t bLen)
{
int arr[MAXLINE][MAXLINE];
int i,j,l,dst,n,add;
// MAXLINE is the limit! If the strings are longer use the other implementation
if (aLen > MAXLINE || bLen > MAXLINE)
{
return Get2(a, aLen, b, bLen);
}
for (i=0;i<=aLen;i++)
{
arr[0][i]=i;
}
for (j=0;j<=bLen;j++)
{
arr[j][0]=j;
}
for (j=1;j<=bLen;j++)
{
for (i=1;i<=aLen;i++)
{
if (a[i-1] == b[j-1])
{
add=0;
}
else
{
add=1;
}
dst = 1+arr[j-1][i];
l = 1+arr[j][i-1];
n = add+arr[j-1][i-1];
arr[j][i] = (dst < l ? (dst < n ? dst : n): (l < n ? l : n));
}
}
return arr[bLen][aLen];
}

195
modules/ANSLPR/src/Line.cpp Normal file
View File

@@ -0,0 +1,195 @@
//************************************************************************
/*
************************************************************************
// Copyright (C) 2003-2006, LPReditor SARL, all rights reserved.
// author : Raphael Poulenard.
************************************************************************
// Line.h: interface for the C_Line class.
//
This program is free software : you can redistribute itand /or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
//////////////////////////////////////////////////////////////////////
*/
// Line.cpp: implementation of the C_Line class.
//
//////////////////////////////////////////////////////////////////////
#include "Line.h"
#ifdef _WINDOWS
#endif //_WINDOWS
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
C_Line::C_Line():a(0.0f),b(0.0f)
{
}
//constructeur a partir de deux pts
C_Line::C_Line(const cv::Point2f& A,const cv::Point2f& B)
{
#ifdef _DEBUG
//assert(A.x!=B.x);
#endif //_DEBUG
if(fabsf(A.x-B.x)<FLT_EPSILON) {
a=(A.y-B.y)/(A.x-B.x+FLT_EPSILON);
b=0.0f;
}
else {a=(A.y-B.y)/(A.x-B.x);
b=A.y-(a*A.x);
}
}
C_Line::C_Line(const C_Line & line_)
{
a=line_.a;
b=line_.b;
}
C_Line::C_Line(const float & a_,const float & b_):a(a_),b(b_)
{
}
C_Line::~C_Line()
{
}
//retourne le pt d'inter de C_Line avec la dte horiz y=ordonnee
float C_Line::get_abs(const int ordonnee) const
{
#ifdef _DEBUG
assert(a!=0.0f);
#endif //_DEBUG
return (ordonnee-b)/a;
}
float C_Line::get_abs(const float ordonnee) const
{
#ifdef _DEBUG
assert(a!=0.0f);
#endif //_DEBUG
return (ordonnee-b)/a;
}
//retourne l'image de abscisse par la fonction affi,e definie par la droite
int C_Line::get_image_entiere(const int abscisse) const
{
float ordonnee(a*abscisse+b);
int ordonne_floor=static_cast<int>(floorf(ordonnee));
if (ordonnee-ordonne_floor>0.5f) {
//le point le plus proche de la droite est situe au dessus.
ordonne_floor++;
#ifdef _DEBUG
assert(ordonne_floor-ordonnee<0.5f);
#endif //_DEBUG
}
return ordonne_floor;
}
//retourne l'image de abscisse par la fonction affi,e definie par la droite
float C_Line::get_image(const float & abscisse) const
{
return a*abscisse+b;
}
//retourne l'image de abscisse par la fonction affi,e definie par la droite
float C_Line::get_image(const int abscisse) const
{
return a*abscisse+b;
}
bool C_Line::is_nearly_the_same(const C_Line & right_op) const
{
return (fabs(a-right_op.a)<FLT_EPSILON
&& fabs(b-right_op.b)<FLT_EPSILON);
}
bool C_Line::is_nearly_horiz() const
{
return (a+0.000001>0.0 && a<0.000001);
}
void C_Line::reset()
{
a=0.0f;b=0.0f;
}
float C_Line::dist_y(const cv::Point& pt) const
{
//true if the line doesnot have too large slope and not large ordonnee a l'origine
if (is_valid()) {
return fabsf(pt.y - a * static_cast<float>(pt.x) - b);
}
else return .0f;
}
float C_Line::dist_y(const cv::Point2f& pt) const
{
//true if the line doesnot have too large slope and not large ordonnee a l'origine
if (is_valid()) {
return fabsf(pt.y - a * static_cast<float>(pt.x) - b);
}
else return .0f;
}
//retourne la distance du point
// sa projection verticale de la droite
float C_Line::difference_y(const cv::Point2f& pt) const
{
return (pt.y - a * pt.x - b);
}
//returns the distance of the center point of a box from its vertical projection on the line
float C_Line::dist_y_from_center(const cv::Rect& box) const
{
cv::Point2f pt(box.x + box.width / 2.0f, box.y + box.height / 2.0f);
return dist_y(pt);
}
float C_Line::difference_y_from_center(const cv::Rect& box) const
{
cv::Point2f pt(box.x + box.width / 2.0f, box.y + box.height / 2.0f);
return difference_y(pt);
}
float C_Line::dist_y_from_center(const std::list<cv::Rect>& boxes) const
{
float dist = 0.0f;
std::list<cv::Rect>::const_iterator it(boxes.begin());
while (it != boxes.end()) {
dist += dist_y_from_center(*it);
it++;
}
return dist;
}
float C_Line::difference_y_from_center(const std::list<cv::Rect>& boxes) const
{
float dist = 0.0f;
std::list<cv::Rect>::const_iterator it(boxes.begin());
while (it != boxes.end()) {
dist += difference_y_from_center(*it);
it++;
}
return dist;
}
//returns sum of distances of a list of points from their vertical projections on the line
float C_Line::dist_y(const std::list<cv::Point2f>& points) const
{
float dist = 0.0f;
std::list<cv::Point2f>::const_iterator it(points.begin());
while (it != points.end()) {
dist += dist_y(*it);
it++;
}
return dist;
}
float C_Line::dist_y(const std::list<cv::Point>& points) const
{
float dist = 0.0f;
std::list<cv::Point>::const_iterator it(points.begin());
while (it != points.end()) {
dist += dist_y(*it);
it++;
}
return dist;
}
float C_Line::error(const std::list<cv::Rect>& boxes) const
{
if (boxes.size()) {
return dist_y_from_center(boxes) / static_cast<float>(boxes.size());
}
else return 0.0f;
}
float C_Line::error(const std::list<cv::Point2f>& points) const
{
if (points.size()) {
return dist_y(points) / static_cast<float>(points.size());
}
else return 0.0f;
}

View File

@@ -0,0 +1,367 @@
/*
// Line.h: interface for the C_Line class.
//
*/
#include "ONNX_detector.h"
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/dnn/dnn.hpp>
#include <filesystem>
OnnxDetector::OnnxDetector(Ort::Env& env_, const ORTCHAR_T* model_path, const Ort::SessionOptions& options) : env(env_), sessionOptions(options), session(env_, model_path, options) {
dump();
}
OnnxDetector::OnnxDetector(Ort::Env& env_, const void* model_data, size_t model_data_length, const Ort::SessionOptions& options) : env(env_),sessionOptions(options),
session(env_, model_data, model_data_length, options)
{
dump();
}
void OnnxDetector::dump() const {
//std::cout << "Available execution providers:\n";
//for (const auto& s : Ort::GetAvailableProviders()) std::cout << '\t' << s << '\n';
Ort::AllocatorWithDefaultOptions allocator;
size_t numInputNodes = session.GetInputCount();
size_t numOutputNodes = session.GetOutputCount();
//std::cout << "Number of Input Nodes: " << numInputNodes << std::endl;
//std::cout << "Number of Output Nodes: " << numOutputNodes << std::endl;
//const char* inputName = session.GetInputName(0, allocator);
//std::cout << "Input Name: " << inputName << std::endl;
Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo();
ONNXTensorElementDataType inputType = inputTensorInfo.GetElementType();
//std::cout << "Input Type: " << inputType << std::endl;
std::vector<int64_t> inputDims = inputTensorInfo.GetShape();
//std::cout << "Input Dimensions: " << inputDims << std::endl;
//for (size_t i = 0; i < inputDims.size() - 1; i++)
// std::cout << inputDims[i] << std::endl;
//std::cout << std::endl;
//const char* outputName = session.GetOutputName(0, allocator);
//std::cout << "Output Name: " << outputName << std::endl;
Ort::TypeInfo outputTypeInfo = session.GetOutputTypeInfo(0);
auto outputTensorInfo = outputTypeInfo.GetTensorTypeAndShapeInfo();
ONNXTensorElementDataType outputType = outputTensorInfo.GetElementType();
//std::cout << "Output Type: " << outputType << std::endl;
std::vector<int64_t> outputDims = outputTensorInfo.GetShape();//1 25200 41
#ifdef _DEBUG
if (std::string(outputName) == "modelOutput" && std::string(inputName) == "modelInput"
|| std::string(outputName) == "platesTypeOutput" && std::string(inputName) == "inputImage") {
assert(inputDims.size() == 4);
assert(inputDims[0] == 1);
assert(inputDims[1] == 3);
assert(inputDims[2] == 80);
assert(inputDims[3] == 200);
for (size_t i = 0; i < inputDims.size(); i++)
std::cout << inputDims[i] << std::endl;
assert(outputDims.size() == 2);
assert(outputDims[0] == 1);
assert(outputDims[1] == 502);//502 types of lps
}
else {
assert(outputDims.size() == 3);
assert(outputDims[0] == 1);
assert(outputDims[2] == 103);// 0,1,2,3 ->box,4->confidence1 -> output classes = 36 characters+pays 61 + 1 vehicle= 98 classes =4+1+36+61+1=103
}
#endif //_DEBUG
// std::cout << "Output Dimensions: " << std::endl;
// for (size_t i = 0; i < outputDims.size(); i++)
// std::cout << outputDims[i] << std::endl;
// std::cout << std::endl;
}
//returns the maximum size of input image (ie width or height of dnn input layer)
int64_t OnnxDetector::max_image_size() const {
std::vector<std::vector<Detection>> result;
cv::Mat resizedImageRGB, resizedImage, preprocessedImage;
Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo();
std::vector<int64_t> inputDims = inputTensorInfo.GetShape();
int64_t max_size = inputDims.at(2);// width;
if (max_size < inputDims.at(3))//height
max_size = inputDims.at(3);
return max_size;
}
std::vector<std::vector<Detection>>
OnnxDetector::Run(const cv::Mat& img, float conf_threshold, float iou_threshold, bool preserve_aspect_ratio) {
std::vector<std::vector<Detection>> result;
cv::Mat resizedImageRGB, resizedImage, preprocessedImage;
Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo();
std::vector<int64_t> inputDims = inputTensorInfo.GetShape();
//cv::Mat img = cv::imread(imageFilepath, cv::ImreadModes::IMREAD_COLOR);
int channels_ = img.channels();
if (
img.size().width &&
img.size().height && ((channels_ == 1) || (channels_ == 3) || (channels_ == 4))) {
if (channels_ == 1) {
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_GRAY2RGB);
}
else if (channels_ == 4) {
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_BGRA2RGB);
}
else if (channels_ == 3) {
int type = img.type();
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_BGR2RGB);
}
float pad_w = -1.0f, pad_h = -1.0f, scale = -1.0f;
if (preserve_aspect_ratio) {
// keep the original image for visualization purpose
std::vector<float> pad_info = LetterboxImage(resizedImageRGB, resizedImageRGB, cv::Size(static_cast<int>(inputDims.at(2)), static_cast<int>(inputDims.at(3))));
//pad_w is the left (and also right) border width in the square image feeded to the model
pad_w = pad_info[0];
pad_h = pad_info[1];
scale = pad_info[2];
}
else {
cv::resize(resizedImageRGB, resizedImageRGB,
cv::Size(static_cast<int>(inputDims.at(2)), static_cast<int>(inputDims.at(3))),
cv::InterpolationFlags::INTER_CUBIC);
}
resizedImageRGB.convertTo(resizedImage, CV_32FC3, 1.0f / 255.0f);
// HWC to CHW
cv::dnn::blobFromImage(resizedImage, preprocessedImage);
int64_t inputTensorSize = vectorProduct(inputDims);
std::vector<float> inputTensorValues(inputTensorSize);
inputTensorValues.assign(preprocessedImage.begin<float>(),
preprocessedImage.end<float>());
Ort::TypeInfo outputTypeInfo = session.GetOutputTypeInfo(0);
auto outputTensorInfo = outputTypeInfo.GetTensorTypeAndShapeInfo();
ONNXTensorElementDataType outputType = outputTensorInfo.GetElementType();//1
#ifdef _DEBUG
assert(outputType == 1);
#endif //_DEBUG
std::vector<int64_t> outputDims = outputTensorInfo.GetShape();//1 25200 41
#ifdef _DEBUG
assert(outputDims.size() == 3);
assert(outputDims[0] == 1);
//assert(outputDims[1] == 25200);
assert(outputDims[2] == 103);// 0,1,2,3 ->box,4->confidence1 -> output classes = 36 characters+pays 61 + 1 vehicle= 98 classes =4+1+36+61+1=103
#endif //_DEBUG
int64_t outputTensorSize = vectorProduct(outputDims);
std::vector<float> outputTensorValues(outputTensorSize);
Ort::AllocatorWithDefaultOptions allocator;
std::vector<const char*> inputNames;
std::vector<Ort::AllocatedStringPtr> inputNodeNameAllocatedStrings; // <-- newly added
std::vector<const char*> outputNames;
std::vector<Ort::AllocatedStringPtr> outputNodeNameAllocatedStrings; // <-- newly added
inputNames.clear();
outputNames.clear();
auto inputName = session.GetInputNameAllocated(0, allocator);//session.GetInputName(0, allocator);
inputNodeNameAllocatedStrings.push_back(std::move(inputName));
inputNames.push_back(inputNodeNameAllocatedStrings.back().get());
auto outputName = session.GetOutputNameAllocated(0, allocator);
outputNodeNameAllocatedStrings.push_back(std::move(outputName));
outputNames.push_back(outputNodeNameAllocatedStrings.back().get());
std::vector<Ort::Value> inputTensors;
std::vector<Ort::Value> outputTensors;
Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(
OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);
inputTensors.push_back(Ort::Value::CreateTensor<float>(
memoryInfo, inputTensorValues.data(), inputTensorSize, inputDims.data(),
inputDims.size()));
outputTensors.push_back(Ort::Value::CreateTensor<float>(
memoryInfo, outputTensorValues.data(), outputTensorSize,
outputDims.data(), outputDims.size()));
// https://github.com/microsoft/onnxruntime/blob/rel-1.6.0/include/onnxruntime/core/session/onnxruntime_cxx_api.h#L353
session.Run(Ort::RunOptions{ nullptr }, inputNames.data(), inputTensors.data(), 1, outputNames.data(), outputTensors.data(), 1);
size_t dimensionsCount = outputTensorInfo.GetDimensionsCount();//3
#ifdef _DEBUG
assert(dimensionsCount == 3);
#endif //_DEBUG
float* output = outputTensors[0].GetTensorMutableData<float>(); // output of onnx runtime ->>> 1,25200,85
size_t size = outputTensors[0].GetTensorTypeAndShapeInfo().GetElementCount(); // 1x25200x85=2142000
int64_t dimensions = outputDims[2]; // 0,1,2,3 ->box,4->confidence5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
#ifdef _DEBUG
assert(dimensions >= 41);// 0,1,2,3 ->box,4->confidence5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
#endif //_DEBUG
const cv::Size& out_size = cv::Size(static_cast<int>(inputDims[3]), static_cast<int>(inputDims[3]));
std::vector<std::vector<Detection>> detections;
if (preserve_aspect_ratio) {
// keep the original image for visualization purpose
detections = (
PostProcessing(
output, // output of onnx runtime ->>> 1,25200,85
dimensionsCount,
size, // 1x25200x85=2142000
static_cast<int>(dimensions),
//pad_w is the left (and also right) border width in the square image feeded to the model
pad_w, pad_h, scale, img.size(),
conf_threshold, iou_threshold));
}
else {
detections = (PostProcessing(
output, // output of onnx runtime ->>> 1,25200,85
dimensionsCount,
size, // 1x25200x85=2142000
static_cast<int>(dimensions),
static_cast<float>(out_size.width), static_cast<float>(out_size.height), img.size(),
conf_threshold, iou_threshold));
}
#ifdef _DEBUG
std::list<cv::Rect> true_boxes; std::list<int> classesId;
std::vector<Detection>::const_iterator it(detections[0].begin());
while (it != detections[0].end()) {
true_boxes.push_back(it->bbox);
classesId.push_back(it->class_idx);
it++;
}
#endif //_DEBUG
return detections;
}
return result;
}
std::list<std::vector<std::vector<Detection>>>
OnnxDetector::Run(const cv::Mat& img
//, float conf_threshold
, float iou_threshold
) {
std::list<std::vector<std::vector<Detection>>> result;
cv::Mat resizedImageRGB, resizedImage, preprocessedImage;
Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo();
std::vector<int64_t> inputDims = inputTensorInfo.GetShape();
//cv::Mat img = cv::imread(imageFilepath, cv::ImreadModes::IMREAD_COLOR);
int channels_ = img.channels();
if (
img.size().width &&
img.size().height && ((channels_ == 1) || (channels_ == 3) || (channels_ == 4))) {
if (channels_ == 1) {
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_GRAY2RGB);
}
else if (channels_ == 4) {
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_BGRA2RGB);
}
else if (channels_ == 3) {
int type = img.type();
cv::cvtColor(img, resizedImageRGB,
cv::ColorConversionCodes::COLOR_BGR2RGB);
}
bool preserve_aspect_ratio = true;
float pad_w = -1.0f, pad_h = -1.0f, scale = -1.0f;
// keep the original image for visualization purpose
std::vector<float> pad_info = LetterboxImage(resizedImageRGB, resizedImageRGB, cv::Size(static_cast<int>(inputDims.at(2)), static_cast<int>(inputDims.at(3))));
//pad_w is the left (and also right) border width in the square image feeded to the model
pad_w = pad_info[0];
pad_h = pad_info[1];
scale = pad_info[2];
resizedImageRGB.convertTo(resizedImage, CV_32FC3, 1.0f / 255.0f);
// HWC to CHW
cv::dnn::blobFromImage(resizedImage, preprocessedImage);
int64_t inputTensorSize = vectorProduct(inputDims);
std::vector<float> inputTensorValues(inputTensorSize);
inputTensorValues.assign(preprocessedImage.begin<float>(),
preprocessedImage.end<float>());
Ort::TypeInfo outputTypeInfo = session.GetOutputTypeInfo(0);
auto outputTensorInfo = outputTypeInfo.GetTensorTypeAndShapeInfo();
ONNXTensorElementDataType outputType = outputTensorInfo.GetElementType();//1
#ifdef _DEBUG
assert(outputType == 1);
#endif //_DEBUG
std::vector<int64_t> outputDims = outputTensorInfo.GetShape();//1 25200 41
#ifdef _DEBUG
assert(outputDims.size() == 3);
assert(outputDims[0] == 1);
//assert(outputDims[1] == 25200);
assert(outputDims[2] == 103);// 0,1,2,3 ->box,4->confidence1 -> output classes = 36 characters+pays 61 + 1 vehicle= 98 classes =4+1+36+61+1=103
#endif //_DEBUG
int64_t outputTensorSize = vectorProduct(outputDims);
std::vector<float> outputTensorValues(outputTensorSize);
Ort::AllocatorWithDefaultOptions allocator;
std::vector<const char*> inputNames;
std::vector<Ort::AllocatedStringPtr> inputNodeNameAllocatedStrings; // <-- newly added
std::vector<const char*> outputNames;
std::vector<Ort::AllocatedStringPtr> outputNodeNameAllocatedStrings; // <-- newly added
inputNames.clear();
outputNames.clear();
auto inputName = session.GetInputNameAllocated(0, allocator);//session.GetInputName(0, allocator);
inputNodeNameAllocatedStrings.push_back(std::move(inputName));
inputNames.push_back(inputNodeNameAllocatedStrings.back().get());
auto outputName = session.GetOutputNameAllocated(0, allocator);
outputNodeNameAllocatedStrings.push_back(std::move(outputName));
outputNames.push_back(outputNodeNameAllocatedStrings.back().get());
std::vector<Ort::Value> inputTensors;
std::vector<Ort::Value> outputTensors;
Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(
OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);
inputTensors.push_back(Ort::Value::CreateTensor<float>(
memoryInfo, inputTensorValues.data(), inputTensorSize, inputDims.data(),
inputDims.size()));
outputTensors.push_back(Ort::Value::CreateTensor<float>(
memoryInfo, outputTensorValues.data(), outputTensorSize,
outputDims.data(), outputDims.size()));
// https://github.com/microsoft/onnxruntime/blob/rel-1.6.0/include/onnxruntime/core/session/onnxruntime_cxx_api.h#L353
session.Run(Ort::RunOptions{ nullptr }, inputNames.data(), inputTensors.data(), 1, outputNames.data(), outputTensors.data(), 1);
size_t dimensionsCount = outputTensorInfo.GetDimensionsCount();//3
#ifdef _DEBUG
assert(dimensionsCount == 3);
#endif //_DEBUG
float* output = outputTensors[0].GetTensorMutableData<float>(); // output of onnx runtime ->>> 1,25200,85
size_t size = outputTensors[0].GetTensorTypeAndShapeInfo().GetElementCount(); // 1x25200x85=2142000
int64_t dimensions = outputDims[2]; // 0,1,2,3 ->box,4->confidence5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
#ifdef _DEBUG
assert(dimensions >= 41);// 0,1,2,3 ->box,4->confidence5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
#endif //_DEBUG
const cv::Size& out_size = cv::Size(static_cast<int>(inputDims[3]), static_cast<int>(inputDims[3]));
const int nb_passes = 10;
const float conf_threshold_min = 0.1f;
for (int conf_threshold_step = 0; conf_threshold_step < nb_passes; conf_threshold_step++)
{
float conf_threshold = conf_threshold_min + static_cast<float>(conf_threshold_step) * (1.0f - 2.0f * conf_threshold_min) / (static_cast<float>(nb_passes));
// keep the original image for visualization purpose
std::vector<std::vector<Detection>> current_detections = (
PostProcessing(
output, // output of onnx runtime ->>> 1,25200,85
dimensionsCount,
size, // 1x25200x85=2142000
static_cast<int>(dimensions),
//pad_w is the left (and also right) border width in the square image feeded to the model
pad_w, pad_h, scale, img.size(),
conf_threshold, iou_threshold));
result.push_back(current_detections);
}
}
return result;
}
//non max suppession algorithm to select boxes
void nms(const std::vector<cv::Rect>& srcRects, std::vector<cv::Rect>& resRects, std::vector<int>& resIndexs, float thresh) {
resRects.clear();
const size_t size = srcRects.size();
if (!size) return;
// Sort the bounding boxes by the bottom - right y - coordinate of the bounding box
std::multimap<int, size_t> idxs;
for (size_t i = 0; i < size; ++i) {
idxs.insert(std::pair<int, size_t>(srcRects[i].br().y, i));
}
// keep looping while some indexes still remain in the indexes list
while (idxs.size() > 0) {
// grab the last rectangle
auto lastElem = --std::end(idxs);
const cv::Rect& last = srcRects[lastElem->second];
resIndexs.push_back(static_cast<int>(lastElem->second));
resRects.push_back(last);
idxs.erase(lastElem);
for (auto pos = std::begin(idxs); pos != std::end(idxs); ) {
// grab the current rectangle
const cv::Rect& current = srcRects[pos->second];
float intArea = static_cast<float>((last & current).area());
float unionArea = last.area() + current.area() - intArea;
float overlap = intArea / unionArea;
// if there is sufficient overlap, suppress the current bounding box
if (overlap > thresh) pos = idxs.erase(pos);
else ++pos;
}
}
}

View File

@@ -0,0 +1,256 @@
//*
// StatSommesX_Y_H_dbl.cpp: implementation of the C_SumsRegLineXYHDbl class.
//
//////////////////////////////////////////////////////////////////////
#ifndef GITHUB
#include "StatSommesX_Y_H_dbl.h"
#include <cmath>
#include <climits>
#ifdef _WINDOWS
#endif //_WINDOWS
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif
inline float divise_par_zero(const float& numerateur, // Modif PN passage de float en double
const bool denominateur_positif = true) {
if (denominateur_positif) {
if (numerateur > 0.0f) {
if (numerateur < FLT_EPSILON) return 0.0f;
else return FLT_MAX;
}
else {
if (FLT_EPSILON + numerateur > 0.0f) return 0.0f;
else return -FLT_MAX;
}
}
else {
if (numerateur > 0.0f) {
if (numerateur < FLT_EPSILON) return 0.0f;
else return -FLT_MAX;
}
else {
if (FLT_EPSILON + numerateur > 0.0f) return 0.0f;
else return FLT_MAX;
}
}
};
C_SumsRegLineXYHDbl::C_SumsRegLineXYHDbl() : somme_x(0.0f)
, somme_y(0.0f)
, produit_xy(0.0f)
, somme_carre_x(0.0f)
, somme_hauteurs(0)
{
}
C_SumsRegLineXYHDbl::C_SumsRegLineXYHDbl(const int somme_hauteurs_) : somme_x(0.0f)
, somme_y(0.0f)
, produit_xy(0.0f)
, somme_carre_x(0.0f)
, somme_hauteurs(somme_hauteurs_)
{
}
C_SumsRegLineXYHDbl::~C_SumsRegLineXYHDbl()
{
}
#ifdef _DEBUG
bool C_SumsRegLineXYHDbl::debug(const float& somme_x_,
const float& somme_y_,
const float& produit_xy_,
const float& somme_carre_x_) const
{
#ifdef LPR_DOUBLE_PRECISION
return (fabsf(somme_x - somme_x_) < FLT_EPSILON
&& fabsf(somme_y - somme_y_) < FLT_EPSILON
&& fabsf((produit_xy - produit_xy_)) < FLT_EPSILON
&& fabsf((somme_carre_x - somme_carre_x_)) < FLT_EPSILON
);
#else // LPR_DOUBLE_PRECISION
bool ok = fabsf(somme_x - somme_x_) < FLT_EPSILON
&& fabsf(somme_y - somme_y_) < FLT_EPSILON;
if (produit_xy + produit_xy_ > FLT_EPSILON)
ok = ok && fabsf((produit_xy - produit_xy_)) < FLT_EPSILON * (produit_xy + produit_xy_);
else ok = ok && fabsf((produit_xy - produit_xy_)) < FLT_EPSILON;
if (somme_carre_x + somme_carre_x_ > FLT_EPSILON)
ok = ok && fabsf((somme_carre_x - somme_carre_x_)) < FLT_EPSILON * (somme_carre_x + somme_carre_x_);
else ok = ok && fabsf((somme_carre_x - somme_carre_x_)) < FLT_EPSILON;
return ok;
#endif // LPR_DOUBLE_PRECISION
}
bool C_SumsRegLineXYHDbl::debug(const float& somme_x_,
const float& somme_y_,
const float& produit_xy_,
const float& somme_carre_x_,
const int heigth_) const
{
/*
return (somme_x== somme_x_ && somme_y==somme_y_ &&
produit_xy==produit_xy_ && somme_carre_x==somme_carre_x_ && somme_hauteurs==heigth_);
*/
#ifdef LPR_DOUBLE_PRECISION
return (fabsf(somme_x - somme_x_) < FLT_EPSILON
&& fabsf(somme_y - somme_y_) < FLT_EPSILON
&& fabsf((produit_xy - produit_xy_)) < FLT_EPSILON
&& fabsf((somme_carre_x - somme_carre_x_)) < FLT_EPSILON
&& somme_hauteurs == heigth_);
#else // LPR_DOUBLE_PRECISION
return (fabsf(somme_x - somme_x_) < FLT_EPSILON
&& fabsf(somme_y - somme_y_) < FLT_EPSILON
&& fabsf((produit_xy - produit_xy_)) < FLT_EPSILON * (produit_xy + produit_xy_)
&& fabsf((somme_carre_x - somme_carre_x_)) < FLT_EPSILON * (somme_carre_x + somme_carre_x_)
&& somme_hauteurs == heigth_);
#endif // LPR_DOUBLE_PRECISION
}
#endif //_DEBUG
float C_SumsRegLineXYHDbl::pente(const int nb_elements) const
{//calcul de la moyenne des xi
//calcul de la moyenne des yi
#ifdef _DEBUG
assert(nb_elements > 1);
#endif //_DEBUG
if (nb_elements == 1) return 0.0f;
else if (nb_elements > 1) {
float moyenne_x = somme_x / nb_elements;
float moyenne_y = somme_y / nb_elements;
//calcul de la std_deviation(X)
float variance = (somme_carre_x)-somme_x * moyenne_x;
if (fabsf(variance) < FLT_EPSILON) return FLT_MAX;
//calcul de la Covariance(X,Y)
float Covariance = (produit_xy)-moyenne_x * somme_y;
//calcul de la pente p=Covariance(X,Y)/variance(X)
#ifdef _DEBUG
assert(variance > -FLT_EPSILON);
#endif //_DEBUG
float pente_;
if (variance != 0.0f) pente_ = Covariance / variance;
else pente_ = divise_par_zero(Covariance);
#ifdef _DEBUG
const float pi = 3.1415926535897932384626433832795f;
#ifdef _DEBUG
assert(atanf(pente_) <= pi / 2 && (atanf(pente_) + pi / 2) > -FLT_EPSILON);
#endif //_DEBUG
#endif
return pente_;
}
else return 0.0f;
}
cv::Point2f C_SumsRegLineXYHDbl::barycenter(const int nb_points)
{
#ifdef _DEBUG
assert(nb_points > 1);
#endif //_DEBUG
if (nb_points <= 0) return cv::Point2f(FLT_MAX, FLT_MAX);
else if (nb_points == 0) {
float moyenne_x = somme_x;
float moyenne_y = somme_y;
return cv::Point2f(moyenne_x, moyenne_y);
}
else {
#ifdef _DEBUG
assert(nb_points > 1);
#endif //_DEBUG
float moyenne_x = somme_x / nb_points;
float moyenne_y = somme_y / nb_points;
return cv::Point2f(moyenne_x, moyenne_y);
}
}
C_Line C_SumsRegLineXYHDbl::regression_line(const int nb_elements)
{
if (nb_elements == 0) return C_Line();
else if (nb_elements == 1) return C_Line(0.0, somme_y);
else if (nb_elements > 1) {
float moyenne_x = somme_x / nb_elements;
float moyenne_y = somme_y / nb_elements;
//calcul de la std_deviation(X)
float variance = (somme_carre_x)-somme_x * moyenne_x;
if (fabsf(variance) < FLT_EPSILON) return C_Line(FLT_MAX, FLT_MAX);
//calcul de la Covariance(X,Y)
float Covariance = (produit_xy)-moyenne_x * somme_y;
//calcul de la pente_ p=Covariance(X,Y)/variance(X)
float pente_ = Covariance / variance;
//calcul du coefficient q ( y=px+q )
float ordonnee_origine = moyenne_y - pente_ * moyenne_x;
#ifdef _DEBUG
const float pi = 3.1415926535897932384626433832795f;
assert(atanf(pente_) <= pi / 2 && (atanf(pente_) + pi / 2) > -FLT_EPSILON);
#endif //_DEBUG
C_Line regression_line(pente_, ordonnee_origine);
#ifdef _DEBUG
//calcul de la moyenne des xi
//calcul de la moyenne des yi
//ce sont les coordonnees du centre de gravit du fond principal
float moyenne_x_ = somme_x / nb_elements;
float moyenne_y_ = somme_y / nb_elements;
//calcul de la std_deviation(X)
float variance_ = (somme_carre_x - somme_x * moyenne_x_);
//calcul de la Covariance(X,Y)
float Covariance_ = (produit_xy - moyenne_x_ * somme_y);
//calcul de la pente p=Covariance(X,Y)/variance(X)
#ifdef _DEBUG
assert(variance > -FLT_EPSILON);
#endif //_DEBUG
float pente__;
if (variance_ > FLT_EPSILON) pente__ = Covariance_ / variance_;
else {
pente__ = divise_par_zero(Covariance_);//calcul du coefficient q ( y=px+q )
}
float ordonnee_origine_ = moyenne_y_ - pente__ * moyenne_x_;
#ifdef _DEBUG
assert(fabsf(regression_line.a - pente__) < FLT_EPSILON && fabsf(regression_line.b - ordonnee_origine_) < 0.001f);
#endif //_DEBUG
float diff_ = regression_line.a - pente__;
diff_ = regression_line.b - ordonnee_origine_;
#endif //_DEBUG
return regression_line;
}
else return C_Line();
}
bool C_SumsRegLineXYHDbl::operator ==(const C_SumsRegLineXYHDbl& right_op) const
{
#ifdef _DEBUG
double somme_x_r = right_op.somme_x;
double somme_y_r = right_op.somme_y;
double produit_xy_r = right_op.produit_xy;
double somme_carre_x_r = right_op.somme_carre_x;
double produit_xy_ = produit_xy;
double somme_carre_x_ = somme_carre_x;
assert(fabsf(somme_x - right_op.somme_x) < FLT_EPSILON
&& fabsf(somme_y - right_op.somme_y) < FLT_EPSILON
&& somme_hauteurs == right_op.somme_hauteurs);
#ifdef LPR_DOUBLE_PRECISION
assert(fabsf((produit_xy - right_op.produit_xy)) <
FLT_EPSILON * (produit_xy + right_op.produit_xy)
&& fabsf((somme_carre_x - right_op.somme_carre_x)) < FLT_EPSILON *
(somme_carre_x + right_op.somme_carre_x));
#else // LPR_DOUBLE_PRECISION
assert(fabsf((produit_xy - right_op.produit_xy)) / (produit_xy + right_op.produit_xy) < FLT_EPSILON
&& fabsf((somme_carre_x - right_op.somme_carre_x)) / (somme_carre_x + right_op.somme_carre_x) < FLT_EPSILON);
#endif // LPR_DOUBLE_PRECISION
float dif = fabsf(somme_x - right_op.somme_x);
dif = fabsf(somme_y - right_op.somme_y);
dif = fabsf((produit_xy - right_op.produit_xy));
dif = fabsf((somme_carre_x - right_op.somme_carre_x));
#endif
#ifdef LPR_DOUBLE_PRECISION
return (fabsf(somme_x - right_op.somme_x) < FLT_EPSILON
&& fabsf(somme_y - right_op.somme_y) < FLT_EPSILON
&& fabsf((produit_xy - right_op.produit_xy)) < FLT_EPSILON * fabsf((produit_xy + right_op.produit_xy))
&& fabsf((somme_carre_x - right_op.somme_carre_x)) < FLT_EPSILON * (somme_carre_x + right_op.somme_carre_x)
&& somme_hauteurs == right_op.somme_hauteurs);
#else // LPR_DOUBLE_PRECISION
return (fabsf(somme_x - right_op.somme_x) < FLT_EPSILON
&& fabsf(somme_y - right_op.somme_y) < FLT_EPSILON
&& fabsf((produit_xy - right_op.produit_xy)) < FLT_EPSILON * (produit_xy + right_op.produit_xy)
&& fabsf((somme_carre_x - right_op.somme_carre_x)) < FLT_EPSILON * (somme_carre_x + right_op.somme_carre_x)
&& somme_hauteurs == right_op.somme_hauteurs);
#endif // LPR_DOUBLE_PRECISION
}
void C_SumsRegLineXYHDbl::clear()
{
somme_x = 0.0f;
somme_y = 0.0f;
produit_xy = 0.0f;
somme_carre_x = 0.0f;
somme_hauteurs = 0;
}
#endif //GITHUB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
/*
// Line.h: interface for the C_Line class.
*/
#include "../include/utils_image_file.h"
#include <opencv2/opencv.hpp>
#include <filesystem>
#include "../include/utils_alpr_detect.h"
#define NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE 36
/**
@brief
returns the true license plate number out of a filename
you must place the true license plate number in the image filename this way : number+underscore+license plate number,
for instance filename 0000000001_3065WWA34.jpg will be interpreted as an image with the license plate 3065WWA34 in it.
@param filename: the image filename that contains in it the true registration number
@return the lpn contained in the image filename
@see
*/
std::string getTrueLPN(const std::string& filename, const bool& vrai_lpn_after_underscore)
{
std::string analysing_string = filename;
if (analysing_string == "")
return std::string();
char sep_underscore = '_';
size_t index = 0;
index = (analysing_string.find(sep_underscore));
if (index != -1)
{
std::string subanalysing_string;//la sous chaine
if (!vrai_lpn_after_underscore) {
subanalysing_string = analysing_string.substr(0, index);//la sous chaine
}
else {
subanalysing_string = analysing_string.substr(index + 1, analysing_string.length() - (index + 1));//la sous chaine
}
if (could_be_lpn(subanalysing_string))
return subanalysing_string;
else return std::string();
}
else {
if (could_be_lpn(filename))
return filename;
else return std::string();
}
}
/**
@brief
//checks if the characters contained in lpn are compatible with the alphabet
@param lpn: the registration of the vehicle as a string
@return
@see
*/
//extracts from a test directory all images files
void load_images_filenames(const std::string& dir, std::list<std::string>& image_filenames)
{
std::filesystem::path p(dir);
std::vector<std::filesystem::directory_entry> v; // To save the file names in a vector.
if (is_directory(p))
{
const std::string dir_path = p.string();
std::filesystem::directory_iterator b(p), e;
for (auto i = b; i != e; ++i)
{
if (std::filesystem::is_regular_file(*i)) {
std::filesystem::path fe = i->path().extension();
std::string extension = fe.string();
if (extension == ".bmp" || extension == ".BMP" || extension == ".jpg" || extension == ".JPG" || extension == ".jpeg")
{
std::filesystem::path p_(i->path());
//if you want to select images that have the true license plate number in the image filename
const bool select_images_with_lpn = true;
if (select_images_with_lpn) {
bool vrai_lpn_after_underscore = true;
//returns the true license plate number out of a filename
//you must place the true license plate number in the image filename this way : number + underscore + license plate number,
//for instance filename 0000000001_3065WWA34.jpg will be interpreted as an image with the license plate 3065WWA34 in it.
std::string ExactLPN(getTrueLPN(p_.stem().string(), vrai_lpn_after_underscore));
if (ExactLPN.size() > 3 && ExactLPN.size() < 11) {
image_filenames.push_back(i->path().string());
}
}
else {//take all images files -- output stats impossible
image_filenames.push_back(i->path().string());
}
}
}
}
}
}
/**
@brief
//return the ascii character that corresponds to index class output by the dnn
@param classe : integer index = class identifier, output by the object detection dnn
@return an ascii character
@see
*/
char get_char(const int classe) {
char _LATIN_LETTERS_LATIN_DIGITS[NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z','0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
if (classe >= 0 && classe < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE)
return _LATIN_LETTERS_LATIN_DIGITS[classe];
else return '?';
}
//retourne l'index du caractere LPChar
int get_index(const char LPChar)
{
switch (LPChar) {
case 'A': {return 0; } break;
case 'B': {return 1; } break;
case 'C': {return 2; } break;
case 'D': {return 3; } break;
case 'E': {return 4; } break;
case 'F': {return 5; } break;
case 'G': {return 6; } break;
case 'H': {return 7; } break;
case 'I': {return 8; } break;
case 'J': {return 9; } break;
case 'K': {return 10; } break;
case 'L': {return 11; } break;
case 'M': {return 12; } break;
case 'N': {return 13; } break;
case 'O': {return 14; } break;
case 'P': {return 15; } break;
case 'Q': {return 16; } break;
case 'R': {return 17; } break;
case 'S': {return 18; } break;
case 'T': {return 19; } break;
case 'U': {return 20; } break;
case 'V': {return 21; } break;
case 'W': {return 22; } break;
case 'X': {return 23; } break;
case 'Y': {return 24; } break;
case 'Z': {return 25; } break;
case '0': {return 26; } break;
case '1': {return 27; } break;
case '2': {return 28; } break;
case '3': {return 29; } break;
case '4': {return 30; } break;
case '5': {return 31; } break;
case '6': {return 32; } break;
case '7': {return 33; } break;
case '8': {return 34; } break;
case '9': {return 35; } break;
}
return -1;
}
//checks if the characters contained in lpn are compatible with the alphabet
/**
@brief
//checks if the characters contained in lpn are compatible with the alphabet
@param lpn: the registration of the vehicle as a string
@return
@see
*/
bool could_be_lpn(const std::string& lpn) {
char _LATIN_LETTERS_LATIN_DIGITS[NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z','0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
std::string::const_iterator it(lpn.begin());
std::list<char> chars;
while (it != lpn.end()) {
int i;
for (i = 0; i < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE; i++) {
if (*it == _LATIN_LETTERS_LATIN_DIGITS[i]) break;
}
if (i < NUMBER_OF_CARACTERS_LATIN_NUMBERPLATE) {
it++;
}
else return false;
}
return true;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff