如何在Vue.js中使用MediaPipe实现人体姿态检测?
Vue中MediaPipe人体姿态检测实现方案
核心问题说明
results的来源:results是MediaPipe Pose的onResults回调函数的参数,由MediaPipe内部完成姿态检测后自动传入,首次出现就是你定义的onResults(results)函数的参数位置,不需要手动定义。- 你错误地在
init方法中直接调用this.results,此时Pose还未启动检测,results根本不存在,这是导致失败的主要原因。 - 绘制逻辑必须放在
onResults回调内,只有当MediaPipe返回检测结果后,才能基于results进行绘制。 - 代码同时混用了ES模块
import和CDN全局变量,导致模块冲突,需统一为CDN方式。
修正后的完整代码
HTML部分
<div id="app"> <div class="container"> <video class="input_video" autoplay playsinline></video> <canvas class="output_canvas" width="1280" height="720"></canvas> <div class="landmark-grid-container"></div> </div> </div> <script src="https://unpkg.com/vue@3"></script> <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@mediapipe/pose/pose.js" crossorigin="anonymous"></script>
JavaScript部分
const App = { name: "App", data() { return { videoElement: null, canvasElement: null, canvasCtx: null, landmarkContainer: null, grid: null, pose: null }; }, mounted() { this.videoElement = document.querySelector(".input_video"); this.canvasElement = document.querySelector(".output_canvas"); this.canvasCtx = this.canvasElement.getContext("2d"); this.landmarkContainer = document.querySelector(".landmark-grid-container"); this.grid = new window.LandmarkGrid(this.landmarkContainer); this.initPose(); }, methods: { initPose() { // 初始化Pose实例 this.pose = new window.Pose({ locateFile: (file) => { return `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`; }, }); // 设置Pose参数 this.pose.setOptions({ modelComplexity: 1, smoothLandmarks: true, enableSegmentation: true, smoothSegmentation: true, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5, }); // 绑定结果回调,确保this指向Vue实例 this.pose.onResults(this.onResults.bind(this)); // 启动相机 const camera = new window.Camera(this.videoElement, { onFrame: async () => { await this.pose.send({ image: this.videoElement }); }, width: 1280, height: 720, }); camera.start(); }, onResults(results) { // 清除画布 this.canvasCtx.save(); this.canvasCtx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height); // 绘制分割蒙版(如果开启) if (results.segmentationMask) { this.canvasCtx.drawImage(results.segmentationMask, 0, 0, this.canvasElement.width, this.canvasElement.height); this.canvasCtx.globalCompositeOperation = "source-in"; this.canvasCtx.fillStyle = "#00FF00"; this.canvasCtx.fillRect(0, 0, this.canvasElement.width, this.canvasElement.height); this.canvasCtx.globalCompositeOperation = "destination-atop"; this.canvasCtx.drawImage(results.image, 0, 0, this.canvasElement.width, this.canvasElement.height); this.canvasCtx.globalCompositeOperation = "source-over"; } else { // 直接绘制原始图像 this.canvasCtx.drawImage(results.image, 0, 0, this.canvasElement.width, this.canvasElement.height); } // 绘制姿态连接点和关键点 if (results.poseLandmarks) { window.drawConnectors(this.canvasCtx, results.poseLandmarks, window.POSE_CONNECTIONS, { color: "#00FF00", lineWidth: 4, }); window.drawLandmarks(this.canvasCtx, results.poseLandmarks, { color: "#FF0000", lineWidth: 2, }); // 更新3D网格 this.grid.updateLandmarks(results.poseWorldLandmarks); } else { this.grid.updateLandmarks([]); } this.canvasCtx.restore(); }, }, }; Vue.createApp(App).mount('#app');
关键修改点说明
- 移除ES模块import:HTML中已通过CDN引入MediaPipe脚本,所有API挂载在
window对象下,直接使用window.Pose、window.drawConnectors即可。 - 绘制逻辑移至
onResults:只有MediaPipe返回检测结果results后,才执行画布绘制、关键点渲染操作。 - 修正
this绑定:用bind(this)确保onResults回调能访问Vue实例属性,避免指向错误。 - 规范实例属性声明:将video、canvas等元素引用放到data中,符合Vue开发规范。
- 增加兼容性判断:针对分割蒙版是否开启做判断,避免无蒙版时的报错。
内容的提问来源于stack exchange,提问作者Bilal
相关产品推荐
相关产品推荐

