使用cv2.getPerspectiveTransform出现异常输出的问题咨询
问题描述
尝试使用OpenCV计算透视变换,代码如下:
import numpy as np import cv2 original_pts = np.array([[745, 1233], [1505, 1581], [1502, 1212], [855, 1229]], dtype=np.float32) desired_pts = np.array([[46., 50.], [949., 490.], [949., 50.], [175., 50.]], dtype=np.float32) M = cv2.getPerspectiveTransform(original_pts, desired_pts) cv2.perspectiveTransform(np.float32(original_pts[None, :, :]), M)[0]
输出结果:
array([[ 46., 50.], [ 0., 0.], [ 949., 50.], [ 175., 50.]], dtype=np.float32)
其中第二个点的变换结果明显错误。为排查问题,自行实现了线性系统:
x = original_pts[:, 0] y = original_pts[:, 1] xt = desired_pts[:, 0] yt = desired_pts[:, 1] A = [] for i in range(4): equation = [x[i], y[i], 1, 0, 0, 0, 0, 0] equation += [0] * i equation += [-xt[i]] equation += [0] * (3 - i) A.append(equation) equation = [0, 0, 0, x[i], y[i], 1, 0, 0] equation += [0] * i equation += [-yt[i]] equation += [0] * (3 - i) A.append(equation) equation = [0, 0, 0, 0, 0, 0, x[i], y[i]] equation += [0] * i equation += [-1] equation += [0] * (3 - i) A.append(equation) A = np.array(A) b = np.array([0]*8 + [-1]*4) res = np.linalg.solve(A, b) my_M = np.array([[res[0], res[1], res[2]], [res[3], res[4], res[5]], [res[6], res[7], 1]]) cv2.perspectiveTransform(np.float32(original_pts[None, :, :]), my_M)[0]
得到的结果:
array([[ 47.653404, 51.79718 ], [ 975.5118 , 503.6889 ], [ 949. , 50. ], [ 175.0361 , 50.036106]], dtype=np.float32)
疑问:是使用cv2.getPerspectiveTransform的方式有误,还是该函数在某些场景下无法正常工作?认为自定义实现是该函数的非优化版本。
分析与解答
- 核心问题:目标点集退化
cv2.getPerspectiveTransform要求输入的两组对应点必须构成非退化的四边形(四个点无三点共线),因为透视变换的数学基础是求解一个8自由度的线性系统,只有当输入点集非退化时,系统才有唯一有效解。
你提供的desired_pts中有3个点的y坐标均为50,这三个点共线,属于退化的点集。这种情况下,线性系统会出现奇异矩阵,导致cv2.getPerspectiveTransform计算出的变换矩阵无效,最终出现第二个点变换结果为(0,0)的异常情况。
自定义实现的错误
你的自定义实现额外添加了第三个约束方程,强行对齐次坐标的w分量进行限制,这不符合透视变换的数学定义。透视变换中,齐次坐标的w分量是由变换矩阵和原始点共同决定的,不能人为约束。这种错误的约束在点集退化的场景下,反而得到了看似接近预期的结果,但该变换矩阵并不满足透视变换的基本要求,无法正确应用于其他点的变换。解决方法
- 修正
desired_pts,确保四个点构成非退化的四边形(无三点共线); - 保证
original_pts和desired_pts的点顺序完全匹配(比如均按顺时针/逆时针的顺序排列,对应四边形的同一顶点); - 若需要将原始四边形映射到一条直线附近的区域,建议使用仿射变换(
cv2.getAffineTransform),但仿射变换仅需要3组对应点,且要求点集共面。
内容的提问来源于stack exchange,提问作者TimWalter
相关产品推荐
相关产品推荐

