Lompat ke konten Lompat ke sidebar Lompat ke footer

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:

  1. 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.

  2. Backend (Logika Server): Dibuat dengan Node.js dan Express.js. Bagian ini akan menerima data dari frontend, memprosesnya, dan menyimpannya ke database MySQL.

  3. 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

  1. Buka phpMyAdmin (biasanya melalui XAMPP Control Panel).

  2. Buat database baru dengan nama db_absensi_wajah.

  3. Jalankan query SQL berikut untuk membuat tabel yang dibutuhkan.

SQL
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 dan deskriptor_wajah (representasi matematis dari wajah, bukan gambar).

  • tabel absensi: Mencatat waktu kehadiran setiap siswa.


Langkah 3: Membuat Backend dengan Node.js & Express.js

  1. Buat sebuah folder baru untuk proyek Anda, misalnya aplikasi-absen.

  2. Buka terminal atau Command Prompt di dalam folder tersebut.

  3. Jalankan perintah npm init -y untuk membuat file package.json.

  4. Instal semua library yang dibutuhkan dengan perintah berikut:

    Bash
    npm install express mysql2 cors
    
    • express: Framework untuk membuat server.

    • mysql2: Driver untuk menghubungkan Node.js ke MySQL.

    • cors: Agar frontend bisa berkomunikasi dengan backend.

  5. 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

  1. Di dalam folder aplikasi-absen, buat folder baru bernama public.

  2. Di dalam folder public, buat tiga file: index.html, style.css, dan script.js.

  3. Anda juga perlu men-download model face-api.js. Buat folder models di dalam public, lalu download file model dari repositori face-api.js dan letakkan di dalamnya.

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

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

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

JavaScript
// 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

  1. Jalankan Database: Pastikan Apache dan MySQL berjalan di XAMPP.

  2. Jalankan Backend: Buka terminal di folder aplikasi-absen dan jalankan perintah:

    Bash
    node server.js
    

    Anda akan melihat pesan Server berjalan di http://localhost:3000.

  3. Buka Frontend: Buka browser (disarankan Chrome atau Firefox) dan akses alamat:

    http://localhost:3000
    

    Karena kita menggunakan express.static('public') di server.js, server akan otomatis menampilkan file index.html dari folder public.

Cara Menggunakan Aplikasi

  1. 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.

  2. 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"