C言語による画像処理: 画像の切り出し
C言語による画像処理: 画像の切り出し
前章で, convertというコマンド(Imagemagickというアプケーションのコマンド)を使って画像をいろいろいじりましたが, 自力でオリジナルの画像処理をするには, プログラミングが必要です。以下の実習では, 必ずしもオリジナルプログラムを作らなくてもImagemagickやGIMPなどのアプリケーションで処理可能のことですが, それでも, 同様の処理を大量に繰り返すときなどは, やはりオリジナルプログラムを作ったほうが効率的であったりします。
前章の最後に作ったraw画像ファイルの左半分だけを切りだしてみましょう。プログラムは以下のようになります:
/* * cutimage.c * cutout left part of an image * usage: $ ./cutimage < source_image > destin_image * compile: $ gcc cutimage.c -o cutimage * 2004/07/10 Kenlo Nishida */ # include <stdio.h> # define XL 800 # define YL 600 # define BPP 3 int main() {int x, y; unsigned char data[BPP]; for (y=0; y<YL; y++) for (x=0; x<XL; x++) {fread(data, BPP, 1, stdin); if (x<XL/2) fwrite(data, BPP, 1, stdout); } }
課題1: これをviなどのテキストエディタで入力し, cutimage.cというファイル名で保存し, コマンドラインで以下のようにコンパイルしてください:
$ gcc cutimage.c -o cutimage
エラーが出なければ, コンパイルが成功してcutimageというファイルができているはずです。では以下のようにして走らせてみましょう:
$ ./cutimage < lake.raw > lake_left.raw
この結果, lake_left.rawというファイルのサイズは, もともとのlake.rawの半分になっているはずです(ls -lコマンドで確かめて下さい)。
課題2: では, この結果を画面に表示してみましょう。そのためには, まずrawバイナリーファイルにヘッダー情報を加えて, 何らかのフォーマットにしなければなりません。ここではPNMにしましょう。まずviなどのテキストエディタでlake_left.ppmという名のファイルを作り, 以下の内容(ヘッダー)を記述します:
P6 # 400 600 255
viなどで作業する場合, 最後の255のあとに改行しないようにしてください。
そして, このヘッダー情報のあとに, rawバイナリーファイルをドッキングさせます:
$ cat lake_left.raw >> lake_left.ppm
この">>"は「リダイレクト」の一種で, 標準出力を, あるファイルに追加するかたちで出力します。
こうしてできたlake_left.ppmを表示してみてください:
$ display lake_left.ppm

↑こうなればOKです。
では上のプログラムを解説します:
/* コメントのはじまり * cutimage.c コメント:プログラム名 * cutout left part of an image コメント:目的 * usage: $ ./cutimage < source_image > destin_image コメント:使用法 * compile: $ gcc cutimage.c -o cutimage コメント:コンパイルの方法 * 2004/07/10 Kenlo Nishida コメント:著作権表示 */ コメントのおわり # include <stdio.h> // 標準ライブラリを組みこめ! # define XL 800 // 画像の横の大きさ(800ピクセル)をXLという名前で定義。 # define YL 600 // 画像の縱の大きさ(600ピクセル)をYLという名前で定義。 # define BPP 3 // 画像の1ピクセルあたりのバイト数(3バイト)をBPPという名前で定義。 int main() {int x, y; // 画像の横と縱のインデックス。 unsigned char data[BPP]; // ピクセルのデータを格納するための配列を, 符号無し1バイト整数で宣言。 for (y=0; y<YL; y++) for (x=0; x<XL; x++) {fread(data, BPP, 1, stdin); // 画像データを標準入力(stdin)から読みこむ。 if (x<XL/2) fwrite(data, BPP, 1, stdout); // 画像の左半分のときのみ, データを標準出力(stdout)に出す。 } }
この中で
if (x<XL/2) fwrite(data, BPP, 1, stdout);
という部分がポイントです。xは元画像の中のピクセルのx座標(左端がゼロ)ですが, それが画像の横サイズ(XL)の半分に満たない場合(つまり元画像の左半分)はデータを出力します。xが画像サイズの半分以上(元画像の右半分)になると, 出力をしません(freadで読み込まれたデータは, 何もされずに捨てられます)。このようにして, 左半分だけを抜き出しているのです。
一般に, 画像ファイルは, 画像の左上のピクセルの情報を先頭として, その右隣りのピクセルの情報がその次,...というふうに, 左上から右下への順番で, 各ピクセルの情報を順々に並べて格納しています。
課題3: 画像lake.rawの, 右半分を切り出してみよ。
課題4: 画像lake.rawの, 下半分を切り出してみよ。
課題5: 画像lake.rawを, 縦50%, 横50%に縮小せよ。ヒント: ヘッダーの画像サイズ記述が400 300になる。for文はそのまま。freadもそのまま。fwriteのタイミングをちょっとだけ調整すればよい。
Keyword(s):
References:[画像解析入門]