读取图像,然后将彩色图像用Otsu算法进行二值化。
Author: Tian YJ
原图如下:
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}^2S02, S12{S_1}^2S12是这两个类中像素值的方差; M0M_0M0,M1M_1M1是这两个类的像素值的平均值;即:
类内方差:Sw2=w0 S02+w1 S12{S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2Sw2=w0 S02+w1 S12 类间方差: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) ^2Sb2=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{常数}St2=Sw2+Sb2=常数根据以上的式子,我们用以下的式子计算分离度XXX:
X=Sb2Sw2=Sb2St2−Sb2
X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2}
X=Sw2Sb2=St2−Sb2Sb2
也就是说:
argmaxt X=argmaxt Sb2
\arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2
argtmax X=argtmax Sb2
换言之,如果使Sb2=w0 w1 (M0−M1)2{S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2Sb2=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,效果非常好!
作者:田纳尔多