zoukankan      html  css  js  c++  java
  • 挑战图像处理100问(4)——Otsu

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

    在这里插入图片描述

    关于Otsu算法

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

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

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

    算法流程

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

    在这里插入图片描述

    实现思路

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

    • 小于阈值tt的类记作00,大于阈值tt的类记作11
    • w0w_0w1w_1是被阈值tt分开的两个类中的像素数占总像素数的比率(满足w0+w1=1w_0+w_1=1);
    • S02{S_0}^2S12{S_1}^2是这两个类中像素值的方差;
    • M0M_0M1M_1是这两个类的像素值的平均值;

    即:

    • 类内方差:Sw2=w0 S02+w1 S12{S_w}^2=w_0 {S_0}^2+w_1 {S_1}^2
    • 类间方差:Sb2=w0 (M0Mt)2+w1 (M1Mt)2=w0 w1 (M0M1)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) ^2
    • 图像所有像素的方差:St2=Sw2+Sb2=常数{S_t}^2 = {S_w}^2 + {S_b}^2 = ext{常数}

    根据以上的式子,我们用以下的式子计算分离度XX
    X=Sb2Sw2=Sb2St2Sb2 X = frac{{S_b}^2}{{S_w}^2} = frac{{S_b}^2}{{S_t}^2 - {S_b}^2}

    也就是说:
    argmaxt X=argmaxt Sb2 argmaxlimits_{t} X=argmaxlimits_{t} {S_b}^2
    换言之,如果使Sb2=w0 w1 (M0M1)2{S_b}^2={w_0} {w_1} (M_0 - M_1)^2最大,就可以得到最好的二值化阈值tt

    代码实现

    # -*- 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 < t)] # 划分为0类
    		m_0 = np.mean(x_0) if len(x_0)>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] = 0
    	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,效果非常好!

  • 相关阅读:
    网站前台性能优化教程
    解决Jboss打开run.bat时闪退不能启动的方法
    如何讲解自己开发的程序
    数据库调优教程汇总
    数据库调优教程(十三) MySQL数据库其他优化方法
    数据库调优教程(十二) 优化sql语句
    数据库调优教程(十一) 设计一张漂亮的表
    数据库调优教程(十) 【精华章节】解决like ’%str’ 时索引不被使用的4种方法
    数据库调优教程(九) 添加了索引但不被使用的几种常见可能
    Redis Cluster 实践
  • 原文地址:https://www.cnblogs.com/Jack-Tim-TYJ/p/12831924.html
Copyright © 2011-2022 走看看