Orientación de la imagen – Android

He estado luchando con este error de manera intermitente durante el último mes más o menos. Cada vez que creo que lo arreglé, parece regresar de alguna forma.

Es el antiguo error “Imagen girada 90 grados” de Android. He leído innumerables publicaciones aquí (StackOverFlow) y he probado numerosos métodos, pero parece que no puedo solucionarlo.

Todavía estoy recibiendo imágenes que se rotan incorrectamente.

En mi aplicación, un usuario elige su perfil de imagen, que luego se establece en un ImageView. La imagen se elige de la Galería de teléfonos

Hace dos días implementé el Código siguiente, esto funcionó para todas las imágenes con las que lo probé en mi teléfono. Sin embargo, cuando uno de mis probadores Beta lo intentó, sus imágenes volvieron a girar. Me envió las imágenes para probarlas, pero se mostraban bien en mi teléfono. De ahí que me siento cada vez más frustrado.

Este es el método que estoy usando para obtener la orientación de Imágenes:

// 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; } 

Luego obtengo un bitmap rotado usando este método:

 // 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; } 

Simplemente no estoy seguro de qué hacer.

Realmente me gustaría si alguien pudiera ayudarme a resolver esto

Gracias de antemano


ACTUALIZAR

Acabo de ver la siguiente línea de código en mi registro después de implementar los métodos sugeridos:

 JHEAD can't open 'file:/external/images/media/3885' 

No estoy seguro de lo que esto significa


ACTUALIZACIÓN # 2

Creo que puedo haber solucionado el problema, obtuve la ruta de imagen correcta para el archivo.

Debe tener en cuenta todas las orientaciones, no solo 90 o 180. Estoy usando esto

  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); } 

y:

  /** * 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; } 

¡Este problema realmente apesta! Noté que esto es un problema al elegir fotos en lugar de tomarlas. Encontré la respuesta enterrada en el código de esta lib de recorte que parecía mostrar siempre las cosas correctamente https://github.com/jdamcd/android-crop mientras recortaba (a pesar de que a veces devuelve cosas con una orientación errónea después). De todos modos, primero empiece seleccionando la foto de la manera que elijo en esta respuesta: Escoja la imagen del fragmento siempre devuelva el código de resultado 0 en algunos dispositivos

Luego haz esto donde necesites:

 private void setRotationVariables(Uri uri) { m_rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil .getFromMediaUri( this, getContentResolver(), uri)); } 

Aquí está la clase:

 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; } 

}

La respuesta mochilogic es muy buena, pero su comentario también es correcto: lo siento, “haz esto donde lo necesites” es tan vago … ”

No puedo agregar todo esto en el comentario a la respuesta mochilogic, así que lo escribiré aquí: si no quieres utilizarlo en setRotationVariables (data.getData), esta es otra forma de usar la clase ImageOrientationUtil de su respuesta y este método:

  private void setRotationVariables(Uri uri) { m_rotationInDegrees = ImageOrientationUtil.getExifRotation (ImageOrientationUtil.getFromMediaUri( this, getContentResolver(), uri)); } 

Puedes enviar a Uri desde la galería a este método para que devuelva la rotación correcta en grados por memebr como lo hace o por valor como lo hice yo:

  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; } 

y luego en la función de llamada después de escalar su Uri a bitmap, puede crear un bitmap usando esta rotationInDegrees con una matriz.

Puedes verlo en mi código aquí en este método. Tomo Uri, lo escalo y lo rotalo y luego lo devuelve como bitmap.

pero primero, básicamente, esto es lo que necesitas:

  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); } 

Este es el código de método completo si alguien lo necesita …

  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; }