OPENCV(python)--初学之路(四)
本文介绍了图像处理中的三种阈值化方法:1)简单阈值法,通过固定阈值将灰度图二值化;2)自适应阈值法,针对不同光照区域采用不同阈值;3)Otsu二值化,自动计算双峰图像的最佳阈值。文章通过Python+OpenCV代码演示了各种方法的实现效果,并比较了不同参数下的处理结果。最后作者提醒在专注学习之余也要适当放松,享受生活。
一.前言
今天是周五,我需要放松。。玩了一半又回来加班。所以今天的内容也不会太多。
二.主要内容
一图像阈值
1简单阈值法
此方法是直截了当的。如果像素值大于阈值,则会被赋为一个值(可能为白色),否则会赋为另一个值(可能为黑色)。使用的函数是 cv.threshold(retval, dst = cv.threshold(src, thresh, maxval, type[, dst]))
。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是 maxval,它表示像素值大于(有时小于)阈值时要给定的值。opencv 提供了不同类型的阈值,由函数的第四个参数决定。不同的类型有:
获得两个输出。第一个是 retval(后文会解释),稍后将解释。第二个输出是我们的阈值图像。
演示代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 读取图像
img = cv.imread('test.jpg', 0)#这里的图片名需要与自己文件夹中名称一致
# 应用不同阈值处理方法
ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
ret, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)
# 设置标题和图像列表
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
# 显示图像
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
结果如下:

2自适应阈值
但在图像在不同区域具有不同照明条件的条件下,这可能不是很好。在这种情况下,我们采用自适应阈值。在此,算法计算图像的一个小区域的阈值。因此,我们得到了同一图像不同区域的不同阈值,对于不同光照下的图像,得到了更好的结果。
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst])
参数详解
-
src: 输入图像(必须为灰度图)
-
maxValue: 满足条件时赋予的像素值
-
adaptiveMethod: 自适应方法
-
cv2.ADAPTIVE_THRESH_MEAN_C: 邻域均值 -
cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 邻域高斯加权和
-
-
thresholdType: 阈值类型(只能是
THRESH_BINARY或THRESH_BINARY_INV) -
blockSize: 邻域大小(必须是奇数)
-
C: 从均值或加权均值中减去的常数
大家可以试一试改变 blockSize的值(只能说奇数并且大于1)看看变化规律
3Otsu 二值化
参数 retval,当我们进行 Otsu 二值化时,它的用途就来了在全局阈值化中,我们使用一个任意的阈值,对吗?那么,我们如何知道我们选择的值是好的还是不好的呢?答案是,试错法。但是考虑一个双峰图像(简单来说,双峰图像是一个直方图有两个峰值的图像)。对于那个图像,我们可以近似地取这些峰值中间的一个值作为阈值,对吗?这就是 Otsu 二值化所做的。所以简单来说,它会自动从双峰图像的图像直方图中计算出阈值。(对于非双峰图像,二值化将不准确。)
为此,我们使用了 cv.threshold 函数,但传递了一个额外的符号 cv.THRESH_OTSU
retval, dst = cv.threshold(src, thresh, maxval, type[, dst])
参数说明
-
src: 输入图像(必须是灰度图)
-
thresh: 阈值
-
maxval: 最大值(当像素值超过阈值时赋予的值)
-
type: 阈值化类型
-
dst: 输出图像(可选)
测试代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def create_noisy_image():
"""创建带有高斯噪声的测试图像"""
img = np.zeros((300, 300), dtype=np.uint8)
cv.rectangle(img, (0, 0), (150, 300), 100, -1)
cv.rectangle(img, (150, 0), (300, 300), 150, -1)
noise = np.random.normal(0, 25, (300, 300)).astype(np.uint8)
noisy_img = cv.add(img, noise)
return noisy_img
img = cv.imread('noisy2.png', 0)
if img is None:
print("noisy2.png 文件不存在,创建测试图像...")
img = create_noisy_image()
cv.imwrite('noisy2.png', img)
print("已创建测试图像: noisy2.png")
ret1, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret2, th2 = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
blur = cv.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.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"]
for i in range(3):
plt.subplot(3, 3, i*3+1)
plt.imshow(images[i*3], 'gray')
plt.title(titles[i*3])
plt.xticks([])
plt.yticks([])
plt.subplot(3, 3, i*3+2)
plt.hist(images[i*3].ravel(), 256)
plt.title(titles[i*3+1])
plt.xticks([])
plt.yticks([])
plt.subplot(3, 3, i*3+3)
plt.imshow(images[i*3+2], 'gray')
plt.title(titles[i*3+2])
plt.xticks([])
plt.yticks([])
plt.tight_layout()
plt.show()
print("阈值结果比较:")
print(f"全局阈值: {ret1}")
print(f"Otsu 阈值: {ret2}")
print(f"高斯滤波后的 Otsu 阈值: {ret3}")
这个代码我通过ds修改了一下,以便能直接看到结果,比较直观。
三.最后一语
今天就先学这么多,我们的生活不只有学习,我们也应该出门走走,毕竟江山如此多娇。
感谢观看!!共勉。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)