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
I try to use code on Nexus S 4.0.4 .
ReplyDeleteit 's error.
and use with Acer A100 tab.
It 's also error.
What phone do you test?
or pls. suggestion.
Hello,
ReplyDeleteI tried on Galaxy Nexus. It is "java.lang.RuntimeException: autoFocus failed"? please check another post java.lang.RuntimeException: autoFocus failed
This comment has been removed by the author.
ReplyDeleteEclipse tells me former warning after each @Override.
ReplyDeleteMultiple markers at this line
- implements android.view.View.OnClickListener.onClick
- The method onClick(View) of type new View.OnClickListener(){} must override a superclass
Hello Toni,
ReplyDeleteI don't know is it your case, please read Error: The method XXX() of type YYY must override a superclass method!!!.
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.
ReplyDeleteCongratulations for your Blog. Really live and actualized.
ReplyDeleteI 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.
ReplyDeleteThis 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.
ReplyDeleteIf anyone has the solution for front cam, feel free to reply. Thanks.
Hi,
ReplyDeleteThanks 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
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..
ReplyDeleteHi...
ReplyDeleteI worked with Samsung S4.its working fine.thanks