pointer file randomly changes value in middle of reading raw bitmap data

2

I am currently working on loading a bitmap file in C. I am kind of new to C, and I ran into a problem: I have a file pointer that reads unsigned chars to a struct of rgb pixels, (it's called rgb but it reads in the order of b,g,r and padding - this is the default of bitmap format file). My file is 12x12 pixels and when it reaches to row 9 it putting only the value '204' in each component, whike the image is white (i.e. all components = 255). All the components before this equal 255. EDIT: I changed the enum to a three defined values returned by the image state (didn't load, blue, not blue). EDIT2: I edited the code, but now im equals 0xffffffff and cannot be accessed to. here's the code:

int CalaculateBlueness()
 {

bih bih;
bfh bfh;
int counterblue = 0;
hsv hsv;
FILE *filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");


//if the file doesn't exist in memory
if (filePtr == NULL)
{
    return IMAGE_NOT_LOADED;
}

//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);

//verify that this is a bmp file by check bitmap id
if (bfh.bitmap_type[0] != 'B' || bfh.bitmap_type[1] != 'M')
{
    fclose(filePtr);
    return IMAGE_NOT_LOADED;
}

fclose(filePtr);

//ensure that the filePtr will point at the start of the image info header
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");
fseek(filePtr, 14, SEEK_CUR);

//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);

if (bih.bit_count!=24)
{
    return ERROR_BPP;
}

//point the pointer file to the start of the raw data
//fseek(filePtr, bfh.file_size, SEEK_SET);

int size = bih.height * WIDTHBYTES(bih.width * 32);

unsigned char *im = calloc(1, size);
//put the raw bitmap pixel data in strcut rgb array 


fread(&im, sizeof(size), 1, filePtr);

//convert each pixel to it's hue value and check if in the range of blue
for (size_t i = 0; i < bih.height; i++)
{
    for (size_t j = 0; j < bih.width; j++)
    {
        hsv =rgbpixel_hue(im);
        if (hsv.h>190 && hsv.h<250)
        {
            counterblue++;
        }
        fseek(im, 3, SEEK_CUR);
    }

}

//check if more than 80% of the image is blue and return the defined state according to the result
if (counterblue > BLUE_THRESHOLD*(bih.height*bih.width))
{
    return  BLUE;
}
return  NOT_BLUE;

}

c
file
bitmap
asked on Stack Overflow Dec 12, 2017 by Meowstar • edited Dec 12, 2017 by Meowstar

2 Answers

2

Reading in bitmaps is always a difficult thing.

There are quite some points to consider.

fread(&im[i][j].res, sizeof(unsigned char), 1, filePtr);

With this line your read the reserved byte of an RGBQUAD from the file. However, this member is not in the file. The image data in the file contains scanlines. See below.

After reading the Bitmap File Header, you open the file again. However, you didn't close it. This might be succesful or it fails because the file was already open. But you don't chek the return value of fopen. Anyway, there is no need to do this because after having read the BFH the filepointer is positioned at the BITMAPINFOHEADER; you can just read it in. And you need to read it in, otherwise you won't know the dimensions of the bitmap.

From MSDN documentation:

The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER [...] structure. An array of RGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).

For 24 bits-per-pixel bitmaps, there is no color table.

so the sequence is now:

    //read the bitmap file header
    fread(&bfh, sizeof(bfh), 1, filePtr);

    //read the bitmap info header
    fread(&bih, sizeof(bih), 1, filePtr);

    int bpp= bih.biBitCount;
    if (bpp != 24) return 0;  // error: must be 24 bpp/ 3 bytes per pixel

Now we must calculate the amount of memory needed to store the image. An image consists of heighth rows of width pixels. The rows are called scanlines.

For some reason, a scanline is alligned on a 4 byte boundary. This means that the last bytes of a scanline may not be in use. So the amount of memory to read the bitmap data from the file is the heigth of the image, times the number of scanlines of the image:

// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)

    int size= bih.biHeight * WIDTHBYTES(bih.biWidth*32));
    unsigned char *im= malloc(size);

Finally you can read the data. If there was a color table, you should have skipped that with a seek but since there is none, you can now read the data:

    fread(im, size, 1, filePtr);

Now, to address the pixels you cannot use a simple two-dimensional notation governed by width and heigth of the image...due to the scanlines. You can use the following:

    int scanlineSize= WIDTHBYTES(bih.biWidth*bih.biBitCount);
    unsigned char *p, *scanline= im;

    for (int i=0; i<bih.biHeight; i++)
    {
        p= scanline;
        for (int j=0; j<bih.biWidth; j++)
        {
            g= *p++;
            b= *p++;
            r= *p++;
        }
        scanline += scanlineSize;
    }

So to address a 3-byte pixel (x,y) use: im[y*scanlineSize + x*3]

.. except that I believe that scanlines are reversed, so the pixel would be at im[(bih.biHeight-y)*scanlinesize + x*3].


UPDATE: complete function

#include <winGDI.h>
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)

unsigned char *readBitmap(char *szFilename)
{
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;
    int i, j, size, scanlineSize;
    unsigned char r, g, b, *p, *img, *scanline;
    FILE *filePtr;

    if ((filePtr=fopen(szFilename, "rb"))==0) return 0;

    //read the bitmap file header
    if (fread(&bfh, sizeof(bfh), 1, filePtr)!=1
    ||  bfh.bfType != 'MB') {fclose(filePtr); return 0;}

    //read the bitmap info header
    if (fread(&bih, sizeof(bih), 1, filePtr)!=1
    ||  bih.biSize!=sizeof(bih)) {fclose(filePtr); return 0;}

    if (bih.biBitCount != 24) {fclose(filePtr); return 0;}  // error: must be 24 bpp/ 3 bytes per pixel

    // allocate memory and read the image
    scanlineSize= WIDTHBYTES(bih.biWidth * bih.biBitCount);
    size= bih.biHeight * scanlineSize;
    if ((img= malloc(size))==0) {fclose(filePtr); return 0;}
    if (fread(img, size, 1, filePtr)!=1) {free (img); fclose(filePtr); return 0;}
    fclose(filePtr);

    scanline= img;
    for (i=0; i<bih.biHeight; i++)
    {
        p= scanline;
        for (j=0; j<bih.biWidth; j++)
        {
            g= *p++;
            b= *p++;
            r= *p++;
        }
        scanline += scanlineSize;
    }
    return img;
}
answered on Stack Overflow Dec 12, 2017 by Paul Ogilvie • edited Jan 12, 2020 by Paul Ogilvie
0

close filePtr before opening file again

fclose(filePtr);
filePtr = fopen("C:\\Users\\mishe\\Desktop\\white.bmp", "rb");

and offset to raw data is bfh.offset

  //point the pointer file to the start of the raw data
    fseek(filePtr, bfh.offset, SEEK_SET);
answered on Stack Overflow Dec 12, 2017 by vaibhav darji

User contributions licensed under CC BY-SA 3.0