我有一组点,它们实际上是 45-45-90 直角三角形的 3 个顶点,还有一些其他点,
a
,应该映射到它们。
import numpy as np
points = np.array([
( 90, 416),
(398, 390),
(374, 84)
])
a = np.array([
(0, 1), # maps to (90, 416)
(1, 1), # maps to (398, 390)
(1, 0) # maps to (374, 84)
])
我想找到将 a
正确映射到
points
的 相似度变换。
from skimage import transform
# transformation that makes sense to me
T1 = transform.estimate_transform(
ttype="similarity",
src=a,
dst=points
)
# invert the rotation for no reason
# other than to show that it works
T2 = transform.SimilarityTransform(
scale=T1.scale,
rotation=-T1.rotation,
translation=T1.translation
)
# apply transformations via matrix multiplication
a_T1 = a @ T1.params[:2, :2] + T1.params[:2, 2]
a_T2 = a @ T2.params[:2, :2] + T2.params[:2, 2]
为什么
T2
(除了我最终发现它有效之外,我只是没有任何真正原因地反转了旋转)会产生更好的映射?或者我在实施中犯了一个愚蠢的错误?
不是你。关于变换矩阵有很多不同的约定,而且碰巧
(M @ x.T).T
作为约定T2.params[:2, :2]
等于T1.params[:2, :2].T
(如果有偏差,这通常不是正确的。)但是,我个人总是对这里的惯例感到困惑。为了弄清楚这一点,我必须知道 skimage 变换有一个
__call__
方法,所以你可以用 T1(a)
应用它们,这会给出正确的结果。所以现在只需查看源代码即可看到它调用了 _apply_mat
,如下所示:
def _apply_mat(self, coords, matrix):
ndim = matrix.shape[0] - 1
coords = np.array(coords, copy=False, ndmin=2)
src = np.concatenate([coords, np.ones((coords.shape[0], 1))], axis=1)
dst = src @ matrix.T
# below, we will divide by the last dimension of the homogeneous
# coordinate matrix. In order to avoid division by zero,
# we replace exact zeros in this column with a very small number.
dst[dst[:, ndim] == 0, ndim] = np.finfo(float).eps
# rescale to homogeneous coordinates
dst[:, :ndim] /= dst[:, ndim : ndim + 1]
return dst[:, :ndim]
对于您的变换(仿射),这本质上相当于
(T1.params[:2, :2] @ a.T).T + T1.params[:2, 2]
,它等于 a @ T1.params[:2, :2].T + T1.params[:2, 2]
,同样,您正在计算的内容的转置并等于 a @ T2.params[:2, :2] + T2.params[:2, 2]
。
尽管存在多种约定令人困惑,但我希望这能澄清为什么事情看起来很奇怪!