第3回CADセミナー@岐阜 演習2/画像の白黒反転
演習の第2段階は,画像の白黒を反転するプログラムを作ります.
同時に画像全体の画素値の平均を求めるプログラムも作成します.
ここでは,
- 白黒を反転する関数
- 画像の画素値の平均値を求める関数
を作成して,そこへ元画像を保存した配列,反転画像の配列などを渡し,
処理結果を得ることとします.
演習2のサンプルプログラムは,そのプログラムになります.
ここでは,まず,ハードディスク上に保存された画像ファイルを開き,
それをコンピュータのメモリ上に保存します.
演習1とはことなり,元画像用の配列,反転画像用の配列の2つが宣言されていることに注意して下さい.
演習2のサンプルプログラム
その基本的な流れは,演習1と同じです.異なる点のみ説明します.
- インクルードファイル,変数の宣言
double ave_a, abe_bにおいて,画素値の平均値を保存するための変数を宣言しています.
平均値は小数となるため,実数型で宣言しています.
また,画像については,元画像用と反転画像用に別の配列を宣言しています.
元画像用:*in,反転画像用:*inv_imgとしました.
- コマンドラインからの変数の読み込み
get_argsに記述しある処理が実行されます.
- 必要なメモリ量の確保
get_argsでは,画像のファイル名,幅,高さ,の情報が得られます(コマンドラインから).
プログラム中では,
image = (char *)malloc(width.....);
となっているところで確保しています.mallocという関数がその動作の中心です.
malloc(必要なバイト数)としてメモリが確保されることになります.
元画像も反転画像も同じ大きさなので,それぞれを同じ大きさの配列となるように確保します.
- 画像ファイルのメモリへの転送
image_read_char()という関数で実行され,ハードディスクからファイルを読み込みます.
- 反転処理を行う関数への配列の引き渡し
反転処理を行う関数は,invert_imageという名前にしました.
この関数は,2つの配列 *org, *outとその大きさ(幅:w,高さ:h)を受け,*orgの白黒反転
画像を*outに作成します.
- 画素値の平均値を求める関数への引き渡し
平均値を求める関数は,get_averageという名前にしました.
この関数は,配列 *inとその大きさ(幅:w,高さ:h)を受け,関数の返り値として
平均値を返します.関数の部分に double get_average(...)と書かれており,先頭の
doubleが返り値がdouble型であることを示しています.
このほかに,voidとついている関数もありますが,そのような関数は,返り値がない(void)ということです.
この関数は,入力をかえれば他の配列(画像)の平均値も求めることができますので,
最初に
ave_a = get_average(in, width, height);
として,元画像の平均値を求めています.その値がave_aに入ります.
次に,
ave_b = get_average(inv_img, width, height);
として,反転画像の平均値がave_bに入るようにしています.
ここで,1つの関数の入力を変えることで,その入力に応じた出力(平均値)が得られており,
関数の再利用性が確認できると思います.
- 平均値の表示
printf文を使って平均値を表示します.printf文の中に計算式を入れても,その計算式が評価された結果が
表示されます.
また,このような平均値などをべつの関数に渡して,渡した先で表示する場合や,さらに処理が複雑な場合は,
それらの値を元に統計的な処理を行う関数を作成して,別の処理を行う,というときもあります.
- メモリ上のデータのハードディスクへの保存
関数image_write_charでは,保存する画像のファイル名,幅,高さ,画像の保存された配列名を渡すと目的の画像を
ハードディスク上に作成します.
以上,画像反転の例になりました.特に,
- 1次元配列を用いて画像を取り扱う
- 1つの関数の入力を変えて,活用する
- 画像から簡単な特徴量(この場合は平均値)を取り出す
という演習を行いました.
このように,画素値を自由にあやつることができるようになると,画像のコントラストを変えたりすること(階調処理)が,
任意にできるようになります.また,原画像が8ビットで与えられると,階調処理を行うときの問題点などが
明らかになってきます.
次の演習では,そのような例を扱ってみましょう.
余談:MMXテクノロジを(きっと)利用した高速画像反転
インテルのPentinumプロセッサには,MMXテクノロジが採用されており(Pentinum 166MHz以降),
その技術を用いると,処理の高速化が可能になって来ます.
ここでMMXとは,Multi Media Extensionの略で,マルチメディア(つまり画像,映像処理)用の演算の高速化を
目的としてます.
PentinumIIIでは,MMXの演算に利用できる64ビットレジスタが8つあり,特定の宣言をすることで,
利用できるようになります.このレジスタでは,整数型の演算のみが可能であり,64ビットの固まりごとに
加算,減算という演算ができるというものです.
一見,これはたいしたことは無さそうですが,8ビットのデータを8つまとめて1命令で動くという,並列処理風の
処理が可能になるということです.つまり,8ビットの画像なら,8画素をまとめて処理できるということになります.
その命令を使うためには,アセンブラ(機械語)を直接Cから呼んで動かす必要があります.
そのサンプルのプログラムは以下のようになります.
MMX命令を利用した画像反転
そのmain文(演習2のmainを改造:白黒反転の関数を呼んでいる部分を変えただけ)
このように高速化することによって,31,257,280画素の8ビット画像の白黒反転で処理時間を比較した結果,
反転処理ににかかる時間はディスク読み出しも含め1.78秒から1.58秒へ短縮しました(読み出しにかかる時間は
両方とも約0.60秒.PentinumIII700MHz利用.).
きっともう少しいろいろな方法があると思いますが,この件については,私もまだまだ勉強中なので,このへんで.