鸿蒙开发-想做虚拟试衣?AR Engine人体骨骼检测详解
想给用户戴面具?AR Engine 人体骨骼检测详解
你有没有想过,那些短视频 APP 里的"一键换装"、"人体特效"是怎么做到的?比如给用户身上套一层盔甲,或者在手上放一个发光的魔法球——这些特效的核心,就是人体骨骼检测。
AR Engine 提供了一套人体骨骼追踪能力,能实时识别出人体的 21 个关键点(脖子、肩膀、肘部、手腕、髋部、膝盖、脚踝、脊柱、眼睛、耳朵、鼻子等等),然后告诉你每个关键点在屏幕上的坐标和置信度。有了这些数据,你就可以在人体的任意位置叠加虚拟内容了。
人体骨骼追踪整体流程
下面是人体骨骼追踪的整体流程概览:
怎么开启人体骨骼追踪?
跟其他 AR 能力一样,你需要先配置 ARConfig,把 type 设成 BODY:
import { arEngine, arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.config = {
type: arEngine.ARType.BODY,
poseMode: arEngine.ARPoseMode.GRAVITY_AND_HEADING,
powerMode: arEngine.ARPowerMode.POWER_SAVING,
depthMode: arEngine.ARDepthMode.AUTOMATIC
};
这里最关键的是 type: arEngine.ARType.BODY。这一行告诉 AR Engine:“我要用人体追踪能力”。BODY 这个类型对应的是人体跟踪与骨骼关键点识别能力,起始版本是 6.1.0(23)。
其他几行配置跟 SLAM 追踪差不多:
poseMode:世界坐标系的建立方式,GRAVITY_AND_HEADING表示 Y 轴跟重力对齐,X 轴指向指南针北向。powerMode:省电模式,AR 运算比较吃性能,省电模式能让手机不那么烫。depthMode:深度模式,AUTOMATIC会自动获取深度信息。
另外,如果你需要同时检测多个人体,可以配置 maxDetectedBodyNum:
import { arEngine, arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.config = {
type: arEngine.ARType.BODY,
maxDetectedBodyNum: 2
};
maxDetectedBodyNum 表示要检测的人体数量最大值,默认是 1,最大是 2。如果你设成超过 2 的值,也会被自动限制为 2。所以目前最多同时追踪两个人。
还有一个有用的配置是 cameraLensFacing。默认情况下用的是后置摄像头(REAR),但如果你要做自拍特效(比如给自己戴面具),可以把它设成 FRONT:
import { arEngine, arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.config = {
type: arEngine.ARType.BODY,
cameraLensFacing: arEngine.ARCameraLensFacing.FRONT
};
当 cameraLensFacing 配置为 FRONT 时,type 必须是 ARType.FACE 或 ARType.BODY 才会生效。
配置好之后,初始化 AR 会话:
await context.init();
这一步会启动摄像头和传感器,开始人体骨骼追踪。
怎么获取人体骨骼关键点?
开启追踪之后,你需要在每一帧的回调里获取骨骼数据。AR Engine 提供了 onFrameUpdate 回调,每一帧渲染前都会触发:
import { arEngine, arViewController } from '@kit.AREngine';
import { Node } from '@kit.ArkGraphics3D';
class ARViewCallbackImpl extends arViewController.ARViewCallback {
onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
console.info('onAnchorAdd');
console.info(`add anchor id = ${String(anchor.id)}`);
console.info(`add anchor translation = ${anchor.getPose().translation}`);
console.info(`add node pose = ${node.position}`);
}
onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
console.info('onAnchorUpdate');
console.info(`update anchor id = ${String(anchor.id)}`);
console.info(`update anchor translation = ${anchor.getPose().translation}`);
console.info(`update node pose = ${node.position}`);
}
async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> {
let arSession: arEngine.ARSession | undefined = ctx.session;
if (arSession) {
let frame: arEngine.ARFrame = arSession.getFrame();
if (!frame) {
console.error('Failed to get arSession.frame, it is undefined or null');
} else {
console.info(`Succeeded in getting arSession.frame = ${frame.timestamp}`);
await frame.release();
}
} else {
console.error('Failed to get arSession, arSession is undefined');
}
}
}
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.callback = new ARViewCallbackImpl();
这段代码看起来挺长,但核心就是继承 arViewController.ARViewCallback,然后重写三个回调方法:
onAnchorAdd:当 AR Engine 检测到新的人体时,会自动创建一个锚点和对应的场景节点,然后触发这个回调。你可以在回调里拿到锚点的位姿信息。
onAnchorUpdate:人体的位置会随着运动不断更新,每更新一次就触发这个回调。你可以在这里拿到最新的位姿。
onFrameUpdate:每一帧都会触发,这是你获取骨骼数据的地方。注意用完 ARFrame 之后要调用 frame.release() 释放资源,不然内存会一直涨。
接下来,在 onFrameUpdate 回调里,通过 frame.acquireBodySkeleton() 获取人体骨骼数据:
import { arEngine } from '@kit.AREngine';
// arSession创建参考ARSession.getFrame接口示例代码
const frame: arEngine.ARFrame = arSession.getFrame();
const arbodys: arEngine.ARBody[] = frame.acquireBodySkeleton();
if (arbodys.length > 0) {
arbodys[0].getLandmarks2D();
这一行代码做了三件事:
frame.acquireBodySkeleton():从当前帧中获取所有被检测到的人体对象,返回一个ARBody数组。如果你配置了maxDetectedBodyNum: 2,那这个数组最多会有两个元素。arbodys.length > 0:检查是否检测到了人体。如果没有人出现在摄像头画面里,这个数组就是空的。arbodys[0].getLandmarks2D():获取第一个人体的所有骨骼关键点信息。
ARBody 是什么?
ARBody 是人体跟踪的结果对象,包含两个只读属性:
trackId:当前人体骨骼关键点的 ID。这个 ID 可以用来区分不同的人——如果你同时追踪两个人,第一个人的trackId始终是同一个值,第二个人也是。timeStamp:当前帧的时间戳,单位是纳秒(ns)。
调用 getLandmarks2D() 方法后,会返回一个 ARBodyLandmark2D 数组,每个元素代表一个骨骼关键点。
ARBodyLandmark2D 每个字段是什么意思?
每个 ARBodyLandmark2D 对象包含 5 个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
x |
number | 骨骼关键点的 x 坐标 |
y |
number | 骨骼关键点的 y 坐标 |
confidence |
number | 置信度,取值范围 [0, 1]。越接近 1 表示这个关键点越可信 |
type |
ARBodyLandmarkType | 关键点类型,比如脖子、左肩、右肘等 |
isValid |
boolean | 这个关键点是否有效。true 表示有效,false 表示无效 |
置信度这个东西很重要。打个比方,如果用户把手背到身后,AR Engine 看不到手腕,那手腕关键点的 confidence 就会很低,甚至 isValid 会变成 false。你在使用的时候,一定要先检查 isValid 和 confidence,不然可能会在错误的位置放虚拟物体。
21 个关键点分别在哪?
ARBodyLandmarkType 枚举定义了人体的 21 个关键点。我把它们按部位整理一下:
头部区域(5 个):
RIGHT_EAR(15):右耳RIGHT_EYE(16):右眼NOSE(17):鼻子LEFT_EYE(18):左眼LEFT_EAR(19):左耳
躯干区域(3 个):
NECK(1):颈部SPINE(20):脊柱MID_HIP(14):中髋部(两髋之间的中点)
右臂(3 个):
RIGHT_SHOULDER(2):右肩RIGHT_ELBOW(3):右肘RIGHT_WRIST(4):右腕
左臂(3 个):
LEFT_SHOULDER(5):左肩LEFT_ELBOW(6):左肘LEFT_WRIST(7):左腕
右腿(3 个):
RIGHT_HIP(8):右髋部RIGHT_KNEE(9):右膝RIGHT_ANKLE(10):右脚踝
左腿(3 个):
LEFT_HIP(11):左髋部LEFT_KNEE(12):左膝LEFT_ANKLE(13):左脚踝
有了这 21 个关键点,你就能勾勒出人体的完整骨架。打个比方,如果你想在用户手上放一个虚拟魔法球,你只需要找到 RIGHT_WRIST 或 LEFT_WRIST 关键点的坐标,然后在那个位置渲染一个球就行了。
怎么在关键点位置放模型?
AR Engine 还提供了一个更方便的方式——直接在指定的关键点处放置 3D 模型。你可以用 ARViewContext.loadAsset() 方法:
import { arEngine, arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
// 此处为用户本地存放模型路径
let resourcePath:ResourceStr = $rawfile('xxxx/xxxx');
context.loadAsset(resourcePath, arViewController.LandmarkType.LEFT_EYE);
这行代码会在左眼位置放置一个 3D 模型。$rawfile('xxxx/xxxx') 是你本地存放模型文件的路径。
LandmarkType 枚举定义了 6 个可以放置模型的关键点:
LEFT_EYE(0):左眼LEFT_SIDE_OF_MOUTH(1):嘴巴左侧RIGHT_EYE(2):右眼RIGHT_SIDE_OF_MOUTH(3):嘴巴右侧TIP_OF_NOSE(4):鼻尖CENTER_OF_FACE(5):人脸中心
这些关键点主要集中在面部区域,所以特别适合做"面具"、“墨镜”、"兔耳朵"之类的面部特效。
如果你想移除某个关键点上已经放置的模型,可以用 removeAsset():
import { arEngine, arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.removeAsset(arViewController.LandmarkType.LEFT_EYE);
如果你想一次性清除所有已放置的模型,可以用 clearResource():
import { arViewController } from '@kit.AREngine';
let context: arViewController.ARViewContext = new arViewController.ARViewContext();
context.clearResource();
怎么做表情同步?
AR Engine 还支持微表情追踪。你可以用 setBlendShapeWeight() 方法来设置特定表情的权重:
import { arEngine, arViewController } from '@kit.AREngine';
import { Node } from '@kit.ArkGraphics3D';
onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
ctx.setBlendShapeWeight(node, arEngine.ARBlendShapeType.EYE_BLINK_LEFT, 0.1);
}
这里 EYE_BLINK_LEFT 是左眼眨眼的微表情类型,0.1 是权重值(范围是 0 到 1)。你可以在 onAnchorUpdate 回调里持续更新这个值,让你的虚拟面具跟着用户的表情动起来。
你也可以用 getBlendShapeWeight() 来获取当前的微表情权重:
import { arEngine, arViewController } from '@kit.AREngine';
import { Node } from '@kit.ArkGraphics3D';
onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
ctx.getBlendShapeWeight(node, arEngine.ARBlendShapeType.EYE_BLINK_LEFT);
}
如果传入的 node 或 type 有误,会返回 null。
设备兼容性检查
不是所有设备都支持人体骨骼追踪。在使用之前,建议先进行兼容性检查:
不是所有设备都支持人体骨骼追踪。在使用之前,你可以用 isARTypeSupported() 来检查当前设备是否支持:
import { arEngine, arViewController } from '@kit.AREngine';
arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_BODY);
返回 true 表示支持,false 表示不支持。ARENGINE_FEATURE_TYPE_BODY 对应的就是人体骨骼点识别与跟踪特性。
这个检查很重要,因为人体骨骼追踪需要设备有比较强的算力。如果在不支持的设备上强行使用,可能会报 801(Device not compatible)错误。
骨骼关键点应用流程
获取到骨骼关键点后,可以根据不同需求进行应用:
实际应用场景
说了这么多 API,来看看人体骨骼检测能做什么:
AR 面具/贴纸:在面部关键点位置放置 3D 模型,配合微表情追踪,就能做出跟着用户表情动的虚拟面具。这是最直接的"给用户戴面具"的玩法。
AR 健身教练:通过追踪全身 21 个关键点,判断用户的动作是否标准。比如用户在做深蹲,你可以检查髋部和膝盖的角度,如果角度不对就提示用户调整姿势。
AR 虚拟试衣:追踪人体的肩部、髋部等关键点,估算人体的体型和尺寸,然后在用户身上叠加虚拟衣服。
AR 游戏:让用户用身体动作来控制游戏角色。比如用户举起左手,游戏里的角色也举起左手。
人体骨骼检测是 AR Engine 比较新的能力,起始版本是 6.1.0(23)。它跟人脸追踪(FACE)是独立的两个能力,但可以配合使用——比如你同时开启 FACE 和 BODY,既能追踪人脸做面具,又能追踪身体做全身特效。
需要注意的是,人体骨骼追踪目前只能获取 2D 坐标(屏幕坐标),还没有 3D 坐标的能力。如果你需要在 3D 空间中定位人体的位置,可能需要结合深度信息来做推算。
更多推荐




所有评论(0)