В основе DroneDeploy лежит продукт под названием Map Engine, способный быстро и точно реконструировать 3D-сцены из коллекции фотографий без каких-либо предварительных сведений о том, где были сделаны фотографии или что они содержат. Это одна из основных проблем области фотограмметрии, которая широко использует методы компьютерного зрения и машинного обучения. Возможность сделать это очень важна, так как цифровая камера намного дешевле и ее легче перемещать, чем 3D-сканер, но вам нужно какое-то интеллектуальное программное обеспечение. Это первый из трех постов, описывающих, как происходит этот процесс от начала до конца. Мы также будем внедрять простой обучающий конвейер, который реконструирует 3D-сцены из 2D-изображений.

Сначала мы рассмотрим некоторые основы камер и запрограммируем некоторые строительные блоки. Наша история начинается с мощной цифровой камеры. Камеры используют свет для захвата 2D-представления 3D-мира, подвергая датчик воздействию света, сфокусированного через линзу. Это создает цифровое изображение. Фотографы говорят о камерах с точки зрения выдержки, фокусного расстояния и ISO. Вот — отличная интерактивная иллюстрация того, как эти факторы влияют на итоговое изображение.

В компьютерном зрении мы подходим к камерам по-другому и больше интересуемся внешней и внутренней геометрией камеры. Внешняя геометрия, называемая внешними элементами, показывает, как мир трансформируется относительно камеры, когда мы смотрим сквозь нее. Однако обычно более интуитивно понятно указать, где камера находится в мире, чем то, как она трансформирует мир. Это называется позой, и мы указываем ее как матрицу [R | t], где R — матрица вращения 3x3, а t — вектор перемещения 3x1. Тогда внешние признаки равны E = [R',-R'*t]. Эта инверсия позы трансформирует мир так, что мы эффективно смотрим вниз по оптической оси камеры.

Далее у нас есть внутренняя геометрия камеры, называемая внутренностями, которые представляют собой преобразование в пиксели. Двумя важными значениями здесь являются центр камеры (также называемый главной точкой) и фокусное расстояние. Вместе в матрице 3x3 как K это представляет собой масштабирование и перевод. Это как отображение пикселей в изображение на основе поля зрения камеры и того, где находится отверстие камеры. Теперь мы можем создать матрицу проекции как P = K * E. Вместе матрицу проекции камеры можно рассматривать как воздействующую на трехмерную точку как перемещение вращения, за которым следует перемещение и масштабирование, дающее окончательные координаты изображения. Что может вызывать края изображения в зависимости от разрешения в пикселях.

Проекция

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

Давайте проверим это, создав несколько трехмерных точек мира. Мы создадим вершины куба и поместим камеру назад по оси Z. Затем мы спроецируем каждую из вершин куба в камеру, и мы должны получить 2D-представление.

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

В стороне: разложение

Мы также можем вернуться назад и разложить проекцию на K, R, t компонентов, помня, что структура P = K[R | -R'*C]мы можем сначала извлечь t и разложить остаток, чтобы получить K и R. К сожалению, это разложение не является уникальным, потому что если вы возьмете результирующие матрицы R и Q из разложения RQ и инвертируете строку и соответствующий столбец R и Q, результирующая матрица проекции будет той же.

Расположение камеры

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

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

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

Еще немного визуализации

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

В результате вся наша сцена с камерами выглядит как наш куб.

Триангуляция

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

Если мы возьмем два изображения нашего куба и запишем, как два 2D-пикселя были вычислены из 3D-точек с использованием двух проекционных матриц ( P1 и P2 ), мы получим линейную систему уравнений. Мы могли бы попытаться решить эту проблему, но здесь кое-что пойдет не так, матрица, как правило, не квадратная (если только у вас нет двух представлений, и поэтому обратное не работает. Лучший и более численно устойчивый способ — вместо этого выглядеть как однородная система уравнений Это имеет тривиальное решение в 0 0 0 которое нас не очень волнует, но если мы возьмем разложение по сингулярным значениям (SVD) этой матрицы и возьмем сингулярный вектор, соответствующий наименьшему сингулярному значению, мы получим решение к системе с некоторыми полезными дополнительными свойствами. В частности, он ограничивает вектор решения величиной 1, что позволяет избежать тривиального решения. Он также численно стабилен и естественным образом используется для решения переопределенных систем. Давайте закодируем это и создадим функция, которая берет список камер и соответствующий список координат изображения и триангулирует их.

Больше триангуляции

Триангуляция на основе SVD делает два предположения. Во-первых, это погрешность, которую мы стремимся минимизировать, а во-вторых, как камера работает с трехмерными точками, а именно как линейная операция. Мы также можем триангулировать точки, используя нелинейный решатель. Мы можем сформулировать это как задачу, в которой мы ищем X, Y, Z координат в мире так, чтобы они проецировались на соответствующие координаты изображения в каждой рассматриваемой нами камере. Мы можем использовать что-то вроде Левенберга-Марквардта, чтобы решить эту проблему. Хотя это занимает больше времени, чем наше аналитическое решение, оно позволяет пользователю быть более гибким в отношении некоторых вещей. Например, вместо метода наименьших квадратов мы можем использовать другие нормы, такие как норма Хьюбера, для обработки выбросов в наших данных. Это полезно, как мы увидим в следующем посте, где мы точно не знаем, где находятся наши 3D- и 2D-точки. Еще одно преимущество заключается в том, что мы можем использовать более сложные модели камер, которые нельзя просто представить в виде умножения матриц.

Искажение

Мы только что упомянули «более сложные модели камер» — до сих пор мы имели дело только с идеальной камерой, называемой камерой-обскурой. В реальном мире камеры не так совершенны. Из-за несовершенства и допусков в производственном процессе каждая камера немного отличается, а также используемый объектив может вносить типы искажений. Например, фотография сцены с идеально прямыми линиями может показаться искривленной на изображении. Это может вызвать проблемы при неправильном обращении, поскольку обычно реконструкции используются для измерения и планирования. Вот простая реализация модели камеры Брауна-Конради:

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

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

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