基于xr-frame实现微信小程序的手部、手势识别3D模型叠加和石头剪刀布游戏功能
前言
xr-frame是一套小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准。xr-frame在基础库v2.32.0开始基本稳定,发布为正式版,但仍有一些功能还在开发,目前(2024.11)有一些限制如下:
1最低要求客户端iOS8.0.29、安卓8.0.30及以上,推荐稳定版在iOS8.0.36、安卓8.0.35及以上。
2基础库最低2.27.1及以上,推荐2.32.0及以上。 3开发工具需要最新版本,建议Nightly版本。
4小程序全局同一时刻只能存在一个xr-frame组件,否则可能会发生异常。
5同一个xr-frame组件只能存在一个xr-scene,并且必须为顶层。 6目前不支持和小程序传统标签比如混写。
7目前不支持wxml自动补全,真机调试需要特别注意,见真机调试文档。
后续的展望:
1 XR-FRAME内置特色的UI组件,让开发者可以在XR-FRAME组件中写UI,来实现一套酷炫的UI系统。 2
AR/VR能力持续增强,支持眼睛设备。 3 交互手段进一步强化,物理碰撞、触发等功能(已完成,待发布)。 4
工具能力强化,包括标签属性自动补全等。在这一文章中,我将会利用以该解决方案的官方demo为参考开发微信小程序的人手识别案例并叠加模型动作的功能,具体使用的是Hand识别模式,去识别出摄像头画面中的会通过图像算法识别出人手部的特征点,然后变换到3D空间,继而进行追踪。用它构建一个XR小程序,实现一个人手识别叠加3D动作模型,实现手势识别的石头剪刀布的小游戏逻辑。
效果
指尖追踪叠加模型:
石头剪刀布效果:
实现过程
Hand模式,从基础库2.28.1开始支持。
其中就需要将模式修改为 手部模式(modes:Hand):
<xr-scene ar-system="modes:Hand" bind:ready="handleReady" bind:tick="handleTick">
</xr-scene>
手部识别模式,会通过图像算法识别出人手部的特征点,然后变换到3D空间,可用于一些手势等场景。与Face模式用法一致,但多出了两个参数:
// 获取手势姿态
const gesture = tracker.gesture;
// 获取总体置信度
const score = tracker.score;
人手特征追踪
人手的识别后,会形成手部对应人手的特征点,特征点的设定如下图:
比如要在大拇指的指尖上叠加一个模型,就使用AR追踪器(xr-ar-tracker)来实现追踪,模式修改为Hand,参照上图的手部特征点数值大拇指为4,同步特征点属性设置为auto-sync=“4”,完整AR追踪器实现如下:
<xr-ar-tracker id="tracker" mode="Hand" auto-sync="4">
<xr-gltf model="hudie" rotation="0 90 -90" anim-autoplay scale="0.5 0.5 0.5"/>
</xr-ar-tracker>
这个就是在大拇指的指尖上叠加了蝴蝶模型,同时自动播放模型动作。
手势识别
图像算法识别出人手部的特征点后,变换到3D空间,进一步会识别出手部的手势,手势的数值通过tracker.gesture获取, tracker.score是手势的置信度,其中手势姿态(0~18,-1为无效/未知手势)如下图:
因为是石头剪刀布的游戏,只用关心这三个状态:布1 ; 剪刀2;石头3;
手势获取通过bind:tick事件(bind:tick=“handleTick”)绑定到handleTick函数,每帧检测手势信息:
handleTick: function () {
if (!this.tracker || this.result) return;
const {gesture, score} = this.tracker;
//console.log(" gesture:"+gesture+" score:"+score);
if (gesture === -1 || score < 0.3 )
{
return;
}
this.triggerEvent('info', {gesture, score});
}
这里的handleTick的处理是将追踪器中的手势信息和置信度信息解析出来,有效手势和置信度大于0.3的再触发事件info,将数据传送到页面。
而页面这边,在wxml中组件中将info事件绑定到handleInfo中处理:
bind:info="handleInfo"
handleInfo函数就将数据记录到data中,而且同时处理石头剪刀布的手势逻辑,和他们的克制关系,让识别出来的手势永远被xr-frame所压制:
handleInfo: function({detail}) {
console.log("handleInfo gesture:"+detail.gesture+" score:"+detail.score);
this.setData({gesture: detail.gesture, score: detail.score.toFixed(2)});
if(this.data.result)
return;
if(this.data.gesture === 1){
this.setData({gesRltImg: 'bu',gesRltName:'布',arRltImg:'jiandao',arRltName:'剪刀'});
}else if(this.data.gesture === 2){
this.setData({gesRltImg: 'jiandao',gesRltName:'剪刀',arRltImg:'shitou',arRltName:'石头'});
}else if(this.data.gesture === 3){
this.setData({gesRltImg: 'shitou',gesRltName:'石头',arRltImg:'bu',arRltName:'布'});
}else{
this.setData({gesRltImg: 'unknow',gesRltName:'未知',arRltImg:'unknow',arRltName:'未知'});
}
},
问题
目前在安卓机实机测试中,感觉变换后手势识别的有些延后,实录如下:
以上的问题,造成了这个石头剪刀布小游戏的体验也不如意,按理识别速度快,可以快速的换手势,系统也能在肉眼不可见的反应时间内,识别出变化,再出一个克制的手势,而现在测试还是无法做到的,要么提前出拳,要么变换后识别不出结果。