二值图像求取连通域算法

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

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

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

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

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

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

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

  1 /// <summary>
  2 /// 回溯法标记连通域
  3 /// </summary>
  4 /// <param name="x">该点的横坐标</param>
  5 /// <param name="y">该点的纵坐标</param>
  6 /// <param name="isMarked">是否已经被标记过,用于记录回溯路线。默认值为false,如果该点已经被标记过,则应指定该参数为true。</param>
  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

时间: 2024-09-29 21:26:25

二值图像求取连通域算法的相关文章

小程序操作DOM以及JS求取字符串算法(前端网备份)

//js获取字符串的字节长度 //这套算法一个汉字2字节,字母符号1字节,按一行40个字节算4行 getLength:function(val){ var str = new String(val); var bytesCount = 0; for (var i = 0, n = str.length; i < n; i++) { var c = str.charCodeAt(i); if ((c >= 0x0001 && c <= 0x007e) || (0xff60

算法学习:后缀数组 height的求取

[定义] [LCP]全名最长公共前缀,两个后缀之间的最长前缀,以下我们定义 lcp ( i , j ) 的意义是后缀 i 和 j 的最长前缀 [z函数] 函数z [ i ] 表示的是,第 i 个后缀和字符串的最长前缀  [解决问题] 这两个算法都是在解决这个问题 即求后缀和字符串和后缀之间的最长公共前缀 但是有所不同的是, 后缀数组最终求出的是,字典序第 i 个后缀和第 i + 1 个后缀的最长公共前缀 z函数最终求出的是,第 i 个后缀和字符串的最长公共前缀 然后通过这个最长公共前缀求一些其他

Raising Modulo Numbers_快速幂取模算法

Description People are different. Some secretly read magazines full of interesting girls' pictures, others create an A-bomb in their cellar, others like using Windows, and some like difficult mathematical games. Latest marketing research shows, that

快速幂取余算法

下面是一个快速幂的介绍: 先贴一个秦九韶算法(Horner算法)的原理: 设有项的次函数 将前项提取公因子,得 再将括号内的前项提取公因子,得 如此反复提取公因子,最后将函数化为 令 ...... 则即为所求 下面是讲解快速幂的:(By  夜せ︱深   感谢作者) 快速幂取模算法 在网站上一直没有找到有关于快速幂算法的一个详细的描述和解释,这里,我给出快速幂算法的完整解释,用的是C语言,不同语言的读者只好换个位啦,毕竟读C的人较多~ 所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求

位运算之——按位与(&amp;)操作——(快速取模算法)

由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a.b中对应位同时为1,则对应结果位也为1. 例如: 10010001101000101011001111000 & 111111100000000 --------------------------------------------- 10101100000000 对10101100000000进行右移8位得到的是101011,这就得

递归算法——求取斐波那契数列(2)

import java.util.Scanner; /** * Created by Administrator on 14-5-13. * 改进的计算斐波那契数列的方法,利用参数,经过测试运行时间会成倍减少 测试数据n=40 * 尾递归的本质是:将单次计算的结果缓存起来,传递给下次调用,相当于自动累积. * 尾部递归是一种编程技巧.递归函数是指一些会在函数内调用自己的函数, * 如果在递归函数中,递归调用返回的结果总被直接返回,则称为尾部递归. * 尾部递归的函数有助将算法转化成函数编程语言,

高速幂取余算法

以下是一个高速幂的介绍: 先贴一个秦九韶算法(Horner算法)的原理: 设有项的次函数 将前项提取公因子,得 再将括号内的前项提取公因子.得 如此重复提取公因子,最后将函数化为 令 ...... 则即为所求 以下是解说高速幂的:(By  夜せ︱深   感谢作者) 高速幂取模算法 在站点上一直没有找到有关于高速幂算法的一个具体的描写叙述和解释,这里,我给出高速幂算法的完整解释,用的是C语言.不同语言的读者仅仅好换个位啦,毕竟读C的人较多~ 所谓的高速幂.实际上是高速幂取模的缩写,简单的说,就是高

【转】C语言快速幂取模算法小结

(转自:http://www.jb51.net/article/54947.htm) 本文实例汇总了C语言实现的快速幂取模算法,是比较常见的算法.分享给大家供大家参考之用.具体如下: 首先,所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余).在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快.计算范围更大的算法,产生了快速幂取模算法.我们先从简单的例子入手:求abmodc 算法1.直接设计这个算法: int ans = 1; for(int i =

二维数组和最大字数组求取 2

题目: 返回一个二维数组的和最大字数组的和 要求: 随机生成二维数组,要求数组元素有正有负 二维数组首尾相连 数组中连续的n(n>=1)元素组成数组的字数组 源代码: import java.util.Random; import java.util.Scanner; public class erweixunhuanzishuzu { public static void main(String[] args) { //产生随机数组 int array[][] = new int[1000][