Support unicode to get users/user APIs

This commit is contained in:
2026-04-06 17:02:42 +10:00
parent b98aed21bf
commit 6d792ee4c0
3 changed files with 73 additions and 38 deletions

View File

@@ -1814,10 +1814,12 @@ namespace ANSCENTER {
}
}
// No faces is a valid state (user just created or faces already removed)
if (faceIds.empty()) {
LogThreadSafe("ANSFacialRecognition::DeleteFacesByUser",
"No faces found for user ID " + std::to_string(userId));
return 0;
"No faces found for user ID " + std::to_string(userId) + " — skipping",
LogLevel::Debug);
return 1;
}
// Delete each face from database (lock per deletion)

View File

@@ -5,6 +5,7 @@
#include <opencv2/dnn/dnn.hpp>
#include <opencv2/highgui.hpp>
#include <utils/ocv_common.hpp>
#include <json.hpp>
namespace ANSCENTER {
constexpr size_t ndetections = 200;
template <typename T> T GetData(const boost::property_tree::ptree& pt, const std::string& key)
@@ -684,46 +685,77 @@ namespace ANSCENTER {
return faceResult;
}
// Encode non-ASCII UTF-8 characters as double-escaped Unicode (\uXXXX) for JSON transport.
// Surrogate pairs are used for codepoints above U+FFFF.
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;
}
// Using nlohmann/json (consistent with FaceObjectsToJsonString)
std::string ANSFRHelper::UserRecordToJsonString(const UserRecord userRecord) {
boost::property_tree::ptree root;
boost::property_tree::ptree faceIds;
for (int i = 0; i < userRecord.FaceIds.size(); i++) {
boost::property_tree::ptree faceIdNode;
faceIdNode.put("", userRecord.FaceIds[i]);
faceIds.push_back(std::make_pair("", faceIdNode));
try {
nlohmann::json faceIdArray = nlohmann::json::array();
for (const auto& faceId : userRecord.FaceIds) {
faceIdArray.push_back(faceId);
}
root.put("user_id", userRecord.UserId);
root.put("user_code", userRecord.UserCode);
root.put("user_username", userRecord.UserName);
root.add_child("face_ids", faceIds);
std::ostringstream stream;
boost::property_tree::write_json(stream, root,false);
std::string userRecordResult = stream.str();
return userRecordResult;
nlohmann::json root = {
{"user_id", std::to_string(userRecord.UserId)},
{"user_code", userRecord.UserCode},
{"user_username", DoubleEscapeUnicode(userRecord.UserName)},
{"face_ids", faceIdArray}
};
return root.dump();
}
catch (const std::exception&) {
return R"({})";
}
}
// Using nlohmann/json (consistent with FaceObjectsToJsonString)
std::string ANSFRHelper::UserRecordsToJsonString(const std::vector<UserRecord> userRecords) {
boost::property_tree::ptree root;
boost::property_tree::ptree userRecordNodes;
for (int i=0; i < userRecords.size(); i++) {
boost::property_tree::ptree userRecordNode;
boost::property_tree::ptree faceIds;
UserRecord userRecord = userRecords[i];
for (int j = 0; j < userRecord.FaceIds.size(); j++) {
boost::property_tree::ptree faceIdNode;
faceIdNode.put("", userRecord.FaceIds[j]);
faceIds.push_back(std::make_pair("", faceIdNode));
try {
nlohmann::json root;
auto& records = root["user_records"] = nlohmann::json::array();
for (const auto& userRecord : userRecords) {
nlohmann::json faceIdArray = nlohmann::json::array();
for (const auto& faceId : userRecord.FaceIds) {
faceIdArray.push_back(faceId);
}
userRecordNode.put("user_id", userRecord.UserId);
userRecordNode.put("user_code", userRecord.UserCode);
userRecordNode.put("user_username", userRecord.UserName);
userRecordNode.add_child("face_ids", faceIds);
userRecordNodes.push_back(std::make_pair("", userRecordNode));
records.push_back({
{"user_id", std::to_string(userRecord.UserId)},
{"user_code", userRecord.UserCode},
{"user_username", DoubleEscapeUnicode(userRecord.UserName)},
{"face_ids", faceIdArray}
});
}
return root.dump();
}
catch (const std::exception&) {
return R"({"user_records":[]})";
}
root.add_child("user_records", userRecordNodes);
std::ostringstream stream;
boost::property_tree::write_json(stream, root,false);
std::string userRecordResult = stream.str();
return userRecordResult;
}
std::string ANSFRHelper::FaceRecordToJsonString(const FaceRecord faceRecord) {
boost::property_tree::ptree root;

View File

@@ -37,6 +37,7 @@ target_include_directories(ANSFR-UnitTest PRIVATE
${ANSLIBS_DIR}/faiss
${ANSLIBS_DIR}/TensorRT/include
${ANSLIBS_DIR}/fastdeploy_gpu/include
${ANSLIBS_DIR}/nlohmann
)
target_link_libraries(ANSFR-UnitTest