经典论文Colorization using Optimization来自于siggraph2004,论文旨在通过简单的交互实现灰度图的全局彩色化,论文短小精悍、思路新颖、实现简单、效果优良。短短6页纸的论文,引用1300+。
论文链接:https://webee.technion.ac.il/people/anat.levin/papers/colorization-siggraph04.pdf
代码链接:https://github.com/lightalchemist/colorize-image
输入:原始灰度图 (下图第一张图) + 局部着色(下图第二张图)
输出:全局着色效果(下图第三张图)
算法工作在YUV空间,简单来讲,YUV适用于电视信号传输,Y表示亮度,UV表示色度。YUV与RGB颜色空间的相互转化参见:https://www.jianshu.com/p/cf583040c930。论文核心思想:亮度近似的像素应当具有相近的颜色。
两个核心能量公式如下所示:
J ( U ) = ∑ r ( U ( r ) − ∑ s ∈ N ( r ) w r s U ( s ) ) 2 (1) J(U) = \sum\limits_r{(U(r) - \sum\limits_{s\in N(r)}{w_{rs}U(s)})^2} \tag1 J(U)=r∑(U(r)−s∈N(r)∑wrsU(s))2(1)
J ( V ) = ∑ r ( V ( r ) − ∑ s ∈ N ( r ) w r s V ( s ) ) 2 (2) J(V) = \sum\limits_r{(V(r) - \sum\limits_{s\in N(r)}{w_{rs}V(s)})^2} \tag2 J(V)=r∑(V(r)−s∈N(r)∑wrsV(s))2(2)
这里, s s s是 r r r的邻居,一般限定在一个3*3的邻域内, w r s w_{rs} wrs是像素 r r r 和 s s s 根据 Y通道计算的相似度, 并且有 ∑ s ∈ N ( r ) w r s = 1.0 \sum_{s\in N(r)}{w_{rs}} = 1.0 ∑s∈N(r)wrs=1.0, w r s w_{rs} wrs计算公式如下:
w r s = e x p ( − ( Y ( r ) − Y ( s ) ) 2 / 2 σ r 2 ) (3) w_{rs} = exp(-(Y(r)-Y(s))^2 / 2\sigma_r^2)\tag3 wrs=exp(−(Y(r)−Y(s))2/2σr2)(3)
以上述公式(1)为例,其实等效于求解一个大型线性方程数组 A x = b Ax= b Ax=b,(2)的解法是一样的,下面对这个过程进行推导。
公式(1)其实是一些平方项的和,我们令:
{ K ( 1 ) = U ( 1 ) − ∑ s ∈ N ( 1 ) w r s U ( s ) K ( 2 ) = U ( 2 ) − ∑ s ∈ N ( 2 ) w r s U ( s ) . . . K ( n ) = U ( n ) − ∑ s ∈ N ( n ) w r s U ( s ) (4) \left\{ \begin{aligned} K(1) =U(1) - \sum\limits_{s\in N(1)}{w_{rs}U(s)}\\ K(2) =U(2) - \sum\limits_{s\in N(2)}{w_{rs}U(s)}\\ ...\\ K(n) =U(n) - \sum\limits_{s\in N(n)}{w_{rs}U(s)}\\ \end{aligned} \right.\tag4 ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧K(1)=U(1)−s∈N(1)∑wrsU(s)K(2)=U(2)−s∈N(2)∑wrsU(s)...K(n)=U(n)−s∈N(n)∑wrsU(s)(4)
那么, K = [ K ( 1 ) , k ( 2 ) , . . . , K ( n ) ] T K = [K(1),k(2),...,K(n)]^T K=[K(1),k(2),...,K(n)]T 可以写成如下的线性方程组的形式 (如果 j ∉ N ( i ) , w i j = 0 j \notin N(i), w_{ij} = 0 j∈/N(i),wij=0):
K = [ 1 − w 12 . . . − w 1 n − w 21 1 . . . − w 2 n . . . . . . . . − w n 1 − w n 2 . . . 1 ] [ U ( 1 ) U ( 2 ) . . . . U ( n ) ] (5) K = \begin{bmatrix} 1 & -w_{12}&... & -w_{1n}\\ -w_{21} & 1&... & -w_{2n}\\ . & .& . & .\\ . & .& . & .\\ -w_{n1} & -w_{n2}&... & 1 \end{bmatrix} \begin{bmatrix} U(1)\\ U(2)\\ ..\\ ..\\ U(n) \end{bmatrix} \tag5 K=⎣⎢⎢⎢⎢⎡1−w21..−wn1−w121..−wn2...........−w1n−w2n..1⎦⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎡U(1)U(2)....U(n)⎦⎥⎥⎥⎥⎤(5)
公式(1)实际对应 K K K中每一项的平方和,也就是:
J ( U ) = ∑ i = 1 n K ( i ) 2 = K T K (6) J(U) = \sum^n_{i=1}K(i)^2 = K^TK \tag6 J(U)=i=1∑nK(i)2=KTK(6)
令(5)中包含权重的稀疏矩阵为 L L L, 那么上式(6)可以进一步改写为:
J ( U ) = K T K = ( L U ) T ( L U ) = U T L T L U (7) J(U) = K^TK = (LU)^T(LU) = U^TL^TLU\tag7 J(U)=KTK=(LU)T(LU)=UTLTLU(7)
其中, U = [ U ( 1 ) , U ( 2 ) , . . . U ( n ) ] T U = [U(1),U(2),...U(n)]^T U=[U(1),U(2),...U(n)]T是待求的未知量, 接下来对上式求导:
∂ J ( U ) ∂ U = 2 L T L U (8) \frac{ \partial J(U) }{ \partial U } = 2L^TLU\tag8 ∂U∂J(U)=2LTLU(8)
令导数等于0,得到关于 U U U的齐次线性方程组:
L T L U = 0 (9) L^TLU = 0\tag9 LTLU=0(9)
可以直接计算上述方程组, 但需要计算大型矩阵的乘积 L T L L^TL LTL较为不便,因此一般不这样求,而是改为计算:
L U = 0 (10) LU = 0\tag{10} LU=0(10)
那么,一个问题是(9)跟(10)是否等价呢?也就是两个方程是否同解呢?看上去等式(10)成立,等式(9)必然成立,那么(9)能否推出(10)呢? 答案是肯定的。
我们对一般的情况进行推导,即证明 (i) A T A X = 0 A^TAX = 0 ATAX=0 与 (ii) A X = 0 AX = 0 AX=0 同解。因为(ii) → \to → (i)是显然的,所以只推 (i) → \to → (ii).
A T A X = 0 → X T A T A X = 0 → ( A X ) T ( A X ) = 0 A^TAX = 0 \to X^TA^TAX = 0 \to (AX)^T(AX) = 0 ATAX=0→XTATAX=0→(AX)T(AX)=0令 Y = A X = [ y 1 , y 2 , . . . , y n ] T Y = AX = [y_1, y_2,..., y_n]^T Y=AX=[y1,y2,...,yn]T, 那么 ( A X ) T ( A X ) = y 1 2 + y 2 2 + . . . + y n 2 = 0 → y i = 0 → A X = 0 (AX)^T(AX) = y^2_1 + y^2_2 +...+y^2_n = 0 \to y_i = 0 \to AX =0 (AX)T(AX)=y12+y22+...+yn2=0→yi=0→AX=0, 所以(i)(ii)同解。
所以最终只需要计算(10)所示的线性方程组即可。
需要说明的是,线性方程组 L U = 0 LU = 0 LU=0 中的 L L L 是Laplacian矩阵,不满秩,有无穷解。 需要用户涂鸦给定一些颜色才能求解,实际上该欠约束的线性方程组 (10) 添加约束。
构建线性系统的核心代码为:
//Y:Y通道(灰度图本身的灰度值), scribbles: 用户着色的图,mask:着色区域的mask
//A:系数矩阵,bu,bv关于U/V方程组的右侧列矩阵
void setupProblem(const cv::Mat& Y, const cv::Mat& scribbles, const cv::Mat& mask,
Eigen::SparseMatrix<double, Eigen::RowMajor>& A, Eigen::VectorXd& bu,
Eigen::VectorXd& bv, double gamma)
{
typedef Eigen::Triplet<double> TD;
auto nrows = Y.rows;
auto ncols = Y.cols;
auto nPixels = nrows * ncols;
A.resize(nPixels, nPixels);
std::vector<TD> coefficients;
coefficients.reserve(nPixels * 3);
bu.resize(nPixels);
bv.resize(nPixels);
bu.setZero();
bv.setZero();
cv::Mat yuvScribbles;
cv::cvtColor(scribbles, yuvScribbles, cv::COLOR_BGR2YUV);
yuvScribbles.convertTo(yuvScribbles, CV_64FC3);
std::vector<cv::Mat> channels;
cv::split(yuvScribbles, channels);
cv::Mat& U = channels[1];
cv::Mat& V = channels[2];
// TODO: See if we can do this more efficiently using matrix reshape
std::vector<double> y, u, v;
std::vector<bool> hasColor;
to1D(Y, y);
to1D(U, u);
to1D(V, v);
to1D(mask, hasColor);
const int numNeighbors = 8;
std::vector<double> weights;
weights.reserve(numNeighbors);
std::vector<unsigned long> neighbors;
neighbors.reserve(numNeighbors);
for (auto i = 0; i < nrows; ++i) {
for (auto j = 0; j < ncols; ++j) {
unsigned long r = i * ncols + j;
getNeighbours(i, j, nrows, ncols, neighbors);
getWeights(y, r, neighbors, weights, gamma);
coefficients.push_back(TD(r, r, 1));
for (auto k = 0u; k < neighbors.size(); ++k) {
auto s = neighbors[k];
auto w = weights[k];
if (hasColor[s]) {
// Move value to RHS of Ax = b
bu(r) += w * u[s];
bv(r) += w * v[s];
} else {
coefficients.push_back(TD(r, s, -w));
}
}
}
}
A.setFromTriplets(coefficients.begin(), coefficients.end());
}
下面给一个直观的例子:我们给出一个2行3列的图像, 现在灰度图上进行了局部着色,如(2)所示,可以抽出着色部分的U通道,接下来我们的任务是求解其他部分的U值,如(3)所示,剩余的 x 2 , x 3 , , x 4 , x 6 x_2,x_3,,x_4,x_6 x2,x3,,x4,x6 是待求解的未知数。V值求解方法一样,灰度图的灰度值本身就是Y,最后我们把YUV合起来就得到了最终的彩色图。
对于 x 1 x_1 x1:因为这里已经被着色,那么必然有: x 1 = u 1 x_1 = u_1 x1=u1
对于 x 2 x_2 x2:有5个邻居,邻域U的加权平均等于 x 2 x_2 x2本身的U值, 因此 x 2 − w 21 u 1 − w 23 x 3 − w 24 x 4 − w 25 u 5 − w 26 x 6 = 0 x_2-w_{21}u_1-w_{23}x_3 -w_{24}x_4 - w_{25}u_5 -w_{26}x_6= 0 x2−w21u1−w23x3−w24x4−w25u5−w26x6=0
对于 x 3 x_3 x3:有3个邻居, x 3 − w 32 x 2 − w 35 u 5 − w 36 x 6 = 0 x_3-w_{32}x_2-w_{35}u_5 -w_{36}x_6 = 0 x3−w32x2−w35u5−w36x6=0
对于 x 4 x_4 x4:有3个邻居, 因此有: x 4 − w 41 u 1 − w 42 x 2 − w 45 u 5 = 0 x_4-w_{41}u_1-w_{42}x_2 -w_{45}u_5= 0 x4−w41u1−w42x2−w45u5=0
对于 x 5 x_5 x5:因为这里已经被着色,那么必然有: x 5 = u 5 x_5 = u_5 x5=u5
对于 x 6 x_6 x6:有3个邻居, 因此有: x 6 − w 62 x 2 − w 63 x 3 − w 65 u 5 = 0 x_6-w_{62}x_2-w_{63}x_3 -w_{65}u_5= 0 x6−w62x2−w63x3−w65u5=0
那么,上式可以写成如下的线性系统:(核心就是将上面各式的已知量全部移到右侧,未知量全在左侧)
( 1 0 0 0 0 0 0 1 − w 23 − w 24 0 − w 26 0 − w 32 1 0 0 − w 36 0 − w 42 0 1 0 0 0 0 0 0 1 0 0 − w 62 − w 63 0 0 1 ) ( x 1 x 2 x 3 x 4 x 5 x 6 ) = ( u 1 w 21 u 1 + w 25 u 5 w 35 u 5 w 41 u 1 + w 45 u 5 u 5 w 65 u 5 ) \begin{pmatrix} 1 &0 &0 &0 & 0 & 0 \\ 0 &1 &-w_{23} &-w_{24} & 0 & -w_{26}\\ 0 & -w_{32} & 1&0 &0 &-w_{36} \\ 0 & -w_{42} &0 & 1& 0 & 0\\ 0 & 0& 0& 0 & 1&0 \\ 0 & -w_{62}& -w_{63}& 0& 0& 1 \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ x_3 \\ x_4 \\ x_5 \\ x_6 \end{pmatrix}= \begin{pmatrix} u_1 \\ w_{21}u_1+w_{25}u_5 \\ w_{35}u_5 \\ w_{41}u_1+w_{45}u_5 \\ u_5 \\ w_{65}u_5 \end{pmatrix} ⎝⎜⎜⎜⎜⎜⎜⎛10000001−w32−w420−w620−w23100−w630−w2401000000100−w26−w36001⎠⎟⎟⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎜⎜⎛x1x2x3x4x5x6⎠⎟⎟⎟⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎜⎜⎛u1w21u1+w25u5w35u5w41u1+w45u5u5w65u5⎠⎟⎟⎟⎟⎟⎟⎞