本篇博客在书写过程中参考了一下两篇博客,在此贴出来,以示对原创者的尊重:OTSU算法(大津法—最大类间方差法)原理及实现、详细及易读懂的 大津法(OTSU)原理 和 算法实现。再次向二位大佬致敬!
1.OTSU(最大类间差法):
OTST算法是用来对灰度图像(单通道)图像进行二值化阈值分割的基础图像分割算法,利用此方法进行图像二值化分割
后,前景与背景
图像的类间方差最大
。
常用场合:求解图像的全局最佳阈值。(原因如下:OTSU按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小
。因此,使类间方差最大的分割意味着错分概率最小。)
缺点**:**对图像噪声敏感
;只能针对单一目标分割
;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。
2.OTSU具体数学原理
(1)背景像素占比::
其中(N1代表前景像素的个数,S代表图像总的像素数)
(2)前景像素占比:
(3)背景像素的平均灰度值:
(其中,Pi表示的是背景中,灰度值为i的像素点的个数,μt1表示的是背景像素相对于整个图像的灰度的数学期望值。)
(4)前景像素的平均灰度值:
(其中,μt2表示的是前景像素相对于整个图像的灰度到的数学期望值。)
(5) 0-M灰度范围内的图像的灰度均值:
(6)类间方差:
(7)将(5)代入(6)得:
3.OTSU源码及应用示例。
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 23 17:48:57 2020
@author: Administrator
"""
'''
OTSU算法详解:
(1)背景像素占比: (N1代表前景像素的个数,S代表图像总的像素数)
(2)前景像素占比:
(3)背景像素的平均灰度值: (其中,Pi表示的是背景中,灰度值为i的像素点的个数,μt1表示的是背景像素相对于整个图像的灰度的数学期望值。)
(4)前景像素的平均灰度值: (其中,μt2表示的是前景像素相对于整个图像的灰度到的数学期望值。)
(5) 0-M灰度范围内的图像的灰度均值::
(6)类间方差:
(7)将(5)代入(6)得:
'''
import numpy as np
import cv2
class OTSU_Segmentation():
def __init__(self,filename):
self.source_img=cv2.imread(filename)
self.u1=0.0#背景像素的平均灰度值
self.u2=0.0#前景像素的平均灰度值
self.th=0.0
'''
OTSU实现
'''
def CalTh(self,GrayScale):
img_gray=cv2.cvtColor(self.source_img,cv2.COLOR_BGR2GRAY)
img_gray=np.array(img_gray).ravel().astype(np.uint8)
#总的像素数目
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
self.u1=u1_tem*1.0/w1
#前景像素的平均灰度值
for n in range(i,GrayScale):
u2_tem=u2_tem+PixRate[n]*n
self.u2=u2_tem/w2
#print(self.u1)
#类间方差公式:G=w1*w2*(u1-u2)**2
tem_var=w1*w2*np.power((self.u1-self.u2),2)
#print(tem_var)
#判断当前类间方差是否为最大值。
if Max_var<tem_var:
Max_var=tem_var#深拷贝,Max_var与tem_var占用不同的内存空间。
self.th=i
#print(self.th)
def Otsu_translation(self):
img_g=cv2.cvtColor(self.source_img,cv2.COLOR_BGR2GRAY)
result,img_seg=cv2.threshold(img_g,self.th,255,cv2.THRESH_BINARY)
print(result)
cv2.imwrite('1_seg.jpg',img_seg)
cv2.imshow('The result of OTSU segtaton',img_seg)
cv2.waitKey(0)
otsu=OTSU_Segmentation('1.jpg')
otsu.CalTh(256)
otsu.Otsu_translation()
原图及使用OTSU阈值分割后的结果如下:
原图1:
求得的阈值如下:
阈值分割结果如下:
原图2:
对应的阈值如下:
分割的结果如下: