C言語による画像処理プログラミング

静岡大学工学部
安藤和敏

はじめに

私の接した学生のうちの多くは, 抽象的な数学的理論を嫌う傾向にあるように思われた. ならば, 具体的で「目に見える形で」結果が得られるものを研究対象として与えてやれば, 興味を持ってくれるのではないかという推測の下に, 学生と一緒に画像処理のプログラミングを始めてみた.

画像形式としては, Windows bitmap (以下 BMP と略記) を選んだ. (基本的に)無圧縮であることが分かりやすく, Windows標準であることが親近感を与えるというのがその理由である.

既にJavaやC++で実装された, BMP形式の画像を扱うことができる関数を含んだライブラリが存在する. しかし, 本来やりたいことの量に対して, 覚えなければいけないことが多すぎる. C言語で気軽に実装してみたい.

テストデータ

以下の画像は, この文書で繰返し登場する. 被写体は, 筑波大学の第2食堂の北側にある自転車置場につながれていた犬である. たぶん雑種であろうが, なかなか賢そうな顔である. カメラを向けると堂々としたカメラ目線を向けてくれた. 筆者が撮影したものであるが, 著作権を主張するつもりはない. (この犬の肖像権を主張される方はご連絡を下さい. )

BMPファイルの構造

BMP形式のファイルは下図のような構造を持っている.

オフセットバイト数説明備考
0x0000 (0)14ファイル・ヘッダ 
0x000e (14)40情報ヘッダ 
  カラー・パレット2,4,8ビット色BMPの場合にのみ存在する.
0x0036 (54) データ部 

dog2.bmpを, 適当なバイナリ・エディタで開いてみると, 下図のような感じになる.

これは, emacs のhexl-modeで開いた状態であり, 16バイトを1行として, 各バイトを16進表記してくれる. 一番左の列は, 行番号(16進表記)である. このファイルは24ビット・カラーBMPであるので, カラー・パレットは存在しない. 以降では, 24ビット・カラーBMPについてのみ考えるので, カラー・パレットについては考察しない.

ヘッダ部

ファイル・ヘッダ

ファイル・ヘッダは以下の表のような構造を持っている.

オフセットバイト数説明備考
0x0000 (0)2ファイル・タイプ0x42,0x4d ("BM")
0x0002 (2)4ファイル・サイズバイト
0x0006 (6)2予約領域1常に0
0x0008 (8)2予約領域2常に0
0x000a (10)4画像データまでのオフセット 

dog2.bmpのファイル・ヘッダを見てみよう.

ファイル・タイプには, 0x42, 0x4dと書かれているが, これらはそれぞれASCIIコードの 'B'と'M'である. ファイル・サイズは, 「リトル・エンディアン」式に, つまり, 下位バイトから順に書かれているので, 0x09ca76 = 641,654 [バイト] である.

情報ヘッダ

情報ヘッダは以下の表のような構造を持っている.

オフセットバイト数説明備考
0x000e (14)4情報ヘッダのサイズバイト
0x0012 (18)4画像の幅ピクセル
0x0016 (22)4画像の高さピクセル
0x001a (26)2プレーン数常に1
0x001c (28)21画素あたりの色数ビット
0x001e (30)4圧縮形式 
0x0022 (34)4画像データのサイズバイト
0x0026 (38)4水平解像度ppm
0x002a (42)4垂直解像度ppm
0x002e (46)4パレットの色数 
0x0032 (50)4重要のパレットのインデックス 

dog2.bmpの情報ヘッダを見てみよう.

情報ヘッダのサイズは, 0x0028 = 40 である. 画像の幅は, 0x00000216 = 534 [ピクセル], 画像の高さは, 0x00000190 = 400 [ピクセル] である. 1画素あたりの色数は, 224=16,777,216 である. 画像データのサイズは, 0x0009ca40 = 641,600 [Byte] である. (ファイル・ヘッダにあったファイル・サイズは, 641,654 = 641,600 + 54 [Byte] であった. ) 水平解像度と垂直解像度はここでは, 1 [ppm] となっているが, 0x00000b12=2,834 [ppm] となっているファイルもある.

画像のサイズが 534×400 [ピクセル] で, 1ピクセルあたり24ビット(=3バイト)なのだから, 本当は, 画像データのサイズは, 534×400×3 = 640,800 [Byte] のはずなのであるが, 画像データのサイズはこれより 800バイト大きい. この理由については, 次節で述べる.

データ部

オフセット 0x0036=54 からファイルの終りまでがデータ部である. (画像の幅×画像の高さ)個の各画素について, その色の情報が並んでいる.

dog2.bmpのデータ部の最初の数十バイトを見てみよう.

オフセット0x0036〜0x0038の3バイトは, B (青) の輝度, G (緑) の輝度, R (赤) の輝度という順番で, 1つの画素の色を表現している. したがって, 通常の色の表記法にしたがえば, この3バイトの表す色は 0xe7c4ca である. 同様に, オフセット0x003f〜0x0041 の3バイトは, 0xeae7cd という色を表し, オフセット0x0051〜0x0053は 0xcad0ea という色を表現している.

これらの画素たちは, 下の行から上の行へ, 各行については左から右へ, という順番で並んでいる (下図を見よ).

したがって, オフセット0x0036〜0x0038の3バイト0xe7c4ca は, dog2.bmp の 左下隅の画素ということであり, このファイルの最後の3バイトは右上隅の画素を表現しているということになる.

上図は, 幅11×高さ11ピクセルの画像を模式的に示しているのであるが, データ部は, 11×11×3=363バイト以上の大きさを持っている. なぜならば, 画像データは「各行が4の倍数のバイト数を持つ」という要求を満すように保存されるからだ. 画像の幅×3が4の倍数でない場合は, 右端に 0 を補って一行の長さ (バイト数) が 4の倍数になるように調整される.

例えば, 上図のように幅が11の画像の場合, 11×3 = 33 は 4 の倍数ではないので, 3つの0が各行の右端に入る. したがって, 画像データのサイズは全体で (33+3)×11 =396 バイトになる. また, dog2.bmpのサイズは534(幅)×400(高さ)であるから, 画像データ一行分のサイズは, 534×3+2=1,604 [バイト]であり, したがって画像データ全体では, 1,604×400=641,600 [バイト] となる.

dog2.bmpのデータ部を見て, この事を確かめてみよう. 最初の1行分のデータの終りは, 54 + 534×3 +2 -1 = 0x0679 であるが, 0x0678と0x0679には0が入っていることが確認できるであろう.

BMPファイル操作のためのC言語ライブラリ

コンパイルの仕方

例えば gcc でコンパイルする場合は,
     gcc bmp.c test0.c -o test0
    
とする.

参考文献

  1. http://www.kk.iij4u.or.jp/~kondo/bmp/

ホームへ戻る
Kazutoshi Ando
Last modified: Thu Oct 5 15:55:15 JST 2006