一、项目介绍
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 模型加载逻辑,接收模型文件路径,返回加载好的神经网络对象,为后续风格迁移做准备。
python
import cv2
def load_style_model(model_path):
net = cv2.dnn.readNet(model_path) # 加载预训练模型
return net
cv2.dnn.readNet()是 OpenCV dnn 模块的通用模型加载函数,支持 Torch(.t7)、Caffe(.prototxt/.caffemodel)、TensorFlow(.pb)等多种格式
2.2 风格迁移处理模块:apply_style_transfer
功能:接收单区域画面、加载好的风格模型、模型输入目标尺寸,完成图像预处理→模型前向传播→结果后处理的完整风格迁移流程,返回风格化后的单区域画面,是项目的核心算法模块。
python
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 output
1.尺寸初始化:若未指定目标尺寸,默认使用输入画面的原始尺寸,保证兼容性
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配置参数定义
将所有可自定义的参数集中定义,便于后续调试和修改
python
# 摄像头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 类打开摄像头
python
# 打开摄像头
cap = cv2.VideoCapture(CAMERA_ID)
if not cap.isOpened():
print(f"错误:无法打开摄像头(ID={CAMERA_ID})")
return
2.3.3 预训练模型批量加载
通过字典推导式批量加载 MODEL_PATHS 中的所有风格模型,加入异常捕获,若模型加载失败则释放摄像头并退出程序
python
try:
models = {
key: load_style_model(path) for key, path in MODEL_PATHS.items()
}
print("所有风格模型加载成功!")
except Exception as e:
print(f"模型加载失败:{e}")
cap.release() # 及时释放摄像头资源
return
2.3.4 实时采集与风格迁移
这是程序的核心执行循环,实现摄像头帧读取→画面四格分割→多风格并行迁移→画面拼接→实时展示的完整逻辑
python
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 提取更高效,无内存冗余
-
风格迁移函数批量调用,每个区域独立处理,无相互干扰
三、结果显示
