Help us understand the problem. What is going on with this article?

画像の向きを考慮したリサイズ機能の実装

More than 5 years have passed since last update.

カメラで撮影した画像は、基本的には横向きで保存されているようです。そのため、縦構図の写真をそのまま処理すると、横向きで処理されてしまいます。この問題は、画像のメタデータ(Exif)に含まれる「向き」に関する情報を利用することで解決できます。

今回は、大きな画像を小さくリサイズし、一時ディレクトリに保存する流れについて述べます。利用シーンとしては、ネット上のストレージにアップロードする前の下ごしらえと言った感じです。

基本的な流れ

処理の流れは以下のとおりです。画像を処理するマトリクスを用意し、マトリクスにリサイズ比と回転の値をセットします。リサイズと回転の処理は、それぞれメソッドにまとめることにします。メソッドの詳細は、下で述べます。

example1
File file = new File(originalFilePath);

Matrix matrix = new Matrix();
matrix = getResizedMatrix(file, matrix);
matrix = getRotatedMatrix(file, matrix);

resizedFile = getTemporaryFile(file, matrix);

処理メソッド

リサイズ

以下は、画像リサイズ用のメソッドです。ここでは、画像のサイズに関する情報以外は必要ないため、BitmapFactory.Optionsのインスタンスを作成し、inJustDecodeBounds = true;を設定します。このオプションを付けると、画像そのもののデータは読み込まないため、処理が早くなります。

example2
/**
 * リサイズするマトリクスを取得
 * 縮小の場合のみ、縮小のマトリクスをセットして返す
 *
 * @param file 入力画像
 * @param matrix 元のマトリクス
 * @return matrix リサイズ後のマトリクス
 */
private Matrix getResizedMatrix(File file, Matrix matrix) {
    // リサイズチェック用にメタデータ読み込み
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(file.getPath(), options);
    int height = options.outHeight;
    int width = options.outWidth;

    // リサイズ比の取得(画像の短辺がMAX_PIXELになる比を求めます)
    float scale = Math.max((float) MAX_PIXEL / width, (float) MAX_PIXEL / height);

    // 縮小のみのため、scaleは1.0未満の場合のみマトリクス設定
    if (scale < 1.0) {
        matrix.postScale(scale, scale);
    }

    return matrix;
}

画像の回転

画像に含まれるExif情報を利用して、画像を回転します。それぞれの詳しい説明は別のサイトにお任せして、とりあえずは、Androidに用意されているExifInterfaceを活用して、マトリクスをセットします。getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);の処理で、Exifの向き情報のタグ値を取得します。

example3
/**
 * 画像の回転後のマトリクスを取得
 *
 * @param file 入力画像
 * @param matrix 元のマトリクス
 * @return matrix 回転後のマトリクス
 */
private Matrix getRotatedMatrix(File file, Matrix matrix){
    ExifInterface exifInterface = null;

    try {
        exifInterface = new ExifInterface(file.getPath());
    } catch (IOException e) {
        e.printStackTrace();
        return matrix;
    }

    // 画像の向きを取得
    int orientation = exifInterface.getAttributeInt(
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_UNDEFINED);

    // 画像を回転させる処理をマトリクスに追加
    switch (orientation) {
        case ExifInterface.ORIENTATION_UNDEFINED:
            break;
        case ExifInterface.ORIENTATION_NORMAL:
            break;
        case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
            // 水平方向にリフレクト
            matrix.postScale(-1f, 1f);
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            // 180度回転
            matrix.postRotate(180f);
            break;
        case ExifInterface.ORIENTATION_FLIP_VERTICAL:
            // 垂直方向にリフレクト
            matrix.postScale(1f, -1f);
            break;
        case ExifInterface.ORIENTATION_ROTATE_90:
            // 反時計回り90度回転
            matrix.postRotate(90f);
            break;
        case ExifInterface.ORIENTATION_TRANSVERSE:
            // 時計回り90度回転し、垂直方向にリフレクト
            matrix.postRotate(-90f);
            matrix.postScale(1f, -1f);
            break;
        case ExifInterface.ORIENTATION_TRANSPOSE:
            // 反時計回り90度回転し、垂直方向にリフレクト
            matrix.postRotate(90f);
            matrix.postScale(1f, -1f);
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            // 反時計回りに270度回転(時計回りに90度回転)
            matrix.postRotate(-90f);
            break;
    }
    return matrix;
}

ファイルの読み込み・処理・保存

最後に、ファイルを読み込み、リサイズ処理を行い、ファイルを保存します。重要な部分は、Bitmap.createBitmap(originalPicture, 0, 0, width, height, matrix, true);の部分です。このメソッドは、デコード済みのBitmap(originalPicture)を元に、指定した範囲(0, 0, width, height = 画像全体の範囲)をクロップし、matrixで変形することができます。trueをつけることで、ジャギー(ギザギザ)の目立たない滑らかな変形が可能です。

exaple4
/**
 * リサイズ・回転後の画像を取得
 * 一時ファイルを作成する
 *
 * @param file オリジナル画像
 * @param matrix 回転・縮小を設定したマトリクス
 * @return file 一時保存先のファイル
 */
private File getTemporaryFile(File file, Matrix matrix) {
    // 元画像の取得
    Bitmap originalPicture = BitmapFactory.decodeFile(file.getPath());
    int height = picture.getHeight();
    int width = picture.getWidth();

    // マトリクスをつけることで縮小、向きを反映した画像を生成
    Bitmap resizedPicture = Bitmap.createBitmap(originalPicture, 0, 0, width, height, matrix, true);

    // 一時ファイルの保存
    try {
        File destination = new File([画像の一時保存先パスファイル名] + ".jpg");
        FileOutputStream outputStream = new FileOutputStream(destination);
        resizedPicture.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
        outputStream.flush();
        outputStream.close();

        // ファイルのインスタンスをリサイズ後のものに変更
        file = destination;
    } catch (IOException e) {
        e.printStackTrace();
    }

    return file;
}

参考資料
JPEGのExifタグ情報のOrientaionの定義の早見表・DQNEO起業日記
Android で Bitmap を安全に操作する(2) ~縮小・ EXIF 情報による回転~ | UB Lab.
Y.A.M の 雑記帳: Exif
Bitmap | Android Developers
BitmapFactory | Android Developers
accessed 2015-07-02

sussan0416
フリーランスのエンジニア。iOSアプリ開発が専門です。
https://sussan-po.com
classi
学校の先生・生徒・保護者向けのB2B2Cの学習支援Webサービス「Classi(クラッシー)」 を開発・運営している会社です。
https://classi.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした