MIF_E31230910_MP-HRIS-WEB/notebook_face_recognition.py

283 lines
10 KiB
Python

# %% [markdown]
# # 🧠 Face Recognition Pipeline: Mandiri, Lengkap & Visual
# Ini adalah perpaduan kode yang **sangat sederhana** (mengambil 100 frame tanpa crop),
# ditambah dengan **Gambar Pendukung (Visualisasi Matplotlib)** agar outputnya tidak sekadar
# teks melainkan bisa Anda lihat perubahan bentuk wajahnya per langkah!
# %% [markdown]
# ## Tahap 1: Persiapan Library
# %%
import cv2
import os
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
print("✅ Library ter-load dengan baik!")
# %% [markdown]
# ## Tahap 2: Ektraksi 100 Frame dari Video Asli 🎥 (Tanpa Potong Wajah)
# %%
VIDEO_DIR = r"C:\xampp\htdocs\mpg-hris\storage\app\private\face_videos"
kumpulan_gambar_mentah = {}
print("🎬 Membaca video enrollment...\n")
for user_id in sorted(os.listdir(VIDEO_DIR)):
video_path = os.path.join(VIDEO_DIR, user_id, "enrollment.mp4")
if not os.path.exists(video_path): continue
cap = cv2.VideoCapture(video_path)
gambar_dikumpulkan = []
while len(gambar_dikumpulkan) < 100:
ret, frame = cap.read()
if not ret: break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame_kecil = cv2.resize(gray, (128, 128)) # Dikecilkan langsung
gambar_dikumpulkan.append(frame_kecil)
# [BARU] Simpan langsung ke folder, agar Anda bisa lihat filenya!
simpan_folder = f"hasil_1_ekstrak_asli/user_{user_id}"
os.makedirs(simpan_folder, exist_ok=True)
# Menulis foto ke harddisk
cv2.imwrite(f"{simpan_folder}/frame_{len(gambar_dikumpulkan)}.jpg", frame_kecil)
cap.release()
kumpulan_gambar_mentah[user_id] = gambar_dikumpulkan
print(f" 👤 User {user_id}: Berhasil mengambil {len(gambar_dikumpulkan)} gambar utuh.")
# %% [markdown]
# ## 📸 Mari Kita Intip Dulu Gambarnya!
# Berikut adalah bagaimana rupa 1 gambar contoh dari video (karena tanpa proses
# crop/potong letak wajah, gambarnya sangat lebar dengan background-nya dominan)
# %%
# Tampilkan 4 user pertama
fig, axes = plt.subplots(1, 4, figsize=(15, 4))
user_terurut = list(kumpulan_gambar_mentah.keys())
for i, ax in enumerate(axes):
if i < len(user_terurut):
contoh_user = user_terurut[i]
contoh_foto = kumpulan_gambar_mentah[contoh_user][10] # Ambil frame ke 10
ax.imshow(contoh_foto, cmap='gray')
ax.set_title(f"Frame Asli User {contoh_user}", fontsize=12, fontweight='bold')
ax.axis('off')
plt.tight_layout()
plt.show()
# %% [markdown]
# ## Tahap 3: Fungsi Pola Jadi Angka (Algoritma LBP) 🔢
# %%
def ubah_wajah_jadi_angka_lbp(gambar):
"""
1. Filter CLAHE: Terangkan bagian wajah yg kena bayangan/gelap.
2. LBP (Radius 1): Sandi tekstur dengan membandingkan 8 titik berdekatan.
3. Jadikan histogram.
"""
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
gambar_jelas = clahe.apply(gambar)
tinggi, lebar = gambar_jelas.shape
pola_lbp = np.zeros((tinggi, lebar), dtype=np.uint8)
for y in range(1, tinggi-1):
for x in range(1, lebar-1):
pusat = gambar_jelas[y, x]
biner = 0
biner |= (gambar_jelas[y-1, x-1] >= pusat) << 7
biner |= (gambar_jelas[y-1, x] >= pusat) << 6
biner |= (gambar_jelas[y-1, x+1] >= pusat) << 5
biner |= (gambar_jelas[y, x+1] >= pusat) << 4
biner |= (gambar_jelas[y+1, x+1] >= pusat) << 3
biner |= (gambar_jelas[y+1, x] >= pusat) << 2
biner |= (gambar_jelas[y+1, x-1] >= pusat) << 1
biner |= (gambar_jelas[y, x-1] >= pusat) << 0
pola_lbp[y, x] = biner
histogram,_ = np.histogram(pola_lbp, bins=256, range=(0, 256), density=True)
# Supaya bisa dilihat untuk chart, saya return pola_lbp nya juga (Tapi histogram yg utama!)
return histogram, gambar_jelas, pola_lbp
print("✅ Fungsi ekstraksi LBP sudah dibuat!")
# %% [markdown]
# ## 📸 Mari Kita Intip BAGAIMANA LBP Bekerja (Proses Sulapnya)
# Apa yang terjadi pada gambar kalau dimasukkan ke fungsi LBP di atas?
# %%
contoh_foto = kumpulan_gambar_mentah[user_terurut[0]][10]
hist, efek_clahe, efek_lbp = ubah_wajah_jadi_angka_lbp(contoh_foto)
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Foto 1: Original
axes[0].imshow(contoh_foto, cmap='gray')
axes[0].set_title("1. Hitam Putih Original", fontsize=12)
# Foto 2: Setelah CLAHE
axes[1].imshow(efek_clahe, cmap='gray')
axes[1].set_title("2. Efek CLAHE (Pinggiran Menonjol)", fontsize=12)
# Foto 3: Hasil Akhir matriks LBP
axes[2].imshow(efek_lbp, cmap='gray')
axes[2].set_title("3. Sandi Tekstur LBP", fontsize=12)
for ax in axes:
ax.axis('off')
plt.tight_layout()
plt.show()
# Kemudian ini histogram dari LBP (Bentuk Akhirnya Berupa Grafik/Angka 256 elemen)
plt.figure(figsize=(12, 3))
plt.plot(hist, color='blue', linewidth=2)
plt.title(f"4. Dan Inilah Histogram/Angka Akhirnya yang Dikirim ke Otak SVM", fontsize=12)
plt.xlabel("Kode LBP (0 - 255)")
plt.ylabel("Berapa Kali Muncul")
plt.fill_between(range(256), hist, color='blue', alpha=0.3)
plt.show()
# %% [markdown]
# ## Tahap 4: Proses Ratusan Gambar Sekaligus 🔄
# %%
X_all = [] # Simpan hasil array angkanya
y_all = [] # Simpan User ID
print("⚙️ Tolong ditunggu, mengolah seluruh gambar menjadi Array Matriks LBP...")
for user_id, daftar_foto in kumpulan_gambar_mentah.items():
counter_foto = 1 # Penghitung nama file
for foto in daftar_foto:
fitur_angka, efek_clahe, efek_lbp = ubah_wajah_jadi_angka_lbp(foto)
# [BARU] Menyimpan foto hasil operasi komputer ke folder!
folder_clahe = f"hasil_2_clahe/user_{user_id}"
folder_lbp = f"hasil_3_lbp/user_{user_id}"
os.makedirs(folder_clahe, exist_ok=True)
os.makedirs(folder_lbp, exist_ok=True)
cv2.imwrite(f"{folder_clahe}/clahe_{counter_foto}.jpg", efek_clahe)
cv2.imwrite(f"{folder_lbp}/lbp_{counter_foto}.jpg", efek_lbp)
counter_foto += 1
X_all.append(fitur_angka)
y_all.append(user_id)
X = np.array(X_all)
y = np.array(y_all)
print(f"✅ Otak buatan memiliki {len(X)} sampel matriks utuh!")
# %% [markdown]
# ## Tahap 5: Belajar (Training 80% Data ke SVM) 🎓
# Mari kita bedah *"Jeroan"* mesin ini! Karena selama ini selesainya terlalu instan/cepat,
# kita akan menyalakan mode `verbose=True` agar mesin melaporkan jurnal perhitungannya ke kita.
# Serta kita gunakan trik `PCA` untuk menekan 256 angka abstrak tadi jadi grafik kordinat 2D (X dan Y)
# yang bisa dilihat mata manusia!
# %%
from sklearn.decomposition import PCA
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"🧠 AI (SVM) mulai menghafal rumus batas untuk {len(X_train)} gambar Training...")
print("=================== ISI KEPALA MESIN (LIBSVM LOG) ===================")
# verbose=True = Tampilkan seluruh hitungan angka kalibrasi matematisnya
ai_model = SVC(kernel='rbf', C=5, verbose=True)
ai_model.fit(X_train_scaled, y_train)
print("=====================================================================")
print(f"✅ Selesai Menghafal!")
# %% [markdown]
# ## 📸 Visualisasi Dimensi Belajar Komputer (PCA)
# Komputer menganalisa grafik dari 256 sudut/dimensi secara bersamaan.
# Manusia hanya mampu melihat 2 Dimensi (kiri-kanan X, atas-bawah Y).
# Berikut adalah bagaimana komputer membuat "wilayah zona wajah" dari setiap User.
# %%
# Paksa padatkan 256 fitur LBP menjadi hanya 2 fitur untuk bisa dipajang
pca = PCA(n_components=2)
X_train_2d = pca.fit_transform(X_train_scaled)
plt.figure(figsize=(10, 6))
colors = ['crimson', 'forestgreen', 'royalblue', 'orange', 'purple', 'black']
user_unik = np.unique(y_train)
for idx, user_vid in enumerate(user_unik):
mask = (y_train == user_vid)
plt.scatter(X_train_2d[mask, 0], X_train_2d[mask, 1],
c=colors[idx % len(colors)], label=f"Zona Wajah {user_vid}",
alpha=0.8, edgecolors='k', s=60)
# Tambahkan Judul & Aksesoris
plt.title("Visualisasi Peta Memori Otak SVM Setelah Training", fontsize=14, fontweight='bold')
plt.xlabel("Dimensi Pola Pipi & Mata (Disederhanakan)")
plt.ylabel("Dimensi Pola Rahang & Bibir (Disederhanakan)")
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
# Bikin kotak merah batas agar terlihat seperti area pemisah (Decision Boundary)
plt.axvline(x=0, color='grey', linewidth=1)
plt.axhline(y=0, color='grey', linewidth=1)
plt.show()
# %% [markdown]
# ## Tahap 6: Ujian AI (Testing Ulang ke 20% Data) 📝
# %%
jawaban_ai = ai_model.predict(X_test_scaled)
akurasi = accuracy_score(y_test, jawaban_ai)
# Supaya menarik, kita tampilkan pakai Plot!
# Kita hitung dulu akurasi per User
user_unik = np.unique(y_test)
akurasi_tiap_user = {}
for u in user_unik:
benar = sum(1 for asli, tebak in zip(y_test, jawaban_ai) if asli == u and tebak == u)
total_ujian = sum(1 for asli in y_test if asli == u)
akurasi_tiap_user[u] = (benar / total_ujian) * 100 if total_ujian > 0 else 0
plt.figure(figsize=(8, 4))
bars = plt.bar(akurasi_tiap_user.keys(), akurasi_tiap_user.values(), color=['red','green','blue','orange'])
plt.title(f"Akurasi Test Keseluruhan: {akurasi*100:.2f} %", fontsize=14, fontweight='bold')
plt.xlabel("ID Pegawai")
plt.ylabel("Akurasi (%)")
plt.ylim(0, 110)
# Tambah text persen di atas tiap batang grafiknya
for bar in bars:
plt.text(bar.get_x() + bar.get_width()/2 - 0.15, bar.get_height() + 2,
f"{bar.get_height():.0f}%", fontweight='bold')
plt.show()
print("\nDetail Singkat (10 Tebakan Pertama):")
for i in range(min(10, len(y_test))):
kunci = y_test[i]
tebakan = jawaban_ai[i]
if kunci == tebakan:
print(f"[{i+1:02d}] Asli: User {kunci:<3} \t|Tebakan: User {tebakan:<3}")
else:
print(f"[{i+1:02d}] Asli: User {kunci:<3} \t|Tebakan: User {tebakan:<3}")