给定0到1矩阵求连通域题解 您所在的位置:网站首页 连通域什么意思 给定0到1矩阵求连通域题解

给定0到1矩阵求连通域题解

2022-05-26 19:13| 来源: 网络整理| 查看: 265

图像处理题目: 注意,一下所有需要写代码的题目,不允许使用OpenCV的Mat类。如果图片内容需要用指针读取。 1 .给定0-1矩阵,求连通域。(遇到过N次,笔试面试都有,最好做到能徒手hack代码或者伪代码。) 二值图像分析最重要的方法就是连通区域标记,它是所有二值图像分析的基础,它通过对二值图像中白色像素(目标)的标记,让每个单独的连通区域形成一个被标识的块,进一步的我们就可以获取这些块的轮廓、外接矩形、质心、不变矩等几何参数。 连通域

在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。

4邻接一共4个点,即上下左右,如下左图所示。8邻接的点一共有8个,包括了对角线位置的点,如下右图所示。

imageimage

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

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

在视觉上看来,彼此连通的点形成了一个区域,而不连通的点形成了不同的区域。

这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

下面这符图中,如果考虑4邻接,则有3个连通区域;如果考虑8邻接,则有2个连通区域。

(注:图像是被放大的效果,图像正方形实际只有4个像素)。

image

连通区域的标记 连通区域标记算法有很多种,有的算法可以一次遍历图像完成标记,有的则需要2次或更多次遍历图像。这也就造成了不同的算法时间效率的差别,在这里我们介绍2种算法。 第一种算法是:现在matlab中连通区域标记函数bwlabel中使的算法,L = bwlabel(BW,n) 返回一个和BW大小相同的L矩阵,包含了标记了BW中每个连通区域的类别标签,这些标签的值为1、2、num(连通区域的个数)。n的值为4或8,表示是按4连通寻找区域,还是8连通寻找,默认为8。 4连通或8连通是图像处理里的基本感念:而8连通,是说一个像素,如果和其他像素在上、下、左、右、左上角、左下角、右上角或右下角连接着,则认为他们是联通的;4连通是指,如果像素的位置在其他像素相邻的上、下、左或右,则认为他们是连接着的,连通的,在左上角、左下角、右上角或右下角连接,则不认为他们连通。请注意“或”字的含义,就是满足其中一个条件就认为是连通的。

[L,num] = bwlabel(BW,n)这里num返回的就是BW中连通区域的个数。

通俗的说,这个函数的作用是用来找这个二值图像中的连通区域的,对于不同的符合条件的连通区域(4连通,8连通)分别用不同的标号加以区别,结果保存在L这个矩阵里,而num里保存的是输入图像中连通区域的总数。

举例说明:

BW = 1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 按4连通计算,方形的区域,和翻转的L形区域,有用是对角连接,不属于连通,所以分开标记,连通区域个数为3,就是有3个不同的连接区域。 L = bwlabel(BW,4) 结果如下: L = 1 1 1 0 0 0 0 0 1 1 1 0 2 2 0 0 1 1 1 0 2 2 0 0 1 1 1 0 0 0 3 0 1 1 1 0 0 0 3 0 1 1 1 0 0 0 3 0 1 1 1 0 0 3 3 0 1 1 1 0 0 0 0 0 而8连通标记,它们是连通的: [L, num] = bwlabel(BW,8) L = 1 1 1 0 0 0 0 0 1 1 1 0 2 2 0 0 1 1 1 0 2 2 0 0 1 1 1 0 0 0 2 0 1 1 1 0 0 0 2 0 1 1 1 0 0 0 2 0 1 1 1 0 0 2 2 0 1 1 1 0 0 0 0 0 这里 num =2 第二种算法是:现在开源库cvBlob中使用的标记算法,它通过定位连通区域的内外轮廓来标记整个图像,这个算法的核心是轮廓的搜索算法,这个算法相比与第一种方法效率上要低一些,但是在连通区域个数在100以内时,两者几乎无差别,当连通区域个数到了103数量级时,上面的算法会比该算法快10倍以上。 基于行程的标记 我们首先给出算法的描述,然后再结合实际图像来说明算法的步骤。

1,逐行扫描图像,我们把每一行中连续的白色像素组成一个序列称为一个团(run),并记下它的起点start、它的终点end以及它所在的行号。

2,对于除了第一行外的所有行里的团,如果它与前一行中的所有团都没有重合区域,则给它一个新的标号;

如果它仅与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它;

如果它与上一行的2个以上的团有重叠区域,则给当前团赋一个相连团的最小标号,并将上一行的这几个团的标记写入等价对,说明它们属于一类。

3,将等价对转换为等价序列,每一个序列需要给一相同的标号,因为它们都是等价的。从1开始,给每个等价序列一个标号。

4,遍历开始团的标记,查找等价序列,给予它们新的标记。

5,将每个团的标号填入标记图像中。

6,结束。

我们来结合一个三行的图像说明,上面的这些操作。

image

第一行,我们得到两个团:[2,6]和[10,13],同时给它们标记1和2。

第二行,我们又得到两个团:[6,7]和[9,10],但是它们都和上一行的团有重叠区域,所以用上一行的团标记,即1和2。

第三行,两个:[2,4]和[7,8]。[2,4]这个团与上一行没有重叠的团,所以给它一个新的记号为3;而[2,4]这个团与上一行的两个团都有重叠,所以给它一个两者中最小的标号,即1,然后将(1,2)写入等价对。

全部图像遍历结束,我们得到了很多个团的起始坐标,终止坐标,它们所在的行以及它们的标号。同时我们还得到了一个等价对的列表。

下面我们用C++实现上面的过程,即步骤2,分两个进行: 1)fillRunVectors函数完成所有团的查找与记录: void fillRunVectors(const Mat& bwImage, int& NumberOfRuns, vector& stRun, vector& enRun, vector& rowRun) { for (int i = 0; i < bwImage.rows; i++) { const uchar* rowData = bwImage.ptr(i); if (rowData[0] == 255) { NumberOfRuns++; stRun.push_back(0); rowRun.push_back(i); } for (int j = 1; j < bwImage.cols; j++) { if (rowData[j - 1] == 0 && rowData[j] == 255) { NumberOfRuns++; stRun.push_back(j); rowRun.push_back(i); } else if (rowData[j - 1] == 255 && rowData[j] == 0) { enRun.push_back(j - 1); } } if (rowData[bwImage.cols - 1]) { enRun.push_back(bwImage.cols - 1); } } } 2)firstPass函数完成团的标记与等价对列表的生成。相比之下第二个函数要稍微难理解一些。 void firstPass(vector& stRun, vector& enRun, vector& rowRun, int NumberOfRuns, vector& runLabels, vector>& equivalences, int offset) { runLabels.assign(NumberOfRuns, 0); int idxLabel = 1; int curRowIdx = 0; int firstRunOnCur = 0; int firstRunOnPre = 0; int lastRunOnPre = -1; for (int i = 0; i < NumberOfRuns; i++) { if (rowRun[i] != curRowIdx) { curRowIdx = rowRun[i]; firstRunOnPre = firstRunOnCur; lastRunOnPre = i - 1; firstRunOnCur = i; } for (int j = firstRunOnPre; j 里。

void replaceSameLabel(vector& runLabels, vector>& equivalence) { int maxLabel = *max_element(runLabels.begin(), runLabels.end()); vector> eqTab(maxLabel, vector(maxLabel, false)); vector>::iterator vecPairIt = equivalence.begin(); while (vecPairIt != equivalence.end()) { eqTab[vecPairIt->first - 1][vecPairIt->second - 1] = true; eqTab[vecPairIt->second - 1][vecPairIt->first - 1] = true; vecPairIt++; } vector labelFlag(maxLabel, 0); vector> equaList; vector tempList; cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有