当前位置: 首页 > news >正文

浏览器中使用AI模型 实现绘制人体骨架

浏览器中实现绘制人体骨架

最近在为后续项目做技术预研遇到了一个问题,需要再视频上绘制人体骨架,并在相应的关节点上显示相关数据。
开始方案是python通过模型识别人体关键点位置并将位置推送到mqtt服务上并将视频在推送到视频服务器上,然后前端订阅mqtt的数据并拉取python推送的流数据
这样实现后发现视频流会存在很大的延迟,并且没法和响应时间的数据对应上。后面再不停地找新的实现方案的时候,发现现在浏览器已经能使用一些模型了,
后就预研了一下这个方案

方案说明

前端通过chrome浏览器提供的运行模型的能力来进行人体骨架的一个识别,识别完成后,在video元素上叠加一个canvas元素,
在canvas通过识别出来的人体骨架关键点进行人体骨架的绘制

安装插件

pnpm i @mediapipe/tasks-vision 官网文档地址在文章末尾

引用插件

import {DrawingUtils, PoseLandmarker,} from "@mediapipe/tasks-vision";

使用

先创建模型

const createPoseLandmarker = async () => {poseLandmarker = await PoseLandmarker.createFromOptions({wasmBinaryPath: 'http://my.zsyou.top/2024/vision_wasm_internal.wasm', //运行模型需要相关的文件,原始官网是通过调用一个网址获取这两个文件地址,原地址需要科学上网,wasmLoaderPath: 'http://my.zsyou.top/2024/vision_wasm_internal.js'}, {baseOptions: {modelAssetPath: `http://my.zsyou.top/2024/pose_landmarker_lite.task`, //模型文件delegate: "GPU"},runningMode: 'VIDEO',numPoses: 2});console.log('poseLandmarker=====>', poseLandmarker)
};

获取视频流


const start = async () => {await createPoseLandmarker() //因为下载文件需要时间所以提前下载const constraints = {video: true};if (navigator.mediaDevices.getUserMedia) {navigator.mediaDevices.getUserMedia(constraints).then((stream) => {videoElement1.value.srcObject = stream;videoElement1.value.addEventListener("loadeddata", predictWebcam);});} else {message.error('调用摄像头失败')}
}

绘制人体骨架


async function predictWebcam() {const render = () => {const canvasCtx = canvasElement1.value.getContext("2d");const drawingUtils = new DrawingUtils(canvasCtx);let startTimeMs = performance.now();if (lastVideoTime !== videoElement1.value.currentTime) {lastVideoTime = videoElement1.value.currentTime;poseLandmarker.detectForVideo(videoElement1.value, startTimeMs, (result) => {canvasCtx.save();canvasCtx.clearRect(0, 0, canvasElement1.value.width, canvasElement1.value.height);for (const landmark of result.landmarks) {drawingUtils.drawLandmarks(landmark, {color: '#FF0000',fillColor: '#FF0000',radius: 2})drawingUtils.drawConnectors(landmark, PoseLandmarker.POSE_CONNECTIONS, {lineWidth: 2,color: '#00FF00',fillColor: '#00FF00',});}canvasCtx.restore();})}requestAnimationFrame(render);}render();
}

完整代码

<!--------------------------------------------* @Author: shiyzhang zsyou0911@126.com* @Date: 2024/8/23 下午2:31* @Description: * 
------------------------------------------------>
<template><div style="width: 100%"><ASpace><AButton @click="start">开 始</AButton><AButton @click="stop"> 关闭摄像头</AButton></ASpace><div class="pose_land_marker"><video ref="videoElement1" autoplay class="local_video" style="height: 480px;width: 640px"></video><canvas ref="canvasElement1" class="canvas_element" height="480" style="height: 480px;width: 640px;right: 0"width="640"></canvas></div></div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from 'vue'
import {message} from 'ant-design-vue'
import dayjs from 'dayjs'
import {DrawingUtils, PoseLandmarker,} from "@mediapipe/tasks-vision";
import {appendWriteFile, getUserBaseDirHandle} from './untis/index'let poseLandmarker: PoseLandmarker = undefined;
let lastVideoTime = -1;
const videoElement1 = ref(null)
const canvasElement1 = ref(null)
const height = 480
const width = 640const createPoseLandmarker = async () => {poseLandmarker = await PoseLandmarker.createFromOptions({wasmBinaryPath: 'http://my.zsyou.top/2024/vision_wasm_internal.wasm',wasmLoaderPath: 'http://my.zsyou.top/2024/vision_wasm_internal.js'}, {baseOptions: {modelAssetPath: `http://my.zsyou.top/2024/pose_landmarker_lite.task`,delegate: "GPU"},runningMode: 'VIDEO',numPoses: 2});console.log('poseLandmarker=====>', poseLandmarker)
};onMounted(() => {})
//停止
const stop = () => {if (videoElement1.value.srcObject) {videoElement1.value.srcObject?.getTracks().forEach((track) => track.stop());}
}
//开始
const start = async () => {await createPoseLandmarker()const constraints = {video: true};if (navigator.mediaDevices.getUserMedia) {navigator.mediaDevices.getUserMedia(constraints).then((stream) => {videoElement1.value.srcObject = stream;videoElement1.value.addEventListener("loadeddata", predictWebcam);});} else {message.error('调用摄像头失败')}}async function predictWebcam() {const render = () => {const canvasCtx = canvasElement1.value.getContext("2d");const drawingUtils = new DrawingUtils(canvasCtx);let startTimeMs = performance.now();if (lastVideoTime !== videoElement1.value.currentTime) {lastVideoTime = videoElement1.value.currentTime;poseLandmarker.detectForVideo(videoElement1.value, startTimeMs, (result) => {canvasCtx.save();canvasCtx.clearRect(0, 0, canvasElement1.value.width, canvasElement1.value.height);for (const landmark of result.landmarks) {drawingUtils.drawLandmarks(landmark, {color: '#FF0000',fillColor: '#FF0000',radius: 2})drawingUtils.drawConnectors(landmark, PoseLandmarker.POSE_CONNECTIONS, {lineWidth: 2,color: '#00FF00',fillColor: '#00FF00',});}canvasCtx.restore();})}requestAnimationFrame(render);}render();
}</script><style scoped>
.pose_land_marker {position: relative;margin-top: 10px;height: 480px;.canvas_element, .local_video {position: absolute;left: 0;top: 0;}
}
</style>

官网api

https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker/web_js?hl=zh-cn

需要科学上网,里面有多种模型,以及示例

图片

注意部分的@mediapipe/tasks-vision包不一致


http://www.mrgr.cn/news/12279.html

相关文章:

  • Qt详解QUrl
  • 使用GDIView工具排查GDI对象泄漏案例的若干细节总结
  • HighConcurrencyCommFramework c++通讯服务器框架 :TCP粘包解决
  • QT: 将QGraphicsScene中的所有QGraphicsItem转化成Gcode
  • Elastic Stack--EFK架构:采集Nginx与Tomcat日志等
  • Unity项目增加字体裁剪
  • git 拉取分支
  • HarmonyOS开发之Gauge(环形图表)的简单使用
  • Python | Leetcode Python题解之第373题查找和最小的K对数字
  • 【iOS】Masonry学习
  • 前端实习手记(9):修修修修bug
  • 二叉树的三个简单题
  • 速盾:cdn可以解决带宽问题么
  • GalaChain 全面剖析:为 Web3 游戏和娱乐而生的创新区块链
  • QT中通过Tcp协议的多线程的文件传输(服务器)
  • 3 Docker 镜像推送
  • 鸿蒙验证码,鸿蒙认证服务验证码,鸿蒙云存储上传图片
  • 华裔二、三代长相变迁的多维度解析
  • 利用深度学习技术来实现街景图像的语义分割(街景图像语义分割)
  • 如何使用 Nginx 解决跨域问题 (CORS)