计算机视觉(opencv)实战三十——摄像头实时风格迁移,附多种风格转换
OpenCV DNN 实时风格迁移:原理与完整代码
图像风格迁移(Neural Style Transfer)是计算机视觉中非常有趣的一个应用,它能够将一张图像的风格“迁移”到另一张图像上,比如把摄像头捕获的视频实时转换为梵高的《星空》风格。本篇文章将通过 OpenCV 的 dnn
模块和预训练的 Torch 模型,来实现实时风格迁移。
1. 环境准备与摄像头读取
相比于处理单张图片,我们需要使用 cv2.VideoCapture()
打开摄像头并逐帧读取输入:
import cv2# 打开默认摄像头
cap = cv2.VideoCapture(0)
# 加载预训练的风格迁移模型
net = cv2.dnn.readNetFromTorch(r'.\model\starry_night.t7')# 检查摄像头是否成功打开
if not cap.isOpened():print("摄像头启动失败")exit()
要点:
cv2.VideoCapture(0)
表示打开默认摄像头,0 通常是电脑的内置摄像头。net = cv2.dnn.readNetFromTorch()
用来加载 Torch 格式的风格迁移模型。
2. 实时图像处理与 blob 构建
在循环中逐帧读取图像,并将其转换为神经网络可以接受的输入格式:
while True:ret, frame = cap.read() # 读取一帧if not ret:print("不能读取摄像头")break(h, w) = frame.shape[:2]# 预处理:转换成符合网络输入的四维blobblob = cv2.dnn.blobFromImage(frame,scalefactor=1,size=(w, h),mean=(0, 0, 0),swapRB=True,crop=False)
这里用到了 cv2.dnn.blobFromImage()
,它的参数含义如下:
image:输入图像,必须是 NumPy 数组。
scalefactor:缩放因子,对像素值进行乘法缩放,默认 1 表示不缩放。
size:调整图像尺寸,必须与模型训练时的输入大小一致。
mean:通道均值,用于减去图像的平均值以进行归一化。设置为
(0,0,0)
表示不做均值减法。swapRB:是否交换 R 和 B 通道。因为 OpenCV 默认是 BGR 通道,而许多深度学习模型训练时采用的是 RGB。
crop:是否在缩放后裁剪图像,通常设为 False。
最终返回的 blob 是一个 四维张量,格式为 NCHW:
N:批量大小(batch size)
C:通道数(通常 3)
H:高度
W:宽度
参数名 | 类型 | 说明 | 默认值 |
---|---|---|---|
image | numpy.ndarray | 输入图像 | 必填 |
scalefactor | float | 缩放因子,像素值会乘以该值 | 1.0 |
size | (w,h) | 调整图像到网络需要的尺寸 | 不缩放 |
mean | (R,G,B) | 每个通道减去的均值 | (0,0,0) |
swapRB | bool | 是否交换 R 和 B 通道(BGR → RGB) | False |
crop | bool | 是否居中裁剪 | False |
ddepth | int | 输出数据类型 | CV_32F |
3. 前向传播与风格化处理
将 blob 设置为网络输入,并执行前向传播得到风格化结果:
# 推理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)
关键步骤说明:
reshape
:去掉 batch 维度,从(1,C,H,W)
→(C,H,W)
。cv2.normalize
:将像素值归一化到指定范围(0-1 或 0-255)。transpose
:将通道维度换到最后,得到 HWC 格式,方便用 OpenCV 显示。
4. 显示实时结果与按键控制
将风格化结果显示在窗口中,并监听键盘按键,实现实时预览和退出功能:
cv2.imshow(winname='result', mat=result)key_pressed = cv2.waitKey(1)if key_pressed == 27: # ESC 键退出breakcap.release()
cv2.destroyAllWindows()
要点:
cv2.imshow()
每次显示最新的风格化帧。cv2.waitKey(1)
设置显示刷新频率,参数越小延迟越低。key_pressed == 27
判断用户是否按下 ESC 键,如果按下就结束循环。
5. 模型加载函数速查表
模型类型 | model 文件 | config 文件 | 调用函数 |
---|---|---|---|
Caffe | .caffemodel | .prototxt | cv2.dnn.readNetFromCaffe() |
TensorFlow | .pb | .pbtxt | cv2.dnn.readNetFromTensorFlow() |
Torch | .t7 或 .net | 无 | cv2.dnn.readNetFromTorch() |
Darknet (YOLO) | .weights | .cfg | cv2.dnn.readNetFromDarknet() |
OpenVINO | .bin | .xml | cv2.dnn.readNetFromModelOptimizer() |
ONNX | .onnx | 无 | cv2.dnn.readNetFromONNX() |
6. 运行效果与优化建议
运行程序后,你将看到摄像头捕获的视频实时被转换成梵高的星空风格,边移动边变化,效果十分炫酷。
优化建议:
提高帧率:使用更高性能的 GPU,可调用
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
和net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
。降低分辨率:适当缩小
size
,提升推理速度。多模型切换:可以加载多种风格模型,按键切换不同艺术风格。
扩展:按1/2/3/4实现多种风格转换:
import cv2# 定义可选风格模型 style_models = {'1': r'.\model\starry_night.t7', # 梵高星空'2': r'.\model\la_muse.t7', # 莫奈风格'3': r'.\model\the_scream.t7', # 蒙克呐喊'4': r'.\model\udnie.t7' # 抽象画风格 }# 默认加载第一种风格 current_model_key = '1' net = cv2.dnn.readNetFromTorch(style_models[current_model_key])cap = cv2.VideoCapture(0) if not cap.isOpened():print("摄像头启动失败")exit()print("按键切换风格:1=星空, 2=莫奈, 3=呐喊, 4=抽象, ESC=退出")while True:ret, frame = cap.read()if not ret:print("不能读取摄像头")break(h, w) = frame.shape[:2]blob = cv2.dnn.blobFromImage(frame,scalefactor=1,size=(w, h),mean=(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)cv2.imshow('Stylized Result', result)key_pressed = cv2.waitKey(1)if key_pressed == 27: # ESC退出breakelif chr(key_pressed & 0xFF) in style_models.keys():# 切换风格模型current_model_key = chr(key_pressed & 0xFF)print(f"切换到风格:{current_model_key}")net = cv2.dnn.readNetFromTorch(style_models[current_model_key])cap.release() cv2.destroyAllWindows()