爲了從稀疏的深度採樣(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