3

我希望将可变高度/宽度的 1 位 bmp 文件转换为一个简单的二维数组,其值为 0 或 1。我没有在代码和大多数库中编辑图像的经验我发现涉及比我需要的更高的位深度。任何有关这方面的帮助都会很棒。

4

4 回答 4

10

这是读取单色 .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”。每个字节包含 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 
于 2013-01-30T18:10:13.960 回答
1

我在 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;
    }
}

一切都很好:

在此处输入图像描述

于 2019-08-13T12:09:54.397 回答
0

以下 c 代码适用于任何大小的单色位图。我假设您已将位图放在缓冲区中,并从文件中初始化了高度和宽度。所以

// allocate mem for global buffer
if (!(img = malloc(h * w)) )
     return(0);

int i = 0, k, j, scanline;

// calc the scanline. Monochrome images are
// padded with 0 at every line end. This
// makes them divisible by 4.
scanline = ( w + (w % 8) ) >> 3;

// account for the paddings
if (scanline % 4)
    scanline += (4 - scanline % 4);

// loop and set the img values
for (i = 0, k = h - 1; i < h; i++)
    for (j = 0; j < w; j++) {
        img[j+i*w] = (buffer[(j>>3)+k*scanline])
           & (0x80 >> (j % 8));
    }

希望这可以帮助。将其转换为 2D 现在是一件微不足道的事情:但是如果你在这里迷失了将 1D 数组转换为 2D 的数学假设 r & c 是行和列,而 w 是宽度,那么:。c + r * w = r, c

如果你有进一步的言论打我,我出去!!!

于 2017-09-25T09:13:40.830 回答
-2

让我们想想 a1x7 单色位图,即这是一个 7 像素宽的直线位图。将此图像存储在 Windows 操作系统上;因为 7 不能被 4 整除,所以它会在其中填充额外的 3 个字节。

所以BITMAPINFOHEADER结构的biSizeImage一共会显示4个字节。尽管如此,biHeight 和 biWidth 成员将正确地说明真实的位图尺寸。

上面的代码将失败,因为 7 / 8 = 0(通过与所有 c 编译器一样的四舍五入)。因此循环“i”不会执行,“k”也不会执行。

这意味着向量“img”现在包含与“数据”中包含的像素不对应的垃圾值,即结果不正确。

通过归纳推理,如果它不满足基本情况,那么它很可能对一般情况没有多大好处。

于 2017-09-05T07:02:07.420 回答