Last-modified: Wed, 06 Jun 2012 06:15:33 JST
Counter:6221 Today:2 Yesterday:1 Online:7
このエントリーをはてなブックマークに追加

2.R成分の抽出と表示

About

1.png

 ここでは画像処理で用いられる最も基本的なアルゴリズムを解説しています。読み込んだ画像から画素情報(RGB)を配列として取得して、その値を操作(画像処理)します。ここでは全ての画素に対して操作を行い、画素の成分RGBの内、GとBを0にしています。この結果、赤み成分Rのみが残った画像が生成されます。実行結果は左の画像の通りとなります。

Sample Program

画像編集のための用意

 読み込んだ画像データから画素(ピクセル)情報を操作して、その操作を反映して表示するための準備を行います。「WriteableBitmapクラス」がWPFには用意されています。このクラスは読み込んだBitmapデータを書き換えて反映させることができるので、ここではそのインスタンスを生成して活用します。下記のサンプルプログラムの部分で「WriteableBitmap」を生成しています。また表示する画像は「BitmapImageクラス」のインスタンスでは無く、「WriteableBitmapクラス」のインスタンスであることに注意してください。

                //ファイルからBitmapImageにデータを読み込む
                bmpImage = new BitmapImage(new Uri(ofDialog.FileName));

                //編集する準備
                writableBmp = new WriteableBitmap(bmpImage);

                //画像を表示する(編集する画像データにする)
                image1.Source = writableBmp;

画像から画素情報を取得する

 画像は前述の通り画素の配列ですから、画像処理を行うに当たって、まずはその画素情報を保存する配列を用意します。画素情報は0~255のbyte値をとりますから、byte配列のインスタンスになります。この時、必要になる配列の大きさを決定する必要があります。ここでは画像データの全ての画素を取得するので、画像に含まれる全ての画素情報が収まる様な大きさにする必要があります。

        private void button_ToRed_Click(object sender, RoutedEventArgs e)
        {
            //データを保存する先を用意する
            byte[] dataArray = new byte[writableBmp.PixelHeight * writableBmp.BackBufferStride];

            //ピクセルのデータを全て保存
            writableBmp.CopyPixels(dataArray, writableBmp.BackBufferStride, 0);

 WPFでは「WriteableBitmapクラス」に画像データのストライドを取得するための「BackBufferStrideプロパティ」が用意されています。ストライドについては「0.画像データの構造」で説明しています。ストライドは画像の横一列あたりに用いられるbyte数ですから、単純に高さを乗算することによって、画像を構成する配列の長さが分かります。よってデータを保存するために用意する配列はソースコードの通りとなります。

 用意した配列に、画像からデータを保存するにはWriteableBitmapクラスの「CopyPixelsメソッド」を利用します。このメソッドは、"画素データの保存先(用意した配列)"、"画像のストライド"、"オフセット"を指定して実行されます。オフセットは「先頭から何番目のデータであるか」を示す値で、ここでは全ての画素情報が欲しいわけですから、オフセットは「0」を指定して、0番目のデータから複製するように指定します。

ストライドを利用しなくても、画像データの配列の長さは求めることができます。
縦横が 200px * 100px、RGBA各色8bit(32bitColor)である画像データを構成する配列の長さは、
 200(px) * 100(px) * 4(byte = 32bit) = 80000
の通り80000となります。

画素データの操作

 ここでは簡単に、得られた画素情報の内、R(赤)の値のみを抽出するプログラムを実装します。先に画素情報を保存した配列を繰り返し操作することで、画像全体を操作することができます。ここで保存された画素情報は配列の中に「B/G/R/空」の順に1画素分として並べられ、それが画素数(つまり縦幅x横幅)の分だけ連続しています。詳細は先の項目「0.画像データの構造」を参照してください。

            //保存したデータは 1pixel = {[B][G][R][X]} の並びで保存されている。
            //各値は0~255。ここではBとGを0にして、Rの成分だけそのままにする。
            for (int i = 0; i < dataArray.Length; i += 4)
            {
                dataArray[i] = 0;//B
                dataArray[i + 1] = 0;//G
                //dataArray[i + 2] = 0;//R
                //dataArray[i + 3] = 0;//余剰分の値
            }

 ここでは画素情報の内、BとGの値を0にしています。そしてRと余剰分に関しては操作をしていません。これを全ての画素に繰り返すわけです。BGRAの順に配列の中に値が入れられているわけですから、配列を4つ進めると次の画素の値を参照することができます。

変更の適用

 保存した画素データを操作した訳ですから、画面上に結果が反映されないので注意してください。保存した画素データを、画像データに書きこみます。WriteableBitmapクラスの「WritePixelsメソッド」を利用します。"書き込む範囲", "書き込む画素データの配列", "書き込むデータのストライド"を指定してデータを書き込みます。"書き込む範囲"は「Int32Rectクラス」を用いた矩形で指定します。ここでは全ての画素を書き変えたいわけですから、左上の座標(0,0)と、右下の座標(横幅,縦幅)を指定します。

            //Bitmapに書き込み
            writableBmp.WritePixels(
                //書き込む範囲(x=0,y=0の位置から,画像の幅と高さ分の範囲を書き換える)
                  new Int32Rect(0, 0, writableBmp.PixelWidth, writableBmp.PixelHeight),
                //書き換えるデータ(先に編集した配列)
                  dataArray,
                //書き換えるストライド
                  writableBmp.BackBufferStride,
                  0);

結果

 ここまでの処理をすべて終了すると結果画像が反映されて、元の画像の赤成分だけが抽出されていることが確認できます(このページの先頭を参照されたい)。任意の色成分だけ抽出したり、値を書き換えるなどして、どう変化するのかを確認してください。