300 lines
10 KiB
C++
300 lines
10 KiB
C++
#include "ANSOCRBase.h"
|
|
#include "Utility.h"
|
|
#include <opencv2/highgui.hpp>
|
|
#include <omp.h>
|
|
#include <json.hpp>
|
|
#include "ANSLibsLoader.h"
|
|
|
|
static bool ansocrLicenceValid = false;
|
|
// Global once_flag to protect license checking
|
|
static std::once_flag ansocrLicenseOnceFlag;
|
|
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 {
|
|
/// <summary>
|
|
/// Base class
|
|
/// </summary>
|
|
///
|
|
///
|
|
static void VerifyGlobalANSOCRLicense(const std::string& licenseKey) {
|
|
try {
|
|
ansocrLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1005, "ANSOCR");//Default productId=1006
|
|
if (!ansocrLicenceValid) { // we also support ANSTS license
|
|
ansocrLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS)
|
|
}
|
|
}
|
|
catch (std::exception& e) {
|
|
ansocrLicenceValid = false;
|
|
}
|
|
}
|
|
void ANSOCRBase::CheckLicense() {
|
|
try {
|
|
// Check once globally
|
|
std::call_once(ansocrLicenseOnceFlag, [this]() {
|
|
VerifyGlobalANSOCRLicense(_licenseKey);
|
|
});
|
|
|
|
// Update this instance's local license flag
|
|
_licenseValid = ansocrLicenceValid;
|
|
}
|
|
catch (const std::exception& e) {
|
|
this->_logger.LogFatal("ANSOCRBase::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
bool ANSOCRBase::Init(const std::string& licenseKey, OCRModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, int engineMode) {
|
|
try {
|
|
ANSCENTER::ANSLibsLoader::Initialize();
|
|
|
|
_licenseKey = licenseKey;
|
|
_engineMode = engineMode;
|
|
_licenseValid = false;
|
|
_modelFolder = "";
|
|
_modelConfigFile = "";
|
|
_modelConfig = modelConfig;
|
|
_modelFolder.clear();
|
|
_modelConfigFile.clear();
|
|
|
|
CheckLicense();
|
|
if (!_licenseValid) {
|
|
this->_logger.LogError("ANSOCRBase::Initialize", "Invalid License", __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
_licenseValid = true;
|
|
|
|
|
|
// 0. Check if the modelZipFilePath exist?
|
|
if (!FileExist(modelZipFilePath)) {
|
|
this->_logger.LogFatal("ANSOCRBase::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
// 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("ANSOCRBase::Initialize. Model name", modelName, __FILE__, __LINE__);
|
|
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 (!std::filesystem::exists(_modelFolder)) {
|
|
this->_logger.LogError("ANSOCRBase::Initialize. Output model folder is not exist", modelName, __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("ANSOCRBase::Initialize", e.what(), __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ANSOCRBase::Initialize(const std::string& licenseKey, OCRModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, int engineMode) {
|
|
try {
|
|
|
|
_licenseKey = licenseKey;
|
|
_engineMode = engineMode;
|
|
_licenseValid = false;
|
|
_modelFolder = "";
|
|
_modelConfigFile = "";
|
|
_modelConfig = modelConfig;
|
|
_modelFolder.clear();
|
|
_modelConfigFile.clear();
|
|
|
|
CheckLicense();
|
|
if (!_licenseValid) {
|
|
this->_logger.LogError("ANSOCRBase::Initialize", "Invalid License", __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
_licenseValid = true;
|
|
|
|
|
|
// 0. Check if the modelZipFilePath exist?
|
|
if (!FileExist(modelZipFilePath)) {
|
|
this->_logger.LogFatal("ANSOCRBase::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
// 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("ANSOCRBase::Initialize. Model name", modelName, __FILE__, __LINE__);
|
|
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 (!std::filesystem::exists(_modelFolder)) {
|
|
this->_logger.LogError("ANSOCRBase::Initialize. Output model folder is not exist", modelName, __FILE__, __LINE__);
|
|
return false; // That means the model file is not exist or the password is not correct
|
|
}
|
|
// 3. Check if the model has the configuration file
|
|
std::string modelConfigName = "model_config.json";
|
|
_modelConfigFile = CreateFilePath(_modelFolder, modelConfigName);
|
|
|
|
//4. For now we do have the model folder so we will assign paths to OCR models
|
|
_modelConfig.detectionModelDir = _modelFolder;
|
|
_modelConfig.recognizerModelDir = _modelFolder;
|
|
_modelConfig.clsModelDir = _modelFolder;
|
|
_modelConfig.layoutModelDir = _modelFolder;
|
|
_modelConfig.layourDictionaryPath = _modelFolder;
|
|
_modelConfig.tableModelDir = _modelFolder;
|
|
_modelConfig.tableCharDictionaryPath = _modelFolder;
|
|
_modelConfig.recogizerCharDictionaryPath = CreateFilePath(_modelFolder, "dict_ch.txt");
|
|
|
|
_modelConfig.detectionModelFile = CreateFilePath(_modelFolder, "ansocrdec.onnx");
|
|
_modelConfig.detectionModelParam = CreateFilePath(_modelFolder, "ansocrdec.onnx");
|
|
_modelConfig.clsModelFile = CreateFilePath(_modelFolder, "ansocrcls.onnx");
|
|
_modelConfig.clsModelParam = CreateFilePath(_modelFolder, "ansocrcls.onnx");
|
|
_modelConfig.recognizerModelFile = CreateFilePath(_modelFolder, "ansocrrec.onnx");
|
|
_modelConfig.recognizerModelParam = CreateFilePath(_modelFolder, "ansocrrec.onnx");
|
|
// For now we do have _modelConfig and _modelFolder
|
|
return true;
|
|
}
|
|
catch (std::exception& e) {
|
|
this->_logger.LogFatal("ANSOCRBase::Initialize", e.what(), __FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
}
|
|
std::string ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(const std::vector<OCRObject>& 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) {
|
|
// Add your error logging here if needed
|
|
return R"({"results":[],"error":"Serialization failed"})";
|
|
}
|
|
}
|
|
|
|
std::vector<cv::Rect> ANSCENTER::ANSOCRUtility::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::ANSOCRUtility::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 ANSCENTER::ANSOCRUtility::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::ANSOCRUtility::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;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|