Bash脚本检查图像是否为动画png(apng)

时间:2017-11-03 21:12:39

标签: bash shell animation

尝试在我的bash脚本中找出一种方法来检查文件是否是动画PNG(apng)文件。在我的情况下,我想忽略它,如果是的话。有什么想法吗?

更新: 下面使用pngcheck的答案让我检查图像是否是动画。另外我检查文件的大小,如果它是“大”我也忽略它。最后,截至2017年11月,“识别”不起作用,这主要是我的初始问题。感谢Mark的帮助。

3 个答案:

答案 0 :(得分:2)

动画PNG的特点是存在acTL(动画控制块)和fcTL(帧控制块) - 请参阅Wikipedia article

所以,我认为合适的测试是使用verbose选项运行pngcheck,并至少查找acTL块:

pngcheck -v animated.png | grep -E "acTL|fcTL"

示例输出

  chunk acTL at offset 0x00025, length 8
  chunk fcTL at offset 0x00039, length 26
  chunk fcTL at offset 0x02f27, length 26
  chunk fcTL at offset 0x05901, length 26
  chunk fcTL at offset 0x083a2, length 26
  chunk fcTL at offset 0x0aea8, length 26
  chunk fcTL at offset 0x0d98c, length 26
  chunk fcTL at offset 0x10406, length 26
  chunk fcTL at offset 0x12e19, length 26
  chunk fcTL at offset 0x15985, length 26
  chunk fcTL at offset 0x185e2, length 26
  chunk fcTL at offset 0x1b2b0, length 26
  chunk fcTL at offset 0x1dfe1, length 26
  chunk fcTL at offset 0x20d24, length 26
  chunk fcTL at offset 0x23a03, length 26
  chunk fcTL at offset 0x26663, length 26
  chunk fcTL at offset 0x29218, length 26
  chunk fcTL at offset 0x2bcdf, length 26
  chunk fcTL at offset 0x2e7e0, length 26
  chunk fcTL at offset 0x312b0, length 26
  chunk fcTL at offset 0x33c51, length 26
  chunk fcTL at offset 0x36598, length 26
  chunk fcTL at offset 0x38f49, length 26
  chunk fcTL at offset 0x3b9bd, length 26
  chunk fcTL at offset 0x3e45e, length 26
  chunk fcTL at offset 0x40ed9, length 26
  chunk fcTL at offset 0x4393c, length 26
  chunk fcTL at offset 0x46521, length 26
  chunk fcTL at offset 0x4919b, length 26
  chunk fcTL at offset 0x4bde2, length 26
  chunk fcTL at offset 0x4eabd, length 26
  chunk fcTL at offset 0x51827, length 26
  chunk fcTL at offset 0x5453a, length 26
  chunk fcTL at offset 0x571c7, length 26
  chunk fcTL at offset 0x59d94, length 26

所以,这会在脚本中建议这个测试:

# Test an animated image, `grep` exit status is zero meaning `acTL` was found
pngcheck -v animated.png | grep -q "acTL"
echo $?
0

# Test a still image, `grep` exit status is 1 meaning `acTL` was not found
pngcheck -v still.png | grep -q "acTL"
echo $?
1

如果您没有,或者不想将pngcheck与您的项目一起发送,我制作了一个Perl脚本,只需对PNG文件进行解块并告诉您块和偏移,它应该在任何地方运行,因为它是Perl。欢迎您使用它。

示例运行

./pngchunks ball.png

13 IHDR
8 acTL                   <--- This one means it is animated
26 fcTL
4634 IDAT
26 fcTL
4344 fdAT
26 fcTL
4042 fdAT
26 fcTL
3828 fdAT
26 fcTL
3521 fdAT
26 fcTL
3168 fdAT
26 fcTL
2777 fdAT
26 fcTL
2588 fdAT
26 fcTL
2720 fdAT
26 fcTL
2792 fdAT
26 fcTL
2665 fdAT
26 fcTL
2581 fdAT
26 fcTL
2652 fdAT
26 fcTL
2774 fdAT
26 fcTL
2844 fdAT
26 fcTL
2886 fdAT
26 fcTL
2966 fdAT
26 fcTL
3197 fdAT
26 fcTL
3518 fdAT
26 fcTL
3995 fdAT
0 IEND
#!/usr/bin/perl -w
################################################################################
# pngchunks
# Mark Setchell
#
# Simple Perl tool to read the chunks in a PNG image file
# See https://en.wikipedia.org/wiki/Portable_Network_Graphics
#
# Usage: pngchunks image.png
################################################################################
use strict;
use Fcntl qw( SEEK_CUR );

my $f=shift or die "Usage: pngchunks image.png\n";

my ($handle,$offset,$buffer,$type,$length);

# Open file
open($handle,'<',$f) || die("Error opening file\n");

# Check 8 byte PNG signature
read($handle,$buffer,8);
if(substr($buffer,0,8) ne "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"){
   die("ERROR: Invalid PNG signature\n");
}

# Loop till IEND chunk
for(;;){

   # Read 4 bytes of length, Network (big-endian)
   read($handle,$buffer,4);
   $length=unpack("N",$buffer);

   # Read 4 bytes of chunk type
   read($handle,$buffer,4);
   $type=substr($buffer,0,4);

   printf("%d %s\n",$length,$type);

   # Break out of loop if IEND chunk
   last if $type eq "IEND";

   # Seek past this chunk and its 4 byte CRC
   $offset=4+$length;
   seek($handle,$offset,SEEK_CUR);
}

答案 1 :(得分:0)

我没有看到好的&#34;这样做的方式,所以我要作弊。首先,我的尝试:

两张测试图片:elephantball。他们实际上非常分散注意力,所以我会让你单独打开它们而不是把它们包括在里面(我只是焚了一分钟茫然地看着它们)。

为简单起见,我们将其重命名为elephant.apngball.apng

$ file elephant.apng ball.apng
elephant.apng: PNG image data, 480 x 400, 8-bit/color RGBA, non-interlaced
ball.apng:     PNG image data, 100 x 100, 8-bit/color RGBA, non-interlaced

$ file -b --mime-type elephant.apng ball.apng
image/png
image/png

$ identify elephant.apng ball.apng
elephant.apng PNG 480x400 480x400+0+0 8-bit sRGB 379KB 0.000u 0:00.000
ball.apng PNG 100x100 100x100+0+0 8-bit sRGB 65.6KB 0.000u 0:00.000

$ strings elephant.apng |grep -i png
APNG Assembler 2.8Q\

$ strings ball.apng |grep -i png

$

所以libmagic(文件5.32,无选项或请求MIME类型)和识别(ImageMagick 6.9.7-4)和字符串(GNU Binutils 2.29.1)与grep(GNU grep 3.1)都找不到任何东西除了APNG Assembler留下的水印外,无法保证。

现在让我们作弊。我将假设大量的熵意味着它是一个动画PNG(而不是像steganographic message那样的其他东西)并且只是通过ImageMagic convert将其转换为一个标准的PNG:

$ convert elephant.apng elephant.png
$ convert ball.apng ball.png
$ wc -c *
 65557 ball.apng
  5239 ball.png
379013 elephant.apng
 21068 elephant.png
470877 total

好的,我们可以解决这个问题:

#!/bin/sh

for image in "$@"; do
  size1="$(wc -c "$image")"
  size2="$(convert "$image" -format PNG - |wc -c)"
  if [ "$size1" -ge "$((size2*2))" ]
    then echo "$image: Animated or steganographic PNG"
    else echo "$image: Basic PNG"
  fi
done

这会将size1设置为图像的大小(以字节为单位)(使用wc -c来获取字符数),然后将size2设置为&#34;之后的图像大小。转换&#34;它是一个PNG(-告诉转换将最终图像推送到标准输出,wc读取它。如果size1至少是size2的两倍,那么我们假设它是一个动画PNG。

这是一个循环,所以你可以在任意数量的图像文件上调用它(甚至非PNG,虽然这没有多大意义,你总会被告知它#&## 39;某种PNG)。请注意,您无法管道,因为它会在第一次wc调用时吸收标准输入,并且没有任何剩余用于转换。

如果您安装了MediaInfo(例如apt install mediainfo),则可以(暂时!)使用此命令检测到错误:

$ mediainfo elephant.apng |grep ^FileExtension_Invalid
FileExtension_Invalid                    : png pns

$ mediainfo ball.apng |grep ^FileExtension_Invalid
FileExtension_Invalid                    : png pns

我不确定这意味着什么,但它并没有显示在我转换的PNG上,所以可能是安全的......至少在MediaInfoLib实际支持APNG文件之前格式(这就是为什么我说&#34;暂时&#34;)。这至少在v0.7.99中有效。

调用grep -q为您的if测试做出沉默。

答案 2 :(得分:-1)

首先使用expr提取文件的扩展名。

检查http://tldp.org/LDP/abs/html/string-manipulation.html

  

expr match "$string" '.*\($substring\)'

     

$substring末尾提取$string,其中$substring是正则表达式。

然后将提取的扩展名与&#34; .png&#34;进行比较。

相关问题