You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:16:00