Face Filter dengan Machine Learning Kit

ML Kit adalah SDK yang digunakan dalam keahlian machine learning pada aplikasi android dan iOS, kemampuan utama dilengkapi dengan satu set API digunakan untuk mengenali text, mendeteksi wajah, mengidentifikasi bangunan terkenal, memindai kode batang, melabeli gambar dan mengidentifikasi bahasa teks.

SDK juga bisa berjalan di perangkat cloud dimana dapat memperoses data anda dengan cepat dan berfungsi bahkan ketika tidak ada koneksi jaringan. Di sisi lain, API berbasis cloud memanfaatkan kecanggihan teknologi machine learning dari Google Cloud Platform untuk memberikan tingkat akurasi yang lebih tinggi.

Bagaimana cara menggunakannya, anda tidak perlu mengetahui secara mendalam mengenai pengoptimalan model untuk memulai, Anda hanya mengimplementasikan fungsi yang diperlukan hanya dalam beberapa baris kode, jika anda developer ML berpengalaman, ML Kit menyediakan API yang mudah digunakan dan dapat membantu anda menggunakan model TensorFlow Lite kustom di aplikasi anda.

Lebih jelasnya mengenai ML Kit, penulis memberikan kasus pada salah satu fitur yang terdapat pada ML Kit membuat aplikasi mendeteksi wajah sekaligus memberikan berupa objek gambar pada saat wajah dideteksi.

Langkah - Langkah Pembuatan

Bagian gradle dependencies anda tambahkan library dari ML Kit

implementation 'com.google.firebase:firebase-ml-vision:23.0.0'

Anda download terlebih dahulu resource yang diperlukan, menentuakan objek dan menampilkan kamera


Download Resource


Manifests

Anda harus daftarkan class dan tambahkan beberapa uses permission pada manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kodetr.facefilter">

    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.NoActionBar">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Layout

Anda buat design dengan menerapkan class CameraSourcePreview dan GraphicOverlay yang anda dapat dari resource yang anda download

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/topLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true"
    android:orientation="vertical">

    <com.kodetr.facefilter.camera.CameraSourcePreview
        android:id="@+id/preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.kodetr.facefilter.camera.GraphicOverlay
            android:id="@+id/faceOverlay"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.kodetr.facefilter.camera.CameraSourcePreview>
</LinearLayout>

Java Class FaceGraphic

Anda buat class FaceGraphic dimana berfungsi sebagai proses untuk menentukan objek pada wajah yang akan ditempatkan gambar dan mengatur objek warna rectangle dari wajah yang di deteksi

package com.kodetr.facefilter

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF

import com.google.android.gms.vision.face.Face
import com.kodetr.facefilter.R
import com.kodetr.facefilter.camera.GraphicOverlay

internal class FaceGraphic(overlay: GraphicOverlay) : GraphicOverlay.Graphic(overlay) {

    private val mFacePositionPaint: Paint
    private val mIdPaint: Paint
    private val mBoxPaint: Paint

    @Volatile
    private var mFace: Face? = null
    private var mFaceId: Int = 0
    private val bitmap: Bitmap
    private var op: Bitmap? = null

    init {

        mCurrentColorIndex = (mCurrentColorIndex + 1) % COLOR_CHOICES.size
        val selectedColor = COLOR_CHOICES[mCurrentColorIndex]

        mFacePositionPaint = Paint()
        mFacePositionPaint.color = selectedColor

        mIdPaint = Paint()
        mIdPaint.color = selectedColor
        mIdPaint.textSize = ID_TEXT_SIZE

        mBoxPaint = Paint()
        mBoxPaint.color = selectedColor
        mBoxPaint.style = Paint.Style.STROKE
        mBoxPaint.strokeWidth = BOX_STROKE_WIDTH
        bitmap = BitmapFactory.decodeResource(overlay.context.resources, R.drawable.joker)
        op = bitmap
    }

    fun setId(id: Int) {
        mFaceId = id
    }

    fun updateFace(face: Face) {
        mFace = face
        op = Bitmap.createScaledBitmap(bitmap, scaleX(face.width).toInt(),
                scaleY(bitmap.height * face.width / bitmap.width).toInt(), false)
        postInvalidate()
    }

    override fun draw(canvas: Canvas) {
        val face = mFace ?: return

        val x = translateX(face.position.x + face.width / 2)
        val y = translateY(face.position.y + face.height / 2)
        val xOffset = scaleX(face.width / 2.0f)
        val yOffset = scaleY(face.height / 2.0f)
        val left = x - xOffset
        val top = y - yOffset
        val right = x + xOffset
        val bottom = y + yOffset
        canvas.drawRect(left, top, right, bottom, mBoxPaint)
        canvas.drawBitmap(op!!, left, top, Paint())
    }

    companion object {
        private const val ID_TEXT_SIZE = 40.0f
        private const val BOX_STROKE_WIDTH = 5.0f
        private val COLOR_CHOICES = intArrayOf(Color.BLUE, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.RED, Color.WHITE, Color.YELLOW)
        private var mCurrentColorIndex = 0
    }
}

Jika anda mengikuti langkah dengan benar selanjutnya yang terakhir anda buat class MainActivity

Java Class MainActivity

Anda buat class MainActivity digunakan sebagai proses dari camera untuk dijalankan sekaligus mengambil class FaceGraphic untuk diterapkan pada Detector Detections Face

package com.kodetr.facefilter

import android.Manifest
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat

import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.vision.CameraSource
import com.google.android.gms.vision.Detector
import com.google.android.gms.vision.MultiProcessor
import com.google.android.gms.vision.Tracker
import com.google.android.gms.vision.face.Face
import com.google.android.gms.vision.face.FaceDetector
import com.kodetr.facefilter.camera.CameraSourcePreview
import com.kodetr.facefilter.camera.GraphicOverlay

import java.io.IOException

class MainActivity : AppCompatActivity() {
    private var mCameraSource: CameraSource? = null
    private var mPreview: CameraSourcePreview? = null
    private var mGraphicOverlay: GraphicOverlay? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mPreview = findViewById(R.id.preview)
        mGraphicOverlay = findViewById(R.id.faceOverlay)

        val rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        if (rc == PackageManager.PERMISSION_GRANTED) {
            createCameraSource()
        } else {
            requestCameraPermission()
        }
    }

    private fun requestCameraPermission() {
        val permissions = arrayOf(Manifest.permission.CAMERA)

        if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.CAMERA)) {
            ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM)
            return
        }
    }

    private fun createCameraSource() {

        val context = applicationContext
        val detector = FaceDetector.Builder(context)
                .setClassificationType(FaceDetector.ALL_CLASSIFICATIONS)
                .setLandmarkType(FaceDetector.ALL_LANDMARKS)
                .setMode(FaceDetector.ACCURATE_MODE)
                .build()

        detector.setProcessor(
                MultiProcessor.Builder(GraphicFaceTrackerFactory())
                        .build())

        mCameraSource = CameraSource.Builder(context, detector)
                .setRequestedPreviewSize(640, 480)
                .setFacing(CameraSource.CAMERA_FACING_BACK)
                .setRequestedFps(30.0f)
                .build()
    }

    override fun onResume() {
        super.onResume()

        startCameraSource()
    }

    override fun onPause() {
        super.onPause()
        mPreview!!.stop()
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mCameraSource != null) {
            mCameraSource!!.release()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        if (requestCode != RC_HANDLE_CAMERA_PERM) {
            Log.d(TAG, "Got unexpected permission result: $requestCode")
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            return
        }

        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Log.d(TAG, "Camera permission granted - initialize the camera source")
            createCameraSource()
            return
        }

        Log.e(TAG, "Permission not granted: results len = " + grantResults.size +
                " Result code = " + if (grantResults.isNotEmpty()) grantResults[0] else "(empty)")

        val listener = DialogInterface.OnClickListener { _, _ -> finish() }

        val builder = AlertDialog.Builder(this)
        builder.setTitle("Face")
                .setMessage("Camera permission")
                .setPositiveButton("Ok", listener)
                .show()
    }

    private fun startCameraSource() {
        val code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
                applicationContext)
        if (code != ConnectionResult.SUCCESS) {
            val dlg = GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS)
            dlg.show()
        }

        if (mCameraSource != null) {
            try {
                mPreview!!.start(mCameraSource!!, mGraphicOverlay!!)
            } catch (e: IOException) {
                Log.e(TAG, "Unable to start camera source.", e)
                mCameraSource!!.release()
                mCameraSource = null
            }
        }
    }

    private inner class GraphicFaceTrackerFactory : MultiProcessor.Factory<Face> {
        override fun create(face: Face): Tracker<Face> {
            return mGraphicOverlay?.let { GraphicFaceTracker(it) }!!
        }
    }

    private inner class GraphicFaceTracker internal constructor(private val mOverlay: GraphicOverlay) : Tracker<Face>() {
        private val mFaceGraphic: FaceGraphic = FaceGraphic(mOverlay)

        override fun onNewItem(faceId: Int, item: Face?) {
            mFaceGraphic.setId(faceId)
        }

        override fun onUpdate(detectionResults: Detector.Detections<Face>?, face: Face?) {
            mOverlay.add(mFaceGraphic)
            mFaceGraphic.updateFace(face!!)
        }

        override fun onMissing(detectionResults: Detector.Detections<Face>?) {
            mOverlay.remove(mFaceGraphic)
        }

        override fun onDone() {
            mOverlay.remove(mFaceGraphic)
        }
    }

    companion object {
        private const val TAG = "MainActivity"
        private const val RC_HANDLE_GMS = 9001
        private const val RC_HANDLE_CAMERA_PERM = 2
    }
}

Demikian yang dapat saya sampaikan dari artikel ini semoga bermanfaat, jika anda mengalami masalah dalam mengerjakan anda bisa mengikuti tutorial dari vidio dan jika ada yang ditanyakan silahkan di kolom komentar dibawah, selamat mencoba.

Share Comments
comments powered by Disqus