Reputation: 11711
I'm capturing an image and setting it to image view.
public void captureImage() {
Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
imageUri = Uri.fromFile(filePhoto);
MyApplicationGlobal.imageUri = imageUri.getPath();
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intentCamera, TAKE_PICTURE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
super.onActivityResult(requestCode, resultCode, intentFromCamera);
if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {
if (intentFromCamera != null) {
Bundle extras = intentFromCamera.getExtras();
if (extras.containsKey("data")) {
bitmap = (Bitmap) extras.get("data");
}
else {
bitmap = getBitmapFromUri();
}
}
else {
bitmap = getBitmapFromUri();
}
// imageView.setImageBitmap(bitmap);
imageView.setImageURI(imageUri);
}
else {
}
}
public Bitmap getBitmapFromUri() {
getContentResolver().notifyChange(imageUri, null);
ContentResolver cr = getContentResolver();
Bitmap bitmap;
try {
bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
return bitmap;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
But the problem is, the image on some devices every time it gets rotated. For example, on a Samsung device it works good, but on a Sony Xperia the image gets rotated by 90 degrees and on Toshiba Thrive (tablet) by 180 degrees.
Upvotes: 473
Views: 305577
Reputation: 1648
So first thing, you sholuld know that since Android 7.0 we have to use FileProvider
and something called ContentUri
, otherwise you will get an annoying error trying to invoke your Intent
. This is sample code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);
Method getUriFromPath(Context, String)
basis on user version of Android create FileUri (file://...)
or ContentUri (content://...)
and there it is:
public Uri getUriFromPath(Context context, String destination) {
File file = new File(destination);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
} else {
return Uri.fromFile(file);
}
}
After onActivityResult
you can catch that uri
where image is saved by camera, but now you have to detect camera rotation, here we will use moddified @Jason Robinson answer:
First we need to create ExifInterface
based on Uri
@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
try {
String path = uri.toString();
if (path.startsWith("file://")) {
return new ExifInterface(path);
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (path.startsWith("content://")) {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
return new ExifInterface(inputStream);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
Above code can be simplified, but i want to show everything. So from FileUri
we can create ExifInterface
based on String path
, but from ContentUri
we can't, Android doesn't support that.
In that case we have to use other constructor based on InputStream
. Remember this constructor isn't available by default, you have to add additional library:
compile "com.android.support:exifinterface:XX.X.X"
Now we can use getExifInterface
method to get our angle:
public float getExifAngle(Context context, Uri uri) {
try {
ExifInterface exifInterface = getExifInterface(context, uri);
if(exifInterface == null) {
return -1f;
}
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90f;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180f;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270f;
case ExifInterface.ORIENTATION_NORMAL:
return 0f;
case ExifInterface.ORIENTATION_UNDEFINED:
return -1f;
default:
return -1f;
}
}
catch (Exception e) {
e.printStackTrace();
return -1f;
}
}
Now you have Angle to properly rotate you image.
Upvotes: 16
Reputation: 10009
By combining Jason Robinson's answer with Felix's answer and filling the missing parts, here is the final complete solution for this issue that will do the following after testing it on Android Android 4.1 (Jelly Bean), Android 4.4 (KitKat) and Android 5.0 (Lollipop).
Steps
Scale down the image if it was bigger than 1024x1024.
Rotate the image to the right orientation only if it was rotate 90, 180 or 270 degree.
Recycle the rotated image for memory purposes.
Here is the code part:
Call the following method with the current Context
and the image URI
that you want to fix
/**
* This method is responsible for solving the rotation issue if exist. Also scale the images to
* 1024x1024 resolution
*
* @param context The current context
* @param selectedImage The Image URI
* @return Bitmap image results
* @throws IOException
*/
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
throws IOException {
int MAX_HEIGHT = 1024;
int MAX_WIDTH = 1024;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close();
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
imageStream = context.getContentResolver().openInputStream(selectedImage);
Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);
img = rotateImageIfRequired(context, img, selectedImage);
return img;
}
Here is the CalculateInSampleSize
method from the pre mentioned source:
/**
* Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
* the closest inSampleSize that will result in the final decoded bitmap having a width and
* height equal to or larger than the requested width and height. This implementation does not
* ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
* results in a larger bitmap which isn't as useful for caching purposes.
*
* @param options An options object with out* params already populated (run through a decode*
* method with inJustDecodeBounds==true
* @param reqWidth The requested width of the resulting bitmap
* @param reqHeight The requested height of the resulting bitmap
* @return The value to be used for inSampleSize
*/
private static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee a final image
// with both dimensions larger than or equal to the requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down further
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
Then comes the method that will check the current image orientation to decide the rotation angle
/**
* Rotate an image if required.
*
* @param img The image bitmap
* @param selectedImage Image URI
* @return The resulted Bitmap after manipulation
*/
private static Bitmap rotateImageIfRequired(Context context, Bitmap img, Uri selectedImage) throws IOException {
InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
ei = new ExifInterface(input);
else
ei = new ExifInterface(selectedImage.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
Finally the rotation method itself
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
Upvotes: 225
Reputation: 3258
here a solution that is based on the ones above but only needs context and the image file as inputs
public static Bitmap rectifyImage(Context context,File imageFile){
Bitmap originalBitmap= BitmapFactory.decodeFile(imageFile.getAbsolutePath());
try{
Uri uri=Uri.fromFile(imageFile);
InputStream input = context.getContentResolver().openInputStream(uri);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
ei = new ExifInterface(input);
else
ei = new ExifInterface(uri.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(originalBitmap, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(originalBitmap, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(originalBitmap, 270);
default:
return originalBitmap;
}
}catch (Exception e){
return originalBitmap;
}
}
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);
}
Upvotes: 5
Reputation: 135
I solved it using a different method. All you have to do is check if the width is greater than height
Matrix rotationMatrix = new Matrix();
if(finalBitmap.getWidth() >= finalBitmap.getHeight()){
rotationMatrix.setRotate(-90);
}else{
rotationMatrix.setRotate(0);
}
Bitmap rotatedBitmap = Bitmap.createBitmap(finalBitmap,0,0,finalBitmap.getWidth(),finalBitmap.getHeight(),rotationMatrix,true);
Upvotes: 6
Reputation: 1327
by using glide lib you can get image with exact orientation not need to check rotation
in kotlin
CoroutineScope(Dispatchers.IO).launch {
var bitmap = Glide.with(context).asBitmap().load(imagePathOrUriOrLink)
/*.apply(
RequestOptions()
.override(MAXIMUM_IMAGE_RESOLUTION)
)*/ //uncomment it if you want original image
/*.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)*/ //uncomment it you want to not cache image
.submit().get()//this is synchronous approach
}
using this dependency
api 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'
Upvotes: 0
Reputation: 41
Use of Glide library worked for me. Rotation is taken cared of automatically.
Bitmap bitmap = Glide.with(myContext).asBitmap().load(imageFilePath).submit(SIZE_ORIGINAL, SIZE_ORIGINAL).get();
Then you save that bitmap to a file in JPEG format, for example.
If you just want to load into an ImageView
instead of saving to a file:
Glide.with(myContext).load(imageFilePath).into(myImageView)
Upvotes: 0
Reputation: 1919
I created a Kotlin extension function that simplifies the operation for Kotlin developers based on @Jason Robinson's answer. I hope it helps.
fun Bitmap.fixRotation(uri: Uri): Bitmap? {
val ei = ExifInterface(uri.path)
val orientation: Int = ei.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
return when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage( 90f)
ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage( 180f)
ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage( 270f)
ExifInterface.ORIENTATION_NORMAL -> this
else -> this
}
}
fun Bitmap.rotateImage(angle: Float): Bitmap? {
val matrix = Matrix()
matrix.postRotate(angle)
return Bitmap.createBitmap(
this, 0, 0, width, height,
matrix, true
)
}
Upvotes: 5
Reputation: 265
Got an answer for this problem without using ExifInterface. We can get the rotation of the camera either front camera or back camera whichever you are using then while creating the Bitmap we can rotate the bitmap using Matrix.postRotate(degree)
public int getRotationDegree() {
int degree = 0;
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
degree = info.orientation;
return degree;
}
}
return degree;
}
After calculating the rotation you can rotate you bitmap like below:
Matrix matrix = new Matrix();
matrix.postRotate(getRotationDegree());
Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
Herare bm should be your bitmap.
If you want to know the rotation of your front camera just change Camera.CameraInfo.CAMERA_FACING_BACK to Camera.CameraInfo.CAMERA_FACING_FRONT above.
I hope this helps.
Upvotes: 0
Reputation: 24988
Below code worked with me, it got the bitmap from the fileUri, and do the rotation fixing if required:
private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
val bitmap = when {
Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
this.contentResolver,
selectedPhotoUri
)
else -> {
val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
ImageDecoder.decodeBitmap(source)
}
}
// If the image is rotated, fix it
return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
ExifInterface.ORIENTATION_ROTATE_90 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(90F) }, true)
ExifInterface.ORIENTATION_ROTATE_180 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(180F) }, true)
ExifInterface.ORIENTATION_ROTATE_270 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(270F) }, true)
else -> bitmap
}
}
Upvotes: 1
Reputation: 475
If you are using Fresco, you can use this -
final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotate())
.build();
mSimpleDraweeView.setController(
Fresco.newDraweeControllerBuilder()
.setImageRequest(imageRequest)
.build());
This automatically rotates the images based on Exif data.
Source: https://frescolib.org/docs/rotation.html
Upvotes: 0
Reputation: 11601
Here is Xamarin.Android
version:
From @Jason Robinson's answer:
Bitmap rotate(Bitmap bitmap, int angle)
{
var matrix = new Matrix();
matrix.PostRotate(angle);
return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
}
Bitmap rotateIfRequired(Bitmap bitmap, string imagePath)
{
var ei = new ExifInterface(imagePath);
var orientation = ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Undefined);
switch (orientation)
{
case (int)Android.Media.Orientation.Rotate90: return rotate(bitmap, 90);
case (int)Android.Media.Orientation.Rotate180: return rotate(bitmap, 180);
case (int)Android.Media.Orientation.Rotate270: return rotate(bitmap, 270);
default: return bitmap;
}
}
Then calculateInSampleSize
method:
int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH)
{
float h = options.OutHeight;
float w = options.OutWidth;
var inSampleSize = 1;
if (h > reqH || w > reqW)
{
if (reqH == 0) inSampleSize = (int)Math.Floor(w / reqW);
else if (reqW == 0) inSampleSize = (int)Math.Floor(h / reqH);
else
{
var hRatio = (int)Math.Floor(h / reqH);
var wRatio = (int)Math.Floor(w / reqW);
inSampleSize = false ? Math.Max(hRatio, wRatio) : Math.Min(hRatio, wRatio);
}
}
return inSampleSize;
}
From @Sami Eltamawy's answer:
Bitmap handleSamplingAndRotationBitmap(string imagePath)
{
var maxHeight = 1024;
var maxWidth = 1024;
var options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeFile(imagePath, options);
options.InSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.InJustDecodeBounds = false;
var bitmap = BitmapFactory.DecodeFile(imagePath, options);
bitmap = rotateIfRequired(bitmap, imagePath);
return bitmap;
}
Upvotes: 0
Reputation: 1293
Jason Robinson's answer and Sami Eltamawy answer are excelent.
Just an improvement to complete the aproach, you should use compat ExifInterface.
com.android.support:exifinterface:${lastLibVersion}
You will be able to instantiate the ExifInterface(pior API <24) with InputStream
(from ContentResolver
) instead of uri paths avoiding "File not found exceptions"
https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html
Upvotes: 7
Reputation: 31284
Most phone cameras are landscape, meaning if you take the photo in portrait, the resulting photos will be rotated 90 degrees. In this case, the camera software should populate the Exif data with the orientation that the photo should be viewed in.
Note that the below solution depends on the camera software/device manufacturer populating the Exif data, so it will work in most cases, but it is not a 100% reliable solution.
ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
Bitmap rotatedBitmap = null;
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotatedBitmap = rotateImage(bitmap, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotatedBitmap = rotateImage(bitmap, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotatedBitmap = rotateImage(bitmap, 270);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
rotatedBitmap = bitmap;
}
Here is the rotateImage
method:
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);
}
Upvotes: 545
Reputation: 1594
This maybe goes without saying but always remember that you can handle some of these image handling issues on your server. I used responses like the ones contained in this thread to handle the immediate display of the image. However my application requires images to be stored on the server (this is probably a common requirement if you want the image to persist as users switch phones).
The solutions contained in many of the threads concerning this topic don't discuss the lack of persistence of the EXIF data which doesn't survive the Bitmap's image compression, meaning you'll need to rotate the image each time your server loads it. Alternatively, you can send the EXIF orientation data to your server, and then rotate the image there if needed.
It was easier for me to create a permanent solution on a server because I didn't have to worry about Android's clandestine file paths.
Upvotes: 1
Reputation: 310
You can just read the orientation of the camera sensor like indicated by Google in the documentation: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html
SENSOR_ORIENTATION
Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.
Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.
Units: Degrees of clockwise rotation; always a multiple of 90
Range of valid values:
0, 90, 180, 270
This key is available on all devices.
Sample code:
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
String cameraId = manager.getCameraIdList()[0];
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}
Upvotes: 12
Reputation: 12045
If somebody experiences problems with ExifInterface
on Android 4.4 (KitKat) for getting the orientation, it might be because of wrong path got from the URI. See a solution for propoer getPath
in Stack Overflow question Get real path from URI, Android KitKat new storage access framework
Upvotes: 2
Reputation: 2703
It's easy to detect the image orientation and replace the bitmap using:
/**
* Rotate an image if required.
* @param img
* @param selectedImage
* @return
*/
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {
// Detect rotation
int rotation = getRotation(context, selectedImage);
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
else{
return img;
}
}
/**
* Get the rotation of the last image added.
* @param context
* @param selectedImage
* @return
*/
private static int getRotation(Context context,Uri selectedImage) {
int rotation = 0;
ContentResolver content = context.getContentResolver();
Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] { "orientation", "date_added" },
null, null, "date_added desc");
if (mediaCursor != null && mediaCursor.getCount() != 0) {
while(mediaCursor.moveToNext()){
rotation = mediaCursor.getInt(0);
break;
}
}
mediaCursor.close();
return rotation;
}
To avoid Out of memories with big images, I'd recommend you to rescale the image using:
private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
throws IOException {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close();
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
imageStream = context.getContentResolver().openInputStream(selectedImage);
Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);
img = rotateImageIfRequired(img, selectedImage);
return img;
}
It's not posible to use ExifInterface to get the orientation because an Android OS issue: https://code.google.com/p/android/issues/detail?id=19268
And here is calculateInSampleSize
/**
* Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
* the closest inSampleSize that will result in the final decoded bitmap having a width and
* height equal to or larger than the requested width and height. This implementation does not
* ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
* results in a larger bitmap which isn't as useful for caching purposes.
*
* @param options An options object with out* params already populated (run through a decode*
* method with inJustDecodeBounds==true
* @param reqWidth The requested width of the resulting bitmap
* @param reqHeight The requested height of the resulting bitmap
* @return The value to be used for inSampleSize
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee a final image
// with both dimensions larger than or equal to the requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down further
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
Upvotes: 50
Reputation: 446
Better try to take the picture in a specific orientation.
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"
For best results give landscape orientation in the cameraview activity.
Upvotes: 1
Reputation: 147
The selected answer uses the most common method answered to this and similar questions. However, it does not work with both front and back cameras on Samsung. For those looking for a solution which works across both front and back cameras for Samsung and other major manufacturers, this answer by nvhausid is awesome:
https://stackoverflow.com/a/18915443/6080472
For those who don't want to click through, the relevant magic is to use the CameraInfo rather then relying on EXIF.
Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);
Full code in the link.
Upvotes: 3
Reputation: 2005
Sadly, @jason-robinson answer above didn't work for me.
Although the rotate function works perfectly:
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);
}
I had to do the following to get the orientation as the Exif orientation was always 0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode,resultCode,data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
Uri selectedImage = data.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]));
}
InputStream imageStream = getContentResolver().openInputStream(selectedImage);
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
switch(orientation) {
case 90:
bitmap = rotateImage(chosen_image_bitmap, 90);
break;
case 180:
bitmap = rotateImage(chosen_image_bitmap, 180);
break;
case 270:
bitmap = rotateImage(chosen_image_bitmap, 270);
break;
default:
break;
}
imageView.setImageBitmap(bitmap );
Upvotes: 4
Reputation: 1842
One line solution:
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Or
Picasso.with(context).load("file:" + photoPath).into(imageView);
This will autodetect rotation and place image in correct orientation
Picasso is a very powerful library for handling images in your app includes: Complex image transformations with minimal memory use.
Upvotes: 24
Reputation: 24848
// Try this way,hope this will help you to solve your problem...
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:id="@+id/imgFromCameraOrGallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/ic_launcher"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnCamera"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Camera"/>
<Button
android:id="@+id/btnGallery"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:layout_height="wrap_content"
android:text="Gallery"/>
</LinearLayout>
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity {
private ImageView imgFromCameraOrGallery;
private Button btnCamera;
private Button btnGallery;
private String imgPath;
final private int PICK_IMAGE = 1;
final private int CAPTURE_IMAGE = 2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgFromCameraOrGallery = (ImageView) findViewById(R.id.imgFromCameraOrGallery);
btnCamera = (Button) findViewById(R.id.btnCamera);
btnGallery = (Button) findViewById(R.id.btnGallery);
btnCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri());
startActivityForResult(intent, CAPTURE_IMAGE);
}
});
btnGallery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, ""), PICK_IMAGE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CAPTURE_IMAGE) {
setCapturedImage(getImagePath());
} else if (requestCode == PICK_IMAGE) {
imgFromCameraOrGallery.setImageBitmap(BitmapFactory.decodeFile(getAbsolutePath(data.getData())));
}
}
}
private String getRightAngleImage(String photoPath) {
try {
ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int degree = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
degree = 0;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
case ExifInterface.ORIENTATION_UNDEFINED:
degree = 0;
break;
default:
degree = 90;
}
return rotateImage(degree,photoPath);
} catch (Exception e) {
e.printStackTrace();
}
return photoPath;
}
private String rotateImage(int degree, String imagePath){
if(degree<=0){
return imagePath;
}
try{
Bitmap b= BitmapFactory.decodeFile(imagePath);
Matrix matrix = new Matrix();
if(b.getWidth()>b.getHeight()){
matrix.setRotate(degree);
b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
matrix, true);
}
FileOutputStream fOut = new FileOutputStream(imagePath);
String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
String imageType = imageName.substring(imageName.lastIndexOf(".") + 1);
FileOutputStream out = new FileOutputStream(imagePath);
if (imageType.equalsIgnoreCase("png")) {
b.compress(Bitmap.CompressFormat.PNG, 100, out);
}else if (imageType.equalsIgnoreCase("jpeg")|| imageType.equalsIgnoreCase("jpg")) {
b.compress(Bitmap.CompressFormat.JPEG, 100, out);
}
fOut.flush();
fOut.close();
b.recycle();
}catch (Exception e){
e.printStackTrace();
}
return imagePath;
}
private void setCapturedImage(final String imagePath){
new AsyncTask<Void,Void,String>(){
@Override
protected String doInBackground(Void... params) {
try {
return getRightAngleImage(imagePath);
}catch (Throwable e){
e.printStackTrace();
}
return imagePath;
}
@Override
protected void onPostExecute(String imagePath) {
super.onPostExecute(imagePath);
imgFromCameraOrGallery.setImageBitmap(decodeFile(imagePath));
}
}.execute();
}
public Bitmap decodeFile(String path) {
try {
// Decode deal_image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 1024;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(path, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public String getAbsolutePath(Uri uri) {
if(Build.VERSION.SDK_INT >= 19){
String id = "";
if(uri.getLastPathSegment().split(":").length > 1)
id = uri.getLastPathSegment().split(":")[1];
else if(uri.getLastPathSegment().split(":").length > 0)
id = uri.getLastPathSegment().split(":")[0];
if(id.length() > 0){
final String[] imageColumns = {MediaStore.Images.Media.DATA };
final String imageOrderBy = null;
Uri tempUri = getUri();
Cursor imageCursor = getContentResolver().query(tempUri, imageColumns, MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
if (imageCursor.moveToFirst()) {
return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
}else{
return null;
}
}else{
return null;
}
}else{
String[] projection = { MediaStore.MediaColumns.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} else
return null;
}
}
private Uri getUri() {
String state = Environment.getExternalStorageState();
if(!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
return MediaStore.Images.Media.INTERNAL_CONTENT_URI;
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
public Uri setImageUri() {
Uri imgUri;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/",getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis() + ".png");
imgUri = Uri.fromFile(file);
imgPath = file.getAbsolutePath();
}else {
File file = new File(getFilesDir() ,getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis()+ ".png");
imgUri = Uri.fromFile(file);
this.imgPath = file.getAbsolutePath();
}
return imgUri;
}
public String getImagePath() {
return imgPath;
}
}
Upvotes: 12
Reputation: 4673
Normally it is recommended to solve the problem with the ExifInterface, like @Jason Robinson did suggest. If this approach doesn't work, you could try to look up the Orientation of the latest image taken...
private int getImageOrientation(){
final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageColumns, null, null, imageOrderBy);
if(cursor.moveToFirst()){
int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
cursor.close();
return orientation;
} else {
return 0;
}
}
Upvotes: 4