Creative-Design / app.py
openfree's picture
Create app.py
43f666f verified
raw
history blame
14.3 kB
import gradio as gr
import replicate
import os
from PIL import Image
import requests
from io import BytesIO
import time
import tempfile
import base64
# Set up Replicate API key from environment variable
os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
def upload_image_to_hosting(image):
"""
Upload image to multiple hosting services with fallback
"""
# Method 1: Try imgbb.com (most reliable)
try:
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
img_base64 = base64.b64encode(buffered.getvalue()).decode()
response = requests.post(
"https://api.imgbb.com/1/upload",
data={
'key': '6d207e02198a847aa98d0a2a901485a5',
'image': img_base64,
}
)
if response.status_code == 200:
data = response.json()
if data.get('success'):
return data['data']['url']
except:
pass
# Method 2: Try 0x0.st (simple and reliable)
try:
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
files = {'file': ('image.png', buffered, 'image/png')}
response = requests.post("https://0x0.st", files=files)
if response.status_code == 200:
return response.text.strip()
except:
pass
# Method 3: Fallback to base64
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
img_base64 = base64.b64encode(buffered.getvalue()).decode()
return f"data:image/png;base64,{img_base64}"
def process_images(prompt, image1, image2=None):
"""
Process uploaded images with Replicate API or generate from text
"""
if not os.getenv('REPLICATE_API_TOKEN'):
return None, "Please set REPLICATE_API_TOKEN"
try:
# Check if any images are uploaded
if not image1 and not image2:
# Text-to-Image mode (no images uploaded)
return generate_from_text(prompt)
# Image-to-Image mode (at least one image uploaded)
image_urls = []
# Upload images
if image1:
url1 = upload_image_to_hosting(image1)
image_urls.append(url1)
if image2:
url2 = upload_image_to_hosting(image2)
image_urls.append(url2)
# Run the model with images
output = replicate.run(
"google/nano-banana",
input={
"prompt": prompt,
"image_input": image_urls
}
)
return process_output(output)
except Exception as e:
return None, f"Error: {str(e)[:100]}"
def generate_from_text(prompt):
"""
Generate image from text only using a text-to-image model
"""
try:
# Use a text-to-image model when no images are provided
# Using SDXL-Lightning for fast generation
output = replicate.run(
"bytedance/sdxl-lightning-4step:5599ed30703defd1d160a25a63321b4dec97101d98b4674bcc56e41f62f35637",
input={
"prompt": prompt,
"negative_prompt": "worst quality, low quality, blurry, nsfw",
"width": 1024,
"height": 1024,
"num_inference_steps": 4,
"guidance_scale": 0,
"scheduler": "K_EULER"
}
)
return process_output(output, "✨ Generated from text successfully!")
except Exception as e:
# Fallback to another model if the first one fails
try:
output = replicate.run(
"stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
input={
"prompt": prompt,
"negative_prompt": "worst quality, low quality",
"width": 1024,
"height": 1024,
"num_inference_steps": 25,
"refine": "expert_ensemble_refiner",
"apply_watermark": False
}
)
return process_output(output, "✨ Generated from text successfully!")
except Exception as e2:
return None, f"Text generation error: {str(e2)[:100]}"
def process_output(output, success_message="✨ Generated successfully!"):
"""
Process the output from Replicate API
"""
if output is None:
return None, "No output received"
# Get the generated image
try:
if hasattr(output, 'read'):
img_data = output.read()
img = Image.open(BytesIO(img_data))
return img, success_message
except:
pass
try:
if hasattr(output, 'url'):
output_url = output.url()
response = requests.get(output_url, timeout=30)
if response.status_code == 200:
img = Image.open(BytesIO(response.content))
return img, success_message
except:
pass
output_url = None
if isinstance(output, str):
output_url = output
elif isinstance(output, list) and len(output) > 0:
output_url = output[0]
if output_url:
response = requests.get(output_url, timeout=30)
if response.status_code == 200:
img = Image.open(BytesIO(response.content))
return img, success_message
return None, "Could not process output"
# Enhanced CSS with modern, minimal design
css = """
.gradio-container {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-height: 100vh;
}
.header-container {
background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%);
padding: 2.5rem;
border-radius: 24px;
margin-bottom: 2.5rem;
box-shadow: 0 20px 60px rgba(255, 179, 71, 0.25);
}
.logo-text {
font-size: 3.5rem;
font-weight: 900;
color: #2d3436;
text-align: center;
margin: 0;
letter-spacing: -2px;
}
.subtitle {
color: #2d3436;
text-align: center;
font-size: 1rem;
margin-top: 0.5rem;
opacity: 0.8;
}
.mode-indicator {
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 0.5rem 1rem;
margin-top: 1rem;
text-align: center;
font-weight: 600;
color: #2d3436;
}
.main-content {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 2.5rem;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
}
.gr-button-primary {
background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%) !important;
border: none !important;
color: #2d3436 !important;
font-weight: 700 !important;
font-size: 1.1rem !important;
padding: 1.2rem 2rem !important;
border-radius: 14px !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
text-transform: uppercase;
letter-spacing: 1px;
width: 100%;
margin-top: 1rem !important;
}
.gr-button-primary:hover {
transform: translateY(-3px) !important;
box-shadow: 0 15px 40px rgba(255, 179, 71, 0.35) !important;
}
.gr-input, .gr-textarea {
background: #ffffff !important;
border: 2px solid #e1e8ed !important;
border-radius: 14px !important;
color: #2d3436 !important;
font-size: 1rem !important;
padding: 0.8rem 1rem !important;
}
.gr-input:focus, .gr-textarea:focus {
border-color: #ffd93d !important;
box-shadow: 0 0 0 4px rgba(255, 217, 61, 0.15) !important;
}
.gr-form {
background: transparent !important;
border: none !important;
}
.gr-panel {
background: #ffffff !important;
border: 2px solid #e1e8ed !important;
border-radius: 16px !important;
padding: 1.5rem !important;
}
.gr-box {
border-radius: 14px !important;
border-color: #e1e8ed !important;
}
label {
color: #636e72 !important;
font-weight: 600 !important;
font-size: 0.85rem !important;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.5rem !important;
}
.status-text {
font-family: 'SF Mono', 'Monaco', monospace;
color: #00b894;
font-size: 0.9rem;
}
.image-container {
border-radius: 14px !important;
overflow: hidden;
border: 2px solid #e1e8ed !important;
background: #fafbfc !important;
}
footer {
display: none !important;
}
/* Equal sizing for all image containers */
.image-upload {
min-height: 200px !important;
max-height: 200px !important;
}
.output-image {
min-height: 420px !important;
max-height: 420px !important;
}
/* Ensure consistent spacing */
.gr-row {
gap: 1rem !important;
}
.gr-column {
gap: 1rem !important;
}
.info-box {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border-radius: 12px;
padding: 1rem;
margin-bottom: 1rem;
border-left: 4px solid #2196f3;
}
"""
with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
with gr.Column(elem_classes="header-container"):
gr.HTML("""
<h1 class="logo-text">🍌 Free Nano Banana</h1>
<p class="subtitle">AI-Powered Image Generation & Style Transfer</p>
<div class="mode-indicator">
💡 Works in two modes: Text-to-Image (no upload) or Style Transfer (with images)
</div>
<div style="display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px;">
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-PRO" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=PRO&color=%230000ff&labelColor=%23800080&logo=HUGGINGFACE&logoColor=white&style=for-the-badge" alt="badge">
</a>
<a href="https://huggingface.co/spaces/openfree/Nano-Banana-Upscale" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=UPSCALE&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana Upscale">
</a>
<a href="https://huggingface.co/spaces/aiqtech/Nano-Banana-API" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=API&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana API">
</a>
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-Video" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=VIDEO&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana VIDEO">
</a>
<a href="https://discord.gg/openfreeai" target="_blank">
<img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="Discord Openfree AI">
</a>
</div>
""")
with gr.Column(elem_classes="main-content"):
# Info box
gr.HTML("""
<div class="info-box">
<strong>How to use:</strong><br>
• <b>Text-to-Image:</b> Enter a prompt without uploading images<br>
• <b>Style Transfer:</b> Upload 1-2 images and describe the style you want
</div>
""")
with gr.Row(equal_height=True):
# Left Column - Inputs
with gr.Column(scale=1):
prompt = gr.Textbox(
label="Prompt / Style Description",
placeholder="Describe what you want to generate or the style to apply...",
lines=3,
value="A beautiful sunset over mountains with golden clouds",
elem_classes="prompt-input"
)
with gr.Row(equal_height=True):
image1 = gr.Image(
label="Primary Image (Optional)",
type="pil",
height=200,
elem_classes="image-container image-upload"
)
image2 = gr.Image(
label="Secondary Image (Optional)",
type="pil",
height=200,
elem_classes="image-container image-upload"
)
generate_btn = gr.Button(
"Generate Magic ✨",
variant="primary",
size="lg"
)
# Right Column - Output
with gr.Column(scale=1):
output_image = gr.Image(
label="Generated Result",
type="pil",
height=420,
elem_classes="image-container output-image"
)
status = gr.Textbox(
label="Status",
interactive=False,
lines=1,
elem_classes="status-text",
value="Ready to generate..."
)
# Event handler
generate_btn.click(
fn=process_images,
inputs=[prompt, image1, image2],
outputs=[output_image, status]
)
# Examples
gr.Examples(
examples=[
["A cyberpunk city at night with neon lights and flying cars", None, None],
["A magical forest with glowing mushrooms and fireflies", None, None],
["Portrait of a robot in renaissance painting style", None, None],
["Underwater palace made of coral and pearls", None, None],
],
inputs=[prompt, image1, image2],
label="Text-to-Image Examples"
)
# Launch
if __name__ == "__main__":
demo.launch(
share=True,
server_name="0.0.0.0",
server_port=7860
)