Art generation with VQGAN + CLIP in Docker, with a simple web UI for anyone with a GPU. A simplified and expanded take on Kevin Costa's work.
aiai-artclipdockerdocker-composegpuhandcodedimagenetpythonpytorchtorchtorchvisionvqganvqgan-clip
1import numpy as np
2
3from PIL import Image
4
5
6def perlin_noise_2d(shape, res):
7 def interpolant(t):
8 return t * t * t * (t * (t * 6 - 15) + 10)
9
10 delta = (res[0] / shape[0], res[1] / shape[1])
11 d = (shape[0] // res[0], shape[1] // res[1])
12 grid = np.mgrid[0 : res[0] : delta[0], 0 : res[1] : delta[1]].transpose(1, 2, 0) % 1
13
14 # Gradients
15 angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
16 gradients = np.dstack((np.cos(angles), np.sin(angles)))
17 gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
18 g00 = gradients[: -d[0], : -d[1]]
19 g10 = gradients[d[0] :, : -d[1]]
20 g01 = gradients[: -d[0], d[1] :]
21 g11 = gradients[d[0] :, d[1] :]
22
23 # Ramps
24 n00 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1])) * g00, 2)
25 n10 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2)
26 n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2)
27 n11 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2)
28
29 # Interpolation
30 t = interpolant(grid)
31 n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
32 n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11
33 return np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)
34
35
36def fractal_noise_2d(shape, res, octaves=1, persistence=0.5, lacunarity=2):
37 noise = np.zeros(shape)
38 frequency = 1
39 amplitude = 1
40
41 for _ in range(octaves):
42 noise += amplitude * perlin_noise_2d(
43 shape, (frequency * res[0], frequency * res[1])
44 )
45 frequency *= lacunarity
46 amplitude *= persistence
47 return (noise - np.min(noise)) / (np.max(noise) - np.min(noise))
48
49
50def random_fractal_image(width, height):
51 _pow = int(np.ceil(np.log(max(width, height)) / np.log(2)))
52 octaves = _pow - 4
53 size = 2**_pow
54 r = fractal_noise_2d((size, size), (32, 32), octaves=octaves)
55 g = fractal_noise_2d((size, size), (32, 32), octaves=octaves)
56 b = fractal_noise_2d((size, size), (32, 32), octaves=octaves)
57
58 tile = np.dstack((r, g, b))[:height, :width, :]
59 return Image.fromarray((255.9 * tile).astype("uint8"))
60
61
62def random_noise_image(width, height):
63 return Image.fromarray(
64 np.random.randint(0, 255, (width, height, 3), dtype=np.dtype("uint8"))
65 )
66
67
68def gradient_2d(start, stop, width, height, is_horizontal):
69 if is_horizontal:
70 return np.tile(np.linspace(start, stop, width), (height, 1))
71 else:
72 return np.tile(np.linspace(start, stop, height), (width, 1)).T
73
74
75def gradient_3d(width, height, starts, stops, is_horizontal_list):
76 result = np.zeros((height, width, len(starts)), dtype=float)
77
78 for i, (start, stop, is_horizontal) in enumerate(
79 zip(starts, stops, is_horizontal_list)
80 ):
81 result[:, :, i] = gradient_2d(start, stop, width, height, is_horizontal)
82
83 return result
84
85
86def random_gradient_image(width, height):
87 array = gradient_3d(
88 width,
89 height,
90 (0, 0, np.random.randint(0, 255)),
91 (
92 np.random.randint(1, 255),
93 np.random.randint(2, 255),
94 np.random.randint(3, 128),
95 ),
96 (True, False, False),
97 )
98 random_image = Image.fromarray(np.uint8(array))
99 return random_image