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

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

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

#基本的な流れ

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

```java:example1

File file = new File(originalFilePath);

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

resizedFile = getTemporaryFile(file, matrix);
```

#処理メソッド

## リサイズ

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

```java: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](http://developer.android.com/reference/android/media/ExifInterface.html)**を活用して、マトリクスをセットします。**[getAttributeInt](http://developer.android.com/reference/android/media/ExifInterface.html#getAttributeInt(java.lang.String, int))(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);**の処理で、Exifの向き情報のタグ値を取得します。

```java: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](http://developer.android.com/reference/android/graphics/Bitmap.html).[createBitmap](http://developer.android.com/reference/android/graphics/Bitmap.html#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean))(originalPicture, 0, 0, width, height, matrix, true);**の部分です。このメソッドは、デコード済みのBitmap（originalPicture）を元に、指定した範囲（0, 0, width, height = 画像全体の範囲）をクロップし、matrixで変形することができます。trueをつけることで、ジャギー（ギザギザ）の目立たない滑らかな変形が可能です。

```java: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起業日記](http://dqn.sakusakutto.jp/2009/02/jpegexiforientaion.html)
[Android で Bitmap を安全に操作する(2) ～縮小・ EXIF 情報による回転～ | UB Lab.](http://www.united-bears.co.jp/blog/archives/947)
[Y.A.M の 雑記帳: Exif](http://y-anz-m.blogspot.jp/2011/03/exif.html)
[Bitmap | Android Developers](http://developer.android.com/reference/android/graphics/Bitmap.html)
[BitmapFactory | Android Developers](http://developer.android.com/reference/android/graphics/BitmapFactory.html)
accessed 2015-07-02