Windows Phoneアプリケーション開発入門

第37回 Mangoで追加されたカメラ機能を使ってみよう!(3)

この記事を読むのに必要な時間:およそ 9 分

ここで少し簡単にですが,カメラプレビューの仕組みを説明します。

カメラのプレビュー表示の仕組みは,カメラデバイスのレンズから取り込んだ映像をパラパラ漫画のように表示させて,人間の目から見ると滞りなく動いているように見せています。パラパラ漫画のように画像を切り替える速度の単位は,FPS(Frames Per Second)と呼ばれます。1秒間に何枚の画像をパラパラ表示させるのかを基準とした単位です。

大体のWindows Phone端末に搭載されているカメラの入力フレームレートは,約30FPSです。1秒間に30枚の画像を表示させていることになります。1枚の表示に使える時間は約33ミリ秒です。

この速度でバーコードの解析は難しいので,1秒間に10回解析を行うようにタイマーを定義します。

タイマーは,System.Windows.Threading名前空間のDispatcherTimerクラスを使用します。カメラの初期化完了通知ハンドラの処理の中で,DispatcherTimerのインスタンスを生成してタイマーを開始させます。

// プレビューフレームの解析を行うタイマー
System.Windows.Threading.DispatcherTimer readTimer = null;

// カメラの初期化処理の完了
void  camera_Initialized(object sender, CameraOperationCompletedEventArgs e) {
    // 初期化処理に失敗した場合は何もしない
    if (!e.Succeeded)
        return;

    // カメラの回転角度に合わせてプレビュー表示も回転させる
    Dispatcher.BeginInvoke(() => {
        PreviewBrush.RelativeTransform = new CompositeTransform() {
            CenterX = 0.5, CenterY = 0.5,
            Rotation = camera.Orientation
        };

        ImageEffect.RenderTransform = new CompositeTransform() {
            CenterX = 0.5, CenterY = 0.5,
            Rotation = camera.Orientation
        };

        // 100ミリ秒毎に満了する様にタイマーを開始
        readTimer = new System.Windows.Threading.DispatcherTimer();
        readTimer.Tick += new EventHandler(readTimer_Tick);
        readTimer.Interval = TimeSpan.FromMilliseconds(100);
        readTimer.Start();
    });
}

100ミリ秒ごとにタイマーが満了し,その都度readTimer_Tickメソッドが呼び出されます。先ほど上で定義したFrameBufferクラスを使ってプレビューフレームを取り出してみましょう。

取り出したプレビューフレームをFrameBufferオブジェクトに取り込み,セピア調にエフェクト処理を行います。WriteableBitmapクラスを経由させてImageコントロールにセットします。

// タイマー満了時にプレビューフレームにエフェクト処理
void readTimer_Tick(object sender, EventArgs e) {

    var frame = new FrameBuffer(
        (int)camera.PreviewResolution.Width,
        (int)camera.PreviewResolution.Height);
    camera.GetPreviewBufferArgb32(frame.Buffer);

    // 取り込んだ画像をセピア調にする
    frame.EffectSepiaTone();

    // 取り込んだ画像をWriteableBitmapへセット
    var wb = new WriteableBitmap(frame.Width, frame.Height);
    frame.Buffer.CopyTo(wb.Pixels, 0);

    ImageEffect.Source = wb;
}

以上のコードを実行すると,下図のようにオリジナルのプレビュー(背景)とエフェクト処理後のプレビュー(手前)が表示されます。

画像

EffectSepiaToneメソッドの実装については,上記では特に取り扱いませんでした。セピア調だけでは寂しいので簡単なエフェクトについてご紹介します。readTimer_Tickメソッドにて,セピア調にエフェクト処理を行ったEffectSepiaToneメソッドをこれから紹介するエフェクトメソッドに変更すると更に面白い効果が得られると思います。

簡単に実装できるエフェクト処理のサンプルコードを記載していますので,好みのエフェクト処理を選んで頂ければと思います。

エフェクト処理の紹介:セピア調

画像

昔の写真のように少し色褪せた感じのセピア調になるエフェクト処理です。セピアというのはイカ墨の色のことで,JIS慣用色名ではRGB値0x6B4A2Bで定義されています。RGBのそれぞれの値に直すと(R:107,G:74,B:43)となります。これをR成分を基準(1)として考えると(赤:1,緑:0.7,青:0.4)の比率になります。この比率でグレイスケール化した後に掛けてみましょう。

        /// <summary>
        /// 画像バッファをセピア調化する
        /// </summary>
        public void EffectSepiaTone() {
            for (int i = 0; i < Buffer.Length; i++) {
                int pixel = Buffer[i];

                // RGB値を取り出す
                byte r = Convert.ToByte((pixel >> 16) & 0xff);
                byte g = Convert.ToByte((pixel >> 8) & 0xff);
                byte b = Convert.ToByte(pixel & 0xff);

                // 平均値を求めてグレースケール化する
                byte avg = (byte)((r + g + b) / 3);

                // セピアに近づける為,計算を行う
                r = (byte)(avg * 1);
                g = (byte)(avg * 0.7);
                b = (byte)(avg * 0.4);

                Buffer[i] = 255 << 24 | r << 16 | g << 8 | b;
            }
        }

著者プロフィール

和田健司(わだけんじ)

1982年10月12日生まれ。大阪で働くプログラマ。Microsoft MVP for Device Application Development(Jul 2010 - Jun 2011)。Windows Mobileに傾倒し今に至る。Windows Mobile向けのTipsを書いています。iPhoneアプリ開発を始めました。嫌いな食べ物はカレー。

URL: http://ch3cooh.jp/
Blog: http://d.hatena.ne.jp/ch3cooh393/