Android: mapy bitowe ładowane z galerii są obracane w ImageView

138

Kiedy ładuję obraz z galerii multimediów do mapy bitowej, wszystko działa dobrze, z wyjątkiem tego, że zdjęcia zrobione aparatem, trzymając telefon w pionie, są obracane tak, że zawsze otrzymuję obraz poziomy, nawet jeśli wygląda pionowo w Galeria. Dlaczego tak jest i jak mogę go poprawnie załadować?

Manuel
źródło

Odpowiedzi:

40

Czy spojrzałeś na dane EXIF ​​obrazów? Może znać orientację aparatu w momencie wykonania zdjęcia.

James
źródło
2
Masz rację, to było oczywiście rozwiązanie. Zamierzam zamieścić swój kod jako przykład w osobnej odpowiedzi później, ale oznaczam ten kod jako zaakceptowany, ponieważ zaprowadził mnie na właściwą ścieżkę.
Manuel,
182

A więc jako przykład ...

Najpierw musisz utworzyć ExifInterface:

ExifInterface exif = new ExifInterface(filename);

Następnie możesz pobrać orientację obrazu:

orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

Oto, co oznaczają wartości orientacji: http://sylvana.net/jpegcrop/exif_orientation.html

Zatem najważniejsze wartości to 3, 6 i 8. Jeśli na przykład orientacja to ExifInterface.ORIENTATION_ROTATE_90(czyli 6), możesz obrócić obraz w następujący sposób:

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

To tylko szybki przykład. Jestem pewien, że istnieją inne sposoby wykonywania rzeczywistej rotacji. Ale znajdziesz je również w StackOverflow.

Manuel
źródło
5
Oto wszystkie wartości rotacji dla różnych orientacji: 3: 180, 6: 90, 8: 270
Nazwa wyświetlana
103
Nie używaj liczb magicznych, gdy możesz użyć nazwanych stałych: ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.
d60402
29
Uważaj na OutOfMemoryErrortakie podejście, ponieważ jednocześnie przechowujesz w pamięci dwie mapy bitowe.
Alex Bonel,
Kolejny kompletny przykład ... stackoverflow.com/questions/14066038/…
CGR
64

To jest pełne rozwiązanie (znalezione w przykładzie Hackbook z Facebook SDK). Ma tę zaletę, że nie potrzebuje dostępu do samego pliku. Jest to niezwykle przydatne, jeśli ładujesz obraz z programu do rozpoznawania treści (np. Jeśli Twoja aplikacja reaguje na zamiar udostępniania zdjęcia).

public static int getOrientation(Context context, Uri photoUri) {
    /* it's on the external media. */
    Cursor cursor = context.getContentResolver().query(photoUri,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);

    if (cursor.getCount() != 1) {
        return -1;
    }

    cursor.moveToFirst();
    return cursor.getInt(0);
}

Następnie możesz uzyskać obróconą mapę bitową w następujący sposób. Ten kod skaluje również obraz (niestety niestety) do MAX_IMAGE_DIMENSION. W przeciwnym razie może zabraknąć pamięci.

public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
    InputStream is = context.getContentResolver().openInputStream(photoUri);
    BitmapFactory.Options dbo = new BitmapFactory.Options();
    dbo.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, dbo);
    is.close();

    int rotatedWidth, rotatedHeight;
    int orientation = getOrientation(context, photoUri);

    if (orientation == 90 || orientation == 270) {
        rotatedWidth = dbo.outHeight;
        rotatedHeight = dbo.outWidth;
    } else {
        rotatedWidth = dbo.outWidth;
        rotatedHeight = dbo.outHeight;
    }

    Bitmap srcBitmap;
    is = context.getContentResolver().openInputStream(photoUri);
    if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
        float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
        float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
        float maxRatio = Math.max(widthRatio, heightRatio);

        // Create the bitmap from file
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = (int) maxRatio;
        srcBitmap = BitmapFactory.decodeStream(is, null, options);
    } else {
        srcBitmap = BitmapFactory.decodeStream(is);
    }
    is.close();

    /*
     * if the orientation is not 0 (or -1, which means we don't know), we
     * have to do a rotation.
     */
    if (orientation > 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(orientation);

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

    return srcBitmap;
}
Timmmm
źródło
1
co to MAX_IMAGE_DIMENDION oznacza?
Sazzad Hissain Khan
3
Jest to maksymalna szerokość lub wysokość otrzymanego obrazu. Powiedzmy, że potrzebujesz tylko obrazu 512x512, jeśli otworzysz obraz o rozdzielczości 24 megapikseli, znacznie wydajniejsze jest otwarcie go już podpróbkowanego niż otwarcie całości, a następnie skalowanie w dół - to i tak prawdopodobnie wyczerpałoby całą pamięć.
Timmmm
W moich programach uznałem za przydatne zdefiniowanie zmiennej Bitmap w działaniu / fragmencie jako prywatnej statycznej i ustawienie jej na null w funkcjach. Miał wtedy mniej problemów z pamięcią.
Gunnar Bernstein
Lepiej jest zamienić MAX_IMAGE_DIMENDION na MAX_IMAGE_WIDTH i MAX_IMAGE_HEIGHT
fnc12
Zaoszczędziłem mnóstwo czasu :) Wielkie dzięki. Dla tych, uzyskanie zerowej kursor, można spróbować ExifInterface exif = new ExifInterface(photoUri.getPath());, a następnie exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1), aby uzyskać orientację (np ORIENTATION_ROTATE_90, ORIENTATION_ROTATE_180)
Atul
60

Rozwiązałem to w moim przypadku za pomocą tego kodu, korzystając z pomocy tego postu:

            Bitmap myBitmap = getBitmap(imgFile.getAbsolutePath());

            try {
                ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
                Log.d("EXIF", "Exif: " + orientation);
                Matrix matrix = new Matrix();
                if (orientation == 6) {
                    matrix.postRotate(90);
                }
                else if (orientation == 3) {
                    matrix.postRotate(180);
                }
                else if (orientation == 8) {
                    matrix.postRotate(270);
                }
                myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true); // rotating bitmap
            }
            catch (Exception e) {

            }
            ImageView img = (ImageView) findViewById(R.id.imgTakingPic);
            img.setImageBitmap(myBitmap);

Mam nadzieję, że to oszczędza komuś czas!

Teo Inke
źródło
sugestie dotyczące edycji: czy nie ma odpowiednich nazwanych stałych dla orientacji 6, 3, 8? czy nie moglibyśmy pominąć nowej mapy bitowej, jeśli nie jest wymagana rotacja?
Cee McSharpface
Jak @ d60402 wcześniej powiedział w komentarzu, możesz użyć nazwanych stałych: ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.
Adrian
42

Użyj narzędzia, aby wykonać ciężkie podnoszenie.

9re stworzyło proste narzędzie do obsługi ciężkich zadań związanych z przetwarzaniem danych EXIF ​​i obracaniem obrazów do ich prawidłowej orientacji.

Możesz znaleźć kod narzędzia tutaj: https://gist.github.com/9re/1990019

Po prostu pobierz to, dodaj do srckatalogu swojego projektu i użyj, ExifUtil.rotateBitmap()aby uzyskać prawidłową orientację, na przykład:

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

Bitmap orientedBitmap = ExifUtil.rotateBitmap(imagePath, myBitmap);
Joshua Pinter
źródło
2
Pracuje dla mnie ! Właśnie zmieniłem rozmiar mapy bitowej na format HD przed przekazaniem jej do ExifUtil.rotateBitmap (), aby uniknąć OutOfMemoryError w ten sposób: Bitmap resized = Bitmap.createScaledBitmap (myBitmap, 720, 1280, true); photo = ExifUtil.rotateBitmap (picturePath, zmieniony rozmiar);
Phil
@Phil Niezły dodatek. Nie wpadłem na to (używam starszych, bardziej gównianych urządzeń z Androidem), ale dobrze to wiedzieć.
Joshua Pinter
3
jesteś bohaterem mój przyjacielu :)
klutch
@klutch Właśnie zrobiłeś mój dzień. :) Szczerze mówiąc, 9re made napisał kod narzędzia, więc jest prawdziwym bohaterem.
Joshua Pinter
1
@SreekanthKarumanaghat Świetne pytanie! Prawdopodobnie wiedziałem, dlaczego to ma sens, kiedy byłem w tym głęboko zaangażowany, ale teraz wydaje mi się to również zbędne. Być może spędzam zbyt dużo czasu w React Native.
Joshua Pinter
8

to dlatego, że galeria poprawnie wyświetla obrócone obrazy, ale nie wygląda na ImageView tutaj:

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

i potrzebujesz tego:

private static int exifToDegrees(int exifOrientation) {        
    if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; } 
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {  return 180; } 
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {  return 270; }            
    return 0;    
} 
farhad.kargaran
źródło
5

Udało mi się to po wielu próbach dzięki wpisowi, którego już nie mogę znaleźć :-(

Exif wydaje się działać zawsze, trudność polegała na uzyskaniu ścieżki do pliku. Kod, który znalazłem, różni się między API starszymi niż 4.4 i późniejszymi niż 4.4. Zasadniczo identyfikator URI obrazu dla wersji 4.4+ zawiera „com.android.providers”. W przypadku tego typu identyfikatora URI kod używa DocumentsContract, aby uzyskać identyfikator obrazu, a następnie uruchamia zapytanie przy użyciu ContentResolver, podczas gdy w przypadku starszego zestawu SDK kod przechodzi bezpośrednio do zapytania o identyfikator URI za pomocą ContentResolver.

Oto kod (przepraszam, nie mogę wymienić tego, kto go opublikował):

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

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

    return res;
}

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

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

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

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

    cursor.close();
    return filePath;
}

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

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

            return getPathForPreV19(context, contentUri);
        }
}
Georges
źródło
Wielkie dzięki za Ciebie. Po wielu godzinach pracy z kursorami i exifami uratowało to majowy dzień. Jak powiedziałeś, w rzeczywistości exif ma prawdziwe i wiarygodne dane zamiast powrotu kursora. Po prostu daj mu właściwą ścieżkę, niż działa.
asozcan
3

Możesz po prostu odczytać ścieżkę z karty SD i wykonać następujący kod ... po obróceniu zastąpi istniejące zdjęcie ..

Nie: Exif nie działa na większości urządzeń, podaje błędne dane, więc dobrze jest zakodować obracanie przed zapisaniem w dowolnym stopniu, wystarczy zmienić wartość kąta w postRotate na dowolną.

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

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

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

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

}
Mayank Saini
źródło
2
Ma to na celu obrócenie, ale nie wiemy, czy obraz wymaga obrotu.
MSaudi
3

Kod Kotlin:

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

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

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

    val rotatedBitmap = Bitmap.createBitmap(bitmap, 0,0 , bitmap.width, bitmap.height, matrix, true)
    bitmap.recycle()
    iv_capture.setImageBitmap(rotatedBitmap)
}
Rahul Khatri
źródło
2

Poprawiłem odpowiedź Teo Inke. Nie obraca już obrazu, chyba że jest to rzeczywiście konieczne. Jest też łatwiejszy do odczytania i powinien działać szybciej.

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

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

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

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

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

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

// TODO: Use Result Here
xxx.setBitmap(bitmap);
user2820531
źródło
2

Pierwszą rzeczą, której potrzebujesz, jest prawdziwa ścieżka do pliku.Jeśli masz ją świetnie, jeśli używasz URI, użyj tej metody, aby uzyskać prawdziwą ścieżkę:

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

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

wyodrębnij swoją mapę bitową, na przykład:

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

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

możesz zamiast tego użyć decodeFile (), jeśli chcesz.

Teraz, gdy masz już mapę bitową i prawdziwą ścieżkę, uzyskaj orientację obrazu:

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

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

        return orientation;
    }

i na koniec obróć go do właściwej pozycji, na przykład:

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

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

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

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

        return bitmap;
    }

To wszystko, masz teraz mapę bitową obróconą do właściwej pozycji.

Twoje zdrowie.

Gal Rom
źródło
1

To działa, ale prawdopodobnie nie jest to najlepszy sposób, ale może komuś pomóc.

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

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

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

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

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

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

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

może to pomoże (obróć o 90 stopni) (u mnie to zadziałało)

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

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

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

    }
tzahibs
źródło
1

Po otwarciu kursor powinien zostać zamknięty.

Oto przykład.

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

    cursor.moveToFirst();
    orientation = cursor.getInt(0);
    cursor.close(); // ADD THIS LINE
   return orientation;
}
GuillaumeAgis
źródło
1

Stopniłem odpowiedź @Timmmm i @Manuel. Jeśli zastosujesz to rozwiązanie, nie otrzymasz wyjątku braku pamięci.

Ta metoda pobiera orientację obrazu:

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

public static int getOrientation(Uri photoUri) throws IOException {

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

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

    return orientation;
}

Dlatego należy użyć tej metody do zmiany rozmiaru obrazu przed załadowaniem go do pamięci. W ten sposób nie otrzymasz wyjątku pamięci.

public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
    InputStream is = context.getContentResolver().openInputStream(photoUri);
    BitmapFactory.Options dbo = new BitmapFactory.Options();
    dbo.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, dbo);
    is.close();

    int rotatedWidth, rotatedHeight;
    int orientation = getOrientation(photoUri);

    if (orientation == 90 || orientation == 270) {
        rotatedWidth = dbo.outHeight;
        rotatedHeight = dbo.outWidth;
    } else {
        rotatedWidth = dbo.outWidth;
        rotatedHeight = dbo.outHeight;
    }

    Bitmap srcBitmap;
    is = context.getContentResolver().openInputStream(photoUri);
    if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
        float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
        float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
        float maxRatio = Math.max(widthRatio, heightRatio);

        // Create the bitmap from file
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = (int) maxRatio;
        srcBitmap = BitmapFactory.decodeStream(is, null, options);
    } else {
        srcBitmap = BitmapFactory.decodeStream(is);
    }
    is.close();

    // if the orientation is not 0, we have to do a rotation.
    if (orientation > 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(orientation);

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

    return srcBitmap;
}

U mnie to działa idealnie. Mam nadzieję, że to pomoże komuś innemu

Antonio
źródło
0

Ulepszenie powyższego rozwiązania przez Timmmm w celu dodania dodatkowego skalowania na końcu, aby upewnić się, że obraz mieści się w granicach:

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

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

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

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

        // Re-scale the bitmap if necessary
        sourceWidth = bitmap.getWidth();
        sourceHeight = bitmap.getHeight();
        if (sourceWidth != targetWidth || sourceHeight != targetHeight) {
            float widthRatio = (float)sourceWidth / (float)targetWidth;
            float heightRatio = (float)sourceHeight / (float)targetHeight;
            float maxRatio = Math.max(widthRatio, heightRatio);
            sourceWidth = (int)((float)sourceWidth / maxRatio);
            sourceHeight = (int)((float)sourceHeight / maxRatio);
            bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
        }
    } catch (Exception e) {
    }
    return bitmap;
}
Meanman
źródło
0

Użyj poniższego kodu, aby poprawnie obrócić obraz:

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

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

        Matrix matrix = new Matrix();

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

        // Rotate the bitmap
        resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }
    catch (Exception exception)
    {
        Logger.d("Could not rotate the image");
    }
    return resultBitmap;
}
NightBits
źródło
Możesz scalić wszystkie warunki if, aby uzyskać mniejszy kod.
Chandranshu
0

Poniższe metody skalują ORAZ obracają mapę bitową zgodnie z orientacją:

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

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

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

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

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

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

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

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

Przykład:

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

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

Rozwiązałem problem, stosując następujące obejście. Zwróć uwagę, że skaluję również obraz, co było konieczne, aby uniknąć wyjątków OutOfMemoryExceptions.

Pamiętaj, że to rozwiązanie nie będzie działać poprawnie w przypadku obrazów portretowych lub odwróconych (dziękuję Timmmm za uwagę). Powyższe rozwiązanie Timmmm może być lepszym wyborem, jeśli jest to wymagane, a także wygląda bardziej elegancko: https://stackoverflow.com/a/8914291/449918

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


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

Bitmap resultBmp = null;

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

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

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

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

// resultBmp now contains the scaled and rotated image

Twoje zdrowie

Jaskółka oknówka
źródło
To nie będzie działać poprawnie. A co z portretami? Obrazy do góry nogami? Korzystanie z danych exif jest znacznie lepsze.
Timmmm
Działa poprawnie w jednej z moich aplikacji, ale oczywiście nie testowałem wszystkich typów scenariuszy. @ Timmmm, czy mógłbyś bardziej szczegółowo określić, w jakich scenariuszach to nie działa? Jestem również dość zdziwiony tym, że głosowałeś na mój post. Wydaje się, że jest to dość ostra odpowiedź na uczciwą próbę podzielenia się potencjalnym rozwiązaniem.
Martin
Nie chciałem być szorstki; Przepraszam! Po prostu nie chciałem, aby ktokolwiek kopiował Twoje rozwiązanie, mając nadzieję, że zadziała. Jak powiedziałem, nie będzie działać w przypadku zdjęć portretowych lub odwróconych. Jako odpowiedź dodam poprawne rozwiązanie.
Timmmm
Widzę. Dodam komentarz podkreślający powyższe rozwiązanie jako preferowane.
Martin