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

利用单应性将椭圆投影为圆时的OpenCV图像投影异常问题

表盘椭圆转圆的单应性投影问题

我是计算机视觉领域新手,正尝试从图像中提取表盘。已成功为表盘拟合椭圆,希望通过修正视角将椭圆投影为圆,使表盘正对相机。经了解,该任务通常使用homography(单应性)实现。

按照OpenCV单应性教程操作后,发现源图像与目标匹配效果极差。查阅相关问题得知,透视投影的圆并非严格意义上的椭圆,无法实现完美投影,但即使是简单案例,投影结果仍不理想。

已标注4张输入图像的椭圆(红色)、单位圆(蓝色)、源点(黄色)和目标点(青色),应用OpenCV(及Scikit)计算的单应性后,椭圆未成功投影到单位圆上,增加计算单应性的点数量也无改善。

使用的代码

def persp_transform(orig):
    img = orig.copy()

    ellipses = find_ellipses(img)
    ellipse = ellipses[0] # Use the largest one

    center_x = ellipse[0]
    center_y = ellipse[1]
    center = np.array([center_x, center_y])
    width = ellipse[2]
    height = ellipse[3]
    angle = ellipse[4]
    minor_semiaxis = min(width, height)/2
    major_semiaxis = max(width, height)/2

    # Translate the image to center the ellipse
    img_center = np.array([img.shape[0]//2, img.shape[1]//2])
    translation = center - img_center
    M = np.float32([[1, 0, -translation[1]], [0, 1, -translation[0]]])
    img = cv.warpAffine(img, M, (img.shape[1], img.shape[0]))

    # Draw ellipse before projection
    rr, cc = ellipse_perimeter(img_center[0], img_center[1], int(major_semiaxis), int(minor_semiaxis), orientation=angle, shape=img.shape)
    draw_ellipse(img, rr, cc, color=0, thickness=10)
    
    def sin(a): return np.sin(np.deg2rad(a))
    def cos(a): return np.cos(np.deg2rad(a))
    
    # Source points around the ellipse
    num_points = 5
    rotation = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])
    from_points = np.array([[major_semiaxis*sin(a), minor_semiaxis*cos(a)] for a in np.linspace(0, 360, num_points)]) @ rotation + img_center
    from_points = from_points.astype(np.float32)

    # Destination points around a centered circle
    to_radius = int((min(img.shape[:2]) / 2) * 0.8)
    to_points = np.array([[to_radius*sin(a), to_radius*cos(a)] for a in np.linspace(0, 360, num_points)]) @ rotation + img_center
    to_points = to_points.astype(np.float32)

    # Draw ellipse center and source points before projection
    cv.circle(img, (img_center[1], img_center[0]), 20, (255, 255, 0), -1)
    for fp in from_points:
        cv.circle(img, (int(fp[1]), int(fp[0])), 20, (255, 255, 0), -1)
    
    # Compute homography and project
    M, _ = cv.findHomography(from_points, to_points, cv.RANSAC, 5.0)
    img = cv.warpPerspective(img, M, (img.shape[1], img.shape[0]))

    # Draw target unit circle and destination points after projection
    cv.circle(img, (img_center[1], img_center[0]), to_radius, (0, 0, 255), 20)
    cv.circle(img, (img_center[1], img_center[0]), 20, (0, 255, 255), -1)
    for tp in to_points:
        cv.circle(img, (int(tp[1]), int(tp[0])), 20, (0, 255, 255), -1)

    return img

编辑1

补充4张原始输入图像,椭圆拟合采用AAMED方法,代码如下:

def find_ellipses(orig, scale=0.3, theta_fsa=20, length_fsa=5.4, T_val=0.9):
    img = cv.resize(orig.copy(), None, fx=scale, fy=scale)
    gray_image = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    aamed = AAMED(img.shape[0]+1, img.shape[1]+1)
    aamed.setParameters(np.deg2rad(theta_fsa), length_fsa, T_val)
    ellipses = aamed.run_AAMED(gray_image)
    aamed.release()

    ellipses = sorted(ellipses, key=lambda e: ellipse_area(e), reverse=True)
    ellipses = [np.array(e) / scale for e in ellipses]

    return ellipses

随后使用skimage.draw.ellipse_perimeter(...)在图像上绘制最大的椭圆。

编辑2

进一步排查发现问题可能出在cv.warpPerspective(...):

  • 已对各点对进行颜色编码,确认映射关系正确
  • 使用cv.perspectiveTransform(...)单独投影源点时,结果正常
  • 但使用cv.warpPerspective(...)投影图像时,结果与点投影不一致

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 22:39:51