在这里,问题直截了当。对于每一个像素,应用相同的阈值。若是像素值小于阈值,则将其设置为0,不然将其设置为最大值。函数cv.threshold用于应用阈值。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是分配给超过阈值的像素值的最大值。OpenCV提供了不一样类型的阈值,这由函数的第四个参数给出。经过使用cv.THRESH_BINARY类型。全部简单的阈值类型为:python
请经过类型的文档来观察区别。git
该方法返回两个输出。第一个是使用的阈值,第二个输出是阈值后的图像。算法
此代码比较了不一样的简单阈值类型:函数
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread('gradient.png',0) 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()
> 注意 为了绘制多个图像,咱们使用plt.subplot()
函数。请查看matplotlib文档以获取更多详细信息。学习
该代码产生如下结果:优化
在上一节中,咱们使用一个全局值做为阈值。但这可能并不是在全部状况下都很好,例如,若是图像在不一样区域具备不一样的光照条件。在这种状况下,自适应阈值阈值化能够提供帮助。在此,算法基于像素周围的小区域肯定像素的阈值。所以,对于同一图像的不一样区域,咱们得到了不一样的阈值,这为光照度变化的图像提供了更好的结果。.net
除上述参数外,方法cv.adaptiveThreshold还包含三个输入参数:code
该adaptiveMethod决定阈值是如何计算的:orm
cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C。 cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C。blog
该BLOCKSIZE肯定附近区域的大小,C是从邻域像素的平均或加权总和中减去的一个常数。
下面的代码比较了光照变化的图像的全局阈值和自适应阈值:
结果:
在全局阈值化中,咱们使用任意选择的值做为阈值。相反,Otsu的方法避免了必须选择一个值并自动肯定它的状况。
考虑仅具备两个不一样图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。相似地,Otsu的方法从图像直方图中肯定最佳全局阈值。
为此,使用了cv.threshold做为附加标志传递。阈值能够任意选择。而后,算法找到最佳阈值,该阈值做为第一输出返回。
查看如下示例。输入图像为噪点图像。在第一种状况下,采用值为127的全局阈值。在第二种状况下,直接采用Otsu阈值法。在第三种状况下,首先使用5x5高斯核对图像进行滤波以去除噪声,而后应用Otsu阈值处理。了解噪声滤波如何改善结果。
import cv2 as cv import numpy as np from matplotlib import pyplot as plt img = cv.imread('noisy2.png',0) # 全局阈值 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二值化的Python实现,以展现其实际工做方式。若是您不感兴趣,能够跳过此步骤。
因为咱们正在处理双峰图像,所以Otsu的算法尝试找到一个阈值(t),该阈值将由关系式给出的加权类内方差最小化:
$$ \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)} $$
实际上,它找到位于两个峰值之间的t值,以使两个类别的差别最小。它能够简单地在Python中实现,以下所示:
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) )