作者:RayChiu_Labloy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
目录
先说固定阈值分割函数:cv2.threshold(src, thresh, maxval, type[, dst])
再说自适应阈值分隔:cv2.adaptiveThreshold()
“二值化”图像的三种方式:固定阈值、自适应阈值和Otsu阈值法(经验之谈:很多人误以为阈值分割就是二值化,两者并不等同,阈值分割结果是两类值,而不是两个值,所以二值化加了引号。)
先说固定阈值分割函数:cv2.threshold(src, thresh, maxval, type[, dst])
- src:表示的是图片源
- thresh:表示的是阈值(起始值)
- maxval:表示的是最大值(第四个参数type为CV_THRESH_BINARY和CV_THRESH_BINARY_INV是的最大值)
- type:表示的是这里划分的时候使用的是什么类型的算法,常用值为0(cv2.THRESH_BINARY)
其中第四个参数type有五种类型:
测试一下效果:
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread("21.bmp",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', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
显示效果:
再说自适应阈值分隔:cv2.adaptiveThreshold()
固定阈值化的缺点就是对暗亮程度不同、明暗分布不均的图片无法做到很好的处理,自适应性二值化,相比于固定阈值的二值化处理,自适应阈值不需要确定一个固定的阈值,而是可以根据对应的自适应方法,通过图像的局部特征自适应的设定阈值,也就是取图片的一小部分计算阈值,做出二值化处理,下面我们分析下自适应阈值分割函数:
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)
这个函数大致意思就是把图片每个像素点作为中心取N*N的区域,然后计算这个区域的阈值,来决定这个像素点变0还是变255,参数解释如下:
测试图片:
测试代码:
import cv2
from matplotlib import pyplot as plt
# 自适应阈值对比固定阈值
img = cv2.imread('21.bmp', 0)
# 固定阈值
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 自适应阈值
th2 = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4)
th3 = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 17, 6)
titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i], fontsize=8)
plt.xticks([]), plt.yticks([])
plt.show()
测试效果:
最后分析OTSU算法(又称为大津算法和最大类间方差法)
在前面固定阈值中,我们是随便选了一个阈值如127,那如何知道我们选的这个阈值效果好不好呢?答案是:不断尝试,所以这种方法在很多文献中都被称为经验阈值。
最大类间方差法是1979年由日本学者大津提出的,是一种自适应阈值确定的方法,又叫大津法,简称OTSU,Otsu阈值法会自动计算阈值,是一种基于全局的二值化算法,它是根据图像的灰度特性(直方图相关),将图像分为前景和背景两个部分。当取最佳阈值时,两部分之间的差别应该是最大的,在OTSU算法中所采用的衡量差别的标准就是较为常见的最大类间方差,前景和背景尽可能分开。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大,当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小,当所取阈值的分割使类间方差最大时就意味着错分概率最小。
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()
测试结果:
Otsu算法详解
Otsu阈值法将整幅图分为前景(目标)和背景,以下是一些符号规定:
$ T $:分割阈值
$ N_0 $:前景像素点数
$ N_1 $:背景像素点数
$ \omega_0 $:前景的像素点数占整幅图像的比例
$ \omega_1 $:背景的像素点数占整幅图像的比例
$ \mu_0 $:前景的平均像素值
$ \mu_1 $:背景的平均像素值
$ \mu $:整幅图的平均像素值
$ rows\times cols $:图像的行数和列数
结合下图会更容易理解一些,有一副大小为4×4的图片,假设阈值T为1,那么:
其实很好理解,$ N_0+N_1 $就是总的像素点个数,也就是行数乘列数:
$ \omega_0 $和$ \omega_1 $是前/背景所占的比例,也就是:
整幅图的平均像素值就是:
此时,我们定义一个前景$ \mu_0 $与背景$ \mu_1 $的方差$ g $:
将前述的1/2/3公式整合在一起,便是:
【如果对您有帮助,交个朋友给个一键三连吧,您的肯定是我博客高质量维护的动力!!!】