2006後期
情報グラフィックス論U
担当 鳥海有紀
授業資料 情報グラフィックス論U

画像の加工 -BMP画像を読み込む-

画像を表示や加工はコンピュータグラフィックの一分野として確立されている。 ここではその一部をgpoint()関数を使ったプログラムで紹介することにする。 ここで紹介する内容はごく基本的なことのみである。詳しくは参考資料などを参照してほしい。
コンピュータ画像は画素の集まりで表現しているものである。 従ってあるgpoint()関数のようなある画素を表示する機能を使用すれば、容易に画像を表示することができる。
下のプログラムでは、画素をx方向とy方向に繰り返し描画して面データ、すなわち画像を表示するものである。 このプログラムでは、ループのカウントを色の変化に利用することで左下の白色から右上の緑色にグラデーションで変わる面になる。

#include <stdio.h>

int main(void)
{
    int   i, j;

    gopen(256,256);

    for (j = 0; j < 256; j++)
    {
        for (i = 0; i < 256; i++)
        {
	    gpoint(i, j, 255-i, 255, 255-j);
        }
    }
    gclose();

    return 0;
}
bmp0.c

画像はこのような描画方法で描いている。

画像ファイルの読み込み

そこで、上のプログラムではループに沿ってデータを作成しているが、 他のソフトで作成した画像を読み込む部分を追加すれば、それを表示させることも可能である。 今回はデータの形式が最も基本的な方式のBMP画像を読み込むことにする。 画像などのファイルの読み込みにはC言語の標準ライブラリ関数を使用する。 BMP画像はペイントやPhotoShopなどラスター画像処理ソフトを使用して作成するとよい。

資料では画像の読み込み部分のソースコードの掲載は省略する。 各自授業フォルダの教材配布からソースを取り出して使用するとよい。

  /* bmp1.c 画像を読み込み表示する */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define X  200
    #define Y  267

    void readBMP(char *filename, int *x, int *y, int rdata[][Y], int gdata[][Y], int bdata[][Y]);

    int main(int argc, char *argv[])
    {
        int xsize = X, ysize = Y;
        int i, j;

        int rdata[X][Y], gdata[X][Y], bdata[X][Y];


        if (argc < 2) {
            printf("読み込むBMPファイルを指定してください。\n");
            printf("\n usage  --   r  filename.bmp \n");
            exit(1);
        }

        readBMP(argv[1], &xsize, &ysize, rdata, gdata, bdata);

        gopen(xsize, ysize);

        for (j = 0; j < ysize; j++) {
            for (i = 0; i < xsize; i++) 
            {
                gpoint(i, j, rdata[i][j],  gdata[i][j], bdata[i][j]);
            }
        }

        gclose();

        return 0;
    }

画像データの取り扱う上でC言語の新しい文法を説明しておく。

#define X 200 #define Y 200

通常の代入と異なる文字の置き換えを行う定義。 この場合X という文字が出てくると200に置き換える。 Yという文字が出てくると200に置き換える。 配列の大きさを決める場合など変数を使えない部分に使う。 一般に#define 構文で使う置き換えの文字は大文字にするのが一般的である。
ここでは画像のサイズを指定する。

int main(int argc, char *argv[])

プログラム始まり部分をこのように記載すると、 実行するときに引数をつけて実行した内容をプログラムで処理することができる。 読み込む画像ファイルを変更することが容易になるので、この方法を採用した。 実行するときは、
実行ファイル名 bmp画像ファイル名
のように実行する。

int rdata[X][Y]; int gdata[X][Y]; int bdata[X][Y];

これは変数の宣言である。rdataが変数名である。 変数名に[]がつく場合、その変数は配列として宣言していることになる。 配列とは同じ種類のデータを一塊りで扱うデータのことを言う。 ここでは、画像データは一塊りとして扱うと都合がよいので配列として宣言する。 配列データは1列に纏めて扱う場合と表計算ソフトのように行と列の2次元で扱う場合がある。 上の例は2次元で扱っている。[X]が列データ、[Y]が行データである。 扱うデータの大きさは[]の中に記載する数値で決まる。 画像データは点が升目状に並んでいるので行と列の2次元データで扱うのが都合がよい。
配列データを使用する場合には、rdata[3][4]のように使用する。 この場合の[3]や[4]を配列の添字と言う。 添字のある配列データは通常の変数と全く同じに扱うことができる。 添字にはプログラムにあるように変数を使用してもかまわない。
画像データを読み込むための関数 readBMP()を使用して画像データを読み込む。 読み込んだデータはRGBの色別にrdata、gdata、bdataに格納されるのでforループを使って画像の表示を行なう。

画像の表示

読み込んだ画像の表示方法を変えると、RGB各色で表示したり、

    /*  画像を青で表示する */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
         gpoint(i, j, 0,  0, bdata[i][j]);
       }
    }

白黒で表示したり、

    /*  画像を白黒で表示する */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
         r = (rdata[i][j] + gdata[i][j] + bdata[i][j]) / 3;
         g = r;
         b = r;

         gpoint(i, j, r, g, b);
       }
    }

各色を補色で表示するとネガ表示にすることができる。

    /*  画像をネガで表示する */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
         r = 255 - rdata[i][j];
         g = 255 - gdata[i][j];
         b = 255 - bdata[i][j];

         gpoint(i, j, r, g, b);
       }
    }

画素の周囲に色を平均して同じ色で表示するとモザイク効果のように表示にすることができる。

    /*  bmp2.c  画像をモザイク効果で表示する */
    step = 5;    /* 一色で表示する画素の大きさを指定する */

    //  各ループの増分はstepで指定した量
    for (j = 0; j < ysize; j = j + step) 
    {
        for (i = 0; i < xsize; i = i + step) 
        {
            
            r = 0;     //  
            g = 0;     //  一色で表示する値をクリアする。
            b = 0;     //

            //   指定した画素の色の平均値を求める
            for (k = 0; k < step; k++)
            { 
                for (l = 0; l < step; l++)
                {        
                    r = r + rdata[i+k][j+l];
                    g = g + gdata[i+k][j+l];
                    b = b + bdata[i+k][j+l];
                }
            }
            r = r / step / step;
            g = g / step / step;
            b = b / step / step;

            //  求めた値が0から255より大きい場合は、0、255にする
            if (r < 0)  r = 0;
            if (g < 0)  g = 0;
            if (b < 0)  b = 0;
            if (255 < r)  r = 255;
            if (255 < g)  g = 255;
            if (255 < b)  b = 255;
         
            //  各画素を求めた色で表示する。表示範囲を超えないようにサイズの確認をする。
            for (k = 0; k < step; k++)
            { 
                for (l = 0; l < step; l++)
                { 
                    if (j + k < ysize)
                    {        
                        if (i + l < xsize)
                        {
                            gpoint(i+l, j+k, r, g, b);
                        }
                    }
                }
            }
        }
    }

画像の加工

画像の加工には様々な手法が取られるが、ある規則に従って元画像の画素の値を変化される方法がある。 この規則のことを階調変換関数という。またこれをグラフにしたものをトーンカーブという。

トーンカーブを使用する加工

例えば、ある入力値を境に白と黒で表示する2階調表示もこの手法を基本にしている。 下のプログラムでは127を境界値にして値を2値化している。

    /*  画像を2値化して表示する */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
         r = (rdata[i][j] + gdata[i][j] + bdata[i][j]) / 3;

         if (r < 127)
         {
              r = 0;
         }
         else
         {
              r = 255;
         }
         g = b = r;

         gpoint(i, j, r, g, b);
       }
    }

同じように暗い画像を明るくするような場合にもこの手法を使用する。 次のプログラムでは、0から127で入力された値を0から255までの値に広げて表示するものである。

    /* 画像を明るくする */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
          if (rdata[i][j] < 127) 
              r =  rdata[i][j] * 2;
          else 
              r = 255;

          if (gdata[i][j] < 127) 
              g =  gdata[i][j] * 2;
          else 
              g = 255;

          if (bdata[i][j] < 127) 
              b =  bdata[i][j] * 2;
          else 
              b = 255;

          gpoint(i, j, r, g, b);
       }
    }

周辺画素から加工

もう1つの手法は周辺画素の情報を使用して1つの画素の表示を決定する方法である。 例えば、ノイズを除去したり、エッジを取り出したり、エッジを際立たせる鮮鋭化を行う場合に使用する。 下のプログラムでは鮮鋭化をするプログラムである。

    /* bmp3.c  鮮鋭化をする加工 */
    for (j = 0; j < ysize; j++) {
       for (i = 0; i < xsize; i++) 
       {
           r = g = b = 0;
           if ( 0 < i - 1 ) 
           {
               if (0 < j - 1)
               {
                   if (i + 1 < xsize)
                   {
                       if (j + 1 < ysize)
                       {
                           r =   rdata[i-1][j-1] * (-1) + rdata[i][j-1] * (-1)  + rdata[i+1][j-1] * (-1)
                               + rdata[i-1][j]   * (-1) + rdata[i][j] * (9) + rdata[i+1][j]   * (-1)
                               + rdata[i-1][j+1] * (-1) + rdata[i][j+1] * (-1)  + rdata[i+1][j+1] * (-1);

                           g =   gdata[i-1][j-1] * (-1) + gdata[i][j-1] * (-1) + gdata[i+1][j-1] * (-1)
                               + gdata[i-1][j]   * (-1) + gdata[i][j] *   (9)  + gdata[i+1][j] * (-1)
                               + gdata[i-1][j+1] * (-1) + gdata[i][j+1] * (-1) + gdata[i+1][j+1] * (-1);

                           b =   bdata[i-1][j-1] * (-1) + bdata[i][j-1] * (-1)  + bdata[i+1][j-1] * (-1)
                               + bdata[i-1][j] *   (-1)   + bdata[i][j] * (9) + bdata[i+1][j] * (-1)
                               + bdata[i-1][j+1] * (-1) + bdata[i][j+1] * (-1)  + bdata[i+1][j+1] * (-1);
                       }
                   }
                }
           }
           if (r < 0)  r = 0;
           if (g < 0)  g = 0;
           if (b < 0)  b = 0;
           if (255 < r)  r = 255;
           if (255 < g)  g = 255;
           if (255 < b)  b = 255;
           gpoint(i, j, r, g, b);
  
       }
       
    }

トップへ


授業資料 情報グラフィックス論U

Copyright 2002-2006 Yuki Toriumi