一、什么是二值图像
彩色图像:三个通道0-255,0-255,0-255,所以可以有2^24位空间
灰度图像:一个通道0-255,所以有256种颜色
二值图像:只有两种颜色,黑和白,1白色,0黑色
二、图像二值化
1、获取阈值
2、根据阈值去二值化图像方法
三、全局阈值
1、函数
threshold函数(固定阈值门限分割)
(1)OpenC的threshold函数进行全局阈值。
threshold(src, thresh, maxval, type[, dst]) -> retval, dst(返回的是阈值和全局阈值后的图像)
src参数表示输入图像(多通道,8位或32位浮点)。
thresh参数表示阈值。
maxval参数表示与THRESH_BINARY和THRESH_BINARY_INV阈值类型一起使用设置的最大值。
type参数表示阈值类型。
retval参数表示返回的阈值。若是全局固定阈值算法,则返回thresh参数值。若是全局自适应阈值算法,则返回自适应计算得出的合适阈值。
dst参数表示输出与src相同大小和类型以及相同通道数的图像。
(2)type参数阈值类型
这部分参考博客:https://blog.csdn.net/iracer/article/details/49232703 ,写的很不错。
阈值类型
原灰度图像的像素值:
1.THRESH_BINARY:过门限的值为最大值,其他值为0
2.THRESH_BINARY_INV:过门限的值为0,其他值为最大值
3.THRESH_TRUNC:过门限的值为门限值,其他值不变
4.THRESH_TOZERO:过门限的值不变,其他设置为0
5.THRESH_TOZERO_INV:过门限的值为0,其他不变
2、代码实现
要二值化图像,要先进行灰度化处理!!
1 2 3 4 import cv2 as cv 5 import numpy as np 6 7 #图像二值化 0白色 1黑色 8 #全局阈值 9 def threshold_image(image): 10 gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) 11 cv.imshow("input_gray_image", gray) 12 13 #threshold(InputArray src, OutputArray dst,double thresh, double maxVal, int thresholdType) 14 15 # 大律法,全局自适应阈值 参数0可改为任意数字但不起作用,gray是灰度图,像素值最大是255,所以写255 16 ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU) 17 print("threshold:%s" % ret) 18 cv.imshow("OTSU", binary) 19 20 # TRIANGLE法(三角形算法),,全局自适应阈值, 参数0可改为任意数字但不起作用,适用于单个波峰 21 ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE) 22 print("threshold:%s" % ret) 23 cv.imshow("TRIANGLE", binary) 24 25 # binary 自定义阈值为150,大于150的是白色 小于的是黑色 26 ret, binary = cv.threshold(gray, 150, 255, cv.THRESH_BINARY) 27 print("threshold:%s" % ret) 28 cv.imshow("diy150_binary", binary) 29 30 # binary_inv 自定义阈值为150,大于150的是黑色 小于的是白色 31 ret, binary = cv.threshold(gray, 150, 255, cv.THRESH_BINARY_INV) 32 print("threshold:%s" % ret) 33 cv.imshow("diy150_binary_inv", binary) 34 35 # trunc截断 大于150的是改为150 小于150的保留 36 ret, binary = cv.threshold(gray, 150, 255, cv.THRESH_TRUNC) 37 print("threshold:%s" % ret) 38 cv.imshow("diy150_trunc", binary) 39 40 # tozero 截断 小于150的是改为150 大于150的保留 41 ret, binary = cv.threshold(gray, 150, 255, cv.THRESH_TOZERO) 42 print("threshold:%s" % ret) 43 cv.imshow("diy150_tozero", binary) 44 45 src = cv.imread("1.jpg") 46 threshold_image(src) 47 cv.waitKey(0) 48 cv.destroyAllWindows()
threshold:115.0 threshold:126.0 threshold:150.0 threshold:150.0 threshold:150.0 threshold:150.0
(1)cv.THRESH_OTSU类型
推文:OTSU算法
我们自己不一定能够找到一个最好的阈值,去二分化图像,所以我们需要算法自己去寻找一个阈值,而cv.THRESH_OTSU就可以满足这个需求,去找到一个最好的阈值。
注意:他非常适用于图像灰度直方图具有双峰的情况,他会在双峰之间找到一个值作为阈值,对于非双峰图像,可能并不是很好用。
因为cv.THRESH_OTSU方法会产生一个阈值,那么函数cv2.threshold的的第二个参数(设置阈值)就是0(None)了,并且在cv2.threshold的方法参数中还得加上语句cv2.THRESH_OTSU。
(2)cv.THRESH_TRIANGLE类型
(有丢失)适用于单个波峰,最开始用于医学分割细胞等。
(3)cv.THRESH_OTSU类型和cv.THRESH_TRIANGLE类型
THRESH_OTSU和THRESH_TRIANGLE和前面的说到的二值化方法组合使用,好处是不用自己指定thresh值,系统会进行计算并且作为返回值返回。
区别是:
THRESH_OTSU最适用于双波峰
THRESH_TRIANGLE最适用于单个波峰
四、局部阈值(更清晰)
1、方法函数
(1)adaptiveThreshold方法
OpenCV的adaptiveThreshold函数进行局部阈值。
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)
src参数表示输入图像(8位单通道图像)。
maxValue参数表示使用 THRESH_BINARY 和 THRESH_BINARY_INV 的最大值.
adaptiveMethod参数表示自适应阈值算法,平均 (ADAPTIVE_THRESH_MEAN_C)或高斯(ADAPTIVE_THRESH_GAUSSIAN_C)。
thresholdType参数表示阈值类型,必须为THRESH_BINARY或THRESH_BINARY_INV的阈值类型。
blockSize参数表示块大小(奇数且大于1,比如3,5,7........ )。
C参数是常数,表示从平均值或加权平均值中减去的数。 通常情况下,这是正值,但也可能为零或负值。
在使用平均和高斯两种算法情况下,通过计算每个像素周围blockSize x blockSize大小像素块的加权均值并减去常量C即可得到自适应阈值。
如果使用平均的方法,则所有像素周围的权值相同;
如果使用高斯的方法,则每个像素周围像素的权值则根据其到中心点的距离通过高斯方程得到。
2、代码实现
(1)ADAPTIVE_THRESH_MEAN_C
1 def threshold_image(image): 2 gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) 3 cv.imshow("input_gray_image", gray) 4 dst = cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,25,10) 5 cv.imshow('local_threshold',dst) 6 src = cv.imread("1.jpg") 7 threshold_image(src) 8 cv.waitKey(0) 9 cv.destroyAllWindows()
(2)ADAPTIVE_THRESH_GAUSSIAN_C轮廓更加清晰
1 def threshold_image(image): 2 gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) 3 cv.imshow("input_gray_image", gray) 4 dst = cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,10) 5 cv.imshow('local_threshold',dst) 6 src = cv.imread("1.jpg") 7 threshold_image(src) 8 cv.waitKey(0) 9 cv.destroyAllWindows()
五、自己计算阈值
1、函数
reshape(a, newshape, order='C'):
numpy的reshape函数是给数组一个新的形状而不改变其数据,函数原型:reshape(a, newshape, order='C')
a参数表示需要重新形成的原始数组。
newshape参数表示int或int类型元组(tuple),若为(1, 3),表示生成的新数组是1行3列。
order参数表表示使用此索引顺序读取a的元素,并使用此索引顺序将元素放置到重新形成的数组中。
函数返回值:如果可能的话,这将是一个新的视图对象; 否则,它会成为副本。
2、代码实现
1 import cv2 as cv 2 import numpy as np 3 4 #图像二值化 0白色 1黑色 5 #全局阈值 6 def custom_threshold(image): 7 gray = cv.cvtColor(image,cv.COLOR_RGB2GRAY) #要二值化图像,要先进行灰度化处理 8 h,w=gray.shape[:2] #求宽高 9 m = np.reshape(gray,[1,w*h]) #将图像转一维数组,一行,w*h列,转换维度要保证其size不变 10 mean = m.sum() / (w*h) #求平均值来当做阈值,来分割图像 11 print("mean :",mean) 12 ret,binary = cv.threshold(gray,mean,255,cv.THRESH_BINARY) 13 cv.imshow("binary",binary) 14 src = cv.imread("1.jpg") 15 cv.imshow('input_iamge',src) 16 custom_threshold(src) 17 cv.waitKey(0) 18 cv.destroyAllWindows()
六、超大图像二值化和空白区域过滤
1、超大图像二值化方法
1.可以采用分块方法
2.先缩放处理就行二值化,然后还原大小
2、分块处理超大图像二值化问题
全局阈值处理每个分块,导致每个分块之间的图像差距较大,出现分块边界现象。
局部阈值处理每个分块,图像中没有明显的分块边界现象,因为是对每一个小块进行局部阈值二分,所以分块的差距并不大。所以局部阈值更好!
1 import cv2 as cv 2 import numpy as np 3 import matplotlib.pyplot as plt 4 from PIL import Image 5 6 def big_image_binary(image): 7 print(image.shape) ##超大图像,屏幕无法显示完整 8 block_w , block_h = 50,50 9 h,w = image.shape[:2] 10 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) 11 for row in range(0,h,block_h): 12 for col in range(0,w,block_w): 13 block = gray[row:row+block_h,col:col+block_w] #获取分块 14 # 对每一个小块全局阈值 15 #ret , binary = cv.threshold(block,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU) 16 # 对每一小块局部阈值 17 binary = cv.adaptiveThreshold(block,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,5) 18 gray[row:row+block_h,col:col+block_w] = binary #分块覆盖 19 print(np.std(binary),np.mean(binary)) 20 cv.imwrite('gray_threshold.jpg',gray) 21 cv.imshow('gray_threshold',gray) 22 img = cv.imread('1.jpg') 23 cv.imshow('1.jpb',img) 24 big_image_binary(img) 25 cv.waitKey(0) 26 cv.destroyAllWindows()
3、空白区域过滤
空白图像的过滤(当我们确认该区域为空白图像,可以不作处理,不进行二分处理)
我们可以将该空白区域(或者满足一定条件的区域),直接设置为0或者255或者其他想要获取的图像,不需要进行多余的阈值二分
(1)函数
numpy.std() 计算矩阵标准差
1 >>> a = np.array([[1, 2], [3, 4]]) 2 >>> np.std(a) # 计算全局标准差 3 1.1180339887498949 4 >>> np.std(a, axis=0) # axis=0计算每一列的标准差 5 array([ 1., 1.]) 6 >>> np.std(a, axis=1) # 计算每一行的标准差 7 array([ 0.5, 0.5])
numpy.mean求平均值
numpy.mean(a, axis=None, dtype=None, out=None, keepdims=False)
经常操作的参数为axis,以m * n矩阵举例:
axis 不设置值,对 m*n 个数求均值,返回一个实数
axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
(2)代码实现
1 def image_binary(image): 2 print(image.shape) 3 cw , ch = 100,100 4 h,w = image.shape[:2] 5 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) #要二值化图像,要先进行灰度化处理 6 cv.imshow('gray',gray) 7 for row in range(0,h,ch): 8 for col in range(0,w,cw): 9 block = gray[row:row+ch,col:col+cw] #获取分块 10 dev = np.std(block) 11 avg = np.mean(block) 12 if dev <15 and avg >200: #满足条件,接近空白区域,让他变黑 13 gray[row:row + ch, col:col + cw] = 0 #全部都赋值为0 14 else: 15 ret,binary = cv.threshold(block,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) 16 gray[row:row + ch, col:col + cw] = binary 17 print(np.std(binary),np.mean(binary)) 18 cv.imshow('gray_threshold_blank',gray) 19 cv.imwrite('gray_threshold_blank.jpg',gray) 20 21 img = cv.imread('1.jpg') 22 image_binary(img) 23 cv.waitKey(0) 24 cv.destroyAllWindows()