问题描述
首先,这不是重复的。我已经阅读了
(33 x 20单色位图)
输出:
颜色表:0x000000 0xFFFFFF
000000000000000000000000000000000
000001111111111111111111111111110
000001111111111111111111111110
000001111111111111111111111110
00000111111111111 011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
0111111111111111111 11111111110010
011111111111111111111111111111110010
011111111111111111111111111111111110
000000000000000000000000000000000
请注意上面的这一行代码:
int pos = y *行大小+ x / 8;
int位= 1<< (7-x%8);
int v =(data [pos]& bit)> 0;
printf(%d,v);
首先我将其写为
int位= 1<< (x%8);
但这显示位的顺序错误,因此我不得不更改为 1<< (7-x%8)
这基本上就是您所做的。我不知道为什么要这样设计。
(以上代码仅适用于低端字节序机器)
First off, this is not a duplicate. I have already read Converting 1-bit bmp file to array in C/C++ and my question is about an inconsistency I'm seeing in the formulas provided with the one that works for me.
The Issue
I am trying to read in a 1-bit Bitmap image that was created in MS Paint. I've used the code provided by other answers on this site, but there are a few things I had to change to get it to work, and I want to understand why,
Change 1: lineSize
must be doubled
Original
int lineSize = (w / 8 + (w / 8) % 4);
Mine:
int lineSize = (w/ 8 + (w / 8) % 4) * 2;
Change 2: Endianness must be reversed
Original:
for(k = 0 ; k < 8 ; k++)
... (data[fpos] >> k ) & 1;
Mine:
for (int k = 7; k >= 0; --k) {
... (data[rawPos] >> k) & 1;
Full Code
NOTE: This code works. There are some changes from the original, but the core read part is the same.
vector<vector<int>> getBlackAndWhiteBmp(string filename) {
BmpHeader head;
ifstream f(filename, ios::binary);
if (!f) {
throw "Invalid file given";
}
int headSize = sizeof(BmpHeader);
f.read((char*)&head, headSize);
if (head.bitsPerPixel != 1) {
f.close();
throw "Invalid bitmap loaded";
}
int height = head.height;
int width = head.width;
// Lines are aligned on a 4-byte boundary
int lineSize = (width / 8 + (width / 8) % 4) * 2;
int fileSize = lineSize * height;
vector<unsigned char> rawFile(fileSize);
vector<vector<int>> img(head.height, vector<int>(width, -1));
// Skip to where the actual image data is
f.seekg(head.offset);
// Read in all of the file
f.read((char*)&rawFile[0], fileSize);
// Decode the actual boolean values of the pixesl
int row;
int reverseRow; // Because bitmaps are stored bottom to top for some reason
int columnByte;
int columnBit;
for (row = 0, reverseRow = height - 1; row < height; ++row, --reverseRow) {
columnBit = 0;
for (columnByte = 0; columnByte < ceil((width / 8.0)); ++columnByte) {
int rawPos = (row * lineSize) + columnByte;
for (int k = 7; k >= 0 && columnBit < width; --k, ++columnBit) {
img[reverseRow][columnBit] = (rawFile[rawPos] >> k) & 1;
}
}
}
f.close();
return img;
}
#pragma pack(1)
struct BmpHeader {
char magic[2]; // 0-1
uint32_t fileSize; // 2-5
uint32_t reserved; // 6-9
uint32_t offset; // 10-13
uint32_t headerSize; // 14-17
uint32_t width; // 18-21
uint32_t height; // 22-25
uint16_t bitsPerPixel; // 26-27
uint16_t bitDepth; // 28-29
};
#pragma pack()
Potentially relevant information:
- I'm using Visual Studio 2017
- I'm compiling for C++14
- I'm on a Windows 10 OS
Thanks.
Both of those line size formulas are incorrect.
For example, for w = 1
, (w / 8 + (w / 8) % 4)
results in zero. It's still zero if you multiply by two. It's expected to be 4 for width = 1.
The correct formula for line size (or bytes per line) is
((w * bpp + 31) / 32) * 4
where bpp
is bits per pixel, in this case it is 1
.
By coincidence the values are sometimes the same, for some smaller width values.
See also MSDN example:
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
Also, 1-bit image has 2 palette entries, for a total of 8 bytes. It seems you are ignoring the palette and assuming that 0 is black, and 1 is white, always.
The part where you flip the bits is correct, the other code appears to be incorrect.
Lets say we have a single byte 1000 0000
This is mean to be a single row, starting with 7 zeros and ending in 1.
Your code is a bit confusing for me (but seems okay when you fix linesize
). I wrote my own version:
void test(string filename)
{
BmpHeader head;
ifstream f(filename, ios::binary);
if(!f.good())
return;
int headsize = sizeof(BmpHeader);
f.read((char*)&head, headsize);
if(head.bitsPerPixel != 1)
{
f.close();
throw "Invalid bitmap loaded";
}
int height = head.height;
int width = head.width;
int bpp = 1;
int linesize = ((width * bpp + 31) / 32) * 4;
int filesize = linesize * height;
vector<unsigned char> data(filesize);
//read color table
uint32_t color0;
uint32_t color1;
uint32_t colortable[2];
f.seekg(54);
f.read((char*)&colortable[0], 4);
f.read((char*)&colortable[1], 4);
printf("colortable: 0x%06X 0x%06X\n", colortable[0], colortable[1]);
f.seekg(head.offset);
f.read((char*)&data[0], filesize);
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < width; x++)
{
int pos = y * linesize + x / 8;
int bit = 1 << (7 - x % 8);
int v = (data[pos] & bit) > 0;
printf("%d", v);
}
printf("\n");
}
f.close();
}
Test image:
(33 x 20 monochrome bitmap)
Output:
colortable: 0x000000 0xFFFFFF
000000000000000000000000000000000
000001111111111111111111111111110
000001111111111111111111111111110
000001111111111111111111111111110
000001111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111110010
011111111111111111111111111110010
011111111111111111111111111111110
000000000000000000000000000000000
Notice this line in above code:
int pos = y * linesize + x / 8;
int bit = 1 << (7 - x % 8);
int v = (data[pos] & bit) > 0;
printf("%d", v);
First I wrote it as
int bit = 1 << (x % 8);
But this shows the bits in the wrong order, so I had to change to 1 << (7 - x % 8)
which is basically what you did also. I don't know why it's designed like that. There must be some historical reasons for it!
(above code is for little-endian machines only)
这篇关于在C ++中读取单色位图是否需要每隔一行读取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!