Как выполнить аффинное преобразование координат с помощью Python? - PullRequest
4 голосов
/ 16 января 2012

Я хотел бы выполнить преобразование для этого примера набора данных.
Существует четыре известных точки с координатами x, y, z в одной системе координат [primary_system] и следующие четыре известные точки с координатами x, y, h, которыепринадлежат другой системе координат [вторичная_система].Эти точки соответствуют;Например, точка primary_system1 и точка second_system1 - это в точности одна и та же точка, но у нас есть ее координаты в двух разных системах координат.Итак, у меня есть четыре пары корректирующих точек, и я хочу преобразовать координаты другой точки из первичной системы во вторичную систему в соответствии с корректировкой.

primary_system1 = (3531820.440, 1174966.736, 5162268.086)
primary_system2 = (3531746.800, 1175275.159, 5162241.325)
primary_system3 = (3532510.182, 1174373.785, 5161954.920)
primary_system4 = (3532495.968, 1175507.195, 5161685.049)

secondary_system1 = (6089665.610, 3591595.470, 148.810)
secondary_system2 = (6089633.900, 3591912.090, 143.120)
secondary_system3 = (6089088.170, 3590826.470, 166.350)
secondary_system4 = (6088672.490, 3591914.630, 147.440)

#transform this point
x = 3532412.323 
y = 1175511.432
z = 5161677.111<br>


в настоящий момент я пытаюсь усреднить перевод для x, yи ось z с использованием каждой из четырех пар точек, таких как:

#x axis
xt1 =  secondary_system1[0] - primary_system1[0]           
xt2 =  secondary_system2[0] - primary_system2[0]
xt3 =  secondary_system3[0] - primary_system3[0]
xt4 =  secondary_system4[0] - primary_system4[0]

xt = (xt1+xt2+xt3+xt4)/4    #averaging

... и т. д. для оси y и z

#y axis
yt1 =  secondary_system1[1] - primary_system1[1]           
yt2 =  secondary_system2[1] - primary_system2[1]
yt3 =  secondary_system3[1] - primary_system3[1]
yt4 =  secondary_system4[1] - primary_system4[1]

yt = (yt1+yt2+yt3+yt4)/4    #averaging

#z axis
zt1 =  secondary_system1[2] - primary_system1[2]           
zt2 =  secondary_system2[2] - primary_system2[2]
zt3 =  secondary_system3[2] - primary_system3[2]
zt4 =  secondary_system4[2] - primary_system4[2]

zt = (zt1+zt2+zt3+zt4)/4    #averaging

Так что выше я попыталсярассчитать средний вектор перевода для каждой оси

Ответы [ 2 ]

9 голосов
/ 16 января 2012

Если это просто перевод и вращение, то это преобразование, известное как аффинное преобразование .

Это в основном принимает форму:

secondary_system = A * primary_system + b

, где A - это матрица 3x3 (поскольку вы находитесь в 3D), а b - это перевод 3x1.

Это может быть эквивалентно написано

secondary_system_coords2 = A2 * primary_system2,

, где

  • secondary_system_coords2 - это вектор [secondary_system,1],
  • primary_system2 - это вектор [primary_system,1], а
  • A2 - это матрица 4x4:

    [   A   b ]
    [ 0,0,0,1 ]
    

(см. Вики-страницу для получения дополнительной информации).

Итак, по сути, вы хотите решить уравнение:

y = A2 x

для A2, где y состоит из точек от secondary_system с 1, застрявшим на конце, и x - от primary_system с 1, застрявшим на конце, а A2 - 4x4 матрица.

Теперь, если x - квадратная матрица, мы можем решить ее следующим образом:

A2 = y*x^(-1)

Но x - это 4x1. Однако вам повезло, и у вас есть 4 наборов x с 4 соответствующими наборами y, так что вы можете создать x, равный 4x4, например:

x = [ primary_system1 | primary_system2 | primary_system3 | primary_system4 ]

, где каждый из primary_systemi является вектором столбца 4x1. То же самое с y.

Если у вас есть A2, чтобы преобразовать точку из системы 1 в систему 2, вы просто делаете:

transformed = A2 * point_to_transform

Вы можете настроить это (например, в numpy) следующим образом:

import numpy as np
def solve_affine( p1, p2, p3, p4, s1, s2, s3, s4 ):
    x = np.transpose(np.matrix([p1,p2,p3,p4]))
    y = np.transpose(np.matrix([s1,s2,s3,s4]))
    # add ones on the bottom of x and y
    x = np.vstack((x,[1,1,1,1]))
    y = np.vstack((y,[1,1,1,1]))
    # solve for A2
    A2 = y * x.I
    # return function that takes input x and transforms it
    # don't need to return the 4th row as it is 
    return lambda x: (A2*np.vstack((np.matrix(x).reshape(3,1),1)))[0:3,:]

Тогда используйте это так:

transformFn = solve_affine( primary_system1, primary_system2, 
                            primary_system3, primary_system4,
                            secondary_system1, secondary_system2,
                            secondary_system3, secondary_system4 )

# test: transform primary_system1 and we should get secondary_system1
np.matrix(secondary_system1).T - transformFn( primary_system1 )
# np.linalg.norm of above is 0.02555

# transform another point (x,y,z).
transformed = transformFn((x,y,z))

Примечание: Конечно, здесь есть числовая ошибка, и это, возможно, не лучший способ решения для преобразования (вы можете сделать что-то вроде наименьших квадратов).

Кроме того, ошибка преобразования primary_systemx в secondary_systemx имеет (для этого примера) порядка 10 ^ (- 2).

Вы должны будете рассмотреть, является ли это приемлемым или нет (оно кажется большим, но оно может быть приемлемым по сравнению с вашими входными точками, которые имеют порядок 10 ^ 6).

0 голосов
/ 27 мая 2019

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

secondary_system = A * primary_system + t

Теперь задача сводится к поиску подходящей матрицы A и вектора t.Я думаю, этот код может помочь вам (извините за плохой стиль кода - я математик, а не программист)

import numpy as np
# input data
ins = np.array([[3531820.440, 1174966.736, 5162268.086],
                [3531746.800, 1175275.159, 5162241.325],
                [3532510.182, 1174373.785, 5161954.920],
                [3532495.968, 1175507.195, 5161685.049]]) # <- primary system
out = np.array([[6089665.610, 3591595.470, 148.810],
                [6089633.900, 3591912.090, 143.120],
                [6089088.170, 3590826.470, 166.350],
                [6088672.490, 3591914.630, 147.440]]) # <- secondary system
p = np.array([3532412.323, 1175511.432, 5161677.111]) #<- transform this point
# finding transformation
l = len(ins)
entry = lambda r,d: np.linalg.det(np.delete(np.vstack([r, ins.T, np.ones(l)]), d, axis=0))
M = np.array([[(-1)**i * entry(R, i) for R in out.T] for i in range(l+1)])
A, t = np.hsplit(M[1:].T/(-M[0])[:,None], [l-1])
t = np.transpose(t)[0]
# output transformation
print("Affine transformation matrix:\n", A)
print("Affine transformation translation vector:\n", t)
# unittests
print("TESTING:")
for p, P in zip(np.array(ins), np.array(out)):
  image_p = np.dot(A, p) + t
  result = "[OK]" if np.allclose(image_p, P) else "[ERROR]"
  print(p, " mapped to: ", image_p, " ; expected: ", P, result)
# calculate points
print("CALCULATION:")
P = np.dot(A, p) + t
print(p, " mapped to: ", P)

Этот код демонстрирует, как восстановить аффинное преобразование в виде матрицы + вектора и проверяет, что начальные точкисопоставлены с тем, где они должны.Вы можете протестировать этот код с помощью Google colab , поэтому вам не нужно ничего устанавливать.

Что касается теории, лежащей в основе этого кода: он основан на уравнении, представленном в " Руководстве для начинающихдля аффинно сопоставления симплексов"восстановление матрицы описано в разделе" Восстановление канонической нотации ", а количество точек, необходимых для точного определения точного аффинного преобразования, обсуждается в разделе" Сколько точек нам нужно? "раздел.Те же авторы опубликовали « рабочую книгу по аффинно по отображению симплексов», в которой содержится много практических примеров такого рода.

...