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 fiturHaar
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.