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]
    # 预处理:转换成符合网络输入的四维blob
    blob = 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 键退出
        break

cap.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退出
        break
    elif 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()

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐