Opencv图像阈值之Otsu’s二值化

今天我们来学习Opencv图像阈值之Otsu’s二值化,大家看到Hungry Lips的作品了吗?看了她的作品,我感觉打了鸡血一样的充满力量。

啥是Otsu’s 二值化呢?

往下看,具体例子中看

我们来看张图

小海豹来了~

我来解释一下这些都是什么

  • Original Noisy Image:这是原图像
  • Histogram:直方图
  • Global Thresholding:全局阈值
  • Otsu’s Thresholding:Otsu’s 阈值
  • Gaussian filtered Image:高斯过滤图像

-啊!我的眼睛,我怎么看不出有什么不一样!

-啊!我的眼睛,我也看不出有什么不一样!

我们来听下书面的解释:

 

============================================================

Otsu又叫大津法,也叫最大类间方差法,是一种自适应的阈值确定的方法,它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均
灰度记为μ,类间方差记为g。假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:

==============================================================

到此为止吧,我的眼睛又瞎了。。。

我就知道你们不会去看的,

所以我先上代码

# -*- coding:utf-8 -*-
"""
Created on Mon Mar 6 19:35:55 2017

@author: Y

"""

import cv2  
import numpy as np  
from matplotlib import pyplot as plt  
 
img = cv2.imread('C:\Users\Administrator\Desktop\opencvimg\haibaobaobao.jpg',0)  
 
# global thresholding  
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)  
 
# Otsu's thresholding  
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)  
 
# Otsu's thresholding after Gaussian filtering
# (5,5)为高斯核的大小,0为标准差
blur = cv2.GaussianBlur(img,(5,5),0)
# 阈值设为0
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)  
 
# plot all the images and their histograms  
images = [img, 0, th1,  
          img, 0, th2,  
          blur, 0, th3]  
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',  
          'Original Noisy Image','Histogram',"Otsu's Thresholding",  
          'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]  
 
for i in xrange(3):  
    plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')  
    plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])  
    plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)  
    plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])  
    plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')  
    plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])  
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()

 

 

代码中用了三种方法:

  1. 127为全局阈值
  2. 直接使用Otsu二值化
  3. 先用5*5 的高斯核去噪,然后使用Otsu二值化

其实就是在

cv2.threshold的基础上多传了个参数,cv2.THRESH_OTSU,

这时我们把阈值设为0,

算法会自动找到最优阈值retVal。

 

我说下我的理解吧:

我们把图像分为前景和背景两部分

图像整体:

  • 分割阈值T
  • 大小M*N
  • 图像的总平均灰度b
  • 类间方差g

前景:

  • 像素点数占整幅图的比例为a1
  • 平均灰度b1

背景:

  • 像素点数占整幅图的比例为a2
  • 平均灰度b2

 

则:

  • a1=b1/(M*N)
  • a2=b2/(M*N)
  • a1+a2=1
  • b1+b2=1
  • b=a1*b1+a2*b2
  • g=a1(b1-b)^2 +a2(b2-b)^2

结合最后两个得:

b=a1*a2(b1-b2)^2

(ps:^是平方)

Otsu标准是:

类间方差最大的分割意味着错分概率最小。

 

然后我们可以用便利的方法得到使类间方差最大的阈值T(retVal)

给出原图像

这张可爱的海豹宝宝就是原图像

 

大家也可以拿别的图试试!

Opencv图像阈值之Otsu’s阈值,写完这一章我整整用了两天的空闲时间,去组织语言与简化,今天终于发出来了,终于可以去睡个好觉了!我知道自己这一章还有提升的空间,但我真的尽力了,不知道如何再去写的更加具有可读性。这一章对于日后的大项目而言真的至关重要,整整好几个小时就是为了以后的顺利进行,所以大家务必务必好好理解。我是Y,希望大家坚定向前!