本章将学习阈值分割,即找到一个合适的阈值实现数字图像的二值化。阈值分割有很多种方法,本章学习直方图阈值、三角法阈值,迭代法阈值,大律法阈值,和自适应阈值。
1.直方图阈值
图像直方图阈值分割法:
算法描述:根据图像的灰度直方图寻找阈值。
算法特点:适应于于直方图为双峰的图像。
绘制直方图代码:
plt.hist(img.ravel(),256,[0,256])
或者
plt.hist(img.flatten(),np.arange(-0.5,256,1),color='g')
绘制二值图:
_,img_bin = cv.threshold(img,125,255,cv.THRESH_BINARY)
cv.threshold(img,125,255,cv.THRESH_BINARY)
参数说明:
img:原始图像,
阈值:125
最大值:255
二值化方式:cv.THRESH_BINARY
cv.THRESH_BINARY_INV(反转)
输出图像:
2.三角阈值
算法描述:三角几何化的过程。首先找到直方图中灰度值最高的一点并判别亮暗,然后找到最左边点,两点连接一条直线,求直方图上离直线最远的点,设置该点的灰度值为阈值。
算法特点:适用于单峰。
代码:
_,img_bin = cv.threshold(img,125,255,cv.THRESH_TRIANGLE)
3.迭代法阈值
算法步骤:
- step1:选取初试分割阈值,一般初始为图像灰度值的平均值T
- step2:根据阈值T将图像分割为前景和背景(大于阈值为前景,小于为背景),分别求出两者的平均值T0T_0T0和T1T_1T1
- step3:计算新阈值T′=(T0+T1)/2T^{'}=(T_0+T_1)/2T′=(T0+T1)/2
- step4:若T==T′T==T^{'}T==T′,则TTT为最终阈值,否则另T=T′T=T^{'}T=T′,执行step2.
实现代码:
T=img.mean()
def ThresholdIteration(T,img):
while True:
T0=img[img>T].mean()
T1=img[img<=T].mean()
t=(T0+T1)/2
if T==t:
return T
else:
T=t
T=int(ThresholdIteration(T,img))
th,img_bin = cv.threshold(img,T,255,cv.THRESH_BINARY)
print(f'Thrshold is {th}')
show(np.hstack([img,img_bin]))
输出为:
4.大津法阈值(OTSU)
算法描述:
对于给定阈值TTT,将图像分为前景和背景。其中背景点数占图像比例为p0p_0p0,平均灰度值为m0m_0m0,而目标点数占图像比例为p1p_1p1,平均灰度值为m1m_1m1。
整幅图的灰度平均值为:mmm,有:m=p0m0+p1m1m=p_0m_0+p_1m_1m=p0m0+p1m1
方差为:σ2=p0(m0−m)2+p1(m1−m)2\sigma^{2}=p_0(m_0-m)^{2}+p_1(m_1-m)^{2}σ2=p0(m0−m)2+p1(m1−m)2
带入p0p_0p0,p1p_1p1,mmm化简得:σ2=p0p1(m0−m1)2\sigma^{2}=p_0p_1(m_0-m_1)^{2}
σ2=p0p1(m0−m1)2
则遍历灰度值,使得σ2\sigma^{2}σ2最大的值为阈值。
代码实现:
def OTSU(img):
n = img.size #图像点数
Sigma = -1 #初始化sigma
for m in range(0,256): #遍历灰度值
fg = img[img>m] #前景
bg = img[img<=m] #背景
p1 = bg.size/n
p0 = fg.size/n
if bg.size==0:
m1=0
else:
m1=bg.mean()
if fg.size==0:
m0=0
else:
m0=fg.mean()
sigma=p1 * p0 * (m0 - m1)**2
#print(sigma)
if sigma>Sigma:
Sigma=sigma
th = m
return th
T=OTSU(img)
th,img_bin = cv.threshold(img,T,255,cv.THRESH_BINARY)
print(f'Thrshold is {th}')
show(np.hstack([img,img_bin]))
输出结果为:
OpenCV自带有OTSU的阈值函数:
在二值化方式选择cv.THRESH_OTSU
即可
th,img_bin = cv.threshold(img,-1,255,cv.THRESH_OTSU)
5.自适应阈值
算法思想:局部二值化
- step1:对某个像素值,原来的SSS,取其周围n*n的区域,求区域均值或者高斯加权值,记为TTT.
- step2:对8为图像,如果S>TS>TS>T,则该像素点二值化为255,否则为0
优化的情况下:1.可以加入参数CCC,CCC可以为任何数,当S>T−CS>T-CS>T−C,则把原像素值二值化为255. 2.也可以增加一个参数aaa,当S>(1−a)TS>(1-a)TS>(1−a)T时把原像素点二值化为255,通常取a=0.15a=0.15a=0.15
代码为:
img_adapt = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,17,1)
show(img_adapt)
PS:
cv.adaptiveThreshold(原图片,最大值,自适应阈值化方式,二值化方式,卷积核尺寸,参数C)
C=1时:
此例C=8时效果最好:
自己代码实现:
C=0
winSize=21
img_GaussBlur = cv.GaussianBlur(img,(winSize,winSize),1)
img_bin=np.uint8(img>img_GaussBlur-3)*255
show(img_bin)
或者
a=0.15
img_bin=np.uint8(img>(1-a)img_GaussBlur)*255
show(img_bin)