Почему изображение, снятое с помощью камеры, поворачивается на некоторых устройствах на Android?

377

Я снимаю изображение и настраиваю его на просмотр изображения.

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

Но проблема в том, что изображение на некоторых устройствах каждый раз поворачивается. Например, на устройстве Samsung он работает хорошо, но на Sony Xperia изображение поворачивается на 90 градусов, а на Toshiba Thrive (планшет) - на 180 градусов.

Шириш Хервейд
источник
1
попробуйте это в своей деятельности menifest android: configChanges = "ориентация" android: screenOrientation = "портрет"
Нарендра Пал
@ Ник это не работает, теперь изображение поворачивается на 90 градусов, а не на 180 градусов на вкладке
Shirish Herwade
1
Как я думаю, когда вы используете внутреннее намерение для работы с приложением камеры, оно поворачивает изображение. Это зависит от того, как вы держите устройство для захвата изображения. Таким образом, вы можете запретить пользователю делать снимки определенным образом, что означает, что пользователь всегда будет снимать изображение, держа устройство в портретной или альбомной ориентации. После этого вы можете изменить его на определенный угол, чтобы получить изображение, как вы хотите .. ИЛИ ДРУГОЙ ВАРИАНТ, СОЗДАЙТЕ СВОЕ ПРИЛОЖЕНИЕ КАМЕРЫ.
Нарендра Пал
@nick «Вы можете запретить пользователю делать снимки определенным образом» означает, что это то же самое, что и установка ориентации = «портрет»? А как «После этого вы можете изменить его на определенный угол, чтобы получить изображение, как вы хотите» достичь? Пожалуйста, не могли бы вы дать несколько полезных ссылок
Шириш Хервейд
3
Я считаю, что цель захвата всегда вызывает приложение камеры по умолчанию, которое имеет определенную ориентацию на каждом устройстве и, следовательно, фиксированную ориентацию фотографии. Это не зависит от того, как пользователь держит устройство или от ориентации вашей деятельности, которая вызвала намерение.
Алекс Кон

Ответы:

441

Большинство телефонных камер имеют альбомную ориентацию. Это означает, что если вы сделаете фотографию в портретной ориентации, полученные фотографии будут повернуты на 90 градусов. В этом случае программное обеспечение камеры должно заполнять данные Exif в той ориентации, в которой должна просматриваться фотография.

Обратите внимание, что приведенное ниже решение зависит от производителя программного обеспечения / устройства камеры, заполняющего данные Exif, поэтому оно будет работать в большинстве случаев, но не является надежным на 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;
}

Вот rotateImageметод:

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
Из кода @JasonRobinson я узнаю, как получить реальную ориентацию, и в сочетании с этим кодом я успешно управляю ориентацией.
Радитья Курнианто
Второй вариант exif.getAttributeIntиспользования ExifInterface.ORIENTATION_UNDEFINEDпочти такой же, так как второй параметр является значением по умолчанию в случае, если функция не может предоставить значение.
Дарпан
5
Этот код для изображения, уже записанного на диск, верно? Я не получаю результатов, используя этот метод для растрового изображения, которое должно быть записано на диск.
Фракийский
4
Это всегда возвращает мне значение 0. Пожалуйста, расскажите, как получить актуальную ориентацию.
Анураг Шривастава
3
Получение 0 всегда, есть идеи почему?
Навя Рамесан
186

Объединив Джейсон Робинсон «s ответ с Felix » s ответ и заполнение недостающих частей, здесь окончательное комплексное решение для этого вопроса , который будет делать следующее после тестирования на Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) и Android 5.0 ( леденец ).

меры

  1. Уменьшите изображение, если оно было больше, чем 1024x1024.

  2. Поверните изображение в правильное положение, только если оно было повернуто на 90, 180 или 270 градусов.

  3. Переработайте повернутое изображение для памяти.

Вот часть кода:

Вызовите следующий метод с текущим Contextи изображением, URIкоторое вы хотите исправить

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

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

Затем идет метод, который будет проверять текущую ориентацию изображения, чтобы определить угол поворота

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

Наконец, сам метод вращения

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

- Не забудьте проголосовать за тех парней, которые ответили за их усилия, и за Шириша Хервейда, который задал этот полезный вопрос.

Сами Эльтамавы
источник
2
Это прекрасно для меня.
Спасибо
1
метод rotateImageIfRequired () работает очень хорошо .. спасибо !!
Мапо
5
Не работает для меня Иногда мой телефон выдает портрет, иногда пейзажные фотографии, но обнаруженная ориентация всегда равна 0 градусам.
Макаделе
@Makalele Эта проблема также возникает при фотографировании и прикреплении через WhatsApp?
Манодж Перумарат
Я не использую WhatsApp, поэтому не могу сказать, но, скорее всего, да. Это потому, что это происходит даже в приложении для фотографий (Google Stock Camera).
Makalele
45

Легко определить ориентацию изображения и заменить растровое изображение, используя:

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

Чтобы избежать появления воспоминаний с большими изображениями, я бы порекомендовал вам изменить масштаб изображения, используя:

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

Невозможно использовать ExifInterface для получения ориентации, поскольку проблема в ОС Android: https://code.google.com/p/android/issues/detail?id=19268.

И вот 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
что такое метод
CalcuInInSampleSize
1
@madhukotagiri здесь у вас есть пример реализации для calcInSampleSize
Феликс,
Спасибо человек, ты определенно один! Мне просто интересно, сколько будет полезно изменение размера, если операция выполняется только изредка.
Марьино
4
Параметр Uri selectedImage не используется в методе getRotation (...). Как нам это нужно использовать? Спасибо.
valerybodak
1
Параметр selectedImage, кажется, нигде не используется. Есть ли причина быть там?
Алекс
20

Одноканальное решение:

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

Или

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

Это автоматически определит поворот и поместит изображение в правильную ориентацию

Picasso - очень мощная библиотека для обработки изображений в вашем приложении, включающая в себя: Сложные преобразования изображений с минимальным использованием памяти.

voytez
источник
1
Интересное решение
Бхавик Мехта
8
Он просто загружает изображение в представление, он не дает вам растровое изображение или файл, которым вы можете манипулировать или загружать на сервер.
flawyte
4
Его отображаемое изображение щелкнуло как есть. Он не вращается как требуется.
Сим
1
@Flawyte, вы можете сделать это, загрузив файл в цель вместо просмотра с обратным вызовом, которое возвращает обрезанное / измененное растровое изображение: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Into (target); где target = new Target () {Переопределить public void onBitmapLoaded (Растровое изображение, Picasso.LoadedFrom from) {
voytez
проблема, с которой я все еще сталкиваюсь, состоит в том, что требуется несколько секунд, чтобы отобразить изображение
Anu
12

Я потратил много времени в поисках решения для этого. И наконец-то удалось это сделать. Не забудьте высказать ответ @ Джейсон Робинсон, потому что мой основан на его.

Итак, во-первых, вы должны знать, что начиная с Android 7.0 мы должны использовать FileProviderи что-то вызывать ContentUri, в противном случае вы получите досадную ошибку при попытке вызвать вашу Intent. Это пример кода:

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

getUriFromPath(Context, String)Основа метода на пользовательской версии Android создать FileUri (file://...)или ContentUri (content://...)и там это:

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

После onActivityResultтого, как вы можете поймать этоuri когда изображение сохраняется камерой, но теперь вам нужно определить вращение камеры, здесь мы будем использовать модифицированный ответ @Jason Robinson:

Сначала нам нужно создать ExifInterfaceна основеUri

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

Выше код может быть упрощен, но я хочу показать все. Так что FileUriмы можем создавать ExifInterfaceна основе String path, но ContentUriмы не можем, Android не поддерживает это.

В этом случае мы должны использовать другой конструктор на основе InputStream. Помните, что этот конструктор не доступен по умолчанию, вам нужно добавить дополнительную библиотеку:

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

Теперь мы можем использовать getExifInterfaceметод, чтобы получить наш угол:

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

Теперь у вас есть угол, чтобы правильно повернуть изображение :).

Артур Шиманский
источник
2
реализация 'androidx.exifinterface: exifinterface: XXX' Это для тех, кто использует androidx. спасибо за ваше сообщение
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;
    }
}
Хареш Челана
источник
Идеальное решение Хареш Бхай
Сагар Пития
9

Вы можете просто прочитать ориентацию датчика камеры, как указано Google в документации: 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.

Образец кода:

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)
{
}
Стивен Ши
источник
6

Джейсон Робинсон ответ и Сам Eltamawy ответ является свободно.

Просто улучшение, чтобы завершить подход, вы должны использовать Compat ExifInterface.

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

Вы сможете создать экземпляр ExifInterface (pior API <24) с InputStream(from ContentResolver) вместо путей uri, избегая «Файл не найден исключений»

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

Ricard
источник
4

Обычно рекомендуется решить проблему с ExifInterface , как и предлагал @Jason Robinson. Если этот подход не работает, вы можете попытаться найти ориентацию последнего полученного изображения ...

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
Я думаю, что этот код только определяет, в какой степени произошло вращение. Теперь я могу это сделать, но не могу выполнить следующую задачу, то есть повернуть изображение.
Шириш Хервейд
Вы правы, но вы не просили ротацию в этой теме, поэтому давайте сохраним ее;) Вот почему я поместил свой ответ на вашу проблему с вращением в вашу другую тему ... Надеюсь, что это помогает, это работает на я: stackoverflow.com/questions/14123809/…
Крис Конвей
4

К сожалению, ответ @ jason-robinson выше не работал для меня.

Хотя функция поворота работает отлично:

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

Мне нужно было сделать следующее, чтобы получить ориентацию, так как ориентация Exif всегда была 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
источник
1
alwasys 0, samsung 7
djdance
2

Лучше постарайтесь сделать снимок в определенной ориентации.

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

Для достижения наилучших результатов задайте альбомную ориентацию при просмотре с камеры.

Сива
источник
извини, это не работает Фактически на вкладке, каждый раз после завершения выполнения onActivityResult, странным образом вызывается onCreate.
Шириш Хервейд
1
извините, проблема в том, как она есть
Шириш Хервейд
2

Если кто - то испытывает проблемы с ExifInterfaceна Android 4.4 (KitKat) для получения ориентации, это может быть из - за неправильного пути получил от URI. Смотрите решение для propoer getPathв вопросе переполнения стека. Получите реальный путь от URI, Android KitKat - новая структура доступа к хранилищу

peter.bartos
источник
Этот комментарий - то, что мне было нужно. Чувак, спасибо тебе большое.
Джоэл Ниман,
1

Выбранный ответ использует наиболее распространенный метод ответа на этот и аналогичные вопросы. Тем не менее, он не работает с передней и задней камерами на Samsung. Для тех, кто ищет решение, которое работает как с передними, так и с задними камерами для Samsung и других крупных производителей, этот ответ от nvhausid потрясающий:

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

Для тех, кто не хочет переходить по ссылкам, уместно использовать CameraInfo, а не полагаться на 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);

Полный код в ссылке.

Д. Скотт
источник
нет, неправильное вращение под разными углами (smasung s7). Я имею в виду галерею конечно
djdance
1

Это может быть само собой разумеется, но всегда помните, что вы можете решить некоторые из этих проблем обработки изображений на вашем сервере. Я использовал ответы, подобные тем, которые содержатся в этой теме, для непосредственного отображения изображения. Однако мое приложение требует, чтобы изображения сохранялись на сервере (это, вероятно, распространенное требование, если вы хотите, чтобы изображение сохранялось, когда пользователи переключают телефоны).

Решения, содержащиеся во многих потоках, относящихся к этой теме, не обсуждают отсутствие постоянства данных EXIF, которые не выдерживают сжатие растрового изображения, а это означает, что вам нужно будет поворачивать изображение каждый раз, когда ваш сервер загружает его. Кроме того, вы можете отправить данные ориентации EXIF ​​на ваш сервер, а затем повернуть изображение там, если это необходимо.

Мне было легче создать постоянное решение на сервере, потому что мне не нужно было беспокоиться о секретных путях файлов Android.

Брэден Холт
источник
Можете ли вы повернуть его один раз во время захвата изображения и сохранить его таким образом, чтобы его больше не нужно было поворачивать?
7
Да, вы можете, и это на самом деле процесс, который я в итоге реализовал. У меня были проблемы с получением пути к файлу из изображения на телефоне Android, что позволило бы мне это сделать. Это ответ, который помог: stackoverflow.com/a/36714242/5443056
Брэден Холт
1

Самое простое решение этой проблемы:

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

Я сохраняю изображение в формате jpg.

DNB
источник
0

Вот Xamarin.Android версия:

От @Jason Робинсон ответа :

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

Тогда calculateInSampleSizeметод:

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

От @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

Если вы используете Fresco, вы можете использовать это -

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

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

Это автоматически поворачивает изображения на основе данных Exif.

Источник: https://frescolib.org/docs/rotation.html

Ритеш Чанднани
источник
0

Приведенный ниже код работал со мной, он получил растровое изображение из fileUri и, при необходимости, выполняет исправление поворота:

    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

Получил ответ на эту проблему без использования ExifInterface . Мы можем получить поворот камеры на передней или задней камере, в зависимости от того, что вы используете, и затем при создании растрового изображения мы можем повернуть растровое изображение с помощью Matrix.postRotate (степень)

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

После расчета поворота вы можете повернуть растровое изображение, как показано ниже:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare BM должен быть вашим растровым изображением.

Если вы хотите узнать поворот передней камеры, просто измените Camera.CameraInfo.CAMERA_FACING_BACK на Camera.CameraInfo.CAMERA_FACING_FRONT выше.

Надеюсь, это поможет.

Ом Пракаш Аграри
источник
1
Ужасный ответ, но я случайно проголосовал. Этот код предполагает, что каждое изображение из вашей галереи сделано с вашей камерой. Это не тот случай
Zun
-1

Я создал функцию расширения Kotlin, которая упрощает работу для разработчиков Kotlin на основе ответа @Jason Robinson. Я надеюсь, что это помогает.

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
источник
1
удивительно, но страдает от той же проблемы, что и все решения, как расширение или функция - не работает на Android 10.
Lior Iluz
-2

Существует более простая команда, чтобы исправить эту ошибку.

Просто добавьте после yourImageView.setBitmap (bitmap); this yourImageView.setRotation (90);

Это исправило мое. Надеюсь, поможет !

JG
источник
6
Как указывалось в ОП, некоторые устройства не поворачивают изображение, некоторые поворачивают его на 90 градусов, некоторые на 180, и т. Д. Так что всегда поворачивать его на 90 будет неправильно в некоторых случаях.
jk7
-8

это сработало для меня

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
Захир
источник
лол, что это за урод. Откуда, черт возьми, вы знаете, что фотография, сделанная камерой, равна -90 / 90/0 / ... Пользователь может снимать фотографию как пейзаж, и неважно, что вы собираетесь повернуть ... lmao
Alex
В этом случае это работает для меня, так как в моем случае пользователь всегда будет делать снимок с телефона в вертикальном положении.
Кристиан Эдуардо Галдамес