OpenCV 作为 Python 计算机视觉领域的核心库,其基础操作是入门计算机视觉的必经之路。本文将围绕边界填充、图像运算、阈值处理、图像平滑四大核心模块,结合实战代码拆解原理与应用,帮助新手快速掌握 OpenCV 图像预处理的核心逻辑。
一、环境准备
1. 依赖安装
确保已安装 OpenCV 和 NumPy 库,执行以下命令完成安装:
python
pip install opencv-python numpy
2. 素材准备
准备 1-2 张测试图片(如 zx_min.jpg、lp.png),放置在代码同级目录下,建议选择彩色、分辨率适中的图片,便于观察处理效果。
二、核心操作详解
(一)边界填充:给图像添加自定义边框
边界填充(Padding)是图像预处理的基础操作,常用于卷积运算中边缘信息补全、图像可视化时的边框美化等场景,OpenCV 中通过 cv2.copyMakeBorder() 实现。
- 函数核心参数
python
cv2.copyMakeBorder(src, top, bottom, left, right, borderType, value)
| 参数 | 说明 |
|---|---|
src |
待填充的原始图像 |
top/bottom/left/right |
上 / 下 / 左 / 右四个方向的边框宽度(单位:像素) |
borderType |
边框类型(核心:5 种预设类型) |
value |
仅 BORDER_CONSTANT 类型需要,指定边框颜色(BGR 格式,如 (299,25,80)) |
- 5 种边框类型对比
| 边框类型 | 填充规则 | 效果示例(以序列 abcdefgh 为例) | ||
|---|---|---|---|---|
BORDER_CONSTANT |
固定颜色填充 | ` 自定义颜色 | abcdefgh | 自定义颜色 ` |
BORDER_REFLECT |
镜面反射(包含边界像素重复) | `gfedcba | abcdefgh | hgfedcba` |
BORDER_REFLECT101 |
镜面反射(无边界重复,推荐) | `gfedcb | abcdefgh | gfedcb` |
BORDER_REPLICATE |
重复边界像素 | `aaaaaa | abcdefgh | hhhhhhh` |
BORDER_WRAP |
循环填充(上下左右边界互替) | `cdefgh | abcdefgh | abcdefg` |
- 实战代码
python
'''----------------边界填充-------------------------'''
# cv2.copyMakeBorder()是OpenCV库中的一个函数,用于给图像添加额外的边界(padding)。
# copyMakeBorder(src: UMat, top: int, bottom: int, left: int, right: int, borderType: int, dst: UMat | None = ..., value: cv2.typing.Scalar = ...)
# 它有以下几个参数:
# src:要扩充边界的原始图像。
# top, bottom, left, right:相应方向上的边框宽度。
# borderType:定义要添加边框的类型,它可以是以下的一种:
# cv2.BORDER_CONSTANT:添加的边界框像素值为常数(需要额外再给定一个参数)。
# cv2.BORDER_REFLECT:添加的边框像素将是边界元素的镜面反射,类似于gfedcba|abcdefgh|hgfedcba。 (交界处也复制了)
# cv2.BORDER_REFLECT_101 或 cv2.BORDER_DEFAULT:和上面类似,但是有一些细微的不同,类似于gfedcb|abcdefgh|gfedcba (交接处删除了)
# cv2.BORDER_REPLICATE:使用最边界的像素值代替,类似于aaaaaa|abcdefgh|hhhhhhh
# cv2.BORDER_WRAP:上下左右边依次替换,cdefgh|abcdefgh|abcdefg
import cv2
zm=cv2.imread('zx_min.jpg')
# zm=cv2.resize(zm,dsize=None,fx=0.5,fy=0.5)
# zm=cv2.resize(zm,(640,480))
top,bottom,left,right=50,50,50,50
consant=cv2.copyMakeBorder(zm,top,bottom,left,right,borderType=cv2.BORDER_CONSTANT,value=(299,25,80))
reflect=cv2.copyMakeBorder(zm,top,bottom,left,right,borderType=cv2.BORDER_REFLECT)
reflect101=cv2.copyMakeBorder(zm,top,bottom,left,right,borderType=cv2.BORDER_REFLECT101)
replicate=cv2.copyMakeBorder(zm,top,bottom,left,right,borderType=cv2.BORDER_REPLICATE)
wrap=cv2.copyMakeBorder(zm,top,bottom,left,right,borderType=cv2.BORDER_WRAP)
cv2.imshow('zx_min',zm)
# cv2.waitKey(0)
cv2.imshow('CONSTANT',consant)
# cv2.waitKey(0)
cv2.imshow('REFLECT',reflect)
# cv2.waitKey(0)
cv2.imshow('REFLECT101',reflect101)
# cv2.waitKey(0)
cv2.imshow('REPLICATE',replicate)
# cv2.waitKey(0)
cv2.imshow('WRAP',wrap)
cv2.waitKey(0)
cv2.destroyAllWindows()
(二)图像运算:像素级加法与加权融合
图像运算的本质是对像素值的逐点计算,核心分为普通加法 和加权加法,适用于图像融合、亮度调节等场景。
1. 普通加法:两种规则的差异
| 运算方式 | 核心规则 | 代码示例 |
|---|---|---|
原生 + 号 |
超过 255 取余((a+b)%256),易导致颜色失真 |
c = a[区域] + b[区域] |
cv2.add() |
超过 255 取 255(饱和加法),符合人眼视觉习惯 | c = cv2.add(a, b) |
注意:加法运算要求两张图像尺寸、通道数完全一致,需先用 cv2.resize() 统一尺寸。
2. 加权加法:图像融合的核心
加权加法通过 cv2.addWeighted() 实现,支持调节图像透明度,公式为:

• alpha/beta:两张图像的权重(通常和为 1,如 0.5+0.5);
• gamma:亮度调节常量(可正可负,如 10 表示整体亮度 +10)。
实战代码:
python
'''-----------------图像运算-----------------------'''
# 图像加法运算
# 对于+号运算,当对图像a,图像b进行加法求和时,遵循以下规则:
# 当某位置像素相加得到的数值小于255时,该位置数值为两图像该位置像素相加之和
# 当某位置像素相加得到的数值大于255时,该位置数值将截断结果并将其减去 256 例如:相加后是260,实际是260-256= 4
a = cv2.imread('lp.png')
b = cv2.imread('zx_min.jpg')
# 3. 对图片a的所有像素值加10(像素级加法,遵循模256规则)
# 原理:每个像素的B/G/R三个通道值都+10,比如像素(100,150,200)→(110,160,210);若加完超255则取余,如(250,250,250)+10→(4,4,4)
c = a + 10
cv2.imshow('yuan', a)
cv2.imshow('a+10', c)
cv2.waitKey(0)
# 7. 截取图片a和b的指定区域并逐像素相加
# a[50:450,50:400]:截取a的行50到449、列50到399的矩形区域(高400像素,宽350像素)
# b[50:450,50:400]:截取b的相同位置区域(要求b的尺寸≥450行×400列,否则报错)
# 加法规则:对应位置的像素值相加,超255则取余(如250+10=260→260-256=4)
c = a[50:450,50:400] + b[50:450,50:400]
cv2.imshow('a+b', c)
cv2.waitKey(0)
# 对于cv2.add()运算,当对图像a,图像b进行加法求和时,遵循以下规则:
# 当某位置像素相加得到的数值小于255时,该位置数值为两图像该位置像素相加之和
#当某位置像素相加得到的数值大于255时,该位置数值为255
#
# 图像加权运算
#就是在计算两幅图像的像素值之和时,将每幅图像的权重考虑进来,可以用公式表示dst=src1xa+src2xB+V
import cv2
a = cv2.imread('lp.png')
b = cv2.imread('zx_min.jpg')
# 3. 将图片b缩放为 400x400 像素(宽400,高400)
# cv2.resize(待缩放图像, (目标宽度, 目标高度)):统一尺寸是像素运算的前提
b = cv2.resize(b, (400, 400))
# 4. 将图片a也缩放为 400x400 像素,保证和b尺寸完全一致
# (cv2.addWeighted要求两张图像尺寸、通道数必须相同,否则报错)
a = cv2.resize(a, (400, 400))
# 5. 图像加权融合(核心函数:cv2.addWeighted)
# 公式:c = a*0.5 + b*0.5 + 10
# 参数说明:
# a:第一张输入图像
# 0.5:a的权重(表示a占融合结果的50%透明度)
# b:第二张输入图像
# 0.5:b的权重(表示b占融合结果的50%透明度)
# 10:亮度调节常量(给所有像素值加10,整体提亮,可正可负)
c = cv2.addWeighted(a, 0.5, b, 0.5, 10)
cv2.imshow('addWeighted', c)
cv2.waitKey(0)
cv2.destroyAllWindows()
(三)阈值处理:图像二值化的核心
阈值处理是将灰度图像转为黑白二值图的关键操作,通过设定 "像素分界线"(阈值)区分前景与背景,核心函数为 cv2.threshold()。
1. 函数核心参数
python
ret, dst = cv2.threshold(src, thresh, maxval, type)
thresh:阈值(0-255,如 150);maxval:最大值(通常为 255,代表纯白);type:阈值处理类型(5 种核心类型)。
2. 5 种阈值类型规则(以阈值 150 为例)
| 类型 | 处理规则 | 视觉效果 |
|---|---|---|
THRESH_BINARY |
≥150 → 255(白),<150 → 0(黑) | 亮区变白,暗区变黑 |
THRESH_BINARY_INV |
≥150 → 0(黑),<150 → 255(白) | 亮区变黑,暗区变白 |
THRESH_TRUNC |
≥150 → 150(截断),<150 → 原值 | 亮区 "封顶",暗区不变 |
THRESH_TOZERO |
≥150 → 原值,<150 → 0(黑) | 亮区保留,暗区变黑 |
THRESH_TOZERO_INV |
≥150 → 0(黑),<150 → 原值 | 亮区变黑,暗区保留 |
3. 实战代码
python
import cv2
import cv2
# 读取图片并转为灰度图(参数0指定灰度模式,像素值范围0-255)
image = cv2.imread('lpp.png', 0)
# 阈值处理核心参数:阈值150,最大值255(纯白)
# 1. 二值化:≥150→255(白),<150→0(黑) | ret返回实际使用的阈值(此处为150)
ret, binary = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
# 2. 反向二值化:≥150→0(黑),<150→255(白)
ret1, binaryinv = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY_INV)
# 3. 截断处理:≥150→150(封顶),<150→保留原值
ret2, trunc = cv2.threshold(image, 150, 255, cv2.THRESH_TRUNC)
# 4. 低于阈值置0:≥150→保留原值,<150→0(黑)
ret3, tozero = cv2.threshold(image, 150, 255, cv2.THRESH_TOZERO)
# 5. 高于阈值置0:≥150→0(黑),<150→保留原值
ret4, tozeroinv = cv2.threshold(image, 150, 255, cv2.THRESH_TOZERO_INV)
# 显示所有处理结果(一次性展示,按任意键关闭所有窗口)
cv2.imshow('gray', image) # 灰度原图
cv2.imshow('binary', binary) # 二值化结果
cv2.imshow('binaryinv', binaryinv) # 反向二值化
cv2.imshow('trunc', trunc) # 截断效果
cv2.imshow('tozero', tozero) # 低于阈值置0
cv2.imshow('tozeroinv', tozeroinv) # 高于阈值置0
cv2.waitKey(0) # 阻塞窗口,按任意键退出
(四)图像平滑:噪声去除的核心手段
图像平滑(模糊处理)通过消除噪声、弱化细节实现图像模糊,常用滤波器包括均值滤波、方框滤波、高斯滤波、中值滤波,以下先实现 "添加椒盐噪声" 模拟真实场景,再演示滤波效果。
1. 生成椒盐噪声(模拟图像噪声)
python
import cv2
import numpy as np
# 定义添加椒盐噪声的函数
def add_peppersalt_noise(image, n=10000):
result = image.copy() # 避免修改原图
h, w = image.shape[:2] # 对于彩色图像(默认 BGR 格式):image.shape 返回 (高度, 宽度, 通道数),例如 (720, 1080, 3);对于灰度图像(单通道):image.shape 返回 (高度, 宽度),例如 (720, 1080);获取图像高、宽
for i in range(n):
# 随机生成噪声点坐标
x = np.random.randint(0, h)
y = np.random.randint(0, w)
# 随机生成黑色(0)或白色(255)噪声
result[x, y] = 0 if np.random.randint(0,2)==0 else 255
return result
# 读取图像并添加噪声
image = cv2.imread('zx_min.jpg')
noise_image = add_peppersalt_noise(image)
# 显示原图与噪声图
cv2.imshow('原图', image)
cv2.imshow('椒盐噪声图', noise_image)
2. 常用滤波器实战
python
# 1. 均值滤波(邻域平均,核越大越模糊)
blur_3 = cv2.blur(noise_image, (3,3)) # 3x3 核(轻度模糊)
blur_63 = cv2.blur(noise_image, (63,63)) # 63x63 核(重度模糊)
cv2.imshow('均值滤波 3x3', blur_3)
cv2.imshow('均值滤波 63x63', blur_63)
# 2. 方框滤波(可归一化/非归一化)
# normalize=True:等价于均值滤波;False:像素值求和(易过曝)
一、核心结论
cv2.boxFilter() 中 normalize=True/False 决定了是否对滤波后的像素值进行归一化(均值化),是方框滤波的核心参数,直接影响滤波效果:
normalize=True:归一化(默认)→ 等价于均值滤波,滤波后图像亮度正常;
normalize=False:不归一化 → 像素值直接求和,大概率溢出(255),图像整体偏白 / 过曝。
二、原理拆解(3×3 核为例)
1. 方框滤波的本质
方框滤波是对图像中每个像素的「邻域(核大小)内所有像素值求和」,再根据 normalize 参数决定是否除以邻域像素总数:
邻域像素总数 = 核的宽 × 核的高(3×3 核 = 9 个像素)
normalize=True(归一化)
计算公式:目标像素值=邻域像素总数/邻域内所有像素值之和
例:3×3 邻域像素和为 1000 → 目标值 = 1000/9 ≈ 111(正常范围 0-255);
• 效果:和 cv2.blur(noise, (3,3)) 完全一致,是常规的 "均值模糊",图像亮度自然。
normalize=False(不归一化)
计算公式:目标像素值=
例:3×3 邻域像素和为 1000 → 目标值 = 1000(远超 255);
关键:OpenCV 中图像像素值默认是 uint8 类型(0-255),超过 255 的部分会被截断为 255(饱和处理);
效果:大部分像素值直接溢出为 255,图像呈现大面积白色(过曝),仅极少数暗区域可能保留细节。
box_norm = cv2.boxFilter(noise_image, -1, (3,3), normalize=True)
box_unnorm = cv2.boxFilter(noise_image, -1, (3,3), normalize=False)
cv2.imshow('方框滤波(归一化)', box_norm)
cv2.imshow('方框滤波(非归一化)', box_unnorm)
# 3. 高斯滤波(高斯权重,模糊更自然)
gaussian = cv2.GaussianBlur(noise_image, (3,3), 1)
cv2.imshow('高斯滤波 3x3', gaussian)
# 4. 中值滤波(专治椒盐噪声)
median = cv2.medianBlur(noise_image, 3) # 核大小为奇数
cv2.imshow('中值滤波 3x3', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 滤波器效果对比
| 滤波器 | 核心特点 | 最佳适用场景 |
|---|---|---|
| 均值滤波 | 简单平均,模糊均匀 | 轻度降噪、整体模糊 |
| 方框滤波 | 可选择是否归一化 | 自定义权重求和、特殊效果 |
| 高斯滤波 | 高斯权重,边缘更柔和 | 自然模糊、保留边缘的降噪 |
| 中值滤波 | 取邻域中值,无像素求和 | 椒盐噪声去除(效果最佳) |
三、核心总结
- 边界填充 :
BORDER_REFLECT101是卷积操作补边的首选,BORDER_CONSTANT适合自定义颜色边框; - 图像运算 :
cv2.add()优于原生+号,addWeighted()是图像融合的核心函数; - 阈值处理:二值化是图像分割基础,阈值需根据图像亮度灵活调整(建议 120-180 之间调试);
- 图像平滑:中值滤波对椒盐噪声效果最佳,高斯滤波模糊更自然,滤波核越大效果越明显(需为奇数)。
四、注意事项
- 所有操作前需校验图像读取是否成功(
if image is None),避免路径错误导致报错; - 像素运算、滤波等操作需保证图像尺寸 / 通道数一致,必要时用
cv2.resize()统一; - 阈值处理建议先转为灰度图,彩色图需分通道处理;
- 显示图像后需调用
cv2.waitKey(0)阻塞窗口,最后用cv2.destroyAllWindows()释放资源。