Add ANSALPR country format
This commit is contained in:
@@ -145,7 +145,11 @@
|
|||||||
"Bash(grep -nE \"^\\\\}$|warmupModel\\\\\\(|#if 0|#endif|ANSONNXCL_legacy_Init\" C:/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/ANSONNXCL.cpp)",
|
"Bash(grep -nE \"^\\\\}$|warmupModel\\\\\\(|#if 0|#endif|ANSONNXCL_legacy_Init\" C:/Projects/CLionProjects/ANSCORE/modules/ANSODEngine/ANSONNXCL.cpp)",
|
||||||
"Bash(git -C C:/Projects/CLionProjects/ANSCORE restore modules/ANSODEngine/ANSONNXCL.h modules/ANSODEngine/ANSONNXCL.cpp)",
|
"Bash(git -C C:/Projects/CLionProjects/ANSCORE restore modules/ANSODEngine/ANSONNXCL.h modules/ANSODEngine/ANSONNXCL.cpp)",
|
||||||
"Bash(git -C C:/Projects/CLionProjects/ANSCORE status --short modules/)",
|
"Bash(git -C C:/Projects/CLionProjects/ANSCORE status --short modules/)",
|
||||||
"Bash(git -C C:/Projects/CLionProjects/ANSCORE diff --stat HEAD modules/ANSODEngine/ANSONNXCL.cpp modules/ANSODEngine/ANSONNXOBB.cpp modules/ANSODEngine/ANSONNXPOSE.cpp modules/ANSODEngine/ANSONNXSEG.cpp modules/ANSODEngine/ANSYOLO12OD.cpp engines/ONNXEngine/ONNXEngine.cpp engines/ONNXEngine/ONNXSAM3.cpp)"
|
"Bash(git -C C:/Projects/CLionProjects/ANSCORE diff --stat HEAD modules/ANSODEngine/ANSONNXCL.cpp modules/ANSODEngine/ANSONNXOBB.cpp modules/ANSODEngine/ANSONNXPOSE.cpp modules/ANSODEngine/ANSONNXSEG.cpp modules/ANSODEngine/ANSYOLO12OD.cpp engines/ONNXEngine/ONNXEngine.cpp engines/ONNXEngine/ONNXSAM3.cpp)",
|
||||||
|
"Bash(awk -F'[:\\(\\)]' '{gsub\\(/^ +| +$/,\"\",$2\\); split\\($2,a,\" \"\\); ms=a[1]; if \\(ms+0 >= 500\\) print $0}')",
|
||||||
|
"Bash(awk -F'[:\\(\\)]' '{split\\($2,a,\" \"\\); ms=a[1]; if \\(ms+0 >= 100\\) print $0}')",
|
||||||
|
"Bash(grep -cE \"^\\\\s*Plate: [^ ]+$\" \"C:/Users/nghia/Downloads/Log4.txt\")",
|
||||||
|
"Bash(grep -oE \"Plate: [^ ]+$\" \"C:/Users/nghia/Downloads/Log5.txt\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,12 @@ namespace ANSCENTER
|
|||||||
else _country = Country::JAPAN; // Default for OCR mode
|
else _country = Country::JAPAN; // Default for OCR mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install the Vietnam-only plate-format defaults now that the
|
||||||
|
// country is known. Idempotent — clears `_plateFormats` first.
|
||||||
|
// SetCountry() also calls this so a runtime country switch picks
|
||||||
|
// up the right format set without an Initialize / restart.
|
||||||
|
ApplyCountryDefaultPlateFormats();
|
||||||
|
|
||||||
// Store the original model zip path — the OCR models (ansocrdec.onnx,
|
// Store the original model zip path — the OCR models (ansocrdec.onnx,
|
||||||
// ansocrcls.onnx, ansocrrec.onnx, dict_ch.txt) are bundled inside the
|
// ansocrcls.onnx, ansocrrec.onnx, dict_ch.txt) are bundled inside the
|
||||||
// same ALPR model zip, so we reuse it for ANSONNXOCR initialization.
|
// same ALPR model zip, so we reuse it for ANSONNXOCR initialization.
|
||||||
@@ -929,6 +935,140 @@ namespace ANSCENTER
|
|||||||
return stripped;
|
return stripped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Country-specific post-processing. See header for full contract.
|
||||||
|
// Mirrors the ANSALPR_OD pipeline so OD and OCR engines emit the
|
||||||
|
// same canonical form for Vietnam / Indonesia / Australia / USA,
|
||||||
|
// while leaving Japan / China text untouched (kana / kanji would
|
||||||
|
// be destroyed by an ASCII alnum filter, so we pass-through).
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
std::string ANSALPR_OCR::AnalyseLicensePlateText(const std::string& ocrText) {
|
||||||
|
if (ocrText.empty()) return ocrText;
|
||||||
|
|
||||||
|
// Country gate — Vietnam-only. Every other country (Japan, China,
|
||||||
|
// Indonesia, Australia, USA, …) returns the input unchanged so the
|
||||||
|
// upstream StripPlateSeparators output flows through verbatim and
|
||||||
|
// kana / kanji / region-specific punctuation is preserved.
|
||||||
|
if (_country != Country::VIETNAM) {
|
||||||
|
return ocrText;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vietnam: keep only ASCII alphanumerics (StripPlateSeparators
|
||||||
|
// already removed the common separators; this is a defensive
|
||||||
|
// second pass that also catches any stray punctuation the OCR
|
||||||
|
// may have emitted). Uppercase so the format-match literals
|
||||||
|
// 'M', 'D', 'N', 'G', 'Q', 'T', 'C', 'V' work against the
|
||||||
|
// uppercase plate output.
|
||||||
|
std::string analysedLP;
|
||||||
|
analysedLP.reserve(ocrText.size());
|
||||||
|
for (char c : ocrText) {
|
||||||
|
if (std::isalnum(static_cast<unsigned char>(c))) {
|
||||||
|
analysedLP += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::transform(analysedLP.begin(), analysedLP.end(), analysedLP.begin(),
|
||||||
|
[](unsigned char ch) { return static_cast<char>(std::toupper(ch)); });
|
||||||
|
|
||||||
|
// Format validation: when `_plateFormats` is configured (which
|
||||||
|
// it is by default for Vietnam via ApplyCountryDefaultPlateFormats),
|
||||||
|
// reject plates that don't match any pattern. Empty list = no
|
||||||
|
// validation (caller cleared it via SetPlateFormats), in which
|
||||||
|
// case everything is accepted just like ANSALPR_OD does.
|
||||||
|
if (!analysedLP.empty() && !_plateFormats.empty()
|
||||||
|
&& !MatchesPlateFormat(analysedLP)) {
|
||||||
|
ANS_DBG("ALPR_Format",
|
||||||
|
"REJECT plate='%s' (no _plateFormats match)",
|
||||||
|
analysedLP.c_str());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return analysedLP;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
this->_logger.LogFatal("ANSALPR_OCR::AnalyseLicensePlateText",
|
||||||
|
e.what(), __FILE__, __LINE__);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ANSALPR_OCR::MatchesPlateFormat(const std::string& plate) const {
|
||||||
|
if (_plateFormats.empty()) {
|
||||||
|
return true; // No formats configured → accept all.
|
||||||
|
}
|
||||||
|
for (const auto& format : _plateFormats) {
|
||||||
|
if (plate.size() != format.size()) continue;
|
||||||
|
bool matches = true;
|
||||||
|
for (size_t i = 0; i < format.size(); ++i) {
|
||||||
|
char f = format[i];
|
||||||
|
char p = plate[i];
|
||||||
|
if (f == 'd') {
|
||||||
|
if (!std::isdigit(static_cast<unsigned char>(p))) { matches = false; break; }
|
||||||
|
}
|
||||||
|
else if (f == 'l') {
|
||||||
|
if (!std::isalpha(static_cast<unsigned char>(p))) { matches = false; break; }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Literal character — exact match required.
|
||||||
|
if (p != f) { matches = false; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ANSALPR_OCR::ApplyCountryDefaultPlateFormats() {
|
||||||
|
// Reset first so repeated SetCountry calls don't accumulate stale
|
||||||
|
// patterns from previous countries.
|
||||||
|
_plateFormats.clear();
|
||||||
|
|
||||||
|
switch (_country) {
|
||||||
|
case Country::VIETNAM: {
|
||||||
|
// Same 11 patterns ANSALPR_OD installs for Vietnam. Keeping
|
||||||
|
// these identical ensures the OD- and OCR-based pipelines
|
||||||
|
// accept exactly the same plate strings in production.
|
||||||
|
// ddlddddd — standard car (e.g. 29A12345)
|
||||||
|
// ddldddd — short variant
|
||||||
|
// ddldddddd — extended variant
|
||||||
|
// ddllddddd — two-letter series
|
||||||
|
// ddMDdddddd — military
|
||||||
|
// dddddNGdd — diplomatic NG
|
||||||
|
// dddddQTdd — diplomatic QT
|
||||||
|
// dddddCVdd — diplomatic CV
|
||||||
|
// dddddNNdd — diplomatic NN
|
||||||
|
// lldddd — special two-letter prefix
|
||||||
|
_plateFormats.push_back("ddlddddd");
|
||||||
|
_plateFormats.push_back("ddldddd");
|
||||||
|
_plateFormats.push_back("ddldddddd");
|
||||||
|
_plateFormats.push_back("ddllddddd");
|
||||||
|
_plateFormats.push_back("ddllddddd");
|
||||||
|
_plateFormats.push_back("ddMDdddddd");
|
||||||
|
_plateFormats.push_back("dddddNGdd");
|
||||||
|
_plateFormats.push_back("dddddQTdd");
|
||||||
|
_plateFormats.push_back("dddddCVdd");
|
||||||
|
_plateFormats.push_back("dddddNNdd");
|
||||||
|
_plateFormats.push_back("lldddd");
|
||||||
|
ANS_DBG("ALPR_Format",
|
||||||
|
"ApplyCountryDefaultPlateFormats: VIETNAM (%zu patterns)",
|
||||||
|
_plateFormats.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Country::CHINA:
|
||||||
|
case Country::AUSTRALIA:
|
||||||
|
case Country::USA:
|
||||||
|
case Country::INDONESIA:
|
||||||
|
case Country::JAPAN:
|
||||||
|
default:
|
||||||
|
// No default patterns configured for these countries. The
|
||||||
|
// caller can still install custom formats via SetPlateFormat
|
||||||
|
// / SetPlateFormats; otherwise format validation accepts all.
|
||||||
|
ANS_DBG("ALPR_Format",
|
||||||
|
"ApplyCountryDefaultPlateFormats: country=%d (no default patterns)",
|
||||||
|
static_cast<int>(_country));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string ANSALPR_OCR::RecoverKanaFromBottomHalf(
|
std::string ANSALPR_OCR::RecoverKanaFromBottomHalf(
|
||||||
const cv::Mat& plateROI, int halfH) const
|
const cv::Mat& plateROI, int halfH) const
|
||||||
{
|
{
|
||||||
@@ -1641,6 +1781,13 @@ namespace ANSCENTER
|
|||||||
combinedText = StripPlateSeparators(combinedText);
|
combinedText = StripPlateSeparators(combinedText);
|
||||||
if (combinedText.empty()) continue;
|
if (combinedText.empty()) continue;
|
||||||
|
|
||||||
|
// Vietnam-only: alnum filter + uppercase + format validation
|
||||||
|
// against `_plateFormats`. No-op on every other country.
|
||||||
|
// Same canonical form ANSALPR_OD emits, so OD- and OCR-based
|
||||||
|
// pipelines can be A/B compared on the same input video.
|
||||||
|
combinedText = AnalyseLicensePlateText(combinedText);
|
||||||
|
if (combinedText.empty()) continue;
|
||||||
|
|
||||||
Object lprObject = lprOutput[info.origIndex];
|
Object lprObject = lprOutput[info.origIndex];
|
||||||
lprObject.cameraId = cameraId;
|
lprObject.cameraId = cameraId;
|
||||||
|
|
||||||
@@ -1906,6 +2053,13 @@ namespace ANSCENTER
|
|||||||
combined = StripPlateSeparators(combined);
|
combined = StripPlateSeparators(combined);
|
||||||
if (combined.empty()) continue;
|
if (combined.empty()) continue;
|
||||||
|
|
||||||
|
// Vietnam-only post-processing (alnum + uppercase + format
|
||||||
|
// validation). Same gate as the full-frame path so OD and
|
||||||
|
// OCR engines emit identical strings on Vietnam input, and
|
||||||
|
// non-Vietnam countries pass through unchanged.
|
||||||
|
combined = AnalyseLicensePlateText(combined);
|
||||||
|
if (combined.empty()) continue;
|
||||||
|
|
||||||
Object out = pm.lpObj;
|
Object out = pm.lpObj;
|
||||||
out.className = combined; // raw OCR — no ALPRChecker
|
out.className = combined; // raw OCR — no ALPRChecker
|
||||||
out.cameraId = cameraId;
|
out.cameraId = cameraId;
|
||||||
@@ -2014,6 +2168,10 @@ namespace ANSCENTER
|
|||||||
if (_ocrEngine) {
|
if (_ocrEngine) {
|
||||||
_ocrEngine->SetCountry(country);
|
_ocrEngine->SetCountry(country);
|
||||||
}
|
}
|
||||||
|
// Re-install country-default plate formats so a runtime country
|
||||||
|
// switch picks up Vietnam's format list (or clears it when leaving
|
||||||
|
// Vietnam) without needing an Initialize / restart.
|
||||||
|
ApplyCountryDefaultPlateFormats();
|
||||||
// Log every SetCountry call so runtime country switches are
|
// Log every SetCountry call so runtime country switches are
|
||||||
// visible and we can confirm the update landed on the right
|
// visible and we can confirm the update landed on the right
|
||||||
// handle. The recovery + rectification gates read _country on
|
// handle. The recovery + rectification gates read _country on
|
||||||
|
|||||||
@@ -109,6 +109,41 @@ namespace ANSCENTER
|
|||||||
// --- Original model zip path (reused for ANSONNXOCR initialization) ---
|
// --- Original model zip path (reused for ANSONNXOCR initialization) ---
|
||||||
std::string _modelZipFilePath;
|
std::string _modelZipFilePath;
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────
|
||||||
|
// Country-specific plate-format processing — Vietnam-only.
|
||||||
|
//
|
||||||
|
// When `_country == VIETNAM`, `AnalyseLicensePlateText` strips
|
||||||
|
// any remaining non-alphanumeric characters, uppercases the
|
||||||
|
// result, and validates it against `_plateFormats` (the same
|
||||||
|
// 11-pattern list ANSALPR_OD installs for Vietnam). Plates
|
||||||
|
// that don't match any configured format are dropped — the
|
||||||
|
// return value is "" and the caller skips the plate.
|
||||||
|
//
|
||||||
|
// Every other country (Japan, China, Indonesia, Australia, USA,
|
||||||
|
// …) returns the input unchanged so the upstream
|
||||||
|
// StripPlateSeparators output flows through verbatim, preserving
|
||||||
|
// kana / kanji / region-specific punctuation.
|
||||||
|
//
|
||||||
|
// Hooked in after StripPlateSeparators and before the ALPRChecker
|
||||||
|
// voter, so locked text + skip-when-locked output are emitted in
|
||||||
|
// the canonical Vietnam form just like ANSALPR_OD's path.
|
||||||
|
// ────────────────────────────────────────────────────────────────
|
||||||
|
[[nodiscard]] std::string AnalyseLicensePlateText(const std::string& ocrText);
|
||||||
|
|
||||||
|
// Format match helper — patterns: 'd' = any digit, 'l' = any
|
||||||
|
// letter, every other character is a literal that must match
|
||||||
|
// exactly. Returns true when `_plateFormats` is empty (i.e.
|
||||||
|
// no formats configured → no validation, accept all).
|
||||||
|
[[nodiscard]] bool MatchesPlateFormat(const std::string& plate) const;
|
||||||
|
|
||||||
|
// Reset `_plateFormats` to the default list for the current
|
||||||
|
// `_country`. Called at the end of Initialize (after country.txt
|
||||||
|
// is read) and from SetCountry so a runtime country switch picks
|
||||||
|
// up the right format set on the very next frame. Currently
|
||||||
|
// populates Vietnam's 11 standard patterns; other countries leave
|
||||||
|
// the list empty (= no validation).
|
||||||
|
void ApplyCountryDefaultPlateFormats();
|
||||||
|
|
||||||
// --- Colour detection helpers ---
|
// --- Colour detection helpers ---
|
||||||
[[nodiscard]] std::string DetectLPColourDetector(const cv::Mat& lprROI, const std::string& cameraId);
|
[[nodiscard]] std::string DetectLPColourDetector(const cv::Mat& lprROI, const std::string& cameraId);
|
||||||
[[nodiscard]] std::string DetectLPColourCached(const cv::Mat& lprROI, const std::string& cameraId, const std::string& plateText);
|
[[nodiscard]] std::string DetectLPColourCached(const cv::Mat& lprROI, const std::string& cameraId, const std::string& plateText);
|
||||||
|
|||||||
@@ -3953,9 +3953,14 @@ int ALPR_OCR_VideoTest() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Set country (JAPAN = 5 — adjust to match the dataset if needed)
|
// Step 2: Set country (VIETNAM = 0 — adjust to match the dataset if needed).
|
||||||
ANSALPR_SetCountry(&infHandle, 1);
|
// Country codes match country.txt parsing in ANSALPR_OCR::Initialize:
|
||||||
std::cout << "Country set to JAPAN" << std::endl;
|
// 0 = VIETNAM, 1 = CHINA, 2 = AUSTRALIA, 3 = USA, 4 = INDONESIA, 5 = JAPAN.
|
||||||
|
// Setting VIETNAM here also activates ANSALPR_OCR's Vietnam-only
|
||||||
|
// post-processing (alnum filter + uppercase + plate-format validation
|
||||||
|
// against the 11 standard Vietnam patterns).
|
||||||
|
ANSALPR_SetCountry(&infHandle, 0);
|
||||||
|
std::cout << "Country set to VIETNAM" << std::endl;
|
||||||
|
|
||||||
// Step 3: Load engine
|
// Step 3: Load engine
|
||||||
auto engineStart = std::chrono::high_resolution_clock::now();
|
auto engineStart = std::chrono::high_resolution_clock::now();
|
||||||
|
|||||||
Reference in New Issue
Block a user