web-dev-qa-db-fra.com

Rotation inexplicable de la caméra Android à la capture pour certains appareils (pas dans EXIF)

Ce que je fais semble être simple, mais je suis toujours perdu après avoir lu toutes les réponses possibles à Stackoverflow que je peux trouver et googler tous les articles que je peux trouver.

J'utilise un aperçu de SurfaceView et je capture une image d'une activité définie pour screenOrientation = "landscape" dans mon fichier AndroidManifest.xml.

J'ai suivi l'exemple de code d'application Camera et pensé que tout fonctionnait bien jusqu'à ce que j'aie essayé mon application sur quelques appareils Motorola exécutant la version 1.5.

OrientationEventListener fonctionne correctement et j'utilise la réflexion pour voir si définir la rotation comme telle:

final int latchedOrientation = roundOrientation(mLastOrientation + 90);

Parameters parameters = preview.camera.getParameters();

JPLog.d("Setting camera rotation = %d", latchedOrientation);
try {
    // if >= 2.0
    Method method = Camera.Parameters.class.getMethod("setRotation",
        int.class);

    if(method != null) {
        method.invoke(parameters, latchedOrientation);
    }

} catch(Throwable t) {
    // if < 2.0
    parameters.set("rotation", latchedOrientation);
}

preview.camera.setParameters(parameters);

NexusOne (OS 2.2) - Fonctionne très bien. latchedOrientation = 0, image OK sans rotation dans l'en-tête EXIF.

T-Mobile G1 (OS 1.6) - Fonctionne également très bien. latchedOrientation = 0, l'image est correcte.

Motorola Backflip (OS 1.5) - Image pivotée. latchedOrientation = 0, l'image n'a pas de rotation EXIF.

Motorola CLIQ (OS 1.5) - Image pivotée. latchedOrientation = 0, l'image n'a pas de rotation EXIF.

Que se passe-t-il avec ces appareils Motorola? Je pensais que mon problème était que le pilote de l'appareil photo Motorola ne faisait pas pivoter les images. J'ai donc trouvé les cours de lecture Sansifan EXIF ​​pour Android et me préparais à les faire pivoter moi-même. Ce qui est drôle, c’est qu’il ya des en-têtes EXIF ​​mais pas d’élément de rotation.

Si je règle manuellement la rotation à 90 degrés, les images seront parfaites pour les appareils Motorola, mais le G1 et le NexusOne ont maintenant des images pivotées de 90 degrés (ce que je ne veux pas). Il doit y avoir quelque chose que je ne vais pas arriver ici.

Je doute que ce soit un problème 1.5, ou sinon quelqu'un aurait posté des infos dessus?

27
Scott Merritt

Il s'agit en fait d'un problème spécifique à un périphérique qui concerne principalement les périphériques Motorola. Les développeurs de Google ont inclus unSETDISPLAYORIENTATIONcall dans l'API 8 pour contourner le problème. Le bogue principal est archivé ici .

Pour ceux qui ne peuvent pas accéder à l'API 8, les deux solutions courantes sont les suivantes:

Remplacer onDraw
Remplacez onDraw dans un ViewGroup de niveau supérieur et faites pivoter la toile de 90 degrés pour compenser la rotation. Notez qu’il ya une mise en garde ici car vos événements tactiles devront également faire l’objet d’une rotation.

Utiliser le mode paysage
Verrouillez l’activité en mode Paysage, mais dessinez les ressources comme si elles étaient en portrait. Cela signifie que vous faites votre mise en page et que vous faites pivoter vos ressources d'image pour donner l'impression que vous êtes en mode portrait afin que la vue soit normale. Cela rend malheureusement difficile l’utilisation du menu car celui-ci s’ouvrira horizontalement.

J'ai également vu des personnes utiliser un contrôleur d'animation pour faire pivoter la vue. L'inconvénient que je n'ai pas pu surmonter est que la vue pivotée ne s'étend pas pour remplir l'écran. Voir le réponse de Georg pour un exemple de mise en œuvre.

10
Error 454

J'ai eu ce problème et j'ai utilisé cette méthode pour capturer l'image. (Sans créer une caméra personnalisée)

final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(intent, 0);

et a fait le reste dans onActivityResult (int requestCode, int resultCode, données d'intention) {}

Mais l'image d'origine (Image réelle de la carte SD) était correcte et Bitmap a été pivoté lorsque j'ai récupéré le fichier suivant. Bitmap bmp = BitmapFactory.decodeStream (..

La solution: 

    try {
        File f = new File(SD_CARD_IMAGE_PATH);
        ExifInterface exif = new ExifInterface(f.getPath());
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        int angle = 0;

        if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
            angle = 90;
        } 
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
            angle = 180;
        } 
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
            angle = 270;
        }

        Matrix mat = new Matrix();
        mat.postRotate(angle);

        Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
        Bitmap correctBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);                 
    }
    catch (IOException e) {
        Log.w("TAG", "-- Error in setting image");
    }   
    catch(OutOfMemoryError oom) {
        Log.w("TAG", "-- OOM Error in setting image");
    }
45
Rukmal Dias

Voici le code que j'ai utilisé onActivityResult () dans mon activité. L'intention renvoyée était de choisir une image du type image/*. Fonctionne bien pour moi! 

Uri imageUri = intent.getData();
                String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
                Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
                int orientation = -1;
                if (cur != null && cur.moveToFirst()) {
                    orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
                }  
                Matrix matrix = new Matrix();
                matrix.postRotate(orientation);
4
dbaugh

Il semble que la suggestion d'utilisation du mode paysage est la seule chose qui fonctionne vraiment. Il semble que ce soit correct que cela soit dans le manifeste ou via un appel à setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) dans l'activité onCreate.

0
KayEss