I am trying to make a custom camera application
I want to let the users can choose the focus mode in this application.
The focus mode is auto and touch-to-focus
If we want to use touch-to-focus in the camera , how can be start with?
The feature is software/hardware/manufacture dependent, my suggestion is that you first find a phone like Galaxy Nexus flashed with Android 4.x, then try the android.hardware.Camera.Parameters.getMaxNumFocusAreas() on it, if the return value is greater than zero then you are lucky, and can then use the setFocusAreas() to implement your "touch to focus" feature.
Why:
In old Android versions there is no public API to set the focus areas. Although many manufacturers managed to create their own API and implementation, they won't share.
Android introduced the focus areas API in API level 14, however the phone manufacturers may choose not to implement it (i.e. choose to stick to their own solutions). To check if the API is supported you can call getMaxNumFocusAreasa() first, if it returns a positive integer that means the phone does implement the API and you can go on enabling the "touch focus" feature in your camera app. (The API is also one of the enablers of the "face detection" feature, when faces are recognized the camera app uses the API to let the camera do auto focus on the them.)
You may refer to the vanilla Android Camera app source code for how to use the API properly.
References:
Android Camera API
getMaxNumFocusAreas()
setFocusAreas()
Android 4.0 Camera app source code
mInitialParams.getMaxNumFocusAreas()
mParameters.setFocusAreas()
Regards
Ziteng Chen
Try this:
public void takePhoto(File photoFile, String workerName, int width, int height, int quality) {
if (getAutoFocusStatus()){
camera.autoFocus(new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
});
}else{
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
However, I've also seen this to work, possibly more accurately:
if (getAutoFocusStatus()){
camera.autoFocus(new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
if(success) camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
});
}else{
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
The last one takes the picture at the moment the focussing is successfully completed. It works very well for using with QR scanning codes. I believe the same applies to cases like this.
It is already implemented buthow to modify this if I want to add touch to focus?
public void takePhoto(File photoFile, String workerName, int width, int height, int quality) {
if (getAutoFocusStatus()){
camera.autoFocus(new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
});
}else{
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
this.photoFile = photoFile;
this.workerName = workerName;
this.imageOutputWidth = width;
this.imageOutputHeight = height;
}
public void takePhoto(File photoFile, int width, int height, int quality) {
takePhoto(photoFile, null, width, height, quality);
}
I was trying to implement focus functionality in my app and achieved this functionality in the way i wanted. To implement Touch to Focus please refer the code below.
CameraPreview.java
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
private OnFocusListener onFocusListener;
private boolean needToTakePic = false;
private Camera.AutoFocusCallback myAutoFocusCallback = new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean arg0, Camera arg1) {
if (arg0) {
mCamera.cancelAutoFocus();
}
}
};
// Constructor that obtains context and camera
#SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera) {
super(context);
this.mCamera = camera;
this.mSurfaceHolder = this.getHolder();
this.mSurfaceHolder.addCallback(this);
this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
this.onFocusListener = (OnFocusListener) context;
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
} catch (IOException e) {
// left blank for now
e.printStackTrace();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mCamera.stopPreview();
this.mSurfaceHolder.removeCallback(this);
mCamera.release();
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
int width, int height) {
// start preview with new settings
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
// intentionally left blank for a test
e.printStackTrace();
}
}
/**
* Called from PreviewSurfaceView to set touch focus.
*
* #param - Rect - new area for auto focus
*/
public void doTouchFocus(final Rect tfocusRect) {
try {
List<Camera.Area> focusList = new ArrayList<Camera.Area>();
Camera.Area focusArea = new Camera.Area(tfocusRect, 1000);
focusList.add(focusArea);
Camera.Parameters param = mCamera.getParameters();
param.setFocusAreas(focusList);
param.setMeteringAreas(focusList);
mCamera.setParameters(param);
mCamera.autoFocus(myAutoFocusCallback);
} catch (Exception e) {
e.printStackTrace();
}
if (isNeedToTakePic()) {
onFocusListener.onFocused();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float x = event.getX();
float y = event.getY();
Rect touchRect = new Rect(
(int) (x - 100),
(int) (y - 100),
(int) (x + 100),
(int) (y + 100));
final Rect targetFocusRect = new Rect(
touchRect.left * 2000 / this.getWidth() - 1000,
touchRect.top * 2000 / this.getHeight() - 1000,
touchRect.right * 2000 / this.getWidth() - 1000,
touchRect.bottom * 2000 / this.getHeight() - 1000);
doTouchFocus(targetFocusRect);
}
return false;
}
public boolean isNeedToTakePic() {
return needToTakePic;
}
public void setNeedToTakePic(boolean needToTakePic) {
this.needToTakePic = needToTakePic;
}
}
MainActivity.java
public class MainActivity extends Activity
implements OnFocusListener {
private Button captureButton, switchCameraButton;
private Camera mCamera;
private CameraPreview mCameraPreview;
private int currentCameraId;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().hasExtra("camera_id")) {
currentCameraId = getIntent().getIntExtra("camera_id", Camera.CameraInfo.CAMERA_FACING_BACK);
} else {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
// Obtain MotionEvent object
v.setEnabled(false);
mCameraPreview.setNeedToTakePic(true);
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
float x = mCameraPreview.getWidth() / 2;
float y = mCameraPreview.getHeight() / 2;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
MotionEvent.ACTION_DOWN,
x,
y,
metaState
);
// Dispatch touch event to view
mCameraPreview.dispatchTouchEvent(motionEvent);
}
});
switchCameraButton = (Button) findViewById(R.id.button_switch_camera);
switchCameraButton.setVisibility(
Camera.getNumberOfCameras() > 1 ? View.VISIBLE : View.GONE);
switchCameraButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCamera.stopPreview();
//NB: if you don't release the current camera before switching, you app will crash
mCameraPreview.getHolder().removeCallback(mCameraPreview);
mCamera.release();
//swap the id of the camera to be used
if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
} else {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
mCamera = getCameraInstance(currentCameraId);
mCameraPreview = new CameraPreview(MainActivity.this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.removeAllViews();
preview.addView(mCameraPreview);
}
});
}
#Override
protected void onResume() {
super.onResume();
mCamera = getCameraInstance(currentCameraId);
mCameraPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mCameraPreview);
}
/**
* Helper method to access the camera returns null if it cannot get the
* camera or does not exist
*
* #return
*/
private Camera getCameraInstance(int currentCameraId) {
Camera camera = null;
try {
camera = Camera.open(currentCameraId);
} catch (Exception e) {
// cannot get camera or does not exist
}
return camera;
}
Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
private static File getOutputMediaFile() {
File mediaStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MyCameraApp");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
// String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
// .format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "IMG_" + "DEMO_" + ".jpg");
if (mediaFile.exists()) mediaFile.delete();
return mediaFile;
}
#Override
public void onFocused() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mCamera.takePicture(null, null, mPicture);
mCameraPreview.setNeedToTakePic(false);
captureButton.setEnabled(true);
}
}, 1500);
}
}
activity_main.xml
<?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">
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="#+id/button_switch_camera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Switch Camera" />
<Button
android:id="#+id/button_capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Capture" />
</LinearLayout>
You can find sample app on Github - Custom Camera App
Call this to enable Touch-To-Focus mode:
private void setTouchToFocusMode(Camera.Parameters parameters){
String focusMode;
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
focusMode = Camera.Parameters.FOCUS_MODE_AUTO;
}
if (focusMode != null && focusMode.length() > 0){
parameters.setFocusMode(focusMode);
}
}
When user taps on screen, call this below to set focus area:
private static final int FOCUS_WIDTH = 80;
private static final int FOCUS_HEIGHT = 80;
public static String setFocalPoint(Camera.Parameters params, int x, int y){
String focusMode = "";
if (params != null && params.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusArea = new ArrayList<Camera.Area>();
focusArea.add(new Camera.Area(new Rect(x, y, x + FOCUS_WIDTH, y + FOCUS_HEIGHT), 1000));
params.setFocusAreas(focusArea);
if(params.getMaxNumMeteringAreas() > 0) {
params.setMeteringAreas(focusArea);
}
if(params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
focusMode = Camera.Parameters.FOCUS_MODE_AUTO;
}
}
return focusMode;
}
Call autoFocus/cancelAutoFocus for action:
mCamera.cancelAutoFocus();
mCamera.autoFocus(mAutoFocusCallback);
Related
I want to implement brightness adjustment in an app that uses that uses OpenCV camera. I have found methods and I have found in the documentation of JavaCameraView and CameraViewBridge Base if exists some method to do it directly but I have not found.
Is there a way to do this?
Follow those steps that will help you adjust the exposure of the camera
First create a CustomCameraView java class
public class CustomCameraView extends JavaCameraView implements Camera.PictureCallback {
private String mPictureFileName;
public CustomCameraView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public List<String> getEffectList() {
return mCamera.getParameters().getSupportedFlashModes();
}
public boolean isEffectSupported() {
return (mCamera.getParameters().getFlashMode() != null);
}
public String getEffect() {
return mCamera.getParameters().getFlashMode();
}
public void setEffect(String effect) {
mCamera.getParameters();
Camera.Parameters params = mCamera.getParameters();
params.setFlashMode(effect);
mCamera.setParameters(params);
}
// set Camera Exposure value from input progress (0.0f - 1.0f)
public void setExposure(float progress) {
if (progress < 0.0f && progress > 1.0f) return;
Camera.Parameters params = mCamera.getParameters();
int min = params.getMinExposureCompensation();
int max = params.getMaxExposureCompensation();
float realProgress = progress - 0.5f;
int value;
if (realProgress < 0) {
value = -(int) (realProgress * 2 * min);
} else {
value = (int) (realProgress * 2 * max);
}
// if changed
if (value != params.getExposureCompensation()) {
params.setExposureCompensation(value);
mCamera.setParameters(params);
}
}
public void setPreviewFPS(double min, double max) {
Camera.Parameters params = mCamera.getParameters();
params.setPreviewFpsRange((int) (min * 1000), (int) (max * 1000));
mCamera.setParameters(params);
}
public List<Camera.Size> getResolutionList() {
return mCamera.getParameters().getSupportedPreviewSizes();
}
public void setResolution(int w, int h) {
disconnectCamera();
mMaxHeight = h;
mMaxWidth = w;
connectCamera(getWidth(), getHeight());
}
public Camera.Size getResolution() {
return mCamera.getParameters().getPreviewSize();
}
public void takePicture(final String fileName) {
this.mPictureFileName = fileName;
// Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
// Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
mCamera.setPreviewCallback(null);
// PictureCallback is implemented by the current class
mCamera.takePicture(null, null, this);
}
#Override
public void onPictureTaken(byte[] data, Camera camera) {
// The camera preview was automatically stopped. Start it again.
mCamera.startPreview();
mCamera.setPreviewCallback(this);
// Write the image in a file (in jpeg format)
try {
FileOutputStream fos = new FileOutputStream(mPictureFileName);
fos.write(data);
fos.close();
} catch (java.io.IOException e) {
Log.e("PictureDemo", "Exception in photoCallback", e);
}
}
public void cameraRelease() {
if (mCamera != null) {
mCamera.release();
}
}
}
After that in the layout file of the activity where you declared the JavaCameraView , change that with this
<your.package.CustomCameraView
android:id="#+id/cameraViewer"
android:layout_width="380dp"
android:layout_height="450dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
opencv:show_fps="false" />
NOTE:change your.package with the appropriate package name
Finally in the activity add this
cameraBridgeViewBase = (CustomCameraView) findViewById(R.id.cameraViewer);
cameraBridgeViewBase.setExposure(1.0f);
NOTE: change this value 1.0f to your desired value.
I hope this helps you.
What I have done so far:
I have implemented custom camera for reading qr code which need to continue focus the camera for better qr reading.
My problem is when I use to focus in every one second with the handler the camera flash on\off button dont works or it takes too much time to turning on and off the camera flash light. Every thing works fine when I remove the code of auto focusing the camera every second (The runnable and the handler).
What I want is to focus automatically and quickly whenever camera moves and also able to turn on and off the flash on demand quickly without using Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE because its not available for API<14.
I have used Camera.Parameters.FOCUS_MODE_AUTO but its only focusing the camera once when started thats why i used handler to focus camera every second.
Min SDK Version of project is 9.
My Camera Activity is
public class CameraActivityNew extends Activity implements OnClickListener,
Camera.PreviewCallback {
CameraPreviewNew mPreview;
FrameLayout flCameraPreview;
ImageButton ibFlashButton;
Boolean isFlashOn = false;
Camera mCamera;
private Handler mAutoFocusHandler;
private boolean mPreviewing = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
mAutoFocusHandler = new Handler();
setContentView(R.layout.activity_camera);
findSetupViews();
mPreview = new CameraPreviewNew(getApplicationContext(), this,
autoFocusCB);
flCameraPreview.addView(mPreview);
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
#Override
protected void onResume() {
super.onResume();
try {
mCamera = Camera.open();
if (mCamera == null) {
return;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
mPreview.setCamera(mCamera);
mPreview.showSurfaceView();
mPreviewing = true;
}
#Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mPreview.setCamera(null);
mCamera.cancelAutoFocus();
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mPreview.hideSurfaceView();
mPreviewing = false;
mCamera = null;
}
}
private void findSetupViews() {
flCameraPreview = (FrameLayout) findViewById(R.id.flCameraPreview);
ibFlashButton = (ImageButton) findViewById(R.id.ibFlash);
ibFlashButton.setOnClickListener(this);
if (getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_FLASH)) {
ibFlashButton.setVisibility(View.VISIBLE);
ibFlashButton.setOnClickListener(this);
} else {
ibFlashButton.setVisibility(View.GONE);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ibFlash:
if (isFlashOn) {
mPreview.setCameraFlashLight(false);
isFlashOn = false;
ibFlashButton.setImageResource(R.drawable.flashoff);
} else {
mPreview.setCameraFlashLight(true);
ibFlashButton.setImageResource(R.drawable.flashon);
isFlashOn = true;
}
break;
}
}
#Override
public void onPreviewFrame(final byte[] data, final Camera camera) {
// processed here qr code and works fine if camera focus
//now removed to narrow the code for posting the question
}
}
And the Camera Preview class is:
public class CameraPreviewNew extends ViewGroup implements Callback {
public static final int CAMERA_BACK = 0;
public static final int CAMERA_FRONT = 1;
public Camera mCamera = null;
private Context context = null;
SurfaceView mSurfaceView;
SurfaceHolder mSurfaceHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;
public CameraPreviewNew(Context context,
PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
super(context);
mPreviewCallback = previewCallback;
mAutoFocusCallback = autoFocusCb;
this.context = context;
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters()
.getSupportedPreviewSizes();
requestLayout();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
setMeasuredDimension(width, height);
}
public void hideSurfaceView() {
mSurfaceView.setVisibility(View.INVISIBLE);
}
public void showSurfaceView() {
mSurfaceView.setVisibility(View.VISIBLE);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e("logtag", "IOException caused by setPreviewDisplay()",
exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.cancelAutoFocus();
mCamera.stopPreview();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (holder.getSurface() == null) {
return;
}
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
mPreviewSize = getBestPreviewSize(mCamera.getParameters(), w, h);
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mCamera.autoFocus(mAutoFocusCallback);
setCameraDisplayOrientation(0);
}
}
private void setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = ((WindowManager) context
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
protected static Comparator<Size> newSizeComparator() {
return new Comparator<Size>() {
#Override
public int compare(Size lhs, Size rhs) {
return Integer.valueOf(rhs.height * rhs.width).compareTo(
lhs.height * lhs.width);
}
};
}
private Size getBestPreviewSize(Parameters parameters, int screenWidth,
int screenHeight) {
List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
Collections.sort(supportedSizes, newSizeComparator());
int previewHeight = screenHeight;
int previewWidth = screenWidth;
if (previewHeight > previewWidth) {
int swap = previewWidth;
previewWidth = previewHeight;
previewHeight = swap;
}
Size bestSize = null;
float bestRatio = 999;
for (Size s : supportedSizes) {
if (s.height > s.width) {
int swap = s.width;
s.width = s.height;
s.height = swap;
}
float cameraRatio = ((float) s.height / (float) s.width);
float screenRatio = ((float) previewHeight)
/ ((float) previewWidth);
if ((s.height >= previewHeight) && (s.width >= previewWidth)) {
float ratioDiff = cameraRatio - screenRatio;
if ((ratioDiff < 0.19) && (ratioDiff > -0.19)
&& (Math.abs(bestRatio) > Math.abs(ratioDiff))) {
bestSize = s;
bestRatio = ratioDiff;
}
}
}
return bestSize;
}
public void setCameraFlashLight(Boolean setFlash) {
Parameters _parameters = mCamera.getParameters();
if (setFlash) {
_parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
} else {
_parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
}
mCamera.setParameters(_parameters);
mCamera.startPreview();
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height
/ previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width
/ previewWidth;
child.layout(0, (height - scaledChildHeight) / 2, width,
(height + scaledChildHeight) / 2);
}
}
}
}
I see some issue with your AutoFocus handling code.
Analysis Result
There is cycle in your autofocus.
Explanation
a) Camera Preview Class mAutoFocusCallback is set with the autoFocusCb of the Camera Activity.
public CameraPreviewNew(Context context,...,AutoFocusCallback autoFocusCb)
{
super(context);
mAutoFocusCallback = autoFocusCb;
...
}
b)surfaceChanged is called once, at the time of loading the activity. The camera is requested to Auto Focus.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
if (mCamera != null)
{
...
mCamera.startPreview();
/*Auto focus camera and call <code>mAutoFocusCallback</code> after autofocus.*/
mCamera.autoFocus(mAutoFocusCallback);
...
}
}
c) On completion of the autofocus the mAutoFocusCallback callback is called. mAutoFocusCallback->autoFocusCb->onAutoFocus()
Camera Activity
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
d)onAutoFocus schedules one more autoFocus after 1000 millisecons, 1 sec.
Camera Activity
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
e)After one second the messages is passed to handler that calles the runnable doAutoFocus requesting camera to auto focus, similar to b) above.
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
f) After completion of the autoFocus, the autoFocusCB is called again, similar to c) above. and cycle continues.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
Solution
I am confused why such implementation. The cycle may be reason behind not listening to the flash enable/disable calls. You need to remove the code below and do something meaningful else leave the onAutoFocus() empty.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
/*REMOVE LINE BELOW*/
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
For auto focusing every time the camera moves you need to take help of the motion sensors provided with the phone. You can google it
Hope that helps.
Happy Coding...
It seems that you dont need to use AutoFocusCallBack for your app, because you did nothing else than delay 1 second.
What you can do to focus all the time is using FOCUS_MODE_CONTINUOUS_PICTURE(read more here) like that(setFocus method is in CameraPreview, not in Activity):
public void setFocus() {
Camera.Parameters p = mCamera.getParameters();
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setParameters(p);
mCamera.startPreview();
}
And call it in SurfaceChanged:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = previewSizes.get(0);
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setRotation(90);
mCamera.setParameters(parameters);
mCamera.startPreview();
setFlash(true);
setZoomLevel(5);
setFocus();
Log.w(TAG, "surfaceChanged()");
}
For flash you can use this method from CameraPreview:
public void setFlash(boolean isFlashOn) {
Camera.Parameters p = mCamera.getParameters();
if (isFlashOn) {
p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(p);
mCamera.startPreview();
} else {
p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(p);
mCamera.startPreview();
}
Log.w(TAG, "setFlash()");
}
Hope it helps you! If you have any questions about my answer feel free to comment.
While this must be late, if you are toggling Camera parameters, it is preferable to do the sequence of operation as below
mCamera.stopPreview();
mCamera.setParameters(params); // set flash on in Camera parameters
mCamera.startPreview();
I am developing an app that is going to be something like camscanner. In my app i have an camera api tha i call. When the app is first opened and i press the camera button it works, but when i click home button and open again my app it freezes and shows a black screen without the app crashing. I found similar questions but none could give me a right answer, i know i probably have to change something in the onResume or onPause and need help to figure out what.
below i have my CameraScreen activity:
public class CameraScreen extends Activity {
ImageView image;
Activity context;
Preview preview;
Camera camera;
Button exitButton;
ImageView fotoButton;
LinearLayout progressLayout;
String path = "/sdcard/KutCamera/cache/images/";
FrameLayout frame;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_layout);
context=this;
fotoButton = (ImageView) findViewById(R.id.imageView_foto);
exitButton = (Button) findViewById(R.id.button_exit);
image = (ImageView) findViewById(R.id.imageView_photo);
progressLayout = (LinearLayout) findViewById(R.id.progress_layout);
preview = new Preview(this,
(SurfaceView) findViewById(R.id.KutCameraFragment));
frame = (FrameLayout) findViewById(R.id.preview);
frame.addView(preview);
preview.setKeepScreenOn(true);
fotoButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
takeFocusedPicture();
} catch (Exception e) {
}
exitButton.setClickable(true);
fotoButton.setClickable(false);
progressLayout.setVisibility(View.VISIBLE);
}
});
}
#Override
protected void onPause() {
super.onPause();
//releaseMediaRecorder(); // if you are using MediaRecorder, release it first
//releaseCamera();
if(null != camera){
camera.release();
camera = null;
}
frame.removeView(preview);
preview = null;// release the camera immediately on pause event
}
private void releaseCamera(){
if (camera != null){
camera.release(); // release the camera for other applications
camera = null;
}
}
#Override
protected void onResume() {
super.onResume();
// TODO Auto-generated method stub
if(camera==null){
Log.d("Camera tes", "Camera==null");
//camera.setPreviewCallback(null);
camera = Camera.open();
camera.startPreview();
camera.setErrorCallback(new ErrorCallback() {
public void onError(int error, Camera mcamera) {
camera.release();
camera = Camera.open();
Log.d("Camera died", "error camera");
}
});
}
if (camera != null) {
//camera.setPreviewCallback(null);
Log.d("Camera tes", "Camera!=null");
if (Build.VERSION.SDK_INT >= 14)
setCameraDisplayOrientation(context,
CameraInfo.CAMERA_FACING_BACK, camera);
preview.setCamera(camera);
}
}
private void setCameraDisplayOrientation(Activity activity, int cameraId,
android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
try{
camera.takePicture(mShutterCallback, null, jpegCallback);
}catch(Exception e){
}
}
};
Camera.ShutterCallback mShutterCallback = new ShutterCallback() {
#Override
public void onShutter() {
// TODO Auto-generated method stub
}
};
public void takeFocusedPicture() {
camera.autoFocus(mAutoFocusCallback);
}
PictureCallback rawCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
// Log.d(TAG, "onPictureTaken - raw");
}
};
PictureCallback jpegCallback = new PictureCallback() {
#SuppressWarnings("deprecation")
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
Calendar c = Calendar.getInstance();
File videoDirectory = new File(path);
if (!videoDirectory.exists()) {
videoDirectory.mkdirs();
}
try {
// Write to SD Card
outStream = new FileOutputStream(path + c.getTime().getSeconds() + ".jpg");
outStream.write(data);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Bitmap realImage;
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 5;
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
realImage = BitmapFactory.decodeByteArray(data,0,data.length,options);
ExifInterface exif = null;
try {
exif = new ExifInterface(path + c.getTime().getSeconds()
+ ".jpg");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Log.d("EXIF value",
exif.getAttribute(ExifInterface.TAG_ORIENTATION));
if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
.equalsIgnoreCase("1")) {
realImage = rotate(realImage, 90);
} else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
.equalsIgnoreCase("8")) {
realImage = rotate(realImage, 90);
} else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
.equalsIgnoreCase("3")) {
realImage = rotate(realImage, 90);
} else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
.equalsIgnoreCase("0")) {
realImage = rotate(realImage, 90);
}
} catch (Exception e) {
}
image.setImageBitmap(realImage);
fotoButton.setClickable(true);
camera.startPreview();
progressLayout.setVisibility(View.GONE);
exitButton.setClickable(true);
}
};
public static Bitmap rotate(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, false);
}
}
And here i have my Preview class:
class Preview extends ViewGroup implements SurfaceHolder.Callback {
private final String TAG = "Preview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
int heightmax ;
int widthmax ;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
#SuppressWarnings("deprecation")
Preview(Context context, SurfaceView sv) {
super(context);
mSurfaceView = sv;
// addView(mSurfaceView);
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPictureSizes();
requestLayout();
// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
mCamera.setParameters(params);
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize=maxSize();
}
}
public Size maxSize(){
Size sizeMax=mSupportedPreviewSizes.get(0);
maxsize=mSupportedPreviewSizes.get(0)
.height*mSupportedPreviewSizes.get(0).width;
for(Size size:mSupportedPreviewSizes){
if(size.height*size.width>sizeMax.width*sizeMax.height){
sizeMax = size;
}
}
return sizeMax;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height / previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width / previewWidth;
child.layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
/*
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
if (mCamera != null) {
mCamera.stopPreview();
}
*/
}
Camera.AutoFocusCallback mnAutoFocusCallback = new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
}
};
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
/*
if(mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
*/
}
}
I would appriciate any help or guides you can give me.
I want to change the area of camera scan. Now I take image to scan as big as screen of device. I'm trying to crop image to analyze. So just the center of preview will be source to scan. Is there any option to set captured preview to be smaller or creating Bitmap from byte[] data and crop it is the only way to get smaller area? I was trying to read something about it but documentation for Zbar Android is very poor (comparing to iOS).
Picture here:
https://postimg.cc/image/4wk4u0mln/
MainActivity
public class MainActivity extends Activity
{
private Camera mCamera;
private Context context;
private CameraPreview mPreview;
private Handler autoFocusHandler;
TextView scanText;
Button scanButton;
ImageScanner scanner;
private PowerManager.WakeLock wl;
private boolean barcodeScanned = false;
private boolean previewing = true;
static {
System.loadLibrary("iconv");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler();
mCamera = getCameraInstance();
context = getApplicationContext();
/* Instance barcode scanner */
scanner = new ImageScanner();
scanner.setConfig(0, Config.X_DENSITY, 3);
scanner.setConfig(0, Config.Y_DENSITY, 3);
mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
preview.addView(mPreview);
scanText = (TextView)findViewById(R.id.scanText);
}
#Override
public void onPause() {
super.onPause();
releaseCamera();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
onStart();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
finish();
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open();
} catch (Exception e){
}
return c;
}
private void releaseCamera() {
if (mCamera != null) {
previewing = false;
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
private Runnable doAutoFocus = new Runnable() {
#Override
public void run() {
if (previewing)
mCamera.autoFocus(autoFocusCB);
}
};
PreviewCallback previewCb = new PreviewCallback() {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize();
//HERE we read taken picture from prieview
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
int result = scanner.scanImage(barcode);
if (result != 0) {
previewing = false;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
SymbolSet syms = scanner.getResults();
for (Symbol sym : syms) {
if (sym.getType() == Symbol.CODE128) {
sym.getData());
MediaPlayer mp = MediaPlayer.create(context, R.raw.beep_ok);
mp.start();
} else {
MediaPlayer mp = MediaPlayer.create(context, R.raw.beep_wrong);
mp.start();
}
mCamera.setPreviewCallback(previewCb);
mCamera.startPreview();
previewing = true;
mCamera.autoFocus(autoFocusCB);
}
}
}
};
// Mimic continuous auto-focusing
AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
//Method to crop Bitmap in case of use
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
}
CameraPrieview.java
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private PreviewCallback previewCallback;
private AutoFocusCallback autoFocusCallback;
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
/*
* Set camera to continuous focus if supported, otherwise use
* software auto-focus. Only works for API level >=9.
*/
/*
Camera.Parameters parameters = camera.getParameters();
for (String f : parameters.getSupportedFocusModes()) {
if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
autoFocusCallback = null;
break;
}
}
*/
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Camera preview released in activity
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
/*
* If your preview can change or rotate, take care of those events here.
* Make sure to stop the preview before resizing or reformatting it.
*/
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
try {
// Hard code camera surface rotation 90 degs to match Activity view in portrait
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e){
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
}
In the PreviewCallback you can actually crop the scanning area. In the onPreviewFrame method, after calling barcode.setData() you can call barcode.setCrop(left,top,width,height). All measurements in pixels.
Also make sure to set the crop size with respect to the preview image size and not the device screen size.
Note: Please make sure that the x-axis is always the longest side even though you orient the phone in portrait mode.
Please refer below Image for better understanding of the arguments.
This solution is specific to zbar for android.
I've been battling with this feature for couple of days now...
It seems, that camera is ignoring(?) focus areas that I've defined. Here is snippets of the code:
Focusing:
protected void focusOnTouch(MotionEvent event) {
if (camera != null) {
Rect rect = calculateFocusArea(event.getX(), event.getY());
Parameters parameters = camera.getParameters();
parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
parameters.setFocusAreas(Lists.newArrayList(new Camera.Area(rect, 500)));
camera.setParameters(parameters);
camera.autoFocus(this);
}
}
Focus area calculation:
private Rect calculateFocusArea(float x, float y) {
int left = clamp(Float.valueOf((x / getSurfaceView().getWidth()) * 2000 - 1000).intValue(), focusAreaSize);
int top = clamp(Float.valueOf((y / getSurfaceView().getHeight()) * 2000 - 1000).intValue(), focusAreaSize);
return new Rect(left, top, left + focusAreaSize, top + focusAreaSize);
}
Couple of log events from Camera.AutoFocusCallback#onAutoFocus
Log.d(TAG, String.format("Auto focus success=%s. Focus mode: '%s'. Focused on: %s",
focused,
camera.getParameters().getFocusMode(),
camera.getParameters().getFocusAreas().get(0).rect.toString()));
08-27 11:19:42.240: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-109, 643 - -13, 739)
08-27 11:19:55.514: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(20, 457 - 116, 553)
08-27 11:19:58.037: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-159, 536 - -63, 632)
08-27 11:20:00.129: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-28, 577 - 68, 673)
Visually it looks like focus succeeds on logged area, but the suddenly it loses focus and focus on center (0, 0), or what takes bigger part of SurfaceView is obtained.
focusAreaSize used in calculation is about 210px (96dp).
Testing on HTC One where Camera.getParameters().getMaxNumFocusAreas() is 1.
Initial focus mode (before first tap) is set to FOCUS_MODE_CONTINUOUS_PICTURE.
Am I doing something wrong here?
Tinkering with Camera.Area rectangle size or weight doesn't show any noticeable effect.
My problem was much simpler :)
All I had to do is cancel previously called autofocus. Basically the correct order of actions is this:
protected void focusOnTouch(MotionEvent event) {
if (camera != null) {
camera.cancelAutoFocus();
Rect focusRect = calculateTapArea(event.getX(), event.getY(), 1f);
Rect meteringRect = calculateTapArea(event.getX(), event.getY(), 1.5f);
Parameters parameters = camera.getParameters();
parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
parameters.setFocusAreas(Lists.newArrayList(new Camera.Area(focusRect, 1000)));
if (meteringAreaSupported) {
parameters.setMeteringAreas(Lists.newArrayList(new Camera.Area(meteringRect, 1000)));
}
camera.setParameters(parameters);
camera.autoFocus(this);
}
}
Update
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
...
Parameters p = camera.getParameters();
if (p.getMaxNumMeteringAreas() > 0) {
this.meteringAreaSupported = true;
}
...
}
/**
* Convert touch position x:y to {#link Camera.Area} position -1000:-1000 to 1000:1000.
*/
private Rect calculateTapArea(float x, float y, float coefficient) {
int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
int left = clamp((int) x - areaSize / 2, 0, getSurfaceView().getWidth() - areaSize);
int top = clamp((int) y - areaSize / 2, 0, getSurfaceView().getHeight() - areaSize);
RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
matrix.mapRect(rectF);
return new Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF.bottom));
}
private int clamp(int x, int min, int max) {
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
}
Beside setting:
parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
you need to set:
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
if you want real 'live' auto-focus. Also, it will be good to check available focuses:
List<String> focusModes = parameters.getSupportedFocusModes();
LLog.d("focusModes=" + focusModes);
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
On Samsung S6 you must set this with little delay (~ 500 ms) after getting camera preview.
I had this problem today :/
And after hours of struggling, I found the solution!
It's strange, but it appears that setting focus-mode to "macro" right before setting focus-areas solved the problem ;)
params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
params.setFocusAreas(focusAreas);
mCamera.setParameters(params);
I have Galaxy S3 with Android 4.1.2
I hope this will work for you either :)
use FOCUS_MODE_FIXED
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(mCameraId);
} else {
mCamera = Camera.open();
}
cameraParams = mCamera.getParameters();
// set the focus mode
cameraParams.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
// set Camera parameters
mCamera.setParameters(cameraParams);
Hi, try below code copy and change for yourself
public class CameraActivity extends AppCompatActivity implements Camera.AutoFocusCallback {
private Camera camera;
private FrameLayout fl_camera_preview;
...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( R.layout.camera_activity );
//this View, is lens camera
fl_camera_preview = findViewById( R.id.fl_camera_preview );
Button someButtonCapturePicture = findViewById(R.id.someButtonCapturePicture);
pictureCall = getPictureCallback();
//check camera access
if ( getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) ) {
if ( safeCameraOpen(0) ) {
cameraPreview = new CameraPreview( this, camera );
fl_camera_preview.addView( cameraPreview );
someButtonCapturePicture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
camera.takePicture(null, null, pictureCall);
}
});
} else {
Log.w(TAG, "getCameraInstance: Camera is not available (in use or does not exist)." );
}
}
}
private boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
camera = Camera.open( id );
// set some parameters
Camera.Parameters par = camera.getParameters();
List<Camera.Size> supportedPreviewSizes = par.getSupportedPreviewSizes();
for ( Camera.Size cs : supportedPreviewSizes ) {
if ( cs.height == 720 ) {
par.setPictureSize(cs.width, cs.height);
par.setPreviewSize(cs.width, cs.height);
break;
}
}
camera.setParameters(par);
qOpened = ( camera != null );
} catch (Exception e) {
Log.e(TAG, "safeCameraOpen: failed to open Camera");
e.printStackTrace();
}
return qOpened;
}
public void touchFocusCamera( final Rect touchFocusRect ) {
//Convert touche coordinate, in width and height to -/+ 1000 range
final Rect targetFocusRect = new Rect(
touchFocusRect.left * 2000/fl_camera_preview.getWidth() - 1000,
touchFocusRect.top * 2000/fl_camera_preview.getHeight() - 1000,
touchFocusRect.right * 2000/fl_camera_preview.getWidth() - 1000,
touchFocusRect.bottom * 2000/fl_camera_preview.getHeight() - 1000);
final List<Camera.Area> focusList = new ArrayList<Camera.Area>();
Camera.Area focusArea = new Camera.Area(targetFocusRect, 1000);
focusList.add(focusArea);
Camera.Parameters para = camera.getParameters();
List<String> supportedFocusModes = para.getSupportedFocusModes();
if ( supportedFocusModes != null &&
supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO) ) {
try {
para.setFocusAreas(focusList);
para.setMeteringAreas(focusList);
camera.setParameters(para);
camera.autoFocus( CameraActivity.this );
} catch (Exception e) {
Log.e(TAG, "handleFocus: " + e.getMessage() );
}
}
}
#Override
public void onAutoFocus(boolean success, Camera camera) {
if ( success ) {
camera.cancelAutoFocus();
}
float focusDistances[] = new float[3];
camera.getParameters().getFocusDistances(focusDistances);
}
/**
* Get Bitmap from camera
* #return picture
*/
private Camera.PictureCallback getPictureCallback() {
Camera.PictureCallback picture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.i(TAG, "onPictureTaken: size bytes photo: " + data.length );
}
};
return picture;
}
...
}
//And SurfaceView with Callback
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
SurfaceHolder holder;
Camera camera;
public CameraPreview( Context context, Camera _camera ) {
super(context);
camera = _camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
holder = getHolder();
holder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if( event.getAction() == MotionEvent.ACTION_DOWN ) {
// Get the pointer's current position
float x = event.getX();
float y = event.getY();
float touchMajor = event.getTouchMajor();
float touchMinor = event.getTouchMinor();
Rect touchRect = new Rect(
(int)(x - touchMajor/2),
(int)(y - touchMinor/2),
(int)(x + touchMajor/2),
(int)(y + touchMinor/2));
((CameraActivity)getContext())
.touchFocusCamera( touchRect );
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (this.holder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
camera.setPreviewDisplay(this.holder);
camera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
...
}