高さ/幅が可変の 1 ビットの bmp ファイルを、値が 0 または 1 の単純な 2 次元配列に変換しようとしています。私が必要とするよりも高いビット深度が含まれていることがわかりました。これに関するヘルプは素晴らしいでしょう。
4 に答える
モノクロの.bmpファイルを読み取るためのコードは次のとおりです
(奇数サイズの.bmpsの小さな修正については、以下のdmbの回答を参照してください)
#include <stdio.h>
#include <string.h>
#include <malloc.h>
unsigned char *read_bmp(char *fname,int* _w, int* _h)
{
unsigned char head[54];
FILE *f = fopen(fname,"rb");
// BMP header is 54 bytes
fread(head, 1, 54, f);
int w = head[18] + ( ((int)head[19]) << 8) + ( ((int)head[20]) << 16) + ( ((int)head[21]) << 24);
int h = head[22] + ( ((int)head[23]) << 8) + ( ((int)head[24]) << 16) + ( ((int)head[25]) << 24);
// lines are aligned on 4-byte boundary
int lineSize = (w / 8 + (w / 8) % 4);
int fileSize = lineSize * h;
unsigned char *img = malloc(w * h), *data = malloc(fileSize);
// skip the header
fseek(f,54,SEEK_SET);
// skip palette - two rgb quads, 8 bytes
fseek(f, 8, SEEK_CUR);
// read data
fread(data,1,fileSize,f);
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
free(data);
*_w = w; *_h = h;
return img;
}
int main()
{
int w, h, i, j;
unsigned char* img = read_bmp("test1.bmp", &w, &h);
for(j = 0 ; j < h ; j++)
{
for(i = 0 ; i < w ; i++)
printf("%c ", img[j * w + i] ? '0' : '1' );
printf("\n");
}
return 0;
}
これはプレーンCであるため、ポインタのキャストはありません。C++で使用する場合は注意してください。
最大の問題は、.bmpファイルの行が4バイトで整列されていることです。これは、シングルビットイメージで非常に重要です。したがって、線のサイズは「幅/ 8 +(幅/ 8)%4」として計算されます。各バイトには1つではなく8つのピクセルが含まれているため、kベースのループを使用します。
他のコードが明白であることを願っています-.bmpヘッダーとパレットデータ(スキップする8バイト)について多くのことが語られています。
期待される出力:
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 0 0 0 0 0 1 1 1 1 0 0 1 1 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 1 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
20x20 のテスト イメージで Viktor Lapyov のソリューションを試しました。
しかし、彼のコードを使用すると、次の出力が得られます (わずかに再フォーマットされていますが、問題を確認できます)。
最後の 4 ピクセルは読み取られません。問題はここにあります。(行の最後の部分バイトは無視されます。)
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
内側のループを次のように書き直しました。
// decode bits
int i, byte_ctr, j, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for( i = 0; i < w; i++) {
byte_ctr = i / 8;
unsigned char data_byte = data[j * lineSize + byte_ctr];
int pos = rev_j * w + i;
unsigned char mask = 0x80 >> i % 8;
img[pos] = (data_byte & mask ) ? 1 : 0;
}
}
すべて順調です:
1x7 のモノクロ ビットマップを考えてみましょう。つまり、これは幅 7 ピクセルの直線のビットマップです。このイメージを Windows OS に保存するには; 7 は 4 で割り切れないため、3 バイト余分にパディングされます。
したがって、BITMAPINFOHEADER 構造体の biSizeImage は合計 4 バイトを示します。それにもかかわらず、biHeight および biWidth メンバーは、実際のビットマップ ディメンションを正しく示します。
上記のコードは 7 / 8 = 0 であるため失敗します (すべての c コンパイラと同様に四捨五入により)。したがって、ループ「i」は実行されず、「k」も実行されます。
これは、ベクター "img" に、" data" に含まれるピクセルに対応しないガベージ値が含まれていることを意味します。つまり、結果が正しくありません。
そして、帰納的推論により、基本ケースを満たさない場合、一般的なケースにはあまり役に立たない可能性があります.