카메라 의도를 사용하여 캡처 한 이미지가 Android의 일부 기기에서 회전되는 이유는 무엇입니까?


376

이미지를 캡처하여 이미지보기로 설정하고 있습니다.

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
    super.onActivityResult(requestCode, resultCode, intentFromCamera);

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

그러나 문제는 회전 할 때마다 일부 장치의 이미지입니다. 예를 들어 Samsung 장치에서는 잘 작동하지만 Sony Xperia 에서는 이미지가 90도 회전하고 Toshiba Thrive (태블릿)에서 180도 회전합니다.


1
screenOrientation = "세로": "오리엔테이션"안드로이드 = configChanges : 당신의 활동 menifest 안드로이드에서 이것을 시도
나렌드라 래에게

@nick 그것을하지 지금 이미지가 90 개도 아닌 탭에서 180도 회전 도착하지 작업,
Shirish Herwade

1
카메라 앱을 다루기 위해 내부 의도를 사용하면 이미지가 회전합니다. 이는 장치를 잡고 이미지를 캡처하는 방법에 따라 다릅니다. 따라서 사용자가 특정 방식으로 이미지를 찍도록 제한 할 수 있습니다. 즉, 사용자는 항상 가로 또는 세로로 장치를 잡고 이미지를 캡처하게됩니다. 그 후 당신은 당신이 원하는대로 이미지를 얻을 수 있도록 특정 각도로 변경할 수 있습니다. 또는 다른 옵션, 자신의 카메라 응용 프로그램을 확인하십시오.
Narendra Pal

@nick "사용자가 특정 방식으로 이미지를 찍도록 제한 할 수 있습니다"는 방향 = "세로"설정과 동일합니까? 그리고 어떻게 "그 후에 당신이 원하는대로 이미지를 얻기 위해 특정 각도로 변경할 수 있습니다"? 유용한 링크를
제공해 주시겠습니까?

3
캡처 의도는 항상 각 장치마다 특정 방향을 가지며 기본적으로 고정 된 사진 방향 인 기본 카메라 앱을 불러옵니다. 사용자가 기기를 잡는 방법이나 의도를 불러 온 활동의 방향에 의존하지 않습니다.
Alex Cohn

답변:


440

대부분의 휴대 전화 카메라는 가로 방향으로 사진을 세로로 찍으면 결과 사진이 90도 회전합니다. 이 경우 카메라 소프트웨어는 Exif 데이터를 사진을 볼 방향으로 채워야합니다 .

아래 솔루션은 Exif 데이터를 채우는 카메라 소프트웨어 / 장치 제조업체에 따라 다르므로 대부분의 경우 작동하지만 100 % 신뢰할 수있는 솔루션은 아닙니다.

ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                     ExifInterface.ORIENTATION_UNDEFINED);

Bitmap rotatedBitmap = null;
switch(orientation) {

    case ExifInterface.ORIENTATION_ROTATE_90:
        rotatedBitmap = rotateImage(bitmap, 90);
        break;

    case ExifInterface.ORIENTATION_ROTATE_180:
        rotatedBitmap = rotateImage(bitmap, 180);
        break;

    case ExifInterface.ORIENTATION_ROTATE_270:
        rotatedBitmap = rotateImage(bitmap, 270);
        break;

    case ExifInterface.ORIENTATION_NORMAL:
    default:
        rotatedBitmap = bitmap;
}

rotateImage방법 은 다음과 같습니다 .

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
                               matrix, true);
}

1
@JasonRobinson 코드가 나는 실제 방향을 얻는 방법을 배우고 함께 결합하여의에서 이 코드를 난 성공적으로 방향을 관리 할 수 있습니다.
Raditya Kurnianto

함수가 값을 제공하지 못하는 경우 두 번째 매개 변수가 기본값이므로 두 번째 exif.getAttributeInt사용 옵션 ExifInterface.ORIENTATION_UNDEFINED은 거의 동일합니다.
Darpan

5
이 코드는 이미 디스크에 기록 된 이미지를위한 것입니다. 비트 맵을 디스크에 쓰려고이 방법을 사용하면 결과가 나타나지 않습니다.
Thracian

4
항상 0 값을 반환합니다. 실제 오리엔테이션을 얻는 방법을 알려주십시오.
Anurag Srivastava

3
항상 0을 얻는 이유는 무엇입니까?
Navya Ramesan

186

결합함으로써 제이슨 로빈슨답을 함께 펠릭스 의 ' 대답 과 누락 된 부분을 채우고, 여기에이 문제에 대한 최종 완벽한 솔루션입니다 안드로이드에서 테스트 한 후 다음을 수행합니다 안드로이드 4.1 ( 젤리 빈 ), 안드로이드 4.4 ( 킷캣가 ) 및 안드로이드 5.0 ( 롤리팝 ).

단계

  1. 이미지가 1024x1024보다 큰 경우 이미지 크기를 줄입니다.

  2. 이미지가 90도, 180도 또는 270도 회전 한 경우 에만 이미지를 올바른 방향 으로 회전하십시오.

  3. 메모리 목적으로 회전 된 이미지를 재활용하십시오.

코드 부분은 다음과 같습니다.

현재 ContextURI수정하려는 이미지로 다음 메소드를 호출 하십시오.

/**
 * This method is responsible for solving the rotation issue if exist. Also scale the images to
 * 1024x1024 resolution
 *
 * @param context       The current context
 * @param selectedImage The Image URI
 * @return Bitmap image results
 * @throws IOException
 */
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
        throws IOException {
    int MAX_HEIGHT = 1024;
    int MAX_WIDTH = 1024;

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(context, img, selectedImage);
    return img;
}

CalculateInSampleSize앞서 언급 한 소스 의 방법은 다음과 같습니다 .

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

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

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

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

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

        final float totalPixels = width * height;

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

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}

그런 다음 현재 이미지 방향을 확인하여 회전 각도를 결정하는 방법 이옵니다.

 /**
 * Rotate an image if required.
 *
 * @param img           The image bitmap
 * @param selectedImage Image URI
 * @return The resulted Bitmap after manipulation
 */
private static Bitmap rotateImageIfRequired(Context context, Bitmap img, Uri selectedImage) throws IOException {

InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
    ei = new ExifInterface(input);
else
    ei = new ExifInterface(selectedImage.getPath());

    int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            return rotateImage(img, 90);
        case ExifInterface.ORIENTATION_ROTATE_180:
            return rotateImage(img, 180);
        case ExifInterface.ORIENTATION_ROTATE_270:
            return rotateImage(img, 270);
        default:
            return img;
    }
}

마지막으로 회전 방법 자체

private static Bitmap rotateImage(Bitmap img, int degree) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
    img.recycle();
    return rotatedImg;
}

이 유용한 질문을 한 Shirish Herwade 와 그들의 노력에 대한 답변에 투표하는 것을 잊지 마십시오 .


2
완벽하게 나를 위해 Woking. 감사합니다
Shohel Rana

1
rotateImageIfRequired () 메소드가 잘 작동합니다.
mapo

5
나를 위해 작동하지 않습니다. 때로는 휴대 전화에서 인물 사진, 때로는 풍경 사진을 제공하지만 감지 된 방향은 항상 0 도입니다.
Makalele

@Makalele이 문제는 사진을 찍고 WhatsApp을 통해 첨부 할 때도 발생합니까?
Manoj Perumarath '

나는 WhatsApp을 사용하지 않으므로 말할 수는 없지만 대부분 가능합니다. 재고 사진 앱 (Google Stock Camera)에서도 발생하기 때문입니다.
Makalele

45

이미지 방향을 쉽게 감지하고 다음을 사용하여 비트 맵을 교체하십시오.

 /**
 * Rotate an image if required.
 * @param img
 * @param selectedImage
 * @return
 */
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {

    // Detect rotation
    int rotation = getRotation(context, selectedImage);
    if (rotation != 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    else{
        return img;
    }
}

/**
 * Get the rotation of the last image added.
 * @param context
 * @param selectedImage
 * @return
 */
private static int getRotation(Context context,Uri selectedImage) {

    int rotation = 0;
    ContentResolver content = context.getContentResolver();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                       new String[] { "orientation", "date_added" },
                                       null, null, "date_added desc");

    if (mediaCursor != null && mediaCursor.getCount() != 0) {
        while(mediaCursor.moveToNext()){
            rotation = mediaCursor.getInt(0);
            break;
        }
    }
    mediaCursor.close();
    return rotation;
}

큰 이미지의 메모리 부족을 피하려면 다음을 사용하여 이미지 크기를 조정하는 것이 좋습니다.

private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
    throws IOException {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(img, selectedImage);
    return img;
}

https://code.google.com/p/android/issues/detail?id=19268 이므로 Android OS 문제로 인해 ExifInterface를 사용하여 방향을 얻을 수 없습니다.

그리고 여기에 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
여기에 calculateInSampleSize 메소드는 무엇입니까
madhu kotagiri

1
@madhukotagiri 여기에 calculateInSampleSize에 대한 구현 예제가 있습니다. gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix

고마워요, 당신은 확실히 하나입니다! 작업이 가끔 수행되는 경우 크기 조정이 얼마나 유용한 지 궁금합니다.
Marino

4
Uri selectedImage 매개 변수가 getRotation (...) 메소드에서 사용되지 않습니다. 어떻게 사용해야합니까? 감사합니다.
valerybodak

1
'selectedImage'매개 변수가 어디에도 사용되지 않는 것 같습니다. 거기에 어떤 이유가 있습니까?
Alex

20

한 줄 솔루션 :

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

또는

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

회전을 자동 감지하고 올바른 방향으로 이미지를 배치합니다.

Picasso는 앱에서 이미지를 처리 ​​할 수있는 매우 강력한 라이브러리입니다 . 최소한의 메모리 사용으로 복잡한 이미지 변환.


1
흥미로운 솔루션
Bhavik Mehta

8
이미지를 뷰에로드하기 만하면 서버에서 조작하거나 업로드 할 수있는 비트 맵이나 파일을 제공하지 않습니다.
flawyte

4
이미지를 그대로 클릭하여 표시합니다. 필요에 따라 회전하지 않습니다.
seema

1
@Flawyte 당신은 자르기 / 크기 조정 된 비트 맵을 반환하는 콜백을 사용하여보기 대신 대상에 파일을로드하여 파일을로드함으로써 그렇게 할 수 있습니다 : Picasso.with (this) .load (cropUriToLoad.resize (1080, 810) .centerInside (). into (target); 여기서 target = new Target () {public void onBitmapLoaded (Bitmap 비트 맵, Picasso.LoadedFrom from) 무시
voytez

내가 아직도 직면하는 문제는 이미지를 표시하는 데 몇 초가 걸린다는 것입니다
Anu

12

나는 이것에 대한 해결책을 찾기 위해 많은 시간을 보냈다. 그리고 마침내이 작업을 수행했습니다. @Jason Robinson의 답변을 찬성하는 것을 잊지 마십시오.

첫 번째로, 당신은 안드로이드 7.0 이후 우리가 사용해야 FileProvider하고 무언가를 호출 해야한다는 것을 알고 있어야합니다. 그렇지 않으면를 ContentUri호출하려고하면 성가신 오류가 발생합니다 Intent. 이것은 샘플 코드입니다.

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);

방법 getUriFromPath(Context, String)안드로이드의 사용자 버전으로 만들 FileUri (file://...)거나 ContentUri (content://...)하고있다 :

public Uri getUriFromPath(Context context, String destination) {
    File file =  new File(destination);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
    } else {
        return Uri.fromFile(file);
    }
}

후에 onActivityResult당신이 것을 잡을 수있는 uri이미지를 카메라로 저장되는 경우,하지만 지금은 당신이 카메라 회전을 감지해야, 우리가 moddified 사용 @ 제이슨 로빈슨 답변 :

먼저 다음을 ExifInterface기반으로 만들어야 합니다Uri

@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
    try {
        String path = uri.toString();
        if (path.startsWith("file://")) {
            return new ExifInterface(path);
        }
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (path.startsWith("content://")) {
                InputStream inputStream = context.getContentResolver().openInputStream(uri);
                return new ExifInterface(inputStream);
            }
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

위의 코드는 단순화 할 수 있지만 모든 것을 보여주고 싶습니다. 따라서 FileUri우리는를 ExifInterface기반으로 만들 수는 String path있지만 ContentUriAndroid에서는 지원하지 않습니다.

이 경우에 기반한 다른 생성자를 사용해야합니다 InputStream. 이 생성자는 기본적으로 사용할 수 없으므로 라이브러리를 추가해야합니다.

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

이제 getExifInterfacemethod를 사용 하여 각도를 얻을 수 있습니다 .

public float getExifAngle(Context context, Uri uri) {
    try {
        ExifInterface exifInterface = getExifInterface(context, uri);
        if(exifInterface == null) {
            return -1f;
        }

        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90f;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180f;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270f;
            case ExifInterface.ORIENTATION_NORMAL:
                return 0f;
            case ExifInterface.ORIENTATION_UNDEFINED:
                return -1f;
            default:
                return -1f;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
        return -1f;
    }
}

이제 이미지를 올바르게 회전시킬 각도가 있습니다 :).


2
구현 'androidx.exifinterface : exifinterface : XXX'이것은 androidx를 사용하는 사람들을위한 것입니다. 게시 해 주셔서 감사합니다
Doongsil

11
// Try this way,hope this will help you to solve your problem...

activity_main.xml

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

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

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

    </LinearLayout>
</LinearLayout>

MainActivity.java

    public class MainActivity extends Activity {

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

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

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

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

    }

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

    }

    private String getRightAngleImage(String photoPath) {

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

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

            return rotateImage(degree,photoPath);

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

        return photoPath;
    }

    private String rotateImage(int degree, String imagePath){

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

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

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

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

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

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

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

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

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

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

    }

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

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

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

    public String getImagePath() {
        return imgPath;
    }
}

완벽한 솔루션 Haresh Bhai
Sagar Pithiya

9

https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html 문서에서 Google이 표시 한 카메라 센서의 방향을 읽을 수 있습니다.

SENSOR_ORIENTATION

Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.

Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.

Units: Degrees of clockwise rotation; always a multiple of 90

Range of valid values:
0, 90, 180, 270

This key is available on all devices.

샘플 코드 :

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

6

Jason Robinson의 답변 과 Sami Eltamawy의 답변 이 우수 합니다.

aproach를 완성하기 위해 개선 된 것이라면, compat ExifInterface를 사용해야합니다.

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

"파일을 찾을 수 없음 예외"를 피하면서 uri 경로 대신 InputStream(from ContentResolver)으로 ExifInterface (pior API <24)를 인스턴스화 할 수 있습니다.

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


4

일반적으로으로 문제를 해결하는 것이 좋습니다 ExifInterface @ 제이슨 로빈슨이 제안했던 것처럼. 이 방법으로 문제가 해결되지 않으면 가장 최근에 촬영 한 이미지 의 방향 을 찾아보십시오 .

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

    if(cursor.moveToFirst()){
        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        cursor.close();
        return orientation;
    } else {
        return 0;
    }
}

1
이 코드는 회전이 발생한 정도만 감지한다고 생각합니다. 이제는 할 수 있지만 다음 작업에서는 이미지를 회전시킬 수 없습니다.
Shirish Herwade

맞습니다.하지만이 스레드에서 회전을 요청하지 않았으므로 깨끗하게 유지하십시오.;) 회전 문제에 대한 답변을 다른 스레드에 넣는 이유는 ... 나 : stackoverflow.com/questions/14123809/…
Chris Conway

4

슬프게도 위의 @ jason-robinson 답변이 저에게 효과적이지 않았습니다.

회전 기능이 완벽하게 작동하지만 :

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
            true);
}

Exif 방향이 항상 0이므로 방향을 얻으려면 다음을 수행해야했습니다.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode,resultCode,data);
    if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
            Uri selectedImage = data.getData();
            String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
            Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
            int orientation = -1;
            if (cur != null && cur.moveToFirst()) {
                    orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
            }
            InputStream imageStream = getContentResolver().openInputStream(selectedImage);
            Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
            switch(orientation) {
                    case 90:
                            bitmap = rotateImage(chosen_image_bitmap, 90);
                            break;
                    case 180:
                            bitmap = rotateImage(chosen_image_bitmap, 180);
                            break;
                    case 270:
                            bitmap = rotateImage(chosen_image_bitmap, 270);
                            break;
                    default:
                            break;
            }
            imageView.setImageBitmap(bitmap );

1
alwasys 0, 삼성 7
djdance

2

특정 방향으로 사진을 찍는 것이 좋습니다.

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

최상의 결과를 얻으려면 카메라 뷰 활동에서 가로 방향을 지정하십시오.


죄송합니다. 작동하지 않습니다. 실제로 onActivityResult의 실행을 마친 후에는 이상하게도 onCreate가 호출됩니다.
Shirish Herwade

1
죄송합니다, 문제는 그대로
Shirish Herwade



1

선택된 답변은 이와 유사한 질문에 대한 가장 일반적인 방법을 사용합니다. 그러나 삼성의 전면 및 후면 카메라에서는 작동하지 않습니다. 삼성 및 기타 주요 제조업체의 전면 및 후면 카메라에서 작동하는 솔루션을 찾는 사람들에게 nvhausid의 대답은 훌륭합니다.

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

클릭을 원하지 않는 사람들에게는 EXIF에 의존하는 대신 CameraInfo를 사용하는 것이 관련 마술입니다.

Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);

링크의 전체 코드.


아니, 다른 각도에서 잘못된 회전 (smasung s7). 나는 물론 갤러리를 의미한다
djdance

1

말할 필요도 없지만 서버에서 이러한 이미지 처리 문제 중 일부를 처리 할 수 ​​있다는 것을 항상 기억하십시오. 이 스레드에 포함 된 것과 같은 응답을 사용하여 이미지의 즉각적인 표시를 처리했습니다. 그러나 내 응용 프로그램에서는 서버에 이미지를 저장해야합니다 (사용자가 전화를 전환 할 때 이미지를 유지하려는 경우 일반적인 요구 사항 일 수 있음).

이 주제와 관련된 많은 스레드에 포함 된 솔루션은 비트 맵의 ​​이미지 압축에서 살아남지 않는 EXIF ​​데이터의 지속성 부족에 대해서는 다루지 않습니다. 즉, 서버가 이미지를로드 할 때마다 이미지를 회전해야합니다. 또는 EXIF ​​방향 데이터를 서버로 보낸 다음 필요한 경우 이미지를 회전시킬 수 있습니다.

안드로이드의 비밀 파일 경로에 대해 걱정할 필요가 없기 때문에 서버에서 영구적 인 솔루션을 만드는 것이 더 쉬웠습니다.


이미지를 캡처 할 때 한 번만 회전시켜 저장하면 다시 회전 할 필요가 없습니까?
jk7

그러나 당신은 할 수 있고 그것은 실제로 결국 구현 한 프로세스입니다. Android 휴대 전화의 이미지에서 파일 경로를 가져 오는 데 문제가있었습니다. 이것이 도움이 된 답변입니다. stackoverflow.com/a/36714242/5443056
Braden Holt

1

이 문제에 대한 가장 간단한 해결책 :

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

이미지를 jpg 형식으로 저장하고 있습니다.


0

여기 Xamarin.Android버전 .

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

그런 다음 calculateInSampleSize방법 :

int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH)
{
    float h = options.OutHeight;
    float w = options.OutWidth;
    var inSampleSize = 1;

    if (h > reqH || w > reqW)
    {
        if (reqH == 0) inSampleSize = (int)Math.Floor(w / reqW);
        else if (reqW == 0) inSampleSize = (int)Math.Floor(h / reqH);
        else
        {
            var hRatio = (int)Math.Floor(h / reqH);
            var wRatio = (int)Math.Floor(w / reqW);
            inSampleSize = false ? Math.Max(hRatio, wRatio) : Math.Min(hRatio, wRatio);
        }
    }

    return inSampleSize;
}

@Sami Eltamawy의 답변에서 :

Bitmap handleSamplingAndRotationBitmap(string imagePath)
{
    var maxHeight = 1024;
    var maxWidth = 1024;

    var options = new BitmapFactory.Options();
    options.InJustDecodeBounds = true;
    BitmapFactory.DecodeFile(imagePath, options);

    options.InSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);

    options.InJustDecodeBounds = false;

    var bitmap = BitmapFactory.DecodeFile(imagePath, options);

    bitmap = rotateIfRequired(bitmap, imagePath);

    return bitmap;
}

0

Fresco를 사용하는 경우 다음을 사용할 수 있습니다.

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

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

Exif 데이터를 기준으로 이미지가 자동으로 회전합니다.

출처 : https://frescolib.org/docs/rotation.html


0

아래 코드는 나와 함께 작동했으며 fileUri에서 비트 맵을 가져 와서 필요한 경우 회전 고정을 수행합니다.

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

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

0

ExifInterface 사용하지 않고이 문제에 대한 답변을 얻었 습니다 . 비트 맵을 만드는 동안 카메라를 전면 카메라 또는 후면 카메라 중 어느 쪽이든 회전시킬 수 있습니다. 우리는 Matrix.postRotate (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;
}

회전을 계산 한 후 아래와 같이 비트 맵을 회전 할 수 있습니다.

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

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

Herare bm 이 비트 맵이어야합니다.

전면 카메라의 회전을 알고 싶다면 Camera.CameraInfo.CAMERA_FACING_BACKCamera.CameraInfo.CAMERA_FACING_FRONT로 변경 하십시오 .

이게 도움이 되길 바란다.


1
끔찍한 대답이지만 우연히 공언했습니다. 이 코드는 갤러리의 모든 이미지로 만든 것으로 가정 하여 카메라. 사실이 아닙니다
Zun

-1

@Jason Robinson의 답변을 기반으로 Kotlin 개발자의 작업을 단순화하는 Kotlin 확장 기능을 만들었습니다. 도움이 되길 바랍니다.

fun Bitmap.fixRotation(uri: Uri): Bitmap? {

    val ei = ExifInterface(uri.path)

    val orientation: Int = ei.getAttributeInt(
        ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_UNDEFINED
    )

    return when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage( 90f)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage( 180f)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage( 270f)
        ExifInterface.ORIENTATION_NORMAL -> this
        else -> this
    }
}

fun Bitmap.rotateImage(angle: Float): Bitmap? {
    val matrix = Matrix()
    matrix.postRotate(angle)
    return Bitmap.createBitmap(
        this, 0, 0, width, height,
        matrix, true
    )
}

1
확장 또는 기능과 같은 모든 솔루션과 똑같은 문제가 있지만 Android 10에서는 작동하지 않습니다.
Lior Iluz

-2

이 오류를 해결하는 더 간단한 명령이 있습니다.

단순히 yourImageView.setBitmap (bitmap); 뒤에 추가하십시오. this yourImageView.setRotation (90);

이 고정 광산. 그것이 도움이되기를 바랍니다!


6
OP가 언급했듯이 일부 장치는 이미지를 회전하지 않으며 일부 장치는 90도, 약 180 등을 회전시킵니다. 따라서 항상 90도 회전하는 것은 올바르지 않습니다.
jk7

-8

이것은 나를 위해 일했다

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

이 괴물이 뭐야? 도대체 어떻게 당신이 카메라로 촬영 한 사진입니다을 알 것입니다 -90 / 90 / 0 / ... 사용자는 풍경 등의 사진과 상관없이 복용 할 수있는 무엇을 거 회전이 ... LMAO
알렉스

이 경우 사용자는 항상 휴대 전화로 세로로 사진을 찍기 때문에 나에게 효과적이었습니다.
Christian Eduardo Galdamez
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.