Bash: Schnellste Methode zum Bestimmen der Bilddimensionen anhand der URL

8

Ich versuche, eine wirklich schnelle Methode zu finden, um die Abmessungen eines Bildes zu bestimmen.

Ich weiß, dass ich das Bild betrachten und dann mit imagemagick die Höhe und Breite des Bildes bestimmen kann. Ich mache mir Sorgen, dass dies möglicherweise nicht der schnellste Weg ist.

Ich mache mir auch Sorgen, dass ich imagemagick installieren muss, wenn ich nur eine sehr kleine Teilmenge der Funktionalität benötige. Ich bin auf einem eingebetteten System mit sehr begrenzten Ressourcen (CPU, RAM, Speicher).

Irgendwelche Ideen?

Exvance
quelle
Welche Bildtypen müssen Sie unterstützen?
Gilles 'SO - hör auf böse zu sein'

Antworten:

13

Wie Sie bemerken, benötigen Sie nicht das gesamte ImageMagick- Paket. Du brauchst nur identify.

Sie benötigen auch die Bibliotheken, auf die die ausführbaren Links verweisen (und die Bibliotheken, auf die diese Bibliotheken verweisen).

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

lddwird eine Liste anzeigen. Als ich dies tat, enthielt es einige X-Bibliotheken, libjpeg usw. und zwei Bibliotheken, die eindeutig aus dem ImageMagick-Paket stammen, libMagickCoreund libMagickWand. Diese scheinen mit den gleichen Dingen verbunden zu sein. Wenn Sie das haben, identifysollten sie funktionieren.

Sie müssen nicht ein ganzes Bild herunterladen, um die Abmessungen zu erhalten, da sich diese in einer Kopfzeile am Anfang der Datei befinden und genau das wird identifyangezeigt. Zum Beispiel kopiere ich hier die ersten 4 kB von einem vollständigen JPEG in eine neue Datei:

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

4 kB sollten mehr als genug sein, um den Header aufzunehmen - ich bin sicher, Sie könnten es mit 1/4 dieser Menge tun. Jetzt:

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

Das sind die richtigen Maße für real.jpg. Beachten Sie jedoch, dass die Größe (4,1 KB) der Größe der abgeschnittenen Datei entspricht, da diese Informationen nicht aus dem Bildheader stammen.

Also: Sie müssen nur das erste Kilobyte oder so von jedem Bild herunterladen.

Goldlöckchen
quelle
12

Sie können curldamit Teile des Bildes herunterladen. Es hängt alles davon ab, wie robust es sein muss. Ein Testfall könnte aus den ersten 500 Bytes bestehen. Scheint für eine Menge von zu arbeiten pngund jpgdann identifyoder ähnliches zu verwenden, um die Größe zu überprüfen.

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

Bearbeiten:


Es ist lange her, dass ich Bildparser geschrieben habe, aber ich habe darüber nachgedacht und einen Teil meines Gedächtnisses aufgefrischt.

Ich vermute, dass es alle Arten von Bildern sind, die Sie überprüfen möchten (aber andererseits vielleicht auch nicht). Ich werde einige der gebräuchlichsten beschreiben : PNG, JPEG (JFIF) und GIF.


PNG:

Diese sind einfach, wenn es um die Extraktion von Größe geht. Ein pngHeader speichert die Größe innerhalb der ersten 24 Bytes. Zuerst kommt ein fester Header:

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.

Als nächstes kommen Brocken durch die Akte. Sie bestehen aus einem festen Feld von Länge, Typ und Prüfsumme. Zusätzlich ein optionaler Datenabschnitt mit Längengröße .

Zum Glück ist der erste Block immer ein IHDRmit diesem Layout:

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

Damit haben wir, dass die Größen Byte 16-20 und 21-24 sind. Sie können die Daten durch zB hexdump sichern:

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

Auf einem Big Endian / Motorola-Computer können die Größen auch direkt gedruckt werden durch:

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

Bei Little Endian / Intel ist dies jedoch nicht so einfach und auch nicht sehr portabel.

Auf diese Weise könnten wir ein bash + hexdump-Skript wie folgt implementieren:

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

Dies ist jedoch nicht direkt effizient. Obwohl es einen größeren Block (75-100 Bytes) erfordert, identifyist es eher schneller. Oder schreiben Sie die Routine in z. B. C, was schneller wäre als Bibliotheksaufrufe.


JPEG:

Wenn es darum jpggeht, ist es nicht so einfach. Es beginnt auch mit einem Signatur-Header , aber der Größenblock hat keinen festen Versatz. Nach dem Header:

 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  ...

Ein neuer Block wird durch eine Zwei-Byte-Markierung angegeben, die mit beginnt 0xff. Derjenige, der Informationen über Dimensionen enthält, hat den Wert 0xffc0, kann aber ziemlich viel in den Daten vergraben sein.

Mit anderen Worten, man überspringt Bytes in Blockgröße , prüft die Markierung, überspringt Bytes in Blockgröße , liest die Markierung usw., bis die richtige kommt.

Wenn gefunden, werden die Größen um jeweils zwei Bytes am Versatz 3 und 5 nach der Markierung gespeichert .

 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.

Schrieb ein einfaches C-Programm, um einige Dateien und etwa 10.000 JPG-Bilder zu überprüfen. Ungefähr 50% hatten die Größeninformationen innerhalb der ersten 500 Bytes, meistens 50% zwischen ca. 100 und 200. Das Schlimmste war rund 80.000 Bytes. Ein Bild, während wir Bilder sprechen:

JFIF_SOF_graph


GIF:

Obwohl in gif normalerweise mehrere Bilder gespeichert werden können, hat es eine in der Kopfzeile angegebene Leinwandgröße , die groß genug ist, um die Bilder aufzunehmen. Es ist so einfach wie mit PNG und erfordert sogar Fieberbytes: 10. Nach Magie und Version finden wir Größen. Beispiel aus einem 364x472-Bild:

<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

Mit anderen Worten, Sie können die ersten sechs Bytes überprüfen, um festzustellen, ob es sich um ein GIF handelt, und dann die nächsten vier für Größen lesen.


Andere Formate:

Hätte weitergehen können, aber ich schätze, ich höre hier vorerst auf.

Runium
quelle
1

Angenommen, Sie haben "identifizieren". Setzen Sie dies in ein Skript und chmod +x <scriptname>. Um es auszuführen, geben Sie ein <scriptname> picture.jpgund Sie erhalten die Höhe und Breite des Bildes. In den ersten beiden Abschnitten wird geprüft, ob ein Bild vorhanden ist, und dann als IMAGE-Variable festgelegt. Der nächste Abschnitt soll sicherstellen, dass die Datei tatsächlich vorhanden ist. In den letzten beiden Abschnitten werden die relevanten Informationen aus der Ausgabe "Identifizieren" entnommen und angezeigt.

#!/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}"
Back2Basics
quelle
schönes Skript. Es wäre jedoch schön, wenn Sie erklären könnten, was es tut (da es bei Stack Exchange um Lernen geht).
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 wird standardmäßig auf Distoren installiert und hängt nur ab von:

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

Ich denke, Sie können es einfach für Embedded installieren. Sie schreiben einfach ein regular expressionfür seine Ausgabe.

Persischer Golf
quelle
2
filegibt keine Dimensionen für z .jpg. B. Dateien an.
Goldlöckchen
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"

Sie ersetzen file://durchhttp://

Persischer Golf
quelle
Ich bin nicht sicher, ob PHP für eingebettete Systeme mit geringen Ressourcen gut geeignet ist. Außerdem scheint dies die gesamte Datei abzurufen.
Peterph
Es ist PHP-CLI nicht PHP-Modul für Apache, es muss nicht Apache.
PersianGulf
Trotzdem wird die gesamte PHP-Engine geladen, die ein Speicherfresser ist. Außerdem müsste ein angemessener Teil von PHP installiert werden, was auch für eingebettete Systeme ein Problem sein könnte (der Speicherplatz ist möglicherweise begrenzt). Für ein normales System ist dies möglicherweise eine Option, obwohl Sie es ändern müssen, um zu verhindern, dass das gesamte Bild abgerufen wird (siehe Sukminders Antwort).
Peterph