scikit-image中映射坐标时,为何反转相似变换的旋转才有效?
问题描述
我有一组点,其中包含一个45-45-90直角三角形的3个顶点,还有一组需映射到这些顶点的点a,具体定义如下:
import numpy as np points = np.array([ ( 90, 416), (398, 390), (374, 84) ]) a = np.array([ (0, 1), # 映射到 (90, 416) (1, 1), # 映射到 (398, 390) (1, 0) # 映射到 (374, 84) ])
我希望找到能将a正确映射到points的相似变换(similarity transformation),于是使用skimage.transform执行如下操作:
from skimage import transform # 我认为合理的变换 T1 = transform.estimate_transform( ttype="similarity", src=a, dst=points ) # 无理由反转旋转,仅为验证有效性 T2 = transform.SimilarityTransform( scale=T1.scale, rotation=-T1.rotation, translation=T1.translation ) # 通过矩阵乘法应用变换 a_T1 = a @ T1.params[:2, :2] + T1.params[:2, 2] a_T2 = a @ T2.params[:2, :2] + T2.params[:2, 2]
为何仅反转T1的旋转得到的T2能产生更优的映射效果?或是我的实现存在错误?相关效果可参考附图。
问题分析与解答
核心问题出在相似变换的矩阵乘法顺序上:
skimage.transform.SimilarityTransform的变换逻辑是列向量左乘矩阵,公式为:
$$\begin{bmatrix}x'\y'\1\end{bmatrix} = T.params \cdot \begin{bmatrix}x\y\1\end{bmatrix}$$
但你在代码中使用的是行向量右乘矩阵:a @ T1.params[:2, :2] + T1.params[:2, 2],这和Skimage内置的变换逻辑完全相反。
由于旋转矩阵是正交矩阵(转置等于逆矩阵),行向量右乘旋转矩阵$R$,等价于列向量左乘$R^T$,也就是反向旋转。这就导致:
estimate_transform得到的T1是符合列向量左乘逻辑的正确变换;- 你用行向量右乘的方式应用T1时,相当于做了反向旋转+缩放+平移,结果自然不符合预期;
- 手动反转T1的旋转得到T2后,再用行向量右乘,刚好抵消了矩阵乘法顺序的错误,得到了正确的映射效果。
修正方案
有两种方式可以得到正确结果:
- 遵循Skimage原生逻辑,直接调用变换对象的函数:
a_T1_correct = T1(a)
- 保持行向量右乘的方式,但对变换矩阵做转置:
a_T1_correct = a @ T1.params[:2, :2].T + T1.params[:2, 2]
这样就无需手动反转旋转角,就能得到和T2一致的正确映射结果。
内容的提问来源于stack exchange,提问作者lanery
相关产品推荐
相关产品推荐

