为了从稀疏的深度采样(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
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