Search code examples
c++qtbitmapbmpofstream

How to write 512x512 pixel array to a bmp file (256 colors and 8 bpp) in c++ using ofstream?


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.


Solution

  • 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.