挑战图像处理100问(4)——Otsu

Halona ·
更新时间:2024-09-21
· 500 次阅读

在这里插入图片描述
读取图像,然后将彩色图像用Otsu算法进行二值化。
Author: Tian YJ
原图如下:

在这里插入图片描述

关于Otsu算法

Otsu算法是灰度图像的自动阈值分割。发明人是个日本人,叫做Nobuyuki Otsu (大津展之),所以此算法也被称大津二值化法。它是一种基于全局的二值化算法,它是根据图像的灰度特性,将图像分为前景和背景两个部分。当取最佳國值时,两部分之间的差别应该是最大的,在Otsu算法中所采用的衡量差别的标准就是较为常见的最大类间方差。

前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大,当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小,当所取阈值的分割使类间方差最大时就意味着错分概率最小

Otsu使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分(可以推广至n个部分),使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分。所以可以在二值化的时候采用Otsu算法来自动选取阈值进行二值化。Otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,至今也被广泛应用于包含医学图像在内的各个领域。

算法流程

下图显示了Otsu算法的流程图,其具体步骤为:
(1)统计灰度级中每个像素在整幅图像中的个数;
(2)计算每个像素在整幅图像的概率分布;
(3)通过目标函数计算最大类间方差下的阈值;
(4)由所得阈值对图像进行阈值分割。

在这里插入图片描述

实现思路

类内方差类间方差的比值计算得来:

小于阈值ttt的类记作000,大于阈值ttt的类记作111; w0w_0w0​和w1w_1w1​是被阈值ttt分开的两个类中的像素数占总像素数的比率(满足w0+w1=1w_0+w_1=1w0​+w1​=1); S02{S_0}^2S0​2, S12{S_1}^2S1​2是这两个类中像素值的方差; M0M_0M0​,M1M_1M1​是这两个类的像素值的平均值;

即:

类内方差:Sw2=w0 S02+w1 S12{S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2Sw​2=w0​ S0​2+w1​ S1​2 类间方差:Sb2=w0 (M0−Mt)2+w1 (M1−Mt)2=w0 w1 (M0−M1)2{S_b}^2 = w_0 \ (M_0 - M_t)^2 + w_1\ (M_1 - M_t)^2 = w_0\ w_1\ (M_0 - M_1) ^2Sb​2=w0​ (M0​−Mt​)2+w1​ (M1​−Mt​)2=w0​ w1​ (M0​−M1​)2 图像所有像素的方差:St2=Sw2+Sb2=常数{S_t}^2 = {S_w}^2 + {S_b}^2 = \text{常数}St​2=Sw​2+Sb​2=常数

根据以上的式子,我们用以下的式子计算分离度XXX:
X=Sb2Sw2=Sb2St2−Sb2 X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2} X=Sw​2Sb​2​=St​2−Sb​2Sb​2​

也就是说:
arg⁡max⁡t X=arg⁡max⁡t Sb2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2 argtmax​ X=argtmax​ Sb​2
换言之,如果使Sb2=w0 w1 (M0−M1)2{S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2Sb​2=w0​ w1​ (M0​−M1​)2最大,就可以得到最好的二值化阈值ttt。

代码实现 # -*- coding: utf-8 -*- """ Created on Tue Apr 7 16:43:27 2020 @author: Tian YJ """ import numpy as np import cv2 # 灰度化函数 def BGR2GRAY(img): # 灰度化 out = np.ones((H,W,3)) for i in range(H): for j in range(W): out[i,j,:] = 0.299*img[i,j,0] + 0.578*img[i,j,1] + 0.114*img[i,j,2] out = out.astype(np.uint8) return out # Otsu 二值化 def otsu_binarization(out, th=128): max_var = 0 # 初始化最大类间方差 max_t = 0 # 初始化最佳阈值 ## 确定阈值 for t in range(255): x_0 = out[np.where(out 0 else 0 # 0类像素值的平均值 w_0 = len(x_0) / (H*W) # 0类像素数占总像素数的比例 x_1 = out[np.where(out >= t)] # 划分为1类 m_1 = np.mean(x_1) if len(x_1)>0 else 0 # 1类像素值的平均值 w_1 = len(x_1) / (H*W) # 1类像素数占总像素数的比例 var = w_0 * w_1 * ((m_0 - m_1)**2) # 求类间方差 if var > max_var: max_var = var max_t = t # 找到最佳阈值后开始二值化 print('最佳阈值:', max_t) th = max_t out[out = th] = 255 return out # 读取图片 path = 'C:/Users/86187/Desktop/image/' file_in = path + 'cake.jpg' file_out = path + 'otsu_binarization.jpg' img = cv2.imread(file_in) # 获取图片尺寸 H, W, C = img.shape # 调用函数 out = BGR2GRAY(img) out = otsu_binarization(out) # 保存图片 cv2.imwrite(file_out, out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows() 输出结果
输出图像 最佳阈值
在这里插入图片描述 在这里插入图片描述
普通二值化 Otsu二值化
在这里插入图片描述 在这里插入图片描述

两种二值化结果对比,可以看出,Otsu确实牛B,效果非常好!


作者:田纳尔多



otsu 图像处理

需要 登录 后方可回复, 如果你还没有账号请 注册新账号