Spaces:
Paused
Paused
Download app.py from rahul7star/jit: direct link, hf CLI and curl.
- Browser
- Download file 9.32 kB
-
https://huggingface.co/spaces/rahul7star/jit/resolve/main/app.py
- Command line
-
hf download hf://spaces/rahul7star/jit/app.py
-
curl -L -o app.py https://huggingface.co/spaces/rahul7star/jit/resolve/main/app.py
9.32 kB
| """Hugging Face Spaces CPU-only image studio.""" | |
| import gc | |
| import os | |
| import random | |
| import tempfile | |
| from pathlib import Path | |
| os.environ['CUDA_VISIBLE_DEVICES'] = '' | |
| os.environ['HF_HUB_DISABLE_TELEMETRY'] = '1' | |
| import gradio as gr | |
| import numpy as np | |
| import torch | |
| from PIL import Image | |
| # CPU-compatible Diffusers backend. WeeLLM itself is GPU-oriented. | |
| MODELS = { | |
| 'Tiny Stable Diffusion (fast test, low quality)': 'diffusers/tiny-stable-diffusion-torch', | |
| 'Stable Diffusion 1.5 (slow, higher quality)': 'stable-diffusion-v1-5/stable-diffusion-v1-5', | |
| } | |
| pipe = None | |
| loaded_id = None | |
| def load_model(model_name, progress): | |
| global pipe, loaded_id | |
| model_id = MODELS[model_name] | |
| if pipe is not None and loaded_id == model_id: | |
| return pipe | |
| progress(0.03, desc='Loading model (first run downloads weights)') | |
| pipe = None | |
| loaded_id = None | |
| gc.collect() | |
| from diffusers import StableDiffusionPipeline | |
| new_pipe = StableDiffusionPipeline.from_pretrained( | |
| model_id, | |
| torch_dtype=torch.float32, | |
| safety_checker=None, | |
| requires_safety_checker=False, | |
| ) | |
| new_pipe = new_pipe.to('cpu') | |
| new_pipe.enable_attention_slicing() | |
| pipe = new_pipe | |
| loaded_id = model_id | |
| return pipe | |
| def render_image(model_name, prompt, negative, width, height, steps, guidance, seed, progress): | |
| if not prompt or not prompt.strip(): | |
| raise gr.Error('Please enter a prompt.') | |
| seed = int(seed) if seed is not None and int(seed) >= 0 else random.randint(0, 2**31 - 1) | |
| model = load_model(model_name, progress) | |
| progress(0.2, desc='Generating image on CPU; this may take several minutes') | |
| with torch.inference_mode(): | |
| result = model( | |
| prompt=prompt.strip(), | |
| negative_prompt=negative or '', | |
| width=int(width), | |
| height=int(height), | |
| num_inference_steps=int(steps), | |
| guidance_scale=float(guidance), | |
| generator=torch.Generator(device='cpu').manual_seed(seed), | |
| ) | |
| return result.images[0], seed | |
| def generate_image(model_name, prompt, negative, width, height, steps, guidance, seed, progress=gr.Progress()): | |
| try: | |
| img, used_seed = render_image(model_name, prompt, negative, width, height, steps, guidance, seed, progress) | |
| progress(1, desc='Done') | |
| return img, f'Image ready · seed {used_seed}' | |
| except Exception as exc: | |
| raise gr.Error(f'{type(exc).__name__}: {exc}') from exc | |
| def animate_image(img, seconds, fps, zoom): | |
| import imageio.v2 as imageio | |
| if img is None: | |
| raise gr.Error('Upload an image or generate one first.') | |
| img = Image.fromarray(np.asarray(img.convert('RGB'))) | |
| width, height = img.size | |
| frames = max(2, round(float(seconds) * int(fps))) | |
| output = Path(tempfile.mkdtemp(prefix='weellm_studio_')) / 'animation.mp4' | |
| try: | |
| writer = imageio.get_writer(str(output), fps=int(fps), codec='libx264', quality=7, macro_block_size=16) | |
| with writer: | |
| for index in range(frames): | |
| t = index / max(1, frames - 1) | |
| scale = 1 + (float(zoom) - 1) * t | |
| cw, ch = max(1, int(width / scale)), max(1, int(height / scale)) | |
| x0 = (width - cw) // 2 | |
| y0 = (height - ch) // 2 | |
| frame = img.crop((x0, y0, x0 + cw, y0 + ch)).resize((width, height), Image.Resampling.LANCZOS) | |
| writer.append_data(np.asarray(frame)) | |
| except Exception as exc: | |
| raise gr.Error(f'Video encoding failed: {exc}') from exc | |
| return str(output), 'MP4 created using camera motion (not AI-generated video).' | |
| def generate_video(model_name, prompt, negative, width, height, steps, guidance, seed, seconds, fps, zoom, progress=gr.Progress()): | |
| try: | |
| img, used_seed = render_image(model_name, prompt, negative, width, height, steps, guidance, seed, progress) | |
| progress(0.85, desc='Encoding MP4') | |
| path, status = animate_image(img, seconds, fps, zoom) | |
| progress(1, desc='Done') | |
| return img, path, f'{status} · seed {used_seed}' | |
| except Exception as exc: | |
| raise gr.Error(f'{type(exc).__name__}: {exc}') from exc | |
| CSS = ''' | |
| .gradio-container {max-width: 1220px !important; margin: auto !important;} | |
| .hero {padding: 18px 6px 24px;} | |
| .hero h1 {font-size: 2.4rem; font-weight: 800; letter-spacing: -.04em; margin-bottom: 6px;} | |
| .hero p {opacity: .78; font-size: 1.05rem;} | |
| ''' | |
| with gr.Blocks(title='WeeLLM CPU Studio', theme=gr.themes.Soft(primary_hue='indigo'), css=CSS) as demo: | |
| gr.HTML('<div class="hero"><h1>✦ WeeLLM CPU Studio</h1><p>Image creation and animated video · Hugging Face CPU Spaces · No GPU required</p></div>') | |
| gr.Markdown('**Backend:** CPU-compatible Diffusers. This deployment does **not** run WeeLLM inference, which may require CUDA. The video feature animates a still image rather than generating motion with a video diffusion model.') | |
| with gr.Tabs(): | |
| with gr.Tab('✦ Text to image'): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| model = gr.Dropdown(list(MODELS), value=list(MODELS)[0], label='Image model') | |
| prompt = gr.Textbox(lines=4, label='Prompt', placeholder='A cozy cabin in a snowy forest, cinematic lighting...') | |
| negative = gr.Textbox(label='Negative prompt', value='blurry, distorted, low quality') | |
| with gr.Accordion('Settings', open=True): | |
| with gr.Row(): | |
| width = gr.Dropdown([256, 384, 512], value=256, label='Width') | |
| height = gr.Dropdown([256, 384, 512], value=256, label='Height') | |
| steps = gr.Slider(1, 30, value=8, step=1, label='Inference steps') | |
| guidance = gr.Slider(0, 12, value=7.5, step=0.5, label='Guidance scale') | |
| seed = gr.Number(value=-1, precision=0, label='Seed (-1 = random)') | |
| image_button = gr.Button('Generate image', variant='primary', size='lg') | |
| with gr.Column(scale=3): | |
| image_output = gr.Image(label='Generated image', type='pil', interactive=False) | |
| image_status = gr.Textbox(label='Status', interactive=False) | |
| image_button.click(generate_image, [model, prompt, negative, width, height, steps, guidance, seed], [image_output, image_status]) | |
| with gr.Tab('▶ Text to animated video'): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| video_model = gr.Dropdown(list(MODELS), value=list(MODELS)[0], label='Image model') | |
| video_prompt = gr.Textbox(lines=4, label='Prompt') | |
| video_negative = gr.Textbox(label='Negative prompt', value='blurry, distorted, low quality') | |
| with gr.Row(): | |
| video_width = gr.Dropdown([256, 384, 512], value=256, label='Width') | |
| video_height = gr.Dropdown([256, 384, 512], value=256, label='Height') | |
| video_steps = gr.Slider(1, 30, value=8, step=1, label='Steps') | |
| video_guidance = gr.Slider(0, 12, value=7.5, step=0.5, label='Guidance') | |
| video_seed = gr.Number(value=-1, precision=0, label='Seed (-1 = random)') | |
| with gr.Row(): | |
| seconds = gr.Slider(1, 6, value=3, step=1, label='Duration (seconds)') | |
| fps = gr.Slider(4, 16, value=8, step=1, label='FPS') | |
| zoom = gr.Slider(1.0, 1.35, value=1.12, step=0.01, label='Ending zoom') | |
| video_button = gr.Button('Generate animated MP4', variant='primary', size='lg') | |
| with gr.Column(scale=3): | |
| video_still = gr.Image(label='Source image', type='pil') | |
| video_output = gr.Video(label='Animated MP4') | |
| video_status = gr.Textbox(label='Status', interactive=False) | |
| video_button.click(generate_video, [video_model, video_prompt, video_negative, video_width, video_height, video_steps, video_guidance, video_seed, seconds, fps, zoom], [video_still, video_output, video_status]) | |
| with gr.Tab('↗ Animate an uploaded image'): | |
| upload = gr.Image(label='Upload image', type='pil') | |
| with gr.Row(): | |
| upload_seconds = gr.Slider(1, 6, value=3, step=1, label='Duration (seconds)') | |
| upload_fps = gr.Slider(4, 16, value=8, step=1, label='FPS') | |
| upload_zoom = gr.Slider(1.0, 1.35, value=1.12, step=0.01, label='Ending zoom') | |
| animate_button = gr.Button('Animate image', variant='primary') | |
| upload_video = gr.Video(label='MP4') | |
| upload_status = gr.Textbox(label='Status', interactive=False) | |
| animate_button.click(animate_image, [upload, upload_seconds, upload_fps, upload_zoom], [upload_video, upload_status]) | |
| gr.Markdown('**CPU tip:** Start with Tiny Stable Diffusion at 256×256 and 8 steps. Stable Diffusion 1.5 is significantly slower and may exceed the memory available on a free CPU Space.') | |
| if __name__ == '__main__': | |
| demo.queue(default_concurrency_limit=1, max_size=5).launch(server_name='0.0.0.0', server_port=int(os.environ.get('PORT', '7860'))) | |