LoginSignup
14
6

More than 1 year has passed since last update.

ImageMagick で BMP処理

Last updated at Posted at 2017-12-26

BMP 画像

Windows の標準画像フォーマットです。
ImageMagick は、BMP2(v2)、BMP3(v3)、BMP(v4) に対応しています。尚、BMPv5 の読み書きも出来ますが、BMP(v4)の少しデータが大きい版として処理するだけで、ICCプロファイルの読み出しや埋め込みは出来ません。
(追記: 2019年2月中旬に ICCプロファイル読み書き対応しました)

% identify -list format | grep BMP
      BMP* BMP       rw-   Microsoft Windows bitmap image
     BMP2* BMP       -w-   Microsoft Windows bitmap image (V2)
     BMP3* BMP       -w-   Microsoft Windows bitmap image (V3)

はじめに BMP ファイルの version 毎の違いを並べて、後半で ImageMagick の操作オプションを紹介します。

BMPフォーマット概要

元々、Windowsで画像を処理する際のメモリ内部構造(いわゆる DIB)なので、ファイル形式の仕様もC言語の構造体で書かれています。あと、数値はリトルエンディアンです。

typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

Win32 API で使われるデータ型で、WORD が 2バイト(16ビット)、DWORD が 4バイト(32ビット)なので、

  • BITMAPFILEHEADER
bfType bfSize bfReserved1 bfReserved2 bfOffBits
WORD DWORD WORD WORD DWORD
2バイト 4バイト 2バイト 2バイト 4バイト

の並びを表現します。

BMPコンテナ図

bmp-container.png

バージョン毎の解説

BMPv3

BMP はよく未圧縮の画像フォーマットとして認識される事が多いのですが、図でいう Info に相当する BITMAPINFOHEADER (BMP v4 の一つ前という意味で BMPv3) で Compression (圧縮) 方式を指定できます。

BMPv4

また、BMP の特徴的な機能として BMPv4 で追加された RGBAMask があります、これは R,G,B,A のビット長をバラバラに持つ事ができる素晴らしい機能です。

BMPv4 は Windows95 / Windows NT からの仕様です。

BMPv5

最近、HDR が一般に少しずつ広まってきています。古いモニタに引きずられた sRGB 以外にも、DPI-P3 や Adobe RGB といった広い色域で鮮やかな色に対応するのに、ICC プロファイルで切り替える方法がメジャーですが、実は BMPv5 で対応しています。
BMPv5 は Windows 98 / Windows2000 からの仕様です。

ただし、ICCプロファイル埋め込みBMPファイルの読み書きに対応している画像ビューアはほぼ皆無なので、フィイルフォーマットとしては実質無視されている機能です。

-define bmp:〜

bmp オプションパラメータを解説します。

bmp:format (string)

BMP のバージョンを指示できます。

  • bmp2
  • bmp3
  • bmp4

例えば、-define bmp:format=bmp3 のようにすると、透明度データが消えます。
BMPv4 で追加された RGBAMask で透明度に対応したので、BMPv3 では基本的(後でこの例外にも触れます)に透明度を持てないからです。

% convert Opaopa-transparent.gif Opaopa-transparent.bmp
% convert Opaopa-transparent.gif -define bmp:format=bmp3 Opaopa-transparent-bmp3.bmp

(BMP 画像は Qiita に貼れないので、PNG で貼り付けます)

Opaopa-transparent.bmp.png Opaopa-transparent-bmp3.bmp.png
Opaopa-transparent.bmp.png Opaopa-transparent-bmp3.bmp.png

bmp:subtype (string)

BMPv4 で追加された RGBAMask を活用して、柔軟に RGBA の深度を指定できます。特によく使われる以下の4形式を subtype で指定できます。

  • ARGB4444
  • ARGB1555
  • RGB555
  • RGB565

ARGB4444 を適用すると、こんな感じです。(ちなみに透明度のない画像に ARGBxxxx 指定しても無視されるのでご注意を)

% convert 26040.png 26040.bmp
% convert 26040.png -define bmp:subtype=ARGB4444 26040-ARGB4444.bmp
26040.bmp.png 26040-ARGB4444.bmp.png
26040.bmp.png 26040-ARGB4444.bmp.png

ディザをかけずに、色の値を足切りする減色で微妙です。。
減色の良い仕方は別エントリで解説する予定ですが、とりあえずは以下の解説が参考になります。

bmp3:alpha (boolean)

先ほど、bmp3 にすると透明度が消えましたが、実は裏技がありまして。
BMPv3 でも BitCount:32 で BGRX形式にすると、仕様では未使用の X に 0 以外の値が入っている時にそれを Alpha値として使うシステムがあるそうです。bmp3:alpha は BitCount:32 に強制する事で、そのような状況にも対応します。

% convert Opaopa-transparent.gif -define bmp:format=bmp3 -define bmp3:alpha=true Opaopa-transparent-bmp3-alpha.bmp
% convert Opaopa-transparent-bmp3-alpha.bmp Opaopa-transparent-bmp3-alpha.bmp.png
Opaopa-transparent-bmp3-alpha.bmp.png
Opaopa-transparent-bmp3-alpha.bmp.png

とはいえ、X を透明度として処理するのは公式仕様にないので、例外として考えた方がよいです。(例えば、macOS のプレビューは対応していません)

追記

BMPv4 Gamma の注意 (追記)

こちらでガンマ値について言及されていたので、折角なので解説します。

BMPv3 では無かったガンマ値フィールドが BMPv4 で追加されています。
ところが、実はこのガンマ値フィールドは滅多に使われません。

その少し上に CSType フィールドがありまして、これに LCS_CALIBRATED_RGB が指定された時だけ、ガンマ値フィールドが参照される決まりです。
実際には LCS_sRGB が入る事が多いので、ガンマ値フィールドには 0 が埋まります。(仕様では ignore としか書いてないので 0 が埋まる保証はなさそうですが。。)

 typedef  enum
 {
   LCS_CALIBRATED_RGB = 0x00000000,
   LCS_sRGB = 0x73524742,
   LCS_WINDOWS_COLOR_SPACE = 0x57696E20
 } LogicalColorSpace;

Windows らしくリトルエンディアンで数値を埋めるので、BMP画像ファイルをテキストとして開くと、"BGRs" の4文字が見えるでしょう。もしかしたら " niW" の方かもしれません。

興味のある方はテキストエディタでご確認下さい。

identify で BMPv3 は gamma が表示され、BMPv4 だと gamma 無し。仕様と逆じゃない?

ImageMagick はガンマ値 1/2.2(=0.4545)がデフォルトで、BMPv3 のように画像ファイルにガンマ値を持たない場合、その 0.4545 を表示します。一方、BMPv4 で identify がガンマ値を表示しないのは、ほぼ不具合に近い微妙な動作でして。。

BMPv4 のガンマ値(&その隣のCIEXYZ-3刺激値)は、CSType が Calibrated RGB の時だけ使われるフィールドで、大抵 CSType は sRGB なので、その場合にはガンマ値に 0 が埋まってます(←お行儀がよければ)。ImageMagick はそれを場合分けせず本当にガンマ値が 0 だとして取り込んでしまい、0 は本来ありえない値なので、identify では表示しないという流れです。

恐らく不具合だと思いますが、今何故それでも問題ないの?というコードを一通り検証したくなるので、問題提起は後回しにしてます。
gamma:0 は異常な値でプログラムの通る場所によっては警告もでますし、特に 0 をデフォルト値扱いするようにも見えません。
ちなみに PNG だと gamma:0 の時は gAMA チャンクを埋めない条件が入ってる上に 0.75 以下だと sRGB として処理。JPEG はそもそも gamma は処理しない。という事でそれらへの変換に問題はなさそうです。多分、綱渡りかなと。。

こうなっている事情の推測

BMPv4 の仕様を見ると。

bV4CSType
The color space of the DIB. The following table lists the value for bV4CSType.
Value Meaning
LCS_CALIBRATED_RGB This value indicates that endpoints and gamma values are given in the appropriate fields.

となってまして。v4 では LCS_CALIBRATED_RGB しか選べないと解釈するのが自然なので、ImageMagick はそれ前提でコードを書いてるのかなと。

そういえば、sRGB 規格自体 98年に出たので、BMPv4(Windows95)の頃には存在しない規格を指定しようもなく、BMPv5(Windows98)が出た頃に後付で BMPv4 にも sRGB を入れるベンダーが出てきたと考えると、MSのドキュメントがこうなっているのは自然な気がします。

BMP画像ファイル出力のデフォルトは v4 と v5 どっち?

マニュアルでは v4 です。

By default the BMP format is version 4

実際そういう意図をもってコードが書かれていますが、実際には BMPv5 (相当)が生成される事が多いです。
ImageMagick はデフォルトで rendering intent を 2(Perceptual)とするので、この条件にひっかかるからです。

if ((image->rendering_intent != UndefinedIntent) ||
    (profile != (StringInfo *) NULL))
  {
    bmp_info.size=124;
    extra_size+=16;
  }

BMPv5 では rendering indent と icc profile のフィールドが追加されたので、こうしているのですが、それをわざわざ打ち消すような処理をしない限り、v5 を出力する結果になります。

convert in.png -intent Undefined  out.bmp

このように明示的に intent を打ち消せば BMPv4 を出力します。サイズが勿体無い場合は、どうぞ。ただし、ICC Profile が入っていた時に事故りそうなので、そこは注意で。

あと、rendering intent は icc profile に従属する概念なので、条件式では profile だけ見たほうが良さそうですが、画像フォーマット的には今のが正しいので、変更しよという提案はちょっと面倒そうです。

そもそも BMP version について。

実は ImageMagick に明示的な v5 は無いです。単に v4 のサイズがちょっと大きな形式として処理します。
v2 は BMP2、v3 はBMP3 としてるのに、v4 も v5 もごっちゃに BMP として扱います。
なので、あまり拘っても意味がないかもしれません。

最後に

BMP ヘッダをバイナリ分解するツールがあるので、どうぞ。 v4 まで対応してます。

v5 も簡単なので時間があったら対応します。 v5 も対応しました

参考

14
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6