OpenCV计算机视觉实战(28)------深度学习初体验
0. 前言
深度学习已彻底改变了计算机视觉领域,从图像分类到目标检测,再到艺术风格迁移,强大的预训练模型让我们在几行代码内就能实现昔日难以企及的效果。本文将首先演示如何无缝加载 Caffe、ONNX、Torch 等多种模型格式并进行高效批量推理;接着用 YOLOv3 在进行实时多尺度目标检测,并通过类别配色、非极大值抑制与 FPS 统计,打造实用的检测流程;最后,加载多款风格迁移网络,轻松为视频流添加艺术特效。
1. DNN 模块使用指南
OpenCV 的 cv2.dnn 模块支持加载各种主流深度学习框架导出的模型( Caffe、TensorFlow、ONNX、Torch 等),并在 CPU/GPU 上高效推理,接下来,展示如何加载 Caffe 模型进行前向推理。
1.1 实现过程
- 加载模型
- 使用 
cv2.dnn.readNetFromCaffe加载Caffe导出的.prototxt和.caffemodel,同时支持GPU加速(需调用net.setPreferableBackend) 
 - 使用 
 - 输入预处理
blobFromImage将H × W × C图像转换为NCHW格式,并进行缩放、均值减除、通道交换等操作,符合网络训练时的输入规范scalefactor:像素缩放系数mean:对每个通道减去的均值
 - 前向推理
net.setInput(blob):将预处理后的blob传入网络net.forward():执行一次完整的前向传播,返回输出多维数组
 - 结果解析
- 对分类输出做 
argsort,取概率最高的前5个类别即可 
 - 对分类输出做 
 
            
            
              python
              
              
            
          
          import cv2
import numpy as np
# 功能:演示 DNN 模块加载 Caffe 模型并进行分类推理
# 1. 加载网络结构与预训练权重
net = cv2.dnn.readNetFromCaffe(
    'bvlc_googlenet.prototxt', 
    'bvlc_googlenet.caffemodel'
)
# 2. 读取并预处理输入图像
img = cv2.imread('2.jpeg')
blob = cv2.dnn.blobFromImage(
    img, 
    scalefactor=1.0, 
    size=(224,224), 
    mean=(104,117,123), 
    swapRB=False, 
    crop=False
)
# 3. 设置网络输入,执行前向推理
net.setInput(blob)
preds = net.forward()
# 4. 解析输出(取 top-5 类别)
idxs = np.argsort(preds.flatten())[::-1][:5]
for i in idxs:
    print(f'ClassID: {i}, Score: {preds[0,i]:.4f}')
        关键函数解析:
cv2.dnn.readNetFromCaffe(proto,model):加载Caffe模型(结构 & 权重)cv2.dnn.blobFromImage(img, ...):对输入图像执行缩放、均值减除、通道交换、NCHW排序net.setInput(blob):将预处理后的数据设置为网络输入net.forward():执行前向推理并返回输出
1.2 优化思路
- 多模型格式支持:除了 
Caffe,还可用readNetFromTensorflow、readNetFromONNX加载TensorFlow、ONNX模型 - 后端与目标设备:可通过 
setPreferableBackend/Target切换OpenVINO、CUDA、Halide等加速方案 - 批量推理:用 
blobFromImages实现批量输入,提高GPU利用率 
            
            
              python
              
              
            
          
          import cv2
import numpy as np
# 功能:同时支持 ONNX 与 Caffe,演示 CPU/GPU 加速与批量推理
# 选择模型格式
use_onnx = True
if use_onnx:
    net = cv2.dnn.readNetFromONNX('resnet50.onnx')
else:
    net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'resnet50.caffemodel')
# 切换到 CUDA(若可用)或 OpenVINO
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)
# 批量加载多张图
imgs = [cv2.imread(p) for p in ['img1.jpg','img2.jpg','img3.jpg']]
blobs = cv2.dnn.blobFromImages(
    imgs,
    scalefactor=1/255.0,
    size=(224,224),
    mean=(0.485,0.456,0.406),
    swapRB=True,
    crop=False
)
# 归一化到 ImageNet 标准
blobs /= np.array([0.229,0.224,0.225]).reshape(3,1,1)
net.setInput(blobs)
outs = net.forward()  # 3×1000 分类得分
# 解析每张图的 top1
for i, out in enumerate(outs):
    idx = out.argmax()
    score = out[idx]
    print(f'Image {i}: Class {idx}, Score {score:.3f}')
        关键函数解析:
readNetFromONNX(path):加载ONNX标准模型setPreferableBackend/Target():切换计算后端,常见后端有CUDA、OpenVINO、HalideblobFromImages(list, ...):一次构造多图的批量输入Blob,减少多次数据上传开销
2. YOLO 实时检测
使用 Darknet 格式的 YOLOv3 模型,在摄像头或视频帧上实时检测常见物体。YOLO 将整张图分成网格,一次前向计算即可输出所有边界框与类别。
2.1 实现过程
- 加载 
Darknet模型readNetFromDarknet支持.cfg + .weights- 调用 
setPreferableBackend/Target可开启GPU或OpenVINO加速 
 - 获取输出层
getUnconnectedOutLayers返回需手动forward的层索引,用于提取检测结果
 - 输入预处理
- 图像缩放到 
416 × 416,像素值归一化到[0, 1],并交换RB通道 
 - 图像缩放到 
 - 后处理
- 根据置信度过滤检测框
 - 用 
NMS(NMSBoxes) 剔除重叠过多的框 - 绘制剩余边界框和标签
 
 
            
            
              python
              
              
            
          
          import cv2
import numpy as np
# 功能:载入 YOLOv3 模型并在摄像头实时检测
net = cv2.dnn.readNetFromDarknet('yolov3.cfg', 'yolov3.weights')
# 获取输出层名称
layer_names = net.getLayerNames()
out_layers_indices = net.getUnconnectedOutLayers()
out_layers = [layer_names[i-1] for i in out_layers_indices]
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    if not ret: break
    # 构建输入 blob
    blob = cv2.dnn.blobFromImage(frame, 1/255.0, (416,416), swapRB=True, crop=False)
    net.setInput(blob)
    outs = net.forward(out_layers)
    h, w = frame.shape[:2]
    boxes, confidences, classIDs = [], [], []
    for out in outs:
        for det in out:
            scores = det[5:]
            cid = np.argmax(scores)
            conf = scores[cid]
            if conf > 0.5:
                cx, cy, bw, bh = (det[0:4] * [w, h, w, h]).astype(int)
                x, y = int(cx - bw/2), int(cy - bh/2)
                boxes.append([x,y,int(bw),int(bh)])
                confidences.append(float(conf))
                classIDs.append(cid)
    # 非极大值抑制
    idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
    if len(idxs):
        for i in idxs.flatten():
            x,y,bw,bh = boxes[i]
            cv2.rectangle(frame, (x,y), (x+bw,y+bh), (0,255,0), 2)
            cv2.putText(frame, f'{classIDs[i]}:{confidences[i]:.2f}',
                        (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0),1)
    cv2.imshow('YOLOv3 Detection', frame)
    if cv2.waitKey(1)==27: break
cap.release()
cv2.destroyAllWindows()
        
关键函数解析:
cv2.dnn.readNetFromDarknet(cfg,weights):加载YOLO Darknet模型net.getUnconnectedOutLayers():获取输出层索引blobFromImage(frame,1/255,size,swapRB):构建适用于YOLO的输入blobcv2.dnn.NMSBoxes(boxes,confs,thr,nt):非极大值抑制,过滤重叠冗余检测框
2.2 优化思路
- 多尺度检测:用多次不同尺寸的 
blobFromImage检测小物体更准确,再合并结果 - 类别名称与配色:加载 
coco.names并为每个类别分配固定颜色,提高可读性 FPS统计:实时计算并叠加FPS信息,便于性能调优
            
            
              python
              
              
            
          
          import cv2
import numpy as np
import time
# 功能:YOLOv3 多尺度检测 + 类名 + FPS 统计
net = cv2.dnn.readNetFromDarknet('yolov3.cfg','yolov3.weights')
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
# 载入类别
with open('coco.names') as f:
    classes = [line.strip() for line in f]
colors = np.random.randint(0,255,(len(classes),3))
# 输出层
ln = [net.getLayerNames()[i-1] for i in net.getUnconnectedOutLayers().flatten()]
# cap = cv2.VideoCapture(0)
cap = cv2.VideoCapture('r2.mp4')
for i in range(1440):
    ret, frame = cap.read()
prev = time.time()
while True:
    ret, frame = cap.read()
    if not ret: break
    # 多尺度输入
    outs = []
    for scale in [320, 416, 608]:
        blob = cv2.dnn.blobFromImage(frame,1/255.0,(scale,scale),swapRB=True, crop=False)
        net.setInput(blob)
        outs += net.forward(ln)
    # 后处理
    h, w = frame.shape[:2]
    boxes,confs,ids = [],[],[]
    for det in outs:
        for d in det:
            scores = d[5:]; cid = np.argmax(scores); conf = scores[cid]
            if conf>0.5:
                cx,cy,bw,bh = (d[0:4]*[w,h,w,h]).astype(int)
                x,y = int(cx-bw/2), int(cy-bh/2)
                boxes.append([x,y,bw,bh]); confs.append(float(conf)); ids.append(cid)
    idxs = cv2.dnn.NMSBoxes(boxes,confs,0.5,0.4)
    # 绘制
    for i in idxs.flatten():
        x,y,bw,bh = boxes[i]; cid = ids[i]
        color = [int(c) for c in colors[cid]]
        cv2.rectangle(frame,(x,y),(x+bw,y+bh),color,2)
        cv2.putText(frame,f'{classes[cid]}:{confs[i]:.2f}',(x,y-5),
                    cv2.FONT_HERSHEY_SIMPLEX,0.5,color,1)
    # FPS 叠加
    fps = 1/(time.time()-prev); prev=time.time()
    cv2.putText(frame,f'FPS: {fps:.1f}',(10,20),
                cv2.FONT_HERSHEY_SIMPLEX,0.6,(0,0,255),2)
    cv2.imshow('YOLO Multi-Scale', frame)
    if cv2.waitKey(1)==27:break
cap.release(); cv2.destroyAllWindows()
        
3. 图像风格迁移
借助 OpenCV DNN 支持的预训练风格迁移模型,将普通图片转换为特定艺术风格。
3.1 实现过程
- 加载 
Torch模型readNetFromTorch支持加载.t7格式的风格迁移网络
 - 输入预处理
- 与 
Caffe类似,需减去训练时的通道均值(103.939,116.779,123.680) - 不交换通道,因为模型期望 
BGR顺序 
 - 与 
 - 前向推理与后处理
forward得到形状为(1,3,H,W)的输出- 去批次维度、加回均值,并转置为 
H × W × 3,最后裁剪到[0,255] 
 
            
            
              python
              
              
            
          
          import cv2
import numpy as np
# 功能:使用 OpenCV DNN 风格迁移模型,将图像转换为油画风格
net = cv2.dnn.readNetFromTorch('model/the_wave.t7')
img = cv2.imread('2.jpeg')
(h, w) = img.shape[:2]
# 将图片缩小以加快处理速度
newW = 600
newH = int(h * newW / w)
resized = cv2.resize(img, (newW, newH))
# 构建输入 blob 并前向传播
blob = cv2.dnn.blobFromImage(resized, 1.0, (newW, newH),
                             (103.939, 116.779, 123.680), swapRB=False, crop=False)
net.setInput(blob)
output = net.forward()
# 还原输出图像
out = output.reshape(3, newH, newW)
out[0] += 103.939; out[1] += 116.779; out[2] += 123.680
out = out.transpose(1,2,0)
out = np.clip(out, 0, 255).astype('uint8')
cv2.imshow('Styled Image', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
        
关键函数解析:
cv2.dnn.readNetFromTorch(model):加载Torch导出的风格迁移模型blobFromImage(img,1.0,size,mean,swapRB):构建符合模型输入的blob,包含均值减除和尺寸调整net.forward():执行前向推理,输出风格迁移后的特征图
3.2 优化思路
- 多风格模型切换:同时加载多种 
.t7模型,按键实时切换风格 - 原图融合:用 
addWeighted将原图与风格图按比例混合,获得"浅度风格化"效果 GPU加速支持:在DNN模块中设置CUDA后端,加速风格迁移推理
            
            
              python
              
              
            
          
          import cv2
import numpy as np
# 功能:多个风格模型切换 + 原图混合 + CUDA 加速
styles = {
    'mosaic': 'model/mosaic.t7',
    'candy':  'model/candy.t7',
    'udnie':  'model/feathers.t7'
}
nets = {k: cv2.dnn.readNetFromTorch(v) for k,v in styles.items()}
for net in nets.values():
    net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)
curr = 'mosaic'
cap = cv2.VideoCapture(0)
alpha = 0.6  # 原图融合比例
while True:
    ret, frame = cap.read()
    if not ret: break
    h,w = frame.shape[:2]
    inp = cv2.dnn.blobFromImage(frame,1.0,(w,h),(103.939,116.779,123.680),swapRB=False,crop=False)
    # 按键切换风格
    key = cv2.waitKey(1) & 0xFF
    if key==ord('1'): curr='mosaic'
    elif key==ord('2'): curr='candy'
    elif key==ord('3'): curr='udnie'
    elif key==27: break
    net = nets[curr]
    net.setInput(inp)
    out = net.forward()[0]
    out = out.reshape(3,h,w).transpose(1,2,0)
    out += np.array((103.939,116.779,123.680))
    out = np.clip(out,0,255).astype('uint8')
    # 原图融合
    blended = cv2.addWeighted(frame,1-alpha,out,alpha,0)
    cv2.putText(blended, f'Style: {curr}', (10,30),
                cv2.FONT_HERSHEY_SIMPLEX,1,(255,255,255),2)
    cv2.imshow('Neural Style Transfer', blended)
cap.release(); cv2.destroyAllWindows()
        
关键函数解析:
- 键盘事件:用 
waitKey获取按键,动态切换风格模型 cv2.addWeighted(src1,alpha,src2,beta,gamma):原图与风格图按alpha:beta比例线性融合CUDA后端:显著提升风格迁移帧率,实现实时特效
小结
在本文中,我们深入探索了 OpenCV 的 DNN 模块如何助力深度学习模型的高效部署与推理。首先,借助 cv2.dnn 对多种主流模型格式(如 Caffe、ONNX、Torch) 进行了加载与分类推理,掌握了图像预处理、前向传播及结果解析等关键流程,并介绍了批量推理与 GPU 加速等优化手段。随后,结合 YOLOv3 模型实现了实时多尺度目标检测,展示了如何提取输出层、进行后处理、绘制检测框及计算帧率等技巧。最后,我们使用风格迁移模型对图像进行了艺术化处理,轻松实现油画风格转换。
系列链接
OpenCV计算机视觉实战(1)------计算机视觉简介
OpenCV计算机视觉实战(2)------环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)------计算机图像处理基础
OpenCV计算机视觉实战(4)------计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)------图像基础操作全解析
OpenCV计算机视觉实战(6)------经典计算机视觉算法
OpenCV计算机视觉实战(7)------色彩空间详解
OpenCV计算机视觉实战(8)------图像滤波详解
OpenCV计算机视觉实战(9)------阈值化技术详解
OpenCV计算机视觉实战(10)------形态学操作详解
OpenCV计算机视觉实战(11)------边缘检测详解
OpenCV计算机视觉实战(12)------图像金字塔与特征缩放
OpenCV计算机视觉实战(13)------轮廓检测详解
OpenCV计算机视觉实战(14)------直方图均衡化
OpenCV计算机视觉实战(15)------霍夫变换详解
OpenCV计算机视觉实战(16)------图像分割技术
OpenCV计算机视觉实战(17)------特征点检测详解
OpenCV计算机视觉实战(18)------视频处理详解
OpenCV计算机视觉实战(19)------特征描述符详解
OpenCV计算机视觉实战(20)------光流法运动分析
OpenCV计算机视觉实战(21)------模板匹配详解
OpenCV计算机视觉实战(22)------图像拼接详解
OpenCV计算机视觉实战(23)------目标检测详解
OpenCV计算机视觉实战(24)------目标追踪算法
OpenCV计算机视觉实战(25)------立体视觉详解
OpenCV计算机视觉实战(26)------OpenCV与机器学习
OpenCV计算机视觉实战(27)------深度学习与卷积神经网络