在说Otsu之前,先说几个概念

灰度直方图:将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。其实就是每个值(0~255)的像素点个数统计。

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

Otsu算法假设这副图片由前景色和背景色组成,通过最大类间方差选取一个阈值,将前景和背景尽可能分开。

一、代码展示

import cv2
from matplotlib import pyplot as plt
img = cv2.imread('noisy.jpg', 0)
# 固定阈值法
ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # Otsu阈值法
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 先进行高斯滤波,再使用Otsu阈值法
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
images = [img, 0, th1, img, 0, th2, blur, 0, th3]
titles = ['Original', 'Histogram', 'Global(v=100)',
'Original', 'Histogram', "Otsu's",
'Gaussian filtered Image', 'Histogram', "Otsu's"] for i in range(3):
# 绘制原图
plt.subplot(3, 3, i * 3 + 1)
plt.imshow(images[i * 3], 'gray')
plt.title(titles[i * 3], fontsize=8)
plt.xticks([]), plt.yticks([]) # 绘制直方图plt.hist, ravel函数将数组降成一维
plt.subplot(3, 3, i * 3 + 2)
plt.hist(images[i * 3].ravel(), 256)
plt.title(titles[i * 3 + 1], fontsize=8)
plt.xticks([]), plt.yticks([]) # 绘制阈值图
plt.subplot(3, 3, i * 3 + 3)
plt.imshow(images[i * 3 + 2], 'gray')
plt.title(titles[i * 3 + 2], fontsize=8)
plt.xticks([]), plt.yticks([])
plt.show()

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

可以看出Otsu阈值明显优于固定阈值。

二、Otsu算法推导

Otsu阈值法将整幅图分为前景(目标)和背景,以下是一些符号规定:

  • T: 分割阈值
  • N0:前景像素点数
  • N1:背景像素点数
  • ω0:前景的像素点数占整幅图像的比例
  • ω1:背景的像素点数占整幅图像的比例
  • μ0:前景的平均像素值
  • μ1:背景的平均像素值
  • μ:整幅图的平均像素值
  • rows * cols:图像的行数和列数

总的像素个数:

       N0+N1=rows×cols

ω0和ω1是前景、背景所占的比例,也就是:

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

整幅图的平均像素值就是:

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

我们定义一个前景μ0与背景μ1的方差g:

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

联立前面几个公式得:

OpenCV-Python入门教程6-Otsu阈值法-LMLPHP

g就是前景与背景两类之间的方差,这个值越大,说明前景和背景的差别也就越大,效果越好。Otsu算法便是遍历阈值T,使得g最大。所以又称为最大类间方差法。基本上双峰图片的阈值T在两峰之间的谷底。

参考网址:https://tianchi.aliyun.com/course/courseConsole?courseId=40992&chapterIndex=1&sectionIndex=12

05-11 22:51