I'm trying to read the focus distance (distance of subject in a photo) from an Android camera. I keep getting 0 for all focus distances on my HTC Desire even when it correctly autofocuses. Here's the whole app, only works on v2.3.3 and above.
ImageCapture.java
package test.test;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import android.app.Activity;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
//THIS CLASS READS THE FOCUS DISTANCES
class ImageFocusCallback implements AutoFocusCallback {
@Override
public void onAutoFocus(boolean success, Camera camera) {
//READ FOCUS DISTANCES HERE
Camera.Parameters parameters = camera.getParameters();
float[] distances = new float[3];
if (success) {
// Only available with Android 9 (2.3)
// Focus Mode is always reported as auto but
// distances do not appear to be updating
// always: 0.1, 1.2, Infinity, (on my device it's 0,0,0)
Log.d("Focus Mode: ", parameters.getFocusMode());
parameters.getFocusDistances(distances);
Log.d("focus distance near", Float.toString(distances[0]));
Log.d("focus distance optimum", Float.toString(distances[1]));
Log.d("focus distance far", Float.toString(distances[2]));
}
}
}
public class ImageCapture extends Activity implements SurfaceHolder.Callback {
//CALL AUTO FOCUS HERE
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
ImageFocusCallback autoFocusCallBack = new ImageFocusCallback();
//AUTOFOCUS IS CALLED HERE
camera.autoFocus(autoFocusCallBack);
return true;
}
return false;
}
//REST OF THE CODE
private Camera camera;
private boolean isPreviewRunning = false;
private SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyyMMddHHmmssSS");
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Uri target = Media.EXTERNAL_CONTENT_URI;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Log.e(getClass().getSimpleName(), "onCreate");
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.main);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
MenuItem item = menu.add(0, 0, 0, "goto gallery");
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(Intent.ACTION_VIEW, target);
startActivity(intent);
return true;
}
});
return true;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
Camera.PictureCallback mPictureCallbackRaw = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera c) {
Log.e(getClass().getSimpleName(), "PICTURE CALLBACK RAW: " + data);
camera.startPreview();
}
};
Camera.PictureCallback mPictureCallbackJpeg = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera c) {
Log.e(getClass().getSimpleName(), "PICTURE CALLBACK JPEG: data.length = " + data);
}
};
Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
Log.e(getClass().getSimpleName(), "SHUTTER CALLBACK");
}
};
// ImageCaptureCallback iccb = null;
// if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// try {
// String filename = timeStampFormat.format(new Date());
// ContentValues values = new ContentValues();
// values.put(Media.TITLE, filename);
// values.put(Media.DESCRIPTION, "Image capture by camera");
// Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,
// values);
// // String filename = timeStampFormat.format(new Date());
// iccb = new
// ImageCaptureCallback(getContentResolver().openOutputStream(uri));
// } catch (Exception ex) {
// ex.printStackTrace();
// Log.e(getClass().getSimpleName(), ex.getMessage(), ex);
// }
// }
// if (keyCode == KeyEvent.KEYCODE_BACK) {
// return super.onKeyDown(keyCode, event);
// }
//
// if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// camera.takePicture(mShutterCallback, mPictureCallbackRaw, iccb);
// return true;
// }
//
// return false;
// }
@Override
protected void onResume() {
Log.e(getClass().getSimpleName(), "onResume");
super.onResume();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onStop() {
Log.e(getClass().getSimpleName(), "onStop");
super.onStop();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e(getClass().getSimpleName(), "surfaceCreated");
camera = Camera.open();
Camera.Parameters parameters = camera.getParameters();
float[] distances = new float[3];
Log.d("Focus Mode: ", parameters.getFocusMode());
parameters.getFocusDistances(distances);
Log.d("focus distance 0", Float.toString(distances[0]));
Log.d("focus distance 1", Float.toString(distances[1]));
Log.d("focus distance 2", Float.toString(distances[2]));
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(getClass().getSimpleName(), "surfaceChanged");
if (isPreviewRunning) {
camera.stopPreview();
}
Camera.Parameters p = camera.getParameters();
p.setPreviewSize(w, h);
camera.setParameters(p);
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.startPreview();
isPreviewRunning = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e(getClass().getSimpleName(), "surfaceDestroyed");
camera.stopPreview();
isPreviewRunning = false;
camera.release();
}
}
class ImageCaptureCallback implements PictureCallback {
private OutputStream filoutputStream;
public ImageCaptureCallback(OutputStream filoutputStream) {
this.filoutputStream = filoutputStream;
}
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
Log.v(getClass().getSimpleName(), "onPictureTaken=" + data + " length = " + data.length);
filoutputStream.write(data);
filoutputStream.flush();
filoutputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
main.xml
<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/surface"
android:layout_width="fill_parent"
android:layout_height="10dip"
android:layout_weight="1">
</SurfaceView>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ImageCapture"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Could it be that that there is a bug in the device driver? In the Android source Camera.java call native_autoFocus() and native_getParameters() to read the distances. Does anyone know where to get the source for the native calls?
In Froyo, frameworks/base/core/jni/android_hardware_Camera.cpp implements native_autoFocus() by calling android_hardware_Camera_autoFocus(), which appears to bind at runtime to the device/vendor specific code.
Unfortunately, "If the camera does not support auto-focus and autoFocus is called, onAutoFocus will be called immediately with a fake value of success set to true." is a documented behavior.
I'm curious which of the functions you use requires 2.3.3, API level 10.