Android camera image orientation without sufaceView - android

I am having a service in which I have to capture image without a surfaceView, everything is working perfect except the result image orientation, which I found to be miss-angled. On small device like HTC, I found it having issue or rotation so set rotation manually to see it working and it worked.
if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
parameters.setRotation(270);
} else if (camInfo.facing ==
Camera.CameraInfo.CAMERA_FACING_BACK) {
parameters.setRotation(90);
}
But when checking over samsung and HTC one (large devices) it creates problem with the angle. I found some posts where there I have to put the image path and then try to set the rotation, but that didn't work for me i.e. this as I am taking picture without serfaceview and then immediately posting it to the server. I also tried the google portion of code for setCameraOrientation() but it requires the activity view to work and so I am failed there too.
All I need is to fix the angle of the image before sent to the server without any surfaceview or activity thing.

setRotation() may only choose to play with the EXIF flags. The result is an image which is still rotated 90°, but with a "flag" that describes the correct orientation. Not all viewers correctly account for this flag. Specifically, BitmapFactory ignores it. You can draw the bitmap rotated on a canvas, or rotate the bitmap acquired from BitmapFactory.decodeFile(), or manipulate the JPEG data before writing it into outStream using a 3rd party lib, e.g. MediaUtil. The Android port is on GitHub.

You can have access to the image orientation information through the ExifInterface object. It will give you different values depending on the phone and if you capture the image in Landscape or Portrait mode. You can then use a matrix to rotate the image according to the ExifInterface information.And finally send it to your server.
Knowing the path of your image ( imagePath ), use the following code:
Matrix matrix = new Matrix();
try{
ExifInterface exif = new ExifInterface(imagePath);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
// Change the image orientation
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
// Change the image orientation
matrix.postRotate(180);
break;
}catch (IOException e) {
e.printStackTrace();
}
Then you use your matrix object in order to rotate your bitmap using:
rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix, true);
You would have to save the bitmap somewhere ( external or internal temp storage ) before sending it to the server.
Hope it helps you.

Your rotation-setting code is a bit too simple, so it's possible that on some devices it'll do the wrong thing. It's not guaranteed that these are the right rotations - the correct answer is a function of your device's current orientation, and the orientation of the sensor on the device.
Take a look at the sample code for Camera.Parameters.setRotation:
public void onOrientationChanged(int orientation) {
if (orientation == ORIENTATION_UNKNOWN) return;
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
orientation = (orientation + 45) / 90 * 90;
int rotation = 0;
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
mParameters.setRotation(rotation);
}
If you don't have an activity, you'll have to figure out how to get the device's current orientation some other way, but you do need to include info.orientation in your calculation.

Related

Can I get the orientation of a photo taken in my app if I limit the activity's orientation to portrait only in my manifest?

I don't want to support landscape UI at all across my app, but I want to be able to automatically rotate the photos users take in landscape mode.
Currently if a user takes a photo in landscape mode, it remains on screen as if it was taken in portrait (the horizon in the photo is vertical).
I've tried to get the orientation from the system like this:
val display = (getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
val screenOrientation = display.rotation
Log.d("orientation", screenOrientation.toString())
But it doesn't work and just gives back 0 every time.
I've tried a few other solutions but couldn't get them to work.
I am wondering, am I wasting my time trying to figure this out? Is it even possible to know the orientation in which the photo was captured if my activity only operates in portrait mode?
There are a few articles out there talking about camera orientation but they don't talk about whether the orientation in their activity is locked or not.
Thank you.
//Yes, find the Orientation Using ExifInterface
try{
File imageFile = new File(imagePath);
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
// dont know exactly but, orientation for landscape may be 180 or 270.
// From the orientation value you can convert image according to orientation and can be set it vertically, horrizentally as below
int rotate = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}
Log.e(Common.TAG, "Exif orientation: " + orientation);
}
catch (Exception e) {
e.printStackTrace();
}
// Now Change image as your wish...
// Image rotation //
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
Bitmap cropped = Bitmap.createBitmap(scaled, x, y, width, height, matrix, true);

Correctly orient raw camera data

My camera app processes raw camera frames from onPreviewFrame() with OpenCV and then displays them on the screen. However, the raw frames are oriented the way the camera is mounted on the phone, which is usually not right-side up. To fix this, I rotate them manually with OpenCV, which is time-consuming.
I've looked into using setDisplayOrientation, but the documentation states that
This does not affect the order of byte array passed in onPreviewFrame
and I need the data to actually be right-side up, not just display right-side up. How can I correctly orient the raw camera data? If this is impossible, can I efficiently rotate the byte array passed to me in onPreviewFrame(), say by using OpenGL?
No, there's no way to get the API to do this for you, but to figure out how you need to rotate the data, take a look at the sample code at Camera.Parameters.setRotation. While setRotation() only affects JPEGs, you want to apply the exact same amount of rotation to the data you get in onPreviewFrame() as you would to the JPEGs.
Reproducing the sample code here, with minor changes:
public void onOrientationChanged(int orientation) {
if (orientation == ORIENTATION_UNKNOWN) return;
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
orientation = (orientation + 45) / 90 * 90;
int rotation = 0;
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
mCameraOrientation = rotation; // Store rotation for later use
}
...
void onPreviewFrame(byte[] data, Camera camera) {
switch(mCameraOrientation) {
case 0:
// data is correctly rotated
break;
case 90:
// rotate data by 90 degrees clockwise
case 180:
// rotate data upside down
case 270:
// rotate data by 90 degrees counterclockwise
}
}
So you need to inherit from OrientationEventListener and override onOrientationChanged as above, and then use the calculated orientation value from there to rotate the preview frames when they come in.
setDisplayOrientation only rotates the camera vs. surface you are drawing into, if I recall correctly.
I believe you want to rotate the camera interpretation, what you may could achieve with setRotation

Is the setDisplayOrientation sample code correct?

The documentation page for Camera.setDisplayOrientation contains the following code sample stating that using it will "make the camera image show in the same orientation as the display":
public static 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);
}
However, I had problems using it, sometimes the image would appear upside down. After some trial-and-error, I found that the correct code would be (replacing the last 8 lines of the method):
int result = (360 + info.orientation - degrees) % 360;
camera.setDisplayOrientation(result);
(Which means the calculation for back-facing cameras is correct for front cameras too.) The "compensate the mirror" comment is a bit weird since mirroring cannot be undone by rotating, that operation only swaps 90° and 270° rotations which doesn't really make sense to me.
So the question is: is the sample wrong indeed or am I missing something? I tried it on multiple devices, both back and front cameras and all supported orientations, so I know that my code IS working. One little detail that might be worth mentioning: all my devices returned 90° as info.orientation.
EDIT: Here is my camera-related code, I have tested it on a Nexus One and a Samsung Galaxy S Plus. It is used in my head-tracking 3D app, the preview is shown in the lower left corner and should always have the correct orientation.
SOLUTION (sort of): It looks like the code is correct, but my testing phone (Samsung Galaxy S Plus) returns an incorrect value for the front camera's CameraInfo.orientation. There are many related discussions about the preview being shown upside down on this model (examples here and here). A way to fix is to include an option to manually rotate the image.
The snippet you've quoted, which I used and applied in my project, is no problem in my circumstances.
For all the devices (Galaxy Note, Galaxy S III, Galaxy Nexus, Galaxy Ace II, Nexus S) I used for test, info.Orientation all return 270 on front camera, and 90 on back camera.
After a few discuss with the question raiser, I found I misunderstood the questions, so I divide the answer in two parts.
For the wrong orientation in camera preview, please refer to this solution:
Solution for wrong orientation in camera preview:
First please ensure info.Orientation will return 270 on front camera, 90 on back camera.
Then please try set your camera preview activity (or similar class that handles the preview) orientation to landscape.
Thus, when you go through the code, you'll find:
degree = 90 for screen orientation, info.Orientation = 270 for the front camera. Then you'll get result = (270 - 90 + 360) % 360, result = 180, which means it will rotate clock wise 180 for your front camera view that will correct the front camera upside-down issue.
Solution for wrong orientation in camera photo results:
If this info.Orientation applies to you, then the problem may be:
For some Samsung (or other) devices (Like Galaxy Note, Galaxy SIII), the camera will only write the Orientation Tag instead of rotate the real pixels.
If you're using the front camera and using the code above, it will display the preview with correct orientation, but will show u the "upside down" picture if you take the shot.
Solution:
/**
*
* Get the orientation from EXIF
 * #param filepath
 * #return orientation
 */
public int getExifOrientation(String filepath) {
int degree = 0;
ExifInterface exif = null;
try {
exif = new ExifInterface(filepath);
} catch (IOException ex) {
Log.e("EXIF info", "cannot read exif", ex);
}
if (exif != null) {
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
if (orientation != -1) {
// We only recognize a subset of orientation tag values.
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
}
} else {
degree = 1;
}
Log.i("EXIF info", "Orientation degrees: " + degree);
return degree;
}
Then
if (isFromCamera) {
if (fromFrontCam) {
// try change here if the orientation still wrong, -90 means rotate counter-clockwise 90 degrees.
matrix.preRotate(-90);
} else {
matrix.preRotate(90);
}
} else {
// read EXIF data
getExifOrientation(path)
// don't forget to handle the situation that some devices won't write exif data
matrix.preRotate(degree);
}

Picture orientation from gallery/camera intent [duplicate]

This question already has answers here:
Images taken with ACTION_IMAGE_CAPTURE always returns 1 for ExifInterface.TAG_ORIENTATION on some Gingerbread devices
(5 answers)
Closed 4 years ago.
I'm getting picture to my app from camera / gallery intent. In many phones picture that I read from the intent / Uri is already rotated to correct orientation. For example N1, Legend, Desire that is the case.
But then on some phones ( for example Milestone1, GalaxyS) the picture is always in landscape more no matter which way the picture was taken. This means that in my application portrait picture is presented wrong way to the user. I tried to read EXIF info of the picture but orientation tag is always 0. There has to be a way to find out the right orientation of the picture because in Milestone1 the gallery application shows the portrait pictures correctly.
How do I know if I need to rotate the picture myself before showing it to the user?
Thank you for you help!
Florian answer is working for gallery images.
The following code works for captured images, havn't tried with gallery images but i believe it should work. Hope this helps anybody.
Code :
public static int getCameraPhotoOrientation(Context context, Uri imageUri, String imagePath){
int rotate = 0;
try {
context.getContentResolver().notifyChange(imageUri, null);
File imageFile = new File(imagePath);
ExifInterface exif = new ExifInterface(
imageFile.getAbsolutePath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}
Log.v(TAG, "Exif orientation: " + orientation);
} catch (Exception e) {
e.printStackTrace();
}
return rotate;
}
EDIT:
As can be read in the comments, some devices do not support Exif information, havn't checked which but i think HTC doesn't. be sure to check what devices and create an alternative.
The following method returns the orientation of an image in degrees. It works with images from the gallery. Return values for:
normal landscape: 0
normal portrait: 90
upside-down landscape: 180
upside-down portrait: 270
image not found: -1
The code:
public static int getOrientation(Context context, Uri photoUri) {
Cursor cursor = context.getContentResolver().query(photoUri,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
null, null, null);
try {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
} else {
return -1;
}
} finally {
cursor.close();
}
}
int rotate = 0;
try {
File imageFile = new File(sourcepath);
ExifInterface exif = new ExifInterface(
imageFile.getAbsolutePath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
bitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
This is the best answer I have found, by ramaral. https://stackoverflow.com/a/20480741/2258389
Works great for gallery or camera
You have to create the gallery object using contentresolver first and then pass the uri created to the image capture intent. Then you can look at the exif data, NOT the gallery orientation data.
Matrix matrix = new Matrix();
ExifInterface exifReader = new ExifInterface(filePath);
int orientation = exifReader.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
if (orientation ==ExifInterface.ORIENTATION_NORMAL) {
// Do nothing. The original image is fine.
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
matrix.postRotate(90);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
matrix.postRotate(180);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
matrix.postRotate(270);
}
I have the same problem with S1, and I noted even if you open the image using stock gallery app it will open in landscape mode(the phone doesn’t know the correct orientation for photo).
And if you rotate the phone to portrait in camera app the icons (take pic, focus and settings) will not rotated, try that in S2 or any device support landscape/portrait camera those icons will be rotated.
What I am sure the stock camera app doesn’t support taking photos in prorate mode.
I already did that for a project, because I had the same problem (Android thinking you'll only do a picture on landscape). What I did was detecting the phone orientation at the time, and then rotate the image. It supposes the orientation is still the same when the intent is received, though.
This looks like the same problem that was solved in this question:
Android: Bitmaps loaded from gallery are rotated in ImageView
Use it this way!
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
And in the code,
Bitmap newImage = Bitmap.createBitmap(actualImage, 0, 0, width, height, matrix, true);
Simple!
What I am doing :
first check the orientation of image taken by camera using its meta data information , and If we found this in portrait then we have to rotate the image by 90 and display otherwise only display.
For getting the Information about orientation of image we can use Exif interface.
That's It!
The simple exiftodegree answers only consistently work when you have taken a photo and i use it for such. For those of you experiencing issues with choosing a photo and getting the correct orientation, see my answer here: Image Orientation - Android

Android camera unexplainable rotation on capture for some devices (not in EXIF)

What I'm doing seems like it should be simple, but I'm still lost after I've read every possible Stackoverflow answer I can find and Googled every article I can find.
I'm using a preview SurfaceView and capturing an image from an activity that is set for screenOrientation="landscape" in my AndroidManifest.xml.
I followed the sample Camera app code and thought things were working until I tried my app on a few Motorola devices running 1.5.
I have the OrientationEventListener running OK and I use reflection to see if set the rotation as such:
final int latchedOrientation = roundOrientation(mLastOrientation + 90);
Parameters parameters = preview.camera.getParameters();
JPLog.d("Setting camera rotation = %d", latchedOrientation);
try {
// if >= 2.0
Method method = Camera.Parameters.class.getMethod("setRotation",
int.class);
if(method != null) {
method.invoke(parameters, latchedOrientation);
}
} catch(Throwable t) {
// if < 2.0
parameters.set("rotation", latchedOrientation);
}
preview.camera.setParameters(parameters);
NexusOne (OS 2.2) - Works great. latchedOrientation = 0, picture OK without any rotation in the EXIF header.
T-Mobile G1 (OS 1.6) - Also works great. latchedOrientation = 0, picture OK.
Motorola Backflip (OS 1.5) - Image rotated. latchedOrientation = 0, picture has no EXIF rotation in it.
Motorola CLIQ (OS 1.5) - Image rotated. latchedOrientation = 0, picture has no EXIF rotation in it.
What's going on with these Motorola devices? I thought my problem was the Motorola camera driver wasn't rotating the images, so found the Sanselan EXIF reading classes for Android and was preparing to rotate them myself. Funny thing is, there is EXIF headers but no rotation element.
If I set the rotation manually to 90 degrees, the images come out perfect the Motorola devices, but now the G1 and the NexusOne have images that are rotated 90 degrees (not what I want). There has to be something I'm not getting here.
I'm doubting this is a 1.5 issue, or else someone would've posted info on it?
I had this issue and I used this method to capture the image. (Without creating a custom camera)
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(intent, 0);
and did the rest in onActivityResult(int requestCode, int resultCode, Intent data) {}
But the original image (Actual SD card image) was correct and Bitmap was rotated when I fetched like this.
Bitmap bmp = BitmapFactory.decodeStream(..
The solution:
try {
File f = new File(SD_CARD_IMAGE_PATH);
ExifInterface exif = new ExifInterface(f.getPath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int angle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
angle = 90;
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
angle = 180;
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
angle = 270;
}
Matrix mat = new Matrix();
mat.postRotate(angle);
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
Bitmap correctBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
}
catch (IOException e) {
Log.w("TAG", "-- Error in setting image");
}
catch(OutOfMemoryError oom) {
Log.w("TAG", "-- OOM Error in setting image");
}
This is actually a device-specific issue that mostly affects Motorola devices. The google devs included a setDisplayOrientation call in API 8 to work around the issue. The main bug is filed here.
For those that can't go to API 8, the two common solutions are:
Override onDraw
Override onDraw in a top-level ViewGroup and rotate the canvas by 90 degrees to compensate for the rotation. Note there is a caveat here as your touch events will also need to be rotated.
Use Landscape Mode
Lock the activity to landscape mode but draw assets as if they are in portrait. This means you do your layout and rotate your image assets to look like you are in portrait mode so that the view looks normal. This unfortunately makes it difficult to use the menu since the menu will open horizontally.
I have also seen people use an animation controller to rotate the view. The drawback here that I wasn't able to overcome is that the rotated view doesn't stretch to fill the screen. See the answer by Georg for a sample implementation.
Here is the code I used onActivityResult() in my activity. The intent returned was for picking an image of the type image/*. Works well for me!
Uri imageUri = intent.getData();
String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
int orientation = -1;
if (cur != null && cur.moveToFirst()) {
orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
}
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
It looks like the 'use landscape mode' suggestion is the only thing that really works. It seems that it's ok for this to be in either the manifest, or done via a call to setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) in the activity onCreate.

Categories

Resources