Предлагается рассмотреть рабочий концепт устройства, которое делает фото (не без помощи человека естественно), распознает vin номер авто на фото, проверяет номер по базе ГИБДД РФ, самостоятельно посещая сайт. Никаких взломов, бэкдоров и т.п., только законные действия.



Предыстория




Идея проверить авто, которое припарковано во дворе и выглядит крайне подозрительно, витала в воздухе достаточно давно. Но что-то постоянно останавливало это реализовать: то ли то, что для этого нужно было заходить на сайт ГИБДД и вводить кучу информации, когда под рукой нет телефона, то ли тот факт, что рег. номер машины может быть совсем не тот, за который себя выдает. По этому было решено собрать что-то не слишком сложное, что могло бы по фото vin (номер обычно виден под стеклом авто) собрать минимальную информацию: не в угоне ли, есть ли штрафы, было ли в ремонте. Всю эту информацию вполне официально можно получить, зайдя на сайт ГИБДД. Но хотелось, так сказать, автоматизации.

Такой подход, на первый взгляд, для простого смертного, конечно, избыточен. Тем более, что изначально казалось, что «служители ЦОДД» (служба парковки-эвакуации в г. Москве) уже имеют что-то подобное в своих планшетах, с которыми перемещаются и фотографируют авто. Однако, выяснилось, что «парковщики» фотографируют только рег. номера и судьба машины, если только она не неправильно припаркована, им не интересна.

Первый блин комом




Первая версия устройства на raspberry pi zero показала себя хорошо, но медленно. Так как предполагалось заходить на сайт ГИБДД c raspberry через selenium python (api у гибдд.рф нет, requests-for-humans также глохнут), то все работало крайне неспеша. Даже с небольшими ускорениями, которые позволялось сделать через /boot/config.txt устройства, чтобы его разогнать. Кроме того, отсутствие встроенного wifi на плате, добавляло необходимость usb свистка, подключаемого к raspberry pi zero замысловато.

Тем не менее были часы, touchscreen, небольшой блок питания.

Пришлось оставить raspberry pi zero в покое, оставив ее для проектов попроще.

Второй блин комом


Недолго думая, решено было «пересесть» на raspberry pi zero 2w (кстати, она на фото выше): тут и железо помощнее, и wifi есть, и все те же небольшие размеры.

Selenium python на zero 2w стал работать резвее, но сайт ГИБДД добавило рекламу в общение с пользователем при посещении сайта, пришлось это учесть, чтобы программа не вылетала.

И вот, в тот самым момент, когда стало ясно, что все работает: программа заходит на сайт ГИБДД, терпеливо переваривает вынужденные рекламные ролики, запрашивает и получает информацию, выяснилась одна немаловажная деталь.

Изначально предполагалось, что на сайт будет улетать уже распознанный vin номер. То есть vin номер в символьном представлении (или строка, как угодно). А вот как эти символы будут получены с фото, было абсолютно не важно. В этом не виделось никакой проблемы. А зря.

Первые же фото в «поле» показали, что все далеко от идеала. Фото, мало того что искажено, так ему еще и мешают блики-отражения лобового стекла авто!

Все попытки очистить фото фильтрами opencv, подружить с tesseract (пакет распознавания) были безуспешны.

Немного спасло ситуацию в деле распознавания с фото применение таких пакетов как easy-ocr и craft, но и они не выдавали идеальный результат, ошибаясь в нескольких символах, что было неприемлемо.

Проект был законсервирован до лучших времен.

Третий блин. Не чайник, но самовар


Прошло 2 года. На Хабре всплывали интересные статьи на тему, как убрать блики на изображении. В одной из них автор плавал по озеру и затем накладывал фильтры на картинку, убирая блики с воды, вторая разработка касалась способа убрать блики на витринах магазина, сопоставляя два фото (с бликами и без). Оба варианта не прижились, несмотря на значительность усилий по их внедрению.

За это время сайт ГИБДД «подкинул дополнительных проблем» пользователю добавив капчу. Чтобы все работало хотя бы в этой части так называемого frontendа, пришлось «рисовать» еще и нейросеть по распознаванию уже символьной капчи сайта.



И вот, в один прекрасный день, появились они — трансформеры (transformers). И их вклад в продвижение проекта сложно переоценить.

Собрав код воедино, проект заиграл новыми красками:

from transformers import TrOCRProcessor, VisionEncoderDecoderModel
from PIL import Image
from functools import wraps
import time

import warnings
warnings.filterwarnings("ignore")

#замер времени исполнения
def timeit(func):
    @wraps(func)
    def timeit_wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        total_time = end_time - start_time
        print(f'Function {func.__name__}{args} {kwargs} --- {total_time:.4f} seconds')
        return result
    return timeit_wrapper

#pip3 install fastwer
# git clone https://github.com/microsoft/unilm.git
# cd unilm
# cd trocr
# pip install pybind11
# pip install -r requirements.txt

@timeit
def craft_my():
    image = './vins/5.jpg' # can be filepath, PIL image or numpy array
    from craft_text_detector import (
        read_image,
        load_craftnet_model,
        load_refinenet_model,
        get_prediction,
        export_detected_regions,
        export_extra_results,
        empty_cuda_cache
    )

    refine_net = load_refinenet_model(cuda=False)
    craft_net = load_craftnet_model(cuda=False)
    output_dir = 'outputs/'    
    image = read_image(image)   
    
    prediction_result = get_prediction(
        image=image,
        craft_net=craft_net,
        refine_net=refine_net,
        text_threshold=0.7,
        link_threshold=0.4,
        low_text=0.4,
        cuda=False,
        long_size=1280
    )
    exported_file_paths = export_detected_regions(
        image=image,
        regions=prediction_result["boxes"],
        output_dir=output_dir,
        rectify=True
    )
    export_extra_results(
        image=image,
        regions=prediction_result["boxes"],
        heatmaps=prediction_result["heatmaps"],
        output_dir=output_dir
    )

@timeit
def trocr_my():
    image = Image.open("./outputs/image_crops/crop_0.png")
    model_version = "microsoft/trocr-base-printed"
    processor = TrOCRProcessor.from_pretrained(model_version)
    model = VisionEncoderDecoderModel.from_pretrained(model_version)

    pixel_values = processor(image, return_tensors="pt").pixel_values
    generated_ids = model.generate(pixel_values)
    generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
    #print(generated_text)
    print("".join(generated_text.split())) #удаляем пробелы, которые бывают в середине текста

craft_my()
trocr_my()

И картинка для для теста кода — image

Небольшие пояснения по коду.

Работают две основные модели: craft и trocr. Первая — ищет на картинке символы, вырезает боксы со словами, вторая — распознает то, что создала первая модель. Небольшой декоратор @timeit — просто замеряет время работы, его можно выкинуть из кода.

На выходе получаем — «SALLAA149A504647», то есть vin номер.

Код работает не в 100% случаев, и в «размытых» картинках он может ошибиться на символ, редко на два и более:

image — результат: WOC2539811V204673 (O вместо D).

Так что, при фотографировании лучше «не дрожать рукой».

Далее возник самый интересный вопрос: запустится ли все это на raspberry pi zero 2w, а если да, то как долго будет работать? Ждать часами распознавания, одиноко созерцая авто на улице не хотелось.

Предварительно немного разогнав плату:

sudo nano /boot/config.txt
    arm_freq=1200
    over_voltage=4

был дан старт ранее написанному коду с небольшими изменениями.

Как ни странно на плате, которая использует систему на armv7l, код запустился, но с небольшими костылями:

LD_PRELOAD=/home/pi/sentencepiece/build/src/libsentencepiece.so.0 python3 transformer.py

Кроме того, предварительно необходимо было увеличить swap хотя бы до 4Гб. Причем swap увеличивался таким образом, чтобы не получить «dd: memory exhausted»:

sudo swapoff -a
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
sudo chmod 0600 /swapfile
sudo mkswap /swapfile  
sudo swapon /swapfile 
grep Swap /proc/meminfo

Сам код стал выглядеть примерно так:

from transformers import TrOCRProcessor, VisionEncoderDecoderModel
from PIL import Image
from functools import wraps
import time

#pip install -q transformers
#LD_PRELOAD=/home/pi/sentencepiece/build/src/libsentencepiece.so.0 python3 transformer.py
#Linux small 5.10.103-v7+ #1529 SMP Tue Mar 8 12:21:37 GMT 2022 armv7l GNU/Linux

#замер времени исполнения
def timeit(func):
    @wraps(func)
    def timeit_wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        total_time = end_time - start_time
        print(f'Function {func.__name__}{args} {kwargs} --- {total_time:.4f} seconds')
        return result
    return timeit_wrapper

@timeit
def job():
    image=Image.open("/home/pi/Desktop/crop_0.png")
    #image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
    model_version = "microsoft/trocr-small-printed"
    #model_version = "microsoft/trocr-base-printed"
    #processor = TrOCRProcessor.from_pretrained("microsoft/trocr-base-handwritten")
    processor = TrOCRProcessor.from_pretrained(model_version)
    # calling the processor is equivalent to calling the feature extractor
    pixel_values = processor(image, return_tensors="pt").pixel_values

    model = VisionEncoderDecoderModel.from_pretrained(model_version)
    generated_ids = model.generate(pixel_values)
    generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
    print(generated_text)
job()

Результат:



19 минут. 19 минут!!!

Как видно, скоростью тут не пахнет. При всем при том, что используется младшая модель transformers — small-printed. С medium и large, очевидно, вообще нет будущего. *craft здесь не представлена, иначе время исполнения было бы еще больше.

Что ж, ожидаемо. Raspberry pi zero 2w не вывозит такие задачи.

Что дальше?


Поняв, что решение по распознаванию vin, в принципе имеется, осталось опробовать его на чем-то более мощном и компактном либо поискать onnx версию модели в надежде, что она будет быстрее при той же точности. В части железа на ум, конечно, пришел compute module 4.

Так как по производительности CM4 схож с raspberry pi 4b, то последняя пошла в тестирование.

По результатам тестирования на aarch64 Raspbian bullseye:

45.8414 seconds модель trocr-base-printed, 
10.6359 seconds модель trocr-small-printed
.



Неплохой результат. Craft модель добавляет сюда 30 сек своего времени.

Но с этим можно работать, это не 19 минут ожидания.

Осталось «приклеить» к кнопкам готового gui интерфейса на tkinter функции распознавания и узнать все о странной машине во дворе.

p.s. все совпадения случайны, как и vin номера в примерах.

Комментарии (8)


  1. lonelymyp
    21.09.2023 10:14
    +3

    Вин номера автомобилей не от балды придумывают, у них есть определённые закономерности которые описаны в стандарте. Например баг с распознаванием D и O можно было легко обойти, зная что в вин номерах не используют букву O.


    1. zoldaten Автор
      21.09.2023 10:14

      а как быть, если либо D либо 0 ?


      1. HomeMan
        21.09.2023 10:14
        +2

        Если у гугла спросить:

        regular expression for vin number

        то многое станет понятно.


  1. stdenis
    21.09.2023 10:14
    +7

    Зачем весь этот колхоз с малинкой, торчащими проводами т.д., когда разрабатывается мобильное устройство и можно взять готовый смартфон/планшет? Неужто мощности современных устройств не хватит для распознания 20 символов?


    1. balamutang
      21.09.2023 10:14
      +1

      Проблема малины еще в том что она не заточена на мобильность так как смартфон, батарейку кушает будь здоров, что малина что ее экран.


  1. bios737
    21.09.2023 10:14
    +2

    Ну, кроме азарта автора и специфического применения, данного решения, сложно оценить его необходимость и ценность. Таскать прибор, ради удовлетворения интереса? Ведь смартфон, всегда в кармане.


    1. zoldaten Автор
      21.09.2023 10:14

      Вопрос скорее дискуссионный. "ЦОДД" тоже с планшетами ходит, выглядят они (планшеты), разумеется, более эстетично. Быстрее и точнее работают ? Возможно. Но по рег. номерам. В части тачскрина - возможно он громоздкий, но доступен и устойчив к небрежности.

      Здесь скорее решаются прикладные задачи/эксперименты: text recognition in natural scenes; оценивается производительность доступных мобильных пк; интеграция нейросетей и т.п.
      В плане полезности: случайно найти машину в розыске, проверить историю машины на месте и т.п.

      В части мобильного приложения: да, было бы удобнее. Пожалуйста: интегрируйте trocr и все остальное в мобильное приложение, с удовольствием приду оценить его.


      1. balamutang
        21.09.2023 10:14
        +2

        Приложение на телефоне должно делать фото и отсылает на сервер для распознавания, а обратно получить и вывести на экран инфу из гибдд.

        Нет смысла таскать с собой распознавалку в этом случае.

        UPD Это мог бы быть даже бот в телеге