【问题标题】:Getting width and height from jpeg image file从jpeg图像文件中获取宽度和高度
【发布时间】:2012-10-29 11:33:35
【问题描述】:

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

//0xFFC0 是包含文件大小的“帧开始”标记 //0xFFC0块的结构很简单 [0xFFC0][ushort 长度][uchar 精度][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,这才是真正的大小。

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

【问题讨论】:

    标签: c height width jpeg


    【解决方案1】:

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

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

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

    Source.

    【讨论】:

    • +1 很好的解释,我落入了同样的陷阱。小提示:有几个部分不遵循一般方案,即SOI(图像开始,你提到的,0xffd8),RSTn(重新启动标记,0xffdn,n = 0..7)和EOI(结束图像,0xffd9)。 DRI (0xffdd) 遵循方案,但长度值始终为 4。en.wikipedia.org/wiki/Jpeg#Syntax_and_structure
    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 我还要指出 widthheight 应该在上面的代码中交换。换句话说,x(先读)给出高度,y 给出宽度。此外,为了支持各种 SOF 标记(例如基线 DCT、渐进式 DCT 等),可能需要扫描 0xFFC00xFFCF 之间的所有标记:参见 equivalent code in Ruby
      • 好点!当我们这样做时,不要忘记提及尺寸/大小以大端格式存储。这是来自 320x128px 图像的相关字节。 ( FF C0 - 00 11 - 08 - 00 80 - 01 40 ) 似乎 x,y 坐标在保存到文件之前被打包成 4 字节长。如果您将尺寸加载为 4 字节 int,则更改字节顺序 - 您最终会得到正确的坐标并按 x,y 顺序..
      • @deltheil 我不确定链接的 Ruby 代码。规范仅将 0xffc0..0xffc3 和 0xffc9..0xffcb 命名为 SOF 标记,而 Ruby 代码添加了 0xffc5..0xffc7 和 0xffcd..0xffcf!?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-15
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 2016-06-04
      • 2015-09-04
      • 2017-05-23
      相关资源
      最近更新 更多