C 从jpeg图像文件获取宽度和高度

C 从jpeg图像文件获取宽度和高度,c,height,width,jpeg,C,Height,Width,Jpeg,我写了这个函数给给定的文件名(一个jpeg文件)应该打印它的大小在像素,w和h。根据我正在阅读的教程 //0xFFC0是包含文件大小的“帧开始”标记 //0xFFC0块的结构非常简单[0xFFC0][ushort 长度][uchar精度][ushort x][ushort y] 所以,我写了这个struct #pragma pack(1) struct imagesize { unsigned short len; /* 2-bytes */ unsigned char c; /*

我写了这个函数给给定的文件名(一个jpeg文件)应该打印它的大小在像素,w和h。根据我正在阅读的教程

//0xFFC0是包含文件大小的“帧开始”标记 //0xFFC0块的结构非常简单[0xFFC0][ushort 长度][uchar精度][ushort x][ushort y]

所以,我写了这个
struct

#pragma pack(1)
struct imagesize {
  unsigned short len; /* 2-bytes */
  unsigned char c;    /* 1-byte */
  unsigned short x;   /* 2-bytes */
  unsigned short y;   /* 2-bytes */
}; //sizeof(struct imagesize) == 7
#pragma pack()
然后:

#define SOF 0xC0 /* start of frame */

    void jpeg_test(const char *filename)
    {
      FILE *fh;
      unsigned char buf[4];
      unsigned char b;

      fh = fopen(filename, "rb");
      if(fh == NULL) 
        fprintf(stderr, "cannot open '%s' file\n", filename);

      while(!feof(fh)) {
        b = fgetc(fh);

        if(b == SOF) {

          struct imagesize img;
    #if 1
          ungetc(b, fh);
          fread(&img, 1, sizeof(struct imagesize), fh);
    #else
          fread(buf, 1, sizeof(buf), fh);
          int w = (buf[0] << 8) + buf[1];
          int h = (buf[2] << 8) + buf[3];
          img.x = w;
          img.y = h;
    #endif

          printf("%dx%d\n",
             img.x,
             img.y);

          break;
        }
      }

      fclose(fh);
    }
#定义SOF 0xC0/*帧的开始*/
无效jpeg_测试(常量字符*文件名)
{
文件*fh;
无符号字符buf[4];
无符号字符b;
fh=fopen(文件名,“rb”);
如果(fh==NULL)
fprintf(stderr,“无法打开“%s”文件,\n”,文件名);
而(!feof(fh)){
b=fgetc(fh);
if(b==SOF){
结构图像大小img;
#如果1
ungetc(b,fh);
fread(&img,1,sizeof(结构图像大小),fh);
#否则
fread(buf,1,sizeof(buf),fh);

int w=(buf[0]正如您所提到的,规范声明标记为0xFFC0。但是,如果(b==SOF)

如果您使用十六进制编辑器打开文件,搜索0xFFC0,您将找到标记。现在,只要文件中的第一个0xC0是标记,您的代码就可以工作。如果不是,则会出现各种未定义的行为


我倾向于先阅读整个文件。它是一个jpg,对吗?它能有多大?(如果在嵌入式系统上,这很重要)然后一步一步地查找标记的第一个字符。找到后,我会使用memcmp查看接下来的3个字节是否与sig的其余部分匹配。

JPEG文件由多个部分组成。每个部分以
0xff
开头,后跟1字节的部分标识符,后跟部分中的数据字节数(2字节),后跟数据字节。数据字节序列内的序列
0xffc0
或任何其他
0xff--
两字节序列没有意义,也不标记节的开始

作为例外,第一节不包含任何数据或长度

在开始阅读下一节之前,您必须依次阅读每个节头,解析长度,然后跳过相应的字节数。您不能只搜索
0xffc0
,更不用说只搜索
0xc0
,而不考虑节结构


根据你想要的程序的“通用性”,有几个问题需要考虑。首先,我推荐使用。一个好的JPEG解析器可能有点血腥,这个库对你来说有很多繁重的事情。

接下来,为了澄清n.m.的说法,您无法保证第一个0xFFCO对是感兴趣的SOF。我发现现代数码相机喜欢在JPEG头中加载一些APP0和APP1块,这可能意味着您在顺序读取过程中遇到的第一个SOF标记实际上可能是图像缩略图。这是bMail通常以JPEG格式存储(据我观察,无论如何),因此配备了自己的SOF标记。一些相机和/或图像编辑软件可以包括比缩略图大(但比实际图像小)的图像预览。此预览图像通常为JPEG格式,并且再次具有自己的SOF标记。图像SOF标记作为最后一个标记并不罕见

大多数(全部?)现代数码相机还将图像属性编码到EXIF标记中。根据您的应用程序要求,这可能是获取图像大小的最直接、最明确的方法。将告诉您编写EXIF解析器所需的全部知识。(可用,但它不适合我的应用程序。)不管怎样,如果你使用自己的EXIF或者依赖一个库,有一些很好的工具来检查EXIF数据。这是一个很好的工具,我也很幸运


最后,请注意尾数。SOF和其他标准JPEG标记是大尾数,但EXIF标记可能会有所不同。

我还想指出,
width
height
应该在上述代码中互换。换句话说,
x
(先读)给出高度,
y
宽度。还支持各种SOF标记(例如基线DCT、渐进DCT等)您可能希望扫描
0xFFC0
0xFFCF
之间的所有标记:请看。好的一点!在这里,不要忘了提到维度/大小以大端格式存储。以下是来自320x128px图像的相关字节。(FF C0-00 11-08-00 80-01 40)似乎x,y坐标在保存到文件之前很久就被压缩到了一个4字节中。如果您将维度作为4字节int加载,然后更改endianness-您最终得到的坐标是正确的,并且是x,y顺序..@deltheil,我不确定链接的Ruby代码。规范只将0xffc0..0xffc3和0xffc9..0xffcb命名为SOF标记,而Ruby代码添加了0xffc5..0xffc7和0xffcd..0xffcf!?+1非常好的解释,我陷入了相同的陷阱。次要提示:有几个部分不遵循一般方案,即SOI(图像的开始,您提到的,0xffd8)、RSTn(重新启动标记,0xffdn,n=0..7)和EOI(图像的结束,0xffd9)。DRI(0xffdd)遵循方案,但长度值始终为4。