1. 程式人生 > >opencv3計算機視覺+Python(四)

opencv3計算機視覺+Python(四)

rate 步驟 運行 sha 簡單 前景 open spa 階段

使用分水嶺和GrabCut算法進行物體分割

用GrabCut算法進行圖像分割

在OpenCV中,實現了grabcut分割算法,該算法可以方便的分割出前景圖像,操作簡單,而且分割的效果很好。算法的原理參見papaer:“GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts

比如下面的一副圖,我們只要選定一個四邊形框,把框中的圖像作為grabcut的一個輸入參數,表示該框中的像素可能屬於前景,但框外的部分一定屬於背景。

GrabCut算法實現步驟為:

1.在圖片中定義含有(一個或多個)物體的矩形

2.矩形外的區域被自動認為是背景

3.對於用戶定義的矩形區域,可用背景中的數據來區別它裏面的前景和背景區域

4.用高斯混合模型(GMM)來對背景和前景建模,並將未定義的像素標記為可能的前景或背景。

5.圖像中的每一個像素都被看作通過虛擬邊與周圍像素相連接,而每條邊都有一個屬於前景或背景的概率,這基於它與周圍像素顏色上的相似性。

6.每一個像素(即算法中的節點)會與一個前景或背景節點連接

7.在節點完成連接後(可能與背景或前景連接),若節點之間的邊屬於不同終端(即一個節點屬於前景,另一個節點屬於背景),則會切斷它們之間的邊(這就是算法名中的切割部分),這就能將圖像各部分分割出來。

import numpy as np
import cv2
from matplotlib import pyplot as plt img=cv2.imread(1.jpg) mask=np.zeros(img.shape[:2],np.uint8)#創建一個掩模

#創建以0填充的前景和背景模型
bgdModel=np.zeros((1,65),np.float64)
fgdModel=np.zeros((1,65),np.float64)

rect=(100,160,400,670)#創建矩形
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)#使用了指定的空模型和掩模來運行GrabCut,並且實際上是用一個矩形來初始化這個操作
#做完這些後,我們的掩模已經變成包含0~3之間的值。值為0和2的將轉為0,值為1,3的將轉為1.然後保存在mask2中。這樣就可以用mask2過濾出所有的0值像素(理論上會完整保留所有前景像素)
mask2
=np.where((mask==0)|(mask==2),0,1).astype(uint8) img=img*mask2[:,:,np.newaxis] plt.subplot(121),plt.imshow(img) plt.title("grabcut"),plt.xticks([]),plt.yticks([]) plt.subplot(122),plt.imshow(cv2.cvtColor(cv2.imread("1.jpg"),cv2.COLOR_BGR2RGB)) plt.title("original"),plt.xticks([]),plt.yticks([]) plt.show()


使用分水嶺算法進行圖像分割

把圖像中的低密度的區域(變化很少)想象成山谷,圖像中高密度的區域(變化很多)想象成山峰。開始向山谷中註入水直到不同的山谷中的水開始匯聚。為了阻止不同山谷的水匯聚,可以設置一些柵欄,最後得到的柵欄就是圖像分割。

import numpy as np
import cv2
from matplotlib import pyplot as plt
img=cv2.imread("1.jpg")
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#顏色轉為灰度
ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)#可為圖像設一個閾值
kernel=np.ones((3,3),np.uint8)
opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)#去除噪聲
sure_bg=cv2.dilate(opening,kernel,iterations=3)

dist_transform=cv2.distanceTransform(opening,cv2.DIST_L2,5)#可以通過distanceTransform來獲取確定的前景區域。也就是說,這是圖像中最可能是前景的區域,越是遠離背景區域的邊界點越可能屬於前景,這裏用了閾值來決定那些區域是前景
ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
#這個階段之後,所得到的前景和背景中有重合的部分該怎麽辦?首先需要確定這些區域,這可從sure_bg與sure_fg的集合相減得到
sure_fg=np.uint8(sure_fg)
unknown=cv2.subtract(sure_bg,sure_fg)
#現在有了這些區域,就可以設定柵欄來阻止水匯聚,這是通過connectedComponents函數完成。
ret,markers=cv2.connectedComponents(sure_fg)

markers=markers+1
markers[unknown==255]=0
#把柵欄繪制成紅色
markers=cv2.watershed(img,markers)
img[markers==-1]=[255,0,0]
plt.imshow(img)
plt.show()

opencv3計算機視覺+Python(四)