教程汇总:python基础入门系列
定义: 图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。
二值化处理是图像预处理中比较常见且重要的一步,通过二值化处理可以凸显需要处理的部分,大大缩减后续图像处理的数据量。
简单阈值当然是最简单,选取一个全局阈值,然后就把整幅图像分成了非黑即白的二值图像了。函数为Python-OpenCV中提供了阈值(threshold)函数:
cv2.threshold(src, thresh, maxval, type)
第一个参数 src 指原图像,原图像应该是灰度图。
第二个参数 thresh 指用来对像素值进行分类的阈值。
第三个参数 maxval 指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
第四个参数 type 指不同的阈值方法,这些方法包括:
假设下图为被处理的图像素值,破折线为将被阈值化的值;虚线为阈值 。
经过不同方法阈值化后示意图如下:
图(1)
大于阈值的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于阈值的像素点的灰度值设定为0。
图(2)
大于阈值的像素点的灰度值设定为0,而小于该阈值的设定为255。
图(3)
像素点的灰度值小于阈值不改变,大于阈值的灰度值的像素点就设定为该阈值。
图(4)
像素点的灰度值小于该阈值的不进行任何改变,而大于该阈值的部分,其灰度值全部变为0。
图(5)
像素点的灰度值大于该阈值的不进行任何改变,像素点的灰度值小于该阈值的,其灰度值全部变为0。
使用示例代码如下:
import cv2
import matplotlib.pyplot as plt
# 打开图片
img=cv2.imread('License-plate.jpg')
# 转为灰度图
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 二值化
# 参数:1.原图像, 2.进行分类的阈值,3.高于(低于)阈值时赋予的新值,4.方法选择参数
# 返回值:1.得到的阈值,2.阈值化后的图像
retval,threshold_img = cv2.threshold(gray_img,150,255,cv2.THRESH_BINARY)
cv2.imshow("img",img)
cv2.imshow("gray_img",gray_img)
cv2.imshow("threshold_img",threshold_img)
# 对灰度图进行直方图统计显示
plt.hist(gray_img.ravel(), 256, [0, 256])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
处理后的结果如下:
原图:
灰度图与二值化图:
阈值的选择可以通过观察灰度图的直方图来确定,该图像直方图如下:
可以明显看到是双波峰结构,这种就比较简单了,选取双波峰之间波谷部分的值就行,比如150。
但现实往往是复杂的比如下面这个图就无法使用上面这种简单阈值来处理了
该图整体偏暗,观察他的直方图可以发现,基本是集中在一起的。
对于这种图的处理就需要用到更高级的阈值化方法了,下面进行讲解。
顾名思义,这个处理用到的阈值是自适应的,而不是整张图共用一个固定值。当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
函数:cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
参数:
这种方法理论上得到的效果更好,相当于在动态自适应的调整属于自己像素点的阈值,而不是整幅图像都用一个阈值。
继续以上图为例,示例代码:
import cv2
import matplotlib.pyplot as plt
# 打开图片
img=cv2.imread('bookpage.jpg')
# 转为灰度图
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 二值化
# 参数:1.原图像, 2.进行分类的阈值,3.高于(低于)阈值时赋予的新值,4.方法选择参数
# 返回值:1.得到的阈值,2.阈值化后的图像
retval,threshold_img = cv2.threshold(gray_img,20,255,cv2.THRESH_BINARY)
# 自适应阈值
adaptive_threshold_img = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 35, 3)
cv2.imshow("img",img)
#cv2.imshow("gray_img",gray_img)
cv2.imshow("threshold_img",threshold_img)
cv2.imshow("adaptive_threshold_img",adaptive_threshold_img)
# 对灰度图进行直方图统计显示
#plt.hist(gray_img.ravel(), 256, [0, 256])
#plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答案就是不停的尝试。如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)呢?我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?这就是 Otsu 二值化要做的。简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法得到的结果可能会不理想)。
这里用到到的函数还是 cv2.threshold(),但是需要多传入一个参数(flag):cv2.THRESH_OTSU。
这时要把阈值设为 0。然后算法会找到最优阈值,这个最优阈值就是返回值 retVal。如果不使用 Otsu 二值化,返回的retVal 值与设定的阈值相等。
下面进行代码演示,仍然选取我们第一幅的车牌图像举例。第一种方法,设127 为全局阈值。第二种方法,直接使用 Otsu 二值化。第三种方法,先使用一个 5x5 的高斯核除去噪音,然后再使用 Otsu 二值化。
import cv2
img = cv2.imread('License-plate.jpg')
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 设127 为全局阈值
ret1,th1 = cv2.threshold(gray_img,127,255,cv2.THRESH_BINARY)
# Otsu 滤波
ret2,th2 = cv2.threshold(gray_img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print(ret2)
# 先使用一个 5x5 的高斯核除去噪音,然后再使用 Otsu 二值化
blur = cv2.GaussianBlur(gray_img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow("img",img)
cv2.imshow("th1",th1)
cv2.imshow("th2",th2)
cv2.imshow("th3",th3)
cv2.waitKey(0)
cv2.destroyAllWindows()