Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何以编程方式检查GIF图像是否已设置动画?_Php_Python_Image - Fatal编程技术网

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

Php 如何以编程方式检查GIF图像是否已设置动画?,php,python,image,Php,Python,Image,这里是一个链接,我问关于同一个项目,我正在工作。我认为这一点背景知识会有所帮助 对于那些懒得打开新标签来回答这个问题的人,我将总结一下我在这里要做的事情:我已经从4scrape下载了大约250000张图片,我想浏览一下GIF,找出哪些图片是动画的还是非动画的。我需要通过编程来完成这项工作,因为我真的觉得我的灵魂(或我与女友的关系)不需要看4chan的几千份礼物,看它们是否有动画效果。如果你知道4chan的性质,那么你就知道图像的性质(即“tits或GTFO”) 我知道PHP和Python,但愿意

这里是一个链接,我问关于同一个项目,我正在工作。我认为这一点背景知识会有所帮助

对于那些懒得打开新标签来回答这个问题的人,我将总结一下我在这里要做的事情:我已经从4scrape下载了大约250000张图片,我想浏览一下GIF,找出哪些图片是动画的还是非动画的。我需要通过编程来完成这项工作,因为我真的觉得我的灵魂(或我与女友的关系)不需要看4chan的几千份礼物,看它们是否有动画效果。如果你知道4chan的性质,那么你就知道图像的性质(即“tits或GTFO”)

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


非常感谢

阅读GIF89A规范并提取信息。

或者使用intergif程序,它可以从动画gif中提取单个图像。解压到一个临时目录中,看看你得到了多少文件。
页面上给出了一些有关
imagecreatefromformgif
函数的解决方案

从我读到的解决方案来看,这一个似乎是最好的,因为它的内存要求更严格

<?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;
}
?>

我从未见过一个程序能告诉你这一点。但GIF是块结构格式,您可以检查文件中是否存在表示动画GIF的块

维基百科文章如下所述:在偏移量0x30D处,GIF文件中的一个应用程序扩展名(即:3字节幻数21 FF 0B)块,后跟偏移量0x310处的幻数4E 45 54 53 43 41 50 45 32 9at,表示文件的其余部分包含多个图像,应设置动画

事实上,维基百科的文章对此进行了更好的解释,下面提到的文档格式在维基百科文章中进行了扩展

因此,您可以使用Python编写的程序解析GIF(多年前我使用C解析GIF,这主要是一个移动文件指针和读取字节的练习)。确定AE是否具有正确的3字节ID,后跟9字节幻数

也看到

也看到


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

我不是GIF文件格式专家,但这对我来说是一个有趣的问题,所以我仔细研究了一下。只有当动画GIF在位置0x310(编辑)处的值为NETSCAPE2.0,而静态GIF在位置0x310(编辑)处的值为NETSCAPE2.0时(在我的测试文件中就是这样),这才有效。这是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";
        }
    }
}
如果您在Linux(或任何具有的系统)上,您可以使用一行shell脚本和
identify
程序:

identify *.gif | fgrep '.gif[1] '
我知道你说过你更喜欢PHP和Python,但你也说过你愿意探索其他解决方案

使用Python和:

试试这个

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

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

from PIL import Image
fp = open('1.gif', 'rb')
im = Image.open(fp)
is_gif = bool(im.format and im.format.upper() == 'GIF')
这将添加属性
已设置动画
,以检查图像是否具有多个层或帧

用法示例:

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

ImageMagick函数getNumberImages将为您执行此操作。因为它返回对象中的图像数


为了让那些不想依赖第三方模块(如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\u animated\u gif()
函数的工作原理是跳过所有扩展名和颜色信息,并计算文件中的实际图像,当它找到第二个图像时,可以安全地假定gif已设置动画且其工作已完成


它不依赖于任何快捷方式,如检查应用程序扩展块的存在,因为gif动画似乎不需要这些快捷方式,我也不想做任何假设。

请参阅此提示,谢谢。Tiny nit:如果我没有弄错的话,将图像搜索回0可能很重要,因为即使在
块中,除了
之外,
gif.tell()
也会返回1。使用函数
getIteratorIndex
实际上非常简单,这是错误的。如果图像文件的格式为
MPO
。上面的例子也是可行的。现在(枕头==4.3.0),你只需从PIL导入图像中执行
;Image.open('animated.gif')。已设置动画
警告!这段代码中有一个主要的bug。while语句应包含括号,否则此函数将无法捕获所有动画GIF。虽然它会抓住一些(哪一个比100%失败更糟糕)。查看此stackoverflow url的完整和修复的代码事实上,此脚本有一个更新的版本,它修复了photoshop cs5动画GIF可能创建的错误。看,请扩大你的答案。这更像是一个评论。虽然这段代码可以回答这个问题,但提供关于为什么和/或如何回答这个问题的额外上下文将显著提高其长期价值。请在你的答案中添加一些解释。这不是一个完整的检查。虽然“帮助”并不适用于所有GIF。这仅适用于GIF。如果我用另一个图像(如JPEG)检查它,我会得到一个异常。@Uri可能是一个应该向开发人员报告的错误。
<?php

$image = new Imagick( YOUR_FILE );

if ( $image->getNumberImages() ) {
    echo "It's animated";
}
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