Fix ALPR Batch and memory leak

This commit is contained in:
2026-04-15 09:23:05 +10:00
parent 7778f8c214
commit b05c49ad93
9 changed files with 686 additions and 83 deletions

View File

@@ -2617,6 +2617,124 @@ namespace ANSCENTER {
return {};
}
// ── Stateless batched inference for pipeline mode ───────────────────
// Caller supplies a full frame + a list of vehicle ROIs in FRAME
// coordinates. We run ONE LP-detect call across all vehicle crops and
// ONE char-OCR batch across every resulting plate, with NO tracker,
// voting, spatial dedup, or per-camera accumulating state. This is the
// drop-in replacement for the per-bbox loop inside
// ANSALPR_RunInferencesComplete_LV (pipeline mode) and is exported as
// ANSALPR_RunInferencesBatch_LV / _V2 in dllmain.cpp.
std::vector<Object> ANSALPR_OD::RunInferencesBatch(
const cv::Mat& input,
const std::vector<cv::Rect>& vehicleBoxes,
const std::string& cameraId)
{
if (!_licenseValid || !valid || !_isInitialized) {
this->_logger.LogWarn("ANSALPR_OD::RunInferencesBatch",
"Invalid state: license=" + std::to_string(_licenseValid) +
" valid=" + std::to_string(valid) +
" init=" + std::to_string(_isInitialized), __FILE__, __LINE__);
return {};
}
if (input.empty() || input.cols < 5 || input.rows < 5) return {};
if (!this->_lpDetector || !this->_ocrDetector) {
this->_logger.LogFatal("ANSALPR_OD::RunInferencesBatch",
"Detector instances are null", __FILE__, __LINE__);
return {};
}
if (vehicleBoxes.empty()) return {};
try {
// ── 1. Clamp and crop vehicle ROIs ────────────────────────
const cv::Rect frameRect(0, 0, input.cols, input.rows);
std::vector<cv::Mat> vehicleCrops;
std::vector<cv::Rect> clamped;
vehicleCrops.reserve(vehicleBoxes.size());
clamped.reserve(vehicleBoxes.size());
for (const auto& r : vehicleBoxes) {
cv::Rect c = r & frameRect;
if (c.width <= 5 || c.height <= 5) continue;
vehicleCrops.emplace_back(input(c));
clamped.push_back(c);
}
if (vehicleCrops.empty()) return {};
// ── 2. ONE batched LP detection call across all vehicles ──
// ANSODBase::RunInferencesBatch is fixed-shape YOLO, so this
// is a single ORT/TRT call regardless of how many vehicles
// the caller passed.
std::vector<std::vector<Object>> lpBatch =
_lpDetector->RunInferencesBatch(vehicleCrops, cameraId);
// ── 3. Flatten detected plates, keeping back-reference ───
struct PlateMeta {
size_t vehIdx; // index into vehicleCrops / clamped
Object lpObj; // LP detection in VEHICLE-local coords
};
std::vector<cv::Mat> alignedLPRBatch;
std::vector<PlateMeta> metas;
alignedLPRBatch.reserve(lpBatch.size() * 2);
metas.reserve(lpBatch.size() * 2);
for (size_t v = 0; v < lpBatch.size() && v < vehicleCrops.size(); ++v) {
const cv::Mat& veh = vehicleCrops[v];
const cv::Rect vehRect(0, 0, veh.cols, veh.rows);
for (const auto& lp : lpBatch[v]) {
cv::Rect lpBox = lp.box & vehRect;
if (lpBox.width <= 0 || lpBox.height <= 0) continue;
alignedLPRBatch.emplace_back(veh(lpBox));
metas.push_back({ v, lp });
}
}
if (alignedLPRBatch.empty()) return {};
// ── 4. ONE batched char-OCR call across every plate ──────
std::vector<std::string> ocrTextBatch =
DetectLicensePlateStringBatch(alignedLPRBatch, cameraId);
if (ocrTextBatch.size() != alignedLPRBatch.size()) {
this->_logger.LogWarn("ANSALPR_OD::RunInferencesBatch",
"Char OCR batch size mismatch", __FILE__, __LINE__);
return {};
}
// ── 5. Assemble — NO tracker, NO voting, NO dedup ────────
std::vector<Object> output;
output.reserve(alignedLPRBatch.size());
for (size_t i = 0; i < alignedLPRBatch.size(); ++i) {
const std::string& text = ocrTextBatch[i];
if (text.empty()) continue;
Object out = metas[i].lpObj;
out.className = text; // raw OCR — no ALPRChecker
out.cameraId = cameraId;
out.box.x += clamped[metas[i].vehIdx].x;
out.box.y += clamped[metas[i].vehIdx].y;
// Colour lookup — uses text-keyed cache, bounded by
// COLOUR_CACHE_MAX_SIZE, no per-frame growth.
std::string colour = DetectLPColourCached(
alignedLPRBatch[i], cameraId, out.className);
if (!colour.empty()) out.extraInfo = "color:" + colour;
output.push_back(std::move(out));
}
return output;
}
catch (const cv::Exception& e) {
this->_logger.LogFatal("ANSALPR_OD::RunInferencesBatch",
std::string("OpenCV Exception: ") + e.what(), __FILE__, __LINE__);
}
catch (const std::exception& e) {
this->_logger.LogFatal("ANSALPR_OD::RunInferencesBatch",
e.what(), __FILE__, __LINE__);
}
catch (...) {
this->_logger.LogFatal("ANSALPR_OD::RunInferencesBatch",
"Unknown exception occurred", __FILE__, __LINE__);
}
return {};
}
std::vector<std::string> ANSALPR_OD::DetectLPColourDetectorBatch(const std::vector<cv::Mat>& lprROIs, const std::string& cameraId) {
// Early validation - no lock needed for immutable config
if (_lpColourModelConfig.detectionScoreThreshold <= 0.0f || !_lpColourDetector) {