使用仿射变换矩阵生成PyTorch中的theta

4

我正在尝试在pytorch上对一个3D体积执行刚性+缩放变换,但我好像无法理解torch.nn.functional.affine_grid所需的theta参数。

我有一个大小为(1,4,4)的变换矩阵,通过将平移矩阵*缩放矩阵*旋转矩阵相乘而生成。如果我在scipy.ndimage.affine_transform中使用此矩阵,则可以正常工作。然而,同样的矩阵(裁剪为大小(1,3,4))则完全在torch.nn.functional.affine_grid中失败。

我已经成功理解了平移是如何工作的(范围为-1到1),并通过简单地将值归一化到该范围来确认平移矩阵有效。至于其他两个矩阵,我感到很迷茫。

我尝试仅使用基本缩放矩阵(如下)进行最基本的比较,但在pytorch中的结果与scipy中的不同。

Scaling = 
[[0.75, 0, 0, 0],
[[0, 0.75, 0, 0],
[[0, 0, 0.75, 0],
[[0, 0, 0, 1]]

我怎样才能将(1,4,4)仿射矩阵转换为可与torch.nn.functional.affine_grid一起使用的矩阵?或者,基于变换参数(平移、欧拉角、缩放),有没有一种方法可以生成正确的矩阵?

你是如何将结果与scipy.ndimage.affine_transform进行比较的? - nnnmmm
1个回答

5
任何未来遇到类似问题的人,scipy与pytorch仿射变换的问题在于,scipy将变换应用于(0, 0, 0)周围,而pytorch则将其应用于图像/体积的中心。
例如,让我们看一下以下参数:
euler_angles = [ea0, ea1, ea2]
translation = [tr0, tr1, tr2]
scale = [sc0, sc1, sc2]

并创建以下转换矩阵:

# Rotation matrix
R_x(ea0, ea1, ea2) = np.array([[1, 0, 0, 0],
                              [0, math.cos(ea0), -math.sin(ea0), 0],
                              [0, math.sin(ea0), math.cos(ea0), 0],
                              [0, 0, 0, 1]])

R_y(ea0, ea1, ea2) = np.array([[math.cos(ea1), 0, math.sin(ea1), 0],
                               [0, 1, 0, 0],
                               [-math.sin(ea1), 0, math.cos(ea1)], 0],
                               [0, 0, 0, 1]])

R_z(ea0, ea1, ea2) = np.array([[math.cos(ea2), -math.sin(ea2), 0, 0],
                               [math.sin(ea2), math.cos(ea2), 0, 0],
                               [0, 0, 1, 0],
                               [0, 0, 0, 1]])

R = R_x.dot(R_y).dot(R_z)

# Translation matrix
T(tr0, tr1, tr2) = np.array([[1, 0, 0, -tr0],
                             [0, 1, 0, -tr1],
                             [0, 0, 1, -tr2],
                             [0, 0, 0, 1]])
# Scaling matrix
S(sc0, sc1, sc2) = np.array([[1/sc0, 0, 0, 0],
                             [0, 1/sc1, 0, 0],
                             [0, 0, 1/sc2, 0],
                             [0, 0, 0, 1]])

如果您有一个大小为(100,100,100)的体积,则在体积中心周围进行scipy变换需要先将体积中心移动到(0,0,0),然后在应用S、T和R之后将其移回到(50,50,50)。定义:
T_zero = np.array([[1, 0, 0, 50],
                   [0, 1, 0, 50],
                   [0, 0, 1, 50],
                   [0, 0, 0, 1]])

T_centre = np.array([[1, 0, 0, -50],
                     [0, 1, 0, -50],
                     [0, 0, 1, -50],
                     [0, 0, 0, 1]])

然后,scipy变换在中心周围进行:

transform_scipy_centre = T_zero.dot(T).dot(S).dot(R).T_centre

在PyTorch中,参数有一些细微的差别。翻译是在-1和1之间定义的,它们的顺序也不同。以相同的(100,100,100)体积为例,PyTorch中的平移参数如下:
# Note the order difference
translation_pytorch = =[tr0_p, tr1_p, tr2_p] = [tr0/50, tr2/50, tr1/50] 
T_p = T(tr0_p, tr1_p, tr2_p)

比例参数的顺序不同: scale_pytorch = [sc0_p, sc1_p, sc2_p] = [sc2, sc0, sc1]
S_p = S(sc0_p, sc1_p, sc2_p)

欧拉角是最大的差异。为了获得等效的变换,首先需要将参数取负并按不同的顺序排列:

# Note the order difference
euler_angles_pytorch = [ea0_p, ea1_p, ea2_p] = [-ea0, -ea2, -ea1]
R_x_p = R_x(ea0_p, ea1_p, ea2_p)
R_y_p = R_y(ea0_p, ea1_p, ea2_p)
R_z_p = R_z(ea0_p, ea1_p, ea2_p)

旋转矩阵计算的顺序也是不同的: #注意顺序差异 R_p = R_x_p.dot(R_z_p).dot(R_y_p)

考虑到所有这些因素,scipy变换为:

transform_scipy_centre = T_zero.dot(T).dot(S).dot(R).T_centre

等同于使用PyTorch transform的:

transform_pytorch = T_p.dot(S_p).dot(R_p)

希望这对你有所帮助!


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