简介:本文详细介绍了 OpenCV 中多种阈值处理方法,包括cv2.threshold函数相关的二值化、反二值化、截断化、超阈值零处理、低阈值零处理等阈值处理方式,解析了阈值分割的作用以及函数返回阈值的原因。同时还涵盖了自适应阈值处理和 Otsu 阈值处理,通过多个实际代码案例展示了不同阈值处理方法在图像上的应用效果对比,帮助读者深入理解并掌握这些阈值处理技术在图像处理中的运用,便于根据实际需求选择合适的阈值处理手段。
如果你觉得我的文章对你有所帮助,希望你点赞收藏关注,持续学习OpenCV 相关的知识
这里写目录标题
1 threshold函数
1.1语法
line返回的阈值 , image = cv2.threshold(要进行阈值分割的图像src,要设定的阈值thresh,阈值分割的类型type,当type为THRESH_BINARY或者THRESH_BINARY_INV类型时,需要设定 的最大值maxval)
1.2为什么要进行阈值分割
作用有很多,这里讲两个:
- 减少数据量:一幅图像通常包含大量的像素信息。阈值分割可以将图像简化为二值图像(只有 0 和 1 两种像素值,分别代表背景和目标)。这样可以大大减少后续处理的数据量。例如,对于一个包含大量细节的自然场景图像,如果只关注图像中的主要物体轮廓,通过阈值分割将物体和背景分离,得到的二值图像可以在存储和处理时占用更少的资源。
- 形状分析:对于形状分析,阈值分割是第一步。将目标物体分割出来后,可以更好地计算物体的形状参数,如周长、面积、圆形度等。例如,在机械零件的形状检测中,先通过阈值分割得到零件的轮廓,然后分析其形状是否符合设计标准。
1.3解析为什么要返回阈值
学习,千万不能知其然,而不知其所以然,不能混日子.做事要用心,不能应付,多想想为什么。要想明白,多思考,对于这个函数:
line返回的阈值 , image = cv2.threshold(要进行阈值分割的图像src,要设定的阈值thresh,阈值分割的类型type,当type为THRESH_BINARY或者THRESH_BINARY_INV类型时,需要设定 的最大值maxval)
为什么要使用line接收他的阈值作为返回值呢?明明我们已经传入了阈值参数。所以我就十分好奇,去搜索了一番原因,有这么几条:
- 在自适应阈值分割中,你传入的初始阈值可能只是一个参考或者根本不被使用。函数会根据图像的局部特征(如局部均值、局部高斯加权均值等)来计算每个像素位置合适的阈值。在这种情况下,返回的阈值可以让你了解函数实际使用的阈值是多少。例如,在处理光照不均匀的图像时,自适应阈值分割会根据图像不同区域的亮度自动调整阈值。返回的阈值可以帮助你分析这些调整后的阈值是否合理,是否符合图像的实际情况。
- 即使你传入了一个固定的阈值,返回这个阈值也有好处。在进行多次阈值分割实验或者在一个处理流程中需要记录阈值信息时,你可以方便地获取并存储这个阈值。假设你正在尝试不同的阈值来优化目标提取的效果,返回的阈值可以让你记录每次使用的阈值和对应的分割结果,以便后续比较和分析,找到最佳的阈值设置。
- 这个返回的阈值可以作为其他函数的输入或者参考。例如,在一些高级的图像处理算法中,可能会根据阈值分割得到的阈值进一步调整图像的其他参数。或者在图像质量评估模块中,返回的阈值可以作为评估阈值分割效果的一个参考因素,帮助判断阈值是否合适,是否导致了过度分割或者分割不足的情况。
1.4 二值化阈值处理(cv2.THRESH_BINARY)
line返回的阈值 , image = cv2.threshold(要进行阈值分割的图像src,要设定的阈值thresh,阈值分割的类型type,当type为THRESH_BINARY或者THRESH_BINARY_INV类型时,需要设定 的最大值maxval)
顾名思义,二值化阈值处理type = cv2.THRESH_BINARY,就是把大于阈值的像素点统统设置为最大值maxval
这是我们文件夹的结构(我用的是jupyter notebooks,你们用pycharm也是一样的,都把图片文件和代码放在同一个文件夹下就可以了)
我是要把这个名字为pig.jpg的图片进行二值化阈值处理
import numpy as np
import cv2
red_pig = cv2.imread("pig.jpg",0)
line , bin_image = cv2.threshold(src = red_pig , thresh = 127 ,type = cv2.THRESH_BINARY,maxval = 255)
cv2.imshow("original",red_pig)
cv2.imshow("binary",bin_image)
cv2.waitKey()
cv2.destroyAllWindows()
我们可以观察到一些灰度值小于等于阈值的点变成了0纯白色,其他的变成了255黑色
1.5 反二值化阈值处理(cv2.THRESH_BINARY_INV)
他是跟二值化阈值处理相反的,凡是大于阈值的都被处理为0白色,小于等于阈值的被处理为黑色,在下面这代码中,同时展现了二值化与反二值化给大家看:
import numpy as np
import cv2
red_pig = cv2.imread("pig.jpg",0)
line , bin_image = cv2.threshold(src = red_pig , thresh = 127 ,type = cv2.THRESH_BINARY,maxval = 255)
line_inv , bin_image_inv = cv2.threshold(src = red_pig , thresh = 127 ,type = cv2.THRESH_BINARY_INV,maxval = 255)
cv2.imshow("original",red_pig)
cv2.imshow("binary",bin_image)
cv2.imshow("INV_bin",bin_image_inv)
cv2.waitKey()
cv2.destroyAllWindows()
从左往右第二张图和第三张图恰好是相反的,前者黑的后者是白的,前者白的后者是黑的
1.6 截断化阈值处理
大于阈值的被处理为阈值,小于阈值的不变
import numpy as np
import cv2
red_pig = cv2.imread("pig.jpg",0)
line ,trunc_image = cv2.threshold(src = red_pig,thresh =127,maxval = 255,type = cv2.THRESH_TRUNC)
print(trunc_image)
1.7 超阈值零处理(cv2.THRESH_TOZERO_INV)与低阈值零处理(cv2.THRESH_TOZERO)
超阈值零处理就是把大于阈值的部分按0处理,小于阈值的部分不变
低阈值零处理就是把小于阈值的部分按0处理,大于阈值的部分不变。
下面这个案例对pig.jpg这张图片分别进行两种零处理,比对效果差别:
import numpy as np
import cv2
red_pig = cv2.imread("pig.jpg",0)
line , ZERO_image = cv2.threshold(src = red_pig , thresh = 127 ,type = cv2.THRESH_TOZERO,maxval = 255)
line_inv , ZERO_image_inv = cv2.threshold(src = red_pig , thresh = 127 ,type = cv2.THRESH_TOZERO_INV,maxval = 255)
cv2.imshow("original",red_pig)
cv2.imshow("ZERO",ZERO_image)
cv2.imshow("INV_ZERO",ZERO_image_inv)
cv2.waitKey()
cv2.destroyAllWindows()
2 自适应阈值处理
自适应阈值处理,适合处理一些明暗差异较大的图像,手动设置阈值不好设置,所以使用自适应比较方便
功能是根据邻域的加权平均值生成阈值
,先讲语法:
adapt_image 阈值处理后的图像 = cv2.adaptiveThreshole(原始图像src,maxValue最大值,adaptiveMethod代表自适应方法(包括均值和高斯两种),阈值处理方式(只能选cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV,邻域大小blocksize))
我们将在下面的案例中用
我从网上找了一张明暗差异较大的图像,起名black_and_white.JPG存入我的文件夹下:
在下面这段代码中展示普通阈值处理 与两种自适应阈值处理的效果差别:
import numpy as np
import cv2
black_white = cv2.imread("black_and_white.JPG",0)
line , bin_image = cv2.threshold(black_white,127,255,cv2.THRESH_BINARY)
Mean = cv2.adaptiveThreshold(black_white,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,3)
Gaussian = cv2.adaptiveThreshold(black_white,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,3)
cv2.imshow("original",black_white)
cv2.imshow("bin",bin_image)
cv2.imshow("mena",Mean)
cv2.imshow("Gauss",Gaussian)
cv2.waitKey()
cv2.destroyAllWindows()
我们可以看到自适应阈值处理的效果明显由于普通的阈值处理
3 Otsu阈值处理
在普通的阈值处理的type加一个参数+cv2.THRESH_OTSU
他就可以自己遍历,找最合适的阈值
请看下面这段代码实例,还用black_and_white.JPG图片做案例:
import numpy as np
import cv2
black_white = cv2.imread("black_and_white.JPG",0)
line , bin_image = cv2.threshold(black_white,127,255,cv2.THRESH_BINARY)
line_otsu,otsu_bin = cv2.threshold(black_white,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow("original",black_white)
cv2.imshow("bin",bin_image)
cv2.imshow("otsu",otsu_bin)
cv2.waitKey()
cv2.destroyAllWindows()
通过结果可以看出来otsu明显获得了更多的轮廓信息
致谢
本文参考了一些博主的文章,博取了他们的长处,也结合了我的一些经验,对他们表达诚挚的感谢,使我对阈值处理 的使用有更深入的了解,也推荐大家去阅读一下他们的文章。纸上学来终觉浅,明知此事要躬行:
9.3Otsu阈值分割
OpenCV阈值处理(threshold函数、自适应阈值处理、Otsu处理)