OpenCV 图像处理学习笔记|图像运算、边界填充、阈值处理、图像平滑滤波

一、本期学习目标

1.理解图像像素级算术运算原理,区分 OpenCV 饱和加法与 NumPy 取模加法的差异,掌握图像加融合。

2.掌握图像边界填充的 5 种模式,理解填充在卷积运算中的实际意义。

3.理解图像阈值分割原理,掌握 5 种阈值处理模式,明白阈值处理的使用前提。

4.理解图像噪声、图像平滑、滤波的基本概念。

5.熟练掌握方框滤波、高斯滤波、中值滤波,对比三类滤波的原理、适用噪声类型、优缺点。

6.能够根据图像噪声类型,选择合适的滤波算法完成图像降噪。

二、图像的加法与加权运

1 .原理基础

数字图像本质是矩阵,彩色图像为三维矩阵(高度,宽度,通道数),灰度图为二维矩阵,矩阵中每一个元素取值范围 0‑255,代表像素亮度,0 最黑,255 最亮。

图像加法,就是两张完全相同尺寸、相同通道数图像对应位置像素值相加。

像素相加会出现溢出:两个像素相加结果大于 255。这里存在两种处理逻辑:

1.NumPy 直接相加 img1 + img2:取模运算

超过 255 时,对 256 取余数。例如:250+20 =270,270 mod256=14。

现象:高亮区域直接变成暗黑色,图像出现反常黑白翻转,图像处理中几乎不使用。

2.OpenCV 加法 cv2.add(img1,img2):饱和截断

像素和超过 255 直接限制最大值 255,不会发生颜色翻转,是图像处理标准做法。

2 图像加权融合 addWeighted

加权融合实现两张图片按权重混合,公式:

dst=α⋅img1+β⋅img2+γ

α:第一张图像权重

β:第二张图像权重

γ:亮度偏移量,可以整体提亮或者压暗图片

同样强制要求两张图像高、宽、通道完全一致,否则直接报错。

3 核心 API

饱和加法

cv2.add(src1, src2)

加权融合

cv2.addWeighted(src1, alpha, src2, beta, gamma)

4.代码
python 复制代码
import cv2

# ===================== 图像加法运算:直接使用 + 号 =====================
# 对于 + 号运算,当对图像a,图像b进行加法求和时,遵循以下规则:
# 当某位置像素相加得到的数值小于255时,该位置数值为两图像该位置像素相加之和
# 当某位置像素相加得到的数值大于255时,该位置数值将截断结果并将其减去 256
# 例如:相加后是260,实际是260-256=4(模256取余,溢出会回绕)
a = cv2.imread('11.jpg')   # 读取第一张图片
b = cv2.imread('22.jpg')   # 读取第二张图片

c = a + 10                 # 整张图每个像素全部+10,+号模式,溢出会回绕
cv2.imshow('yuantu', a)    # 显示原图a
cv2.imshow('a+10', a+10)    # 显示每个像素加10之后的图片
cv2.waitKey(0)              # 等待按键,暂停窗口

# 切片截取两张图相同区域,做 + 加法运算
# a[高度范围,宽度范围]:50到450行,50到400列
m = a[50:450, 50:400] + b[50:450, 50:400]
cv2.imshow('a+b', m)        # 显示切片直接相加结果(溢出回绕)
cv2.waitKey(0)              # 等待按键

# ===================== 图像加法运算:cv2.add()函数 =====================
# 对于cv2.add()运算,当对图像a,图像b进行加法求和时,遵循以下规则:
# 当某位置像素相加得到的数值小于255时,该位置数值为两图像该位置像素相加之和
# 当某位置像素相加得到的数值大于255时,该位置数值直接取最大值255(饱和截断,不会回绕)
# 注意:add要求两张图片宽高通道数必须完全一致

a = cv2.imread('11.jpg')
b = cv2.imread('22.jpg')
b = cv2.resize(b, (400, 400))  # 将b缩放到400×400
a = cv2.resize(a, (400, 400))  # 将a缩放到400×400,保证两张图尺寸一样

c = cv2.add(a, b)              # OpenCV官方加法,超过255直接置为255
cv2.imshow('a add b', c)       # 显示add相加结果
cv2.waitKey(0)
cv2.destroyAllWindows()        # 关闭所有窗口

# ===================== 加权混合 cv2.addWeighted() =====================
# addWeighted(图1,图1权重,图2,图2权重,亮度偏移值)
# 公式:输出 = a*alpha + b*beta + gamma
# a权重0.5,b权重0.8,整体再加10的亮度偏移
c = cv2.addWeighted(a, 0.5, b, 0.8, 10)
cv2.imshow('addweighted', c)   # 显示两张图片加权融合效果
cv2.waitKey(0)
cv2.destroyAllWindows()
运算方式 溢出处理 现象 适用场景
numpy + 模 256,溢出回绕 高光出现黑斑、色彩反转 几乎不用于图像处理
cv2.add() 饱和截断到 255 高光正常变白,无黑斑 图像直接叠加
cv2.addWeighted() 饱和截断 按权重混合两张图,可调节整体亮度 图片融合、淡入淡出

【1】numpy 的+是矩阵数学运算,不考虑图像像素 0‑255 的显示逻辑,溢出会回绕,不要直接用来做图像相加。

【2】OpenCV 的cv2.add()做饱和截断,符合图像显示逻辑,是图像加法的标准 API。

【3】cv2.add()、cv2.addWeighted()都强制要求输入图像尺寸、通道数完全一致,尺寸不同需要先 resize 缩放。

【4】addWeighted 的 gamma 参数可以整体调节输出图像的明暗。

三、图像边界填充 copyMakeBorder

1 为什么需要边界填充

当执行卷积、滤波操作的时候,卷积核滑动到图像边缘,边缘像素缺少邻域像素参与计算。

如果不做填充,卷积之后输出图像的宽高会缩小。

边界填充就是在原图四周扩充像素,保证卷积之后输出图像和原图尺寸保持一致。

cv2.copyMakeBorder(src, top, bottom, left, right, borderType, value)

top,bottom,left,right:上下左右需要填充多少像素

borderType:填充模式

value:仅 CONSTANT 模式生效,填充的颜色值

2 五种填充模式详解

1.cv2.BORDER_REPLICATE 复制填充

复制图像最边缘那一行 / 列像素向外延展。

特点:边缘过渡平缓,卷积经常使用。

2.cv2.BORDER_REFLECT 镜像反射填充

以图像边界为镜面,把图像内容镜像翻转复制出去,边界像素会重复。

3.cv2.BORDER_REFLECT_101 反射 101(也叫镜像,OpenCV 最常用)

镜像填充,边界像素不重复,相邻不会出现重复像素,卷积操作首选填充方式。

4.cv2.BORDER_WRAP 环绕平铺填充

把图像当作瓦片,循环平铺。右边缺的像素从图像左边拿,下边缺的像素从图像上边拿。视觉上会出现画面拼接割裂感。

5.cv2.BORDER_CONSTANT 常量纯色填充

四周全部填充固定颜色 value,比如黑色边框、白色边框。

3代码
python 复制代码
import cv2

# 读取图片
ys = cv2.imread('11.jpg')
# 修改图片尺寸为400*400
ys = cv2.resize(ys, dsize=(400, 400))

# 设置上下左右需要填充的像素宽度
top,bottom,left,right = 50,50,50,50

# 1.常量填充:用固定颜色填充边界
constant = cv2.copyMakeBorder(ys,top,bottom,left,right,borderType=cv2.BORDER_CONSTANT)
# 2.反射填充:镜像复制,包含边缘像素
reflect = cv2.copyMakeBorder(ys,top,bottom,left,right,borderType=cv2.BORDER_REFLECT)
# 3.反射101填充:镜像复制,不重复边缘像素,项目最常用
reflect101_ = cv2.copyMakeBorder(ys,top,bottom,left,right,borderType=cv2.BORDER_REFLECT_101)
# 4.复制填充:直接复制图像最边缘那一行/列像素向外拉伸
replicate = cv2.copyMakeBorder(ys,top,bottom,left,right,borderType=cv2.BORDER_REPLICATE)
# 5.环绕填充:把图片整体平铺环绕,类似卷纸循环
wrap = cv2.copyMakeBorder(ys,top,bottom,left,right,borderType=cv2.BORDER_WRAP)

# 显示原图
cv2.imshow('yuantu',ys)
# 显示常量填充效果
cv2.imshow('constant',constant)
# 显示reflect镜像效果
cv2.imshow('reflect',reflect)
# 显示reflect101镜像效果,窗口标题是reflect101,传入处理好的图片变量reflect101_
cv2.imshow('reflect101',reflect101_)
# 显示复制边缘填充效果
cv2.imshow('replicate',replicate)
# 显示环绕平铺填充效果
cv2.imshow('wrap',wrap)

# 等待任意按键,0代表无限等待
cv2.waitKey(0)
# 关闭全部opencv窗口,释放资源
cv2.destroyAllWindows()
填充模式 视觉现象 是否出现明显边框 适用场景
CONSTANT 四周纯色边框 手动添加边框
REFLECT 镜像,边界像素重复 几乎无,但边缘有重复纹路 少用
REFLECT_101 平滑镜像,无像素重复 卷积滤波首选,工业项目常用
REPLICATE 边缘像素向外拉伸 边缘颜色单一图像
WRAP 画面循环错位拼接 拼接割裂感 纹理平铺

【1】copyMakeBorder可以在图像上下左右扩充指定像素,输出图像宽高变大。

【2】REFLECT_101实际工程中使用最多,镜像不重复边界像素,卷积时可以有效减少边界伪影。

【3】CONSTANT产生人为纯色边界,会引入虚假像素,卷积运算尽量不要使用。

【4】WRAP会把图像另一端的内容搬运到边界,普通图像会出现内容错乱,只适合纹理素材。

四、图像阈值处理 threshold

1 基本概念

阈值处理一般针对灰度图像。设定一个阈值 T,将图像每一个像素和 T 比较,把像素划分两类,实现图像二值分割。

注意:彩色图不能直接做 threshold,需要先转灰度cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)。

函数:retval, dst = cv2.threshold(src, thresh, maxval, type)

src:输入灰度图

thresh:设定阈值 T

maxval:满足条件时赋予的最大值(一般填 255)

type:阈值模式

retval:返回实际使用的阈值;dst 输出处理后图像

2 五种阈值模式

1.cv2.THRESH_BINARY 二值化

大于阈值→变白;小于阈值→变黑。

2.cv2.THRESH_BINARY_INV 反向二值化

大于阈值变黑,小于阈值变白,和上面完全反转。

3.cv2.THRESH_TRUNC 截断模式

大于阈值的像素强制等于阈值;小于阈值保持原值。画面不会直接变成纯黑纯白,只是把高光截断。

4.cv2.THRESH_TOZERO 置零模式

大于阈值保持原值;小于阈值直接置 0 变黑。

5.cv2.THRESH_TOZERO_INV 反向置零

小于阈值保持原值;大于阈值置 0 变黑。

3. 应用场景

阈值是图像分割的基础,把目标物体和背景分离开。

例如:文档扫描文字提取、物体轮廓提取。

4.代码
python 复制代码
import cv2

# 读取图片,第二个参数0代表直接读取为灰度图像(单通道)
image = cv2.imread('11.jpg',0)
# 将图片缩放为400×400大小
image = cv2.resize(image,(400,400))

# threshold(输入灰度图,阈值,最大值,阈值模式)
# 返回值:ret 代表使用的阈值;binary 处理之后的图像
# 模式1:THRESH_BINARY 二值化:大于阈值→255白色;小于阈值→0黑色
ret,binary= cv2.threshold(image,175,255,cv2.THRESH_BINARY)

# 模式2:THRESH_BINARY_INV 反向二值化:大于阈值→0黑色;小于阈值→255白色
ret1,binaryinv= cv2.threshold(image,175,255,cv2.THRESH_BINARY_INV)

# 模式3:THRESH_TRUNC 截断:大于阈值就等于阈值,小于阈值保持原值不变
ret2,trunc= cv2.threshold(image,175,255,cv2.THRESH_TRUNC)

# 模式4:THRESH_TOZERO 置零:大于阈值保持原值;小于阈值置为0黑色
ret3,tozero= cv2.threshold(image,175,255,cv2.THRESH_TOZERO)

# 模式5:THRESH_TOZERO_INV 反向置零:大于阈值置0黑色;小于阈值保持原来像素值
ret4,tozeroinv= cv2.threshold(image,175,255,cv2.THRESH_TOZERO_INV)

# 显示灰度原图
cv2.imshow('gray',image)
cv2.waitKey(0)   # 等待按键,按任意键切换下一张

# 显示BINARY正向二值化结果
cv2.imshow('binary',binary)
cv2.waitKey(0)

# 显示BINARY_INV反向二值化结果(注意代码里窗口名拼写binrayinv是笔误)
cv2.imshow('binrayinv',binaryinv)
cv2.waitKey(0)

# 显示TRUNC截断模式结果
cv2.imshow('trunc',trunc)
# 显示TOZERO置零模式结果
cv2.imshow('tozero',tozero)
cv2.waitKey(0)

# 显示TOZERO_INV反向置零结果
cv2.imshow('tozeroinv',tozeroinv)
cv2.waitKey(0)

# 全部窗口关闭释放(建议加上,实验好习惯)
cv2.destroyAllWindows()
阈值模式 输出特点 像素规则总结
THRESH_BINARY 二值图,只有 0 和 255 大于阈值变白,小于变黑
THRESH_BINARY_INV 二值图,只有 0 和 255 大于阈值变黑,小于变白
THRESH_TRUNC 保留灰度,无纯白 大于阈值就限制为阈值,暗部不变
THRESH_TOZERO 保留高于阈值的灰度 小于阈值全部变黑
THRESH_TOZERO_INV 保留低于阈值的灰度 大于阈值全部变黑

【1】cv2.threshold()要求输入必须是灰度单通道图像;如果传入彩色三通道图像,代码不会报错,但结果完全没有意义。

【2】THRESH_BINARY与THRESH_BINARY_INV输出严格二值图像,只有 0、255,多用于前景背景分割。

【3】TRUNC、TOZERO、TOZERO_INV 不会生成二值图,会保留一部分灰度信息,用于亮度筛选,而不是分割。

【4】阈值 T 的取值会直接改变分割效果:阈值调大,更多像素被判定为暗;阈值调小,更多像素判定为亮。

五、 图像平滑与滤波基础概念

1 图像噪声

图像在采集传输过程引入的错误像素点,主要两类:

高斯噪声 :整体画面分布细小噪点,符合高斯正态分布。

椒盐噪声:画面随机出现孤立白点(盐)、黑点(胡椒)。

2 图像平滑(低通滤波)

图像的像素突变(边缘、噪点)属于图像高频信息;大面积缓慢变化属于低频信息。

低通滤波:保留低频,抑制高频。结果就是图像变模糊,同时消除噪声。

注意:滤波降噪是有代价的:消除噪声同时,图像物体边缘、细节会被模糊。

核 (kernel) 尺寸越大,降噪越强,图像模糊越严重。

3 方框滤波 BoxFilter

cv2.boxFilter(src, ddepth, ksize, normalize)

ksize:滤波核大小,例如 (3,3)、(5,5),必须奇数

normalize=True:邻域全部像素求平均值,就是均值滤波。

normalize=False:邻域像素直接求和,很容易像素溢出大面积变白。

原理:滑动窗口内所有像素同等权重,相加求平均。

缺点:把噪声像素也参与平均,降噪同时边缘模糊严重。

4 高斯滤波 GaussianBlur(工程最常用)

cv2.GaussianBlur(src,ksize,sigmaX)

高斯核,窗口内像素权重不是均等;卷积核中心权重最高,越往四周权重逐渐变小。

sigmaX:X 方向高斯标准差;sigmaY 不填默认等于 sigmaX。

适合:高斯噪声。

优点:相比方框滤波,边缘保留效果更好,现实项目最常用平滑算法。

5 中值滤波 medianBlur

cv2.medianBlur(src, ksize)

ksize 必须是大于 1 的奇数。

非线性滤波,不做加权求和。把窗口内所有像素排序,取中间值作为输出像素。

噪点(孤立白点黑点)一般是极值,排序后会被直接丢弃。

特别适合 椒盐噪声。

对高斯噪声效果一般;会抹除细小纹理细节。

6 三类滤波横向对比表
滤波类型 原理 适合噪声 缺点
方框滤波 邻域像素求平均 轻微高斯噪声 边缘模糊严重
高斯滤波 高斯加权平均 高斯噪声 不能完美去除椒盐噪点
中值滤波 邻域取像素中值 椒盐噪声 丢失细小纹理细节
7.代码
python 复制代码
import cv2
import numpy as np

# 定义函数:给图片添加椒盐噪声(胡椒盐噪声,随机白点、黑点)
def add_peppersalt_noise(image,n = 10000):
    result = image.copy()  #复制图片,对图片的副本进行处理,以防修改了原图
    h,w = image.shape[:2]  #选取图片的高和宽,彩色图片image.shape返回三个值[高,宽,通道数],灰度图只返回[高,宽]
    # 循环n次,生成n个噪声点
    for i in range(n):
        # 随机生成x行坐标,范围0~图片高度
        x = np.random.randint(0 , h)
        # 随机生成y列坐标,范围0~图片宽度
        y = np.random.randint(0 , w)
        # 随机0或1
        if np.random.randint(0,2) == 0:
            result[x,y] =  0   # 0为黑色,胡椒噪声点
        else:
            result[x,y] = 255  # 255为白色,盐噪声点
    return result

# 读取原始图片
image = cv2.imread('11.jpg')
# 修改图片尺寸为400×400
image =cv2.resize(image,(400,400))
# 显示原图
cv2.imshow('yuantu',image)
cv2.waitKey(0)

# 调用函数,生成带椒盐噪声的图片
noise = add_peppersalt_noise(image)
cv2.imshow('noise',noise)
cv2.waitKey(0)

# ==========均值滤波 cv2.blur()==========
# (3,3)代表3×3的卷积核,邻域内所有像素求平均值作为输出,去除噪声
blur_1 = cv2.blur(noise,(3,3))
cv2.imshow('blur_1',blur_1)
cv2.waitKey(0)

# (63,63)很大的卷积核,模糊效果极强,图片会变得非常糊
blur_2 = cv2.blur(noise,(63,63))
cv2.imshow('blur_2',blur_2)
cv2.waitKey(0)

cv2.destroyAllWindows()
python 复制代码
import cv2
import numpy as np
def add_peppersalt_noise(image,n = 10000):
    result = image.copy()#复制图片,对图片的副本进行处理,以防修改了原图
    h,w = image.shape[:2]#选取图片的高和宽,彩色图片image.shape返回三个值[高,宽,通道数],灰度图只返回[高,宽]
    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

image = cv2.imread('11.jpg')
image =cv2.resize(image,(400,400))
noise = add_peppersalt_noise(image)
cv2.imshow('yuantu',image)
cv2.waitKey(0)


#放框滤波
###src 是需要处理的图像,即原始图像。
# # # # # ddepth是处理结果图像的图像深度,一般使用-1表示与原始图像使用相同的图像深度。(可以理解为数据类型)
######ksize 是滤波核的大小。滤波核大小是指在滤波处理过程中所选择的邻域图像的高 度和宽度。
###### anchor 是锚点,(指对应哪个区域)
# #####normalize 表示在滤波时是否进行归一化。
####1.当值为True时,归一化,用邻域像素值的和除以面积。此时方框滤波与 均值滤波 效果相同
####2.当值为False时,不归一化,直接使用邻域像素值的和。和>255时使用255

boxFilter_1 = cv2.boxFilter(noise,-1,(3,3),normalize= True)#
cv2.imshow('boxFilter_1',boxFilter_1)
cv2.waitKey(0)
boxFilter_2 = cv2.boxFilter(noise,-1,(3,3),normalize= False)
cv2.imshow('boxFilter_2',boxFilter_2)
cv2.waitKey(0)

#高斯滤波
### cv2.GaussianBlur(src,ksize[,sigmaX[,sigmaY[,dst]]])高斯滤波
# # # 参数说明:
# # # src:输入图像,通常是一个NumPy数组。
# # # ksize:滤波器的大小,它是一个元组,表示在水平和垂直方向上的像素数量。例如,(5,5)表示一个5x5的滤波器。
# ### sigmaX和sigmay:分别表示在X轴和Y轴方向上的标准差。这些值与滤波器大小相同。默认情况下,它们都等于0,这意味着没有高斯模糊。# # # dst:输出图像,通常是一个NumPy数组。如果为None,则会创建一个新的数组来存储结果。
GaussianB = cv2.GaussianBlur(noise,(3,3),1)
cv2.imshow('GaussianBlur',GaussianB)
cv2.waitKey(0)

#中值滤波
# cv2.medianBlur(src,ksizel,dst])中值滤波
# 参数说明:
# src:输入图像。
# ksize:滤波器的大小,它是一个整数,表示在水平和垂直方向上的像素数量。例如,5表示一个5x5的滤波器。
# dst:输出图像,通常是一个NumPy数组。如果为None,则会创建一个新的数组来存储结果。
mediamB = cv2.medianBlur(noise,3)
cv2.imshow('medianBlur',mediamB)
cv2.waitKey(0)
cv2.destroyAllWindows()
#中值滤波效果最好

六、本期完整核心 API 汇总

python 复制代码
# =========图像运算=========
cv2.add()
cv2.addWeighted()

# =========边界填充=========
cv2.copyMakeBorder()

# =========阈值处理=========
cv2.threshold()

# =========滤波平滑=========
cv2.boxFilter()      #方框滤波
cv2.GaussianBlur()   #高斯滤波
cv2.medianBlur()     #中值滤波

七、本期总结

图像像素相加区分 NumPy 取模加法 和 OpenCV 饱和加法,图像处理优先使用 cv2.add。加权融合需要两张图像尺寸通道完全一致。

copyMakeBorder 五种填充模式,REFLECT_101 镜像填充最适合卷积扩边;CONSTANT 用于人为增加纯色边框。填充的目的是解决卷积后图像尺寸收缩。

threshold 阈值处理输入必须是灰度图像,5 种阈值模式,二值化模式最常用于图像分割。

图像平滑本质低通滤波,牺牲图像细节换取噪声抑制。

噪声类型决定滤波选择:高斯噪声选高斯滤波;椒盐噪声优先中值滤波;方框滤波使用较少。

滤波核越大降噪越强,但图像细节丢失越严重,实际项目需要权衡核大小。

相关推荐
呱呱巨基1 小时前
Docker 基础概念学习
linux·c++·学习·docker
AI的探索之旅1 小时前
嵌入式视觉学习路线:Qt + OpenCV + 工业相机,从零到综合项目
qt·opencv·学习
sakiko_1 小时前
Swift学习笔记39-实战注意事项
前端·笔记·学习·swift
短视频矩阵源码定制1 小时前
个性化学习机构四大模式深度评测与选型指南
人工智能·学习
xiaoxiangsiyan2 小时前
企业网络运维自动化与管理协议深度指南
linux·运维·服务器·网络·学习·自动化
数智工坊2 小时前
SAMosaic3D:以可学习马赛克组装实现实时在线3D实例分割 | CVPR 2026 论文精读
学习·3d
Aision_2 小时前
实习学习笔记:HTTP请求走私漏洞全方位解析
笔记·学习·安全·web安全·http·网络安全·网络攻击模型
GlueNa2SiO33 小时前
07-Docker Compose多容器编排
笔记·学习·docker·容器
brave_zhao3 小时前
vetur是什么
学习