License Plate Recognition (ANPR/LPR) Implementation

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
License Plate Recognition (ANPR/LPR) Implementation
Medium
~3-5 business days
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822

Реализация распознавания номерных знаков автомобилей (ANPR/LPR)

ANPR (Automatic Number Plate Recognition) / LPR (License Plate Recognition) — система автоматического считывания государственных регистрационных знаков. Applications: контроль въезда на парковку, контроль скорости, система «Безопасный город», таможенный контроль, розыск угнанных автомобилей.

Architecture двухэтапного пайплайна

Видео/Фото → Детекция автомобиля → Детекция номерного знака → OCR → База данных

Двухэтапный подход (авто → номер) точнее однозтапного, так как позволяет обрабатывать разные форматы номеров из разных стран.

from ultralytics import YOLO
from paddleocr import PaddleOCR
import cv2
import numpy as np
import re

class ANPRSystem:
    def __init__(self,
                 vehicle_model: str = 'yolov8l.pt',
                 plate_model: str = 'plate_detector.pt'):
        self.vehicle_detector = YOLO(vehicle_model)
        self.plate_detector = YOLO(plate_model)  # дообученный на номерах
        self.ocr = PaddleOCR(
            use_angle_cls=True,
            lang='en',
            rec_algorithm='SVTR_LCNet'
        )

    def process(self, frame: np.ndarray) -> list[dict]:
        # Детекция транспортных средств
        vehicles = self.vehicle_detector(frame, classes=[2, 3, 5, 7],  # car/moto/bus/truck
                                          conf=0.5)
        results = []

        for vehicle_box in vehicles[0].boxes.xyxy:
            x1, y1, x2, y2 = map(int, vehicle_box)
            vehicle_crop = frame[y1:y2, x1:x2]

            # Детекция номерного знака в кропе автомобиля
            plates = self.plate_detector(vehicle_crop, conf=0.5)

            for plate_box in plates[0].boxes.xyxy:
                px1, py1, px2, py2 = map(int, plate_box)
                plate_crop = vehicle_crop[py1:py2, px1:px2]

                # OCR номера
                plate_text = self._recognize_plate(plate_crop)

                if plate_text:
                    results.append({
                        'plate': plate_text,
                        'vehicle_bbox': [x1, y1, x2, y2],
                        'plate_bbox': [x1+px1, y1+py1, x1+px2, y1+py2],
                        'confidence': float(plates[0].boxes.conf[0])
                    })

        return results

    def _recognize_plate(self, plate_img: np.ndarray) -> str | None:
        # Предобработка
        plate_img = self._preprocess_plate(plate_img)

        result = self.ocr.ocr(plate_img, cls=False)
        if not result or not result[0]:
            return None

        text = ''.join([line[1][0] for line in result[0]])
        text = re.sub(r'[^A-Z0-9А-Я]', '', text.upper())

        # Валидация формата российского номера
        if re.match(r'^[АВЕКМНОРСТУХ]\d{3}[АВЕКМНОРСТУХ]{2}\d{2,3}$', text):
            return text

        return text if len(text) >= 6 else None

Предобработка изображения номера

def _preprocess_plate(self, image: np.ndarray) -> np.ndarray:
    # Масштабирование до стандартной высоты
    target_h = 64
    scale = target_h / image.shape[0]
    new_w = int(image.shape[1] * scale)
    image = cv2.resize(image, (new_w, target_h), interpolation=cv2.INTER_CUBIC)

    # Конвертация в оттенки серого
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Нормализация яркости
    normalized = cv2.normalize(gray, None, 0, 255, cv2.NORM_MINMAX)

    return normalized

Работа с разными форматами номеров

Российские номера: X000XX00[0] (стандарт), X000XX000 (транзитные). Дополнительные форматы: таможенные, дипломатические, военные.

Для международных систем — мультиязычная OCR + несколько наборов регулярных выражений валидации.

Performance в продакшене

Метрика Значение
Accuracy (хорошее освещение, < 80 км/ч) 96–99%
Accuracy (ночь, ИК-подсветка) 92–96%
Accuracy (высокая скорость, 120+ км/ч) 80–88%
Latency (T4 GPU, 1080p кадр) 35–50 ms
False positive rate < 2%

Хранение и поиск по базе

Для систем контроля въезда: Redis для горячего списка (whitelist/blacklist), PostgreSQL с pg_trgm для нечёткого поиска (учитывает ошибки OCR 0/O, I/1, B/8).

Масштаб системы Срок
1–4 камеры, парковочный контроль 3–5 недель
8–16 камер, городская система 6–10 недель
50+ камер, распределённая инфраструктура 12–18 недель