Camera display doesn't maintain aspect ratio/seems skewed - android

I'm running into an issue with camera input in Android. After searching over the internet for half a day I'm not getting any further.
Wat I want to achieve at this stage is correctly displaying the current camera input. Nothing more and nothing less for now. When holding the device in landscape mode, all seems fine. However as soon as I switch the device to portrait the display seems to be horizontally scaled in order to match the width of the screen on the device.
In order to clarify this issue a bit more, I've attached two photographs:
Landscape (looks correct)
Portrait (looks incorrect)
It seems like this issue also occurs with the camera example from the API examples, but it doesn't seem to occur when I use the camera application from my device. So clearly something else is happening there.
The code that I'm using is based on examples that can be found over the internet and has no modifications:
class Preview extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
Camera mCamera;
Preview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(w, h);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
The API examples make use of a method "getOptimalPreviewSize(...)" which sounds like the method I need, but that does not change anything. I left it out here in order to be as brief as possible.
Am I missing something obvious or is this the expected behavior?
Hopefully someone can point me in the correct direction.
Thanks in advance!
Paul

Okay, I just found the answer to my own question.
The way I solved it was by going fullscreen with my application:
requestWindowFeature(Window.FEATURE_NO_TITLE);
This makes a little bit of sense, as I guess we can not use just any resolution as the preview size on the camera. I guess it will internally snap to the closest allowed resolution/aspect ratio and it doesn't do any smart things like cropping the image to match the requested resolution while maintaining aspect ratio. In my case I was displaying a title bar while the source image was fullscreen and then downsized to fit on the screen.
I think it is kind of stupid that the aspect ratio is not maintained and images are not cropped by default,

Related

Custom camera displays wrong orientation

I followed the guide on the Android developers website on how to make a custom camera but the orientation is 90 degrees off. When the camera is in landscape mode (turning left) it displays how it should - however, when it's in landscape after turning right it displays the view upside down.
I did some reading online and tried a few things but none of them seem to work. For example, I tried using mCamera.setDisplayOrientation(90), which works initially, but now it's 90 degrees off in landscape mode. Also, when I save the image it still saves it with the wrong orientation.
I have included this in my AndroidManifest:
<uses-permission android:name="android.permission.CAMERA" />
And this is the surfaceCreated() method that I'm using:
public void surfaceCreated(SurfaceHolder holder) {
try {
if(mCamera != null) {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} else {
mCamera = Camera.open();
mCamera.startPreview();
}
} catch (IOException e) {
}
}
Does anybody know why this is happening? It seems like a lot of people have faced the same issue but I haven't had any luck fixing it so far. Really grateful for any help, thank you.

Android camera changes dimensions when rotating phone

I'm developing an android application which uses the camera. I got a problem with surfaceChanged() method. Here is my code.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged");
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(w, h);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
the variable mPreviewRunning is initialized as false at the beginning. program runs just fine with horizontal orientation. but when I rotates the phone to the vertical orientation the screen is rotated and is stretched. I can't understand why it's happened. Please help me to solve this problem.
Camera does not change orientation at all - CCD is soldered to motherboard , and delivers pixels as if it were in landscape mode ( most probably ) however, your activity could be restarted by OS on orientation change, unless you disable it in manifest.
( and your surface view is recreated on this restart )
Look in this project for android demos handling camera management:
https://sourceforge.net/p/javaocr/source/ci/5cb9b4176f40ada57296cce79addd205e4c1405c/tree/demos/camera-utils/src/main/java/net/sf/javaocr/demos/android/utils/camera/CameraManager.java#l85
Your mistake is to set preview size from surface size - do not do this. Camera provides limited set of acceptable preview sizes and is free to ignore other settings ( exact behaviour is device depending)
Preview size means used CCD resolution, and camera software will render it on your surface view in size of the surface view doung scaling as necessary.

Does Face Detection require a SurfaceView to work?

Using API 14 I have created an Activity which uses face detection successfully (I'm a bit of a newb at this Face Detection stuff).
I don't want to show the preview of the Camera however; I just want to know when the user's face is in front of the camera. I added a few buttons to make the SurfaceView Visible/Invisible and I found that the face detection stops working when it is INVISIBLE or GONE.
Is there a way to enable the face detection without requiring the SurfaceView in the layout?
Here is how I have coded it:
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
if(mCamera.getParameters().getMaxNumDetectedFaces() >0) {
mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() {
#Override public void onFaceDetection(Face[] faces, Camera camera) {
if(faces.length > 0) {
System.out.println("Found someone");
}
}
});
mCamera.startFaceDetection();
}
To hide the surfaceview I have added a black View. :-)
<View android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000"/>
I've had a reasonable look around and I haven't found much code using startFaceDetection() yet.
Thanks for any ideas / help.
You should rather use a dummy SurfaceTexture for your purposes.
Just create a SurfaceTexture object by passing any integer such as
mSurfaceTexture = new SurfaceTexture(1);
Now, open your camera and do the following:
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
try{
mCamera.setPreviewTexture(mSurfaceTexture);
}
catch (IOException t) {
//Do Something here
}
3) You can do everything else just the same way i.e. using face detection.
The Camera Preview doesn't get displayed if you omit
setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
I have not yet tested this with Face Detection (no 4.0 device and the emulator doesn't yet support it). But it should work.
Reference: https://groups.google.com/forum/?fromgroups#!topic/android-developers/EzBgJRetaCo
You could also attempt to use setPreviewTexture(SurfaceTexture st) instead of setPreviewDisplay and use a SurfaceTexture that you can control.

ColorEffect not visible on camera preview

I am trying to get a camera preview with a color effect applied to it, such as for example the NEGATIVE effect. There are no errors, and the preview is visible without problems, but independent of the ColorEffect I set - the camera preview remains unchanged. I tested if the effects I am trying to use are available to my phone by running params.getSupportedColorEffects() (also these effects also work in the built in photo app).
I have no idea what is wrong with the code - I am posting it below. Perhaps someone here has an idea what could make this work? Thanks in advance.
public class CustomCameraView extends SurfaceView{
Camera mCamera;
SurfaceHolder mHolder;
public CustomCameraView(Context context){
super(context);
mHolder = this.getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.addCallback(mSurfaceHolderListener);
}
SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
mCamera=Camera.open();
try {
mCamera.setPreviewDisplay(mHolder);
}
catch (Exception e){ }
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
Camera.Parameters params = mCamera.getParameters();
params.setColorEffect(Camera.Parameters.EFFECT_NEGATIVE);
mCamera.setParameters(params);
mCamera.startPreview();
}
public void surfaceDestroyed(SurfaceHolder arg0)
{
mCamera.stopPreview();
mCamera.release();
}
};
}
After some testing it turned out the problem could be related to the HTC Desire I was testing on (or maybe its OS version). The code works correctly on some other Samsung phones. I haven't figured out what could be the problem on the HTC.
UPDATE:
I have managed to get the effects working, but truly by accident, and I still don't understand why. But I will give the answer here - perhaps someone will find it useful, or maybe will be able to explain why it happens this way:
I added the following line to the surfaceChanged method because I was trying to decrease the size of the preview:
previewHolder.setFixedSize(width, height-1);
This had the result of making the selected effect visible.
When I changed this line to:
previewHolder.setFixedSize(width, height);
the effect was not visible any more once again. So odd.... it works for set height being anything less than the received height parameter.
I have been struggling with this as well. I found out that the HTC Desire its camera needs a strange order of executing the setParameters, setPreviewDisplay and startPreview for the color effect to work. The order is:
Camera.Parameters parameters = camera.getParameters();
//set the parameters
camera.setParameters(parameters);
camera.startPreview();
camera.setParameters(parameters);
camera.setPreviewDisplay(surfaceHolder);
Calling startPreview before setPreviewDisplay is documented in the Android SDK as a way of initializing the camera and the surfaceView in parallel.
Regarding your update about getting the effects to work by accident, the same happend to me! I assume for the same reason, some of my code got called twice in quick succesion (in my case due to a changing database object). This caused the method to (re)set the parameters and (re)start the preview to be called twice producing the desired result. After realising this and some more experimenting the above order seemed to work on both my HTC Desire and Acer Iconia A500 and I was quite happy with it.
However I have just received a comment for my application saying it produces corrupted images on the HTC Desire HD so I would recommend not using this order of camera initialization as a default but rather as a fix for the HTC Desire.
After setting new parameters to camera and starting preview invalidate() are calling on your SurfaceView . But it only Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. So there is no guarantees that onDraw() will be called immediately. But onDraw() are always invoking after calling onMeasure() with size differs from current. So it can be a reason of this odd behavior.
Simple answers use following type :
Camera camera = null;
camera = Camera.open();
if (camera != null) {
try {
Camera.Parameters parameters = camera.getParameters();
// Set all kind of stuffs here..
parameters.setSceneMode(Camera.Parameters.FLASH_MODE_AUTO);
parameters.setColorEffect(Camera.Parameters.EFFECT_SEPIA); // whatever effect you want
camera.setParameters(parameters);
camera.setPreviewDisplay(surface_holder);
camera.startPreview();
} catch (IOException exception) {
camera.release();
camera = null;
}
}

LED flashlight on Galaxy Nexus controllable by what API?

So many LED flashlight API questions for Android. I'm afraid to ask yet another, but here goes..
Using the tried and true FLASH_MODE_TORCH I am able to achieve satisfaction with my Samsung Galaxy SII and get the LED flash turned on. On my friend's Galaxy Nexus, no such luck. Nor on my other friend's Droid X.
I'm noticing for a not insignificant number of devices specific native IOCTL calls seem to be required. Is this the case for the Galaxy Nexus? How do I find a reference to program it?
I am doing the standard FLASH_MODE_TORCH/"flash-mode"="torch", startPreview() chain.
Kind of disappointing that this seemingly standard API doesn't appear to be so universal after all.
What I found out is that some devices need a SurfaceView to turn on the LED.
SurfaceView preview = (SurfaceView) findViewById(R.id.PREVIEW);
SurfaceHolder mHolder = preview.getHolder();
mHolder.addCallback(this);
Camera mCamera = Camera.open();
mCamera.setPreviewDisplay(mHolder);
// Turn on LED
Parameters params = mCamera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(params);
mCamera.startPreview();
...
// Turn off LED
Parameters params = mCamera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(params);
mCamera.stopPreview();
mCamera.release();
Your activity needs to implement SurfaceHolder.Callback:
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {}
public void surfaceCreated(SurfaceHolder holder) {
mHolder = holder;
mCamera.setPreviewDisplay(mHolder);
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mHolder = null;
}
The surfaceview has to be visible, android:visibility="invisible" or a height and width of 0 won't work. Unfortunately I didn't find a good solution to hide it, so I
just gave it a size of 1x1dip and positioned it underneath a button..
**(To expand on above paragraph [Don't have enough rep to reply, but felt it was useful]) Somewhere in the XML of your current Content View, you want:
<SurfaceView
android:id="#+id/PREVIEW"
android:layout_width="1dip"
android:layout_height="1dip"/>
If you have it inside a RelativeLayout (preferred), you can also do alignParentLeft/Bottom to tuck it in a corner. This method works for my Galaxy Nexus, but it's a known problem (phone-side) for Droid X (with the .621 update, anyway), and doesn't work on mine. Great answer, timosch!

Categories

Resources