やること
部分的に太さが違う、あるいは部分的に模様が違う、そんなチューリング・パターンを作ってみましょう。
必修科目
こちらを履修しておいてください。
実行環境
WinPythonかGoogle Colaboratoryを用います。
WinPython3.6をおすすめしています。
WinPython - Browse /WinPython_3.6/3.6.7.0 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows
Google Colaboratoryが利用可能です。
Google Colab
どうやって部分的に太さを変えるか
17-7では、次のような円形の畳み込みフィルタを用いて、各セルの状態を更新していきました。畳み込んだ結果が0より大きければ1(True)、0以下であれば0(False)となります。
このとき、r1, r2の規模によってパターンの太さが異なることが分かりました。
r1=3, r2=6
r1=10, r2=20
そこで今回は、規模が異なる複数のフィルターを用意しておいて、フィールドのある部分では小さいフィルターを、ある部分では大きいフィルターを、というように使い分けてみます。
フィールドのどの部分で何番目のフィルターを使うかは、次のとおりとします。
中央が太く、両端が細くなるのではないかと想像しています。
ソースコード
場所によって使うフィルターが違うと、前回までのように scipy.signal.convolv2d() でお手軽に畳み込むことができません。泣きながら手動で畳み込むコードを書きました。
import numpy as np
import numpy.random as nr
import matplotlib.pyplot as plt
#============================
#設定
#============================
#フィールドサイズ
h, w = 90, 160
#パラメータ
r1_range = range(3, 11, 1) # 個数は合わせる
r2_range = range(6, 21, 2) # 個数は合わせる
w1 = 10
w2 = -3.2
#終了ステップ数
max_step = 20
#============================
#メイン処理
#============================
#フィールドの初期化
#f = np.zeros((h, w), dtype=bool) #0で初期化
f = nr.randint(0, 2, (h, w), dtype=bool) #ランダムに初期化
#畳み込み用のフィルタを作る関数
def make_filter(r1, r2):
g = np.zeros((r2*2 - 1, r2*2 - 1))
for i in range(len(g)):
for j in range(len(g[0])):
if (i + 1 - r2)**2 + (j + 1 - r2)**2 < r1**2:
g[i, j] = w1
if r1**2 <= (i + 1 - r2)**2 + (j + 1 - r2)**2 < r2**2:
g[i, j] = w2
return g
#規模が異なるフィルタを複数作成しておく
g_all = []
for i in range(len(r1_range)):
g_all.append(make_filter(r1_range[i], r2_range[i]))
print('フィルターの個数:{}'.format(len(g_all)))
#全フィルターの確認
for g in g_all:
plt.imshow(g, cmap='RdGy', vmin=-15, vmax=15)
plt.show(), print()
#フィールドの拡張幅
s = r2_range[-1]
#フィールドの各地点でどのフィルタを用いるか
c = np.tile(np.hstack([np.linspace(0, len(g_all), w//2, endpoint=False), np.linspace(0, len(g_all), w//2, endpoint=False)[::-1]]), (h, 1))
#切り捨てて整数に
c = np.array(c, dtype=int)
#表示
plt.imshow(c, cmap='binary')
plt.show(), print()
#初期状態の表示
plt.imshow(f, cmap='binary', vmin=0, vmax=1)
#plt.savefig('save/{}.png'.format(0), bbox_inches='tight', pad_inches=0)
plt.show(), print()
#状態の更新
for n in range(1, max_step + 1):
#畳み込みとフィールドの更新、0より大きければ生存(True)、0以下であれば死(False)
#f = signal.convolve2d(f, g, mode='same',boundary='wrap') >= 0
#フィールドを拡張しておく
f = np.hstack([f[:, -s:], f, f[:, :s]])
f = np.vstack([f[-s:, :], f, f[:s, :]])
#手動で畳み込む
f_new = np.zeros((h, w), dtype=bool)
for i in range(h):
for j in range(w):
#この地点でのフィルターの選択
g = g_all[c[i, j]]
#フィルターの半径
g_r = g.shape[0] // 2
#拡張フィールドにおける座標
center = (i + s, j + s)
#拡張フィールドをフィルターサイズに切り取る
f_cut = f[center[0] - g_r : center[0] + g_r + 1, center[1] - g_r : center[1] + g_r + 1]
#畳み込み
f_new[i, j] = np.sum(f_cut * g) >= 0
#フィールドの更新
f = f_new
#表示
plt.imshow(f, cmap='binary', vmin=0, vmax=1)
#plt.savefig('save/{}.png'.format(n), bbox_inches='tight', pad_inches=0)
plt.show(), print()
結果
フィルターの個数:8
いいんじゃないでしょうか!
どうやって部分的に模様を変えるのか
一方で、斑点になるか線になるかは、w1, w2のバランスによって決まるのでした。
w1=10, w2=-3.5
w1=10, w2=-3.0
w1=10, w2=-2.5
ということで、今度は場所によってw2を変えてみます。
w2_range = np.arange(-4.0, -2.2, 0.1)
という18段階にして、フィールドのどの部分で何番目のフィルターを使うかは、次のとおりとします。(この図、伝わるのか?)
ソースコード
import numpy as np
import numpy.random as nr
import matplotlib.pyplot as plt
#============================
#設定
#============================
#フィールドサイズ
h, w = 90, 160
#パラメータ
r1 = 5
r2 = 10
w1 = 10
w2_range = np.arange(-4.0, -2.2, 0.1)
#終了ステップ数
max_step = 20
#============================
#メイン処理
#============================
#フィールドの初期化
#f = np.zeros((h, w), dtype=bool) #0で初期化
f = nr.randint(0, 2, (h, w), dtype=bool) #ランダムに初期化
#畳み込み用のフィルタを作る関数
def make_filter(w1, w2):
g = np.zeros((r2*2 - 1, r2*2 - 1))
for i in range(len(g)):
for j in range(len(g[0])):
if (i + 1 - r2)**2 + (j + 1 - r2)**2 < r1**2:
g[i, j] = w1
if r1**2 <= (i + 1 - r2)**2 + (j + 1 - r2)**2 < r2**2:
g[i, j] = w2
return g
#規模が異なるフィルタを複数作成しておく
g_all = []
for i in range(len(w2_range)):
g_all.append(make_filter(w1, w2_range[i]))
print('フィルターの個数:{}'.format(len(g_all)))
#フィールドの拡張幅
s = r2
"""
以下同じ
"""
結果
フィルターの個数:18
こちらも良くできています!やりました!