酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

JPEGの回転角度を取得する

Windows Phone Advent Calendar "ひとり" 2011」第19日目です。 (遅延実行中です)

標準のカメラAPI(CameraCaptureTaskやPhotoCamera)で撮影した写真を、そのままアプリケーションに取り込むと大抵の場合横向きのまま表示されてしまいます。

モバイル向けカメラモジュールは端末に対して横向きに設置されています。撮影を行うと横方向の画像が出力されます。

プレビュー時は90度や180回転して縦方向に合わせるのですが、撮影となると最近のカメラは高画質でわざわざ縦方向に回転してJPEGエンコードを行う時間がもったいないので、「カメラの向きは逆時計まわりに180度回転されているので、表示するときは時計まわりに180度回転してね」というExifと呼ばれるタグを付与します。

ピクチャーハブでの表示は、正しくこのExifタグが解釈されているようですが、BitmapImageでのデコードではExifタグを解釈してくれず、横向きのままになってしまうという事です。

@tmytにExifタグの弄り方を教えてもらったので、実装してみました。

コード

まずJPEGファイルにExifタグが付与されているかどうかを調べます。

/// <summary>
/// Exifタグが付与されているかどうか調べる
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
int FindExifMaker(Stream stream) {
    int n = 0;
    byte[] buf = new byte[2];
    while (true) {
        if (n + 2 > stream.Length) break;
        stream.Seek(n, SeekOrigin.Begin);
        stream.Read(buf, 0, 2);
        if (buf[0] == 0xFF && buf[1] == 0xE1) {
            return n;
        }
        n++;
        if (n > 2048) break;
    }
    return -1;
}

次に、Exifタグが含まれているのが分かりました。Exifタグの中からOrientation(0x0112)タグがあるかどうかを調べます。

/// <summary>
/// </summary>
/// <returns>回転種別</returns>
public int GetJpegOrientation(Stream stream) {
    var exifIdx = FindExifMaker(stream);
    if (exifIdx < 0) {
        return -1;
    }
    stream.Seek(exifIdx, SeekOrigin.Begin);

    int n = 0;
    byte[] buf = new byte[2];
    while (true) {
        if (n + 2 > stream.Length) break;
        stream.Seek(n, SeekOrigin.Begin);
        stream.Read(buf, 0, 2);
        if (buf[0] == 0x01 && buf[1] == 0x12) {
            n += 2;
            stream.Seek(n, SeekOrigin.Begin);
            stream.Read(buf, 0, 2);
            return buf[0] * 256 + buf[1];
        }

        n++;
        if (n > 2048) break;
    }
    return -1;
}

これで1から8までの回転種別を取得する事が可能です。WriteableBitmapEx等のピクセル操作を行うライブラリを使用して、うまく写真を回転させてあげてください。

回転種別 説明
1 通常
2 左右反転
3 時計回りに180度回転
4 上下反転
5 時計回りに270度回転&上下反転
6 時計回りに90度回転
7 時計回りに90度回転&上下反転
8 時計回りに270度回転

先ほどのコードは、こんな感じで使用すれば良いでしょう。

using System;
using System.IO;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;

namespace JpegRotateCheckTest {
    public partial class MainPage : PhoneApplicationPage {
        // コンストラクター
        public MainPage() {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e) {
            var task = new PhotoChooserTask();
            task.Completed += new EventHandler<PhotoResult>(task_Completed);
            task.Show();
        }

        void task_Completed(object sender, PhotoResult e) {
            if (e.ChosenPhoto == null) return;
            var orientation = GetJpegOrientation(e.ChosenPhoto);

            // 種別に応じて画像を回転させる
        }

あ、@mikiofukuの「wp7/tips/image_get_jpegsize - Windows Phone, Windows 8 Store アプリ by Smart-PDA.net」と合わせてご活用すると良いかもしれません。