Python+OpenCV+Flask录制带边框视频:下载后边框缺失问题
问题分析与解决
你当前的代码存在核心逻辑断层:前端通过MediaRecorder录制的视频直接在浏览器生成Blob并提供下载,完全没有调用后端的处理接口,后端负责添加边框的add_border_to_video函数根本没被执行,因此下载的视频不会带有边框。
修改方案
需要将前端录制好的视频文件上传到后端,由后端完成边框添加后,再返回处理后的视频供下载。
1. 修改 app.py
新增接收视频文件的接口,处理后返回下载链接:
from flask import Flask, render_template, send_file, request import cv2 import numpy as np import os app = Flask(__name__) # 确保临时文件目录存在 os.makedirs('temp', exist_ok=True) @app.route('/') def index(): return render_template('index.html') def add_border_to_video(video_path, output_path, border_thickness=20, border_color=(0, 0, 255)): cap = cv2.VideoCapture(video_path) if not cap.isOpened(): raise Exception("无法打开视频文件") frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) # 修复:添加边框后视频尺寸变大,更新输出尺寸 output_width = frame_width + 2 * border_thickness output_height = frame_height + 2 * border_thickness fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, fps, (output_width, output_height)) while True: ret, frame = cap.read() if not ret: break # 添加边框 bordered_frame = cv2.copyMakeBorder(frame, border_thickness, border_thickness, border_thickness, border_thickness, cv2.BORDER_CONSTANT, value=border_color) out.write(bordered_frame) cap.release() out.release() @app.route('/upload_process', methods=['POST']) def upload_process(): if 'video' not in request.files: return "未收到视频文件", 400 video_file = request.files['video'] if video_file.filename == '': return "未选择视频文件", 400 # 保存上传的临时视频 temp_input_path = 'temp/input_video.mp4' video_file.save(temp_input_path) # 处理添加边框 output_path = 'temp/output_with_border.mp4' add_border_to_video(temp_input_path, output_path) # 返回处理后的视频供下载 return send_file(output_path, as_attachment=True, download_name='bordered_video.mp4') if __name__ == '__main__': app.run(debug=True)
2. 修改 index.html
调整前端逻辑,将录制好的视频Blob上传到后端,接收处理后的视频:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Record and Download Video</title> <style> #video-container { width: 640px; height: 480px; margin: 0 auto; border: 2px solid black; } </style> </head> <body> <h1>Record and Download Video</h1> <div id="video-container"> <video id="video" width="640" height="480" autoplay></video> </div> <button id="startRecord">Start Recording</button> <button id="stopRecord" disabled>Stop Recording</button> <button id="downloadVideo" style="display: none;">Download Processed Video</button> <script> const video = document.getElementById('video'); const startRecordBtn = document.getElementById('startRecord'); const stopRecordBtn = document.getElementById('stopRecord'); const downloadVideoBtn = document.getElementById('downloadVideo'); let mediaRecorder; let chunks = []; startRecordBtn.addEventListener('click', async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); video.srcObject = stream; startRecordBtn.disabled = true; stopRecordBtn.disabled = false; downloadVideoBtn.style.display = 'none'; mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.start(); } catch (err) { console.error('Error accessing camera:', err); } }); stopRecordBtn.addEventListener('click', () => { mediaRecorder.stop(); startRecordBtn.disabled = false; stopRecordBtn.disabled = true; }); function handleDataAvailable(event) { chunks.push(event.data); if (mediaRecorder.state === 'inactive') { const blob = new Blob(chunks, { type: 'video/mp4' }); // 显示下载按钮,点击时上传到后端处理 downloadVideoBtn.style.display = 'inline'; downloadVideoBtn.onclick = async () => { const formData = new FormData(); formData.append('video', blob, 'recorded_video.mp4'); try { const response = await fetch('/upload_process', { method: 'POST', body: formData }); if (!response.ok) { throw new Error('处理视频失败'); } // 触发下载处理后的视频 const blob = await response.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'bordered_video.mp4'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (err) { console.error('上传或处理视频出错:', err); } }; // 清空chunks以便下次录制 chunks = []; } } </script> </body> </html>
关键修复点说明
- 前端逻辑调整:不再直接下载原始录制视频,而是将Blob上传到后端
/upload_process接口,接收处理后的视频再触发下载。 - 后端新增接口:
/upload_process负责接收上传的视频文件,调用添加边框函数后返回处理结果。 - 修复边框尺寸问题:原代码中
VideoWriter的输出尺寸还是原始视频尺寸,但添加边框后视频尺寸变大,导致写入失败,现在更新为计算后的新尺寸。
内容的提问来源于stack exchange,提问作者MEB
相关产品推荐
相关产品推荐

