本章将学习阈值分割,即找到一个合适的阈值实现数字图像的二值化。阈值分割有很多种方法,本章学习直方图阈值、三角法阈值,迭代法阈值,大律法阈值,和自适应阈值。

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将图像分割为前景和背景(大于阈值为前景,小于为背景),分别求出两者的平均值 T 0 T_0 T0 T 1 T_1 T1
  • step3:计算新阈值 T ′ = ( T 0 + T 1 ) / 2 T^{'}=(T_0+T_1)/2 T=(T0+T1)/2
  • step4:若 T = = T ′ T==T^{'} T==T,则 T T T为最终阈值,否则另 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)

算法描述:
对于给定阈值 T T T,将图像分为前景和背景。其中背景点数占图像比例为 p 0 p_0 p0,平均灰度值为 m 0 m_0 m0,而目标点数占图像比例为 p 1 p_1 p1,平均灰度值为 m 1 m_1 m1
整幅图的灰度平均值为: m m m,有: m = p 0 m 0 + p 1 m 1 m=p_0m_0+p_1m_1 m=p0m0+p1m1
方差为: σ 2 = p 0 ( m 0 − m ) 2 + p 1 ( m 1 − m ) 2 \sigma^{2}=p_0(m_0-m)^{2}+p_1(m_1-m)^{2} σ2=p0(m0m)2+p1(m1m)2
带入 p 0 p_0 p0, p 1 p_1 p1, m m m化简得: σ 2 = p 0 p 1 ( m 0 − m 1 ) 2 \sigma^{2}=p_0p_1(m_0-m_1)^{2} σ2=p0p1(m0m1)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:对某个像素值,原来的 S S S,取其周围n*n的区域,求区域均值或者高斯加权值,记为 T T T.
  • step2:对8为图像,如果 S > T S>T S>T,则该像素点二值化为255,否则为0

优化的情况下:1.可以加入参数 C C C, C C C可以为任何数,当 S > T − C S>T-C S>TC,则把原像素值二值化为255. 2.也可以增加一个参数 a a a,当 S > ( 1 − a ) T S>(1-a)T S>(1a)T时把原像素点二值化为255,通常取 a = 0.15 a=0.15 a=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)

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐