I have following class code in an APK, doing face detection and drawing a custom view on camera preview. The face detection works fine on My "Samsung-S3" but i tested it on couple of other android cell phones where face detection never starts. Why is that so/How to get it work? ( Don't mind indentation please)
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private MyDrawing md;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void refreshCamera(Camera camera) {
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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
setCamera(camera);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
//startFaceDetection();
} catch (Exception e) {
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
refreshCamera(mCamera);
}
public void setCamera(Camera camera) {
//method to set a camera instance
mCamera = camera;
mCamera.setFaceDetectionListener(faceDetectionListener);
startFaceDetection();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
mCamera.release();
}
private Camera.FaceDetectionListener faceDetectionListener = new Camera.FaceDetectionListener() {
#Override
public void onFaceDetection(Camera.Face[] faces, Camera c) {
if (faces.length > 0) {
Log.d("FaceDetection", "face detected X and Y are as: " + faces.length +
" Face 1 Location X: " + faces[0].rect.centerX() +
"Y: " + faces[0].rect.centerY() +" LIES IN "+(MyDrawing.w-MyDrawing.radius) +"--"+(MyDrawing.w+MyDrawing.radius));
if(faces[0].rect.centerX()>=0 && faces[0].rect.centerX()<115 )
{
Log.d("ALERT = ", "Detection Started" );
AndroidVideoCaptureExample.capture.setText("Recording/ stopNsave ");
AndroidVideoCaptureExample.faceDetect();
}
} else {
Log.d("FaceDetection", "circle cordinates are as: " + (MyDrawing.w-MyDrawing.radius) +"cX"+ MyDrawing.radius+"cY");
}
}
};
public void startFaceDetection(){
// Try starting Face Detection
Camera.Parameters params = mCamera.getParameters();
// start face detection only *after* preview has started
if (params.getMaxNumDetectedFaces() > 0){
// camera supports face detection, so can start it:
mCamera.startFaceDetection();
}
}
Min supported SDK is set 10
The problem was in setting TargetSDK in Android Manifest file. I have set it to latest Android M while before it was working for 4.4.4(KitKat) as Target. Now works fine.Bingo.
Related
I copied the code Google example in:
https://developer.android.com/guide/topics/media/camera.html
For the first time I open the Camera, face detection works correctly and the callback is fired, I take the photo, close the camera, open the camera again but this time the face detection callback is not called.
If I'll restart my app, the same thing will happen - face detection will only work for the first time I'll use the camera.
here is my code:
private PictureTakenListener mPictureTakenListener;
AlertDialog mAlertDialog;
private Camera mCamera;
boolean mFaceDetectionAvailable;
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
showText("took a photo with a size of " + data.length);
releaseCamera();
if (mAlertDialog != null) mAlertDialog.dismiss();
mPictureTakenListener.photoTaken(data);
}
};
public class MyFaceDetectionListener implements Camera.FaceDetectionListener {
#Override
public void onFaceDetection(Camera.Face[] faces, Camera camera) {
if (faces != null)
if (faces.length > 0)
if (faces[0].score >= 40)
if (!takingPhotoFlag.get()){
takingPhotoFlag.set(true);
mCamera.takePicture(null, null, mPicture);
}
}
}
private void takePicture(PictureTakenListener listener){
mPictureTakenListener = listener;
if (this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
showText("this device has a camera");
showText("device has " + String.valueOf(Camera.getNumberOfCameras()) + " cameras");
int foundCamera = -1;
for (int i = 0; i < Camera.getNumberOfCameras(); i++){
Camera.CameraInfo info = new Camera.CameraInfo(); ;
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
foundCamera = i;
break;
}
}
if (foundCamera == -1){
return;
}
mCamera = null;
try {
mCamera = Camera.open(foundCamera); // attempt to get a Camera instance
}
catch (Exception e){
e.printStackTrace();
showText("could not open the front camera. " + e.getMessage());
return;
}
showText("successfully opened the camera");
mCamera.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setPreviewSize(640, 480);
params.setRotation(270);
params.setPictureSize(640, 480);
mFaceDetectionAvailable = params.getMaxNumDetectedFaces() > 0;
mCamera.setParameters(params);
FrameLayout frameLayout = new FrameLayout(this);
if (!mFaceDetectionAvailable) {
frameLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCamera.takePicture(null, null, mPicture);
}
});
}
AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (!mFaceDetectionAvailable)
alert.setTitle("CLICK IMAGE TO CAPTURE");
alert.setView(frameLayout).setCancelable(true).setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
releaseCamera();
runOnUiThread(new Runnable() {
#Override
public void run() {
//do UI stuff...
}
});
}
});
mAlertDialog = alert.create();
mAlertDialog.show();
mAlertDialog.getCurrentFocus();
mAlertDialog.getWindow().setLayout(960, 1280); //Controlling width and height.
mAlertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
CameraPreview mPreview = new CameraPreview(this, mCamera);
frameLayout.addView(mPreview);
} else {
showText("no camera on this device");
}
}
public interface PictureTakenListener{
void photoTaken(byte[] image);
}
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// 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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
if (mFaceDetectionAvailable) {
mCamera.startFaceDetection();
mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
}
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
private void releaseCamera(){
if (mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.setFaceDetectionListener(null);
mCamera.stopFaceDetection();
mCamera.setErrorCallback(null);
mCamera.release();
mCamera = null;
showText("released camera");
}
}
Thank you.
I am trying to write a class that shows the preview of the camera surface in android. I have tried the following code. But the preview is not showing up. I tried changing the parameters but no luck. I have gone through the logcat. Nothing is shown, no error or warning. Only a black screen is shown. The app doesnot crash.
public class Preview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
public Preview(Context context, Camera camera) {
super(context);
this.camera = camera;
holder = getHolder();
holder.addCallback(this);
holder.setFixedSize(50, 50);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.d("error", "Can't set camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (this.holder.getSurface() == null) {
return;
}
try {
camera.stopPreview();
} catch (Exception e) {
}
try {
camera.setPreviewDisplay(this.holder);
camera.startPreview();
} catch (Exception e) {
Log.d("DG_DEBUG", "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
The problem seems here that you have not included the type of the holder. You need to set the type to SURFACE_TYPE_PUSH_BUFFERS. Use below line after adding callback
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
So your constructor should be like below
public Preview(Context context, Camera camera) {
super(context);
this.camera = camera;
holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.setFixedSize(50, 50);
}
I have tried the code. It works perfectly now.
How to solve the streching of image in my camera preview?
Camera Preview Class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
// create the surface and start camera preview
if (mCamera == null) {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
} catch (IOException e) {
Log.d(VIEW_LOG_TAG,
"Error setting camera preview: " + e.getMessage());
}
}
public void refreshCamera(Camera camera) {
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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
setCamera(camera);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(VIEW_LOG_TAG,
"Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
refreshCamera(mCamera);
}
public void setCamera(Camera camera) {
// method to set a camera instance
mCamera = camera;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// mCamera.release();
}
}
Maybe your CameraView's aspect ratio is not match with the aspect ratio of camera preview size.
You should caculate your CameraView's aspect ratio and use this aspect ratio to find best size for Camera.Parameters.setPreviewSize() method.
Try this
private Camera.Size getBestPreviewSize(Camera.Parameters parameters, int width, int height) {
Camera.Size result = null;
double ratio = width / height;
double w = 0, h = 0;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
w = size.width;
h = size.height;
if (w / h == ratio) {
if (result != null) {
if (w > result.width) {
result = size;
}
} else {
result = size;
}
}
}
return (result);
}
I have integrated camera in my app.When user click on capture button i am making toolbar hide so that camera preview screen size increases.This crashes the app on stopping the recording on line - mMediaRecorder.stop();.
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(MediaRecorder.java)
However if i don't hide the toolbar or never show the toolbar then there is no crash.So the problem is because the camera params changes after i hide the toolbar.How could i resolve this?I have seen this answer and added tht code to CameraPreview class.
private Camera mCamera;
private CameraPreview mPreview;
mCamera = getCameraInstance();
mCamera.setDisplayOrientation(90);
mPreview = new CameraPreview(mActivity, mCamera);
preview = (FrameLayout) view.findViewById(R.id.camera_preview);
preview.addView(mPreview);
capture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mToolbar.setVisibility(View.GONE);
if (prepareVideoRecorder()) {
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
}
}
}):
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mMediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
mCamera.lock();
}
}):
CameraPreview.class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
// if(mCamera==null)
// mCamera = getCameraInstance();
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(profile.videoFrameWidth,profile.videoFrameHeight);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("error", "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
Log.e("surfaceDestroyed issue","surfaceDestroyed called");
if ((mCamera != null)&&(!CameraActivity.getInstance().getOnBackPressedListener().isPreview())) {
Log.e("surfaceDestroyed issue","surface destroyed");
try {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}catch(Exception e){}
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// 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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d("error", "Error starting camera preview: " + e.getMessage());
}
}
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
} catch (Exception e) {
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
Error-
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(MediaRecorder.java)
at xyzCameraFragment$5.onClick(CameraFragment.java:208)
at android.view.View.performClick(View.java:4761)
at android.view.View$PerformClick.run(View.java:19767)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5312)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
In you capture click set a boolean variable
capture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mToolbar.setVisibility(View.GONE);
startCapture = true;
}):
Hiding the ToolBar causes surfaceChanged to get called.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// 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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d("error", "Error starting camera preview: " + e.getMessage());
}
//Check if capture is set and start recording
if(startCapture)
{
if (prepareVideoRecorder()) {
mMediaRecorder.start();
}
startCapture = false;
}
}
I'm developing for API 7 (2.1). I implemented a camera view like this:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
int width;
int height;
Camera mCamera;
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
initHolder();
}
public CameraView(Context context) {
super(context);
initHolder();
}
private void initHolder() {
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where to
// draw.
mCamera = Camera.open();
// Parameters params = mCamera.getParameters();
// // If we aren't landscape (the default), tell the camera we want
// // portrait mode
// if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
// params.set("orientation", "portrait"); // "landscape"
// // And Rotate the final picture if possible
// // This works on 2.0 and higher only
// // params.setRotation(90);
// // Use reflection to see if it exists and to call it so you can
// // support older versions
// try {
// Method rotateSet = Camera.Parameters.class.getMethod("setRotation", new Class[] { Integer.TYPE });
// Object arguments[] = new Object[] { new Integer(90) };
// rotateSet.invoke(params, arguments);
// } catch (NoSuchMethodException nsme) {
// // Older Device
// Log.v("CAMERAVIEW", "No Set Rotation");
// } catch (IllegalArgumentException e) {
// Log.v("CAMERAVIEW", "Exception IllegalArgument");
// } catch (IllegalAccessException e) {
// Log.v("CAMERAVIEW", "Illegal Access Exception");
// } catch (InvocationTargetException e) {
// Log.v("CAMERAVIEW", "Invocation Target Exception");
// }
// }
// mCamera.setParameters(params);
setDisplayOrientation(mCamera, 90);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
// Because the CameraDevice object is not a shared resource, it's very
// important to release it when the activity is paused.
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
width = w;
height = h;
// Now that the size is known, set up the camera parameters and begin the preview.
Camera.Parameters parameters = mCamera.getParameters();
//parameters.setPreviewSize(w, h);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
public void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) {
mCamera.takePicture(shutter, raw, jpeg);
}
protected void setDisplayOrientation(Camera camera, int angle){
Method downPolymorphic;
try {
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
if (downPolymorphic != null) {
downPolymorphic.invoke(camera, new Object[] { angle });
}
} catch (Exception e1) {}
}
}
Only the approach using reflection (taken from Williew's answer in Android camera rotate) works on my device to show the preview with the correct rotation, otherwise, the preview is always rotated -90°
So far so good, but now I have another problem. When I get the bitmap with the activitie's callback:
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length);
Log.d("test", "---width: " + b.getWidth() + " height: " + b.getHeight());
}
It's always in landscape mode! (in this case 2560 x 1920). So if the user took a picture holding the device in portrait mode, I get a bitmap 2560 x 1920, which anyways, for some reason looks exactly like the portrait pic I took, when I put in in an image view. Problem comes when the user takes the pic holding the device in landscape mode, I would like to rotate it, in order to show the result in portrait mode (scaled down) - or do some other special actions for landscape pics.
But I can't differentiate them from the portrait pics because the bitmap's dimensions are the same :/
How do I recognize portrait / landscape pictures?
Any idea...? I'm new to the camera and kind of lost...
Thanks in advance.
Edit
Ok, I think there's no problem, with the dimensions always being the same, because the picture has actually always the same dimensions, no matter how I was holding the device. Only thing I don't understand is why I always get width > height when the preview and the pics are clearly in portrait mode.