- PVSM.RU - https://www.pvsm.ru -
Наткнулся на рекламу сайта продажи авто в телеграме. Зашёл на сайт из любопытства, потыкал объявления и наткнулся на фичу — «3D-панорама». Зажимаешь, ведёшь пальцем, машина поворачивается перед тобой.
Покрутил её со всех сторон и поймал мысль: под капотом это карусель из фотографий, снятых по кругу. Машину видно со всех боков, но плоско. Угол, которого нет в кадре, не подсмотришь. Своей траекторией не облетишь. Это слайд-шоу, прикинувшееся объёмом.
А под такую съёмку — объект, отснятый по кольцу — есть инструмент, который вытащит из неё честную 3D-модель. Gaussian Splatting. Скармливаешь кадры с орбиты, он восстанавливает геометрию и расцветку сцены облаком гауссиан, и дальше камера летает где угодно: ближе, дальше, под любым углом, по своей дуге. Я взял одну машину с и прогнал через 3DGS — проверить, насколько «лучше» выйдет их плоской панорамы.
Хочу сказать сразу, что в этой статье не будет точных описаний всех алгоритмов, статья будет сугубо демонстративная. В будущем надеюсь раскрыть все детали подробнее.
Разница в том, что первое — набор картинок, а второе — геометрия, которую можно показать с ракурса, которого в съёмке не было.
Под «3D-панорамой» лежит набор обычных jpg. Для одной машины — ~100 кадров, снятых по кругу, шириной 1200 пикселей. Драг пальцем перематывает их, как флипбук. Глубины нет, модели нет — листалка кадров с фиксированной орбиты.
Как я скачал кадры — не тема этой статьи. Важно одно: панорама лежит как папка картинок, а номер машины сервис закрывает своим вотермарком, так что с приватностью разбираться не пришлось.
Сама съёмка — подарок. Камера обошла машину по кольцу, кадры плотные и равномерные. Под такой сценарий и заточены фотограмметрия с Gaussian Splatting: дай им объект, отснятый с разных сторон, и они соберут сцену в объёме. Обычно ради этих кадров ходишь вокруг предмета сам с телефоном, а тут уже походили за меня.
Чего в панораме нет — координат камеры. Чтобы собрать 3D, мне нужно знать, откуда снят каждый кадр: положение и поворот камеры. В съёмке этого нет, есть только картинки в порядке обхода. Восстановление поз — первая задача, которую пришлось решить.
Сцену в 3DGS [1] описывает облако крошечных трёхмерных «клякс» — гауссианов. Каждый это мягкий эллипсоид: у него есть положение в пространстве, размер и ориентация (насколько и куда вытянут), цвет и прозрачность. Миллион таких клякс вместе приближает поверхности и цвета сцены, как мазки полупрозрачной краски, развешанные в воздухе.
Рендер работает так: каждый эллипсоид проецируется на экран (это и есть splat — размазать), кляксы сортируются по глубине и смешиваются от ближних к дальним. Вся операция дифференцируема. Значит, можно замерить, насколько рендер расходится с реальной фотографией, и подтолкнуть параметры каждого гауссиана в сторону, которая уменьшает ошибку.
Обучение крутит этот цикл. Стартуешь с грубого облака точек. Из каждой известной позы камеры рендеришь текущие гауссианы, сравниваешь с настоящим кадром из той же позы, прогоняешь ошибку по пикселям обратно в положения, цвета и формы клякс. По ходу дела алгоритм клонирует гауссианы там, где не хватает детали, и выбрасывает бесполезные. Через несколько тысяч шагов облако сходится с фотографиями со всех тренировочных ракурсов — и держится на углах между ними.
Почему не меш и не NeRF [2]. Восстановление меша спотыкается на тонких и блестящих вещах: автомобильная краска и стекло превращают его в кашу. NeRF даёт отличное качество, но рендерит медленно — на каждый луч запрос к нейросети. 3DGS рисует в реальном времени (растеризация клякс, без сети на пиксель) и обучается за минуты, оставаясь фотореалистичным. Для «крутить машину в браузере плавно и с любого угла» это попадание в точку.
Под нашу задачу вход почти идеальный: машина, отснятая по полному кольцу, — плотное равномерное покрытие одного объекта. Дай 3DGS такие кадры с позами и стартовое облако точек, и он соберёт модель, вокруг которой летаешь свободно. Ровно то, чего не умеет плоская панорама.
3DGS требует от меня двух вещей: каждый кадр с его позой камеры и то самое начальное облако. Панорама не даёт ни первого, ни второго напрямую. Следующая часть — про то, как я добыл и позы, и облако из голых кадров.
Чтобы посмотреть примеры обученных 3DGS моделей, можете зайти на сайт https://superspl.at/ [3] - pinterest в мире 3DGS
У меня 118 кадров одной машины, разложенных по кругу, и порядок обхода. Где стояла камера на каждом кадре — неизвестно. Без этого 3DGS не стартует: ему нужно знать точку и угол съёмки каждого кадра, иначе он не сошьёт пиксели в общую геометрию.
Классический ответ — COLMAP [4]. Это Structure-from-Motion: программа находит общие точки между кадрами, по ним считает позы камер и заодно отдаёт разреженное облако. Метод рабочий, но медленный, и облако выходит дырявым — несколько тысяч точек на всю сцену. COLMAP основан на классических методах оптимизации поз камеры, которые потихоньку уходят на второй план.
С другой стороны, сейчас идет бурное развитие feed-forward методов для оценки карт глубин и поз (VGGT [5], DepthAnything3 [6], VGGT-Ω [7]). Кадры отдал в VGGT-Ω — нейросеть, которая за один прогон по набору картинок выдаёт позы камер и карту глубины для каждого кадра. 118 кадров она прожевала за шесть секунд на одной видеокарте. На выходе — поза каждой камеры и depth, плотный, на весь кадр.
Из этих глубин я собрал стартовое облако. Пиксель, его глубина и параметры камеры разворачиваются обратно в 3D-точку. Повторяешь для каждого кадра, выкидываешь точки, в которых модель не уверена, складываешь всё вместе. Вышло около 600 тысяч цветных точек — на два порядка гуще, чем дал бы COLMAP. С этой болванки 3DGS и начинает оптимизацию.
COLMAP в пайплайне остался, но в скромной роли — выпрямить кадры под единую модель камеры.
Когда всё сошлось, я проверил геометрию на вменяемость — куда встали камеры относительно облака. Они легли кольцом вокруг точечной массы машины. Можно учить гауссианы.
Дальше пошла бы рутина 3DGS, если бы не одна засада. Кормишь алгоритму позы, кадры и стартовое облако — он крутит цикл оптимизации: рендер из каждой позы, сравнение с реальным кадром, поправка гауссианов. Тридцать тысяч шагов, семь минут на одной видеокарте, на выходе — обученное облако из сотен тысяч клякс.
Позы от VGGT хорошие, но не идеальные, и сама панорама — не математически ровный круг: машину обходили живой рукой, камера гуляла. Поэтому я использовал режим, где оптимизатор подкручивает не только гауссианы, но и сами позы камер по ходу обучения. Чуть точнее позы — заметно резче картинка.
Депсы от VGGT я пустил в дело дважды. Первый раз — на старте: из них собрано облако-болванка. Второй — внутри обучения, дополнительным сигналом, и вот зачем.
Одни фотографии говорят, как сцена выглядит, но не задают её настоящую форму. Под один и тот же набор кадров подходит масса разных 3D-сцен — особенно там, где пиксели бедны на детали: гладкая краска, стекло, блики. На таких местах фотометрический лосс почти ничего не подсказывает оптимизатору, и гауссианы расползаются не туда — висящие в воздухе «флоатеры», размазанная геометрия. Карта глубины фиксирует, на каком расстоянии лежит поверхность в каждом кадре. Оптимизатор подгоняет не только цвет, но и глубину, и форма выходит чище, без мусора между машиной и камерой.
Семь минут спустя у меня лежал обученный набор гауссианов — та самая 3D-модель машины, которую можно рендерить с любого угла.
Модель готова, остаётся показать её — снять плавный круговой облёт. У меня 118 обученных ракурсов, но видео из 118 кадров дёргается и ничем по смыслу не отличается от исходного ряда изображений. Между соседними позами я добавил промежуточные: повороты — сферическим SLERP, сдвиги — линейно, по восемь вставок на стык. Из 118 поз вышло 937 кадров на 30 fps, полминуты гладкого хода.
Первый прогон поехал, но машина то наезжает, то отъезжает. Панораму снимали с руки, и дистанция до машины по ходу обхода плавала, а интерполяция честно унаследовала эти качели. Я нашёл центр орбиты — точку, куда смотрят все камеры, как пересечение их лучей взгляда, — и насадил каждый кадр на сферу одного радиуса вокруг неё, с прицелом в центр. Машина встала на одном размере.
В сцене видны флоатеры (гауссианы в воздухе, не соответствующие геометрии) на одном сегменте - это связано с тем, что карты глубин на дальних объектах были неверны, но моя цель была получить 3д модель машины, и я кропал глубины дальних объектов. При более тщательной обработке глубин, можно снизить количество флоатеров.
Из плоской листалки в браузере получилась 3D-модель, вокруг которой летаешь свободно — ближе, дальше, под углом, которого в исходной съёмке не было. Та самая разница между набором картинок и геометрией, с которой всё начиналось.
Честно про границы. Вместе с машиной восстановился и фон — асфальт, забор, чужие машины на парковке. восстановились неидеально из-за неточностей в глубине для дальних объектов, но ведь нас больше интересует сам автомобиль. Под чистую модель «только автомобиль» сюда просится сегментация: вырезать объект маской и учить гауссианы лишь на нём. Я этого пока не делал, оставил кадр как есть.
Весь путь от папки с кадрами до облёта складывается в короткую цепочку: достал кадры → VGGT дал позы и глубину → из глубины собрал стартовое облако → 3DGS с pose-opt и опорой по глубине обучил гауссианы → рендер свёл их в гладкое кольцо. Одна машина проходит конвейер за минуты на одной видеокарте.
И вот тут самое интересное на будущее. Конвейер не привязан к одной машине — он одинаково жуёт любую панораму.
Что докрутить дальше: дополнительные трики для улучшения качества гауссиан - работа с отражениями, блюром, глубинами; использование семантической маски для выделения машины и еще много всего.
Как я писал ранее, это мой первый пост. Однако, не последний. Есть множество тем, которые я описал вскользь, и о которых, надеюсь смогу рассказать в будущем.
Спасибо за внимание!
Автор: bulatko
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/3d/453636
Ссылки в тексте:
[1] 3DGS: https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
[2] NeRF: https://www.ecva.net/papers/eccv_2020/papers_ECCV/papers/123460392.pdf
[3] https://superspl.at/: https://superspl.at/
[4] COLMAP: https://github.com/colmap/colmap
[5] VGGT: https://vgg-t.github.io/
[6] DepthAnything3: https://depth-anything-3.github.io/
[7] VGGT-Ω: https://vggt-omega.github.io/
[8] Источник: https://habr.com/ru/articles/1049458/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1049458
Нажмите здесь для печати.