基于图的图像分割方法 Efficient Graph-Based Image Segmentation

Efficient Graph-Based Image Segmentation 前一段时间在看Selective Search的 阅读详情

本文概述了论文《Efficient Graph-Based Image Segmentation》所述的基于图的图像分割方法。

参考:

  1. 《Efficient Graph-Based Image Segmentation》代码及论文 http://cs.brown.edu/people/pfelzens/segment/

1、快速开始

(1) 作者给出的C++代码

首先下载《Efficient Graph-Based Image Segmentation》的代码 http://cs.brown.edu/people/pfelzens/segment/,解压下载的文件segment.zip,执行make编译之后,就可以进行图像分割了。分割命令为 “segment sigma k min input output”,这里的输入输出是ppm格式的图像,ppm格式的介绍可以访问这里https://blog.youkuaiyun.com/kinghzkingkkk/article/details/70226214。

先把图片转为ppm格式

import cv2
im = cv2.imread("car.png")
cv2.imwrite("car.ppm",im)

然后执行下面的命令完成图像分割。 

./segment 0.5 500 20 car.ppm car_seg.ppm

结果如下图所示,左侧是原图,右侧是分割的图。

(2) skimage中的python接口 

# encoding:utf-8

import cv2
from skimage import segmentation
import skimage
import numpy

im_orig = cv2.imread("len_std.jpg")
scale = 500
sigma = 0.5
min_size = 20

im_orig = numpy.array(im_orig)

im_mask = segmentation.felzenszwalb(skimage.util.img_as_float(im_orig),
                                    scale=scale,
                                    sigma=sigma,
                                    min_size=min_size)

rect = {}
min_l = numpy.min(im_mask)
max_l = numpy.max(im_mask)
for l in range(int(min_l),int(max_l)+1):
    y_arr, x_arr = numpy.where(im_mask==l)
    rect[l] = {"min_x": x_arr.min(), "min_y": y_arr.min(),
               "max_x": x_arr.max(), "max_y": y_arr.max(), "labels": l}

for l, v in rect.items():
    area = (v["max_x"]-v["min_x"]) * (v["max_y"]-v["min_y"])
    ratio = (v["max_x"]-v["min_x"]) / (v["max_y"]-v["min_y"])
    # 分割的区域太多,这里画出像素数量在(5500,6000),并且宽高比在(0.7,1.3)的区域边框
    if area>5500<6000 and ratio>0.7<1.3:
        cv2.rectangle(im_orig,(v["min_x"],v["min_y"]),(v["max_x"],v["max_y"]),color=(0,255,0),thickness=2)


# 随机上色
rgb = {}
for l in rect.keys():
    # randint参数high为None的时候,生成的是[0,low)的随机整数
    rgb[l] = numpy.random.randint(256,size=(3,))
im_tintage = numpy.empty(shape=im_orig.shape, dtype=numpy.uint8)
for height in range(im_orig.shape[0]):
    for row in range(im_orig.shape[1]):
        im_tintage[height, row, :] = rgb[im_mask[height, row]]

gap = 255*numpy.ones([im_orig.shape[0],50,3],dtype=numpy.uint8)
im_display = numpy.hstack([im_orig, gap,im_tintage])

cv2.imshow("",im_display)
cv2.waitKey(0)

结果如图所示,这里没有画出所有的边框,可以看到中间的边框近似的框住了人脸,但是从右边的染色图来看整个人脸并不是被分割在同一个区域中。

 

2、算法流程

 

在上述的算法流程中,G是一个无向图,图G中每个顶点是一个像素点,边E是像素点之间的不相似度,即第3步中w(o_q)。但是实际中不会去计算所有像素点对之间的不相似度,论文中讨论了两种构建图G的情形,grid-graph和Nearest Neighbor Graphs。

第一种情形是grid-graph,这种情况下只计算每个像素点周围8个点的不相似度,因此图像上相近的像素点在图G上也相近。在灰度图中,按照下式来计算两个像素点的不相似度。

w((v_i,v_j))=|I(p_i)-I(p_j)|

其中I(p_i)是像素点p_i的灰度值。如果是彩色图像的话,论文中分别在RGB这3个通道上进行分割,然后将3个通道的分割结果相交,也就是说,在3个通道图里,某两个像素点都属于同一个component,那这两个像素点就属于同一个component。

第二种情形是Nearest Neighbor Graphs,这种情形下将像素点映射到特征空间中再去计算不相似度,论文中提到的方法是将像素点(r,g,b)映射到(x,y,r,g,b),其中x和y是像素点的坐标,以特征空间中的欧式距离作为图G中两个点之间的不相似度。通过ANN算法(Approximate nearest neighbor searching)可以找到每个像素点相邻的点,那么每个像素点要和几个相邻点计算不相似度呢?论文中给出了两个思路,一个是固定相邻点的数量,另外也可以设定一个半径,将此半径范围内的相邻点纳入计算。

在Nearest Neighbor Graphs情形下,图像上离得较远的两个像素点也可能是图G中相邻的点(假如这两个像素点的颜色特别相似的话),因此图像上不相邻的区域可能会划分到同一个component,这在第一种情形grid-graph中是不会出现的。

在分割开始的时候把每个像素点当作一个component,然后逐渐融合。融合过程中,对于某条边,如果这条边的两个顶点不在同一个component,将这两个component记为C_1和C_2,此时如果这条边权值小于或者等于阈值MInt(C_1,C_2),则将这两个点所属的component进行融合,阈值的计算公式如下:

MInt(C_1,C_2)=min(Int(C_1)+\tau(C_1),Int(C_2)+\tau(C_2))

上式中的Int(C_1)是C_1最小生成树中最大的权值,函数\tau(C)计算方法如下,其中k是一个超参数。

\tau (C) = \frac{k}{|C|}

3、源码分析

这一部分对http://cs.brown.edu/people/pfelzens/segment/ 给出的源码进行分析,分割的主要过程写在segment-graph.h中,下面对主要代码段进行解释。

(1) 输入

image<rgb> *segment_image(image<rgb> *im, float sigma, float c, int min_size,
                          int *num_ccs)

(2) 高斯模糊

进行图像分割之前,首先将图像进行平滑(高斯模糊),论文中对此解释说:

We always use a Gaussian with σ = 0.8, which does not produce any visible change to the image but helps remove artifacts.

方差为0.8的高斯模糊示意图如下,左边是原图,右边是平滑的图,细节虽然不是很清楚了,但是整体的色块没有受影响(直观的看这样更容易进行分割)。

  int width = im->width();
  int height = im->height();
  // 将rgb三个通道分离
  image<float> *r = new image<float>(width, height);
  image<float> *g = new image<float>(width, height);
  image<float> *b = new image<float>(width, height);

  // smooth each color channel
  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      imRef(r, x, y) = imRef(im, x, y).r;
      imRef(g, x, y) = imRef(im, x, y).g;
      imRef(b, x, y) = imRef(im, x, y).b;
    }
  }
  // 这里的smooth方法实现了高斯模糊
  image<float> *smooth_r = smooth(r, sigma);
  image<float> *smooth_g = smooth(g, sigma);
  image<float> *smooth_b = smooth(b, sigma);
  delete r;
  delete g;
  delete b;

(3) 建立连接图G

连接图中计算了每个像素点相邻的8个点之间的连接权值,边缘点有5个相邻点,角点有3个相邻点。

// build graph
  edge *edges = new edge[width * height * 4];
  int num = 0;
  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      // 对所有不在最后一列的点,计算该点和右侧点之间的不相似度
      if (x < width - 1)
      {
        edges[num].a = y * width + x;
        edges[num].b = y * width + (x + 1);
        edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x + 1, y);
        num++;
      }
      // 对所有不在最后一行的点,计算该点和正下方点之间的不相似度
      if (y < height - 1)
      {
        edges[num].a = y * width + x;
        edges[num].b = (y + 1) * width + x;
        edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x, y + 1);
        num++;
      }
      // 对所有不在最后一行也不在最后一列的点,计算该点和右下方点之间的不相似度
      if ((x < width - 1) && (y < height - 1))
      {
        edges[num].a = y * width + x;
        edges[num].b = (y + 1) * width + (x + 1);
        edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x + 1, y + 1);
        num++;
      }
      // 对所有不在最后一列也不在第一行的点,计算该点和右上方点之间的不相似度
      if ((x < width - 1) && (y > 0))
      {
        edges[num].a = y * width + x;
        edges[num].b = (y - 1) * width + (x + 1);
        edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x + 1, y - 1);
        num++;
      }
    }
  }
  delete smooth_r;
  delete smooth_g;
  delete smooth_b;

不相似度计算方法如下,这里是用rgb空间的欧式距离作为两个像素点的不相似度,即考虑了像素颜色的相似性。

// dissimilarity measure between pixels
static inline float diff(image<float> *r, image<float> *g, image<float> *b,
                         int x1, int y1, int x2, int y2)
{
  return sqrt(square(imRef(r, x1, y1) - imRef(r, x2, y2)) +
              square(imRef(g, x1, y1) - imRef(g, x2, y2)) +
              square(imRef(b, x1, y1) - imRef(b, x2, y2)));
}

(4) 分割

图G建好后,利用segment_graph方法分割图G,该方法定义在segment-graph.h中。

// segment
  universe *u = segment_graph(width * height, num, edges, c);

segment_graph方法如下所示,该方法将图G中所有的顶点分为多个disjoint-set,组成一个disjoint-set forest(由于每个disjoint-set是一个树结构,多个树就组成了一个森林),然后迭代融合,在disjoint-set forest的实现中使用了union by rank(按秩合并)和path compression(路径压缩)两种优化方法。

/*
 * Segment a graph
 *
 * Returns a disjoint-set forest representing the segmentation.
 *
 * num_vertices: number of vertices in graph.
 * num_edges: number of edges in graph
 * edges: array of edges.
 * c: constant for treshold function.
 */
universe *segment_graph(int num_vertices, int num_edges, edge *edges,
                        float c)
{
  // sort edges by weight
  std::sort(edges, edges + num_edges);

  // make a disjoint-set forest 并查集
  universe *u = new universe(num_vertices);

  // init thresholds
  float *threshold = new float[num_vertices];
  for (int i = 0; i < num_vertices; i++)
    // 论文中的阈值包括两部分 Int(C)+\tau(C)
    // 因为开始的时候每个集合只有一个元素,因此Int(C)=0
    // 下面的c就是论文中的k,由于目前一个点是一类,因此集合元素数量|C|=1
    // 所以初始的阈值就是这里的c(论文中的k)
    threshold[i] = THRESHOLD(1, c); 

  // for each edge, in non-decreasing weight order...
  for (int i = 0; i < num_edges; i++)
  {
    edge *pedge = &edges[i];

    // components conected by this edge
    int a = u->find(pedge->a); // 当前这条边的起始点所在集合的root节点序号
    int b = u->find(pedge->b); // 当前这条边的终止点所在集合的root节点序号
    if (a != b)
    {
      if ((pedge->w <= threshold[a]) &&        // 权重w是按照欧式距离计算的
          (pedge->w <= threshold[b]))
      {
        u->join(a, b);                         // 将a和b两个点所在的集合合并
        a = u->find(a);                        // 合并之后将新集合的root节点序号赋给a
        // 两个集合合并了之后更新阈值,阈值包括两部分 Int(C)+\tau(C)
        // 计算\tau(C)用到了此时集合的元素数量
        // Int(C)指的是最小生成树中最大的权值,由于之前将所有的权值升序排列
        // 所以当前权值pedge->w就是Int(C)
        // 理解这一部分可以参考最小生成树的Kruskal algorithm
        threshold[a] = pedge->w + THRESHOLD(u->size(a), c);
      }
    }
  }

  // free up
  delete threshold;
  return u;
}

(5) 合并较小的component

图G分割完成之后,会将包含像素较少的相邻component合并在一起,这里引入了一个超参数k,论文里对k的解释如下:

Thus k effectively sets a scale of observation, in that a larger k causes a preference for larger components.

  // post process small components
  for (int i = 0; i < num; i++)
  {
    int a = u->find(edges[i].a);
    int b = u->find(edges[i].b);
    if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size)))
      u->join(a, b);
  }
  delete[] edges;
  *num_ccs = u->num_sets();

(6) 随机上色

图像分割完成之后,对像素点进行随机上色,输出上色的图像。

image<rgb> *output = new image<rgb>(width, height);
  
  // pick random colors for each component
  rgb *colors = new rgb[width * height];
  for (int i = 0; i < width * height; i++)
    colors[i] = random_rgb();

  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      int comp = u->find(y * width + x);
      imRef(output, x, y) = colors[comp];
    }
  }

  delete[] colors;
  delete u;

  return output;

 

分割算法|Efficient Graph-Based Image Segmentation 本文的基本结构: 论文的笔记 源码的分析笔记 一 本文的分割思想——无向图分割 1 名词和符号介绍 无向图,每个像素是一个顶点, 是顶点集合,是邻边集合, (component):可以理解为分割后的每一个部分(类似region) (segmentation)是划分成components的结果,每个component 2 分割的思想 2.1 分割原则 在同一个component内相邻顶点的边的权值应尽量小,不同component内的顶点的权重较大 2.2 分割方法 定. 阅读详情

相关推荐

基于graph的图像分割算法:Efficient Graph-Based Image Segmentation

这篇文章阐述了一种传统的图像分割的方法,不需要任何的先验知识,原理也十分简单易懂。它是著名的标定框提取算法Selective search的基础。

Wallace的学习笔记 1314

图像分割—基于图的图像分割(Graph-Based Image Segmentation)

Graph-Based Segmentation 是经典的图像分割算法,作者Felzenszwalb也是提出DPM算法的大牛。该算法是基于图的贪心聚类算法,实现简单,速度比较快,精度也还行。不过,目前直接用它做分割的应该比较少,毕竟是99年的跨世纪元老,但是很多算法用它作垫脚石,比如Object Propose的开山之作《Segmentation as Selective Search for Object Recognition》就用它来产生过分割(oversegmentation)。还有的语义分割(se

Note of Transposition 4万+

《Efficient Graph-Based Image Segmentation》论文解读

文章目录Abstract1.Introduction2. Related Work3 .Graph—Based Segmentation3.1 Pairwise Region Comparison Predicate4.The Algorithm and Its Properties4.1 Implementation Issues and Running Time5.Results ofr Gr...

给时光以生命 845

信息基础2 - Selective Search

知乎:Selective Search 知乎:Selective Search   基于图的分割 Efficient Graph-Based Image Segmentation 论文详解 scikit-image开发者手册 :Felsenszwalb基于有效图的图像分割 scikit-image/skimage/segmentation/_felzenszwalb.py 源码...

weixin_41703033的博客 315

R-CNN之前的准备:Efficient Graph-Based Image Segmentation

R-CNN大致分为两个步骤,一个是使用提出检测建议(detection proposals)的算法,另一个是使用神经网络对这些检测建议进行分类和融合。在R-CNN中用到的提出检测建议的算法是选择性搜索算法(selective search),这个算法分为两个大步骤,一个是使用图像分割算法把图像分为许多的小区域,另一个是对这些区域按照一定的准则进行融合形成更大的区域,并对这些区域生成相应的包围矩形(b

xbcReal的博客 2176

SSM高校四六级报名系统:状态机事务与防重设计实战

SSM框架(Spring+SpringMVC+MyBatis)是Java Web开发的基础技术栈,其核心价值在于对传统企业级业务场景的精准建模能力。通过显式配置事务传播、手动管理DAO层SQL与连接池,开发者能深入理解状态机驱动型事务、数据库唯一约束与分页插件PageHelper等关键机制。这类系统广泛应用于教务管理、考试报名等强一致性要求场景,尤其适合需兼顾可调试性、可演示性与论文技术深度的毕业设计项目。本文聚焦高校四六级报名这一典型用例,详解tinyint状态枚举、复合唯一索引防重、DATETIME时间

weixin_34293246的博客 449

目标检测1:Selective Search

备忘,原博文作者写的非常好了,直接copy过来~~~~ 1.Selective Search for Object Recognition http://blog.youkuaiyun.com/surgewong/article/details/39316931 2.Efficient Graph-Based Image Segmentation http://blog.youkuaiyun.com/surgew...

微风❤水墨 1409

Efficient Graph-Based Image Segmentation.rar

《Efficient Graph-Based Image Segmentation》论文C++代码,交流学习下

3.1 EGBIS

目录3.1 EGBIS3.1.1 算法基础概念3.1.2 EGBIS 算法3.1.3 算法应用参考资料 3.1 EGBIS 在本章内容中,我们将介绍基于图的高效图像分割(Efficient Graph-Based Image Segmentation, EGBIS)算法,该算法是基于图的贪婪聚合算法,原理简单,速度较快,也是后期诸如选择搜索、语义分割等算法的基础。EGBIS 算法的目标是作为一种...

weixin_37179744的博客 753

基于图割理论的图像分割

图像分割的算法有很多种 基于图割理论的分割算法是利用了图的理论 构造网络图 来进行图像的分割

NeuralKG的安装及简单demo使用(浙江大学开源知识图谱表示学习工具)

记录NeuralKG的安装和使用,以及解决windows系统下的运行demo.py出现的问题。

m0_46621271的博客 1399

论文笔记:Efficient Graph-Based Image Segmentation

前言 Graph-Based Segmentation 是经典的图像分割算法,作者Felzenszwalb也是提出DPM算法的大牛。该算法是基于图的贪心聚类算法,实现简单,速度比较快,精度也还行。不过,目前直接用它做分割的应该比较少,毕竟是99年的跨世纪元老,但是很多算法用它作垫脚石,比如Object Propose的开山之作《Segmentation as Selective Search for Object Recognition》就用它来产生过分割(oversegmentation)。还有的语义分割

qq_43409114的博客 1710

从“盲控”到“预见”:聊聊永磁同步电机负载观测器(PMSM Load Observer)如何让控制更丝滑

本文深入探讨了永磁同步电机负载观测器(PMSM Load Observer)如何通过前馈补偿和转动惯量辨识技术实现智能抗扰控制。从传统‘盲控’到预见性调节,负载观测器显著减少转速波动和能耗,提升系统响应速度。文章详细解析了观测器算法架构、工程调参技巧及前沿发展趋势,为电机控制领域提供实用解决方案。

weixin_33737774的博客 406

Efficient Graph-Based Image Segmentation论文思路

Efficient Graph-Based Image Segmentation 是2004年由Felzenszwalb发表在IJCV上的一篇文章。主要介绍了基于图表示的图像分割。并且提出了一种基于贪心选择的图像分割方法,此方法能够考虑到全局特征。根据距离度量方式的不同,此算法有两种具体的实现形式。结果表明算法的运行时间接近于线性(相对于图中边的个数来说)。此算法的更重要的特性在于,在特征变化较小

doggggggggggggggggggggggggggggggggggggie的专栏 9875

VC Spyglass CDC实战:如何快速定位和解决重汇聚问题(附详细调试步骤)

本文深入探讨了在SoC设计中利用VC Spyglass进行CDC验证时,如何高效定位和解决棘手的重汇聚问题。通过实战演练,详细解析了重汇聚的本质、VC Spyglass的检测逻辑,并提供了从报告筛选、GUI原理图分析到制定设计修复或约束豁免方案的全套调试步骤,帮助工程师快速攻克这一常见验证难题。

weixin_29219539的博客 753

《使用R包ggtree进行进化树的绘制与美化》

都2023年了,才去使用Y叔早在2018年发表的进化树构建利器——ggtree。刚好最近在做基因家族分析,进化树的构建与美化确实有点麻烦,先前使用MEGA11进行了绘制,但图像简陋,于是导出了nwk文件,准备使用其他软件进行美化。这里,我使用ggtree包进行操作。

沉香best的博客 6895

图像分割之 Efficient Graph-Based Image Segmentation

图像分割历来是很难的问题,因为计算机并不是人眼,可以识别一个个物体。在他看来,一张图片,无非就是一串串数字而已。因而,如何让计算机能够自动分离出一张图片的若干个物体成为人们研究的重点,不过很可惜,所有的分割算法(不加交互)都有这样那样的缺陷,也许要再过一百年,才能发展出不加交互的良好的分割算法吧。 今天介绍一种04年发展的算法,名叫Efficient Graph-Based Image

lcbwlx的专栏 2889

智能车图像处理21-进阶篇13--正入十字补线2

这篇文章主要的正入十字补线与1基本相同,主要防止图像中双十字的问题。

taiyuezyh的博客 2032

【论文阅读】Efficient Graph-Based Image Segmentation

图像分割,最小生成树(Minimum Spanning Tree,MST),kruskal算法,prim算法

qq_34222839的博客 1339

T113-S3-RTL8211网口phy芯片调试

在嵌入式系统开发中,网络连接是至关重要的一部分。T113-S3开发板搭载了RTL8211系列的网口PHY芯片,用于实现以太网连接。在开发过程中,对于网口的稳定性和性能的调试至关重要。本篇博客将介绍如何调试T113-S3开发板中的RTL8211网口PHY芯片,以确保网络连接的正常工作。

flypig has a dream! 4614

阿里九宫格验证码 (Baxia SecCaptcha) 逆向工程与自动化求解

实现对阿里巴夏 (Baxia) 九宫格点击验证码的全自动求解模式说明依赖浏览器模式通过 Puppeteer/Playwright 控制真实浏览器,享有完整 DOM/Canvas/WASM 环境需要 Chrome/Chromium纯算模式脱离浏览器,通过 HTTP API + Node.js WASM 解密完成求解无需浏览器。

猫敷雪 379

Efficient Graph-Based Image Segmentation原文的详细解析

图像分割—基于图的图像分割(Graph-Based Image Segmentation) Reference: Efficient Graph-Based Image Segmentation,IJCV 2004,MIT Code Graph-Based Segmentation 是经典的图像分割算法,作者Felzenszwalb也是提出DPM算法的大牛。该算法是基于图

aiaiai010101的博客 7618

手把手教你学Simulink--基于光伏(PV)系统的建模与仿真场景:-不同天气条件下光伏系统的输出性能分析

本文介绍了使用Simulink和Simscape Electrical工具箱进行光伏系统建模与仿真的完整流程。内容涵盖准备工作、模型构建、仿真分析三大步骤,重点演示了如何模拟不同天气条件(光照强度、温度变化)对光伏输出性能的影响。通过添加PVArray模块、配置参数、设置环境变化条件、连接测量仪器等操作,实现了光伏系统在动态天气条件下的输出特性分析。文章还提供了仿真参数设置建议和结果分析方法,帮助用户理解环境因素与光伏性能的关联性,为光伏系统设计和优化提供参考。

MHD0815的博客 189
上一篇: 形态学算子:腐蚀,膨胀,开闭
下一篇: Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift--论文解析
__Destiny__
博客等级 码龄9年 27粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值