Search code examples
c++bitmapgrayscalebitmapimage

BGR grayscale conversion of image giving corrupted image


I am trying to conver BGR bitmap image to grayscale using the code,

int main()
{
    FILE* fIn;
    FILE* fOut;
    if (fopen_s(&fIn, "D:\\VBox\\Shared\\images\\sample01.bmp", "rb") != 0) {
        return 1;
    }

    if (fopen_s(&fOut, "D:\\VBox\\Shared\\images\\sample01_gs1.bmp", "wb") != 0) {
        return 1;
    }

    unsigned char info[54];
    fread(info, sizeof(unsigned char), 54, fIn); // read the 54-byte header


    int32_t width, height;
    uint16_t bitperpixl;
    memcpy(&width, info + 18, sizeof(int32_t));
    memcpy(&height, info + 22, sizeof(int32_t));
    memcpy(&bitperpixl, info + 28, sizeof(uint16_t));

    int padding = ((width * bitperpixl) / 8) % 4; // which Is 0 for the image which I am using. 

    fwrite(info, sizeof(unsigned char), 54, fOut);

    unsigned char pixel[3]; //bgr
    for (int y = 0; y < height; ++y)
    {
        for (int x = 0; x < width; ++x)
        {
            fread(pixel, 3, 1, fIn);
            unsigned char gray = pixel[2] * 0.3 + pixel[1] * 0.58 + pixel[0] * 0.11;
            memset(pixel, gray, sizeof(pixel));
            fwrite(&pixel, 3, 1, fOut);
        }
        fread(pixel, padding, 1, fIn);
        fwrite(pixel, padding, 1, fOut);
    }
    fclose(fOut);
    fclose(fIn);
}

The file created is same size of the input image. When I try open the file using windows image viewer or pain it shows that the image is not supported or corrupted.


Solution

  • There are several significant issues with your code. First, you should not assume the bitmap image data always starts at byte 54. The File structure section of Wikipedia's "BMP file format" article starts with

    The bitmap image file consists of fixed-size structures (headers) as well as variable-sized structures appearing in a predetermined sequence. Many different versions of some of these structures can appear in the file, due to the long evolution of this file format.

    There's then a diagram showing the optional or semi-optional sections which occur before the actual pixel array. Instead, as shown in the Bitmap file header section, the pixel data start position is 4 bytes at offset 10, so you should have a couple of lines like

    uint32_t pixelData;
    memcpy(&pixelData, info + 10, sizeof(pixelData));
    

    Of course, you then start reading from that position when dealing with the pixels.

    Also, regarding dealing with the pixel data itself, note you can't assume they are all in 4 byte chunks, with the first 3 bytes of each chunk being a RGB value and then a padding byte. Instead, there may be a color table being used (e.g., where the values are instead the offsets into that table). Also, if the Windows BITMAPINFOHEADER is being used, then at offset 30 there's a 4 byte value indicating the compression method being used. The Wikipedia article describes what the various bits indicate and, thus, how your code will need to interpret the pixel values.

    If you can be certain the input .bmp file is one of a certain set of limited formats, you can write code to deal with just those types of files. Otherwise, you should handle all of these various additional details. Note that although the BMP file format is actually basically the simplest image formats, there are still quite a few things you need to check on and deal with to handle all of the cases.


    There's the more general question of what the purpose is of writing your own code, from scratch, to read a BMP file and convert the image data to grayscale. If it's something like for your own personal education, then just using the information I give here, as well as the other details in the Wikipedia article, is sufficient for you to get a working program (although it will take quite a bit time, and code, to properly handle all of the various options).

    Otherwise, due to how long the BMP file format has been around, as well as how relatively common it is, there are multiple free, and many commercial, programs that allow you to read BMP files and make various adjustments to their contents, such as what you're trying to do.

    Alternatively, there are various open source code image libraries you can at least check on to get some ideas. For example, 10 Best Open Source Image Processing Libraries provides a good list. Note that ImageMagick is AFAIK one of the oldest and most popular ones, although other good ones to check on include VIPS and OpenCV.

    In addition, there are freely available classes, libraries, etc., that can at least reduce the amount of work you need to do. A few suggestions for BMP files are provided in C++: What's the simplest way to read and write BMP files using C++ on Windows?, and for multiple image types in C++ Image Processing Libraries.