Продолжаем постигать современную магию (компьютерное зрение). Часть 2 не значит, что нужно сначала читать часть 1. Часть 2 значит, что теперь всё серьёзно — мы хотим понять всю мощь нейросетей в зрении. Детектирование, трекинг, сегментация, оценка позы, распознавание действий… Самые модные и крутые архитектуры, сотни слоёв и десятки гениальных идей уже ждут вас под катом!
![](https://habrastorage.org/r/w780q1/webt/yt/nk/uu/ytnkuundiudek47rjvlmlujrrm4.jpeg)
Напомню, что в первой части мы познакомились со свёрточными нейросетями и их визуализацией, а также с задачами классификации изображений и построения их эффективных представлений (эмбеддингов). Мы даже обсудили задачи распознавания лиц и ре-идентификации людей.
Ещё в предыдущей статье поговорили про разные типы архитектур (да, те самые таблички, которые я делал месяц,), и тут Google времени зря не терял: они выпустили ещё одну крайне быструю и точную архитектуру EfficientNet. Они создали её, используя NAS и специальную процедуру Compound Scaling. Ознакомьтесь со статьёй, оно того стоит.
![](https://habrastorage.org/r/w780q1/webt/9m/_h/5c/9m_h5cc1tsxs7bfkainm5zom-wg.jpeg)
Ну а пока некоторые исследователи анимируют лица и ищут в фильмах поцелуи, мы займёмся более насущными проблемами.
Вот люди говорят: “распознавание изображений”. Но что такое “распознавание”? Что такое “понимание (сцены)”? На мой взгляд, ответы на эти вопросы зависят от того, что именно мы хотим “распознать”, и что именно хотим “понять”. Если мы строим Искусственный Интеллект, который будет извлекать информацию о мире из визуального потока также эффективно (или даже лучше), как люди, то нужно идти от задач, от потребностей. Исторически сложилось, что современное “распознавание” и “понимание сцены” можно разделить на несколько конкретных задач: классификация, детектирование, трекинг, оценка позы и точек лица, сегментация, распознавание действий на видео и описание картинки текстом. В этой статье речь пойдёт о первых двух задачах из списка (уп-с, спойлер третьей части), поэтому текущий план такой:
Let's rock, superstars!
![](https://habrastorage.org/r/w780q1/webt/-q/9e/on/-q9eonan6thdv5jivk8kq0h7gm0.jpeg)
Итак, задача звучит просто — дана картинка, необходимо найти на ней объекты заранее заданных классов (человек, книга, яблоко, артезиано-нормандский бассет-гриффон и т.д.). Для того, чтобы решить эту задачу с помощью нейросетей, поставим её в терминах тензоров и машинного обучения.
Мы помним, что цветная картинка — это тензор (H,W,3) (если не помним, то есть часть 1). Раньше мы умели только классифицировать картинку целиком, теперь же наша цель — предсказать положения интересующих объектов (координаты пикселей) на картинке и их классы.
Ключевая идея здесь в том, чтобы решать сразу две задачи — классификацию и регрессию. Используем нейросеть, чтобы регрессировать координаты и классифицировать объекты внутри них.
Но координаты объекта, вообще говоря, можно по-разному формализовать, в DL есть три основных способа: детектирование (боксы объектов), оценка позы (ключевые точки объектов) и сегментация («маски» объектов). Сейчас поговорим про предсказание именно bounding box’ов, точки и сегментация будут дальше по тексту.
![](https://habrastorage.org/r/w780q1/webt/ki/wt/xm/kiwtxmvvhmwsvqn3_5dovlmp8w8.jpeg)
В основном датасеты для детектирования размечены box’ами в формате: “координаты левого верхнего и правого нижнего углов для каждого объекта на каждой картинке” (этот формат ещё называют top-left, bottom-right), и большинство нейросетевых подходов предсказывают именно эти координаты.
![](https://habrastorage.org/r/w1560/webt/uo/zs/tj/uozstjspdifxpslvqyuurauxs2g.png)
Перед тем как окунуться в виды нейросетей для детекции, давайте вместе подумаем, как вообще можно решать задачу детектирования чего-либо на изображениях. Наверное, если мы хотим найти определённый объект на картинке, то мы примерно знаем, как он выглядит и какую должен занимать на изображении площадь (хотя она может и изменяться).
Далее я иногда буду опираться на новый обзор области детектирования от января 2019 (картинки тоже будут из него). Это просто must read, если хотите быстро получить максимально широкий взгляд на DL в детекции.
Одной из первых статей, посвящённых детектированию и локализации с помощью CNN, была сеть Overfeat. Авторы утверждают, что впервые использовали нейросеть для детектирования на ImageNet, переформулировав задачу и изменив лосс. Подход, кстати, был практически end-to-end (ниже — схема Overfeat).
![](https://habrastorage.org/r/w1560/webt/8o/5v/tv/8o5vtvhgukkn7frba0nx8yltfis.png)
Следующей важной архитектурой стала изобретённая исследователями из FAIR в 2014 году нейросеть Region-based Convolutional Neural Network (RCNN). Суть её в том, что она предсказывает сначала много так называемых “регионов интереса” (RoI's), внутри которых потенциально могут быть объекты (с помощью алгоритма Selective Search), и уже их классифицирует и уточняет координаты боксов с помощью CNN.
![](https://habrastorage.org/r/w1560/webt/2r/2p/kp/2r2pkpcoysglv4z_v-ll_y14mqw.png)
Правда подобный пайплайн делал всю систему медленной, ведь мы прогоняли через нейросеть каждый регион (тысячи раз делали forward pass). Уже через год тот же Ross Girshick из FAIR улучшил RCNN до Fast-RCNN. Здесь идея была в том, чтобы поменять местами Selective Search и предсказание сетью: сначала пропускаем через предобученную нейросеть всю картинку, а потом поверх выданного feature map’а сети-backbone'а предсказываем регионы интереса (например, с помощью того же Selective Search, но там есть и другие алгоритмы). Было всё ещё довольно медленно, куда медленнее, чем real-time (пока что будем считать, что real-time — это меньше 40 миллисекунд на одну картинку).
![](https://habrastorage.org/r/w1560/webt/tn/uc/d-/tnucd-y6i7tr4edgjeudtrsj16u.png)
На скорость влияла больше всего уже не CNN, а сам алгоритм генерации боксов, поэтому решено было заменить его на вторую нейросеть — Region Proposal Network (RPN), которая будет обучаться предсказывать регионы интереса объектов. Так появилась Faster-RCNN (да, над названием явно долго не думали). Схема:
![](https://habrastorage.org/r/w1560/webt/6v/h_/ye/6vh_yee2zrsflyhh8jdvtm_bbgy.png)
Потом было ещё улучшение в виде R-FCN, о нём подробно говорит не будем, но хочу упомянуть Mask-RCNN. Mask-RCNN — уникальную в своём роде, первая нейросеть, которая решает и задачу детектирования, и instance-сегментации одновременно — она предсказывает точные маски (силуэты) объектов внутри bounding box’ов. Её идея на самом деле довольно проста — есть две ветки: для детекции и для сегментации, и нужно обучать сеть на обе задачи сразу. Главное — иметь размеченные данные. Сам по себе Mask-RCNN устроен очень похоже на Faster-RCNN: backbone тот же, но в конце две «головы» (так часто называют последние слои нейросети) под две разные задачи.
![](https://habrastorage.org/r/w1560/webt/7w/ig/hq/7wighq6ox7tptik5f_7d7cez2hg.png)
Это были так называемые Two-Stage (или Region-based) подходы. Параллельно с ними в DL-детектировании развивались аналоги — One-Stage подходы. К ним можно отнести такие нейросети, как: Single-Shot Detector (SSD), You Only Look Once (YOLO), Deeply Supervised Object Detector (DSOD), Receptive Field Block Network (RFBNet) и многие другие (см. карту ниже, из этого репозитория).
![](https://habrastorage.org/r/w1560/webt/ue/lc/-y/uelc-yeav4_avjdkycglf9uwrmm.png)
One-stage подходы, в отличие от two-stage, не используют отдельный алгоритм для генерации боксов, а просто предсказывают несколько координат боксов для каждого feature map’а, выданного свёрточной нейросетью. Подобным образом действует YOLO, SSD слегка отличается, но идея одна: 1х1 свёртка предсказывает из полученных feature map’ов много чисел по глубине, однако мы заранее договариваемся, какое число что значит.
![](https://habrastorage.org/r/w1560/webt/qj/ml/w_/qjmlw_ympdcib6jkpfdjdvcirdy.png)
![](https://habrastorage.org/r/w1560/webt/jr/i3/oy/jri3oymb48sv5vq9dwwdxbndszg.png)
Например, предсказываем из feature map’a размера 13х13х256 feature map 13х13х(4*(5+80)) чисел, где по глубине мы для 4 боксов предсказываем по 85 чисел: первые 4 числа в последовательности всегда — координаты бокса, 5-ое число — уверенность в боксе, и 80 чисел — вероятности каждого из классов (классификация). Это нужно, чтобы потом подать нужные числа в нужные лоссы и правильно обучить нейросеть.
![](https://habrastorage.org/r/w1560/webt/ao/xi/o2/aoxio2rty3hgpu2f8mduomvs9nu.png)
Хочу обратить внимание на то, что качество работы детектора напрямую зависит от качества работы нейросети для извлечения признаков (то есть backbone-нейросети). Обычно в этой роли выступает одна из архитектур, о которых я говорил в предыдущей статье (ResNet, SENet и др.), однако иногда авторы придумывают собственные более оптимальные архитектуры (например, Darknet-53 в YOLOv3) или модификации (например, Feature Pyramid Pooling (FPN)).
Снова отмечу, что мы обучаем сеть и на классификацию, и на регрессию одновременно. В сообществе это принято называть multi-task loss: в одном лоссе фигурирует сумма лоссов для нескольких задач (с некоторыми коэффициентами).
Недавно вышли статьи от 2019 года, в которых авторы заявляют о ещё лучшем соотношении скорость/точность в задаче детекции с помощью предсказания боксов на основе точек. Я говорю о статьях “Objects as Points” и “CornerNet-Lite”. ExtremeNet является модификацией CornerNet. Кажется, сейчас их можно назвать SOTA в детекции с помощью нейросетей (но это не точно).
UPD (25 апреля 2020): На данный момент среди детекторов значительно выделяется YOLOv4, в которой авторы указывают на наилучшее соотношение скорость-качество среди практически всех остальных нейросетевых детекторов.
![](https://habrastorage.org/r/w1560/webt/wk/qf/mr/wkqfmrenwm3u5f6c6bzinjcffga.png)
Если вдруг моё объяснение детекторов всё же показалось сумбурным и непонятным, в нашем видео я обсуждаю это не спеша. Возможно, сначала стоит посмотреть его.
Ниже я привёл таблицы нейросетей в детектировании со ссылками на код и кратким описанием фишек каждой сети. Я постарался собрать только те сети, которые действительно важно знать (по крайней мере, их идеи), чтобы иметь хорошее представление об object detection сегодня:
Для того, чтобы понять, как соотносятся скорость/качество каждой из архитектур, можно посмотреть в этот обзор или в более популярную его версию.
Архитектуры — это прекрасно, но детектирование является прежде всего практической задачей. “Не имей сто сетей, а имей хотя бы 1 работающую” — таков мой message. В таблице выше есть ссылки на код, но лично я редко сталкиваюсь с запуском детекторов непосредственно из репозиториев (по крайней мере, с целью дальнейшего деплоя в продакшн). Чаще для этого используется какая-либо библиотека, например, TensorFlow Object Detection API (см. практическую часть моего занятия) или библиотека от исследователей из CUHK. Предлагаю вашему вниманию очередную супер-таблицу (они ведь вам нравятся, да?):
Часто детектировать нужно объект только одного класса, но специфичного и весьма вариативного. Например, детектировать все лица на фото (для дальнейшей верификации/подсчёта людей), детектировать людей целиком (для ре-идентификации/подсчёта/трекинга) или детектировать текст на сцене (для OCR/перевода слов на фото). В целом, подход “обычной” детекции здесь до определённой степени сработает, но в каждой из этих подзадач есть свои трюки, чтобы улучшить качество.
![](https://habrastorage.org/r/w780q1/webt/ic/ul/rp/iculrpbc7niyrdxg1yk_8r82nsw.jpeg)
Здесь появляется некоторая специфика, поскольку лица часто занимают достаточно малую часть изображения. Плюс люди не всегда смотрят в камеру, часто лицо видно лишь сбоку. Одним из первых подходов к распознаванию лиц был знаменитый детектор Виолы-Джонса на основе каскадов Хаара, изобретённый ещё в 2001 году.
![](https://habrastorage.org/r/w1560/webt/hc/tf/zn/hctfzn0xudbedi_aymhmlcwkamu.png)
Нейросети тогдабыли не в моде не были ещё так сильны в зрении, однако старый добрый hand-crafted подход делал своё дело. В нём активно использовались несколько типов специальных масок-фильтров, которые помогали извлекать лицевые регионы с изображения и их признаки, и далее эти признаки подавались в AdaBoost-классификатор. Кстати, этот метод действительно нормально работает и сейчас, он достаточно быстрый и запускается “из коробки” с помощью OpenCV. Недостаток этого детектора в том, что он видит только лица, развёрнутые фронтально к камере. Стоит лишь немного повернуться, и стабильность детекции нарушается.
Для подобных более сложных случаев можно использовать dlib. Это C++-библиотека, в которой реализованиы многие алгоритмы зрения, в том числе для детектирования лиц.
Из нейросетевых подходов в детектировании лиц особенно значимым является Multi-task Cascaded CNN (MTCNN) (MatLab, TensorFlow). В целом, она и сейчас активно используется (в том же facenet).
![](https://habrastorage.org/r/w780q1/webt/80/wn/vu/80wnvuf59poodmodzswcgjlyjt4.jpeg)
Идея MTCNN — использовать для предсказания положения лица и его особых точек три нейросети последовательно (поэтому и “каскад”). Особых точек лица в данном случае ровно 5: левый глаз, правый глаз, левый край губ, правый край губ и нос. Первая нейросеть из каскада (P-Net) используется для генерации потенциальных регионов лица. Вторая (R-Net) — для улучшения координат полученных боксов. Третья (O-Net) нейросеть ещё раз регрессирует координаты боксов и, помимо того, предсказывает 5 ключевых точек лица. Multi-task эта сеть потому, что решаются три задачи: регрессия точек боксов, классификация лицо/не лицо для каждого бокса и регрессия точек лица. Причём MTCNN делает это всё в real-time, то есть ей требуется менее 40 ms на одну картинку.
![](https://habrastorage.org/r/w780q1/webt/5e/iz/d5/5eizd5lwag9umfo1ccypep42eik.jpeg)
Из современных State-of-the-Art можно отметить Dual Shot Face Detector (DSFD) и FaceBoxes. FaceBoxes имеет возможность быстрого запуска на CPU (!), а DSFD отличился лучшим качеством (вышел в апреле 2019 года). DSFD устроен посложнее, чем MTCNN, поскольку внутри сети используются специальный модуль для улучшения признаков (с dilated convolutions), две ветки их обработки и специальные типы лоссов. Кстати, с dilated convolutions мы ещё не раз столкнёмся в статьях про сегментацию в следующей части. Ниже пример работы DSFD (впечатляет, не правда ли?).
![](https://habrastorage.org/r/w780q1/webt/xv/or/it/xvoritceua0_ocjteyvm4xvisbq.jpeg)
Чтобы научиться ещё и распознавать лица, не забудьте заглянуть в предыдущую статью серии, там я про это вкратце рассказал.
![](https://habrastorage.org/r/w1560/webt/qn/v9/wo/qnv9woeqru3dioeennrkvalkjqg.png)
Обратите внимание на фото выше. Легко заметить, что, если предсказывать bounding box’ы, параллельные осям координат (как мы делали раньше), то получится весьма некачественно. Часто это оказывается весьма критично, если мы хотим, например, подать потом эти боксы на вход recognition-нейросети, которая по картинке будет предсказывать текст.
В таких случаях принято предсказывать повёрнутые bounding box’ы, или и вовсе ограничивать текст многоугольниками вместо прямоугольников, если он изогнутый (примеры ниже). С предсказанием повёрнутых боксов справляется, например, EAST-детектор.
![](https://habrastorage.org/r/w1560/webt/j3/hd/bj/j3hdbj-xozs4voec8ekiz4k1eo0.png)
Идея EAST-детектора в том, чтобы предсказывать не координаты углов боксов, а следующие три вещи:
Таким образом, это больше напоминает задачу сегментации (выделения масок текста), нежели детектирования. Поясняющая картинка из arxiv-статьи:
![](https://habrastorage.org/r/w1560/webt/dj/vt/uu/djvtuu36dzinoratlumncsgb5t8.png)
Задача распознавания текста (а значит и его детектирования) весьма популярна, поэтому есть и аналоги:TextBoxes++ (Caffe) и SegLinks, однако EAST, на мой взгляд, наиболее прост и доступен.
После детектирования текста хочется сразу скормить его другой нейросети, чтобы распознать его и выдать строку символов. Здесь можно заметить интересную смену модальности — из картинок в текст. Бояться этого совсем не стоит, ведь всё зависит лишь от того, какова архитектура сети, что именно предсказывается на последнем слое и какой используется лосс. Например, MORAN (код на PyTorch) и ASTER (код на TensorFlow) вполне справляются с поставленной задачей.
![](https://habrastorage.org/r/w1560/webt/vt/ot/nc/vtotncz1d1zhhsiytuzmarta9ta.png)
В них нет чего-то сверхъестественного, однако очень грамотно используются сразу два принципиально разных типа нейросетей: CNN и RNN. Первое нужно для извлечения признаков из картинки, а второе для генерации текста. Подробнее на примере MORAN'а: ниже архитектура его распознающей сети.
![](https://habrastorage.org/r/w1560/webt/hu/qx/_2/huqx_2jakjcggsgacvhj9a8vce4.png)
Однако несмотря на повёрнутые боксы от EAST'а, сетям-распознавателям на вход всё равно приходит прямоугольная картинка, а значит текст внутри неё может занимать далеко не всё пространство. Для того, чтобы распознавателю было проще предсказать по картинке непосредственно текст на ней, можно преобразовать её определённым способом.
Можем применить афинное преобразование ко входной картинке, чтобы растянуть/повернуть текст. Этого можно добиться с помощью Spatial Transformet Network (STN), поскольку она самостоятельно выучивает подобные преобразования и легко встраивается в другие нейронные сети (кстати, можно подобное выравнивание для любой картинки делать, не только для текста). Ниже пример до/после STN.
![](https://habrastorage.org/r/w780q1/webt/6y/z1/p9/6yz1p942ensgepwirqrtzpcnb7q.jpeg)
Подробно про STN здесь рассказывать нет смысла, поскольку есть замечательная статья на Хабре (картинка взята оттуда, спасибо автору) и код на PyTorch.
Но MORAN (та самая нейросеть для распознавания текста) поступает ещё умнее — она не ограничивается семейством афинных преобразований, а предсказывает для каждого пикселя входной картинки карту смещений по x и по y, таким образом добиваясь любого преобразования, которое улучшит обучение сети для распознавания. Этот метод называется rectification, то есть исправление картинки с помощью вспомогательной нейросети (rectifier'а). Ниже сравнение картинки после афинного преобразования и после ректификации:
![](https://habrastorage.org/r/w1560/webt/n4/az/du/n4azduih5p7wd9sx-tqhpgy-p20.png)
Однако помимо подходов к распознаванию текста «модульно» (сеть детекции -> сеть распознавания), есть end-to-end архитектуры: на входе картинка, а на выходе — детекции и распознанный внутри них текст. И всё это единым пайплайном, который обучается на обе задачи сразу. В этом направлении есть внушительная работа Fast Oriented Text Spotting with a Unified Network (FOTS) (код на PyTorch), где авторы также отмечают, что end-to-end подход в два раза быстрее, чем «детекция+распознавание». Ниже схема нейросети FOTS, особую роль играет блок RoiRotate, благодаря которому есть возможность «прокидывать градиенты» с сети для распознавания на нейросеть для детекции (это и правда сложнее, чем кажется).
![](https://habrastorage.org/r/w1560/webt/a5/xh/ch/a5xhchryrwf-zpfudielmab9pqu.png)
К слову, каждый год проходит конференция ICDAR, к которой приурочены несколько соревнований по распознаванию текста на самых различных изображениях.
На мой взгляд, главная проблема в детектировании сейчас не качество модели-детектора, а данные: их обычно долго и дорого размечать, особенно если классов, которые нужно детектировать, очень много (но кстати есть пример решения для 500 классов). Поэтому многие работы сейчас посвящены генерации как можно более правдоподобных данных «синтетически» и получения разметки “бесплатно”. Ниже картинка измоего диплома статьи от Nvidia, в которой речь идёт как раз о генерации синтетических данных.
![](https://habrastorage.org/r/w1560/webt/6k/mn/wh/6kmnwhvc1pgahzwtjloa4dy9sbm.png)
Но всё же здорово, что теперь мы можем точно сказать, где на картинке что находиться. И если хотим, например, посчитать количество чего-то на кадре, то достаточно лишь задетектить это и выдать количество боксов. В детекции людей хорошо работает и обычная YOLO, просто главное подать много данных. Тот же Darkflow подойдёт, а класс «человек» встречается почти во всех крупных датасетах по детектированию. Так что если хотим посчитать с помощью камеры количество людей, которые прошли мимо, скажем, за одни сутки, или количество товаров, которые взял человек в магазине, просто задетектим и выдадим количество…
![](https://habrastorage.org/r/w780q1/webt/m2/ts/pl/m2tsplbgrnmoauvlghy8ivq2jdm.jpeg)
Стоп. Но ведь если мы будем детектировать людей на каждом изображении с камеры, то мы можем посчитать их количество на одном кадре, а на двух — уже нет, поскольку не сможем сказать, где какой именно человек. Нам необходим алгоритм, который позволит считать именно уникальных людей в видеопотоке. Им может быть алгоритм ре-идентификации, но когда речь заходит про видео и детекцию, грех не использовать алгоритмы трекинга.
До сих пор мы говорили только про задачи на картинках, но самое-то интересное происходит на видео. Чтобы решать то же распознавание действий, нам необходимо использовать не только так называемую пространственную (spatial) компоненту, но и временнУю (temporal), поскольку видео — это последовательность изображений во времени.
![](https://habrastorage.org/r/w780q1/webt/qm/da/pa/qmdapao0kcytnxhorj7htm6susy.jpeg)
Трекинг — это аналог детектирования изображений, но для видео. То есть мы хотим научить сеть предсказывать не бокс на картинке, а треклет во времени (который есть по сути последовательность боксов). Ниже пример изображения, на котором показаны «хвосты» — треки этих людей на видео.
![](https://habrastorage.org/r/w1560/webt/xz/yv/c_/xzyvc_oumhrnf_-bmf8gz5l_hli.png)
Давайте подумаем, как можно решать задачу трекинга. Пусть есть видео, и его кадры #1 и #2. Рассмотрим пока что только один объект — трекаем один мячик. На кадре #1 мы можем использовать детектор, чтобы детектировать его. На втором тоже можем детектировать мячик, и если он там там один, то всё хорошо: говорим, что бокс на предыдущем кадре — это бокс того же мяча, что и на кадре #2. Так же можно продолжать и на остальные кадры, ниже gif из курса по зрению pyimagesearch.
![](https://habrastorage.org/webt/_w/et/ak/_wetakdpybemefhert6cxslqaju.gif)
Кстати, в целях экономии времени можем не запускать нейросеть на втором кадре, а просто “вырезать” бокс мяча из первого кадра и искать ровно такой же на втором кадре корреляцией или попиксельно. Такой подход утилизируют корреляционные трекеры, они считаются простыми и более-менее надёжными, если мы имеем дело с простыми случаями по типу “трекинг одного мяча перед камерой в пустой комнате”. Такую задачу ещё называют Visual Object Tracking. Ниже пример работы корреляционного трекера на примере одного человека.
![](https://habrastorage.org/webt/cv/fp/qd/cvfpqdd5ghelxfpucxrejxq6vpm.gif)
Однако если детекций/людей несколько, то нужно уметь сопоставлять боксы с кадра #1 и с кадра #2. Первая идея, которая приходит в голову — попробовать сопоставлять бокс тому, который имеет с ним наибольшую область пересечения (IoU). Правда, в случае нескольких перекрывающихся детекций такой трекер будет нестабилен, поэтому нужно использовать ещё больше информации.
![](https://habrastorage.org/r/w1560/webt/qx/i2/t1/qxi2t1ejpnir0h52ip-23zpf4ti.png)
Подход с IoU опирается лишь на «геометрические» признаки детекций, то есть просто пытается сопоставить их по близости на кадрах. Но ведь у нас в распоряжении целое изображение (даже два в данном случае), и мы можем использовать то, что внутри этих детекций — «визуальные» признаки. Плюс к этому мы имеем историю детекций для каждого человека, что позволяет более точно предсказывать его следующее положение на основе скорости и направления движения, это условно можно назвать «физические» признаки.
![](https://habrastorage.org/webt/mc/ay/se/mcaysee7xq25-j6hnvjalhfy5z0.gif)
Одним из первых real-time трекеров, который был вполне надёжен и умел справляться со сложными ситуациями, был опубликованный в 2016 году Simple Online and Realtime Traker (SORT) (код на Python). SORT не использовал какие-либо визуальные признаки и нейросети, а лишь оценивал ряд параметров каждого бокса на каждом кадре: текущую скорость (по x и по y отдельно) и размер (высота и ширина). Соотношение сторон (aspect ratio) бокса всегда берутся от самой первой детекции этого бокса. Далее скорости предсказываются с помощью фильтров Калмана (они вообще добро и свет в мире обработке сигналов), строится матрица пересечений боксов по IoU и детекции назначаются венгерским алгоритмом.
Если вам кажется, что математики уже стало многовато, то в этой статье всё доступно объясняется (это ж medium :).
Уже в 2017 году вышла модификация SORT'а в виде DeepSORT (код на TensorFlow). DeepSORT уже стал применять нейросеть для извлечения визуальных признаков, используя их для разрешения коллизий. Качество трекинга выросло — не зря он считается одним из лучших онлайн-трекеров сегодня.
![](https://habrastorage.org/webt/15/it/ny/15itnyht32oes3n-keaqk36jnpq.gif)
Область трекинга и правда активно развивается: есть и трекеры с сиамскими нейросетями, и трекеры с RNN. Следует держать руку на пульсе, ведь в любой день может выйти (или уже вышла) ещё более точная и быстрая архитектура. Кстати, за подобными вещами очень удобно следить на PapersWithCode, там всегда ссылки на статьи и код к ним (если он есть).
![](https://habrastorage.org/r/w780q1/webt/jb/yh/bf/jbyhbf2ovu_ynurp_t_d9_hi4vg.jpeg)
Мы уже действительно многое пережили и многое узнали. Но компьютерное зрение — крайне обширная область, а я — крайне упрямый человек. Именно поэтому мы ещё увидимся в третьей статье этого цикла (будет ли она последней? Кто знает...), где более подробно обсудим сегментацию, оценку позы, распознавание действий на видео и генерацию описания по изображению с помощью нейросетей.
P.S. хочу выразить особую благодарность Вадиму Горбачёву за его ценные советы и комментарии при подготовке этой и предыдущей статьи.
![](https://habrastorage.org/webt/yt/nk/uu/ytnkuundiudek47rjvlmlujrrm4.jpeg)
В прошлой серии
Напомню, что в первой части мы познакомились со свёрточными нейросетями и их визуализацией, а также с задачами классификации изображений и построения их эффективных представлений (эмбеддингов). Мы даже обсудили задачи распознавания лиц и ре-идентификации людей.
Ещё в предыдущей статье поговорили про разные типы архитектур (да, те самые таблички
![](https://habrastorage.org/webt/9m/_h/5c/9m_h5cc1tsxs7bfkainm5zom-wg.jpeg)
Ну а пока некоторые исследователи анимируют лица и ищут в фильмах поцелуи, мы займёмся более насущными проблемами.
Вот люди говорят: “распознавание изображений”. Но что такое “распознавание”? Что такое “понимание (сцены)”? На мой взгляд, ответы на эти вопросы зависят от того, что именно мы хотим “распознать”, и что именно хотим “понять”. Если мы строим Искусственный Интеллект, который будет извлекать информацию о мире из визуального потока также эффективно (или даже лучше), как люди, то нужно идти от задач, от потребностей. Исторически сложилось, что современное “распознавание” и “понимание сцены” можно разделить на несколько конкретных задач: классификация, детектирование, трекинг, оценка позы и точек лица, сегментация, распознавание действий на видео и описание картинки текстом. В этой статье речь пойдёт о первых двух задачах из списка (уп-с, спойлер третьей части), поэтому текущий план такой:
- Найди меня, если сможешь: детектирование объектов
- Детектирование лиц: не пойман — не вор
- Много букв: детектирование (и распознавание) текста
- Видео и трекинг: единым потоком
Let's rock, superstars!
Найди меня, если сможешь: детектирование объектов
![](https://habrastorage.org/webt/-q/9e/on/-q9eonan6thdv5jivk8kq0h7gm0.jpeg)
Итак, задача звучит просто — дана картинка, необходимо найти на ней объекты заранее заданных классов (человек, книга, яблоко, артезиано-нормандский бассет-гриффон и т.д.). Для того, чтобы решить эту задачу с помощью нейросетей, поставим её в терминах тензоров и машинного обучения.
Мы помним, что цветная картинка — это тензор (H,W,3) (если не помним, то есть часть 1). Раньше мы умели только классифицировать картинку целиком, теперь же наша цель — предсказать положения интересующих объектов (координаты пикселей) на картинке и их классы.
Ключевая идея здесь в том, чтобы решать сразу две задачи — классификацию и регрессию. Используем нейросеть, чтобы регрессировать координаты и классифицировать объекты внутри них.
Классификация? Регрессия?
Напомню, что мы говорим про задачи машинного обучения. В задаче классификации в качество истинных меток для объектов выступают метки классов, и мы предсказываем класс объекта. В задаче регрессии в качестве истинных ответов выступают вещественные числа, и мы предсказываем число (например: вес, рост, зарплату, количество персонажей, которые умрут в очередной серии Игры Престолов...). Более подробно — милости прошу в 3-ю лекцию DLSchool (ФПМИ МФТИ).
Но координаты объекта, вообще говоря, можно по-разному формализовать, в DL есть три основных способа: детектирование (боксы объектов), оценка позы (ключевые точки объектов) и сегментация («маски» объектов). Сейчас поговорим про предсказание именно bounding box’ов, точки и сегментация будут дальше по тексту.
![](https://habrastorage.org/webt/ki/wt/xm/kiwtxmvvhmwsvqn3_5dovlmp8w8.jpeg)
В основном датасеты для детектирования размечены box’ами в формате: “координаты левого верхнего и правого нижнего углов для каждого объекта на каждой картинке” (этот формат ещё называют top-left, bottom-right), и большинство нейросетевых подходов предсказывают именно эти координаты.
![](https://habrastorage.org/webt/uo/zs/tj/uozstjspdifxpslvqyuurauxs2g.png)
Про датасеты и метрики в задаче детектирования
После постановки задачи лучше всего посмотреть, какие есть данные для обучения и какие используются метрики для измерения качества. Об этом я не спеша рассказываю в первой половине 13-ой лекции от Deep Learning School (на х2.0 самое то).
Перед тем как окунуться в виды нейросетей для детекции, давайте вместе подумаем, как вообще можно решать задачу детектирования чего-либо на изображениях. Наверное, если мы хотим найти определённый объект на картинке, то мы примерно знаем, как он выглядит и какую должен занимать на изображении площадь (хотя она может и изменяться).
Изобретаем детектирование с нуля
Наивным и простейшим подходом будет просто сделать алгоритм “поиска шаблона”: пусть картинка 100х100 пикселей, и мы ищем футбольный мяч. Пусть есть шаблон мяча 20х20 пикселей. Возьмём этот шаблон и будем проходиться им прямо как свёрткой по всей картинке, считая попиксельную разность. Так работает template matching (ещё часто используется какой-либо тип корреляции вместо попиксельной разности).
Если шаблона нет, но есть нейросеть-классификатор, то можем поступить так: будем идти окном фиксированного размера по картинке и предсказывать класс текущей области картинки. Потом просто скажем, что самые вероятные регионы объектов — те, где классификатор ответил уверенно. Таким образом проблему того, что объект выглядит внешне по-разному, мы нейросетью решить можем (так как она обучалась на классификацию на весьма разнообразной выборке).
Но тут же всплывает проблема — объекты на картинках имеют разный размер. Тот же футбольный мяч может быть во всю высоту/ширину картинки, а может быть далеко у ворот, занимая всего 10-20 пикселей из 1000. Хочется написать Brute Force алгоритм: будем просто в цикле перебирать размеры окна. Допустим, у нас 100х200 пикселей, тогда будем идти окном 2х2, 2Х3, 3х2, 2х4, 4х2, 3х3…, 3х4, 4х3… Думаю, вам стало понятно, что количество возможных окон будет 100*200, и причём каждым мы проходимся по картинке, совершая (100-W_window) * (200 — H_window) операций классификации, что занимает немало времени. Боюсь, мы не дождёмся, пока такой алгоритм отработает.
Можно, конечно, выбрать наиболее характерные окна в зависимости от объекта, но это тоже будет весьма долго работать, а если и быстро, то вряд ли точно — в реальных приложениях будет безумное количество вариаций размеров объектов на изображениях.
Если шаблона нет, но есть нейросеть-классификатор, то можем поступить так: будем идти окном фиксированного размера по картинке и предсказывать класс текущей области картинки. Потом просто скажем, что самые вероятные регионы объектов — те, где классификатор ответил уверенно. Таким образом проблему того, что объект выглядит внешне по-разному, мы нейросетью решить можем (так как она обучалась на классификацию на весьма разнообразной выборке).
Но тут же всплывает проблема — объекты на картинках имеют разный размер. Тот же футбольный мяч может быть во всю высоту/ширину картинки, а может быть далеко у ворот, занимая всего 10-20 пикселей из 1000. Хочется написать Brute Force алгоритм: будем просто в цикле перебирать размеры окна. Допустим, у нас 100х200 пикселей, тогда будем идти окном 2х2, 2Х3, 3х2, 2х4, 4х2, 3х3…, 3х4, 4х3… Думаю, вам стало понятно, что количество возможных окон будет 100*200, и причём каждым мы проходимся по картинке, совершая (100-W_window) * (200 — H_window) операций классификации, что занимает немало времени. Боюсь, мы не дождёмся, пока такой алгоритм отработает.
Можно, конечно, выбрать наиболее характерные окна в зависимости от объекта, но это тоже будет весьма долго работать, а если и быстро, то вряд ли точно — в реальных приложениях будет безумное количество вариаций размеров объектов на изображениях.
Далее я иногда буду опираться на новый обзор области детектирования от января 2019 (картинки тоже будут из него). Это просто must read, если хотите быстро получить максимально широкий взгляд на DL в детекции.
Одной из первых статей, посвящённых детектированию и локализации с помощью CNN, была сеть Overfeat. Авторы утверждают, что впервые использовали нейросеть для детектирования на ImageNet, переформулировав задачу и изменив лосс. Подход, кстати, был практически end-to-end (ниже — схема Overfeat).
![](https://habrastorage.org/webt/8o/5v/tv/8o5vtvhgukkn7frba0nx8yltfis.png)
Следующей важной архитектурой стала изобретённая исследователями из FAIR в 2014 году нейросеть Region-based Convolutional Neural Network (RCNN). Суть её в том, что она предсказывает сначала много так называемых “регионов интереса” (RoI's), внутри которых потенциально могут быть объекты (с помощью алгоритма Selective Search), и уже их классифицирует и уточняет координаты боксов с помощью CNN.
![](https://habrastorage.org/webt/2r/2p/kp/2r2pkpcoysglv4z_v-ll_y14mqw.png)
Правда подобный пайплайн делал всю систему медленной, ведь мы прогоняли через нейросеть каждый регион (тысячи раз делали forward pass). Уже через год тот же Ross Girshick из FAIR улучшил RCNN до Fast-RCNN. Здесь идея была в том, чтобы поменять местами Selective Search и предсказание сетью: сначала пропускаем через предобученную нейросеть всю картинку, а потом поверх выданного feature map’а сети-backbone'а предсказываем регионы интереса (например, с помощью того же Selective Search, но там есть и другие алгоритмы). Было всё ещё довольно медленно, куда медленнее, чем real-time (пока что будем считать, что real-time — это меньше 40 миллисекунд на одну картинку).
![](https://habrastorage.org/webt/tn/uc/d-/tnucd-y6i7tr4edgjeudtrsj16u.png)
На скорость влияла больше всего уже не CNN, а сам алгоритм генерации боксов, поэтому решено было заменить его на вторую нейросеть — Region Proposal Network (RPN), которая будет обучаться предсказывать регионы интереса объектов. Так появилась Faster-RCNN (да, над названием явно долго не думали). Схема:
![](https://habrastorage.org/webt/6v/h_/ye/6vh_yee2zrsflyhh8jdvtm_bbgy.png)
Потом было ещё улучшение в виде R-FCN, о нём подробно говорит не будем, но хочу упомянуть Mask-RCNN. Mask-RCNN — уникальную в своём роде, первая нейросеть, которая решает и задачу детектирования, и instance-сегментации одновременно — она предсказывает точные маски (силуэты) объектов внутри bounding box’ов. Её идея на самом деле довольно проста — есть две ветки: для детекции и для сегментации, и нужно обучать сеть на обе задачи сразу. Главное — иметь размеченные данные. Сам по себе Mask-RCNN устроен очень похоже на Faster-RCNN: backbone тот же, но в конце две «головы» (так часто называют последние слои нейросети) под две разные задачи.
![](https://habrastorage.org/webt/7w/ig/hq/7wighq6ox7tptik5f_7d7cez2hg.png)
Это были так называемые Two-Stage (или Region-based) подходы. Параллельно с ними в DL-детектировании развивались аналоги — One-Stage подходы. К ним можно отнести такие нейросети, как: Single-Shot Detector (SSD), You Only Look Once (YOLO), Deeply Supervised Object Detector (DSOD), Receptive Field Block Network (RFBNet) и многие другие (см. карту ниже, из этого репозитория).
![](https://habrastorage.org/webt/ue/lc/-y/uelc-yeav4_avjdkycglf9uwrmm.png)
One-stage подходы, в отличие от two-stage, не используют отдельный алгоритм для генерации боксов, а просто предсказывают несколько координат боксов для каждого feature map’а, выданного свёрточной нейросетью. Подобным образом действует YOLO, SSD слегка отличается, но идея одна: 1х1 свёртка предсказывает из полученных feature map’ов много чисел по глубине, однако мы заранее договариваемся, какое число что значит.
![](https://habrastorage.org/webt/qj/ml/w_/qjmlw_ympdcib6jkpfdjdvcirdy.png)
![](https://habrastorage.org/webt/jr/i3/oy/jri3oymb48sv5vq9dwwdxbndszg.png)
Например, предсказываем из feature map’a размера 13х13х256 feature map 13х13х(4*(5+80)) чисел, где по глубине мы для 4 боксов предсказываем по 85 чисел: первые 4 числа в последовательности всегда — координаты бокса, 5-ое число — уверенность в боксе, и 80 чисел — вероятности каждого из классов (классификация). Это нужно, чтобы потом подать нужные числа в нужные лоссы и правильно обучить нейросеть.
![](https://habrastorage.org/webt/ao/xi/o2/aoxio2rty3hgpu2f8mduomvs9nu.png)
Хочу обратить внимание на то, что качество работы детектора напрямую зависит от качества работы нейросети для извлечения признаков (то есть backbone-нейросети). Обычно в этой роли выступает одна из архитектур, о которых я говорил в предыдущей статье (ResNet, SENet и др.), однако иногда авторы придумывают собственные более оптимальные архитектуры (например, Darknet-53 в YOLOv3) или модификации (например, Feature Pyramid Pooling (FPN)).
Снова отмечу, что мы обучаем сеть и на классификацию, и на регрессию одновременно. В сообществе это принято называть multi-task loss: в одном лоссе фигурирует сумма лоссов для нескольких задач (с некоторыми коэффициентами).
Новости с передовой про Multitask Loss
На Machines Can See 2019 один из докладчиков применял multi-task loss для 7-ми задач одновременно, Карл. Оказалось, что некоторые задачи были изначально поставлены в противовес друг другу и получился “конфликт”, что мешало сети учиться лучше, чем если обучать на каждую задачу по-отдельности. Вывод: если используете multi-task loss, убедитесь, что эти самые multi-task’и не конфликтуют по постановке (например, предсказание границ объектов и их внутренней сегментации может друг другу мешать, поскольку эти вещи могут опираться внутри сети на разные признаки). Автор же обошёл это с помощью добавления отдельных Squeeze-and-Excitation блоков для каждой задачи.
Недавно вышли статьи от 2019 года, в которых авторы заявляют о ещё лучшем соотношении скорость/точность в задаче детекции с помощью предсказания боксов на основе точек. Я говорю о статьях “Objects as Points” и “CornerNet-Lite”. ExtremeNet является модификацией CornerNet. Кажется, сейчас их можно назвать SOTA в детекции с помощью нейросетей (но это не точно).
UPD (25 апреля 2020): На данный момент среди детекторов значительно выделяется YOLOv4, в которой авторы указывают на наилучшее соотношение скорость-качество среди практически всех остальных нейросетевых детекторов.
![](https://habrastorage.org/webt/wk/qf/mr/wkqfmrenwm3u5f6c6bzinjcffga.png)
Если вдруг моё объяснение детекторов всё же показалось сумбурным и непонятным, в нашем видео я обсуждаю это не спеша. Возможно, сначала стоит посмотреть его.
Ниже я привёл таблицы нейросетей в детектировании со ссылками на код и кратким описанием фишек каждой сети. Я постарался собрать только те сети, которые действительно важно знать (по крайней мере, их идеи), чтобы иметь хорошее представление об object detection сегодня:
Нейросетевые детекторы (two-stage)
Год | Статья | Ключевая идея | Код |
---|---|---|---|
2013-2014 | RCNN | генерация регионов интереса и предсказание нейросетью классов внутри них | Caffe |
2015 | Fast-RCNN | сначала пропускать картинку через сеть, а потом генерировать регионы интереса | Caffe |
2016 | Faster-RCNN | использовать RPN для генерации регионов интереса | PyTorch |
2016 | R-FCN | fully-convolutional подход вместо генерации регионов интереса | Caffe |
2017 | Mask-RCNN | две «головы» для решения сразу двух задач, RoI-Align | Keras, TF |
2019 | Reasoning-RCNN | улучшение качества RCNN с помощью построения графа семантических связей объектов | --- |
Нейросетевые детекторы (one-stage)
Год | Статья | Ключевая идея | Код |
---|---|---|---|
2013-2014 | Overfeat | один из первых нейросетевых детекторов | C++ (с обёртками под другие языки) |
2015 | SSD | очень гибкий one-stage подход, использующийся сейчас во многих приложениях | PyTorch |
2015 | YOLO | схожая с SSD идея, развивается параллельно и не менее популярна (есть новые версии) | C++ |
2016 | YOLOv2 (aka YOLO9000) | ряд улучшений для YOLO | PyTorch |
2017 | YOLOv3 | ряд улучшений для YOLOv2 | PyTorch |
2017-2018 | DSOD | идея Deep Supervision и идеи из DenseNet | Caffe |
2017-2018 | RFBNet | фильтры свёрток аккуратно подобраны исходя из строения зрительной системы человека (RF-блок) | PyTorch |
2020 | YOLOv4 | Комбинация большого количества различных улучшений над YOLOv3 (см. статью) | С++ |
Нейросетевые детекторы (разное)
Год | Статья | Ключевая идея | Код |
---|---|---|---|
2018 | RetinaNet | специальный focal loss для решения проблемы дисбаланса классов | Keras |
2014-2015 | SPP | модуль, позволяющий эффективно работать с изображениями разных размеров | Keras |
2016-2017 | FPN | пирамиды признаков для лучшей детекции объектов разных размеров | TensorFlow |
2019 | NAS-FPN | поиск лучшей FPN с помощью Neural Architecture Search | TensorFlow |
2019 | Daedalus | как сломать детектор Adversarial-атакой | --- |
Нейросетевые детекторы (на основе точек)
Год | Статья | Ключевая идея | Код |
---|---|---|---|
2019 | CenterNet | новый подход к детекции, позволяющий качественно и быстро решать задачи нахождения точек, боксов и 3D-боксов одновременно | PyTorch |
2019 | CornerNet | предсказание боксов на основе пар угловых точек | PyTorch |
2019 | CornerNet-Lite | ускоренная CornerNet | PyTorch |
2019 | ExtremeNet | предсказание «экстремальных» точек объектов (геометрически точные границы) | PyTorch |
Для того, чтобы понять, как соотносятся скорость/качество каждой из архитектур, можно посмотреть в этот обзор или в более популярную его версию.
Архитектуры — это прекрасно, но детектирование является прежде всего практической задачей. “Не имей сто сетей, а имей хотя бы 1 работающую” — таков мой message. В таблице выше есть ссылки на код, но лично я редко сталкиваюсь с запуском детекторов непосредственно из репозиториев (по крайней мере, с целью дальнейшего деплоя в продакшн). Чаще для этого используется какая-либо библиотека, например, TensorFlow Object Detection API (см. практическую часть моего занятия) или библиотека от исследователей из CUHK. Предлагаю вашему вниманию очередную супер-таблицу (они ведь вам нравятся, да?):
Библиотеки для запуска моделей детектирования
Название | Авторы | Описание | Реализованные нейросети | Фреймворк |
---|---|---|---|---|
Detectron | Facebook AI Research | репозиторий от Facebook с кодом различных моделей для детекции и оценки позы | Все Region-based | Caffe2 |
TF Object Detection API | TensorFlow team | Очень много моделей, готовых к использованию (даны веса) | Все Region-based и SSD (с разными backbone'ами) | TensorFlow |
Darkflow | thtrieu | Реализации YOLO и YOLOv2, готовые к использованию | Все типы YOLO (с модификациями), кроме YOLOv3 | TensorFlow |
mmdetection | Open MMLab (CUHK) | Огромное количество детекторов на PyTorch, см. их статью | Практически все модели, кроме YOLO-семейства | PyTorch |
Darknet (modified) | AlexAB | Удобная реализация YOLOv3 с множеством улучшений оригинального репозитория | YOLOv3 | C++ |
Часто детектировать нужно объект только одного класса, но специфичного и весьма вариативного. Например, детектировать все лица на фото (для дальнейшей верификации/подсчёта людей), детектировать людей целиком (для ре-идентификации/подсчёта/трекинга) или детектировать текст на сцене (для OCR/перевода слов на фото). В целом, подход “обычной” детекции здесь до определённой степени сработает, но в каждой из этих подзадач есть свои трюки, чтобы улучшить качество.
Детектирование лиц: не пойман — не вор
![](https://habrastorage.org/webt/ic/ul/rp/iculrpbc7niyrdxg1yk_8r82nsw.jpeg)
Здесь появляется некоторая специфика, поскольку лица часто занимают достаточно малую часть изображения. Плюс люди не всегда смотрят в камеру, часто лицо видно лишь сбоку. Одним из первых подходов к распознаванию лиц был знаменитый детектор Виолы-Джонса на основе каскадов Хаара, изобретённый ещё в 2001 году.
![](https://habrastorage.org/webt/hc/tf/zn/hctfzn0xudbedi_aymhmlcwkamu.png)
Нейросети тогда
Для подобных более сложных случаев можно использовать dlib. Это C++-библиотека, в которой реализованиы многие алгоритмы зрения, в том числе для детектирования лиц.
Из нейросетевых подходов в детектировании лиц особенно значимым является Multi-task Cascaded CNN (MTCNN) (MatLab, TensorFlow). В целом, она и сейчас активно используется (в том же facenet).
![](https://habrastorage.org/webt/80/wn/vu/80wnvuf59poodmodzswcgjlyjt4.jpeg)
Идея MTCNN — использовать для предсказания положения лица и его особых точек три нейросети последовательно (поэтому и “каскад”). Особых точек лица в данном случае ровно 5: левый глаз, правый глаз, левый край губ, правый край губ и нос. Первая нейросеть из каскада (P-Net) используется для генерации потенциальных регионов лица. Вторая (R-Net) — для улучшения координат полученных боксов. Третья (O-Net) нейросеть ещё раз регрессирует координаты боксов и, помимо того, предсказывает 5 ключевых точек лица. Multi-task эта сеть потому, что решаются три задачи: регрессия точек боксов, классификация лицо/не лицо для каждого бокса и регрессия точек лица. Причём MTCNN делает это всё в real-time, то есть ей требуется менее 40 ms на одну картинку.
![](https://habrastorage.org/webt/5e/iz/d5/5eizd5lwag9umfo1ccypep42eik.jpeg)
Как, вы всё ещё не читаете статьи с ArXiv самостоятельно?
В таком случае рекомендую попробовать прочитать оригинальную статью про MTCNN, если уже имеете некоторый бекграунд в свёрточных сетях. Эта статья занимает всего 5 страниц, но в ней изложена вся нужная для понимания подхода информация. Попробуйте, вдруг затянет :)
Из современных State-of-the-Art можно отметить Dual Shot Face Detector (DSFD) и FaceBoxes. FaceBoxes имеет возможность быстрого запуска на CPU (!), а DSFD отличился лучшим качеством (вышел в апреле 2019 года). DSFD устроен посложнее, чем MTCNN, поскольку внутри сети используются специальный модуль для улучшения признаков (с dilated convolutions), две ветки их обработки и специальные типы лоссов. Кстати, с dilated convolutions мы ещё не раз столкнёмся в статьях про сегментацию в следующей части. Ниже пример работы DSFD (впечатляет, не правда ли?).
![](https://habrastorage.org/webt/xv/or/it/xvoritceua0_ocjteyvm4xvisbq.jpeg)
Чтобы научиться ещё и распознавать лица, не забудьте заглянуть в предыдущую статью серии, там я про это вкратце рассказал.
Много букв: детектирование (и распознавание) текста
![](https://habrastorage.org/webt/qn/v9/wo/qnv9woeqru3dioeennrkvalkjqg.png)
Обратите внимание на фото выше. Легко заметить, что, если предсказывать bounding box’ы, параллельные осям координат (как мы делали раньше), то получится весьма некачественно. Часто это оказывается весьма критично, если мы хотим, например, подать потом эти боксы на вход recognition-нейросети, которая по картинке будет предсказывать текст.
В таких случаях принято предсказывать повёрнутые bounding box’ы, или и вовсе ограничивать текст многоугольниками вместо прямоугольников, если он изогнутый (примеры ниже). С предсказанием повёрнутых боксов справляется, например, EAST-детектор.
![](https://habrastorage.org/webt/j3/hd/bj/j3hdbj-xozs4voec8ekiz4k1eo0.png)
Идея EAST-детектора в том, чтобы предсказывать не координаты углов боксов, а следующие три вещи:
- Text Score Map'ы (вероятность нахождения текста в каждом пикселе)
- Угол поворота каждого бокса
- Расстояния до границ прямоугольника для каждого пикселя
Таким образом, это больше напоминает задачу сегментации (выделения масок текста), нежели детектирования. Поясняющая картинка из arxiv-статьи:
![](https://habrastorage.org/webt/dj/vt/uu/djvtuu36dzinoratlumncsgb5t8.png)
Задача распознавания текста (а значит и его детектирования) весьма популярна, поэтому есть и аналоги:TextBoxes++ (Caffe) и SegLinks, однако EAST, на мой взгляд, наиболее прост и доступен.
После детектирования текста хочется сразу скормить его другой нейросети, чтобы распознать его и выдать строку символов. Здесь можно заметить интересную смену модальности — из картинок в текст. Бояться этого совсем не стоит, ведь всё зависит лишь от того, какова архитектура сети, что именно предсказывается на последнем слое и какой используется лосс. Например, MORAN (код на PyTorch) и ASTER (код на TensorFlow) вполне справляются с поставленной задачей.
![](https://habrastorage.org/webt/vt/ot/nc/vtotncz1d1zhhsiytuzmarta9ta.png)
В них нет чего-то сверхъестественного, однако очень грамотно используются сразу два принципиально разных типа нейросетей: CNN и RNN. Первое нужно для извлечения признаков из картинки, а второе для генерации текста. Подробнее на примере MORAN'а: ниже архитектура его распознающей сети.
![](https://habrastorage.org/webt/hu/qx/_2/huqx_2jakjcggsgacvhj9a8vce4.png)
Однако несмотря на повёрнутые боксы от EAST'а, сетям-распознавателям на вход всё равно приходит прямоугольная картинка, а значит текст внутри неё может занимать далеко не всё пространство. Для того, чтобы распознавателю было проще предсказать по картинке непосредственно текст на ней, можно преобразовать её определённым способом.
Можем применить афинное преобразование ко входной картинке, чтобы растянуть/повернуть текст. Этого можно добиться с помощью Spatial Transformet Network (STN), поскольку она самостоятельно выучивает подобные преобразования и легко встраивается в другие нейронные сети (кстати, можно подобное выравнивание для любой картинки делать, не только для текста). Ниже пример до/после STN.
![](https://habrastorage.org/webt/6y/z1/p9/6yz1p942ensgepwirqrtzpcnb7q.jpeg)
Подробно про STN здесь рассказывать нет смысла, поскольку есть замечательная статья на Хабре (картинка взята оттуда, спасибо автору) и код на PyTorch.
Но MORAN (та самая нейросеть для распознавания текста) поступает ещё умнее — она не ограничивается семейством афинных преобразований, а предсказывает для каждого пикселя входной картинки карту смещений по x и по y, таким образом добиваясь любого преобразования, которое улучшит обучение сети для распознавания. Этот метод называется rectification, то есть исправление картинки с помощью вспомогательной нейросети (rectifier'а). Ниже сравнение картинки после афинного преобразования и после ректификации:
![](https://habrastorage.org/webt/n4/az/du/n4azduih5p7wd9sx-tqhpgy-p20.png)
Однако помимо подходов к распознаванию текста «модульно» (сеть детекции -> сеть распознавания), есть end-to-end архитектуры: на входе картинка, а на выходе — детекции и распознанный внутри них текст. И всё это единым пайплайном, который обучается на обе задачи сразу. В этом направлении есть внушительная работа Fast Oriented Text Spotting with a Unified Network (FOTS) (код на PyTorch), где авторы также отмечают, что end-to-end подход в два раза быстрее, чем «детекция+распознавание». Ниже схема нейросети FOTS, особую роль играет блок RoiRotate, благодаря которому есть возможность «прокидывать градиенты» с сети для распознавания на нейросеть для детекции (это и правда сложнее, чем кажется).
![](https://habrastorage.org/webt/a5/xh/ch/a5xhchryrwf-zpfudielmab9pqu.png)
К слову, каждый год проходит конференция ICDAR, к которой приурочены несколько соревнований по распознаванию текста на самых различных изображениях.
Текущие проблемы в детектировании
На мой взгляд, главная проблема в детектировании сейчас не качество модели-детектора, а данные: их обычно долго и дорого размечать, особенно если классов, которые нужно детектировать, очень много (но кстати есть пример решения для 500 классов). Поэтому многие работы сейчас посвящены генерации как можно более правдоподобных данных «синтетически» и получения разметки “бесплатно”. Ниже картинка из
![](https://habrastorage.org/webt/6k/mn/wh/6kmnwhvc1pgahzwtjloa4dy9sbm.png)
Но всё же здорово, что теперь мы можем точно сказать, где на картинке что находиться. И если хотим, например, посчитать количество чего-то на кадре, то достаточно лишь задетектить это и выдать количество боксов. В детекции людей хорошо работает и обычная YOLO, просто главное подать много данных. Тот же Darkflow подойдёт, а класс «человек» встречается почти во всех крупных датасетах по детектированию. Так что если хотим посчитать с помощью камеры количество людей, которые прошли мимо, скажем, за одни сутки, или количество товаров, которые взял человек в магазине, просто задетектим и выдадим количество…
![](https://habrastorage.org/webt/m2/ts/pl/m2tsplbgrnmoauvlghy8ivq2jdm.jpeg)
Стоп. Но ведь если мы будем детектировать людей на каждом изображении с камеры, то мы можем посчитать их количество на одном кадре, а на двух — уже нет, поскольку не сможем сказать, где какой именно человек. Нам необходим алгоритм, который позволит считать именно уникальных людей в видеопотоке. Им может быть алгоритм ре-идентификации, но когда речь заходит про видео и детекцию, грех не использовать алгоритмы трекинга.
Видео и трекинг: единым потоком
До сих пор мы говорили только про задачи на картинках, но самое-то интересное происходит на видео. Чтобы решать то же распознавание действий, нам необходимо использовать не только так называемую пространственную (spatial) компоненту, но и временнУю (temporal), поскольку видео — это последовательность изображений во времени.
![](https://habrastorage.org/webt/qm/da/pa/qmdapao0kcytnxhorj7htm6susy.jpeg)
Трекинг — это аналог детектирования изображений, но для видео. То есть мы хотим научить сеть предсказывать не бокс на картинке, а треклет во времени (который есть по сути последовательность боксов). Ниже пример изображения, на котором показаны «хвосты» — треки этих людей на видео.
![](https://habrastorage.org/webt/xz/yv/c_/xzyvc_oumhrnf_-bmf8gz5l_hli.png)
Давайте подумаем, как можно решать задачу трекинга. Пусть есть видео, и его кадры #1 и #2. Рассмотрим пока что только один объект — трекаем один мячик. На кадре #1 мы можем использовать детектор, чтобы детектировать его. На втором тоже можем детектировать мячик, и если он там там один, то всё хорошо: говорим, что бокс на предыдущем кадре — это бокс того же мяча, что и на кадре #2. Так же можно продолжать и на остальные кадры, ниже gif из курса по зрению pyimagesearch.
![](https://habrastorage.org/webt/_w/et/ak/_wetakdpybemefhert6cxslqaju.gif)
Кстати, в целях экономии времени можем не запускать нейросеть на втором кадре, а просто “вырезать” бокс мяча из первого кадра и искать ровно такой же на втором кадре корреляцией или попиксельно. Такой подход утилизируют корреляционные трекеры, они считаются простыми и более-менее надёжными, если мы имеем дело с простыми случаями по типу “трекинг одного мяча перед камерой в пустой комнате”. Такую задачу ещё называют Visual Object Tracking. Ниже пример работы корреляционного трекера на примере одного человека.
![](https://habrastorage.org/webt/cv/fp/qd/cvfpqdd5ghelxfpucxrejxq6vpm.gif)
Однако если детекций/людей несколько, то нужно уметь сопоставлять боксы с кадра #1 и с кадра #2. Первая идея, которая приходит в голову — попробовать сопоставлять бокс тому, который имеет с ним наибольшую область пересечения (IoU). Правда, в случае нескольких перекрывающихся детекций такой трекер будет нестабилен, поэтому нужно использовать ещё больше информации.
![](https://habrastorage.org/webt/qx/i2/t1/qxi2t1ejpnir0h52ip-23zpf4ti.png)
Подход с IoU опирается лишь на «геометрические» признаки детекций, то есть просто пытается сопоставить их по близости на кадрах. Но ведь у нас в распоряжении целое изображение (даже два в данном случае), и мы можем использовать то, что внутри этих детекций — «визуальные» признаки. Плюс к этому мы имеем историю детекций для каждого человека, что позволяет более точно предсказывать его следующее положение на основе скорости и направления движения, это условно можно назвать «физические» признаки.
![](https://habrastorage.org/webt/mc/ay/se/mcaysee7xq25-j6hnvjalhfy5z0.gif)
Одним из первых real-time трекеров, который был вполне надёжен и умел справляться со сложными ситуациями, был опубликованный в 2016 году Simple Online and Realtime Traker (SORT) (код на Python). SORT не использовал какие-либо визуальные признаки и нейросети, а лишь оценивал ряд параметров каждого бокса на каждом кадре: текущую скорость (по x и по y отдельно) и размер (высота и ширина). Соотношение сторон (aspect ratio) бокса всегда берутся от самой первой детекции этого бокса. Далее скорости предсказываются с помощью фильтров Калмана (они вообще добро и свет в мире обработке сигналов), строится матрица пересечений боксов по IoU и детекции назначаются венгерским алгоритмом.
Если вам кажется, что математики уже стало многовато, то в этой статье всё доступно объясняется (это ж medium :).
Уже в 2017 году вышла модификация SORT'а в виде DeepSORT (код на TensorFlow). DeepSORT уже стал применять нейросеть для извлечения визуальных признаков, используя их для разрешения коллизий. Качество трекинга выросло — не зря он считается одним из лучших онлайн-трекеров сегодня.
![](https://habrastorage.org/webt/15/it/ny/15itnyht32oes3n-keaqk36jnpq.gif)
Область трекинга и правда активно развивается: есть и трекеры с сиамскими нейросетями, и трекеры с RNN. Следует держать руку на пульсе, ведь в любой день может выйти (или уже вышла) ещё более точная и быстрая архитектура. Кстати, за подобными вещами очень удобно следить на PapersWithCode, там всегда ссылки на статьи и код к ним (если он есть).
Послесловие
![](https://habrastorage.org/webt/jb/yh/bf/jbyhbf2ovu_ynurp_t_d9_hi4vg.jpeg)
Мы уже действительно многое пережили и многое узнали. Но компьютерное зрение — крайне обширная область, а я — крайне упрямый человек. Именно поэтому мы ещё увидимся в третьей статье этого цикла (будет ли она последней? Кто знает...), где более подробно обсудим сегментацию, оценку позы, распознавание действий на видео и генерацию описания по изображению с помощью нейросетей.
P.S. хочу выразить особую благодарность Вадиму Горбачёву за его ценные советы и комментарии при подготовке этой и предыдущей статьи.