Документация Engee

CoordinateTransformations

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

Пакет предоставляет две основные возможности.

  1. Во-первых, интерфейс для определения преобразований Transformation и их применения (путем вызова), инвертирования (inv()), комбинирования ( или compose()) и дифференцирования (transform_deriv() и transform_deriv_params()).

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

Быстрое начало работы

Давайте преобразуем трехмерную точку:

using CoordinateTransformations, Rotations, StaticArrays

x = SVector(1.0, 2.0, 3.0)  # SVector предоставляется пакетом StaticArrays.jl
trans = Translation(3.5, 1.5, 0.0)

y = trans(x)

Мы можем либо применять различные преобразования по очереди,

rot = LinearMap(RotX(0.3))  # Выполнить поворот на 0,3 радиана относительно оси X (с помощью Rotations.jl)

z = trans(rot(x))

либо построить составное преобразование с помощью оператора (в REPL введите \circ, а затем нажмите клавишу TAB):

composed = trans ∘ rot  # альтернативный вариант — compose(trans, rot)

composed(x) == z

В результате композиции Translation и LinearMap получается AffineMap.

Преобразование можно инвертировать:

composed_inv = inv(composed)

composed_inv(z) == x

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

rot_around_x = recenter(rot, x)

Теперь rot_around_x — это поворот вокруг точки x = SVector(1.0, 2.0, 3.0).

Наконец, можно построить матрицу, описывающую то, как компоненты z дифференцируются относительно компонентов x:

# В общем случае преобразование может быть нелинейным, поэтому
# для вычисления производной требуется значение x
∂z_∂x = transform_deriv(composed, x)

Может также потребоваться узнать, как изменится y в зависимости от изменения параметров преобразования:

∂y_∂θ = transform_deriv_params(trans, x)

Интерфейс

Преобразования являются производными от Transformation. Пример: Translation{T} <: Transformation. Translation принимает и преобразует точки в различных форматах, таких как Vector или SVector, но в целом пользовательские подтипы Transformation могут преобразовывать любой объект Julia.

Преобразования можно обратить с помощью inv(trans). Их можно объединить в цепочку с помощью оператора (trans1 ∘ trans2) или функции compose (compose(trans1, trans2)). В подобном случае к данным сначала применяется trans2, а затем trans1. Композиция может быть интеллектуальной. Например, новое преобразование Translation может предварительно вычисляться путем суммирования элементов двух существующих Translation, а другие преобразования могут образовывать IdentityTransformation. Однако по умолчанию результатом композиции является объект ComposedTransformation, который просто диспетчеризуется для применения преобразований в правильном порядке.

Наконец, с помощью метода transform_deriv(trans, x) можно вычислить матрицу, описывающую то, как передаются дифференциалы в ходе преобразования. Доступ к производным, определяющим зависимость выходных данных от параметров преобразования, осуществляется через transform_deriv_params(trans, x). В настоящее время пользователям приходится перегружать эти методы, так как резервное автоматическое дифференцирование не предусмотрено. Однако все встроенные типы и преобразования совместимы с методами автоматического дифференцирования и могут быть параметризованы с помощью DualNumber из DualNumbers или Dual из ForwardDiff.

Встроенные преобразования

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

Типы координат

Пакет принимает любой тип AbstractVector в качестве декартовых координат. Для повышения скорости мы рекомендуем использовать контейнер постоянного размера, например SVector{N} из StaticArrays.

Мы также предоставляем несколько специальных типов координат. Тип Polar(r, θ) — это двухмерное полярное представление точки, а в трехмерном пространстве аналогичным образом определены типы Spherical(r, θ, ϕ) и Cylindrical(r, θ, z).

Преобразования системы координат

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

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

Однако проще в использовании могут оказаться такие вспомогательные конструкторы, как Polar(SVector(1.0, 2.0)).

Переносы

Переносы можно применять к декартовым координатам в произвольном количестве измерений, например с помощью Translation(Δx, Δy) или Translation(Δx, Δy, Δz) в двухмерном или трехмерном пространстве соответственно либо с помощью Translation(Δv) в общем случае (где Δv — AbstractVector). При композиции двух переносов Translation создается новый перенос Translation путем добавления векторов переноса.

Линейные преобразования

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

Вы можете предоставить любую матрицу на свой выбор, однако выбор типа существенно влияет на быстродействие. Так, если известна размерность точек (например, двухмерные или трехмерные), можно прибегнуть к матрице постоянного размера, например SMatrix из StaticArrays.jl. Мы рекомендуем выполнять трехмерные повороты, используя инструменты из Rotations.jl из-за их скорости и гибкости. Масштабирование будет эффективным со встроенной функцией Julia UniformScaling. Кроме того, обратите внимание, что при композиции двух отображений LinearMap создается новое отображение LinearMap путем умножения матриц преобразования.

Аффинные отображения

Аффинное отображение инкапсулирует более общий набор преобразований, которые определяются композицией переноса и линейного преобразования. Объект AffineMap строится из переноса v для AbstractVector и линейного преобразования M для AbstractMatrix. Он выполняет отображение x -> M*x + v, но очередность сложения и умножения будет более очевидной (и контролируемой) при его создании на основе композиции линейного отображения и переноса, например Translation(v) ∘ LinearMap(v) (или любого сочетания LinearMap, Translation и AffineMap).

Отображения AffineMap можно строить так, чтобы они соответствовали парам точек from_points => to_points:

julia> from_points = [[0, 0], [1, 0], [0, 1]];

julia> to_points   = [[1, 1], [3, 1], [1.5, 3]];

julia> AffineMap(from_points => to_points)
AffineMap([1.9999999999999996 0.5; -5.551115123125783e-16 2.0], [0.9999999999999999, 1.0000000000000002])

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

Преобразования проецирования

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

Команда PerspectiveMap() создает преобразование Transformation для выполнения отображения проецирования. Ее можно применять по отдельности, но она особенно эффективна в сочетании с объектом AffineMap, содержащим положение и ориентацию камеры в сцене. Например, чтобы перенести точки points из трехмерного пространства в двухмерный объект screen_points с учетом их проецируемого положения на изображении виртуальной камеры, можно использовать следующий код:

cam_transform = PerspectiveMap() ∘ inv(AffineMap(cam_rotation, cam_position))
screen_points = map(cam_transform, points)

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

Благодарности