BMP Research

Specifics of the .BMP File Format

In general, a BMP file consists of a header, an optional color lookup table area, and a pixel data area. Header data values are LSB first.Here is how the BMP header is usually organized.

Decimal
Hexadecimal
Description
00-01
00-01
Ascii 2 byte "BM" bitmap identifier
02-05
02-05
Total length of bitmap file in bytes - Four byte integer, LSB first
06-09
06-09
Reserved, possibly for image id or revision. Four byte integer, LSB first
10-13
0A-0D
Offset to start of actual pixel data. Four byte integer, LSB first
14-17
0E-11
Size of data header, usually 40 bytes. Four byte integer, LSB first
18-21
12-15
Width of bitmap in pixels. Four byte integer, LSB first
22-25
16-19
Height of bitmap in pixels. Four byte integer, LSB first
26-27
1A-1B
Number of color planes. Usually 01 . Two byte integer, LSB first
28-29
1C-1D
Number of bits per pixel. Sets color mode. Two byte integer, LSB first.1- Monochrome4 - 16 lookup colors8 - 256 lookup colors16 - 65536 lookup colors24 - 16,777,216 RGB col32 - 16,777,216 RGC col
30-33
1E-21
Non-lossy compression mode in use. Four byte integer, LSB first.0 - None1 - 8 Bit run length encoded1 - 4 bit run length encoded
34-37
22-25
Size of stored pixel data. Four byte integer, LSB first
38-41
26-29
Width resolution in pixels per meter. Four byte integer, LSB first
42-45
2A-2D
Height resolution in pixels per meter. Four byte integer, LSB first
46-49
46-49
Number of colors actually used. Four byte integer, LSB first
50-53
50-53
Number of important colors. Four byte integer, LSB first


Color lookup tables follow the header for 8 bit and 16 bit images. These in turn are followed by the actual pixel data in appropriate format.

In the 24-bit uncompressed RGB color mode, there are no color lookup tables used. Each pixel consists of an 8-bit blue byte, a green byte, and a red byte in that order. Working from left to right upwards line-by-line starting at the lower left. The pixel data starts at the data offset and continues to the end of the .BMP file.

Understanding Line Padding

There is a crucial line padding detail that must be attended to when dealing with actual .BMP bitmap data. The rule is simple : Each new .BMP line must start on an even 32 bit boundary! Because three does not divide into four very well, zero, one, two, or three "00" padding bytes must be added to the end of each .BMP data line in the 24-bit uncompressed format. The exact number of padding bits is set by the number of horizontal pixels per line.

Programming Details

The first thing you'll find stored in a bitmap file is what's called the File Header structure:

Private Type BITMAPFILEHEADER
bfType As Integer
bfSize As Long
bfReserved1 As Integer
bfReserved2 As Integer
bfOffBits As Long
End Type

This UDT (User-Defined Type) will be used to extract the first 14 bytes of information from the BMP file (an Integer takes up two bytes, a Long takes up four). bfType will always return "19778" which corresponds to the two character string, "BM" for bitmap. All bitmaps start with these two characters. bfSize will describe the entire file's size in bytes, bfReserved1 and bfReserved2 are reserved spaces and should simply be set to zero. bfOffBits tells us the byte offset from the beginning of the file at which the bitmap data starts (yes, I know it's called bfOffBITS, but it actually describes the number of bytes). So if bfOffBits = 1078 (as it should for most 8bit bitmaps 54+256*4) then we know that the header and colour table will be complete by the 1078th byte, and the picture data has begun.

Now for the Info Header structure:

Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type

biSize is the size of the BITMAPINFOHEADER structure given in bytes (usually equals 40). biWidth and biHeight describe the width and height of the bitmap in pixels, as you would expect. biPlanes describes the number of planes contained in the bitmap, this is not normally used, and is set to one.

Now, biBitCount is an important one. It describes the "bit-depth" of this bitmap. It can have any of four values: 1, 4, 8, and 24. A bit depth of one indicates that the bitmap will have only two colours (monochrome), a bit depth of 4 will allow 16 colours, 8bit equals 256 colours, and 24bit is 16.8 million colours. Now the bit depth dictates whether or not a bitmap will use a colour table (discussed later). 24bit bitmaps DO NOT use a colour table, while the other bit formats do (8 bit and 16 bit).

biCompression indicates whether or not RLE (Run Length Encoding) is used to compress the bitmap data.Simply set this to zero for no compression. biSizeImage contains the length of the bitmap image data (the actual pixels) in bytes. You might expect this to simply be equal to the width multiplied by the height, but it isn't always.

biXPelsPerMeter and biYPelsPerMeter describe the resolution of the bitmap in pixels per meter. To go with standard resolution, just set these to zero. biClrUsed indicates how many of the colour table colours are actually used in the bitmap, set to zero to use all colours. biClrImportant tells the program which colours are most important, this can increase the display speed under some circumstances. Simply set this to zero for standard operation.

Now, that I have described the bitmap, all that's left is to list the colours to be used (if it's not 24bit) and then store the actual per-pixel data. First, lets look at the structure used for the colour table (or palette):

Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type

Each of these rgb values are a number from 0-255 indicating the intensity of that particular channel. The rgbReserved byte must be set to zero. The colour table is comprised of a number of these RGBQUAD structures, the exact number of which depends on the bit depth of the bitmap. A 1bit bitmap will have two RGBQUAD's describing its colour table (since a 1bit bitmap can have only two colours). A 4bit bitmap will have 16 RGBQUAD's, and an 8bit bitmap will have 256. These values will be referred to by the per-pixel data stored later in the BMP file. For example, to display the colour white in a pixel, we would have to set up an RGBQUAD structure where each of the values (rgbBlue, rgbGreen, and rgbWhite) were equal to 255 and then refer to this structure by its order in the colour table. So if we set this as the first colour (that's zero in an array) in the table then any pixel referring to the zeroth colour will show up white.

24bit bitmaps, on the other hand, have no colour table because each pixel is made up of 3bytes, each describing the intensity of a specific colour channel (red, green, or blue). There is no need to look up a colour in the table since 16.8million can be described by each 3byte triplet. An 8bit bitmap, on the other hand, would only be able to display 256 colours without a colour table, since you can only store 256 discrete values within an 8bit span. Similarly, for 4bit bitmaps, only 16 combinations can be stored in the 4bit span. Therefore, having a colour look-up table at the start of the bitmap enables these lower bit formats to display a variety of colours, not simply a fixed set of 256 or 16.

Finally the bitmap data itself can be stored in a simple array of bytes:

Dim BMPData() As Byte

You'll have to "redim" this array to the size of the biSizeImage member of the BITMAPINFOHEADER structure. As noted before, this value is not always simply the multiplication of the bitmap's height and width. This is due to 32bit boundary padding. You see, computers these days like things to be presented to them in 32bit chunks, so in order to ensure optimal performance, bitmaps are always encoded so that their "scan line boundaries" end on 32bit edges. That is to say, if your 8bit bitmap is supposed to be 3 pixels wide (3 pixels at 8bits per pixel = 24bits) then you'd be 8bits shy of the 32bit boundary. Eight zero padded bits would then have to be added to make up the difference. So, if your 8bit bitmap is 3 pixels wide and 3 pixels tall, you'd normally expect the pixel data to be stored in 72bytes (3pixels * 3pixels * 8bits per pixel) but as a result of the padding, each 3 pixel row (scan line) will end up being represented by 32bits rather than 24bits. That's 8 extra bits per row, for 3 rows. Our final total would then come to 96bits (3 rows * 32 bits per row). This works just the same for the scan lines at any bit depth, they all have to end on 32bit boundaries. If a 1bit bitmap were 30 pixels wide, then there'd have to be 2bits of padding (30 pixels * 1bit per pixel = 30bits, 2 bits short).

The only other thing you need to know about bitmaps is that the pixel data is stored from the bottom left hand corner and progressing upward with scan lines from left to right. So the whole bottom row is stored first, then the second from the bottom, etc, until the final pixel from the top right-corner of the bitmap is stored at last. Bear this in mind when you store your data or read data from a bitmap, otherwise your picture will come out upside-down.

To open a bitmap and modify or extract the data, simply access it in binary mode:

Open "SAMPLE.BMP" For Binary Access Read Write Lock Write As #1

You can then use "get" and "put" statements to extract or place the data. For example, to extract the BITMAPFILEHEADER and BITMAPINFOHEADER you can do this:

Dim BMPFileHeader As BITMAPFILEHEADER
Dim BMPInfoHeader As BITMAPINFOHEADER
Get #1, 1, BMPFileHeader
Get #1, , BMPInfoHeader

The BMPFileHeader variable should extract the data from the very start of the file, so we pass "1" as the second argument for the "get" statement. The BMPInfoHeader variable data commences immediately following the BITMAPFILEHEADER data, and so we omit the second argument, indicating that we'd like to continue extracting data where we left off. You can then "get" the RGBQUAD data, according to the number of entries in the colour table, and finally extract the bitmap pixel data itself, by "getting" an appropriately sized array of bytes.

8-bit Grey Scale Info

If you are using an 8-bit image as the cover image, many steganography experts recommend using images featuring 256 shades of grey as the palette instead of 256 colors. Grey scale images are preferred because the shades change very gradually between palette entries. This increases the images ability to hide information.

When using LSB techniques on 8-bit images, more care needs to be taken, as 8 bit formats are not as forgiving to data changes as 24-bit formats are. Care needs to be taken in the selection of the cover image, so that changes to the data will not be visible in the stego-image. When modifying the LSB bits in 8-bit images, the pointers to entries in the palette are changed. It is important to remember that a change of even one bit could mean the difference between a shade of red and a shade of blue. Such a change would be immediately noticeable on the displayed image and is thus unacceptable. For this reason, data hiding experts recommend using grey scale palettes where the differences between shades is not as pronounced.

Gray Scaling Images:

In grayscale, images are represented as pixels, as in color images, except each pixel can assume only a shade of gray. But what is "gray"? Technically, gray is any color created by mixing equal amounts of red, green, and blue light. Zero red, green and blue yields black. Full intensity red, green and blue yields bright white. Varying intensities in between create lighter or darker grays.

Reducing images to a gray scale is a common image-processing task, which is also very simple to implement. The simplest method to do this is simply to take the green value and apply the same value to Red and Blue pixels. This works well because the eye responds to Green light in RGB colors much more strongly than to the Red and Blue values. Obviously completely saturated Reds and Blues will not be represented this way. A more accurate conversion can be performed using weighted formulas of this type: (BrightnessOfRed * Red + BrightnessOfGreen * Green + BrightnessOfBlue * Blue) / (BrightnessOfRed + BrightnessOfGreen + BrightnessOfBlue). Here are some of the most commonly seen setups:

(a) Gray = .30*Red + .11*Blue+.59*Green
(b) Gray = (77 * Red + 150 * Green + 28 * Blue) / 255
(c) Gray = (222 * Red + 707 * Green + 71 * Blue) / 1000

Note that the three formulas lead to different results, however none is better or more correct than the other! This is because what these formulas attempt to express is the eye's/the mind's responsiveness to colors.

Notice that green has the highest coefficient. This is because nearly 60% of the colors that you see depend on green. Of course no one know s exactly why we see green so well, but one theory works something like this: Most animals lack color vision, and see only in grayscale. Millions of years ago, however, our ancestors lived in trees as pre-primate creatures. Living in the green leafy environment of a jungle means having to detect predators who attempt to mimic the colors of the jungle, like the green of a tree living python or the shadow like stripes of a tiger. There are two ways to detect predators: by their movement and by color sense. Most animals detect other animals by motion. Our ancestors added a remarkable ability to see extremely fine differences in color, like a few wavelengths difference in color between a green snake and the leaves that it hid in. Since a lot of their world was leaves, green played a big role in their color sense, as it does today to our color sense. Notice also that your are most insensitive to blue; that's one reason why many people find blue to be a calming color. Looking at blues doesn't excite the optic nerve as much, which means that the ancient parts of our brains that are still looking out for predators can rest for a while.



Back