在C ++中读取单色位图是否需要每隔一行读取? [英] Reading a Monochrome Bitmap in C++ requires reading every other line?

查看:87
本文介绍了在C ++中读取单色位图是否需要每隔一行读取?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,这不是重复的。我已经阅读了

(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 ++中读取单色位图是否需要每隔一行读取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆