Анимированные GIF-изображения прозрачны только в первом кадре (Python PIL) - PullRequest
0 голосов
/ 14 января 2020

Следующий код создает GIF из двух изображений:

# GIFs are always palette images so they would be converted later anyway
im1 = PIL.Image.open('grin-emoji-by-twitter-rgba.png').convert('P')
im2 = PIL.Image.open('grin-emoji-by-twitter-rgba-2.png').convert('P')

im1.save('output.gif', save_all=True, append_images=[im2, im1, im2], loop=0, duration=200, transparency=255)

Однако результат неожиданно неверный. Первый кадр в порядке, но последующие кадры содержат черный прямоугольник вокруг области обновления вместо прозрачности:

enter image description here

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

Есть ли способ обойти это?

Ответы [ 2 ]

0 голосов
/ 14 января 2020

В качестве альтернативы моему другому ответу, вы также можете просто установить значение disposal :

im1.save('output.gif', save_all=True, append_images=[im2, im1, im2], 
         loop=0, duration=200, transparency=255, disposal=2)

Обратите внимание, что в отличие от моего другого ответа это не работает на 100% время, когда канал прозрачности может перейти к другим индексам. : / / Однако это происходит только в длинных GIF-файлах с большим количеством цветов.

Слева: этот ответ, справа: Другой ответ с ручным выравниванием

example of bug persisting example of bug fixed

0 голосов
/ 14 января 2020

Да, есть способ. Мы можем вручную отредактировать цветовую палитру, чтобы переместить прозрачность с индекса 255 на 0. save не будет плохо себя вести, если прозрачность будет с индексом 0 вместо 255.

Я выполнил это правильно смещение всей цветовой палитры на один индекс, так что индекс 5 становится индексом 6, индекс 255 становится индексом 0 и т. д.

В худшем случае (например, длинный красочный GIF) прозрачность НЕ всегда находится в индексе 255, и мы должны вручную выровнять его по индексу 0 (см. shiftme строка).

im1 = PIL.Image.open('grin-emoji-by-twitter-rgba.png').convert('P')
im2 = PIL.Image.open('grin-emoji-by-twitter-rgba-2.png').convert('P')

p1 = im1.getpalette()
p2 = im2.getpalette()

# if you know a data point in the resulting image that will be
# transparent you can also set it directly e.g. 'shiftme = -frame[0][0]'
shiftme = 1       
im1 = (numpy.array(im1) + shiftme) % 256  # shift data pointing into palette
im2 = (numpy.array(im2) + shiftme) % 256

im1 = PIL.Image.fromarray( im1 ).convert('P')
im2 = PIL.Image.fromarray( im2 ).convert('P')

im1.putpalette( p1[-3*shiftme:] + p1[:-3*shiftme] )  # shift palette
im2.putpalette( p2[-3*shiftme:] + p2[:-3*shiftme] )  # NB this is NOT '-4' as it is RGB not RGBA

print(numpy.array(im1))
print(numpy.array(im2))

im1.save('output.gif', save_all=True, append_images=[im2, im1, im2], loop=0, duration=200, transparency=0)

Результат ?

enter image description here

...