Friday, August 14, 2015

Face Detection with Google Play services, Mobile Vision API (with demo APK)

With the release of Google Play services 7.8, new Mobile Vision APIs was added, to include a new Face API that finds human faces in images and video. ~ source: Android Developers Blog - Face Detection in Google Play services.

Follow the code lab "Face Detection with the Mobile Vision API", you can create an App to process an image that is already present in your app, to detects faces.

In this exercise, I modify the code lab to add the feature to load photos using Intent of ACTION_GET_CONTENT, such that you can test it with your own photos. APK is available on the bottom of this post.


- Make sure you have Google Play Services 7.8 or higher installed on your testing devices (Check your installed Google Play Services version).

- Create your app of "Blank Activity", with Min Sdk Version of API 17 (Android 4.2.2) or higher.

- Add dependency for Google Play services in Android Studio Project, make sure you have Google Play Services version 26 or higher installed.

- Edit your AndroidManifest.xml to add the statement of <meta-data...>:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidfacedetection" >
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="com.google.android.gms.vision.DEPENDENCIES"
            android:value="face" />
    </application>

</manifest>


layout/activity_main.xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="18dp"
        android:text="Face Detection with the Mobile Vision API" />

    <Button
        android:id="@+id/btnLoad"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Photo"/>

    <Button
        android:id="@+id/btnDetectFace"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Detect Face" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imgview"/>

</LinearLayout>


com.example.androidfacedetection.MainActivity.java
package com.example.androidfacedetection;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.face.Face;
import com.google.android.gms.vision.face.FaceDetector;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    private static final int RQS_LOADIMAGE = 1;
    private Button btnLoad, btnDetFace;
    private ImageView imgView;
    private Bitmap myBitmap;

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

        btnLoad = (Button)findViewById(R.id.btnLoad);
        btnDetFace = (Button)findViewById(R.id.btnDetectFace);
        imgView = (ImageView)findViewById(R.id.imgview);

        btnLoad.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                startActivityForResult(intent, RQS_LOADIMAGE);
            }
        });

        btnDetFace.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                if(myBitmap == null){
                    Toast.makeText(MainActivity.this,
                            "myBitmap == null",
                            Toast.LENGTH_LONG).show();
                }else{
                    detectFace();
                    Toast.makeText(MainActivity.this,
                            "Done",
                            Toast.LENGTH_LONG).show();
                }
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == RQS_LOADIMAGE
                && resultCode == RESULT_OK){

            if(myBitmap != null){
                myBitmap.recycle();
            }

            try {
                InputStream inputStream =
                        getContentResolver().openInputStream(data.getData());
                myBitmap = BitmapFactory.decodeStream(inputStream);
                inputStream.close();
                imgView.setImageBitmap(myBitmap);

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    /*
    reference:
    https://search-codelabs.appspot.com/codelabs/face-detection
     */
    private void detectFace(){

        //Create a Paint object for drawing with
        Paint myRectPaint = new Paint();
        myRectPaint.setStrokeWidth(5);
        myRectPaint.setColor(Color.RED);
        myRectPaint.setStyle(Paint.Style.STROKE);

        //Create a Canvas object for drawing on
        Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth(), myBitmap.getHeight(), Bitmap.Config.RGB_565);
        Canvas tempCanvas = new Canvas(tempBitmap);
        tempCanvas.drawBitmap(myBitmap, 0, 0, null);

        //Detect the Faces
        FaceDetector faceDetector = new FaceDetector.Builder(getApplicationContext()).build();

        //!!!
        //Cannot resolve method setTrackingEnabled(boolean)
        //skip for now
        //faceDetector.setTrackingEnabled(false);

        Frame frame = new Frame.Builder().setBitmap(myBitmap).build();
        SparseArray<Face> faces = faceDetector.detect(frame);

        //Draw Rectangles on the Faces
        for(int i=0; i<faces.size(); i++) {
            Face thisFace = faces.valueAt(i);
            float x1 = thisFace.getPosition().x;
            float y1 = thisFace.getPosition().y;
            float x2 = x1 + thisFace.getWidth();
            float y2 = y1 + thisFace.getHeight();
            tempCanvas.drawRoundRect(new RectF(x1, y1, x2, y2), 2, 2, myRectPaint);
        }
        imgView.setImageDrawable(new BitmapDrawable(getResources(),tempBitmap));

    }
}


In my trial:
- The calling of faceDetector.setTrackingEnabled(false) report error of "Cannot resolve method setTrackingEnabled(boolean)"! So I skip it in this example.
(fixed in next post: "FaceDetector error: Cannot resolve method setTrackingEnabled(boolean)")

- It work on RedMi 2, running Android 4.4.4, with Google Play services version 7.8.99 installed.
- But cannot detect face on Nexus 7 2012 (WITHOUT front camera) running Android 5.1.1, with the same Google Play services version 7.8.99 installed! with warning of "FaceDetectorHandle﹕ Native face detector not yet available.  Reverting to no-op detection".

Test on RedMi 2, running Android 4.4.4, with Google Play services version 7.8.99 installed.


download filesDownload the files (Android Studio Format).

download filesYou can also Download the APK here, for test without coding.


Next:
FaceDetector error: Cannot resolve method setTrackingEnabled(boolean)
Google Play services Face Detection, get Landmarks (eyes, nose, etc.)
- Detect Smiling
Introducing Face Detection in the Google Vision APIs, from 100 Days of Google Dev

5 comments:

Unknown said...

I tried this but it is not installing the local libraries on my phone. Even when I run your apk I get W/FaceDetectorHandle﹕ Native face detector not yet available. Reverting to no-op detection. I have Google Play Services 7.8.99 and Android 5.1

Erik said...

hello Tyler Carberry,

Same case with my Nexus 7 2012!
What device you use?

Unknown said...

Moto X 2014

Unknown said...

It just installed the dependencies without me needed to do anything.

Unknown said...

It worked on nexus 7 4.3,
But it doesn't work on any other phone.
What is the problem ?!