【问题标题】:How to get the width/height of jpeg file without using library?如何在不使用库的情况下获取 jpeg 文件的宽度/高度?
【发布时间】:2013-08-16 01:19:14
【问题描述】:

首先我想说我尝试了很多次通过谷歌搜索找到答案,我找到了很多结果但我不明白,因为我不知道读取二进制文件的想法,并转换值获得可读值。

我尝试过这样做。

unsigned char fbuff[16];
FILE *file;
file = fopen("C:\\loser.jpg", "rb");
if(file != NULL){
   fseek(file, 0, SEEK_SET);
   fread(fbuff, 1, 16, file);
   printf("%d\n", fbuff[1]);
   fclose(file);
}else{
   printf("File does not exists.");
}

我想要一个简单的示例说明,如何从文件头获取 jpeg 文件的宽度/高度,然后将该值转换为可读值。

【问题讨论】:

  • 您有 jpeg 文件中包含的内容的详细信息吗?如果有,请将其包含在您的问题中。我怀疑您的上述方法是否可行,因为开头通常有一个标题,然后实际像素值开始。如果您只需要高度和宽度信息,我相信您可以通过单独阅读标题来获得。
  • @mishr:我说的是一般意义上的jpeg files
  • 我明白,但问题是你知道 jpeg 文件的格式是什么吗?还是您希望我们为您找到它?
  • @mishr:这是第一次处理二进制文件,比如jpeg,我对此一无所知。
  • 看看这个fastgraph.com/help/jpeg_header_format.html。它表示标题分别包含偏移量 2 和 4 处的宽度和高度信息。您需要做的就是使用fseekfread 指向这些偏移量,并从每个位置读取2 个字节。然后您需要将这些字节转换为整数。试一试。

标签: c jpeg binaryfiles


【解决方案1】:

不幸的是,JPEG 似乎并不简单。您应该查看jhead 命令行工具的源代码。它提供了这些信息。浏览源代码时,您将看到函数ReadJpegSections。此功能扫描 JPEG 文件中包含的所有片段以提取所需信息。图片宽高是在处理带有SOFn标记的帧时得到的。

我看到来源在公共领域,所以我将显示获取图像信息的sn-p:

static int Get16m(const void * Short)
{
    return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
}

static void process_SOFn (const uchar * Data, int marker)
{
    int data_precision, num_components;

    data_precision = Data[2];
    ImageInfo.Height = Get16m(Data+3);
    ImageInfo.Width = Get16m(Data+5);

从源代码中,我很清楚没有包含此信息的单个“标题”。您必须扫描 JPEG 文件,解析每个片段,直到找到包含所需信息的片段。这在wikipedia article 中有描述:

JPEG 图像由一系列片段组成,每个片段都以一个标记开头,每个片段都以一个 0xFF 字节开头,后跟一个字节,指示它是什么类型的标记。一些标记仅由这两个字节组成;其他的后跟两个字节,指示随后的特定于标记的有效负载数据的长度。


JPEG 文件由一系列片段组成:

SEGMENT_0
SEGMENT_1
SEGMENT_2
...

每个段都以一个 2 字节的标记开始。第一个字节是0xFF,第二个字节决定了段的类型。随后是段长度的编码。段内是特定于该段类型的数据。

图像的宽度和高度可以在SOFn 类型的片段中找到,或“帧开始 [n]”,其中“n”是某个数字,对于 JPEG 解码器来说意味着特殊的东西。只查找SOF0 就足够了,它的字节名称是0xC0。找到此帧后,您可以对其进行解码以找到图像的高度和宽度。

因此,执行您想要的程序的结构如下所示:

file_data = the data in the file
data = &file_data[0]
while (data not at end of file_data)
    segment_type = decoded JPEG segment type at data
    if (type != SOF0)
        data += byte length for segment_type
        continue
    else
        get image height and width from segment
        return

这本质上就是Michael Petrov's get_jpeg_size() implementation 中的结构。

【讨论】:

  • @LionKing,如果解释不清楚,或者您需要其他帮助,请告诉我。
  • 谢谢,不过没看懂,想要一个很简单的方法和例子,以便理解。
  • 我非常感谢投反对票的理由。谢谢!
  • 很抱歉,这是无意(错误)发生的,请见谅。
  • @LionKing:真的没有问题。仅供参考,反对票是完全匿名的,所以我无法知道谁反对我。我只是想要一个理由,以便改进答案。
【解决方案2】:

那么你必须找到 jpeg 的高度和宽度标记,即 [ffc0]。

在二进制格式中找到ffc0后,四、五字节为高,六、七字节为宽。

eg: [ff c0] d8 c3 c2 [ff da] [00 ff]
                      |         |
                      |         |
                      ->height  ->width

int position;
unsigned char len_con[2];
/*Extract start of frame marker(FFC0) of width and hight and get the position*/
for(i=0;i<FILE_SIZE;i++)
{
    if((image_buffer[i]==FF) && (image_buffer[i+1]==c0) )
    {
        position=i;
    }
}
/*Moving to the particular byte position and assign byte value to pointer variable*/
position=position+5;
*height=buffer_src[position]<<8|buffer_src[position+1];
*width=buffer_src[position+2]<<8|buffer_src[position+3];

printf("height %d",*height);
printf("width %d",*width);

【讨论】:

    【解决方案3】:

    这个问题很老,其他答案是正确的,但它们的格式不是最简单的。我只是使用getc 来快速获取尺寸,同时跳过不相关的标记(它还支持Progressive JPEGs):

      int height, width;
      // start of image (SOI)
      getc(f);   // oxff
      getc(f);   // oxd8
      // Scan miscellaneous markers until we reach SOF0 marker (0xC0)
      for(;;) {
         // next marker
         int marker;
         while((marker = getc(f)) != 0xFF);
         while((marker = getc(f)) == 0xFF);
         // SOF
         if (marker == 0xC0 || marker == 0xC2) {
            getc(f);   // length (2 bytes)
            getc(f);   // #
            getc(f);   // bpp, usually 8
            height = (getc(f) << 8) + getc(f);   // height
            width = (getc(f) << 8) + getc(f);   // width
            break;
         }
      }
    

    【讨论】:

    • 除非我遗漏了什么,否则如果 ff c0 或 ff c2 段出现在有效负载恰好包含 ff c0 / 的其他段之后,则读取所有字节的所有其他答案都将失败ff c2.
    • @Dave S 从未听说过字节填充?
    • @Luca 当我浏览规范中的 wiki 条目时,我读到它说段只对熵编码数据进行字节填充,因此 ff co / ff c2 可能出现在其他有效负载中。
    • @DaveS 你知道其他更可靠的方法来找出这些信息吗?
    • @HRK44 只是做一些最小的解析,读取记录大小并跳过它们,直到你看到 ff c0 记录。
    【解决方案4】:

    JPEG 文件中的图像尺寸如下:

    1) 寻找FF C0

    2) 在此位置之后的偏移量 +4 和 +6 处分别是高度和宽度(单词)。

    在大多数情况下,高度和宽度的绝对偏移量分别为 A3 和 A5。

    【讨论】:

      【解决方案5】:

      这是我编写的一些简单代码,似乎可以可靠地工作。

      #define MOTOSHORT(p) ((*(p))<<8) + *(p+1)
      unsigned char cBuf[32];
      int iBytes, i, j, iMarker, iFilesize;
      unsigned char ucSubSample;
      int iBpp, iHeight, iWidth;
      
               Seek(iHandle, 0, 0); // read the first 32 bytes
               iBytes = Read(iHandle, cBuf, 32);
      
               i = j = 2; /* Start at offset of first marker */
               iMarker = 0; /* Search for SOF (start of frame) marker */
               while (i < 32 && iMarker != 0xffc0 && j < iFileSize)
                  {
                  iMarker = MOTOSHORT(&cBuf[i]) & 0xfffc;
                  if (iMarker < 0xff00) // invalid marker, could be generated by "Arles Image Web Page Creator" or Accusoft
                     {
                     i += 2;
                     continue; // skip 2 bytes and try to resync
                     }
                  if (iMarker == 0xffc0) // the one we're looking for
                     break;
                  j += 2 + MOTOSHORT(&cBuf[i+2]); /* Skip to next marker */
                  if (j < iFileSize) // need to read more
                     {
                     Seek(iHandle, j, 0); // read some more
                     iBytes = Read(iHandle, cBuf, 32);
                     i = 0;
                     }
                  else // error, abort
                     break;
                  } // while
               if (iMarker != 0xffc0)
                  goto process_exit; // error - invalid file?
               else
                  {
                  iBpp = cBuf[i+4]; // bits per sample
                  iHeight = MOTOSHORT(&cBuf[i+5]);
                  iWidth = MOTOSHORT(&cBuf[i+7]);
                  iBpp = iBpp * cBuf[i+9]; /* Bpp = number of components * bits per sample */
                  ucSubSample = cBuf[i+11];
                  }
      

      【讨论】:

      • 谢谢,前面的例子是使用C/C++?,SeekRead是什么函数?,这个函数MOTOSHORT有什么好处?,还有@ 987654326@变量?.
      • 查找和读取函数是通用文件 i/o,应该存在于所有系统中。 MOTOSHORT 是一个宏(见代码顶部),它便于在任何系统上读取 big endian 短裤,而不管字节顺序如何。 ihandle 变量是假定在调用函数之前打开的文件句柄。
      【解决方案6】:
      int  GetJpegDimensions(
          char            *pImage,
          size_t          nSize,
          unsigned32      *u32Width,
          unsigned32      *u32Height,
          char            *szErrMsg)
      {
          int             nIndex;
          int             nStartOfFrame;
          int             nError = NO_ERROR;
          bool            markerFound = false;
          unsigned char   ucWord0;
          unsigned char   ucWord1;
      
          // verify START OF IMAGE marker = FF D8
          nIndex = 0;
          ucWord0 = pImage[nIndex];
          ucWord1 = pImage[nIndex+1];
      
          // marker FF D8  starts a valid JPEG
          if ((ucWord0 == 0xFF)  && (ucWord1 == 0xD8))
          {
              // search for START OF FRAME 0  marker  FF C0
              for (nIndex = 2;
                  (nIndex < nSize-2) && (markerFound == false);
                   nIndex += 2)
              {
                  ucWord0 = pImage[nIndex];
                  ucWord1 = pImage[nIndex+1];
                  if (ucWord0 == 0xFF)
                  {
                      if (ucWord1 == 0xC0)
                      {
                          markerFound = true;
                          nStartOfFrame = nIndex;
                      }
                  }
                  if (ucWord1 == 0xFF)
                  {
                      ucWord0 = pImage[nIndex+2];
                      if (ucWord0 == 0xC0)
                      {
                          markerFound = true;
                          nStartOfFrame = nIndex+1;
                      }
                  }
              } // while
      
              if (markerFound)
              {
                  nError  = NO_ERROR;
                  ucWord0 = pImage[nStartOfFrame+5];
                  ucWord1 = pImage[nStartOfFrame+6];
                  *u32Height = ucWord1 + (ucWord0 << 8);
      
                  ucWord0 = pImage[nStartOfFrame+7];
                  ucWord1 = pImage[nStartOfFrame+8];
                  *u32Width =  ucWord1 + (ucWord0 << 8);
              }
              else
              {
                  // start of frame 0 not found
                  nError = -2;
                  sprintf(szErrMsg,
                    "Not a valid JPEG image. START OF FRAME 0 marker FFC0 not found");
              }
          }
          else   // START OF IMAGE marker not found
          {
              nError = -1;
              sprintf(szErrMsg,
                "Not a valid JPEG image. START OF IMAGE marker FFD8 not found");
          }
          return nError;
      }
      

      【讨论】:

        【解决方案7】:

        这是我用 Java 编写的代码。适用于从相机拍摄的 jpeg。它扫描所有代码以找到最大的图像尺寸。我无法改进它以跳过每个块的长度,因为它不起作用。如果有人可以改进代码来做到这一点,那就太好了。

        int getShort(byte[] p, int i)
        {
           int p0 = p[i] & 0xFF;
           int p1 = p[i+1] & 0xFF;
           return p1 | (p0 << 8);
        }
        
        int[]  GetJpegDimensions(byte[] b)
        {
            int nIndex;
            int height=0, width=0, size=0;
            int nSize = b.length;
        
            // marker FF D8  starts a valid JPEG
            if (getShort(b,0) == 0xFFD8)
               for (nIndex = 2; nIndex < nSize-1; nIndex += 4)
                  if (b[nIndex] == -1/*FF*/ && b[nIndex+1] == -64/*C0*/)
                  {
                     int w = getShort(b,nIndex+7);
                     int h = getShort(b,nIndex+5);
                     if (w*h > size)
                     {
                        size = w*h;
                        width = w;
                        height = h;
                     }
                  }
            return new int[]{width,height};
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-13
          • 2010-12-19
          • 2013-12-01
          相关资源
          最近更新 更多