Loading an 8bpp grayscale BMP in C
Asked Answered
T

2

5

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);
Topnotch answered 29/5, 2012 at 3:6 Comment(2)
What is biBitCount set to when you read it from the image? If as you said it is an 8-bit image it should have a palette that you need to deal with.Cobaltite
Why did you tag C question with C++ tag?Tortilla
S
7

"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

Stieglitz answered 29/5, 2012 at 8:30 Comment(4)
there's some padding in the ColorTable's entries - hmm, are you sure about that? Maybe it's the alpha channel.Vandenberg
I think that 'trivial' greyscsale palette is actually suboptimal. I cannot find a reference confirming this right now, but I believe there's a better way which takes the perceived brightness into account (a green 100 pixel is perceived to be brighter than a blue 100 pixel).Vandenberg
Something like 0.12 (R), 0.59(G), 0.29(B) would be OK for the CRT display, but has to be adjusted for the LCD display. You're right, but I just didn't talk much about color models - it is up to the original poster, he has problems with the file format.Stieglitz
See the MSDN/RGBQUAD docs - it is not alpha field in the RGBQUAD, it is just something reservedStieglitz
H
2

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.

Heist answered 29/5, 2012 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.