Membuat Face Recognation Menggunakan Luxand FaceSDK

Luxand FaceSDK adalah library yang digunakan untuk mendeteksi wajah dan pengenalan wajah yang dapat di integrasikan ke dalam aplikasi pelanggan, library FaceSDK dapat digunakan di semua platform salah satunya penulis menerapkan di Android.

FaceSDK dilengkapi dengan API Pelacak yang memungkinkan pelacakan dan mengenali wajah dalam vidio langsung. API pelacak bekerja menyederhanakan dengan streaming video, menawarkan fungsi untuk menandai subjek dengan nama dan mengenalinya lebih lanjut.

SDK menyediakan koordinat 70 titik fitur wajah (termasuk mata, alis, mulut, hidung, dan kontur wajah). Luxand FaceSDK menggunakan beberapa inti prosesor untuk mempercepat pengenalan. Perpustakaan mendukung kamera web yang kompatibel dengan DirectShow dan kamera IP dengan antarmuka MJPEG. Lebih jelasnya anda bisa baca dokumentasi dari Luxand FaceSDK

Spesifikasi Teknis

Sepsifikasi yang disediakan Luxand FaceSDK meliputi:

  • Deteksi wajah,
  • Pencocokan Wajah,
  • Pengenalan Video Langsung dengan API Pelacak,
  • Deteksi Fitur Wajah,
  • Deteksi Pusat Mata,
  • Deteksi Jenis Kelamin,
  • Deteksi Usia, dll

Untuk spesifikasi yang lebih jelas anda dapat baca di laman Luxand FaceSDK

Dari spesifikasi teknis diatas penulis hanya menjelaskan mengenai Deteksi Wajah dan Deteksi Jenis Kelamin dimana kasus yang penulis gunakan aplikasi yang akan diupload ke Playstore, dalam kasus ini penulis menggunakan Android, jadi jika anda sudah download FaceSDK anda harus menyesuaikan dengan bahasa pemrograman yang digunakan.


Download apk Aplikasi


Pembuatan Module

Jika anda buat module anda harus membuat terlebih dahulu project baru.

Buat Module untuk mendeteksi wajah dengan nama lib_wajah kemudian anda daftarkan class dan uses-permission dibagian manifest modul.

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

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

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

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

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

        <activity
            android:name="com.luxand.facerecognition.MainActivity"
            android:screenOrientation="portrait"/>
    </application>

</manifest>

kemudian anda buat design bagian layout dengan code xml berikut

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

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

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1.5dp"
            android:background="@color/colorAccent" />

    </LinearLayout>

    <RelativeLayout
        android:id="@+id/footer"
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:layout_alignParentBottom="true"
        android:background="@color/colorPrimary"
        android:orientation="horizontal">

        <View
            android:layout_width="match_parent"
            android:layout_height="1.5dp"
            android:background="@color/colorAccent" />

        <LinearLayout
            android:layout_marginTop="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="#ffffff"
            android:textSize="15dp"
            android:text="@string/wajah"/>

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

Kemudian anda buat class FSDK sebelum package

package com.luxand;

public class FSDK
{
	static {
		System.loadLibrary("stlport_shared");
		System.loadLibrary("fsdk");
	}

	public static final int FSDKE_OK = 0;
	public static final int FSDKE_FAILED = -1;
	public static final int FSDKE_NOT_ACTIVATED = -2;
	public static final int FSDKE_OUT_OF_MEMORY	= -3;
	public static final int FSDKE_INVALID_ARGUMENT = -4;
	public static final int FSDKE_IO_ERROR = -5;
	public static final int FSDKE_IMAGE_TOO_SMALL = -6;
	public static final int FSDKE_FACE_NOT_FOUND = -7;
	public static final int FSDKE_INSUFFICIENT_BUFFER_SIZE = -8;
	public static final int FSDKE_UNSUPPORTED_IMAGE_EXTENSION =	-9;
	public static final int FSDKE_CANNOT_OPEN_FILE = -10;
	public static final int FSDKE_CANNOT_CREATE_FILE = -11;
	public static final int FSDKE_BAD_FILE_FORMAT = -12;
	public static final int FSDKE_FILE_NOT_FOUND = -13;
	public static final int FSDKE_CONNECTION_CLOSED = -14;
	public static final int FSDKE_CONNECTION_FAILED = -15;
	public static final int FSDKE_IP_INIT_FAILED = -16;
	public static final int FSDKE_NEED_SERVER_ACTIVATION = -17;
    public static final int FSDKE_ID_NOT_FOUND = -18;
    public static final int FSDKE_ATTRIBUTE_NOT_DETECTED = -19;
    public static final int FSDKE_INSUFFICIENT_TRACKER_MEMORY_LIMIT = -20;
    public static final int FSDKE_UNKNOWN_ATTRIBUTE = -21;
    public static final int FSDKE_UNSUPPORTED_FILE_VERSION = -22;
    public static final int FSDKE_SYNTAX_ERROR = -23;
    public static final int FSDKE_PARAMETER_NOT_FOUND = -24;
	public static final int FSDKE_INVALID_TEMPLATE = -25;
	public static final int FSDKE_UNSUPPORTED_TEMPLATE_VERSION = -26;
	public static final int FSDKE_CAMERA_INDEX_DOES_NOT_EXIST = -27;

	// Facial feature count
	
	public static final int FSDK_FACIAL_FEATURE_COUNT = 70;
	
	// Types
	
    public static class FSDK_VIDEOCOMPRESSIONTYPE {
        public static final int FSDK_MJPEG = 0;
        public int type;
    }
	
	public static class FSDK_IMAGEMODE {
		public static final int FSDK_IMAGE_GRAYSCALE_8BIT = 0;
		public static final int FSDK_IMAGE_COLOR_24BIT = 1;
		public static final int FSDK_IMAGE_COLOR_32BIT = 2;
		public int mode;
	}
	
	public static class HImage {  //to pass himage "by reference"
		protected int himage;
	}
	
	public static class HCamera {  //to pass hcamera "by reference"
		protected int hcamera;
	}
	
	public static class HTracker {
		protected int htracker;
	}
	
	public static class TFacePosition {
		public int xc, yc, w;
		public int padding;
		public double angle;
	}

	public static class TFaces {
		public TFacePosition faces[];
		public int maxFaces;
		public TFaces(){
			maxFaces = 100;
			faces = null;
		}
		public TFaces(int MaxFaces){
			maxFaces = MaxFaces;
			faces = null;
		}
	}
	
	public static class TPoint {
		public int x, y;
	}

	public static class FSDK_Features {
		public TPoint features[] = new TPoint[FSDK_FACIAL_FEATURE_COUNT];
	}
	
	public static class FSDK_FaceTemplate {
		public byte template[] = new byte[13324];
	}

	// Facial features

	public static final int FSDKP_LEFT_EYE = 0;
	public static final int FSDKP_RIGHT_EYE	= 1;
	public static final int FSDKP_LEFT_EYE_INNER_CORNER =	24;
	public static final int FSDKP_LEFT_EYE_OUTER_CORNER =	23;
	public static final int FSDKP_LEFT_EYE_LOWER_LINE1 =	38;
	public static final int FSDKP_LEFT_EYE_LOWER_LINE2 =	27;
	public static final int FSDKP_LEFT_EYE_LOWER_LINE3 =	37;
	public static final int FSDKP_LEFT_EYE_UPPER_LINE1 =	35;
	public static final int FSDKP_LEFT_EYE_UPPER_LINE2 =	28;
	public static final int FSDKP_LEFT_EYE_UPPER_LINE3 =	36;
	public static final int FSDKP_LEFT_EYE_LEFT_IRIS_CORNER =	29;
	public static final int FSDKP_LEFT_EYE_RIGHT_IRIS_CORNER =	30;
	public static final int FSDKP_RIGHT_EYE_INNER_CORNER =	25;
	public static final int FSDKP_RIGHT_EYE_OUTER_CORNER =	26;
	public static final int FSDKP_RIGHT_EYE_LOWER_LINE1 =	41;
	public static final int FSDKP_RIGHT_EYE_LOWER_LINE2 =	31;
	public static final int FSDKP_RIGHT_EYE_LOWER_LINE3 =	42;
	public static final int FSDKP_RIGHT_EYE_UPPER_LINE1 =	40;
	public static final int FSDKP_RIGHT_EYE_UPPER_LINE2 =	32;
	public static final int FSDKP_RIGHT_EYE_UPPER_LINE3 =	39;
	public static final int FSDKP_RIGHT_EYE_LEFT_IRIS_CORNER =	33;
	public static final int FSDKP_RIGHT_EYE_RIGHT_IRIS_CORNER =	34;
	public static final int FSDKP_LEFT_EYEBROW_INNER_CORNER	 = 13;
	public static final int FSDKP_LEFT_EYEBROW_MIDDLE =	16;
	public static final int FSDKP_LEFT_EYEBROW_MIDDLE_LEFT =	18;
	public static final int FSDKP_LEFT_EYEBROW_MIDDLE_RIGHT	= 19;
	public static final int FSDKP_LEFT_EYEBROW_OUTER_CORNER	= 12;
	public static final int FSDKP_RIGHT_EYEBROW_INNER_CORNER =	14;
	public static final int FSDKP_RIGHT_EYEBROW_MIDDLE =	17;
	public static final int FSDKP_RIGHT_EYEBROW_MIDDLE_LEFT =	20;
	public static final int FSDKP_RIGHT_EYEBROW_MIDDLE_RIGHT =	21;
	public static final int FSDKP_RIGHT_EYEBROW_OUTER_CORNER =	15;
	public static final int FSDKP_NOSE_TIP =	2;
	public static final int FSDKP_NOSE_BOTTOM =	49;
	public static final int FSDKP_NOSE_BRIDGE =	22;
	public static final int FSDKP_NOSE_LEFT_WING =	43;
	public static final int FSDKP_NOSE_LEFT_WING_OUTER =	45;
	public static final int FSDKP_NOSE_LEFT_WING_LOWER =	47;
	public static final int FSDKP_NOSE_RIGHT_WING =	44;
	public static final int FSDKP_NOSE_RIGHT_WING_OUTER =	46;
	public static final int FSDKP_NOSE_RIGHT_WING_LOWER =	48;
	public static final int FSDKP_MOUTH_RIGHT_CORNER =	3;
	public static final int FSDKP_MOUTH_LEFT_CORNER	= 4;
	public static final int FSDKP_MOUTH_TOP	= 54;
	public static final int FSDKP_MOUTH_TOP_INNER	= 61;
	public static final int FSDKP_MOUTH_BOTTOM =	55;
	public static final int FSDKP_MOUTH_BOTTOM_INNER =	64;
	public static final int FSDKP_MOUTH_LEFT_TOP =	56;
	public static final int FSDKP_MOUTH_LEFT_TOP_INNER =	60;
	public static final int FSDKP_MOUTH_RIGHT_TOP =	57;
	public static final int FSDKP_MOUTH_RIGHT_TOP_INNER =	62;
	public static final int FSDKP_MOUTH_LEFT_BOTTOM =	58;
	public static final int FSDKP_MOUTH_LEFT_BOTTOM_INNER =	63;
	public static final int FSDKP_MOUTH_RIGHT_BOTTOM =	59;
	public static final int FSDKP_MOUTH_RIGHT_BOTTOM_INNER =	65;
	public static final int FSDKP_NASOLABIAL_FOLD_LEFT_UPPER =	50;
	public static final int FSDKP_NASOLABIAL_FOLD_LEFT_LOWER =	52;
	public static final int FSDKP_NASOLABIAL_FOLD_RIGHT_UPPER =	51;
	public static final int FSDKP_NASOLABIAL_FOLD_RIGHT_LOWER =	53;
	public static final int FSDKP_CHIN_BOTTOM =	11;
	public static final int FSDKP_CHIN_LEFT =	9;
	public static final int FSDKP_CHIN_RIGHT =	10;
	public static final int FSDKP_FACE_CONTOUR1 =	7;
	public static final int FSDKP_FACE_CONTOUR2 =	5;
	public static final int FSDKP_FACE_CONTOUR12 =	6;
	public static final int FSDKP_FACE_CONTOUR13 =	8;	
	public static final int FSDKP_FACE_CONTOUR14 =	66;
	public static final int FSDKP_FACE_CONTOUR15 =	67;
	public static final int FSDKP_FACE_CONTOUR16 =	68;
	public static final int FSDKP_FACE_CONTOUR17 =	69;	

	public static native int ActivateLibrary(String LicenseKey);
	//public static native int GetHardware_ID(String HardwareID[]); //not implemented
	public static native int GetLicenseInfo(String LicenseInfo[]);
	public static native int SetNumThreads(int Num);
	public static native int GetNumThreads(int Num[]);   
	public static native int Initialize();
	public static native int Finalize();
	
	public static native int CreateEmptyImage(HImage Image);
	public static native int FreeImage(HImage Image);
	
	public static native int LoadImageFromFile(HImage Image, String FileName);
	public static native int LoadImageFromFileWithAlpha(HImage Image, String FileName);
	public static native int SaveImageToFile(HImage Image, String FileName);
	public static native int SetJpegCompressionQuality(int Quality);
	public static native int GetImageWidth(HImage Image, int Width[]);
	public static native int GetImageHeight(HImage Image, int Height[]);
	public static native int LoadImageFromBuffer(HImage Image, byte Buffer[], int Width, int Height, int ScanLine, FSDK_IMAGEMODE ImageMode);
	public static native int GetImageBufferSize(HImage Image, int BufSize [], FSDK_IMAGEMODE ImageMode);
	public static native int SaveImageToBuffer(HImage Image, byte Buffer[], FSDK_IMAGEMODE ImageMode);
	public static native int LoadImageFromJpegBuffer(HImage Image, byte Buffer[], int BufferLength);
	public static native int LoadImageFromPngBuffer(HImage Image, byte Buffer[], int BufferLength);
	public static native int LoadImageFromPngBufferWithAlpha(HImage Image, byte Buffer[], int BufferLength);
	   
	public static native int DetectFace(HImage Image, TFacePosition FacePosition);
	public static native int DetectMultipleFaces(HImage Image, TFaces FacePositions); 
	public static native int SetFaceDetectionParameters(boolean HandleArbitraryRotations, boolean DetermineFaceRotationAngle, int InternalResizeWidth);
	public static native int SetFaceDetectionThreshold(int Threshold);
	public static native int GetDetectedFaceConfidence(int Confidence[]);
	
	public static native int DetectFacialFeatures(HImage Image, FSDK_Features FacialFeatures);
	public static native int DetectFacialFeaturesInRegion(HImage Image, TFacePosition FacePosition, FSDK_Features FacialFeatures);
	public static native int DetectEyes(HImage Image, FSDK_Features Eyes);
	public static native int DetectEyesInRegion(HImage Image, TFacePosition FacePosition, FSDK_Features Eyes);

	public static native int CopyImage(HImage SourceImage, HImage DestImage);
	public static native int ResizeImage(HImage SourceImage, double ratio, HImage DestImage);
	public static native int RotateImage90(HImage SourceImage, int Multiplier, HImage DestImage);
	public static native int RotateImage(HImage SourceImage, double angle, HImage DestImage);
	public static native int RotateImageCenter(HImage SourceImage, double angle, double xCenter, double yCenter, HImage DestImage);
	public static native int CopyRect(HImage SourceImage, int x1, int y1, int x2, int y2, HImage DestImage);
	public static native int CopyRectReplicateBorder(HImage SourceImage, int x1, int y1, int x2, int y2, HImage DestImage);
	public static native int MirrorImage(HImage Image, boolean UseVerticalMirroringInsteadOfHorizontal);

	public static native int ExtractFaceImage(HImage Image, FSDK_Features FacialFeatures, int Width, int Height, HImage ExtractedFaceImage, FSDK_Features ResizedFeatures);

	public static native int GetFaceTemplate(HImage Image, FSDK_FaceTemplate FaceTemplate);
	public static native int GetFaceTemplateInRegion(HImage Image, TFacePosition FacePosition, FSDK_FaceTemplate FaceTemplate);
    public static native int GetFaceTemplateUsingFeatures(HImage Image, FSDK_Features FacialFeatures, FSDK_FaceTemplate FaceTemplate);
    public static native int GetFaceTemplateUsingEyes(HImage Image, FSDK_Features EyeCoords, FSDK_FaceTemplate FaceTemplate);
	public static native int MatchFaces(FSDK_FaceTemplate FaceTemplate1, FSDK_FaceTemplate FaceTemplate2, float Similarity[]);
    public static native int GetMatchingThresholdAtFAR(float FARValue, float Threshold[]);
    public static native int GetMatchingThresholdAtFRR(float FRRValue, float Threshold[]);

    public static native int CreateTracker(HTracker Tracker);
    public static native int FreeTracker(HTracker Tracker);
    public static native int ClearTracker(HTracker Tracker);
    public static native int SetTrackerParameter(HTracker Tracker, String ParameterName, String ParameterValue);
    public static native int SetTrackerMultipleParameters(HTracker Tracker, String Parameters, int ErrorPosition[]);
	public static native int GetTrackerParameter(HTracker Tracker, String ParameterName, String ParameterValue[], int MaxSizeInBytes);
    public static native int FeedFrame(HTracker Tracker, long CameraIdx, HImage Image, long FaceCount[], long IDs[]);
    public static native int GetTrackerEyes(HTracker Tracker, long CameraIdx, long ID, FSDK_Features Eyes);  
	public static native int GetTrackerFacialFeatures(HTracker Tracker, long CameraIdx, long ID, FSDK_Features FacialFeatures); 
	public static native int GetTrackerFacePosition(HTracker Tracker, long CameraIdx, long ID, TFacePosition FacePosition);
    public static native int LockID(HTracker Tracker, long ID);
    public static native int UnlockID(HTracker Tracker, long ID);
    public static native int PurgeID(HTracker Tracker, long ID);
    public static native int SetName(HTracker Tracker, long ID, String Name);
    public static native int GetName(HTracker Tracker, long ID, String Name[], long MaxSizeInBytes);
    public static native int GetAllNames(HTracker Tracker, long ID, String Names[], long MaxSizeInBytes);
    public static native int GetIDReassignment(HTracker Tracker, long ID, long ReassignedID[]);
	public static native int GetSimilarIDCount(HTracker Tracker, long ID, long Count[]);
	public static native int GetSimilarIDList(HTracker Tracker, long ID, long SimilarIDList[]);
	
    public static native int SaveTrackerMemoryToFile(HTracker Tracker, String FileName);
    public static native int LoadTrackerMemoryFromFile(HTracker Tracker, String FileName);
	public static native int GetTrackerMemoryBufferSize(HTracker Tracker, long [] BufSize);
	public static native int SaveTrackerMemoryToBuffer(HTracker Tracker, byte Buffer[]);
	public static native int LoadTrackerMemoryFromBuffer(HTracker Tracker, byte Buffer[]);

	public static native int GetTrackerFacialAttribute(HTracker Tracker, long CameraIdx, long ID, String AttributeName, String AttributeValues[], long MaxSizeInBytes);
	public static native int DetectFacialAttributeUsingFeatures(HImage Image, FSDK_Features FacialFeatures, String AttributeName, String AttributeValues[], long MaxSizeInBytes);
	public static native int GetValueConfidence(String AttributeValues, String Value, float Confidence[]);

	public static native int SetHTTPProxy(String ServerNameOrIPAddress, short Port, String UserName, String Password);
	public static native int OpenIPVideoCamera(FSDK_VIDEOCOMPRESSIONTYPE CompressionType, String URL, String Username, String Password, int TimeoutSeconds, HCamera CameraHandle);
	public static native int CloseVideoCamera(HCamera CameraHandle);
	public static native int GrabFrame(HCamera CameraHandle, HImage Image);
	public static native int InitializeCapturing();
	public static native int FinalizeCapturing();
}

Bagian package anda buat beberapa class:

Class Face Rectangle

Membuat object dalam bentuk rectangle dimana muncul saat pertama kali wajah diseleksi, dimana class ini hanya berisikan variabel untuk menampung ukuran yang dilakukan proses dari class ProcessImageAndDrawResults

class FaceRectangle {
    int x1, y1, x2, y2;
}

Class PrProcess Image And Draw Resultse view

Proses dimana untuk membuat gambar object dari rectangle

// Draw graphics on top of the video
class ProcessImageAndDrawResults extends View {
    public HTracker mTracker;

    final int MAX_FACES = 50;
    final FaceRectangle[] mFacePositions = new FaceRectangle[MAX_FACES];
    final long[] mIDs = new long[MAX_FACES];
    final Lock faceLock = new ReentrantLock();
    int mTouchedIndex;
    long mTouchedID;
    int mStopping;
    int mStopped;

    Context mContext;
    Paint mPaintGreen, mPaintBlue, mPaintBlueTransparent;
    byte[] mYUVData;
    byte[] mRGBData;
    int mImageWidth, mImageHeight;
    boolean first_frame_saved;
    boolean rotated;

    int GetFaceFrame(FSDK.FSDK_Features Features, FaceRectangle fr) {
        if (Features == null || fr == null)
            return FSDK.FSDKE_INVALID_ARGUMENT;

        float u1 = Features.features[0].x;
        float v1 = Features.features[0].y;
        float u2 = Features.features[1].x;
        float v2 = Features.features[1].y;
        float xc = (u1 + u2) / 2;
        float yc = (v1 + v2) / 2;
        int w = (int) Math.pow((u2 - u1) * (u2 - u1) + (v2 - v1) * (v2 - v1), 0.5);

        fr.x1 = (int) (xc - w * 1.6 * 0.9);
        fr.y1 = (int) (yc - w * 1.1 * 0.9);
        fr.x2 = (int) (xc + w * 1.6 * 0.9);
        fr.y2 = (int) (yc + w * 2.1 * 0.9);
        if (fr.x2 - fr.x1 > fr.y2 - fr.y1) {
            fr.x2 = fr.x1 + fr.y2 - fr.y1;
        } else {
            fr.y2 = fr.y1 + fr.x2 - fr.x1;
        }
        return 0;
    }


    public ProcessImageAndDrawResults(Context context) {
        super(context);

        mTouchedIndex = -1;

        mStopping = 0;
        mStopped = 0;
        rotated = false;
        mContext = context;
        mPaintGreen = new Paint();
        mPaintGreen.setStyle(Paint.Style.FILL);
        mPaintGreen.setColor(Color.parseColor("#FF4081"));
        mPaintGreen.setTextSize(18 * MainActivity.sDensity);
        mPaintGreen.setTextAlign(Align.CENTER);
        mPaintBlue = new Paint();
        mPaintBlue.setStyle(Paint.Style.FILL);
        mPaintBlue.setColor(Color.parseColor("#0488d1"));
        mPaintBlue.setTextSize(18 * MainActivity.sDensity);
        mPaintBlue.setTextAlign(Align.CENTER);

        mPaintBlueTransparent = new Paint();
        mPaintBlueTransparent.setStyle(Paint.Style.STROKE);
        mPaintBlueTransparent.setStrokeWidth(2);
        mPaintBlueTransparent.setColor(Color.parseColor("#0488d1"));
        mPaintBlueTransparent.setTextSize(25);

        //mBitmap = null;
        mYUVData = null;
        mRGBData = null;

        first_frame_saved = false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mStopping == 1) {
            mStopped = 1;
            super.onDraw(canvas);
            return;
        }

        if (mYUVData == null || mTouchedIndex != -1) {
            super.onDraw(canvas);
            return; //nothing to process or name is being entered now
        }

        int canvasWidth = canvas.getWidth();
        //int canvasHeight = canvas.getHeight();

        // Convert from YUV to RGB
        decodeYUV420SP(mRGBData, mYUVData, mImageWidth, mImageHeight);

        // Load image to FaceSDK
        FSDK.HImage Image = new FSDK.HImage();
        FSDK.FSDK_IMAGEMODE imagemode = new FSDK.FSDK_IMAGEMODE();
        imagemode.mode = FSDK.FSDK_IMAGEMODE.FSDK_IMAGE_COLOR_24BIT;
        FSDK.LoadImageFromBuffer(Image, mRGBData, mImageWidth, mImageHeight, mImageWidth * 3, imagemode);
        FSDK.MirrorImage(Image, false);
        FSDK.HImage RotatedImage = new FSDK.HImage();
        FSDK.CreateEmptyImage(RotatedImage);

        //it is necessary to work with local variables (onDraw called not the time when mImageWidth,... being reassigned, so swapping mImageWidth and mImageHeight may be not safe)
        int ImageWidth = mImageWidth;
        //int ImageHeight = mImageHeight;
        if (rotated) {
            ImageWidth = mImageHeight;
            //ImageHeight = mImageWidth;
            FSDK.RotateImage90(Image, -1, RotatedImage);
        } else {
            FSDK.CopyImage(Image, RotatedImage);
        }
        FSDK.FreeImage(Image);

        // Save first frame to gallery to debug (e.g. rotation angle)

        long IDs[] = new long[MAX_FACES];
        long face_count[] = new long[1];

        FSDK.FeedFrame(mTracker, 0, RotatedImage, face_count, IDs);
        FSDK.FreeImage(RotatedImage);

        faceLock.lock();

        for (int i = 0; i < MAX_FACES; ++i) {
            mFacePositions[i] = new FaceRectangle();
            mFacePositions[i].x1 = 0;
            mFacePositions[i].y1 = 0;
            mFacePositions[i].x2 = 0;
            mFacePositions[i].y2 = 0;
            mIDs[i] = IDs[i];
        }

        float ratio = (canvasWidth * 1.0f) / ImageWidth;
        for (int i = 0; i < (int) face_count[0]; ++i) {
            FSDK.FSDK_Features Eyes = new FSDK.FSDK_Features();
            FSDK.GetTrackerEyes(mTracker, 0, mIDs[i], Eyes);

            GetFaceFrame(Eyes, mFacePositions[i]);
            mFacePositions[i].x1 *= ratio;
            mFacePositions[i].y1 *= ratio;
            mFacePositions[i].x2 *= ratio;
            mFacePositions[i].y2 *= ratio;
        }

        faceLock.unlock();

        int shift = (int) (22 * MainActivity.sDensity);

        // Mark and name faces
        for (int i = 0; i < face_count[0]; ++i) {
            canvas.drawRect(mFacePositions[i].x1, mFacePositions[i].y1, mFacePositions[i].x2, mFacePositions[i].y2, mPaintBlueTransparent);

            boolean named = false;
            if (IDs[i] != -1) {
                String names[] = new String[1];
                FSDK.GetAllNames(mTracker, IDs[i], names, 1024);
                if (names[0] != null && names[0].length() > 0) {
                    canvas.drawText(names[0], (mFacePositions[i].x1 + mFacePositions[i].x2) / 2, mFacePositions[i].y2 + shift, mPaintBlue);
                    named = true;
                }
            }
            if (!named) {
                canvas.drawText("Tidak di kenal", (mFacePositions[i].x1 + mFacePositions[i].x2) / 2, mFacePositions[i].y2 + shift, mPaintGreen);
            }
        }
        super.onDraw(canvas);
    } // end onDraw method


    @Override
    public boolean onTouchEvent(MotionEvent event) { //NOTE: the method can be implemented in Preview class
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                int x = (int) event.getX();
                int y = (int) event.getY();

                faceLock.lock();
                FaceRectangle rects[] = new FaceRectangle[MAX_FACES];
                long IDs[] = new long[MAX_FACES];
                for (int i = 0; i < MAX_FACES; ++i) {
                    rects[i] = new FaceRectangle();
                    rects[i].x1 = mFacePositions[i].x1;
                    rects[i].y1 = mFacePositions[i].y1;
                    rects[i].x2 = mFacePositions[i].x2;
                    rects[i].y2 = mFacePositions[i].y2;
                    IDs[i] = mIDs[i];
                }
                faceLock.unlock();

                for (int i = 0; i < MAX_FACES; ++i) {
                    if (rects[i] != null && rects[i].x1 <= x && x <= rects[i].x2 && rects[i].y1 <= y && y <= rects[i].y2 + 30) {
                        mTouchedID = IDs[i];

                        mTouchedIndex = i;

                        // requesting name on tapping the face
                        final EditText input = new EditText(mContext);
                        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                        builder.setMessage("Masukan Identitas")
                                .setView(input)
                                .setPositiveButton("Simpan", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int j) {
                                        FSDK.LockID(mTracker, mTouchedID);
                                        String userName = input.getText().toString();
                                        FSDK.SetName(mTracker, mTouchedID, userName);
                                        if (userName.length() <= 0)
                                            FSDK.PurgeID(mTracker, mTouchedID);
                                        FSDK.UnlockID(mTracker, mTouchedID);
                                        mTouchedIndex = -1;
                                    }
                                })
                                .setNegativeButton("Batal", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int j) {
                                        mTouchedIndex = -1;
                                    }
                                })
                                .setCancelable(false) // cancel with button only
                                .show();

                        break;
                    }
                }
        }
        return true;
    }

    static public void decodeYUV420SP(byte[] rgb, byte[] yuv420sp, int width, int height) {
        final int frameSize = width * height;
        int yp = 0;
        for (int j = 0; j < height; j++) {
            int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
            for (int i = 0; i < width; i++) {
                int y = (0xff & ((int) yuv420sp[yp])) - 16;
                if (y < 0) y = 0;
                if ((i & 1) == 0) {
                    v = (0xff & yuv420sp[uvp++]) - 128;
                    u = (0xff & yuv420sp[uvp++]) - 128;
                }
                int y1192 = 1192 * y;
                int r = (y1192 + 1634 * v);
                int g = (y1192 - 833 * v - 400 * u);
                int b = (y1192 + 2066 * u);
                if (r < 0) r = 0;
                else if (r > 262143) r = 262143;
                if (g < 0) g = 0;
                else if (g > 262143) g = 262143;
                if (b < 0) b = 0;
                else if (b > 262143) b = 262143;

                rgb[3 * yp] = (byte) ((r >> 10) & 0xff);
                rgb[3 * yp + 1] = (byte) ((g >> 10) & 0xff);
                rgb[3 * yp + 2] = (byte) ((b >> 10) & 0xff);
                ++yp;
            }
        }
    }
} // end of ProcessImageAndDrawResults class

Class Preview

Menampilkan proses vidio dari camera sekaligus melakukan deteksi face beserta menampilkan hasil dari class ProcessImageAndDrawResults.

@SuppressLint("ViewConstructor")
class Preview extends SurfaceView implements SurfaceHolder.Callback {
    Context mContext;
    SurfaceHolder mHolder;
    Camera mCamera;
    ProcessImageAndDrawResults mDraw;
    boolean mFinished;

    Preview(Context context, ProcessImageAndDrawResults draw) {
        super(context);
        mContext = context;
        mDraw = draw;

        //Install a SurfaceHolder.Callback so we get notified when the underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    //SurfaceView callback
    public void surfaceCreated(SurfaceHolder holder) {
        mFinished = false;

        // Find the ID of the camera
        int cameraId = 1;
        boolean frontCameraFound = false;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            Camera.getCameraInfo(i, cameraInfo);
//			ganti kamera belakang dan depan
//            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                cameraId = i;
                frontCameraFound = true;
            }
        }

        if (frontCameraFound) {
            mCamera = Camera.open(cameraId);
        } else {
            mCamera = Camera.open();
        }

        try {
            mCamera.setPreviewDisplay(holder);

            // Preview callback used whenever new viewfinder frame is available
            mCamera.setPreviewCallback(new PreviewCallback() {
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if ((mDraw == null) || mFinished)
                        return;

                    if (mDraw.mYUVData == null) {
                        // Initialize the draw-on-top companion
                        Camera.Parameters params = camera.getParameters();
                        mDraw.mImageWidth = params.getPreviewSize().width;
                        mDraw.mImageHeight = params.getPreviewSize().height;
                        mDraw.mRGBData = new byte[3 * mDraw.mImageWidth * mDraw.mImageHeight];
                        mDraw.mYUVData = new byte[data.length];
                    }

                    // Pass YUV data to draw-on-top companion
                    System.arraycopy(data, 0, mDraw.mYUVData, 0, data.length);
                    mDraw.invalidate();
                }
            });
        } catch (Exception exception) {
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage("Cannot open camera")
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            android.os.Process.killProcess(android.os.Process.myPid());
                        }
                    })
                    .show();
            if (mCamera != null) {
                mCamera.release();
                mCamera = null;
            }
        }
    }

    //SurfaceView callback
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        // Because the CameraDevice object is not a shared resource, it's very
        // important to release it when the activity is paused.
        mFinished = true;
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    //SurfaceView callback, configuring camera
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mCamera == null) return;

        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        Camera.Parameters parameters = mCamera.getParameters();

        //Keep uncommented to work correctly on phones:
        //This is an undocumented although widely known feature
        /**/
        if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            parameters.set("orientation", "portrait");
            mCamera.setDisplayOrientation(90); // For Android 2.2 and above
            mDraw.rotated = true;
        } else {
            parameters.set("orientation", "landscape");
            mCamera.setDisplayOrientation(0); // For Android 2.2 and above
        }
        /**/

        // choose preview size closer to 640x480 for optimal performance
        List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
        int width = 0;
        int height = 0;
        for (Size s : supportedSizes) {
            if ((width - 640) * (width - 640) + (height - 480) * (height - 480) >
                    (s.width - 640) * (s.width - 640) + (s.height - 480) * (s.height - 480)) {
                width = s.width;
                height = s.height;
            }
        }

        //try to set preferred parameters
        try {
            if (width * height > 0) {
                parameters.setPreviewSize(width, height);
            }
            //parameters.setPreviewFrameRate(10);
            parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT);
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            mCamera.setParameters(parameters);
        } catch (Exception ex) {
        }
        mCamera.startPreview();

        parameters = mCamera.getParameters();
        Camera.Size previewSize = parameters.getPreviewSize();
        makeResizeForCameraAspect(1.0f / ((1.0f * previewSize.width) / previewSize.height));
    }

    private void makeResizeForCameraAspect(float cameraAspectRatio) {
        LayoutParams layoutParams = this.getLayoutParams();
        int matchParentWidth = this.getWidth();
        int newHeight = (int) (matchParentWidth / cameraAspectRatio);
        if (newHeight != layoutParams.height) {
            layoutParams.height = newHeight;
            layoutParams.width = matchParentWidth;
            this.setLayoutParams(layoutParams);
            this.invalidate();
        }
    }
}

Class MainActivity

Mendeklarisakan Luxand FaceSDK beserta memanggil class berupa ProcessImageAndDrawResults dan class Preview

public class MainActivity extends AppCompatActivity {

    private boolean mIsFailed = false;
    private Preview mPreview;
    private ProcessImageAndDrawResults mDraw;
    private final String database = "Memory50.dat";
    public static float sDensity = 1.0f;

    private void resetTrackerParameters() {
        int errpos[] = new int[1];
        FSDK.SetTrackerMultipleParameters(mDraw.mTracker, "ContinuousVideoFeed=true;FacialFeatureJitterSuppression=0;RecognitionPrecision=1;Threshold=0.996;Threshold2=0.9995;ThresholdFeed=0.97;MemoryLimit=2000;HandleArbitraryRotations=false;DetermineFaceRotationAngle=false;InternalResizeWidth=70;FaceDetectionThreshold=3;", errpos);
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sDensity = getResources().getDisplayMetrics().scaledDensity;

        int res = FSDK.ActivateLibrary(getString(R.string.LISENSE_KEY));

        if (res != FSDK.FSDKE_OK) {
            mIsFailed = true;
        } else {
            FSDK.Initialize();

            // Lock orientation
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

            // Camera layer and drawing layer
            mDraw = new ProcessImageAndDrawResults(this);
            mPreview = new Preview(this, mDraw);
            mDraw.mTracker = new HTracker();
            String templatePath = this.getApplicationInfo().dataDir + "/" + database;
            if (FSDK.FSDKE_OK != FSDK.LoadTrackerMemoryFromFile(mDraw.mTracker, templatePath)) {
                res = FSDK.CreateTracker(mDraw.mTracker);
                if (FSDK.FSDKE_OK != res) {
//                    showErrorAndClose("Error creating tracker", res);
                }
            }

            resetTrackerParameters();

            setContentView(mPreview); //creates MainActivity contents
            addContentView(mDraw, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
//
//            // Menu
            LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View buttons = inflater.inflate(R.layout.activity_wajah, null);

            Toolbar toolbar = (Toolbar) buttons.findViewById(R.id.toolbar);
            toolbar.setTitle(getString(R.string.app_name));
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            addContentView(buttons, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == android.R.id.home) {
            finish();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPause() {
        super.onPause();
        pauseProcessingFrames();
        String templatePath = this.getApplicationInfo().dataDir + "/" + database;
        FSDK.SaveTrackerMemoryToFile(mDraw.mTracker, templatePath);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mIsFailed)
            return;
        resumeProcessingFrames();
    }

    private void pauseProcessingFrames() {
        mDraw.mStopping = 1;

        // It is essential to limit wait time, because mStopped will not be set to 0, if no frames are feeded to mDraw
        for (int i = 0; i < 100; ++i) {
            if (mDraw.mStopped != 0) break;
            try {
                Thread.sleep(10);
            } catch (Exception ex) {
            }
        }
    }

    private void resumeProcessingFrames() {
        mDraw.mStopped = 0;
        mDraw.mStopping = 0;
    }
}

Bagian Project

Masukan dependencies dibagian project yang anda buat

implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'de.hdodenhof:circleimageview:1.3.0'
implementation 'com.pixplicity.letterpress:library:1.0'
implementation project(':lib_wajah')

pada bagian lib_wajah itu merupakan dependencies yang anda buat, jika anda buat maka secara otomatis akan tertera dibagian dependencies.

Sebelum anda lanjutkan langkah selanjutnya anda harus download komponen seperti file di dalam folder jniLibs, yang merupakan bawaan dari library Luxand dan komponen didalam folder Resource


Download Resource


Manifests

Anda harus daftarkan class dan tambahkan beberapa uses permission seperti berikut

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

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

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

        <activity
            android:name=".Flash"
            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=".MainAktivity" />

        <activity
            android:name=".ShowLinkURL"
            android:theme="@style/AppTheme.NoActionBar"/>

        <activity
            android:name=".Tentang"
            android:theme="@style/AppTheme.NoActionBar"/>

    </application>
</manifest>

Membuat class MainActivity

Berfungsi sebagai menu

package com.codetr.tanwir.deteksiface;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

/**
 * Created by Tanwir on 18/09/2019.
 */

public class MainAktivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainAktivity.this, Tentang.class));
            }
        });

        Button btnWajah = findViewById(R.id.btnWajah);
        btnWajah.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainAktivity.this, com.luxand.facerecognition.MainActivity.class));
            }
        });

        Button btnKelamin = findViewById(R.id.btnKelamin);
        btnKelamin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainAktivity.this, com.luxand.genderrecognition.MainActivity.class));
            }
        });

        TextView btnBelajar= findViewById(R.id.btnBelajar);
        btnBelajar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent urlSend = new Intent(MainAktivity.this, ShowLinkURL.class);
                urlSend.putExtra("Url","http://opencv.org/");
                startActivity(urlSend);
            }
        });
    }
}

Membuat class ShowLinkURL

Berfungsi menampilkan halaman web dalam webview

package com.codetr.tanwir.deteksiface

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient

import androidx.appcompat.app.AppCompatActivity

/**
 * Created by Tanwir on 18/09/2019.
 */
class ShowLinkURL : AppCompatActivity() {

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_show_link_url)

        val i = intent
        val url = i.getStringExtra("Url")

        val webView = findViewById<View>(R.id.webView) as WebView
        webView.settings.javaScriptEnabled = true
        webView.webViewClient = MyBrowser()
        webView.loadUrl(url)
    }

    private inner class MyBrowser : WebViewClient() {
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            view.loadUrl(url)
            return true
        }
    }
}

Membuat class Flash

Berfungsi menampilkan halaman utama

package com.codetr.tanwir.deteksiface;

import android.content.Intent;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

/**
 * Created by Tanwir on 18/09/2019.
 */
public class Flash extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flash);

        Thread time = new Thread() {
            public void run() {
                try {
                    sleep(8000);
                    Intent intent = new Intent(Flash.this, MainAktivity.class);
                    startActivity(intent);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    finish();
                }
            }
        };
        time.start();
    }
}

Membuat class Tentang

Berfungsi menampilkan keterangan tentang aplikasi

package com.codetr.tanwir.deteksiface

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout

import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar

/**
 * Created by Tanwir on 18/09/2019.
 */
class Tentang : AppCompatActivity() {

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

        val toolbar = findViewById<View>(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        toolbar.setNavigationOnClickListener { finish() }

        val ab = supportActionBar
        if (ab != null) {
            ab.title = "Deteksi Wajah"
            ab.setDisplayHomeAsUpEnabled(true)
        }

        val facebook = findViewById<View>(R.id.facebook) as LinearLayout
        facebook.setOnClickListener {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.facebook.com/74nwi12"))
            startActivity(intent)
        }
        val blog = findViewById<View>(R.id.blog) as LinearLayout
        blog.setOnClickListener {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://kodetr.com"))
            startActivity(intent)
        }
    }
}

Jika anda sudah mengikuti artikel ini sesuai intruksi dari vidio maka anda berhasil membuat aplikasi Deteksi Wajah.

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