确定 JPEG (JFIF) 图像的大小

发布于 2024-08-07 18:02:01 字数 548 浏览 2 评论 0原文

我需要找到 JPEG (JFIF) 图像的大小。该图像没有保存为独立文件,因此我无法使用 GetFileSize 或任何其他 API(图像放置在流中,并且不存在其他标头,除了通常的 JPEG/JFIF 标头)。

我做了一些研究,发现 JPEG 图像由不同的部分组成,每个部分都以帧标记(0xFF 0xXX)以及该帧的大小开头。使用这些信息,我能够从文件中解析出很多信息。

问题是,我找不到压缩数据的大小,因为压缩数据似乎没有帧​​标记。此外,压缩数据似乎遵循 SOS (FFDA) 标记,图像以图像结束 (EOI) (FFD9) 标记结束。

实现此目的的一种方法是逐个字节搜索 EOI 标记,但我认为压缩数据可能包含这种字节组合,对吗?

有没有一种简单而正确的方法来找到图像的总大小? (我更喜欢一些代码/想法没有任何外部库

基本上,我需要图像开始(SOI-FFE0)和结束之间的距离(以字节为单位)图像(EOI-FFD9)。

I need to find the size of a JPEG (JFIF) image. The image is not saved as a stand-alone file, so I can't use GetFileSize or any other API such this one (the image is placed in a stream and no other header is present, except the usual JPEG/JFIF header(s)).

I did some research and found out that JPEG images are composed of different parts, each part starting with a frame marker (0xFF 0xXX), and the size of this frame. Using this information I was able to parse a lot of information from the file.

The problem is, I cannot find the size of the compressed data, as it seems there is no frame marker for the compressed data. Also, it seems the compressed data follows the SOS (FFDA) marker and the image ends with the End Of Image (EOI) (FFD9) marker.

A way to accomplish this would be to search for the EOI marker from byte to byte, but I think the compressed data might contain this combination of bytes, right?

Is there an easy and correct way to find the total size of the image? (I would prefer some code/idea without any external library)

Basically, I need the distance (in bytes) between the Start of Image (SOI-FFE0) and End of Image (EOI-FFD9).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一抹苦笑 2024-08-14 18:02:01

压缩数据不会包含 SOI 或 EOI 字节,因此您在那里是安全的。但注释、应用程序数据或其他标头可能会这样。幸运的是,您可以根据给定的长度识别并跳过这些部分。

JPEG 规范告诉您需要什么:
http://www.w3.org/Graphics/JPEG/itu-t81.pdf

请参阅第 32 页的表 B.1。带有 * 的符号后面没有长度字段(RST、SOI、EOI、TEM)。其他人也这么做。

您将需要跳过各个字段,但这还不错。

如何进行:

  1. 开始阅读 SOI (FFD8)。这就是开始。它应该是流中的第一件事。

    • 然后,浏览文件,查找更多标记并跳过标题:

    • SOI 标记 (FFD8):图像损坏。您应该已经找到意向书了!

    • TEM (FF01):独立标记,继续。

    • RST(FFD0FFD7):独立标记,继续。您可以验证重新启动标记是否从 FFD0FFD7 计数并重复,但这对于测量长度来说不是必需的。

    • EOI 标记 (FFD9):您完成了!

    • 除 RST、SOI、EOI、TEM 之外的任何标记(FF01FFFE,减去上述例外情况):在标记之后,阅读下一个标记2 个字节,这是该帧头的 16 位大端长度(不包括 2 字节标记,但包括长度字段)。跳过给定的数量(通常长度减 2,因为您已经获得了这些字节)。

    • 如果您在 EOI 之前收到文件结尾,则图像已损坏。

    • 一旦您获得了 EOI,您就已经完成了 JPEG 并应该获得了长度。如果您希望流中包含多个 JPEG,您可以通过读取另一个 SOI 重新开始。

The compressed data will not include SOI or EOI bytes, so you are safe there. But the comment, application data, or other headers might. Fortunately, you can identify and skip these sections as the length is given.

The JPEG specification tells you what you need:
http://www.w3.org/Graphics/JPEG/itu-t81.pdf

Look at Table B.1, on page 32. The symbols that have an * do not have a length field following it (RST, SOI, EOI, TEM). The others do.

You will need to skip over the various fields, but it is not too bad.

How to go through:

  1. Start reading SOI (FFD8). This is the start. It should be the first thing in the stream.

    • Then, progress through the file, finding more markers and skipping over the headers:

    • SOI marker (FFD8): Corrupted image. You should have found an EOI already!

    • TEM (FF01): standalone marker, keep going.

    • RST (FFD0 through FFD7): standalone marker, keep going. You could validate that the restart markers count up from FFD0 through FFD7 and repeat, but that is not necessary for measuring the length.

    • EOI marker (FFD9): You're done!

    • Any marker that is not RST, SOI, EOI, TEM (FF01 through FFFE, minus the exceptions above): After the marker, read the next 2 bytes, this is the 16-bit big-endian length of that frame header (not including the 2-byte marker, but including the length field). Skip the given amount (typically length minus 2, since you already got those bytes).

    • If you get an end-of-file before EOI, then you've got a corrupted image.

    • Once you've got an EOI, you've gotten through the JPEG and should have the length. You can start again by reading another SOI if you expect more than one JPEG in your stream.

十年九夏 2024-08-14 18:02:01

也许是这样的

int GetJpgSize(unsigned char *pData, DWORD FileSizeLow, unsigned short *pWidth, unsigned short *pHeight)
{
  unsigned int i = 0;


  if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0)) {
    i += 4;

    // Check for valid JPEG header (null terminated JFIF)
    if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F')
        && (pData[i + 6] == 0x00)) {

      //Retrieve the block length of the first block since the first block will not contain the size of file
      unsigned short block_length = pData[i] * 256 + pData[i + 1];

      while (i < FileSizeLow) {
        //Increase the file index to get to the next block
        i += block_length; 

        if (i >= FileSizeLow) {
          //Check to protect against segmentation faults
          return -1;
        }

        if (pData[i] != 0xFF) {
          return -2;
        } 

        if (pData[i + 1] == 0xC0) {
          //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]
          *pHeight = pData[i + 5] * 256 + pData[i + 6];
          *pWidth = pData[i + 7] * 256 + pData[i + 8];

          return 0;
        }
        else {
          i += 2; //Skip the block marker

          //Go to the next block
          block_length = pData[i] * 256 + pData[i + 1];
        }
      }

      //If this point is reached then no size was found
      return -3;
    }
    else {
      return -4;
    } //Not a valid JFIF string
  }
  else {
    return -5;
  } //Not a valid SOI header

  return -6;
}  // GetJpgSize

Maybe something like this

int GetJpgSize(unsigned char *pData, DWORD FileSizeLow, unsigned short *pWidth, unsigned short *pHeight)
{
  unsigned int i = 0;


  if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0)) {
    i += 4;

    // Check for valid JPEG header (null terminated JFIF)
    if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F')
        && (pData[i + 6] == 0x00)) {

      //Retrieve the block length of the first block since the first block will not contain the size of file
      unsigned short block_length = pData[i] * 256 + pData[i + 1];

      while (i < FileSizeLow) {
        //Increase the file index to get to the next block
        i += block_length; 

        if (i >= FileSizeLow) {
          //Check to protect against segmentation faults
          return -1;
        }

        if (pData[i] != 0xFF) {
          return -2;
        } 

        if (pData[i + 1] == 0xC0) {
          //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]
          *pHeight = pData[i + 5] * 256 + pData[i + 6];
          *pWidth = pData[i + 7] * 256 + pData[i + 8];

          return 0;
        }
        else {
          i += 2; //Skip the block marker

          //Go to the next block
          block_length = pData[i] * 256 + pData[i + 1];
        }
      }

      //If this point is reached then no size was found
      return -3;
    }
    else {
      return -4;
    } //Not a valid JFIF string
  }
  else {
    return -5;
  } //Not a valid SOI header

  return -6;
}  // GetJpgSize
弥枳 2024-08-14 18:02:01

由于您没有发布任何语言,我不确定这是否有效,但是:

您可以 Stream.Seek(0, StreamOffset.End); 然后获取流的位置吗?

请具体说明您使用的框架。

事实是,如果文件头没有指定预期的大小,您必须查找(或读取)到图像的末尾。

编辑

由于您正在尝试流式传输多个文件,因此您将需要使用流式传输友好的容器格式。

OGG 应该很适合这个。

JPEG 实际上已经适合流式传输,但您必须保证每个文件在将其发送到流中之前都有一个有效的终止符,否则您将面临因意外输入而导致应用程序崩溃的风险。

Since you don't have any language posted, I'm not sure that this will work, but:

Can you Stream.Seek(0, StreamOffset.End); and then take the stream's position?

Please be specific about what framework you are using.

The real fact of the matter is, if the file header doesn't specify the expected size, you have to seek (or read) to the end of the image.

EDIT

Since you are trying to stream multiple files, you will want to use a streaming friendly container format.

OGG should be a nice fit for this.

JPEG is actually already streaming friendly, but you must guarantee that each file has a valid terminator before sending it down the stream or else you run the risk of crashing your app with unexpected input.

戏舞 2024-08-14 18:02:01

在 python 中,您可以将整个文件读入字符串对象,并找到第一次出现的 FF E0 和最后一次出现的 FF D9。想必,这些就是您正在寻找的开始和结束吧?

f = open("filename.jpg", "r")
s = f.read()
start = s.find("\xff\xe0")
end = s.rfind("\xff\xd9")
imagesize = end - start

In python, you could just read the whole file into a string object and find the first occurrence of FF E0 and the last occurrence of FF D9. Presumably, these are the start and end that you are looking for?

f = open("filename.jpg", "r")
s = f.read()
start = s.find("\xff\xe0")
end = s.rfind("\xff\xd9")
imagesize = end - start
强者自强 2024-08-14 18:02:01

对于 C# 和 .NET,有一个简单的解决方案。无需手动解析任何内容。无论如何,它都会读取整个集群,但不会读取完整的文件内容:

using (var fileStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    using (var image = Image.FromStream(fileStream, false, false))
    {       
         var height = image.Height;
         var width = image.Width;
    }
}

来源:GitHub 参考

In case of C# and .NET there is a simple solution. There is no need to parse anything manually. It reads a whole cluster anyway, but not the complete file contents:

using (var fileStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    using (var image = Image.FromStream(fileStream, false, false))
    {       
         var height = image.Height;
         var width = image.Width;
    }
}

Source: GitHub reference

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文