ComputerVision (Ruby && OpenCV)

16 июня 2016
Людмила Дежкина, Senior Ruby Developer

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

Основные части библиотеки — интерпретация изображений и алгоритмы машинного обучения. Список возможностей, предоставляемых OpenCV, весьма обширен:

  • интерпретация изображений;
  • калибровка камеры по эталону;
  • устранение оптических искажений;
  • определение сходства;
  • анализ перемещения объекта;
  • определение формы объекта и слежение за объектом;
  • 3D-реконструкция;
  • сегментация объекта;
  • распознавание жестов.

Сейчас OpenCV используется во многих сферах. Вот несколько интересных примеров:

  1. Google:
    1. Google self-driving car — в беспилотных автомобилях Google OpenCV используется для разработки прототипа распознавания окружающей обстановки;
      (сегодня построенная система основывается преимущественно на LIDAR - в связи с трудностями распознавания при плохом освещении)
    2. Google Glass — в этих очках 3D-реконструкция изображения построена на OpenCV;
    3. Google Mobile;
  2. Робототехника и Arduino;
  3. Промышленное производство — иногда какой-нибудь завод делает на OpenCV систему подсчета деталей или что-то вроде того.

Сложно или интересно?

Хотя порой трудно сказать, какие данные можно считать действительно «большими», в случае с OpenCV таких сомнений нет: так, self-driving car может обрабатывать по приблизительных подсчетах около 1 Гб/с, и это действительно — большие данные. Для сравнения, человеческий мозг обрабатывает ~ 45 Мб - 3Гб/с — это зависит, в частности, от освещенности помещения.

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

Также в OpenCV применяются технологии машинного (machine learning) и глубинного обучения (deep learning), так как распознавание частично построено на нейронных сетях. Deep learning и machine learning — весьма интересная тема, изучать которую советую по курсам на Coursera. Что касается темы компьютерного зрения вообще, я советую вот эту книгу:

Из чего состоит OpenCV?

CXCORE (собственно ядро), как ни странно, с точки зрения программирования устроено элементарно. Оно содержит базовые структуры данных и алгоритмы:

  • базовые операции над многомерными числовыми массивами — они позволяют, например, перемножить матрицу на вектор, перемножить две матрицы и т. д.;
  • матричная алгебра, математические функции, генераторы случайных чисел — чтобы с этим работать, достаточно знать название нужной функции, и все;
  • запись/восстановление структур данных в/из XML;
  • базовые функции 2D графики — с их помощью можно, например, нарисовать змейку.

CV — это модуль обработки изображений и компьютерного зрения. Он включает:

  • базовые операции над изображениями (фильтрация, геометрические преобразования, преобразование цветовых пространств и т. д.);
  • анализ изображений (выбор отличительных признаков, морфология, поиск контуров, гистограммы);
  • анализ движения, слежение за объектами;
  • обнаружение объектов, в частности лиц;
  • калибровку камер и элементы восстановления пространственной структуры.

Геометрические преобразования, между прочим — это очень важная часть библиотеки: ведь когда вы пытаетесь что-то построить, зачастую нужно учитывать поворот и угол камеры.

HighGUI — модуль для ввода/вывода изображений и видео, создания пользовательского интерфейса. Модуль выполняет следующие функции:

  • захват видео с камер и из видео файлов, чтение/запись статических изображений;
  • функции для организации простого UI (все демо-приложения используют HighGUI).

ML — встроенные алгоритмы машинного обучения, работающие из коробки, хотя в 3-й версии от них постепенно отказываются, потому чтохорошие алгоритмы машинного обучения сейчас разрабатывают другие компании (о них будет сказано далее).

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

Cvaux — это экспериментальные и устаревшие функции:

  • пространственное зрение: стереокалибрация, самокалибрация;
  • поиск стереосоответствия, клики в графах;
  • нахождение и описание черт лица.

Примеры патентов или стартапов

Хороший пример одного из последних успешных стартапов, использующих OpenCV, —виртуальная примерочная от компании Zugara, обеспечивающая действительно высокую конверсию. Как она работает? Алгоритм приблизительно следующий: она фотографирует пользователя и считает расстояние до его лица. Затем пользователь вводит несколько своих размеров, и примерочная обсчитывает что-то из одежды, приглянувшейся покупателю. Есть, впрочем, и еще одна значительная часть в этой системе — AutoCAD-модель: каждая вещь перед тем, как вы будете ее примерять, проходит 3D-реконструкцию.

Второй хороший пример применения OpenCV — система распознавания автомобильных номеров на дорогах. Точность такой системы, однако — до 90 %, так как многое зависит от качества съемки, от скорости машины, от того, насколько загрязнен номер и т. д.

Нейронные сети (механизмы обучения)

Вторая важная часть OpenCV (после той, что отвечает за обработку изображения) — это машинное обучение. Помимо встроенного в OpenCV, сейчас существует несколько механизмов машинного обучения:

  • TensorFlow от Google, построенный полностью на тензорах;
  • Theano, PyLearn2 && EcoSystem — это одна из крупнейших разработок, очень сложная в применении;
  • Torch — уже устаревший механизм;
  • Caffe — это лучшая система для начинающих, которую просто использовать. Ее, между прочим, необязательно использовать именно для распознавания — вы можете применять ее, например, в финансовой сфере. Так, для прототипирования банковских манипуляций часто используется именно Caffe. Также есть биологические системы, построенные на ней.

Трудности построения системы

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

  • софт или железо,
  • алгоритм или нейронная сеть.

Дилемма софта и железа состоит в том, что, чем хуже железо, тем лучше нужно разработать программную часть, чтобы получить толковый результат. Вторая дилемма следующая — что лучше использовать в программной части: какой-либо алгоритм или нейронную сеть? На самом деле, иногда нейронная сеть проигрывает алгоритму. Я выбираю между алгоритмом и нейронной сетью следующим образом: если нейронная сеть занимает больше места, чем алгоритм, выбираю алгоритм. Алгоритм вообще надежней, и в простых случаях я выбирала бы именно его. А нейросеть порой не может решить даже очень простую задачу: так, перцептрон Розенблатта не может понять, находится точка выше или ниже прямой.

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

Поговорим немного о распознавании символов, что может понадобиться, например, при создании системы распознавания автомобильных номеров.

Tesseract OCR — открытое ПО, автоматически распознающее и единичную букву, и сразу текст. Tesseract удобен тем, что он есть для любых ОС, стабильно работает и легко обучаем. Однако у него есть существенный недостаток: он очень плохо работает с замыленным, битым, грязным и деформированным текстом. Поэтому Tesseract вряд ли подойдет для распознавания номеров, однако он очень хорошо справится с распознаванием простого текста. Т. ч. Tesseract можно отлично применять, например, в документообороте.

K-nearest — очень простой для понимания алгоритм распознавания символов, который, несмотря на свою примитивность, часто может побеждать не самые удачные реализации SVM или нейросетевых методов.

Работает он следующим образом:

  1. предварительно записываем приличное количество изображений реальных символов, которые были перед этим вручную разбиты на классы;
  2. вводим меру расстояния между символами (если изображение бинаризованно, операция XOR будет оптимальна);
  3. затем, когда мы пытаемся распознать символ, поочередно рассчитываем дистанцию между ним и всеми символами в базе. Среди ближайших соседей, возможно, будут представители различных классов. Представителей какого класса больше среди соседей, к тому классу стоит отнести распознаваемый символ.

Типы данных OpenCV

Тут все просто.

CvPoint — точка (структура из двух переменных (x, y))
CvSize — размер (структура из двух переменных (width, height))
CvRect — прямоугольник (структура из 4 переменных (x, y, width, height))
CvScalar — скаляр (4 числа типа double)
CvArr — массив — его можно считать “абстрактным базовым классом” для CvMat и далее IplImage (CvArr -> CvMat -> IplImage)
CvMat — матрица
IplImage — изображение

Вот и все типы данных, которые есть в OpenCV.

Загрузка картинки

Это уже то, что вы можете сделать на Ruby. После того, как вы подключите библиотеку, можете загрузить картинку. Важно, что, если вы хотите ее посмотреть, ее нужно не забыть вывести в окно.

cvLoadImage( filename, int iscolor=CV_LOAD_IMAGE_COLOR )
// окно для отображения картинки

В качестве параметров принимаются имя файла и качество изображения:

  • filename — имя файла
  • iscolor — определяет как представить картинку
  • iscolor > 0 — цветная картинка с 3-мя каналами
  • iscolor == 0 — картинка будет загружена в формате GRAYSCALE (градации серого)
  • iscolor < 0 картинка будет загружена как есть

cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
// показываем картинку
cvShowImage("original",image);

Информация, доступная после загрузки

Тут есть около 25 методов, но я пользуюсь только этими:

  • image->nChannels // число каналов картинки (RGB, хотя в OpenCV — BGR ) (1-4);
  • image->depth // глубина в битах (это нужно для накладывания определенных фильтров, например);
  • image->width // ширина картинки в пикселях;
  • image->height // высота картинки в пикселях;
  • image->imageSize // память, занимаемая картинкой (==image->height*image->widthStep);
  • image->widthStep // расстояние между соседними по вертикали точками изображения (число байт в одной строчке картинки) — может потребоваться для самостоятельного обхода всех пикселей изображения.

Метод Виолы-Джонса

Этот метод распознавания лиц, изобретенный в 2005 г., основан на признаках Хаара. Он используется почти во всех фотоаппаратах для определения лиц. Вот как он работает.

Величина каждого признака вычисляется как сумма пикселей в белых прямоугольниках, из которой вычитается сумма пикселей в черных областях. Прямоугольные признаки более примитивны, чем steerable filter,

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

А если говорить человеческим языком, берется лицо и делится на две части. Область возле носа и под глазами будет более темная, щеки — более светлыми и т. д. Представим, что каждый пиксель изображения — одно значение вектора: при помощи этого вычисляется количество серых и белых пикселей. На основании этого делается вывод, похоже это на лицо или нет.

Нейронные сети

Нейросети сейчас делятся на два типа. Первый тип —старые двух- и трехуровневые сети. Такие сети обучаются градиентными методами с обратным распространением ошибок. При работе с ними вы берете вектор, направляете функцию, после чего, например, идет слой перцептрона. У вас есть input (изображение, которое уже обработано — например, берется нужная его часть, а в случае с номером — нарезается на прямоугольники для каждого символа номера). После этого раскладывается каждый пиксель — вектор, и мы считаем переходы. Собственно, это просто обход по массиву. Такая технология уже устарела.

Более новые сети второго типа — глубокие и сверточные, использующие операцию свертки. Операция свертки показывает схожесть одной функции с отраженной и сдвинутой копией другой. Вся свертка в OpenCV происходит по 2D-фильтру:

cvFilter2D( src, dst, kernel, CvPoint anchor CV_DEFAULT(cvPoint(-1,-1)))

Детектор границ Canny

Если мы хотим обрабатывать изображения лиц или номеров, нужно вычислить границы этих изображений. Это очень непростая задача решается в OpenCV с помощью очень старого встроенного алгоритма — Canny 1986 г.

Края (границы) — такие кривые на изображении, вдоль которых происходит резкое изменение яркости или других видов неоднородностей. Проще говоря, край — резкий переход или изменение яркости.

Причины возникновения краев:

  • изменение освещенности;
  • изменение цвета;
  • изменение глубины сцены (ориентации поверхности).

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

cvCanny( image, edges, threshold1,
threshold2, CV_DEFAULT(3) );

image — одноканальное изображение для обработки (градации серого);
edges — одноканальное изображение для хранения границ, найденных функцией;
threshold1 — порог минимума;
threshold2 — порог максимума;
aperture_size — размер для оператора Собеля.

Вот как работает алгоритм Canny пошагово:

  • Убирает шум и лишние детали из изображения.
  • Рассчитывает градиент изображения.
  • делает края тонкими (edge thinning).
  • связывает края в контуры (edge linking).

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

И надеюсь, что тема актуальна в связи с наступлением Эры роботехники.