AI/AR:物体追踪(基于 three.js)
准备工作
由于本文示例中通过 OBJLoader 加载了 obj 模型,所以推荐使用 microapp-ar-three 中解决了兼容性问题的 three.js,在终端执行 npm i @douyin-microapp/microapp-ar-three 之后,可以在以下路径中找到:node_modules/@douyin-microapp/microapp-ar-three/libs/three.js。
Tracking AR 需要在抖音 1790(基础库 2.26.0)版本以上运行。
使用该算法需要提前准备一张识别图,由小程序团队根据识别图像提供对应的算法模型文件。识别图的格式应尽量符合以下标准:
- 1.识别图应为不小于 480 x 480 像素的图片。
- 2.识别图格式应为 jpg 格式。
- 3.识别图应小于 3M。
- 4.识别图命名格式。
- ◦模板图:产品名_商品名_实物名_md5, 例如: douyin_chanel_coco_2c4796683b5cdae6ae52cfbb776fb540.jpg。
- ◦测试图:产品名_商品名_实物名_test, 例如:douyin_chanel_coco_test.jpg。
如有多张图片在后面加数字,例如:douyin_chanel_coco_test1.jpg。
注意:图片名不要使用中文,可以用拼音代替。
- 5.识别图必须是 2D 平面图形,不支持 3D 物体等实体物作为触发物。
- 6.识别图中主要的影响因素是特征点,它多为色差较大的图案边缘的拐点(即图像灰度值发生剧烈变化后在边缘形成的曲率较大的点),可通过不同形态、不同形状的设计元素构造丰富的特征点。
- 7.图片纹理相似度高会对识图触发过程产生影响,建议通过组合、叠加、手绘等形式减少相似度高的纹理。
- 8.如识别图中需加入文字,建议对文字进行不同风格的艺术设计,尽量减少标准字体下的相似特征点,
- 9.识别图纹理需分布均匀,避免元素过于集中。
- 10.识别图的相邻色块间的灰度值差别应尽量大。
- 11.请使用高分辨率、高清晰度、高像素的识别图。不建议反光、过暗、有噪点、强曝光的图片作为识别图。
- 12.明确要识别的区域,如果不是整图识别,一定要提前说明。
如果有在线上小程序中使用该算法能力的需求,请联系商务运营相关人员。商务运营合作请使用飞书扫下方二维码:
本文按照以下识别图和对应的算法模型文件完成示例程序的编写:
获取算法数据
在 AI/AR:足部识别(基于 three.js 和 microapp-ar-three)中我们了解到了如何绘制 AI/AR 场景中的相机,以及如何初始化 AlgorithmManager。接下来我们基于“将相机画在 canvas 上” 这一节的示例,获取 tracking AR 的算法数据。
由于 tracking AR 算法需要将识别图对应的算法模型拷贝到手机中去,并将目标路径传给 algorithm manager,所以需要先获取 recognition_model.dat 到小程序中,修改一下 AlgorithmModule.js 中的代码:
export function initTrackingArAlgorithm(_width, _height, onResultFallback) { cb = onResultFallback; width = _width; height = _height; const algModelPath = "https://lf3-developer.bytemastatic.com/obj/developer/misc/AI_AR_demo/recognition_model.dat"; tt.downloadFile({ url: algModelPath, success(res) { const options = { trackingAr: { modelPath: res.tempFilePath, }, }; tt.getAlgorithmManager({ width: _width, height: _height, useSyncMode: true, requirements: ["trackingAr"], options: options, success: (algMgr) => { console.log("get algorithm Manager ~"); console.log(algMgr); algorithmManager = algMgr.algorithmManager; }, fail: (errMsg) => { console.log(errMsg); }, complete: () => { console.log("get alg mgr complete"); }, }); }, fail(res) { console.log(`downloadFile调用失败`); }, }); }
使用调试模式扫码运行小程序后,可以看到控制台中输出的算法结果如下:
其中 objectId 表示识别图对应的模型。我们可以输出一下 algorithmResult.objectId,相机画面对准两张图像的输出是不一样的,分别是 1 和 2,-1 表示没有检测到识别图:
使用 three.js 绘制模型
我们先使用 three.js 绘制一个 box 模型:
let cubeMesh = null; export function initModule(_canvas) { ... const cubeGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.1); const cubeMaterial = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide, }); cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial); cubeMesh.matrixAutoUpdate = false; mixedScene.add(cubeMesh); ... }
算法结果返回的 pose 和 projection 矩阵分别用于控制模型以及 three.js 相机,我们可以通过将两个矩阵赋值给 cubeMesh.matrix 以及 mixedCamera.projectionMatrix 来实现对模型的追踪:
function setMatrix(matrix, mat4) { matrix.set(...mat4); } export function onAlgorithmResult(algorithmResult) { console.log("algorithmResult.objectId = ", algorithmResult.objectId); setMatrix(cubeMesh.matrix, algorithmResult.pose); setMatrix(mixedCamera.projectionMatrix, algorithmResult.projection); }
运行小程序,将相机对准两张图像中的任意一张,可看到对正方体模型追踪的效果:
使用 three.js 加载模型
有时候需要加载模型作为物体追踪的对象,本文用 logo.obj 这个 OBJ 模型作为示例。和之前的模型文件一样,我们通过上面的链接让小程序获取该模型,在 RenderModule.js 中添加以下代码:
const modelUrl = "https://lf3-developer.bytemastatic.com/obj/developer/misc/AI_AR_demo/logo.obj"; let trackingModel0 = null; let trackingModel1 = null; let trackingModelGroup = null; let lastModelId = null; function loadOBJModel(modelUrl) { const loader = new THREE.OBJLoader(); loader.load( modelUrl, function (loadedMesh) { tt.showToast({ title: "模型加载成功", duration: 2000, success(res) {}, fail(res) { console.log(`showToast failed`); }, }); trackingModel1 = loadedMesh; trackingModel1.matrixAutoUpdate = false; trackingModel1.visible = false; trackingModelGroup.add(trackingModel1); const cubeMaterial = new THREE.MeshLambertMaterial({ color: "white" }); trackingModel1.children.forEach(function (child) { child.material = cubeMaterial; }); mixedScene.add(trackingModelGroup); }, undefined, function (e) { tt.showToast({ title: "模型加载失败", duration: 2000, success(res) { console.log(`${res}`); }, fail(res) { console.log(`showToast failed`); }, }); console.error("load model error :: ", e); } ); } export function initModule(_canvas) { ... trackingModelGroup = new THREE.Group(); const cubeGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.1); const cubeMaterial = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide, }); trackingModel0 = new THREE.Mesh(cubeGeometry, cubeMaterial); trackingModel0.matrixAutoUpdate = false; trackingModel0.visible = false; trackingModelGroup.add(trackingModel0); loadOBJModel(modelUrl); ... } export function onAlgorithmResult(algorithmResult) { console.log("algorithmResult.objectId = ", algorithmResult.objectId); if (algorithmResult.objectId == -1) return; if (trackingModelGroup.children.length < algorithmResult.objectId) { console.error( `objectId = ${algorithmResult.objectId} not exist in model group!` ); return; } if (lastModelId == null) { lastModelId = algorithmResult.objectId - 1; trackingModelGroup.children[lastModelId].visible = true; } else if (lastModelId != algorithmResult.objectId - 1) { trackingModelGroup.children[lastModelId].visible = false; lastModelId = algorithmResult.objectId - 1; trackingModelGroup.children[lastModelId].visible = true; } setMatrix( trackingModelGroup.children[lastModelId].matrix, algorithmResult.pose ); setMatrix(mixedCamera.projectionMatrix, algorithmResult.projection); }
运行小程序就可以看到物体追踪的效果: