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

时间:2013-01-30 05:03:53

标签: c++ c bmp

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

4 个答案:

答案 0 :(得分:10)

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

#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字节对齐的,这对于单比特图像很重要。因此我们将线尺寸计算为“width / 8 +(width / 8)%4”。每个字节包含8个像素,而不是一个,因此我们使用基于k的循环。

我希望其他代码是显而易见的 - 有很多关于.bmp标题和pallete数据(我们跳过的8个字节)。

Sample image

预期产出:

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 

答案 1 :(得分:1)

我在20x20的测试图像上尝试了Viktor Lapyov的解决方案: enter image description here

但是用他的代码,我得到了这个输出(稍微重新格式化,但是您可以看到问题):

enter image description here

不读取最后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;
    }
}

一切都很好:

enter image description here

答案 2 :(得分: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&amp; c是行和列,w是宽度: 。 c + r * w = r,c

如果你有进一步的评论打击我,我出去了!!!

答案 3 :(得分:-2)

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

因此BITMAPINFOHEADER结构的biSizeImage将显示总共4个字节。尽管如此,biHeight和biWidth成员将正确地指出真正的位图尺寸。

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

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

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