ANPR Uzbekistan — распознаватель автомобильных номеров
EN: Scene-text recognition model for Uzbek license plates. TPS-ResNet-BiLSTM-CTC, trained from scratch on a hand-cleaned dataset. 90% exact-match on an independent sample of 80 real photos. All numbers below are measured, reproducible with one command, and include negative results.
Распознаватель текста автомобильных номеров Узбекистана. Обучен автором проекта на собственноручно собранном и вычищенном датасете.
- Код и сервис вокруг модели: https://github.com/IzzatulloOne/plate-recognition-uz
- Попробовать без установки: ноутбук Colab в README репозитория
Архитектура
Вход 32×100, оттенки серого
Transformation TPS_SpatialTransformerNetwork, F=20
FeatureExtraction ResNet (DTRB), 1 канал → 512
SequenceModeling 2 × BidirectionalLSTM(hidden=256)
Prediction две CTC-головы, Linear(256 → 95)
head_anpr — текст номера
head_ctype — вспомогательный код
95 классов = 1 blank (CTC) + 94 символа (string.printable[:94]).
Анализ bias и норм строк выходных слоёв показывает фактически используемые классы:
head_anpr— цифры и A–Z, кромеI; строчные и пунктуация мертвыhead_ctype— цифры,H,M
Для чего предназначена
Однострочные номера Узбекистана:
| Формат | Пример | Тип |
|---|---|---|
DD L DDD LL |
01 A 123 BC |
частные |
DD DDD LLL |
01 123 ABC |
юрлица |
DD M DDDDDD |
01 M 018461 |
электромобили |
DD H DDDDDD |
01 H 018461 |
иностранные граждане |
Вне области применения: двухстрочные квадратные номера прицепов и спецтехники — модель их не читает (0 из 4 на выборке). Это ограничение чекпоинта.
Измеренная точность
Выборка собрана автоматически из галереи platesmania.com/uz: сайт отдаёт вместе с фото «информер» с точным текстом номера, поэтому эталон получен без ручной разметки. В обучение эти фотографии не входили.
Белые номера, 80 фото
| Метрика | Результат |
|---|---|
| greedy-декодирование как есть | 71/80 = 88.8% |
| после правил формата | 72/80 = 90.0% |
| формат опознан | 77/80 = 96.2% |
По классам:
| Класс | Результат |
|---|---|
| юрлица | 11/11 = 100% |
| электромобили | 1/1 = 100% |
| частные | 60/64 = 93.8% |
| прицепы (двухстрочные) | 0/4 = 0% |
Если исключить прицепы как выходящие за область применения — 72/76 = 94.7%.
Цветные номера, 39 фото (19 жёлтых, 20 зелёных)
| Метрика | Результат |
|---|---|
| greedy как есть | 20/39 = 51.3% |
| после правил формата | 30/39 = 76.9% |
| зелёные (электромобили) | 16/20 = 80.0% |
| жёлтые (иностранные) | 14/19 = 73.7% |
Разрыв между 51.3% и 76.9% — вклад слоя правил формата. На цветных номерах
шесть цифр подряд, и модель систематически ставит там букву вместо цифры
(A вместо 1, O вместо 0); жёсткий формат эти подмены чинит.
Известные ограничения
Двухстрочные номера не читаются — 0 из 4. Ограничение чекпоинта, препроцессингом не обходится: включение режима разбиения на строки стоило 184 мс против 97 и не дало ни одного верного чтения.
На мелких кропах модель ошибается уверенно. Измерено: пластина 58×28 пикселей распозналась неверно с уверенностью 0.96 и в формально правильном формате — то есть ответ выглядел легитимным. Правильного ответа не было ни в одной beam-альтернативе ни при одном варианте препроцесса; текст не смог прочитать и человек. В сервисе вокруг модели стоит порог по ширине пластины: ниже него система не отвечает вовсе.
Правила формата не ловят подмену одного допустимого символа другим. Примеры из
выборки: 30A072NT → 80A072NT (3→8) и 10Y757YY → 10Y757XY (Y→X). Обе ошибочные
строки — валидные узбекские номера, зацепиться правилам не за что.
Ночная съёмка со вспышкой бьёт по коробке региона. Пластина световозвращающая,
вспышка даёт блики, и отдельная рамка с регионом страдает сильнее основной части:
на разобранном снимке там было 3.9% пересвеченных пикселей против 0.5% в остальной
пластине, регион 01 прочитался как 80, остальные шесть символов верно. Причина
не доказана — это наблюдение на одном кадре; исключены разрешение (на оригинале
273 px промах тот же), препроцесс (все четыре варианта), beam-альтернативы
и вторая голова.
Перекос обучающих данных. В исходной галерее распределение такое:
частные + юрлица 65 935 90.7%
иностранные 2 020 2.8%
прицепы 4 216 5.8%
Отсюда разрыв между белыми (88.8% greedy) и цветными (51.3% greedy): модель видела почти только белые.
Отрицательные результаты
Проверено и отброшено — чтобы не повторять:
| Что пробовал | Результат |
|---|---|
| Мультикроп | 72 → 72, без изменений |
| Вертикальная обрезка кропа | +1 из 80 — шум, не взято |
Перебор crop_padding |
0.06 уже оптимален |
| Режим двух строк | 184 мс против 97, 0 верных чтений |
Вывод head_ctype в кандидаты для цветных |
+1 из 39, на жёлтых ровно 0 — гипотеза не подтвердилась |
head_ctype для чтения региона |
90.1% против 97.3% у основной головы на 111 номерах |
| Поиск систематического перекоса в подменах символов | нет: 4 подмены на 119 фото, все разные |
Последняя строка про вторую голову: её алфавит (цифры, H, M) совпадает с алфавитом
цветных номеров, и была гипотеза, что её вывод поможет. Набрать выборку оказалось трудно —
цветных в галерее около 3%, пришлось просмотреть 300 страниц ради 39 номеров. Прирост
в пределах шума.
Как использовать
Модель — часть пайплайна: детекция пластины (YOLO11) → кроп → распознавание → правила формата. Отдельно от обвязки она читает уже вырезанную пластину.
from huggingface_hub import hf_hub_download
hf_hub_download(
repo_id="IzzatulloOne/anpr-uz",
filename="best_accuracy.pth",
local_dir=".",
)
Дальше — через код сервиса:
git clone https://github.com/IzzatulloOne/plate-recognition-uz
cd plate-recognition-uz
pip install -e .
python -m tools.fetch_yolo # веса детектора
python -m tools.probe_model crop.jpg --chars
Воспроизвести замер точности:
python -m tools.eval_uz --fetch 80 --run
Обучающие данные
Фотографии из галереи platesmania.com/uz, разметка получена из текста номера, который сайт публикует рядом с каждым фото. Датасет чистился вручную.
Выборки для замера (80 белых и 39 цветных) в обучение не входили.
Лицензия
CC BY-NC 4.0 — использование с указанием авторства, некоммерческое.
Код сервиса вокруг модели — MIT.