1. 程式人生 > >python數字影象處理(18):高階形態學處理

python數字影象處理(18):高階形態學處理

形態學處理,除了最基本的膨脹、腐蝕、開/閉運算、黑/白帽處理外,還有一些更高階的運用,如凸包,連通區域標記,刪除小塊區域等。

1、凸包

凸包是指一個凸多邊形,這個凸多邊形將圖片中所有的白色畫素點都包含在內。

函式為:

skimage.morphology.convex_hull_image(image)

輸入為二值影象,輸出一個邏輯二值影象。在凸包內的點為True, 否則為False

例:

複製程式碼

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()函式。

函式格式:skimage.morphology.convex_hull_object(imageneighbors=8)

輸入引數image是一個二值影象,neighbors表示是採用4連通還是8連通,預設為8連通。

例:

複製程式碼

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()

複製程式碼

2、連通區域標記

在二值影象中,如果兩個畫素點相鄰且值相同(同為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 區域標記

3、刪除小塊區域

有些時候,我們只需要一些大塊區域,那些零散的、小塊的區域,我們就需要刪除掉,則可以使用morphology子模組的remove_small_objects()函式。

函式格式:skimage.morphology.remove_small_objects(armin_size=64connectivity=1in_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),結果如下圖:

 4、綜合示例:閾值分割+閉運算+連通區域標記+刪除小區塊+分色顯示

複製程式碼

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()

複製程式碼