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

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

Как и многие разработчики я часто пользуюсь результатами чужого труда (статьи на Хабре, код на гитхабе, ...), поэтому я всегда рад делиться с сообществом своими результатами в ответ. Написание статей — это не только возврат долга сообществу, но так же позволяет найти единомышленников, получить комментарии от профессионалов в узкой сфере и ещё больше углубить свои знания в исследуемой области.

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



Сейчас очень сильно развивается «Машинное обучение», по нему написано уже очень много статей, в том числе на хабре и практически каждый разработчик хотел бы взять и начать его использовать в своих рабочих задачах и домашних проектах, но с чего начать и к чему применять не всегда понятно. Большинство статей для начинающих предлагают кучу литературы, на прочтение которой не хватит и жизни, англоязычные курсы (а не все из нас могут усваивать материал на английском так же эффективно как и на русском), «недорогие» русскоязычные курсы и т.д.

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

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

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


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


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

Итак, что же я сделал кроме логотипа?


(надеюсь, что opencv не засудит меня за плагиат)

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

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

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

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

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

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

$image = cv\imread("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);

Полный код примера

Результат:



Как видно из примера, не составляет проблем найти лицо даже на фото в гриме зомби. Очки также не мешают нахождению лица.

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


Для этого в 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 (уверенность)

Полный код примера

Наборы лиц:





Результат:



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

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


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

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

Полный код примера

Результат:



Как видно из примера, грим зомби может ухудшить нахождение опорных точек на лице. Очки также могут помешать нахождению лица. Засветка тоже влияет. При этом посторонние предметы во рту (клубника, сигарета и т.д.) могут и не мешать.

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

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


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

Вот пример начала prototxt-файла:

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.
После этого нам остаётся только подать blob на вход сети с помощью метода setInput и вызвать метод forward, который вернёт нам готовый результат.

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

$net = \CV\DNN\readNetFromCaffe('models/ssd/res10_300x300_ssd_deploy.prototxt', 'models/ssd/res10_300x300_ssd_iter_140000.caffemodel');

$blob = \CV\DNN\blobFromImage($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 МБ, полный код примера.

Результат:



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

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


Я уже давно слышал про библиотеку waifu2x, которая позволяет устранять шум и увеличивать размеры иконок/фото. Сама библиотека написана на lua, а под капотом использует несколько моделей (для увеличения иконок, устранения шума фото и т.д.) натренированных в torch. Автор библиотеки экспортировал эти модели в caffe и помог мне использовать их из opencv. В результате чего был написан пример на php для увеличения разрешения иконок.

Размер модели 2 МБ, полный код примера.

Оригинал:



Результат:



Увеличение картинки без использования нейронной сети:



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


Нейронная сеть MobileNet, обученная на наборе данных ImageNet позволяет классифицировать изображение. Всего она может определять 1000 классов, что по-моему достаточно не мало.

Размер модели 16 МБ, полный код примера.

Оригинал:



Результат:

87% — Egyptian cat, 4% — tabby, tabby cat, 2% — tiger cat

Tensorflow Object Detection API


Нейронная сеть MobileNet SSD (Single Shot MultiBox Detector), натренированная в Tensorflow на датасете COCO может не только классифицировать изображение, но и возвращать регионы, правда всего определять она может только 182 класса.

Размер модели 19 МБ, полный код примера.

Оригинал:



Результат:



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


В репозиторий с примерами я также добавил файл phpdoc.php. Благодаря ему Phpstorm подсвечивает синтакис функций, классов и их методов, а также работает автодополнение кода. Этот файл не нужно подключать в свой код (иначе будет ошибка), его достаточно положить в свой проект. Лично мне это упрощает жизнь. В этом файле описано большинство функций 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, но пока не осилил туда заливку и не нашёл ничего лучше, чем просто залить пакеты на гитхаб. Также я создал заявку на создание аккаунта в 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.
Также я собрал docker-образ размером 168 МБ.

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


Скачивание:

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

Запуск:

php detect_face_by_dnn_ssd.php

PS


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

Традиционно предупреждаю, что я не консультирую и не помогаю через личные сообщения Хабра и соцсети.

Вы всегда можете задать вопросы, создав Issue на гитхабе (можно на русском).

Благодарности:
dkurt за быстрые ответы на гитхабе.
arrybn за статью «Deep Learning, теперь и в OpenCV»

Ссылки:

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Можно ли называть вызов методов opencv из php как «Работа с компьютерным зрением в php»?
Удобный способ установки php-opencv
Нужны ли ещё статьи на эту тему?
php и python
Машинное обучение и php-разработчики
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 18
  • +2
    Поправьте «Вообщем», пожалуйста. Нельзя так с моими глазами утром в понедельник.
    Извините.

    В целом — статья годная. На досуге попробую вкурить opencv поподробнее.
    Спасибо.
    • +1
      Еще расскажите зачем вам распознавание образов на картинке из php :)
      • +2
        А почему собственно нет, это какая-то непосильная задача для php, что нужен другой язык разработки? Чем matlab или lua настолько лучше, что вы не допускаете даже возможности реализации подобной задачи на php? Мне казалось, что в задачах по машинному обучению от языка зависит только скорость исполнения, а качество работы — только от модели.
        • 0
          Акцент в моем вопросе был не на php, а на «зачем» :) имел в виду, зачем вообще вам нужно было распознавание образов? какой практический опыт работы из php
          • 0
            Если под «распознавание образов» вы имеете ввиду раздел статьи «Распознавание (узнавание) лиц на фото.», то мой ответ такой. Часто происходит когда на работе вываливают огромные объёмы фотографий с корпоратива или спортивного мероприятия, а я например там всего на десяти фото из тысячи. Натренировать сеть на нахождение своего лица и написать на php скрипт, который найдёт все фотки с тобой — очень быстро и просто. Но вообще у каждого могут быть разные задачи, которые можно решить таким образом.
            Кто-то захочет написать модуль для bitrix «корпоративный портал», чтобы решать описываемую мной проблему, кто-то использует owncloud вместо googledrive и хотел бы, чтобы происходило тегирование/сортировка по людям и т.д. Причин «зачем?» может быть сотни, не вижу смысла их все перечислять, просто включите фантазию. Именно поэтому я не очень понимаю ваш вопрос «зачем?».
            Я хотел бы оставить в секрете основную причину по которой я это делал для себя. Всё чем я хотел поделиться — я рассказал в статье и не хотел бы отвечать на личные вопросы.
            • 0
              Натренировать сеть на нахождение своего лица и написать на php скрипт, который найдёт все фотки с тобой — очень быстро и просто.

              Так себе бизнес-кейс, потому что если это реально задача «для себя», то никто бы не стал делать её на РНР, когда инструментарий для таких задач намного удобнее под Python или в крайнем случае C++.
              • +1
                Вообще-то это был ответ на вопрос «зачем?». На вопрос «зачем на php» даже не знаю какой смысл отвечать.
                Вы результаты опроса к статье смотрели? Большая часть php-разработчиков не знает python и при этом интересуется машинным обучением.
                Если мне например нужно сделать ресайз картинки на php, то я не спешу выносить эту часть на python потому что там намного удобнее, меня и в php всё устраивает. Я уже молчу про то что «удобство» — абстрактное понятие и его не навязывают другим людям (кроме извечных споров «android vs ios»). Кому-то удобнее на python, кому-то на php, а кому-то и на matlab.
                Кроме того, только один из всех примеров я нашёл в готовом виде на python. И в статье я писал, что на питоне ни то что неудобно написать пример с Facemark, там вообще нет к нему биндингов, все примеры только на c++, что показалось мне не очень удобно и я дописал биндинги для php.
                • +1
                  Вы результаты опроса к статье смотрели?

                  я смотрел и варианты ответов подобраны так что бы объективного опроса не вышло.


                  Большая часть php-разработчиков не знает python

                  и не знают ничего о CV. по трудозатратам на обучение выучить синтаксис python не составляет труда. Более того, будет даже проще потому что намного больше примеров на том же python. Банально даже те что идут вместе с opencv.


                  и при этом интересуется машинным обучением.

                  "интересуется" — тут надо было уточнять что именно означает фраза интересуется. Читает статьи на хабре, и публикации на эту тему — это разные все же вещи. Или там запустить дэмку распознавания лиц которая идет в комплекте с opencv тоже не совсем точно степень вовлеченности характеризует.


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


                  то я не спешу выносить эту часть на python

                  это делает nginx из коробки. Можно еще поставить imaginary. И только если вам уж совсем прям что-то другое надо можно изголяться и что-то писать на эту тему.


                  Проблема не в том что питон как язык лучше, проблема в экосистеме. Для php есть биндинги того же vips, вполне себе удобные, но если вам надо побыстрому SVM классификатор или с класстеризацией побаловаться + немного DSP — то под питон вы банально быстрее найдете готовые решения. И не просто готовые а качественные готовые решения.


                  «удобство» — абстрактное понятие

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


                  там вообще нет к нему биндингов

                  вроде уже есть

                  • 0
                    Большое спасибо за развёрнутый ответ. Как я уже говорил выше, один из плюсов написания статей — это получение обратной связи от профессионалов. А от вас лично я получаю комментарии с моей самой первой статьи, т.е. уже почти 7 лет, за что большое спасибо.
                    Однако я опечален, что вы реально так думаете:
                    Вы результаты опроса к статье смотрели? я смотрел и варианты ответов подобраны так что бы объективного опроса не вышло.
                    В каждой моей статье есть опрос и не один — это всё для того чтобы лучше понимать свою аудиторию, а не «подтасовывать результаты».
                    Большая часть php-разработчиков не знает python
                    и не знают ничего о CV. по трудозатратам на обучение выучить синтаксис python не составляет труда. Более того, будет даже проще потому что намного больше примеров на том же python. Банално даже те что идут вместе с opencv.
                    Ещё раз хотел бы написать, я просто предоставил инструмент и никому не навязываю его использование. Если кому-то покажется проще в работающий проект вставить кусок кода из примера и использовать свою предобученную модель, то кто я такой, чтобы осуждать этого человека.
                    Лично мне такой вариант показался проще, потому что многие найденные библиотеки требовали много зависимостей: opencv, caffe/tensorflow/torch/mxnet, openblas, pandas, scipy, scikit и т.д. (а ещё с моей первой статьи вы наверное помните, что я не люблю лишние зависимости).
                    В 9 из 10 библиотек использовался opencv и мне было не совсем понятно, зачем библиотека тянет столько других библиотек, если было бы достаточно использовать только opencv. Но так кому-то удобнее. Numpy в большинстве случаев использовался для операций не сложнее доставания значения из массива по ключу.
                    Можно еще поставить imaginary.
                    Зачем мне ставить imaginary если мне достаточно написать одну строчку?
                    $imagick->resizeImage
                    Я не заставляю вас так делать, если вам удобнее иначе, то пожалуйста.
                    там вроде уже есть биндинги
                    Ну или там захотите вы написать новый маскарад — и окажется что opencv для этого не годится (медленно на мобилках).
                    А вот это уже конструктив, большое спасибо за него.
                    Я читал эту статью про сравнение производительности и другую статью, после которых был убеждён, что opencv с предобученными моделями работает быстрее и потребляет при этом меньше памяти.
                    • 0
                      > работает быстрее и потребляет при этом меньше памяти.

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

                      так же есть коммерческие реализации все тех же алгоритмов которые на порядки быстрее, но…
            • +1
              Обычное дело. Дополнительная классификация объявления/продукта добавленного пользователем/импортом по загруженному изображению, для валидации и релевантности поиска, например.
        • +1

          Очень интересно. На практике заинтересовал метод увеличения фото с сохранением качества, вот это думаю многим было бы годно попробовать уже сейчас. Было бы классно даже оформить в отдельный сервис с методами resize и crop. По остальному практическое применение пока неясно. Спасибо.

        • +1
          Спасибо большое за статью! Интересуюсь данной темой, однако все как-то времени не хватало серьезно взяться за дело. После прочтения Вашей статьи получил «бафф вдохновение»! =)
          • +1
            Большое спасибо. Ваша статья мне как раз попалась в нужное время и в нужном месте. Пробую сейчас это использовать в медицине (интерпретация результатов ЭКГ, например). Посмотрим, что получится. Напишите что-то ещё. Родина вас не забудет!
            • +1
              Отличная статья. Большое спасибо). На вопрос «зачем?» — я обычно отвечаю — внутренний перфекционист против языкового зоопарка. С лицами достаточно много натренированных моделей, попадались ли Вам натренированные модели для автомобильных номеров?
              • +1
                Спасибо большое за статью.
                • +1
                  Очень интересно. Хотелось бы еще примеры какие-нибудь. Как можно использовать это, например, в интернет-магазинах…

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое