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

时间:2021-10-14 17:07:07

I wrote this function to given filename(a jpeg file) shall print its size in pixels, w and h. According to tutorial that I'm reading,

我将这个函数写入给定的文件名(一个jpeg文件)将打印它的大小以像素为单位,w和h。

//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

我写了这个结构

#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?

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

3 个解决方案

#1


8  

A JPEG file consists of a number of sections. 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. 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.

JPEG文件由许多部分组成。每个部分从0xff开始,接着是1节段标识符,后面是数据字节数(2个字节),然后是数据字节。在数据字节序列中,序列0xffc0或任何其他的0xff- 2字节的序列没有意义,并且没有标记一个节的开始。

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.

源。

#2


4  

There are several issues to consider, depending on how "universal" you want your program to be. First, I recommend using libjpeg. A good JPEG parser can be a bit gory, and this library does a lot of the heavy lifting for you.

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

Next, to clarify n.m.'s statement, you have no guarantee that the first 0xFFCO pair is the SOF of interest. 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. This thumbnail is usually stored in JPEG format (as far as I have observed, anyway) and is thus equipped with its own SOF marker. 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. It's not unusual for the image SOF marker to be the last one.

其次,阐明新墨西哥州。对于这个语句,您不能保证第一个0xFFCO对是关注的SOF。我发现,现代数码相机喜欢在JPEG头中加载大量的APP0和APP1块,这可能意味着在连续读取过程中遇到的第一个SOF标记实际上可能是图像缩略图。这个缩略图通常以JPEG格式存储(就我所观察到的而言),因此具有自己的SOF。有些相机和/或图像编辑软件可以包含比缩略图大(但比实际图像小)的图像预览。这个预览图像通常是JPEG,也有自己的SOF标志。标记的图像是最后一个,这并不罕见。

Most (all?) modern digital cameras also encode the image attributes in the EXIF tags. 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. (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. jhead is very good tool, and I've also had good luck with ExifTool.

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

Lastly, pay attention to endianess. SOF and other standard JPEG markers are big-endian, but EXIF markers may vary.

最后,注意意外发现。SOF和其他标准JPEG标记是大端标记,但EXIF标记可能有所不同。

#3


2  

As you mention, the spec states that the marker is 0xFFC0. But it seems that you only ever look for a single byte with the code if (b==SOF)

正如您所提到的,规范声明该标记是0xFFC0。但是,如果(b= SOF)

If you open the file up with a hex editor, and search for 0xFFC0 you'll find the marker. Now as long as the first 0xC0 in the file is the marker, your code will work. If it's not though, you get all sorts of undefined behaviour.

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

I'd be inclined to read the whole file first. It's a jpg right, how big could it be? (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.

我倾向于先读整个文件。它是一个jpg,对,它能有多大?(如果是在嵌入式系统中,这一点很重要)然后在它中寻找我标记的第一个字符。找到后,我将使用memcmp查看接下来的3个字节是否覆盖了sig的其余部分。

#1


8  

A JPEG file consists of a number of sections. 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. 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.

JPEG文件由许多部分组成。每个部分从0xff开始,接着是1节段标识符,后面是数据字节数(2个字节),然后是数据字节。在数据字节序列中,序列0xffc0或任何其他的0xff- 2字节的序列没有意义,并且没有标记一个节的开始。

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.

源。

#2


4  

There are several issues to consider, depending on how "universal" you want your program to be. First, I recommend using libjpeg. A good JPEG parser can be a bit gory, and this library does a lot of the heavy lifting for you.

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

Next, to clarify n.m.'s statement, you have no guarantee that the first 0xFFCO pair is the SOF of interest. 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. This thumbnail is usually stored in JPEG format (as far as I have observed, anyway) and is thus equipped with its own SOF marker. 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. It's not unusual for the image SOF marker to be the last one.

其次,阐明新墨西哥州。对于这个语句,您不能保证第一个0xFFCO对是关注的SOF。我发现,现代数码相机喜欢在JPEG头中加载大量的APP0和APP1块,这可能意味着在连续读取过程中遇到的第一个SOF标记实际上可能是图像缩略图。这个缩略图通常以JPEG格式存储(就我所观察到的而言),因此具有自己的SOF。有些相机和/或图像编辑软件可以包含比缩略图大(但比实际图像小)的图像预览。这个预览图像通常是JPEG,也有自己的SOF标志。标记的图像是最后一个,这并不罕见。

Most (all?) modern digital cameras also encode the image attributes in the EXIF tags. 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. (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. jhead is very good tool, and I've also had good luck with ExifTool.

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

Lastly, pay attention to endianess. SOF and other standard JPEG markers are big-endian, but EXIF markers may vary.

最后,注意意外发现。SOF和其他标准JPEG标记是大端标记,但EXIF标记可能有所不同。

#3


2  

As you mention, the spec states that the marker is 0xFFC0. But it seems that you only ever look for a single byte with the code if (b==SOF)

正如您所提到的,规范声明该标记是0xFFC0。但是,如果(b= SOF)

If you open the file up with a hex editor, and search for 0xFFC0 you'll find the marker. Now as long as the first 0xC0 in the file is the marker, your code will work. If it's not though, you get all sorts of undefined behaviour.

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

I'd be inclined to read the whole file first. It's a jpg right, how big could it be? (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.

我倾向于先读整个文件。它是一个jpg,对,它能有多大?(如果是在嵌入式系统中,这一点很重要)然后在它中寻找我标记的第一个字符。找到后,我将使用memcmp查看接下来的3个字节是否覆盖了sig的其余部分。