PillowとMatplotlibを使ってアニメーションをやってみました。
作成する図形はマンデルブロ集合というフラクタル図形にしました。
VSCodeのJupyter Notebookでアニメーション表示することができます。
適当なコードですみませんが、多少コメントを付けて貼ります。
アニメーション&フラクタル図形の計算で処理が重めです。N(画素数)とframes(フレーム数)を減らすと軽くなります。
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from IPython import display
from matplotlib import rcParams
from matplotlib.animation import ArtistAnimation
rcParams['animation.embed_limit'] = 2**128 # アニメーションのサイズ上限を変更
# マンデルブロ集合のパラメータ
N = 1000
epsilon = 0.1
center = [-0.53, 0.613]
frames = 100 # フレーム数を100に制限
def fn(z, c):
return z*z + c
def convergence(c):
z = 0.0
i = 0
while i < 100:
z = fn(z, c)
if abs(z) > 2:
return i
i += 1
return i
def display_mandelbrot_set():
grid = np.zeros((N, N), dtype=np.uint8)
points_x = np.linspace(center[0]-epsilon, center[0]+epsilon, N)
points_y = np.linspace(center[1]-epsilon, center[1]+epsilon, N)
image = Image.new("RGBA", (N, N), tuple([0, 0, 0, 0]))
plt.axis("off")
plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False)
fig = plt.figure(figsize=(15, 15))
im = plt.imshow(np.array(image), vmin=0, vmax=100, cmap="Blues_r") # 初期値
ims = [[im]] # 画像(アニメーションのフレーム)のリスト
frate = N * N / frames
i = 0
for n, rec in enumerate(points_x):
for m, imc in enumerate(points_y):
c = rec + imc*1J
grid[m, n] = convergence(c) # 画像(フレーム)に値を入れる
if i % frate == 0: # アニメーションのフレーム数を100に制限
im = plt.imshow(np.array(grid), vmin=0,
vmax=100, cmap="Blues_r",
animated=True) # 画像の作成
ims.append([im]) # 画像を追加
i += 1
ani = ArtistAnimation(fig, ims, interval=0.1, blit=True, repeat_delay=100)
html = display.HTML(ani.to_jshtml())
display.display(html) # Jupyter NotebookのためにHTML形式で表示
ani.save("mandelbrot_set.gif", writer="pillow") # GIF形式で保存
plt.close(fig)
display_mandelbrot_set()
アニメーション作成処理が重くなってしまったのでフレーム数は100に絞りました。
1ピクセルずつ計算する過程をアニメーションにしている感じになりました。
図形が動くようなアニメーションも作ってみます。
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from IPython import display
from matplotlib import rcParams
from matplotlib.animation import ArtistAnimation
rcParams['animation.embed_limit'] = 2**128
solutions = np.array([2.7693, 0.11535-0.58974J, 0.11535+0.58974J])
N = 300
epsilon = 0.1
center = [-0.530, 0.613]
frames = 100
def fn(z, c):
return z*z + c
def convergence(c):
z = 0.0
i = 0
while i < 100:
z = fn(z, c)
if abs(z) > 2:
return i
i += 1
return i
def display_mandelbrot_set(center:list) -> np.array:
grid = np.zeros((N, N), dtype=np.uint8)
points_x = np.linspace(center[0]-epsilon, center[0]+epsilon, N)
points_y = np.linspace(center[1]-epsilon, center[1]+epsilon, N)
for n, rec in enumerate(points_x):
for m, imc in enumerate(points_y):
c = rec + imc*1J
grid[m, n] = convergence(c)
return grid
if __name__ == '__main__':
image = Image.new("RGBA", (N, N), tuple([0, 0, 0, 0]))
plt.axis("off")
plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False)
fig = plt.figure(figsize=(15, 15))
im = plt.imshow(np.array(image), vmin=0, vmax=100, cmap="Blues_r")
ims = [[im]]
for frame in range(frames):
grid = display_mandelbrot_set(center) # 画像(フレーム)を取得
im = plt.imshow(np.array(grid), vmin=0, vmax=100, cmap="Blues_r", animated=True)
ims.append([im]) # 画像を追加
center = [x - 0.001 for x in center] # 中心を少しだけずらす
ani = ArtistAnimation(fig, ims, interval=100, blit=True, repeat_delay=1000)
html = display.HTML(ani.to_jshtml())
display.display(html)
ani.save("mandelbrot_set.gif", writer="pillow")
plt.close(fig)
これは中心center
を少しずつ動かしているだけですが、アニメーションらしくなりました。
matplotlib 3.9.1
pillow 10.4.0
参考)マンデルブロ集合のコードをお借りました