OTSU(大津算法)
确定图像二值化分割阈值
不受图像亮度和对比度的影响
用于图像分割过程中,自动计算出一个最佳全局阈值的算法
通过最大类间平方差的方法来区分图像前景及背景
缺点
- 对图像噪声敏感
- 只能针对单一目标分割
- 当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,效果不好
类间平方差
OTSU算法的核心是类间平方差
类间平方差:将图像的阈值划分为两个区域寻找这两个区域阈值间的最大方差
类间方差公式
假设存在一个阈值TH将整幅图(灰度值为0~255)分为了两个部分:
- 第一部分是小于假定阈值TH的那部分。这一部分的平均灰度值为m1,这一部分在整幅图中出现的概率为P1
- 第二部分是大于假定阈值TH的那部分。这部分的平均灰度值为m2,出现的概率为P2
- MG则是全局的平均阈值
公式求解:
- m1可以想象为从0到假定阈值TH处每个灰度值与该灰度值出现的频率的乘积再除以该区域的总频率,就是它的平均阈值
- k——假定阈值TH
- i——灰度值
- pi——每个灰度值出现的频率
- p1——0~k区域每个灰度值出现概率的总和
- m2:
- L: 图像的像素级,通常都是255
- p2: k+1~L区域每个灰度值出现概率的总和
- p1、p2
- 全局的平均阈值
- 类间方差表达式
实现
import cv2 import numpy as np img = cv2.imread("./lena.jpg",0) h,w = img.shape hist = [0 for i in range(256)] for i in range(w): for j in range(h): hist[img.item(j,i)]+=1 ave_hist = list(map(lambda i: i/(w*h),hist)) M=0 for i in range(256): M += i*ave_hist[i] otsu_value = [] for i in range(256): m1,m2,p1,p2=0,0,0,0 for v1 in range(0,i+1): p1+=ave_hist[v1] m1+=ave_hist[v1]*v1 if p1: m1 /= p1 else: m1=-1 for v2 in range(i+1,256): p2+=ave_hist[v2] m2+=ave_hist[v2]*v2 if p2: m2 /= p2 else: m2=-1 M = p1*m1+p2*m2 thes = p1*(m1-M)**2 + p2*(m2-M)**2 otsu_value.append(thes) max_v = max(otsu_value) idx = otsu_value.index(max_v) print(len(ave_hist)) print("thes:",idx)
opencv 内部的函数:
ret, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)