777 lines
30 KiB
C++
777 lines
30 KiB
C++
#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;
|
|
}
|
|
}
|