The two-stage approach is the only reliable way to build license plate recognition. First, use YOLO to detect the plate region in the image. Second, crop that region and run OCR on it. Trying to OCR the whole image or using single-stage models gives terrible accuracy.
Here’s the complete pipeline with real Python code you can run today.
The Two-Stage Pipeline
Stage 1 is detection. YOLO finds the bounding box coordinates of license plates in your image or video frame. This isolates the region of interest and eliminates background noise that confuses OCR.
Stage 2 is recognition. EasyOCR or PaddleOCR reads the text from the cropped plate region. You’ll get better results by preprocessing the crop first—convert to grayscale, apply contrast enhancement, and resize to a consistent height.
Use YOLOv8 for detection. It’s fast enough for real-time processing and you can fine-tune it on license plate datasets like CCPD or OpenALPR. For OCR, EasyOCR works out of the box for most plate formats, but PaddleOCR is faster if you need to process video streams at high FPS.
Install Dependencies
| |
If you want PaddleOCR instead (faster for video), install it separately:
| |
Detection with YOLO
Train YOLOv8 on a license plate dataset or use a pretrained model. Here’s how to detect plates and crop them:
| |
The preprocessing steps matter more than you’d think. Histogram equalization fixes uneven lighting, and resizing to 2x the original height gives OCR models more pixels to work with. Skip this and you’ll get garbage like “4BC 7O3” instead of “ABC 703”.
Real-Time Video Processing
For video streams, you need to balance accuracy and FPS. Process every 3rd frame to maintain 10+ FPS on a decent GPU:
| |
The process_every_n_frames parameter is critical. Processing every frame kills your FPS and doesn’t improve accuracy—plates don’t change between consecutive frames. Process every 3rd frame and you’ll hit 15-20 FPS on a GTX 1660 or better.
Handling Different Plate Formats
Different countries use different plate formats. US plates are typically 2-3 letters followed by 3-4 numbers (e.g., ABC 1234). European plates start with country codes. Chinese plates have province characters.
You need format validation to filter out false positives. Here’s a regex-based validator:
| |
Add multiple format patterns and try them all. If a plate matches any valid format, keep it. This simple check eliminates 90% of OCR garbage.
Common Errors and Fixes
“RuntimeError: CUDA out of memory”
Your GPU doesn’t have enough VRAM for the YOLO model and OCR together. Use gpu=False in easyocr.Reader(['en'], gpu=False) to run OCR on CPU, or switch to a smaller YOLO model like yolov8n.pt.
OCR returns empty results The plate crop is too small or too blurry. Check that your YOLO model is accurately detecting plates—visualize the bounding boxes. If crops are tiny (less than 50x20 pixels), increase the image resolution or retrain YOLO with smaller plate examples.
Confusing O/0, I/1, S/5 This is the classic OCR problem. Use post-processing rules based on plate format. For example, US plates usually alternate letters and numbers (ABC 123), so you can infer whether a character should be O or 0 based on position.
Low FPS on video
You’re processing every frame or using CPU. Drop process_every_n_frames to 5 or higher, use a GPU, and switch from EasyOCR to PaddleOCR. PaddleOCR is 2-3x faster for real-time applications.
Plates not detected in low light YOLO struggles with underexposed images. Apply gamma correction to the input frame before detection:
| |
High false positive rate
Lower the YOLO confidence threshold (conf=0.6 instead of 0.4) and add format validation. Most false positives are random text that doesn’t match plate patterns.
Related Guides
- How to Build Real-Time Face Detection with MediaPipe and Python
- How to Build a Color Detection and Extraction Pipeline with OpenCV
- How to Build a Video Object Removal Pipeline with Inpainting
- How to Build a Barcode and QR Code Scanner with OpenCV and ZBar
- How to Build a Document Table Extraction Pipeline with Vision Models
- How to Build a Panoramic Image Stitching Pipeline with OpenCV
- How to Build Document Scanner with OpenCV and Perspective Transform
- How to Build Video Action Recognition with SlowFast and PyTorch
- How to Build a Lane Detection Pipeline with OpenCV and YOLO
- How to Build a Receipt Scanner with OCR and Structured Extraction