最近一段时间已知忙着赶图像分析与理解的项目,在三个星期内强行接触了CNN,MRF,Caffe,openCV在内的很多东西。
现在项目已经完全结束了,反而有点怀念看论文写代码的日子~希望能用这篇博文将我这段时间的工作作一个整理,
也方便我之后写报告。
问题描述
深度估计是从2D图片中得到深度信息,深度估计主要分为两种形式:从单个的单目图像中获得深度信息,从一系列
不同角度的单目图像中得到深度信息。在这个项目中我用到的方式主要是第一种。平面检测的目标是识别2D图像中属于同一个平面的一部分。
深度估计和平面检测的工作结合起来可以有很多应用,例如3D重建,场景理解,机器人技术以及SLAM
(Simutaneous Localization And Mapping)问题。
深度估计
1. 利用MRF(马尔科夫随机场)建模
这里介绍的是参考文献[1]中提到的一种方法,之后同一波人写的论文包括[2],[3]等都是用到了相似的方式。关于这相关的project还有一个网站Make 3D,这个项目在[3]中有较多介绍,这个网站里也有一些关于3D重建所需要的数据集。
a.特征提取
作者表示,人类之所以可以很清楚的从单个的单目图像中获取深度信息,是因为用到了纹理变化,纹理梯度还有颜色等“单目线索”。所以只要我们从图片中提取了这些特征,并且有监督的让机器去学习它,那么就可以让机器从单幅的单目图像中获得深度信息。
在这篇文章中,作者把一张图分割成很多patch,然后估计每一个patch的深度。而对于这些patch,有”绝对”和“相对”两对特征。绝对特征用了纹理变化,纹理梯度和雾霾信息。这里用到了9个3*3的Law’s Mask,6个方向的梯度filter,两个颜色通道(YCbCr颜色空间中的Cb和Cr一共十七个模板。而能量
(k=1,2)
计算了模板和原图像卷积后的L1与L2距离当做是最初的34个特征向量。除此之外,为了更好的捕捉到全局的信息,在这34个特征向量的基础上还加上了三个不同尺度的四邻域mask。三个不同尺度的四领域机上4个列特征所以特征向量一共的维数是19*34=646维。为了表示相对深度,作者为17个特征中的每一个feature vector计算了10个bin的直方图。
b.概率模型
为了表示相邻patch之间的深度关系,这里用到了MRF。除此之外,还对不同尺度邻域间的相互影响进行了建模。
这里d表示不同尺度的目标像素与四邻域的均值,x表示的是上节获得的特征向量,而方差σ则与则与第i个patch与第j个patch的相对深度成正比,具体说来:
,其中参数u的目的是使σ与d(i)-d(j)的平方更为接近。
c.小结
这篇paper用到了在图像分析领域用的比较多的MRF模型来更好的表示图像中邻域之间的关系。而且除了一般使用的高斯MRF模型,还提出了拉普拉斯的MRF建模方式。这两种模型都有各适用之处。作者最后提出这种模型在分析相对深度比绝对深度效果好,绝对深度最多能够估计到81m。不得不提这篇在05年出的论文实验的performance已经非常好了,在CNN还没出来之前这种方式应该是业内很流行的。要说缺点,可能就是特征太多,参数也多,我真的是看了好几遍才看明白。= =|||。本来很想把MRF这种方式自己实现一遍都已经在网上下好数据集了,看着这么多要提取的特征又望而却步了(哭泣脸)。
2. 利用CNN(卷积神经网络)
12年CNN开始流行起来之后,似乎无论是分类还是回归都可以用CNN搞定,这篇用CNN估计深度的论文也出现的正是时候。利用AlexNet得到粗粒度的深度图再自己训练一个三层的神经网络得到细节部分工作量看起来也并不是特别大,当然不出意料的也得到了state-of-art的结果。[4]
a.网络结构
这张图片就很清楚的说明了整个网络的结构,上面是把AlexNet的前五层拿出来,然后后面套了两个全连接层,得到了一个很coarse的深度图,只能看出一些模糊的特征。为了让它更清楚的表示局部特征,把原图再通过一个三层的神经网络训练,得到一些局部特征。最后与上面的coarse深度图结合一下,就可以得到最终的深度图。
b.Loss function
为了评判得到的depth map与groud truth之间的差异,这篇paper运用了下面的误差函数:
其中y表示的是通过神经网络得到的预测值,而y*表示的是真实值,这个式子化简之后得:
其实表示的就是每一个点的局部误差之和减去整张图片的整体误差,可以看做是一个归一化的处理。
c.小结
根据最后的实验结果,无论是室内的数据集NYU Depth还是室外的数据集KITTI这种方式都全面吊打make 3D。只要涉及到基于特征的分类或者回归,现在CNN似乎都处于王者地位。唯一的缺点可能就是训练的时间长吧。
3. 代码实现
最开始看的就是CNN那一篇,当时为我们实验室没有GPU而焦虑烦躁了很久很久。之后上github发现有这篇论文的重现代码,于是我开始撸起袖子一点一点装caffe,opencv,从零开始接触这些,居然发现还挺顺利的,都被自己感动了。严格的说起来,这一个部分的代码不算是我实现的,我只是一个搬运工。但是为了能将代码运行成功我也是费了相当大的功夫的。
我们项目里用到的是github博主已经训练好的caffemodel,这个model是在NYU的训练集上训练的,也就是说我如果直接拿来用几乎是只符合室内的:( …这里的test script就是将输入图片resize成固定的大小,然后用吧input扔进训练好的模型就可以得到output了。
|
|
ps:我发现原来这篇NIPS 2014的paper有项目网站,而且其中还有source code~是在theano框架下写的,weights也在里面!我决定安装一波theano再做一次实验。
平面检测
在做完深度估计之后,思考了蛮久深度估计和平面检测的共同之处。看了[5]之后才发现利用深度信息3D重建之后要探测平面就容易了很多。深度估计和平面检测结合起来在机器人技术,场景理解等方面都有所应用。
1. 利用深度信息与超像素算法
这篇论文([5])看了很多遍,前面一部分是按照前面提到的MRF模型得到了深度信息,后面一个部分则是用重建后的3D点和超像素算法判断共面。论文中用到的超像素算法是[6],这是一个基于图的贪心聚类算法,实现比较简单,但是年代比较久远。在经过了一番调研之后,我决定利用PAMI 2012中一片论文[7]提到的SLIC方法来做过分割,这个方法的核心思想是利用颜色和位置的距离信息作KNN聚类。
a.超像素算法SLIC
SLIC 即simple linear iterative clustering。分簇的依据是像素之间的颜色相似性与邻近性。其中颜色相
似性的度量因子是lab 颜色空间的L2 范数,颜色邻近性的度量因子是图像二维坐标空间xy。因而综合的度量因子
是[labxy]五维空间。下面所述的距离度量因子由下式计算得到:
其中Ns与Nc分别是距离与颜色的权重。
算法思路是对种子坐标为中心的2S*2S范围内所有像素,求这些像素到种子坐标像素的距离度量因子dist,相邻簇之间的重叠区域像素按照距离最小的种子编号(BlockIndex)标记。整幅图像扫描一遍之后,每个像素点都对应一个BlockIndex,相同BlockIndex 的像素属于同一个簇。接下来进入迭代,对上一次划分的每一个簇,求出每一个簇的labxy 均值,作为新的簇心(种子),按照上述规则重新标记,当迭代一定次数之后,分簇结果基本不发生改变即划分完成,迭代结束。(这里摘自SLIC图像超像素分割算法解析)
b.平面估计
在获取了超像素分割的cluster,以及通过获取的深度信息重建了3D坐标之后,就可以来进行平面估计了。主要分三步:
- 对于过分割的每一个cluster,根据cluster中的3D点拟合出一个平面并求出一个法向量
- 根据超像素分割的结果建立一个邻接矩阵判断BlockIndex之间是否相邻
- 根据BFS算法遍历邻接矩阵,通过每个cluster拟合平面法向量的夹角余弦来判断相邻cluster之间是否共面。
主要流程是这样,其中拟合平面的部分我用到了SVD降维的方法,即求出取样点的协方差矩阵,对角化后最小的特征值对应的特征向量就是平面的法向量。
c.小结
这一部分算法还是很好理解的,看懂算法我基本上就可以开始撸代码了。这个算法的优点在于简单,直观,但是缺点在于有很多参数影响(SLIC的参数,夹角余弦共面的阈值),而且这种方法是极其依赖于深度图的效果(我们组做presentation的时候老师说就是因为深度图不够好所以结果差强人意)。下面提到的方法比较复杂,但是实现的效果也会好一些。
2. RVM+MRF建模
这个方法出自[8].这个paper我觉得可读性真的是不太好,读了几遍才总算是把它的方法给看明白了,核心思想和NIPS 2005深度估计那篇paper类似,提取特征,马尔科夫建模得到每个像素点是否处于平面区域以及每个平面的方向,之后又用滑动窗口再去检测那些不同方向之间重叠的区域的具体方向,这样就能得到一个比较准确的结果。
a.平面评估
这篇paper提到的Depth Estimation的方法主要出自[9],通过这种方式可以粗略估计独立区域的方向。
首先提取特征,这里主要用Gaussian descriptor提取了纹理和颜色特征,具体说来纹理体征用到12个bin的梯度直方图,而颜色特征则是用到了20个bin的红绿蓝三通道的密度直方图。由于得到的特征维度太大,所以利用bag of words的方法把这些特征KNN聚类减少维度(Bag-of-words model)。为了合并得到的纹理和颜色两个词典,用NOMF(Non-negative matrix factorization)将两个文档矩阵合并起来。最后对于图像的每一个区域根据提取的特征词典创建空域图,而空域图Sa,Sb的相似度也是之后作回归和分类的基础。
在得到了相似度ρ之后,可以用一个基于ρ的核函数
来进行RVM(Relevance vector machine)进行回归得到角度值以及进行二值分类得到它是否为一个平面。
b.平面检测
前面的RVM只能做到平面的评估,在边缘细节方面的方向估计可能还不够精确。所以来要用MRF建模对上一步中得到的结果进一步改进。
首先用sweep window在整张图像上滑动,对于那些显著特性(over-lapping area)的点,把这些点作为圆心,利用周围的点的特性来判断圆心的点是否为平面以及角度。利用多组半径做实验,取中位数得到的属于平面的概率ri,以及它的方向di。
之后利用MRF进行建模,对于是否为平面只需做二值分类,而方向判定则转换为属于一张图中有限平面之一,其中ri和di用的是sweep window得到的结果,n表示图像中每一个可能的平面对应的方向。
c.小结
最后这个实验得到的结果是,88%对是否为平面的判断是正确的,而对于角度有18.3°的误差,此外,这种方法对于检测小区域的效果比较差。总体说来,这里的平面检测没有用到重构得到的3D坐标,直接用2D坐标建模,应该比之前的方式实现起来更容易。但是这里用了两个模型一共三次,RVM+MRF+RVM,而且篇paper的整个算法流程真的讲的很难明白。对于用如此复杂的方式得到一个并不算好的结果,我对这种方法是不喜欢的。
3. 代码实现
这里用到的是前面一篇paper的方式,slic算法+BFS遍历+共面判断,实现起来也不复杂。在验收的时候老师问我在重建了3D点之后为什么不用ransac,当时回答没有想到,但是现在细细想想,我觉得在一个重建的3D图里用ransac应该只能检测出一个最大的平面(就我理解ransac应该是整张图符合一个模型,而使outliner尽可能少),如果需要多个平面应该要提前分割对每一块去拟合平面。这样想想我还是没有办法用一个尽可能简单清晰的方式用ransac得到这种算法的效果。
在这边列几个函数,首先是多个3D点拟合平面的:
然后是BFS遍历的部分:
网站建设
网站建设其实是项目一开始没有考虑到的。一刚开始我就只是git clone了两个项目SLIC-Superpixels(in C++)和Depth-estimation(in python),在SLIC算法之中增加了[5]这篇paper的实现。也就是说核心算法部分用到了python和C++两种语言。本来觉得能跑出结果就很好了,可是在验收的前一周突然又想到可以做一个demo展示一下,不然还有一个星期的时间很浪费。最后大胆的决定用python将plane-detection的部分完全重构一遍,最后用Django框架写一个前端页面与后台可以连接起来。
1. Django简介
Django是一个开放源代码的Web应用框架,由Python写成。采用了MVC的框架模式,即模型M,视图V和控制器C。利用Django开发可以省去很多web开发的麻烦,程序员可以专注于写应用而不用去造轮子。并且它是开源且免费的。(Django Overview)
2. view与template
关于django我了解的不多,也主要是现学现卖了。这里我的项目主要用到了template和view方面的知识,简单的说一下我用到的功能,如需更系统学习django可以访问django Documentation.
view负责的是对视图的渲染,一个页面中有一个内容是动态的,不是静态写在html中而是要需要一定的操作(譬如从数据库中提取,或是经过一定处理)才能够展现在静态页面里的。访问了一个views.py中的函数就相当于提前把要在页面上显示的内容准备一下,之后再返回一个已经写好的html页面,并把html中相关的动态内容替换成处理后的内容。在本项目中,Views.py中的 get pic 函数调用了 get depth 与 get_ plane 两个函数得到了所需要的深度图和平面检测图,并以一定的名称保存到本地。
template负责的是展示的页面,我的项目中有一个demo.html页面负责选择图片上传,还有一个show_pic.html的页面负责展示原图,深度图,两种不同形式的平面检测图。具体参见我们的Demo Video这两个页面放在新建的template文件夹中。
除此之外,还要在url.py里进行配置,这一步主要是将在浏览器地址栏中输入的url与views.py中渲染页面的函数对应起来。
这一部分详细见 Part3:Views and templates.
3. 代码实现
处理图片之后显示的代码在views.py中的get pic 函数中,在get _depth 与get plane 两个函数中都把处理过后的图片保存下来了。
|
|
小结
前前后后大概花了三个星期来完成这个项目,又花了三天的时间码完这篇1w字的技术博客真是满满的成就感啊~这个项目的源码在这里,我们的项目网站在这里。说起来我对机器学习和深度学习也并没有很了解,关于深度估计和平面检测这块我也是第一次接触,上文中很多提到的方法可能有错误或者不足,也希望大家能指出来。接下来自己想学习部分在python爬虫 / tensorflow写深度学习。立个flag吧,四月份还会写一篇技术博客。加油!
参考文献
- Learning Depth from Single Monocular Images, Ashutosh Saxena, Sung H. Chung, Andrew Y. Ng. NIPS 2005.
- 3-D Depth Reconstruction from a Single Still Image, Ashutosh Saxena, Sung H. Chung, Andrew Y. Ng. In IJCV 2007.
- Make3D: Learning 3D Scene Structure from a Single Still Image, Ashutosh Saxena, Min Sun, Andrew Y. Ng. IEEE Transactions of Pattern Analysis and Machine Intelligence (PAMI), vol. 30, no. 5, pp 824-840, 2009.
- Depth Map Prediction from a Single Image using a Multi-Scale Deep Network.(Nips 2014)
- Accurate 3D ground plane estimation from a single image(ICRA 2009)
- Efficient Graph-Based Image Segmentation,IJCV 2004,MIT
- SLIC Superpixels Compared to State-of-the-art Superpixel Methods, IEEE Transactions on Pattern Analysis and Machine Intelligence
- Detecting planes and estimating their orientation from a single image, In Proc. of BMVC 2011.