当前位置: 首页 > news >正文

OpenCV 图像预处理核心技术:阈值处理与滤波去噪

图像预处理是计算机视觉任务的关键步骤,其目标是优化图像质量、突出有效信息,为后续的边缘检测、目标识别等操作奠定基础。本文将通过实战代码,系统解析图像阈值处理(全局阈值、自适应阈值、Otsu 阈值)和图像滤波去噪(均值、高斯、中值、双边滤波)两大核心技术,帮助你理解不同场景下的预处理方案选择。

一、图像阈值处理:分割前景与背景

阈值处理是通过设定像素值阈值,将灰度图像(或彩色图像的单通道)转换为二值图像(仅黑白两色),从而分割出前景目标与背景。OpenCV 提供了三类阈值处理方法,分别应对不同的图像条件(如光照均匀 / 不均、有无噪声)。

1. 全局阈值:固定阈值分割(适用于光照均匀图像)

全局阈值使用单一固定阈值对整幅图像进行分割,操作简单但对光照不均的图像效果较差。OpenCV 支持 5 种全局阈值类型,核心是通过cv2.threshold()实现。

代码实现:5 种全局阈值类型对比
import cv2
import numpy as np
from matplotlib import pyplot as plt# 读取图像(注意:阈值处理通常基于灰度图,此处直接用彩色图会对三通道分别处理)
img = cv2.imread('ocv01.jpg')
# 转换为灰度图(推荐做法,避免彩色通道干扰)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 全局阈值处理:参数依次为(输入图,阈值,最大值,阈值类型)
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)       # 二值化:>127→255(白),<127→0(黑)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)   # 反二值化:>127→0,<127→255
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)        # 截断:>127→127,<127不变
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)       # 归零:>127不变,<127→0
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)   # 反归零:>127→0,<127不变# 显示结果
titles = ['Original Gray Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img_gray, thresh1, thresh2, thresh3, thresh4, thresh5]for i in range(6):plt.subplot(2, 3, i+1)plt.imshow(images[i], cmap='gray')  # 灰度模式显示plt.title(titles[i])plt.xticks([]), plt.yticks([])  # 隐藏坐标轴plt.show()
关键参数与类型解析
阈值类型作用逻辑适用场景
cv2.THRESH_BINARY像素值 > 阈值 → 最大值(255),否则 → 0分割亮前景(如白字黑底)
cv2.THRESH_BINARY_INV像素值 > 阈值 → 0,否则 → 最大值(255)分割暗前景(如黑字白底)
cv2.THRESH_TRUNC像素值 > 阈值 → 阈值(127),否则不变降低亮区域对比度
cv2.THRESH_TOZERO像素值 > 阈值 → 不变,否则 → 0保留亮目标,抑制暗噪声
cv2.THRESH_TOZERO_INV像素值 > 阈值 → 0,否则 → 不变保留暗目标,抑制亮噪声
  • ret:函数返回的阈值(此处为 127,自定义时与输入阈值一致;自动阈值方法中为计算出的最优阈值)。
  • 注意:彩色图像直接阈值处理会对 B、G、R 三通道分别分割,可能导致颜色异常,因此推荐先转换为灰度图

2. 自适应阈值:局部动态阈值(适用于光照不均图像)

全局阈值的缺陷是:当图像光照不均(如局部阴影、明暗差异大)时,单一阈值无法兼顾所有区域。自适应阈值通过局部区域的像素均值 / 高斯加权均值动态计算每个区域的阈值,解决光照不均问题。

代码实现:自适应阈值 vs 全局阈值
import cv2
import numpy as np
from matplotlib import pyplot as plt# 读取图像并转换为灰度图
img = cv2.imread('ocv01.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 中值滤波预处理(降低噪声对阈值的影响)
img_gray = cv2.medianBlur(img_gray, 5)# 1. 全局阈值(对比用)
ret, th_global = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)# 2. 自适应阈值:参数依次为(输入图,最大值,自适应方法,阈值类型,块大小,常数C)
# 方法1:局部均值自适应(块内像素均值 - C 作为阈值)
th_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,  # 自适应方法:局部均值cv2.THRESH_BINARY,           # 阈值类型11, 2                        # 块大小(奇数),常数C(均值减去C)
)# 方法2:局部高斯加权均值自适应(块内像素高斯加权均值 - C 作为阈值)
th_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,  # 自适应方法:高斯加权均值cv2.THRESH_BINARY, 11, 2
)# 显示对比
titles = ['Original Gray Image', 'Global Threshold (v=127)', 'Adaptive Mean Threshold', 'Adaptive Gaussian Threshold'
]
images = [img_gray, th_global, th_mean, th_gaussian]for i in range(4):plt.subplot(2, 2, i+1)plt.imshow(images[i], cmap='gray')plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()
核心参数解析
  • 块大小(11):计算局部阈值的区域大小,必须为奇数(确保中心像素存在);块越大,阈值越平滑,反之越灵敏。
  • 常数 C:从局部均值 / 高斯均值中减去的数值,用于调整阈值高低(C 为正则阈值降低,更多像素被判定为白色;C 为负则阈值升高)。
  • 自适应方法对比
    • ADAPTIVE_THRESH_MEAN_C:计算速度快,但对噪声敏感。
    • ADAPTIVE_THRESH_GAUSSIAN_C:对噪声更鲁棒(高斯加权突出中心像素),但计算稍慢,适合噪声较多的图像。

3. Otsu 阈值:自动最优阈值(适用于噪声图像)

当图像灰度直方图呈现明显双峰分布(前景与背景像素值集中在两个区间)时,Otsu 阈值能自动计算出 “分离两个峰” 的最优阈值,无需手动调整。常与高斯滤波结合,降低噪声对直方图的干扰。

代码实现:Otsu 阈值(含噪声处理)
import cv2
import numpy as np
from matplotlib import pyplot as plt# 读取含噪声的图像并转换为灰度图
img_noisy = cv2.imread('noise.jpg')
img_gray = cv2.cvtColor(img_noisy, cv2.COLOR_BGR2GRAY)# 1. 普通全局阈值(手动设127,效果差)
ret1, th1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)# 2. Otsu阈值(自动计算最优阈值,无需手动设阈值)
# 阈值设0,通过cv2.THRESH_OTSU标志自动计算ret2(最优阈值)
ret2, th2 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 3. 高斯滤波+Otsu阈值(先去噪,再优化阈值)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 0)  # 5x5高斯滤波
ret3, th3 = cv2.threshold(img_blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 显示结果(含直方图,直观展示双峰分布)
titles = ['Original Noisy Image', 'Histogram', 'Global Threshold (v=127)','Original Noisy Image', 'Histogram', "Otsu's Thresholding",'Gaussian Filtered Image', 'Histogram', "Otsu's Thresholding (After Blur)"
]
images = [img_gray, 0, th1, img_gray, 0, th2, img_blur, 0, th3]for i in range(3):# 第1列:图像plt.subplot(3, 3, i*3 + 1)plt.imshow(images[i*3], cmap='gray')plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])# 第2列:直方图(ravel()将图像展平为一维数组)plt.subplot(3, 3, i*3 + 2)plt.hist(images[i*3].ravel(), 256)  # 256个灰度级plt.title(titles[i*3 + 1]), plt.xticks([]), plt.yticks([])# 标注Otsu计算的最优阈值if i > 0:plt.axvline(x=ret2 if i==1 else ret3, color='r', linestyle='--', label=f'Threshold={ret2 if i==1 else ret3:.0f}')plt.legend()# 第3列:阈值结果plt.subplot(3, 3, i*3 + 3)plt.imshow(images[i*3 + 2], cmap='gray')plt.title(titles[i*3 + 2]), plt.xticks([]), plt.yticks([])plt.show()
核心原理与优势
  • Otsu 算法通过计算 “类间方差最大化” 来确定最优阈值:将像素分为前景和背景两类,当两类的方差最大时,阈值能最清晰地分割两者。
  • 适用场景:灰度直方图有明显双峰的图像(如文字扫描件、细胞图像);若直方图无明显双峰(如渐变图像),Otsu 效果会下降。
  • 噪声影响:噪声会导致直方图峰模糊,因此先进行高斯滤波去噪,再用 Otsu 阈值,能显著提升分割效果。

二、图像滤波去噪:平滑图像与保留细节

图像噪声(如椒盐噪声、高斯噪声)会干扰后续处理(如阈值分割、边缘检测),滤波的核心是抑制噪声的同时,尽可能保留图像细节(如边缘)。OpenCV 提供线性滤波和非线性滤波两类方法,各有优劣。

1. 线性滤波:均值滤波(基于卷积的平滑)

线性滤波通过固定卷积核对图像进行卷积运算,均值滤波是最基础的线性滤波,核心是用 “卷积核覆盖区域的像素均值” 替代中心像素值,实现平滑去噪,但会模糊边缘。

代码实现:自定义均值滤波(用cv2.filter2D
import cv2
import numpy as np
from matplotlib import pyplot as plt# 读取彩色图像
img = cv2.imread('ocv01.jpg')
# 转换为RGB格式(matplotlib默认RGB显示,OpenCV默认BGR)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 1. 定义均值卷积核:5x5大小,每个元素为1/25(确保卷积后像素值在0-255)
kernel = np.ones((5, 5), np.uint8) / 25  # 5x5核的总权重为1,避免亮度异常# 2. 应用滤波:cv2.filter2D(输入图,输出深度(-1表示与输入一致),卷积核)
img_blur = cv2.filter2D(img_rgb, -1, kernel)# 显示对比
plt.subplot(121), plt.imshow(img_rgb), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_blur), plt.title('Mean Filter (5x5)')
plt.xticks([]), plt.yticks([])
plt.show()
原理与局限性
  • 卷积核大小影响:核越大,平滑效果越强,但边缘模糊越严重(如 3x3 核模糊轻,7x7 核模糊重)。
  • 局限性:线性滤波对所有像素一视同仁,会同时模糊噪声和边缘,适合对边缘要求不高的场景。

2. 常用非线性滤波:兼顾去噪与边缘保留

非线性滤波通过像素值的统计特性(如中值、高斯权重)动态调整输出,能在去噪的同时更好地保留边缘,是实际应用中的首选。OpenCV 提供 3 种常用非线性滤波:高斯滤波、中值滤波、双边滤波。

代码实现:4 种滤波效果对比
import cv2
import numpy as np
from matplotlib import pyplot as plt# 读取彩色图像并转换为RGB
img = cv2.imread('ocv01.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 1. 均值滤波(线性,对比用)
blur_mean = cv2.blur(img_rgb, (5, 5))  # 5x5核,等价于filter2D的均值核# 2. 高斯滤波(非线性,基于高斯权重,去高斯噪声优)
blur_gaussian = cv2.GaussianBlur(img_rgb, (5, 5), 0  # 5x5核,sigmaX=0(自动根据核大小计算)
)# 3. 中值滤波(非线性,基于像素中值,去椒盐噪声优)
blur_median = cv2.medianBlur(img_rgb, 5)  # 5x5核(仅需奇数,无权重)# 4. 双边滤波(非线性,去噪+保边缘,效果最优但最慢)
blur_bilateral = cv2.bilateralFilter(img_rgb, 9, 75, 75  # 直径=9,颜色相似度sigma=75,空间相似度sigma=75
)# 显示对比(按需选择一种滤波显示)
plt.subplot(121), plt.imshow(img_rgb), plt.title('Original')
plt.xticks([]), plt.yticks([])
# 替换下方为要对比的滤波结果(如blur_gaussian、blur_median、blur_bilateral)
plt.subplot(122), plt.imshow(blur_bilateral), plt.title('Bilateral Filter')
plt.xticks([]), plt.yticks([])
plt.show()
4 种滤波核心对比
滤波类型原理优势劣势适用场景
均值滤波局部像素均值计算快边缘模糊严重快速平滑、对边缘无要求
高斯滤波局部像素高斯加权均值(中心权重高)去高斯噪声效果好,模糊轻于均值滤波仍会轻微模糊边缘高斯噪声为主的图像(如低光拍摄)
中值滤波局部像素中值(替换中心像素)去椒盐噪声(黑白噪点)效果最优,保边缘对高斯噪声效果差,处理慢于高斯滤波椒盐噪声为主的图像(如压缩失真)
双边滤波结合颜色相似度 + 空间相似度(边缘处权重低)去噪同时最大程度保留边缘计算最慢,参数调整复杂需保留边缘的场景(如人脸、文档)
实用参数调整技巧
  • 高斯滤波:sigmaX越大,去噪越强但模糊越重;核大小需与sigmaX匹配(如sigmaX=1用 3x3 核,sigmaX=2用 5x5 核)。
  • 双边滤波:sigmaColor越大,允许更多颜色差异的像素被平滑;sigmaSpace越大,平滑范围越广;两者需同步调整(如均设为 50-100)。

总结

本文覆盖的图像预处理技术,是计算机视觉的 “地基”,核心应用场景如下:

  1. 阈值处理
    • 光照均匀 → 全局阈值(简单高效)。
    • 光照不均 → 自适应阈值(局部动态调整)。
    • 噪声多 + 直方图双峰 → Otsu 阈值(自动最优,需先去噪)。
  2. 滤波去噪
    • 高斯噪声 → 高斯滤波。
    • 椒盐噪声 → 中值滤波。
    • 需保边缘 → 双边滤波(优先选择,容忍较慢速度)。
    • 快速平滑 → 均值滤波(仅用于临时预览)。

实际项目中,预处理通常是 “滤波→阈值” 的组合流程(如先高斯滤波去噪,再自适应阈值分割),需根据图像特点(噪声类型、光照条件)灵活选择参数,才能达到最佳效果。

http://www.dtcms.com/a/356250.html

相关文章:

  • LubanCat-RK3568 UART串口通信,以及遇到bug笔记
  • CRYPT32!CryptMsgUpdate函数分析和asn.1 editor nt5inf.cat 的总览信息
  • 第八篇 永磁同步电机控制-MTPA、MTPV
  • 深入解析Qt节点编辑器框架:数据流转与扩展机制(三)
  • 实时音视频延迟优化指南:从原理到实践
  • 零知开源——基于STM32F407VET6和ADXL345三轴加速度计的精准运动姿态检测系统
  • Blender模拟结构光3D Scanner(三)获取相机观测点云的真值
  • OpenCV 基础知识总结
  • 无懈可击的 TCP AIMD
  • 亚马逊季节性产品运营策略:从传统到智能化的演进
  • kimi浏览器助手-月之暗面推出的智能浏览器扩展
  • docker中的mysql有中文显示问题跟大小写区分问题?
  • Python从入门到高手9.4节-基于字典树的敏感词识别算法
  • 使用Python脚本执行Git命令
  • React 状态丢失:组件 key 用错引发的渲染异常
  • Rust 安装与运行指南
  • Custom SRP - LOD and Reflections
  • 柳州市委常委、统战部部长,副市长潘展东率队首访深兰科技集团新总部,共探 AI 赋能制造大市与东盟合作新局
  • Claude Code 完整手册:从入门、配置到高级自动化
  • 【python】相机输出图片时保留时间戳数据
  • Linux学习——sqlite3
  • 179-183动画
  • IntelliJ IDEA2025+启动项目提示 Failed to instantiate SLF4J LoggerFactory
  • 零基础json入门教程(基于vscode的json配置文件)
  • 【贪心算法】day4
  • HTML 核心标签全解析:从文本排版到媒体嵌入
  • 联想打印机2268w安装
  • 根据并发和响应延迟,实现语音识别接口自动切换需求
  • IP v 6
  • Linux下的软件编程——数据库