147 votes

Android : Les bitmaps chargés depuis une galerie sont pivotés dans ImageView

Lorsque je charge une image de la galerie multimédia dans un bitmap, tout fonctionne bien, sauf que les photos qui ont été prises avec l'appareil photo en tenant le téléphone verticalement, sont tournées de sorte que j'obtienne toujours une image horizontale même si elle apparaît verticalement dans la galerie. Pourquoi et comment puis-je les charger correctement ?

0 votes

187voto

Manuel Points 2525

Donc, à titre d'exemple...

Tout d'abord, vous devez créer une ExifInterface :

ExifInterface exif = new ExifInterface(filename);

Vous pouvez alors saisir l'orientation de l'image :

orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

Voici ce que signifient les valeurs d'orientation : http://sylvana.net/jpegcrop/exif_orientation.html

Ainsi, les valeurs les plus importantes sont 3, 6 et 8. Si l'orientation est ExifInterface.ORIENTATION_ROTATE_90 (qui est 6), par exemple, vous pouvez faire pivoter l'image comme ceci :

Matrix matrix = new Matrix();
matrix.postRotate(90);
rotatedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), matrix, true);

Mais ce n'est qu'un exemple rapide. Je suis sûr qu'il existe d'autres façons d'effectuer la rotation réelle. Mais vous les trouverez également sur StackOverflow.

5 votes

Voici toutes les valeurs de rotation pour les différentes orientations : 3 : 180, 6 : 90, 8 : 270

107 votes

N'utilisez pas de nombres magiques lorsque vous pouvez utiliser des constantes nommées : ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.

30 votes

Attention aux OutOfMemoryError lorsque vous utilisez cette approche, car vous conservez deux bitmaps en mémoire en même temps.

68voto

Timmmm Points 9909

Il s'agit d'une solution complète (trouvée dans l'exemple Hackbook du SDK de Facebook). Elle a l'avantage de ne pas nécessiter l'accès au fichier lui-même. C'est extrêmement utile si vous chargez une image à partir du résolveur de contenu (par exemple, si votre application répond à une intention de partage de photo).

public static int getOrientation(Context context, Uri photoUri) {
    /* it's on the external media. */
    Cursor cursor = context.getContentResolver().query(photoUri,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);

    if (cursor.getCount() != 1) {
        return -1;
    }

    cursor.moveToFirst();
    return cursor.getInt(0);
}

Et ensuite, vous pouvez obtenir un Bitmap pivoté comme suit. Ce code réduit également la taille de l'image (malheureusement mal) à MAX_IMAGE_DIMENSION. Sinon, vous risquez de manquer de mémoire.

public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
    InputStream is = context.getContentResolver().openInputStream(photoUri);
    BitmapFactory.Options dbo = new BitmapFactory.Options();
    dbo.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, dbo);
    is.close();

    int rotatedWidth, rotatedHeight;
    int orientation = getOrientation(context, photoUri);

    if (orientation == 90 || orientation == 270) {
        rotatedWidth = dbo.outHeight;
        rotatedHeight = dbo.outWidth;
    } else {
        rotatedWidth = dbo.outWidth;
        rotatedHeight = dbo.outHeight;
    }

    Bitmap srcBitmap;
    is = context.getContentResolver().openInputStream(photoUri);
    if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
        float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
        float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
        float maxRatio = Math.max(widthRatio, heightRatio);

        // Create the bitmap from file
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = (int) maxRatio;
        srcBitmap = BitmapFactory.decodeStream(is, null, options);
    } else {
        srcBitmap = BitmapFactory.decodeStream(is);
    }
    is.close();

    /*
     * if the orientation is not 0 (or -1, which means we don't know), we
     * have to do a rotation.
     */
    if (orientation > 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(orientation);

        srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
                srcBitmap.getHeight(), matrix, true);
    }

    return srcBitmap;
}

1 votes

Que signifie ce MAX_IMAGE_DIMENDION ?

3 votes

C'est la largeur ou la hauteur maximale de l'image que vous obtenez. Par exemple, si vous n'avez besoin que d'une image de 512x512, si vous ouvrez une image de 24 mégapixels, il est beaucoup plus efficace de l'ouvrir déjà sous-échantillonnée que d'ouvrir la totalité de l'image et de la réduire ensuite - ce qui épuiserait probablement toute votre mémoire de toute façon.

0 votes

Dans mes programmes, j'ai trouvé utile de définir la variable Bitmap dans l'activité/fragment comme private static et de lui attribuer la valeur null dans les fonctions. J'avais alors moins de problèmes de mémoire.

66voto

Teo Inke Points 176

Je l'ai résolu dans mon cas avec ce code en utilisant l'aide de ce post :

            Bitmap myBitmap = getBitmap(imgFile.getAbsolutePath());

            try {
                ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
                Log.d("EXIF", "Exif: " + orientation);
                Matrix matrix = new Matrix();
                if (orientation == 6) {
                    matrix.postRotate(90);
                }
                else if (orientation == 3) {
                    matrix.postRotate(180);
                }
                else if (orientation == 8) {
                    matrix.postRotate(270);
                }
                myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true); // rotating bitmap
            }
            catch (Exception e) {

            }
            ImageView img = (ImageView) findViewById(R.id.imgTakingPic);
            img.setImageBitmap(myBitmap);

J'espère que cela fera gagner du temps à quelqu'un !

0 votes

Edit suggestions : il n'y a pas de constantes correctement nommées pour les orientations 6, 3, 8 ? ne pourrions-nous pas sauter la nouvelle bitmap si aucune rotation n'est requise ?

0 votes

Comme @d60402 l'a dit précédemment dans un commentaire, vous pouvez utiliser des constantes nommées : ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.

46voto

Josh Pinter Points 3814

Utilisez un utilitaire pour faire le gros du travail.

9re a créé un utilitaire simple pour traiter les données EXIF et faire pivoter les images dans leur orientation correcte.

Vous pouvez trouver le code de l'utilitaire ici : https://gist.github.com/9re/1990019

Il suffit de le télécharger, de l'ajouter au dossier de votre projet. src et utiliser ExifUtil.rotateBitmap() pour obtenir l'orientation correcte, comme ceci :

String imagePath = photoFile.getAbsolutePath();             // photoFile is a File class.
Bitmap myBitmap  = BitmapFactory.decodeFile(imagePath);

Bitmap orientedBitmap = ExifUtil.rotateBitmap(imagePath, myBitmap);

2 votes

Cela fonctionne pour moi ! J'ai juste redimensionné le bitmap au format HD avant de le passer à ExifUtil.rotateBitmap() pour éviter OutOfMemoryError comme ça : Bitmap resized = Bitmap.createScaledBitmap(myBitmap, 720, 1280, true) ; photo = ExifUtil.rotateBitmap(picturePath, resized) ;

0 votes

@Phil Bel ajout. Je n'ai pas rencontré ce problème (j'utilise des appareils Android plus anciens et plus merdiques) mais c'est vraiment bon à savoir.

4 votes

Tu es un héros mon ami :)

40voto

James Points 5526

Avez-vous regardé les données EXIF des images ? Elles peuvent connaître l'orientation de l'appareil photo au moment où la photo a été prise.

3 votes

Vous avez raison, c'était bien sûr la solution. Je vais poster mon code comme exemple dans une réponse séparée, plus tard, mais je marque celle-ci comme acceptée car elle m'a mis sur la bonne voie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X