日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第17章:图像分割提取

發布時間:2023/12/9 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第17章:图像分割提取 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第17章:圖像分割提取

    • 一、用分水嶺算法實現圖像分割提取:
      • 1. 算法原理:
      • 2. 相關函數介紹:
        • (1) 形態學函數回顧:
        • (2) 距離變換函數distanceTransform:
        • (3) 確定未知區域:
        • (4) 函數connectedComponents對象標注:
        • (5) 函數cv2.watershed():
      • 3. 分水嶺算法圖像分割實例
    • 二、交互式前景提取

在圖像處理的過程中,我們經常需要將前景對象從圖像中提取出來。例如,在視頻監控中,觀測到的是固定背景下的視頻內容,而我們對背景本身并無興趣,感興趣的是背景中出現的車輛、行人或者其他對象。我們希望將這些對象從視頻中提取出來,而忽略那些沒有對象進入背景的視頻內容。

一、用分水嶺算法實現圖像分割提取:

? 圖像分割是圖像處理過程中一種非常重要的操作。分水嶺算法將圖像形象地比喻為地理學上的地形表面,實現圖像分割,該算法非常有效。

1. 算法原理:

? 任何一幅灰度圖像,都可以被看作是地理學上的地形表面,灰度值高的區域可以被看成是山峰,灰度值低的區域可以被看成是山谷。如下圖所示,其中左圖是原始圖像,右圖是其對應的“地形表面”。

? 如果我們向每一個山谷中“灌注”不同顏色的水(這里采用了OpenCV官網的表述,岡薩雷斯將灌注表述為在山谷中打洞,然后讓水穿過洞以均勻的速率上升)。那么,隨著水位不斷地升高,不同山谷的水就會匯集到一起。在這個過程中,為了防止不同山谷的水交匯,我們需要在水流可能匯合的地方構建堤壩。該過程將圖像分成兩個不同的集合:集水盆地和分水嶺線。我們構建的堤壩就是分水嶺線,也即對原始圖像的分割。這就是分水嶺算法。

? 下圖中左圖是原始圖像,右圖是使用分水嶺算法得到的圖像分割結果。在CMM的網站上不僅提供了該示例圖像,還提供了動畫演示效果,有興趣的讀者可以去網站上看看。

? 但是由于噪聲等因素的影響,采用上述基礎分水嶺算法經常會得到過度分割的結果。過度分割會將圖像劃分為一個個稠密的獨立小塊,讓分割失去了意義。下圖展示了過度分割的圖像。其中左圖是電泳現象的圖像,右圖是過度分割的結果圖像,可以看到過度分割現象非常嚴重。

? 為了改善圖像分割效果,人們提出了基于掩模的改進的分水嶺算法。改進的分水嶺算法允許用戶將他認為是同一個分割區域的部分標注出來(被標注的部分就稱為掩模)。這樣,分水嶺算法在處理時,就會將標注的部分處理為同一個分割區域。

? 在下圖中,左圖是原始圖像,我們對其做了標注處理,其中被標注為深色的三個小色塊表示:在使用掩模分水嶺算法時,這些部分所包含的顏色都會被分割在同一個區域內。使用掩模分水嶺算法得到的分割結果如圖中的右圖所示。

? 采用改進的分水嶺算法對圖中左側的電泳圖像進行掩模處理,得到右側的分割結果。可以看出,分割結果得到明顯的改進。

2. 相關函數介紹:

? 在OpenCV中,可以使用函數cv2.watershed()實現分水嶺算法。在具體的實現過程中,還需要借助于形態學函數、距離變換函數 cv2.distanceTransform()、cv2.connectedComponents()來完成圖像分割。下面對分水嶺算法中用到的函數進行簡單的說明。

  • 使用分水嶺算法之前需要對圖像進行一些預處理,下面就是圖像預處理的步驟。
  • 也就是,圖像分割可以使用分水嶺算法來進行,使用分水嶺算法需要對圖像進行預處理。
  • OpenCV中的watershed函數實現的分水嶺算法是基于“標記”的分割算法,用于解決傳統的分水嶺算法過度分割的問題。

(1) 形態學函數回顧:

? 在使用分水嶺算法對圖像進行分割前,需要對圖像進行簡單的形態學處理。先回顧一下形態學里的基本操作。

  • 開運算: 開運算是先腐蝕、后膨脹的操作,開運算能夠去除圖像內的噪聲。例如,在下圖中,先對左圖進行腐蝕操作,會得到中間的圖像,再對中間的圖像進行膨脹操作,會得到右側的圖像。對圖像進行開運算,能夠去除圖像內的噪聲。在用分水嶺算法處理圖像前,要先使用開運算去除圖像內的噪聲,以避免噪聲對圖像分割可能造成的干擾。

  • 獲取圖像邊界: 通過形態學操作和減法運算能夠獲取圖像的邊界。例如,在下圖中,左圖是原始圖像,中間的圖是對其進行腐蝕而得到的圖像,對二者進行減法運算,就會得到右側的圖像。通過觀察可知,右圖是左圖的邊界。

    通過以上分析可知,使用形態學操作和減法運算能夠獲取圖像的邊界信息。但是,形態學操作僅適用于比較簡單的圖像。如果圖像內的前景對象存在連接的情況,使用形態學操作就無法準確獲取各個子圖像的邊界了。

(2) 距離變換函數distanceTransform:

? 當圖像內的各個子圖沒有連接時,可以直接使用形態學的腐蝕操作確定前景對象,但是如果圖像內的子圖連接在一起時,就很難確定前景對象了。此時,借助于距離變換函數cv2.distanceTransform()可以方便地將前景對象提取出來。

? 距離變換函數cv2.distanceTransform()計算二值圖像內所有點到最近背景點的距離(即圖像內非零值像素點到最近的零值像素點的距離)。當然,如果像素點本身的值為0,則這個距離也為0。

? 距離變換函數cv2.distanceTransform()的計算結果反映了圖像內各個像素與背景(值為0的像素點)的距離關系。通常情況下:

  • 如果前景對象的中心(質心)距離值為0的像素點距離較遠,會得到一個較大的值。
  • 如果前景對象的邊緣距離值為0的像素點較近,會得到一個較小的值。

? 如果對上述計算結果進行閾值化,就可以得到圖像內前景對象的中心、骨架等信息。距離變換函數cv2.distanceTransform()可以用于計算對象的中心,還能細化輪廓、獲取圖像前景等,有多種功能。

距離變換函數cv2.distanceTransform()的語法格式為:

  • dst=cv2.distanceTransform(src,distanceType,maskSize[,dstType]])

    • src:是8位單通道的二值圖像。

    • distanceType:為距離類型參數,其具體值和含義如表所示。

    • maskSize:為掩模的尺寸,其可能的值如表所示。需要注意,當 distanceType=cv2.DIST_L1或 cv2.DIST_C時,maskSize強制為3(因為設置為3和設置為5及更大值沒有什么區別)。

    • dstType:為目標圖像的類型,默認值為CV_32F。

    • dst:返回值,表示計算得到的目標圖像,可以是8位或32位浮點數,尺寸和src相同。

示例:用距離變換函數 cv2.distanceTransform(),計算一幅圖像的確定前景,并觀察效果

import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('img.jpg') # img = cv2.imread('../sugar.tiff') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 閾值處理 rst, thresh = cv2.threshold(gray_img, 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)# 圖像距離計算 dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)# 獲取前景對象的中心 rst, front = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)plt.subplot(161) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(162) plt.imshow(gray_img, cmap='gray') plt.title('gray_img') plt.axis('off')plt.subplot(163) plt.imshow(thresh, cmap='gray') plt.title('thresh') plt.axis('off')plt.subplot(164) plt.imshow(opening, cmap='gray') plt.title('opening') plt.axis('off')plt.subplot(165) plt.imshow(dist_transform, cmap='gray') plt.title('dist_transform') plt.axis('off')plt.subplot(166) plt.imshow(front, cmap='gray') plt.title('front') plt.axis('off')plt.show()

(3) 確定未知區域:

? 使用形態學的膨脹操作能夠將圖像內的前景“膨脹放大”。當圖像內的前景被放大后,背景就會被“壓縮”,所以此時得到的背景信息一定小于實際背景的,不包含前景的“確定背景”。以下為了方便說明將確定背景稱為B。

? 距離變換函數cv2.distanceTransform()能夠獲取圖像的“中心”,得到“確定前景”。為了方便說明,將確定前景稱為F。

? 圖像中有了確定前景F和確定背景B,剩下區域的就是未知區域UN了。這部分區域正是分水嶺算法要進一步明確的區域

針對一幅圖像O,通過以下關系能夠得到未知區域UN:

  • 未知區域UN=圖像O-確定背景B-確定前景F

對上述表達式進行整理,可以得到:

  • 未知區域UN=(圖像O-確定背景B)-確定前景F

上式中的“圖像O-確定背景B”,可以通過對圖像進行形態學的膨脹操作得到。前景對象膨脹 = 圖像o - 確定背景B

示例:標注一幅圖像的確定前景確定背景及未知區域

import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('img.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 閾值分割 rst, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) kernel = np.ones((3, 3), dtype=np.uint8) # 開運算 opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 膨脹 bg = cv2.dilate(opening, kernel, iterations=3)# 距離計算 dist_tansform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) rst, fore = cv2.threshold(dist_tansform, 0.7 * dist_tansform.max(), 255, 0) fore = np.uint8(fore) un = cv2.subtract(bg, fore)plt.subplot(221) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(222) plt.imshow(bg) plt.title('bg') plt.axis('off')plt.subplot(223) plt.imshow(fore) plt.title('fore') plt.axis('off')plt.subplot(224) plt.imshow(un) plt.title('un') plt.axis('off')plt.show()

注意的是,在圖右上角的圖像bg中:

  • 前景的一個個小圓是“原始圖像-確定背景”部分,而不是“確定背景”。
  • 其背景圖像才是“確定背景”。

(4) 函數connectedComponents對象標注:

明確了確定前景后,就可以對確定前景圖像進行標注了。在 OpenCV 中,可以使用函數cv2.connectedComponents()進行標注。該函數會將背景標注為0,將其他的對象使用從1開始的正整數標注。

函數cv2.connectedComponents()的語法格式為:

  • retval,labels=cv2.connectedComponents(image)
    • image:為8位單通道的待標注圖像。
    • retval:為返回的標注的數量。
    • labels:為標注的結果圖像。
import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('img.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)rst, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) kernel = np.ones((3, 3), dtype=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) ret, fore = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0) fore = np.uint8(fore)# 標注前景對象 rst, markers = cv2.connectedComponents(fore) print(markers)plt.subplot(131) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(132) plt.imshow(fore) plt.title('fore') plt.axis('off')plt.subplot(133) plt.imshow(markers) plt.title('markers') plt.axis('off')plt.show()

  • 左圖是原始圖像
  • 中間的是經過距離變換后得到的前景圖像的中心點圖像fore。
  • 右圖是對前景圖像的中心點圖像進行標注后的結果圖像markers。

可以看到,前景圖像的中心點被做了不同的標注

函數 cv2.connectedComponents()在標注圖像時,會將背景標注為0,將其他的對象用從1開始的正整數標注。具體的對應關系為:

  • 數值0代表背景區域。
  • 從數值1開始的值,代表不同的前景區域。

在分水嶺算法中,標注值0代表未知區域。所以,我們要對函數cv2.connectedComponents()標注的結果進行調整:將標注的結果都加上數值1。經過上述處理后,在標注結果中:

  • 數值1代表背景區域。
  • 從數值2開始的值,代表不同的前景區域。

為了能夠使用分水嶺算法,還需要對原始圖像內的未知區域進行標注,將已經計算出來的未知區域標注為0即可。

ret,markers=cv2.connectedComponents(fore) markers=markers+1 markers[未知區域]=0

示例:對cv2.connectedComponents()標注結果進行修正

import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('img.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)rst, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) kernel = np.ones((3, 3), dtype=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) ret, fore = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) fore = np.uint8(fore) rst, markers = cv2.connectedComponents(fore)# 修正標注的前景對象 fore_adv = fore.copy() unknown = cv2.subtract(sure_bg, fore_adv) ret2, markers2 = cv2.connectedComponents(fore_adv) markers2 += 1 markers2[unknown == 255] = 0plt.subplot(141) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(142) plt.imshow(fore) plt.title('fore') plt.axis('off')plt.subplot(143) plt.imshow(markers) plt.title('markers') plt.axis('off')plt.subplot(144) plt.imshow(markers2) plt.title('markers2') plt.axis('off')plt.show()

  • markers圖是對一幅圖像使用函數cv2.connectedComponents()直接標注后的結果。
  • markers2圖是修正后的標注結果。

對比左右圖可以看出,右圖在前景圖像的邊緣(未知區域)進行了標注,使得每一個確定前景都有一個黑色的邊緣,這個邊緣是被標注的未知區域。

(5) 函數cv2.watershed():

OpenCV中的watershed函數實現的分水嶺算法是基于“標記”的分割算法,用于解決傳統的分水嶺算法過度分割的問題。

完成上述圖像預處理后,就可以使用分水嶺算法對預處理結果圖像進行分割了。在 OpenCV 中,實現分水嶺算法的函數是cv2.watershed(),其語法格式為:

  • markers=cv2.watershed(image,markers)

    • image:是輸入圖像,必須是8位三通道的圖像。

    • markers:是32位單通道的標注結果,它應該和image具有相等大小。

      • 在對圖像使用cv2.watershed()函數處理之前,必須先對圖像進行預處理,用正數大致勾畫出圖像中的期望分割區域。每一個分割的區域會被標注為1、2、3等。對于尚未確定的區域,需要將它們標注為0。我們可以將標注區域理解為進行分水嶺算法分割的“種子”區域。

      • 在markers中,每一個像素要么被設置為初期的“種子值”,要么被設置為“-1”表示邊界。

      • 算法會根據markers傳入的輪廓作為種子(也就是所謂的注水點),對圖像上其他的像素點根據分水嶺算法規則進行判斷,并對每個像素點的區域歸屬進行劃定,直到處理完圖像上所有像素點。而區域與區域之間的分界處的值被置為“-1”,以做區分。

3. 分水嶺算法圖像分割實例

使用分水嶺算法進行圖像分割時,基本的步驟為:

  • 通過形態學開運算對原始圖像O去噪。
  • 通過腐蝕操作獲取“確定背景B”。需要注意,這里得到“原始圖像-確定背景”即可。
  • 利用距離變換函數cv2.distanceTransform()對原始圖像進行運算,并對其進行閾值處理,得到“確定前景F”。
  • 計算未知區域UN(UN=O –B-F)。
  • 利用函數cv2.connectedComponents()對原始圖像O進行標注。
  • 對函數cv2.connectedComponents()的標注結果進行修正。
  • 使用分水嶺函數完成對圖像的分割。
  • 1-6是圖像預處理,只要是將圖像中的未知區域標記為0,已知區域標注為1、2、3…,也就是標記種子區域。第7步是根據標注使用分水嶺算法對圖像進行分割。

    示例:

    import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('img.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) new_img = rgb_img.copy()rst, thresh = cv2.threshold(gray_img, 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) ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg, sure_fg) ret, markers = cv2.connectedComponents(sure_fg) markers += 1 markers[unknown == 255] = 0 markers = cv2.watershed(new_img, markers) new_img[markers == -1] = [0, 255, 0]plt.subplot(121) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(122) plt.imshow(new_img) plt.title('rst') plt.axis('off')plt.show()

    二、交互式前景提取

    ? 經典的前景提取技術主要使用紋理(顏色)信息,如魔術棒工具,或根據邊緣(對比度)信息,如智能剪刀等完成。2004年,微軟研究院(劍橋)的Rother等人在論文GrabCut:Interactive Foreground Extraction Using Iterated Graph Cuts中提出了交互式前景提取技術。他們提出的算法,僅需要做很少的交互操作,就能夠準確地提取出前景圖像。

    ? 在開始提取前景時,先用一個矩形框指定前景區域所在的大致位置范圍,然后不斷迭代地分割,直到達到最好的效果。 經過上述處理后,提取前景的效果可能并不理想,存在前景沒有提取出來,或者將背景提取為前景的情況,此時需要用戶干預提取過程。用戶在原始圖像的副本中(也可以是與原始圖像大小相等的任意一幅圖像),用白色標注要提取為前景的區域,用黑色標注要作為背景的區域。然后,將標注后的圖像作為掩模,讓算法繼續迭代提取前景從而得到最終結果。

    例如,對于下圖的左圖,先用矩形框將要提取的前景 Lena 框出來,再分別用白色和黑色對前景圖像、背景圖像進行標注。完成標注后,使用交互式前景提取算法,就會得到右圖所示的結果圖像。

    下面我們來看GrabCut算法的具體實施過程。

  • 將前景所在的大致位置使用矩形框標注出來。值得注意的是,此時矩形框框出的僅僅是前景的大致位置,其中既包含前景又包含背景,所以該區域實際上是未確定區域。但是,該區域以外的區域被認為是“確定背景”。

  • 根據矩形框外部的“確定背景”數據來區分矩形框區域內的前景和背景。

  • 用高斯混合模型(Gaussians Mixture Model,GMM)對前景和背景建模。GMM會根據用戶的輸入學習并創建新的像素分布。對未分類的像素(可能是背景也可能是前景),根據其與已知分類像素(前景和背景)的關系進行分類。

  • 根據像素分布情況生成一幅圖,圖中的節點就是各個像素點。除了像素點之外,還有兩個節點:前景節點和背景節點。所有的前景像素都和前景節點相連,所有的背景像素都和背景節點相連。每個像素連接到前景節點或背景節點的邊的權重由像素是前景或背景的概率來決定。

  • 圖中的每個像素除了與前景節點或背景節點相連外,彼此之間還存在著連接。兩個像素連接的邊的權重值由它們的相似性決定,兩個像素的顏色越接近,邊的權重值越大。

  • 完成節點連接后,需要解決的問題變成了一幅連通的圖。在該圖上根據各自邊的權重關系進行切割,將不同的點劃分為前景節點和背景節點。

  • 不斷重復上述過程,直至分類收斂為止。
    OpenCV 的官網上有更詳細的資料(http://www.cs.ru.ac.za/research/g02m1682/),讀者有興趣的話可以進一步學習。

  • 在OpenCV中,實現交互式前景提取的函數是cv2.grabCut(),其語法格式為:

    • mask,bgdModel,fgdModel=cv2.grabCut(img,mask,rect,bgdModel,fgdModel,iterCount[,mode])

      • img:為輸入圖像,要求是8位3通道的。

      • mask:為掩模圖像,要求是8位單通道的。該參數用于確定前景區域、背景區域和不確定區域,可以設置為4種形式。

        • cv2.GC_BGD:表示確定背景,也可以用數值0表示。
        • cv2.GC_FGD:表示確定前景,也可以用數值1表示。
        • cv2.GC_PR_BGD:表示可能的背景,也可以用數值2表示。
        • cv2.GC_PR_FGD:表示可能的前景,也可以用數值3表示。

        注意,mask不僅是做為參數的使用的掩膜圖像,同時也是同時也是grabCut函數處理完后的結果掩膜圖像,我們會根據這個結果掩膜圖像提取前景對象。

        在最后使用模板提取前景時,會將參數值0和2合并為背景(均當作0處理),將參數值1和3合并為前景(均當作1處理)。在通常情況下,我們可以使用白色筆刷和黑色筆刷在掩模圖像上做標記,再通過轉換將其中的白色像素設置為0,黑色像素設置為1。

      • rect:指包含前景對象的區域,該區域外的部分被認為是“確定背景”。因此,在選取時務必確保讓前景包含在rect指定的范圍內;否則,rect外的前景部分是不會被提取出來的。只有當參數mode的值被設置為矩形模式cv2.GC_INIT_WITH_RECT時,參數rect才有意義。其格式為(x,y,w,h),分別表示區域左上角像素的x軸和y軸坐標以及區域的寬度和高度。如果前景位于右下方,又不想判斷原始圖像的大小,對于w 和h可以直接用一個很大的值。使用掩模模式時,將該值設置為none即可。

      • bgdModel:為算法內部使用的數組,只需要創建大小為(1,65)的numpy.float64數組。

      • fgdModel:為算法內部使用的數組,只需要創建大小為(1,65)的numpy.float64數組。

      • iterCount:表示迭代的次數。

      • mode:表示迭代模式。其可能的值與含義如表所示。

      • 函數的返回值為mask,bgdModel,fgdModel

    示例1:使用GrabCut 算法提取圖像的前景,并觀察提取效果。

    import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('../lena512color.tiff') rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) mask = np.zeros(img.shape[:2], dtype=np.uint8) bgd_model = np.zeros((1, 65), dtype=np.float64) fgd_model = np.zeros((1, 65), dtype=np.float64) rect = (50, 50, 500, 500)# 函數的返回值為mask,bgdModel,fgdModel cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT) print(mask) mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')rst = img * mask2[:, :, np.newaxis] rst = cv2.cvtColor(rst, cv2.COLOR_BGR2RGB)plt.subplot(121) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(122) plt.imshow(rst) plt.title('rst') plt.axis('off')plt.show()

    ? 可以看到,在不使用掩模(掩模值都設置為默認值0時),函數 cv2.grabCut()的處理效果并不太好:提取左圖的前景時,人物的帽子沒有提取完整。對于有些圖像,也有可能將背景錯誤地提取出來。

    ? 為了得到完整的前景對象,需要做一些改進。這里對原始圖像進行標注,**將需要保留的部分設置為白色,將需要刪除的背景設置為黑色。**以標記好的圖像作為模板,使用函數cv2.grabCut()完成前景的提取。
    這個過程主要包含以下步驟:

  • 利用函數 cv2.grabCut()在 cv2.GC_INIT_WITH_RECT 模式下對圖像進行初步的前景提取,得到初步提取的結果圖像og。主要是為了得到初步的mask。
  • 使用Windows系統自帶的筆刷工具,打開要提取前景的圖像,比如lena。
  • 使用白色筆刷在希望提取的前景區域做標記。
  • 使用黑色筆刷在希望刪除的背景區域做標記。
  • 將當前設置好的lena圖像另存為模板圖像m0。
  • 將模板圖像m0中的白色值和黑色值映射到模板m中。將模板圖像m0中的白色值(像素值為255)映射為模板圖像m中的確定前景(像素值為1),將模板圖像m0中的黑色值(像素值為0)映射為模板圖像m中的確定背景(像素值為0)。
  • 以模板圖像m作為函數cv2.grabCut()的模板參數(mask),對圖像og完成前景提取。
  • 需要注意,在上述步驟中,使用畫筆標記的模板圖像m0不能直接作為模板(即參數mask)使用。函數cv2.grabCut()要求,參數mask的值必須是cv2.GC_BGD(確定背景)、cv2.GC_FGD(確定前景)、cv2.GC_PR_BGD(可能的背景)、cv2.GC_PR_FGD(可能的前景),或者是0、1、2、3之中的值。 此時的模板圖像 m0中,存在著[0,255]內的值,所以它的值不滿足函數cv2.grabCut()的要求,無法作為參數mask直接使用。必須先將模板圖像m0中的白色值和黑色值映射到模板m上,再將模板圖像m作為函數cv2.grabCut()的模板參數。

    import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('lena512color.tiff') rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 對前景對象進行初步提取,獲取初步mask mask = np.zeros(img.shape[:2], dtype=np.uint8) bgd = np.zeros((1, 65), dtype=np.float64) fgd = np.zeros((1, 65), dtype=np.float64) rect = (50, 50, 500, 500) cv2.grabCut(img, mask, rect, bgd, fgd, 5, cv2.GC_INIT_WITH_RECT)# 讀取模板,根據模板設置得到的初始mask mask2 = cv2.imread('m.tiff') rgb_mask2 = cv2.cvtColor(mask2, cv2.COLOR_BGR2RGB) gray_mask2 = cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY) mask[gray_mask2 == 0] = 0 mask[gray_mask2 == 255] = 1# 根據修改后的mask再次進行前景對象提取 cv2.grabCut(img, mask, None, bgd, fgd, 5, cv2.GC_INIT_WITH_MASK) mask = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')# 根據得到的mask提取前景對象 new_img = rgb_img.copy() rst = new_img * mask[:, :, np.newaxis]plt.subplot(131) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(132) plt.imshow(rgb_mask2) plt.title('m') plt.axis('off')plt.subplot(133) plt.imshow(rst) plt.title('rst') plt.axis('off')plt.show()

    總結

    以上是生活随笔為你收集整理的第17章:图像分割提取的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。