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

基于Flask API的实时机器学习项目:AJAX传输摄像头视频卡顿问题求解

问题:实时机器学习项目中Flask API传输摄像头视频帧速度异常缓慢

我正在开发一个实时机器学习项目,需要将摄像头采集的视频传输至指定Flask API,经预测处理后再返回给用户展示。但使用以下POST方法在本地运行时,传输速度异常缓慢。

然而,当直接使用cap = cv2.VideoCapture(0)而不通过API传输时,运行完全正常。请问有什么解决方案?

前端代码

var canvas = document.createElement("canvas");
canvas.width = 320;
canvas.height = 240;
var context = canvas.getContext('2d');
var image = document.getElementById('image');
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
        video.srcObject = stream;
        window.setInterval(function() {
            context.drawImage(video, 0, 0, 320, 240);
            canvas.toBlob(upload, "image/jpeg");
        }, 100);
    });
}
function upload(file) {
    var formdata = new FormData();
    formdata.append("snap", file);
    var xhr = new XMLHttpRequest();
    var url = "http://localhost:5000/vid"
    xhr.open("POST", url , true);
    xhr.responseType = 'blob';
    xhr.onload = function() {
        if(this.status = 200) {
        } else {
            console.error(xhr);
        }
        image.src = URL.createObjectURL(this.response);
    };
    xhr.send(formdata);
}

Flask后端代码

from flask import Flask, request, make_response
import cv2
import numpy as np

app = Flask(__name__)

def send_file_data(data, mimetype='image/jpeg', filename='output.jpg'):
    response = make_response(data)
    response.headers.set('Content-Type', mimetype)
    response.headers.set('Content-Disposition', 'attachment', filename=filename)
    return response

# 预测处理函数
def generate_frames(frame):
    img_face = cv2.resize(frame,(256,256))
    ** prediction process **
    ret,buffer=cv2.imencode('.jpg',img_face)
    frame=buffer.tobytes()
    return frame

# 前端调用接口
@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        fs = request.files.get('snap')
        if fs:
            img = cv2.imdecode(np.frombuffer(fs.read(), np.uint8), cv2.IMREAD_UNCHANGED)
            img = np.array(img)
            return send_file_data(generate_frames(img))
        else:
            return 'ERROR'
    return 'WELCOME'

解决方案分析与优化建议

先给你拆解下速度慢的核心原因,再一步步给出优化方案:

1. 前端侧的关键优化点

  • 解决请求堆积问题:你用setInterval每100ms发一次请求,但如果上一次请求还没完成,新请求就会排队,浏览器并发连接数有限(一般是6个),堆积后必然变慢。我们可以加个状态标记,确保同一时间只发一个请求。
  • 修复逻辑错误:原代码里this.status = 200是赋值而非判断,会导致逻辑混乱,改成this.status == 200。
  • 降低图像传输体积:canvas.toBlob默认JPEG质量很高,可手动指定质量参数(比如0.5),大幅减少传输数据量。
  • 用requestAnimationFrame替代setInterval:更贴合浏览器渲染周期,避免不必要的帧捕获。

优化后的前端代码:

var canvas = document.createElement("canvas");
canvas.width = 320;
canvas.height = 240;
var context = canvas.getContext('2d');
var image = document.getElementById('image');
var isUploading = false; // 标记是否正在上传,防止并发

if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
        video.srcObject = stream;
        video.play(); // 必须调用play,否则drawImage可能失效
        
        function processFrame() {
            if (!isUploading) {
                context.drawImage(video, 0, 0, 320, 240);
                // 指定JPEG质量为0.5,减少传输体积
                canvas.toBlob(upload, "image/jpeg", 0.5);
            }
            requestAnimationFrame(processFrame);
        }
        processFrame();
    });
}

function upload(file) {
    isUploading = true;
    var formdata = new FormData();
    formdata.append("snap", file);
    var xhr = new XMLHttpRequest();
    var url = "http://localhost:5000/upload"; // 修正URL与后端接口一致
    
    xhr.open("POST", url , true);
    xhr.responseType = 'blob';
    
    xhr.onload = function() {
        isUploading = false;
        if(this.status == 200) {
            image.src = URL.createObjectURL(this.response);
        } else {
            console.error('请求失败:', xhr.statusText);
        }
    };
    
    xhr.onerror = function() {
        isUploading = false;
        console.error('网络错误');
    };
    
    xhr.send(formdata);
}

2. Flask后端的性能提升

  • 启用多线程/多进程:Flask默认开发服务器是单线程的,同一时间只能处理一个请求,直接改成多线程模式,或者生产环境用Gunicorn等高性能服务器。
  • 优化响应头:原代码设置了Content-Disposition: attachment,会让浏览器把响应当成下载文件,而非直接显示的图片,去掉这个头可减少不必要的开销。
  • 降低返回图像质量:后端编码JPEG时也指定质量参数,减少返回数据量。
  • 提前初始化模型:如果你的预测模型是在generate_frames里每次请求都加载,一定要把模型初始化移到函数外面,避免重复加载的开销。

优化后的后端代码:

from flask import Flask, request, make_response
import cv2
import numpy as np

app = Flask(__name__)

# 提前初始化你的机器学习模型,避免每次请求都加载
# model = load_your_ml_model()

# 优化响应函数,去掉下载头
def send_image_data(data, mimetype='image/jpeg'):
    response = make_response(data)
    response.headers.set('Content-Type', mimetype)
    return response

def generate_frames(frame):
    img_face = cv2.resize(frame,(256,256))
    # ** prediction process ** 这里用提前初始化的model做预测
    # result = model.predict(img_face)
    # 处理预测结果...
    
    # 指定JPEG质量为50,减少返回体积
    ret,buffer=cv2.imencode('.jpg',img_face, [cv2.IMWRITE_JPEG_QUALITY, 50])
    frame=buffer.tobytes()
    return frame

@app.route('/upload', methods=['POST'])
def upload():
    fs = request.files.get('snap')
    if not fs:
        return 'ERROR', 400
    
    # 直接读取流,避免全量加载到内存
    img_data = fs.stream.read()
    # 指定IMREAD_COLOR,减少通道数(如果不需要透明通道)
    img = cv2.imdecode(np.frombuffer(img_data, np.uint8), cv2.IMREAD_COLOR)
    if img is None:
        return 'Invalid image', 400
    
    processed_frame = generate_frames(img)
    return send_image_data(processed_frame)

if __name__ == '__main__':
    # 启用多线程,提高并发处理能力
    app.run(debug=False, threaded=True)
    # 生产环境建议用Gunicorn:gunicorn -w 4 -b 0.0.0.0:5000 app:app

3. 进阶优化方案(如果基础优化还不够)

  • 改用WebSocket代替HTTP POST:HTTP请求-响应模型每次都要建立连接、发送头信息,开销极大。WebSocket是长连接,适合实时数据传输,能大幅降低延迟。
  • 帧采样与分辨率压缩:如果实时性要求极高,可以降低帧率(比如每秒10帧)或进一步缩小帧分辨率(比如160x120)。
  • GPU加速:如果你的预测模型用TensorFlow/PyTorch等框架,务必启用GPU加速,这会让预测过程快数倍。

先试试基础优化,应该能看到明显的速度提升,如果还达不到要求,再考虑进阶方案。

内容的提问来源于stack exchange,提问作者MSI2

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 20:17:34