Python图像与ROI旋转异常求助:偏心对象ROI匹配失败
问题原因与解决方案
你的核心问题是:在旋转后的图像上选取的ROI坐标,不能直接通过围绕图像中心逆旋转的方式映射回原图——因为OpenCV旋转偏心物体时,物体会产生平移,你当前的变换逻辑没有对应上这个平移量。
错误根源
OpenCV的rotate_image函数是围绕图像中心旋转整个图像,偏心物体旋转后,其在图像中的位置会发生平移。你现在直接对旋转后图像上的ROI矩形做逆旋转,相当于把这个矩形当作是围绕图像中心旋转后的结果,但实际上这个ROI的位置是旋转+平移后的最终位置,必须用逆仿射变换来还原坐标,而不是直接旋转矩形。
另外,ax.transData是Matplotlib用来将数据坐标(图像像素)转换为屏幕坐标的变换,和OpenCV的像素级几何变换逻辑不兼容,用它来做ROI坐标还原会出错。
修正方案
用OpenCV的逆仿射变换,把旋转后图像上的ROI坐标映射回原图:
- 先获取旋转矩阵的逆矩阵,用于还原坐标
- 将ROI的顶点坐标通过逆矩阵变换,得到原图中的对应位置
- 用变换后的坐标在原图上绘制ROI
修正后的完整代码
import matplotlib.pyplot as plt import numpy as np import cv2 def rotate_image(image, angle): image_center = tuple(np.array(image.shape[1::-1]) / 2) rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0) result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR) return result, rot_mat # 新增返回旋转矩阵 def crop_image(Message, image): coordinates = cv2.selectROI(Message, image) if coordinates != (0,0,0,0): # 修正判断逻辑,避免空元组误判 print("\nSuccess! ROI has been selected.") cv2.destroyAllWindows() r1, c1 = int(coordinates[1]), int(coordinates[0]) # 左上角行、列 r2, c2 = int(coordinates[1]+coordinates[3]), int(coordinates[0]+coordinates[2]) # 右下角行、列 Matrix = [[r1, c1], [r2, c2]] cropped_img = image[r1:r2, c1:c2] return cropped_img, Matrix plt.close("all") h = 500 w = 1000 cx = int(w/2) cy = int(h/2) rot_angle = -45 W_Rec = 300 H_Rec = 100 # 故意让矩形偏心,测试效果 upper_left_point = (cx - int(W_Rec/2) + 150, cy - int(H_Rec/2)) # 向右偏移150 lower_right_point = (cx + int(W_Rec/2) + 150, cy + int(H_Rec/2)) img = np.ones((h, w), dtype=np.uint8) rec_cv2 = cv2.rectangle(img, pt1=upper_left_point, pt2=lower_right_point, color=255, thickness=-1) fig, axs = plt.subplots(ncols=3, tight_layout=True) axs[0].imshow(img) axs[0].set_title("Original") # 获取旋转后的图像和旋转矩阵 im_rot, rot_mat = rotate_image(img, rot_angle) axs[1].imshow(im_rot) _, Matrix = crop_image("Define Matrix", im_rot) c0, c1 = Matrix[0][1], Matrix[1][1] r0, r1 = Matrix[0][0], Matrix[1][0] # 在旋转后的图像上绘制选中的ROI rec = plt.Rectangle((c0, r0), (c1 - c0), (r1 - r0), fill=False, color="w", ls="dashed") axs[1].add_patch(rec) axs[1].set_title("Rotated with patch") # --- 核心修正:用逆变换还原ROI坐标到原图 --- # 计算逆旋转矩阵 inv_rot_mat = cv2.invertAffineTransform(rot_mat) # 准备ROI的四个顶点坐标(旋转后图像中的坐标) roi_points = np.array([ [c0, r0], # 左上角 [c1, r0], # 右上角 [c1, r1], # 右下角 [c0, r1] # 左下角 ], dtype=np.float32) # 应用逆变换,得到原图中的坐标 original_roi_points = cv2.transform(np.array([roi_points]), inv_rot_mat)[0] # 转换为整数坐标 original_roi_points = original_roi_points.astype(int) # 在原图上绘制变换后的ROI axs[2].imshow(img) # 用多边形绘制(因为旋转后矩形可能不再轴对齐) from matplotlib.patches import Polygon rec_rot = Polygon(original_roi_points, fill=False, color="w", ls="dashed") axs[2].add_patch(rec_rot) axs[2].set_title("Original w/ matched ROI") for n in range(3): axs[n].axis("off") plt.show()
关键修正点说明
- 返回旋转矩阵:修改
rotate_image函数,返回OpenCV生成的旋转矩阵,方便后续计算逆矩阵。 - 逆仿射变换:通过
cv2.invertAffineTransform得到逆矩阵,将旋转后的ROI坐标还原为原图坐标,完全对齐OpenCV的旋转逻辑。 - 多边形绘制:因为旋转后的矩形逆变换回原图后,不再是轴对齐的矩形,所以用
Polygon绘制更准确。
内容的提问来源于stack exchange,提问作者Mads Søndergaard Larsen
相关产品推荐
相关产品推荐

