形態學處理,除了最基本的膨脹、腐蝕、開/閉運算、黑/白帽處理外,還有一些更高級的運用,如凸包,連通區域標記,刪除小塊區域等。數組
一、凸包網絡
凸包是指一個凸多邊形,這個凸多邊形將圖片中全部的白色像素點都包含在內。dom
函數爲:函數
skimage.morphology.convex_hull_image(image)測試
輸入爲二值圖像,輸出一個邏輯二值圖像。在凸包內的點爲True, 不然爲Falseui
例:spa
import matplotlib.pyplot as plt
from skimage import data,color,morphology #生成二值測試圖像 img=color.rgb2gray(data.horse()) img=(img<0.5)*1 chull = morphology.convex_hull_image(img) #繪製輪廓 fig, axes = plt.subplots(1,2,figsize=(8,8)) ax0, ax1= axes.ravel() ax0.imshow(img,plt.cm.gray) ax0.set_title('original image') ax1.imshow(chull,plt.cm.gray) ax1.set_title('convex_hull image')
convex_hull_image()是將圖片中的全部目標看做一個總體,所以計算出來只有一個最小凸多邊形。若是圖中有多個目標物體,每個物體須要計算一個最小凸多邊形,則須要使用convex_hull_object()函數。rest
函數格式:skimage.morphology.convex_hull_object(image, neighbors=8)code
輸入參數image是一個二值圖像,neighbors表示是採用4連通仍是8連通,默認爲8連通。blog
例:
import matplotlib.pyplot as plt
from skimage import data,color,morphology,feature #生成二值測試圖像 img=color.rgb2gray(data.coins()) #檢測canny邊緣,獲得二值圖片 edgs=feature.canny(img, sigma=3, low_threshold=10, high_threshold=50) chull = morphology.convex_hull_object(edgs) #繪製輪廓 fig, axes = plt.subplots(1,2,figsize=(8,8)) ax0, ax1= axes.ravel() ax0.imshow(edgs,plt.cm.gray) ax0.set_title('many objects') ax1.imshow(chull,plt.cm.gray) ax1.set_title('convex_hull image') plt.show()
二、連通區域標記
在二值圖像中,若是兩個像素點相鄰且值相同(同爲0或同爲1),那麼就認爲這兩個像素點在一個相互連通的區域內。而同一個連通區域的全部像素點,都用同一個數值來進行標記,這個過程就叫連通區域標記。在判斷兩個像素是否相鄰時,咱們一般採用4連通或8連通判斷。在圖像中,最小的單位是像素,每一個像素周圍有8個鄰接像素,常見的鄰接關係有2種:4鄰接與8鄰接。4鄰接一共4個點,即上下左右,以下左圖所示。8鄰接的點一共有8個,包括了對角線位置的點,以下右圖所示。
在skimage包中,咱們採用measure子模塊下的label()函數來實現連通區域標記。
函數格式:
skimage.measure.label(image,connectivity=None)
參數中的image表示須要處理的二值圖像,connectivity表示鏈接的模式,1表明4鄰接,2表明8鄰接。
輸出一個標記數組(labels), 從0開始標記。
import numpy as np import scipy.ndimage as ndi from skimage import measure,color import matplotlib.pyplot as plt #編寫一個函數來生成原始二值圖像 def microstructure(l=256): n = 5 x, y = np.ogrid[0:l, 0:l] #生成網絡 mask = np.zeros((l, l)) generator = np.random.RandomState(1) #隨機數種子 points = l * generator.rand(2, n**2) mask[(points[0]).astype(np.int), (points[1]).astype(np.int)] = 1 mask = ndi.gaussian_filter(mask, sigma=l/(4.*n)) #高斯濾波 return mask > mask.mean() data = microstructure(l=128)*1 #生成測試圖片 labels=measure.label(data,connectivity=2) #8連通區域標記 dst=color.label2rgb(labels) #根據不一樣的標記顯示不一樣的顏色 print('regions number:',labels.max()+1) #顯示連通區域塊數(從0開始標記) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4)) ax1.imshow(data, plt.cm.gray, interpolation='nearest') ax1.axis('off') ax2.imshow(dst,interpolation='nearest') ax2.axis('off') fig.tight_layout() plt.show()
在代碼中,有些地方乘以1,則能夠將bool數組快速地轉換爲int數組。
結果如圖:有10個連通的區域,標記爲0-9
若是想分別對每個連通區域進行操做,好比計算面積、外接矩形、凸包面積等,則須要調用measure子模塊的regionprops()函數。該函數格式爲:
skimage.measure.regionprops(label_image)
返回全部連通區塊的屬性列表,經常使用的屬性列表以下表:
屬性名稱 | 類型 | 描述 |
area | int | 區域內像素點總數 |
bbox | tuple | 邊界外接框(min_row, min_col, max_row, max_col) |
centroid | array | 質心座標 |
convex_area | int | 凸包內像素點總數 |
convex_image | ndarray | 和邊界外接框同大小的凸包 |
coords | ndarray | 區域內像素點座標 |
Eccentricity | float | 離心率 |
equivalent_diameter | float | 和區域面積相同的圓的直徑 |
euler_number | int | 區域歐拉數 |
extent | float | 區域面積和邊界外接框面積的比率 |
filled_area | int | 區域和外接框之間填充的像素點總數 |
perimeter | float | 區域周長 |
label | int | 區域標記 |
三、刪除小塊區域
有些時候,咱們只須要一些大塊區域,那些零散的、小塊的區域,咱們就須要刪除掉,則可使用morphology子模塊的remove_small_objects()函數。
函數格式:skimage.morphology.remove_small_objects(ar, min_size=64, connectivity=1, in_place=False)
參數:
ar: 待操做的bool型數組。
min_size: 最小連通區域尺寸,小於該尺寸的都將被刪除。默認爲64.
connectivity: 鄰接模式,1表示4鄰接,2表示8鄰接
in_place: bool型值,若是爲True,表示直接在輸入圖像中刪除小塊區域,不然進行復制後再刪除。默認爲False.
返回刪除了小塊區域的二值圖像。
import numpy as np import scipy.ndimage as ndi from skimage import morphology import matplotlib.pyplot as plt #編寫一個函數來生成原始二值圖像 def microstructure(l=256): n = 5 x, y = np.ogrid[0:l, 0:l] #生成網絡 mask = np.zeros((l, l)) generator = np.random.RandomState(1) #隨機數種子 points = l * generator.rand(2, n**2) mask[(points[0]).astype(np.int), (points[1]).astype(np.int)] = 1 mask = ndi.gaussian_filter(mask, sigma=l/(4.*n)) #高斯濾波 return mask > mask.mean() data = microstructure(l=128) #生成測試圖片 dst=morphology.remove_small_objects(data,min_size=300,connectivity=1) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4)) ax1.imshow(data, plt.cm.gray, interpolation='nearest') ax2.imshow(dst,plt.cm.gray,interpolation='nearest') fig.tight_layout() plt.show()
在此例中,咱們將面積小於300的小塊區域刪除(由1變爲0),結果以下圖:
四、綜合示例:閾值分割+閉運算+連通區域標記+刪除小區塊+分色顯示
import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches from skimage import data,filter,segmentation,measure,morphology,color #加載並裁剪硬幣圖片 image = data.coins()[50:-50, 50:-50] thresh =filter.threshold_otsu(image) #閾值分割 bw =morphology.closing(image > thresh, morphology.square(3)) #閉運算 cleared = bw.copy() #複製 segmentation.clear_border(cleared) #清除與邊界相連的目標物 label_image =measure.label(cleared) #連通區域標記 borders = np.logical_xor(bw, cleared) #異或 label_image[borders] = -1 image_label_overlay =color.label2rgb(label_image, image=image) #不一樣標記用不一樣顏色顯示 fig,(ax0,ax1)= plt.subplots(1,2, figsize=(8, 6)) ax0.imshow(cleared,plt.cm.gray) ax1.imshow(image_label_overlay) for region in measure.regionprops(label_image): #循環獲得每個連通區域屬性集 #忽略小區域 if region.area < 100: continue #繪製外包矩形 minr, minc, maxr, maxc = region.bbox rect = mpatches.Rectangle((minc, minr), maxc - minc, maxr - minr, fill=False, edgecolor='red', linewidth=2) ax1.add_patch(rect) fig.tight_layout() plt.show()