Friday, April 20, 2012

Android 4 Face Detection: Display detected face area

Further works on last exercise "Face detection for Camera". Once face detected, the call-back method onFaceDetection() of FaceDetectionListener will be called with parameter of Face[], a list of face objects. The face object, Face, contain bounds of the face (rect).



A custom View, DrawingView, is added to draw rectangle to show the detected face area, over the Preview SurfaceView.

For rect, bounds of the face: (-1000, -1000) represents the top-left of the camera field of view, and (1000, 1000) represents the bottom-right of the field of view ~ Refer http://developer.android.com/reference/android/hardware/Camera.Face.html#rect.

So we have to convert from (-1000, -1000)~(1000, 1000) to (0, 0)~(width, height of the View) in onDraw() of DrawingView.

Modify the main code in AndroidCamera.java
package com.exercise.AndroidCamera;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Face;
import android.hardware.Camera.FaceDetectionListener;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class AndroidCamera extends Activity implements SurfaceHolder.Callback{

 Camera camera;
 SurfaceView surfaceView;
 SurfaceHolder surfaceHolder;
 boolean previewing = false;
 LayoutInflater controlInflater = null;
 
 Button buttonTakePicture;
 TextView prompt;
 
 DrawingView drawingView;
 Face[] detectedFaces;
 
 final int RESULT_SAVEIMAGE = 0;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        
        getWindow().setFormat(PixelFormat.UNKNOWN);
        surfaceView = (SurfaceView)findViewById(R.id.camerapreview);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        
        drawingView = new DrawingView(this);
        LayoutParams layoutParamsDrawing 
         = new LayoutParams(LayoutParams.FILL_PARENT, 
           LayoutParams.FILL_PARENT);
        this.addContentView(drawingView, layoutParamsDrawing);
        
        controlInflater = LayoutInflater.from(getBaseContext());
        View viewControl = controlInflater.inflate(R.layout.control, null);
        LayoutParams layoutParamsControl 
         = new LayoutParams(LayoutParams.FILL_PARENT, 
           LayoutParams.FILL_PARENT);
        this.addContentView(viewControl, layoutParamsControl);
        
        buttonTakePicture = (Button)findViewById(R.id.takepicture);
        buttonTakePicture.setOnClickListener(new Button.OnClickListener(){

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    camera.takePicture(myShutterCallback, 
      myPictureCallback_RAW, myPictureCallback_JPG);
   }});
        
        LinearLayout layoutBackground = (LinearLayout)findViewById(R.id.background);
        layoutBackground.setOnClickListener(new LinearLayout.OnClickListener(){

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub

    buttonTakePicture.setEnabled(false);
    camera.autoFocus(myAutoFocusCallback);
   }});
        
        prompt = (TextView)findViewById(R.id.prompt);
    }
    
    FaceDetectionListener faceDetectionListener
    = new FaceDetectionListener(){

  @Override
  public void onFaceDetection(Face[] faces, Camera camera) {
   
   if (faces.length == 0){
    prompt.setText(" No Face Detected! ");
    drawingView.setHaveFace(false);
   }else{
    prompt.setText(String.valueOf(faces.length) + " Face Detected :) ");
    drawingView.setHaveFace(true);
    detectedFaces = faces;
   }
   
   drawingView.invalidate();
   
  }};
    
    AutoFocusCallback myAutoFocusCallback = new AutoFocusCallback(){

  @Override
  public void onAutoFocus(boolean arg0, Camera arg1) {
   // TODO Auto-generated method stub
   buttonTakePicture.setEnabled(true);
  }};
    
    ShutterCallback myShutterCallback = new ShutterCallback(){

  @Override
  public void onShutter() {
   // TODO Auto-generated method stub
   
  }};
  
 PictureCallback myPictureCallback_RAW = new PictureCallback(){

  @Override
  public void onPictureTaken(byte[] arg0, Camera arg1) {
   // TODO Auto-generated method stub
   
  }};
  
 PictureCallback myPictureCallback_JPG = new PictureCallback(){

  @Override
  public void onPictureTaken(byte[] arg0, Camera arg1) {
   // TODO Auto-generated method stub
   /*Bitmap bitmapPicture 
    = BitmapFactory.decodeByteArray(arg0, 0, arg0.length); */
   
   Uri uriTarget = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues());

   OutputStream imageFileOS;
   try {
    imageFileOS = getContentResolver().openOutputStream(uriTarget);
    imageFileOS.write(arg0);
    imageFileOS.flush();
    imageFileOS.close();
    
    prompt.setText("Image saved: " + uriTarget.toString());
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

   camera.startPreview();
   camera.startFaceDetection();
  }};

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
   int height) {
  // TODO Auto-generated method stub
  if(previewing){
   camera.stopFaceDetection();
   camera.stopPreview();
   previewing = false;
  }
  
  if (camera != null){
   try {
    camera.setPreviewDisplay(surfaceHolder);
    camera.startPreview();

    prompt.setText(String.valueOf(
      "Max Face: " + camera.getParameters().getMaxNumDetectedFaces()));
    camera.startFaceDetection();
    previewing = true;
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }

 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  camera = Camera.open();
  camera.setFaceDetectionListener(faceDetectionListener);
 }

 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  camera.stopFaceDetection();
  camera.stopPreview();
  camera.release();
  camera = null;
  previewing = false;
 }
 
 private class DrawingView extends View{
  
  boolean haveFace;
  Paint drawingPaint;

  public DrawingView(Context context) {
   super(context);
   haveFace = false;
   drawingPaint = new Paint();
   drawingPaint.setColor(Color.GREEN);
   drawingPaint.setStyle(Paint.Style.STROKE); 
   drawingPaint.setStrokeWidth(2);
  }
  
  public void setHaveFace(boolean h){
   haveFace = h;
  }

  @Override
  protected void onDraw(Canvas canvas) {
   // TODO Auto-generated method stub
   if(haveFace){

    // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
     // UI coordinates range from (0, 0) to (width, height).
     
     int vWidth = getWidth();
     int vHeight = getHeight();
    
    for(int i=0; i<detectedFaces.length; i++){
     
     int l = detectedFaces[i].rect.left;
     int t = detectedFaces[i].rect.top;
     int r = detectedFaces[i].rect.right;
     int b = detectedFaces[i].rect.bottom;
     int left = (l+1000) * vWidth/2000;
     int top  = (t+1000) * vHeight/2000;
     int right = (r+1000) * vWidth/2000;
     int bottom = (b+1000) * vHeight/2000;
     canvas.drawRect(
       left, top, right, bottom,  
       drawingPaint);
    }
   }else{
    canvas.drawColor(Color.TRANSPARENT);
   }
  }
  
 }
}


Download the files.

Next: - Android 4 Face Detection: setFocusAreas() using face detected faces

12 comments:

Android Arduino said...

I try to use code on Nexus S 4.0.4 .
it 's error.

and use with Acer A100 tab.
It 's also error.

What phone do you test?

or pls. suggestion.

Erik said...

Hello,

I tried on Galaxy Nexus. It is "java.lang.RuntimeException: autoFocus failed"? please check another post java.lang.RuntimeException: autoFocus failed

Toni said...
This comment has been removed by the author.
Toni said...

Eclipse tells me former warning after each @Override.

Multiple markers at this line
- implements android.view.View.OnClickListener.onClick
- The method onClick(View) of type new View.OnClickListener(){} must override a superclass

Erik said...

Hello Toni,

I don't know is it your case, please read Error: The method XXX() of type YYY must override a superclass method!!!.

Toni said...

Yesss!! It was exactly my case. Thanks for the velocity and accuracy of your answer. This is what makes really Android community. Now I have no errors in eclipse. I have tried to run it in my Samsung Galaxy S II and does not work. The mobile show the says AndroidCamera has Stopped. But at least Indigo Eclipse when compiles says the App is fine.

Toni said...

Congratulations for your Blog. Really live and actualized.

Toni said...

I have to say that finally it does NOT work in Samsung Galaxy S II. The app is well build in eclipse. But when tested in the device (Phone) the error "Camera has stopped" appears.

Unknown said...

This is working with the Back Cam. But how to display detected face area with Front Cam? The face is detected, but the area is not displayed on view. So I guess it is out of range.
If anyone has the solution for front cam, feel free to reply. Thanks.

סטולפר said...

Hi,

Thanks for all your great code.
I have one problem when i run the code on an android device the camera pops on for a second and then I receiveerror message - "Unfortunately Android Camera has stopped". The camera works fine with other code using the camera.
Any ideas ?

Thank you

ayjc said...

i am trying to detect faces on preview..but i dont know how i will integrate the code here with my code..can you help me in that..

Anonymous said...

Hi...

I worked with Samsung S4.its working fine.thanks