OpenCV 作为计算机视觉领域最常用的开源库,提供了丰富的图像处理函数,能轻松实现图像边界填充、像素运算、阈值处理和图像平滑(滤波)等核心操作。本文将从实用角度出发,详解 OpenCV 中这些基础且高频的图像处理技术,重点聚焦图像滤波的原理与实战,帮助初学者快速掌握核心用法。
一、基础铺垫:图像边界填充
在图像处理中,当需要对图像边缘进行操作(如卷积、滤波)时,边界像素往往会因缺乏邻域信息导致处理不完整,此时就需要用为cv2.copyMakeBorder()图像添加边界(填充)。
核心参数与填充类型
python
import cv2
import numpy as np
# 读取图像
img = cv2.imread('img1.jpeg')
# 定义上下左右填充宽度
top, bottom, left, right = 50, 50, 50, 50
# 1. 常数填充:边界像素为指定颜色
constant = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(229, 25, 80))
# 2. 反射填充:边界像素为原图像边缘的镜面反射
reflect = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT)
# 3. 反射填充(优化版):避免边界重复
reflect101 = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT101)
# 4. 复制填充:用最边缘像素填充整个边界
replicate = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REPLICATE)
# 5. 环绕填充:上下左右边界循环替换
wrap = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_WRAP)
不同填充类型适用于不同场景:常数填充适合需要明确边界颜色的场景,反射 / 复制填充适合滤波时保持边缘连续性,环绕填充则适用于周期性图像处理。
二、像素级操作:图像运算
图像的本质是像素矩阵,OpenCV 支持两种核心加法运算,以及更灵活的加权运算。
1. 普通加法(+):截断规则
普通加法遵循「模 256」规则,像素和超过 255 时会减去 256:
python
a = cv2.imread('img1.jpeg')
b = cv2.imread('img2.jpeg')
# 像素值整体加10
c1 = a + 10
# 局部区域像素相加
c2 = a[50:450, 50:400] + b[50:450, 50:400]
2. cv2.add ():饱和规则
cv2.add()更符合人眼视觉,像素和超过 255 时直接取 255:
python
# 统一图像尺寸
a = cv2.resize(a, (400, 400))
b = cv2.resize(b, (400, 400))
c = cv2.add(a, b)
3. 加权加法(cv2.addWeighted):图像融合
加权运算公式:dst = src1*α + src2*β + γ,适合图像融合(如水印、渐变):
python
# α=0.5, β=0.5, γ=10(亮度补偿)
blend = cv2.addWeighted(a, 0.5, b, 0.5, 10)
三、像素筛选:阈值处理cv2.threshold()
阈值处理是将图像像素分为「高于阈值」和「低于阈值」两类,实现图像二值化或像素截断,核心函数cv2.threshold():
python
# 读取灰度图像
gray = cv2.imread('img1.jpeg', 0)
# 二值化阈值:像素>175设为255(白),否则0(黑)
ret, binary = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)
# 反二值化:像素>175设为0,否则255
ret, binary_inv = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY_INV)
# 截断:像素>175设为175,否则不变
ret, trunc = cv2.threshold(gray, 175, 255, cv2.THRESH_TRUNC)
# 归零:像素>175不变,否则0
ret, tozero = cv2.threshold(gray, 175, 255, cv2.THRESH_TOZERO)
阈值处理常用于图像分割、文字提取、轮廓检测等场景。
四、重点:图像平滑(滤波)------ 消除噪声的核心手段
图像噪声(如椒盐噪声、高斯噪声)会影响后续处理,滤波是消除噪声的关键,OpenCV 提供了 4 种常用滤波方式,我们先构造含椒盐噪声的图像,再对比不同滤波效果:
1. 生成椒盐噪声
python
def add_peppersalt_noise(image, n=10000):
"""添加椒盐噪声:随机生成黑白噪点"""
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)
# 随机生成黑/白点
result[x, y] = 0 if np.random.randint(0, 2) == 0 else 255
return result
# 读取图像并添加噪声
img = cv2.imread('img1.jpeg')
noise_img = add_peppersalt_noise(img)
2. 均值滤波(cv2.blur):简单平均
原理:用邻域内所有像素的平均值替换中心像素,优点是简单,缺点是会模糊图像边缘,核越大模糊越明显。
python
# 3x3核均值滤波(轻度模糊)
blur_3 = cv2.blur(noise_img, (3, 3))
# 30x30核均值滤波(重度模糊)
blur_30 = cv2.blur(noise_img, (30, 30))
3. 方框滤波(cv2.boxFilter):可归一化的均值滤波
原理:与均值滤波类似,但可通过normalize参数控制是否归一化:
- normalize=True:等同于均值滤波(邻域和 / 面积);
- normalize=False:直接取邻域和,超过 255 则截断为 255(易过曝)。
python
# 归一化方框滤波(同均值滤波)
box_norm = cv2.boxFilter(noise_img, -1, (3, 3), normalize=True)
# 不归一化方框滤波(易出现白色区块)
box_unnorm = cv2.boxFilter(noise_img, -1, (3, 3), normalize=False)
4. 高斯滤波(cv2.GaussianBlur):保留边缘的模糊
原理:对邻域像素加权平均(距离中心越近权重越高),能在消除噪声的同时,更好保留图像边缘细节,是最常用的滤波方式。
python
# 3x3核,X/Y方向标准差=1
gaussian = cv2.GaussianBlur(noise_img, (3, 3), 1)
5. 中值滤波(cv2.medianBlur):专治椒盐噪声
原理 :用邻域像素的中位数替换中心像素,能完美消除椒盐噪声(黑白噪点),且不会模糊边缘,是处理椒盐噪声的首选。
python
# 3x3核中值滤波(核大小需为奇数)
median = cv2.medianBlur(noise_img, 3)
滤波效果对比
| 滤波类型 | 核心特点 | 适用场景 |
|---|---|---|
| 均值滤波 | 简单平均,模糊明显 | 轻度噪声、无边缘保护需求 |
| 方框滤波 | 可归一化,灵活控制 | 自定义加权平均场景 |
| 高斯滤波 | 加权平均,保留边缘 | 通用降噪(高斯噪声) |
| 中值滤波 | 中位数替换,无模糊 | 椒盐噪声、颗粒噪声 |
五、完整实战代码
python
import cv2
import numpy as np
# ========== 1. 边界填充 ==========
img = cv2.imread('img1.jpeg')
top, bottom, left, right = 50, 50, 50, 50
constant = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(229, 25, 80))
# ========== 2. 图像运算 ==========
a = cv2.imread('img1.jpeg')
b = cv2.imread('img2.jpeg')
a = cv2.resize(a, (400, 400))
b = cv2.resize(b, (400, 400))
blend = cv2.addWeighted(a, 0.5, b, 0.5, 10)
# ========== 3. 阈值处理 ==========
gray = cv2.imread('img1.jpeg', 0)
ret, binary = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)
# ========== 4. 图像滤波 ==========
def add_peppersalt_noise(image, n=10000):
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)
result[x, y] = 0 if np.random.randint(0, 2) == 0 else 255
return result
noise_img = add_peppersalt_noise(img)
# 均值滤波
blur = cv2.blur(noise_img, (3, 3))
# 方框滤波
box = cv2.boxFilter(noise_img, -1, (3, 3), normalize=True)
# 高斯滤波
gaussian = cv2.GaussianBlur(noise_img, (3, 3), 1)
# 中值滤波
median = cv2.medianBlur(noise_img, 3)
# 显示结果
cv2.imshow('原始图像', img)
cv2.imshow('椒盐噪声', noise_img)
cv2.imshow('均值滤波', blur)
cv2.imshow('高斯滤波', gaussian)
cv2.imshow('中值滤波', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
总结
- OpenCV 的边界填充、图像运算、阈值处理是图像处理的基础,填充用于边缘补全,运算用于像素级操作,阈值用于像素筛选;
- 图像滤波是消除噪声的核心:均值滤波简单但模糊明显,高斯滤波兼顾降噪与边缘保留,中值滤波是椒盐噪声的 "克星";
- 实际开发中需根据噪声类型选择滤波方式:高斯噪声用高斯滤波,椒盐噪声用中值滤波,通用场景优先选高斯滤波。
掌握这些基础操作,就能应对大部分入门级的图像处理需求,后续可结合卷积、形态学操作等进阶技术,实现更复杂的计算机视觉任务。