确定JPEG(JFIF)图像的大小

问题描述 投票:30回答:4

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

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

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

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

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

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

size jpeg
4个回答
38
投票

压缩数据不包括SOI或EOI字节,因此您在那里安全。但评论,应用程序数据或其他标题可能会。幸运的是,您可以在给出长度时识别并跳过这些部分。

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

请参见表B.1,第32页。具有*的符号后面没有长度字段(RST,SOI,EOI,TEM)。其他人呢。

你需要跳过各个领域,但这并不算太糟糕。

怎么办:

  1. 开始阅读SOI(FFD8)。这是一个开始。它应该是流中的第一件事。 然后,浏览文件,查找更多标记并跳过标题: SOI标记(FFD8):图像损坏。你应该已经找到了一个EOI! TEM(FF01):独立标记,继续前进。 RST(FFD0通过FFD7):独立标记,继续前进。您可以验证重启标记从FFD0FFD7计数并重复,但这不是测量长度所必需的。 EOI标记(FFD9):你已经完成了! 任何不是RST,SOI,EOI,TEM的标记(FF01FFFE,减去上面的例外):在标记之后,读取接下来的2个字节,这是该帧头的16位大端长度(不包括2字节标记,但包括长度字段)。跳过给定的数量(通常长度减去2,因为你已经得到了这些字节)。 如果您在EOI之前收到文件结尾,那么您的图像已损坏。 一旦你有了EOI,你就已经完成了JPEG并且应该有长度。如果您希望流中有多个JPEG,则可以通过读取另一个SOI重新开始。

2
投票

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

你能Stream.Seek(0, StreamOffset.End);,然后采取流的位置?

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

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

编辑

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

OGG应该很适合这个。

JPEG实际上已经是流媒体友好的,但您必须保证每个文件都有一个有效的终结符,然后再将其发送到流中,否则您可能会因意外输入而导致应用程序崩溃。


2
投票

也许这样的事情

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

0
投票

在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
© www.soinside.com 2019 - 2024. All rights reserved.