pulsar-clip / utils.py
neverix
Add full app
724a768
raw
history blame
4.71 kB
import numpy as np
import random
import torch
import math
def rotate_axis(x, add_angle=0, axis=1): # TODO Replace with a rotation matrix # But this is more fun
axes = list(range(3))
axes.remove(axis)
ax1, ax2 = axes
angle = torch.atan2(x[..., ax1], x[..., ax2])
if isinstance(add_angle, torch.Tensor):
while add_angle.ndim < angle.ndim:
add_angle = add_angle.unsqueeze(-1)
angle = angle + add_angle
dist = x.norm(dim=-1)
t = []
_, t = zip(*sorted([
(axis, x[..., axis]),
(ax1, torch.sin(angle) * dist),
(ax2, torch.cos(angle) * dist),
]))
return torch.stack(t, dim=-1)
noise_level = 0.5
# stolen from https://gist.github.com/ac1b097753f217c5c11bc2ff396e0a57
# ported from https://github.com/pvigier/perlin-numpy/blob/master/perlin2d.py
def rand_perlin_2d(shape, res, fade=lambda t: 6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3):
delta = (res[0] / shape[0], res[1] / shape[1])
d = (shape[0] // res[0], shape[1] // res[1])
grid = torch.stack(torch.meshgrid(torch.arange(0, res[0], delta[0]), torch.arange(0, res[1], delta[1])), dim=-1) % 1
angles = 2 * math.pi * torch.rand(res[0] + 1, res[1] + 1)
gradients = torch.stack((torch.cos(angles), torch.sin(angles)), dim=-1)
tile_grads = lambda slice1, slice2: gradients[slice1[0]:slice1[1], slice2[0]:slice2[1]].repeat_interleave(d[0],
0).repeat_interleave(
d[1], 1)
dot = lambda grad, shift: (
torch.stack((grid[:shape[0], :shape[1], 0] + shift[0], grid[:shape[0], :shape[1], 1] + shift[1]),
dim=-1) * grad[:shape[0], :shape[1]]).sum(dim=-1)
n00 = dot(tile_grads([0, -1], [0, -1]), [0, 0])
n10 = dot(tile_grads([1, None], [0, -1]), [-1, 0])
n01 = dot(tile_grads([0, -1], [1, None]), [0, -1])
n11 = dot(tile_grads([1, None], [1, None]), [-1, -1])
t = fade(grid[:shape[0], :shape[1]])
return math.sqrt(2) * torch.lerp(torch.lerp(n00, n10, t[..., 0]), torch.lerp(n01, n11, t[..., 0]), t[..., 1])
def rand_perlin_2d_octaves(shape, res, octaves=1, persistence=0.5):
noise = torch.zeros(shape)
frequency = 1
amplitude = 1
for _ in range(octaves):
noise += amplitude * rand_perlin_2d(shape, (frequency * res[0], frequency * res[1]))
frequency *= 2
amplitude *= persistence
noise *= random.random() - noise_level # haha
noise += random.random() - noise_level # haha x2
return noise
def load_clip(model_name="ViT-B/16", device="cuda:0" if torch.cuda.is_available() else "cpu"):
import clip
model, preprocess = clip.load(model_name, device=device, jit=False)
if len(preprocess.transforms) > 4:
preprocess.transforms = preprocess.transforms[-1:]
return model, preprocess
# http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
def ico():
phi = (1 + 5 ** 0.5) / 2
return (
np.array([
[-1, phi, 0],
[1, phi, 0],
[-1, -phi, 0],
[1, -phi, 0],
[0, -1, phi],
[0, 1, phi],
[0, -1, -phi],
[0, 1, -phi],
[phi, 0, -1],
[phi, 0, 1],
[-phi, 0, -1],
[-phi, 0, 1]
]) / phi,
[
[0, 11, 5],
[0, 5, 1],
[0, 1, 7],
[0, 7, 10],
[0, 10, 11],
[1, 5, 9],
[5, 11, 4],
[11, 10, 2],
[10, 7, 6],
[7, 1, 8],
[3, 9, 4],
[3, 4, 2],
[3, 2, 6],
[3, 6, 8],
[3, 8, 9],
[4, 9, 5],
[2, 4, 11],
[6, 2, 10],
[8, 6, 7],
[9, 8, 1]
]
)
def ico_at(xyz=np.array([0, 0, 0]), radius=1.0, i=0):
vert, idx = ico()
return vert * radius + xyz, [[y + i for y in x] for x in idx]
def save_obj(points, out_path):
with torch.inference_mode():
vert_pos, vert_col, vert_rad, vert_opa = (x.detach().cpu().numpy() for x in points)
verts = []
faces = []
for xyz, radius, (r, g, b), a in zip(vert_pos, vert_rad, vert_col, vert_opa):
v, i = ico_at(xyz, radius, len(verts))
for x, y, z in v:
verts.append((x, y, z, r, g, b)) # int(r * 255), int(g * 255), int(b * 255), int(a * 255)))
faces += i
with open(out_path, "w") as out_file:
for v in verts:
out_file.write("v " + " ".join(map(str, v)) + "\n")
for f in faces:
out_file.write("f " + " ".join(map(str, f)) + "\n")