Чому зображення, зняте за допомогою намірів камери, обертається на деяких пристроях Android?


376

Я знімаю зображення і налаштовую його на перегляд зображення.

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 = "портрет"
Narendra Pal

@nick це не працює, тепер зображення повертається на 90 градусів, а не на 180 градусів на вкладці
Shirish Herwade

1
як я думаю, коли ви використовуєте внутрішній намір мати справу з додатком для камери, то він обертає зображення. Це залежить від того, як ви тримаєте пристрій для зйомки зображення. Таким чином, ви можете обмежити користувач, зокрема, фотографувати, це означає, що користувач завжди буде робити знімки, тримаючи пристрій у портретному або пейзажному режимі. Після цього ви можете змінити його під конкретний кут, щоб отримати зображення, як вам захочеться. АБО ДРУГА ОПЦІЯ, Зробіть власне додаток для камери.
Нарендра Пал

@ nick "Ви можете обмежити користувачеві фотографувати особливим чином", це означає, що це те саме, що налаштування орієнтації = "po Portrait"? І як "після цього ви можете змінити його під конкретний кут, щоб отримати зображення, як ви хочете" досягти? Надайте, будь ласка, корисні посилання
Shirish Herwade

3
Я вважаю, що намір зйомки завжди відображає програму камери за замовчуванням, яка має конкретну орієнтацію на кожному пристрої, а отже - фіксовану орієнтацію фотографій. Це не залежить від способу, яким користувач тримає пристрій, або орієнтації вашої діяльності, яка викликала наміри.
Алекс Кон

Відповіді:


440

Більшість телефонних камер мають пейзаж, тобто якщо ви робите фотографії в портреті, отримані фотографії будуть повертатися на 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 я дізнаюся, як отримати фактичну орієнтацію і, поєднуючись з цим кодом, я успішно керую орієнтацією.
Raditya Kurnian до

Другий варіант 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 ( Lollipop ).

Кроки

  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)
Макалеле

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/isissue/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
що тут обчислити метод
InSampleSize

1
@madhukotagiri тут у вас є приклад впровадження для CalcuInSampleSize
Фелікс

Спасибі, ти точно такий! Мені просто цікаво, наскільки корисне зміна буде корисною, якщо операція виконується лише зрідка.
Маріно

4
Uri вибраний параметрImage, не використовується в методі getRotation (...). Як нам це потрібно використовувати? Дякую.
валерібодак

1
Параметр "selectedImage", схоже, ніде не використовується. Будь-яка причина бути там?
Олексій

20

Рішення з однієї лінії:

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

Або

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

Це автоматично виявить обертання і розмістить зображення у правильній орієнтації

Picasso - це дуже потужна бібліотека для обробки зображень у вашій програмі, яка включає в себе: Складні перетворення зображення з мінімальним використанням пам'яті.


1
Цікаве рішення
Бхавік Мехта

8
Він просто завантажує зображення у подання, воно не дає вам растрового зображення чи файлу, яким ви можете маніпулювати чи завантажувати на сервер.
flawyte

4
Її відображене зображення клацнуло так, як воно є. Він не обертається, як потрібно.
seema

1
@Flawyte ви можете зробити це, завантаживши файл у ціль замість перегляду з зворотним дзвоном, який повертає обрізану / змінену растрову карту: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). В (target); де target = new Target () {Переосмислити публічну недійсність наBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom from) {
voytez

проблема я до сих пір облицювання це займає кілька секунд , щоб зображення дисплея
Ана

12

Я витратив багато часу на пошуки рішення для цього. І нарешті вдалося це зробити. Не забудьте подати відповідь на @Jason Robinson, тому що моя заснована на його.

Тож перше, що ви повинні знати, що оскільки 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, але ContentUriAndroid не може цього підтримувати.

У цьому випадку ми повинні використовувати інший конструктор на основі 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;
    }
}

Тепер у вас є Angle, щоб правильно обертати зображення :).


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

Ідеальне рішення Haresh Bhai
Sagar Pithiya

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 (API pior <24) за допомогою InputStream(від ContentResolver) замість урі-контурів, уникаючи "Файлу не знайдено винятків"

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


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
Я думаю, що цей код виявляє лише те, в якому ступені відбулося обертання. Тепер я можу це зробити, але не в змозі в наступному завданні, тобто повернути зображення.
Shirish Herwade

Ви маєте рацію, але ви не просили обертання в цій темі, тому давайте чистимо її;) Ось чому я поставив свою відповідь на вашу проблему, що обертається, в іншу нитку ... Сподіваюся, що це допомагає, воно спрацьовує для me: 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 );

1
alwasys 0, samsung 7
djdance

2

Краще спробуйте зробити знімок у конкретній орієнтації.

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

Для найкращих результатів дайте орієнтацію на пейзаж у режимі перегляду камери.


Вибачте, це не працює. По суті, на вкладці кожен раз після завершення виконання onActivityResult дивно викликається onCreate.
Ширіш Ерваде

1
вибачте, проблема така, як є
Ширіш Ерваде

2

Якщо хто - то відчуває проблеми з ExifInterfaceна Android 4.4 (KitKat) для отримання орієнтації, це може бути з - за неправильного шляху отримав від URI. Дивіться рішення для пропонера getPathу запитанні про переповнення стека. Отримайте реальний шлях від нової системи доступу до пам’яті URI, Android KitKat


Цей один коментар - це те, що мені було потрібно. Чувак, дуже дякую
Джоель Німан


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, які не переживають стиснення зображення Bitmap, тобто вам потрібно буде обертати зображення кожного разу, коли ваш сервер завантажує його. Крім того, ви можете надіслати дані орієнтації EXIF ​​на свій сервер, а потім обернути зображення там, якщо потрібно.

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


Чи можете ви повернути його один раз під час зйомки зображення і зберегти його таким чином, щоб його ніколи не потрібно було повертати знову?
jk7

Так, ви можете, і це насправді процес, який я врешті-решт здійснив. У мене виникли проблеми з отриманням шляху файлу від зображення на телефоні Android, який дозволив би мені це зробити. Це відповідь, яка допомогла: stackoverflow.com/a/36714242/5443056
Бреден Холт

1

Найпростіше рішення цієї проблеми:

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

Я зберігаю зображення у форматі jpg.


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

Нижче код працював зі мною, він отримав растрову карту з файлуUri і, якщо потрібно, зробіть виправлення повороту:

    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 . Ми можемо отримати обертання камери передньою камерою або задньою камерою залежно від того, що ви використовуєте тоді, створюючи Bitmap, ми можемо обертати растрову карту за допомогою 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
Жахлива відповідь, але я випадково схвалив. Цей код передбачає, що кожне зображення з вашої галереї зроблене за допомогою вашої камери. Це не так
Зун

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

1
приголомшливий, але страждає від тієї ж проблеми, що і всі рішення, як розширення або функція - не працює на Android 10.
Lior Iluz

-2

Існує більш проста команда виправити цю помилку.

Просто додайте після yourImageView.setBitmap (bitmap); це yourImageView.setRotation (90);

Це нерухома шахта. Сподіваюся, це допомагає!


6
Як зазначено в ОП, деякі пристрої не обертають зображення, деякі обертають його на 90 градусів, деякі на 180, .. і т.д. Тому завжди обертати його на 90 було б неправильно в деяких випадках.
jk7

-8

це працювало для мене

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

хаха, що таке виродка. Як, пекло, ви знаєте, що фото, зроблене камерою, становить -90 / 90/0 / ... Користувач може фотографувати як краєвид і незалежно від того, що ви будете обертати ... lmao
Alex

У цьому випадку це працювало для мене, оскільки в моєму випадку користувач завжди буде робити знімок разом із телефоном вертикально.
Крістіан Едуардо Гальдамес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.