Русский
Русский
English
Статистика
Реклама

Компьютерное зрение

Перевод Мы создали Web приложение для определения лиц и масок для Google Chrome

15.03.2021 10:06:02 | Автор: admin

Введение

Основная цель - обнаружение лица и маски в браузере, не используя бэкенд на Python. Это простое приложение WebApp / SPA, которое содержит только JS-код и может отправлять некоторые данные на серверную часть для следующей обработки. Но начальное обнаружение лица и маски выполняется на стороне браузера и никакой реализации Python для этого не требуется.

На данный момент приложение работает только в браузере Chrome.

В следующей статье я опишу более технические подробности реализации на основе наших исследований.

Есть 2 подхода, как можно это реализовать в браузере:

Оба они поддерживают WASM, WebGL и CPU бэкенд. Но мы будем сравнивать только WASM и WebGL, так как производительность на CPU очень низкая и не может быть использована в конечной реализации.

Демо тут

TensorFlow.js

На официальном сайте TensorFlow.js предлагает несколько обученных и готовых к использованию моделей с готовой постобработкой. Для обнаружения лиц в реальном времени мы взяли BlazeFace модель, онлайн демо доступно тут.

Больше информации о BlazeFace можно найти тут.

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

WASM (размер изображения для определения лица: 160x120px; размер изображения для определения маски: 64x64px)

WebGL (размер изображения для определения лица: 160x120px; размер изображения для определения маски: 64x64px)

Результаты производительности: получение кадров

Мы можем получать кадры с помощью соответствующих функций HTML API. Но и этот процесс занимает время. Поэтому нам нужно понимать, сколько времени мы потратим на такие действия. Ниже приведены соответствующие временные метрики.

Наша цель - обнаружить лицо как можно быстрее, поэтому мы должны получать каждый кадр стрима и обрабатывать его. Для этой задачи мы можем использовать requestAnimationFrame, который вызывается каждые 16,6мс или каждый фрейм.

Метод grabFrame() из интерфейса ImageCapture позволяет нам взять текущий кадр из видео в MediaStreamTrack и вернуть Promise, который вернет нам изображение в формате ImageBitmap.

Одиночный режим - это максимальная производительность при получении кадра. Режим синхронизации - это то, как часто мы можем получать кадр с распознаванием лиц.

Результаты производительности: определения лица и маски

Цветовая схема: < 6 fps красный, 7-12 fps оранжевый, 13-18 fps желтый, 19+ fps зеленый.

Результаты:

Мы не учитываем временные метрики при запуске приложения. Очевидно, что во время запуска приложения и первых запусков модели оно будет потреблять больше ресурсов и времени. Когда приложение находится в "теплом" режиме, только тогда стоит получать показатели производительности. Теплый режим в нашем случае - это просто позволить приложению проработать 5-10 секунд, а затем получать показатели производительности.

Возможные неточности в собранных временных показателях - до 50мс.

Модель BlazeFace была разработана специально для мобильных устройств и помогает достичь хорошей производительности при использовании TFLite на платформах Android и IOS (~ 50-200 FPS).

Больше информации тут.

Набор данных для переобучения модели с нуля недоступен (исследовательская группа Google не поделилась им).

BlazeFace содержит 2 модели:

  • Фронтальную камеру: размер изображения 128 x 128px, быстрее, но с меньшей точностью.

  • Задняя камера: размер изображения 256 x 256px, медленнее, но с высокой точностью.

Размер изображения

Исходное изображение может быть любого размера в соответствии с настройками камеры и бизнес требованиями. Но когда мы обрабатываем кадры для обнаружения лица и маски, размер исходного кадра изменяется до подходящего размера в зависимости от модели.

Изображение, используемое BlazeFace для распознавания лиц, имеет размер 128 x 128px. Исходное изображение будет изменено до этого размера с учетом его пропорций. Изображение, которое используется для обнаружения маски, имеет размер 64 x 64px.

Мы выбрали минимальные разрешения для обоих изображений с учетом требований к производительности и результатам. Такие минимальные изображения показали наилучшие результаты на ПК и мобильных устройствах. Мы используем изображения размером 64 x 64px для обнаружения маски, потому что размера 32 x 32px недостаточно для обнаружения маски с достаточной точностью.

Как получить лучшие изображения для анализа приложением?

Используя TensorFlow.js у нас есть следующие опции для получения лучшего изображения, чтоб использовать в приложении:

  • BlazeFace позволяет настроить точность определения лица. Мы установили этот показатель с высоким значением (> 0,9), чтобы избежать неожиданных положительных результатов (например "призраки" в темном помещении или затылок человека определяло как лицо).

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

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

Такие проверки могут быть следующими:

  1. Область лица должна находиться в калибровочной рамке в X%.

  2. Все ориентиры или их часть должны быть в калибровочной рамке.

  3. Ширина и высота области лица должны быть достаточными для дальнейшего анализа

  4. Проверки ширины / высоты области лица и ориентиров выполняются очень быстро на стороне клиента с помощью JS.

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

Размер моделей определения маски

Для определения маски мы использовали MobileNetV2 и MobileNetV3 с различными типами и мультипликаторами.

Мы предлагаем использовать легкие или сверхлегкие модели с TensorFlow.js в браузере (<3Mb). Основная причина в том, что WASM работает быстрее с такими моделями. Это указано в официальной документации, а также подтверждено нашими тестами производительности.

Дополнительные ресурсы

  • WASM JS бэкенд: ~60Kb

  • OpenCV.js: 1.6Mb

  • Наше SPA приложение (+TensorFlow.js): ~500Kb

  • BlazeFace модель: 466Kb

Для веб приложений время до взаимодействия (TTI) с ~3.5Mb JavaScript кода и бинарниками + JSON модели размером 1.5Mb to 6Mb будет >10 сек в холодном режиме; в прогретом режиме ожидается TTI - 4-5 сек.

Если использовать Web Worker (и OpenCV.js будет только в воркерах), это значительно уменьшит размер основного приложения до 800-900Kb. TTI будет 7-8 секунд в холодном режиме; в прогретом режиме <5 сек.

Возможные подходы к запуску моделей нейронных сетей в браузере

Однопоточная реализация

Это реализация по умолчанию для браузеров. Мы запускаем обе модели для обнаружения лиц и масок в одном потоке. Ключевым моментом является обеспечение хорошей производительности для обеих моделей для работы с любыми проблемами в одном потоке. Это текущий подход, который использовался для получения указанных выше показателей производительности.

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

Использование Web Workers для запуска моделей в разном контексте и распараллеливание в браузере

В основном потоке JS запускается модель BlazeFace для обнаружения лиц, а обнаружение маски выполняется в отдельном потоке через Web Worker. Благодаря такой реализации мы можем разделить обе запущенные модели и ввести параллельную обработку кадров в браузере. Это положительно повлияет на общее восприятие UX в приложении. Веб-воркеры будут загружать библиотеки TensorFlow.js и OpenCV.js, основной поток JS - только TensorFlow.js. Это означает, что основное приложение будет запускаться намного быстрее, и тем самым мы сможем значительно сократить время TTI в браузере. Обнаружение лиц будет запускаться чаще, это увеличит FPS процесса обнаружения лиц. В результате процесс обнаружения маски будет запускаться чаще, а также будет увеличиваться FPS этого процесса. Ожидаемые улучшения до ~ 20%. Это означает, что указанные в статье выше FPS и миллисекунды могут быть улучшены на это значение.

При таком подходе к запуску разных моделей в разных контекстах с помощью веб-воркеров мы можем запускать больше моделей нейронных сетей в браузере с хорошей производительностью. Основным фактором здесь будут аппаратные характеристики устройства, на котором это запускается.

Мы реализовали такой подход в приложении, и он работает. Но у нас есть некоторые технические проблемы с обратным вызовом postMessage, когда веб-воркер отправляет сообщение обратно в основной поток. По каким-то причинам он вводит дополнительную задержку (до 200мс на мобильных устройствах), которая убивает улучшение производительности, которого мы достигли с помощью распараллеливания (эта проблема актуальна только для чистого JS, после реализации на React.js эта проблема исчезла).

Результаты реализации с Web Workers

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

Тестовые параметры:mobileNetVersion=V3mobileNetVersionMultiplier = 0.75mobileNetVersionType = float16thumbnailSize=32pxbackend = wasm

В приложении мы запускаем BlazeFace в основном потоке и модель обнаружения маски в веб-воркере.

Измерения времени обработки Web Worker на разных устройствах:

Приведенные выше результаты демонстрируют, что по некоторым причинам время для отправки обратного вызова из Web Worker в основной поток зависит от модели, работающей или использующей метод TensorFlow browser.fromPixels в этом Web Worker. Если он запущен с моделью, обратный вызов в Mac OS отправляется ~ 27мс, если модель не запущена - 5мс. Эта разница в 22мс для Mac OS может привести к задержкам 100300мс на более слабых устройствах и повлияет на общую производительность приложения при использовании Web Worker. В настоящее время мы не понимаем, почему это происходит.

Как повысить точность и уменьшить количество ложных срабатываний?

Нам нужно иметь больше контекста, чтобы принять соответствующее решение, чтобы сообщить об обнаружении лица и отсутствии маски. Такой контекст - это ранее обработанные кадры и их состояние (лицо есть или нет, лицо есть или нет, маска есть или нет). Мы реализовали плавающий сегмент для управления таким дополнительным контекстом. Длина сегмента настраивается и зависит от текущего FPS, но в любом случае она не должна быть больше 200300мс, чтобы не было видимых задержек. Ниже приведена соответствующая схема, описывающая идею:

Заключение:

Чем мощнее устройство, тем лучше результаты производительности при измерении:

Для ПК мы могли получить следующие показатели:

  • Определение лица: > 30fps

  • Определение лица + определение маски: до 45fps

Для мобильных устройств мы могли получить следующие показатели:

  • Определение лица: от 2.5fps до 12-15fps в зависимости от мобильного устройства

  • Определение лица + определение маски: от 2 до 12fps в зависимости от мобильного устройства

  1. Обращаем внимание, что видео всегда в реальном времени и его производительность зависит от самого устройства, но всегда будет от 30 кадров в секунду.

  2. Для модели обнаружения маски в большинстве случаев лучшие результаты демонстрирует модель MobileNetV2 0.35, типы не влияют на метрики производительности.

  3. Размер модели обнаружения маски зависит от типов. Поскольку типы не влияют на показатели производительности, рекомендуется использовать модели uint16 или float16, чтобы иметь меньший размер модели в браузере и более быстрый TTI.

  4. Среда выполнения WASM показывает лучшие результаты по сравнению с WebGL для модели BlazeFace. Это соответствует официальной документации TensorFlow.js относительно производительности небольших моделей (<3Mb):

    Для большинства моделей серверная часть WebGL по-прежнему будет превосходить серверную часть WASM, однако WASM может быть быстрее для сверхлегких моделей (менее 3Mb и 60млн операций умножения и добавления). В этом случае преимущества распараллеливания GPU перевешиваются фиксированными накладными расходами на выполнение шейдеров WebGL.

  5. Время TTI всегда лучше для моделей WASM по сравнению с WebGL с той же конфигурацией моделей.

  6. Производительность среды выполнения и моделей TensorFlow.js можно повысить, используя расширение WASM для добавления инструкций SIMD, позволяющих векторизовать и выполнять несколько операций с плавающей точкой параллельно. Предварительные тесты показывают, что включение этих расширений обеспечивает ускорение в 23 раза по сравнению с WASM. Больше информации здесь. Это все еще экспериментальная функция, которая по умолчанию не поставляется со средой выполнения. Также после релиза он будет доступен во время выполнения по умолчанию или через дополнительные параметры конфигурации.

  7. На стороне клиента ожидаемый размер приложения составляет ~ 3,5Mb JS кода со всеми зависимостями, 466Kb модели BlazeFace, от 1,1Mb до 5,6Mb модели обнаружения маски. Ожидаемое время TTI для приложения составляет > 10 секунд для мобильных устройств в холодном режиме; в теплом режиме - ~ 5сек.

  8. При использовании веб-воркеров OpenCV.js может быть загружен только в веб-воркеры, это значительно снижает TTI для основного приложения.

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

  10. Также было замечено, что лучше, если устройства будут полностью заряжены и подключены к зарядному устройству во время работы. В этом случае производительность устройств по нашим наблюдениям лучше. Зарядка от USB не позволяет поддерживать батарею на одном уровне долго, поэтому следует использовать зарядку от розетки. Эту информацию следует принимать во внимание.

  11. На наш взгляд, текущему решению достаточно даже 4-5 кадров в секунду, чтобы обеспечить хорошее восприятие пользователем UX. Но важно не показывать на экране область лица или ориентиры, а управлять всем на видео / экране:

    • Выделяя на экране, когда мы обнаружили человека.

    • Информирование о маске / отсутствии маски с помощью текстовых сообщений или другого выделения экрана.

  12. При таком взаимодействии пользователя задержки между реальным временем и нашими метаданными на экране будут составлять 200300мс. Такие значения будут рассматриваться пользователями системы как некритические задержки.

Подробнее..

Scaled YOLO v4 самая лучшая нейронная сеть для обнаружения объектов на датасете MS COCO

07.12.2020 20:14:08 | Автор: admin

Scaled YOLO v4 является самой точной нейронной сетью (55.8% AP) на датасете Microsoft COCO среди всех опубликованных нейронных сетей на данный момент. А также является лучшей с точки зрения соотношения скорости к точности во всем диапазоне точности и скорости от 15 FPS до 1774 FPS. На данный момент это Top1 нейронная сеть для обнаружения объектов.

Scaled YOLO v4 обгоняет по точности нейронные сети:

  • Google EfficientDet D7x / DetectoRS or SpineNet-190 (self-trained on extra-data)
  • Amazon Cascade-RCNN ResNest200
  • Microsoft RepPoints v2
  • Facebook RetinaNet SpineNet-190




Мы показываем, что подходы YOLO и Cross-Stage-Partial (CSP) Network являются лучшими с точки зрения, как абсолютной точности, так и соотношения точности к скорости.

График Точности (вертикальная ось) и Задержки (горизонтальная ось) на GPU Tesla V100 (Volta) при batch=1 без использования TensorRT:



Даже при меньшем разрешении сети Scaled-YOLOv4-P6 (1280x1280) 30 FPS чуть точнее и в 3.7х раза быстрее, чем EfficientDetD7 (1536x1536) 8.2 FPS. Т.е. YOLOv4 эффективнее использует разрешение сети.

Scaled YOLO v4 лежит на кривой оптимальности по Парето какую бы другую нейронную сеть вы не взяли, всегда есть такая сеть YOLOv4, которая или точнее при той же скорости, или быстрее при той же точности, т.е. YOLOv4 является лучшей с точки зрения отношения скорости и точности.

Scaled YOLOv4 точнее и быстрее, чем нейронные сети:
  • Google EfficientDet D0-D7x
  • Google SpineNet S49s S143
  • Baidu Paddle-Paddle PP YOLO
  • И многие другие


Scaled YOLO v4 это серия нейронных сетей, созданная из улучшенной и отмасштабированной сети YOLOv4. Наша нейронная сеть была обучена с нуля без использования предобученных весов (Imagenet или любых других).

Рейтинг точности опубликованных нейронных сетей: paperswithcode.com/sota/object-detection-on-coco


Скорость нейронной сети YOLOv4-tiny достигает 1774 FPS на игровой видеокарте GPU RTX 2080Ti при использовании TensorRT+tkDNN (batch=4, FP16): github.com/ceccocats/tkDNN

YOLOv4-tiny может исполняться в real-time со скоростью 39 FPS / 25ms Latency на JetsonNano (416x416, fp16, batch=1) tkDNN/TensorRT:


Scaled YOLOv4 намного эффективнее использует ресурсы параллельных вычислителей, таких как GPU и NPU. Например, GPU V100 (Volta) имеет производительность: 14 TFLops 112 TFLops-TC images.nvidia.com/content/technologies/volta/pdf/tesla-volta-v100-datasheet-letter-fnl-web.pdf

Если мы будем тестировать обе модели на GPU V100 с batch=1, с параметрами --hparams=mixed_precision=true и без --tensorrt=FP32, то:

  • YOLOv4-CSP (640x640) 47.5% AP 70 FPS 120 BFlops (60 FMA)
    Исходя из BFlops, должно быть 933 FPS = (112 000 / 120), но в действительности мы получаем 70 FPS, т.е. используется 7.5% GPU = (70 / 933)
  • EfficientDetD3 (896x896) 47.5% AP 36 FPS 50 BFlops (25 FMA)
    Исходя из BFlops, должно быть 2240 FPS = (112 000 / 50), но в действительности мы получаем 36 FPS, т.е. используется 1.6% GPU = (36 / 2240)


Т.е. эффективность вычислительных операций на устройствах с массивными параллельными вычислениями типа GPU, используемых в YOLOv4-CSP (7.5 / 1.6) = в 4.7x раза лучше, чем эффективность операций, используемых в EfficientDetD3.

Обычно нейронные сети запускаются на CPU только в исследовательских задачах для более легкой отладки, а характеристика BFlops на данный момент имеет только академический интерес. В реальных задачах важны реальные скорость и точность, а не характеристики на бумаге. Реальная скорость YOLOv4-P6 в 3.7х раза выше, чем EfficientDetD7 на GPU V100. Поэтому почти всегда используются устройства с массовым параллелизмом GPU / NPU / TPU/ DSP с гораздо более оптимальными: скоростью, ценой и тепловыделением:

  • Embedded GPU (Jetson Nano/Nx)
  • Mobile-GPU/NPU/DSP (Bionic-NPU/Snapdragon-DSP/Mediatek-APU/Kirin-NPU/Exynos-GPU/...)
  • TPU-Edge (Google Coral/Intel Myriad/Mobileye EyeQ5/Tesla-motors TPU 144 TOPS-8bit)
  • Cloud GPU (nVidia A100/V100/TitanV)
  • Cloud NPU (Google-TPU, Huawei Ascend, Intel Habana, Qualcomm AI 100, ...)


Также при использовании нейронных сетей On Web обычно используется GPU через библиотеки WebGL, WebAssembly, WebGPU, for this case the size of the model can matter: github.com/tensorflow/tfjs#about-this-repo

Использование устройств и алгоритмов со слабым параллелизмом это тупиковый путь развития, т.к. уменьшать размер литографии меньше размера атома кремния для увеличения частоты процессора не получится:

  • Текущий лучший размер Литографии процессоров (Semiconductor device fabrication) равен 5 нанометров.
  • Размер кристаллической решетки кремния равен 0.5 нанометров.
  • Атомный радиус кремния равен 0.1 нанометра.

Решение это вычислители с массивным параллелизмом: на одном кристалле или на нескольких кристаллах, соединенных интерпозером. Поэтому крайне важно создавать нейронные сети, которые эффективно используют вычислители с массивным параллелизмом, такие как GPU и NPU.

Улучшения в Scaled YOLOv4 по сравнению с YOLOv4:
  • В Scaled YOLOv4 применяли оптимальные способы масштабирования сети для получения YOLOv4-CSP -> P5 -> P6 -> P7 сетей
  • Улучшенная архитектура сети: оптимизирован Backbone, а также в Neck (PAN) используются Cross-stage-partial (CSP) connections и Mish-активация
  • Во время обучения используется Exponential Moving Average (EMA) это частный случай SWA: pytorch.org/blog/pytorch-1.6-now-includes-stochastic-weight-averaging
  • Для каждого разрешения сети обучается отдельная нейронная сеть (в YOLOv4 обучали только одну нейронную сеть для всех разрешений)
  • Улучшены нормализаторы в [yolo] слоях
  • Изменены активации для Width и Height, что позволяет быстрее обучать сеть
  • Используется параметр [net] letter_box=1 (сохраняет соотношение сторон входного изображения) для сетей с большим разрешением (для всех кроме yolov4-tiny.cfg)


Архитектура нейронной сети Scaled-YOLOv4 (примеры трех сетей: P5, P6, P7):


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


Простейший пример CSP-соединения (слева обычная сеть, справа CSP-сеть):


Пример CSP-соединения в YOLOv4-CSP / P5 / P6 / P7
(слева обычная сеть, справа CSP-сеть использу):


В YOLOv4-tiny используются 2 CSP-соединения (используя Partial Concatenation)


YOLOv4 применяется в различных областях и задачах:

И во многих других задачах.

Имеются реализации на различных фреймворках:


Как скомпилировать и запустить Обнаружение объектов в облаке бесплатно:
  1. colab: colab.research.google.com/drive/12QusaaRj_lUwCGDvQNfICpa7kA7_a2dE
  2. video: www.youtube.com/watch?v=mKAEGSxwOAY


Как скомпилировать и запустить Обучение в облаке бесплатно:
  1. colab: colab.research.google.com/drive/1_GdoqCJWXsChrOiY8sZMr_zbr_fH-0Fg?usp=sharing
  2. video: youtu.be/mmj3nxGT2YQ


Также подход YOLOv4 может использоваться в других задачах, например, при обнаружении 3D объектов:


Подробнее..

Как мы обучили нейросеть генерировать тени на фотографии

25.12.2020 10:05:30 | Автор: admin

Привет, Хабр!

Я работаю Computer Vision Engineer в Everypixel и сегодня расскажу вам, как мы учили генеративно-состязательную сеть создавать тени на изображении.

Разрабатывать GAN не так трудно, как кажется на первый взгляд. В научном мире существует множество статей и публикаций на тему генеративно-состязательных сетей. В этой статье я покажу вам, как можно реализовать архитектуру нейросети и решение, предложенное в одной из научных статей. В качестве опорной статьи я выбрал ARShadowGAN публикация о GAN, генерирующей реалистичные тени для нового, вставленного в изображение объекта. Поскольку от оригинальной архитектуры я буду отклоняться, то дальше я буду называть своё решение ARShadowGAN-like.

Пример работы нейронной сети ARShadowGAN-likeПример работы нейронной сети ARShadowGAN-like

Вот что вам понадобится:

  • браузер;

  • опыт работы с Python;

  • гугл-аккаунт для того, чтобы работать в среде Google Colaboratory.

Описание генеративно-состязательной сети

Напомню, что генеративно-состязательная сеть состоит из двух сетей:

  • генератора, создающего изображение из входного шума (у нас генератор будет создавать тень, принимая изображение без тени и маску вставленного объекта);

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

Упрощённая схема ARShadowGAN-likeУпрощённая схема ARShadowGAN-like

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

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

Генератор

Генератор ARShadowGAN-like состоит из двух основных блоков: attention и shadow generation (SG).

Схема генератораСхема генератора

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

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

Архитектура модуля: U-Net, в котором 4 канала на входе (RGB-изображение без тени и маска вставленного объекта) и 2 канала на выходе (маска окклюдеров и соответствующих им теням).

Shadow generation самый важный блок в архитектуре всей сети. Его цель: создание 3-канальной маски тени. Он, аналогично attention, имеет U-Net-архитектуру с дополнительным блоком уточнения тени на выходе (refinement). На вход блоку поступает вся известная на данный момент информация: исходное изображение без тени (3 канала), маска вставленного объекта (1 канал) и выход attention блока маска соседних объектов (1 канал) и маска теней от них (1 канал). Таким образом, на вход модулю приходит 6-канальный тензор. На выходе 3 канала цветная маска тени для вставленного объекта.

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

Дискриминатор

В качестве дискриминатора возьмем дискриминатор от SRGAN. Он привлек своей небольшой, но достаточно мощной архитектурой, а также простотой реализации.

Таким образом, полная схема ARShadowGAN-like будет выглядеть примерно так (да, мелко, но крупным планом отдельные кусочки были показаны выше ):

Полная схема обучения ARShadowGAN-likeПолная схема обучения ARShadowGAN-like

О датасете

Обучение генеративно-состязательных сетей обычно бывает paired и unpaired.

С парными данными (paired) всё достаточно прозрачно: используется подход обучения с учителем, то есть имеется правильный ответ (ground truth), с которым можно сравнить выход генератора. Для обучения сети составляются пары изображений: исходное изображение измененное исходное изображение. Нейронная сеть учится генерировать из исходного изображения его модифицированную версию.

Непарное обучение подход обучения сети без учителя. Зачастую такой подход используется, когда получить парные данные либо невозможно, либо трудно. Например, unpaired обучение часто применяется в задаче Style Transfer перенос стиля с одного изображения на другое. Здесь вообще неизвестен правильный ответ, именно поэтому происходит обучение без учителя.

Пример Style TransferПример Style Transfer

Изображение взято здесь.

Вернемся к нашей задаче генерации теней. Авторы ARShadowGAN используют парные данные для обучения своей сети. Парами здесь являются изображение без тени соответствующее ему изображение с тенью.

Как же собрать такой датасет?

Вариантов здесь достаточно много, приведу некоторые из них:

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

  • Альтернативным подходом я вижу сбор датасета из других изображений с тенями. Логика такая: возьмем изображение с тенью и тень удалим. Отсюда вытекает другая, не менее лёгкая задача Image Inpainting восстановление вырезанных мест в изображении, либо опять же ручная работа в фотошопе. Кроме того, сеть может легко переобучиться на таком датасете, поскольку могут обнаружиться артефакты, которые не видны человеческому глазу, но заметны на более глубоком семантическом уровне.

  • Еще один способ сбор синтетического датасета с помощью 3D. Авторы ARShadowGAN пошли по этому пути и собрали ShadowAR-dataset. Идея следующая: сперва авторы выбрали несколько 3D-моделей из известной библиотеки ShapeNet, затем эти модели фиксировались в правильном положении относительно сцены. Далее запускался рендер этих объектов на прозрачном фоне с включенным источником освещения и выключенным с тенью и без тени. После этого рендеры выбранных объектов просто вставлялись на 2D-изображения сцен без дополнительных обработок. Так получили пары: исходное изображение без тени (noshadow) и ground truth изображение с тенью (shadow). Подробнее о сборе ShadowAR-dataset можно почитать в оригинальной статье.

Итак, пары изображений noshadow и shadow у нас есть. Откуда берутся маски?

Напомню, что масок у нас три: маска вставленного объекта, маска соседних объектов (окклюдеров) и маска теней от них. Маска вставленного объекта легко получаются после рендера объекта на прозрачном фоне. Прозрачный фон заливается черным цветом, все остальные области, относящиеся к нашему объекту, белым. Маски же соседних объектов и теней от них были получены авторами ARShadowGAN с помощью привлечения человеческой разметки (краудсорсинга).

Пример Shadow-AR датасета.Пример Shadow-AR датасета.

Функции потерь и метрики

Attention

В этом месте отклонимся от статьи возьмем функцию потерь для решения задачи сегментации.

Генерацию карт внимания (масок) можно рассматривать как классическую задачу сегментации изображений. В качестве функции потерь возьмем Dice Loss. Она хорошо устойчива по отношению к несбалансированным данным.

В качестве метрики возьмем IoU (Intersection over Union).

Подробнее о Dice Loss и IoU можно посмотреть здесь.

Shadow generation

Функцию потерь для блока генерации возьмем подобной той, что приведена в оригинальной статье. Она будет состоять из взвешенной суммы трёх функций потерь: L2, Lper и Ladv:

L2 будет оценивать расстояние от ground truth изображения до сгенерированных (до и после refinement-блока, обозначенного как R).

Lper (perceptual loss) функция потерь, вычисляющая расстояние между картами признаков сети VGG16 при прогоне через неё изображений. Разница считается стандартным MSE между ground truth изображением с тенью и сгенерированными изображениями до и после refinement-блока соответственно.

Ladv стандартный adversarial лосс, который учитывает соревновательный момент между генератором и дискриминатором. Здесь D(.) вероятность принадлежности к классу настоящее изображение. В ходе обучения генератор пытается минимизировать Ladv, в то время как дискриминатор, наоборот, пытается его максимизировать.

Подготовка

Установка необходимых модулей

Для реализации ARShadowGAN-like будет использоваться библиотека глубокого обучения для Python pytorch.

Используемые библиотеки: что для чего?

Работу начнём с установки необходимых модулей:

  • segmentation-models-pytorch для импорта U-Net архитектуры;

  • albumentations для аугментаций;

  • piq для импорта необходимой функции потерь;

  • matplotlib для отрисовки изображений внутри ноутбуков;

  • numpy для работы с массивами;

  • opencv-python для работы с изображениями;

  • tensorboard для визуализации графиков обучения;

  • torch для нейронных сетей и глубокого обучения;

  • torchvision для импорта моделей, для глубокого обучения;

  • tqdm для progress bar визуализации.

pip install segmentation-models-pytorch==0.1.0pip install albumentations==0.5.1pip install piq==0.5.1pip install matplotlib==3.2.1pip install numpy==1.18.4pip install opencv-python>=3.4.5.20pip install tensorboard==2.2.1pip install torch>=1.5.0pip install torchvision>=0.6.0pip install tqdm>=4.41.1

Датасет

Датасет: структура, скачивание, распаковка

Для обучения и тестирования я буду использовать готовый датасет. В нём данные уже разбиты на train и test выборки. Скачаем и распакуем его.

unzip shadow_ar_dataset.zip

Структура папок в наборе данных следующая.

Каждая из выборок содержит 5 папок с изображениями:
- noshadow (изображения без теней);
- shadow (изображения с тенями);
- mask (маски вставленных объектов);
- robject (соседние объекты или окклюдеры);
- rshadow (тени от соседних объектов).

dataset train    noshadow  example1.png, ...    shadow  example1.png, ...    mask  example1.png, ...    robject  example1.png, ...    rshadow  example1.png, ... test     noshadow  example2.png, ...     shadow  example2.png, ...     mask  example2.png, ...     robject  example2.png, ...     rshadow  example2.png, ...

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

Итак, подготовим класс ARDataset для обработки изображений и выдачи i-ой порции данных по запросу.

Импорт библиотек
import osimport os.path as ospimport cv2import randomimport numpy as npimport albumentations as albuimport torchimport torch.nn as nnfrom torch.utils.data import Dataset, DataLoaderfrom torch.autograd import Variablefrom piq import ContentLossimport segmentation_models_pytorch as smp

Далее определим сам класс. Основная функция в классе __getitem__() . Она возвращает i-ое изображение и соответствующую ему маску по запросу.

Класс ARDataset
class ARDataset(Dataset):    def __init__(self, dataset_path, augmentation=None, \                 augmentation_images=None, preprocessing=None, \                 is_train=True, ):        """ Инициализация параметров датасета        dataset_path - путь до папки train или test        augmentation - аугментации, применяемые как к изображениям, так и                       к маскам        augmentation_images - аугментации, применяемые только к         изображениям        preprocessing - предобработка изображений        is_train - флаг [True - режим обучения / False - режим предсказания]        """        noshadow_path = os.path.join(dataset_path, 'noshadow')        mask_path = os.path.join(dataset_path, 'mask')        # соберём пути до файлов        self.noshadow_paths = []; self.mask_paths = [];        self.rshadow_paths = []; self.robject_paths = [];        self.shadow_paths = [];        if is_train:            rshadow_path = osp.join(dataset_path, 'rshadow')            robject_path = osp.join(dataset_path, 'robject')            shadow_path = osp.join(dataset_path, 'shadow')        files_names_list = sorted(os.listdir(noshadow_path))        for file_name in files_names_list:            self.noshadow_paths.append(osp.join(noshadow_path, file_name))            self.mask_paths.append(osp.join(mask_path, file_name))            if is_train:                self.rshadow_paths.append(osp.join(rshadow_path, file_name))                self.robject_paths.append(osp.join(robject_path, file_name))                self.shadow_paths.append(osp.join(shadow_path, file_name))        self.augmentation = augmentation        self.augmentation_images = augmentation_images        self.preprocessing = preprocessing        self.is_train = is_train    def __getitem__(self, i):        """ Получение i-го набора из датасета.        i - индекс        Возвращает:        image - изображение с нормализацией для attention блока        mask - маска с нормализацией для attention блока        image1 - изображение с нормализацией для shadow generation блока        mask1 - маска с нормализацией для shadow generaion блока        """        # исходное изображение        image = cv2.imread(self.noshadow_paths[i])        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)        # маска вставленного объекта        mask = cv2.imread(self.mask_paths[i], 0)        if self.is_train:            # маска соседних объектов            robject_mask = cv2.imread(self.robject_paths[i], 0)            # маска теней от соседних объектов            rshadow_mask = cv2.imread(self.rshadow_paths[i], 0)            # результирующее изображение            res_image = cv2.imread(self.shadow_paths[i])            res_image = cv2.cvtColor(res_image, cv2.COLOR_BGR2RGB)            # применяем аугментации отдельно к изображениям            if self.augmentation_images:                sample = self.augmentation_images(                  image=image,                   image1=res_image                )                image = sample['image']                res_image = sample['image1']            # соберём маски в одну переменную для применения аугментаций            mask = np.stack([robject_mask, rshadow_mask, mask], axis=-1)            mask = mask.astype('float')            # аналогично с изображениями            image = np.concatenate([image, res_image], axis=2)            image = image.astype('float')        # применяем аугментации        if self.augmentation:            sample = self.augmentation(image=image, mask=mask)            image, mask = sample['image'], sample['mask']        # нормализация масок        mask[mask >= 128] = 255; mask[mask < 128] = 0        # нормализация для shadow generation блока        image1, mask1 = image.astype(np.float) / 127.5 - 1.0, \                        mask.astype(np.float) / 127.5 - 1.0        # нормализация для attention блока        image, mask = image.astype(np.float) / 255.0, \                      mask.astype(np.float) / 255.0        # применяем препроцессинг        if self.preprocessing:            sample = self.preprocessing(image=image, mask=mask)            image, mask = sample['image'], sample['mask']            sample = self.preprocessing(image=image1, mask=mask1)            image1, mask1 = sample['image'], sample['mask']        return image, mask, image1, mask1    def __len__(self):        """ Возвращает длину датасета"""        return len(self.noshadow_paths)

Объявим аугментации и функции для обработки данных. Аугментации будем брать из репозитория albumentations.

Аугментации и предобработка
def get_training_augmentation():    """ Аугментации для всех изображений, тренировочная выборка. """    train_transform = [        albu.Resize(256,256),        albu.HorizontalFlip(p=0.5),        albu.Rotate(p=0.3, limit=(-10, 10), interpolation=3, border_mode=2),    ]    return albu.Compose(train_transform)def get_validation_augmentation():    """ Аугментации для всех изображений, валидационная / тестовая выборка """    test_transform = [        albu.Resize(256,256),    ]    return albu.Compose(test_transform)def get_image_augmentation():    """ Аугментации только для изображений (не для масок). """    image_transform = [        albu.OneOf([          albu.Blur(p=0.2, blur_limit=(3, 5)),          albu.GaussNoise(p=0.2, var_limit=(10.0, 50.0)),          albu.ISONoise(p=0.2, intensity=(0.1, 0.5), \                        color_shift=(0.01, 0.05)),          albu.ImageCompression(p=0.2, quality_lower=90, quality_upper=100, \                                compression_type=0),          albu.MultiplicativeNoise(p=0.2, multiplier=(0.9, 1.1), \                                   per_channel=True, \                                   elementwise=True),        ], p=1),        albu.OneOf([          albu.HueSaturationValue(p=0.2, hue_shift_limit=(-10, 10), \                                  sat_shift_limit=(-10, 10), \                                  val_shift_limit=(-10, 10)),          albu.RandomBrightness(p=0.3, limit=(-0.1, 0.1)),          albu.RandomGamma(p=0.3, gamma_limit=(80, 100), eps=1e-07),          albu.ToGray(p=0.1),          albu.ToSepia(p=0.1),        ], p=1)    ]    return albu.Compose(image_transform, additional_targets={        'image1': 'image',        'image2': 'image'    })def get_preprocessing():    """ Препроцессинг """    _transform = [        albu.Lambda(image=to_tensor, mask=to_tensor),    ]    return albu.Compose(_transform)def to_tensor(x, **kwargs):    """ Приводит изображение в формат: [channels, width, height] """    return x.transpose(2, 0, 1).astype('float32')

Обучение

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

Датасеты, даталоадеры, девайс
# число изображений, прогоняемых через нейросеть за один разbatch_size = 8dataset_path = '/path/to/your/dataset'train_path = osp.join(dataset_path, 'train')test_path = osp.join(dataset_path, 'test')# объявим датасетыtrain_dataset = ARDataset(train_path,\                          augmentation=get_training_augmentation(),\                          preprocessing=get_preprocessing(),)valid_dataset = ARDataset(test_path, \                          augmentation=get_validation_augmentation(),\                          preprocessing=get_preprocessing(),)# объявим даталоадерыtrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)valid_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)

Определим устройство, на котором будем учить сеть:

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

Будем учить attention и shadow generation блоки по отдельности.

Обучение attention блока

В качестве модели attention блока возьмём U-Net. Архитектуру импортируем из репозитория segmentation_models.pytorch. Для повышения качества работы сети заменим стандартную кодирующую часть U-Net на сеть-классификатор resnet34.

Поскольку на вход attention блок принимает изображение без тени и маску вставленного объекта, то заменим первый сверточный слой в модели: на вход модулю поступает 4-канальный тензор (3 цветных канала + 1 черно-белый).

# объявим модель Unet с 2 классами на выходе - 2 маски (соседние объекты и их тени)model = smp.Unet(encoder_name='resnet34', classes=2, activation='sigmoid',)# заменим в модели первый сверточный слой - на входе должно быть 4 каналаmodel.encoder.conv1 = nn.Conv2d(4, 64, kernel_size=(7, 7), stride=(2, 2), \                                padding=(3, 3), bias=False)

Объявим функцию потерь, метрику и оптимизатор.

loss = smp.utils.losses.DiceLoss()metric = smp.utils.metrics.IoU(threshold=0.5)optimizer = torch.optim.Adam([dict(params=model.parameters(), lr=1e-4),])

Создадим функцию для обучения attention блока. Обучение стандартное, состоит из трех циклов: цикла по эпохам, тренировочного цикла по батчам и валидационного цикла по батчам.

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

Функция для обучения attention и её вызов
def train(n_epoch, train_loader, valid_loader, model_path, model, loss,\          metric, optimizer, device):    """ Функция обучения сети.    n_epoch -- число эпох    train_loader -- даталоадер для тренировочной выборки    valid_loader -- даталоадер для валидационной выборки    model_path -- путь для сохранения модели    model -- предварительно объявленная модель    loss -- функция потерь    metric -- метрика    optimizer -- оптимизатор    device -- определенный torch.device    """    model.to(device)    max_score = 0    total_train_steps = len(train_loader)    total_valid_steps = len(valid_loader)    # запускаем цикл обучения    print('Start training!')    for epoch in range(n_epoch):        # переведём модель в режим тренировки        model.train()        train_loss = 0.0        train_metric = 0.0        # тренировочный цикл по батчам        for data in train_loader:            noshadow_image = data[0][:, :3].to(device)            robject_mask = torch.unsqueeze(data[1][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[1][:, 1], 1).to(device)            mask = torch.unsqueeze(data[1][:, 2], 1).to(device)            # прогоним через модель            model_input = torch.cat((noshadow_image, mask), axis=1)            model_output = model(model_input)            # сравним выход модели с ground truth данными            ground_truth = torch.cat((robject_mask, rshadow_mask), axis=1)            loss_result = loss(ground_truth, model_output)            train_metric += metric(ground_truth, model_output).item()            optimizer.zero_grad()            loss_result.backward()            optimizer.step()            train_loss += loss_result.item()        # переведём модель в eval-режим        model.eval()        valid_loss = 0.0        valid_metric = 0.0        # валидационный цикл по батчам        for data in valid_loader:            noshadow_image = data[0][:, :3].to(device)            robject_mask = torch.unsqueeze(data[1][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[1][:, 1], 1).to(device)            mask = torch.unsqueeze(data[1][:, 2], 1).to(device)            # прогоним через модель            model_input = torch.cat((noshadow_image, mask), axis=1)            with torch.no_grad():                model_output = model(model_input)            # сравним выход модели с ground truth данными            ground_truth = torch.cat((robject_mask, rshadow_mask), axis=1)            loss_result = loss(ground_truth, model_output)            valid_metric += metric(ground_truth, model_output).item()            valid_loss += loss_result.item()        train_loss = train_loss / total_train_steps        train_metric = train_metric / total_train_steps        valid_loss = valid_loss / total_valid_steps        valid_metric = valid_metric / total_valid_steps        print(f'\nEpoch {epoch}, train_loss: {train_loss}, train_metric: {train_metric}, valid_loss: {valid_loss}, valid_metric: {valid_metric}')        # если получили новый максимум по точности - сохраняем модель        if max_score < valid_metric:            max_score = valid_metric            torch.save(model.state_dict(), model_path)            print('Model saved!')# вызовем функцию:# число эпохn_epoch = 10# путь для сохранения моделиmodel_path = '/path/for/model/saving' train(n_epoch=n_epoch,      train_loader=train_loader,      valid_loader=valid_loader,      model_path=model_path,      model=model,      loss=loss,      metric=metric,      optimizer=optimizer,      device=device)

После того, как обучение attention блока закончено, приступим к основной части сети.

Обучение shadow generation блока

В качестве модели shadow generation блока аналогично возьмём U-Net, только в качестве кодировщика возьмем сеть полегче resnet18.

Поскольку на вход shadow generation блок принимает изображение без тени и 3 маски (маску вставленного объекта, маску соседних объектов и маску теней от них), заменим первый сверточный слой в модели: на вход модулю поступает 6-канальный тензор (3 цветных канала + 3 черно-белых).

После U-Net добавим в конце 4 refinement-блока. Один такой блок состоит из последовательности: BatchNorm2d, ReLU и Conv2d.

Объявим класс генератор.

Класс генератор
class Generator_with_Refin(nn.Module):    def __init__(self, encoder):        """ Инициализация генератора."""        super(Generator_with_Refin, self).__init__()        self.generator = smp.Unet(            encoder_name=encoder,            classes=1,            activation='identity',        )        self.generator.encoder.conv1 = nn.Conv2d(6, 64, kernel_size=(7, 7), \                                                 stride=(2, 2), padding=(3, 3), \                                                 bias=False)        self.generator.segmentation_head = nn.Identity()        self.SG_head = nn.Conv2d(in_channels=16, out_channels=3, \                                 kernel_size=3, stride=1, padding=1)        self.refinement = torch.nn.Sequential()        for i in range(4):            self.refinement.add_module(f'refinement{3*i+1}', nn.BatchNorm2d(16))            self.refinement.add_module(f'refinement{3*i+2}', nn.ReLU())            refinement3 = nn.Conv2d(in_channels=16, out_channels=16, \                                    kernel_size=3, stride=1, padding=1)            self.refinement.add_module(f'refinement{3*i+3}', refinement3)        self.output1 = nn.Conv2d(in_channels=16, out_channels=3, kernel_size=3, \                                 stride=1, padding=1)    def forward(self, x):      """ Прямой проход данных через сеть."""        x = self.generator(x)        out1 = self.SG_head(x)        x = self.refinement(x)        x = self.output1(x)        return out1, x

Объявим класс дискриминатор.

Класс дискриминатор
class Discriminator(nn.Module):    def __init__(self, input_shape):        super(Discriminator, self).__init__()        self.input_shape = input_shape        in_channels, in_height, in_width = self.input_shape        patch_h, patch_w = int(in_height / 2 ** 4), int(in_width / 2 ** 4)        self.output_shape = (1, patch_h, patch_w)        def discriminator_block(in_filters, out_filters, first_block=False):            layers = []            layers.append(nn.Conv2d(in_filters, out_filters, kernel_size=3, \                                    stride=1, padding=1))            if not first_block:                layers.append(nn.BatchNorm2d(out_filters))            layers.append(nn.LeakyReLU(0.2, inplace=True))            layers.append(nn.Conv2d(out_filters, out_filters, kernel_size=3, \                                    stride=2, padding=1))            layers.append(nn.BatchNorm2d(out_filters))            layers.append(nn.LeakyReLU(0.2, inplace=True))            return layers        layers = []        in_filters = in_channels        for i, out_filters in enumerate([64, 128, 256, 512]):            layers.extend(discriminator_block(in_filters, out_filters, \                                              first_block=(i == 0)))            in_filters = out_filters        layers.append(nn.Conv2d(out_filters, 1, kernel_size=3, stride=1, \                                padding=1))        self.model = nn.Sequential(*layers)    def forward(self, img):        return self.model(img)

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

Генератор, дискриминатор, функции потерь, оптимизаторы
generator = Generator_with_Refin('resnet18')discriminator = Discriminator(input_shape=(3,256,256))l2loss = nn.MSELoss()perloss = ContentLoss(feature_extractor="vgg16", layers=("relu3_3", ))GANloss = nn.MSELoss()optimizer_G = torch.optim.Adam([dict(params=generator.parameters(), lr=1e-4),])optimizer_D = torch.optim.Adam([dict(params=discriminator.parameters(), lr=1e-6),])

Всё готово для обучения, определим функцию для обучения SG блока. Её вызов будет аналогичен вызову функции обучения attention.

Функция для обучения SG блока
def train(generator, discriminator, device, n_epoch, optimizer_G, optimizer_D, train_loader, valid_loader, scheduler, losses, models_paths, bettas, writer):    """Функция для обучения SG-блока        generator: модель-генератор        discriminator: модель-дискриминатор        device: torch-device для обучения        n_epoch: количество эпох        optimizer_G: оптимизатор для модели-генератора        optimizer_D: оптимизатор для модели-дискриминатора        train_loader: даталоадер для тренировочной выборки        valid_loader: даталоадер для валидационной выборки        scheduler: шедуллер для изменения скорости обучения        losses:  список функций потерь        models_paths: список путей для сохранения моделей        bettas: список коэффициентов для функций потерь        writer: tensorboard writer    """    # перенесем модели на ГПУ    generator.to(device)    discriminator.to(device)    # для валидационного минимума    val_common_min = np.inf    print('Запускаем обучение!')    for epoch in range(n_epoch):        # переводим модели в режим обучения        generator.train()        discriminator.train()        # списки для значений функций потерь        train_l2_loss = []; train_per_loss = []; train_common_loss = [];         train_D_loss = []; valid_l2_loss = []; valid_per_loss = [];         valid_common_loss = [];        print('Цикл по батчам (пакетам):')        for batch_i, data in enumerate(tqdm(train_loader)):            noshadow_image = data[2][:, :3].to(device)            shadow_image = data[2][:, 3:].to(device)            robject_mask = torch.unsqueeze(data[3][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[3][:, 1], 1).to(device)            mask = torch.unsqueeze(data[3][:, 2], 1).to(device)            # подготовим входной тензор для модели            model_input = torch.cat((noshadow_image, mask, robject_mask, rshadow_mask), axis=1)            # ------------ учим генератор -------------------------------------            shadow_mask_tensor1, shadow_mask_tensor2 = generator(model_input)            result_nn_tensor1 = torch.add(noshadow_image, shadow_mask_tensor1)            result_nn_tensor2 = torch.add(noshadow_image, shadow_mask_tensor2)            for_per_shadow_image_tensor = torch.sigmoid(shadow_image)            for_per_result_nn_tensor1 = torch.sigmoid(result_nn_tensor1)            for_per_result_nn_tensor2 = torch.sigmoid(result_nn_tensor2)            # Adversarial ground truths            valid = Variable(torch.cuda.FloatTensor(np.ones((data[2].size(0), *discriminator.output_shape))), requires_grad=False)            fake = Variable(torch.cuda.FloatTensor(np.zeros((data[2].size(0), *discriminator.output_shape))), requires_grad=False)            # вычисляем функции потерь            l2_loss = losses[0](shadow_image, result_nn_tensor1) + losses[0](shadow_image, result_nn_tensor2)            per_loss = losses[1](for_per_shadow_image_tensor, for_per_result_nn_tensor1) + losses[1](for_per_shadow_image_tensor, for_per_result_nn_tensor2)            gan_loss = losses[2](discriminator(result_nn_tensor2), valid)            common_loss = bettas[0] * l2_loss + bettas[1] * per_loss + bettas[2] * gan_loss            optimizer_G.zero_grad()            common_loss.backward()            optimizer_G.step()            # ------------ учим дискриминатор ---------------------------------            optimizer_D.zero_grad()            loss_real = losses[2](discriminator(shadow_image), valid)            loss_fake = losses[2](discriminator(result_nn_tensor2.detach()), fake)            loss_D = (loss_real + loss_fake) / 2            loss_D.backward()            optimizer_D.step()            # ------------------------------------------------------------------            train_l2_loss.append((bettas[0] * l2_loss).item())            train_per_loss.append((bettas[1] * per_loss).item())            train_D_loss.append((bettas[2] * loss_D).item())            train_common_loss.append(common_loss.item())        # переводим generator в eval-режим        generator.eval()        # валидация        for batch_i, data in enumerate(valid_loader):            noshadow_image = data[2][:, :3].to(device)            shadow_image = data[2][:, 3:].to(device)            robject_mask = torch.unsqueeze(data[3][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[3][:, 1], 1).to(device)            mask = torch.unsqueeze(data[3][:, 2], 1).to(device)            # подготовим вход в для модели            model_input = torch.cat((noshadow_image, mask, robject_mask, rshadow_mask), axis=1)            with torch.no_grad():                shadow_mask_tensor1, shadow_mask_tensor2 = generator(model_input)            result_nn_tensor1 = torch.add(noshadow_image, shadow_mask_tensor1)            result_nn_tensor2 = torch.add(noshadow_image, shadow_mask_tensor2)            for_per_result_shadow_image_tensor = torch.sigmoid(shadow_image)            for_per_result_nn_tensor1 = torch.sigmoid(result_nn_tensor1)            for_per_result_nn_tensor2 = torch.sigmoid(result_nn_tensor2)            # вычисляем функции потерь            l2_loss = losses[0](shadow_image, result_nn_tensor1) + losses[0](shadow_image, result_nn_tensor2)            per_loss = losses[1](for_per_result_shadow_image_tensor, for_per_result_nn_tensor1) + losses[1](for_per_result_shadow_image_tensor, for_per_result_nn_tensor2)            common_loss = bettas[0] * l2_loss + bettas[1] * per_loss            valid_per_loss.append((bettas[1] * per_loss).item())            valid_l2_loss.append((bettas[0] * l2_loss).item())            valid_common_loss.append(common_loss.item())        # усредняем значения функций потерь        tr_l2_loss = np.mean(train_l2_loss)        val_l2_loss = np.mean(valid_l2_loss)        tr_per_loss = np.mean(train_per_loss)        val_per_loss = np.mean(valid_per_loss)        tr_common_loss = np.mean(train_common_loss)        val_common_loss = np.mean(valid_common_loss)        tr_D_loss = np.mean(train_D_loss)        # добавляем результаты в tensorboard        writer.add_scalar('tr_l2_loss', tr_l2_loss, epoch)        writer.add_scalar('val_l2_loss', val_l2_loss, epoch)        writer.add_scalar('tr_per_loss', tr_per_loss, epoch)        writer.add_scalar('val_per_loss', val_per_loss, epoch)        writer.add_scalar('tr_common_loss', tr_common_loss, epoch)        writer.add_scalar('val_common_loss', val_common_loss, epoch)        writer.add_scalar('tr_D_loss', tr_D_loss, epoch)        # печатаем информацию        print(f'\nEpoch {epoch}, tr_common loss: {tr_common_loss:.4f}, val_common loss: {val_common_loss:.4f}, D_loss {tr_D_loss:.4f}')        if val_common_loss <= val_common_min:            # сохраняем лучшую модель            torch.save(generator.state_dict(), models_paths[0])            torch.save(discriminator.state_dict(), models_paths[1])            val_common_min = val_common_loss            print(f'Model saved!')        # делаем шаг шедуллера        scheduler.step(val_common_loss)

Процесс обучения

Визуализация процесса обучения Визуализация процесса обучения

Графики, общая информация

Для обучения я использовал видеокарту GTX 1080Ti на сервере hostkey. В процессе я отслеживал изменение функций потерь по построенным графикам с помощью утилиты tensorboard. Ниже, на рисунках, представлены графики обучения на тренировочной и валидационной выборке.

Графики обучения тренировочная выборкаГрафики обучения тренировочная выборка

Особенно полезен второй рисунок, поскольку валидационная выборка не участвует в процессе обучения генератора и является независимой. По графикам обучения видно, что выход на плато произошел в районе 200-250-й эпохи. Здесь можно было уже тормозить обучение генератора, поскольку монотонность у функции потерь отсутствует.

Однако полезно также смотреть на графики обучения в логарифмической шкале она более наглядно показывает монотонность графика. По графику логарифма валидационной функции потерь видим, что обучение в районе 200-250-й эпохи останавливать рановато, можно было сделать это позже, на 400-й эпохе.

Графики обучения валидационная выборкаГрафики обучения валидационная выборка

Для наглядности эксперимента периодически происходило сохранение предсказанной картинки (см. гифку визуализации процесса обучения выше).

Некоторые трудности

В процессе обучения пришлось решить достаточно простую проблему неправильное взвешивание функций потерь.

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

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

Пример сгенерированной тени в случае отсутствия вклада L2-лоссаПример сгенерированной тени в случае отсутствия вклада L2-лосса

На картинке слева ground truth изображение, справа сгенерированное изображение.

Инференс

Для предсказания и тестирования объединим модели attention и SG в один класс ARShadowGAN.

Класс ARShadowGAN, объединяющий attention и shadow generation блоки
class ARShadowGAN(nn.Module):    def __init__(self, model_path_attention, model_path_SG, encoder_att='resnet34', \                 encoder_SG='resnet18', device='cuda:0'):        super(ARShadowGAN, self).__init__()        self.device = torch.device(device)        self.model_att = smp.Unet(            classes=2,            encoder_name=encoder_att,            activation='sigmoid'        )        self.model_att.encoder.conv1 = nn.Conv2d(4, 64, kernel_size=(7,7), stride=(2,2), padding=(3,3), bias=False)        self.model_att.load_state_dict(torch.load(model_path_attention))        self.model_att.to(device)        self.model_SG = Generator_with_Refin(encoder_SG)        self.model_SG.load_state_dict(torch.load(model_path_SG))        self.model_SG.to(device)    def forward(self, tensor_att, tensor_SG):        self.model_att.eval()        with torch.no_grad():            robject_rshadow_tensor = self.model_att(tensor_att)        robject_rshadow_np = robject_rshadow_tensor.cpu().numpy()        robject_rshadow_np[robject_rshadow_np >= 0.5] = 1        robject_rshadow_np[robject_rshadow_np < 0.5] = 0        robject_rshadow_np = 2 * (robject_rshadow_np - 0.5)        robject_rshadow_tensor = torch.cuda.FloatTensor(robject_rshadow_np)        tensor_SG = torch.cat((tensor_SG, robject_rshadow_tensor), axis=1)        self.model_SG.eval()        with torch.no_grad():            output_mask1, output_mask2 = self.model_SG(tensor_SG)        result = torch.add(tensor_SG[:,:3, ...], output_mask2)        return result, output_mask2

Далее приведём сам код инференса.

Инференс
# укажем пути до данных и чекпоинтовdataset_path = '/content/arshadowgan/uploaded'result_path = '/content/arshadowgan/uploaded/shadow'path_att = '/content/drive/MyDrive/ARShadowGAN-like/attention.pth'path_SG = '/content/drive/MyDrive/ARShadowGAN-like/SG_generator.pth'# объявим датасет и даталоадерdataset = ARDataset(dataset_path, augmentation=get_validation_augmentation(256), preprocessing=get_preprocessing(), is_train=False)dataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=0)# определим устройствоdevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')# объявим полную модельmodel = ARShadowGAN(    encoder_att='resnet34',    encoder_SG='resnet18',    model_path_attention=path_att,    model_path_SG=path_SG,    device=device)# переведем ее в режим тестированияmodel.eval()# предсказаниеfor i, data in enumerate(dataloader):    tensor_att = torch.cat((data[0][:, :3], torch.unsqueeze(data[1][:, -1], axis=1)), axis=1).to(device)    tensor_SG = torch.cat((data[2][:, :3], torch.unsqueeze(data[3][:, -1], axis=1)), axis=1).to(device)    with torch.no_grad():        result, shadow_mask = model(tensor_att, tensor_SG)        shadow_mask = np.uint8(127.5*shadow_mask[0].cpu().numpy().transpose((1,2,0)) + 1.0)        output_image = np.uint8(127.5 * (result.cpu().numpy()[0].transpose(1,2,0) + 1.0))        cv2.imwrite(osp.join(result_path, 'test.png'), output_image)        print('Результат сохранен: ' + result_path + '/test.png')

Заключение

В данной статье рассмотрена генеративно-состязательная сеть на примере решения одной из амбициозных и непростых задач на стыке Augmented Reality и Computer Vision. В целом полученная модель умеет генерировать тени, пусть и не всегда идеально.

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

Весь приведенный код в репозитории, примеры запуска в Google Colab ноутбуке.

P.S. Буду рад открытой дискуссии, каким-либо замечаниям и предложениям.

Спасибо за внимание!

Подробнее..

Как устроено компьютерное зрение?

24.03.2021 14:22:50 | Автор: admin
Мы запускаем камеру на смартфоне, наводим на объект и видим маленькую иконку внизу. Смартфон понимает что именно мы снимаем. Вы когда-нибудь задумывались, как это работает?

Беспилотные автомобили спокойно объезжают машины и тормозят перед пешеходами, камеры видеонаблюдения на улицах распознают наши лица, а пылесосы отмечают на карте, где лежат тапочки всё это не чудеса. Это происходит прямо сейчас. И всё благодаря компьютерному зрению.


Поэтому сегодня разберем, как работает компьютерное зрение, чем оно отличается от человеческого и чем может быть полезно нам, людям?

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

  1. Нам нужно как-то получить изображение
  2. Нам нужно его обработать
  3. И уже только потом проанализировать



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

Этап 1. Получение изображения


В начале компьютеру надо что-то увидеть. Для этого нужны разного рода датчики. Насколько много датчиков и насколько они должны быть сложные зависит от задачи. Для простых задач типа детектора движения или распознавания объектов в кадре достаточно простой камеры или даже инфракрасного сенсора.

В нашем пылесосе есть целых две камеры, они находятся спереди.А вот, например, для ориентации в трехмерном пространстве понадобятся дополнительные сенсоры. В частности 3D-сенсор. Тут он тоже есть и расположен сверху. Но что это за сенсор?

LiDAR


Вообще с названиями 3D-сенсоров есть небольшая путаница, одно и тоже часто называют разными словами.

Эта штука сверху называется LDS или лазерный датчик расстояния, по-английски Laser Distance Sensor. Подобные датчики вы наверняка могли заметить на крышах беспилотных беспилотных автомобилей. Это не мигалка, это лазерный датчик расстояния, такой же как на роботе пылесосе.

Вот только в мире беспилотников такой сенсор принято называть лидаром LIDAR Light Detection and Ranging. Да-да, как в новых iPhone и iPad Pro.





А вот в Android-смартфонах вместо лидаров используется термин ToF-камера: ToF Time-of-flight.

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

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



Кстати, лидары использует не только в задачах навигации. Благодаря лидарам сейчас происходит настоящая революция в археологии. Археологи сканируют территорию с самолета при помощи лидара, после чего очищают данные ландшафта от деревьев. И это позволяет находить древние города, скрытые от глаз человека!





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



Еще 8 лет назад такие сенсоры стоили каких-то невероятных денег, под 100 тысяч долларов. А теперь у вас по дому может спокойно ездить маленький беспилотник.



Лидар в пылесосе


Окей, тут лидар используется для построения карты помещения и это не новая история. Такую технологию мы видели еще года 3-4 назад.

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

Но внутри пылесоса стоит, на секундочку, восьмиядерный Qualcomm Snapdragon 625 (Qualcomm APQ8053), поэтому у него хватает мозгов не только построить карту, но и ориентироваться по ней.

Более того пылесос может хранить в памяти до четырёх карт и распознаёт этажи. Это существенно ускоряет уборку. Потому при переносе с этажа на этаж пылесос это может понять и не тратит время, чтобы построить карту заново.



Также каждую из 4 карт можно поделить на 10 специальных зон. Для которых можно настроить свои параметры уборки: мощность всасывания (до 2500 Па), количество проходов и прочее. А куда-то можно вообще запретить ездить. Можно даже выбирать сухую и влажную уборку для разных зон. Правда для этого не нужно подключать/отключать отдельный резервуар с водой. И всё это стало возможно благодаря лидару.

Тем не менее у технологии есть некоторые недостатки очень разреженные данные.Пространство сканируется линиями. В больших автомобильных радарах разрешение от 64 до 128 линий. Плюс ко всему у лидар есть мертвая зона. Если лидар стоит на крыше то он не видит, что творится в достаточно большом радиусе вокруг него.

Также в роботе-пылесосе лидар тут сканирует пространство всего одним лучом. Поэтому, всё что он видит это тонкая линия на высоте где-то 9-10 сантиметров от пола. Это позволяет определять где стены и мебель, но он не видит того, что валяется на полу.

Две камеры




Поэтому, чтобы исправить этот недочет лидаров. как в автомобили, так и в пылесосы ставят дополнительные камеры.Тут камеры сразу две, и они обеспечивают стереоскопическое зрение. Да-да, у пылесоса всё как у людей два глаза.

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

Это позволяет пылесосу обнаруживать предметы размером не менее 5 см в ширину и 3 см в высоту и объезжать их.

Этап 2. Обработка


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

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

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



Но вот совместить данные с разных сенсоров это нетривиальная задача.

Например, пылесос на полу обнаружил какой-то предмет. Дальше ему нужно понять где именно он находится на карте построенной при помощи лидара. А также нужно предположить какие у него габариты по проекции с одной стороны. То есть нам нужно поместить предмет в некий объёмный куб правильного размера.





Эту задачу можно решить разными способами. Один из способов называется усеченная пирамида.Сначала на камере обнаруживаются предметы. Потом эти предметы помещаются в конус, а объем этого конуса вычисляется нейросетью.



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



А раз мы заговорили про нейросети, значит мы уже немного зашли на 3-й этап компьютерного зрения анализ.

Этап 3. Анализ


За распознавание, сегментацию и классификацию объектов на изображении в современном мире в основном отвечают нейросети. Мы даже делали подробный ролик о том как это работает, посмотрите.



Если кратко, нейросеть это такое большое количество уравнений, связанных между собой. Загружая в нейросеть любые данные ты обязательно получишь какий-то ответ.
Но, например, если постоянно загружать в нейросеть фотографии кошечек, и указать ей, что ответ должен быть кошка. В какой-то момент, нейросеть перестает ошибаться на обучающей выборке. И тогда ей начинают показывать новые незнакомые изображения и если на них она тоже безошибочно определяет кошек нейросеть обучена.

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

Что-то похожее происходит с нейронными связями в человеческом мозге. Когда мы чему-то учимся или запоминаем, мы повторяем одно и то же действие несколько раз. Нейронные связи в мозге постепенно укрепляются и потом нам это легко даётся!

Например, в данном пылесосе за работу нейросети отвечает встроенный NPU-модуль. Всё-таки внутри Snapdragon, пылесос может себе такое позволить.

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

Распознавание предметов происходит при помощи гугловской библиотеке Tensorflow. Алгоритм самообучается и умнеет от уборки к уборке.

Практика




В нашем роботе-пылесосе технология распознавания называется Reactive AI. Мы протестировали насколько она хорошо работает на практике.

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

Проследить за тем, что видит пылесос всегда можно через фирменное приложение или Mi Home от Xiaomi. Можно даже просто кататься по дому управляя пылесосом слать на него голосовые сообщения. Управлять пылесосом можно также через Google Ассистента или Алису.Всё на русском языке.

С недавних пор бренд начал официально продаётся в России, поэтому устройства полностью локализованные.

Внутри кстати стоит батарейка на 5200 мАч, которая способна выдержать до 3 часов уборки.

Итоги




Ребят, ну вы сами всё видели. Правда, стоит обратить внимание, что пока корректное распознавание предметов работает только если запускать пылесос через специальное приложение. И это нюанс, поскольку оно пока недоступно в Play Market Россия. Но в течение нескольких месяцев оно появится.
Подробнее..

Компьютерное зрение в промышленной дефектоскопии Часть 2 Генерируем стремные трубы чтобы порадовать нейронку

18.02.2021 18:15:13 | Автор: admin


В предыдущей заметке мы рассказали о том, как мы решали задачу из области промышленной дефектоскопии методами современного машинного зрения. В частности, мы упомянули, что одним из подходов к обогащению данных обучающей выборки является генератор синтетических данных. В этой заметке мы расскажем:


  • как сделали такой генератор на основе Blender и Python,
  • какие типы масок для задач компьютерного зрения вообще можно получить в Blender.

Заметка от партнера IT-центра МАИ и организатора магистерской программы VR/AR & AI компании PHYGITALISM.


Введение


Напомним, что генератор фотореалистичных 3D моделей промышленных труб и их дефектов нужен был для того, чтобы обогатить набор обучающих данных для нейросетевых алгоритмов детекции дефектов на изображениях (дефекты труб на ТЭЦ, снятые при помощи беспилотников). Беспилотники и автоматическая детекция дефектов на практике должна минимизировать время и расходы на проведение сервисного обслуживания станции.


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


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


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

Поскольку генератор позволяет получать 3D объекты, он способен стать источником новых данных не только для алгоритмов классического компьютерного зрения (CV), но и для целого ряда задач геометрического глубокого обучения (3D ML, GDL).


Применение 3D ML подходов может дать преимущество при решении задач дефектоскопии, так как пространственные сканеры / камеры глубины (RGB-D, Lidar и пр.) позволяют находить менее очевидные человеческому глазу дефекты и реконструировать изучаемые объекты (например, вздутие трубы не всегда можно обнаружить, не потрогав трубу руками или чувствительным щупом).


Часть 1: Реализация генератора данных



Рис.1 Примеры данных, сгенерированных в проекте. Слева направо: меш труб, отрендеренные изображения с текстурами, битовые маски дефектов (авторазметка), ограничивающие прямоугольники дефектов (авторазметка).


Вся работа по созданию искусственного набора данных была осуществлена в Blender, с использованием скриптов на языке Python. Исключение составила лишь программа преобразования растровой разметки в формат Yolo, написанная на языке Rust.


Минутка текстовых шуток для IT-шников:
В нашем проекте Python генерировал змеевики, а Rust получал разметку ржавчины.


Рис.2 Рабочее окно Blender с плагином генератора труб.


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



Рис. 3 Пример сцены, из которой рендерились наборы изображений с разметкой поперечных трещин.


Задача создания генератора синтетических данных была разделена на следующие этапы:


  • Настройка цифрового двойника камеры БПЛА с накамерным светом (рендеринг итоговых изображений должен позволять добиться реалистичности в синтетических данных).
  • Создание инструмента для быстрого моделирования базовой геометрии труб.
  • Настройка процедурных материалов для различных поверхностей труб (металлический блеск, ржавчина и пр.).
  • Настройка процедурных материалов для различных дефектов труб (различные трещины, дыры, изгибы и пр.).
  • Настройка процедурной анимации позиции камеры, освещения и материалов для создания разнообразных изображений из одной сцены.
  • Настройка масок дефектов (битовые маски и ограничивающие прямоугольники для разных классов дефектов).
  • Рендер итоговых сцен.
  • Приведение разметки дефектов к форматам YOLO и MS COCO.

Настройка цифрового двойника камеры БПЛА с накамерным светом



Рис.4 Камера с осветителем на сцене в Blender.


Объект с самосветящимся материалом, имитирующий кольцевой осветитель, и всенаправленная лампа назначены дочерними объектами камеры. Размер сенсора, угол обзора, относительное отверстие объектива и разрешение получаемого изображения настроены в соответствии с реальными характеристиками камеры DJI Mavic 2 Zoom.


Инструмент для быстрого моделирования базовой геометрии труб



Рис.5 Сцена, наполненная змеевиками труб, полученными с помощью скрипта из этого раздела.


В первую очередь нужно отметить, что вся геометрия труб строилась по NURBs без прямой конвертации в полигональные модели (Про использование NURBS в 3D моделировании на хабре писали в этой заметке). Все дефекты, в том числе, геометрические создавались посредством материалов на этапе рендера.


Не было никакой необходимости выстраивать совершенно новую сцену в отношении геометрии для каждого кадра, достаточно было сделать несколько заготовок и менять ракурсы, освещение и материалы. Выяснилось, что наиболее распространённая схема размещения труб простой массив. Создать массив параллельных труб не представляло никакой сложности, для этого есть модификатор Array. Другой распространенной схемой оказались змеевики, в том числе облегающие цилиндрические поверхности. Для быстрого создания таких труб был написан скрипт на языке Python, в котором можно настроить радиус цилиндрической поверхности, шаг змеевика, его направление и количество повторений.


Код для процедурной генерации труб:
import bpyimport timefrom math import sin, cos, pi, radians# Генерация змеевиков в плоскости внутри прямоугольника со сторонами# sizeX, sizeYdef create_flat_curve(sizeX, sizeY):    points = []    for y in range(sizeY):        for x in range(sizeX):            if y % 2 == 0:                point = [x, y]            else:                point = [sizeX - x - 1, y]            points.append(point)    curve_name = "Pipe_Flat_" + str(time.time_ns())    curveData = bpy.data.curves.new(curve_name, type='CURVE')    curveData.dimensions = '3D'    curveData.resolution_u = 2    polyline = curveData.splines.new('NURBS')    polyline.points.add(len(points)-1)    for i, point in enumerate(points):        x,y = point        polyline.points[i].co = (x, y, 0, 1)    curveData.bevel_depth = 0.4    #polyline.use_endpoint_u = True    curveOB = bpy.data.objects.new(curve_name, curveData)    bpy.context.collection.objects.link(curveOB)# Генерация змеевиков в пространствеdef create_cyl_curve(radius, angle, height, density, horizontal):    phi = radians(angle)    steps = int(density * phi / (pi*2))     print("Steps:", steps)    points = []    if horizontal:        for z in range(height):            for step in range(steps):                if z % 2 == 0:                    x = radius * cos(step * phi / steps)                    y = radius * sin(step * phi / steps)                    else:                    x = radius * cos(phi - (step+1) * phi / steps)                    y = radius * sin(phi - (step+1) * phi / steps)                  point = [x, y, z]                points.append(point)    if not horizontal:        for step in range(steps):            for z in range(height):                x = radius * cos(step * phi / steps)                y = radius * sin(step * phi / steps)                    if step % 2 == 0:                    point = [x, y, height-z-1]                else:                    point = [x, y, z]                points.append(point)    print("Points:", len(points))    curve_name = "Pipe_Cylinder_" + str(time.time_ns())    curveData = bpy.data.curves.new(curve_name, type='CURVE')    curveData.dimensions = '3D'    curveData.resolution_u = 2    polyline = curveData.splines.new('NURBS')    polyline.points.add(len(points)-1)    for i, point in enumerate(points):        x,y,z = point        polyline.points[i].co = (x, y, z, 1)    curveData.bevel_depth = 0.3    #polyline.use_endpoint_u = True    curveOB = bpy.data.objects.new(curve_name, curveData)    bpy.context.collection.objects.link(curveOB)

Настройка материалов поверхностей труб


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


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

Запекание сгенерированных текстур, например из Substance Painter, было исключено чтобы не множить инструменты и сущности (такая вот бритва Оккама у нас вышла).



Рис.6 Группа нод (назовем ее супернодой) для настройки материалов, объединенные в одну большую ноду.


Все ноды базовых материалов были объединены в группу с выведенными в интерфейс основными параметрами. В зависимости от конкретной сцены к тем или иным параметрам материала подключались генераторы псевдо-случайных чисел, собранные из ноды белого шума, произвольного индекса объекта, анимированного значения и математических нод.


Примечание:

Здесь и далее для конструирования шейдеров используется интерфейс с нодами, поскольку разработчик данного решения CG художник, и этот подход был для него предпочтительным =)



Рис. 7 Содержание суперноды из рис.6: материалы внутри группы собирались преимущественно из шумов и градиентов.


Настройка материалов дефектов труб



Рис.8 Пример сгенерированных труб с дефектами коррозии (слева) и цветами побежалости (справа).


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



Рис.9 Создание дефекта Разрыв трубы в Blender.


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



Рис.10 Создание дефекта Выход трубы из ряда в Blender.


Для таких дефектов, как выход трубы из ряда и разрыв, использовалось векторное смещение по выбранной оси в системе координат объекта. Создание такого чисто геометрического результата средствами шейдеров обусловлено удобством вывода данных для разметки как значения материала (примеры таких разметок смотри во второй части заметки).


Анимация камеры, освещение и материалы


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



Рис.11 Применение шума на анимационных кривых позиции, поворота камеры, интенсивности и позиции источника света для процедурной съемки сцены.


Настройка масок дефектов


Для вывода черно-белых масок разметки дефектов использовался канал Arbitrary Output Value (AOV), в ноду которого подавался коэффициент смешивания базового материала и материала дефекта. Иногда использовалась бинарная математическая нода Greater Than (на выходе 0, если входное значение меньше порогового, иначе 1).


Рендер


В композиторе было настроено две выводящих ноды: одна сохраняла изображение, вторая маску. Сцены рендерились как анимированные, то есть на каждый кадр в заданном диапазоне сохранялось два файла. Изображения отправлялись в директорию с данными согласно соглашениям разметки Yolo, одноименные маски сохранялись во временной директории для последующего преобразования в разметку.



Рис.12 Директории с сохраненными изображениями (слева) и разметкой (справа).


Приведение разметки к формату YOLO


Формат разметки YOLO предполагает обозначение участков изображения ограничивающими прямоугольниками. Текстовый файл должен содержать нормированные координаты центров ограничивающих прямоугольников и их габариты. Для получения такого вида разметки была написана программа, рекурсивно проходящая по соседним пикселям маски, значения которых отличны от нуля, и сохраняющая минимальные и максимальные координаты связанных пикселей, после чего абсолютные координаты вершин прямоугольников нормализовались. Выбор языка Rust для написания этой программы был обусловлен скоростью выполнения и возможностью с лёгкостью реализовать одновременную обработку нескольких изображений на разных потоках процессора. Ниже приведен код на Python для поиска группы пикселей изображения, относящейся к одному дефекту.



Рис.13 Сгенерированные трубы с дефектом трещины (слева) и соответствующая маска для данного изображения (справа).


Код с поиском масок дефектов на Python (предполагается что на входе имеется изображение как на рис.13 справа):
import bpyimport colorsysimage = bpy.data.images["two_cubes.png"]sizeX = 64sizeY = 64image.scale(sizeX, sizeY)pixels = image.pixelssize = [sizeX, sizeY]print(len(pixels))grid = [[ [0] for y in range(size[1])] for x in range(size[0])] print("LEN:", len(grid))print(len(pixels)/4, " == ", sizeX*sizeY)def rgb_to_hex(rgb):    hex_string = ""    for c in rgb:        hex_string += str(hex(max(min(int(c * 255 + 0.5), 255), 0)))[2::]     return hex_string.upper()def search_neighbours(grid, x, y, color, l,b,r,t):    grid[x][y] = 0    #print("FROM", x, y)    if x < l:        l = x    if x > r:        r = x    if y < b:        b = y    if y > t:        t = y    if x < size[0]-1:            if grid[x+1][y] == color:        #print("RIGHT")            l,b,r,t = search_neighbours(grid, x+1, y, color, l, b, r, t)    if y < size[1]:        if grid[x][y+1] == color:        #print("UP")            l,b,r,t = search_neighbours(grid, x, y+1, color, l, b, r, t)    if x > 0:        if grid[x-1][y] == color:        #print("LEFT")            l,b,r,t = search_neighbours(grid, x-1, y, color, l, b, r, t)    if y > 0:        if grid[x][y-1] == color:        #print("DOWN")            l,b,r,t = search_neighbours(grid, x, y-1, color, l, b, r, t)    return (l,b,r,t)  for i in range(0, int(len(pixels) / 4)):    if pixels[i*4] > 0:#sum(pixels[i*4:i*4+3]) > 0:        x = (i) % size[1]        y = int(i / size[1])        #color = pixels[i*4:i*4+2]        #hex_col = rgb_to_hex(pixels[i*4:i*4+3])        grid[x][y] = 1              #print(x,y)print("GRID FINISHED")        color = 1islands = []for y in range(size[1]):    for x in range(size[0]):        if grid[x][y] == color:            "LOOKING FOR NEW ISLAND"            print(search_neighbours(grid, x, y, color, x, y, x, y))

Часть 2: Создание масок и разметки в Blender



Рис.14 Исходная тестовая сцена в Blender.


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


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


1.Комбинированные Рендер-пассы


Рис. 15 Combined Pass: итоговый рендер сцены с учетом всех компонент.


2. Глубина сцены


Рис.16 Depth Pass: карта глубины для данной сцены.


Канал глубины позволяет передать информацию о позициях пикселей в пространстве через их удалённость от камеры. Используя пару изображений Combined+Depth можно тренировать нейросети, восстанавливающие трёхмерные сцены из изображений.


3. Карты нормалей


Рис. 17 Normal Pass: карта нормалей сцены.


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


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


4. Диффузная составляющая


Рис. 18 Diffuse Color Pass: рассеивающая составляющая материалов (например высокое значение у зеркальных металлических поверхностей и низкое значение у шероховатых диэлектриков).


5. Бликовая составляющая


Рис. 19 Glossy Color Pass: отражающая способность материала (блики).


6. Имитирующая составляющая


Рис. 20 Emission Pass: самосвечение материалов.


7. Суммарная интенсивность света


Рис. 21 Ambient Occlusion Pass: суммарная интенсивность света в каждой точке.


8. Теневая составляющая


Рис. 22 Shadow Pass: тени (для каждой точки пространства просчитываются относительно источников света на сцене).


Одной из распространенных задач в генерации синтетических данных является создание фото-реалистичных изображений, сопровождающихся разметкой определенных объектов или их частей. Blender предлагает несколько способов создания масок, которые могут быть использованы в качестве разметки.


Маски можно создавать на индивидуальные объекты и их группы, материалы, а также на произвольные параметры материалов.


9. Маска индивидуальных объектов (instance segmentation)


Рис. 23 Cryptomatte Object Pass: разметка различных объектов случайным цветом.


10. Разметка объектов по классам материалов


Рис. 24 Cryptomatte Material Pass: разметка различных материалов случайными цветами.


В пассах Cryptomatte всем объектам и материалам присваиваются уникальные цвета.


Допустим, мы хотим создать две маски: на одной будут отмечены все обезьянки, на другой геометрические примитивы. Всем объектам нужно назначить Object ID (он же Pass Index), для обезьянок это будет 1, для примитивов 2, 0 останется для пола. Для удобства объекты разных классов можно распределить по коллекциям и написать скрипт, который присваивает всем объектам коллекции свой Object ID.



Чтобы получить необходимую маску, нужно использовать ноду ID Mask в композиторе.
Также в композиторе можно настроить одновременный вывод пассов и масок в отдельные файлы (см. рисунок ниже).



11. Разметка объектов по категориям (semantic segmentation)


Рис. 25 monkeys mask: маскирование объектов класса обезьяна.



Рис. 26 primitives mask: маскирование объектов класса геометрические примитивы.


Если мы хотим отметить каждый интересующий нас объект по отдельности, им нужно присвоить свои уникальные Object ID.


12. AOV - Arbitrary Output Value

Blender позволяет выводить в изображение любые параметры материалов. Для этого в настройках пассов нужно создать слой AOV, в который будут сохраняться значения из шейдеров в виде RGB или числа с плавающей точкой. В соответствующем материале нужно создать ноду Output/AOV и присвоить ей имя слоя.


Разберём этот материал:

Рис. 27 Combined Pass, умноженный на маску материала.



Здесь текстура шума подана на параметр Scale шейдера подповерхностного рассеивания (см. рис. выше). Допустим, мы хотим получить маску на те области поверхности, в которых параметр Scale больше 1.8.


В результате получим маску:


Проделаем теперь подобное с другим материалом и выделим красные области, подав в AOV фактор смешивания синего и красного цветов:


AOV даёт более широкие возможности для разметки, этот подход можно использовать для обозначения областей объектов, подверженных смещению (Displacement). На объектах на изображении ниже использовалось смещение по нормали поверхности для имитации повреждений:


Нужно отметить, что на этих объектах разные материалы, но для каждого из них включен вывод значения смещения в один канал AOV, значения при этом складываются.



Отдельным примером может служить использование AOV для разметки повреждённых областей объектов, на которых основной материал заменяется на прозрачный. На этой обезьянке применено трёхмерное смещение (Vector Displacement), то есть каждый участок, подверженный такому эффекту смещается не по нормали к исходной поверхности, а по трём осям согласно значениям из цвета, подаваемого на вход (R,G и B соответствуют X, Y и Z).





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




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


Заключение



Blender обладает богатым набором инструментов для создания изображений из трёхмерных сцен, которые также можно использовать для генерации и визуализации многомерных данных. Каналы и пассы позволяют создавать маски для участков изображения, представляющих интерес для разметки. Выгодной особенностью Blender также является возможность расширения его функционала за счет скриптов на языке Python.


В будущем мы постараемся рассказать и про другие наши эксперименты связанные с 3D ML вобще и с Blender в частности, а пока можете подписаться на наш канал в Telegram 3D ML, где мы рассказываем несколько раз в неделю о новостях и достижениях в этой науке)

Подробнее..

3D реконструкция лица, или как получить своего цифрового двойника (Часть 1)

24.03.2021 20:12:56 | Автор: admin
Фотография (слева) и рендеринг 3D модели лица (справа)Фотография (слева) и рендеринг 3D модели лица (справа)

Поговорим об одном интересном методе восстановления 3D лица человека, которое почти не отличить от фотографий.

На хабре уже 2 года не появлялись статьи про лицевую 3D реконструкцию, и в Twin3D мы хотим постепенно заполнять этот пробел и регулярно выкладывать обзоры интересных статей, методов и наших собственных результатов на тему 3D digital human в целом.

Сначала пара слов о том, кому и зачем эти 3D лица и тела нужны (вообще об этом можно отдельную статью написать). Изначально 3D сканирование человека использовалось в кино для спецэффектов, где с лицом героя должно произойти что-то мало желаемое в реальности (например, взрыв) или невозможное. С увеличением требований к графике в компьютерных играх появилась потребность создавать всё более реалистичных персонажей и гораздо проще отсканировать живого человека, чем лепить/рисовать с нуля по фотографиям. Теперь же к этому прибавляется желание людей оказаться в виртуальном мире или самим быть персонажами игр и для этого, естественно, тоже нужны 3D модели.

Стоит отметить, что предыдущие статьи на хабре фокусировались на методах легкого создания 3D моделей лиц. Как обычно, тут есть trade-off между качеством и простотой получения 3D модели. В нашем цикле статей мы расскажем про 3 метода в порядке убывания сложности процесса сканирования: от специального сетапа с 24 камерами и 6 вспышками (об этом методе поговорим сейчас) до фотографии со смартфона.

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

Терминология

Результатом базовой 3D реконструкции лица является следующее сочетание: геометрия + текстура альбедо + отражаемость и нормали (картинки будут ниже).

  • Геометрия это просто меш, т.е. упорядоченный набор связанных между собой точек в 3D.

  • Текстура альбедо это по сути набор пикселей, которые покрывают этот меш, настоящий цвет кожи.

  • Отражаемость и карта нормалей информация про каждый пиксель о том, как он отражает падающий свет (как сильно и в каком направлении).

Только при наличии всех эти трех компонент можно получить качественную фотореалистичную 3D модель лица.

Пара слов о методе

Метод реконструкции лица, о котором мы сейчас поговорим, описан в статье "Near-Instant Capture of High-Resolution Facial Geometry and Reflection", которая написана G. Fyffe, P. Graham, B. Tunwattanapong, A. Ghosh, P. Debevec и представлена на Eurographics 2016. Ее можно почитать здесь (дальше все картинки взяты оттуда). Эта работа примечательна тем, что авторам впервые удалось получить качество восстановления с точностью до пор кожи при почти мгновенном сканировании (66 мс). На заставке вы увидели результаты именно этой статьи. Статье уже 5 лет, но она стала своего рода классикой, да и авторы у нее широко известны в узких кругах (тот же Дебевек из Google). Статья написана довольно специфичным языком и с опусканием многих неочевидных деталей, так что пришлось немного поломать голову, чтобы ее понять и написать этот текст.

Как это работает

Для начала, авторы собрали весьма интересный риг из камер и вспышек. В нем 24 DSLR камеры CanonEOS 600D и 6 профессиональных вспышекSigma EM-140. Вспышки эти включаются последовательно, а вместе с ними одновременно фотографируют какое-то подмножество камер, так что в итоге каждая камера фотографирует ровно один раз. Камеры установлены и разбиты на группы так, чтобы оптимально покрыть всю область лица и для каждой точки увидеть хотя бы 3 разных отражения (дальше увидим, зачем). Реализована съемка с помощью микроконтроллера 80MHz Microchip PIC32. Авторы отдельно продумали, что весь этот процесс должен занимать меньше скорости моргания человека (~100 мс), так что от первой до последней фотографии проходит 66 мс, согласно статье.

Риг для съемки лицаРиг для съемки лица

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

Пайплайн алгоритмаПайплайн алгоритма

Исходный меш получается через обычный multiview stereo (например, Metashape). Но его качество довольно низкое (+- 2 мм), так что на основе карты нормалей этот меш в конце уточняется.

Поэтапное улучшение исходного мешаПоэтапное улучшение исходного меша

В основе алгоритма лежит photometric stereo набор методов компьютерного зрения, при котором используются не только сами фотографии, но и информация о падающем свете: интенсивность и направление света. Этот подход позволяет понять, как конкретный пиксель текстуры отражает свет в разных условиях, что для кожи человека особенно важно. Как я упомянул выше, алгоритм выдает две карты нормалей. Первая диффузная соответствует матовому отражению лица, то есть отражениям от глубинных слоев кожи. Вторая спекулярная нужна для рендеринга мельчайших деталей поверхности кожи.

Диффузная и спекулярная карты нормалейДиффузная и спекулярная карты нормалей

А получаются эти нормали для каждого пикселя по сути через решение систем линейных уравнений

\bf{L} \beta = \bf{P},

где L матрица направлений света для всех видов камер, \beta искомая нормаль (3-мерный вектор), P условные значения пикселей для этих точек и видов камер. После пристального взгляда на эту систему становится понятным, зачем нужно видеть точку хотя бы с трех ракурсов в противном случае систему однозначно не решить. Если хочется иметь карту разрешения 4096x4096, то соответственно нужно решить 16 млн таких систем, так что эффективное использование GPU здесь must have. Параллелизация таких вычислений отдельная нетривиальная задача.

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

Результаты

При наличии уточненного меша, альбедо, освещаемости и карт нормали можно зарендерить 3D модель лица под произвольным ракурсом и освещением.

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

Сравнение фотографии (слева) и рендеринга 3D модели под тем же углом и освещением (справа)Сравнение фотографии (слева) и рендеринга 3D модели под тем же углом и освещением (справа)

Если же мы посмотрим на рендеринг под новым ракурсом и освещением, то тут тоже всё весьма прилично.

Уточненный меш и рендеринг под новым ракурсом и освещениемУточненный меш и рендеринг под новым ракурсом и освещением

Итоги

При всей крутости результата и метода, конечно, в результате не получается супер-идеальный аватар человека. Тут никак не обрабатываются несколько ключевых элементов:

  • Волосы на всех результатах люди просто в шапочках, ни у кого нет щетины

  • Отдельные артефакты уши, ноздри носа и другие слабо видимые места никаким специальным образом не процессятся

  • Глаза они, конечно, не подойдут для игр или кино :)

В Twin3D мы тоже работаем над алгоритмами фотореалистичной реконструкции лица и тела человека; собрали свой риг, в котором можем отсканировать лицо и по классической фотограмметрии, и по фотометрик стерео . Мы верим, что такими подходами можно и нужно вдохновляться, но в конечном итоге для получения фотореалистичных цифровых людей без артефактов нужен элемент обучения на данных, ибо классический подход не понимает, что ноздри и глаза нужно как-то иначе обрабатывать, чем кожу. О подходах с элементами обучения мы расскажем в будущих статьях.

Подробнее..

Глубокие нейросети в компьютерном зрении genesis for geeks

20.11.2020 14:23:38 | Автор: admin

Если вы в общих чертах представляете себе, как работает компьютерное зрение, но жаждете деталей, то эта статья для вас.

Распознавание объектов нейросетью на системах NvidiaРаспознавание объектов нейросетью на системах Nvidia

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

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

Статья написана по мотивам выступления Евгения Бурнаева, кандидата физико-математических наук, доцента центра Сколтеха по научным и инженерным вычислительным технологиям для задач с большими массивами данных, в московской городской Точке кипения и нашей последующей беседы с ним.

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

Привет! Я занимаюсь deep learningом глубоким обучением нейросетей для компьютерного зрения и предиктивной аналитики. Наша научная группа включает 30 исследователей. Мы активно публикуемся в передовых журналах и много сотрудничаем с индустрией Huawei, Airbus, Bosch, Louis Vuitton, Sahara Force India Formula 1 team.

При упоминании словосочетания искусственный интеллект все начинают вспоминать страшилки вроде Терминатора.

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

Ассоциация математического подхода с нейросетями возникла еще в 40-х годах прошлого века, когда Питтс и Мак-Каллок предложили простейшую математическую модель нейрона. Одновременно появился простой алгоритм обучения. В результате люди нафантазировали чуть ли не человекоподобных роботов. В реальности ни одну из этих фантазий так и не внедрили не существовало технических возможностей. Так наступила, как это теперь называют, первая зима искусственного интеллекта: финансирование сократили, а интерес к вопросу снизился.

Следующий всплеск интереса произошел лишь в 90-х, когда появились вычислительные мощности и новые хорошие математические алгоритмы, которые позволяли решать задачи распознавания и прогнозирования. А в 2014 году технологии распознавания получили буквально третье рождение благодаря тому, что мы научились решать подобные задачи на порядок лучше, чем раньше. Но ассоциация с нейронами сохранилась по сей день.

Технологии шагнули довольно далеко. Но пока еще у систем распознавания есть много проблем. Требуется дорабатывать алгоритмы, чтобы повысить эффективность их работы. Здесь есть где развернуться не только инженеру, но и ученому.

Но начнем с того, как это работает.

Компьютерное зрение

Компьютерное зрение это прикладная область, составная часть искусственного интеллекта.

В теории от компьютерного зрения мы ожидаем возможности имитировать способности человека по распознаванию объектов на фото способности понимать, где текст, где лицо, а где здание.

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

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

Пример обнаружения объектов на фотоПример обнаружения объектов на фотои идентификациии идентификации

Первые алгоритмы компьютерного зрения появились давно. Типичный пример один из самых простых детекторов лиц Виолы Джонса, который отмечает положение людей в кадре.

Работа алгоритма обнаружения лиц в фотокамерах

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

Как устроены системы компьютерного зрения

Цифровое изображение это матрица, где каждый пиксель это некоторый элемент, содержащий число. В случае черно-белого изображения это число от 0 до 255, отражающее интенсивность серого.

Пиксели растрового изображения и их яркость в однобайтной кодировкеПиксели растрового изображения и их яркость в однобайтной кодировке

Для цветного изображения это обычно комбинация трех цветов. Еще в позапрошлом веке первые цветные фотографии одновременно снимали на три камеры в разном цвете, а потом полученные кадры совмещали. И до сих пор цветные изображения часто раскладывают на те же три цвета красный, зеленый и синий.

Построение цветного изображения на заре фотографииПостроение цветного изображения на заре фотографииПостроение цветного изображения из пикселей красного, синего и зеленого цветовПостроение цветного изображения из пикселей красного, синего и зеленого цветов

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

Задача категоризации

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

Как выглядит базовая задача категоризации изображенияКак выглядит базовая задача категоризации изображения

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

Виды задачи классификацииВиды задачи классификации

Имитируем распознавание

Предположим, у нас есть картинка. Инженер подошел бы к распознаванию следующим образом: он начал бы проверять, что есть на этом изображении. Например, какие есть объекты, имеющие овальную форму. Для этого он выбрал бы какие-то признаки, которые на объектах овальной формы принимали бы большие значения.

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

Условная схема решения задачи классификацииУсловная схема решения задачи классификации

Типичный пример классификатора то, что называется деревом решений. Самые простые деревья мы строим в обычной жизни:

Пример простейшего дерева решенийПример простейшего дерева решений

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

На практике обычно комбинируют множество деревьев решений, то есть получают ответы с каждого, а потом проводят что-то типа голосования.

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

Примеры признаков, которые можно использовать для категоризации фотоПримеры признаков, которые можно использовать для категоризации фото

Признаки и их расчет

Какого типа признаки здесь можно было бы использовать?

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

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

Другой пример фильтр, увеличивающий высокие частоты (резкость):

А если я возьму такую матрицу чисел, изображение, наоборот, будет размытым:

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

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

Решение задачи классификации при помощи нейросетиРешение задачи классификации при помощи нейросети

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

Большая выборка

Прорыв в этой области произошел в 2010 году, когда появился датасет ImageNet, который содержал 10 млн картинок. Чтобы его получить, проделали огромную работу: каждой из картинок вручную присвоили класс объекта, который там изображен.

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

Примеры баз размеченных изображенийПримеры баз размеченных изображений

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

Многослойность

Предположим, у нас есть изображение. Есть первый слой с каким-то количеством фильтров. Применяя эти фильтры последовательно к изображению, мы получаем новую картинку. После этого применяем к изображению специальное нелинейное преобразование (в нейросетях оно называется Transfer Function передаточная функция), затем другие фильтры, а следом новое нелинейное преобразование. И так далее. Каждый такой этап называется слоем.

Пример последовательного применения фильтров к изображениюПример последовательного применения фильтров к изображению

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

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

Ниже на гистограмме показано, как со временем эволюционировала точность от 2010-го до 2015 года, а также отмечено количество слоев нейросети, которое необходимо, чтобы достичь такой точности.

Изменение точности и сложности нейросетей с 2010-го по 2015 год (справа налево)Изменение точности и сложности нейросетей с 2010-го по 2015 год (справа налево)

Ошибка классификации 33,5%, и это лучше, чем у человека. Человек распознает с ошибкой 45%.

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

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

Аппаратное обеспечение

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

Несколько лет назад Nvidia заказала у создателей MythBusters забавный пиарный ролик для демонстрации параллельных вычислений

Речь идет о графических процессорах (GPU), которые изначально создавали для ресурсоемких игр. Их адаптировали под быстрое выполнение матричных вычислений. А нейросети, по сути, у нас и построены на матричных вычислениях, то есть умножениях одной таблицы чисел на другую.

Какие задачи мы можем решить?

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

Можем ответить на вопрос, какая у человека позиция относительно других тел, и даже спрогнозировать по двумерной фотографии положение частей тела человека в 3D.

Можем отыскать лицо человека.

Обнаружение лица, идентификация, оценка позы, распознавание эмоцийОбнаружение лица, идентификация, оценка позы, распознавание эмоций

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

Сопровождение объектов, распознавание действий, оптический потокСопровождение объектов, распознавание действий, оптический поток

Где сейчас используют нейросети

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

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

Поиск по картинкам

Все мы пользуемся стандартной функцией поиска объектов на фото в поисковиках вроде Яндекса и Google. На вход мы подаем фотографию. С помощью фильтров нейросеть считывает признаки, характеризующие семантический смысл фотографии (я говорил о них ранее). Далее они сравниваются с признаками фотографий, которые уже есть в интернете (те заранее были подсчитаны и сохранены в виде векторов чисел). Изображения со сходными признаками оказываются семантически близки.

Примеры поисковой выдачи по заданным фотоПримеры поисковой выдачи по заданным фото

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

По такому же принципу устроено детектирование и идентификация лиц. Это приложение важно для обеспечения безопасности тех же банков.

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

Определите, в каком из шести случаев изображен один и тот же человекОпределите, в каком из шести случаев изображен один и тот же человек

Правильно обученные системы компьютерного зрения не ошибутся даже в сложных ситуациях и в условиях плохого освещения. Точность распознавания у них достигает 99%.

Правильный ответПравильный ответ

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

Социальный протест

Использование нейросетей, связанное с распознаванием лиц, вызывает у обывателей опасения. Вот один из таких заголовков:

Заголовок одного из изданий: Судя по сканирующим мозг каскам, Китай не заинтересован в тайне частной жизни рабочихЗаголовок одного из изданий: Судя по сканирующим мозг каскам, Китай не заинтересован в тайне частной жизни рабочих

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

Более реалистичное применение камеры в учебном классе, которые оценивают, насколько студенты вовлечены в процесс. Так можно косвенно определить эффективность процесса обучения.

Система оценки вовлеченности обучающихсяСистема оценки вовлеченности обучающихся

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

Фантазии на тему китайского соцрейтингаФантазии на тему китайского соцрейтинга

Насчет китайского опыта существуют разные мнения. Лично я в Китае не жил и не могу описать ситуацию. Но на Западе внедрение подобных систем вызывает социальный протест. Например, некоторое время назад рабочие Amazon жаловались на жесткие рамки учета рабочего времени, прописанные в новой системе мониторинга.

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

Модификация фотографий

С помощью подобных нейросетевых моделей можно манипулировать изображениями к примеру, стилизовать фото.

Нейросеть добавляет налет абстракционизмаНейросеть добавляет налет абстракционизма

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

Повторение мимики и жестов при помощи нейросетиПовторение мимики и жестов при помощи нейросети

А эту анимацию делали коллеги из дружественной нам группы Сколтеха.

Анимирование картины при помощи нейросетиАнимирование картины при помощи нейросети

Подобные методы используют при создании фильмов и рекламы.

Физическая безопасность и обучение

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

Система отмечает, что на объекте находятся люди без каски или перчатокСистема отмечает, что на объекте находятся люди без каски или перчаток

Контроль соблюдения скоростного режима на дорогах также может осуществляться с помощью нейросетевых моделей.

Для распознавания госномеров нейросеть не нужна. Но определить тип и модель транспортного средства без нее уже сложноДля распознавания госномеров нейросеть не нужна. Но определить тип и модель транспортного средства без нее уже сложно

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

Строительство и городское планирование

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

Контроль строительства с помощью нейросетиКонтроль строительства с помощью нейросети

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

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

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

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

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

Пример анализа жилых объектов при помощи нейросетиПример анализа жилых объектов при помощи нейросети

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

3D-модель промышленного объекта, созданная по результатам сканирования при помощи нейросети. Объем модели нереально огромен: 12 млрд точек. С помощью модели намного проще проектировать ремонт и реконструкцию3D-модель промышленного объекта, созданная по результатам сканирования при помощи нейросети. Объем модели нереально огромен: 12 млрд точек. С помощью модели намного проще проектировать ремонт и реконструкцию

Геология

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

Восстановление цифровой модели породы при помощи нейросетейВосстановление цифровой модели породы при помощи нейросетей

От того, насколько эта порода пористая и как через нее распространяется вязкая жидкость, зависит, насколько трудно будет извлечь нефть.

Анализ прохождения вязкой жидкости через породуАнализ прохождения вязкой жидкости через породу

Медицина

В медицине существует большое количество задач, связанных с обработкой снимков, на которые врач тратит довольно много времени. Например, по снимкам сердца и предсердия надо вычислить их объем. Или по данным сканирования глазного дна понять, есть ли там какие-то изменения, связанные с диабетом.

Пример расчета объема сердца по снимку при помощи нейросетиПример расчета объема сердца по снимку при помощи нейросети

Если врач будет выполнять такого рода работу вручную, она займет много времени. Самое главное, что могут возникнуть ошибки, связанные с тем, что человек, допустим, устал. Чтобы этого избежать, нейросеть выполняет роль советчика. Подобное программное обеспечение уже одобрено минздравами многих стран. Это уже значительный рынок, где методы компьютерного зрения позволяют ускорить работу и сделать анализы более точными.

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

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

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

Ритейл

Я думаю, многие слышали о магазине Amazon Go. Продавцов там нет. Человек заходит, берет товар. За счет автоматического распознавания товара можно рассчитать стоимость покупки. По выходе из магазина с человека списывают деньги.

Самый первый магазин Amazon Go в Сиэтле (фото: SounderBruce, CC BY-SA 4.0)Самый первый магазин Amazon Go в Сиэтле (фото: SounderBruce, CC BY-SA 4.0)

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

Распознавание выкладки товаров по фотоРаспознавание выкладки товаров по фото

Эти приложения активно развиваются не только на Западе, но и в России. Первый магазин без продавцов уже протестировали в Москве.

Автономный транспорт

Типичная задача в этой сфере спрогнозировать положение объектов в трехмерном пространстве по изображениям с камер. Расчет координат центра, угла поворота все эти задачи успешно решаются с применением нейросетей.

Позиционирование в 3D при помощи нейросети, помогающее автономному автомобилю перемещаться в пространствеПозиционирование в 3D при помощи нейросети, помогающее автономному автомобилю перемещаться в пространстве

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

Визуальная навигация при помощи нейросетей, позволяющая автономному автомобилю точно определить расстояние до окружающих предметовВизуальная навигация при помощи нейросетей, позволяющая автономному автомобилю точно определить расстояние до окружающих предметов

Расширение задачи

Вначале мы определяли компьютерное зрение как возможность ответить на любой вопрос об изображении. Но чем сильнее мы хотим имитировать возможности человека, тем больше нам потребуется разных нейросетей, обученных на большом количестве изображений разных объектов. И в этом мы ощущаем свои пределы. Например, в ImageNet содержится порядка 10 тысяч классов объектов.

Мы можем соединить несколько нейросетей, заточенных под конкретные вопросы, это в какой-то степени уже реализуется в некоторых инженерных системах. Но полностью повторить человеческое описание картинки мы пока не можем.

Ошибки и обман систем распознавания

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

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

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

И если обмана или ошибки надо избежать, то рано или поздно разработчики проанализируют ситуацию и поставят соответствующую защиту.

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

Взлом нейросети. Пример смешивания изображений, в результате которого сверточная нейросеть выдает ошибочный результат (источник: spectrum.ieee.org)

Аналогичные вещи можно делать и с физическими объектами, например, добавить посторонние элементы к знакам дорожного движения.

Даже небольшие помехи сбивают с толку алгоритм распознавания (подробности: spectrum.ieee.org)

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

Но постепенно ситуация улучшается. Вспомним хотя бы систему разблокировки смартфонов по лицу. Первые реализации были ненадежными: чтобы обмануть систему, достаточно было использовать распечатанную фотографию. Теперь же необходимо создать объемный макет.

Для обмана используют напечатанную на 3D-принтере основу, которую оклеивают фотографиямиДля обмана используют напечатанную на 3D-принтере основу, которую оклеивают фотографиями

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

Мысли про сохранность рабочих мест

Отдельная тема это разговоры про сохранность рабочих мест. Не останутся ли люди без работы после внедрения всех этих технологий? Я полагаю, что проблема не столь катастрофична. Нейросети сокращают одни рабочие места и создают другие связанные с поддержкой и обучением этих систем. Вдобавок они открывают новые рынки, где тоже будет нужна разработка и обслуживание.

Подробнее..

Шесть степеней свободы 3D object detection и не только

28.10.2020 20:04:59 | Автор: admin

В компьютерном зрении часто приходится работать с двумерными изображениями, и значительно реже - с 3D объектами. Из-за этого многие ML инженеры чувствуют себя неуверенно в этой области: много незнакомых слов, непонятно, куда тут применить старых друзей Resnet и Unet. Поэтому сегодня я хотел бы немного поговорить о 3D на примере задачи определения шести степеней свободы, что в каком-то виде синонимично 3D object detection. Я разберу одну из относительно свежих работ на эту тему с некоторыми отступлениями.

Меня зовут Арсений, я работаю ML инженером и веду Telegram-канал partially unsupervised. Эта статья написана по мотивам моего же видео для Data Fest 2020, секция CV в индустрии.

Кратко о задаче

Для начала давайте определимся, что такое шесть степеней свободы (6 DoF - degrees of freedom). Представим себе некоторый ригидный (неизменяемый, т.е. при трансформации все точки будут оставаться на той же дистанции друг от друга) объект в трехмерном мире. Чтобы описать его положение относительно наблюдателя понадобится 6 измерений: три будут отвечать за повороты по разным осям, а еще три - за смещение по соответствующим осям. Соответственно, имея эти шесть чисел, мы представляем, как объект расположен относительно какого-то базиса (например, точки, с которой ведется фотосъемка). Эта задача является классической для робототехники (где находится объект, который нужно схватить роборукой?), дополненной реальности (где нарисовать маску в MSQRD, ушки в Snapchat или кроссовки в Wanna Kicks?) , беспилотных автомобилей и других доменов.

Будем рассматривать статью MobilePose: Real-Time Pose Estimation for Unseen Objects with Weak Shape Supervision (Hou et al., 2020). Эта статья, написанная авторами из Google Research, предлагает надежный и, что немаловажно, быстрый пайплайн для решения задачи, будет уместно разобрать его по частям.

Пайплайн состоит из трех основных кусков:

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

  2. Выходы сети не выглядят инновационно. Detection, regression - все знакомые слова! Впрочем, насчет shape могут возникнуть вопросы. Но давайте отложим их на время.

  3. Постпроцессинг может показаться загадочным для тех, кто не в теме. Что такое EPnP и почему оно превращает 2D точки в 3D bounding box?

3D для чайников

И здесь сразу нужно сделать важное отступление, которое поможет нам ответить на все эти вопросы. Давайте высокоуровнево посмотрим на некоторую математику 3D мира. Пусть есть некоторый набор 3D-точек X - матрица размером (n, 3), в которой n - количество точек. Как мы помним, шесть степеней свободы - это три поворота и три смещения, т.е. Rigid transformation. Если обозначить R матрицу поворота, а t - вектор переноса (rotation и translation соответственно), будет соблюдаться такое уравнение:

X = X @ R + t

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

Но X - это все еще 3D-координаты. Потому стоит сказать, что еще существует некоторая проективная матрица P. Эта матрица характеризует то, как мы проецируем объект на двумерную плоскость, условно рендерим его. Эта матрица зависит от размера фотографии, фокусного расстояния, искажений, но в нашей задаче ее можно считать константной. Имя такую матрицу, можно получить 2D координаты точек, просто умножив ее на X:

x = X @ P

Совсем на пальцах: мы ищем, как нужно повернуть и подвинуть некий объект, чтобы какие-то его точки спроецировались так, как они сейчас изображены на фотографии. Я все упростил до неприличия, потому отправляю всех желающих просветлиться посмотреть на CS231a.

Подзадача нахождения R и t, зная X, x и P, называется Perspective-n-Point. Т.е. мы знаем, как выглядит наш объект в 3D (это X), знаем, на какое изображение он спроецирован (P) и где на этом изображении находятся его точки. Выглядит как задача оптимизации! Есть целое семейство алгоритмов, которые решают эту задачу, например, некоторые уже реализованы в OpenCV.

Еще некоторые ссылки по теме:
Monocular Model-Based 3D Tracking of Rigid Objects: A Survey (Lepetit et. al 2005) - классический обзор;
EPnP: An Accurate O(n) Solution to the PnP Problem (Lepetit et. al 2008) - сильный бейзлайн;
PnP-Net: A hybrid Perspective-n-Point Network (Sheffer and Wiesel, 2020) - для тех, кто хочет скрестить ужа и ежа, т.е. добавить к PnP немного диплернинга.

Кстати, к этой проблеме подходят и с другой стороны. Адепты deep learning могут найти множество статей, где используется специальный projection layer, который преобразует 2D и 3D точки друг в друга. Обычно, чтобы обучить такой слой, используют синтетические данные, т.к. 3D координаты из реального мира получать дорого и сложно. Пример такой статьи.

Где взять 3D точки?

Итак, нам нужен X - напомню, это набор 3D точек. Откуда его взять?

Самый простой вариант - пытаться найти один и тот же объект на всех изображениях. Берем какую-то 3D CAD модель (готовую, рисуем с нуля, сканируем настоящий объект специальным сканером) и используем его (точнее, какие-то его точки) в качестве X. Иными словами, делаем явное допущение "на фотографии находится именно такой объект" - на первый взгляд, это прямо-таки нагло, но практика показывает, что для оценки 6 DoF этого достаточно.

Более сложный подход - так называемые параметризированные модели. Образцовый пример - Basel Face. Исследователи из Университета Базеля отсканировали много лиц и при помощи PCA обучили такую модель, чтобы изменение малого числа ее параметров позволяло сгенерировать эти 3D лица. Таким образом, можно крутить малое количество ручек - главных компонент и получать довольно разные модели.

Параметризованная модель может быть и куда проще. Например, если мы ищем на фотографии 3D bounding box, в качестве базовой модели можно использовать куб, а для параметризации использовать его соотношения длины-ширины-высоты.

Если наша 3D модель параметризована, ее параметры можно подбирать разными итеративными методами и выбирать такую, на которой reprojection error будет меньше. Т.е. берем некоторую модель X, решаем PnP, получаем R и t и выбираем такой X, чтобы разность x и (X @ R + t) @ P была минимальна, для примера можно посмотреть на procrustes analysis.

Истинные диплернеры идут дальше и в каком-то виде выучивают или 3D модель, или ее параметры. Хороший пример - известная работа DensePose от Facebook Research, которая популяризовала подход с выучиванием dense карты координат. Т.е. модель предсказывает для каждого пикселя его относительное расположение на 3D модели. Дальше можно найти соответствия и получить для каждого пикселя некоторое приближение его 3D координаты.

В статье, которую мы изначально взялись разбирать, есть выход с таинственным названием shape. В зависимости от наличия grouth truth данных (об этом немного позже) авторы либо учат там сегментационную маску объекта (т.н. weak supervision, для улучшения сходимости), либо как раз карту координат объекта.

Также может возникнуть вопрос - координаты каких именно точек мы хотим найти? Ответ простой: на самом деле, нам все равно. 3D модель обычно состоит из тысяч вершин, мы можем выбрать подмножество по своему вкусу. Единственный более или менее важный критерий - приблизительная равноудаленность точек друг от друга; в случае неудачной выборки решение PnP становится нестабильным.

Где взять 2D точки?

Итак, с 3D объектом худо-бедно разобрались, давайте пойдем в более знакомую большинству CV инженеров область, т.е. вернемся в 2D и подумаем, где взять координаты на плоскости.

Для получения 2D координат точек (обычно эта задача называется keypoint detection) популярны два основных подхода: регрессия напрямую (последний слой сети выдает x, y для каждой точки) и heatmap-регрессия (последний слой выдает тепловую карту с пятном около точки). Первый подход может быть быстрее, т.к. необязательно выстраивать полную encoder-decoder архитектуру, второй обычно точнее и достаточно легко обучается (это почти та же сегментация).

изображение взято из Adaptive Wing Loss for Robust Face Alignment via Heatmap Regressionизображение взято из Adaptive Wing Loss for Robust Face Alignment via Heatmap Regression

Авторы MobilePose не пошли ни по одному из этих путей и придумали интересный гибрид, вдохновившись современными безанкорными архитектурами для детекции вроде CenterNet. Помните, на схеме есть головы Detection и Regression? Так вот, Detection голова предсказывает центр объекта, а Regression - где вершины объекта находятся относительно этого центра.

В этом подходе есть изящный трюк. Я много писал о том, как выбрать 3D модель, а в этом случае все осознанно упрощается: в качестве модели используется простой параллелепипед, он же 3D bounding box! То есть X - это вершины бокса, а x - проекция этих вершин. Значит, достаточно знать соотношения сторон этого бокса (которые мы получаем из самой архитектуры детектора), и остальная магия ни к чему.

Особенности приготовления данных

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

Unlike 2D object detection, it is prohibitive to manually label data for 3D detection. Due to this difficulty of collecting sufficiently large amounts of labeled training data, such approaches are typically trained on real data that are highly correlated with the test data (e.g., same camera, same object instances, similar lighting conditions). As a result, one challenge of existing approaches is generalizing to test data that are significantly different from the training set.

Synthetic data is a promising alternative for training such deep neural networks, capable of generating an almost unlimited amount of pre-labeled training data with little effort. Synthetic data comes with its own problems, however. Chief among these is the reality gap, that is, the fact that networks trained on synthetic data usually do not perform well on real data.

В рассматриваемой статье авторы сделали один из таких трюков: вместо того, чтобы бездумно рендерить весь мир, они сделали комбинацию реальных и синтетических данных. Для этого они взяли видео из AR-приложений (хорошо быть гуглом и иметь много данных из ARCore). В таких видео есть и знание о плоскостях, оценка 6 DoF, полученная при помощи визуальной одометрии, и оценка освещения. Это позволяет рендерить искусственные объекты не где попало, а только на плоских поверхностях, адаптируя освещенность, что значительно снижает reality gap между синтетическими и реальными данными. К сожалению, повторить этот трюк в домашних условиях кажется довольно сложным.

Все вместе

(Сделано из палок и скотча за полчаса)(Сделано из палок и скотча за полчаса)

Ура, мы галопом пробежались по всем ключевым концепциям пайплайна! Этого должно хватить, чтобы читатель смог собрать из open source компонентов, например, приложение, которое будет рисовать маску на лице (для этого можно даже не учить модели самостоятельно, готовых сетей для face alignment немало).

Конечно, это будет только прототип. При доведении такого приложения до продакшена возникнет еще много вопросов, например:

  • Как добиться консистентности между кадрами?

  • Что делать с неригидными объектами?

  • Что делать, если объект частично не виден?

  • Что делать, если в кадре много объектов?

Именно там начнутся настоящие приключения.

Подробнее..

Распознаем номера автомобилей. Разработка multihead-модели в Catalyst

11.06.2021 08:06:47 | Автор: admin

Фиксация различных нарушений, контроль доступа, розыск и отслеживание автомобилей лишь часть задач, для которых требуется по фотографии определить номер автомобиля (государственный регистрационный знак или ГРЗ).

В этой статье мы рассмотрим создание модели для распознавания с помощью Catalyst одного из самых популярных высокоуровневых фреймворков для Pytorch. Он позволяет избавиться от большого количества повторяющегося из проекта в проект кода цикла обучения, расчёта метрик, создания чек-поинтов моделей и другого и сосредоточиться непосредственно на эксперименте.

Сделать модель для распознавания можно с помощью разных подходов, например, путем поиска и определения отдельных символов, или в виде задачи image-to-text. Мы рассмотрим модель с несколькими выходами (multihead-модель). В качестве датасета возьмём датасет с российскими номерами от проекта Nomeroff Net. Примеры изображений из датасета представлены на рис. 1.

Рис. 1. Примеры изображений из датасета

Общий подход к решению задачи

Необходимо разработать модель, которая на входе будет принимать изображение ГРЗ, а на выходе отдавать строку распознанных символов. Модель будет состоять из экстрактора фичей и нескольких классификационных голов. В датасете представлены ГРЗ из 8 и 9 символов, поэтому голов будет девять. Каждая голова будет предсказывать один символ из алфавита 1234567890ABEKMHOPCTYX, плюс специальный символ - (дефис) для обозначения отсутствия девятого символа в восьмизначных ГРЗ. Архитектура схематично представлена на рис. 2.

Рис. 2. Архитектура модели

В качестве loss-функции возьмём стандартную кросс-энтропию. Будем применять её к каждой голове в отдельности, а затем просуммируем полученные значения для получения общего лосса модели. Оптимизатор Adam. Используем также OneCycleLRWithWarmup как планировщик leraning rate. Размер батча 128. Длительность обучения установим в 10 эпох.

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

Кодирование

Далее рассмотрим основные моменты кода. Класс датасета (листинг 1) в общем обычный для CV-задач на Pytorch. Обратить внимание стоит лишь на то, как мы возвращаем список кодов символов в качестве таргета. В параметре label_encoder передаётся служебный класс, который умеет преобразовывать символы алфавита в их коды и обратно.

class NpOcrDataset(Dataset):   def __init__(self, data_path, transform, label_encoder):       super().__init__()       self.data_path = data_path       self.image_fnames = glob.glob(os.path.join(data_path, "img", "*.png"))       self.transform = transform       self.label_encoder = label_encoder    def __len__(self):       return len(self.image_fnames)    def __getitem__(self, idx):       img_fname = self.image_fnames[idx]       img = cv2.imread(img_fname)       if self.transform:           transformed = self.transform(image=img)           img = transformed["image"]       img = img.transpose(2, 0, 1)             label_fname = os.path.join(self.data_path, "ann",                                  os.path.basename(img_fname).replace(".png", ".json"))       with open(label_fname, "rt") as label_file:           label_struct = json.load(label_file)           label = label_struct["description"]       label = self.label_encoder.encode(label)        return img, [c for c in label]

Листинг 1. Класс датасета

В классе модели (листинг 2) мы используем библиотеку PyTorch Image Models для создания экстрактора фичей. Каждую из классификационных голов модели мы добавляем в ModuleList, чтобы их параметры были доступны оптимизатору. Логиты с выхода каждой из голов возвращаются списком.

class MultiheadClassifier(nn.Module):   def __init__(self, backbone_name, backbone_pretrained, input_size, num_heads, num_classes):       super().__init__()        self.backbone = timm.create_model(backbone_name, backbone_pretrained, num_classes=0)       backbone_out_features_num = self.backbone(torch.randn(1, 3, input_size[1], input_size[0])).size(1)        self.heads = nn.ModuleList([           nn.Linear(backbone_out_features_num, num_classes) for _ in range(num_heads)       ])     def forward(self, x):       features = self.backbone(x)       logits = [head(features) for head in self.heads]       return logits

Листинг 2. Класс модели

Центральным звеном, связывающим все компоненты и обеспечивающим обучение модели, является Runner. Он представляет абстракцию над циклом обучения-валидации модели и отдельными его компонентами. В случае обучения multihead-модели нас будет интересовать реализация метода handle_batch и набор колбэков.

Метод handle_batch, как следует из названия, отвечает за обработку батча данных. Мы в нём будем только вызывать модель с данными батча, а обработку полученных результатов расчёт лосса, метрик и т.д. мы реализуем с помощью колбэков. Код метода представлен в листинге 3.

class MultiheadClassificationRunner(dl.Runner):   def __init__(self, num_heads, *args, **kwargs):       super().__init__(*args, **kwargs)       self.num_heads = num_heads    def handle_batch(self, batch):       x, targets = batch       logits = self.model(x)             batch_dict = { "features": x }       for i in range(self.num_heads):           batch_dict[f"targets{i}"] = targets[i]       for i in range(self.num_heads):           batch_dict[f"logits{i}"] = logits[i]             self.batch = batch_dict

Листинг 3. Реализация runnerа

Колбэки мы будем использовать следующие:

  • CriterionCallback для расчёта лосса. Нам потребуется по отдельному экземпляру для каждой из голов модели.

  • MetricAggregationCallback для агрегации лоссов отдельных голов в единый лосс модели.

  • OptimizerCallback чтобы запускать оптимизатор и обновлять веса модели.

  • SchedulerCallback для запуска LR Schedulerа.

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

  • CheckpointCallback чтобы сохранять лучшие веса модели.

Код, формирующий список колбэков, представлен в листинге 4.

def get_runner_callbacks(num_heads, num_classes_per_head, class_names, logdir):   cbs = [       *[           dl.CriterionCallback(               metric_key=f"loss{i}",               input_key=f"logits{i}",               target_key=f"targets{i}"           )           for i in range(num_heads)       ],       dl.MetricAggregationCallback(           metric_key="loss",           metrics=[f"loss{i}" for i in range(num_heads)],           mode="mean"       ),       dl.OptimizerCallback(metric_key="loss"),       dl.SchedulerCallback(),       *[           dl.AccuracyCallback(               input_key=f"logits{i}",               target_key=f"targets{i}",               num_classes=num_classes_per_head,               suffix=f"{i}"           )           for i in range(num_heads)       ],       dl.CheckpointCallback(           logdir=os.path.join(logdir, "checkpoints"),           loader_key="valid",           metric_key="loss",           minimize=True,           save_n_best=1       )   ]     return cbs

Листинг 4. Код получения колбэков

Остальные части кода являются тривиальными для Pytorch и Catalyst, поэтому мы не станем приводить их здесь. Полный код к статье доступен на GitHub.

Результаты эксперимента

Рис. 3. График лосс-функции модели в процессе обучения. Оранжевая линия train loss, синяя valid loss

В списке ниже перечислены некоторые ошибки, которые модель допустила на тест-сете:

  • Incorrect prediction: T970XT23- instead of T970XO123

  • Incorrect prediction: X399KT161 instead of X359KT163

  • Incorrect prediction: E166EP133 instead of E166EP123

  • Incorrect prediction: X225YY96- instead of X222BY96-

  • Incorrect prediction: X125KX11- instead of X125KX14-

  • Incorrect prediction: X365PC17- instead of X365PC178

Здесь присутствуют все возможные типы: некорректно распознанные буквы и цифры основной части ГРЗ, некорректно распознанные цифры кода региона, лишняя цифра в коде региона, а также неверно предсказанное отсутствие последней цифры.

Заключение

В статье мы рассмотрели способ реализации multihead-модели для распознавания ГРЗ автомобилей с помощью фреймворка Catalyst. Основными компонентами явились собственно модель, а также раннер и набор колбэков для него. Модель успешно обучилась и показала высокую точность на тестовой выборке.

Спасибо за внимание! Надеемся, что наш опыт был вам полезен.

Больше наших статей по машинному обучению и обработке изображений:

Подробнее..

Подборка статей о машинном обучении кейсы, гайды и исследования за декабрь 2020

11.01.2021 18:14:50 | Автор: admin


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

MuZero


DeepMind неожиданно опубликовали статью о MuZero, алгоритме, который способен играть как в популярные логические настольные игры вроде шахмат, Сёги и Го, так и в видеоигры Atari вроде Pac-Man.

MuZero пытается смоделировать не всю среду, а только отдельные аспекты, которые важны для процесса принятия стратегических решений агентом. Алгоритм постоянно собирает информацию о текущем и предыдущем состоянии игры изучая таким образом запреты и награды. Так, например, модель понимает, что в шахматах цель игры поставить мат, а в пакмане съесть желтую точку.

Есть еще одно важное преимущество: MuZero многократно использует изученную модель для улучшения планирования, а не для сбора новых данных о среде. Например, в играх Atari со сложной изменяющейся средой алгоритм использовал изученную модель в 90% случаев чтобы перепланировать то, что должно было быть сделано в прошлых игровых сессиях.

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



Infinite Nature


Все хоть раз видели захватывающую съемку с дрона, летящего вдоль живописной береговой линии. Обученный на подобных роликах с youtube алгоритм синтезирует видео из одного статичного изображения.

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

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



Time Travel Rephotography


Нейросеть для реставрации и колоризации старых фото, напоминающая DeOldify. В отличие от обычных фильтров восстановления изображений, которые применяют независимые операции, такие как шумоподавление, раскрашивание и апскейлинг, здесь используется StyleGAN2, чтобы синтезировать близкое к оригиналу лицо. На выходе получаются портретные фотографии сразу в цвете и хорошем разрешении. Код тоже обещают выкатить позже.



pi-GAN


Еще одна GAN-модель, которая генерирует 3D представление объекта из нескольких неразмеченных двухмерных изображений. В демо показано, как модель можно использовать для вращения головы, подобно тому как ранее демонстрировали Nvidia в Maxine.



Neural Scene Flow Fields


Новый NeRf-метод, который строит представление динамической сцены из видео, снятого на обычную камеру. Это позволяет, например, остановить кадр и сдвинуть камеру или наоборот зафиксировать камеру, но будто перемотать время. Алгоритм дорисовывает окружение со сложной структурой, например, с тонкими объектами вроде решеток, и движущимися объектами вроде мыльных пузырей.



YolactEdge


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

ModNet


Технология, которая позволяет не только качественно удалять фон с портретных снимков, но также заменять фон на видео. По факту это может стать неплохой заменой хромакея. В отличие от платного remove.bg, здесь есть и исходный код, коллаб и даже веб-приложение с простеньким интерфейсом, в котором пока можно протестировать только работу с фото.

Svoice


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


Hypersim


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

ArtLine


Открытая модель, которая превращает фотопортрет в карандашный набросок. Пока что плохо справляется с текстурами одежды и с тенями, но в целом выдает достойные результаты. За основу взята архитектура DeOldify, которая позволяет хорошо распознавать черты лица.

На этом все, вот таким на удивление насыщенным оказался декабрь. Начало года тоже обещает быть интересным. Нам уже не терпится посмотреть, что в январе появится на основе Dall-E от OpenAI. Как говорится, stay tuned!
Подробнее..

Нейродайджест главное из области машинного обучения за январь 2021

01.02.2021 18:10:50 | Автор: admin


12 выпусков позади, значит пора немного поменять формат, но внутри вас всё так же ждут исследования, демонстрации, открытые модели и датасеты. Встречайте новый выпуск подборки материалов о машинном обучении.

DALLE



Доступность: страница проекта / доступ к закрытому API через лист ожидания

OpenAI представили свою новую языковую модель-трансформер DALL-E с 12 миллиардами параметров, натренированную на парах изображение-текст. Модель сделана на основе GPT-3 и используется для синтеза изображений по текстовым описаниям.

В июне прошлого года компания уже показывала, как обученная на последовательностях пикселей с точным описанием модель может дополнять пустоты на изображениях, которые подаются на вход. Результаты тогда уже были впечатляющими, но здесь Open AI превзошли все ожидания. Подобно тому, как GPT-3 синтезирует связные законченные предложения, DALLE создает сложные изображения.



Модели удивительно хорошо удаются антропоморфные объекты (редис, выгуливающий собаку) и сочетание несочетаемых предметов (улитка в виде арфы), из-за чего для названия и выбрали слияние двух имен испанского сюрреалиста Сальвадора Дали и пиксаровского робота ВАЛЛ-И.

Итак, какими результатами может похвастаться модель?

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

Помимо этого, модель способна применять к сцене оптические эффекты, например как при съемке с фишай-объектива. Но с отражениями пока справляется плохо куб в зеркале синтезировать убедительным образом не удалось. Таким образом, с разной степенью надежности DALLE через естественный язык справляется с задачами, для которых в индустрии используются движки 3D-моделирования. Это позволяет использовать ее для отрисовки рендеров дизайна помещений.

Модель неплохо осведомлена о географии и знаковых достопримечательностях, а также об отличительных чертах отдельных эпох. Она может синтезировать фотографию старинного телефонного аппарата или моста Золотые Ворота в Сан-Франциско.

При всем этом модели не нужно сверхточное описание часть пробелов она восполнит сама. Как отмечает Open AI, чем точнее описание, тем хуже результат.

Напомним, что GPT-3 это zero-shot модель, её не нужно дополнительно настраивать и обучать для выполнения конкретных задач. Помимо описания можно дать подсказку, чтобы модель сгенерировала нужный ответ. DALLE делает тоже самое с визуализацией и может выполнять разные задачи image-to-image преобразования, опираясь на подсказки. Например можно дать на вход изображение и попросить сделать его в виде скетча.

Что поразительно, создатели не ставили перед собой такую цель и никак не предусматривали это при обучении модели. Способность обнаружилась только в ходе тестирования.
Руководствуясь этим открытием, авторы изучили способность DALLE решать логические задачи визуального IQ-теста и поставили задачу не выбирать правильный ответ из представленных вариантов, а полностью предсказывать недостающий элемент.
В целом, модели удалось правильно продолжить последовательность в части задач, где требовалось геометрическое осмысление.

Модель пока не выложили, и даже нет примерного описания ее архитектуры. На данном этапе можно запросить доступ к API или ознакомиться с неофициальной имплементацией на PyTorch (также ведется работа над неофициальной версией на TensorFlow).

CLIP (Contrastive LanguageImage Pre-training)





Доступность: страница проекта / исходный код

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

Во-первых, создание датасетов остается очень затратным, но при этом в результате позволяет распознавать очень ограниченный набор визуальных образов и подходит для узких задач. Например, при подготовке датасета ImageNet, чтобы составить описания к 14 миллионам изображений для 22 000 категорий объектов, потребовалось привлечь 25 000 человек. При этом модель ImageNet хороша для предсказания только тех категорий, которые представлены в датасете, и если потребуется выполнить любую другую задачу, специалистам придется создавать новые наборы данных и доучивать модель.

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

Открытая нейросеть CLIP от OpenAI призвана решить эти проблемы. Модель обучается на большом количестве изображений и текстовых описаний, доступных в интернете, и переводит их в векторные представления, эмбеддинги. Эти представления сопоставляются так, что у надписи и подходящей к ней картинке числа будут близки.

CLIP можно сразу тестировать на разных бенчмарках, не обучая на их данных. Модель выполняет тесты по классификации без прямой оптимизации. Например, тест ObjectNet проверяет способность модели распознавать объекты при разных расположениях и при сменяющемся фоне, в то время как ImageNet Rendition и ImageNet Sketch проверяют способность модели распознавать более абстрактные изображения объектов (не просто банан, а нарезанный банан или скетч банана). CLIP показывает одинаково хорошие результаты на всех них.

CLIP может быть адаптирована для выполнения широкого спектра задач визуальной классификации без дополнительных обучающих примеров. Чтобы применить CLIP к новой задаче, нужно только дать кодировщику названия визуальных представлений, и он выдаст линейный классификатор этих представлений, по точности не уступающий обученным с учителем моделям.
На гитхабе уже появилась реализация для фотографий с Unsplash, которая показывает, насколько хорошо модель группирует изображения. Дизайнеры уже могут ее использовать для разработки мудбордов.

DeBERTa от Microsoft



Доступность: исходный код / страница проекта

Как и обычно, новости от OpenAI затмили прочие анонсы, хотя было еще одно событие, которое активно обсуждалось в сообществе. Представленная Microsoft модель DeBERTa превзошла базовые показатели человека в тесте SuperGLUE на сложное понимание естественного языка (NLU).

Бенчмарк на основе 10 параметров определяет, понимает ли алгоритм прочитанное, и составляет рейтинг. Средний показатель для людей не экспертов составляет 89.8 баллов, и задачи, которые нужно решать модели, сравнимы с экзаменом по английскому. DeBERTa показала 90.3, следом за ней идет T5+Meena от Google.

Таким образом, модели уже во второй раз удалось обогнать человека, но примечательно здесь то, что DeBERTa имеет 1,5 млрд тренировочных параметров, в 8 раз меньше T5.

Модель представляет новый, отличный от оригинального трансформера, разделённый механизм внимания, где каждый токен кодируется векторами контента и позиции, которые не суммируются в один вектор, c ними работают отдельные матрицы.

NeuralMagicEye



Доступность: страница проекта / код / колаб

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

Автор исследования обучил CNN-модель восстанавливать глубину автостереограммы и понимать ее содержание. Чтобы добиться стерео-эффекта, модель нужно было обучить обнаруживать и оценивать несоответствие квазипериодических текстур. Модель обучалась на наборе данных из 3D-моделей, без учителя.

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

StyleFlow



Доступность: исходный код

Как мы это уже не раз видели, с помощью безусловных GAN (типа StyleGAN) можно создавать высококачественные, фотореалистичные изображения. Однако управлять процессом генерации с использованием семантических атрибутов, сохраняя при этом качество вывода, получается редко. Из-за сложного и запутанного латентного пространства GAN, редактирование одного атрибута часто приводит к нежелательным изменениям по другим. Эта модель помогает решить эту проблему. Например, можно менять угол обзора, вариацию освещения, выражение, растительность на лице, пол и возраст.

Taming Transformers


Доступность: страница проекта / исходный код
Трансформеры способны показывать превосходные результаты в разных областях применения. Но с точки зрения вычислительных мощностей они очень требовательные, поэтому не подходят для работы с изображениями с высоким разрешением. Авторы исследования объединили трансформер с индуктивным смещением сверточной сети и смогли изображения с высоким разрешением.

POse EMbedding



Доступность: исходный код

Повседневные действия, будь то бег или чтение книги, можно рассматривать как последовательность поз, состоящую из положения и ориентации тела человека в пространстве. Распознавание поз открывает ряд возможностей в AR, управление жестами и т.д. Однако данные, полученные из двухмерного изображения, отличаются в зависимости от точки обзора камеры. Данный алгоритм от Google AI распознает сходство поз при разных ракурсах, сопоставляя ключевые точки двухмерного отображения позы с инвариантным к обзору эмбеддингом.

Learning to Learn


Доступность: исходный код

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

Вот таким ярким был первый месяц этого года. Спасибо за внимание, и следите за будущими выпусками!
Подробнее..

Нейродайджест главное из области машинного обучения за февраль 2021

01.03.2021 20:22:05 | Автор: admin

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


Papers with Datasets and Libraries


Есть такой ресурс Papers with Code, миссия которого прямо соответствует названию агрегировать публикации из сферы машинного обучения, у которых есть код, а также дать возможность предложить свою имплементацию.

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

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

Google Model Search


Доступность: страница проекта, репозиторий

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

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

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

ZenML


Доступность: сайт проекта / репозиторий

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

TensorFlow 3D


Доступность: Статья / репозиторий

С распространением устройств, захватывающих 3D-данные, вроде лидаров и камер измерения глубины, обострилась потребность в технологии обработки этих данных и понимания трехмерной сцены. Это нужно для навигации и работы в реальном мире самоуправляемых автомобилей и роботов, а также для совершенствования AR-технологий.

Google представили модульную библиотеку для применения глубокого обучения на 3D-данных в TensorFlow. Она содержит пайплайны обучения и оценки для трехмерной семантической сегментации, классификации сцены, обнаружения трехмерных объектов и т.д.

MeInGame



Доступность: статья / репозиторий

В компьютерных играх часто есть редактор персонажа, который позволяет с помощью настроек разных параметров менять внешность игрока. Алгоритм MeInGame позволяет создать кастомного персонажа всего по одной фотографии. Нейросеть предсказывает форму лица и его текстуру. Хотя методы, основанные на 3D Morphable Face Model (3DMM), могут генерировать 3D-портрет из отдельных изображений, топология сетки обычно отличается от тех, что используются в большинстве игр. Авторы этого алгоритма заявляют, что эту проблему решили.

SAM



Доступность: статья / репозиторий

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

Внутри используется StyleGAN, но здесь исследователи также используют предварительно обученную сеть возрастной регрессии, с помощью которой кодировщик генерирует скрытые коды, соответствующие целевому возрасту. Метод рассматривает процесс непрерывного старения как задачу регрессии между входным возрастом и целевым возрастом, обеспечивая точный контроль над созданным изображением. Модель позволяет редактировать сгенерированные изображения.

VOGUE



Доступность: страница проекта / интерактивное демо

Новый кейс применения StyleGAN для виртуальной примерки одежды. Алгоритм переносит одежду с фотографии одного человека на фотографию человека, которая подается на вход. В основе метода лежит интерполяция скрытого пространства с учетом позы StyleGAN2, которая работает с формой тела, волосами, цветом кожи целевого человека. Алгоритм позволяет одежде деформироваться в соответствии с заданной формой тела, сохраняя при этом узор и детали материала. На выходе получается фотореалистичные изображения в достойном разрешении 512x512.

NeRViS



Доступность: страница проекта / репозиторий

Существующие методы стабилизации видео либо сильно обрезают границы кадра, либо создают артефакты и искажения. Данный алгоритм
предварительно оценивает плотные поля деформации и для синтеза полного стабилизированного кадра использует соседние кадры. Новизна подхода в основанном на обучении гибридном пространственном синтезе, который устраняет артефакты, вызванные неточностью оптического потока и быстро движущимися объектами.

Stable View Synthesis


Доступность: статья / репозиторий

На основе набора фотографий, изображающих сцену со свободно распределенных точек обзора, алгоритм синтезирует новые виды сцены. Метод работает на геометрическом скаффолдинге, который расчитывается на основе SfM-фотограмметрии. Целевое представление рендерится сверточной сетью из тензора характеристик, синтезированных для всех пикселей.

Статью опубликовали еще в ноябре прошлого года, но код стал доступен только сейчас.

JigsawGan


Доступность: статья

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

CharacterGAN



Доступность: статья / репозиторий

Генеративная нейросеть, которую можно обучить только на нескольких изображениях персонажа в разных позах, для генерации новых поз по расположению ключевых точек. Это позволяет анимировать статичные изображения. Новизна подхода в том, что изображение разбивается на слои, каждый из которых обрабатывается отдельно. Это решает проблему преграждений, когда на передний план выходит посторонний объект. Для удобства добавлен GUI, позволяющий вручную корректировать позы по ключевым точкам.

Discrete VAE


Доступность: репозиторий

В прошлом выпуске мы рассказывали о потрясающей DALL-E. В конце февраля OpenAI создали репозиторий с названием модели, но саму модель пока что не выложили внутри только часть модели, а именно PyTorch пакет для discrete VAE. Это вариационный автоэнкодер, который, в нашем случае, генерирует изображения из текстовых описаний.

Deep Nostalgia


Доступность: онлайн-сервис

Ну и напоследок, всегда приятно когда на основе моделей делают простой и понятный продукт. Так вот, компания MyHeritage, которая занимается вопросами генеалогии и родословных, судя по всему, взяла алгоритм First Order Model, прикрутила удобный пользовательский интерфейс и сделала на его основе сервис по оживлению фотографий.

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

На этом все, спасибо за внимание и до встречи через месяц!
Подробнее..

Перевод Как удалить татуировку с помощью глубокого обучения

20.04.2021 16:13:49 | Автор: admin

Глубокое обучение интересная тема и моя любимая область исследований. Мне очень нравится играть с новыми исследовательскими разработками специалистов по глубокому обучению. Я только что наткнулся на удивительный репозиторий GitHub одного из моих товарищей по группе компьютерного зрения. Мне он так понравился, что я решил поделиться им. Основа репозитория генеративно-состязательная сеть (GAN), которая способна удалять татуировки с тела. Я расскажу вам шаг за шагом, как применять упомянутый репозиторий на примере фотографии в Pexels.


Запуск Google Colab

Google Colab бесплатно предоставляет мощные возможности графического процессора, чтобы выполнять или обучать наши модели глубокого обучения за меньшее время. Введите в браузере следующий URL-адрес и нажмите клавишу Enter:

https://colab.research.google.com/

После запуска войдите в свою учётную запись Google. Если вы уже вошли, платформа просто выберет основную учётную запись для входа. Не беспокойтесь! Ваши данные здесь в безопасности. После входа перейдите к файлу и откройте новую записную книжку.

Клонирование репозитория GitHub

Теперь в только что созданной записной книжке мы должны выполнить такую команду:

!git clone https://github.com/vijishmadhavan/SkinDeep.git SkinDeep
Эта команда клонирует код GitHub в вашу среду Colab.Эта команда клонирует код GitHub в вашу среду Colab.

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

cd SkinDeep

Установка библиотек

Чтобы установить все необходимые библиотеки, в очередной ячейке выполните:

!pip install -r colab_requirements.txt

Определение архитектуры модели

Теперь настало время инициализировать архитектуру модели. Архитектура доступна в том же репозитории GitHub, который мы клонировали. Чтобы инициализировать модель, в соседней ячейке выполните следующий код:

import fastaifrom fastai.vision import *from fastai.utils.mem import *from fastai.vision import open_image, load_learner, image, torchimport numpy as npimport urllib.requestimport PIL.Imagefrom io import BytesIOimport torchvision.transforms as Tfrom PIL import Imageimport requestsfrom io import BytesIOimport fastaifrom fastai.vision import *from fastai.utils.mem import *from fastai.vision import open_image, load_learner, image, torchimport numpy as npimport urllib.requestimport PIL.Imagefrom io import BytesIOimport torchvision.transforms as Tclass FeatureLoss(nn.Module):    def __init__(self, m_feat, layer_ids, layer_wgts):        super().__init__()        self.m_feat = m_feat        self.loss_features = [self.m_feat[i] for i in layer_ids]        self.hooks = hook_outputs(self.loss_features, detach=False)        self.wgts = layer_wgts        self.metric_names = ['pixel',] + [f'feat_{i}' for i in range(len(layer_ids))              ] + [f'gram_{i}' for i in range(len(layer_ids))]    def make_features(self, x, clone=False):        self.m_feat(x)        return [(o.clone() if clone else o) for o in self.hooks.stored]        def forward(self, input, target):        out_feat = self.make_features(target, clone=True)        in_feat = self.make_features(input)        self.feat_losses = [base_loss(input,target)]        self.feat_losses += [base_loss(f_in, f_out)*w                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]        self.feat_losses += [base_loss(gram_matrix(f_in), gram_matrix(f_out))*w**2 * 5e3                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]        self.metrics = dict(zip(self.metric_names, self.feat_losses))        return sum(self.feat_losses)        def __del__(self): self.hooks.remove()

Загрузка файла модели

После инициализации модели загрузите предварительно обученную модель GAN для удаления татуировок. В очередной ячейке выполните эти команды:

MODEL_URL = "https://www.dropbox.com/s/vxgw0s7ktpla4dk/SkinDeep2.pkl?dl=1"urllib.request.urlretrieve(MODEL_URL, "SkinDeep2.pkl")path = Path(".")learn=load_learner(path, 'SkinDeep2.pkl')

Входное изображение

Наконец, можно определить своё входное изображение для тестирования. В приведённом ниже сегменте кода подставьте URL-адрес изображения.

url = 'https://images.pexels.com/photos/5045947/pexels-photo-5045947.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260' #@param {type:"string"}response = requests.get(url)img = PIL.Image.open(BytesIO(response.content)).convert("RGB")img_t = T.ToTensor()(img)img_fast = Image(img_t)show_image(img_fast, figsize=(8,8), interpolation='nearest');

Тестирование модели и получение результатов

Начинается самое интересное. Запустим модель, чтобы получить результат. В соседней ячейке выполните следующие строки кода:

p,img_hr,b = learn.predict(img_fast)Image(img_hr).show(figsize=(8,8))

Заключение

Вот и всё. Мы обсудили пошаговое реальное применение модели SkinDeep для удаления татуировок с кожи. Подобные забавы лишь малая демонстрация потенциала глубокого обучение. Оно способно способно генерировать новые функции без вмешательства человека, из ограниченного набора функций, расположенных в наборе учебных данных. Для специалистов это означает, что они могут использовать более сложные наборы функций по сравнению с традиционным ПО для машинного обучения. Если вас заинтересовала эта сфера ждем вас на расширенном курсе Machine Learning и Deep Learning, в котором мы совместили изучение DL с классическим курсом по ML, чтобы студент начал с основ и постепенно перешел к более сложным вещам.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Обучаем качественные модели без DensePose разметки

06.06.2021 12:22:20 | Автор: admin

Возможно ли обучить качественную модель, которая предсказывает 3D координаты поверхности тела животного по фотографии, без соответствующей DensePose разметки?

Исследователи из Facebook Artificial Intelligence Research затронули этот вопрос на конференции по машинному зрению CVPR 2020.

Источник: https://www.facebook.com/watch/?v=678774242681114Источник: https://www.facebook.com/watch/?v=678774242681114

О задаче DensePose

Ранее эти исследователи представили научному сообществу новый датасет DensePose-COCO и архитектуру нейронной сети, которая работает с этими данными (статья). Этот датасет состоит из собранной особым образом разметки людей на картинках из COCO 2014.

Подробнее одатасете

Данные включают в себя:

  • bounding boxes людей на фото,

  • pixel-perfect foreground-background маски,

  • сегментацию 32х частей тела, внутри вышеупомянутой маски,

  • и большой набор троек (c, u, v) для каждой фотографии, где cэто индекс части тела, u, vэто геодезические координаты в пределах части тела.

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

Разметчикам ставили задачу определить соответствие между случайно брошенными на изображение человека точками (в пределах маски сегментации) и точками на шести пререндеренных изображениях 3D модели человека, т.е. на шести 2D проекциях модели SMPL под разными углами. Получив такую разметку исследователи восстановили (c, u, v) координаты для этих точек на поверхности человека.

По этому принципу было собрано 5 миллионов точек для 50 тысяч изображений людей внутри датасета COCO 2014.

Такого рода данные позволили построить Mask-RCNN подобную модель для предсказания масок с 3D координатами по снимкам.

Это решение было примечательно фоллоу-апами с переодеванием. Поскольку нейронная сеть позволяет получить модель человека, то можно поместить поверх этой модели модель одежды. Примеры ниже:

Визуализации в формате видео можно увидеть здесь.

Теперь об обезьянах

В новой статье исследователи предлагают воспользоваться разметками из DensePose-COCO и COCO Dataset для того, чтобы решить схожую задачу предсказания 3D координат поверхности животных. При этом предлагается не размечать изображения животных по описанному выше алгоритму. Утверждается, что можно воспользоваться существующей разметкой не только для шимпанзе, но и для любых других животных, которые менее похожи на человека с точки зрения анатомии.

Таким способом исследователям удалось достичь качества по метрике DensePose Average Precision = 34.9. Имея размеченные данные для класса людей, модель из первой статьи показывала результат = 46.8. Это при том, что метрика варьируется от 0 до 100. Неплохой результат knowledge transferring?

Чтобы измерить качество новой модели, потребовалось разметить некоторый объем фотографий шимпанзе (по образу того, как это делалось для человека). Для этого был предложен метод восстановления соответствия между точками SMPL модели человека и очень детальной 3D модели шимпанзе.

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

Show me thecode!

Как это обычно бывает со статьями, которые выпускают именитые научные группы по типу FAIRони сопровождаются кодом. Для обеих статей он доступен внутри официального репозитория detectron2 на GitHub.

Ранний код из первой статьи, написанную с использованием detectron первой версии, в основе которого лежит Caffe2, можно так же найти на GitHub.

Подробнее..

Как мы на хакатоне транспорт кластеризировали

30.03.2021 20:09:12 | Автор: admin

Привет, Хабр! Компьютерное зрение и искусственный интеллект одни из самых востребованных направлений в современном IT. Поэтому мы выбрали именно их для учебного"Межгалактического Хакатона 2021"который организовали НИТУ МИСиС и Zavtra.Online (подразделение SkillFactory по работе с университетами).

В хакатоне были представлены 5 кейсов от разных компаний, и одним из них был кейс от компании IntelliVision кластеризация изображений транспортных средств. Его и выбрала команда финалистов, описав реализацию подобного проекта от А до Я.


Начало работы

Целью любого кластерного анализа является поиск существующих структур. Так и в нашей задаче были даны изображения, которые нужно разбить на кластеры и интерпретировать каждый из них. В качестве исходных данных нам предоставили изображения транспортных средств разных типов, цветов, ракурсов и деталей. Исходные изображения были загружены в нейронную сеть, которая определила паттерны и построила модель, и эта модель отображается в виде вектора (дескриптора), полученного на промежуточном (скрытом) слое нейронной сети. Варианты дескрипторов, полученные с помощью глубокого обучения, были исходными данными для дальнейшей кластеризации: color_model, osnet, efficientnet-b7, type_model.

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

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

Особенности выполнения задания

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

Сначала проводилась работа по понижению размерности данных для увеличения скорости обучения моделей, но PCA давал малоинформативные результаты, а t-SNE из библиотеки scikit-learn имел низкую производительность. И тут на помощь пришёлRapids бесплатный open-source фреймворк для ускорения обработки данных от NVIDIA, в котором есть библиотека машинного обучения cuML. Задача была решена в Jupyter Notebook в Google Colab, так как в эту среду легко установитьRapids, а также использовать его совместно с GPU Tesla K80 с 13 Гб видеопамяти на борту.

Исследование набора данных color_model

Этот набор данных в виде csv файла из 416314 строк и 128 столбцов был получен при помощи модели регрессии для определения цвета транспортных средств в формате RGB. Это единственный набор, который было возможно использовать для проведения экспериментов без понижения размерности.

Кандидатами в лучшее число кластеров на основе метода локтя оказались 3 и 4.

По визуализации результатов кластеризации (кандидаты в лучшие кластеры) для стандартизированных и нормированных данных видно, что предобработка данных повлияла на результат кластеризации. Разделение на 2 и 3 кластера почти одинаковое вне зависимости от способа обработки данных, но есть разница в разбиении на 4 кластера.

Это увеличенные результаты кластеризации на данных, размерность которых была понижена при помощи t-SNE. Видно, что алгоритм пытается выделять кластеры и один из них синий отделил успешно, но в целом чёткой границы нет.

По итогам экспериментов мы визуализировали случайные картинки для каждого из результатов разбиения на 2, 3, 4 кластера.

Видно, что в зависимости от потребностей при 2-х, 3-х и 4-х кластерах вне зависимости от типа обработки можно получить кластеры:

[Светлый-Тёмный],

[Светлый-Тёмный-Цветной],

[Светлый-Серый-Тёмный-Цветной]

А что же другие алгоритмы? Мы попробовали также DBSCAN, и у него получилось выделить отдельно выбросы фото низкого качества. Это фото с включёнными фарами, вблизи и сзади. Интересный эффект кластер жёлтых автомобилей.

Видно, что, комбинируя K-means и DBSCAN, можно в зависимости от потребностей получить разбивку на цветовые кластеры с различной детализацией, а также выделить выбросы.

Исследование набора данных osnet

Следующим набором дескрипторов, на котором мы провели исследования, сталosnet.

Из условий задачи нам было известно, что для построения дескрипторов osnet использовалась нейронная сеть, обученная для повторной идентификации людей, животных и машин (reID). В отличие от type_model и color_model эта модель не обучалась на исходном датасете veriwild.

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

В связи большим размером данныхosnet(csv файл из 416314 строк и 512 столбцов) и ограниченности имеющихся вычислительных мощностей и сроков все эксперименты проводились на предобработанных данных с пониженной при помощи t-SNE размерностью.

Помимо простого алгоритма k-Means мы решили проверить Mini-Banch k-Means. Как и ожидалось, Mini-Banch k-Means справился быстрее, но качество кластеризации, показанное простым k-Means, оказалось лучше.

Как и на других наборах данных, оптимальное число кластеров определяли с помощьюметода локтя.Оптимальными кандидатами являются 3-й и 4-й кластеры.

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

В завершение эксперимента мы пытались визуально оценить и интерпретировать результаты разбиения с помощью случайной выборки из 9 фотографий для каждого кластера.

В результате мы увидели, что при увеличении количества кластеров модель старается разделить выборку одновременно по цвету автомобиля и ракурсу съёмки.

Также мы увидели, что модель не разделила транспортные средства на отдельные кластеры в зависимости от типа: седан/джип/грузовик/автобус. Можно предположить, что другие параметры кластеризации позволят улучшить качество такого разбиения.

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

Исследование набора данных efficientnet-b7

Следующим этапом исследования было исследование работы модели на дескрипторе Efficientnet-b7. Этот дескриптор был самым большим по размеру (416314, 2560), данная модель классификации изображений обучена на Imagenet и до этой работы данных из veriwild не видела.

Было решено уменьшить размерность модели, чтобы была возможность в разумные сроки сделать кластеризацию на доступных компьютерах. Для этого применён метод t-SNE, как показавший ранее большую точность итоговых сжатых данных.

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

Затем кластеризовали данные на 2, 3, 4, 20 кластеров методом k-Means, как показавшим лучшие результаты на предыдущих моделях, описанных выше.

Аналогично описанию выше было определено исследуемое количество кластеров с помощью Elbow method clistering (метод локтя):

В силу схожей природы индекса Дэвиса Болдуина (ДБ) и метода локтя в данном исследовании взят индекс ДБ с его локтями в точках 9 и 20. Для того чтобы компенсировать недостаток данного индекса, заключающийся во влиянии масштаба данных на значение индекса, применена нормализация данных перед исследованием (craftappmobile.com).

Визуализация для нормализованной модели:

Результаты кластеризации (в виду большого числа картинок, все под спойлерами):

3 кластера
Кластер 0 светлые машины, задом.Кластер 0 светлые машины, задом.Кластер 1 тёмные машины.Кластер 1 тёмные машины.Кластер 2 светлые машины, передом.Кластер 2 светлые машины, передом.
4 кластера попытка разделить на светлые/тёмные зад/перед
Кластер 0 светлые машины, передом.Кластер 0 светлые машины, передом.Кластер 1 тёмные машины, передом.Кластер 1 тёмные машины, передом.Кластер 2 светлые машины, задом много выбросов.Кластер 2 светлые машины, задом много выбросов.Кластер 3 тёмные машины, задом много выбросов.Кластер 3 тёмные машины, задом много выбросов.
9 кластеров попытка разделить на светлые/тёмные зад/перед ракурс тип кузова по цвету
Кластер 0 светлые машины, передом, ракурс направо, внедорожник (вэн) много выбросов.Кластер 0 светлые машины, передом, ракурс направо, внедорожник (вэн) много выбросов.Кластер 1 тёмные машины, задом, ракурс влево много выбросов. Кластер 1 тёмные машины, задом, ракурс влево много выбросов. Кластер 2 светлые машины, задом, ракурс влево.Кластер 2 светлые машины, задом, ракурс влево.Кластер 3 светлые машины, задом, ракурс вправо, внедорожник (вэн) много выбросов.Кластер 3 светлые машины, задом, ракурс вправо, внедорожник (вэн) много выбросов.Кластер 4 синие машины, передом, ракурс вправо.Кластер 4 синие машины, передом, ракурс вправо.Кластер 5 белые машины, передом, ракурс вправо, внедорожник (вэн).Кластер 5 белые машины, передом, ракурс вправо, внедорожник (вэн).Кластер 6 белые машины, задом, ракурс вправо, седан много выбросов.Кластер 6 белые машины, задом, ракурс вправо, седан много выбросов.Кластер 7 белые машины, передом, ракурс вправо, седан много выбросов.Кластер 7 белые машины, передом, ракурс вправо, седан много выбросов.Кластер 8 тёмные машины, передом, ракурс вправо много выбросов.Кластер 8 тёмные машины, передом, ракурс вправо много выбросов.
20 кластеров разделение по цвету зад/перед ракурс тип кузова
Кластер 0 красный, передом, влево много выбросов из-за сливающегося цвета фар.Кластер 0 красный, передом, влево много выбросов из-за сливающегося цвета фар.Кластер 1 т.-серый, перед, вправо, кроссовер.Кластер 1 т.-серый, перед, вправо, кроссовер.Кластер 2 синий, зад, вправо много выбросов.Кластер 2 синий, зад, вправо много выбросов.Кластер 3 белый, перед, вправо нет ошибок, кузов определён неточно: хэтчбек/кроссовер.Кластер 3 белый, перед, вправо нет ошибок, кузов определён неточно: хэтчбек/кроссовер.Кластер 4 св.-серый, зад, вправо, седан.Кластер 4 св.-серый, зад, вправо, седан.Кластер 5 т.-серый/чёрный, перед, влево, кроссовер.Кластер 5 т.-серый/чёрный, перед, влево, кроссовер.Кластер 6 белый, зад, вправо, кроссовер.Кластер 6 белый, зад, вправо, кроссовер.Кластер 7 белые, перед, вправо, седан.Кластер 7 белые, перед, вправо, седан.Кластер 8 белый, зад, влево, кроссовер.Кластер 8 белый, зад, влево, кроссовер.Кластер 9 т.-серый, перед, вправо, седан.Кластер 9 т.-серый, перед, вправо, седан.Кластер 10 т.-серый, зад, вправо, седан.Кластер 10 т.-серый, зад, вправо, седан.Кластер 11 св.-серый, перед, влево, вэн. Кластер 11 св.-серый, перед, влево, вэн. Кластер 12 св.-серый, перед, вправо, вэн.Кластер 12 св.-серый, перед, вправо, вэн.Кластер 13 белый, перед, вправо, седан.Кластер 13 белый, перед, вправо, седан.Кластер 14 т.-серый/чёрный, перед, влево, седан смешан с кластером 5.Кластер 14 т.-серый/чёрный, перед, влево, седан смешан с кластером 5.Кластер 15 белый, перед, влево, седан.Кластер 15 белый, перед, влево, седан.Кластер 16 т.-серый, зад, влево, седан.Кластер 16 т.-серый, зад, влево, седан.Кластер 17 св.-серый, перед, вправо, кроссовер.Кластер 17 св.-серый, перед, вправо, кроссовер.Кластер 18 чёрный, перед, вправо, кроссовер много выбросов.Кластер 18 чёрный, перед, вправо, кроссовер много выбросов.Кластер 19 чёрный, перед, вправо, седан.Кластер 19 чёрный, перед, вправо, седан.

Выводы:

  1. Модель кластеризации с 20 кластерами, определёнными методом локтя с помощью индекса Дэвиса Болдуина показала себя лучше, чем модели с меньшим количеством кластеров, и в целом достаточно информативно.

  2. После кластеризации стало заметно, что стандартизированная модель кластеризует заметно с большим количеством ошибок, чем нормализованная.

  3. Ошибок в кластере не более 33 % (3 из 9), но чаще 22 % (2 из 9), при этом всего кластеров с ошибками 40 % (8 из 20).

  4. Следует отметить, что модель плохо различает данные (фотографии), когда фары и машина одного цвета: происходит больше ошибок.

  5. Для упрощения работы с моделью можно применять методы уменьшения размерности, в частности, t_SNE показал лучший результат уменьшив размерность с (416314, 2560) до (416314, 2), что позволило использовать значительно меньшие мощности вычисления и получить вполне интерпретируемый результат.

  6. Более подробного анализа выбросов в данном разделе не производилось.

Исследование набора данных type_model

Набор данных в виде csv файла, имеющего 416314 и 512 столбцов, был получен при помощи модели определения типа, и в целом работа с ним была аналогична работе с набором color.

Кандидатами в лучшее число кластеров на основе метода локтя оказался случай с 6 кластерами. При этом расчёты и визуализации также осуществлялись для большего и меньшего числа кластеров

Кластеризация проводилась как на стандартных, так и на нормированных данных.

Как и в случае с набором данных color, результаты кластеризации на данных, размерность которых была понижена при помощи t-SNE, целом не дают чёткого деления на кластеры.

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

В случае с 3 кластерами, несмотря на относительно чёткое разделение, значимой кластеризации добиться не удалось. Нормированные данные дают некоторое разделение по размеру автомобилей.

Кластер 0 седаны (небольшие авто),
Кластер 1 кроссоверы (джипы),
Кластер 2 микроавтобусы, автобусы (крупные авто).

В случае с 6 кластерами их условно можно идентифицировать следующим образом:

Кластер 0 седаны (вид спереди).
Кластер 1 джипы, хэтчбеки.
Кластер 2 мини-вэны.
Кластер 3 седаны (вид сзади).
Кластер 4 грузовики.
Кластер 5 хэтчбеки.

Ожидаемая кластеризация по типу кузова транспортного средства на основе подхода K-MEANS не была достаточно уверенной, однако подход для всех кластеров стабильно показывает кластеризацию по виду (спереди, сзади), например, седаны по виду спереди и сзади. Существует кластер для пикапов и мини-вэнов (кластеризация по размерам), хэтчбеки и джипы в остальных кластерах выделяются плохо. Автобусы появляются в 7 кластерах в группе с мини-вэнами с шумом от предыдущей кластеризации. Возможным решением является разделение отдельных кластеров на подкластеры.

Заключение

Несмотря на крайне сжатые сроки, поставленная задача была выполнена, и были получены интерпретируемые результаты, которые можно использовать в дальнейшей работе. У кого будут добавления смело делитесь ими в комментариях. Прилагаем ссылку на репозиторий вGitHub, а итоговый файл с разметкой кластеров расположен в репозитории и доступен поссылке.

Участники командыCloud_9:

  • Екатерина Лушпина Team Leader / презентация / коммуникация / статья;

  • Анастасия Сухоносенко Product Manager / план исследования / код-фреймворк / презентация / статья;

  • Александр Кудрявцев Speaker, Team Member / исследование / презентация / спикер / статья;

  • Наталья Авдеева Team Member / исследование / презентация / статья;

  • Павел Озернов Team Member / исследование / техническая поддержка / презентация / статья.

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

Примечание редактора

Если в вашей компании есть кейcы по Data Science, которые вы бы хотели решить в рамках хакатона или стать ментором пишите в личные сообщенияskillfactory_school. Также мы будем рады индустриальным партнёрам для наших программ и можем предложить им разные виды сотрудничества: членство в наблюдательном совете, экспертиза для создания курсов, стажировки в ваших компаниях для студентов и другие виды партнёрства. Давайте развивать Data Science вместе!

Узнать больше про нашу магистратуру можно на сайтеdata.misis.ruи вTelegram-канале.

Ну, и, конечно, не магистратурой единой!.. Хотите узнать больше проData Science,машинное и глубокое обучение заглядывайте к нам на соответствующие курсы; будет непросто, но увлекательно.

Узнайте, как прокачаться в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Как построить свою систему поиска похожих изображений

04.04.2021 14:14:58 | Автор: admin

Представлюсь

Всем привет! Меня зовут Влад Виноградов, я руководитель отдела компьютерного зрения в компании EORA.AI. Мы занимаемся глубоким обучением уже более трех лет и за это время реализовали множество проектов для российских и международных клиентов в которые входила исследовательская часть и обучение моделей. В последнее время мы фокусируемся на решении задач поиска похожих изображений и на текущий момент создали системы поиска по логотипам, чертежам, мебели, одежде и другим товарам.

Эта публикация предназначена для Machine Learning инженеров и написана по мотивам моего выступления Поиск похожих изображений - справочник от А до Я, который был опубликован сообществом Open Data Science на Data Fest Online 2020.

Данная статья содержит справочную информацию по зарекомендованным методам, применяемым в задаче Image Retireval. Прочитав статью, вы сможете построить систему поиска похожих изображений под вашу задачу с нуля (не включая процесс разработки production решения).

О задаче

Поиск похожих изображений (по-другому, Content-Based Image Retrieval или CBIR) - это любой поиск, в котором участвуют изображения.

Проще всего о задаче расскажет картинка сверху из статьи Recent Advance in Content-based Image Retrieval: A Literature Survey (2017).

Сейчас все активнее применяется подход "Поиск по фото", в частности, в e-commerce сервисах (AliExpress, Wildberries и др.). "Поиск по ключевому слову" (с пониманием контента изображений) уже давно осел в поисковых движках Google, Яндекс и пр., но вот до маркетплейсов и прочих частных поисковых систем еще не дошел. Думаю, с момента появления нашумевшего в кругах компьютерного зрения CLIP: Connecting Text and Images ускорится глобализация и этого подхода.

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

Базовые компоненты сервиса

Шаг 1. Обучение модели. Модель может быть сделана на классике CV или на базе нейронной сети. На вход модели - изображение, на выход - D-мерный дескриптор/эмбеддинг. В случае с классикой это может быть комбинация SIFT-дескриптора + Bag of Visual Words. В случае с нейронной сетью - стандартный бэкбон по типу ResNet, EfficientNet и пр. + замысловатые пулинг слои + хитрые техники обучения, о которых мы далее поговорим. Могу сказать, что при наличии достаточного объема данных или хорошего претрена нейронные сети сильно выиграют почти всегда (мы проверяли), поэтому сосредоточимся на них.

Шаг 2. Индексирование базы изображений. Индексирование представляет из себя прогон обученной модели на всех изображениях и запись эмбеддингов в специальный индекс для быстрого поиска.

Шаг 3. Поиск. По загруженному пользователем изображению делается прогон модели, получение эмбеддинга и сравнение данного эмбеддинга с остальными в базе. Результатом поиска является отсортированная по релевантности выдача.

Нейросети и Metric Learning

Нейронная сеть в задаче поиска похожих используется как feature extractor (бэкбон). Выбор бэкбона зависит от объема и сложности данных - рассмотреть можно все от ResNet18 до Visual Transformer.

Первая особенность моделей в Image Retrieval - это магия в голове нейросети. На лидерборде по Image Retrieval борются за построение лучших дескрипторов - тут есть и Combined Global Descriptors с параллельными пулингами и Batch Drop Block для более равномерного распределения активации по выходной карте признаков.

Второй главной фишкой являются функции ошибок. Их очень много. Только в Deep Image Retrieval: A Survey представлено больше десятка зарекомендованных парных лоссов. Еще столько же есть классификационных. Главная суть всех этих лоссов - обучить нейросеть трансформировать изображение в вектор линейно разделимого пространства, так чтобы далее можно было сравнивать эти вектора по косинусному или евклидову расстоянию: похожие изображения будут иметь близкие эмбеддинги, непохожие - далекие. Рассмотрим подробнее.

Функции ошибок

Contrastive Loss

Самая простая для понимания функция ошибки - Contrastive Loss. Это парный лосс, т.е. объекты сравниваются по расстоянию между друг другом.

Нейросеть штрафуется за отдаленность друг от друга эмбеддингов изображений p и q, если эти изображения на самом деле похожи. Аналогично, возникает штраф за близость эмбеддингов, изображения которых на самом деле непохожи друг на друга. При этом в последнем случае мы ставим границу m (например, 0.5), преодолев которую, мы считаем, что нейросеть справилась с задачей "разъединения" непохожих изображений.

Triplet Loss

Triplet Loss берет во внимание три объекта - якорь, позитив (похожий на якорь) и негатив (отличный от якоря). Это также парный лосс.

Здесь мы нацелены на минимизацию расстояния от якоря до позитива и максимизацию расстояния от якоря до негатива. Впервые Triplet Loss был представлен в статье FaceNet от Google по распознаванию лиц и долгое время был state-of-the-art решением.

N-tupled Loss

N-tupled Loss - развитие Triplet Loss, в котором также берется якорь и позитив, но вместо одного негатива используется несколько негативов.

Angular Additive Margin (ArcFace)

Проблема парных лоссов заключается в выборе комбинаций позитивов, негативов и якорей - если их просто брать равномерно случайными из датасета, то возникнет проблема "легких пар". Это такие простые пары изображений, для которых лосс будет 0. Оказывается, сеть достаточно быстро сходится к состоянию, в котором большинство элементов в батче будут для нее "легкими", и лосс для них окажется нулевым - сеть перестанет учиться. Чтобы избежать этой проблемы, стали придумывать изощренные техники майнинга пар - hard negative и hard positive mining. Подробнее о проблеме можно почитать в этой статье. Существует также библиотека PML, в которой реализовано множество методов майнинга, да и вообще в библиотеке представлено много полезного по задаче Metric Learning на PyTorch.

Еще одним решением проблемы являются классификационные лоссы. Рассмотрим одну популярную функцию ошибки, которая привела к state-of-the-art в распознавании лиц три года назад - ArcFace.

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

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

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

Пулинги

Вернемся к архитектуре нейросети и рассмотрим парочку pooling слоев, применяемых в задачах Image Retrieval

R-MAC

Regional Maximum Activation of Convolutions (R-MAC) - пулинг слой, принимающий выходную карту нейронной сети (до глобального пулинга или слоев классификации) и возвращающий вектор-дескриптор, посчитанный как сумма активаций в различных окнах выходной карты. Здесь активацией окна является взятие максимума по этому окну для каждого канала независимо.

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

GeM

Generalized Mean (GeM) - простой пулинг, который может улучшить качество выходного дескриптора. Суть в том, что классический average pooling можно обобщить на lambda-норму. При увеличении lambda мы заставляем сеть фокусироваться на значимых частях изображения, что в определенных задачах может быть важно.

Ранжирование

Индексы

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

Самое простое - сохранить "в лоб" эмбеддинги и делать brute-force поиск по ним, например, с помощью косинусного расстояния. Проблемы появляются тогда, когда эмбеддингов становится много - миллионы, десятки миллионов или еще больше. Скорость поиска значительно снижается, объем занимаемой динамической памяти увеличивается. Одна позитивная вещь остается - это качество поиска, оно идеально при имеющихся эмбеддингах.

Датасет glove, размер эмбеддинга 100, расстояние - angularДатасет glove, размер эмбеддинга 100, расстояние - angular

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

Самые популярные: отечественная NMSLIB, Spotify Annoy, Facebook Faiss, Google Scann. Также, если хочется взять индексирование с REST API "из коробки", можно рассмотреть приложение Jina.

Переранжирование

Исследователи в области Information Retrieval давно поняли, что упорядоченная поисковая выдача может быть улучшена неким способом переупорядочивания элементов после получения исходной выдачи.

Одним из таких методов является Query Expansion. Идея состоит в том, чтобы использовать top-k ближайших элементов для генерации нового эмбеддинга. В самом простом случае можно взять усредненный вектор, как показано на картинке выше. Также можно взвесить эмбеддинги, например, по отдаленности в выдаче или косинусному расстоянию от запроса. Подобные улучшения описаны в едином фреймворке в статье Attention-Based Query Expansion Learning. По желанию можно применить Query Expansion рекурсивно.

k-reciprocal

k-reciprocal - множество элементов из top-k, в числе k ближайших которых присутствует сам запрос. На базе этого множества строят процесс переранжирования выдачи, один из которых описан в статье Re-ranking Person Re-identification with k-reciprocal Encoding. По определению, k-reciprocal ближе к запросу, чем k-nearest neighbors. Соответственно, можно грубо считать элементы, попавшие в множество k-reciprocal заведомо позитивными и изменять правило взвешивания, например, для Query Expansion. В данной статье разработан механизм пересчета дистанций с использований k-reciprocal множеств самих элементов в top-k. В статье много выкладок, это выходит за рамки данного поста, поэтому предлагаю читателю ознакомиться самостоятельно.

Валидация

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

Метрики

В первую очередь - метрики. Рассмотрим такие популярные метрики в задаче Image Retrieval: precision@k, recall@k, R-precision, mAP и nDCG.

precision@k

Показывает долю релевантных среди top-k ответов.

Плюсы:

  • показывает, насколько система избирательна в построении top-k

Минусы:

  • очень чувствительна к числу релевантных для данного запроса, что не позволяет объективно оценить качество поиска, где для разных запросов имеется разное число релевантных примеров

  • достичь значение 1 возможно только, если число релевантных >= k для всех запросов

R-precision

То же самое, что precision@k, где k устанавливается равным числу релевантных к данному запросу

Плюсы:

  • исчезает чувствительность к числу k в precision@k, метрика становится стабильной

Минусы:

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

recall@k

Показывает, какая доля релевантных была найдена в top-k

Плюсы:

  • отвечает на вопрос, найдены ли релевантные в принципе среди top-k

  • стабильна и хорошо усредняется по запросам

mAP (mean Average Precision)

Показывает насколько плотно мы заполняем топ выдачи релевантными примерами. Можно на это посмотреть как на объем информации, полученной пользователем поискового движка, который прочитал наименьшее число страниц. Соответственно, чем больше объем информации к числу прочитанных страниц, тем выше метрика.

Плюсы:

  • объективная стабильная оценка качества поиска

  • является одно-численным представлением precision-recall кривой, которая сама по себе богата информацией для анализа

Минусы

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

Подробнее про метрики в Information Retrieval, в том числе посмотреть вывод mAP, можно почитать здесь.

nDCG (Normalized Discounted Gain)

Данная метрика показывает, насколько корректно упорядочены элементы в top-k между собой. Плюсы и минусы этой метрики не будем рассматривать, поскольку в нашем списке это единственная метрика, учитывающая порядок элементов. Тем не менее, есть исследования, показывающие, что при необходимости учитывать порядок данная метрика является достаточно стабильной и может подойти в большинстве случаев.

Усреднение

Также важно отметить варианты усреднения метрик по запросам. Рассмотрим два варианта:

macro: для каждого запроса считается метрика, усредняем по всем запросам

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

micro: число размеченных релевантных и отдельно успешно найденных релевантных суммируется по всем запросам, затем участвует в дроби соответствующей метрики

+: запросы оцениваются с учетом числа размеченных релевантных для каждого из них
-: метрика может стать сильно низкой / сильно высокой, если для какого-то запроса было очень много размеченных релевантных и система неуспешно / успешно вывела их в топ

Схемы валидации

Предлагаю рассмотреть два варианта валидации.

Валидация на множестве запросов и выбранных к ним релевантных

На вход: изображения-запросы и изображения, релевантные к ним. Имеется разметка в виде списка релевантных для данного запроса.

Для подсчета метрик можно посчитать матрицу релевантности каждый с каждым и, на основе бинарной информации о релевантности элементов посчитать метрики.

Валидация на полной базе

На вход: изображения-запросы, и изображения, релевантные к ним. Также должна быть валидационная база изображений, в которой в идеале отмечены все релевантные к данным запросам. А также в ней не должно присутствовать изображений-запросов, иначе придется их чистить на этапе поиска, чтобы они не засоряли top-1. Валидационная база участвует как база негативов - наша задача вытянуть релевантные по отношению к ней.

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

Пример реализованного проекта

Некоторые компании спорят с другими компаниями, чтобы вторые не использовали изобразительные элементы бренда первых. В таких случаях более слабый производитель пытается паразитировать на успехе крупного бренда, выдавая свои продукты и услуги под похожей символикой. От этого страдают и пользователи - вы можете по ошибке купить сыр не того производителя, которому вы уже доверяете, а взять подделанный товар, не прочитав внимательно этикетку. Пример из недавнего: Apple Is Attempting to Block a Pear Logo Trademark.

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

Устройство нашей системы

Для обучения, валидации и разработки поискового приложения мы разработали такую систему. Здесь Training pipeline, Benchmark, Indexer и Demo Web app - независимые репозитории, Logo Search app - поисковое приложение одного из клиентов. Объем индексируемой базы изображений: 1.5 млн товарных знаков.

Примеры работы

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

Заключение

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

Подробнее..

Как открывали глаза компьютерам

04.06.2021 14:18:19 | Автор: admin

В 1998 году выпускник ННГУ им. Лобачевского Вадим Писаревский устроился на работу в небольшую IT-компанию и получил задание начать работать над технологией, о которой ничего не знал. В то время он и не предполагал, что эта работа затянется на десятилетия, а на основе созданных с его участием разработок в мире будет изготовлено несколько миллиардов гаджетов, и жизнь людей на планете Земля существенно изменится.

Как малая компания начала работу планетарного масштаба

Все началось с того, что математики и IT-специалисты выходцы из Сарова создали Нижегородскую Программную Технологическую Лабораторию (NSTL Nizhny Software Technology Lab[1]). Ранее они работали над исполнением контракта между Intel и Саровским ВНИИЭФ. Но сотрудничество с международными компаниями в закрытом городе атомщиков, с его режимом секретности, было очень неудобным. Поэтому несколько саровских специалистов и переехали в областной центр, сохранив при этом связи с Intel. Мы выполняли несколько контрактов с Intel, когда мне предложили взяться за работу над библиотекой[2]компьютерного зрения, вспоминает Валерий Федорович Курякин (в те годы один из руководителей NSTL). У меня уже был опыт работы с компьютерным зрением, и я понимал перспективность этой технологии. И хотя тогда был вынужден разрываться между перспективными и менее перспективными, но приносящими деньги проектами (сил у компании не хватало), решил согласиться.

Сил у NSTL действительно не хватало. В то время в ней работали менее 40 человек, в основном, молодых специалистов. Да и не все сотрудники горели желанием работать неизвестно над чем. Безуспешно перепробовав нескольких программистов, руководитель NSTL дал задание начать работать над библиотекой двум вчерашним выпускникам ННГУ им. Лобачевского: Вадиму Писаревскому (сегодня сотрудник Института искусственного интеллекта и робототехники для общества (г. Шэньчжень, Китай) и Виктору Ерухимову (сейчас CEO & Founder в itSeez3D). О компьютерном зрении они не знали ничего, но за работу взялись с энтузиазмом. Ощущения чего-то большого у меня не было, зато были интересные задачи и какой-то бесконечный драйв, когда все бегали с вытаращенными глазами, вспоминает Виктор Ерухимов. В те годы нижегородские разработчики работали в парах со специалистами Intel в США, а курировали весь проект его инициаторы в Intel Гари Брадски и Шинн Ли. Позже, когда нижегородцы показали хорошие результаты, им предоставили большую автономию.

Количество занятых в проекте сотрудников постепенно увеличивалось. К 2000 году у нас сложилась техническая команда, рассказывает Вадим Писаревский. Я был техническим лидером, но не справился бы без таких людей, как Сергей Обломов, который разбирался, как строить библиотеки в Linux, Валерий Черепенников, занимавшийся оптимизацией, или Валерий Мосягин, специализировался на калибрации камер. Всего нас было уже человек десять. Примерно столько же инженеров работало и в США. И этими силами был создан первый готовый продукт.

Выпущенная в 2000 году первая версия библиотеки получилась достаточно убедительной и наделала много шума в мировом IT-сообществе. Функциональность ее по современным меркам была весьма скромной: простейшая обработка изображений, калибрация камер, возможность отслеживать движение точек на компьютере Но некоторые разработанные тогда программы используются и до сих пор. К примеру, когда вы делаете фотографию смартфоном, вокруг лиц фотографируемых людей появляются прямоугольники, и на них наводится резкость. Эта функция присутствовала еще в первой версии библиотеки, и написанная тогда программа до сих пор используется практически без изменений. Главное же: разработка сразу стала применяться для решения практических задач. Компьютерное зрение стало использоваться, например, на конвейерах промышленных предприятий для определения координат передвигающихся деталей или для контроля правильности пайки электронных плат.

Как к разработке компьютерного зрения привлекли мировое сообщество

Вскоре после первого успеха американский инициатор проекта Гари Брадски убедил руководство Intel развивать технологию в виде свободно распространяемой библиотеки с открытым исходным кодом. То есть, доступ к разработке Intel мог бесплатно получить любой программист мира. Сейчас, когда все крупные компании участвуют в разработке открытого кода и поддерживают проекты, разрабатываемые другими командами, это решение кажется очевидным. Но тогда, 20 лет назад, компании еще не были готовы вкладываться в разработки, которые затем выложат в открытый доступ, и конкуренты смогут использовать их бесплатно. Аргументом в пользу такого решения могло служить только то, что Intel не сможет зарабатывать на производстве и продаже более мощных процессоров, если не будет существовать программного обеспечения, требующего большей вычислительной мощности.

В 2000 году библиотека получила свое нынешнее название OpenCV. Я думаю, что это было единственно верное решение, заявляет Вадим Писаревский. Такие большие проекты не в силах реализовать не только один человек, но и одна команда. Они под силу только мировому IT-сообществу. Десятки тысяч программистов со всего мира стали пользоваться разработками программистов Intel, но и тысячи энтузиастов начали присылать разработанные ими патчи[3]для развития библиотеки.

Библиотека быстро росла и превратилась в мощный инструмент для развития технологии компьютерного зрения. Если раньше каждый программист должен был начинать любую свою разработку с азов, создавая простейшие программы, то теперь он использовал для нее готовые модули OpenCV. Поэтому OpenCV резко снизил порог входа на рынок. Для иллюстрации этого тезиса один из участников проекта (сегодня руководитель Intel IOTG Computer Vision) ирилл Корняков рассказал о своей работе со студентами: Во время пандемии группа из трех студентов-второкурсников, сидя на карантине, за пару недель реализовала проект, позволяющий детектировать: есть ли на лице человека маска? Актуальная тогда тема. Еще лет десять назад на этот проект взяли бы группу высококвалифицированных программистов и дали бы им на работу несколько месяцев. Теперь, благодаря использованию возможностей OpenCV, такая разработка даже на уровень курсовой не тянет.

Как OpenCV ушла от Intel к Intel

Разумеется, интерес к столь удобному инструменту быстро рос. Количество пользователей OpenCV резко увеличилось, но энтузиазм Intel по развитию этой библиотеки в середине 2000-х стал угасать. Команда с пиковой численности примерно в 30 сотрудников была уменьшена до двух человек. А в 2009 году Intel официально прекратил проект OpenCV. Решения крупных компаний иногда трудно понять, но разработчики проекта объясняют отказ американской корпорации от OpenCV тем, что библиотека на протяжении нескольких лет не приносила компании прямого дохода. Да, технология интересная, да, выглядела перспективно, но в то время было непонятно, как ее можно монетизировать, рассуждает Валерий Черепенников (сегодня вице-президент Российского Исследовательского Института Huawei (Huawei Russian Research Institute, RRI).

После отказа Intel от финансирования OpenCV Вадим Писаревский на протяжении нескольких лет поддерживал библиотеку бесплатно, на общественных началах. И если бы он не делал этого, то проект, скорее всего, умер бы, повторив судьбу многих других открытых библиотек, оставшихся без поддержки. Если кто-то не будет постоянно отслеживать работу с библиотекой, она затухнет. Проектов с открытым кодом тысячи, и большинство из них, к сожалению, погибли, утверждает Валерий Черепенников.

Часть нижегородских программистов разработчиков OpenCV также решили не отказываться от освоенной ими темы. Они уволились из Intel и основали собственный микробизнес: Центр компьютерного зрения Аргус. Довольно быстро этот бизнес из микро- вырос в малый, а затем и в совсем немалый. На его основе была создана компания Itseez, с офисами в Нижнем Новгороде и Сан-Франциско, в которой работали более 100 инженеров. В то время выяснилось, что на основе OpenCV можно разрабатывать продукты, которые хорошо работают не только на процессорах Intel, вспоминает Виктор Ерухимов. Так NVIDIA Corporation стала использовать разработки нижегородцев для развития своих графических ускорителей и автомобильных бортовых компьютеров. А разработчик персональных роботов помощников для дома Willow Garage даже совершил революцию в робототехнике. Я сам писал программу для Willow Garage, благодаря который робот при разряде аккумулятора зрительно находит в комнате розетку, подъезжает к ней, и своей рукой вставляет в нее вилку для подзарядки батареи, вспоминает Виктор Ерухимов.

Компьютерное зрение стало приносить реальные деньги. Когда руководство Intel осознало, что другие компании успешно конкурируют на ими же созданной площадке, корпорация вновь взялась за развитие этой технологии сразу в трех своих подразделениях: в Нижнем Новгороде, в США и Китае. А в 2016 году Intel купил основанную своими бывшими нижегородскими сотрудниками и известную, прежде всего, ключевой ролью в развитии библиотеки OpenCV компанию Itseez.

Как у компьютеров появился искусственный интеллект

Первые статьи о Deep learning (глубокое обучение машин на основе нейронных сетей, которое в популярной литературе называется Искусственный интеллект) были написаны еще в конце 90-х годов XX века. Но тогда казалось, что эта технология бесперспективна. Когда в 2003 году я начинал заниматься машинным обучением, нейронные сети среди его алгоритмов считались абсолютным изгоем. Потому что с ними невозможно работать, их нельзя тренировать, они плохо реагируют на ошибки данных и т.д., рассказывает Виктор Ерухимов. В 2009 году появились первые смартфоны с фотоаппаратом и выходом в Интернет. А вслед за ними возникли миллионы изображений, которыми можно было тренировать компьютеры на больших объемах данных.

Первой запустила крупный проект такой тренировки NVIDIA Corporation, для которой нижегородская Itseez разработала первые алгоритмы глубокого машинного обучения. Тогда еще никто не знал, что из этого получится. А получилась новая техническая революция. Раньше человечество не предполагало, что если нейронную сетку натренировать на миллионе изображений, то она будет обучаться и дальше уже самостоятельно, объясняет Вадим Писаревский, но оказалось, что количество переросло в качество. И человечество смотрит на это, как на новую игрушку, думая, что с ней еще можно сделать.

К примеру, при анализе рентгеновских снимков врачи показывают машине сотни тысяч изображений как здоровых, так и больных человеческих органов. Затем машина с помощью Искусственного интеллекта не только точнее человека определяет наличие известных современной медицине заболеваний, но и указывает на еще неизвестные аномалии. Десять лет назад, мы рассуждали, сможет ли алгоритм детектировать пешехода на маломощном автомобильном компьютере, сейчас этот этап пройден. Intel производит огромное количество датчиков, которые идентифицируют людей, автомобили, препятствия... Сейчас вопрос, скорее, в том, насколько автомобиль без водителя может прогнозировать поведение автомобилей с водителем с точки зрения здравого смысла и целесообразности. К примеру, когда вы выезжаете со второстепенной дороги на главную, а там пробка, вы всегда понимаете, кто вас пропускает. А машина пока нет. Но она скоро поймет. Аналогичная история в других отраслях, рассказывает Виктор Ерухимов.

Несколько лет мы делали проект по распознаванию дорожных знаков для автомобильных навигаторов, вспоминает Валерий Федорович Курякин. Знаки на дорогах часто меняются, и чтобы актуализировать карты, приходилось посылать большое количество автомобилей с цифровыми камерами на борту. Сделанные ими видеозаписи пересылались на обработку малоквалифицированным специалистам в Индии, которые их просматривали и вносили корректировки в карты вручную. При этом допускалось много ошибок. Благодаря разработанной на основе Искусственного интеллекта программе сегодня дорожные знаки считываются видеорегистраторами участников дорожного движения, и карты актуализируются в режиме реального времени без дополнительных затрат разработчиков.

Благодаря Искусственному интеллекту мир меняется. Компьютеры стали обучаться не только на основе изображений, но и звуков, и стали распознавать человеческую речь. Появились машинные переводчики, которые в режиме реального времени переводят речь с одного языка на другой. Во многих отраслях Искусственный интеллект догнал человеческий, а в некоторых уже и обошел его. Мы даже не осознаем быстроты изменений. В начале девяностых годов я сделал систему распознавания лиц для пропускной системы в Сарове с 95% достоверностью. Там был использован технический трюк, с помощью которого всегда удавалось получить изображение в стандартном виде человек не мог наклонить или повернуть голову как-то не так. И все равно это казалось фантастикой, рассказывает Валерий Федорович Курякин. А сегодня машинам удается распознать/идентифицировать человека почти с любого ракурса и при изменениях во внешности.

Как не закончилась эта история

Созданная под руководством нижегородцев библиотека OpenCV приобрела всемирную популярность в IT-среде. К настоящему времени она скачана уже более 20 млн раз. Но скачивают ее не конечные пользователи, а разработчики. Пользователей у нее несколько миллиардов. Ведь ее элементы применялись при разработке Android и iOS, а следовательно, каждый владелец смартфона на планете пользуется трудом нижегородских программистов. Использована OpenCV и при разработке многих других гаджетов и устройств. Но, как объяснил Вадим Писаревский, документировано утверждать этого нельзя: лицензия OpenCV очень либеральная и позволяет копипастить код (переиспользовать в любом виде), а компании-производители, если есть возможность не указывать использование в разработке своего продукта OpenCV, этого и не делают.

Мировое сообщество пока не видит потолка развития технологии компьютерного зрения. А технологии Искусственного интеллекта едва оторвались от пола. Поэтому библиотека OpenCV будет расти еще долго, и как эти технологии изменят жизнь человечества, пока не может предсказать никто.

Подробнее..

Как я обучил модель с нуля для обнаружения и сегментации печатей

06.12.2020 18:10:56 | Автор: admin

Привет хабр! Сегодня хочу поделиться своим кейсом. Распознавание печатей позволило бы автоматизировать множество рутиных задач, упростив работу человека. Для своей задачи я использую модель Mask R-CNN.

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

Что такое сегментация?

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

Классификация: на этом изображении есть кошка.

Обнаружение объектов:на этом изображении в этих местах есть две кошки, одна собака и утка.Мы начинаем учитывать перекрывающиеся объекты.

Сегментация: в этих местах есть 4 всплывающих окон, и это пиксели, принадлежащие каждому из них.

Подготовка набора данных для обучения

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

Вы наверное задаётесь вопросом, разве нам не нужен больше изображений, чем 2 тысяч для обучения модели глубокого обучения? Иногда да, но часто нет. Я полагаюсь на трансферное обучение. Это просто означает, что вместо обучения модели с нуля я начинаю с файла весов, который был обучен на наборе данных COCO (в репозитории Mask R-CNN они предоставляют его).Хотя набор данных COCOнесодержит класса печатей, он содержит много других изображений (~ 120 тысяч), поэтому обученные веса уже изучили множество функций, общих для естественных изображений, что действительно помогает.

Для аннотирования изображений я использовал VIA (VGG Image Annotator). Это отдельный HTML-файл, который вы загружаете и открываете в браузере.Аннотирование первых нескольких изображений было очень медленным, но как только я привык к пользовательскому интерфейсу, я начал аннотировать объект около 20 секунд.

Загрузка набора данных

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

Инструмент VIA сохраняет аннотации в файле JSON, и каждая маска представляет собой набор точек многоугольника.Я не нашел документации по формату, но это довольно легко понять, просмотрев сгенерированный JSON.Я включил в код комментарии, чтобы объяснить, как выполняется синтаксический анализ.

Совет по коду:простой способ написать код для нового набора данных - скопироватьcoco.pyи изменить его в соответствии с вашими потребностями.Что я и сделал.Я сохранил новый файл как seal.py

Мой SealDataset класс выглядит так:

class SealDataset(utils.Dataset):    def load_balloons(self, dataset_dir, subset):        ...    def load_mask(self, image_id):        ...    def image_reference(self, image_id):        ...

load_balloonsсчитывает файл JSON, извлекают аннотации и итеративно вызывает внутренниеadd_classиadd_imageфункцию для создания набора данных.

load_maskгенерирует растровые маски для каждого объекта изображения путем рисования многоугольников.

image_referenceпросто возвращает строку, которая идентифицирует изображение для целей отладки.Здесь он просто возвращает путь к файлу изображения.

Вы могли заметить, что мой класс не содержит функций для загрузки изображений или возврата ограничивающих рамок.Функция по умолчаниюload_imageв базовомDatasetклассе обрабатывает загрузку изображений.И ограничивающие прямоугольники генерируются динамически из масок.

Проверить набор данных

Чтобы убедиться, что мой новый код реализован правильно, я добавил этузаписную книжку Jupyter.Он загружает набор данных, визуализирует маски и ограничивающие рамки, а также визуализирует якоря, чтобы убедиться, что размеры моих якорей подходят для размеров моих объектов.Вот пример того, что вы должны ожидать:

Совет по коду:чтобы создать этот блокнот, я скопировалфайл inspect_data.ipynb, который мы написали для набора данных COCO, и изменил один блок кода вверху, чтобы вместо него загрузить набор данных Seal.

Конфигурации

Конфигурации для этого проекта аналогичны базовой конфигурации, используемой для обучения набора данных COCO, поэтому мне просто нужно было переопределить 3 значения.Как и в случае сDatasetклассом, я наследую от базовогоConfigкласса и добавляю свои переопределения:

class BalloonConfig(Config):    # Give the configuration a recognizable name    NAME = "seal"    # Number of classes (including background)    NUM_CLASSES = 1 + 1  # Background + seal    # Number of training steps per epoch    STEPS_PER_EPOCH = 100

В базовой конфигурации для большей точности используются входные изображения размером 1024x1024 пикселей.Я так и оставил.Мои изображения немного меньше, но модель автоматически меняет их размер.

Обучение

Маска R-CNN - довольно большая модель. Вам нужен современный графический процессор. Я пробовал на Quadro M2000 c 4 ГБ памяти. Обучение заняло около 3-4 часов.

Начните обучение с этой команды, запущенной изseal.Здесь мы указываем, что тренировка должна начинаться с предварительно натренированных весов COCO.Код автоматически загрузит веса из нашего репозитория:

python seal.py train --dataset=/путь/к/датасету --model=coco

И возобновить тренировку, если она остановилась:

python seal.py train --dataset=/путь/к/датасету --model=last

Проверка результатов

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

Предобученные весы для обнаружения и сегментации можете скачать здесь. Чтобы использовать, добавьте проект в папку samples в Mask R-CNN. Если есть необходимость датасета, напишите мне в почту: galym55010@gmail.com

Подробнее..

Как я Лигу Легенд парсил

19.04.2021 00:19:54 | Автор: admin

Привет, Хабр!

Осмелюсь сегодня рассказать, как мне довелось извлекать данные прямо из видеозаписей турнирных игр по игре League of Legends с помощью глубоких нейронных сетей: зачем это нужно, какие архитектуры и приёмы использовались, и с какими сложностями я столкнулся.

Шаг 0: Разбираемся, что к чему

Лига Легенд (League of Legends, LoL) - популярная MOBA игра, с ежемесячной аудиторией более чем в 100 млн игроков всему миру. LoL была разработана компанией Riot Games и выпущена в далёком 2009-м году.

Именно благодаря политике компании Riot Games мне и довелось познакомиться с данной задачей извлечения статистик прямо из турнирных видео. Дело в том, что Риоты с очень большой неохотой отдают турнирные данные, из-за чего проведение анализа сильно осложняется. К примеру китайский чемпионат вообще имеет закрытое апи и получить ну хоть какие-то игровые статистики становится невозможным. Почти...

Скриншот стрима матча континентальной лиги. LCL Летний Сплит 2020.Скриншот стрима матча континентальной лиги. LCL Летний Сплит 2020.

Для начала разберёмся с игровым HUD-ом (Heads-Up Display - визуальный интерфейс игры). На картинке выше цветами выделены основные его части:

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

  2. Боковые панели (фиолетовый цвет) - две панели, содержащие в себе информацию о чемпионах - игровых персонажах обеих команд. Панели показывают количество жизненной силы и маны чемпиона, а также набор его способностей с индикацией использования.

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

  4. Нижняя панель (красный цвет) - наиболее нагруженная панель, показывающая более детальные статистики по каждому чемпиону, такие как: K/D/A (Kills/Deaths/Assists), кол-во убитых миньонов, а также набор предметов в инвентаре чемпиона.

Шаг 1: Парсинг верхней панели

  1. Первично была произведена аннотация данных. С использованием утилиты CVAT были выделены обрамляющими прямоугольниками необходимые поля, а также в ручном режиме выписаны их числовые значения. Хоть и задача распознавания чисел на картинке и не кажется сложной для современных нейронных сетей, в ручном режиме необходимость всё же была, т.к. эти самые современные нейронные сети (Google OCR, Yandex OCR) показали откровенно плохие результаты на некоторых типах полей при тестировании, не говоря уже об открытых движках для распознавания (Tesseract OCR, EasyOCR).

  2. Далее нам необходимо научиться выделять (детектировать) нужные нам поля. Для решения данной задачи было принято использовать segmentation-based подход. Я взял сеть Unet c предобученным efficientnet энкодером и обучил решать задачу instance сегментации для трех классов: башни (жёлтый цвет), золото (зелёный цвет) и кол-во убийств (фиолетовый цвет). Имплементация модели была взята из репозитория segmentation_models.pytorch. Хороший код для обучения таких моделей на Pytorch Lightning вы можете найти в репозитории Владимира Игловикова по сегментированию одежды.

  3. Теперь мы умеем получать маски с классами, а после применения watershed алгоритма даже целые отдельные области нахождения наших полей. Пора научиться их распознавать. Вдохновением для решения данной задачи послужили статьи по распознаванию номеров домов SVHN , а конкретно multihead архитектуры. Идея такова, что если мы имеем последовательности чисел фиксированной небольшой длины (а мы имеем), то мы можем не возиться с RNN или же детектировать отдельные символы. Мы можем взять энкодер и поставить за ним несколько отдельных голов по количеству цифр в числе, с 11-ю (11-ый для случаев, когда цифры нет) выходами в каждой. Каждая голова будет отвечать за предсказание отдельной цифры в числе, но учится все они будут вместе. Схожий подход можно найти имплементированным на Pytorch здесь.

  4. Но не будем забывать, что мы имеем дело с видео. Т.е. нам нужно научить модель работать с последовательностями кадров. Для этого был выбран простейший подход: а давайте просто заменим все 2D свёртки на трёхмерные. Как итог, практически без изменения архитектуры сети, мы добиваемся нужного результата, ведь размерности выходов у этих слоёв одинаковы. Добавление 3D свёрток сильно уменьшает количество выбросов распознавания, ведь сеть учится, что после числа N может идти либо само это чисто, либо же N+1.

  5. Последним шагом остаётся постпроцессинг выходных данных модели: маппинг статистик ко значению таймера, сглаживание выбросов, интерполяция игровых пауз и моментов повторов, в случаях, когда интерфейсы закрыты или не видны совсем.

Multihead OCR архитектураMultihead OCR архитектураНо зачем таймер-то парсить?

Если вы подумали, что в этом нет необходимости и зная одно лишь начало игры таймер восстанавливается однозначно, то я поспешу вас расстроить. Таймер на видео (внимание) не является линейным. Скажу даже больше, если вам интересно - можете посмотреть данный пятисекундный ролик и объяснить, почему семнадцатая секунда длится по времени как две :/

Шаг 2: Парсинг боковых панелей

  1. Размечаем маски необходимых нам полей, куда без этого: заклинания чемпиона (синий цвет), иконка чемпиона (красный цвет), здоровье и мана чемпиона (салатовый и фиолетовый цвета соотвественно). Обучаем Unet из прошлого шага.

  2. Начнём с самого простого: определение кол-ва здоровья и маны чемпиона. Для начала стоит предобработать вырезанные прямоугольные области . Переведём их в цветовую модель HSV (данный переход нужен для более простых манипуляций с цветами) и оставим лишь цвета из нужного диапазона: зелёного для здоровья и синего для маны. Теперь всё просто: итерируемся по матричному представлению одного из каналов изображения (или всех вместе) и ищем наиболее резкий переход между цветом и его подложкой. Разделив x-координату на размер всей области, из относительного перехода получаем значение кол-ва здоровья/маны чемпиона. Данный элементарный метод, как ни странно, показывает отличные результаты, являясь в достаточной мере робастным и точным.

  3. Далее нам нужно распознать классы заклинаний чемпиона и их статус(готовы заклинания к использованию или нет). О статусе заклинания можно узнать по наличию постепенно уходящего затемнения по часовой стрелке , чуть позже мы научимся определять прогресс этого отката. А пока вспомним, что такое multihead архитектура нейронной сети и применим ёё и здесь. Вместо предобученного энкодера возьмём простой набор из нескольких свёрточных и пуллинг слоёв в качестве кодировщика и пару голов с полносвязными слоями, одна из которых будет отвечать за предсказание класса заклинания, а другая за его состояние (активен/нет активен).

  4. К сожалению, свёрточные нейронные сети хуже приспособлены к тому, чтобы решать регрессионные задачи, а задача определения прогресса, когда заклинание будет готово является таковой. В процессе рисёрча я наткнулся на статью, в которой исследователи (да, кто-то публикует статьи про LoL) решили её за меня. Основная идея заключалась в том, чтобы взять неглубокую свёрточную нейросеть и решать ей задачу классификации, разделив набор вариантов отката заклинания на 20-ть интервалов воспринимая их как 20-ть отдельных классов и считая финальное число как взвешенную среднюю активаций выходных нейронов. Я слегка изменил данный подход, сделав не 20-ть, а 100 интервалов, вычисляя финальное число как argmax по выходам сети (в целом, так делать теоретически правильнее).

  5. На сладкое остаётся задача классификации чемпиона по его иконке. Её также можно решать обучив простую нейросеть классификации. На первое время так и сделаем, для того, чтобы набрать данных для обучения другой сети, более архитектурно подходящей для наших баранов. Подход с простой сетью-классификатором ограничен проблемой, которая называется OOD (Out-of-Domain), ведь разработчики достаточно часто добавляют новых чемпионов в игру, и чтобы не переобучать сеть каждый раз я решил обучить другую, основанную на подходе metric learning. Данный подход позволяет обучать сеть находить схожие изображения. Для этого я взял простую сеть-классификатор и вместо оптимизации кросс-энтропии и случайного сэмплирования оптимизировал hinge-loss с hard-negative triplet сэмплированием, чтобы сеть научалась выучивать эмбеддинги чемпионов.

Мяу?

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

Но зачем hinge-loss-то обучать?

В целом вопрос хороший. На самом деле саму задачу можно научиться решать вообще без использования нейросетей, т.к. сама по себе она является задачей pattern recogntion. Но при тестировании методы pattern recogntion оказались очень медленными и не робастными, поэтому от них я отказался.

Шаг 3: Парсинг мини-карты

  1. Ещё одна задача, которую кто-то уже решал до меня. Человек под ником Farza собрал датасет из ста тысяч изображений (огромное спасибо ему за это) и обучил на нём модель детекции Yolo, получив неплохие метрики детекции, но не особо хорошие классификации чемпионов. Давайте применим немного другой подход, используя его датасет.

  2. Yolo - anchor-based архитектура детекции, которая не очень хорошо справляется с коллизиями объектов. Мне же нравится идея применить segmentation-based подход. Для начала сгенерируем маски. Используя имеющуюся разметку будем генерировать два вида масок: маска чемпиона (синий цвет), граница изображения чемпиона (желтый цвет). Это довольно известный приём для instance сегментации, идея которого состоит в предсказании всего двух видов масок, а потом в вычитании границ.

  3. Вновь обучаем Unet.

  4. Далее в ход идёт уже упомянутый watershed алгоритм. Теперь нужно научиться понимать, что за чемпиона вы отсегментировали. Вспоминаем, что у нас уже обучена нейросеть, которая выдаёт нам векторные представления чемпионов. Просто итерируемся по уже найденным героям на боковых панелях и сравниваем используя косинусное расстояние, их изображения с нашим отсегментированным.

  5. Вжух, всё готово. Остаётся только лишь эвристически выровнять координаты чемпионов на мини-карте.

Шаг N: Итоги

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

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

Спасибо за внимание!

Подробнее..

Как мы участвовали в соревнованиях автономных дронов Aerobot 2020 от русской DARPA

24.12.2020 04:15:09 | Автор: admin

Немного соревнований автономных дронов, рассчитанных на взрослых разработчиков и организации, проводятся в России. В статье расскажу о нашем участии в Аэробот-2020. Мы и без соревнований работаем с актуальными технологиями локализации дрона в помещении, планирования движения и картографии (exploration), детекции объектов (perception) и оптимального управления движением. В условиях соревнования были задачи как раз из этой области.

Наша команда состояла из сотрудников Центра компетенций НТИ по направлению Технологии компонентов робототехники и мехатроники на базе Университета Иннополис и студентов университета.

Под катом много увлекательных полетов и падений дронов.

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

Соревновательная робототехника

Если спортивное программирование и индустриальная разработка уже во многих случаях весьма далеки друг от друга, то в робототехнике зачастую крупные соревнования становятся драйверами развития целых направлений. Пожалуй, самый яркий пример DARPA Grand Challenge, которые предопределили бурное развитие автономных автомобилей. Основатель DJI начинал свою работу по БПЛА с соревнований ABU Robocon в составе команды гонконгского университета HKUST.

Среди современных крупных конкурсов в области воздушной робототехники я выделю:

А что в России?

В России робототехника вообще становится популярна, есть кружки, курсы и много STEM-education-движухи. Соревнования дронов в основном проводятся среди школьников и студентов. Мы их и сами проводим. Конкурсов, ориентированных на организации и более сложные задачи, немного (хотя порой и современные школьники делают удивительные вещи). Когда-то компания КРОК проводила интересный конкурс, с тех пор мало подобного было. Пример недавнего конкурса для производителей БПЛА конкурс от ПАО Газпром нефть и AeroNet по перевозке груза, имитирующего пробы нефти. Однако, там задачи больше по аппаратной составляющей БПЛА, чем по планированию движения и обработке сенсорных данных. Можно упомянуть еще Copter Hack, но это конкурс проектов, а не соревнования в которых команды соревнуются в выполнении одних и тех же заданий.

Аэробот-2020

Соревнования Aerobot проводятся в России второй год и как раз ориентированы на развитие решений в области разработки, создания и эксплуатации перспективных робототехнических комплексов (систем) гражданского, военного, специального и двойного назначения воздушного базирования (sic!). Как видите, даже название пропитано духом двойного назначения, что не случайно: соревнования проходят при поддержке ФПИ, призванного быть российским аналогом DARPA. При этом задания вполне интересные, даже если вы пацифист или опасаетесь военной бюрократии, как я.

Заданий в этом году было 3:

  1. Инспекция помещения и поиск объектов в нем с указанием их координат (жизненный аналог поисковые операции в разрушенном здании, например);

  2. Движение по QR-код (получается этакий дрон для складской инвентаризации, вот похожий проект от коллег из Сколково);

  3. Гонки дронов максимально быстро пролететь между воротами и обойти препятствия между ними.

Эти соревнования проводились во второй раз. В этот раз организатором выступила наша с @GigaFlopsis (и еще нескольких теперешних иннополисян) alma mater НИИ робототехники и процессов управления Южного федерального университета (НИИ РиПУ), г. Таганрог.

Команды-участники

Соревнования проводились в два этапа. Для первого, отборочного, был сделан симулятор на базе Gazebo с клёвыми обоями (я просил добавить ковры на стены, но это, видимо, оставили для следующих версий). Второй этап проходил на специальном полигоне.

Из-за коронавируса в этом году команд было меньше, чем в прошлом, но уровень был выше: у всех дроны летали и все делали попытки выполнения заданий. Еще порадовало, что команды были гибридные представители академических институтов и индустрии. Список и краткая инфа о командах:

  • RaccoonLab, Университет Иннополис это мы;

  • ФИЦ ИУ РАН (Москва), в составе разработчики из Fast Sense Studio. Они делают бортовые компьютеры для мобильных роботов и дроны для складской инспекции;

  • ИПУ РАН (Москва) использовали дрон стартапа Airspector по промышленной инспекции при помощи БПЛА;

  • С305, ДВФУ (Владивосток), Центр проектной деятельности студентов;

  • QuadroZ, НИИ РиПУ (Таганрог), команда организатора соревнований;

  • Альтаир, студенты ЮФУ (Таганрог);

  • Команда ВИТ Эра (Анапа).

Состав нашей команды

  • Дмитрий Девитт главный заводила, знает вкус победы предыдущих соревнований Аэробот-2019. Видели Noize MC, обвешанного гитарой и укулеле с сэмплером и клавишами? Вот примерно так работает Дима при тестах: в одной руке ноутбук, в другой пульт ручного управления для подстраховки;

  • Дмитрий Пономарев программист, реализовал фьюзинг нескольких реалсенсов для локализации, генерировал миры в Gazebo;

  • Илья Севостьянов студент Университета Иннополис, работал над детекцией полосы и посадочной площадки;

  • Юрий Сухоруков студент, много занимался сборкой кошерного дрона, 3D-печатью, сделал ворота и параллельно порадовал сайд-проектом по детекции масок с дрона;

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

Следующие сотрудники большой команды нашей лаборатории не были в официальном составе соревнований (по регламенту не больше 5 человек с российским гражданством), однако их наработки были очень полезны:

  • Виктор Массагуе алгоритм инспекции;

  • Гисара Пратхап сегментация на облаке точек, создание миров в Gazebo;

  • Никита Ермоленко алгоритмы CV для детекции ворот.

Отборочный этап

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

Все три задания от третьего к первому на симуляторе (этап 1) в таймлапсе:

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

Разбор решений

Approach

Общий подход у нас и у других команд состоял в классической структуре системы управления с разделением на функциональные блоки локализации и картографии, управления, планирования движения, восприятия окружающей среды, планирования миссии. End-2-end и подобное никто не делал, хотя элементы такого подхода в третьем задании напрашиваются.

Железо

Все летали на опенсорс автопилоте PX4 (слава ему).

По бортовым компьютерам: Nvidia Jetson (Nano/NX/Xavier) или Raspberry Pi или свой компьютер у FastSense.

Из сенсоров для восприятия мира, как правило, есть камеры глубины Realsense D435, многие летают также с использованием RPLIDAR плюс обычные камеры (вебки) для детекции объектов.

Локализация

Здесь две доминирующие идеи: 2D SLAM на Google Cartographer (мы использовали только на этапе симуляциий) c RPLIDAR и tracking камеры Realsense T265.

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

Комплексирование данных локализации Комплексирование данных локализации

Картография

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

Мы использовали свою наработку в этой области, основанную на модифицированном алгоритме NBV (next-best-view), а также библиотеке voxblox для представления карты.

Алгоритм строит 3d карту (цветной mesh) в реальном времени на борту дрона, зная только предполагаемые размеры области инспекции. При планировании также выполняется обход препятствий, пролет в окно для инспекции следующего помещения также планирует этот алгоритм.

Как это выглядит в реальных тестах:

Почитать подробнее можно, например, здесь.

Траекторное управление и обход препятствий

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

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

На основе облака точек формируется новая карта с дополнительными границами вокруг препятствий (т.н. inflation). По этой карте находится оптимальный маршрут, используя несколько алгоритмов (для быстрого поиска сильно модифицированный elastic band planner, в сложных случаях дополнительно используется A*).

На выходе мы получаем набор координат, через которые нужно построить траекторию дрона. Для генерации физичной траектории используются сплайны. Существует много хороших открытых решений от крутых лабораторий, например mav_trajectory_generation от Autonomous Systems Lab (ETH, Цюрих), B-traj от HKUST Aerial Robotics Group (Гонконг) и множество реализаций minimum snap и jerk генераторов траекторий (btw, jerk и snap это названия соответственно третьей и четвертой производных перемещения по времени, по-русски толчок и рывок). Мы интегрировали решение от HKUST Aerial Robotics Group. Результатом работы данного модуля является гладкое движение дрона с отклонением от препятствий.

Один из примеров полёта у нас в подвале:

Еще один пример работы планировщика уже в симуляторе Gazebo:

Восприятие

В задачи восприятия, которых мы касаемся здесь, входят детекция (и локализация)

  • кубиков с логотипом (задача 1);

  • посадочной площадки (все задания);

  • QR-кодов (задание 2);

  • препятствий и ворот (задание 3).

Для детекцией кубиков хватило базовых методов OpenCV: SIFT + Homography + PnP. Т.к. мы знали паттерн на сторонах куба, достаточно было определить точное положение этого паттерна относительно камеры, а дальше можно получить позицию относительно любой системы координат. Для этого был написан простой пакет под ROS на python.

Для QR кодов помимо координат необходимо было произвести расшифровку кода. На python с этим отлично справляется библиотека zbar.

Посадка по маркеруПосадка по маркеруQR-кодыQR-коды

Забавно было с детекцией ворот и препятствий. На этапе симулятора мы заюзали кластеризацию облаков точек на базе https://github.com/PRBonn/depth_clustering. Ворота сегментировались как один кластер и мы задавали траекторию полета через его центр. Если вместо ворот были препятствия, их облетал алгоритм траекторного управления с обходом препятствий (см. выше).

На этапе полигона мы, конечно, от этого ушли :)

Ad hoc vs унифицированные подходы

Общефилософское отступление в целом о соревнованиях. С одной стороны, соревнования направлены на стимулирование развитие технологий для практического применения. С другой, искусственность соревнований обуславливает то, что более простые решения, работающие для конкретных условий, выигрывают в сравнении с более общими подходами, которые к тому же требуют более длительной разработки. Вообще, это называется костыли, но, говорят, есть более научный термин: Ad hoc.

В нашем случае, у нас были в заготовке еще до соревнований некоторые универсальные средства: планировщики, сегментация облаков, конструкция дрона, программная инфраструктура. С другой стороны, воля к победе требует в процессе отладке во время соревнований применять и некоторые оптимизации и упрощения.

Хорошим примером здесь будет наш планировщик для первого задания. Можно делать универсальный алгоритм с NBV, оптимизацией движения с целью максимального захвата камерой неисследованного объема, а можно, зная размеры комнат и что они имеют простую форму, задать несколько рандомных точек в каждой. Также можно задавать направление движения, зная что карта устроена для движения по или против часовой стрелки. Еще забавный момент если предположить, что организаторы расставили объекты поиска в точки с целыми координатами (или с координатами кратными 0.5), что свойственно человеку, можно довести точность их определения дроном до абсолютной простым округлением :)

Отступление 2

Читайте регламент заранее и рассчитывайте на худший сценарий. Если заметили неточность, предлагайте уточнение.

Отступление 3

Все кто делают дронов, рассчитывайте на тройное резервирование, бюджеты умножьте на 3 и не соглашайтесь на меньшее :) Дроны падают.

Обещанное видео падений:

И еще. Делайте бэкапы. У нас, например, накрылась флешка со всей системой на бортовом компьютере и это стоило нам дня соревнований и ночи восстановления.

Как вам такое перед зачётной попыткой?Как вам такое перед зачётной попыткой?

Полигон и летающая звезда Давида

Технополис Эра

Облако теговОблако тегов

Эра это военный Технополис. Круто, что он у моря. Расположение Технополиса на морском побережье создает комфортные условия для работы и жизни. сказано на сайте Технополиса :)

И да, у них свой пирс с вертолётной площадкой.

Источник: era-tehnopolis.ruИсточник: era-tehnopolis.ru

Особенностью работы в военном Технополисе является отсутствие Интернета, в том числе мобильного из-за экранирования. Поступали так:

А это мобильная базовая станция

Полигон

Полигон в его вариантах для разных заданий выглядел так:

Неофициальный партнер соревнований Леруа Мерлен :)Неофициальный партнер соревнований Леруа Мерлен :)

Железо

Дроны других участников

Дрон команды QuadroZ (НИИ РиПУ, Таганрог) Дрон команды QuadroZ (НИИ РиПУ, Таганрог) Дрон ИПУ РАН (Москва) от AirspectorДрон ИПУ РАН (Москва) от AirspectorБПЛА ВИТ Эра (Геоскан Пионер)БПЛА ВИТ Эра (Геоскан Пионер)Дрон и команда ИЦ ИУ РАН и Fast Sense Studio (Москва) с нашим шпиономДрон и команда ИЦ ИУ РАН и Fast Sense Studio (Москва) с нашим шпионом

Технология тряпочки

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

Итак, уважаемые знатоки, внимание, вопрос: для чего нужна тряпочка на взлетно-посадочной площадке?

ЗагадкаЗагадкаОтвет в спойлере

Всё просто на тряпочке много фич по сравнению с гладким полом, и визуальной одометрии есть за что зацепиться в момент взлёта, когда камера только поверхность под дроном и видит.

Наш дрон

Про звезду Давида мы не специально (кажется). Просто такая форма получилась достаточно жесткой.

Вообще, конструкция сделана модульная, с расчетом на ремонтопригодность, изготовление на базе стандартных карбоновых трубой и 3D-печатных деталей.

Готовим запчасти (3D-печать)Готовим запчасти (3D-печать)

Последних мы взяли с собой мешочек про запас. Питстоп по замене сломанного крепления двигателя не более 60 секунд:

Пит-стопПит-стоп

Полёты

Репортаж о соревнованиях и дронах, у которых есть имена (источник):

Победила дружба

По результатам соревнований судейской коллегией было принято решение не выявлять победителей, т.к. ни одной командой не были пройдены задания второго тура целиком. По итогу: 1) потрачено много запчастей и нервов, 2) было весело, собрались интересные ребята, 3) победила дружба. Следующие соревнование организаторы планируют проводить с теми же заданиями. А мы занимаемся прикладной разработкой и поглядываем на MBZIRC. Привет участникам и организаторам!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru