如何计算透视变换与顺时针90度旋转后的点坐标
透视变换+顺时针90度旋转后的点坐标计算方案
需求背景
目标是将图像中的矩形对齐并转为横屏方向(宽度>高度)。原始矩形由四个顶点定义,部分场景下OpenCV会自动交换宽高,这时需要额外执行90度旋转,但旋转后可能出现矩形反向的情况。可以通过对比变换后左上(TL)和左下(BL)顶点的y坐标来修正这个问题。已知顶点映射关系(TL、TR、BR、BL),需要对原始顶点执行和图像相同的变换(仅透视变换,或透视变换+90度旋转),再通过y坐标判断是否需要180度修正。
核心问题
如何构建一个能表示「仅透视变换」或「透视变换+顺时针90度旋转」的组合变换矩阵,用来对已知相对位置的目标点执行坐标变换。
修正后的代码实现
import cv2 import numpy as np # 原始矩形参数(中心坐标、宽高、旋转角度) rectangle_area = [[270.2044677734375, 685.4646606445312], [417.27044677734375, 908.5230957031249], 1.0000191926956177] # 获取矩形四个顶点 rectangle = cv2.boxPoints(rectangle_area) rectangle = np.int0(rectangle) src_pts = rectangle.astype("float32") width = int(rectangle_area[1][0]) height = int(rectangle_area[1][1]) # 判断是否需要执行90度旋转(宽<高时转为横屏) rotate_90 = width < height # 定义透视变换的目标顶点(对应TL、TR、BR、BL) dst_pts = np.array([[0, height-1], [0, 0], [width-1, 0], [width-1, height-1]], dtype="float32") # 计算透视变换矩阵 M_perspective = cv2.getPerspectiveTransform(src_pts, dst_pts) # 先执行透视变换得到矫正后的图像 warped = cv2.warpPerspective(img, M_perspective, (width, height)) points_reprojection_matrix = None if rotate_90: # 对透视变换后的图像执行顺时针90度旋转 warped = cv2.rotate(warped, cv2.ROTATE_90_CLOCKWISE) h_rot, w_rot = warped.shape[:2] # 构建顺时针90度旋转的齐次变换矩阵(3x3) # 顺时针90度旋转的矩阵:[0, 1, 0; -1, 0, w; 0, 0, 1],w为旋转前的宽度 M_rotate = np.array([ [0, 1, 0], [-1, 0, width], [0, 0, 1] ], dtype=np.float32) # 组合变换矩阵:先透视再旋转,矩阵乘法顺序为旋转矩阵 × 透视矩阵 points_reprojection_matrix = M_rotate @ M_perspective else: # 不需要旋转,直接使用透视变换矩阵 points_reprojection_matrix = M_perspective # 计算原始顶点经过变换后的坐标 points_reprojected = [] for pnt in src_pts: # 转为齐次坐标 pnt_homogeneous = np.array([pnt[0], pnt[1], 1], dtype=np.float32) # 矩阵乘法计算变换后的齐次坐标 pnt_reprojected_hom = points_reprojection_matrix @ pnt_homogeneous # 转为非齐次坐标(除以第三个分量) pnt_reprojected = pnt_reprojected_hom[:2] / pnt_reprojected_hom[2] points_reprojected.append(pnt_reprojected) # 检查变换后左上顶点(索引0)和左下顶点(索引3)的y坐标,判断是否需要180度旋转修正 tl_y = points_reprojected[0][1] bl_y = points_reprojected[3][1] if tl_y > bl_y: # 构建180度旋转的齐次变换矩阵 target_w = w_rot if rotate_90 else width target_h = h_rot if rotate_90 else height M_rotate_180 = np.array([ [-1, 0, target_w], [0, -1, target_h], [0, 0, 1] ], dtype=np.float32) # 更新变换矩阵,加入180度旋转 points_reprojection_matrix = M_rotate_180 @ points_reprojection_matrix # 重新计算所有点的坐标 points_reprojected = [] for pnt in src_pts: pnt_homogeneous = np.array([pnt[0], pnt[1], 1], dtype=np.float32) pnt_reprojected_hom = points_reprojection_matrix @ pnt_homogeneous pnt_reprojected = pnt_reprojected_hom[:2] / pnt_reprojected_hom[2] points_reprojected.append(pnt_reprojected) # 同时修正图像 warped = cv2.rotate(warped, cv2.ROTATE_180)
关键说明
- 变换矩阵组合规则:图像变换顺序是先透视再旋转,对应矩阵乘法要按「旋转矩阵 × 透视矩阵」的顺序计算(矩阵乘法为右乘,变换顺序从右到左)。
- 齐次坐标处理:OpenCV的透视变换矩阵是3x3齐次矩阵,点需要转为
(x,y,1)的齐次坐标计算,最后需除以第三个分量得到实际(x,y)坐标。 - 90度旋转矩阵构建:顺时针90度旋转的齐次矩阵需考虑图像尺寸,旋转后原宽度变为高度,原高度变为宽度,矩阵中的偏移量要使用旋转前的宽度。
- 180度修正逻辑:对比变换后左上和左下顶点的y坐标,若左上y值更大,说明矩形反向,通过180度旋转修正。
内容的提问来源于stack exchange,提问作者Niko Gamulin
相关产品推荐
相关产品推荐

