Mengapa gambar yang diambil menggunakan niat kamera diputar pada beberapa perangkat di Android?

376

Saya mengambil foto dan mengaturnya ke tampilan gambar.

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

Tapi masalahnya adalah, gambar pada beberapa perangkat setiap kali diputar. Misalnya, pada perangkat Samsung berfungsi baik, tetapi pada Sony Xperia gambar diputar sebesar 90 derajat dan pada Toshiba Thrive (tablet) sebesar 180 derajat.

Shirish Herwade
sumber
1
coba ini di aktivitas kamu menifest android: configChanges = "orientasi" android: screenOrientation = "portrait"
Narendra Pal
@nick itu tidak berfungsi, sekarang gambar akan diputar ke 90 derajat daripada 180 derajat pada tab
Shirish Herwade
1
karena saya pikir ketika Anda menggunakan niat internal untuk berurusan dengan aplikasi kamera, maka itu memutar gambar. Ini tergantung pada bagaimana Anda memegang perangkat untuk mengambil gambar. Jadi Anda dapat membatasi pengguna untuk mengambil gambar dengan cara tertentu berarti pengguna akan selalu menangkap gambar dengan memegang perangkat dalam posisi portrait atau landscape. Setelah itu Anda dapat mengubahnya ke sudut tertentu untuk mendapatkan gambar seperti yang Anda inginkan .. ATAU PILIHAN LAIN, MEMBUAT APLIKASI KAMERA ANDA SENDIRI.
Narendra Pal
@nick "Anda dapat membatasi pengguna untuk mengambil gambar dengan cara tertentu" artinya apakah sama dengan menetapkan orientasi = "potrait"? Dan bagaimana cara "Setelah itu Anda dapat mengubahnya ke sudut tertentu untuk mendapatkan gambar yang Anda inginkan" tercapai? Tolong bisakah Anda memberikan beberapa tautan bermanfaat
Shirish Herwade
3
Saya percaya maksud pengambilan selalu memunculkan aplikasi kamera default yang memiliki orientasi spesifik pada setiap perangkat dan akibatnya - memperbaiki orientasi foto. Itu tidak tergantung pada cara pengguna memegang perangkat atau orientasi aktivitas Anda yang memanggil niat.
Alex Cohn

Jawaban:

440

Sebagian besar kamera ponsel berbentuk lansekap, artinya jika Anda mengambil foto dalam potret, foto yang dihasilkan akan diputar 90 derajat. Dalam hal ini, perangkat lunak kamera harus mengisi data Exif dengan orientasi bahwa foto harus dilihat.

Perhatikan bahwa solusi di bawah ini tergantung pada produsen perangkat lunak / perangkat kamera yang mengisi data Exif, sehingga ini akan berfungsi dalam kebanyakan kasus, tetapi itu bukan solusi yang dapat diandalkan 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;
}

Inilah rotateImagemetodenya:

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
                               matrix, true);
}
Jason Robinson
sumber
1
Dari kode @JasonRobinson, saya belajar cara mendapatkan orientasi aktual dan dengan menggabungkan dengan kode ini saya berhasil mengelola orientasi.
Raditya Kurnianto
Opsi kedua untuk exif.getAttributeIntmenggunakan ExifInterface.ORIENTATION_UNDEFINEDhampir sama, karena parameter kedua adalah nilai default jika fungsi gagal memberikan nilai.
Darpan
5
Kode ini untuk gambar yang sudah ditulis ke disk, bukan? Saya tidak mendapatkan hasil menggunakan metode ini untuk bitmap yang akan ditulis ke disk.
Thracian
4
Itu selalu mengembalikan nilai saya 0. Tolong beri tahu cara mendapatkan orientasi aktual.
Anurag Srivastava
3
Mendapatkan 0 selalu, tahu mengapa?
Navya Ramesan
186

Dengan menggabungkan Jason Robinson 's jawaban dengan Felix ' s jawaban dan mengisi bagian yang hilang, di sini adalah solusi lengkap akhir untuk masalah ini yang akan melakukan hal berikut setelah pengujian pada Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) dan Android 5.0 ( Lollipop ).

Langkah

  1. Perkecil gambar jika lebih besar dari 1024x1024.

  2. Putar gambar ke orientasi kanan hanya jika diputar 90, 180 atau 270 derajat.

  3. Daur ulang gambar yang diputar untuk keperluan memori.

Inilah bagian kode:

Panggil metode berikut dengan arus Contextdan gambar URIyang ingin Anda perbaiki

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

Berikut adalah CalculateInSampleSizemetode dari sumber yang disebutkan sebelumnya :

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

Kemudian datang metode yang akan memeriksa orientasi gambar saat ini untuk menentukan sudut rotasi

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

Akhirnya metode rotasi itu sendiri

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

-Jangan lupa untuk memilih jawaban orang-orang atas upaya mereka dan Shirish Herwade yang mengajukan pertanyaan bermanfaat ini.

Sami Eltamawy
sumber
2
Woking for me Perfectly. Terima kasih
Shohel Rana
1
metode rotateImageIfRequired () berfungsi dengan sangat baik .. terima kasih !!
mapo
5
Tidak bekerja untuk saya. Terkadang ponsel saya memberikan potret, terkadang foto lanskap, tetapi orientasi yang terdeteksi selalu 0 derajat.
Makalele
@Makalele Apakah masalah ini juga terjadi saat mengambil foto dan melampirkan melalui WhatsApp?
Manoj Perumarath
Saya tidak menggunakan WhatsApp jadi saya tidak bisa mengatakannya, tetapi kemungkinan besar ya. Itu karena itu bahkan terjadi di aplikasi stok foto (Google Stock Camera).
Makalele
45

Mudah untuk mendeteksi orientasi gambar dan mengganti bitmap menggunakan:

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

Untuk menghindari Kehabisan memori dengan gambar besar, saya sarankan Anda untuk mengubah skala gambar menggunakan:

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

Tidak mungkin menggunakan ExifInterface untuk mendapatkan orientasi karena masalah OS Android: https://code.google.com/p/android/issues/detail?id=19268

Dan ini dia calculateInSampleSize

/**
 * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
 * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
 * the closest inSampleSize that will result in the final decoded bitmap having a width and
 * height equal to or larger than the requested width and height. This implementation does not
 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
 * results in a larger bitmap which isn't as useful for caching purposes.
 *
 * @param options   An options object with out* params already populated (run through a decode*
 *                  method with inJustDecodeBounds==true
 * @param reqWidth  The requested width of the resulting bitmap
 * @param reqHeight The requested height of the resulting bitmap
 * @return The value to be used for inSampleSize
 */
public static int calculateInSampleSize(BitmapFactory.Options options,
                                        int reqWidth, int reqHeight) {

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}
Felix
sumber
1
apa metode menghitungInSampleSize di sini
madhu kotagiri
1
@madhukotagiri di sini Anda memiliki contoh implementantion untuk menghitungInSampleSize: gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix
Terima kasih kawan, kau yang pasti! Saya hanya ingin tahu berapa ukurannya akan berguna, jika operasi dilakukan sesekali.
Marino
4
Parameter Uri selectedImage tidak digunakan dalam metode getRotation (...). Bagaimana kita perlu menggunakannya? Terima kasih.
valerybodak
1
Parameter 'selectedImage' tampaknya tidak digunakan di mana pun. Ada alasan untuk berada di sana?
Alex
20

Solusi satu baris:

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

Atau

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

Ini akan mendeteksi rotasi otomatis dan menempatkan gambar dalam orientasi yang benar

Picasso adalah perpustakaan yang sangat kuat untuk menangani gambar di aplikasi Anda termasuk: Transformasi gambar yang kompleks dengan penggunaan memori minimal.

voytez
sumber
1
Solusi menarik
Bhavik Mehta
8
Ini hanya memuat gambar ke tampilan, itu tidak memberi Anda bitmap atau file yang dapat Anda manipulasi atau unggah ke server.
flawyte
4
Gambar yang ditampilkan diklik seperti apa adanya. Itu tidak berputar sesuai kebutuhan.
seema
1
@ Flawyte Anda dapat melakukannya dengan memuat file ke dalam target alih-alih melihat dengan panggilan balik yang mengembalikan bitmap yang dipangkas / diubah ukurannya: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Ke (target); di mana target = Target baru () {Override public void onBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom from) {
voytez
masalah yang masih saya hadapi adalah butuh beberapa detik untuk menampilkan gambar
Anu
12

Saya telah menghabiskan banyak waktu mencari solusi untuk ini. Dan akhirnya berhasil melakukan ini. Jangan lupa untuk menjawab @Jason Robinson karena saya berdasarkan pada jawabannya.

Jadi hal pertama, Anda harus tahu bahwa sejak Android 7.0 kita harus menggunakan FileProviderdan sesuatu yang disebut ContentUri, jika tidak, Anda akan mendapatkan kesalahan yang mengganggu ketika mencoba memanggil Anda Intent. Ini adalah kode contoh:

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

Metode getUriFromPath(Context, String)dasar pada versi pengguna Android buat FileUri (file://...)atau ContentUri (content://...)dan itu ada:

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

Setelah onActivityResultAnda dapat menangkap bahwa di urimana gambar disimpan oleh kamera, tetapi sekarang Anda harus mendeteksi rotasi kamera, di sini kita akan menggunakan jawaban @Jason Robinson yang dimodifikasi:

Pertama kita perlu membuat ExifInterfaceberdasarkanUri

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

Kode di atas dapat disederhanakan, tetapi saya ingin menunjukkan semuanya. Jadi dari FileUrikita dapat membuat ExifInterfaceberdasarkan String path, tetapi dari ContentUrikita tidak bisa, Android tidak mendukung itu.

Dalam hal ini kita harus menggunakan konstruktor lain berdasarkan InputStream. Ingat konstruktor ini tidak tersedia secara default, Anda harus menambahkan perpustakaan tambahan:

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

Sekarang kita bisa menggunakan getExifInterfacemetode untuk mendapatkan sudut pandang kita:

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

Sekarang Anda memiliki Angle untuk memutar gambar Anda dengan benar :).

Artur Szymański
sumber
2
implementasi 'androidx.exifinterface: exifinterface: XXX' Ini untuk mereka yang menggunakan androidx. terima kasih atas kiriman Anda
Doongsil
11
// Try this way,hope this will help you to solve your problem...

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center">
        <ImageView
            android:id="@+id/imgFromCameraOrGallery"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:src="@drawable/ic_launcher"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btnCamera"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Camera"/>
        <Button
            android:id="@+id/btnGallery"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_marginLeft="5dp"
            android:layout_height="wrap_content"
            android:text="Gallery"/>

    </LinearLayout>
</LinearLayout>

MainActivity.java

    public class MainActivity extends Activity {

    private ImageView imgFromCameraOrGallery;
    private Button btnCamera;
    private Button btnGallery;

    private String imgPath;
    final private int PICK_IMAGE = 1;
    final private int CAPTURE_IMAGE = 2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imgFromCameraOrGallery = (ImageView) findViewById(R.id.imgFromCameraOrGallery);
        btnCamera = (Button) findViewById(R.id.btnCamera);
        btnGallery = (Button) findViewById(R.id.btnGallery);

        btnCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri());
                startActivityForResult(intent, CAPTURE_IMAGE);
            }
        });

        btnGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, ""), PICK_IMAGE);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == CAPTURE_IMAGE) {
                setCapturedImage(getImagePath());
            } else if (requestCode == PICK_IMAGE) {
                imgFromCameraOrGallery.setImageBitmap(BitmapFactory.decodeFile(getAbsolutePath(data.getData())));
            }
        }

    }

    private String getRightAngleImage(String photoPath) {

        try {
            ExifInterface ei = new ExifInterface(photoPath);
            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            int degree = 0;

            switch (orientation) {
                case ExifInterface.ORIENTATION_NORMAL:
                    degree = 0;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                case ExifInterface.ORIENTATION_UNDEFINED:
                    degree = 0;
                    break;
                default:
                    degree = 90;
            }

            return rotateImage(degree,photoPath);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return photoPath;
    }

    private String rotateImage(int degree, String imagePath){

        if(degree<=0){
            return imagePath;
        }
        try{
            Bitmap b= BitmapFactory.decodeFile(imagePath);

            Matrix matrix = new Matrix();
            if(b.getWidth()>b.getHeight()){
                matrix.setRotate(degree);
                b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
                        matrix, true);
            }

            FileOutputStream fOut = new FileOutputStream(imagePath);
            String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
            String imageType = imageName.substring(imageName.lastIndexOf(".") + 1);

            FileOutputStream out = new FileOutputStream(imagePath);
            if (imageType.equalsIgnoreCase("png")) {
                b.compress(Bitmap.CompressFormat.PNG, 100, out);
            }else if (imageType.equalsIgnoreCase("jpeg")|| imageType.equalsIgnoreCase("jpg")) {
                b.compress(Bitmap.CompressFormat.JPEG, 100, out);
            }
            fOut.flush();
            fOut.close();

            b.recycle();
        }catch (Exception e){
            e.printStackTrace();
        }
        return imagePath;
    }

    private void setCapturedImage(final String imagePath){
        new AsyncTask<Void,Void,String>(){
            @Override
            protected String doInBackground(Void... params) {
                try {
                    return getRightAngleImage(imagePath);
                }catch (Throwable e){
                    e.printStackTrace();
                }
                return imagePath;
            }

            @Override
            protected void onPostExecute(String imagePath) {
                super.onPostExecute(imagePath);
                imgFromCameraOrGallery.setImageBitmap(decodeFile(imagePath));
            }
        }.execute();
    }

    public Bitmap decodeFile(String path) {
        try {
            // Decode deal_image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, o);
            // The new size we want to scale to
            final int REQUIRED_SIZE = 1024;

            // Find the correct scale value. It should be the power of 2.
            int scale = 1;
            while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
                scale *= 2;
            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeFile(path, o2);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getAbsolutePath(Uri uri) {
        if(Build.VERSION.SDK_INT >= 19){
            String id = "";
            if(uri.getLastPathSegment().split(":").length > 1)
                id = uri.getLastPathSegment().split(":")[1];
            else if(uri.getLastPathSegment().split(":").length > 0)
                id = uri.getLastPathSegment().split(":")[0];
            if(id.length() > 0){
                final String[] imageColumns = {MediaStore.Images.Media.DATA };
                final String imageOrderBy = null;
                Uri tempUri = getUri();
                Cursor imageCursor = getContentResolver().query(tempUri, imageColumns, MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
                if (imageCursor.moveToFirst()) {
                    return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                }else{
                    return null;
                }
            }else{
                return null;
            }
        }else{
            String[] projection = { MediaStore.MediaColumns.DATA };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null) {
                int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            } else
                return null;
        }

    }

    private Uri getUri() {
        String state = Environment.getExternalStorageState();
        if(!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
            return MediaStore.Images.Media.INTERNAL_CONTENT_URI;

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

    public Uri setImageUri() {
        Uri imgUri;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/",getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis() + ".png");
            imgUri = Uri.fromFile(file);
            imgPath = file.getAbsolutePath();
        }else {
            File file = new File(getFilesDir() ,getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis()+ ".png");
            imgUri = Uri.fromFile(file);
            this.imgPath = file.getAbsolutePath();
        }
        return imgUri;
    }

    public String getImagePath() {
        return imgPath;
    }
}
Haresh Chhelana
sumber
Solusi Sempurna Haresh Bhai
Sagar Pithiya
9

Anda cukup membaca orientasi sensor kamera seperti yang ditunjukkan oleh Google dalam dokumentasi: 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.

Kode sampel:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
    String cameraId = manager.getCameraIdList()[0];
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}
Stephen Shi
sumber
6

Jason Robinson jawaban dan Sami Eltamawy jawabannya adalah Excellent.

Hanya peningkatan untuk menyelesaikan pendekatan, Anda harus menggunakan compat ExifInterface.

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

Anda akan dapat meng-instantiate ExifInterface (pior API <24) dengan InputStream(dari ContentResolver) alih-alih jalur uri menghindari "File not found exception"

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

Ricard
sumber
4

Biasanya disarankan untuk menyelesaikan masalah dengan ExifInterface , seperti yang disarankan oleh @Jason Robinson. Jika pendekatan ini tidak berhasil, Anda dapat mencoba mencari Orientasi dari gambar terbaru yang diambil ...

private int getImageOrientation(){
    final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            imageColumns, null, null, imageOrderBy);

    if(cursor.moveToFirst()){
        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        cursor.close();
        return orientation;
    } else {
        return 0;
    }
}
Chris Conway
sumber
1
Saya pikir kode ini hanya mendeteksi berapa derajat rotasi yang terjadi. Sekarang saya bisa melakukan itu, tetapi tidak dapat dalam tugas selanjutnya yaitu memutar gambar.
Shirish Herwade
Anda benar, tetapi Anda tidak meminta rotasi di Thread ini, jadi mari kita tetap bersih;) Itulah mengapa saya meletakkan jawaban saya untuk masalah rotasi Anda ke Thread Anda yang lain ... Semoga membantu, itu berfungsi untuk saya: stackoverflow.com/questions/14123809/…
Chris Conway
4

Sayangnya, @ jason-robinson jawaban di atas tidak berhasil untuk saya.

Meskipun fungsi putar berfungsi dengan sempurna:

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

Saya harus melakukan yang berikut untuk mendapatkan orientasi karena orientasi Exif selalu 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
sumber
1
alwasys 0, samsung 7
djdance
2

Lebih baik coba mengambil gambar dalam orientasi tertentu.

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

Untuk hasil terbaik, berikan orientasi lanskap dalam aktivitas tampilan kamera.

Siva
sumber
maaf, itu tidak berhasil. Bahkan pada tab, setiap kali setelah menyelesaikan eksekusi onActivityResult, anehnya onCreate dipanggil.
Shirish Herwade
1
maaf, masalahnya seperti itu
Shirish Herwade
1

Jawaban yang dipilih menggunakan metode yang paling umum dijawab untuk ini dan pertanyaan serupa. Namun, itu tidak bekerja dengan kamera depan dan belakang di Samsung. Bagi mereka yang mencari solusi yang bekerja di kedua kamera depan dan belakang untuk Samsung dan produsen besar lainnya, jawaban dari nvhausid ini luar biasa:

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

Bagi mereka yang tidak ingin mengklik, sihir yang relevan adalah dengan menggunakan CameraInfo daripada mengandalkan 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);

Kode lengkap di tautan.

D. Scott
sumber
tidak, rotasi salah dalam sudut yang berbeda (smasung s7). Maksudku galeri tentu saja
djdance
1

Ini mungkin tidak perlu dikatakan tetapi selalu ingat bahwa Anda dapat menangani beberapa masalah penanganan gambar ini di server Anda. Saya menggunakan tanggapan seperti yang ada di utas ini untuk menangani tampilan langsung gambar. Namun aplikasi saya membutuhkan gambar untuk disimpan di server (ini mungkin persyaratan umum jika Anda ingin gambar tetap ada sebagai pengguna beralih ponsel).

Solusi yang terkandung dalam banyak utas mengenai topik ini tidak membahas kurangnya data EXIF ​​yang tidak bertahan dari kompresi gambar Bitmap, artinya Anda harus memutar gambar setiap kali server memuatnya. Atau, Anda dapat mengirim data orientasi EXIF ​​ke server Anda, dan kemudian memutar gambar di sana jika diperlukan.

Lebih mudah bagi saya untuk membuat solusi permanen di server karena saya tidak perlu khawatir tentang jalur file klandestin Android.

Braden Holt
sumber
Bisakah Anda memutarnya sekali pada waktu pengambilan gambar dan menyimpannya sedemikian rupa sehingga tidak perlu diputar lagi?
jk7
Ya, Anda bisa dan itu sebenarnya proses yang akhirnya saya laksanakan pada akhirnya. Saya kesulitan mendapatkan jalur file dari gambar di ponsel Android yang memungkinkan saya melakukan itu. Ini adalah jawaban yang membantu: stackoverflow.com/a/36714242/5443056
Braden Holt
1

Solusi paling sederhana untuk masalah ini:

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

Saya menyimpan gambar dalam format jpg.

DNB
sumber
0

Ini Xamarin.Androidversinya:

Dari jawaban @Jason Robinson :

Bitmap rotate(Bitmap bitmap, int angle)
{
    var matrix = new Matrix();
    matrix.PostRotate(angle);

    return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
}

Bitmap rotateIfRequired(Bitmap bitmap, string imagePath)
{
    var ei = new ExifInterface(imagePath);
    var orientation = ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Undefined);

    switch (orientation)
    {
        case (int)Android.Media.Orientation.Rotate90: return rotate(bitmap, 90);
        case (int)Android.Media.Orientation.Rotate180: return rotate(bitmap, 180);
        case (int)Android.Media.Orientation.Rotate270: return rotate(bitmap, 270);
        default: return bitmap;
    }
}

Kemudian calculateInSampleSizemetode:

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

Dari jawaban @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;
}
Mehdi Dehghani
sumber
0

Jika Anda menggunakan Fresco, Anda bisa menggunakan ini -

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

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

Ini secara otomatis memutar gambar berdasarkan data Exif.

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

Ritesh Chandnani
sumber
0

Kode di bawah ini bekerja dengan saya, itu mendapatkan bitmap dari fileUri, dan melakukan perbaikan rotasi jika diperlukan:

    private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
        val bitmap = when {
            Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
                this.contentResolver,
                selectedPhotoUri
            )
            else -> {
                val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
                ImageDecoder.decodeBitmap(source)
            }
        }

        // If the image is rotated, fix it
        return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
            ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(90F) }, true)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(180F) }, true)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(270F) }, true)
            else -> bitmap
        } 
    }
Hasan A Yousef
sumber
0

Mendapat jawaban untuk masalah ini tanpa menggunakan ExifInterface . Kita bisa mendapatkan rotasi kamera baik kamera depan atau kamera belakang mana pun yang Anda gunakan saat membuat Bitmap, kami dapat memutar bitmap menggunakan Matrix.postRotate (degree)

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

Setelah menghitung rotasi, Anda dapat memutar bitmap Anda seperti di bawah ini:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm harus menjadi bitmap Anda.

Jika Anda ingin mengetahui rotasi kamera depan Anda cukup ganti Camera.CameraInfo.CAMERA_FACING_BACK ke Camera.CameraInfo.CAMERA_FACING_FRONT di atas.

Saya harap ini membantu.

Om Prakash Agrahari
sumber
1
Jawaban yang mengerikan tetapi saya tidak sengaja terbalik. Kode ini mengasumsikan setiap gambar dari galeri Anda dibuat dengan kamera Anda . Ini tidak terjadi
Zun
-1

Saya membuat fungsi ekstensi Kotlin yang menyederhanakan operasi untuk pengembang Kotlin berdasarkan jawaban @Jason Robinson. Saya harap ini membantu.

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
sumber
1
luar biasa tetapi menderita masalah yang sama dengan semua solusi, seperti ekstensi atau fungsi - tidak berfungsi pada Android 10.
Lior Iluz
-2

Ada perintah yang lebih sederhana untuk memperbaiki kesalahan ini.

Cukup tambahkan setelah yourImageView.setBitmap (bitmap); ini yourImageView.setRotation (90);

Tambang ini diperbaiki. Semoga ini bisa membantu!

J G
sumber
6
Seperti yang dinyatakan OP, beberapa perangkat tidak memutar gambar, beberapa memutarnya 90 derajat, beberapa 180, ..etc. Jadi, selalu memutarnya 90 akan salah dalam beberapa kasus.
jk7
-8

ini bekerja untuk saya

ImageView display_image = findViewById(R.id.image);
this.display_image.setRotation(90);
zaheer
sumber
lol apa yang aneh ini. Bagaimana Anda tahu foto yang diambil oleh kamera adalah -90 / 90/0 / ... Pengguna mungkin mengambil foto sebagai pemandangan dan tidak peduli apa yang akan Anda putar itu ... lmao
Alex
Dalam hal ini berfungsi untuk saya karena dalam kasus saya pengguna akan selalu mengambil gambar dengan telepon secara vertikal.
Christian Eduardo Galdamez