# OpenCV-Python 系列 十三 | 图像阈值

### 目标

• 在本教程中，您将学习简单阈值，自适应阈值和Otsu阈值。

### 简单阈值

• cv.THRESH_BINARY
• cv.THRESH_BINARY_INV
• cv.THRESH_TRUNC
• cv.THRESH_TOZERO
• cv.THRESH_TOZERO_INV

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in xrange(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()


### 自适应阈值

BLOCKSIZE确定附近区域的大小，C是从邻域像素的平均或加权总和中减去的一个常数。

### Otsu的二值化

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 全局阈值
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu阈值
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# 高斯滤波后再采用Otsu阈值
blur = cv.GaussianBlur(img,(5,5),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# 绘制所有图像及其直方图
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()


#### Otsu的二值化如何实现？

$$sigma_w^2(t) = q_1(t)sigma_1^2(t)+q_2(t)sigma_2^2(t)$$

$$q_1(t) = sum_{i=1}^{t} P(i) quad quad q_2(t) = sum_{i=t+1}^{I} P(i)$$

$$mu_1(t) = sum_{i=1}^{t} frac{iP(i)}{q_1(t)} quad quad mu_2(t) = sum_{i=t+1}^{I} frac{iP(i)}{q_2(t)}$$

$$sigma_1^2(t) = sum_{i=1}^{t} [i-mu_1(t)]^2 frac{P(i)}{q_1(t)} quad quad sigma_2^2(t) = sum_{i=t+1}^{I} [i-mu_2(t)]^2 frac{P(i)}{q_2(t)}$$

img = cv.imread('noisy2.png',0)
blur = cv.GaussianBlur(img,(5,5),0)
# 寻找归一化直方图和对应的累积分布函数
hist = cv.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.max()
Q = hist_norm.cumsum()
bins = np.arange(256)
fn_min = np.inf
thresh = -1
for i in xrange(1,256):
p1,p2 = np.hsplit(hist_norm,[i]) # 概率
q1,q2 = Q[i],Q[255]-Q[i] # 对类求和
b1,b2 = np.hsplit(bins,[i]) # 权重
# 寻找均值和方差
m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2
# 计算最小化函数
fn = v1*q1 + v2*q2
if fn < fn_min:
fn_min = fn
thresh = i
# 使用OpenCV函数找到otsu的阈值
ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
print( "{} {}".format(thresh,ret) )


### 其他资源

1. Digital Image Processing, Rafael C. Gonzalez

### 练习题

1. Otsu的二值化有一些优化。您可以搜索并实现它。