作者:荣顶、github
声明:文章为稀土技术社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!
本文 demo 在线体会地址
源代码地址
前语
你可能会想,作为一个前端开发员
,既没有人工智能和机器学习的基础,又没有深厚的学术理论功底能做深度学习
吗?
答案是肯定的。 为什么呢?
首要,咱们要知道的是,现在社区中已经有许多十分老练并且已经练习好的模型,比方:人脸辨认
、人体姿势辨认
、图画分类
、图画切割
、目标检测
等等等等,十分多,这些模型都是经过大量的数据练习得到的,咱们只需求学会怎么运用这些模型,并不需求自己去写算法,去练习模型。它就像 npm
的包相同,装置它,看文档,运用它,就能够了。
如果咱们仅仅从运用深度学习
的视点出发,去运用现成的模型,来处理咱们实际中存在的问题。那么就像前后端分离相同,让专业的人去做专业的事,深度学习也是如此,咱们并不需求去花许多时刻,深化了解深度学习的原理,也不需求自己去练习杂乱的算法模型。
而 Tensorflow.js
便是一扇前端开发人员进入深度学习范畴最好的大门,它供给了一套完整的 API,让咱们能够很便利的运用深度学习模型。它能够在浏览器中运转,也能够在 node.js 中运转。它的 API 规划十分简单,并且它的文档也十分具体,咱们能够很快的上手。
一些常见的深度学习模型能够看 开箱即用的 TensorFlow.js 预练习模型,它们都是开源的 Github 地址。
社区中所有的模型能够在这儿找到 TensorFlow Hub
人体姿势辨认
在这篇文章中,咱们将会介绍怎么运用 WebRTC 相关 API 结合 Tensorflow.js
来完结一个运动直播的运用。
TensorFlow.js 与 WebRTC 结合,能够实实际时的人体姿势检测,然后能够在运动健康的直播中完结人体姿势的跟踪和辨认。这样“老师”,或许“学员”能够更加直观的感受到自己和他人的身体姿势是否共同,能更明晰的观察动作的准确性,共同性。当然,这个运用还能够用于其他的场景,比方:健身房
、瑜伽教室
、舞蹈教室
等等。
人体姿势估量的办法有许多,如:根据深度学习的办法、根据传统机器学习的办法、根据几何的办法、根据动态规划的办法、根据粒子滤波的办法、根据模板匹配的办法、根据图画切割的办法、根据人体姿势模型的办法等。
根据深度学习的办法是现在最流行的人体姿势估量办法。根据深度学习的办法能够分为两类:一类是根据 CNN 的办法,如:OpenPose、PoseNet 等;另一类是根据 RNN 的办法,如:PoseFlow 等。
本文将介绍根据深度学习的办法,运用 Tensorflow.js
的 posenet
模型来完结人体姿势估量。
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
,后来发现不引入会有以下错误
准备工作都做好了,下面咱们开端正式的完结。
加载模型,创立检测器
现在@tensorflow-models/pose-detection
已有 3 种可选模型,分别是:
BlazePose
、MoveNet
、PoseNet
。其间 BlazePose
是根据 CNN 的办法,MoveNet
和 PoseNet
是根据 RNN 的办法。
然后它们大概的特色为:
- MoveNet:是一种速度快、准确率高的姿势检测模型,可检测人体的 17 个要害点,能够以 50+ fps 的速度在笔记本电脑和手机上运转。
- BlazePose:MediaPipe BlazePose 能够检测人体 33 个要害点,除了 17 个 COCO 要害点之外,它还为脸部、手和脚供给了额定的要害点检测。
- PoseNet:能够检测多个姿势,每个姿势包含 17 个要害点。
人体要害点
到这儿,咱们需求了解下人体要害点的界说:
人体姿势估量的原理是经过检测人体的要害点来估量人体的姿势。人体的要害点包含:头部、颈部、肩部、手臂、腰部、腿部等。人体的姿势包含:站立、坐着、躺着、跑步、跳跃等。
MoveNet, PoseNet(COCO 要害点)
COCO 要害点包含:鼻子、左眼、右眼、左耳、右耳、左肩、右肩、左肘、右肘、左手腕、右手腕、左臀、右臀、左膝、右膝、左脚踝、右脚踝。
BlazePose
BlazePose 回来的要害点更多, 有 33 个要害点,除了 17 个 COCO 要害点之外,它还为脸部、手和脚供给了额定的要害点检测。
相关逻辑
PoseNet
最简单了,这儿作为演示,直接运用 PoseNet
让咱们更简单理解。 另外两个,我也会在 demo 中更新。 完整源码在这儿:frontend-park
好,咱们继续,PoseNet
模型后,创立检测器。
其间 createDetector,接纳两个参数,第一个是模型,第二个是模型的相关装备。
可装备项有许多,咱们能够直接 ctrl + 鼠标左键 点击 createDetector 查看。
这儿我就不一一列举了,我这儿主要用到的是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,由于咱们只需求检测到一个人。
检测的成果信息打印出来,如下图所示:
制作画布
从上面的回来信息能够看到,由于咱们设置最多只检测一个人,所以检测到的成果是一个长度为 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 帧左右够够了。
然后咱们能够看到效果图: 体会地址在这儿
将视频撒播输给对端
媒体流处理,创立衔接和信令服务相关的逻辑,我在这个专栏前三篇文章中都有写,这儿就不再赘述了。
然后咱们就能够经过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,实际上这方面的可玩性十分高。体感游戏,换装,语音辨认,人脸辨认,都能够结合这个思路来完结。
好了,这篇文章就到这儿了,如果你觉得这篇文章对你有协助或许有任何疑问,欢迎点赞或许在下方评论区留言,我会及时回复的。感谢支撑。