Python中的三维仿射变换

3
我正在使用PyMel为Autodesk Maya编程Python函数。我有三个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...


考虑到这个网站上很少有人能理解你的问题(因为它实在是非常特定),你最好的选择是发布你正在处理向量的代码,因为我们可能会真正地帮助你解决问题。 - sshashank124
抱歉,我觉得我的代码可能没有什么帮助。到目前为止,我只是分配了一些点数。我现在已经添加了它,希望这会让我的问题更容易理解。 - user1066278
3个回答

2

这里有一个使用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

1

描述和您的代码都很令人困惑。描述有点模糊,而代码示例缺少重要的细节。因此,这就是我对问题的理解:

如果在两个空间中知道三个点,如何构建从空间A到空间B的转换?

two spaces transformed

图片1:如何在两个空间之间进行转换。
答案取决于空间的变换类型。您会发现,三个点总是形成一个平面跨度。这意味着您可以知道新空间的旋转、变换和均匀缩放。您还可以知道平面上的剪切,以及非均匀缩放。然而,您无法知道平面法线方向上的剪切或非均匀缩放。
因此,问题变成了如何旋转和平移两个空间以匹配?这很容易做到,平移部分直接为:

trans = q0 - p0

这样就只剩下旋转了,旋转已经在几篇文章中进行了解释: 你可以在此之后计算一个缩放因子。

0

我已经想通了

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的投影点来解决它,然后将其移出平面相同的距离。

如果您有一个仅涉及矩阵的解决方案,请随时分享,这可能仍然对我有所帮助!:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接