从仿射变换矩阵生成 pytorch 的 theta

Fel*_*ser 4 python affinetransform pytorch

我正在尝试使用 pytorch 在 3D 体积上执行刚性+缩放变换,但我似乎无法理解 torch.nn.function.affine_grid 所需的 theta 是如何工作的。

我有一个大小为 (1,4,4) 的变换矩阵,是通过矩阵平移 * 缩放 * 旋转相乘生成的。例如,如果我在 scipy.ndimage.affine_transform 中使用此矩阵,它可以正常工作。然而,相同的矩阵(裁剪为大小(1,3,4))在torch.nn.function.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]]
Run Code Online (Sandbox Code Playgroud)

如何将 (1,4,4) 仿射矩阵转换为与 torch.nn.function.affine_grid 相同的工作方式?或者,有没有办法根据变换参数(移位、欧拉角、缩放)生成正确的矩阵?

Fel*_*ser 5

对于将来遇到类似问题的任何人来说,scipy 与 pytorch 仿射变换的问题在于 scipy 在 (0, 0, 0) 周围应用变换,而 pytorch 在图像/体积的中间应用变换。

\n\n

例如,我们采用参数:

\n\n
euler_angles = [ea0, ea1, ea2]\ntranslation = [tr0, tr1, tr2]\nscale = [sc0, sc1, sc2]\n
Run Code Online (Sandbox Code Playgroud)\n\n

并创建以下变换矩阵:

\n\n
# Rotation matrix\nR_x(ea0, ea1, ea2) = np.array([[1, 0, 0, 0],\n                              [0, math.cos(ea0), -math.sin(ea0), 0],\n                              [0, math.sin(ea0), math.cos(ea0), 0],\n                              [0, 0, 0, 1]])\n\nR_y(ea0, ea1, ea2) = np.array([[math.cos(ea1), 0, math.sin(ea1), 0],\n                               [0, 1, 0, 0],\n                               [-math.sin(ea1), 0, math.cos(ea1)], 0],\n                               [0, 0, 0, 1]])\n\nR_z(ea0, ea1, ea2) = np.array([[math.cos(ea2), -math.sin(ea2), 0, 0],\n                               [math.sin(ea2), math.cos(ea2), 0, 0],\n                               [0, 0, 1, 0],\n                               [0, 0, 0, 1]])\n\nR = R_x.dot(R_y).dot(R_z)\n\n# Translation matrix\nT(tr0, tr1, tr2) = np.array([[1, 0, 0, -tr0],\n                             [0, 1, 0, -tr1],\n                             [0, 0, 1, -tr2],\n                             [0, 0, 0, 1]])\n# Scaling matrix\nS(sc0, sc1, sc2) = np.array([[1/sc0, 0, 0, 0],\n                             [0, 1/sc1, 0, 0],\n                             [0, 0, 1/sc2, 0],\n                             [0, 0, 0, 1]])\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你有一个大小为 (100, 100, 100) 的体积,围绕体积中心的 scipy 变换需要首先将体积中心移动到 (0, 0, 0),然后将其移回到 (50, 50, 50) 应用 S、T 和 R 后。定义:

\n\n
T_zero = np.array([[1, 0, 0, 50],\n                   [0, 1, 0, 50],\n                   [0, 0, 1, 50],\n                   [0, 0, 0, 1]])\n\nT_centre = np.array([[1, 0, 0, -50],\n                     [0, 1, 0, -50],\n                     [0, 0, 1, -50],\n                     [0, 0, 0, 1]])\n
Run Code Online (Sandbox Code Playgroud)\n\n

围绕中心的 scipy 变换为:

\n\n
transform_scipy_centre = T_zero.dot(T).dot(S).dot(R).T_centre\n
Run Code Online (Sandbox Code Playgroud)\n\n

在pytorch中,参数有一些细微的差异。

\n\n

平移定义在-1和1之间。它们的顺序也不同。以相同(100,100,100)体积为例,pytorch中的平移参数由下式给出:

\n\n
# Note the order difference\ntranslation_pytorch = =[tr0_p, tr1_p, tr2_p] = [tr0/50, tr2/50, tr1/50] \nT_p = T(tr0_p, tr1_p, tr2_p)\n
Run Code Online (Sandbox Code Playgroud)\n\n

缩放参数的顺序不同:\n scale_pytorch = [sc0_p, sc1_p, sc2_p] = [sc2, sc0, sc1]
\n S_p = S(sc0_p, sc1_p, sc2_p)

\n\n

欧拉角是最大的区别。要获得等效变换,首先参数为负且顺序不同:

\n\n
# Note the order difference\neuler_angles_pytorch = [ea0_p, ea1_p, ea2_p] = [-ea0, -ea2, -ea1]\nR_x_p = R_x(ea0_p, ea1_p, ea2_p)\nR_y_p = R_y(ea0_p, ea1_p, ea2_p)\nR_z_p = R_z(ea0_p, ea1_p, ea2_p)\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
transform_scipy_centre = T_zero.dot(T).dot(S).dot(R).T_centre\n
Run Code Online (Sandbox Code Playgroud)\n\n

相当于 pytorch 变换:

\n\n
transform_pytorch = T_p.dot(S_p).dot(R_p)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我希望这有帮助!

\n