Bash: maneira mais rápida de determinar as dimensões da imagem do URL

8

Estou tentando descobrir um método realmente rápido na hora de determinar as dimensões de uma imagem.

Eu sei que eu poderia wget a imagem e, em seguida, use imagemagick para determinar a altura e largura da imagem. Estou preocupado que isso não seja a maneira mais rápida de fazê-lo.

Também estou preocupado em ter que instalar o imagemagick quando preciso apenas de um subconjunto muito pequeno de funcionalidade. Eu estou em um sistema embarcado que tem recursos muito limitados (CPU, RAM, armazenamento).

Alguma idéia?

    
por exvance 15.10.2013 / 19:12

5 respostas

13

Como você observou, não é necessário todo o pacote ImageMagick . Você só precisa de identify .

Você também precisará das bibliotecas dos links executáveis (e das bibliotecas às quais essas bibliotecas se vinculam).

> whereis identify
identify: /bin/identify /usr/bin/identify /usr/share/man/man1/identify.1.gz
> ldd /bin/identify

ldd mostrará uma lista. Quando fiz isso, incluí algumas X libs, libjpeg, etc. e duas bibliotecas claramente do pacote ImageMagick, libMagickCore e libMagickWand . Eles parecem estar ligados ao mesmo grupo de coisas, então se você tem isso, identify deve funcionar.

Você não precisa baixar uma imagem inteira para obter as dimensões, porque elas estão em um cabeçalho no início do arquivo e é isso que o identify observa. Por exemplo, aqui estou copiando os primeiros 4 kB de um jpeg completo para um novo arquivo:

dd if=real.jpg of=test.jpg bs=1024 count=4

4 kB deve ser mais do que suficiente para incluir o cabeçalho - tenho certeza de que você poderia fazê-lo com 1/4 desse valor. Agora:

>identify test.jpg 
test.jpg JPEG 893x558 893x558+0+0 8-bit DirectClass 4.1KB 0.000u 0:00.000

Essas são as dimensões corretas para real.jpg . Observe, no entanto, que o tamanho (4,1 KB) é o tamanho do arquivo truncado, uma vez que essa informação não é do cabeçalho da imagem.

Então: você só precisa baixar o primeiro kilobyte de cada imagem.

    
por 15.10.2013 / 19:37
11

Você pode usar curl para baixar partes da imagem. Tudo depende de quão robusto tem que ser. Um caso de teste pode ser o primeiro de 500 bytes. Parece funcionar muito para png e jpg , depois use identify ou algo parecido para verificar o tamanho.

curl -o 500-peek -r0-500 "http://example.net/some-image.png"

Editar:

Muito tempo desde que escrevi analisadores de imagem, mas pensei um pouco e atualizei parte da minha memória.

Eu suspeito que seja todo tipo de imagens que você deseja verificar (mas, novamente, talvez não). Vou descrever alguns dos mais comuns: PNG , JPEG (JFIF) e GIF .

PNG:

Estes são simples quando se trata de extração de tamanho. Um cabeçalho png armazena o tamanho dentro dos primeiros 24 bytes. Primeiro vem um cabeçalho fixo:

byte  value  description
   0  0x89   Bit-check. 0x89 has bit 7 set.
 1-3  PNG    The letters P,N and G
 4-5  \r\n   Newline check.
   6    ^z   MS-DOS won't print data beyond this using 'print'
   7    \n   *nix newline.

Em seguida, vêm pedaços através do arquivo. Eles consistem em um campo fixo de comprimento, tipo e soma de verificação. Além disso, uma seção opcional data do tamanho tamanho .

Felizmente, o primeiro pedaço é sempre um IHDR com esse layout:

byte  description
0-3   Image Width
4-7   Image Height
  8   Bits per sample or per palette index
...   ...

Por isso, temos tamanhos que são bytes 16-20 e 21-24. Você pode despejar os dados, por exemplo, hexdump:

hexdump -vn29 -e '"Bit-test: " /1 "%02x" "\n" "Magic   : " 3/1 "%_c" "\n" "DOS-EOL : " 2/1 "%02x" "\n" "DOS-EOF : " /1 "%02x" "\n" "NIX-EOL : " /1 "%02x" "\n" "Chunk Size: " 4/1 "%02u" "\n" "Chunk-type: " 4/1 "%_c" "\n" "Img-Width : " 4/1 "%02x" "\n" "Img-Height: " 4/1 "%02x" "\n" /1 "Depth : %u bit" "\n" /1 "Color : %u" "\n" /1 "Compr.: %u" "\n" /1 "Filter: %u" "\n" /1 "Interl: %u" "\n"' sample.png

Em uma máquina Big Endian / Motorola, também é possível imprimir os tamanhos diretamente por:

hexdump -s16 -n8 -e '1/4 "%u" "\n"' sample.png

No entanto, em Little Endian / Intel, não é assim tão fácil, e nem é muito portátil.

Por isso, podemos implementar um script bash + hexdump como em:

png_hex='16/1 "%02x" " " 4/1 "%02x" " " 4/1 "%02x" "\n"'
png_valid="89504e470d0a1a0a0000000d49484452"

function png_wh()
{
    read -r chunk1 img_w img_h<<<$(hexdump -vn24 -e "$png_hex" "$1")
    if [[ "$chunk1" != "$png_valid" ]]; then
        printf "Not valid PNG: \'%s'\n" "$1" >&2
        return 1
    fi
    printf "%10ux%-10u\t%s\n" "0x$img_w" "0x$img_h" "$1"
    return 0
}

if [[ "$1" == "-v" ]]; then verbose=1; shift; fi

while [[ "$1" ]]; do png_wh "$1"; shift; done

Mas isso não é diretamente eficiente. Embora exija um pedaço maior (75-100 bytes), identify é bem mais rápido. Ou escreva a rotina em p. C, o que seria mais rápido do que as chamadas da biblioteca.

JPEG:

Quando se trata de jpg , não é tão fácil. Ele também começa com um cabeçalho de assinatura , mas o bloco tamanho não está em um deslocamento fixo. Depois do cabeçalho:

 byte  value
 0-1   ffd8          SOI (Start Of Image)
 2-3   ffe0          JFIF marker
 4-5   <block-size>  Size of this block including this number
 6-10  JFIF
 0-1   ffc0          SOF marker
 2-3   <block-size>  Size of this block including this number
   4   <bits>        Sample precision.
 5-6   <Y-size>      Height
 7-8   <X-size>      Width
   9   <components>  Three for color baseline, one for grayscale.
... 11-12 <version> 13 ...

um novo bloco vem especificado por um marcador de dois bytes começando com 0xff . O que contém informações sobre dimensões tem o valor 0xffc0 , mas pode ser enterrado um pouco abaixo dos dados.

Em outras palavras, um ignora tamanho de bloco bytes, verifica marcador, pula tamanho de bloco bytes, lê marcador e assim por diante até que o correto apareça.

Quando encontrados, os tamanhos são armazenados por dois bytes cada no deslocamento 3 e 5 após marcador .

<byte>  <hex>   <value>
  0-2   474946  GIF  Magic
  3-5   383961  89a  Version (87a or 89a)
  6-7   6c01    364  Logical Screen Width
  8-9   d801    472  Logical Screen Height

Escreveu um programa C simples para verificar alguns arquivos e cerca de 10.000 imagens jpg, aproximadamente 50% tinham a informação de tamanho dentro dos primeiros 500 bytes, principalmente 50% entre ca. 100 e 200. O pior foi em torno de 80.000 bytes. Uma foto, enquanto falamos fotos:

GIF:

Emboragifgeralmentepossaterváriasimagensarmazenadasdentro,eletemumtamanhocanvasespecificadonocabeçalho,issoégrandeosuficienteparaabrigarasimagens.ÉtãofácilquantocomPNG,erequeratémesmofebre:10.Depoisdamágicaedaversão,encontramostamanhos.Exemplodeumaimagem364x472:

curl-o500-peek-r0-500"http://example.net/some-image.png"

Em outras palavras, você pode verificar os primeiros seis bytes para ver se é um gif e, em seguida, ler os próximos quatro para tamanhos.

Outros formatos:

Poderia ter continuado, mas acho que eu paro aqui por enquanto.

    
por 15.10.2013 / 19:40
1

Supõe que você tenha "identificar". Coloque isso em um script e chmod +x <scriptname> . Para executá-lo, digite <scriptname> picture.jpg e você terá a altura e a largura da imagem. As duas primeiras seções são para verificar se há uma imagem, em seguida, defini-lo como a variável IMAGE. A próxima seção é verificar se o arquivo está realmente lá. As últimas duas seções devem pegar as informações relevantes da saída "identificar" e exibi-las.

#!/bin/bash
if [[ "${#}" -ne "1" ]]
then
die "Usage: $0 <image>"
fi

IMAGE="${1}"

if [[ ! -f "${IMAGE}" ]]
then
die "File not found: ${IMAGE}"
fi

IMG_CHARS='identify "$1" | cut -f 3 -d' ''
WIDTH='echo $IMG_CHARS | cut -d'x' -f 1'
HEIGHT='echo $IMG_CHARS | cut -d'x' -f 2'

echo -e "W: ${WIDTH} H: ${HEIGHT}"
    
por 15.10.2013 / 19:34
0
mohsen@debian:~/codes/amlak/amlak/src$ file ~/Screenshot\ from\ 2013-07-10\ 01\:25\:34.png 
/home/mohsen/Screenshot from 2013-07-10 01:25:34.png: PNG image data, 1366 x 768, 8-bit/color RGB, non-interlaced

file command é instalado por padrão nos distors e depende apenas de:

Depends: libc6 (>= 2.4), libmagic1 (= 1:5.14-2), zlib1g (>= 1:1.1.4)

Eu acho que você pode instalá-lo facilmente para incorporados. Você acabou de escrever um regular expression para sua saída.

    
por 15.10.2013 / 19:22
0
mohsen@debian:~/codes/amlak/amlak/src$ php -r "print_r(getimagesize('file:///archives/Picture/12 farvardin/20120331_013.jpg'));"
Array
(
    [0] => 2560
    [1] => 1440
    [2] => 2
    [3] => width="2560" height="1440"
    [bits] => 8
    [channels] => 3
    [mime] => image/jpeg
)
mohsen@debian:~/codes/amlak/amlak/src$ php -r "print_r(getimagesize('file:///archives/Picture/12 farvardin/20120331_013.jpg'));" |egrep w
    [3] => width="2560" height="1440"
mohsen@debian:~/codes/amlak/amlak/src$ php -r "print_r(getimagesize('file:///archives/Picture/12 farvardin/20120331_013.jpg'));" |egrep w | awk {'print $3'}
width="2560"
mohsen@debian:~/codes/amlak/amlak/src$ php -r "print_r(getimagesize('file:///archives/Picture/12 farvardin/20120331_013.jpg'));" |egrep w | awk {'print $4'}
height="1440"

Você substitui file:// por http://

    
por 15.10.2013 / 19:34