给定0-1矩阵求连通域_xunan003的博客-CSDN博客_连通域
求0-1矩阵连通域这篇文章方法讲的算是比较详细,但是程序由于我没学过C++,再加上编程经验很少,这篇博文给出的程序一致没怎么看懂,啃了两天终于有点眉目了,在此做一个总结,也跟小白们分享一下,希望也能对你们有帮助。
求连通域的方法分为三步
第一步:(打开冰箱)对一个二维矩阵,先求出每一行白色团的起始和终止坐标(列),并给每个团标记序列
第二步:合并密接团的标签,鉴别密接的方式是通过轨迹(列)重叠的方式,轨迹重叠,标签归化,若某团与两类不同的团发生密接,代表这两类团等价(初筛阶段以2个为标准),需要统一隔离。
第三步:对等价团进行汇总筛选,凡有交叠的统一归为一类,以此分为不同的感染群类。
给出了三段程序,分别对应上述的三个步骤。
void fillRunVectors(const Mat& bwImage, int& NumberOfRuns, vector阅读程序的步骤& stRun, vector & enRun, vector & rowRun) void firstPass(vector & stRun, vector & enRun, vector & rowRun, int NumberOfRuns, vector & runLabels, vector >& equivalences, int offset) void replaceSameLabel(vector & runLabels, vector >& equivalence)
第一段程序
一、理解程序目的和实现方式拿到每一段程序一定要完全搞清楚程序做了什么事情,是怎么实现的,如果没有详细说明怎么实现的,就需要猜测验证,否则程序会读的很痛苦,就像一篇不认识单词也不知所云的阅读理解。
第一段程序完成每一行白色团的起始和终止坐标,并给每个团标记序列。
1、如何标记坐标
第i行的第j1个点为白色团的起始坐标j2个点为终止坐标,就需要将j1压入start队列中,j2压入end队列中,并对应处其属于第i行。
2、如何分辨起始和终止坐标
起始点(1、第一列为白色像素;2、此列为白色像素,同时左列为黑色像素)
终止点(1、此列为白色色像素,右列为黑色像素;2、最后一列为白色像素)
二、理解程序中由大到小的结构对应完成的功能(结构自大到小)主体有两个for循环,每个for循环是在循环什么?
i循环行,j循环列,找到起始和终止点的坐标
条件语句是做什么判断(看小括号的内容)?选择后实现什么功能(看大括号最后的结论)?
判断是否为白像素,分别压入了坐标值
三、根据变量的行为,对应目标功能就可以猜测出每个变量的指代和含义NumberofRuns代表白团的数目,rowrun对应每个白团所属的行数
strun代表每个白团的起始坐标,enrun代表每个白团的终止坐标
四、最后综合复盘程序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); } } }
第二段程序
依旧是按照上面的四个步骤
一、firstPass函数完成团的标记与等价对列表的生成
a、第一行的团,分别给出标号;
b1、对于除了第一行外的所有行里的团,如果它与前一行中的所有团都没有重合区域,则给它一个新的标号;
b2、如果它仅与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它;
b3、上一行的2个以上的团有重叠区域,则给当前团赋一个相连团的最小标号,并将上一行的这几个团的标记分别写入等价对,说明它们属于一类(等价对是两个团之间的)
1、如何判定相邻行的两个团有重合?
根据两个团的起始和终点列标重合情况进行判断,curstart>preend&curend
二、依旧存在两个嵌套的for循环
for循环实现遍历的功能,根据循环的上限可以提示循环的对象
这一段功能需要遍历所有的团,读取每个团的起始和终止坐标
需要遍历上一行所有的团,读取上一行每个团的起始和终止坐标
将二者信息比对进行重叠判断
第一个条件句实现递归内容的更新替换(上一行内容更新)
第二个条件句实现重复判定,并对团进行标号,生成等价对
三、大胆猜测小心求证每个变量的含义
strun代表团的起始坐标,enrun代表终止坐标,rowrun代表团对应的行数,currowidx代表当前判断行的行数,runlabels代表团的标记,equivalence存储等价对
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 <= lastRunOnPre; j++) { if (stRun[i] <= enRun[j] + offset && enRun[i] >= stRun[j] - offset && rowRun[i] == rowRun[j] + 1) { if (runLabels[i] == 0) // 没有被标号过 runLabels[i] = runLabels[j]; else if (runLabels[i] != runLabels[j])// 已经被标号 equivalences.push_back(make_pair(runLabels[i], runLabels[j])); // 保存等价对 } } if (runLabels[i] == 0) // 没有与前一列的任何run重合 { runLabels[i] = idxLabel++; } } } 第三段程序
一、对等价对的处理,需要将它转化为若干个等价序列
比如有如下等价对:
(1,2),(1,6),(3,7),(9-3),(8,1),(8,10),(11,5),(11,8),(11,12),(11,13),(11,14),(15,11)
我们需要得到最终序列是:
1-2-5-6-8-10-11-12-13-14-15
3-7-9
4
采用方法是图像深入优先遍历的原理,进行等价序列的查找。
不要着急读程序,先把方法对应的原理吃透,否则读程序会读得很痛苦,看似已经开始了,其实磕磕绊绊地读不下去,理解不了,也不要一个字一个字的读程序(就像英语阅读理解一样),要按照结构和功能读程序。
图像深入优先遍历原理
找到所有团V对应等价(有链接关系)的团W,以及V的相对于W的下一个邻接点。
参考文献:
图的遍历:深度优先遍历(DFS)_Uncertainty!!的博客-CSDN博客_深度优先遍历
二、根据深入优先遍历原理,寻找连通域的过程就需要进行这样几个嵌套的遍历过程。
首先需要遍历所有团(矩阵列自上到下);需要遍历与某个团等价的团(矩阵行自左到右);还需要遍历与这个等价团邻接的等价团(矩阵列自上到下)
分别对应i
三、猜测每个变量的含义
maxLabel代表图数,eqTab代表等价对矩阵,vecPairIt代表循环器,作用是遍历整个eqTab
labelFlag代表团所属与的类,tempList代表暂时一类的等价序列,equaList代表最终的等价序列
void replaceSameLabel(vectorPS:程序理解的一些小问题记录& 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 << maxLabel << endl; for (int i = 1; i <= maxLabel; i++) { if (labelFlag[i - 1]) { continue; } labelFlag[i - 1] = equaList.size() + 1; tempList.push_back(i); for (vector ::size_type j = 0; j < tempList.size(); j++) { for (vector ::size_type k = 0; k != eqTab[tempList[j] - 1].size(); k++) { if (eqTab[tempList[j] - 1][k] && !labelFlag[k]) { tempList.push_back(k + 1); labelFlag[k] = equaList.size() + 1; } } } equaList.push_back(tempList); tempList.clear(); } cout << equaList.size() << endl; for (vector ::size_type i = 0; i != runLabels.size(); i++) { runLabels[i] = labelFlag[runLabels[i] - 1]; } } 1、if(a);如果a为真就继续,这里的真包括非0数字和字符。
2、vector
(maxLabel, false) a(b,false); 在C#中-789找 (789zhao.com)">std :: vector a(b,false); 在C#中-789找 (789zhao.com) 3、iterator vecPairIt C++迭代器iterator详解_C 语言_脚本之家 (jb51.net)
4、vecPairIt->透析_范红康的博客-CSDN博客"> (1条消息) C中的->透析_范红康的博客-CSDN博客
5、eqTab[vecPairIt->first - 1][vecPairIt->second - 1](1条消息) c++ 里面的map容器的迭代器 first second_imik的博客-CSDN博客_map的first和second
6、 vector
> 用vector实现二维向量 - 百度文库 (baidu.com) 7、"&&"、"||"和"!"。a && b,一假必假,结合性从左至右。||是逻辑或运算符,a || b,一真必真,结合性从左至右,&&!是要&&左边成立且右边不成立才成立;
结束,为什么感觉这么小的程序就好难,5555,开始怀疑适不适合干图像算法,加油呀小猴!
最后感谢在解决问题过程中各位大佬的帮助,如果我的回答帮助到你,还麻烦给我点赞加油呀~~



