第3回CADセミナー@岐阜 演習2/画像の白黒反転

演習の第2段階は,画像の白黒を反転するプログラムを作ります.
同時に画像全体の画素値の平均を求めるプログラムも作成します.

ここでは,

を作成して,そこへ元画像を保存した配列,反転画像の配列などを渡し, 処理結果を得ることとします.

演習2のサンプルプログラムは,そのプログラムになります.
ここでは,まず,ハードディスク上に保存された画像ファイルを開き,
それをコンピュータのメモリ上に保存します.
演習1とはことなり,元画像用の配列,反転画像用の配列の2つが宣言されていることに注意して下さい.

演習2のサンプルプログラム

その基本的な流れは,演習1と同じです.異なる点のみ説明します.
  1. インクルードファイル,変数の宣言
    double ave_a, abe_bにおいて,画素値の平均値を保存するための変数を宣言しています.
    平均値は小数となるため,実数型で宣言しています.
    また,画像については,元画像用と反転画像用に別の配列を宣言しています.
    元画像用:*in,反転画像用:*inv_imgとしました.

  2. コマンドラインからの変数の読み込み
    get_argsに記述しある処理が実行されます.

  3. 必要なメモリ量の確保
    get_argsでは,画像のファイル名,幅,高さ,の情報が得られます(コマンドラインから).
    プログラム中では,
    image = (char *)malloc(width.....);
    となっているところで確保しています.mallocという関数がその動作の中心です.
    malloc(必要なバイト数)としてメモリが確保されることになります.
    元画像も反転画像も同じ大きさなので,それぞれを同じ大きさの配列となるように確保します.

  4. 画像ファイルのメモリへの転送
    image_read_char()という関数で実行され,ハードディスクからファイルを読み込みます.

  5. 反転処理を行う関数への配列の引き渡し
    反転処理を行う関数は,invert_imageという名前にしました.
    この関数は,2つの配列 *org, *outとその大きさ(幅:w,高さ:h)を受け,*orgの白黒反転 画像を*outに作成します.

  6. 画素値の平均値を求める関数への引き渡し
    平均値を求める関数は,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つの関数の入力を変えることで,その入力に応じた出力(平均値)が得られており, 関数の再利用性が確認できると思います.

  7. 平均値の表示
    printf文を使って平均値を表示します.printf文の中に計算式を入れても,その計算式が評価された結果が 表示されます.
    また,このような平均値などをべつの関数に渡して,渡した先で表示する場合や,さらに処理が複雑な場合は, それらの値を元に統計的な処理を行う関数を作成して,別の処理を行う,というときもあります.

  8. メモリ上のデータのハードディスクへの保存
    関数image_write_charでは,保存する画像のファイル名,幅,高さ,画像の保存された配列名を渡すと目的の画像を ハードディスク上に作成します.

以上,画像反転の例になりました.特に, という演習を行いました.

このように,画素値を自由にあやつることができるようになると,画像のコントラストを変えたりすること(階調処理)が, 任意にできるようになります.また,原画像が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利用.).
きっともう少しいろいろな方法があると思いますが,この件については,私もまだまだ勉強中なので,このへんで.