如何用数学方法将4个2D点转换到坐标轴约束空间?
四边形坐标转换的矩阵求解问题
本人并非数学专家,若术语表述有误请见谅。
在Python应用中,输入坐标空间为一个已将质心移至原点的四边形,需将其转换为坐标轴约束的目标空间:原4个点(ax, ay)、(bx, by)、(cx, cy)、(dx, dy)要对应转换为(1, 0)、(0, 1)、(-1, 0)、(0, -1)。
最初计划用矩阵变换公式A_T = T * A,想通过T = A_T * inverse(A)求解变换矩阵T,但A不是方阵,没法计算逆矩阵。后来参考方法写了Python代码,为解决A矩阵奇异的问题,把A最后一行最后一个元素设为0.9999999,此时T@A能得到正确的A_T,但单个点转换时结果出错,需要解决这个问题。
原代码
import numpy as np x_all = [x - x_center for x in x_all] y_all = [y - y_center for y in y_all] # All x coordinates x_all = [258.75, -401.25, -741.25, 883.75] # All y coordinates y_all = [156.25, 171.25, -188.75, -138.75] # original coordinate matrix A = np.array([x_all, y_all, [1, 1, 1, 1], [1, 1, 1, .9999999]]) # transformed coordinate matrix A_T = np.array([[1, 0, -1, 0], [0, 1, 0, -1], [1, 1, 1, 1], [1, 1, 1, 1]]) # Solve for T where A_T = T @ A T_1 = np.linalg.solve(A.transpose(), A_T.transpose()[:, 0]) T_2 = np.linalg.solve(A.transpose(), A_T.transpose()[:, 1]) T_3 = np.linalg.solve(A.transpose(), A_T.transpose()[:, 2]) T_4 = np.linalg.solve(A.transpose(), A_T.transpose()[:, 3]) T = np.array([T_1, T_2, T_3, T_4]) # Check to see if T @ A = A_T print((T @ A).round(decimals=3)) # Multiply each original coordinate pair of the original coordinate space to see if they are correctly transformed for i in range(4): print((T @ np.matrix([[x_all[i]], [y_all[i]], [1], [1]])).round(decimals=3))
代码运行结果
[[1.] [0.] [1.] [1.]] [[-0.] [ 1.] [ 1.] [ 1.]] [[-1.] [ 0.] [ 1.] [ 1.]] [[ 1.577] [-2.104] [ 1. ] [ 1. ]]
问题分析与解决方法
问题根源
修改A最后一行最后一个元素为0.9999999,本质是人为破坏了矩阵的一致性——原矩阵A的最后两行原本都是齐次坐标的1,修改后导致变换矩阵T是基于这个“不一致”的A求解出来的。当用标准齐次坐标[x,y,1,1]去乘T时,自然和求解时的条件不匹配,最后一个点就出错了。
正确解法:用最小二乘法求解最优变换矩阵
原问题是4个点对应4个目标点,属于超定方程组场景,不需要强行把A改成方阵,直接用最小二乘法求最优解即可,既不会破坏齐次坐标一致性,也能避免矩阵奇异问题。
修改后的代码:
import numpy as np # 原始坐标(已移至质心原点) x_all = [258.75, -401.25, -741.25, 883.75] y_all = [156.25, 171.25, -188.75, -138.75] # 构造原始点的齐次坐标矩阵(每行一个点,形式为 [x, y, 1]) A = np.array([x_all, y_all, [1]*4]).T # 形状为(4,3) # 目标点的齐次坐标矩阵(每行一个点) A_T = np.array([[1,0,1], [0,1,1], [-1,0,1], [0,-1,1]]) # 形状为(4,3) # 用最小二乘法求解变换矩阵T:A_T = A @ T.T T = np.linalg.lstsq(A, A_T, rcond=None)[0].T # T形状为(3,3) print("变换矩阵T:") print(T.round(decimals=6)) # 验证所有点的转换结果 print("\n转换结果:") for x, y in zip(x_all, y_all): point = np.array([x, y, 1]) transformed = T @ point print(f"({x:.2f}, {y:.2f}) → ({transformed[0]:.3f}, {transformed[1]:.3f})")
说明
- 2D空间的仿射变换用3x3矩阵足够,不需要扩展到4x4矩阵,之前的4x4矩阵冗余且引入了奇异问题。
- 最小二乘法会自动处理超定方程组,找到最接近所有点约束的最优变换矩阵,避免人为修改矩阵元素带来的不一致性。
- 转换后的结果会非常接近目标点,误差在浮点精度范围内。
内容的提问来源于stack exchange,提问作者Tubal Cayenne
相关产品推荐
相关产品推荐

