Fix ALPR Batch and memory leak
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user