edumanagerpro2/manager/scratch/match_faces.js

118 lines
3.8 KiB
JavaScript

import * as faceapi from '@vladmandic/face-api';
import sharp from 'sharp';
import pg from 'pg';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Disable certificate verification for CDN download if needed
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
const pool = new pg.Pool({
connectionString: 'postgresql://edumanager:EduManager2026!Seguro@150.230.87.131:5432/edumanager'
});
async function run() {
try {
// 1. Initialize face-api environment for Node
console.log('Inicializando face-api...');
// Tiny Face Detector, Face Landmark 68, and Face Recognition Nets
const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model/';
console.log('Carregando modelos face-api do CDN...');
await Promise.all([
faceapi.nets.ssdMobilenetv1.loadFromUri(MODEL_URL),
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL)
]);
console.log('Modelos carregados com sucesso!');
// 2. Fetch students and their face descriptors from DB
console.log('Buscando alunos do banco de dados...');
const { rows: students } = await pool.query('SELECT id, nome, face_descriptor FROM alunos WHERE face_descriptor IS NOT NULL');
console.log(`Encontrados ${students.length} alunos com biometria no banco.`);
// 3. Read downloaded photos from disk
const photosDir = path.join(__dirname, 'photos');
const photoFiles = fs.readdirSync(photosDir).filter(f => f.endsWith('.webp'));
console.log(`Encontradas ${photoFiles.length} fotos webp locais.`);
const results = [];
// 4. Process each photo, get its descriptor, and find best match
for (const file of photoFiles) {
const filePath = path.join(photosDir, file);
console.log(`Processando ${file}...`);
// Use sharp to convert webp to raw RGB buffer for face-api tensor
const image = sharp(filePath);
const metadata = await image.metadata();
const { data, info } = await image
.raw()
.toBuffer({ resolveWithObject: true });
// Create tensor from raw pixels
const tensor = faceapi.tf.tensor3d(
new Uint8Array(data),
[info.height, info.width, 3],
'int32'
);
// Detect face and extract descriptor
const detection = await faceapi.detectSingleFace(tensor)
.withFaceLandmarks()
.withFaceDescriptor();
tensor.dispose();
if (!detection) {
console.warn(`Nenhum rosto detectado na foto ${file}`);
continue;
}
const imgDescriptor = detection.descriptor;
// Compare with all students using Euclidean distance
let bestMatch = null;
let minDistance = Infinity;
for (const s of students) {
const dbDescriptorStr = typeof s.face_descriptor === 'string'
? s.face_descriptor
: JSON.stringify(s.face_descriptor);
const dbDescriptor = new Float32Array(JSON.parse(dbDescriptorStr));
const distance = faceapi.euclideanDistance(imgDescriptor, dbDescriptor);
if (distance < minDistance) {
minDistance = distance;
bestMatch = s;
}
}
console.log(`Foto: ${file} | Melhor Match: ${bestMatch?.nome} | Distância: ${minDistance.toFixed(4)}`);
results.push({
file,
studentId: bestMatch?.id,
studentName: bestMatch?.nome,
distance: minDistance
});
}
console.log('\n--- RESULTADO DE PROJEÇÃO DE CORRELAÇÃO DE BIOMETRIA ---');
results.forEach(r => {
console.log(`Foto: ${r.file} -> Aluno: ${r.studentName} (ID: ${r.studentId}) | Distância: ${r.distance.toFixed(4)}`);
});
} catch (err) {
console.error('Erro na execução:', err);
} finally {
await pool.end();
}
}
run();