Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| from PIL import Image, ImageDraw, ImageFont | |
| import io | |
| import openai | |
| from text_overlay import create_pattern_template, get_pattern_template, add_text_to_image, get_font_path | |
| # Image Generation Functions | |
| def generate_ideogram_image(api_key, prompt, aspect_ratio="ASPECT_1_1"): | |
| """Generate image using Ideogram API""" | |
| if not api_key or not prompt: | |
| return None, "Please provide both API key and prompt", None | |
| url = "https://api.ideogram.ai/generate" | |
| headers = { | |
| "Api-Key": api_key, | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "image_request": { | |
| "prompt": prompt, | |
| "aspect_ratio": aspect_ratio, | |
| "model": "V_2", | |
| "magic_prompt_option": "AUTO" | |
| } | |
| } | |
| try: | |
| response = requests.post(url, json=payload, headers=headers) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if result.get("data"): | |
| image_url = result["data"][0]["url"] | |
| img_response = requests.get(image_url) | |
| image = Image.open(io.BytesIO(img_response.content)) | |
| safe_copy = safe_image_copy(image) | |
| return image, "Image generated successfully!", safe_copy | |
| else: | |
| return None, "No image data received", None | |
| else: | |
| return None, f"Error: {response.status_code} - {response.text}", None | |
| except Exception as e: | |
| return None, f"Error generating image: {str(e)}", None | |
| def generate_dalle_image(api_key, prompt, size="1024x1024"): | |
| """Generate image using DALL-E 3 API""" | |
| if not api_key or not prompt: | |
| return None, "Please provide both API key and prompt", None | |
| try: | |
| client = openai.OpenAI(api_key=api_key) | |
| response = client.images.generate( | |
| model="dall-e-3", | |
| prompt=prompt, | |
| size=size, | |
| quality="standard", | |
| n=1, | |
| ) | |
| image_url = response.data[0].url | |
| img_response = requests.get(image_url) | |
| image = Image.open(io.BytesIO(img_response.content)) | |
| safe_copy = safe_image_copy(image) | |
| return image, "Image generated successfully with DALL-E 3!", safe_copy | |
| except Exception as e: | |
| return None, f"Error generating image: {str(e)}", None | |
| def process_uploaded_image(image): | |
| """Process uploaded image""" | |
| if image is None: | |
| return None, "No image uploaded", None | |
| try: | |
| safe_copy = safe_image_copy(image) | |
| return image, "Image uploaded successfully!", safe_copy | |
| except Exception as e: | |
| print(f"Error processing uploaded image: {e}") | |
| return image, f"Image uploaded with warning: {e}", image | |
| # Fix the image passing with better error handling | |
| def safe_image_copy(img): | |
| """Safely copy an image to avoid corruption issues""" | |
| if img is None: | |
| return None | |
| try: | |
| # Convert to RGB and create a fresh copy | |
| if hasattr(img, 'mode'): | |
| # It's a PIL image | |
| rgb_img = img.convert('RGB') | |
| # Create a new image by copying pixel data | |
| new_img = Image.new('RGB', rgb_img.size) | |
| new_img.paste(rgb_img) | |
| return new_img | |
| else: | |
| print(f"Warning: Unexpected image type: {type(img)}") | |
| return img | |
| except Exception as e: | |
| print(f"Error copying image: {e}") | |
| return img | |
| # Create the Gradio Interface | |
| with gr.Blocks(title="AI Image Generator & Text Overlay") as demo: | |
| gr.Markdown("# π¨ AI Image Generator & Text Overlay") | |
| gr.Markdown("Generate images with AI or upload your own, then add custom text overlays!") | |
| # Shared state for the current image | |
| current_image = gr.State(value=None) | |
| # Tab 1: Image Generation | |
| with gr.Tab("πΌοΈ Image Generation", id="generation_tab"): | |
| gr.Markdown("## Generate or Upload Images") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # API Key inputs | |
| ideogram_key = gr.Textbox( | |
| label="Ideogram API Key", | |
| type="password", | |
| placeholder="Enter your Ideogram API key" | |
| ) | |
| dalle_key = gr.Textbox( | |
| label="OpenAI API Key", | |
| type="password", | |
| placeholder="Enter your OpenAI API key" | |
| ) | |
| # Prompt input | |
| prompt = gr.Textbox( | |
| label="Image Prompt", | |
| placeholder="Describe the image you want to generate...", | |
| lines=3 | |
| ) | |
| # Generation options | |
| with gr.Row(): | |
| ideogram_aspect = gr.Dropdown( | |
| choices=["ASPECT_1_1", "ASPECT_16_9", "ASPECT_9_16", "ASPECT_4_3", "ASPECT_3_4"], | |
| value="ASPECT_1_1", | |
| label="Ideogram Aspect Ratio" | |
| ) | |
| dalle_size = gr.Dropdown( | |
| choices=["1024x1024", "1792x1024", "1024x1792"], | |
| value="1024x1024", | |
| label="DALL-E Size" | |
| ) | |
| # Generation buttons | |
| with gr.Row(): | |
| ideogram_btn = gr.Button("π¨ Generate with Ideogram", variant="primary") | |
| dalle_btn = gr.Button("π€ Generate with DALL-E 3", variant="primary") | |
| # Upload option | |
| gr.Markdown("### Or Upload Your Own Image") | |
| upload_image = gr.Image(label="Upload Image", type="pil") | |
| upload_btn = gr.Button("π Process Uploaded Image") | |
| with gr.Column(): | |
| # Output | |
| generated_image = gr.Image(label="Generated/Uploaded Image", type="pil") | |
| status_message = gr.Textbox(label="Status", interactive=False) | |
| # Event handlers for Tab 1 | |
| ideogram_btn.click( | |
| generate_ideogram_image, | |
| inputs=[ideogram_key, prompt, ideogram_aspect], | |
| outputs=[generated_image, status_message, current_image] | |
| ) | |
| dalle_btn.click( | |
| generate_dalle_image, | |
| inputs=[dalle_key, prompt, dalle_size], | |
| outputs=[generated_image, status_message, current_image] | |
| ) | |
| upload_btn.click( | |
| process_uploaded_image, | |
| inputs=[upload_image], | |
| outputs=[generated_image, status_message, current_image] | |
| ) | |
| # Tab 2: Add Text Overlay | |
| with gr.Tab("π Add Text", id="text_tab"): | |
| gr.Markdown("## Overlay Text on Images") | |
| gr.Markdown("Choose a layout pattern, type your text, and render it on the current image.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Text pattern selection with template preview | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # FIXED: Use proper tuple format and matching value | |
| pattern = gr.Dropdown( | |
| choices=[ | |
| ("2-lines-top", "π 2 Lines - Top (Header style)"), | |
| ("2-lines-center", "π 2 Lines - Center (Title style)"), | |
| ("2-lines-bottom", "π 2 Lines - Bottom (Caption style)"), | |
| ("3-lines-top", "π 3 Lines - Top (Header + subtitle)"), | |
| ("3-lines-center", "π 3 Lines - Center (Full title)"), | |
| ("3-lines-bottom", "π 3 Lines - Bottom (Credits style)") | |
| ], | |
| value="2-lines-top", | |
| label="Layout Pattern" | |
| ) | |
| with gr.Column(scale=1): | |
| # Template preview image | |
| template_preview = gr.Image( | |
| label="Template Preview", | |
| value=None, | |
| show_label=True, | |
| interactive=False, | |
| width=200, | |
| height=120 | |
| ) | |
| # Text input fields | |
| line1_inp = gr.Textbox( | |
| label="Line 1", | |
| placeholder="Enter first line of text..." | |
| ) | |
| line2_inp = gr.Textbox( | |
| label="Line 2", | |
| placeholder="Enter second line of text..." | |
| ) | |
| line3_inp = gr.Textbox( | |
| label="Line 3", | |
| placeholder="Enter third line of text..." | |
| ) | |
| # Styling options | |
| with gr.Row(): | |
| font_size = gr.Slider( | |
| minimum=12, | |
| maximum=120, | |
| value=48, | |
| step=2, | |
| label="Font Size" | |
| ) | |
| color = gr.ColorPicker( | |
| label="Text Color", | |
| value="#FFFFFF" | |
| ) | |
| # NEW: Font selector | |
| with gr.Row(): | |
| font_selector = gr.Dropdown( | |
| choices=[ | |
| "Impact", # Bold, condensed - great for memes | |
| "Arial Bold", # Clean, readable | |
| "Times Bold", # Classic, serif | |
| "Comic Sans", # Casual, friendly | |
| "Bebas Neue" # Modern, tall | |
| ], | |
| value="Impact", | |
| label="Font Style", | |
| info="Choose font for text overlay" | |
| ) | |
| # Add outline option | |
| add_outline = gr.Checkbox( | |
| label="Add black outline", | |
| value=True, | |
| info="Adds black outline for better text visibility" | |
| ) | |
| add_text_btn = gr.Button("β¨ Add Text to Image", variant="primary") | |
| with gr.Column(): | |
| # Output | |
| text_image = gr.Image(label="Image with Text Overlay") | |
| # Event handlers for Tab 2 | |
| # Update template when pattern changes | |
| pattern.change( | |
| get_pattern_template, | |
| inputs=[pattern], | |
| outputs=[template_preview] | |
| ) | |
| # Add text to image | |
| add_text_btn.click( | |
| add_text_to_image, | |
| inputs=[current_image, pattern, line1_inp, line2_inp, line3_inp, font_size, color, add_outline, font_selector], | |
| outputs=[text_image, status_message] | |
| ) | |
| # FIXED: Move demo.load() INSIDE the Blocks context | |
| demo.load( | |
| lambda: get_pattern_template("2-lines-top"), | |
| outputs=[template_preview] | |
| ) | |
| # Launch the application - OUTSIDE the Blocks context | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860 | |
| ) | |