Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
-
# app.py
|
2 |
"""
|
3 |
-
Gradio Space: GPT‑Image‑1 – BYOT playground
|
4 |
Generate · Edit (paint mask!) · Variations
|
5 |
==========================================
|
6 |
Adds an **in‑browser paint tool** for the edit / inpaint workflow so users can
|
@@ -10,7 +9,7 @@ draw the mask directly instead of uploading one.
|
|
10 |
* Upload an image.
|
11 |
* Use the *Mask* canvas to **paint the areas you’d like changed** (white =
|
12 |
editable, black = keep).
|
13 |
-
|
14 |
* The painted mask is converted to a 1‑channel PNG and sent to the
|
15 |
`images.edit()` endpoint.
|
16 |
|
@@ -22,7 +21,7 @@ from __future__ import annotations
|
|
22 |
|
23 |
import io
|
24 |
import os
|
25 |
-
from typing import List, Optional
|
26 |
|
27 |
import gradio as gr
|
28 |
import numpy as np
|
@@ -105,10 +104,33 @@ def _bytes_from_numpy(arr: np.ndarray) -> bytes:
|
|
105 |
return out.getvalue()
|
106 |
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
def edit_image(
|
109 |
api_key: str,
|
110 |
image_numpy: np.ndarray,
|
111 |
-
|
112 |
prompt: str,
|
113 |
n: int,
|
114 |
size: str,
|
@@ -122,11 +144,13 @@ def edit_image(
|
|
122 |
img_bytes = _bytes_from_numpy(image_numpy)
|
123 |
|
124 |
mask_bytes: Optional[bytes] = None
|
|
|
|
|
125 |
if mask_numpy is not None:
|
126 |
-
# Convert painted area (
|
127 |
-
if mask_numpy.shape[-1] == 4: # RGBA
|
128 |
alpha = mask_numpy[:, :, 3]
|
129 |
-
else: # RGB
|
130 |
alpha = np.any(mask_numpy != 0, axis=-1).astype(np.uint8) * 255
|
131 |
bw = np.stack([alpha] * 3, axis=-1) # 3‑channel white/black
|
132 |
mask_bytes = _bytes_from_numpy(bw)
|
@@ -206,7 +230,7 @@ def build_ui():
|
|
206 |
# ----- Edit Tab ----- #
|
207 |
with gr.TabItem("Edit / Inpaint"):
|
208 |
img_edit = gr.Image(label="Image", type="numpy")
|
209 |
-
mask_canvas = gr.
|
210 |
prompt_edit = gr.Textbox(label="Edit prompt", lines=2, placeholder="Replace the sky with a starry night")
|
211 |
btn_edit = gr.Button("Edit 🖌️")
|
212 |
gallery_edit = gr.Gallery(columns=2, height="auto")
|
|
|
|
|
1 |
"""
|
2 |
+
Gradio Space: GPT‑Image‑1 – BYOT playground
|
3 |
Generate · Edit (paint mask!) · Variations
|
4 |
==========================================
|
5 |
Adds an **in‑browser paint tool** for the edit / inpaint workflow so users can
|
|
|
9 |
* Upload an image.
|
10 |
* Use the *Mask* canvas to **paint the areas you’d like changed** (white =
|
11 |
editable, black = keep).
|
12 |
+
The new `gr.ImageMask` component captures your brush strokes.
|
13 |
* The painted mask is converted to a 1‑channel PNG and sent to the
|
14 |
`images.edit()` endpoint.
|
15 |
|
|
|
21 |
|
22 |
import io
|
23 |
import os
|
24 |
+
from typing import List, Optional, Union, Dict, Any
|
25 |
|
26 |
import gradio as gr
|
27 |
import numpy as np
|
|
|
104 |
return out.getvalue()
|
105 |
|
106 |
|
107 |
+
def _extract_mask_array(mask_value: Union[np.ndarray, Dict[str, Any], None]) -> Optional[np.ndarray]:
|
108 |
+
"""Handle ImageMask / ImageEditor return formats and extract a numpy mask array."""
|
109 |
+
if mask_value is None:
|
110 |
+
return None
|
111 |
+
|
112 |
+
# If we already have a numpy array (ImageMask with type="numpy")
|
113 |
+
if isinstance(mask_value, np.ndarray):
|
114 |
+
return mask_value
|
115 |
+
|
116 |
+
# If it's an EditorValue dict coming from ImageEditor/ImageMask with type="numpy"
|
117 |
+
if isinstance(mask_value, dict):
|
118 |
+
# Prefer the composite (all layers merged) if present
|
119 |
+
comp = mask_value.get("composite")
|
120 |
+
if comp is not None:
|
121 |
+
return np.asarray(comp)
|
122 |
+
# Fallback to the topmost layer
|
123 |
+
layers = mask_value.get("layers")
|
124 |
+
if layers:
|
125 |
+
return np.asarray(layers[-1])
|
126 |
+
# Unknown format – ignore
|
127 |
+
return None
|
128 |
+
|
129 |
+
|
130 |
def edit_image(
|
131 |
api_key: str,
|
132 |
image_numpy: np.ndarray,
|
133 |
+
mask_value: Optional[Union[np.ndarray, Dict[str, Any]]],
|
134 |
prompt: str,
|
135 |
n: int,
|
136 |
size: str,
|
|
|
144 |
img_bytes = _bytes_from_numpy(image_numpy)
|
145 |
|
146 |
mask_bytes: Optional[bytes] = None
|
147 |
+
mask_numpy = _extract_mask_array(mask_value)
|
148 |
+
|
149 |
if mask_numpy is not None:
|
150 |
+
# Convert painted area (any non‑zero pixel) to white, else black; 1‑channel.
|
151 |
+
if mask_numpy.shape[-1] == 4: # RGBA (has alpha channel)
|
152 |
alpha = mask_numpy[:, :, 3]
|
153 |
+
else: # RGB or grayscale
|
154 |
alpha = np.any(mask_numpy != 0, axis=-1).astype(np.uint8) * 255
|
155 |
bw = np.stack([alpha] * 3, axis=-1) # 3‑channel white/black
|
156 |
mask_bytes = _bytes_from_numpy(bw)
|
|
|
230 |
# ----- Edit Tab ----- #
|
231 |
with gr.TabItem("Edit / Inpaint"):
|
232 |
img_edit = gr.Image(label="Image", type="numpy")
|
233 |
+
mask_canvas = gr.ImageMask(label="Mask – paint white where the image should change", type="numpy")
|
234 |
prompt_edit = gr.Textbox(label="Edit prompt", lines=2, placeholder="Replace the sky with a starry night")
|
235 |
btn_edit = gr.Button("Edit 🖌️")
|
236 |
gallery_edit = gr.Gallery(columns=2, height="auto")
|