画像処理入門1

はじめに

画像処理、って聞くとなんだか難しそうなイメージがありますが、
実はやってみると、そんなに難しくありません。
 
たぶん難しいのは、画像処理を解説している本が、
難しそうな数式などを使っていたりと、
わざと難しく書いてあるだけなのではないかと思っていますw
 
ということで、そんな画像処理をできるだけ簡単に解説しようと思います。
 

画像とは?

ずばり、ピクセルのカタマリのことです。
(正確には、画像にはベクタ画像とラスタ画像があり、ここではラスタ画像のことを言っているのですが、その説明はまた今度に…)
ピクセルとは、画素とかドットとか、画像を構成する最小単位のことです。
 
例えば、400x300の画像であれば、120000ピクセル(画素)の画像であるわけです。
 
ということで、画像処理プログラムをする上で、一番意識しなければならないのが、
このピクセルというメモリのカタマリを操作する、ということです。
 
そして、1ピクセルには、「R・G・B」という3つの要素があります。
この「R・G・B」がそのピクセルの色を表現しています。
 
そして、ここでは24bit画像を扱います。
24bit画像というのは、Rが8bit、Gが8bit、Bが8bitの画像のことです。
つまり、それぞれの要素が0〜255の値を取る、ということです。
 
ピクセルについては以上です。
 

Bitmapとは?

画像処理をする上で、もっとも簡単な画像形式がBitmapです(たぶん)
ということで、Bitmapのファイル形式を解説します。(というか、これしか知らないので…(;´∀
 
Bitmapは、大きく分けて以下の3つの部分に分かれています。

  • BITMAPFILEHEADER(ファイルヘッダ構造体)
  • BITMAPINFOHEADER(インフォヘッダ構造体)
  • ピクセル

 
それぞれの構造体は、以下の属性を持っています。
 

「BITMAP FILE HEADER」構造体
  • bfType :ファイルタイプ識別子(重要)
  • bfSize :ファイルのサイズ(重要)
  • bfReserved1 :予備
  • bfReserved2 :予備
  • bfOffBits :画像データの開始位置(重要)
「BITMAP INFO HEADER」構造体
  • biSize :ヘッダのサイズ(重要)
  • biWidth :画像の幅(重要)
  • biHeight :画像の高さ(重要)
  • biPlanes :プレーン数(重要)
  • biBitCount :ピクセルの色数(重要)
  • biCompression :圧縮方式
  • biSizeImage :画像サイズ
  • biXPelsPerMeter :水平方向解像度
  • biYPelsPerMeter :垂直方向解像度
  • biClrUsed :使用色数
  • biClrImportant :重要な色数

 
なんだか、たくさんありますが、重要と書いてある部分だけ注意すればOKです。
それ以外は、0でも入れておけば問題ありません(笑)
 
1つずつ解説していきます。
まずは、「BITMAP FILE HEADER」構造体です。
 
bfTypeというのは、このファイルが何であるか、ということです。
Bitmapなので、'BM'と入れます。
 
bfSizeはファイルのサイズです。これはそのままですね。
 
bfOffBitsは、「54」固定です。なぜかというと、
「BITMAP FILE HEADER」構造体のサイズが「14」。
「BITMAP INFO HEADER」構造体のサイズが「40」。
14+40=54で、54バイト目から、ピクセルのデータがあるからですね。
 
続いて、「BITMAP INFO HEADER」構造体です。
biSizeは「40」固定です。
なぜなら、「BITMAP INFO HEADER」構造体のサイズが「40」だからです。
 
biWidthとbiHeightには、画像のサイズを入れます。
例えば、400x300の画像であれば、biWidthは「400」、biHeightは「300」になります。
 
biPlanesは「1」固定です。理由は不明です。(…)
 
biBitCountは「24」固定です。24bit画像を扱うからです。
 

では、プログラムしてみよう!

 
とりあえずは、「Bitmapの読み込み・書き込み」処理を作ってみます。
まずは、Bitmapに慣れることです。
 
VisualC++であれば、windows.hをインクルードすれば、ヘッダの定義は完了ですが、
それ以外の環境でもできるように、テンプレートを用意しました。
 

typedef long  LONG;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned char BYTE;

/*BITMAPFILEHEADER*/
struct BITMAPFILEHEADER
{
	WORD  bfType;
	DWORD bfSize;
	WORD  bfReserved1;
	WORD  bfReserved2;
	DWORD bfOffBits;
};

/*BITMAPINFOHEADER*/
struct BITMAPINFOHEADER
{
	DWORD biSize; 
	LONG  biWidth;
	LONG  biHeight;
	WORD  biPlanes;
	WORD  biBitCount;
	DWORD biCompression;
	DWORD biSizeImage;
	LONG  biXPelsPerMeter;
	LONG  biYPelsPerMeter;
	DWORD biClrUsed;
	DWORD biClrImportant;
};

const int MAX_PIXEL = 512;
struct MY_BITMAP
{
	BITMAPFILEHEADER bmf;
	BITMAPINFOHEADER bmi;
	BYTE pixel[MAX_PIXEL][MAX_PIXEL][3];
};

MY_BITMAP gBitmap;
void main()
{
	// ここに処理を書く
}

 
読み込む画像は、24bitビットマップであれば何でも構いません。
 
とりあえずは、ヘッダの情報をテキストファイルに書き出すことからはじめましょう。
そのためには、FILE構造体、fopen、freadなどを駆使する必要があります。
ここらへんは、「ポインタを本当に理解しているか?」ということを試されますので、
自信のない方は是非、挑戦してみてください。
 
答えは、過去の日記にあります(笑)
 
ということで、次回に続きます。
(というか、今回は画像処理でもないような…)
 

補足

Bitmapには、ヘンテコな仕様が3つあります。