在很多时候,我们想要一种处理方式:设定一个值,低于这个值的像素全都变为0,高于这个值的像素全都设置为255, 这就是阈值处理。
Threshold()
opencv中有一个cv2.threshold()函数,语法格式为:
retval, dst = cv2.threshold(src, thresh, maxval, type)
- retval代表返回的阈值。
- dst代表阈值分割结果图像,与原始图像具有相同的大小和类型。
- src代表要进行阈值分割的图像,可以是多通道的,8位或32位浮点型数值。
- thresh代表要设定的阈值。
- maxval代表当type参数为THRESH_BINARY或者THRESH_BINARY_INV类型时,需要设定的最大值。
-
type代表阈值分割的类型,具体类型值如图
下面就把各种类型举例说明一下
二值化阈值处理(cv2.THRESH_BINARY)
该类型的规则为(表达式在类型图中有展示,就不再贴图了)
- 对于灰度值大于阈值thresh的像素点,将其灰度值设定为最大值。
- 对于灰度值小于或等于阈值thresh的像素点,将其灰度值设定为0。
例如我们对一幅灰度图像进行操作,设定127为阈值:
import cv2
Bastion = cv2.imread("Bastion2.jpg") # 读取彩色图像
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY) # 把图像转换成灰度图像
# t为阈值,Bastion_thresh_binary为得到的二值图像,127是设定的阈值,
# 255是选择了cv2.THRESH_BINARY这个参数时,需要设定的最大值
t, Bastion_thresh_binary = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_BINARY)
print("阈值为:", t)
cv2.imshow("gray", Bastion_gray)
cv2.imshow("thresh", Bastion_thresh_binary)
cv2.waitKey()
cv2.destroyAllWindows()
得到两幅图像是这样的:(0像素为黑色,255是白色)
反二值化阈值处理(cv2.THRESH_BINARY_INV)
反二值化阈值处理,顾名思义,和上面那个类型相反呗,规则是这样的:
- 对于灰度值大于阈值的像素点,将其值设定为0。
- 对于灰度值小于或等于阈值的像素点,将其值设定为255
直接示例咯,还是以127为阈值,这样方便和上面的图对比,且只需要把类型参数从cv2.THRESH_BINARY改为cv2.THRESH_BINARY_INV
import cv2
Bastion = cv2.imread("Bastion2.jpg")
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY)
# 这里cv2.THRESH_BINARY改为了cv2.THRESH_BINARY_INV
t, Bastion_thresh = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_BINARY_INV)
print("阈值为:", t)
cv2.imshow("gray", Bastion_gray)
cv2.imshow("thresh", Bastion_thresh)
cv2.waitKey()
cv2.destroyAllWindows()
结果是这样的,正好相反
截断阈值化处理(cv2.THRESH_TRUNC)
该类型的规则为:
- 对于像素值大于阈值的像素点,其像素值将被设定为阈值。
- 对于像素值小于或等于阈值的像素点,其像素值将保持不变。
直接上代码示例,仍然以127作为阈值
import cv2
Bastion = cv2.imread("Bastion2.jpg")
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY)
t, Bastion_thresh = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_TRUNC) # 仅修改了参数,其他没有变化
print("阈值为:", t)
cv2.imshow("gray", Bastion_gray)
cv2.imshow("thresh", Bastion_thresh)
cv2.waitKey()
cv2.destroyAllWindows()
结果:
大于阈值的点因该是更亮的(255是白色),该类型把大于阈值的点修改为阈值,所以结果图像变得灰暗
超阈值零处理(cv2.THRESH_TOZERO_INV)
截断阈值化处理(cv2.THRESH_TRUNC)是把大于阈值的点改成阈值,而超阈值零处理是把超过阈值的点改成0,规则:
- 对于像素值大于阈值的像素点,其像素值将被处理为0。
- 对于像素值小于或等于阈值的像素点,其像素值将保持不变。
仍然选择127为阈值,我们来和截断阈值化处理的结果做对比
import cv2
Bastion = cv2.imread("Bastion2.jpg")
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY)
t, Bastion_thresh = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_TOZERO_INV)
print("阈值为:", t)
cv2.imshow("gray", Bastion_gray)
cv2.imshow("thresh", Bastion_thresh)
cv2.waitKey()
cv2.destroyAllWindows()
上个例子把高于127的点变成127,就是亮的部分变暗;这回直接把亮的部分变0,也就是变黑,对比很显然。
低阈值零处理(cv2.THRESH_TOZERO)
与超阈值零处理(cv2.THRESH_TOZERO_INV)相反,规则:
- 对于像素值大于阈值的像素点,其值将保持不变。
-
对于像素值小于或等于阈值的像素点,其值将被处理为0。
代码就不贴了,修改参数即可,结果如图:
小于127的点全都变成黑色的了。
Otsu处理
前面我们都是自己设定一个阈值127,作为举例。但是有时候图像色彩不是很均衡的话,用127就会显得不合适,实际处理的图像往往是很复杂的,不太可能一眼就观察出最合适的阈值。如果一个个去尝试,工作量无疑是巨大的。
Otsu方法能够根据当前图像给出最佳的类间分割阈值。简而言之,Otsu方法会遍历所有可能阈值,从而找到最佳的阈值。
通过在函数cv2.threshold()中对参数type的类型多传递一个参数“cv2.THRESH_OTSU”,即可实现Otsu方式的阈值分割。在使用Otsu方法时,要把阈值设为0。此时的函数cv2.threshold()会自动寻找最优阈值,并将该阈值返回。
示例:
import cv2
Bastion = cv2.imread("Bastion2.jpg") # 读取彩色图像
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY) # 把图像转换成灰度图像
# 设置阈值为127
t1, Bastion_binary = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_BINARY)
# 阈值设置为0,载添加一个otsu参数
t2, Bastion_otsu = cv2.threshold(Bastion_gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print("阈值(127):", t1)
print("阈值(otsu):", t2)
cv2.imshow("binary", Bastion_binary)
cv2.imshow("otsu", Bastion_otsu)
cv2.waitKey()
cv2.destroyAllWindows()
otsu方法找到的阈值和我们自己设定的不一样:
图像也就有一些差异了:
自适应阈值处理
介绍一个函数cv2.adaptiveThreshold(),是自适应处理的。
对于色彩均衡的图像,直接使用一个阈值就能完成对图像的阈值化处理。但是,有时图像的色彩是不均衡的,此时如果只使用一个阈值,就无法得到清晰有效的阈值分割结果图像。有一种改进的阈值处理技术,其使用变化的阈值完成对图像的阈值处理,这种技术被称为自适应阈值处理。在进行阈值处理时,自适应阈值处理的方式通过计算每个像素点周围临近区域的加权平均值获得阈值,并使用该阈值对当前像素点进行处理。与普通的阈值处理方法相比,自适应阈值处理能够更好地处理明暗差异较大的图像。
函数语法格式与上面介绍的函数类似
dst = cv2.adaptiveThreshold(src, maxValue, adptiveMethod, thresholdType, blockSize, c)
# dst 结果图像
# src 需要处理的原图像
# maxValue 最大值
# adptiveMethod 自适应方法
# thresholdtype 阈值处理方式,该值必须是cv2.THRESH_BINARY 或者cv2.THRESH_BINARY_INV中的一个。
# blocksize 块大小。表示一个像素在计算其阈值时所使用的邻域尺寸,通常为3、5、7等。
# c 常量
函数cv2.adaptiveThreshold()根据参数adaptiveMethod来确定自适应阈值的计算方法,函数包含cv2.ADAPTIVE_THRESH_MEAN_C和cv2.ADAPTIVE_THRESH_GAUSSIAN_C两种不同的方法。这两种方法都是逐个像素地计算自适应阈值,自适应阈值等于每个像素由参数blockSize所指定邻域的加权平均值减去常量C。两种不同的方法在计算邻域的加权平均值时所采用的方式不同:
- cv2.ADAPTIVE_THRESH_MEAN_C:邻域所有像素点的权重值是一致的。
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C:与邻域各个像素点到中心点的距离有关,通过高斯方程得到各个点的权重值。
示例
import cv2
Bastion = cv2.imread("Bastion2.jpg")
Bastion_gray = cv2.cvtColor(Bastion, cv2.COLOR_BGR2GRAY)
t, Bastion_thresh = cv2.threshold(Bastion_gray, 127, 255, cv2.THRESH_BINARY)
Bastion_adaptiveThresh_MEAN_C = cv2.adaptiveThreshold(Bastion_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 3)
Bastion_adaptiveThresh_GAUSSIAN_C = cv2.adaptiveThreshold(Bastion_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 3)
cv2.imshow("RBG",Bastion)
cv2.imshow("gray", Bastion_gray)
cv2.imshow("thresh_binary", Bastion_thresh)
cv2.imshow("MEAN",Bastion_adaptiveThresh_MEAN_C)
cv2.imshow("GAUSSIAN_C",Bastion_adaptiveThresh_GAUSSIAN_C)
cv2.waitKey()
cv2.destroyAllWindows()
结果,分别是原图,灰度图,二值化阈值处理(cv2.THRESH_BINARY),自适应的两个参数的处理:
通过对比普通的阈值处理与自适应阈值处理可以发现,自适应阈值处理保留了更多的细节信息。在一些极端情况下,普通的阈值处理会丢失大量的信息,而自适应阈值处理可以得到效果更好的二值图像。
参考:
《OpenCV轻松入门:面向python》