댓글 검색 목록

[Nodejs] Node.js, Express.js, MongoDB, Face-api.js로 얼굴 인식 API 빌드

페이지 정보

작성자 운영자 작성일 21-06-13 15:50 조회 1,874 댓글 0

소개 


안녕하세요,이 기사를 통해 Node.js, Express, MongoDB 및 face-api.js로 얼굴 인식 API를 만드는 방법을 보여 드리겠습니다. 이것들은 주요 패키지이지만 나중에 기사에서 보여줄 패키지도 더 필요합니다.

작동 방식을 보여 드리기 위해 코드를 작성하겠습니다.이 API의 주요 목적은 얼굴 데이터를 데이터베이스에 지속적으로 저장하여 서버가 다운 되더라도 데이터를 데이터베이스에 저장하는 것입니다.

따라서 모든 이미지를 사용하여 모델을 다시 훈련 할 필요가 없습니다. 시작하기 전에 다음 주제를 이해했는지 확인하십시오.

  • Node.js
  • Express.js
  • MongoDB + Mongoose
  • Async functions
API의 목적 

얼굴 인식은 요즘 중요한 기능이 되고 있습니다. 최근에 저는 대학에서 안면 인식 기반 인증 기능이 필요한 프로젝트를 진행해야 했습니다. Node.js 및 Express로 이 프로젝트를 작업 할 때 서버를 얼굴 인식 모델과 통합하고 데이터베이스에 데이터를 저장하기에 충분한 리소스를 찾을 수 없었습니다.
또한 일부 얼굴 인식 API는 더 이상 사용되지 않습니다. 다행히도 이 문제를 해결할 수있는 face-api.js라는 패키지를 찾았지만 서버와 데이터베이스로 구현할 문서가 충분하지 않았습니다. 그래서 나는 이 기사를 쓰기로 결정했다. 아래에 이 기사의 API에 대한 face-api.js 문서, 모델 및 전체 코드에 대한 링크를 추가하겠습니다. -
API 작동 방식 

이 기사를 짧게 유지하기 위해 일반 용도의 얼굴 인식을 사용하는 데 필요한 주요 기능 만 보여 드리겠습니다. 이러한 기능은 다음과 같습니다.

  • 얼굴과 라벨의 여러 이미지를 가져옵니다.
  • 이미지에서 특징을 추출하고 레이블과 함께 데이터베이스에 저장합니다.
  • 새 이미지가 업로드 될 때 얼굴을 확인하고 가장 유사한 이미지로 응답합니다.
다음은 작동 방식에 대한 간단한 아키텍처입니다.

asdsa (1).png 


코드를 살펴 보자 

먼저 API에 필요한 패키지 가져 오기부터 시작하겠습니다. 다음은 npm 패키지 관리자를 사용하여 설치해야 하는 종속성 목록입니다.

express
mongoose
express-fileupload
face-api.js 
canvas
패키지를 설치 한 후 주 서버 파일에서 가져올 수 있습니다. 제 경우에는 app.js 파일입니다. 그런 다음 익스프레스 서버를 시작하고 데이터베이스에 연결하고 원하는 포트를 수신합니다. 또한 express-fileupload를 미들웨어로 서버에 추가해야 합니다.

const express = require("express");
const faceapi = require("face-api.js");
const mongoose = require("mongoose");
const { Canvas, Image } = require("canvas");
const canvas = require("canvas");
const fileUpload = require("express-fileupload");
faceapi.env.monkeyPatch({ Canvas, Image });

const app = express();

app.use(
  fileUpload({useTempFiles: true})
);


// add your mongo key instead of the ***
mongoose.connect(
    `***`,
    {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
    }
  ).then(() => {
    app.listen(process.env.PORT || 5000);
    console.log("DB connected and server us running.");
  }).catch((err) => {
    console.log(err);
  });
모델 시작 

이제 서버가 작동하므로 모델을로드해야합니다. face-api.js 패키지의 사전 학습 된 모델을 사용하고 있으므로 저장된 모델을 다운로드하고 모델로 face-api를 시작해야 합니다. 이를 위해 모델을 다운로드 할 수 있는지 확인하고, 위의 링크를 첨부하여 다운로드 한 다음 서버의 루트 디렉토리에 있는 폴더에 저장할 수 있습니다. 저에게는 이 폴더의 이름을 모델로 지정하고, 이름은 귀하에게 달려 있으며, 다음 코드를 사용하여 모델을 로드합니다. 미들웨어로 파일 업로드를 추가 한 직후에 추가했습니다.
async function LoadModels() {
  // Load the models
  // __dirname gives the root directory of the server
  await faceapi.nets.faceRecognitionNet.loadFromDisk(__dirname + "/models");
  await faceapi.nets.faceLandmark68Net.loadFromDisk(__dirname + "/models");
  await faceapi.nets.ssdMobilenetv1.loadFromDisk(__dirname + "/models");
}
LoadModels();
MongoDB 스키마 정의 

MongoDB 데이터베이스에 데이터를 저장하려면 먼저 데이터베이스를 만들고 코드에 스키마를 정의해야 합니다. 여기서는 데이터베이스 생성 방법을 보여주지 않겠지 만 다음 코드는 얼굴 데이터에 대한 스키마를 정의한 방법입니다. 스키마의 데이터 유형을 확인하는 것이 중요합니다. 레이블은 문자열로 저장하고 설명은 Array (배열에는 실제로 객체가 포함됨)로 저장됩니다.
const faceSchema = new mongoose.Schema({
  label: {
    type: String,
    required: true,
    unique: true,
  },
  descriptions: {
    type: Array,
    required: true,
  },
});

const FaceModel = mongoose.model("Face", faceSchema);
/post-face 경로 및 데이터베이스에 데이터 저장 

이제 모델을 로드하고 스키마를 정의 했으므로 레이블이 지정된 얼굴 이미지를 수신하여 MongoDB 데이터베이스에 저장할 수 있습니다.

이를 수행하려면 먼저 이미지와 라벨 세트를 수신 한 다음 얼굴 설명을 추출하여 데이터베이스에 저장하는 함수를 정의해야 합니다. 다음 함수는 방금 말한 추출 작업을 수행합니다.
async function uploadLabeledImages(images, label) {
  try {

    const descriptions = [];
    // Loop through the images
    for (let i = 0; i < images.length; i++) {
      const img = await canvas.loadImage(images[i]);
      // Read each face and save the face descriptions in the descriptions array
      const detections = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor();
      descriptions.push(detections.descriptor);
    }

    // Create a new face document with the given label and save it in DB
    const createFace = new FaceModel({
      label: label,
      descriptions: descriptions,
    });
    await createFace.save();
    return true;
  } catch (error) {
    console.log(error);
    return (error);
  }
}

이 함수의 입력으로 이미지와 레이블을 가져옵니다. 그런 다음 함수를 try-catch로 래핑하여 프로세스에 오류가 있어도 앱이 충돌하지 않도록 합니다. 그 후 데이터베이스에 업로드하기 전에 모든 설명을 저장하는 배열을 정의하고 각 이미지를 통해 canvas.loadImage() 함수로 이미지를 읽습니다. 그런 다음 이미지 데이터를 face-api 메서드에 전달하고 얼굴 특징을 감지합니다. 그런 다음 설명은 기능에서 추출되어 설명 배열로 푸시됩니다. 따라서 모든 이미지 기능이 추출 된 후 스키마에 따라 데이터베이스에 데이터를 저장하고 작업이 완료되면 true를 반환합니다.


메인 메서드가 준비되었으므로 이제 경로를 시작할 수 있습니다. 다음은 경로에 대한 코드입니다.

app.post("/post-face",async (req,res)=>{
    const File1 = req.files.File1.tempFilePath
    const File2 = req.files.File2.tempFilePath
    const File3 = req.files.File3.tempFilePath
    const label = req.body.label
    let result = await uploadLabeledImages([File1, File2, File3], label);
    if(result){

        res.json({message:"Face data stored successfully"})
    }else{
        res.json({message:"Something went wrong, please try again."})

    }
})

경로는 매우 간단합니다. 파일과 레이블을 받은 다음 앞에서 정의한 함수에 전달합니다. 그런 다음 저장 여부에 따라 사용자에게 응답을 보냅니다.


req.files는 앞서 언급 한 express-fileupload 패키지를 사용하는 경우에만 작동합니다. 그렇지 않으면 다른 방법을 사용하여 이미지를 업로드 해야 합니다. 또한 이 예에서는 얼굴 이미지를 3 개만 업로드하는 것을 보여 주었지만 정확도를 높이려면 더 많이 사용할 수 있습니다. *


/face-check 경로 및 얼굴 인식 


async function getDescriptorsFromDB(image) {
  // Get all the face data from mongodb and loop through each of them to read the data
  let faces = await FaceModel.find();
  for (i = 0; i < faces.length; i++) {
    // Change the face data descriptors from Objects to Float32Array type
    for (j = 0; j < faces[i].descriptions.length; j++) {
      faces[i].descriptions[j] = new Float32Array(Object.values(faces[i].descriptions[j]));
    }
    // Turn the DB face docs to
    faces[i] = new faceapi.LabeledFaceDescriptors(faces[i].label, faces[i].descriptions);
  }

  // Load face matcher to find the matching face
  const faceMatcher = new faceapi.FaceMatcher(faces, 0.6);

  // Read the image using canvas or other method
  const img = await canvas.loadImage(image);
  let temp = faceapi.createCanvasFromMedia(img);
  // Process the image for the model
  const displaySize = { width: img.width, height: img.height };
  faceapi.matchDimensions(temp, displaySize);

  // Find matching faces
  const detections = await faceapi.detectAllFaces(img).withFaceLandmarks().withFaceDescriptors();
  const resizedDetections = faceapi.resizeResults(detections, displaySize);
  const results = resizedDetections.map((d) => faceMatcher.findBestMatch(d.descriptor));
  return results;
}

이 부분은 매우 중요하며 매우 조심해야 합니다. 먼저 데이터베이스에서 모든 얼굴 데이터를 가져옵니다. 그러나 우리가 얻는 데이터는 배열의 객체 일뿐입니다. 모델이 각 이미지에 대한 설명을 읽으려면 LabeledFaceDescriptors 객체 여야 합니다.


그러나 그렇게 하려면 설명을 Float32Array로 전달해야 합니다. 따라서 각 얼굴 데이터에 대해 객체 인 각 설명을 반복합니다. 이러한 Object 유형을 Array로 바꾼 다음 Float32Array로 바꿉니다. 그런 다음 facematcher를 시작하고 인식을 수행하기 위해 함수에 전달 된 이미지를 읽습니다. Face API 문서를 기반으로 처리 및 감지 기능을 실행하고 결과를 반환합니다.


이제 얼굴을 확인할 경로를 정의 할 수 있습니다. 다음은 경로입니다.

app.post("/check-face", async (req, res) => {

  const File1 = req.files.File1.tempFilePath;
  let result = await getDescriptorsFromDB(File1);
  res.json({ result });

});

경로에서 이미지를 전달하고 getDescriptorsFromDB 함수가 얼굴 인식을 수행하고 결과를 반환 할 때까지 기다립니다.


Testing 


API를 사용할 준비가 되었습니다. 이제 Postman으로 테스트 해 보겠습니다. Postman은 API를 무료로 테스트 할 수 있는 훌륭한 도구입니다. 여기에서 다운로드 할 수 있습니다.


먼저 이렇게 라벨이 붙은 같은 사람의 이미지 3 장을 업로드했습니다.


zulkarblog-test1-question.png 


뾰족한 세부 사항에 주의하십시오. 코드가 일치하지 않으면 서버에서 오류로 응답합니다. 일이 잘되면 이런 응답을 받아야 합니다.


zulkarblog-test1-result.png 


데이터베이스에는 업로드 된 각 얼굴에 대해 다음과 같은 문서가 있어야 합니다.


zulkarblog-mongodb-facedoc.png 


이제 테스트 이미지를 업로드하고 API가 얼굴을 인식 할 수 있는지 확인할 수 있습니다.


zulkarblog-test1-question.png 


내가 얻는 결과는 이것입니다.


zulkarblog-test1-result.png 


기사가 너무 길면 죄송 합니다만 이것이 제 첫 기사였습니다. 콘텐츠를 즐기 셨기를 바랍니다. 이것이 도움이 되었다면 내 github에 별표를 남기고 이 블로그를 다른 사람들과 공유하십시오. 이 코드에 문제가 있는 경우 트위터 @ Zulkarn30860075에서 저에게 연락 할 수 있습니다.


https://blog.mdzulkarnine.com/building-face-recognition-api-with-nodejs-expressjs-mongodb-face-apijs



댓글목록 0

등록된 댓글이 없습니다.

웹학교 로고

온라인 코딩학교

코리아뉴스 2001 - , All right reserved.