http://www.tuicool.com/articles/63aANv
一、序言
陆陆续续的如果累计起来,我估计至少有二十来位左右的朋友加我QQ,向我咨询有关抠图方面的算法,可惜的是,我对这方面之前一直是没有研究过的。除了利用和Photoshop中的魔棒一样的技术或者Photoshop中的选区菜单中的色彩范围类似的算法(这两个我有何PS至少90%一致的代码)是实现简单的抠图外,现在一些state of art 方面的算法我都不了解。因此,也浪费了不少的将知识转换为资产的机会。年30那天,偶然的一个机会,有位朋友推荐我看了一篇关于抠图的文章,并有配套的实现代码,于是我就决定从这篇文章开始我的抠图算法研究之旅。
这篇文章就是Shared Sampling for Real-Time Alpha Matting,关于这篇文章的一些信息,可以在这个网站里找到很多:http://www.inf.ufrgs.br/~eslgastal/SharedMatting/ ,配套的一个代码在CSDN中可以下载,具体见: http://download.csdn.net/detail/jlwyc/4676516
这篇文章的标题很具有吸引力,发表日期为2010,也算是比较新的。在大家继续看下去之前,我要提醒的是,这里的Real - Time有比较多的限制:主要是(1)必须依赖于强劲的GPU;(2)应用的抠图场合的背景应该比较简单。
不管如何,因为有配套的实现代码,作为起步的研究来说,该文还是算不错的。
从目前流行的抠图技术来看,这篇文章的思路算是比较落伍的一种。
二、技术细节
好了,不管那么多,我先贴些论文中的公式及一些说明将文章的主体细路描述一下。
简单的说,抠图问题就是要解决如下的一个超级病态的方程:
式中:C p 是我们观察到的图像的颜色,F P、 B P、 α p 均是未知量,可分别称之为前景、背景及透明度。
要解决这样的一个病态的方程,就必须给其增加一些附加的约束,通常,这种约束可以是和待scribbles分割图像同等大小的TriMap或者是用户收工划定的scribbles的形式存在,如下两图所示(如未特别说明,一般白色部分表示前景,黑色表示背景,灰色表示待识别的部分):
TriMap scribbles
这样的约束条件使得我们知道了那一部分是明确属于前景(α p =1),而那一部分是属于背景(α p =0),那么下面的主要任务就是搞定那些未知区域的α p 值 。
按照论文的说法,在2010年前后解决matting问题的主要方法是基于 sampling, pixel af?nities 或者两者的结合,特别是后两种是主流的方式。但是这两种都需要求解一个大型的线性系统,这个系统的大小和未知点的个数成正比(我简单看了下closed form那篇抠图文档的代码,就用到了一个庞大的稀疏矩阵),因此对于1MB左右大小的图,求解时间在几秒到几分钟不等。这篇论文提出的算法应该说是基于sampling技术的,他充分利用了相邻像素之间的相似性,并利用了算法内在的并行性,结合GPU编程,实现抠图的实时展示。
总的来说,论文提出的算法可以分成4个步骤:
第一步:Expansion,针对用户的输入,对已知区域(前景或背景)进行小规模的扩展;
第二步:Sample and Gather,对剩余的未知区域内的每个点按一定的规则取样,并选择出最佳的一对前景和背景取样点;
第三步:Re?nement,在一定的领域范围内,对未知区域内的每个点的最佳配对重新进行组合。
第四步:Local Smoothing,对得到的前景和背景对以及透明度值进行局部平滑,以减少噪音。
2.1 Expansion
这一步,按照我的经验,可以不做,他唯一的作用就是减少未知点的个数,可能在一定程度上减小后期的计算量,原理也很简单,就是对一个未知点,在其一定的邻域半径内(文中推荐值10,
并且是圆形半径),如果有已知的背景点或前景点,则计算其颜色和这些已知点颜色的距离,然后把这个未知点归属于和其颜色距离小于某个值并且最靠近该点的对象(前景或背景)。
在CSDN提供的参考代码中,这一部分的编码其实写的还是很有特色的,他的循环方式不同于我们普通的邻域编码,他是从像素点逐渐向外部循环开来,有点类似左图的这种循环方式(实际上还是有点区别的,实际是上下两行一起处理,在左右两列处理,然后再向外层扩散),这种处理方式的明显好处就是,只要找到某个点颜色距离小于设定的值,就可以停止循环了,因为这个点肯定是第一个符合颜色距离条件又同时符合物理距离最小的要求的。
这一步做不做,最最终的结果又一定的影响,但是他不具有质的影响。
2.2 Sample and Gather
总的来说,这一步是算法的核心部分,也是对结果影响最大的,他的步骤说起来其实也很简单,我们先看下图。
在这个图中,P和q点都是未知区域,我们需要通过一定的原则在已知区域为其取得一定的样本对,论文中提出的提取方法是:
设定一个参数Kg,其意义为一个点最多可能取样的前景点和背景点的个数,也就意味着最多的取样对为Kg*Kg组,通常这个值可以取为4或者更多,论文建议取4就可以了,越大则程序越耗时。
这样对于每个未知点,从该点出发,引出Kg条路径,每个路径之间成360/Kg的夹角,记录下每条路径经过的路线中首次遇到的前景或背景点,直到超出图像的边缘。
为了算法的稳定性,每3*3的矩形区域内(4*4或者5*5也没说不可以的),路径的起始角度周期性的改变,这样相邻像素的Kg条路径经过的区域就有着较大的不同能得到更为有效的结果集。
由上图可以看到,在不少情况下,未知点的前景和背景取样数并不能达到Kg个,甚至极端情况下,找不到任何一个取样点,这样该点就无法进行透明度的计算了,这就要靠后面的过程了。
前景取样点数量分布 背景取样点数量分布 前景+背景取样点数量分布
上图绘制了前面列举的TriMap图中未知区域每个部位的取样点数量分布情况,颜色越靠近白色,表明取样点的数量越大,从图中可以明显看出,处于图像角落的一些未知点取样情况并不是特别理想,但基本上未出现没有取到样的情况,那我们在来看看scribbles那张图的结果。
前景取样点数量分布 背景取样点数量分布 前景+背景取样点数量分布
特别是前景取样分布的结果似乎不太令人满意,有些部分取样数为0了,这个问题下面还会谈到。
在完成取样计算后,我们就需要找出这些取样点中那些是最佳的组合,这个时候就涉及到一般优化时常谈到的目标函数了,在这篇论文中,对目标函数用了四个小函数的乘积来计算,分别如下:
1:
其中
为了全面,我们将上式中α p 的计算公式列出:
公式(2)的道理很为明显,用一对F/B算出的α值如果很合理的话,那么用α结合F/B重新计算出的颜色应该和原始颜色的差距很小。公式(3)在表明在一定的领域内,由于像素一般不会有突变,差值的平均值也应该很小。
为方便理解,我贴出计算α的部分代码:
/// <summary> /// 通过当前点、前景点以及背景点的颜色值计算对应的Alpha值,对应论文的公式(12)。 /// </summary> /// <param name="BC、GC、RC">当前点的BGR颜色分量值。</param> /// <param name="BF、GF、RF">前景点的BGR颜色分量值。</param> /// <param name="BF、GF、RF">背景点的BGR颜色分量值。</param> /// <remarks>Alpha会出现不在[0,1]区间的情况,因此需要抑制。</remarks> double CalcAlpha(int BC, int GC, int RC, int BF, int GF, int RF, int BB, int GB, int RB) { double Alpha =(double) ((BC - BB) * (BF - BB) + (GC - GB) * (GF - GB) + (RC - RB) * (RF - RB)) / ((BF - BB) * (BF - BB) + (GF - GB) * (GF - GB) + (RF - RB) * (RF - RB) + 0.0000001); // 这里0.0000001换成Eps在LocalSmooth阶段似乎就不对了,有反常的噪点产生 if (Alpha > 1) Alpha = 1; else if (Alpha < 0) Alpha = 0; return Alpha; }
2: 作者考虑在未知点到取样的前景和背景点之间的直线路径上,应该尽量要少有像素的突变,比如如果这条路径需要经过图像的边缘区域,则应该设计一个函数使得该函数的返回值较大,于是作者使用了下面的公式:
上式即沿着路径对像素颜色进行积分,离散化后也就是一些累加,CSDN的提供的代码在这个函数的处理过程中是有错误的,因为他最后一个判断条件使得循环只会进行一次,有兴趣的朋友可以自己去改改。
按照公式(4)的意义,一个未知点属于前景的可能性可由下式表示:
、
而一个好的组合也应该最小化下式:
3、未知点和前景点之间的物理距离,一个好的组合中的前景点应该要尽量靠近未知点;
4、未知点和背景点之间的物理距离,一个好的组合中的背景点也应该要尽量靠近未知点;
将这四个条件组合起来,最终得到如下的目标函数:
各子项的指数数据可详见论文本身。
按照这个要求,对前面进行取样得到数据进行处理,并记录下使上式最小的那一对组合,就初步确定了最佳的取样点。
其实,这个时候我们也就可以初步获得处理后的α值了,比如对于我们前面所说的Trimap,其原始图像及经过sample和gather处理后的结果如下图:
从处理结果看,已经可以粗略的得到处理的效果了。
2.3、Re?nement
初步的gather处理后,正如前文所说,得到的结果还不够细腻,并且有些未知点由于采样的过程未收集到有效的前景和背景数据,造成该点无法进行处理,因此,在Re?nement阶段需要进一步解决这个问题。
论文提出,首先,在一定的邻域内,比如半径为5的领域内,首先统计出公式(2)对应的MP值最小的3个点相关颜色数据,并对这些数据进行加权平均,得到数据对:
然后按照下面这些公式计算新的前景、背景、透明度及可信度的计算。
可信度的计算是为下一步的局部平滑做准备的,他反应了我们在这一步确定的取样点是否合理程度的一个度量,经由此步骤,我们可得到的透明度和合成图如下所示:
可见在这一步得到的结果对于上图来说已经相当完美了。
2.4 Local Smoothing
这一步说实在的我没有花太多的精力去看,他的实现过程大概有点类似于高斯模糊,但里面多了很多其他方面的处理,一个很好的事情就是在CSDN提供的代码中对这部分每个公式的实现都是正确的,也是完整的,因此,有兴趣的朋友需要自己多看下论文和对应的代码了。
三、算法的效果
按照论文提供的相关资料集我自己搜集的一些图及配套的Trimap测试了该算法的一些结果,现贴出如下所示:
原图 Trimap 合成后的效果图
可见,对于这些Trimap图,在很多情况下是能获得较为满意的效果的。
我还找了一些简单的图,使用scribble的方式进行处理,效果如下所示:
原图 操作界面 结果图
我选的这些都是背景比较简单的图,因此还能获得较为理想的效果,如果是比较复杂的图,使用scribble是基本上获取不到很理想的效果的,除非人工仔细的划分边界。
由于CSDN提供了代码,我这里不提供我自己写的了。毕竟那个代码只是个原型。
提供了三个示例程序,给有兴趣的朋友测试下效果: http://files.cnblogs.com/Imageshop/SharedMatting.rar
*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档*******************************
*************************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己***************************