Spaces:
Runtime error
Runtime error
Commit
·
47b3fde
1
Parent(s):
54db940
added app
Browse files- app.py +111 -0
- 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
|