管中窥豹——从grabcut初探opencv纵向结构(一)

研究生的一年级上半学期期间也曾用matlab来做过一些作业以及一些数字图像处理的练习,matlab的语法简单直白,涉及多个领域的数学计算和建模仿真,实在强大的很,一定要挑毛病,也只能说总是觉得它有点儿憨。早就听说过opencv在机器视觉方面的强大能力,委于精力有限,也一直没有能够上手。一年级下学期基本没课了,开始课题研究阶段,就借机逐步的把opencv学习了起来,做应用,也会穿插着看大牛的心得笔记、opencv源代码及相关论文,到现在算来也有2周时间了,自己也感觉渐渐的有了一些心得,记录在此算是备忘。

废话半天,进入正题吧,本文计划主要完成几件事儿:

1)快速了解一下图割(Graph Cuts)的研究现状

2)通过学习matlab对于gracut的的实现,初步了解opencv的纵向结构。

3)大致讲一下grabcut的思想(如果还有余力,或者文章还不算太长,就继续完成)


图割(Graph Cuts)

图割是数字图象处理中一个非常重要的承上启下的环节,这个环节的工作是从图像采集环节采集得到的图像中提出感兴趣的区域来,以便后续对这部分图像进行有针对性的特征分析和提取。下面先从伟大的维基皮迪亚翻译一些有关于图割的背景资料:

源起:1989年,法国杜伦大学(Durham University)的Seheult大牛在论文《Exact maximum a posteriori estimation for binary image》一文中首次在数字图像领域提出了Graph Cuts这个概念的

小插曲,Durham University还真是挺美的

方法分类:

1、标准方法(Standard):在整个图像S上最优化能量函数E(下面会解释所谓的“能量函数”(energy function))

2、迭代方法(Iterated):a)利用K-means方法作以色彩为参数参数作能量函数在图像S上的最优化

3、动态方法(Dynamic)(Sorry,我不知道啥意思,求解释

能量函数(energy function):

1、基于色彩(Ecolor)的,包括

1)直方图(Histogram)

2)GMM(Gaussian Mixture Model)GMM就是OpenCV中的grabcut方法选用的能量函数模型

3)纹理(Texon)

2、基于关联性(Ecoherence)的,包括:

1)基于像素(Pixel)的

我的理解是现在一般的图像处理研究的待处理图像都是采集自CCD的,由于CCD里色彩传感器横平竖直的网格点阵状的分布,因此,所采集到的图像也都是离散的点,这些点在采集到的图像中最直接的联系就是邻接关系,也就是我们常说的四方向图和八方向图

2)基于代价(Costs)的,包括一些细分类,包括:

a)局部密度梯度(local intensity gradient)

b)拉普拉斯零交叉(laplacian zero crossing)(这是啥?!求解释

c)梯度方向(gradient direction)

d)色彩混合模型(color mixture model)

当前的研究存在的争议(Criticism)(所谓的争议,我觉得也就是存在问题的地方,其实也就是——潜在的研究方向哦!专门搞图像处理研究的童鞋可以稍稍关注一下)

1、Metrication artifacts(youdao翻译出来是“十进制文物”?!因为辞不达意,就不作翻译了。根据上下文的含义,我理解说的其实就是说的是在图像采集的步骤中将图像人工的“规则化”为了像素网格(见前文解释))

目前的研究已经提出了一些方法应对这个问题:

1)使用增加边(additional edges)的方法(待查资料

2)在连续空间解决最大流(max-flow)的问题

注:前文所说的OpenCV中的grabcut方法是基于GMM模型作为所谓的能量函数的,这种情况下的图割问题的解其实就是对这个GMM模型在图像S取得全局最优时,对于这个最优化问题的求解可以等价于对一个“最大流最小割”问题的求解

(所谓的“最大流最小割”问题这里就不详细解释,感兴趣的童鞋可以继续去看书看资料,这里只是把这个问题的结论给出来——连通图的最大流(max-flow)等于最小割(min-cut)。其实就是说一个图的最大流量受限于并且正好等于它的所有割点中流量最小的那个量(是不是有点儿升级版的“木桶原理”的感觉?))

2、“缩水”偏差(Shrinking bias)

因为是寻求全局最优化的的过程等价于求取最小割问题的,因此(这个“因此”我并没有理解):

1)可能会收敛于较小的围线(contour)

注:这里所谓的围线,其实就是分割结果的边界线

2)如果图像中包含一些比较细小的物体(thin objects)——例如血管之类的,则分割效果不佳

2005年Vladimir Kolmogorov和Yuri Boykov对此问题作出一些推进(还没看

3、Multiple Labels

通常的Graph Cuts只是考虑将图像区域进行2分分类——即分为前景区域和背景区域,多区域分割怎么办?

2001年Y. Boykov和O. Veksler及R. Zabih提出了一些解决方法(还没看

4、Memory Usage

最大流最小割问题的算法存在着巨大内存空间占用的问题,根据相关的研究空间复杂度为24n+14m(其中n为节点数,m为边数)


从grabcut初探opencv的纵向结构

(以下根据opencv-3.0.0作为代码根目录)

/samples/cpp/grabcut.cpp是opencv自带的grabcut的例子应用,我们的opencv之旅就从这里开始吧。

编译好opencv整个工程以后(cmake)从如下从不带参数执行samples/cpp中的grabcut程序可以从帮助看到grabcut例子应用的主要功能。

通常的图割算法可以理解为生成一个二值蒙板,所谓的前景其实就是图割完成后剪切出来的兴趣区域(在生成的蒙板上体现为白色区域——透过),所谓的背景就是相对的不感兴趣的被算法剪去的部分(在生成的蒙板上体现为黑色区域——遮挡)。

langrui@langruis-MBP:~/Downloads/opencv-3.0.0/samples/cpp$ ./cpp-example-grabcut

This program demonstrates GrabCut segmentation -- select an object in a region
and then grabcut will attempt to segment it out.
Call:
./grabcut <image_name>

Select a rectangular area around the object you want to segment

Hot keys: 
	ESC - quit the program
	r - restore the original image
	n - next iteration<span style="white-space:pre">					</span><span style="font-family: Arial, Helvetica, sans-serif;">//</span>开始下一次迭代寻优

	left mouse button - set rectangle<span style="white-space:pre">			</span><span style="font-family: Arial, Helvetica, sans-serif;">//</span><span style="font-family: Arial, Helvetica, sans-serif;">这个是划定兴趣区域,grabcut算法对于区域以外的部分全部划为背景(剪掉的部分)</span><span style="white-space:pre">
</span>
	CTRL+left mouse button - set GC_BGD pixels<span style="white-space:pre">		</span><span style="font-family: Arial, Helvetica, sans-serif;">//</span>勾画背景
	SHIFT+left mouse button - set GC_FGD pixels<span style="white-space:pre">		</span><span style="font-family: Arial, Helvetica, sans-serif;">//</span>勾画前景

	CTRL+right mouse button - set GC_PR_BGD pixels<span style="white-space:pre">		</span><span style="font-family: Arial, Helvetica, sans-serif;">//勾画主要背景</span>
	SHIFT+right mouse button - set GC_PR_FGD pixels<span style="white-space:pre">		</span><span style="font-family: Arial, Helvetica, sans-serif;">//勾画主要前景</span>

从程序的开头部分可以看到grabcut对于opencv的引用关系。可以看到,包括了opencv中重要的三个部分:imgcodecs部分包含各种类型图像资源的编解码方法,负责图像文件读取和存储;highgui部分负责高层(这里所谓高层主要是体现在对操作系统这个“低”层的无关性上)的GUI(图形用户界面);imgproc中包括了包括grabcut算法在内的各种图像处理的算法,是主要的图像处理算法库。

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace std;
using namespace cv;

在/samples/cpp/grabcut.cpp中最核心的调用imgproc库中的grabCut方法进行图像分割的部分是如下代码段:

246 int GCApplication::nextIter()
247 {
248     if( isInitialized )
249         grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );
250     else
251     {
252         if( rectState != SET )
253             return iterCount;
254 
255         if( lblsState == SET || prLblsState == SET )
256             grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );
257         else
258             grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );
259 
260         isInitialized = true;
261     }
262     iterCount++;
263 
264     bgdPxls.clear(); fgdPxls.clear();
265     prBgdPxls.clear(); prFgdPxls.clear();
266 
267     return iterCount;
268 }

图像的读取是基于imgcodecs库的:

290     Mat image = imread( filename, 1 );

此外的多数代码大多都是和接受和处理用户交互及参数获取相关的,而这些交互工作是基于highgui库的。


不早了,要开始干活了,先摆个opencv的主要模块图作为本次的结束,下次继续。


猜你喜欢

转载自blog.csdn.net/solomonlangrui/article/details/46795101