import os import gradio as gr import numpy as np import torch from diffusers import DDPMScheduler from PIL import Image from config import ( cfg, device, ema_unet_model_path, vae_model_path, ) from models import unet, vae from utils import revert_images def load_models() -> None: """Load pre-trained model weights.""" if not (os.path.exists(ema_unet_model_path) and os.path.exists(vae_model_path)): msg = "Model weights not found. Train the models before running the app." raise RuntimeError(msg) vae.load_state_dict(torch.load(vae_model_path, map_location=device)) unet.load_state_dict(torch.load(ema_unet_model_path, map_location=device)) vae.eval() unet.eval() @torch.no_grad() def generate_digit(digit: int) -> Image.Image: """Generate a single digit conditioned on user's requested digit class.""" noise_scheduler = DDPMScheduler(num_train_timesteps=cfg.denoising_timesteps) latents = torch.randn((1, cfg.latent_channels, 8, 8)).to(device) label = torch.tensor([digit]).to(device) for t in noise_scheduler.timesteps: noise_pred = unet( latents, t, class_labels=label, encoder_hidden_states=None ).sample latents = noise_scheduler.step(noise_pred, t, latents).prev_sample recon = vae.decode(latents).sample img_np = revert_images(recon)[0].astype(np.uint8) img = Image.fromarray(img_np, mode="L") return img.resize((512, 512), resample=Image.NEAREST) def infer(digit: str) -> Image.Image: return generate_digit(int(digit)) def build_demo() -> gr.Interface: dropdown = gr.Dropdown( choices=[str(i) for i in range(10)], value="0", label="Choose Digit to Generate" ) output = gr.Image(type="pil", height=512, width=512) return gr.Interface( fn=infer, inputs=dropdown, outputs=output, title="Digit Generator with Latent Diffuser Model", ) if __name__ == "__main__": load_models() demo = build_demo() demo.launch()