3

我将此函数写入给定的文件名(一个 jpeg 文件)应以像素、w 和 h 为单位打印其大小。根据我正在阅读的教程,

//0xFFC0 是包含文件大小的“帧开始”标记 //0xFFC0 块的结构非常简单 [0xFFC0][ushort length][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,而是真正的大小。

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

4

3 回答 3

8

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

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

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

来源

于 2012-10-28T19:28:22.097 回答
4

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

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

大多数(全部?)现代数码相机也在 EXIF 标签中编码图像属性。根据您的应用程序要求,这可能是获取图像大小的最直接、最明确的方法。EXIF 标准文档将告诉您编写 EXIF 解析器所需的所有知识。(libExif可用,但它不适合我的应用程序。)无论如何,如果您使用自己的 EXIF 或依赖库,有一些很好的工具可以检查 EXIF 数据。jhead是非常好的工具,我也很幸运使用ExifTool

最后,注意字节序。SOF 和其他标准 JPEG 标记是大端的,但 EXIF 标记可能会有所不同。

于 2012-10-29T03:33:35.610 回答
2

正如您所提到的,规范指出标记是 0xFFC0。但似乎你只用代码寻找一个字节if (b==SOF)

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

我倾向于先阅读整个文件。是jpg吧,能有多大?(认为​​这在嵌入式系统上很重要)然后只需逐步查找我的标记的第一个字符。找到后,我会使用 memcmp 来查看下一个 3 字节是否计算了信号的其余部分。

于 2012-10-28T18:39:49.290 回答