经典图割算法中图的构建及实现:Graph-Cut

经典图割算法中图的构建及实现之graph-cut

本文目的:

讲解目前典型的3种图割算法:graph-cut、grab-but、one-cut。本文主要讲解graph-cut的方法在应用时,准则函数与图构建关系,如何构建图,以及如何代码实现图的构建。图割的原理网上文章和论文已介绍比较详细,不再详细介绍。

一.graph-cut:准则函数

该方法可谓是图割方法的开山鼻祖。该方法的准则函数如下:

R(A)是先验惩罚项,B(A)是区域相似度惩罚项,\lambda是平衡因子。

该准则函数意义:同类间,颜色差别小;异类间,颜色差别大。原则上该准则可解决图像任意类分割,并且一定是有全局最优解得,但在无种子点的超过2分类的问题时,该优化是个NP难问题,需要进行指数级的比较才能获得最优解,无工程价值。

二.Graph-cut:图的建立

1.术语:

  1. )与S和T链接的边叫t-link(红线与绿线),领域之间的链接边叫n-link(黑线)。其中红线进一步称为s-t-link,绿线进一步称为t-t-link。
  2. )黑线的权值对应的是B(A)项,红线与绿线的权值对应的是R(A)项。
  3. )权值用w表示。
  4. )蓝色节点表示类别标志节点,S表示正类类标节点,T表示负类类标节点,黄色节点是图像中的每一个像素点。

       最终通过求最小割之后,与节点S相连的所有黄色节点(图像像素点)属于一类,同理与节点T相连的所有黄色节点属于另一类。两类被最小割割开,割值即是准则函数的值。

2.图的建立

       拿到待分割的图像后,图的节点与边已确定,即图的形状已确定下来。仅仅需要做的就是给图中所有边赋值相应的权值。

图中的边有3种情况:种子点的t-link;非种子点的t-link;像素领域关系的n-link。接下来将说明每一种边的权值取值。

1).种子点t-link权值:种子点认为是硬约束,其用户预设类别后,类别不会随分割算法而改变。

a.对于正类别种子点,s-t-link必须保留,t-t-link必须割去。工程中,通过将s-t-link权值设置为超级大值,t-t-link设置为0。保证一定仅仅割去t-t-link,否则一定不是最小割,因为当前w(s-t-link)权值是超级大值,割去这条边的代价一定是最大的。

           b.反之同理。

2).非种子点的t-link权值:通过正负类种子点,我们能建立2类的颜色直方图。将直方图归一化成概率密度函数,定义为H_F,H_B。其中s-t-link权值为-ln(H_F(x)),t-t-link权值为-ln(H_B(x)),x为该像素点颜色值。

3).n-link权值:n-link用于度量相邻像素点之间颜色的差异性。设一对相邻点Pi,Pj,则n-link(Pi-Pj)的权值w等于:

w = \exp ( - \frac{​{​{​{({x_i} - {x_j})}^2}}}{​{2{\sigma ^2}}}) \cdot \frac{1}{​{dist({P_i},{P_j})}}

         其中,dist()是距离函数,表示点之间的图像距离。即4领域下,所以领域点距离均为1,;8领域下,对角像素点距离为\sqrt 2;在5*5领域下,对角像素点距离为\2\sqrt 2

         设种子点的超级大值是1000,\sigma=1。图像是3*2的灰度图,数字表示灰度值,红色和蓝色节点表示用户选择的正负种子点。当然种子点过少时,计算的H_FH_B可能不准,可将种子点附近的像素点也算入先验直方图中,往往可以取得更好效果

  

       如上图所示,将所有边的权值赋值后,图就建立完毕。剩余则直接运用最小割算法即可求解。最小割算法有很多,包括graph-cut作者提出的快速算法An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision。Opencv即采用该算法计算最小割。

3.建立图的代码实现:

void GCApplication::graphConstruct(const Mat& img, GCGraphMy<double>& graph)
{
    lambda = 1000;//极大值
    beta = calcBeta(*(this->image));

    Mat leftW, upleftW, upW, uprightW;
    calcNWeights(img, leftW, upleftW, upW, uprightW, beta, gamma);

    int vtxCount = img.cols*img.rows,
        edgeCount = 2 * (4 * img.cols*img.rows - 3 * (img.cols + img.rows) + 2);

    fillSeedToMask(this->mask);
    calSeedPHist(img, this->mask);

    graph.create(vtxCount, edgeCount);
    Point p;
    double a = 1.5;
    for (p.y = 0; p.y < img.rows; p.y++)
    {
        for (p.x = 0; p.x < img.cols; p.x++)
        {
            // add node
            int vtxIdx = graph.addVtx();
            Vec3b color = img.at<Vec3b>(p);

            // set t-weights
            double fromSource, toSink;
            if (mask.at<uchar>(p) == 0)//非种子点的t-link
            {
                fromSource = -a*log(calBgdPrioriCost(color));
                toSink = -a*log(calFgdPrioriCost(color));
            }
            else if (mask.at<uchar>(p) == MASK_BG_COLOR)//负种子点t-link
            {
                fromSource = 0;
                toSink = lambda;
            }
            else if (mask.at<uchar>(p) == MASK_FG_COLOR) //正种子点t-link
            {
                fromSource = lambda;
                toSink = 0;
            }
            graph.addTermWeights(vtxIdx, fromSource, toSink);

            // set n-link-weights,每个点只需要与左上4个点进行边连接即可,这样可以不重复的添加所有的N-8-edge
            if (p.x>0)
            {
                double w = leftW.at<double>(p);
                graph.addEdges(vtxIdx, vtxIdx - 1, w, w);
            }
            if (p.x>0 && p.y>0)
            {
                double w = upleftW.at<double>(p);
                graph.addEdges(vtxIdx, vtxIdx - img.cols - 1, w, w);
            }
            if (p.y>0)
            {
                double w = upW.at<double>(p);
                graph.addEdges(vtxIdx, vtxIdx - img.cols, w, w);
            }
            if (p.x<img.cols - 1 && p.y>0)
            {
                double w = uprightW.at<double>(p);
                graph.addEdges(vtxIdx, vtxIdx - img.cols + 1, w, w);
            }
        }
    }
}

 

2)分割效果:

       从实验结果来看,图中如果有多个类,该方法一般不能取得较好结果。对于2类的图像,该方法效果很好,最后仅需再加上一些空洞填补、小区域过滤等操作就好。

3)完整资源代码:https://download.csdn.net/download/jy02660221/10761799,最后鄙视一下CSDN最低下载分数限制。

  • 40
    点赞
  • 147
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Bellman-Ford算法是一种用于解决单源最短路径问题的算法,它可以处理有负权边的图。下面是一个应用实例及代码: 假设有一个有向图,其中包含5个节点和7条边,如下所示: ![image](https://img-blog.csdn.net/20180527152938277?watermark/2/text/aHRcDovL2Jsb2cuY3Nkbi5uZXQvY2hhdGJhY2s=/font/5a6L5L2T/fontsize/400/fill/IJBQkFCMA==/dissolve/70/q/80) 我们要求从节点1到其他节点的最短路径,使用Bellman-Ford算法可以得到以下结果: 节点1到节点2的最短路径为:1 -> 2,路径长度为2 节点1到节点3的最短路径为:1 -> 3,路径长度为4 节点1到节点4的最短路径为:1 -> 2 -> 4,路径长度为6 节点1到节点5的最短路径为:1 -> 2 -> 4 -> 5,路径长度为8 下面是使用C语言实现Bellman-Ford算法的代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_NODES 5 #define MAX_EDGES 7 struct Edge { int src, dest, weight; }; struct Graph { int V, E; struct Edge* edge; }; struct Graph* createGraph(int V, int E) { struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph)); graph->V = V; graph->E = E; graph->edge = (struct Edge*) malloc(E * sizeof(struct Edge)); return graph; } void printArr(int dist[], int n) { printf("Vertex Distance from Source\n"); for (int i = ; i < n; ++i) printf("%d \t\t %d\n", i+1, dist[i]); } void BellmanFord(struct Graph* graph, int src) { int V = graph->V; int E = graph->E; int dist[V]; for (int i = ; i < V; i++) dist[i] = INT_MAX; dist[src] = ; for (int i = 1; i <= V-1; i++) { for (int j = ; j < E; j++) { int u = graph->edge[j].src; int v = graph->edge[j].dest; int weight = graph->edge[j].weight; if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) dist[v] = dist[u] + weight; } } printArr(dist, V); } int main() { struct Graph* graph = createGraph(MAX_NODES, MAX_EDGES); graph->edge[].src = 1; graph->edge[].dest = 2; graph->edge[].weight = 2; graph->edge[1].src = 1; graph->edge[1].dest = 3; graph->edge[1].weight = 4; graph->edge[2].src = 2; graph->edge[2].dest = 3; graph->edge[2].weight = 1; graph->edge[3].src = 2; graph->edge[3].dest = 4; graph->edge[3].weight = 3; graph->edge[4].src = 3; graph->edge[4].dest = 4; graph->edge[4].weight = 2; graph->edge[5].src = 3; graph->edge[5].dest = 5; graph->edge[5].weight = 4; graph->edge[6].src = 4; graph->edge[6].dest = 5; graph->edge[6].weight = 3; BellmanFord(graph, ); return ; } ``` 输出结果为: ``` Vertex Distance from Source 1 2 2 3 4 4 6 5 8 ``` 以上就是Bellman-Ford算法的一个应用实例及代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值