作者:荣顶、github
声明:文章为稀土技术社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

本文 demo 在线体会地址
源代码地址

前语

你可能会想,作为一个前端开发员,既没有人工智能和机器学习的基础,又没有深厚的学术理论功底能做深度学习吗?

答案是肯定的。 为什么呢?

首要,咱们要知道的是,现在社区中已经有许多十分老练并且已经练习好的模型,比方:人脸辨认人体姿势辨认图画分类图画切割目标检测等等等等,十分多,这些模型都是经过大量的数据练习得到的,咱们只需求学会怎么运用这些模型,并不需求自己去写算法,去练习模型。它就像 npm 的包相同,装置它,看文档,运用它,就能够了。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

如果咱们仅仅从运用深度学习的视点出发,去运用现成的模型,来处理咱们实际中存在的问题。那么就像前后端分离相同,让专业的人去做专业的事,深度学习也是如此,咱们并不需求去花许多时刻,深化了解深度学习的原理,也不需求自己去练习杂乱的算法模型。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

Tensorflow.js 便是一扇前端开发人员进入深度学习范畴最好的大门,它供给了一套完整的 API,让咱们能够很便利的运用深度学习模型。它能够在浏览器中运转,也能够在 node.js 中运转。它的 API 规划十分简单,并且它的文档也十分具体,咱们能够很快的上手。

一些常见的深度学习模型能够看 开箱即用的 TensorFlow.js 预练习模型,它们都是开源的 Github 地址。

社区中所有的模型能够在这儿找到 TensorFlow Hub

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

人体姿势辨认

在这篇文章中,咱们将会介绍怎么运用 WebRTC 相关 API 结合 Tensorflow.js 来完结一个运动直播的运用。

TensorFlow.js 与 WebRTC 结合,能够实实际时的人体姿势检测,然后能够在运动健康的直播中完结人体姿势的跟踪和辨认。这样“老师”,或许“学员”能够更加直观的感受到自己和他人的身体姿势是否共同,能更明晰的观察动作的准确性,共同性。当然,这个运用还能够用于其他的场景,比方:健身房瑜伽教室舞蹈教室等等。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

人体姿势估量的办法有许多,如:根据深度学习的办法、根据传统机器学习的办法、根据几何的办法、根据动态规划的办法、根据粒子滤波的办法、根据模板匹配的办法、根据图画切割的办法、根据人体姿势模型的办法等。

根据深度学习的办法是现在最流行的人体姿势估量办法。根据深度学习的办法能够分为两类:一类是根据 CNN 的办法,如:OpenPose、PoseNet 等;另一类是根据 RNN 的办法,如:PoseFlow 等。

本文将介绍根据深度学习的办法,运用 Tensorflow.jsposenet 模型来完结人体姿势估量。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

ok,相关铺垫就到这儿,下面咱们开端正式的完结。

开搞

装置相关依靠

首要,咱们需求装置 Tensorflow.js 相关的依靠。

npm i @tensorflow-models/pose-detection @tensorflow/tfjs-backend-webgl

其间 @tensorflow-models/pose-detection 这个包供给了多个最先进的模型来运转实时姿势检测。@tensorflow/tfjs-backend-webgl 这个包为 TensorFlow.js 完结了一个 GPU 加速的 WebGL 后端。能够让咱们在浏览器中运转 Tensorflow.js

装置好后,引入它们

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

一开端我不知道运用@tensorflow/tfjs-backend-webgl,后来发现不引入会有以下错误

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

准备工作都做好了,下面咱们开端正式的完结。

加载模型,创立检测器

现在@tensorflow-models/pose-detection 已有 3 种可选模型,分别是:

BlazePoseMoveNetPoseNet。其间 BlazePose 是根据 CNN 的办法,MoveNetPoseNet 是根据 RNN 的办法。

然后它们大概的特色为:

  • MoveNet:是一种速度快、准确率高的姿势检测模型,可检测人体的 17 个要害点,能够以 50+ fps 的速度在笔记本电脑和手机上运转。
  • BlazePose:MediaPipe BlazePose 能够检测人体 33 个要害点,除了 17 个 COCO 要害点之外,它还为脸部、手和脚供给了额定的要害点检测。
  • PoseNet:能够检测多个姿势,每个姿势包含 17 个要害点。

人体要害点

到这儿,咱们需求了解下人体要害点的界说:

人体姿势估量的原理是经过检测人体的要害点来估量人体的姿势。人体的要害点包含:头部、颈部、肩部、手臂、腰部、腿部等。人体的姿势包含:站立、坐着、躺着、跑步、跳跃等。

MoveNet, PoseNet(COCO 要害点)

COCO 要害点包含:鼻子、左眼、右眼、左耳、右耳、左肩、右肩、左肘、右肘、左手腕、右手腕、左臀、右臀、左膝、右膝、左脚踝、右脚踝。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

BlazePose

BlazePose 回来的要害点更多, 有 33 个要害点,除了 17 个 COCO 要害点之外,它还为脸部、手和脚供给了额定的要害点检测。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

相关逻辑

PoseNet最简单了,这儿作为演示,直接运用 PoseNet让咱们更简单理解。 另外两个,我也会在 demo 中更新。 完整源码在这儿:frontend-park

好,咱们继续,PoseNet模型后,创立检测器。

其间 createDetector,接纳两个参数,第一个是模型,第二个是模型的相关装备。
可装备项有许多,咱们能够直接 ctrl + 鼠标左键 点击 createDetector 查看。

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

这儿我就不一一列举了,我这儿主要用到的是modelType,它有三种模型的类型可供挑选,分别为:”lite”、”full “和 “heavy”。这些改变了检测模型的巨细。Lite 的检测精度最低,但功能最好,而 “heavy “的检测精度最好,但更耗费功能,而 “full “则处于中心位置。咱们挑选了它 。

然后咱们能够界说一个初始化的函数,在这儿面,把一些初始化操作都完结,比方:打开摄像头,获取媒体流,并且将媒体流赋值给 video 标签…

<video id="video" autoplay playsinline class="w-[360px] h-[270px] object-fill"></video>
<canvas id="output" width="360" height="270"></canvas>
// 其他地方要用到的公共变量
let posenetInput: HTMLVideoElement | HTMLImageElement | HTMLCanvasElement
let posenetOutput: HTMLCanvasElement
let posenetOutputCtx: CanvasRenderingContext2D
let detector: PoseDetector
let model: poseDetection.SupportedModels.PoseNet
// 初始化
const init = async () => {
  // 获取 canvas 元素
  posenetOutput = document.getElementById('output') as HTMLCanvasElement
  posenetOutputCtx = posenetOutput.getContext('2d')!
  // 获取视频流
  posenetInput = document.getElementById('video') as HTMLVideoElement
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true,
  })
  posenetInput.srcObject = stream
  // 界说模型
  model = poseDetection.SupportedModels.PoseNet
  // 加载模型
  detector = await poseDetection.createDetector(model, {
    modelType: 'full',
  })
  // 开端检测
  detectPose()
}

然后咱们能够界说一个检测的函数,这儿咱们需求传入一个检测器,然后在这个函数里边,咱们能够获取到检测到的成果,然后咱们能够根据成果,来制作咱们的画布。

// 开端检测
const detectPose = async () => {
  // 获取检测成果
  const poses = await detector.estimatePoses(posenetInput, {
    flipHorizontal: false, // 是否水平翻转
    maxPoses: 1, // 最大检测人数
    scoreThreshold: 0.5, // 置信度
    nmsRadius: 20, // 非极大值按捺
  })
  // 将 pose 上的 17 个要害点的坐标信息存入 pointList
  const pointList = poses[0]?.keypoints || []
  console.log(' / pointList', pointList)
  // 。。。制作画布
}

这儿咱们能够经过 detector.estimatePoses 获取到检测到的成果,这儿咱们需求传入两个参数,第一个是咱们的输入,第二个是咱们的装备项,例如其间的 maxPoses,它表示最大检测人数,咱们这儿设置为 1,由于咱们只需求检测到一个人。

检测的成果信息打印出来,如下图所示:

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

制作画布

从上面的回来信息能够看到,由于咱们设置最多只检测一个人,所以检测到的成果是一个长度为 1 的数组,里边 keypoints 中有 17 个要害点的坐标信息,咱们能够经过这些坐标信息,来制作咱们的画布。

// 开端检测
const detectPose = async () => {
  // 。。。接上面的代码
  // 将 pose 上的 17 个要害点的坐标信息存入一个数组中
  const pointList = poses[0]?.keypoints || []
  // 制作视频
  posenetOutputCtx.drawImage(posenetInput, 0, 0, canvas.width, canvas.height)
  // 将这 17 个要害点的坐标信息 画到 canvas 上
  // 画出所有要害点
  pointList.forEach(({ x, y, score, name }: any) => {
    if (score > 0.5) {
      // 画点
      drawPoint(x, y, 5, '#f00000', posenetOutputCtx)
    }
  })
  // 获取相邻的要害点信息
  const adjacentPairs = poseDetection.util.getAdjacentPairs(model)
  // 画出所有连线
  adjacentPairs.forEach(([i, j]: any) => {
    const kp1 = pointList[i]
    const kp2 = pointList[j]
    // score 不为空就画线
    const score1 = kp1.score != null ? kp1.score : 1
    const score2 = kp2.score != null ? kp2.score : 1
    if (score1 >= 0.5 && score2 >= 0.5) {
      // 画出所有连线
      drawSegment([kp1.x, kp1.y], [kp2.x, kp2.y], 'aqua', 1, posenetOutputCtx)
    }
  })
  // requestAnimationFrame(() => detectPose(detector))
  setTimeout(() => {
    detectPose()
  }, 50)
}

封装好一个画点和画线段的函数,便利上面运用。↑

// 画点
function drawPoint(x: number, y: number, r: number, color: string, ctx: CanvasRenderingContext2D) {
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2 * Math.PI)
  ctx.fillStyle = color
  ctx.fill()
}
// 画线段
function drawSegment([ax, ay]: number[], [bx, by]: number[], color: string, scale: number, ctx: CanvasRenderingContext2D) {
  ctx.beginPath()
  ctx.moveTo(ax * scale, ay * scale)
  ctx.lineTo(bx * scale, by * scale)
  ctx.lineWidth = 4
  ctx.strokeStyle = color
  ctx.stroke()
}

拿到 17 个点的信息后,咱们先将视频制作到画布上,然后再将这 17 个要害点的坐标信息画到画布上,其间只要在 score 大于 0.5 的时分,才会制作到画布上。 然后咱们经过 poseDetection.util.getAdjacentPairs 获取到相邻的要害点信息,然后再将这些要害点进行连线,制作到画布上。

然后再尾部递归调用 detectPose 函数,这样就能够实实际时的检测了。你能够用 requestAnimationFrame 来完结,也能够用 setTimeout 来完结,requestAnimationFrame 一般是每秒 60 次 , 也便是常说的 60 帧(60FPS),如果计算量特别大导致你电脑卡的话,你也能够用 setTimeout 自己界说间隔时长,这样就能够控制帧数了。 一般来说人眼能够感知的帧数是 24 帧,电影院的帧数也是 24 帧,所以 24 帧左右够够了。

然后咱们能够看到效果图: 体会地址在这儿

WebRTC + Tensorflow.js 在运动健康类项目中的前端运用

将视频撒播输给对端

媒体流处理,创立衔接和信令服务相关的逻辑,我在这个专栏前三篇文章中都有写,这儿就不再赘述了。

然后咱们就能够经过captureStream API 从 canvas 中拿到视频流,然后经过 RTCPeerConnection 供给的 API 将视频流轨迹 加到 peerConnection 中传输给对端。

const peerConnection = new RTCPeerConnection({
  iceServers: [
    {
      urls: 'stun:stun.voipbuster.com ',
    },
  ],
})
// 获取output 中的视频流
const getVideo = () => {
  const output = document.getElementById('output') as HTMLCanvasElement
  const stream = output.captureStream()
  return stream
}
// 传输视频流
const transfer = () => {
  const stream = getVideo()
  stream.getTracks().forEach((track) => {
    peerConnection.addTrack(track, stream)
  })
}

最后

这篇文章主要是介绍了怎么运用 WebRTC 与 TensorFlow.js 的结合,实实际时的人体姿势检测。这儿仅仅简单的做了一个 demo,实际上这方面的可玩性十分高。体感游戏,换装,语音辨认,人脸辨认,都能够结合这个思路来完结。

好了,这篇文章就到这儿了,如果你觉得这篇文章对你有协助或许有任何疑问,欢迎点赞或许在下方评论区留言,我会及时回复的。感谢支撑。