实战:用 AR Engine 做虚拟试衣

虚拟试衣是 AR 技术最热门的应用之一——用户打开摄像头,选择一件衣服,衣服就"穿"在身上了。今天我们来看看用 AR Engine 怎么实现这个功能。

虚拟试衣整体流程

下面是虚拟试衣的完整工作流程:

用户打开摄像头

初始化AR Session

启用人体骨骼追踪

每帧检测人体

检测到人体?

提示用户站在摄像头前

获取骨骼关键点

计算衣服放置位置

根据肩膀和腰部定位

渲染3D衣服模型

实时更新跟随人体移动

虚拟试衣的原理

虚拟试衣的核心是人体骨骼检测 + 3D 模型渲染

  1. 人体骨骼检测:AR Engine 检测摄像头画面中的人体,识别出肩膀、手臂、躯干等关键点
  2. 3D 模型渲染:根据骨骼位置,把衣服的 3D 模型"贴"在正确的位置
  3. 实时更新:随着人体移动,衣服模型跟着移动

第一步:定义人体关键点

import { arEngine } from '@kit.ArkGraphics2D';
import { common } from '@kit.AbilityKit';

导入需要的模块。

// 人体关键点索引
const BODY_JOINTS = {
  HEAD: 0,
  NECK: 1,
  LEFT_SHOULDER: 2,
  RIGHT_SHOULDER: 3,
  LEFT_ELBOW: 4,
  RIGHT_ELBOW: 5,
  LEFT_WRIST: 6,
  RIGHT_WRIST: 7,
  HIP: 8,
  LEFT_HIP: 9,
  RIGHT_HIP: 10,
  LEFT_KNEE: 11,
  RIGHT_KNEE: 12,
  LEFT_ANKLE: 13,
  RIGHT_ANKLE: 14
};

AR Engine 检测人体时,会返回 15 个关键点的位置。每个关键点有一个索引,从头部(0)到脚踝(14)。

第二步:初始化 AR

@Entry
@Component
struct VirtualTryOn {
  @State status: string = '初始化中...';
  @State selectedClothing: string = 'tshirt';
  private arSession: arEngine.ARSession | null = null;

定义状态变量。

  async aboutToAppear() {
    await this.initAR();
  }

  async initAR() {
    const context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;

获取上下文。

    // 检查 AR 能力
    let isSupported = arEngine.isSupport(context);
    if (!isSupported) {
      this.status = '设备不支持 AR';
      return;
    }

先检查设备是否支持 AR。不支持的话直接提示用户。

    // 创建 AR Session
    let config: arEngine.ARSessionConfig = {
      // 启用人体骨骼追踪
      planeFindingMode: arEngine.PlaneFindingMode.DISABLE,
      bodyTrackingMode: arEngine.BodyTrackingMode.ENABLE
    };

    this.arSession = await arEngine.createSession(context, config);

创建 AR Session 时,启用人体骨骼追踪,禁用平面检测(虚拟试衣不需要平面检测)。

    if (this.arSession == null) {
      this.status = '创建 AR Session 失败';
      return;
    }

    // 启动 AR
    this.arSession.start();
    this.status = '请面对摄像头';

启动 AR,开始检测。

第三步:检测人体

    // 开始检测人体
    this.startBodyTracking();
  }

  startBodyTracking() {
    // 每帧检测人体
    setInterval(() => {
      if (this.arSession == null) return;

      // 获取人体数据
      let bodies = this.arSession.getAllTrackables(arEngine.Body);

getAllTrackables(arEngine.Body) 获取所有检测到的人体。返回的是一个数组——理论上可以检测多个人,但我们这里只取第一个。

      if (bodies.length > 0) {
        let body = bodies[0];
        this.status = '检测到人体,正在试衣...';

        // 获取骨骼关键点
        let joints = body.getJoints();
        this.renderClothing(joints);
      } else {
        this.status = '请站在摄像头前';
      }
    }, 100);  // 每 100ms 检测一次
  }

每 100ms 检测一次人体。如果检测到,获取骨骼关键点并渲染衣服。

第四步:获取关键点位置

  renderClothing(joints: Map<number, arEngine.ARBodyJoint>) {
    // 根据选择的衣服类型渲染
    switch (this.selectedClothing) {
      case 'tshirt':
        this.renderTShirt(joints);
        break;
      case 'jacket':
        this.renderJacket(joints);
        break;
      case 'dress':
        this.renderDress(joints);
        break;
    }
  }

根据用户选择的衣服类型调用不同的渲染方法。

  renderTShirt(joints: Map<number, arEngine.ARBodyJoint>) {
    // 获取关键点位置
    let leftShoulder = joints.get(BODY_JOINTS.LEFT_SHOULDER);
    let rightShoulder = joints.get(BODY_JOINTS.RIGHT_SHOULDER);
    let leftHip = joints.get(BODY_JOINTS.LEFT_HIP);
    let rightHip = joints.get(BODY_JOINTS.RIGHT_HIP);

获取肩膀和腰部的关键点。

    if (leftShoulder == null || rightShoulder == null ||
        leftHip == null || rightHip == null) {
      return;
    }

如果关键点检测不到,直接返回。

    // 计算 T 恤的位置和大小
    let centerX = (leftShoulder.getPosition().x + rightShoulder.getPosition().x) / 2;
    let centerY = (leftShoulder.getPosition().y + leftHip.getPosition().y) / 2;
    let width = Math.abs(rightShoulder.getPosition().x - leftShoulder.getPosition().x) * 1.5;
    let height = Math.abs(leftHip.getPosition().y - leftShoulder.getPosition().y);

根据肩膀和腰部的位置计算 T 恤应该放在哪里、多大尺寸。

    // 这里会调用 3D 渲染引擎,把 T 恤模型放在计算出的位置
    // 具体实现依赖于你使用的 3D 渲染方案
    console.info(`渲染 T 恤: 中心(${centerX}, ${centerY}), 大小(${width}x${height})`);
  }

实际项目中,这里会调用 3D 渲染引擎(比如 ArkGraphics 3D)把衣服模型放在计算出的位置。

第五步:页面布局

  build() {
    Column() {
      Text(this.status)
        .fontSize(18)
        .margin({ top: 20 })

      // AR 渲染区域
      // 实际项目中,这里会放一个 AR 视图组件

      // 衣服选择
      Row() {
        Button('T 恤')
          .margin(10)
          .onClick(() => this.selectedClothing = 'tshirt')

        Button('夹克')
          .margin(10)
          .onClick(() => this.selectedClothing = 'jacket')

        Button('裙子')
          .margin(10)
          .onClick(() => this.selectedClothing = 'dress')
      }
      .margin({ top: 20 })
    }
    .width('100%')
    .height('100%')
  }

显示状态文字和衣服选择按钮。

  aboutToDisappear() {
    if (this.arSession != null) {
      this.arSession.stop();
      this.arSession.release();
    }
  }
}

页面销毁时释放 AR 资源。

衣服位置计算流程

根据骨骼关键点计算衣服的放置位置:

获取骨骼关键点

获取左肩位置

获取右肩位置

获取左腰位置

获取右腰位置

计算中心点X

计算中心点Y

计算衣服宽度

计算衣服高度

确定衣服位置和大小

渲染3D衣服模型

实际应用中的注意事项

1. 性能优化

人体骨骼检测很消耗资源。不要每帧都检测,可以:

  • 降低检测频率(比如每 100ms 检测一次)
  • 用上一帧的结果做平滑过渡
  • 在不需要时暂停检测

2. 骨骼点平滑

骨骼检测结果会有抖动。你可以用简单的平滑算法:

smoothJoint(current: Position, previous: Position, factor: number = 0.7): Position {
  return {
    x: previous.x * factor + current.x * (1 - factor),
    y: previous.y * factor + current.y * (1 - factor)
  };
}

3. 遮挡处理

当用户转身或手臂遮挡身体时,部分关键点可能检测不到。你需要处理这些边界情况。

性能优化策略

虚拟试衣需要注意性能优化和边界情况:

虚拟试衣优化

性能优化

骨骼点平滑

遮挡处理

降低检测频率100ms

上一帧结果平滑过渡

不需要时暂停检测

加权平均算法

当前帧0.3+上一帧0.7

转身时关键点丢失

手臂遮挡身体

检测置信度判断

小结

虚拟试衣的核心流程:

  1. arEngine.createSession 创建 AR Session,启用人体追踪
  2. getAllTrackables(arEngine.Body) 检测人体
  3. getJoints() 获取骨骼关键点
  4. 根据关键点位置计算衣服的放置位置
  5. 渲染 3D 衣服模型

AR Engine 提供了人体骨骼检测能力,但 3D 模型渲染部分需要你结合其他图形 API(比如 ArkGraphics 3D)来实现。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐