从jpeg图像文件获取宽度和高度

时间:2012-10-28 17:29:32

标签: c height width jpeg

我把这个函数写成给定的文件名(一个jpeg文件)应该以像素为单位打印它的大小,w和h。根据我正在阅读的教程,

  

// 0xFFC0是包含文件大小的“帧起始”标记   // 0xFFC0块的结构非常简单[0xFFC0] [ushort   长度] [uchar precision] [ushort x] [ushort y]

所以,我写了这个struct

#pragma pack(1)
struct imagesize {
  unsigned short len; /* 2-bytes */
  unsigned char c;    /* 1-byte */
  unsigned short x;   /* 2-bytes */
  unsigned short y;   /* 2-bytes */
}; //sizeof(struct imagesize) == 7
#pragma pack()

然后:

#define SOF 0xC0 /* start of frame */

    void jpeg_test(const char *filename)
    {
      FILE *fh;
      unsigned char buf[4];
      unsigned char b;

      fh = fopen(filename, "rb");
      if(fh == NULL) 
        fprintf(stderr, "cannot open '%s' file\n", filename);

      while(!feof(fh)) {
        b = fgetc(fh);

        if(b == SOF) {

          struct imagesize img;
    #if 1
          ungetc(b, fh);
          fread(&img, 1, sizeof(struct imagesize), fh);
    #else
          fread(buf, 1, sizeof(buf), fh);
          int w = (buf[0] << 8) + buf[1];
          int h = (buf[2] << 8) + buf[3];
          img.x = w;
          img.y = h;
    #endif

          printf("%dx%d\n",
             img.x,
             img.y);

          break;
        }
      }

      fclose(fh);
    }

但是我得到了520x537而不是700x537,这才是真正的大小。

有人可以指出并解释我错在哪里吗?

3 个答案:

答案 0 :(得分:8)

JPEG文件由许多部分组成。每个部分以0xff开头,后跟1字节的部分标识符,后跟部分中的数据字节数(以2个字节为单位),后跟数据字节。数据字节序列中的序列0xffc0或任何其他0xff--双字节序列没有任何意义,也没有标记段的开头。

作为例外,第一部分不包含任何数据或长度。

您必须依次读取每个节头,解析长度,然后在开始阅读下一节之前跳过相应的字节数。您不仅可以搜索0xffc0,更不用说0xc0,而不考虑部分结构。

Source

答案 1 :(得分:4)

要考虑几个问题,具体取决于您希望程序具有“通用性”。首先,我建议使用libjpeg。一个好的JPEG解析器可能有点血腥,这个库为你做了很多繁重的工作。

接下来,为了澄清 n.m.的语句,您无法保证第一个0xFFCO对是感兴趣的SOF。我发现现代数码相机喜欢用多个APP0和APP1块加载JPEG标题,这可能意味着在顺序读取过程中遇到的第一个SOF标记实际上可能是图像缩略图。这个缩略图通常以JPEG格式存储(据我所知,无论如何),因此配备了自己的SOF标记。一些相机和/或图像编辑软件可以包括大于缩略图(但小于实际图像)的图像预览。此预览图像通常为JPEG,并且还具有自己的SOF标记。图像SOF标记最后一个并不罕见。

大多数(全部?)现代数码相机还对EXIF标签中的图像属性进行编码。根据您的应用要求,这可能是获得图像大小最直接,最明确的方法。 EXIF standard document会告诉您编写EXIF解析器所需的全部内容。 (libExif可用,但它永远不适合我的应用程序。)无论如何,如果您使用自己的EXIF或依赖库,有一些很好的工具可用于检查EXIF数据。 jhead是非常好的工具,我也很幸运ExifTool

最后,要注意结束。 SOF和其他标准JPEG标记是big-endian,但EXIF标记可能会有所不同。

答案 2 :(得分:2)

如您所述,规范声明标记为0xFFC0。但似乎您只使用代码if (b==SOF)

查找单个字节

如果使用十六进制编辑器打开文件,并搜索0xFFC0,您将找到标记。现在,只要文件中的第一个0xC0是标记,您的代码就可以工作。如果不是这样,你会得到各种不确定的行为。

我倾向于先阅读整个文件。这是一个jpg权利,它有多大? (如果在嵌入式系统上这很重要)那么只需逐步查找我标记的第一个字符。找到后,我会使用memcmp来查看接下来的3字节是否与其余的信号相匹配。