How to Set Android Camera Orientation Properly

How to set Android camera orientation properly?

I finally fixed this using the Google's camera app. It gets the phone's orientation by using a sensor and then sets the EXIF tag appropriately. The JPEG which comes out of the camera is not oriented automatically.

Also, the camera preview works properly only in the landscape mode. If you need your activity layout to be oriented in portrait, you will have to do it manually using the value from the orientation sensor.

How to set camera orientation in Android?


 Camera.Parameters params= mCamera.getParameters();
params.set("rotation", 90);
mCamera.setParameters(params);

Android how to fix camera orientation

I found the solution here. Answer by @Ed Jellard.

i just have to add camera.setDisplayOrientation(90); on surfaceCreated(SurfaceHolder holder) method, now the display is on the right angle.

see the happy T-REX :)

Android: Camera preview orientation in portrait mode

To force portrait orientation:

set android:screenOrientation="portrait" in your AndroidManifest.xml and call camera.setDisplayOrientation(90); before calling camera.startPreview();

I have not tested on the GS2 but it works on every phone I have tested on.

Note: setDisplayOrientation was added in API Level 8

Camera setDisplayOrientation() in Portrait Mode Breaks Aspect Ratio

OK, I think that I figured this out. There were a few pieces to the proverbial puzzle, and I thank @kcoppock for the insight that helped me solve some of this.

First, you need to take the orientation into account when determining what preview size to choose. For example, the getOptimalPreviewSize() from the CameraPreview of ApiDemos is oblivious to orientation, simply because their version of that app has the orientation locked to landscape. If you wish to let the orientation float, you need to reverse the target aspect ratio to match. So, where getOptimalPreviewSize() has:

double targetRatio=(double)width / height;

you also need:

if (displayOrientation == 90 || displayOrientation == 270) {
targetRatio=(double)height / width;
}

where displayOrientation is a value from 0 to 360, that I am determining from about 100 lines of some seriously ugly code, which is why I am wrapping all of this up in a reusable component that I'll publish shortly. That code is based on the setDisplayOrientation() JavaDocs and this StackOverflow answer.

(BTW, if you are reading this after 1 July 2013, and you don't see a link in this answer to that component, post a comment to remind me, as I forgot)

Second, you need to take that display orientation into account when controlling the aspect ratio of the SurfaceView/TextureView that you are using. The CameraPreview activity from ApiDemos has its own Preview ViewGroup that handles the aspect ratio, and there you need to reverse the aspect ratio for use in portrait:

    if (displayOrientation == 90
|| displayOrientation == 270) {
previewWidth=mPreviewSize.height;
previewHeight=mPreviewSize.width;
}
else {
previewWidth=mPreviewSize.width;
previewHeight=mPreviewSize.height;
}

where displayOrientation is that same value (90 and 270 being portrait and reverse-portrait respectively, and note that I haven't tried getting reverse-portrait or reverse-landscape to work, so there may be more tweaking required).

Third -- and one that I find infuriating -- you must start the preview before calling setPictureSize() on the Camera.Parameters. Otherwise, it's as if the aspect ratio of the picture is applied to the preview frames, screwing things up.

So I used to have code resembling the following:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
parameters.setPictureFormat(ImageFormat.JPEG);

camera.setParameters(parameters);
camera.startPreview();

and that's wrong. What you really need is:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

camera.setParameters(parameters);
camera.startPreview();

parameters=camera.getParameters();
parameters.setPictureSize(pictureSize.width, pictureSize.height);
parameters.setPictureFormat(ImageFormat.JPEG);
camera.setParameters(parameters);

Without that change, I'd get a stretched aspect ratio, even in landscape. This wasn't a problem for CameraPreview of ApiDemos, as they were not taking pictures, and so they never called setPictureSize().

But, so far, on a Nexus 4, I now have square aspect ratios for portrait and landscape, with a floating orientation (i.e., not locked to landscape).

I'll try to remember to amend this question if I run into other hacks required by other devices, plus to link to my CameraView/CameraFragment components when they are released.


UPDATE #1

Well, of course, it couldn't possibly by nearly this simple. :-)

The fix for the problem where setPictureSize() screws up the aspect ratio works... until you take a picture. At that point, the preview switches to the wrong aspect ratio.

One possibility would be to limit pictures to ones with the same (or very close) aspect ratio to the preview, so the hiccup does not exist. I don't like that answer, as the user should be able to get whatever picture size the camera offers.

You can limit the scope of the damage by:

  • Only updating the picture size, etc. of the Camera.Parameters just before taking the picture
  • Reverting to the pre-picture-taking Camera.Parameters after taking the picture

This still presents the wrong aspect ratio during the moments while the picture is being taken. The long-term solution for this -- I think -- will be to temporarily replace the camera preview with a still image (the last preview frame) while the picture is being taken. I'll try this eventually.


UPDATE #2: v0.0.1 of my CWAC-Camera library is now available, incorporating the aforementioned code.

Android - Camera preview is sideways

This issue appeared to start out as a bug with certain hardware see here but can be overcome by using the call to mCamera.setDisplayOrientation(degrees) available in API 8. So this is how I implement it:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {            
if (isPreviewRunning) {
mCamera.stopPreview();
}

Parameters parameters = mCamera.getParameters();
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

if(display.getRotation() == Surface.ROTATION_0) {
parameters.setPreviewSize(height, width);
mCamera.setDisplayOrientation(90);
}

if(display.getRotation() == Surface.ROTATION_90) {
parameters.setPreviewSize(width, height);
}

if(display.getRotation() == Surface.ROTATION_180) {
parameters.setPreviewSize(height, width);
}

if(display.getRotation() == Surface.ROTATION_270) {
parameters.setPreviewSize(width, height);
mCamera.setDisplayOrientation(180);
}

mCamera.setParameters(parameters);
previewCamera();
}

And the previewCamera method :

public void previewCamera() {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
isPreviewRunning = true;
} catch(Exception e) {
Log.d(APP_CLASS, "Cannot start preview", e);
}
}

This was on an HTC Desire and I had to initially put in logging statements in each of the rotation checks to say what the rotation was and then debugged on the device and watched the logCat output while I rotated the device. For the HTC Desire, 0 was the phone as you would have expected (portrait), 90 degrees was turning the phone 90 degrees COUNTER-CLOCKWISE (I had assumed it would have been clockwise). In the code you'll see I didn't need to do any display rotation when the phone was at 90 or 180 degrees - the device seemed to handle this itself. Only one point not working properly: The 270 degree rotation is when you turn the device 90 degrees clockwise and the display rotation counters that ok but if you rotate the device 270 degrees counter-clockwise, it doesn't appear to compensate it properly.

P.S. Note the swapover of width and height in the appropriate rotations.

Android: retaining camera preview on orientation change while other views can rotate

If your manifest does not declare that the activity handles orientation changes, and is not locked to a single orientation, then rotation of the device will cause the whole activity to get unloaded and reloaded again. Isn't this what you experience now?

If you specify android:configChanges="orientation|screenSize" for your activity, then the user experience will be much smoother. But still, you cannot keep the camera preview "intact" after such rotation; the layout will be rebuilt.

Finally, the approach of the stock camera app is to set fixed (usually landscape) activity orientation and fake the portrait orientation by changing the buttons and other UI elements. But if you look for the system notifications or navigation (soft) buttons on the bottom, you will understand that actually device is locked in landscape, only controls are redrawn. You will also notice that camera apps avoid standard text widgets, because such widgets cannot be easily rotated.

By the way, there is another misunderstanding in your question. If you want to store the picture as portrait, you will need (or not need) manual rotation of the JPEG regardless of your activity orientation. On most devices, camera can only produce landscape JPEG files. Camera API includes setRotation() method, but usually, this only sets an EXIF flag in the JPEG header. Not all image viewers respect this flag. You may need manual rotation of the image if you don't want to compromize.



Related Topics



Leave a reply



Submit