Unmixing-Based Soft Color Segmentation for Image Manipulation 是一篇基于软分割的图像处理论文,发表于SIGGRAPH 2017。全文19页,总体来讲,比较难懂,文中写了很多看似无关的内容,比如Matting,迭代优化等。我参考了论文实现才对文章有了清楚的把握。
参考代码:https://github.com/liuguoyou/color-unmixing
B站video:https://www.bilibili.com/video/BV1bU4y1x7MY/?vd_source=6a91312d89cec082cf6d5a92fee7279a
按照惯例,先上一张teaser。对于输入图像(a),该算法自动提取多个layer(b),每个layer所在的region几乎不相交(在不同region边界会有一定相交,所谓的软分割),并且每个layer的颜色服从一个正太分布, 提取图层后,就可以对图像进行颜色调整,如(c-d)所示。

简单回顾一下经典的基于调色板的图层提取算法。这类算法(Chang et al, 聚类算法; Tan et al,几何凸包):
1)首先提取输入图像的几种代表性颜色作为图像的调色板:
C
=
{
C
1
,
C
2
.
.
.
C
k
}
C = \{C_1,C_2...C_k\}
C={C1,C2...Ck}.
2)接下来对图像中的像素进行插值,将每个像素表示成调色板的凸组合:
c
p
=
∑
i
w
i
p
C
i
c_p = \sum_iw^p_iC_i
cp=∑iwipCi, 且满足
∑
i
w
i
p
=
1
\sum_iw^p_i=1
∑iwip=1.
3)自然的每一个图层就可以表示为:
L
i
p
=
w
i
p
C
i
L^p_i = w^p_iC_i
Lip=wipCi, 其中,
L
i
p
L^p_i
Lip表示图层
L
i
L_i
Li中
p
p
p点的颜色。
Recoloring:根据 2),我们只需计算一次插值权重,然后修改调色板颜色即可实现重着色:
c
p
′
=
∑
i
w
i
p
C
i
′
c'_p = \sum_iw^p_iC'_i
cp′=∑iwipCi′.
基于调色板的算法的主要缺点是很难保持插值的稀疏性。通俗地讲,比如2)中,可能 p p p 关于所有调色板颜色的插值权重 w 1 p w^p_1 w1p 均大于0,如此一来,当你改变一个调色板的颜色时可能使得图像全局的颜色发生变化,使得重着色操作的局部性不够好。因此插值稀疏性是一个重要的问题,稀疏的插值才能实现局部性良好的图像重着色。我想今天要讲的这篇论文从一定程度上实现了较好的稀疏性,因为大多数像素只关联到一个图层而非多个,只在region边界的像素才关联到多个图层(类似于matting算法的效果)。
回到正题,简单讲讲这篇论文的算法。
算法将提取多个图层,每个图层的颜色符合正太分布。类似于基于调色板的图层分解算法,任意图层
L
i
L_i
Li 包含 颜色 和 不透明度信息。图层中像素
p
p
p 的颜色和不透明度分别表示为:
u
i
p
u^p_i
uip 和
α
i
p
\alpha^p_i
αip. 因此,图像中任意像素点的颜色可由多个图层混合而成:
c
p
=
∑
i
α
i
p
u
i
p
(1)
c^p = \sum_i\alpha^p_iu^p_i\tag1
cp=i∑αipuip(1)
需要满足的条件1:保证凸组合,权重之和为1:
∑
i
α
i
p
=
1
(2)
\sum_i\alpha^p_i=1\tag2
i∑αip=1(2).
需要满足的条件2:颜色和不透明度的值域位于0,1之间:
α
i
p
,
u
i
p
∈
[
0
,
1
]
(3)
\alpha_{i}^{p}, {u}_{i}^{p} \in[0,1]\tag3
αip,uip∈[0,1](3).
接下来介绍,首先介绍如何得到图层及其参数。
之前提到本文提取的每个layer的颜色服从正太分布。因此,首先要确定有几个layer以及估计每个layer的正态分布。针对初始layer估计,文章采用了一种迭代的方式通过投票的方式自动决定layer的数目并进一步估计layer的参数。
1)首先将输入图像在RGB空间均分为
10
×
10
×
10
=
1000
10\times10\times10=1000
10×10×10=1000 个bins。
2)然后计算图像的梯度,可以直接调用Opencv的库函数 cv2.Laplacian求得所有像素点的梯度。
3)遍历所有像素点,针对每一个像素点
p
p
p, 定位到所在的 bin 假设其坐标为
b
=
{
b
r
,
b
g
,
b
b
}
b=\{b_r,b_g,b_b\}
b={br,bg,bb},计算该像素点的投票权重:
v
p
=
e
−
∥
∇
c
p
∥
(
1
−
e
−
r
p
)
(4)
v^{p}=e^{-\left\|\nabla c^{p}\right\|}\left(1-e^{-r^{p}}\right)\tag4
vp=e−∥∇cp∥(1−e−rp)(4)
其中,
∇
c
p
\nabla c^{p}
∇cp 表示
p
p
p 的梯度,
r
p
r^{p}
rp 论文中称为 representation score,可以认为是
p
p
p 的重建误差, 稍后会在第二部分讲述如何计算这个重建误差。 从式子可以看出:
a) 重建误差
r
p
r^{p}
rp 越大,
e
−
r
p
e^{-r^{p}}
e−rp 越接近0, 从而
(
1
−
e
−
r
p
)
\left(1-e^{-r^{p}}\right)
(1−e−rp) 越大,导致
v
p
v^{p}
vp 越大。
b) 梯度
∇
c
p
\nabla c^{p}
∇cp 越小即该点颜色跟周围颜色差异越小,
e
−
∥
∇
c
p
∥
e^{-\left\|\nabla c^{p}\right\|}
e−∥∇cp∥ 越大。
接下来,将该点的投票权重累积到所在的bin: b i n s [ b r ] [ b g ] [ b b ] + = v p bins[b_r][b_g][b_b] += v^p bins[br][bg][bb]+=vp.
4)选出投票权重最大的bin:
b
i
n
m
a
x
=
m
a
x
(
b
i
n
[
0
]
[
0
]
[
0
]
,
b
i
n
[
0
]
[
0
]
[
[
1
]
,
.
.
.
b
i
n
[
9
]
[
9
]
[
9
]
)
bin_{max} = max(bin[0][0][0],bin[0][0][[1],...bin[9][9][9])
binmax=max(bin[0][0][0],bin[0][0][[1],...bin[9][9][9])
5)在
b
i
n
m
a
x
bin_{max}
binmax 中选出种子点。遍历
b
i
n
m
a
x
bin_{max}
binmax 中的所有像素点, 针对任一像素点
p
p
p, 统计其
20
×
20
20 \times 20
20×20 的邻域中有多少个像素点也落在
b
i
n
m
a
x
bin_{max}
binmax 中,记为
S
p
S^p
Sp。 最后,再次计算
p
p
p 点的得分:
s
c
o
r
e
p
=
S
p
e
−
∥
∇
c
p
∥
(5)
score_p =S^pe^{-\left\|\nabla c^{p}\right\|} \tag5
scorep=Spe−∥∇cp∥(5)
选出
b
i
n
m
a
x
bin_{max}
binmax 中得分最高的像素点作为种子点:
s
i
=
arg
max
p
∈
bin
S
p
s
c
o
r
e
p
(6)
s_{i}=\underset{p \in \text { bin }}{\arg \max } \mathcal{S}^{p} score_p\tag6
si=p∈ bin argmaxSpscorep(6)
6)对种子点
s
i
s_i
si 所在的
20
×
20
20 \times 20
20×20 邻域进行类似高斯滤波的操作, 计算邻域每个像素的滤波权重,并让权重归一化。
7)对种子点
s
i
s_i
si 所在的
20
×
20
20 \times 20
20×20 邻域, 估计正太分布参数:均值和协方差矩阵。这样我们就得到了第一个layer对应的正态分布。
8)重复1)~ 7),循环停止条件:如果绝大多数(99.5%)像素的重建误差都已经很小:
r
p
<
τ
2
r^p < \tau^2
rp<τ2, (
τ
=
5
\tau=5
τ=5),则算法停止。
上一张论文中迭代选取种子点的过程图,图中的人就是论文作者本人!

输入:
n
n
n 个layer对应的正太分布。
输出:计算每个像素点的representation score。
针对每个像素点
p
p
p, 其representation score 通过最小化如下能量函数求得:
F
S
=
∑
i
α
i
p
D
i
(
u
i
p
)
+
σ
(
∑
i
α
i
p
∑
i
(
α
i
p
)
2
−
1
)
(7)
\mathcal{F}_{\mathcal{S}}=\sum_{i} \alpha^p_{i} \mathcal{D}_{i}\left(u^p_i\right)+\sigma\left(\frac{\sum_{i} \alpha^p_{i}}{\sum_{i} {(\alpha^p_{i}})^{2}}-1\right)\tag7
FS=i∑αipDi(uip)+σ(∑i(αip)2∑iαip−1)(7)
该能量函数包括两项:
1)第一项 :
∑
i
α
i
p
D
i
(
u
i
p
)
\sum_{i} \alpha^p_{i} \mathcal{D}_{i}\left(u^p_i\right)
∑iαipDi(uip), 其中
D
i
{D}_{i}
Di 表示图层
L
i
L_i
Li 对应的正太分布,
D
i
(
u
i
p
)
\mathcal{D}_{i}\left(u^p_i\right)
Di(uip) 表示待求的颜色
u
i
p
u^p_i
uip 到 该正太分布的 Mahalanobis distance(马氏距离),
α
i
p
\alpha^p_i
αip 表示待求的不透明度。这一项主要的目的是求出的图层在
p
p
p 点的颜色尽可能服从估计的正太分布,乘以系数
α
i
p
\alpha^p_i
αip 在于对这些距离加权,
α
i
p
\alpha^p_i
αip 越大的那些图层更加重要,要尽量服从这些图层的正太分布,因为
p
p
p 点的颜色,主要由这些不透明度较大图层决定。
2)第二项: σ ( ∑ i α i p ∑ i ( α i p ) 2 − 1 ) \sigma\left(\frac{\sum_{i} \alpha^p_{i}}{\sum_{i} {(\alpha^p_{i}})^{2}}-1\right) σ(∑i(αip)2∑iαip−1) 是一个稀疏性约束, σ \sigma σ是一个系数,一般设定为10. 从这个式子可以看出来,当 p p p点关于多个图层的不透明度集合 α p = { α 1 p , α 2 p , α 3 p , . . . α k p } \alpha^p = \{\alpha^p_{1},\alpha^p_{2},\alpha^p_{3},...\alpha^p_{k}\} αp={α1p,α2p,α3p,...αkp}, 如果只有一个不透明度为1,其余全为0时,将取得最小值1,从而使得公式(7)的第2项的值为0. 这就使得优化后的不透明度集合稀疏(少量的值>0),如文章开头所说,这样保证了图像编辑良好的局部性。
初始化:考察
p
p
p点在输入图像中的颜色,计算最匹配的图层(马氏距离最小),假设为
L
j
L_j
Lj, 那么分别初始化颜色和不透明度:
u
p
=
{
0
,
0
,
0
,
.
.
.
u
j
p
=
1
,
.
.
.
0
}
,
α
p
=
{
0
,
0
,
0
,
.
.
.
α
j
p
=
1
,
.
.
.
0
}
(8)
u^p = \{0,0,0,...u^p_j=1,...0\}, \alpha^p = \{0,0,0,...\alpha^p_j=1,...0\}\tag8
up={0,0,0,...ujp=1,...0},αp={0,0,0,...αjp=1,...0}(8)
注意能量最小化的时候,同时需要考虑公式(1)所示的重建误差,公式(2)所示的凸组合约束,公式(3)所示的值域约束条件。
最后上一张图层分解结果对比。可以看到,本文提取的图层稀疏性较好,比如图像中的橙子(左边第1行第4列)被非常好的提取出来,而Tan的方法提取的橙子位于;两个图层(右侧第1行第2列,右侧第2行第1列)。

简单总结:
1)方法较为新颖,跟一般调色板分解算法不同,假设每个图层服从正太分布;
2)算法计算复杂度较高,需要多次迭代,需要最优化求解像素的representation sore;
3)重着色比一般基于调色板的图层分解算法复杂,因为本文图层并不是单色,需要导出到PS中进一步重着色,比较麻烦。
4)个人认为,论文写得不好,读起来比较费劲,不好理解。