Fix AMD and OpenVINO
This commit is contained in:
@@ -514,6 +514,177 @@ extern "C" ANSFR_API int InsertUser(ANSCENTER::ANSFacialRecognition** H
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: repair mixed-encoding LabVIEW LStrHandle to clean UTF-16LE.
|
||||
// LabVIEW text controls may produce a mix of UTF-16LE pairs, embedded UTF-8
|
||||
// multi-byte sequences, and lone space bytes (0x20 without 0x00 high byte).
|
||||
// This normalizes everything to proper UTF-16LE pairs.
|
||||
// Input: BOM-stripped raw bytes. Output: clean UTF-16LE vector.
|
||||
static std::vector<unsigned char> RepairLabVIEWUTF16LE_Local(const unsigned char* data, int len) {
|
||||
std::vector<unsigned char> repaired;
|
||||
if (!data || len <= 0) return repaired;
|
||||
repaired.reserve(len + 32);
|
||||
|
||||
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
|
||||
// 2-byte UTF-8: C2-DF followed by 80-BF
|
||||
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: E0-EF followed by 80-BF 80-BF
|
||||
if (b >= 0xE0 && b <= 0xEF && i + 2 < len) {
|
||||
unsigned char b1 = data[i + 1], b2 = data[i + 2];
|
||||
if ((b1 & 0xC0) == 0x80 && (b2 & 0xC0) == 0x80) {
|
||||
uint32_t cp = ((b & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F);
|
||||
if (cp >= 0x0800 && (cp < 0xD800 || cp > 0xDFFF)) {
|
||||
emitU16(static_cast<uint16_t>(cp));
|
||||
i += 3; continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 4-byte UTF-8: F0-F4 followed by 80-BF 80-BF 80-BF
|
||||
if (b >= 0xF0 && b <= 0xF4 && i + 3 < len) {
|
||||
unsigned char b1 = data[i + 1], b2 = data[i + 2], 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) {
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
// Helper: convert LStrHandle (mixed UTF-8/UTF-16LE or system codepage) to UTF-8 string
|
||||
static std::string LStrHandleToUTF8(LStrHandle handle) {
|
||||
if (!handle) return "";
|
||||
int byteLen = (*handle)->cnt;
|
||||
if (byteLen <= 0) return "";
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>((*handle)->str);
|
||||
|
||||
// Check for BOM or 0x00 bytes → UTF-16LE (possibly mixed with UTF-8)
|
||||
bool isUtf16le = false;
|
||||
if (byteLen >= 2 && data[0] == 0xFF && data[1] == 0xFE) isUtf16le = true;
|
||||
if (!isUtf16le) {
|
||||
for (int i = 0; i < byteLen; i++) {
|
||||
if (data[i] == 0x00) { isUtf16le = true; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (isUtf16le) {
|
||||
const unsigned char* convData = data;
|
||||
int convLen = byteLen;
|
||||
if (convLen >= 2 && convData[0] == 0xFF && convData[1] == 0xFE) { convData += 2; convLen -= 2; }
|
||||
if (convLen <= 0) return "";
|
||||
|
||||
// Repair mixed encoding (UTF-8 islands, lone spaces) → clean UTF-16LE
|
||||
auto repaired = RepairLabVIEWUTF16LE_Local(convData, convLen);
|
||||
|
||||
#ifdef _WIN32
|
||||
int wideLen = static_cast<int>(repaired.size()) / 2;
|
||||
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(repaired.data());
|
||||
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Len > 0) {
|
||||
std::string utf8(utf8Len, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8[0], utf8Len, nullptr, nullptr);
|
||||
return utf8;
|
||||
}
|
||||
#endif
|
||||
return std::string(reinterpret_cast<const char*>(repaired.data()), repaired.size());
|
||||
} else {
|
||||
// No 0x00 bytes — try UTF-8 first, fall back to system codepage.
|
||||
// IsValidUTF8: check if bytes form valid UTF-8 with at least one multi-byte sequence.
|
||||
auto IsValidUTF8 = [](const unsigned char* d, int l) -> bool {
|
||||
bool hasMulti = false;
|
||||
for (int j = 0; j < l; ) {
|
||||
unsigned char c = d[j];
|
||||
if (c <= 0x7F) { j++; }
|
||||
else if (c >= 0xC2 && c <= 0xDF) {
|
||||
if (j + 1 >= l || (d[j + 1] & 0xC0) != 0x80) return false;
|
||||
hasMulti = true; j += 2;
|
||||
} else if (c >= 0xE0 && c <= 0xEF) {
|
||||
if (j + 2 >= l || (d[j + 1] & 0xC0) != 0x80 || (d[j + 2] & 0xC0) != 0x80) return false;
|
||||
hasMulti = true; j += 3;
|
||||
} else if (c >= 0xF0 && c <= 0xF4) {
|
||||
if (j + 3 >= l || (d[j + 1] & 0xC0) != 0x80 || (d[j + 2] & 0xC0) != 0x80 || (d[j + 3] & 0xC0) != 0x80) return false;
|
||||
hasMulti = true; j += 4;
|
||||
} else { return false; }
|
||||
}
|
||||
return hasMulti;
|
||||
};
|
||||
|
||||
if (IsValidUTF8(data, byteLen)) {
|
||||
return std::string(reinterpret_cast<const char*>(data), byteLen);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
int wideLen = MultiByteToWideChar(CP_ACP, 0, reinterpret_cast<const char*>(data), byteLen, nullptr, 0);
|
||||
if (wideLen > 0) {
|
||||
std::wstring wideStr(wideLen, 0);
|
||||
MultiByteToWideChar(CP_ACP, 0, reinterpret_cast<const char*>(data), byteLen, &wideStr[0], wideLen);
|
||||
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), wideLen, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Len > 0) {
|
||||
std::string utf8(utf8Len, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), wideLen, &utf8[0], utf8Len, nullptr, nullptr);
|
||||
return utf8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return std::string(reinterpret_cast<const char*>(data), byteLen);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" ANSFR_API int InsertUser_LV(ANSCENTER::ANSFacialRecognition** Handle, const char* userCode, LStrHandle userName) {
|
||||
try {
|
||||
if (!Handle || !*Handle || !userCode || !userName) return -1;
|
||||
std::string utf8Name = LStrHandleToUTF8(userName);
|
||||
if (utf8Name.empty()) return -1;
|
||||
return (*Handle)->InsertUser(userCode, utf8Name);
|
||||
}
|
||||
catch (const std::exception& e) { return -1; }
|
||||
}
|
||||
|
||||
extern "C" ANSFR_API int UpdateUser_LV(ANSCENTER::ANSFacialRecognition** Handle, int userId, const char* userCode, LStrHandle userName) {
|
||||
try {
|
||||
if (!Handle || !*Handle || !userCode || !userName) return -1;
|
||||
std::string utf8Name = LStrHandleToUTF8(userName);
|
||||
if (utf8Name.empty()) return -1;
|
||||
return (*Handle)->UpdateUser(userId, userCode, utf8Name);
|
||||
}
|
||||
catch (const std::exception& e) { return -1; }
|
||||
}
|
||||
|
||||
extern "C" ANSFR_API int UpdateUser(ANSCENTER::ANSFacialRecognition** Handle, int userId, const char* userCode, const char* userName) {
|
||||
try {
|
||||
if (!Handle || !*Handle || !userCode || !userName) return -1;
|
||||
|
||||
@@ -963,7 +963,9 @@ namespace ANSCENTER {
|
||||
|
||||
// Run license plate detection
|
||||
cv::Mat activeFrame = frame(detectedArea);
|
||||
fprintf(stderr, "[ALPR] RunInference: calling lpd %dx%d cam=%s\n", activeFrame.cols, activeFrame.rows, cameraId.c_str());
|
||||
std::vector<Object> lprOutput = _lpDetector->RunInference(activeFrame, cameraId);
|
||||
fprintf(stderr, "[ALPR] RunInference: lpd done, %zu detections cam=%s\n", lprOutput.size(), cameraId.c_str());
|
||||
for (size_t _di = 0; _di < lprOutput.size(); ++_di) {
|
||||
ANS_DBG("ALPR_Track", "cam=%s det[%zu] tid=%d box=(%d,%d,%d,%d) conf=%.2f",
|
||||
cameraId.c_str(), _di, lprOutput[_di].trackId,
|
||||
@@ -1005,7 +1007,9 @@ namespace ANSCENTER {
|
||||
cv::Mat alignedLPR = frame(lprPos);// .clone();
|
||||
|
||||
// OCR inference
|
||||
fprintf(stderr, "[ALPR] RunInference: calling OCR on plate %dx%d cam=%s\n", alignedLPR.cols, alignedLPR.rows, cameraId.c_str());
|
||||
std::string ocrText = DetectLicensePlateString(alignedLPR, cameraId);
|
||||
fprintf(stderr, "[ALPR] RunInference: OCR done, text='%s' cam=%s\n", ocrText.c_str(), cameraId.c_str());
|
||||
|
||||
if (ocrText.empty()) {
|
||||
continue;
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace ANSCENTER {
|
||||
// to distinguish OBB (angle values in [-pi, pi]) from detection
|
||||
bool likelyOBB = false;
|
||||
if (extra >= 2) {
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
int numSamples = std::min(numBoxes, 100);
|
||||
int angleCount = 0;
|
||||
for (int s = 0; s < numSamples; ++s) {
|
||||
@@ -371,13 +371,13 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessEndToEnd(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (outputShape.size() < 3) return {};
|
||||
|
||||
@@ -427,13 +427,13 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessLegacy(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (outputShape.size() < 3) return {};
|
||||
|
||||
@@ -656,12 +656,12 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessOBBEndToEnd(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
const float* raw = outputTensors[0].GetTensorData<float>();
|
||||
const float* raw = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto shape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (shape.size() < 3) return {};
|
||||
|
||||
@@ -721,12 +721,12 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessOBBLegacy(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (outputShape.size() < 3) return {};
|
||||
|
||||
@@ -822,13 +822,13 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessSegEndToEnd(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold)
|
||||
{
|
||||
if (outputTensors.size() < 2) return {};
|
||||
|
||||
const float* raw = outputTensors[0].GetTensorData<float>();
|
||||
const float* raw = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto shape0 = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
const auto protoShape = outputTensors[1].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (shape0.size() < 3 || protoShape.size() < 4) return {};
|
||||
@@ -884,7 +884,7 @@ namespace ANSCENTER {
|
||||
|
||||
// Generate masks: coeffs @ protos → sigmoid → crop-in-proto → resize-to-box → threshold
|
||||
if (!objs.empty() && !maskCoeffs.empty()) {
|
||||
const float* protoData = outputTensors[1].GetTensorData<float>();
|
||||
const float* protoData = outputTensors[1].GetTensorMutableData<float>();
|
||||
cv::Mat protos(nm, protoH * protoW, CV_32F, const_cast<float*>(protoData));
|
||||
cv::Mat matmulRes = (maskCoeffs * protos).t();
|
||||
|
||||
@@ -951,13 +951,13 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessSegLegacy(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet)
|
||||
{
|
||||
if (outputTensors.size() < 2) return {};
|
||||
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto shape0 = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
const auto protoShape = outputTensors[1].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (shape0.size() < 3 || protoShape.size() < 4) return {};
|
||||
@@ -1035,7 +1035,7 @@ namespace ANSCENTER {
|
||||
|
||||
// Generate masks
|
||||
if (!objs.empty() && !masks.empty()) {
|
||||
const float* protoData = outputTensors[1].GetTensorData<float>();
|
||||
const float* protoData = outputTensors[1].GetTensorMutableData<float>();
|
||||
cv::Mat protos(nm, protoH * protoW, CV_32F, const_cast<float*>(protoData));
|
||||
cv::Mat matmulRes = (masks * protos).t();
|
||||
|
||||
@@ -1106,12 +1106,12 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessPoseEndToEnd(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, int numKPS)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
const float* raw = outputTensors[0].GetTensorData<float>();
|
||||
const float* raw = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto shape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (shape.size() < 3) return {};
|
||||
|
||||
@@ -1172,12 +1172,12 @@ namespace ANSCENTER {
|
||||
std::vector<Object> ONNXYOLO::postprocessPoseLegacy(
|
||||
const cv::Size& originalImageSize,
|
||||
const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int numKPS, int maxDet)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (outputShape.size() < 3) return {};
|
||||
|
||||
@@ -1273,12 +1273,12 @@ namespace ANSCENTER {
|
||||
// ====================================================================
|
||||
|
||||
std::vector<Object> ONNXYOLO::postprocessClassify(
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
const cv::Size& imageSize)
|
||||
{
|
||||
if (outputTensors.empty()) return {};
|
||||
const float* raw = outputTensors[0].GetTensorData<float>();
|
||||
const float* raw = outputTensors[0].GetTensorMutableData<float>();
|
||||
const auto shape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
if (shape.size() < 2) return {};
|
||||
|
||||
@@ -1339,7 +1339,7 @@ namespace ANSCENTER {
|
||||
// ====================================================================
|
||||
|
||||
/*static*/ Ort::Value ONNXYOLO::sliceBatchOutput(
|
||||
const Ort::Value& batchTensor,
|
||||
Ort::Value& batchTensor,
|
||||
int64_t batchIndex,
|
||||
const std::vector<int64_t>& fullShape,
|
||||
Ort::MemoryInfo& memInfo)
|
||||
@@ -1349,8 +1349,8 @@ namespace ANSCENTER {
|
||||
for (size_t d = 1; d < fullShape.size(); ++d)
|
||||
elemsPerImage *= fullShape[d];
|
||||
|
||||
const float* batchData = batchTensor.GetTensorData<float>();
|
||||
float* imageData = const_cast<float*>(batchData + batchIndex * elemsPerImage);
|
||||
float* batchData = batchTensor.GetTensorMutableData<float>();
|
||||
float* imageData = batchData + batchIndex * elemsPerImage;
|
||||
|
||||
// Shape for single image: [1, D1, D2, ...]
|
||||
std::vector<int64_t> singleShape = fullShape;
|
||||
@@ -1504,7 +1504,7 @@ namespace ANSCENTER {
|
||||
// Class count mismatch — probe last channel for OBB angles
|
||||
bool likelyOBB = false;
|
||||
if (extra >= 2) {
|
||||
const float* rawOutput = perImageOutputs[0].GetTensorData<float>();
|
||||
const float* rawOutput = perImageOutputs[0].GetTensorMutableData<float>();
|
||||
int numSamp = std::min(numBoxes, 100);
|
||||
int angleCount = 0;
|
||||
for (int s = 0; s < numSamp; ++s) {
|
||||
@@ -1571,6 +1571,22 @@ namespace ANSCENTER {
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSONNXYOLO::InitOrtEngine(ANSCENTER::EngineType engineType) {
|
||||
try {
|
||||
if (!FileExist(_modelFilePath)) {
|
||||
_logger.LogError("ANSONNXYOLO::InitOrtEngine",
|
||||
"Model file does not exist: " + _modelFilePath, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
m_ortEngine = std::make_unique<ONNXYOLO>(_modelFilePath, engineType);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSONNXYOLO::InitOrtEngine", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSONNXYOLO::Initialize(std::string licenseKey, ModelConfig modelConfig,
|
||||
const std::string& modelZipFilePath,
|
||||
const std::string& modelZipPassword,
|
||||
@@ -1807,9 +1823,12 @@ namespace ANSCENTER {
|
||||
const std::string& camera_id)
|
||||
{
|
||||
try {
|
||||
ANS_DBG("ONNXYOLO", "DetectObjects: cam=%s acquiring mutex...", camera_id.c_str());
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
ANS_DBG("ONNXYOLO", "DetectObjects: mutex acquired, cam=%s", camera_id.c_str());
|
||||
if (!m_ortEngine) {
|
||||
_logger.LogError("ANSONNXYOLO::DetectObjects", "ORT engine is null", __FILE__, __LINE__);
|
||||
ANS_DBG("ONNXYOLO", "DetectObjects: ORT engine is null!");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1880,6 +1899,7 @@ namespace ANSCENTER {
|
||||
return results;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ANS_DBG("ONNXYOLO", "DetectObjects EXCEPTION: %s cam=%s", e.what(), camera_id.c_str());
|
||||
_logger.LogFatal("ANSONNXYOLO::DetectObjects", e.what(), __FILE__, __LINE__);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -83,55 +83,55 @@ namespace ANSCENTER {
|
||||
// ── Detection postprocess ───────────────────────────────────────
|
||||
std::vector<Object> postprocessEndToEnd(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames, float confThreshold);
|
||||
|
||||
std::vector<Object> postprocessLegacy(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet = 300);
|
||||
|
||||
// ── OBB postprocess ─────────────────────────────────────────────
|
||||
std::vector<Object> postprocessOBBEndToEnd(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames, float confThreshold);
|
||||
|
||||
std::vector<Object> postprocessOBBLegacy(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet = 300);
|
||||
|
||||
// ── Segmentation postprocess ────────────────────────────────────
|
||||
std::vector<Object> postprocessSegEndToEnd(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames, float confThreshold);
|
||||
|
||||
std::vector<Object> postprocessSegLegacy(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int maxDet = 300);
|
||||
|
||||
// ── Pose postprocess ────────────────────────────────────────────
|
||||
std::vector<Object> postprocessPoseEndToEnd(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, int numKPS);
|
||||
|
||||
std::vector<Object> postprocessPoseLegacy(
|
||||
const cv::Size& originalImageSize, const cv::Size& resizedImageShape,
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
float confThreshold, float iouThreshold, int numKPS, int maxDet = 300);
|
||||
|
||||
// ── Classification postprocess ──────────────────────────────────
|
||||
std::vector<Object> postprocessClassify(
|
||||
const std::vector<Ort::Value>& outputTensors,
|
||||
std::vector<Ort::Value>& outputTensors,
|
||||
const std::vector<std::string>& classNames,
|
||||
const cv::Size& imageSize);
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace ANSCENTER {
|
||||
|
||||
// ── Batch output slicing helper ────────────────────────────────
|
||||
static Ort::Value sliceBatchOutput(
|
||||
const Ort::Value& batchTensor,
|
||||
Ort::Value& batchTensor,
|
||||
int64_t batchIndex,
|
||||
const std::vector<int64_t>& fullShape,
|
||||
Ort::MemoryInfo& memInfo);
|
||||
@@ -224,6 +224,9 @@ namespace ANSCENTER {
|
||||
|
||||
// Initialise ORT engine from the resolved model path
|
||||
bool InitOrtEngine();
|
||||
public:
|
||||
// Initialise ORT engine with explicit engine type override (e.g. CPU fallback for AMD iGPUs)
|
||||
bool InitOrtEngine(ANSCENTER::EngineType engineType);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -218,6 +218,12 @@ namespace ANSCENTER
|
||||
std::min(6, static_cast<int>(std::thread::hardware_concurrency())));
|
||||
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
|
||||
|
||||
// DirectML REQUIRES these two settings per ORT documentation
|
||||
if (ep.type == ANSCENTER::EngineType::AMD_GPU) {
|
||||
sessionOptions.DisableMemPattern();
|
||||
sessionOptions.SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL);
|
||||
}
|
||||
|
||||
// ── Log available providers ─────────────────────────────────────────
|
||||
std::vector<std::string> availableProviders = Ort::GetAvailableProviders();
|
||||
std::cout << "Available Execution Providers:" << std::endl;
|
||||
@@ -519,7 +525,7 @@ namespace ANSCENTER
|
||||
{
|
||||
try {
|
||||
// Get raw output pointer (NO COPY!)
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
std::vector<int64_t> outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
|
||||
const int numClasses = static_cast<int>(outputShape[2]) - 5;
|
||||
@@ -647,11 +653,11 @@ namespace ANSCENTER
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::vector<Object> YOLOOD::postprocessv11(const cv::Size& originalImageSize,const cv::Size& resizedImageShape,const std::vector<Ort::Value>& outputTensors,float confThreshold,float iouThreshold)
|
||||
std::vector<Object> YOLOOD::postprocessv11(const cv::Size& originalImageSize,const cv::Size& resizedImageShape,std::vector<Ort::Value>& outputTensors,float confThreshold,float iouThreshold)
|
||||
{
|
||||
try {
|
||||
// Get raw output
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const std::vector<int64_t> outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
|
||||
const size_t numFeatures = outputShape[1];
|
||||
@@ -1448,7 +1454,7 @@ namespace ANSCENTER
|
||||
);
|
||||
|
||||
// Parse output
|
||||
const float* rawOutput = outputTensors[0].GetTensorData<float>();
|
||||
const float* rawOutput = outputTensors[0].GetTensorMutableData<float>();
|
||||
const std::vector<int64_t> outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
|
||||
const int dimensions = static_cast<int>(outputShape[1]); // 4 + num_classes
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace ANSCENTER {
|
||||
cv::Mat preprocessv11(const cv::Mat& image, std::vector<float>& blob, std::vector<int64_t>& inputTensorShape);
|
||||
std::vector<Object> postprocessing(const cv::Size& resizedImageShape,const cv::Size& originalImageShape,std::vector<Ort::Value>& outputTensors,
|
||||
const float& confThreshold, const float& iouThreshold);
|
||||
std::vector<Object> postprocessv11(const cv::Size& originalImageSize,const cv::Size& resizedImageShape,const std::vector<Ort::Value>& outputTensors,float confThreshold,float iouThreshold);
|
||||
std::vector<Object> postprocessv11(const cv::Size& originalImageSize,const cv::Size& resizedImageShape,std::vector<Ort::Value>& outputTensors,float confThreshold,float iouThreshold);
|
||||
BoundingBox scaleCoordsv11(const cv::Size& imageShape, BoundingBox coords,const cv::Size& imageOriginalShape, bool p_Clip);
|
||||
std::vector<const char*> inputNodeNames;
|
||||
std::vector<const char*> outputNodeNames;
|
||||
|
||||
@@ -355,6 +355,7 @@ extern "C" ANSODENGINE_API std::string CreateANSODHandle(ANSCENTER::ANSODBase**
|
||||
// TEXTSCENSE = 6
|
||||
|
||||
//Force modelType to ANSONNXYOLO and ANSRTYOLO if detectionType is detection and modelType is TENSORRT or ONNX
|
||||
|
||||
if ((modelType == 4) || // TensorRT
|
||||
(modelType == 14)|| // TensorRT Yolov10
|
||||
(modelType == 22)|| // TensorRT Pose
|
||||
@@ -376,7 +377,6 @@ extern "C" ANSODENGINE_API std::string CreateANSODHandle(ANSCENTER::ANSODBase**
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch (detectionType) {
|
||||
case 0:
|
||||
modelConfig.detectionType = ANSCENTER::DetectionType::CLASSIFICATION;
|
||||
|
||||
@@ -804,34 +804,54 @@ extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandl
|
||||
int len = (int)strlen(utf8Str);
|
||||
if (len == 0) return 0;
|
||||
const char bom[2] = { '\xFF', '\xFE' };
|
||||
|
||||
// Check if input contains \uXXXX escape sequences
|
||||
bool hasUnicodeEscapes = false;
|
||||
for (int i = 0; i + 1 < len; i++) {
|
||||
if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; }
|
||||
}
|
||||
|
||||
if (hasUnicodeEscapes) {
|
||||
std::string utf16le;
|
||||
if (includeBOM) utf16le.assign(bom, 2);
|
||||
utf16le.reserve(len * 2 + 2);
|
||||
// Two-pass approach: first decode \uXXXX escapes to UTF-8, then convert to UTF-16LE.
|
||||
// This correctly handles mixed input (raw UTF-8 + \uXXXX escapes) by producing
|
||||
// clean UTF-8 first, then using MultiByteToWideChar for proper UTF-16LE conversion.
|
||||
std::string utf8Decoded;
|
||||
utf8Decoded.reserve(len);
|
||||
for (int i = 0; i < len; ) {
|
||||
if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') {
|
||||
char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 };
|
||||
uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16);
|
||||
utf16le += static_cast<char>(cp & 0xFF);
|
||||
utf16le += static_cast<char>((cp >> 8) & 0xFF);
|
||||
uint32_t cp = (uint32_t)strtoul(hex, nullptr, 16);
|
||||
// Encode codepoint as UTF-8
|
||||
if (cp <= 0x7F) {
|
||||
utf8Decoded += static_cast<char>(cp);
|
||||
} else if (cp <= 0x7FF) {
|
||||
utf8Decoded += static_cast<char>(0xC0 | (cp >> 6));
|
||||
utf8Decoded += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
} else {
|
||||
utf8Decoded += static_cast<char>(0xE0 | (cp >> 12));
|
||||
utf8Decoded += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||
utf8Decoded += static_cast<char>(0x80 | (cp & 0x3F));
|
||||
}
|
||||
i += 6;
|
||||
} else {
|
||||
utf16le += utf8Str[i];
|
||||
utf16le += '\0';
|
||||
utf8Decoded += utf8Str[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
int size = (int)utf16le.size();
|
||||
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||
// Now convert the clean UTF-8 to UTF-16LE
|
||||
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF8ToUTF16LE(utf8Decoded);
|
||||
if (converted.empty()) return 0;
|
||||
int dataSize = static_cast<int>(converted.size());
|
||||
int bomSize = includeBOM ? 2 : 0;
|
||||
int totalSize = bomSize + dataSize;
|
||||
MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar));
|
||||
if (error != noErr) return -2;
|
||||
(*result)->cnt = size;
|
||||
memcpy((*result)->str, utf16le.data(), size);
|
||||
(*result)->cnt = totalSize;
|
||||
if (includeBOM) memcpy((*result)->str, bom, 2);
|
||||
memcpy((*result)->str + bomSize, converted.data(), dataSize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF8ToUTF16LE(utf8Str);
|
||||
if (converted.empty()) return 0;
|
||||
int dataSize = static_cast<int>(converted.size());
|
||||
@@ -850,23 +870,31 @@ extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandl
|
||||
extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) {
|
||||
try {
|
||||
if (!utf16leBytes || byteLen <= 0 || !result) return -1;
|
||||
bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0);
|
||||
const unsigned char* data = utf16leBytes;
|
||||
int dataLen = byteLen;
|
||||
// Strip BOM (FF FE) if present
|
||||
if (dataLen >= 2 && data[0] == 0xFF && data[1] == 0xFE) {
|
||||
data += 2;
|
||||
dataLen -= 2;
|
||||
}
|
||||
if (dataLen <= 0) return 0;
|
||||
bool isUtf16le = (dataLen >= 2 && dataLen % 2 == 0);
|
||||
if (isUtf16le) {
|
||||
bool isAscii = true;
|
||||
for (int i = 1; i < byteLen; i += 2) {
|
||||
if (utf16leBytes[i] != 0x00) { isAscii = false; break; }
|
||||
for (int i = 1; i < dataLen; i += 2) {
|
||||
if (data[i] != 0x00) { isAscii = false; break; }
|
||||
}
|
||||
if (isAscii) {
|
||||
int asciiLen = byteLen / 2;
|
||||
int asciiLen = dataLen / 2;
|
||||
MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar));
|
||||
if (error != noErr) return -2;
|
||||
(*result)->cnt = asciiLen;
|
||||
for (int i = 0; i < asciiLen; i++) (*result)->str[i] = utf16leBytes[i * 2];
|
||||
for (int i = 0; i < asciiLen; i++) (*result)->str[i] = data[i * 2];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF16LEToUTF8(
|
||||
reinterpret_cast<const char*>(utf16leBytes), byteLen);
|
||||
reinterpret_cast<const char*>(data), dataLen);
|
||||
if (converted.empty()) return 0;
|
||||
int size = static_cast<int>(converted.size());
|
||||
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||
@@ -909,6 +937,168 @@ extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes(const unsigned char*
|
||||
catch (...) { return -1; }
|
||||
}
|
||||
|
||||
// Helper: copy a std::string into a LabVIEW LStrHandle.
|
||||
static int CopyStringToLStrHandle(LStrHandle handle, const std::string& str) {
|
||||
if (str.empty()) return 0;
|
||||
int size = static_cast<int>(str.size());
|
||||
MgErr error = DSSetHandleSize(handle, sizeof(int32) + size * sizeof(uChar));
|
||||
if (error != noErr) return -2;
|
||||
(*handle)->cnt = size;
|
||||
memcpy((*handle)->str, str.data(), size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Helper: copy raw bytes into a LabVIEW LStrHandle.
|
||||
static int CopyBytesToLStrHandle(LStrHandle handle, const unsigned char* data, int len) {
|
||||
if (!data || len <= 0) return 0;
|
||||
MgErr error = DSSetHandleSize(handle, sizeof(int32) + len * sizeof(uChar));
|
||||
if (error != noErr) return -2;
|
||||
(*handle)->cnt = len;
|
||||
memcpy((*handle)->str, data, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Helper: detect if LabVIEW LStrHandle contains UTF-16LE (BOM or 0x00 bytes).
|
||||
static bool DetectUTF16LE(const unsigned char* data, int byteLen) {
|
||||
if (byteLen >= 2 && data[0] == 0xFF && data[1] == 0xFE) return true;
|
||||
for (int i = 0; i < byteLen; i++) {
|
||||
if (data[i] == 0x00) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper: strip BOM from UTF-16LE data. Returns pointer and adjusts length.
|
||||
static const unsigned char* StripBOM(const unsigned char* data, int& len) {
|
||||
if (len >= 2 && data[0] == 0xFF && data[1] == 0xFE) { data += 2; len -= 2; }
|
||||
return data;
|
||||
}
|
||||
|
||||
// LStrHandle-safe version: reads raw bytes from LabVIEW LStrHandle directly.
|
||||
// Two paths:
|
||||
// 1. Pure UTF-8 (no BOM, no 0x00 bytes, valid UTF-8) → pass through to output as-is
|
||||
// 2. Contains UTF-16LE (BOM or 0x00 bytes) → RepairLabVIEWUTF16LE (normalizes
|
||||
// mixed UTF-8/UTF-16LE + lone spaces to clean UTF-16LE) → convert to UTF-8
|
||||
extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8_LV(LStrHandle input, LStrHandle result) {
|
||||
try {
|
||||
if (!input || !result) return -1;
|
||||
int byteLen = (*input)->cnt;
|
||||
if (byteLen <= 0) return 0;
|
||||
|
||||
// Copy input data first — input and result may be the same LStrHandle
|
||||
std::vector<unsigned char> inputCopy(byteLen);
|
||||
memcpy(inputCopy.data(), (*input)->str, byteLen);
|
||||
const unsigned char* data = inputCopy.data();
|
||||
|
||||
if (DetectUTF16LE(data, byteLen)) {
|
||||
// Path 2: UTF-16LE detected — repair mixed encoding, then convert to UTF-8
|
||||
int convLen = byteLen;
|
||||
const unsigned char* convData = StripBOM(data, convLen);
|
||||
if (convLen <= 0) return 0;
|
||||
|
||||
auto repaired = ANSCENTER::ANSUtilities::RepairLabVIEWUTF16LE(convData, convLen);
|
||||
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF16LEToUTF8(
|
||||
reinterpret_cast<const char*>(repaired.data()), static_cast<int>(repaired.size()));
|
||||
return CopyStringToLStrHandle(result, converted);
|
||||
}
|
||||
|
||||
if (ANSCENTER::ANSUtilities::IsValidUTF8(data, byteLen)) {
|
||||
// Path 1: Pure UTF-8 — pass through as-is
|
||||
return CopyBytesToLStrHandle(result, data, byteLen);
|
||||
}
|
||||
|
||||
// Fallback: not UTF-16LE, not valid UTF-8 — assume system codepage
|
||||
#ifdef _WIN32
|
||||
int wideLen = MultiByteToWideChar(CP_ACP, 0,
|
||||
reinterpret_cast<const char*>(data), byteLen, nullptr, 0);
|
||||
if (wideLen > 0) {
|
||||
std::wstring wideStr(wideLen, 0);
|
||||
MultiByteToWideChar(CP_ACP, 0,
|
||||
reinterpret_cast<const char*>(data), byteLen, &wideStr[0], wideLen);
|
||||
int utf8Len = WideCharToMultiByte(CP_UTF8, 0,
|
||||
wideStr.c_str(), wideLen, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Len > 0) {
|
||||
std::string utf8Str(utf8Len, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0,
|
||||
wideStr.c_str(), wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||
return CopyStringToLStrHandle(result, utf8Str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return CopyBytesToLStrHandle(result, data, byteLen);
|
||||
}
|
||||
catch (...) { return -1; }
|
||||
}
|
||||
|
||||
// LStrHandle-safe version with auto-detection.
|
||||
// Two paths:
|
||||
// 1. Pure UTF-8 → convert UTF-8 to Unicode escapes (\uXXXX)
|
||||
// 2. Contains UTF-16LE → RepairLabVIEWUTF16LE → convert to Unicode escapes
|
||||
extern "C" ANSULT_API int ANSConvertUTF16LEToUnicodeEscapes_LV(LStrHandle input, LStrHandle result) {
|
||||
try {
|
||||
if (!input || !result) return -1;
|
||||
int byteLen = (*input)->cnt;
|
||||
if (byteLen <= 0) return 0;
|
||||
|
||||
// Copy input data first — input and result may be the same LStrHandle
|
||||
std::vector<unsigned char> inputCopy(byteLen);
|
||||
memcpy(inputCopy.data(), (*input)->str, byteLen);
|
||||
const unsigned char* data = inputCopy.data();
|
||||
|
||||
std::string escaped;
|
||||
|
||||
if (DetectUTF16LE(data, byteLen)) {
|
||||
// Path 2: UTF-16LE detected — repair mixed encoding, then convert to escapes
|
||||
int convLen = byteLen;
|
||||
const unsigned char* convData = StripBOM(data, convLen);
|
||||
if (convLen <= 0) return 0;
|
||||
|
||||
auto repaired = ANSCENTER::ANSUtilities::RepairLabVIEWUTF16LE(convData, convLen);
|
||||
|
||||
// Re-add BOM for ConvertUTF16LEToUnicodeEscapes (it expects optional BOM)
|
||||
std::vector<unsigned char> withBom;
|
||||
withBom.reserve(2 + repaired.size());
|
||||
withBom.push_back(0xFF);
|
||||
withBom.push_back(0xFE);
|
||||
withBom.insert(withBom.end(), repaired.begin(), repaired.end());
|
||||
|
||||
escaped = ANSCENTER::ANSUtilities::ConvertUTF16LEToUnicodeEscapes(
|
||||
reinterpret_cast<const char*>(withBom.data()), static_cast<int>(withBom.size()));
|
||||
}
|
||||
else {
|
||||
// Path 1: No UTF-16LE — get UTF-8, then convert to Unicode escapes
|
||||
std::string utf8Str;
|
||||
if (ANSCENTER::ANSUtilities::IsValidUTF8(data, byteLen)) {
|
||||
utf8Str.assign(reinterpret_cast<const char*>(data), byteLen);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else {
|
||||
int wideLen = MultiByteToWideChar(CP_ACP, 0,
|
||||
reinterpret_cast<const char*>(data), byteLen, nullptr, 0);
|
||||
if (wideLen > 0) {
|
||||
std::wstring wideStr(wideLen, 0);
|
||||
MultiByteToWideChar(CP_ACP, 0,
|
||||
reinterpret_cast<const char*>(data), byteLen, &wideStr[0], wideLen);
|
||||
int utf8Len = WideCharToMultiByte(CP_UTF8, 0,
|
||||
wideStr.c_str(), wideLen, nullptr, 0, nullptr, nullptr);
|
||||
if (utf8Len > 0) {
|
||||
utf8Str.resize(utf8Len);
|
||||
WideCharToMultiByte(CP_UTF8, 0,
|
||||
wideStr.c_str(), wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (utf8Str.empty()) {
|
||||
utf8Str.assign(reinterpret_cast<const char*>(data), byteLen);
|
||||
}
|
||||
escaped = ANSCENTER::ANSUtilities::ConvertUTF8ToUnicodeEscapes(utf8Str);
|
||||
}
|
||||
|
||||
return CopyStringToLStrHandle(result, escaped);
|
||||
}
|
||||
catch (...) { return -1; }
|
||||
}
|
||||
|
||||
extern "C" ANSULT_API int ANSConvertUnicodeEscapesToUTF8(const char* escapedStr, LStrHandle result) {
|
||||
try {
|
||||
if (!escapedStr || !result) return -1;
|
||||
|
||||
Reference in New Issue
Block a user