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,843 @@
#include "ANSLPR_OV.h"
namespace ANSCENTER {
void tryPush(const std::weak_ptr<Worker>& worker, std::shared_ptr<Task>&& task) {
try {
std::shared_ptr<Worker>(worker)->push(task);
} catch (const std::bad_weak_ptr&) {}
}
void fillROIColor(cv::Mat& displayImage, cv::Rect roi, cv::Scalar color, double opacity) {
if (opacity > 0) {
roi = roi & cv::Rect(0, 0, displayImage.cols, displayImage.rows);
cv::Mat textROI = displayImage(roi);
cv::addWeighted(color, opacity, textROI, 1.0 - opacity , 0.0, textROI);
}
}
void putTextOnImage(cv::Mat& displayImage, std::string str, cv::Point p,
cv::HersheyFonts font, double fontScale, cv::Scalar color,
int thickness = 1, cv::Scalar bgcolor = cv::Scalar(),
double opacity = 0) {
int baseline = 0;
cv::Size textSize = cv::getTextSize(str, font, 0.5, 1, &baseline);
fillROIColor(displayImage, cv::Rect(cv::Point(p.x, p.y + baseline),
cv::Point(p.x + textSize.width, p.y - textSize.height)),
bgcolor, opacity);
cv::putText(displayImage, str, p, font, fontScale, color, thickness);
}
Detector::Detector(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const std::vector<float>& detectionTresholds,
const bool autoResize) :
m_autoResize(autoResize),
m_detectionTresholds{ detectionTresholds }
{
slog::info << "Reading model: " << xmlPath << slog::endl;
std::shared_ptr<ov::Model> model = core.read_model(xmlPath);
logBasicModelInfo(model);
// Check model inputs and outputs
ov::OutputVector inputs = model->inputs();
if (inputs.size() != 1) {
throw std::logic_error("Detector should have only one input");
}
m_detectorInputName = model->input().get_any_name();
ov::Layout modelLayout = ov::layout::get_layout(model->input());
if (modelLayout.empty())
modelLayout = { "NCHW" };
ov::OutputVector outputs = model->outputs();
if (outputs.size() != 1) {
throw std::logic_error("Vehicle Detection network should have only one output");
}
ov::Output<ov::Node> output = outputs[0];
m_detectorOutputName = output.get_any_name();
ov::Shape output_shape = output.get_shape();
if (output_shape.size() != 4) {
throw std::logic_error("Incorrect output dimensions for SSD");
}
if (maxProposalCount != output_shape[2]) {
throw std::logic_error("unexpected ProposalCount");
}
if (objectSize != output_shape[3]) {
throw std::logic_error("Output should have 7 as a last dimension");
}
ov::preprocess::PrePostProcessor ppp(model);
ov::preprocess::InputInfo& inputInfo = ppp.input();
ov::preprocess::InputTensorInfo& inputTensorInfo = inputInfo.tensor();
// configure desired input type and layout, the
// use preprocessor to convert to actual model input type and layout
inputTensorInfo.set_element_type(ov::element::u8);
inputTensorInfo.set_layout({ "NHWC" });
if (autoResize) {
inputTensorInfo.set_spatial_dynamic_shape();
}
ov::preprocess::InputModelInfo& inputModelInfo = inputInfo.model();
inputModelInfo.set_layout(modelLayout);
ov::preprocess::PreProcessSteps& preProcessSteps = inputInfo.preprocess();
preProcessSteps.convert_layout(modelLayout);
preProcessSteps.convert_element_type(ov::element::f32);
if (autoResize) {
preProcessSteps.resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
}
model = ppp.build();
slog::info << "Preprocessor configuration: " << slog::endl;
slog::info << ppp << slog::endl;
m_compiled_model = core.compile_model(model, deviceName);
logCompiledModelInfo(m_compiled_model, xmlPath, deviceName, "Vehicle And License Plate Detection");
}
ov::InferRequest Detector::createInferRequest() {
return m_compiled_model.create_infer_request();
}
void Detector::setImage(ov::InferRequest& inferRequest, const cv::Mat& img) {
ov::Tensor inputTensor = inferRequest.get_tensor(m_detectorInputName);
ov::Shape shape = inputTensor.get_shape();
if (m_autoResize) {
if (!img.isSubmatrix()) {
// just wrap Mat object with Tensor without additional memory allocation
ov::Tensor frameTensor = wrapMat2Tensor(img);
inferRequest.set_tensor(m_detectorInputName, frameTensor);
}
else {
throw std::logic_error("Sparse matrix are not supported");
}
}
else {
// resize and copy data from image to tensor using OpenCV
resize2tensor(img, inputTensor);
}
}
std::list<Detector::Result> Detector::getResults(ov::InferRequest& inferRequest,
cv::Size upscale,
std::vector<std::string>& rawResults) {
// there is no big difference if InferReq of detector from another device is passed
// because the processing is the same for the same topology
std::list<Result> results;
ov::Tensor output_tensor = inferRequest.get_tensor(m_detectorOutputName);
const float* const detections = output_tensor.data<float>();
// pretty much regular SSD post-processing
for (int i = 0; i < maxProposalCount; i++) {
float image_id = detections[i * objectSize + 0]; // in case of batch
if (image_id < 0) { // indicates end of detections
break;
}
size_t label = static_cast<decltype(m_detectionTresholds.size())>(detections[i * objectSize + 1]);
float confidence = detections[i * objectSize + 2];
if (label - 1 < m_detectionTresholds.size() && confidence < m_detectionTresholds[label - 1]) {
continue;
}
cv::Rect rect;
rect.x = static_cast<int>(detections[i * objectSize + 3] * upscale.width);
rect.y = static_cast<int>(detections[i * objectSize + 4] * upscale.height);
rect.width = static_cast<int>(detections[i * objectSize + 5] * upscale.width) - rect.x;
rect.height = static_cast<int>(detections[i * objectSize + 6] * upscale.height) - rect.y;
results.push_back(Result{ label, confidence, rect });
std::ostringstream rawResultsStream;
rawResultsStream << "[" << i << "," << label << "] element, prob = " << confidence
<< " (" << rect.x << "," << rect.y << ")-(" << rect.width << "," << rect.height << ")";
rawResults.push_back(rawResultsStream.str());
}
return results;
}
VehicleAttributesClassifier::VehicleAttributesClassifier(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const bool autoResize) :m_autoResize(autoResize)
{
slog::info << "Reading model: " << xmlPath << slog::endl;
std::shared_ptr<ov::Model> model = core.read_model(xmlPath);
logBasicModelInfo(model);
ov::OutputVector inputs = model->inputs();
if (inputs.size() != 1) {
throw std::logic_error("Vehicle Attribs topology should have only one input");
}
m_attributesInputName = model->input().get_any_name();
ov::Layout modelLayout = ov::layout::get_layout(model->input());
if (modelLayout.empty())
modelLayout = { "NCHW" };
ov::OutputVector outputs = model->outputs();
if (outputs.size() != 2) {
throw std::logic_error("Vehicle Attribs Network expects networks having two outputs");
}
// color is the first output
m_outputNameForColor = outputs[0].get_any_name();
// type is the second output.
m_outputNameForType = outputs[1].get_any_name();
ov::preprocess::PrePostProcessor ppp(model);
ov::preprocess::InputInfo& inputInfo = ppp.input();
ov::preprocess::InputTensorInfo& inputTensorInfo = inputInfo.tensor();
// configure desired input type and layout, the
// use preprocessor to convert to actual model input type and layout
inputTensorInfo.set_element_type(ov::element::u8);
inputTensorInfo.set_layout({ "NHWC" });
if (autoResize) {
inputTensorInfo.set_spatial_dynamic_shape();
}
ov::preprocess::PreProcessSteps& preProcessSteps = inputInfo.preprocess();
preProcessSteps.convert_layout(modelLayout);
preProcessSteps.convert_element_type(ov::element::f32);
if (autoResize) {
preProcessSteps.resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
}
ov::preprocess::InputModelInfo& inputModelInfo = inputInfo.model();
inputModelInfo.set_layout(modelLayout);
model = ppp.build();
slog::info << "Preprocessor configuration: " << slog::endl;
slog::info << ppp << slog::endl;
m_compiled_model = core.compile_model(model, deviceName);
logCompiledModelInfo(m_compiled_model, xmlPath, deviceName, "Vehicle Attributes Recognition");
}
ov::InferRequest VehicleAttributesClassifier::createInferRequest() {
return m_compiled_model.create_infer_request();
}
void VehicleAttributesClassifier::setImage(ov::InferRequest& inferRequest,
const cv::Mat& img,
const cv::Rect vehicleRect)
{
ov::Tensor inputTensor = inferRequest.get_tensor(m_attributesInputName);
ov::Shape shape = inputTensor.get_shape();
if (m_autoResize) {
ov::Tensor frameTensor = wrapMat2Tensor(img);
ov::Coordinate p00({ 0, static_cast<size_t>(vehicleRect.y), static_cast<size_t>(vehicleRect.x), 0 });
ov::Coordinate p01({ 1, static_cast<size_t>(vehicleRect.y + vehicleRect.height), static_cast<size_t>(vehicleRect.x) + vehicleRect.width, 3 });
ov::Tensor roiTensor(frameTensor, p00, p01);
inferRequest.set_tensor(m_attributesInputName, roiTensor);
}
else {
const cv::Mat& vehicleImage = img(vehicleRect);
resize2tensor(vehicleImage, inputTensor);
}
}
std::pair<std::string, std::string> VehicleAttributesClassifier::getResults(ov::InferRequest& inferRequest) {
static const std::string colors[] = {
"white", "gray", "yellow", "red", "green", "blue", "black"
};
static const std::string types[] = {
"car", "van", "truck", "bus"
};
// 7 possible colors for each vehicle and we should select the one with the maximum probability
ov::Tensor colorsTensor = inferRequest.get_tensor(m_outputNameForColor);
const float* colorsValues = colorsTensor.data<float>();
// 4 possible types for each vehicle and we should select the one with the maximum probability
ov::Tensor typesTensor = inferRequest.get_tensor(m_outputNameForType);
const float* typesValues = typesTensor.data<float>();
const auto color_id = std::max_element(colorsValues, colorsValues + 7) - colorsValues;
const auto type_id = std::max_element(typesValues, typesValues + 4) - typesValues;
return std::pair<std::string, std::string>(colors[color_id], types[type_id]);
}
Lpr::Lpr(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const bool autoResize) :m_autoResize(autoResize)
{
slog::info << "Reading model: " << xmlPath << slog::endl;
std::shared_ptr<ov::Model> model = core.read_model(xmlPath);
logBasicModelInfo(model);
// LPR network should have 2 inputs (and second is just a stub) and one output
// Check inputs
ov::OutputVector inputs = model->inputs();
if (inputs.size() != 1 && inputs.size() != 2) {
throw std::logic_error("LPR should have 1 or 2 inputs");
}
for (auto input : inputs) {
if (input.get_shape().size() == 4) {
m_LprInputName = input.get_any_name();
m_modelLayout = ov::layout::get_layout(input);
if (m_modelLayout.empty())
m_modelLayout = { "NCHW" };
}
// LPR model that converted from Caffe have second a stub input
if (input.get_shape().size() == 2)
m_LprInputSeqName = input.get_any_name();
}
// Check outputs
m_maxSequenceSizePerPlate = 1;
ov::OutputVector outputs = model->outputs();
if (outputs.size() != 1) {
throw std::logic_error("LPR should have 1 output");
}
m_LprOutputName = outputs[0].get_any_name();
for (size_t dim : outputs[0].get_shape()) {
if (dim == 1) {
continue;
}
if (m_maxSequenceSizePerPlate == 1) {
m_maxSequenceSizePerPlate = dim;
}
else {
throw std::logic_error("Every dimension of LPR output except for one must be of size 1");
}
}
ov::preprocess::PrePostProcessor ppp(model);
ov::preprocess::InputInfo& inputInfo = ppp.input(m_LprInputName);
ov::preprocess::InputTensorInfo& inputTensorInfo = inputInfo.tensor();
// configure desired input type and layout, the
// use preprocessor to convert to actual model input type and layout
inputTensorInfo.set_element_type(ov::element::u8);
inputTensorInfo.set_layout({ "NHWC" });
if (autoResize) {
inputTensorInfo.set_spatial_dynamic_shape();
}
ov::preprocess::PreProcessSteps& preProcessSteps = inputInfo.preprocess();
preProcessSteps.convert_layout(m_modelLayout);
preProcessSteps.convert_element_type(ov::element::f32);
if (autoResize) {
preProcessSteps.resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
}
ov::preprocess::InputModelInfo& inputModelInfo = inputInfo.model();
inputModelInfo.set_layout(m_modelLayout);
model = ppp.build();
slog::info << "Preprocessor configuration: " << slog::endl;
slog::info << ppp << slog::endl;
m_compiled_model = core.compile_model(model, deviceName);
logCompiledModelInfo(m_compiled_model, xmlPath, deviceName, "License Plate Recognition");
}
ov::InferRequest Lpr::createInferRequest() {
return m_compiled_model.create_infer_request();
}
void Lpr::setImage(ov::InferRequest& inferRequest, const cv::Mat& img, const cv::Rect plateRect) {
ov::Tensor inputTensor = inferRequest.get_tensor(m_LprInputName);
ov::Shape shape = inputTensor.get_shape();
if ((shape.size() == 4) && m_autoResize) {
// autoResize is set
ov::Tensor frameTensor = wrapMat2Tensor(img);
ov::Coordinate p00({ 0, static_cast<size_t>(plateRect.y), static_cast<size_t>(plateRect.x), 0 });
ov::Coordinate p01({ 1, static_cast<size_t>(plateRect.y + plateRect.height), static_cast<size_t>(plateRect.x + plateRect.width), 3 });
ov::Tensor roiTensor(frameTensor, p00, p01);
inferRequest.set_tensor(m_LprInputName, roiTensor);
}
else {
const cv::Mat& vehicleImage = img(plateRect);
resize2tensor(vehicleImage, inputTensor);
}
if (m_LprInputSeqName != "") {
ov::Tensor inputSeqTensor = inferRequest.get_tensor(m_LprInputSeqName);
float* data = inputSeqTensor.data<float>();
std::fill(data, data + inputSeqTensor.get_shape()[0], 1.0f);
}
}
std::string Lpr::getResults(ov::InferRequest& inferRequest) {
static const char* const items[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "",
"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"
};
std::string result;
result.reserve(14u + 6u); // the longest province name + 6 plate signs
ov::Tensor lprOutputTensor = inferRequest.get_tensor(m_LprOutputName);
ov::element::Type precision = lprOutputTensor.get_element_type();
// up to 88 items per license plate, ended with "-1"
switch (precision) {
case ov::element::i32:
{
const auto data = lprOutputTensor.data<int32_t>();
for (int i = 0; i < m_maxSequenceSizePerPlate; i++) {
int32_t val = data[i];
if (val == -1) {
break;
}
result += items[val];
}
}
break;
case ov::element::f32:
{
const auto data = lprOutputTensor.data<float>();
for (int i = 0; i < m_maxSequenceSizePerPlate; i++) {
int32_t val = int32_t(data[i]);
if (val == -1) {
break;
}
result += items[val];
}
}
break;
default:
throw std::logic_error("Not expected output blob precision");
break;
}
return result;
}
// Utilities
ReborningVideoFrame::~ReborningVideoFrame() {
try {
const std::shared_ptr<Worker>& worker = std::shared_ptr<Worker>(context.readersContext.readersWorker);
context.videoFramesContext.lastFrameIdsMutexes[sourceID].lock();
const auto frameId = ++context.videoFramesContext.lastframeIds[sourceID];
context.videoFramesContext.lastFrameIdsMutexes[sourceID].unlock();
std::shared_ptr<ReborningVideoFrame> reborn = std::make_shared<ReborningVideoFrame>(context, sourceID, frameId, frame);
worker->push(std::make_shared<Reader>(reborn));
}
catch (const std::bad_weak_ptr&) {}
}
void ResAggregator::process() {
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
context.freeDetectionInfersCount += context.detectorsInfers.inferRequests.lockedSize();
context.frameCounter++;
context.boxesAndDescrs = boxesAndDescrs;
try {
std::shared_ptr<Worker>(context.resAggregatorsWorker)->stop();
}
catch (const std::bad_weak_ptr&) {}
}
bool DetectionsProcessor::isReady() {
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
if (requireGettingNumberOfDetections) {
classifiersAggregator = std::make_shared<ClassifiersAggregator>(sharedVideoFrame);
std::list<Detector::Result> results;
results = context.inferTasksContext.detector.getResults(*inferRequest, sharedVideoFrame->frame.size(), classifiersAggregator->rawDetections);
for (Detector::Result result : results) {
switch (result.label) {
case 1:// Vehicle
{
vehicleRects.emplace_back(result.location & cv::Rect{ cv::Point(0, 0), sharedVideoFrame->frame.size() });
break;
}
case 2:// License Plate
{
// expanding a bounding box a bit, better for the license plate recognition
result.location.x -= 5;
result.location.y -= 5;
result.location.width += 10;
result.location.height += 10;
plateRects.emplace_back(result.location & cv::Rect{ cv::Point(0, 0), sharedVideoFrame->frame.size() });
break;
}
default:
throw std::runtime_error("Unexpected detection results"); // must never happen
break;
}
}
context.detectorsInfers.inferRequests.lockedPushBack(*inferRequest);
requireGettingNumberOfDetections = false;
}
if ((vehicleRects.empty()) && (plateRects.empty())) {
return true;
}
else {
InferRequestsContainer& attributesInfers = context.attributesInfers;
attributesInfers.inferRequests.mutex.lock();
const std::size_t numberOfAttributesInferRequestsAcquired = std::min(vehicleRects.size(), attributesInfers.inferRequests.container.size());
reservedAttributesRequests.assign(attributesInfers.inferRequests.container.end() - numberOfAttributesInferRequestsAcquired,attributesInfers.inferRequests.container.end());
attributesInfers.inferRequests.container.erase(attributesInfers.inferRequests.container.end() - numberOfAttributesInferRequestsAcquired,attributesInfers.inferRequests.container.end());
attributesInfers.inferRequests.mutex.unlock();
InferRequestsContainer& platesInfers = context.platesInfers;
platesInfers.inferRequests.mutex.lock();
const std::size_t numberOfLprInferRequestsAcquired = std::min(plateRects.size(), platesInfers.inferRequests.container.size());
reservedLprRequests.assign(platesInfers.inferRequests.container.end() - numberOfLprInferRequestsAcquired, platesInfers.inferRequests.container.end());
platesInfers.inferRequests.container.erase(platesInfers.inferRequests.container.end() - numberOfLprInferRequestsAcquired,platesInfers.inferRequests.container.end());
platesInfers.inferRequests.mutex.unlock();
return numberOfAttributesInferRequestsAcquired || numberOfLprInferRequestsAcquired;
}
}
void DetectionsProcessor::process() {
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
auto vehicleRectsIt = vehicleRects.begin();
for (auto attributesRequestIt = reservedAttributesRequests.begin(); attributesRequestIt != reservedAttributesRequests.end();
vehicleRectsIt++, attributesRequestIt++) {
const cv::Rect vehicleRect = *vehicleRectsIt;
ov::InferRequest& attributesRequest = *attributesRequestIt;
context.detectionsProcessorsContext.vehicleAttributesClassifier.setImage(attributesRequest, sharedVideoFrame->frame, vehicleRect);
attributesRequest.set_callback(
std::bind(
[](std::shared_ptr<ClassifiersAggregator> classifiersAggregator,
ov::InferRequest& attributesRequest,
cv::Rect rect,
Context& context) {
attributesRequest.set_callback([](std::exception_ptr) {}); // destroy the stored bind object
const std::pair<std::string, std::string>& attributes =context.detectionsProcessorsContext.vehicleAttributesClassifier.getResults(attributesRequest);
if (((classifiersAggregator->sharedVideoFrame->frameId == 0 && !context.isVideo) || context.isVideo)) {
classifiersAggregator->rawAttributes.lockedPushBack("Vehicle Attributes results:" + attributes.first + ';' + attributes.second);
}
classifiersAggregator->push(BboxAndDescr{ BboxAndDescr::ObjectType::VEHICLE, rect, attributes.first + ' ' + attributes.second });
context.attributesInfers.inferRequests.lockedPushBack(attributesRequest);
},
classifiersAggregator,
std::ref(attributesRequest),
vehicleRect,
std::ref(context)));
attributesRequest.start_async();
}
vehicleRects.erase(vehicleRects.begin(), vehicleRectsIt);
auto plateRectsIt = plateRects.begin();
for (auto lprRequestsIt = reservedLprRequests.begin(); lprRequestsIt != reservedLprRequests.end(); plateRectsIt++, lprRequestsIt++) {
const cv::Rect plateRect = *plateRectsIt;
ov::InferRequest& lprRequest = *lprRequestsIt;
context.detectionsProcessorsContext.lpr.setImage(lprRequest, sharedVideoFrame->frame, plateRect);
lprRequest.set_callback(
std::bind(
[](std::shared_ptr<ClassifiersAggregator> classifiersAggregator,
ov::InferRequest& lprRequest,
cv::Rect rect,
Context& context) {
lprRequest.set_callback([](std::exception_ptr) {}); // destroy the stored bind object
std::string result = context.detectionsProcessorsContext.lpr.getResults(lprRequest);
if (((classifiersAggregator->sharedVideoFrame->frameId == 0 && !context.isVideo) || context.isVideo)) {
classifiersAggregator->rawDecodedPlates.lockedPushBack("License Plate Recognition results:" + result);
}
classifiersAggregator->push(BboxAndDescr{ BboxAndDescr::ObjectType::PLATE, rect, std::move(result) });
context.platesInfers.inferRequests.lockedPushBack(lprRequest);
}, classifiersAggregator,
std::ref(lprRequest),
plateRect,
std::ref(context)));
lprRequest.start_async();
}
plateRects.erase(plateRects.begin(), plateRectsIt);
if (!vehicleRects.empty() || !plateRects.empty()) {
tryPush(context.detectionsProcessorsContext.detectionsProcessorsWorker,
std::make_shared<DetectionsProcessor>(sharedVideoFrame, std::move(classifiersAggregator), std::move(vehicleRects), std::move(plateRects)));
}
}
bool InferTask::isReady() {
InferRequestsContainer& detectorsInfers = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context.detectorsInfers;
if (detectorsInfers.inferRequests.container.empty()) {
return false;
}
else {
detectorsInfers.inferRequests.mutex.lock();
if (detectorsInfers.inferRequests.container.empty()) {
detectorsInfers.inferRequests.mutex.unlock();
return false;
}
else {
return true; // process() will unlock the mutex
}
}
}
void InferTask::process() {
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
InferRequestsContainer& detectorsInfers = context.detectorsInfers;
std::reference_wrapper<ov::InferRequest> inferRequest = detectorsInfers.inferRequests.container.back();
detectorsInfers.inferRequests.container.pop_back();
detectorsInfers.inferRequests.mutex.unlock();
context.inferTasksContext.detector.setImage(inferRequest, sharedVideoFrame->frame);
inferRequest.get().set_callback(
std::bind(
[](VideoFrame::Ptr sharedVideoFrame,
ov::InferRequest& inferRequest,
Context& context) {
inferRequest.set_callback([](std::exception_ptr) {}); // destroy the stored bind object
tryPush(context.detectionsProcessorsContext.detectionsProcessorsWorker,
std::make_shared<DetectionsProcessor>(sharedVideoFrame, &inferRequest));
}, sharedVideoFrame,
inferRequest,
std::ref(context)));
inferRequest.get().start_async();
// do not push as callback does it
}
bool Reader::isReady() {
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
context.readersContext.lastCapturedFrameIdsMutexes[sharedVideoFrame->sourceID].lock();
if (context.readersContext.lastCapturedFrameIds[sharedVideoFrame->sourceID] + 1 == sharedVideoFrame->frameId) {
return true;
}
else {
context.readersContext.lastCapturedFrameIdsMutexes[sharedVideoFrame->sourceID].unlock();
return false;
}
}
void Reader::process() {
unsigned sourceID = sharedVideoFrame->sourceID;
sharedVideoFrame->timestamp = std::chrono::steady_clock::now();
Context& context = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context;
const std::vector<std::shared_ptr<InputChannel>>& inputChannels = context.readersContext.inputChannels;
if (inputChannels[sourceID]->read(sharedVideoFrame->frame)) {
context.readersContext.lastCapturedFrameIds[sourceID]++;
context.readersContext.lastCapturedFrameIdsMutexes[sourceID].unlock();
tryPush(context.inferTasksContext.inferTasksWorker, std::make_shared<InferTask>(sharedVideoFrame));
}
else {
context.readersContext.lastCapturedFrameIds[sourceID]++;
context.readersContext.lastCapturedFrameIdsMutexes[sourceID].unlock();
try {
std::shared_ptr<Worker>(context.resAggregatorsWorker)->stop();
}
catch (const std::bad_weak_ptr&) {}
}
}
/// <summary>
/// Main class
/// </summary>
ANSALPR_OV::ANSALPR_OV() {};
ANSALPR_OV::~ANSALPR_OV() {
if (_detector == nullptr) {
delete _detector;
_detector = nullptr;
}
if (_vehicleAttributesClassifier == nullptr) {
delete _vehicleAttributesClassifier;
_vehicleAttributesClassifier = nullptr;
}
if (_lpr == nullptr) {
delete _lpr;
_lpr = nullptr;
}
};
bool ANSALPR_OV::Destroy() {
if (_detector == nullptr) {
delete _detector;
_detector = nullptr;
}
if (_vehicleAttributesClassifier == nullptr) {
delete _vehicleAttributesClassifier;
_vehicleAttributesClassifier = nullptr;
}
if (_lpr == nullptr) {
delete _lpr;
_lpr = nullptr;
}
return true;
};
bool ANSALPR_OV::Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword) {
try {
_licenseKey = licenseKey;
_licenseValid = false;
CheckLicense();
if (!_licenseValid) {
this->_logger->LogError("ANSALPR_OV::Initialize.", "License is not valid.", __FILE__, __LINE__);
return false;
}
// Extract model folder
// 0. Check if the modelZipFilePath exist?
if (!FileExist(modelZipFilePath)) {
this->_logger->LogFatal("ANSALPR_OV::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
}
// 1. Unzip model zip file to a special location with folder name as model file (and version)
std::string outputFolder;
std::vector<std::string> passwordArray;
if (!modelZipPassword.empty()) passwordArray.push_back(modelZipPassword);
passwordArray.push_back("AnsDemoModels20@!");
passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
std::string modelName = GetFileNameWithoutExtension(modelZipFilePath);
size_t vectorSize = passwordArray.size();
for (size_t i = 0; i < vectorSize; i++) {
if (ExtractPasswordProtectedZip(modelZipFilePath, passwordArray[i], modelName, _modelFolder, false))
break; // Break the loop when the condition is met.
}
// 2. Check if the outputFolder exist
if (!FolderExist(_modelFolder)) {
this->_logger->LogError("ANSALPR_OV::Initialize. Output model folder is not exist", _modelFolder, __FILE__, __LINE__);
return false; // That means the model file is not exist or the password is not correct
}
_vehicleLPModel = CreateFilePath(_modelFolder, "vehiclelp.xml");
_vehicleAtModel = CreateFilePath(_modelFolder, "vehicle.xml");
_lprModel = CreateFilePath(_modelFolder, "lpr.xml");
ov::Core core;
int FLAGS_nthreads = 0;
std::set<std::string> devices;
for (const std::string& netDevices : { "GPU", "GPU", "GPU" }) {
if (netDevices.empty()) {
continue;
}
for (const std::string& device : parseDevices(netDevices)) {
devices.insert(device);
}
}
std::map<std::string, int32_t> device_nstreams = parseValuePerDevice(devices, "");
for (const std::string& device : devices) {
if ("CPU" == device) {
if (FLAGS_nthreads != 0) {
core.set_property("CPU", ov::inference_num_threads(FLAGS_nthreads));
}
//core.set_property("CPU", ov::affinity(ov::Affinity::NONE));
core.set_property("CPU", ov::streams::num((device_nstreams.count("CPU") > 0 ? ov::streams::Num(device_nstreams["CPU"]) : ov::streams::AUTO)));
device_nstreams["CPU"] = core.get_property("CPU", ov::streams::num);
}
if ("GPU" == device) {
core.set_property("GPU", ov::streams::num(device_nstreams.count("GPU") > 0 ? ov::streams::Num(device_nstreams["GPU"]) : ov::streams::AUTO));
device_nstreams["GPU"] = core.get_property("GPU", ov::streams::num);
if (devices.end() != devices.find("CPU")) {
core.set_property("GPU", ov::intel_gpu::hint::queue_throttle(ov::intel_gpu::hint::ThrottleLevel(1)));
}
}
}
double FLAGS_t = 0.5;
if(FileExist(_vehicleLPModel))_detector = new Detector(core, "GPU", _vehicleLPModel, { static_cast<float>(FLAGS_t), static_cast<float>(FLAGS_t) }, false);
else {
this->_logger->LogFatal("ANSALPR_OV::Initialize", _vehicleLPModel, __FILE__, __LINE__);
}
if(FileExist(_vehicleAtModel))_vehicleAttributesClassifier = new VehicleAttributesClassifier(core, "GPU", _vehicleAtModel, false);
else {
this->_logger->LogFatal("ANSALPR_OV::Initialize", _vehicleAtModel, __FILE__, __LINE__);
}
if(FileExist(_lprModel)) _lpr = new Lpr(core, "CPU", _lprModel, false);
else {
this->_logger->LogFatal("ANSALPR_OV::Initialize", _lprModel, __FILE__, __LINE__);
}
if (FileExist(_vehicleLPModel) &&
FileExist(_vehicleAtModel) &&
FileExist(_lprModel))return true;
else return false;
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR_OV::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
};
bool ANSALPR_OV::Inference(const cv::Mat& input, std::string& lprResult) {
cv::Mat frame = input.clone();
std::shared_ptr<IInputSource> inputSource = std::make_shared<ImageSource>(frame, true);
std::vector<std::shared_ptr<InputChannel>> _inputChannels;
_inputChannels.push_back(InputChannel::create(inputSource));
unsigned nireq = 1;
bool isVideo = false;
std::size_t nclassifiersireq{ 0 };
std::size_t nrecognizersireq{ 0 };
nclassifiersireq = nireq * 3;
nrecognizersireq = nireq * 3;
Context context = { _inputChannels,
*_detector,
*_vehicleAttributesClassifier,
*_lpr,
2,
nireq,
isVideo,
nclassifiersireq,
nrecognizersireq };
std::shared_ptr<Worker> worker = std::make_shared<Worker>(2);
context.readersContext.readersWorker = worker;
context.inferTasksContext.inferTasksWorker = worker;
context.detectionsProcessorsContext.detectionsProcessorsWorker = worker;
context.resAggregatorsWorker = worker;
for (unsigned sourceID = 0; sourceID < _inputChannels.size(); sourceID++) {
VideoFrame::Ptr sharedVideoFrame = std::make_shared<ReborningVideoFrame>(context, sourceID, 0);
worker->push(std::make_shared<Reader>(sharedVideoFrame));
}
// Running
worker->runThreads();
worker->threadFunc();
worker->join();
std::list<BboxAndDescr> boxesAndDescrs = context.boxesAndDescrs;
std::vector<ALPRObject> output;
output.clear();
for(const BboxAndDescr boxesAndDescr: boxesAndDescrs)
{
if (boxesAndDescr.objectType == ANSCENTER::BboxAndDescr::ObjectType::PLATE) {
ALPRObject result;
result.classId = 0;
result.className = boxesAndDescr.descr;
result.confidence = 1.0;
result.box = boxesAndDescr.rect;
output.push_back(result);
}
}
lprResult = VectorDetectionToJsonString(output);
return true;
};
bool ANSALPR_OV::Inference(const cv::Mat& input, const std::vector<cv::Rect>& Bbox, std::string& lprResult) {
return true;
}
std::string ANSALPR_OV::VectorDetectionToJsonString(const std::vector<ALPRObject>& dets) {
boost::property_tree::ptree root;
boost::property_tree::ptree detectedObjects;
for (int i = 0; i < dets.size(); i++) {
boost::property_tree::ptree detectedNode;
detectedNode.put("class_id", dets[i].classId);
detectedNode.put("class_name", dets[i].className);
detectedNode.put("prob", dets[i].confidence);
detectedNode.put("x", dets[i].box.x);
detectedNode.put("y", dets[i].box.y);
detectedNode.put("width", dets[i].box.width);
detectedNode.put("height", dets[i].box.height);
detectedNode.put("mask", "");//Todo: convert masks to mask with comma seperated dets[i].mask);
detectedNode.put("extra_info", "");
// we might add masks into this using comma seperated string
detectedObjects.push_back(std::make_pair("", detectedNode));
}
root.add_child("results", detectedObjects);
std::ostringstream stream;
boost::property_tree::write_json(stream, root, false);
std::string trackingResult = stream.str();
return trackingResult;
}
}

View File

@@ -0,0 +1,23 @@
// ANSGpuFrameRegistry.cpp — Cross-DLL singleton resolver for ANSLPR.dll.
//
// Finds the canonical ANSGpuFrameRegistry instance exported by ANSCV.dll
// via GetProcAddress. No link dependency on ANSCV.lib needed.
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include "ANSGpuFrameRegistry.h"
ANSGpuFrameRegistry* ANSGpuFrameRegistry::resolveProcessWide() {
// ANSCV.dll is always loaded before inference starts (it provides RTSP).
HMODULE hMod = GetModuleHandleA("ANSCV.dll");
if (hMod) {
typedef ANSGpuFrameRegistry* (*GetInstanceFn)();
auto fn = reinterpret_cast<GetInstanceFn>(
GetProcAddress(hMod, "ANSGpuFrameRegistry_GetInstance"));
if (fn) return fn();
}
// Fallback: local instance (unit tests without ANSCV.dll).
static ANSGpuFrameRegistry local;
return &local;
}

769
modules/ANSLPR/ANSLPR.cpp Normal file
View File

@@ -0,0 +1,769 @@
#include "ANSLPR.h"
#include <json.hpp>
#include "ANSLibsLoader.h"
static bool ansalprLicenceValid = false;
// Global once_flag to protect license checking
static std::once_flag ansalprLicenseOnceFlag;
template <typename T>
T GetData(const boost::property_tree::ptree& pt, const std::string& key)
{
T ret;
if (boost::optional<T> data = pt.get_optional<T>(key))
{
ret = data.get();
}
return ret;
}
namespace ANSCENTER {
// Checker class
void ALPRChecker::Init(int framesToStore) {
maxFrames = framesToStore;
}
int ALPRChecker::levenshteinDistance(const std::string& s1, const std::string& s2) {
try {
int len1 = static_cast<int>(s1.size());
int len2 = static_cast<int>(s2.size());
std::vector<std::vector<int>> dp(len1 + 1, std::vector<int>(len2 + 1));
for (int i = 0; i <= len1; i++) dp[i][0] = i;
for (int j = 0; j <= len2; j++) dp[0][j] = j;
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (s1[i - 1] == s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
else {
dp[i][j] = std::min({ dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1 });
}
}
}
return dp[len1][len2];
}
catch (const std::exception& e) {
return -1;
}
}
float ALPRChecker::computeIoU(const cv::Rect& a, const cv::Rect& b) {
int x1 = std::max(a.x, b.x);
int y1 = std::max(a.y, b.y);
int x2 = std::min(a.x + a.width, b.x + b.width);
int y2 = std::min(a.y + a.height, b.y + b.height);
int intersection = std::max(0, x2 - x1) * std::max(0, y2 - y1);
int unionArea = a.area() + b.area() - intersection;
return (unionArea > 0) ? static_cast<float>(intersection) / unionArea : 0.f;
}
std::string ALPRChecker::majorityVote(const std::deque<std::string>& history) {
if (history.empty()) return "";
// Count frequency of each plate text, weighted by recency
// (more recent entries get higher weight)
std::map<std::string, float> weightedFreq;
int n = static_cast<int>(history.size());
for (int i = 0; i < n; i++) {
// Recency weight: older=1.0, newest=2.0
float weight = 1.0f + static_cast<float>(i) / std::max(1, n - 1);
weightedFreq[history[i]] += weight;
}
// Also group similar plates (Levenshtein <= 1) under the most frequent variant
std::string bestText;
float bestScore = 0.f;
for (const auto& kv : weightedFreq) {
float totalScore = kv.second;
// Add scores from similar plates
for (const auto& other : weightedFreq) {
if (other.first == kv.first) continue;
// Use quick length check to skip obvious non-matches
int lenDiff = std::abs(static_cast<int>(kv.first.size()) - static_cast<int>(other.first.size()));
if (lenDiff <= 1) {
// Only compute Levenshtein for candidates within 1 char length difference
int dist = 0;
// Inline fast Levenshtein for short strings
const std::string& s1 = kv.first;
const std::string& s2 = other.first;
int len1 = static_cast<int>(s1.size()), len2 = static_cast<int>(s2.size());
if (len1 <= 15 && len2 <= 15) {
std::vector<int> prev(len2 + 1), curr(len2 + 1);
for (int j = 0; j <= len2; j++) prev[j] = j;
for (int i = 1; i <= len1; i++) {
curr[0] = i;
for (int j = 1; j <= len2; j++) {
if (s1[i - 1] == s2[j - 1]) curr[j] = prev[j - 1];
else curr[j] = 1 + std::min({ prev[j], curr[j - 1], prev[j - 1] });
}
std::swap(prev, curr);
}
dist = prev[len2];
}
if (dist == 1) {
totalScore += other.second * 0.5f; // partial credit for 1-edit variants
}
}
}
if (totalScore > bestScore) {
bestScore = totalScore;
bestText = kv.first;
}
}
return bestText;
}
// Original API — backward compatible, no spatial tracking
std::string ALPRChecker::checkPlate(const std::string& cameraId, const std::string& detectedPlate) {
// Delegate to spatial version with empty box (disables IoU matching)
return checkPlate(cameraId, detectedPlate, cv::Rect());
}
// Enhanced API with bounding box for spatial plate tracking
std::string ALPRChecker::checkPlate(const std::string& cameraId, const std::string& detectedPlate, const cv::Rect& plateBox) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
if (detectedPlate.empty()) return detectedPlate;
auto& plates = trackedPlates[cameraId];
bool hasSpatial = (plateBox.width > 0 && plateBox.height > 0);
// Increment framesSinceLastSeen for all plates (will be reset for matched plate)
for (auto& p : plates) {
p.framesSinceLastSeen++;
}
// Periodic pruning: remove plates not seen for a long time
_pruneCounter++;
if (_pruneCounter >= 30 && plates.size() > 10) {
_pruneCounter = 0;
int staleThreshold = maxFrames * 3; // 180 frames = ~6 seconds at 30fps
plates.erase(
std::remove_if(plates.begin(), plates.end(),
[staleThreshold](const TrackedPlate& p) {
return p.framesSinceLastSeen > staleThreshold;
}),
plates.end());
}
// Step 1: Find matching tracked plate
// Strategy: try IoU first, then text similarity as fallback
int bestIdx = -1;
float bestIoU = 0.f;
// 1a. Try spatial IoU matching
if (hasSpatial) {
for (int i = 0; i < static_cast<int>(plates.size()); i++) {
float iou = computeIoU(plateBox, plates[i].lastBox);
if (iou > iouMatchThreshold && iou > bestIoU) {
bestIoU = iou;
bestIdx = i;
}
}
}
// 1b. If IoU failed, try text similarity against locked texts
if (bestIdx < 0) {
for (int i = 0; i < static_cast<int>(plates.size()); i++) {
if (!plates[i].lockedText.empty()) {
int dist = levenshteinDistance(detectedPlate, plates[i].lockedText);
if (dist <= 1) {
bestIdx = i;
break;
}
}
// Also check against recent history even if not locked
if (bestIdx < 0 && !plates[i].textHistory.empty()) {
int checkCount = std::min(static_cast<int>(plates[i].textHistory.size()), 3);
for (int j = static_cast<int>(plates[i].textHistory.size()) - 1;
j >= static_cast<int>(plates[i].textHistory.size()) - checkCount; j--) {
int dist = levenshteinDistance(detectedPlate, plates[i].textHistory[j]);
if (dist <= 1) {
bestIdx = i;
break;
}
}
}
}
}
// Step 2: Create new tracked plate if no match found
if (bestIdx < 0) {
// Before creating a new plate, check if this detection conflicts with
// a nearby locked plate — if so, it's likely a bad OCR read and we should
// return the locked text instead of creating a garbage entry
if (hasSpatial) {
float bestProximity = 0.f;
int proximityIdx = -1;
for (int i = 0; i < static_cast<int>(plates.size()); i++) {
if (plates[i].lockedText.empty()) continue;
if (plates[i].lastBox.width <= 0 || plates[i].lastBox.height <= 0) continue;
float cx1 = plateBox.x + plateBox.width * 0.5f;
float cy1 = plateBox.y + plateBox.height * 0.5f;
float cx2 = plates[i].lastBox.x + plates[i].lastBox.width * 0.5f;
float cy2 = plates[i].lastBox.y + plates[i].lastBox.height * 0.5f;
float dx = std::abs(cx1 - cx2);
float dy = std::abs(cy1 - cy2);
float maxDim = std::max(static_cast<float>(plateBox.width), static_cast<float>(plates[i].lastBox.width));
if (dx < maxDim * 2.0f && dy < maxDim * 1.5f) {
float proximity = 1.0f / (1.0f + dx + dy);
if (proximity > bestProximity) {
bestProximity = proximity;
proximityIdx = i;
}
}
}
if (proximityIdx >= 0) {
auto& tp = plates[proximityIdx];
tp.lastBox = plateBox;
tp.framesSinceLastSeen = 0;
tp.textHistory.push_back(detectedPlate);
return tp.lockedText;
}
}
TrackedPlate newPlate;
newPlate.lastBox = plateBox;
newPlate.textHistory.push_back(detectedPlate);
plates.push_back(newPlate);
return detectedPlate;
}
// Step 3: Update matched tracked plate
auto& tp = plates[bestIdx];
if (hasSpatial) tp.lastBox = plateBox;
tp.framesSinceLastSeen = 0; // Reset — this plate was just seen
// Store RAW detection (not corrected — avoids feedback loop)
tp.textHistory.push_back(detectedPlate);
if (static_cast<int>(tp.textHistory.size()) > maxFrames) {
tp.textHistory.pop_front();
}
// Step 4: Majority vote to find stable text
std::string voted = majorityVote(tp.textHistory);
// Step 5: Lock logic — once we have enough consistent reads, lock the text
if (tp.lockedText.empty()) {
int matchCount = 0;
int lookback = std::min(static_cast<int>(tp.textHistory.size()), maxFrames);
for (int i = static_cast<int>(tp.textHistory.size()) - 1;
i >= static_cast<int>(tp.textHistory.size()) - lookback; i--) {
if (tp.textHistory[i] == voted) matchCount++;
}
if (matchCount >= minVotesToStabilize) {
tp.lockedText = voted;
tp.lockCount = 1;
}
return voted;
}
else {
int dist = levenshteinDistance(detectedPlate, tp.lockedText);
if (dist == 0) {
tp.lockCount++;
return tp.lockedText;
}
else {
int newDist = levenshteinDistance(voted, tp.lockedText);
if (newDist > 1) {
int newMatchCount = 0;
int recent = std::min(static_cast<int>(tp.textHistory.size()), minVotesToStabilize * 2);
for (int i = static_cast<int>(tp.textHistory.size()) - 1;
i >= static_cast<int>(tp.textHistory.size()) - recent; i--) {
if (tp.textHistory[i] == voted) newMatchCount++;
}
if (newMatchCount >= minVotesToStabilize) {
tp.lockedText = voted;
tp.lockCount = 1;
return tp.lockedText;
}
}
return tp.lockedText;
}
}
}
catch (const std::exception& e) {
return detectedPlate;
}
}
//
static void VerifyGlobalANSALPRLicense(const std::string& licenseKey) {
try {
ansalprLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1006, "ANSALPR");//Default productId=1006
if (!ansalprLicenceValid) { // we also support ANSTS license
ansalprLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS)
}
}
catch (std::exception& e) {
ansalprLicenceValid = false;
}
}
void ANSALPR::CheckLicense() {
try {
// Check once globally
std::call_once(ansalprLicenseOnceFlag, [this]() {
VerifyGlobalANSALPRLicense(_licenseKey);
});
// Update this instance's local license flag
_licenseValid = ansalprLicenceValid;
}
catch (const std::exception& e) {
this->_logger.LogFatal("ANSODBase::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
}
}
bool ANSALPR::LoadEngine() {
return true;
}
bool ANSALPR::Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colorThreshold) {
try {
ANSCENTER::ANSLibsLoader::Initialize();
_licenseKey = licenseKey;
_licenseValid = false;
_detectorThreshold = detectorThreshold;
_ocrThreshold = ocrThreshold;
_colorThreshold = colorThreshold;
ANSALPR::CheckLicense();
if (!_licenseValid) {
this->_logger.LogError("ANSALPR::Initialize.", "License is not valid.", __FILE__, __LINE__);
return false;
}
// Extract model folder
// 0. Check if the modelZipFilePath exist?
if (!FileExist(modelZipFilePath)) {
this->_logger.LogFatal("ANSALPR::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
}
// 1. Unzip model zip file to a special location with folder name as model file (and version)
std::string outputFolder;
std::vector<std::string> passwordArray;
if (!modelZipPassword.empty()) passwordArray.push_back(modelZipPassword);
passwordArray.push_back("AnsDemoModels20@!");
passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
std::string modelName = GetFileNameWithoutExtension(modelZipFilePath);
//this->_logger.LogDebug("ANSFDBase::Initialize. Model name", modelName);
size_t vectorSize = passwordArray.size();
for (size_t i = 0; i < vectorSize; i++) {
if (ExtractPasswordProtectedZip(modelZipFilePath, passwordArray[i], modelName, _modelFolder, false))
break; // Break the loop when the condition is met.
}
// 2. Check if the outputFolder exist
if (!FolderExist(_modelFolder)) {
this->_logger.LogError("ANSFDBase::Initialize. Output model folder is not exist", _modelFolder, __FILE__, __LINE__);
return false; // That means the model file is not exist or the password is not correct
}
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSALPR::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
ANSALPR::~ANSALPR(){};
bool ANSALPR::Destroy() { return true; };
std::vector<cv::Rect> ANSCENTER::ANSALPR::GetBoundingBoxes(const std::string& strBBoxes) {
std::vector<cv::Rect> bBoxes;
bBoxes.clear();
std::stringstream ss;
ss << strBBoxes;
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
BOOST_FOREACH(const boost::property_tree::ptree::value_type & child, pt.get_child("results"))
{
const boost::property_tree::ptree& result = child.second;
const auto x = GetData<float>(result, "x");
const auto y = GetData<float>(result, "y");
const auto width = GetData<float>(result, "width");
const auto height = GetData<float>(result, "height");
cv::Rect rectTemp;
rectTemp.x = x;
rectTemp.y = y;
rectTemp.width = width;
rectTemp.height = height;
bBoxes.push_back(rectTemp);
}
return bBoxes;
}
std::string ANSCENTER::ANSALPR::PolygonToString(const std::vector<cv::Point2f>& polygon) {
if (polygon.empty()) {
return "";
}
std::string result;
result.reserve(polygon.size() * 20);
char buffer[64];
for (size_t i = 0; i < polygon.size(); ++i) {
if (i > 0) {
snprintf(buffer, sizeof(buffer), ";%.3f;%.3f", polygon[i].x, polygon[i].y);
}
else {
snprintf(buffer, sizeof(buffer), "%.3f;%.3f", polygon[i].x, polygon[i].y);
}
result += buffer;
}
return result;
}
std::string ANSALPR::VectorDetectionToJsonString(const std::vector<Object>& dets) {
if (dets.empty()) {
return R"({"results":[]})";
}
try {
nlohmann::json root;
auto& results = root["results"] = nlohmann::json::array();
for (const auto& det : dets) {
results.push_back({
{"class_id", std::to_string(det.classId)},
{"track_id", std::to_string(det.trackId)},
{"class_name", det.className},
{"prob", std::to_string(det.confidence)},
{"x", std::to_string(det.box.x)},
{"y", std::to_string(det.box.y)},
{"width", std::to_string(det.box.width)},
{"height", std::to_string(det.box.height)},
{"mask", ""}, // TODO: convert masks to comma separated string
{"extra_info", det.extraInfo},
{"camera_id", det.cameraId},
{"polygon", PolygonToString(det.polygon)},
{"kps", KeypointsToString(det.kps)}
});
}
return root.dump();
}
catch (const std::exception& e) {
this->_logger.LogFatal("ANSALPR::VectorDetectionToJsonString", e.what(), __FILE__, __LINE__);
return R"({"results":[],"error":"Serialization failed"})";
}
}
void ANSALPR::SetPlateFormats(const std::vector<std::string>& formats) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
_plateFormats.clear();
_plateFormats = formats;
}
void ANSALPR::SetPlateFormat(const std::string& format) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
_plateFormats.clear();
_plateFormats.push_back(format);
}
std::vector<std::string> ANSALPR::GetPlateFormats() const {
return _plateFormats;
}
std::string ANSCENTER::ANSALPR::KeypointsToString(const std::vector<float>& kps) {
if (kps.empty()) {
return "";
}
std::string result;
result.reserve(kps.size() * 10);
char buffer[32];
for (size_t i = 0; i < kps.size(); ++i) {
if (i > 0) result += ';';
snprintf(buffer, sizeof(buffer), "%.3f", kps[i]);
result += buffer;
}
return result;
}
std::vector<cv::Point2f> ANSCENTER::ANSALPR::RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight) {
// Ensure imageWidth and imageHeight are non-zero to avoid division by zero
if (imageWidth <= 0 || imageHeight <= 0) {
std::vector<cv::Point2f> emptyPolygon;
return emptyPolygon;
}
// Calculate normalized points for each corner of the rectangle
std::vector<cv::Point2f> polygon = {
{ rect.x / imageWidth, rect.y / imageHeight }, // Top-left
{ (rect.x + rect.width) / imageWidth, rect.y / imageHeight }, // Top-right
{ (rect.x + rect.width) / imageWidth, (rect.y + rect.height) / imageHeight }, // Bottom-right
{ rect.x / imageWidth, (rect.y + rect.height) / imageHeight } // Bottom-left
};
return polygon;
}
// Functions for screen size division
double ANSALPR::calculateDistanceToCenter(const cv::Point& center, const cv::Rect& rect) {
cv::Point rectCenter(rect.x + rect.width / 2, rect.y + rect.height / 2);
return std::sqrt(std::pow(rectCenter.x - center.x, 2) + std::pow(rectCenter.y - center.y, 2));
}
std::vector<ANSALPR::ImageSection> ANSALPR::divideImage(const cv::Mat& image) {
if (image.empty()) {
std::cerr << "Error: Empty image!" << std::endl;
return cachedSections;
}
cv::Size currentSize(image.cols, image.rows);
// Check if the image size has changed
if (currentSize == previousImageSize) {
return cachedSections; // Return cached sections if size is the same
}
// Update previous size
previousImageSize = currentSize;
cachedSections.clear();
int width = image.cols;
int height = image.rows;
int maxDimension = std::max(width, height);
int numSections = 10;// std::max(1, numSections); // Ensure at least 1 section
if (maxDimension <= 2560)numSections = 8;
if (maxDimension <= 1280)numSections = 6;
if (maxDimension <= 960)numSections = 4;
if (maxDimension <= 640)numSections = 2;
if (maxDimension <= 320)numSections = 1;
int gridRows = std::sqrt(numSections);
int gridCols = (numSections + gridRows - 1) / gridRows; // Ensure all sections are covered
int sectionWidth = width / gridCols;
int sectionHeight = height / gridRows;
cv::Point imageCenter(width / 2, height / 2);
std::vector<std::pair<double, ImageSection>> distancePriorityList;
// Create sections and store their distance from the center
for (int r = 0; r < gridRows; ++r) {
for (int c = 0; c < gridCols; ++c) {
int x = c * sectionWidth;
int y = r * sectionHeight;
int w = (c == gridCols - 1) ? width - x : sectionWidth;
int h = (r == gridRows - 1) ? height - y : sectionHeight;
ImageSection section(cv::Rect(x, y, w, h));
double distance = calculateDistanceToCenter(imageCenter, section.region);
distancePriorityList.emplace_back(distance, section);
}
}
// Sort sections based on distance from center, then top-to-bottom, then left-to-right
std::sort(distancePriorityList.begin(), distancePriorityList.end(),
[](const std::pair<double, ImageSection>& a, const std::pair<double, ImageSection>& b) {
if (std::abs(a.first - b.first) > 1e-5) {
return a.first < b.first; // Sort by closest distance to center
}
// If distance is the same, prioritize top to bottom, then left to right
return a.second.region.y == b.second.region.y
? a.second.region.x < b.second.region.x
: a.second.region.y < b.second.region.y;
});
// Assign priority
int priority = 1;
for (auto& entry : distancePriorityList) {
entry.second.priority = priority++;
cachedSections.push_back(entry.second);
}
return cachedSections;
}
std::vector<ANSALPR::ImageSection> ANSALPR::createSlideScreens(const cv::Mat& image) {
if (image.empty()) {
std::cerr << "Error: Empty image!" << std::endl;
return cachedSections;
}
cv::Size currentSize(image.cols, image.rows);
if (currentSize == previousImageSize) {
return cachedSections;
}
previousImageSize = currentSize;
cachedSections.clear();
int maxSize = std::max(image.cols, image.rows);
const int minCellSize = 320;
int maxSections = 10;
const float minAspectRatio = 0.8f;
const float maxAspectRatio = 1.2f;
if (maxSize <= 640) maxSections = 1;
else if (maxSize <= 960) maxSections = 2;
else if (maxSize <= 1280) maxSections = 4;
else if (maxSize <= 2560) maxSections = 6;
else if (maxSize <= 3840) maxSections = 8;
else maxSections = 10;
int width = image.cols;
int height = image.rows;
int bestRows = 1, bestCols = 1;
int bestTileSize = std::numeric_limits<int>::max();
for (int rows = 1; rows <= maxSections; ++rows) {
for (int cols = 1; cols <= maxSections; ++cols) {
if (rows * cols > maxSections) continue;
int tileWidth = (width + cols - 1) / cols;
int tileHeight = (height + rows - 1) / rows;
if (tileWidth < minCellSize || tileHeight < minCellSize)
continue;
float aspectRatio = static_cast<float>(tileWidth) / static_cast<float>(tileHeight);
if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio)
continue;
int maxTileDim = std::max(tileWidth, tileHeight);
if (maxTileDim < bestTileSize) {
bestTileSize = maxTileDim;
bestRows = rows;
bestCols = cols;
}
}
}
// Generate tiles using bestRows, bestCols
int tileWidth = (width + bestCols - 1) / bestCols;
int tileHeight = (height + bestRows - 1) / bestRows;
int priority = 1;
for (int r = bestRows - 1; r >= 0; --r) {
for (int c = 0; c < bestCols; ++c) {
int x = c * tileWidth;
int y = r * tileHeight;
int w = std::min(tileWidth, width - x);
int h = std::min(tileHeight, height - y);
if (w <= 0 || h <= 0) continue;
cv::Rect region(x, y, w, h);
ImageSection section(region);
section.priority = priority++;
cachedSections.push_back(section);
}
}
return cachedSections;
}
int ANSALPR::getHighestPriorityRegion() {
if (!cachedSections.empty()) {
return cachedSections.front().priority; // First element has the highest priority
}
return 0; // Return empty rect if no sections exist
}
int ANSALPR::getLowestPriorityRegion() {
if (!cachedSections.empty()) {
return cachedSections.back().priority; // Last element has the lowest priority
}
return 0; // Return empty rect if no sections exist
}
cv::Rect ANSALPR::getRegionByPriority(int priority) {
for (const auto& section : cachedSections) {
if (section.priority == priority) {
return section.region;
}
}
return cv::Rect(); // Return empty rect if priority not found
}
std::vector<Object> ANSALPR::AdjustLicensePlateBoundingBoxes(
const std::vector<Object>& detectionsInROI,
const cv::Rect& roi,
const cv::Size& fullImageSize,
float aspectRatio,
int padding
) {
std::vector<Object> adjustedDetections;
try {
// Basic input validation
if (detectionsInROI.empty()) {
return adjustedDetections;
}
if (roi.width <= 0 || roi.height <= 0 ||
fullImageSize.width <= 0 || fullImageSize.height <= 0) {
return adjustedDetections;
}
for (const auto& detInROI : detectionsInROI) {
try {
if (detInROI.box.width <= 0 || detInROI.box.height <= 0)
continue; // Skip invalid box
// Convert ROI-relative box to full-image coordinates
cv::Rect detInFullImg;
try {
detInFullImg = detInROI.box + roi.tl();
detInFullImg &= cv::Rect(0, 0, fullImageSize.width, fullImageSize.height);
}
catch (const std::exception& e) {
std::cerr << "[AdjustBBox] Failed to calculate full image box: " << e.what() << std::endl;
continue;
}
// Check if it touches ROI border
bool touchesLeft = detInROI.box.x <= 0;
bool touchesRight = detInROI.box.x + detInROI.box.width >= roi.width - 1;
bool touchesTop = detInROI.box.y <= 0;
bool touchesBottom = detInROI.box.y + detInROI.box.height >= roi.height - 1;
bool touchesBorder = touchesLeft || touchesRight || touchesTop || touchesBottom;
// Compute target width based on aspect ratio
int targetWidth = 0, expandWidth = 0;
try {
targetWidth = std::max(detInFullImg.width, static_cast<int>(detInFullImg.height * aspectRatio));
expandWidth = std::max(0, targetWidth - detInFullImg.width);
}
catch (const std::exception& e) {
std::cerr << "[AdjustBBox] Aspect ratio adjustment failed: " << e.what() << std::endl;
continue;
}
int expandLeft = expandWidth / 2;
int expandRight = expandWidth - expandLeft;
int padX = touchesBorder ? padding : 0;
int padY = touchesBorder ? (padding / 2) : 0;
// Apply padded and expanded box
int newX = std::max(0, detInFullImg.x - expandLeft - padX);
int newY = std::max(0, detInFullImg.y - padY);
int newWidth = detInFullImg.width + expandWidth + 2 * padX;
int newHeight = detInFullImg.height + 2 * padY;
// Clamp to image boundaries
if (newX + newWidth > fullImageSize.width) {
newWidth = fullImageSize.width - newX;
}
if (newY + newHeight > fullImageSize.height) {
newHeight = fullImageSize.height - newY;
}
if (newWidth <= 0 || newHeight <= 0)
continue;
// Construct adjusted object
Object adjustedDet = detInROI;
adjustedDet.box = cv::Rect(newX, newY, newWidth, newHeight);
adjustedDetections.push_back(adjustedDet);
}
catch (const std::exception& e) {
std::cerr << "[AdjustBBox] Exception per detection: " << e.what() << std::endl;
continue;
}
catch (...) {
std::cerr << "[AdjustBBox] Unknown exception per detection." << std::endl;
continue;
}
}
}
catch (const std::exception& e) {
std::cerr << "[AdjustBBox] Fatal error: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "[AdjustBBox] Unknown fatal error occurred." << std::endl;
}
return adjustedDetections;
}
}

150
modules/ANSLPR/ANSLPR.h Normal file
View File

@@ -0,0 +1,150 @@
#ifndef ANSLPR_H
#define ANSLPR_H
#define ANSLPR_API __declspec(dllexport)
#pragma once
#include <iostream>
#include <cassert>
#include <fstream>
#include <string>
#include <filesystem>
#include <opencv2/opencv.hpp>
#include "Utility.h"
#include "ANSLicense.h"
#include "LabVIEWHeader/extcode.h"
#include "ANSEngineCommon.h"
#define MAX_ALPR_FRAME 60
namespace ANSCENTER
{
enum Country {
VIETNAM = 0,
CHINA = 1,
AUSTRALIA = 2,
USA = 3,
INDONESIA = 4
};
class ALPRChecker {
private:
int maxFrames;
int minVotesToStabilize = 3; // need at least 3 identical reads to lock
float iouMatchThreshold = 0.3f; // IoU to match same physical plate across frames
std::recursive_mutex _mutex;
// Per-plate spatial tracking: each tracked plate has a location + text history
struct TrackedPlate {
cv::Rect lastBox;
std::deque<std::string> textHistory; // rolling history of OCR reads
std::string lockedText; // majority-vote winner (empty = not locked yet)
int lockCount = 0; // how many consecutive frames the lock held
int framesSinceLastSeen = 0; // how many checkPlate calls since this plate was last matched
};
int _pruneCounter = 0; // counts checkPlate calls for periodic pruning
// cameraId -> list of tracked plates
std::unordered_map<std::string, std::vector<TrackedPlate>> trackedPlates;
[[nodiscard]] int levenshteinDistance(const std::string& s1, const std::string& s2);
[[nodiscard]] float computeIoU(const cv::Rect& a, const cv::Rect& b);
[[nodiscard]] std::string majorityVote(const std::deque<std::string>& history);
public:
void Init(int framesToStore = MAX_ALPR_FRAME);
ALPRChecker(int framesToStore = MAX_ALPR_FRAME) : maxFrames(framesToStore) {}
// Original API (backward compatible) — no spatial tracking
[[nodiscard]] std::string checkPlate(const std::string& cameraId, const std::string& detectedPlate);
// Enhanced API with bounding box for spatial plate tracking
[[nodiscard]] std::string checkPlate(const std::string& cameraId, const std::string& detectedPlate, const cv::Rect& plateBox);
};
class ANSLPR_API ANSALPR {
protected:
SPDLogger& _logger = SPDLogger::GetInstance("ALPR", false);
bool _debugFlag{ false };
bool _licenseValid{ false };
bool _isInitialized{ false};
double _detectorThreshold{ 0.5 };
double _ocrThreshold{ 0.5 };
double _colorThreshold{ 0.0 };// If we use the colour information, this threshold should be greater than 0.0
std::vector<std::string> _plateFormats;
std::string _licenseKey{};
std::string _modelFolder;
struct ImageSection {
cv::Rect region;
int priority;
ImageSection(const cv::Rect& r) : region(r), priority(0) {}
};
cv::Size previousImageSize = cv::Size(0, 0);
std::vector<ImageSection> cachedSections;
int _currentPriority{ 0 }; // None
cv::Rect _detectedArea;// Area where license plate are detected
Country _country;
std::recursive_mutex _mutex;
public:
[[nodiscard]] virtual bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colourTheshold=0);
[[nodiscard]] virtual bool LoadEngine();
[[nodiscard]] virtual bool Inference(const cv::Mat& input, std::string& lprResult) = 0;
[[nodiscard]] virtual bool Inference(const cv::Mat& input, std::string& lprResult,const std::string &cameraId) = 0;
[[nodiscard]] virtual bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult) = 0;
[[nodiscard]] virtual bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult,const std::string & cameraId) = 0;
[[nodiscard]] virtual std::vector<Object> RunInference(const cv::Mat& input, const std::string &cameraId) = 0;
[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<Object>& dets);
void SetPlateFormats(const std::vector<std::string>& formats);
void SetPlateFormat(const std::string& format);
[[nodiscard]] std::vector<std::string> GetPlateFormats() const;
virtual ~ANSALPR();
void CheckLicense();
/// Enable/disable deep pipeline benchmarking.
/// When enabled, logs per-stage timing for the full ALPR pipeline
/// (LP detection, OCR, color classification, validation, serialization).
/// Also propagates the flag to sub-detectors (_lpDetector, _ocrDetector, etc.).
virtual void ActivateDebugger(bool debugFlag) { _debugFlag = debugFlag; }
[[nodiscard]] virtual bool Destroy() = 0;
[[nodiscard]] static std::vector<cv::Rect> GetBoundingBoxes(const std::string& strBBoxes);
[[nodiscard]] static std::string PolygonToString(const std::vector<cv::Point2f>& polygon);
[[nodiscard]] static std::string KeypointsToString(const std::vector<float>& kps);
[[nodiscard]] static std::vector<cv::Point2f> RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight);
protected:
// Utilities for scanning license plate
// Function to seperate screen size
[[nodiscard]] double calculateDistanceToCenter(const cv::Point& center, const cv::Rect& rect);
[[nodiscard]] std::vector<ImageSection> divideImage(const cv::Mat& image);
[[nodiscard]] std::vector<ANSALPR::ImageSection> createSlideScreens(const cv::Mat& image);
[[nodiscard]] int getHighestPriorityRegion();
[[nodiscard]] int getLowestPriorityRegion();
[[nodiscard]] cv::Rect getRegionByPriority(int priority);
[[nodiscard]] std::vector<Object> AdjustLicensePlateBoundingBoxes(const std::vector<Object>& detectionsInROI,
const cv::Rect& roi, const cv::Size& fullImageSize,
float aspectRatio = 2.0f, // width at least 2x height
int padding = 10 // base padding
);
};
}
extern "C" ANSLPR_API int CreateANSALPRHandle(ANSCENTER::ANSALPR **Handle, const char* licenseKey, const char* modelZipFilePath, const char* modelZipPassword, int engineType, double detectorThreshold, double ocrThreshold, double colourThreshold);
extern "C" ANSLPR_API int LoadANSALPREngineHandle(ANSCENTER::ANSALPR** Handle);
extern "C" ANSLPR_API std::string ANSALPR_RunInference(ANSCENTER::ANSALPR **Handle, unsigned char* jpeg_string, unsigned int bufferLength);
extern "C" ANSLPR_API std::string ANSALPR_RunInferenceWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId);
extern "C" ANSLPR_API std::string ANSALPR_RunInferenceBinary(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height);
extern "C" ANSLPR_API std::string ANSALPR_RunInferenceBinaryInCroppedImages(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, const char* strBboxes);
extern "C" ANSLPR_API std::string ANSALPR_RunInferenceInCroppedImages(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes);
extern "C" ANSLPR_API std::string ANSALPR_RunInferenceInCroppedImagesWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId);
extern "C" ANSLPR_API int ReleaseANSALPRHandle(ANSCENTER::ANSALPR **Handle);
//// For LabVIEW API
extern "C" ANSLPR_API int ANSALPR_RunInference_LV(ANSCENTER::ANSALPR **Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult);
extern "C" ANSLPR_API int ANSALPR_RunInferenceWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId, LStrHandle detectionResult);
extern "C" ANSLPR_API int ANSALPR_RunInferenceBinary_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult);
extern "C" ANSLPR_API int ANSALPR_RunInferenceInCroppedImages_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, LStrHandle detectionResult);
extern "C" ANSLPR_API int ANSALPR_RunInferenceInCroppedImagesWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult);
extern "C" ANSLPR_API int ANSALPR_RunInferenceComplete_LV(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr);
extern "C" ANSLPR_API int ANSALPR_RunInferenceComplete_CPP(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize,std::string& detectionResult, std::string& imageStr);
extern "C" ANSLPR_API int ANSALPR_RunInferencesComplete_LV(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int maxImageSize,const char* strBboxes, LStrHandle detectionResult);
// Get/Set format
extern "C" ANSLPR_API int ANSALPR_SetFormat(ANSCENTER::ANSALPR** Handle, const char* format);
extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats);// comma separated formats
extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle formats);// comma separated formats
#endif

File diff suppressed because it is too large Load Diff

188
modules/ANSLPR/ANSLPR_CPU.h Normal file
View File

@@ -0,0 +1,188 @@
#ifndef ANSLPRCPU_H
#define ANSLPRCPU_H
#pragma once
#include "ANSLPR.h"
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <include/paddleocr.h>
#include "openvino/openvino.hpp"
#include "openvino/runtime/intel_gpu/properties.hpp"
namespace ANSCENTER
{
class ANSOVDetector { // OpenVINO Yolov8 detector
public:
[[nodiscard]] bool Initialize(const std::string& modelFilePath, const std::string& classFilePath, std::vector <std::string>& _classes);
[[nodiscard]] std::vector<Object> RunInference(const cv::Mat& input);
private:
std::string _modelFilePath;
std::string _classFilePath;
float _detectionScoreThreshold{ 0.5 };
float _modelConfThreshold{ 0.48 };
float _modelMNSThreshold{ 0.5 };
bool _isInitialized{ false };
private:
void PreprocessImage(cv::Mat& frame);
[[nodiscard]] cv::Rect GetBoundingBox(const cv::Rect& src);
[[nodiscard]] std::vector<Object>PostProcessing();
[[nodiscard]] std::vector <std::string> LoadClassesFromFile();
cv::Mat resized_frame_;
cv::Point2f factor_;
cv::Size2f model_input_shape_;
cv::Size model_output_shape_;
ov::Tensor input_tensor_;
ov::InferRequest inference_request_;
ov::CompiledModel compiled_model_;
ov::Core _core;
[[nodiscard]] std::string GetOpenVINODevice();
std::recursive_mutex _mutex;
};
class ANSLPR_API ANSALPR_CPU :public ANSALPR {
private:
ANSCENTER::EngineType engineType;
ANSCENTER::ModelConfig _lpdmodelConfig;
ANSCENTER::ModelConfig _ocrModelConfig;
ANSCENTER::ModelConfig _lpColourModelConfig;
std::string _lpdLabels;
std::string _ocrLabels;
std::string _lpColourLabels;
cv::Mat _frameBuffer; // Reusable buffer for color conversion
std::string runOCROnPlate(const cv::Mat& frame, const cv::Rect& plateRect);
std::unique_ptr<ANSCENTER::ANSODBase> _lprDetector = nullptr;
//std::unique_ptr<ANSCENTER::ANSODBase> _lpColourDetector = nullptr; // License plate colour classifier
std::vector <std::string> _lprModelClass; //model that recognise license plate
Country _country;
std::recursive_mutex _mutex;
std::vector<std::string> ValidVNMotobikeList = { "29BA", "29BB", "29BC", "29BD", "29BE",
"29BF", "29BG", "29BH", "29BK", "29BL",
"29BM", "29BN", "29BP", "29AA", "29AD",
"29AE", "29AF", "29AG", "29AH", "29AK",
"29AL", "29AM", "29AN", "29AP", "29AS",
"29AT", "29AU", "29AV", "29AX", "29AY","29AZ","29AB", "29AC"//HN
"59AA", "59TA", "59FA", "59CA", "59HA",
"59KA", "59CB", "59LA", "59UA", "59MA",
"59GA", "59DB", "59PA", "59NA", "59EA",
"59SA", "59VA", "59YA", "59YB", "59ZA",
"59ZB", "59NB", "59XB", "59BA", "59XA",
"59XB", "59FA", //HCM
"43AA", "43AC", "43AD", "43AE", "43AF", "43AG", "43AB",// Da Nang
"67AA", "67AD", "67AE", "67AH", "67AB",
"67AF", "67AK", "67AL", "67AM", "67AN",// An Giang
"94AB", "94AE", "94AF", "94AH", "94AK","94FD","94FE",// Bac Lieu
"98AA", "98AH", "98AB", "98AC", "98AD",
"98AE", "98AF", "98AG", "98AK", "98AL",// Bac Giang
"71AA", "71AF", "71AG", "71AH", "71AK",
"71AB", "71AC", "71AD", "71AE", // Ben Tre
"77AA", "77AE", "77AB", "77AH",
"77AK", "77AC", "77AD", "77AF",
"77AG", "77AN", "77AM", // Binh Dinh
"69AA", "69AB", "69AC", "69AD", "69AE",
"69AF", "69KA", "69AK", "69AL", "69AM", // Ca Mau
"65MA", "65AA", "65CA", "65DB", "65EA",
"65FA", "65GA", "65HA", "65KA", // Can Tho
"76AA", "76AU", "76AV", "76AH", "76AB",
"76AC", "76AD", "76AE", "76AF", "76AK",
"76AL", "76AM", "76AN", "76AP", "76AS","76AT",// Quang Ngai
"84HA", "66PA", "84AE", "88AB", "68MA",
"63AB", "84AF", "66HA", "60AD", "47AG", "74AE",
"84AH", "76AA", "21BA", "68HA", "68AA", "28FN",
"49AL", "12HA", "66MA", "15AA", "15AC", "83VK",
"95EA", "64AA", "12FA", "21AA", "83YM", "38AG",
"68TA", "63AE", "79NA", "12ZA", "20AC", "64EA",
"66VA", "83CA", "21FA", "68KB", "74AB", "63AK",
"23AB", "47AK", "60AM", "23AE", "74AK", "84CA",
"47AV", "26AA", "28FC", "21LA", "76AS", "68FA",
"21GA", "47AH", "47AU", "23AC", "63AN", "21KA",
"49AD", "74AH", "20AK", "26AC", "74AF", "76AC",
"68NA", "60AE", "22LA", "12XA", "15AH", "26AD",
"23AA", "83EA", "47AA", "38AA", "26AB", "60AB",
"60AF", "38AE", "28FD", "66BA", "68CA", "20AD",
"21EA", "38AS", "60AA", "88AC", "64BA", "22AB",
"49AG", "38AK", "47AS", "64AC", "47AD", "68SA",
"12BA", "15AE", "28FM", "26AM", "79ZA", "66FA",
"63AH", "64DA", "47AM", "68UA", "26AG", "84AC",
"66SA", "76AD", "49AN", "26AF", "79XA", "28FS",
"49AA", "63AS", "84BA", "12SA", "83DB", "60AL",
"21HA", "38AF", "22AA", "63AF", "49AE", "47AT",
"23AD", "84AD", "23AH", "79CA", "60AH", "66GA",
"26AK", "21CA", "38AB", "28FE", "26AP", "47AP",
"47AE", "76AV", "49AC", "38AN", "12PA", "76AE",
"64FA", "12VA", "22FA", "79DA", "95NA", "76AP",
"66NA", "47AB", "74AG", "60AC", "68GA", "63AC",
"47AN", "22YA", "68PA", "47AC", "83GE", "26AL",
"64DB", "47AL", "83ZY", "60AN", "64GA", "84AL",
"49AK", "28FB", "20AE", "20AG", "76AU", "66KA",
"64CA", "76AK", "23AV", "38AX", "79HA", "47AX",
"49AM", "83PT", "15AF", "95AA", "38AM", "76AT",
"47AF", "74AC", "20AB", "64HA", "12UA", "22SA",
"68BA", "49AH", "23AK", "20AF", "63AD", "60AK",
"28FZ", "28FL", "76AB", "49AF", "26AH", "63AL",
"74AA", "79DB", "22NA", "23AU", "23AY", "83TG",
"63AM", "64KA", "23AM", "74AD", "79VA", "95XA",
"28FF", "68KA", "49AP", "83XL", "83MF", "64HB",
"15AN", "15AK", "76AF", "22TA", "12LA", "38AL",
"95HA", "68EA", "84AA", "66CA", "26AE", "66LA",
"76AN", "23AX", "95RA", "68B", "68LA", "20AH",
"20AA", "76AH", "38AC", "12TA", "76AM",
"59C3", "29S7", "29V7", "29L2", "72K1", "72D1", "98AF",
"59D2", "59S1", "59K2", "67K1", "59C4", "98AG", "59E2",
"67H2", "94L1", "29S1", "59E1", "59X4", "94B2", "67L1",
"29E1", "72C1", "29V3", "67M1", "29X7", "29X1", "98AD",
"59N3", "59C1", "59D3", "59P1", "67F1", "59M2", "59K1",
"98AB", "59T2", "72E1", "59V1", "29N2", "29E2", "67D2",
"72E2", "29T2", "29V5", "59L3", "29S2", "59L2", "72G1",
"59G2", "30Z1", "67N1", "98B1", "59U1", "59D1", "72C2",
"98B2", "98M1", "29P1", "67M2", "98H1", "29V1", "29K2",
"59V2", "59F2", "59N1", "29Z1", "98D1", "59C2", "94K1",
"67G1", "98AL", "29Y5", "59B1", "41B1", "94G1", "59G1",
"94M1", "59X2", "67C1", "67D1", "59X3", "67B1", "98AC",
"67C2", "98B3", "59Z1", "59Z2", "98AH", "59S3", "94F1",
"98L1", "59H2", "98D2", "29D1", "59M1", "29B2", "29D2",
"29U1", "59U2", "29H2", "29K1", "94B1", "98C1", "59F1",
"67H1", "29F1", "29Y1", "59P2", "67L2", "59X1", "98G1",
"29C1", "67B2", "59T1", "29G2", "29L5", "29S6", "29Y7",
"59Y2", "67E1", "98AK", "94C1", "29X2", "98K1", "98AE",
"98AA", "29H1", "29T1", "67K2", "94D2", "94E1", "29M1",
"29N1", "59Y1", "72H1", "29X3", "98F1", "29C2", "94K2",
"72F1", "29M2", "29Y3", "59P3", "29E3", "29G1", "59V3",
"59Y3", "29P2", "59N2", "29L1", "98E1", "59G3", "29X5",
"59S2", "94D1", "59H1", "29B1", "59L1", "50X1", "72L1",
"43B1", "68L1", "70G1", "36M1", "81N1", "90K1", "17B1", "64E1", "99D1", "60B2", "74L1", "60C1", "68M1", "63B7", "34B1", "69M1", "24B1", "15M1", "83Y1", "48C1", "95H1", "79X1", "17B6", "36E1", "38K1", "25N1", "25U1", "61B1", "36C1", "36B3", "38F1", "99G1", "69N1", "97D1", "92T1", "92B1", "88B1", "97G1", "14U1", "63A1", "26N1", "19D1", "93C1", "73B1", "84B1", "81K1", "18L1", "64D1", "35M1", "61N1", "83P1", "15S1", "82B1", "92U1", "43D1", "22L1", "63B5", "64G1", "27N1", "14X1", "62C1", "81D1", "38G1", "19F1", "34K1", "49P1", "89H1", "14T1", "19M1", "78D1", "76A1", "66K1", "66C1", "71C1", "37K1", "19G1", "15F1", "85C1", "49B1", "21B1", "89F1", "23M1", "66L1", "90B5", "93M1", "14P1", "77N1", "36B8", "86B1", "12U1", "63B3", "21L1", "36G5", "65G1", "82E1", "61H1", "65H1", "84A1", "23F1", "95C1", "99K1", "49G1", "92D1", "36K3", "92N1", "82X1", "83M1", "11N1", "14K1", "19H1", "93H1", "60A1", "79A1", "20D1", "90D1", "81C1", "66P1", "36K1", "92V1", "18B1", "37P1", "22Y1", "23H1", "26D1", "66G1", "78F1", "49C1", "26H1", "38P1", "47T1", "74H1", "63P1", "47D1", "15D1", "23D1", "68E1", "20B1", "49F1", "43K1", "65K1", "27Z1", "92S1", "79H1", "21E1", "35Y1", "14S1", "75E1", "24Y1", "12T1", "27P1", "77B1", "88H1", "60B3", "23P1", "61F1", "99H1", "23K1", "59A3", "26C1", "81B1", "74E1", "66B1", "22S1", "92P1", "93B1", "69B1", "81P1", "12H1", "62K1", "35A1", "77C1", "27V1", "68N1", "12D1", "64K1", "41A1", "12Z1", "76C1", "38B1", "78G1", "74K1", "69H1", "94A1", "61K1", "86B7", "82G1", "14N1", "82M1", "76E1", "18E1", "61C1", "15N1", "90A1", "77F1", "34D1", "47B1", "62S1", "43E1", "81M1", "92X1", "75B1", "34F1", "70H1", "62B1", "26B1", "60B4", "61A1", "12B1", "90T1", "92E1", "34C1", "47G1", "97B1", "25S1", "70E1", "93Y1", "47S1", "37F1", "28N1", "11K1", "38E1", "78M1", "74C1", "12S1", "75S1", "37A1", "28D1", "65L1", "22B1", "99B1", "74G1", "79K1", "76K1", "76H1", "23B1", "15R1", "36B1", "74D1", "62L1", "37E1", "78E1", "89K1", "26M1", "25F1", "48H1", "79D1", "43H1", "76F1", "36L1", "43L1", "21K1", "88L1", "27S1", "92K1", "77D1", "19N1", "66H1", "36H5", "62N1", "18G1", "75D1", "37L1", "68K1", "28C1", "26E1", "35N1", "85H1", "62D1", "27U1", "19E1", "99E1", "14Y1", "49L1", "66M1", "73F1", "70K1", "36F5", "97H1", "93E1", "68P1", "43F1", "48G1", "75K1", "62U1", "86B9", "65F1", "27L1", "70L1", "63B8", "78L1", "11Z1", "68C1", "18D1", "15L1", "99C1", "49E1", "84E1", "69E1", "38A1", "48D1", "68S1", "81E1", "84K1", "63B6", "24T1", "95A1", "86B4", "34M1", "84L1", "24V1", "14M1", "36H1", "15B1", "69F1", "47E1", "38H1", "88D1", "28E1", "60C2", "63B9", "75Y1", "21D1", "35H1", "68F1", "86B5", "15H1", "36B5", "83X1", "17B7", "12V1", "86B8", "95E1", "63B2", "74F1", "86C1", "48K1", "89M1", "85D1", "71C4", "34E1", "97C1", "88E1", "81F1", "60B5", "84M1", "92H1", "28L1", "34H1", "38X1", "82L1", "61E1", "82F1", "62P1", "93F1", "65B1", "93L1", "95B1", "15P1", "77G1", "28M1", "35B1", "68G1", "36C2", "68D1", "69K1", "14L1", "36M3", "24X1", "24Z1", "86A1", "88C1", "15E1", "77E1", "83E1", "47L1", "25T1", "89C1", "71C3", "49D1", "36L6", "48F1", "36B6", "34P1", "84D1", "15C1", "38M1", "85F1", "77K1", "86B3", "74B1", "78H1", "89G1", "64A2", "15K1", "85B1", "49K1", "21H1", "73C1", "47U1", "65E1", "18C1", "69D1", "63B1", "95G1", "19L1", "20G1", "76D1", "29A1", "68T1", "75L1", "12L1", "89L1", "37C1", "27B1", "19C1", "11H1", "81X1", "70B1", "11V1", "43G1", "22A1", "83C1", "75C1", "79C1", "22F1", "92F1", "81G1", "81T1", "28H1", "66N1", "71B1", "18H1", "76P1", "26F1", "81U1", "34N1", "64F1", "76N1", "24S1", "26P1", "63B4", "35T1", "36N1", "47F1", "81L1", "61G1", "77M1", "34G1", "26G1", "97F1", "62H1", "28F1", "62T1", "93G1", "73D1", "65A1", "47P1", "74P1", "82N1", "20E1", "36D1", "60B1", "49M1", "37H1", "37M1", "38D1", "84F1", "88F1", "36B2", "65C1", "92M1", "86B6", "75H1", "38L1", "20C1", "97E1", "85E1", "38N1", "26K1", "89B1", "99F1", "28B1", "34L1", "86B2", "66F1", "77L1", "27Y1", "68H1", "37D1", "92L1", "82K1", "99A1", "69L1", "76M1", "90B4", "48B1", "95D1", "20H1", "64H1", "79Z1", "92G1", "23G1", "21G1", "37G1", "35K1", "81H1", "83Z1", "76T1", "36F1", "36B4", "14B9", "47K1", "20K1", "62M1", "84H1", "62F1", "74A1", "18A1", "73H1", "37N1", "79N1", "61D1", "11P1", "15G1", "47N1", "19K1", "71C2", "81S1", "11M1", "60B7", "60B8", "62G1", "71A1", "24P1", "69A1", "38C1", "49N1", "21C1", "84G1", "37B1", "72A1", "88K1", "88G1", "83V1", "78C1", "73K1", "78K1", "73E189D1", "67A1", "27X1", "62A1", "18K1", "70F1", "36K5", "19B1", "49H1", "66S1", "12P1"};
ALPRChecker alprChecker;
std::vector<std::string> ValidVNCarList = { "94H", "49F", "93A", "20F", "81H", "95R", "38R", "29F", "81F", "28G", "19A", "85B", "2", "43H", "51L", "28C", "21A", "51D", "50F", "24H", "93R", "92H", "71G", "75H", "86G", "30L", "79A", "82B", "79H", "78C", "61E", "70A", "90C", "72G", "34B", "17E", "18E", "78A", "37F", "51E", "71A", "28F", "47E", "83D", "81B", "84C", "71H", "76G", "92E", "36A", "69R", "30M", "27R", "71D", "19B", "34E", "38K", "88G", "68G", "30E", "68E", "25F", "74D", "98K", "89H", "36R", "84D", "61F", "49G", "25H", "17F", "14R", "36H", "47G", "90A", "68A", "83C", "26B", "15B", "61C", "15K", "47H", "78E", "75D", "15C", "63E", "34C", "36F", "38G", "15E", "93F", "22G", "60B", "94D", "62R", "24D", "11R", "12A", "76A", "94C", "97R", "24E", "26A", "15F", "72A", "49H", "62D", "98C", "71B", "61A", "12C", "27A", "78R", "51M", "69E", "76D", "78F", "49R", "81A", "64F", "29D", "18A", "19F", "21E", "92A", "65G", "86E", "62G", "61K", "47A", "23R", "14F", "95D", "36B", "74R", "11H", "24C", "11G", "66D", "63A", "43R", "70F", "86B", "61G", "47M", "67C", "37D", "43G", "14H", "90F", "51G", "86A", "11E", "29K", "85C", "83F", "24B", "98R", "19E", "61B", "90D", "82G", "14K", "74G", "72D", "85A", "19C", "37G", "98E", "74F", "28H", "90E", "89D", "35R", "97H", "83H", "95A", "20C", "65E", "15R", "73C", "37A", "38E", "77G", "94B", "17A", "75R", "98F", "65R", "76R", "20B", "24G", "25B", "73G", "62F", "29G", "77C", "22H", "14D", "23F", "93C", "19R", "15D", "47R", "79D", "60G", "77A", "82C", "63G", "21H", "81E", "25D", "12D", "37R", "36K", "84F", "98G", "28B", "51N", "18F", "50R", "74C", "35C", "30G", "64A", "95F", "18C", "99G", "99B", "37C", "76H", "60K", "67R", "75A", "83R", "28E", "65F", "17D", "92G", "23C", "60R", "90R", "38A", "43D", "50H", "43C", "77H", "47B", "89F", "82F", "65H", "89E", "62C", "24R", "26G", "84E", "17C", "65B", "34A", "12B", "64R", "29H", "71C", "88D", "79F", "76C", "98A", "69H", "22B", "29A", "72R", "67H", "48C", "22D", "60C", "35H", "38H", "63P", "70D", "49D", "18H", "89A", "72E", "92D", "26H", "73R", "85G", "20E", "98H", "69C", "18B", "73B", "22E", "34G", "30K", "20D", "50A", "34D", "15H", "34H", "71E", "62E", "64C", "51R", "82D", "99E", "70R", "18D", "92F", "94R", "24A", "85H", "11C", "73E", "95E", "86C", "94F", "86R", "37K", "23B", "20H", "73D", "95H", "35A", "89B", "82H", "67F", "70H", "97F", "29E", "97A", "51K", "68D", "37B", "82E", "18R", "86H", "35B", "43E", "35F", "95B", "70E", "21D", "27F", "36E", "63D", "68C", "50E", "36G", "75F", "21G", "29B", "93B", "22A", "18G", "43F", "93G", "62A", "83B", "28D", "75C", "22C", "21R", "25E", "23G", "97C", "75E", "79E", "19H", "47K", "65C", "35E", "20R", "68B", "89R", "67A", "75G", "81R", "78B", "77D", "78G", "20K", "36D", "66C", "38F", "27G", "19D", "67B", "84G", "22F", "61D", "20G", "48A", "76F", "48H", "92B", "85R", "26C", "65A", "70B", "38D", "14C", "66A", "73A", "49C", "74E", "68R", "66B", "74A", "49E", "17B", "69D", "51C", "85F", "21F", "99C", "17G", "72H", "94E", "51F", "92R", "60H", "21B", "93D", "19G", "86F", "51A", "66R", "72B", "26D", "64E", "93H", "12H", "97E", "60E", "82A", "60A", "83E", "27D", "64B", "11B", "11D", "76B", "95G", "14A", "61R", "21C", "30F", "23H", "89C", "97G", "62B", "63R", "88B", "98B", "90B", "67G", "69F", "73H", "20A", "72C", "65D", "68H", "51H", "79G", "70C", "90G", "66G", "83A", "77F", "63B", "64G", "25A", "88E", "68F", "99D", "26E", "94A", "48F", "34R", "61H", "90H", "74B", "14G", "12F", "15A", "27E", "69A", "35D", "12E", "85E", "25C", "29M", "89G", "17R", "78D", "84R", "95C", "15G", "28R", "99A", "69G", "48D", "97D", "27C", "78H", "14E", "79R", "73F", "88A", "48E", "48B", "64H", "99R", "14B", "77R", "75B", "88F", "84B", "11A", "67E", "12R", "50M", "11F", "79C", "49A", "43A", "88R", "77E", "48G", "51B", "81D", "74H", "93E", "37H", "88C", "71F", "94G", "38C", "29C", "43B", "30H", "81G", "28A", "26R", "66H", "66E", "17H", "79B", "49B", "63C", "98D", "81C", "69B", "63H", "85D", "26F", "22R", "83G", "37E", "12G", "77B", "35G", "62H", "60D", "60F", "99H", "70G", "76E", "84A", "72F", "25R", "27B", "30A", "47F", "34F", "97B", "23E", "36C", "66F", "48R", "92C", "71R", "23A", "50G", "47C", "82R", "63F", "84H", "38B", "47D", "67D", "25G", "86D", "88H", "64D", "24F", "23D", "99F" };
std::unique_ptr<PaddleOCR::PPOCR> ppocr = std::make_unique<PaddleOCR::PPOCR>();
[[nodiscard]] std::string AnalyseLicensePlateText(const std::string& ocrText);
[[nodiscard]] char convertDigitToLetter(char c);
[[nodiscard]] char convertLetterToDigit(char c);
[[nodiscard]] char fixLPDigit(char c);
[[nodiscard]] int findSubstringIndex(const std::string& str);
[[nodiscard]] std::string convertStringToDigits(const std::string& input);
[[nodiscard]] std::string convertStringToLetters(const std::string& input);
[[nodiscard]] int searchDiplomacyLP(const std::string& input);
[[nodiscard]] bool ValidateVNMotobikeLP(const std::string& input);
[[nodiscard]] bool ValidateVNCarLP(const std::string& input);
bool valid;
cv::Mat alignPlateForOCR(const cv::Mat& fullImage, const cv::Rect& bbox);
cv::Mat enhanceForOCR(const cv::Mat& plateROI);
public:
ANSALPR_CPU();
~ANSALPR_CPU();
[[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colourThreshold) override;
[[nodiscard]] bool LoadEngine() override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult) override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult, const std::string & cameraId) override;
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult) override;
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult, const std::string & cameraId) override;
[[nodiscard]] std::vector<Object> RunInference(const cv::Mat& input, const std::string & cameraId) override;
//std::string VectorDetectionToJsonString(const std::vector<Object>& dets);
[[nodiscard]] bool Destroy() override;
};
}
#endif

2704
modules/ANSLPR/ANSLPR_OD.cpp Normal file

File diff suppressed because it is too large Load Diff

191
modules/ANSLPR/ANSLPR_OD.h Normal file
View File

@@ -0,0 +1,191 @@
#ifndef ANSLPROD_H
#define ANSLPROD_H
#pragma once
#include "ANSLPR.h"
#include "NV12PreprocessHelper.h"
#include <list>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace ANSCENTER
{
class ANSLPR_API ANSALPR_OD :public ANSALPR {
private:
ANSCENTER::EngineType engineType;
std::unique_ptr<ANSCENTER::ANSODBase>_lpDetector = nullptr; // License plate detector
std::unique_ptr<ANSCENTER::ANSODBase>_ocrDetector = nullptr; // OCR detector
std::unique_ptr<ANSCENTER::ANSODBase>_lpColourDetector = nullptr; // License plate colour classifier
ANSCENTER::ModelConfig _lpdmodelConfig;
ANSCENTER::ModelConfig _ocrModelConfig;
ANSCENTER::ModelConfig _lpColourModelConfig;
cv::Ptr<cv::CLAHE> _clahe; // Reusable CLAHE instance
ANSCENTER::NV12PreprocessHelper _nv12Helper; // NV12 crop for high-res plate OCR
std::string _lpdLabels;
std::string _ocrLabels;
std::string _lpColourLabels;
cv::Mat _frameBuffer; // Reusable buffer for color conversion
cv::Mat enhanceAndDebug(const cv::Mat& roi);
std::vector <std::string> _lprModelClass; //model that recognise license plate
#ifdef FNS_DEBUG
void showDebugComparison(const cv::Mat& roi, const cv::Mat& processed,
const std::vector<std::vector<cv::Point>>& contours, int bestIdx,
const cv::RotatedRect& bestRect);
#endif
[[nodiscard]] bool MatchesPlateFormat(const std::string& plate) const;
std::vector<std::string> ValidVNMotobikeList = { "29BA", "29BB", "29BC", "29BD", "29BE",
"29BF", "29BG", "29BH", "29BK", "29BL",
"29BM", "29BN", "29BP", "29AA", "29AD",
"29AE", "29AF", "29AG", "29AH", "29AK",
"29AL", "29AM", "29AN", "29AP", "29AS",
"29AT", "29AU", "29AV", "29AX", "29AY","29AZ","29AB", "29AC"//HN
"59AA", "59TA", "59FA", "59CA", "59HA",
"59KA", "59CB", "59LA", "59UA", "59MA",
"59GA", "59DB", "59PA", "59NA", "59EA",
"59SA", "59VA", "59YA", "59YB", "59ZA",
"59ZB", "59NB", "59XB", "59BA", "59XA",
"59XB", "59FA", //HCM
"43AA", "43AC", "43AD", "43AE", "43AF", "43AG", "43AB",// Da Nang
"67AA", "67AD", "67AE", "67AH", "67AB",
"67AF", "67AK", "67AL", "67AM", "67AN",// An Giang
"94AB", "94AE", "94AF", "94AH", "94AK","94FD","94FE",// Bac Lieu
"98AA", "98AH", "98AB", "98AC", "98AD",
"98AE", "98AF", "98AG", "98AK", "98AL",// Bac Giang
"71AA", "71AF", "71AG", "71AH", "71AK",
"71AB", "71AC", "71AD", "71AE", // Ben Tre
"77AA", "77AE", "77AB", "77AH",
"77AK", "77AC", "77AD", "77AF",
"77AG", "77AN", "77AM", // Binh Dinh
"69AA", "69AB", "69AC", "69AD", "69AE",
"69AF", "69KA", "69AK", "69AL", "69AM", // Ca Mau
"65MA", "65AA", "65CA", "65DB", "65EA",
"65FA", "65GA", "65HA", "65KA", // Can Tho
"76AA", "76AU", "76AV", "76AH", "76AB",
"76AC", "76AD", "76AE", "76AF", "76AK",
"76AL", "76AM", "76AN", "76AP", "76AS","76AT",// Quang Ngai
"84HA", "66PA", "84AE", "88AB", "68MA",
"63AB", "84AF", "66HA", "60AD", "47AG", "74AE",
"84AH", "76AA", "21BA", "68HA", "68AA", "28FN",
"49AL", "12HA", "66MA", "15AA", "15AC", "83VK",
"95EA", "64AA", "12FA", "21AA", "83YM", "38AG",
"68TA", "63AE", "79NA", "12ZA", "20AC", "64EA",
"66VA", "83CA", "21FA", "68KB", "74AB", "63AK",
"23AB", "47AK", "60AM", "23AE", "74AK", "84CA",
"47AV", "26AA", "28FC", "21LA", "76AS", "68FA",
"21GA", "47AH", "47AU", "23AC", "63AN", "21KA",
"49AD", "74AH", "20AK", "26AC", "74AF", "76AC",
"68NA", "60AE", "22LA", "12XA", "15AH", "26AD",
"23AA", "83EA", "47AA", "38AA", "26AB", "60AB",
"60AF", "38AE", "28FD", "66BA", "68CA", "20AD",
"21EA", "38AS", "60AA", "88AC", "64BA", "22AB",
"49AG", "38AK", "47AS", "64AC", "47AD", "68SA",
"12BA", "15AE", "28FM", "26AM", "79ZA", "66FA",
"63AH", "64DA", "47AM", "68UA", "26AG", "84AC",
"66SA", "76AD", "49AN", "26AF", "79XA", "28FS",
"49AA", "63AS", "84BA", "12SA", "83DB", "60AL",
"21HA", "38AF", "22AA", "63AF", "49AE", "47AT",
"23AD", "84AD", "23AH", "79CA", "60AH", "66GA",
"26AK", "21CA", "38AB", "28FE", "26AP", "47AP",
"47AE", "76AV", "49AC", "38AN", "12PA", "76AE",
"64FA", "12VA", "22FA", "79DA", "95NA", "76AP",
"66NA", "47AB", "74AG", "60AC", "68GA", "63AC",
"47AN", "22YA", "68PA", "47AC", "83GE", "26AL",
"64DB", "47AL", "83ZY", "60AN", "64GA", "84AL",
"49AK", "28FB", "20AE", "20AG", "76AU", "66KA",
"64CA", "76AK", "23AV", "38AX", "79HA", "47AX",
"49AM", "83PT", "15AF", "95AA", "38AM", "76AT",
"47AF", "74AC", "20AB", "64HA", "12UA", "22SA",
"68BA", "49AH", "23AK", "20AF", "63AD", "60AK",
"28FZ", "28FL", "76AB", "49AF", "26AH", "63AL",
"74AA", "79DB", "22NA", "23AU", "23AY", "83TG",
"63AM", "64KA", "23AM", "74AD", "79VA", "95XA",
"28FF", "68KA", "49AP", "83XL", "83MF", "64HB",
"15AN", "15AK", "76AF", "22TA", "12LA", "38AL",
"95HA", "68EA", "84AA", "66CA", "26AE", "66LA",
"76AN", "23AX", "95RA", "68B", "68LA", "20AH",
"20AA", "76AH", "38AC", "12TA", "76AM",
"59C3", "29S7", "29V7", "29L2", "72K1", "72D1", "98AF",
"59D2", "59S1", "59K2", "67K1", "59C4", "98AG", "59E2",
"67H2", "94L1", "29S1", "59E1", "59X4", "94B2", "67L1",
"29E1", "72C1", "29V3", "67M1", "29X7", "29X1", "98AD",
"59N3", "59C1", "59D3", "59P1", "67F1", "59M2", "59K1",
"98AB", "59T2", "72E1", "59V1", "29N2", "29E2", "67D2",
"72E2", "29T2", "29V5", "59L3", "29S2", "59L2", "72G1",
"59G2", "30Z1", "67N1", "98B1", "59U1", "59D1", "72C2",
"98B2", "98M1", "29P1", "67M2", "98H1", "29V1", "29K2",
"59V2", "59F2", "59N1", "29Z1", "98D1", "59C2", "94K1",
"67G1", "98AL", "29Y5", "59B1", "41B1", "94G1", "59G1",
"94M1", "59X2", "67C1", "67D1", "59X3", "67B1", "98AC",
"67C2", "98B3", "59Z1", "59Z2", "98AH", "59S3", "94F1",
"98L1", "59H2", "98D2", "29D1", "59M1", "29B2", "29D2",
"29U1", "59U2", "29H2", "29K1", "94B1", "98C1", "59F1",
"67H1", "29F1", "29Y1", "59P2", "67L2", "59X1", "98G1",
"29C1", "67B2", "59T1", "29G2", "29L5", "29S6", "29Y7",
"59Y2", "67E1", "98AK", "94C1", "29X2", "98K1", "98AE",
"98AA", "29H1", "29T1", "67K2", "94D2", "94E1", "29M1",
"29N1", "59Y1", "72H1", "29X3", "98F1", "29C2", "94K2",
"72F1", "29M2", "29Y3", "59P3", "29E3", "29G1", "59V3",
"59Y3", "29P2", "59N2", "29L1", "98E1", "59G3", "29X5",
"59S2", "94D1", "59H1", "29B1", "59L1", "50X1", "72L1",
"43B1", "68L1", "70G1", "36M1", "81N1", "90K1", "17B1", "64E1", "99D1", "60B2", "74L1", "60C1", "68M1", "63B7", "34B1", "69M1", "24B1", "15M1", "83Y1", "48C1", "95H1", "79X1", "17B6", "36E1", "38K1", "25N1", "25U1", "61B1", "36C1", "36B3", "38F1", "99G1", "69N1", "97D1", "92T1", "92B1", "88B1", "97G1", "14U1", "63A1", "26N1", "19D1", "93C1", "73B1", "84B1", "81K1", "18L1", "64D1", "35M1", "61N1", "83P1", "15S1", "82B1", "92U1", "43D1", "22L1", "63B5", "64G1", "27N1", "14X1", "62C1", "81D1", "38G1", "19F1", "34K1", "49P1", "89H1", "14T1", "19M1", "78D1", "76A1", "66K1", "66C1", "71C1", "37K1", "19G1", "15F1", "85C1", "49B1", "21B1", "89F1", "23M1", "66L1", "90B5", "93M1", "14P1", "77N1", "36B8", "86B1", "12U1", "63B3", "21L1", "36G5", "65G1", "82E1", "61H1", "65H1", "84A1", "23F1", "95C1", "99K1", "49G1", "92D1", "36K3", "92N1", "82X1", "83M1", "11N1", "14K1", "19H1", "93H1", "60A1", "79A1", "20D1", "90D1", "81C1", "66P1", "36K1", "92V1", "18B1", "37P1", "22Y1", "23H1", "26D1", "66G1", "78F1", "49C1", "26H1", "38P1", "47T1", "74H1", "63P1", "47D1", "15D1", "23D1", "68E1", "20B1", "49F1", "43K1", "65K1", "27Z1", "92S1", "79H1", "21E1", "35Y1", "14S1", "75E1", "24Y1", "12T1", "27P1", "77B1", "88H1", "60B3", "23P1", "61F1", "99H1", "23K1", "59A3", "26C1", "81B1", "74E1", "66B1", "22S1", "92P1", "93B1", "69B1", "81P1", "12H1", "62K1", "35A1", "77C1", "27V1", "68N1", "12D1", "64K1", "41A1", "12Z1", "76C1", "38B1", "78G1", "74K1", "69H1", "94A1", "61K1", "86B7", "82G1", "14N1", "82M1", "76E1", "18E1", "61C1", "15N1", "90A1", "77F1", "34D1", "47B1", "62S1", "43E1", "81M1", "92X1", "75B1", "34F1", "70H1", "62B1", "26B1", "60B4", "61A1", "12B1", "90T1", "92E1", "34C1", "47G1", "97B1", "25S1", "70E1", "93Y1", "47S1", "37F1", "28N1", "11K1", "38E1", "78M1", "74C1", "12S1", "75S1", "37A1", "28D1", "65L1", "22B1", "99B1", "74G1", "79K1", "76K1", "76H1", "23B1", "15R1", "36B1", "74D1", "62L1", "37E1", "78E1", "89K1", "26M1", "25F1", "48H1", "79D1", "43H1", "76F1", "36L1", "43L1", "21K1", "88L1", "27S1", "92K1", "77D1", "19N1", "66H1", "36H5", "62N1", "18G1", "75D1", "37L1", "68K1", "28C1", "26E1", "35N1", "85H1", "62D1", "27U1", "19E1", "99E1", "14Y1", "49L1", "66M1", "73F1", "70K1", "36F5", "97H1", "93E1", "68P1", "43F1", "48G1", "75K1", "62U1", "86B9", "65F1", "27L1", "70L1", "63B8", "78L1", "11Z1", "68C1", "18D1", "15L1", "99C1", "49E1", "84E1", "69E1", "38A1", "48D1", "68S1", "81E1", "84K1", "63B6", "24T1", "95A1", "86B4", "34M1", "84L1", "24V1", "14M1", "36H1", "15B1", "69F1", "47E1", "38H1", "88D1", "28E1", "60C2", "63B9", "75Y1", "21D1", "35H1", "68F1", "86B5", "15H1", "36B5", "83X1", "17B7", "12V1", "86B8", "95E1", "63B2", "74F1", "86C1", "48K1", "89M1", "85D1", "71C4", "34E1", "97C1", "88E1", "81F1", "60B5", "84M1", "92H1", "28L1", "34H1", "38X1", "82L1", "61E1", "82F1", "62P1", "93F1", "65B1", "93L1", "95B1", "15P1", "77G1", "28M1", "35B1", "68G1", "36C2", "68D1", "69K1", "14L1", "36M3", "24X1", "24Z1", "86A1", "88C1", "15E1", "77E1", "83E1", "47L1", "25T1", "89C1", "71C3", "49D1", "36L6", "48F1", "36B6", "34P1", "84D1", "15C1", "38M1", "85F1", "77K1", "86B3", "74B1", "78H1", "89G1", "64A2", "15K1", "85B1", "49K1", "21H1", "73C1", "47U1", "65E1", "18C1", "69D1", "63B1", "95G1", "19L1", "20G1", "76D1", "29A1", "68T1", "75L1", "12L1", "89L1", "37C1", "27B1", "19C1", "11H1", "81X1", "70B1", "11V1", "43G1", "22A1", "83C1", "75C1", "79C1", "22F1", "92F1", "81G1", "81T1", "28H1", "66N1", "71B1", "18H1", "76P1", "26F1", "81U1", "34N1", "64F1", "76N1", "24S1", "26P1", "63B4", "35T1", "36N1", "47F1", "81L1", "61G1", "77M1", "34G1", "26G1", "97F1", "62H1", "28F1", "62T1", "93G1", "73D1", "65A1", "47P1", "74P1", "82N1", "20E1", "36D1", "60B1", "49M1", "37H1", "37M1", "38D1", "84F1", "88F1", "36B2", "65C1", "92M1", "86B6", "75H1", "38L1", "20C1", "97E1", "85E1", "38N1", "26K1", "89B1", "99F1", "28B1", "34L1", "86B2", "66F1", "77L1", "27Y1", "68H1", "37D1", "92L1", "82K1", "99A1", "69L1", "76M1", "90B4", "48B1", "95D1", "20H1", "64H1", "79Z1", "92G1", "23G1", "21G1", "37G1", "35K1", "81H1", "83Z1", "76T1", "36F1", "36B4", "14B9", "47K1", "20K1", "62M1", "84H1", "62F1", "74A1", "18A1", "73H1", "37N1", "79N1", "61D1", "11P1", "15G1", "47N1", "19K1", "71C2", "81S1", "11M1", "60B7", "60B8", "62G1", "71A1", "24P1", "69A1", "38C1", "49N1", "21C1", "84G1", "37B1", "72A1", "88K1", "88G1", "83V1", "78C1", "73K1", "78K1", "73E189D1", "67A1", "27X1", "62A1", "18K1", "70F1", "36K5", "19B1", "49H1", "66S1", "12P1" };
ALPRChecker alprChecker;
std::vector<std::string> ValidVNCarList = { "94H", "49F", "93A", "20F", "81H", "95R", "38R", "29F", "81F", "28G", "19A", "85B", "2", "43H", "51L", "28C", "21A", "51D", "50F", "24H", "93R", "92H", "71G", "75H", "86G", "30L", "79A", "82B", "79H", "78C", "61E", "70A", "90C", "72G", "34B", "17E", "18E", "78A", "37F", "51E", "71A", "28F", "47E", "83D", "81B", "84C", "71H", "76G", "92E", "36A", "69R", "30M", "27R", "71D", "19B", "34E", "38K", "88G", "68G", "30E", "68E", "25F", "74D", "98K", "89H", "36R", "84D", "61F", "49G", "25H", "17F", "14R", "36H", "47G", "90A", "68A", "83C", "26B", "15B", "61C", "15K", "47H", "78E", "75D", "15C", "63E", "34C", "36F", "38G", "15E", "93F", "22G", "60B", "94D", "62R", "24D", "11R", "12A", "76A", "94C", "97R", "24E", "26A", "15F", "72A", "49H", "62D", "98C", "71B", "61A", "12C", "27A", "78R", "51M", "69E", "76D", "78F", "49R", "81A", "64F", "29D", "18A", "19F", "21E", "92A", "65G", "86E", "62G", "61K", "47A", "23R", "14F", "95D", "36B", "74R", "11H", "24C", "11G", "66D", "63A", "43R", "70F", "86B", "61G", "47M", "67C", "37D", "43G", "14H", "90F", "51G", "86A", "11E", "29K", "85C", "83F", "24B", "98R", "19E", "61B", "90D", "82G", "14K", "74G", "72D", "85A", "19C", "37G", "98E", "74F", "28H", "90E", "89D", "35R", "97H", "83H", "95A", "20C", "65E", "15R", "73C", "37A", "38E", "77G", "94B", "17A", "75R", "98F", "65R", "76R", "20B", "24G", "25B", "73G", "62F", "29G", "77C", "22H", "14D", "23F", "93C", "19R", "15D", "47R", "79D", "60G", "77A", "82C", "63G", "21H", "81E", "25D", "12D", "37R", "36K", "84F", "98G", "28B", "51N", "18F", "50R", "74C", "35C", "30G", "64A", "95F", "18C", "99G", "99B", "37C", "76H", "60K", "67R", "75A", "83R", "28E", "65F", "17D", "92G", "23C", "60R", "90R", "38A", "43D", "50H", "43C", "77H", "47B", "89F", "82F", "65H", "89E", "62C", "24R", "26G", "84E", "17C", "65B", "34A", "12B", "64R", "29H", "71C", "88D", "79F", "76C", "98A", "69H", "22B", "29A", "72R", "67H", "48C", "22D", "60C", "35H", "38H", "63P", "70D", "49D", "18H", "89A", "72E", "92D", "26H", "73R", "85G", "20E", "98H", "69C", "18B", "73B", "22E", "34G", "30K", "20D", "50A", "34D", "15H", "34H", "71E", "62E", "64C", "51R", "82D", "99E", "70R", "18D", "92F", "94R", "24A", "85H", "11C", "73E", "95E", "86C", "94F", "86R", "37K", "23B", "20H", "73D", "95H", "35A", "89B", "82H", "67F", "70H", "97F", "29E", "97A", "51K", "68D", "37B", "82E", "18R", "86H", "35B", "43E", "35F", "95B", "70E", "21D", "27F", "36E", "63D", "68C", "50E", "36G", "75F", "21G", "29B", "93B", "22A", "18G", "43F", "93G", "62A", "83B", "28D", "75C", "22C", "21R", "25E", "23G", "97C", "75E", "79E", "19H", "47K", "65C", "35E", "20R", "68B", "89R", "67A", "75G", "81R", "78B", "77D", "78G", "20K", "36D", "66C", "38F", "27G", "19D", "67B", "84G", "22F", "61D", "20G", "48A", "76F", "48H", "92B", "85R", "26C", "65A", "70B", "38D", "14C", "66A", "73A", "49C", "74E", "68R", "66B", "74A", "49E", "17B", "69D", "51C", "85F", "21F", "99C", "17G", "72H", "94E", "51F", "92R", "60H", "21B", "93D", "19G", "86F", "51A", "66R", "72B", "26D", "64E", "93H", "12H", "97E", "60E", "82A", "60A", "83E", "27D", "64B", "11B", "11D", "76B", "95G", "14A", "61R", "21C", "30F", "23H", "89C", "97G", "62B", "63R", "88B", "98B", "90B", "67G", "69F", "73H", "20A", "72C", "65D", "68H", "51H", "79G", "70C", "90G", "66G", "83A", "77F", "63B", "64G", "25A", "88E", "68F", "99D", "26E", "94A", "48F", "34R", "61H", "90H", "74B", "14G", "12F", "15A", "27E", "69A", "35D", "12E", "85E", "25C", "29M", "89G", "17R", "78D", "84R", "95C", "15G", "28R", "99A", "69G", "48D", "97D", "27C", "78H", "14E", "79R", "73F", "88A", "48E", "48B", "64H", "99R", "14B", "77R", "75B", "88F", "84B", "11A", "67E", "12R", "50M", "11F", "79C", "49A", "43A", "88R", "77E", "48G", "51B", "81D", "74H", "93E", "37H", "88C", "71F", "94G", "38C", "29C", "43B", "30H", "81G", "28A", "26R", "66H", "66E", "17H", "79B", "49B", "63C", "98D", "81C", "69B", "63H", "85D", "26F", "22R", "83G", "37E", "12G", "77B", "35G", "62H", "60D", "60F", "99H", "70G", "76E", "84A", "72F", "25R", "27B", "30A", "47F", "34F", "97B", "23E", "36C", "66F", "48R", "92C", "71R", "23A", "50G", "47C", "82R", "63F", "84H", "38B", "47D", "67D", "25G", "86D", "88H", "64D", "24F", "23D", "99F" };
[[nodiscard]] std::string AnalyseLicensePlateText(const std::string& ocrText);
[[nodiscard]] char convertDigitToLetter(char c);
[[nodiscard]] char convertLetterToDigit(char c);
[[nodiscard]] char fixLPDigit(char c);
[[nodiscard]] int findSubstringIndex(const std::string& str);
[[nodiscard]] std::string convertStringToDigits(const std::string& input);
[[nodiscard]] std::string convertStringToLetters(const std::string& input);
[[nodiscard]] int searchDiplomacyLP(const std::string& input);
[[nodiscard]] bool ValidateVNMotobikeLP(const std::string& input);
[[nodiscard]] bool ValidateVNCarLP(const std::string& input);
bool valid;
cv::Mat alignPlateForOCR(const cv::Mat& fullImage, const cv::Rect& bbox);
cv::Mat enhanceForOCR(const cv::Mat& plateROI);
[[nodiscard]] std::string DetectLicensePlateString(const cv::Mat& lprROI, const std::string& cameraId);
[[nodiscard]] std::string DetectLPColourDetector(const cv::Mat& lprROI, const std::string& cameraId);
[[nodiscard]] std::string DetectLPColourCached(const cv::Mat& lprROI, const std::string& cameraId, const std::string& plateText);
// LPC colour cache: keyed by plate text (e.g. "29BA-12345"),
// stores { colour, hitCount }. Plate colour doesn't change, so
// we only run the classifier once per unique plate and reuse the
// result for subsequent frames.
struct ColourCacheEntry {
std::string colour;
int hitCount = 0;
};
std::unordered_map<std::string, ColourCacheEntry> _colourCache;
static constexpr size_t COLOUR_CACHE_MAX_SIZE = 200;
public:
ANSALPR_OD();
~ANSALPR_OD();
[[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colourThreshold) override;
[[nodiscard]] bool LoadEngine() override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult) override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult, const std::string &cameraId) override;
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult) override;
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult, const std::string & cameraId) override;
[[nodiscard]] std::vector<Object> RunInferenceSingleFrame(const cv::Mat& input, const std::string& cameraId);
[[nodiscard]] std::vector<Object> RunInference(const cv::Mat& input, const std::string& cameraId) override;
[[nodiscard]] std::vector<std::string> DetectLicensePlateStringBatch(const std::vector<cv::Mat>& lprROIs, const std::string& cameraId);
[[nodiscard]] std::vector<std::string> DetectLPColourDetectorBatch(const std::vector<cv::Mat>& lprROIs, const std::string& cameraId);
[[nodiscard]] std::string ProcessSingleOCRResult(const std::vector<Object>& ocrOutput);
[[nodiscard]] bool Destroy() override;
/// Propagate debug flag to all sub-detectors (LPD, OCR, colour)
void ActivateDebugger(bool debugFlag) override {
_debugFlag = debugFlag;
if (_lpDetector) _lpDetector->ActivateDebugger(debugFlag);
if (_ocrDetector) _ocrDetector->ActivateDebugger(debugFlag);
if (_lpColourDetector) _lpColourDetector->ActivateDebugger(debugFlag);
}
};
}
#endif

328
modules/ANSLPR/ANSLPR_OV.h Normal file
View File

@@ -0,0 +1,328 @@
#ifndef ANSLPROV_H
#define ANSLPROV_H
#pragma once
#include "ANSLPR.h"
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "openvino/openvino.hpp"
#include "openvino/runtime/intel_gpu/properties.hpp"
#include "utils/args_helper.hpp"
#include "utils/input_wrappers.hpp"
#include "utils/common.hpp"
#include "utils/ocv_common.hpp"
#include "utils/threads_common.hpp"
#include "utils/grid_mat.hpp"
#include "monitors/presenter.h"
namespace ANSCENTER
{
void tryPush(const std::weak_ptr<Worker>& worker, std::shared_ptr<Task>&& task);
struct BboxAndDescr {
enum class ObjectType {
NONE,
VEHICLE,
PLATE,
} objectType;
cv::Rect rect;
std::string descr;
};
class Detector {
public:
struct Result {
std::size_t label;
float confidence;
cv::Rect location;
};
static constexpr int maxProposalCount = 200;
static constexpr int objectSize = 7; // Output should have 7 as a last dimension"
Detector() = default;
Detector(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const std::vector<float>& detectionTresholds,
const bool autoResize);
ov::InferRequest createInferRequest();
void setImage(ov::InferRequest& inferRequest, const cv::Mat& img);
std::list<Result> getResults(ov::InferRequest& inferRequest, cv::Size upscale, std::vector<std::string>& rawResults);
private:
bool m_autoResize;
std::vector<float> m_detectionTresholds;
std::string m_detectorInputName;
std::string m_detectorOutputName;
ov::CompiledModel m_compiled_model;
};
class VehicleAttributesClassifier {
public:
VehicleAttributesClassifier() = default;
VehicleAttributesClassifier(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const bool autoResize);
ov::InferRequest createInferRequest();
void setImage(ov::InferRequest& inferRequest, const cv::Mat& img, const cv::Rect vehicleRect);
std::pair<std::string, std::string> getResults(ov::InferRequest& inferRequest);
private:
bool m_autoResize;
std::string m_attributesInputName;
std::string m_outputNameForColor;
std::string m_outputNameForType;
ov::CompiledModel m_compiled_model;
};
class Lpr {
public:
Lpr() = default;
Lpr(ov::Core& core,
const std::string& deviceName,
const std::string& xmlPath,
const bool autoResize);
ov::InferRequest createInferRequest();
void setImage(ov::InferRequest& inferRequest, const cv::Mat& img, const cv::Rect plateRect);
std::string getResults(ov::InferRequest& inferRequest);
private:
bool m_autoResize;
int m_maxSequenceSizePerPlate = 0;
std::string m_LprInputName;
std::string m_LprInputSeqName;
std::string m_LprOutputName;
ov::Layout m_modelLayout;
ov::CompiledModel m_compiled_model;
};
//Utilities
struct InferRequestsContainer {
InferRequestsContainer() = default;
InferRequestsContainer(const InferRequestsContainer&) = delete;
InferRequestsContainer& operator=(const InferRequestsContainer&) = delete;
void assign(const std::vector<ov::InferRequest>& inferRequests) {
actualInferRequests = inferRequests;
this->inferRequests.container.clear();
for (auto& ir : this->actualInferRequests) {
this->inferRequests.container.push_back(ir);
}
}
ConcurrentContainer<std::vector<std::reference_wrapper<ov::InferRequest>>> inferRequests;
std::vector<ov::InferRequest> actualInferRequests;
};
// stores all global data for tasks
struct Context {
Context(const std::vector<std::shared_ptr<InputChannel>>& inputChannels,
const Detector& detector,
const VehicleAttributesClassifier& vehicleAttributesClassifier,
const Lpr& lpr,
uint64_t lastFrameId,
uint64_t nireq,
bool isVideo,
std::size_t nclassifiersireq,
std::size_t nrecognizersireq) :
readersContext{ inputChannels, std::vector<int64_t>(inputChannels.size(), -1), std::vector<std::mutex>(inputChannels.size()) },
inferTasksContext{ detector },
detectionsProcessorsContext{ vehicleAttributesClassifier, lpr },
videoFramesContext{ std::vector<uint64_t>(inputChannels.size(), lastFrameId), std::vector<std::mutex>(inputChannels.size()) },
nireq{ nireq },
isVideo{ isVideo },
freeDetectionInfersCount{ 0 },
frameCounter{ 0 }
{
std::vector<ov::InferRequest> detectorInferRequests;
std::vector<ov::InferRequest> attributesInferRequests;
std::vector<ov::InferRequest> lprInferRequests;
detectorInferRequests.reserve(nireq);
attributesInferRequests.reserve(nclassifiersireq);
lprInferRequests.reserve(nrecognizersireq);
std::generate_n(std::back_inserter(detectorInferRequests), nireq, [&] {return inferTasksContext.detector.createInferRequest(); });
std::generate_n(std::back_inserter(attributesInferRequests), nclassifiersireq, [&] {return detectionsProcessorsContext.vehicleAttributesClassifier.createInferRequest(); });
std::generate_n(std::back_inserter(lprInferRequests), nrecognizersireq, [&] {return detectionsProcessorsContext.lpr.createInferRequest(); });
detectorsInfers.assign(detectorInferRequests);
attributesInfers.assign(attributesInferRequests);
platesInfers.assign(lprInferRequests);
}
struct {
std::vector<std::shared_ptr<InputChannel>> inputChannels;
std::vector<int64_t> lastCapturedFrameIds;
std::vector<std::mutex> lastCapturedFrameIdsMutexes;
std::weak_ptr<Worker> readersWorker;
} readersContext;
struct {
Detector detector;
std::weak_ptr<Worker> inferTasksWorker;
} inferTasksContext;
struct {
VehicleAttributesClassifier vehicleAttributesClassifier;
Lpr lpr;
std::weak_ptr<Worker> detectionsProcessorsWorker;
} detectionsProcessorsContext;
struct {
std::vector<uint64_t> lastframeIds;
std::vector<std::mutex> lastFrameIdsMutexes;
} videoFramesContext;
std::weak_ptr<Worker> resAggregatorsWorker;
std::mutex classifiersAggregatorPrintMutex;
uint64_t nireq;
bool isVideo;
std::atomic<std::vector<ov::InferRequest>::size_type> freeDetectionInfersCount;
std::atomic<uint32_t> frameCounter;
InferRequestsContainer detectorsInfers, attributesInfers, platesInfers;
PerformanceMetrics metrics;
std::list<BboxAndDescr> boxesAndDescrs;
};
// End of Context
class ReborningVideoFrame : public VideoFrame {
public:
ReborningVideoFrame(Context& context,
const unsigned sourceID,
const int64_t frameId,
const cv::Mat& frame = cv::Mat()) :
VideoFrame{ sourceID, frameId, frame },
context(context) {}
virtual ~ReborningVideoFrame();
Context& context;
};
// draws results on the frame
class ResAggregator : public Task {
public:
ResAggregator(const VideoFrame::Ptr& sharedVideoFrame,
std::list<BboxAndDescr>&& boxesAndDescrs) :
Task{ sharedVideoFrame, 4.0 },
boxesAndDescrs{ std::move(boxesAndDescrs) } {}
bool isReady() override {
return true;
}
void process() override;
private:
std::list<BboxAndDescr> boxesAndDescrs;
};
// waits for all classifiers and recognisers accumulating results
class ClassifiersAggregator {
public:
std::vector<std::string> rawDetections;
ConcurrentContainer<std::list<std::string>> rawAttributes;
ConcurrentContainer<std::list<std::string>> rawDecodedPlates;
explicit ClassifiersAggregator(const VideoFrame::Ptr& sharedVideoFrame) :
sharedVideoFrame{ sharedVideoFrame } {}
~ClassifiersAggregator() {
std::mutex& printMutex = static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context.classifiersAggregatorPrintMutex;
printMutex.lock();
if (!rawDetections.empty()) {
slog::debug << "Frame #: " << sharedVideoFrame->frameId << slog::endl;
slog::debug << rawDetections;
// destructor assures that none uses the container
for (const std::string& rawAttribute : rawAttributes.container) {
slog::debug << rawAttribute << slog::endl;
}
for (const std::string& rawDecodedPlate : rawDecodedPlates.container) {
slog::debug << rawDecodedPlate << slog::endl;
}
}
printMutex.unlock();
tryPush(static_cast<ReborningVideoFrame*>(sharedVideoFrame.get())->context.resAggregatorsWorker,
std::make_shared<ResAggregator>(sharedVideoFrame, std::move(boxesAndDescrs)));
}
void push(BboxAndDescr&& bboxAndDescr) {
boxesAndDescrs.lockedPushBack(std::move(bboxAndDescr));
}
const VideoFrame::Ptr sharedVideoFrame;
private:
ConcurrentContainer<std::list<BboxAndDescr>> boxesAndDescrs;
};
// extracts detections from blob InferRequests and runs classifiers and recognisers
class DetectionsProcessor : public Task {
public:
DetectionsProcessor(VideoFrame::Ptr sharedVideoFrame, ov::InferRequest* inferRequest) :
Task{ sharedVideoFrame, 1.0 },
inferRequest{ inferRequest },
requireGettingNumberOfDetections{ true }
{
}
DetectionsProcessor(VideoFrame::Ptr sharedVideoFrame,
std::shared_ptr<ClassifiersAggregator>&& classifiersAggregator,
std::list<cv::Rect>&& vehicleRects,
std::list<cv::Rect>&& plateRects) :
Task{ sharedVideoFrame, 1.0 },
classifiersAggregator{ std::move(classifiersAggregator) },
inferRequest{ nullptr },
vehicleRects{ std::move(vehicleRects) },
plateRects{ std::move(plateRects) },
requireGettingNumberOfDetections{ false }
{
}
bool isReady() override;
void process() override;
private:
std::shared_ptr<ClassifiersAggregator> classifiersAggregator; // when no one stores this object we will draw
ov::InferRequest* inferRequest;
std::list<cv::Rect> vehicleRects;
std::list<cv::Rect> plateRects;
std::vector<std::reference_wrapper<ov::InferRequest>> reservedAttributesRequests;
std::vector<std::reference_wrapper<ov::InferRequest>> reservedLprRequests;
bool requireGettingNumberOfDetections;
};
// runs detection
class InferTask : public Task {
public:
explicit InferTask(VideoFrame::Ptr sharedVideoFrame) :
Task{ sharedVideoFrame, 5.0 } {}
bool isReady() override;
void process() override;
};
class Reader : public Task {
public:
explicit Reader(VideoFrame::Ptr sharedVideoFrame) :
Task{ sharedVideoFrame, 2.0 } {}
bool isReady() override;
void process() override;
};
class ANSLPR_API ANSALPR_OV :public ANSALPR {
private:
std::string _vehicleLPModel; //model that detects both vehicle and license plate
std::string _vehicleAtModel; //model that detects vehicle attributes
std::string _lprModel; //model that recognise license plate
Detector* _detector = nullptr;
VehicleAttributesClassifier* _vehicleAttributesClassifier = nullptr;
Lpr* _lpr = nullptr;
[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<ALPRObject>& dets);
public:
ANSALPR_OV();
~ANSALPR_OV();
[[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword) override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult);
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect>& Bbox, std::string& lprResult);
[[nodiscard]] bool Destroy() override;
};
}
#endif

View File

@@ -0,0 +1,776 @@
#include "ANSLPR_RT.h"
namespace ANSCENTER {
ANSALPR_RT::ANSALPR_RT() {
_licenseValid = false;
_globalViewId = 0;
_focusedOnLPRId = 0;
_platesTypesClassifierId = 0;
}
ANSALPR_RT::~ANSALPR_RT() {
CloseReferences();
}
bool ANSALPR_RT::Destroy() {
CloseReferences();
return true;
}
bool ANSALPR_RT::CloseReferences() {
try {
bool session_closed = CloseDetector(_globalViewId);
if (!session_closed) {
this->_logger->LogFatal("ANSALPR_RT::CloseReferences", "Cannot close global view model reference", __FILE__, __LINE__);
}
else {
_globalViewId = 0;
}
session_closed = CloseDetector(_focusedOnLPRId);
if (!session_closed) {
this->_logger->LogFatal("ANSALPR_RT::CloseReferences", "Cannot close focused on LRP model reference", __FILE__, __LINE__);
}
else {
_focusedOnLPRId = 0;
}
session_closed = ClosePlatesTypesClassifier(_platesTypesClassifierId);
if (!session_closed) {
this->_logger->LogFatal("ANSALPR_RT::CloseReferences", "Cannot close plate type classifer model reference", __FILE__, __LINE__);
}
else {
_platesTypesClassifierId = 0;
}
return true;
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR_RT::CloseReferences", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSALPR_RT::Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword) {
try {
_licenseKey = licenseKey;
_licenseValid = false;
CheckLicense();
if (!_licenseValid) {
this->_logger->LogError("ANSALPR::Initialize.", "License is not valid.", __FILE__, __LINE__);
return false;
}
// Extract model folder
// 0. Check if the modelZipFilePath exist?
if (!FileExist(modelZipFilePath)) {
this->_logger->LogFatal("ANSALPR::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
}
// 1. Unzip model zip file to a special location with folder name as model file (and version)
std::string outputFolder;
std::vector<std::string> passwordArray;
if (!modelZipPassword.empty()) passwordArray.push_back(modelZipPassword);
passwordArray.push_back("AnsDemoModels20@!");
passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
std::string modelName = GetFileNameWithoutExtension(modelZipFilePath);
//this->_logger->LogInfo("ANSFDBase::Initialize. Model name", modelName);
size_t vectorSize = passwordArray.size();
for (size_t i = 0; i < vectorSize; i++) {
if (ExtractPasswordProtectedZip(modelZipFilePath, passwordArray[i], modelName, _modelFolder, false))
break; // Break the loop when the condition is met.
}
// 2. Check if the outputFolder exist
if (!FolderExist(_modelFolder)) {
this->_logger->LogError("ANSFDBase::Initialize. Output model folder is not exist", _modelFolder, __FILE__, __LINE__);
return false; // That means the model file is not exist or the password is not correct
}
//3. Get License plate models
_globalModelFileName = CreateFilePath(_modelFolder, "anslpr_alpr_focused_on_lp.onnx");
if (!FileExist(_globalModelFileName)) {
this->_logger->LogError("ANSALPR::Initialize. Global view model does not exist", _globalModelFileName, __FILE__, __LINE__);
return false;
}
_focusedOnLPRModelFileName = CreateFilePath(_modelFolder, "anslpr_alpr_global_view.onnx");
if (!FileExist(_focusedOnLPRModelFileName)) {
this->_logger->LogError("ANSALPR::Initialize. Focused On LRP model does not exist", _focusedOnLPRModelFileName, __FILE__, __LINE__);
return false;
}
_platesTypesClassifierFileName = CreateFilePath(_modelFolder, "plates_types_7.onnx");
if (!FileExist(_platesTypesClassifierFileName)) {
this->_logger->LogError("ANSALPR::Initialize.LRP classifier model does not exist", _platesTypesClassifierFileName, __FILE__, __LINE__);
return false;
}
_platesTypesLabelsFileName = CreateFilePath(_modelFolder, "plates_types_7.txt");
if (!FileExist(_platesTypesLabelsFileName)) {
this->_logger->LogError("ANSALPR::Initialize.LRP classifier label model does not exist", _platesTypesLabelsFileName, __FILE__, __LINE__);
return false;
}
// Load models
size_t len = _globalModelFileName.size();
//step 1 : Initializes a new detector by loading its model file. In return, you get a unique id. The repo comes with two models namely anslpr_alpr_focused_on_lpand anslpr_alpr_global_view.
//So you have to call this function twice to initialize both models.
_globalViewId = InitYoloDetector(len, _globalModelFileName.c_str());
if (_globalViewId <= 0) {
this->_logger->LogError("ANSALPR::Initialize.", "Global view model cannot be loaded", __FILE__, __LINE__);
return false;
}
len = _focusedOnLPRModelFileName.size();
_focusedOnLPRId = InitYoloDetector(len, _focusedOnLPRModelFileName.c_str());
if (_focusedOnLPRId <= 0) {
this->_logger->LogError("ANSALPR::Initialize.", "Focused on LPR model cannot be loaded", __FILE__, __LINE__);
return false;
}
len = _platesTypesClassifierFileName.size();
_platesTypesClassifierId = InitPlatesClassifer(len, _platesTypesClassifierFileName.c_str(), _platesTypesLabelsFileName.size(), _platesTypesLabelsFileName.c_str());
if (_platesTypesClassifierId <= 0) {
this->_logger->LogError("ANSALPR::Initialize.", "Plate type classifier model cannot be loaded", __FILE__, __LINE__);
return false;
}
return true;
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSALPR_RT::Inference(const cv::Mat& input, std::string& lprResult)
{
if (!_licenseValid) {
lprResult = "";
return false;
}
try {
const size_t lpn_len = 15;
char* lpn = new char[lpn_len + 1];
std::vector<ALPRObject> output;
output.clear();
cv::Rect bbox;
bool detected = TwoStageLPRPlatesTypeDetection(
input.cols,//width of image
input.rows,//height of image i.e. the specified dimensions of the image
input.channels(),// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
input.data,
input.step,// source image bytes buffer
_globalViewId,
_focusedOnLPRId,//id : unique interger to identify the detector to be used
_platesTypesClassifierId,//unique id to identify the platestype classifier
lpn_len,
lpn, bbox);
lprResult = lpn;
if (detected) {
ALPRObject result;
result.classId = 0;
result.className = lpn;
result.confidence = 1.0;
result.box = bbox;
output.push_back(result);
}
lprResult = VectorDetectionToJsonString(output);
return detected;
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR::CheckStatus", e.what(), __FILE__, __LINE__);
lprResult = "";
return false;
};
}
bool ANSALPR_RT::Inference(const cv::Mat& input, const std::vector<cv::Rect>& Bbox, std::string& lprResult) {
if (!_licenseValid) {
lprResult = "";
return false;
}
try {
const size_t lpn_len = 15;
char* lpn = new char[lpn_len + 1];
std::vector<ALPRObject> output;
output.clear();
bool detected = false;
if (Bbox.size() > 0) {
cv::Mat frame = input.clone();
for (std::vector<cv::Rect>::iterator it = Bbox.begin(); it != Bbox.end(); it++) {
int x1, y1, x2, y2;
x1 = (*it).x;
y1 = (*it).y;
x2 = (*it).x + (*it).width;
y2 = (*it).y + (*it).height;
// Get cropped objects
cv::Rect objectPos(cv::Point(x1, y1), cv::Point(x2, y2));
cv::Mat croppedObject = frame(objectPos);
cv::Rect bbox;
detected = TwoStageLPRPlatesTypeDetection(
croppedObject.cols,//width of image
croppedObject.rows,//height of image i.e. the specified dimensions of the image
croppedObject.channels(),// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
croppedObject.data,
croppedObject.step,// source image bytes buffer
_globalViewId,
_focusedOnLPRId,//id : unique interger to identify the detector to be used
_platesTypesClassifierId,//unique id to identify the platestype classifier
lpn_len,
lpn, bbox);
lprResult = lpn;
if (detected) {
ALPRObject result;
result.classId = 0;
result.className = lpn;
result.confidence = 1.0;
result.box.x = bbox.x + x1;
result.box.y = bbox.y + y1;
result.box.width = bbox.width;
result.box.height = bbox.height;
output.push_back(result);
}
}
lprResult = VectorDetectionToJsonString(output);
}
else {
cv::Rect bbox;
detected = TwoStageLPRPlatesTypeDetection(
input.cols,//width of image
input.rows,//height of image i.e. the specified dimensions of the image
input.channels(),// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
input.data,
input.step,// source image bytes buffer
_globalViewId,
_focusedOnLPRId,//id : unique interger to identify the detector to be used
_platesTypesClassifierId,//unique id to identify the platestype classifier
lpn_len,
lpn, bbox);
lprResult = lpn;
if (detected) {
ALPRObject result;
result.classId = 0;
result.className = lpn;
result.confidence = 1.0;
result.box = bbox;
output.push_back(result);
}
lprResult = VectorDetectionToJsonString(output);
}
return detected;
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR::CheckStatus", e.what(), __FILE__, __LINE__);
lprResult = "";
return false;
};
}
// Private:
std::string ANSALPR_RT::VectorDetectionToJsonString(const std::vector<ALPRObject>& dets) {
boost::property_tree::ptree root;
boost::property_tree::ptree detectedObjects;
for (int i = 0; i < dets.size(); i++) {
boost::property_tree::ptree detectedNode;
detectedNode.put("class_id", dets[i].classId);
detectedNode.put("class_name", dets[i].className);
detectedNode.put("prob", dets[i].confidence);
detectedNode.put("x", dets[i].box.x);
detectedNode.put("y", dets[i].box.y);
detectedNode.put("width", dets[i].box.width);
detectedNode.put("height", dets[i].box.height);
detectedNode.put("mask", "");//Todo: convert masks to mask with comma seperated dets[i].mask);
detectedNode.put("extra_info", "");
// we might add masks into this using comma seperated string
detectedObjects.push_back(std::make_pair("", detectedNode));
}
root.add_child("results", detectedObjects);
std::ostringstream stream;
boost::property_tree::write_json(stream, root, false);
std::string trackingResult = stream.str();
return trackingResult;
}
void ANSALPR_RT::CheckStatus(OrtStatus* status) {
try {
if (status != nullptr) {
const char* msg = g_ort->GetErrorMessage(status);
this->_logger->LogError("ANSALPR::CheckStatus", msg, __FILE__, __LINE__);
g_ort->ReleaseStatus(status);
exit(1);
}
}
catch (std::exception& e) {
this->_logger->LogFatal("ANSALPR::CheckStatus", e.what(), __FILE__, __LINE__);
}
}
std::list<Yolov5_alpr_onxx_detector*>::const_iterator ANSALPR_RT::GetDetector(unsigned int id, const std::list<Yolov5_alpr_onxx_detector*>& detectors,
const std::list<unsigned int>& detectors_ids)
{
assert(detectors_ids.size() == detectors.size());
std::list<Yolov5_alpr_onxx_detector*>::const_iterator it(detectors.begin());
std::list<unsigned int>::const_iterator it_id(detectors_ids.begin());
while (it != detectors.end() && it_id != detectors_ids.end()) {
if (*it_id == id) {
return it;
}
else {
it_id++;
it++;
}
}
return detectors.end();
}
std::list<Plates_types_classifier*>::const_iterator ANSALPR_RT::GetPlatesTypesClassifier(unsigned int id, const std::list<Plates_types_classifier*>& plates_types_classifiers,
const std::list<unsigned int>& plates_types_classifiers_ids) {
assert(plates_types_classifiers_ids.size() == plates_types_classifiers.size());
std::list<Plates_types_classifier*>::const_iterator it(plates_types_classifiers.begin());
std::list<unsigned int>::const_iterator it_id(plates_types_classifiers_ids.begin());
while (it != plates_types_classifiers.end() && it_id != plates_types_classifiers_ids.end()) {
if (*it_id == id) {
return it;
}
else {
it_id++;
it++;
}
}
return plates_types_classifiers.end();
}
unsigned int ANSALPR_RT::GetNewId(const std::list<unsigned int>& detectors_ids) {
if (detectors_ids.size()) {
auto result = std::minmax_element(detectors_ids.begin(), detectors_ids.end());
return *result.second + 1;
}
else return 1;
}
bool ANSALPR_RT::CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Yolov5_alpr_onxx_detector*>& _detectors,
std::list<unsigned int>& _detectors_ids) {
assert(_detectors_ids.size() == _detectors.size()
&& _detectors_ids.size() == _envs.size()
&& _detectors_ids.size() == _lsessionOptions.size());
std::list<Yolov5_alpr_onxx_detector*>::iterator it(_detectors.begin());
std::list<unsigned int>::iterator it_id(_detectors_ids.begin());
std::list<Ort::SessionOptions*>::iterator it_sessionOptions(_lsessionOptions.begin());
std::list<Ort::Env*>::iterator it_envs(_envs.begin());
while (it != _detectors.end() && it_id != _detectors_ids.end()
&& it_envs != _envs.end() && it_sessionOptions != _lsessionOptions.end()
) {
if (*it_id == id) {
if (*it != nullptr) delete* it;
if (*it_sessionOptions != nullptr) delete* it_sessionOptions;
if (*it_envs != nullptr) delete* it_envs;
it_envs = _envs.erase(it_envs);
it_sessionOptions = _lsessionOptions.erase(it_sessionOptions);
it = _detectors.erase(it);
it_id = _detectors_ids.erase(it_id);
return true;
}
else {
it_sessionOptions++;
it_envs++;
it_id++;
it++;
}
}
return false;
}
bool ANSALPR_RT::CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Plates_types_classifier*>& _detectors,
std::list<unsigned int>& _detectors_ids) {
assert(_detectors_ids.size() == _detectors.size()
&& _detectors_ids.size() == _envs.size()
&& _detectors_ids.size() == _lsessionOptions.size());
std::list<Plates_types_classifier*>::iterator it(_detectors.begin());
std::list<unsigned int>::iterator it_id(_detectors_ids.begin());
std::list<Ort::SessionOptions*>::iterator it_sessionOptions(_lsessionOptions.begin());
std::list<Ort::Env*>::iterator it_envs(_envs.begin());
while (it != _detectors.end() && it_id != _detectors_ids.end()
&& it_envs != _envs.end() && it_sessionOptions != _lsessionOptions.end()
) {
if (*it_id == id) {
if (*it != nullptr) delete* it;
if (*it_sessionOptions != nullptr) delete* it_sessionOptions;
if (*it_envs != nullptr) delete* it_envs;
it_envs = _envs.erase(it_envs);
it_sessionOptions = _lsessionOptions.erase(it_sessionOptions);
it = _detectors.erase(it);
it_id = _detectors_ids.erase(it_id);
return true;
}
else {
it_sessionOptions++;
it_envs++;
it_id++;
it++;
}
}
return false;
}
// Private interface
unsigned int ANSALPR_RT::InitYoloDetector(unsigned int len, const char* model_file)
{
assert(detectors_ids.size() == detectors.size());
const std::string model_filename(model_file, len);
if (!model_filename.size() || !std::filesystem::exists(model_filename)
|| !std::filesystem::is_regular_file(model_filename)
)
{
this->_logger->LogError("ANSALPR::InitYoloDetector. Model file is not regular file.", model_filename, __FILE__, __LINE__);
return 0;
}
//step 2 declare an onnx runtime environment
std::string instanceName{ "image-classification-inference" };
// https://github.com/microsoft/onnxruntime/blob/rel-1.6.0/include/onnxruntime/core/session/onnxruntime_c_api.h#L123
Ort::Env* penv = new Ort::Env(OrtLoggingLevel::ORT_LOGGING_LEVEL_WARNING, instanceName.c_str());
if (penv != nullptr) {
//step 3 declare options for the runtime environment
Ort::SessionOptions* psessionOptions = new Ort::SessionOptions();
if (psessionOptions != nullptr) {
psessionOptions->SetIntraOpNumThreads(1);
// Sets graph optimization level
// Available levels are
// ORT_DISABLE_ALL -> To disable all optimizations
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node
// removals) ORT_ENABLE_EXTENDED -> To enable extended optimizations
// (Includes level 1 + more complex optimizations like node fusions)
// ORT_ENABLE_ALL -> To Enable All possible optimizations
psessionOptions->SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
#ifdef ANSLPR_USE_CUDA
// Optionally add more execution providers via session_options
// E.g. for CUDA include cuda_provider_factory.h and uncomment the following line:
// nullptr for Status* indicates success
OrtStatus* status = OrtSessionOptionsAppendExecutionProvider_CUDA(*psessionOptions, 0);
//or status =nullptr; //if you don t have CUDA
if (status == nullptr) {
#endif //ANSLPR_USE_CUDA
Yolov5_alpr_onxx_detector* onnx_net = nullptr;
#ifdef _WIN32
//step 4 declare an onnx session (ie model), by giving references to the runtime environment, session options and file path to the model
std::wstring widestr = std::wstring(model_filename.begin(), model_filename.end());
onnx_net = new Yolov5_alpr_onxx_detector(*penv, widestr.c_str(), *psessionOptions);
#else
onnx_net = new Yolov5_alpr_onxx_detector(*penv, model_filename.c_str(), *psessionOptions);
#endif
if (onnx_net != nullptr && penv != nullptr && psessionOptions != nullptr) {
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
lck.lock();
detectors_envs.push_back(penv);
l_detectors_sessionOptions.push_back(psessionOptions);
detectors.push_back(onnx_net);
unsigned int id = GetNewId(detectors_ids);
detectors_ids.push_back(id);
lck.unlock();
return id;
}
else {
this->_logger->LogError("ANSALPR::InitYoloDetector. Error while creating onnxruntime session with file", model_filename.c_str(), __FILE__, __LINE__);
return 0;
}
#ifdef ANSLPR_USE_CUDA
}
else {
CheckStatus(status);
this->_logger->LogError("ANSALPR::InitYoloDetector.", "Cuda error", __FILE__, __LINE__);
return 0;
}
#endif //ANSLPR_USE_CUDA
}
else {
this->_logger->LogError("ANSALPR::InitYoloDetector.", "Error while creating SessionOptions", __FILE__, __LINE__);
return 0;
}
}
else {
this->_logger->LogError("ANSALPR::InitYoloDetector.", "Error while creating while creating session environment (Ort::Env)", __FILE__, __LINE__);
return 0;
}
}
unsigned int ANSALPR_RT::InitPlatesClassifer(unsigned int len_models_filename, const char* model_file, unsigned int len_labels_filename, const char* labels_file)
{
assert(plates_types_classifier_ids.size() == plates_types_classifiers.size());
const std::string model_filename(model_file, len_models_filename);
const std::string labels_filename(labels_file, len_labels_filename);
if (!model_filename.size() || !std::filesystem::exists(model_filename)
|| !std::filesystem::is_regular_file(model_filename)
|| !labels_filename.size() || !std::filesystem::exists(labels_filename)
|| !std::filesystem::is_regular_file(labels_filename)
)
{
this->_logger->LogDebug("ANSALPR::InitPlatesClassifer. Model file is not regular file.", model_filename, __FILE__, __LINE__);
return 0;
}
//step 2 declare an onnx runtime environment
std::string instanceName{ "image-classification-inference" };
// https://github.com/microsoft/onnxruntime/blob/rel-1.6.0/include/onnxruntime/core/session/onnxruntime_c_api.h#L123
Ort::Env* penv = new Ort::Env(OrtLoggingLevel::ORT_LOGGING_LEVEL_WARNING, instanceName.c_str());
if (penv != nullptr) {
//step 3 declare options for the runtime environment
Ort::SessionOptions* psessionOptions = new Ort::SessionOptions();
if (psessionOptions != nullptr) {
psessionOptions->SetIntraOpNumThreads(1);
// Sets graph optimization level
// Available levels are
// ORT_DISABLE_ALL -> To disable all optimizations
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node
// removals) ORT_ENABLE_EXTENDED -> To enable extended optimizations
// (Includes level 1 + more complex optimizations like node fusions)
// ORT_ENABLE_ALL -> To Enable All possible optimizations
psessionOptions->SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
#ifdef ANSLPR_USE_CUDA
// Optionally add more execution providers via session_options
// E.g. for CUDA include cuda_provider_factory.h and uncomment the following line:
// nullptr for Status* indicates success
OrtStatus* status = OrtSessionOptionsAppendExecutionProvider_CUDA(*psessionOptions, 0);
//or status =nullptr; //if you don t have CUDA
if (status == nullptr) {
#endif //ANSLPR_USE_CUDA
Plates_types_classifier* onnx_net = nullptr;
#ifdef _WIN32
//step 4 declare an onnx session (ie model), by giving references to the runtime environment, session options and file path to the model
std::wstring widestr = std::wstring(model_filename.begin(), model_filename.end());
onnx_net = new Plates_types_classifier(*penv, widestr.c_str(), *psessionOptions, labels_filename);
#else
onnx_net = new Plates_types_classifier(*penv, model_filename.c_str(), *psessionOptions, labels_filename);
#endif
if (onnx_net != nullptr && penv != nullptr && psessionOptions != nullptr) {
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
lck.lock();
plates_types_envs.push_back(penv);
l_plates_types_classifier_sessionOptions.push_back(psessionOptions);
plates_types_classifiers.push_back(onnx_net);
unsigned int id = GetNewId(plates_types_classifier_ids);
plates_types_classifier_ids.push_back(id);
lck.unlock();
return id;
}
else {
this->_logger->LogError("ANSALPR::InitPlatesClassifer. Error while creating onnxruntime session with file.", model_filename, __FILE__, __LINE__);
return 0;
}
#ifdef ANSLPR_USE_CUDA
}
else {
CheckStatus(status);
this->_logger->LogError("ANSALPR::InitPlatesClassifer.", "Cuda error", __FILE__, __LINE__);
return 0;
}
#endif //ANSLPR_USE_CUDA
}
else {
this->_logger->LogError("ANSALPR::InitPlatesClassifer.", "Error while creating SessionOptions", __FILE__, __LINE__);
return 0;
}
}
else {
this->_logger->LogError("ANSALPR::InitPlatesClassifer.", "Error while creating session environment (Ort::Env)", __FILE__, __LINE__);
return 0;
}
}
bool ANSALPR_RT::TwoStageALPR(const int width,//width of image
const int height,//height of image i.e. the specified dimensions of the image
const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
void* pbData, unsigned int step,// source image bytes buffer
unsigned int id_global_view,
unsigned int id_focused_on_lp,
unsigned int lpn_len,
char* lpn, cv::Rect& bbox)
{
if ((pixOpt != 1) && (pixOpt != 3) && (pixOpt != 4) || height <= 0 || width <= 0 || pbData == nullptr) {
this->_logger->LogError("ANSALPR::TwoStageLPR.", "Condition on image (pixOpt != 1) && (pixOpt != 3) && (pixOpt != 4) || height <= 0 || width <= 0 || pbData == nullptr not met", __FILE__, __LINE__);
return false;
}
else {
cv::Mat destMat;
if (pixOpt == 1)
{
destMat = cv::Mat(height, width, CV_8UC1, pbData, step);
}
if (pixOpt == 3)
{
destMat = cv::Mat(height, width, CV_8UC3, pbData, step);
}
if (pixOpt == 4)
{
destMat = cv::Mat(height, width, CV_8UC4, pbData, step);
}
std::list<Yolov5_alpr_onxx_detector*>::const_iterator it_global_view = GetDetector(id_global_view, detectors, detectors_ids);
if (it_global_view != detectors.end()) {
std::list<Yolov5_alpr_onxx_detector*>::const_iterator it_focused_on_lp = GetDetector(id_focused_on_lp, detectors, detectors_ids);
std::string lpn_str;
std::list<cv::Rect> ROIs;
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
if (it_focused_on_lp != detectors.end()) {
lck.lock();
//for normal plates
ROIs = (*it_global_view)->TwoStage_LPR(*(*it_focused_on_lp), destMat, lpn_str);
//for small plates
lck.unlock();
}
else {
this->_logger->LogError("ANSALPR::TwoStageLPR.", "id_focused_on_lp does not point to a valid detector", __FILE__, __LINE__);
lck.lock();
//for normal plates
ROIs = (*it_global_view)->TwoStage_LPR(*(*it_global_view), destMat, lpn_str);
//for small plates
lck.unlock();
}
std::string::const_iterator it_lpn(lpn_str.begin());
int i = 0;
while (it_lpn != lpn_str.end() && i < lpn_len - 1) {
lpn[i] = *it_lpn;
i++; it_lpn++;
}
while (i < lpn_len) {
lpn[i] = '\0';
i++;
}
bbox = GetGlobalROI(ROIs);
return (lpn_str.length() > 0);
}
else {
this->_logger->LogError("ANSALPR::TwoStageLPR.", "id_global_view does not point to a valid detector", __FILE__, __LINE__);
return false;
}
}
}
cv::Rect ANSALPR_RT::GetGlobalROI(std::list<cv::Rect> ROIs) {
cv::Rect result;
if (ROIs.size() > 0) result = ROIs.front();
return result;
/*std::list<cv::Rect>::iterator it;
cv::Rect v = ROIs.front();
int x_min, y_min, w_min, h_min;
int x_max, y_max, w_max, h_max;
x_min = v.x;
x_max = v.x;
y_min = v.y;
y_max = v.y;
w_min = v.width;
w_max = v.width;
h_min = v.height;
h_max = v.height;
for (it = ROIs.begin(); it != ROIs.end(); ++it) {
if (x_min > it->x) { x_min = it->x; }
if (x_max < it->x) { x_max = it->x; }
if (y_min > it->y) { y_min = it->y; }
if (y_max < it->y) { y_max = it->y; }
if (w_min > it->width) { w_min = it->width; }
if (w_max < it->width) { w_max = it->width; }
if (h_min > it->height) { w_min = it->height; }
if (h_max < it->height) { w_max = it->height; }
}
v.x = x_min - 2;
v.y = y_min - 2;
v.width = x_max + w_max - x_min + 2;
v.height = h_max + 2;
return v;*/
}
bool ANSALPR_RT::TwoStageLPRPlatesTypeDetection(const int width,//width of image
const int height,//height of image i.e. the specified dimensions of the image
const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
void* pbData, unsigned int step,// source image bytes buffer
unsigned int id_global_view,
unsigned int id_focused_on_lp,
unsigned int id_plates_types_classifier,
unsigned int lpn_len, char* lpn, cv::Rect& bbox)
{
if ((pixOpt != 1) && (pixOpt != 3) && (pixOpt != 4) || height <= 0 || width <= 0 || pbData == nullptr) {
this->_logger->LogError("ANSALPR::TwoStageLPRPlatesTypeDetection.", "Condition on image (pixOpt != 1) && (pixOpt != 3) && (pixOpt != 4) || height <= 0 || width <= 0 || pbData == nullptr not met", __FILE__, __LINE__);
return false;
}
else {
cv::Mat destMat;
if (pixOpt == 1)
{
destMat = cv::Mat(height, width, CV_8UC1, pbData, step);
}
if (pixOpt == 3)
{
destMat = cv::Mat(height, width, CV_8UC3, pbData, step);
}
if (pixOpt == 4)
{
destMat = cv::Mat(height, width, CV_8UC4, pbData, step);
}
std::list<Plates_types_classifier*>::const_iterator it_plates_types_classifier = GetPlatesTypesClassifier(id_plates_types_classifier,
plates_types_classifiers, plates_types_classifier_ids);
if (it_plates_types_classifier != plates_types_classifiers.end()) {
std::list<Yolov5_alpr_onxx_detector*>::const_iterator it_global_view = GetDetector(id_global_view, detectors, detectors_ids);
if (it_global_view != detectors.end()) {
std::list<Yolov5_alpr_onxx_detector*>::const_iterator it_focused_on_lp = GetDetector(id_focused_on_lp, detectors, detectors_ids);
std::string lpn_str;
std::list<cv::Rect> ROIs;
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
if (it_focused_on_lp != detectors.end()) {
lck.lock();
//for normal plates
ROIs = (*it_global_view)->TwoStageLPR(*(*it_focused_on_lp), *(*it_plates_types_classifier), destMat, lpn_str);
//for small plates
lck.unlock();
}
else {
this->_logger->LogError("ANSALPR::TwoStageLPRPlatesTypeDetection.", "id_focused_on_lp does not point to a valid detector", __FILE__, __LINE__);
lck.lock();
//for normal plates
ROIs = (*it_global_view)->TwoStageLPR(*(*it_global_view), *(*it_plates_types_classifier), destMat, lpn_str);
//for small plates
lck.unlock();
}
std::string::const_iterator it_lpn(lpn_str.begin());
int i = 0;
while (it_lpn != lpn_str.end() && i < lpn_len - 1) {
lpn[i] = *it_lpn;
i++; it_lpn++;
}
while (i < lpn_len) {
lpn[i] = '\0';
i++;
}
bbox = GetGlobalROI(ROIs);
return (lpn_str.length() > 0);
}
else {
this->_logger->LogError("ANSALPR::TwoStageLPRPlatesTypeDetection.", "id_global_view does not point to a valid detector", __FILE__, __LINE__);
return false;
}
}
else {
this->_logger->LogError("ANSALPR::TwoStageLPRPlatesTypeDetection.", "id_plates_types_classifier does not point to a valid detector", __FILE__, __LINE__);
return TwoStageALPR(width,//width of image
height,//height of image i.e. the specified dimensions of the image
pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
pbData, step,// source image bytes buffer
id_global_view, id_focused_on_lp, lpn_len, lpn, bbox);
}
}
}
bool ANSALPR_RT::CloseDetector(unsigned int id)
{
assert(detectors_ids.size() == detectors.size());
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
lck.lock();
bool session_closed = CloseDetector(id, detectors_envs, l_detectors_sessionOptions, detectors, detectors_ids);
lck.unlock();
return session_closed;
}
bool ANSALPR_RT::ClosePlatesTypesClassifier(unsigned int id)
{
assert(plates_types_classifier_ids.size() == plates_types_classifiers.size());
std::unique_lock<std::mutex> lck(mtxMutex, std::defer_lock);
lck.lock();
bool session_closed = CloseDetector(id, plates_types_envs, l_plates_types_classifier_sessionOptions, plates_types_classifiers, plates_types_classifier_ids);
lck.unlock();
return session_closed;
}
}

View File

@@ -0,0 +1,97 @@
#ifndef ANSLPRRT_H
#define ANSLPRRT_H
#pragma once
#include "ANSLPR.h"
#include <onnxruntime_c_api.h>
#include <onnxruntime_cxx_api.h>
#include "yolov5_alpr_onnx_detector.h"
#include "ONNX_detector.h"
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::defer_lock
namespace ANSCENTER
{
class ANSLPR_API ANSALPR_RT :public ANSALPR {
private:
std::mutex mtxMutex; // mutex for critical section
std::string _globalModelFileName{};
std::string _focusedOnLPRModelFileName{};
std::string _platesTypesClassifierFileName{};
std::string _platesTypesLabelsFileName{};
size_t _globalViewId;
size_t _focusedOnLPRId;
size_t _platesTypesClassifierId;
[[nodiscard]] bool CloseReferences();
public:
ANSALPR_RT();
~ANSALPR_RT();
[[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword) override;
[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult);
[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect>& Bbox, std::string& lprResult);
[[nodiscard]] bool Destroy() override;
private:
[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<Object>& dets);
void CheckStatus(OrtStatus* status);
cv::Rect GetGlobalROI(std::list<cv::Rect> ROIs);
std::list<Yolov5_alpr_onxx_detector*>::const_iterator GetDetector(unsigned int id, const std::list<Yolov5_alpr_onxx_detector*>& detectors,
const std::list<unsigned int>& detectors_ids);
std::list<Plates_types_classifier*>::const_iterator GetPlatesTypesClassifier(unsigned int id, const std::list<Plates_types_classifier*>& plates_types_classifiers,
const std::list<unsigned int>& plates_types_classifiers_ids);
unsigned int GetNewId(const std::list<unsigned int>& detectors_ids);
bool CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Yolov5_alpr_onxx_detector*>& _detectors,
std::list<unsigned int>& _detectors_ids);
bool CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Plates_types_classifier*>& _detectors,
std::list<unsigned int>& _detectors_ids);
unsigned int InitYoloDetector(unsigned int len, const char* model_file);
unsigned int InitPlatesClassifer(unsigned int len_models_filename, const char* model_file, unsigned int len_labels_filename, const char* labels_file);
bool TwoStageALPR(const int width,//width of image
const int height,//height of image i.e. the specified dimensions of the image
const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
void* pbData, unsigned int step,// source image bytes buffer
unsigned int id_global_view,
unsigned int id_focused_on_lp,
unsigned int lpn_len, char* lpn, cv::Rect& bbox);
bool TwoStageLPRPlatesTypeDetection(const int width,//width of image
const int height,//height of image i.e. the specified dimensions of the image
const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
void* pbData,
unsigned int step,// source image bytes buffer
unsigned int id_global_view,
unsigned int id_focused_on_lp,
unsigned int id_plates_types_classifier,
unsigned int lpn_len,
char* lpn,
cv::Rect& bbox);
bool CloseDetector(unsigned int id);
bool ClosePlatesTypesClassifier(unsigned int id);
private:
// Version-negotiated OrtApi pointer: caps at the DLL's max supported API level so
// that a newer SDK header (ORT_API_VERSION=22) paired with an older runtime DLL
// (e.g. ORT 1.17.1, API ≤17) does not emit "[ORT ERROR] API version not available".
// Mirrors the negotiation pattern used in EPLoader.cpp.
const OrtApi* g_ort = []() -> const OrtApi* {
const OrtApiBase* base = OrtGetApiBase();
int dllMaxApi = ORT_API_VERSION;
const char* verStr = base->GetVersionString();
int major = 0, minor = 0;
if (verStr && sscanf(verStr, "%d.%d", &major, &minor) == 2)
dllMaxApi = minor;
int targetApi = (ORT_API_VERSION < dllMaxApi) ? ORT_API_VERSION : dllMaxApi;
return base->GetApi(targetApi);
}();
std::list<Ort::Env*> detectors_envs;
std::list<Ort::SessionOptions*> l_detectors_sessionOptions;
std::list<Yolov5_alpr_onxx_detector*> detectors;
std::list<unsigned int> detectors_ids;
std::list<Ort::Env*> plates_types_envs;
std::list<Ort::SessionOptions*> l_plates_types_classifier_sessionOptions;
std::list<Plates_types_classifier*> plates_types_classifiers;
std::list<unsigned int> plates_types_classifier_ids;
};
}
#endif

View File

@@ -0,0 +1,73 @@
# ANSLPR — License Plate Recognition DLL
# Explicit source list from original vcxproj
set(ANSLPR_SOURCES
ANSLPR.cpp
ANSLPR_CPU.cpp
ANSLPR_OD.cpp
ANSGpuFrameRegistry.cpp
dllmain.cpp
pch.cpp
)
file(GLOB ANSLPR_HEADERS "*.h")
file(GLOB ANSLPR_INCLUDE_HEADERS "include/*.h")
# PaddleOCR sources compiled into ANSLPR (from original vcxproj)
set(PADDLE_SOURCES
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/clipper.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/ocr_cls.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/ocr_det.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/ocr_rec.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/paddleocr.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/paddleocr_utility.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/paddlestructure.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/postprocess_op.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/preprocess_op.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/structure_layout.cpp
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/src/structure_table.cpp
)
add_library(ANSLPR SHARED
${ANSLPR_HEADERS}
${ANSLPR_SOURCES}
${ANSLPR_INCLUDE_HEADERS}
${PADDLE_SOURCES}
)
target_include_directories(ANSLPR PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(ANSLPR PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/engines/ONNXEngine
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/include
${CMAKE_SOURCE_DIR}/engines/TensorRTAPI/include
${CMAKE_SOURCE_DIR}/modules/ANSODEngine
${CMAKE_SOURCE_DIR}/modules/ANSOCR
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR
${CMAKE_SOURCE_DIR}/modules/ANSOCR/ANSPaddleOCR/include
${SHARED_INCLUDE_DIR}
)
target_link_libraries(ANSLPR
PUBLIC ANSLibsLoader
PRIVATE ANSODEngine
PRIVATE ANSLicensingSystem
PRIVATE ANSMOT
PRIVATE labview
PRIVATE spdlog_dep
PRIVATE opencv
PRIVATE onnxruntime
PRIVATE tensorrt
PRIVATE openvino
PRIVATE CUDA::cudart
)
target_compile_definitions(ANSLPR PRIVATE UNICODE _UNICODE
ANSLPR_EXPORTS
_USRDLL
ANSLPR_USE_CUDA
LPR_EDITOR_USE_CUDA
)
target_precompile_headers(ANSLPR PRIVATE pch.h)

1091
modules/ANSLPR/dllmain.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define NOMINMAX // Prevent windows.h from defining min/max macros
// which break std::min / std::max (C2589)
// Windows Header Files
#include <windows.h>

View File

@@ -0,0 +1,68 @@
/*
// Line.h: interface for the C_Line class.
//////////////////////////////////////////////////////////////////////
*/
// Levenshtein.h: interface for the Levenshtein class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(LEVENSHTEIN_H)
#define LEVENSHTEIN_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <cstddef>
/// \brief une classe qui sert <20> calculer la distance entre deux chaines de caracters
/*! \class C_OCROutput
* \brief une classe qui sert <20> calculer la distance entre deux chaines de caracters
**
*/
class Levenshtein
{
public:
//****************************
// Get minimum edit (levenstein) between two strings
//****************************
[[nodiscard]] int Get (const char* a, const char* b);
[[nodiscard]] int Get (const char* a, size_t aLen, const char* b, size_t bLen);
[[nodiscard]] int Get2 (char const *s, char const *t);
[[nodiscard]] int Get2 (char const *s, size_t n, char const *t, size_t dst);
//**************************
// construct/destruct
//**************************
Levenshtein();
virtual ~Levenshtein();
private:
//****************************
// Get minimum of three values
//****************************
int Minimum (int a, int b, int c)
{
int mi = a;
if (b < mi) mi = b;
if (c < mi) mi = c;
return mi;
}
//**************************************************
// Get a pointer to the specified cell of the matrix
//**************************************************
int *GetCellPointer (int *pOrigin, size_t col, size_t row, size_t nCols)
{ return pOrigin + col + (row * (nCols + 1)); }
//*****************************************************
// Get the contents of the specified cell in the matrix
//*****************************************************
int GetAt (int *pOrigin, size_t col, size_t row, size_t nCols)
{
int *pCell = GetCellPointer (pOrigin, col, row, nCols);
return *pCell;
}
//********************************************************
// Fill the specified cell in the matrix with the value x
//********************************************************
void PutAt (int *pOrigin, size_t col, size_t row, size_t nCols, int x)
{
int *pCell = GetCellPointer (pOrigin, col, row, nCols);
*pCell = x;
}
};
#endif // !defined(LEVENSHTEIN_H)

View File

@@ -0,0 +1,192 @@
/*
// Line.h: interface for the C_Line class.
//////////////////////////////////////////////////////////////////////
*/
#if !defined(AFX_LINE_H__E3A0AA20_F151_11D5_B96A_CCFB30288840__INCLUDED_)
#define AFX_LINE_H__E3A0AA20_F151_11D5_B96A_CCFB30288840__INCLUDED_
#include <list>
#include <opencv2/opencv.hpp>
#ifdef _DEBUG
#include <cassert>
#endif //_DEBUG
//**************************
// conversion between number types
//**************************
inline float doubleToFloat(const double& value)
{
#ifdef LPR_PRINTF
if ((value > FLT_MAX) || (value < -FLT_MAX))
{
printf("Probleme de depassement lors d'un cast de DOUBLE en FLOAT\n");
}
#endif
return static_cast<float>(value);
}
inline float intToFloat(int value)
{
return static_cast<float>(value);
}
inline float int64ToFloat(const int64_t& value)
{
return static_cast<float>(value);
}
inline int floatToInt(float value)
{
#ifdef LPR_PRINTF
if ((value > INT_MAX) || (value < -INT_MAX))
{
printf("Probleme de depassement lors d'un cast de float en int\n");
}
#endif //LPR_PRINTF
return static_cast<int>(value);
}
/// \brief une classe qui modelise une droite dans un repere, donn<6E>e par son ordonn<6E>e <20> l'origine et sa pente
/*! \class C_OCROutput
* \brief a class that models a line in a frame, given by its intercept and its slope
**
*/
class C_Line
{
public:
//true if the line doesnot have too large slope and not large ordonnee a l'origine
inline bool is_valid() const
{
return (fabsf(a)<FLT_MAX && fabsf(b)<FLT_MAX);
};
//true if the line is (nearly) a vertical one
inline bool is_vertical() const
{
return fabsf(a)>FLT_MAX*0.5f;
};
void reset();
//determine si les deux droites sont paralleles
inline bool is_parallel(const C_Line & line_) const
{
const float MAX_FLT_ERROR=0.000001f;
return (fabsf(a-line_.a)<MAX_FLT_ERROR);
};
//retourne l'image de abscisse par la fonction affi,e definie par la droite
int get_image_entiere(const int abscisse) const;
//retourne l'image de abscisse par la fonction affi,e definie par la droite
float get_image(const int abscisse) const;
//retourne l'image de abscisse par la fonction affi,e definie par la droite
float get_image(const float& abscisse) const;
//retourne le coefficient directeur
inline float get_skew() const{
return a;
};
//retour l'angle de la droite par rapport a l'horizontale
inline float get_skew_angle() const{
return static_cast<float>(atanf(a));
};
//retour l'angle de la droite par rapport a l'horizontale
inline float get_skew_angle(const C_Line & l) const{
return (l.get_skew_angle()- atanf(a));
};
/**
@brief cette fonction donne l'angle de l'inclinaison d'un caractere
cette fonction sert effectueer une correction de l'inclinaison des characteres
cette fonction est utilise apres rotation de l'image de la plaque. Elle doit d'abord dterminer
si les characteres ainsi reforms prsentent un angle de slant.
cette fonction effectuee une correction de l'inclinaison des characteres
this function uses the vanishing point detection algorithm, based
on the concept of clustering line intersections based on the density of line intersections in a local region.
@return the slant angle of the line of characters
@see
*/
inline float get_slant_angle() const{
const float pi_sur_2=1.570796327f;
float slant_angle(atanf(a));
#ifdef _DEBUG
assert(slant_angle > -pi_sur_2 - .1 && slant_angle < pi_sur_2 + .1);
#endif //_DEBUG
if(slant_angle>0) {
// * *******
// * * *
// * * *
// * *******
// * *
// * *
// * *
// * *
//In that case the slant is positive
//correction
// Y'=y
// X'= x-y*cos(teta)
// X'= x-y*sin (ret)
slant_angle-=pi_sur_2;
#ifdef _DEBUG
assert(-slant_angle>-FLT_EPSILON);
#endif //_DEBUG
}
else {
// * *******
// * * *
// * * *
// * *******
// * *
// * *
// * *
// **
//In that case the slant is negative
//il faut que ret <0
slant_angle+=pi_sur_2;
#ifdef _DEBUG
assert(-slant_angle<FLT_EPSILON);
#endif //_DEBUG
}
#ifdef _DEBUG
assert(slant_angle > -pi_sur_2 - .1 && slant_angle < pi_sur_2 + .1);
#endif //_DEBUG
return -slant_angle;
};
inline bool is_x_axis() const{
if(fabsf(a)>FLT_EPSILON) return false;
else return(fabsf(b)<FLT_EPSILON);
};
//l'ordonnee a l'origine
float b;
//le coefficient directeur
float a;
//**************************
// construct/destruct
//**************************
C_Line();
C_Line(const C_Line & line_);
C_Line(const float & a_,const float & b_);
C_Line(const cv::Point2f& A, const cv::Point2f& B);
virtual ~C_Line();
//retourne le pt d'inter de C_Line avec la dte horiz y=ordonnee
float get_abs(const int ordonnee) const;
float get_abs(const float ordonnee) const;
bool is_horizontale() const
{
return(fabsf(a)<FLT_EPSILON);
};
bool is_nearly_the_same(const C_Line & right_op) const;
//returns the distance (positive or negative) of the point from its vertical projection on the line
float difference_y(const cv::Point2f& pt) const;
//retourne la distance du point a sa projection verticale sur la droite
float dist_y(const cv::Point& pt) const;
//returns the distance of the point from its vertical projection on the line
float dist_y(const cv::Point2f& pt) const;
//returns sum of distances of a list of points from their vertical projections on the line
float dist_y(const std::list<cv::Point2f>& points) const;
//returns the distance of the center point of a box from its vertical projection on the line
float dist_y_from_center(const cv::Rect& box) const;
//returns sum of distances of the center points of a list of boxes,from their vertical projections on the line
float dist_y_from_center(const std::list<cv::Rect>& boxes) const;
//returns sum of distances (which can be positive or negative) of the center points of a list of boxes,from their vertical projections on the line
float difference_y_from_center(const std::list<cv::Rect>& boxes) const;
//returns the distance (which can be positive or negative) of the center point of a box from its vertical projection on the line
float difference_y_from_center(const cv::Rect& box) const;
float dist_y(const std::list<cv::Point>& points) const;
//returns average distance of the center points of a list of boxes,from their vertical projections on the line
float error(const std::list<cv::Rect>& boxes) const;
//returns average distance of a list of points from their vertical projections on the line
float error(const std::list<cv::Point2f>& points) const;
protected:
//true if the line is nearly horizontal
bool is_nearly_horiz() const;
};
#endif // !defined(AFX_LINE_H__E3A0AA20_F151_11D5_B96A_CCFB30288840__INCLUDED_)

View File

@@ -0,0 +1,68 @@
/*
// Line.h: interface for the C_Line class.
*/
#if !defined(ONNX_RUNTIME_DETECTOR_H)
#define ONNX_RUNTIME_DETECTOR_H
# pragma once
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <functional>
#include <onnxruntime_c_api.h>
#include <onnxruntime_cxx_api.h>
#ifdef ANSLPR_USE_CUDA
#include <cpu_provider_factory.h>
#endif //ANSLPR_USE_CUDA
#include "utils_alpr_detect.h"
class OnnxDetector {
public:
/***
* @brief constructor
* @param model_path - path of the TorchScript weight file
*/
OnnxDetector(Ort::Env& env, const void* model_data, size_t model_data_length, const Ort::SessionOptions& options);
OnnxDetector(Ort::Env& env, const ORTCHAR_T* model_path, const Ort::SessionOptions& options);
void dump() const;
/***
* @brief inference module
* @param img - input image
* @param conf_threshold - confidence threshold
* @param iou_threshold - IoU threshold for nms
* @return detection result - bounding box, score, class index
*/
std::vector<std::vector<Detection>>
Run(const cv::Mat& img, float conf_threshold, float iou_threshold, bool preserve_aspect_ratio);
/***
* @brief inference module
* @param img - input image
* @param conf_threshold - confidence threshold
* @param iou_threshold - IoU threshold for nms
* @return detection result - bounding box, score, class index
*/
std::list<std::vector<std::vector<Detection>>>
Run(const cv::Mat& img, float iou_threshold);
/***
* @brief
* @return the maximum size of input image (ie width or height of dnn input layer)
*/
int64_t max_image_size() const;
bool is_valid() const {
return (session.GetInputCount() > 0 && session.GetOutputCount() > 0);
}
protected:
//session options are created outside the class. The classifier access to its options through a constant reference
const Ort::SessionOptions & sessionOptions;
Ort::Session session;
//ONNX environment are created outside the class. The classifier access to its envirponment through a constant reference
const Ort::Env& env;
};
//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);
//standard scalar product
template <typename T>
T vectorProduct(const std::vector<T>& v)
{
return std::accumulate(v.begin(), v.end(), 1, std::multiplies<T>());
}
#endif // !defined(ONNX_RUNTIME_DETECTOR_H)

View File

@@ -0,0 +1,106 @@
// StatSommesX_Y_H_dbl.h: interface for the C_SumsRegLineXYHDbl class.
//
#if !defined(AFX_STATSOMMESX_Y_H_DBL_H__EA007980_0205_11D6_B96A_DB3892D34B43__INCLUDED_)
#define AFX_STATSOMMESX_Y_H_DBL_H__EA007980_0205_11D6_B96A_DB3892D34B43__INCLUDED_
#ifndef GITHUB
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "Line.h"
/// class used in segmentation algo filtering method
/**cette classe sert a calculer des sommes moyennes et
variances necessaires a l'tabissement d'une droite de regresssion*/
/**cette classe sert a calculer des sommes moyennes et
variances necessaires a l'tabissement d'une droite de regresssion*/
class C_SumsRegLineXYHDbl
{
public:
//! to reset the structure.
/*!
this function reassign to the member their default value.
*/
void clear();
//gets the barycenter of the points cloud
cv::Point2f barycenter(const int nb_elements);
C_Line regression_line(const int nb_elements);
float pente(const int nb_elements) const;
#ifdef _DEBUG
bool debug(const float & somme_x_,
const float & somme_y_,
const float & produit_xy_,
const float & somme_carre_x_) const;
bool debug(const float & somme_x_,
const float & somme_y_,
const float & produit_xy_,
const float & somme_carre_x_,
const int heigth_) const;
#endif //_DEBUG
//**************************
// construct/destruct
//**************************
C_SumsRegLineXYHDbl();
C_SumsRegLineXYHDbl(const int somme_hauteurs_);
virtual ~C_SumsRegLineXYHDbl();
//sum of x coordinates of points cloud
float somme_x;
//sum of y coordinates of points cloud
float somme_y;
//sum of x by y coordinates product of points cloud
float produit_xy;
//sum of squared x coordinates of points cloud
float somme_carre_x;
//sum of heights of bouding boxes (not used in that project)
int somme_hauteurs;
inline void add(const float& x, const float& y, const int hauteur) {
somme_x += x;
somme_y += y;
produit_xy += x*y;
somme_carre_x += x*x;
somme_hauteurs += hauteur;
}
inline void operator+=(const cv::Point2f & Center) {
somme_x += Center.x;
somme_y += Center.y;
produit_xy += Center.x*Center.y;
somme_carre_x += Center.x*Center.x;
}
inline void operator-=(const cv::Point2f & Center) {
somme_x -= Center.x;
somme_y -= Center.y;
produit_xy -= Center.x*Center.y;
somme_carre_x -= Center.x*Center.x;
#ifdef _DEBUG
assert(somme_x >=0 && somme_y >=0 &&
produit_xy >=0 && somme_carre_x>=0);
#endif //_DEBUG
}
inline void substract(const float & x, const float & y, const int hauteur) {
somme_x -= x;
somme_y -= y;
produit_xy -= x*y;
somme_carre_x -=x*x;
#ifdef _DEBUG
assert(somme_x >=0 && somme_y >=0 &&
produit_xy >=0 && somme_carre_x>=0);
#endif //_DEBUG
somme_hauteurs -= hauteur;
}
inline void operator+=(const C_SumsRegLineXYHDbl & stat) {
somme_x += stat.somme_x;
somme_y += stat.somme_y;
produit_xy += stat.produit_xy;
somme_carre_x += stat.somme_carre_x;
somme_hauteurs+=stat.somme_hauteurs;
}
inline void subtract(const cv::Point2f & Center, const int hauteur) {
somme_x -= Center.x;
somme_y -= Center.y;
produit_xy -= Center.x*Center.y;
somme_carre_x-=Center.x*Center.x;
somme_hauteurs-=hauteur;
}
bool operator ==(const C_SumsRegLineXYHDbl & right_op) const;
};
#endif //GITHUB
#endif // !defined(AFX_STATSOMMESX_Y_H_DBL_H__EA007980_0205_11D6_B96A_DB3892D34B43__INCLUDED_)

View File

@@ -0,0 +1,810 @@
/*
// Line.h: interface for the C_Line class.
*/
#if !defined(UTILS_ALPR_ONNX_H)
#define UTILS_ALPR_ONNX_H
#pragma once
#include <opencv2/core.hpp>
#include "Levenshtein.h"
#include "Line.h"
enum Det {
tl_x = 0,
tl_y = 1,
br_x = 2,
br_y = 3,
score = 4,
class_idx = 5
};
struct Detection {
cv::Rect bbox;
float score;
int class_idx;
};
struct LPN_signature {
int nb_caracs_on_first_line;
int nb_caracs_on_second_line;
int nb_caracs_on_third_line;
};
//std::string getTrueLPN(const std::string& filename, const bool& vrai_lpn_after_underscore);
/**
@brief
//rearrange detected bounding boxes from left to right
@param[out] boxes : set of detected boxes
@param[out] confidences : confidences detected boxes
@param[out] classIds : std::list of indeces that indicate the classes of each of the above detected boxes
@return void
@see
*/
void sort_from_left_to_right(std::list<cv::Rect>& boxes, std::list<float>& confidences, std::list<int>& classIds);
/**
@brief
@param r1 : first rectangle
@param r2 : second rectangle
@return the intersection rect of the rectangles
@see
*/
cv::Rect get_inter(const cv::Rect& r1, const cv::Rect& r2);
/**
@brief
//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)
@param box : box a bounding box
@param rect_im : ROi or second bounding box
@return true if intersection is at least 90% of the box (which means box is nearly entirely in the second argument)
@see
*/
bool is_in_rect_if(const cv::Rect& box, const cv::Rect& rect_im);
/**
@brief
//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)
@param box : box a bounding box
@param rect_im : ROi or second bounding box
@return true if intersection is at least 90% of the box (which means box is nearly entirely in the second argument)
@see
*/
bool is_in_rect_if(const cv::Rect& box, const cv::Rect& rect_im, const float min_area_ratio);
/**
@brief
///from all heights of the boxes, get the median value
@param[in] boxes : set of detected boxes
@return the median height of the boxes
@see
*/
int get_median_height(const std::list<cv::Rect>& boxes);
/**
@brief
//returns the iou (intersection over union) of two boxes
@param r1 : first rectangle
@param r2 : second rectangle
@return the iou (a float value between 0 and 1)
@see
*/
float iou(const cv::Rect& r1, const cv::Rect& r2);
/**
@brief
//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
@param[out] boxes : set of detected boxes
@param[out] confidences : confidences detected boxes
@param[out] classIds : std::list of indeces that indicate the classes of each of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@return void
@see
*/
void filter_iou2(
std::list<cv::Rect>& boxes,
std::list<float>& confidences,
std::list<int>& classIds, float nmsThreshold = 0.5f);
/** @brief get barycenters of a list of bounding boxes.
* @param[in] boxes : bounding boxes.
* @param[out] les_points : the barycenters of the above boxes.
* @return void
* @see
*/
void get_barycenters(const std::list<cv::Rect>& boxes, std::list<cv::Point2f>& les_points);
/**
@brief
//checks if the bounding boxes are ordered from left to right
@param[in] boxes : set of detected boxes
@return true if the bounding boxes are ordered from left to right
@see
*/
#ifdef _DEBUG
//cette fonction verifie que la liste est triee de gauche a droite
bool debug_left(const std::list<cv::Rect>& boxes);
#endif //_DEBUG
/**
@brief
//examines how boxes are disposed and filter out boxes with a position that are incompatible with the positions of other boxes
@param[in] boxes : set of detected boxes
@param[out] angles_with_horizontal_line : angles determined by segment joining centroids of two consecutives boxes and the horizontal line
@param[out] mean_angles_par_rapport_horiz : mean of the above angles - roughly the tilt of the license plate
@param[out] standard_deviation_consecutives_angles : standard deviation of the above angles (normally a small value, since boxes should be aligned)
@param[out] interdistances : distances between centroids of two consecutives boxes
@param[out] mean_interdistances : mean of the above distances
@param[out] standard_deviation_interdistances : standard deviation of the above distances
@param[out] mean_produit_interdistance_avec_angle : parameter produced by the algorithm
@param[out] standard_deviation_produit_interdistance_avec_angle : parameter produced by the algorithm
@return void
@see
*/
void filtre_grubbs_sides(const std::list<cv::Rect>& boxes, std::list<float>& angles_with_horizontal_line,
float& mean_angles_par_rapport_horiz,
float& standard_deviation_consecutives_angles,
std::list<int>& interdistances,
float& mean_interdistances,
float& standard_deviation_interdistances,
float& mean_produit_interdistance_avec_angle, float& standard_deviation_produit_interdistance_avec_angle);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] l_confidences : confidences of detected boxes
@param[in] l_classIds : classes indeces of detected boxes.
@param[out] l_reordered : std list of the detected boxes when they are rearranged in the order of the registration string
@param[out] l_reordered_confidences : confidences of the above boxes
@param[out] l_reordered_classIds : classes indeces of the above boxes.
@param[out] levels : levels are int that indicates on which line of the license plate the box lies : -1 = upper line, +1 = lower line, 0 if the license plate have just one line
@return void
@see
*/
void is_bi_level_plate(const std::list<cv::Rect>& boxes, const std::list<float>& l_confidences, const std::list<int>& l_classIds,
std::list<cv::Rect>& l_reordered, std::list<float>& l_reordered_confidences, std::list<int>& l_reordered_classIds, std::list<int>& levels);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] l_classIds : classes indeces of detected boxes.
@param[out] l_reordered : std list of the detected boxes when they are rearranged in the order of the registration string
@param[out] l_reordered_classIds : classes indeces of the above boxes.
@param[out] levels : levels are int that indicates on which line of the license plate the box lies : -1 = upper line, +1 = lower line, 0 if the license plate have just one line
@return void
@see
*/
void is_bi_level_plate(const std::list<cv::Rect>& boxes, const std::list<int>& l_classIds,
std::list<cv::Rect>& l_reordered, std::list<int>& l_reordered_classIds, std::list<int>& levels);
//returns true if we can merge two list of characters bounding boxes on a single row
bool merge_is_acceptable(const std::list<cv::Rect>& boxes_on_one_line, const std::list<cv::Rect>& boxes_on_another_line);
/**
@brief
//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, ...)
@param[in] input : a character (of the license plate).
@return 1 if the carac is a digit (0...9), 0 if the carac is a letter (A,B,...,Z), -1 else
@see
*/
int is_digit(const char input);
//removes from lists every box which id doenot correspond to a lp character
void filter_out_everything_but_characters(std::list<cv::Rect>& boxes,
std::list<int>& l_classIds);
//removes from lists every box which id doenot correspond to a lp character
void filter_out_everything_but_characters(std::list<cv::Rect>& boxes,
std::list<float>& l_confidences, std::list<int>& l_classIds);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@return the lpn extracted out of detections
@see
*/
std::string get_single_lpn(
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, float nmsThreshold = 0.5f);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@return the lpn extracted out of detections
@see
*/
std::string get_single_lpn(
const std::vector<cv::Rect>& boxes,
const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<int>& tri_left_classIds, float nmsThreshold = 0.5f);
//
/**
@brief : 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
@param[in] classId: current box class index
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return single character--> returns 1 or license plate--> returns 2 or vehicle--> returns 3 or negative index--> returns 0 must be an error
@see
*/
int is_this_box_a_character_a_license_plate_or_a_vehicle(const int classId, const int classId_last_country);
//
//classId_last_country : is the class index of the last country in the list of detected classes.
/***
* @brief
@param[in] classId: current box class index
* @param[in] number_of_characters_latin_numberplate : number of characters in a latin alphabet (usually 36 = 26 letters + 10 digits)
* @return //single character--> returns 1
//license plate--> returns 2
//negative index--> returns 0 must be an error
*/
int is_this_box_a_character(const int classId, const int number_of_characters_latin_numberplate);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void group_characters_in_the_same_license_plate(
//raw detections
const std::list<cv::Rect>& boxes,
const std::list<float>& confidences, const std::list<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::list<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country
);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void group_characters_in_the_same_license_plate(
//raw detections
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country
);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void group_characters_in_the_same_license_plate(
//raw detections
const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::list<int>>& l_vect_of_classIds_in_a_license_plate
, const int classId_last_country
);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void group_characters_in_the_same_license_plate(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections of same lp are regrouped in a vector
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list < std::vector<int>>& l_vect_of_classIds_in_a_license_plate, const int classId_last_country
//classId_last_country : is the class index of the last country in the list of detected classes.
);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
int is_detections_of_a_unique_license_plate(const std::vector<int>& classIds);
//1;->ok
//2;->size too small
//4;->second detection is not a vehicle
//6;->detection after first two ones, is not a character
int is_detections_of_a_unique_license_plate(const std::list<int>& classIds);
//From confidences of detections of all boxes of a plate, we get the average confidence.
float get_average_confidence_of_license_plate(const std::vector<int>& classIds,
const std::vector<float>& confidences);
//From confidences of detections of all boxes of a plate, we get the average confidence.
float get_average_confidence_of_license_plate(const std::list<int>& classIds,
const std::list<float>& confidences);
/**
@brief
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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] one_lp :set of detected boxes when they rearranged from left to right
@param[out] confidence_one_lp : confidences corresponding detected boxes
@param[out] classIds_one_lp : set of indeces of the above detected boxes
@return void
@see
*/
void get_best_plate(
//detections when they are separated license plates by license plates
const std::list < std::vector<int>>& classIds, const std::list < std::vector<float>>& confidences, const std::list < std::vector<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp);
/**
@brief
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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] one_lp :set of detected boxes when they rearranged from left to right
@param[out] confidence_one_lp : confidences corresponding detected boxes
@param[out] classIds_one_lp : set of indeces of the above detected boxes
@return void
@see
*/
void get_best_plate(
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp);//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.
/**
@brief
given the boxes+confidences+classIds and given the actual lpn string in the image (ExactLPN), outputs the lpn that is closest to ExactLPN
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.
@param[in] ExactLPN : the actual license plate number in the image
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] one_lp :set of detected boxes when they rearranged from left to right
@param[out] confidence_one_lp : confidences corresponding detected boxes
@param[out] classIds_one_lp : set of indeces of the above detected boxes
@return void
@see
*/
void get_best_plate(const std::string& ExactLPN,
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp);
/**
@brief
given the boxes+confidences+classIds and given the actual lpn string in the image (ExactLPN), outputs the lpn that is closest to ExactLPN
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.
@param[in] ExactLPN : the actual license plate number in the image
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] one_lp :set of detected boxes when they rearranged from left to right
@param[out] confidence_one_lp : confidences corresponding detected boxes
@param[out] classIds_one_lp : set of indeces of the above detected boxes
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
*/
void get_best_plate(const std::string& ExactLPN,
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::vector<float>& confidence_one_lp, std::vector < cv::Rect>& one_lp, std::vector<int>& classIds_one_lp);
//this function adds if they dont already exist, a roi for the licene plate (equal to the global rect englobing the boxes) and a blank rect for the vehicle box
void add_lp_and_vehicle(const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes, std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds
, const int classId_last_country
);
//
/** @brief
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
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void add_lp_and_vehicle(const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
std::list<cv::Rect>& tri_left_vect_of_detected_boxes, std::list<float>& tri_left_confidences, std::list<int>& tri_left_classIds
, const int classId_last_country
);
//
/** @brief
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
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void add_lp_and_vehicle(const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes, std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds
, const int classId_last_country
);
//
/** @brief
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
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void add_lp_and_vehicle(const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
std::list<cv::Rect>& tri_left_vect_of_detected_boxes, std::list<float>& tri_left_confidences, std::list<int>& tri_left_classIds
, const int classId_last_country
);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return the lpn extracted out of detections
@see
*/
std::string get_best_lpn(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//characters inside the best lpn that have been chosen from the above double linked list
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, const float nmsThreshold = 0.5f, const int classId_last_country = 96
);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences of corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return the lpn extracted out of detections
@see
*/
std::string get_best_lpn(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//characters inside the best lpn that have been chosen from the above double linked list
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds, const float nmsThreshold = 0.5f, const int classId_last_country =//95
96
);
/**
@brief
//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
@param[in] one_lp : set of detected boxes
@param[in] confidence_one_lp : confidences of each bb detected by the dnn
@param[in] classIds_one_lp : set of indeces that indicate the classes of each of these detected boxes
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return the lpn extracted out of detections
@see
*/
void get_best_lpn(const std::list<cv::Rect>& one_lp, const std::list<float>& confidence_one_lp, const std::list<int>& classIds_one_lp,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes);
//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
/**
@brief
given the boxes+confidences+classIds and given the actual lpn string in the image (ExactLPN), outputs the lpn that is closest to ExactLPN
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classes : set of indeces that indicate the classes of each of these detected boxes
@param[in] ExactLPN : the actual license plate number in the image
@param[out] best_boxes :set of detected boxes that compose the lp that has the best confidence
@param[out] best_confidences : confidences corresponding detected boxes
@param[out] best_classes : set of indeces of the above detected boxes
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return edit distance to the actual lpn
*/
int find_nearest_plate_substitutions_allowed(const std::string& ExactLPN,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, const std::list <int>& lp_country_class, const std::list < cv::Rect>& lp_rois, const
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, const std::list < std::list<cv::Rect>>& boxes,
//output = nearest lpn + its class + its bounding box
std::string& best_lpn, int& best_country_class, cv::Rect& best_lpn_roi,
//output = characters in nearest lpn
std::list<float>& best_confidences, std::list<int>& best_classes, std::list<cv::Rect>& best_boxes);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] l_confidences : confidences of detected boxes
@param[in] l_classIds : classes indeces of detected boxes.
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@return the lpn extracted out of detections
@see
*/
std::string get_lpn(
const std::list<cv::Rect>& l_of_detected_boxes,
const std::list<float>& l_confidences, const std::list<int>& l_classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<float>& tri_left_confidences, std::vector<int>& tri_left_classIds,
float nmsThreshold = 0.5f
);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] l_confidences : confidences of detected boxes
@param[in] l_classIds : classes indeces of detected boxes.
@param[out] tri_left_vect_of_detected_boxes :set of detected boxes when they rearranged from left to right
@param[out] tri_left_confidences : confidences corresponding detected boxes
@param[out] tri_left_classIds : set of indeces of the above detected boxes
@param[in] nmsThreshold A threshold used in non maximum suppression.
@return the lpn extracted out of detections
@see
*/
std::string get_lpn(
const std::list<cv::Rect>& l_of_detected_boxes,
const std::list<int>& l_classIds,
std::vector<cv::Rect>& tri_left_vect_of_detected_boxes,
std::vector<int>& tri_left_classIds,
float nmsThreshold = 0.5f
);
/**
@brief
//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
@param[in] l_classIds : classes indeces of detected boxes.
@return the lpn extracted out of detections
@see
*/
std::string get_lpn(const std::list<int>& l_classIds);
/**
@brief
//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
@param[in] l_classIds : classes indeces of detected boxes.
@return the lpn extracted out of detections
@see
*/
std::string get_lpn(const std::vector<int>& l_classIds);
/**
@brief
//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
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[out] l_vect_of_boxes_in_a_license_plate_tri_left : double list of detected boxes when they rearranged in license plates from left to right
@param[out] l_vect_of_confidences_in_a_license_plate_tri_left : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate_tri_left : double list of corresponding indeces.
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::list<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, //classId_last_country : is the class index of the last country in the list of detected classes.
const float nmsThreshold = 0.5f
);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[out] l_vect_of_boxes_in_a_license_plate_tri_left : double list of detected boxes when they rearranged in license plates from left to right
@param[out] l_vect_of_confidences_in_a_license_plate_tri_left : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate_tri_left : double list of corresponding indeces.
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
//groups detected boxes that correspond to the same vehicle. The separation is based on detected license plates by the dnn
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::list<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::list<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::list<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, const float nmsThreshold
);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[out] l_vect_of_boxes_in_a_license_plate_tri_left : double list of detected boxes when they rearranged in license plates from left to right
@param[out] l_vect_of_confidences_in_a_license_plate_tri_left : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate_tri_left : double list of corresponding indeces.
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::vector<cv::Rect>& boxes, const std::vector<float>& confidences, const std::vector<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, const float nmsThreshold = 0.5f
);
/**
@brief
//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.
@param[in] boxes : set of detected boxes
@param[in] confidences : confidences of each bb detected by the dnn
@param[in] classIds : set of indeces that indicate the classes of each of these detected boxes
@param[out] l_vect_of_boxes_in_a_license_plate : double list of detected boxes when they are grouped. A single list contains all the boxes of a single vehicle present in image.
@param[out] l_vect_of_confidences_in_a_license_plate : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate : double list of corresponding indeces.
@param[out] l_vect_of_boxes_in_a_license_plate_tri_left : double list of detected boxes when they rearranged in license plates from left to right
@param[out] l_vect_of_confidences_in_a_license_plate_tri_left : double list of corresponding confidences.
@param[out] l_vect_of_classIds_in_a_license_plate_tri_left : double list of corresponding indeces.
@param[in] nmsThreshold A threshold used in non maximum suppression.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
@return void
@see
*/
void separate_license_plates_if_necessary_add_blank_vehicles(
//raw detections
const std::list<cv::Rect>& boxes, const std::list<float>& confidences, const std::list<int>& classIds,
//detections when they are separated license plates by license plates
std::list<std::string>& lpns, std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate,
//double lists (one element list for each lp detected) of detected characters inside a lp
std::list < std::vector<cv::Rect>>& l_vect_of_boxes_in_a_license_plate_tri_left,
std::list < std::vector<float>>& l_vect_of_confidences_in_a_license_plate_tri_left, std::list <std::vector<int>>& l_vect_of_classIds_in_a_license_plate_tri_left,
const int classId_last_country, const float nmsThreshold = 0.5f
);
//extracts, from a test directory, all images files
//void load_images_filenames(const std::string& dir, std::list<std::string>& image_filenames);
/***
* @brief Padded resize
* @param src - input image
* @param dst - output image
* @param out_size - desired output size
* @return padding information - pad width, pad height and zoom scale
*/
std::vector<float> LetterboxImage(const cv::Mat& src, cv::Mat& dst, const cv::Size& out_size = cv::Size(640, 640));
/***
* @brief Rescale coordinates to original input image
* @param data - detection result after inference and nms
* @param pad_w - width padding
* @param pad_h - height padding
* @param scale - zoom scale
* @param img_shape - original input image shape
*/
void ScaleCoordinates(std::vector<Detection>& data, float pad_w, float pad_h,
float scale, const cv::Size& img_shape);
/***
* @brief Performs Non-Maximum Suppression (NMS) on inference results
* @note For 640x640 image, 640 / 32(max stride) = 20, sum up boxes from each yolo layer with stride (8, 16, 32) and
* 3 scales at each layer, we can get total number of boxes - (20x20 + 40x40 + 80x80) x 3 = 25200
* @param detections - inference results from the network, example [1, 25200, 85], 85 = 4(xywh) + 1(obj conf) + 80(class score)
* @param modelWidth - width of model input 640
* @param modelHeight - height of model input 640
* @param conf_threshold - object confidence(objectness) threshold
* @param iou_threshold - IoU threshold for NMS algorithm
* @return detections with shape: nx7 (batch_index, x1, y1, x2, y2, score, classification)
*/
std::vector<std::vector<Detection>> PostProcessing(
float* output, // output of onnx runtime ->>> 1,25200,85
size_t dimensionsCount,
size_t size, // 1x25200x85=2142000
int dimensions,
float modelWidth, float modelHeight, const cv::Size& img_shape,
float conf_threshold, float iou_threshold);
/***
* @brief Performs Non-Maximum Suppression (NMS) on inference results
* @note For 640x640 image, 640 / 32(max stride) = 20, sum up boxes from each yolo layer with stride (8, 16, 32) and
* 3 scales at each layer, we can get total number of boxes - (20x20 + 40x40 + 80x80) x 3 = 25200
* @param detections - inference results from the network, example [1, 25200, 85], 85 = 4(xywh) + 1(obj conf) + 80(class score)
* @param pad_w - width padding
* @param pad_h - height padding
* @param scale - zoom scale
* @param conf_threshold - object confidence(objectness) threshold
* @param iou_threshold - IoU threshold for NMS algorithm
* @return detections with shape: nx7 (batch_index, x1, y1, x2, y2, score, classification)
*/
std::vector<std::vector<Detection>> PostProcessing(
float* output, // output of onnx runtime ->>> 1,25200,85
size_t dimensionsCount,
size_t size, // 1x25200x85=2142000
int dimensions,
//pad_w is the left (and also right) border width in the square image feeded to the model
float pad_w, float pad_h, float scale, const cv::Size& img_shape,
float conf_threshold, float iou_threshold);
//if two boxes have an iou (intersection over union) that is too large, then they cannot represent two adjacent characters of the license plate
//so we discard the one with the lowest confidence rate
void filter_iou(std::vector<int>& classIds,
std::vector<float>& confidences,
std::vector<cv::Rect>& vect_of_detected_boxes, const float& nmsThreshold = 0.5f);
bool plates_types_differ_with_one_character(const std::string& type1, std::string& lpn
, const std::string& type2);
//this func extract all info from the filenames of the platesmania dataset :
std::string get_plate_sub_type(const std::string& lpn);
//this func extract all info from the filenames of the platesmania dataset :
std::string get_plate_sub_type(const std::list<char>& lpn);
std::vector<std::string> get_plates_types_labels(const std::string& filename);
#endif // !defined(UTILS_ANLR_ONNX_H)

View File

@@ -0,0 +1,40 @@
/*
// Line.h: interface for the C_Line class.
*/
#if !defined(UTILS_IMAGE_FILE_H)
#define UTILS_IMAGE_FILE_H
#pragma once
#include <list>
#include "Levenshtein.h"
#include <opencv2/core.hpp>
/**
@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);
/**
@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);
/**
@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
*/
//retourne l'index du caractere LPChar
int get_index(const char LPChar);
std::string getTrueLPN(const std::string& filename, const bool& vrai_lpn_after_underscore);
//extracts from a test directory all images files
void load_images_filenames(const std::string& dir, std::list<std::string>& image_filenames);
#endif // !defined(UTILS_IMAGE_FILE_H)

View File

@@ -0,0 +1,373 @@
#if !defined(UTILS_OPEN_CV)
#define UTILS_OPEN_CV
#include "Line.h"
#include <opencv2/opencv.hpp>
//#include <opencv2/text.hpp>
#define cvGetSeqElemLPR( param1, param2, param3) cvGetSeqElem( param1, param2)
/**
@brief
//for each box in the container, check that it is nearly entirely contained in the second argument
@param box : box a bounding box
@param rect_im : ROi or second bounding box
@return true if intersection is at least 90% of the box (which means box is nearly entirely in the second argument)
@see
*/
//for each box in the container, check that it is nearly entirely contained in the second argument
bool is_in_rect_if(const std::list<cv::Rect>& boxes, const cv::Rect& rect_im);
/**
@brief
//for each box in the container, check that it is nearly entirely contained in the second argument
@param box : box a bounding box
@param rect_im : ROi or second bounding box
@return true if intersection is at least 90% of the box (which means box is nearly entirely in the second argument)
@see
*/
//for each box in the container, check that it is nearly entirely contained in the second argument
bool is_in_rect_if(const std::vector<cv::Rect>& boxes, const cv::Rect& rect_im);
/**
@brief
//returns the iou (intersection over union) of two boxes
@param r1 : first rectangle
@param r2 : second rectangle
@return the iou (a float value between 0 and 1)
@see
*/
//float iou(const cv::Rect& r1, const cv::Rect& r2);
std::list<cv::Rect> fiter_out(
const std::vector<cv::Rect>& true_boxes, // the true boxes extracted from pascal voc xml file, as a list
const std::list<cv::Rect>& detected_boxes, // the boxes detected by nn detector, stored in a list of rectagles objects
const std::list<float>& ious,
std::list<float>& out_ious);
std::list<cv::Rect> fiter_out(
const std::list<cv::Rect>& true_boxes, // the true boxes extracted from pascal voc xml file, as a list
const std::list<cv::Rect>& detected_boxes, // the boxes detected by nn detector, stored in a list of rectagles objects
const std::list<float>& ious,
std::list<float>& out_ious);
float iou(
const std::list<cv::Rect>& true_boxes, // the true boxes extracted from pascal voc xml file, as a list
const std::list<cv::Rect>& detected_boxes // the boxes detected by nn detector, stored in a list of rectagles objects
);
float iou(
const std::list<cv::Rect>& boxes, // the true boxes extracted from pascal voc xml file, as a list
const cv::Rect& box // the boxes detected by nn detector, stored in a list of rectagles objects
);
/**
@brief
//rearrange bounding boxes from left to right
cette fonction trie la liste de gauche a droite
@return reordered set of boxes
@see
*/
std::list<cv::Rect> sort_from_left_to_right(const std::list<cv::Rect>& boxes);
/**
@brief
//rearrange bounding boxes from left to right
cette fonction trie la liste de gauche a droite
*@param[in] number_of_characters_latin_numberplate : number of characters in a latin alphabet(usually 36 = 26 letters + 10 digits)
@return reordered set of boxes
@see
*/
std::list<cv::Rect> sort_from_left_to_right(const std::list<cv::Rect>& boxes, const std::list<int>& classes, std::list<int>& sorted_classes,
const int number_of_characters_latin_numberplate
//, const int index_first_mark_model
);
/**
@brief
//rearrange bounding boxes from left to right
cette fonction trie la liste de gauche a droite
@return reordered set of boxes
@see
*/
void sort_from_left_to_right(
std::list<cv::Rect>& boxes, std::list<float>& confidences,
std::list<int>& classIds_);
/**
@brief
cette fonction trie la liste de gauche a droite
//rearrange detected bounding boxes from left to right
@param[out] :std list of detected boxes
@param[out] confidences : confidences of detected boxes
@param[out] classIds : std::list of indeces that indicate the classes of each of the above detected boxes
@return void
@see
*/
void sort_from_left_to_right(
std::list<cv::Rect>& boxes,
std::list<int>& classIds_);
bool is_on_the_left(const cv::Rect& box1, const cv::Rect& box2);
//
/**
@brief cette fonction trie la liste de gauche a droite
//rearrange detected bounding boxes from left to right
@param[out] :std list of detected boxes
@param[out] confidences : confidences of detected boxes
@param[out] classIds : std::list of indeces that indicate the classes of each of the above detected boxes
@return void
@see
*/
void sort_from_left_to_right(
std::list<cv::Rect>& boxes, std::list<float>& confidences);
bool is_in_rect(const std::list<cv::Rect>& boxes, const cv::Rect& rect_im);
bool is_in_rect(const cv::Rect& box, const cv::Rect& rect_im);
bool is_in_rect(const cv::Point& pt, const cv::Rect& rect_im);
//type_of_roi_for_iou==1 large
//type_of_roi_for_iou ==2 right_side
//type_of_roi_for_iou == 3 left side
//type_of_roi_for_iou ==0 no expansion
//float detect(const cv::Mat & frame, const std::list<cv::Rect>& boxes, const cv::Rect& box, const int w, const int type_of_roi_for_iou);
void get_mean_std(const cv::Mat & frame, const cv::Rect& box, float& mean, float& standard_deviation);
bool get_upperand_lower_lines
(const std::list<cv::Rect>& boxes, C_Line& line_sup, C_Line& line_inf);
struct MSERParams
{
MSERParams(int _delta = 5, int _min_area = 60, int _max_area = 14400,
double _max_variation = 0.25, double _min_diversity = .2,
int _max_evolution = 200, double _area_threshold = 1.01,
double _min_margin = 0.003, int _edge_blur_size = 5)
{
delta = _delta;
minArea = _min_area;
maxArea = _max_area;
maxVariation = _max_variation;
minDiversity = _min_diversity;
maxEvolution = _max_evolution;
areaThreshold = _area_threshold;
minMargin = _min_margin;
edgeBlurSize = _edge_blur_size;
pass2Only = false;
}
int delta;
int minArea;
int maxArea;
double maxVariation;
double minDiversity;
bool pass2Only;
int maxEvolution;
double areaThreshold;
double minMargin;
int edgeBlurSize;
};
/*
bool trouve_la_plaque(const cv::Mat& frame
, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_,
cv::Rect& rect_OpenLP);*/
bool trouve_la_plaque(const cv::Mat& frame, const cv::Rect& global_rect
, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_,
cv::Rect& rect_OpenLP);
cv::Rect get_global_rect(const cv::Point& bottom_right,
const cv::Point& bottom_left, const cv::Point& top_right
, const cv::Point& top_left);
//cette fonction retourne le rect englobant la collection
//gets the reunion of all the boxes
cv::Rect get_global_rect(const std::list<cv::Rect>& l);
//cette fonction retourne le rect englobant la collection
//gets the reunion of all the boxes
cv::Rect get_global_rect(const std::vector<cv::Rect>& l);
void vector_to_list(
const std::vector<cv::Rect>& boxes,
std::list<cv::Rect>& lboxes);
bool trouve_la_plaque(const cv::Mat& frame, const cv::Rect& global_rect
, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_,
cv::Rect& rect_OpenLP);
bool trouve_la_plaque(const cv::Mat& frame,
const std::list<int>& classes, const std::list<cv::Rect>& boxes
,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_, cv::Rect& rect_OpenLP);
bool in_quadrilatere(const cv::Point& pt,
const cv::Point& top_left, const cv::Point& top_right, const cv::Point& bottom_right, const cv::Point& bottom_left, const float& signed_distance);
bool in_quadrilatere(const cv::Point& pt, const std::vector<cv::Point >& contours, const float& signed_distance);
bool in_quadrilatere(const cv::Rect& box, const std::vector<cv::Point >& contours, const float& signed_distance);
bool in_quadrilatere(const std::list<cv::Rect>& boxes, const std::vector<cv::Point >& contours, const float& signed_distance);
#include "opencv2/imgproc/imgproc_c.h"
float cosine(const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt0);
float cosine(const CvPoint* pt1, const CvPoint* pt2, const CvPoint* summit);
bool is_2D(const CvSeq* polygone);
int zappeUnPoint(const CvSeq* polygone, CvMemStorage* storage);
float dist(const CvPoint& pt1, const CvPoint& pt2);
CvPoint moyennepond(const CvPoint& pt1, const CvPoint& pt2, const int a1, const int a2);
CvPoint moyenne(const CvPoint& pt1, const CvPoint& pt2);
CvPoint moyenne4(const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3, const CvPoint& pt4);
CvPoint trouveleplusproche(const CvPoint& ref, const CvPoint& pt1,
const CvPoint& pt2, const CvPoint& pt3, const CvPoint& pt4);
CvSeq* sort(const CvSeq* note, CvMemStorage* storage);
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image img
// IplImage* img
// CvMemStorage* storage
//peut traiter les images sources couleur ou en niveaux de gris
CvSeq* findSquares4(IplImage* img, CvMemStorage* storage, const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int nb_composantes_color, const int dist_min_bord,
const int hauteur_plaque_min, const float& rapport_largeur_sur_hauteur_min);
CvSeq* findSquares4
(IplImage* im_src, const cv::Rect& global_rect, CvMemStorage* storage,
const int nTresh, const int mean_carac
, const int mean_fond, //
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
CvSeq* findSquares4
(IplImage* im_src, CvMemStorage* storage,
const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int nb_composantes_color,
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// renvoie une seuence de 4 points encadrant la plaque
// trouveesur l'image im_src
// IplImage* im_src
// CvMemStorage* storage
//peut traiter les images sources couleur ou en niveaux de gris
CvSeq* findSquares4
(IplImage* imgR, IplImage* imgG, IplImage* imgB, CvMemStorage* storage,
const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
CvSeq* findSquares4_multiresolution
(IplImage* im_src, CvMemStorage* storage,
const int nTresh, //
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
CvSeq* findSquares4_multiresolution(
IplImage* im_src, CvMemStorage* storage,
const int nTresh, const int mean_carac
, const int mean_fond, //
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
CvSeq* findSquares4_multiresolution
(
IplImage* im_src, const cv::Rect& global_rect, CvMemStorage* storage,
const int nTresh, const int mean_carac
, const int mean_fond, //
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min
);
CvSeq* findSquares4_multiresolution
(
IplImage* im_src, CvMemStorage* storage,
const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int nb_composantes_color,
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min
);
CvSeq* findSquares4_multiresolution
(IplImage* imgR, IplImage* imgG, IplImage* imgB, CvMemStorage* storage,
const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
/*
CvSeq* findSquares4
(IplImage* imgR, IplImage* imgG, IplImage* imgB, CvMemStorage* storage,
const int nTresh, //
//le nb de composantes couleurs ex nb_composantes_color=3 pour une image RGB
const int dist_min_bord,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);*/
//peut traiter les images sources couleur ou en niveaux de gris
/*
bool trouve_la_plaque(IplImage* etude, const int nTresh, const int nb_composantes_color,
int resultat[4][2], const int dist_min_bord,
const int hauteur_plaque_min, const float& rapport_largeur_sur_hauteur_min);*/
///////////////////////////////////////////////////////////////////////////////////////////
// retourne le nb de points en commun entre deux images
float percentage_match(IplImage* img1, IplImage* img2);
bool quadrilatere_is_convex(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3);
//must be convex quadrilatere
bool width_is_larger(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3);
//must be convex quadrilatere
bool get_corners(const cv::Point& pt0, const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3,
cv::Point& top_left, cv::Point& top_right, cv::Point& bottom_right, cv::Point& bottom_left);
//must be convex quadrilatere
bool get_corners(const IplImage* im, const CvPoint& pt0, const CvPoint& pt1, const CvPoint& pt2, const CvPoint& pt3,
cv::Point& top_left, cv::Point& top_right, cv::Point& bottom_right, cv::Point& bottom_left);
/*
bool trouve_la_plaque(IplImage* etude, int nTresh,
cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_, cv::Rect& rect_OpenLP,
const int dist_min_bord
,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
//peut traiter les images sources couleur ou en niveaux de gris
bool trouve_la_plaque(IplImage* etude, int nTresh
, const int mean_carac
, const int mean_fond,
cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_, cv::Rect& rect_OpenLP,
const int dist_min_bord
,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
*/
//peut traiter les images sources couleur ou en niveaux de gris
bool trouve_la_plaque(IplImage* etude, const cv::Rect& global_rect, int nTresh
, const int mean_carac
, const int mean_fond,
cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_, cv::Rect& rect_OpenLP,
const int dist_min_bord
,
const int hauteur_plaque_min,
const float& rapport_largeur_sur_hauteur_min);
bool trouve_la_plaque(const cv::Mat& frame, const cv::Rect& global_rect
, cv::Point& p0, cv::Point& p1, cv::Point& p2, cv::Point& p3,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_,
cv::Rect& rect_OpenLP);
bool trouve_la_plaque(const cv::Mat& frame,
const std::list<int>& classes, const std::list<cv::Rect>& boxes
,
cv::Point& top_left_,
cv::Point& top_right_,
cv::Point& bottom_right_,
cv::Point& bottom_left_, cv::Rect& rect_OpenLP);
std::string get_plate_type(
const std::list<cv::Rect>& vect_of_detected_boxes,
const std::list<int>& classIds, const int number_of_characters_latin_numberplate
);
std::string get_plate_type(
const std::vector<cv::Rect>& vect_of_detected_boxes,
const std::vector<int>& classIds, const int number_of_characters_latin_numberplate
);
#endif // !defined UTILS_OPEN_CV
#pragma once

View File

@@ -0,0 +1,532 @@
/*
// Line.h: interface for the C_Line class.
*/
#if !defined(YOLOV5_ALPR_ONNX_DETECTOR)
#define YOLOV5_ALPR_ONNX_DETECTOR
#include "ONNX_detector.h"
class Plates_types_classifier : public OnnxDetector
{
public:
//**************************
// construct/destruct
//**************************
Plates_types_classifier(Ort::Env& env, const void* model_data, size_t model_data_length, const Ort::SessionOptions& options
, const std::vector<std::string>& labels_);
Plates_types_classifier(Ort::Env& env, const ORTCHAR_T* model_path, const Ort::SessionOptions& options
, const std::vector<std::string>& labels_);
Plates_types_classifier(Ort::Env& env, const ORTCHAR_T* model_path, const Ort::SessionOptions& options
, const std::string& labels_filename);
Plates_types_classifier(Ort::Env& env, const void* model_data, size_t model_data_length, const Ort::SessionOptions& options
, const std::string& labels_filename);
OnnxDetector& get_ref() {
return *this;
};
const Plates_types_classifier& get_const_ref() {
return *this;
};
virtual ~Plates_types_classifier();
std::string GetPlatesType(const cv::Mat& img, float& uncalibrated_confidence);
void get_best_plate(const cv::Mat& frame,
//detections when they are separated license plates by license plates
const std::list < std::list<int>>& classIds, const std::list < std::list<float>>& confidences, const std::list < std::list<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp);
//nov 21 update this func with plates_types_classifier classifier
//uses double linked lists : inside list is for characters and outside list is for plates.
//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 cv::Mat& frame,
//detections when they are separated license plates by license plates
const std::list < std::vector<int>>& classIds, const std::list < std::vector<float>>& confidences, const std::list < std::vector<cv::Rect>>& boxes
//output the list of the best (most probable/readable) lp
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp);
private:
int GetPlatesClasse(const cv::Mat& img, float& uncalibrated_confidence);
protected:
std::vector<std::string> labels;
};
class Yolov5_alpr_onxx_detector : public OnnxDetector
{
public:
//**************************
// construct/destruct
//**************************
Yolov5_alpr_onxx_detector(Ort::Env& env, const void* model_data, size_t model_data_length, const Ort::SessionOptions& options);
Yolov5_alpr_onxx_detector(Ort::Env& env, const ORTCHAR_T* model_path, const Ort::SessionOptions& options);
OnnxDetector& get_ref() {
return *this;
};
const OnnxDetector& get_const_ref() {
return *this;
};
virtual ~Yolov5_alpr_onxx_detector();
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
* uses double linked lists : inside list is for characters and outside list is for plates.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, std::list<std::vector<int>>& classIds,
std::list < std::vector<float>>& confidences, std::list < std::vector<cv::Rect>>& boxes,
float nmsThreshold);
/** @brief Given the @p input frame, create input blob, run net and return result detections.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void raw_detections_with_different_confidences(const cv::Mat& frame, std::list<std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes,
float nmsThreshold);
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
* uses double linked lists : inside list is for characters and outside list is for plates.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, std::list<std::vector<int>>& classIds,
std::list < std::vector<float>>& confidences, std::list < std::vector<cv::Rect>>& boxes, std::list <std::list<std::string>>& lpns,
float nmsThreshold, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
);
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, std::list<std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes,
std::list <std::list<std::string>>& lpns,
float nmsThreshold, const int classId_last_country//classId_last_country : is the class index of the last country in the list of detected classes.
);
/** @brief Given the @p input frame, create input blob, run net and return result detections.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] confThreshold A threshold used to filter boxes by confidences.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void detect(const cv::Mat& frame, std::vector<int>& classIds, std::vector<float>& confidences, std::vector<cv::Rect>& boxes,
const float confThreshold = 0.7f, float nmsThreshold = 0.5f);
// Given the @p input frame, create input blob, run net and return result detections.
//this func can manage list of boxes of characters that dont have an englobing lp box (gloabal rect)
//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)
//Produces double linked lists : inside list is for characters and outside list is for plates.
/** @brief
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
* @param[in] confThreshold A threshold used to filter boxes by confidences.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
//and remaining elements are characters
void detect_and_add_lp_and_vehicle_if_necessary(const cv::Mat& frame, std::list<std::vector<int>>& classIds,
std::list < std::vector<float>>& confidences, std::list < std::vector<cv::Rect>>& boxes,
std::list<std::string>& lpns,
const int classId_last_country,
const float confThreshold = 0.7f, float nmsThreshold = 0.5f);
// Given the @p input frame, create input blob, run net and return result detections.
//this func can manage list of boxes of characters that dont have an englobing lp box (gloabal rect)
//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
/** @brief
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
* @param[in] confThreshold A threshold used to filter boxes by confidences.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void detect_and_add_lp_and_vehicle_if_necessary(const cv::Mat& frame, std::list<std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes,
std::list<std::string>& lpns,
const int classId_last_country,
const float confThreshold = 0.7f, float nmsThreshold = 0.5f);
std::list<cv::Rect> TwoStage_LPR(Yolov5_alpr_onxx_detector& parking_detector, const cv::Mat& frame, std::string& lpn);
std::list<cv::Rect> TwoStageLPR(Yolov5_alpr_onxx_detector& parking_detector, Plates_types_classifier& plates_types_classifier, const cv::Mat& frame, std::string& lpn);
/** @brief Given the @p input frame, create input blob, run net and return result detections.
* @param[in] frame : input image.
* @param[out] classIds : classes indeces in resulting detected bounding boxes.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[in] confThreshold A threshold used to filter boxes by confidences.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void detect(const cv::Mat& frame, std::list<int>& classIds, std::list<float>& confidences, std::list<cv::Rect>& boxes,
const float confThreshold = 0.7f, float nmsThreshold = 0.5f);
/** @brief process an image file.
* @param[in] image_filename : filename of the he input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
* @param[in] show_image : boolean if true the image will be displayed in a window with license plate in image banner
* @param[in] time_delay : time delay in ms after which the image is destroyed
@param[in] classId_last_country : is the class index of the last country in the list of detected classes.
* @return void
* @see
*/
void detect(const std::string& image_filename, std::list<std::string>& lpns, const int classId_last_country
//, const bool show_image=false, const int time_delay=0
);
/** @brief from image dir, extract license plate numbers.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] dir : directory path that contains all images files that will be proceeded.
* @return void
* @see
*/
float two_stage_lpr(const std::string& dir);
/** @brief from image dir, extract license plate numbers.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] dir : directory path that contains all images files that will be proceeded.
* @return void
* @see
*/
float two_stage_lpr(Yolov5_alpr_onxx_detector& parking_detector, const std::string& dir);
/** @brief from image dir, extract license plate numbers.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] frame : input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
@param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(Yolov5_alpr_onxx_detector& parking_detector, const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
/** @brief from image dir, extract license plate numbers.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] frame : input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
@param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(Yolov5_alpr_onxx_detector& parking_detector, Plates_types_classifier& plates_types_classifier, const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
/** @brief from image frame, extract license plate number.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] image_filename : filename of the he input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
@param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(Yolov5_alpr_onxx_detector& parking_detector, const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
/** @brief from image filename, extract license plate number.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] image_filename : filename of the he input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
* @param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
/** @brief Given the @p input frame, create input blob, run net and return result detections.
//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.
* @param[in] frame : The input image.
* @param[out] classIds Class indexes in result detection.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[out] one_lp :set of detected boxes when they rearranged from left to right
* @param[out] confidence_one_lp : confidences corresponding detected boxes
* @param[out] classIds_one_lp : set of indeces of the above detected boxes
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
* @param[in] confThreshold A threshold used to filter boxes by confidences.
* @param[in] nmsThreshold A threshold used in non maximum suppression.
*/
void detect_lpn_and_add_lp_and_vehicle_if_necessary(const cv::Mat& frame, std::list < std::vector<int>>& classIds,
std::list < std::vector<float>>& confidences, std::list < std::vector<cv::Rect>>& boxes
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp,
const int classId_last_country,
//const C_OCROutputs& availableAlpha,
const float confThreshold = 0.7f, float nmsThreshold = 0.5f);
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
//it selects just one lpn although all lps have been detected and stored in double linked lists, then from these lists, selects the one that is the best
//(with best confidences of its characters and with greateast size)
//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
* @param[in] frame : The input image.
* @param[out] classIds Class indexes in result detection.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[out] one_lp :set of detected boxes when they rearranged from left to right
* @param[out] confidence_one_lp : confidences corresponding detected boxes
* @param[out] classIds_one_lp : set of indeces of the above detected boxes
* @param[in] nmsThreshold A threshold used in non maximum suppression.
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, std::list < std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp,
const int classId_last_country,
//const C_OCROutputs& availableAlpha,
float nmsThreshold);
//
//Given the @p input frame, create input blob, run net and return result detections.
//it selects just one lpn although all lps are detected in double linked lists
////this func can manage list of boxes of characters that dont have an englobing lp box (gloabal rect)
////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 detect_lpn_and_add_lp_and_vehicle_if_necessary(const cv::Mat& frame, Plates_types_classifier& plates_types_classifier, std::list < std::vector<int>>& classIds,
std::list < std::vector<float>>& confidences, std::list < std::vector<cv::Rect>>& boxes
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp,
const int classId_last_country,//classId_last_country : is the class index of the last country in the list of detected classes.We remember that ascii(latin) characters come fist(36 classes) then come the license plates countries(another 60 classses) then come a long list of vehicles classes
//const C_OCROutputs & availableAlpha,
const float confThreshold, float nmsThreshold);
//Given the @p input frame, create input blob, run net then, from result detections, assembly license plates present in the input image.
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, Plates_types_classifier& plates_types_classifier, std::list < std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp,
const int classId_last_country,//classId_last_country : is the class index of the last country in the list of detected classes.We remember that ascii(latin) characters come fist(36 classes) then come the license plates countries(another 60 classses) then come a long list of vehicles classes
//const C_OCROutputs & availableAlpha,
float nmsThreshold);
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
//it selects just one lpn although all lps have been detected and stored in double linked lists, then from these lists, selects the one that is the best
//(with best confidences of its characters and with greateast size)
//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
* @param[in] frame : The input image.
@param[in] ExactLPN : the actual license plate number in the image
* @param[out] classIds Class indexes in result detection.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[out] one_lp :set of detected boxes when they rearranged from left to right
* @param[out] confidence_one_lp : confidences corresponding detected boxes
* @param[out] classIds_one_lp : set of indeces of the above detected boxes
* @param[in] nmsThreshold A threshold used in non maximum suppression.
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, const std::string& ExactLPN, std::list < std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes
, std::list<float>& confidence_one_lp, std::list < cv::Rect>& one_lp, std::list<int>& classIds_one_lp,
const int classId_last_country,
//const C_OCROutputs& availableAlpha,
float nmsThreshold);
/** @brief Given the @p input frame, create input blob, run net then, from result detections, assembies license plates present in the input image.
//it selects just one lpn although all lps have been detected and stored in double linked lists, then from these lists, selects the one that is the best
//(with best confidences of its characters and with greateast size)
//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
* @param[in] frame : The input image.
* @param[in] ExactLPN : the actual license plate number in the image
* @param[out] classIds Class indexes in result detection.
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : set of detected bounding boxes.
* @param[out] one_lp :set of detected boxes when they rearranged from left to right
* @param[out] confidence_one_lp : confidences corresponding detected boxes
* @param[out] classIds_one_lp : set of indeces of the above detected boxes
* @param[in] nmsThreshold A threshold used in non maximum suppression.
* @param[in] classId_last_country : is the class index of the last country in the list of detected classes.
*/
void detect_with_different_confidences_then_separate_plates(const cv::Mat& frame, const std::string& ExactLPN, std::list < std::list<int>>& classIds,
std::list < std::list<float>>& confidences, std::list < std::list<cv::Rect>>& boxes
, std::vector<float>& confidence_one_lp, std::vector < cv::Rect>& one_lp, std::vector<int>& classIds_one_lp,
const int classId_last_country,
//const C_OCROutputs& availableAlpha,
float nmsThreshold);
};
/** @brief from image filename, extract license plate number.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] freeflow_detectors : yolo detectors that detect license plates in images that are not focused and that can contain multiple license plates.
* @param[in] parking_detectors : yolo detectors that read characters in a localized and focused license plate in image
* @param[in] image_filename : filename of the he input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
* @param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors,
const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors
, Plates_types_classifier& plates_types_classifier,
const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors
, Plates_types_classifier& plates_types_classifier,
const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//make a bounding box greater (just to be sure we don t miss something in detections)
void get_larger_roi(cv::Rect& lpn_roi, const int width, const int height
);
//make a bounding box greater (just to be sure we don t miss something in detections)
void get_larger_roi(cv::Rect& lpn_roi, const int width, const int height, const float& scale_x, const float& scale_y
);
Yolov5_alpr_onxx_detector* get_detector_with_smallest_size_bigger_than_image(const std::list<Yolov5_alpr_onxx_detector*>& detectors, const int max_size);
Yolov5_alpr_onxx_detector* get_detector_with_smallest_size_bigger_than_image(const std::list<Yolov5_alpr_onxx_detector*>& detectors, const int width, const int height);
/** @brief from image frame, extract license plate number.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] freeflow_detectors : yolo detectors that detect license plates in images that are not focused and that can contain multiple license plates.
* @param[in] parking_detectors : yolo detectors that read characters in a localized and focused license plate in image
* @param[in] frame : input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
* @param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors,
const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
/** @brief from image filename, extract license plate number.
this performs a two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
Difference between this func and detect func is that
two_stage_lpr first focus on license plate detection and secondly on its characters. This is supposed to get best results.
* @param[in] image_filename : filename of the he input image.
* @param[out] lpn : the license plate number found in the image by the dnn detector.
* @param[in] classes : set of indeces that indicate the classes of each of these detected boxes
* @param[out] confidences : detection confidences of detected bounding boxes
* @param[out] boxes : A set of bounding boxes.
* @param[out] chosen_lp_classIds : set of indeces that indicate the classes of each box of the license plate that has been chosen by engine (ie the one with the highest confidence)
* @param[out] chosen_lp_confidences : detection confidences of the corresponding boxes
* @param[out] chosen_lp_boxes : A set of bounding boxes of the license plate that has been chosen by engine (ie the one with the highest confidence)
*/
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& detectors, const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors
, Plates_types_classifier& plates_types_classifier,
const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
void two_stage_lpr(Yolov5_alpr_onxx_detector& freeflow_detector, Yolov5_alpr_onxx_detector& parking_detector
, Plates_types_classifier& plates_types_classifier,
const std::string& image_filename,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
void two_stage_lpr(const std::list<Yolov5_alpr_onxx_detector*>& freeflow_detectors, const std::list<Yolov5_alpr_onxx_detector*>& parking_detectors
, Plates_types_classifier& plates_types_classifier,
const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
//two stage lpn detection : first a global nn detects lpn of a free flow vehicle, then a second nn focuses and reads the lpn of the previously detected lpn.
void two_stage_lpr(Yolov5_alpr_onxx_detector& freeflow_detector, Yolov5_alpr_onxx_detector& parking_detector
, Plates_types_classifier& plates_types_classifier,
const cv::Mat& frame,
//double linked lists to separate lps
std::list < std::list<float>>& confidences, std::list < std::list<int>>& classes, std::list < std::list<cv::Rect>>& boxes,
//all lps in the image given by lpn (as string), lp country ppronenace (as class index) and lp area in the image (cv::Rect)
std::list <std::string>& lpns, std::list <int>& lp_country_class, std::list < cv::Rect>& lp_rois,
//detection inside the chosen lp
std::list<int>& chosen_lp_classIds, std::list<float>& chosen_lp_confidences, std::list<cv::Rect>& chosen_lp_boxes
);
#endif // !defined(YOLOV5_ALPR_ONNX_DETECTOR)

5
modules/ANSLPR/pch.cpp Normal file
View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

13
modules/ANSLPR/pch.h Normal file
View File

@@ -0,0 +1,13 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#endif //PCH_H

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