Bash:URLから画像の寸法を決定する最速の方法


8

私は、画像のサイズを決定するbashの本当に速い方法を理解しようとしています。

画像を取得し、imagemagickを使用して画像の高さと幅を決定できることはわかっています。これが最速の方法ではないのではないかと心配しています。

また、機能のごく一部のみが必要な場合にimagemagickをインストールする必要があることにも関心があります。リソース(CPU、RAM、ストレージ)が非常に限られている組み込みシステムを使用しています。

何か案は?


サポートする必要がある画像タイプは何ですか?
Gilles「SO-邪悪なことをやめなさい」

回答:


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パッケージから明らかに2つのライブラリーlibMagickCoreを含んでいましたlibMagickWand。それらは同じものにリンクされているように見えるので、それがあれば動作するidentifyはずです。

寸法を取得するために画像全体をダウンロードする必要はありません。これは、これらがファイルの先頭のヘッダーにあり、それidentifyが見ているためです。たとえば、ここでは完全なjpegから最初の4 kBを新しいファイルにコピーしています。

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

ヘッダーを含めるには4 kBで十分です。その量の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.1KB)は切り捨てられたファイルのサイズです。

つまり、各画像の最初の1キロバイト程度をダウンロードするだけで済みます。


12

を使用curlして、イメージの一部をダウンロードできます。それはすべてそれがいかに堅牢でなければならないかに依存します。テストケースは最初の500バイトになる可能性があります。たくさんの仕事に思えるpngし、jpgその後、使用し、identifyサイズを確認するなど。

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

編集:


イメージパーサーを作成してから長い時間が経過しましたが、少し考えて、私の記憶の一部を更新しました。

それはあなたがチェックしたいすべての種類の画像だと思います(しかし、おそらく、おそらくそうではありません)。より一般的なもののいくつかを説明します:PNGJPEG (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

ビッグエンディアン/モトローラマシンでは、次の方法でサイズを直接印刷することもできます。

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

ただし、リトルエンディアン/インテルでは、それほど簡単ではなく、移植性も高くありません。

これにより、次のように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  ...

新しいブロックは、で始まる2バイトのマーカーで指定されて一緒に来ます0xff。次元に関する情報を保持するものには値0xffc0がありますが、データのかなり下に埋めることができます。

つまり、ブロックサイズのバイトをスキップし、マーカーをチェックし、ブロックサイズのバイトをスキップし、マーカーを読み取って、正しいバイトが来るまで続けます。

見つかった場合、サイズは、マーカーの後のオフセット3と5にそれぞれ2バイトずつ格納されます

 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.

いくつかのファイルと約10.000のjpg画像をチェックする単純なCプログラムを書いたところ、およそ50%が最初の500バイト以内のサイズ情報を持っていました。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

つまり、最初の6バイトをチェックしてgifかどうかを確認し、次の4バイトを読み取ってサイズを確認できます。


その他のフォーマット:

続けることができたかもしれませんが、私は今ここで停止すると思います。


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}"

素敵なスクリプト。ただし、それが何をするかを説明できればいいのですが(Stack Exchangeは学習についてのものです)。
2013年

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ファイルの寸法は提供しません。
goldilocks 2013年

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が低リソースの組み込みシステムに適しているかどうかはわかりません。さらに、これはファイル全体をフェッチするようです。
peterph 2013年

これはapacheのphpモジュールではなくphp-cliであり、apacheする必要はありません。
PersianGulf 2013年

それでも、メモリを独占しているPHPエンジン全体をロードします。さらに、PHPの妥当な部分をインストールする必要があります。これは、組み込みシステムでも問題になる可能性があります(ディスク容量が限られている場合があります)。通常のシステムの場合はオプションの場合がありますが、画像全体を取得しないように変更する必要があります(Sukminderの回答を参照)。
peterph 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.