Облако точек — это неструктурированный набор векторов вида . Опционально каждой точке может также соответствовать вектор , который содержит дополнительные признаки, например, RGB цвет точки, интенсивность полученного сенсором сигнала и т.д.
Облака точек возникают во многих задачах из реального трёхмерного мира и позволяют нам понять, как он выглядит. Например, беспилотный автомобиль воспринимает окружающие его объекты в виде облака точек и строит между ними безопасный маршрут. Современные смартфоны с помощью сенсоров, которые возвращают облака точек, способны восстановить точную геометрию окружающего пространства.
Интуитивно кажется, что облака точек похожи на трёхмерные изображения, но есть важное отличие: облако точек — это неупорядоченный, неструктурированный массив, и чтобы извлечь из него пространственную структуру, нужно ещё постараться.
Поэтому, мы не сможем напрямую использовать для работы с облаками точек архитектуры, созданные для изображений. В этом параграфе мы расскажем о том, как извлекать признаки из облаков точек и какие end-to-end архитектуры существуют для популярных задач: классификации и сегментации.
Но прежде, чем мы переедем к описанию конкретных архитектур, давайте разберёмся, откуда берутся облака точек.
Сенсоры для получения облаков точек
LiDAR
Один из самых популярных сенсоров для получения облаков точек — это LiDAR. Принцип его действия состоит в следующем: посылается луч, он отражается от поверхности и возвращается на детектор. Зная скорость света и время, за которое луч вернулся, мы можем посчитать расстояние до объекта.
Благодаря используемой технологии у каждой точки помимо позиции будет известна ещё интенсивность. Она показывает, насколько сильным был отклик от поверхности. Например, от стекол или снега мы можем ожидать околонулевой мощности отражения, в то время как от дорожных знаков отражение будет очень сильным. Таким образом, интенсивность может оказаться важным признаком при построении предсказательной модели над облаками.
Большим преимуществом лидара является способность получать отклик на расстоянии 200 метров и больше при сохранении высокой точности.
Камера RGB-D
RGB-D камера — это камера, которая способна возвращать глубину каждого пикселя помимо RGB значения. Зная глубину и математическую модель камеры, мы можем восстановить облако точек. Большим преимуществом такого сенсора является наличие цвета для каждой точки. Это может быть полезно при построении моделей сегментации, детекции и так далее.
RGB-D камеры, как правило, обладают меньшей точностью, чем LiDAR. Также такие сенсоры не позволяют оценивать глубину дальше 10-15 метров.
RGB-камеры
Облако точек можно получить и с помощью обычной камеры, если воспользоваться алгоритмами компьютерного зрения для вычисления глубины. Существуют алгоритмы оценки глубины по одному кадру (monocular depth estimation), оценки глубины по нескольким кадрам (multi view stereo), восстановление облака точек окружающих объектов с помощью движения камеры (Structure From Motion). В зависимости от алгоритма, камеры и среды качество итоговых облаков может различаться. В каждом отдельном практическом приложении нужно смотреть, насколько тот или иной сенсор (или метод получения облака точек) подходит для задачи.
Архитектуры для обработки облаков точек
В этой части мы рассмотрим различные подходы к построению нейросетей для работы с облаками точек. Мы обсудим детали архитектур, их минусы и плюсы, а также варианты использования этих архитектур в приложениях.
PointNet
Давайте подумаем как, именно мы можем обрабатывать облака точек.
Во-первых мы можем применять некоторую функцию, например, MLP (полносвязную нейросеть) к вектору признаков каждой точки нашего облака.
Проблема с таким подходом в том, что мы работаем не с облаком, а с отдельными точками. У такой архитектуры не будет пространственного контекста. В терминах CNN, receptive field каждой точки будет равен 1 — это эквивалентно свёрточной архитектуре, где все ядра размера 1x1.
Значит нам нужен некий механизм, с помощью которого точки смогут обмениваться информацией друг с другом. Таких механизмов можно придумать много: это и построение графа над облаком, и пересылка сообщений между вершинами, это может быть механизм внимания.
Авторы архитектуры PointNet предложили максимально простой механизм: имея для каждой точки некоторый вектор признаков , давайте применим к этим векторам некоторую глобальную агрегацию AGGR, например, GlobalAveragePooling или GlobalMaxPooling:
Таким образом из набора векторов для точек мы получим общий вектор признаков, описывающий всё облако.
Полученный вектор описывает облако в целом, но точки при таком подходе по-прежнему не обмениваются информацией: ни одна из них не «знает», что происходит вокруг.
Чтобы исправить недостатки двух описанных выше подходов, объединим их, рассмотрев следующий базовый «слой» преобразования облака:
- Агрегируем векторы признаков точек , получаем общий вектор признаков для облака в целом;
- Для каждой точки конкатенируем её вектор признаков с вектором признаков облака и строим новый вектор признаков ;
- Применим MLP к вектору признаков каждой точки: .
Такие слои можно применять последовательно, формируя сколь угодно глубокую архитектуру.
Теперь, если мы хотим решить задачу классификации над облаком, мы можем взять очередной вектор , полученный после агрегации всех векторов признаков точек, применить к нему MLP, получить логиты и обучать полученную сеть на cross-entropy loss.
Если же мы хотим решить задачу сегментации (то есть классификации для каждой точки), мы можем точно после очередного слоя применить MLP с необходимыми размерами к каждому вектору и получить логиты.
Авторы архитектуры PointNet предложили ещё одно небольшое усложнение описанной выше архитектуры. Давайте внимательно посмотрим на схему из статьи:
Синий прямоугольник наверху соответствует одному описанному выше «слою», но вместо одного MLP здесь последовательность двух MLP, между которыми вставлена операция feature transform. Эта операция состоит в следующем:
- с помощью дополнительной сети по облаку в целом строим матрицу;
- эта матрица умножается на вектор признаков каждой из точек.
Тем самым точки дополнительно обмениваются информацией. Во многих последующих статьях эту часть не включают, и практика показывает, что для PointNet это не ключевой элемент архитектуры, и им можно пренебречь.
PointNet — это достаточно простая архитектура. Она использует геометрию облака, потому что координаты точек входят в векторы признаков, но работает не с локальной окрестностью точки, а со всем облаком в целом. Если мы обратимся к аналогиям с изображениями, то такая архитектура будет эквивалентна CNN со свёртками размера всего изображения. Такая архитектура с трудом улавливает локальные паттерны и детали, и именно это будет основным направлением дальнейшего улучшения этой архитектуры.
PointNet++
Продолжением статьи PointNet стала статья PointNet++ от той же научной группы. Новая архитектура развивает идею локальности, о который мы писали в предыдущем параграфе.
Давайте подумаем, каким образом мы можем получить локальные, зависящие только от окрестности точки операции над облаком точек. Первым делом нам необходимо понять, что такое локальность. В облаке точек очевидным решением кажется определить окрестность точки исходя из евклидового расстояния между точками — как шар некоторого радиуса — и проводить операции внутри этого шара.
Какие операции мы будем проводить внутри шара? У нас уже есть PointNet, который по произвольному набору точек может построить вектор, описывающий этот набор в целом. С его помощью мы можем описать многослойную архитектуру, напоминающую энкодер свёрточной сети, каждый слой которой будет выглядеть следующим образом:
- сэмплируем ключевых точек в облаке, где меньше размера облака (как правило в несколько раз);
- вокруг каждой ключевой точки фиксируем шар радиуса ;
- для каждого шара находим все точки, которые в нём лежат;
- запускаем PointNet с одними и теми же весами для каждого шара;
- получаем новое облако из точек, где каждой точке присвоен вектор признаков, полученный из PointNet.
Далее мы можем повторять эту процедуру, пока у нас не останется одна точка, то есть один вектор признаков для всего облака в целом. Полученный эмбединг можно использовать для решения различных задач.
Обсудим детали реализации этой архитектуры.
В качестве алгоритма сэмплирования предлагается Farthest Point Sampling (FPS). Он заключается в том, что мы жадно сэмплируем точку, которая максимально удалена от текущего насэмплированного множества. Этот процесс мы повторяем, пока не наберём достаточное количество точек. Благодаря FPS мы можем в некоторой степени гарантировать, что покроем равномерно все облако и не пропустим какие-то мелкие, но удалённые от основного облака объекты. В этом ценное отличие FPS от случайного сэмплирования, при котором детали, содержащие мало точек, могут быть легко потеряны.
Выбор радиуса шара зависит, как правило, от плотности облака. Стоит посмотреть, сколько примерно точек попадает в каждую окрестность, и исходя из этого фиксировать радиус. Также этот параметр можно подобрать с помощью валидации. Для удобства реализации фиксируют не только радиус, но и максимальное количество объектов внутри шара.
Это делается для того, чтобы при реализации архитектуры можно было манипулировать тензорами, избегая итерации по всем точкам каждого шара. Например, мы фиксируем количество шаров , количество точек внутри шара и размерность вектора признаков для каждой точки . Тогда размер тензора будет: . Если в окрестности шара оказалось больше точек, чем мы заранее определили, то возьмем ближайшие . В случае, если точек в тензоре меньше, чем , тензор паддится нулями до нужного размера.
Внутри каждого слоя веса PointNet одинаковы для всех шаров. На окрестностях с одинаковой локальной структурой мы хотим получать одинаковые результаты, поэтому мы не можем использовать абсолютные значения .
В этом плане мы хотим, чтобы наши слои были похожи на свёртки, результат работы которых тоже не зависят от положения пикселя на изображении. Чтобы этого добиться, координаты каждой точки преобразуются в локальные координаты , где — координаты центра шара.
Отдельно стоит обсудить, как получить поточечные признаки для решения задачи сегментации. Нам предстоит обратить энкодер и получить архитектуру декодера. Для этого каждый раз, когда мы делаем downsampling облака, то есть переход от вектора для каждой точки к общему вектору для некоторой окрестности, мы будем запоминать, какие точки принадлежали к какому шару.
В процессе upsampling, то есть перехода от вектора окрестности к поточечным векторам, мы будем конкатенировать вектор окрестности с исходными векторами признаков точек. После каждой операции upsampling мы можем запускать MLP, чтобы точка, получив информацию с более глубокого уровня, могла извлечь оттуда информацию.
Воксельные архитектуры
Мы разобрали архитектуру PointNet++, которая построена на обработке локальных окрестностей точек, определённых исходя из евклидового расстояния. У этой архитектуры есть свои проблемы.
Во-первых, поиск ближайших соседей и FPS — это медленные процедуры. В результате могут возникнуть проблемы, когда мы захотим использовать такую сеть в real-time приложениях.
Во-вторых, в зависимости от расстояния до сенсора у нас может меняться паттерн разреженности: если вблизи сенсора в шаре радиуса мы можем найти в среднем 100 точек, то, как правило, чем больше расстояние до наблюдателя — тем меньше точек мы будем находить в таком же объёме. Это усложняет обучение и может привести к тому, что с расстоянием качество будет сильно деградировать.
Давайте попробуем напрямую применить свёрточные нейронные сети в нашей задаче. Идея следующая: возьмём прямоугольный параллелепипед, который накрывает интересующую нас область пространства. Далее разобьём этот прямоугольный параллелепипед на значительно меньшие прямоугольные параллелипипеды одинакового размера. Назовем эти прямоугольные параллелепипеды вокселями. В результате внутри каждого вокселя окажется некоторый набор точек. Нам нужно каким-то образом превратить точки внутри каждого вокселя в один вектор. После этого мы можем применить обычные свёрточные сети и решать любые задачи.
Чтобы из набора точек внутри вокселя получить вектор признаков, мы можем просто применить PointNet. Таким образом, мы получим тензор размера , где первые три размерности пространственные, а последняя размерность — размерность вектора признаков.
Далее мы можем использовать 3D-свёртки для того, чтобы обработать этот тензор. Но проблема в том, что ядро в 3D-свёртках имеет на одну размерность больше, чем в 2D-свёртках, что делает их дорогими для вычисления.
В статьях были предложены различные подходы для того, чтобы ускорить эту архитектуру. Часто размерность по высоте и размерность признаков объединяют в одну: тензор размерности превращается в тензор размерности , как будто мы смотрим на него сверху (bird’s eye view). Для работы с ними мы можем использовать 2D-свёртки.
Ещё одним популярным трюком является использование высокой степени разреженности этого тензора: большое количество «столбиков» не содержат ни одной точки. Давайте возьмём все «столбики», содержащие хотя бы одну точку; предположим, что их штук. Соберём из них тензор размера и применим к нему линейный слой, который уменьшит число каналов, то есть высоту «столбиков».
Получится тензор для выбранного нами . Его столбцы мы снова расставим на их исходные места, получив тензор . «Столбики» этого тензора, которые соответствуют пустым «столбикам» исходного тензора , мы заполним нулями.
Воксельный подход очень популярен в задаче детекции, потому что позволяет напрямую переиспользовать некоторые подходы к 2D детектированию. Но у него есть и недостатки. Например, нельзя сколько угодно плотно покрыть вокселями сколь угодно большой объём, потому что это напрямую влияет на размер тензора, а значит — на время работы, что может быть критично в real-time приложениях. В итоге мы можем потерять какие-то важные удалённые объекты или не справиться с мелкими деталями.
Архитектуры с цилиндрической проекцией
Основная проблема воксельных архитектур — это неспособность обрабатывать облака произвольного размера и дальности. У вас может быть самый лучший LiDAR в мире, который видит на 300 метров во все стороны, но если ваша архитектура не будет способна обрабатывать полученное облако точек в режиме реального времени, то такой сенсор бесполезен.
Чтобы преодолеть эту проблему, мы можем воспользоваться тем фактом, что лидар сканирует окрестность из одной точки. Давайте окружим сенсор виртуальным цилиндром и спроецируем все точки на этот цилиндр. Далее цилиндр можно развернуть в прямоугольник и получить представление с которым могут работать свёрточные сети.
Обычно точки параметризуют двумя углами:
- pitch — наклон по вертикали,
- yaw — угол по горизонтали.
Мы можем дискретизовать эти углы и таким образом получить для каждой точки координаты пикселя, в который она должна быть помещена. В каналы нашего тензора мы можем записать: расстояние до точки, интенсивность сигнала, абсолютную высоту точки. Далее такое представление может быть обработано любой 2D архитектурой.
Важно отметить, что такое представление не будет работать для других методов получения облаков точек. Лидар сканирует окрестность из одной точки, и потому в направлении каждого луча у нас будет только одна точка. Это означает, что мы не видим за препятствиями. В то время облака, полученные, например, с помощью Structure from Motion, в направлении одного луча могут содержать несколько точек, что лишает нас возможности без потерь спроецировать всё облако на 2D холст.
Такое представление снимает проблему с производительностью, так как размер обрабатываемых данных не зависит от разброса облака. Но, к сожалению, появляются новые проблемы.
Во-первых, объекты, которые находятся далеко друг от друга в трёхмерном пространстве, могут оказаться рядом в этой проекции — в итоге, информация о таких объектах будет смешиваться, и в результате границы в задаче сегментации могут получиться размытыми. Воксельные- или PointNet-архитектуры не имеют этой проблемы — в них далёкие друг от друга объекты не будут так сильно взаимодействовать.
Во-вторых, объекты при таком представлении будут иметь разные размеры в зависимости от расстояния, и модели придётся адаптироваться к этому. Воксельные и PointNet архитектуры, опять же, лишены этой проблемы, так как их представления инвариантны к переносу.
Обе эти проблемы могут приводить к существенной деградации качества. Именно поэтому архитектуры, построенные на цилиндрической проекции, часто проигрывают другим архитектурам по качеству.
Объединение информации с облака и изображения
В некоторых приложениях у нас может быть несколько различных сенсоров: например, лидар и камера. Тогда возникает задача: построить архитектуру, которая использует информацию с обоих сенсоров.
Конечно, мы бы могли отдельно подготовить архитектуру для работы с изображением, отдельно архитектуру для работы с облаком и затем каким-то образом объединить результаты. Минус такого подхода в том, что нам требуется в два раза больше вычислительных мощностей, а нейронные сети никак не делятся информацией на промежуточных слоях. Возможно, нейронная сеть могла бы извлечь более богатые представления из объединенных данных. Например, разрешить некоторую неопределённость в облаке с помощью изображений.
Оказывается, что задача смешивания различных сенсоров очень непростая, и в литературе долгое время не могли для задачи 3D детекции получить результаты, которые бы превосходили только лидарный подход. Мы обсудим идеи, как именно можно смешивать изображения и 3D информацию.
Прежде чем мы перейдем к описанию подходов, давайте обсудим одну важную техническую деталь. Смешивание информации с разных сенсоров всегда предполагает, что сенсоры скалиброваны. Это означает что нам известна матрица перехода из системы координат одного сенсора в систему координат другого. Таким образом, у нас есть необходимая информация, для того, чтобы спроецировать лидарную точку на плоскость изображения.
Архитектура, которая первой показала действительно значительный прирост качества при смешивании сенсоров, называется PointPainting. Идея была следующей:
- Делаем сегментацию изображения;
- Для каждой лидарной точки найдём соответствующий ей пиксель на изображении и присвоим этой точке соответствующий класс из сегментации;
- Для архитектуры, которая работает с облаком точек, этот класс будет еще одним дополнительным признаком на входе.
Важно отметить, что сопоставление точки класса не будет идеальным, например, из-за того, что камера и лидар находятся не в одной точке.
Идея очень простая, но при этом позволяет сильно улучшить метрики. Проблема в том, что данный подход не решает проблему возросших вычислений при обработке данных с двух сенсоров. Более того, мы передаем в 3D архитектуру лишь одно число для каждой точки и теряем много информации об изображении.
Другой подход был описан в статье LaserNet++. Авторы используют цилиндрическую проекцию для обработки облака. Перед началом обработки они пропускают изображение через небольшую свёрточную сеть, получая в результате некоторый вектор признаков в каждом пикселе.
Далее для каждой лидарной точки находим соответствующий пиксель на изображении и добавляем его признаки к признакам точки. Отличие от PointPainting в том, что в данном подходе обе нейронные сети обучаются end-to-end. Это позволяет нам извлечь более богатое представление из изображения.
В статье авторы сообщают о росте метрик на in-house наборе данных. К сожалению, на открытых данных этот подход не смог продемонстрировать превосходства над lidar-only моделями.
В этом параграфе мы разобрали основные идеи работы с облаками точек. Верхнеуровнево все архитектуры можно разделить на следующие группы:
- Облако как граф (PointNet и многие другие архитектуры);
- Вокселизация (VoxelNet, PointPillars и другие);
- Проекция лидарного облака на 2D поверхность (RangerNet, LaserNet, LaserNet++ и другие).
Каждый подход имеет свои плюсы и минусы и выбор конкретного подхода должен быть обусловлен решаемой задачей.