在 GrabCut 中更新吉布斯能量的数据项
Update data term of Gibbs energy in GrabCut
我正在研究 GrabCut 算法,我想将吉布斯能量的数据项更新为以下内容:
其中, and . p^f and p^b are the Gaussian Mixture Model (GMM) with 4 and 8 components respectively. I was going through the code of GrabCut,我可以看到平滑度是在 calcNWeights() 函数中计算的。但是我找不到的是数据项的计算。数据项在代码中是如何计算的以及如何更新它?
计算Graph Cuts中的数据项就是计算t-links。这显示在 grabcut.cpp
源代码第 465 行的源代码中 - 特别是在 constructGCGraph
函数中:https://github.com/opencv/opencv/blob/master/modules/imgproc/src/grabcut.cpp#L465。请注意,该函数的声明是 static void
,这意味着它是私有的,在 cv
工作区之外是不可见的。这意味着您无法在源代码中调用它,除非您侵入源代码本身。
换句话说:
// set t-weights
double fromSource, toSink;
if( mask.at<uchar>(p) == GC_PR_BGD || mask.at<uchar>(p) == GC_PR_FGD )
{
fromSource = -log( bgdGMM(color) );
toSink = -log( fgdGMM(color) );
}
else if( mask.at<uchar>(p) == GC_BGD )
{
fromSource = 0;
toSink = lambda;
}
else // GC_FGD
{
fromSource = lambda;
toSink = 0;
}
graph.addTermWeights( vtxIdx, fromSource, toSink );
"source"和"sink"的术语来自Graph Cuts算法,其中"source"表示前景像素,"sink"表示背景像素。另请注意,有四种类型的标签。这些在名为 cv::GrabCutClasses
的 enum
中定义(您可以在此处找到它们:https://docs.opencv.org/3.0.0/d7/d1b/group__imgproc__misc.html#gad43d3e4208d3cf025d8304156b02ba38)。
具体来说:
GC_BGD
: 一个明显的背景像素
GC_FGD
: 一个明显的前景(对象)像素
GC_PR_BGD
: 一个可能的背景像素
GC_PR_FGD
: 一个可能的前景像素
GC_BGD
和 GC_FGD
是代表用于描绘图像的前景和背景笔划的像素。这些是你指定的。对于 GC_PR_BGD
和 GC_PR_FGD
,我们因此依赖于为前景和背景构建 GMM 并计算负对数概率。这背后的本质是,如果颜色肯定属于前景,我们分配一个低成本将它绑定到汇节点,这样更有吸引力地切割这个 link 以保持源节点完好无损,从而对其进行分类作为前景像素。您可以类似地对源节点和背景执行此操作。对于那些我们明确知道它们是前景还是背景的像素,我们将高成本 lambda
应用于所需标签的 link 代表,因此相反的 link 被切割,从而保留所需的像素标签。例如,如果我们知道一个像素是背景,我们确保 t-link 到源节点的成本为零,这样我们就可以切割这个 link 没有任何后果确保像素被分配给背景。
到 "update" 数据项,这是通过在图像中指定更多的前景和背景描边来完成的,以更好地描述您要分割的对象。如果不自己破解源代码,就没有其他方法可以做到这一点。
作为最后的说明,我建议阅读有关 Graph Cuts 算法工作原理的摘要:Image segmentation with maxflow。它提供了有关 GrabCut 源代码正在做什么的更多视角。毕竟,GrabCut 只是 Graph Cuts 的更高级抽象。
我正在研究 GrabCut 算法,我想将吉布斯能量的数据项更新为以下内容:
其中,
计算Graph Cuts中的数据项就是计算t-links。这显示在 grabcut.cpp
源代码第 465 行的源代码中 - 特别是在 constructGCGraph
函数中:https://github.com/opencv/opencv/blob/master/modules/imgproc/src/grabcut.cpp#L465。请注意,该函数的声明是 static void
,这意味着它是私有的,在 cv
工作区之外是不可见的。这意味着您无法在源代码中调用它,除非您侵入源代码本身。
换句话说:
// set t-weights
double fromSource, toSink;
if( mask.at<uchar>(p) == GC_PR_BGD || mask.at<uchar>(p) == GC_PR_FGD )
{
fromSource = -log( bgdGMM(color) );
toSink = -log( fgdGMM(color) );
}
else if( mask.at<uchar>(p) == GC_BGD )
{
fromSource = 0;
toSink = lambda;
}
else // GC_FGD
{
fromSource = lambda;
toSink = 0;
}
graph.addTermWeights( vtxIdx, fromSource, toSink );
"source"和"sink"的术语来自Graph Cuts算法,其中"source"表示前景像素,"sink"表示背景像素。另请注意,有四种类型的标签。这些在名为 cv::GrabCutClasses
的 enum
中定义(您可以在此处找到它们:https://docs.opencv.org/3.0.0/d7/d1b/group__imgproc__misc.html#gad43d3e4208d3cf025d8304156b02ba38)。
具体来说:
GC_BGD
: 一个明显的背景像素GC_FGD
: 一个明显的前景(对象)像素GC_PR_BGD
: 一个可能的背景像素GC_PR_FGD
: 一个可能的前景像素
GC_BGD
和 GC_FGD
是代表用于描绘图像的前景和背景笔划的像素。这些是你指定的。对于 GC_PR_BGD
和 GC_PR_FGD
,我们因此依赖于为前景和背景构建 GMM 并计算负对数概率。这背后的本质是,如果颜色肯定属于前景,我们分配一个低成本将它绑定到汇节点,这样更有吸引力地切割这个 link 以保持源节点完好无损,从而对其进行分类作为前景像素。您可以类似地对源节点和背景执行此操作。对于那些我们明确知道它们是前景还是背景的像素,我们将高成本 lambda
应用于所需标签的 link 代表,因此相反的 link 被切割,从而保留所需的像素标签。例如,如果我们知道一个像素是背景,我们确保 t-link 到源节点的成本为零,这样我们就可以切割这个 link 没有任何后果确保像素被分配给背景。
到 "update" 数据项,这是通过在图像中指定更多的前景和背景描边来完成的,以更好地描述您要分割的对象。如果不自己破解源代码,就没有其他方法可以做到这一点。
作为最后的说明,我建议阅读有关 Graph Cuts 算法工作原理的摘要:Image segmentation with maxflow。它提供了有关 GrabCut 源代码正在做什么的更多视角。毕竟,GrabCut 只是 Graph Cuts 的更高级抽象。