require("@tensorflow/tfjs-node");
|
const logger = require('./logger'); // 引入上面创建的logger
|
const fs= require ('fs');
|
const express = require("express");
|
const moment = require("moment");
|
// 配置 cors 这个中间件
|
const cors = require('cors')
|
// const faceapi = require("face-api.js");
|
const faceapi = require('@vladmandic/face-api');
|
const mongoose = require("mongoose");
|
const canvas = require("canvas");
|
const { Canvas, Image } = require("canvas");
|
const fileUpload = require("express-fileupload");
|
const multer = require('multer')
|
console.log('----')
|
faceapi.env.monkeyPatch({ Canvas, Image });
|
|
const app = express();
|
|
function getFileExtension(filename) {
|
// 检查是否有点存在,如果没有直接返回空字符串
|
if (filename.indexOf('.') === -1) return '';
|
|
// 使用split方法根据最后一个点分割字符串,并取最后一部分作为扩展名
|
return filename.split('.').pop();
|
}
|
|
|
// 设置存放格式
|
const storage = multer.diskStorage({
|
destination(req, file, cb) {
|
cb(null, './uploads')
|
},
|
filename(req, file, cb) {
|
const originalname = getFileExtension(file.originalname)
|
cb(null, Date.now() + '.'+originalname)
|
}
|
})
|
// 加载文件存储
|
const upload = multer({ storage })
|
// app.use(
|
// fileUpload({
|
// useTempFiles: true,
|
// })
|
// );
|
app.use(cors())
|
app.use(express.static('./web'))
|
app.use('/uploads', express.static('./uploads'));
|
async function LoadModels() {
|
await faceapi.nets.faceRecognitionNet.loadFromDisk(__dirname + "/models");
|
await faceapi.nets.faceLandmark68Net.loadFromDisk(__dirname + "/models");
|
await faceapi.nets.ssdMobilenetv1.loadFromDisk(__dirname + "/models");
|
}
|
|
LoadModels();
|
|
const faceSchema = new mongoose.Schema({
|
label: {
|
type: String,
|
required: true,
|
unique: true,
|
},
|
images:{
|
type:Array,
|
required:true
|
},
|
descriptions: {
|
type: Array,
|
required: true,
|
},
|
});
|
|
const FaceModel = mongoose.model("Face", faceSchema);
|
|
|
/**
|
*
|
* @param {图片} images
|
* @param {图片标记:张三} label
|
* @returns
|
*/
|
async function uploadLabeledImages(images, label) {
|
try {
|
let counter = 0;
|
const descriptions = [];
|
const imgs=[]
|
// 循环图片
|
for (let i = 0; i < images.length; i++) {
|
const img = await canvas.loadImage(images[i]);
|
// 调整图像大小
|
const resizedImage = faceapi.resizeResults(img, { width: 600, height: 600 });
|
// 设置检测选项
|
const options = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.3 });
|
counter = (i / images.length) * 100;
|
console.log(`Progress = ${counter}%`);
|
// Read each face and save the face descriptions in the descriptions array
|
const detections = await faceapi.detectSingleFace(resizedImage,options).withFaceLandmarks().withFaceDescriptor();
|
if(detections?.descriptor){
|
descriptions.push(detections.descriptor);
|
imgs.push(images[i])
|
}
|
|
}
|
console.log(descriptions,images)
|
if(descriptions==[]){
|
console.log('人脸读取有错误')
|
return false
|
}
|
//用给定的标签创建一个新的面文档,并将其保存在数据库中
|
const createFace = new FaceModel({
|
label: label,
|
descriptions: descriptions,
|
images:imgs
|
});
|
try{
|
await createFace.save();
|
console.log('人脸录入成功!')
|
return true
|
}catch(error){
|
return error;
|
}
|
} catch (error) {
|
console.log(error);
|
return (error);
|
}
|
}
|
/**
|
* 清空 人脸库
|
* @param {人物标记} label
|
* @returns
|
*/
|
async function deleteLabelImages(label) {
|
try{
|
await FaceModel.deleteMany({ label: label })
|
return true
|
}catch(error){
|
return error
|
}
|
|
}
|
/**
|
* 清空 人脸库
|
* @param {人物标记} label
|
* @returns
|
*/
|
async function selectLabelImages(label) {
|
try{
|
await FaceModel.findOne({ label: label })
|
return true
|
}catch(error){
|
return error
|
}
|
|
}
|
async function getDescriptorsFromDB(image) {
|
// 从mongodb获取所有的面部数据,并循环遍历每个面部数据以读取数据
|
let faces = await FaceModel.find();
|
for (i = 0; i < faces.length; i++) {
|
// 将面数据描述符从obiects更改为Float32Array类型
|
for (j = 0; j < faces[i].descriptions.length; j++) {
|
faces[i].descriptions[j] = new Float32Array(Object.values(faces[i].descriptions[j]));
|
}
|
// 将DB面文档改为
|
faces[i] = new faceapi.LabeledFaceDescriptors(faces[i].label, faces[i].descriptions);
|
}
|
|
// 加载面匹配器以查找匹配面
|
const faceMatcher = new faceapi.FaceMatcher(faces, 0.5);
|
// 使用画布或其他方法读取图像
|
const img = await canvas.loadImage(image);
|
// // 调整图像大小
|
// const resizedImage = faceapi.resizeResults(img, { width: 600, height: 600 });
|
|
// // 设置检测选项
|
const options = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.3 });
|
let temp = faceapi.createCanvasFromMedia(img);
|
|
// 处理模型的图像
|
const displaySize = { width: 600, height: 600 };
|
faceapi.matchDimensions(temp, displaySize);
|
const currentTime = moment().format("YYYY-MM-DD HH:mm:ss");
|
// 查找匹配的人脸
|
const detections = await faceapi.detectAllFaces(img,options).withFaceLandmarks().withFaceDescriptors();
|
const currentTime2 = moment().format("YYYY-MM-DD HH:mm:ss");
|
const resizedDetections = faceapi.resizeResults(detections, displaySize);
|
const results = resizedDetections.map((d) => faceMatcher.findBestMatch(d.descriptor));
|
return results;
|
}
|
|
|
/**
|
* 上传人脸信息
|
*/
|
app.post("/post-face",upload.array('files',6),async (req,res)=>{
|
let imgs=[]
|
for (let i = 0; i < req.files.length; i++) {
|
imgs.push(req.files[i].path)
|
}
|
let label = req.body.label
|
logger.info(`上传${label}人脸信息`)
|
console.log('---------------',label)
|
let result = await uploadLabeledImages(imgs, label);
|
console.log('---------------',result)
|
if(result===true){
|
logger.info(`上传${label}人脸信息成功`)
|
res.json({code:200, message:"录入成功"})
|
}else{
|
// let result = await getDescriptorsFromDB(File1);
|
imgs.forEach(e=>{
|
fs.unlink(e,(err)=>{
|
if(err){
|
console.log('删除失败')
|
}else{
|
console.log('删除成功')
|
}
|
})
|
})
|
logger.info(`删除${label}人脸信息,录入失败`)
|
res.json({code:400, message:"人脸模型录入失败,请重新录入"})
|
|
}
|
})
|
/**验证人脸对比 */
|
app.post("/check-face",upload.single('file'), async (req, res) => {
|
const File1 = req.file.path;
|
logger.info(`验证人脸信息识别:${File1}`)
|
try {
|
let result = await getDescriptorsFromDB(File1);
|
fs.unlink(File1,(err)=>{
|
if(err){
|
console.log('删除失败')
|
}else{
|
console.log('删除成功')
|
}
|
})
|
res.json({code:200,result });
|
} catch (error) {
|
res.json({code:200,error });
|
}
|
|
|
});
|
// 获取人脸模型
|
app.post('/get-face',upload.single('file'),async (req, res) => {
|
|
try{
|
const label = req.body.label
|
logger.info(`获取人脸模型:${label}`)
|
const result=await FaceModel.findOne({ "label": label })
|
logger.info(`返回人脸模型:${label}:${JSON.stringify(result)}`)
|
res.json({code:200,result})
|
}catch(error){
|
res.json({code:200,error})
|
}
|
|
})
|
// 清除人脸模型
|
app.post("/del-face",upload.single('file'),async(req,res)=>{
|
const label = req.body.label
|
logger.info(`删除人脸库${label}`)
|
FaceModel.findOne({ "label": label }).then(re=>{
|
console.log('kankan---')
|
console.log(re.images)
|
if(re.images.length>0){
|
for (let i = 0; i < re.images.length; i++) {
|
fs.unlink(re.images[i],(err)=>{
|
if(err){
|
console.log('删除失败')
|
}else{
|
console.log('删除成功')
|
}
|
})
|
}
|
}
|
})
|
let result= deleteLabelImages(label)
|
if(result){
|
logger.info(`删除人脸库${label};成功`)
|
res.json({message:'success'})
|
|
}else{
|
logger.info(`删除人脸库${label};失败`)
|
res.json({message:"err"})
|
}
|
})
|
app.post("/test",async(req,res)=>{
|
console.log('---测试--------')
|
res.json({message:"err"})
|
|
})
|
|
|
|
//链接数据库初始化
|
mongoose
|
.connect(
|
`mongodb://localhost/test`,
|
{
|
useNewUrlParser: true,
|
useUnifiedTopology: true,
|
useCreateIndex: true,
|
}
|
)
|
.then(() => {
|
app.listen(process.env.PORT || 80,'0.0.0.0');
|
console.log("DB connected and server us running.");
|
console.log('http-sse-'+80)
|
logger.info('数据库连接成功,服务已启动,端口号80')
|
})
|
.catch((err) => {
|
console.log(err);
|
});
|