Tại sao hình ảnh được chụp bằng mục đích camera được quay trên một số thiết bị trên Android?


376

Tôi đang chụp một hình ảnh và đặt nó vào chế độ xem hình ảnh.

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

Nhưng vấn đề là, hình ảnh trên một số thiết bị mỗi khi nó được xoay. Ví dụ, trên thiết bị Samsung, nó hoạt động tốt, nhưng trên Sony Xperia , hình ảnh được xoay 90 độ và trên Toshiba Thrive (máy tính bảng) 180 độ.


1
Hãy thử điều này trong hoạt động của bạn menifest android: configChanges = "direction" android: screenOrientation = "Portrait"
Narendra Pal

@nick nó không hoạt động, bây giờ hình ảnh được xoay đến 90 độ thay vì 180 độ trên tab
Shirish Herwade

1
như tôi nghĩ khi bạn sử dụng mục đích nội bộ để xử lý ứng dụng camera, thì nó sẽ xoay hình ảnh. Điều này phụ thuộc vào cách bạn giữ thiết bị để chụp ảnh. Vì vậy, bạn có thể hạn chế người dùng chụp ảnh theo cách cụ thể có nghĩa là người dùng sẽ luôn chụp ảnh bằng cách giữ thiết bị ở chế độ dọc hoặc ngang. Sau đó, bạn có thể thay đổi nó thành góc cụ thể để có được hình ảnh như bạn muốn .. HOẶC TÙY CHỌN KHÁC, TẠO ỨNG DỤNG CAMERA RIÊNG CỦA BẠN.
Narendra Pal

@nick "bạn có thể hạn chế người dùng chụp ảnh theo cách cụ thể" có nghĩa là giống như cài đặt direction = "pot Eo" không? Và làm thế nào để "Sau đó bạn có thể thay đổi nó thành góc cụ thể để có được hình ảnh như bạn muốn" đạt được? Xin vui lòng bạn có thể cung cấp một số liên kết hữu ích
Shirish Herwade

3
Tôi tin rằng mục đích chụp luôn mang đến ứng dụng camera mặc định có định hướng cụ thể trên từng thiết bị và do đó - hướng ảnh cố định. Nó không phụ thuộc vào cách người dùng giữ thiết bị hoặc định hướng hoạt động của bạn mà viện dẫn ý định.
Alex Cohn

Câu trả lời:


440

Hầu hết các máy ảnh điện thoại là phong cảnh, có nghĩa là nếu bạn chụp ảnh theo chiều dọc, ảnh thu được sẽ được xoay 90 độ. Trong trường hợp này, phần mềm máy ảnh sẽ điền dữ liệu Exif với hướng mà ảnh sẽ được xem.

Lưu ý rằng giải pháp bên dưới phụ thuộc vào nhà sản xuất phần mềm / thiết bị máy ảnh điền dữ liệu Exif, vì vậy nó sẽ hoạt động trong hầu hết các trường hợp, nhưng nó không phải là giải pháp đáng tin cậy 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;
}

Đây là rotateImagephương pháp:

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
Từ mã @JasonRobinson, tôi tìm hiểu cách nhận định hướng thực tế và bằng cách kết hợp với các mã này, tôi quản lý thành công định hướng.
Raditya Kurnianto

Tùy chọn thứ hai của exif.getAttributeIntviệc sử dụng ExifInterface.ORIENTATION_UNDEFINEDgần như giống nhau, vì tham số thứ hai là giá trị mặc định trong trường hợp hàm không cung cấp giá trị.
Darpan

5
Mã này là cho một hình ảnh đã được ghi vào đĩa, phải không? Tôi không nhận được kết quả bằng phương pháp này cho bitmap sắp được ghi vào đĩa.
Thracian

4
Nó luôn luôn trả về cho tôi 0 giá trị. Xin cho biết làm thế nào để có được định hướng thực tế.
Anurag Srivastava

3
Bắt 0 luôn, có ý kiến ​​gì không?
Navya Ramesan

186

Bằng cách kết hợp Jason Robinson 's câu trả lời với Felix ' s câu trả lời và điền vào những phần còn thiếu, đây là giải pháp hoàn chỉnh cuối cùng cho vấn đề này sẽ làm những điều sau đây sau khi thử nghiệm nó trên Android Android 4.1 ( Jelly Bean ), Android 4.4 ( KitKat ) và Android 5.0 ( Lollipop ).

Các bước

  1. Thu nhỏ hình ảnh nếu nó lớn hơn 1024x1024.

  2. Xoay hình ảnh sang đúng hướng chỉ khi nó xoay 90, 180 hoặc 270 độ.

  3. Tái chế hình ảnh xoay cho mục đích bộ nhớ.

Đây là phần mã:

Gọi phương thức sau với hiện tại Contextvà hình ảnh URImà bạn muốn sửa

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

Đây là CalculateInSampleSizephương pháp từ nguồn được đề cập trước :

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

Sau đó, đến phương pháp sẽ kiểm tra hướng hình ảnh hiện tại để quyết định góc xoay

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

Cuối cùng, chính phương thức xoay

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

-Đừng quên bỏ phiếu cho những người đó trả lời cho những nỗ lực của họ và Shirish Herwade , người đã hỏi câu hỏi hữu ích này.


2
Woking cho tôi hoàn hảo
Cảm ơn

1
phương thức rotationImage IfRequired () hoạt động rất tốt .. cảm ơn !!
mapo

5
Không làm việc cho tôi. Đôi khi điện thoại của tôi cho ảnh chân dung, đôi khi ảnh phong cảnh, nhưng hướng phát hiện luôn là 0 độ.
Makalele

@Makalele Có phải vấn đề này cũng xảy ra trong khi chụp ảnh và đính kèm qua WhatsApp?
Manoj Perumarath

Tôi không sử dụng WhatsApp vì vậy tôi không thể nói, nhưng hầu hết có lẽ là có. Đó là bởi vì nó thậm chí còn xảy ra trong ứng dụng ảnh chứng khoán (Google Stock Camera).
Makalele

45

Thật dễ dàng để phát hiện hướng hình ảnh và thay thế bitmap bằng cách sử dụng:

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

Để tránh hết ký ức với những hình ảnh lớn, tôi khuyên bạn nên bán lại hình ảnh bằng cách sử dụng:

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

Không thể sử dụng ExifInterface để nhận định hướng vì sự cố hệ điều hành Android: https://code.google.com.vn/p/android/issues/detail?id=19268

Và đây là 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
Phương thức tính
toánSSSSSS

1
@madhukotagiri ở đây bạn có một ví dụ về cách triển khai để tính toánInSampleSize
Felix

Cảm ơn người đàn ông, bạn chắc chắn là một! Tôi chỉ tự hỏi việc thay đổi kích thước sẽ hữu ích bao nhiêu, nếu hoạt động chỉ được thực hiện đôi khi.
Marino

4
Tham số Uri chọnImage không được sử dụng trong phương thức getRotation (...). Làm thế nào để chúng ta cần sử dụng nó? Cảm ơn bạn.
valerybodak 6/2/2015

1
Tham số 'được chọn' dường như không được sử dụng ở bất cứ đâu. Bất kỳ lý do để được ở đó?
Alex

20

Giải pháp một dòng:

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

Hoặc là

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

Điều này sẽ tự động phát hiện xoay và đặt hình ảnh theo đúng hướng

Picasso là một thư viện rất mạnh để xử lý hình ảnh trong ứng dụng của bạn bao gồm: Biến đổi hình ảnh phức tạp với việc sử dụng bộ nhớ tối thiểu.


1
Giải pháp thú vị
Bhasta Mehta

8
Nó chỉ tải hình ảnh vào một khung nhìn, nó không cung cấp cho bạn một bitmap hoặc một tệp mà bạn có thể thao tác hoặc tải lên máy chủ.
lỗ hổng

4
Hình ảnh hiển thị của nó nhấp như nó là. Nó không được quay theo yêu cầu.
seema

1
@Flawyte bạn có thể làm điều đó bằng cách tải tệp vào mục tiêu thay vì xem với hàm gọi lại trả về bitmap đã cắt / thay đổi kích thước: Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). Vào (đích); where target = new Target () {Ghi đè khoảng trống công khai trênBitmapLoaded (Bitmap bitmap, Picasso.LoadedFrom từ) {
Voytez

vấn đề tôi vẫn gặp phải là mất vài giây để hiển thị hình ảnh
Anu

12

Tôi đã dành rất nhiều thời gian để tìm kiếm giải pháp cho việc này. Và cuối cùng quản lý để làm điều này. Đừng quên upvote @Jason Robinson trả lời vì câu trả lời của tôi dựa trên anh ấy.

Vì vậy, điều đầu tiên, bạn sholuld biết rằng vì Android 7.0 chúng ta phải sử dụng FileProvidervà một cái gì đó được gọi ContentUri, nếu không, bạn sẽ gặp một lỗi khó chịu khi cố gắng gọi ra Intent. Đây là mã mẫu:

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

Phương pháp getUriFromPath(Context, String)dựa trên phiên bản người dùng của Android tạo FileUri (file://...)hoặc ContentUri (content://...)ở đó là:

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

Sau khi onActivityResultbạn có thể bắt được nóuri hình ảnh được lưu bằng camera, nhưng bây giờ bạn phải phát hiện xoay camera, ở đây chúng tôi sẽ sử dụng câu trả lời @Jason Robinson đã được sửa đổi:

Đầu tiên chúng ta cần tạo ExifInterfacedựa trênUri

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

Mã trên có thể được đơn giản hóa, nhưng tôi muốn hiển thị tất cả mọi thứ. Vì vậy, từ FileUrichúng tôi có thể tạo ExifInterfacedựa trên String path, nhưng từ ContentUrichúng tôi không thể, Android không hỗ trợ điều đó.

Trong trường hợp đó, chúng ta phải sử dụng hàm tạo khác dựa trên InputStream. Hãy nhớ rằng hàm tạo này không có sẵn theo mặc định, bạn phải thêm thư viện bổ sung:

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

Bây giờ chúng ta có thể sử dụng getExifInterfacephương pháp để có được góc của chúng ta:

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

Bây giờ bạn có Góc để xoay hình ảnh của bạn đúng cách :).


2
triển khai 'androidx.exifinterface: exifinterface: XXX' Đây là dành cho những người đang sử dụng androidx. cảm ơn bạn đã đăng bài
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;
    }
}

Giải pháp hoàn hảo Haresh Bhai
Sagar Pithiya

9

Bạn chỉ có thể đọc hướng của cảm biến máy ảnh như được Google chỉ định trong tài liệu: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristic.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.

Mã mẫu:

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

Jason Robinson câu trả lời và Sami Eltamawy câu trả lời là xuất sắc.

Chỉ cần một cải tiến để hoàn thành aproach, bạn nên sử dụng compat ExifInterface.

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

Bạn sẽ có thể khởi tạo ExifInterface (API pior <24) với InputStream(từContentResolver ) thay vì đường dẫn uri tránh "Không tìm thấy ngoại lệ tệp"

https://android-developers.googleblog.com/2016/12/int sinhing-the-exifinterface-support-l Library.html


4

Thông thường nên giải quyết vấn đề với ExifInterface , như @Jason Robinson đã đề xuất. Nếu phương pháp này không hiệu quả, bạn có thể thử tra cứu Định hướng của hình ảnh mới nhất được chụp ...

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
Tôi nghĩ rằng mã này chỉ phát hiện trong những gì xoay vòng độ xảy ra. Bây giờ tôi có thể làm điều đó, nhưng không thể thực hiện tác vụ tiếp theo tức là xoay hình ảnh.
Shirish Herwade

Bạn đúng, nhưng bạn đã không yêu cầu xoay vòng trong Chủ đề này, vì vậy hãy giữ cho nó sạch sẽ;) Đó là lý do tại sao tôi đặt câu trả lời của tôi cho vấn đề xoay vòng của bạn vào Chủ đề khác của bạn ... Hy vọng điều đó có ích, nó hoạt động cho tôi: stackoverflow.com/questions/14123809/ Mạnh
Chris Conway

4

Đáng buồn thay, câu trả lời @ jason-robinson ở trên không làm việc cho tôi.

Mặc dù chức năng xoay hoạt động hoàn hảo:

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

Tôi đã phải làm như sau để có được định hướng vì định hướng Exif luôn là 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

Tốt hơn nên cố gắng chụp ảnh theo một định hướng cụ thể.

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

Để có kết quả tốt nhất, hãy đưa ra hướng cảnh quan trong hoạt động của người quay phim.


xin lỗi, nó không hoạt động Trong thực tế trên tab, mỗi lần sau khi kết thúc thực hiện onActivityResult, lạ là onCreate được gọi.
Shirish Herwade

1
xin lỗi, vấn đề là như vậy
Shirish Herwade



1

Câu trả lời được chọn sử dụng phương pháp phổ biến nhất được trả lời cho câu hỏi này và các câu hỏi tương tự. Tuy nhiên, nó không hoạt động với cả camera trước và sau trên Samsung. Đối với những người tìm kiếm một giải pháp hoạt động trên cả camera trước và sau cho Samsung và các nhà sản xuất lớn khác, câu trả lời này của nvhausid thật tuyệt vời:

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

Đối với những người không muốn nhấp qua, phép thuật có liên quan là sử dụng CameraInfo thay vì dựa vào 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);

Mã đầy đủ trong liên kết.


không, xoay sai ở các góc khác nhau (smasung s7). Ý tôi là bộ sưu tập tất nhiên
djdance

1

Điều này có thể không cần phải nói nhưng luôn nhớ rằng bạn có thể xử lý một số vấn đề xử lý hình ảnh này trên máy chủ của mình. Tôi đã sử dụng các câu trả lời giống như những câu trả lời trong chủ đề này để xử lý việc hiển thị hình ảnh ngay lập tức. Tuy nhiên, ứng dụng của tôi yêu cầu hình ảnh được lưu trữ trên máy chủ (đây có thể là một yêu cầu phổ biến nếu bạn muốn hình ảnh tồn tại khi người dùng chuyển đổi điện thoại).

Các giải pháp có trong nhiều luồng liên quan đến chủ đề này không thảo luận về việc thiếu dữ liệu EXIF ​​không tồn tại nén hình ảnh của Bitmap, có nghĩa là bạn sẽ cần phải xoay hình ảnh mỗi khi máy chủ của bạn tải nó. Ngoài ra, bạn có thể gửi dữ liệu định hướng EXIF ​​đến máy chủ của mình và sau đó xoay hình ảnh ở đó nếu cần.

Tôi đã dễ dàng hơn trong việc tạo ra một giải pháp lâu dài trên máy chủ vì tôi không phải lo lắng về đường dẫn tệp bí mật của Android.


Bạn có thể xoay nó một lần tại thời điểm chụp ảnh và lưu nó theo cách đó để nó không bao giờ cần phải xoay nữa không?
jk7

Vâng, bạn có thể và đó thực sự là quá trình tôi đã thực hiện cuối cùng. Tôi đã gặp sự cố khi lấy đường dẫn tệp từ hình ảnh trên điện thoại Android sẽ cho phép tôi làm điều đó. Đây là câu trả lời đã giúp: stackoverflow.com/a/36714242/5443056
Braden Holt

1

Giải pháp đơn giản nhất cho vấn đề này:

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

Tôi đang lưu hình ảnh ở định dạng jpg.


0

Đây là Xamarin.Android phiên bản:

Từ câu trả lời của @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;
    }
}

Sau đó, calculateInSampleSizephương pháp:

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

Từ câu trả lời của @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

Nếu bạn đang sử dụng Fresco, bạn có thể sử dụng cái này -

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

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

Điều này sẽ tự động xoay hình ảnh dựa trên dữ liệu Exif.

Nguồn: https://frescolib.org/docs/rotation.html


0

Mã bên dưới làm việc với tôi, nó nhận bitmap từ tệpUri và thực hiện sửa lỗi xoay nếu cần:

    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

Có câu trả lời cho vấn đề này mà không cần sử dụng ExifInterface . Chúng ta có thể xoay camera của camera trước hoặc camera sau bất cứ khi nào bạn đang sử dụng sau đó trong khi tạo Bitmap, chúng ta có thể xoay bitmap bằng 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;
}

Sau khi tính toán vòng quay, bạn có thể xoay bitmap như bên dưới:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm nên là bitmap của bạn.

Nếu bạn muốn biết xoay vòng camera trước của mình, chỉ cần thay đổi Camera.CameraInfo.CAMERA_FACING_BACK thành Camera.CameraInfo.CAMERA_FACING_FRONT ở trên.

Tôi hi vọng cái này giúp được.


1
Câu trả lời khủng khiếp nhưng tôi vô tình nâng cao. Mã này giả định mọi hình ảnh từ bộ sưu tập của bạn được tạo bằng máy ảnh của bạn . Đây không phải là trường hợp
Zun

-1

Tôi đã tạo một chức năng mở rộng Kotlin để đơn giản hóa hoạt động cho các nhà phát triển Kotlin dựa trên câu trả lời của @Jason Robinson. Tôi hy vọng nó sẽ giúp.

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
tuyệt vời nhưng bị vấn đề giống như tất cả các giải pháp, như tiện ích mở rộng hoặc chức năng - không hoạt động trên Android 10.
Lior Iluz

-2

Có một lệnh đơn giản hơn để sửa lỗi này.

Chỉ cần thêm sau yourImageView.setBitmap (bitmap); thisImageView.setRotation (90);

Cái này cố định của tôi. Hy vọng nó giúp !


6
Như OP đã nêu, một số thiết bị không xoay hình ảnh, một số thiết bị xoay 90 độ, một số 180, ..v.v. Vì vậy, luôn luôn xoay nó 90 sẽ không chính xác trong một số trường hợp.
jk7

-8

cái này làm việc cho tôi

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

lol cái quái gì thế này Làm thế quái nào bạn biết ảnh được chụp bằng máy ảnh là -90 / 90/0 / ... Người dùng có thể đang chụp ảnh làm phong cảnh và bất kể bạn sẽ xoay cái gì ... lmao
Alex

Trong trường hợp này, nó hoạt động với tôi vì trong trường hợp của tôi, người dùng sẽ luôn chụp ảnh với điện thoại theo chiều dọc.
Christian Eduardo Galdamez
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.