文章目錄
- 二值化
- 圖像骨骼
- 連通域分割
二值化
所謂圖像分割,就是將圖像的目標和背景分離開來,更直觀一點,就是把目標涂成白色,背景涂成黑色,言盡于此,是不是恍然大悟:這不就是二值化么?
【threshold]是此前提到的二值化函數,但只講解了固定閾值分割模式,而并未講解其自動分割的OTSU模式。
【adaptiveThreshold】是opencv提供的自適應閾值函數,可根據不同的卷積核來對局部進行二值化,可以更加細致地得到物體邊緣。
OTSU算法,mean核,高斯核的分割結果如下圖所示,其中150是手動設置的分割閾值;100是OTSU自動計算出的分割閾值。
提示說圖像違規,也不知道哪違規了。
代碼如下
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import ascent
import cv2path = 'coins.jpg'coins = {}
coins["original"] = plt.imread(path)
coins["gray"] = cv2.cvtColor(coins["original"],cv2.COLOR_RGB2GRAY)_, coins['th150'] = cv2.threshold(coins["gray"], 150, 255, cv2.THRESH_BINARY)
th, bImg = cv2.threshold(coins["gray"], 0, 255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
coins[f'otsu({th})'] = bImgmethod = {"mean":cv2.ADAPTIVE_THRESH_MEAN_C,"gaussian":cv2.ADAPTIVE_THRESH_GAUSSIAN_C}
for key in method:coins[key] = cv2.adaptiveThreshold(coins["gray"], 255,method[key], cv2.THRESH_BINARY, 11, 2)for i,key in enumerate(coins,1):plt.subplot(2,3,i)plt.imshow(coins[key], cmap='gray')plt.title(key)plt.axis('off')plt.show()
圖像骨骼
如果把二值圖像理解成地形,黑色表示海洋,白色表示陸地,那么陸地上任意一點,到海洋都有一個最近的距離,如下圖所示。由于硬幣圖案的顏色并不完全一致,所以在二值化時可能會出現不一致的情況,為此,需要通過腐蝕或者膨脹等形態學處理,將其內部涂抹均勻,從而得到一張目標與背景完全分割的圖像,此即【dilate】圖。對dilate圖而言,【dist-bg】為其黑色區域的骨骼;【dist-fg】為白色區域的骨骼。
實現代碼如下
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import ascent
import cv2bImg = coins[f'otsu({th})']kernel = np.ones((5,5),np.uint8)
coins["dilate"] = cv2.dilate(coins[f'otsu({th})'], kernel)
coins["dist-fg"] = cv2.distanceTransform(coins["dilate"], cv2.DIST_L2,5)
coins["dist-bg"] = cv2.distanceTransform(255-coins["dilate"], cv2.DIST_L2,5)keys = ['dilate', 'dist-bg', 'dist-fg']
for i,key in enumerate(keys,1):plt.subplot(1,3,i)plt.imshow(coins[key], cmap='gray')plt.title(key)plt.axis('off')plt.show()
【distanceTransform】函數的功能是,計算當前像素點到零像素點的最短距離,其輸入參數有三,分別是輸入的二值圖像;求解距離的類型,以及掩膜尺寸,一般可設為3或者5。
在一張圖像中,兩點之間的距離有多種計算方式,比如
- a a a 水平和數豎直方向的變化量
- b b b 對角方向的變化量
- c c c 條約移動的變化量
距離變換函數綜合了這三種距離,根據各種距離的權重不同,提供了下面幾種不同的距離類別
distanceType | maskSize | 參數 |
---|---|---|
CV_DIST_C | 3 ( 3 × 3 ) (3\times3) (3×3) | a = 1 , b = 1 a=1, b=1 a=1,b=1 |
CV_DIST_L1 | 3 ( 3 × 3 ) (3\times3) (3×3) | a = 1 , b = 2 a=1, b=2 a=1,b=2 |
CV_DIST_L2 | 3 ( 3 × 3 ) (3\times3) (3×3) | a = 0.955 , b = 1.3693 a=0.955, b=1.3693 a=0.955,b=1.3693 |
CV_DIST_L2 | 5 ( 5 × 5 ) (5\times5) (5×5) | a = 1 , b = 1.4 , c = 2.1969 a=1, b=1.4, c=2.1969 a=1,b=1.4,c=2.1969 |
連通域分割
所謂連通域,即Connected Component,是一組彼此相連的像素點的集合,這些像素點彼此之間可以假設一條互相鏈接的路徑,路徑上所有像素的灰度一致,或者符合某個特定的條件。
通過連通域分割,可以將圖像中不同的目標區分開來,為進一步的處理打下基礎,最常用的連通域濾波流程大致如下:圖像灰度化->二值化->形態學處理->標記連通域,其前面的幾個步驟已經在二值化以及距離變換中得以體現,其生成標簽的結果如下圖所示
其中,dilate是膨脹二值圖。對其進行連通域分割,得到labels圖像,其中每一枚硬幣所在區域,都被分配到了一個編號,即Label,最后的三維圖,便是這張圖像的標簽值。
處理和繪圖代碼如下
ret, coins["labels"] = cv2.connectedComponents(coins["dilate"])for i,key in enumerate(['dilate', 'labels'],1):plt.subplot(1,3,i)plt.imshow(coins[key], cmap='gray')plt.title(key)plt.axis('off')ax = plt.subplot(133, projection='3d')
ys, xs = np.indices(coins['labels'].shape)
ax.plot_surface(xs, ys, coins['labels'])
plt.title("labels")
plt.show()
【connectedComponents】是opencv提供的連通域分割函數,其必不可少的輸入參數是一個二值圖像,此外還有兩個整型參數,分別用于規定鄰域形式和輸出的Labels類型。其中,鄰域形式主要分為4-鄰域和8鄰域,前者把當前像素的上下左右四個像素算作鄰域,換言之,這四個像素與當前像素是連通的;8-鄰域則將一個像素周圍的8個像素視作鄰域。