In Android I have noticed that when I take a picture with the camera and map it to an ImageView, on some cameras (such as my physical phone), it's rotated 90 degrees (whereas on the emulator phones I use, they aren't rotated).
I tried this:
//once you have the main bitmap
ExifInterface ei = null;
try {
ei = new ExifInterface(imageFilepath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
bitmap = getRotatedBitmap(bitmap, orientation);
}
catch (IOException e) {
e.printStackTrace();
}
Now the rotate code:
public static Bitmap getRotatedBitmap(Bitmap bitmap, int orientation) {
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return bitmap;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
default:
return bitmap;
}
try {
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return bmRotated;
}
catch (OutOfMemoryError e) {
return bitmap;
}
}
Is this correct? Or is it horribly inefficient? Is there some smarter way to do this? If I apply the rotate code to a bitmap and then save it back to Storage, will it now be "incorrectly rotated" or do I have to rotate it back? I don't know what the accepted practice is for all this.
Is this correct?
I am skeptical that you will actually see all of those orientation possibilities in the wild. And rotating the image takes up a lot of heap space, so expect this code to fail with an OutOfMemoryError sometimes.
Is there some smarter way to do this?
If all you want is for the image to show up properly in the ImageView, rotate the ImageView.
If I apply the rotate code to a bitmap and then save it back to Storage, will it now be "incorrectly rotated" or do I have to rotate it back?
That's impossible to answer, as only you know how you are saving it and what "incorrectly" would mean in that context. IMHO, if you are going through this process, you rotate it to the ORIENTATION_NORMAL state, then either save it with no EXIF headers or save it with ORIENTATION_NORMAL.
Related
So I noticed that on some devices my captured image (bitmap) is weirdly rotated - to 90 degrees usually. I did create a function that can rotate it correctly, but the problem now is, that sometimes the image comes in (is captured) perfectly okay. Is there a way I can find out if captured image is rotated (it's orientation)?
I found a code fragment like this:
try {
ExifInterface exif = new ExifInterface(filename);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
int rotate = 0;
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate-=90;break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate-=90;break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate-=90;break;
}
...
}
But ExifInterface exif = new ExifInterface(filename); expects a file or file uri to be created. Does this mean I have to change my bitmap to file first and then use this approach or is there other ways to fight with this thing?
P.S. I'm using Android's Camera X
It is recommended to use the AndroidX ExifInterface Library. You can use the ExifInterface(InputStream) if the captured image is Jpeg by wrapping the buffer with InputStream.
The sensor orientation is different across devices. CameraX respects that and returns the image with a rotation value in the callback, which can be used to rotate it upright.
https://developer.android.com/reference/androidx/camera/core/ImageCapture.OnImageCapturedListener.html#onCaptureSuccess(androidx.camera.core.ImageProxy,%20int)
you can try this
Bitmap rotateBitmap(String src, Bitmap bitmap) {
DebugLog.write();
int orientation;
try {
orientation = getExifOrientation(src);
} catch (IOException e) {
e.printStackTrace();
return bitmap;
}
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
return bitmap;
}
try {
Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle(); //wht
return oriented;
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
return bitmap;
}
private int getExifOrientation(String src) throws IOException {
DebugLog.write();
ExifInterface exifInterface = new ExifInterface(src);
return exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
}
I have tried most of the code on stackoverflow but none of them are working.
I am using moto x4 for uploading picture using camera. when I use back camera it gets rotated 90 degree left and when I use front camera it gets rotated 90 degree right. but in debug mode, in both case I found the orientation = 0;
else if (requestCode == CAMERA) {
Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
String imagePath = saveImage(thumbnail);
File imageFile = new File(imagePath);
ExifInterface exif = null;
try {
exif = new ExifInterface(imageFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
bmap = GetandSetBitmap.rotateBitmap(thumbnail,orientation);
mImageView.setBackgroundResource(0);
mImageView.setImageBitmap(bmap);
}
This probably has to do with the fact that one camera is by default in landscape and the other in reverse landscape, so orientation = 0, as the orientation is detected to be the normal one in both cases. Unfortunately, I don't have a solution that wouldn't involve manually rotating the image to cover all cases. Personally I've used a switch to cater for the cases where my photo was saved with a 90 degrees rotation:
ExifInterface exifInterface = new ExifInterface(pictureFile.getPath());
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Bitmap correctedBitmap;
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
correctedBitmap = bitmap;
capturedImageHolder.setImageBitmap(correctedBitmap);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
correctedBitmap = rotateImage(bitmap, 90);
capturedImageHolder.setImageBitmap(correctedBitmap);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
correctedBitmap = rotateImage(bitmap, 180);
capturedImageHolder.setImageBitmap(correctedBitmap);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
correctedBitmap = rotateImage(bitmap, 270);
capturedImageHolder.setImageBitmap(correctedBitmap);
break;
}
} catch (IOException e) {
}
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
matrix, true);
}
In your case, you would need to detect if the photo is taken from the front-facing camera or the back and adjust the values accordingly.
Issues facing on Samsung, when I am capturing the image, the captured image get rotated. I am trying to rotate the image in the vertical direction 90 degree. On Back Facing the image get rotated with below code. But two images get stored on the device. Also issues facing while I am capturing the image on front face the image get rotated, how I can handle the image rotation in below code for both the front and back facing. Any ideas ?
try {
upload_bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
large_bitmap = scaleDownLargeImageWithAspectRatio(upload_bitmap);
String fileNameLarge = "myapp_" + System.currentTimeMillis() + ".jpg";
large_bitmap_path = MediaStore.Images.Media.insertImage(getContentResolver(), large_bitmap, fileNameLarge, null);
//Start change orientation
String device_name = Build.MANUFACTURER;
if(device_name.equals("samsung"))
{
int rotate=0;
try {
String realPath = getRealPathFromUri(CameraActivity.this, Uri.parse(large_bitmap_path));
exif = new ExifInterface(realPath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
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;
case ExifInterface.ORIENTATION_NORMAL:
case ExifInterface.ORIENTATION_UNDEFINED:
break;
}
} catch (Exception e) {
e.printStackTrace();
}
Matrix matrix = new Matrix();
matrix.postRotate(90);
large_bitmap = Bitmap.createBitmap(large_bitmap, 0, 0, large_bitmap.getWidth(), large_bitmap.getHeight(), matrix, true);
horizontalList.add(large_bitmap_path);
horizontal_rv.smoothScrollBy(1000, 10);
sqLiteHelper.insertPath(large_bitmap_path, "jpg", "fileName");
}
use this library from git hub it handles all these thing related to image library link
Im using one of api face tracker sample and i modified code a bit to see if it will work according to my idea for app. what i want to do is i want to add face masks after detecting face.
What i've done so far, i've added a sample mask drawable and when i detect a face instead of drawing face points i draw drawable in face tracking rectangle. now its showing that mask in preview on the face but when i try to capture that image it only capture frame from camera not with graphic overlay i added mask on. is there any way i can capture from camera with that mask on it ?
Image being saved
image being displayed on mobile screen
I have achieved something similar to this in a project i worked on in the past but no longer have access to the project.
when you call your capture method you need to store a reference to the position of the face.
Im not sure how much control the vision api gives you over the camera so you either:
take the picture and before saving the file add the mask resource on top of the returned bitmap.
or
load the saved file add the mask resource on top of the it.
I will have a look around later for some code if it will help.
Edit Rotate Bitmap
bitmap = android.provider.MediaStore.Images.Media
.getBitmap(cr, selectedImage);
ExifInterface exif = new ExifInterface("/storage/emulated/0/Pic.jpg");
String exifOrientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int i = Integer.parseInt(exifOrientation);
bitmap = rotateImage(bitmap, i);
//
private Bitmap rotateImage(Bitmap bm, int i) {
Matrix matrix = new Matrix();
switch (i) {
case ExifInterface.ORIENTATION_NORMAL:
return bm;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
default:
return bm;
}
try {
Bitmap bmRotated = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
bm.recycle();
return bmRotated;
} catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
I know applying EXIF orientation on the bitmap object, doing this -
public static Bitmap getCorrectBitmap(Bitmap bitmap, String filePath) {
ExifInterface ei;
Bitmap rotatedBitmap =bitmap;
try {
ei = new ExifInterface(filePath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate(270);
break;
}
rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap .getWidth(), bitmap .getHeight(), matrix, true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rotatedBitmap;
}
But, what I am having is - a few rotated images (camera images) uri and their corresponding file paths (of sd card).
It will be great, if someone suggests me a good way to apply similar EXIF orientation on the image uri and getting back the correctly oriented uri.
I do not want to involve bitmap object in this process as getting bitmap of the image consumes time (about 1 second per image).