AR人脸识别 Three.js + tensorflow.js(一)

概述

如有不明白的可以加QQ:2354528292;wx: aichitudousien
更多教学视频请访问:https://space.bilibili.com/236087412
源码获取:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.3c3a3d0d66B2fR&ft=t&id=714757394880

这一期来使用Three.js和tensorflow.js来完成一个AR人脸识别的项目,主要使用的前端框架为Vue,项目中主要的功能点有可以实现人脸替换,在摄像头检测检测到人脸后会将人脸替换成一个预先准备好的人脸模型,然后我们人脸移动和变化的时候模型会跟随移动和变化,在模型上我们可以画线,画logo, 可以自由作图,然后还具备返回上一步和删除的功能
先来看一下视频效果:

AR人脸识别

tensorflow.js介绍

three.js库就不介绍了,相信大家也已经很熟悉了,主要来介绍一下tensorflow.js这个库。
Tensorflow.js是一个基于deeplearn.js构建的库,可直接在浏览器上创建深度学习模块。使用它可以在浏览器上创建CNN(卷积神经网络)、RNN(循环神经网络)等等,且可以使用终端的GPU处理能力训练这些模型。因此,可以不需要服务器GPU来训练神经网络。这一次我们就主要使用tensorflow.js来完成人脸识别。

项目搭建

前端框架是vue,版本使用的是2.6,three.js版本使用的124版本,tensorflow使用了@tensorflow-models/face-landmarks-detection,tensorflow/tfjs-backend-webgl, tensorflow/tfjs-converter,tensorflow/tfjs-core

初始化摄像头

前端界面很简单,就一点小图标就可以了,随便写一点样式就ok
在这里插入图片描述

  1. 在vue文件中调用init函数
let useCamera = true, video;
export async function init() {
	if (useCamera) {
     await setupCamera();
     video.play();
     video.width = video.videoWidth;
     video.height = video.videoHeight;
     await facemesh.init(video);
  	}
}
  1. 设置相机参数
async function setupCamera() {
  video = document.createElement('video');
  // navigator.mediaDevices.getUserMedia 提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。

  const stream = await navigator.mediaDevices.getUserMedia({
    // 关闭音频
    audio: false,
    video: {
      // 在移动设备上面,表示优先使用前置摄像头
      facingMode: 'user',
      // 判断是否是移动端,如果是移动端就自适应,PC端为640*640
      width: mobile ? undefined : 640,
      height: mobile ? undefined : 640
    }
  });
  
  video.srcObject = stream;
  return new Promise((resolve) => {
    // 在视频的元数据加载后执行 JavaScript
    video.onloadedmetadata = () => {
      resolve(video);
    };
  });
}
  1. 初始化tensorflow
    tf.setBackend 设置负责创建张量并对这些张量执行操作的后端(cpu、webgl、wasm)等, 这里主要使用webgl,最大人脸识别数为1
const state = {
  backend: 'webgl',
  maxFaces: 1
};
async function init(video) {
  // tf.setBackend 设置负责创建张量并对这些张量执行操作的后端(cpu、webgl、wasm等
  await tf.setBackend(state.backend);

  // facemesh.load 输入中检测到的最大人脸数
  model = await faceLandmarksDetection.load(
    faceLandmarksDetection.SupportedPackages.mediapipeFacemesh,
    {
      maxFaces: state.maxFaces,
      detectorModelUrl: 'model/blazeface/model.json',
      // 用于指定自定义 iris 模型 url 或tf.io.IOHandler对象的可选参数
      irisModelUrl: 'model/iris/model.json',
      // 用于指定自定义 facemesh 模型 url 或tf.io.IOHandler对象的可选参数
      modelUrl: 'model/facemesh/model.json'
    }
  );
}

这里有一个大坑。detectorModelUrl,irisModelUrl,modelUrl如果不设置路径会自动去https://tfhub.dev/上获取人脸检测模型,但是这个网站在国内是无法访问的,所以我们需要修改一下路径,我们可以去https://hub.tensorflow.google.cn/网站上下载我们所需要的人脸检测模型,将包文件下载下来后替换对应的路径就可以了
具体的说明文档可以在此链接上找到:https://www.npmjs.com/package/@tensorflow-models/face-landmarks-detection

此时我们应该可以看到摄像头的运行状态已经开启了

初始化three.js

这里就是一个常规的初始化three.js的步骤,我们创建一个和video视频一样大的three.js场景

threeEl = await three.init(video);
async function init(video) {
  width = video ? video.width : 640;
  const height = video ? video.height : 640;
  const ratio = width / height;
  const fov = 50;
  const near = 1;
  const far = 5000;
  camera = new THREE.PerspectiveCamera(fov, ratio, near, far);
  camera.position.z = height;
  camera.position.x = -width / 2;
  camera.position.y = -height / 2;

  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  scene = new THREE.Scene();
  if (video) {
    // 创建video贴图
    addVideoSprite(video);
  }

  // 初始化射线
  raycaster = new THREE.Raycaster();

  // 增加人脸模型
  await addObjMesh();

  return renderer.domElement;
}

关键步骤,将video作为贴图附加到小精灵上,这样我们就可以在three.js中呈现我们的视频了,到了这里项目实现的原理也已经可以明白了,通过video标签获取视频流,然后将视频流作为贴图附加到对应的mesh上,此时我们就可以做任何想做的事情了,添加模型,添加mesh,任何three.js可以完成的
增加mesh

function addVideoSprite(video) {
  videoTexture = new THREE.Texture(video);
  videoTexture.minFilter = THREE.LinearFilter;
  const videoSprite = new THREE.Sprite(
    new THREE.MeshBasicMaterial({
      map: videoTexture,
      depthWrite: false
    })
  );
  const width = video.width;
  const height = video.height;
  videoSprite.center.set(0.5, 0.5);
  videoSprite.scale.set(width, height, 1);
  videoSprite.position.copy(camera.position);
  videoSprite.position.z = 0;
  scene.add(videoSprite);
}

增加人脸模型

function addObjMesh() {
  const loader = new OBJLoader();
  return new Promise((resolve, reject) => {
    loader.load('model/facemesh.obj', (obj) => {
      obj.traverse((child) => {
        if (child instanceof THREE.Mesh) {
          const mat = new THREE.MeshNormalMaterial({
            side: THREE.DoubleSide
          });
          if (!params.debug) {
            mat.transparent = true;
            mat.opacity = 0;
          }
          baseMesh = new THREE.Mesh(child.geometry, mat);
          scene.add(baseMesh);
          resolve();
        }
      });
    });
  });
}

生成了场景,也生成了小精灵,这是我们只要将场景渲染即可,在使用window.requestAnimationFrame(update)渲染的时候一定要更新我们的视频贴图,并将视频贴图的needsUpdate设置为true,不然是不会更新的

function update(facemesh) {
  if (videoTexture) {
    videoTexture.needsUpdate = true;
  }

  renderer.render(scene, camera);
}

此时我们的界面中应该就可以看到实时视频了
在这里插入图片描述
ok,此我们来进行个实验,在视频的中间添加一个会旋转的box
代码很简单

var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
var material = new THREE.MeshBasicMaterial( {color: 0x00ff00, depthTest:false} );
cube = new THREE.Mesh( geometry, material );
cube.position.copy(camera.position)
cube.position.z = 0;
scene.add( cube );

这样我们就可以看到我们的视频中间会出现一个box,那此时我们就可以思考了,我们手机里面的ar应用,比如拍摄到固定的位置出现一些模型,动画,或者图片i介绍,那我们是不是都可以完成了呢

在这里插入图片描述
这篇文章就介绍到这里,下一篇我们接着介绍如何生成人脸模型~

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐