THE RULES BEHIND THE SOFTWARE
License Plate Recognition
A computer-vision pipeline that follows vehicles from motion detection to plate recognition and operator playback.
Find candidates
Keep an identity
Crop → OCR
Compare readings
Code-native explanation of the implementation. Illustrative data.
- CONTEXT
- University capstone
- MY ROLE
- Developer · computer vision & pipeline integration
- TIMELINE
- Nov 2024 - Jul 2025
- EVIDENCE
- Source reviewed · illustrative diagrams
AT A GLANCE
One frame, four decisions
Movement becomes a tracked vehicle, a cropped plate and a refined reading. OpenCV, YOLO and PaddleOCR connect through Python services, Redis queues and a desktop viewer, so an operator can inspect the recognition path.
My contribution +
My development work covered motion detection, data preparation, model training and fine-tuning, and recognition-pipeline integration.
WHAT THE SYSTEM HANDLES
The interesting parts.
Find the movement. Keep the identity.
Optical flow, vehicle detection and centroid tracking turn consecutive frames into persistent vehicle tracks.
Implementation details
- Farneback flow with magnitude/area filtering
- Vehicle detections filtered by motion overlap and class
- Centroid association and disappearance handling
Give each plate a second look.
Associate a plate with its vehicle, prepare the crop for OCR, then retain stronger repeated readings.
Implementation details
- Spatial plate-to-vehicle association
- CPU PaddleOCR with image preprocessing
- Format checks and look-alike character correction
- Repeated equal-or-better-confidence readings mark a track stable
Let the stages run at different speeds.
Video acquisition, edge inference, OCR and desktop display communicate through queues and HTTP.
Inspect the whole recognition path.
The repository preserves annotated frames and evaluation scripts for component detections and complete plate results.
Implementation details
- Ground-truth bounding-box annotations
- Precision/recall/F1 computation
- Exact and explicitly thresholded partial plate matching
STRUCTURE & JUDGMENT
How the parts fit.
File / RTSP input
User-supplied video; OpenCV acquisition.
→ Video serviceVideo service
FastAPI controls and nominal4fps frame acceptance.
→ Redis frame_queueRedis frame_queue
Pickled frames; queue itself has no configured bound.
→ Edge worker queueEdge worker queue
30 in-process slots and four workers.
→ Motion + vehicle tracksMotion + vehicle tracks
Farneback motion, YOLO vehicle filtering, centroid matching.
→ Plate associationPlate association
YOLO plate candidates aligned to tracked vehicles.
→ OCR serviceOCR service
JPEG crop over HTTP → preprocessing → PaddleOCR → format validation.
→ Recognition stateRecognition state
Equal-or-higher-confidence text and repeated observations.
→ Redis visual_frame_queueRedis visual_frame_queue
Annotated800×600 output frames.
→ PyQt viewerPyQt viewer
Playback controls and annotated video display.
→ Video service- Python
- OpenCV / Farneback optical flow
- Ultralytics YOLO11
- PaddleOCR2.10.0 / PaddlePaddle2.6.2
- NumPy / SciPy
- FastAPI
- Redis
- Docker Compose
- PyQt5
Use motion as a gate before new vehicle inference.
Preserve vehicle identity across frames.
Centroid tracking lets repeated plate crops contribute to one vehicle record instead of treating each frame independently.
WHY IT MATTERSDistance/disappearance thresholds can confuse occlusion or nearby vehicles; no tracking-quality benchmark is published.
Split inference and display into service boundaries.
Acquisition, edge processing, OCR and desktop rendering have different costs and control needs.
WHY IT MATTERSSerialization, queue backlog and synchronous OCR requests introduce extra failure and latency points; no end-to-end latency results were measured.
Check likely plate formats after OCR.
Pattern checks and look-alike substitutions reject or correct some raw OCR strings.
WHY IT MATTERSA valid format is not proof of the correct plate; generic patterns and correction order can bias ambiguous results.
Expose operator controls around the pipeline.
Scope, limitations & what I would revisit
- No runtime or accuracy claim from this audit. Source-only review; models were not loaded, services were not started and evaluation scripts were not executed. No source-published model/end-to-end accuracy result found.
- Unauthorized-vehicle alerts are not wired into the current service path. The current edge endpoint always calls visualize(frame, True). List authorization exists in a stale legacy path. Describe the current system as plate recognition and operator visualization, with authorization as earlier prototype logic.
- ROI, direction classification and fusion helpers are historical/experimental. RegionAdjuster is unused by current prediction; image fusion, multiple-threshold preprocessing and weighted majority voting are defined but not called by the current OCR path.
- Four workers share sequential model/tracker state. All workers receive one EdgeService; its prev_frame and tracker state can be touched across frames. Tracker methods lock portions of state, but EdgeService.lock is not used around prediction. Ordering and concurrency behavior need runtime evaluation.
- Queue and network behavior are prototype-grade. The Python queue is bounded, but Redis input/output lists are not; the poller busy-loops and OCR HTTP calls have no timeout. Do not describe the complete system as fully bounded or fault tolerant.
- Startup clears Redis globally. Both video startup and the new-architecture tester call flushall. Any future local execution must use an isolated Redis container and ports.
- Evaluation contracts need reconciliation. New tester expects tracked_plates, but no active production caller invokes log_results; older tests import missing classes or call superseded signatures. Syntax parsing passed but does not prove tests pass.
- Videos and reproducible training history are absent. Full videos are ignored, model weights are tracked, and this revision has no training script/configuration or held-out benchmark report. My development, data preparation and training work are described above; this source snapshot does not provide a reproducible training record.
- Desktop paths and container mounts differ. A path picked on Windows is sent directly to the service, while Compose mounts only ./recordings at /app/recordings. A future run must map/copy only authorized local fixtures.