Convert 1 bit BMP file to array in C / C ++
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).
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
source to share
I tried Viktor Lapiev's solution on a 20x20 test image:
But with his code, I get this output (slightly reformatted, but you can see the problem):
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:
source to share
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 !!!
source to share
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.
source to share