最近正在学习CG,争取有时间就看点论文写写代码。
GrabCut在OpenCv里面是有内置函数的,不过我还是用C++纯手工实现了一边原汁原味的论文,github链接GrabCut,里面有具体的实现代码和步骤,还有实现结果,欢迎来hack
GrabCut是04年的一篇文章,是少数用传统方法~~(对,就是没用神经网络的意思)~~解决前景和背景分离的算法。
需要的前置知识有:
混合多高斯模型
最小割集合划分
下面先介绍这两个模型
备注:本文不含任何数学证明,仅仅介绍算法,如果想要严格证明请移步百度
平平无奇的一手公式:维数是 D D D,均值是 μ \mu μ,协方差 Σ \Sigma Σ,参数集合 θ = { μ , Σ } \theta=\{\mu, \Sigma\} θ={μ,Σ}
X ∼ N ( μ , Σ ) : P ( x ∣ θ ) = ϕ ( x ∣ μ , Σ ) = 1 ( 2 π ) D / 2 s q r t ∣ Σ ∣ e x p { ( x − μ ) T Σ − 1 ( x − μ ) } X\sim N(\mu,\Sigma):P(x|\theta)=\phi(x|\mu,\Sigma)=\frac{1}{(2\pi)^{D/2} sqrt{|\Sigma}|}exp\{(x-\mu)^T\Sigma^{-1}(x-\mu)\} X∼N(μ,Σ):P(x∣θ)=ϕ(x∣μ,Σ)=(2π)D/2sqrt∣Σ∣1exp{(x−μ)TΣ−1(x−μ)}
其实混合模型就是字面意思,把很多个模型通过一定的概率混合起来,我们首先定义模型的个数 K K K,那么参数集合就变成了 θ = { μ k , Σ k , π k } \theta=\{\mu_k,\Sigma_k,\pi_k\} θ={μk,Σk,πk},其中 π k \pi_k πk是第 k k k个模型的混合加权系数
P ( x ∣ θ ) = ∑ k K π k ϕ ( x ∣ μ k , Σ k ) P(x|\theta)=\sum_k^K\pi_k\phi(x|\mu_k,\Sigma_k) P(x∣θ)=k∑Kπkϕ(x∣μk,Σk)
应用很简单:聚类。也就是如果我们假设一堆数据恰好分成K堆并且可以看成每类都是正太分布,那么如果能用GMM模型去拟合这些数据,我们只需要算出每个数据属于某个高斯模型的概率,就可以把这些数据分类了。
拟合的算法有一个经典的EM算法。本质似乎是最大似然概率优化。不过本文通过Local/Global的视角去看待这个算法。
输入:数据 z 1 ⋯ z n z_1\cdots z_n z1⋯zn和指定的参数 K K K
输出:拟合参数 θ = { μ k , Σ k , π k } \theta = \{\mu_k,\Sigma_k,\pi_k\} θ={μk,Σk,πk}
初始化参数,包括正则化数据,随机化均值,单位化方差和混合比
Esitimate步骤,计算后验概率 γ j k \gamma_{jk} γjk
μ k n e w = ∑ i N γ i k x i ∑ i N γ i k \mu_{k}^{new}=\frac{\sum_i^N\gamma_{ik}x_i}{\sum_i^N \gamma_{ik}} μknew=∑iNγik∑iNγikxi
Σ k n e w = ∑ i N γ i k ( x i − μ k n e w ) ( x i − μ k n e w ) T ∑ i N γ i k \Sigma_{k}^{new}=\frac{\sum_i^N\gamma_{ik}(x_i-\mu_k^{new})(x_i-\mu_k^{new} )^T}{\sum_i^N\gamma_{ik}} Σknew=∑iNγik∑iNγik(xi−μknew)(xi−μknew)T
π k n e w = ∑ i N γ i k / n \pi_k^{new}=\sum_i^N\gamma_{ik}/n πknew=∑iNγik/n
这个算法用Local/Global理解非常make sense。也就是先固定参数,计算出对应的每个数据属于某个模型的概率(Local Phase),计算好概率之后,再用对应的概率和数据去更新均值和协方差矩阵以及混合概率。
输入:大小为 n n n的集合 S S S,对于集合的一个划分 A / B A/B A/B,定义能量贡献如下:
w ( i ) = [ i ∈ A ] A i + [ i ∈ B ] B i w(i)=[i\in A]A_i+[i\in B]B_i w(i)=[i∈A]Ai+[i∈B]Bi,也就是每个元素分到某个集合有一定的代价。
e ( i , j ) = [ α i ≠ α j ] w i j e(i,j)=[\alpha_i\neq \alpha_j]w_{ij} e(i,j)=[αi=αj]wij, α \alpha α表示 i i i的归属。也就是若干对元素,如果他们不在同一个集合,那么有一定的代价
输出:一个划分,最小化能量。
算法:建图, S r c → i : A i , i → S i n k : B i , u n d i r e c t e d _ e d g e ( i , j , w i j ) Src\to i:A_i,i\to Sink:B_i,undirected\_edge(i,j,w_{ij}) Src→i:Ai,i→Sink:Bi,undirected_edge(i,j,wij),求最小割即可。
经典算法了,不多解释
输入数据:
一张彩色图片,可以转化为三通道向量的二维表数据 z 1 ⋯ z N z_1\cdots z_N z1⋯zN
用户交互,先圈定一段区域 T u T_u Tu,表示这个区域内可能是前景,剩下的部分 T B = T ˉ u T_B=\bar T_u TB=Tˉu,表示剩余部分一定是背景
输出数据:
一个 α i j ∈ { 0 , 1 } \alpha_{ij}\in\{0,1\} αij∈{0,1}透明度二维表,0表示背景,1表示前景
事实上,上述算法描述是很有问题,因为没有对一个分割做出评估,我并不知道哪个 α \alpha α表是最优的。
GrabCut算法的核心就是,把背景和前景分别看成GMM混合模型。并以GMM混合模型参数的拟合程度作为优化目标。
所以优化的参数列表就是,GMM的参数 θ = { μ ( α , k ) , π ( α , k ) , Σ ( α , k ) } \theta=\{\mu(\alpha, k),\pi(\alpha, k), \Sigma(\alpha, k)\} θ={μ(α,k),π(α,k),Σ(α,k)},以及每个像素的透明度 α n ∈ { 0 , 1 } \alpha_n\in\{0,1\} αn∈{0,1}和分类 k n ∈ { 0 ⋯ K − 1 } k_n\in\{0\cdots K-1\} kn∈{0⋯K−1}(属于第几个高斯模型),也就是每一类的混合高斯模型参数。其中 K K K取5。
对于这些参数,我们可以首先定义一个能量函数
U ( α , θ , k , z ) = ∑ n D ( α n , k n , θ , z n ) U(\alpha, \theta, k, z)=\sum_n D(\alpha_n,k_n,\theta,z_n) U(α,θ,k,z)=n∑D(αn,kn,θ,zn)
其中
D ( α n , k n , θ , z n ) = − log p ( z n ∣ α n , k n , θ ) − log π ( α n , k n ) = − log π ( α n , k n ) + log d e t ( Σ ( α n , k n ) ) / 2 + [ z n − μ ( α n , k n ) ] T Σ ( α n , k n ) [ z n − μ ( α n , k n ) ] / 2 D(\alpha_n, k_n,\theta, z_n)=-\log p(z_n|\alpha_n,k_n,\theta)-\log \pi(\alpha_n,k_n)\\ =-\log \pi(\alpha_n,k_n)+\log det(\Sigma(\alpha_n,k_n))/2 +[z_n-\mu(\alpha_n,k_n)]^T\Sigma(\alpha_n,k_n)[z_n-\mu(\alpha_n,k_n)]/2 D(αn,kn,θ,zn)=−logp(zn∣αn,kn,θ)−logπ(αn,kn)=−logπ(αn,kn)+logdet(Σ(αn,kn))/2+[zn−μ(αn,kn)]TΣ(αn,kn)[zn−μ(αn,kn)]/2
也就是第k类的混合加权系数以及在这一类的高斯分布的概率密度的log值
当然,除了这个部分以外,分割还关心的是边界的情况,所以论文还定义了另一个能量函数来描述边界的情况
V ( α , z ) = γ ∑ ( m , n ) ∈ C [ α n ≠ α m ] e x p − β ∣ ∣ z m − z n ∣ ∣ 2 V(\alpha,z)=\gamma \sum_{(m,n)\in C}[\alpha_n\neq \alpha_m]exp-\beta||z_m-z_n||^2 V(α,z)=γ(m,n)∈C∑[αn=αm]exp−β∣∣zm−zn∣∣2
C C C是8相邻的像素, γ \gamma γ一般取50,后面那一项实际上表示,边界相差越大,分割得越好,能量越小。
所以 β \beta β参数就应该是图上相邻像素相差的平均情况,也就是 β = ( 2 < ∣ ∣ z m − z n ∣ ∣ 2 > ) − 1 , < ⋅ > \beta=(2<||z_m-z_n||^2>)^{-1},<\cdot> β=(2<∣∣zm−zn∣∣2>)−1,<⋅>表示在全图上的期望
那么最终的能量优化目标就是
E ( α n , k n , θ , z n ) = U ( α , θ , k , z ) + V ( α , z ) E(\alpha_n,k_n,\theta,z_n)=U(\alpha,\theta, k, z)+V(\alpha, z) E(αn,kn,θ,zn)=U(α,θ,k,z)+V(α,z)
我们先上算法,目前还未实现border matting,只实现了硬分割
对于用户给予的初始分割,我们可以把两个区域内的像素看成数据,并应用GMM算法估计出两个区域内的GMM模型
接下来的步骤是不断迭代如下过程,直到能量函数收敛
先固定GMM参数 θ \theta θ,我们算出每个点属于哪个高斯模型的概率最高。
得到了k,我们根据每一类的数据,反推出每一类的参数 θ \theta θ,也就是分别计算每类的均值 μ \mu μ和方差 Σ \Sigma Σ
现在我们确定优化了 k , μ , Σ k,\mu,\Sigma k,μ,Σ,固定前面这些参数,我们优化 α \alpha α以最小化 E E E
具体地,我们可以发现, E E E的模型在 k , μ , Σ k,\mu, \Sigma k,μ,Σ确定的情况下, α = 0 , 1 \alpha=0,1 α=0,1的代价是固定的,而 V V V则相当于是在划分不同类的时候产生贡献。这就是我们的最小割模型,因此得以解决。