Pull to refresh

Распознаём изображение с токена при помощи камеры

Reading time3 min
Views34K
В организации, где я тружусь в свободное от отдыха время, очень высокие требования к безопасности. Везде, где только можно, для аутентификации пользователей используются токены. Мне выдали вот такую вот штуку:

и сказали: жмёшь кнопку, смотришь цифры, вводишь пароль и радуешься. «Безопасность, конечно, превыше всего, но и о комфорте забывать не следует» — примерно так подумал я и провёл ревизию имеющегося у меня электронного хлама.

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

Анализ технического задания и подбор компонентов



Первым делом на глаза мне попалась старая веб-камера «Logitech QuickCam 3000», отправившаяся в «хлам» в связи с покупкой ноутбука со встроенной веб-камерой. План созрел моментально: снимаем камерой циферки с токена, распознаём их на компьютере и… Профит! Дальше был найден сервопривод (ну не нажимать же кнопку на токене каждый раз руками, правда?), старый USB-хаб (в моём ноутбуке всего два USB-порта, и один из них постоянно занят USB-Ethernet адаптером), и плата Arduino, неизвестно каким образом у меня оказавшаяся. В качестве корпуса для своего девайса я решил использовать конструктор «Лего», купленный впрок моему годовалому сыну (кажется, теперь я понимаю, почему жена ехидно ухмыльнулась при покупке).

К сожалению, фотографий донорских девайсов в целости и сохранности у меня нет, поэтому я могу «похвастаться» только устройством в сборе:



Принципиальная схема и прошивка микроконтроллера



Собственно, всё до смешного просто (я даже схему рисовать не буду): USB-хаб, к которому подключена веб-камера и ардуина. К ардуине подключен сервопривод (через ШИМ). Вот и всё.Исходный код программы, которая заливается в ардуину, тривиален: github.com/dreadatour/lazy/blob/master/servo.ino
Ардуина ждёт букву 'G' на ком-порт, и при её поступлении дёргает серву туда-назад. Задержка (500мс) и угол наклона сервы были подобраны экспериментальным путём.

Выбор языка программирования и анализ существующих библиотек «компьютерного зрения»



Единственным языком программирования, которому бы я доверил такую сложную задачу, как «компьютерное зрение» — благославленный Python. Благо в нём, практически из коробки, есть биндинги к такой славной библиотеке, как OpenCV. Собственно, на этом и остановимся.

Алгоритм распознавания кода токена



Я буду давать ссылки на куски кода, которые отвечают за описываемый функционал — на мой взгляд это оптимальный формат подачи информации. Весь код можно посмотреть на гитхабе.

Для начала берём изображение с камеры:


Облегчаем себе задачу: камера неподвижна относительно основы, токен может двигаться в своём лотке совсем незначительно, поэтому находим границы возможных положений токена, обрезаем кадр с камеры и (исключительно для нашего удобства) поворачиваем изображение на 90˚:


Дальше делаем некоторые преобразования: конвертируем получившееся изображение в grayscale и находим границы с помощью детектора границ Канни — это будут границы ЖК-экрана токена:


Находим контуры на полученном изображении. Контур представляет собой массив линий — отбрасываем линии меньше определённой длины:


С помощью тупого алгоритма определяем четыре линии, ограничивающие наш ЖК-дисплей:


Находим точки персечения этих линий и выполняем несколько проверок:
  • проверяем, чтобы длина вертикальных и горизонтальных линий примерно совпадала друг с другом и чтобы длина линий примерно совпадала с размером ЖК-дисплея (который мы вычислили экспериментальным путём)
  • проверяем, чтобы диагонали были примерно равны (нам нужно прямоугольник — ЖК-дисплей)

Далее находим угол, на который повёрнут наш токен, поворачиваем изображение на этот угол и вырезаем изображение ЖК:


Самое сложное позади. Теперь нам нужно повысить контрастность полученного изображения. Для этого мы запоминаем изображение пустого ЖК-экрана (до нажатия на кнопку токена), и просто «вычитаем» это изображение из картинки с цифрами (после нажатия на кнопку):


Получаем чёрно-белое изображение. Для этого с помощью ещё одного тупого алгоритма находим оптимальный порог, который будет разделять все пиксели на изображении на «чёрные» и «белые», конвертируем изображение в Ч/Б и вырезаем символы:


Ну а дальше просто распознаём цифры. Нам нет смысла мучаться с нейронными сетями и прочими штуками, т.к. у нас семисегментный индикатор: есть семь «точек», по которым однозначно определяется каждая цифра:


На всякий случай распознаём цифры с нескольких кадров: если три кадра подряд мы получили одинаковый результат — считаем, что распознавание прошло успешно, выводим результат с помощью программы «growlnotify» пользователю и копируем полученный код в буфер обмена.

Видео работы девайса


Осторожно, звук!


Исходный код целиком
Tags:
Hubs:
Total votes 178: ↑172 and ↓6+166
Comments60

Articles