Add CPU/GPU gate and support new ANSALPR using OCR
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include <iostream>
|
||||
#define NOMINMAX
|
||||
#include <iostream>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/dnn.hpp>
|
||||
@@ -31,6 +32,60 @@
|
||||
#include <cuda_runtime.h>
|
||||
#include "EPLoader.h"
|
||||
|
||||
// Decode \\uXXXX (literal backslash-u-hex) sequences back to UTF-8.
|
||||
// VectorDetectionToJsonString double-escapes Unicode for LabVIEW compatibility,
|
||||
// so JSON strings contain literal "\u54c1" text instead of actual Unicode chars.
|
||||
static std::string DecodeUnicodeEscapes(const std::string& input) {
|
||||
std::string result;
|
||||
result.reserve(input.size());
|
||||
size_t i = 0;
|
||||
while (i < input.size()) {
|
||||
if (i + 5 < input.size() && input[i] == '\\' && input[i + 1] == 'u') {
|
||||
// Parse 4 hex digits
|
||||
std::string hex = input.substr(i + 2, 4);
|
||||
char* end = nullptr;
|
||||
uint32_t cp = static_cast<uint32_t>(strtoul(hex.c_str(), &end, 16));
|
||||
if (end == hex.c_str() + 4) {
|
||||
// Check for surrogate pair (\\uD800-DBFF followed by \\uDC00-DFFF)
|
||||
if (cp >= 0xD800 && cp <= 0xDBFF && i + 11 < input.size()
|
||||
&& input[i + 6] == '\\' && input[i + 7] == 'u') {
|
||||
std::string hex2 = input.substr(i + 8, 4);
|
||||
uint32_t cp2 = static_cast<uint32_t>(strtoul(hex2.c_str(), &end, 16));
|
||||
if (end == hex2.c_str() + 4 && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
|
||||
cp = 0x10000 + ((cp - 0xD800) << 10) + (cp2 - 0xDC00);
|
||||
i += 12;
|
||||
} else {
|
||||
i += 6;
|
||||
}
|
||||
} else {
|
||||
i += 6;
|
||||
}
|
||||
// Encode codepoint as UTF-8
|
||||
if (cp < 0x80) {
|
||||
result += static_cast<char>(cp);
|
||||
} else if (cp < 0x800) {
|
||||
result += static_cast<char>(0xC0 | (cp >> 6));
|
||||
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
} else if (cp < 0x10000) {
|
||||
result += static_cast<char>(0xE0 | (cp >> 12));
|
||||
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
} else {
|
||||
result += static_cast<char>(0xF0 | (cp >> 18));
|
||||
result += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
|
||||
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
}
|
||||
} else {
|
||||
result += input[i++];
|
||||
}
|
||||
} else {
|
||||
result += input[i++];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetOptionalValue(const boost::property_tree::ptree& pt, std::string attribute, T defaultValue) {
|
||||
if (pt.count(attribute)) {
|
||||
@@ -3533,8 +3588,229 @@ int ANSLPR_OD_CPU_VideoTest() {
|
||||
return (frameIndex > 0) ? 0 : -4;
|
||||
}
|
||||
|
||||
// ── ANSALPR_OCR test: Japanese license plate detection using ANSONNXOCR ──
|
||||
// Render UTF-8 text onto a cv::Mat using Windows GDI (supports CJK/Unicode).
|
||||
// cv::putText only handles ASCII — Japanese characters render as '?'.
|
||||
#ifdef WIN32
|
||||
static void putTextUnicode(cv::Mat& img, const std::string& text, cv::Point org,
|
||||
double fontScale, cv::Scalar color, int thickness) {
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, nullptr, 0);
|
||||
std::wstring wtext(wlen - 1, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, &wtext[0], wlen);
|
||||
|
||||
HDC hdc = CreateCompatibleDC(nullptr);
|
||||
int fontHeight = (int)(fontScale * 30);
|
||||
|
||||
HFONT hFont = CreateFontW(fontHeight, 0, 0, 0,
|
||||
(thickness > 2) ? FW_BOLD : FW_NORMAL,
|
||||
FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Yu Gothic UI");
|
||||
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
|
||||
|
||||
SIZE sz;
|
||||
GetTextExtentPoint32W(hdc, wtext.c_str(), (int)wtext.size(), &sz);
|
||||
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = sz.cx;
|
||||
bmi.bmiHeader.biHeight = -sz.cy;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
void* bits = nullptr;
|
||||
HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
|
||||
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp);
|
||||
|
||||
SetBkMode(hdc, TRANSPARENT);
|
||||
SetTextColor(hdc, RGB((int)color[2], (int)color[1], (int)color[0]));
|
||||
TextOutW(hdc, 0, 0, wtext.c_str(), (int)wtext.size());
|
||||
|
||||
cv::Mat textImg(sz.cy, sz.cx, CV_8UC4, bits);
|
||||
for (int row = 0; row < sz.cy; ++row) {
|
||||
for (int col = 0; col < sz.cx; ++col) {
|
||||
cv::Vec4b px = textImg.at<cv::Vec4b>(row, col);
|
||||
if (px[0] != 0 || px[1] != 0 || px[2] != 0) {
|
||||
int dy = org.y + row;
|
||||
int dx = org.x + col;
|
||||
if (dy >= 0 && dy < img.rows && dx >= 0 && dx < img.cols) {
|
||||
img.at<cv::Vec3b>(dy, dx) = cv::Vec3b(px[0], px[1], px[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SelectObject(hdc, hOldBmp);
|
||||
SelectObject(hdc, hOldFont);
|
||||
DeleteObject(hBmp);
|
||||
DeleteObject(hFont);
|
||||
DeleteDC(hdc);
|
||||
}
|
||||
#endif
|
||||
|
||||
int ALPR_OCR_Test() {
|
||||
std::cout << "=== ALPR_OCR_Test: Japanese License Plate (ANSALPR_OCR) ===" << std::endl;
|
||||
std::filesystem::path currentPath = std::filesystem::current_path();
|
||||
std::cout << "Current working directory: " << currentPath << std::endl;
|
||||
|
||||
ANSCENTER::ANSALPR* infHandle = nullptr;
|
||||
std::string licenseKey = "";
|
||||
std::string modelFilePath = "C:\\Projects\\ANSVIS\\Models\\ANS_GenericALPR_v2.0.zip";
|
||||
std::string imagePath = "C:\\Programs\\ModelTraining\\JLPD\\data\\test7.jpg";
|
||||
|
||||
int engineType = 2; // ANSALPR_OCR
|
||||
double detectionThreshold = 0.3;
|
||||
double ocrThreshold = 0.5;
|
||||
double colourThreshold = 0.0; // No colour detection for this test
|
||||
|
||||
// Step 1: Create handle
|
||||
int createResult = CreateANSALPRHandle(&infHandle, licenseKey.c_str(),
|
||||
modelFilePath.c_str(), "", engineType, detectionThreshold, ocrThreshold, colourThreshold);
|
||||
std::cout << "CreateANSALPRHandle result: " << createResult << std::endl;
|
||||
if (!createResult || !infHandle) {
|
||||
std::cerr << "Failed to create ANSALPR_OCR handle" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 2: Set country to Japan
|
||||
ANSALPR_SetCountry(&infHandle, 5); // JAPAN = 5
|
||||
std::cout << "Country set to JAPAN" << std::endl;
|
||||
|
||||
// Step 3: Load engine
|
||||
auto engineStart = std::chrono::high_resolution_clock::now();
|
||||
int loadResult = LoadANSALPREngineHandle(&infHandle);
|
||||
auto engineEnd = std::chrono::high_resolution_clock::now();
|
||||
double engineMs = std::chrono::duration<double, std::milli>(engineEnd - engineStart).count();
|
||||
std::cout << "LoadANSALPREngineHandle result: " << loadResult << " (" << engineMs << " ms)" << std::endl;
|
||||
if (!loadResult) {
|
||||
std::cerr << "Failed to load ANSALPR_OCR engine" << std::endl;
|
||||
ReleaseANSALPRHandle(&infHandle);
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Step 4: Load image
|
||||
cv::Mat input = cv::imread(imagePath, cv::IMREAD_COLOR);
|
||||
if (input.empty()) {
|
||||
std::cerr << "Failed to load image: " << imagePath << std::endl;
|
||||
ReleaseANSALPRHandle(&infHandle);
|
||||
return -3;
|
||||
}
|
||||
std::cout << "Image loaded: " << input.cols << "x" << input.rows << std::endl;
|
||||
|
||||
cv::Mat frame = input.clone();
|
||||
int width = frame.cols;
|
||||
int height = frame.rows;
|
||||
|
||||
// Convert to raw BGR bytes for RunInferenceBinary
|
||||
unsigned int bufferLength = static_cast<unsigned int>(frame.total() * frame.elemSize());
|
||||
unsigned char* imageBytes = new unsigned char[bufferLength];
|
||||
std::memcpy(imageBytes, frame.data, bufferLength);
|
||||
|
||||
// Step 5: Warmup run
|
||||
auto warmupStart = std::chrono::high_resolution_clock::now();
|
||||
std::string detectionResult = ANSALPR_RunInferenceBinary(&infHandle, imageBytes, width, height);
|
||||
auto warmupEnd = std::chrono::high_resolution_clock::now();
|
||||
double warmupMs = std::chrono::duration<double, std::milli>(warmupEnd - warmupStart).count();
|
||||
std::cout << "Warmup inference: " << warmupMs << " ms" << std::endl;
|
||||
std::cout << "ALPR Result: " << detectionResult << std::endl;
|
||||
|
||||
// Step 6: Benchmark
|
||||
const int benchmarkIterations = 10;
|
||||
std::vector<double> times;
|
||||
times.reserve(benchmarkIterations);
|
||||
for (int i = 0; i < benchmarkIterations; ++i) {
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
std::string result = ANSALPR_RunInferenceBinary(&infHandle, imageBytes, width, height);
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
|
||||
times.push_back(ms);
|
||||
std::cout << " Run " << (i + 1) << "/" << benchmarkIterations << ": " << ms << " ms" << std::endl;
|
||||
}
|
||||
std::sort(times.begin(), times.end());
|
||||
double sum = std::accumulate(times.begin(), times.end(), 0.0);
|
||||
double avg = sum / benchmarkIterations;
|
||||
double median = (benchmarkIterations % 2 == 0)
|
||||
? (times[benchmarkIterations / 2 - 1] + times[benchmarkIterations / 2]) / 2.0
|
||||
: times[benchmarkIterations / 2];
|
||||
std::cout << "\n=== Benchmark (" << benchmarkIterations << " runs) ===" << std::endl;
|
||||
std::cout << " Avg: " << avg << " ms" << std::endl;
|
||||
std::cout << " Median: " << median << " ms" << std::endl;
|
||||
std::cout << " Min: " << times.front() << " ms" << std::endl;
|
||||
std::cout << " Max: " << times.back() << " ms" << std::endl;
|
||||
std::cout << " FPS: " << (1000.0 / avg) << std::endl;
|
||||
|
||||
delete[] imageBytes;
|
||||
|
||||
// Step 7: Draw results on image
|
||||
if (!detectionResult.empty()) {
|
||||
try {
|
||||
boost::property_tree::ptree pt;
|
||||
std::stringstream ss(detectionResult);
|
||||
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& res = child.second;
|
||||
const auto class_name_raw = GetData<std::string>(res, "class_name");
|
||||
const std::string class_name = DecodeUnicodeEscapes(class_name_raw);
|
||||
const auto x = GetData<int>(res, "x");
|
||||
const auto y = GetData<int>(res, "y");
|
||||
const auto w = GetData<int>(res, "width");
|
||||
const auto h = GetData<int>(res, "height");
|
||||
|
||||
cv::rectangle(frame, cv::Rect(x, y, w, h), cv::Scalar(0, 255, 0), 2);
|
||||
|
||||
std::string extraInfo = GetOptionalValue<std::string>(res, "extra_info", "");
|
||||
std::cout << " Plate: " << class_name << std::endl;
|
||||
if (!extraInfo.empty()) {
|
||||
std::cout << " extra_info: " << extraInfo << std::endl;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
int textH = (int)(1.5 * 30);
|
||||
int ty = y - 5 - textH;
|
||||
if (ty < 0) ty = y + 3;
|
||||
putTextUnicode(frame, class_name, cv::Point(x, ty),
|
||||
1.5, cv::Scalar(0, 0, 255), 3);
|
||||
}
|
||||
#else
|
||||
cv::putText(frame, class_name, cv::Point(x, y - 5),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 8: Display result
|
||||
cv::Mat display;
|
||||
double scale = std::min(1920.0 / frame.cols, 1080.0 / frame.rows);
|
||||
if (scale < 1.0) {
|
||||
cv::resize(frame, display, cv::Size(), scale, scale);
|
||||
} else {
|
||||
display = frame;
|
||||
}
|
||||
cv::namedWindow("ALPR_OCR_Test", cv::WINDOW_AUTOSIZE);
|
||||
cv::imshow("ALPR_OCR_Test", display);
|
||||
cv::waitKey(0);
|
||||
|
||||
// Cleanup
|
||||
ReleaseANSALPRHandle(&infHandle);
|
||||
cv::destroyAllWindows();
|
||||
frame.release();
|
||||
input.release();
|
||||
|
||||
std::cout << "=== ALPR_OCR_Test complete ===" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
// ANSLPR_OD_INDOInferences_FileTest();
|
||||
//ANSLPR_OD_Inferences_FileTest();
|
||||
//ANSLPR_OD_VideoTest();
|
||||
@@ -3544,11 +3820,12 @@ int main()
|
||||
// ANSLPR_CPU_Inferences_FileTest();
|
||||
//}
|
||||
//ANSLPR_SingleTask_Test();
|
||||
ANSLPR_CPU_StressTest();
|
||||
//ANSLPR_CPU_StressTest();
|
||||
//ANSLPR_MultiGPU_StressTest();
|
||||
//ANSLPR_MultiGPU_StressTest_SimulatedCam();
|
||||
// ANSLPR_MultiGPU_StressTest_FilePlayer();
|
||||
//ANSLPR_OD_CPU_VideoTest();
|
||||
ALPR_OCR_Test();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user