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

通过HTTP请求传输JS实时视频流至Python人脸识别遇BadRequestKeyError

问题解决方案

一、修复当前HTTP POST流传输方案的错误

1. 前端JavaScript代码修正

解决canvas尺寸缺失、重复绑定事件、请求格式不匹配问题:

<button id="startBtn" onclick="openCam()">Open Webcam</button>
<br>
<button id="closeButton">Close</button>
<br>
<video id="video" autoplay></video>

<script>
    const video = document.getElementById('video');
    const closeButton = document.getElementById('closeButton');
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    let intervalID;
    let streamObj;

    // 关闭按钮事件仅绑定一次
    closeButton.addEventListener('click', () => {
        if (intervalID) {
            clearInterval(intervalID);
            intervalID = null;
        }
        if (streamObj) {
            streamObj.getTracks().forEach(track => track.stop());
            video.srcObject = null;
        }
        window.location.href = "{{ url_for('markin') }}";    
    });

    function openCam(){
        navigator.mediaDevices.getUserMedia({ video: true })
        .then((stream) => {
            streamObj = stream;
            video.srcObject = stream;
            // 同步canvas与video的尺寸
            video.onloadedmetadata = () => {
                canvas.width = video.videoWidth;
                canvas.height = video.videoHeight;
                intervalID = setInterval(() => {
                    context.drawImage(video, 0, 0, canvas.width, canvas.height);
                    canvas.toBlob((blob) => {
                        const formData = new FormData();
                        formData.append('video', blob, 'frame.jpg');
                        const xhr = new XMLHttpRequest();
                        xhr.open('POST', '/markin');
                        xhr.send(formData);
                    }, 'image/jpeg', 0.8);
                }, 1000 / 30);
            };
        })
        .catch((error) => {
            console.error(error);
        });
    }
</script>

2. Python后端代码修正

解决KeyError、语法错误,并完成Bytes到numpy数组的转换:

import numpy as np
from PIL import Image
from io import BytesIO
from flask import request, render_template

# Flask路由示例
@app.route('/markin', methods=['GET', 'POST'])
def markin():
    if request.method == 'POST':
        # 校验请求中的视频帧
        if 'video' not in request.files:
            print("未接收到视频帧")
            return "Bad Request", 400
        
        video_file = request.files['video']
        if video_file.filename == '':
            print("空文件")
            return "Bad Request", 400
        
        # 将Bytes转换为numpy数组
        img_bytes = video_file.read()
        img = Image.open(BytesIO(img_bytes))
        img_np = np.array(img)
        
        print("已转换为numpy数组,尺寸:", img_np.shape)
        
        # 此处插入人脸识别逻辑(如使用face_recognition或OpenCV)
        # ...
        
        return "Success", 200
    
    # GET请求返回页面
    return render_template('markin.html')

二、简化WebRTC实时流方案示例

如果HTTP轮询延迟过高,可使用WebRTC实现低延迟传输,以下是基于aiortc库的简化实现:

1. 服务端代码(Python)

from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
from flask import Flask, render_template, request, jsonify
import asyncio
import numpy as np

app = Flask(__name__)
pcs = set()

class FaceDetectionTrack(VideoStreamTrack):
    def __init__(self, track):
        super().__init__()
        self.track = track

    async def recv(self):
        frame = await self.track.recv()
        # 将帧转换为numpy数组进行人脸识别
        img = frame.to_ndarray(format="bgr24")
        # 插入你的人脸识别逻辑
        # ...
        # 返回处理后的帧(可选)
        return frame

@app.route('/')
def index():
    return render_template('webrtc.html')

@app.route('/offer', methods=['POST'])
async def offer():
    params = await request.json
    offer = RTCSessionDescription(sdp=params['sdp'], type=params['type'])

    pc = RTCPeerConnection()
    pcs.add(pc)

    @pc.on('iceconnectionstatechange')
    async def on_iceconnectionstatechange():
        if pc.iceConnectionState == 'failed':
            await pc.close()
            pcs.discard(pc)

    # 处理视频流
    @pc.on('track')
    def on_track(track):
        if track.kind == 'video':
            pc.addTrack(FaceDetectionTrack(track))

    await pc.setRemoteDescription(offer)
    answer = await pc.createAnswer()
    await pc.setLocalDescription(answer)

    return jsonify({
        'sdp': pc.localDescription.sdp,
        'type': pc.localDescription.type
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, ssl_context='adhoc')

2. 客户端HTML/JS代码

<!DOCTYPE html>
<html>
<head>
    <title>WebRTC Face Detection</title>
</head>
<body>
    <video id="localVideo" autoplay playsinline></video>
    <video id="remoteVideo" autoplay playsinline></video>
    <button onclick="start()">Start</button>
    <button onclick="stop()">Stop</button>

    <script>
        let pc;
        const localVideo = document.getElementById('localVideo');
        const remoteVideo = document.getElementById('remoteVideo');

        async function start() {
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            localVideo.srcObject = stream;

            pc = new RTCPeerConnection();
            stream.getTracks().forEach(track => pc.addTrack(track, stream));

            pc.ontrack = (event) => {
                remoteVideo.srcObject = event.streams[0];
            };

            const offer = await pc.createOffer();
            await pc.setLocalDescription(offer);

            const response = await fetch('/offer', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    sdp: pc.localDescription.sdp,
                    type: pc.localDescription.type
                })
            });

            const answer = await response.json();
            await pc.setRemoteDescription(new RTCSessionDescription(answer));
        }

        async function stop() {
            if (pc) {
                await pc.close();
                pc = null;
            }
            localVideo.srcObject.getTracks().forEach(track => track.stop());
            remoteVideo.srcObject = null;
        }
    </script>
</body>
</html>

内容的提问来源于stack exchange,提问作者Dancun Gerald Nyabuto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:44:56