Fix mixed UTF16 issue (LabVIEW) and fix ANSFR for Intel

This commit is contained in:
2026-04-08 08:47:10 +10:00
parent 866e0282e2
commit a4a8caaa86
10 changed files with 594 additions and 38 deletions

View File

@@ -315,26 +315,30 @@ namespace ANSCENTER
EngineType ANSCENTER::ANSLicenseHelper::CheckHardwareInformation() EngineType ANSCENTER::ANSLicenseHelper::CheckHardwareInformation()
{ {
ANS_DBG("HWInfo", "CheckHardwareInformation: starting hardware detection");
bool cpuIntelAvailable = false; bool cpuIntelAvailable = false;
bool gpuNvidiaAvailable = false; bool gpuNvidiaAvailable = false;
bool gpuAMDAvailable = false; bool gpuAMDAvailable = false;
bool gpuOpenVINOAvailable = false; bool gpuOpenVINOAvailable = false;
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// CPU <EFBFBD> Intel socket detection (OpenVINO fallback) // CPU Intel socket detection (OpenVINO fallback)
// ---------------------------------------------------------------- // ----------------------------------------------------------------
auto sockets = hwinfo::getAllSockets(); auto sockets = hwinfo::getAllSockets();
for (auto& socket : sockets) { for (auto& socket : sockets) {
const auto& cpu = socket.CPU(); const auto& cpu = socket.CPU();
ANS_DBG("HWInfo", "CPU vendor=%s model=%s", cpu.vendor().c_str(), cpu.modelName().c_str());
if (cpu.vendor() == "GenuineIntel") if (cpu.vendor() == "GenuineIntel")
cpuIntelAvailable = true; cpuIntelAvailable = true;
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// GPU <EFBFBD> classify all adapters by vendor string // GPU classify all adapters by vendor string
// ---------------------------------------------------------------- // ----------------------------------------------------------------
auto gpus = hwinfo::getAllGPUs(); auto gpus = hwinfo::getAllGPUs();
ANS_DBG("HWInfo", "Found %zu GPU adapter(s)", gpus.size());
for (auto& gpu : gpus) { for (auto& gpu : gpus) {
std::string vendor = gpu.vendor(); std::string vendor = gpu.vendor();
ANS_DBG("HWInfo", "GPU: name=%s vendor=%s", gpu.name().c_str(), vendor.c_str());
std::cout << "[HWInfo] Detected GPU: " << gpu.name() << " (Vendor: " << vendor << ")" << std::endl; std::cout << "[HWInfo] Detected GPU: " << gpu.name() << " (Vendor: " << vendor << ")" << std::endl;
// Normalize to uppercase for reliable substring matching // Normalize to uppercase for reliable substring matching
std::transform(vendor.begin(), vendor.end(), std::transform(vendor.begin(), vendor.end(),
@@ -345,12 +349,18 @@ namespace ANSCENTER
if (vendor.find("INTEL") != std::string::npos) if (vendor.find("INTEL") != std::string::npos)
gpuOpenVINOAvailable = true; gpuOpenVINOAvailable = true;
// AMD cards report as "AMD", "Advanced Micro Devices", or // AMD cards report as "AMD", "Advanced Micro Devices", or
// legacy "ATI" branding // legacy "ATI Technologies" branding.
// NOTE: "ATI" alone is too broad — "INTEL CORPORATION" contains
// "ATI" inside "CORPOR-ATI-ON", causing a false positive.
// Use "ATI " (with trailing space) to match "ATI Technologies"
// or "ATI Radeon" without matching "CORPORATION".
if (vendor.find("AMD") != std::string::npos || if (vendor.find("AMD") != std::string::npos ||
vendor.find("ADVANCED") != std::string::npos || vendor.find("ADVANCED MICRO") != std::string::npos ||
vendor.find("ATI") != std::string::npos) vendor.find("ATI ") != std::string::npos)
gpuAMDAvailable = true; gpuAMDAvailable = true;
} }
ANS_DBG("HWInfo", "Detection: cpuIntel=%d gpuNvidia=%d gpuAMD=%d gpuOpenVINO=%d",
cpuIntelAvailable, gpuNvidiaAvailable, gpuAMDAvailable, gpuOpenVINOAvailable);
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// Priority: NVIDIA > AMD > Intel OpenVINO > CPU // Priority: NVIDIA > AMD > Intel OpenVINO > CPU
@@ -361,19 +371,23 @@ namespace ANSCENTER
// ---------------------------------------------------------------- // ----------------------------------------------------------------
if (gpuNvidiaAvailable) { if (gpuNvidiaAvailable) {
std::cout << "[HWInfo] Engine: NVIDIA GPU -> CUDA" << std::endl; std::cout << "[HWInfo] Engine: NVIDIA GPU -> CUDA" << std::endl;
ANS_DBG("HWInfo", "Selected: NVIDIA_GPU (CUDA)");
return EngineType::NVIDIA_GPU; return EngineType::NVIDIA_GPU;
//return EngineType::OPENVINO_GPU; //return EngineType::OPENVINO_GPU;
} }
if (gpuAMDAvailable) { if (gpuAMDAvailable) {
std::cout << "[HWInfo] Engine: AMD GPU -> DirectML" << std::endl; std::cout << "[HWInfo] Engine: AMD GPU -> DirectML" << std::endl;
ANS_DBG("HWInfo", "Selected: AMD_GPU (DirectML)");
return EngineType::AMD_GPU; return EngineType::AMD_GPU;
} }
if (gpuOpenVINOAvailable) { if (gpuOpenVINOAvailable) {
std::cout << "[HWInfo] Engine: Intel CPU -> OpenVINO" << std::endl; std::cout << "[HWInfo] Engine: Intel CPU -> OpenVINO" << std::endl;
ANS_DBG("HWInfo", "Selected: OPENVINO_GPU (OpenVINO)");
return EngineType::OPENVINO_GPU; return EngineType::OPENVINO_GPU;
} }
std::cout << "[HWInfo] Engine: CPU (fallback)" << std::endl; std::cout << "[HWInfo] Engine: CPU (fallback)" << std::endl;
ANS_DBG("HWInfo", "Selected: CPU (fallback)");
return EngineType::CPU; return EngineType::CPU;
} }

View File

@@ -8,7 +8,7 @@
// Set to 0 for production builds to eliminate all debug output overhead. // Set to 0 for production builds to eliminate all debug output overhead.
// ============================================================================ // ============================================================================
#ifndef ANSCORE_DEBUGVIEW #ifndef ANSCORE_DEBUGVIEW
#define ANSCORE_DEBUGVIEW 0 // 1 = enabled (debug), 0 = disabled (production) #define ANSCORE_DEBUGVIEW 1 // 1 = enabled (debug), 0 = disabled (production)
#endif #endif
// ANS_DBG: Debug logging macro for DebugView (OutputDebugStringA on Windows). // ANS_DBG: Debug logging macro for DebugView (OutputDebugStringA on Windows).

View File

@@ -372,6 +372,7 @@ namespace ANSCENTER {
bool ANSFacialRecognition::LoadEngine() { bool ANSFacialRecognition::LoadEngine() {
try { try {
ANS_DBG("ANSFR", "LoadEngine: starting");
// Unload existing engine (handles its own locking) // Unload existing engine (handles its own locking)
UnloadEngine(); UnloadEngine();
@@ -379,17 +380,23 @@ namespace ANSCENTER {
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
ANS_DBG("ANSFR", "LoadEngine: InitializeDetector...");
if (!InitializeDetector()) { if (!InitializeDetector()) {
LogThreadSafe("ANSFacialRecognition::LoadEngine", LogThreadSafe("ANSFacialRecognition::LoadEngine",
"Failed to initialize detector"); "Failed to initialize detector");
ANS_DBG("ANSFR", "LoadEngine: InitializeDetector FAILED");
return false; return false;
} }
ANS_DBG("ANSFR", "LoadEngine: InitializeDetector OK");
ANS_DBG("ANSFR", "LoadEngine: InitializeRecognizer...");
if (!InitializeRecognizer()) { if (!InitializeRecognizer()) {
LogThreadSafe("ANSFacialRecognition::LoadEngine", LogThreadSafe("ANSFacialRecognition::LoadEngine",
"Failed to initialize recognizer"); "Failed to initialize recognizer");
ANS_DBG("ANSFR", "LoadEngine: InitializeRecognizer FAILED");
return false; return false;
} }
ANS_DBG("ANSFR", "LoadEngine: InitializeRecognizer OK");
_recognizerModelFolder = _recognizer->GetModelFolder(); _recognizerModelFolder = _recognizer->GetModelFolder();
} }
@@ -402,7 +409,9 @@ namespace ANSCENTER {
} }
// Configure device // Configure device
ANS_DBG("ANSFR", "LoadEngine: getting OpenVINO device...");
std::string deviceName = GetOpenVINODevice(); std::string deviceName = GetOpenVINODevice();
ANS_DBG("ANSFR", "LoadEngine: OpenVINO device=%s", deviceName.c_str());
if (deviceName == "NPU") { if (deviceName == "NPU") {
// Configure NPU with GPU fallback // Configure NPU with GPU fallback
@@ -795,6 +804,7 @@ namespace ANSCENTER {
} }
bool ANSFacialRecognition::InitializeRecognizer() { bool ANSFacialRecognition::InitializeRecognizer() {
try { try {
ANS_DBG("ANSFR", "InitializeRecognizer: starting, modelPath=%s", _recognizerFilePath.c_str());
// Create recognizer instance // Create recognizer instance
_recognizer = std::make_unique<ANSFaceRecognizer>(); _recognizer = std::make_unique<ANSFaceRecognizer>();
_recognizer->SetMaxSlotsPerGpu(m_maxSlotsPerGpu); _recognizer->SetMaxSlotsPerGpu(m_maxSlotsPerGpu);
@@ -810,6 +820,9 @@ namespace ANSCENTER {
recognizerConfig.modelConfThreshold = 0.48f; recognizerConfig.modelConfThreshold = 0.48f;
recognizerConfig.unknownPersonThreshold = _config._knownPersonThreshold; recognizerConfig.unknownPersonThreshold = _config._knownPersonThreshold;
ANS_DBG("ANSFR", "InitializeRecognizer: knownPersonThreshold=%.3f NMS=%.3f bbox=%.3f",
_config._knownPersonThreshold, _config._detThresholdNMS, _config._detThresholdBbox);
// LOCK DURING INITIALIZATION // LOCK DURING INITIALIZATION
std::string labelMap; std::string labelMap;
bool initSuccess; bool initSuccess;
@@ -817,6 +830,7 @@ namespace ANSCENTER {
{ {
std::lock_guard<std::mutex> lock(_recognitionMutex); std::lock_guard<std::mutex> lock(_recognitionMutex);
ANS_DBG("ANSFR", "InitializeRecognizer: calling _recognizer->Initialize...");
initSuccess = _recognizer->Initialize( initSuccess = _recognizer->Initialize(
_licenseKey, _licenseKey,
recognizerConfig, recognizerConfig,
@@ -829,12 +843,15 @@ namespace ANSCENTER {
if (!initSuccess) { if (!initSuccess) {
LogThreadSafe("ANSFacialRecognition::InitializeRecognizer", LogThreadSafe("ANSFacialRecognition::InitializeRecognizer",
"Failed to initialize recognizer - check file path: " + _recognizerFilePath); "Failed to initialize recognizer - check file path: " + _recognizerFilePath);
ANS_DBG("ANSFR", "InitializeRecognizer: FAILED - path=%s", _recognizerFilePath.c_str());
return false; return false;
} }
ANS_DBG("ANSFR", "InitializeRecognizer: SUCCESS");
return true; return true;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
ANS_DBG("ANSFR", "InitializeRecognizer EXCEPTION: %s", e.what());
LogThreadSafe("ANSFacialRecognition::InitializeRecognizer", LogThreadSafe("ANSFacialRecognition::InitializeRecognizer",
"Failed to initialize: " + std::string(e.what())); "Failed to initialize: " + std::string(e.what()));
return false; return false;
@@ -2674,13 +2691,19 @@ namespace ANSCENTER {
std::string ANSFacialRecognition::GetOpenVINODevice() { std::string ANSFacialRecognition::GetOpenVINODevice() {
ov::Core core; ov::Core core;
std::vector<std::string> available_devices = core.get_available_devices(); std::vector<std::string> available_devices = core.get_available_devices();
ANS_DBG("ANSFR", "GetOpenVINODevice: %zu devices available", available_devices.size());
for (const auto& d : available_devices) {
ANS_DBG("ANSFR", " OpenVINO device: %s", d.c_str());
}
// Prioritize devices: NPU > GPU > CPU // Prioritize devices: NPU > GPU > CPU
std::vector<std::string> priority_devices = { "NPU","GPU","CPU" }; std::vector<std::string> priority_devices = { "NPU","GPU","CPU" };
for (const auto& device : priority_devices) { for (const auto& device : priority_devices) {
if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) { if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) {
ANS_DBG("ANSFR", "GetOpenVINODevice: selected %s", device.c_str());
return device; // Return the first available device based on priority return device; // Return the first available device based on priority
} }
} }
ANS_DBG("ANSFR", "GetOpenVINODevice: fallback to CPU");
return "CPU"; return "CPU";
} }

View File

@@ -363,6 +363,10 @@ extern "C" ANSFR_API int GetFaces(ANSCENTER::ANSFacialRecognition * *Handle,i
extern "C" ANSFR_API int DeleteFacesByUser(ANSCENTER::ANSFacialRecognition * *Handle,int userId); extern "C" ANSFR_API int DeleteFacesByUser(ANSCENTER::ANSFacialRecognition * *Handle,int userId);
extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, unsigned int bufferLength);
// LStrHandle-based InsertUser/UpdateUser — handles UTF-16LE and UTF-8 input from LabVIEW
extern "C" ANSFR_API int InsertUser_LV(ANSCENTER::ANSFacialRecognition * *Handle, const char* userCode, LStrHandle userName);
extern "C" ANSFR_API int UpdateUser_LV(ANSCENTER::ANSFacialRecognition * *Handle, int userId, const char* userCode, LStrHandle userName);
// Unicode conversion utilities for LabVIEW wrapper classes // Unicode conversion utilities for LabVIEW wrapper classes
extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1); extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM = 1);
extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);

View File

@@ -22,12 +22,19 @@ namespace ANSCENTER {
ov::Core core; ov::Core core;
std::vector<std::string> available_devices = core.get_available_devices(); std::vector<std::string> available_devices = core.get_available_devices();
ANS_DBG("FaceRecognizer", "OpenVINO available devices: %zu", available_devices.size());
for (const auto& d : available_devices) {
ANS_DBG("FaceRecognizer", " OpenVINO device: %s", d.c_str());
}
std::vector<std::string> priority_devices = { "GPU", "CPU" }; std::vector<std::string> priority_devices = { "GPU", "CPU" };
for (const auto& device : priority_devices) { for (const auto& device : priority_devices) {
if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) { if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) {
ANS_DBG("FaceRecognizer", "OpenVINO selected device: %s", device.c_str());
return device; return device;
} }
} }
ANS_DBG("FaceRecognizer", "OpenVINO fallback to CPU");
return "CPU"; return "CPU";
} }
@@ -37,12 +44,18 @@ namespace ANSCENTER {
const std::string& modelZipPassword, const std::string& modelZipPassword,
std::string& labelMap) std::string& labelMap)
{ {
ANS_DBG("FaceRecognizer", "Initialize: modelZip=%s", modelZipFilePath.c_str());
bool result = ANSFRBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); bool result = ANSFRBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
if (!result) return false; if (!result) {
ANS_DBG("FaceRecognizer", "ANSFRBase::Initialize FAILED");
return false;
}
#ifdef CPU_MODE #ifdef CPU_MODE
engineType = EngineType::CPU; engineType = EngineType::CPU;
ANS_DBG("FaceRecognizer", "CPU_MODE forced: engineType=CPU");
#else #else
engineType = ANSLicenseHelper::CheckHardwareInformation(); engineType = ANSLicenseHelper::CheckHardwareInformation();
ANS_DBG("FaceRecognizer", "HW detection: engineType=%d", static_cast<int>(engineType));
#endif #endif
try { try {
_modelConfig = modelConfig; _modelConfig = modelConfig;
@@ -52,6 +65,12 @@ namespace ANSCENTER {
m_knownPersonThresh = _modelConfig.unknownPersonThreshold; m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
if (m_knownPersonThresh == 0.0f) m_knownPersonThresh = 0.35f; if (m_knownPersonThresh == 0.0f) m_knownPersonThresh = 0.35f;
ANS_DBG("FaceRecognizer", "engineType=%d (NVIDIA=%d, OPENVINO=%d, AMD=%d, CPU=%d)",
static_cast<int>(engineType),
static_cast<int>(EngineType::NVIDIA_GPU),
static_cast<int>(EngineType::OPENVINO_GPU),
static_cast<int>(EngineType::AMD_GPU),
static_cast<int>(EngineType::CPU));
if (engineType == EngineType::NVIDIA_GPU) { if (engineType == EngineType::NVIDIA_GPU) {
// 1. Load ONNX model // 1. Load ONNX model
std::string onnxfile = CreateFilePath(_modelFolder, "ansfacerecognizer.onnx"); std::string onnxfile = CreateFilePath(_modelFolder, "ansfacerecognizer.onnx");
@@ -97,6 +116,11 @@ namespace ANSCENTER {
_modelFilePath, __FILE__, __LINE__); _modelFilePath, __FILE__, __LINE__);
return false; return false;
} }
// Create CUDA stream for GPU preprocessing (lazy init)
if (!m_gpuStream) {
m_gpuStream = std::make_unique<cv::cuda::Stream>();
}
} }
} }
else { else {
@@ -112,10 +136,13 @@ namespace ANSCENTER {
_modelFilePath, __FILE__, __LINE__); _modelFilePath, __FILE__, __LINE__);
return false; return false;
} }
faceRecognizer = std::make_unique<GlintArcFace>(faceidModel); ANS_DBG("FaceRecognizer", "Creating GlintArcFace with engineType=%d model=%s",
static_cast<int>(engineType), faceidModel.c_str());
faceRecognizer = std::make_unique<GlintArcFace>(faceidModel, engineType);
if (!faceRecognizer) { if (!faceRecognizer) {
_logger.LogFatal("ANSFaceRecognizer::Initialize", _logger.LogFatal("ANSFaceRecognizer::Initialize",
"Failed to initialize ONNX face recognizer", __FILE__, __LINE__); "Failed to initialize ONNX face recognizer", __FILE__, __LINE__);
ANS_DBG("FaceRecognizer", "FAILED: GlintArcFace returned null");
return false; return false;
} }
#else #else
@@ -151,6 +178,7 @@ namespace ANSCENTER {
return true; return true;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
ANS_DBG("FaceRecognizer", "Initialize EXCEPTION: %s", e.what());
_logger.LogFatal("ANSFaceRecognizer::Initialize", e.what(), __FILE__, __LINE__); _logger.LogFatal("ANSFaceRecognizer::Initialize", e.what(), __FILE__, __LINE__);
return false; return false;
} }
@@ -688,8 +716,8 @@ namespace ANSCENTER {
} }
cv::Mat cpuRGB; cv::Mat cpuRGB;
cv::cvtColor(cpuResized, cpuRGB, cv::COLOR_BGR2RGB); cv::cvtColor(cpuResized, cpuRGB, cv::COLOR_BGR2RGB);
m_gpuRgb.upload(cpuRGB, m_gpuStream); m_gpuRgb.upload(cpuRGB, *m_gpuStream);
m_gpuStream.waitForCompletion(); m_gpuStream->waitForCompletion();
// Prepare inference inputs // Prepare inference inputs
std::vector<cv::cuda::GpuMat> inputVec; std::vector<cv::cuda::GpuMat> inputVec;
@@ -703,7 +731,7 @@ namespace ANSCENTER {
bool succ = m_trtEngine->runInference(inputs, featureVectors); bool succ = m_trtEngine->runInference(inputs, featureVectors);
// Synchronize stream // Synchronize stream
m_gpuStream.waitForCompletion(); m_gpuStream->waitForCompletion();
if (!succ) { if (!succ) {
_logger.LogError("ANSFaceRecognizer::RunArcFace", _logger.LogError("ANSFaceRecognizer::RunArcFace",
@@ -783,11 +811,11 @@ namespace ANSCENTER {
cv::cuda::GpuMat d_img = gpuFaceROIs[i]; // already on GPU cv::cuda::GpuMat d_img = gpuFaceROIs[i]; // already on GPU
if (d_img.cols != GPU_FACE_WIDTH || d_img.rows != GPU_FACE_HEIGHT) { if (d_img.cols != GPU_FACE_WIDTH || d_img.rows != GPU_FACE_HEIGHT) {
cv::cuda::GpuMat d_resized; cv::cuda::GpuMat d_resized;
cv::cuda::resize(d_img, d_resized, targetSize, 0, 0, cv::INTER_LINEAR, m_gpuStream); cv::cuda::resize(d_img, d_resized, targetSize, 0, 0, cv::INTER_LINEAR, *m_gpuStream);
d_img = d_resized; d_img = d_resized;
} }
cv::cuda::GpuMat d_rgb; cv::cuda::GpuMat d_rgb;
cv::cuda::cvtColor(d_img, d_rgb, cv::COLOR_BGR2RGB, 0, m_gpuStream); cv::cuda::cvtColor(d_img, d_rgb, cv::COLOR_BGR2RGB, 0, *m_gpuStream);
batchGpu.emplace_back(std::move(d_rgb)); batchGpu.emplace_back(std::move(d_rgb));
} else { } else {
const auto& roi = faceROIs[i]; const auto& roi = faceROIs[i];
@@ -807,7 +835,7 @@ namespace ANSCENTER {
cv::Mat cpuRGB; cv::Mat cpuRGB;
cv::cvtColor(cpuResized, cpuRGB, cv::COLOR_BGR2RGB); cv::cvtColor(cpuResized, cpuRGB, cv::COLOR_BGR2RGB);
cv::cuda::GpuMat d_rgb; cv::cuda::GpuMat d_rgb;
d_rgb.upload(cpuRGB, m_gpuStream); d_rgb.upload(cpuRGB, *m_gpuStream);
batchGpu.emplace_back(std::move(d_rgb)); batchGpu.emplace_back(std::move(d_rgb));
} }
} }
@@ -823,7 +851,7 @@ namespace ANSCENTER {
FR_START_TIMER(trt_infer); FR_START_TIMER(trt_infer);
std::vector<std::vector<std::vector<float>>> featureVectors; std::vector<std::vector<std::vector<float>>> featureVectors;
bool succ = m_trtEngine->runInference(inputs, featureVectors); bool succ = m_trtEngine->runInference(inputs, featureVectors);
m_gpuStream.waitForCompletion(); m_gpuStream->waitForCompletion();
FR_END_TIMER(trt_infer, "RunArcFaceBatch TRT inference (batch=" + std::to_string(actualCount) + ")"); FR_END_TIMER(trt_infer, "RunArcFaceBatch TRT inference (batch=" + std::to_string(actualCount) + ")");
if (!succ) { if (!succ) {

View File

@@ -112,7 +112,9 @@ namespace ANSCENTER {
const int CPU_FACE_HEIGHT = 160; const int CPU_FACE_HEIGHT = 160;
#endif #endif
// Pooled GPU buffers to avoid per-frame allocation (Fix #8) // Pooled GPU buffers to avoid per-frame allocation (Fix #8)
cv::cuda::Stream m_gpuStream; // Lazy-initialized: only created when engineType == NVIDIA_GPU
// to avoid triggering CUDA init on non-NVIDIA systems.
std::unique_ptr<cv::cuda::Stream> m_gpuStream;
cv::cuda::GpuMat m_gpuImg; cv::cuda::GpuMat m_gpuImg;
cv::cuda::GpuMat m_gpuResized; cv::cuda::GpuMat m_gpuResized;
cv::cuda::GpuMat m_gpuRgb; cv::cuda::GpuMat m_gpuRgb;

View File

@@ -1396,35 +1396,53 @@ namespace ANSCENTER
break; break;
} }
// NPU availability is probed once per process to avoid
// repeated "Failed to load shared library" errors.
static bool s_npuProbed = false;
static bool s_npuAvailable = false;
const std::string precision = "FP16"; const std::string precision = "FP16";
const std::string numberOfThreads = "8"; const std::string numberOfThreads = "8";
const std::string numberOfStreams = "8"; const std::string numberOfStreams = "8";
std::vector<std::unordered_map<std::string, std::string>> try_configs = { auto makeConfig = [&](const std::string& device) {
{ {"device_type","AUTO:NPU,GPU"}, {"precision",precision}, return std::unordered_map<std::string, std::string>{
{"num_of_threads",numberOfThreads}, {"num_streams",numberOfStreams}, {"device_type", device}, {"precision", precision},
{"enable_opencl_throttling","False"}, {"enable_qdq_optimizer","True"} }, {"num_of_threads", numberOfThreads}, {"num_streams", numberOfStreams},
{ {"device_type","GPU.0"}, {"precision",precision}, {"enable_opencl_throttling", "False"}, {"enable_qdq_optimizer", "True"}
{"num_of_threads",numberOfThreads}, {"num_streams",numberOfStreams}, };
{"enable_opencl_throttling","False"}, {"enable_qdq_optimizer","True"} },
{ {"device_type","GPU.1"}, {"precision",precision},
{"num_of_threads",numberOfThreads}, {"num_streams",numberOfStreams},
{"enable_opencl_throttling","False"}, {"enable_qdq_optimizer","True"} },
{ {"device_type","AUTO:GPU,CPU"}, {"precision",precision},
{"num_of_threads",numberOfThreads}, {"num_streams",numberOfStreams},
{"enable_opencl_throttling","False"}, {"enable_qdq_optimizer","True"} }
}; };
std::vector<std::unordered_map<std::string, std::string>> try_configs;
if (!s_npuProbed || s_npuAvailable) {
try_configs.push_back(makeConfig("AUTO:NPU,GPU"));
}
try_configs.push_back(makeConfig("GPU.0"));
try_configs.push_back(makeConfig("GPU.1"));
try_configs.push_back(makeConfig("AUTO:GPU,CPU"));
for (const auto& config : try_configs) { for (const auto& config : try_configs) {
try { try {
_ortLivenessSessionOptions->AppendExecutionProvider_OpenVINO_V2(config); _ortLivenessSessionOptions->AppendExecutionProvider_OpenVINO_V2(config);
std::cout << "[ANSFDBase] OpenVINO EP attached (" std::cout << "[ANSFDBase] OpenVINO EP attached ("
<< config.at("device_type") << ")." << std::endl; << config.at("device_type") << ")." << std::endl;
attached = true; attached = true;
if (config.at("device_type").find("NPU") != std::string::npos) {
s_npuProbed = true;
s_npuAvailable = true;
}
break; break;
} }
catch (const Ort::Exception& e) { catch (const Ort::Exception& e) {
this->_logger.LogError("ANSFDBase::LoadLivenessModel", e.what(), __FILE__, __LINE__); if (config.at("device_type").find("NPU") != std::string::npos) {
if (!s_npuProbed) {
std::cout << "[ANSFDBase] NPU not available — skipping NPU configs for subsequent models." << std::endl;
}
s_npuProbed = true;
s_npuAvailable = false;
} else {
this->_logger.LogError("ANSFDBase::LoadLivenessModel", e.what(), __FILE__, __LINE__);
}
} }
} }

View File

@@ -1003,6 +1003,131 @@ namespace ANSCENTER
#endif #endif
} }
std::vector<unsigned char> ANSUtilities::RepairLabVIEWUTF16LE(const unsigned char* data, int len) {
std::vector<unsigned char> repaired;
if (!data || len <= 0) return repaired;
repaired.reserve(len + 32);
// Helper: emit a BMP codepoint as UTF-16LE pair
auto emitU16 = [&](uint16_t cp) {
repaired.push_back(static_cast<unsigned char>(cp & 0xFF));
repaired.push_back(static_cast<unsigned char>((cp >> 8) & 0xFF));
};
for (int i = 0; i < len; ) {
unsigned char b = data[i];
// --- 1. Detect embedded UTF-8 multi-byte sequences ---
// LabVIEW text controls may mix UTF-8 encoded characters into a
// UTF-16LE stream. UTF-8 lead bytes (C2-F4) followed by valid
// continuation bytes (80-BF) are a strong signal.
// We decode the UTF-8 codepoint and re-encode as UTF-16LE.
// 2-byte UTF-8: 110xxxxx 10xxxxxx (U+0080 .. U+07FF)
if (b >= 0xC2 && b <= 0xDF && i + 1 < len) {
unsigned char b1 = data[i + 1];
if ((b1 & 0xC0) == 0x80) {
uint32_t cp = ((b & 0x1F) << 6) | (b1 & 0x3F);
emitU16(static_cast<uint16_t>(cp));
i += 2;
continue;
}
}
// 3-byte UTF-8: 1110xxxx 10xxxxxx 10xxxxxx (U+0800 .. U+FFFF)
if (b >= 0xE0 && b <= 0xEF && i + 2 < len) {
unsigned char b1 = data[i + 1];
unsigned char b2 = data[i + 2];
if ((b1 & 0xC0) == 0x80 && (b2 & 0xC0) == 0x80) {
uint32_t cp = ((b & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F);
// Reject overlong encodings and surrogates
if (cp >= 0x0800 && (cp < 0xD800 || cp > 0xDFFF)) {
emitU16(static_cast<uint16_t>(cp));
i += 3;
continue;
}
}
}
// 4-byte UTF-8: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (U+10000 .. U+10FFFF)
if (b >= 0xF0 && b <= 0xF4 && i + 3 < len) {
unsigned char b1 = data[i + 1];
unsigned char b2 = data[i + 2];
unsigned char b3 = data[i + 3];
if ((b1 & 0xC0) == 0x80 && (b2 & 0xC0) == 0x80 && (b3 & 0xC0) == 0x80) {
uint32_t cp = ((b & 0x07) << 18) | ((b1 & 0x3F) << 12)
| ((b2 & 0x3F) << 6) | (b3 & 0x3F);
if (cp >= 0x10000 && cp <= 0x10FFFF) {
// Surrogate pair
cp -= 0x10000;
emitU16(static_cast<uint16_t>(0xD800 + (cp >> 10)));
emitU16(static_cast<uint16_t>(0xDC00 + (cp & 0x3FF)));
i += 4;
continue;
}
}
}
// --- 2. Normal UTF-16LE pair (low byte + 0x00 high byte) ---
if (i + 1 < len && data[i + 1] == 0x00) {
repaired.push_back(data[i]);
repaired.push_back(0x00);
i += 2;
}
// --- 3. Lone space byte — LabVIEW dropped the 0x00 high byte ---
else if (b == 0x20 && (i + 1 >= len || data[i + 1] != 0x00)) {
repaired.push_back(0x20);
repaired.push_back(0x00);
i += 1;
}
// --- 4. Non-ASCII UTF-16LE pair (e.g. ễ = C5 1E) ---
else if (i + 1 < len) {
repaired.push_back(data[i]);
repaired.push_back(data[i + 1]);
i += 2;
}
// --- 5. Trailing odd byte — skip ---
else {
i++;
}
}
return repaired;
}
bool ANSUtilities::IsValidUTF8(const unsigned char* data, int len) {
if (!data || len <= 0) return false;
bool hasMultiByte = false;
for (int i = 0; i < len; ) {
unsigned char b = data[i];
if (b <= 0x7F) {
// ASCII — valid, but alone doesn't prove UTF-8
i++;
} else if (b >= 0xC2 && b <= 0xDF) {
// 2-byte sequence
if (i + 1 >= len || (data[i + 1] & 0xC0) != 0x80) return false;
hasMultiByte = true;
i += 2;
} else if (b >= 0xE0 && b <= 0xEF) {
// 3-byte sequence
if (i + 2 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80) return false;
uint32_t cp = ((b & 0x0F) << 12) | ((data[i + 1] & 0x3F) << 6) | (data[i + 2] & 0x3F);
if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF)) return false; // overlong or surrogate
hasMultiByte = true;
i += 3;
} else if (b >= 0xF0 && b <= 0xF4) {
// 4-byte sequence
if (i + 3 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) return false;
uint32_t cp = ((b & 0x07) << 18) | ((data[i + 1] & 0x3F) << 12) | ((data[i + 2] & 0x3F) << 6) | (data[i + 3] & 0x3F);
if (cp < 0x10000 || cp > 0x10FFFF) return false;
hasMultiByte = true;
i += 4;
} else {
return false; // invalid lead byte (C0, C1, F5-FF)
}
}
// Only confirm UTF-8 if we found at least one multi-byte sequence.
// Pure ASCII is ambiguous — let the caller decide.
return hasMultiByte;
}
std::string ANSUtilities::ConvertUTF16LEToUnicodeEscapes(const char* utf16leBytes, int byteLen) { std::string ANSUtilities::ConvertUTF16LEToUnicodeEscapes(const char* utf16leBytes, int byteLen) {
if (!utf16leBytes || byteLen <= 0) return ""; if (!utf16leBytes || byteLen <= 0) return "";
int offset = 0; int offset = 0;
@@ -1013,13 +1138,18 @@ namespace ANSCENTER
offset = 2; offset = 2;
} }
int remaining = byteLen - offset; int remaining = byteLen - offset;
if (remaining <= 0 || remaining % 2 != 0) return ""; if (remaining <= 0) return "";
// Drop trailing odd byte if present (e.g. null terminator appended by LabVIEW)
if (remaining % 2 != 0) remaining--;
int endPos = offset + remaining; // safe end position (even-aligned)
std::string result; std::string result;
result.reserve(remaining * 3); result.reserve(remaining * 3);
for (int i = offset; i + 1 < byteLen; i += 2) { for (int i = offset; i + 1 < endPos; i += 2) {
uint16_t codepoint = static_cast<unsigned char>(utf16leBytes[i]) uint16_t codepoint = static_cast<unsigned char>(utf16leBytes[i])
| (static_cast<unsigned char>(utf16leBytes[i + 1]) << 8); | (static_cast<unsigned char>(utf16leBytes[i + 1]) << 8);
// Pass through printable ASCII including space (0x20-0x7E)
// Escape control characters and non-ASCII as \uXXXX
if (codepoint >= 0x20 && codepoint <= 0x7E) { if (codepoint >= 0x20 && codepoint <= 0x7E) {
result += static_cast<char>(codepoint); result += static_cast<char>(codepoint);
} else { } else {
@@ -1038,10 +1168,16 @@ namespace ANSCENTER
size_t i = 0; size_t i = 0;
while (i < utf8Str.size()) { while (i < utf8Str.size()) {
unsigned char c = static_cast<unsigned char>(utf8Str[i]); unsigned char c = static_cast<unsigned char>(utf8Str[i]);
if (c <= 0x7F) { if (c >= 0x20 && c <= 0x7E) {
// ASCII byte -- pass through as-is (including \r, \n, \t, space, etc.) // Printable ASCII including space (0x20-0x7E) — pass through as-is
result += utf8Str[i]; result += utf8Str[i];
i++; i++;
} else if (c <= 0x7F) {
// Control chars (0x00-0x1F), DEL (0x7F) — escape as \uXXXX
char buf[7];
snprintf(buf, sizeof(buf), "\\u%04x", c);
result += buf;
i++;
} else { } else {
// Multi-byte UTF-8 sequence -- decode to Unicode codepoint // Multi-byte UTF-8 sequence -- decode to Unicode codepoint
uint32_t codepoint = 0; uint32_t codepoint = 0;

View File

@@ -5,6 +5,7 @@
#include "LabVIEWHeader/extcode.h" #include "LabVIEWHeader/extcode.h"
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
#include "ANSLicense.h" #include "ANSLicense.h"
#include <CkRest.h> #include <CkRest.h>
#include "CkAuthGoogle.h" #include "CkAuthGoogle.h"
@@ -118,6 +119,19 @@ namespace ANSCENTER {
// Useful for encoding Unicode text into JSON-safe ASCII. // Useful for encoding Unicode text into JSON-safe ASCII.
static std::string ConvertUTF8ToUnicodeEscapes(const std::string& utf8Str); static std::string ConvertUTF8ToUnicodeEscapes(const std::string& utf8Str);
// Repair mixed-encoding input from LabVIEW text controls.
// LabVIEW may produce a mix of UTF-16LE pairs and UTF-8 multi-byte
// sequences in a single byte stream. It may also insert space (0x20) as
// a single byte without the 0x00 high byte.
// This function normalizes everything to proper UTF-16LE pairs.
// Input: raw bytes from LStrHandle (BOM should already be stripped).
// Output: clean UTF-16LE with proper 2-byte alignment.
static std::vector<unsigned char> RepairLabVIEWUTF16LE(const unsigned char* data, int len);
// Check if a byte sequence is valid UTF-8.
// Returns true if all bytes form valid UTF-8 sequences.
static bool IsValidUTF8(const unsigned char* data, int len);
// Double-escape \uXXXX sequences: \u1ee7 becomes \\u1ee7. // Double-escape \uXXXX sequences: \u1ee7 becomes \\u1ee7.
// Useful when the string will be embedded in JSON and the literal \uXXXX must survive parsing. // Useful when the string will be embedded in JSON and the literal \uXXXX must survive parsing.
static std::string DoubleEscapeUnicode(const std::string& str); static std::string DoubleEscapeUnicode(const std::string& str);
@@ -276,6 +290,9 @@ extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr,
extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes(const unsigned char* utf16leBytes, int byteLen, LStrHandle result); extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
extern "C" ANSULT_API int ANSConvertUnicodeEscapesToUTF8(const char* escapedStr, LStrHandle result); extern "C" ANSULT_API int ANSConvertUnicodeEscapesToUTF8(const char* escapedStr, LStrHandle result);
// LStrHandle-safe versions: input is LStrHandle (preserves null bytes in UTF-16LE data)
extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8_LV(LStrHandle input, LStrHandle result);
extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes_LV(LStrHandle input, LStrHandle result);
extern "C" ANSULT_API int ANSConvertUTF8ToUnicodeEscapes(const char* utf8Str, LStrHandle result); extern "C" ANSULT_API int ANSConvertUTF8ToUnicodeEscapes(const char* utf8Str, LStrHandle result);
extern "C" ANSULT_API int ANSDoubleEscapeUnicode(const char* str, LStrHandle result); extern "C" ANSULT_API int ANSDoubleEscapeUnicode(const char* str, LStrHandle result);
extern "C" ANSULT_API int ANSConvertUTF8ToDoubleEscapedUnicode(const char* utf8Str, LStrHandle result); extern "C" ANSULT_API int ANSConvertUTF8ToDoubleEscapedUnicode(const char* utf8Str, LStrHandle result);

View File

@@ -18,6 +18,7 @@
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <iomanip>
#include "ANSFR.h" #include "ANSFR.h"
#include "fastdeploy/vision.h" #include "fastdeploy/vision.h"
#include "ANSFilePlayer.h" #include "ANSFilePlayer.h"
@@ -820,6 +821,317 @@ int ANSVISTest() {
return 0; return 0;
} }
// ANSVISTestCPU — CPU-only variant of ANSVISTest for Intel CPU PCs without NVIDIA GPU.
// Uses CPU face detector model and forces precision=0 (FP32) for ONNX Runtime / OpenVINO.
int ANSVISTestCPU() {
boost::property_tree::ptree pt;
std::string databaseFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFR\\ANSFR.db";
std::string recognizerFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFR\\ANS_FaceRecognizer_v1.1.zip";
std::string facedetectorFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFDET\\ANS_GenericFD(CPU)_v1.0.zip";
std::string videoFilePath = "C:\\ProgramData\\ANSCENTER\\Shared\\classroom.mp4";
const char* configFilePath = "";
ANSCENTER::ANSFacialRecognition* infHandle = nullptr;
std::string licenseKey = "";
int enableHeadPose = 1;
int enableFaceLiveness = 1;
int enableAgeGender = 1;
int enableEmotion = 1;
int enableAntispoofing = 1;
int precision = 0; // FP32
std::cout << "=== ANSVISTestCPU ===" << std::endl;
std::cout << "Database: " << databaseFilePath << std::endl;
std::cout << "Recognizer: " << recognizerFilePath << std::endl;
std::cout << "FaceDetector: " << facedetectorFilePath << std::endl;
std::cout << "Video: " << videoFilePath << std::endl;
// Step 1: Create handle
std::cout << "[CPU Test] Step 1: Creating handle..." << std::endl;
int result = CreateANSRFHandle(&infHandle,
licenseKey.c_str(),
configFilePath,
databaseFilePath.c_str(),
recognizerFilePath.c_str(),
facedetectorFilePath.c_str(),
precision,
0.25, enableAgeGender, enableEmotion, enableHeadPose, 30, 0.55, enableFaceLiveness, enableAntispoofing);
std::cout << "[CPU Test] CreateANSRFHandle result: " << result << std::endl;
if (result < 0) {
std::cerr << "[CPU Test] FAILED: CreateANSRFHandle returned " << result << std::endl;
return -1;
}
// Step 2: Load engine
std::cout << "[CPU Test] Step 2: Loading engine..." << std::endl;
int loadEngineResult = LoadANSRFEngine(&infHandle);
std::cout << "[CPU Test] LoadANSRFEngine result: " << loadEngineResult << std::endl;
if (loadEngineResult != 1) {
std::cerr << "[CPU Test] FAILED: LoadANSRFEngine returned " << loadEngineResult << std::endl;
ReleaseANSRFHandle(&infHandle);
return -2;
}
// Step 3: Reload database
std::cout << "[CPU Test] Step 3: Reloading database..." << std::endl;
Reload(&infHandle);
// Step 4: Open video
std::cout << "[CPU Test] Step 4: Opening video..." << std::endl;
cv::VideoCapture capture(videoFilePath);
if (!capture.isOpened()) {
std::cerr << "[CPU Test] FAILED: Could not open video: " << videoFilePath << std::endl;
ReleaseANSRFHandle(&infHandle);
return -3;
}
int totalFrames = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_COUNT));
std::cout << "[CPU Test] Video opened: " << totalFrames << " frames" << std::endl;
// Step 5: Run inference on video frames
std::cout << "[CPU Test] Step 5: Running inference..." << std::endl;
int frameIndex = 0;
int totalDetections = 0;
double totalInferenceMs = 0.0;
int maxFrames = 200; // Process up to 200 frames for the test
while (frameIndex < maxFrames) {
cv::Mat frame;
if (!capture.read(frame)) {
std::cout << "[CPU Test] End of video at frame " << frameIndex << std::endl;
break;
}
frameIndex++;
unsigned int bufferLength = 0;
unsigned char* jpeg_string = ANSCENTER::ANSUtilityHelper::CVMatToBytes(frame, bufferLength);
int height = frame.rows;
int width = frame.cols;
auto start = std::chrono::system_clock::now();
string detectionResult = RunANSRFInferenceBinary(&infHandle, jpeg_string, width, height);
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
totalInferenceMs += static_cast<double>(elapsed.count());
delete[] jpeg_string;
if (!detectionResult.empty()) {
try {
pt.clear();
std::stringstream ss;
ss << detectionResult;
boost::property_tree::read_json(ss, pt);
int detCount = 0;
BOOST_FOREACH(const boost::property_tree::ptree::value_type& child, pt.get_child("results")) {
const boost::property_tree::ptree& r = child.second;
const auto class_id = GetData<int>(r, "user_id");
const auto class_name = GetData<std::string>(r, "user_name");
const auto x = GetData<float>(r, "x");
const auto y = GetData<float>(r, "y");
const auto w = GetData<float>(r, "width");
const auto h = GetData<float>(r, "height");
const auto sim = GetData<float>(r, "similarity");
detCount++;
cv::rectangle(frame, cv::Rect(x, y, w, h), cv::Scalar(0, 255, 0), 2);
cv::putText(frame, cv::format("%s:%d-%.2f", class_name.c_str(), class_id, sim),
cv::Point(x, y - 5), 0, 0.6, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
}
totalDetections += detCount;
}
catch (const std::exception& e) {
std::cerr << "[CPU Test] JSON parse error at frame " << frameIndex << ": " << e.what() << std::endl;
}
}
// Show every 10th frame progress
if (frameIndex % 10 == 0) {
std::cout << "[CPU Test] Frame " << frameIndex << "/" << maxFrames
<< " | Time: " << elapsed.count() << "ms"
<< " | Detections: " << totalDetections << std::endl;
}
cv::imshow("ANS CPU Test", frame);
if (cv::waitKey(1) == 27) {
std::cout << "[CPU Test] ESC pressed, stopping." << std::endl;
break;
}
}
// Step 6: Print summary
double avgMs = (frameIndex > 0) ? (totalInferenceMs / frameIndex) : 0.0;
std::cout << "\n=== CPU Test Summary ===" << std::endl;
std::cout << "Frames processed: " << frameIndex << std::endl;
std::cout << "Total detections: " << totalDetections << std::endl;
std::cout << "Avg inference: " << avgMs << " ms/frame" << std::endl;
std::cout << "Total time: " << totalInferenceMs << " ms" << std::endl;
if (frameIndex == 0) {
std::cerr << "[CPU Test] FAILED: No frames processed" << std::endl;
} else {
std::cout << "[CPU Test] PASSED" << std::endl;
}
// Cleanup
capture.release();
cv::destroyAllWindows();
ReleaseANSRFHandle(&infHandle);
std::cout << "[CPU Test] Done." << std::endl;
return (frameIndex > 0) ? 0 : -4;
}
// ANSVISTestCPU_Lightweight — Detection + Recognition only, no attribute models.
// Measures baseline face detection + recognition speed on Intel CPU/iGPU.
int ANSVISTestCPU_Lightweight() {
boost::property_tree::ptree pt;
std::string databaseFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFR\\ANSFR.db";
std::string recognizerFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFR\\ANS_FaceRecognizer_v1.1.zip";
std::string facedetectorFilePath = "C:\\ProgramData\\ANSCENTER\\ANSVIS Server\\ANSFDET\\ANS_GenericFD(CPU)_v1.0.zip";
std::string videoFilePath = "C:\\ProgramData\\ANSCENTER\\Shared\\classroom.mp4";
const char* configFilePath = "";
ANSCENTER::ANSFacialRecognition* infHandle = nullptr;
std::string licenseKey = "";
// All attribute models DISABLED for lightweight test
int enableHeadPose = 0;
int enableFaceLiveness = 0;
int enableAgeGender = 0;
int enableEmotion = 0;
int enableAntispoofing = 0;
int precision = 0; // FP32
std::cout << "\n=== ANSVISTestCPU_Lightweight ===" << std::endl;
std::cout << "Mode: Detection + Recognition ONLY (no attributes)" << std::endl;
std::cout << "Database: " << databaseFilePath << std::endl;
std::cout << "Recognizer: " << recognizerFilePath << std::endl;
std::cout << "FaceDetector: " << facedetectorFilePath << std::endl;
std::cout << "Video: " << videoFilePath << std::endl;
// Step 1: Create handle
std::cout << "[Lightweight] Step 1: Creating handle..." << std::endl;
int result = CreateANSRFHandle(&infHandle,
licenseKey.c_str(),
configFilePath,
databaseFilePath.c_str(),
recognizerFilePath.c_str(),
facedetectorFilePath.c_str(),
precision,
0.25, enableAgeGender, enableEmotion, enableHeadPose, 30, 0.55, enableFaceLiveness, enableAntispoofing);
std::cout << "[Lightweight] CreateANSRFHandle result: " << result << std::endl;
if (result < 0) {
std::cerr << "[Lightweight] FAILED: CreateANSRFHandle returned " << result << std::endl;
return -1;
}
// Step 2: Load engine
std::cout << "[Lightweight] Step 2: Loading engine..." << std::endl;
int loadEngineResult = LoadANSRFEngine(&infHandle);
std::cout << "[Lightweight] LoadANSRFEngine result: " << loadEngineResult << std::endl;
if (loadEngineResult != 1) {
std::cerr << "[Lightweight] FAILED: LoadANSRFEngine returned " << loadEngineResult << std::endl;
ReleaseANSRFHandle(&infHandle);
return -2;
}
// Step 3: Reload database
std::cout << "[Lightweight] Step 3: Reloading database..." << std::endl;
Reload(&infHandle);
// Step 4: Open video
std::cout << "[Lightweight] Step 4: Opening video..." << std::endl;
cv::VideoCapture capture(videoFilePath);
if (!capture.isOpened()) {
std::cerr << "[Lightweight] FAILED: Could not open video: " << videoFilePath << std::endl;
ReleaseANSRFHandle(&infHandle);
return -3;
}
int totalFrames = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_COUNT));
std::cout << "[Lightweight] Video opened: " << totalFrames << " frames" << std::endl;
// Step 5: Run inference
std::cout << "[Lightweight] Step 5: Running inference..." << std::endl;
int frameIndex = 0;
int totalDetections = 0;
double totalInferenceMs = 0.0;
int maxFrames = 200;
while (frameIndex < maxFrames) {
cv::Mat frame;
if (!capture.read(frame)) {
std::cout << "[Lightweight] End of video at frame " << frameIndex << std::endl;
break;
}
frameIndex++;
unsigned int bufferLength = 0;
unsigned char* jpeg_string = ANSCENTER::ANSUtilityHelper::CVMatToBytes(frame, bufferLength);
int height = frame.rows;
int width = frame.cols;
auto start = std::chrono::system_clock::now();
string detectionResult = RunANSRFInferenceBinary(&infHandle, jpeg_string, width, height);
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
totalInferenceMs += static_cast<double>(elapsed.count());
delete[] jpeg_string;
if (!detectionResult.empty()) {
try {
pt.clear();
std::stringstream ss;
ss << detectionResult;
boost::property_tree::read_json(ss, pt);
int detCount = 0;
BOOST_FOREACH(const boost::property_tree::ptree::value_type& child, pt.get_child("results")) {
const boost::property_tree::ptree& r = child.second;
const auto x = GetData<float>(r, "x");
const auto y = GetData<float>(r, "y");
const auto w = GetData<float>(r, "width");
const auto h = GetData<float>(r, "height");
detCount++;
cv::rectangle(frame, cv::Rect(x, y, w, h), cv::Scalar(0, 255, 0), 2);
}
totalDetections += detCount;
}
catch (...) {}
}
if (frameIndex % 10 == 0) {
double avgSoFar = totalInferenceMs / frameIndex;
std::cout << "[Lightweight] Frame " << frameIndex << "/" << maxFrames
<< " | Time: " << elapsed.count() << "ms"
<< " | Avg: " << static_cast<int>(avgSoFar) << "ms"
<< " | FPS: " << std::fixed << std::setprecision(1) << (1000.0 / avgSoFar)
<< " | Faces: " << totalDetections << std::endl;
}
cv::imshow("ANS CPU Lightweight", frame);
if (cv::waitKey(1) == 27) break;
}
// Summary
double avgMs = (frameIndex > 0) ? (totalInferenceMs / frameIndex) : 0.0;
double fps = (avgMs > 0) ? (1000.0 / avgMs) : 0.0;
std::cout << "\n=== Lightweight Test Summary ===" << std::endl;
std::cout << "Frames processed: " << frameIndex << std::endl;
std::cout << "Total detections: " << totalDetections << std::endl;
std::cout << "Avg inference: " << avgMs << " ms/frame" << std::endl;
std::cout << "Avg FPS: " << std::fixed << std::setprecision(1) << fps << std::endl;
std::cout << "Total time: " << totalInferenceMs << " ms" << std::endl;
std::cout << (frameIndex > 0 ? "[Lightweight] PASSED" : "[Lightweight] FAILED") << std::endl;
capture.release();
cv::destroyAllWindows();
ReleaseANSRFHandle(&infHandle);
return (frameIndex > 0) ? 0 : -4;
}
// ANSVISTestFilePlayer — Same as ANSVISTest but uses ANSFILEPLAYER (HW decode + NV12 registry) // ANSVISTestFilePlayer — Same as ANSVISTest but uses ANSFILEPLAYER (HW decode + NV12 registry)
// instead of cv::VideoCapture. This enables NV12 fast-path testing for the FR pipeline: // instead of cv::VideoCapture. This enables NV12 fast-path testing for the FR pipeline:
// - SCRFD face detection uses fused NV12→RGB center-letterbox kernel // - SCRFD face detection uses fused NV12→RGB center-letterbox kernel
@@ -1321,16 +1633,16 @@ int TestCompleteFR1() {
} }
int main() int main()
{ {
//FaceDetectorTest(); //FaceDetectorTest();
//TestFaceRecognition(); //TestFaceRecognition();
//TestCompleteFR1(); //TestCompleteFR1();
//ANSVISImageTest(); //ANSVISImageTest();
ANSVISTest(); //ANSVISTest();
//ANSVISTestFilePlayer(); //ANSVISTestFilePlayer();
// ANSVISRecordingTest(); // ANSVISRecordingTest();
//FRStressTest(); //FRStressTest();
//for (int i = 0; i < 20; i++) { //for (int i = 0; i < 20; i++) {
// ANSVISTest(); // ANSVISTest();
//} //}
@@ -1341,6 +1653,8 @@ int main()
// TestCompleteFR(); // TestCompleteFR();
//TestFaceRecognition(); //TestFaceRecognition();
//FaceRecognitionBenchmark(); //FaceRecognitionBenchmark();
ANSVISTestCPU_Lightweight();
ANSVISTestCPU();
std::cin.get(); std::cin.get();
} }