Spaces:
Running
Running
Deploy Gradio app with multiple files
Browse files- app.py +82 -0
- models.py +151 -0
- requirements.txt +2 -0
app.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from PIL import Image
|
| 3 |
+
from models import gemini_remix_image, gpt_remix_image
|
| 4 |
+
|
| 5 |
+
def remix_images_wrapper(image1_path: str | None, image2_path: str | None, image3_path: str | None, prompt: str) -> tuple[Image.Image, Image.Image]:
|
| 6 |
+
"""
|
| 7 |
+
Wrapper function to process input paths, convert to PIL Images,
|
| 8 |
+
and call both mock remixing models.
|
| 9 |
+
"""
|
| 10 |
+
pil_images = []
|
| 11 |
+
for path in [image1_path, image2_path, image3_path]:
|
| 12 |
+
if path is not None:
|
| 13 |
+
pil_images.append(Image.open(path))
|
| 14 |
+
else:
|
| 15 |
+
pil_images.append(None) # Maintain None for absent images
|
| 16 |
+
|
| 17 |
+
# Call the mock model functions
|
| 18 |
+
gemini_output = gemini_remix_image(pil_images[0], pil_images[1], pil_images[2], prompt)
|
| 19 |
+
gpt_output = gpt_remix_image(pil_images[0], pil_images[1], pil_images[2], prompt)
|
| 20 |
+
|
| 21 |
+
return gemini_output, gpt_output
|
| 22 |
+
|
| 23 |
+
with gr.Blocks(title="AI Image Remixer") as demo:
|
| 24 |
+
gr.Markdown(
|
| 25 |
+
"<div style='text-align: center; margin-bottom: 20px;'>"
|
| 26 |
+
"<h1>🎨 AI Image Remixer 🖼️</h1>"
|
| 27 |
+
"<p>Drag up to three images into the input boxes, add a text prompt, and see them remixed by different AI models!</p>"
|
| 28 |
+
"</div>"
|
| 29 |
+
)
|
| 30 |
+
gr.Markdown(
|
| 31 |
+
"<div style='text-align: center; margin-bottom: 20px;'>"
|
| 32 |
+
"<p>Built with <a href='https://huggingface.co/spaces/akhaliq/anycoder' target='_blank'>anycoder</a></p>"
|
| 33 |
+
"</div>"
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
with gr.Row():
|
| 37 |
+
with gr.Column(scale=1):
|
| 38 |
+
gr.Markdown("<h3>Input Images (Drag & Drop)</h3>")
|
| 39 |
+
with gr.Row():
|
| 40 |
+
input_image1 = gr.Image(
|
| 41 |
+
type="filepath", label="Image 1",
|
| 42 |
+
height=200, width=200,
|
| 43 |
+
sources=["upload", "clipboard"],
|
| 44 |
+
value="https://i.imgur.com/S9s4P2c.png" # Example image 1
|
| 45 |
+
)
|
| 46 |
+
input_image2 = gr.Image(
|
| 47 |
+
type="filepath", label="Image 2",
|
| 48 |
+
height=200, width=200,
|
| 49 |
+
sources=["upload", "clipboard"],
|
| 50 |
+
value="https://i.imgur.com/gK1545n.png" # Example image 2
|
| 51 |
+
)
|
| 52 |
+
input_image3 = gr.Image(
|
| 53 |
+
type="filepath", label="Image 3",
|
| 54 |
+
height=200, width=200,
|
| 55 |
+
sources=["upload", "clipboard"],
|
| 56 |
+
value="https://i.imgur.com/XqR7m0D.png" # Example image 3
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
prompt_textbox = gr.Textbox(
|
| 60 |
+
label="Text Prompt",
|
| 61 |
+
placeholder="Describe how to remix the images, e.g., 'futuristic city, vibrant colors'",
|
| 62 |
+
lines=2,
|
| 63 |
+
value="Create a magical forest scene with bioluminescent plants."
|
| 64 |
+
)
|
| 65 |
+
remix_button = gr.Button("Remix Images!")
|
| 66 |
+
|
| 67 |
+
with gr.Column(scale=2):
|
| 68 |
+
gr.Markdown("<h3>Remixed Outputs</h3>")
|
| 69 |
+
with gr.Row():
|
| 70 |
+
gemini_output_image = gr.Image(label="Gemini-2 Remix", height=400, show_share_button=True)
|
| 71 |
+
gpt_output_image = gr.Image(label="GPT Image-1 Remix", height=400, show_share_button=True)
|
| 72 |
+
|
| 73 |
+
remix_button.click(
|
| 74 |
+
fn=remix_images_wrapper,
|
| 75 |
+
inputs=[input_image1, input_image2, input_image3, prompt_textbox],
|
| 76 |
+
outputs=[gemini_output_image, gpt_output_image],
|
| 77 |
+
api_name="remix_images",
|
| 78 |
+
show_progress="minimal"
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
demo.launch()
|
models.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 2 |
+
import random
|
| 3 |
+
import io
|
| 4 |
+
|
| 5 |
+
# Note: These are MOCK implementations for 'gemini-2' and 'gpt image-1' image remixing.
|
| 6 |
+
# Direct multi-image remixing APIs for these specific model names that take three arbitrary
|
| 7 |
+
# input images are not publicly available or are not standard usage.
|
| 8 |
+
# These functions simulate the desired behavior using the Pillow library for demonstration.
|
| 9 |
+
|
| 10 |
+
def _get_font(size: int = 24) -> ImageFont.FreeTypeFont:
|
| 11 |
+
"""Helper to get a font that is likely available on most systems or a default."""
|
| 12 |
+
try:
|
| 13 |
+
# Try a common system font
|
| 14 |
+
return ImageFont.truetype("arial.ttf", size)
|
| 15 |
+
except IOError:
|
| 16 |
+
# Fallback to default if Arial is not found
|
| 17 |
+
return ImageFont.load_default(size)
|
| 18 |
+
|
| 19 |
+
def gemini_remix_image(image1: Image.Image | None, image2: Image.Image | None, image3: Image.Image | None, prompt: str) -> Image.Image:
|
| 20 |
+
"""
|
| 21 |
+
MOCK: Simulates image remixing with a "Gemini-2" style.
|
| 22 |
+
Combines up to three input images and adds prompt text dynamically.
|
| 23 |
+
"""
|
| 24 |
+
canvas_width, canvas_height = 800, 600
|
| 25 |
+
output_image = Image.new("RGB", (canvas_width, canvas_height), color=(240, 240, 255)) # Light blueish background
|
| 26 |
+
draw = ImageDraw.Draw(output_image)
|
| 27 |
+
|
| 28 |
+
images = [img for img in [image1, image2, image3] if img is not None]
|
| 29 |
+
|
| 30 |
+
if not images and not prompt:
|
| 31 |
+
# Return a small blank image if no inputs are provided
|
| 32 |
+
return Image.new("RGB", (1, 1), color=(255, 255, 255))
|
| 33 |
+
|
| 34 |
+
# Define positions for images
|
| 35 |
+
positions = [
|
| 36 |
+
(canvas_width // 4 - 50, canvas_height // 4 - 50), # Top-left quadrant
|
| 37 |
+
(canvas_width // 2 + 50, canvas_height // 4 - 50), # Top-right quadrant
|
| 38 |
+
(canvas_width // 4, canvas_height // 2 + 50) # Bottom-center, overlapping
|
| 39 |
+
]
|
| 40 |
+
img_target_size = (canvas_width // 3, canvas_height // 3)
|
| 41 |
+
|
| 42 |
+
for i, img in enumerate(images):
|
| 43 |
+
# Apply a subtle tint and transparency for overlap effect
|
| 44 |
+
tint_color = (random.randint(180,220), random.randint(180,220), random.randint(180,220))
|
| 45 |
+
img_with_tint = Image.composite(Image.new(img.mode, img.size, tint_color), img, Image.new("L", img.size, 128))
|
| 46 |
+
|
| 47 |
+
resized_img = img_with_tint.resize(img_target_size)
|
| 48 |
+
|
| 49 |
+
# Create a mask for semi-transparency
|
| 50 |
+
mask = Image.new("L", resized_img.size, int(255 * 0.8)) # 80% opaque
|
| 51 |
+
|
| 52 |
+
# Paste with alpha composite to handle potential transparency
|
| 53 |
+
try:
|
| 54 |
+
output_image.paste(resized_img, positions[i % len(positions)], resized_img)
|
| 55 |
+
except ValueError: # Fallback for images without alpha channel
|
| 56 |
+
output_image.paste(resized_img, positions[i % len(positions)])
|
| 57 |
+
|
| 58 |
+
# Add prompt text with artistic styling
|
| 59 |
+
font_size = 30
|
| 60 |
+
font = _get_font(font_size)
|
| 61 |
+
text_color = (70, 70, 70) # Darker grey
|
| 62 |
+
|
| 63 |
+
if prompt:
|
| 64 |
+
wrapped_prompt = ""
|
| 65 |
+
current_line = ""
|
| 66 |
+
words = prompt.split(" ")
|
| 67 |
+
for word in words:
|
| 68 |
+
if draw.textbbox((0, 0), current_line + " " + word, font=font)[2] < canvas_width * 0.9:
|
| 69 |
+
current_line += " " + word
|
| 70 |
+
else:
|
| 71 |
+
wrapped_prompt += current_line.strip() + "\n"
|
| 72 |
+
current_line = word
|
| 73 |
+
wrapped_prompt += current_line.strip()
|
| 74 |
+
|
| 75 |
+
text_bbox = draw.textbbox((0, 0), wrapped_prompt, font=font)
|
| 76 |
+
text_width = text_bbox[2] - text_bbox[0]
|
| 77 |
+
text_height = text_bbox[3] - text_bbox[1]
|
| 78 |
+
text_x = (canvas_width - text_width) // 2
|
| 79 |
+
text_y = canvas_height - text_height - 20
|
| 80 |
+
|
| 81 |
+
draw.multiline_text((text_x, text_y), wrapped_prompt, font=font, fill=text_color, align="center")
|
| 82 |
+
else:
|
| 83 |
+
draw.text((canvas_width // 2 - 80, canvas_height - 40), "Gemini-2 AI Magic ✨", font=_get_font(20), fill=(150, 150, 150))
|
| 84 |
+
|
| 85 |
+
return output_image
|
| 86 |
+
|
| 87 |
+
def gpt_remix_image(image1: Image.Image | None, image2: Image.Image | None, image3: Image.Image | None, prompt: str) -> Image.Image:
|
| 88 |
+
"""
|
| 89 |
+
MOCK: Simulates image remixing with a "GPT Image-1" (DALL-E) style.
|
| 90 |
+
Combines up to three input images with a different arrangement and adds prompt text.
|
| 91 |
+
"""
|
| 92 |
+
canvas_width, canvas_height = 800, 600
|
| 93 |
+
output_image = Image.new("RGB", (canvas_width, canvas_height), color=(220, 230, 245)) # Soft blue background
|
| 94 |
+
draw = ImageDraw.Draw(output_image)
|
| 95 |
+
|
| 96 |
+
images = [img for img in [image1, image2, image3] if img is not None]
|
| 97 |
+
|
| 98 |
+
if not images and not prompt:
|
| 99 |
+
return Image.new("RGB", (1, 1), color=(255, 255, 255))
|
| 100 |
+
|
| 101 |
+
# Different layout: Center large image, smaller images in corners
|
| 102 |
+
img_large_size = (canvas_width // 2, canvas_height // 2)
|
| 103 |
+
img_small_size = (canvas_width // 4, canvas_height // 4)
|
| 104 |
+
|
| 105 |
+
if len(images) > 0:
|
| 106 |
+
main_img = images[0].resize(img_large_size)
|
| 107 |
+
output_image.paste(main_img, (canvas_width // 2 - img_large_size[0] // 2, canvas_height // 2 - img_large_size[1] // 2))
|
| 108 |
+
|
| 109 |
+
if len(images) > 1:
|
| 110 |
+
top_left_img = images[1].resize(img_small_size)
|
| 111 |
+
output_image.paste(top_left_img, (20, 20))
|
| 112 |
+
|
| 113 |
+
if len(images) > 2:
|
| 114 |
+
bottom_right_img = images[2].resize(img_small_size)
|
| 115 |
+
output_image.paste(bottom_right_img, (canvas_width - img_small_size[0] - 20, canvas_height - img_small_size[1] - 20))
|
| 116 |
+
|
| 117 |
+
# Add artistic borders and effects
|
| 118 |
+
for i, img in enumerate(images):
|
| 119 |
+
if i < len(images): # Apply to images that were placed
|
| 120 |
+
pos = None
|
| 121 |
+
size = None
|
| 122 |
+
if i == 0 and len(images) > 0: # Main image
|
| 123 |
+
pos = (canvas_width // 2 - img_large_size[0] // 2, canvas_height // 2 - img_large_size[1] // 2)
|
| 124 |
+
size = img_large_size
|
| 125 |
+
elif i == 1 and len(images) > 1: # Top-left
|
| 126 |
+
pos = (20, 20)
|
| 127 |
+
size = img_small_size
|
| 128 |
+
elif i == 2 and len(images) > 2: # Bottom-right
|
| 129 |
+
pos = (canvas_width - img_small_size[0] - 20, canvas_height - img_small_size[1] - 20)
|
| 130 |
+
size = img_small_size
|
| 131 |
+
|
| 132 |
+
if pos and size:
|
| 133 |
+
# Draw a subtle border
|
| 134 |
+
border_color = (random.randint(100, 150), random.randint(100, 150), random.randint(100, 150))
|
| 135 |
+
draw.rectangle((pos[0]-2, pos[1]-2, pos[0]+size[0]+2, pos[1]+size[1]+2), outline=border_color, width=3)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
# Add prompt text with a distinct style
|
| 139 |
+
font_size = 32
|
| 140 |
+
font = _get_font(font_size)
|
| 141 |
+
text_color = (25, 25, 25) # Very dark grey
|
| 142 |
+
|
| 143 |
+
if prompt:
|
| 144 |
+
text_bbox = draw.textbbox((0, 0), prompt, font=font)
|
| 145 |
+
text_width = text_bbox[2] - text_bbox[0]
|
| 146 |
+
text_x = (canvas_width - text_width) // 2
|
| 147 |
+
draw.text((text_x, 10), prompt, font=font, fill=text_color, stroke_width=1, stroke_fill=(150,150,150))
|
| 148 |
+
else:
|
| 149 |
+
draw.text((canvas_width // 2 - 90, 10), "GPT Image-1 Creativity", font=_get_font(22), fill=(100, 100, 100))
|
| 150 |
+
|
| 151 |
+
return output_image
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
Pillow
|