Flutter: Face Detection with ML Kit

How to create face detection with Google ML Kit

Yudi Setiawan
Nusanet Developers
9 min readFeb 13, 2022

--

Pengenalan

Di tulisan kali ini kita akan membahas mengenai cara membuat pendeteksi wajah menggunakan layanan dari Google ML Kit. Btw, di tulisan-tulisan saya yang sebelumnya saya pernah bahas juga mengenai topik yang seperti ini namun bukan deteksi wajah melainkan deteksi penggunaan masker menggunakan TensorFlow Lite. Kalau kamu tertarik, tulisan tersebut bisa kamu baca pada link berikut ini ya.

Untuk membuat face detection di Flutter tidaklah terlalu sulit karena ada banyak sekali third party diluar sana yang sudah bagus-bagus mulai dari yang gratisan sampai yang berbayar pun juga ada. Tergantung dari kebutuhan kita mau buat pendeteksi wajah yang gimana. Berhubung ditulisan kali ini kita mau belajar maka, saya akan pakai yang gratisan saja ya. Untuk yang gratisan kita bisa pakai third party miliknya si Google yang bernama Google ML Kit. Dan kalau kita cek di pub.dev itu plugin-nya sudah lumayan banyak yang buat ya.

List plugin google ml kit di pub.dev

Ya, walaupun kalau kita perhatikan itu sebenarnya bukan milik official-nya langsung dari Google tapi nggak apa-apa lah. Itulah gunanya komunitas. Dan sedikit info aja nih kalau plugin yang miliknya Firebase itu plugin-nya sudah discontinued ya. Ini plugin official-nya milik si Firebase.

Plugin firebase ml vision sudah discontinued

Bisa kita lihat sendiri bahwa plugin firebase ml vision sudah discontinued dan mereka sarankan untuk pakai plugin google_ml_kit yang ini.

Tapi, saya sudah coba duluan itu plugin google_ml_kit dan sayangnya plugin tersebut menyebabkan plugin image_picker tidak berfungsi dengan baik di iOS. Jadi, sorry to say bahwa untuk di tulisan kali ini saya tidak akan memakai plugin tersebut ya. Dan yang kena bukan cuma saya saja. Yang lain juga kena loh. Ini salah satu thread-nya di Stack Overflow.

Jadi, berhubung plugin google_ml_kit bermasalah jadi kita akan pakai plugin yang ini saja ya.

Pembuatan Aplikasi

Untuk langkah pertamanya silakan kita buat projek baru pada text editor masing-masing dengan nama flutter_face_detection_demo. Dan pastikan projeknya kita buat target-nya hanya ke Android dan iOS karena untuk platform web dan desktop tidak akan kita buat ya karena dari plugin-nya juga belum support.

Buat projek baru dengan nama flutter_face_detection_demo

Setup pubspec.yaml

Langkah selanjutnya kita perlu atur file pubspec.yaml-nya dan ubah bagian dependencies-nya menjadi seperti berikut.

...
dependencies:
flutter:
sdk: flutter

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2

# Flutter plugin for Google ML Kit on-device vision apis. It includes barcode scanning,
# image labeling, text recognition and face detection.
google_ml_vision: ^0.0.7

# Flutter plugin for selecting images from the Android and iOS image library, and
# taking new pictures with the camera.
image_picker: ^0.8.4+8

...

Bisa kita lihat diatas bahwa kita membutuhkan beberapa plugin untuk membuat aplikasi pada projek kali ini. Dan beberapa plugin tersebut adalah sebagai berikut:

  1. google_ml_vision
    Plugin ini kita butuhkan untuk membuat fitur face detection-nya.
  2. image_picker
    Plugin ini kita butuhkan untuk mengambil foto dari kamera dan galeri.

Selanjutnya, jangan lupa ya jalankan perintah flutter pub get untuk mengunduh paket-paket dependency-nya.

Setup Plugin Google ML Vision dan Image Picker

Setelah kita berhasil mengunduh paket-paket dependency-nya. Langkah berikutnya adalah kita perlu setup di sisi Android dan iOS-nya agar plugin Google ML Vision-nya dan Image Picker bisa berjalan dengan baik.

Android

Untuk di sisi Android kita hanya perlu mengubah nilai minSdkVersion-nya menjadi 19. Kamu bisa ubah nilainya didalam file android/app/build.gradle.

Ubah nilai minSdkVersion menjadi 19

iOS

Untuk di sisi iOS kita hanya perlu mengubah iOS deployment target-nya menjadi 11.0

Ubah iOS Deployment Target menjadi 11.0

Dan karena di tulisan kali ini kita mau buat app-nya agar ambil gambar dari galeri maka, kita perlu menambahkan permission NSPhotoLibraryUsageDescription kedalam file Info.plist

...
<true/>
<key>NSPhotoLibraryUsageDescription</key>
<string>Butuh permission untuk mengambil gambar dari galeri</string>
...

Buat UI Utama

Langkah berikutnya adalah kita perlu buat halaman utama dari app kita. Buka file main.dart dan ubah kode didalamnya menjadi seperti berikut.

import 'dart:io';

import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Face Detection',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Face Detection'),
);
}
}

class MyHomePage extends StatefulWidget {
final String title;

const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
var pathPhoto = '';
var isLoading = false;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () async {
// TODO: Ambil foto dari kamera atau galeri menggunakan plugin image_picker
},
),
body: buildWidgetBody(),
);
}

Widget buildWidgetBody() {
/// Tampilkan loading ditengah-tengah layar
if (isLoading) {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}

/// Tampilkan info bahwa tidak ada foto yang diambil
if (pathPhoto.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'Silakan ambil foto dulu ya\n'
'dengan cara tekan tombol tambah di bagian kanan bawah',
textAlign: TextAlign.center,
),
),
);
}

/// Tampilkan foto yang kita ambil dari kamera atau galeri
return Center(
child: Image.file(
File(
pathPhoto,
),
),
);
}

void showDialogMessage(String message) {
// TODO: Tampilkan pesan dialog
}
}

Sekarang coba kita jalankan programnya maka, outputnya akan menjadi seperti berikut.

Buat UI utama

Buat Fungsi Tampilkan Pesan Dialog

Sebelum kita membuat fungsi untuk mengambil gambar-nya. Kita perlu lengkapi dulu nih fungsi showDialogMessage . Silakan kita ubah kode berikut ini

void showDialogMessage(String message) {
// TODO: Tampilkan pesan dialog
}

Menjadi seperti berikut.

void showDialogMessage(String message) {
if (Platform.isIOS) {
showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: const Text('Info'),
content: Text(message),
actions: [
CupertinoDialogAction(
child: const Text('Ok'),
onPressed: () => Navigator.pop(context),
),
],
);
},
);
} else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Info'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
);
},
);
}
}

Buat Fitur Face Detection

Langkah berikutnya adalah kita masuk ke bagian pembuatan fitur face detection-nya ya. Untuk membuatnya silakan kita ubah bagian kode berikut ini.

floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () async {
// TODO: Ambil foto dari kamera atau galeri menggunakan plugin image_picker
},
),

Menjadi seperti berikut.

floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () async {
/// Ambil gambar dari galeri
final image = await ImagePicker().pickImage(source: ImageSource.gallery);

/// Pastikan bahwa gambarnya valid
if (image != null) {
/// Tampilkan loading
setState(() => isLoading = true);

/// Ambil path image-nya
pathPhoto = image.path;

/// Buat objek GoogleVisionImage dengan datanya adalah gambar yang kita ambil dari galeri
final googleVisionImage = GoogleVisionImage.fromFilePath(pathPhoto);

/// Buat objek FaceDetector
final faceDetector = GoogleVision.instance.faceDetector();

/// Jalankan proses untuk deteksi wajahnya
final faces = await faceDetector.processImage(googleVisionImage);

/// Tampilkan pesan apakah wajahnya terdeteksi atau tidak
if (faces.isEmpty) {
showDialogMessage('Wajah tidak terdeteksi');
} else {
showDialogMessage('Wajah terdeteksi');
}

/// Sembunyikan loading
setState(() => isLoading = false);
}
}
,
),

Berikut adalah output dari fitur face detection-nya.

Fitur face detection Google ML Vision

Nah, fitur face detection-nya sudah berfungsi ya. Tapi, kalau face detection-nya cuma sekedar pesan dialog gitu kurang keren lah. Biar lebih keren kita perlu tambah sedikit fitur nih. Jadi, kita akan beri bingkai dimana letak wajahnya dari foto yang kita upload.

Buat Fitur Bingkai Wajah Yang Terdeteksi

Untuk membuatnya kita perlu menggunakan CustomPainter untuk menggambar bingkai-nya. Kita buat file baru dengan nama face_detector_painter.dart dan isi dengan kode berikut.

import 'package:flutter/material.dart';
import 'package:google_ml_vision/google_ml_vision.dart';

class FaceDetectorPainter extends CustomPainter {
final Size imageSize;
final List<Face> faces;
final bool isReflection;

const FaceDetectorPainter(
this.imageSize,
this.faces, {
this.isReflection = false,
});

@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1.0
..color = Colors.blue;

for (final face in faces) {
final faceRect = _reflectionRect(face.boundingBox);
canvas.drawRect(
_scaleRect(
rect: faceRect,
imageSize: imageSize,
widgetSize: size,
),
paint,
);
}
}

@override
bool shouldRepaint(covariant FaceDetectorPainter oldDelegate) {
return oldDelegate.imageSize != imageSize || oldDelegate.faces != faces;
}

Rect _reflectionRect(Rect boundingBox) {
if (isReflection) {
return boundingBox;
}
final centerX = imageSize.width / 2;
final left = ((boundingBox.left - centerX) * -1) + centerX;
final right = ((boundingBox.right - centerX) * -1) + centerX;
return Rect.fromLTRB(left, boundingBox.top, right, boundingBox.bottom);
}

Rect _scaleRect({
required Rect rect,
required Size imageSize,
required Size widgetSize,
}) {
final scaleX = widgetSize.width / imageSize.width;
final scaleY = widgetSize.height / imageSize.height;

final scaledRect = Rect.fromLTRB(
rect.left.toDouble() * scaleX,
rect.top.toDouble() * scaleY,
rect.right.toDouble() * scaleX,
rect.bottom.toDouble() * scaleY,
);

return scaledRect;
}
}

Lalu, kita perlu ubah sedikit kode kita yang didalam main.dart. Pertama, kita perlu tambahkan variable-variable berikut didalam class _MyHomePageState .

class _MyHomePageState extends State<MyHomePage> {
var pathPhoto = '';
var isLoading = false;
var widthImage = 0.0;
var heightImage = 0.0;
var faces = <Face>[];

...

Kemudian, kita juga perlu ubah kode bagian berikut.

/// Tampilkan foto yang kita ambil dari kamera atau galeri
return Center(
child: Image.file(
File(
pathPhoto,
),
),
);

Menjadi seperti berikut.

/// Tampilkan foto yang kita ambil dari kamera atau galeri
return Center(
child: CustomPaint(
foregroundPainter: FaceDetectorPainter(
Size(widthImage, heightImage),
faces,
isReflection: true,
),
child: Image.file(
File(
pathPhoto,
),
),
),
);

Langkah berikutnya, kita perlu tambahkan sedikit logic agar kita bisa menggambarkan bingkai didalam gambarnya. Untuk membuatnya kita perlu ubah kode bagian berikut.

/// Pastikan bahwa gambarnya valid
if (image != null) {
/// Tampilkan loading
setState(() => isLoading = true);

/// Ambil path image-nya
pathPhoto = image.path;

/// Buat objek GoogleVisionImage dengan datanya adalah gambar yang kita ambil dari galeri
final googleVisionImage = GoogleVisionImage.fromFilePath(pathPhoto);

/// Buat objek FaceDetector
final faceDetector = GoogleVision.instance.faceDetector();

/// Jalankan proses untuk deteksi wajahnya
final faces = await faceDetector.processImage(googleVisionImage);

/// Tampilkan pesan apakah wajahnya terdeteksi atau tidak
if (faces.isEmpty) {
showDialogMessage('Wajah tidak terdeteksi');
} else {
showDialogMessage('Wajah terdeteksi');
}

/// Sembunyikan loading
setState(() => isLoading = false);
}

Menjadi seperti berikut.

/// Pastikan bahwa gambarnya valid
if (imagePicker != null) {
/// Tampilkan loading
setState(() => isLoading = true);

/// Ambil path image-nya
pathPhoto = imagePicker.path;

/// Ambil nilai width dan height dari gambar
final imageBytes = await File(pathPhoto).readAsBytes();
final image = await decodeImageFromList(imageBytes);
widthImage = image.width.toDouble();
heightImage = image.height.toDouble();


/// Buat objek GoogleVisionImage dengan datanya adalah gambar yang kita ambil dari galeri
final googleVisionImage = GoogleVisionImage.fromFilePath(pathPhoto);

/// Buat objek FaceDetector
final faceDetector = GoogleVision.instance.faceDetector();

/// Jalankan proses untuk deteksi wajahnya
faces.clear();
faces = await faceDetector.processImage(googleVisionImage);

/// Tampilkan pesan apakah wajahnya terdeteksi atau tidak
if (faces.isEmpty) {
showDialogMessage('Wajah tidak terdeteksi');
} else {
showDialogMessage('Wajah terdeteksi');
}

/// Sembunyikan loading
setState(() => isLoading = false);
}

Sekarang coba kita jalankan lagi app-nya. Seharusnya sudah ada bingkainya untuk menunjukkan dimana letak wajahnya.

Fitur face detection with border

Kesimpulan

Jadi gimana? Gampang kan cara buatnya. Jadi, kesimpulannya adalah pada tulisan ini kita sudah berhasil membuat fitur face detection di Android dan iOS menggunakan plugin Google ML Vision. Selain itu, kita juga telah belajar gimana cara menambahkan bingkai di setiap wajah yang ada didalam foto. Seperti biasa untuk source code lengkapnya bisa dilihat di Github ya.

--

--