Maneira rápida de obter as dimensões da imagem (não o tamanho do arquivo)


138

Estou procurando uma maneira rápida de obter a altura e largura de uma imagem em pixels. Ele deve lidar com pelo menos JPG, PNG e TIFF, mas quanto mais, melhor. Enfatizo rapidamente porque minhas imagens são muito grandes (até 250 MB) e leva muito tempo para obter o tamanho do ImageMagick's, identifyporque ele obviamente lê as imagens como um todo primeiro.

De preferência, procuro uma maneira que funcione bem em Ruby, ou mesmo no Rails 3.

Conheço o material da teoria (vários formatos de imagem, seus cabeçalhos e diferenças, e assim por diante). Na verdade, peço algum tipo de biblioteca que possa resolver meu problema de maneira bastante genérica.

Acabei de encontrar um tamanho de imagem que parece promissor, embora o desenvolvimento pareça estar morto.


8
Isso não parece ser verdade para as novas versões do ImageMagick. Usando o ImageMagick 6.5.4-7 Confirmei que o identificador (pelo menos para TIF e PNG) lê apenas o cabeçalho (até 60 KB) e funciona muito rápido, mesmo para imagens de 335 MB.
Coderforlife 12/02/2014

Respostas:


195
  • O filecomando imprime as dimensões para vários formatos de imagem (por exemplo, PNG, GIF, JPEG; versões recentes também PPM, WEBP) e lê apenas o cabeçalho.

  • O identifycomando (do ImageMagick) imprime muitas informações de imagem para uma ampla variedade de imagens. Parece restringir-se a ler a parte do cabeçalho (ver comentários). Ele também tem uma saída unificada que fileinfelizmente não tem.

  • exiv2fornece dimensões para vários formatos, incluindo JPEG, TIFF, PNG, GIF, WEBP, mesmo que não exista um cabeçalho EXIF. Não está claro se ele lê todos os dados para isso. Veja a página de manual do exiv2 para todos os formatos de imagem suportados.

  • head -n1 fornecerá as dimensões para os formatos PPM, PGM.

Para formatos populares na Web, ambos exiv2e identifyfarão o trabalho. Dependendo do caso de uso, pode ser necessário escrever seu próprio script que combine / analise as saídas de várias ferramentas.


3
Fiz alguns testes com o comando ImageMagick, usando o strace para gravar chamadas de abertura / leitura / mmap / fechamento para ver quantos dados foram lidos na imagem identificada. Depende um pouco do tipo e tamanho do arquivo, mas eu estava obtendo 20-60 KB de leitura por "identity" para imagens de 5-335 MB (também testei em "convert" que mostrava todos os bytes sendo lidos). Portanto, parece que "identificar" é uma boa opção aqui (já que suporta todos os formatos populares e lê apenas o cabeçalho).
Coderforlife

1
Acho exiv2 também faz PNG.
chx 20/08/14

Alguma maneira de analisar esse arquivo de comandos com facilidade? Identificar é grande, mas ele não funciona com WebP arquivos infelizmente
Brian Leishman

Identificar faz o trabalho com WebP, e ImageMagick tem suporte para WebP durante anos. Talvez você possa receber uma atualização?
ypnos

32

Não sei se você tem o php instalado, mas essa função PHP é bastante útil

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

1
Isso é muito mais rápido que "identificar". Boa abordagem. Obrigado.
souravb

19

Você pode usar a função de identificação do ImageMagick . Aqui está como você faz isso no bash (nota $ 0 é o caminho da imagem):

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

E isso também oculta qualquer possível mensagem de erro. Implementações modernas de identifyapenas ler o cabeçalho, não a imagem inteira, por isso é rápido. Não tenho certeza de como ele se compara a outros métodos.


2
Eu acredito que é muito mais eficiente assim:read width height < <(identify -format "%w %h" "${1}")
Cromax 29/04

5

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

Aqui para dois formatos PNG e JPG.

Meu código é de uma classe projetada para meu uso, você pode editar de acordo com suas necessidades.

Por favor, verifique estas funções / métodos 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 o 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

Agora, estas funções / métodos 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 o 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]

Vejo que você está usando matrizes para argumentos como um hack para obter ref/ outparâmetros em Java - isso é considerado uma prática recomendada?
Dai

Esta resposta é muito antiga, agora não estou disposto a atualizar (esqueço muitas coisas e não tenho tempo), mas você pode verificar o código e editá-lo.
Jseluisbz


Para este exemplo, recomendo implementar uma nova classe com 3 campos, Format, High e Width, retornando uma instância dessa classe.
Jseluisbz #

1

São as dimensões de pixel que você deseja (largura e altura), presumo?

Eu acho que a maioria dos formatos de arquivo tem algumas informações de cabeçalho que definem as dimensões, para que o software que está lendo o arquivo saiba quanto espaço deve reservar antes de começar a ler o arquivo. Alguns formatos de arquivo do tipo "bruto" podem ser apenas um fluxo de bytes com algum byte "final de linha" no final de cada linha horizontal de pixels (nesse caso, o software deve ler a primeira linha e dividir o tamanho do fluxo de bytes) pelo comprimento da linha para obter a altura).

Eu não acho que você possa fazer isso de maneira "genérica", pois você precisa entender o formato do arquivo (ou usar uma biblioteca, é claro) para saber como lê-lo. Provavelmente, você pode encontrar algum código que, na maioria dos casos, forneça uma estimativa aproximada das dimensões sem ler o arquivo inteiro, mas acho que alguns tipos de arquivos podem exigir que você leia o arquivo inteiro para ter certeza de que dimensões ele realmente possui. Espero que a maioria dos formatos de imagem centrados na Web possua um cabeçalho com essas informações, para que o navegador possa criar as dimensões da caixa antes que toda a imagem seja carregada.

Eu acho que uma boa biblioteca teria alguns métodos para obter as dimensões dos arquivos que manipula, e que esses métodos seriam implementados da maneira mais eficiente possível.

Atualização : imageinfo parece que faz o que você deseja. (Não testei)


Essa ferramenta funciona tão rápido quanto eu preciso;). Vou ver se consigo usá-lo corretamente.
dAnjou

0

Se você tiver informações EXIF ​​nas imagens, basta ler o cabeçalho EXIF.


Infelizmente, não sei que tipo de imagens haverá e se elas têm dados EXIF.
dAnjou

3
Quantos de suas imagens NÃO tem essa informação? Talvez se 90% deles tiverem dados EXIF, a lentidão do uso do ImageMagick nos outros 10% será aceitável.
Andy Lester

Por que esta resposta tem votos negativos? É uma resposta válida para a pergunta e pode ser exatamente o que o OP ou outra pessoa está procurando.
Will Sheppard

0

-ping é uma opção que parece ter sido introduzida para esse fim.

No entanto, a partir do ImageMagick 6.7.7, não observo lentidão nem para todos os arquivos grandes, por exemplo:

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

Você pode produzir uma imagem de entrada de exemplo para a qual ainda é lenta?


0

tldr: o arquivo "imagename" fará

funciona com webp, todos os formatos jpg (jpeg, jpg200, ..),

Saída de amostra parece

Dados de imagem JPEG, padrão JFIF 1.02, proporção, densidade 1x1, comprimento do segmento 16, linha de base, precisão 8, 650x400, quadros 3

carregue a saída do arquivo em uma lista python e use o 4º campo na lista.

Para sua informação, otimizou mais de 18.000 imagens para reduzir o tráfego de rede.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.