15.1 图像二值化原理
二值化核心思想,设阈值,大于阈值的为0(黑色)或 255(白色),使图像称为黑白图。
阈值可固定,也可以自适应阈值。
自适应阈值一般为一点像素与这点为中序的区域像素平均值或者高斯分布加权和的比较,其中可以设置一个差值也可以不设置。
图像的阈值化旨在提取图像中的目标物体,将背景以及噪声区分开来。通常会设定一个阈值T,通过T将图像的像素分为两类:大于T的像素群和小于T的像素群。
灰度转换处理后的图像中,每个像素都只有一个灰度值,其大小表示明暗程度。所谓图像的二值化 ,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体。
常用的二值化算法下所示:
Y=0, gray<TY=1, gray≥T
当灰度Gray小于阈值T的时候,其像素设置为0,表示黑色;当灰度Gray大于或等于阈值T时,其Y值为255,表示白色。
全局阈值就是一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体;常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。
局部阈值就是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
二值化处理广泛应用于各行各业,比如生物学中的细胞图分割,交通领域的车牌设计等。在文化应用领域中,通过二值化处理将所需民族文物图像转换为黑白两色图,从而为后面的图像识别提供更好的支撑作用。
15.2 cv2.threshold() 简单阈值处理(全局阈值)
原型:ret,dest=threshold(src, thresh, maxval, type, dst=None)
参数
:
- src 指原图像,原图像应该是灰度图,只能输入单通道图像
- thresh 指用来对像素值进行分类的阈值
- maxval 指当像素值高于(有时是小于,根据 type 来决定)阈值时应该被赋予的新的像素值,在二元阈值THRESH_BINARY和逆二元阈值THRESH_BINARY_INV中使用的最大值
- type 指不同的不同的阈值方法,这些方法包括以下五种类型(0,1,2,3,4):cv2.THRESH_BINARY 二进制阈值化。超过阈值部分取 maxval(最大值),否则取 0
在运用该阈值类型的时候,先要选定一个特定的阈值量,比如:125,这样,新的阈值产生规则可以解释为大于125的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于125的像素点的灰度值设定为0。
;cv2.THRESH_BINARY_INV 反二进制阈值化
该阈值化与二进制阈值化相似,先选定一个特定的灰度值作为阈值,不过最后的设定值相反。(在8位灰度图中,例如大于阈值的设定为0,而小于该阈值的设定为255)。
;cv2.THRESH_TRUNC 截断阈值化,大于阈值部分设为阈值,否则不变
同样首先需要选定一个阈值,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变。(例如:阈值选取为125,那小于125的阈值不改变,大于125的灰度值(230)的像素点就设定为该阈值)。
;cv2.THRESH_TOZERO 阈值化为零,大于阈值部分不改变,否则设为零
先选定一个阈值,然后对图像做如下处理:1 像素点的灰度值大于该阈值的不进行任何改变;2 像素点的灰度值小于该阈值的,其灰度值全部变为0。
;cv2.THRESH_TOZERO_INV 反阈值化为零
原理类似于0阈值,但是在对图像做处理的时候相反,即:像素点的灰度值小于该阈值的不进行任何改变,而大于该阈值的部分,其灰度值全部变为0。
示例
import cv2
import numpy as np
from matplotlib import pyplot as plt
def parse_thresh(path):
imgCV = cv2.imread(path) # 读取原始照片
img =cv2.merge((imgCV[:,:,2],imgCV[:,:,1],imgCV[:,:,0]))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
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 = ['Origin Image', 'gray', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV'] # 标题
images = [img, gray, thresh1, thresh2, thresh3, thresh4, thresh5] # 对应的图
plt.figure(figsize=(20,6.4))
for i in range(7): # 画7次图
plt.subplot(2, 4, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i],fontsize=30)
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == '__main__':
path = 'C:\\Users\\xxx\\Downloads\\picture1.jpeg'
parse_thresh(path)
15.3 cv2.adaptiveThreshold()
自适应阈值处理(局部阈值)
原型:cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, param1, dst=None)
参数
:
- 第一个参数 src 指原图像,原图像应该是灰度图。
- 第二个参数 x 指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
- 第三个参数 adaptive_method 参数为:CV_ADAPTIVE_THRESH_MEAN_C、CV_ADAPTIVE_THRESH_GAUSSIAN_C
- 第四个参数 threshold_type 指取阈值类型:必须是下者之一:CV_THRESH_BINARY、CV_THRESH_BINARY_INV
- 第五个参数 block_size 指用来计算阈值的象素邻域大小: 3, 5, 7, …
- 第六个参数 param1 指与方法有关的参数。
- 对CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数, 尽管它可以是负数。
- 对方法CV_ADAPTIVE_THRESH_MEAN_C,先求出块中的均值,再减掉param1。
- 对方法 CV_ADAPTIVE_THRESH_GAUSSIAN_C ,先求出块中的加权和(gaussian), 再减掉param1。
示例
import cv2
from matplotlib import pyplot as plt
#详细说明参考上方例子
img = cv2.imread('C:\\Users\\xxx\\Downloads\\picture1.jpeg',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]
plt.figure(figsize=(32,4.8))
for i in range(4):
plt.subplot(1,4,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i],fontsize=24)
plt.xticks([]),plt.yticks([])
plt.show()
if __name__ == '__main__':
path = 'C:\\Users\\xxx\\Downloads\\picture1.jpeg'
parse_thresh(path)
综合示例
import cv2 as cv
#全局阈值
def threshold_demo(image):
gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) #把输入图像灰度化
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) #直接阈值化是对输入的单通道矩阵逐像素进行阈值分割。
#print("threshold value %s"%ret)
cv.namedWindow("threshold", cv.WINDOW_NORMAL)
cv.imshow("threshold", binary)
#局部阈值
def local_threshold(image):
gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) #把输入图像灰度化
binary = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY, 25, 10) #自适应阈值化能够根据图像不同区域亮度分布,改变阈值
cv.namedWindow("adaptiveThreshold", cv.WINDOW_NORMAL)
cv.imshow("adaptiveThreshold", binary)
src = cv.imread('C:\\Users\\xxx\\Downloads\\picture1.jpeg')
cv.namedWindow('input_image', cv.WINDOW_NORMAL) #设置为WINDOW_NORMAL可以任意缩放
cv.imshow('input_image', src) #源图
threshold_demo(src) #全局
local_threshold(src) #局部
cv.waitKey(0)
cv.destroyAllWindows()
15.4 Qtsu二值化
在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答案就是不停的尝试。如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)呢?我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?这就是 Otsu 二值化要做的。简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法得到的结果可能会不理想)。
这里用到到的函数还是 cv2.threshold(),但是需要多传入一个参数(flag):cv2.THRESH_OTSU。
这时要把阈值设为 0。然后算法会找到最优阈值,这个最优阈值就是返回值 retVal。如果不使用 Otsu 二值化,返回的retVal 值与设定的阈值相等。
示例
下面的例子中,输入图像是一副带有噪声的图像。第一种方法,设127 为全局阈值。第二种方法,直接使用 Otsu 二值化。第三种方法,先使用一个 5x5 的高斯核除去噪音,然后再使用 Otsu 二值化。
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C:\\Users\\xxx\\Downloads\\picture1.jpeg',0)
# 设127 为全局阈值
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# 直接使用 Otsu 二值化
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# 先使用一个 5x5 的高斯核除去噪音,然后再使用 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 Noisy Image','Histogram','Global Thresholding (v=127)',
'Original Noisy Image','Histogram',"Otsu's Thresholding",
'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
plt.figure(figsize=(19.2,14.4))
for i in range(3):
plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
plt.title(titles[i*3],fontsize=30), plt.xticks([]), plt.yticks([])
plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)
plt.title(titles[i*3+1],fontsize=30), 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=30), plt.xticks([]), plt.yticks([])
plt.show()