1.画像基礎


1−1.画像フォーマット

ここでは画像フォーマットが簡単な ppm (portable pixmap) を利用します.ppm は画素ごとに RGB それぞれに 8 bit (合計 3×8 = 24 ビット)のカラー画像が表現できます.データ形式はバイナリ形式(raw)とアスキー形式(ascii)がありますが,まずはバイナリ形式を利用します. 画像フォーマットは,

P6
XSIZE YSIZE
最大階調
画像データ:バイナリ形式で左上から右下へRGBRGBRGB…とデータが並ぶ

のようになっています.また一行目以外の場所に # でコメントを入れることも可能です.例えば,640×480 の画像を作りたければ,

P6
640 480
255
RGBRGBRGB...(実際はバイナリ形式)

のように記述します.次のプログラムは 640×480 の真っ白の画像(white.ppm)を作るためのものです.

#include <stdio.h>

main(){
  unsigned char image[480][640][3];
  int x,y;
  FILE *fp;
  fp=fopen("white.ppm","wb");
  fprintf(fp,"P6\n#←シャープでコメントも入れられる\n640 480\n255\n"); //ヘッダの書き込み
  for(y=0;y<480;y++){
    for(x=0;x<640;x++){
      image[y][x][0]=image[y][x][1]=image[y][x][2]=0xff;
    }
  }
  fwrite(image,sizeof(char),640*480*3,fp); //データの書き込み
  fclose(fp);
}

画素値は大きいほど色が濃く,最大値は 0xff ( = 255) です.色の三原色ではすべてを混ぜると黒になるますが,光の三原色では全てを混ぜると白になるので注意して下さい.つまり RGB の全てが 0xff になると白になり,全てが 0 で黒になります.


光の三原色(RGB)

色の三原色(CMY)

こんなに単純で便利なフォーマットであるにも関わらず,Windows の場合は標準では開くことができません.IrfanView や Susie などをインストールして ppm の画像を見ることのできる環境を作ってください.linux なら xv や gimp で開くことができます.

まず,sample.ppm をダウンロードして,自分の使っているマシン環境で ppm ファイルを見ることができるかを確認しましょう.確認できれば,下の演習に取り掛かってください.

演習1−1

  1. white.ppm を作って,できた画像を確認しよう.
  2. 下の例のような red.ppm, gradation.ppm, circle.ppm を作ろう.
  3. 作った画像をメモ帳などのテキストエディタで開いてみよう.


black.ppm

red.ppm

gradation.ppm

circle.ppm


1−2.テキストとバイナリ

これまでの例ではバイナリ形式で画素値を書き込みました.次にアスキー形式で書き込む ppm について説明します.

アスキー形式の ppm ファイルはヘッダ部分が P3 になります.画素値は 10 進数ですので,%d で書き込みます.テキストなので,fopen をするときは "w" になるので注意して下さい.P3 で真っ白の画像を作るプログラムは次のようになります.

#include <stdio.h>

main(){
  unsigned char image[480][640][3];
  int x,y;
  FILE *fp;
  fp=fopen("whiteP3.ppm","w");
  fprintf(fp,"P3\n#←シャープでコメントも入れられる\n640 480\n255\n"); //ヘッダの書き込み
  for(y=0;y<480;y++){
    for(x=0;x<640;x++){
      image[y][x][0]=image[y][x][1]=image[y][x][2]=0xff;
      fprintf(fp, "%d %d %d\n", image[y][x][0], image[y][x][1], image[y][x][2]); //データの書き込み
    }
  }
  fclose(fp);
}

画素値の区切りはスペース,タブ,改行のいずれかを利用します.

演習1−2

  1. 上のプログラムを実行して whiteP3.ppm を作って,できた画像を確認しよう.
  2. 作った画像をメモ帳などのテキストエディタで開いてみよう.
  3. 1−1で作ったプログラムと今回作ったプログラムの違いを見てみよう.
  4. バイナリ形式の ppm とテキスト形式の ppm のどちらが良いかを考えてみよう.
さて,どちらのフォーマットが良いかの結論は出たでしょうか?テキストで値が見られるのだからテキスト形式の方が良いと思う人も多いかも知れません.果たしてそうでしょうか?

それでは今回作った white.ppm と whiteP3.ppm のファイルのサイズを見比べてみてください.white.ppm が 901KB に対して,whiteP3.ppm は 3901KB です.テキストであるがためにファイルサイズが大きくなってしまいます.それにテキストで値が見られるとはいっても,(300,20) の値は?といわれてもテキストのどこになるのか探すのは難しいです.それにテキストの方がファイルを書き込むのにも時間がかかります.

という訳で,これからはバイナリ形式の P6 を使っていきます.


1−3.配列とポインタ

これまでのプログラムでは,直感的に理解しやすいように画像を収納するメモリを,unsigned char image[480][640][3]; の3次元配列で宣言しました.しかし,もちろん unsigned char image[480*640*3]; のように一次元配列でも記述することができますし,unsigned char *image; のようにポインタを使って記述することもできます.

image[480][640][3] で宣言されている場合には,image[y][x][i] と *(image+3*(640*y+x)+i) が同じ意味であることを理解していれば,プログラムの本質は変わりません.両者が同じ意味になることが分からない人はもう少しポインタを勉強しましょう.

次のプログラムは,1−1で作った gradation.ppm をファイルに取り込んで左右反転した画像 reverse.ppm を作るプログラムです.

#include <stdio.h>

main(){
  unsigned char image[480][640][3];
  unsigned char ctmp;
  int x,y,i;
  FILE *fp;
  if((fp=fopen("gradation.ppm","rb"))==NULL){
     printf("ファイルが開けません\n");
     exit(-1);
  }
  fscanf(fp,"P6\n#←シャープでコメントも入れられる\n640 480\n255\n); //ヘッダを読み飛ばす
  fread(image,sizeof(char),640*480*3,fp);
  fclose(fp);

  for(y=0;y<480;y++){ //データの左右を反転する
    for(x=0;x<640/2;x++){
      for(i=0;i<3;i++){
        ctmp = image[y][x][i];
        image[y][x][i] = image[y][639-x][i];
        image[y][639-x][i] = ctmp;
      }
    }
  }

  fp=fopen("reverse.ppm","wb");
  fprintf(fp,"P6\n#←シャープでコメントも入れられる\n640 480\n255\n");
  fwrite(image,sizeof(char),640*480*3,fp);
  fclose(fp);
}

演習1−3

  1. 上のプログラムを上下左右反転のプログラムに変更しよう.
  2. できたプログラムの3次元配列を1元配列に変更したプログラムを作ろう.
  3. できたプログラムの1次元配列をポインタに変更したプログラムを作ろう.
1次元配列は,unsigned char image[480*640*3]; で,ポインタは unsigned char *image; で宣言して下さい.ポインタを使うときは malloc でメモリの確保が必要です.使い終わったら free で開放するのも忘れないように注意しましょう.


gradation.ppm

reverse.ppm

reverse.ppm

以前のコンパイラでは大きな配列はうまく確保できなかったので,ポインタで malloc をしないとプログラムが動かなかったりしたのですが,最近のコンパイラは大きな配列もうまく確保してくれるようです.それでもやはり大きな配列を使う時は,ポインタを使用した方が無難です.


| トップ | 0章へ | 1章へ | 2章へ | 3章へ | 4章へ | 5章へ |
Copyright(c) Masaki Onishi All rights reserved.