Face API 无法比较两个图像,即使它们是相同的图像

问题描述 投票:0回答:1

我正在开展一个大学项目,其中涉及与电子门集成的面部识别系统。我为人员创建了注册,现在我需要将接收到的面孔与存储的面孔进行比较。为此,我创建了两个包含相同照片的文件夹和两种检测方法:一种将图像 URL 存储在我的 Amazon S3 存储桶中,另一种从 to_compare 文件夹中获取图像。然而,即使图像相同,系统也不会将它们识别为同一个人。一种可能性是图像具有不同大小的数组,但我已经将数组过滤为只有 100 个元素。

const mongoose = require('mongoose');

const personSchema = new mongoose.Schema({
  name: { type: String, required: true },
  cpf: { type: String, required: true, unique: true },
  birthDate: { type: Date, required: true },
  photoUrl: { type: String },
  biometrics: { type: Object }, // Dados biométricos processados da imagem
  employee: { type: Boolean, required: true, default: false },
  position: {
    type: {
      title: { type: String, required: true },
      department: { type: String, required: true },
      hireDate: { type: Date, required: true }
    },
    default: null,
    validate: {
      validator: function () {
        return this.employee || this.position === null;
      },
      message: 'Position should be null if employee is false'
    }
  },
  active: { type: Boolean, default: true } // Campo indicando se a pessoa está ativa
});

// Método para ativar a pessoa
personSchema.methods.activate = function () {
  this.active = true;
  return this.save();
};

// Método para desativar a pessoa
personSchema.methods.deactivate = function () {
  this.active = false;
  return this.save();
};

module.exports = mongoose.model('Person', personSchema);
// src/controller/personController.js

const Person = require('../model/personModel');
const { processBiometrics, compareBiometrics, compareWithRegisteredImages } = require('../util/biometricsUtils');

/**
 * Creates a new person in the system with processed biometric data.
 * @param {Object} req - Request object containing person data in the body.
 * @param {Object} res - Response object.
 */
exports.createPerson = async (req, res) => {
    try {
        console.log("Creating person with data:", req.body);
        const { name, cpf, birthDate, photoUrl, employee, position } = req.body;

        // Processes biometrics if an image is provided
        const biometrics = photoUrl ? await processBiometrics(photoUrl) : null;
        console.log("Biometrics processed:", biometrics);

        // Creates a new person
        const person = new Person({
            name,
            cpf,
            birthDate,
            photoUrl,
            biometrics,
            employee,
            position: employee ? position : null,
        });

        await person.save();
        console.log("Person created successfully:", person);
        res.status(201).json({ message: 'Person created successfully', person });
    } catch (error) {
        console.error("Error creating person:", error);
        res.status(500).json({ error: 'Error creating person' });
    }
};

/**
 * Retrieves a person by ID.
 * @param {Object} req - Request object containing the person's ID in the parameters.
 * @param {Object} res - Response object.
 */
exports.getPersonById = async (req, res) => {
    try {
        console.log("Fetching person by ID:", req.params.id);
        const person = await Person.findById(req.params.id);
        if (!person) {
            console.warn("Person not found with ID:", req.params.id);
            return res.status(404).json({ error: 'Person not found' });
        }

        console.log("Person fetched:", person);
        res.status(200).json(person);
    } catch (error) {
        console.error("Error fetching person:", error);
        res.status(500).json({ error: 'Error fetching person' });
    }
};

/**
 * Updates a person's data by ID and reactivates them.
 * @param {Object} req - Request object with the person's ID in the parameters and new data in the body.
 * @param {Object} res - Response object.
 */
exports.updatePerson = async (req, res) => {
    try {
        console.log("Updating person with ID:", req.params.id);
        const { name, cpf, birthDate, photoUrl, employee, position } = req.body;

        const person = await Person.findById(req.params.id);
        if (!person) {
            console.warn("Person not found with ID:", req.params.id);
            return res.status(404).json({ error: 'Person not found' });
        }

        person.name = name || person.name;
        person.cpf = cpf || person.cpf;
        person.birthDate = birthDate || person.birthDate;
        person.photoUrl = photoUrl || person.photoUrl;
        person.employee = employee !== undefined ? employee : person.employee;
        person.position = employee ? position : null;

        // Updates biometrics if a new photo is provided
        if (photoUrl) {
            console.log("Processing new biometrics for update...");
            person.biometrics = await processBiometrics(photoUrl);
        }

        // Reactivates the person on update
        await person.activate();
        console.log("Person updated and activated successfully:", person);

        res.status(200).json({ message: 'Person updated and activated successfully', person });
    } catch (error) {
        console.error("Error updating person:", error);
        res.status(500).json({ error: 'Error updating person' });
    }
};

/**
 * Deactivates a person by ID, instead of deleting them from the database.
 * @param {Object} req - Request object with the person's ID in the parameters.
 * @param {Object} res - Response object.
 */
exports.deletePerson = async (req, res) => {
    try {
        console.log("Deactivating person with ID:", req.params.id);
        const person = await Person.findById(req.params.id);
        if (!person) {
            console.warn("Person not found with ID:", req.params.id);
            return res.status(404).json({ error: 'Person not found' });
        }

        await person.deactivate();
        console.log("Person deactivated successfully:", person);
        res.status(200).json({ message: 'Person deactivated successfully' });
    } catch (error) {
        console.error("Error deactivating person:", error);
        res.status(500).json({ error: 'Error deactivating person' });
    }
};

/**
 * Lists all persons registered in the system.
 * @param {Object} req - Request object.
 * @param {Object} res - Response object.
 */
exports.getAllPersons = async (req, res) => {
    try {
        console.log("Fetching all persons...");
        const persons = await Person.find();
        console.log("Persons fetched:", persons);
        res.status(200).json(persons);
    } catch (error) {
        console.error("Error fetching persons:", error);
        res.status(500).json({ error: 'Error fetching persons' });
    }
};

/**
 * Verifies an image provided by URL and compares it with the biometrics of active persons.
 * @param {Object} req - Request object containing the image URL in the body.
 * @param {Object} res - Response object.
 */
exports.verifyPerson = async (req, res) => {
    try {
        console.log("Verifying person with photoUrl:", req.body.photoUrl);
        const { photoUrl } = req.body;

        // Processes the image to obtain biometric data
        const biometrics = await processBiometrics(photoUrl);
        if (!biometrics) {
            console.warn("No biometric data found in provided image.");
            return res.status(404).json({ match: false });
        }

        console.log("Processed biometrics for verification:", biometrics);

        // Compares biometrics with registered persons
        const persons = await Person.find({ active: true });
        console.log("Comparing biometrics with active persons...");

        for (let person of persons) {
            if (person.biometrics && compareBiometrics(biometrics, person.biometrics)) {
                console.log("Match found:", person);
                return res.status(299).json({ match: true, person });
            }
        }

        console.log("No match found.");
        res.status(404).json({ match: false });
    } catch (error) {
        console.error("Error verifying person:", error);
        res.status(500).json({ error: 'Error verifying person' });
    }
};

/**
 * Verifies a test image from the 'to_compare' folder by comparing it with registered images.
 * Receives the image name and compares it with stored biometrics in the 'registered' folder.
 * @param {Object} req - Request object containing the image name in the body.
 * @param {Object} res - Response object.
 */
exports.verifyPersonWithImage = async (req, res) => {
    try {
        const { imageName } = req.body;
        console.log("Verifying person with image:", imageName);

        // Calls the comparison function with registered images
        const result = await compareWithRegisteredImages(imageName);

        // Responds with comparison results
        if (result.match) {
            res.status(299).json({ match: true, fileName: result.fileName });
        } else {
            res.status(404).json({ match: false });
        }
    } catch (error) {
        console.error("Error verifying person:", error);
        res.status(500).json({ error: 'Error verifying person' });
    }
};

// src/util/biometricsUtils.js

const faceapi = require('face-api.js'); // Library for facial recognition
const tf = require('@tensorflow/tfjs-node'); // TensorFlow for data processing
const { Canvas, Image, ImageData } = require('canvas'); // Canvas for image manipulation
const canvas = require('canvas');
const path = require('path');
const fs = require('fs');

// Configures face-api.js to use canvas and manipulate images
faceapi.env.monkeyPatch({ Canvas, Image, ImageData });

/**
 * Loads the necessary facial recognition models for detection,
 * facial landmarks extraction, and facial descriptor.
 */
const loadModels = async () => {
    const modelPath = './models'; // Path where models are stored
    await faceapi.nets.ssdMobilenetv1.loadFromDisk(modelPath);
    await faceapi.nets.faceLandmark68Net.loadFromDisk(modelPath);
    await faceapi.nets.faceRecognitionNet.loadFromDisk(modelPath);
};

/**
 * Processes a local image to extract biometric data with the first 100 elements.
 * @param {string} imagePath - Path to the local image to be processed.
 * @returns {Float32Array | null} Extracted biometric data (100 elements) or null if no face is detected.
 */
const processBiometrics = async (imagePath) => {
    try {
        console.log("Processing biometrics for image:", imagePath);

        // Loads models if not already loaded
        await loadModels();

        // Loads the image from the specified path
        const img = await canvas.loadImage(imagePath);
        const detections = await faceapi.detectSingleFace(img)
            .withFaceLandmarks()
            .withFaceDescriptor();

        if (!detections) {
            console.warn("No face detected in image.");
            return null;
        }

        // Returns only the first 100 elements of the facial descriptor
        const biometrics = detections.descriptor.slice(0, 100);
        if (biometrics && biometrics.length === 100) {
            console.log("Biometric data loaded successfully with 100 elements.");
        } else {
            console.warn("Biometric data not loaded correctly or has an incorrect length.");
        }
        return biometrics;
    } catch (error) {
        console.error("Error processing biometrics:", error);
        return null;
    }
};

/**
 * Compares two sets of biometric data to check for a match.
 * @param {Float32Array} biometrics1 - First set of biometric data (100 points).
 * @param {Float32Array} biometrics2 - Second set of biometric data (100 points).
 * @returns {boolean} Returns true if data matches, false otherwise.
 */
const compareBiometrics = (biometrics1, biometrics2) => {
    if (!biometrics1 || !biometrics2) {
        console.warn("One of the biometric arrays is null.");
        return false;
    }
    if (biometrics1.length !== 100 || biometrics2.length !== 100) {
        console.warn("Biometric data length mismatch. Expected 100 elements.");
        return false;
    }

    try {
        // Calculates the Euclidean distance between the descriptors
        const distance = faceapi.euclideanDistance(biometrics1, biometrics2);
        const threshold = 0.7; // Adjusted threshold for more flexible matching
        return distance < threshold;
    } catch (error) {
        console.error("Error comparing biometrics:", error);
        return false;
    }
};

/**
 * Compares two sets of biometric data multiple times to check for match consistency.
 * @param {Float32Array} biometrics1 - First set of biometric data.
 * @param {Float32Array} biometrics2 - Second set of biometric data.
 * @param {number} attempts - Number of verification attempts.
 * @returns {boolean} Returns true if there is a match in all attempts, false otherwise.
 */
const verifyBiometricsMultipleAttempts = (biometrics1, biometrics2, attempts = 3) => {
    let matches = 0;
    for (let i = 0; i < attempts; i++) {
        if (compareBiometrics(biometrics1, biometrics2)) {
            matches++;
        }
    }
    return matches >= attempts;
};

/**
 * Loads and processes all images in the 'registered' folder for storing biometric data.
 * @returns {Object} An object containing biometric data for each registered image.
 */
const loadRegisteredBiometrics = async () => {
    const registeredPath = path.join(__dirname, '../../images/registered');
    const registeredBiometrics = {};

    const files = fs.readdirSync(registeredPath);
    for (const file of files) {
        const imagePath = path.join(registeredPath, file);
        const biometricData = await processBiometrics(imagePath);
        if (biometricData) {
            registeredBiometrics[file] = biometricData;
        }
    }
    return registeredBiometrics;
};

/**
 * Compares an image from the 'to_compare' folder with all registered images in the 'registered' folder.
 * Checks each comparison multiple times to ensure the match.
 * @param {string} compareImageName - Name of the image to be compared.
 * @returns {Object} An object indicating if there was a match and the name of the corresponding image.
 */
const compareWithRegisteredImages = async (compareImageName) => {
    const registeredBiometrics = await loadRegisteredBiometrics();
    const compareImagePath = path.join(__dirname, '../../images/to_compare', compareImageName);
    const compareBiometricsData = await processBiometrics(compareImagePath);

    if (!compareBiometricsData) {
        console.warn("No biometric data found in comparison image.");
        return { match: false };
    }

    for (const [fileName, registeredBiometricsData] of Object.entries(registeredBiometrics)) {
        // Checks for a match with at least 3 attempts
        if (verifyBiometricsMultipleAttempts(compareBiometricsData, registeredBiometricsData, 3)) {
            console.log(`Match found with registered image: ${fileName}`);
            return { match: true, fileName };
        }
    }

    console.log("No match found.");
    return { match: false };
};

module.exports = {
    processBiometrics,
    compareBiometrics,
    loadRegisteredBiometrics,
    compareWithRegisteredImages
};
// src/route/personRoutes.js

const express = require('express');
const personController = require('../controller/personController');
const router = express.Router();

/**
 * Route to create a new person.
 * Method: POST
 * Endpoint: /persons
 * Request Body: JSON with person data (name, cpf, birthDate, photoUrl, etc.)
 */
router.post('/', personController.createPerson);

/**
 * Route to retrieve a person by ID.
 * Method: GET
 * Endpoint: /persons/:id
 * Parameter: Person's ID in the URL.
 */
router.get('/:id', personController.getPersonById);

/**
 * Route to update a person's data by ID.
 * Method: PUT
 * Endpoint: /persons/:id
 * Parameter: Person's ID in the URL.
 * Request Body: JSON with the data to be updated.
 */
router.put('/:id', personController.updatePerson);

/**
 * Route to deactivate a person by ID.
 * Method: DELETE
 * Endpoint: /persons/:id
 * Parameter: Person's ID in the URL.
 */
router.delete('/:id', personController.deletePerson);

/**
 * Route to list all registered persons.
 * Method: GET
 * Endpoint: /persons
 */
router.get('/', personController.getAllPersons);

/**
 * Route to verify a person's biometrics using an image URL.
 * Method: POST
 * Endpoint: /persons/verify
 * Request Body: JSON with the image URL (photoUrl).
 */
router.post('/verify', personController.verifyPerson);

/**
 * Route to verify a person's biometrics using a local image from the 'to_compare' folder.
 * Method: POST
 * Endpoint: /persons/verify-image
 * Request Body: JSON with the name of the image to be compared (imageName).
 */
router.post('/verify-image', personController.verifyPersonWithImage);

module.exports = router;

我尝试使用 ChatGPT 进行检查,但我一直在绕圈子。

我使用 ChatGPT 在代码中添加注释以便更好地理解,我想知道我可能在哪里出错。如果有人可以提供帮助,我将非常感激。

javascript node.js backend face-api facial-identification
1个回答
0
投票

我有“同样”的问题。 我上传了我的照片,但是不同的日子并没有认出我。
该软件给我的脸提供了另一个哈希值。 有关于face-api-js的discord服务器吗???

© www.soinside.com 2019 - 2024. All rights reserved.