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

如何实现客户端摄像头视频帧经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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:43:15