You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python图像与ROI旋转异常求助:偏心对象ROI匹配失败

问题原因与解决方案

你的核心问题是:在旋转后的图像上选取的ROI坐标,不能直接通过围绕图像中心逆旋转的方式映射回原图——因为OpenCV旋转偏心物体时,物体会产生平移,你当前的变换逻辑没有对应上这个平移量。

错误根源

OpenCV的rotate_image函数是围绕图像中心旋转整个图像,偏心物体旋转后,其在图像中的位置会发生平移。你现在直接对旋转后图像上的ROI矩形做逆旋转,相当于把这个矩形当作是围绕图像中心旋转后的结果,但实际上这个ROI的位置是旋转+平移后的最终位置,必须用逆仿射变换来还原坐标,而不是直接旋转矩形。

另外,ax.transData是Matplotlib用来将数据坐标(图像像素)转换为屏幕坐标的变换,和OpenCV的像素级几何变换逻辑不兼容,用它来做ROI坐标还原会出错。

修正方案

用OpenCV的逆仿射变换,把旋转后图像上的ROI坐标映射回原图:

  1. 先获取旋转矩阵的逆矩阵,用于还原坐标
  2. 将ROI的顶点坐标通过逆矩阵变换,得到原图中的对应位置
  3. 用变换后的坐标在原图上绘制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()

关键修正点说明

  1. 返回旋转矩阵:修改rotate_image函数,返回OpenCV生成的旋转矩阵,方便后续计算逆矩阵。
  2. 逆仿射变换:通过cv2.invertAffineTransform得到逆矩阵,将旋转后的ROI坐标还原为原图坐标,完全对齐OpenCV的旋转逻辑。
  3. 多边形绘制:因为旋转后的矩形逆变换回原图后,不再是轴对齐的矩形,所以用Polygon绘制更准确。

内容的提问来源于stack exchange,提问作者Mads Søndergaard Larsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:35:58