VisionKit

微信小程序也在基础库 2.20.0 版本开始提供了开发 AR 功能的能力,即 VisionKit。VisionKit 包含了 AR 在内的视觉算法,要想开发微信小程序的 AR 功能,我们需要先了解 VisionKit。

VKSession

VisionKit 的核心就是 VKSession,即 VisionKit 会话对象。我们可以通过 wx.createVKSession 来创建 VKSession 的实例,此实例在页面上是单例,和页面的生命周期强相关,且页面间的 VKSeesion 实例运行周期互斥,这就确保了一个微信小程序在一个确定的时刻最多只会有一个 VKSession 实例,下面的 demo 以 v2 版本为例。

const session = wx.createVKSession({
  track: {
    plane: {mode: 3},
  },
  version: 'v2', 
})

调 VKSession 实例的 start 方法可以启动 VKSession 实例:

session.start(err => {
  if (err) return console.error('VK error: ', err)

  // do something
})

接下来,我们要构建 3D 世界和渲染了。

渲染

AR 本意即为增强现实,通俗来讲就是可以在现实世界融入虚拟的东西,比如在现实世界的桌面上放一个虚拟的机器人。

那么要在微信小程序中看到这个效果,我们首先要能将现实画面画到屏幕上,这就依赖我们的摄像头了。当然,画面不是静止不动的,所以我们还得连续的将摄像头拍到的画面上屏,这就和我们使用 WebGL 绘制 3D 世界类似,逐帧渲染:

session.start(err => {
  if (err) return console.error('VK error: ', err)

  const onFrame = timestamp => {
    const frame = session.getVKFrame(canvas.width, canvas.height)
    if (frame) {
      renderFrame(frame)
    }

    session.requestAnimationFrame(onFrame)
  }
  session.requestAnimationFrame(onFrame)
})

在大家熟知的 requestAnimationFrame 内,通过 VKSession 实例的 getVKFrame 方法可以获取到帧对象,帧对象中即包含了我们需要上屏的画面。此处我们在调 getVKFrame 时传入了画布的宽高,是因为我们此处就准备将其用 WebGL 渲染出来,之后我们就来看看 renderFrame 里是如何做的:

function renderFrame(frame) {
  renderGL(frame)

  // do something
}

function renderGL(frame) {
  const { yTexture, uvTexture } = frame.getCameraTexture(gl, 'yuv')
  const displayTransform = frame.getDisplayTransform()

  // 上屏
}

通过 getCameraTexture 我们可以拿到 yuv 纹理,而此纹理是未经裁剪调整的纹理,所以还需要通过 getDisplayTransform 获取到纹理调整矩阵,然后在上屏时可以使用此矩阵对纹理进行裁剪调整。此处代码中的 gl 即是 WebGLRenderingContext 实例。

WebGL & three.js

那么上屏需要如何操作呢?这里需要我们拥有一定的 WebGL 知识,在此 demo 中我们自己编写着色器来将画面渲染到画布上,用 three.js 来渲染 3D 模型。

首先是初始化 three.js 部分:

import { createScopedThreejs } from 'threejs-miniprogram'
import { registerGLTFLoader } from './loaders/gltf-loader'

const THREE = createScopedThreejs(canvas)
registerGLTFLoader(THREE)

// 相机
const camera = new THREE.Camera()

// 场景
const scene = new THREE.Scene()

// 光源
const light1 = new THREE.HemisphereLight(0xffffff, 0x444444) // 半球光
light1.position.set(0, 0.2, 0)
scene.add(light1)
const light2 = new THREE.DirectionalLight(0xffffff) // 平行光
light2.position.set(0, 0.2, 0.1)
scene.add(light2)

// 渲染层
const renderer = new THREE.WebGLRenderer({antialias: true, alpha: true})
renderer.gammaOutput = true
renderer.gammaFactor = 2.2

// 机器人模型
const loader = new THREE.GLTFLoader()
let model
loader.load('https://dldir1.qq.com/weixin/miniprogram/RobotExpressive_aa2603d917384b68bb4a086f32dabe83.glb', gltf => {
  model = {
    scene: gltf.scene,
    animations: gltf.animations,
  }
})
const clock = new THREE.Clock()

此处使用 threejs-miniprogram 包,这是经过特殊封装以兼容微信小程序环境的 three.js 包,当然开发者们也可以替换成任意其它可以在微信小程序中跑的 WebGL 引擎,此处仅仅是以 three.js 来举例。registerGLTFLoader 则是用来加载 3D 模型。关于 three.js 的使用,这里只是给出了一个简单的 demo,有兴趣者可以查阅官方文档进行了解。

接下来是初始化 WebGL:

const gl = renderer.getContext()

// 编写着色器
const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM)
const vs = `
  attribute vec2 a_position;
  attribute vec2 a_texCoord;
  uniform mat3 displayTransform;
  varying vec2 v_texCoord;
  void main() {
    vec3 p = displayTransform * vec3(a_position, 0);
    gl_Position = vec4(p, 1);
    v_texCoord = a_texCoord;
  }
`
const fs = `
  precision highp float;

  uniform sampler2D y_texture;
  uniform sampler2D uv_texture;
  varying vec2 v_texCoord;
  void main() {
    vec4 y_color = texture2D(y_texture, v_texCoord);
    vec4 uv_color = texture2D(uv_texture, v_texCoord);

    float Y, U, V;
    float R ,G, B;
    Y = y_color.r;
    U = uv_color.r - 0.5;
    V = uv_color.a - 0.5;

    R = Y + 1.402 * V;
    G = Y - 0.344 * U - 0.714 * V;
    B = Y + 1.772 * U;

    gl_FragColor = vec4(R, G, B, 1.0);
  }
`
const vertShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertShader, vs)
gl.compileShader(vertShader)

const fragShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragShader, fs)
gl.compileShader(fragShader)

const program = gl.createProgram()
gl.attachShader(program, vertShader)
gl.attachShader(program, fragShader)
gl.deleteShader(vertShader)
gl.deleteShader(fragShader)
gl.linkProgram(program)
gl.useProgram(program)

const uniformYTexture = gl.getUniformLocation(program, 'y_texture')
gl.uniform1i(uniformYTexture, 5)
const uniformUVTexture = gl.getUniformLocation(program, 'uv_texture')
gl.uniform1i(uniformUVTexture, 6)

const dt = gl.getUniformLocation(program, 'displayTransform')
gl.useProgram(currentProgram)

// 初始化 VAO
const ext = gl.getExtension('OES_vertex_array_object')
const currentVAO = gl.getParameter(gl.VERTEX_ARRAY_BINDING)
const vao = ext.createVertexArrayOES()

ext.bindVertexArrayOES(vao)

const posAttr = gl.getAttribLocation(program, 'a_position')
const pos = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, pos)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW)
gl.vertexAttribPointer(posAttr, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(posAttr)
vao.posBuffer = pos

const texcoordAttr = gl.getAttribLocation(program, 'a_texCoord')
const texcoord = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, texcoord)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, 0, 1, 1, 0, 0, 0]), gl.STATIC_DRAW)
gl.vertexAttribPointer(texcoordAttr, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(texcoordAttr)
vao.texcoordBuffer = texcoord

ext.bindVertexArrayOES(currentVAO)

这一块属于 WebGL 的知识,这里就不再做过多赘述,有兴趣者可以借助搜索引擎查阅相关资料了解。之后我们就可以完善前面的 renderGL 方法,完成上屏代码的编写:

function renderGL(frame) {
  const gl = renderer.getContext()
  gl.disable(gl.DEPTH_TEST)

  // 获取纹理和调整矩阵
  const {yTexture, uvTexture} = frame.getCameraTexture(gl, 'yuv')
  const displayTransform = frame.getDisplayTransform()

  if (yTexture && uvTexture) {
    const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM)
    const currentActiveTexture = gl.getParameter(gl.ACTIVE_TEXTURE)
    const currentVAO = gl.getParameter(gl.VERTEX_ARRAY_BINDING)

    gl.useProgram(program)
    ext.bindVertexArrayOES(vao)

    // 传入调整矩阵
    gl.uniformMatrix3fv(dt, false, displayTransform)
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1)

    // 传入 y 通道纹理
    gl.activeTexture(gl.TEXTURE0 + 5)
    const bindingTexture5 = gl.getParameter(gl.TEXTURE_BINDING_2D)
    gl.bindTexture(gl.TEXTURE_2D, yTexture)

    // 传入 uv 通道纹理
    gl.activeTexture(gl.TEXTURE0 + 6)
    const bindingTexture6 = gl.getParameter(gl.TEXTURE_BINDING_2D)
    gl.bindTexture(gl.TEXTURE_2D, uvTexture)

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)

    gl.bindTexture(gl.TEXTURE_2D, bindingTexture6)
    gl.activeTexture(gl.TEXTURE0 + 5)
    gl.bindTexture(gl.TEXTURE_2D, bindingTexture5)

    gl.useProgram(currentProgram)
    gl.activeTexture(currentActiveTexture)
    ext.bindVertexArrayOES(currentVAO)
  }
}

至此,基础背景画面就画到屏幕上了,在手机上看到的效果就如同开着摄像头一样。

放置 3D 模型

诚然,仅效果来看,到此为止还不能将其称其为 AR,接下来我们要实现这么一个功能:点击屏幕,然后在画面上对应的 3D 世界位置放置一个机器人模型;比如点击画面中的桌子,就在桌子上放一个机器人模型。

前面我们引入的 three.js 就是为了做这个效果,现在流行的 WebGL 引擎基本上都封装了大量方便我们快速使用的接口,比如光照渲染、模型加载等,前面的代码已经做过演示,这里就不再重复说明。

这里我们需要了解的是 VKSession 的 hitTest 接口。这个接口的主要是为了将 2D 坐标转成 3D 世界坐标,即 (x, y) 转成 (x, y, z)。通俗来说就是画面上显示的桌子,在屏幕上它是 2D 的,当我们手指触摸屏幕时拿到的坐标是 2D 坐标,也就是 (x, y);hitTest 接口可以将其转换成 3D 世界坐标 (x, y, z),而 3D 世界坐标系的原点则是相机打开瞬间其所在的点:

function onTouchEnd(evt) {
  const touches = evt.changedTouches.length ? evt.changedTouches : evt.touches

  // 在点击位置放一个机器人模型
  if (touches.length === 1) {
    const touch = touches[0]
    if (session && scene && model) {
      // 调用 hitTest
      const hitTestRes = session.hitTest(touch.x / width, touch.y / height)
      if (hitTestRes.length) {
        model.scene.scale.set(0.05, 0.05, 0.05)

        // 动画混合器
        const mixer = new THREE.AnimationMixer(scene)
        for (let i = 0; i < model.animations.length; i++) {
          const clip = model.animations[i]
          if (clip.name === 'Dance') {
            const action = mixer.clipAction(clip)
            action.play()
          }
        }

        // 把模型放到对应的位置上
        const cnt = new THREE.Object3D()
        cnt.add(model.scene)
        model.matrixAutoUpdate = false
        model.matrix.fromArray(hitTestRes[0].transform)
        scene.add(model)
      }
    }
  }
}

可以看到 hitTest 传入的两个参数并不是标准的坐标值,而是将其除以画布宽高后得到的值再传入。这里接受的参数其实是相对于画布视窗的坐标,取值范围为 [0, 1],0 为左/上边缘,1 为右/下边缘。而 hitTest 返回的结果则是矩阵,里面包含了 3D 世界坐标的位置、旋转和放缩信息。可以看到这矩阵可以直接为 three.js 所用,这也是此次 demo 选用 three.js 的原因之一,它封装了很多繁杂的实现细节,简化了大量代码。

之后就是调 three.js 相关的渲染接口,把机器人模型也画到画面上,这里我们可以继续完善前面的 renderFrame 方法:

function renderFrame(frame) {
  renderGL(frame)

  const frameCamera = frame.camera

  // 更新动画
  const dt = clock.getDelta()
  mixer.update(dt)

  // 相机
  if (camera) {
    camera.matrixAutoUpdate = false
    camera.matrixWorldInverse.fromArray(frameCamera.viewMatrix)
    camera.matrixWorld.getInverse(camera.matrixWorldInverse)

    const projectionMatrix = frameCamera.getProjectionMatrix(NEAR, FAR)
    camera.projectionMatrix.fromArray(projectionMatrix)
    camera.projectionMatrixInverse.getInverse(camera.projectionMatrix)
  }

  renderer.autoClearColor = false
  renderer.render(scene, camera)
  renderer.state.setCullFace(THREE.CullFaceNone)
}

这里通过帧对象的 camera 属性拿到了帧相机,然后通过帧相机的 viewMatrix 拿到了视图矩阵,通过 getProjectionMatrix 方法拿到了投影矩阵,统统传给 three.js 的相机对象,以确保 three.js 的相机位置、角度正确,同时确保 3D 世界渲染出来的效果符合我们人眼所看到的景象。

至此,前面那个点屏幕点击位置对应的 3D 世界放置一个机器人模型的效果得以完成。

平面检测

在对如何在微信小程序中实现一个 AR 功能有所了解后,我们可能需要扩展一些场景:比如需要检测出 3D 世界的平面。

VisionKit 识别到的平面会以 anchor 对象的方式提供给我们,这里 VKSession 提供了很便利的事件:addAnchors/updateAnchors/removeAnchors,通过这三个事件我们可以监听 anchor 列表的变化:

session.on('addAnchors', anchors => {
  // anchor.id - anchor 唯一标识
  // anchor.type - anchor 类型,0 表示是平面 anchor
  // anchor.transform - 包含位置、旋转、放缩信息的矩阵,以列为主序
  // anchor.size - 尺寸
  // anchor.alignment - 方向

  // do something
})
session.on('updateAnchors', anchors => {
  // do something
})
session.on('removeAnchors', anchors => {
  // do something
})
  • 程序示例
    可以在 VisionKit基础能力使用参考 页面查看示例代码。

6DoF-平面 AR能力

方法定义

6DoF-平面AR能力,提供基础AR功能,提供旋转和平移6自由度的定位功能。

包括 V1V2 两种适用不同场景的算法,两种平面AR能力各有优劣,用户根据适用场景及产品需求,自行判断调用接口类型,具体介绍如下:

  1. V1平面接口,适用于用户在平面场景下,例如桌面,地面,泛平面场景,放置虚拟物体,不提供真实世界距离。用户放置物体时,手机相机倾斜向下对着目标平面点击即可,具有广泛的机型支持。
  2. V2平面接口,提供真实物理距离的AR定位功能,提供平面识别功能,用户在平面范围点击放置虚拟物体的功能,具有有限的支持机型。

能力扩展

V2 平面基础上,可以通过配置开启多种扩展能力,比如:

  1. marker 识别能力,即平面空间下多个不同识别目标的识别。
  2. 虚实遮挡的能力,即虚拟物体和真实世界的交互遮挡能力。

更多使用效果与开关配置细节,可以参考 平面AR能力扩展。

如何开启 V1 或 V2

初始化时,通过 VKSession 配置 version 确定。

const session = wx.createVKSession({
  track: {
    plane: {
        mode: 1
    },
  },
  version: 'v2' // 在满足设备条件时开启,否则会使用 v1
  // version: "v1" 强制开启v1
})

有关完整的VKSession配置,详见 VKSession配置参考

V1 与 V2 对比

接口类型 平面检测 真实距离 初始化速度 机型覆盖率 功耗 精度 多物体放置效果 多种扩展能力
V1


V2




应用场景示例

V1平面演示

planev1_gif

V2平面演示

planev2_gif

V2平面+虚实遮挡演示

planev2_deepocc_gif

程序示例

  1. 可以在 V1平面 页面查看示例代码。
  2. 可以在 V2平面 页面查看示例代码。
  3. 可以在 V2平面+虚实遮挡 页面查看示例代码,在微信小程序示例中的接口-VisionKit视觉能力-水平面AR-v2-虚实遮挡中体验。

附录

V1平面接口系统要求

  1. iOS机型要求: iPhone 6s及以上机型
  2. Android机型要求:Android 7.0, Android SDK 24及以上

V2平面AR接口支持列表

  1. iOS机型要求: iPhone 7及以上机型

  2. Android机型支持会逐步增加,如测试机型效果有误可论坛反馈,目前支持机型包括:

手机厂商 手机型号
Hi Hi nova 9
OPPO OPPO A32
OPPO OPPO A53
OPPO OPPO A57
OPPO OPPO A72 5G
OPPO OPPO A92s
OPPO OPPO A93s
OPPO OPPO A93
OPPO OPPO A95
OPPO OPPO Ace2
OPPO OPPO Find X2
OPPO OPPO Find X3 Pro
OPPO OPPO Find X3
OPPO OPPO Find X5 Pro
OPPO OPPO Find X5
OPPO OPPO K10 Pro
OPPO OPPO K10 Pro
OPPO OPPO K10x
OPPO OPPO K10
OPPO OPPO K3
OPPO OPPO K5
OPPO OPPO K9 Pro
OPPO OPPO Pad
OPPO OPPO R15
OPPO OPPO R17
OPPO OPPO Reno 10倍变焦版
OPPO OPPO Reno Ace
OPPO OPPO Reno Z
OPPO OPPO Reno2
OPPO OPPO Reno3 Pro 5G
OPPO OPPO Reno3 元气版
OPPO OPPO Reno3
OPPO OPPO Reno4
OPPO OPPO Reno5 K
OPPO OPPO Reno5 Pro+
OPPO OPPO Reno5
OPPO OPPO Reno6 Pro+
OPPO OPPO Reno6 Pro
OPPO OPPO Reno6
OPPO OPPO Reno7 Pro
OPPO OPPO Reno7 SE
OPPO OPPO Reno7
OPPO OPPO Reno8 Pro+
OPPO OPPO Reno8 Pro
OPPO OPPO Reno8
OPPO OPPO Reno9 Pro+
OPPO OPPO Reno
OPPO Oppo A11
OPPO Oppo K7x
OPPO Oppo Reno2 Z
OPPO Oppo Reno5 Pro
ROG ROG游戏手机2精英版
Realme Realme C11 (2021)
Realme Realme GT Neo 5
Realme Realme X7 Pro
Samsung Samsung Galaxy Note10+(855)
Samsung Samsung Galaxy S10+
Samsung Samsung Galaxy S10
VIVO VIVO IQOO
VIVO VIVO S7
VIVO Vivo X90
VIVO Vivo Z5x
VIVO Vivo iQOO 11 Pro
VIVO Vivo iQOO Neo7 Racing
VIVO vivo IQOO NEO6 SE
VIVO vivo NEX 3
VIVO vivo NEX
VIVO vivo Pad
VIVO vivo S10
VIVO vivo S12 Pro
VIVO vivo S12
VIVO vivo S15 Pro
VIVO vivo S15e
VIVO vivo S15
VIVO vivo S16e
VIVO vivo S16
VIVO vivo S5
VIVO vivo S6
VIVO vivo S9e
VIVO vivo S9
VIVO vivo T1
VIVO vivo X27
VIVO vivo X30 Pro
VIVO vivo X30
VIVO vivo X50 Pro
VIVO vivo X50
VIVO vivo X60 Pro
VIVO vivo X60
VIVO vivo X70 Pro
VIVO vivo X70
VIVO vivo X80
VIVO vivo Y52s
VIVO vivo Y53s
VIVO vivo Y70s
VIVO vivo Y73s
VIVO vivo Z5
VIVO vivo iQOO 3 5G
VIVO vivo iQOO 5 5G
VIVO vivo iQOO Neo
VIVO vivo iQOO Pro
iQOO iQOO 10 Pro
iQOO iQOO 10
iQOO iQOO 11
iQOO iQOO 7
iQOO iQOO 8 Pro
iQOO iQOO 8
iQOO iQOO 9 Pro
iQOO iQOO 9
iQOO iQOO Neo 855
iQOO iQOO Neo3
iQOO iQOO Neo5 SE
iQOO iQOO Neo5S
iQOO iQOO Neo5
iQOO iQOO Neo6
iQOO iQOO Neo7 SE
iQOO iQOO Z1x
iQOO iQOO Z1
iQOO iQOO Z3
iQOO iQOO Z5x
iQOO iQOO Z5
iQOO iQOO Z6
realme realme GT Neo2T
realme realme GT Neo2
realme realme GT Neo3
realme realme GT Neo
realme realme GT
realme realme Q3 Pro
realme realme Q3
realme realme V15
realme realme X2 Pro
一加 OnePlus 11
一加 OnePlus Ace 2V
一加 OnePlus Ace 2
一加 一加10 Pro
一加 一加7 Pro
一加 一加7T Pro
一加 一加7T
一加 一加7
一加 一加8 Pro
一加 一加8T
一加 一加8
一加 一加9 Pro
一加 一加9RT
一加 一加9R
一加 一加9
一加 一加Ace Pro
一加 一加Ace 竞速版
一加 一加Ace
努比亚 努比亚红魔3
努比亚 努比亚红魔6
华为 HUAWEI Mate50 Pro
华为 HUAWEI Mate50
华为 HUAWEI MatePad Pro 11英寸
华为 HUAWEI nova 10 SE
华为 Huawei Mate 30 5G
华为 Huawei Mate 30 RS Porsche Design
华为 Huawei Mate 30E Pro 5G
华为 Huawei Mate 40 Pro+
华为 Huawei nova 6 5G
华为 华为Mate 20 Pro
华为 华为Mate 9
华为 华为Mate20X
华为 华为Mate20
华为 华为Mate30 Pro
华为 华为Mate30
华为 华为Mate40 Pro
华为 华为Mate40E
华为 华为Mate40
华为 华为MatePad 10.8
华为 华为MatePad 11
华为 华为MatePad Pro
华为 华为MatePad Pro
华为 华为P20
华为 华为P30 Pro
华为 华为P30
华为 华为P40 Pro+
华为 华为P40 Pro
华为 华为P40
华为 华为P50 Pro
华为 华为P50 Pro
华为 华为nova 10 Pro
华为 华为nova 10
华为 华为nova 4e
华为 华为nova 4
华为 华为nova 5 Pro
华为 华为nova 5i Pro
华为 华为nova 6
华为 华为nova 7 5G
华为 华为nova 7 Pro 5G
华为 华为nova 7 Pro
华为 华为nova 7
华为 华为nova 8 Pro
华为 华为nova 8
华为 华为nova 9 Pro
华为 华为nova 9
华为 华为平板 M6 8.4英寸
华为 华为畅享10 Plus
坚果 坚果Pro 3
小米 Redmi K20 Pro
小米 Redmi K30 5G
小米 Redmi K30S
小米 Redmi K40 Pro
小米 Redmi K40S
小米 Redmi K40
小米 Redmi K50 Pro
小米 Redmi K50 Ultra
小米 Redmi K60 Pro
小米 Redmi K60
小米 Redmi Note 10 Pro
小米 Redmi Note 11T Pro+
小米 Redmi Note 11
小米 Redmi Note 12 Pro
小米 Redmi Note 9 5G
小米 Xiaomi 13 Pro
小米 Xiaomi 13
小米 Xiaomi Mi 10 Ultra
小米 Xiaomi Redmi K30 Pro
小米 Xiaomi Redmi K30
小米 Xiaomi Redmi Note 9 Pro 5G
小米 小米 9
小米 小米10 Pro
小米 小米10S
小米 小米10
小米 小米10青春版
小米 小米11 Pro
小米 小米11 Ultra
小米 小米11
小米 小米11青春版
小米 小米12 Pro
小米 小米12S Pro
小米 小米12S Ultra
小米 小米12X
小米 小米12
小米 小米8
小米 小米Civi 1S
小米 小米MIX4
小米 小米平板5 pro
小米 小米平板5
小米 红米K30 Ultra
小米 红米Note 11T Pro
联想 联想小新 Pad
联想 联想拯救者 Y700
联想 联想拯救者电竞手机Pro
荣耀 Honor80 Pro
荣耀 Honor80
荣耀 荣耀20S
荣耀 荣耀20
荣耀 荣耀30 Pro+
荣耀 荣耀30S
荣耀 荣耀30
荣耀 荣耀50 Pro
荣耀 荣耀50
荣耀 荣耀60 Pro
荣耀 荣耀60
荣耀 荣耀70 Pro
荣耀 荣耀70
荣耀 荣耀9X
荣耀 荣耀Magic3
荣耀 荣耀Magic4 Pro
荣耀 荣耀Magic4
荣耀 荣耀V20
荣耀 荣耀V30 PRO
荣耀 荣耀V30
荣耀 荣耀V40
荣耀 荣耀X10
荣耀 荣耀X30
荣耀 荣耀X40 GT
荣耀 荣耀X40
荣耀 荣耀平板V6
荣耀 荣耀平板V7 Pro
黑鲨 黑鲨5 RS
黑鲨 黑鲨游戏手机2 Pro
黑鲨 黑鲨游戏手机2
黑鲨 黑鲨游戏手机3
黑鲨 黑鲨游戏手机4

平面AR 扩展能力

方法定义

6DoF-平面AR能力 的能力的多种扩展能力,不同能力可以组合使用。

  1. marker 识别能力,即平面空间下多个不同识别目标的识别。
  2. (暂不建议使用,待优化) 虚实遮挡的能力,即虚拟物体和真实世界的交互遮挡能力。

需要 在 V2 平面基础上开启使用。

marker 识别能力

通过配置微信小程序 VKSession 中的 marker 字段启用。然后与普通 marker 的使用流程一致,通过 session.addMarker,添加不同的识别目标进行识别。示例代码:

const session = wx.createVKSession({
  track: {
    plane: {
        mode: 1
    },
    marker: true,
  },
  version: 'v2'
})

// ... 初始化session相关流程

// 动态添加marker目标,使用流程与普通 marker一致
session.addMarker(filePath)

该模式下,marker 识别后,会放置于平面识别的世界空间。允许同时进行多个不同识别目标的识别。 目前版本,该模式适用于静态物体,识别物体更新频率相对较慢。每 3s,未明显移动会更新一下位置,每 7s 会进行重新检测。

虚实遮挡能力 (暂不建议使用,待优化)

虚实遮挡 初始化开启,不支持多扩展混用

通过配置微信小程序 VKSession 中的 depth 字段启用, 示例代码:

const session = wx.createVKSession({
  track: {
    depth: { mode: 1 }
  },
  version: 'v2'
})

虚实遮挡 动态开启与关闭,支持多扩展混用

let depthOpenFlag = true; 
session.setDepthSwitch(depthOpenFlag) // 更改深度开启状态

// 深度开启状态后,可以通过 VKFrame 获取度缓冲
const frame = session.getVKFrame(this.canvas.width, this.canvas.height)
const depthBufferRes = frame.getDepthBuffer();
// 具体深度使用可以参考<a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>示例

应用场景示例

平面模式下,多 marker 识别

plane+marker

程序示例

以上示例,可以在 水平面+水平面 + 附加能力 示例 页面查看示例代码。

2D/3D物体AR能力 (2D/3D Marker AR)

方法定义

2D/3D Marker AR能力,能够识别预先设定的目标物体(定义为Marker,包括2D平面物体和3D物体),进行视觉跟踪与定位,通过在目标物体周围渲染虚拟物体,从而实现AR功能。

方法区别

  1. 2D Marker,仅适用于平面类物体,用户上传一张平面物体的俯视图像作为目标物体,算法运行时识别该平面物品,并渲染出相关虚拟物体。2D Marker可以理解为特殊的3D Marker。
  2. 3D Marker,相比于2D Marker,能够识别3D物体,不局限于平面物体,具有更广的使用范围。算法运行前,需要手动制作3D Marker的识别目标文件(.map文件),然后算法运行时载入该文件用于识别。

使用提示

  1. 使用 addMarker 接口之前,需要在 createVKSession 的时候声明开启 marker 跟踪。即 wx.createVKSession({ track: { marker: true } })
  2. 可以添加 多个 Marker 图片,但不能重复添加相同的 marker 图片。
  3. 在 v2 模式下,使用 平面识别 与 Marker检测 结合,允许 同时识别多个 Marker,同时可输出多个2d/3d marker 识别结果(需要基础库版本3.0.0),目标在视野中消失后之前返回的pose位姿信息依然有效可用,具体可以参考 平面AR能力扩展 以及内部的相关例子。

识别物体规范

为提高Marker质量,保证算法识别效果,具体请仔细阅读Marker规范文档 Marker规范

3D Marker 识别目标文件 map 生成

目前仅允许通过 微信小程序示例 的 接口VisionKit视觉能力3DMarkerAR 页面生成。

生成任务状态解析

  1. 准备中 – 已上传,但生成服务器还未返回结果。建议等待20分钟,再考虑上传下一个视频(未更新结果,可以手动删除本任务,不过最好等待一天左右)
  2. 生成失败 – 会在错误提示上标明错误原因
  3. 已完成 – 生成完成,默认产物仅保留 30 天,请自行下载。

服务耗时:

  1. 当前版本 30秒视频耗时约 20分钟,请静待算法返回模型。
  2. 本服务同一时间仅处理一项任务,多个任务同时进行可能会导致后续任务的失败,建议闲时错峰进行生成。

对传入的视频有如下要求:

  1. 视频长宽比为 16:9 或 4:3,短边大于 480像素
  2. 目标物体易于和背景物体区分出来,同时目标物体放置与背景物体一定距离,放置底面与物体易于区分,底面可以放置一张白纸。
  3. 目标物体最好为刚体,本身不会发生较大形变,容易变形的物体不适合用作识别对象
  4. 视频匀速移动,避免模糊,对目标识别面环绕物体拍摄,需要保证相机有足够的平移移动
  5. marker物体要求 与 2d图像要求类似,具有丰富细节,避免重复单一纹理,不反光,无高光
  6. 拍摄视频中特征纹理丰富,如果 marker 本身纹理较弱,可以在背景中适当添加纹理物体
  7. 不建议使用透明物体,生成效果较差。

对传入的视频建议:

  1. 视频格式:视频帧率30fps,分辨率建议1080p
  2. 视频时长:视频建议时长在20秒~30秒,超过30秒会被截断,时长过短会导致 marker 效果欠佳

3D Marker 来源视频参考

demo-marker3d

程序示例

以下接口可在 微信小程序接口能力展示demo 中的 接口 – VisionKit视觉能力 中体验

2D Marker 能力

  1. 基础 2D Marker 能力示例 基础 2D Marker 识别示例。
  2. 水平面 + 2D Marker 能力示例 水平面AR 结合 2D Marker 识别示例。
  3. 水平面 + 附加能力 示例 水平面AR 结合 多种附加能力示例。

3D Marker 能力

  1. 3D Marker能力使用示例 3D Marker 的生成、调用与测试示例。

3D Marker案例 默认识别 大致效果

demo-marker3d

3D Marker案例 默认识别 来源视频参考

demo-marker3d

3D Marker案例 默认识别 图片

demo-marker3d

应用场景示例

2D示例:

  1. 工卡AR
  2. 门票AR
  3. 艺术画AR

marker2d

3D物体示例:

  1. 饮料、化妆瓶等容器类AR
  2. 公仔AR

饮料、化妆瓶等容器类AR

公仔AR

单样本检测

方法定义

单样本检测(One-shot Detection, OSD)只使用一张待检测类别的图片,就能检测到输入图像中该类别的物体。 待检测类别图片被称为OSD marker。 与一般物体检测不同,OSD 理论上可以检测任意用户指定的类别。该方法允许 OSD marker 与输入图像中的物体有视角差异或一定程度的外形差异。

应用场景示例

  1. 标志性建筑检测。
  2. Logo检测。
  3. 商品检测。
  4. 宠物检测。
  5. 动漫形象检测。

osd移动端演示

能力介绍

  • 使用方法
  1. 添加 OSD marker :调用 VKSession.addOSDMarker() 设置需要检测的物体,例如一张商品图片,返回markerId。说明:
    (1) 可多次调用该函数来添加多张图片,用于检测多种类物体。
    (2) 添加 OSD marker 后就可以持续输入图像来检测物体,不需要每次输入图像前都添加 OSD marker
  2. 删除 OSD marker :调用 VKSession.removeOSDMarker() 根据 markerId 删除对应 OSD marker ,从而不再检测该物体。
  3. 获取当前所有 OSD marker 信息:调用 VKSession.getAllOSDMarker() 得到当前所有 OSD marker 的列表,列表每一项包含了 markerId 和该图片的路径。
  • 程序示例
    可以在 单样本检测(OSD)能力使用参考 页面查看示例代码。

人脸关键点检测

VisionKit 从基础库 2.25.0 版本 (安卓微信>=8.0.25,iOS微信>=8.0.24) 开始提供人脸关键点检测,作为与 marker 能力OSD 能力 平行的能力接口。

从 微信 >= 8.1.0 版本开始提供人脸3D关键点检测,作为人脸2D关键点检测的扩展能力接口。

方法定义

人脸关键点检测有2种使用方法,一种是输入一张静态图片进行检测,另一种是通过摄像头实时检测。

1. 静态图片检测

通过 VKSession.detectFace 接口 输入一张图像,算法检测到图像中的人脸,然后通过 VKSession.on 接口 输出人脸位置坐标、106个关键点坐标以及人脸在三维坐标系中的旋转角度。

示例代码:

const session = wx.createVKSession({
  track: {
    face: { mode: 2 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})
// 静态图片检测模式下,每调一次 detectFace 接口就会触发一次 updateAnchors 事件
session.on('updateAnchors', anchors => {
  anchors.forEach(anchor => {
    console.log('anchor.points', anchor.points)
    console.log('anchor.origin', anchor.origin)
    console.log('anchor.size', anchor.size)
    console.log('anchor.angle', anchor.angle)
  })
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
    session.detectFace({
      frameBuffer, // 图片 ArrayBuffer 数据。人脸图像像素点数据,每四项表示一个像素点的 RGBA
      width, // 图像宽度
      height, // 图像高度
      scoreThreshold: 0.5, // 评分阈值
      sourceType: 1,
      modelMode: 1,
    })
  }
})

2. 通过摄像头实时检测

算法实时检测相机中的人脸,通过 VKSession.on 接口 实时输出人脸位置坐标、106个关键点坐标以及人脸在三维坐标系中的旋转角度。

示例代码:

const session = wx.createVKSession({
  track: {
    face: { mode: 1 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 摄像头实时检测模式下,监测到人脸时,updateAnchors 事件会连续触发 (每帧触发一次)
session.on('updateAnchors', anchors => {
  anchors.forEach(anchor => {
    console.log('anchor.points', anchor.points)
    console.log('anchor.origin', anchor.origin)
    console.log('anchor.size', anchor.size)
    console.log('anchor.angle', anchor.angle)
  })
})

// 当人脸从相机中离开时,会触发 removeAnchors 事件
session.on('removeAnchors', () => {
  console.log('removeAnchors')
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
  }
})

3. 开启3D关键点检测

想要开启人脸3D关键点检测能力,静态图片模式仅需要在2D调用基础上增加open3d字段,如下

// 静态图片模式调用
session.detectFace({
      ...,           // 同2D调用参数
      open3d: true,  // 开启人脸3D关键点检测能力,默认为false
    })

摄像头实时模式则在2D调用基础上增加3D开关更新函数,如下

// 摄像头实时模式调用
session.on('updateAnchors', anchors => {
  this.session.update3DMode({open3d: true})  // 开启人脸3D关键点检测能力,默认为false
  ...,  // 同2D调用参数
})

输出说明

1. 点位定义

人脸2D关键点与人脸3D关键点均为106点,定义方式如下图所示。在脸部姿态发生变化时,人脸2D关键点的轮廓点会始终沿着可见人脸边缘,而人脸3D关键点则维持立体结构。

2. 人脸2D关键点

人脸2D关键点输出字段包括

struct anchor
{
  points,    // 106点在图像中的(x,y)坐标
  origin,    // 人脸框的左上角(x,y)坐标
  size,      // 人脸框的宽和高(w,h)
  angle,     // 人脸角度信息(pitch, yaw, roll)
  confidence // 人脸关键点的置信度
}

3. 人脸3D关键点

开启人脸3D关键点检测能力后,可以获取人脸2D及3D关键点信息,其中人脸3D关键点输出字段包括

struct anchor
{ 
  ...,               // 人脸关键点2D输出信息
  points3d,          // 人脸106点的(x,y,z)3D坐标
  camExtArray,       // 相机外参矩阵,定义为[R, T \\ 0^3 , 1], 使用相机内外参矩阵可将3D点位投影回图像
  ccamIntArray       // 相机内参矩阵,参考glm::perspective(fov, width / height, near, far);
}

应用场景示例

  1. 人脸检测。
  2. 人脸特效。
  3. 人脸姿态估计。
  4. 人脸 AR 游戏。

程序示例

  1. 实时摄像头人脸检测能力使用参考
  2. 静态图像人脸检测能力使用参考

特别说明

微信小程序人脸识别功能涉及采集、存储用户生物特征(如人脸照片或视频、身份证和手持身份证、身份证照和免冠照等),此类型服务需使用微信原生人脸识别接口

Body检测

VisionKit从基础库 2.28.0版本开始提供body检测能力。从 微信>=8.1.0 版本开始提供人体3D关键点检测,作为Body检测的扩展能力接口。

方法定义

body检测有2种使用方法,一种是输入一张静态图片进行检测,另一种是通过摄像头实时检测。

1. 静态图片检测

通过 VKSession.detectBody 接口 输入一张图像,算法检测到图像中的人体,然后通过 VKSession.on 接口 输出获取的人体关键点信息。

示例代码:

const session = wx.createVKSession({
  track: {
    body: { mode: 2 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 静态图片检测模式下,每调一次 detectBody 接口就会触发一次 updateAnchors 事件
session.on('updateAnchors', anchors => {
    this.setData({
        anchor2DList: anchors.map(anchor => {
            points: anchor.points, // 关键点坐标
            origin: anchor.origin, // 识别框起始点坐标
            size: anchor.size // 识别框的大小
        }),
    })
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
    session.detectBody({
      frameBuffer, // 图片 ArrayBuffer 数据。待检测图像的像素点数据,每四项表示一个像素点的 RGBA
      width, // 图像宽度
      height, // 图像高度
      scoreThreshold: 0.5, // 评分阈值
      sourceType: 1 //图片来源, 默认为1, 0表示输入图片来自于视频的连续帧
    })
  }
})

2. 通过摄像头实时检测

算法实时检测相机中的人体姿态,通过 VKSession.on 接口 实时输出检测到的人体关键点。

示例代码:

const session = wx.createVKSession({
  track: {
    body: { mode: 1 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 摄像头实时检测模式下,监测到人体时,updateAnchors 事件会连续触发 (每帧触发一次)
session.on('updateAnchors', anchors => {
    this.data.anchor2DList = []
    this.data.anchor2DList = this.data.anchor2DList.concat(anchors.map(anchor => {
        points: anchor.points,
        origin: anchor.origin,
        size: anchor.size
    }))
})

// 当人体从相机中离开时,会触发 removeAnchors 事件
session.on('removeAnchors', () => {
  console.log('removeAnchors')
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
  }
})

3. 开启3D关键点检测

想要开启人体3D关键点检测能力,静态图片模式仅需要在2D调用基础上增加open3d字段,如下

// 静态图片模式调用
session.detectBody({
      ...,           // 同2D调用参数
      open3d: true,  // 开启人体3D关键点检测能力,默认为false
    })

摄像头实时模式则在2D调用基础上增加3D开关更新函数,如下

// 摄像头实时模式调用
session.on('updateAnchors', anchors => {
  this.session.update3DMode({open3d: true})  // 开启人体3D关键点检测能力,默认为false
  ...,  // 同2D调用参数
})

输出说明

点位说明

人体2D关键点为23点定义,如下图所示。

人体3D关键点为SMPL-24点关节定义,如下图所示。

人体检测

人体检测输出字段包括

struct anchor
{
  points,    // 人体2D关键点在图像中的(x,y)坐标
  origin,    // 人体检测框的左上角(x,y)坐标
  size,      // 人体检测框的宽和高(w,h)
  score,     // 人体检测框的置信度
  confidence // 人体关键点的置信度
}

人体3D关键点

开启人体3D关键点检测能力后,可以获取人体2D及3D关键点信息,其中人体3D关键点输出字段包括

struct anchor
{ 
  ...,               // 人体检测2D输出信息
  points3d,          // 人体3D关键点的(x,y,z)3D坐标
  camExtArray,       // 相机外参矩阵,定义为[R, T \\ 0^3 , 1], 使用相机内外参矩阵可将3D点位投影回图像
  camIntArray        // 相机内参矩阵,参考glm::perspective(fov, width / height, near, far);
}

应用场景示例

  1. 人像抠图。
  2. 越界检测。
  3. 人群流量统计。

程序示例

  1. 实时摄像头body检测能力使用参考
  2. 静态图像body检测能力使用参考

Hand检测

VisionKit 从基础库 2.28.0 版本开始提供手部检测能力。从 微信>=8.1.0 版本开始提供人手3D关键点检测,作为手部检测的扩展能力接口。

方法定义

手部检测有2种使用方法,一种是输入一张静态图片进行检测,另一种是通过摄像头实时检测。

1. 静态图片检测

通过 VKSession.detectHand 接口 输入一张图像,算法检测到图像中的手势,然后通过 VKSession.on 接口 输出获取的手势关键点信息。

示例代码:

const session = wx.createVKSession({
  track: {
    hand: { mode: 2 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 静态图片检测模式下,每调一次 detectHand 接口就会触发一次 updateAnchors 事件
session.on('updateAnchors', anchors => {
    this.data.anchor2DList = []
    this.setData({
        anchor2DList: anchors.map(anchor => ({
            points: anchor.points, // 关键点坐标
            origin: anchor.origin, // 识别框起始点坐标
            size: anchor.size, // 识别框的大小
            gesture: anchor.gesture // 手势分类
        })),
    }) 
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
    session.detectHand({
      frameBuffer, // 图片 ArrayBuffer 数据。待检测图像的像素点数据,每四项表示一个像素点的 RGBA
      width, // 图像宽度
      height, // 图像高度
      scoreThreshold: 0.5, // 评分阈值
      algoMode: 2 //算法模式:0, 检测模式,输出框和点;1,手势模式,输出框和手势分类;2,同时具备0和1,输出框、点、手势分类
    })
  }
})

2. 通过摄像头实时检测

算法实时检测相机中的手势姿态,通过 VKSession.on 接口 实时输出检测到的手势关键点。

示例代码:

const session = wx.createVKSession({
  track: {
    hand: { mode: 1 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 摄像头实时检测模式下,监测到手势时,updateAnchors 事件会连续触发 (每帧触发一次)
session.on('updateAnchors', anchors => {
    this.data.anchor2DList = []
    this.data.anchor2DList = this.data.anchor2DList.concat(anchors.map(anchor => ({
        points: anchor.points,
        origin: anchor.origin,
        size: anchor.size
    })))
})

// 当手势从相机中离开时,会触发 removeAnchors 事件
session.on('removeAnchors', () => {
  console.log('removeAnchors')
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
  }
})

3. 开启3D关键点检测

想要开启人手3D关键点检测能力,静态图片模式仅需要在2D调用基础上增加open3d字段,如下

// 静态图片模式调用
session.detectHand({
      ...,           // 同2D调用参数
      open3d: true,  // 开启人手3D关键点检测能力,默认为false
    })

摄像头实时模式则在2D调用基础上增加3D开关更新函数,如下

// 摄像头实时模式调用
session.on('updateAnchors', anchors => {
  this.session.update3DMode({open3d: true})  // 开启人手3D关键点检测能力,默认为false
  ...,  // 同2D调用参数
})

输出说明

点位说明

人手2D关键点定义与OpenPose相同,采用21点点位定义,如下图所示。

人手3D关键点采用MANO-16点关节定义,如下图所示。

人手检测

人手检测输出字段包括

struct anchor
{
  points,     // 人手2D关键点在图像中的(x,y)坐标
  origin,     // 人手检测框的左上角(x,y)坐标
  size,       // 人手检测框的宽和高(w,h)
  score,      // 人手检测框的置信度
  confidence, // 人手关键点的置信度
  gesture     // 人手手势类别
}

人手3D关键点

开启人手3D关键点检测能力后,可以获取人手2D及3D关键点信息,其中人手3D关键点输出字段包括

struct anchor
{ 
  ...,               // 人手检测2D输出信息
  points3d,          // 人手3D关键点的(x,y,z)3D坐标
  camExtArray,       // 相机外参矩阵,定义为[R, T \\ 0^3 , 1], 使用相机内外参矩阵可将3D点位投影回图像
  camIntArray        // 相机内参矩阵,参考glm::perspective(fov, width / height, near, far);
}

应用场景示例

  1. 智能家居。
  2. AR互动。
  3. 智能车载。

程序示例

  1. 实时摄像头手部检测能力使用参考
  2. 静态图像手部检测能力使用参考

Shoe 鞋部检测

VisionKit 从基础库 3.2.1 版本 (安卓微信>=8.0.43,iOS微信>=8.0.43) 开始支持。

鞋子检测 能力作为与 其他 VisionKit 能力 平行的能力接口。

该能力一般用于用户进行 AR试鞋 或者 腿部遮挡 等功能的开发。

方法定义

鞋子检测,目前只支持通过 摄像头实时检测

可以通过配置,决定是否每帧获取 用于对鞋子的腿部区域进行蒙版过滤的 腿部遮挡纹理

摄像头实时检测

首先需要创建 VKSession 的配置,然后通过 VKSession.start 启动 VKSession 实例。

运行过程中,算法实时检测相机中的鞋子,通过 VKSession.on 实时输出鞋子矩阵信息(位置旋转缩放)、8个关键点坐标。

最后,实时获取 VKSession 的帧数据 VKFrame,获得 VKCamera。得到 VKCamera.viewMatrixVKCamera.getProjectionMatrix 并结合,鞋子矩阵关键点信息 进行具体渲染。

开启VKSession示例代码:

// VKSession 配置
const session = wx.createVKSession({
    track: {
        shoe: {
            mode: 1 // 1 使用摄像头
        }
    }
})

// 摄像头实时检测模式下,监测到鞋子时,updateAnchors 事件会连续触发 (每帧触发一次)
session.on('updateAnchors', anchors => {
    // 目前版本能识别 1-2 双鞋子,anchors 长度为 0-2
    anchors.forEach(anchor => {
        // shoedirec 鞋子左右,0 为左,1 为右。
        console.log('anchor.shoedirec', anchor.shoedirec)
        // transform 鞋子的矩阵信息(位置旋转缩放),4*4 行主序矩阵,
        console.log('anchor.transform', anchor.transform)
        // points3d 位置,8个关键点坐标
        console.log('anchor.points3d', anchor.points3d)
    })
})

// 摄像头实时检测模式下,当鞋子从相机中丢失时,会不断触发
session.on('removeAnchors', () => {
  console.log('removeAnchors')
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
  }
})

渲染试鞋示例代码:

// 可以理解每一帧都需要执行的行为

// 1. 通过 VKSession 实例的 `getVKFrame` 方法可以获取到帧对象
const frame = this.session.getVKFrame(canvas.width, canvas.height)

// 2. 获取 VKCamera
const VKCamera = frame.camera

// 3. 获取 VKCamera 的 视图矩阵与投影矩阵
const viewMat = VKCamera.viewMatrix;
const projMat = VKCamera.getProjectionMatrix(near, far);

// 4. 结合 updateAnchors 里面返回 anchor 的 transform、points3d 进行具体渲染。(具体可以参考官方示例)

开启腿部遮挡

摄像头实时模式下,想要开启腿部分割能力。

需要保证在 VKSession.start 接口 调用,开启 VKSession 后。 调用 VKSessionupdateMaskMode 更新是否开启腿部分割纹理的返回。然后通过 VKFramegetLegSegmentBuffer 获取具体的腿部遮挡纹理buffer,然后基于 buffer 创建纹理,进行对应的遮挡剔除。

示例代码:

// 1. 开启腿部遮挡纹理获取开关
// VKSession.start 后,可以通过 updateMaskMode 更新是否能够获取腿部遮挡纹理buffer
this.session.updateMaskMode({
    useMask: true // 开启或关闭腿部遮挡纹理buffer (客户端 8.0.43 默认开启获取,可以手动关闭)
});

// 2. 通过 VKSession 实例的 `getVKFrame` 方法可以获取到帧对象
const frame = this.session.getVKFrame(canvas.width, canvas.height)

// 3. 通过帧对象,获取具体腿部分割纹理Buffer(160*160、单通道)
const legSegmentBuffer = frame.getLegSegmentBuffer();

// 4. 基于腿部分割纹理过滤内容(具体可以参考官方案例)

PS.除了算法分割外,可以手动添加Mesh进行遮挡,可以实现类似的遮挡分割效果。具体可以参考官方案例。

输出说明

anchor 信息

struct anchor
{
  transform,  // 鞋子矩阵信息,位置旋转缩放
  points3d,   // 鞋子关键点数组
  shoedirec,  // 鞋子左右脚,0 为左脚,1 为右脚
}

1. 鞋子矩阵 transform

长度为 16 的数组,表示 行主序 的 4*4 矩阵。

2. 鞋子关键点 points3d

长度为 8 的数组,表示 鞋子识别的 8个关键点:

Array<Point>(8) point3d

每个数组元素结构为:

struct Point { x, y, z }

表示在 鞋子矩阵 作用后,每个点的 三维偏移位置

以下为关键点图示与点位解释:

  • 点位0 位于鞋的底部后部,脚后跟处,靠近地面的位置。
  • 点位1 位于鞋子最前面,在竖直方向上大致处于鞋子上表面和鞋子下底面的中间。
  • 点位2 位于鞋的后部,在点位0的上方,位于靠近脚踝的位置。
  • 点位3、4 位于鞋子侧面,其中3点位于左鞋的左侧,右鞋的右侧。4点位于左鞋子的右侧,右鞋的左侧。上图中的3点处于被遮挡的位置。3,4点均紧靠鞋子下底面。
  • 点位5,6 位于鞋子侧面,相对于3,4更靠后,在鞋子下底面偏上一点的位置。
  • 点位7 大致位于鞋舌的中央。

3.腿部分割纹理Buffer

160*160 的 单通道浮点数颜色 ArrayBuffer,数值 0.0 对应非腿部区域,大于 0.0 代表是腿部区域,越靠近 1.0 越接近是腿部区域。

如何基于返回信息摆放模型

首先,可以将添加一个节点同步鞋子矩阵信息。再往该节点添加模型,基于鞋子关键点,设置对应的偏移,然后可以缩放模型适配比例,使模型贴合鞋子关键点,达到目标效果。

程序示例

摄像头实时检测示例

微信小程序示例 的 接口VisionKit视觉能力实时鞋部检测 - 试鞋案例

开源地址:实时鞋部检测-试鞋案例

识别鞋子,并将鞋子模型放置到识别出的区域。默认并支持动态开启与关闭腿部遮挡,以及显示与隐藏对应的鞋子关键点。

案例效果:

OCR检测

VisionKit从基础库 2.27.0版本开始提供OCR能力。

方法定义

OCR检测有2种使用方法,一种是输入一张静态图片进行检测,另一种是通过摄像头实时检测。

1. 静态图片检测

通过 VKSession.runOCR 接口 输入一张图像,算法检测到图像中的文字,然后通过 VKSession.on 接口 输出获取的文字内容。

示例代码:

const session = wx.createVKSession({
  track: {
    OCR: { mode: 2 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 静态图片检测模式下,每调一次 runOCR 接口就会触发一次 updateAnchors 事件
session.on('updateAnchors', anchors => {
  console.log('anchors.text', "".concat(anchors.map(anchor=>anchor.text)))
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
    session.runOCR({
      frameBuffer, // 图片 ArrayBuffer 数据。待检测图像的像素点数据,每四项表示一个像素点的 RGBA
      width, // 图像宽度
      height, // 图像高度
    })
  }
})

2. 通过摄像头实时检测

算法实时检测相机中的文字内容,通过 VKSession.on 接口 实时输出文字。

示例代码:

const session = wx.createVKSession({
  track: {
    OCR: { mode: 1 } // mode: 1 - 使用摄像头;2 - 手动传入图像
  },
})

// 摄像头实时检测模式下,监测到文字时,updateAnchors 事件会连续触发 (每帧触发一次)
session.on('updateAnchors', anchors => {
  console.log('anchors.text',"".concat(anchors.map(anchor=>anchor.text)))
})

// 当文字区域从相机中离开时,会触发 removeAnchors 事件
session.on('removeAnchors', () => {
  console.log('removeAnchors')
})

// 需要调用一次 start 以启动
session.start(errno => {
  if (errno) {
    // 如果失败,将返回 errno
  } else {
    // 否则,返回null,表示成功
  }
})

应用场景示例

  1. 文本检测。
  2. 车牌识别。
  3. 证件文本识别。

程序示例

  1. 实时摄像头OCR检测能力使用参考
  2. 静态图像OCR检测能力使用参考