gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
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.6 });
      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();
      console.log('kankan ================')
      console.log('kankan ================')
      console.log('kankan ================')
      console.log(detections===undefined)
      if(detections&&detections.descriptor){
        descriptions.push(detections.descriptor);
        imgs.push(images[i])
      }else{
        return '录入失败';
      }
      
    }
    console.log(descriptions,images)
    if(descriptions==[]){
      console.log('人脸读取有错误')
      return false
    }
    //用给定的标签创建一个新的面文档,并将其保存在数据库中
    const createFace = new FaceModel({
      label: label,
      descriptions: descriptions,
      images:imgs
    });
    console.log(label)
    console.log(descriptions)
    console.log(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.48);
  // 使用画布或其他方法读取图像
  const img = await canvas.loadImage(image);
  // // 调整图像大小
  // const resizedImage = faceapi.resizeResults(img, { width: 600, height: 600 });
 
  // // 设置检测选项 味着只有当人脸检测的置信度大于或等于0.3时,该检测结果才会被接受。换句话说,如果模型认为检测到的对象是人脸的确定性低于30%,
  //则不会将这个检测结果包含在最终输出中。这有助于减少误报(即错误地将非人脸区域识别为人脸的情况)。
  const options = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.4 });
  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 || 8188,'0.0.0.0');
    console.log("DB connected and server us running.");
    console.log('http-sse-'+8188+process.env.PORT)
    logger.info('数据库连接成功,服务已启动,端口号8188')
  })
  .catch((err) => {
    console.log(err);
  });