Fast way to get image dimensions (not filesize)

LinuxImage

Linux Problem Overview


I'm looking for a fast way to get the height and width of an image in pixels. It should handle at least JPG, PNG and TIFF, but the more the better. I emphasize fast because my images are quite big (up to 250 MB) and it takes soooo long to get the size with ImageMagick's identify because it obviously reads the images as a whole first.

Preferably, I look for a way that works well in Ruby, or even in Rails 3.

I know the theory stuff (various image formats, their headers and their differences, and so on). Indeed, I ask for some kind of library that can resolve my issue in a fairly generic way.

I just found imagesize which looks promising although development seems to be dead.

Linux Solutions


Solution 1 - Linux

  • The file command prints the dimensions for several image formats (e.g. PNG, GIF, JPEG; recent versions also PPM, WEBP), and does only read the header.

  • The identify command (from ImageMagick) prints lots of image information for a wide variety of images. It seems to restrain itself to reading the header portion (see comments). It also uses a unified format which file sadly lacks.

  • exiv2 gives you dimensions for many formats, including JPEG, TIFF, PNG, GIF, WEBP, even if no EXIF header present. It is unclear if it reads the whole data for that though. See the manpage of exiv2 for all supported image formats.

  • head -n1 will give you the dimensions for PPM, PGM formats.

For formats popular on the web, both exiv2 and identify will do the job. Depending on the use-case you may need to write your own script that combines/parses outputs of several tools.

Solution 2 - Linux

I not sure you have php installed, but this PHP function is pretty handy

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

Solution 3 - Linux

You can use ImageMagick's identify function. Here's how you do it in bash (Note $0 is the image's path):

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

And this also hides any potential error messages. Modern implementations of identify only read the header, not the whole image, so it is fast. Not sure how it compares to other methods though.

Solution 4 - Linux

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

Here for two formats PNG and JPG.

My code is from a class designed to my use, you can to edit according to your needs.

Please check these functions/method using 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);
  }

Using the PHP Code:

      $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

Now these functions/method using 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));
  }

Using the Java code:

        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]

Solution 5 - Linux

Imagemagick -ping option

It seems to have been introduced for that purpose.

However as of ImageMagick 6.7.7 I don't observe slowdown even for every large files, e.g.:

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

Can you produce an example input image for which it is still slow?

See also: https://stackoverflow.com/questions/1555509/can-imagemagick-return-the-image-size

Solution 6 - Linux

It's the pixel dimensions you want (width and height), I assume?

I'd think that most file formats have some header info defining the dimensions, so that the software reading the file can know how much room it must reserve before starting to read the file. Some "raw" type file formats might just be a stream of bytes with some "end of line" byte at the end of each horizontal row of pixels (in which case the software must read the first line and divide the size of the byte stream by the line length to get the height).

I don't think you can make this in any "generic" way, as you need to understand the file format (or use a library of course) in order to know how to read it. You can probably find some code that will in most cases give a rough estimate of the dimensions without reading the whole file, but I think some filetypes may require you to read the whole file to be sure what dimensions it really have. I expect that most web centric image formats have a header with such info so that the browser can create the box dimensions before the whole image is loaded.

I'd guess a good library would have some methods to get the dimensions of the files it handles, and that those methods would be implemented as efficient as possible.

Update: imageinfo seems like it does what you want. (Have not tested it)

Solution 7 - Linux

If you have EXIF information in the images, you can just read the EXIF header.

Solution 8 - Linux

tldr: file "imagename" will do

works with webp, all jpg formats (jpeg,jpg200,..),

Sample output looks like

> JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, > segment length 16, baseline, precision 8, 650x400, frames 3

load the output of file to a python list & use the 4'th field in the list.

FYI, did optimize around 18000+ images to cut down network traffic.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondAnjouView Question on Stackoverflow
Solution 1 - LinuxypnosView Answer on Stackoverflow
Solution 2 - LinuxajrealView Answer on Stackoverflow
Solution 3 - LinuxJames L.View Answer on Stackoverflow
Solution 4 - LinuxjoseluisbzView Answer on Stackoverflow
Solution 5 - LinuxCiro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 6 - LinuxStein G. StrindhaugView Answer on Stackoverflow
Solution 7 - LinuxGeorgiView Answer on Stackoverflow
Solution 8 - Linuxmj-ekView Answer on Stackoverflow