Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
2import torch
5def interpolant(t):
6 """
7 Taken from https://github.com/pvigier/perlin-numpy/
8 """
9 return t*t*t*(t*(t*6 - 15) + 10)
12def shape_for_fractal_noise(shape, res, octaves, lacunarity):
13 shape = list(shape)
14 for i, (s, r) in enumerate(zip(shape, res)):
15 multiple_of = lacunarity**(octaves-1)*r
16 if s % multiple_of != 0:
17 shape[i] += multiple_of - (s % multiple_of)
18 return tuple(shape)
21def generate_perlin_noise_2d(
22 shape, res, tileable=(False, False), interpolant=interpolant
23):
24 """Generate a 2D numpy array of perlin noise.
26 Taken from https://github.com/pvigier/perlin-numpy/
28 Args:
29 shape: The shape of the generated array (tuple of two ints).
30 This must be a multple of res.
31 res: The number of periods of noise to generate along each
32 axis (tuple of two ints). Note shape must be a multiple of
33 res.
34 tileable: If the noise should be tileable along each axis
35 (tuple of two bools). Defaults to (False, False).
36 interpolant: The interpolation function, defaults to
37 t*t*t*(t*(t*6 - 15) + 10).
39 Returns:
40 A numpy array of shape shape with the generated noise.
42 Raises:
43 ValueError: If shape is not a multiple of res.
44 """
45 delta = (res[0] / shape[0], res[1] / shape[1])
46 d = (shape[0] // res[0], shape[1] // res[1])
47 grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]]\
48 .transpose(1, 2, 0) % 1
49 # Gradients
50 angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
51 gradients = np.dstack((np.cos(angles), np.sin(angles)))
52 if tileable[0]:
53 gradients[-1,:] = gradients[0,:]
54 if tileable[1]:
55 gradients[:,-1] = gradients[:,0]
56 gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
57 g00 = gradients[ :-d[0], :-d[1]]
58 g10 = gradients[d[0]: , :-d[1]]
59 g01 = gradients[ :-d[0],d[1]: ]
60 g11 = gradients[d[0]: ,d[1]: ]
61 # Ramps
62 n00 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1] )) * g00, 2)
63 n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1] )) * g10, 2)
64 n01 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1]-1)) * g01, 2)
65 n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)
66 # Interpolation
67 t = interpolant(grid)
68 n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
69 n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
70 return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)
73def generate_fractal_noise_2d(
74 shape, res, octaves=1, persistence=0.5,
75 lacunarity=2, tileable=(False, False),
76 interpolant=interpolant
77):
78 """Generate a 2D numpy array of fractal noise.
80 Adapted from https://github.com/pvigier/perlin-numpy/
82 Args:
83 shape: The shape of the generated array (tuple of two ints).
84 This must be a multiple of lacunarity**(octaves-1)*res.
85 res: The number of periods of noise to generate along each
86 axis (tuple of two ints). Note shape must be a multiple of
87 (lacunarity**(octaves-1)*res).
88 octaves: The number of octaves in the noise. Defaults to 1.
89 persistence: The scaling factor between two octaves.
90 lacunarity: The frequency factor between two octaves.
91 tileable: If the noise should be tileable along each axis
92 (tuple of two bools). Defaults to (False, False).
93 interpolant: The, interpolation function, defaults to
94 t*t*t*(t*(t*6 - 15) + 10).
96 Returns:
97 A numpy array of fractal noise and of shape shape generated by
98 combining several octaves of perlin noise.
99 """
101 # Make shape bigger if necessary
102 fractal_shape = shape_for_fractal_noise(shape, res, octaves, lacunarity)
104 noise = np.zeros(fractal_shape)
105 frequency = 1
106 amplitude = 1
107 for _ in range(octaves):
108 noise += amplitude * generate_perlin_noise_2d(
109 fractal_shape, (frequency*res[0], frequency*res[1]), tileable, interpolant
110 )
111 frequency *= lacunarity
112 amplitude *= persistence
114 # Crop if necessary
115 if fractal_shape != shape:
116 noise = noise[:shape[0], :shape[1]]
118 return noise
121def generate_perlin_noise_3d(
122 shape, res, tileable=(False, False, False),
123 interpolant=interpolant
124):
125 """Generate a 3D numpy array of perlin noise.
127 Taken from https://github.com/pvigier/perlin-numpy/
129 Args:
130 shape: The shape of the generated array (tuple of three ints).
131 This must be a multiple of res.
132 res: The number of periods of noise to generate along each
133 axis (tuple of three ints). Note shape must be a multiple
134 of res.
135 tileable: If the noise should be tileable along each axis
136 (tuple of three bools). Defaults to (False, False, False).
137 interpolant: The interpolation function, defaults to
138 t*t*t*(t*(t*6 - 15) + 10).
140 Returns:
141 A numpy array of shape shape with the generated noise.
143 Raises:
144 ValueError: If shape is not a multiple of res.
145 """
146 delta = (res[0] / shape[0], res[1] / shape[1], res[2] / shape[2])
147 d = (shape[0] // res[0], shape[1] // res[1], shape[2] // res[2])
148 grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1],0:res[2]:delta[2]]
149 grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1],0:res[2]:delta[2]]
150 grid = grid.transpose(1, 2, 3, 0) % 1
151 # Gradients
152 theta = 2*np.pi*np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)
153 phi = 2*np.pi*np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)
154 gradients = np.stack(
155 (np.sin(phi)*np.cos(theta), np.sin(phi)*np.sin(theta), np.cos(phi)),
156 axis=3
157 )
158 if tileable[0]:
159 gradients[-1,:,:] = gradients[0,:,:]
160 if tileable[1]:
161 gradients[:,-1,:] = gradients[:,0,:]
162 if tileable[2]:
163 gradients[:,:,-1] = gradients[:,:,0]
164 gradients = gradients.repeat(d[0], 0).repeat(d[1], 1).repeat(d[2], 2)
165 g000 = gradients[ :-d[0], :-d[1], :-d[2]]
166 g100 = gradients[d[0]: , :-d[1], :-d[2]]
167 g010 = gradients[ :-d[0],d[1]: , :-d[2]]
168 g110 = gradients[d[0]: ,d[1]: , :-d[2]]
169 g001 = gradients[ :-d[0], :-d[1],d[2]: ]
170 g101 = gradients[d[0]: , :-d[1],d[2]: ]
171 g011 = gradients[ :-d[0],d[1]: ,d[2]: ]
172 g111 = gradients[d[0]: ,d[1]: ,d[2]: ]
173 # Ramps
174 n000 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1] , grid[:,:,:,2] ), axis=3) * g000, 3)
175 n100 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1] , grid[:,:,:,2] ), axis=3) * g100, 3)
176 n010 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1]-1, grid[:,:,:,2] ), axis=3) * g010, 3)
177 n110 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1]-1, grid[:,:,:,2] ), axis=3) * g110, 3)
178 n001 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1] , grid[:,:,:,2]-1), axis=3) * g001, 3)
179 n101 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1] , grid[:,:,:,2]-1), axis=3) * g101, 3)
180 n011 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1]-1, grid[:,:,:,2]-1), axis=3) * g011, 3)
181 n111 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1]-1, grid[:,:,:,2]-1), axis=3) * g111, 3)
182 # Interpolation
183 t = interpolant(grid)
184 n00 = n000*(1-t[:,:,:,0]) + t[:,:,:,0]*n100
185 n10 = n010*(1-t[:,:,:,0]) + t[:,:,:,0]*n110
186 n01 = n001*(1-t[:,:,:,0]) + t[:,:,:,0]*n101
187 n11 = n011*(1-t[:,:,:,0]) + t[:,:,:,0]*n111
188 n0 = (1-t[:,:,:,1])*n00 + t[:,:,:,1]*n10
189 n1 = (1-t[:,:,:,1])*n01 + t[:,:,:,1]*n11
190 return ((1-t[:,:,:,2])*n0 + t[:,:,:,2]*n1)
193def generate_fractal_noise_3d(
194 shape, res, octaves=1, persistence=0.5, lacunarity=2,
195 tileable=(False, False, False), interpolant=interpolant
196):
197 """Generate a 3D numpy array of fractal noise.
199 Adapted from https://github.com/pvigier/perlin-numpy/
201 Args:
202 shape: The shape of the generated array (tuple of three ints).
203 This must be a multiple of lacunarity**(octaves-1)*res.
204 res: The number of periods of noise to generate along each
205 axis (tuple of three ints). Note shape must be a multiple of
206 (lacunarity**(octaves-1)*res).
207 octaves: The number of octaves in the noise. Defaults to 1.
208 persistence: The scaling factor between two octaves.
209 lacunarity: The frequency factor between two octaves.
210 tileable: If the noise should be tileable along each axis
211 (tuple of three bools). Defaults to (False, False, False).
212 interpolant: The, interpolation function, defaults to
213 t*t*t*(t*(t*6 - 15) + 10).
215 Returns:
216 A numpy array of fractal noise and of shape shape generated by
217 combining several octaves of perlin noise.
218 """
220 # Make shape bigger if necessary
221 fractal_shape = shape_for_fractal_noise(shape, res, octaves, lacunarity)
223 noise = np.zeros(fractal_shape)
224 frequency = 1
225 amplitude = 1
226 for _ in range(octaves):
227 noise += amplitude * generate_perlin_noise_3d(
228 fractal_shape,
229 (frequency*res[0], frequency*res[1], frequency*res[2]),
230 tileable,
231 interpolant
232 )
233 frequency *= lacunarity
234 amplitude *= persistence
236 # Crop if necessary
237 if fractal_shape != shape:
238 noise = noise[:shape[0], :shape[1], :shape[2]]
240 return noise
243class FractalNoiseTensor():
244 def __init__(self, shape):
245 """
246 Args:
247 shape (tuple): The shape of the output array.
249 Adapted from https://github.com/pvigier/perlin-numpy/
250 """
251 self.shape = shape
252 self.dim = len(shape)
253 self.func = generate_fractal_noise_3d if self.dim == 3 else generate_fractal_noise_2d
255 def __call__(self, *args, **kwargs):
256 res = np.random.choice([1,2,4] if self.dim == 3 else [1,2,4,8])
257 octaves = np.random.randint(low=1, high=5 if self.dim == 3 else 7)
258 res_tuple = (res,) * self.dim
260 x = self.func(self.shape, res=res_tuple, octaves=octaves)
262 x = torch.from_numpy(x).float()
263 x = (x - x.min())/(x.max()-x.min())*2.0 - 1.0
264 x = x.unsqueeze(0)
265 return x