用C语言从BMP文件中读取数据

用C语言从BMP文件中读取数据,c,bitmap,base64,C,Bitmap,Base64,我在从bmp文件中读取像素时遇到问题。它可能是在行的末尾有填充或base64填充的内容。我不知道。我已经为此挣扎了几天,无法继续前进,因为下一个任务需要解决这个问题。 我只分享代码的重要部分,因为读取bmp头工作得很好(测试没有失败) bmp.c struct pixel* read_data(FILE* stream, const struct bmp_header* header){ if(stream == NULL || header == NULL){ return 0;

我在从bmp文件中读取像素时遇到问题。它可能是在行的末尾有填充或base64填充的内容。我不知道。我已经为此挣扎了几天,无法继续前进,因为下一个任务需要解决这个问题。 我只分享代码的重要部分,因为读取bmp头工作得很好(测试没有失败)

bmp.c

struct pixel* read_data(FILE* stream, const struct bmp_header* header){
  if(stream == NULL || header == NULL){
    return 0;
  }
  // w == 1 && p == 1;   w == 2 && p == 2;   w == 3 && p == 3;   w == 4 && p == 0  
  int padding = header->width % 4; 
  int num_of_pixels = header->width * header->height;
  struct pixel* Pixel[num_of_pixels];

  fseek(stream, 54, SEEK_SET); //move 54B (header size)
  int index_p = 0;
  for(int i = 0; i < header->height; i++){
    for(int j = 0; j < header->width; j++){
      Pixel[index_p] = malloc(sizeof(struct pixel));
      fread(&(Pixel[index_p]->blue), 1, 1, stream); 
      fread(&(Pixel[index_p]->green), 1, 1, stream); 
      fread(&(Pixel[index_p]->red), 1, 1, stream); 
      index_p++;
    }
    fseek(stream, padding, SEEK_CUR);  //padding at the end of row
  }
  return *Pixel;
}
标题(如果需要)(基本上我们只使用24位颜色)

main.c我不需要为调用的函数分配任何变量,因为我们有一个测试程序,我只需要在main中调用它们

int main(){

  struct bmp_header* header;
  FILE *stream = fopen("./assets/square.2x3.bmp", "rb");
  header = read_bmp_header(stream);
  read_data(stream, header);
  read_bmp(stream);
  struct bmp_image* image;
  image = malloc(sizeof(struct bmp_image));
  free_bmp_image(image);  
  fclose(stream);
  return 0;
}
测试(还有更多的测试,但这应该足够了)

所以在“==”之后是预期的结果,括号中是我的代码的结果。正如我所提到的,它可能是有填充物的,因为它开始得很好,但结束得不好。
谢谢您的帮助。

简短回答:将填充设置为
(4-((3*宽度)%4))%4

长答案

您的代码包括:

int padding = header->width % 4; 

//Some lines of code

fseek(stream, padding, SEEK_CUR);
在位图中,添加填充,直到每行是4字节的倍数。您将填充设置为宽度%4

首先,每个像素需要3个字节(对于rgb)。所以它应该是
(3*宽度)%4
。接下来,我们需要从4个字节中减去它(因为填充是占用4-
像素的
)。因此,填充将是
4-((3*宽度)%4)
。另一个小的修改是,如果
(3*width)%4==0
,那么填充将是4(然而,我们期望它是0)。因此,我们采取另一个mod4只是为了确定

所以填充结果将是
(4-((3*宽度)%4))%4

编辑:


正如用户Craig Estey在评论中指出的,最好使用sizeof(结构像素)而不是3

简短回答:将填充设置为
(4-((3*width)%4))%4

长答案

您的代码包括:

int padding = header->width % 4; 

//Some lines of code

fseek(stream, padding, SEEK_CUR);
在位图中,添加填充,直到每行是4字节的倍数。您将填充设置为宽度%4

首先,每个像素需要3个字节(对于rgb)。所以它应该是
(3*宽度)%4
。接下来,我们需要从4个字节中减去它(因为填充是占用4-
像素的
)。因此,填充将是
4-((3*宽度)%4)
。另一个小的修改是,如果
(3*width)%4==0
,那么填充将是4(然而,我们期望它是0)。因此,我们采取另一个mod4只是为了确定

所以填充结果将是
(4-((3*宽度)%4))%4

编辑:


正如用户Craig Estey在评论中指出的,最好使用sizeof(struct pixel)而不是3

Ok,我添加了
int padding=4-(header->width*3)%4;如果(padding==4){padding=0;}
添加到我的代码中,但是测试会给我这个错误“消息字符串太长”。仅仅通过查看代码,我找不到任何会导致此错误的内容。有什么想法吗?@Tchiggy不太确定。但基于代码,我假设你有一张2px x 3px的图片。在函数返回之前,是否可以尝试
printf
ing正在读取的值?printf ing似乎正确(总共打印6个像素)。是的,目前使用2x3图像,但测试中的大小可能会有所不同。我将在下一次测试中看到。
FILE*stream=“Qk0+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiy4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabase64编码流结构bmp\u头*头=读取bmp\u头(流);fseek(流、偏移、寻道集);断言“读取数据(流、头)=“/wAAAP8A”失败。[got”/wAARaJV]
因此,对于其他测试,我的输出再次是错误的,开始再次是好的,但它会打印随机字符,这些字符甚至不在输入流中。好的,我添加了
int padding=4-(header->width*3)%4;如果(padding==4){padding=0;}
添加到我的代码中,但是测试会给我这个错误“消息字符串太长”。仅仅通过查看代码,我找不到任何会导致此错误的内容。有什么想法吗?@Tchiggy不太确定。但基于代码,我假设你有一张2px x 3px的图片。在函数返回之前,是否可以尝试
printf
ing正在读取的值?printf ing似乎正确(总共打印6个像素)。是的,目前使用2x3图像,但测试中的大小可能会有所不同。我将在下一次测试中看到。
FILE*stream=“Qk0+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiy4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabase64编码流结构bmp\u头*头=读取bmp\u头(流);fseek(流、偏移、寻道集);断言“读取数据(流、头)=“/wAAAP8A”失败。[got”/wAARaJV]
因此,对于其他测试,我的输出再次是错误的,开始再次是好的,但随后它会打印随机字符,这些字符甚至不在输入流中。关于:`if(stream==NULL | | header==NULL){return 0;}`应该已经在打开流和调用此函数时进行测试。因此,无需在此处重新检查<代码>文件*stream=“Qk0+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa这看起来不像磁盘上的.bmp文件。我错过了什么?关于<代码>整数填充=标题->宽度%4这无法考虑每个像素的字节宽度,需要的不是下一个4字节倍数的字节数,而是使行成为4字节倍数所需的字节数关于:
fseek(stream,54,SEEK_SET)这不是有效的计算。建议提取“偏移量”到像素数据,然后将“文件指针”移动到文件开头,然后将“文件指针”向前移动“偏移量”字节。关于:
struct pixel*pixel[num_of_pixels]这声明了一个指向像素的指针数组,它不声明任何包含像素的数组,因为它在堆栈上,所以当函数退出时它“超出范围”。建议调用
malloc()
1: 
FILE* stream = "Qk0+AAAAAAAAADYAAAAoAAAAAgAAAAEAAAABABgAAAAAAAgAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAP8AAAA="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAP8A"' failed. [got "/wAAFctV"]

2: 
FILE* stream = "Qk1GAAAAAAAAADYAAAAoAAAAAgAAAAIAAAABABgAAAAAABAAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAAAAAAAAAP8A/wAAAA=="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAAAAAAD/AP8A"' failed. [got "/wAAAAAAAAAAAAAA"]
int padding = header->width % 4; 

//Some lines of code

fseek(stream, padding, SEEK_CUR);