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


8

Estou tentando descobrir um método muito rápido na determinação da dimensão das imagens.

Sei que poderia criar a imagem e usar a imagemagick para determinar a altura e a largura da imagem. Estou preocupado que essa possa não ser 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 funcionalidades. Estou em um sistema embarcado com recursos muito limitados (CPU, RAM, armazenamento).

Alguma ideia?


Quais tipos de imagem você precisa suportar?
Gilles 'SO- stop be evil'

Respostas:


13

Como você observa, você não precisa de todo o pacote ImageMagick . Você só precisa identify.

Você também precisará das bibliotecas às quais os links executáveis ​​estão vinculados (e as 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

lddirá mostrar uma lista. Quando eu fiz isso, ele incluiu algumas bibliotecas X, libjpeg etc. e duas bibliotecas claramente do pacote ImageMagick libMagickCoree libMagickWand. Eles parecem estar ligados ao mesmo monte de coisas; portanto, se você tiver isso, identifydeve funcionar.

Você não precisa fazer o download de uma imagem inteira para obter as dimensões, porque elas estão em um cabeçalho no início do arquivo e é isso que se identifyvê. 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 que você poderia fazê-lo com 1/4 dessa quantidade. 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, pois essas informações não são do cabeçalho da imagem.

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


12

Você pode usar curlpara baixar partes da imagem. Tudo depende de quão robusto deve ser. Um caso de teste pode ter os primeiros 500 bytes. Parece funcionar muito pnge jpg, em seguida, use identifyou algo semelhante para verificar o tamanho.

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

Editar:


Faz muito tempo que escrevi analisadores de imagens, mas pensei um pouco e atualizei parte da minha memória.

Eu suspeito que é todo o 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 pngcabeçalho armazena o tamanho nos 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 os pedaços do arquivo. Eles consistem em um campo fixo de comprimento, tipo e soma de verificação. Além disso, uma seção de dados opcional do tamanho do comprimento .

Felizmente, o primeiro pedaço é sempre um IHDRcom este layout:

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

Por isso, temos que os tamanhos são byte 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:

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

No entanto, em Little Endian / Intel, não é 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é bastante mais rápido. Ou escreva a rotina em, por exemplo, C, que seria mais rápido que as chamadas da biblioteca.


JPEG:

Quando se trata jpg, não é tão fácil. Também começa com um cabeçalho de assinatura , mas o pedaço de tamanho não está em um deslocamento fixo. Após o 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        ...
11-12  <version>
   13  ...

surge um novo bloco especificado por um marcador de dois bytes começando com 0xff. Aquele que contém informações sobre dimensões tem o valor, 0xffc0mas pode ser bastante escondido nos dados.

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

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

 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.

Escreveu um programa C simples para verificar alguns arquivos e cerca de 10.000 imagens jpg, aproximadamente 50% tinham as informações de tamanho nos primeiros 500 bytes, principalmente 50% entre ca. 100 e 200. O pior foi de cerca de 80.000 bytes. Uma imagem, como falamos fotos:

JFIF_SOF_graph


GIF:

Embora o gif normalmente possa ter várias imagens armazenadas, ele possui um tamanho de tela especificado no cabeçalho, mas é grande o suficiente para abrigar as imagens. É tão fácil quanto no PNG e requer bytes de febre: 10. Após a mágica e a versão, encontramos tamanhos. Exemplo de uma imagem de 364x472:

<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

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


Outros formatos:

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


1

Supõe que você tenha "identificação". Coloque isso em um script e chmod +x <scriptname>. Para executá-lo, digite <scriptname> picture.jpge você obterá a altura e a largura da imagem. As duas primeiras seções são para verificar se há uma imagem e depois configurá-la como a variável IMAGE. A próxima seção é garantir que o arquivo esteja realmente lá. As duas últimas 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}"

bom roteiro. no entanto, seria bom se você pudesse explicar o que ele faz (já que o Stack Exchange é sobre aprendizado).
strugee

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 em 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 incorporado. Você acabou de escrever um regular expressionpara sua saída.


2
filenão fornece dimensões para, por exemplo, .jpgarquivos.
Goldilocks

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://porhttp://


Não tenho certeza se o PHP é adequado para sistemas embarcados com poucos recursos. Além disso, isso parece buscar o arquivo inteiro.
Peterph

É um módulo php-cli não php para apache, não precisa apache.
Golfo Pérsico

Ainda assim, ele carregará todo o mecanismo PHP, que é um porco da memória. Além disso, uma parte razoável do PHP precisaria ser instalada, o que também pode ser um problema para o sistema incorporado (o espaço em disco pode ser limitado). Para um sistema regular, pode ser uma opção, embora você precise modificá-lo para impedir a obtenção de uma imagem inteira (consulte a resposta de Sukminder).
Peterph
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.