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

Angular12集成BlazePose TFJS时estimatePoses返回空数组问题咨询

问题原因及修复方案

1. 为什么返回空数组

  • 核心原因是调用estimatePoses的时机过早:给video元素赋值srcObject之后,视频流需要加载元数据、缓冲第一帧之后才会有有效画面,你在赋值后立刻调用检测,此时视频还没有可用的帧数据,模型自然检测不到任何姿态,返回空数组。
  • 次要排查点:你当前使用的@tensorflow-models/pose-detection@0.0.3是非常早期的版本,BlazePose默认要求输入画面中人体占比不能过小,检测时确保人体完整出现在摄像头画面中。

2. 修复代码

2.1 HTML部分不需要修改,TypeScript部分调整如下:

import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, NgZone, OnDestroy } from '@angular/core';

import '@tensorflow/tfjs-backend-webgl';
import * as poseDetection from '@tensorflow-models/pose-detection';

@Component({
  selector: 'app-pose',
  templateUrl: './pose.component.html',
  styleUrls: ['./pose.component.css']
})
export class PoseComponent implements OnInit, AfterViewInit, OnDestroy {

  @ViewChild("videoplayer", { static: false }) videoplayer: ElementRef;

  public detector: any;
  public poses: any;
  public error: string;
  private isRunning = false; // 控制检测循环的开关

  // 注入NgZone避免Angular变更检测失效
  constructor(private ngZone: NgZone) { }

  ngOnInit(): void {}

  ngAfterViewInit () : void{
    this.init();
  }

  async init() {
    if (!navigator.mediaDevices?.getUserMedia) {
      this.error = "当前浏览器不支持摄像头调用";
      return;
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { width: 640, height: 480 } // 固定尺寸适配BlazePose输入要求,提升检测效率
      });
      this.videoplayer.nativeElement.srcObject = stream;
      // 等待视频加载完成后再初始化检测器和启动检测循环
      this.videoplayer.nativeElement.oncanplay = async () => {
        console.log("视频已就绪,开始加载检测器");
        const detectorConfig = {
          runtime: 'tfjs',
          enableSmoothing: true,
          modelType: 'full'
        };
        this.detector = await poseDetection.createDetector(poseDetection.SupportedModels.BlazePose, detectorConfig);
        console.log("检测器加载完成,启动检测循环");
        this.isRunning = true;
        this.detectLoop();
        this.error = null;
      };
    } catch (e) {
      this.error = e.message || "摄像头调用失败";
    }
  }

  // 实时检测循环
  async detectLoop() {
    if (!this.isRunning || !this.detector) return;
    try {
      const poses = await this.detector.estimatePoses(this.videoplayer.nativeElement);
      console.log("检测结果:", poses);
      this.poses = poses;
    } catch (e) {
      console.error("检测出错:", e);
    }
    // 用requestAnimationFrame实现和浏览器帧率同步的循环
    this.ngZone.runOutsideAngular(() => {
      requestAnimationFrame(() => this.detectLoop());
    });
  }

  // 组件销毁时停止循环,释放资源
  ngOnDestroy() {
    this.isRunning = false;
    const stream = this.videoplayer?.nativeElement?.srcObject;
    if (stream) {
      stream.getTracks().forEach(track => track.stop());
    }
  }

}

3. 补充说明

  • 持续检测必须手动实现循环,推荐使用requestAnimationFrame而不是setInterval,前者会在浏览器每次重绘前执行,和视频渲染帧率同步,性能更好、不会出现掉帧的情况。
  • 代码中加入了NgZone的使用,因为requestAnimationFrame的回调是在Angular的变更检测区域外执行的,如果你后续需要把姿态数据渲染到模板上,需要用ngZone.run包裹赋值逻辑才能触发视图更新,如果只需要在控制台打印结果可以省略这部分。
  • 组件销毁时要手动停止检测循环、关闭摄像头流,避免内存泄漏和摄像头一直被占用的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 15:15:08