Carla自动驾驶仿真五:opencv绘制运动车辆的boudingbox(代码详解)

news2024/11/17 21:33:05

文章目录

  • 一、安装opencv
  • 二、opencv绘制车辆的boudingbox
      • 1、构造相机投影矩阵函数
      • 2、定义将Carla世界坐标转换成相机坐标的函数
      • 3、设置Carla并生成主车和相机
      • 4、使用队列接收相机的数据
      • 5、计算相机投影矩阵
      • 6、定义顶点创建边的列表
      • 7、通过opencv显示相机的画面
      • 8、通过opencv绘制boudingbox
  • 二、运行Carla与Python
      • 1、打开Carla客户端
      • 2、运行Python程序
      • 3、boudingbox效果展示



提示:以下是本篇文章正文内容,下面案例可供参考

一、安装opencv

1、opencv安装可以参照我上一篇文章:opencv安装教程 ,这一篇文章即将讲述如果在carla仿真中,将仿真世界中的车辆通过opencv将boudingbox绘制出来。

二、opencv绘制车辆的boudingbox

1、构造相机投影矩阵函数

def build_projection_matrix(w, h, fov):
    focal = w / (2.0 * np.tan(fov * np.pi / 360.0))
    K = np.identity(3)
    K[0, 0] = K[1, 1] = focal
    K[0, 2] = w / 2.0
    K[1, 2] = h / 2.0
    return K

1)该函数用于构建相机的投影矩阵:

  • w:相机视图的宽度(单位:像素)

  • h:相机视图的高度(单位:像素)

  • fov:相机的视野角度(单位:度)

  • 函数首先计算焦距(focal),使用公式 focal = w / (2.0 * np.tan(fov * np.pi / 360.0))。这里使用了 numpy 库中的 np.tan 函数来计算视角的正切值。

  • 然后,函数创建一个 3x3 的单位矩阵 K,作为投影矩阵。投影矩阵的主要作用是将三维空间中的点投影到二维图像平面上。在这里,投影矩阵的第一行和第二行的对角元素设置为焦距值,即 K[0, 0] = K[1, 1] = focal。投影矩阵的第一行和第二行的第三列元素设置为视图宽度和高度的一半,即 K[0, 2] = w / 2.0K[1, 2] = h / 2.0

  • 最后,函数返回构建的投影矩阵 K。


2、定义将Carla世界坐标转换成相机坐标的函数

def get_image_point(loc, K, w2c):
    # 计算三维坐标的二维投影

    # 格式化输入坐标(loc 是一个 carla.Position 对象)
    point = np.array([loc.x, loc.y, loc.z, 1])

    # 转换到相机坐标系
    point_camera = np.dot(w2c, point)

    # 将坐标系从 UE4 的坐标系转换为标准坐标系(y, -z, x),同时移除第四个分量
    point_camera = [point_camera[1], -point_camera[2], point_camera[0]]

    # 使用相机矩阵进行三维到二维投影
    point_img = np.dot(K, point_camera)

    # 归一化
    point_img[0] /= point_img[2]
    point_img[1] /= point_img[2]

    return point_img[0:2]

1)该函数用于计算三维坐标的二维投影。下面是函数的解释:

  • loc:一个 carla.Position 对象,表示三维坐标点的位置。

  • K:相机的投影矩阵。

  • w2c:世界坐标系到相机坐标系的转换矩阵。

  • 格式化输入坐标:将 loc 转换为一个包含 [x, y, z, 1] 的数组。

  • 将坐标转换到相机坐标系:使用矩阵乘法将坐标点 point 与世界坐标到相机坐标的转换矩阵 w2c 相乘,得到 point_camera

  • 转换坐标系:将坐标点从 UE4 的坐标系转换为标准坐标系 (y, -z, x),同时移除第四个分量。

  • 使用相机投影矩阵进行三维到二维投影:将 point_camera 与相机投影矩阵 K 相乘,得到投影后的二维坐标点 point_img

  • 归一化:将投影坐标点的 x 和 y 分量除以其 z 分量,以进行归一化。

  • 返回归一化后的二维投影坐标点的前两个分量,即 point_img[0:2]

  • 通过调用这个函数,并传入相应的参数,您可以将三维坐标点投影到相机视图中的二维坐标点,并进行归一化处理。


3、设置Carla并生成主车和相机

这部分在前几篇文章都讲过,不细讲了

#连接Carla并获取世界
client = carla.Client('localhost', 2000)
world  = client.get_world()
bp_lib = world.get_blueprint_library()

# 生成车辆
vehicle_bp =bp_lib.find('vehicle.lincoln.mkz_2020')
spawn_points = world.get_map().get_spawn_points()
vehicle = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points))

# 生成相机
camera_bp = bp_lib.find('sensor.camera.rgb')
camera_init_trans = carla.Transform(carla.Location(z=2))
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=vehicle)
vehicle.set_autopilot(True)

#生成目标车辆
for i in range(50):
    vehicle_bp = random.choice(bp_lib.filter('vehicle'))
    npc = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points))
    if npc:
        npc.set_autopilot(True)

# 设置仿真模式为同步模式
settings = world.get_settings()
settings.synchronous_mode = True # 启用同步模式
settings.fixed_delta_seconds = 0.05
world.apply_settings(settings)

4、使用队列接收相机的数据

# 创建对接接收相机数据
image_queue = queue.Queue()
camera.listen(image_queue.put)

1)这段代码使用了队列(Queue)来接收图像数据。

  • 首先,创建了一个名为 image_queue 的队列对象,用于存储图像数据。

  • 然后,使用 camera.listen(image_queue.put) 将 camera 对象设置为监听模式,并将接收到的图像数据放入 image_queue 队列中,在后续中使用。


5、计算相机投影矩阵

计算相机投影矩阵,用于从三维坐标投影到二维坐标

# 从相机获取属性
image_w = camera_bp.get_attribute("image_size_x").as_int()  # 图像宽度
image_h = camera_bp.get_attribute("image_size_y").as_int()  # 图像高度
fov = camera_bp.get_attribute("fov").as_float()  # 视场角

# 计算相机投影矩阵,用于从三维坐标投影到二维坐标
K = build_projection_matrix(image_w, image_h, fov)

6、定义顶点创建边的列表

为了在相机图像上绘制边界框,我们需要以适当的顺序连接顶点以创建边。为此,我们需要以下边缘对列表:

edges = [[0,1], [1,3], [3,2], [2,0], [0,4], [4,5], [5,1], [5,7], [7,6], [6,4], [6,2], [7,3]]

7、通过opencv显示相机的画面

# 获取第一张图像
world.tick()
image = image_queue.get()

# 将原始数据重新整形为 RGB 数组
img = np.reshape(np.copy(image.raw_data), (image.height, image.width, 4))

# 在 OpenCV 的显示窗口中显示图像
cv2.namedWindow('ImageWindowName', cv2.WINDOW_AUTOSIZE)
cv2.imshow('ImageWindowName', img)
cv2.waitKey(1)

1)通过以上代码,您可以完成以下操作:

  • 调用 world.tick() 更新世界状态,确保获取到最新的图像数据。
  • 使用 image_queue.get() 从图像队列中获取一张图像。
  • 将图像的原始数据重新整形为 RGB 数组,并存储在 img 中。
  • 使用 OpenCV 创建一个窗口,命名为 ‘ImageWindowName’,并将图像 img 显示在该窗口中。
  • 调用 cv2.waitKey(1) 等待用户按下键盘上的任意键来关闭图像窗口。

8、通过opencv绘制boudingbox

while True:
    # 更新世界状态并获取图像
    world.tick()
    image = image_queue.get()

    img = np.reshape(np.copy(image.raw_data), (image.height, image.width, 4))

    # 获取相机投影矩阵
    world_2_camera = np.array(camera.get_transform().get_inverse_matrix())

    for npc in world.get_actors().filter('*vehicle*'):
        # 过滤掉自车
        if npc.id != vehicle.id:
            bb = npc.bounding_box
            dist = npc.get_transform().location.distance(vehicle.get_transform().location)

            # 筛选距离在50米以内的车辆
            if dist < 50:
                forward_vec = vehicle.get_transform().get_forward_vector()
                ray = npc.get_transform().location - vehicle.get_transform().location

                # 计算车辆前进方向与车辆之间的向量的点积,
                # 通过阈值判断是否在相机前方绘制边界框
                if forward_vec.dot(ray) > 1:
                    p1 = get_image_point(bb.location, K, world_2_camera)
                    verts = [v for v in bb.get_world_vertices(npc.get_transform())]

                    for edge in edges:
                        p1 = get_image_point(verts[edge[0]], K, world_2_camera)
                        p2 = get_image_point(verts[edge[1]], K, world_2_camera)
                        cv2.line(img, (int(p1[0]), int(p1[1])), (int(p2[0]), int(p2[1])), (255, 0, 0, 255), 1)

    cv2.imshow('ImageWindowName', img)
    if cv2.waitKey(1) == ord('q'):
        break

cv2.destroyAllWindows()

通过以上代码,实现了以下功能:

  • 循环获取图像并进行处理。
  • 将图像的原始数据重新整形为 RGB 数组,并存储在 img 中。
  • 获取世界到相机的矩阵。
  • 遍历世界中的车辆角色。
  • 过滤掉自车,并筛选出距离在 50 米内的车辆。
  • 计算车辆前向向量和车辆与其他车辆之间向量的点积,以判断是否在摄像头前方。
  • 将车辆的顶点投影到图像平面,并绘制边界框的线段。
  • 使用 OpenCV 显示处理后的图像。
  • 按下键盘上的 ‘q’ 键时,结束循环。

二、运行Carla与Python

1、打开Carla客户端

  • 运行carla客户端./CarlaUE4.sh -prefernvidia

在这里插入图片描述

2、运行Python程序

import carla
import random
import queue
import numpy as np
import cv2

#构造相机投影矩阵函数
def build_projection_matrix(w, h, fov):
    focal = w / (2.0 * np.tan(fov * np.pi / 360.0))
    K = np.identity(3)
    K[0, 0] = K[1, 1] = focal
    K[0, 2] = w / 2.0
    K[1, 2] = h / 2.0
    return K

def get_image_point(loc, K, w2c):
    # 计算三维坐标的二维投影

    # 格式化输入坐标(loc 是一个 carla.Position 对象)
    point = np.array([loc.x, loc.y, loc.z, 1])

    # 转换到相机坐标系
    point_camera = np.dot(w2c, point)

    # 将坐标系从 UE4 的坐标系转换为标准坐标系(y, -z, x),同时移除第四个分量
    point_camera = [point_camera[1], -point_camera[2], point_camera[0]]

    # 使用相机矩阵进行三维到二维投影
    point_img = np.dot(K, point_camera)

    # 归一化
    point_img[0] /= point_img[2]
    point_img[1] /= point_img[2]

    return point_img[0:2]

#连接Carla并获取世界
client = carla.Client('localhost', 2000)
world  = client.get_world()
bp_lib = world.get_blueprint_library()

# 生成车辆
vehicle_bp =bp_lib.find('vehicle.lincoln.mkz_2020')
spawn_points = world.get_map().get_spawn_points()
vehicle = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points))

# 生成相机
camera_bp = bp_lib.find('sensor.camera.rgb')
camera_init_trans = carla.Transform(carla.Location(z=2))
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=vehicle)
vehicle.set_autopilot(True)

#生成目标车辆
for i in range(20):
    vehicle_bp = random.choice(bp_lib.filter('vehicle'))
    npc = world.try_spawn_actor(vehicle_bp, random.choice(spawn_points))
    if npc:
        npc.set_autopilot(True)

# 设置仿真模式为同步模式
settings = world.get_settings()
settings.synchronous_mode = True # 启用同步模式
settings.fixed_delta_seconds = 0.05
world.apply_settings(settings)

# 创建对接接收相机数据
image_queue = queue.Queue()
camera.listen(image_queue.put)

# 从相机获取属性
image_w = camera_bp.get_attribute("image_size_x").as_int()  # 图像宽度
image_h = camera_bp.get_attribute("image_size_y").as_int()  # 图像高度
fov = camera_bp.get_attribute("fov").as_float()  # 视场角

# 计算相机投影矩阵,用于从三维坐标投影到二维坐标
K = build_projection_matrix(image_w, image_h, fov)

edges = [[0,1], [1,3], [3,2], [2,0], [0,4], [4,5], [5,1], [5,7], [7,6], [6,4], [6,2], [7,3]]

# 获取第一张图像
world.tick()
image = image_queue.get()

# 将原始数据重新整形为 RGB 数组
img = np.reshape(np.copy(image.raw_data), (image.height, image.width, 4))

# 在 OpenCV 的显示窗口中显示图像
cv2.namedWindow('ImageWindowName', cv2.WINDOW_AUTOSIZE)
cv2.imshow('ImageWindowName', img)
cv2.waitKey(1)

while True:
    # 更新世界状态并获取图像
    world.tick()
    image = image_queue.get()

    img = np.reshape(np.copy(image.raw_data), (image.height, image.width, 4))

    # 获取相机投影矩阵
    world_2_camera = np.array(camera.get_transform().get_inverse_matrix())

    for npc in world.get_actors().filter('*vehicle*'):
        # 过滤掉自车
        if npc.id != vehicle.id:
            bb = npc.bounding_box
            dist = npc.get_transform().location.distance(vehicle.get_transform().location)

            # 筛选距离在50米以内的车辆
            if dist < 50:
                forward_vec = vehicle.get_transform().get_forward_vector()
                ray = npc.get_transform().location - vehicle.get_transform().location

                # 计算车辆前进方向与车辆之间的向量的点积,
                # 通过阈值判断是否在相机前方绘制边界框
                if forward_vec.dot(ray) > 1:
                    p1 = get_image_point(bb.location, K, world_2_camera)
                    verts = [v for v in bb.get_world_vertices(npc.get_transform())]

                    for edge in edges:
                        p1 = get_image_point(verts[edge[0]], K, world_2_camera)
                        p2 = get_image_point(verts[edge[1]], K, world_2_camera)
                        cv2.line(img, (int(p1[0]), int(p1[1])), (int(p2[0]), int(p2[1])), (255, 0, 0, 255), 1)

    cv2.imshow('ImageWindowName', img)
    if cv2.waitKey(1) == ord('q'):
        break

cv2.destroyAllWindows()

3、boudingbox效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/570152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识点梳理:ATTO 647N NHS ester,ATTO 647N 琥珀酰亚胺酯,荧光标记用于红色光谱区

ATTO 647N NHS ester&#xff0c;ATTO 647N SE&#xff0c;ATTO 647N 琥珀酰亚胺酯&#xff0c;ATTO 647N NHS酯 激发波长(nm)&#xff1a;646 发射波长(nm)&#xff1a;664 反应图像&#xff1a; 产品规格&#xff1a; 1.CAS号&#xff1a;N/A 2.分子式&#xff1a;N/A 3.分…

Sentinel降级规则

1.降级规则简介 官方文档 熔断降级概述 除了流量控制以外&#xff0c;对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块&#xff0c;可能是另外的一个远程服务、数据库&#xff0c;或者第三方 API 等。例如&#xff0c;支付的…

chatgpt赋能python:Python倒序遍历

Python倒序遍历 Python是一种常用的编程语言&#xff0c;其操作序列化和数据结构的方式使得其在网站开发、科学计算和人工智能领域具有重要作用。本文将重点介绍Python中倒序遍历的基本原则和实现方法。 什么是倒序遍历 倒序遍历是指从后往前迭代序列的过程。Python中提供了…

SSRF之GOPHER协议利用

目录 GOPHER协议 GOPHER协议发起的格式 GOPHER利用工具 案例一&#xff1a;CTFSHOW-359关 GOPHER协议 GOPHER协议是一种比HTTP协议还要古老的协议&#xff0c;默认工作端口70&#xff0c;但是gopher协议在SSRF漏洞利用上比HTTP协议更有优势。GOPHER协议可以以单个URL的形式…

chatgpt赋能python:Python编写接口实践:让API更高效、更可靠

Python编写接口实践&#xff1a;让API更高效、更可靠 随着互联网技术的不断发展&#xff0c;API已经成为了现代应用架构的基石之一。而Python作为一种高效、灵活的语言&#xff0c;也逐渐成为了接口开发的首选。 什么是API接口&#xff1f; API是应用程序接口&#xff08;Ap…

chatgpt赋能python:Python单行循环:提升开发效率的必备技巧

Python单行循环&#xff1a;提升开发效率的必备技巧 在Python编程中&#xff0c;循环是一种非常重要的控制流程&#xff0c;可以让程序执行特定的操作多次。而Python有一种针对短小的循环语句进行简化的技巧&#xff0c;即“单行循环”&#xff0c;也被称为“列表解析”或“生…

数据分析学习

tableau tableau介绍 tableau可以做数据可视化&#xff0c;但可视化只是tableau的基操&#xff0c;数据赋能和数据探索才是tableau的正确打开方式 数据赋能&#xff1a;让业务一线也可以轻松使用最新数据 数据探索&#xff1a;通过统计分析和数据可视化&#xff0c;从数据发现…

从应用层到MCU,看Windows处理键盘输入 [2.a.1.传球手User32.dll]

副标题:精准型消息断点 引言1. 前文作为系列的开篇&#xff0c;我们站在Notepad.exe的视角&#xff0c;看它接过系统传来的消息&#xff0c;交由Notepad的窗口处理函数(WndProc)进行处理的过程。User32.dll!DispatchMessage API是前面"系统传来"4个字中的一环&#…

Kerberos认证原理及相关漏洞

Kerberos认证协议 Kerberos认证协议也称三头犬协议&#xff0c;因为在Kerberos认证过程中&#xff0c;需要有三个角色&#xff1a;Client、Server以及KDC(Key Distribution Center)密钥分发中心。 Kerberos认证协议的目的是为客户端/服务端提供身份验证。最主要的问题是如何证明…

Win11硬盘分区

电脑重装了Win11系统&#xff0c;按WinE打开主文件夹&#xff0c;再点击此电脑&#xff0c;发现&#xff1a; 磁盘只有一个C盘。硬盘的所有空间都在该盘上了&#xff0c;那么我们怎么将其分区呢&#xff1f; Win11硬盘分区步骤&#xff1a; 步骤1&#xff1a; 按WinR输入dis…

数据库中的事务,隔离级别,以及数据展示

想要知道和学习数据库中的锁&#xff0c;要先学习数据库的事务和并发事务所带来的问题&#xff01; 1.数据库中的事务&#xff01; 1.1什么事务 事务是由一组SQL语句组成的逻辑处理单元&#xff08;多个sql进行修改&#xff0c;新增等&#xff09;&#xff0c;这些操作要么同时…

跟踪任何目标(想跟踪什么就跟踪什么)

结果展示 介绍 该项目是一个简单的跟踪工具,可以用于跟踪任何你感兴趣的东西。它提供了一个基于Web的界面,让用户可以轻松地创建和管理跟踪列表,同时也提供了一个RESTful API,可以方便地进行数据交互。 项目的原理是将用户需要跟踪的内容,通过创建跟踪项的方式存储到数据…

Hausdorff 距离

1. 定义 给定欧氏空间中的两点集 A { a 1 , a 2 , . . . } \rm A\left \{a_1, a_2,... \right\} A{a1​,a2​,...} 和 B { b 1 , b 2 , . . . } \rm B\left \{b_1, b_2,... \right\} B{b1​,b2​,...} &#xff0c; H a u s d o r f f {\rm Hausdorff} Hausdorff 距离就是用…

基于SSM的在线考试系统开发与设计-(附源码文档)-毕业设计

文章目录 1.适用人群2.你将收获3.项目介绍4.系统需求分析4.1 需求特性分析4.2 功能需求分析 5.系统设计5.1 系统总体结构设计5.2 数据库设计5.2.1 数据库概念原则设计5.2.2 数据库各部分模块设计5.2.3 数据库表设计 6.系统详细设计6.1 系统各模块功能设计6.1.1 登录模块6.1.2 注…

chatgpt赋能python:如何利用Python加快计算速度

如何利用Python加快计算速度 在大数据时代&#xff0c;计算效率的问题成为了企业和科研机构普遍关注的焦点问题。Python是一种高级编程语言&#xff0c;其具有灵活、易学、语法简洁、运行速度快等优点&#xff0c;因此在数据分析和科学计算领域广泛应用。然而&#xff0c;Pyth…

chatgpt赋能python:Python内部函数介绍

Python内部函数介绍 Python是一门功能强大、易于学习的编程语言&#xff0c;拥有许多内部函数可供使用。本文将介绍Python的内部函数和其用途&#xff0c;以便更好地利用和理解Python。 什么是内部函数&#xff1f; 内部函数是Python提供的一组内置函数&#xff0c;它们可以…

2023/5/25总结

学习CSS list-style:none 去掉无序列表的带有的样式&#xff0c;比如原点。 border-radius:length 设置圆角&#xff0c;也可以写%&#xff0c;不一定需要些半径大小&#xff0c;也可以顺时针写半径大小&#xff0c;就会出现四个顶点不一样的圆角。或者写&#xff1a;borde…

chatgpt赋能python:Python写Log的技巧与最佳实践

Python 写 Log 的技巧与最佳实践 在编写 Python 应用程序时&#xff0c;日志记录&#xff08;Logging&#xff09;是一项非常重要的功能&#xff0c;尤其是在调试或部署过程中。本文将介绍一些 Python 写 Log 的技巧和最佳实践&#xff0c;以帮助你更好地处理日志记录并提高应…

Java的String(字符串详解)

字符串 1.字符串的常见构造方法 主要有三种&#xff0c;一种是直接使用常量去构造&#xff0c;要么使用new String来构造&#xff0c;或者还可以使用字符数组的形式。 public static void main(String[] args) { // 使用常量串构造 String s1 "hello"; System.ou…

order by排序语句的用法

文章目录 学习连接语法用法示例1、按单个列的值排序2、按多个列的值排序3、按指定的规则排序4、按中文拼音字母顺序5、Order by和where条件共用 数据库中常用order by关键字对结果集进行排序&#xff0c;又可使用desc和asc来进行指定规则的排序。 学习连接 数据库&#xff1a;…