seanshahkarami commited on
Commit
47b3fde
·
1 Parent(s): 54db940
Files changed (2) hide show
  1. app.py +111 -0
  2. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from scipy.fft import fftn, ifftn, fftshift, fftfreq
3
+ import streamlit as st
4
+ import matplotlib.pyplot as plt
5
+ import subprocess
6
+
7
+ image_seed = st.sidebar.slider("Image Seed", 0, 10000, 0)
8
+ transform_seed = st.sidebar.slider("Transform Seed", 0, 10000, 0)
9
+ transform_level = st.sidebar.slider("Transform Level", 0.0, 100.0, 0.0)
10
+
11
+
12
+ def random_channel(n, rand, fpower=2.0):
13
+ freq = fftn(rand.rand(n, n))
14
+
15
+ fx = fftfreq(n)[:, None]
16
+ fy = fftfreq(n)[None, :]
17
+
18
+ # combine as l2 norm of freq
19
+ f = (fx**2 + fy**2) ** 0.5
20
+
21
+ i = f > 0
22
+ freq[i] /= f[i] ** fpower
23
+ freq[0, 0] = 0.0
24
+
25
+ data = np.real(ifftn(freq))
26
+ data -= data.min()
27
+ data /= data.max()
28
+ return data
29
+
30
+
31
+ @st.cache_data
32
+ def random_gray(n, seed, fpower=2.0):
33
+ rand = np.random.RandomState(seed)
34
+ return random_channel(n, rand, fpower)
35
+
36
+
37
+ @st.cache_data
38
+ def random_color(n, seed):
39
+ rand = np.random.RandomState(seed)
40
+ return np.stack([random_channel(n, rand) for _ in range(3)], 2)
41
+
42
+
43
+ def rotate_image(img, i, j, a):
44
+ c = np.cos(a)
45
+ s = np.sin(a)
46
+
47
+ img = 2.0 * img - 1.0
48
+ x, y = img[:, :, i], img[:, :, j]
49
+ img[:, :, i], img[:, :, j] = c * x + s * y, -s * x + c * y
50
+ img -= img.min()
51
+ img /= img.max()
52
+ return img
53
+
54
+
55
+ n = 512
56
+ img = random_color(n, image_seed)
57
+
58
+ a = random_color(n, transform_seed) * transform_level
59
+ img = rotate_image(img, 0, 1, a[:, :, 0])
60
+ img = rotate_image(img, 0, 2, a[:, :, 1])
61
+ img = rotate_image(img, 1, 2, a[:, :, 2])
62
+
63
+ """
64
+ # Output
65
+
66
+ Play around with the parameters on the left to change the image.
67
+ """
68
+
69
+ st.image(img, width=600)
70
+
71
+ """
72
+ The image is generated as follows:
73
+
74
+ 1. Generate 6D cube of "brownian noise". We'll name these coordinates (R, G, B, A1, A2, A3).
75
+ 2. Apply a rotation of angle A1*Level to (R, G) plane.
76
+ 3. Apply a rotation of angle A2*Level to (R, B) plane.
77
+ 4. Apply a rotation of angle A3*Level to (G, B) plane.
78
+ 5. The resulting (R, G, B) data is the image.
79
+
80
+ # Background
81
+
82
+ I was inspired to write this after seeing some neat examples from the [accidental noise library](http://accidentalnoise.sourceforge.net).
83
+
84
+ My approach to doing this was:
85
+
86
+ 1. Generate nice looking 2D noise. I found that a 2D analog of [brownian noise](https://en.wikipedia.org/wiki/Colors_of_noise#Brownian_noise) looked pretty good.
87
+ 2. Stack 6 independent channels to form the 6D cube.
88
+ 3. Apply the rotations outlined above.
89
+
90
+ Out of these steps, I found the most interesting bit to be generating the 2D noise.
91
+
92
+ There seem to be many approaches for doing this, including classic approaches like [Perlin](https://en.wikipedia.org/wiki/Perlin_noise) and [Simplex](https://en.wikipedia.org/wiki/Simplex_noise) noise. Since I had access to good numeric libraries, I ended up going straight to the following FFT based approach.
93
+
94
+ First, we generate a 2D image of uniformly sampled random noise. It pretty much looks like the static on your TV a.k.a. white noise.
95
+ """
96
+
97
+ st.image(random_gray(512, 32, 0.0))
98
+
99
+ """
100
+ White noise looks pretty harsh, so we want to smooth it out to get brownian noise.
101
+
102
+ To do this, we take the 2D FFT to get to the frequency domain and then rescale things so they fall off like 1/f^2. To be nitpicky, I only found a general definition of brownian noise in 1D. Still... we're doing something very similar by rescaling by "one over the Euclidean norm squared".
103
+
104
+ Finally, we take the inverse 2D FFT to get our image back.
105
+ """
106
+
107
+ st.image(random_gray(512, 32))
108
+
109
+ """
110
+ Noice!
111
+ """
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ numpy
2
+ scipy
3
+ matplotlib
4
+ streamlit