一、项目介绍
1 实现效果
运行程序后,调用电脑默认摄像头,实时采集画面并分割为左上、右上、左下、右下四个区域,每个区域独立应用不同的艺术风格迁移模型(星月夜、缪斯、海浪、马赛克),最终拼接为完整画面实时展示,按下 Esc 键即可退出程序。
2.预训练风格模型准备
本文使用的是 OpenCV dnn 模块兼容的Torch 格式(.t7) 风格迁移预训练模型,这类模型体积小、推理速度快,适合实时场景:
1.模型下载:可从 OpenCV 官方示例库、GitHub 开源仓库获取星月夜(starry_night.t7)、缪斯(la_muse.t7)、海浪(the_wave.t7)、马赛克(mosaic.t7)等经典模型;
2.路径配置:在项目根目录下创建model文件夹,将下载的所有.t7 模型放入其中,保证代码中模型路径与实际文件一致。
二、核心功能模块
2.1 模型加载模块:load_style_model
功能:封装 OpenCV dnn 模型加载逻辑,接收模型文件路径,返回加载好的神经网络对象,为后续风格迁移做准备。
import cv2 def load_style_model(model_path): net = cv2.dnn.readNet(model_path) # 加载预训练模型 return netcv2.dnn.readNet()是 OpenCV dnn 模块的通用模型加载函数,支持 Torch(.t7)、Caffe(.prototxt/.caffemodel)、TensorFlow(.pb)等多种格式
2.2 风格迁移处理模块:apply_style_transfer
功能:接收单区域画面、加载好的风格模型、模型输入目标尺寸,完成图像预处理→模型前向传播→结果后处理的完整风格迁移流程,返回风格化后的单区域画面,是项目的核心算法模块。
import cv2 import numpy as np def apply_style_transfer(frame, net, target_size=None): h, w = frame.shape[:2] if target_size is None: target_size = (w, h) # 预处理:转换为dnn模块支持的blob格式 blob = cv2.dnn.blobFromImage(frame, 1, target_size, (0, 0, 0), swapRB=False, crop=False) # 前向传播:输入blob,获取风格化结果 net.setInput(blob) output = net.forward() # 后处理:恢复维度、归一化、转换为常规图像格式 output = output.reshape(output.shape[1], output.shape[2], output.shape[3]) cv2.normalize(output, output, norm_type=cv2.NORM_MINMAX) output = output.transpose(1, 2, 0) # 恢复原区域尺寸 output = cv2.resize(output, (w, h)) return output1.尺寸初始化:若未指定目标尺寸,默认使用输入画面的原始尺寸,保证兼容性
2.图像预处理(blob 转换):
cv2.dnn.blobFromImage()是 dnn 模块的核心预处理函数,将常规的 HWC(高度 - 宽度 - 通道)格式图像转换为 NCHW(批次 - 通道 - 高度 - 宽度)格式的 blob(深度学习模型标准输入格式);
参数说明:scale=1(像素值不缩放)、mean=(0,0,0)(不做均值消减)、swapRB=False(风格模型要求 RGB 格式,OpenCV 读取的图像为 BGR,需根据模型需求调整)、crop=False(不裁剪,保持画面完整)
3.模型前向传播:net.setInput(blob)设置模型输入,net.forward()执行推理计算,直接输出风格化后的特征图
4.结果后处理:
reshape:调整特征图维度,适配后续处理;
cv2.normalize():使用 MINMAX 归一化,将特征图数值映射到 [0,1] 区间,避免像素值溢出;
transpose:将 NCHW 格式转回 HWC 格式,恢复为常规图像的维度顺序;
resize:将风格化后的画面恢复为原区域尺寸,保证拼接后无变形。
2.3 主程序模块:main
功能:项目的入口和总控模块,整合所有功能,完成参数配置→摄像头初始化→模型批量加载→主循环实时处理→资源释放的完整流程,是程序的运行核心。
2.3.1配置参数定义
将所有可自定义的参数集中定义,便于后续调试和修改
# 摄像头ID,默认0为电脑内置摄像头,外接摄像头可改为1/2 CAMERA_ID = 0 # 四格区域与对应风格模型的映射,键为区域名称,值为模型文件路径 MODEL_PATHS = { "top_left": r"model\starry_night.t7", "top_right": r"model\la_muse.t7", "bottom_left": r"model\the_wave.t7", "bottom_right": r"model\mosaic.t7" } TARGET_SIZE = (200, 140) # 模型输入尺寸,越小推理速度越快,可根据性能调整 ESC_KEY = 27 # Esc键的ASCII码,用于退出程序2.3.2 摄像头初始化
调用 OpenCV 的 VideoCapture 类打开摄像头
# 打开摄像头 cap = cv2.VideoCapture(CAMERA_ID) if not cap.isOpened(): print(f"错误:无法打开摄像头(ID={CAMERA_ID})") return2.3.3 预训练模型批量加载
通过字典推导式批量加载 MODEL_PATHS 中的所有风格模型,加入异常捕获,若模型加载失败则释放摄像头并退出程序
try: models = { key: load_style_model(path) for key, path in MODEL_PATHS.items() } print("所有风格模型加载成功!") except Exception as e: print(f"模型加载失败:{e}") cap.release() # 及时释放摄像头资源 return2.3.4 实时采集与风格迁移
这是程序的核心执行循环,实现摄像头帧读取→画面四格分割→多风格并行迁移→画面拼接→实时展示的完整逻辑
while True: # 1. 读取摄像头帧,ret为是否读取成功,frame为采集到的画面 ret, frame = cap.read() if not ret: print("错误:无法读取摄像头画面") break # 2. 获取原画面尺寸,计算分割点(画面中心) h, w, c = frame.shape h_half, w_half = h // 2, w // 2 # 3. 四格画面分割:基于numpy切片,高效分割无冗余 top_left = frame[0:h_half, 0:w_half, :] top_right = frame[0:h_half, w_half:w, :] bottom_left = frame[h_half:h, 0:w_half, :] bottom_right = frame[h_half:h, w_half:w, :] # 4. 为每个区域应用对应风格迁移,调用封装的处理函数 top_left_style = apply_style_transfer(top_left, models["top_left"], TARGET_SIZE) top_right_style = apply_style_transfer(top_right, models["top_right"], TARGET_SIZE) bottom_left_style = apply_style_transfer(bottom_left, models["bottom_left"], TARGET_SIZE) bottom_right_style = apply_style_transfer(bottom_right, models["bottom_right"], TARGET_SIZE) # 5. 画面拼接:先水平拼接行,再垂直拼接列,基于numpy的hstack/vstack,高效无失真 top_row = np.hstack((top_left_style, top_right_style)) # 水平拼接左上、右上 bottom_row = np.hstack((bottom_left_style, bottom_right_style)) # 水平拼接左下、右下 final_frame = np.vstack((top_row, bottom_row)) # 垂直拼接上下两行 # 6. 尺寸校准:保证拼接后画面与原摄像头画面尺寸一致,避免显示变形 final_frame = cv2.resize(final_frame, (w, h)) # 7. 实时展示风格化后的完整画面 cv2.imshow("Camera 4-Grid Art Style Transfer", final_frame) # 8. 按键检测:60ms延迟,平衡画面流畅度和按键响应速度,0xFF处理跨平台按键兼容 key = cv2.waitKey(60) & 0xFF if key == ESC_KEY: print("用户按下Esc键,退出程序") break采用 numpy 切片分割画面,比 OpenCV 的 ROI 提取更高效,无内存冗余
风格迁移函数批量调用,每个区域独立处理,无相互干扰