如何基于无序矩形顶点实现物体正确横屏旋转?
解决矩形物体横屏校正中的180度翻转问题
问题背景
训练的模型可检测矩形物体及其角点,目标是将物体调整为横屏方向,但现有逻辑存在错误,偶尔会导致物体180度翻转。核心问题是主物体的vertices顶点顺序不固定(非始终为顺时针),导致竖屏物体的旋转逻辑失效。
原实现代码
def save_poly_crop(vertices, img, vertex_top_left, vertex_top_right): img = np.array(img) # RGB to BGR # img = img[:, :, ::-1].copy() tag_area = list(cv2.minAreaRect(vertices)) tag_area[0] = list(tag_area[0]) tag_area[1] = list(tag_area[1]) # get width and height of the detected rectangle # THIS IS FLIPPED: width = int(tag_area[1][0]) height = int(tag_area[1][1]) rotate_90_ccw = False rotate_90 = False if width < height: if vertex_top_left is not None and vertex_top_right is not None: if vertex_top_left[1] > vertex_top_right[1]: rotate_90 = True else: rotate_90_ccw = True else: rotate_90 = True box = cv2.boxPoints(tag_area) box = np.int0(box) src_pts = box.astype("float32") # coordinate of the points in box points after the rectangle has been # straightened dst_pts = np.array([[0, height-1], [0, 0], [width-1, 0], [width-1, height-1]], dtype="float32") # the perspective transformation matrix M = cv2.getPerspectiveTransform(src_pts, dst_pts) # directly warp the rotated rectangle to get the straightened rectangle warped = cv2.warpPerspective(img, M, (width, height)) if rotate_90: warped = cv2.rotate(warped, cv2.ROTATE_90_CLOCKWISE) if rotate_90_ccw: warped = cv2.rotate(warped, cv2.ROTATE_90_COUNTERCLOCKWISE) return warped
失效案例
正常顶点顺序(逻辑生效)
vertices = array([[ 55, 1006], [ 67, 357], [ 484, 364], [ 473, 1013]]) rectangle_top_left = array([[ -38, 1005], [ -38, 893], [ 73, 893], [ 73, 1005]]) vertex_top_left = cv2.minAreaRect(rectangle_top_left)[0] rectangle_top_right = array([[-20, 464], [-18, 352], [ 93, 354], [ 91, 466]]) vertex_top_right = cv2.minAreaRect(rectangle_top_right)[0]
旋转失效的顶点顺序
vertices = array([[ 706, 751], [ 866, 726], [ 907, 986], [ 747, 1011]]) rectangle_top_left = array([[837, 707], [889, 699], [897, 752], [845, 759]]) vertex_top_left = cv2.minAreaRect(rectangle_top_left)[0] rectangle_top_right = array([[ 870, 967], [ 922, 960], [ 930, 1012], [ 877, 1020]]) vertex_top_right = cv2.minAreaRect(rectangle_top_right)[0]
注:vertex_top_right和vertex_top_left只需接近对应角点,无需与主物体顶点完全重合。
修复后的代码
def save_poly_crop(vertices, img, vertex_top_left, vertex_top_right): img = np.array(img) # 获取最小外接矩形,统一处理顶点顺序 tag_area = cv2.minAreaRect(vertices) box = cv2.boxPoints(tag_area) box = np.int0(box) # 对box顶点按顺时针排序,消除原始顶点顺序影响 def sort_clockwise(points): # 计算重心 center = np.mean(points, axis=0) # 计算每个点相对于重心的角度 angles = np.arctan2(points[:, 1] - center[1], points[:, 0] - center[0]) # 按角度排序(顺时针) sorted_indices = np.argsort(angles) return points[sorted_indices] sorted_box = sort_clockwise(box) src_pts = sorted_box.astype("float32") # 获取矩形的真实宽高,确保width为较长边 width = int(tag_area[1][0]) height = int(tag_area[1][1]) if width < height: width, height = height, width # 目标点:横屏状态下的顺时针顶点(左上、右上、右下、左下) dst_pts = np.array([[0, 0], [width-1, 0], [width-1, height-1], [0, height-1]], dtype="float32") # 计算透视变换矩阵并执行变换 M = cv2.getPerspectiveTransform(src_pts, dst_pts) warped = cv2.warpPerspective(img, M, (width, height)) # 利用检测到的两个上角点校正180度翻转问题 if vertex_top_left is not None and vertex_top_right is not None: # 将角点映射到变换后的坐标系 corner_pts = np.array([vertex_top_left, vertex_top_right], dtype="float32") transformed_corners = cv2.perspectiveTransform(corner_pts.reshape(-1, 1, 2), M).reshape(-1, 2) # 横屏状态下左上角x应小于右上角x,否则执行180度翻转 if transformed_corners[0][0] > transformed_corners[1][0]: warped = cv2.rotate(warped, cv2.ROTATE_180) return warped
关键修改说明
- 统一顶点顺序:新增
sort_clockwise函数,通过重心和角度计算对矩形顶点进行顺时针排序,彻底消除原始顶点顺序混乱的影响。 - 固定宽高定义:强制
width为矩形的较长边,确保变换后基础图像是横屏方向的候选。 - 基于角点的方向校正:将检测到的两个上角点通过透视变换映射到新坐标系,判断其相对位置,若顺序颠倒则执行180度翻转,保证最终图像方向正确。
内容的提问来源于stack exchange,提问作者Niko Gamulin
相关产品推荐
相关产品推荐

