Face Liveness Challenge
On this page
- Sekilas
- Yang dikerjakannya
- A. Memandu seseorang melewati tiga pose
- B. Menangkap satu frame tajam per pose
- C. Menolak tangkapan yang tak berguna
- D. Menjaga bukti tetap privat dan menyerahkannya ke peninjau
- E. Bisa ditaruh di halaman mana pun sebagai widget
- Antarmuka
- Standar
- Di dalamnya
- Keputusan rancangan
- Spesifikasi
- Terkait
- Istilah
Face Liveness Challenge adalah service Python yang meminta orang di depan kamera membuktikan bahwa ia ada di sana, hidup, pada saat itu: hadap ke kamera, menoleh ke kanan, menoleh ke kiri. Ia mengikuti setiap gerakan frame demi frame, menangkap satu gambar tajam di tiap pose, lalu menyimpan ketiga gambar itu sebagai bukti untuk peninjau manusia. Ia tidak mengenali wajah dan tidak membandingkannya dengan apa pun — tugasnya menunjukkan bahwa orang hidup menjalankan tantangan itu, bukan siapa orangnya.
Saya membangunnya untuk aplikasi yang butuh kehadiran seseorang sebelum tindakan penting, seperti menandatangani dokumen, dan yang tetap menempatkan manusia dalam keputusannya. Satu foto yang di-upload tak membuktikan apa-apa: bisa saja itu foto dari foto. Urutan gerakan singkat yang dipandu, dilakukan langsung dan direkam sebagai tiga frame, memberi peninjau bukti yang tak bisa diberikan foto diam.
Sekilas
| Bahasa & framework | Python, dengan FastAPI, MediaPipe untuk landmark wajah, dan OpenCV |
|---|---|
| Arsitektur | Transaction Script Service |
| Versi | 2.15.1 |
| Status | Stabil, berjalan di production |
| Bentuk | Service HTTP dan WebSocket dengan widget kamera yang bisa disematkan, dikirim sebagai satu executable hasil bundel |
| Kode sumber | Privat |
Yang dikerjakannya
A. Memandu seseorang melewati tiga pose
Tantangannya tiga langkah dengan urutan tetap: hadap ke kamera, menoleh ke kanan, menoleh ke kiri. Untuk setiap frame, service memberi tahu widget pose apa yang dilihatnya, pose apa yang diharapkan, dan sejauh mana langkah yang sedang berjalan, sehingga orang itu selalu tahu apa yang harus dilakukan berikutnya.
B. Menangkap satu frame tajam per pose
Pose harus ditahan sejenak lalu dijaga tetap diam sebelum frame-nya disimpan, sehingga buktinya tak pernah berupa gambar buram yang tertangkap di tengah gerakan. Bergerak terlalu cepat menunda tangkapan, tak mengulang langkah dari awal.
C. Menolak tangkapan yang tak berguna
Di langkah hadap depan, detektor masker memeriksa wajah sebelum frame disimpan, karena peninjau tak bisa menilai wajah yang tertutup masker.
D. Menjaga bukti tetap privat dan menyerahkannya ke peninjau
Ketiga frame masuk ke bucket storage privat lewat link upload berumur pendek. Ketika peninjau membuka kasusnya, aplikasi meminta service link baca berumur pendek ke tiga frame terbaru milik orang itu.
E. Bisa ditaruh di halaman mana pun sebagai widget
Kamera, panduan, dan koneksinya tinggal dalam satu halaman mandiri yang disematkan aplikasi di dalam frame. Widget memberi tahu halaman induk ketika tantangan berhasil, kedaluwarsa, atau gagal, dan halaman induk cukup membuka dan menutupnya.
Antarmuka
| Kemampuan | Kegunaan |
|---|---|
| Memulai tantangan | untuk user yang sedang login; mengembalikan kode sesi, daftar pose, dan waktu kedaluwarsa |
| Aliran kamera | frame masuk, panduan keluar, sukses di akhir |
| Status | apakah tantangan sudah selesai |
| Widget kamera | untuk disematkan di halaman aplikasi |
| Readiness | penyimpan sesi dan storage |
Untuk setiap frame yang diterimanya, service menjawab dengan status yang diubah widget menjadi panduan:
Standar
| Standar | Yang dijaminnya di sini |
|---|---|
| WebSocket (RFC 6455) | satu koneksi terbuka untuk frame dan panduan, dengan latensi cukup rendah untuk mengikuti kepala yang bergerak |
| JSON Web Token dengan RS256, key dari endpoint JWKS | tantangan milik user yang sedang login, diperiksa issuer, audience, dan client-nya |
| Presigned URL, AWS Signature Version 4 | bukti ditulis dan dibaca lewat link berumur pendek, tanpa kredensial storage di dalam service |
| W3C Trace Context dan OpenTelemetry | satu tantangan tampil di trace aplikasi yang memulainya |
Di dalamnya
Service ini berbentuk Transaction Script Service. Endpoint WebSocket-nya adalah satu prosedur yang membaca setiap langkah tantangan secara berurutan — periksa token, muat sesi, ikuti frame, upload tangkapan, catat hasilnya — dan kerja tiap langkah tinggal di module-nya sendiri: mesin wajah beserta pelacak tantangannya, detektor masker, autentikasi, penyimpan sesi, client storage, dan telemetry. Ia dibundel menjadi satu executable bersama model-modelnya.
Keputusan rancangan
Liveness, bukan identitas, dan manusia yang memutuskan. Service menjawab satu pertanyaan — apakah orang hidup baru saja menjalankan tantangan ini — dan menyerahkan keputusan tentang siapa orangnya kepada peninjau yang melihat ketiga frame. Dengan begitu service jujur tentang apa yang bisa dibuktikannya, dan penilaian tetap berada di tempat tanggung jawab memang berada. Ia menghasilkan bukti, bukan vonis, dan log hanya membawa referensi ke frame yang tersimpan, tak pernah gambarnya.
Tiga pose menggantikan pola sembilan titik. Tantangan pertama meminta orang menggerakkan ujung hidung melewati tiga dari sembilan titik di layar, dengan jalur acak seperti pola buka kunci ponsel, dan grid-nya bahkan harus dikecilkan supaya wajah tetap di dalam panduan. Orang menganggapnya tak intuitif, jadi saya menggantinya dengan tiga pose kepala yang langsung dipahami siapa pun. Struktur pelacaknya — tahan, tenang, maju — dan kontrak output-nya sengaja saya pertahankan, dan hanya pemeriksaan apakah pose saat ini benar yang diganti, sehingga semua yang sudah terbukti soal tangkapan yang tajam ikut terbawa.
Setiap pemeriksaan harus membuktikan tempatnya. Seiring waktu, tantangan itu juga mewajibkan mata terbuka, tanpa kacamata hitam, dan senyum di langkah hadap depan. Pengukuran dengan model landmark yang asli menunjukkan bahwa pemeriksaan senyumlah sumber kesulitannya: senyum lebar mendapat skor 0,787 terhadap ambang 0,35, tapi senyum wajar dengan bibir tertutup jauh di bawahnya, sehingga orang terpaksa membuka mulut supaya lolos. Dua penjelasan saya sendiri gugur saat diuji, dan itu mencegah saya melonggarkan bagian yang salah. Saya membuang pemeriksaan senyum, mata, dan kacamata, dan hanya mempertahankan pemeriksaan masker — satu-satunya yang menjaga kemampuan peninjau melihat wajah — lalu mengganti nama pose pertama dari "senyum" menjadi "depan", karena nama yang berbohong lebih berbahaya daripada fitur yang hilang.
Arah ditentukan oleh kamera sungguhan, dan tangkapan diambil sesudah bukti diam. Arah menoleh diestimasi dari letak ujung hidung di antara kedua tepi wajah, dari satu inferensi landmark per frame. Versi pertama membalik kiri dan kanan: penalaran dari frame yang dicerminkan ternyata keliru, dan hanya uji dengan kamera sungguhan yang bisa memastikan tanda mana yang berarti kanan bagi orang itu — kodenya kini menyatakan hal itu. Saya menurunkan ambang menoleh bertahap karena orang merasa pose-nya sulit dicapai. Frame baru disimpan setelah pose ditahan lalu tetap diam, dan bunyi shutter hanya berbunyi untuk frame itu; dulu bunyi itu terdengar saat frame diambil sementara, sehingga orang mendengar tiga shutter padahal baru satu langkah yang tersimpan.
Diam diukur sebagai kecepatan, bukan sebagai jarak dari titik awal. Satu rilis membuat pose pertama mustahil diselesaikan: ia membandingkan setiap frame dengan posisi di awal tangkapan, sehingga getaran kepala yang biasa menumpuk sampai melewati batas. Simulasi menunjukkan bahwa dengan getaran dua puluh piksel, tak satu tangkapan pun terjadi. Pelacak kini mengukur seberapa cepat hidung atau tolehan bergerak, menghaluskannya, dan cukup menunggu bila terlalu cepat, tanpa mengulang langkah dari awal.
Widget memandu mata dan telinga. Halaman kamera berdiri sendiri, karena logika yang terbelah antara halaman induk dan frame membuat keduanya tak lagi seirama. Oval panduan meminta orang mendekat atau mundur, panah menunjukkan arah tolehan, dan bunyi — tik selama pose salah, shutter ketika frame disimpan, denting di akhir — membantu orang yang sedang memalingkan wajah dari layar. Panduan digambar begitu tantangan dimulai, tanpa menunggu koneksi, dan frame yang tertangkap tak pernah ditunjukkan kembali ke orangnya: frame itu bukti untuk peninjau, bukan pratinjau.
Bukti tak boleh hilang diam-diam. Ketiga frame di-upload lewat satu permintaan untuk tiga link upload ke bucket privat, masing-masing dengan content type yang eksplisit, karena tanpa itu storage mencatatnya sebagai data form. Bila satu upload gagal, yang sudah tertulis dihapus supaya tak ada set yang setengah jadi. Saat link baca untuk peninjau diminta, frame yang tak bisa ditandatangani dilaporkan sebagai hilang dan kegagalan total sebagai error; versi sebelumnya cukup mengembalikan daftar yang lebih pendek, dan untuk bukti, itu lebih buruk daripada kegagalan yang terlihat.
Tantangan milik orang yang memulainya. Setiap request diautentikasi dengan token yang ditandatangani identity service milik aplikasi, yang key-nya diambil dan di-cache, lalu diperiksa issuer, audience, dan client-nya. Token di koneksi kamera diperiksa sebelum koneksi diterima, dan orang di koneksi itu harus sama dengan orang yang memulai tantangan.
Telemetry yang bisa dipercaya lengkap. Telemetry pindah dari menulis langsung ke Elasticsearch menjadi OpenTelemetry, diinstrumentasi lewat kode karena executable hasil bundel tak punya launcher. Berbulan-bulan, panggilan ke storage tak pernah muncul di trace: di dalam bundel, metadata paket HTTP client-nya hilang, sehingga instrumentasinya mematikan diri tanpa sepatah kata, sampai build-nya dibuat membawa metadata itu. Histogram durasi lenyap dengan cara diam yang sama, sampai metrics pindah ke delta temporality. Service kini mencatat berapa tantangan yang dimulai, berhasil, kedaluwarsa, atau gagal, berapa lama berlangsungnya, dan kenapa pose gagal, memakai sekumpulan kecil alasan yang tetap.
Spesifikasi
| Masukan | Token user yang sedang login; aliran kamera langsung berupa frame JPEG dari widget |
|---|---|
| Hasil | Panduan untuk setiap frame; tiga tangkapan PNG di storage privat; penanda selesai; link baca untuk peninjau |
| Tantangan | Depan, kanan, kiri; tiap pose ditahan, lalu dijaga diam sebelum ditangkap; ambang menoleh bisa diatur |
| Pemeriksaan | Satu inferensi landmark wajah per frame; deteksi masker pada tangkapan hadap depan |
| Sesi | Kode untuk satu user dengan umur pendek, disimpan di Redis |
| Observability | Trace, log, dan metrics OpenTelemetry lewat OTLP, termasuk hasil tantangan dan alasan pose gagal |
| Berjalan sebagai | Executable hasil bundel beserta model-modelnya, dalam image container multi-stage, di samping instance Redis |
Terkait
Service storage yang menerbitkan link tempat ketiga frame ditulis dan dibaca.
Ke mana trace, log, dan metrics service ini pergi setelah meninggalkannya.
Istilah
- Liveness
- Bukti bahwa orang sungguhan sedang hadir di depan kamera, bukan foto atau rekaman; ia tak mengatakan apa pun tentang siapa orangnya.
- Face recognition
- Mengenali atau mencocokkan seseorang dengan membandingkan wajahnya dengan wajah yang tersimpan; service ini tidak melakukannya.
- Landmark wajah
- Titik-titik yang ditempatkan model pada wajah — ujung hidung, tepi wajah, mata — untuk mengestimasi pose dan gerakan.
- Yaw
- Seberapa jauh kepala menoleh ke kiri atau ke kanan.
- WebSocket
- Koneksi yang tetap terbuka sehingga browser dan server bisa saling mengirim pesan terus-menerus.
Technologies covered
- Python
- FastAPI
- OpenTelemetry
- WebSocket
- MediaPipe