图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting

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

*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档*******************************

*************************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己***************************

时间: 2024-10-05 11:34:10

图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting的相关文章

paper 116:自然图像抠图/视频抠像技术梳理(image matting, video matting)

1. Bayesian Matting, Chuang, CVPR 2001.http://grail.cs.washington.edu/projects/digital-matting/papers/cvpr2001.pdf  论文下载http://grail.cs.washington.edu/projects/digital-matting/image-matting/项目网址 2. GraphCut Segmentation System, Rother, 2004.http://pd

Alpha matting算法发展

一.抠图算法简介 Alpha matting算法研究的是如何将一幅图像中的前景信息和背景信息分离的问题,即抠图.这类问题是数字图像处理与数字图像编辑领域中的一类经典问题,广泛应用于视频编缉与视频分割领域中.Alpha matting的数学模型是     由Porter 和 Duff于1984 年提出[1].他们首先引入了α 通道的概念,即它是一种前景和背景颜色的线性混合表示方法.一张图片包含前景信息.背景信息,将该图片看成是前景图和背景图的合成图,于是便有了以上的混合模型.前景α为1, 背景α为

算法学习笔记

对于一个软件人员来说,算法和数据结构是无法逃避的,越是逃避说明越是需要继续学习和巩固.加深.转载一篇github上有关算法的文章,以便于进行继续学习和理解.当然并不是说非得全部掌握所有算法至少达到需要的时候会找,找到了会使,使完了能明白是什么东西才能更好的进行coding.这篇文章是有关C的 下次再弄个Java语言的算法部分.学无止境嘛,不能光看java也要学习学习C 学习方法 把所有经典算法写一遍 看算法有关源码 加入算法学习社区,相互鼓励学习 看经典书籍 刷题 原文地址:https://gi

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

我的算法学习之路

关于 严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口--况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的. 这篇文章讲了什么? 我这些年学习数据结构和算法的总结. 一些不错的算法书籍和教程. 算法的重要性. 初学 第一次接触数据结构是在大二下学期的数据结构课程.然而这门课程并没有让我入门--当时自己正忙于倒卖各种MP3和耳机,对于这些课程根本就不屑一顾--反正最后考试划个重点也能过,于是这门整个计算机专业本

算法学习三阶段

?? 第一阶段:练经典经常使用算法,以下的每一个算法给我打上十到二十遍,同一时候自己精简代码, 由于太经常使用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都能够把程序打 出来. 1.最短路(Floyd.Dijstra,BellmanFord) 2.最小生成树(先写个prim,kruscal 要用并查集,不好写) 3.大数(高精度)加减乘除 4.二分查找. (代码可在五行以内) 5.叉乘.判线段相交.然后写个凸包. 6.BFS.DFS,同一时候熟练hash 表(要熟,要灵活,代码要

周总结(2017.2.16):第一周算法学习。

周总结:算法学习总结之DFS和BFS 一:DFS算法 目的:达到被搜索结构的叶节点. 定义:假定给定图G的初态是所有的定点都没有访问过,在G中任选一定点V为初始出发点,首先访问出发点并标记,然后依次从V出发搜索V的每个相邻点W,若W未曾出现过,则对W进行深度优先遍历(DFS),知道所有和V有路径相通的定点被访问. 如果从V0开始寻找一条长度为4的路径的话: 思路步骤: 先寻找V0的所有相邻点:dis{v1,v2,v3},V1没有访问过,所以对V1进行深度遍历并将V1标记为访问过,此时路径长度为1

基于matlab的经典图像边缘检测算法

图像边缘检测算法 (1)Robert算子边缘检测 (2)Sobel算子边缘检测 (3)Prewitt算子边缘检测 (4)LOG算子边缘检测 (5)Canny边缘检测 Matlab的实现. 其实还只是掉包侠,一点算法没有写 争取有空用openCV写一遍 I=imread('1.jpg'); I0=rgb2gray(I); subplot(231); imshow(I); BW1=edge(I0,'Roberts',0.16); subplot(232); imshow(BW1); title('R

图像缩放算法

图像缩放算法较多,下面仅以最邻近插值算法和双线性插值算法作介绍. 如下图1所示,表示原始图像和缩放以后的图像. 图1 图像缩放(原始图像à缩放图像) 图像缩放就是将原始图像中的点经过某一算法映射到目标图像的点的行为,即要找到目标图像中的点p1对应在原始图像中点p0,简单而言就是找点p0. 假设: 原始图像src的分辨率为(srcW * srcH): 目标图像dst的分辨率为(dstW * dstH). 那么: 原始图像宽与目标图像宽的比例 原始图像高与目标图像高的比例 由 所以,原始图像中的点p