- PVSM.RU - https://www.pvsm.ru -

Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv

Всем привет. Это моя юбилейная статья на хабре. За почти 7 лет я написал 10 статей (включая эту), 8 из них — технические. Общее количество просмотров всех статей — около полумиллиона.
Основной вклад я внёс в два хаба: PHP и Серверное администрирование. Мне нравится работать на стыке этих двух областей, но сфера моих интересов гораздо шире.
Как и многие разработчики я часто пользуюсь результатами чужого труда (статьи на хабре, код на гитхабе, ...), поэтому я всегда рад делиться с сообществом своими результатами в ответ. Написание статей — это не только возврат долга сообществу, но так же позваляет найти единомышленников, получить комментарии от профессионалов в узкой сфере и ещё больше углубить свои знания в исследуемой области.

Собственно эта статья об одном из таких моментов. В ней я опишу чем занимался почти всё своё свободное время за последние полгода. Кроме тех моментов, когда я ходил купаться в море через дорогу [1], смотрел сериалы или игрался в игры.

Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 1

Сейчас очень сильно развивается «Машинное обучение», по нему написано уже очень много статей, в том числе на хабре и практически каждый разработчик хотел бы взять и начать его использовать в своих рабочих задачах и домашних проектах, но с чего начать и к чему применять не всегда понятно. Большинство статей для начинающих предлагают кучу литературы, на прочтение которой не хватит и жизни, англоязычные курсы (а не все из нас могут усваивать материал на английском так же эффективно как и на русском), «недорогие» русскоязычные курсы и т.д.
Регулярно выходят новые статьи, в которых описаны новые подходы к решению той или иной задачи. На гитхабе можно найти реализацию описанного в статьях подхода. В качестве языков программирования чаще используются: c / c++, python 2/3, lua и matlab, а в качестве фремворков: caffe, tensorflow, torch. Каждый пишет — кто на чём горазд. Большая сегментация по языкам программирования и фреймворкам сильно усложняет процедуру поиска того, что тебе нужно и интеграцию этого в проект. К тому же в последнее время очень много исходного кода с комментариями на китайском языке.

Чтобы как-то уменьшить весь этот хаос в opencv добавили модуль dnn [2], который позволяет использовать модели, натренированные в основных фреймворках. Я со своей стороны покажу как этот модуль можно использовать из php.

Как_множатся_стандарты.jpg

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

Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 2

Jeremy Howard [3] (создатель бесплатного практического курса «машинное обучение для кодеров» [4]) считает, что сейчас есть большой порог между изучением машинного обучения и применении его на практике.

Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 3

Howard говорит, что для начала изучения машинного обучения достаточно одного года опыта программирования [5]. Я с ним полностью согласен и надеюсь, что моя статья поможет снизить порог вхождения в opencv для php-разработчиков, которые мало знакомы с машинным обучением и ещё не уверены хотят ли они вообще этим заниматься или нет, а также постараюсь описать все моменты, на которые я тратил часы и дни, чтобы у вас на это уходило не больше минуты.

Итак, что же я сделал кроме логотипа?
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 4
(надеюсь, что opencv не засудит меня за плагиат)

Я рассматривал возможность написать модуль php-opencv самостоятельно с помощью SWIG [6] и потратил на это кучу времени, но так ничего и не добился. Всё осложнялось тем, что я не знал с/с++ и не писал расширений под php 7. К сожалению большинство материалов в интернете по php-расширениям было написано для php 5, поэтому приходилось собирать информацию по крупицам, а возникающие проблемы решать самостоятельно.

Потом я нашёл на просторах гитхаба библиотеку php-opencv [7], она представляет из себя модуль для php7, который делает вызовы методов opencv. Чтобы скомпилировать, установить и запустить примеры у меня ушло несколько вечеров. Я начал пробовать различные возможности этого модуля, но мне не хватало некоторых методов, я их добавил самостоятельно, создал пулреквест, а автор библиотеки принял. Позже я добавил ещё больше функций.

Возможно читатель на этом моменте задаст себе вопрос: зачем вообще автору нужны были такие проблемы, почему было просто не начать использовать python и tensorflow?

Ответ. Осторожно, занудство и отмазки!

Дело в том, что я не профессиональный специалист по машинному обучению, я не могу на данном этапе разработать свой собственный подход к решению той или иной узкой задачи, в которой я достигну результатов на пару процентов лучше, чем другие исследователи, а потом ещё получить на это дело патент. Например, так сделали пять китайских парней с научными степенями, которые разработали mtcnn [8] и написали реализацию на matlab и caffe. Потом другие три китайских парня перенесли этот код на C++ & caffe, Python & mxnet, Python & caffe. Как вы наверное уже догадались, на знании только python и tensorflow далеко не уедешь. Придётся постоянно сталкиваться с кодом на разных языках с использованием разных фреймворков и комментариями на китайском.
Другой пример, я хотел использовать facemark [9] из opencv, но к сожалению авторы не добавили поддержку этого модуля при работе из python. При этом, чтобы добавить биндинги facemark в php у меня ушёл один вечер.
Я так же пытался скомпилировать opencv для работы с nodejs, согласно нескольким инструкциям но у меня выдавались различные ошибки и не получилось достигнуть результата.
По большей части мне было интересно этим заниматься не смотря на все трудности.

Вообщем, пока меня устраивает работать с opencv на php.

Вот так выглядит загрузка изображения:

$image = cvimread("images/faces.jpg");

Для сравнения, на питоне это выглядит так:

image = cv2.imread("images/faces.jpg")

При чтении изображения в php (также как и в с++) информация сохраняется в объект Mat (матрица). В php её аналогом является многомерный массив, но в отличие от многомерного массива этот объект позволяет различные быстрые манипуляции, например, деление всех элементов на число. В питоне при загрузке изображения возвращается объект numpy.

Осторожно, легаси! Так уж вышло, что imread (в php, c++ и pyton) загружает изображение не в формате RGB, а в BGR. Поэтому в примерах с opencv можно часто увидеть процедуру конвертации BGR->RGB и обратно.

Поиск лиц на фото

Первым делом я попробовал эту функцию. Для неё в opencv есть класс CascadeClassifier, который может использовать предобученную модель в формате xml. Перед нахождением лица рекомендуется переводить изображение в чёрно-белый формат.

$src = imread("images/faces.jpg");
$gray = cvtColor($src, COLOR_BGR2GRAY);

$faceClassifier = new CascadeClassifier();
$faceClassifier->load('models/lbpcascades/lbpcascade_frontalface.xml');

$faceClassifier->detectMultiScale($gray, $faces);

полный код примера [10]
Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 5
Как видно из примера, не составляет проблем найти лицо даже на фото в гриме зомби. Очки также не мешают нахождению лица.

Распознавание (узнавание) лиц на фото

Для этого в opencv есть класс LBPHFaceRecognizer и методы train/predict.
Если мы хотим узнать кто присутствует на фотографии, то сначала нужно натренировать модель с помощью метода train, он принимает два параметра: массив изображений лиц и массив числовых меток для этих изображений. После можно вызвать метод predict на тестовом изображении (лице) и получить числовую метку, которой оно соответствует.

$faceRecognizer = LBPHFaceRecognizer::create();
$faceRecognizer->train($myFaces, $myLabels = [1,1,1,1]); // 4 мои лица
$faceRecognizer->update($angelinaFaces, $angelinaLabels = [2,2,2,2]); // 4 лица Анжелины
$label = $faceRecognizer->predict($faceImage, $confidence);
// получаем label (1 или 2) и $confidence (уверенность)

полный код примера [11]
Наборы лиц:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 6

Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 7

Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 8
Когда я начинал работать с LBPHFaceRecognizer, у него не было возможности сохранения/загрузки/дообучения готовой модели. Собственно первый мой пулреквест добавил эти методы: write/read/update.

Нахождение меток на лицах

Когда я начинал знакомиться с opencv, то часто натыкался на фотографии лиц, на которых точками отмечены глаза, нос, губы и т.д. Мне хотелось повторить этот эксперимент самостоятельно, но в версии opencv для питона этого не реализовали. У меня ушёл вечер, чтобы добавить поддержку FacemarkLBF на php и отправить второй пулреквест. Всё работает просто, загружаем предобученную модель, подаём на вход массив лиц, получаем массив точек для каждого лица.

$facemark = FacemarkLBF::create();
$facemark->loadModel('models/opencv-facemark-lbf/lbfmodel.yaml');
$facemark->fit($src, $faces, $landmarks);

полный код примера [12]
Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 9
Как видно из примера, грим зомби может ухудшить нахождение опорных точек на лице. Очки также могут помешать нахождению лица. Засветка тоже влияет. При этом посторонние предметы во рту (клубника, сигарета и т.д.) могут и не мешать.

После моего первого пулреквеста я вдохновился и стал смотреть, что можно сделать ещё с помощью opencv и наткнулся на статью Deep Learning, теперь и в OpenCV [2]. Не долго думая, я решил добавить в php-opencv возможность использования предобученных моделей, которых полно на просторах интернета. Это оказалось не сильно сложно для загрузки caffe-моделей, правда позже у меня ушло куча времени чтобы получить научиться работать с многомерными матрицами, половина из которого ушла на разбирательство с c++ и изучение внутренностей opencv, а вторая на python и работу с моделями caffe/torch/tensorflow без использования opencv.

Поиск лиц на фото с помощью модуля dnn

Итак, opencv позволяет загружать предобученные модели в Caffe с помощью функции readNetFromCaffe [13]. Она принимает два параметра — пути до файлов .prototxt и .caffemodel. В prototxt-файле лежит описание модели, а в caffemodel — веса, вычисленные во время тренировки модели.
Вот пример начала prototxt-файла [14]:

input: "data"
input_shape {
  dim: 1
  dim: 3
  dim: 300
  dim: 300
}

Этот кусок файла описывает, что на вход ожидается 4-х мерная матрица 1x3x300x300. В описании моделей обычно пишут, что ожидается в таком формате, но чаще всего этого означает, что на вход ожидается изображение RGB (3 канала) размером 300x300.
Загружая RGB-изображение размером 300x300 c помощью функции imread мы получаем матрицу 300x300x3.
Для приведения матрицы 300x300x3 к виду 1x3x300x300 в opencv есть функция blobFromImage [15].
После этого нам остаётся только подать blob на вход сети с помощью метода setInput [16] и вызвать метод forward [17], который вернёт нам готовый результат.

$src = imread("images/faces.jpg");

$net = CVDNNreadNetFromCaffe('models/ssd/res10_300x300_ssd_deploy.prototxt', 'models/ssd/res10_300x300_ssd_iter_140000.caffemodel');

$blob = CVDNNblobFromImage($src, $scalefactor = 1.0, $size = new Size(300, 300), $mean = new Scalar(104, 177, 123), $swapRB = true, $crop = false);

$net->setInput($blob, "");

$result = $net->forward();

В данном случае результат — это матрица 1x1x200x7, т.е. 200 массивов по 7 элементов каждый. На фото с четырьмя лицами сеть нашла нам 200 кандидатов. Каждый из которых выглядит так [,, $confidence, $startX, $startY, $endX, $endY]. Элемент $confidence отвечает за «уверенность», т.е. то что вероятность предсказания удачна, например 0.75. Следующие элементы отвечают за координаты прямоугольника с лицом. В данном примере было найдено только 3 лица с уверенностью больше 50%, а оставшиеся 197 кандидатов лиц имеют уверенность менее 15%.

Размер модели 10 МБ, полный код примера [18].
Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 10
Как видно из примера, нейронная сеть не всегда выдаёт хорошие результаты при использовании её «в лоб». Не было найдено четвёртое лицо, при этом если четвёртое фото вырезать и отправить в сеть отдельно, то лицо будет найдено.

Улучшение качества изображения с помощью нейронной сети

Я уже давно слышал про библиотеку waifu2x [19], которая позволяет устранять шум и увеличивать размеры иконок/фото. Сама библиотека написана на lua, а под капотом использует несколько моделей (для увеличения иконок, устранения шума фото и т.д.) натренированных в torch. Автор библиотеки экспортировал эти модели в caffe и помог мне использовать их из opencv. В результате чего был написан пример на php для увеличения разрешения иконок.
Размер модели 2 МБ, полный код примера [20].
Оригинал:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 11
Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 12
Увеличение картинки без использования нейронной сети:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 13

Классификация изображений

Нейронная сеть MobileNet [21], обученная на наборе данных ImageNet [22] позволяет классифицировать изображение. Всего она может определять 1000 классов [23], что по-моему достаточно не мало.
Размер модели 16 МБ, полный код примера [24].
Оригинал:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 14
Результат:
87% — Egyptian cat, 4% — tabby, tabby cat, 2% — tiger cat

Tensorflow Object Detection API

Нейронная сеть MobileNet SSD (Single Shot MultiBox Detector), натренированная в Tensorflow [25] на датасете COCO [26] может не только классифицировать изображение, но и возвращать регионы, правда всего определять она может только 182 класса [27].
Размер модели 19 МБ, полный код примера [28].
Оригинал:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 15
Результат:
Компьютерное зрение и машинное обучение в PHP используя библиотеку opencv - 16

Подсветка синтаксиса и автодополнение кода

В репозиторий с примерами я также добавил файл phpdoc.php [29]. Благодаря ему Pphstorm подсвечивает синтакис функций, классов и их методов, а также работает автодополнение кода. Этот файл не нужно подключать в свой код (иначе будет ошибка), его достаточно положить в свой проект. Лично мне это упрощает жизнь. В этом файле описано большинство функций opencv, но не все, так что пулреквесты приветствуются.

Установка

Модуль dnn появился в opencv только в версии 3.4 (до этого он был в opencv-contrib).
В ubuntu 18.04 самая последняя версия opencv — 3.2. Сборка opencv из исходников занимает где-то полчаса, поэтому я собрал пакет под ubuntu 18.04 (работает и для 17.10, размер 25МБ), а также собрал пакеты php-opencv для php 7.2 (ubuntu 18.04) и php 7.1 (ubuntu 17.10) (размер 100КБ). Зарегистрировал ppa:php-opencv, но пока не осилил туда заливку и не нашёл ничего лучше, чем просто залить пакеты на гитхаб [30]. Также я создал заявку на создание аккаунта в pecl, но спустя несколько месяцев так и не получил ответа.
Таким образом сейчас установка под ubuntu 18.04 выглядит так:

apt update && apt install -y wget && 
wget https://raw.githubusercontent.com/php-opencv/php-opencv-packages/master/opencv_3.4_amd64.deb && dpkg -i opencv_3.4_amd64.deb && rm opencv_3.4_amd64.deb && 
wget https://raw.githubusercontent.com/php-opencv/php-opencv-packages/master/php-opencv_7.2-3.4_amd64.deb && dpkg -i php-opencv_7.2-3.4_amd64.deb && rm php-opencv_7.2-3.4_amd64.deb && 
echo "extension=opencv.so" > /etc/php/7.2/cli/conf.d/opencv.ini

Установка таким вариантом занимает около 1 минуты. Все варианты установки на ubuntu [31].
Также я собрал docker-образ размером 168 МБ [32].

Использование примеров

Скачивание:

git clone github.com/php-opencv/php-opencv-examples.git && cd php-opencv-examples

Запуск:

php detect_face_by_dnn_ssd.php

PS

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

Ссылки:
php-opencv-examples [33] — все примеры из статьи
php-opencv/php-opencv [34] — мой форк с поддержкой модуля dnn
hihozhou/php-opencv [7] — оригинальный репозиторий, без поддержки модуля dnn (я создал пулреквест, но он пока ещё не был принят).
Перевод статьи на английский язык [35] — я слышал, что англичане и американцы очень терпеливы к тем кто делает ошибки на английском, но мне кажется, что всему есть передел и я пересёк эту черту :) вообщем лайкните, кому не жалко. Тоже самое на реддите [36].

Автор: Владимир Гончаров

Источник [37]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/283162

Ссылки в тексте:

[1] купаться в море через дорогу: https://habr.com/post/352118/

[2] opencv добавили модуль dnn: https://habr.com/company/intel/blog/333612/

[3] Jeremy Howard: https://en.wikipedia.org/wiki/Jeremy_Howard_(entrepreneur)

[4] «машинное обучение для кодеров»: http://course.fast.ai/

[5] одного года опыта программирования: http://www.fast.ai/2017/11/16/what-you-need/#the-background-you-need-1-year-of-coding

[6] SWIG: https://ru.wikipedia.org/wiki/SWIG

[7] php-opencv: https://github.com/hihozhou/php-opencv

[8] mtcnn: https://github.com/kpzhang93/MTCNN_face_detection_alignment

[9] facemark: https://www.learnopencv.com/facemark-facial-landmark-detection-using-opencv/

[10] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/detect_face_by_cascade_classifier.php

[11] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/recognize_face_by_lbph.php

[12] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/detect_facemarks_by_lbf.php

[13] readNetFromCaffe: https://docs.opencv.org/3.4.1/d6/d0f/group__dnn.html#ga29d0ea5e52b1d1a6c2681e3f7d68473a

[14] prototxt-файла: https://github.com/php-opencv/php-opencv-examples/blob/master/models/ssd/res10_300x300_ssd_deploy.prototxt

[15] blobFromImage: https://docs.opencv.org/3.4.1/d6/d0f/group__dnn.html#ga152367f253c81b53fe6862b299f5c5cd

[16] setInput: https://docs.opencv.org/3.4.1/db/d30/classcv_1_1dnn_1_1Net.html#a672a08ae76444d75d05d7bfea3e4a328

[17] forward: https://docs.opencv.org/3.4.1/db/d30/classcv_1_1dnn_1_1Net.html#a98ed94cb6ef7063d3697259566da310b

[18] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/detect_face_by_dnn_ssd.php

[19] waifu2x: https://github.com/nagadomi/waifu2x

[20] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/upscale_image_x2_by_dnn_waifu2x.php

[21] MobileNet: https://habr.com/post/352804/

[22] ImageNet: https://ru.wikipedia.org/wiki/ImageNet

[23] 1000 классов: https://github.com/php-opencv/php-opencv-examples/blob/master/models/mobilenet/classes.txt

[24] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/classify_image_by_dnn_mobilenet.php

[25] Tensorflow: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md

[26] COCO: http://cocodataset.org/

[27] 182 класса: https://github.com/php-opencv/php-opencv-examples/blob/master/models/ssdlite_mobilenet_v2_coco/classes.txt

[28] полный код примера: https://github.com/php-opencv/php-opencv-examples/blob/master/detect_objects_by_dnn_mobilenet.php

[29] phpdoc.php: https://github.com/php-opencv/php-opencv-examples/blob/master/phpdoc.php

[30] пакеты на гитхаб: https://github.com/php-opencv/php-opencv-packages

[31] Все варианты установки на ubuntu: https://github.com/php-opencv/php-opencv-examples/wiki/Installation-on-ubuntu

[32] docker-образ размером 168 МБ: https://github.com/php-opencv/php-opencv-examples/wiki/installation-via-docker

[33] php-opencv-examples: https://github.com/php-opencv/php-opencv-examples

[34] php-opencv/php-opencv: https://github.com/php-opencv/php-opencv

[35] Перевод статьи на английский язык: https://medium.com/@morozovsk/computer-vision-and-machine-learning-in-php-using-the-opencv-library-3131fe9df94b

[36] реддите: https://www.reddit.com/r/PHP/comments/8rtbk7/computer_vision_and_machine_learning_in_php_using/

[37] Источник: https://habr.com/post/358902/?utm_source=habrahabr&utm_medium=rss&utm_campaign=358902