C言語による画像処理: 画像の左右反転・上下反転
C言語による画像処理: 画像の反転
画像の左右反転
前章の画像を, 左右反転してみましょう。 これまではrawバイナリーの入出力でしたが, ヘッダーの入出力もプログラムに組み込み, PPM型式の入出力にしてみましょう。プログラムは以下のようになります:
/* reverse_LR.c * reverse left-right of an image * usage: $ ./reverse_LR < source_image.ppm > destin_image.ppm * compile: $ gcc reverse_LR.c -o reverse_LR * 2006/07/14 Kenlo Nishida */ # include <stdio.h> # define XL 800 # define YL 600 # define BPP 3 int linescan(FILE *fp, char *buff) {int in=0, i=0; while((in=fgetc(fp))!='\n' && feof(fp)==0) {buff[i]=in; i++;} buff[i]=0; return(feof(fp)); } int main() {int x, y; char head[255]; unsigned char data[BPP*XL]; linescan(stdin, head); printf("%s\n", head); linescan(stdin, head); printf("%s\n", head); linescan(stdin, head); printf("%s\n", head); linescan(stdin, head); printf("%s\n", head); for (y=0; y<YL; y++) {fread(data, BPP, XL, stdin); for (x=0; x<XL; x++) fwrite(&data[BPP*(XL-x-1)], BPP, 1, stdout); } }
課題1: これを, 以下のようにコンパイルして実行してみて下さい:
$ gcc reverse_LR.c -o reverse_LR $ ./reverse_LR < lake.ppm > lake_reverse_LR.ppm $ display lake_reverse_LR.ppm

↑lake.ppm

↑lake_reverse_LR.ppm
では上のプログラムを解説しましょう:
int linescan(FILE *fp, char *buff) {int in=0, i=0; while((in=fgetc(fp))!='\n' && feof(fp)==0) {buff[i]=in; i++;} buff[i]=0; return(feof(fp)); }
↑この部分は, main()から使われる関数(数学的な関数ではなく, プログラムの下請をする小さなプログラムのこと)を作っています。一般に, C言語では, ほとんどの機能は「関数」という形で実装されます。例えば, 皆さんがこれまで使った, freadやscanfやprintfなども, すべて関数です。それらは, C言語に標準的に実装されている関数ですが, それ以外にも, ユーザーが独自に関数を作ることができます。ここでは, linescanという名前の関数を独自に作っているのです(linescanという名前は, 私がテキトーにつけました)。
この関数は, 引数(関数に与える変数)としてファイルポインタという型の変数*fpと, 文字列変数*buffをとります。linescanの前のshortというのは, この関数が返す出力値の型です。実際は何も返さないのですが, とりあえずここを定めておかないと, コンパイラに怒られるので(警告が出る), テキトーにshortと定めています。このlinescanという関数は, 改行コードに出会うまで, ファイルから文字列を読みつづけて, 読みこんだ文字列を*buffという変数に入れます。
ではその次の部分を読んでみましょう:
int main() {int x, y; char head[255]; ヘッダーファイルを読みこむための文字列の配列変数。 unsigned char data[BPP*XL]; 画像データを読みこむための, 符号無し整数の配列変数。 linescan(stdin, head); ヘッダーファイルを1行読みこむ。 printf("%s\n", head); 読みこんだ行をそのまま出力。
ここでlinescanとprintfのカタマリが4回くり返されているのは, この画像のヘッダー情報が4行ぶんあるからです。
ではその次の部分を読んでみましょう:
for (y=0; y<YL; y++) {fread(data, BPP, XL, stdin); for (x=0; x<XL; x++) fwrite(&data[BPP*(XL-x-1)], BPP, 1, stdout); }
これまではyのループのすぐ内側にxのループがあったのに, ここでは, xのループの前に, fread文が入っています。じつはここで, 画像の横1ラインぶんのデータをがっさりと読みこんでいます。従来はxのループの中で1ピクセルごとに読んでいたのですが, このプログラムではxのループの外で1ラインを一気に読みこんでいます。これは, 画像の左右を反転させるためです。画像データは左から右に順に格納されるので, 左右をひっくりかえすには, まず1ラインをぜんぶ読みこんで, それを反転させて書き出す必要があるわけです。それをやっているのが, fwrite(&data[BPP*(XL-x-1)]の部分です。(XL-x-1)とすることで, インデックスxの増加にともなって座標をへらしていくことができ, それが右から左の順でデータを指定することになるわけです。
画像の上下反転
画像の上下を反転するには, 上のプログラムを以下のように変えればよろしい:
- unsigned char data[BPP*XL]; を unsigned char data[BPP*XL*YL]; に。
- for (y=0; y<YL; y++) の行の前に fread(data, BPP, XL*YL, stdin); を入れる。
- for (y=0; y<YL; y++) の中のループは fwrite(&data[BPP*XL*(YL-y-1)], BPP, XL, stdout); とする(1行のみ)。
課題2: このプログラムをreverse_UD.cという名で保存し, 次のようにコンパイル・実行してみて下さい:
$ gcc reverse_UD.c -o reverse_UD $ ./reverse_UD < lake.ppm > lake_reverse_UD.ppm $ display lake_reverse_UD.ppm
Keyword(s):
References:[画像解析入門]