快速获取图像尺寸(而不是文件大小)的方法


138

我正在寻找一种快速的方法来获取图像的高度和宽度(以像素为单位)。它应至少处理JPG,PNG和TIFF,但越好。我强调快速是因为我的图像很大(最大为250 MB),并且用ImageMagick的尺寸获取图像的时间太长了,identify因为它显然首先读取了整个图像。

最好,我正在寻找一种在Ruby甚至Rails 3中都能正常工作的方法。

我知道一些理论知识(各种图像格式,它们的标题和它们的区别,等等)。确实,我要求一种可以以相当通用的方式解决我的问题的库。

我只是发现了尽管发展似乎已经停滞,但看起来很有希望的图像尺寸


8
对于新版本的ImageMagick,似乎并非如此。使用ImageMagick 6.5.4-7,我已经确认,identify(至少对于TIF和PNG)仅读取标头(最大60KB),并且即使对于335MB的图像也可以非常快速地工作。
coderforlife 2014年

Answers:


195
  • file命令将打印几种图像格式(例如PNG,GIF,JPEG;最新版本也为PPM,WEBP)的尺寸,并且仅读取标头。

  • identify命令(来自ImageMagick)可打印大量图像信息,以显示各种图像。似乎只能阅读标题部分(请参见注释)。file可悲的是,它还具有统一的输出。

  • exiv2即使没有EXIF标头,也可以为您提供多种格式的尺寸,包括JPEG,TIFF,PNG,GIF,WEBP。目前尚不清楚是否读取全部数据。有关所有受支持的图像格式,请参见exiv2的手册页。

  • head -n1 将为您提供PPM,PGM格式的尺寸。

对于网络上流行的格式,两者exiv2identify都会起作用。根据用例,您可能需要编写自己的脚本,以结合/解析多个工具的输出。


3
我已经使用ImageMagick标识命令进行了一些测试,使用strace记录了open / read / mmap / close调用以查看从标识的图像中读取了多少数据。它确实取决于文件类型和文件大小,但是对于5-335 MB的图像,我通过“标识”读取了20-60 KB的图像(我还针对显示所有字节被读取的“转换”进行了测试)。因此,看起来“ identify”在这里是一个不错的选择(因为它支持所有流行的格式并且仅读取标头)。
coderforlife 2014年

1
我认为exiv2也可以使用PNG。
chx

有什么方法可以轻松解析该文件命令输出?识别很棒,但可悲的是它不能与WebP文件一起使用
Brian Leishman,

Identify 确实可以与WebP一起使用,并且ImageMagick多年来一直支持WebP。也许您可以获得更新?
ypnos

32

我不确定您是否已安装php,但是此PHP函数非常方便

 php -r "print_r(getimagesize('http://www.google.com/images/logos/ps_logo2.png'));"

1
这比“识别”要快得多。好方法。谢谢。
souravb

19

您可以使用ImageMagick的识别功能。这是在bash中进行操作的方式(注意$ 0是图像的路径):

width=$(identify -format "%w" "$0")> /dev/null
height=$(identify -format "%h" "$0")> /dev/null

而且这也隐藏了任何潜在的错误消息。现代的实现identify只读取标头,而不读取整个图像,因此速度很快。虽然不确定与其他方法相比。


2
我相信这样会更有效:read width height < <(identify -format "%w %h" "${1}")
Cromax,

5

https://joseluisbz.wordpress.com/2013/08/06/obtaining-size-or-dimension-of-images/(BMP、PNG、GIF、JPG、TIF或WMF)

这里有两种格式PNG和JPG。

我的代码来自我设计的类,您可以根据需要进行编辑。

请使用PHP检查以下功能/方法:

  public function ByteStreamImageString($ByteStream,&$Formato,&$Alto,&$Ancho) {
    $Alto = 0;
    $Ancho = 0;
    $Formato = -1;
    $this->HexImageString = "Error";
    if (ord($ByteStream[0])==137 && ord($ByteStream[1])==80 && ord($ByteStream[2])==78){
      $Formato = 1; //PNG
      $Alto = $this->Byte2PosInt($ByteStream[22],$ByteStream[23]);
      $Ancho = $this->Byte2PosInt($ByteStream[18],$ByteStream[19]);
    }
    if (ord($ByteStream[0])==255 && ord($ByteStream[1])==216
        && ord($ByteStream[2])==255 && ord($ByteStream[3])==224){
      $Formato = 2; //JPG
      $PosJPG = 2;
      while ($PosJPG<strlen($ByteStream)){
        if (sprintf("%02X%02X", ord($ByteStream[$PosJPG+0]),ord($ByteStream[$PosJPG+1]))=="FFC0"){
          $Alto = $this->Byte2PosInt($ByteStream[$PosJPG+5],$ByteStream[$PosJPG+6]);
          $Ancho = $this->Byte2PosInt($ByteStream[$PosJPG+7],$ByteStream[$PosJPG+8]);
        }
        $PosJPG = $PosJPG+2+$this->Byte2PosInt($ByteStream[$PosJPG+2],$ByteStream[$PosJPG+3]);
      }
    }
    if ($Formato > 0){
      $this->HexImageString = "";
      $Salto = 0;
      for ($i=0;$i < strlen($ByteStream); $i++){
        $Salto++;
        $this->HexImageString .= sprintf("%02x", ord($ByteStream[$i]));
        if ($Salto==64){
          $this->HexImageString .= "\n";
          $Salto = 0;
        }
      }
    }
  }


  private function Byte2PosInt($Byte08,$Byte00) {
    return ((ord($Byte08) & 0xFF) << 8)|((ord($Byte00) & 0xFF) << 0);
  }

使用PHP代码:

      $iFormato = NULL;//Format PNG or JPG
      $iAlto = NULL; //High
      $iAncho = NULL;//Wide
      ByteStreamImageString($ImageJPG,$iFormato,$iAlto,$iAncho);//The Dimensions will stored in  iFormato,iAlto,iAncho

现在,这些功能/方法使用JAVA

  private void ByteStreamImageString(byte[] ByteStream,int[] Frmt,int[] High,int[] Wide) {
    High[0] = 0;
    Wide[0] = 0;
    Frmt[0] = -1;
    this.HexImageString = "Error";
    if ((int)(ByteStream[0]&0xFF)==137 && (int)(ByteStream[1]&0xFF)==80 &&(int)(ByteStream[2]&0xFF)==78){
      Frmt[0] = 1; //PNG
      High[0] = this.Byte2PosInt(ByteStream[22],ByteStream[23]);
      Wide[0] = this.Byte2PosInt(ByteStream[18],ByteStream[19]);
    }
    if ((int)(ByteStream[0]&0xFF)==255 && (int)(ByteStream[1]&0xFF)==216
        &&(int)(ByteStream[2]&0xFF)==255 && (int)(ByteStream[3]&0xFF)==224){
      Frmt[0] = 2; //JPG
      int PosJPG = 2;
      while (PosJPG<ByteStream.length){
        if (String.format("%02X%02X", ByteStream[PosJPG+0],ByteStream[PosJPG+1]).equals("FFC0")){
          High[0] = this.Byte2PosInt(ByteStream[PosJPG+5],ByteStream[PosJPG+6]);
          Wide[0] = this.Byte2PosInt(ByteStream[PosJPG+7],ByteStream[PosJPG+8]);
        }
        PosJPG = PosJPG+2+this.Byte2PosInt(ByteStream[PosJPG+2],ByteStream[PosJPG+3]);
      }
    }
    if (Frmt[0] > 0){
      this.HexImageString = "";
      int Salto = 0;
      for (int i=0;i < ByteStream.length; i++){
        Salto++;
        this.HexImageString += String.format("%02x", ByteStream[i]);
        if (Salto==64){
          this.HexImageString += "\n";
          Salto = 0;
        }
      }
    }
  }


  private Integer Byte2PosInt(byte Byte08, byte Byte00) {
    return new Integer (((Byte08 & 0xFF) << 8)|((Byte00 & 0xFF) << 0));
  }

使用Java代码:

        int[] iFormato = new int[1]; //Format PNG or JPG
        int[] iAlto = new int[1]; //High
        int[] iAncho = new int[1]; //Wide
        ByteStreamImageString(ImageJPG,iFormato,iAlto,iAncho); //The Dimensions will stored in  iFormato[0],iAlto[0],iAncho[0]

我看到您正在使用数组作为参数来获取Java中的ref/ out参数-这被认为是最佳实践吗?

这个答案很旧,现在我不愿意更新(我忘记了很多事情,但是我没有时间),但是您可以检查代码并进行编辑。
joseluisbz


对于此示例,我建议实现一个具有3个字段的新类,即Format,High和Width,并返回该类的实例。
joseluisbz

1

我想这是您想要的像素尺寸(宽度和高度)吗?

我认为大多数文件格式都有一些定义尺寸的标头信息,以便读取文件的软件可以知道在开始读取文件之前必须保留多少空间。某些“原始”类型的文件格式可能只是字节流,而每个水平像素行的末尾都有一些“行尾”字节(在这种情况下,软件必须读取第一行并划分字节流的大小通过线长来获得高度)。

我认为您无法以任何“通用”方式实现此目的,因为您需要了解文件格式(或者当然要使用库)才能知道如何读取它。您可能会找到一些代码,这些代码在大多数情况下不会读取整个文件就能粗略估计尺寸,但是我认为某些文件类型可能需要您读取整个文件才能确定其真正具有的尺寸。我希望大多数以Web为中心的图像格式都具有带有此类信息的标头,以便浏览器可以在加载整个图像之前创建框尺寸。

我猜一个好的库将提供一些方法来获取其处理的文件的尺寸,并且这些方法将尽可能高效地实现。

更新imageinfo似乎可以满足您的要求。(尚未测试)


该工具可以按我需要的速度工作;)。我看看是否可以正确使用它。
dAnjou 2011年

0

如果图像中有EXIF信息,则只需阅读EXIF标头即可。


不幸的是,我不知道会有什么样的图像以及它们是否具有EXIF数据。
dAnjou 2011年

3
如何使你的图片很多DO方面的资料?也许如果其中90%具有EXIF数据,则可以接受在其他10%上使用ImageMagick的缓慢性。
安迪·莱斯特

为什么这个答案有否决权?这是对这个问题的有效答案,很可能正是OP或其他人正在寻找的东西。
Will Sheppard

0

-ping是为此目的引入的一个选项。

但是从ImageMagick 6.7.7开始,即使对于每个大文件,我也没有观察到速度变慢,例如:

head -c 100000000 /dev/urandom > f.gray
# I don't recommend that you run this command as it eats a lot of memory.
convert -depth 8 -size 20000x10000 f.gray f.png
identify f.png

您能否生成示例输入图像,但该图像仍然很慢?


0

tldr:文件“ imagename”将

适用于webp,所有jpg格式(jpeg,jpg200等),

样本输出看起来像

JPEG图像数据,JFIF标准1.02,宽高比,密度1x1,段长度16,基线,精度8、650x400,帧3

将文件的输出加载到python列表中,并使用列表中的第4个字段。

仅供参考,确实优化了大约18000多个图像,以减少网络流量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.