# ðū āļāļđāđāļĄāļ·āļāļāļēāļĢāļāļąāļāđāļāđāļāļāđāļāļĄāļđāļĨāļĨāļāļāļēāļāļāđāļāļĄāļđāļĨ ## Face Recognition Database Integration Guide āđāļāļāļŠāļēāļĢāļāļĩāđāļāļāļīāļāļēāļĒāļ§āļīāļāļĩāļāļēāļĢāļāļĢāļąāļāļāļĢāļļāļāļĢāļ°āļāļ Face Recognition āđāļŦāđāļŠāļēāļĄāļēāļĢāļāļāļąāļāđāļāđāļāļāđāļāļĄāļđāļĨāđāļāļŦāļāđāļēāļĨāļāļāļēāļāļāđāļāļĄāļđāļĨāđāļāļ·āđāļāđāļāđāđāļāļāļēāļĢāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļāđāļāļāļāļēāļĢāđāļāđāļĢāļđāļāļ āļēāļāļāđāļāļāļāļąāļ **āļāļđāđāļāļąāļāļāļē:** Goragod Wiriya **āļ§āļąāļāļāļĩāđāļāļąāļāđāļāļ:** 15 āļāļĢāļāļāļēāļāļĄ 2568 --- ## ðïļ āđāļāļĢāļāļŠāļĢāđāļēāļāļāļēāļāļāđāļāļĄāļđāļĨ ### āļāļēāļĢāļēāļ users (āļāđāļāļĄāļđāļĨāļāļļāļāļāļĨ) ```sql CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE, phone VARCHAR(20), department VARCHAR(100), position VARCHAR(100), status ENUM('active', 'inactive') DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` ### āļāļēāļĢāļēāļ face_descriptors (āļāđāļāļĄāļđāļĨ Face Descriptor) ```sql CREATE TABLE face_descriptors ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, descriptor_data TEXT NOT NULL, -- JSON array āļāļāļ face descriptor (128 dimensions) image_path VARCHAR(500), confidence FLOAT DEFAULT 0, quality_score FLOAT DEFAULT 0, is_primary BOOLEAN DEFAULT FALSE, -- āļāļģāļŦāļāļāļ§āđāļēāđāļāđāļ descriptor āļŦāļĨāļąāļāļŦāļĢāļ·āļāđāļĄāđ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ``` ### āļāļēāļĢāļēāļ face_logs (āļāļąāļāļāļķāļāļāļēāļĢāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļ) ```sql CREATE TABLE face_logs ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, similarity_score FLOAT, match_result BOOLEAN, detection_time FLOAT, -- āđāļ§āļĨāļēāļāļĩāđāđāļāđāđāļāļāļēāļĢāļāļĢāļ§āļāļāļąāļ (āļ§āļīāļāļēāļāļĩ) timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ip_address VARCHAR(45), user_agent TEXT, location VARCHAR(100), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ); ``` ### āļāļēāļĢāļēāļ access_logs (āļāļąāļāļāļķāļāļāļēāļĢāđāļāđāļēāđāļāđāļāļēāļ) ```sql CREATE TABLE access_logs ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, access_type ENUM('login', 'logout', 'register', 'update') NOT NULL, success BOOLEAN DEFAULT TRUE, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ip_address VARCHAR(45), notes TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ); ``` ### āļāļēāļĢāļŠāļĢāđāļēāļ Index āđāļāļ·āđāļāđāļāļīāđāļĄāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ ```sql CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_status ON users(status); CREATE INDEX idx_face_user_id ON face_descriptors(user_id); CREATE INDEX idx_face_primary ON face_descriptors(is_primary); CREATE INDEX idx_logs_timestamp ON face_logs(timestamp); CREATE INDEX idx_logs_user_id ON face_logs(user_id); CREATE INDEX idx_access_timestamp ON access_logs(timestamp); ``` --- ## ð§ Backend API (PHP) āļŠāļĢāđāļēāļāđāļāļĨāđ `api.php`: ```php pdo = new PDO( "mysql:host={$this->host};dbname={$this->dbname};charset=utf8mb4", $this->username, $this->password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ] ); } catch(PDOException $e) { $this->sendError('Database connection failed: ' . $e->getMessage()); } } public function getConnection() { return $this->pdo; } private function sendError($message) { http_response_code(500); echo json_encode(['error' => $message]); exit(); } } // āļāļĨāļēāļŠāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļāļāļąāļāļāļēāļĢ Face Recognition class FaceRecognitionAPI { private $db; private $uploadDir = 'uploads/faces/'; private $maxFileSize = 5 * 1024 * 1024; // 5MB private $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif']; public function __construct() { $this->db = (new DatabaseConnection())->getConnection(); $this->createUploadDirectory(); } private function createUploadDirectory() { if (!is_dir($this->uploadDir)) { mkdir($this->uploadDir, 0777, true); } } public function handleRequest() { $action = $_POST['action'] ?? $_GET['action'] ?? ''; switch($action) { case 'register_face': $this->registerFace(); break; case 'find_match': $this->findMatchingFace(); break; case 'get_all_users': $this->getAllUsers(); break; case 'get_user_faces': $this->getUserFaces(); break; case 'update_user': $this->updateUser(); break; case 'delete_user': $this->deleteUser(); break; case 'log_access': $this->logAccess(); break; case 'get_statistics': $this->getStatistics(); break; default: $this->sendError('Invalid action', 400); } } // āļĨāļāļāļ°āđāļāļĩāļĒāļāđāļāļŦāļāđāļēāđāļŦāļĄāđ public function registerFace() { try { $name = $this->validateInput($_POST['name'] ?? ''); $email = $this->validateEmail($_POST['email'] ?? ''); $phone = $_POST['phone'] ?? ''; $department = $_POST['department'] ?? ''; $position = $_POST['position'] ?? ''; $descriptorData = $_POST['descriptor'] ?? ''; if (empty($name) || empty($email) || empty($descriptorData)) { $this->sendError('Missing required fields', 400); } // āļāļĢāļ§āļāļŠāļāļ descriptor format $descriptor = json_decode($descriptorData, true); if (!$descriptor || count($descriptor) !== 128) { $this->sendError('Invalid face descriptor format', 400); } // āļāļĢāļ§āļāļŠāļāļ email āļāđāļģ $existingUser = $this->getUserByEmail($email); if ($existingUser) { $this->sendError('Email already exists', 409); } // āļāļąāļāđāļŦāļĨāļāļĢāļđāļāļ āļēāļ $imagePath = $this->uploadImage(); // āđāļĢāļīāđāļĄ transaction $this->db->beginTransaction(); try { // āđāļāļīāđāļĄāļāđāļāļĄāļđāļĨāļāļđāđāđāļāđ $stmt = $this->db->prepare(" INSERT INTO users (name, email, phone, department, position) VALUES (?, ?, ?, ?, ?) "); $stmt->execute([$name, $email, $phone, $department, $position]); $userId = $this->db->lastInsertId(); // āļāļģāļāļ§āļāļāļļāļāļ āļēāļāļāļāļ descriptor $qualityScore = $this->calculateDescriptorQuality($descriptor); // āļāļąāļāļāļķāļ face descriptor $stmt = $this->db->prepare(" INSERT INTO face_descriptors (user_id, descriptor_data, image_path, quality_score, is_primary) VALUES (?, ?, ?, ?, TRUE) "); $stmt->execute([ $userId, $descriptorData, $imagePath, $qualityScore ]); // āļāļąāļāļāļķāļ access log $this->logUserAccess($userId, 'register', true); $this->db->commit(); $this->sendSuccess([ 'user_id' => $userId, 'message' => 'Face registered successfully', 'quality_score' => $qualityScore ]); } catch (Exception $e) { $this->db->rollBack(); throw $e; } } catch (Exception $e) { $this->sendError($e->getMessage()); } } // āļāđāļāļŦāļēāđāļāļŦāļāđāļēāļāļĩāđāļāļĢāļāļāļąāļ public function findMatchingFace() { try { $inputDescriptor = json_decode($_POST['descriptor'] ?? '', true); $threshold = floatval($_POST['threshold'] ?? 0.6); if (!$inputDescriptor || count($inputDescriptor) !== 128) { $this->sendError('Invalid face descriptor', 400); } $startTime = microtime(true); // āļāļķāļāļāđāļāļĄāļđāļĨāđāļāļŦāļāđāļēāļāļąāđāļāļŦāļĄāļāļāļĩāđ active $stmt = $this->db->prepare(" SELECT u.id, u.name, u.email, u.department, u.position, f.descriptor_data, f.quality_score, f.image_path FROM users u JOIN face_descriptors f ON u.id = f.user_id WHERE u.status = 'active' AND f.is_primary = TRUE "); $stmt->execute(); $faces = $stmt->fetchAll(); $bestMatch = null; $bestDistance = PHP_FLOAT_MAX; foreach($faces as $face) { $storedDescriptor = json_decode($face['descriptor_data'], true); $distance = $this->calculateEuclideanDistance($inputDescriptor, $storedDescriptor); if($distance < $bestDistance) { $bestDistance = $distance; $bestMatch = $face; } } $detectionTime = microtime(true) - $startTime; $similarity = $bestDistance < 1 ? (1 - $bestDistance) * 100 : 0; $isMatch = $bestDistance < $threshold; // āļāļąāļāļāļķāļāļāļĨāļāļēāļĢāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļ $this->logComparison( $isMatch ? $bestMatch['id'] : null, $similarity, $isMatch, $detectionTime ); if ($isMatch && $bestMatch) { $this->sendSuccess([ 'match' => true, 'user' => [ 'id' => $bestMatch['id'], 'name' => $bestMatch['name'], 'email' => $bestMatch['email'], 'department' => $bestMatch['department'], 'position' => $bestMatch['position'], 'image_path' => $bestMatch['image_path'] ], 'similarity' => round($similarity, 2), 'distance' => round($bestDistance, 4), 'quality_score' => $bestMatch['quality_score'], 'detection_time' => round($detectionTime, 4) ]); } else { $this->sendSuccess([ 'match' => false, 'similarity' => round($similarity, 2), 'detection_time' => round($detectionTime, 4) ]); } } catch (Exception $e) { $this->sendError($e->getMessage()); } } // āļāļķāļāļāđāļāļĄāļđāļĨāļāļđāđāđāļāđāļāļąāđāļāļŦāļĄāļ public function getAllUsers() { try { $page = intval($_GET['page'] ?? 1); $limit = intval($_GET['limit'] ?? 20); $search = $_GET['search'] ?? ''; $offset = ($page - 1) * $limit; $whereClause = "WHERE u.status = 'active'"; $params = []; if (!empty($search)) { $whereClause .= " AND (u.name LIKE ? OR u.email LIKE ? OR u.department LIKE ?)"; $searchParam = "%{$search}%"; $params = [$searchParam, $searchParam, $searchParam]; } // āļāļąāļāļāļģāļāļ§āļāļāļąāđāļāļŦāļĄāļ $countStmt = $this->db->prepare("SELECT COUNT(*) as total FROM users u {$whereClause}"); $countStmt->execute($params); $total = $countStmt->fetch()['total']; // āļāļķāļāļāđāļāļĄāļđāļĨ $stmt = $this->db->prepare(" SELECT u.*, COUNT(f.id) as face_count, MAX(f.quality_score) as best_quality FROM users u LEFT JOIN face_descriptors f ON u.id = f.user_id {$whereClause} GROUP BY u.id ORDER BY u.created_at DESC LIMIT {$limit} OFFSET {$offset} "); $stmt->execute($params); $users = $stmt->fetchAll(); $this->sendSuccess([ 'users' => $users, 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $total, 'pages' => ceil($total / $limit) ] ]); } catch (Exception $e) { $this->sendError($e->getMessage()); } } // āļāļķāļāļāđāļāļĄāļđāļĨāļŠāļāļīāļāļī public function getStatistics() { try { $period = $_GET['period'] ?? '30'; // days // āļŠāļāļīāļāļīāļāļēāļĢāđāļāđāļāļēāļ $stmt = $this->db->prepare(" SELECT DATE(timestamp) as date, COUNT(*) as total_checks, COUNT(CASE WHEN match_result = 1 THEN 1 END) as successful_matches, AVG(similarity_score) as avg_similarity, AVG(detection_time) as avg_detection_time FROM face_logs WHERE timestamp >= DATE_SUB(NOW(), INTERVAL ? DAY) GROUP BY DATE(timestamp) ORDER BY date DESC "); $stmt->execute([$period]); $dailyStats = $stmt->fetchAll(); // āļŠāļāļīāļāļīāļāļđāđāđāļāđ $stmt = $this->db->query(" SELECT COUNT(*) as total_users, COUNT(CASE WHEN status = 'active' THEN 1 END) as active_users FROM users "); $userStats = $stmt->fetch(); // āļāļđāđāđāļāđāļāļĩāđāđāļāđāļēāđāļāđāļāļēāļāļĨāđāļēāļŠāļļāļ $stmt = $this->db->prepare(" SELECT u.name, u.department, l.timestamp, l.similarity_score FROM face_logs l JOIN users u ON l.user_id = u.id WHERE l.match_result = 1 ORDER BY l.timestamp DESC LIMIT 10 "); $stmt->execute(); $recentMatches = $stmt->fetchAll(); $this->sendSuccess([ 'daily_statistics' => $dailyStats, 'user_statistics' => $userStats, 'recent_matches' => $recentMatches ]); } catch (Exception $e) { $this->sendError($e->getMessage()); } } // āļāļąāļāļāđāļāļąāļāļāđāļ§āļĒāđāļŦāļĨāļ·āļ private function validateInput($input) { return trim(htmlspecialchars($input, ENT_QUOTES, 'UTF-8')); } private function validateEmail($email) { $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { throw new Exception('Invalid email format'); } return $email; } private function getUserByEmail($email) { $stmt = $this->db->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); return $stmt->fetch(); } private function uploadImage() { if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) { return null; } $file = $_FILES['image']; // āļāļĢāļ§āļāļŠāļāļāļāļāļēāļāđāļāļĨāđ if ($file['size'] > $this->maxFileSize) { throw new Exception('File size too large'); } // āļāļĢāļ§āļāļŠāļāļāļāļēāļĄāļŠāļāļļāļĨāđāļāļĨāđ $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($extension, $this->allowedExtensions)) { throw new Exception('Invalid file type'); } // āļŠāļĢāđāļēāļāļāļ·āđāļāđāļāļĨāđāđāļŦāļĄāđ $fileName = uniqid() . '_' . date('Y-m-d') . '.' . $extension; $uploadPath = $this->uploadDir . $fileName; if (move_uploaded_file($file['tmp_name'], $uploadPath)) { return $uploadPath; } throw new Exception('Failed to upload image'); } private function calculateEuclideanDistance($desc1, $desc2) { if (count($desc1) !== count($desc2)) { return PHP_FLOAT_MAX; } $sum = 0; for ($i = 0; $i < count($desc1); $i++) { $sum += pow($desc1[$i] - $desc2[$i], 2); } return sqrt($sum); } private function calculateDescriptorQuality($descriptor) { // āļāļģāļāļ§āļāļāļļāļāļ āļēāļāļāļēāļāļāļ§āļēāļĄāđāļāļĢāļāļĢāļ§āļāļāļāļ descriptor $mean = array_sum($descriptor) / count($descriptor); $variance = 0; foreach ($descriptor as $value) { $variance += pow($value - $mean, 2); } $variance /= count($descriptor); // āđāļāļĨāļāđāļāđāļāļāļ°āđāļāļ 0-100 return min(100, $variance * 1000); } private function logComparison($userId, $similarity, $matchResult, $detectionTime) { $stmt = $this->db->prepare(" INSERT INTO face_logs (user_id, similarity_score, match_result, detection_time, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?) "); $stmt->execute([ $userId, $similarity, $matchResult, $detectionTime, $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? '' ]); } private function logUserAccess($userId, $accessType, $success = true) { $stmt = $this->db->prepare(" INSERT INTO access_logs (user_id, access_type, success, ip_address) VALUES (?, ?, ?, ?) "); $stmt->execute([ $userId, $accessType, $success, $_SERVER['REMOTE_ADDR'] ?? '' ]); } private function sendSuccess($data) { echo json_encode(['success' => true, 'data' => $data]); exit(); } private function sendError($message, $code = 500) { http_response_code($code); echo json_encode(['error' => $message]); exit(); } } // āđāļĢāļīāđāļĄāļāđāļāļāļēāļĢāļāļģāļāļēāļ try { $api = new FaceRecognitionAPI(); $api->handleRequest(); } catch (Exception $e) { http_response_code(500); echo json_encode(['error' => 'Internal server error']); } ?> ``` --- ## ð Frontend JavaScript Integration āļŠāļĢāđāļēāļāđāļāļĨāđ `database.js`: ```javascript // āļāļĨāļēāļŠāļŠāļģāļŦāļĢāļąāļāļāļąāļāļāļēāļĢāļāļēāļĢāļāļīāļāļāđāļāļāļąāļ Database API class FaceDatabase { constructor(apiUrl = 'api.php') { this.apiUrl = apiUrl; this.cache = new Map(); this.cacheTimeout = 5 * 60 * 1000; // 5 minutes } // āļĨāļāļāļ°āđāļāļĩāļĒāļāđāļāļŦāļāđāļēāđāļŦāļĄāđ async registerFace(userData, descriptor, imageFile) { const formData = new FormData(); formData.append('action', 'register_face'); formData.append('name', userData.name); formData.append('email', userData.email); formData.append('phone', userData.phone || ''); formData.append('department', userData.department || ''); formData.append('position', userData.position || ''); formData.append('descriptor', JSON.stringify(Array.from(descriptor))); if (imageFile) { formData.append('image', imageFile); } try { const response = await fetch(this.apiUrl, { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { this.clearCache(); return result.data; } else { throw new Error(result.error); } } catch (error) { console.error('Registration error:', error); throw error; } } // āļāđāļāļŦāļēāđāļāļŦāļāđāļēāļāļĩāđāļāļĢāļāļāļąāļ async findMatch(descriptor, threshold = 0.6) { try { const response = await fetch(this.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ action: 'find_match', descriptor: JSON.stringify(Array.from(descriptor)), threshold: threshold.toString() }) }); const result = await response.json(); if (result.success) { return result.data; } else { throw new Error(result.error); } } catch (error) { console.error('Match finding error:', error); throw error; } } // āļāļķāļāļĢāļēāļĒāļāļ·āđāļāļāļđāđāđāļāđāļāļąāđāļāļŦāļĄāļ async getAllUsers(page = 1, limit = 20, search = '') { const cacheKey = `users_${page}_${limit}_${search}`; // āļāļĢāļ§āļāļŠāļāļ cache if (this.cache.has(cacheKey)) { const cached = this.cache.get(cacheKey); if (Date.now() - cached.timestamp < this.cacheTimeout) { return cached.data; } } try { const params = new URLSearchParams({ action: 'get_all_users', page: page.toString(), limit: limit.toString(), search: search }); const response = await fetch(`${this.apiUrl}?${params}`); const result = await response.json(); if (result.success) { // āļāļąāļāļāļķāļāđāļ cache this.cache.set(cacheKey, { data: result.data, timestamp: Date.now() }); return result.data; } else { throw new Error(result.error); } } catch (error) { console.error('Error fetching users:', error); throw error; } } // āļāļķāļāļŠāļāļīāļāļīāļāļēāļĢāđāļāđāļāļēāļ async getStatistics(period = 30) { try { const params = new URLSearchParams({ action: 'get_statistics', period: period.toString() }); const response = await fetch(`${this.apiUrl}?${params}`); const result = await response.json(); if (result.success) { return result.data; } else { throw new Error(result.error); } } catch (error) { console.error('Error fetching statistics:', error); throw error; } } // āļĨāđāļēāļ cache clearCache() { this.cache.clear(); } // āļāļĢāļ§āļāļŠāļāļāļŠāļāļēāļāļ°āļāļēāļĢāđāļāļ·āđāļāļĄāļāđāļ async checkConnection() { try { const response = await fetch(this.apiUrl + '?action=ping'); return response.ok; } catch (error) { return false; } } } // āļāļĢāļąāļāļāļĢāļļāļ Face Recognition App āđāļŦāđāđāļāđāļāļēāļ Database class DatabaseFaceRecognition extends FaceRecognitionApp { constructor() { super(); this.database = new FaceDatabase(); this.isDbMode = true; this.setupDatabaseUI(); } setupDatabaseUI() { // āđāļāļīāđāļĄ UI āļŠāļģāļŦāļĢāļąāļāļĨāļāļāļ°āđāļāļĩāļĒāļ this.createRegistrationForm(); this.createUserList(); this.createStatisticsPanel(); } createRegistrationForm() { const registrationHTML = `
`; document.querySelector('.container').insertAdjacentHTML('beforeend', registrationHTML); } async registerCurrentFace() { if (!this.referenceDescriptor) { this.showError('āļāļĢāļļāļāļēāļāļąāļāđāļŦāļĨāļāļĢāļđāļāļ āļēāļāļāđāļāļāļĨāļāļāļ°āđāļāļĩāļĒāļ'); return; } const formData = { name: document.getElementById('reg-name').value, email: document.getElementById('reg-email').value, phone: document.getElementById('reg-phone').value, department: document.getElementById('reg-department').value, position: document.getElementById('reg-position').value }; if (!formData.name || !formData.email) { this.showError('āļāļĢāļļāļāļēāļāļĢāļāļāļāđāļāļĄāļđāļĨāļāļĩāđāļāļģāđāļāđāļ'); return; } try { this.showLoading('āļāļģāļĨāļąāļāļāļąāļāļāļķāļāļāđāļāļĄāļđāļĨ...'); const result = await this.database.registerFace( formData, this.referenceDescriptor, this.currentImageFile ); this.showResult(`â āļĨāļāļāļ°āđāļāļĩāļĒāļāļŠāļģāđāļĢāđāļ! āļāļļāļāļ āļēāļ: ${result.quality_score.toFixed(1)}%`); document.getElementById('registration-form').reset(); } catch (error) { this.showError(`āđāļĄāđāļŠāļēāļĄāļēāļĢāļāļĨāļāļāļ°āđāļāļĩāļĒāļāđāļāđ: ${error.message}`); } } // āļāļĢāļąāļāļāļąāļāļāđāļāļąāļāļāļēāļĢāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļāđāļŦāđāđāļāđ Database async performDatabaseComparison() { if (!this.isModelLoaded || !this.video) return; try { const detection = await faceapi.detectSingleFace( this.video, new faceapi.TinyFaceDetectorOptions() ).withFaceLandmarks().withFaceDescriptor(); if (detection) { // āļāđāļāļŦāļēāđāļāļāļēāļāļāđāļāļĄāļđāļĨ const matchResult = await this.database.findMatch(detection.descriptor, 0.6); if (matchResult.match) { const user = matchResult.user; this.displayMatchResult({ match: true, user: user, similarity: matchResult.similarity, detectionTime: matchResult.detection_time, qualityScore: matchResult.quality_score }); } else { this.displayMatchResult({ match: false, similarity: matchResult.similarity, detectionTime: matchResult.detection_time }); } // āļ§āļēāļāļāļĢāļāļ this.drawBoxOnVideo(detection); } else { this.clearMatchResult(); this.clearVideoCanvas(); } } catch (error) { console.error('Database comparison error:', error); this.showError('āđāļāļīāļāļāđāļāļāļīāļāļāļĨāļēāļāđāļāļāļēāļĢāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļ'); } } displayMatchResult(result) { if (result.match) { this.resultsContainer.innerHTML = `āļāļ§āļēāļĄāđāļĄāđāļāļĒāļģ: ${result.similarity.toFixed(1)}%
āđāļ§āļĨāļēāļāļĢāļ§āļāļŠāļāļ: ${(result.detectionTime * 1000).toFixed(0)}ms
āļāļļāļāļ āļēāļ: ${result.qualityScore?.toFixed(1) || 'N/A'}%
āļāļ§āļēāļĄāļāļĨāđāļēāļĒāļāļĨāļķāļāļŠāļđāļāļŠāļļāļ: ${result.similarity.toFixed(1)}%
āđāļ§āļĨāļēāļāļĢāļ§āļāļŠāļāļ: ${(result.detectionTime * 1000).toFixed(0)}ms
| āļāļ·āđāļ | āļāļĩāđāļĄāļĨ | āđāļāļāļ | āļāļģāđāļŦāļāđāļ | āļ§āļąāļāļāļĩāđāļĨāļāļāļ°āđāļāļĩāļĒāļ | āļāļēāļĢāļāļąāļāļāļēāļĢ |
|---|