Pourquoi une image capturée à l'aide de l'objectif de la caméra est-elle pivotée sur certains appareils sur Android?

377

Je capture une image et la mets en vue image.

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

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

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

Mais le problème est que l'image sur certains appareils à chaque rotation. Par exemple, sur un appareil Samsung, cela fonctionne bien, mais sur un Sony Xperia, l'image pivote de 90 degrés et sur Toshiba Thrive (tablette) de 180 degrés.

Shirish Herwade
la source
1
essayez ceci dans votre activité menifest android: configChanges = "orientation" android: screenOrientation = "portrait"
Narendra Pal
@nick ça ne marche pas, maintenant l'image pivote à 90 degrés au lieu de 180 degrés sur l'onglet
Shirish Herwade
1
comme je pense que lorsque vous utilisez l'intention interne pour traiter l'application de l'appareil photo, cela fait pivoter l'image. Cela dépend de la façon dont vous tenez l'appareil pour capturer l'image. Ainsi, vous pouvez restreindre l'utilisateur à prendre une image de manière particulière, ce qui signifie que l'utilisateur capturera toujours l'image en tenant l'appareil en mode portrait ou paysage. Après cela, vous pouvez le changer à un angle spécifique pour obtenir l'image que vous souhaitez .. OU UNE AUTRE OPTION, FAITES VOTRE PROPRE APPAREIL PHOTO.
Narendra Pal
@nick "vous pouvez restreindre l'utilisateur à prendre une image d'une manière particulière" signifie que c'est la même chose que de définir orientation = "potrait"? Et comment "Après cela, vous pouvez le changer selon un angle spécifique pour obtenir l'image comme vous le souhaitez"? S'il vous plaît pouvez-vous donner quelques liens utiles
Shirish Herwade
3
Je crois que l'intention de capture fait toujours apparaître l'application d'appareil photo par défaut qui a une orientation spécifique sur chaque appareil et, par conséquent, une orientation photo fixe. Cela ne dépend pas de la façon dont l'utilisateur tient l'appareil ou de l'orientation de votre activité qui a invoqué l'intention.
Alex Cohn

Réponses:

441

La plupart des appareils photo du téléphone sont en mode paysage, ce qui signifie que si vous prenez la photo en mode portrait, les photos résultantes seront pivotées de 90 degrés. Dans ce cas, le logiciel de l'appareil photo doit remplir les données Exif avec l'orientation dans laquelle la photo doit être affichée.

Notez que la solution ci-dessous dépend du fabricant du logiciel / appareil photo de l'appareil photo qui remplit les données Exif, donc cela fonctionnera dans la plupart des cas, mais ce n'est pas une solution fiable à 100%.

ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                     ExifInterface.ORIENTATION_UNDEFINED);

Bitmap rotatedBitmap = null;
switch(orientation) {

    case ExifInterface.ORIENTATION_ROTATE_90:
        rotatedBitmap = rotateImage(bitmap, 90);
        break;

    case ExifInterface.ORIENTATION_ROTATE_180:
        rotatedBitmap = rotateImage(bitmap, 180);
        break;

    case ExifInterface.ORIENTATION_ROTATE_270:
        rotatedBitmap = rotateImage(bitmap, 270);
        break;

    case ExifInterface.ORIENTATION_NORMAL:
    default:
        rotatedBitmap = bitmap;
}

Voici la rotateImageméthode:

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
                               matrix, true);
}
Jason Robinson
la source
1
Grâce au code @JasonRobinson, j'apprends comment obtenir l'orientation réelle et en combinant avec ces codes, je réussis à gérer l'orientation.
Raditya Kurnianto
La deuxième option d' exif.getAttributeIntutilisation ExifInterface.ORIENTATION_UNDEFINEDest presque la même, car le deuxième paramètre est la valeur par défaut au cas où la fonction ne fournirait pas la valeur.
Darpan
5
Ce code est pour une image déjà écrite sur le disque, non? Je n'obtiens aucun résultat en utilisant cette méthode pour que le bitmap soit sur le point d'être écrit sur le disque.
Thrace
4
C'est toujours me renvoyer la valeur 0. Veuillez indiquer comment obtenir une orientation réelle.
Anurag Srivastava
3
Obtenir toujours 0, une idée pourquoi?
Navya Ramesan
186

En combinant Jason Robinson « s réponse avec Felix » s réponse et remplir les pièces manquantes, voici la solution finale et complète de cette question qui fera ce qui suit après l' avoir testé sur Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) et Android 5.0 ( Lollipop ).

Pas

  1. Réduisez l'image si elle était supérieure à 1024x1024.

  2. Faites pivoter l'image dans le bon sens uniquement si elle a été pivotée de 90, 180 ou 270 degrés.

  3. Recyclez l'image pivotée à des fins de mémoire.

Voici la partie code:

Appelez la méthode suivante avec le courant Contextet l'image URIque vous souhaitez corriger

/**
 * This method is responsible for solving the rotation issue if exist. Also scale the images to
 * 1024x1024 resolution
 *
 * @param context       The current context
 * @param selectedImage The Image URI
 * @return Bitmap image results
 * @throws IOException
 */
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
        throws IOException {
    int MAX_HEIGHT = 1024;
    int MAX_WIDTH = 1024;

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(context, img, selectedImage);
    return img;
}

Voici la CalculateInSampleSizeméthode de la source précitée :

/**
  * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
  * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
  * the closest inSampleSize that will result in the final decoded bitmap having a width and
  * height equal to or larger than the requested width and height. This implementation does not
  * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
  * results in a larger bitmap which isn't as useful for caching purposes.
  *
  * @param options   An options object with out* params already populated (run through a decode*
  *                  method with inJustDecodeBounds==true
  * @param reqWidth  The requested width of the resulting bitmap
  * @param reqHeight The requested height of the resulting bitmap
  * @return The value to be used for inSampleSize
  */
private static int calculateInSampleSize(BitmapFactory.Options options,
                                         int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}

Vient ensuite la méthode qui vérifiera l'orientation actuelle de l'image pour décider de l'angle de rotation

 /**
 * Rotate an image if required.
 *
 * @param img           The image bitmap
 * @param selectedImage Image URI
 * @return The resulted Bitmap after manipulation
 */
private static Bitmap rotateImageIfRequired(Context context, Bitmap img, Uri selectedImage) throws IOException {

InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
    ei = new ExifInterface(input);
else
    ei = new ExifInterface(selectedImage.getPath());

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

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            return rotateImage(img, 90);
        case ExifInterface.ORIENTATION_ROTATE_180:
            return rotateImage(img, 180);
        case ExifInterface.ORIENTATION_ROTATE_270:
            return rotateImage(img, 270);
        default:
            return img;
    }
}

Enfin la méthode de rotation elle-même

private static Bitmap rotateImage(Bitmap img, int degree) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
    img.recycle();
    return rotatedImg;
}

-N'oubliez pas de voter pour les réponses de ces gars pour leurs efforts et Shirish Herwade qui a posé cette question utile.

Sami Eltamawy
la source
2
Its Woking for me Perfectly.Thank you
Shohel Rana
1
la méthode rotationImageIfRequired () fonctionne très bien .. merci !!
mapo
5
Ça ne marche pas pour moi. Parfois, mon téléphone donne des portraits, parfois des photos de paysages, mais l'orientation détectée est toujours de 0 degré.
Makalele
@Makalele Ce problème se produit-il également lors de la prise de photos et de la connexion via WhatsApp?
Manoj Perumarath
Je n'utilise pas WhatsApp donc je ne peux pas dire, mais très probablement oui. En effet, cela se produit même dans l'application photo stock (Google Stock Camera).
Makalele
45

Il est facile de détecter l'orientation de l'image et de remplacer le bitmap à l'aide de:

 /**
 * Rotate an image if required.
 * @param img
 * @param selectedImage
 * @return
 */
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {

    // Detect rotation
    int rotation = getRotation(context, selectedImage);
    if (rotation != 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    else{
        return img;
    }
}

/**
 * Get the rotation of the last image added.
 * @param context
 * @param selectedImage
 * @return
 */
private static int getRotation(Context context,Uri selectedImage) {

    int rotation = 0;
    ContentResolver content = context.getContentResolver();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                       new String[] { "orientation", "date_added" },
                                       null, null, "date_added desc");

    if (mediaCursor != null && mediaCursor.getCount() != 0) {
        while(mediaCursor.moveToNext()){
            rotation = mediaCursor.getInt(0);
            break;
        }
    }
    mediaCursor.close();
    return rotation;
}

Pour éviter de manquer de souvenirs avec de grandes images, je vous recommande de redimensionner l'image en utilisant:

private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
    throws IOException {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(img, selectedImage);
    return img;
}

Il n'est pas possible d'utiliser ExifInterface pour obtenir l'orientation en raison d'un problème de système d'exploitation Android: https://code.google.com/p/android/issues/detail?id=19268

Et voici calculateInSampleSize

/**
 * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
 * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
 * the closest inSampleSize that will result in the final decoded bitmap having a width and
 * height equal to or larger than the requested width and height. This implementation does not
 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
 * results in a larger bitmap which isn't as useful for caching purposes.
 *
 * @param options   An options object with out* params already populated (run through a decode*
 *                  method with inJustDecodeBounds==true
 * @param reqWidth  The requested width of the resulting bitmap
 * @param reqHeight The requested height of the resulting bitmap
 * @return The value to be used for inSampleSize
 */
public static int calculateInSampleSize(BitmapFactory.Options options,
                                        int reqWidth, int reqHeight) {

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}
Felix
la source
1
qu'est-ce que la méthode CalculateInSampleSize ici
madhu kotagiri
1
@madhukotagiri ici vous avez un exemple d'implémentation pour CalculateInSampleSize: gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix
Merci mec, tu es définitivement le seul! Je me demande simplement dans quelle mesure le redimensionnement sera utile, si l'opération est effectuée de temps en temps.
Marino
4
Uri selectedImage paramètre non utilisé dans la méthode getRotation (...). Comment devons-nous l'utiliser? Je vous remercie.
valerybodak
1
Le paramètre 'selectedImage' ne semble être utilisé nulle part. Une raison d'être là-bas?
Alex
20

Solution en une ligne:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Ou

Picasso.with(context).load("file:" + photoPath).into(imageView);

Cela détectera automatiquement la rotation et placera l'image dans la bonne orientation

Picasso est une bibliothèque très puissante pour gérer les images dans votre application comprend: Transformations d'images complexes avec une utilisation minimale de la mémoire.

voytez
la source
1
Solution intéressante
Bhavik Mehta
8
Il charge simplement l'image dans une vue, il ne vous donne pas de bitmap ou de fichier que vous pouvez manipuler ou télécharger sur un serveur.
flawyte
4
Son image affichée a cliqué telle quelle. Il ne tourne pas comme requis.
seema
1
@Flawyte vous pouvez le faire en chargeant le fichier dans la cible au lieu de la vue avec un rappel qui renvoie un bitmap recadré / redimensionné: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Into (target); where target = new Target () {Override public void onBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom from) {
voytez
le problème auquel je suis toujours confronté est qu'il faut quelques secondes pour afficher l'image
Anu
12

J'ai passé beaucoup de temps à chercher une solution pour cela. Et finalement réussi à le faire. N'oubliez pas de voter pour @Jason Robinson, car ma réponse est basée sur la sienne.

Donc, premièrement, vous devez savoir que depuis Android 7.0, nous devons utiliser FileProvideret quelque chose appelé ContentUri, sinon vous obtiendrez une erreur ennuyeuse en essayant d'invoquer votre Intent. Voici un exemple de code:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);

Méthode getUriFromPath(Context, String)basée sur la version utilisateur d'Android create FileUri (file://...)ou ContentUri (content://...)et voilà:

public Uri getUriFromPath(Context context, String destination) {
    File file =  new File(destination);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
    } else {
        return Uri.fromFile(file);
    }
}

Une fois que onActivityResultvous pouvez attraper là urioù l'image est enregistrée par la caméra, mais maintenant vous devez détecter la rotation de la caméra, ici nous utiliserons la réponse @Jason Robinson modifiée:

Nous devons d'abord créer ExifInterfacesur la baseUri

@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
    try {
        String path = uri.toString();
        if (path.startsWith("file://")) {
            return new ExifInterface(path);
        }
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (path.startsWith("content://")) {
                InputStream inputStream = context.getContentResolver().openInputStream(uri);
                return new ExifInterface(inputStream);
            }
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Le code ci-dessus peut être simplifié, mais je veux tout montrer. Donc, à partir de ce que FileUrinous pouvons créer ExifInterfacesur la base String path, mais à partir de ContentUrinous ne pouvons pas, Android ne prend pas en charge cela.

Dans ce cas, nous devons utiliser un autre constructeur basé sur InputStream. N'oubliez pas que ce constructeur n'est pas disponible par défaut, vous devez ajouter une bibliothèque supplémentaire:

compile "com.android.support:exifinterface:XX.X.X"

Maintenant, nous pouvons utiliser la getExifInterfaceméthode pour obtenir notre angle:

public float getExifAngle(Context context, Uri uri) {
    try {
        ExifInterface exifInterface = getExifInterface(context, uri);
        if(exifInterface == null) {
            return -1f;
        }

        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90f;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180f;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270f;
            case ExifInterface.ORIENTATION_NORMAL:
                return 0f;
            case ExifInterface.ORIENTATION_UNDEFINED:
                return -1f;
            default:
                return -1f;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
        return -1f;
    }
}

Vous avez maintenant Angle pour faire pivoter correctement votre image :).

Artur Szymański
la source
2
implémentation 'androidx.exifinterface: exifinterface: XXX' Ceci est pour ceux qui utilisent androidx. merci pour votre message
Doongsil
11
// Try this way,hope this will help you to solve your problem...

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center">
        <ImageView
            android:id="@+id/imgFromCameraOrGallery"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:src="@drawable/ic_launcher"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btnCamera"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Camera"/>
        <Button
            android:id="@+id/btnGallery"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_marginLeft="5dp"
            android:layout_height="wrap_content"
            android:text="Gallery"/>

    </LinearLayout>
</LinearLayout>

MainActivity.java

    public class MainActivity extends Activity {

    private ImageView imgFromCameraOrGallery;
    private Button btnCamera;
    private Button btnGallery;

    private String imgPath;
    final private int PICK_IMAGE = 1;
    final private int CAPTURE_IMAGE = 2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imgFromCameraOrGallery = (ImageView) findViewById(R.id.imgFromCameraOrGallery);
        btnCamera = (Button) findViewById(R.id.btnCamera);
        btnGallery = (Button) findViewById(R.id.btnGallery);

        btnCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri());
                startActivityForResult(intent, CAPTURE_IMAGE);
            }
        });

        btnGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, ""), PICK_IMAGE);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == CAPTURE_IMAGE) {
                setCapturedImage(getImagePath());
            } else if (requestCode == PICK_IMAGE) {
                imgFromCameraOrGallery.setImageBitmap(BitmapFactory.decodeFile(getAbsolutePath(data.getData())));
            }
        }

    }

    private String getRightAngleImage(String photoPath) {

        try {
            ExifInterface ei = new ExifInterface(photoPath);
            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            int degree = 0;

            switch (orientation) {
                case ExifInterface.ORIENTATION_NORMAL:
                    degree = 0;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                case ExifInterface.ORIENTATION_UNDEFINED:
                    degree = 0;
                    break;
                default:
                    degree = 90;
            }

            return rotateImage(degree,photoPath);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return photoPath;
    }

    private String rotateImage(int degree, String imagePath){

        if(degree<=0){
            return imagePath;
        }
        try{
            Bitmap b= BitmapFactory.decodeFile(imagePath);

            Matrix matrix = new Matrix();
            if(b.getWidth()>b.getHeight()){
                matrix.setRotate(degree);
                b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
                        matrix, true);
            }

            FileOutputStream fOut = new FileOutputStream(imagePath);
            String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
            String imageType = imageName.substring(imageName.lastIndexOf(".") + 1);

            FileOutputStream out = new FileOutputStream(imagePath);
            if (imageType.equalsIgnoreCase("png")) {
                b.compress(Bitmap.CompressFormat.PNG, 100, out);
            }else if (imageType.equalsIgnoreCase("jpeg")|| imageType.equalsIgnoreCase("jpg")) {
                b.compress(Bitmap.CompressFormat.JPEG, 100, out);
            }
            fOut.flush();
            fOut.close();

            b.recycle();
        }catch (Exception e){
            e.printStackTrace();
        }
        return imagePath;
    }

    private void setCapturedImage(final String imagePath){
        new AsyncTask<Void,Void,String>(){
            @Override
            protected String doInBackground(Void... params) {
                try {
                    return getRightAngleImage(imagePath);
                }catch (Throwable e){
                    e.printStackTrace();
                }
                return imagePath;
            }

            @Override
            protected void onPostExecute(String imagePath) {
                super.onPostExecute(imagePath);
                imgFromCameraOrGallery.setImageBitmap(decodeFile(imagePath));
            }
        }.execute();
    }

    public Bitmap decodeFile(String path) {
        try {
            // Decode deal_image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, o);
            // The new size we want to scale to
            final int REQUIRED_SIZE = 1024;

            // Find the correct scale value. It should be the power of 2.
            int scale = 1;
            while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
                scale *= 2;
            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeFile(path, o2);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

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

    }

    private Uri getUri() {
        String state = Environment.getExternalStorageState();
        if(!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
            return MediaStore.Images.Media.INTERNAL_CONTENT_URI;

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

    public Uri setImageUri() {
        Uri imgUri;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/",getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis() + ".png");
            imgUri = Uri.fromFile(file);
            imgPath = file.getAbsolutePath();
        }else {
            File file = new File(getFilesDir() ,getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis()+ ".png");
            imgUri = Uri.fromFile(file);
            this.imgPath = file.getAbsolutePath();
        }
        return imgUri;
    }

    public String getImagePath() {
        return imgPath;
    }
}
Haresh Chhelana
la source
Solution parfaite Haresh Bhai
Sagar Pithiya
9

Vous pouvez simplement lire l'orientation du capteur de la caméra comme indiqué par Google dans la documentation: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html

SENSOR_ORIENTATION

Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.

Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.

Units: Degrees of clockwise rotation; always a multiple of 90

Range of valid values:
0, 90, 180, 270

This key is available on all devices.

Exemple de code:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
    String cameraId = manager.getCameraIdList()[0];
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}
Stephen Shi
la source
6

Jason Robinson réponse et Sami Eltamawy réponse sont excelent.

Juste une amélioration pour terminer l'approche, vous devez utiliser compat ExifInterface.

com.android.support:exifinterface:${lastLibVersion}

Vous pourrez instancier l'ExifInterface (pior API <24) avec InputStream(de ContentResolver) au lieu des chemins uri en évitant les «exceptions de fichier introuvable»

https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html

Ricard
la source
4

Normalement, il est recommandé de résoudre le problème avec ExifInterface , comme l'a suggéré @Jason Robinson. Si cette approche ne fonctionne pas, vous pouvez essayer de rechercher l' orientation de la dernière image prise ...

private int getImageOrientation(){
    final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            imageColumns, null, null, imageOrderBy);

    if(cursor.moveToFirst()){
        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        cursor.close();
        return orientation;
    } else {
        return 0;
    }
}
Chris Conway
la source
1
Je pense que ce code ne détecte que dans quelle mesure la rotation s'est produite. Maintenant, je suis capable de le faire, mais pas dans la tâche suivante, c'est-à-dire faire pivoter l'image.
Shirish Herwade
Vous avez raison, mais vous n'avez pas demandé la rotation dans ce fil, alors gardons-le propre;) C'est pourquoi j'ai mis ma réponse à votre problème de rotation dans votre autre fil ... J'espère que cela aide, cela fonctionne pour moi: stackoverflow.com/questions/14123809/…
Chris Conway
4

Malheureusement, la réponse @ jason-robinson ci-dessus n'a pas fonctionné pour moi.

Bien que la fonction de rotation fonctionne parfaitement:

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
            true);
}

J'ai dû faire ce qui suit pour obtenir l'orientation car l'orientation Exif était toujours 0

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode,resultCode,data);
    if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
            Uri selectedImage = data.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]));
            }
            InputStream imageStream = getContentResolver().openInputStream(selectedImage);
            Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
            switch(orientation) {
                    case 90:
                            bitmap = rotateImage(chosen_image_bitmap, 90);
                            break;
                    case 180:
                            bitmap = rotateImage(chosen_image_bitmap, 180);
                            break;
                    case 270:
                            bitmap = rotateImage(chosen_image_bitmap, 270);
                            break;
                    default:
                            break;
            }
            imageView.setImageBitmap(bitmap );
rharvey
la source
1
alwasys 0, samsung 7
djdance
2

Mieux vaut essayer de prendre la photo dans une orientation spécifique.

android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"

Pour de meilleurs résultats, donnez une orientation paysage à l'activité de la vue caméra.

Siva
la source
désolé, ça ne marche pas. En fait sur l'onglet, chaque fois après avoir terminé l'exécution de onActivityResult, étrangement onCreate est appelé.
Shirish Herwade,
1
désolé, le problème est le même
Shirish Herwade
2

Si des expériences de quelqu'un avec des problèmes ExifInterfacesur Android 4.4 (KitKat) pour obtenir l'orientation, il pourrait être à cause de mauvais chemin obtenu à partir de l'URI. Voir une solution pour propoer getPathdans la question Stack Overflow Obtenez le chemin réel à partir de l'URI, du nouveau framework d'accès au stockage Android KitKat

peter.bartos
la source
Ce seul commentaire est ce dont j'avais besoin. Mec, merci beaucoup.
Joel Nieman
1

La réponse sélectionnée utilise la méthode la plus courante pour répondre à cette question et à des questions similaires. Cependant, cela ne fonctionne pas avec les caméras avant et arrière de Samsung. Pour ceux qui recherchent une solution qui fonctionne à la fois avec des caméras avant et arrière pour Samsung et d'autres grands fabricants, cette réponse de nvhausid est impressionnante:

https://stackoverflow.com/a/18915443/6080472

Pour ceux qui ne veulent pas cliquer, la magie appropriée consiste à utiliser CameraInfo plutôt que de s'appuyer sur EXIF.

Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);

Code complet dans le lien.

D. Scott
la source
non, mauvaise rotation sous différents angles (smasung s7). Je veux dire la galerie bien sûr
djdance
1

Cela va peut-être de soi, mais n'oubliez pas que vous pouvez gérer certains de ces problèmes de gestion d'image sur votre serveur. J'ai utilisé des réponses comme celles contenues dans ce fil pour gérer l'affichage immédiat de l'image. Cependant, mon application nécessite que les images soient stockées sur le serveur (c'est probablement une exigence courante si vous voulez que l'image persiste lorsque les utilisateurs changent de téléphone).

Les solutions contenues dans de nombreux threads concernant ce sujet ne traitent pas du manque de persistance des données EXIF ​​qui ne survit pas à la compression d'image du bitmap, ce qui signifie que vous devrez faire pivoter l'image chaque fois que votre serveur la charge. Vous pouvez également envoyer les données d'orientation EXIF ​​à votre serveur, puis y faire pivoter l'image si nécessaire.

Il était plus facile pour moi de créer une solution permanente sur un serveur car je n'avais pas à me soucier des chemins de fichiers clandestins d'Android.

Braden Holt
la source
Pouvez-vous le faire pivoter une fois au moment de la capture d'image et l'enregistrer de cette façon afin qu'il ne doive plus jamais être pivoté à nouveau?
jk7
Oui, vous pouvez et c'est en fait le processus que j'ai finalement mis en œuvre. J'avais du mal à obtenir le chemin du fichier à partir de l'image sur le téléphone Android qui me permettrait de le faire. C'est la réponse qui a aidé: stackoverflow.com/a/36714242/5443056
Braden Holt
1

La solution la plus simple à ce problème:

captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                   characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));

J'enregistre l'image au format jpg.

DNB
la source
0

Voici la Xamarin.Androidversion:

De la réponse de @Jason Robinson :

Bitmap rotate(Bitmap bitmap, int angle)
{
    var matrix = new Matrix();
    matrix.PostRotate(angle);

    return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
}

Bitmap rotateIfRequired(Bitmap bitmap, string imagePath)
{
    var ei = new ExifInterface(imagePath);
    var orientation = ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Undefined);

    switch (orientation)
    {
        case (int)Android.Media.Orientation.Rotate90: return rotate(bitmap, 90);
        case (int)Android.Media.Orientation.Rotate180: return rotate(bitmap, 180);
        case (int)Android.Media.Orientation.Rotate270: return rotate(bitmap, 270);
        default: return bitmap;
    }
}

Puis calculateInSampleSizeméthode:

int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH)
{
    float h = options.OutHeight;
    float w = options.OutWidth;
    var inSampleSize = 1;

    if (h > reqH || w > reqW)
    {
        if (reqH == 0) inSampleSize = (int)Math.Floor(w / reqW);
        else if (reqW == 0) inSampleSize = (int)Math.Floor(h / reqH);
        else
        {
            var hRatio = (int)Math.Floor(h / reqH);
            var wRatio = (int)Math.Floor(w / reqW);
            inSampleSize = false ? Math.Max(hRatio, wRatio) : Math.Min(hRatio, wRatio);
        }
    }

    return inSampleSize;
}

De la réponse de @Sami Eltamawy :

Bitmap handleSamplingAndRotationBitmap(string imagePath)
{
    var maxHeight = 1024;
    var maxWidth = 1024;

    var options = new BitmapFactory.Options();
    options.InJustDecodeBounds = true;
    BitmapFactory.DecodeFile(imagePath, options);

    options.InSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);

    options.InJustDecodeBounds = false;

    var bitmap = BitmapFactory.DecodeFile(imagePath, options);

    bitmap = rotateIfRequired(bitmap, imagePath);

    return bitmap;
}
Mehdi Dehghani
la source
0

Si vous utilisez Fresco, vous pouvez utiliser ceci -

final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotate())
.build();

mSimpleDraweeView.setController(
Fresco.newDraweeControllerBuilder()
    .setImageRequest(imageRequest)
    .build());

Cela fait automatiquement pivoter les images en fonction des données Exif.

Source: https://frescolib.org/docs/rotation.html

Ritesh Chandnani
la source
0

Le code ci-dessous a fonctionné avec moi, il a obtenu le bitmap de fileUri et fait la correction de rotation si nécessaire:

    private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
        val bitmap = when {
            Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
                this.contentResolver,
                selectedPhotoUri
            )
            else -> {
                val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
                ImageDecoder.decodeBitmap(source)
            }
        }

        // If the image is rotated, fix it
        return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
            ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(90F) }, true)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(180F) }, true)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(270F) }, true)
            else -> bitmap
        } 
    }
Hasan A Yousef
la source
0

Vous avez une réponse à ce problème sans utiliser ExifInterface . Nous pouvons obtenir la rotation de la caméra, soit la caméra avant ou la caméra arrière, selon ce que vous utilisez, puis lors de la création du bitmap, nous pouvons faire pivoter le bitmap à l'aide de Matrix.postRotate (degré)

public int getRotationDegree() {
    int degree = 0;

    for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            degree = info.orientation;

            return degree;
        }
    }

    return degree;
}

Après avoir calculé la rotation, vous pouvez faire pivoter votre bitmap comme ci-dessous:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

 Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);

Herare bm devrait être votre bitmap.

Si vous souhaitez connaître la rotation de votre caméra avant, remplacez Camera.CameraInfo.CAMERA_FACING_BACK par Camera.CameraInfo.CAMERA_FACING_FRONT ci-dessus.

J'espère que ça aide.

Om Prakash Agrahari
la source
1
Réponse horrible mais j'ai accidentellement voté positivement. Ce code suppose que chaque image de votre galerie est créée avec votre appareil photo. Ce n'est pas le cas
Zun
-1

J'ai créé une fonction d'extension Kotlin qui simplifie l'opération pour les développeurs Kotlin basée sur la réponse de @Jason Robinson. J'espère que ça aide.

fun Bitmap.fixRotation(uri: Uri): Bitmap? {

    val ei = ExifInterface(uri.path)

    val orientation: Int = ei.getAttributeInt(
        ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_UNDEFINED
    )

    return when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage( 90f)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage( 180f)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage( 270f)
        ExifInterface.ORIENTATION_NORMAL -> this
        else -> this
    }
}

fun Bitmap.rotateImage(angle: Float): Bitmap? {
    val matrix = Matrix()
    matrix.postRotate(angle)
    return Bitmap.createBitmap(
        this, 0, 0, width, height,
        matrix, true
    )
}
seyfullah.bilgin
la source
1
génial mais souffre du même problème que toutes les solutions, comme une extension ou une fonction - ne fonctionne pas sur Android 10.
Lior Iluz
-2

Il existe une commande plus simple pour corriger cette erreur.

Ajoutez simplement après votreImageView.setBitmap (bitmap); ce yourImageView.setRotation (90);

Cette mine fixe. J'espère que cela aide !

JG
la source
6
Comme l'OP l'a indiqué, certains appareils ne font pas pivoter l'image, certains la font pivoter de 90 degrés, d'autres de 180, etc. Donc, toujours le faire tourner à 90 serait incorrect dans certains cas.
jk7
-8

cela a fonctionné pour moi

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
zaheer
la source
lol ce que le monstre est-ce. Comment diable sauriez-vous que la photo prise par la caméra est de -90 / 90/0 / ... L'utilisateur peut prendre la photo comme paysage et peu importe ce que vous allez faire pivoter ... lmao
Alex
Dans ce cas, cela a fonctionné pour moi car dans mon cas, l'utilisateur prendra toujours la photo avec le téléphone en position verticale.
Christian Eduardo Galdamez