像素间相似性
中心像素和邻域像素的相似性有两个部分:位置相似和像素值相似 位置相似性:两个像素位置越接近,我们认为越相似。如以C为中心的邻域内,A和C的距离是
d
i
s
=
2
dis=\sqrt2
dis=2
?,B和C的距离是
d
i
s
=
1
dis=1
dis=1,相似性可以用
s
p
o
s
=
1
d
i
s
s_{pos}=\frac{1}{dis}
spos?=dis1?来衡量
s
p
o
s
s_{pos}
spos?值越大,越两个像素越相似
像素值相似:两个像素颜色越接近,我们认为越相似。如A和C的L2距离是
d
L
2
=
∣
v
(
a
)
?
v
(
b
)
∣
2
d_{L2}=|v(a)-v(b)|^2
dL2?=∣v(a)?v(b)∣2。这是一种简单的衡量方式,但它是值越小像素越相似,我们可以改进一下:
1、计算取反
?
d
L
2
=
?
∣
v
(
a
)
?
v
(
b
)
∣
2
-d_{L2}=-|v(a)-v(b)|^2
?dL2?=?∣v(a)?v(b)∣2,这样就变成了值越大越相似 2、再取指数
e
?
d
L
2
e^{-d_{L2}}
e?dL2?,这样变化范围就变成[0,1]区间了,但随之而来的问题是,随着变化量越大,计算结果差别会越小 3、再加一项调整指数范围
e
?
β
×
d
L
2
e^{-\beta \times d_{L2}}
e?β×dL2?,采用很小的β参数,可以解决2的问题
令像素相似性为
s
p
i
x
=
e
?
β
×
d
L
2
s_{pix}=e^{-\beta \times d_{L2}}
spix?=e?β×dL2?,值越大,越两个像素越相似
我们把两则综合一下,由于我们知道两者的范围其实差不多,所以直接相乘即可:
两个像素相似性计算公式:
s
=
s
p
o
s
×
s
p
i
x
s=s_{pos}\times s_{pix}
s=spos?×spix?
图能量
图中的边分为两类:n-links(黄色部分)和t-links(蓝色和橙色部分)。
n-links的n是neighbour的缩写,表示节点间的连接边,衡量节点不连续性(两节点差异越大,惩罚值越大);
t-links的t是terminal的缩写,表示节点与终点(terminal)相连,衡量节点赋予所连接终点须耗费的代价值。
所以图的能量可以由n-links的惩罚值,和t-links的代价值描述:
E
=
∑
V
(
p
,
q
)
+
∑
D
(
p
)
E=\sum V(p,q) +\sum D(p)
E=∑V(p,q)+∑D(p)
最大流最小割
通过切断图的某些边,将图划分为S,T两个集合。并使得这些被切断的边的代价总和最小(即S,T集合的能量总和最大)。
也就是一个图的没分割之前,具有一定能量,我们要使得分割以后,两个图的能量之和最大
原理详细参考装逼之二 最小割与最大流(mincut & maxflow) b站有个课程讲的很清晰,推荐观看:理解最大流最小割定理
grabcut源码分析
void cv_grabCut(InputArray _img, InputOutputArray _mask, Rect rect,
InputOutputArray _bgdModel, InputOutputArray _fgdModel,
int iterCount, int mode)
{
Mat img = _img.getMat();
Mat& mask = _mask.getMatRef();
Mat& bgdModel = _bgdModel.getMatRef();
Mat& fgdModel = _fgdModel.getMatRef();
if (img.empty())
CV_Error(CV_StsBadArg, "image is empty");
if (img.type() != CV_8UC3)
CV_Error(CV_StsBadArg, "image mush have CV_8UC3 type");
GMM bgdGMM(bgdModel), fgdGMM(fgdModel);
Mat compIdxs(img.size(), CV_32SC1);
if (mode == GC_INIT_WITH_RECT || mode == GC_INIT_WITH_MASK)
{
if (mode == GC_INIT_WITH_RECT)
initMaskWithRect(mask, img.size(), rect);
else
checkMask(img, mask);
initGMMs(img, mask, bgdGMM, fgdGMM);
}
if (iterCount <= 0)
return;
if (mode == GC_EVAL)
checkMask(img, mask);
const double gamma = 50;
const double lambda = 9 * gamma;
const double beta = calcBeta(img);
Mat leftW, upleftW, upW, uprightW;
calcNWeights(img, leftW, upleftW, upW, uprightW, beta, gamma);
for (int i = 0; i < iterCount; i++)
{
GCGraph<double> graph;
assignGMMsComponents(img, mask, bgdGMM, fgdGMM, compIdxs);
learnGMMs(img, mask, compIdxs, bgdGMM, fgdGMM);
constructGCGraph(img, mask, bgdGMM, fgdGMM, lambda, leftW, upleftW, upW, uprightW, graph);
estimateSegmentation(graph, mask);
}
}
|