图像处理的过程中,有时候为了加快处理速度,可以容忍一定的信息丢失,这时候就需要用到图像的二值化处理,opencv提供了一些图像二值化的API
1. 简单二值化
简单的二值化处理为,输入图像的灰度图数据,如果像素值大于阈值,则会为其指定一个值(可能为白色),否则将为其指定另一个值(可能为黑色),使用的函数为
double cv::threshold (
InputArray src, //输入的灰度图
OutputArray dst, //输出图
double thresh, //像素值阈值
double maxval, // 当阈值类型为THRESH_BINARY和THRESH_BINARY_INV时的最大值
int type //二值化类型
)
二值化类型
二值化类型图解
示例代码
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg',0)
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','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()
py版本的cv2.threshold返回值有两个,其中第二个参数是本次二值化所使用的阈值(上表的最后两种二值化类型是根据图像计算阈值的),第一个是二值化结果。结果:
2. 自适应二值化
在简单二值化中,我们使用全局参数作为阈值。但是,假如图像各部分光照情况不同,可能会丢失掉很多信息,得到不好的结果。在这种情况下,我们采用自适应阈值。在该算法中,对图像中的一个小区域进行阈值计算。因此,对于同一图像的不同区域,就得到了不同的阈值,对于光照不同的图像,自适应二值化能得到更好的结果。
void cv::adaptiveThreshold (
InputArray src, //8位单通道图像数据
OutputArray dst, //输出图像
double maxValue, //当condition满足时目标像素点的值,非零
int adaptiveMethod, //自适应算法
int thresholdType, //二值化类型,见上一小节中的说明,此处只能取值THRESH_BINARY或THRESH_BINARY_INV
int blockSize, //用来计算自适应阈值的块大小,如3、5、7...
double C //从平均数或加权平均数中减去的常数。通常情况下,它是正的,但也可以是零,也可以是负的。
)
自适应算法:
|算法名|说明|
|-|-|
|ADAPTIVE_THRESH_MEAN_C |自适应阈值 T(x,y)=(x,y)周边blockSizeblockSize范围内点像素值的平均值 - C|
|ADAPTIVE_THRESH_GAUSSIAN_C |自适应阈值 T(x,y)=(x,y)周边blockSizeblockSize范围内点像素值基于高斯窗口的加权平均数 - C|
示例代码:
import cv2
import numpy as np
from matplotlib import pyplot as plt
def testAdaptiveThreshold():
img = cv2.imread('test.jpg', 0)
img = cv2.medianBlur(img, 5)
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
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])
plt.xticks([]), plt.yticks([])
plt.show()
效果非常明显,很好的提取了轮廓部分
3. 大津法二值化
大津法二值化,本身也是一种固定阈值的二值化,只不过其阈值是根据图像直方图来计算的,这种方法对于直方图有两个波峰的图像效果比较好。
使用cv2.Threshold()函数,二值化类型传cv2.THRESH_Otsu,阈值传零即可。该算法会自动找到最优阈值,并将找到的阈值作为第二个返回值返回。如果未使用Otsu阈值,则retval与所使用的阈值相同。
下面的示例,输入图像是一个有噪声的图像。第一种情况,使用全局阈值127;第二种情况,直接使用Otsu的阈值;第三种情况,使用5x5高斯核过滤图像以去除噪声,然后应用Otsu阈值。噪声过滤结合大津法二值化,很好的优化了结果。
import cv2
import numpy as np
from matplotlib import pyplot as plt
def testOtsu():
img = cv2.imread('noisy2.png', 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
blur = cv2.GaussianBlur(img, (5, 5), 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 range(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()
本文链接:https://www.zoucz.com/blog/2019/02/27/fb0f42a0-3a65-11e9-9947-3d7b79f522a2/