【CUDA-BEVFusion】src/main.cpp—— visualize函数说明

部署运行你感兴趣的模型镜像

CUDA-BEVFusion中,src/main.cpp—— visualize函数的主要功能是将激光雷达(LiDAR)点云数据、3D边界框(Bounding Box)以及摄像头图像进行可视化,并将结果保存为一张图片。代码使用了CUDA进行加速,并且涉及到多个模块的协同工作,包括LiDAR点云的可视化、3D边界框的绘制、摄像头图像的投影等。

1. 函数参数

  • bboxes: 3D边界框的列表,类型为std::vector<bevfusion::head::transbbox::BoundingBox>
  • lidar_points: LiDAR点云数据,类型为nv::Tensor
  • images: 摄像头图像数据,类型为std::vector<unsigned char*>
  • lidar2image: LiDAR到图像的变换矩阵,类型为nv::Tensor
  • save_path: 可视化结果的保存路径,类型为std::string
  • stream: CUDA流,用于异步操作。

  • paddinglidar_sizecontent_widthcontent_height等变量用于定义场景的尺寸和布局。
  • scene_artist_param用于设置场景画布的参数,包括宽度、高度、步长等。
  • scene_device_image是一个3通道的图像张量,用于存储最终的场景图像。

  • 使用scene_artist_param创建一个场景画布scene,并将scene_device_image作为画布的背景图像。
  • bev_artist_param用于设置BEV可视化的参数,包括图像尺寸、旋转角度、归一化尺寸等。
  • bev_visualizer用于绘制LiDAR点云、3D边界框以及自车(ego)的位置。

2. visualize代码

static void visualize(const std::vector<bevfusion::head::transbbox::BoundingBox>& bboxes, const nv::Tensor& lidar_points,
                      const std::vector<unsigned char*> images, const nv::Tensor& lidar2image, const std::string& save_path,
                      cudaStream_t stream) {
  // 将检测框数据拷贝到 predictions 向量中
  std::vector<nv::Prediction> predictions(bboxes.size());
  memcpy(predictions.data(), bboxes.data(), bboxes.size() * sizeof(nv::Prediction));

  // 定义可视化图像的尺寸和布局参数
  int padding = 300;  // 图像边缘的填充大小
  int lidar_size = 1024;  // 点云图像的宽度
  int content_width = lidar_size + padding * 3;  // 最终图像的宽度
  int content_height = 1080;  // 最终图像的高度

  // 初始化 SceneArtist 的参数
  nv::SceneArtistParameter scene_artist_param;
  scene_artist_param.width = content_width;  // 图像宽度
  scene_artist_param.height = content_height;  // 图像高度
  scene_artist_param.stride = scene_artist_param.width * 3;  // 图像步长(每行的字节数)

  // 创建设备上的图像张量,并初始化为全零(黑色)
  nv::Tensor scene_device_image(std::vector<int>{scene_artist_param.height, scene_artist_param.width, 3}, nv::DataType::UInt8);
  scene_device_image.memset(0x00, stream);

  // 将设备图像指针配置到 SceneArtistParameter 中
  scene_artist_param.image_device = scene_device_image.ptr<unsigned char>();
  auto scene = nv::create_scene_artist(scene_artist_param);  // 创建 SceneArtist 实例

  // 初始化 BEVArtist 的参数
  nv::BEVArtistParameter bev_artist_param;
  bev_artist_param.image_width = content_width;  // 图像宽度
  bev_artist_param.image_height = content_height;  // 图像高度
  bev_artist_param.rotate_x = 70.0f;  // 点云绕 x 轴的旋转角度
  bev_artist_param.norm_size = lidar_size * 0.5f;  // 点云的归一化大小
  bev_artist_param.cx = content_width * 0.5f;  // 图像中心点的 x 坐标
  bev_artist_param.cy = content_height * 0.5f;  // 图像中心点的 y 坐标
  bev_artist_param.image_stride = scene_artist_param.stride;  // 图像步长

  // 将点云数据转移到设备上
  auto points = lidar_points.to_device();
  auto bev_visualizer = nv::create_bev_artist(bev_artist_param);  // 创建 BEVArtist 实例

  // 渲染点云、预测框和自车位置
  bev_visualizer->draw_lidar_points(points.ptr<nvtype::half>(), points.size(0));  // 绘制点云
  bev_visualizer->draw_prediction(predictions, false);  // 绘制预测框
  bev_visualizer->draw_ego();  // 绘制自车位置
  bev_visualizer->apply(scene_device_image.ptr<unsigned char>(), stream);  // 将渲染结果应用到图像上

  // 初始化 ImageArtist 的参数
  nv::ImageArtistParameter image_artist_param;
  image_artist_param.num_camera = images.size();  // 相机数量
  image_artist_param.image_width = 1600;  // 相机图像的宽度
  image_artist_param.image_height = 900;  // 相机图像的高度
  image_artist_param.image_stride = image_artist_param.image_width * 3;  // 相机图像的步长
  image_artist_param.viewport_nx4x4.resize(images.size() * 4 * 4);  // 视口矩阵的大小
  memcpy(image_artist_param.viewport_nx4x4.data(), lidar2image.ptr<float>(),
         sizeof(float) * image_artist_param.viewport_nx4x4.size());  // 拷贝视口矩阵数据

  // 定义相机图像的布局参数
  int gap = 0;  // 相机图像之间的间隔
  int camera_width = 500;  // 相机图像的宽度
  int camera_height = static_cast<float>(camera_width / (float)image_artist_param.image_width * image_artist_param.image_height);  // 相机图像的高度
  int offset_cameras[][3] = {
      {-camera_width / 2, -content_height / 2 + gap, 0},  // 相机 1 的位置和翻转标志
      {content_width / 2 - camera_width - gap, -content_height / 2 + camera_height / 2, 0},  // 相机 2 的位置和翻转标志
      {-content_width / 2 + gap, -content_height / 2 + camera_height / 2, 0},  // 相机 3 的位置和翻转标志
      {-camera_width / 2, +content_height / 2 - camera_height - gap, 1},  // 相机 4 的位置和翻转标志
      {-content_width / 2 + gap, +content_height / 2 - camera_height - camera_height / 2, 0},  // 相机 5 的位置和翻转标志
      {content_width / 2 - camera_width - gap, +content_height / 2 - camera_height - camera_height / 2, 1}};  // 相机 6 的位置和翻转标志

  // 创建 ImageArtist 实例
  auto visualizer = nv::create_image_artist(image_artist_param);
  for (size_t icamera = 0; icamera < images.size(); ++icamera) {
    // 计算相机图像在最终图像中的位置
    int ox = offset_cameras[icamera][0] + content_width / 2;
    int oy = offset_cameras[icamera][1] + content_height / 2;
    bool xflip = static_cast<bool>(offset_cameras[icamera][2]);  // 是否水平翻转

    // 在相机图像上绘制预测框
    visualizer->draw_prediction(icamera, predictions, xflip);

    // 将相机图像数据拷贝到设备上
    nv::Tensor device_image(std::vector<int>{900, 1600, 3}, nv::DataType::UInt8);
    device_image.copy_from_host(images[icamera], stream);

    // 如果需要水平翻转,则对图像进行翻转
    if (xflip) {
      auto clone = device_image.clone(stream);
      scene->flipx(clone.ptr<unsigned char>(), clone.size(1), clone.size(1) * 3, clone.size(0), device_image.ptr<unsigned char>(),
                   device_image.size(1) * 3, stream);
      checkRuntime(cudaStreamSynchronize(stream));  // 同步 CUDA 流
    }

    // 将绘制结果应用到相机图像上
    visualizer->apply(device_image.ptr<unsigned char>(), stream);

    // 将相机图像调整大小并放置到最终图像中
    scene->resize_to(device_image.ptr<unsigned char>(), ox, oy, ox + camera_width, oy + camera_height, device_image.size(1),
                     device_image.size(1) * 3, device_image.size(0), 0.8f, stream);
    checkRuntime(cudaStreamSynchronize(stream));  // 同步 CUDA 流
  }

  // 保存最终图像到指定路径
  printf("Save to %s\n", save_path.c_str());
  stbi_write_jpg(save_path.c_str(), scene_device_image.size(1), scene_device_image.size(0), 3,
                 scene_device_image.to_host(stream).ptr(), 100);  // 保存为 JPG 文件
}

3. 代码功能总结

visualize 函数的主要功能是将点云、检测框和相机图像拼接成一张完整的可视化图像,并保存为 JPG 文件。具体步骤包括:

  1. 处理检测框数据:将检测框数据拷贝到 predictions 向量中。
  2. 初始化场景图像:创建并初始化设备上的场景图像。
  3. 渲染点云和检测框:使用 BEVArtist 渲染点云、检测框和自车位置。
  4. 处理相机图像:将相机图像数据拷贝到设备上,绘制检测框,并根据需要翻转图像。
  5. 拼接图像:将相机图像调整大小并放置到场景图像中。
  6. 保存结果:将最终的可视化图像保存为 JPG 文件。

您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

train: weights=yolov5s.pt, cfg=, data=data/bvn.yaml, hyp=data/hyps/hyp.scratch.yaml, epochs=300, batch_size=16, imgsz=640, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, evolve=None, bucket=, cache=None, image_weights=False, device=, multi_scale=False, single_cls=False, adam=False, sync_bn=False, workers=8, project=runs/train, name=exp, exist_ok=False, quad=False, linear_lr=False, label_smoothing=0.0, patience=100, freeze=0, save_period=-1, local_rank=-1, entity=None, upload_dataset=False, bbox_interval=-1, artifact_alias=latest github: skipping check (not a git repository), for updates see https://github.com/ultralytics/yolov5 YOLOv5 🚀 2021-10-12 torch 2.4.1+cu121 CUDA:0 (NVIDIA GeForce RTX 4060 Laptop GPU, 7915.4375MB) hyperparameters: lr0=0.01, lrf=0.1, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=0.05, cls=0.5, cls_pw=1.0, obj=1.0, obj_pw=1.0, iou_t=0.2, anchor_t=4.0, fl_gamma=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0 Weights & Biases: run 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs (RECOMMENDED) TensorBoard: Start with 'tensorboard --logdir runs/train', view at http://localhost:6006/ /home/wsy/yolov5/train.py:116: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature. ckpt = torch.load(weights, map_location=device) # load checkpoint Overriding model.yaml nc=80 with nc=2 from n params module arguments 0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2] 1 -1 1 18560 models.common.Conv [32, 64, 3, 2] 2 -1 1 18816 models.common.C3 [64, 64, 1] 3 -1 1 73984 models.common.Conv [64, 128, 3, 2] 4 -1 2 115712 models.common.C3 [128, 128, 2] 5 -1 1 295424 models.common.Conv [128, 256, 3, 2] 6 -1 3 625152 models.common.C3 [256, 256, 3] 7 -1 1 1180672 models.common.Conv [256, 512, 3, 2] 8 -1 1 1182720 models.common.C3 [512, 512, 1] 9 -1 1 656896 models.common.SPPF [512, 512, 5] 10 -1 1 131584 models.common.Conv [512, 256, 1, 1] 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 12 [-1, 6] 1 0 models.common.Concat [1] 13 -1 1 361984 models.common.C3 [512, 256, 1, False] 14 -1 1 33024 models.common.Conv [256, 128, 1, 1] 15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 16 [-1, 4] 1 0 models.common.Concat [1] 17 -1 1 90880 models.common.C3 [256, 128, 1, False] 18 -1 1 147712 models.common.Conv [128, 128, 3, 2] 19 [-1, 14] 1 0 models.common.Concat [1] 20 -1 1 296448 models.common.C3 [256, 256, 1, False] 21 -1 1 590336 models.common.Conv [256, 256, 3, 2] 22 [-1, 10] 1 0 models.common.Concat [1] 23 -1 1 1182720 models.common.C3 [512, 512, 1, False] 24 [17, 20, 23] 1 18879 models.yolo.Detect [2, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]] /home/wsy/anaconda3/envs/pytorch1.6/lib/python3.8/site-packages/torch/functional.py:513: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at ../aten/src/ATen/native/TensorShape.cpp:3609.) return _VF.meshgrid(tensors, **kwargs) # type: ignore[attr-defined] Model Summary: 270 layers, 7025023 parameters, 7025023 gradients, 16.0 GFLOPs Transferred 343/349 items from yolov5s.pt Scaled weight_decay = 0.0005 optimizer: SGD with parameter groups 57 weight, 60 weight (no decay), 60 bias train: Scanning 'datasets/labels/train.cache' images and labels... 31 found, 0 missing, 0 empty, 0 corrupted: 100%|█ val: Scanning 'datasets/labels/val.cache' images and labels... 5 found, 0 missing, 0 empty, 0 corrupted: 100%|█| 5/5 Plotting labels... autoanchor: Analyzing anchors... anchors/target = 3.81, Best Possible Recall (BPR) = 1.0000 /home/wsy/yolov5/train.py:262: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead. scaler = amp.GradScaler(enabled=cuda) Image sizes 640 train, 640 val Using 8 dataloader workers Logging results to runs/train/exp11 Starting training for 300 epochs... Epoch gpu_mem box obj cls labels img_size 0%| | 0/2 [00:00<?, ?it/s] Traceback (most recent call last): File "/home/wsy/yolov5/train.py", line 620, in <module> main(opt) File "/home/wsy/yolov5/train.py", line 517, in main train(opt.hyp, opt, device, callbacks) File "/home/wsy/yolov5/train.py", line 298, in train accumulate = max(1, np.int64(ni, xi, [1, nbs / batch_size]).round()) TypeError: function takes at most 1 argument (3 given)如何解决
08-17
/yolo88(SE)/yolov5-master/detect.py" detect: weights=/home/zzc/yolo88(SE)/yolov5-master/best.pt, source=0, data=/home/zzc/yolo88(SE)/yolov5-master/data/smoke_data.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_csv=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs/detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False, vid_stride=1 requirements: Ultralytics requirement ['Pillow>=10.0.1'] not found, attempting AutoUpdate... WARNING ⚠️ requirements: ❌ AutoUpdate skipped (offline) YOLOv5 🚀 2025-5-13 Python-3.11.2 torch-2.7.0+cpu CPU Fusing layers... YOLOv5_aux summary: 167 layers, 10126476 parameters, 0 gradients, 21.4 GFLOPs [ WARN:0@6.414] global cap_v4l.cpp:1848 getProperty VIDEOIO(V4L2:/dev/video0): Unable to get camera FPS 1/1: 0... Success (inf frames 640x480 at 99.00 FPS) Traceback (most recent call last): File "/home/zzc/yolo88(SE)/yolov5-master/detect.py", line 298, in <module> main(opt) File "/home/zzc/yolo88(SE)/yolov5-master/detect.py", line 293, in main run(**vars(opt)) File "/home/zzc/.local/lib/python3.11/site-packages/torch/utils/_contextlib.py", line 116, in decorate_context return func(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^ File "/home/zzc/yolo88(SE)/yolov5-master/detect.py", line 109, in run dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/zzc/yolo88(SE)/yolov5-master/utils/dataloaders.py", line 411, in __init__ s = np.stack([letterbox(x, img_size, stride=stride, auto=auto)[0].shape for x in self.imgs]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/zzc/yolo88(SE)/yolov5-master/utils/dataloaders.py", line 411, in <listcomp> s = np.stack([letterbox(x, img_size, stride=stride, auto=auto)[0].shape for x in self.imgs]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/zzc/yolo88(SE)/yolov5-master/utils/augmentations.py", line 113, in letterbox shape = im.shape[:2] # current shape [height, width] ^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'shape'
06-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值