一、环境准备
node环境下
@vladmandic/face-api 默认依赖 @tensorflow/tfjs-node,需要提前npm install @tensorflow/tfjs-node 安装好
国内网络由于访问不到谷歌的资源库 可以找其他镜像库或者使用 @tensorflow/tfjs和@tensorflow/tfjs-backend-wasm 使用 wasm版本实现跨平台使用。
二、Express 配置集成
这里使用 @tensorflow/tfjs和@tensorflow/tfjs-backend-wasm 实现跨平台版本
注意node使用@tensorflow/tfjs 需要安装 node-canvas 包 和jsdom 模拟dom环境。
以下是app.ts关于face-api 相关配置
import { Canvas, Image } from 'canvas';
import { JSDOM } from 'jsdom';
import * as faceapi from '@vladmandic/face-api/dist/face-api.node-wasm.js';
import { loadModels } from './utils/faceUtil.js';
...
/ 初始化环境
const dom = new JSDOM();
global.document = dom.window.document;
global.HTMLCanvasElement = Canvas as unknown as { new (): HTMLCanvasElement; prototype: HTMLCanvasElement; };
faceapi.env.monkeyPatch({
Canvas: Canvas as unknown as { new (): HTMLCanvasElement; prototype: HTMLCanvasElement; },
Image: Image as unknown as { new (): HTMLImageElement; prototype: HTMLImageElement; }
});
...
//加载人脸模型
loadModels().then(() => {
// 创建HTTPS服务器
https.createServer(options, app).listen(process.env.API_PORT, () => {
...
});
})
faceUtil.js
import * as faceapi from '@vladmandic/face-api/dist/face-api.node-wasm.js';
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-wasm';
// 加载模型(需提前下载至./models目录)
export const loadModels = async (): Promise<void> => {
try {
await tf.setBackend('wasm');
console.log('Backend:', tf.getBackend());
await Promise.all([
faceapi.nets.ssdMobilenetv1.loadFromDisk('model'),
faceapi.nets.faceLandmark68Net.loadFromDisk('model'),
faceapi.nets.faceRecognitionNet.loadFromDisk('model')
]);
} catch (err) {
console.error('Model loading failed:', err);
throw err;
}
}
这里加载model 下的预处理模板。
具体模板可以去项目下的 ./node_modules/@vladmandic/face-api/model 找自己想用的
三、照片转成人脸数据
这里的照片可以通过multer 中间件 接收接口传递过来的文件。
public saveFaceFeatures = async (req: Request, res: Response): Promise<void> => {
try {
//获取参数
const { studentId } = req.query;
const path = req.file?.path;
//服务层处理逻辑
...
} catch(err) {
errHandler(err, req, res);
}
}
这里就可以看到 multer中件件处理过的接口 通过req可以读取文件的信息了
默认它会把文件放到你配置的文件路径下。
import * as faceapi from '@vladmandic/face-api/dist/face-api.node-wasm.js';
import { loadImage } from 'canvas';
saveFaceFeatures = async (filePath: string): Promise<...> => {
try {
if (!filePath) {
...
}
const img = await loadImage(filePath) as unknown as HTMLImageElement;
const detection = await faceapi
.detectSingleFace(img)
.withFaceLandmarks()
.withFaceDescriptor();
if (!detection){
...
}
// 提取128维特征向量(只处理单人的人脸数据)
const descriptor128 = detection?.descriptor.slice(0, 128);
//保存人脸特征数据
const desBuffer = Buffer.from(descriptor128.buffer);
...
} catch(err) {
console.log(err);
throw err;
}
}
业务层通过放置的文件路径 和canvas 模拟一个图片 传给 faceapi 检测和获取人脸的128向量特征。
之后就可以把向量特征数据存入数据库了,记得要用 BLOB 类型接收。
四、人脸校验和数据库余弦相似度算法
下面就是接收人脸信息 和数据库已经有的进行比对了
const img = await loadImage(Buffer.from(faceBuffer)) as unknown as HTMLImageElement;
const detection = await faceapi
.detectSingleFace(img)
.withFaceLandmarks()
.withFaceDescriptor();
const descriptor128 = detection?.descriptor.slice(0, 128) || new Float32Array();
如果前端发生的是人脸图片使用 faceapi 再次获取 特征数据比对。
const floatArr = new Float32Array(faceBuffer);
const saveBuffer = floatArr.slice(0, 128).buffer;
如果传的就是 128维ArrayBuffer,可以直接用来比对,当然这里也可以做下转换,保证数据正确性。
SELECT id
cosine_similarity_128d(?, feature_vector) AS similarity
FROM table
ORDER BY similarity DESC
LIMIT 1
使用上面的sql就能获取一条最相似的 数据。
cosine_similarity_128d 数据库方法如下
CREATE DEFINER=`root`@`localhost` FUNCTION `cosine_similarity_128d`(
blob1 BLOB,
blob2 BLOB
) RETURNS float
DETERMINISTIC
BEGIN
DECLARE dot_product FLOAT DEFAULT 0;
DECLARE norm1 FLOAT DEFAULT 0;
DECLARE norm2 FLOAT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE offset INT;
DECLARE val1, val2 FLOAT;
WHILE i < 128 DO
SET offset = i * 4;
SET val1 = CAST(CONV(HEX(SUBSTRING(blob1, offset+1, 4)), 16, 10) AS FLOAT);
SET val2 = CAST(CONV(HEX(SUBSTRING(blob2, offset+1, 4)), 16, 10) AS FLOAT);
SET dot_product = dot_product + val1 * val2;
SET norm1 = norm1 + POW(val1, 2);
SET norm2 = norm2 + POW(val2, 2);
SET i = i + 1;
END WHILE;
-- 完整余弦相似度计算
RETURN dot_product / (SQRT(norm1) * SQRT(norm2));
END
以上就是一个简单的node.js环境下的 纯后端 人脸识别服务例子。(人脸数据采集还是需要前端配合)