Spaces:
Running
Running
Commit
ยท
359875a
1
Parent(s):
fd7e20d
update
Browse files
app.py
CHANGED
|
@@ -18,10 +18,19 @@ gemini_client = genai.Client(api_key=GEMINI_API_KEY) if GEMINI_API_KEY else None
|
|
| 18 |
|
| 19 |
DEFAULT_PROMPT = "Add furnishings and accessories to this room as an interior designer would do for a real estate staging. The generated image shall have the exact same dimensions as the original image and architectural details. Respect doorways and windows and make sure they are consistent with the source image and not blocked by furniture. Use cute accessories and with appropriate wall space, add smart simple graphic paintings. Use neutral colors with light colored accents to match the colors of the room. Give the area an attractive glow."
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
def b64_to_pil(b64_str):
|
| 22 |
if not b64_str: return None
|
| 23 |
-
if "base64," in b64_str:
|
| 24 |
-
b64_str = b64_str.split("base64,")[1]
|
| 25 |
return Image.open(io.BytesIO(base64.b64decode(b64_str)))
|
| 26 |
|
| 27 |
def bytes_to_pil(img_bytes):
|
|
@@ -29,8 +38,7 @@ def bytes_to_pil(img_bytes):
|
|
| 29 |
|
| 30 |
def get_image_inputs(image_file, image_url):
|
| 31 |
if image_file:
|
| 32 |
-
with open(image_file, "rb") as f:
|
| 33 |
-
raw_bytes = f.read()
|
| 34 |
raw_b64 = base64.b64encode(raw_bytes).decode('utf-8')
|
| 35 |
fal_url = fal_client.upload_file(image_file)
|
| 36 |
return raw_bytes, raw_b64, fal_url
|
|
@@ -44,44 +52,30 @@ def get_image_inputs(image_file, image_url):
|
|
| 44 |
def run_qwen(raw_b64, prompt):
|
| 45 |
url = f"https://api.runpod.ai/v2/{QWEN_ENDPOINT_ID}/runsync"
|
| 46 |
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {RUNPOD_API_KEY}"}
|
| 47 |
-
payload = {
|
| 48 |
-
"input": {
|
| 49 |
-
"image": raw_b64, "prompt": prompt, "seed": 42,
|
| 50 |
-
"use_lightning": True, "true_guidance_scale": 2.5, "num_inference_steps": 4
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
try:
|
| 54 |
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
| 55 |
return b64_to_pil(response.json()["output"]["images"][0])
|
| 56 |
-
except:
|
| 57 |
-
return None
|
| 58 |
|
| 59 |
def run_fal_flux(image_url, prompt):
|
| 60 |
try:
|
| 61 |
handler = fal_client.submit("fal-ai/flux-2/edit", arguments={"prompt": prompt, "image_urls": [image_url]})
|
| 62 |
result = handler.get()
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
except:
|
| 66 |
-
return None
|
| 67 |
|
| 68 |
-
def run_gemini(image_bytes, prompt):
|
| 69 |
if not gemini_client: return None
|
| 70 |
try:
|
| 71 |
response = gemini_client.models.generate_content(
|
| 72 |
model="gemini-2.5-flash-image",
|
| 73 |
contents=[Part.from_bytes(data=image_bytes, mime_type="image/jpeg"), prompt],
|
| 74 |
-
config=GenerateContentConfig(
|
| 75 |
-
response_modalities=["IMAGE"],
|
| 76 |
-
image_config=ImageConfig(aspect_ratio="16:9"),
|
| 77 |
-
candidate_count=1,
|
| 78 |
-
),
|
| 79 |
)
|
| 80 |
for part in response.candidates[0].content.parts:
|
| 81 |
-
if part.inline_data:
|
| 82 |
-
|
| 83 |
-
except:
|
| 84 |
-
return None
|
| 85 |
|
| 86 |
def compare_all(image_file, image_url, prompt):
|
| 87 |
raw_bytes, raw_b64, web_url = get_image_inputs(image_file, image_url)
|
|
@@ -90,66 +84,51 @@ def compare_all(image_file, image_url, prompt):
|
|
| 90 |
return
|
| 91 |
|
| 92 |
og_pil = bytes_to_pil(raw_bytes)
|
|
|
|
| 93 |
qwen_img, flux_img, gemini_img = None, None, None
|
| 94 |
|
| 95 |
-
# Step 0: Show Original
|
| 96 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 97 |
-
|
| 98 |
-
# Step 1: Qwen
|
| 99 |
qwen_img = run_qwen(raw_b64, prompt)
|
| 100 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 101 |
-
|
| 102 |
-
# Step 2: Flux
|
| 103 |
flux_img = run_fal_flux(web_url, prompt)
|
| 104 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 105 |
-
|
| 106 |
-
# Step 3: Gemini
|
| 107 |
-
gemini_img = run_gemini(raw_bytes, prompt)
|
| 108 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 109 |
|
| 110 |
with gr.Blocks() as demo:
|
| 111 |
-
gr.HTML("<h2 style='text-align: center; margin
|
| 112 |
|
| 113 |
with gr.Row():
|
| 114 |
-
with gr.Column(scale=2):
|
| 115 |
-
input_prompt = gr.Textbox(label="Edit Prompt", value=DEFAULT_PROMPT, lines=4)
|
| 116 |
with gr.Column(scale=1):
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
|
| 129 |
-
# BOTTOM: Three Models
|
| 130 |
with gr.Row():
|
| 131 |
with gr.Column():
|
| 132 |
-
gr.HTML("<center><b>QWEN-EDIT</b><br><small>$0.004
|
| 133 |
-
out_qwen = gr.Image(show_label=False, type="pil", height=
|
| 134 |
|
| 135 |
with gr.Column():
|
| 136 |
gr.HTML("<center><b>FLUX-2 EDIT</b><br><small>$0.03</small></center>")
|
| 137 |
-
out_fal = gr.Image(show_label=False, type="pil", height=
|
| 138 |
|
| 139 |
with gr.Column():
|
| 140 |
gr.HTML("<center><b>GEMINI 2.5 FLASH</b><br><small>$0.039</small></center>")
|
| 141 |
-
out_gemini = gr.Image(show_label=False, type="pil", height=
|
| 142 |
|
| 143 |
-
run_btn.click(
|
| 144 |
-
fn=compare_all,
|
| 145 |
-
inputs=[input_file, input_url, input_prompt],
|
| 146 |
-
outputs=[out_og, out_qwen, out_fal, out_gemini]
|
| 147 |
-
)
|
| 148 |
|
| 149 |
if __name__ == "__main__":
|
| 150 |
demo.launch(
|
| 151 |
-
server_name="0.0.0.0",
|
| 152 |
-
|
| 153 |
-
theme=gr.themes.Soft(),
|
| 154 |
-
css=".gradio-container {max-width: 95% !important} img {object-fit: contain !important;}"
|
| 155 |
)
|
|
|
|
| 18 |
|
| 19 |
DEFAULT_PROMPT = "Add furnishings and accessories to this room as an interior designer would do for a real estate staging. The generated image shall have the exact same dimensions as the original image and architectural details. Respect doorways and windows and make sure they are consistent with the source image and not blocked by furniture. Use cute accessories and with appropriate wall space, add smart simple graphic paintings. Use neutral colors with light colored accents to match the colors of the room. Give the area an attractive glow."
|
| 20 |
|
| 21 |
+
def get_closest_ratio(pil_img):
|
| 22 |
+
w, h = pil_img.size
|
| 23 |
+
ratio = w / h
|
| 24 |
+
ratios = {
|
| 25 |
+
"9:16": 0.56, "2:3": 0.66, "3:4": 0.75, "4:5": 0.8,
|
| 26 |
+
"1:1": 1.0, "5:4": 1.25, "4:3": 1.33, "3:2": 1.5,
|
| 27 |
+
"16:9": 1.77, "21:9": 2.33
|
| 28 |
+
}
|
| 29 |
+
return min(ratios, key=lambda x: abs(ratios[x] - ratio))
|
| 30 |
+
|
| 31 |
def b64_to_pil(b64_str):
|
| 32 |
if not b64_str: return None
|
| 33 |
+
if "base64," in b64_str: b64_str = b64_str.split("base64,")[1]
|
|
|
|
| 34 |
return Image.open(io.BytesIO(base64.b64decode(b64_str)))
|
| 35 |
|
| 36 |
def bytes_to_pil(img_bytes):
|
|
|
|
| 38 |
|
| 39 |
def get_image_inputs(image_file, image_url):
|
| 40 |
if image_file:
|
| 41 |
+
with open(image_file, "rb") as f: raw_bytes = f.read()
|
|
|
|
| 42 |
raw_b64 = base64.b64encode(raw_bytes).decode('utf-8')
|
| 43 |
fal_url = fal_client.upload_file(image_file)
|
| 44 |
return raw_bytes, raw_b64, fal_url
|
|
|
|
| 52 |
def run_qwen(raw_b64, prompt):
|
| 53 |
url = f"https://api.runpod.ai/v2/{QWEN_ENDPOINT_ID}/runsync"
|
| 54 |
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {RUNPOD_API_KEY}"}
|
| 55 |
+
payload = {"input": {"image": raw_b64, "prompt": prompt, "seed": 42, "use_lightning": True, "true_guidance_scale": 2.5, "num_inference_steps": 4}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
try:
|
| 57 |
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
| 58 |
return b64_to_pil(response.json()["output"]["images"][0])
|
| 59 |
+
except: return None
|
|
|
|
| 60 |
|
| 61 |
def run_fal_flux(image_url, prompt):
|
| 62 |
try:
|
| 63 |
handler = fal_client.submit("fal-ai/flux-2/edit", arguments={"prompt": prompt, "image_urls": [image_url]})
|
| 64 |
result = handler.get()
|
| 65 |
+
return bytes_to_pil(requests.get(result['images'][0]['url']).content)
|
| 66 |
+
except: return None
|
|
|
|
|
|
|
| 67 |
|
| 68 |
+
def run_gemini(image_bytes, prompt, ratio_str):
|
| 69 |
if not gemini_client: return None
|
| 70 |
try:
|
| 71 |
response = gemini_client.models.generate_content(
|
| 72 |
model="gemini-2.5-flash-image",
|
| 73 |
contents=[Part.from_bytes(data=image_bytes, mime_type="image/jpeg"), prompt],
|
| 74 |
+
config=GenerateContentConfig(response_modalities=["IMAGE"], image_config=ImageConfig(aspect_ratio=ratio_str), candidate_count=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
)
|
| 76 |
for part in response.candidates[0].content.parts:
|
| 77 |
+
if part.inline_data: return bytes_to_pil(part.inline_data.data)
|
| 78 |
+
except: return None
|
|
|
|
|
|
|
| 79 |
|
| 80 |
def compare_all(image_file, image_url, prompt):
|
| 81 |
raw_bytes, raw_b64, web_url = get_image_inputs(image_file, image_url)
|
|
|
|
| 84 |
return
|
| 85 |
|
| 86 |
og_pil = bytes_to_pil(raw_bytes)
|
| 87 |
+
ratio_str = get_closest_ratio(og_pil)
|
| 88 |
qwen_img, flux_img, gemini_img = None, None, None
|
| 89 |
|
|
|
|
| 90 |
yield og_pil, qwen_img, flux_img, gemini_img
|
|
|
|
|
|
|
| 91 |
qwen_img = run_qwen(raw_b64, prompt)
|
| 92 |
yield og_pil, qwen_img, flux_img, gemini_img
|
|
|
|
|
|
|
| 93 |
flux_img = run_fal_flux(web_url, prompt)
|
| 94 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 95 |
+
gemini_img = run_gemini(raw_bytes, prompt, ratio_str)
|
|
|
|
|
|
|
| 96 |
yield og_pil, qwen_img, flux_img, gemini_img
|
| 97 |
|
| 98 |
with gr.Blocks() as demo:
|
| 99 |
+
gr.HTML("<h2 style='text-align: center; margin: 10px 0;'>๐๏ธ Interior Design Model Arena</h2>")
|
| 100 |
|
| 101 |
with gr.Row():
|
|
|
|
|
|
|
| 102 |
with gr.Column(scale=1):
|
| 103 |
+
gr.HTML("<center><b>ORIGINAL REFERENCE</b></center>")
|
| 104 |
+
out_og = gr.Image(show_label=False, type="pil", height=320)
|
| 105 |
+
|
| 106 |
+
with gr.Column(scale=1):
|
| 107 |
+
input_prompt = gr.Textbox(label="Edit Prompt", value=DEFAULT_PROMPT, lines=4)
|
| 108 |
+
with gr.Row():
|
| 109 |
+
input_file = gr.Image(label="Upload", type="filepath", height=100)
|
| 110 |
+
input_url = gr.Textbox(label="OR: Image URL", placeholder="Paste URL...")
|
| 111 |
+
run_btn = gr.Button("๐ Generate Comparison", variant="primary")
|
| 112 |
+
|
| 113 |
+
gr.HTML("<hr style='margin: 15px 0;'>")
|
| 114 |
|
|
|
|
| 115 |
with gr.Row():
|
| 116 |
with gr.Column():
|
| 117 |
+
gr.HTML("<center><b>QWEN-EDIT</b><br><small>$0.004 - $0.008</small></center>")
|
| 118 |
+
out_qwen = gr.Image(show_label=False, type="pil", height=350)
|
| 119 |
|
| 120 |
with gr.Column():
|
| 121 |
gr.HTML("<center><b>FLUX-2 EDIT</b><br><small>$0.03</small></center>")
|
| 122 |
+
out_fal = gr.Image(show_label=False, type="pil", height=350)
|
| 123 |
|
| 124 |
with gr.Column():
|
| 125 |
gr.HTML("<center><b>GEMINI 2.5 FLASH</b><br><small>$0.039</small></center>")
|
| 126 |
+
out_gemini = gr.Image(show_label=False, type="pil", height=350)
|
| 127 |
|
| 128 |
+
run_btn.click(fn=compare_all, inputs=[input_file, input_url, input_prompt], outputs=[out_og, out_qwen, out_fal, out_gemini])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
if __name__ == "__main__":
|
| 131 |
demo.launch(
|
| 132 |
+
server_name="0.0.0.0", server_port=7860, theme=gr.themes.Soft(),
|
| 133 |
+
css=".gradio-container {max-width: 98% !important} img {object-fit: contain !important;}"
|
|
|
|
|
|
|
| 134 |
)
|