如何以编程方式检查GIF图像是否已设置动画?

时间:2009-09-11 18:21:18

标签: php python image

这是another question的链接,我询问了我正在处理的同一个项目。我认为这一点背景会有所帮助。

对于那些懒得打开这个问题的新标签的人,我会总结一下我在这里要做的事情:我从4scrape下载了大约250,000张图片,我想通过GIF查找哪些是动画或不动画。我需要以编程方式执行此操作,因为我真的不觉得我的灵魂(或我与我的女朋友的关系)可以使用从4chan查看几千个GIF来查看它们是否有动画。如果您知道4chan的性质,那么您就知道图像的本质(即“山雀或GTFO”)。

我了解PHP和Python,但愿意探索其他解决方案。在Windows上运行的独立软件也可以使用。

非常感谢!

12 个答案:

答案 0 :(得分:17)

使用Python和PIL

from PIL import Image
gif = Image.open('path.gif')
try:
    gif.seek(1)
except EOFError:
    isanimated = False
else:
    isanimated = True

答案 1 :(得分:13)

如果您使用的是Linux(或任何具有ImageMagick的系统),您可以使用单线程shell脚本和identify程序:

identify *.gif | fgrep '.gif[1] '

我知道你说你更喜欢PHP和Python,但你也说过你愿意探索其他解决方案。 :)

答案 2 :(得分:7)

我从来没有见过会告诉你这个的程序。但是GIF是块结构化格式,您可以检查文件中是否存在指示动画GIF的块。

来自下面提到的维基百科文章:在偏移量0x30D处,GIF文件中的应用程序扩展(即:3字节幻数21 FF 0B)块,后跟幻数4E 45 54 53 43 41 50 45 32 9at偏移量0x310表示文件的其余部分包含多个图像,它们应该是动画的。

维基百科的文章确实更好地解释了它,下面提到的格式文档扩展了维基文章。

因此,您可以使用Python编写的程序解析GIF(我多年前使用C解析GIF,主要是移动文件指针并读取字节)。确定AE是否存在正确的3字节ID,然后是9字节幻数。

请参阅http://en.wikipedia.org/wiki/Graphics_Interchange_Format#Animated_.gif

另见http://www.martinreddy.net/gfx/2d/GIF87a.txt

另见http://www.martinreddy.net/gfx/2d/GIF89a.txt

对不起,我能为你做的最好。

答案 3 :(得分:3)

imagecreatefromgif功能的PHP docs页面上提供了一些解决方案。

从我读过的解决方案来看,这个解决方案似乎是最好的,因为它需要更紧密的内存。

<?php
function is_ani($filename) {
    if(!($fh = @fopen($filename, 'rb')))
        return false;
    $count = 0;
    //an animated gif contains multiple "frames", with each frame having a
    //header made up of:
    // * a static 4-byte sequence (\x00\x21\xF9\x04)
    // * 4 variable bytes
    // * a static 2-byte sequence (\x00\x2C)

    // We read through the file til we reach the end of the file, or we've found
    // at least 2 frame headers
    while(!feof($fh) && $count < 2) {
        $chunk = fread($fh, 1024 * 100); //read 100kb at a time
        $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00\x2C#s', $chunk, $matches);
    }
    fclose($fh);
    return $count > 1;
}
?>

答案 4 :(得分:3)

On Jun 30, 2015, Pillow added is_animated.

  

这会添加属性is_animated,以检查图像是否有多个图层或框架。

使用示例:

from PIL import Image
print(Image.open("test.gif").is_animated)

答案 5 :(得分:2)

阅读GIF89A规范并提取信息。 http://www.w3.org/Graphics/GIF/spec-gif89a.txt

或者容易和懒惰并准备好使用intergif程序,它可以从动画gif中提取单个图像。解压缩到临时目录并查看您获得的文件数量。 http://utter.chaos.org.uk/~pdh/software/intergif/download.htm

答案 6 :(得分:2)

我不是GIF文件格式专家,但对我来说这是一个有趣的问题,所以我稍微研究了一下。只有当动画GIF在位置0x310(编辑)中具有值NETSCAPE2.0并且静态gif不在,(/ edit)时,才会有效,这在我的测试文件中就是这种情况。这是C#,如果你想我可以将它编译成一个控制台应用程序,它以一个目录作为参数,你可以在你的非常大的gif集合上运行一些测试,看看它是否能产生可靠的结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            string ani = @"C:\path\to\ani.gif";
            string sta = @"C:\path\to\static.gif";

            Console.WriteLine(isAnimated(ani));
            Console.WriteLine(isAnimated(sta));
        }

        static bool isAnimated(string path)
        {
            byte[] bytes = File.ReadAllBytes(path);
            byte[] netscape = bytes.Skip(0x310).Take(11).ToArray();

            StringBuilder sb = new StringBuilder();

            foreach (var item in netscape)
            {
                sb.Append((char)item);
            }

            return sb.ToString() == "NETSCAPE2.0";
        }
    }
}

答案 7 :(得分:1)

查看GIF文件中是否有多个LocalDescriptor。

答案 8 :(得分:1)

ImageMagick函数getNumberImages将为您完成此操作。由于它返回对象中的图像数。 Imagick::getNumberImages

<?php

$image = new Imagick( YOUR_FILE );

if ( $image->getNumberImages() ) {
    echo "It's animated";
}

答案 9 :(得分:0)

试试这个

import Image

def checkAnimate():
    im = Image.open('image.gif')
    if not im.info['version'].__contains__('GIF'):
        print "It's not a GIF file"
    else:
        if im.info.has_key('duration'):
            if im.info['duration'] > 0:
                return True
            else:
                return False
        else:
            return False

答案 10 :(得分:0)

from PIL import Image
fp = open('1.gif', 'rb')
im = Image.open(fp)
is_gif = bool(im.format and im.format.upper() == 'GIF')

答案 11 :(得分:0)

对于那些不想依赖第三方模块(例如Pillow)的人来说,这是一个完全使用python 2和3的本机解决方案:

import sys
is_py2 = sys.version_info[0] == 2


def is_animated_gif(image_path):
    """Return true if image is an animated gif

    primarily used this great deep dive into the structure of an animated gif
    to figure out how to parse it:

        http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp

    Other links that also helped:

        https://en.wikipedia.org/wiki/GIF#Animated_GIF
        https://www.w3.org/Graphics/GIF/spec-gif89a.txt
        https://stackoverflow.com/a/1412644/5006

    :param image_path: string, assumed to be a path to a gif file
    :returns: boolean, True if the image is an animated gif
    """
    ret = False
    image_count = 0

    def skip_color_table(fp, packed_byte):
        """this will fp.seek() completely passed the color table"""
        if is_py2:
            packed_byte = int(packed_byte.encode("hex"), 16)

        has_gct = (packed_byte & 0b10000000) >> 7
        gct_size = packed_byte & 0b00000111

        if has_gct:
            global_color_table = fp.read(3 * pow(2, gct_size + 1))

    def skip_image_data(fp):
        """skips the image data, which is basically just a series of sub blocks
        with the addition of the lzw minimum code to decompress the file data"""
        lzw_minimum_code_size = fp.read(1)
        skip_sub_blocks(fp)

    def skip_sub_blocks(fp):
        """skips over the sub blocks

        the first byte of the sub block tells you how big that sub block is, then
        you read those, then read the next byte, which will tell you how big
        the next sub block is, you keep doing this until you get a sub block
        size of zero"""
        num_sub_blocks = ord(fp.read(1))
        while num_sub_blocks != 0x00:
            fp.read(num_sub_blocks)
            num_sub_blocks = ord(fp.read(1))

    with open(image_path, "rb") as fp:
        header = fp.read(6)
        if header == b"GIF89a": # GIF87a doesn't support animation
            logical_screen_descriptor = fp.read(7)
            skip_color_table(fp, logical_screen_descriptor[4])

            b = ord(fp.read(1))
            while b != 0x3B: # 3B is always the last byte in the gif
                if b == 0x21: # 21 is the extension block byte
                    b = ord(fp.read(1))
                    if b == 0xF9: # graphic control extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        b = ord(fp.read(1))
                        if b != 0x00:
                            raise ValueError("GCT should end with 0x00")

                    elif b == 0xFF: # application extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        skip_sub_blocks(fp)

                    elif b == 0x01: # plain text extension
                        block_size = ord(fp.read(1))
                        fp.read(block_size)
                        skip_sub_blocks(fp)

                    elif b == 0xFE: # comment extension
                        skip_sub_blocks(fp)

                elif b == 0x2C: # Image descriptor
                    # if we've seen more than one image it's animated
                    image_count += 1
                    if image_count > 1:
                        ret = True
                        break

                    # total size is 10 bytes, we already have the first byte so
                    # let's grab the other 9 bytes
                    image_descriptor = fp.read(9)
                    skip_color_table(fp, image_descriptor[-1])
                    skip_image_data(fp)

                b = ord(fp.read(1))

    return ret

is_animated_gif()函数的工作原理是跳过所有扩展名和颜色信息并计算文件中的实际图像,当找到第二个图像时,它可以安全地假定gif处于动画状态并完成了工作。

它不依赖于检查应用程序扩展块是否存在的任何快捷方式,因为它看起来好像不可以使gif动画化,而且我也不想承担任何责任。