Python3实现TwoPass算法检测区域连通性
目录技术背景TwoPass算法测试数据的生成TwoPass算法的实现算法的执行流程标签的重映射其他的测试用例总结概要版权声明参考链接技术背景
连通性检测是图论中常常遇到的一个问题,我们可以用五子棋的思路来理解这个问题五子棋中,横、竖、斜相邻的两个棋子,被认为是相连接的,而一样的道理,在一个二维的图中,只要在横、竖、斜三个方向中的一个存在相邻的情况,就可以认为图上相连通的。比如以下案例中的python数组,3号元素和5号元素就是相连接的,5号元素和6号元素也是相连接的,因此这三个元素实际上是属于同一个区域的:array(〔〔0,3,0〕,〔0,5,0〕,〔6,0,0〕〕)
而再如下面这个例子,其中的1、2、3三个元素是相连的,4、5、6三个元素也是相连的,但是这两个区域不存在连接性,因此这个网格被分成了两个区域:array(〔〔1,0,4〕,〔2,0,5〕,〔3,0,6〕〕)
那么如何高效的检测一张图片或者一个矩阵中的所有连通区域并打上标签,就是我们所关注的一个问题。TwoPass算法
一个典型的连通性检测的方案是TwoPass算法,该算法可以用如下的一张动态图来演示:
该算法的核心在于用两次的遍历,为所有的节点打上分区的标签,如果是不同的分区,就会打上不同的标签。其基本的算法步骤可以用如下语言进行概述:遍历网格节点,如果网格的上、左、左上三个格点不存在元素,则为当前网格打上新的标签,同时标签编号加一;当上、左、左上的网格中存在一个元素时,将该元素值赋值给当前的网格作为标签;当上、左、左上的网格中有多个元素时,取最低值作为当前网格的标签;在标签赋值时,留意标签上边和左边已经被遍历过的4个元素,将4个元素中的最低值与这四个元素分别添加到Union的数据结构中(参考链接1);再次遍历网格节点,根据Union数据结构中的值刷新网格中的标签值,最终得到划分好区域和标签的元素矩阵。测试数据的生成
这里我们以Python3为例,可以用Numpy来产生一系列随机的01矩阵,这里我们产生一个2020大小的矩阵:twopass。pyimportnumpyasnpimportmatplotlib。pyplotaspltifnamemain:np。random。seed(1)graphnp。random。choice(〔0,1〕,size(20,20))print(graph)plt。figure()plt。imshow(graph)plt。savefig(randombingraph。png)
执行的输出结果如下:python3twopass。py〔〔11001111100101100100〕〔01001000100011111000〕〔11111101100100111010〕〔01101111001100001110〕〔10011011010011101101〕〔11100000111111100000〕〔01111110011001000111〕〔11010100011101000010〕〔10111000000100100110〕〔00100001000011001110〕〔00001110110001101110〕〔11110100101011011011〕〔10101011111100110001〕〔10000011111110010001〕〔01010000110001011001〕〔01000101011101011110〕〔01000011011001111111〕〔00000001000001111000〕〔10101000000100010110〕〔01101010110010000011〕〕
同时会生成一张网格的图片:
其实从这个图片中我们可以看出,图片的上面部分几乎都是连接在一起的,只有最下面存在几个独立的区域。TwoPass算法的实现
这里需要说明的是,因为我们并没有使用Union的数据结构,而是只使用了Python的字典数据结构,因此代码写起来会比较冗余而且不是那么美观,但是这里我们主要的目的是先用代解决这一实际问题,因此代码乱就乱一点吧。twopass。pyimportnumpyasnpimportmatplotlib。pyplotaspltfromcopyimportdeepcopydeffirstpass(g)list:graphdeepcopy(g)heightlen(graph)widthlen(graph〔0〕)label1indexdict{}forhinrange(height):forwinrange(width):ifgraph〔h〕〔w〕0:continueifh0andw0:graph〔h〕〔w〕labellabel1continueifh0andgraph〔h〕〔w1〕0:graph〔h〕〔w〕graph〔h〕〔w1〕continueifw0andgraph〔h1〕〔w〕0:ifgraph〔h1〕〔w〕graph〔h1〕〔min(w1,width1)〕:graph〔h〕〔w〕graph〔h1〕〔w〕indexdict〔graph〔h1〕〔min(w1,width1)〕〕graph〔h1〕〔w〕elifgraph〔h1〕〔min(w1,width1)〕0:graph〔h〕〔w〕graph〔h1〕〔min(w1,width1)〕indexdict〔graph〔h1〕〔w〕〕graph〔h1〕〔min(w1,width1)〕continueifh0orw0:graph〔h〕〔w〕labellabel1continueneighbors〔graph〔h1〕〔w〕,graph〔h〕〔w1〕,graph〔h1〕〔w1〕,graph〔h1〕〔min(w1,width1)〕〕neighborslist(filter(lambdax:x0,neighbors))iflen(neighbors)0:graph〔h〕〔w〕min(neighbors)forninneighbors:ifninindexdict:indexdict〔n〕min(indexdict〔n〕,min(neighbors))else:indexdict〔n〕min(neighbors)continuegraph〔h〕〔w〕labellabel1returngraph,indexdictdefremap(idxdict)dict:indexdictdeepcopy(idxdict)foridinidxdict:idvidxdict〔id〕whileidvinidxdict:ifidvidxdict〔idv〕:breakidvidxdict〔idv〕indexdict〔id〕idvreturnindexdictdefsecondpass(g,indexdict)list:graphdeepcopy(g)heightlen(graph)widthlen(graph〔0〕)forhinrange(height):forwinrange(width):ifgraph〔h〕〔w〕0:continueifgraph〔h〕〔w〕inindexdict:graph〔h〕〔w〕indexdict〔graph〔h〕〔w〕〕returngraphdefflatten(g)list:graphdeepcopy(g)fgraphsorted(set(list(graph。flatten())))flattendict{}foriinrange(len(fgraph)):flattendict〔fgraph〔i〕〕igraphsecondpass(graph,flattendict)returngraphifnamemain:np。random。seed(1)graphnp。random。choice(〔0,1〕,size(20,20))graph1,idxdictfirstpass(graph)idxdictremap(idxdict)graph2secondpass(graph1,idxdict)graph3flatten(graph2)print(graph3)plt。subplot(131)plt。imshow(graph)plt。subplot(132)plt。imshow(graph3)plt。subplot(133)plt。imshow(graph30)plt。savefig(randombingraph。png)
完整代码的输出如下所示:python3twopass。py〔〔11001111100101100100〕〔01001000100011111000〕〔11111101100100111010〕〔01101111001100001110〕〔10011011010011101101〕〔11100000111111100000〕〔01111110011001000111〕〔11010100011101000010〕〔10111000000100100110〕〔00100001000011001110〕〔00001110110001101110〕〔11110100101011011011〕〔10101011111100110001〕〔10000011111110010001〕〔01020000110001011001〕〔01000101011101011110〕〔01000011011001111111〕〔00000001000001111000〕〔30304000000500010110〕〔03304060770050000011〕〕
同样的我们可以看看此时得到的新的图像:
这里我们并列的画了三张图,第一张图是原图,第二张图是划分好区域和标签的图,第三张是对第二张图进行二元化的结果,以确保在运算过程中没有丢失原本的信息。经过确认这个标签的结果划分是正确的,但是因为涉及到一些算法实现的细节,这里我们还是需要展开来介绍一下。算法的执行流程ifnamemain:np。random。seed(1)graphnp。random。choice(〔0,1〕,size(20,20))graph1,idxdictfirstpass(graph)idxdictremap(idxdict)graph2secondpass(graph1,idxdict)graph3flatten(graph2)
这个部分是算法的核心框架,在本文中的算法实现流程为:先用firstpass遍历一遍网格节点,按照上一个章节中介绍的TwoPass算法打上标签,并获得一个映射关系;然后用remap将上面得到的映射关系做一个重映射,确保每一个级别的映射都对应到了最根部(可以联系参考链接1的内容进行理解,虽然这里没有使用Union的数据结构,但是本质上还是一个树形的结构,需要做一个重映射);然后用secondpass执行TwoPass算法的第二次遍历,得到一组打上了新的独立标签的网格节点;最后需要用flatten将标签进行压平,因为前面映射的关系,有可能导致标签不连续,所以我们这里又做了一次映射,确保标签是连续变化的,实际应用中可以不使用这一步。标签的重映射
关于节点的遍历,大家可以直接看算法代码,这里需要额外讲解的是标签的重映射模块的代码:defremap(idxdict)dict:indexdictdeepcopy(idxdict)foridinidxdict:idvidxdict〔id〕whileidvinidxdict:ifidvidxdict〔idv〕:breakidvidxdict〔idv〕indexdict〔id〕idvreturnindexdict
这里的算法是先对得到的标签进行遍历,在字典中获取当前标索引所对应的值,作为新的索引,直到键跟值一致为止,相当于在一个树形的数据结构中重复寻找父节点直到找到根节点。其他的测试用例
这里我们可以再额外测试一些案例,比如增加几个0元素使得网格节点更加稀疏:graphnp。random。choice(〔0,0,0,1〕,size(20,20))
得到的结果图片如下所示:
还可以再稀疏一些:graphnp。random。choice(〔0,0,0,0,0,1〕,size(20,20))
得到的结果如下图所示:
越是稀疏的图,得到的分组结果就越分散。总结概要
在本文中我们主要介绍了利用TwoPass的算法来检测区域连通性,并给出了Python3的代码实现,当然在实现的过程中因为没有使用到Union这样的数据结构,仅仅用了字典来存储标签之间的关系,因此效率和代码可读性都会低一些,单纯作为用例的演示和小规模区域划分的计算是足够用了。在该代码实现方案中,还有一点与原始算法不一致的是,本实现方案中打新的标签是读取上、上左和左三个方向的格点,但是存储标签的映射关系时,是读取了上、上左、上右和左这四个方向的格点。版权声明
本文首发链接为:https:www。cnblogs。comdechinphyptwopass。html
作者ID:DechinPhy
更多原著文章请参考:https:www。cnblogs。comdechinphy
打赏专用链接:https:www。cnblogs。comdechinphygalleryimage379634。html
腾讯云专栏同步:https:cloud。tencent。comdevelopercolumn91958参考链接https:blog。csdn。netlichengyuarticledetails13986521https:www。cnblogs。comriddickp8280883。html
姜丹尼尔下面好大他被诊断有抑郁症了如今的姜丹尼尔是很出名的韩国男明星,他的身材和的颜值都是很高的,而且也真的是一个特别的有才华的人,姜丹尼尔下面好大怎么回事?因为自己女朋友的事情,真是得罪了不少的粉丝,在粉丝的……
1天上6次厕所,亚马逊员工被解雇了上厕所太多会被老板开除,这个网上流传已久的段子现在变成真事了。亚马逊仓库资料图新华社法新据《商业内幕》8月24日报道,一名前亚马逊仓库员工近日正在起诉该公司,她声称……
港股尾盘拉升阿里巴巴收涨超5惟B站再创新低周四,在科技股反弹的带动下,香港股市触底回升,主要指数集体转涨;恒指涨0。72,早盘一度跌0。86。尽管美联储会议纪要鹰派,但此前港股已经连续大跌,而南下资金乘机买入。恒……
Redmi10将在国外开售,起售价格预计在千元以内小米官方推特公布,Redmi10将要现身。除此之外,小米官方推特公布了Redmi10的外观。如下图所示,Redmi10后背与小米10至尊纪念版类似,为矩阵相机设计构……
姜振宇为什么不上非诚勿扰了是不是有什么原因《非常勿扰》是很多人都比较喜欢的一档节目。开播十年以来收视一直都很高。在这个舞台上大家除了见到到不同身份的男女嘉宾之外,还通过这个舞台认识了一些驻场情感导师。姜振宇就是其中的一……
智能家居强电开关面板1、强电面板概述强电开关面板一般指的是面板背后的接线端子输出信号是220V,该信号直接驱动家用电器,如灯,风扇,排气扇,插座,等等。面板正面,24键面板背面……
水岛津实最惨的一部腰振口吐白沫是剧情安排吗水岛津实是岛国的AV,其实是真的很难想象水岛津实为什么会如此做,在国外是很出名,只是说水岛津实的作品出现之后,争得了比较多的人模仿,不过最近网络中说水岛津实最惨的一部腰振口吐白……
迟重瑞为什么38才结婚结婚后有几个孩子作为中国的四大名著之一《西游记》,每年的假期档都会播出。如果有人说没有看过西游记还真的是有些难以置信了。可以说西游记在国人的心目中是很有中国代表性的电视剧作品之一。因为西游记当……
有没有人还记得PocketPC?PocketPC发展史一、PocketPC发展史1、从PDA到WINCE1993年,苹果公司推出史上首台PDANewton(PersonalDigitalAssis……
818大促购机攻略从1000到6000,各价位最值得买手机都随着手机市场的快速发展,在国内发布的手机型号越来越多,这就导致了如何选择最好的手机成为了消费者头疼的问题。我也是对比了30多款各价位热门手机,历经3天结合手机产品力以及口碑等多……
晚香玉咬人猫黑历史cos圈有名的绿茶婊做了什么晚香玉和咬人猫其实是cos圈子中的比较有名的模特了,只是两个人最近被说有什么矛盾,还有人在好奇晚香玉和咬人猫两个人之间的关系,据说曾经在网络中闹的还是非常僵硬啊!最近则是有人在……
你被App窥视跟踪了吗?来源:人民政协报记者李木元周佳佳黄喆经常听到周围的人跟我说,和朋友聚会时刚谈到一个商品,不久后在手机中就出现了该商品的广告;还有人跟我反映,在用平台类App如打车软……