Ответ 1
С Python и PIL:
from PIL import Image
gif = Image.open('path.gif')
try:
gif.seek(1)
except EOFError:
isanimated = False
else:
isanimated = True
Вот ссылка на еще один вопрос. Я спросил о том же проекте, над которым я работаю. Я думаю, что немного фона будет полезно.
Для тех, кто слишком ленив, чтобы открыть новую вкладку для этого вопроса, я подведу итог тому, что я пытаюсь сделать здесь: я загрузил около 250 000 изображений из 4scrape, и я хочу пройти через GIF и найти какие анимированные или нет. Мне нужно сделать это программно, потому что я действительно не чувствую, что моя душа (или мои отношения с моей девушкой) может использовать, глядя на пару тысяч GIF из 4chan, чтобы увидеть, анимированы они или нет. Если вы знаете природу 4chan, то вы знаете природу изображений (т.е. "Сиськи или GTFO" ).
Я знаю PHP и Python, но хотел бы изучить другие решения. Также будет работать автономная часть программного обеспечения, которая работает в Windows.
Спасибо большое!
С Python и PIL:
from PIL import Image
gif = Image.open('path.gif')
try:
gif.seek(1)
except EOFError:
isanimated = False
else:
isanimated = True
Если вы работаете в Linux (или в любой системе с ImageMagick), вы можете использовать оболочку с одним слоем script и identify
программа:
identify *.gif | fgrep '.gif[1] '
Я знаю, что вы сказали, что предпочитаете PHP и Python, но вы также сказали, что готовы исследовать другие решения.:)
Я никогда не видел программу, которая расскажет вам об этом. Но 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
Извините, лучше всего я могу сделать для вас.
30 июня 2015 года добавлена Подушка is_animated
.
Это добавляет свойство
is_animated
, чтобы проверить, имеет ли изображение несколько слоев или кадров.
Пример использования:
from PIL import Image
print(Image.open("test.gif").is_animated)
Несколько рекомендаций приведены на странице 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;
}
?>
Прочитайте спецификацию GIF89A и извлеките информацию. http://www.w3.org/Graphics/GIF/spec-gif89a.txt
Или легко и лениво и готово к взлому, используйте программу intergif, которая может извлекать отдельные изображения из анимированного gif. Извлеките в каталог temp и посмотрите, сколько файлов вы получите. http://utter.chaos.org.uk/~pdh/software/intergif/download.htm
Я не эксперт по формату файлов 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";
}
}
}
Посмотрите, есть ли в файле GIF несколько локальных дескрипторов.
Функция ImageMagick getNumberImages сделает это за вас. Так как он возвращает количество изображений в объекте. Imagick :: getNumberImages
<?php
$image = new Imagick( YOUR_FILE );
if ( $image->getNumberImages() ) {
echo "It animated";
}
попробуйте это
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
from PIL import Image
fp = open('1.gif', 'rb')
im = Image.open(fp)
is_gif = bool(im.format and im.format.upper() == 'GIF')
Для людей, которые не хотят полагаться на сторонние модули, такие как 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, и я не хотел ничего предполагать.