二值图像连通域标记算法与代码
- 格式:docx
- 大小:71.88 KB
- 文档页数:23
基于FPGA的二值图像连通域快速标记汪滴珠;安涛;何培龙【摘要】针对连通域标记算法运算量大、速度慢、硬件实现困难的缺点,提出一种适于现场可编程逻辑门阵列(FPGA)实现的二值图像连通域快速标记的算法,并用VHDL硬件开发语言在XILINX公司的FPGA上实现.实验结果表明了该算法能对二值图像复杂的连通关系正确标记,易于硬件实现,大大节约了硬件资源,电路结构简单,满足实时性要求.%In order to solve the prablems of low speed large computation and difficult hardware implementation of connected component labeling, a connected component fast labcling algorithm of binary image lageling applicable for field programmable gate array (FPGA) is proposed, which is implemented by VHDI. hardware description language based on FPGA platform of XILINX corporation. Experimental results show that the proposed algorithm can label binary image with complex connections correctly, implement hardware easily,save more hardware resource and meet real-time demands.【期刊名称】《现代电子技术》【年(卷),期】2011(034)008【总页数】3页(P115-117)【关键词】FPGA;二值图像;连通域;快速标记【作者】汪滴珠;安涛;何培龙【作者单位】中国科学院,光电技术研究所,四川,成都,610209;中国科学院研究生院,北京,100039;中国科学院,光电技术研究所,四川,成都,610209;中国科学院,光电技术研究所,四川,成都,610209【正文语种】中文【中图分类】TN919-34;TP391连通域标记算法是图像处理、计算机视觉和模式识别等领域的基本算法,它可以对图像中不同目标标上不同的标记,进而提取、分离目标,确定目标的特征和参数,从而对目标进行识别和跟踪。
二值图像连通域标记算法与代码收藏10:19:42二值图像连通域标记算法与代码这里列举二值图像连通域标记算法包括直接扫描标记算法和二值图像连通域标记快速算法一、直接扫描标记算法把连续区域作同一个标记,常见的四邻域标记算法和八邻域标记算法。
1、四邻域标记算法:1)判断此点四邻域中的最左,最上有没有点,如果都没有点,则表示一个新的区域的开始。
2)如果此点四邻域中的最左有点,最上没有点,则标记此点为最左点的值;如果此点四邻域中的最左没有点,最上有点,则标记此点为最上点的值。
3)如果此点四邻域中的最左有点,最上都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
2、八邻域标记算法:1)判断此点八邻域中的最左,左上,最上,上右点的情况。
如果都没有点,则表示一个新的区域的开始。
2)如果此点八邻域中的最左有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
3)如果此点八邻域中的左上有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
4)否则按照最左,左上,最上,上右的顺序,标记此点为四个中的一个。
代码实现:#include <list>#include <vector>#include <algorithm>像初步标记:为每个像素赋予临时标记,并且将临时标记的等价关系记录在等价表中2.整理等价表:这一环节分为两个步骤:(1)将具有等价关系的临时标记全部等价为其中的最小值;(2)对连通区域以自然数顺序重新编号,得到临时标记与最终标记之间的等价关系。
3.图像代换:对图像进行逐像素代换,将临时标记代换为最终标记.经过3个环节处理后,算法输出标记后的图像,图像中连通域按照由上到下,由左至右出现的顺序被标以连续的自然数。
代码实现:#include <list>#include <vector>#include <algorithm>arkValue1);pInnerListAdd->AddTail( (void *)().MarkValue2);( (void *)pInnerListAdd );();/* 定义pFindValue1和pFindValue2,存放在所有内层链表中找到特定值的某个内层链表的头指针,也就是外层链表的某个元素值。
⼆值图像的贴标签算法 贴标签就是将⼆值图中属于同⼀个连通域的像素标记起来。
之前编的程序需要多次遍历图像,所以速度⽐较慢,最近有朋友告诉我⼀种更简单的⽅法。
这⾥对⼆值图中⽩⾊的连通域进⾏贴标签,具体步骤是: 1、按⾏遍历图像,当遇到⼀个⽩点时,说明遇到了⼀个标签区域; 2、将当前⽩点的坐标作为种⼦点⼊栈; 3、判断栈是否为空,若栈⾮空,则在栈顶元素所在位置贴上对应的标签号,同时将⼆值图上的该位置赋成别的颜⾊(表明当前元素已经贴过标签),弹出栈顶元素,并将其8邻域的⽩点⼊栈,重复3直到栈空,这时,当前连通域已经完成贴标签过程; 4、继续按⾏遍历图像,直到遇到下⼀个⽩点,然后重复步骤2-3,直到遍历完图像;1////////////////////////////////////////////////////2// 功能:⼆值图贴标签(针对感兴趣区域)3// 参数:4// pBin - ⼆值图像5// pLabel - 标签矩阵(在外申请,和图像同样⼤⼩,并全部置0)6// lineByte - 图像的每⾏字节数7// roi - 感兴趣区域(左闭右开、上闭下开区间)8// 返回值:9// nLabel - 标签个数10///////////////////////////////////////////////////1112int Labeling(unsigned char *pBin, int *pLabel, int lineByte, CRect roi)13 {14//感兴趣区域的4条边界15int xMin = roi.left;16int xMax = roi.right;17int yMin = roi.top;18int yMax = roi.bottom;1920 stack<CPoint> stk;21int i,j,x,y,k;22int nLabel = 0; //标签编号(从1号标签贴起,⾮标签区域贴0)23for (y=yMin; y<yMax; y++)24 {25for (x=xMin; x<xMax; x++)26 {27if (pBin[y*lineByte+x]==255) //以找到的第⼀个⽩点为种⼦点28 {29 nLabel++;30 stk.push(CPoint(x,y)); //将种⼦点⼊栈31 pLabel[y*lineByte+x] = nLabel;32 pBin[y*lineByte+x] = nLabel*40;3334//若栈⾮空,弹出⼀个元素,并将其8邻域内的⽩点⼊栈,栈空时当前连通域完成贴标签35while (!stk.empty())36 {37 CPoint pt = stk.top();38 stk.pop();39 i = pt.y;40 j = pt.x;41 k = i*lineByte+j;42if (i>yMin && j>xMin && pBin[k-lineByte-1]==255) //左上43 {44 stk.push(CPoint(j-1,i-1));45 pLabel[k-lineByte-1] = nLabel;46 pBin[k-lineByte-1] = nLabel*40;47 }48if (i>yMin && pBin[k-lineByte]==255) //上49 {50 stk.push(CPoint(j,i-1));51 pLabel[k-lineByte] = nLabel;52 pBin[k-lineByte] = nLabel*40;53 }54if (i>yMin && j<xMax-1 && pBin[k-lineByte+1]==255) //右上55 {56 stk.push(CPoint(j+1,i-1));57 pLabel[k-lineByte+1] = nLabel;58 pBin[k-lineByte+1] = nLabel*40;59 }60if (j>xMin && pBin[k-1]==255) //左61 {62 stk.push(CPoint(j-1,i));63 pLabel[k-1] = nLabel;64 pBin[k-1] = nLabel*40;65 }66if (j<xMax-1 && pBin[k+1]==255) //右67 {68 stk.push(CPoint(j+1,i));69 pLabel[k+1] = nLabel;70 pBin[k+1] = nLabel*40;71 }72if (i<yMax-1 && j>xMin && pBin[k+lineByte-1]==255) //左下73 {74 stk.push(CPoint(j-1,i+1));75 pLabel[k+lineByte-1] = nLabel;76 pBin[k+lineByte-1] = nLabel*40;77 }78if (i<yMax-1 && pBin[k+lineByte]==255) //下79 {80 stk.push(CPoint(j,i+1));81 pLabel[k+lineByte] = nLabel;82 pBin[k+lineByte] = nLabel*40;83 }84if (i<yMax-1 && j<xMax-1 && pBin[k+lineByte+1]==255) //右下85 {86 stk.push(CPoint(j+1,i+1));87 pLabel[k+lineByte+1] = nLabel;88 pBin[k+lineByte+1] = nLabel*40;89 }90 }91 }92 }93 }9495return nLabel;96 }。
python中连通域算法Python 中连通域算法连通域可以简单理解为一个图像中相邻的像素点相连形成的一块区域。
在图像处理中,我们常常需要对这些连通的区域进行一些操作,比如找到其中的特征、选择某个区域进行分析或对整个区域进行一些操作。
在Python 中,我们可以使用一些算法来找到图像中的连通域并进行相应的操作。
接下来,我将逐步介绍Python 中连通域的概念、如何找到连通域、如何在连通域上进行特定操作的方法。
1. 连通域的概念在图像处理中,连通域是一个由相邻的像素点构成的区域。
在二值图像中,像素点的值通常只有两个,例如黑色和白色,被称为“0”和“1”。
连通域的数量和形状通常与图像中的目标有关。
例如,在一张图片中,我们可能需要找到所有联通的数字或字母,或者只需要找到图像中最大或最小的连通区域。
2. 连通域的查找算法在Python 中,我们可以使用不同的算法来查找连通域。
其中,最常用的两种算法是DFS(深度优先搜索)和BFS(广度优先搜索)。
在这里,我们将重点介绍DFS 算法。
DFS 算法是一种非常适合连通域查找的算法。
它的基本思路是从图像中的某个像素点开始,沿着该点所在的连通域递归地访问所有相邻的未访问过的像素点。
通过递归,我们可以遍历整个连通域,并找到所有的像素点。
为了演示DFS 算法,我们先创建一个简单的二值图像。
以下代码创建一个10x10 的方形图像,其中心区域是黑色的(0),外围是白色的(1)。
import numpy as npimport matplotlib.pyplot as pltimage = np.ones((10, 10), dtype=np.uint8) # 创建一个10x10的白色方形图像image[3:7, 3:7] = 0 # 将图像中心区域切换为黑色plt.imshow(image, cmap=plt.cm.gray) # 绘制图像plt.axis('off')plt.show()运行后,可以看到以下图像:是图像处理中一种用来查找和标记图像中连通区域(或联通分量、区域)的方法。
这种算法首次被提出是在20世纪60年代,如今已成为图像处理中常用的一种方法。
这种算法相对简单而且高效,因此在实际应用中被广泛使用。
在本文中,我们将详细介绍两遍算法的原理和实现过程,并且通过一些示例来说明其应用。
首先,我们需要了解什么是连通区域。
在图像处理中,连通区域是指图像中由相邻像素点组成的一块区域。
在这个区域内的像素点具有相似的性质,比如颜色、亮度等。
通常情况下,我们希望将图像中相互连接的像素点组成的区域识别出来,并进行标记,这样我们就可以在后续的处理中对这些区域进行分析和处理。
两遍算法是一种经典的连通区域查找方法。
其基本思路就是通过两次扫描图像来实现。
在第一次扫描中,我们会遍历整个图像,对每一个像素点进行分析,并进行标记。
在第二次扫描中,我们会对已经被标记的像素点进行进一步处理,比如合并相邻的区域或者进行其他操作。
接下来,我们将详细介绍两遍算法的具体步骤。
第一步:初始化在进行第一次扫描之前,我们需要对一些变量进行初始化。
首先,我们需要一个数组来存储每个像素点的标记。
通常情况下,我们会使用一个与图像大小相同的数组来存储这些标记。
其次,我们需要设定一个阈值,用来确定两个像素点是否属于同一个区域。
最后,我们还需要定义一个函数,用来判断两个像素点是否相邻。
在大多数情况下,我们会使用四邻域或者八邻域来进行判断。
第二步:第一次扫描在第一次扫描中,我们会遍历整个图像,并对每一个像素点进行分析。
首先,我们会检查当前像素点是否已经被标记。
如果已经被标记,我们会继续遍历下一个像素点;如果没有被标记,我们将对该像素点进行标记,并进一步对相邻的像素点进行分析。
通过这种方式,我们就可以逐步找到图像中所有的连通区域,并对其进行标记。
第三步:第二次扫描在第二次扫描中,我们会对已经标记的像素点进行处理。
连通域算法
连接域算法是图像处理的常用技术,是一种从图像中提取像素点连成的连通单元的算法。
1. 基本概念
连接域算法主要基于图像二值化原理,将图像进行分割,划分为像素点的连通分量,多指由相同像素点组成的对象,又称为连接域或连接部件,这里以像素点值为标准,即相同像素点值得组成为一个连接域相互之间互不相干,因此,将整幅图像划分为多个连接元素自然成为显而易见。
2.算法步骤
(1)第一步从原始图像提取像素点,用于鉴别其区域是否为连接域。
(2)第二部找到连接域的连接算法,像素点的第一步根据特定模式集合,从原始图像把可以定义为连接域的像素点提取出来。
(3)第三步建立连接域的空间模型,这一步的目的是建立表示连接域空间模型的相应数据结构。
(4)第四步根据空间模型和熵损失函数构建连接域,熵损失函数用于平滑连接域边缘,从而实现良好的分割效果。
3.应用领域
(1)文本识别:针对不同类型的文字,连接域可以有效的分割文本的
字体、颜色等属性,并为文字识别提供重要的支撑。
(2)图像分割:图像分割时,可以将不同区域表示成相应的连接域,
然后分开进行分类,从而实现对图像的分割。
(3)图像检索:由于连接域可以有效的表达图像的空间特征,因此,
它可以用来表征图像,从而实现图像检索。
4.缺点
(1)连接域法较为复杂,所需计算量较大,因此计算效率相对较低。
(2)由于连接域的计算依赖像素点的邻域函数,容易受图像噪声影响,其计算结果也会受到影响。
(3)连通域无法考虑全局空间特征,如果图像中存在复杂类内结构,
很容易出现计算错误。
图像分析之连通组件标记算法图像处理之连接组件标记算法连接组件标记算法(connected component labeling algorithm)是图像分析中最常⽤的算法之⼀,算法的实质是扫描⼀幅图像的每个像素,对于像素值相同的分为相同的组(group),最终得到图像中所有的像素连通组件。
扫描的⽅式可以是从上到下,从左到右,对于⼀幅有N个像素的图像来说,最⼤连通组件个数为N/2。
扫描是基于每个像素单位,对于⼆值图像⽽⾔,连通组件集合可以是V={1}或者V={0}, 取决于前景⾊与背景⾊的不同。
对于灰度图像来说,连图组件像素集合可能是⼀系列在0 ~ 255之间的灰度值。
算法流程如下:1. ⾸先扫描当前像素相邻的⼋邻域像素值,发现连通像素加以标记。
2. 完全扫描所有像素点之后,根据标记将所有连通组件合并。
算法实现Class⽂件解释:AbstractConnectedComponentLabel:⼀个抽象的Class定义了抽象⽅法doConntectedLabel()同时完成了⼀些公共⽅法ConnectedComponentLabelAlgOne:⼀个容易读懂的连接组件算法完成,没有任何优化,继承上⾯的⾃抽象类ConnectedComponentLabelAlgTwo:⼀个快速的连接组件算法,基于算法优化,取当前像素的四邻域完成扫描与标记合并。
Label与PixelInfo是两个数据结构,⽤来存储算法计算过程中的中间变量。
ImageLabelFilter⽤来测试算法的驱动类,ImageAnalysisUI是现实测试结果的UI类算法运⾏结果:根据标记的索引将组件着⾊。
定义数据结构的代码如下:public class Label {private int index;private Label root;public Label(int index) {this.index = index;this.root = this;}public Label getRoot() {if(this.root != this) {this.root = this.root.getRoot();}return root;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}public void setRoot(Label root) {this.root = root;}}Pixelnfo的代码如下:package com.gloomyfish.image.analysis;public class PixelInfo {private int value; // pixel valueprivate int xp;private int yp;public PixelInfo(int pixelValue, int yp, int xp) { this.value = pixelValue;this.yp = yp;this.xp = xp;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public int getXp() {return xp;}public void setXp(int xp) {this.xp = xp;}public int getYp() {return yp;}public void setYp(int yp) {this.yp = yp;}}抽象的组件连通标记算法Class如下:public abstract class AbstractConnectedComponentLabel {protected int width;protected int height;protected Color fgColor;protected int[] inPixels;protected int[][] chessborad;protected Map<Integer, Integer> neighbourMap;public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public abstract Map<Integer, List<PixelInfo>> doConntectedLabel();public boolean isForeGround(int tr, int tg, int tb) {if(tr == fgColor.getRed() && tg == fgColor.getGreen() && tb == fgColor.getBlue()) {return true;} else {return false;}}}实现抽象类的算法one的代码如下:import java.awt.Color;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class ConnectedComponentLabelAlgOne extends AbstractConnectedComponentLabel {public ConnectedComponentLabelAlgOne(Color fgColor, int[] srcPixel, int width, int height) { this.fgColor = fgColor;this.width = width;this.height = height;this.inPixels = srcPixel;this.chessborad = new int[height][width];for(int i=0; i<height; i++) {for(int j=0; j<width; j++) {chessborad[i][j] = 0;}}this.neighbourMap = new HashMap<Integer, Integer>();}// assume the input image data is binary image.public Map<Integer, List<PixelInfo>> doConntectedLabel() {System.out.println("start to do connected component labeling algorithm");int index = 0;int labelCount = 0;Label currentLabel = new Label(0);HashMap<Integer, Label> allLabels = new HashMap<Integer, Label>();for(int row=0; row<height; row++) {int ta = 0, tr = 0, tg = 0, tb = 0;for(int col=0; col<width; col++) {index = row * width + col;ta = (inPixels[index] >> 24) & 0xff;tr = (inPixels[index] >> 16) & 0xff;tg = (inPixels[index] >> 8) & 0xff;tb = inPixels[index] & 0xff;if(isForeGround(tr, tg, tb)) {getNeighboringLabels(row, col);if(neighbourMap.size() == 0) {currentLabel.setIndex(++labelCount);allLabels.put(labelCount,new Label(labelCount));} else {for(Integer pixelLabel : neighbourMap.keySet().toArray(new Integer[0])) {currentLabel.setIndex(pixelLabel);break;}mergeLabels(currentLabel.getIndex(), neighbourMap, allLabels);}chessborad[row][col] = currentLabel.getIndex();}}}Map<Integer, List<PixelInfo>> connectedLabels = consolidateAllLabels(allLabels);return connectedLabels;}private Map<Integer, List<PixelInfo>> consolidateAllLabels(HashMap<Integer, Label> allLabels) { Map<Integer, List<PixelInfo>> patterns = new HashMap<Integer, List<PixelInfo>>();int patternNumber;List<PixelInfo> shape;for (int i = 0; i < this.height; i++){for (int j = 0; j < this.width; j++){patternNumber = chessborad[i][j];if (patternNumber != 0){patternNumber = allLabels.get(patternNumber).getRoot().getIndex();if (!patterns.containsKey(patternNumber)){shape = new ArrayList<PixelInfo>();shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));}else{shape = patterns.get(patternNumber);shape.add(new PixelInfo(Color.BLUE.getRGB(), i, j));}patterns.put(patternNumber, shape);}}}return patterns;private void mergeLabels(int index, Map<Integer, Integer> neighbourMap, HashMap<Integer, Label> allLabels) {Label root = allLabels.get(index).getRoot();Label neighbour;for(Integer key : neighbourMap.keySet().toArray(new Integer[0])) {if (key != index){neighbour = allLabels.get(key);if(neighbour.getRoot() != root) {neighbour.setRoot(neighbour.getRoot());// thanks zhen712,}}}}/*** get eight neighborhood pixels** @param row* @param col* @return*/public void getNeighboringLabels(int row, int col) {neighbourMap.clear();for(int i=-1; i<=1; i++) {int yp = row + i;if(yp >=0 && yp < this.height) {for(int j=-1; j<=1; j++) {if(i == 0 && j==0) continue; // ignore/skip center pixel/itselfint xp = col + j;if(xp >=0 && xp < this.width) {if(chessborad[yp][xp] != 0) {if(!neighbourMap.containsKey(chessborad[yp][xp])) {neighbourMap.put(chessborad[yp][xp],0);}}}}}}}}测试代码可以参考以前的⽂章 -。
二值图像连通域标记快速算法实现算法描述首先,在进行标记算法以前,利用硬件开辟独立的图像标记缓存和连通关系数组,接着在视频流的采集传输过程中,以流水线的方式按照视频传输顺序对图像进行逐行像素扫描,然后对每个像素的邻域分别按照逆时针方向和水平方向进行连通性检测和等价标记关系合并,检测出的结果对标记等价数组和标记缓存进行更新,在一帧图像采集传输结束后,得到图像的初步标记结果以及初步标记之间的连通关系,最后,根据标号对连通关系数组从小到大的传递过程进行标号的归并,利用归并后的连通关系数组对图像标记缓存中的标号进行替换,替换后的图像为最终标记结果,并且连通域按照扫描顺序被赋予唯一的连续自然数。
图 1 标记算法流程本文快速二值图像连通域标记算法分为三个环节:1.图像初步标记:为每个像素赋予临时标记,并且将临时标记的等价关系记录在等价表中2.整理等价表:这一环节分为两个步骤:(1)将具有等价关系的临时标记全部等价为其中的最小值;(2)对连通区域以自然数顺序重新编号,得到临时标记与最终标记之间的等价关系。
3.图像代换:对图像进行逐像素代换,将临时标记代换为最终标记.经过3个环节处理后,算法输出标记后的图像,图像中连通域按照由上到下,由左至右出现的顺序被标以连续的自然数。
1 图像初始标记标记算法符号约定:算法在逆时钟方向检测连通域时用w1,w2表示连续两行的图像数据,在紧接着的顺时钟方向连通域检测时用k0,k表示连续两行经过逆时钟方向标记后的图像数据。
其在工作窗口的位置在图2、3中分别说明;对初始逆时针方向临时标记用Z表示。
Z初始标记值为1。
二值图像连通域标记算法采用8连通判断准则,通过缩小标记范围剔除了图像的边界效应。
为了简化标记处理过程,使标记处理在硬件对一帧图像传输操作时间内结束,标记处理利用中间数据缓存分为连续的两种类型,其中类型1用于直接图像序列传输,硬件发起图像序列传输时,类型1采用逆时钟顺序连通域检测,对2×3工作窗口中的二值像素进行初始标记。
二、连通域
如果像素点A与B邻接,我们称A与B连通,于是我们不加证明的有如下的结论:
三、连通区域的标记
第一行,我们得到两个团:[2,6]和[10,13],同时给它们标记1和2。
下面是这个过程的C++实现,每个等价表用一个vector<int>来保存,等价对列表保存在map<pair<int,int>>里。
整个算法步骤,我们只扫描了一次图像,同时我们对图像中的像素进行标记,要么赋予一个新的标号,要么用它同行P点。
最后不要忘了把C的值加1。
这个过程如下面图像S1中所示。
情况3
1)如果P是外轮廓的起点,也就是说我们是从P点开始跟踪的,那么我们从7号(右上角)位置P1P1开始,看7号在L上标记为一个负值。
如下图所示,其中右图像标记的结果。
2)那么如果P是不是外轮廓的起点,即P是外轮廓路径上的一个点,那么它肯定是由一个点进入的,我们设置为P−
在OpenCV中查找轮廓的函数已经存在了,而且可以得到轮廓之间的层次关系。
这个函数按上面的算法实现起来并不。
matlab 连通域标记在图像处理中,连通域标记是指将图像中的相邻像素点组成的区域进行分组和标记的过程。
连通域标记可以用于图像分割、物体识别、图像分析等领域。
Matlab是一种功能强大的数值计算和科学编程软件,提供了丰富的图像处理工具箱,可以方便地实现连通域标记算法。
在Matlab中,可以使用bwlabel函数来实现连通域标记。
该函数可以将二值图像中的连通域进行标记,并返回每个像素所属的连通域编号。
具体步骤如下:1. 将图像二值化,即将图像转换成黑白两色(前景和背景)。
2. 使用bwlabel函数对二值图像进行连通域标记。
该函数的语法为:[L, num] = bwlabel(BW),其中BW为二值图像,L为标记后的图像,num为连通域的数量。
3. 可以使用regionprops函数对每个连通域进行属性分析。
该函数可以计算每个连通域的面积、重心、边界框等属性,并返回一个结构体数组。
以下是一个示例代码,演示了如何使用Matlab进行连通域标记和属性分析:```matlab% 读取图像image = imread('example.png');% 将图像转换为二值图像bw = imbinarize(image);% 进行连通域标记[L, num] = bwlabel(bw);% 对每个连通域进行属性分析props = regionprops(L, 'Area', 'Centroid','BoundingBox');% 输出每个连通域的属性信息for i = 1:numfprintf('连通域 %d:', i);fprintf('面积: %d', props(i).Area);fprintf('重心: (%d, %d)', props(i).Centroid(1), props(i).Centroid(2));fprintf('边界框: (%d, %d, %d, %d)', props(i).BoundingBox(1), props(i).BoundingBox(2), props(i).BoundingBox(3), props(i).BoundingBox(4));end```以上代码会输出每个连通域的面积、重心和边界框信息。
二值图像连通域标记算法与代码这里列举二值图像连通域标记算法包括直接扫描标记算法和二值图像连通域标记快速算法一、直接扫描标记算法把连续区域作同一个标记,常见的四邻域标记算法和八邻域标记算法。
1、四邻域标记算法:1)判断此点四邻域中的最左,最上有没有点,如果都没有点,则表示一个新的区域的开始。
2)如果此点四邻域中的最左有点,最上没有点,则标记此点为最左点的值;如果此点四邻域中的最左没有点,最上有点,则标记此点为最上点的值。
3)如果此点四邻域中的最左有点,最上都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
2、八邻域标记算法:1)判断此点八邻域中的最左,左上,最上,上右点的情况。
如果都没有点,则表示一个新的区域的开始。
2)如果此点八邻域中的最左有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
3)如果此点八邻域中的左上有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
4)否则按照最左,左上,最上,上右的顺序,标记此点为四个中的一个。
代码实现:#include<list>#include<vector>#include<algorithm>//连通区域属性结构typedef struct tagMarkRegion{std::list<POINT> MarkPointList;//点列表RECT rect;}MarkRegion;//定义MarkMap 结构,用来存放等价对typedef struct tagEqualMark{ int MarkValue1; //标记值int MarkValue2; //标记值} EqualMark;//定义MarkMapping 结构,用来存放标记映射关系typedef struct tagMarkMapping{ int nOriginalMark; //第一次扫描的标记int nMappingMark; //等价整理之后对应标记} MarkMapping;/*功能说明:八连通标记参数说明:I,表示图像数据指针ImageWidth,表示图像宽ImageHeight,表示图像高off,表示偏移量nFlag,表示指定标记iColorType,表示颜色类型,(黑点,白点)markInfo,表示连通区域属性信息返回值:连通点数量,int类型*/int FillAreaFlag33(LPINT I,int ImageWidth,int ImageHeight,long off,int nFlag,int iColorType, MarkRegion &markInfo){bool bNew;RECT rect;int m,n,i,j,k,nDot=1,offset,offtemp,yMin;int dxy[8],x,y;dxy[0]=-ImageWidth-1; dxy[1]=-ImageWidth; dxy[2]=-ImageWidth+1;dxy[3]=-1;dxy[4]=1;dxy[5]=ImageWidth-1; dxy[6]=ImageWidth; dxy[7]=ImageWidth+1;rect.left=65535; rect.right=-1;rect.bottom=65535; rect.top=-1;markInfo.MarkPointList.clear();POINT ptTmp;if(I[off]==iColorType && I[off]!=nFlag)//黑点同时未被标记的情况{I[off]=nFlag;x=off%ImageWidth;y=off/ImageWidth;ptTmp.x = x;ptTmp.y = y;markInfo.MarkPointList.push_back(ptTmp);if(x<rect.left)rect.left=x;if(x>rect.right)rect.right=x;if(y<rect.bottom)rect.bottom=y;if(y>rect.top)rect.top=y;}else{return 0;}for(i=y; i<ImageHeight; i++){bNew=false;yMin=i;for(j=0; j<ImageWidth; j++){offset=i*ImageWidth+j;if(I[offset]==nFlag){for(k=0; k<8; k++)//八邻域搜索{if(i==0 && k<=2)continue;if(i==ImageHeight-1 && k>=5)continue;if(j==0 && (k==0 || k==3 || k==5))continue;if(j==ImageWidth-1 && (k==2 || k==4 || k==7))continue;offtemp=offset+dxy[k];if(I[offtemp]==iColorType && I[offtemp]!=nFlag) {I[offtemp]=nFlag;nDot++;m=offtemp/ImageWidth;n=offtemp%ImageWidth;ptTmp.x = n;ptTmp.y = m;markInfo.MarkPointList.push_back(ptTmp);if(n < rect.left)rect.left=n;if(n > rect.right)rect.right=n;if(m < rect.bottom)rect.bottom=m;if(m > rect.top)rect.top=m;y=offtemp/ImageWidth;if(y<=yMin){yMin=y;if(!bNew)bNew=true;}}}}}if(bNew){i=yMin-1;}}markInfo.rect.left = rect.left;markInfo.rect.right = rect.right;markInfo.rect.top = rect.top;markInfo.rect.bottom = rect.bottom;return nDot;}/*功能说明:四连通标记参数说明:I,表示图像数据指针ImageWidth,表示图像宽ImageHeight,表示图像高off,表示偏移量nFlag,表示指定标记iColorType,表示颜色类型,(黑点,白点)markInfo,表示连通区域属性信息返回值:连通点数量,int类型*/int FillAreaFlag22(LPINT I,int ImageWidth,int ImageHeight,long off,int nFlag,int iColorType, MarkRegion &markInfo){bool bNew;RECT rect;int m,n,i,j,k,nDot=1,offset,offtemp,yMin;int dxy[4],x,y;dxy[0]=-ImageWidth; dxy[1]=1;dxy[2]=ImageWidth; dxy[3]=-1;rect.left=65535; rect.right=-1;rect.bottom=65535; rect.top=-1;markInfo.MarkPointList.clear();POINT ptTmp;if(I[off]==iColorType && I[off]!=nFlag)//黑点同时未被标记的情况 {I[off]=nFlag;x=off%ImageWidth;y=off/ImageWidth;ptTmp.x = x;ptTmp.y = y;markInfo.MarkPointList.push_back(ptTmp);if(x<rect.left)rect.left=x;if(x>rect.right)rect.right=x;if(y<rect.bottom)rect.bottom=y;if(y>rect.top)rect.top=y;}else{return 0;}for(i=y; i<ImageHeight; i++){bNew=false;yMin=i;for(j=0; j<ImageWidth; j++){offset=i*ImageWidth+j;if(I[offset]==nFlag){for(k=0; k<4; k++)//四邻域搜索{if(i==0 && k==0)continue;if(i==ImageHeight-1 && k==2)continue;if(j==0 && k==3)continue;if(j==ImageWidth-1 && k==1)continue;offtemp=offset+dxy[k];if(I[offtemp]==iColorType && I[offtemp]!=nFlag) {I[offtemp]=nFlag;nDot++;m=offtemp/ImageWidth;n=offtemp%ImageWidth;ptTmp.x = n;ptTmp.y = m;markInfo.MarkPointList.push_back(ptTmp);if(n < rect.left)rect.left=n;if(n > rect.right)rect.right=n;if(m < rect.bottom)rect.bottom=m;if(m > rect.top)rect.top=m;y=offtemp/ImageWidth;if(y<=yMin){yMin=y;if(!bNew)bNew=true;}}}}}if(bNew){i=yMin-1;}}markInfo.rect.left = rect.left;markInfo.rect.right = rect.right;markInfo.rect.top = rect.top;markInfo.rect.bottom = rect.bottom;return nDot;}二、二值图像连通域标记快速算法算法描述首先,在进行标记算法以前,利用硬件开辟独立的图像标记缓存和连通关系数组,接着在视频流的采集传输过程中,以流水线的方式按照视频传输顺序对图像进行逐行像素扫描,然后对每个像素的邻域分别按照逆时针方向和水平方向进行连通性检测和等价标记关系合并,检测出的结果对标记等价数组和标记缓存进行更新,在一帧图像采集传输结束后,得到图像的初步标记结果以及初步标记之间的连通关系,最后,根据标号对连通关系数组从小到大的传递过程进行标号的归并,利用归并后的连通关系数组对图像标记缓存中的标号进行替换,替换后的图像为最终标记结果,并且连通域按照扫描顺序被赋予唯一的连续自然数。