カメラインテントを使用してキャプチャした画像が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
これをあなたの活動のメニフェストで試してくださいandroid:configChanges = "orientation" android:screenOrientation = "portrait"
Narendra Pal

@nick機能しません。画像がタブで180度ではなく90度に回転します
Shirish Herwade

1
カメラアプリを処理するために内部インテントを使用すると、画像が回転します。これは、デバイスを保持して画像をキャプチャする方法に依存します。したがって、ユーザーが特定の方法で画像を撮影するように制限できます。つまり、ユーザーはデバイスを縦または横に構えて常に画像をキャプチャします。その後、特定の角度に変更して、必要に応じて画像を取得できます。または、別のオプションで、独自のカメラアプリを作成します。
Narendra Pal

@nick "ユーザーが特定の方法で画像を撮るように制限できます"は、orientation = "potrait"を設定することと同じですか?そして、「その後、あなたはそれをあなたが望むように特定の角度に変更してあなたが望むように画像を得ることができます」を達成する方法は?役立つリンクを教えてください
Shirish Herwade

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 2014

関数が値を提供できない場合の2番目のパラメーターはデフォルト値であるため、exif.getAttributeInt使用の2番目のオプションExifInterface.ORIENTATION_UNDEFINEDはほとんど同じです。
ダーパン

5
このコードはすでにディスクに書き込まれたイメージ用ですよね?ディスクに書き込まれるビットマップにこのメソッドを使用しても結果が得られません。
トラキア

4
常に0の値が返されます。実際のオリエンテーションの方法を教えてください。
Anurag Srivastava

3
常に0を取得していますが、その理由は何ですか?
Navya Ramesan

186

組み合わせることによりジェイソンロビンソンさんの答えフェリックス s 'の答えをして足りない部分を埋め、ここではこの問題の最終的な完全なソリューションである Android上でそれをテストした後、次の操作を行いますアンドロイド4.1ジェリービーンが)、アンドロイド4.4キットカット)とAndroid 5.0Lollipop)。

手順

  1. 画像が1024x1024より大きい場合は、画像を縮小します。

  2. 90度、180度、または270度回転した場合にのみ、画像を正しい方向に回転させます。

  3. 回転した画像をメモリの目的でリサイクルします。

これがコード部分です:

修正したい現在Contextと画像URIで次のメソッドを呼び出します

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

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

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

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

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

これは、CalculateInSampleSize前述のソースからの方法です

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

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

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

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

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

        final float totalPixels = width * height;

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

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

次に、現在の画像の向きをチェックして回転角度を決定するメソッドが表示されます

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

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

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

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

最後に回転方法自体

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

-彼らの努力と、この役立つ質問をしたShirish Herwadeへの回答に投票することを忘れないでください。


2
それは私にとっては完璧です。ありがとう
Shohel Rana

1
メソッドrotateImageIfRequired()は非常にうまく機能します。
麻浦

5
うまくいきません。時々私の電話は肖像画を、時には風景写真を提供しますが、検出された向きは常に0度です。
マカレル2018年

@Makaleleこの問題は、写真を撮り、WhatsAppを通じて添付するときにも発生しますか?
Manoj Perumarath

私はWhatsAppを使用していないので、言えませんが、おそらくそうです。これは、ストックフォトアプリ(Google Stock Camera)でも発生するためです。
マカレル

45

画像の向きを検出してビットマップを置き換えるのは簡単です。

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

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

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

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

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

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

大きな画像でメモリ不足を回避するには、次の方法で画像を再スケーリングすることをお勧めします。

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

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

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

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

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

Android OSの問題のため、ExifInterfaceを使用して方向を取得することはできません。https://code.google.com/p/android/issues/detail?id = 19268

そしてここに calculateInSampleSize

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

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

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

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

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

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

        final float totalPixels = width * height;

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

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

1
ここでcalculateInSampleSizeメソッドとは
madhu kotagiri

1
@madhukotagiriここに、calculateInSampleSizeの実装例があります:gist.github.com/anonymous/b7ea25fc2bbc54e43616
Felix

ありがとう、間違いなくあなたです!操作がたまにしか実行されない場合、サイズ変更がどれだけ役立つか知りたいだけです。
Marino

4
URIのselectedImageパラメーターがgetRotation(...)メソッドで使用されていません。どのように使用する必要がありますか?ありがとうございました。
valerybodak 2015

1
パラメータ「selectedImage」はどこでも使用されていないようです。そこにいる理由は?
Alex

20

1行のソリューション:

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は、ビューの代わりにファイルをターゲットにロードすることで、クロップ/サイズ変更されたビットマップを返すコールバックを使用してそれを行うことができます。 where target = new Target(){オーバーライドpublic void onBitmapLoaded(Bitmap bitmap、Picasso.LoadedFrom from){
voytez

私がまだ直面している問題は、画像を表示するのに数秒かかることです
Anu

12

私はこれに対する解決策を探すために多くの時間を費やしてきました。そしてようやくこれを実現しました。私は彼に基づいているため、@ Jason Robinsonの回答に投票することを忘れないでください。

したがって、まず最初に、Android 7.0以降ではFileProviderと呼ばれるものを使用する必要があることを知っておくContentUri必要がありますIntent。これはサンプルコードです:

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

getUriFromPath(Context, String)Androidのユーザーバージョンに基づくメソッドの作成FileUri (file://...)または作成ContentUri (content://...)

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

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

カメラで画像が保存されonActivityResultているuri場所を把握できた後、カメラの回転を検出する必要がある場合、ここでは変更された@Jason Robinsonの回答を使用します。

最初にExifInterface基づいて作成する必要がありますUri

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

上記のコードは簡略化できますが、すべてを表示したいと思います。したがって、FileUriExifInterface基づいて作成できますString pathが、ContentUriできないので、Androidはそれをサポートしていません。

その場合、に基づく他のコンストラクタを使用する必要がありますInputStream。このコンストラクターはデフォルトでは使用できないので、ライブラリーを追加する必要があります。

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

これで、getExifInterfaceメソッドを使用して角度を取得できます。

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

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

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

これで、画像を適切に回転させる角度ができました:)。


2
実装 'androidx.exifinterface:exifinterface:XXX'これはandroidxを使用している人向けです。投稿していただきありがとうございます
Doongsil

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

activity_main.xml

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

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

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

    </LinearLayout>
</LinearLayout>

MainActivity.java

    public class MainActivity extends Activity {

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

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

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

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

    }

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

    }

    private String getRightAngleImage(String photoPath) {

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

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

            return rotateImage(degree,photoPath);

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

        return photoPath;
    }

    private String rotateImage(int degree, String imagePath){

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

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

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

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

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

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

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

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

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

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

    }

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

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

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

    public String getImagePath() {
        return imgPath;
    }
}

完璧なソリューションHaresh Bhai
Sagar Pithiya

9

ドキュメントでGoogleによって示されているように、カメラセンサーの向きを読み取るだけです:https : //developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html

SENSOR_ORIENTATION

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

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

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

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

This key is available on all devices.

サンプルコード:

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

6

Jason Robinsonの回答とSami Eltamawyの回答は優れています

アプローチを完了するための改良点として、互換ExifInterfaceを使用する必要があります。

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

ExifInterface(pior API <24)をInputStream(fromContentResolver「ファイルが見つかりません」という例外を回避するために、uriパスの代わりに)

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


4

通常、@ Jason Robinsonが示唆したように、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、2013年

あなたは正しいですが、あなたはこのスレッドで回転を要求しなかったので、それをきれいに保ちましょう;)そのため、私はあなたの回転問題に対する私の答えを他のスレッドに入れました...私: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、samsung 7
djdance

2

特定の方向で写真を撮るようにしてください。

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

最良の結果を得るには、cameraviewアクティビティで横向きを指定します。


申し訳ありませんが、機能しません。実際には、タブ上で、onActivityResultの実行が完了するたびに、奇妙なことにonCreateが呼び出されます。
Shirish Herwade、

1
申し訳ありませんが、問題は
現状のまま

2

Android 4.4(KitKat)で向きを取得する際に問題が発生しExifInterfaceた場合は、URIから取得したパスが間違っている可能性があります。Stack Overflowの質問でpropoerのソリューションを参照してくださいURIから実際のパスを取得、Android KitKatの新しいストレージアクセスフレームワークgetPath


この1つのコメントは私が必要としたものです。どうもありがとうございました。
Joel Nieman、


1

選択された回答は、この質問および類似の質問に回答された最も一般的な方法を使用しています。ただし、Samsungの前面カメラと背面カメラの両方では機能しません。Samsungおよび他の主要メーカーのフロントカメラとバックカメラの両方で機能するソリューションを探している人にとって、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)。当然のI平均ギャラリー
djdance

1

これはおそらく言うまでもありませんが、サーバーでこれらの画像処理の問題のいくつかを処理できることを常に覚えておいてください。このスレッドに含まれているような応答を使用して、画像の即時表示を処理しました。ただし、私のアプリケーションではサーバーに画像を保存する必要があります(ユーザーが電話を切り替えても画像を保持したい場合、これはおそらく一般的な要件です)。

このトピックに関する多くのスレッドに含まれているソリューションでは、ビットマップの画像圧縮に耐えられないEXIFデータの永続性の欠如については説明されていません。つまり、サーバーが画像を読み込むたびに画像を回転させる必要があります。または、EXIF方向データをサーバーに送信し、必要に応じて画像をそこで回転させることもできます。

Androidの秘密のファイルパスを気にする必要がなかったので、サーバーで永続的なソリューションを作成する方が簡単でした。


画像のキャプチャ時に一度回転させ、そのように保存して、再度回転させる必要がないようにすることはできますか?
jk7

できます。それが実際に私が最終的に実装したプロセスです。Androidフォンの画像からファイルパスを取得できないため、ファイルパスを取得できませんでした。これが助けになった答えです。stackoverflow.com
a

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

フレスコを使用している場合、これを使用できます-

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

-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
Alex

私の場合、ユーザーは常に電話を垂直にして写真を撮るので、この場合はうまくいきました。
クリスチャンエドゥアルドガルダメス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.