Python中連通域分割Two-Pass算法的原理與實(shí)現(xiàn)詳解
連通域Two-Pass算法
在沒(méi)有任何先驗(yàn)知識(shí)的情況下,想完成連通域的搜索,幾乎最直接的想法,就是遍歷圖像所有像素點(diǎn),如果兩個(gè)像素點(diǎn)相連接,便將二者視為一體,直到遍歷所有的像素。但這種遍歷在遇到類(lèi)似下面的圖像時(shí)就會(huì)出現(xiàn)問(wèn)題。

上面的矩陣中,數(shù)字代表有效像素,如果掃描順序是從左向右,從上到下,那么在掃描第二行時(shí),2并不和1聯(lián)通,3盡管和1,2均聯(lián)通,但只能賦予1個(gè)編號(hào),從而上面的情況最終標(biāo)記如下

所以,原本屬于一個(gè)連通域的3個(gè)像素點(diǎn),被分為1,3和2兩個(gè)區(qū)域,所以需要再去遍歷一次,把相鄰的不同編號(hào)統(tǒng)一,這就是Two-Pass算法的基本思想。
數(shù)據(jù)準(zhǔn)備
在具體實(shí)現(xiàn)算法之前,先準(zhǔn)備一張二值圖像,并封裝一個(gè)繪圖函數(shù)。
import matplotlib.pyplot as plt
from scipy.ndimage import binary_erosion
import numpy as np
path = r"coin.png"
img = plt.imread(path).astype(float)
img = np.mean(img, axis=2)
th = 0.513 # climb(img, 0.1, 0, 0.01)
b = img>0.4
def drawImg(im1, im2, c1='jet', c2='jet'):
fig = plt.figure()
ax = fig.add_subplot(121)
plt.imshow(im1, cmap=c1)
plt.axis('off')
ax = fig.add_subplot(122)
plt.imshow(im2, cmap=c2)
plt.axis('off')
plt.show()
b = img>0.4
bb = binary_erosion(b, np.ones([5,5]))
第一次
第一次掃描的目的是建立當(dāng)前像素與左邊和上邊的像素之間的聯(lián)通關(guān)系,同時(shí)需要一個(gè)字典保存這種映射,為后續(xù)的連通域合并做準(zhǔn)備。
def firstPass(inImg):
L = 0 # 標(biāo)記號(hào)
outImg = inImg.astype(float)
h, w = inImg.shape
dct = {}
for i,j in product(range(h), range(w)):
if inImg[i,j] == 0:
continue
neighbors = [] # 記錄符合要求的鄰域前景
if i-1> 0 and inImg[i-1, j]>0:
neighbors.append(outImg[i-1, j])
if j-1 > 0 and inImg[i, j-1] > 0:
neighbors.append(outImg[i, j-1])
if len(neighbors) == 0:
L += 1
outImg[i,j] = L
dct[L] = [[i],[j]]
else:
tmpL = min(neighbors)
outImg[i,j] = tmpL
dct[tmpL][0].append(i)
dct[tmpL][1].append(j)
return outImg, dct
實(shí)驗(yàn)效果如下
c1, d = firtPass(bb) drawImg(bb, c1)

第二次
第二次掃描的目的是,將屬于同一連通域,但編號(hào)不同的區(qū)域,賦予相同的序號(hào)。為此 ,需要再次遍歷圖像,并通過(guò)第一次遍歷得到的映射字典,來(lái)完成連通域的合并。
def secondPass(outImg, dct):
outImg = outImg * 1
print('version')
for i,j in zip(*np.where(outImg!=0)):
Ls = [outImg[i,j]]
if i-1>0 and outImg[i-1,j] != 0:
Ls.append(outImg[i-1,j])
if j-1 > 0 and outImg[i, j-1] != 0:
Ls.append(outImg[i, j-1])
Ls = np.unique(Ls)
if len(Ls)<2:
continue
minL = np.min(Ls)
for L in Ls:
if L == minL:
continue
y,x = dct[L]
outImg[y,x] = minL
dct[minL][0].extend(dct[L][0])
dct[minL][1].extend(dct[L][1])
del dct[L]
u = np.unique(outImg)
u = np.sort(u) # 排序
N = len(u) - 1 # 此為圖標(biāo)數(shù)
for i in range(1, N+1):
outImg[outImg==u[i]] = i
N = len(np.unique(outImg))
return N, outImg
效果如下
n, c2 = secondPass(c1, d) drawImg(c1, c2)

到此這篇關(guān)于Python中連通域分割Two-Pass算法的原理與實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Python連通域分割內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python異常信息的不同展現(xiàn)方法總結(jié)
在日常開(kāi)發(fā)的過(guò)程中,當(dāng)代碼報(bào)錯(cuò)時(shí),我們通常要不斷打印、閱讀traceback提示信息,來(lái)調(diào)試代碼,這篇文章介紹了如何實(shí)現(xiàn)一個(gè)Exception?Hooks,使得traceback模塊的提示信息更加精確;同時(shí)還介紹了一些第三方庫(kù),這些庫(kù)也提供了Exception?Hooks的功能2022-11-11
Python+Pika+RabbitMQ環(huán)境部署及實(shí)現(xiàn)工作隊(duì)列的實(shí)例教程
RabbitMQ是一個(gè)消息隊(duì)列服務(wù)器,在本文中我們將學(xué)習(xí)到Python+Pika+RabbitMQ環(huán)境部署及實(shí)現(xiàn)工作隊(duì)列的實(shí)例教程,需要的朋友可以參考下2016-06-06
python模塊itsdangerous簡(jiǎn)單介紹
這篇文章主要介紹了python模塊itsdangerous簡(jiǎn)單介紹,本文通過(guò)案例分析給大家詳細(xì)講解,對(duì)python模塊itsdangerous相關(guān)知識(shí)感興趣的朋友一起看看吧2022-11-11
利用Python中的mock庫(kù)對(duì)Python代碼進(jìn)行模擬測(cè)試
這篇文章主要介紹了利用Python中的mock庫(kù)對(duì)Python代碼進(jìn)行模擬測(cè)試,mock庫(kù)自從Python3.3依賴(lài)成為了Python的內(nèi)置庫(kù),本文也等于介紹了該庫(kù)的用法,需要的朋友可以參考下2015-04-04
Python使用百度通用API進(jìn)行翻譯實(shí)現(xiàn)
本文主要介紹了Python使用百度通用API進(jìn)行翻譯實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Python如何使用logging為Flask增加logid
這篇文章主要介紹了Python如何使用logging為Flask增加logid,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-03-03

