perlin noiseをPILでつくった

パーリンノイズ(perlin noise)ってやつを作ってみたいなと思っていたが、道具さえ選べば簡単だった。

まじめにやると、画像の補間法をちゃんと勉強しなければいけないんだけど、pythonのPILモジュールは画像の拡大にバイキュービック法が使えるようだったので、これを活用するとよい。

まず、2x2ピクセルのモノクロ画像をつくり、全ピクセル(といっても4つ)を0から255のランダムな明度で埋める。そののち、適当なサイズに拡大する。このときの拡大はバイキュービック法で。できる画像は下のとおり。

f:id:yamatt2:20140213101052p:plain

次に、4x4ピクセルのモノクロ画像をつくって、同じようなことをしてから、さっきと同じサイズに拡大する。

f:id:yamatt2:20140213101104p:plain

次に8x8。

f:id:yamatt2:20140213101112p:plain

16x16、32x32、64x64… と、順々に同様の画像をつくる。すこしづつ、画像を暗くしていくのがミソ。つまり、はじめは0から255の明度にしたけど、次は0から216、次は0から183まで…といったように、暗めの範囲を使うようにしていく。

f:id:yamatt2:20140213101120p:plain
f:id:yamatt2:20140213101127p:plain
f:id:yamatt2:20140213101131p:plain
f:id:yamatt2:20140213101140p:plain
f:id:yamatt2:20140213101144p:plain

最後にこれらの明度をすべて足し合わせた画像をつくればできあがり。単純に足せば明度は255を振り切ってしまうので、明度を足し合わせた結果をさらに0から255の範囲にとどまるように調整することが必要だけど。

f:id:yamatt2:20140213101203p:plain

スクリプトは下のよう(要PIL)。動作の調整には、size変数に最終的に作りたい画像サイズ(縦横同じ)、ampd変数に明度の減衰率をセットする程度。

#
# Generate Perlin noise
#

import random
from PIL import Image

size = 256
amp = 255
ampd = 0.85

buf = [[0 for _ in xrange(size)] for _ in xrange(size)]

o = 2
oa = [o,]
while o < size:
  o *= 2
  oa.append(o)

for x in oa:

  # generate seed image with random pixels
  img = Image.new("L", (x, x))
  px = img.load()
  for i in xrange(x):
    for j in xrange(x):
      px[j, i] = random.randint(0,amp)

  # magnify seed image (using any interpolation)
  img2 = img.resize((size,size), Image.BICUBIC)
  px2 = img2.load()
  for i in xrange(size):
    for j in xrange(size):
      buf[j][i] += px2[j,i]

  amp = int(amp * ampd)

mx = max([max(i) for i in buf])
mn = min([min(i) for i in buf])

img3 = Image.new("L", (size,size))
px3 = img3.load()
for i in xrange(size):
  for j in xrange(size):
    c = int((buf[j][i]-mn) * 255 / (mx-mn))
    px3[j,i] = c

img3.save("perlin.png")