Reputation: 10376
I'm finding in onPictureTaken
that the bitmap saved is mirrored about the y-axis and rotated 90 degrees clockwise even though the camera preview was not. This is on my Nexus S that's running 2.3.6. The same program running on my Nexus 4 with 4.2 has the resulting bitmap mirrored about the y-axis and rotated 180 degrees clockwise.
This is the code I'm running in onPictureTaken
:
@Override
public void onPictureTaken(final byte[] data, Camera camera) {
Bitmap picture = BitmapFactory.decodeByteArray(data, 0, data.length);
String path = MediaStore.Images.Media.insertImage(getContentResolver(), picture, "name" , "description");
Log.e("tag", "path: " + path); // prints something like "path: content://media/external/images/media/819"
try {
ExifInterface exif = new ExifInterface(path); // prints this error: "04-25 21:28:21.063: E/JHEAD(12201): can't open 'content://media/external/images/media/819'"
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Log.e("tag", "exif orientation: " + orientation); // this is outputting orientation unknown
} catch (IOException e) {
e.printStackTrace();
}
}
Can anyone show me how I rectify this given that I seem to be getting different results from different devices? How do I detect the orientation of the resulting bitmap so that I know to rotate it either 90 or 180 degrees counterclockwise?
[EDIT]
I added some more information using the ExifInterface stuff I've been reading about, but that information doesn't seem to pan out...
Upvotes: 2
Views: 12167
Reputation: 2064
Instead of rotating the picture explicitly in the picture taken callback, you can configure the camera to have picture rotated for you when the picture is taken.
camera.SetDisplayOrientation(degrees) //sets the orientation in the preview
while
cameraParameters.SetRotation(degress) //rotates the actual captured image
Upvotes: 0
Reputation: 642
I have put a lot of work into this and thought, I'd share my solution. It is tested on a Motorola Devy, Samsung Xcover 1 and Samsung XCover 2.
As I work with a custom camera preview, the solution basically has two parts. 1. Take care of the camera preview and set rotation of the preview according to device rotation. 2. Once a picture is taken, that is the 'onPictureTaken' callback is invoked rotate the picture by the correct angle, such that it shows what the preview just showed.
private void initPreview(int width, int height) {
if (camera != null && holder.getSurface() != null) {
try {
camera.setPreviewDisplay(holder);
} catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
Toast.makeText(getContext(), t.getMessage(),
Toast.LENGTH_LONG).show();
}
try {
Camera.Parameters parameters=camera.getParameters();
Camera.Size size=getBestPreviewSize(width, height, parameters);
Camera.Size pictureSize=getSmallestPictureSize(parameters);
Display display = windowManager.getDefaultDisplay();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
if (isPortrait(display)) {
parameters.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
} else {
parameters.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
}
} else { // for 2.2 and later
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
else parameters.setPreviewSize(size.width, size.height);
camera.setDisplayOrientation(90);
break;
case Surface.ROTATION_90:
if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
else parameters.setPreviewSize(size.width, size.height);
camera.setDisplayOrientation(0);
break;
case Surface.ROTATION_180:
if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
else parameters.setPreviewSize(size.width, size.height);
camera.setDisplayOrientation(270);
break;
case Surface.ROTATION_270:
if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
else parameters.setPreviewSize(size.width, size.height);
camera.setDisplayOrientation(180);
break;
}
}
parameters.setPictureSize(pictureSize.width, pictureSize.height);
//parameters.setPictureFormat(ImageFormat.JPEG);
camera.setParameters(parameters);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Your 'surfaceChanged' method, in your camera preview (SurfaceView), you should look like this:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
stopPreview();
initPreview(w, h);
startPreview();
}
where
stopPreview:
private void stopPreview() {
if (camera != null) {
camera.stopPreview();
}
}
startPreview:
private void startPreview() {
if (camera != null) {
camera.startPreview();
}
}
In your 'onPictureTaken' callback rotate the picture, using the following code:
Display display = getWindowManager().getDefaultDisplay();
int rotation = 0;
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
rotation = 90;
break;
case Surface.ROTATION_90:
rotation = 0;
break;
case Surface.ROTATION_180:
rotation = 270;
break;
case Surface.ROTATION_270:
rotation = 180;
break;
}
Bitmap bitmap = BitmapTools.toBitmap(data);
bitmap = BitmapTools.rotate(bitmap, rotation);
BitmapTools.java
public class BitmapTools {
public static Bitmap toBitmap(byte[] data) {
return BitmapFactory.decodeByteArray(data , 0, data.length);
}
public static Bitmap rotate(Bitmap in, int angle) {
Matrix mat = new Matrix();
mat.postRotate(angle);
return Bitmap.createBitmap(in, 0, 0, in.getWidth(), in.getHeight(), mat, true);
}
}
Upvotes: 15
Reputation: 1039
you must read about ExifInterface to resolve this issue.
i have this function in my application to check weather weather image taken from camera needs to be rotateed or not.
if(ExifNeedsRotate(GetPathFromUri(context, selectedImage))){
// Rotate your bitmap using the Matrix
}
public static boolean ExifNeedsRotate(String paramString){
if (android.os.Build.VERSION.SDK_INT >= 5){
try
{
Class localClass = Class.forName("android.media.ExifInterface");
Class[] arrayOfClass1 = new Class[1];
arrayOfClass1[0] = String.class;
Constructor localConstructor = localClass.getConstructor(arrayOfClass1);
Class[] arrayOfClass2 = new Class[1];
arrayOfClass2[0] = String.class;
Method localMethod = localClass.getMethod("getAttribute", arrayOfClass2);
Object[] arrayOfObject1 = new Object[1];
arrayOfObject1[0] = paramString;
Object localObject1 = localConstructor.newInstance(arrayOfObject1);
Object[] arrayOfObject2 = new Object[1];
arrayOfObject2[0] = "Orientation";
Object localObject2 = localMethod.invoke(localObject1, arrayOfObject2);
if (localObject2 != null){
boolean bool = localObject2.equals("6");
if (bool)
return true;
}
}
catch (Exception localException){
return false;
}
}
return false;
}
Pass the path of the ImageUri as a input.
public static String GetPathFromUri(Context paramContext, Uri paramUri)
{
String str;
try
{
if (paramUri.toString().startsWith("file:")){
str = paramUri.getPath();
}
else
{
str = null;
String[] arrayOfString = new String[1];
arrayOfString[0] = "_data";
Cursor localCursor = paramContext.getContentResolver().query(paramUri, arrayOfString, null, null, null);
if (localCursor != null)
{
localCursor.moveToFirst();
int i = localCursor.getColumnIndex(arrayOfString[0]);
if ((localCursor.getCount() >= 1) && (localCursor.getColumnCount() >= i + 1))
str = localCursor.getString(i);
localCursor.close();
}
}
}
catch (Exception localException){
str = null;
}
return str;
}
Upvotes: 0
Reputation: 7114
here you go check this out, save the picture and maybe this will work and remember if(bitmap.getWidth > bitmap.getHeight()) as another check
public static int getExifRotation(String imgPath)
{
try
{
ExifInterface exif = new ExifInterface(imgPath);
String rotationAmount = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
if (!TextUtils.isEmpty(rotationAmount))
{
int rotationParam = Integer.parseInt(rotationAmount);
switch (rotationParam)
{
case ExifInterface.ORIENTATION_NORMAL:
return 0;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
}
}
else
{
return 0;
}
}
catch (Exception ex)
{
return 0;
}
}
Upvotes: 0