""" Voice-controlled image generator (Gradio) for deployment on Hugging Face Spaces. """ import os import random from typing import Optional from contextlib import contextmanager import torch from PIL import Image import gradio as gr from transformers import pipeline from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler HF_TOKEN = os.environ.get("HF_TOKEN") DEVICE = "cuda" if torch.cuda.is_available() else "cpu" ASR_MODEL = "openai/whisper-small" SD_MODEL = "runwayml/stable-diffusion-v1-5" asr = None pipe = None def load_asr(): global asr if asr is None: device_index = 0 if DEVICE == "cuda" else -1 asr = pipeline("automatic-speech-recognition", model=ASR_MODEL, device=device_index, chunk_length_s=30) return asr def load_sd(): global pipe if pipe is None: pipe = StableDiffusionPipeline.from_pretrained( SD_MODEL, use_auth_token=HF_TOKEN if HF_TOKEN else None, safety_checker=None, ) try: pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config) except Exception: pass pipe = pipe.to(DEVICE) try: pipe.enable_xformers_memory_efficient_attention() except Exception: pass return pipe @contextmanager def nullcontext(): yield # Gradio component (return numpy array + sample rate) mic = gr.Audio(type="numpy", label="Record or upload audio") def transcribe_audio(audio_input): if audio_input is None: return "" # audio_input = (numpy array, sample_rate) audio_array, sample_rate = audio_input asr_model = load_asr() try: result = asr_model({"array": audio_array, "sampling_rate": sample_rate}) return result.get("text", "").strip() if isinstance(result, dict) else str(result).strip() except Exception as e: return f"Error: {str(e)}" def generate_image(prompt: str, negative_prompt: str, steps: int, guidance: float, seed: int): if not prompt.strip(): return None, "Please provide a prompt." pipe = load_sd() if seed is None or int(seed) < 0: seed = random.randint(1, 2**32 - 1) gen = torch.Generator(device=DEVICE).manual_seed(int(seed)) if DEVICE == "cuda" else torch.Generator().manual_seed(int(seed)) autocast_ctx = torch.autocast(device_type="cuda") if DEVICE == "cuda" else nullcontext() with autocast_ctx: out = pipe( prompt=prompt, negative_prompt=negative_prompt or None, num_inference_steps=int(steps), guidance_scale=float(guidance), generator=gen, ) image = out.images[0] return image, f"Seed: {seed} | Steps: {steps} | Guidance: {guidance}" # --- Gradio UI --- with gr.Blocks(title="Voice-controlled Image Generator") as demo: gr.Markdown("## 🎙️ Voice-controlled Image Generator\nRecord or upload a voice prompt → transcribe → edit → generate images.") with gr.Row(): with gr.Column(scale=2): mic = gr.Audio(type="filepath", label="Record or upload audio") # ✅ fixed transcribe_btn = gr.Button("Transcribe") prompt_box = gr.Textbox(label="Prompt (editable)", placeholder="Speak or type your prompt here", lines=3) negative = gr.Textbox(label="Negative prompt (optional)", placeholder="Things to avoid", lines=2) with gr.Row(): steps = gr.Slider(minimum=10, maximum=60, value=28, step=1, label="Sampling steps") guidance = gr.Slider(minimum=1.0, maximum=25.0, value=7.5, step=0.5, label="Guidance scale (CFG)") with gr.Row(): seed_in = gr.Number(label="Seed (leave -1 for random)", value=-1) gen_btn = gr.Button("Generate Image") status = gr.Textbox(label="Status / Info", interactive=False) with gr.Column(scale=3): image_out = gr.Image(label="Generated image", type="pil") gallery = gr.gallery = gr.Gallery( label="Generated Captions", show_label=True, elem_id="gallery", columns=[1], # replaces grid=[1] height="auto" ) def on_transcribe(audio_data): transcript = transcribe_audio(audio_data) return transcript, "Transcription complete." if transcript else "Couldn't transcribe." def on_generate(prompt_text, negative_prompt, steps_val, guidance_val, seed_val): seed_int = int(seed_val) if seed_val is not None else -1 img, info = generate_image(prompt_text, negative_prompt, steps_val, guidance_val, seed_int) return img, [img] if img else [], info transcribe_btn.click(fn=on_transcribe, inputs=[mic], outputs=[prompt_box, status]) gen_btn.click(fn=on_generate, inputs=[prompt_box, negative, steps, guidance, seed_in], outputs=[image_out, gallery, status]) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))