Python+OpenCV實(shí)現(xiàn)分水嶺分割算法的示例代碼
前言
分水嶺算法是用于分割的經(jīng)典算法,在提取圖像中粘連或重疊的對(duì)象時(shí)特別有用,例如下圖中的硬幣。
使用傳統(tǒng)的圖像處理方法,如閾值和輪廓檢測(cè),我們將無(wú)法從圖像中提取每一個(gè)硬幣,但通過(guò)利用分水嶺算法,我們能夠檢測(cè)和提取每一個(gè)硬幣。
在使用分水嶺算法時(shí),我們必須從用戶定義的標(biāo)記開(kāi)始。這些標(biāo)記可以通過(guò)點(diǎn)擊手動(dòng)定義,或者我們可以使用閾值和/或形態(tài)學(xué)操作等方法自動(dòng)或啟發(fā)式定義它們。
基于這些標(biāo)記,分水嶺算法將輸入圖像中的像素視為地形——該方法通過(guò)“淹沒(méi)”山谷,從標(biāo)記開(kāi)始向外移動(dòng),直到不同標(biāo)記相遇。為了獲得準(zhǔn)確的分水嶺分割,必須正確放置標(biāo)記。
在這篇文章的剩下部分,我將向您展示如何使用分水嶺算法來(lái)分割和提取圖像中既粘連又重疊的對(duì)象。
為此,我們將使用各種 Python 包,包括 SciPy、scikit-image 和 OpenCV。
在上圖中,您可以看到使用簡(jiǎn)單閾值和輪廓檢測(cè)無(wú)法提取對(duì)象,由于這些對(duì)象是粘連的、重疊的或兩者兼有,
因此簡(jiǎn)單的輪廓提取會(huì)將粘連的對(duì)象視為單個(gè)對(duì)象,而不是多個(gè)對(duì)象。
1.使用分水嶺算法進(jìn)行分割
# 打開(kāi)一個(gè)新文件,將其命名為 watershed.py ,然后插入以下代碼:
# 打開(kāi)一個(gè)新文件,將其命名為 watershed.py ,然后插入以下代碼: # 導(dǎo)入必要的包 from skimage.feature import peak_local_max from skimage.morphology import watershed from scipy import ndimage import numpy as np import argparse import imutils import cv2 # 構(gòu)造參數(shù)解析并解析參數(shù) ap = argparse.ArgumentParser() # ap.add_argument("-i", "--image", default="HFOUG.jpg", help="path to input image") ap.add_argument("-i", "--image", default="watershed_coins_01.jpg", help="path to input image") args = vars(ap.parse_args()) # 加載圖像并執(zhí)行金字塔均值偏移濾波以輔助閾值化步驟 image = cv2.imread(args["image"]) shifted = cv2.pyrMeanShiftFiltering(image, 21, 51) cv2.imshow("Input", image) # 將圖像轉(zhuǎn)換為灰度,然后應(yīng)用大津閾值 gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv2.imshow("Thresh", thresh) # 計(jì)算從每個(gè)二進(jìn)制圖像中的像素到最近的零像素的精確歐氏距離,然后找出這個(gè)距離圖中的峰值 D = ndimage.distance_transform_edt(thresh) # 可視化距離函數(shù) D_show = cv2.normalize(D, None, 0, 1, cv2.NORM_MINMAX) # print(np.max(D_show)) cv2.imshow("D_show", D_show) # 以坐標(biāo)列表(indices=True)或布爾掩碼(indices=False)的形式查找圖像中的峰值。峰值是2 * min_distance + 1區(qū)域內(nèi)的局部最大值。 # (即峰值之間至少相隔min_distance)。此處我們將確保峰值之間至少有20像素的距離。 localMax = peak_local_max(D, indices=False, min_distance=20, labels=thresh) # 可視化localMax temp = localMax.astype(np.uint8) cv2.imshow("localMax", temp * 255) # 使用8-連通性對(duì)局部峰值進(jìn)行連接成分分析,然后應(yīng)用分水嶺算法 # scipy.ndimage.label(input, structure=None, output=None) # input :待標(biāo)記的數(shù)組對(duì)象。輸入中的任何非零值都被視為待標(biāo)記對(duì)象,零值被視為背景。 # structure:定義要素連接的結(jié)構(gòu)化元素。對(duì)于二維數(shù)組。默認(rèn)是四連通, 此處選擇8連通 # markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 可視化markers temp_markers = markers.astype(np.uint8) cv2.imshow("temp_markers", temp_markers * 20) # 由于分水嶺算法假設(shè)我們的標(biāo)記代表距離圖中的局部最小值(即山谷),因此我們?nèi)?D 的負(fù)值。 labels = watershed(-D, markers, mask=thresh) print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1)) # 循環(huán)遍歷分水嶺算法返回的標(biāo)簽 for label in np.unique(labels): # 0表示背景,忽略它 if label == 0: continue # 否則,為標(biāo)簽區(qū)域分配內(nèi)存并將其繪制在掩碼上 mask = np.zeros(gray.shape, dtype="uint8") mask[labels == label] = 255 # 在mask中檢測(cè)輪廓并抓取最大的一個(gè) cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) c = max(cnts, key=cv2.contourArea) # 在物體周圍畫一個(gè)圓 ((x, y), r) = cv2.minEnclosingCircle(c) cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2) cv2.putText(image, "#{}".format(label), (int(x) - 10, int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) # 顯示輸出圖像 cv2.imshow("Output", image) cv2.waitKey(0)
2.Watershed與random walker分割對(duì)比
示例比較了兩種分割方法,以分離兩個(gè)相連的磁盤:分水嶺算法和隨機(jī)游走算法。 兩種分割方法都需要種子,即明確屬于某個(gè)區(qū)域的像素。在這里,到背景的距離圖的局部最大值被用作種子。
import numpy as np from skimage.segmentation import watershed from skimage.feature import peak_local_max from skimage import measure from skimage.segmentation import random_walker import matplotlib.pyplot as plt from scipy import ndimage # import cv2 # Generate an initial image with two overlapping circles x, y = np.indices((80, 80)) x1, y1, x2, y2 = 28, 28, 44, 52 r1, r2 = 16, 20 mask_circle1 = (x - x1) ** 2 + (y - y1) ** 2 < r1 ** 2 mask_circle2 = (x - x2) ** 2 + (y - y2) ** 2 < r2 ** 2 image = np.logical_or(mask_circle1, mask_circle2) # Now we want to separate the two objects in image # Generate the markers as local maxima of the distance # to the background distance = ndimage.distance_transform_edt(image) D_show = distance/np.max(distance) # D_show = cv2.normalize(distance, None, 0, 1, cv2.NORM_MINMAX) local_maxi = peak_local_max( distance, indices=False, footprint=np.ones((3, 3)), labels=image) markers = measure.label(local_maxi) labels_ws = watershed(-distance, markers, mask=image) markers[~image] = -1 labels_rw = random_walker(image, markers) plt.figure(figsize=(12, 3.5)) plt.subplot(141) plt.imshow(image, cmap='gray', interpolation='nearest') plt.axis('off') plt.title('image') plt.subplot(142) plt.imshow(D_show, cmap='Spectral',interpolation='nearest') plt.axis('off') plt.title('distance map') plt.subplot(143) plt.imshow(labels_ws, cmap='Spectral', interpolation='nearest') plt.axis('off') plt.title('watershed segmentation') plt.subplot(144) plt.imshow(labels_rw, cmap='Spectral', interpolation='nearest') plt.axis('off') plt.title('random walker segmentation') plt.tight_layout() plt.show()
到此這篇關(guān)于Python+OpenCV實(shí)現(xiàn)分水嶺分割算法的示例代碼的文章就介紹到這了,更多相關(guān)Python OpenCV分水嶺分割算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python:Scrapy框架中Item Pipeline組件使用詳解
這篇文章主要介紹了Python:Scrapy框架中Item Pipeline組件使用詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12python實(shí)現(xiàn)數(shù)字炸彈游戲程序
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)數(shù)字炸彈游戲程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07利用scrapy將爬到的數(shù)據(jù)保存到mysql(防止重復(fù))
這篇文章主要給大家介紹了關(guān)于利用scrapy將爬到的數(shù)據(jù)保存到mysql(防止重復(fù))的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2018-03-03使用相同的Apache實(shí)例來(lái)運(yùn)行Django和Media文件
這篇文章主要介紹了使用相同的Apache實(shí)例來(lái)運(yùn)行Django和Media文件,Django是最具人氣的Python web開(kāi)發(fā)框架,需要的朋友可以參考下2015-07-07Django項(xiàng)目之Elasticsearch搜索引擎的實(shí)例
今天小編就為大家分享一篇Django項(xiàng)目之Elasticsearch搜索引擎的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08Python使用Matplotlib模塊時(shí)坐標(biāo)軸標(biāo)題中文及各種特殊符號(hào)顯示方法
這篇文章主要介紹了Python使用Matplotlib模塊時(shí)坐標(biāo)軸標(biāo)題中文及各種特殊符號(hào)顯示方法,結(jié)合具體實(shí)例分析了Python使用Matplotlib模塊過(guò)程中針對(duì)中文及特殊符號(hào)的顯示方法,需要的朋友可以參考下2018-05-05matplotlib savefig 保存圖片大小的實(shí)例
今天小編就為大家分享一篇matplotlib savefig 保存圖片大小的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05python 使用三引號(hào)時(shí)容易犯的小錯(cuò)誤
這篇文章主要介紹了python 使用三引號(hào)時(shí)容易犯的小錯(cuò)誤,幫助新手學(xué)習(xí),避免入坑,感興趣的朋友可以了解下2020-10-10Windows上使用Python增加或刪除權(quán)限的方法
下面小編就為大家分享一篇Windows上使用Python增加或刪除權(quán)限的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04