habrahabr

Как я прикручивал спидометр к танкам

  • вторник, 1 июля 2014 г. в 03:11:08
http://habrahabr.ru/post/228085/

image

Как-то давно увидел видео, где человек играл в некий автосимулятор и у него на столе стояли 2 больших стрелочных индикатора (вольтметры, если я не ошибаюсь), которые выполняли роль спидометра и тахометра. Cпустя несколько лет я решил повторить нечто похожее.

Играл я только в World of Tanks (и то давно забросил, так и не докачавшись до 10 уровня) поэтому и решил все провернуть именно с танками. Сразу скажу, вся разработка, за исключением отладки производилась на linux, код выполняемый на ПК написан на питоне. Да он медленный и код показывать мне стыдно, поэтому обойдемся без него.

Спидометр


В качестве спидометра тоже решил использовать что-нибудь со стрелкой, эта роль выпала старому, дешевому, китайскому мультиметру со стрелочной индикацией.

Было решено, что управлять спидометром будет ATMEGA16, поскольку имелась отладочная плата со всей необходимой обвязкой на борту. Общение ПК -> МК организовано через последовательный порт.

Показометр и МК




Прошивка примитивная, алгоритм примерно такой:
  1. В бесконечном цикле слушаем USART.
  2. При получении данных скорости, они проверялись на валидность и при помощи ШИМ'а устанавливался необходимый уровень напряжения.


Это была самая простая часть, далее необходимо было получить значение скорости. Сразу возникло 2 идеи: Получить данные у процесса игры (шерстить память в надежде найти вожделенный адрес) или сканировать значение непосредственно с самого интерфейса и распознать его. Последнее для меня было совсем темный лес и звучало интересно.

Получение заветной скорости


Сначала получаем скриншот игры, я наивно полагал, что с этим проблем не будет, но, как оказалось, захватить экран в режиме DirectX вещь не самая простая, но решаемая конечно. Вместо интерфейса игры получались прямоугольники Малевича, т.е. бесполезные изображения, я недолго думая перешел в оконный режим и развернул окно игры на весь экран, разница в информативности с полноэкранным режимом минимальная, но экран стал замечательно захватываться.

Захватывал я не весь экран, а небольшой участок в 200х200 примерно пикселей, с ним уже и работал.

Скриншот игрового интерфейса и панель повреждений




Затем я приоткрыл для себя такую замечательную вещь как OpenCV, с ее помощью обесцветил картинку и инвертировал цвета (изначально фон темный, а цифры светлые).

Скорость у нас представляет максимум двухразрядное число, причем моноширным шрифтом, что тоже радовало, потому как разряды занимали строго определенное место и не смещались при изменении значения скорости. Оставалось только выделить эти 2 позиции и распознавать их.



Распознавание

Распознавание значения я свел к сравнению с эталоном. Сравнение происходило по разнице хешей изображений: чем меньше разница, тем выше вероятность, что это именно то число. Наделал очень много эталонов всех цифр (например 50 шт цифры 3 из первого и второго разряда), захватывал наши позиции, нумеровал, потом вручную сортировал по папкам согласно номиналу. И затем сравнивал хеш в нутри одного номининала (т.е. единицы с единицами и т.д.), чтобы узнать, насколько сильно непохожи друг на друга одинаковые значения. Потом подмешивал похожие по начертаниям (например, к 3 — 8) и снова считал разницу. Это информация была ключевой в точности определения. Но как оказалось, были пограничные значения, т.е. иногда все-таки 3 и 8 и 9 путались хоть и не часто, но не приятно. Причиной этому оказался полупрозрачный фон и очень маленькие размеры самих цифр.

Пришлось лезть в клиент и изменять стандартную панель повреждений на кастомную, на которой я в 2 раза увеличил шрифт, изменил цвет фона, а так же полностью отключил прозрачность. Провел тесты с новой панелью и, как следовало ожидать, точность распознавания выросла на порядок. Так же отказался от инверсии цвета.

Новая панель и числа




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

Для захвата использовал Python Imaging Library (PIL).
Для работы с изображением: OpenCV.

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