通过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
相关产品推荐
相关产品推荐

