I am trying to program an application which has to display on the mobile phone's screen what is being filmed by the front camera [The application is not recording/saving anything in the memory of the phone]. Also in case a face is filmed (and detected), it has to appear sourrended by a rectangle.
To do so I'm using:
- A Surfaceview to display what is being filmed by the front camera.
- A FaceDetectionListener to detect faces in the camera input.
So far the application displays properly what is being filmed by the front camera. It also detects correctly faces. But I'm not able to draw the boundary rectangle around the detected face.
Here are some snippets to show how I'm trying to solve the task.
Activity:
@SuppressLint("InflateParams")
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class FaceDetectorTutorial extends Activity {
MySurface mMySurface;
private SurfaceView surfaceView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_face_detector_tutorial);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView = (SurfaceView)findViewById(R.id.camPreview);
mMySurface = new MySurface(this, surfaceView);
}
}
MySurface class:
class MySurface extends SurfaceView implements SurfaceHolder.Callback {
Paint paint = new Paint();
public Camera camera;
String Tag = "Log: ";
private SurfaceHolder surfaceHolder;
boolean preview = false;
////////// CLASS CONSTRUCTOR //////////
MySurface(Context context, SurfaceView msurfaceView) {
super(context);
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
surfaceHolder = msurfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
///////// FACE DETECTION LISTENER /////////
@SuppressLint("NewApi")
FaceDetectionListener faceDetectionListener = new FaceDetectionListener(){
@Override
public void onFaceDetection(Face[] faces, Camera camera) {
if (faces.length > 0){
//Just for the first one detected
Rect Boundary = faces[0].rect;
System.out.println(Boundary);
tryDrawing(Boundary);
}
}};//End of FaceDetectionListener
////////// SURFACE METHODS ////////
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
// TODO Auto-generated method stub
if(preview){
camera.stopFaceDetection();
camera.stopPreview();
preview = false;
}
if (camera != null){
try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
camera.startFaceDetection();
preview = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
int cameraId = -1;
int numberOfCameras = camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
cameraId = i;
break;
}
}
camera = Camera.open(cameraId);
camera.setFaceDetectionListener(faceDetectionListener);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
camera.stopFaceDetection();
camera.stopPreview();
camera.release();
camera = null;
preview = false;
}
private void tryDrawing(Rect Boundary) {
Log.i(Tag, "Trying to draw...");
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas == null) {
Log.e(Tag, "Cannot draw onto the canvas as it's null");
} else {
drawMyStuff(canvas,Boundary);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
private void drawMyStuff(final Canvas canvas, Rect Boundary) {
canvas.drawRect(Boundary.left, Boundary.top, Boundary.right, Boundary.bottom, paint);
Log.i(Tag, "Drawing...");
}
}
The Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<SurfaceView
android:id="@+id/camPreview"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
From the logcat I understand the error is locking the surface. But I don't understand why.
11-23 17:13:51.791: I/System.out(12515): Rect(-187, -495 - 328, 196)
11-23 17:13:51.791: I/Log:(12515): Trying to draw...
11-23 17:13:51.791: E/SurfaceHolder(12515): Exception locking surface
11-23 17:13:51.791: E/SurfaceHolder(12515): java.lang.IllegalArgumentException
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.view.Surface.nativeLockCanvas(Native Method)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.view.Surface.lockCanvas(Surface.java:452)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:781)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.view.SurfaceView$4.lockCanvas(SurfaceView.java:757)
11-23 17:13:51.791: E/SurfaceHolder(12515): at com.example.facedetectiontutorial.MySurface.tryDrawing(FaceDetectorTutorial.java:175)
11-23 17:13:51.791: E/SurfaceHolder(12515): at com.example.facedetectiontutorial.MySurface.access$0(FaceDetectorTutorial.java:172)
11-23 17:13:51.791: E/SurfaceHolder(12515): at com.example.facedetectiontutorial.MySurface$1.onFaceDetection(FaceDetectorTutorial.java:109)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.hardware.Camera$EventHandler.handleMessage(Camera.java:815)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.os.Handler.dispatchMessage(Handler.java:99)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.os.Looper.loop(Looper.java:137)
11-23 17:13:51.791: E/SurfaceHolder(12515): at android.app.ActivityThread.main(ActivityThread.java:5041)
11-23 17:13:51.791: E/SurfaceHolder(12515): at java.lang.reflect.Method.invokeNative(Native Method)
11-23 17:13:51.791: E/SurfaceHolder(12515): at java.lang.reflect.Method.invoke(Method.java:511)
11-23 17:13:51.791: E/SurfaceHolder(12515): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
11-23 17:13:51.791: E/SurfaceHolder(12515): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
11-23 17:13:51.791: E/SurfaceHolder(12515): at dalvik.system.NativeStart.main(Native Method)
11-23 17:13:51.893: E/Log:(12515): Cannot draw onto the canvas as it's null
I based my solution in the accepted answer of the following question:
Android drawing on surfaceview and canvas
But it seems I'm applying it wrong. Could anyone tell me what should I change?