簡體   English   中英

Python中的仿射3D變換

[英]Affine 3D transformation in Python

我在Autodesk Maya中使用Python編寫一個函數(使用PyMel for Maya)

我有三個3D點; p0,p1,p2。

然后他們做了一個嚴格的轉變,所以在轉變(仿射變換)后,我有了他們的新職位; q0,q1,q2。

轉型前我還有第四點; P3。 我想在同樣的轉變后計算它的位置; Q4。

所以我需要計算變換矩陣,然后將其應用到p4。 我不知道該怎么做。 List =一個對象數組

import pymel.core as pm
import pymel.core.datatypes as dt

p0 = dt.Vector(pm.getAttr(list[0]+".tx"), pm.getAttr(list[0]+".ty"), pm.getAttr(list[0]+".tz"))
p1 = dt.Vector(pm.getAttr(list[1]+".tx"), pm.getAttr(list[1]+".ty"), pm.getAttr(list[1]+".tz"))
p2 = dt.Vector(pm.getAttr(list[2]+".tx"), pm.getAttr(list[2]+".ty"), pm.getAttr(list[2]+".tz")
p3 = dt.Vector(pm.getAttr(list[3]+".tx"), pm.getAttr(list[3]+".ty"), pm.getAttr(list[3]+".tz"))

從Maya場景中的動畫對象讀取3D點。 所以在另一個框架中,我運行此代碼來獲取

q0 = dt.Vector(pm.getAttr(list[0]+".tx"), pm.getAttr(list[0]+".ty"), pm.getAttr(list[0]+".tz"))
q1 = dt.Vector(pm.getAttr(list[1]+".tx"), pm.getAttr(list[1]+".ty"), pm.getAttr(list[1]+".tz"))
q2 = dt.Vector(pm.getAttr(list[2]+".tx"), pm.getAttr(list[2]+".ty"), pm.getAttr(list[2]+".tz"))
#q3 = TransformationMatrix between (p0,p1,p2) and (q0,q1,q2), applied to p3

我試着用向量計算,但由於划分為零,我最終得到了錯誤...所以我認為轉換矩陣應該沒有問題地解決它。

我有一個不遠處的截止日期,我真的需要幫助解決這個問題! 請幫忙!

編輯: 如何使用python執行坐標仿射變換?

我需要這個函數“solve_affine”,但每組只需要3分而不是4分。我不能使用numpy ......

這是一個使用numpy和scipy的解決方案。 scipy主要用於生成隨機旋轉,除了scipy.linalg.norm,它很容易編碼自己。 numpy使用的主要內容是交叉乘法和矩陣乘法,它們也很容易編碼。

基本思想是:給定三個非共線點x1,x2,x3,可以找到正交的三個向量(軸)v1,v2,v3,其中v1在x2-x1方向,v2在平面內跨越(x2-x1)和(x3-x1),v3完成三重。

點y1,y2,y3相對於x1,x2,x3旋轉和平移。 從y1,y2,y3生成的軸w1,w2,w3從v1,v2,v3旋轉(即,不平移)。 這兩組三元組各自是正交的,因此很容易找到它們的旋轉: R = W * transpose(V)

一旦我們進行了旋轉,找到平移很簡單: y1 = R*x + t ,所以t = y1 - R*x 使用最小二乘解算器並將所有三個點組合起來得到t的估計值可能更好。

import numpy
import scipy.linalg


def rand_rot():
    """Return a random rotation

    Return a random orthogonal matrix with determinant 1"""
    q, _ = scipy.linalg.qr(numpy.random.randn(3, 3))
    if scipy.linalg.det(q) < 0:
        # does this ever happen?
        print "got a negative det"
        q[:, 0] = -q[:, 0]
    return q


def rand_noncollinear():
    """Return 3 random non-collinear vectors"""
    while True:
        b = numpy.random.randn(3, 3)
        sigma = scipy.linalg.svdvals(b)
        if sigma[2]/sigma[0] > 0.1:
            # "very" non-collinear
            break
        # "nearly" collinear; try again

    return b[:, 0], b[:, 1], b[:, 2]


def normalize(a):
    """Return argument normalized"""
    return a/scipy.linalg.norm(a)


def colstack(a1, a2, a3):
    """Stack three vectors as columns"""
    return numpy.hstack((a1[:, numpy.newaxis],
                         a2[:, numpy.newaxis],
                         a3[:, numpy.newaxis]))


def get_axes(a1, a2, a3):
    """Generate orthogonal axes from three non-collinear points"""
    # I tried to do this with QR, but something didn't work
    b1 = normalize(a2-a1)
    b2 = normalize(a3-a1)
    b3 = normalize(numpy.cross(b1, b2))
    b4 = normalize(numpy.cross(b3, b1))
    return b1, b4, b3

# random rotation and translation
r = rand_rot()
t = numpy.random.randn(3)

# three non-collinear points
x1, x2, x3 = rand_noncollinear()
# some other point
x4 = numpy.random.randn(3)

# the images of the above in the transformation.
# y4 is for checking only -- won't be used to estimate r or t
y1, y2, y3, y4 = [numpy.dot(r, x) + t
                  for x in x1, x2, x3, x4]


v1, v2, v3 = get_axes(x1, x2, x3)
w1, w2, w3 = get_axes(y1, y2, y3)

V = colstack(v1, v2, v3)
W = colstack(w1, w2, w3)

# W = R V, so R = W * inverse(V); but V orthogonal, so inverse(V) is
# transpose(V):
rfound = numpy.dot(W, V.T)

# y1 = R x1 + t, so...
tfound = y1-numpy.dot(r, x1)

# get error on images of x2 and x3, just in case

y2err = scipy.linalg.norm(numpy.dot(rfound, x2) + tfound - y2)
y3err = scipy.linalg.norm(numpy.dot(rfound, x3) + tfound - y3)

# and check error image of x4 -- getting an estimate of y4 is the
# point of all of this
y4err = scipy.linalg.norm(numpy.dot(rfound, x4) + tfound - y4)

print "y2 error: ", y2err
print "y3 error: ", y3err
print "y4 error: ", y4err

描述和代碼都令人困惑。 描述有點模糊,而代碼示例缺少重要的部分。 所以這就是我理解這個問題的方法:

知道兩個空間中的三個點如何構建從空間A到空間B的變換?

兩個空間變換了

圖1 :如何在2個空格之間形成轉換。

答案取決於空間的變換類型。 您會看到三個點始終形成平面跨度。 這意味着您可以知道新空間的旋轉,變換和統一比例是什么。 您還可以知道平面上的剪切以及非均勻刻度。 但是,您無法知道平面法線方向上的剪切或非均勻尺度。

因此,有意義的問題是如何旋轉和翻譯兩個空格以匹配? 這很容易做翻譯部分直接:

trans = q0 - p0

這讓你有了輪換,已在幾篇文章中解釋過:

您也可以在此之后計算縮放系數。

我已經明白了

p0p1 = p1-p0
p0p2 = p2-p0
p0p3 = p3-p0

q0q1 = q1-q0
q0q2 = q2-q0
q0q3 = q3-q0

before = dt.Matrix([p0.x, p0.y, p0.z, 0],[p1.x, p1.y, p1.z, 0],[p2.x, p2.y, p2.z, 0], [0,0,0,1]);
after = dt.Matrix([q0.x, q0.y, q0.z, 0],[q1.x, q1.y, q1.z, 0],[q2.x, q2.y, q2.z, 0], [0,0,0,1]);

normal = p0p1.cross(p0p2).normal()
dist = p0p3.dot(normal)
q3 = p3 - dist*normal

transformMatrix = before.inverse()*after
solve = dt.Matrix(q3.x, q3.y, q3.z, 1)*transformMatrix

q3 =  dt.Vector(solve[0][0], solve[0][1], solve[0][2])

newNormal = q0q1.cross(q0q2).normal()
q3 = q3 + newNormal*dist

pm.move(list[3], q3, r=False)

變換矩陣僅適用於平面p0p1p2內的點。 所以我通過改變p3的投影點來解決它,然后將它從平面移出相同的距離。

如果您的解決方案只涉及矩陣,請隨時分享,它可能仍然對我有幫助! :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM