Monday, 23 April 2012

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


Last exercise "Android 4 Face Detection: Display detected face area" we can get the detected face areas in onFaceDetection() of FaceDetectionListener. We can create a List of Camera.Area from the detected faces of Face[], to assign the area for focusing.



note:
Before using this API or setFocusAreas(List), apps should call getMaxNumFocusAreas() to know the maximum number of focus areas first. If the value is 0, focus area is not supported.


Each focus area is a rectangle with specified weight. The direction is relative to the sensor orientation, that is, what the sensor sees. The direction is not affected by the rotation or mirroring of setDisplayOrientation(int). Coordinates of the rectangle range from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000) is the lower right point. The width and height of focus areas cannot be 0 or negative.


The weight must range from 1 to 1000. The weight should be interpreted as a per-pixel weight - all pixels in the area have the specified weight. This means a small area with the same weight as a larger area will have less influence on the focusing than the larger area. Focus areas can partially overlap and the driver will add the weights in the overlap region. ~ Reference: http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getFocusAreas().

Modify the main code in last exercise:

package com.exercise.AndroidCamera;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

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.Parameters;
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;

/*
int maxNumFocusAreas = camera.getParameters().getMaxNumFocusAreas();
int maxNumMeteringAreas = camera.getParameters().getMaxNumMeteringAreas();
prompt.setText(String.valueOf(faces.length) + " Face Detected :) "
+ " maxNumFocusAreas=" + maxNumFocusAreas
+ " maxNumMeteringAreas=" + maxNumMeteringAreas
);
*/

//Set the FocusAreas using the first detected face
List<Camera.Area> focusList = new ArrayList<Camera.Area>();
Camera.Area firstFace = new Camera.Area(faces[0].rect, 1000);
focusList.add(firstFace);

if(camera.getParameters().getMaxNumFocusAreas()>0){
camera.getParameters().setFocusAreas(focusList);
}

if(camera.getParameters().getMaxNumMeteringAreas()>0){
camera.getParameters().setMeteringAreas(focusList);
}

buttonTakePicture.setEnabled(false);
//camera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

//Stop further Face Detection
camera.stopFaceDetection();

buttonTakePicture.setEnabled(false);
camera.autoFocus(myAutoFocusCallback);

}

drawingView.invalidate();

}};

AutoFocusCallback myAutoFocusCallback = new AutoFocusCallback(){

@Override
public void onAutoFocus(boolean arg0, Camera arg1) {
// TODO Auto-generated method stub
buttonTakePicture.setEnabled(true);
camera.cancelAutoFocus();

}};

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++){

if(i == 0){
drawingPaint.setColor(Color.GREEN);
}else{
drawingPaint.setColor(Color.RED);
}

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);
}
}

}
}

Important Note!

Please note that it's a simple exercise to try the face detection function for Android 4, not a completed application. I test it in Galaxy Nexus, it throw error of java.lang.RuntimeException: autoFocus failed sometimes. May be because of cannot focus. ~ Solved, refer the next post: java.lang.RuntimeException: autoFocus failed.

It is another bug here. The code of setFocusAreas() and setMeteringAreas() is in-correct! Please refer to the post Set Camera.Parameters.

Download the files.

No comments:

Post a Comment