Android Face Recognation dengan OpenCV

Face Recognation atau Deteksi Wajah memungkinkan kita untuk mengidentifikasi atau memverifikasi wajah seorang melalui sebuah gambar digital. caranya ialah dengan mencocokan tektur lekuk wajah kita dengan data wajah yang tersimpan di database, untuk membuat Deteksi Wajah diperlukan library yaitu OpenCV.

OpenCV menggunakan sebuah tipe face detector yang disebut Harr-cascade classifier jika ada sebuah image, face detector akan menguji tiap lokasi image dan mengklasifikasinya sebagai wajah atau bukan wajah. Klasifikasi wajah ini menggunakan sebuah pemisalan skala yang tetap, misalnya 60×60 pixel. Jika wajah pada image lebih besar atau lebih kecil dari pixel tersebut, classifier terus menerus jalan beberapa kali, untuk mencari wajah pada gambar tersebut.

Classifier menggunakan data yang disimpan pada file XML untuk memutuskan bagaimana mengklasifikasi tiap lokasi image. OpenCV menggunakan 4 data XML untuk deteksi wajah depan, dan satu untuk wajah profile. Termasuk juga 3 file XML untuk bukan wajah: satu untuk mendeteksi badan secara penuh, satu untuk badan bagian atas, dan satu untuk badan bagian bawah. Kita harus memberitahukan (mendeklarasikan) letak dari classifier yang digunakan. Salah satunya bernama haarcascade_frontalface_default.xml.

Haar-Like Feature

Memproses gambar dalam kotak-kotak, dimana dalam satu kotak terdapat beberapa pixel. Per kotak itu pun kemudian di-proses dan didapatkan perbedaan nilai (threshold) yang menandakan daerah gelap dan terang. Nilai – nilai inilah yang nantinya dijadikan dasar dalam image processing.

Lalu untuk gambar bergerak (video), perhitungan dan penjumlahan pixel terjadi secara terus – menerus dan membutuhkan waktu yang lama. Oleh karena itu, penjumlahan diganti dengan integral sehingga didapatkan hasil lebih cepat. Hasil deteksi dari Haar-Like kurang akurat jika hanya menggunakan satu fungsi saja sehingga biasanya digunakan beberapa fungsi sekaligus (massal). Semakin banyak fungsi yang digunakan maka hasilnya akan semakin akurat. Pemrosesan Haar-Like feature yang banyak tersebut diorganisir atau diatur di dalam classifier cascade.

Konsep Pendeteksi wajah

OpenCV face detector menggunakan metode Paul-Viola dan Michael Jones yang dipublikasikan pada tahun 2001. Pendekatan ini mendeteksi objek dengan menggabungkan 4 konsep :

  • Fitur rectangular sederhana yang disebut fitur Haar
  • Integral image untuk deteksi fitur yang cepat
  • Metode machine learning AdaBoost.

Sebuah pengklasifikasi cascade untuk mengkombinasikan banyak fitur secara efisien.

Fitur yang digunakan Viola dan Jones menggunakan bentuk gelombang Haar. Bentuk gelombang Haar ialah sebuah gelombang kotak. Pada 2 dimensi, gelombang kotak ialah pasangan persegi yang bersebelahan, 1 terang dan 1 gelap. Haar ditentukan oleh pengurangan pixel rata-rata daerah gelap dari pixel rata-rata daerah terang. Jika perbedeaan diatas threshold (diset selama learning), fitur tersebut dikatakan ada. Untuk menentukan ada atau tidaknya Haar feature di setiap lokasi image / gambar, Viola dan Jones menggunakan teknik yang disebut Integral Image. Umumnya integral menambahkan unit kecil secara bersamaan. Dalam hal ini unit kecil ini disebut dengan nilai dari pixel. Nilai dari integral / integral value pada masing-masing pixel merupakan penjumlahan dari semua pixel di atasnya dan di sebelah kirinya. Dimulai dari kiri atas sampai kanan bawah, image / gambar dapat diintegrasikan sebagai operasi matematika per pixel.

Untuk memilih fitur Haar yang digunakan dan untuk mengubah nilai threshold, Viola dan Jones menggunakan metode machine-learning yang disebut AdaBoost. AdaBoost menggabungkan banyak classifier untuk membuat satu classifier. Masing-masing classifier menetapkan suatu bobot, dan gabungan dari bobot inilah yang akan membentuk satu classifier yang kuat.

Viola dan Jones menggabungkan serangkaian AdaBoost classifier sebagai rantai filter / filter chain. Masing-masing filter merupakan AdaBoost classifier yang terpisah dengan jumlah weak classifier yang sedikit dan sama.

Filter pada masing-masing level dilatih untuk mengklasifikasikan gambar yang sebelumnya telah difilter (Training set merupakan database dari wajah). Selama penggunaannya, jika satu dari filter-filter tersebut gagal, image region / daerah pada gambar diklasifikasikan sebagai Bukan Wajah. Saat filter berhasil melewatkan image region, image region kemudian masuk pada filter yang selanjutnya. Image region yang telah melalui semua filter akan dianggap sebagai Wajah.

Langkah - Langkah Pembuatan

Jadi untuk memudahkan dalam membuat aplikasi Face Recognation bisa melihat langkah-langkah pembuatan beserta dengan demo aplikasi dalam vidio berikut

Buat project baru dengan nama Face Recognation kemudian masukan library OpenCV ke project anda, caranya anda bisa lihat di vidio diatas jika sudah anda masuk dibagian gradle app, Masukan semua library berikut dibagian dependencies

implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.github.markushi:circlebutton:1.1'
implementation project(':libV2410')

Buka dibagian AndroidManifest Kemudian anda harus menambahkan uses-permission dan mendaftarkan class activity sekaligus menambahkan attribut setiap activity yang didaftarkan,

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

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:resizeable="true"
        android:smallScreens="true" />

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

    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.camera.autofocus"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.camera.front"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.camera.front.autofocus"
        android:required="false" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MainFace"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity
            android:name=".MainActivity"
            android:configChanges="keyboardHidden|orientation"
            android:label="@string/app_name"
            android:screenOrientation="landscape"
            android:theme="@style/Theme.AppCompat.NoActionBar" />
        <activity
            android:name=".AddActivity"
            android:configChanges="keyboardHidden|orientation"
            android:label="@string/app_name"
            android:screenOrientation="landscape"
            android:theme="@style/Theme.AppCompat.NoActionBar" />
    </application>
</manifest>

Membuat design dari class AddActivity untuk menambahkan Person dan ditampung di database, kasi nama add_activity

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.kodetr.facerecognation.utils.CameraView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/linearLayout"
        android:layout_toStartOf="@+id/imageButton1" />

    <at.markushi.ui.CircleButton
        android:id="@+id/imageButton1"
        android:layout_width="70dip"
        android:layout_height="70dip"
        android:layout_alignParentTop="true"
        android:layout_alignStart="@+id/toggleButton2"
        android:src="@drawable/ic_switch"
        app:cb_color="@color/colorAccent"
        app:cb_pressedRingWidth="8dip" />

    <ToggleButton
        android:id="@+id/toggleButton2"
        android:layout_width="70dip"
        android:layout_height="70dip"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:layout_weight="0.10"
        android:textOff="Rec"
        android:textOn="Stop" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="120dp"
        android:layout_height="140dp"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:weightSum="1">

        <ToggleButton
            android:id="@+id/toggleButton1"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginStart="11dp"
            android:layout_weight="0.08"
            android:text="Train"
            android:textOff="Train"
            android:textOn="Stop" />

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:text="Name"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <EditText
            android:id="@+id/editText1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_weight="0.66"
            android:maxLines="1"
            android:singleLine="true">

            <requestFocus />
        </EditText>
    </LinearLayout>
</RelativeLayout>

Membuat class AddActivity

package com.kodetr.facerecognation;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import at.markushi.ui.CircleButton;
import com.kodetr.facerecognation.utils.CameraView;
import com.kodetr.facerecognation.utils.Labels;

/**
 * Created by kodetr on 25/12/2017.
 */

public class AddActivity extends Activity implements CvCameraViewListener2 {

    private static final String TAG = "OCVSample::Activity";
    private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255);
    public static final int JAVA_DETECTOR = 0;
    public static final int NATIVE_DETECTOR = 1;

    public static final int TRAINING = 0;
    public static final int SEARCHING = 1;
    public static final int IDLE = 2;

    private static final int frontCam = 1;
    private static final int backCam = 2;
    private int faceState = IDLE;
    private Mat mRgba;
    private Mat mGray;
    private File mCascadeFile;
    private CascadeClassifier mJavaDetector;

    private int mDetectorType = JAVA_DETECTOR;
    private String[] mDetectorName;

    private float mRelativeFaceSize = 0.2f;
    private int mAbsoluteFaceSize = 0;
    private int mLikely = 999;

    String mPath = "";

    private CameraView mOpenCvCameraView;
    private int mChooseCamera = backCam;

    EditText text;
    TextView textresult;
    private ImageView Iv;
    Bitmap mBitmap;
    Handler mHandler;

    CtrRecognizer fr;
    ToggleButton toggleButtonRec, toggleButtonTrain;
    CircleButton imCamera;

    static final long MAXIMG = 10;

    int countImages = 0;

    Labels labelsFile;

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");

                    fr = new CtrRecognizer(mPath);
                    String s = getResources().getString(R.string.Straininig);
                    Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
                    fr.load();

                    try {
                        // load cascade file from application resources
                        InputStream is = getResources().openRawResource(R.raw.frontalface);
                        File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
                        mCascadeFile = new File(cascadeDir, "lbpcascade.xml");
                        FileOutputStream os = new FileOutputStream(mCascadeFile);

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bytesRead);
                        }
                        is.close();
                        os.close();

                        mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                        if (mJavaDetector.empty()) {
                            Log.e(TAG, "Failed to load cascade classifier");
                            mJavaDetector = null;
                        } else
                            Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());

                        cascadeDir.delete();

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mOpenCvCameraView.enableView();
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

    public AddActivity() {
        mDetectorName = new String[2];
        mDetectorName[JAVA_DETECTOR] = "Java";
        mDetectorName[NATIVE_DETECTOR] = "Native (tracking)";
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.add_activity);

        mOpenCvCameraView = findViewById(R.id.surface_view);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/KodeTR/";

        Log.e("KodeTR : ", mPath);

        labelsFile = new Labels(mPath);

        Iv = findViewById(R.id.imageView1);
        textresult = findViewById(R.id.textView1);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.obj == "IMG") {
                    Canvas canvas = new Canvas();
                    canvas.setBitmap(mBitmap);
                    Iv.setImageBitmap(mBitmap);
                    if (countImages >= MAXIMG - 1) {
                        toggleButtonRec.setChecked(false);
                        grabarOnclick();
                    }
                } else {
                    textresult.setText(msg.obj.toString());
                }
            }
        };
        text = findViewById(R.id.editText1);
        toggleButtonRec = findViewById(R.id.toggleButton2);
        toggleButtonTrain = findViewById(R.id.toggleButton1);
        imCamera = findViewById(R.id.imageButton1);
        text.setVisibility(View.INVISIBLE);
        textresult.setVisibility(View.INVISIBLE);
        toggleButtonRec.setVisibility(View.INVISIBLE);

        text.setOnKeyListener(new View.OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((text.getText().toString().length() > 0) && (toggleButtonTrain.isChecked()))
                    toggleButtonRec.setVisibility(View.VISIBLE);
                else
                    toggleButtonRec.setVisibility(View.INVISIBLE);
                return false;
            }
        });

        toggleButtonTrain.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (toggleButtonTrain.isChecked()) {
                    textresult.setVisibility(View.VISIBLE);
                    text.setVisibility(View.VISIBLE);
                    textresult.setText(getResources().getString(R.string.SFaceName));
                    if (text.getText().toString().length() > 0)
                        toggleButtonRec.setVisibility(View.VISIBLE);
                } else {
                    textresult.setText("");
                    text.setVisibility(View.INVISIBLE);
                    textresult.setText("");
                    {
                        toggleButtonRec.setVisibility(View.INVISIBLE);
                        text.setVisibility(View.INVISIBLE);
                    }
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.Straininig), Toast.LENGTH_LONG).show();
                    fr.train();
                }
            }
        });

        toggleButtonRec.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {
                grabarOnclick();
            }
        });
        imCamera.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mChooseCamera == frontCam) {
                    mChooseCamera = backCam;
                    mOpenCvCameraView.setCamBack();
                } else {
                    mChooseCamera = frontCam;
                    mOpenCvCameraView.setCamFront();
                }
            }
        });

        boolean success = (new File(mPath)).mkdirs();
        if (!success) {
            Log.e("Error", "Error creating directory");
        }
    }

    void grabarOnclick() {
        if (toggleButtonRec.isChecked())
            faceState = TRAINING;
        else {
            if (faceState == TRAINING) ;
            countImages = 0;
            faceState = IDLE;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
        } else {
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        mGray = new Mat();
        mRgba = new Mat();
    }

    public void onCameraViewStopped() {
        mGray.release();
        mRgba.release();
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
        }

        MatOfRect faces = new MatOfRect();
        if (mDetectorType == JAVA_DETECTOR) {
            if (mJavaDetector != null)
                mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
                        new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        } else {
            Log.e(TAG, "Detection method is not selected!");
        }

        Rect[] facesArray = faces.toArray();

        if ((facesArray.length == 1)
                && (faceState == TRAINING)
                && (countImages < MAXIMG)
                && (!text.getText().toString().isEmpty())) {

            Mat m;
            Rect r = facesArray[0];

            m = mRgba.submat(r);
            mBitmap = Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);

            Utils.matToBitmap(m, mBitmap);
            Message msg = new Message();
            String textTochange = "IMG";
            msg.obj = textTochange;
            mHandler.sendMessage(msg);

            if (countImages < MAXIMG) {
                fr.add(m, text.getText().toString());
                countImages++;
            }

        } else if ((facesArray.length > 0) && (faceState == SEARCHING)) {

            Mat m;
            m = mGray.submat(facesArray[0]);
            mBitmap = Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);

            Utils.matToBitmap(m, mBitmap);
            Message msg = new Message();
            String textTochange = "IMG";
            msg.obj = textTochange;
            mHandler.sendMessage(msg);

            textTochange = fr.predict(m);
            mLikely = fr.getProb();
            msg = new Message();
            msg.obj = textTochange;
            mHandler.sendMessage(msg);
        }
        for (int i = 0; i < facesArray.length; i++)
            Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        return mRgba;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            startActivity(new Intent(AddActivity.this, MainFace.class));
            AddActivity.this.finish();
        }
        return super.onKeyDown(keyCode, event);
    }
}

Membuat Model untuk menampung Person, nama class Person

package com.kodetr.facerecognation.models;

import android.graphics.Bitmap;

/**
 * Created by kodetr on 25/12/2017.
 */

public class Person {

    String name;
    Bitmap image;

    public Person(String name, Bitmap image) {
        this.name = name;
        this.image = image;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Bitmap getImage() {
        return image;
    }

    public void setImage(Bitmap image) {
        this.image = image;
    }
}

Membuat design dari adapter yang menampilkan data berupa nama dan gambar dari Person, nama design item_person

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/delete"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="5dp"
    android:layout_marginTop="5dp"
    android:padding="10dp"
    android:background="?attr/selectableItemBackgroundBorderless"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <com.kodetr.facerecognation.utils.RoundedImageView
        android:id="@+id/image"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:adjustViewBounds="true"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/txt_nama"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center_horizontal"
        android:text="My Name"
        android:textSize="16sp" />

    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="25dp"
        android:layout_marginTop="10dp"
        android:animateLayoutChanges="true"
        app:cardBackgroundColor="@drawable/view_selector_blue"
        app:cardCornerRadius="12.5dp"
        app:cardElevation="0dp">

        <TextView
            android:id="@+id/update"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:paddingEnd="16dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingStart="16dp"
            android:text="Update"
            android:textColor="@android:color/white"
            android:textSize="12sp" />

    </android.support.v7.widget.CardView>
</LinearLayout>

Membuat class adapter untuk menampilkan data Person dalam bentuk list, nama class AdapterPerson

package com.kodetr.facerecognation.adapter;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.kodetr.facerecognation.MainFace;
import com.kodetr.facerecognation.R;
import com.kodetr.facerecognation.models.Person;
import com.kodetr.facerecognation.utils.RoundedImageView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by kodetr on 25/12/2017.
 */

public class AdapterPerson extends BaseAdapter {

    private MainFace activity;
    private List<Person> lPerson;

    public AdapterPerson(MainFace activity) {
        this.activity = activity;
        lPerson = new ArrayList<>();
    }

    @Override
    public int getCount() {
        return lPerson.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public void addPerson(Person mPerson) {
        lPerson.add(mPerson);
        notifyDataSetChanged();
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        Person setPerson = lPerson.get(position);

        ViewHolder mHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false);
            mHolder = new ViewHolder(convertView);
            convertView.setTag(mHolder);
        } else {
            mHolder = (ViewHolder) convertView.getTag();
        }

        mHolder.name.setText(setPerson.getName());
        mHolder.image.setImageBitmap(setPerson.getImage());

        mHolder.update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                activity.ClickUpdate(position);
            }
        });
        mHolder.delete.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                activity.ClickDelete(position);
                return false;
            }
        });

        return convertView;
    }

    public Person getPerson(int position) {
        return lPerson.get(position);
    }

    public class ViewHolder {

        RoundedImageView image;
        TextView name;
        TextView update;
        LinearLayout delete;

        ViewHolder(View view) {
            update = view.findViewById(R.id.update);
            delete = view.findViewById(R.id.delete);
            image = view.findViewById(R.id.image);
            name = view.findViewById(R.id.txt_nama);
        }
    }
}

Mengambil data dari list adapter kemudian ditampilkan menggunakan GridView, membuat design dengan nama activity_face.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

        </android.support.design.widget.AppBarLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <GridView
                android:id="@+id/gridview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:horizontalSpacing="10dp"
                android:numColumns="3"
                android:stretchMode="columnWidth"
                android:verticalSpacing="10dp" />

        </LinearLayout>
    </LinearLayout>

    <at.markushi.ui.CircleButton
        android:id="@+id/fab"
        android:layout_width="70dip"
        android:layout_height="70dip"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:src="@drawable/ic_searched"
        app:cb_color="@color/red"
        app:cb_pressedRingWidth="8dip" />

</android.support.design.widget.CoordinatorLayout>

Membuat class dari data yang ditampilkan di GridView

package com.kodetr.facerecognation;

import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.GridView;
import android.widget.Toast;

import com.kodetr.facerecognation.adapter.AdapterPerson;
import com.kodetr.facerecognation.models.Person;
import com.kodetr.facerecognation.utils.Labels;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import at.markushi.ui.CircleButton;

/**
 * Created by kodetr on 25/12/2017.
 */

public class MainFace extends AppCompatActivity {

    private GridView gridview;
    private AdapterPerson adapterPerson;
    private Person mPerson;
    private static Labels thelabels;
    private static int count = 0;
    private static Bitmap bmlist[];
    private static String namelist[];
    private static String mPath = "";

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_face);
        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle(getString(R.string.app_name));
        setSupportActionBar(toolbar);

        gridview = findViewById(R.id.gridview);

        CircleButton fb = findViewById(R.id.fab);
        fb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainFace.this, MainActivity.class));
            }
        });

        mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/KodeTR/";
        thelabels = new Labels(mPath);
        thelabels.Read();
        count = 0;
        showPerson();
    }

    public void showPerson() {
        configGridView();
        List<Person> userList = getPerson();
        for (int i = 0; i < userList.size(); i++) {
            mPerson = userList.get(i);
            adapterPerson.addPerson(mPerson);
        }
    }

    public void configGridView() {
        adapterPerson = new AdapterPerson(this);
        gridview.setAdapter(adapterPerson);
    }

    public static List<Person> getPerson() {
        List<Person> wordList = new ArrayList<>();

        int max = thelabels.max();

        for (int i = 0; i <= max; i++) {
            if (thelabels.get(i) != "") {
                count++;
            }
        }

        bmlist = new Bitmap[count];
        namelist = new String[count];
        count = 0;

        for (int i = 0; i <= max; i++) {
            if (thelabels.get(i) != "") {
                File root = new File(mPath);
                final String fname = thelabels.get(i);
                FilenameFilter pngFilter = new FilenameFilter() {
                    public boolean accept(File dir, String name) {
                        return name.toLowerCase().startsWith(fname.toLowerCase() + "-");
                    }
                };
                File[] imageFiles = root.listFiles(pngFilter);
                if (imageFiles.length > 0) {
                    InputStream is;
                    try {
                        is = new FileInputStream(imageFiles[0]);

                        bmlist[count] = BitmapFactory.decodeStream(is);
                        namelist[count] = thelabels.get(i);

                        Person mPerson = new Person(namelist[count], bmlist[count]);
                        wordList.add(mPerson);

                    } catch (FileNotFoundException e) {
                        // TODO Auto-generated catch block
                        Log.e("File Erorr", e.getMessage() + " " + e.getCause());
                        e.printStackTrace();
                    }
                }
                count++;
            }
        }
        return wordList;
    }

    private Person selectedPerson;

    public void ClickUpdate(int position) {
        selectedPerson = adapterPerson.getPerson(position);
        Toast.makeText(this, String.valueOf(selectedPerson.getName()), Toast.LENGTH_SHORT).show();
    }

    public void ClickDelete(int position) {
        selectedPerson = adapterPerson.getPerson(position);
        DialogPersonDelete(selectedPerson);
    }

    public void DeletePerson(final Person person) {
        File root = new File(mPath);
        FilenameFilter pngFilter = new FilenameFilter() {
            public boolean accept(File dir, String n) {
                String s = person.getName();
                return n.toLowerCase().startsWith(s.toLowerCase() + "-");
            }
        };
        File[] imageFiles = root.listFiles(pngFilter);
        for (File image : imageFiles) {
            image.delete();
            int i;
            for (i = 0; i < count; i++) {
                if (namelist[i].equalsIgnoreCase(person.getName())) {
                    int j;
                    for (j = i; j < count - 1; j++) {
                        namelist[j] = namelist[j + 1];
                        bmlist[j] = bmlist[j + 1];
                    }
                    count--;
                    showPerson();
                    break;
                }
            }
        }
    }

    public void DialogPersonDelete(final Person person) {
        AlertDialog alertDialog = new AlertDialog.Builder(this).create();
        alertDialog.setTitle("Options");
        alertDialog.setMessage("You sure person...");
        alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Delete",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        DeletePerson(person);
                        dialog.dismiss();
                    }
                });
        alertDialog.show();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add_person:
                startActivity(new Intent(MainFace.this, AddActivity.class));
                break;
            default:
                return super.onOptionsItemSelected(item);
        }
        return true;
    }
}

Selanjutnya anda harus membuat proses deteksi wajah dengan proses mencari dan mencocokan dari database, Buat design dengan nama main_activity dan masukan kode dibawah.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.kodetr.facerecognation.utils.CameraView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toStartOf="@+id/imageButton1" />

    <at.markushi.ui.CircleButton
        android:id="@+id/imageButton1"
        android:layout_width="70dip"
        android:layout_height="70dip"
        android:src="@drawable/ic_switch"
        app:cb_color="@color/colorAccent"
        app:cb_pressedRingWidth="8dip"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:layout_centerVertical="true"
        android:layout_alignParentEnd="true" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="120dp"
        android:layout_height="140dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginStart="11dp"
                android:layout_toEndOf="@+id/imageView3"
                android:text="@string/search"
                android:textAppearance="?android:attr/textAppearanceLarge" />

            <ImageView
                android:id="@+id/imageView3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_gps_green" />

            <ImageView
                android:id="@+id/imageView4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_gps_yellow" />

            <ImageView
                android:id="@+id/imageView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_gps_red" />

        </RelativeLayout>
    </LinearLayout>
</RelativeLayout>

Membuat class MainActivity dari design yang dibuat sebelumnya dan masukan kode dibawah.

package com.kodetr.facerecognation;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.kodetr.facerecognation.utils.CameraView;
import com.kodetr.facerecognation.utils.Labels;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import at.markushi.ui.CircleButton;

/**
 * Created by kodetr on 25/12/2017.
 */

public class MainActivity extends Activity implements CvCameraViewListener2 {

    private static final String TAG = "OCVSample::Activity";
    private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255);
    public static final int JAVA_DETECTOR = 0;
    public static final int NATIVE_DETECTOR = 1;

    public static final int TRAINING = 0;
    public static final int SEARCHING = 1;
    public static final int IDLE = 2;

    private static final int frontCam = 1;
    private static final int backCam = 2;

    private int faceState = IDLE;

    private Mat mRgba;
    private Mat mGray;
    private File mCascadeFile;
    private CascadeClassifier mJavaDetector;

    private int mDetectorType = JAVA_DETECTOR;
    private String[] mDetectorName;

    private float mRelativeFaceSize = 0.2f;
    private int mAbsoluteFaceSize = 0;
    private int mLikely = 999;

    String mPath = "";
    private CameraView mOpenCvCameraView;
    private int mChooseCamera = backCam;

    TextView textresult;
    private ImageView Iv;
    Bitmap mBitmap;
    Handler mHandler;

    CtrRecognizer fr;
    ImageView ivGreen, ivYellow, ivRed;
    CircleButton imCamera;

    static final long MAXIMG = 10;
    int countImages = 0;
    Labels labelsFile;

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
                    fr = new CtrRecognizer(mPath);
                    String s = getResources().getString(R.string.Straininig);
                    Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
                    fr.load();

                    try {
                        // load cascade file from application resources
                        InputStream is = getResources().openRawResource(R.raw.frontalface);
                        File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
                        mCascadeFile = new File(cascadeDir, "frontalface.xml");
                        FileOutputStream os = new FileOutputStream(mCascadeFile);

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bytesRead);
                        }
                        is.close();
                        os.close();

                        mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                        if (mJavaDetector.empty()) {
                            Log.e(TAG, "Failed to load cascade classifier");
                            mJavaDetector = null;
                        } else
                            Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
                        cascadeDir.delete();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
                    }
                    mOpenCvCameraView.enableView();
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

    public MainActivity() {
        mDetectorName = new String[2];
        mDetectorName[JAVA_DETECTOR] = "Java";
        mDetectorName[NATIVE_DETECTOR] = "Native (tracking)";
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.main_activity);

        mOpenCvCameraView = findViewById(R.id.surface_view);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/KodeTR/";
        labelsFile = new Labels(mPath);
        Iv = findViewById(R.id.imageView1);
        textresult = findViewById(R.id.textView1);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.obj == "IMG") {
                    Canvas canvas = new Canvas();
                    canvas.setBitmap(mBitmap);
                    Iv.setImageBitmap(mBitmap);
                } else {
                    ivGreen.setVisibility(View.INVISIBLE);
                    ivYellow.setVisibility(View.INVISIBLE);
                    ivRed.setVisibility(View.INVISIBLE);

                    if (mLikely < 0) {
                    } else if (mLikely < 50) {
                        textresult.setText(msg.obj.toString());
                        ivGreen.setVisibility(View.VISIBLE);
                    } else if (mLikely < 80) {
                        textresult.setText(msg.obj.toString());
                        ivYellow.setVisibility(View.VISIBLE);
                    } else {
                        textresult.setText("Unknow");
                        ivRed.setVisibility(View.VISIBLE);
                    }
                }
            }
        };

        ivGreen = findViewById(R.id.imageView3);
        ivYellow = findViewById(R.id.imageView4);
        ivRed = findViewById(R.id.imageView2);
        imCamera = findViewById(R.id.imageButton1);

        ivGreen.setVisibility(View.INVISIBLE);
        ivYellow.setVisibility(View.INVISIBLE);
        ivRed.setVisibility(View.VISIBLE);
        textresult.setVisibility(View.INVISIBLE);

        imCamera.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                if (mChooseCamera == frontCam) {
                    mChooseCamera = backCam;
                    mOpenCvCameraView.setCamBack();
                } else {
                    mChooseCamera = frontCam;
                    mOpenCvCameraView.setCamFront();
                }
            }
        });

        faceState = SEARCHING;
        textresult.setVisibility(View.VISIBLE);
        boolean success = (new File(mPath)).mkdirs();
        if (!success) {
            Log.e("Error", "Error creating directory");
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        mGray = new Mat();
        mRgba = new Mat();
    }

    public void onCameraViewStopped() {
        mGray.release();
        mRgba.release();
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
        }

        MatOfRect faces = new MatOfRect();
        if (mDetectorType == JAVA_DETECTOR) {
            if (mJavaDetector != null)
                mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
                        new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        } else {
            Log.e(TAG, "Detection method is not selected!");
        }

        Rect[] facesArray = faces.toArray();
        if ((facesArray.length == 1) && (faceState == TRAINING) && (countImages < MAXIMG)) {
            Mat m;
            Rect r = facesArray[0];

            m = mRgba.submat(r);
            mBitmap = Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);

            Utils.matToBitmap(m, mBitmap);

            Message msg = new Message();
            String textTochange = "IMG";
            msg.obj = textTochange;
            mHandler.sendMessage(msg);

        } else if ((facesArray.length > 0) && (faceState == SEARCHING)) {

            Mat m;
            m = mGray.submat(facesArray[0]);
            mBitmap = Bitmap.createBitmap(m.width(), m.height(), Bitmap.Config.ARGB_8888);

            Utils.matToBitmap(m, mBitmap);
            Message msg = new Message();
            String textTochange = "IMG";
            msg.obj = textTochange;
            mHandler.sendMessage(msg);
            textTochange = fr.predict(m);
            mLikely = fr.getProb();
            msg = new Message();
            msg.obj = textTochange;
            mHandler.sendMessage(msg);
        }
        for (int i = 0; i < facesArray.length; i++)
            Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        return mRgba;
    }
}

Demikian yang dapat saya sampaikan dari artikel ini semoga bermanfaat, jika ada yang ditanyakan silahkan di kolom komentar dibawah, selamat mencoba.

Share Comments
comments powered by Disqus