- ベストアンサー
16bppBMPの仕様について
環境 Windows7 VS2008 SP1 windows BMP の「16bpp」の仕様について質問です。 http://www5d.biglobe.ne.jp/~noocyte/Programming/Windows/BmpFileForm... によれば 16 最大 216 色.ビットマップデータの各 WORD が1画素を表す. と書かれているのですが、 これが本当だとして BGRは(B,G,R)=(5bit,5bit,5bit)で構成すればよいのでしょうか? 1bitはあまりです。 結果としては、 Windowsフォトビュワーやmspaintで 正常にみれればよいと考えています また、16bppの仕様がいまいちよくわかりません。 1)mspaintで24bppの画像を16bppで保存したもの 2)自分のプログラムで生成した16bpp画像 を比較すると、「ヘッダ部分」は全て合っているのですが、 「画像データ部分」が全く異なっています。 私は1lineが BGR(2byte)+BGR(2byte)+・・・・・・+修正値(4byteバウンダリ) だと思っているのですが 違うのでしょうか? ちなみに5bit 5bit 5bitをつめこむプログラムは以下です int i,j; int x=0,y=0; for(i=0;i<3*width*height;i+=3*width) { for(j=0;j<3*width;j+=3) { Output[x+y]=((layer[i+j])|(layer[i+j+1]>>SHIFT_R_5)); Output[x+y+1]=(((layer[i+j+1]>>SHIFT_R_3)<<SHIFT_L_6))|(((layer[i+j+2]>>SHIFT_R_3)<<SHIFT_L_1)); y+=2; } x+=2*width; y=0; } 間違っていたらご指摘お願いします。 以上 よろしくおねがいします
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 http://msdn.microsoft.com/ja-jp/library/cc352308.aspx 「RGB555/RGB565」を明確にする為に、BITMAPINFOHEADER::biCompressionにBI_BITFIELDSを指定して、ビットフィールドを設定します。 いつの間にかBI_RGBでも可能になっているみたいですが、まぁ、指定しておいた方が良いと思います。 デバイスコンテキストを利用した変換は以下URLを参照。 http://oshiete1.goo.ne.jp/qa5180838.html 大体以下の様になります。参考程度に。 #include <stdio.h> #include <windows.h> #define _RGB555_ #ifdef _RGB555_ const DWORD RMASK = 0x7c00; const DWORD GMASK = 0x03e0; const DWORD BMASK = 0x001f; const int GreenFactor = 8; #define RGBWORD(r, g, b) (((r) << 10) | ((g) << 5) | (b)) #else const DWORD RMASK = 0xf800; const DWORD GMASK = 0x07e0; const DWORD BMASK = 0x001f; const int GreenFactor = 4; #define RGBWORD(r, g, b) (((r) << 11) | ((g) << 5) | (b)) #endif //パディングを含めた1行当たりのバイト数 static DWORD CalcScanLineByte(const DWORD w, const DWORD bpp) { return ((((bpp * w) + 31) / 32) * 4); } //ビットマップヘッダの初期化 static void InitHDR(LPBITMAPINFOHEADER p, long w, long h) { p->biSize = sizeof(*p); p->biPlanes = 1; p->biBitCount = 16; p->biCompression = BI_BITFIELDS; p->biWidth = w; p->biHeight= h; p->biSizeImage = ::CalcScanLineByte(p->biWidth, p->biBitCount) * p->biHeight; } //RGBフィールドの設定 static void InitFields(DWORD f[]) { f[0] = RMASK; f[1] = GMASK; f[2] = BMASK; } //確認 int main(void) { //テスト用の24bitイメージ static BYTE layer[99 * 97 * 3]; layer[0] = 255; layer[(99 * 1 * 3) - 2] = 255; layer[(99 * 97 * 3) - 1] = 255; int w = 99; int h = 97; //ビットマップ情報ヘッダ BYTE buf[sizeof(BITMAPINFOHEADER) + sizeof(DWORD) * 3]; BITMAPINFO* pBmi = reinterpret_cast<BITMAPINFO*>(buf); ::InitHDR(&pBmi->bmiHeader, w, h); ::InitFields(reinterpret_cast<DWORD*>(&pBmi->bmiColors)); //ビットマップファイルヘッダ BITMAPFILEHEADER bfi = {0}; bfi.bfType = 'B' | ('M' << 8); bfi.bfOffBits = sizeof(bfi) + sizeof(buf); bfi.bfSize = bfi.bfOffBits + pBmi->bmiHeader.biSizeImage; //割り当てと変換 LPBYTE pOut = static_cast<LPBYTE>(::calloc(pBmi->bmiHeader.biSizeImage, 1)); const int nStride = ::CalcScanLineByte(pBmi->bmiHeader.biWidth, pBmi->bmiHeader.biBitCount); for(int y = 0; y < h; ++y) { for(int x = 0; x < w; ++x) { const int dpos = (x * sizeof(WORD)) + (nStride * (h - y - 1)); const int spos = (x * 3) + (w * 3 * y); const int R = layer[spos] / 8; const int G = layer[spos + 1] / GreenFactor; const int B = layer[spos + 2] / 8; LPWORD pWord = reinterpret_cast<LPWORD>(&pOut[dpos]); *pWord = RGBWORD(R, G, B); } } //ファイルへ書き込み FILE* pf = ::fopen("test16bit.bmp", "wb"); if(pf == NULL)return 0; ::fwrite(&bfi, sizeof(bfi), 1, pf); ::fwrite(buf, sizeof(buf), 1, pf); ::fwrite(pOut, pBmi->bmiHeader.biSizeImage, 1, pf); ::fclose(pf); //後始末 ::free(pOut); return 0; }
その他の回答 (3)
- penta1331
- ベストアンサー率64% (16/25)
こんにちは。 16bitのフォーマットに関してのみですが、参考となれば。 たとえばPhotoshop(最新版ではないですが...)などでBMPフォーマットで保存すると、16bitでは以下の方式を選択可能です。 - X1 R5 G5 B5 →最上位1bit未使用、RGB各5bit - A1 R5 G5 B5 →最上位1bitがαチャンネル、RGB各5bit - R5 G6 B5 →R5bit、G6bit、B5bit - X4 R4 G4 B4 →上位4bit未使用、RGB各4bit - A4 R4 G4 B4 →上位4bitαチャンネル、RGB各4bit 私自身が調べた時も、情報が見つからなかったので実際の画像編集ソフトで使われているフォーマットを参考にした記憶があります。 使用するソフトが決まっているならそのソフトのフォーマットだけに絞って作成する方がいいと思います。 もう試しているかも知れませんが、「R255 G0 B0」「R0 G255 B0」「R0 G0 B255」のようなファイルを作れば、ビット構成も推測できると思います。 αチャンネルについては、ビットマップではほとんど使われていないと思います。参考データを探すほうが難しい?
お礼
こんばんは! 具体的な調査方法などを伝授していだだき ありがとうございました。!
- pokapoka1980
- ベストアンサー率41% (48/117)
RGB16の2バイトの構成は以下 0x7C00 (0111 1100 0000 0000) <-赤のマスク 0x03E0 (0000 0011 1110 0000) <-緑のマスク 0x001F (0000 0000 0001 1111) <-青のマスク 最上位1ビットは未使用。 カラーテーブルを使う場合は、また別になるはずです。 あとはご自身で・・。
お礼
仕様をおしえてくださりありがとうございました! 助かりました!
- kmee
- ベストアンサー率55% (1857/3366)
(画像が入っていると思われる)layerの定義、添字の意味、各要素の仕様は? Outputはunsigned charの配列ですか? (マクロと思われる)SHIFT_R_5,SHIFT_R_3....の定義は? 同サイトの下の方、「5.ビットマップデータ」には ・biCompression=BI_RGB の場合 LSB から順に5ビットずつ,B,G,R の輝度を表す.MSB は未使用. とあります 幅widthの画像で、座標(列,行)が layer[ 行 * width *3 + 列 * 3 ] : Blue 5bit layer[ 行 * width *3 + 列 * 3 +1] : Green 5bit layer[ 行 * width *3 + 列 * 3 +2] : Red 5bit となっていて #define SHIFT_R_5 5 のように、最後の数字がそのまま定義されているとすると > Output[x+y]=((layer[i+j])|(layer[i+j+1]>>SHIFT_R_5)); layer[i+j+1]>>SHIFT_R_5 は5bitしかないデータを右に5bitシフトするので常に「0」になります。つまり、この文は > Output[x+y]=(layer[i+j]); と同じです。シフト方向が逆ではないでしょうか Output[x+y]=(((layer[i+j])|(layer[i+j+1]<<SHIFT_R_5))) & 0xff ; (念のため、 & 0xff を付けました) >Output[x+y+1]=(((layer[i+j+1]>>SHIFT_R_3)<<SHIFT_L_6))|(((layer[i+j+2]>>SHIFT_R_3)<<SHIFT_L_1)); layer[i+j+1]>>SHIFT_R_3 で5bit中の上位2bitが残り、それを左に6bitシフトしています。 Greenを g4 g3 g2 g1 g0という5bitだとすると g4 g3 0 0 0 0 0 0 となります。 (((layer[i+j+2]>>SHIFT_R_3)<<SHIFT_L_1))も同様に 0 0 0 0 0 r4 r3 0 となります。これらの | をとれば g4 g3 0 0 0 r4 r3 0 前の行は(修正したなら) g2 g1 g0 b4 b3 b2 b1 b0 ですから、r2 r1 r0 がありません。 >>SHIFT_R_3が余分だと考えれば (layer[i+j+2] <<SHIFT_L_1) は 0 0 r4 r3 r2 r1 r0 0 となり、 | の計算結果は g4 g3 r4 r3 r2 r1 r0 0 で、全ビットが入りました。しかし、これを16bitに並べると、リトルエンディアンなのでOutput[x+y+1],Output[x+y]の順になり g4 g3 r4 r3 r2 r1 r0 0. g2 g1 g0 b4 b3 b2 b1 b0 となります。これでは Gが分断されいるし、0も変な場所にあります。欲しいのは 0 r4 r3 r2 r1 r0 g4 g3 であり、それぞれの色に分解すれば 0 0 0 0 0 0 g4 g3 0 r4 r3 r2 r1 r0 0 0 なのですから、Greenは右に3bitシフトしたもの、Redは左に2bitシフトしたものです Output[x+y+1]=(layer[i+j+1]>> 3)| (layer[i+j+2]<<2); もう少しすっきりさせるなら、先に16bitにしてから、8bitずつ取り出すとよいでしょう int bpp16 ; /* 16bit計算用の変数: unsigned shortで十分だけど、どうせintで計算されるのだから */ とでもしておいて /* R <<10(=5+5) | G << 5 | B */ bpp16 = ( layer[i+j+2] << 10 ) | ( layer[i+j+1] << 5 ) | layer[i+j] ; Output[x+y] = bpp16 & 0xff ; Output[x+y+1] = (bpp16 >> 8) & 0xff ; --- 以下余談 ・Outputはそのまま出力するのでしょうか? 横方向のバイト数は4の倍数でないといけません。 このプログラムではwidthが奇数のときにおかしくなります。 width_step = (( width * 2 + 3)/ 4) * 4 ; と4の倍数に切り上げた変数を用意して Outputのサイズを height * width_step に x+=2*width; を x+=width_step ; にします。 ・変数の命名は自由と言えば自由ですが、普通は 横方向: x, width 縦方向: y, height を使います。 最初質問のプログラムを見たとき、縦横を入れ替えているのかと思いました。
お礼
こんばんは! 具体的なソースをだしてくれて大変助かりました また、指摘もありがとうございました。 プログラミングする上で気をつける様にします。 Outputはunsigned charの配列です layerもunsigned charです int i #define SHIFT_R_5 5 の様にしています。 ご教授 ご指摘ありがとうございました!
お礼
こんばんは! 具体的なソースをだしてくれて 本当にありがとうございます。 かなり助かりました 本当にありがとうございます