Bash:从URL确定图像尺寸的最快方法


8

我试图找出一种确定图像尺寸的快速方法。

我知道我可以获取图像,然后使用imagemagick确定图像的高度和宽度。我担心这可能不是最快的方法。

当我只需要很小的功能子集时,我还担心必须安装imagemagick。我在资源(CPU,RAM,存储)非常有限的嵌入式系统上。

有任何想法吗?


您需要支持哪些图像类型?
吉尔(Gilles)'所以

Answers:


13

如您所述,您不需要整个ImageMagick软件包。您只需要identify

您还将需要可执行链接链接到的库(以及那些库链接到的库)。

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

ldd将显示一个列表。当我这样做,它包括一些X库,libjpeg的,等两个库显然来自ImageMagick包,libMagickCorelibMagickWand。这些看起来与同一堆东西相关,因此,如果有的话,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)是截断文件的大小,因为该信息不是来自图像头文件。

因此:您只需要下载每个图像的前千字节左右。


12

您可以curl用来下载图像的一部分。这完全取决于它必须具有的坚固性。一个测试用例可以是前500个字节。似乎工作进行了大量的pngjpg,然后用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

在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,然后阅读下四个字节以获取大小。


其他格式:

本来可以继续,但我现在应该停在这里。


1

假设您具有“身份”。将其放在脚本中chmod +x <scriptname>。要运行它<scriptname> picture.jpg,请输入图像的高度和宽度。前两个部分将检查是否有图像,然后将其设置为IMAGE变量。下一部分将确保文件确实存在。最后两节将从“标识”输出中获取相关信息并显示出来。

#!/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是关于学习的)。
13年

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 默认情况下安装在Distors上,仅取决于:

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是否适合低资源嵌入式系统。另外,这似乎可以获取整个文件。
彼得

它是apache的php-cli而不是php模块,它不需要apache。
PersianGulf

它仍然会加载整个PHP引擎,这是一个内存消耗。再加上必须安装PHP的合理部分,这对于嵌入式系统也是一个问题(磁盘空间可能有限)。对于常规系统,这可能是一个选择,尽管您需要对其进行修改以防止获取整个图像(请参阅Sukminder的回答)。
彼得
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.