main
commit
e73d66bb7f
6 changed files with 437 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||||
|
.venv |
||||||
|
engines |
||||||
@ -0,0 +1,198 @@ |
|||||||
|
import os |
||||||
|
import sys |
||||||
|
import time |
||||||
|
|
||||||
|
import torch |
||||||
|
from diffusers import AutoencoderTiny, StableDiffusionPipeline |
||||||
|
from diffusers.utils import load_image |
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('../StreamDiffusion')) |
||||||
|
|
||||||
|
from streamdiffusion import StreamDiffusion |
||||||
|
from streamdiffusion.image_utils import postprocess_image |
||||||
|
|
||||||
|
from utils.viewer import receive_images |
||||||
|
|
||||||
|
from utils.wrapper import StreamDiffusionWrapper |
||||||
|
from threading import Thread |
||||||
|
|
||||||
|
|
||||||
|
from multiprocessing import Process, Queue, get_context |
||||||
|
|
||||||
|
from perlin import perlin_2d, rand_perlin_2d, rand_perlin_2d_octaves, perlin_2d_octaves |
||||||
|
from scene_prompt import surreal_prompt_parts |
||||||
|
from scene_prompt import surreal_prompts |
||||||
|
from scene_prompt import regret_prompts |
||||||
|
|
||||||
|
from spout_util import send_spout_image, get_spout_image |
||||||
|
|
||||||
|
from osc import start_osc_server |
||||||
|
|
||||||
|
|
||||||
|
import fire |
||||||
|
|
||||||
|
def image_generation_process( |
||||||
|
queue: Queue, |
||||||
|
fps_queue: Queue, |
||||||
|
prompt_queue: Queue, |
||||||
|
input_queue: Queue, |
||||||
|
# prompt: str, |
||||||
|
model_id_or_path: str, |
||||||
|
)-> None: |
||||||
|
# stream = StreamDiffusionWrapper( |
||||||
|
# model_id_or_path=model_id_or_path, |
||||||
|
# lora_dict=None, |
||||||
|
# t_index_list=[0, 16, 32, 45], |
||||||
|
# frame_buffer_size=1, |
||||||
|
# width=512, |
||||||
|
# height=512, |
||||||
|
# warmup=10, |
||||||
|
# acceleration="xformers", |
||||||
|
# mode="txt2img", |
||||||
|
# use_denoising_batch=False, |
||||||
|
# cfg_type="none", |
||||||
|
# seed=2, |
||||||
|
# ) |
||||||
|
stream = StreamDiffusionWrapper( |
||||||
|
model_id_or_path=model_id_or_path, |
||||||
|
t_index_list=[0], |
||||||
|
frame_buffer_size=1, |
||||||
|
warmup=10, |
||||||
|
acceleration="tensorrt", |
||||||
|
use_lcm_lora=False, |
||||||
|
mode="txt2img", |
||||||
|
cfg_type="none", |
||||||
|
use_denoising_batch=True, |
||||||
|
) |
||||||
|
|
||||||
|
start_prompt = "A glowing, vintage phone booth standing in surreal landscapes across different scene" |
||||||
|
# Prepare the stream |
||||||
|
stream.prepare( |
||||||
|
prompt=start_prompt, |
||||||
|
num_inference_steps=4, |
||||||
|
) |
||||||
|
|
||||||
|
# Prepare image |
||||||
|
# init_image = load_image("example.png").resize((512, 512)) |
||||||
|
|
||||||
|
# Warmup >= len(t_index_list) x frame_buffer_size |
||||||
|
# for _ in range(stream.batch_size - 1): |
||||||
|
# stream() |
||||||
|
|
||||||
|
previous_output = None |
||||||
|
idx=0 |
||||||
|
last_time = time.time() |
||||||
|
|
||||||
|
while True: |
||||||
|
# try: |
||||||
|
start_time = time.time() |
||||||
|
# x_output = stream(image=previous_output) |
||||||
|
# x_output=stream.stream.txt2img_sd_turbo(1).cpu() |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
input_image= input_queue.get(block=True) |
||||||
|
|
||||||
|
|
||||||
|
# Check if a new prompt is available in the prompt_queue |
||||||
|
if not prompt_queue.empty(): |
||||||
|
new_prompt = prompt_queue.get(block=False) |
||||||
|
if new_prompt: |
||||||
|
x_output = stream.img2img(image=input_image, prompt=new_prompt) |
||||||
|
print(f"Received new prompt from queue: {new_prompt}") |
||||||
|
else: |
||||||
|
# Use the current prompt if no new prompt is available |
||||||
|
x_output = stream.img2img(image=input_image) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
preprocessed_image =stream.preprocess_image(x_output) |
||||||
|
|
||||||
|
queue.put(preprocessed_image, block=False) |
||||||
|
|
||||||
|
# queue.put(preprocessed_image, block=False) |
||||||
|
|
||||||
|
# Calculate FPS |
||||||
|
elapsed_time = time.time() - start_time |
||||||
|
fps = 1 / elapsed_time if elapsed_time > 0 else float('inf') |
||||||
|
fps_queue.put(fps) |
||||||
|
|
||||||
|
# x_output = (x_output + 1) / 2 # Scale from [-1, 1] to [0, 1] |
||||||
|
# x_output = torch.clamp(x_output, 0, 1) |
||||||
|
# previous_output = x_output |
||||||
|
|
||||||
|
# except KeyboardInterrupt: |
||||||
|
# print(f"fps: {fps}") |
||||||
|
# return |
||||||
|
|
||||||
|
|
||||||
|
def main()-> None: |
||||||
|
|
||||||
|
try: |
||||||
|
ctx = get_context('spawn') |
||||||
|
queue = Queue() |
||||||
|
fps_queue = Queue() |
||||||
|
# noise_queue = Queue() |
||||||
|
spout_in_queue = Queue() |
||||||
|
|
||||||
|
# prompt = "A surreal landscapes" |
||||||
|
# prompt=regret_prompts[0] |
||||||
|
|
||||||
|
prompt_queue = Queue() |
||||||
|
|
||||||
|
# model_id_or_path = "KBlueLeaf/kohaku-v2.1" |
||||||
|
model_id_or_path = "stabilityai/sd-turbo" |
||||||
|
|
||||||
|
|
||||||
|
# start_osc_server(prompt_queue) |
||||||
|
process_osc = ctx.Process( |
||||||
|
target=start_osc_server, |
||||||
|
args=(prompt_queue,) |
||||||
|
) |
||||||
|
process_osc.start() |
||||||
|
|
||||||
|
print("Starting spout input process") |
||||||
|
process_spout_in = ctx.Process( |
||||||
|
target=get_spout_image, |
||||||
|
args=(spout_in_queue, 512, 512), |
||||||
|
) |
||||||
|
process_spout_in.start() |
||||||
|
|
||||||
|
|
||||||
|
print("Starting image generation process") |
||||||
|
process_gen= ctx.Process( |
||||||
|
target=image_generation_process, |
||||||
|
args=(queue, fps_queue, prompt_queue, spout_in_queue, model_id_or_path), |
||||||
|
) |
||||||
|
process_gen.start() |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# process_show=ctx.Process(target=receive_images, args=(queue, fps_queue)) |
||||||
|
# process_show.start() |
||||||
|
|
||||||
|
print("Starting spout output process") |
||||||
|
process_spout_out=ctx.Process(target=send_spout_image, args=(queue, 512, 512)) |
||||||
|
process_spout_out.start() |
||||||
|
|
||||||
|
|
||||||
|
process_gen.join() |
||||||
|
process_spout_in.join() |
||||||
|
process_spout_out.join() |
||||||
|
process_osc.join() |
||||||
|
|
||||||
|
|
||||||
|
except KeyboardInterrupt: |
||||||
|
print("Process interrupted") |
||||||
|
|
||||||
|
process_gen.terminate() |
||||||
|
process_spout_in.terminate() |
||||||
|
process_spout_out.terminate() |
||||||
|
process_osc.terminate() |
||||||
|
|
||||||
|
return |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
fire.Fire(main) |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
import argparse |
||||||
|
import math |
||||||
|
|
||||||
|
from pythonosc.dispatcher import Dispatcher |
||||||
|
from pythonosc import osc_server |
||||||
|
|
||||||
|
OSC_PORT = 8787 |
||||||
|
|
||||||
|
|
||||||
|
def start_osc_server(queue): |
||||||
|
|
||||||
|
def onReceivePrompt(address, *args): |
||||||
|
prompt = " ".join(args) |
||||||
|
print(f"Received prompt: {prompt}") |
||||||
|
queue.put(prompt) |
||||||
|
|
||||||
|
dispatcher = Dispatcher() |
||||||
|
dispatcher.map("/prompt", onReceivePrompt) |
||||||
|
|
||||||
|
|
||||||
|
server = osc_server.ThreadingOSCUDPServer(("localhost", OSC_PORT), dispatcher) |
||||||
|
print(f"OSC server is running on port {OSC_PORT}") |
||||||
|
server.serve_forever() |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
import torch |
||||||
|
import math |
||||||
|
|
||||||
|
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 |
||||||
|
return noise |
||||||
|
|
||||||
|
def perlin_2d(shape, res, seed, 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 |
||||||
|
base_seed = int(seed) |
||||||
|
frac_seed = seed - base_seed |
||||||
|
|
||||||
|
torch.manual_seed(base_seed) |
||||||
|
angles_base = 2 * math.pi * torch.rand(res[0] + 1, res[1] + 1) |
||||||
|
gradients_base = torch.stack((torch.cos(angles_base), torch.sin(angles_base)), dim=-1) |
||||||
|
|
||||||
|
torch.manual_seed(base_seed + 1) |
||||||
|
angles_next = 2 * math.pi * torch.rand(res[0] + 1, res[1] + 1) |
||||||
|
gradients_next = torch.stack((torch.cos(angles_next), torch.sin(angles_next)), dim=-1) |
||||||
|
|
||||||
|
gradients = (1 - frac_seed) * gradients_base + frac_seed * gradients_next |
||||||
|
|
||||||
|
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 perlin_2d_octaves(shape, res, seed, octaves=1, persistence=0.5, fade=lambda t: 6*t**5 - 15*t**4 + 10*t**3): |
||||||
|
noise = torch.zeros(shape) |
||||||
|
frequency = 1 |
||||||
|
amplitude = 1 |
||||||
|
for i in range(octaves): |
||||||
|
noise += amplitude * perlin_2d(shape, (frequency * res[0], frequency * res[1]), seed + i, fade) |
||||||
|
frequency *= 2 |
||||||
|
amplitude *= persistence |
||||||
|
return noise |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
surreal_prompts = [ |
||||||
|
"a surreal landscape of floating islands under a glowing sky", |
||||||
|
"an ethereal valley where waterfalls rise into the clouds", |
||||||
|
"a dreamlike desert with mirrored sand and hovering stones", |
||||||
|
"an endless ocean reflecting fractured moons and stars", |
||||||
|
"a neon-lit canyon with levitating ruins and glowing mist", |
||||||
|
"a twilight forest where the trees grow upside-down", |
||||||
|
"a luminous terrain with bioluminescent plants and crystal arches", |
||||||
|
"a gravity-defying mountain range spiraling into the void", |
||||||
|
"a shattered realm of glass bridges and hovering towers", |
||||||
|
"an alien world lit by pulsating constellations and fluid geometry" |
||||||
|
] |
||||||
|
|
||||||
|
surreal_prompt_parts = [ |
||||||
|
"a surreal landscape", |
||||||
|
"with floating islands", |
||||||
|
"glowing waterfalls", |
||||||
|
"neon-colored skies", |
||||||
|
"mirror-like desert ground", |
||||||
|
"levitating rocks", |
||||||
|
"upside-down trees", |
||||||
|
"ancient ruins suspended in air", |
||||||
|
"bioluminescent flora", |
||||||
|
"shattered moons overhead", |
||||||
|
"a path of glass tiles", |
||||||
|
"crystal towers emitting soft hums", |
||||||
|
"gravity-defying rivers", |
||||||
|
"alien constellations glowing brightly" |
||||||
|
] |
||||||
|
|
||||||
|
regret_prompts = [ |
||||||
|
"a lone figure standing in a vast, empty desert at dusk", |
||||||
|
"fractured mirrors scattered across the sand, reflecting different memories", |
||||||
|
"a withered tree growing upside down from the sky, its roots dripping ink", |
||||||
|
"floating clocks melting into the horizon, ticking backwards", |
||||||
|
"ghostly silhouettes walking in reverse, retracing forgotten steps", |
||||||
|
"a house half-submerged in water, its windows glowing faintly with past laughter", |
||||||
|
"the sky opens into a tunnel of old photographs slowly burning at the edges", |
||||||
|
"giant stone hands reaching out from the earth, trying to grasp something lost", |
||||||
|
"an ocean made of letters never sent, waves crashing with whispered apologies", |
||||||
|
"a child version of the figure stands alone, staring at the adult with distant eyes" |
||||||
|
] |
||||||
@ -0,0 +1,103 @@ |
|||||||
|
import torch |
||||||
|
import SpoutGL |
||||||
|
|
||||||
|
from itertools import islice, cycle, repeat |
||||||
|
import array |
||||||
|
from random import randint |
||||||
|
import time |
||||||
|
from OpenGL import GL |
||||||
|
|
||||||
|
from multiprocessing import Queue |
||||||
|
import numpy as np |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TARGET_FPS = 30 |
||||||
|
SEND_WIDTH = 512 |
||||||
|
SEND_HEIGHT = 512 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def spout_buffer_to_tensor(buffer, width, height): |
||||||
|
np_buffer = np.asarray(buffer, dtype=np.uint8) |
||||||
|
image_bgra = np_buffer.reshape((height, width, 4)) |
||||||
|
|
||||||
|
image_rgb = image_bgra[..., [2, 1, 0]] |
||||||
|
image_float = image_rgb.astype(np.float32) / 255.0 |
||||||
|
# image_normalized = (image_float * 2.0) - 1.0 |
||||||
|
tensor = torch.from_numpy(image_float).permute(2, 0, 1) |
||||||
|
|
||||||
|
return tensor.unsqueeze(0) |
||||||
|
|
||||||
|
|
||||||
|
def get_spout_image(queue, wwidth: int, wheight: int) -> None: |
||||||
|
with SpoutGL.SpoutReceiver() as receiver: |
||||||
|
receiver.setReceiverName("Spout DX11 Sender") |
||||||
|
|
||||||
|
buffer = None |
||||||
|
|
||||||
|
while True: |
||||||
|
result = receiver.receiveImage(buffer, GL.GL_RGBA, False, 0) |
||||||
|
# print("Receive result", result) |
||||||
|
|
||||||
|
if receiver.isUpdated(): |
||||||
|
width = receiver.getSenderWidth() |
||||||
|
height = receiver.getSenderHeight() |
||||||
|
buffer = array.array('B', [0] * (width * height * 4)) # Correctly reallocate buffer with updated size |
||||||
|
print("Spout Receiver updated, Buffer size", width, height) |
||||||
|
|
||||||
|
if buffer and result and not SpoutGL.helpers.isBufferEmpty(buffer): |
||||||
|
pixels=spout_buffer_to_tensor(buffer, width, height) |
||||||
|
# print("get_spout_image", pixels.shape) |
||||||
|
queue.put(pixels, block=False) |
||||||
|
|
||||||
|
# Wait until the next frame is ready |
||||||
|
# Wait time is in milliseconds; note that 0 will return immediately |
||||||
|
# receiver.waitFrameSync("SpoutSender", 10000) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def randcolor(): |
||||||
|
return randint(0, 255) |
||||||
|
|
||||||
|
|
||||||
|
def tensor_to_spout_image(tensor): |
||||||
|
image = tensor.squeeze(0) |
||||||
|
image = image.permute(1, 2, 0) |
||||||
|
image_np = image.cpu().numpy() |
||||||
|
|
||||||
|
if image_np.min() < 0: |
||||||
|
image_np = (image_np + 1) / 2 # Scale from [-1, 1] to [0, 1] |
||||||
|
image_np = np.clip(image_np * 255, 0, 255).astype(np.uint8) |
||||||
|
|
||||||
|
h, w, _ = image_np.shape |
||||||
|
alpha = np.full((h, w, 1), 255, dtype=np.uint8) |
||||||
|
image_rgba = np.concatenate((image_np, alpha), axis=-1) |
||||||
|
|
||||||
|
image_bgra = image_rgba[..., [2, 1, 0, 3]] |
||||||
|
|
||||||
|
return np.ascontiguousarray(image_bgra) # Ensure the array is contiguous in memory |
||||||
|
|
||||||
|
def send_spout_image(queue: Queue, width: int, height: int)->None: |
||||||
|
|
||||||
|
with SpoutGL.SpoutSender() as sender: |
||||||
|
sender.setSenderName("StreamDiffusion") |
||||||
|
|
||||||
|
while True: |
||||||
|
|
||||||
|
# Check if there are images in the queue |
||||||
|
if not queue.empty(): |
||||||
|
image = queue.get(block=False) |
||||||
|
pixels = tensor_to_spout_image(image) |
||||||
|
|
||||||
|
result = sender.sendImage(pixels, width, height, GL.GL_RGBA, False, 0) |
||||||
|
# print("Send result", result) |
||||||
|
|
||||||
|
# Indicate that a frame is ready to read |
||||||
|
sender.setFrameSync("StreamDiffusion") |
||||||
|
|
||||||
|
# Wait for next send attempt |
||||||
|
# time.sleep(1./TARGET_FPS) |
||||||
Loading…
Reference in new issue