とらりもんHOME  Index  Search  Changes  Login

とらりもん - C言語による画像処理: 画像の左右反転・上下反転 Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

![[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

{{attach_view(lake_s.jpg)}}

↑lake.ppm

{{attach_view(lake_reverse_LRs.jpg)}}

↑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