大津法(otsu算法)是基於全局的閾值分割算法,極易受到光照不均的干擾,使圖像前景像素與背景像素分割不合理,達不到預期效果,故需要採用局部閾值分割的方法,本文采用以每列爲局部,將每列進行一次otsu算法,求出列閾值,以此類推,直至遍歷完所有的列。
代碼如下:
import cv2 as cv
import numpy as np
# 轉灰
def rgb2gray(img):
h=img.shape[0]
w=img.shape[1]
img1=np.zeros((h,w),np.uint8)
for i in range(h):
for j in range(w):
img1[i,j]=0.144*img[i,j,0]+0.587*img[i,j,1]+0.299*img[i,j,1]
return img1
# 二值化
def otsu(img):
h=img.shape[0]
w=img.shape[1]
otsuimg=np.zeros((h,w),np.uint8)
for i in range(w): # 遍歷列
sigma=threshold=0 # 定義類間方差和最終閾值
histogram=np.zeros(256,np.int32) # 初始化各灰度級個數統計
probability=np.zeros(256,np.float32) # 初始化各灰度級概率分佈
for j in range (h): # 遍歷行,進行otsu算法
s=img[j,i]
histogram[s]+=1 # 統計灰度級中每個像素在整幅圖像中的個數
for k in range (256):
probability[k]=histogram[k]/h # 統計每個灰度級佔圖像中的分佈
for p in range (255):
w0 = w1 = 0 # 定義前景像素點和背景像素點灰度級佔圖像中的分佈
fgs = bgs = 0 # 定義前景像素點灰度級總和and背景像素點灰度級總和
for q in range (256):
if q<=p: # 當前i爲分割閾值
w0+=probability[q] # 前景像素點佔整幅圖像的比例累加
fgs+=q*probability[q] # 前景像素點的平均灰度
else:
w1+=probability[q] # 背景像素點佔整幅圖像的比例累加
bgs+=q*probability[q] # 背景像素點的平均灰度
u0=fgs/w0
u1=bgs/w1
g=w0*w1*(u0-u1)**2 # 類間方差
if g>=sigma:
sigma=g
threshold=p
for j in range (h): # 對某列的每一行進行二值化
if img[j,i]>threshold:
otsuimg[j,i]=255
else:
otsuimg[j,i]=0
return otsuimg
image = cv.imread("D:/Testdata/a.png")
grayimage = rgb2gray(image)
otsuimage = otsu(grayimage)
cv.imshow("image", image)
cv.imshow("otsuimage", otsuimage)
cv.waitKey(0)
cv.destroyAllWindows()
實驗結果:
爲檢驗二值化效果,與otsu算法實驗結果比較:
比較發現,這種基於otsu算法的局部二值化算法效果並不明顯,所以算是一個失敗品吧。