【三维深度学习】Sparse Convolutional Network 基于稀疏采样不变性的深度稠密重建

285 篇文章 55 订阅
261 篇文章 14 订阅

为了从稀疏的深度采样(Lidar)中重建出稠密的深度信息, 研究人员提出了一种高效的稀疏卷积层,用于在训练过程中精确定位数据缺失的位置,实现了具有稀疏不变性的特征抽取和重建手段。Sparsity Invariant CNNs

1.基于稀疏采样的稠密深度重建

激光雷达是十分重要的传感器,能给出非常准确的距离信息。但其缺陷在于采集的数据较为稀疏、同时数据也不规则,现有的神经网络在处理非规则稀疏数据时会产生一系列问题(无法明确定义卷积操作)。为了处理这个问题,研究人员将三维点云投影到虚拟的二维平面实现2.5D的表示,并利用图像修复/深度补全的方法进行处理,得到稠密的2.5D表示。最重要的是,这种特征还不随稀疏性变化,具有不变性。
在这里插入图片描述
这篇文章的主要目的是>>>通过稀疏的输入,获得稠密的输出结果,并得到稀疏不变性的表示<<<<


2.Sparse Conv稀疏卷积

可以将稠密重建视为从稀疏空间向稠密空间中的映射过程,这个过程由多层神经网络来实现。
在这里插入图片描述


与先前方法不同的是,研究人员根据采样点的位置同时定义了一个观测二进制掩膜O,来为网络提供输入点的位置信息:
在这里插入图片描述
针对稀疏的输入数据,只有精确地在观测像素位置上点才被考虑(也就是o为1的位置),并利用观测点的数量进行归一化处理。这一操作背后的想法是由于输入数据的稀疏性不同,这种方法直接针对输入的有效点进行处理,使得输出具有不变性。


随后利用下面的最大池化方法来为后续的层提供新的观测mask,如果滤波器中的领域都没有值那么这个点就属于没有观测到的点,在mask中置零,如果有值则证明这是有值的观测点置为一:
在这里插入图片描述

3.模型架构

研究人员提出了了下面的网络架构,其中输入包含了稀疏的深度图(黄色)和二进制的观测掩膜(红色),输出预测后的稠密深度图(这个图可以对应第二个代码实现中最后一部分网络结构来理解)。
在这里插入图片描述
这里面最重要的是将mask加入到了模型的训练过程中,可以看到在每一层都有稀疏卷积在作用,对真正观测到的值进行处理。特征和mask同时输入稀疏卷积操作。首先上部分支中,a.特征和mask先进行点乘,保留下观测到的值,b.而后与权重进行第一次卷积;c.mask与全1矩阵进行归一化卷积,d.而后与权重作用后的特征j进行点乘,e.最后加上偏置得到最终的特征结果;f.而mask分支则利用第二节里面提到的改进最大池化方法来得到新的mask(这里的a~f可以对应代码注释中对应步骤来理解):
在这里插入图片描述
通过mask与特征的共同作用,使模型专注于对于观测数据的特征提取和学习,使得最后恢复的稠密深度图更为精确,得到的表示特征具有稀疏不变性。下面的表格中可以看到不同采样率下的稀疏卷积结果都比较稳定
在这里插入图片描述

4.代码实现

找到了一个关于这篇论文的代码实现,主要对文章中的稀疏卷积部分进行了定义:

import tensorflow as tf
# copy from https://github.com/PeterTor/sparse_convolution/blob/master/sparse.py
"""输入为一个特征张量和一个观测值的mask
   输出为稀疏卷积后的张量和对应新的mask
   函数内部和一般卷积类似,包含了滤波器个数、核的大小、步长以及L2的幅值
   对应上面稀疏卷积来看会更清晰,对应a~f步骤
"""

def sparse_conv(tensor,binary_mask = None,filters=32,kernel_size=3,strides=2,l2_scale=0.0):

    if binary_mask == None: #first layer has no binary mask
        b,h,w,c = tensor.get_shape()
        channels=tf.split(tensor,c,axis=3)
        #assume that if one channel has no information, all channels have no information
        binary_mask = tf.where(tf.equal(channels[0], 0), tf.zeros_like(channels[0]), tf.ones_like(channels[0])) #mask should only have the size of (B,H,W,1)   #生成掩膜
    
    #稀疏卷积的上半部分支
    features = tf.multiply(tensor,binary_mask)  #对应第一步逐点乘法>>a
    features = tf.layers.conv2d(features, filters=filters, kernel_size=kernel_size, strides=(strides, strides), trainable=True, use_bias=False, padding="same",kernel_regularizer=tf.contrib.layers.l2_regularizer(scale=l2_scale))  #对应与权重的卷积>>b
 
    #稀疏卷积的下半部分支,处理mask
    norm = tf.layers.conv2d(binary_mask, filters=filters,kernel_size=kernel_size,strides=(strides, strides),kernel_initializer=tf.ones_initializer(),trainable=False,use_bias=False,padding="same")
    norm = tf.where(tf.equal(norm,0),tf.zeros_like(norm),tf.reciprocal(norm))  #>>c,归一化mask
    _,_,_,bias_size = norm.get_shape()

    b = tf.Variable(tf.constant(0.0, shape=[bias_size]),trainable=True)   #这个是卷积里面的b
    feature = tf.multiply(features,norm)+b   #点乘卷积结果>>d, 加上偏置结果>>e
    mask = tf.layers.max_pooling2d(binary_mask,strides = strides,pool_size=kernel_size,padding="same")    #>>f,最大池化输出新的mask

    # 返回稀疏卷积后新的特征值和mask
    return feature,mask


#下面是一个使用的示例
image = tf.placeholder(tf.float32, shape=[None,64,64,2], name="input_image")
b_mask = tf.placeholder(tf.float32, shape=[None,64,64,1], name="binary_mask")
features,b_mask = sparse_conv(image)
features,b_mask = sparse_conv(features,binary_mask=b_mask)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

在另一个代码中有整个个sparseConvNet的实现,包含了对于稀疏卷积的定义和模块的定义,模型基于pytorch实现:

from __future__ import absolute_import
# copy from:https://github.com/yxgeee/DepthComplete/blob/release/models/SparseConvNet.py
import torch
from torch import nn
from torch.nn import functional as F
import torchvision

# 定义稀疏卷积类以及前向函数
class SparseConv(nn.Module):
	# Convolution layer for sparse data
	def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, bias=True):
		super(SparseConv, self).__init__()
		self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, dilation=dilation, bias=False)   #定义卷积
		self.if_bias = bias  #定义偏置
		if self.if_bias:
			self.bias = nn.Parameter(torch.zeros(out_channels).float(), requires_grad=True)
		self.pool = nn.MaxPool2d(kernel_size, stride=stride, padding=padding, dilation=dilation)    #定义池化

		nn.init.kaiming_normal_(self.conv.weight, mode='fan_out', nonlinearity='relu')
		self.pool.require_grad = False

	def forward(self, input):
		x, m = input         # 输入特征和mask
		mc = m.expand_as(x)  # 得到掩膜
		x = x * mc           # 掩膜与输入特征点乘>>a 
		x = self.conv(x)     # 结果卷积>>b

		weights = torch.ones_like(self.conv.weight)
		mc = F.conv2d(mc, weights, bias=None, stride=self.conv.stride, padding=self.conv.padding, dilation=self.conv.dilation)   # 掩膜卷积归一化>>c
		mc = torch.clamp(mc, min=1e-5)
		mc = 1. / mc
		x = x * mc   # 归一化后点乘>>d
		if self.if_bias:
			x = x + self.bias.view(1, self.bias.size(0), 1, 1).expand_as(x)    # 加偏置>>e
		m = self.pool(m)     # 最后池化输入新的mask>>f

		return x, m

随后基于稀疏卷积构建整个稀疏卷积模型:

class SparseConvBlock(nn.Module):

	def __init__(self, in_channel, out_channel, kernel_size, stride=1, padding=0, dilation=1, bias=True):
		super(SparseConvBlock, self).__init__()
		self.sparse_conv = SparseConv(in_channel, out_channel, kernel_size, stride=stride, padding=padding, dilation=dilation, bias=True)
		self.relu = nn.ReLU(inplace=True)
		# 构建稀疏卷积和对应的激活函数

	def forward(self, input):
		x, m = input
		x, m = self.sparse_conv((x, m))
		assert (m.size(1)==1)
		x = self.relu(x)
		return x, m  
		# 返回稀疏卷积后的mask和特征x

最后构建整个网络,可对应模型架构部分第一个图理解:

class SparseConvNet(nn.Module):
# 稀疏卷积网络模型
	def __init__(self, in_channel=1, out_channel=1, kernels=[11,7,5,3,3], mid_channel=16):
		super(SparseConvNet, self).__init__()
		channel = in_channel   # 输入通道数
		convs = []
		for i in range(len(kernels)):   # 构建多层不同核大小的卷积层
			assert (kernels[i]%2==1)
			convs += [SparseConvBlock(channel, mid_channel, kernels[i], padding=(kernels[i]-1)//2)]
			channel = mid_channel
		self.sparse_convs = nn.Sequential(*convs)       # 多个卷积层序列操作
		self.mask_conv = nn.Conv2d(mid_channel+1, out_channel, 1)

	def forward(self, x):
		m = (x>0).detach().float()
		x, m = self.sparse_convs((x,m))    # 稀疏卷积
		x = torch.cat((x,m), dim=1)
		x = self.mask_conv(x)              # mask卷积
		# x = F.relu(x, inplace=True)
		return x

稀疏卷积性能不错,可以衍生出深度图像补全和深图修复等任务,以及激光雷达点云加密等工作,可以继续参考文章
Sparse-to-Dense Self-Supervised Depth Completion from LiDAR and Monocular Camera
Sparse-to-Dense: Depth Prediction from Sparse Depth Samples and a Single Image
DFineNet
mafangchan

picture from https://images.pexels.com/photos/1876291/pexels-photo-1876291.png


ref:
paper:https://lmb.informatik.uni-freiburg.de/Publications/2017/UB17a/sparsity_invariant_cnns.pdf
supplements:https://lmb.informatik.uni-freiburg.de/Publications/2017/UB17a/supplemental.pdf http://www.cvlibs.net/publications/Uhrig2017THREEDV_supplementary.pdf
code:https://github.com/PeterTor/sparse_convolution
code:https://github.com/yxgeee/DepthComplete
Author:https://lmb.informatik.uni-freiburg.de/people/uhrigj/publications.html
弗莱堡大学视觉组:https://lmb.informatik.uni-freiburg.de/index.php
#-------------------------------------------------------------------#
Facebook SparseConvNet:https://github.com/facebookresearch/SparseConvNet
SparseConvNetwiki:https://github.com/btgraham/SparseConvNet/wiki
fastai version:https://github.com/goodok/fastai_sparse
Sparsely Aggreagated Convolutional Networ: https://github.com/Lyken17/SparseNet
Efficient Sparse-Winograd Convolutional Neural Networks:https://github.com/xingyul/sparse-winograd-cnn
intelab skimcaffe:https://github.com/IntelLabs/SkimCaffe
sfm深度预测:https://github.com/tinghuiz/SfMLearner
马普研究所感知系:https://ps.is.tuebingen.mpg.de/
https://ps.is.tuebingen.mpg.de/employees/stang

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值