Bash: Cara tercepat untuk menentukan dimensi gambar dari URL

8

Saya mencoba mencari metode yang sangat cepat dalam menentukan dimensi suatu gambar.

Saya tahu saya bisa membuat gambar dan kemudian menggunakan imagemagick untuk menentukan tinggi dan lebar gambar. Saya khawatir ini mungkin bukan cara tercepat untuk melakukannya.

Saya juga khawatir harus menginstal imagemagick ketika saya hanya membutuhkan sebagian kecil fungsi. Saya menggunakan sistem tertanam yang memiliki sumber daya sangat terbatas (CPU, RAM, penyimpanan).

Ada ide?

exvance
sumber
Jenis gambar apa yang perlu Anda dukung?
Gilles 'SO- stop being evil'

Jawaban:

13

Seperti yang Anda perhatikan, Anda tidak memerlukan seluruh paket ImageMagick . Anda hanya perlu identify.

Anda juga akan membutuhkan pustaka untuk tautan yang dapat dieksekusi (dan pustaka yang ditautkan pustaka tersebut).

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

lddakan menampilkan daftar. Ketika saya melakukan ini, itu termasuk beberapa lib X, libjpeg, dll dan dua perpustakaan jelas dari paket ImageMagick, libMagickCoredan libMagickWand. Mereka tampaknya terkait dengan banyak hal yang sama, jadi jika Anda memilikinya, itu identifyakan berhasil.

Anda tidak perlu mengunduh seluruh gambar untuk mendapatkan dimensi, karena ini ada di header di awal file dan itulah yang identifyterlihat. Sebagai contoh, di sini saya menyalin 4 kB pertama dari jpeg lengkap ke file baru:

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

4 kB harus lebih dari cukup untuk memasukkan header - saya yakin Anda bisa melakukannya dengan 1/4 jumlah itu. Sekarang:

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

Itu adalah dimensi yang tepat untuk real.jpg. Perhatikan, bagaimanapun, bahwa ukuran (4.1KB) adalah ukuran file terpotong, karena informasi itu bukan dari header gambar.

Jadi: Anda hanya perlu mengunduh kilobyte pertama atau lebih dari setiap gambar.

goldilocks
sumber
12

Anda dapat menggunakan curluntuk mengunduh bagian gambar. Itu semua tergantung pada seberapa kuat itu harus. Kasing uji bisa menjadi 500 byte pertama. Tampaknya bekerja banyak pngdan jpg, kemudian gunakan identifyatau sejenisnya untuk memeriksa ukuran.

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

Edit:


Sudah lama sejak saya menulis parser gambar, tetapi memikirkannya dan menyegarkan ingatan saya.

Saya menduga itu semua jenis gambar yang ingin Anda periksa (tapi sekali lagi, mungkin tidak). Saya akan menjelaskan beberapa yang lebih umum: PNG, JPEG (JFIF) dan GIF.


PNG:

Ini sederhana dalam hal ekstraksi ukuran. Sebuah pngsundulan menyimpan ukuran dalam 24 byte. Pertama datang tajuk tetap:

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.

Selanjutnya datang potongan melalui file. Mereka terdiri dari bidang tetap panjang, jenis dan checksum. Selain itu bagian data opsional ukuran panjang .

Untungnya potongan pertama selalu merupakan IHDRdengan tata letak ini:

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

Dengan ini kita memiliki ukuran byte 16-20, dan 21-24. Anda dapat membuang data dengan misalnya 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

Pada mesin Big Endian / Motorola orang juga dapat mencetak ukuran langsung dengan:

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

Namun, pada Little Endian / Intel, ini tidak mudah, dan juga tidak terlalu portabel.

Dengan ini kita bisa mengimplementasikan skrip bash + hexdump seperti pada:

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

Tapi, ini tidak efisien secara langsung. Meskipun membutuhkan potongan yang lebih besar (75-100 byte), identifyini agak lebih cepat. Atau tulis rutin dalam misal C, yang akan lebih cepat dari panggilan pustaka.


JPEG:

Ketika sampai pada jpghal itu tidaklah mudah. Itu juga dimulai dengan header tanda tangan , tetapi ukuran chunk tidak pada offset tetap. Setelah tajuk:

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

blok baru datang ditentukan oleh penanda dua byte dimulai dengan 0xff. Yang memegang informasi tentang dimensi memiliki nilai 0xffc0tetapi dapat dikuburkan di dalam data.

Dengan kata lain, satu lewati byte ukuran blok , periksa marker, lewati byte ukuran blok , baca marker, dan seterusnya sampai yang benar muncul.

Ketika ditemukan ukuran disimpan oleh dua byte masing-masing pada offset 3 dan 5 setelah penanda .

 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.

Menulis program C sederhana untuk memeriksa beberapa file dan sekitar 10.000 gambar jpg, sekitar 50% memiliki informasi ukuran dalam 500 byte pertama, sebagian besar 50% antara ca. 100 dan 200. Yang terburuk adalah sekitar 80.000 byte. Gambar, seperti yang kita bicarakan gambar:

JFIF_SOF_graph


GIF:

Meskipun gif biasanya dapat menyimpan banyak gambar di dalamnya, ia memiliki ukuran kanvas yang ditentukan di header, ini cukup besar untuk menampung gambar. Ini semudah dengan PNG , dan bahkan memerlukan byte demam: 10. Setelah sihir dan versi kita menemukan ukuran. Contoh dari gambar 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

Dengan kata lain Anda dapat memeriksa enam byte pertama untuk melihat apakah itu gif, lalu baca empat byte berikutnya untuk ukuran.


Format lain:

Bisa saja terus, tetapi saya kira saya berhenti di sini untuk saat ini.

Runium
sumber
1

Asumsikan Anda telah "mengidentifikasi". Masukkan ini ke dalam skrip dan chmod +x <scriptname>. Untuk menjalankannya ketik <scriptname> picture.jpgdan Anda akan mendapatkan tinggi dan lebar gambar. 2 bagian pertama adalah untuk memeriksa apakah ada gambar kemudian mengaturnya sebagai variabel IMAGE. Bagian selanjutnya adalah memastikan file tersebut benar-benar ada. 2 bagian terakhir adalah untuk mengambil informasi yang relevan dari output 'identifikasi' dan menampilkannya.

#!/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
sumber
naskah yang bagus. Namun, alangkah baiknya jika Anda bisa menjelaskan apa yang dilakukannya (karena Stack Exchange adalah tentang belajar).
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 diinstal secara default pada distors dan hanya bergantung pada:

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

Saya pikir Anda dapat menginstalnya dengan mudah untuk disematkan. Anda hanya menulis regular expressionuntuk hasilnya.

Teluk Persia
sumber
2
filetidak memberikan dimensi untuk, misalnya, .jpgfile.
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"

Anda ganti file://denganhttp://

Teluk Persia
sumber
Saya tidak yakin PHP sangat cocok untuk sistem embedded sumber daya rendah. Plus ini sepertinya mengambil seluruh file.
peterph
Ini php-cli bukan modul php untuk apache, tidak perlu apache.
PersianGulf
Masih akan memuat seluruh mesin PHP yang merupakan babi memori. Plus, sebagian besar PHP harus diinstal, yang mungkin juga menjadi masalah untuk sistem embedded (ruang disk mungkin terbatas). Untuk sistem biasa, ini mungkin opsi, meskipun Anda harus memodifikasinya untuk mencegah pengambilan seluruh gambar (lihat jawaban Sukminder).
peterph