Назад
205

Blender для генерации данных

205

Введение

Успешное решение любой задачи Deep Learning состоит из двух аспектов:

  1. Умение инженера позаимствовать подходящую архитектуру сети из Github;
  2. Большое количество обучающих данных для этой задачи.

Если кто-то собирается оплатить решение задачи — это значит, что один из пунктов не является тривиальным. Сегодня мы разберём второй случай — вопрос нехватки данных. В этой статье мы обсудим, как с помощью Blender получить синтетические данные практически к любой 2D или 3D-задаче компьютерного зрения.

Умение легко генерировать синтетику под возникшую задачу хоть и не закроет потребность в данных на 100%, но сделает процесс прототипирования и отладки решения более приятным, а также позволит улучшить итоговые метрики. Давайте же познакомимся с Blender’ом и его полезным для программистов функционалом 😊

Программируем в Blender и BPY

Для начала нам нужно скачать Blender. Сделать это можно при помощи инструкции на официальном сайте: https://www.blender.org/download/.

После успешной установки и запуска программы мы увидим привычный интерфейс 3D-редактора:

Рисунок 1. Интерфейс Blender’а с его элементами и возможностями (они выделены и подписаны зелёным цветом)

На старте мы находимся во вкладке Layout — она нужна для создания и комбинирования различных объектов в сцене. Но сегодня нам будет интересна другая вкладка — Scripting. Она позволяет программировать поведение редактора и автоматизировать работу в нём. Давайте перейдём на неё и узнаем, какие возможности автоматизации есть в Blender.

Рисунок 2. Вкладка Scripting: центральное окно помогает создавать и редактировать скрипт, окно слева позволяет обращаться к интерпретатору Python в интерактивном режиме

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

Всё, что нам осталось сделать — разобраться с библиотекой BPY. Тогда мы сможем управлять 3D-сценой в Blender с помощью Python.

Итак, библиотека BPY содержит Python API для работы с Blender. Для простого примера взаимодействия Python и Blender давайте обратимся к интерактивной консоли и наберём в ней:

bpy.data.objects.keys()

Так мы напечатаем имена всех объектов в сцене, а именно:

📌[‘Camera’, ‘Cube’, ‘Light’]

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

Итак, с интерактивным исполнением Python разобрались. Перейдём к работе со скриптами — для возможности их сохранять, редактировать и загружать есть центральное окно. Там всё выполняется в специальном блокноте. Обратите внимание: в случае с текстовыми скриптами вам необходимо выполнить импорт библиотеки BPY, а в остальном процесс неcильно отличается от консоли:

import bpy

На данном этапе может показаться, что для работы с Python потребуется выучить внушительное API Blender со всеми тонкостями организации данных и надолго пропасть в документации. Однако разработчики Blender (спасибо им 🙂) пошли на встречу и сделали «документацию» интерактивной, встроенной в интерфейс редактора. Чтобы её активировать, нужно попасть в следующие меню:

Edit→Preferences…

Затем в появившемся окне активировать «Python Tooltips»:

Рисунок 3. Активация интерактивной подсказки через Python в интерфейсе Blender

Теперь при использовании редактора вы сможете наводить мышкой на любой объект и получать всплывающую подсказку о том, как произвести аналогичное действие с помощью Python. Давайте потренируемся на примере сдвига куба. Для этого в (правом нижнем) окне свойств объекта наведём мышкой на «Location» куба и посмотрим, как возможно изменить его положение по выбранной оси.

Рисунок 4. Пример всплывающей подсказки Python API в Blender

Отлично! Теперь при использовании этого кода на вкладке Scripting в текстовом скрипте или консоле мы увидим смещение кубика в сцене:

bpy.data.objects["Cube"].location[2] += 4

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

Используем готовые модели

Давайте отвлечёмся от программирования на Python и обратим внимание на одну немаловажную особенность — никто из нас не художник. Действительно, чтобы манипулировать какими-либо объектами в 3D, их сначала нужно где-то найти. Наиболее гибкий способ — нарисовать самостоятельно. На нашем курсе 3D Computer Vision я подробно рассказываю, как можно моделировать объекты в Blender, а в рамках этой статьи (с особенностями её формата и небольшого размера) я покажу, где и как лучше найти подходящие данные без необходимости их моделирования.

Секрет успеха здесь следующий — Blender умеет импортировать почти все возможные форматы трёхмерных данных. Следовательно, нам нужно только найти источник открытых и бесплатных 3D-моделей. Хорошо, что в интернете их достаточно много. Среди художников и разработчиков игр большой популярностью пользуются сайты для обмена или продажи трёхмерных моделей. Вот примеры некоторых из них:

Чтобы удостовериться в том, что у нас откроется скачанная модель, рекомендуем скачивать их формате *.obj. Он откроется, где угодно (в том числе и в Blender). Для импорта в Blender воспользуйтесь опцией меню File→Import (для *.obj файлов выберете Wavefront). Скачанных моделей в сцену можно импортировать несколько штук — все они будут появляться в окне объектов справа и в сцене соответственно.

Рисунок 5. Самолёт вылетает из сайта с готовыми 3D-моделями
Рисунок 6. И приземляется прямиком в Blender, где мы можем управлять им с помощью Python

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

Генерируем разметку

Итак, мы научились работать с Python, познакомились с библиотекой BPY и узнали, где можно найти готовые 3D-модели. Теперь давайте поговорим о разметке данных и о том, как превратить 3D-сцену в 2D-изображения для обучения нейронных сетей.

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

bpy.context.scene.render.filepath = "/home/david/tmp.png"
bpy.ops.render.render(write_still=True)

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

Рисунок 7. Результат рендеринга

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

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

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

Сегментация

Чтобы сделать разметку сегментации, нам нужно выполнить два трюка:

  1. Рендеринг изображения с прозрачностью (альфа-канал станет нашей сегментационной маской).
  2. Умение включать и выключать видимость объектов (в рендеринг разметки не попадут лишние элементы сцены).

Для исполнения первого трюка используем код на Python:

bpy.context.scene.render.film_transparent = True
bpy.context.scene.render.image_settings.color_mode = 'RGBA'

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

Для выполнения второго трюка мы переключаем видимость объектов:

bpy.data.objects["Cube"].hide_render = False

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

  • рендеринг сцены в изображение;
  • рендеринг разметки в другое изображение;
  • преобразование полученных масок в прямоугольники для детекции с помощью OpenCV (опционально).

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

Применяем аугментации

В завершение давайте обсудим наиболее популярные приёмы аугментации для генерации 2D-данных в Blender. Никто, конечно, не отменяет использование Albumentations и прочих модификаций в домене изображений. Мы рассмотрим возможности разнообразия данных на этапе рендеринга.

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

Перед манипуляциями с камерой установим её в некоторое начальное положение:

cam = bpy.data.objects['Camera']
cam.rotation_euler = (math.pi/2, 0, 0)

Чаще всего нужно вращать камеру вокруг сцены, чтобы рендерить объекты с разных ракурсов и максимально извлекать пользу из того, что они трёхмерные. Производить манипуляции над положением и ориентацией камеры (или любого другого объекта сцены) можно с помощью её свойств location и rotation_euler. В итоге, чтобы вращать камеру вокруг заданной точки [t_loc_x, t_loc_y], мы будем сдвигать её в координаты, вычисленные по формуле окружности, и постепенно поворачивать:

target_angle = 360
num_steps = 5
r = 10
t_loc_x = 0
t_loc_y = 0

for x in range(num_steps):
    alpha = (x)*target_angle/num_steps
    cam.rotation_euler[2] = math.pi/2 + alpha
    cam.location.x = t_loc_x + math.cos(alpha)*r
    cam.location.y = t_loc_y + math.sin(alpha)*r

Если мы вызовем функцию render из главы «Генерируем разметку» на каждой итерации цикла — мы сохраним рендеры сцены с различных углов обзора. Логику полёта камеры можно усложнить в зависимости от задачи, добавить новые плоскости вращения или шум в траекторию движения, чтобы снизить переобучение на сгенерированных таким образом данных.

Другие практики аугментации сцены могут включать, например, аугментацию освещения по схожей схеме. Мы можем получить доступ к объекту bpy.data.objects['Light'] и разместить его в различных участках сцены. Также есть возможность добавить несколько источников освещения в сцену для большей гибкости в аугментациях.

Ещё можно поменять взаимное положение объектов в сцене, слегка модифицировать их геометрию, текстуры и свойства материалов. Про текстуры и материалы мы в этой статье не говорили, но подробно разберём, как можно их применять и настраивать в соответствующей лекции на курсе 3D Computer Vision 😊

Заключение

В статье мы рассмотрели основы работы с 3D-редактором Blender и узнали, как стать Senior Python-программистом в Blender с помощью активации лишь одного переключателя в меню. Теперь, когда вам понадобится создать свои 3D или 2D синтетические данные, вы сможете обратиться к следующему рецепту:

  1. Включить Blender;
  2. Найти в интернете подходящую модель;
  3. Поправить её до нужного вида;
  4. Написать несложный Python-скрипт для рендеринга изображений и разметки;
  5. Насладиться приростом в точности сети.

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

💡Если хотите научиться решать задачи 3D CV, то приходите к нам на курс! Вы научитесь работать с лидарными данными, создавать цифровые аватары людей, 3D-модели по 2D-фото и поймёте математику, стоящую за алгоритмами.

Последний поток в формате онлайн-лекций в Zoom стартует 8 октября.

Запишитесь на программу, чтобы успеть присоединиться на самых выгодных условиях!

Телеграм-канал

DeepSchool

Короткие посты по теории ML/DL, полезные
библиотеки и фреймворки, вопросы с собеседований
и советы, которые помогут в работе

Открыть Телеграм

Увидели ошибку?

Напишите нам в Telegram!