| from prompt_data import prompt_data |
| import os |
| import google.generativeai as genai |
| from io import BytesIO |
| import pyperclip |
| from google import genai |
| from google.genai import types |
| import torch |
| from diffusers import DiffusionPipeline |
| import gradio as gr |
|
|
| |
| styles = prompt_data.get("DRAWING STYLES", []) + prompt_data.get("VISUAL STYLES", []) |
| subjects = prompt_data.get("SUBJECTS", []) |
| moods = prompt_data.get("EMOTIONS", []) |
| clothing = prompt_data.get("CLOTHING", []) |
| props = prompt_data.get("PROPS", []) |
| poses = prompt_data.get("POSES", []) |
| settings = prompt_data.get("SETTINGS", []) |
| scenes = prompt_data.get("SCENE", []) |
| artists = prompt_data.get("ARTISTS", []) |
| colors = prompt_data.get("COLORS", []) |
|
|
| |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") |
|
|
| |
| |
| GCP_PROJECT_ID = os.getenv('GCP_PROJECT_ID', 'your-gcp-project-id') |
| GCP_REGION = os.getenv('GCP_REGION', 'us-central1') |
|
|
| |
| try: |
| client = genai.Client(vertexai=True, project=GCP_PROJECT_ID, location=GCP_REGION) |
| except Exception as e: |
| client = None |
| print(f"Vertex AI Client Initialization Error: {e}") |
| print("Please ensure GCP_PROJECT_ID and GCP_REGION are correct and Vertex AI API is enabled.") |
|
|
| |
| def generate_prompt(style, subject, mood, clothing_sel, prop_sel, pose_sel, setting_sel, scene_sel, artist_sel, color_sel, custom_attributes): |
| |
| def ensure_list(x): |
| return x if isinstance(x, list) else ([] if x is None else [x]) |
| style = ensure_list(style) |
| subject = ensure_list(subject) |
| mood = ensure_list(mood) |
| clothing_sel = ensure_list(clothing_sel) |
| prop_sel = ensure_list(prop_sel) |
| pose_sel = ensure_list(pose_sel) |
| setting_sel = ensure_list(setting_sel) |
| scene_sel = ensure_list(scene_sel) |
| artist_sel = ensure_list(artist_sel) |
| color_sel = ensure_list(color_sel) |
| |
| if not custom_attributes: |
| custom_attributes_flat = [] |
| elif isinstance(custom_attributes[0], list): |
| custom_attributes_flat = [item for sublist in custom_attributes for item in sublist] |
| else: |
| custom_attributes_flat = custom_attributes |
| prompt_parts = [ |
| ", ".join(style), ", ".join(subject), ", ".join(mood), ", ".join(clothing_sel), ", ".join(prop_sel), ", ".join(pose_sel), ", ".join(setting_sel), ", ".join(scene_sel), ", ".join(artist_sel), ", ".join(color_sel), ", ".join(custom_attributes_flat) |
| ] |
| prompt = ", ".join([p for p in prompt_parts if p]) |
| return prompt |
|
|
| def enhance_prompt_with_gemini(prompt): |
| if not GEMINI_API_KEY: |
| return "[Gemini API key not set. Please set GEMINI_API_KEY environment variable.]" |
| try: |
| |
| text_client = genai.Client(api_key=GEMINI_API_KEY) |
| response = text_client.models.generate_content( |
| model='gemini-1.5-flash-latest', |
| contents=f"Rewrite this comma-separated list as a perfect, detailed prompt for an AI image generation model: {prompt}" |
| ) |
| return response.text.strip() |
| except Exception as e: |
| return f"[Gemini API error during prompt enhancement: {e}]" |
|
|
| def copy_to_clipboard(text): |
| try: |
| pyperclip.copy(text) |
| return 'Copied!' |
| except Exception as e: |
| return f'Copy failed: {str(e)}' |
|
|
| |
| full_custom_css = ''' |
| <style> |
| /* General Body and Container Styling */ |
| body { |
| background: linear-gradient(135deg, #f0f7ff 0%, #e0efff 100%) !important; /* Lighter, subtle blue gradient */ |
| min-height: 100vh; |
| font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; |
| color: #1a3e63 !important; /* Darker blue for text */ |
| } |
| |
| /* Force all Gradio containers to be light and consistent */ |
| .gradio-container, .gr-block, .gr-panel, .gr-column, .gr-row, .gr-box, .gr-form, .gr-input, .gr-dropdown, .gr-group, .gr-accordion, .gr-acc-panel { |
| background: #ffffff !important; /* Pure white background for blocks */ |
| color: #1a3e63 !important; |
| border-radius: 16px !important; /* Slightly rounded corners */ |
| box-shadow: 0 4px 20px rgba(0, 50, 100, 0.08) !important; /* Softer, wider shadow */ |
| border: 1px solid #cce7ff !important; /* Light blue border */ |
| padding: 1.5em !important; |
| } |
| |
| /* Labels and Headings */ |
| .gr-label, label, .gr-dropdown label, .gr-input label, .gr-markdown, .gr-markdown *, .prose, .prose *, h1, h2, h3, h4, h5, h6 { |
| color: #1a3e63 !important; /* Dark blue for labels */ |
| background: transparent !important; |
| font-weight: 600 !important; |
| font-size: 1.1em !important; |
| border: none !important; |
| padding: 0.2em 0.8em 0.2em 0 !important; |
| } |
| |
| /* Inputs and Dropdowns (base styles) */ |
| input, select, textarea, .gr-input input, .gr-input select, .gr-input textarea, .gr-dropdown select { |
| border: 1.5px solid #82c2f0 !important; /* Medium blue border */ |
| border-radius: 10px !important; |
| padding: 0.6em 1.2em !important; |
| font-size: 1em !important; |
| background: #f8fcff !important; /* Very light blue background for inputs */ |
| color: #1a3e63 !important; |
| transition: border 0.3s, box-shadow 0.3s; |
| box-shadow: none !important; |
| } |
| input:focus, select:focus, textarea:focus { |
| border-color: #007bff !important; /* Bright blue on focus */ |
| box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25) !important; /* Light glow on focus */ |
| outline: none !important; |
| } |
| |
| /* Buttons */ |
| .gr-button, button, .copy-btn { |
| background: #007bff !important; /* Primary blue for buttons */ |
| color: #ffffff !important; |
| border-radius: 12px !important; |
| font-weight: 600 !important; |
| box-shadow: 0 4px 15px rgba(0, 123, 255, 0.2) !important; |
| border: none !important; |
| transition: background 0.3s, box-shadow 0.3s, transform 0.2s; |
| padding: 0.7em 1.5em !important; /* Consistent padding */ |
| } |
| .gr-button:hover, button:hover, .copy-btn:hover { |
| background: #0056b3 !important; /* Darker blue on hover */ |
| box-shadow: 0 6px 20px rgba(0, 123, 255, 0.3) !important; |
| transform: translateY(-2px); /* Slight lift on hover */ |
| } |
| |
| /* Chips (for custom attributes or similar) */ |
| .chip { |
| display: inline-block; |
| background: #e0f0ff !important; /* Lighter blue for chips */ |
| color: #1a3e63 !important; |
| border-radius: 20px !important; /* More rounded chips */ |
| padding: 0.4rem 1.2rem !important; |
| margin: 0.3rem 0.4rem 0.3rem 0 !important; |
| font-size: 0.95rem !important; |
| font-weight: 500 !important; |
| box-shadow: 0 2px 10px rgba(0, 50, 100, 0.05) !important; |
| opacity: 1; |
| animation: chipIn 0.4s cubic-bezier(.4,2,.6,1) both; |
| border: 1px solid #cce7ff !important; /* Border for chips */ |
| } |
| @keyframes chipIn { |
| 0% { opacity: 0; transform: scale(0.7) translateY(10px);} |
| 100% { opacity: 1; transform: scale(1) translateY(0);} |
| } |
| |
| /* Main Heading */ |
| .main-heading { |
| text-align: center; |
| font-size: 3.2em; |
| font-weight: 700; |
| margin-top: 1em; |
| margin-bottom: 0.8em; |
| color: #0056b3; /* A strong blue for the main title */ |
| letter-spacing: 0.02em; |
| text-shadow: 1px 1px 3px rgba(0,0,0,0.05); |
| } |
| |
| /* Section Headings and Labels */ |
| .gr-markdown h2, .gr-markdown h3, .gr-markdown h4, .gr-markdown h5, .gr-markdown h6 { |
| font-size: 1.2em !important; |
| font-weight: 600 !important; |
| color: #1a3e63 !important; |
| background: transparent !important; |
| border: none !important; |
| padding: 0.2em 0.8em 0.2em 0 !important; |
| } |
| |
| /* Normal text and instructions */ |
| .gr-markdown, .gr-markdown *, .prose, .prose *, p, li { |
| font-size: 1em !important; |
| color: #335d8a !important; /* Slightly lighter blue for body text */ |
| background: transparent !important; |
| line-height: 1.6; /* Improved readability */ |
| } |
| |
| /* Make Gradio progress/status text and bar clearly visible */ |
| .gr-progress-status, .gr-progress-bar, .gr-progress-bar * { |
| color: #007bff !important; |
| font-weight: 600 !important; |
| font-size: 1em !important; |
| background: #e0f0ff !important; |
| border-radius: 8px !important; |
| padding: 0.5em 1em; |
| } |
| |
| /* Specific styling for the prompt preview textboxes */ |
| #prompt-preview textarea, #enhanced-prompt textarea { |
| background-color: #f0f7ff !important; /* Very light blue for prompt output */ |
| border: 1px dashed #a0d0ff !important; /* Dashed border for distinction */ |
| min-height: 80px; /* Ensure sufficient height */ |
| overflow-y: auto; /* Enable scrolling if content is long */ |
| } |
| |
| /* Image generation note */ |
| #image-gen-note { |
| color: #0056b3 !important; |
| font-style: italic; |
| font-size: 0.9em; |
| text-align: center; |
| margin-top: 1em; |
| margin-bottom: 0.5em; |
| } |
| |
| /* --- Specific Fixes for Custom Attributes and Image Container --- */ |
| |
| /* Ensure gr.List items and their text are styled correctly */ |
| .gr-list-item { |
| background-color: #f8fcff !important; /* Light background for list items */ |
| color: #1a3e63 !important; /* Dark text for list items */ |
| border: 1px solid #cce7ff !important; |
| border-radius: 8px !important; |
| margin-bottom: 0.5em !important; /* Space between items */ |
| padding: 0.6em 1em !important; |
| } |
| |
| /* Targeting the input field specifically within the custom attributes section */ |
| .gr-textbox[placeholder="Type and press Enter to add"] input { |
| background-color: #f8fcff !important; |
| color: #1a3e63 !important; |
| border-color: #82c2f0 !important; |
| } |
| |
| /* Styling for the gr.Image container */ |
| .gr-image { |
| background-color: #f8fcff !important; /* Very light blue background for image container */ |
| border: 1px solid #cce7ff !important; /* Consistent light blue border */ |
| border-radius: 16px !important; |
| box-shadow: 0 4px 20px rgba(0, 50, 100, 0.05) !important; /* Soft shadow */ |
| min-height: 300px; /* Ensure it's not too small when empty */ |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| /* Ensure the loading spinner is visible on the lighter background */ |
| .gr-image .progress-spinner { |
| filter: invert(1); /* Invert color of spinner to make it visible on light background */ |
| } |
| |
| /* --- Fix for Dropdown Containers (new additions for outer container) --- */ |
| |
| /* Target the main gr-dropdown container itself */ |
| .gr-dropdown { |
| background-color: #ffffff !important; /* Force white background for the dropdown container */ |
| border: 1px solid #cce7ff !important; /* Ensure light border */ |
| box-shadow: 0 4px 20px rgba(0, 50, 100, 0.08) !important; /* Consistent shadow */ |
| border-radius: 16px !important; /* Consistent border-radius */ |
| padding: 1.5em !important; /* Consistent padding */ |
| } |
| /* Ensure labels inside dropdowns are correctly colored (might be redundant but for safety) */ |
| .gr-dropdown label { |
| color: #1a3e63 !important; /* Dark blue for dropdown labels */ |
| } |
| |
| /* Target the core dropdown input area where selected items (chips) live */ |
| .gr-multiselect-input, .gr-dropdown-input { |
| background-color: #f8fcff !important; /* Very light blue background */ |
| color: #1a3e63 !important; /* Dark text */ |
| border: 1.5px solid #82c2f0 !important; |
| border-radius: 10px !important; |
| padding: 0.6em 1.2em !important; |
| padding-top: 0.3em !important; |
| padding-bottom: 0.3em !important; |
| min-height: 38px; /* Ensure a consistent height even with no selections */ |
| } |
| |
| /* Style the selected chips within the dropdowns */ |
| .gr-multiselect-input .gradio-chip { |
| background-color: #e0f0ff !important; /* Lighter blue for selected chips */ |
| color: #1a3e63 !important; |
| border-radius: 16px !important; |
| padding: 0.3rem 0.8rem !important; |
| margin: 0.2rem !important; |
| font-size: 0.9em !important; |
| border: 1px solid #cce7ff !important; |
| box-shadow: none !important; |
| } |
| |
| /* Style the 'x' button on dropdown chips */ |
| .gr-multiselect-input .gradio-chip button { |
| background: transparent !important; |
| color: #007bff !important; |
| font-size: 1.1em !important; |
| padding: 0 0.2em !important; |
| } |
| .gr-multiselect-input .gradio-chip button:hover { |
| color: #0056b3 !important; |
| background: transparent !important; |
| transform: none !important; |
| } |
| |
| |
| /* Style the dropdown menu (the actual list of options) when it opens */ |
| .gr-options, .gr-options > div { |
| background-color: #ffffff !important; |
| border: 1px solid #cce7ff !important; |
| border-radius: 10px !important; |
| box-shadow: 0 4px 15px rgba(0, 50, 100, 0.1) !important; |
| } |
| |
| /* Style individual items in the dropdown menu */ |
| .gr-option { |
| color: #1a3e63 !important; |
| padding: 0.8em 1.2em !important; |
| background-color: transparent !important; |
| } |
| |
| .gr-option:hover { |
| background-color: #e0f0ff !important; |
| color: #0056b3 !important; |
| } |
| |
| .gr-option.selected { |
| background-color: #cce7ff !important; |
| color: #0056b3 !important; |
| font-weight: 600 !important; |
| } |
| |
| /* Ensure the caret (down arrow) is visible */ |
| .gr-dropdown-caret { |
| color: #007bff !important; |
| } |
| |
| |
| </style> |
| <div class="glow-overlay"></div> |
| <script> |
| document.addEventListener("DOMContentLoaded", function() { |
| const overlay = document.querySelector('.glow-overlay'); |
| if (!overlay) return; |
| document.body.addEventListener("mousemove", (event) => { |
| overlay.style.setProperty("--glow-x", `${event.clientX}px`); |
| overlay.style.setProperty("--glow-y", `${event.clientY}px`); |
| overlay.style.setProperty("--glow-opacity", "1"); |
| }); |
| document.body.addEventListener("mouseleave", () => { |
| overlay.style.setProperty("--glow-opacity", "0"); |
| }); |
| setTimeout(() => { |
| document.querySelectorAll('h1, h2, h3, h4, h5, h6, p, li, strong, em, .gr-markdown, .prose, .gr-block, .gradio-container').forEach(el => { |
| el.style.color = "#1a3e63"; /* Ensure text color after dynamic styles load */ |
| }); |
| }, 1000); |
| }); |
| </script> |
| ''' |
|
|
| |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| model_repo_id = "stabilityai/sdxl-turbo" |
| if torch.cuda.is_available(): |
| torch_dtype = torch.float16 |
| else: |
| torch_dtype = torch.float32 |
| pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype) |
| pipe = pipe.to(device) |
|
|
| def generate_image(prompt_text): |
| if not prompt_text or prompt_text.strip() == "": |
| return None |
| image = pipe(prompt=prompt_text, guidance_scale=0.0, num_inference_steps=2, width=1024, height=1024).images[0] |
| return image |
|
|
| |
| with gr.Blocks(theme=gr.themes.Soft(), css=full_custom_css) as demo: |
| gr.HTML('<div class="main-heading">AI Image Prompt Generator 🎨</div>') |
| with gr.Row(): |
| gr.Markdown("""# Create creative prompts for AI text-to-image models! |
| Welcome! Build your perfect prompt step by step. Select or add options below, and copy your prompt to use in your favorite AI image tool.""") |
| with gr.Row(): |
| with gr.Column(): |
| gr.Markdown("""**How to use:** |
| 1. Choose or randomize options for each category (or add your own). |
| 2. See your prompt update live below. |
| 3. Click 'Copy Prompt' to use it elsewhere!""") |
| with gr.Column(): |
| pass |
|
|
| |
| with gr.Row(): |
| style = gr.Dropdown(choices=styles, multiselect=True, label="Style") |
| subject = gr.Dropdown(choices=subjects, multiselect=True, label="Subject") |
| mood = gr.Dropdown(choices=moods, multiselect=True, label="Mood/Emotion") |
| with gr.Row(): |
| clothing_sel = gr.Dropdown(choices=clothing, multiselect=True, label="Clothing") |
| prop_sel = gr.Dropdown(choices=props, multiselect=True, label="Prop") |
| pose_sel = gr.Dropdown(choices=poses, multiselect=True, label="Pose") |
| with gr.Row(): |
| setting_sel = gr.Dropdown(choices=settings, multiselect=True, label="Setting") |
| scene_sel = gr.Dropdown(choices=scenes, multiselect=True, label="Scene") |
| artist_sel = gr.Dropdown(choices=artists, multiselect=True, label="Artist") |
| color_sel = gr.Dropdown(choices=colors, multiselect=True, label="Color/Lighting") |
|
|
| gr.Markdown("---") |
| gr.Markdown("#### 2. Add any extra custom attributes:") |
| with gr.Row(): |
| custom_attr = gr.Textbox(label="Add custom attribute (anything not covered above)", placeholder="Type and press Enter to add", interactive=True) |
| add_btn = gr.Button("Add Attribute") |
| custom_attr_list = gr.List(label="Custom Attributes", value=[], interactive=True) |
|
|
| gr.Markdown("---") |
| gr.Markdown("#### 3. Your generated prompt:") |
| |
| with gr.Row(): |
| prompt = gr.Textbox(label="Prompt Preview", interactive=False, elem_id="prompt-preview", scale=8, lines=3) |
| copy_prompt_btn = gr.Button("📋", elem_classes=["copy-btn"], scale=1) |
| |
| with gr.Row(): |
| |
| enhanced_prompt = gr.Textbox(label="Enhanced Prompt", interactive=False, elem_id="enhanced-prompt", scale=8, lines=3) |
| copy_enhanced_btn = gr.Button("📋", elem_classes=["copy-btn"], scale=1) |
| refine_btn = gr.Button("Refine Prompt with Gemini 🪄", variant="secondary") |
|
|
| |
| gr.Markdown("---") |
| gr.Markdown("#### 4. Generate image from your prompt:") |
| gr.Markdown("_Note: Image generation generally takes 300-400 seconds. Please be patient!_", elem_id="image-gen-note") |
| with gr.Row(): |
| generate_img_btn = gr.Button("Generate Image 🖼️", variant="primary") |
| img_output = gr.Image(label="Generated Image", show_label=True) |
|
|
| |
|
|
| def add_custom_attribute(custom_attr_input, current_custom_attr_list): |
| if custom_attr_input and [custom_attr_input] not in current_custom_attr_list: |
| current_custom_attr_list = current_custom_attr_list + [[custom_attr_input]] |
| return current_custom_attr_list, "" |
|
|
| add_btn.click(add_custom_attribute, [custom_attr, custom_attr_list], [custom_attr_list, custom_attr]) |
| custom_attr.submit(add_custom_attribute, [custom_attr, custom_attr_list], [custom_attr_list, custom_attr]) |
|
|
| copy_prompt_btn.click(copy_to_clipboard, inputs=prompt, outputs=None, show_progress=False) |
| copy_enhanced_btn.click(copy_to_clipboard, inputs=enhanced_prompt, outputs=None, show_progress=False) |
| refine_btn.click(enhance_prompt_with_gemini, [prompt], [enhanced_prompt]) |
| generate_img_btn.click(generate_image, inputs=prompt, outputs=img_output) |
|
|
| |
| for comp in [style, subject, mood, clothing_sel, prop_sel, pose_sel, setting_sel, scene_sel, artist_sel, color_sel, custom_attr_list]: |
| comp.change( |
| generate_prompt, |
| [style, subject, mood, clothing_sel, prop_sel, pose_sel, setting_sel, scene_sel, artist_sel, color_sel, custom_attr_list], |
| [prompt] |
| ) |
|
|
| demo.launch(share=True) |