如何在不使用库的情况下获取jpeg文件的宽度/高度?

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

我尝试过做什么

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似乎并不简单。 您应该查看jhead命令行工具的源代码。 它提供了这些信息。 浏览源代码时,您将看到函数ReadJpegSections 。 此function扫描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解码器的特殊内容。 仅查看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()实现中的结构 。

然后你必须找到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 

问题是旧的,其他答案是正确的,但他们的格式不是最简单的。 我只是使用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; } } 

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

 #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]; } 
 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; } 

这是我用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}; }