13

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

我尝试做的事情。

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 文件的宽度/高度,然后将该值转换为可读值。

4

7 回答 7

19

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

我看到来源在公共领域,所以我将展示获取图像信息的片段:

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 文件,解析每个片段,直到找到包含所需信息的片段。这在维基百科文章中有所描述:

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


JPEG 文件由一系列段组成:

SEGMENT_0
SEGMENT_1
SEGMENT_2
...

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

图像的宽度和高度位于类型SOFn为 或“帧开始 [n]”的段中,其中“n”是某个数字,对于 JPEG 解码器来说意味着特殊的东西。只查找 a 应该就足够了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 的get_jpeg_size()实现中发现的结构。

于 2013-08-16T02:03:14.157 回答
12

那么你必须找到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);
于 2014-06-18T12:22:42.033 回答
5

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

  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;
     }
  }
于 2017-04-10T00:16:28.723 回答
2

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

1) 寻找FF C0

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

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

于 2019-05-12T09:48:11.733 回答
1

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

#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];
            }
于 2013-08-17T13:27:28.543 回答
0

这是我用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};
}
于 2014-12-04T22:15:56.630 回答
0
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;
}
于 2014-03-20T19:50:16.943 回答