#ifndef ANSLPROCR_H #define ANSLPROCR_H #pragma once #include "ANSLPR.h" #include #include #include #include #include #include #include // Forward-declare ANSONNXOCR to avoid pulling in the full ANSOCR header chain namespace ANSCENTER { class ANSONNXOCR; struct OCRModelConfig; } namespace ANSCENTER { /// ANSALPR_OCR — License plate recognition using YOLO for LP detection /// and ANSONNXOCR (PaddleOCR v5) for text recognition. /// /// Pipeline: /// 1. Detect license plates using _lpDetector (ANSRTYOLO on NVIDIA GPU, ANSONNXYOLO otherwise) /// 2. For each detected plate, run OCR using _ocrEngine (ANSONNXOCR) /// 3. Optionally classify plate colour using _lpColourDetector (ANSRTYOLO on NVIDIA GPU, ANSONNXYOLO otherwise) /// /// Supports multiple countries via the Country enum and ALPR post-processing /// from ANSOCR's ANSOCRBase infrastructure. class ANSLPR_API ANSALPR_OCR : public ANSALPR { private: ANSCENTER::EngineType engineType; // --- Detectors --- std::unique_ptr _lpDetector = nullptr; // License plate detector std::unique_ptr _lpColourDetector = nullptr; // License plate colour classifier std::unique_ptr _ocrEngine = nullptr; // OCR text recognizer // --- Model configs --- ANSCENTER::ModelConfig _lpdmodelConfig; ANSCENTER::ModelConfig _lpColourModelConfig; std::string _lpdLabels; std::string _lpColourLabels; cv::Mat _frameBuffer; // Reusable buffer for color conversion std::vector _lprModelClass; ALPRChecker alprChecker; // ---------------------------------------------------------------- // Full-frame vs pipeline auto-detection (ported from ANSALPR_OD) // // When the caller feeds ANSLPR_OCR pre-cropped vehicle ROIs (each // frame is a different small image), the tracker can't work — the // LP detector sees a totally new image every call so trackIds mean // nothing. In that "pipeline" mode we must skip the ALPRChecker // voting layer entirely and return raw OCR results. // // When the caller feeds full-frame video (same resolution every // frame, plates moving through the scene), the tracker works // normally and we run plate text through ALPRChecker majority // voting + spatial dedup to stabilise readings. // // Mode is auto-detected by watching whether consecutive frames // share the exact same (width, height) for at least // CONFIRM_THRESHOLD frames. Pipeline crops vary by a few pixels; // full-frame video is pixel-identical. // ---------------------------------------------------------------- struct ImageSizeTracker { cv::Size lastSize{ 0, 0 }; int consistentCount = 0; bool detectedFullFrame = false; static constexpr int CONFIRM_THRESHOLD = 5; static constexpr int MIN_FULLFRAME_WIDTH = 1000; }; std::unordered_map _imageSizeTrackers; [[nodiscard]] bool shouldUseALPRChecker(const cv::Size& imageSize, const std::string& cameraId); // ---------------------------------------------------------------- // Spatial plate identity persistence (ported from ANSALPR_OD) // // Prevents the same plate string from appearing on two different // vehicles in the same frame. The LP tracker may briefly assign // the same trackId to two different plates when vehicles pass // each other, or two different trackIds to the same plate when // occlusion breaks a track. In either case, OCR can produce the // same text for two spatial locations for a frame or two — which // looks like "plate flicker" in the UI. // // ensureUniquePlateText() resolves the ambiguity by accumulating // confidence per spatial location. When two detections share a // plate text, the one whose spatial history has the higher score // wins and the other has its className cleared. // ---------------------------------------------------------------- struct SpatialPlateIdentity { cv::Point2f center; // plate center in frame coords std::string plateText; float accumulatedScore = 0.0f; int framesSinceLastSeen = 0; }; std::mutex _plateIdentitiesMutex; std::unordered_map> _plateIdentities; static constexpr float PLATE_SPATIAL_MATCH_THRESHOLD = 0.3f; // IoU threshold void ensureUniquePlateText(std::vector& results, const std::string& cameraId); // --- Original model zip path (reused for ANSONNXOCR initialization) --- std::string _modelZipFilePath; // --- 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); // LPC colour cache struct ColourCacheEntry { std::string colour; int hitCount = 0; }; std::mutex _colourCacheMutex; std::unordered_map _colourCache; static constexpr size_t COLOUR_CACHE_MAX_SIZE = 200; // --- OCR helper --- [[nodiscard]] std::string RunOCROnPlate(const cv::Mat& plateROI, const std::string& cameraId); public: ANSALPR_OCR(); ~ANSALPR_OCR(); [[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colourThreshold) override; [[nodiscard]] bool LoadEngine() override; [[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult) override; [[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult, const std::string& cameraId) override; [[nodiscard]] bool Inference(const cv::Mat& input, const std::vector& Bbox, std::string& lprResult) override; [[nodiscard]] bool Inference(const cv::Mat& input, const std::vector& Bbox, std::string& lprResult, const std::string& cameraId) override; [[nodiscard]] std::vector RunInference(const cv::Mat& input, const std::string& cameraId) override; [[nodiscard]] std::vector RunInferencesBatch( const cv::Mat& input, const std::vector& vehicleBoxes, const std::string& cameraId) override; [[nodiscard]] bool Destroy() override; /// Propagate country to inner OCR engine so ALPR post-processing /// uses the correct plate formats and character corrections. void SetCountry(Country country) override; /// Propagate debug flag to all sub-detectors void ActivateDebugger(bool debugFlag) override { _debugFlag = debugFlag; if (_lpDetector) _lpDetector->ActivateDebugger(debugFlag); if (_lpColourDetector) _lpColourDetector->ActivateDebugger(debugFlag); } }; } #endif