Forma rápida de obtener dimensiones de imagen (no tamaño de archivo)

138

Estoy buscando una forma rápida de obtener la altura y el ancho de una imagen en píxeles. Debe manejar al menos JPG, PNG y TIFF, pero cuanto más mejor. Destaco rápido porque mis imágenes son bastante grandes (hasta 250 MB) y toma mucho tiempo obtener el tamaño con ImageMagick identifyporque obviamente primero lee las imágenes como un todo.

Preferiblemente, busco una forma que funcione bien en Ruby, o incluso en Rails 3.

Conozco la teoría (varios formatos de imagen, sus encabezados y sus diferencias, etc.). De hecho, solicito algún tipo de biblioteca que pueda resolver mi problema de una manera bastante genérica.

Acabo de encontrar el tamaño de imagen que parece prometedor, aunque el desarrollo parece estar muerto.

dAnjou
fuente
8
Esto no parece ser cierto para las nuevas versiones de ImageMagick. Usando ImageMagick 6.5.4-7, he confirmado que la identificación (al menos para TIF y PNG) solo lee el encabezado (hasta 60 KB) y funciona muy rápido, incluso para imágenes de 335 MB.
coderforlife

Respuestas:

195
  • El filecomando imprime las dimensiones para varios formatos de imagen (por ejemplo, PNG, GIF, JPEG; versiones recientes también PPM, WEBP), y solo lee el encabezado.

  • El identifycomando (de ImageMagick) imprime mucha información de imagen para una amplia variedad de imágenes. Parece limitarse a leer la parte del encabezado (ver comentarios). También tiene una salida unificada que filelamentablemente carece.

  • exiv2le brinda dimensiones para muchos formatos, incluidos JPEG, TIFF, PNG, GIF, WEBP, incluso si no hay un encabezado EXIF ​​presente. Sin embargo, no está claro si lee toda la información para eso. Consulte la página de manual de exiv2 para ver todos los formatos de imagen compatibles.

  • head -n1 le dará las dimensiones para los formatos PPM, PGM.

Para los formatos populares en la web, tanto exiv2y identifyhará el trabajo. Dependiendo del caso de uso, es posible que deba escribir su propio script que combine / analice los resultados de varias herramientas.

ypnos
fuente
3
He realizado algunas pruebas con el comando de identificación ImageMagick, usando strace para grabar llamadas abiertas / leídas / mmap / close para ver cuántos datos se leyeron de la imagen identificada. Depende un poco del tipo de archivo y el tamaño del archivo, pero estaba obteniendo una lectura de 20-60 KB mediante "identificación" para imágenes de 5-335 MB (también probé con "convertir" que mostraba que todos los bytes se leían). Parece que "identificar" es una buena opción aquí (ya que admite todos los formatos populares y lee solo el encabezado).
coderforlife
1
Creo que exiv2 también hace PNG.
chx
¿Alguna forma de analizar fácilmente los comandos de archivo? Identificar es genial pero no funciona con archivos WebP tristemente
Brian Leishman
Identificar qué trabajo con WebP, y ImageMagick tiene soporte para WebP durante años. Tal vez podría obtener una actualización?
ypnos
32

No estoy seguro de que tenga instalado php, pero esta función PHP es bastante útil

 php -r "print_r(getimagesize('http://www.google.com/images/logos/ps_logo2.png'));"
ajreal
fuente
1
Esto es mucho más rápido que "identificar". Buen enfoque. Gracias.
souravb
19

Puede usar la función de identificación de ImageMagick . Así es como lo haces en bash (Nota $ 0 es la ruta de la imagen):

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

Y esto también oculta cualquier mensaje de error potencial. Las implementaciones modernas de identifysolo leen el encabezado, no toda la imagen, por lo que es rápido. Sin embargo, no estoy seguro de cómo se compara con otros métodos.

James L.
fuente
2
Creo que es mucho más eficiente de esta manera: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 o WMF)

Aquí para dos formatos PNG y JPG.

Mi código es de una clase diseñada para mi uso, puede editar según sus necesidades.

Por favor revise estas funciones / método usando 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);
  }

Usando el código 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

Ahora estas funciones / método usando 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));
  }

Usando el código 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]
joseluisbz
fuente
Veo que está utilizando matrices para argumentos como un truco para obtener ref/ outparámetros en Java: ¿se considera la mejor práctica?
Dai
Esta respuesta es muy antigua, ahora no estoy dispuesto a actualizar (me olvido de muchas cosas y no tengo tiempo), pero puedes verificar el código y editarlo.
joseluisbz
joseluisbz.wordpress.com/2013/07/26/… (explicación de WMF)
joseluisbz
Para este ejemplo, recomiendo implementar una nueva clase con 3 campos, Formato, Alto y Ancho, devolviendo una instancia de esta clase.
joseluisbz
1

Supongo que son las dimensiones en píxeles que desea (ancho y alto).

Creo que la mayoría de los formatos de archivo tienen información de encabezado que define las dimensiones, de modo que el software que lee el archivo puede saber cuánto espacio debe reservar antes de comenzar a leer el archivo. Algunos formatos de archivo de tipo "sin formato" podrían ser solo una secuencia de bytes con algún byte de "fin de línea" al final de cada fila horizontal de píxeles (en cuyo caso el software debe leer la primera línea y dividir el tamaño de la secuencia de bytes por la longitud de la línea para obtener la altura).

No creo que pueda hacer esto de ninguna manera "genérica", ya que necesita comprender el formato del archivo (o usar una biblioteca, por supuesto) para saber cómo leerlo. Probablemente pueda encontrar algún código que en la mayoría de los casos proporcione una estimación aproximada de las dimensiones sin leer el archivo completo, pero creo que algunos tipos de archivos pueden requerir que lea todo el archivo para asegurarse de qué dimensiones tiene realmente. Espero que la mayoría de los formatos de imagen centrados en la web tengan un encabezado con dicha información para que el navegador pueda crear las dimensiones del cuadro antes de que se cargue toda la imagen.

Supongo que una buena biblioteca tendría algunos métodos para obtener las dimensiones de los archivos que maneja, y que esos métodos se implementarían de la manera más eficiente posible.

Actualización : imageinfo parece que hace lo que quieres. (No lo he probado)

Stein G. Strindhaug
fuente
Esa herramienta funciona tan rápido como la necesito;). Veré si puedo usarlo correctamente.
dAnjou
0

Si tiene información EXIF ​​en las imágenes, puede leer el encabezado EXIF.

Georgi
fuente
Desafortunadamente, no sé qué tipo de imágenes habrá y si tienen datos EXIF.
dAnjou
3
¿Cuántas de tus imágenes TIENEN esa información? Tal vez si el 90% de ellos tienen datos EXIF, entonces la lentitud de usar ImageMagick en el otro 10% será aceptable.
Andy Lester
¿Por qué esta respuesta tiene votos negativos? Es una respuesta válida a la pregunta y puede ser exactamente lo que está buscando el OP u otra persona.
Will Sheppard
0

-ping es una opción que parece haber introducido para ese propósito.

Sin embargo, a partir de ImageMagick 6.7.7 no observo la desaceleración incluso para todos los archivos grandes, por ejemplo:

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

¿Puedes producir una imagen de entrada de ejemplo para la que todavía es lenta?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
0

tldr: el archivo "nombre de imagen" servirá

funciona con webp, todos los formatos jpg (jpeg, jpg200, ..),

La salida de muestra se parece a

Datos de imagen JPEG, estándar JFIF 1.02, relación de aspecto, densidad 1x1, longitud del segmento 16, línea base, precisión 8, 650x400, cuadros 3

cargue la salida del archivo a una lista de python y use el 4º campo en la lista.

FYI, optimizó alrededor de 18000+ imágenes para reducir el tráfico de red.

mj-ek
fuente