本博文基于python-opencv實現(xiàn)了按照面積閾值篩選連通域、按照面積排序篩選topK連通域、 連通域細(xì)化(連通域骨架提取)、連通域分割(基于分水嶺算法使連通域在細(xì)小處斷開)、按照面積排序賽選topK輪廓等常見的連通域處理代碼。并將代碼封裝為shapeUtils類,在自己的python代碼中import shapeUtil后即可使用相應(yīng)的連通域處理方法。
1、背景知識
1.1 輪廓
輪廓(Contour )由連續(xù)的點組成,以線條的形式聚集在一起,通常是一個有x,y組成的點集,形式為N x 2(N表示輪廓中有n個點)。其是空心的,通常所統(tǒng)計的輪廓面積是那一圈線所包含的面積。在opencv中使用cv2.findContours來查找輪廓,使用cv2.contourArea來統(tǒng)計輪廓包含的面積,使用cv2.drawContours繪制輪廓。如下圖就包含了2個輪廓
1.2 連通域
連通域(Connection)由在空間上連續(xù)(相鄰)的像素點組成,是一個圖形區(qū)域。相鄰的標(biāo)準(zhǔn)有4連通域和8連通域,具體可以參考https://zhuanlan.zhihu.com/p/394073982。對二值圖統(tǒng)計完連通域后,得到一個labels圖,具體如下右圖所示,其背景區(qū)域被標(biāo)記為0,每個聯(lián)通域的值都從原來的255更改為連通域序號。下圖是按照8連通域的方式進(jìn)行統(tǒng)計的,如果按照4連通域進(jìn)行統(tǒng)計,那么標(biāo)記為2的連通域就會被斷開為兩個(總共會有5個連通域,標(biāo)簽從1~5)。
1.3 連通域與輪廓的轉(zhuǎn)換
連通域信息與輪廓存在本質(zhì)的區(qū)別,聯(lián)通域是一個形狀(Mat),輪廓是一個閉合的線條點集(list,元素為坐標(biāo))。我們可以使用cv2.drawContours將輪廓繪制為連通域,也可以使用cv2.findContours統(tǒng)計連通域的輪廓信息。在某些情況下,一個輪廓就可以對應(yīng)一個連通域;當(dāng)連通域中存在孔洞的時候,則需要多個輪廓才能表示一個連通域。
具體如下圖所示,當(dāng)連通域沒有孔洞時,可以轉(zhuǎn)換為一個輪廓;當(dāng)前存在一個孔洞時,則需要轉(zhuǎn)換2個輪廓。
2、連通域處理方法
2.1 按照面積閾值篩選連通域
通過cv2.connectedComponentsWithStats函數(shù)統(tǒng)計出聯(lián)通域的信息,labels為連通域標(biāo)記圖(具體參考1.2中的描述),stats為聯(lián)通域統(tǒng)計信息(可見代碼中的注釋,其包含連通域的xywhs信息),通過對聯(lián)通域統(tǒng)計信息stats
的判斷(將連通域面積與閾值threshold進(jìn)行比較
),修改連通域標(biāo)記圖labels
(將小于閾值的連通域標(biāo)簽值修改為0
)。最后通過二值化方法,將聯(lián)通域標(biāo)記圖轉(zhuǎn)換為二值圖。
import cv2
import numpy as np
class shapeUtils:
def find_big_areo(img,threshold=1000):
#https://blog.csdn.net/weixin_44599604/article/details/111687531
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img, connectivity=8)
#stats的格式為二維數(shù)組,其中每一個元素為 x,y,w,h,s的格式,s為聯(lián)通域面積
'''
stats #我們看出有3個連通區(qū)域
# x y w h s
>>> array([[ 0, 0, 10, 10, 76], # 這代表整個圖片,0值也有連通區(qū)域
[ 4, 1, 5, 6, 18], # 這里18代表有18個像素 下面的6同理
[ 2, 2, 3, 2, 6]], dtype=int32)
'''
for i in range(1,stats.shape[0]):
conj=i#獲取聯(lián)通域的標(biāo)記值
areo=stats[i,4]
if areo<threshold:
labels[labels==conj]=0
labels=labels.astype(np.uint8)
ret,labels=cv2.threshold(labels,1,255,cv2.THRESH_BINARY)
return labels
img=cv2.imread("res.png",0)
ret,img=cv2.threshold(img,64,255,cv2.THRESH_BINARY)
im2=shapeUtils.find_big_areo(img,5000)
cv2.imshow("img",img)
cv2.imshow("labels",im2)
cv2.waitKey()
文章來源:http://www.zghlxwxcb.cn/news/detail-765591.html
2.2 按照面積排序篩選topK連通域
按照面積排序篩選topK連通域。先使用connectedComponentsWithStats統(tǒng)計出labels和stats,然后創(chuàng)建一個行號(其實就是labels中連通域的標(biāo)簽值
),并使其與stats的shape相同并將其與stats拼接在一起(在原始的stats中,第i個信息對應(yīng)著標(biāo)簽值為i的聯(lián)通域,對stats按面積排序后則會無法正常對應(yīng),故需要進(jìn)行拼接
),然后使用np.argsort對stats進(jìn)行排序,在根據(jù)排序結(jié)果將topk個連通域后的標(biāo)簽值全部修改為0(將topk后的連通域刪除
),最后通過二值化方法,將聯(lián)通域標(biāo)記圖轉(zhuǎn)換為二值圖。文章來源地址http://www.zghlxwxcb.cn/news/detail-765591.html
import cv2
import numpy as np
class shapeUtils:
def find_topK_areo(img,k=1):
#https://blog.csdn.net/weixin_44599604/article/details/111687531
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img, connectivity=8)
#stats的格式為二維數(shù)組,其中每一個元素為 x,y,w,h,s的格式,s為聯(lián)通域面積
'''
stats #我們看出有3個連通區(qū)域
# x y w h s
>>> array([[ 0, 0, 10, 10, 76], # 這代表整個圖片,0值也有連通區(qū)域
[ 4, 1, 5, 6, 18], # 這里18代表有18個像素 下面的6同理
[ 2, 2, 3, 2, 6]], dtype=int32)
'''
#創(chuàng)建一個行號,并使其與stats的shape相同
rows_num=[x for x in range(stats.shape[0])]
rows_num=np.array(rows_num)#shape (3)
rows_num=rows_num.reshape((-1,1)) #shape (3,1)
print(rows_num.shape,stats.shape)
#數(shù)據(jù)維度變化:(3, 1) (3, 5)=>(3, 6)
stats=np.concatenate((rows_num,stats),axis=1)#拼接時要僅有一個維度不同,才能拼接
#此時的stats的格式為二維數(shù)組,其中每一個元素為 x,y,w,h,s的格式,s為聯(lián)通域面積
'''
拼接后的 stats 如下所示
#row x y w h s
>>> array([[0, 0, 0, 10, 10, 76], # 這代表整個圖片,0值也有連通區(qū)域
[1, 4, 1, 5, 6, 18], # 這里18代表有18個像素 下面的6同理
[2, 2, 2, 3, 2, 6]], dtype=int32)
'''
#安裝面積對連通域進(jìn)行排序
sortId=np.argsort(stats[:,-1])#生成一個排序好的下標(biāo),從小到大排序
sortId=sortId[::-1]#對下標(biāo)進(jìn)行逆序,使其變?yōu)閺拇蟮叫〉呐判?/span>
stats=stats[sortId]#根據(jù)序號重新取數(shù)據(jù)
#stats=stats[np.argsort(stats[:,-1])[::-1] ]
print(stats)
#將第k面積個后的連通域label設(shè)置為0
for i in range(k+1,stats.shape[0]):
conj=stats[i][0]#獲取聯(lián)通域的標(biāo)記值
labels[labels==conj]=0
print(labels
到了這里,關(guān)于opencv 十六 python下各種連通域處理方法(按面積閾值篩選連通域、按面積排序篩選連通域、連通域分割等方法)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!