Mode belajar: tulis kode sendiri. Copy/cut, seleksi teks dan klik kanan dibatasi pada halaman; pembatasan UI bukan jaminan anti-penyalinan mutlak.
1. Tujuan
- Memuat cascade wajah dan mata serta memeriksa keberhasilan pemuatannya.
- Mendeteksi dan menghitung wajah pada foto yang diunggah serta menggambar kotak hasil.
- Membandingkan hasil dua nilai minNeighbors dengan hitungan manual.
- Menjelaskan pelacakan centroid objek pada rangkaian frame tanpa menganggapnya pengenalan identitas.
Prasyarat: Mampu membaca citra, mengubah BGR ke grayscale/RGB, melakukan cropping ROI, dan menggunakan perulangan Python.
2. Materi Pembahasan

2.1 Deteksi, penghitungan, dan identitas adalah tugas berbeda
Deteksi menjawab di mana kandidat objek berada dalam gambar. Pada praktikum ini objek pertama adalah wajah, dan hasilnya berupa kotak dengan posisi x, y, lebar w, serta tinggi h. Penghitungan wajah dilakukan setelah deteksi dengan menghitung kotak yang dikembalikan. Angka itu bukan bukti jumlah orang sebenarnya: wajah kecil dapat terlewat, sedangkan tekstur tertentu dapat salah dianggap wajah. Karena itu kita membandingkan hasil dengan pengamatan manual, bukan hanya memuji tampilan kotaknya.
Pengenalan identitas menjawab siapa orangnya, membutuhkan pendekatan dan pengamanan berbeda, serta tidak dibahas di sini. Deteksi mata hanya mencari lokasi kandidat mata pada area wajah. Dua kotak mata bukan syarat wajib bagi sebuah wajah; kacamata, arah pandang, bayangan, dan penutupan sebagian wajah dapat mengubah hasil. Tuliskan jumlah kandidat yang terlihat dan bedakan dari jumlah objek menurut manusia.
2.2 Cara kerja Haar cascade secara intuitif
Bayangkan penyaring bertingkat yang memeriksa perbedaan terang dan gelap pada banyak jendela gambar. Jendela yang tidak cocok ditolak lebih awal, sedangkan kandidat yang menjanjikan diperiksa lebih lanjut. Model cascade telah dilatih sebelumnya; kita memuat berkas XML yang disediakan OpenCV, bukan melatih ulang model dari foto praktikum. Wajah frontal dan mata menggunakan berkas berbeda sehingga keduanya tidak bisa dipertukarkan. Ringkasan API pemuatan dan deteksi tersedia pada referensi OpenCV.
Sebelum deteksi, citra diubah menjadi grayscale sehingga penyaring bekerja pada intensitas, bukan tiga kanal warna. Pemeriksaan empty() memastikan berkas model berhasil dibaca. Hasil grayscale tidak menggantikan foto berwarna untuk presentasi: kotak digambar pada salinan BGR, kemudian diubah ke RGB untuk Matplotlib. Menyimpan salinan membantu kita membandingkan masukan asli dan anotasi tanpa mengubah sumber eksperimen.
2.3 Skala pencarian, tetangga, dan ROI
scaleFactor menentukan perubahan skala antar tingkat pencarian; nilai harus lebih besar dari satu. Perubahan yang lebih kecil membuat pencarian lebih rapat tetapi dapat memperpanjang waktu kerja. minNeighbors mengatur dukungan kandidat agar dipertahankan, sedangkan minSize membatasi ukuran minimum objek dalam piksel. Menaikkan minNeighbors cenderung mengurangi kandidat lemah, tetapi bukan jaminan setiap hasil lebih benar. Bandingkan dua pengaturan pada foto yang sama agar faktor lain tidak ikut berubah.
Setelah kotak wajah diperoleh, kita memotong grayscale menjadi ROI wajah lalu mendeteksi mata di dalamnya. Cara ini membatasi pencarian dan menunjukkan hubungan koordinat lokal dengan global. Kotak mata berkoordinat ex, ey harus digambar pada x+ex, y+ey. Hindari kesimpulan bahwa semua kotak di ROI adalah mata sungguhan; tetap periksa visual, terutama ketika gambar buram atau wajah miring.
2.4 Pelacakan objek sederhana melalui centroid
Pelacakan memperhatikan perubahan posisi antarframe. Demo opsional menggunakan benda hijau, mengubah frame menjadi HSV, membuat mask warna, lalu mengambil kontur terbesar yang cukup luas. Centroid dihitung dari moments dan posisi yang berurutan digambar sebagai jejak. Ini mengajarkan representasi posisi, segmentasi, dan urutan waktu tanpa model pengenalan orang. Jika tidak ada kontur valid, jejak diputus agar garis tidak menyambung melalui periode objek hilang.
Asumsinya kuat: hanya satu benda target dominan, warna cukup berbeda dari latar, dan pencahayaan relatif stabil. Benda hijau lain dapat mengambil alih kontur terbesar; bayangan dapat mengubah mask. Metode ini tidak mempertahankan identitas target dan tidak cocok dianggap pelacak multiobjek. Hasilnya adalah ringkasan rangkaian tangkapan, bukan kecepatan real-time atau bukti target yang sama selalu diikuti.
2.5 Webcam Colab dan penggunaan foto yang bertanggung jawab
Runtime Colab berjalan jauh dari komputer pengguna. Jalur utama praktikum adalah unggah foto; cv2.VideoCapture(0) tidak dipakai untuk kamera laptop. Jalur opsional meminta izin melalui getUserMedia pada browser dan mengirim JPEG melalui output.eval_js. Pengambilan dibatasi paling banyak dua belas frame dan sepuluh detik setelah kamera siap. Track kamera dihentikan dalam finally; jika izin terlambat setelah timeout, track yang baru muncul juga segera dihentikan.
Gunakan foto sendiri atau foto dengan izin yang jelas, khususnya untuk wajah orang lain. Hindari mengunggah dokumen identitas, anak tanpa izin wali, atau gambar sensitif ke notebook yang dibagikan. Variasikan pose dan cahaya untuk mempelajari kegagalan, bukan untuk menyimpulkan sifat seseorang. Jangan mengklaim hasil adil pada semua kelompok hanya dari beberapa foto. Catat keterbatasan model dan hapus tangkapan yang tidak diperlukan sebelum membagikan link Viewer.
Video pembelajaran sesuai bab
Haar Cascade Object Detection Face & Eye - OpenCV with Python for Image and Video Analysis 16 — sentdex.
Fokus saat menonton: Amati ROI wajah dan mata serta hasil Haar cascade. Kotak deteksi tidak membuktikan identitas seseorang.
Referensi dan sumber ilustrasi
3. Praktikum Google Colab
Buat notebook baru, simpan pada Drive milik sendiri, lalu isi identitas. Gunakan sel kode terpisah untuk setiap langkah dan jalankan berurutan dengan Shift + Enter. Tidak perlu mengunduh notebook contoh atau memasang Python lokal.
# Materi 07 — Pengolahan Citra
Nama: [Nama lengkap mahasiswa]
NPM: [NPM mahasiswa]
Kelas: [Kelas, jika diperlukan]- Buat notebook Colab, jalankan setup 0 sebelum import, lalu ketik sel utama 1–4 berurutan setelah identitas Nama/NPM.
- Unggah foto berizin, tampilkan citra, dan catat jumlah wajah menurut pengamatan manual.
- Tampilkan kotak wajah/mata dan jumlah kandidat hasil deteksi.
- Bandingkan minNeighbors=3 dan 7; jelaskan kotak keliru atau wajah yang terlewat.
- Jika memilih webcam, jalankan sel 5 dan gunakan deteksi_wajah pada frames_webcam[-1] untuk menghitung wajah; sel 6 adalah pengayaan pelacakan objek.
- Tulis analisis dalam Markdown dan periksa akses link Colab sebagai Viewer.
Sel 1 — 0. Setup OpenCV 4.x
Jalankan sebelum import cv2: baseline praktikum memakai OpenCV 4.x karena API CascadeClassifier tidak tersedia pada sebagian instalasi 5.x. Jika cv2 sudah diimport, restart runtime melalui menu Runtime, lalu jalankan sel dari awal. Jangan menaikkan versi NumPy secara terpisah.
%pip install "opencv-python-headless>=4.10,<5"Sel 2 — 1. Import dan model cascade
Jalankan sebelum mengunggah foto. Model bawaan harus terbaca; pesan kesalahan menghentikan praktik.
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files, output
from IPython.display import display, Javascript
import base64
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
if face_cascade.empty() or eye_cascade.empty():
raise RuntimeError('Berkas cascade tidak berhasil dimuat.')
print('Cascade wajah dan mata siap.')Sel 3 — 2. Unggah satu foto
Gunakan JPG/PNG maksimal 10 MB dan sisi maksimal 6000 piksel. Foto besar diperkecil untuk demo; hitungan manual mengikuti citra yang ditampilkan.
uploaded = files.upload()
if len(uploaded) != 1:
raise ValueError('Unggah tepat satu foto JPG/PNG.')
nama, data = next(iter(uploaded.items()))
if not nama.lower().endswith(('.jpg', '.jpeg', '.png')) or len(data) > 10 * 1024 * 1024:
raise ValueError('Format harus JPG/PNG dan ukuran maksimal 10 MB.')
img = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)
if img is None:
raise ValueError('Isi berkas tidak dapat dibaca sebagai citra.')
h, w = img.shape[:2]
if max(h, w) > 6000:
raise ValueError('Sisi gambar maksimal 6000 piksel.')
scale = min(1.0, 1200 / max(h, w))
if scale < 1:
img = cv2.resize(img, (round(w * scale), round(h * scale)))
plt.figure(figsize=(10, 6))
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()Sel 4 — 3. Deteksi wajah dan mata
Kotak mata dikembalikan ke koordinat citra penuh. Jumlah wajah ditampilkan, termasuk jika hasilnya nol.
def deteksi_wajah(citra, tetangga=5):
gray = cv2.cvtColor(citra, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1,
minNeighbors=tetangga, minSize=(30, 30))
hasil = citra.copy()
for x, y, w, h in faces:
cv2.rectangle(hasil, (x, y), (x+w, y+h), (0, 255, 0), 2)
roi = gray[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(roi, scaleFactor=1.1,
minNeighbors=5, minSize=(10, 10))
for ex, ey, ew, eh in eyes:
cv2.rectangle(hasil, (x+ex, y+ey), (x+ex+ew, y+ey+eh), (255, 0, 0), 2)
cv2.putText(hasil, f'Wajah: {len(faces)}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)
return hasil, faces
hasil, faces = deteksi_wajah(img)
print('Jumlah kandidat wajah:', len(faces))
plt.figure(figsize=(10, 6))
plt.imshow(cv2.cvtColor(hasil, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()Sel 5 — 4. Bandingkan parameter
Bandingkan kandidat dengan hitungan manual pada gambar yang sama. Jangan memilih parameter hanya karena jumlahnya lebih tinggi.
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
for ax, tetangga in zip(axes, [3, 7]):
anotasi, kandidat = deteksi_wajah(img, tetangga)
ax.imshow(cv2.cvtColor(anotasi, cv2.COLOR_BGR2RGB))
ax.set_title(f'minNeighbors={tetangga}; kandidat={len(kandidat)}')
ax.axis('off')
plt.show()Sel 6 — 5. Opsional: tangkapan webcam browser
Lewati bila izin kamera tidak tersedia. Jalankan di Colab melalui browser; tangkapan hanya 12 frame/10 detik setelah kamera siap, bukan video real-time.
display(Javascript(r"""
window.captureLimited = async function() {
const video = document.createElement('video');
video.muted = true; video.playsInline = true;
const canvas = document.createElement('canvas');
let stream = null, timer = null, cancelled = false;
document.body.appendChild(video);
try {
const request = navigator.mediaDevices.getUserMedia({video: true, audio: false});
request.then(s => { if (cancelled) s.getTracks().forEach(t => t.stop()); }, () => {});
stream = await Promise.race([request, new Promise((_, reject) => {
timer = setTimeout(() => {cancelled = true; reject(new Error('Izin kamera timeout'));}, 15000);
})]);
clearTimeout(timer);
video.srcObject = stream;
await Promise.race([video.play(), new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('Kamera tidak siap')), 5000);
})]);
clearTimeout(timer);
canvas.width = 640; canvas.height = 480;
const ctx = canvas.getContext('2d'), frames = [], start = performance.now();
while (frames.length < 12 && performance.now() - start < 10000) {
ctx.drawImage(video, 0, 0, 640, 480);
frames.push(canvas.toDataURL('image/jpeg', 0.8));
await new Promise(r => setTimeout(r, 400));
}
return frames;
} finally {
cancelled = true; clearTimeout(timer);
if (stream) stream.getTracks().forEach(t => t.stop());
video.srcObject = null; video.remove(); canvas.remove();
}
};
"""))
encoded_frames = output.eval_js('captureLimited()')
frames_webcam = []
for item in encoded_frames:
raw = base64.b64decode(item.split(',', 1)[1])
frame = cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_COLOR)
if frame is None:
raise ValueError('Tangkapan kamera gagal didekode.')
frames_webcam.append(frame)
if not frames_webcam:
raise RuntimeError('Tidak ada frame webcam.')
print('Frame diterima:', len(frames_webcam))
hasil_webcam, faces_webcam = deteksi_wajah(frames_webcam[-1])
print('Jumlah kandidat wajah pada tangkapan terakhir:', len(faces_webcam))
plt.figure(figsize=(8, 5))
plt.imshow(cv2.cvtColor(hasil_webcam, cv2.COLOR_BGR2RGB))
plt.axis('off'); plt.show()Sel 7 — 6. Opsional: ikuti benda hijau
Gerakkan satu benda hijau saat tangkapan. Kontur terbesar adalah target sementara, bukan identitas tetap; sesuaikan batas HSV dengan cahaya.
jejak = []
anotasi_frames = []
for frame in frames_webcam:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, np.array([35, 60, 50]), np.array([85, 255, 255]))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((5, 5), np.uint8))
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
hasil_track = frame.copy()
kandidat = max(contours, key=cv2.contourArea) if contours else None
if kandidat is not None and cv2.contourArea(kandidat) > 300:
M = cv2.moments(kandidat)
if M['m00'] > 0:
pusat = (int(M['m10']/M['m00']), int(M['m01']/M['m00']))
jejak.append(pusat)
cv2.circle(hasil_track, pusat, 7, (0, 0, 255), -1)
else:
jejak = []
for a, b in zip(jejak, jejak[1:]):
cv2.line(hasil_track, a, b, (0, 255, 255), 2)
anotasi_frames.append(hasil_track)
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for ax, indeks in zip(axes, np.linspace(0, len(anotasi_frames)-1, 3, dtype=int)):
ax.imshow(cv2.cvtColor(anotasi_frames[indeks], cv2.COLOR_BGR2RGB))
ax.set_title(f'Tangkapan {indeks+1}'); ax.axis('off')
plt.show()Jika terjadi error
- Tidak ada wajah terdeteksi.
- Periksa citra, ukuran wajah, pose frontal, dan pencahayaan; ubah satu parameter lalu bandingkan dengan hitungan manual.
- Kotak mata bergeser keluar wajah.
- Tambahkan offset x,y wajah pada koordinat lokal ROI; gunakan fungsi yang disediakan.
- Kamera ditolak atau eval_js gagal.
- Periksa izin browser dan gunakan upload foto sebagai jalur utama. Tidak perlu memaksa akses webcam.
- Objek hijau lain mengambil alih jejak.
- Gunakan latar berbeda warna dan satu objek dominan; jelaskan keterbatasan metode kontur terbesar.
Runtime Colab dan file upload bersifat sementara. Simpan output penting pada notebook/Drive sebelum sesi berakhir. Jika runtime direset, jalankan ulang sel dari awal. Gunakan media berizin dan hindari unggahan data sensitif tanpa izin.
4. Tugas 7
Mini project mendeteksi wajah pada gambar atau tangkapan webcam dan menghitung jumlah kandidat wajah. Kumpulkan hanya satu link Google Colab dengan output serta analisis terlihat dan akses Viewer.
Ketentuan pengerjaan
- Nama/NPM pada sel Markdown teratas.
- Gunakan minimal dua foto berizin dengan kondisi berbeda, atau dua tangkapan webcam; unggah foto adalah jalur yang direkomendasikan.
- Tampilkan citra masukan, kotak wajah, kandidat mata, jumlah kandidat wajah, dan jumlah wajah menurut hitungan manual.
- Bandingkan dua nilai minNeighbors pada masukan yang sama dan identifikasi false positive/false negative secara visual.
- Tulis analisis kegagalan, batas penghitungan, dan izin penggunaan foto; pelacakan warna bersifat pengayaan opsional.
- Tidak mengumpulkan gambar, screenshot, ZIP, PDF, atau notebook terpisah.
Pertanyaan analisis
- Apakah jumlah kandidat sama dengan hitungan manual? Jelaskan selisih pada setiap masukan.
- Bagaimana perubahan minNeighbors memengaruhi kandidat?
- Mengapa hitungan wajah bukan jumlah identitas unik?
- Kondisi apa yang menyebabkan mata atau wajah terlewat, dan bagaimana privasi foto dijaga?
# Materi 07 — Pengolahan Citra
Nama: [Nama lengkap mahasiswa]
NPM: [NPM mahasiswa]
Kelas: [Kelas, jika diperlukan]Pengumpulan
Kumpulkan satu link Google Colab saja. Nama dan NPM wajib berada pada sel Markdown paling atas. Sertakan kode, output yang tersimpan/terlihat serta analisis di notebook; tidak perlu mengumpulkan file .ipynb, ZIP, PDF atau screenshot terpisah.
- Jalankan sel dengan konfigurasi valid dan simpan notebook beserta output. Pastikan opsi menghilangkan output saat menyimpan tidak aktif untuk versi pengumpulan.
- Bagikan sebagai Viewer/Pelihat kepada dosen. Gunakan akses link jika diizinkan institusi; bila dibatasi, beri akses khusus akun dosen. Jangan memberi izin edit publik.
- Periksa akses dari akun penerima. Kirim URL notebook Colab, bukan folder Drive atau runtime sementara.
| Aspek | Bobot | Indikator |
|---|---|---|
| Implementasi deteksi dan hitungan | 40% | Cascade dimuat, koordinat benar, wajah dihitung, dan notebook berjalan. |
| Eksperimen dan keluaran | 25% | Dua kondisi masukan, dua parameter, anotasi, dan hitungan manual terlihat. |
| Analisis dan etika | 25% | Kesalahan, keterbatasan identitas, serta izin foto dijelaskan berdasarkan output. |
| Keterbacaan notebook | 10% | Identitas, urutan sel, analisis Markdown, dan satu link Viewer lengkap. |