Aplikasi Absensi Wajah Mudah Meriah
Berikut adalah panduan lengkap beserta kode untuk membangun aplikasi absensi siswa dengan wajah.
Arsitektur Aplikasi
Aplikasi kita akan terdiri dari 3 bagian utama:
Frontend (Tampilan Pengguna): Dibuat dengan HTML, CSS, dan JavaScript. Bagian ini akan menampilkan video dari webcam, mendeteksi wajah, dan mengirim data ke backend. Kita akan menggunakan library
face-api.js
yang sangat populer untuk deteksi wajah di browser.Backend (Logika Server): Dibuat dengan Node.js dan Express.js. Bagian ini akan menerima data dari frontend, memprosesnya, dan menyimpannya ke database MySQL.
Database: Menggunakan MySQL untuk menyimpan data siswa, data wajah (deskriptor), dan catatan absensi.
Langkah 1: Persiapan Awal
Sebelum memulai, pastikan Anda sudah menginstal:
Node.js: Untuk menjalankan server backend.
XAMPP atau MySQL Server: Untuk membuat dan mengelola database.
Langkah 2: Pengaturan Database MySQL
Buka phpMyAdmin (biasanya melalui XAMPP Control Panel).
Buat database baru dengan nama
db_absensi_wajah
.Jalankan query SQL berikut untuk membuat tabel yang dibutuhkan.
CREATE TABLE siswa (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(100) NOT NULL,
deskriptor_wajah TEXT NOT NULL,
waktu_daftar TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE absensi (
id INT AUTO_INCREMENT PRIMARY KEY,
id_siswa INT NOT NULL,
waktu_absen DATETIME NOT NULL,
FOREIGN KEY (id_siswa) REFERENCES siswa(id) ON DELETE CASCADE
);
tabel siswa
: Menyimpan nama siswa dandeskriptor_wajah
(representasi matematis dari wajah, bukan gambar).tabel absensi
: Mencatat waktu kehadiran setiap siswa.
Langkah 3: Membuat Backend dengan Node.js & Express.js
Buat sebuah folder baru untuk proyek Anda, misalnya
aplikasi-absen
.Buka terminal atau Command Prompt di dalam folder tersebut.
Jalankan perintah
npm init -y
untuk membuat filepackage.json
.Instal semua library yang dibutuhkan dengan perintah berikut:
Bashnpm install express mysql2 cors
express
: Framework untuk membuat server.mysql2
: Driver untuk menghubungkan Node.js ke MySQL.cors
: Agar frontend bisa berkomunikasi dengan backend.
Buat file baru bernama
server.js
dan salin kode di bawah ini:JavaScript// server.js const express = require('express'); const mysql = require('mysql2/promise'); const cors = require('cors'); const app = express(); const port = 3000; // Middleware app.use(cors()); app.use(express.json({ limit: '10mb' })); // Menaikkan limit untuk data deskriptor wajah app.use(express.static('public')); // Menyajikan file frontend dari folder 'public' // Konfigurasi koneksi database const dbConfig = { host: 'localhost', user: 'root', // Ganti jika user Anda berbeda password: '', // Ganti jika ada password database: 'db_absensi_wajah' }; // Endpoint untuk mendaftarkan siswa baru app.post('/register', async (req, res) => { const { name, descriptor } = req.body; if (!name || !descriptor) { return res.status(400).json({ success: false, message: 'Nama dan deskriptor wajah wajib diisi.' }); } try { const connection = await mysql.createConnection(dbConfig); const sql = 'INSERT INTO siswa (nama, deskriptor_wajah) VALUES (?, ?)'; await connection.execute(sql, [name, JSON.stringify(descriptor)]); await connection.end(); res.json({ success: true, message: 'Siswa berhasil didaftarkan!' }); } catch (error) { console.error('Database Error:', error); res.status(500).json({ success: false, message: 'Gagal mendaftarkan siswa.' }); } }); // Endpoint untuk mendapatkan semua data siswa yang terdaftar app.get('/students', async (req, res) => { try { const connection = await mysql.createConnection(dbConfig); const [rows] = await connection.query('SELECT id, nama, deskriptor_wajah FROM siswa'); await connection.end(); // Mengubah string JSON deskriptor kembali menjadi array const students = rows.map(student => ({ ...student, deskriptor_wajah: JSON.parse(student.deskriptor_wajah) })); res.json(students); } catch (error) { console.error('Database Error:', error); res.status(500).json({ success: false, message: 'Gagal mengambil data siswa.' }); } }); // Endpoint untuk mencatat absensi app.post('/attendance', async (req, res) => { const { student_id } = req.body; if (!student_id) { return res.status(400).json({ success: false, message: 'ID Siswa wajib diisi.' }); } try { const connection = await mysql.createConnection(dbConfig); const sql = 'INSERT INTO absensi (id_siswa, waktu_absen) VALUES (?, NOW())'; await connection.execute(sql, [student_id]); await connection.end(); res.json({ success: true, message: 'Absensi berhasil dicatat!' }); } catch (error) { console.error('Database Error:', error); res.status(500).json({ success: false, message: 'Gagal mencatat absensi.' }); } }); // Menjalankan server app.listen(port, () => { console.log(`Server berjalan di http://localhost:${port}`); });
Langkah 4: Membuat Frontend dengan HTML, CSS, & JavaScript
Di dalam folder
aplikasi-absen
, buat folder baru bernamapublic
.Di dalam folder
public
, buat tiga file:index.html
,style.css
, danscript.js
.Anda juga perlu men-download model
face-api.js
. Buat foldermodels
di dalampublic
, lalu download file model dari dan letakkan di dalamnya.repositori face-api.js
Struktur Folder Akhir:
/aplikasi-absen
├── /public
│ ├── /models
│ │ ├── tiny_face_detector_model-weights_manifest.json
│ │ ├── face_landmark_68_model-weights_manifest.json
│ │ ├── ... (dan file model lainnya)
│ ├── index.html
│ ├── style.css
│ ├── script.js
├── server.js
└── package.json
Kode index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aplikasi Absensi Wajah</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Absensi Siswa dengan Wajah</h1>
<div class="video-container">
<video id="video" width="720" height="560" autoplay muted></video>
</div>
<div id="status">Menginisialisasi kamera...</div>
</div>
<div class="registration-form">
<h2>Daftarkan Siswa Baru</h2>
<input type="text" id="studentName" placeholder="Masukkan Nama Siswa">
<button id="registerButton">Ambil Gambar & Daftarkan</button>
</div>
<script defer src="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js"></script>
<script defer src="script.js"></script>
</body>
</html>
Kode style.css
body {
font-family: Arial, sans-serif;
background-color: #f0f2f5;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 0;
padding: 20px;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1, h2 {
color: #333;
}
.video-container {
position: relative;
width: 720px;
height: 560px;
margin: 20px auto;
border: 2px solid #ddd;
}
video {
position: absolute;
top: 0;
left: 0;
}
canvas {
position: absolute;
top: 0;
left: 0;
}
#status {
margin-top: 15px;
font-size: 1.2em;
color: #555;
font-weight: bold;
}
.registration-form {
margin-top: 20px;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 10px;
width: 300px;
}
input[type="text"] {
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1em;
}
button:hover {
background-color: #0056b3;
}
Kode script.js
// script.js
const video = document.getElementById('video');
const registerButton = document.getElementById('registerButton');
const studentNameInput = document.getElementById('studentName');
const statusDiv = document.getElementById('status');
let labeledFaceDescriptors = null;
// Memuat model face-api.js
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
faceapi.nets.faceRecognitionNet.loadFromUri('/models')
]).then(start);
async function start() {
statusDiv.innerText = 'Model berhasil dimuat. Mengakses kamera...';
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: {} });
video.srcObject = stream;
// Memuat data siswa yang sudah terdaftar
labeledFaceDescriptors = await loadLabeledImages();
statusDiv.innerText = 'Kamera siap. Arahkan wajah Anda.';
} catch (err) {
console.error(err);
statusDiv.innerText = 'Gagal mengakses kamera.';
}
}
video.addEventListener('play', () => {
const canvas = faceapi.createCanvasFromMedia(video);
document.querySelector('.video-container').append(canvas);
const displaySize = { width: video.width, height: video.height };
faceapi.matchDimensions(canvas, displaySize);
const faceMatcher = new faceapi.FaceMatcher(labeledFaceDescriptors, 0.6);
const alreadyAttended = new Set(); // Set untuk melacak siswa yang sudah diabsen
setInterval(async () => {
const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceDescriptors();
const resizedDetections = faceapi.resizeResults(detections, displaySize);
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
faceapi.draw.drawDetections(canvas, resizedDetections);
const results = resizedDetections.map(d => faceMatcher.findBestMatch(d.descriptor));
results.forEach((result, i) => {
const box = resizedDetections[i].detection.box;
const studentId = parseInt(result.label);
const studentName = result.toString().replace(/\s\(\d+\.\d+\)$/, ''); // Hapus skor confidence dari label
const drawBox = new faceapi.draw.DrawBox(box, { label: studentName });
drawBox.draw(canvas);
// Jika wajah dikenali dan belum diabsen hari ini
if (result.label !== 'unknown' && !alreadyAttended.has(studentId)) {
markAttendance(studentId, studentName);
alreadyAttended.add(studentId); // Tambahkan ke set agar tidak diabsen lagi
}
});
}, 200); // Deteksi setiap 200ms
});
// Fungsi untuk memuat data wajah dari database
async function loadLabeledImages() {
statusDiv.innerText = 'Memuat data siswa terdaftar...';
const response = await fetch('http://localhost:3000/students');
const students = await response.json();
return Promise.all(
students.map(student => {
// Deskriptor sudah dalam bentuk array, jadi langsung digunakan
const descriptors = [new Float32Array(Object.values(student.deskriptor_wajah[0]))];
return new faceapi.LabeledFaceDescriptors(String(student.id), descriptors);
})
);
}
// Event listener untuk tombol registrasi
registerButton.addEventListener('click', async () => {
const name = studentNameInput.value;
if (!name) {
alert('Nama siswa tidak boleh kosong.');
return;
}
statusDiv.innerText = 'Mendeteksi wajah untuk pendaftaran...';
const detections = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceDescriptor();
if (detections) {
const descriptor = [detections.descriptor];
const response = await fetch('http://localhost:3000/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: name, descriptor: descriptor }),
});
const result = await response.json();
alert(result.message);
// Muat ulang data setelah registrasi berhasil
labeledFaceDescriptors = await loadLabeledImages();
studentNameInput.value = '';
} else {
alert('Wajah tidak terdeteksi. Coba lagi.');
}
statusDiv.innerText = 'Kamera siap. Arahkan wajah Anda.';
});
// Fungsi untuk mengirim data absensi ke server
async function markAttendance(studentId, studentName) {
statusDiv.innerText = `Wajah ${studentName} terdeteksi. Mencatat kehadiran...`;
await fetch('http://localhost:3000/attendance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ student_id: studentId })
});
// Tampilkan notifikasi atau update UI
statusDiv.innerText = `✅ Kehadiran ${studentName} berhasil dicatat!`;
setTimeout(() => {
statusDiv.innerText = 'Kamera siap. Arahkan wajah Anda.';
}, 5000); // Kembali ke status normal setelah 5 detik
}
Langkah 5: Menjalankan Aplikasi
Jalankan Database: Pastikan Apache dan MySQL berjalan di XAMPP.
Jalankan Backend: Buka terminal di folder
aplikasi-absen
dan jalankan perintah:Bashnode server.js
Anda akan melihat pesan
Server berjalan di http://localhost:3000
.Buka Frontend: Buka browser (disarankan Chrome atau Firefox) dan akses alamat:
http://localhost:3000
Karena kita menggunakan
express.static('public')
diserver.js
, server akan otomatis menampilkan fileindex.html
dari folderpublic
.
Cara Menggunakan Aplikasi
Mendaftarkan Siswa:
Ketik nama siswa di kolom input.
Posisikan wajah siswa di depan kamera hingga terlihat jelas.
Klik tombol "Ambil Gambar & Daftarkan". Aplikasi akan mendeteksi wajah, mengambil deskriptornya, dan menyimpannya ke database.
Melakukan Absensi:
Cukup arahkan wajah ke kamera.
Jika wajah dikenali sebagai siswa yang terdaftar, sistem akan otomatis mencatat kehadirannya dan menampilkan pesan konfirmasi. Setiap siswa hanya akan diabsen sekali per sesi.
Selamat mencoba! Proyek ini adalah cara yang fantastis untuk menggabungkan pengembangan web modern dengan kecerdasan buatan di lingkungan sekolah.
Aplikasinya :
https://drive.google.com/drive/folders/1VFZIbZg-Gy3IF7kP-LtBpDjkubT_FTke?usp=sharing
Posting Komentar untuk "Aplikasi Absensi Wajah Mudah Meriah"