IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 大律法OTSU【详细】(python实现) -> 正文阅读

[Python知识库]大律法OTSU【详细】(python实现)


一、 前言

图像的灰度直方图

灰度直方图是关于灰度级分布的函数,是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。灰度直方图是灰度级的函数,它表示图像中具有某种灰度级的像素的个数,反映了图像中某种灰度出现的频率。【from百度百科】

计算直方图

1.使用opencv的函数

cv2.calcHist(images, channels, mask, histSize, ranges)
  • 参数1:要计算的原图,以方括号的传入,如:[img]。
  • 参数2:类似前面提到的dims,灰度图写[0]就行,彩色图B/G/R分别传入[0]/[1]/[2]。
  • 参数3:要计算的区域ROI,计算整幅图的话,写None。
  • 参数4:也叫bins,子区段数目,如果我们统计0-255每个像素值,bins=256;如果划分区间,比如0-15,16-31…240-255这样16个区间,bins=16。
  • 参数5:range,要计算的像素值范围,一般为[0,256)。

eg:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('test.jpg')
# hist是256x1数组,每个值对应于该图像中具有相应像素值的像素数
hist = cv.calcHist([img],[0],None,[256],[0,256])
# 绘制直方图
plt.plot(hist)
plt.show()

2. 使用Numpy函数

bincount(x, weights=None, minlength=None)

相对应的函数详解可参考:numpy.bincount详解
eg:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('test.jpg')
# ravel()函数将二维矩阵展平变成一维数组
hist = np.bincount(img.ravel(), minlength=256) 
# 绘制直方图
plt.plot(hist)
plt.show()

二、 OTSU算法简介

OTSU算法是一种图像二值化算法。其算法假设图像中存在一个阈值 T ,判断图像中每个像素与T的大小关系,可以将所有像素分为背景 C0 和 前景 C1 两类,且当选取到最佳T阈值时,背景部分与前景部分的差别最大,而OTSU 算法利用最大类间方差来衡量这一差别。由于OTSU算法采用了最大类间方法的思想,因此该算法也被OTSU最大类间方差法

缺点:对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好

三、 数学原理

该算法的原理主要涉及 均值、方差等概念和部分公式的推导

  1. 将图片的像素值分为 [1, 2, …, L] 个水平,用ni 表示各个水平像素值的像素个数,那么很容易得到总像素个数N为:N = n1 + n2 + … + nL

  2. 利用像素值对应个数与总数的商作为某个像素值出现的概率,定义pi
    p i = n i / N , p i ≥ 0 , ∑ i = 1 L p i = 1 (1) p_i = n_i/N, p_i ≥ 0, \sum_{i=1}^L p_i = 1 \tag1 pi?=ni?/N,pi?0,i=1L?pi?=1(1)

  3. 定义两个量w0 , w1为C0,C1的局部概率之和,并且得到二者的关系
    w 0 = P r ( C 0 ) = ∑ i = 1 k p i = w ( k ) (2) w_0 = Pr(C_0) = \sum_{i=1}^k p_i = w(k) \tag2 w0?=Pr(C0?)=i=1k?pi?=w(k)(2)
    w 1 = P r ( C 1 ) = ∑ i = k + 1 L p i = 1 ? w ( k ) (3) w_1 = Pr(C_1) = \sum_{i=k+1}^L p_i = 1 - w(k) \tag3 w1?=Pr(C1?)=i=k+1L?pi?=1?w(k)(3)

  4. 由此我们得到总的数学期望【期望是平均数随样本趋于无穷的极限,所以可以将期望 ≈ 均值】和C0 , C1各自的数学期望并指出三者的关系,式中 i 代表像素值,除于各自概率的和用于进行归一。
    u 0 = ∑ i = 1 k P r ( i ∣ C 0 ) = ∑ i = 1 k i ? p i / w 0 = u ( k ) w ( k ) (4) u_0 = \sum_{i=1}^k Pr(i|C_0) = \sum_{i=1}^k i * p_i / w_0 = \frac{u(k)}{w(k)} \tag4 u0?=i=1k?Pr(iC0?)=i=1k?i?pi?/w0?=w(k)u(k)?(4)
    u 1 = ∑ i = k + 1 L P r ( i ∣ C 1 ) = ∑ i = k + 1 L i ? p i / w 1 = u ( T ) ? u ( k ) 1 ? w ( k ) (5) u_1 = \sum_{i=k+1}^L Pr(i|C_1) = \sum_{i=k+1}^L i * p_i / w_1 = \frac{u(T) - u(k)}{1 - w(k)} \tag5 u1?=i=k+1L?Pr(iC1?)=i=k+1L?i?pi?/w1?=1?w(k)u(T)?u(k)?(5)
    w ( k ) = ∑ i = 1 k p i (6) w(k) = \sum_{i=1}^k p_i \tag6 w(k)=i=1k?pi?(6)
    u ( k ) = ∑ i = 1 k i ? p i (7) u(k) = \sum_{i=1}^k i * p_i \tag7 u(k)=i=1k?i?pi?(7)
    u T = u ( L ) = ∑ i = 1 L i ? p i (8) u_T = u(L) = \sum_{i=1}^L i * p_i \tag8 uT?=u(L)=i=1L?i?pi?(8)
    上述公式间的关系为:
    w 0 u 0 + w 1 u 1 = u T , w 0 + w 1 = 1 (9) w_0 u_0 + w_1 u_1 = u_T , w_0 + w_1 = 1 \tag9 w0?u0?+w1?u1?=uT?,w0?+w1?=1(9)

  5. 各自局部方差和总方差为
    σ 0 2 = ∑ i = 1 k ( i ? u 0 ) 2 P r ( i ∣ C 0 ) = ∑ i = 1 k ( i ? u 0 ) 2 p i / w 0 (10) σ^2_0 = \sum_{i=1}^k (i - u_0)^2 Pr(i|C_0) = \sum_{i=1}^k (i - u_0)^2 p_i / w_0 \tag{10} σ02?=i=1k?(i?u0?)2Pr(iC0?)=i=1k?(i?u0?)2pi?/w0?(10)
    σ 1 2 = ∑ i = k + 1 L ( i ? u 1 ) 2 P r ( i ∣ C 1 ) = ∑ i = k + 1 L ( i ? u 1 ) 2 p i / w 1 (11) σ^2_1 = \sum_{i=k+1}^L (i - u_1)^2 Pr(i|C_1) = \sum_{i=k+1}^L (i - u_1)^2 p_i / w_1 \tag{11} σ12?=i=k+1L?(i?u1?)2Pr(iC1?)=i=k+1L?(i?u1?)2pi?/w1?(11)
    σ T 2 = ∑ i = 1 L ( i ? u T ) 2 p i (12) σ^2_T = \sum_{i=1}^L (i - u_T)^2 p_i \tag{12} σT2?=i=1L?(i?uT?)2pi?(12)

  6. within_class variance 类内差异
    σ w 2 = w 0 σ 0 2 + w 1 σ 1 2 (13) σ^2_w = w_0 σ^2_0 + w_1 σ^2_1 \tag{13} σw2?=w0?σ02?+w1?σ12?(13)
    between_class variance 类间差异
    σ b 2 = w 0 ( u 0 ? u T ) 2 + w 1 ( u 1 ? u T ) 2 (14) σ^2_b = w_0(u_0 - u_T)^2 + w_1(u_1 - u_T)^2 \tag{14} σb2?=w0?(u0??uT?)2+w1?(u1??uT?)2(14)

  7. 当最大化类间差距时,即最小化类内差距,因为 二者的和为定值。
    σ w 2 + σ b 2 = σ T 2 (15) σ^2_w + σ^2_b = σ^2_T \tag{15} σw2?+σb2?=σT2?(15)


四、 python实现

假设一图像为灰度图,大小为M*N,背景部分较暗,OTSU算法步骤如下:

  1. 选取一个分割阈值T,统计计算属于前景的像素点数(像素值小于T)在全部像素点的占比 w 0 w_0 w0? 以及其平均灰度 u 0 u_0 u0? ; 同理统计属于背景部分的 w 1 w_1 w1? u 1 u_1 u1? ;
  2. 计算图像全部像素的平均灰度 u T = w 0 ? u 0 + w 1 ? u 1 u_T = w_0 * u_0 + w_1 * u_1 uT?=w0??u0?+w1??u1? 以及类间方差

g = w 0 ? ( u 0 ? u T ) 2 + w 1 ? ( u 1 ? u T ) 2 = w 0 ? w 1 ? ( u 0 ? u 1 ) 2 (16) g = w_0 * (u_0 - u_T)^2 + w_1 * (u_1 - u_T)^2 = w_0 * w_1 * (u_0 - u_1)^2 \tag{16} g=w0??(u0??uT?)2+w1??(u1??uT?)2=w0??w1??(u0??u1?)2(16)

  1. 遍历全部分割阈值T,重复1-2步骤,得到使类间方差 g 最大的阈值T;
import numpy as np
import cv2 as cv

#将图片转为灰度图
# https://blog.csdn.net/weixin_43635647/article/details/99625105
img = cv.imread('test.jpg', 0)
cv.imshow("img", img)
cv.waitKey()

def OTSU(img_gray, GrayScale):
    assert img_gray.ndim == 2, "must input a gary_img"  #shape有几个数字, ndim就是多少
    img_gray = np.array(img_gray).ravel().astype(np.uint8)
    u1=0.0#背景像素的平均灰度值
    u2=0.0#前景像素的平均灰度值
    th=0.0

    #总的像素数目
    PixSum=img_gray.size
    #各个灰度值的像素数目
    PixCount=np.zeros(GrayScale)
    #各灰度值所占总像素数的比例
    PixRate=np.zeros(GrayScale)
    #统计各个灰度值的像素个数
    for i in range(PixSum):
        #默认灰度图像的像素值范围为GrayScale
        Pixvalue=img_gray[i]
        PixCount[Pixvalue]=PixCount[Pixvalue]+1
    
    #确定各个灰度值对应的像素点的个数在所有的像素点中的比例。
    for j in range(GrayScale):
        PixRate[j]=PixCount[j]*1.0/PixSum
    Max_var = 0
    #确定最大类间方差对应的阈值
    for i in range(1,GrayScale):#从1开始是为了避免w1为0.
        u1_tem=0.0
        u2_tem=0.0
        #背景像素的比列
        w1=np.sum(PixRate[:i])
        #前景像素的比例
        w2=1.0-w1
        if w1==0 or w2==0:
            pass
        else:#背景像素的平均灰度值
            for m in range(i):
                u1_tem=u1_tem+PixRate[m]*m
            u1 = u1_tem * 1.0 / w1
             #前景像素的平均灰度值
            for n in range(i,GrayScale):
                u2_tem = u2_tem + PixRate[n]*n
            u2 = u2_tem / w2
            #print(u1)
            #类间方差公式:G=w1*w2*(u1-u2)**2
            tem_var=w1*w2*np.power((u1-u2),2)
            #print(tem_var)
            #判断当前类间方差是否为最大值。
            if Max_var<tem_var:
                Max_var=tem_var#深拷贝,Max_var与tem_var占用不同的内存空间。
                th=i
    return th 
th = OTSU(img,256)
print("使用numpy的方法:" + str(th))		# 结果为 135

直接使用 opencv的函数

import numpy as np
import cv2 as cv
#该函数返回的第一个值就是输入的thresh值,第二个就是处理后的图像
img = cv.imread('test.jpg',0)
retVal, a_img = cv.threshold(img, 0, 255, cv.THRESH_OTSU)
print("使用opencv函数的方法:" + str(retVal))	# 结果为 134
cv.imshow("a_img",a_img)
cv.waitKey()

左图为原图,右图为二值化后的图像
结果


五、 参考文章

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 13:59:04  更:2021-08-14 14:01:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 10:16:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码