2

Let me start with an intro. I've been reading about bitmap file format (wiki, msdn, etc) and researching how to read and write bmp files in c++. I'm writing a program in c++, without the use of bmp libraries, that can extract data from a bmp and then create a new bmp using that data. The purpose of this is to see if the new image file is the same as the original. Then if it works I can move on to manipulating the extracted data in order to perform histogram equalization.

Currently, my program is able to successfully retrieve the Bitmap file header and Bitmap information header from the original bmp file then write it to a new bmp file. It then does the same thing with the Color Table. The problem occurs, or at least this is what I currently believe, with the Pixel data. It looks like it is being read correctly and even looks like it is being written correctly at first glance. When I open the new file in a hex editor and compare it to the original it can be seen that the values begin to differ at offset (h) 630. Also, the new image when opened doesn't look like the original.

Here is the updated structure:

#pragma pack(2)                    // Using pragma to force structure format
struct BMPFH                       // Bitmap file header
{
  char HeaderField[2];             // Used to identify the BMP and DIB file is 0x42 0x4D in hexadecimal, same as BM in ASCII
  unsigned int Size_of_BMP;        // size of the BMP file in bytes
  unsigned short Reserved1;        // Reserved; actual value depends on the application that creates the image
  unsigned short Reserved2;        // "                                                                       "
  unsigned int StartAddress;       // offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found
};

#pragma pack()
struct DIBH                        // Bitmap information header
{
  unsigned int Size_of_Header;     // Size of this header (40 bytes)
  signed int Width;                // bitmap width in pixels (signed integer)
  signed int Height;               // bitmap height in pixels (signed integer)
  unsigned short Num_of_Planes;    // number of color planes (must be 1)
  unsigned short Num_of_Bits;      // number of bits per pixel, which is the color depth (1, 4, 8, 16, 24, 32)
  unsigned int CompMethod;         // compression method being used (0, 1, 2, 3)
  unsigned int Size_of_Raw;        // size of the raw bitmap data
  signed int HRes;                 // horizontal resolution of the image. (pixel per meter, signed integer)
  signed int VRes;                 // vertical resolution of the image. (pixel per meter, signed integer)
  unsigned int Num_of_Col;         // number of colors in the color palette, or 0 to default to 2^n
  unsigned int Num_of_ICol;        // number of important colors used, or 0 when every color is important; generally ignored
};

struct ColorTable
{
    unsigned char data[1024];
};

struct Pixel
{
    unsigned char pix[262144];      
};

This is the updated relevant code to the question:

//write pixel data to new file
    unsigned char p;
    for (int j = 0; j < H; j++)
    {
        for (int i = 0; i < W; i++)
        {
            p = opx.pix[j*W + i];
            outFile.write(reinterpret_cast<char*>(&p), sizeof(p));
        }
    }

This is what outputs to the screen:

 Bitmap File Header

 Header Field: BM
 Size of BMP: 263222
 Start Address: 1078

 Bitmap Information Header

 Header size: 40
 Image width: 512
 Image height: 512
 Number of bits for pixel: 8
 Used compression: 0
 Image size: 0
 Horizontal resolution: 2835
 Vertical resolution: 2835
 Number of colors in the color palette: 256
 Number of important colors used: 256
---------------------------------------------------

 Total number of bytes to store one row of pixels: 512

 Total amount of bytes to store the array of pixels: 262144

 The first three entries in color table: 0 0 0

 The first three pixels (Blue, Green, Red): 98 96 91

The hex editor I'm using is HxD. The compiler I'm using is Qt Creator.

And this is the bmp image I'm using: https://drive.google.com/file/d/0B4emsCaxwnh5c3IxNWdsc1k2MGs/view?usp=sharing

Thank you to anyone who spent their valuable time looking over this wall of text. I'd appreciate feedback and definitely let me know if I missed something obvious.

Community
  • 1
  • 1
  • First of all you vialotes strict aliasing rule here `BMPFH* fh = (BMPFH*)(temp);`. Simply read structure entire structure instead of byte array. – Mykola Jan 20 '16 at 23:23
  • Could it be that you are reading `DIBH` structure directly, but writing it one element at a time, AND the structure members are not aligned to single byte (`#pragma pack`)? – Dialecticus Jan 20 '16 at 23:24
  • There are also different versions of BMP file. – Mykola Jan 20 '16 at 23:26
  • PPM format https://en.wikipedia.org/wiki/Netpbm_format#PPM_example is a little easier to read/write as a first attempt. bmp contains a few oddities that you don't want to worry about. – Martin Beckett Jan 20 '16 at 23:40
  • `inFile.seekg(57, ios::beg);` seems suspicious because of the odd offset. Just about everything in .BMP is aligned to at least an even offset and typically to a multiple of 4. – Adrian McCarthy Jan 21 '16 at 00:00
  • Could you upload the raw bmp file you are using? The link points to a png. – Danny_ds Jan 21 '16 at 00:03
  • The header size is 54 bytes, so the 1024 byte color table should begin at offset 54 (not 57). You are strangely writing 3 bytes of 0 (as "offset") between the header and color table. When you write the pixel data, as repeated copies of the first line of the source, the Pixel index is offset by another strange 3 bytes -- this causes 3 bytes to be read beyond the end of the source array. – Christopher Oicles Jan 21 '16 at 00:34
  • After further reflection, it seems to me like you are trying to patch the first color in the color table with black. The first problem with this is that color table entries are 4 bytes long, not 3 (it is 1024 bytes long because 256*4==1024). The last byte is zero padding. I recommend that you first read the color table normally (1024 bytes at offset 54), and then set the first 3 elements of the array to 0 instead of messing with offsets later on in the code. Then you can skip writing the "offset" value and also remove the offending i+3 index when writing out lines of pixel data. – Christopher Oicles Jan 21 '16 at 00:49
  • Also, your "intensity" calculation takes the first 3 pixels and seems to be using an RGB weighting, as if the first pixel is red, the second green and the third blue. However, your image is monochrome, so such weighting is inappropriate -- all pixels are gray. This would be correct when processing a bitmap with 24 bits per pixel (for a single pixel, one byte each for red, green, blue) -- but this is an 8bpp bitmap and each pixel's value is an offest into the color table, which holds RGB levels. – Christopher Oicles Jan 21 '16 at 01:05
  • @Danny_ds I've updated the image link to ensure that it keeps the original bmp formatting. – James Williams Jan 21 '16 at 18:54
  • Thank you everyone for the advice. @ChristopherOicles , you correctly figured out what I was doing. I took your advice and fixed the code (updated original post as well) however I'm still suffering from the same problem as before. At a seemingly random point the new pixel data begins to differ from the original pixel data. Row 630 (when viewing in hex editor) shows "7A 7A 77 78 79 76 61 5D 5B 5B 5A 5B 5B 5B 5B 5B" for the original and "7A 7A 77 78 79 76 62 60 5B 5B 5B 5B 5E 60 5C 5B" for the new. Everything before that row is exactly correct. – James Williams Jan 21 '16 at 18:59

1 Answers1

2

Your final nested loops (the output loop) are writing the same row of pixel data over and over.

//write pixel data to new file
unsigned char p;
for (int j = 0; j < H; j++)
    {
        for (int i = 0; i < W; i++)
        {
            p = opx.pix[i];
            outFile.write(reinterpret_cast<char*>(&p), sizeof(p));
        }
    }

The i in this line:

            p = opx.pix[i];

is the column offset. It starts over for each row.

To fix it, you can change it to:

            p = opx.pix[j*W + i];

There are more efficient ways to do this, but this will get your code working.

The 630 in your hex editor is the offset (in hex) from the beginning of your file, and your problem appears to start six bytes after that. Note that 636h would be the first bytes of the second row of pixel data. (File header is 14 bytes, DIB header is 40 bytes, color table is 1024 bytes, first row is 512 bytes.) This was the clue as to where to look for the problem.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • Thank you very much for your insight. I made that change and at first it caused the program to freeze but that was because of "unsigned char pix[512]". I changed that to 262144 and the program output an exact match of the original bmp file. I chose 262144 because that's the number of bytes needed to store the array of pixels. Is that the correct number to put there? (Referring to the struct of Pixel in my code) – James Williams Jan 21 '16 at 23:22
  • Yes, that's the right size for this .BMP. Be aware there are likely other problems if you try to use this on files of a different format. There are a lot of legal variations in the .BMP format. – Adrian McCarthy Jan 21 '16 at 23:58