add Perlin noise as distribution
This commit is contained in:
parent
ba0834d729
commit
dc3503a407
25
main.py
25
main.py
@ -1,13 +1,11 @@
|
|||||||
import io
|
import io
|
||||||
import os
|
from perlin import CoordsGenerator
|
||||||
import matplotlib
|
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.font_manager as font_manager
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@ -90,12 +88,13 @@ marker_subs = {
|
|||||||
@app.get("/speckles/")
|
@app.get("/speckles/")
|
||||||
def make_wallpaper(
|
def make_wallpaper(
|
||||||
speckle_colours: str,
|
speckle_colours: str,
|
||||||
density: float | None = 0.12,
|
density: float = 0.12,
|
||||||
size: float | None = 3,
|
size: float = 3,
|
||||||
fileformat: str = "svg",
|
fileformat: str = "svg",
|
||||||
orientation: str | None = "landscape",
|
orientation: str = "landscape",
|
||||||
local: bool = False,
|
local: bool = False,
|
||||||
markers: str | None = ".",
|
markers: str = ".",
|
||||||
|
perlin: bool = True,
|
||||||
):
|
):
|
||||||
if fileformat not in MEDIA_TYPES:
|
if fileformat not in MEDIA_TYPES:
|
||||||
return
|
return
|
||||||
@ -126,15 +125,25 @@ def make_wallpaper(
|
|||||||
ax.set_yticks([])
|
ax.set_yticks([])
|
||||||
ax.margins(0, 0)
|
ax.margins(0, 0)
|
||||||
|
|
||||||
|
if perlin:
|
||||||
|
gen = CoordsGenerator(y, x)
|
||||||
|
|
||||||
for color, marker, size in itertools.product(
|
for color, marker, size in itertools.product(
|
||||||
speckle_colours,
|
speckle_colours,
|
||||||
markers,
|
markers,
|
||||||
np.logspace(0, size, 10, base=np.exp(2)),
|
np.logspace(0, size, 10, base=np.exp(2)),
|
||||||
):
|
):
|
||||||
marker = marker_subs.get(marker, marker)
|
marker = marker_subs.get(marker, marker)
|
||||||
ax.scatter(
|
if perlin:
|
||||||
|
x_coords, y_coords = gen.pick(speckles_per_colour)
|
||||||
|
else:
|
||||||
|
x_coords, y_coords = (
|
||||||
[random.random() * x / 8 for _ in range(speckles_per_colour)],
|
[random.random() * x / 8 for _ in range(speckles_per_colour)],
|
||||||
[random.random() * y / 8 for _ in range(speckles_per_colour)],
|
[random.random() * y / 8 for _ in range(speckles_per_colour)],
|
||||||
|
)
|
||||||
|
ax.scatter(
|
||||||
|
x_coords,
|
||||||
|
y_coords,
|
||||||
c=color,
|
c=color,
|
||||||
s=size,
|
s=size,
|
||||||
marker=marker,
|
marker=marker,
|
||||||
|
84
perlin.py
Normal file
84
perlin.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import numpy as np
|
||||||
|
from scipy.special import softmax
|
||||||
|
|
||||||
|
|
||||||
|
def interpolant(t):
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_perlin_noise_2d(
|
||||||
|
shape, res, tileable=(False, False), interpolant=interpolant
|
||||||
|
):
|
||||||
|
"""Generate a 2D numpy array of perlin noise.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shape: The shape of the generated array (tuple of two ints).
|
||||||
|
This must be a multple of res.
|
||||||
|
res: The number of periods of noise to generate along each
|
||||||
|
axis (tuple of two ints). Note shape must be a multiple of
|
||||||
|
res.
|
||||||
|
tileable: If the noise should be tileable along each axis
|
||||||
|
(tuple of two bools). Defaults to (False, False).
|
||||||
|
interpolant: The interpolation function, defaults to
|
||||||
|
t*t*t*(t*(t*6 - 15) + 10).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A numpy array of shape shape with the generated noise.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If shape is not a multiple of res.
|
||||||
|
"""
|
||||||
|
delta = (res[0] / shape[0], res[1] / shape[1])
|
||||||
|
d = (shape[0] // res[0], shape[1] // res[1])
|
||||||
|
grid = np.mgrid[0 : res[0] : delta[0], 0 : res[1] : delta[1]].transpose(1, 2, 0) % 1
|
||||||
|
# Gradients
|
||||||
|
angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
|
||||||
|
gradients = np.dstack((np.cos(angles), np.sin(angles)))
|
||||||
|
if tileable[0]:
|
||||||
|
gradients[-1, :] = gradients[0, :]
|
||||||
|
if tileable[1]:
|
||||||
|
gradients[:, -1] = gradients[:, 0]
|
||||||
|
gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
|
||||||
|
g00 = gradients[: -d[0], : -d[1]]
|
||||||
|
g10 = gradients[d[0] :, : -d[1]]
|
||||||
|
g01 = gradients[: -d[0], d[1] :]
|
||||||
|
g11 = gradients[d[0] :, d[1] :]
|
||||||
|
# Ramps
|
||||||
|
n00 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1])) * g00, 2)
|
||||||
|
n10 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2)
|
||||||
|
n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2)
|
||||||
|
n11 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2)
|
||||||
|
# Interpolation
|
||||||
|
t = interpolant(grid)
|
||||||
|
n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
|
||||||
|
n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11
|
||||||
|
return np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)
|
||||||
|
|
||||||
|
|
||||||
|
class CoordsGenerator:
|
||||||
|
def __init__(self, x: int, y: int, factor: int = 500):
|
||||||
|
print(x, y, x // factor, y // factor)
|
||||||
|
self.noise = generate_perlin_noise_2d((x, y), (x // factor, y // factor))
|
||||||
|
self.noise_distribution = softmax(self.noise, axis=1)
|
||||||
|
|
||||||
|
def pick(self, n):
|
||||||
|
x, y = self.noise.shape
|
||||||
|
x = np.random.choice(x, size=n, replace=False)
|
||||||
|
y = [
|
||||||
|
np.random.choice(y, size=1, p=self.noise_distribution[x_, :], replace=False) for x_ in x
|
||||||
|
]
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# gen = CoordsGenerator(1920, 1080, threshold=0.85)
|
||||||
|
# x, y = gen.pick(1000)
|
||||||
|
# plt.scatter(x, y)
|
||||||
|
factor = 500
|
||||||
|
noise = generate_perlin_noise_2d((1080, 1920), (1080 // factor, 1920 // factor))
|
||||||
|
|
||||||
|
plt.matshow(noise, cmap="bwr")
|
||||||
|
plt.colorbar()
|
||||||
|
plt.show()
|
Loading…
Reference in New Issue
Block a user