本文介绍一种基于 Flask 和 OpenCV 的端到端视频处理方案:在浏览器中实时调用摄像头录制视频,并在服务端为每一帧动态添加红色(RGB: 0,0,255)边框,最终生成并提供带边框的 MP4 文件供下载。
本文介绍一种基于 Flask 和 OpenCV 的端到端视频处理方案:在浏览器中实时调用摄像头录制视频,并在服务端为每一帧动态添加红色(RGB: 0,0,255)边框,最终生成并提供带边框的 MP4 文件供下载。
在原始实现中,用户通过前端 MediaRecorder 录制视频并保存为本地 Blob,再上传或触发后端处理——但该流程存在关键缺陷:边框是在录制完成后的离线转码阶段添加的,而 MediaRecorder 生成的 MP4 文件常因编码兼容性问题导致 cv2.VideoCapture 读取失败或帧尺寸异常,致使 cv2.copyMakeBorder 无法正确应用。更严重的是,原始代码中 VideoWriter 初始化时仍使用原始帧尺寸 (frame_width, frame_height),未适配加边框后的新分辨率,造成写入失败或画面裁剪。
本教程采用更鲁棒、更符合生产逻辑的架构:服务端直连摄像头持续采集 → 实时预处理(如镜像)→ 流式传输至前端预览 → 同步后处理(逐帧加边框并写入文件)。整个流程避免了浏览器端视频编码/解码的不可控因素,确保边框精准、稳定地叠加在每一帧上。
import timefrom flask import Flask, render_template_string, Responseimport cv2def coroutine(func):def start(*args, **kwargs):cr = func(*args, **kwargs)next(cr)return crreturn start@coroutinedef video_writer(dst_video, frame_width, frame_height):border_thickness = 20border_color = (0, 0, 255)# BGR order for OpenCV# ✅ 关键:输出尺寸 = 原始尺寸 + 双侧边框out = cv2.VideoWriter(dst_video,cv2.VideoWriter_fourcc(*'MJPG'),10,(frame_width + 2 * border_thickness, frame_height + 2 * border_thickness))try:while True:frame = (yield)# ✅ 实时加边框(BORDER_CONSTANT + RGB→BGR 转换已隐含)bordered_frame = cv2.copyMakeBorder(frame,top=border_thickness,bottom=border_thickness,left=border_thickness,right=border_thickness,borderType=cv2.BORDER_CONSTANT,value=border_color)out.write(bordered_frame)except GeneratorExit:passfinally:out.release()def stream_main(target, path_video):cap = cv2.VideoCapture(0)if not cap.isOpened():raise RuntimeError("Failed to open camera")frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))# 初始化协程(传入视频路径和尺寸)writer = target(path_video, frame_width, frame_height)def gen_frame_content(img_bytes):return b''.join([b'--framernContent-Type: image/jpegrnrn',img_bytes,b'rn'])while True:ret, frame = cap.read()if not ret:break# ✅ 后处理:写入带边框视频(同步)writer.send(frame)# ✅ 预处理:供前端显示(如水平翻转,提升用户体验)display_frame = cv2.flip(frame, 1)# 编码为 JPEG 并流式返回success, buffer = cv2.imencode('.jpg', display_frame)if not success:continueyield gen_frame_content(buffer.tobytes())time.sleep(0.04)# 控制约 25 FPScap.release()writer.close()# —— 路由配置 ——app = Flask(__name__)OUTPUT_VIDEO_PATH = 'recorded_with_border.avi'# 推荐 .avi + MJPEG,兼容性优于 [email protected]('/')def index():return render_template_string('''<h2>? Live Recording with Red Border</h2><p>Camera starts automatically. To stop recording, close this tab or reload.</p><img src="{{ url_for('video_feed') }}" width="640" height="480" style="border:3px solid #f00;">''')@app.route('/video_feed')def video_feed():return Response(stream_main(video_writer, OUTPUT_VIDEO_PATH),mimetype='multipart/x-mixed-replace; boundary=frame')if __name__ == '__main__':app.run(debug=True, host='0.0.0.0', port=5000)
启动服务后访问 http://localhost:5000/,摄像头自动开启,画面实时显示(已镜像)。同时,recorded_with_border.avi 在后台持续写入——打开该文件,将看到每帧四周均有 20 像素宽的纯红色边框,且无黑边、拉伸或闪烁。
此方案将视频处理逻辑完全收口于服务端,规避了浏览器编码差异与前后端数据传递损耗,是构建可靠视频标注、教学录制、安防预览等系统的坚实基础。