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

OpenCV drawFrameAxes()函数轴长过长时出现畸变断裂问题

Charuco板位姿估计后drawFrameAxes轴长过大导致畸变/断裂问题

问题现象

使用OpenCV Contrib 4.10.0及opencv-python 4.7.0.72完成Charuco板检测与位姿估计后,调用cv2.drawFrameAxes()绘制坐标轴时,当轴长设置过大(例如设为10),轴线超出图像范围后会出现以下异常:

  • 轴线发生畸变,与预期坐标轴方向不重合
  • 出现线条断裂、仅保留轮廓的情况
  • 不同轴长下显示的坐标轴形态不一致
    该问题在模拟图像与真实采集图像中均存在。

复现代码及相机参数

import os
import numpy as np
import matplotlib.pyplot as plt
import cv2

# 标定后的相机内参
mtx = np.array([[3341.0930071 ,    0.        , 2020.04757797],
   [   0.        , 3332.54238818, 1108.10670945],
   [   0.        ,    0.        ,    1.        ]])
   
# 畸变系数
dist = np.array([-0.10474984,  0.04565679,  0.00948721,  0.        ,  0.        ])

img_rgb = cv2.imread("some img")
img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
img = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)

# Charuco板配置
dictionary = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_100)
board = cv2.aruco.CharucoBoard((7, 5), 0.055, 0.041, dictionary)

params = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(dictionary, params)

# 检测标记点与Charuco角点
marker_corners, marker_ids, rejectedCandidates = detector.detectMarkers(img)
ret, charucoCorners, charucoIds = cv2.aruco.interpolateCornersCharuco(marker_corners, marker_ids, img, board)

# 位姿估计
ret, rvec, tvec = cv2.aruco.estimatePoseCharucoBoard(charucoCorners, charucoIds, board, mtx, dist, None, None)

# 绘制不同轴长的坐标轴
axis_size = 0.1
result_short = cv2.drawFrameAxes(np.copy(img_rgb), mtx, dist, rvec, tvec, axis_size)

axis_size = 5
result_long = cv2.drawFrameAxes(np.copy(img_rgb), mtx, dist, rvec, tvec, axis_size)

# 可视化对比
plt.subplot(1,2,1)
plt.imshow(result_short)
plt.subplot(1,2,2)
plt.imshow(result_long)
plt.show()

# 叠加绘制不同轴长
result_both = cv2.drawFrameAxes(np.copy(img_rgb), mtx, dist, rvec, tvec, 0.2)
result_both = cv2.drawFrameAxes(result_both, mtx, dist, rvec, tvec, 2)

原因分析

drawFrameAxes()的核心逻辑是:先在3D空间生成坐标轴端点(原点及三个轴的末端点,长度由axis_size指定),再通过相机内参和畸变参数将这些3D点投影到图像平面。当轴长过大时,部分3D端点会落在相机视锥体之外,投影后的像素坐标会出现异常(如超出图像尺寸范围、产生不符合透视规律的畸变坐标),最终导致绘制的轴线错乱、断裂。

此外,OpenCV的投影函数不会自动裁剪视锥体外的点到图像范围内,而是直接绘制这些异常投影点,进而引发视觉上的形态不一致问题。

解决方案

1. 合理设置轴长

axis_size的单位与Charuco板的方格尺寸单位一致(示例中为米),需根据相机与Charuco板的实际距离设置合理值,确保坐标轴端点投影后落在图像范围内。比如示例中Charuco方格边长为0.055米,轴长设为0.1-0.5米(对应真实世界10-50厘米)是比较合理的,避免设置远大于场景尺度的值(如5、10米)。

2. 手动裁剪投影点后自定义绘制

如果确实需要绘制较长的坐标轴,可以手动生成3D坐标轴点,投影后裁剪到图像范围内,再自行绘制轴线,避免异常点干扰:

def draw_custom_frame_axes(img, mtx, dist, rvec, tvec, axis_length, img_size):
    # 定义3D坐标轴点:原点 + X/Y/Z轴端点
    axis_3d = np.float32([[0,0,0], [axis_length,0,0], [0,axis_length,0], [0,0,axis_length]]).reshape(-1,3)
    # 将3D点投影到图像平面
    axis_2d, _ = cv2.projectPoints(axis_3d, rvec, tvec, mtx, dist)
    axis_2d = np.int32(axis_2d).reshape(-1,2)
    
    # 裁剪坐标到图像有效范围内
    h, w = img_size
    def clip_point(p):
        return (max(0, min(p[0], w-1)), max(0, min(p[1], h-1)))
    
    origin = clip_point(axis_2d[0])
    x_end = clip_point(axis_2d[1])
    y_end = clip_point(axis_2d[2])
    z_end = clip_point(axis_2d[3])
    
    # 绘制轴线(X红、Y绿、Z蓝)
    cv2.line(img, origin, x_end, (0,0,255), 3)
    cv2.line(img, origin, y_end, (0,255,0), 3)
    cv2.line(img, origin, z_end, (255,0,0), 3)
    return img

# 使用示例
h, w = img_rgb.shape[:2]
result_custom = draw_custom_frame_axes(np.copy(img_rgb), mtx, dist, rvec, tvec, 5, (h,w))
plt.imshow(cv2.cvtColor(result_custom, cv2.COLOR_BGR2RGB))
plt.show()

3. 升级OpenCV版本

部分旧版本OpenCV的drawFrameAxes存在视锥体外点处理的bug,尝试升级到最新稳定版(如4.15.x或更高),可能已修复该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:54:53