Turkish license plate detection and recognition system designed to prevent incorrect fuel filling at gas stations. Field sales staff use this system to automatically read a vehicle's license plate, look up its registered fuel type from the database, and ensure the correct fuel is dispensed.
Vehicles are also registered with their brand, model, and color — because plates can be swapped between vehicles, these additional identifiers help verify that the right car is being served.
Powered by a custom-trained YOLOv26n model for plate detection and GLM-OCR (via Ollama) for plate text reading.
| Plate Not Detected (Manual Entry) | New Vehicle Registration | Known Vehicle |
|---|---|---|
![]() |
![]() |
![]() |
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────┐ ┌────────┐
│ Frontend │────▶│ Backend │────▶│ YOLO Service │ │ LiteLLM │────▶│ Ollama │
│ React+Nginx │◀────│ FastAPI │────▶│ Detection │ │ Proxy │ │ LLM │
└─────────────┘ │ │ └──────────────┘ └─────────┘ └────────┘
│ │────▶┌──────────────┐────▶ │
│ │ │ OCR Service │───────────┘
│ │ │ Recognition │
└──────┬───────┘ └──────────────┘
│
┌──────▼───────┐
│ PostgreSQL │
│ Database │
└──────────────┘
| Service | Tech | Port | Description |
|---|---|---|---|
| Frontend | React + Vite + Nginx | 3001 | Upload UI, vehicle info cards, scan history |
| Backend | FastAPI + SQLAlchemy | 8000 | REST API, business logic, database operations |
| YOLO Service | FastAPI + Ultralytics | 8001 | License plate detection with custom YOLOv26n model |
| OCR Service | FastAPI + httpx | 8002 | Plate text recognition via GLM-OCR vision model |
| LiteLLM | LiteLLM Proxy | 4000 | AI gateway proxying requests to Ollama (GLM-OCR) |
| PostgreSQL | PostgreSQL 16 | 5432 | Vehicle and recognition log storage |
| LiteLLM DB | PostgreSQL 16 | 5433 | LiteLLM internal storage |
Image Upload → YOLO Detection → Crop Best Plate → OCR (Vision LLM) → DB Lookup → Response
- User uploads a vehicle image
- YOLO Service detects plate regions and returns cropped plate images with confidence scores
- OCR Service sends the cropped plate to GLM-OCR (running on Ollama) via LiteLLM, sanitizes the output, and validates against Turkish plate format
- Backend normalizes the plate text, looks up the vehicle database, logs the recognition, and returns the result
- If the vehicle is not registered, the user can register it with fuel type, brand, model, and color
- If detection fails, the user can manually enter vehicle information
The plate detection model was trained using a Turkish license plate dataset:
- Base Model: YOLOv26n
- Dataset: Turkish License Plate Dataset from Kaggle
- Split: Train / Validation (80/20)
- Epochs: 150 (with early stopping, patience=30)
- Image Size: 640x640
- Augmentation: HSV, translate, scale, horizontal flip, mosaic
- Platform: Google Colab (GPU)
- Output:
best.pt— best performing weights used in production
The training script is available at train_yolo.py.
Backend: Python 3.11, FastAPI, SQLAlchemy, Pydantic, httpx Frontend: React 18, Vite, Nginx AI/ML: YOLOv26n (Ultralytics), GLM-OCR (Ollama), LiteLLM Database: PostgreSQL 16 Infrastructure: Docker, Docker Compose
- Docker & Docker Compose
- Ollama running on the host with a vision model (e.g.,
glm-ocr) - Trained YOLO model (
best.pt) placed inyolo-service/models/
-
Clone the repository:
git clone https://github.com/<your-username>/plate-recognition-system.git cd plate-recognition-system
-
Create
.envfrom the example:cp .env.example .env
-
Edit
.envand set yourLITELLM_MASTER_KEYand other values as needed. -
Place your trained YOLO model:
cp /path/to/best.pt yolo-service/models/best.pt
-
Make sure Ollama is running with your vision model:
ollama run glm-ocr
-
Start all services:
docker compose up -d --build
-
Open http://localhost:3001 in your browser.
See .env.example for all available configuration options including:
- Database credentials
- LiteLLM API key and model settings
- YOLO confidence threshold
- OCR confidence thresholds
- Service timeouts
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/recognize |
Upload image for plate recognition |
GET |
/api/vehicles |
List registered vehicles (paginated) |
POST |
/api/vehicles |
Register a new vehicle |
PUT |
/api/vehicles/{id} |
Update vehicle information |
GET |
/api/logs |
List recognition logs (paginated) |
PATCH |
/api/logs/{id}/confirm |
Confirm/correct a detected plate |
GET |
/health |
Service health check |
# Backend tests
cd backend && pip install -r requirements.txt && python -m pytest tests/ -v
# OCR service tests
cd ocr-service && pip install -r requirements.txt && python -m pytest tests/ -v
# YOLO service tests
cd yolo-service && pip install -r requirements.txt && python -m pytest tests/ -vplate-recognition-system/
├── backend/ # Main API service
│ ├── app/
│ │ ├── routes/ # API endpoints (recognize, vehicles, logs, feed)
│ │ ├── config.py # Centralized configuration
│ │ ├── database.py # Database connection pool
│ │ ├── models.py # SQLAlchemy models (Vehicle, RecognitionLog)
│ │ ├── schemas.py # Pydantic request/response schemas
│ │ ├── services.py # Business logic (recognition pipeline)
│ │ └── plate_utils.py # Plate normalization and validation
│ └── tests/
├── frontend/ # React UI
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── styles/ # CSS styles
│ │ ├── api.js # API client
│ │ └── App.jsx # Main application
│ └── nginx.conf # Nginx reverse proxy config
├── yolo-service/ # Plate detection microservice
│ ├── app/
│ │ ├── detector.py # YOLO inference
│ │ └── config.py # Detection settings
│ └── tests/
├── ocr-service/ # Plate OCR microservice
│ ├── app/
│ │ ├── recognizer.py # LLM-based plate reading + sanitization
│ │ └── config.py # OCR settings and confidence thresholds
│ └── tests/
├── docker-compose.yml # Multi-service orchestration
├── litellm_config.yaml # LiteLLM proxy configuration
└── .env.example # Environment variable template
This project is actively being developed. Planned improvements:
- Dataset Augmentation: The current model struggles with plates positioned on the far right or left of the frame. Data augmentation techniques (rotation, shifting, perspective transforms) will be applied to improve detection accuracy in edge cases.
- Training Improvements: More epochs, hyperparameter tuning, and additional Turkish plate datasets to improve model robustness.
- International Plate Support: Extend the system to recognize plates from other countries (EU, US, etc.) as a next major milestone.
- Live Camera Feed: Real-time plate recognition from IP cameras or webcam streams.
MIT


