web-dev-qa-db-fra.com

Les images prises avec ACTION_IMAGE_CAPTURE renvoient toujours 1 pour ExifInterface.TAG_ORIENTATION sur certains périphériques Gingerbread.

J'ai eu le problème d'orientation lorsque je travaillais avec l'activité ACTION_IMAGE_CAPTURE. J'ai utilisé le TAG_ORIENTATION pour pouvoir faire pivoter l'image en conséquence. Mais maintenant, nous avons constaté que cela ne fonctionnait pas sur certains appareils plus récents. En fait, il retourne 1 pour toutes les orientations.

Voici la liste des périphériques sur lesquels nous avons observé ceci; 

  • Samsung Infuse 4G (2.3.3) 
  • Samsung Galaxy SII X (2.3.5) 
  • Sony Xperia Arc (2.3.3)

Ce qui est intéressant, c'est qu'une fois que cette image est la galerie, elle s'affiche correctement et si je la sélectionne, le TAG_ORIENTATION est rempli correctement. Donc, d'une manière ou d'une autre, OS remplit cette information correctement mais pas sur ActivityResult.

Quel est le moyen le plus fiable de déterminer l'orientation? Quelqu'un sur une autre question a suggéré de comparer la hauteur et la largeur, mais lors de l'obtention de celles-ci, elles sont correctement commutées en fonction de l'orientation (un autre mystère)

EDIT: Il semble que cela pourrait être lié à un autre bug où le système d'exploitation duplique l'image prise dans la galerie (il est uniquement censé enregistrer l'image dans l'URL spécifiée par nous), le fait est que cette image dans la galerie contient les informations ORIENTATION pendant celui de l'emplacement spécifié ne le fait pas.

C'est le bug. http://code.google.com/p/Android/issues/detail?id=19268

EDIT-2: J'ai déposé un nouveau bogue avec Android. Je suis à peu près sûr que c'est un bogue du système d'exploitation lié au bogue susmentionné. http://code.google.com/p/Android/issues/detail?id=22822

40
Tolga E

Ok les gars, il semble que ce bug pour Android ne soit pas corrigé pendant un moment. Bien que j'ai trouvé un moyen d'implémenter ExifInformation pour que les deux périphériques (ceux avec la balise Exif appropriée et les balises exif inappropriées fonctionnent ensemble).

Le problème concerne donc certains appareils (plus récents). Il existe un bogue qui rend la photo prise enregistrée dans le dossier de votre application sans les balises exif appropriées, alors qu'une image correctement pivotée est enregistrée dans le dossier par défaut d'Android (même si ce n'est pas le cas). .

Maintenant, ce que je fais, c’est que j’enregistre le moment où je lance l’application appareil photo à partir de mon application. Ensuite, sur le résultat de l'activité, j'interroge le fournisseur de média pour savoir si des images ont été enregistrées après l'horodatage que j'ai enregistré. Cela signifie que, très probablement, le système d’exploitation a sauvegardé l’image correctement pivotée dans le dossier par défaut et, bien sûr, a placé une entrée dans le magasin de média et nous pouvons utiliser les informations de rotation de cette ligne. Maintenant, pour vérifier que nous recherchons la bonne image, je compare la taille de ce fichier à celle à laquelle j'ai accès (enregistrée dans mon propre dossier d'application);

    int rotation =-1;
    long fileSize = new File(filePath).length();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] {MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.MediaColumns.SIZE }, MediaStore.MediaColumns.DATE_ADDED + ">=?", new String[]{String.valueOf(captureTime/1000 - 1)}, MediaStore.MediaColumns.DATE_ADDED + " desc");

    if (mediaCursor != null && captureTime != 0 && mediaCursor.getCount() !=0 ) {
        while(mediaCursor.moveToNext()){
            long size = mediaCursor.getLong(1);
            //Extra check to make sure that we are getting the orientation from the proper file
            if(size == fileSize){
                rotation = mediaCursor.getInt(0);
                break;
            }
        }
    }

Maintenant, si la rotation à ce stade est toujours -1, cela signifie que c'est l'un des téléphones avec les informations de rotation appropriées. À ce stade, nous pouvons utiliser l'orientation exif habituelle sur le fichier renvoyé à notre onActivityResult.

    else if(rotation == -1){
        rotation = getExifOrientationAttribute(filePath);
    }

Vous pouvez facilement trouver comment trouver des orientations exif, comme la réponse à cette question Problème d'orientation de l'appareil photo sous Android

Notez également qu'ExifInterface est uniquement pris en charge après le niveau 5 d'API. Ainsi, si vous souhaitez prendre en charge les téléphones antérieurs à la version 2.0, vous pouvez utiliser cette bibliothèque pratique que j'ai trouvée pour Java avec la permission de Drew Noakes; http://www.drewnoakes.com/code/exif/

Bonne chance avec votre image en rotation!

EDIT: Parce qu'il a été demandé, l'intention que j'ai utilisé et comment j'ai commencé était comme ça

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//mediaFile is where the image will be saved
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mediaFile));
startActivityForResult(intent, 1);
53
Tolga E

vous pouvez y aller aussi:

Matrix matrix = new Matrix();
// rotate the Bitmap (there a problem with exif so we'll query the mediaStore for orientation
Cursor cursor = getApplicationContext().getContentResolver().query(selectedImage,
      new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() == 1) {
cursor.moveToFirst();
    int orientation =  cursor.getInt(0);
    matrix.preRotate(orientation);
    }
7
oferiko

En effet un bug problématique! Je ne suis pas sûr d'aimer la solution de contournement suggérée, alors en voici un autre :)

La clé consiste à utiliser EXTRA_OUTPUT et à l'interroger lorsque l'image a été capturée! Évidemment, cela ne fonctionne que si vous vous permettez de spécifier le nom du fichier.

protected void takePictureSequence() {      
    try {
        ContentValues values = new ContentValues();  
        values.put(MediaStore.Images.Media.TITLE, UUID.randomUUID().toString() + ".jpg");  
        newPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

        Intent intent = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(Android.provider.MediaStore.EXTRA_OUTPUT, newPhotoUri);

        startActivityForResult(intent, ActivityResults.TAKE_NEW_PICTURE_RESULT);
    } catch (Exception e) {
        Toast.makeText(this, R.string.could_not_initalize_camera, Toast.LENGTH_LONG).show();
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ActivityResults.TAKE_NEW_PICTURE_RESULT) {
        if (resultCode == RESULT_OK) {
            try {
                String[] projection = { MediaStore.Images.Media.DATA }; 
                CursorLoader loader = new CursorLoader(this, newPhotoUri, projection, null, null, null);
                Cursor cursor = loader.loadInBackground();

                int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();

                // Rotation is stored in an EXIF tag, and this tag seems to return 0 for URIs.
                // Hence, we retrieve it using an absolute path instead!
                int rotation = 0;
                String realPath = cursor.getString(column_index_data);
                if (realPath != null) {
                    rotation = ImageHelper.getRotationForImage(realPath);
                }

                // Now we can load the bitmap from the Uri, using the correct rotation.
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public int getRotationForImage(String path) {
    int rotation = 0;

    try {
        ExifInterface exif = new ExifInterface(path);
        rotation = (int)exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return rotation;
}
7
l33t

Ce que j’ai appris récemment, c’est que si vous redimensionnez l’image, elle perd généralement ses informations EXIF. Donc, vous voulez donner au nouveau fichier les anciennes informations EXIF. 

La source.

2
Rohit Sharma

Ma solution pour cela. Testé sur LG G2 mobile. J'ai remarqué que lorsque j'utilise un appareil photo et que je prends une nouvelle photo, tout fonctionne correctement. ExifInterface renvoie la bonne orientation. Donc, il doit y avoir quelque chose dans le chemin car mon chemin était null dans cette ligne de code:

exif = new ExifInterface(path);

mais quand j'ai utilisé le chemin absolu mon appli plante. Mais la solution est dans cette méthode ci-dessous, car cela dépend de votre version de SDK. Encore une note à mentionner, j’ai utilisé le chemin d'accès absolu uniquement pour sélectionner une image de la galerie, car si je l’utilisais pour l'appareil photo, mon application s'effondrait. Je suis nouveau dans la programmation et vient de perdre 2 jours pour résoudre ce problème. J'espère que ça va aider quelqu'un.

   public String getRealPathFromURI(Uri uri) {
        if(Build.VERSION.SDK_INT >= 19){
            String id = uri.getLastPathSegment().split(":")[1];
            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{
            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;
        }
    }

Je reçois donc mon ExifInterface dans la méthode onActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GALLERY_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
        try {
            exif = new ExifInterface(getRealPathFromURI(data.getData()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(data.getData());
    } else if (requestCode == CAMERA_IMAGE_REQUEST && resultCode == RESULT_OK) {
        try {
            exif = new ExifInterface(Uri.fromFile(getCameraFile()).getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(Uri.fromFile(getCameraFile()));
    }
}

et ma méthode de show image ressemble à ceci

public void showImage(Uri uri) {
    if (uri != null) {
        try {

            Bitmap bitmap = scaleBitmapDown(MediaStore.Images.Media.getBitmap(getContentResolver(), uri), IMAGE_SIZE);

            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

            bitmap = rotateBitmap(bitmap, orientation);



            if (whatPlayer.equals("Player1")) {
                mImagePlayer1.setImageBitmap(bitmap);

                bitmapPlayer1 = bitmap; //*save picture in static variable so other activity can use this
            }
            if (whatPlayer.equals("Player2")) {
                mImagePlayer2.setImageBitmap(bitmap);

                bitmapPlayer2 = bitmap;
            }

        } catch (IOException e) {
            Log.d(TAG, "Image picking failed because " + e.getMessage());
            Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
        }
    } else {
        Log.d(TAG, "Image picker gave us a null image.");
        Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
    }
}
0
Lantus