首页 > 编程问答 >OpenCV绘制轴功能的问题

OpenCV绘制轴功能的问题

时间:2024-07-30 16:50:46浏览次数:11  
标签:python opencv computer-vision camera-calibration aruco

我正在使用 OpenCV Contrib 4.10.0 版本检测 charuco 板。一旦我检测并估计了它的姿态,我就会尝试绘制轴;但是,如果我改变它们的长度,即使一切保持不变,我也可以看到不同的轴。我提供了示例图像、相机参数和要重现的代码。也看看附加的图像。

Different axis length plotted on one image

Different axis lengths

Example picture

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

# camera params after calibration
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)

# board info
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)

# detect markers and charuco corners
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, mtx2, dist2, None, None)

# start plotting
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)

# plot
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)

编辑:将示例图片更改为未裁剪的版本。


在使用 OpenCV 的 drawFrameAxes 函数时观察到的行为是由于函数的工作原理以及计算机图形中透视投影的本质造成的。让我解释一下:

  1. 透视投影: 当使用 drawFrameAxes 在 2D 图像上绘制 3D 轴时,OpenCV 会使用透视投影来创建深度错觉。在这种投影中,远离相机的物体看起来更小,即使它们在 3D 空间中大小相同。

  2. drawFrameAxes 的工作原理: 此函数获取相机姿态(旋转和平移)、相机矩阵、畸变系数和 轴长度 作为输入。然后,它沿着提供的相机姿态计算 3D 空间中表示轴端点的点的坐标。接下来,它将这些 3D 点投影到 2D 图像平面,并绘制连接原点和投影点的线。

现在,这就是问题所在:提供的 轴长度 用于在 3D 空间 中定义轴的端点。但是,由于透视投影,在 2D 图像平面 上投影的轴的长度将根据相机姿态和轴相对于相机的位置而变化。

在的示例中,当使用 axis_size = 0.1 时,在 3D 空间中绘制的轴实际上非常小。当投影到 2D 图像平面时,由于透视投影,它们看起来甚至更小,尤其是当相机距离棋盘较远时。

另一方面,当使用 axis_size = 5 时,在 3D 空间中绘制的轴要大得多。当投影到 2D 图像平面时,即使在透视投影之后,它们看起来也更大。

为了获得更一致的结果,可以在 3D 空间(世界坐标)中定义一个固定长度的轴,然后将其投影到图像上。 这可以通过以下方式完成:

import numpy as np
import cv2

# ...(现有的代码)...

# 定义世界坐标中轴的长度
axis_length_world = 0.1

# 定义 3D 空间中的轴端点
axis_ends_world = np.array(
    [
        [axis_length_world, 0, 0],
        [0, axis_length_world, 0],
        [0, 0, axis_length_world],
    ]
)

# 将旋转和平移向量转换为 4x4 变换矩阵
transformation_matrix = np.hstack((cv2.Rodrigues(rvec)[0], tvec))
transformation_matrix = np.vstack((transformation_matrix, np.array([0, 0, 0, 1])))

# 将轴端点从世界坐标转换为相机坐标
axis_ends_camera = transformation_matrix @ np.hstack(
    (axis_ends_world, np.ones((3, 1)))
).transpose()

# 将 3D 点投影到 2D 图像平面
projected_points, _ = cv2.projectPoints(
    axis_ends_camera[:3, :].transpose(), rvec, tvec, mtx, dist
)

# 在图像上绘制轴
cv2.line(
    img_rgb,
    tuple(map(int, projected_points[0][0])),
    tuple(map(int, projected_points[1][0])),
    (255, 0, 0),
    2,
)
cv2.line(
    img_rgb,
    tuple(map(int, projected_points[0][0])),
    tuple(map(int, projected_points[2][0])),
    (0, 255, 0),
    2,
)
cv2.line(
    img_rgb,
    tuple(map(int, projected_points[0][0])),
    tuple(map(int, projected_points[3][0])),
    (0, 0, 255),
    2,
)

# ...(现有的代码的其余部分)...

这将确保无论相机姿态和位置如何,轴在图像上的长度都保持一致。

标签:python,opencv,computer-vision,camera-calibration,aruco
From: 78808361

相关文章

  • Python - Creating alternative initializers using class Methods
    Classmethodsallowustodefinealternativeinitializers(alsoknownasfactorymethods)inaclass.Thesemethodshelpuscreateinstanceobjectsfromdifferenttypesofinputdata.Letusunderstandthiswiththehelpofanexample.Again,wetakethe......
  • 如何让 Python 请求信任自签名 SSL 证书?
    importrequestsdata={'foo':'bar'}url='https://foo.com/bar'r=requests.post(url,data=data)如果URL使用自签名证书,则会失败requests.exceptions.SSLError:[Errno1]_ssl.c:507:error:14090086:SSLroutines:SSL3_GET_SERVER_CERTIF......
  • python 偏函数
    如下代码loop=tornado.ioloop.IOLoop.current()ctx=contextvars.copy_context()func_call=functools.partial(ctx.run,func,*args,**kwargs)returnawaitloop.run_in_executor(executor,func_call)偏函数一个函数作为模板,通过提供部分参数来产生一个新的函数。......
  • Chapter 18 Python异常
    欢迎大家订阅【Python从入门到精通】专栏,一起探索Python的无限可能!文章目录前言一、什么是异常二、捕获异常三、异常的传递前言在Python中,异常是一种特定的对象,能够在程序运行过程中被抛出和处理。有效地管理异常不仅可以增强程序的稳定性,还可以提高用户体验,使程......
  • Python正则表达式匹配数字的第一次重复
    示例:For0123123123,1应匹配,因为第二个1出现在任何其他数字重复之前。For01234554321,5应该匹配,因为第二个5出现在任何其他数字的重复之前。我尝试过的一些正则表达式:......
  • 当 python 极坐标中某些列条目为空时,如何分解 List[_] 列?
    给定如下所示的Polarsdf,如何在两列上调用explode(),同时将空条目扩展到正确的长度以与其行匹配?shape:(3,2)┌───────────┬─────────────────────┐│x┆y││---┆---......
  • OpenCV实现图搜图简单案例
    一、概述使用OpenCV实现一个简单的图搜索的小功能特点:暴力匹配实现原理:1.将图片集合生成特征描述,并存入文件2.加载目标图像,并生成图像特征描述3.加载图像特征描述文件列表4.图像特征描述和集合中的特征描述列表进行匹配......
  • 使用python从网站自动下载pdf时出错
    我想从一个名为epadossier.nl的网站自动批量下载pdf我用谷歌搜索了这个并找到了一段代码并修复了一个小错误。所以现在我得到了这个`importrequestsurl="https://www.epadossier.nl/adres/plaats/straat/num"response=requests.get(url)ifresponse.status_cod......
  • 避免字符串连接的嵌套循环的 Pythonic 方法
    我想找到所有5位数字的字符串,其中前三位数字在我的第一个列表中,第二个槽第四个数字在我的第二个列表中,第三到第五个数字在我的最后一个列表中:l0=["123","567","451"]l1=["234","239","881"]l2=["348","551","399"......
  • Python 环境配置(二)安装jupyter、matplotlib、numpy库
    Python环境配置(二)安装jupyter、matplotlib、numpy库一、numpypipinstallnumpy二、matplotlibpipinstallmatplotlib三、jupyter1、anaconda自带Jupyter2、pycharm插件只有Pycharm的Professional版才支持JupyterNotebook,请注意版本3、新建文件#%......