微信小程序 VisionKit 实战(二):静态图片人脸检测与人像区域提取

303 阅读3分钟

蓝光剑 动漫男孩 4K壁纸 3840x2160_彼岸图网.jpg

一、前言

微信小程序实现身份证识别与裁剪(基于 VisionKit)

在许多小程序场景中,身份证识别只是第一步,提取证件照人像才是最终目的。
例如:

  • 实名认证时提取身份证头像;
  • 智能证件照生成;
  • 自动对比身份证照片与自拍人脸。

本文将介绍如何使用小程序 VisionKit 提供的 face 模块,完成静态图片人脸检测,并计算出头像的可视区域,实现精准截取。


二、核心逻辑与完整封装

我们先来看核心文件:utils/faceDetector.js

📘 完整代码

    /**
     * 初始化 VKSession
     */
    function createVKSession(gl) {
      const session = wx.createVKSession({
        track: {
          face: { mode: 2 }, // 静态图片模式
        },
        version: "v1",
        gl,
      })
      return session
    }

    /**
     * 静态图片人脸检测
     * @param {Object} options
     * @param {string} options.imgUrl 本地图片路径
     * @param {number} options.width 图片宽
     * @param {number} options.height 图片高
     * @returns {Promise<Object>} 返回人脸框信息和关键点
     */
    function detectFace({ imgUrl, width, height, gl }) {
      return new Promise(async (resolve, reject) => {
        try {
          const session = createVKSession(gl)
          const canvas = wx.createOffscreenCanvas({ type: "2d", width, height })
          const ctx = canvas.getContext("2d")

          const img = canvas.createImage()
          await new Promise((r) => {
            img.onload = r
            img.src = imgUrl
          })

          // 绘制图像
          ctx.clearRect(0, 0, width, height)
          ctx.drawImage(img, 0, 0, width, height)
          const imgData = ctx.getImageData(0, 0, width, height)

          // 监听识别结果
          session.on("updateAnchors", (anchors) => {
            if (anchors && anchors.length) {
              const faceData = anchors.map((a) => ({
                origin: a.origin,   // 左上角坐标(归一化 0~1)
                size: a.size,       // 宽高(归一化)
                points: a.points,   // 关键点(五官坐标)
              }))
              resolve(faceData)
            } else {
              reject("未识别到人脸")
            }
          })

          // 启动检测
          session.start(() => {
            session.detectFace({
              frameBuffer: imgData.data.buffer,
              width,
              height,
              scoreThreshold: 0.5,
              sourceType: 1, // 静态图片
              modelMode: 1,
            })
          })
        } catch (err) {
          reject(err)
        }
      })
    }

    module.exports = { detectFace }

三、使用示例

身份证识别完成后,我们会获得身份证的矫正图(cropImg)及其宽高信息。
接下来调用 detectFace() 即可检测身份证上的人脸区域。

    const faceData = await detectFace({
      imgUrl: detectIdCardInfo.cropImg,
      width: detectIdCardInfo.affineImgWidth,
      height: detectIdCardInfo.affineImgHeight,
      gl: this.gl,
    })
    this.computeFaceRegion(faceData[0], detectIdCardInfo)

四、人像区域计算与裁剪

人脸检测返回的坐标是归一化坐标(0~1) ,所以我们需要根据身份证图像的实际像素宽高换算出真实坐标。

下面是完整实现:

    // 计算身份证人像完整区域的显示坐标
    async computeFaceRegion(faceData, detectIdCardInfo) {
      const fixWidth = 375
      const expandRatioX = 1.4 // 宽度扩展比例
      const expandRatioY = 1.7 // 高度扩展比例

      const { cropImg, affineImgWidth, affineImgHeight } = detectIdCardInfo
      const fixHeight = (fixWidth / affineImgWidth) * affineImgHeight

      const originX = faceData.origin.x
      const originY = faceData.origin.y
      const width = faceData.size.width
      const height = faceData.size.height

      // 扩展人脸区域(避免裁切过紧)
      const newX = Math.max(originX - ((expandRatioX - 1) / 2) * width, 0)
      const newY = Math.max(originY - ((expandRatioY - 1) / 2) * height, 0)
      const newWidth = Math.min(width * expandRatioX, 1 - newX)
      const newHeight = Math.min(height * expandRatioY, 1 - newY)

      // 向上微调,让头像居中
      const moveUpRatio = 0.1
      const faceBoxTop = Math.max(newY * fixHeight - newHeight * fixHeight * moveUpRatio, 0)

      const faceRegion = {
        faceImgUrl: cropImg,
        faceImgWidth: fixWidth,
        faceImgHeight: fixHeight,
        faceBoxLeft: newX * fixWidth,
        faceBoxTop,
        faceBoxWidth: newWidth * fixWidth,
        faceBoxHeight: newHeight * fixHeight,
      }

      this.setData({ ...faceRegion })

      // 调用裁剪逻辑
      const cropResult = await cropFace(cropImg, faceRegion)
      this.setData({ cropUrl: cropResult.tempFilePath, cropBase64: cropResult.base64 })
    }

这样就能从身份证中自动提取头像区域,生成标准证件照。


五、逻辑总结

模块功能说明
createVKSession初始化 VisionKit 会话指定人脸检测模式(静态图片)
detectFace调用 VisionKit 识别人脸返回人脸框坐标与五官关键点
computeFaceRegion根据人脸框计算裁剪区域支持扩展、上移、缩放调整
cropFaceCanvas 裁剪函数最终生成证件照或头像图

六、原理概述:VisionKit 人脸识别模块

VisionKit 是微信小程序提供的视觉识别能力,支持包括:

功能模式
人脸检测 (face)实时 / 静态
身份证识别 (IDCard)照片模式
姿态估计 (body)实时人体识别

在本例中,我们使用了:

    track: { face: { mode: 2 } }

代表静态图片模式,适合身份证头像识别、图片对比等场景。