Оглавление
Продолжаем рассказ об интересных задачах, с которыми приходится сталкиваться мобильным разработчикам компании JetRuby. Сегодня мы поведаем вам, как простая оптимизация user experience (UX, он же — пользовательский опыт) может превратиться в магию автоматизации.
Постановка задачи
Изначальная задача была предельно простой: от пользователя требовалось навести прицел камеры на дисконтную карту и щелкнуть затвором. Приложение обрезало фотографию по краям прицела, и получалось достаточно милое изображение пластиковой карты. Однако не у всех пользователей это происходило столь легко и непринужденно. Некоторым из них для получения хорошей фотографии приходилось делать по 3-4 кадра. А приложение, на минуточку, предназначалось для хранения всех дисконтных карт в одном месте.
Мы погрузились в тяжкие раздумья. Как же исправить ситуацию? Как избавить пользователей от потери драгоценного времени? Комплексный анализ возникшей проблемы подсказал нам верное решение: необходимо добавить автоматическое распознавание карты в прицеле.
Реализация
Для исправления ситуации нам пришлось совершить три совершенно простых, на первый взгляд, шага:
- предварительная фильтрация изображения;
- выделение границ объекта;
- проверка объекта на предмет попадания в прицел.
Но дьявол кроется в деталях. В процессе реализации принятого решения нам пришлось столкнуться с некоторыми сложностями. Наверное наибольшей из них стала производительность. Использование таких подходов, как Hough Line Transform позволяет выделять прямые линии объекта. Однако на мобильных устройствах все это происходит очень медленно. Даже расчет всех операций на GPU девайса не помогает достичь приемлемой производительности. В результате мы решили пойти другим путем.
Опустим малолюбопытные подробности. Как получить видеопоток с камеры и отправить его на предварительную фильтрацию, вы можете узнать и без нашей помощи. Перейдем сразу к делу.
Предварительная фильтрация
Насколько успешно будет работать алгоритм выделения границ объектов, во многом зависит именно от предварительной фильтрации. Для этого мы использовали пикселизацию с легким размытием, что позволило добиться четкого выделения границ объекта, а также убрать лишние детали с изображения карты.
Для пикселизации изображения карты мы использовали комбинацию из Erosion и Dilation фильтров.
Первый фильтр задает для каждого пикселя минимальное значение красного канала. В результате происходит расширение темных контуров и изображение становится затемненным.
Dilation фильтр действует противоположным образом. Для каждого пикселя задается максимальное значение красного канала. Это делает изображение чрезмерно ярким.
Далее добавляем легкое размытие, которым вы наверняка баловались графических редакторах.
Так выглядит наша карта после предварительной фильтрации.
Выделение границ объекта
Переходим к следующему шагу. Существует множество алгоритмов выделения границ объекта, но мы решили остановиться на операторе Собеля, окрашивающем горизонтальные и вертикальные линии в разные цвета. В дальнейшем это поможет нам определить, насколько точно пользователь попал в прицел.
Но перед тем как показать вам код шейдера фильтра для определения границ объекта, мы бы хотели рассказать почему из множества подобных алгоритмов мы выбрали именно оператор Собеля . По нашему мнению его главным конкурентом является Canny Edge Detection. Проведя сравнительный анализ, мы пришли к однозначному выводу: оператор Собеля менее требователен к предварительной фильтрации. И это то, что нам нужно — подумали мы. Правда, наша интерпретация оператора Собеля не является его чистой реализацией. Классический Собель не добавляет цвета границ и является сугубо бинаризирующим фильтром. А вот его продолжение XY Derivative Filter уже делает то что нам надо.
Код шейдера фильтра для любознательных:
Теперь наше приложение видит мир в синих тонах.
Как видите, фильтрация отлично справилась с выделением границ пластиковой карты.
Проверка объекта на предмет попадания в прицел
Ну и последнее. В этом нет ничего сложного и тем более — сверхъестественного. Мы просто определяем “прямоугольник-зазор” с каждой стороны прицела и ожидаем получить в нем линию нужного цвета и длины, сопоставимой с длиной прямоугольника.
Самое время посмотреть как это работает.
Вот так с помощью “магии” автоматизации и компьютерного зрения мы упрощаем жизнь пользователям нашего приложения. Как говорится — все гениальное просто:)