#include "ANSOCRBase.h" #include "Utility.h" #include #include #include #include "ANSLibsLoader.h" static bool ansocrLicenceValid = false; // Global once_flag to protect license checking static std::once_flag ansocrLicenseOnceFlag; template T GetData(const boost::property_tree::ptree& pt, const std::string& key) { T ret; if (boost::optional data = pt.get_optional(key)) { ret = data.get(); } return ret; } namespace ANSCENTER { /// /// Base class /// /// /// 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 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 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& 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 ANSCENTER::ANSOCRUtility::GetBoundingBoxes(const std::string& strBBoxes) { std::vector 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(result, "x"); const auto y = GetData(result, "y"); const auto width = GetData(result, "width"); const auto height = GetData(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& 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& 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 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 emptyPolygon; return emptyPolygon; } // Calculate normalized points for each corner of the rectangle std::vector 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; } };