Как программно проверить, анимирован ли GIF-образ?

Вот ссылка на еще один вопрос. Я спросил о том же проекте, над которым я работаю. Я думаю, что немного фона будет полезно.

Для тех, кто слишком ленив, чтобы открыть новую вкладку для этого вопроса, я подведу итог тому, что я пытаюсь сделать здесь: я загрузил около 250 000 изображений из 4scrape, и я хочу пройти через GIF и найти какие анимированные или нет. Мне нужно сделать это программно, потому что я действительно не чувствую, что моя душа (или мои отношения с моей девушкой) может использовать, глядя на пару тысяч GIF из 4chan, чтобы увидеть, анимированы они или нет. Если вы знаете природу 4chan, то вы знаете природу изображений (т.е. "Сиськи или GTFO" ).

Я знаю PHP и Python, но хотел бы изучить другие решения. Также будет работать автономная часть программного обеспечения, которая работает в Windows.

Спасибо большое!

Ответ 1

С Python и PIL:

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

Ответ 2

Если вы работаете в Linux (или в любой системе с ImageMagick), вы можете использовать оболочку с одним слоем script и identify программа:

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

Я знаю, что вы сказали, что предпочитаете PHP и Python, но вы также сказали, что готовы исследовать другие решения.:)

Ответ 3

Я никогда не видел программу, которая расскажет вам об этом. Но GIF является блочным структурированным форматом, и вы можете проверить, присутствует ли в ваших файлах блок, обозначающий анимированный GIF.

Из статьи по википедии, отмеченной ниже: при смещении 0x30D в GIF файле добавляется расширение приложения (то есть: 3 байта магического номера 21 FF 0B), а затем магическое число 4E 45 54 53 43 41 50 45 32 9at offset 0x310 указывает, что остальная часть файла содержит несколько изображений, и они должны быть анимированы.

Действительно, статья в Википедии объясняет это лучше, и нижеуказанные документы формата раскрываются в статье Wiki.

Итак, вы можете анализировать GIF с помощью программы, написанной на Python (я анализировал GIF, используя C много лет назад, это было главным образом упражнение по перемещению указателя на файл и чтение байтов). Определите, присутствует ли AE с правильным 3-байтным идентификатором, а затем 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

Извините, лучше всего я могу сделать для вас.

Ответ 4

30 июня 2015 года добавлена Подушка is_animated.

Это добавляет свойство is_animated, чтобы проверить, имеет ли изображение несколько слоев или кадров.

Пример использования:

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

Ответ 5

Несколько рекомендаций приведены на странице PHP docs для функции imagecreatefromgif.

Из решений, которые я прочитал, кажется лучшим из-за его более жестких требований к памяти.

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

Ответ 6

Прочитайте спецификацию GIF89A и извлеките информацию. http://www.w3.org/Graphics/GIF/spec-gif89a.txt

Или легко и лениво и готово к взлому, используйте программу intergif, которая может извлекать отдельные изображения из анимированного gif. Извлеките в каталог temp и посмотрите, сколько файлов вы получите. http://utter.chaos.org.uk/~pdh/software/intergif/download.htm

Ответ 7

Я не эксперт по формату файлов GIF, но для меня это интересная проблема, поэтому я немного ее рассмотрел. Это будет работать только в том случае, если всегда верно, что анимированные gifs имеют значение NETSCAPE2.0 в позиции 0x310 (редактировать). И статические gif не делают (/edit), что было в моих тестовых файлах. Это С#, если вы хотите, чтобы я мог скомпилировать его в консольном приложении, которое принимает каталог в качестве аргумента, и вы можете запустить некоторый тест в своей очень большой коллекции 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";
        }
    }
}

Ответ 8

Посмотрите, есть ли в файле GIF несколько локальных дескрипторов.

Ответ 9

Функция ImageMagick getNumberImages сделает это за вас. Так как он возвращает количество изображений в объекте. Imagick :: getNumberImages

<?php

$image = new Imagick( YOUR_FILE );

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

Ответ 10

попробуйте это

import Image

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

Ответ 11

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

Ответ 12

Для людей, которые не хотят полагаться на сторонние модули, такие как 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 does not 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 animated
                    image_count += 1
                    if image_count > 1:
                        ret = True
                        break

                    # total size is 10 bytes, we already have the first byte so
                    # let 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, и я не хотел ничего предполагать.