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

