二值图像求取连通域算法

一幅图像二值化处理后往往包含多个区域,需要通过标记把它们分别提取出来。标记分割后图像中各区域的简单而有效的方法是检查各像素与其相邻像素的连通性。

在二值图像中,背景区像素的值为0 ,目标区域的像素值为1 。假设对一幅图像从左向右,从上向下进行扫描,要标记当前正被扫描的像素需要检查它与在它之前被扫描到的若干个近邻像素的连通性。

考虑4 连通的情形。对图像进行逐像素扫描。

假如当前像素值为0 ,就移动到下一个扫描的位置。

假如当前像素值为1 ,检查它左边和上边的两个邻接像素(这两个像素一定会在当前像素之前被扫描到)。这两个像素值和标记的组合有四种情况要考虑。

  1. 他们的像素值都为0。此时给该像素一个新的标记(表示一个新的连通域的开始)。
  2. 它们中间只有一个像素值为1。此时当前像素的标记=为1的像素值的标记。
  3. 它们的像素值都为1且标记相同。此时当前像素的标记=该标记。
  4. 它们的像素值为1且标记不同。将其中的较小的值赋给当前像素。之后从另一边回溯到区域的开始像素为止。每次回溯再分别执行上述四个判断步骤。

这样即可保证所有的连通域都被标记出来。之后再通过对不同的标记赋予不同的颜色或将其加上边框即可完成标记。

1 ///
  2 /// 回溯法标记连通域
  3 ///
  4 /// 该点的横坐标
  5 /// 该点的纵坐标
  6 /// 是否已经被标记过,用于记录回溯路线。默认值为false,如果该点已经被标记过,则应指定该参数为true。
  7         private void Connect(int x, int y, bool isMarked = false)
  8         {
  9             if (x == 0 && y == 0) //mat[0, 0]
 10             {
 11                 if (f(x, y) == 1) mat[x, y] = mark; // new area
 12             }
 13
 14             else if (x != 0 && y == 0) // First Row
 15             {
 16                 if (f(x, y) == 1)
 17                 {
 18                     if (mat[x - 1, y] != 0)
 19                     {
 20                         mat[x, y] = mat[x - 1, y]; // left one
 21                         Connect(x - 1, y, true);
 22                     }
 23                     else
 24                     {
 25                         if (isMarked == false)
 26                             mat[x, y] = ++mark; // new area
 27                     }
 28                 }
 29             }
 30
 31             else if (x == 0 && y != 0) // First Column
 32             {
 33                 if (f(x, y) == 1)
 34                 {
 35                     if (mat[x, y - 1] != 0)
 36                     {
 37                         mat[x, y] = mat[x, y - 1]; // up one
 38                         Connect(x, y - 1, true);
 39                     }
 40                     else
 41                     {
 42                         if (isMarked == false)
 43                             mat[x, y] = ++mark;
 44                     }
 45                 }
 46             }
 47
 48             else if (x != 0 && y != 0) // other pixel
 49             {
 50                 if (f(x, y) == 1)
 51                 {
 52                     if (mat[x, y - 1] == 0 && mat[x - 1, y] == 0) // new area
 53                     {
 54                         if (isMarked == false)
 55                             mat[x, y] = ++mark;
 56                     }
 57                     else if (mat[x, y - 1] == 0 && mat[x - 1, y] != 0)
 58                     {
 59                         if (isMarked == false)
 60                             mat[x, y] = mat[x - 1, y];
 61                         else
 62                         {
 63                             if (mat[x - 1, y] > mat[x, y])
 64                                 mat[x - 1, y] = mat[x, y];
 65                             Connect(x - 1, y, true); // 沿x方向继续回溯
 66                         }
 67                     }
 68                     else if (mat[x, y - 1] != 0 && mat[x - 1, y] == 0)
 69                     {
 70                         if (isMarked == false)
 71                             mat[x, y] = mat[x, y - 1];
 72                         else
 73                         {
 74                             if (mat[x, y - 1] > mat[x, y])
 75                                 mat[x, y - 1] = mat[x, y];
 76                             Connect(x, y - 1, true); // 沿y方向继续回溯
 77                         }
 78                     }
 79                     else if (mat[x, y - 1] != 0 && mat[x - 1, y] != 0 && mat[x, y - 1] == mat[x - 1, y])
 80                     {
 81                         if (isMarked == false)
 82                             mat[x, y] = mat[x, y - 1];
 83                         else
 84                         {
 85                             if (mat[x, y - 1] > mat[x, y])
 86                             {
 87                                 mat[x, y - 1] = mat[x - 1, y] = mat[x, y];
 88                                 Connect(x - 1, y, true); // 遇到上边和左边都有已标记像素的情况,两边同时回溯
 89                                 Connect(x, y - 1, true);
 90                             }
 91                         }
 92
 93                     }
 94                     else if (mat[x, y - 1] != 0 && mat[x - 1, y] != 0 && mat[x, y - 1] != mat[x - 1, y])
 95                     {
 96                         mat[x, y] = Math.Min(mat[x - 1, y], mat[x, y - 1]);
 97                         mat[x - 1, y] = mat[x, y - 1] = mat[x, y]; // 直接消除等价类
 98                         Connect(x - 1, y, true);
 99                         Connect(x, y - 1, true);
100                     }
101                 }
102             }

执行效果如下:

二值图像求取连通域算法

二值化后的图像

二值图像求取连通域算法

标记了连通域后的图像

当然这个方法的一个问题是执行效率很低,对比较大的图片需要较长时间才能完成标记步骤。但准确率还是比较高的。

参考文献:

[1] 章毓晋. _图像分割._科学出版社,2001年,pp.63

[2] R.Gonzalez. 数字图像处理. 电子工业出版社, 2014年, pp.38-40

Original: https://www.cnblogs.com/ryuasuka/p/4932239.html
Author: 飞鸟_Asuka
Title: 二值图像求取连通域算法

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/610385/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Shell第四章《正则表达式》

    1.1、名词解释 正则表达式(regular expression, RE)是一种字符模式,用于在查找过程中匹配指定的字符。在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/…

    Linux 2023年6月6日
    096
  • 小记:音频格式转化ByPython(下)

    上文中我们已经大致明白了pydub库的使用方法,今天的目标是写个爬虫爬取歌曲信息。 关于网络爬虫,Python的标准库里是有相应的包的,可以直接打开:https://docs.py…

    Linux 2023年6月8日
    097
  • Java 集合框架

    一、 Collection集合 1.1 集合概述 集合:集合是java中提供的一种容器,可以用来存储多个数据。 数组的长度是固定的。集合的长度是可变的。 数组中存储的是同一类型的元…

    Linux 2023年6月7日
    076
  • Redis 经验谈

    新浪作为全世界最大的Redis用户,在开发和运维方面有非常多的经验。本文作者来自新浪,希望能为业界提供一些亲身经历,让大家少走弯路。 使用初衷 从2010年上半年起,我们就开始尝试…

    Linux 2023年5月28日
    085
  • Linux 程序后台运行 ☞ nohup

    nohup(no hang up),可以使程序在系统后台运行,即使退出终端也不受影响。 安装教程: CSDN: Linux 安装nohup 常见问题 执行jar包时: ignori…

    Linux 2023年6月14日
    0103
  • 简单动态页面的爬取

    ch6&7 动态渲染页面的爬取 ajax数据爬取 动态渲染页面的爬取 selenium的使用 splash的使用 1. ajax数据爬取 ajax数据的爬取主要依靠分析XH…

    Linux 2023年6月7日
    085
  • Django Model 如何返回空的 QuerySet

    >>> from django.contrib.auth.models import User >>> User.objects.none() …

    Linux 2023年6月7日
    080
  • [转帖]shell 学习之for语句

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年5月28日
    084
  • 博客停更通知

    楼主要忙工作了,这些工具使用相关的博客我还会维护;如有不正确的地方,欢迎大家批评指正!翻了翻这几年的内容,都是一些简单记录,且作为休闲娱乐吧。 2022年04月16日 于杭州; O…

    Linux 2023年6月14日
    064
  • Liunx-LVM创建与扩容

    LVM是 Logical Volume Manager(逻辑卷管理)的简写,它是Linux环境下对磁盘分区进行管理的一种机制,它由Heinz Mauelshagen在Linux 2…

    Linux 2023年6月8日
    085
  • UWP 在XAML设计器中使用设计时数据

    1. 功能解释 有些布局没有数据很难进行可视化。 在本文档中,我们将审查从事桌面项目的开发人员可在 XAML 设计器中模拟数据的一种方法。 此方法是使用现有可忽略的”d…

    Linux 2023年6月13日
    085
  • Ubuntu16.04部署django+nginx项目

    项目使用django+nginx部署。这个项目断断续续地部署4遍了。感觉每次部署都挺费时间的(找各种配置的资料),于是写一个博客总结一下。 安装vsftpd $ sudo apt-…

    Linux 2023年6月7日
    061
  • Xshell 设置右键粘贴功能

    参考链接:百度经验 活在当下, 从零 出发; posted @2018-04-27 09:38 半天的半天 阅读(266 ) 评论() 编辑 Original: https://w…

    Linux 2023年5月28日
    086
  • 分布式系统中数据存储方案实践

    数据膨胀的时候,必然放大细节。 一、背景简介 在项目研发的过程中,对于数据存储能力的依赖无处不在,项目初期,相比系统层面的组件选型与框架设计,由于数据体量不大,在存储管理方面通常容…

    Linux 2023年6月14日
    075
  • [ Linux ] 设置服务器开机自启端口

    https://www.cnblogs.com/yeungchie/ 需要用到的工具: crontab iptables crontab.set SHELL=/bin/bash P…

    Linux 2023年6月7日
    097
  • 了解Redis这个核心数据类型

    string 字符串 tring 类型是二进制安全的,即 string 中可以包含任何数据。 Redis 中的普通 string 采用 raw encoding …

    Linux 2023年5月28日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球