OpenCV:二值图像连通区域分析与标记算法实现

本文介绍了连通域的基本概念,包括4邻接与8邻接,并详细解释了两种连通区域标记方法:两遍扫描法(Two-Pass)与种子填充法(Seed Filling)。同时提供了基于OpenCV的种子填充法实现代码。

http://www.tuicool.com/articles/Z3IVruJ

 

参考文章:

http://www.cnblogs.com/ronny/p/img_aly_01.html 

!! http://blog.youkuaiyun.com/icvpr/article/details/10259577 

 

image

编译环境:

操作系统:Win8.1  64位 

IDE平台:Visual Studio 2013 Ultimate

OpenCV:2.4.8 

一、连通域

    在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。4邻接一共4个点,即上下左右,如下左图所示。8邻接的点一共有8个,包括了对角线位置的点,如下右图所示。

image         image

   如果像素点A与B邻接,我们称A与B连通,于是我们不加证明的有如下的结论:

   如果A与B连通,B与C连通,则A与C连通。

   在视觉上看来,彼此连通的点形成了一个区域,而不连通的点形成了不同的区域。这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

   下面这符图中,如果考虑4邻接,则有3个连通区域;如果考虑8邻接,则有2个连通区域。(注:图像是被放大的效果,图像正方形实际只有4个像素)。

image

二、连通区域的标记

1)Two-Pass(两遍扫描法) 

下面给出Two-Pass算法的简单步骤: 

(1)第一次扫描:

 

访问当前像素B(x,y),如果 B(x,y) == 1:

 

 

a、如果 B(x,y) 的领域中像素值都为0,则赋予 B(x,y) 一个新的label:

 

 

 

label += 1, B(x,y)   = label;

 

 

 

b、 如果B(x,y) 的领域中有像素值 > 1的像素Neighbors:

 

1) 将 Neighbors中的最小值赋予给B(x,y):

 

 

 

B(x,y)   = min{Neighbors} 

 

2)记录 Neighbors 中各个值(label)之间的相等关系,即这些值(label)同属同一个连通区域;

 

 labelSet[i] = { label_m, .., label_n }, labelSet[i] 中的所有label都属于同一个连通区域(注:这里可以有多种实现方式,只要能够记录这些具有相等关系的label之间的关系即可) 

 

 

 

(2)第二次扫描: 

 

访问当前像素B(x,y),如果 B(x,y) > 1:

 

a、找到与label = B(x,y)同属相等关系的一个最小label值,赋予给B(x,y) ;

b、完成扫描后,图像中具有相同label值的像素就组成了同一个连通区域 。

 

 

2)Seed Filling(种子填充法)

     种子填充方法来源于计算机图形学,常用于对某个图形进行填充。思路:选取一个前景像素点作为种子,然后根据连通区域的两个基本条件(像素值相同、位置相邻)将与种子相邻的前景像素合并到同一个像素集合中,最后得到的该像素集合则为一个连通区域。

下面给出基于种子填充法的连通区域分析方法:

(1)扫描图像,直到当前像素点B(x,y) == 1:

 

a、将B(x,y)作为种子(像素位置),并赋予其一个label,然后将该种子相邻的所有前景像素都压入栈中; 

b、弹出栈顶像素,赋予其相同的label,然后再将与该栈顶像素相邻的所有前景像素都压入栈中;

c、重复b步骤,直到栈为空;

此时,便找到了图像B中的一个连通区域,该区域内的像素值被标记为label;

 

(2)重复第(1)步,直到扫描结束;

扫描结束后,就可以得到图像B中所有的连通区域;

三、程序代码

#include "stdafx.h"
#include<iostream>
#include <string>
#include <list>
#include <vector>
#include <map> #include <stack> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace std; void Seed_Filling(const cv::Mat& binImg, cv::Mat& lableImg) //种子填充法 {  // 4邻接方法  if (binImg.empty() ||   binImg.type() != CV_8UC1)  {   return;  }  lableImg.release();  binImg.convertTo(lableImg, CV_32SC1);  int label = 1;  int rows = binImg.rows - 1;  int cols = binImg.cols - 1;  for (int i = 1; i < rows-1; i++)  {   int* data= lableImg.ptr<int>(i);   for (int j = 1; j < cols-1; j++)   {    if (data[j] == 1)    {     std::stack<std::pair<int,int>> neighborPixels;     neighborPixels.push(std::pair<int,int>(i,j)); // 像素位置: <i,j>     ++label; // 没有重复的团,开始新的标签     while (!neighborPixels.empty())     {      std::pair<int,int> curPixel = neighborPixels.top(); //如果与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它      int curX = curPixel.first;      int curY = curPixel.second;      lableImg.at<int>(curX, curY) = label;      neighborPixels.pop();      if (lableImg.at<int>(curX, curY-1) == 1)      {//左边       neighborPixels.push(std::pair<int,int>(curX, curY-1));      }      if (lableImg.at<int>(curX, curY+1) == 1)      {// 右边       neighborPixels.push(std::pair<int,int>(curX, curY+1));      }      if (lableImg.at<int>(curX-1, curY) == 1)      {// 上边       neighborPixels.push(std::pair<int,int>(curX-1, curY));      }      if (lableImg.at<int>(curX+1, curY) == 1)      {// 下边       neighborPixels.push(std::pair<int,int>(curX+1, curY));    

转载于:https://www.cnblogs.com/carl2380/p/4572208.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值