python图像分割算法_OpenCV-Python 图像分割与Watershed算法 | 三十四
目標(biāo)
在本章中,
我們將學(xué)習(xí)使用分水嶺算法實(shí)現(xiàn)基于標(biāo)記的圖像分割
我們將看到:cv.watershed()
理論
任何灰度圖像都可以看作是一個(gè)地形表面,其中高強(qiáng)度表示山峰,低強(qiáng)度表示山谷。你開始用不同顏色的水(標(biāo)簽)填充每個(gè)孤立的山谷(局部最小值)。隨著水位的上升,根據(jù)附近的山峰(坡度),來自不同山谷的水明顯會(huì)開始合并,顏色也不同。為了避免這種情況,你要在水融合的地方建造屏障。你繼續(xù)填滿水,建造障礙,直到所有的山峰都在水下。然后你創(chuàng)建的屏障將返回你的分割結(jié)果。這就是Watershed背后的“思想”。你可以訪問Watershed的CMM網(wǎng)頁,了解它與一些動(dòng)畫的幫助。
但是這種方法會(huì)由于圖像中的噪聲或其他不規(guī)則性而產(chǎn)生過度分割的結(jié)果。因此OpenCV實(shí)現(xiàn)了一個(gè)基于標(biāo)記的分水嶺算法,你可以指定哪些是要合并的山谷點(diǎn),哪些不是。這是一個(gè)交互式的圖像分割。我們所做的是給我們知道的對(duì)象賦予不同的標(biāo)簽。用一種顏色(或強(qiáng)度)標(biāo)記我們確定為前景或?qū)ο蟮膮^(qū)域,用另一種顏色標(biāo)記我們確定為背景或非對(duì)象的區(qū)域,最后用0標(biāo)記我們不確定的區(qū)域。這是我們的標(biāo)記。然后應(yīng)用分水嶺算法。然后我們的標(biāo)記將使用我們給出的標(biāo)簽進(jìn)行更新,對(duì)象的邊界值將為-1。
代碼
下面我們將看到一個(gè)有關(guān)如何使用距離變換和分水嶺來分割相互接觸的對(duì)象的示例。
考慮下面的硬幣圖像,硬幣彼此接觸。即使你設(shè)置閾值,它也會(huì)彼此接觸。
我們先從尋找硬幣的近似估計(jì)開始。因此,我們可以使用Otsu的二值化。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('coins.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV cv.THRESH_OTSU)
現(xiàn)在我們需要去除圖像中的任何白點(diǎn)噪聲。為此,我們可以使用形態(tài)學(xué)擴(kuò)張。要去除對(duì)象中的任何小孔,我們可以使用形態(tài)學(xué)侵蝕。因此,現(xiàn)在我們可以確定,靠近對(duì)象中心的區(qū)域是前景,而離對(duì)象中心很遠(yuǎn)的區(qū)域是背景。我們不確定的唯一區(qū)域是硬幣的邊界區(qū)域。
因此,我們需要提取我們可確定為硬幣的區(qū)域。侵蝕會(huì)去除邊界像素。因此,無論剩余多少,我們都可以肯定它是硬幣。如果物體彼此不接觸,那將起作用。但是,由于它們彼此接觸,因此另一個(gè)好選擇是找到距離變換并應(yīng)用適當(dāng)?shù)拈撝怠=酉聛?#xff0c;我們需要找到我們確定它們不是硬幣的區(qū)域。為此,我們擴(kuò)張了結(jié)果。膨脹將對(duì)象邊界增加到背景。這樣,由于邊界區(qū)域已刪除,因此我們可以確保結(jié)果中背景中的任何區(qū)域?qū)嶋H上都是背景。參見下圖。
剩下的區(qū)域是我們不知道的區(qū)域,無論是硬幣還是背景。分水嶺算法應(yīng)該找到它。這些區(qū)域通常位于前景和背景相遇(甚至兩個(gè)不同的硬幣相遇)的硬幣邊界附近。我們稱之為邊界。可以通過從sure_bg區(qū)域中減去sure_fg區(qū)域來獲得。
# 噪聲去除
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
# 確定背景區(qū)域
sure_bg = cv.dilate(opening,kernel,iterations=3)
# 尋找前景區(qū)域
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# 找到未知區(qū)域
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
查看結(jié)果。在閾值圖像中,我們得到了一些硬幣區(qū)域,我們確定它們是硬幣,并且現(xiàn)在已分離它們。(在某些情況下,你可能只對(duì)前景分割感興趣,而不對(duì)分離相互接觸的對(duì)象感興趣。在那種情況下,你無需使用距離變換,只需侵蝕就足夠了。侵蝕只是提取確定前景區(qū)域的另一種方法。)
現(xiàn)在我們可以確定哪些是硬幣的區(qū)域,哪些是背景。因此,我們創(chuàng)建了標(biāo)記(它的大小與原始圖像的大小相同,但具有int32數(shù)據(jù)類型),并標(biāo)記其中的區(qū)域。我們肯定知道的區(qū)域(無論是前景還是背景)都標(biāo)有任何正整數(shù),但是帶有不同的整數(shù),而我們不確定的區(qū)域則保留為零。為此,我們使用cv.connectedComponents()。它用0標(biāo)記圖像的背景,然后其他對(duì)象用從1開始的整數(shù)標(biāo)記。
但是我們知道,如果背景標(biāo)記為0,則分水嶺會(huì)將其視為未知區(qū)域。所以我們想用不同的整數(shù)來標(biāo)記它。相反,我們將未知定義的未知區(qū)域標(biāo)記為0。
# 類別標(biāo)記
ret, markers = cv.connectedComponents(sure_fg)
# 為所有的標(biāo)記加1,保證背景是0而不是1
markers = markers 1
# 現(xiàn)在讓所有的未知區(qū)域?yàn)?
markers[unknown==255] = 0
參見JET colormap中顯示的結(jié)果。深藍(lán)色區(qū)域顯示未知區(qū)域。當(dāng)然,硬幣的顏色不同。剩下,肯定為背景的區(qū)域顯示在較淺的藍(lán)色,跟未知區(qū)域相比。
現(xiàn)在我們的標(biāo)記已準(zhǔn)備就緒。現(xiàn)在是最后一步的時(shí)候了,使用分水嶺算法。然后標(biāo)記圖像將被修改。邊界區(qū)域?qū)?biāo)記為-1。
markers = cv.watershed(img,markers)
img[markers == -1] = [255,0,0]
請(qǐng)參閱下面的結(jié)果。對(duì)某些硬幣,它們接觸的區(qū)域被正確地分割,而對(duì)于某些硬幣,卻不是。
附加資源
練習(xí)
OpenCV samples has an interactive sample on watershed segmentation, watershed.py. Run it, Enjoy it, then learn it.
總結(jié)
以上是生活随笔為你收集整理的python图像分割算法_OpenCV-Python 图像分割与Watershed算法 | 三十四的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 图形_Python图形数据
- 下一篇: python中队列的应用用场景_消息队列