Hide keyboard shortcuts

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 

3 

4 

5def interpolant(t): 

6 """  

7 Taken from https://github.com/pvigier/perlin-numpy/ 

8 """ 

9 return t*t*t*(t*(t*6 - 15) + 10) 

10 

11 

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) 

19 

20 

21def generate_perlin_noise_2d( 

22 shape, res, tileable=(False, False), interpolant=interpolant 

23): 

24 """Generate a 2D numpy array of perlin noise. 

25 

26 Taken from https://github.com/pvigier/perlin-numpy/ 

27 

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). 

38 

39 Returns: 

40 A numpy array of shape shape with the generated noise. 

41 

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) 

71 

72 

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. 

79 

80 Adapted from https://github.com/pvigier/perlin-numpy/ 

81 

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). 

95 

96 Returns: 

97 A numpy array of fractal noise and of shape shape generated by 

98 combining several octaves of perlin noise. 

99 """ 

100 

101 # Make shape bigger if necessary 

102 fractal_shape = shape_for_fractal_noise(shape, res, octaves, lacunarity) 

103 

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 

113 

114 # Crop if necessary 

115 if fractal_shape != shape: 

116 noise = noise[:shape[0], :shape[1]] 

117 

118 return noise 

119 

120 

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. 

126 

127 Taken from https://github.com/pvigier/perlin-numpy/ 

128 

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). 

139 

140 Returns: 

141 A numpy array of shape shape with the generated noise. 

142 

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) 

191 

192 

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. 

198 

199 Adapted from https://github.com/pvigier/perlin-numpy/ 

200 

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). 

214 

215 Returns: 

216 A numpy array of fractal noise and of shape shape generated by 

217 combining several octaves of perlin noise. 

218 """ 

219 

220 # Make shape bigger if necessary 

221 fractal_shape = shape_for_fractal_noise(shape, res, octaves, lacunarity) 

222 

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 

235 

236 # Crop if necessary 

237 if fractal_shape != shape: 

238 noise = noise[:shape[0], :shape[1], :shape[2]] 

239 

240 return noise 

241 

242 

243class FractalNoiseTensor(): 

244 def __init__(self, shape): 

245 """ 

246 Args: 

247 shape (tuple): The shape of the output array. 

248 

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 

254 

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 

259 

260 x = self.func(self.shape, res=res_tuple, octaves=octaves) 

261 

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