确定JPEG(JFIF)图像的大小

时间:2009-10-12 21:35:41

标签: size jpeg

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

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

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

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

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

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

4 个答案:

答案 0 :(得分:38)

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

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

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

您需要跳过各个字段,但这并不算太糟糕。

如何通过:

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

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

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

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

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

    • EOI标记(FFD9):你已经完成了!

    • 任何非RST,SOI,EOI,TEM(FF01FFFE的标记,减去上述异常):在标记之后,读取接下来的2个字节,这是该帧头的16位大端长度(不包括2字节标记,但包括长度字段)。跳过给定的数量(通常长度减2,因为你已经得到了这些字节)。

    • 如果您在EOI之前收到文件结尾,那么您的图片已损坏。

    • 一旦你有了EOI,你就已经完成了JPEG并且应该有足够的长度。如果您希望流中有多个JPEG,则可以通过读取另一个SOI重新开始。

答案 1 :(得分:2)

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

你可以Stream.Seek(0, StreamOffset.End);然后选择流的位置吗?

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

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

修改

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

OGG应该非常适合这个。

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

答案 2 :(得分: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

答案 3 :(得分: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