1枚の1000x1000のJulia集合の画像作成が0.02秒余りでできるようになったので、定数Cを次々に変更すると、動画になると思われるので、やってみよう。
自分で画像を次々と作っては表示するという仕組みを作るのは面倒なので、PythonのGUIを利用することにしよう。
色々なGUIが用意されているが、ここではPythonに標準でついてくるTkinterというのを使うことにしよう。
今更Tkinterなのだが、PythonのGUIは種類がとても多く選ぶのに困る状態である。
そのため、Python標準のTkinterという安直この上ない選択をした。
ここでは凝ったGUIを作る予定はないので、あえて標準の古臭いTkinterにした。
実際にGUIを作る場合には、適切なGUIを選ぼう。
その前に、Julia集合を作成・テストに使っていたプログラムjulia_cy5.pyxを2つに分割する。
まず、Cython部分を示す。
# distutils: extra_compile_args = -fopenmp
# distutils: extra_link_args = -fopenmp
# cython: boundscheck=False
# cython: wraparound=False
from threading import Thread
from cython.parallel import *
import numpy as np
import matplotlib.pyplot as plt
import time
cdef int escape( double complex z, double complex c,
double z_max, int n_max) nogil:
cdef:
int i = 0
double z_max2 = z_max * z_max
while norm2(z) < z_max2 and i < n_max:
z = z * z + c
i += 1
return i
cdef inline double norm2(double complex z) nogil:
return z.real * z.real + z.imag * z.imag
def calc_julia( int resolution, double complex c,
double bound=1.2, double z_max=2.0, int n_max=1000 ):
cdef:
double step = 2.0 * bound / resolution
int i,j
double complex z
double real, imag
int[:,:] counts
counts = np.zeros((resolution+1,resolution+1), dtype=np.int32)
for i in prange(resolution+1,nogil=True,schedule='static',chunksize=1):
imag = bound - i * step
for j in range(resolution+1):
real = -bound + j * step
z = real + imag *1j
counts[i,j] = escape(z,c,z_max,n_max)
return np.asarray(counts)
calc_julia以下の部分をそのまま切り出しただけなので、説明は不要だろう。
残り部分を、Pythonのままで、ファイル名をjulia_move0.py とした。
## ジュリア集合を動かす
from threading import Thread
from cython.parallel import *
import numpy as np
import matplotlib.pyplot as plt
import time
from calc_julia import calc_julia
def julia():
tm_start = time.time()
jl = calc_julia(1000,(0.322+0.047j))
tm_end = time.time()
plt.figure(figsize=(10,10))
plt.imshow(np.log(jl
print("time={}".format(tm_end-tm_start))
plt.show()
if __name__ == '__main__':
julia()
これで、コマンドベースで直接起動することができる。
$ python3 julia_move0.py
time=0.024521589279174805
julia集合の画像も出てくるが、まったく同じものなので省略する。
Python部分をTkInter対応に書き換えてみよう。とりあえずは、1枚の画像を表示した後はメインループを回っているが、イベントの発生を何も書かなければ、最初の画像を表示し続けるだけである。
書き換えはPythonの部分だけである。
GUIの書き方の基本が分かっていれば、理解は簡単ではないかと思う。画像データをcanvasに貼り付ける部分の処理が多くなっているが、簡単なコメントをステップ毎に付けた。
途中でそのまま画像ファイルとしてセーブできるところがあり、今はコメントにしている。このコメントを外せば、画像ファイルが作られる。
PythonのGUIが初めての場合には、何らかの資料、本などで少し勉強した方が良いかもしれない。
## julia集合の画像をtkinterのcanvasに描く, 動的に変化する。
import calc_julia
import tkinter
from PIL import Image, ImageTk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
def draw_new_image():
global C_value, canvas, im
im = calc_julia.calc_julia(1000,C_value) ## Julia集合の計算
im = np.uint8(plt.get_cmap()(im)*255) ## defaultのカラーマップを適用し、uint8 に変換
im = Image.fromarray(im) ## <class 'PIL.Image.Image'>
## im.save('julia_move1.png') ## 画像ファイル(png)としてセーブできる
im = ImageTk.PhotoImage(image=im) ## Tkinterの標準のフォトイメージ
canvas.create_image( 500, 500, image=im ) ## Canvasにイメージを貼り付ける。(500,500)が中心
canvas.update() ## update()されると表示される
def gui_init():
global canvas, root
root = tkinter.Tk()
root.wm_title("Julia集合をtkinterのcanvasに動的に表示")
canvas_frame = tkinter.Frame(root) ## frameを作る
canvas_frame.pack() ## packする
canvas = tkinter.Canvas(canvas_frame,width=1001,height=1001) ## frameの中にcanvasを作る
canvas.pack(expand = tkinter.YES, fill = tkinter.BOTH) ## packして配置が決定
if __name__ == '__main__':
gui_init() ## GUIの初期化
C_value = complex(0.322,0.047) ## 初期定数C
draw_new_image() ## 初期表示
root.mainloop() ## メインループ(イベント待ち)
実行すると、ちゃんと同じ画像が出てくることが確かめられる。
ここまでできれば、動画にするのはあと一歩であるが、それは次回に回す。