Support UTF8 to UTF16 LE
This commit is contained in:
@@ -1583,7 +1583,7 @@ namespace ANSCENTER {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogThreadSafe("ANSFacialRecognition::UpdateUser",
|
LogThreadSafe("ANSFacialRecognition::UpdateUser",
|
||||||
"Successfully updated user ID " + std::to_string(userId));
|
"Successfully updated user ID " + std::to_string(userId), LogLevel::Info);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2350,6 +2350,32 @@ namespace ANSCENTER {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Using nlohmann/json (much faster)
|
// Using nlohmann/json (much faster)
|
||||||
|
static std::string DoubleEscapeUnicode(const std::string& utf8Str) {
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (unsigned char c : utf8Str) {
|
||||||
|
if (c >= 0x80) { hasNonAscii = true; break; }
|
||||||
|
}
|
||||||
|
if (!hasNonAscii) return utf8Str;
|
||||||
|
std::string result;
|
||||||
|
result.reserve(utf8Str.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < utf8Str.size()) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(utf8Str[i]);
|
||||||
|
if (c < 0x80) { result += utf8Str[i++]; continue; }
|
||||||
|
uint32_t cp = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x1F) << 6) | (static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F); i += 2;
|
||||||
|
} else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F); i += 3;
|
||||||
|
} else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x07) << 18) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 3]) & 0x3F); i += 4;
|
||||||
|
} else { i++; continue; }
|
||||||
|
if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; }
|
||||||
|
else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ANSFacialRecognition::FaceObjectsToJsonString(const std::vector<FaceResultObject>& faces) {
|
std::string ANSFacialRecognition::FaceObjectsToJsonString(const std::vector<FaceResultObject>& faces) {
|
||||||
START_TIMER(json_total);
|
START_TIMER(json_total);
|
||||||
|
|
||||||
@@ -2361,7 +2387,7 @@ namespace ANSCENTER {
|
|||||||
for (const auto& face : faces) {
|
for (const auto& face : faces) {
|
||||||
results.push_back({
|
results.push_back({
|
||||||
{"user_id", face.userId},
|
{"user_id", face.userId},
|
||||||
{"user_name", face.userName},
|
{"user_name", DoubleEscapeUnicode(face.userName)},
|
||||||
{"similarity", std::to_string(face.similarity)},
|
{"similarity", std::to_string(face.similarity)},
|
||||||
{"is_unknown", std::to_string(face.isUnknown)},
|
{"is_unknown", std::to_string(face.isUnknown)},
|
||||||
{"prob", std::to_string(face.confidence)},
|
{"prob", std::to_string(face.confidence)},
|
||||||
@@ -2399,7 +2425,7 @@ namespace ANSCENTER {
|
|||||||
nlohmann::json detectedNode;
|
nlohmann::json detectedNode;
|
||||||
detectedNode["class_id"] = std::to_string(det.classId);
|
detectedNode["class_id"] = std::to_string(det.classId);
|
||||||
detectedNode["track_id"] = std::to_string(det.trackId);
|
detectedNode["track_id"] = std::to_string(det.trackId);
|
||||||
detectedNode["class_name"] = det.className;
|
detectedNode["class_name"] = DoubleEscapeUnicode(det.className);
|
||||||
detectedNode["prob"] = std::to_string(det.confidence);
|
detectedNode["prob"] = std::to_string(det.confidence);
|
||||||
detectedNode["x"] = std::to_string(det.box.x);
|
detectedNode["x"] = std::to_string(det.box.x);
|
||||||
detectedNode["y"] = std::to_string(det.box.y);
|
detectedNode["y"] = std::to_string(det.box.y);
|
||||||
|
|||||||
@@ -351,4 +351,8 @@ 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);
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result);
|
||||||
|
extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -750,6 +750,103 @@ extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, un
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSFR_API int ANSFR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf8Str || !result) return -1;
|
||||||
|
int len = (int)strlen(utf8Str);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
bool hasUnicodeEscapes = false;
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true;
|
||||||
|
if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true;
|
||||||
|
}
|
||||||
|
if (!hasNonAscii && !hasUnicodeEscapes) {
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = len;
|
||||||
|
memcpy((*result)->str, utf8Str, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (hasUnicodeEscapes) {
|
||||||
|
std::string utf16le;
|
||||||
|
utf16le.reserve(len * 2);
|
||||||
|
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);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
utf16le += utf8Str[i];
|
||||||
|
utf16le += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = (int)utf16le.size();
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, utf16le.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0);
|
||||||
|
if (wideLen <= 0) return 0;
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen);
|
||||||
|
int size = wideLen * (int)sizeof(wchar_t);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, wideStr.data(), size);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" ANSFR_API int ANSFR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf16leBytes || byteLen <= 0 || !result) return -1;
|
||||||
|
bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0);
|
||||||
|
if (isUtf16le) {
|
||||||
|
bool isAscii = true;
|
||||||
|
for (int i = 1; i < byteLen; i += 2) {
|
||||||
|
if (utf16leBytes[i] != 0x00) { isAscii = false; break; }
|
||||||
|
}
|
||||||
|
if (isAscii) {
|
||||||
|
int asciiLen = byteLen / 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];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = byteLen / (int)sizeof(wchar_t);
|
||||||
|
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(utf16leBytes);
|
||||||
|
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (utf8Len <= 0) return 0;
|
||||||
|
std::string utf8Str(utf8Len, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = utf8Len;
|
||||||
|
memcpy((*result)->str, utf8Str.data(), utf8Len);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" ANSFR_API int UpdateParameters(ANSCENTER::ANSFacialRecognition** Handle, float knownPersonThreshold, int enableAgeGender, int enableFaceEmotions, int enableHeadPose, int minFaceSize, float faceDetectorThreshold, int enableFaceliveness, int antiSpoof, int removeFakeFaces) {
|
extern "C" ANSFR_API int UpdateParameters(ANSCENTER::ANSFacialRecognition** Handle, float knownPersonThreshold, int enableAgeGender, int enableFaceEmotions, int enableHeadPose, int minFaceSize, float faceDetectorThreshold, int enableFaceliveness, int antiSpoof, int removeFakeFaces) {
|
||||||
try {
|
try {
|
||||||
if (!Handle || !*Handle) return -1;
|
if (!Handle || !*Handle) return -1;
|
||||||
|
|||||||
@@ -408,6 +408,32 @@ namespace ANSCENTER {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string DoubleEscapeUnicode(const std::string& utf8Str) {
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (unsigned char c : utf8Str) {
|
||||||
|
if (c >= 0x80) { hasNonAscii = true; break; }
|
||||||
|
}
|
||||||
|
if (!hasNonAscii) return utf8Str;
|
||||||
|
std::string result;
|
||||||
|
result.reserve(utf8Str.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < utf8Str.size()) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(utf8Str[i]);
|
||||||
|
if (c < 0x80) { result += utf8Str[i++]; continue; }
|
||||||
|
uint32_t cp = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x1F) << 6) | (static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F); i += 2;
|
||||||
|
} else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F); i += 3;
|
||||||
|
} else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x07) << 18) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 3]) & 0x3F); i += 4;
|
||||||
|
} else { i++; continue; }
|
||||||
|
if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; }
|
||||||
|
else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ANSALPR::VectorDetectionToJsonString(const std::vector<Object>& dets) {
|
std::string ANSALPR::VectorDetectionToJsonString(const std::vector<Object>& dets) {
|
||||||
if (dets.empty()) {
|
if (dets.empty()) {
|
||||||
return R"({"results":[]})";
|
return R"({"results":[]})";
|
||||||
@@ -421,13 +447,13 @@ namespace ANSCENTER {
|
|||||||
{"class_id", std::to_string(det.classId)},
|
{"class_id", std::to_string(det.classId)},
|
||||||
//{"track_id", std::to_string(det.trackId)},
|
//{"track_id", std::to_string(det.trackId)},
|
||||||
{"track_id", std::to_string(0)},
|
{"track_id", std::to_string(0)},
|
||||||
{"class_name", det.className},
|
{"class_name", DoubleEscapeUnicode(det.className)},
|
||||||
{"prob", std::to_string(det.confidence)},
|
{"prob", std::to_string(det.confidence)},
|
||||||
{"x", std::to_string(det.box.x)},
|
{"x", std::to_string(det.box.x)},
|
||||||
{"y", std::to_string(det.box.y)},
|
{"y", std::to_string(det.box.y)},
|
||||||
{"width", std::to_string(det.box.width)},
|
{"width", std::to_string(det.box.width)},
|
||||||
{"height", std::to_string(det.box.height)},
|
{"height", std::to_string(det.box.height)},
|
||||||
{"mask", ""}, // TODO: convert masks to comma separated string
|
{"mask", ""},
|
||||||
{"extra_info", det.extraInfo},
|
{"extra_info", det.extraInfo},
|
||||||
{"camera_id", det.cameraId},
|
{"camera_id", det.cameraId},
|
||||||
{"polygon", PolygonToString(det.polygon)},
|
{"polygon", PolygonToString(det.polygon)},
|
||||||
|
|||||||
@@ -142,4 +142,8 @@ extern "C" ANSLPR_API int ANSALPR_SetFormat(ANSCENTER::ANSALPR** Handle, con
|
|||||||
extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats);// comma separated formats
|
extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats);// comma separated formats
|
||||||
extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle formats);// comma separated formats
|
extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle formats);// comma separated formats
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result);
|
||||||
|
extern "C" ANSLPR_API int ANSLPR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -772,6 +772,103 @@ extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LS
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf8Str || !result) return -1;
|
||||||
|
int len = (int)strlen(utf8Str);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
bool hasUnicodeEscapes = false;
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true;
|
||||||
|
if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true;
|
||||||
|
}
|
||||||
|
if (!hasNonAscii && !hasUnicodeEscapes) {
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = len;
|
||||||
|
memcpy((*result)->str, utf8Str, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (hasUnicodeEscapes) {
|
||||||
|
std::string utf16le;
|
||||||
|
utf16le.reserve(len * 2);
|
||||||
|
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);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
utf16le += utf8Str[i];
|
||||||
|
utf16le += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = (int)utf16le.size();
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, utf16le.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0);
|
||||||
|
if (wideLen <= 0) return 0;
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen);
|
||||||
|
int size = wideLen * (int)sizeof(wchar_t);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, wideStr.data(), size);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" ANSLPR_API int ANSLPR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf16leBytes || byteLen <= 0 || !result) return -1;
|
||||||
|
bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0);
|
||||||
|
if (isUtf16le) {
|
||||||
|
bool isAscii = true;
|
||||||
|
for (int i = 1; i < byteLen; i += 2) {
|
||||||
|
if (utf16leBytes[i] != 0x00) { isAscii = false; break; }
|
||||||
|
}
|
||||||
|
if (isAscii) {
|
||||||
|
int asciiLen = byteLen / 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];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = byteLen / (int)sizeof(wchar_t);
|
||||||
|
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(utf16leBytes);
|
||||||
|
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (utf8Len <= 0) return 0;
|
||||||
|
std::string utf8Str(utf8Len, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = utf8Len;
|
||||||
|
memcpy((*result)->str, utf8Str.data(), utf8Len);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// V2 API — accepts uint64_t handle by value (eliminates LabVIEW buffer reuse bug)
|
// V2 API — accepts uint64_t handle by value (eliminates LabVIEW buffer reuse bug)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -177,6 +177,60 @@ namespace ANSCENTER {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Helper: convert non-ASCII UTF-8 chars to literal \\uXXXX text so that
|
||||||
|
// JSON parsers (e.g., LabVIEW) preserve them as displayable escape sequences.
|
||||||
|
// Pure ASCII strings pass through unchanged (zero overhead).
|
||||||
|
static std::string DoubleEscapeUnicode(const std::string& utf8Str) {
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (unsigned char c : utf8Str) {
|
||||||
|
if (c >= 0x80) { hasNonAscii = true; break; }
|
||||||
|
}
|
||||||
|
if (!hasNonAscii) return utf8Str;
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
result.reserve(utf8Str.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < utf8Str.size()) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(utf8Str[i]);
|
||||||
|
if (c < 0x80) {
|
||||||
|
result += utf8Str[i++];
|
||||||
|
} else {
|
||||||
|
// Decode UTF-8 codepoint
|
||||||
|
uint32_t cp = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x1F) << 6) | (static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F);
|
||||||
|
i += 2;
|
||||||
|
} else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 6)
|
||||||
|
| (static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F);
|
||||||
|
i += 3;
|
||||||
|
} else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x07) << 18) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 12)
|
||||||
|
| ((static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F) << 6)
|
||||||
|
| (static_cast<unsigned char>(utf8Str[i + 3]) & 0x3F);
|
||||||
|
i += 4;
|
||||||
|
} else {
|
||||||
|
i++; continue; // skip invalid byte
|
||||||
|
}
|
||||||
|
// Encode as \\uXXXX (literal backslash + u + 4 hex digits)
|
||||||
|
if (cp <= 0xFFFF) {
|
||||||
|
char buf[8];
|
||||||
|
snprintf(buf, sizeof(buf), "\\u%04x", cp);
|
||||||
|
result += buf;
|
||||||
|
} else {
|
||||||
|
// Surrogate pair for codepoints > 0xFFFF
|
||||||
|
cp -= 0x10000;
|
||||||
|
uint16_t hi = 0xD800 + (uint16_t)(cp >> 10);
|
||||||
|
uint16_t lo = 0xDC00 + (uint16_t)(cp & 0x3FF);
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", hi, lo);
|
||||||
|
result += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(const std::vector<OCRObject>& dets)
|
std::string ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(const std::vector<OCRObject>& dets)
|
||||||
{
|
{
|
||||||
if (dets.empty()) {
|
if (dets.empty()) {
|
||||||
@@ -191,7 +245,7 @@ namespace ANSCENTER {
|
|||||||
results.push_back({
|
results.push_back({
|
||||||
{"class_id", std::to_string(det.classId)},
|
{"class_id", std::to_string(det.classId)},
|
||||||
{"track_id", std::to_string(det.trackId)},
|
{"track_id", std::to_string(det.trackId)},
|
||||||
{"class_name", det.className},
|
{"class_name", DoubleEscapeUnicode(det.className)},
|
||||||
{"prob", std::to_string(det.confidence)},
|
{"prob", std::to_string(det.confidence)},
|
||||||
{"x", std::to_string(det.box.x)},
|
{"x", std::to_string(det.box.x)},
|
||||||
{"y", std::to_string(det.box.y)},
|
{"y", std::to_string(det.box.y)},
|
||||||
@@ -205,7 +259,6 @@ namespace ANSCENTER {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure_ascii=true escapes non-ASCII chars as \uXXXX for LabVIEW compatibility
|
|
||||||
return root.dump(-1, ' ', true);
|
return root.dump(-1, ' ', true);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
@@ -846,7 +899,7 @@ namespace ANSCENTER {
|
|||||||
jsonResults.push_back({
|
jsonResults.push_back({
|
||||||
{"class_id", "0"},
|
{"class_id", "0"},
|
||||||
{"track_id", "0"},
|
{"track_id", "0"},
|
||||||
{"class_name", res.fullPlateText},
|
{"class_name", DoubleEscapeUnicode(res.fullPlateText)},
|
||||||
{"prob", std::to_string(res.confidence)},
|
{"prob", std::to_string(res.confidence)},
|
||||||
{"x", std::to_string(res.plateBox.x)},
|
{"x", std::to_string(res.plateBox.x)},
|
||||||
{"y", std::to_string(res.plateBox.y)},
|
{"y", std::to_string(res.plateBox.y)},
|
||||||
|
|||||||
@@ -243,6 +243,10 @@ extern "C" ANSOCR_API int SetANSOCRMode(ANSCENTER::ANSOCRBase** Handle,
|
|||||||
extern "C" ANSOCR_API int SetANSOCRCountry(ANSCENTER::ANSOCRBase** Handle, int country);
|
extern "C" ANSOCR_API int SetANSOCRCountry(ANSCENTER::ANSOCRBase** Handle, int country);
|
||||||
extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, const char* formatJson);
|
extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, const char* formatJson);
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result);
|
||||||
|
extern "C" ANSOCR_API int ANSOCR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
|
||||||
|
|
||||||
// V2 Create / Release — handle as uint64_t by value (no pointer-to-pointer)
|
// V2 Create / Release — handle as uint64_t by value (no pointer-to-pointer)
|
||||||
extern "C" ANSOCR_API uint64_t CreateANSOCRHandleEx_V2(const char* licenseKey, const char* modelFilePath,
|
extern "C" ANSOCR_API uint64_t CreateANSOCRHandleEx_V2(const char* licenseKey, const char* modelFilePath,
|
||||||
const char* modelFileZipPassword, int language, int engineMode, int gpuId,
|
const char* modelFileZipPassword, int language, int engineMode, int gpuId,
|
||||||
|
|||||||
@@ -422,6 +422,123 @@ extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
// Converts input string to UTF-16LE. Handles both:
|
||||||
|
// - JSON Unicode escapes (\uXXXX) from ensure_ascii=true output
|
||||||
|
// - Raw UTF-8 encoded strings
|
||||||
|
// Pure ASCII input is passed through directly (no conversion overhead).
|
||||||
|
extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf8Str || !result) return -1;
|
||||||
|
int len = (int)strlen(utf8Str);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
|
||||||
|
// Check if input contains \uXXXX escapes or non-ASCII bytes
|
||||||
|
bool hasUnicodeEscapes = false;
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true;
|
||||||
|
if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pure ASCII with no escapes — pass through directly
|
||||||
|
if (!hasNonAscii && !hasUnicodeEscapes) {
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = len;
|
||||||
|
memcpy((*result)->str, utf8Str, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contains \uXXXX escapes, decode them to UTF-16LE directly
|
||||||
|
if (hasUnicodeEscapes) {
|
||||||
|
std::string utf16le;
|
||||||
|
utf16le.reserve(len * 2);
|
||||||
|
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);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
// ASCII or raw UTF-8 byte — convert as single char
|
||||||
|
utf16le += utf8Str[i];
|
||||||
|
utf16le += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = (int)utf16le.size();
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, utf16le.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw UTF-8 — convert via Windows API
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0);
|
||||||
|
if (wideLen <= 0) return 0;
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen);
|
||||||
|
int size = wideLen * (int)sizeof(wchar_t);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, wideStr.data(), size);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" ANSOCR_API int ANSOCR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf16leBytes || byteLen <= 0 || !result) return -1;
|
||||||
|
// Check if input is already pure ASCII (no high bytes, or not valid UTF-16LE)
|
||||||
|
// If all bytes < 0x80 and byteLen has no null bytes in odd positions pattern,
|
||||||
|
// treat as already-UTF8 ASCII and pass through
|
||||||
|
bool isAlreadyAscii = true;
|
||||||
|
bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0);
|
||||||
|
if (isUtf16le) {
|
||||||
|
// Check if all high bytes (odd indices) are 0x00 — means pure ASCII in UTF-16LE
|
||||||
|
for (int i = 1; i < byteLen; i += 2) {
|
||||||
|
if (utf16leBytes[i] != 0x00) { isAlreadyAscii = false; break; }
|
||||||
|
}
|
||||||
|
if (isAlreadyAscii) {
|
||||||
|
// Extract just the low bytes (ASCII characters)
|
||||||
|
int asciiLen = byteLen / 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];
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = byteLen / (int)sizeof(wchar_t);
|
||||||
|
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(utf16leBytes);
|
||||||
|
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (utf8Len <= 0) return 0;
|
||||||
|
std::string utf8Str(utf8Len, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = utf8Len;
|
||||||
|
memcpy((*result)->str, utf8Str.data(), utf8Len);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" ANSOCR_API std::string RunInferenceImagePath(ANSCENTER::ANSOCRBase** Handle, const char* imageFilePath) {
|
extern "C" ANSOCR_API std::string RunInferenceImagePath(ANSCENTER::ANSOCRBase** Handle, const char* imageFilePath) {
|
||||||
if (!Handle || !*Handle) return "";
|
if (!Handle || !*Handle) return "";
|
||||||
OCRHandleGuard guard(AcquireOCRHandle(*Handle));
|
OCRHandleGuard guard(AcquireOCRHandle(*Handle));
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
#include "ANSODEngine.h"
|
#include "ANSODEngine.h"
|
||||||
#include "ANSYOLOOD.h"
|
#include "ANSYOLOOD.h"
|
||||||
#include "ANSTENSORRTOD.h"
|
#include "ANSTENSORRTOD.h"
|
||||||
@@ -1078,6 +1082,32 @@ namespace ANSCENTER {
|
|||||||
return boundingBox;
|
return boundingBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string DoubleEscapeUnicode(const std::string& utf8Str) {
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (unsigned char c : utf8Str) {
|
||||||
|
if (c >= 0x80) { hasNonAscii = true; break; }
|
||||||
|
}
|
||||||
|
if (!hasNonAscii) return utf8Str;
|
||||||
|
std::string result;
|
||||||
|
result.reserve(utf8Str.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < utf8Str.size()) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(utf8Str[i]);
|
||||||
|
if (c < 0x80) { result += utf8Str[i++]; continue; }
|
||||||
|
uint32_t cp = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0 && i + 1 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x1F) << 6) | (static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F); i += 2;
|
||||||
|
} else if ((c & 0xF0) == 0xE0 && i + 2 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F); i += 3;
|
||||||
|
} else if ((c & 0xF8) == 0xF0 && i + 3 < utf8Str.size()) {
|
||||||
|
cp = ((c & 0x07) << 18) | ((static_cast<unsigned char>(utf8Str[i + 1]) & 0x3F) << 12) | ((static_cast<unsigned char>(utf8Str[i + 2]) & 0x3F) << 6) | (static_cast<unsigned char>(utf8Str[i + 3]) & 0x3F); i += 4;
|
||||||
|
} else { i++; continue; }
|
||||||
|
if (cp <= 0xFFFF) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", cp); result += buf; }
|
||||||
|
else { cp -= 0x10000; char buf[16]; snprintf(buf, sizeof(buf), "\\u%04x\\u%04x", 0xD800 + (uint16_t)(cp >> 10), 0xDC00 + (uint16_t)(cp & 0x3FF)); result += buf; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ANSCENTER::ANSUtilityHelper::VectorDetectionToJsonString(const std::vector<Object>& dets)
|
std::string ANSCENTER::ANSUtilityHelper::VectorDetectionToJsonString(const std::vector<Object>& dets)
|
||||||
{
|
{
|
||||||
if (dets.empty()) {
|
if (dets.empty()) {
|
||||||
@@ -1091,13 +1121,13 @@ namespace ANSCENTER {
|
|||||||
{"class_id", std::to_string(det.classId)},
|
{"class_id", std::to_string(det.classId)},
|
||||||
//{"track_id", std::to_string(det.trackId)},
|
//{"track_id", std::to_string(det.trackId)},
|
||||||
{"track_id", "0"},
|
{"track_id", "0"},
|
||||||
{"class_name", det.className},
|
{"class_name", DoubleEscapeUnicode(det.className)},
|
||||||
{"prob", std::to_string(det.confidence)},
|
{"prob", std::to_string(det.confidence)},
|
||||||
{"x", std::to_string(det.box.x)},
|
{"x", std::to_string(det.box.x)},
|
||||||
{"y", std::to_string(det.box.y)},
|
{"y", std::to_string(det.box.y)},
|
||||||
{"width", std::to_string(det.box.width)},
|
{"width", std::to_string(det.box.width)},
|
||||||
{"height", std::to_string(det.box.height)},
|
{"height", std::to_string(det.box.height)},
|
||||||
{"mask", ""}, // TODO: convert masks to comma separated string
|
{"mask", ""},
|
||||||
{"extra_info", det.extraInfo},
|
{"extra_info", det.extraInfo},
|
||||||
{"camera_id", det.cameraId},
|
{"camera_id", det.cameraId},
|
||||||
{"polygon", PolygonToString(det.polygon)},
|
{"polygon", PolygonToString(det.polygon)},
|
||||||
@@ -2212,6 +2242,144 @@ namespace ANSCENTER {
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSENGINE_API int ANSEngine_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf8Str || !result) return -1;
|
||||||
|
int len = (int)strlen(utf8Str);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
bool hasUnicodeEscapes = false;
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true;
|
||||||
|
if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true;
|
||||||
|
}
|
||||||
|
if (!hasNonAscii && !hasUnicodeEscapes) {
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = len;
|
||||||
|
memcpy((*result)->str, utf8Str, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (hasUnicodeEscapes) {
|
||||||
|
std::string utf16le;
|
||||||
|
utf16le.reserve(len * 2);
|
||||||
|
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);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
utf16le += utf8Str[i];
|
||||||
|
utf16le += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = (int)utf16le.size();
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, utf16le.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0);
|
||||||
|
if (wideLen <= 0) return 0;
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen);
|
||||||
|
int size = wideLen * (int)sizeof(wchar_t);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, wideStr.data(), size);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" ANSENGINE_API int ANSEngine_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf16leBytes || byteLen <= 0 || !result) return -1;
|
||||||
|
bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0);
|
||||||
|
if (isUtf16le) {
|
||||||
|
bool isAscii = true;
|
||||||
|
for (int i = 1; i < byteLen; i += 2) {
|
||||||
|
if (utf16leBytes[i] != 0x00) { isAscii = false; break; }
|
||||||
|
}
|
||||||
|
if (isAscii) {
|
||||||
|
int asciiLen = byteLen / 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];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
int wideLen = byteLen / (int)sizeof(wchar_t);
|
||||||
|
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(utf16leBytes);
|
||||||
|
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (utf8Len <= 0) return 0;
|
||||||
|
std::string utf8Str(utf8Len, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = utf8Len;
|
||||||
|
memcpy((*result)->str, utf8Str.data(), utf8Len);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ANSUtilityHelper::DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(escapedStr.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < escapedStr.size()) {
|
||||||
|
if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') {
|
||||||
|
// Parse \uXXXX
|
||||||
|
char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 };
|
||||||
|
uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16);
|
||||||
|
// UTF-16LE: low byte first, high byte second
|
||||||
|
result += static_cast<char>(codepoint & 0xFF);
|
||||||
|
result += static_cast<char>((codepoint >> 8) & 0xFF);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
// ASCII character -> UTF-16LE (2 bytes: char, 0x00)
|
||||||
|
result += escapedStr[i];
|
||||||
|
result += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ANSUtilityHelper::ConvertUTF8ToUTF16LE(const std::string& utf8Str) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (utf8Str.empty()) return "";
|
||||||
|
// First call: get required buffer size
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), nullptr, 0);
|
||||||
|
if (wideLen <= 0) return "";
|
||||||
|
// Allocate wide string buffer
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), &wideStr[0], wideLen);
|
||||||
|
// Convert wchar_t buffer to raw bytes (UTF-16LE on Windows)
|
||||||
|
const char* rawBytes = reinterpret_cast<const char*>(wideStr.data());
|
||||||
|
return std::string(rawBytes, wideLen * sizeof(wchar_t));
|
||||||
|
#else
|
||||||
|
// Non-Windows: return UTF-8 as-is (LabVIEW is primarily Windows)
|
||||||
|
return utf8Str;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
float ANSUtilityHelper::calculate_intersection_area(const cv::Rect& box1, const cv::Rect& box2) {
|
float ANSUtilityHelper::calculate_intersection_area(const cv::Rect& box1, const cv::Rect& box2) {
|
||||||
int xx1 = std::max(box1.x, box2.x);
|
int xx1 = std::max(box1.x, box2.x);
|
||||||
int yy1 = std::max(box1.y, box2.y);
|
int yy1 = std::max(box1.y, box2.y);
|
||||||
|
|||||||
@@ -660,6 +660,17 @@ namespace ANSCENTER
|
|||||||
static std::string PolygonToString(const std::vector<cv::Point2f>& polygon);
|
static std::string PolygonToString(const std::vector<cv::Point2f>& polygon);
|
||||||
static std::string KeypointsToString(const std::vector<float>& kps);
|
static std::string KeypointsToString(const std::vector<float>& kps);
|
||||||
static std::vector<cv::Point2f> RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight);
|
static std::vector<cv::Point2f> RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight);
|
||||||
|
|
||||||
|
// Convert a UTF-8 encoded string to UTF-16LE byte string.
|
||||||
|
// Useful for LabVIEW which can display UTF-16LE Unicode text on Windows.
|
||||||
|
// Returns a std::string containing the raw UTF-16LE bytes (2 bytes per character).
|
||||||
|
static std::string ConvertUTF8ToUTF16LE(const std::string& utf8Str);
|
||||||
|
|
||||||
|
// Decode JSON Unicode escape sequences (\uXXXX) to UTF-16LE byte string.
|
||||||
|
// Input: ASCII string with \uXXXX escapes (e.g., "\\u6c5f\\u6771 599")
|
||||||
|
// Output: UTF-16LE byte string for LabVIEW display.
|
||||||
|
// ASCII characters pass through as 2-byte UTF-16LE (e.g., 'A' -> 0x41 0x00).
|
||||||
|
static std::string DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr);
|
||||||
static std::vector<cv::Point2f> MaskToNormalizedPolygon(const cv::Mat& binaryMask, const cv::Rect& boundingBox, float imageWidth, float imageHeight, float simplificationEpsilon = 2.0f, int minContourArea = 10, int maxPoints = 50);
|
static std::vector<cv::Point2f> MaskToNormalizedPolygon(const cv::Mat& binaryMask, const cv::Rect& boundingBox, float imageWidth, float imageHeight, float simplificationEpsilon = 2.0f, int minContourArea = 10, int maxPoints = 50);
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -1142,4 +1153,8 @@ namespace ANSCENTER
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW wrapper classes
|
||||||
|
extern "C" ANSENGINE_API int ANSEngine_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result);
|
||||||
|
extern "C" ANSENGINE_API int ANSEngine_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "ANSUtilities.h"
|
#include "ANSUtilities.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <CkFileAccess.h>
|
#include <CkFileAccess.h>
|
||||||
#include <CkAuthGoogle.h>
|
#include <CkAuthGoogle.h>
|
||||||
@@ -949,5 +952,55 @@ namespace ANSCENTER
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ANSUtilities::DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(escapedStr.size() * 2);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < escapedStr.size()) {
|
||||||
|
if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') {
|
||||||
|
char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 };
|
||||||
|
uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16);
|
||||||
|
result += static_cast<char>(codepoint & 0xFF);
|
||||||
|
result += static_cast<char>((codepoint >> 8) & 0xFF);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
result += escapedStr[i];
|
||||||
|
result += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ANSUtilities::ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!utf16leBytes || byteLen <= 0) return "";
|
||||||
|
int wideLen = byteLen / (int)sizeof(wchar_t);
|
||||||
|
const wchar_t* wideStr = reinterpret_cast<const wchar_t*>(utf16leBytes);
|
||||||
|
// First call: get required UTF-8 buffer size
|
||||||
|
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (utf8Len <= 0) return "";
|
||||||
|
std::string utf8Str(utf8Len, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr);
|
||||||
|
return utf8Str;
|
||||||
|
#else
|
||||||
|
return std::string(utf16leBytes, byteLen);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ANSUtilities::ConvertUTF8ToUTF16LE(const std::string& utf8Str) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (utf8Str.empty()) return "";
|
||||||
|
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), nullptr, 0);
|
||||||
|
if (wideLen <= 0) return "";
|
||||||
|
std::wstring wideStr(wideLen, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), &wideStr[0], wideLen);
|
||||||
|
const char* rawBytes = reinterpret_cast<const char*>(wideStr.data());
|
||||||
|
return std::string(rawBytes, wideLen * sizeof(wchar_t));
|
||||||
|
#else
|
||||||
|
return utf8Str;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,19 @@ namespace ANSCENTER {
|
|||||||
|
|
||||||
// Restart PC
|
// Restart PC
|
||||||
[[nodiscard]] static bool RestartPC();
|
[[nodiscard]] static bool RestartPC();
|
||||||
|
|
||||||
|
// Convert a UTF-8 encoded string to UTF-16LE byte string.
|
||||||
|
// Useful for LabVIEW which can display UTF-16LE Unicode text on Windows.
|
||||||
|
static std::string ConvertUTF8ToUTF16LE(const std::string& utf8Str);
|
||||||
|
|
||||||
|
// Decode JSON Unicode escape sequences (\uXXXX) to UTF-16LE byte string.
|
||||||
|
// Input: ASCII string with \uXXXX escapes (e.g., "\\u6c5f\\u6771 599")
|
||||||
|
// Output: UTF-16LE byte string for LabVIEW display.
|
||||||
|
static std::string DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr);
|
||||||
|
|
||||||
|
// Convert a UTF-16LE byte string to UTF-8.
|
||||||
|
// Useful for receiving Unicode text from LabVIEW and converting to UTF-8 for internal processing.
|
||||||
|
static std::string ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen);
|
||||||
};
|
};
|
||||||
// Connection bundle for pool
|
// Connection bundle for pool
|
||||||
struct S3Connection {
|
struct S3Connection {
|
||||||
@@ -230,6 +243,10 @@ extern "C" ANSULT_API int SendEmail(const char* smtpServer, int port,
|
|||||||
|
|
||||||
extern "C" ANSULT_API int RebootSystem();
|
extern "C" ANSULT_API int RebootSystem();
|
||||||
|
|
||||||
|
// Unicode conversion utilities for LabVIEW
|
||||||
|
extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result);
|
||||||
|
extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, LStrHandle result);
|
||||||
|
extern "C" ANSULT_API int ANSConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result);
|
||||||
|
|
||||||
// AWS S3 class
|
// AWS S3 class
|
||||||
|
|
||||||
|
|||||||
@@ -798,6 +798,104 @@ extern "C" ANSULT_API int RebootSystem() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" ANSULT_API int ANSConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!utf8Str || !result) return -1;
|
||||||
|
int len = (int)strlen(utf8Str);
|
||||||
|
if (len == 0) return 0;
|
||||||
|
bool hasUnicodeEscapes = false;
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true;
|
||||||
|
if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true;
|
||||||
|
}
|
||||||
|
if (!hasNonAscii && !hasUnicodeEscapes) {
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = len;
|
||||||
|
memcpy((*result)->str, utf8Str, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (hasUnicodeEscapes) {
|
||||||
|
std::string utf16le;
|
||||||
|
utf16le.reserve(len * 2);
|
||||||
|
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);
|
||||||
|
i += 6;
|
||||||
|
} else {
|
||||||
|
utf16le += utf8Str[i];
|
||||||
|
utf16le += '\0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int size = (int)utf16le.size();
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, utf16le.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF8ToUTF16LE(utf8Str);
|
||||||
|
if (converted.empty()) return 0;
|
||||||
|
int size = static_cast<int>(converted.size());
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, converted.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (isUtf16le) {
|
||||||
|
bool isAscii = true;
|
||||||
|
for (int i = 1; i < byteLen; i += 2) {
|
||||||
|
if (utf16leBytes[i] != 0x00) { isAscii = false; break; }
|
||||||
|
}
|
||||||
|
if (isAscii) {
|
||||||
|
int asciiLen = byteLen / 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];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string converted = ANSCENTER::ANSUtilities::ConvertUTF16LEToUTF8(
|
||||||
|
reinterpret_cast<const char*>(utf16leBytes), byteLen);
|
||||||
|
if (converted.empty()) return 0;
|
||||||
|
int size = static_cast<int>(converted.size());
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, converted.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" ANSULT_API int ANSDecodeJsonUnicodeToUTF16LE(const char* escapedStr, LStrHandle result) {
|
||||||
|
try {
|
||||||
|
if (!escapedStr || !result) return -1;
|
||||||
|
std::string decoded = ANSCENTER::ANSUtilities::DecodeJsonUnicodeToUTF16LE(escapedStr);
|
||||||
|
if (decoded.empty()) return 0;
|
||||||
|
int size = static_cast<int>(decoded.size());
|
||||||
|
MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar));
|
||||||
|
if (error != noErr) return -2;
|
||||||
|
(*result)->cnt = size;
|
||||||
|
memcpy((*result)->str, decoded.data(), size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user