このページでは Python でアニメーション GIF を作成する方法について解説します。
ちょっとした動画をウェブサイトに載せる際などにアニメーション GIF は非常に便利です。
Python では PIL を利用すればアニメーション GIF を簡単に作成できます。このページではこのアニメーション GIF の作成方法について解説していきたいと思います。
GIFがアニメではなく静止画になっている場合、ページをスーパーリロードしていただくことでアニメ表示にできる可能性があります
スーパーリロードの仕方はブラウザ等によって異なりますが、「Shift キー」を押しながら更新ボタンをクリックすることで実行できることが多いです
このページで紹介するサンプルスクリプトは下記環境で動作確認を行っています
- OS:macOS Catalina
- Python:3.8
- Tkinter:8.6
- NumPy:1.18.2
- OpenCV2:4.2.0
- Pillow:7.1.1
Contents
アニメーション GIF の作成方法
アニメーション GIF を Python で作成するための手順は大きく分けると下記の2つになります。
- アニメーション用の複数の画像データを用意
- PIL Image モジュールの
Image
クラスが提供するsave
メソッドを実行
複数の画像データを用意
まず前提として、アニメーション GIF はある時間おきに複数の画像データを連続して表示するファイルになります。
ですので、アニメーション GIF を作成するためには、事前に画像データを複数用意しておく必要があります(この辺りの実例はサンプルスクリプトで紹介します)。
スポンサーリンク
save
メソッドによるアニメーション GIF の作成
さらに、それらの複数の画像データをつなげる形でファイルとして保存することでアニメーション GIF を作成することができます。
これは PIL Image モジュールの Image
クラスが提供する save
メソッドにより簡単に実現することができます。
save
メソッドは他のフォーマットの画像ファイルを作成(保存)するためにも利用できますが、下記のように save
メソッドに引数を指定することで、アニメーション GIF 形式のファイルとして作成することができます。
image1.save(
"ファイル名",
format="gif",
save_all=True,
append_images=image_list
)
save_all
を True
として設定することで、ただの画像ではなく、アニメーション GIF を作成することができます。
この時、アニメーション GIF の先頭画像は、この save
メソッドを実行したオブジェクト(PIL.Image.Image
クラスのオブジェクト)の画像となります。
また、アニメーション GIF の2枚目以降の画像は、append_images
に指定したリストの要素が参照する画像オブジェクト(PIL.Image.Image
クラスのオブジェクト)の画像となります。
ですので、save
メソッド実行前に、2枚目以降の画像(のオブジェクト)をリストの各要素に参照させておく必要があります。
アニメーション GIF の作成時の save
の引数
アニメーション GIF 作成時に save
メソッドに指定する引数には下記のようなものがあります(他にも指定可能な引数はありますが、私も詳しくないので省略します)。
fp
ファイルパスもしくはファイルオブジェクトを指定します。第1引数として指定することが可能です。
format
保存する画像のフォーマットを指定します。アニメーション GIF を作成するためには "gif"
を指定します。
fp
で指定するファイル名の拡張子を .gif にしておけば、自動的に画像のフォーマットが GIF になるため、この場合は引数 format
をわざわざ指定する必要はありません。
save_all
前述の通り、ファイルをアニメーションとして保存するかどうかを指定する引数になります。
アニメーション GIF を作成・保存する場合は save_all=True
として指定します。
append_images
画像オブジェクトのリスト(参照のリスト)を指定します。
このリストの各要素が参照する画像オブジェクトがアニメーション GIF の2枚目以降の画像となります。
loop
引数 loop
ではアニメーションを何回繰り返すかを設定します。
「引数 loop
で指定した値+1」回分のループが行われます。
例えば loop=3
と指定すれば、アニメーションは4回ループして表示されることになります。
duration
引数 duration
では、各画像を表示する時間を設定します。単位は ms です。
例えば duration=1000
と指定すれば、各画像が 1000ms、つまり1秒間表示され、その後に次の画像が表示されるようになります。
duration
にはリストもしくはタプルを指定することも可能であり、各要素に時間を指定することで、画像ごとに表示時間を設定することができます。
例えば duration=[1000, 50, 50, 300]
と指定すれば、画像の1枚目は 1000ms 、2枚目と3枚目は 50 ms 、4枚目は 300ms 表示されることになります。
アニメーション GIF 作成のサンプルスクリプト
アニメーション GIF を作成するサンプルスクリプトをいくつか紹介していきます。
画像としては、下記の neko_before.jpg と neko_after.jpg を使用します。
- neko_before.jpg
- neko_after.jpg
スポンサーリンク
2枚の画像のアニメーション GIF の作成
まずはオーソドックスに2枚の画像のアニメーション GIF を作成するサンプルスクリプトを紹介していきます。
サンプルスクリプト
2枚の画像のアニメーション GIF を作成するサンプルスクリプトは下記になります。
# -*- coding:utf-8 -*-
from PIL import Image
# アニメーションの最初の画像のオブジェクト作成
before = Image.open("neko_before.jpg")
# "read of closed file" エラー回避用
before = before.copy()
# アニメーションの最後の画像のオブジェクト作成
after = Image.open("neko_after.jpg")
# "read of closed file" エラー回避用
after = after.copy()
# アニメーション GIF として保存
before.save(
# ファイル名
"neko_anime.gif",
# アニメーションとして保存
save_all=True,
# アニメーションに含ませる画像のリスト
append_images=[after,],
# 画像の表示時間
duration=1000,
# 3回表示
loop=2
)
生成されるアニメーション GIF
出来上がりは下のようになります。
サンプルスクリプトの説明
before
に save
メソッドを実行させているので、アニメーションの最初の画像は neko_before.jpg になります。
また save
メソッドの append_images
に要素に after
(への参照)のみを持つリストを指定していますので、2枚目の画像として neko_after.jpg が表示されます。
duration
に 1000
を表示していますので、両方の画像が1秒ずつ表示されます。
copy
メソッドを実行しないと “ValueError: read of closed file” が発生することがあるため、無意味なようにも思える画像オブジェクトのコピーを行なっています
どうもファイルオブジェクトを保持している画像オブジェクト(Image.open
で生成したオブジェクト)が save
メソッドを実行すると、save
メソッドの中でクローズ後のファイルにアクセスしてエラーになることがあるようです(JPEG では発生しなかったが、PNG では発生した)
copy
メソッドではファイルオブジェクトまではコピーされないため、コピー後の画像オブジェクトで save
メソッドを実行することで上記エラーを回避しています
徐々に画像が変化していくアニメーション GIF の作成
今度は1枚目の画像から2枚目の画像へ徐々に変化していくアニメーション GIF を作成するサンプルスクリプトを紹介します。
サンプルスクリプト
画像が徐々に変化するアニメーション GIF を作成するサンプルスクリプトは下記になります。
# -*- coding:utf-8 -*-
from PIL import Image
# アニメーションの最初の画像のオブジェクト作成
before = Image.open("neko_before.jpg")
# アニメーションの最後の画像のオブジェクト作成
after = Image.open("neko_after.jpg")
# 画像を格納するリスト(空)を作成
frames = []
# beforeからafterへ徐々に変化させていく
for a in range(0, 101, 4):
# beforeとafterの混ぜ具合を設定
alpha = a / 100
# beforeとafterをalphaの混ぜ具合でブレンド
blended_image = Image.blend(before, after, alpha)
# 作成した画像オブジェクトをリストに追加
frames.append(blended_image)
# durationを格納するリスト(空)を作成
duration = []
# 各画像に対してdurationを設定
for i in range(len(frames)):
if i == 0 or i == len(frames) - 1:
# 1枚目と最後の画像だけ2000ms表示
duration.append(2000)
else:
# その他の画像は50ms表示
duration.append(50)
# アニメーション GIF として保存
frames[0].save(
# ファイル名
"neko_anime.gif",
# アニメーションとして保存
save_all=True,
# アニメーションに含ませる画像のリスト
append_images=frames[1:],
# 画像の表示時間(リストで指定)
duration=duration,
# 3回表示
loop=2
)
生成されるアニメーション GIF
出来上がりは下のようになります。
サンプルスクリプトの説明
スクリプト内で実行している Image.blend
関数は2つの画像(image1
と image2
)を合成して1つの画像を生成する関数です。
image = Image.blend(image1, image2, alpha)
alpha
(0.0 – 1.0 で指定)は、2つの画像のうちのどちらを強く合成するかを設定するものになります。
詳しい説明は省略しますが、alpha
を徐々に増やしながら画像を順々に生成し、それらの画像からアニメーション GIF を作成すれば、1枚目の画像から2枚目の画像に徐々に変化していくアニメーション GIF になります。
この Image.blend
関数で生成した画像をリスト frames
の末尾に追加し、frames[0]
に save
メソッドを実行することで、アニメーション GIF の先頭画像が frames[0]
の参照する画像になります(つまり neko_before.jpg)。
また save
メソッドの引数 append_images
に frames[1:]
を指定することで、リスト frames
の第1要素(先頭から2番目の要素)から後ろの画像が追加される形でアニメーション GIF が作成されます。
さらに、このサンプルスクリプトでは save
メソッドの引数 duration
にリストを指定することで、画像によって表示する時間を変更しています(最初と最後の画像だけ 2000 ms、他の画像は 50ms 表示)。
アニメーション GIF をキレイに作成する方法
作成したアニメーション GIF によっては下のようにチカチカすることがあります。
このチカチカする現象を解消する方法を紹介します。
スポンサーリンク
ディザを無くしてチカチカする現象を解消
結論を言うと、このチカチカする現象は「ディザを無くす」ことで解消できる場合があります。
ディザとは
JPEG などの画像ファイルは一般に「16777216色」を表現できますが、GIF では「256色」しか表現することができません。
したがって、JPEG などから GIF に変換する際に画像の劣化が発生します。特にグラデーション部分はノッペリした画像になってしまいます。
「ディザ」はこのノッペリ感を無くすために、ノイズを加えることで、より見栄えが元の画像に近いように施す処理のことを言います。
このディザが施された画像を拡大してみると、下のように斑模様に様々な色の点があることが確認できます。これがディザです。
「ディザ」については Wikipedia が図付きで解説していて分かりやすいです。詳しく知りたい方は読んでみると良いと思います。
参考 ディザWikipediaディザは前述の通り、より元画像に近い見栄えにすることができますが、このディザによりアニメーションがチカチカして見える場合があります。
特に下のように、画像の平坦部にディザが施されると(もともと背景はグレー1色なのに下のようにピクセルに様々な色がついている)、アニメーション表示時にチカチカするように見えてしまう傾向にあります。
ディザを無くす方法
そして、このディザはアニメーション GIF 作成時の save
メソッド内で自動的に実行されます。より具体的には save
メソッドの中で convert("P")
が実行され、この中でディザが施されるようです。
ただし、このディザは元々の画像のモードが "RGB"
の時のみに施されるようになっているようです。
したがって、元々の画像のモードを "RGB"
以外に変換しておくことで、ディザによってアニメーションがチカチカして見える現象を抑えることができます。
オススメなのは Image
クラスの quantize
メソッドを利用すことです。
quantize
メソッドは指定した色数(デフォルトでは 256 色)に画像の色数を減らして画像のモードを "P"
(パレットモード)に変換するメソッドになります。このメソッド実行後の画像オブジェクトでは、save
メソッドでアニメーション GIF を作成した場合でもディザ処理は行われないようです。
例えば徐々に画像が変化していくアニメーション GIF の作成で紹介したスクリプトの、frames
に画像オブジェクトを追加していく部分を下記のように書き換えれば、ディザを行わずにアニメーション GIF を作成することができます。
# 画像を格納するリスト(空)を作成
frames = []
# beforeからafterへ徐々に変化させていく
for a in range(0, 101, 4):
# beforeとafterの混ぜ具合を設定
alpha = a / 100
# beforeとafterをalphaの混ぜ具合でブレンド
blended_image = Image.blend(before, after, alpha)
# ディザを避けるために事前にPモードに変換
quantized_image = blended_image.quantize(method=0)
# 作成した画像オブジェクトをリストに追加
frames.append(quantized_image)
先ほどチカチカしていたアニメーション GIF も、ディザを無くすことで下のような結果になります。
quantize
メソッドには引数 method
を指定することができ、これにより出来上がりの画像の画質や処理速度が異なるようです。
試した感じだと method=0
だと画質がキレイだけど処理が遅く、method=2
だと画質がイマイチだけど処理速度が速い印象を受けました。
前述の通り、ディザ自体は画像の見栄えをよくするためのものです。特に自然画などのグラデーションが多い場合はディザを有効にしたほうがキレイに見えることも多いです。
基本はディザを有効にしておき、作成したアニメーションが気に入らない場合に、ディザを無くしてみるのが良いと思います。
まとめ
このページでは Python でアニメーション GIF を作成する方法について解説しました。
アニメーション GIF は PIL を利用すれば save
メソッドを実行するだけで簡単に作成することができます。
ただし事前に複数の画像データを用意することと、save
メソッドの引数に save_all=True
と append_images
を指定することを忘れないようにしましょう。
アニメーションがチカチカする現象が気になる場合はディザを無くすことで改善するケースもありますので、こちらもぜひ試してみてください!
[…] 画像透過:https://symfoware.blog.fc2.com/blog-entry-1532.html アニメーション化:https://daeudaeu.com/anime_gif/ […]