【3D目标检测】稀疏卷积

2023-05-16

稀疏卷积实现部分

先说说实现部分,对原理感兴趣的往后看

1.稀疏数据生成

这里的思路主要是先利用np.meshgrid和np.stack创建出稀疏数据补全后shape大小的点云坐标,

然后随机取前num_points个点。然后再对这些点赋值。

def generate_sparse_data(shape,
                         num_points,
                         num_channels,
                         integer=False,
                         data_range=(-1, 1),
                         with_dense=True,
                         dtype=np.float32):
    dense_shape = shape
    print("shape:",shape)
    ndim = len(dense_shape)
    
    # num_points = np.random.randint(10, 100, size=[batch_size, ndim])
    num_points = np.array(num_points)
    # num_points = np.array([3, 2])
    batch_size = len(num_points)
    batch_indices = []
    coors_total = np.stack(np.meshgrid(*[np.arange(0, s) for s in shape]),
                           axis=-1)
    coors_total = coors_total.reshape(-1, ndim)
    for i in range(batch_size):
        np.random.shuffle(coors_total)
        inds_total = coors_total[:num_points[i]]
        inds_total = np.pad(inds_total, ((0, 0), (0, 1)),
                            mode="constant",
                            constant_values=i)
        batch_indices.append(inds_total)
    if integer:
        sparse_data = np.random.randint(data_range[0],
                                        data_range[1],
                                        size=[num_points.sum(),
                                              num_channels]).astype(dtype)
    else:
        sparse_data = np.random.uniform(data_range[0],
                                        data_range[1],
                                        size=[num_points.sum(),
                                              num_channels]).astype(dtype)

    # sparse_data = np.arange(1, num_points.sum() + 1).astype(np.float32).reshape(5, 1)

    res = {
        "features": sparse_data.astype(dtype),
    }
    if with_dense:
        dense_data = np.zeros([batch_size, num_channels, *dense_shape],
                              dtype=sparse_data.dtype)
        start = 0
        for i, inds in enumerate(batch_indices):
            for j, ind in enumerate(inds):
                dense_slice = (i, slice(None), *ind[:-1])
                dense_data[dense_slice] = sparse_data[start + j]
            start += len(inds)
        res["features_dense"] = dense_data.astype(dtype)
    batch_indices = np.concatenate(batch_indices, axis=0)
    res["indices"] = batch_indices.astype(np.int32)
    return res

2,稀疏索引计算

稀疏索引主要根据输入数据的位置计算卷积核权值的位置,再根据卷积核权值的位置找对应的输出点的位置。这里一个输出点可能对应多个输入点。

计算是有公式关系的:

输入数据FIN,输出是FOUT

(1)对应到的kernel点是(FIN%stride, FIN%stride+stride, FIN%stride+stride+stride,…),直到其得到的kernel编码>=kernel或者>FIN;

(2)最大的FOUT的坐标是 int( FIN/stride),然后是int( FIN/stride)-1,FOUT坐标依次减1,个数和kernel点个数一致;

def get_Pin2Pout_Rulebook_2d(in_indice, kernel, stride,out_size):
    '''
    return:
    k2in: kernel对应ouput idx的矩阵, size(kernel*kernel, input_point_length)
    out_indice: output idx to output坐标
    k2out: kernel对应ouput idx的矩阵, size(kernel*kernel, output_point_length)
    '''
    max_ho=out_size[0]-1
    max_wo=out_size[1]-1
 
    offset = {i: [] for i in range(ks ** 2)}
    out_indice={}
    out_count=-1
    for i in range(len(in_indice)): #遍历每一个有效输入点
        hi,wi,n=in_indice[i]
        vh = int(hi / stride)   #output_height 坐标
        if vh > max_ho:  #判断height是否是最后一个ouput点,如果是,kernel只用到一个
            if stride==1:
                kh_all=list(range(hi-max_ho,min(hi+1,kernel),stride))
            else:
                if hi - max_ho * stride>=kernel:
                    kh_all=[]
                else:
                    kh_all = [hi - max_ho * stride]
            vh = max_ho
        else:
            kh_all=list(range(hi%stride,min(hi+1,kernel),stride))  #kernel_h从hi%stride开始,按照stride递增,直到kernel的大小。退出的条件是kh>kernel或者kh>hi
 
        for kh in kh_all:
            vw = int(wi / stride)
            if vw > max_wo:  #判断weights是否是最后一个ouput点,如果是,kernel只用到一个
                if stride == 1:
                    kw_all = list(range(wi - max_wo, min(wi + 1, kernel), stride))
                else:
                    if wi - max_wo * stride>=kernel:
                        kw_all=[]
                    else:
                        kw_all = [wi - max_wo * stride]
                vw = max_wo
            else:
                kw_all=list(range(wi%stride,min(wi+1,kernel),stride))
 
            for kw in kw_all:
                if (n,vh,vw) not in out_indice.keys():
                    out_indice.update({(n,vh,vw):out_count+1})
                    offset[kh*kernel+kw].append([i,out_count+1])
                    out_count+=1
                else:
                    offset[kh * kernel + kw].append([i,out_indice[(n,vh,vw)]])
                vw=vw-1
            vh=vh-1
    return offset,out_indice
 
def get_output_2d(rulebook,in_data,weight_data,out_indice,out_data):
    '''
    遍历每一个kernel, 通过查找pin_idx和对应的kernel, 矩阵乘得到pout的值,并放回位置。
    同一个pout结果累加
    '''
    for key in rulebook.keys():
        cur_book=rulebook[key]
        w_data=weight_data[key]
        for i in range(len(cur_book)):
            x=in_data[cur_book[i][0],:]
            if type(out_indice)==dict:
                n, ho, wo=list(out_indice.keys())[list(out_indice.values()).index(cur_book[i][1])]
            else:
                n,ho,wo=out_indice[cur_book[i][1]]
 
            out_data[n,:,ho,wo]+=np.matmul(x,w_data)
    return out_data

 参考:优化版-基于pytorch简单实现稀疏3d卷积(SECOND)_Briwisdom的博客-CSDN博客_pytorch 稀疏卷积

稀疏卷积原理部分

稀疏卷积是对无论是2D卷积还是3D卷积进行加速运算的一种方式,其中由于3D点云的稀疏性比较大,加速将更为明显。

举例子之前的定义

为了逐步解释稀疏卷积的概念,使其更易于理解,本文以二维稀疏图像处理为例。由于稀疏信号采用数据列表和索引列表表示,二维和三维稀疏信号没有本质区别。

1. 输入定义

使用以下稀疏图像作为输入

如图所示,我们有一个5 × 5的3通道图像。除了 P1和 P2两点外,所有像素都是(0,0,0) (虽然0这个假设也很不严谨)。根据文献[1] ,P1和 P2,这种非零元素也称为active input sites

在稀疏格式中,数据列表是[[0.1,0.1,0.1] ,[0.2,0.2,0.2] ,索引列表是[1,2] ,[2,3] ,并且是 YX 顺序。

2. kernel 定义

假设使用以下参数进行卷积操作

稀疏卷积的卷积核与传统的卷积核相同。上图是一个例子,其内核大小为3x3。

深色和浅色代表两种滤镜。在本例中,我们使用以下卷积参数。

conv2D(kernel_size=3, out_channels=2, stride=1, padding=0)

3. 输出的定义

稀疏卷积的输出与传统的卷积有很大的不同。

对于稀疏卷积的发展,有两篇很重要的论文,所以对应的,稀疏卷积也有两种输出。

一种是 regular output definition,就像普通的卷积一样,只要kernel 覆盖一个 active input site,就可以计算出output site。

另一个称为submanifold output definition。只有当kernel的中心覆盖一个 active input site时,卷积输出才会被计算。

上图说明了这两种输出之间的区别。

A1代表 active site,即 P1产生的卷积结果。

类似地,A2代表从 P2计算出的 active site。A1A2代表 active site,它是 P1和 P2输出的总和。

深色和浅色代表不同的输出通道。

好的,假设完了,让我们看看稀疏卷积到底是怎么算的。

三、稀疏卷积的计算过程

1、构建 Input Hash Table 和 Output Hash Table

现在要把 input 和 Output 都表示成 hash table 的形式。

为什么要这么表示呢?因为&^*%。

input hash table和output hash table 对应上图的 Hash_in,和 Hash_out。

对于 Hash_in:

v_in 是下标,key_ in 表示value在input matrix中的位置。

现在的input一共两个元素 P1和P2,P1在input matrxi的(2, 1)位置, P2在 input matrix 的(3,2)的位置,并且是 YX 顺序。

是的没错,这里只记录一下p1的位置 ,先不管 p1代表的数字。所以其实可以把这个input hash table命名为 input position hash table

input hash tabel的构建完成了,接下来构建 output hash table

先来看一下卷积过程中 P1是怎么向下传导的:

用一个kernel去进行卷积操作:

但是,并不是每次卷积kernel都可以刚好碰到P1。所以,从第7次开始,输出的这个矩阵就不再变化了。

然后记录每个元素的位置。

上面说的只是操作P1,当然P2也是同样的操作。

然后把P1, P2的结果结合起来(主要是消除掉重复元素),得到了一张位置表。是的没错,此处记录的还是位置。

然后编号,就得到了 output hash table

2、构建 Rulebook

第二步是建立规则手册——rulebook。

这是稀疏卷积的关键部分!!! (敲黑板了)

规则手册的目的类似于 im2col [5] ,它将卷积从数学形式转化为有效的可编程形式。

但是与 im2col 不同的是,rulebook集合了卷积中所有涉及到的原子运算,然后将它们关联到相应的核元素上。

上图就是如何构建 rulebook 的例子。

rulebook的每一行都是一个 atomic operation(这个的定义看下面的列子就知道了),rulebook的第一列是一个索引,第二列是一个计数器count, v_in和 v_ out 分别是atomic operation的 input hash table 的 index和 output hash tabel的index。(没错,到现在为止,依然是index,而没有用到真实的数据。)

atomic operation是什么呢?举个例子

红色框框表示的是下图的atomic operation

黄色框框表示的是下图的atomic operation

因为这个时候(0, -1) 是第二次被遍历到,所以count+1.

3、Computation Pipeline

综上,编程中的过程是什么样子的呢?

现在有输入(这个图上面出现过了)

对它进行卷积操作

conv2D(kernel_size=3, out_channels=2, stride=1, padding=0)

深色和浅色的kernel表示2个不同的kernel,即output channel=2。

则,程序里的稀疏卷积过程是:

如图所示,稀疏卷积中的卷积计算,不用滑动窗口方法,而是根据rulebook计算所有的原子操作。在图中,红色和蓝色箭头表示两个不同的计算实例。

红色箭头处理rulebook中第一个 atomic operation。从rulebook中,我们知道这个atomic operation 有来自 input index (v_in) =0 位置(2,1)的 P1 的输入,和 output index (v_out) =5 位置 (2,1)的输出。

对于p1 代表的 (0.1, 0.1, 0.1),分别跟深色和浅色两个kernel进行卷积运算,得到深黄色和浅黄色两个channel的输出。

同样,蓝色箭头表示另一个原子操作。

可以看到红色操作和蓝色操作有相同的output index (v_out),没事的,直接把他们的输出加起来就好了。

四、总结

input/output hash tabel只维护那些真正有元素的条目。

所以说,稀疏卷积是非常 efficient的,因为我们只计算非零元素(元素指的是像素 或者 体素)的卷积,而不需要计算所有的元素。

虽然构建 rulebook 也是需要额外的计算开销的,但是这个构建过程也是可以在GPU上并行处理的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【3D目标检测】稀疏卷积 的相关文章

  • Linux中的动态库和静态库(.a.la.so.o)

    Linux中的动态库和静态库 a la so o 原文地址 xff1a https www cnblogs com findumars p 5421910 html 在windows下 xff0c 一般可以通过文件的后缀名来识别文件的类型
  • C++继承中的同名覆盖问题

    1 同名覆盖的理论关键 xff1a 继承中同名覆盖问题的核心知识点 xff1a 作用域问题 xff0c 例子 xff1a span class token keyword int span a span class token punctu
  • 链表问题技巧:使用伪头节点

    小技巧 xff1a 对于链表问题 xff0c 创建头节点时不知道合适的节点值 xff0c 因此通常需要先初始化一个预先指针 伪头节点 pre xff0c 该指针的下一个节点指向真正的头结点head 使用预先指针的目的在于链表初始化时无可用节
  • STL自定义排序函数:sort()函数;priority_queue,set,map等容器排序函数

    1 sort 函数自定义排序 xff1a 1 1 sort 模板原型 xff1a 1 1 1 默认模板 xff1a 利用 lt 比较 xff0c 升序排列 span class token keyword template span spa
  • 大数求余:即答案对1e9+7(1000000007)取模原因、方法总结

    1 大数求余原因 xff1a 大数越界 大数越界 xff1a 随着n增大 xff0c f n 会超过Int32甚至Int64的取值范围 xff0c 导致最终的返回值错误 当一个问题只对答案的正确性有要求 xff0c 而不在乎答案的数值 xf
  • 回车与换行的区别

    回车与换行的区别 xff1a 1 两个控制字符的介绍 以前打字机中 xff0c 每行后面加两个表示结束的字符 一个叫做 回车 return xff08 r xff09 xff0c 告诉打字机把打印头定位在左边界 xff1b 另一个叫做 换行
  • -1的原码、反码、补码(0xff)

    1的表示 1 0xff的无符号数为255 xff0c 当作为有符号数显示则为 1 2 1的原码表示为1001 xff1b 除符号位取反得反码 xff1a 1110 xff1b 加1得补码 xff1a 1111即0xff 3 负数在计算机中用
  • C++头文件包含(2):cpp多次包含同一头文件,会有什么问题?头文件保护

    1 目录结构 xff1a span class token operator span base span class token operator span main main span class token punctuation s
  • Go语言学习之读文件

    三种方式读取文件 span class token keyword package span main span class token keyword import span span class token punctuation sp
  • include_directories和target_include_directories

    1 作用 xff1a 给源文件添加头文件搜索路径 xff1a 将指定目录添加到编译器的头文件搜索路径之下 xff0c 指定的目录被解释成当前源码路径的相对路径 2 差别 xff1a 2 1 include directories xff1a
  • CMAKE常用内置变量解释:CMAKE_SOURCE_DIR/EXECUTABLE_OUTPUT_PATH/CMAKE_EXPORT_COMPILE_COMMANDS

    前言 xff1a cmake的内置命令是不区分大小写的 因此add subdirectory与ADD SUBDIRECTORY作用一致 cmake的所有变量都是区分大小写的 1 PROJECT SOURCE DIR 与 PROJECT BI
  • C++文件读写类介绍

    一 现有的文件读写方案 方案一 xff1a 采用C 43 43 标准库读写 该库拥有输入输出模板类及两个标准实例化集 xff1a 一个是用于操作char类型元素的实例化集 即常用的cin xff0c cout等 xff0c 另一个用于操作w
  • 自定义target命令:add_custom_target

    一 前置知识 1 CMake中一切都是基于target的 xff0c 如add library会产生一个library的target xff0c add executable会产生一个exe的target 2 以上命令生成的target放在
  • 软件设计原则:迪米特法则

    一 定义 迪米特法则 xff1a 要求一个对象应该对其他对象有最少的了解 xff0c 所以又叫做最少知识原则 二 法则内容 xff1a 1 不该有直接依赖关系的类之间 xff0c 不要有依赖 xff1a 即 xff0c 不和陌生人说话 xf
  • ElasticSearch最佳入门实践(六十二)type底层数据结构

    type xff0c 是一个index中用来区分类似的数据的 xff0c 类似的数据 xff0c 但是可能有不同的fields xff0c 而且有不同的属性来控制索引建立 分词器 field的value xff0c 在底层的lucene中建
  • 四轴的组成及参数评定

    电气工程及其自动化专业 xff0c 坐标广东湛江 xff0c 大一时期对专业上很感兴趣 xff0c 自学了许多东西 xff0c 但是只是停留在理论基础上而缺乏实践 xff0c 和学校在这方面的普及有点关系吧 xff0c 趁着国家有这方面的支
  • sudo rosdep init报错的解决方式

    Ubuntu16 04下安装ROS时 xff0c 执行到sudo rosdep init这一步时会遇到问题 xff0c 如下图所示 xff1a 尝试了很多办法 xff0c 都没有成功的 后来参考了https www ioiox com ar
  • VS版本和VC版本的对应【完整版】

    看到网上杂七杂八 xff0c 很乱 xff0c 索性自己发帖多版本开发福音 xff08 该帖不更新了 xff0c 请看参考里连接中的官方文档 xff0c 非常清楚 xff0c 还保持最新 xff09 MSC 1 0 MSC VER 61 6
  • 搭建运行激光slam环境中遇到的问题

    1 先是踩了一些坑 xff0c 重复安装了一些库 xff0c 因为ros noetic里面就自带了一些库 xff0c 所以安装的时候重复安装了 解决方法 xff1a 删掉重装 另外缺少一些库 xff0c 乱装一顿 xff0c 居然凑齐 Ub
  • mac上用VSCode搭建 c++ 工程,用于学习Opengl

    先下载VSCode安装c c 43 43 插件 xff0c 安装微软这个 创建一个文件夹作为项目 xff0c 然后用VSCode打开这个目录在这个文件夹中创建好四个目录 xff0c 分别是src xff0c lib include bin

随机推荐

  • 刷赞与评论

    网站自动刷帖 xff0c 刷赞 xff0c 刷评论等网络推广方式的基本实现 里面的思路有东西
  • 系统复制-快速重装系统

    ubuntu 直接把安装好常用软件和环境的系统打包成镜像 xff0c 用systemback安装 xff0c 便捷很多 之前那种 xff0c ubuntu安装都要好久 xff0c 少说也得20分钟吧 xff0c 之前就是等 xff0c 等它
  • 机器人 控制领域

    机器人 控制领域好像没太有很新很有用的工作 xff0c 还是依据Dynamic Model的Motion Planning更接近于任务层 其实 xff0c 感觉自己喜欢的不是控制 而是motion xff0c motion control
  • 树莓派电压过低 串口数据错误增多

    调试过程中 xff0c 树莓派串口读单片机上传的数据 的程序突然一堆checksum error 换一块满电的LiPo电池就大幅减少了报错 一开始猜测原因 可能是电压过低导致CPU运行慢了 xff08 可能叫做 降频 xff09 xff0c
  • 机器人知识体系

    纲 机电力算控感 知识体系体系各元素特点体系的建立和完善 机电力算控感 知识体系 机械 电子电气 力学 xff08 静力学与动力学分析 流体力学 材料力学等 xff09 计算 xff08 通用计算机和嵌入式计算机 xff09 控制理论 感知
  • OpenCV之imwrite()等基本操作

    参考 xff1a Opencv之imwrite 函数的用处 imwrite 函数用来保存图片 opencv3中的imwrite函数是用来输出图像到文件 xff0c 其声明如下 xff1a CV EXPORTS W bool imwrite
  • 麦克纳姆轮全向移动原理

    什么是麦克纳姆轮 在竞赛机器人和特殊工种机器人中 xff0c 全向移动经常是一个必需的功能 全向移动 意味着可以在平面内做出任意方向平移同时自转的动作 为了实现全向移动 xff0c 一般机器人会使用 全向轮 xff08 Omni Wheel
  • 卡尔曼滤波(KF)与扩展卡尔曼滤波(EKF)的一种理解思路及相应推导(1)

    前言 xff1a 从上个世纪卡尔曼滤波理论被提出 xff0c 卡尔曼滤波在控制论与信息论的连接上做出了卓越的贡献 为了得出准确的下一时刻状态真值 xff0c 我们常常使用卡尔曼滤波 扩展卡尔曼滤波 无迹卡尔曼滤波 粒子滤波等等方法 xff0
  • Qt Cmake添加*.qrc资源文件

    cmake minimum required VERSION 3 5 project Test LANGUAGES CXX 这里 file GLOB RECURSE QRC SOURCE FILES CMAKE CURRENT SOURCE
  • IOS 加载本地HTML

    web qtt以 folder形式添加到项目中 xff0c 注意是蓝色的颜色 创建swift项目 xff0c 写入如下代码 span class token comment span span class token comment Vie
  • C#实现:将十进制数转换为十六进制(含完整源码)

    C 实现 将十进制数转换为十六进制 含完整源码 在C 中 我们可以使用基础数据类型来存储整数值 如int long等 而十进制数是我们最常用的数制 但有些场景下需要将其转换为其它进制 如十六进制 本文将介绍如何使用C 来实现将十进制数转换为
  • 怎样用串口发送结构体-简单协议的封包和解包

    先说解决方案 xff0c 细节和实现代码都放在正文 下位机 xff1a 把结构体拆分成8位的整型数据 xff0c 加上数据包头和包尾 xff0c 然后按顺序单个单个地发出 xff1b 上位机 xff1a 把串口里的数据读取出来 xff0c
  • 计算机网络学习笔记——IP Header Checksum(校验和)的计算方法

    从TCP IP协议看到IP数据报 xff0c 看到Checksum的算法描述 xff0c 不甚了了 The checksum field is the 16 bit one s complement of the one s complem
  • 在Ubuntu18.04中更新指定python版本以及pip

    在Ubuntu18 04中更新指定python版本以及pip 更新指定python版本 xff08 eg python3 8 xff09 xff1a 参考 教你Ubuntu安装python3 7 xff0c 并更新python默认指向 xf
  • 【MATLAB数学建模编程实战】遗传算法求解最短路径(附代码及运行效果)

    欢迎关注 xff0c 本专栏主要更新MATLAB仿真 界面 基础编程 画图 算法 矩阵处理等操作 xff0c 拥有丰富的实例练习代码 xff0c 欢迎订阅该专栏 xff01 xff08 等该专栏建设成熟后将开始收费 xff0c 快快上车吧
  • stm32HAL库 串口接收不定长数据(DMA传输)

    相信大家很多初学者都会遇到串口接收不定长数据的情况 对于初学者可能看着有点难理解 xff0c 多看几遍就好 xff0c 亲测能用 话不多说上菜上菜 xff01 xff01 xff01 xff01 此代码是本人在具体工程应用 xff0c 实测
  • Flask - after_request 和 before_request

    目录 特殊的装饰器多个中间件怎么执行的 特殊的装饰器 64 app before request 在视图函数执行前执行 64 app after request 在视图函数执行后执行 span class token keyword fro
  • VScode 占用cpu风扇狂转, C/C++ IntelliSense Server for Visual Studio Code cpptools.exe占用cpu 30%

    点击下面那个红框中的东西 xff0c 然后选择暂停分析 cpu占用立马降下来了
  • 学习C++中遇到的各种问题

    拷贝构造函数到底是个是什么东西 xff1f 到底什么时候用const xff1f amp 是写在前还是写在后 xff1f 有区别 xff1f 为什么在析构函数中加了delete程序就会卡死 xff1f size t是个什么东西 xff1f
  • 【3D目标检测】稀疏卷积

    稀疏卷积实现部分 先说说实现部分 xff0c 对原理感兴趣的往后看 1 稀疏数据生成 这里的思路主要是先利用np meshgrid和np stack创建出稀疏数据补全后shape大小的点云坐标 xff0c 然后随机取前num points个