【问题标题】:Getting Image size of JPEG from its binary从二进制文件中获取 JPEG 的图像大小
【发布时间】:2011-01-31 20:24:23
【问题描述】:

我有很多图像大小不同的 jpeg 文件。例如,这里是大小为 256*384(像素)的图像的 hexdump 给出的前 640 个字节:

0000000: ffd8 ffe0 0010 4a46 4946 0001 0101 0048  ......JFIF.....H
0000010: 0048 0000 ffdb 0043 0003 0202 0302 0203  .H.....C........
0000020: 0303 0304 0303 0405 0805 0504 0405 0a07  ................
0000030: 0706 080c 0a0c 0c0b 0a0b 0b0d 0e12 100d  ................

我猜尺寸信息一定在这些行之内。但是我无法看到哪些字节正确地给出了大小。谁能帮我找到包含尺寸信息的字段?

【问题讨论】:

    标签: file binary jpeg


    【解决方案1】:

    根据JPEG page on wikipediaSyntax and structure 部分,图像的宽度和高度似乎并没有存储在图像本身中——或者,至少,不是以一种很容易找到的方式.


    不过,引用JPEG image compression FAQ, part 1/2

    主题:[22] 我的程序如何从 JPEG 文件中提取图像尺寸 文件?

    JPEG 文件的标题包括 一系列块,称为“标记”。 图片高度和宽度被存储 在 SOFn 类型的标记中 (Start Of 框架,类型 N)
    查找 SOFn 你必须跳过前面的 标记;你不必知道是什么 在其他类型的标记中,只需 用他们的长词跳过 他们。
    所需的最低逻辑是 也许是一页 C 代码。
    (有些 人们建议只是搜索 对于表示 SOFn 的字节对, 不注意标记 块结构。这是不安全的 因为先前的标记可能包含 SOFn 模式,无论是偶然还是 因为它包含 JPEG 压缩 缩略图。如果你不跟随 您将检索的标记结构 缩略图的大小而不是 主图像大小。)
    大量 可以在 C 中找到注释的示例 IJG 发行版中的 rdjpgcom.c (见第 2 部分,第 15 项)。
    Perl 代码 可以在 wwwis 中找到,从 http://www.tardis.ed.ac.uk/~ark/wwwis/.

    (呃,那个链接好像坏了……)


    不过,这里有一部分 C 代码可以帮助您:Decoding the width and height of a JPEG (JFIF) file

    【讨论】:

    • 如果是这种情况,nautilus 或其他图像查看器如何决定图像的分辨率?他们似乎也同意该图像的值 256*384
    • 非常感谢!我现在明白了。 greping 0xFFC0 似乎有效,无论如何我确实了解其中涉及的危险!再次感谢!顺便说一句,这是我在 stackoverflow 上的第一篇文章!对响应的速度和精确性感到非常惊讶。谢谢大家!
    • 最后两个链接坏了。
    • 最后两个链接仍然断开
    【解决方案2】:

    此函数将读取 JPEG 属性

    function jpegProps(data) {          // data is an array of bytes
        var off = 0;
        while(off<data.length) {
            while(data[off]==0xff) off++;
            var mrkr = data[off];  off++;
            
            if(mrkr==0xd8) continue;    // SOI
            if(mrkr==0xd9) break;       // EOI
            if(0xd0<=mrkr && mrkr<=0xd7) continue;
            if(mrkr==0x01) continue;    // TEM
            
            var len = (data[off]<<8) | data[off+1];  off+=2;  
            
            if(mrkr==0xc0) return {
                bpc : data[off],     // precission (bits per channel)
                h   : (data[off+1]<<8) | data[off+2],
                w   : (data[off+3]<<8) | data[off+4],
                cps : data[off+5]    // number of color components
            }
            off+=len-2;
        }
    }
    

     

    【讨论】:

    • 简短而完美的解决方案。
    【解决方案3】:

    我已将 CPP 代码从最佳答案转换为 python 脚本。

    """
    Source: https://stackoverflow.com/questions/2517854/getting-image-size-of-jpeg-from-its-binary#:~:text=The%20header%20of%20a%20JPEG,Of%20Frame%2C%20type%20N).
    """
    def get_jpeg_size(data):
       """
       Gets the JPEG size from the array of data passed to the function, file reference: http:#www.obrador.com/essentialjpeg/headerinfo.htm
       """
       data_size=len(data)
       #Check for valid JPEG image
       i=0   # Keeps track of the position within the file
       if(data[i] == 0xFF and data[i+1] == 0xD8 and data[i+2] == 0xFF and data[i+3] == 0xE0): 
       # Check for valid JPEG header (null terminated JFIF)
          i += 4
          if(data[i+2] == ord('J') and data[i+3] == ord('F') and data[i+4] == ord('I') and data[i+5] == ord('F') and data[i+6] == 0x00):
             #Retrieve the block length of the first block since the first block will not contain the size of file
             block_length = data[i] * 256 + data[i+1]
             while (i<data_size):
                i+=block_length               #Increase the file index to get to the next block
                if(i >= data_size): return False;   #Check to protect against segmentation faults
                if(data[i] != 0xFF): return False;   #Check that we are truly at the start of another block
                if(data[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]
                   height = data[i+5]*256 + data[i+6];
                   width = data[i+7]*256 + data[i+8];
                   return height, width
                else:
                   i+=2;                              #Skip the block marker
                   block_length = data[i] * 256 + data[i+1]   #Go to the next block
             return False                   #If this point is reached then no size was found
          else:
             return False                  #Not a valid JFIF string
       else:
          return False                     #Not a valid SOI header
    
    
    
    
    with open('path/to/file.jpg','rb') as handle:
       data = handle.read()
    
    h, w = get_jpeg_size(data)
    print(s)
    

    【讨论】:

      【解决方案4】:

      这就是我使用 js 实现它的方式。您要查找的标记是 Sofn 标记,伪代码基本上是:

      • 从第一个字节开始
      • 段的开头始终是FF,后跟另一个指示标记类型的字节(这两个字节称为标记)
      • 如果其他字节是01D1D9,则该段中没有数据,因此继续下一段
      • 如果该标记是 C0C2(或任何其他 Cn,代码的 cmets 中有更多详细信息),那就是您正在寻找的 Sofn 标记
        • 标记后的以下字节将分别为P(1字节)、L(2字节)、高度(2字节)、宽度(2字节)
      • 否则,接下来的两个字节将是长度属性(整个段的长度,不包括标记,2个字节),使用它跳到下一个段
      • 重复直到找到 Sofn 标记
      function getJpgSize(hexArr) {
        let i = 0;
        let marker = '';
      
        while (i < hexArr.length) {
          //ff always start a marker,
          //something's really wrong if the first btye isn't ff
          if (hexArr[i] !== 'ff') {
            console.log(i);
            throw new Error('aaaaaaa');
          }
      
          //get the second byte of the marker, which indicates the marker type
          marker = hexArr[++i];
      
          //these are segments that don't have any data stored in it, thus only 2 bytes
          //01 and D1 through D9
          if (marker === '01' || (!isNaN(parseInt(marker[1])) && marker[0] === 'd')) {
            i++;
            continue;
          }
      
          /*
          sofn marker: https://www.w3.org/Graphics/JPEG/itu-t81.pdf pg 36
            INFORMATION TECHNOLOGY –
            DIGITAL COMPRESSION AND CODING
            OF CONTINUOUS-TONE STILL IMAGES –
            REQUIREMENTS AND GUIDELINES
      
          basically, sofn (start of frame, type n) segment contains information
          about the characteristics of the jpg
      
          the marker is followed by:
            - Lf [frame header length], two bytes
            - P [sample precision], one byte
            - Y [number of lines in the src img], two bytes, which is essentially the height
            - X [number of samples per line], two bytes, which is essentially the width 
            ... [other parameters]
      
          sofn marker codes: https://www.digicamsoft.com/itu/itu-t81-36.html
          apparently there are other sofn markers but these two the most common ones
          */
          if (marker === 'c0' || marker === 'c2') {
            break;
          }
          //2 bytes specifying length of the segment (length excludes marker)
          //jumps to the next seg
          i += parseInt(hexArr.slice(i + 1, i + 3).join(''), 16) + 1;
        }
        const size = {
          height: parseInt(hexArr.slice(i + 4, i + 6).join(''), 16),
          width: parseInt(hexArr.slice(i + 6, i + 8).join(''), 16),
        };
        return size;
      }
      

      【讨论】:

        【解决方案5】:

        如果您使用的是 linux 系统并且手头有 PHP,则此 php 脚本的变体可能会产生您正在寻找的内容:

        #! /usr/bin/php -q
        <?php
        
        if (file_exists($argv[1]) ) {
        
            $targetfile = $argv[1];
        
            // get info on uploaded file residing in the /var/tmp directory:
            $safefile       = escapeshellcmd($targetfile);
            $getinfo        = `/usr/bin/identify $safefile`;
            $imginfo        = preg_split("/\s+/",$getinfo);
            $ftype          = strtolower($imginfo[1]);
            $fsize          = $imginfo[2];
        
            switch($fsize) {
                case 0:
                    print "FAILED\n";
                    break;
                default:
                    print $safefile.'|'.$ftype.'|'.$fsize."|\n";
            }
        }
        
        // eof
        

        主机> imageinfo 009140_DJI_0007.JPG

        009140_DJI_0007.JPG|jpeg|4000x3000|

        (以管道分隔格式输出文件名、文件类型、文件尺寸)

        来自手册页:

        有关“识别”命令的更多信息,请将您的浏览器指向 [...] http://www.imagemagick.org/script/identify.php

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-07-04
          • 1970-01-01
          • 1970-01-01
          • 2017-03-18
          • 1970-01-01
          • 1970-01-01
          • 2011-10-18
          相关资源
          最近更新 更多