目录
一、图像修改
图片打码
python
import cv2
import numpy as np
# 图片打码
a = cv2.imread(r'./xin.jpg')
xxx = np.random.randint(0, 256, (3, 3))
print(xxx)
a[100:200, 200:300] = np.random.randint(0, 256, (100, 100, 3))
cv2.imshow('masaike', a)
cv2.waitKey(0)
cv2.destroyAllWindows()
-
图片本质是三维numpy数组:(高度, 宽度, 3个RGB通道),可通过数组切片直接修改像素。
-
切片语法:a高度起始:高度结束, 宽度起始:宽度结束,对应图片的矩形区域。
-
np.random.randint(0,256,(h,w,3)):生成0~255的随机像素值,模拟马赛克效果。
注意:
赋值的数组大小必须完全匹配切片区域的大小
切片:100:200 高度共 100行,200:300 宽度共 100列
随机数组必须是 (100,100,3),行列数量不一致会直接报错。
运行效果

图片组合
python
# 图片组合
a = cv2.imread('xin.jpg')
b = cv2.imread('o.png')
b[200:350, 200:350] = a[50:200, 100:250]
cv2.imshow('b', b)
cv2.imshow('a', a)
cv2.waitKey(0)
cv2.destroyAllWindows()
-
图片像素可以跨图片复制粘贴:将一张图的像素区域,直接赋值给另一张图的区域。
-
原理:OpenCV图片均为numpy数组,数组之间支持直接赋值操作。
注意:
两张图片的切片区域,尺寸必须严格一致,行列数量不匹配,会直接报错。
运行效果

图片缩放
python
# 图片缩放
a = cv2.imread('xin.jpg')
# 写法1:直接指定宽高缩放
a_new = cv2.resize(a, dsize=(600,200))
# 写法2:按比例缩放(fx宽比例、fy高比例)
# a_new = cv2.resize(a, None, fx=1.5, fy=0.5)
cv2.imshow('a',a)
cv2.imshow('a_new',a_new)
cv2.waitKey(0)
cv2.destroyAllWindows()
两个核心使用方式:
方式1:dsize=(宽度, 高度),直接指定输出图片的宽高
方式2:fx、fy 缩放系数,fx=1.5代表宽度放大1.5倍,fy=0.5代表高度缩小一半
注意:
-
dsize 格式为 (宽, 高),与图片数组(高,宽)顺序相反,容易写反。
-
两种缩放方式二选一:指定dsize后,fx/fy会自动失效。
-
缩小图片会丢失细节,放大图片会产生模糊,无法还原原始清晰度。
运行效果

二、图像运算
图片加法
python
import cv2
# 图像加法运算:当某位置像素相加得到的数值大于255时,将该位置数值相加结果阶段减去256(例如:相加后得到260,实际是260-256=4)
a = cv2.imread('mimi.jpg')
b = cv2.imread('xin_new.jpg')
c = a+100
cv2.imshow('yuan', a)
cv2.imshow('a+10', c)
cv2.waitKey(0)
c = a[50:450, 50:450]+b[50:450, 50:450]
cv2.imshow('a+b', c)
cv2.waitKey(0)
-
像素和 <255:结果 = 两像素直接相加
-
像素和 >255:执行取模运算(循环截断),公式:结果 = 总和 - 256
例:260 → 260-256 = 4
注意:
他的本质是 numpy 数组运算,遵循 uint8 类型的循环溢出规则
两张图片相加时,参与运算的区域尺寸必须完全一致,否则直接报错
运行效果

cv2.add()运算
python
# cv2.add()运算规则:当某位置像素相加得到的数字大于255时,该位置数值为255
a = cv2.imread('mimi.jpg')
b = cv2.imread('xin_new.jpg')
b = cv2.resize(b, (400, 400))
a = cv2.resize(a, (400, 400))
c = cv2.add(a, b)
cv2.imshow('a add b', c)
cv2.waitKey(0)
cv2.destroyAllWindows()
-
像素和 <255:结果 = 两像素直接相加
-
像素和 >255:执行饱和截断,直接固定最大值为255
注意:
-
他与 + 运算最大区别是,溢出后不会归零,直接保持纯白255
-
两张图片宽高必须完全一致,否则运算直接报错
-
专门用于图像融合,是OpenCV官方推荐的加法方式
运行效果

图片加权运算
python
# 图像加权运算:计算两张图片的像素值之和,将每张图的权重都考虑进来(公式c像素=a像素×α权重+b像素×β权重+γ偏移量/变亮变暗)
a = cv2.imread('mimi.jpg')
b = cv2.imread('xin_new.jpg')
b = cv2.resize(b, (400, 400))
a = cv2.resize(a, (400, 400))
c = cv2.addWeighted(a, 0.5, b, 0.4, 10)
cv2.imshow('addWeighed', c)
cv2.waitKey(0)
cv2.destroyAllWindows()
公式:dst = src1 × α + src2 × β + γ
alpha(α):第一张图的权重系数(透明度,0~1之间)
beta(β):第二张图的权重系数(透明度,0~1之间)
gamma(γ):全局亮度偏移值,给整张融合图片统一提亮/压暗
注意:
-
同样要求两张图片尺寸完全一致
-
权重之和 alpha+beta 通常设为1,保证亮度均衡;大于1画面会整体变亮
-
gamma为最终提亮值,正数提亮、负数压暗,是调整画面明暗的常用参数
运行效果

阈值处理
剔除图像内像素值高于/低于设定阈值的像素点,实现图像二值分割(黑白区分)。
标准函数格式:
retval, dst = cv2.threshold(src, thresh, maxval, type)
-
src:需要做阈值分割的图像,必须先转为灰度图(代码中 flags=0 就是读取灰度图)
-
thresh:手动设定的分割阈值
-
maxval:像素超过阈值时填充的最大值
-
type:阈值分割的5种类型
返回值
retval:最终生效的阈值(手动设置时和thresh一致,自动阈值算法才会变化)
dst:阈值处理后的结果图像
| 阈值类型 | 像素值 > 175 | 其他情况(≤175) | 含义 |
|---|---|---|---|
| cv2.THRESH_BINARY | 设为maxval(255)(纯白) | 设为0(纯黑) | 标准二值化:亮部变白,暗部变黑 |
| cv2.THRESH_BINARY_INV | 设为0(纯黑) | 设为maxval(255)(纯白) | 反二值化:亮部变黑,暗部变白 |
| cv2.THRESH_TRUNC | 固定为阈值175 | 保持原灰度值 | 高光截断:超过阈值的像素统一变暗,画面呈现灰调 |
| cv2.THRESH_TOZERO | 保持原灰度值 | 设为0(纯黑) | 暗部抑制:低于阈值的像素变黑,亮部保持不变 |
| cv2.THRESH_TOZERO_INV | 设为0(纯黑) | 保持原灰度值 | 亮部抑制:高于阈值的像素变黑,暗部保持不变 |
python
import cv2
image = cv2.imread('zl.png', 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)
cv2.imshow('gray', image)
cv2.waitKey(0)
cv2.imshow('binary', binary)
cv2.waitKey(0)
cv2.imshow('binaryinv', binaryinv)
cv2.waitKey(0)
cv2.imshow('trunc', trunc)
cv2.waitKey(0)
cv2.imshow('tozero', tozero)
cv2.waitKey(0)
cv2.imshow('tozeroinv', tozeroinv)
cv2.waitKey(0)
-
强制灰度图:threshold 函数只能接收单通道灰度图,彩色图必须先转灰度,否则直接报错
-
maxval仅2种模式生效:只有 BINARY / BINARY_INV 会用到maxval,另外3种类型该参数无效
-
像素取值范围:所有阈值处理基于 0~255 的uint8像素值,超出范围会直接截断
运行效果

三、图像平滑处理
椒盐噪声生成
给图片添加椒盐噪声(黑白随机噪点)
-
定义生成椒盐噪点的函数
-
读取原图并显示
-
调用函数生成带噪点的图片
-
显示带噪点的图片
python
import cv2
import numpy as np
# 导入OpenCV图像处理库与数值计算库,是所有OpenCV项目的基础依赖。
# 定义【椒盐噪声生成函数】
def add_peppersalt_noise(image, n=10000):
# 复制原图,避免直接修改原始图片
result = image.copy()
# 获取图片的高度、宽度(shape[:2]只取前两个维度,排除RGB通道)
h, w = image.shape[:2]
# 循环n次,生成n个随机噪点
for i in range(n):
# 随机生成x坐标(纵向:0 ~ 图片高度h之间)
x = np.random.randint(0, h)
# 随机生成y坐标(横向:0 ~ 图片宽度w之间)
y = np.random.randint(0, w)
# 50%概率生成黑点,50%概率生成白点
if np.random.randint(0, 2) == 0:
result[x, y] = 0 # 0 = 纯黑色(胡椒噪点)
else:
result[x, y] = 255 # 255 = 纯白色(盐粒噪点)
# 返回添加完噪点的图片
return result
函数核心知识点1. 椒盐噪声定义:0 → 黑色胡椒噪点,255 → 白色盐粒噪点
-
参数n=10000:噪点的总数量,数值越大噪点越密集,画面越模糊。
-
image.copy():复制原图操作,核心原则:绝对不能直接修改原始图片。
-
np.random.randint(0,2):随机生成0或1,保证黑白噪点的生成概率各占一半。
python
# 读取原始图片
image = cv2.imread('xin_new.png')
# 显示原图
cv2.imshow('yntu', image)
cv2.waitKey(0) # 等待按键,不按则窗口一直停留
# 调用函数,给图片添加椒盐噪点
noise = add_peppersalt_noise(image)
# 显示添加噪点后的图片
cv2.imshow('noise', noise)
cv2.waitKey(0)
注意:
-
彩色图兼容:该函数可直接用于彩色图片,代码会自动给3个RGB通道同时添加黑白噪点,无需额外修改。
-
噪点数量调整:
n=5000:轻微噪点
n=10000:常规密度噪点(当前代码默认值)
n=50000:重度噪点,画面会严重受损
滤波处理
均值滤波
python
blur_1 = cv2.blur(noise, (3, 3))
cv2.imshow('blur_1', blur_1)
cv2.waitKey(0)
blur_2 = cv2.blur(noise, (63, 63))
cv2.imshow('blur_2', blur_2)
cv2.waitKey(0)
-
参数 (3,3):卷积核大小,代表取周围3×3=9个像素,求平均值替换中心像素
-
原理:邻域所有像素取平均值,让整体画面模糊平滑
-
两组对比效果:
(3,3):小卷积核,轻微模糊,能去除部分噪点
(63,63):大卷积核,画面极度模糊,丢失大量细节
去除噪声效果差,只能弱化噪点,无法彻底清除,画面会整体发糊。
效果展示

方框滤波
python
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)
-
-1:输出图像深度,固定写 -1 即可,代表和原图格式保持一致
-
(3,3):卷积核大小,和均值滤波完全一致
-
normalize
True 归一化:求和后÷像素数量,结果和 cv2.blur 均值滤波一模一样
False 不归一化:直接累加所有像素值,数值极易超过255,画面会变成几乎纯白色
方框滤波本质是均值滤波的通用版,仅归一化模式有实际使用价值。
高斯滤波
python
GaussianB = cv2.GaussianBlur(noise, (3,3), sigmaX=1)
cv2.imshow('GaussianBlur',GaussianB)
cv2.waitKey(0)
-
原理:距离中心越近的像素权重越高,模拟人眼观察效果,模糊效果更自然
-
参数 sigmaX=1:X方向高斯标准差,数值越大,模糊程度越强
去噪效果优于均值滤波,画面不会出现生硬的方块模糊,但对椒盐噪声的去除效果依旧一般,仅能弱化噪点,无法彻底清除
中值滤波
python
medianB = cv2.medianBlur(noise, 3)
cv2.imshow('medianBlur',medianB)
cv2.waitKey(0)
cv2.destroyAllWindows()
-
参数 3:卷积核尺寸,必须是奇数(3、5、7......)
-
原理:取3×3邻域内所有像素的中间值,直接剔除0和255的极值噪点
去除椒盐噪声效果最好,能直接清除黑白噪点,画面保留细节,不会像均值滤波一样整体严重发糊
效果展示

视频平滑处理作业
给定一个视频文件:'test.avi',要求完成以下操作:
1、使其每一帧画面都随机生成10000个黑白点(椒盐噪声)添加噪声效果,
2、然后选择合适的图像平滑处理方法,处理每一帧画面,消除噪声的同时尽可能使原画面清晰,
3、同时弹出三个窗口,分别展示原视频、含有噪声的视频、平滑处理后的视频。
代码实现
python
import cv2
import numpy as np
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)
if np.random.randint(0, 2) == 0:
result[x, y] = 0
else:
result[x, y] = 255
return result
video_capture = cv2.VideoCapture('test.avi')
if not video_capture.isOpened():
print("无法打开视频文件")
exit()
while True:
ret, frame = video_capture.read()
if not ret:
break
noise_frame = add_peppersalt_noise(frame, n=10000)
medianB_frame = cv2.medianBlur(noise_frame, 3)
cv2.imshow('yuan Video', frame)
cv2.imshow('Noise Video', noise_frame)
cv2.imshow('medianB Video', medianB_frame)
if cv2.waitKey(100) == 27:
break
video_capture.release()
cv2.destroyAllWindows()
代码解读
整体逻辑流程
-
定义函数:给单张图片添加椒盐黑白噪点
-
打开视频文件,循环读取每一帧画面
-
给每一帧视频画面添加噪点
-
使用中值滤波去除椒盐噪点
-
同时显示「原图、带噪视频、去噪后视频」三个窗口,对比效果
-
按下ESC键结束程序
第一部分:椒盐噪声生成函数
python
import cv2
import numpy as np
# 定义椒盐噪声生成函数
def add_peppersalt_noise(image, n=10000):
# 复制原图,绝对不修改原始图片
result = image.copy()
# 获取图片高度、宽度(只取前两个维度,排除RGB通道)
h, w = image.shape[:2]
# 循环n次,生成n个随机噪点
for i in range(n):
# 随机生成纵向坐标x(0 ~ 图片高度之间)
x = np.random.randint(0, h)
# 随机生成横向坐标y(0 ~ 图片宽度之间)
y = np.random.randint(0, w)
# 50%概率生成黑点,50%概率生成白点
if np.random.randint(0, 2) == 0:
result[x, y] = 0 # 0 = 纯黑色(胡椒噪点)
else:
result[x, y] = 255 # 255 = 纯白色(盐粒噪点)
return result
第二部分:视频读取 + 逐帧处理逻辑
python
# 打开视频文件test.avi
video_capture = cv2.VideoCapture('test.avi')
# 判断视频是否成功打开
if not video_capture.isOpened():
print("无法打开视频文件")
exit()
# 无限循环读取视频每一帧
while True:
# 读取一帧画面:ret=是否读取成功,frame=当前帧图片
ret, frame = video_capture.read()
# 视频播放完毕,跳出循环结束
if not ret:
break
# 1. 给当前帧添加椒盐噪点
noise_frame = add_peppersalt_noise(frame, n=10000)
# 2. 中值滤波去除椒盐噪点(3×3卷积核,去噪最优方案)
medianB_frame = cv2.medianBlur(noise_frame, 3)
# 三个窗口同步显示,实时对比效果
cv2.imshow('yuan Video', frame) # 原始干净视频
cv2.imshow('Noise Video', noise_frame) # 布满黑白噪点的视频
cv2.imshow('medianB Video', medianB_frame) # 中值滤波去噪后的视频
# 按下ESC键(按键码27)退出循环
if cv2.waitKey(100) == 27:
break
# 释放视频资源,关闭所有窗口
video_capture.release()
cv2.destroyAllWindows()
cv2.VideoCapture('test.avi'):打开视频,视频本质就是连续播放的图片帧
while True 循环:一帧一帧读取视频画面,逐帧处理
关键执行顺序:原图帧 → 添加椒盐噪点 → 中值滤波去噪,处理顺序不能颠倒
中值滤波去除椒盐噪点的效果最好,必须先生成噪点,再去噪
卷积核尺寸必须是奇数(3、5、7......),偶数会直接报错
waitKey(100):控制视频播放速度,数值越大播放越慢
视频读取完毕后会自动结束循环,不会卡死程序
运行展示
