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

第8回Windows phoneで画像エフェクトアプリを作ろう!(1)

はじめに

今や⁠ケータイ⁠にはカメラは外せないデバイスです。Windows phoneにもカメラデバイスが搭載されており、コンシューマ向けのみならず、ビジネス用途でもカメラを活用したアプリケーションが沢山リリースされています。例えば、名刺を撮影して自動でアドレス帳に登録するアプリケーションや、買取対象を撮影して本部に送り査定する買取サービスがあります。

今回は、Windows phoneを使ってカメラで撮影(もしくは撮影済みの写真を選択)して、画像を加工する方法を学んでいきましょう。サンプルプロジェクトを用意しましたので、是非ソースコードと一緒に記事を読み進めてください。

画像エフェクトアプリの仕様

左ソフトキーでWindows phone標準の写真選択ダイアログを表示し、右ソフトキーのエフェクトにて詳細なエフェクト効果を選択します。

図1 アプリの完成像
図1 アプリの完成像

シンプルなUIなので上記のコントロールを配置した状態を前提として話を進めていきます。

カメラ撮影・画像選択ダイアログを使う

まずは加工する画像を選ばなければいけません。.NET Compact Frameworkには、Microsoft.WindowsMobile.Forms名前空間にSelectPictureDialogクラスが用意されており、Windows phone標準の写真選択ダイアログを簡単に呼び出して使う事が出来ます。

SelectPictureDialogクラスは、前述した通り画像を選択するダイアログなのですが、その機種デフォルトのカメラを起動して写真を撮影させる事も可能なコンポーネントとなっています。撮影した写真は端末に保存されますので、FileNameプロパティから写真のファイルパスを取得することができます。

取得したファイルパスからBitmapオブジェクトを生成させます。複数のエフェクト処理をかけますので、オリジナルのBitmapオブジェクトは変数OriginalBmpに保持しておきましょう。

// オリジナルのBitmapを保持
Bitmap OriginalBmp = null;

private void menuGetPicture_Click(object sender, EventArgs e)
{
    SelectPictureDialog dlg = new SelectPictureDialog();
    dlg.Title = "加工する画像を選択してください";
    
    if ( dlg.ShowDialog() != DialogResult.OK)
    {
        return;
    }
    
    // ファイルパスからBitmapを生成する
    this.OriginalBmp = new Bitmap(dlg.FileName);
}

今回は使用しませんが、カメラ撮影を行わせずに画像選択機能だけで十分な場合は、CameraAccessプロパティをfalseに設定すると、カメラ撮影アイコンを非表示にできます。

またカメラ撮影だけを行わせたい場合は、同じMicrosoft.WindowsMobile.Forms名前空間にあるCameraCaptureDialogクラスを使いましょう。画質やサイズを設定して撮影出来ます。

撮影・選択した画像を画面に表示させる

ダイアログで選択した画像を表示させます。単純に表示させるだけであれば、表示領域全体にドッキングしたPictureBoxのImageプロパティにthis.OriginalBmpを設定するだけで表示することができます。

pictureBox1.Image = this.OriginalBmp;

画像がpictureBoxより大きく表示領域を超えてしまう場合は、ImageSizeプロパティに画像をPictureBoxのサイズに合わせて画像全体を表示するImageSize.Stechを設定すると良いかもしれません。

pictureBox1.ImageSize = ImageSize.Stech;
pictureBox1.Image = this.OriginalBmp;

ただし、この方法は縦横の比率は関係無しに画像のサイズをPictureBoxに合わせてしまいますのでご注意ください。

エフェクト処理を行う

次に画像に対して色味を変えるエフェクト処理を行いましょう。現在表示している画像から1ピクセル分の色情報を抽出して、色情報の加工を行い、元のピクセルへ設定します。ピクセルは色情報を持つ最小単位で、三原色(R成分、G成分、B色成分)の情報を持っています。このRGBはRed、Green、Blueの頭文字を取ったものです。

グレイスケール化

グレイスケールは白と黒とその中間色で表現した言わば白黒写真です。白黒写真と言っても色空間やガンマ値を考慮した沢山の方法があります。R成分、G成分、B成分の最大値と最小値を足して2で割る中間値法や、R成分、G成分、B成分のそれぞれの要素に重み付けをして平均を取る加重平均法等です。

今回は一番簡単な単純平均法を使用しましょう。1ピクセルのR成分、G成分、B成分の要素をすべて足し合わせて、3で割り平均値を取ります。

private void menuGrayScale_Click(object sender, EventArgs e)
{
    Bitmap bmp = (Bitmap)this.OriginalBmp.Clone();
    
    for (int j = 0; j < bmp.Height; j++)
    {
        for (int i = 0; i < bmp.Width; i++)
        {
            Color orgColor = bmp.GetPixel(i, j);
            
            int avg = (int)((orgColor.R + orgColor.G + orgColor.B) / 3);
            Color c = Color.FromArgb(avg, avg, avg);
            
            bmp.SetPixel(i, j, c);
        }
    }
    
    pictureBox1.Image = bmp;
}

上記のコードを実行してみました。

図2 サンプル画像をグレイスケール化
図2 サンプル画像をグレイスケール化

セピア調化

次に一昔前の写真のようにセピア調に加工してみましょう。セピアというのはイカ墨の色のことで、JIS慣用色名ではRGB値0x6B4A2Bで定義されています。RGBのそれぞれの値に直すと (R:107, G:74, B:43) となります。

R成分を基準(1)として考えると(赤:1, 緑:0.7, 青:0.4)の比率になります。この比率を一旦グレイスケール化した後に掛けてみましょう。

private void menuSepiaTone_Click(object sender, EventArgs e)
{
    Bitmap bmp = (Bitmap)this.OriginalBmp.Clone();
    
    for (int j = 0; j < bmp.Height; j++)
    {
        for (int i = 0; i < bmp.Width; i++)
        {
            Color orgColor = bmp.GetPixel(i, j);
            
            int avg = (int)((orgColor.R + orgColor.G + orgColor.B) / 3);
            Color c = Color.FromArgb((int)(avg * 1),
                                     (int)(avg * 0.7),
                                     (int)(avg * 0.4));
            
            bmp.SetPixel(i, j, c);
        }
    }
    
    pictureBox1.Image = bmp;
}

上記のコードを実行してみました。

図3 サンプル画像をセピア調化
図3 サンプル画像をセピア調化

さいごに

ただ、サンプルアプリにてエフェクト処理を実行すると、フリーズしたかのように止まらなかったでしょうか。これは、Bitmapクラスに格納している画像データはアンマネージドな領域に確保されており、SetPixelメソッドとGetPixelメソッドの呼び出しの度にオーバーヘッドが発生してしまうため、画素数の多い画像になればなるほど時間が掛かってしまうからです。

直接画像データを操作することで高速化が可能ですので、次回取り上げてみたいと思います。今回は以上で終わりです。ありがとうございました。

おすすめ記事

記事・ニュース一覧