Face Detection dan Face Recognition di Angular dengan face-api.js (Tensorflow)

Membuat Face Detection dan Face Recognition di Angular dengan face-api.js (Tensorflow)

Pada artikel kali ini, kita akan belajar cara membuat fitur Face Detection dan Face Recognition menggunakan Face-API.js di Angular. Fitur-fitur yang akan dibuat meliputi:

  1. Pendeteksi senyum (smile detection) 
  2. Deteksi berkedip (blink detection) 
  3. Deteksi mulut terbuka/tertutup 
  4. Deteksi pose kepala (head pose) 
  5. Estimasi umur dan jenis kelamin 
Kita akan membuat sebuah komponen bernama FaceApiComponent untuk menangani seluruh proses ini.

Instalasi Face-API.js

Untuk memulai, instal Face-API.js di proyek Angular Anda:

npm install face-api.js

Setelah itu, buatlah sebuah komponen baru bernama FaceApiComponent. Komponen ini akan menangani semua fungsi terkait deteksi wajah dan menampilkan hasilnya di layar.

Persiapan Model

Face-API.js membutuhkan model pra-latih untuk mendeteksi wajah, landmark, ekspresi, dll. Pastikan Anda telah mengunduh model-model berikut dan menyimpannya di folder src/assets/models:

  • tinyFaceDetector 
  • faceLandmark68Net 
  • faceExpressionNet 
  • ageGenderNet 

Model ini dapat diunduh dari repository resmi Face-API.js.

Membuat Komponen Face-API

Gunakan perintah Angular CLI untuk membuat komponen:

ng generate component face-api --standalone

Salin kode berikut pada komponen yang sudah di generate tadi.

1. face-api.component.ts

Berikut adalah kode TypeScript untuk komponen FaceApiComponent:


import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import * as faceapi from 'face-api.js';

@Component({
  selector: 'app-face-api',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './face-api.component.html',
  styleUrls: ['./face-api.component.scss'],
})
export class FaceApiComponent implements OnInit {
  @ViewChild('video') videoElement!: ElementRef<HTMLVideoElement>;
  @ViewChild('canvas') canvasElement!: ElementRef<HTMLCanvasElement>;

  smileValue: string = 'No face detected';
  blinkStatus: string = 'Eyes open';
  mouthStatus: string = 'Mouth closed';
  headPose: string = 'Head position normal';
  ageGender: string = 'Age: N/A, Gender: N/A';

  async ngOnInit() {
    await this.loadModels();
    this.startVideo();
  }

  async loadModels() {
    const MODEL_URL = '/assets/models';
    await Promise.all([
      faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
      faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
      faceapi.nets.faceExpressionNet.loadFromUri(MODEL_URL),
      faceapi.nets.ageGenderNet.loadFromUri(MODEL_URL), // For age and gender detection
    ]);
    console.log('Models loaded successfully');
  }

  startVideo() {
    navigator.mediaDevices
      .getUserMedia({ video: true })
      .then((stream) => {
        const video = this.videoElement.nativeElement;
        video.srcObject = stream;

        video.onloadedmetadata = () => {
          video.play();
          video.width = video.videoWidth;
          video.height = video.videoHeight;
          this.onPlay();
        };
      })
      .catch((err) => console.error('Error accessing webcam: ', err));
  }

  async onPlay() {
    const video = this.videoElement.nativeElement;
    const canvas = this.canvasElement.nativeElement;

    video.addEventListener('play', () => {
      const displaySize = {
        width: video.videoWidth,
        height: video.videoHeight,
      };

      faceapi.matchDimensions(canvas, displaySize);

      setInterval(async () => {
        if (video.videoWidth === 0 || video.videoHeight === 0) {
          console.error('Video dimensions are invalid.');
          return;
        }

        const detections = await faceapi
          .detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())
          .withFaceLandmarks()
          .withFaceExpressions()
          .withAgeAndGender();

        const ctx = canvas.getContext('2d');
        ctx?.clearRect(0, 0, canvas.width, canvas.height);

        if (detections) {
          const resizedDetections = faceapi.resizeResults(
            detections,
            displaySize
          );
          faceapi.draw.drawDetections(canvas, resizedDetections);
          faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);

          // Smile Detection
          const happyExpression = detections.expressions.happy || 0;
          this.smileValue =
            happyExpression > 0.5
              ? `Smiling: ${(happyExpression * 100).toFixed(1)}%`
              : 'No smile detected';

          // Blink Detection
          const landmarks = detections.landmarks;
          const leftEye = landmarks.getLeftEye();
          const rightEye = landmarks.getRightEye();
          const leftEyeBlink = this.calculateEyeAspectRatio(leftEye);
          const rightEyeBlink = this.calculateEyeAspectRatio(rightEye);

          this.blinkStatus =
            leftEyeBlink < 0.2 && rightEyeBlink < 0.2
              ? 'Eyes closed'
              : 'Eyes open';

          // Mouth Openness Detection
          const mouth = landmarks.getMouth();
          const mouthOpenness = this.calculateMouthOpenness(mouth);
          this.mouthStatus =
            mouthOpenness > 0.3 ? 'Mouth open' : 'Mouth closed';

          // Head Pose Detection
          const nose = landmarks.getNose();
          this.headPose = this.detectHeadPose(nose);

          // Age and Gender Detection
          this.ageGender = `Age: ${Math.round(detections.age)}, Gender: ${
            detections.gender
          }`;
        } else {
          this.smileValue = 'No face detected';
          this.blinkStatus = 'No face detected';
          this.mouthStatus = 'No face detected';
          this.headPose = 'No face detected';
          this.ageGender = 'Age: N/A, Gender: N/A';
        }
      }, 200);
    });
  }

  calculateEyeAspectRatio(eye: faceapi.Point[]): number {
    const vertical1 = this.distance(eye[1], eye[5]);
    const vertical2 = this.distance(eye[2], eye[4]);
    const horizontal = this.distance(eye[0], eye[3]);

    return (vertical1 + vertical2) / (2 * horizontal);
  }

  calculateMouthOpenness(mouth: faceapi.Point[]): number {
    const vertical = this.distance(mouth[13], mouth[19]); // Top and bottom lip
    const horizontal = this.distance(mouth[12], mouth[16]); // Left and right corners

    return vertical / horizontal;
  }

  detectHeadPose(nose: faceapi.Point[]): string {
    const dx = nose[0].x - nose[3].x; // Horizontal distance
    const dy = nose[0].y - nose[3].y; // Vertical distance

    if (dx > 10) return 'Head tilted left';
    if (dx < -10) return 'Head tilted right';
    if (dy > 10) return 'Head tilted down';
    if (dy < -10) return 'Head tilted up';
    return 'Head position normal';
  }

  distance(point1: faceapi.Point, point2: faceapi.Point): number {
    const dx = point1.x - point2.x;
    const dy = point1.y - point2.y;
    return Math.sqrt(dx * dx + dy * dy);
  }
}
  

2. face-api.component.html

Berikut adalah kode HTML untuk tampilan dari komponen FaceApiComponent:


  <div class="container">
  <h2>
    Face Detection and Face Recognition in the browser and nodejs with
    tensorflow.js
  </h2>
  <table>
    <tr>
      <td>
        <video #video autoplay muted></video>
      </td>
      <td>
        <canvas #canvas></canvas>
      </td>
    </tr>
  </table>
  <table>
    <tr>
      <td>
        <p>{{ smileValue }}</p>
      </td>
      <td>  |  </td>
      <td>
        <p>{{ blinkStatus }}</p>
      </td>
      <td>  |  </td>
      <td>
        <p>{{ mouthStatus }}</p>
      </td>
      <td>  |  </td>
      <td>
        <p>{{ headPose }}</p>
      </td>
      <td>  |  </td>
      <td>
        <p>{{ ageGender }}</p>
      </td>
    </tr>
  </table>
</div>

3. face-api.component.scss

Berikut adalah kode SCSS untuk styling komponen:


.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

video {
  width: 640px;
  height: 480px;
  border: 2px solid black;
}

canvas {
  position: absolute;
  width: 640px;
  height: 480px;
  top: 0;
  left: 0;
  border: 2px solid red;
}

table {
  margin-top: 20px;
}

td {
  padding: 5px;
}
  

Kode Lengkap

Untuk melihat kode lengkapnya bisa kunjungi langsung repositori ini : https://github.com/dyazincahya-blog/angular-face-api

Pengujian Aplikasi

Untuk menguji aplikasi ini, pastikan Anda sudah mengakses kamera dengan izin yang sesuai. Aplikasi akan mendeteksi wajah, dan menampilkan informasi mengenai ekspresi wajah, status kedipan mata, status mulut, posisi kepala, serta perkiraan usia dan jenis kelamin langsung di layar. Untuk hasilnya ada pada gambar di atas.

Kesimpulan

Dengan menggunakan face-api.js di Angular, kita bisa dengan mudah melakukan deteksi dan pengenalan wajah dalam aplikasi web. Anda bisa mengembangkan lebih lanjut aplikasi ini dengan menambahkan berbagai fitur seperti deteksi lebih dari satu wajah atau analisis ekspresi wajah lebih lanjut.

Mungkin cukup sekian dulu untuk pembahasan kali ini, Terimakasih :)

0/Post a Comment/Comments

Lebih baru Lebih lama