Palette-based Photo Recoloring 是图像重着色领域的经典论文,发表于SIGGRAPH 2015,目前引用量180+。因为跟我目前的工作高度相关,所以今天简要回顾一下这篇论文。
先上一张论文的teaser,下图第1列是输入图像和提取的调色板,第2、3、4列展示了修改后的调色板以及原始图像的重着色效果,看上去效果很不错。基于调色板的图像重着色主要解决两个问题:1)调色板提取,2)基于调色板的图像重着色。针对调色板的提取,目前主要存在两类方法,分别是基于聚类的方法和基于几何凸包的方法。这篇论文是基于聚类的方法提取调色板。
1、调色板提取
作者使用改进的K-Means算法提取图像的调色板。直接在所有图像像素上聚类复杂度较高,为了加速聚类过程,作者对图像空间进行采样,并在这些采样点上进行聚类。具体而言,作者将整个RGB颜色空间归一化到 [ 0 , 1 ] 3 [0,1]^3 [0,1]3,并将RGB颜色空间平均划分为 16 × 16 × 16 16\times16\times16 16×16×16个网格。然后,统计每一个网格内部的像素数目 n i n_i ni,并计算网格内像素的平均颜色 c i c_i ci 代表该网格的颜色。需要注意的是,这里的网格颜色需要从RGB空间转化到Lab空间,一方面作者认为Lab空间比RGB空间感知更加均匀,另一方面后续对调色板的操作会涉及到Lab中的L通道。
采样点搞定后,接下来就是选取初始的聚类中心。由于K-Means算法对初值较为敏感,为了选取差异较大的初始颜色。作者设计了一种类似于最远点采样的方式选取初始聚类中心。首先给每个采样颜色
C
i
C_i
Ci赋权值
n
i
n_i
ni,接下来迭代的选取权重最大的颜色加入到初始聚类中心集合,因此,第一次就选取包含最多像素的网格的平均颜色作为第1个聚类中心
C
1
C_1
C1;接下来,对其他候选颜色权重进行更新:
n
j
=
(
1
−
e
x
p
(
−
d
i
j
2
/
σ
α
2
)
)
n
j
(1)
n_j = (1-exp(-d^2_{ij}/\sigma^2_\alpha))n_j \tag1
nj=(1−exp(−dij2/σα2))nj(1)
其中,
d
i
j
d_{ij}
dij表示Lab空间中
C
i
C_i
Ci与
C
j
C_j
Cj的距离,
σ
\sigma
σ是一个经验值,这里取值为80。容易看出来,权重跟
d
i
j
d_{ij}
dij正相关,颜色差异越大,分数越高,后续越有可能被选入初始聚类中心集合,反之则不太可能选入初始聚类中心。重复该过程,直到所有K个颜色都加入到初始值集合。
最后一步就是使用K-Means对所有采样颜色进行聚类。聚类算法仅针对有效的采样点(内部没有像素的网格被丢弃)实施,聚类速度跟图像像素数目无关,只跟采样点数目有关,因此可以快速收敛。最终收敛的聚类中心组成图像调色板。需要注意的是,图像中的黑色一般难以调整,因此一般默认先将黑色放入调色板内,待聚类收敛后,再将黑色剔除。
2、图像重着色
基于调色板的图像编辑操作简单,用户只需要对调色板的颜色进行简单的修改就可以驱动图像重新着色。经过第一步,我们已经提取到输入图像对应的调色板。接下来探讨如何将调色板的变化映射到图像的颜色上。
输入:原始调色板: { C i } \{C_i\} {Ci}, 修改后的调色板: { C i ′ } \{C'_i\} {Ci′}
输出:重着色的图像,对任意像素 I i I_i Ii : I i ′ = f ( I i ) I'_i = f(I_i) Ii′=f(Ii)。 ( I i ′ I'_i Ii′ 即是重着色后的颜色, f f f 是要求的映射函数)
基于调色板的图像编辑本质上是一个插值问题,文中列出的一些重要的插值性质包括:
- Interpolation: 图像中的颜色如果跟初始调色板的颜色相同,则能精确的映射到修改后的调色板颜色: f ( C i ) = C i ′ f(C_i)=C'_i f(Ci)=Ci′。
- In Gamut: 输出的颜色位于色域内 (比如RGB的话,像素的三个通道均位于[0,255]区间)。
- Pixel Continuity: 相似的颜色映射后也要相似: l i m q → p f ( q ) = f ( p ) lim_{q \to p} f(q) = f(p) limq→pf(q)=f(p)。
- One-to-One:满足映射 f f f是一个单射,不同的输入对应不同的输出: f ( p ) = f ( q ) = > p = q f(p)=f(q) => p = q f(p)=f(q)=>p=q
- Monotonicity in L:相对亮度保持: L ( p ) < L ( q ) = > L ( f ( p ) ) < = L ( f ( q ) ) L(p)<L(q) => L(f(p)) <= L(f(q)) L(p)<L(q)=>L(f(p))<=L(f(q))
为了得到满足以上所有性质的映射函数,作者将L通道跟ab通道分开处理。
2.1 亮度通道L变换
之前提到,调色板的所有颜色位于Lab空间,这里首先对调色板的颜色根据亮度排序使得:
L
(
C
i
<
j
)
<
L
(
C
j
)
L(C_{i<j}) < L(C_{j})
L(Ci<j)<L(Cj).
为了满足插值性质5,作者提出了两种方案:
1)当用户修改调色板的某些颜色后,保持整个调色板的相对亮度不变,从而使得图像重着色后的像素保持相对亮度。
- 修改后的调色板中某个颜色
C
i
′
C'_{i}
Ci′亮度增加。
C
i
′
C'_{i}
Ci′之前的所有颜色亮度依然保持递增,对于后续的颜色的亮度L修改为:
L ( C j > i ′ ) = m a x ( L ( C j ′ ) , L ( C j − 1 ′ ) ) L(C'_{j>i}) = max(L(C'_{j}), L(C'_{j-1})) L(Cj>i′)=max(L(Cj′),L(Cj−1′)) - 修改后的调色板中某个颜色
C
i
′
C'_{i}
Ci′亮度降低。
C
i
′
C'_{i}
Ci′之后的所有颜色亮度依然保持递增,对于之前的颜色的亮度L修改为:
L ( C j < i ′ ) = m i n ( L ( C j ′ ) , L ( C j + 1 ′ ) ) L(C'_{j<i}) = min(L(C'_{j}), L(C'_{j+1})) L(Cj<i′)=min(L(Cj′),L(Cj+1′))
2)对于任意像素颜色映射时,其亮度可以通过最接近的两个调色板的亮度加权平均取得,比如在原始图像中像素
x
x
x的亮度可以通过两个临近的调色板颜色插值得到:
L
(
x
)
=
λ
L
(
C
j
)
+
(
1
−
λ
)
L
(
C
k
)
L(x) = \lambda L(C_j) + (1-\lambda)L(C_k)
L(x)=λL(Cj)+(1−λ)L(Ck)
那么当调色板的亮度变化后依然可以保持这种线性权重不变即:
L
(
x
′
)
=
λ
L
(
C
j
′
)
+
(
1
−
λ
)
L
(
C
k
′
)
L(x') = \lambda L(C'_j) + (1-\lambda)L(C'_k)
L(x′)=λL(Cj′)+(1−λ)L(Ck′)
2.2 颜色通道ab插值
2.2.1 单个调色板颜色修改
首先讲简单的情况,即调色板只包含一种颜色,且变化为
C
→
C
′
C\to C'
C→C′, 那么理想的情况是所有像素都经过一个类似的变化:
f
1
(
x
)
=
x
+
C
′
−
C
f_1(x) = x + C' - C
f1(x)=x+C′−C
但可能导致某些位于Lab空间边界上的像素可能越界,超过色域范围。如下图(b)中下方的情况:
x
0
=
x
+
C
′
−
C
x_0 = x + C' - C
x0=x+C′−C. 为此,作者提出如下的解决方案。
首先找到射线
C
C
′
CC'
CC′ 与ab空间边界的交点
C
b
C_b
Cb;
对于任意像素点
x
x
x, 我们令
x
0
=
x
+
C
′
−
C
x_0 = x + C' - C
x0=x+C′−C, 根据得到的值是否越过色域分为两种情况:
- 如果 x 0 x_0 x0 位于色域内,这里称为far case。 那么通过二分搜索确定射线 x x 0 xx_0 xx0 与ab空间边界的交点 x b x_b xb;
- 如果 x 0 x_0 x0 位于色域外,这里称为near case。 那么通过二分搜索确定射线 C ′ x 0 C'x_0 C′x0 与ab空间边界的交点 x b x_b xb;
针对任意的像素点
x
x
x, 重着色后的颜色
x
′
x'
x′满足:
(1)
x
′
x'
x′ 位于线段
x
x
b
xx_b
xxb上;(2)如下等式:
∣
∣
x
′
−
x
∣
∣
∣
∣
C
′
−
C
∣
∣
=
m
i
n
(
1
,
∣
∣
x
b
−
x
∣
∣
∣
∣
C
b
−
C
∣
∣
)
(2)
\frac{||x'-x||}{||C'-C||} = min(1,\frac{||x_b-x||}{||C_b-C||}) \tag2
∣∣C′−C∣∣∣∣x′−x∣∣=min(1,∣∣Cb−C∣∣∣∣xb−x∣∣)(2)
因此,进一步分两种情况:
(1) 如果
∣
∣
x
b
−
x
∣
∣
>
∣
∣
C
b
−
C
∣
∣
||x_b-x|| > ||C_b-C||
∣∣xb−x∣∣>∣∣Cb−C∣∣, 则:
∣
∣
x
′
−
x
∣
∣
∣
∣
C
′
−
C
∣
∣
=
m
i
n
(
1
,
∣
∣
x
b
−
x
∣
∣
∣
∣
C
b
−
C
∣
∣
)
=
1
\frac{||x'-x||}{||C'-C||} = min(1,\frac{||x_b-x||}{||C_b-C||})=1
∣∣C′−C∣∣∣∣x′−x∣∣=min(1,∣∣Cb−C∣∣∣∣xb−x∣∣)=1
x
′
=
x
+
C
′
−
C
(3)
x'=x+C'-C\tag3
x′=x+C′−C(3)
(2) 如果
∣
∣
x
b
−
x
∣
∣
<
∣
∣
C
b
−
C
∣
∣
||x_b-x|| < ||C_b-C||
∣∣xb−x∣∣<∣∣Cb−C∣∣, 则:
∣
∣
x
′
−
x
∣
∣
∣
∣
C
′
−
C
∣
∣
=
m
i
n
(
1
,
∣
∣
x
b
−
x
∣
∣
∣
∣
C
b
−
C
∣
∣
)
=
∣
∣
x
b
−
x
∣
∣
∣
∣
C
b
−
C
∣
∣
\frac{||x'-x||}{||C'-C||} = min(1,\frac{||x_b-x||}{||C_b-C||})=\frac{||x_b-x||}{||C_b-C||}
∣∣C′−C∣∣∣∣x′−x∣∣=min(1,∣∣Cb−C∣∣∣∣xb−x∣∣)=∣∣Cb−C∣∣∣∣xb−x∣∣
∣
∣
x
′
−
x
∣
∣
=
∣
∣
x
b
−
x
∣
∣
∣
∣
C
′
−
C
∣
∣
∣
∣
C
b
−
C
∣
∣
||x'-x|| = ||x_b-x||\frac{||C'-C||}{||C_b-C||}
∣∣x′−x∣∣=∣∣xb−x∣∣∣∣Cb−C∣∣∣∣C′−C∣∣
x
′
=
x
+
(
x
b
−
x
)
∣
∣
C
′
−
C
∣
∣
∣
∣
C
b
−
C
∣
∣
(4)
x' = x + (x_b-x)\frac{||C'-C||}{||C_b-C||} \tag4
x′=x+(xb−x)∣∣Cb−C∣∣∣∣C′−C∣∣(4)
其实我没有完全理解这样做的深意,我不知道对于near case,
x
b
x_b
xb 取为
x
x
0
xx_0
xx0 与ab空间边界的交点 会如何。
需要注意的是:第2部分计算颜色转换时,作者的实现跟论文描述有较大差异,最主要的区别在于:寻找边界的时候,文中是在LAB空间边界寻找 x b , C b x_b, C_b xb,Cb,而具体实现确是将LAB转换到RGB空间,然后看对应的RGB是否越界。
2.2.2 多个调色板颜色修改
如上图 (c)所示,当有多个调色板的颜色改变时,某个点最终的颜色应当受到到多个调色板颜色影响。
Δ
x
=
∑
i
k
w
i
(
x
)
f
i
(
x
)
,
a
n
d
∑
i
k
w
i
(
x
)
=
1
(5)
\Delta x= \sum^k_iw_i(x)f_i(x) , and \sum^k_i w_i(x)= 1 \tag5
Δx=i∑kwi(x)fi(x),andi∑kwi(x)=1(5)
改变后的颜色为:(这个很重要!!)
x
′
=
x
+
Δ
x
x' = x + \Delta x
x′=x+Δx
其中
k
k
k表示调色板的颜色数目。
w
i
(
x
)
w_i(x)
wi(x)表示混合权重,这里采用RBF插值的方法求这些权重:
w
i
(
x
)
=
∑
j
k
λ
i
j
ϕ
(
x
,
C
j
)
(6)
w_i(x) =\sum^k_j\lambda_{ij}\phi(x,C_j) \tag6
wi(x)=j∑kλijϕ(x,Cj)(6)
其中,
ϕ
(
x
,
C
j
)
=
e
x
p
(
−
(
x
−
C
j
)
2
/
2
σ
r
2
)
(7)
\phi(x,C_j) = exp(-(x-C_j)^2/2\sigma^2_r) \tag7
ϕ(x,Cj)=exp(−(x−Cj)2/2σr2)(7)
可以看到每个 w i w_i wi关联到 k k k个 λ \lambda λ: λ i 1 , λ i 2 , . . . . λ i k \lambda_{i1},\lambda_{i2},....\lambda_{ik} λi1,λi2,....λik. 因此,所有的权重关联到 k 2 k^2 k2个 λ \lambda λ。因此要求 w w w,实际需要求解一个 k × k k\times k k×k的 λ \lambda λ矩阵。
给定原始调色板:
C
1
,
C
2
,
.
.
.
C
k
{C_1,C_2,...C_k}
C1,C2,...Ck,以及修改后的调色板:
C
1
′
,
C
2
′
,
.
.
.
C
k
′
{C'_1,C'_2,...C'_k}
C1′,C2′,...Ck′.
那么对于任意的颜色
x
=
C
i
x = C_i
x=Ci,则一定能精确的转变为
x
′
=
C
i
′
x' = C'_i
x′=Ci′ (插值性质1定义)。这样的话,
x
=
C
i
x = C_i
x=Ci将只受到调色板
C
i
→
C
i
′
C_i \to C'_i
Ci→Ci′的影响,而不会受到其他调色板的颜色变化影响,因此
w
i
=
1
,
w
j
≠
i
=
0
w_i = 1, w_{j \neq i} = 0
wi=1,wj=i=0. 为此,我们可以将公式(6)展开成矩阵乘积的形式,给出如下的线性方程组求解
λ
\lambda
λ矩阵:
[ λ 11 λ 12 . . . λ 1 k λ 21 λ 22 . . . λ 2 k . . . . λ k 1 λ k 2 . . . λ k k ] [ d 11 d 21 . . . d k 1 d 12 d 22 . . . d k 2 . . . . d 1 k d 2 k . . . d k k ] = [ 1 0 . . . 0 0 1 . . . 0 . . 1 . 0 0 . . . 1 ] (8) \begin{bmatrix} \lambda_{11} & \lambda_{12}&... & \lambda_{1k}\\ \lambda_{21} & \lambda_{22}&... & \lambda_{2k}\\ . & .& . & .\\ \lambda_{k1} & \lambda_{k2}&... & \lambda_{kk} \end{bmatrix} \begin{bmatrix} d_{11} & d_{21}&... & d_{k1}\\ d_{12} & d_{22}&... & d_{k2}\\ . & .& . & .\\ d_{1k} & d_{2k}&... & d_{kk} \end{bmatrix}= \begin{bmatrix} 1 & 0 &... & 0\\ 0 & 1 &... & 0\\ . & .& 1 & .\\ 0 & 0 &... & 1 \end{bmatrix}\tag8 λ11λ21.λk1λ12λ22.λk2..........λ1kλ2k.λkk d11d12.d1kd21d22.d2k..........dk1dk2.dkk = 10.001.0......1...00.1 (8)
其中, d i j = ϕ ( C i , C j ) d_{ij} =\phi(C_i,C_j) dij=ϕ(Ci,Cj), 上式右侧为单位矩阵,实际要求的 λ \lambda λ矩阵等于上述第2个矩阵的逆。
3、一些加速方法
文章并不是直接计算所有像素的插值,而是借助之前的网格,先计算所有网格点的颜色,然后再通过三线性插值的方法对网格内部的像素点插值。
实现效果和对比
注:OpenCV中RGB2LUV的方法:
Vec3d RGB2LUV(Vec3d c) {
Vec3f c_f(c[0], c[1], c[2]);
Mat3f rgb(c_f), luv;
cvtColor(rgb, luv, cv::COLOR_RGB2Luv);
Vec3f LUV(luv.at<Vec3f>(0, 0)[0], luv.at<Vec3f>(0, 0)[1], luv.at<Vec3f>(0, 0)[2]);
return Vec3d(LUV[0], LUV[1], LUV[2]);
}