38823

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

Question:

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). <strong>EDIT2</strong>: 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;

}

Answer1:

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 <em>scanlines</em>. 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:

<blockquote>

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

</blockquote>

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 <em>heighth</em> rows of <em>width</em> pixels. The rows are called <em>scanlines</em>.

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

Answer2:

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);

Recommend