简体   繁体   English

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

[英]Getting width and height from jpeg image file

I wrote this function to given filename(a jpeg file) shall print its size in pixels, w and h. 我把这个函数写成给定的文件名(一个jpeg文件)应该以像素为单位打印它的大小,w和h。 According to tutorial that I'm reading, 根据我正在阅读的教程,

//0xFFC0 is the "Start of frame" marker which contains the file size //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y] // 0xFFC0是“帧起始”标记,包含文件大小// 0xFFC0块的结构非常简单[0xFFC0] [ushort length] [uchar precision] [ushort x] [ushort y]

So, I wrote this struct 所以,我写了这个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()

and then: 接着:

#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);
    }

But I'm getting 520x537 instead of 700x537 , that's the real size. 但我得到520x537而不是700x537 ,这是真正的尺寸。

Can someone point and explain where I'm wrong? 有人能指出并解释我错在哪里吗?

A JPEG file consists of a number of sections. JPEG文件由许多部分组成。 Each section starts with 0xff , followed by 1-byte section identifier, followed by number of data bytes in the section (in 2 bytes), followed by the data bytes. 每个部分以0xff开头,后跟1字节的部分标识符,后跟部分中的数据字节数(以2个字节为单位),后跟数据字节。 The sequence 0xffc0 , or any other 0xff-- two-byte sequence, inside the data byte sequence, has no significance and does not mark a start of a section. 数据字节序列内的序列0xffc0或任何其他0xff--双字节序列没有意义,也没有标记段的开头。

As an exception, the very first section does not contain any data or length. 作为例外,第一部分不包含任何数据或长度。

You have to read each section header in turn, parse the length, then skip corresponding number of bytes before starting to read next section. 您必须依次读取每个节头,解析长度,然后在开始阅读下一节之前跳过相应的字节数。 You cannot just search for 0xffc0 , let alone just 0xc0 , without regard to the section structure. 你不能只搜索0xffc0 ,更不用说只搜索0xc0 ,而不考虑段结构。

Source . 来源

There are several issues to consider, depending on how "universal" you want your program to be. 有几个问题需要考虑,具体取决于您希望程序具有“通用性”。 First, I recommend using libjpeg . 首先,我建议使用libjpeg A good JPEG parser can be a bit gory, and this library does a lot of the heavy lifting for you. 一个好的JPEG解析器可能有点血腥,这个库为你做了很多繁重的工作。

Next, to clarify nm's statement, you have no guarantee that the first 0xFFCO pair is the SOF of interest. 接下来,为了澄清nm的陈述,您无法保证第一个0xFFCO对是感兴趣的SOF。 I've found that modern digital cameras like to load up the JPEG header with a number of APP0 and APP1 blocks, which can mean that the first SOF marker you encounter during a sequential read may actually be the image thumbnail. 我发现现代数码相机喜欢用多个APP0和APP1块加载JPEG标题,这可能意味着在顺序读取过程中遇到的第一个SOF标记实际上可能是图像缩略图。 This thumbnail is usually stored in JPEG format (as far as I have observed, anyway) and is thus equipped with its own SOF marker. 这个缩略图通常以JPEG格式存储(据我所知,无论如何),因此配备了自己的SOF标记。 Some cameras and/or image editing software can include an image preview that is larger than a thumbnail (but smaller than the actual image). 一些相机和/或图像编辑软件可以包括大于缩略图(但小于实际图像)的图像预览。 This preview image is usually JPEG and again has it's own SOF marker. 此预览图像通常为JPEG,并且还具有自己的SOF标记。 It's not unusual for the image SOF marker to be the last one. 图像SOF标记最后一个并不罕见。

Most (all?) modern digital cameras also encode the image attributes in the EXIF tags. 大多数(全部?)现代数码相机还对EXIF标签中的图像属性进行编码。 Depending upon your application requirements, this might be the most straightforward, unambiguous way to obtain the image size. 根据您的应用要求,这可能是获得图像大小最直接,最明确的方法。 The EXIF standard document will tell you all you need to know about writing an EXIF parser. EXIF标准文档将告诉您有关编写EXIF解析器的所有信息。 ( libExif is available, but it never fit my applications.) Regardless, if you roll your own EXIF or rely on a library, there are some good tools for inspecting EXIF data. libExif是可用的,但它永远不适合我的应用程序。)无论如何,如果您使用自己的EXIF或依赖库,有一些很好的工具可用于检查EXIF数据。 jhead is very good tool, and I've also had good luck with ExifTool . jhead是非常好的工具,我也和ExifTool好运。

Lastly, pay attention to endianess. 最后,要注意结束。 SOF and other standard JPEG markers are big-endian, but EXIF markers may vary. SOF和其他标准JPEG标记是big-endian,但EXIF标记可能会有所不同。

As you mention, the spec states that the marker is 0xFFC0. 如您所述,规范声明标记为0xFFC0。 But it seems that you only ever look for a single byte with the code if (b==SOF) 但似乎你只用代码来寻找单个字节if (b==SOF)

If you open the file up with a hex editor, and search for 0xFFC0 you'll find the marker. 如果使用十六进制编辑器打开文件,并搜索0xFFC0,您将找到标记。 Now as long as the first 0xC0 in the file is the marker, your code will work. 现在,只要文件中的第一个0xC0是标记,您的代码就可以工作。 If it's not though, you get all sorts of undefined behaviour. 如果不是这样,你会得到各种未定义的行为。

I'd be inclined to read the whole file first. 我倾向于先读完整个文件。 It's a jpg right, how big could it be? 这是一个jpg权利,它有多大? (thought this is important if on an embedded system) Then just step through it looking for the first char of my marker. (如果在嵌入式系统上这很重要)那么只需逐步查找我标记的第一个字符。 When found, I'd use a memcmp to see if the next 3bytes mathed the rest of the sig. 找到后,我会使用memcmp来查看接下来的3bytes是否与其余的sig相匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM