# ðŸ’ū āļ„āļđāđˆāļĄāļ·āļ­āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļĨāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ ## 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.user.name}

āđāļœāļ™āļ: ${result.user.department || 'āđ„āļĄāđˆāļĢāļ°āļšāļļ'}

āļ•āļģāđāļŦāļ™āđˆāļ‡: ${result.user.position || 'āđ„āļĄāđˆāļĢāļ°āļšāļļ'}

āļ­āļĩāđ€āļĄāļĨ: ${result.user.email}

āļ„āļ§āļēāļĄāđāļĄāđˆāļ™āļĒāļģ: ${result.similarity.toFixed(1)}%

āđ€āļ§āļĨāļēāļ•āļĢāļ§āļˆāļŠāļ­āļš: ${(result.detectionTime * 1000).toFixed(0)}ms

āļ„āļļāļ“āļ āļēāļž: ${result.qualityScore?.toFixed(1) || 'N/A'}%

`; } else { this.resultsContainer.innerHTML = `

ðŸ”ī āđ„āļĄāđˆāļžāļšāļ‚āđ‰āļ­āļĄāļđāļĨāđƒāļ™āļĢāļ°āļšāļš

āļ„āļ§āļēāļĄāļ„āļĨāđ‰āļēāļĒāļ„āļĨāļķāļ‡āļŠāļđāļ‡āļŠāļļāļ”: ${result.similarity.toFixed(1)}%

āđ€āļ§āļĨāļēāļ•āļĢāļ§āļˆāļŠāļ­āļš: ${(result.detectionTime * 1000).toFixed(0)}ms

`; } } showRegistrationForm() { document.getElementById('registration-panel').style.display = 'block'; document.getElementById('reg-name').focus(); } } // āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āđƒāļŠāđ‰āļ‡āļēāļ™ Database Mode document.addEventListener('DOMContentLoaded', () => { if (typeof faceapi !== 'undefined') { window.app = new DatabaseFaceRecognition(); } else { setTimeout(() => { window.app = new DatabaseFaceRecognition(); }, 1000); } }); ``` --- ## ðŸ“ą Admin Dashboard āļŠāļĢāđ‰āļēāļ‡āđ„āļŸāļĨāđŒ `admin.html`: ```html Face Recognition - Admin Dashboard

🎛ïļ Face Recognition Admin Dashboard

0
āļˆāļģāļ™āļ§āļ™āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļąāđ‰āļ‡āļŦāļĄāļ”
0
āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āļąāļ™āļ™āļĩāđ‰
0%
āļ­āļąāļ•āļĢāļēāļ„āļ§āļēāļĄāļŠāļģāđ€āļĢāđ‡āļˆ

📊 āļŠāļ–āļīāļ•āļīāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļĢāļēāļĒāļ§āļąāļ™

ðŸ‘Ĩ āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰

āļŠāļ·āđˆāļ­ āļ­āļĩāđ€āļĄāļĨ āđāļœāļ™āļ āļ•āļģāđāļŦāļ™āđˆāļ‡ āļ§āļąāļ™āļ—āļĩāđˆāļĨāļ‡āļ—āļ°āđ€āļšāļĩāļĒāļ™ āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ
``` --- ## 🔒 āļ‚āđ‰āļ­āļ„āļ§āļĢāļžāļīāļˆāļēāļĢāļ“āļēāļ”āđ‰āļēāļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ ### 1. āļāļēāļĢāđ€āļ‚āđ‰āļēāļĢāļŦāļąāļŠāļ‚āđ‰āļ­āļĄāļđāļĨ ```php // āđ€āļ‚āđ‰āļēāļĢāļŦāļąāļŠ Face Descriptor function encryptDescriptor($data, $key) { $cipher = "AES-256-CBC"; $ivlen = openssl_cipher_iv_length($cipher); $iv = openssl_random_pseudo_bytes($ivlen); $encrypted = openssl_encrypt($data, $cipher, $key, 0, $iv); return base64_encode($encrypted . '::' . $iv); } function decryptDescriptor($data, $key) { $cipher = "AES-256-CBC"; list($encrypted_data, $iv) = explode('::', base64_decode($data), 2); return openssl_decrypt($encrypted_data, $cipher, $key, 0, $iv); } ``` ### 2. āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒ ```php // JWT Token Validation function validateToken($token) { try { $decoded = JWT::decode($token, $secret_key, ['HS256']); return $decoded; } catch (Exception $e) { return false; } } ``` ### 3. Rate Limiting ```php // Simple rate limiting function checkRateLimit($ip, $action, $limit = 10, $window = 60) { // Implementation for rate limiting // Store in Redis or database } ``` --- ## 📈 āļāļēāļĢāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž ### 1. Database Indexing ```sql -- Composite indexes for faster searches CREATE INDEX idx_face_user_quality ON face_descriptors(user_id, quality_score DESC); CREATE INDEX idx_logs_date_result ON face_logs(DATE(timestamp), match_result); ``` ### 2. Query Optimization ```sql -- Use EXPLAIN to analyze query performance EXPLAIN SELECT u.name, f.descriptor_data FROM users u JOIN face_descriptors f ON u.id = f.user_id WHERE u.status = 'active'; ``` ### 3. Caching Strategy ```javascript // Client-side caching with expiration class CacheManager { constructor(defaultTTL = 300000) { // 5 minutes this.cache = new Map(); this.defaultTTL = defaultTTL; } set(key, value, ttl = this.defaultTTL) { this.cache.set(key, { value, expires: Date.now() + ttl }); } get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expires) { this.cache.delete(key); return null; } return item.value; } } ``` --- ## 🚀 āļāļēāļĢ Deploy Production ### 1. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” Server - PHP 7.4+ with MySQLi/PDO - MySQL 5.7+ āļŦāļĢāļ·āļ­ MariaDB 10.3+ - SSL Certificate (HTTPS) - Sufficient storage for face images ### 2. Configuration ```php // config.php return [ 'database' => [ 'host' => $_ENV['DB_HOST'] ?? 'localhost', 'name' => $_ENV['DB_NAME'] ?? 'face_recognition', 'user' => $_ENV['DB_USER'] ?? 'root', 'pass' => $_ENV['DB_PASS'] ?? '', ], 'upload' => [ 'max_size' => 5 * 1024 * 1024, // 5MB 'allowed_types' => ['jpg', 'jpeg', 'png'], 'path' => '/var/www/uploads/faces/', ], 'security' => [ 'jwt_secret' => $_ENV['JWT_SECRET'] ?? 'your-secret-key', 'encrypt_key' => $_ENV['ENCRYPT_KEY'] ?? 'your-encrypt-key', ] ]; ``` ### 3. Backup Strategy ```bash #!/bin/bash # backup.sh DATE=$(date +%Y%m%d_%H%M%S) mysqldump -u$DB_USER -p$DB_PASS face_recognition > backup_$DATE.sql tar -czf images_backup_$DATE.tar.gz uploads/faces/ ``` --- ## 📞 āļāļēāļĢāļŠāļ™āļąāļšāļŠāļ™āļļāļ™ āļŠāļģāļŦāļĢāļąāļšāļ„āļģāļ–āļēāļĄāđ€āļāļĩāđˆāļĒāļ§āļāļąāļšāļāļēāļĢāļ•āļīāļ”āļ•āļąāđ‰āļ‡āđāļĨāļ°āđƒāļŠāđ‰āļ‡āļēāļ™: - 📧 Email: admin@goragod.com - 🌐 Website: https://goragod.com --- **ÂĐ 2568 Goragod Wiriya - Face Recognition Database Integration Guide**