CV8 OpenCV环境下实现大津算法

一 大津算法简介

大津算法(OTSU)是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。

  • 大津算法最终的目的,就是求一个标准的全局阈值区分前景和背景(小于该阈值的为前景。大于该阈值的为背景),使得前景和背景像素的灰度值方差之和最大。因为方差越大,相关性越小,黑白越分明。

  • 大津算法的优势及劣势:对于一些光照变化的图像有较好的滤除性,但需注意,光照部分需影响图像的每一个像素,而不是渐变式影响。这里说的有些抽象,下面我将用图片去解释。

二 理论分析

我们知道,数学中方差公式为:
s 2 = 1 n ×   [ ∑ i = 1 n ( x i − x ‾ ) 2 ] s^2 =\frac{1}{n} \times\ [\sum_{i=1}^n (x_i-\overline{x})^2] s2=n1× [i=1n(xix)2]
如果我们想求整张图像的灰度值方差,那么xi就是各个像素的灰度值,x0就是整张图像的平均灰度值

但在大津算法中,我们默认将图像分为前景和背景,所以方差公式也有一定的变化

大津算法核心公式:
s 2 = w 0 ×   ( u 0 − u ‾ ) 2 + w 1 ×   ( u 1 − u ‾ ) 2 s^2 = w_0 \times\ (u_0-\overline{u})^2+w_1 \times\ (u_1-\overline{u})^2 s2=w0× (u0u)2+w1× (u1u)2
化简后:
g = w 0 ×   w 1 ×   ( u 0 − u 1 ) 2 g = w0\times\ w1 \times\ (u0-u1)^2 g=w0× w1× (u0u1)2
分析这段公式前,我们不妨设几个参数

h:图像的高度(已知)

w:图像的宽度(已知)(h*w 得到图像的像素数量)

t :灰度阈值(我们要求的值,大于这个值的像素我们将它的灰度设置为255,小于的设置为0)

n0:小于阈值的像素,前景

n1:大于等于阈值的像素,背景

图像像素的个数
n 0 + n 1 = h ×   w n_0 + n_1 = h \times\ w n0+n1=h× w
w0:前景像素占总像素的比例
w 0 = n 0 h ×   w w_0 = \frac {n_0}{h \times\ w }\\ w0=h× wn0
w1:背景像素占总像素的比例
w 1 = n 1 h ×   w w_1 = \frac {n_1}{h \times\ w }\\ w1=h× wn1
u0:前景平均灰度
u 0 = ∑ i = 1 n 0 u 0 i n 0 u_0 = \frac {\sum_{i=1}^{n0} u_{0i}}{n_0} u0=n0i=1n0u0i
u1:背景平均灰度
u 1 = ∑ i = 1 n 1 u 1 i n 1 u_1 = \frac {\sum_{i=1}^{n1} u_{1i}}{n_1} u1=n1i=1n1u1i
u:平均灰度
u = w 0 ×   u 0 + w 1 ×   u 1 u = w_0 \times\ {u_0} + w_1 \times\ {u_1} u=w0× u0+w1× u1

最后,标准阈值u从0到255遍历,使得方差最大,并且找出最大方差对应得阈值

三 代码实现

  • 算法库的Python实现
import cv2
import numpy as np

# 大津二值化算法
def otsu(gray_img):
    h = gray_img.shape[0]
    w = gray_img.shape[1]

    threshold_t = 0
    max_g =0

    # 遍历每一个灰度层
    for t in range(255):
        # 使用numpy直接对数组进行运算
        n0 = gray_img[np.where(gray_img <t)]
        n1 = gray_img[np.where(gray_img >=t)]

        w0 = len(n0)/(h*w)
        w1 = len(n1)/(h*w)

        u0 = np.mean(n0) if len(n0) >0 else 0.
        u1 = np.mean(n1) if len(n1) >0 else 0.

        g = w0*w1*(u0-u1)**2
        if g>max_g:
            max_g=g
            threshold_t = t

    print('类间最大方差的阈值:',threshold_t)
    gray_img[gray_img < threshold_t] = 0
    gray_img[gray_img >= threshold_t] =255
    return gray_img

img = cv2.imread('c2.png',0)
otsu_img = otsu(img)
cv2.imshow('otsu_img',otsu_img)

cv2.waitKey(0)
cv2.destroyAllWindows()
  • 大津算法优劣对比
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('p4.png',0)

#不使用大津算法
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

#使用大津算法
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

#使用了大津算法+高斯滤波器
blur = cv2.GaussianBlur(img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# 绘制所有图像及其直方图
images = [img, 0, th1,
          img, 0, th2,
          blur, 0, th3]

titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
         'Original Noisy Image','Histogram',"Otsu's Thresholding",
         'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]

for i in range(3):
    plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
    plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
    plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
    plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])

plt.show()
效果图

右边依次是不使用大津算法,使用大津算法,使用了大津算法+高斯滤波器的图像处理结果,也许你看不出来有什么区别

假设现在图像环境的光照减小,亮度变低了(这里我添加一个黑色,透明度为50%的图层模拟)

使用大津算法有着较好的处理结果,这是它最大的优势:可以滤除光照影响

但是,光照若是斜射的(下面我用渐变模拟),那么大津算法将不适用

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落叶随峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值