java 图像方向 - Android
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29971319/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Image Orientation - Android
提问by Richard
I have been struggling with this bug on and off for the last month or so. Everytime that I think I have fixed it it seems to come back in some form.
在过去的一个月左右的时间里,我一直在努力解决这个错误。每次我认为我已经修复它时,它似乎都会以某种形式回来。
It is the old Android "Image Rotated 90 degrees" bug. I have read countless Posts on here (StackOverFlow) aswell as tried numerous methods but just cannot seem to fix it.
这是旧的 Android“图像旋转 90 度”错误。我在这里(StackOverFlow)上阅读了无数帖子,也尝试了很多方法,但似乎无法修复它。
I am still getting images that are rotated incorrectly.
我仍然收到旋转不正确的图像。
In my application a user chooses his/her profile Picture, which is then set to an ImageView. The image is chosen from the Phones Gallery
在我的应用程序中,用户选择他/她的个人资料图片,然后将其设置为 ImageView。该图像是从电话库中选择的
Two days ago I implemented the Following Code, this worked for all the images I tested it with on my Phone. However when one of my Beta testers tried it, his images were once again rotated. He sent me the images for testing but they were displaying fine on my Phone. Hence why I am getting more and more frustrated.
两天前,我实现了以下代码,这适用于我在手机上测试的所有图像。然而,当我的一位 Beta 测试人员尝试它时,他的图像再次旋转。他给我发送了图像进行测试,但它们在我的手机上显示良好。因此,为什么我越来越沮丧。
This is the method I am using to get the Images orientation:
这是我用来获取图像方向的方法:
// Gets an Images Orientation
public static int getOrientationEXIF(Context context, Uri uri) {
int orientation = 0;
try {
ExifInterface exif = new ExifInterface(uri.getPath());
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
orientation = 90;
return orientation;
case ExifInterface.ORIENTATION_ROTATE_180:
orientation = 180;
return orientation;
}
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
I then get a rotated Bitmap using this Method:
然后我使用这个方法得到一个旋转的位图:
// Rotate a Bitmap
public static Bitmap rotate(float rotationValue, String filePath) {
Bitmap original= BitmapFactory.decodeFile(filePath);
int width = original.getWidth();
int height = original.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate(rotationValue);
Bitmap rotated = Bitmap.createBitmap(original, 0, 0, width, height, matrix, true);
return rotated;
}
I am just not sure what to do anymore.
我只是不知道该怎么办了。
I would really like it if someone could help me figure this out
如果有人能帮我解决这个问题,我真的很喜欢
Thank you in advance
先感谢您
UPDATE
更新
I just saw the following line of Code in my Log after implementing the suggested Methods:
实施建议的方法后,我刚刚在我的日志中看到了以下代码行:
JHEAD can't open 'file:/external/images/media/3885'
I am not sure what this means
我不确定这意味着什么
UPDATE #2
更新 #2
I think I may have fixed the problem, I got the proper image path for the file.
我想我可能已经解决了这个问题,我得到了文件的正确图像路径。
采纳答案by kandroidj
You need to account for all orientations not just 90 or 180. I am using this
您需要考虑所有方向,而不仅仅是 90 或 180。我正在使用这个
File curFile = new File("path-to-file"); // ... This is an image file from my device.
Bitmap rotatedBitmap;
try {
ExifInterface exif = new ExifInterface(curFile.getPath());
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix matrix = new Matrix();
if (rotation != 0f) {matrix.preRotate(rotationInDegrees);}
rotatedBitmap = Bitmap.createBitmap(bitmap,0,0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}catch(IOException ex){
Log.e(TAG, "Failed to get Exif data", ex);
}
and:
和:
/**
* Gets the Amount of Degress of rotation using the exif integer to determine how much
* we should rotate the image.
* @param exifOrientation - the Exif data for Image Orientation
* @return - how much to rotate in degress
*/
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;
}
回答by A.Sanchez.SD
This problem really sucks ! I notice this is an issue when choosing photos rather than taking them. I found the answer buried in the code for this cropping lib which seemed to always display things correctly https://github.com/jdamcd/android-cropwhile cropping (despite it sometimes returning things with wrong orientation afterwards). Anyways, first start off by choosing the photo the way I choose in this answer: Pick image from fragment always return resultcode 0 in some devices
这个问题真的很烂!我注意到这是选择照片而不是拍照时的问题。我在这个裁剪库的代码中找到了答案,它似乎总是在裁剪时正确显示内容 https://github.com/jdamcd/android-crop(尽管它有时会返回错误方向的内容)。无论如何,首先按照我在此答案中选择的方式选择照片: 从片段中选择图像总是在某些设备中返回结果代码 0
Next do this where you need to:
接下来在您需要的地方执行此操作:
private void setRotationVariables(Uri uri)
{
m_rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
.getFromMediaUri(
this,
getContentResolver(),
uri));
}
Here is the class:
这是课程:
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.io.Closeable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ImageOrientationUtil
{
private static final String SCHEME_FILE = "file";
private static final String SCHEME_CONTENT = "content";
public static void closeSilently(@Nullable Closeable c) {
if (c == null) return;
try {
c.close();
} catch (Throwable t) {
// Do nothing
}
}
public static int getExifRotation(File imageFile) {
if (imageFile == null) return 0;
try {
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
// We only recognize a subset of orientation tag values
switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return ExifInterface.ORIENTATION_UNDEFINED;
}
} catch (IOException e) {
// Log.e("Error getting Exif data", e);
return 0;
}
}
@Nullable
public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) {
if (uri == null) return null;
if (SCHEME_FILE.equals(uri.getScheme())) {
return new File(uri.getPath());
} else if (SCHEME_CONTENT.equals(uri.getScheme())) {
final String[] filePathColumn = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME };
Cursor cursor = null;
try {
cursor = resolver.query(uri, filePathColumn, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d")) ?
cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) :
cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
// Picasa images on API 13+
if (columnIndex != -1) {
String filePath = cursor.getString(columnIndex);
if (!TextUtils.isEmpty(filePath)) {
return new File(filePath);
}
}
}
} catch (IllegalArgumentException e) {
// Google Drive images
return getFromMediaUriPfd(context, resolver, uri);
} catch (SecurityException ignored) {
// Nothing we can do
} finally {
if (cursor != null) cursor.close();
}
}
return null;
}
private static String getTempFilename(Context context) throws IOException {
File outputDir = context.getCacheDir();
File outputFile = File.createTempFile("image", "tmp", outputDir);
return outputFile.getAbsolutePath();
}
@Nullable
private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) {
if (uri == null) return null;
FileInputStream input = null;
FileOutputStream output = null;
try {
ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
FileDescriptor fd = pfd.getFileDescriptor();
input = new FileInputStream(fd);
String tempFilename = getTempFilename(context);
output = new FileOutputStream(tempFilename);
int read;
byte[] bytes = new byte[4096];
while ((read = input.read(bytes)) != -1) {
output.write(bytes, 0, read);
}
return new File(tempFilename);
} catch (IOException ignored) {
// Nothing we can do
} finally {
closeSilently(input);
closeSilently(output);
}
return null;
}
}
}
回答by Udi Reshef
mochilogic answer is very good but his comment is also correct: sorry, " do this where you need to " is so vague.."
mochilogic 的答案非常好,但他的评论也是正确的:抱歉,“在需要的地方执行此操作”太含糊了..
I cannot add all of this in comment to mochilogic answer so I will write this here: If you dont like touse it in setRotationVariables(data.getData) - this is another way to use the class ImageOrientationUtil from his answer and this method:
我无法在 mochilogic 答案的评论中添加所有这些,所以我会在这里写:如果你不喜欢在 setRotationVariables(data.getData) 中使用它 - 这是从他的答案和这个方法中使用 ImageOrientationUtil 类的另一种方法:
private void setRotationVariables(Uri uri)
{
m_rotationInDegrees = ImageOrientationUtil.getExifRotation
(ImageOrientationUtil.getFromMediaUri(
this,
getContentResolver(),
uri));
}
You can send Uri from the galery to this method make it return the correct rotation in degrees by memebr as he does or by value as I did:
您可以将 galery 中的 Uri 发送到此方法,使其像他一样通过 memebr 或像我一样通过值返回正确的旋转度数:
private static int setRotationVariables(Uri uri) {
int rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
.getFileFromMediaUri(
appCtx,
appCtx.getContentResolver(),
uri));
Log.d(TAG, "setRotationVariables:" + "according to original Image Uri Exif details we need to rotate in "+rotationInDegrees + " Degrees");
return rotationInDegrees;
}
and then on the calling function after you scale your Uri to bitmap you can create bitmap using this rotationInDegrees with a matrix.
然后在将 Uri 缩放为位图后的调用函数上,您可以使用带有矩阵的 rotationInDegrees 创建位图。
you can see it in my code here in this method I take Uri and scale it and rotate it and then returns it as bitmap.
你可以在我的代码中看到它在这个方法中我使用 Uri 并对其进行缩放和旋转,然后将其作为位图返回。
but first - basicaly this is what you need:
但首先 - 基本上这就是你需要的:
int rotationDegree = setRotationVariables(uri);
if (rotationDegree > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
This is the full method code if anyones need it..
如果有人需要,这是完整的方法代码。
public static Bitmap getScaledBitmapFromUri(Uri uri) throws FileNotFoundException, IOException {
final int TRY_SCALE_TO_THIS_SIZE = 1024;
Log.d(TAG, "getScaledBitmapFromUri:: calling setRotationVariables() to figure rotationDegree");
int rotationDegree = setRotationVariables(uri);
Context ctx = MessagingApp.getContext();
InputStream input = ctx.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
if ( (onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1) )
return null;
int BiggestOriginalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
//we will add 1 to Math.round (BiggestOriginalSize / (double)TRY_SCALE_TO_THIS_SIZE) in order to harden the scaling(we need smaller images for this project!)
double ratio = (BiggestOriginalSize > TRY_SCALE_TO_THIS_SIZE) ? (1 + Math.round(BiggestOriginalSize / (double) TRY_SCALE_TO_THIS_SIZE)) : 1.0;
Log.w(TAG, "getScaledBitmapFromUri:: originalSize: " + BiggestOriginalSize + "PX, TRY_SCALE_TO_THIS_SIZE (if original is bigger):" + TRY_SCALE_TO_THIS_SIZE +"PX");
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); //this one will give abstract results (sometimes bigger then TRY_SCALE_TO_THIS_SIZE)
Log.w(TAG, format("bitmapOptions.inSampleSize: " + bitmapOptions.inSampleSize));
bitmapOptions.inJustDecodeBounds = false; //check this out!!! maybe delete?
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
//bitmapOptions.rogetrotationInDegrees
input = ctx.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
//bitmap = findExactInSampleSize(onlyBoundsOptions, TRY_SCALE_TO_THIS_SIZE, bitmap); // this one will never give bigger length then TRY_SCALE_TO_THIS_SIZE
if (rotationDegree > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
Log.w(TAG, "after decodeStream : bitmap.getWidth(): " + bitmap.getWidth() + "PX, bitmap.getHeight(): " + bitmap.getHeight() +"PX.");
input.close();
return bitmap;
}