如何实现客户端摄像头视频帧经Flask服务器处理后回传?
解决客户端-服务器视频帧传输与处理问题
我来帮你修正代码里的问题,实现视频帧的持续捕获、传输到服务器处理再返回显示的完整流程。
修正后的客户端代码(index.html)
<div id="container"> <!-- 用img标签展示处理后的帧,因为服务器返回的是base64格式的图片数据 --> <img id="processedFrame" style="max-width: 100%;" /> </div> <script type="text/javascript" charset="utf-8"> var socket = io('http://127.0.0.1:5000'); // 监听连接状态 socket.on('connect', function(){ console.log("已连接到服务器... ", socket.connected); }); // 创建隐藏的video元素(仅用于捕获摄像头流,不需要在页面显示) var video = document.createElement('video'); video.style.display = 'none'; document.body.appendChild(video); // 获取用于显示处理后帧的img元素 var processedImg = document.querySelector("#processedFrame"); var captureInterval; // 请求摄像头权限并捕获流 if (navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { video.srcObject = stream; video.play(); // 必须播放视频才能获取有效帧 // 定时捕获并发送帧到服务器,这里设置每100ms发送一次(可根据性能调整) captureInterval = setInterval(() => { socket.emit('catch-frame', { image: true, buffer: getFrame() }); }, 100); }) .catch(function (error) { console.error("摄像头访问失败:", error); console.log("出现错误!"); }); } // 捕获当前视频帧并转为base64格式 const getFrame = () => { const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video, 0, 0); return canvas.toDataURL('image/png'); } // 接收服务器处理后的帧并显示 socket.on('response_back', function(data){ // 直接将base64数据设置为img的src属性 processedImg.src = data.buffer; }); // 断开连接时清理定时器,避免资源浪费 socket.on('disconnect', () => { clearInterval(captureInterval); console.log("与服务器断开连接"); }); </script>
修正后的服务器代码(app.py)
这里我添加了一个简单的灰度处理示例,你可以替换成自己的业务逻辑:
from flask import Flask, render_template from flask_socketio import SocketIO, emit from PIL import Image, ImageOps import io import base64 app = Flask(__name__) # 允许跨域请求,本地测试时需要 socketio = SocketIO(app, cors_allowed_origins="*") @app.route('/', methods=['POST', 'GET']) def index(): return render_template('index.html') @socketio.on('catch-frame') def catch_frame(data): # 解析客户端传来的base64数据(去掉前缀"data:image/png;base64,") img_data = data['buffer'].split(',')[1] img_bytes = base64.b64decode(img_data) # --- 这里添加你的视频帧处理逻辑 --- # 示例:将彩色图转为灰度图 with Image.open(io.BytesIO(img_bytes)) as img: gray_img = ImageOps.grayscale(img) # 将处理后的图像重新编码为base64格式 buffer = io.BytesIO() gray_img.save(buffer, format='PNG') processed_img_data = base64.b64encode(buffer.getvalue()).decode('utf-8') processed_img_base64 = f"data:image/png;base64,{processed_img_data}" # 将处理后的帧发回客户端 emit('response_back', {'buffer': processed_img_base64}) if __name__ == '__main__': socketio.run(app, host='127.0.0.1', debug=True)
关键修改说明
客户端侧:
- 修复了未定义的
video_t变量,改用隐藏的video元素捕获流,避免直接在页面显示原始视频 - 添加了定时发送帧的逻辑,实现实时流传输(只发送一次帧无法满足实时需求)
- 服务器返回的是base64图片数据,不能用
srcObject(该属性用于媒体流),改用img标签展示处理后的结果 - 增加了断开连接时的资源清理,防止内存泄漏
- 修复了未定义的
服务器侧:
- 配置了跨域允许,解决本地测试时的跨域问题
- 实现了base64数据与图像对象的互转,方便添加处理逻辑
- 示例中加入了灰度处理,你可以替换为OpenCV等工具进行更复杂的图像分析
关于WebRTC的补充:
你提到的WebRTC确实多用于点对点场景,但如果你的需求是必须经过服务器处理视频帧,当前的Socket.io方案更简单直接。如果后续需要更低延迟的传输,可以考虑WebRTC的SFU(选择性转发单元)架构,但实现复杂度会高很多,目前的方案完全能满足你的需求。
内容的提问来源于stack exchange,提问作者akan
相关产品推荐
相关产品推荐

