Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading an 8bpp grayscale BMP in C

Tags:

c

windows

image

bmp

I can't make sense of the BMP format, I know its supposed to be simple, but somehow I'm missing something. I thought it was 2 headers followed by the actual bytes defining the image, but the numbers do not add up.

For instance, I'm simply trying to load this BMP file into memory (640x480 8bpp grayscale) and just write it back to a different file. From what I understand, there are two different headers BITMAPFILEHEADER and BITMAPINFOHEADER. The BITMAPFILEHEADER is 14 bytes, and the BITMAPINFOHEADER is 40 bytes (this one depends on the BMP, how can I tell that's another story). Anyhow, the BITMAPFILEHEADER, through its parameter bfOffBits says that the bitmap bits start at offset 1078. This means that there are 1024 ( 1078 - (40+14) ) other bytes, carrying more information. What are those bytes, and how do I read them, this is the problem. Or is there a more correct way to load a BMP and write it to disk ?

For reference here is the code I used ( I'm doing all of this under windows btw.)

#include <windows.h>
#include <iostream>
#include <stdio.h>


HANDLE hfile;
DWORD written;

BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;

int main()
    hfile = CreateFile("image.bmp",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
ReadFile(hfile,&bfh,sizeof(bfh),&written,NULL);

ReadFile(hfile,&bih,sizeof(bih),&written,NULL);

int imagesize = bih.biWidth * bih.biHeight;

image = (unsigned char*) malloc(imagesize);

ReadFile(hfile,image,imagesize*sizeof(char),&written,NULL);

CloseHandle(hfile);

I'm then doing the exact opposite to write to a file,

hfile = CreateFile("imageout.bmp",GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

WriteFile(hfile,&bfh,sizeof(bfh),&written,NULL);
WriteFile(hfile,&bih,sizeof(bih),&written,NULL);
WriteFile(hfile,image,imagesize*sizeof(char),&written,NULL);

CloseHandle(hfile);

Edit --- Solved

Ok so I finally got it right, it wasn't really complicated after all. As Viktor pointed out, these 1024 bytes represent the color palette.

I added the following to my code:

RGBQUAD palette[256];
// [...] previous declarations [...] int main() [...] then read two headers
ReadFile(hfile,palette,sizeof(palette),&written,NULL);

And then when I write back I added the following,

WriteFile(hfile,palette,sizeof(palette),&written,NULL);
like image 314
RegisteredUser Avatar asked Jan 17 '23 13:01

RegisteredUser


2 Answers

"What are those bytes, and how do I read them, this is the problem."

Those bytes are Palette (or ColorTable in .BMP format terms), as Retired Ninja mentioned in the comment. Basically, it is a table which specifies what color to use for each 8bpp value encountered in the bitmap data.

For greyscale the palette is trivial (I'm not talking about color models and RGB -> greyscale conversion):

for(int i = 0 ; i < 256 ; i++)
{
    Palette[i].R = i;
    Palette[i].G = i;
    Palette[i].B = i;
}

However, there's some padding in the ColorTable's entries, so it takes 4 * 256 bytes and not 256 * 3 needed by you. The fourth component in the ColorTable's entry (RGBQUAD Struct) is not the "alpha channel", it is just something "reserved". See the MSDN on RGBQUAD (MSDN, RGBQUAD).

The detailed format description can be found on the wikipedia page:Wiki, bmp format

There's also this linked question on SO with RGBQUAD structure: Writing BMP image in pure c/c++ without other libraries

like image 74
Viktor Latypov Avatar answered Jan 24 '23 16:01

Viktor Latypov


As Viktor says in his answer, those bits are the pallete. As for how should you read them, take a look at this header-only bitmap class. In particular look at references to ColorTable for how it treats the pallette bit depending on the type of BMP is it was given.

like image 32
thehouse Avatar answered Jan 24 '23 16:01

thehouse