Perché un'immagine catturata utilizzando l'intento della fotocamera viene ruotata su alcuni dispositivi su Android?


377

Sto acquisendo un'immagine e la sto impostando per la visualizzazione dell'immagine.

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;
    }
}

Ma il problema è che l'immagine su alcuni dispositivi ogni volta che viene ruotata. Ad esempio, su un dispositivo Samsung funziona bene, ma su un Sony Xperia l'immagine viene ruotata di 90 gradi e su Toshiba Thrive (tablet) di 180 gradi.


1
provalo nelle tue attività menifest android: configChanges = "orientamento" android: screenOrientation = "portrait"
Narendra Pal

@nick non funziona, ora l'immagine viene ruotata di 90 gradi anziché di 180 gradi nella scheda
Shirish Herwade,

1
come penso quando usi l'intento interno per gestire l'app della fotocamera, quindi ruota l'immagine. Questo dipende da come tieni il dispositivo per catturare l'immagine. Quindi puoi limitare l'utente a scattare l'immagine in modo particolare significa che l'utente acquisirà sempre l'immagine tenendo il dispositivo in verticale o in orizzontale. Dopodiché puoi cambiarlo in un'angolazione specifica per ottenere l'immagine come vuoi .. O ALTRE OPZIONI, REALIZZA LA TUA APP TELECAMERA.
Narendra Pal,

@nick "puoi limitare l'utente a scattare l'immagine in un modo particolare" significa che è uguale all'impostazione orientamento = "ritratto"? E come "Dopo di che puoi cambiarlo in un angolo specifico per ottenere l'immagine come vuoi" raggiungere? Per favore, puoi dare qualche link utile
Shirish Herwade,

3
Credo che l'intento di acquisizione porti sempre in primo piano l'app della fotocamera predefinita che abbia un orientamento specifico su ciascun dispositivo e, di conseguenza, un orientamento fotografico fisso. Non dipende dal modo in cui l'utente detiene il dispositivo o dall'orientamento della tua attività che ha invocato l'intento.
Alex Cohn,

Risposte:


441

La maggior parte delle fotocamere del telefono sono in orizzontale, il che significa che se scatti la foto in verticale, le foto risultanti verranno ruotate di 90 gradi. In questo caso, il software della fotocamera dovrebbe popolare i dati Exif con l'orientamento in cui la foto deve essere visualizzata.

Si noti che la soluzione seguente dipende dal produttore del software / dispositivo della fotocamera che popola i dati Exif, quindi funzionerà nella maggior parte dei casi, ma non è una soluzione affidabile al 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;
}

Ecco il rotateImagemetodo:

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);
}

1
Dai codici di @JasonRobinson imparo come ottenere l'orientamento effettivo e combinando con questi codici gestisco con successo l'orientamento.
Raditya Kurnianto,

La seconda opzione di exif.getAttributeIntutilizzo ExifInterface.ORIENTATION_UNDEFINEDè quasi la stessa, poiché il secondo parametro è il valore predefinito nel caso in cui la funzione non fornisca il valore.
Darpan,

5
Questo codice è per un'immagine già scritta su disco, giusto? Non ottengo risultati usando questo metodo per bitmap che sta per essere scritta su disco.
Tracia,

4
Restituisce sempre il valore 0. Si prega di dire come ottenere l'orientamento effettivo.
Anurag Srivastava,

3
Sempre 0, hai idea del perché?
Navya Ramesan

186

Grazie alla combinazione di Jason Robinson 's risposta con Felix ' s risposta e riempire le parti mancanti, ecco la soluzione completa finale per questo problema che farà il seguente testo dopo testarlo su Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) e Android 5.0 ( Lollipop ).

passi

  1. Ridimensiona l'immagine se era più grande di 1024x1024.

  2. Ruota l'immagine con l'orientamento corretto solo se ruotava di 90, 180 o 270 gradi.

  3. Ricicla l'immagine ruotata per scopi di memoria.

Ecco la parte di codice:

Chiamare il seguente metodo con l'attuale Contexte l'immagine URIche si desidera correggere

/**
 * 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;
}

Ecco il CalculateInSampleSizemetodo dalla fonte precitata :

/**
  * 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;
}

Quindi arriva il metodo che controllerà l'orientamento attuale dell'immagine per decidere l'angolo di rotazione

 /**
 * 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;
    }
}

Finalmente il metodo di rotazione stesso

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;
}

-Non dimenticare di votare le risposte di quei ragazzi per i loro sforzi e Shirish Herwade che ha posto questa utile domanda.


2
È
perfetto

1
il metodo rotateImageIfRequired () funziona molto bene .. grazie !!
mapo,

5
Non funziona per me. A volte il mio telefono offre ritratti, a volte foto di paesaggi, ma l'orientamento rilevato è sempre di 0 gradi.
Makalele,

@Makalele Questo problema si verifica anche durante lo scatto di foto e il collegamento tramite WhatsApp?
Manoj Perumarath,

Non uso WhatsApp quindi non posso dirlo, ma molto probabilmente sì. Questo perché succede anche nell'app per foto d'archivio (Google Stock Camera).
Makalele,

45

È facile rilevare l'orientamento dell'immagine e sostituire la bitmap usando:

 /**
 * 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;
}

Per evitare i ricordi con immagini grandi, ti consiglio di ridimensionare l'immagine usando:

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;
}

Non è possibile utilizzare ExifInterface per ottenere l'orientamento a causa di un problema con il sistema operativo Android: https://code.google.com/p/android/issues/detail?id=19268

Ed eccolo qui 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;
}

1
qual è il metodo di calcolo
InSampleSize

1
@madhukotagiri qui hai un esempio di implementazione per calcolaInSampleSize
Felix

Grazie amico, sei sicuramente quello! Mi chiedo solo quanto sarà utile il ridimensionamento, se l'operazione viene eseguita solo occasionalmente.
Marino,

4
Parametro Uri selectedImage non utilizzato nel metodo getRotation (...). Come dobbiamo usarlo? Grazie.
Valerybodak,

1
Il parametro 'selectedImage' non sembra essere usato da nessuna parte. Qualche motivo per esserci?
Alex,

20

Una soluzione di linea:

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

O

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

Ciò rileverà automaticamente la rotazione e posizionerà l'immagine con l'orientamento corretto

Picasso è una libreria molto potente per la gestione delle immagini nella tua app che include: Trasformazioni di immagini complesse con un utilizzo minimo della memoria.


1
Soluzione interessante
Bhavik Mehta,

8
Carica semplicemente l'immagine in una vista, non ti dà una bitmap o un file che puoi manipolare o caricare su un server.
flawyte,

4
L'immagine visualizzata è scattata così com'è. Non ruota come richiesto.
sembra

1
@Flawyte puoi farlo caricando il file nella destinazione invece della vista con callback che restituisce bitmap ritagliata / ridimensionata: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Into (target); dove target = new Target () {Sostituisci void pubblico onBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom da) {
voytez

il problema che sto ancora affrontando è che ci vogliono pochi secondi per visualizzare l'immagine
Anu

12

Ho trascorso molto tempo a cercare una soluzione per questo. E finalmente sono riuscito a farlo. Non dimenticare di votare @Jason Robinson perché la mia è basata sulla sua.

Quindi, per prima cosa, dovresti sapere che da Android 7.0 dobbiamo usare FileProvidere qualcosa chiamato ContentUri, altrimenti otterrai un fastidioso errore nel tentativo di invocare il tuo Intent. Questo è un codice di esempio:

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);

Metodo getUriFromPath(Context, String)base sulla versione utente di Android creare FileUri (file://...)o ContentUri (content://...)ed eccolo:

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);
    }
}

Dopo onActivityResultche puoi catturare quello uridove l'immagine viene salvata dalla fotocamera, ma ora devi rilevare la rotazione della fotocamera, qui useremo la risposta modificata @Jason Robinson:

Per prima cosa dobbiamo creare in ExifInterfacebaseUri

@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;
}

Il codice sopra può essere semplificato, ma voglio mostrare tutto. Quindi da FileUrinoi possiamo creare ExifInterfacesulla base String path, ma da ContentUrinon possiamo, Android non lo supporta.

In tal caso, dobbiamo usare altri costruttori basati su InputStream. Ricorda che questo costruttore non è disponibile per impostazione predefinita, devi aggiungere una libreria aggiuntiva:

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

Ora possiamo usare il getExifInterfacemetodo per ottenere il nostro angolo:

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;
    }
}

Ora hai Angolo per ruotare correttamente l'immagine :).


2
implementazione 'androidx.exifinterface: exifinterface: XXX' Questo è per coloro che usano androidx. grazie per la tua pubblicazione
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;
    }
}

Soluzione perfetta Haresh Bhai
Sagar Pithiya,

9

Puoi semplicemente leggere l'orientamento del sensore della fotocamera come indicato da Google nella documentazione: 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.

Codice di esempio:

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)
{
}


4

Normalmente si consiglia di risolvere il problema con ExifInterface , come suggerito da @Jason Robinson. Se questo approccio non funziona, puoi provare a cercare l' Orientamento dell'ultima immagine scattata ...

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;
    }
}

1
Penso che questo codice rilevi solo in quale grado si è verificata la rotazione. Ora sono in grado di farlo, ma incapace nel prossimo compito, cioè di ruotare l'immagine.
Shirish Herwade,

Hai ragione, ma non hai chiesto la rotazione in questo thread, quindi manteniamolo pulito;) Ecco perché ho messo la mia risposta al tuo problema di rotazione nell'altro thread ... Spero che ti aiuti, funziona per io: stackoverflow.com/questions/14123809/…
Chris Conway,

4

Purtroppo, la risposta di @ jason-robinson sopra non ha funzionato per me.

Sebbene la funzione di rotazione funzioni perfettamente:

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);
}

Ho dovuto fare quanto segue per ottenere l'orientamento poiché l'orientamento Exif era sempre 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 );

1
alwasys 0, samsung 7
djdance,

2

Meglio provare a scattare la foto con un orientamento specifico.

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

Per risultati ottimali, fornire orientamento orizzontale nell'attività di ripresa.


scusa, non funziona. Infatti sulla scheda, ogni volta che termina l'esecuzione di onActivityResult, stranamente onCreate viene chiamato.
Shirish Herwade,

1
scusate, il problema è come è
Shirish Herwade,



1

La risposta selezionata utilizza il metodo più comune di risposta a questa e a domande simili. Tuttavia, non funziona con entrambe le fotocamere anteriore e posteriore su Samsung. Per coloro che cercano una soluzione che funzioni su entrambe le fotocamere anteriore e posteriore per Samsung e altri grandi produttori, questa risposta di nvhausid è fantastica:

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

Per coloro che non vogliono fare clic, la magia rilevante è usare CameraInfo piuttosto che fare affidamento su 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);

Codice completo nel collegamento.


no, rotazione errata in diverse angolazioni (smasung s7). Intendo ovviamente la galleria
djdance il

1

Questo forse è ovvio, ma ricorda sempre che puoi gestire alcuni di questi problemi di gestione delle immagini sul tuo server. Ho usato risposte come quelle contenute in questo thread per gestire la visualizzazione immediata dell'immagine. Tuttavia, la mia applicazione richiede che le immagini siano archiviate sul server (questo è probabilmente un requisito comune se si desidera che l'immagine persista mentre gli utenti cambiano telefono).

Le soluzioni contenute in molti dei thread riguardanti questo argomento non discutono della mancanza di persistenza dei dati EXIF ​​che non sopravvivono alla compressione dell'immagine di Bitmap, il che significa che dovrai ruotare l'immagine ogni volta che il server lo carica. In alternativa, è possibile inviare i dati di orientamento EXIF ​​al server e quindi ruotare l'immagine lì, se necessario.

È stato più facile per me creare una soluzione permanente su un server perché non dovevo preoccuparmi dei percorsi dei file clandestini di Android.


Puoi ruotarlo una volta al momento dell'acquisizione dell'immagine e salvarlo in questo modo in modo che non debba mai essere ruotato di nuovo?
jk7,

Sì, puoi e questo è il processo che alla fine ho implementato. Ho avuto problemi a ottenere il percorso del file dall'immagine sul telefono Android che mi avrebbe permesso di farlo. Questa è la risposta che ha aiutato: stackoverflow.com/a/36714242/5443056
Braden Holt,

1

La soluzione più semplice per questo problema:

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

Sto salvando l'immagine in formato jpg.


0

Ecco la Xamarin.Androidversione:

Dalla risposta di @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;
    }
}

Quindi calculateInSampleSizemetodo:

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;
}

Dalla risposta di @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;
}

0

Se stai usando Fresco, puoi usare questo -

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

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

Questo ruota automaticamente le immagini in base ai dati Exif.

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


0

Sotto il codice ha funzionato con me, ha ottenuto la bitmap da fileUri e, se necessario, esegue la correzione della rotazione:

    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
        } 
    }

0

Hai una risposta per questo problema senza usare ExifInterface . Siamo in grado di ottenere la rotazione della fotocamera anteriore o posteriore, a seconda di quale si sta utilizzando, mentre durante la creazione della bitmap è possibile ruotare la bitmap utilizzando Matrix.postRotate (gradi)

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;
}

Dopo aver calcolato la rotazione, puoi ruotare la bitmap come di seguito:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm dovrebbe essere la tua bitmap.

Se vuoi conoscere la rotazione della tua fotocamera frontale, cambia semplicemente Camera.CameraInfo.CAMERA_FACING_BACK in Camera.CameraInfo.CAMERA_FACING_FRONT sopra.

Spero che questo possa essere d'aiuto.


1
Risposta orribile ma ho accidentalmente votato. Questo codice presuppone che tutte le immagini della tua galleria siano realizzate con la tua fotocamera. Questo non è il caso
Zun

-1

Ho creato una funzione di estensione di Kotlin che semplifica l'operazione per gli sviluppatori di Kotlin sulla base della risposta di @Jason Robinson. Spero possa essere d'aiuto.

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
    )
}

1
fantastico ma soffre dello stesso problema di tutte le soluzioni, come estensione o funzione - non funziona su Android 10.
Lior Iluz

-2

Esiste un comando più semplice per correggere questo errore.

Basta semplicemente aggiungere dopo yourImageView.setBitmap (bitmap); this yourImageView.setRotation (90);

Questa miniera fissa. Spero che sia d'aiuto !


6
Come affermato dall'OP, alcuni dispositivi non ruotano l'immagine, altri la ruotano di 90 gradi, circa 180, ecc. Quindi ruotarlo sempre di 90 sarebbe errato in alcuni casi.
jk7,

-8

questo ha funzionato per me

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);

lol quello che è il mostro questo. Come diavolo sapresti che la foto scattata dalla fotocamera è -90 / 90/0 / ... L'utente potrebbe scattare una foto come paesaggio e non importa cosa ruoterai ... Lmao
Alex

In questo caso ha funzionato per me poiché nel mio caso l'utente scatta sempre la foto con il telefono in verticale.
Christian Eduardo Galdamez,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.