Android: les bitmaps chargés à partir de la galerie pivotent dans ImageView

138

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

Manuel
la source

Réponses:

40

Avez-vous regardé les données EXIF ​​des images? Il peut connaître l'orientation de l'appareil photo lorsque la photo a été prise.

James
la source
2
Vous avez raison, c'était bien sûr la solution. Je vais publier 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.
Manuel
182

Donc, à titre d'exemple ...

Vous devez d'abord créer une ExifInterface:

ExifInterface exif = new ExifInterface(filename);

Vous pouvez ensuite 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);

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

Manuel
la source
5
Voici toutes les valeurs de rotation pour les différentes orientations: 3: 180, 6: 90, 8: 270
Nom d'affichage
103
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.
d60402
29
Faites attention OutOfMemoryErrorlorsque vous utilisez cette approche car vous tenez deux bitmaps en mémoire en même temps.
Alex Bonel du
Un autre exemple complet ... stackoverflow.com/questions/14066038/…
CGR
64

Il s'agit d'une solution complète (trouvée dans l'exemple Hackbook du SDK Facebook). Il a l'avantage de ne pas avoir besoin d'accéder au fichier lui-même. Ceci 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 puis vous pouvez obtenir un Bitmap pivoté comme suit. Ce code réduit également l'image (malheureusement malheureusement) à 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;
}
Timmmm
la source
1
que signifie MAX_IMAGE_DIMENDION?
Sazzad Hissain Khan
3
C'est la largeur ou la hauteur maximale de l'image que vous obtenez. Je dis que 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 le tout et de le réduire - cela épuiserait probablement toute votre mémoire de toute façon.
Timmmm
Dans mes programmes, j'ai trouvé utile de définir la variable Bitmap dans l'activité / le fragment comme statique privée et de la définir sur null dans les fonctions. A eu moins de problèmes de mémoire alors.
Gunnar Bernstein
Il est plus intelligent de remplacer MAX_IMAGE_DIMENDION par MAX_IMAGE_WIDTH et MAX_IMAGE_HEIGHT
fnc12
J'ai économisé beaucoup de temps :) Merci beaucoup. Pour ceux obtenir le curseur null, vous pouvez essayer ExifInterface exif = new ExifInterface(photoUri.getPath());et exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1)d'obtenir l' orientation (par exemple ORIENTATION_ROTATE_90, ORIENTATION_ROTATE_180)
Atul
60

Résolu dans mon cas avec ce code à l'aide de cet article:

            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!

Teo Inke
la source
Modifier les suggestions: n'y a-t-il pas de constantes nommées appropriées pour les orientations 6, 3, 8? ne pourrions-nous pas ignorer le nouveau bitmap si aucune rotation n'est requise?
Cee McSharpface
Comme @ d60402 l'a déjà dit 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.
Adrian
42

Utilisez un utilitaire pour faire le levage lourd.

9re a créé un utilitaire simple pour gérer les tâches lourdes de traitement des données EXIF ​​et de rotation des images dans leur bonne orientation.

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

Téléchargez simplement ceci, ajoutez-le au srcrépertoire de votre projet et utilisez ExifUtil.rotateBitmap()pour obtenir la bonne orientation, comme ceci:

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

Bitmap orientedBitmap = ExifUtil.rotateBitmap(imagePath, myBitmap);
Joshua Pinter
la source
2
Travaille pour moi ! Je viens de redimensionner 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 (chemin de l'image, redimensionné);
Phil
@Phil Bel ajout. Je n'ai pas rencontré cela (j'utilise des appareils Android plus anciens et plus merdiques) mais c'est vraiment bon à savoir.
Joshua Pinter
3
tu es un héros mon ami :)
klutch
@klutch Vous venez de faire ma journée. :) Pour être honnête, 9re a écrit le code utilitaire, donc c'est le vrai héros.
Joshua Pinter
1
@SreekanthKarumanaghat Excellente question! Je savais probablement pourquoi cela avait du sens quand j'étais profondément dans ce domaine, mais pour le moment, cela me semble également redondant. J'ai peut-être passé trop de temps à React Native.
Joshua Pinter
8

C'est parce que la galerie affiche correctement les images pivotées mais pas ImageView, regardez ici:

                    myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(),optionss);
                    ExifInterface exif = new ExifInterface(selectedImagePath);
                    int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
                    int rotationInDegrees = exifToDegrees(rotation);
                    deg = rotationInDegrees;
                    Matrix matrix = new Matrix();
                    if (rotation != 0f) {
                        matrix.preRotate(rotationInDegrees);
                        myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true);
                    }

et vous en avez besoin:

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;    
} 
farhad.kargaran
la source
5

Je l'ai fait fonctionner après de nombreuses tentatives grâce à un message que je ne trouve plus :-(

Exif semble toujours fonctionner, la difficulté était d'obtenir le chemin du fichier. Le code que j'ai trouvé fait une différence entre les API antérieures à 4.4 et ultérieures à 4.4. Fondamentalement, l'URI d'image pour 4.4+ contient "com.android.providers". Pour ce type d'URI, le code utilise DocumentsContract pour obtenir l'ID d'image, puis exécute une requête à l'aide de ContentResolver, tandis que pour les anciens SDK, le code interroge directement l'URI avec ContentResolver.

Voici le code (désolé, je ne peux pas créditer qui l'a publié):

/**
 * Handles pre V19 uri's
 * @param context
 * @param contentUri
 * @return
 */
public static String getPathForPreV19(Context context, Uri contentUri) {
    String res = null;

    String[] proj = { MediaStore.Images.Media.DATA };
    Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
    if(cursor.moveToFirst()){;
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        res = cursor.getString(column_index);
    }
    cursor.close();

    return res;
}

/**
 * Handles V19 and up uri's
 * @param context
 * @param contentUri
 * @return path
 */
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPathForV19AndUp(Context context, Uri contentUri) {
    String wholeID = DocumentsContract.getDocumentId(contentUri);

    // Split at colon, use second item in the array
    String id = wholeID.split(":")[1];
    String[] column = { MediaStore.Images.Media.DATA };

    // where id is equal to
    String sel = MediaStore.Images.Media._ID + "=?";
    Cursor cursor = context.getContentResolver().
            query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    column, sel, new String[]{ id }, null);

    String filePath = "";
    int columnIndex = cursor.getColumnIndex(column[0]);
    if (cursor.moveToFirst()) {
        filePath = cursor.getString(columnIndex);
    }

    cursor.close();
    return filePath;
}

public static String getRealPathFromURI(Context context,
        Uri contentUri) {
    String uriString = String.valueOf(contentUri);
    boolean goForKitKat= uriString.contains("com.android.providers");

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && goForKitKat) {
            Log.i("KIKAT","YES");
            return getPathForV19AndUp(context, contentUri);
        } else {

            return getPathForPreV19(context, contentUri);
        }
}
Georges
la source
Merci beaucoup pour vous. Après des heures de travail avec des curseurs et des exifs, cela a pu être sauvé. Comme vous l'avez dit, en fait, exif a des données vraies et fiables au lieu de retours de curseur. Donnez-lui simplement le chemin correct que cela fonctionne.
asozcan le
3

Vous pouvez simplement lire le chemin de la carte SD et faire le code suivant ... cela remplacera la photo existante après l'avoir fait pivoter.

Non: Exif ne fonctionne pas sur la plupart des appareils, il donne les mauvaises données, il est donc bon de coder en dur la rotation avant d'enregistrer à n'importe quel degré que vous voulez, il vous suffit de changer la valeur de l'angle dans postRotate à ce que vous voulez.

    String photopath = tempphoto.getPath().toString();
    Bitmap bmp = BitmapFactory.decodeFile(photopath);

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

    FileOutputStream fOut;
    try {
        fOut = new FileOutputStream(tempphoto);
        bmp.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
        fOut.flush();
        fOut.close();

    } catch (FileNotFoundException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}
Mayank Saini
la source
2
Il s'agit de faire pivoter mais nous ne savons pas si l'image a besoin d'une rotation.
MSaudi
3

Code Kotlin:

if (file.exists()){
    val bitmap = BitmapFactory.decodeFile(file.absolutePath)

    val exif = ExifInterface(file.absoluteFile.toString())
    val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
    val matrix = Matrix()

    when(orientation){
        ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
        ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
        ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
    }

    val rotatedBitmap = Bitmap.createBitmap(bitmap, 0,0 , bitmap.width, bitmap.height, matrix, true)
    bitmap.recycle()
    iv_capture.setImageBitmap(rotatedBitmap)
}
Rahul Khatri
la source
2

J'ai amélioré la réponse de Teo Inke. Il ne fait plus pivoter l'image à moins que cela ne soit réellement nécessaire. Il est également plus facile à lire et devrait fonctionner plus rapidement.

// Load Image
Bitmap bitmap = BitmapFactory.decodeFile(filePath);

// Rotate Image if Needed
try
{
    // Determine Orientation
    ExifInterface exif = new ExifInterface(filePath);
    int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

    // Determine Rotation
    int rotation = 0;
    if      (orientation == 6)      rotation = 90;
    else if (orientation == 3)      rotation = 180;
    else if (orientation == 8)      rotation = 270;

    // Rotate Image if Necessary
    if (rotation != 0)
    {
        // Create Matrix
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);

        // Rotate Bitmap
        Bitmap rotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); 

        // Pretend none of this ever happened!
        bitmap.recycle();
        bitmap = rotated;
        rotated = null;
     }
}
catch (Exception e)
{
    // TODO: Log Error Messages Here
}

// TODO: Use Result Here
xxx.setBitmap(bitmap);
user2820531
la source
2

La première chose dont vous avez besoin est le vrai chemin du fichier.Si vous l'avez bien, si vous utilisez l'URI, utilisez cette méthode pour obtenir le vrai chemin:

 public static String getRealPathFromURI(Uri contentURI,Context context) {
    String path= contentURI.getPath();
    try {
        Cursor cursor = context.getContentResolver().query(contentURI, null, null, null, null);
        cursor.moveToFirst();
        String document_id = cursor.getString(0);
        document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
        cursor.close();

        cursor = context.getContentResolver().query(
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
        cursor.moveToFirst();
        path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        cursor.close();
    }
    catch(Exception e)
    {
        return path;
    }
    return path;
}

extrayez votre Bitmap par exemple:

  try {
                            Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);

                        }
                        catch (IOException e)
                        {
                            Log.e("IOException",e.toString());
                        }

vous pouvez utiliser decodeFile () à la place si vous le souhaitez.

Maintenant que vous avez le Bitmap et le vrai chemin, obtenez l'orientation de l'image:

 private static int getExifOrientation(String src) throws IOException {
        int orientation = 1;

        ExifInterface exif = new ExifInterface(src);
        String orientationString=exif.getAttribute(ExifInterface.TAG_ORIENTATION);
        try {
            orientation = Integer.parseInt(orientationString);
        }
        catch(NumberFormatException e){}

        return orientation;
    }

et enfin le faire pivoter dans la bonne position comme ceci:

public static Bitmap rotateBitmap(String src, Bitmap bitmap) {
        try {
            int orientation = getExifOrientation(src);

            if (orientation == 1) {
                return bitmap;
            }

            Matrix matrix = new Matrix();
            switch (orientation) {
                case 2:
                    matrix.setScale(-1, 1);
                    break;
                case 3:
                    matrix.setRotate(180);
                    break;
                case 4:
                    matrix.setRotate(180);
                    matrix.postScale(-1, 1);
                    break;
                case 5:
                    matrix.setRotate(90);
                    matrix.postScale(-1, 1);
                    break;
                case 6:
                    matrix.setRotate(90);
                    break;
                case 7:
                    matrix.setRotate(-90);
                    matrix.postScale(-1, 1);
                    break;
                case 8:
                    matrix.setRotate(-90);
                    break;
                default:
                    return bitmap;
            }

            try {
                Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                bitmap.recycle();
                return oriented;
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
                return bitmap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bitmap;
    }

Voilà, vous avez maintenant le bitmap tourné à la bonne position.

à votre santé.

Gal Rom
la source
1

Cela fonctionne, mais ce n'est probablement pas la meilleure façon de le faire, mais cela pourrait aider quelqu'un.

String imagepath = someUri.getAbsolutePath();
imageview = (ImageView)findViewById(R.id.imageview);
imageview.setImageBitmap(setImage(imagepath, 120, 120));    

public Bitmap setImage(String path, final int targetWidth, final int targetHeight) {
    Bitmap bitmap = null;
// Get exif orientation     
    try {
        ExifInterface exif = new ExifInterface(path);
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
        if (orientation == 6) {
            orientation_val = 90;
        }
        else if (orientation == 3) {
            orientation_val = 180;
        }
        else if (orientation == 8) {
            orientation_val = 270;
        }
    }
        catch (Exception e) {
        }

        try {
// First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, options);

// Adjust extents
            int sourceWidth, sourceHeight;
            if (orientation_val == 90 || orientation_val == 270) {
                sourceWidth = options.outHeight;
                sourceHeight = options.outWidth;
            } else {
                sourceWidth = options.outWidth;
                sourceHeight = options.outHeight;
            }

// Calculate the maximum required scaling ratio if required and load the bitmap
            if (sourceWidth > targetWidth || sourceHeight > targetHeight) {
                float widthRatio = (float)sourceWidth / (float)targetWidth;
                float heightRatio = (float)sourceHeight / (float)targetHeight;
                float maxRatio = Math.max(widthRatio, heightRatio);
                options.inJustDecodeBounds = false;
                options.inSampleSize = (int)maxRatio;
                bitmap = BitmapFactory.decodeFile(path, options);
            } else {
                bitmap = BitmapFactory.decodeFile(path);
            }

// Rotate the bitmap if required
            if (orientation_val > 0) {
                Matrix matrix = new Matrix();
                matrix.postRotate(orientation_val);
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            }

// Re-scale the bitmap if necessary
            sourceWidth = bitmap.getWidth();
            sourceHeight = bitmap.getHeight();
            if (sourceWidth != targetWidth || sourceHeight != targetHeight) {
                float widthRatio = (float)sourceWidth / (float)targetWidth;
                float heightRatio = (float)sourceHeight / (float)targetHeight;
                float maxRatio = Math.max(widthRatio, heightRatio);
                sourceWidth = (int)((float)sourceWidth / maxRatio);
                sourceHeight = (int)((float)sourceHeight / maxRatio);
                bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth,     sourceHeight, true);
            }
        } catch (Exception e) {
        }
        return bitmap;
    }
erebos
la source
1

peut-être que cela aidera (tourner à 90 degrés) (cela a fonctionné pour moi)

private Bitmap rotateBitmap(Bitmap image){
        int width=image.getHeight();
        int height=image.getWidth();

        Bitmap srcBitmap=Bitmap.createBitmap(width, height, image.getConfig());

        for (int y=width-1;y>=0;y--)
            for(int x=0;x<height;x++)
                srcBitmap.setPixel(width-y-1, x,image.getPixel(x, y));
        return srcBitmap;

    }
tzahibs
la source
1

Le curseur doit être fermé après son ouverture.

Voici un exemple.

 public static int getOrientation(Context context, Uri selectedImage)
{
    int orientation = -1;
    Cursor cursor = context.getContentResolver().query(selectedImage,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
    if (cursor.getCount() != 1)
       return orientation;

    cursor.moveToFirst();
    orientation = cursor.getInt(0);
    cursor.close(); // ADD THIS LINE
   return orientation;
}
GuillaumeAgis
la source
1

J'ai fondu la réponse @Timmmm et @Manuel. Si vous faites cette solution, vous n'obtiendrez pas d'exception de manque de mémoire.

Cette méthode récupère l'orientation de l'image:

private static final int ROTATION_DEGREES = 90;
// This means 512 px
private static final Integer MAX_IMAGE_DIMENSION = 512;

public static int getOrientation(Uri photoUri) throws IOException {

    ExifInterface exif = new ExifInterface(photoUri.getPath());
    int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            orientation = ROTATION_DEGREES;
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            orientation = ROTATION_DEGREES * 2;
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            orientation = ROTATION_DEGREES * 3;
            break;
        default:
            // Default case, image is not rotated
            orientation = 0;
    }

    return orientation;
}

Par conséquent, vous utiliserez cette méthode pour redimensionner l'image avant de la charger en mémoire. De cette façon, vous n'obtiendrez pas d'exception 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(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, 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;
}

Cela fonctionne parfaitement pour moi. J'espère que cela aide quelqu'un d'autre

Antonio
la source
0

Amélioration de la solution ci-dessus par Timmmm pour ajouter une mise à l'échelle supplémentaire à la fin pour garantir que l'image s'insère dans les limites:

public static Bitmap loadBitmap(String path, int orientation, final int targetWidth, final int targetHeight) {
    Bitmap bitmap = null;
    try {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Adjust extents
        int sourceWidth, sourceHeight;
        if (orientation == 90 || orientation == 270) {
            sourceWidth = options.outHeight;
            sourceHeight = options.outWidth;
        } else {
            sourceWidth = options.outWidth;
            sourceHeight = options.outHeight;
        }

        // Calculate the maximum required scaling ratio if required and load the bitmap
        if (sourceWidth > targetWidth || sourceHeight > targetHeight) {
            float widthRatio = (float)sourceWidth / (float)targetWidth;
            float heightRatio = (float)sourceHeight / (float)targetHeight;
            float maxRatio = Math.max(widthRatio, heightRatio);
            options.inJustDecodeBounds = false;
            options.inSampleSize = (int)maxRatio;
            bitmap = BitmapFactory.decodeFile(path, options);
        } else {
            bitmap = BitmapFactory.decodeFile(path);
        }

        // Rotate the bitmap if required
        if (orientation > 0) {
            Matrix matrix = new Matrix();
            matrix.postRotate(orientation);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        }

        // Re-scale the bitmap if necessary
        sourceWidth = bitmap.getWidth();
        sourceHeight = bitmap.getHeight();
        if (sourceWidth != targetWidth || sourceHeight != targetHeight) {
            float widthRatio = (float)sourceWidth / (float)targetWidth;
            float heightRatio = (float)sourceHeight / (float)targetHeight;
            float maxRatio = Math.max(widthRatio, heightRatio);
            sourceWidth = (int)((float)sourceWidth / maxRatio);
            sourceHeight = (int)((float)sourceHeight / maxRatio);
            bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
        }
    } catch (Exception e) {
    }
    return bitmap;
}
Méchant homme
la source
0

Utilisez le code suivant pour faire pivoter correctement une image:

private Bitmap rotateImage(Bitmap bitmap, String filePath)
{
    Bitmap resultBitmap = bitmap;

    try
    {
        ExifInterface exifInterface = new ExifInterface(filePath);
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

        Matrix matrix = new Matrix();

        if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
        {
            matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_90);
        }
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
        {
            matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_180);
        }
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
        {
            matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_270);
        }

        // Rotate the bitmap
        resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }
    catch (Exception exception)
    {
        Logger.d("Could not rotate the image");
    }
    return resultBitmap;
}
NightBits
la source
Vous pouvez fusionner toutes les conditions if ensemble pour avoir un code plus petit.
Chandranshu
0

Les méthodes ci-dessous mettent à l'échelle ET font pivoter le bitmap en fonction de l'orientation:

public Bitmap scaleAndRotateImage(String path, int orientation, final int targetWidth, final int targetHeight)
{
    Bitmap bitmap = null;

    try
    {
        // Check the dimensions of the Image
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Adjust the Width and Height
        int sourceWidth, sourceHeight;
        if (orientation == 90 || orientation == 270)
        {
            sourceWidth = options.outHeight;
            sourceHeight = options.outWidth;
        }
        else
        {
            sourceWidth = options.outWidth;
            sourceHeight = options.outHeight;
        }

        // Calculate the maximum required scaling ratio if required and load the bitmap
        if (sourceWidth > targetWidth || sourceHeight > targetHeight)
        {
            float widthRatio = (float)sourceWidth / (float)targetWidth;
            float heightRatio = (float)sourceHeight / (float)targetHeight;
            float maxRatio = Math.max(widthRatio, heightRatio);
            options.inJustDecodeBounds = false;
            options.inSampleSize = (int)maxRatio;
            bitmap = BitmapFactory.decodeFile(path, options);
        }
        else
        {
            bitmap = BitmapFactory.decodeFile(path);
        }

        // We need to rotate the bitmap (if required)
        int orientationInDegrees = exifToDegrees(orientation);
        if (orientation > 0)
        {
            Matrix matrix = new Matrix();
            if (orientation != 0f)
            {
                matrix.preRotate(orientationInDegrees);
            };

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

        // Re-scale the bitmap if necessary
        sourceWidth = bitmap.getWidth();
        sourceHeight = bitmap.getHeight();

        if (sourceWidth != targetWidth || sourceHeight != targetHeight)
        {
            float widthRatio = (float)sourceWidth / (float)targetWidth;
            float heightRatio = (float)sourceHeight / (float)targetHeight;
            float maxRatio = Math.max(widthRatio, heightRatio);
            sourceWidth = (int)((float)sourceWidth / maxRatio);
            sourceHeight = (int)((float)sourceHeight / maxRatio);
            bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
        }
    }
    catch (Exception e)
    {
        Logger.d("Could not rotate the image");
        Logger.d(e.getMessage());
    }
    return bitmap;
}

Exemple:

public void getPictureFromDevice(Uri Uri,ImageView imageView)
{
    try
    {
        ExifInterface exifInterface = new ExifInterface(Uri.getPath());
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        Bitmap bitmap = scaleAndRotateImage(Uri.getPath(), orientation, imageView.getWidth(), imageView.getHeight());
        imageView.setImageBitmap(bitmap);
    }
    catch (OutOfMemoryError outOfMemoryError)
    {
        Logger.d(outOfMemoryError.getLocalizedMessage());
        Logger.d("Failed to load image from filePath (out of memory)");
        Logger.d(Uri.toString());
    }
    catch (Exception e)
    {
        Logger.d("Failed to load image from filePath");
        Logger.d(Uri.toString());
    }
}
NightBits
la source
-3

J'ai résolu le problème avec la solution de contournement suivante. Notez que je redimensionne également l'image, ce qui était nécessaire pour éviter les OutOfMemoryExceptions.

Attention, cette solution ne fonctionnera pas correctement avec des images de portrait ou des images à l'envers (merci Timmmm de l'avoir noté). La solution de Timmmm ci-dessus pourrait être le meilleur choix si cela est nécessaire et elle a également l'air plus élégante: https://stackoverflow.com/a/8914291/449918

File path = // ... location of your bitmap file
int w = 512; int h = 384; // size that does not lead to OutOfMemoryException on Nexus One
Bitmap b = BitmapFactory.decodeFile(path);


// Hack to determine whether the image is rotated
boolean rotated = b.getWidth() > b.getHeight();

Bitmap resultBmp = null;

// If not rotated, just scale it
if (!rotated) {
    resultBmp = Bitmap.createScaledBitmap(b, w, h, true);
    b.recycle();
    b = null;

// If rotated, scale it by switching width and height and then rotated it
} else {
    Bitmap scaledBmp = Bitmap.createScaledBitmap(b, h, w, true);
    b.recycle();
    b = null;

    Matrix mat = new Matrix();
    mat.postRotate(90);
    resultBmp = Bitmap.createBitmap(scaledBmp, 0, 0, h, w, mat, true);

    // Release image resources
    scaledBmp.recycle();
    scaledBmp = null;
}

// resultBmp now contains the scaled and rotated image

À votre santé

Martin
la source
Cela ne fonctionnera pas correctement. Qu'en est-il des images de portrait? Des images à l'envers? L'utilisation des données exif est bien meilleure.
Timmmm
Cela fonctionne correctement dans l'une de mes applications, mais bien sûr, je n'ai pas testé tous les types de scénarios. @Timmmm pourriez-vous être plus précis dans quels scénarios cela ne fonctionne pas? Je suis également assez perplexe que vous ayez voté contre mon poste. Cela semble être une réponse assez dure à une tentative honnête de partager une solution potentielle.
Martin
Je ne voulais pas être dur; Désolé! Je ne voulais simplement pas que quiconque copie votre solution en espérant qu'elle fonctionnerait. Comme je l'ai dit, cela ne fonctionnera pas pour les portraits ou les images à l'envers. J'ajouterai la bonne solution comme réponse.
Timmmm
Je vois. J'ajouterai un commentaire soulignant votre solution ci-dessus comme étant la solution préférée.
Martin