Dlaczego obraz zarejestrowany przy użyciu aparatu jest obracany na niektórych urządzeniach z Androidem?

376

Przechwytuję obraz i ustawiam go do widoku obrazu.

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

Problem w tym, że obraz na niektórych urządzeniach za każdym razem, gdy się obraca. Na przykład na urządzeniu Samsung działa dobrze, ale na Sony Xperia obraz jest obracany o 90 stopni, a na Toshiba Thrive (tablet) o 180 stopni.

Shirish Herwade
źródło
1
spróbuj tego w tobie menifest aktywności android: configChanges = "orientacja" android: screenOrientation = "portret"
Narendra Pal
@nick to nie działa, teraz obraz jest obracany o 90 stopni zamiast 180 stopni na zakładce
Shirish Herwade 28.12.
1
jak myślę, kiedy używasz wewnętrznej intencji do czynienia z aplikacją aparatu, to obraca obraz. Zależy to od tego, jak trzymasz urządzenie, aby przechwycić obraz. Możesz więc ograniczyć użytkownika do robienia zdjęć w określony sposób, co oznacza, że ​​zawsze będzie przechwytywał obraz, trzymając urządzenie pionowo lub poziomo. Następnie możesz zmienić go pod określonym kątem, aby uzyskać obraz tak, jak chcesz. LUB KOLEJNĄ OPCJĘ, ZRÓB WŁASNĄ APLIKACJĘ KAMERY.
Narendra Pal
@nick „możesz ograniczyć użytkownika do robienia zdjęć w określony sposób” oznacza, czy to samo, co ustawienie orientacji = „potrait”? I jak osiągnąć „Po tym można zmienić go pod określonym kątem, aby uzyskać obraz, jak chcesz”? Proszę podać kilka użytecznych linków
Shirish Herwade,
3
Wierzę, że cel przechwytywania zawsze wywołuje domyślną aplikację aparatu, która ma określoną orientację na każdym urządzeniu, aw konsekwencji - stałą orientację zdjęcia. Nie zależy to od sposobu trzymania urządzenia przez użytkownika ani od orientacji Twojej aktywności, która wywołała zamiar.
Alex Cohn

Odpowiedzi:

440

Większość aparatów telefonicznych jest ustawiona poziomo, co oznacza, że ​​jeśli zrobisz zdjęcie portretowe, powstałe zdjęcia zostaną obrócone o 90 stopni. W takim przypadku oprogramowanie aparatu powinno wypełnić dane Exif orientacją, w której zdjęcie powinno zostać wyświetlone.

Zwróć uwagę, że poniższe rozwiązanie zależy od producenta oprogramowania / urządzenia wypełniającego dane Exif, więc będzie działać w większości przypadków, ale nie jest to rozwiązanie w 100% niezawodne.

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

Oto rotateImagemetoda:

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
źródło
1
Z kodu @JasonRobinson uczę się, jak uzyskać rzeczywistą orientację, a dzięki połączeniu z tym kodem z powodzeniem zarządzam orientacją.
Raditya Kurnianto
Druga opcja exif.getAttributeIntużycia ExifInterface.ORIENTATION_UNDEFINEDjest prawie taka sama, ponieważ drugi parametr jest wartością domyślną na wypadek, gdyby funkcja nie podała tej wartości.
Darpan
5
Ten kod dotyczy obrazu już zapisanego na dysk, prawda? Nie otrzymuję wyników przy użyciu tej metody bitmapy, która ma zostać zapisana na dysku.
Tracki
4
Zawsze zwraca mi wartość 0. Powiedz, jak uzyskać rzeczywistą orientację.
Anurag Srivastava
3
Otrzymywanie 0 zawsze, jakiś pomysł dlaczego?
Navya Ramesan
186

Łącząc Jason Robinson „s odpowiedź z Felix ” s odpowiedź i wypełnienie brakujących części, tutaj jest ostateczna kompletne rozwiązanie tego problemu , który będzie wykonać następujące czynności po przetestowaniu go na Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) i Android 5.0 ( Lollipop ).

Kroki

  1. Zmniejsz obraz, jeśli był większy niż 1024x1024.

  2. Obróć obraz do właściwej orientacji tylko wtedy, gdy został obrócony o 90, 180 lub 270 stopni.

  3. Przetwarzaj obrócony obraz do celów pamięci.

Oto część kodu:

Wywołaj następującą metodę z bieżącym Contexti obrazem URI, który chcesz naprawić

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

Oto CalculateInSampleSizemetoda z wcześniej wspomnianego źródła :

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

Następnie pojawia się metoda, która sprawdzi bieżącą orientację obrazu w celu ustalenia kąta obrotu

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

Wreszcie sama metoda rotacji

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

-Nie zapomnij zagłosować na odpowiedzi tych facetów za ich wysiłki i Shirish Herwade, który zadał to pomocne pytanie.

Sami Eltamawy
źródło
2
To Woking dla mnie idealnie.
Dziękuję
1
metoda rotateImageIfRequired () działa bardzo dobrze .. dzięki !!
mapo
5
Nie działa dla mnie. Czasami mój telefon daje portrety, czasem zdjęcia w orientacji poziomej, ale orientacja zawsze wynosi 0 stopni.
Makalele
@Makalele Czy ten problem występuje również podczas robienia zdjęć i dołączania za pomocą WhatsApp?
Manoj Perumarath
Nie używam WhatsApp, więc nie mogę powiedzieć, ale najprawdopodobniej tak. Dzieje się tak, ponieważ dzieje się tak nawet w aplikacji do zdjęć stockowych (Google Stock Camera).
Makalele
45

Łatwo jest wykryć orientację obrazu i zastąpić mapę bitową za pomocą:

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

Aby uniknąć braku pamięci w przypadku dużych obrazów, zalecamy przeskalowanie obrazu za pomocą:

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

Nie można użyć ExifInterface do uzyskania orientacji, ponieważ problem z systemem operacyjnym Android: https://code.google.com/p/android/issues/detail?id=19268

I oto jest 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
źródło
1
co to jest metoda
replaceInSampleSize
1
@madhukotagiri tutaj masz przykład implementacji dla replaceInSampleSize
Felix
Dzięki stary, zdecydowanie jesteś tym jedynym! Zastanawiam się tylko, jak bardzo zmiana rozmiaru będzie przydatna, jeśli operacja będzie wykonywana tylko okazjonalnie.
Marino
4
Parametr Uri selectedImage nieużywany w metodzie getRotation (...). Jak tego potrzebujemy? Dziękuję Ci.
valerybodak
1
Wydaje się, że parametr „selectedImage” nie jest nigdzie używany. Czy jest jakiś powód, aby tam być?
Alex,
20

Rozwiązanie jednoliniowe:

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

Lub

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

Spowoduje to automatyczne wykrycie obrotu i ustawienie obrazu we właściwej orientacji

Picasso to bardzo potężna biblioteka do obsługi obrazów w Twojej aplikacji obejmuje: Złożone transformacje obrazów przy minimalnym zużyciu pamięci.

Voytez
źródło
1
Ciekawe rozwiązanie
Bhavik Mehta
8
Po prostu ładuje obraz do widoku, nie daje bitmapy ani pliku, którym można manipulować lub przesłać na serwer.
flawyte
4
Jego wyświetlany obraz kliknął w tym momencie. Nie obraca się zgodnie z wymaganiami.
wydają się
1
@Flawyte możesz to zrobić, ładując plik do celu zamiast widoku z wywołaniem zwrotnym, który zwraca przyciętą / zmienioną bitmapę: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Do (cel); gdzie target = nowy Target () {Zastąp publiczną nieważność onBitmapLoaded (Bitmap Bitmap, Picasso.LoadedFrom from) {
voytez
Problem, z którym wciąż mam do czynienia, polega na tym, że wyświetlenie obrazu zajmuje kilka sekund
Anu
12

Spędziłem dużo czasu na szukaniu rozwiązania tego problemu. I w końcu udało się to zrobić. Nie zapomnij głosować na odpowiedź Jason Robinson, ponieważ moja jest oparta na jego.

Po pierwsze, powinieneś wiedzieć, że od Androida 7.0 musimy używać FileProvideri coś o nazwie ContentUri, w przeciwnym razie pojawi się irytujący błąd przy próbie wywołania twojego Intent. To jest przykładowy kod:

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

Metoda getUriFromPath(Context, String)oparta na utworzonej przez użytkownika wersji Androida FileUri (file://...)lub ContentUri (content://...)istnieje:

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

Po onActivityResulttym, jak to złapieszuri wykryciu, obraz jest zapisywany przez kamerę, ale teraz musisz wykryć obrót kamery, tutaj użyjemy zmodyfikowanej odpowiedzi @Jason Robinson:

Najpierw musimy stworzyć ExifInterfacena podstawieUri

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

Powyższy kod można uprościć, ale chcę pokazać wszystko. Dlatego FileUrimożemy tworzyć ExifInterfacena podstawie String path, ale ContentUrinie możemy, Android nie obsługuje tego.

W takim przypadku musimy użyć innego konstruktora opartego na InputStream. Pamiętaj, że ten konstruktor nie jest domyślnie dostępny, musisz dodać dodatkową bibliotekę:

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

Teraz możemy użyć getExifInterfacemetody, aby uzyskać nasz kąt:

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

Teraz masz Kąt, aby poprawnie obrócić obraz :).

Artur Szymański
źródło
2
implementacja „androidx.exifinterface: exifinterface: XXX” To jest dla tych, którzy używają androidx. dziękuję za wysłanie
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
źródło
Idealne rozwiązanie Haresh Bhai
Sagar Pithiya
9

Możesz po prostu odczytać orientację czujnika aparatu, jak wskazano w Google w dokumentacji: 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.

Przykładowy kod:

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
źródło
6

Jason Robinson za odpowiedź i Sami Eltamawy odpowiedź są doskonałe.

Tylko ulepszenie, aby ukończyć podejście, powinieneś użyć kompatybilnego ExifInterface.

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

Będziesz mógł utworzyć instancję ExifInterface (API pior <24) przy pomocy InputStream(zContentResolver ) zamiast ścieżek uri, unikając „wyjątków Nie znaleziono pliku”

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

Ricard
źródło
4

Zwykle zaleca się rozwiązanie problemu z interfejsem Exif , jak sugerował @Jason Robinson. Jeśli to podejście nie działa, możesz spróbować sprawdzić orientację ostatnio wykonanego zdjęcia ...

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
źródło
1
Myślę, że ten kod wykrywa tylko stopień rotacji. Teraz jestem w stanie to zrobić, ale nie mogę wykonać następnego zadania, tj. Obrócić obrazu.
Shirish Herwade,
Masz rację, ale nie poprosiłeś o obrót w tym wątku, więc utrzymajmy go w czystości;) Dlatego moją odpowiedź na twój problem z obrotem umieściłem w twoim drugim wątku ... Mam nadzieję, że to pomaga, działa dla ja: stackoverflow.com/questions/14123809/…
Chris Conway
4

Niestety powyższa odpowiedź @ jason-robinson nie zadziałała dla mnie.

Chociaż funkcja obracania działa idealnie:

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

Musiałem wykonać następujące czynności, aby uzyskać orientację, ponieważ orientacja Exif zawsze wynosiła 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
źródło
1
alwasys 0, samsung 7
djdance
2

Lepiej spróbuj zrobić zdjęcie w określonej orientacji.

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

W celu uzyskania najlepszych rezultatów podaj orientację poziomą podczas aktywności kamery.

Siva
źródło
przepraszam, to nie działa W rzeczywistości na karcie, za każdym razem po zakończeniu wykonywania onActivityResult, dziwnie onCreate zostaje wywołany.
Shirish Herwade,
1
przepraszam, problem jest taki, jaki jest
Shirish Herwade
1

Wybrana odpowiedź wykorzystuje najczęściej stosowaną metodę odpowiedzi na to i podobne pytania. Jednak nie działa zarówno z aparatami przednimi, jak i tylnymi w Samsungu. Dla tych, którzy szukają rozwiązania, które działa zarówno na przednich, jak i tylnych aparatach dla Samsunga i innych głównych producentów, odpowiedź nvhausid jest niesamowita:

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

Dla tych, którzy nie chcą klikać, odpowiednią magią jest użycie CameraInfo zamiast polegania na 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);

Pełny kod w linku.

D. Scott
źródło
nie, niewłaściwy obrót pod różnymi kątami (smasung s7). Oczywiście mam na myśli galerię
djdance,
1

To może być oczywiste, ale zawsze pamiętaj, że możesz poradzić sobie z niektórymi z tych problemów z obsługą obrazów na serwerze. Użyłem odpowiedzi takich jak te zawarte w tym wątku, aby obsłużyć natychmiastowe wyświetlanie obrazu. Jednak moja aplikacja wymaga przechowywania obrazów na serwerze (jest to prawdopodobnie powszechny wymóg, jeśli chcesz, aby obraz trwał, gdy użytkownicy przełączają telefony).

Rozwiązania zawarte w wielu wątkach dotyczących tego tematu nie omawiają braku trwałości danych EXIF, które nie przetrwają kompresji obrazu bitmapy, co oznacza, że ​​będziesz musiał obrócić obraz za każdym razem, gdy serwer go załaduje. Alternatywnie możesz wysłać dane orientacyjne EXIF ​​na serwer, a następnie obrócić tam obraz, jeśli to konieczne.

Łatwiej było mi stworzyć trwałe rozwiązanie na serwerze, ponieważ nie musiałem się martwić o tajne ścieżki plików Androida.

Braden Holt
źródło
Czy możesz go obrócić raz w trakcie robienia zdjęć i zapisać w ten sposób, aby nigdy więcej nie musiał być obracany?
jk7
Tak, możesz i właśnie taki proces ostatecznie wdrożyłem. Miałem problem z uzyskaniem ścieżki pliku z obrazu na telefonie z Androidem, który by mi na to pozwolił. To była odpowiedź, która pomogła: stackoverflow.com/a/36714242/5443056
Braden Holt
1

Najprostsze rozwiązanie tego problemu:

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

Zapisuję obraz w formacie jpg.

DNB
źródło
0

Oto Xamarin.Androidwersja:

Od @Jason Robinsona odpowiedź :

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

Następnie calculateInSampleSizemetoda:

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

Od @Sami Eltamawy za odpowiedź :

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
źródło
0

Jeśli używasz Fresco, możesz użyć tego -

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

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

To automatycznie obraca obrazy na podstawie danych Exif.

Źródło: https://frescolib.org/docs/rotation.html

Ritesh Chandnani
źródło
0

Poniższy kod działał ze mną, pobrałem bitmapę z plikuUri i w razie potrzeby dokonałem naprawy rotacji:

    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
źródło
0

Uzyskaj odpowiedź na ten problem bez użycia ExifInterface . Możemy uzyskać obrót kamery przednią lub tylną, niezależnie od tego, którego używasz, a następnie podczas tworzenia mapy bitowej możemy obrócić mapę bitową za pomocą Matrix.postRotate (stopień)

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

Po obliczeniu obrotu możesz obrócić bitmapę jak poniżej:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm powinna być twoją bitmapą.

Jeśli chcesz poznać obrót kamery przedniej, po prostu zmień Camera.CameraInfo.CAMERA_FACING_BACK na Camera.CameraInfo.CAMERA_FACING_FRONT powyżej.

Mam nadzieję, że to pomoże.

Om Prakash Agrahari
źródło
1
Okropna odpowiedź, ale przypadkowo głosowałem. Kod ten zakłada, każdy obraz z galerii wykonany jest ze swojej kamery. Tak nie jest
Zun
-1

Stworzyłem funkcję rozszerzenia Kotlin, która upraszcza obsługę programistów Kotlin na podstawie odpowiedzi @Jason Robinson. Mam nadzieję, że to pomoże.

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
źródło
1
niesamowite, ale ma ten sam problem co wszystkie rozwiązania, jako rozszerzenie lub funkcja - nie działa na Androidzie 10.
Lior Iluz
-2

Istnieje prostsze polecenie, aby naprawić ten błąd.

Po prostu dodaj po yourImageView.setBitmap (bitmap); this yourImageView.setRotation (90);

To naprawiło moje. Mam nadzieję, że to pomoże !

JG
źródło
6
Jak stwierdzono w OP, niektóre urządzenia nie obracają obrazu, niektóre obracają go o 90 stopni, niektóre o 180 itd. Dlatego zawsze obrócenie go o 90 byłoby w niektórych przypadkach niepoprawne.
jk7
-8

to zadziałało dla mnie

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
zaheer
źródło
lol, co to za dziwak. Skąd, do diabła, wiesz, że zdjęcie zrobione aparatem to -90 / 90/0 / ... Użytkownik może robić zdjęcie jako krajobraz i bez względu na to, co go obrócisz ... lmao
Alex
W tym przypadku zadziałało to dla mnie, ponieważ w moim przypadku użytkownik zawsze zrobi zdjęcie telefonem w pozycji pionowej.
Christian Eduardo Galdamez