Refactor project structure
This commit is contained in:
843
modules/ANSLPR/ANSALPR_OV.cpp
Normal file
843
modules/ANSLPR/ANSALPR_OV.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
23
modules/ANSLPR/ANSGpuFrameRegistry.cpp
Normal file
23
modules/ANSLPR/ANSGpuFrameRegistry.cpp
Normal 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
769
modules/ANSLPR/ANSLPR.cpp
Normal 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
150
modules/ANSLPR/ANSLPR.h
Normal 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
|
||||
1446
modules/ANSLPR/ANSLPR_CPU.cpp
Normal file
1446
modules/ANSLPR/ANSLPR_CPU.cpp
Normal file
File diff suppressed because it is too large
Load Diff
188
modules/ANSLPR/ANSLPR_CPU.h
Normal file
188
modules/ANSLPR/ANSLPR_CPU.h
Normal 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
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
191
modules/ANSLPR/ANSLPR_OD.h
Normal 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
328
modules/ANSLPR/ANSLPR_OV.h
Normal 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
|
||||
776
modules/ANSLPR/ANSLPR_RT.cpp
Normal file
776
modules/ANSLPR/ANSLPR_RT.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
97
modules/ANSLPR/ANSLPR_RT.h
Normal file
97
modules/ANSLPR/ANSLPR_RT.h
Normal 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
|
||||
73
modules/ANSLPR/CMakeLists.txt
Normal file
73
modules/ANSLPR/CMakeLists.txt
Normal 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
1091
modules/ANSLPR/dllmain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
modules/ANSLPR/framework.h
Normal file
7
modules/ANSLPR/framework.h
Normal 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>
|
||||
68
modules/ANSLPR/include/Levenshtein.h
Normal file
68
modules/ANSLPR/include/Levenshtein.h
Normal 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)
|
||||
192
modules/ANSLPR/include/Line.h
Normal file
192
modules/ANSLPR/include/Line.h
Normal 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_)
|
||||
68
modules/ANSLPR/include/ONNX_detector.h
Normal file
68
modules/ANSLPR/include/ONNX_detector.h
Normal 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)
|
||||
106
modules/ANSLPR/include/StatSommesX_Y_H_dbl.h
Normal file
106
modules/ANSLPR/include/StatSommesX_Y_H_dbl.h
Normal 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_)
|
||||
810
modules/ANSLPR/include/utils_alpr_detect.h
Normal file
810
modules/ANSLPR/include/utils_alpr_detect.h
Normal 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)
|
||||
40
modules/ANSLPR/include/utils_image_file.h
Normal file
40
modules/ANSLPR/include/utils_image_file.h
Normal 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)
|
||||
373
modules/ANSLPR/include/utils_opencv.h
Normal file
373
modules/ANSLPR/include/utils_opencv.h
Normal 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
|
||||
532
modules/ANSLPR/include/yolov5_alpr_onnx_detector.h
Normal file
532
modules/ANSLPR/include/yolov5_alpr_onnx_detector.h
Normal 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
5
modules/ANSLPR/pch.cpp
Normal 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
13
modules/ANSLPR/pch.h
Normal 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
|
||||
136
modules/ANSLPR/src/Levenshtein.cpp
Normal file
136
modules/ANSLPR/src/Levenshtein.cpp
Normal 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
195
modules/ANSLPR/src/Line.cpp
Normal 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;
|
||||
}
|
||||
367
modules/ANSLPR/src/ONNX_detector.cpp
Normal file
367
modules/ANSLPR/src/ONNX_detector.cpp
Normal 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->confidence,1 -> 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->confidence,1 -> 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->confidence,5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
|
||||
#ifdef _DEBUG
|
||||
assert(dimensions >= 41);// 0,1,2,3 ->box,4->confidence,5-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->confidence,1 -> 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->confidence,5-85 -> output classes = 35 characters+pays 60 = 95 classes confidence
|
||||
#ifdef _DEBUG
|
||||
assert(dimensions >= 41);// 0,1,2,3 ->box,4->confidence,5-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
256
modules/ANSLPR/src/StatSommesX_Y_H_dbl.cpp
Normal file
256
modules/ANSLPR/src/StatSommesX_Y_H_dbl.cpp
Normal 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
|
||||
2902
modules/ANSLPR/src/utils_alpr_detect.cpp
Normal file
2902
modules/ANSLPR/src/utils_alpr_detect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
171
modules/ANSLPR/src/utils_image_file.cpp
Normal file
171
modules/ANSLPR/src/utils_image_file.cpp
Normal 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;
|
||||
}
|
||||
5509
modules/ANSLPR/src/utils_opencv.cpp
Normal file
5509
modules/ANSLPR/src/utils_opencv.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1994
modules/ANSLPR/src/yolov5_alpr_onnx_detector.cpp
Normal file
1994
modules/ANSLPR/src/yolov5_alpr_onnx_detector.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user