第三回-02 配布したGUIアプリケーションの概略


画像処理アプリケーション

第一回-02 コンソールアプリケーションとGUIアプリケーション」にて、 画像処理アプリケーションの例として
imageopen.zip をダウンロード→ビルド→実行してもらった。
今回はこのアプリケーションのプログラムを変更することで少し実践的なGUIプログラミングを体験してもらう。

第一回と同様、 imageopen.zip をダウンロードして展開しよう。
展開したら、ImageOpen フォルダの中の ImageOpen.sln と言うファイルを ダブルクリックして起動しよう。

そして、
「ビルド」→「ソリューションのビルド」
および
「デバッグ」→「デバッグ開始」
を実行しよう。GUI アプリケーションが起動するので、 「開く」ボタンを押して、imageopen.zip に含まれていた birds_in_australia.jpg を読み込もう。
すると、以下のように画像とその左右対称な鏡像が現われる。

すなわち、このアプリケーションは画像を読み込み、その左右対称な鏡像を作成してそれぞれ表示するアプリケーションであった。



このGUIアプリケーションのプログラムの内、今回着目して欲しいのは以下の部分である。
各自、ソースファイル (ImageOpen.Dlg.cpp) から 該当する行を探して欲しい。



画像のピクセル情報

さて、先程のプログラムにおいて重要なのは以下の7行である。
本日はまずこの7行を理解することを目指そう。

for(int j=0 ; j<mHeight ; j++){
	for(int i=0 ; i<mWidth ; i++){

		outImage[index(i,j) + 0] = inImage[index(mWidth-1-i,j) + 0];  // Blue
		outImage[index(i,j) + 1] = inImage[index(mWidth-1-i,j) + 1];  // Green
		outImage[index(i,j) + 2] = inImage[index(mWidth-1-i,j) + 2];  // Red
	
	}
}

まず、PC上で取り扱う画像は、ピクセルと呼ばれる格子で構成されており、1ピクセルごとに画像の色情報が格納されている。

ある画像のピクセルサイズがどれだけであるかを知る方法は既に1年生でパワーポイントを学ぶ際に取り扱った。
こちらで復習してみると良いだろう。

一般に、プログラミングにおいて画像を取り扱う際は、ピクセルごとの情報を配列というデータ形式に格納して操作することが多い。
(Windows の GetDIBits という手法で取得した) 画像データは、以下の図の様に格納されている。

なお、一般に画像の座標は下図の様に左上を原点とすることがほとんどなのだが、
実はこの imageopen.zip で用いている GetDIBits では画面の左下が原点となっている (歴史的な理由?)。
演習ではこの違いは影響しないので図は原点を左上のままとしている。



読み取るべきポイントを列挙すると以下の通り。 ここで、「1ピクセルあたりに青 (Blue)緑 (Green)赤 (Red)3つの情報」が必要であることは、1年生の「ホームページの作成」で学んだ。
このリンクをクリックして現われるアプリケーションを用いて三原色の組合せでどのような色ができるか学んだことを覚えているだろうか。

また、このアプリケーションでは 青 (Blue)緑 (Green)赤 (Red) がそれぞれ 0~255 の数値で表現されることも思い出して欲しい。
つまり、それぞれの色が 8 ビットの符号なし数として表現されているわけである。

そうすると、3 色により合成される色は 8×3 = 24 ビットで構成されることになる。
24 ビットで表される色の組合せは 256×256×256 = 16,777,216 (約 1600 万通り) ということになる。


画像の左右反転

さて、上記のように「一次元配列上に各ピクセルごと三原色の画像情報が格納されている」ことを踏まえ、「画像の左右反転」を理解しよう。

まず、最初に現われる二つの for 文は以下のように「出力画像の位置 (i, j) を for 文で全て調べつくす」ということを意味している。
細かな文法は Visual Basic と若干異なるが、基本的な考え方は Visual Basic で既に学んだことと変わらない。

for(int j=0 ; j<mHeight ; j++){  // 出力画像の縦方向の座標 j を 0 ~ mHeight-1 まで for 文で変化
	for(int i=0 ; i<mWidth ; i++){  // 出力画像の横方向の座標 i を 0 ~ mWidth-1 まで for 文で変化

今回はこの for 文の部分を書き換える必要はないが、上記のことを知っておくと、以下の理解がスムーズであろう。

次に、以下の部分である。この部分は、出力画像 outImage を、入力画像 imImage を元に決定している。それを下の図で見てゆこう。

	outImage[index(i,j) + 0] = inImage[index(mWidth-1-i,j) + 0];  // 位置 (i, j) の Blue 情報を決定
	outImage[index(i,j) + 1] = inImage[index(mWidth-1-i,j) + 1];  // 位置 (i, j) の Green 情報を決定
	outImage[index(i,j) + 2] = inImage[index(mWidth-1-i,j) + 2];  // 位置 (i, j) の Red 情報を決定


下の図は、入力画像 inImage から、左右を反転した出力画像 outImage をいかにして作るかを示す模式図である。
今、for 文により全てのピクセルについて同じ処理を繰り返す枠組みができているので、
後は outImage の座標 (i, j) のピクセルをどのように決めるか、という問題に帰着される。

outImage は inImage の左右を反転したものなのであるから、outImage の座標 (i, j) のピクセルは
下図より inImage の座標 (mWidth - 1 -i , j) のピクセルをコピーすればよいことがわかる。

そして、1ピクセル辺りの情報は 青 (Blue)緑 (Green)赤 (Red)があるのだったから、 3つの情報を全てコピーして代入してやれば良い。
それを実行しているのが上の囲い内の3命令である。



以上で目標の7行を全て理解できた。



←第三回-01 初めての C/C++ プログラミング(GUIアプリケーション編)第三回-03 画像処理アプリケーションをいろいろと変更してみよう→

非情報系学生のための C/C++ 入門に戻る