在 C/C++ 中将 1 位 bmp 文件转换为数组 [关闭]

Posted

技术标签:

【中文标题】在 C/C++ 中将 1 位 bmp 文件转换为数组 [关闭]【英文标题】:Converting 1-bit bmp file to array in C/C++ [closed] 【发布时间】:2013-01-30 05:03:53 【问题描述】:

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

【问题讨论】:

给你,伙计:en.wikipedia.org/wiki/BMP_file_format 您到底想解决什么问题?您的程序是否关心加载任何随机单色 BMP 文件并对其进行处理?还是程序只需要处理一系列静态单声道 BMP 资产?还是别的什么? 我一次只需要处理一个位图图像。现在我在我的程序中明确定义了一个 16x16 的 1 位数组,但需要一种方法来自动填充 1 位数组以获取更大的数据。 【参考方案1】:

这是读取单色 .bmp 文件的代码

(请参阅下面的 dmb's answer 以了解对奇数大小的 .bmps 的小修复)

#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 

【讨论】:

这有一个错误。如果您制作一个大小不是 8 倍数的小 bmp(例如 20x20),则字节的最后一部分不会从文件数据复制到数组中。这是因为 for(i = 0 ; i (错误修正贴在下面)【参考方案2】:

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

一切都很好:

【讨论】:

感谢您的修复,我将链接到您的答案【参考方案3】:

以下 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

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

【讨论】:

【参考方案4】:

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

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

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

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

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

【讨论】:

这很难理解——因为你把所有的东西都放在了一个没完没了的段落里。提示:你想帮助读者阅读......所以形式很重要!

以上是关于在 C/C++ 中将 1 位 bmp 文件转换为数组 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在C#中将int数组转换为BMP

在 ASP.net 中将 BMP 转换为 PDF 的最简单方法是啥

bmp文件格式中rgb555与rgb888之间的转换,24位与16位位图的转换

在java中将bmp转换为jpg

图片转换,PNG转32位BMP;BMP大小转换

iOS:将 png 图像转换为 1 位 bmp 图像