学习日记 8.1
前言
前一篇文章讲了图片读取与显示,今天继续看图像运算、阈值处理和噪声去除。主要涉及:
- 图像运算:图像的加法运算与加权融合
- 阈值处理:二值化、截断等阈值操作,以及图像边框的填充
- 噪声去除:椒盐噪声的生成与多种滤波器的去噪效果对比
一、图像运算
1.1 使用 NumPy 的加法(+ 运算符)
直接用+运算符做图像加法,但 NumPy 的加法是取模运算:
import cv2 a1 = cv2.imread("3.png") b1 = cv2.imread("4.jpg") a1 = cv2.resize(a1, (500, 400)) b1 = cv2.resize(b1, (500, 400)) NumPy 加法:像素值相加后对 256 取模 c1 = a1 + 10 # 每个像素值加 10 c2 = a1[50:450, 50:400] + b1[50:450, 50:400] # ROI 区域相加当像素值超过 255 时,NumPy 会将其对 256 取模。例如250 + 10 = 260,取模后变成260 % 256 = 4,这会导致原本应该更亮的区域反而变暗,出现“溢出回绕”的现象。
1.2 使用 OpenCV 的 cv2.add() 函数
OpenCV 提供了专门的加法函数cv2.add(),它的处理方式是饱和运算——超过 255 的值会被截断为 255:
a2 = cv2.imread("3.png") b2 = cv2.imread("4.jpg") b2 = cv2.resize(b2, (400, 400)) a2 = cv2.resize(a2, (400, 400)) OpenCV 加法:超过 255 的像素值会被截断为 255(饱和运算) c3 = cv2.add(a2, b2)| 运算方式 | 函数 | 溢出处理 |
|---|---|---|
| NumPy 加法 | img1 + img2 | 取模运算(% 256) |
| OpenCV 加法 | cv2.add(img1, img2) | 饱和运算(截断到 255) |
小结:在处理图像加法时,推荐使用cv2.add(),因为它避免了取模操作带来的“反色”异常,视觉效果更加自然。
1.3 图像加权融合 —— cv2.addWeighted()
有时候我们希望将两张图像按照一定比例混合,而不是简单地相加。这时候就可以使用cv2.addWeighted():
# 参数:img1, 权重1, img2, 权重2, 亮度偏移量 c4 = cv2.addWeighted(a2, 0.7, b2, 0.3, 100)公式为:dst = α · img1 + β · img2 + γ,其中 α 和 β 是两幅图像的权重,γ 是亮度调节参数。在上面的代码中,a2占 70% 的权重,b2占 30%,最后再加上 100 的亮度偏移。这个函数在图像叠加、水印添加、视频特效等场景中很常用。
二、图像边框与阈值处理
2.1 为图像添加边框 —— cv2.copyMakeBorder()
为图像添加边框可以用cv2.copyMakeBorder(),支持多种填充方式:
import cv2 ys = cv2.imread("4.jpg") top, bottom, left, right = 50, 50, 50, 50 1. 常量边框:用固定颜色填充 constant = cv2.copyMakeBorder(ys, top, bottom, left, right, borderType=cv2.BORDER_CONSTANT, value=[229, 25, 80]) 2. 镜像边框:以边缘为轴进行镜像 reflect = cv2.copyMakeBorder(ys, top, bottom, left, right, borderType=cv2.BORDER_REFLECT) 3. 镜像边框(不含边界):与 REFLECT 类似但不包含边界像素 reflect101 = cv2.copyMakeBorder(ys, top, bottom, left, right, borderType=cv2.BORDER_REFLECT101) 4. 复制边框:复制边缘像素 replicate = cv2.copyMakeBorder(ys, top, bottom, left, right, borderType=cv2.BORDER_REPLICATE) 5. 环绕边框:用对面边界的像素填充 warp = cv2.copyMakeBorder(ys, top, bottom, left, right, borderType=cv2.BORDER_WRAP)五种边框类型的效果对比:
| 边框类型 | OpenCV 常量 | 说明 |
|---|---|---|
| 常量边框 | BORDER_CONSTANT | 用指定颜色值填充边框区域 |
| 镜像边框 | BORDER_REFLECT | 以边缘像素为镜像轴,向外镜像扩展,如 fedcba|abcdefgh|hgfedcb |
| 反射101 | BORDER_REFLECT101 | 以边缘像素为对称中心,如 gfedcb|abcdefgh|gfedcba |
| 复制边框 | BORDER_REPLICATE | 直接复制最边缘的像素值,如 aaaaaa|abcdefgh|hhhhhhh |
| 环绕边框 | BORDER_WRAP | 用对面边界的像素来填充,如 cdefgh|abcdefgh|abcdefg |
默认推荐:BORDER_REFLECT101 是 OpenCV 中许多函数(如滤波)的默认边框处理方式,它在大多数场景下表现最佳。
2.2 阈值处理 —— cv2.threshold()
阈值处理是图像分割中最基础的方法之一:设定一个阈值,将像素值与阈值比较,根据比较结果对像素分类。OpenCV 提供了五种阈值处理类型:
image = cv2.imread("4.jpg", 0) # 以灰度模式读取 参数:源图像, 阈值, 最大值, 阈值类型 ret, binary = cv2.threshold(image, 175, 255, cv2.THRESH_BINARY) # 二值化 ret1, binaryinv = cv2.threshold(image, 175, 255, cv2.THRESH_BINARY_INV) # 反二值化 ret2, trunc = cv2.threshold(image, 175, 255, cv2.THRESH_TRUNC) # 截断 ret3, tozero = cv2.threshold(image, 175, 255, cv2.THRESH_TOZERO) # 阈值归零 ret4, tozeroinv = cv2.threshold(image, 175, 255, cv2.THRESH_TOZERO_INV) # 反阈值归零| 阈值类型 | 处理规则 | 效果描述 |
|---|---|---|
| THRESH_BINARY | dst = maxVal 若 src > thresh,否则 0 | 超过阈值为白色,否则为黑色 |
| THRESH_BINARY_INV | dst = 0 若 src > thresh,否则 maxVal | 二值化的反转版本 |
| THRESH_TRUNC | dst = thresh 若 src > thresh,否则 src | 超过阈值的像素被截断为阈值 |
| THRESH_TOZERO | dst = src 若 src > thresh,否则 0 | 低于阈值的像素归零 |
| THRESH_TOZERO_INV | dst = 0 若 src > thresh,否则 src | 高于阈值的像素归零 |
返回值ret是实际使用的阈值(在使用 Otsu 等自适应方法时可能与传入值不同),binary等是处理后的图像。
应用场景:阈值处理常用于文档扫描二值化、物体轮廓提取、前景/背景分离等任务。
三、图像噪声与滤波
3.1 椒盐噪声的生成
椒盐噪声(Salt-and-Pepper Noise)是图像中常见的噪声类型,表现为随机出现的黑白像素点(像撒了盐和胡椒一样)。可以手动模拟这种噪声:
import numpy as np def add_peppersalt_noise(image, n=10000): """向图像添加椒盐噪声 Args: image: 输入图像 n: 噪声点数量 Returns: 添加了噪声的图像 """ result = image.copy() h, w = image.shape[:2] # 获取图像的高和宽 for i in range(n): x = np.random.randint(0, h) # 随机行坐标 y = np.random.randint(0, w) # 随机列坐标 if np.random.randint(0, 2) == 0: result[x][y] = 0 # 黑点(胡椒噪声) else: result[x][y] = 255 # 白点(盐噪声) return result这段代码会在图像的随机位置生成黑白噪声点,模拟真实场景中的椒盐噪声。
3.2 均值滤波 —— cv2.blur() 与 cv2.boxFilter()
均值滤波是最简单的线性滤波方法,它用像素邻域的平均值来替代中心像素的值:
# 均值滤波:5x5 的卷积核 blur_1 = cv2.blur(noise, (5, 5)) 方框滤波:当 normalize=True 时与均值滤波等效 boxFilter_1 = cv2.boxFilter(noise, -1, (3, 3), normalize=True) 方框滤波:normalize=False 时,求和但不平均(容易溢出) boxFilter_2 = cv2.boxFilter(noise, -1, (3, 3), normalize=False)| 参数 | 说明 |
|---|---|
-1 | 输出图像的深度,-1表示与输入图像一致 |
(3, 3) | 卷积核大小 |
normalize=True | 是否对结果进行归一化处理 |
注意:当normalize=False时,方框滤波不再除以核面积,像素值容易超过 255,导致画面一片白。
3.3 高斯滤波 —— cv2.GaussianBlur()
高斯滤波使用高斯函数作为权重分配,离中心越近的像素权重越大,去噪的同时能更好地保留边缘信息:
# 高斯滤波:核大小 (3,3),标准差为 1 Gaussian_Noise = cv2.GaussianBlur(noise, (3, 3), 1)参数说明:(3, 3)是高斯核的大小(必须是奇数),1是 X 方向的标准差(如果设为 0,则根据核大小自动计算)。
3.4 中值滤波 —— cv2.medianBlur()
中值滤波是非线性滤波,它用邻域像素的中值替代中心像素,对椒盐噪声的去除效果尤其出色:
# 中值滤波:核大小为 3 medianB = cv2.medianBlur(noise, 3)3.5 滤波效果对比
结论:对于椒盐噪声,中值滤波是首选方案。它能在有效去除椒盐噪声的同时最大限度地保留图像的边缘细节。
四、综合实战
4.1 实战一:摄像头实时画面与背景图融合
这是一个综合运用图像运算的实战案例,实现摄像头实时画面与背景图片的融合效果:
import cv2 cap = cv2.VideoCapture(0) background = cv2.imread('4.jpg') background = cv2.resize(background, (640, 480)) background_gray = cv2.cvtColor(background, cv2.COLOR_BGR2GRAY) if not cap.isOpened(): print("无法打开摄像头") exit() while True: ret, frame = cap.read() if not ret: break frame = cv2.resize(frame, (640, 480)) frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 彩色融合(各占 50% 透明度) c1 = cv2.addWeighted(frame, 0.5, background, 0.5, 100) 灰度融合(各占 50% 透明度) c2 = cv2.addWeighted(frame_gray, 0.5, background_gray, 0.5, 100) cv2.imshow('Color Fusion', c1) cv2.imshow('Gray Fusion', c2) 按下 ESC 键退出 if cv2.waitKey(60) == 27: break cap.release() cv2.destroyAllWindows()4.2 实战二:视频椒盐噪声处理
对视频文件逐帧添加椒盐噪声,并使用中值滤波进行去噪:
import cv2 import numpy as np def add_peppersalt_noise(image, n=8000): result = image.copy() h, w = image.shape[:2] for i in range(n): x = np.random.randint(0, h) y = np.random.randint(0, w) if np.random.randint(0, 2) == 0: result[x][y] = 0 else: result[x][y] = 255 return result cap = cv2.VideoCapture("test.avi") if not cap.isOpened(): print("无法打开视频文件") exit() while True: ret, frame = cap.read() if not ret: break # 添加椒盐噪声 noise = add_peppersalt_noise(frame) 中值滤波去噪 medianB = cv2.medianBlur(noise, 3) 同时显示三个窗口 cv2.imshow('Original', frame) # 原视频 cv2.imshow('Noise', noise) # 含噪声视频 cv2.imshow('Denoised', medianB) # 去噪后视频 if cv2.waitKey(60) == 27: break cap.release() cv2.destroyAllWindows()五、总结
今天的学习内容涵盖了 OpenCV 图像处理的三个核心模块:
| 模块 | 核心函数 | 要点 |
|---|---|---|
| 图像运算 | cv2.add(),cv2.addWeighted() | 区分 NumPy 取模加法与 OpenCV 饱和加法;掌握加权融合 |
| 阈值处理 | cv2.threshold(),cv2.copyMakeBorder() | 五种阈值类型 + 五种边框填充方式 |
| 滤波去噪 | cv2.blur(),cv2.boxFilter(),cv2.GaussianBlur(),cv2.medianBlur() | 椒盐噪声首选中值滤波 |
这些知识都是后续学习更高级图像处理操作(如边缘检测、形态学操作、轮廓提取等)的基础,建议动手运行每一段代码,观察不同参数带来的效果变化。
