Bash: самый быстрый способ определения размеров изображения по URL

8

Я пытаюсь найти действительно быстрый метод определения размеров изображений.

Я знаю, что могу создать изображение, а затем использовать imagemagick для определения высоты и ширины изображения. Я обеспокоен тем, что это не самый быстрый способ сделать это.

Я также обеспокоен необходимостью установить imagemagick, когда мне нужен только небольшой набор функций. Я нахожусь на встроенной системе, которая имеет очень ограниченные ресурсы (процессор, память, память).

Любые идеи?

exvance
источник
Какие типы изображений вам нужно поддерживать?
Жиль "ТАК - перестань быть злым"

Ответы:

13

Как вы заметили, вам не нужен весь пакет ImageMagick . Тебе просто нужно identify.

Вам также понадобятся библиотеки, на которые ссылаются исполняемые файлы (и библиотеки, на которые эти библиотеки ссылаются).

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

lddпокажет список. Когда я сделал это, он включал в себя несколько библиотек X libs, libjpeg и т. Д., А также две библиотеки из пакета ImageMagick, libMagickCoreи libMagickWand. Они выглядят связанными с одной и той же кучей вещей, поэтому, если у вас есть это, identifyдолжно работать.

Вам не нужно загружать все изображение, чтобы получить размеры, потому что они находятся в заголовке в начале файла, и это то, на что мы identifyсмотрим. Например, здесь я копирую первые 4 КБ из полного jpeg в новый файл:

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

4 кБ должно быть более чем достаточно, чтобы включить заголовок - я уверен, что вы могли бы сделать это с 1/4 этой суммы. Сейчас же:

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

Это правильные размеры для real.jpg. Однако обратите внимание, что размер (4,1 КБ) - это размер усеченного файла, поскольку эта информация не из заголовка изображения.

Итак, вам нужно всего лишь загрузить первый килобайт каждого изображения.

лютик золотистый
источник
12

Вы можете использовать curlдля загрузки частей изображения. Все зависит от того, насколько надежным он должен быть. Тестовый случай может быть первым 500 байтов. Кажется, для работы много, pngа jpgзатем используйте identifyили как, чтобы проверить размер.

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

Редактировать:


Я давно писал парсеры изображений, но немного подумал и освежил часть своей памяти.

Я подозреваю, что это все виды изображений, которые вы хотите проверить (но опять же, возможно, нет). Я опишу некоторые из наиболее распространенных из них: PNG, JPEG (JFIF) и GIF.


PNG:

Это просто, когда дело доходит до извлечения размера. pngЗаголовка хранит размер в пределах первых 24 байт. Сначала идет фиксированный заголовок:

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.

Далее идут куски через файл. Они состоят из фиксированного поля длины, типа и контрольной суммы. Кроме того необязательных данные секция длиной размера.

К счастью, первый блок всегда IHDRс таким макетом:

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

Таким образом, мы имеем размеры 16-20 и 21-24. Вы можете вывести данные, например, с помощью 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

На машине Big Endian / Motorola можно также распечатать размеры напрямую:

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

Однако на Little Endian / Intel это не так просто и не очень портативно.

Таким образом, мы можем реализовать скрипт bash + hexdump, как в:

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

Но это не совсем эффективно. Хотя для этого требуется больший кусок (75-100 байт), он identifyработает довольно быстро. Или напишите подпрограмму, например, в C, которая будет быстрее, чем вызовы библиотеки.


JPEG:

Когда дело доходит до jpgэтого не так просто. Он также начинается с заголовка подписи , но размер блока не с фиксированным смещением. После заголовка:

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

появляется новый блок, указанный двухбайтовым маркером, начинающимся с 0xff. Тот, который содержит информацию об измерениях, имеет значение, 0xffc0но может быть скрыт в данных.

Другими словами, один пропускает байты размера блока , проверяет маркер, пропускает байты размера блока , читает маркер и так далее, пока не появится правильный.

Найденные размеры сохраняются по два байта каждый со смещением 3 и 5 после маркера .

 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.

Написал простую C-программу для проверки некоторых файлов и около 10.000 изображений в формате jpg, примерно 50% имели информацию о размере в первых 500 байтах, в основном 50% между ок. 100 и 200. Худшее было около 80.000 байтов. Картинка, как мы говорим, картинки:

JFIF_SOF_graph


GIF:

Хотя GIF обычно может иметь несколько изображений , хранящихся внутри, он имеет брезентовый размер , указанный в заголовке, это достаточно большая для размещения изображений. Это так же просто, как с PNG , и требует даже байтов лихорадки: 10. После магии и версии мы находим размеры. Пример из изображения 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

Другими словами, вы можете проверить первые шесть байтов, чтобы увидеть, если это GIF, а затем прочитать следующие четыре для размеров.


Другие форматы:

Можно было бы продолжить, но, думаю, я остановлюсь здесь сейчас.

Runium
источник
1

Предполагается, что у вас есть «определить». Поместите это в сценарий и chmod +x <scriptname>. Для запуска наберите <scriptname> picture.jpgи вы получите высоту и ширину изображения. Первые 2 раздела должны проверить, есть ли изображение, а затем установить его в качестве переменной IMAGE. Следующий раздел должен убедиться, что файл на самом деле там. Последние 2 раздела должны взять соответствующую информацию из «идентификатора» и отобразить ее.

#!/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
источник
хороший сценарий Тем не менее, было бы неплохо, если бы вы могли объяснить, что он делает (так как Stack Exchange предназначен для обучения).
стружка
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 устанавливается по умолчанию на искажениях и зависит только от:

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

Я думаю, что вы можете легко установить его для встроенных. Вы просто пишите regular expressionдля его вывода.

Персидский залив
источник
2
fileне дает размеры, например, для .jpgфайлов.
Златовласка
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"

Вы заменяете file://наhttp://

Персидский залив
источник
Я не уверен, что PHP хорошо подходит для встраиваемых систем с ограниченными ресурсами. К тому же, похоже, что это файл целиком.
Петер
Это php-cli, а не php модуль для apache, ему не нужен apache.
Персидский залив
Тем не менее, он загрузит весь PHP-движок, который является бременем памяти. Кроме того, должна быть установлена ​​разумная часть PHP, что также может быть проблемой для встроенной системы (дисковое пространство может быть ограничено). Для обычной системы это может быть вариант, хотя вам нужно изменить его, чтобы предотвратить выборку всего изображения (см. Ответ Сукминдера).
Петер