指针文件在读取原始位图数据的过程中随机改变值 [英] pointer file randomly changes value in middle of reading raw bitmap data

查看:18
本文介绍了指针文件在读取原始位图数据的过程中随机改变值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在用 C 加载位图文件.我是 C 的新手,但遇到了一个问题:我有一个文件指针,它读取无符号字符到一个 rgb 像素结构,(它被称为 rgb但它按 b、g、r 和填充的顺序读取 - 这是位图格式文件的默认值).我的文件是 12x12 像素,当它到达第 9 行时,它只在每个组件中放置值204",而图像是白色的(即所有组件 = 255).在此之前的所有组件都等于 255.我将枚举更改为图像状态返回的三个定义值(未加载,蓝色,不是蓝色).EDIT2:我编辑了代码,但现在 im 等于 0xffffffff 并且无法访问.这是代码:

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

}

推荐答案

阅读位图总是一件困难的事情.

Reading in bitmaps is always a difficult thing.

有很多要点需要考虑.

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

通过这一行,您可以从文件中读取 RGBQUAD 的保留字节.但是,该成员不在文件中.文件中的图像数据包含扫描线.见下文.

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.

读取位图文件头后,您再次打开文件.但是,您没有关闭它.这可能会成功或失败,因为文件已经打开.但是你不检查 fopen 的返回值.无论如何,没有必要这样做,因为在读取 BFH 后,文件指针位于 BITMAPINFOHEADER;你可以直接读进去.而且你需要读进去,否则你不会知道位图的尺寸.

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.

来自 MSDN 文档:

From MSDN documentation:

已建立的位图文件格式由一个 BITMAPFILEHEADER 结构和一个 BITMAPINFOHEADER [...] 结构组成.RGBQUAD 结构数组(也称为颜色表)遵循位图信息标头结构.颜色表之后是颜色表中的第二个索引数组(实际位图数据).

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

对于每像素 24 位的位图,没有颜色表.

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

所以现在的顺序是:

    //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

现在我们必须计算存储图像所需的内存量.图像由 heighthwidth 像素组成.这些行称为扫描线.

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.

出于某种原因,扫描线在 4 字节边界上对齐.这意味着可能未使用扫描线的最后一个字节.所以从文件中读取位图数据的内存量为图像的高度乘以图像的扫描线数:

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

所以要解决一个 3 字节的像素 (x,y) 使用:im[y*scanlineSize + x*3]

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

.. 除了我相信扫描线是反向的,所以像素将在 im[(bih.biHeight-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;
}

这篇关于指针文件在读取原始位图数据的过程中随机改变值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆