【CUDA编程】 动态体素化实现

news2024/12/26 0:01:11

动态体素化实现

动态体素化DV克服了硬体素化HV的一些缺点。动态体素化DV保留了分组grouping阶段,相反,它没有采样固定的点数或体素容量,它保留了点和体素之间的完全映射。因此,体素数和每个体素中的点数都是动态的,依赖于具体的映射函数。这消除了对固定大小缓冲区的需求,也消除了对随机点和体素的丢弃过程。

        因为所有原始点和体素信息都被保留了,动态体素化DV没有引入信息丢失,并产生了确定的体素嵌入,使得检测结果更稳定。

        另外,动态体素化建立了每一个点和体素对之间的双向关系,为从不同视图融合点级上下文特征提供了自然基础。

        动态体素化DV动态高效分配资源来管理所有体素和点。

        简单的动态体素特征编码,也是简单地对体素中所有点的特征向量进行平均值运算作为整个体素的特征,只不过每个体素中的点个数是动态的,每个体素都不同。
 

1,核心函数

torch::Tensor generate_voxel(torch::Tensor &points,
                             const std::vector<float> &voxel_size,
                             const std::vector<float> &coors_range,
                             const int NDim){
    torch::Tensor coors = points.new_zeros({points.size(0), 3},
                                           torch::TensorOptions().dtype(torch::kInt).device(torch::kCUDA, 0));
    dynamic_voxelize_gpu(points, coors, voxel_size, coors_range, 3);
    return coors;
}
std::vector<at::Tensor> DynamicSimpleVFE(torch::Tensor &voxels,
                                         torch::Tensor &coors){
    assert(coors.size(1) == 3 && voxels.size(0) == coors.size(0));
    std::vector<at::Tensor> res = dynamic_point_to_voxel_forward_gpu(voxels, coors, MEAN);
    assert(res.size() == 2);
    return res;
}

2.动态体素化 dynamic_voxelize_gpu


void dynamic_voxelize_gpu(const at::Tensor& points, at::Tensor& coors,
                          const std::vector<float> voxel_size,
                          const std::vector<float> coors_range,
                          const int NDim) {
    // check device
    CHECK_INPUT(points);

    at::cuda::CUDAGuard device_guard(points.device());

    const int num_points = points.size(0);
    const int num_features = points.size(1);

    const float voxel_x = voxel_size[0];
    const float voxel_y = voxel_size[1];
    const float voxel_z = voxel_size[2];
    const float coors_x_min = coors_range[0];
    const float coors_y_min = coors_range[1];
    const float coors_z_min = coors_range[2];
    const float coors_x_max = coors_range[3];
    const float coors_y_max = coors_range[4];
    const float coors_z_max = coors_range[5];

    const int grid_x = round((coors_x_max - coors_x_min) / voxel_x);
    const int grid_y = round((coors_y_max - coors_y_min) / voxel_y);
    const int grid_z = round((coors_z_max - coors_z_min) / voxel_z);

//    const int col_blocks = at::cuda::ATenCeilDiv(num_points, threadsPerBlock);
    const int col_blocks = ATenCeilDiv(num_points, threadsPerBlock);
    dim3 blocks(col_blocks);
    dim3 threads(threadsPerBlock);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

//    dynamic_voxelize_kernel<scalar_t, int><<<blocks, threads, 0, stream>>>(
//            points.contiguous().data_ptr<scalar_t>(),
//            coors.contiguous().data_ptr<int>(), voxel_x, voxel_y, voxel_z,
//            coors_x_min, coors_y_min, coors_z_min, coors_x_max, coors_y_max,
//            coors_z_max, grid_x, grid_y, grid_z, num_points, num_features, NDim);
    AT_DISPATCH_ALL_TYPES(points.scalar_type(), "dynamic_voxelize_kernel", [&] {
        dynamic_voxelize_kernel<scalar_t, int><<<blocks, threads, 0, stream>>>(
                points.contiguous().data_ptr<scalar_t>(),
                coors.contiguous().data_ptr<int>(), voxel_x, voxel_y, voxel_z,
                coors_x_min, coors_y_min, coors_z_min, coors_x_max, coors_y_max,
                coors_z_max, grid_x, grid_y, grid_z, num_points, num_features, NDim);
    });
    cudaDeviceSynchronize();
    AT_CUDA_CHECK(cudaGetLastError());

    return;
}

以上代码实现了获取了cuda streams 然后调用kernel函数,实现如下过程比较简单,其实就是一个将原始点的坐标转换为体素格子的坐标。voxel size为0.1时,原来范围是-100,100精确到米,现在就是-1000,1000精确到分米。

__global__ void dynamic_voxelize_kernel(
        const T* points, T_int* coors, const float voxel_x, const float voxel_y,
        const float voxel_z, const float coors_x_min, const float coors_y_min,
        const float coors_z_min, const float coors_x_max, const float coors_y_max,
        const float coors_z_max, const int grid_x, const int grid_y,
        const int grid_z, const int num_points, const int num_features,
        const int NDim) {
    //   const int index = blockIdx.x * threadsPerBlock + threadIdx.x;
   // CUDA_1D_KERNEL_LOOP(index, num_points) 
   for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < num_points; 
       index += blockDim.x * gridDim.x)

     {
        // To save some computation
        auto points_offset = points + index * num_features;
        auto coors_offset = coors + index * NDim;
        int c_x = floor((points_offset[0] - coors_x_min) / voxel_x);
        if (c_x < 0 || c_x >= grid_x) {
            coors_offset[0] = -1;
            return;
        }

        int c_y = floor((points_offset[1] - coors_y_min) / voxel_y);
        if (c_y < 0 || c_y >= grid_y) {
            coors_offset[0] = -1;
            coors_offset[1] = -1;
            return;
        }

        int c_z = floor((points_offset[2] - coors_z_min) / voxel_z);
        if (c_z < 0 || c_z >= grid_z) {
            coors_offset[0] = -1;
            coors_offset[1] = -1;
            coors_offset[2] = -1;
        } else {
            coors_offset[0] = c_z;
            coors_offset[1] = c_y;
            coors_offset[2] = c_x;
        }
    }
}

3.动态体素特征提取  dynamic_point_to_voxel_forward_gpu

     coors.masked_fill(coors.lt(0).any(-1, true), -1);  
    .lt 小于0的返回true  .any表示有true就赋值为ture  torch.any(input, dim, keepdim=False, *, out=None) → Tensor
    masked_fill作用:在tensor对应mask对应的位置填充value,其中mask的shape必须是可以广播到该张量的。
    因此此句含义为将坐标值(x,y,z)中有一个小于0的点都赋值为(-1,-1,-1)

    std::tie(out_coors, coors_map, reduce_count) =at::unique_dim(coors_clean, 0, true, true, true);
    unique_dim这里返回了体素对应的坐标,以及每个体素的点数

std::vector<at::Tensor> dynamic_point_to_voxel_forward_gpu(
        const at::Tensor &feats, const at::Tensor &coors,
        const reduce_t reduce_type) {
    CHECK_INPUT(feats);
    CHECK_INPUT(coors);

    const int num_input = feats.size(0);
    const int num_feats = feats.size(1);

    if (num_input == 0)
        return {feats.clone().detach(),
                coors.clone().detach(),
                coors.new_empty({0}, torch::kInt32),
                coors.new_empty({0}, torch::kInt32)};

    at::Tensor out_coors;
    at::Tensor coors_map;
    at::Tensor reduce_count;

    auto coors_clean = coors.masked_fill(coors.lt(0).any(-1, true), -1);  
    //.lt 小于0的返回true  .any表示有true就赋值为ture  torch.any(input, dim, keepdim=False, *, out=None) → Tensor
    //masked_fill作用:在tensor对应mask对应的位置填充value,其中mask的shape必须是可以广播到该张量的。
    //因此此句含义为将坐标值(x,y,z)中有一个小于0的点都赋值为(-1,-1,-1)

    std::tie(out_coors, coors_map, reduce_count) =
            at::unique_dim(coors_clean, 0, true, true, true);
    //这里返回了体素对应的坐标,以及每个体素的点数
    

    if (out_coors.index({0, 0}).lt(0).item<bool>()) {
        // the first element of out_coors (-1,-1,-1) and should be removed
        out_coors = out_coors.slice(0, 1);
        reduce_count = reduce_count.slice(0, 1);
        coors_map = coors_map - 1;
    }

    coors_map = coors_map.to(torch::kInt32);
    reduce_count = reduce_count.to(torch::kInt32);

    auto reduced_feats =
            at::empty({out_coors.size(0), num_feats}, feats.options());

    AT_DISPATCH_FLOATING_TYPES(
            feats.scalar_type(), "feats_reduce_kernel", ([&] {
        if (reduce_type == reduce_t::MAX)
            reduced_feats.fill_(-std::numeric_limits<scalar_t>::infinity());
        else
            reduced_feats.fill_(static_cast<scalar_t>(0));

        dim3 blocks(std::min(ATenCeilDiv(num_input, threadsPerBlock),
                             maxGridDim));
        dim3 threads(threadsPerBlock);
        feats_reduce_kernel<<<blocks, threads>>>(
                feats.data_ptr<scalar_t>(), coors_map.data_ptr<int32_t>(),
                reduced_feats.data_ptr<scalar_t>(), num_input, num_feats, reduce_type);
        if (reduce_type == reduce_t::MEAN)
            reduced_feats /= reduce_count.unsqueeze(-1).to(reduced_feats.dtype());
    }));
    AT_CUDA_CHECK(cudaGetLastError());

    return {reduced_feats, out_coors};
}

4.如何编译

4.1 使用find_package

如果CMake的版本小于3.10,可以在CMakeLists.txt文件中使用find_package来导入CUDA包,然后就可以使用cuda_add_executable()或者cuda_add_library()来编译CUDA可执行文件或者库文件了。

cmake_minimum_required(VERSION 3.8)
project(CUDA_TEST)
 
find_package(CUDA REQUIRED)
 
message(STATUS "cuda version: " ${CUDA_VERSION_STRING})
include_directories(${CUDA_INCLUDE_DIRS})
 
cuda_add_executable(cuda_test cuda_test.cu)
target_link_libraries(cuda_test ${CUDA_LIBRARIES})

其中变量CUDA_VERSION_STRING表示CUDA的版本号,CUDA_INCLUDE_DIRS表示CUDA头文件存放的目录,CUDA_LIBRARIES表示CUDA的库文件。更多说明可以参考CMake的官方文档:

https://cmake.org/cmake/help/latest/module/FindCUDA.html

CMakeLists.txt写好后,执行下面的命令就可以编译出可执行文件:

mkdir build && cd build

cmake ..

make

4.2 添加CUDA编程语言支持

3.10及以上版本的CMake中,find_package的方式已经被弃用(可以用但不推荐),要编译CUDA代码可以CMakeLists.txt文件中添加对CUDA编程语言的支持。如果程序中CUDA代码是可选的,那么可以在CMakeLists.txt文件中使用下面的语句进行使能:

enable_language(CUDA)

如果CUDA代码是必须的,那么就需要像下面这样进行设置,表示在项目CUDA_TEST中要用到CUDAC++两种编程语言:

project(CUDA_TEST LANGUAGES CUDA CXX)

可以通过CheckLanuage判断CUDA是否可用

include(CheckLanguage)

check_language(CUDA)

然后就可以跟编译普通C++代码一样用add_executable编译可执行文件了:

cmake_minimum_required(VERSION 3.10)
project(CUDA_TEST LANGUAGES CUDA CXX)
 
include(CheckLanguage)
check_language(CUDA)
 
add_executable(cuda_test cuda_test.cu)

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

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

相关文章

深入理解 SpringBoot 日志框架:从入门到高级应用——(七)SpringBoot日志配置

SpringBoot 官方文档&#xff1a;https://docs.spring.io/spring-boot/docs/2.7.12/reference/htmlsingle SpringBoot 底层依赖 Spring Boot 对所有内部日志记录使用 Commons Logging&#xff0c;但使底层日志实现保持为打开状态。 为 Java Util Logging、Log4J2 和 Logback …

【计算机网络自顶向下】如何学好计网-第一章概论

相关术语 URI&#xff1a;Uniform Resource Identifier 统一资源标识符&#xff0c;指的是一个资源 URL&#xff1a;Uniform Resource Location 统一资源定位符&#xff0c;URI的子集&#xff0c;用地址定为的方式指定一个资源 URN&#xff1a;Uniform Resource Name 统一资…

DBeaver连接SQLite数据库

一、前言 SQLite小巧轻便的开源免费关系型数据库&#xff0c;适合嵌入单机应用随身携带。桌面版推荐使用DBeaver。 官网&#xff1a;SQLite Download Page github&#xff1a;GitHub - sqlite/sqlite: Official Git mirror of the SQLite source tree 类似的开源免费且小巧…

vue+elementui实现app布局小米商城,样式美观大方

目录 一、效果图 1.首页效果图 2.分类 3.购物车 4.我的 5.登录注册 6.商品详情 7.搜索 二、项目实现 1.项目结构、设计说明 2.路由配置实现 3.首页实现源码 4.登录注册实现&#xff0c;模拟登录注册流程&#xff0c;用户数据存储到本地浏览器缓存 三、总结 一、效果…

『2023北京智源大会』开幕式以及基础模型前沿技术论坛

『2023北京智源大会』开幕式以及基础模型前沿技术论坛 文章目录 一. 黄铁军丨智源研究院院长1. 大语言模型2. 大语言模型评测体系FlagEval3. 大语言模型生态(软硬件)4. 三大路线通向 AGI(另外2条路径) 二. Towards Machines that can Learn, Reason, and Plan(杨立昆丨图灵奖得…

UE4/5样条线学习(四):样条线的创建和自然摆动

这一次我们创建一个actor蓝图&#xff0c;不过我们这次并不需要在一开始就创建样条线组件&#xff0c;而是在游戏中根据两个点去创建样条线&#xff0c;然后用时间轴根据样条线带动物品旋转位移。 制作&#xff1a; 组件部分&#xff1a; 第一步&#xff0c;创建一个actor蓝图…

CSDN铁粉增长秘籍

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

【Jmeter】在进行综合场景压测时,由于不同的请求,要求所占比例不同,那如何实现呢?

在进行综合场景压测时&#xff0c;由于不同的请求&#xff0c;要求所占比例不同&#xff0c;那如何实现呢&#xff1f; 有人说将这些请求分别放到单独的线程组下&#xff0c;然后将线程组的线程数按照比例进行配置&#xff0c;这种方法不是很好&#xff0c;想想&#xff0c;不…

【计算机网络自顶向下】如何学好计网-第二章应用层

第二章 应用层 应用层协议原理 网络应用程序体系结构 客户机/服务器体系结构&#xff1a;至少有一个服务器&#xff0c;一个客户机&#xff0c;其中服务器总是打开的&#xff0c;具有固定的众所周知的IP地址&#xff0c;主机群集常被用于创建强大的虚拟服务器&#xff0c;而客…

OpenCV 笔记_1

笔记_1 文章目录 笔记_1Mat类数据类型读取Mat类支持的运算图像读取&#xff0c;显示&#xff0c;保存imread 图像读取namedWindow 创建要显示的窗口imshow 窗口显示imwrite 图像保存 视频加载与摄像头的使用VideoCapture 加载视频或摄像头get 获取属性VideoWriter 保存视频 图像…

vue 生命周期

人的-生命周期 一组件从 创建 到 销毁 的整个过程就是生命周期 Vue_生命周期 1. 钩子函数 Vue 框架内置函数&#xff0c;随着组件的生命周期阶段&#xff0c;自动执行 作用: 特定的时间点&#xff0c;执行特定的操作 场景: 组件创建完毕后&#xff0c;可以在created 生命周期…

实际项目中使用gorm-gen来生成实体类

一、为什么要使用gorm-gen来生成实体类和查询 1、根据gorm官网地址&#xff0c;正常的写法是先写数据模型,然后由数据模型自动同步生成到数据库中,但是这样的工作量会比较大,对于写后端的人来说都熟悉sql语句,正常来说都是先自己手动创建表,利用工具将表字段同步到项目实体类中…

java商业销售分析系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 商业销售分析系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

电脑重装系统后需要更新哪些驱动

在电脑重装系统后&#xff0c;由于系统的重置&#xff0c;您需要重新安装和更新一些关键的驱动程序&#xff0c;以确保硬件设备正常工作和性能最佳化。以下是在电脑重装系统后需要更新的一些常见驱动程序。 工具/原料&#xff1a; 系统版本&#xff1a;win10系统 品牌型号&…

TOGAF10®标准中文版-(介绍和核心概念)摘要

第1章&#xff1a;简介 TOGAF标准是企业架构的框架。任何希望开发企业架构以在该组织内使用的组织都可以免费使用它&#xff08;见第1.3.1节&#xff09;。 TOGAF标准由The Open Group成员在架构论坛内开发和维护&#xff08;请参阅www.opengroup.org/Architecture&#xff0…

java8 (jdk 1.8) 新特性——Lambda 以及函数式接口

1. 什么是lambda? 目前已知的是&#xff0c;有个箭头 -> 说一大段官方话&#xff0c;也没有任何意义 我们直接看代码&#xff1a; 之前我们创建线程是这样的 Runnable runnable new Runnable() {Overridepublic void run() {System.out.println("run。。。。。。…

阿里云服务器的网络性能如何?有多快?是否适合高流量应用?

阿里云服务器的网络性能如何&#xff1f;有多快&#xff1f;是否适合高流量应用&#xff1f;   [本文由阿里云代理商[聚搜云www.4526.cn]撰写]    阿里云服务器网络性能简介   阿里云服务器&#xff08;ECS&#xff09;在网络性能方面表现卓越&#xff0c;可满足用户对高…

通过环路分析仪得到系统的闭环传递函数方法(Matlab System Identification)

目录 前言 环路分析仪数据整理 Matlab导入环路分析仪的数据 System Identification使用 闭环传递函数导出 总结 前言 之前开发的时候通过Matlab的环路设计工具实现了控制系统的补偿器参数整定&#xff0c;然后在系统硬件上面进行了验证&#xff0c;设计带宽和环路分析仪的…

基于Java实验中心管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

排序算法:插入排序(直接插入排序、希尔排序)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关排序算法的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…