Add ANSALPR country format
This commit is contained in:
@@ -194,6 +194,12 @@ namespace ANSCENTER
|
||||
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,
|
||||
// ansocrcls.onnx, ansocrrec.onnx, dict_ch.txt) are bundled inside the
|
||||
// same ALPR model zip, so we reuse it for ANSONNXOCR initialization.
|
||||
@@ -929,6 +935,140 @@ namespace ANSCENTER
|
||||
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(
|
||||
const cv::Mat& plateROI, int halfH) const
|
||||
{
|
||||
@@ -1641,6 +1781,13 @@ namespace ANSCENTER
|
||||
combinedText = StripPlateSeparators(combinedText);
|
||||
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];
|
||||
lprObject.cameraId = cameraId;
|
||||
|
||||
@@ -1906,6 +2053,13 @@ namespace ANSCENTER
|
||||
combined = StripPlateSeparators(combined);
|
||||
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;
|
||||
out.className = combined; // raw OCR — no ALPRChecker
|
||||
out.cameraId = cameraId;
|
||||
@@ -2014,6 +2168,10 @@ namespace ANSCENTER
|
||||
if (_ocrEngine) {
|
||||
_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
|
||||
// visible and we can confirm the update landed on the right
|
||||
// handle. The recovery + rectification gates read _country on
|
||||
|
||||
@@ -109,6 +109,41 @@ namespace ANSCENTER
|
||||
// --- Original model zip path (reused for ANSONNXOCR initialization) ---
|
||||
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 ---
|
||||
[[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);
|
||||
|
||||
Reference in New Issue
Block a user