Ostu
一.定义、原理
Otsu算法:最大类间方差法(大津算法),是一种确定阈值的算法。
对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。
假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
(1) ω0=N0/ (M×N)
(2) ω1=N1/ (M×N)
(3) N0 + N1 = M×N
(4) ω0 + ω1 = 1
(5) μ = ω0 * μ0 + ω1 * μ1
(6) g = ω0 * (μ0 - μ)2 + ω1 * (μ1 - μ)2
将式(5)代入式(6),得到等价公式:
(7) g = ω0 *ω1 * (μ0 - μ1)2
采用遍历的方法得到使类间方差g最大的阈值T。
二、优缺点
优点:算法简单,当目标与背景的面积相差不大时,能够有效地对图像进行分割。
缺点:当图像中的目标与背景的面积相差很大时,表现为直方图没有明显的双峰,或者两个峰的大小相差很大,分割效果不佳,或者目标与背景的灰度有较大的重叠时也不能准确的将目标与背景分开。
原因:该方法忽略了图像的空间信息,同时将图像的灰度分布作为分割图像的依据,对噪声也相当敏感。
三、程序
opencv可以直接调用这种算法,threshold(gray, dst, 0, 255, CV_THRESH_OTSU);
import numpy as np
def otsu_threshold(im):
width, height = im.size
img_size = width * height
pixel_counts = np.zeros(256)
for x in range(width):
for y in range(height):
pixel = im.getpixel((x, y))
pixel_counts[pixel] = pixel_counts[pixel] + 1
# 得到图片的以0-255索引的像素值个数列表
s_max = (0, -10)
for threshold in range(256):
# 遍历所有阈值
# 更新
n_0 = sum(pixel_counts[:threshold]) # 得到阈值以下像素个数
n_1 = sum(pixel_counts[threshold:]) # 得到阈值以上像素个数
w_0 = n_0 / img_size
w_1 = n_1 / img_size
# 得到阈值下所有像素的平均灰度
u_0 = sum([i * pixel_counts[i] for i in range(0, threshold)]) / n_0 if n_0 > 0 else 0
# 得到阈值上所有像素的平均灰度
u_1 = sum([i * pixel_counts[i] for i in range(threshold, 256)]) / n_1 if n_1 > 0 else 0
# 总平均灰度
u = w_0 * u_0 + w_1 * u_1
# 类间方差
g = w_0 * (u_0 - u) * (u_0 - u) + w_1 * (u_1 - u) * (u_1 - u)
# 类间方差等价公式
# g = w_0 * w_1 * (u_0 * u_1) * (u_0 * u_1)
# 取最大的
if g > s_max[1]:
s_max = (threshold, g)
return s_max[0]