BMPファイルをバイナリエディタで見る
コンテンツ
はじめに
プログラミングをしている時ファイルの読み込みはどうやって行っているのか気になったことはないでしょうか。どのプログラミング言語も基本的なライブラリは十分で画像の読み込みがしたければ、それ用のライブラリを探して導入すればすぐに実現可能です。しかし、仕組みぐらいは知っておいた方がいいのではないかとよく思います。というか…自分は低級(高級言語の高級の対義語としての低級)なことにも興味がある質で、そもそもどうなっているのかということが結構気になってしまいます。ファイルといってもテキスト、音声、画像、動画、実行ファイル、特定のソフトウェア(エクセル)のファイル等様々です。今回は初歩の初歩のそもそもファイルってどんな構造になっているかの一例を調べたのでそのメモです。今回調べたファイルは画像ファイルのビットマップ(bmp : bitmap)ファイルです
バイナリエディタでbmpファイルをみる
バイナリエディタの見方
バイナリエディタでファイルをあまり見たことがなかったので見方のポイントをまとめておきます。
バイナリエディタとは
バイナリエディタとは、任意の形式のバイナリファイルの内容を読み込んで表示し、編集することができるソフトウェア。データを16進数(hexadecimal)の数値列として表示する方式が一般的なため、英語では “hex editor” と呼ばれる。
e-wordsでは以上のようになっています。バイナリファイルと呼ばれるものを16進数で表示して編集するソフトです。バイナリファイルとは、いづれかの文字コードで文字にデコード(単語の使い方が正しいか不明…0,1の数列を文字コードを元に文字にする)したときに人が理解できないようなデータ(バイナリデータ)のファイルを指します。文字にしたとき人が読めるようなデータはテキストデータといい、そのファイルがテキストファイルです。テキストファイルも所詮はバイナリですが、一般的にバイナリファイルというときはテキストファイル以外のファイルを指すことが多いようです。
実際の画面
以下の画像をバイナリエディタで開いた画像をその下に記載しました。バイナリエディタは、Visual Studio Code に Hex Editor というプラグインを入れたもの使用しています。
バイナリを見るのに覚えておきたいこと
バイト(Byte)
コンピュータで情報(数値)を扱うには、2値(binary)が便利なためコンピュータでは2進法(binary notation)が用いられています。2進法とは、0と1のみで数字を表すことです。人が普段使用しているのは、0~9を使って数字を表す10進法です。2進法の1桁のことを1bit(Binary digit や bits of informationが語源らしい)といい、コンピュータなどが処理する基本単位のbit数を表すのがByte(Bite:噛むをもじった造語の説がある)です。現在は 1Byte = 8bit が一般的です(6,7,9bitなどを処理の基本単位とする計算機もある…った?)。
バイナリエディタでは、1Byteすなわち8bit毎を16進数で表示します。16進数は0,…,9,A,…,Fの16の記号で数字を表します。1Byteは8bitなので2桁の16進数で表されます(0~255は16進数では2桁)。10進数で表示はエディタの機能についていたりするのですが、2進数や8進数の変換の簡単な方法を以下にメモしておきます。
- 16進数 ← → 2進数
- 8進数 ← → 2進数
4 | 2 ← → 0100 | 0010 (10進数で 66)
16進数から2進数への変換は各桁を4bitの2進数にしてつなげます。2進数から16進数への変換は2進数を4bitに区切り、各フィールドを16進数の数値に変換してつなげます
1 | 0 | 2 ← → (0)01 | 000 | 010 (10進数で 66、 8bitを下の桁から3bitに区切る)
8進数から2進数への変換は各桁を3bitの2進数にしてつなげます。2進数から8進数への変換は2進数を3bitに区切り、各フィールドを8進数の数値(0~7)に変換してつなげます
オフセット(アドレス)
コンピュータ関連の用語としてのオフセットは所定の位置までの、先頭からの距離を示す数です。以下の画像の左側(縦軸)の数値(16進数)と上側(横軸)の数値(16進数)を足した数が先頭から何バイト目かを表します。たまにこれをアドレスとしているバイナリエディタもあり、ファイルの先頭のバイトをアドレス0(16進数)としてそこから10バイト目をアドレスA(16進数)というような感じです。どちらでも問題ないですが考え方的にオフセットの方がしっくりきます。
ASCII(American Standard Code for Information Interchange)
現代英語や西ヨーロッパ言語で使われるラテン文字を中心とした文字コード
以上のようにASCIIは文字コードです。バイナリエディタでは、上記の画像右側のようにASCIIでデコードされたテキストが表示されます。
エンディアン(バイトオーダ)
2Byte以上のデータをメモリに格納するときや転送するときにはバイト毎に行い、そのバイトの順番をエンディアン(バイトオーダ)といいます。エンディアンには2種類あり、最下位のバイトから順番(メモリならアドレスが昇順)に格納または送信する方法をリトルエンディアンで、最上位のバイトから順番に格納または送信する方法をビックエンディアンです。
bmpファイルの構造
バイナリエディタはファイルを解析したいがために使うのが普通かもしれませんが、今回はファイルの構造を知ったうえでバイナリエディタで見ます。
ファイルの仕様書は公開されているものもあれば、非公開のものもあります。公開されているファイルの仕様はオープンフォーマットと呼ばれるため「オープンフォーマット」で検索するとどんなファイルの仕様が公開されているがわかるものもあります。例えば、PNGは仕様が公開されています( PNGの仕様 )。
bmpファイルはというと公開されている仕様書がないと思われるので、有志の方が公開してくれている構造を参考に見ます。bmpは、windowsが標準で対応している形式なのでwindowsのドキュメントに、bmpに関連するクラスなどが載っており、それを参考にしているようです(windows app development)。
全体像
bmpファイルの構造は順番から次のようになっています。
順番 | 要素 | サイズ | 備考 |
---|---|---|---|
1 | ヘッダ | 14byte | BMPファイルであることやファイルサイズなどの基本的な情報が記述されている |
2 | 情報ヘッダ | 12~124byte | 画像データ扱う上での必要な情報が記述されている いくつかの種類があってサイズがことなる |
3 | ビットフィールド | 4byte | 特定の形式のBMPファイルの場合に必要な情報が記述されている 必要がなければ存在しない |
4 | カラーパレット | カラーインデックスによる | 特定の形式のBMPファイルの場合に必要な情報が記述されている 必要がなければ存在しない |
5 | 画像データ | 画像のピクセル数による | いくつかの形式がある(情報ヘッダに記述されいてる) |
また、BMPファイルの整数値データはリトルエンディアンになっている。以下でヘッダ、情報ヘッダ、画像データについて簡単に説明します。
ヘッダ
ヘッダは以下で構成されています。ファイルそのものの情報が格納されています。
オフセット | 要素 | サイズ | 備考 |
---|---|---|---|
0x00 | ファイルタイプ | 2byte | 1byte目に「B」、2byte目に「M」が格納されている。BMPファイルを示す。 |
0x02 | ファイルサイズ | 4byte | ファイル全体のサイズ(符号なし整数) |
0x06 | 予約領域1 | 2byte | 将来の拡張用 |
0x08 | 予約領域2 | 2byte | 将来の拡張用 |
0x0A | 画像データまでのオフセット | 4byte | BMPファイルの先頭から画像データまでのオフセット(符号なし整数) |
情報ヘッダ
情報ヘッダはいくつか種類があるようですが、以下はよく使われているinfoヘッダと呼ばれる情報ヘッダの構成です。
オフセット | 要素 | サイズ | 備考 |
---|---|---|---|
0x0E | ヘッダのサイズ | 4byte | 情報ヘッダの種類に関わらず情報ヘッダの初めの4byteは情報ヘッダのサイズ(0x28) |
0x12 | 画像の幅 | 4byte | 画像の幅(符号つき整数) |
0x16 | 画像の高さ | 4byte | 画像の高さ(符号つき整数) |
0x1A | プレーン数 | 2byte | 常に1 |
0x1C | 1ピクセルのbit数 | 2byte | 1ピクセルの色を表すのに必要なbit数(符号なし整数) |
0x1E | 圧縮形式 | 4byte | 画像データの圧縮形式 0,1,2,3,4,5で0が非圧縮(1,2,…参考ページ参照) |
0x22 | 画像データサイズ | 4byte | 画像データのサイズ(符号なし整数、単位はbyte) |
0x26 | 水平方向の解像度 | 4byte | 水平方向の解像度(符号なし整数、ピクセル/m) |
0x2a | 垂直方向の解像度 | 4byte | 垂直方向の解像度(符号なし整数、ピクセル/m) |
0x2e | カラーインデックス数 | 4byte | カラーパレットの色数(符号なし整数) |
0x32 | 重要な色数 | 4byte | カラーパレットの色で正確に表せなければならない色数。0はすべて正確に表す |
画像データ
情報ヘッダに記述されている形式で画像データが格納されている。形式は非圧縮、ランレングス符号での圧縮、JPEG、PNGがある。BMPファイルの画像データは行ごとのデータ(行はピクセルからなる)からなり、幅や高さが正の整数のときは、画像の下から上にかけての順で並んでいます。反対に幅や高さが負の整数のときは、画像の上から下にかけての順で並んでいます。
例えば、1ピクセルのbit数が24で圧縮形式が0の場合は画像データは、一番左下のピクセルの青成分の値(1byte)、一番左下のピクセルの緑成分の値(1byte)、一番左下のピクセルの赤成分の値(1byte)、一番下の左から2番目のピクセルの青成分の値、…となっている。
実際にBMPファイルをみる
BMPファイルのヘッダ、情報ヘッダ、画像データ部分をバイナリで見てます。
ヘッダ
BMPファイルのヘッダの最初はファイルタイプとなっており、以下の画像のように「BM」となっていることが確認できました。
ファイルタイプ「BM」の次はファイルサイズが格納されています。オフセットは0x02~0x05でリトルエンディアンなので、値は0x000222F6となっており、エクスプローラのプロパティ表示のサイズと一致しています。
ファイルサイズのあとは、予約領域がありそのあとに画像データまでのオフセットが4byteで格納されています。ヘッダが14byte、情報ヘッダが40byteでオフセットは54byteになります。
情報ヘッダ
BMPファイルの情報ヘッダの最初は情報ヘッダのサイズとなっており、最初の4byteに40が格納されています。
情報ヘッダのサイズの後ろには、画像の幅と高さが格納されています。見るとエクスプロラーのプロパティ表示と一致する値がちゃんと格納されていました。
画像の幅、高さのあとはプレーン数が2byteで格納されており、その後ろに1ピクセルを何bitで表すかの値が入っています。
画像データ
今回の画像は圧縮形式(オフセット 0x1E)が 0 で非圧縮となっていて、1ピクセルが24bitとなっているので、一番左下のピクセルの青、緑、赤成分の値から画像データが始まります。以下で確認できました。