Convert 1 bit BMP file to array in C / C ++

I want to turn a 1-bit bmp file of variable height / width into a simple two-dimensional array with values ​​of 0 or 1. I have no experience editing images in code and most of the libraries I have found require more bit depth than what I need. Any help on this would be great.

+4


source to share


4 answers


Here is the code to read the monochrome .bmp file

(See dmb's answer below for a little odd-sized fix .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;
}

      

This is normal C, so don't cast pointers - be careful when using it in C ++.

The biggest problem is that the lines in the .bmp files are 4-byte aligned, which is very important for single-bit images. So we calculate the line size as "width / 8 + (width / 8)% 4". Each byte contains 8 pixels, not just one, so we use a k-based loop.



I hope the other code is obvious - a lot has been said about the .bmp header and pallet data (the 8 bytes we're missing).

Sample image

Expected Result:

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 

      

+10


source


I tried Viktor Lapiev's solution on a 20x20 test image: enter image description here

But with his code, I get this output (slightly reformatted, but you can see the problem):

enter image description here

The last 4 pixels are not readable. The problem is here. (The last partial byte in the line is ignored.)

// 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;
    }
}

      



I rewrote the inner loop as follows:

// 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;
    }
}

      

and everything's good:

enter image description here

+1


source


The following c code works with monochrome bitmaps of any size. I am assuming you have a bitmap in a buffer with heights and widths initialized from a file. So

// 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));
    }

      

Hope this help. To convert it to 2D is now a trivial affair: but if you get lost here, then this is the math for converting a 1D array to 2D. Suppose r and c are row and column and w is width:, c + r * w = r, c

If you got any further comments, hit me back, I'm out !!!

0


source


Let's consider a monochrome bitmap file a1x7 i.e. it is a 7 pixels wide straight line bitmap. To save this image in Windows OS; since 7 is not evenly divisible by 4 and an extra 3 bytes will be added to it.

Thus, the biSizeImage of the BITMAPINFOHEADER structure displays a total of 4 bytes. However, the biHeight and biWidth members correctly determine the true dimensions of bitmaps.

The above code will not work because 7/8 = 0 (rounding like all c compilers). Therefore, loop "i" will not run so "k" will.

This means that the vector "img" now contains garbage values ​​that do not correspond to the pixels contained in the "data", that is, the result is incorrect.

And by inductive reasoning, if it doesn't satisfy the base case, it probably won't do much good for general cases.

-2


source







All Articles