youcans_已于 2022-06-02 09:36:11 修改
3227
收藏 2
分類專欄:
# OpenCV例程/youcans 文章標(biāo)簽:
opencv python 圖像處理 計(jì)算機(jī)視覺(jué) 算法版權(quán)
OpenCV例程/youcans專欄收錄該內(nèi)容261 篇文章1666 訂閱
訂閱專欄
歡迎關(guān)注
『youcans 的 OpenCV 例程 200 篇』 系列,持續(xù)更新中
OpenCV 例程200篇 總目錄-202205更新【youcans 的 OpenCV 例程200篇】164.使用 Laplace 邊緣信息改進(jìn)全局閾值處理
3.4 全局閾值處理改進(jìn)方法
在實(shí)際的
圖像處理中,噪聲嚴(yán)重影響閾值處理的結(jié)果,嚴(yán)重的噪聲會(huì)把簡(jiǎn)單的閾值處理問(wèn)題變?yōu)椴荒芙鉀Q的問(wèn)題。
例程 11.21:使用 Laplace 邊緣信息改進(jìn)全局閾值處理
對(duì)于酵母細(xì)胞圖像,希望通過(guò)全局閾值處理等等圖像中與亮點(diǎn)對(duì)應(yīng)的區(qū)域。如果直接使用
OTSU 方法可以分割細(xì)胞區(qū)域,但不能檢測(cè)亮點(diǎn)。
使用 Laplace
算子計(jì)算梯度,可以得到亮點(diǎn)的邊緣像素,忽略背景區(qū)域像素對(duì)直方圖的貢獻(xiàn),可以改善直方圖的分布,從而便于通過(guò)閾值處理進(jìn)行分割。
具體步驟如下:
(1)計(jì)算圖像 f ( x , y ) f(x,y)f(x,y) 的 Laplace 算子,得到梯度圖像;
(2)以灰度值的 99.5% 分位為閾值,對(duì)梯度圖像進(jìn)行二值處理,作為遮罩模板,以排除無(wú)效背景像素的影響;
(3)基于遮罩模板計(jì)算圖像 f ( x , y ) f(x,y)f(x,y) 的直方圖分布,即只對(duì) g T ( x , y ) = 1 g_T(x,y)=1gT(x,y)=1 的像素進(jìn)行統(tǒng)計(jì)計(jì)算;
(4)基于遮罩模板的直方圖分布,采用 OTSU 算法計(jì)算最佳分割閾值;
(5)使用 OTSU 算法得到的最佳分割閾值,對(duì)圖像 f ( x , y ) f(x,y)f(x,y) 進(jìn)行全局閾值處理。
注意本例中用 OTSU 算法求非零像素的最佳分割閾值,不能通過(guò)調(diào)用 cv2.threshold() 獲得。
# 11.21 使用 Laplace 邊緣信息改進(jìn)全局閾值處理 img = cv2.imread("../images/Fig1043a.tif", flags=0) # # 全局閾值處理,作為參照比較 histCV1 = cv2.calcHist([img], [0], None, [256], [0, 256]) # 灰度直方圖 ret1, imgOtsu = cv2.threshold(img, 127, 255, cv2.THRESH_OTSU) # 閾值分割, thresh=T # (1) 計(jì)算 Laplacian 梯度算子 laplace = cv2.Laplacian(img, cv2.CV_32F, ksize=3) # Laplace 卷積算子 grad = cv2.convertScaleAbs(laplace) gradMax = np.int(np.max(grad)) # (2) 以灰度值的 99.5% 分位為閾值, 對(duì)邊緣圖像進(jìn)行二值處理, 作為遮罩模板 per995 = np.percentile(grad, q=99.5) # 99.5 分位的灰度值, [0, per995] 占比99.5% _, gradPer995 = cv2.threshold(np.uint8(grad), per995, 1, cv2.THRESH_BINARY) # 對(duì)邊緣圖像二值處理 # (3) 計(jì)算基于遮罩模板的直方圖分布,以排除無(wú)效背景像素的影響 fp = np.uint8(img * gradPer995) histCV2 = cv2.calcHist([fp], [0], None, [256], [0, 256]) histCV2[0] = 0 # fp 非零像素直方圖 # (4) OTSU 算法計(jì)算 fp 非零像素的最佳分割閾值 # nonzeroPixels = np.count_nonzero(gradPer995) # 非零像素總數(shù) nonzeroPixels = sum(histCV2[1:]) # 非零像素總數(shù) totalGray = np.dot(histCV2[:,0], range(256)) # 內(nèi)積, 總和灰度值 mG = totalGray / nonzeroPixels # 平均灰度 icv = np.zeros(256) numFt, sumFt = 0, 0 for t in range(0, 256): # 遍歷灰度值 numFt += histCV2[t,0] # F(t) 像素?cái)?shù)量 sumFt += histCV2[t,0] * t # F(t) 灰度值總和 pF = numFt / nonzeroPixels # F(t) 像素?cái)?shù)占比 mF = (sumFt/numFt) if numFt>0 else 0 # F(t) 平均灰度 numBt = nonzeroPixels-numFt # B(t) 像素?cái)?shù)量 sumBt = totalGray - sumFt # B(t) 灰度值總和 pB = numBt / nonzeroPixels # B(t) 像素?cái)?shù)占比 mB = (sumBt/numBt) if numBt>0 else 0 # B(t) 平均灰度 icv[t] = pF * (mF-mG)**2 + pB * (mB-mG)**2 # OTSU 算法: 灰度 t 的類間方差 maxIcv = max(icv) # ICV 的最大值 maxIndex = np.argmax(icv) # 最大值的索引 print(per995, nonzeroPixels, maxIcv, maxIndex) # 使用 fp 非零像素的最佳分割閾值,對(duì)原始圖像進(jìn)行固定閾值處理 ret, imgBin = cv2.threshold(img, maxIndex, 255, cv2.THRESH_BINARY) # 以 maxIndex 作為最優(yōu)閾值 plt.figure(figsize=(10, 7)) plt.subplot(231), plt.axis('off'), plt.title("Origin"), plt.imshow(img, 'gray') plt.subplot(232,yticks=[]), plt.axis([0,255,0,np.max(histCV1)]) plt.bar(range(256), histCV1[:,0]), plt.title("Gray Hist") plt.subplot(233), plt.title("OTSU binary(T={})".format(round(ret1))), plt.axis('off') plt.imshow(imgOtsu, 'gray') plt.subplot(234), plt.axis('off'), plt.title("Threshold of Laplacian") plt.imshow(gradPer995, cmap='gray') # 遮罩模板,Laplacian 995 分位 plt.subplot(235, yticks=[]), plt.title("Hist of boundries") # 直方圖 plt.bar(range(256), histCV2[:,0]) plt.subplot(236), plt.title("OTSU by Laplacian(T={})".format(maxIndex)), plt.axis('off') plt.imshow(imgBin, 'gray') plt.show()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
(本節(jié)完)