理解并获取C中的位图信息

时间:2015-05-31 11:05:13

标签: c bitmap

我很难理解和解析位图图像中存在的信息数据。为了更好地理解,我阅读了以下教程Raster Data.

现在,存在的代码如下,(Greyscale 8bit颜色值)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/*-------STRUCTURES---------*/
typedef struct {int rows; int cols; unsigned char* data;} sImage;

/*-------PROTOTYPES---------*/
long getImageInfo(FILE*, long, int);

int main(int argc, char* argv[])
{
  FILE          *bmpInput, *rasterOutput;
  sImage        originalImage;
  unsigned char     someChar;
  unsigned char*    pChar;
  int           nColors;  /* BMP number of colors */
  long          fileSize; /* BMP file size */
  int           vectorSize; /* BMP vector size */
  int           r, c;       /* r = rows, c = cols */


  /* initialize pointer */
  someChar = '0';
  pChar = &someChar;

  if(argc < 2)
  {
    printf("Usage: %s bmpInput.bmp\n", argv[0]);
    //end the execution
    exit(0);
  }
  printf("Reading filename %s\n", argv[1]);

  /*--------READ INPUT FILE------------*/
  bmpInput = fopen(argv[1], "rb");
  //fseek(bmpInput, 0L, SEEK_END);

  /*--------DECLARE OUTPUT TEXT FILE--------*/
  rasterOutput = fopen("data.txt", "w");

  /*--------GET BMP DATA---------------*/
  originalImage.cols = (int)getImageInfo(bmpInput, 18, 4);
  originalImage.rows = (int)getImageInfo(bmpInput, 22, 4);
  fileSize = getImageInfo(bmpInput, 2, 4);
  nColors = getImageInfo(bmpInput, 46, 4);
  vectorSize = fileSize - (14 + 40 + 4*nColors);

  /*-------PRINT DATA TO SCREEN-------------*/
  printf("Width: %d\n", originalImage.cols);
  printf("Height: %d\n", originalImage.rows);
  printf("File size: %ld\n", fileSize);
  printf("# Colors: %d\n", nColors);
  printf("Vector size: %d\n", vectorSize);

  /*----START AT BEGINNING OF RASTER DATA-----*/
  fseek(bmpInput, (54 + 4*nColors), SEEK_SET);

  /*----------READ RASTER DATA----------*/
  for(r=0; r<=originalImage.rows - 1; r++)
  {
    for(c=0; c<=originalImage.cols - 1; c++)
    {
      /*-----read data and print in (row,column) form----*/
      fread(pChar, sizeof(char), 1, bmpInput);
      fprintf(rasterOutput, "(%d, %d) = %d\n", r, c, *pChar);
    }
  }

  fclose(bmpInput);
  fclose(rasterOutput);


}


/*----------GET IMAGE INFO SUBPROGRAM--------------*/
long getImageInfo(FILE* inputFile, long offset, int numberOfChars)
{
  unsigned char     *ptrC;
  long          value = 0L;
  unsigned char     dummy;
  int           i;

  dummy = '0';
  ptrC = &dummy;

  fseek(inputFile, offset, SEEK_SET);

  for(i=1; i<=numberOfChars; i++)
  {
    fread(ptrC, sizeof(char), 1, inputFile);
    /* calculate value based on adding bytes */
    value = (long)(value + (*ptrC)*(pow(256, (i-1))));
  }
  return(value);

} /* end of getImageInfo */

我不理解: -

  1. 我无法理解&#39; GET IMAGE INTOSUBPROGRAM&#39; 部分代码试图获取图像信息,如行,列等没有。为什么这些信息是否存储在4个字节以上,value = (long)(value + (*ptrC)*(pow(256, (i-1))));指令的用途是什么。

  2. 为什么创建了unsigned char dummy ='0'然后分配了ptrC =&dummy

  3. 为什么我们只能通过读取1个字节的数据来获取图像中的行数,例如获取特定行和列的灰度值。

  4. 为什么我们使用 unsigned char 来存储字节,是不是有其他数据类型或int或long我们可以在这里有效使用?

  5. 请帮助我理解这些疑惑(混乱!!?)如果他们听起来很无聊,我会原谅我。

    谢谢。

2 个答案:

答案 0 :(得分:3)

我会说这个教程在某些方面非常糟糕,你理解它的问题并不总是因为初学者。

  

我无法理解'GET IMAGE INTOSUBPROGRAM'部分,其中代码试图获取图像信息,如行,列等没有。为什么这些信息存储超过4个字节,值的用途是什么=(长)(值+( ptrC)(pow(256,(i-1))));指令。

存储超过4个字节的原因是允许图像的大小在0到2 ^ 32-1之间。如果我们只使用一个字节,我们只能有0..255的图像和2个字节的0..65535。

奇怪的value = (long)(value + (*ptrC)*(pow(256, (i-1))));是我以前从未见过的。它用于将字节转换为long,以便它可以用于任何字节序。我们的想法是使用256的幂来将*ptrC设置为value,即将第一个字节与1相乘,然后将其与256相乘,接着将其与65536等相乘。

更易读的方法是使用班次,例如value = value + ((long)(*ptrC) << 8*(i-1));。或者更好的是从最高的一个读取字节到更低的字节并使用value = value << 8 + *ptrC;。在我的眼中好多了,但是当字节以不同的顺序出现时,并不总是那么简单。

更容易理解的简单重写是

long getImageInfo(FILE* inputFile, long offset, int numberOfChars)
{
  unsigned char     ptrC;
  long          value = 0L;
  int           i;

  fseek(inputFile, offset, SEEK_SET);

  for(i=0; i<numberOfChars; i++)  // Start with zero to make the code simpler
  {
    fread(&ptrC, 1, 1, inputFile); // sizeof(char) is always 1, no need to use it
    value = value + ((long)ptrC << 8*i); // Shifts are a lot simpler to look at and understand what's the meaning
  }
  return value; // Parentheses would make it look like a function
}
  

为什么会创建unsigned char dummy ='0'然后分配ptrC =&amp; dummy?

这也毫无意义。他们可以使用unsigned char ptrC,然后使用&ptrC代替ptrCptrC代替*ptrC。这也表明它只是一个普通的静态变量。

  

为什么我们不能通过读取1个字节的数据来获取图像中的行数,例如获取特定行和列的灰度值。

如果图像高3475行怎么办?一个字节是不够的。所以它需要更多的字节。阅读方式有点复杂。

  

为什么我们使用unsigned char来存储字节,是不是有其他数据类型或int或long我们可以在这里有效使用?

无符号字符长度恰好是一个字节。那么为什么我们会使用任何其他类型来存储一个字节呢?

答案 1 :(得分:1)

(4)二进制文件的数据由字节组成,在C中由unsigned char表示。因为这是一个很长的要输入的词,有时typedef会被byteuchar提供。一种良好的符合标准的定义字节的方法是使用uint8_t中的<stdint.h>

(3)我不太确定你想要的是什么,但是第一个字节 - 通常是54,但有一些BMP文件 - 构成了一个BMP文件,其中包含有关颜色的信息图像的深度,宽度和高度。字节54之后的字节存储原始数据。我没有测试过yopur代码,但填充可能存在问题,因为必须填充每行的数据以使原始数据大小可以被4整除。

(2)这里定义一个额外的指针没有意义。您也可以直接fread(&dummy, ...)

(1)呃。此函数从文件中位置offset的文件中读取多字节值。该文件由字节组成,但是几个字节可以形成其他数据类型。例如,一个4字节的无符号字由:

组成
uint8_t raw[4];
uint32_t x;

x = raw[0] + raw[1]*256 + raw[2]*256*256 + raw[3]*256*256*256;

在PC上,它使用Little Endian数据。

该示例还显示了pow(256, i)的来源。在这里使用pow函数不是一个好主意,因为它意味着与浮点数一起使用。即使乘以256也不是很惯用。通常,我们通过字节移位来构造值,其中乘以2是左移1,因此乘以256是左移8。类似地,上面的加法添加非重叠范围并且通常表示为按位OR,|

x = raw[0] | (raw[1]<<8) | (raw[2]<<16) | (raw[3]<<24);

该函数通过重新定位文件指针(并将其保留在新位置)来访问文件。那不是很有效。最好将标题读取为54字节数组并直接访问数组。

代码陈旧而笨拙。看到类似的东西:

for(r=0; r<=originalImage.rows - 1; r++)

已经足够让我不相信它。我相信你可以找到一个从BMP读取灰度图像的更好例子。您甚至可以自己编写,并以BMP格式的Wikipedia article开头。