Wednesday, April 25, 2012

Gets the distances from the camera to the focus point - getFocusDistances()


The method getFocusDistances(float[] output) of android.hardware.Camera.Parameters gets the distances from the camera to where an object appears to be in focus. The object is sharpest at the optimal focus distance. The depth of field is the far focus distance minus near focus distance. ~ Since: API Level 9.



Modify AndroidCamera.java from last exercise "java.lang.RuntimeException: autoFocus failed", call getFocusDistances() in onAutoFocus() of myAutoFocusCallback, when focused.


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 java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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;
 
 private ScheduledExecutorService myScheduledExecutorService;
 
    /** 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 tcamera) {
   
   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;
    
    //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);
    
    /*
     * Allways throw java.lang.RuntimeException: autoFocus failed 
     * if I call autoFocus(myAutoFocusCallback) here!
     * 
     camera.autoFocus(myAutoFocusCallback);
    */
    
    //Delay call autoFocus(myAutoFocusCallback)
    myScheduledExecutorService = Executors.newScheduledThreadPool(1);
    myScheduledExecutorService.schedule(new Runnable(){
          public void run() {
           camera.autoFocus(myAutoFocusCallback);
            }
          }, 500, TimeUnit.MILLISECONDS);

   }
   
   drawingView.invalidate();
   
  }};
    
    AutoFocusCallback myAutoFocusCallback = new AutoFocusCallback(){

  @Override
  public void onAutoFocus(boolean arg0, Camera arg1) {
   // TODO Auto-generated method stub
   if (arg0){
    buttonTakePicture.setEnabled(true);
    camera.cancelAutoFocus();      
   }
   
   float focusDistances[] = new float[3];
   arg1.getParameters().getFocusDistances(focusDistances);
   prompt.setText("Optimal Focus Distance(meters): " 
     + focusDistances[Camera.Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]);

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


Download the files.

Note:
- Set Camera.Parameters

Related:
- Touch to select focus and metering area


8 comments:

  1. Brillaint...i did not know we could get the distance from our phone itself....But does it give atleast close to approximate values...because when i focused on an object which is some 30 cm away the optimum focus distance is given as 0.034 m, i.e. 0.034 x 100 = 3.4 cm....??

    ReplyDelete
  2. I am also facing the same thing...like the distance returned by the app is 0.1 times the actual one...Any reason for this. How to get the correct distance please help.

    Madan Kumar -(amkumar91@gmail.com)

    ReplyDelete
  3. hi,,
    Pls help me with my project where i use the OpenCV to detect same colors of the object.
    then how to find out the length of the colored object after detecting it.

    ReplyDelete
  4. can anyone please send me updated download link of the code. as the current download link is not working anymore. Also please mention the layout file used in this code. Please respond as I want to implement this part immediately.

    ReplyDelete
  5. please help my, give me step by step to make that aplication.
    this project with eclipse and openCV or what ?

    ReplyDelete
  6. Can you please help me : I am doing exactly the same in camera2 context but the focus distance is always 0.0

    how to get the LENS_FOCUS_DISTANCE in CONTROL_AF_MODE_AUTO ? I have tried the below code but the focusValue is always 0.0 in AUTO MODE

    final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
    super.onCaptureCompleted(session, request, result);
    createCameraPreview();

    focusValue = result.get(CaptureResult.LENS_FOCUS_DISTANCE);
    }

    ReplyDelete
  7. Can you please help me on using the same example in camera2

    how to get the LENS_FOCUS_DISTANCE in CONTROL_AF_MODE_AUTO ? I have tried the below code but the focusValue is always 0.0 in AUTO MODE

    final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
    super.onCaptureCompleted(session, request, result);
    createCameraPreview();

    focusValue = result.get(CaptureResult.LENS_FOCUS_DISTANCE);
    }

    ReplyDelete
  8. IS it not given a relevant result i tried but failed please mention what is the parameter you use in this formula how can i get from app and for your relevance i give u small description(i create a custom camera and click picture i want to get the distance from camera to object please help to resolve this query)

    i tried almost every link and every formula but nothing is positive hope best for your reply asap.

    ReplyDelete