1. DNN模块是什么?
OpenCV DNN(Deep Neural Networks)模块是一个用于深度神经网络推理的模块,它允许用户在OpenCV中加载和运行预训练的深度学习模型。主要特点:
-
跨框架支持:支持TensorFlow、PyTorch、Caffe、DarkNet、ONNX等多种框架训练的模型
-
硬件加速:支持CPU、GPU(CUDA、OpenCL)推理
-
轻量级:无需安装完整的深度学习框架
-
实时推理:针对计算机视觉任务优化,适合实时应用
2. 核心函数详解
2.1 cv2.dnn.blobFromImage() - 图像预处理
python
blob = cv2.dnn.blobFromImage(
image, # 输入图像(BGR格式)
scalefactor=1.0, # 像素值缩放因子(通常为1/255)
size=(w, h), # 输出blob的尺寸(模型要求的输入尺寸)
mean=(0, 0, 0), # 各通道减去的均值(通常是训练时的均值)
swapRB=True, # 是否交换R和B通道(OpenCV用BGR,模型通常用RGB)
crop=False # 是否中心裁剪
)
参数详细说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
image |
ndarray | - | 输入图像,OpenCV读取的BGR格式图像 |
scalefactor |
float | 1.0 | 像素值缩放系数: pixel = pixel × scalefactor 常用值:1/255.0(归一化到0-1) |
size |
tuple | - | 输出blob的(width, height),必须是模型要求的输入尺寸 |
mean |
tuple/scalar | (0,0,0) | 各通道要减去的均值: pixel = pixel - mean 常用值:(104, 117, 123)(Caffe模型)或(0.485, 0.456, 0.406)(ImageNet均值) |
swapRB |
bool | False | 是否交换R和B通道: True:BGR→RGB False:保持BGR |
crop |
bool | False | 是否中心裁剪: True:调整大小后中心裁剪 False:直接resize |
返回值:
-
blob:4D数组,形状为(1, C, H, W),其中:-
1:batch size(单张图像) -
C:通道数(通常为3) -
H:高度 -
W:宽度
-
2.2 cv2.dnn.readNet() - 加载模型
各参数的含义如下:
model: 神经网络的实际结构和功能。它定义了数据如何通过网络流动,如何进行训练,如何进行推理
config: 一组组参数和设置,帮助控制模型的行为,包括网络架构、训练过程、优化器等内容
framework: DNN框架,可省略。DNN模块会自动推断框架种类
net: 返回值,返回网络模型对象
支持的主要模型格式:
| 框架 | 模型文件 | 配置文件 | 专用函数 |
|---|---|---|---|
| Caffe | .caffemodel |
.prototxt |
readNetFromCaffe() |
| TensorFlow | .pb |
.pbtxt |
readNetFromTensorflow() |
| PyTorch | .t7或.pth |
- | readNetFromTorch() |
| Darknet | .weights |
.cfg |
readNetFromDarknet() |
| ONNX | .onnx |
- | readNetFromONNX() |
| OpenVINO | .xml |
.bin |
readNetFromModelOptimizer() |
参数说明:
-
model:包含训练权重的文件 -
config:网络架构定义文件(对于某些框架) -
framework:显式指定框架类型(通常可自动推断)
2.3 其他重要函数
python
# 设置输入
net.setInput(blob, name="input") # name可选,指定输入层名称
# 前向传播
output = net.forward(outputName=None) # 输出指定层的结果
# 获取网络信息
layer_names = net.getLayerNames() # 获取所有层名
unconnected_layers = net.getUnconnectedOutLayers() # 获取输出层索引
3. 注意事项
-
通道顺序 :OpenCV默认BGR,许多模型期望RGB,注意
swapRB参数 -
归一化:确保预处理与模型训练时一致
-
尺寸匹配:输入尺寸必须与模型要求一致
-
均值减法:如果模型训练时进行了均值减法,推理时也要做
-
输出解析:不同模型的输出格式不同,需要根据具体任务解析
DNN模块为在OpenCV中集成深度学习模型提供了统一接口,大大简化了深度学习模型的部署过程。
4.实际运用
1)对图片进行风格迁移
python
import cv2
# 读取输入图像
image = cv2.imread('xiaomao.jpg')
image=cv2.resize(image,dsize=None,fx=0.5,fy=0.5)
# 显示输入图像
cv2.imshow('yuan', image)
cv2.waitKey(0)
# 图片预处理
(h, w) = image.shape[:2] # 获取图像尺寸
blob = cv2.dnn.blobFromImage(image, scalefactor=1, size=(w, h), mean=(0, 0, 0), swapRB=False, crop=False)
net = cv2.dnn.readNet('model\\candy.t7') # 得到一个用torch训练之后的星空模型
# 设置输入
net.setInput(blob)
# 对输入图像进行前向传播,得到输出结果
out = net.forward()
# 将输出结果转换为合适的格式
# out是四维的:B*C*H*W
# B: batch图像数量(通常为1),C: channels通道数,H: height高度,W: width宽度
# 输出处理
# 重塑形状(忽略第1维),4维变3维
# 调整输出out的形式,模型推理输出out是四维BCHW形式的,调整为三维CHW形式
out_new = out.reshape(out.shape[1], out.shape[2], out.shape[3])
# 对输入的数组(或图像)进行归一化处理,使其数值范围在指定的范围内
cv2.normalize(out_new, out_new, norm_type=cv2.NORM_MINMAX)
# 转置输出结果的维度
result = out_new.transpose(1, 2, 0)
# 显示转换后的图像
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

2)对视频或摄像头进行风格迁移
python
import cv2
# 加载模型
net = cv2.dnn.readNet('model\\candy.t7') # 加载风格迁移模型
# 打开视频文件或摄像头
# 如果是视频文件,使用:cv2.VideoCapture('your_video.mp4')
# 如果是摄像头,使用:cv2.VideoCapture(0) # 0表示默认摄像头
cap = cv2.VideoCapture(0) # 修改为你的视频文件路径
while True:
# 读取视频帧
ret, frame = cap.read()
# 如果读取失败(视频结束),退出循环
if not ret:
print("视频结束或读取失败")
break
# 可选:调整帧大小(如果太大可以缩小)
frame = cv2.resize(frame, None, fx=0.4, fy=0.4)
# 获取当前帧的尺寸
(h, w) = frame.shape[:2]
# 预处理图像,创建blob
blob = cv2.dnn.blobFromImage(
frame,
scalefactor=1.0,
size=(w, h),
mean=(0, 0, 0),
swapRB=False,
crop=False
)
# 设置输入并进行前向传播
net.setInput(blob)
out = net.forward()
# 处理输出结果
# 重塑形状:4维(B,C,H,W) -> 3维(C,H,W)
out_new = out.reshape(out.shape[1], out.shape[2], out.shape[3])
# 归一化
cv2.normalize(out_new, out_new, norm_type=cv2.NORM_MINMAX)
# 转置维度:(C,H,W) -> (H,W,C)
result = out_new.transpose(1, 2, 0)
# 或者只显示风格化后的帧
cv2.imshow('shipin', result)
# 可选:保存到输出视频
# out.write(result)
# 按'esc'键退出
key = cv2.waitKey(30)
if key == 27: # 按下Esc键,退出循环
break
# 释放资源
cap.release()
# out.release() # 如果使用了VideoWriter,取消注释
cv2.destroyAllWindows()

5.延申
任务
将摄像头实时画面分割成四个方格,每个方格应用不同的艺术风格滤镜,最后拼接成一个完整画面展示出来。
(传入的图片缩放一下再处理,不然会卡顿)
python
import cv2
import numpy as np
cap = cv2.VideoCapture(0) # 0 表示默认摄像头
# 加载四种风格迁移模型
net1 = cv2.dnn.readNetFromTorch(r'model\starry_night.t7') # 星夜
net2 = cv2.dnn.readNetFromTorch(r'model\the_scream.t7') # 呐喊
net3 = cv2.dnn.readNetFromTorch(r'model\feathers.t7') # 羽毛
net4 = cv2.dnn.readNetFromTorch(r'model\mosaic.t7') # 马赛克
# 风格迁移处理函数(修正版)
def qianyi(frame,net):
blob = cv2.dnn.blobFromImage(frame, 1, (w, h), (0, 0, 0), swapRB=True, crop=False)
net.setInput(blob)
out = net.forward()
out_new = out.reshape(out.shape[1], out.shape[2], out.shape[3])
cv2.normalize(out_new, out_new, norm_type=cv2.NORM_MINMAX)
result = out_new.transpose(1, 2, 0)
return result
while True:
ret, frame = cap.read()
frame = cv2.resize(frame, dsize=None, fx=0.3, fy=0.3) # 缩小到30%
if not ret:
print("不能读取摄像头")
break
(h, w) = frame.shape[:2]
# 将画面分为四个区域
zs = frame[:h // 2, 0:w // 2] # 左上
ys = frame[:h // 2, w // 2:] # 右上
zx = frame[h // 2:, :w // 2] # 左下
yx = frame[h // 2:, w // 2:] # 右下
# 分别对四个区域应用不同风格
qianyi_zs = qianyi(zs, net1) # 左上:星夜
qianyi_ys = qianyi(ys, net2) # 右上:呐喊
qianyi_zx = qianyi(zx, net3) # 左下:羽毛
qianyi_yx = qianyi(yx, net4) # 右下:马赛克
# 拼接结果
shang = np.hstack((qianyi_zs, qianyi_ys)) # 上半部分
xia = np.hstack((qianyi_zx, qianyi_yx)) # 下半部分
result = np.vstack((shang, xia)) # 完整结果
cv2.imshow('result', result)
key_pressed = cv2.waitKey(1)
if key_pressed == 27: # ESC键退出
break
cap.release()
cv2.destroyAllWindows()
