Home > OS >  converting bmp to 2d matrix messing up colors
converting bmp to 2d matrix messing up colors

Time:06-07

I'm trying to create a 2d array that contains RGB value for each pixel in the image, I created a copy of the original image to check out if the images are similar and I got that the output is mostly gray pixels. (I try to use only standard libraries).

that's the structs I use

typedef struct { //bmp file values struct
int width;
int height;
} image_t;

typedef struct { //pixel color
unsigned char r;
unsigned char g;
unsigned char b;
} color_t;

the main

int main() {
int i, j;

color_t** matrix;
static image_t image;

if ((LoadSprite(&image, BMP)) != 0) {
    printf_s("Failed to load file: \" %s\"", BMP);
    return -1;
}

matrix = malloc(sizeof(color_t*) * image.height); // allocate memory to image pixel matrix
for (i = 0;i < image.height;i  ) {
    matrix[i] = malloc(sizeof(color_t) * image.width);
}

imgtrx(matrix, image, BMP);
CreateBMP(BMPCPY, matrix, image.height, image.width);


return 0;
}

the function imgtrx essentially assigns RGB pixel value to the correct location in the matrix according to the image height & width

void imgtrx(color_t** mtrx, image_t image, char* filename) {
int val, t, i = 0, j, k = 0;
FILE* file;

val = fopen_s(&file, filename, "rt");

//fseek(file, 54, SEEK_SET);

fseek(file, 10, SEEK_SET);
fread(&t, 1, 4, file);     //reads the offset and puts it in t
fseek(file, t, SEEK_SET);
int  p, e;

for ( i = 0; i < image.width; i  )
{
    for (j = 0;j < image.height;j  ) {
        fread(&mtrx[i][j].r, 8, 1, file);
        fread(&mtrx[i][j].g, 8, 1, file);
        fread(&mtrx[i][j].b, 8, 1, file);
    }
}


fclose(file);

return 0;
}

the following function converts the 2d array to single dimensions and writes a BMP copy

void CreateBMP(char* filename, color_t** matrix, int height, int width) 
{
    int i, j;
    int padding, bitmap_size;
    color_t* wrmat;

    wrmat = malloc(sizeof(color_t) * height * width);

    for (i = 0;i < height;i  ) {
        for (j = 0;j < width;j  ) {
            wrmat[i   j] = matrix[i][j];
        }
    }

    if (((width * 3) % 4) != 0) {
        padding = (width * 3)   1;
    }
    else
    {
        padding = width * 3;
    }

    bitmap_size = height * padding * 3;

    char tag[] = { 'B', 'M' };
    int header[] = {
        0x3a, 0x00, 0x36,
        0x28,                // Header Size
        width, height,       // Image dimensions in pixels
        0x180001,            // 24 bits/pixel, 1 color plane
        0,                   // BI_RGB no compression
        0,                   // Pixel data size in bytes
        0x002e23, 0x002e23,  // Print resolution
        0, 0,                // No color palette
    };
    header[0] = sizeof(tag)   sizeof(header)   bitmap_size;

    FILE* fp;
    fopen_s(&fp, filename, "w ");
    fwrite(&tag, sizeof(tag), 1, fp);
    fwrite(&header, sizeof(header), 1, fp); //write header to disk
    fwrite(wrmat, bitmap_size * sizeof(char), 1, fp);
    fclose(fp);

    fclose(fp);
    free(wrmat);
}

CodePudding user response:

Note that BMP files can have different pixel formats as detailed in the BITMAPINFOHEADER family of structures documented in the Microsoft manuals for Win32 and OS/2 .

From your "all grey" result, I suspect you are trying to interpret a less than 24bpp format as RGB values, or have run into a technical problem reading the pixel area of the file.

So to do what you are trying to do, your code needs to read the first 4 bytes of the BITMAPINFOHEADER structure, use that to determine the structure version, then read in the rest of the BITMAPINFORHEADER to determine the pixel array format and the size/format of the palette/color information structures that lie between the BITMAPINFOHEADER and the actual pixels.

Also remember to convert any header fields (including the offset field you already parse) from little endian disk format to whatever your runtime CPU uses. Please note the use of "negative height value" to indicate reversed scanline order is extremely common, as most PC and television hardware stores the top left pixel first, while positive height BMP files store the bottom left pixel first.

The BITMAPINFOHEADER structure versions to worry about are these: 2. BITMAPCOREHEADER (from the 1980s) 3. BITMAPINFOHEADER (Since Windows 3.00) 4. BITMAPV4HEADER 5. BITMAPV5HEADER (Note that the documentation has typos in the linked color profile description).

The pixel formats to worry about are:

  • 1bpp (black and white, with a mandatory palette indicating the RGB equivalent of 0 and 1 bits), as in CGA/EGA/VGA hardware, the most significant bit of each byte is the leftmost (essentially a big endian pixel format).

  • 4bpp (traditionally standard CGA colors, but the palette can specify any other RGB values for the 16 possible pixel codes). The most significant 4 bits of each byte are the leftmost pixel (essentially a big endian pixel format). This format is also used for 3bpp EGA hardware pixels and 2bpp CGA pixels, but using only 8 or 4 of the 16 values.

  • 8bpp (the mandatory palette indicates how each byte value maps to RGB values). VGA hardware included a dedicated chip (the RAMDAC) to do the RGB mapping at the full pixel speed of the monitor.

  • 16bpp (the mandatory palette is an array of 3 unit32_t bitmasks to AND with each pixel to get back only the bits storing the B, G and R values, converting those masks to appropriate shift values is left as an exercise for every graphics programmer). Each pixel is a little endian 2-byte value to be ANDed with the 3 mask values to get the Blue, Green and Red channel values. Most common bitmask values are those for 5:5:5 15bpp hardware and those for 5:6:5 hardware with 1 more green bit.

  • 24bpp (there is no palette or mask data) each pixel is 3 bytes in the order Blue, Green, Red, each giving the color values as a fraction of 255.

  • 32bpp is the same as 16bpp, only with 4 bytes in each pixel value. Common formats described by the mask are Blue,Green,Red,x and Red,Green,Blue,x where x may be 0, random noise or an alpha channel. There are also a few files that use more than 8 bits per color channel to encode HDR images. If you are writing a generic decoder, you need to at least extract the 8 most significant bits of each color channel, though keeping extra pixel bits is rarely necessary.

On top of all these color formats, the HEADER may indicate any of multiple compression formats, most notably the historic BMP RLE4 and BMP RLE8 compressions used by Windows 3.x and the use of common 3rd party compressions such as JPEG, however using BMP format as a wrapper around compressed images is quite rare these days, so you probably don't need to worry, it is however the traditional way to ask the GPU or laser printer to decompress JPEG and PNG files for you, by passing the contents of an in-memory JPEG (from a standard JFIF file) along with an in-memory BITMAPINFOHEADER structure to a GDI or GPI API after checking that other GDI/GPI APIs report that the installed driver supports this.

  • Related