Spaces:
Running
Running
add nano banana
Browse files
app.py
CHANGED
|
@@ -2175,46 +2175,41 @@ def generate_image_with_qwen(prompt: str, image_index: int = 0, token: gr.OAuthT
|
|
| 2175 |
return f"Error generating image: {str(e)}"
|
| 2176 |
|
| 2177 |
def generate_image_to_image(input_image_data, prompt: str, token: gr.OAuthToken | None = None) -> str:
|
| 2178 |
-
"""Generate an image using image-to-image
|
| 2179 |
|
| 2180 |
-
|
|
|
|
|
|
|
| 2181 |
"""
|
| 2182 |
try:
|
| 2183 |
-
# Check
|
| 2184 |
-
|
| 2185 |
-
|
| 2186 |
-
|
| 2187 |
-
# Prepare client
|
| 2188 |
-
client = InferenceClient(
|
| 2189 |
-
provider="auto",
|
| 2190 |
-
api_key=os.getenv('HF_TOKEN'),
|
| 2191 |
-
bill_to="huggingface",
|
| 2192 |
-
)
|
| 2193 |
|
| 2194 |
# Normalize input image to bytes
|
| 2195 |
import io
|
| 2196 |
from PIL import Image
|
|
|
|
|
|
|
|
|
|
| 2197 |
try:
|
| 2198 |
import numpy as np
|
| 2199 |
except Exception:
|
| 2200 |
np = None
|
| 2201 |
|
| 2202 |
if hasattr(input_image_data, 'read'):
|
| 2203 |
-
# File-like object
|
| 2204 |
raw = input_image_data.read()
|
| 2205 |
pil_image = Image.open(io.BytesIO(raw))
|
| 2206 |
elif hasattr(input_image_data, 'mode') and hasattr(input_image_data, 'size'):
|
| 2207 |
-
# PIL Image
|
| 2208 |
pil_image = input_image_data
|
| 2209 |
elif np is not None and isinstance(input_image_data, np.ndarray):
|
| 2210 |
pil_image = Image.fromarray(input_image_data)
|
| 2211 |
elif isinstance(input_image_data, (bytes, bytearray)):
|
| 2212 |
pil_image = Image.open(io.BytesIO(input_image_data))
|
| 2213 |
else:
|
| 2214 |
-
# Fallback: try to convert via bytes
|
| 2215 |
pil_image = Image.open(io.BytesIO(bytes(input_image_data)))
|
| 2216 |
|
| 2217 |
-
# Ensure RGB
|
| 2218 |
if pil_image.mode != 'RGB':
|
| 2219 |
pil_image = pil_image.convert('RGB')
|
| 2220 |
|
|
@@ -2223,34 +2218,84 @@ def generate_image_to_image(input_image_data, prompt: str, token: gr.OAuthToken
|
|
| 2223 |
if pil_image.width > max_input_size or pil_image.height > max_input_size:
|
| 2224 |
pil_image.thumbnail((max_input_size, max_input_size), Image.Resampling.LANCZOS)
|
| 2225 |
|
| 2226 |
-
|
| 2227 |
-
|
| 2228 |
-
|
| 2229 |
-
|
| 2230 |
-
|
| 2231 |
-
image = client.image_to_image(
|
| 2232 |
-
input_bytes,
|
| 2233 |
-
prompt=prompt,
|
| 2234 |
-
model="Qwen/Qwen-Image-Edit",
|
| 2235 |
-
)
|
| 2236 |
-
|
| 2237 |
-
# Resize/optimize (larger since not using data URIs)
|
| 2238 |
-
max_size = 1024
|
| 2239 |
-
if image.width > max_size or image.height > max_size:
|
| 2240 |
-
image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
|
| 2241 |
|
| 2242 |
-
|
| 2243 |
-
|
| 2244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2245 |
|
| 2246 |
-
#
|
| 2247 |
filename = "image_to_image_result.jpg"
|
| 2248 |
temp_url = upload_media_to_hf(image_bytes, filename, "image", token, use_temp=True)
|
| 2249 |
-
|
| 2250 |
-
# Check if creation was successful
|
| 2251 |
if temp_url.startswith("Error"):
|
| 2252 |
return temp_url
|
| 2253 |
-
|
| 2254 |
return f"<img src=\"{temp_url}\" alt=\"{prompt}\" style=\"max-width: 100%; height: auto; border-radius: 8px; margin: 10px 0;\" loading=\"lazy\" />"
|
| 2255 |
except Exception as e:
|
| 2256 |
print(f"Image-to-image generation error: {str(e)}")
|
|
|
|
| 2175 |
return f"Error generating image: {str(e)}"
|
| 2176 |
|
| 2177 |
def generate_image_to_image(input_image_data, prompt: str, token: gr.OAuthToken | None = None) -> str:
|
| 2178 |
+
"""Generate an image using image-to-image via OpenRouter.
|
| 2179 |
|
| 2180 |
+
Uses Google Gemini 2.5 Flash Image Preview via OpenRouter chat completions API.
|
| 2181 |
+
|
| 2182 |
+
Returns an HTML <img> tag whose src is an uploaded temporary URL.
|
| 2183 |
"""
|
| 2184 |
try:
|
| 2185 |
+
# Check for OpenRouter API key
|
| 2186 |
+
openrouter_key = os.getenv('OPENROUTER_API_KEY')
|
| 2187 |
+
if not openrouter_key:
|
| 2188 |
+
return "Error: OPENROUTER_API_KEY environment variable is not set. Please set it to your OpenRouter API key."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2189 |
|
| 2190 |
# Normalize input image to bytes
|
| 2191 |
import io
|
| 2192 |
from PIL import Image
|
| 2193 |
+
import base64
|
| 2194 |
+
import requests
|
| 2195 |
+
import json as _json
|
| 2196 |
try:
|
| 2197 |
import numpy as np
|
| 2198 |
except Exception:
|
| 2199 |
np = None
|
| 2200 |
|
| 2201 |
if hasattr(input_image_data, 'read'):
|
|
|
|
| 2202 |
raw = input_image_data.read()
|
| 2203 |
pil_image = Image.open(io.BytesIO(raw))
|
| 2204 |
elif hasattr(input_image_data, 'mode') and hasattr(input_image_data, 'size'):
|
|
|
|
| 2205 |
pil_image = input_image_data
|
| 2206 |
elif np is not None and isinstance(input_image_data, np.ndarray):
|
| 2207 |
pil_image = Image.fromarray(input_image_data)
|
| 2208 |
elif isinstance(input_image_data, (bytes, bytearray)):
|
| 2209 |
pil_image = Image.open(io.BytesIO(input_image_data))
|
| 2210 |
else:
|
|
|
|
| 2211 |
pil_image = Image.open(io.BytesIO(bytes(input_image_data)))
|
| 2212 |
|
|
|
|
| 2213 |
if pil_image.mode != 'RGB':
|
| 2214 |
pil_image = pil_image.convert('RGB')
|
| 2215 |
|
|
|
|
| 2218 |
if pil_image.width > max_input_size or pil_image.height > max_input_size:
|
| 2219 |
pil_image.thumbnail((max_input_size, max_input_size), Image.Resampling.LANCZOS)
|
| 2220 |
|
| 2221 |
+
# Convert to base64
|
| 2222 |
+
import io as _io
|
| 2223 |
+
buffered = _io.BytesIO()
|
| 2224 |
+
pil_image.save(buffered, format='PNG')
|
| 2225 |
+
img_b64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2226 |
|
| 2227 |
+
# Call OpenRouter API
|
| 2228 |
+
headers = {
|
| 2229 |
+
"Authorization": f"Bearer {openrouter_key}",
|
| 2230 |
+
"Content-Type": "application/json",
|
| 2231 |
+
"HTTP-Referer": os.getenv("YOUR_SITE_URL", "https://example.com"),
|
| 2232 |
+
"X-Title": os.getenv("YOUR_SITE_NAME", "AnyCoder Image I2I"),
|
| 2233 |
+
}
|
| 2234 |
+
payload = {
|
| 2235 |
+
"model": "google/gemini-2.5-flash-image-preview:free",
|
| 2236 |
+
"messages": [
|
| 2237 |
+
{
|
| 2238 |
+
"role": "user",
|
| 2239 |
+
"content": [
|
| 2240 |
+
{"type": "text", "text": prompt},
|
| 2241 |
+
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_b64}"}},
|
| 2242 |
+
],
|
| 2243 |
+
}
|
| 2244 |
+
],
|
| 2245 |
+
"max_tokens": 2048,
|
| 2246 |
+
}
|
| 2247 |
+
|
| 2248 |
+
try:
|
| 2249 |
+
resp = requests.post(
|
| 2250 |
+
"https://openrouter.ai/api/v1/chat/completions",
|
| 2251 |
+
headers=headers,
|
| 2252 |
+
data=_json.dumps(payload),
|
| 2253 |
+
timeout=60,
|
| 2254 |
+
)
|
| 2255 |
+
resp.raise_for_status()
|
| 2256 |
+
result_data = resp.json()
|
| 2257 |
+
|
| 2258 |
+
# Corrected response parsing logic
|
| 2259 |
+
message = result_data.get('choices', [{}])[0].get('message', {})
|
| 2260 |
+
|
| 2261 |
+
if message and 'images' in message and message['images']:
|
| 2262 |
+
# Get the first image from the 'images' list
|
| 2263 |
+
image_data = message['images'][0]
|
| 2264 |
+
base64_string = image_data.get('image_url', {}).get('url', '')
|
| 2265 |
+
|
| 2266 |
+
if base64_string and ',' in base64_string:
|
| 2267 |
+
# Remove the "data:image/png;base64," prefix
|
| 2268 |
+
base64_content = base64_string.split(',')[1]
|
| 2269 |
+
|
| 2270 |
+
# Decode the base64 string and create a PIL image
|
| 2271 |
+
img_bytes = base64.b64decode(base64_content)
|
| 2272 |
+
edited_image = Image.open(_io.BytesIO(img_bytes))
|
| 2273 |
+
|
| 2274 |
+
# Convert PIL image to JPEG bytes for upload
|
| 2275 |
+
out_buf = _io.BytesIO()
|
| 2276 |
+
edited_image.convert('RGB').save(out_buf, format='JPEG', quality=90, optimize=True)
|
| 2277 |
+
image_bytes = out_buf.getvalue()
|
| 2278 |
+
else:
|
| 2279 |
+
raise RuntimeError(f"API returned an invalid image format. Response: {_json.dumps(result_data, indent=2)}")
|
| 2280 |
+
else:
|
| 2281 |
+
raise RuntimeError(f"API did not return an image. Full Response: {_json.dumps(result_data, indent=2)}")
|
| 2282 |
+
|
| 2283 |
+
except requests.exceptions.HTTPError as err:
|
| 2284 |
+
error_body = err.response.text
|
| 2285 |
+
if err.response.status_code == 401:
|
| 2286 |
+
return "Error: Authentication failed. Check your OpenRouter API key."
|
| 2287 |
+
elif err.response.status_code == 429:
|
| 2288 |
+
return "Error: Rate limit exceeded or insufficient credits. Check your OpenRouter account."
|
| 2289 |
+
else:
|
| 2290 |
+
return f"Error: An API error occurred: {error_body}"
|
| 2291 |
+
except Exception as e:
|
| 2292 |
+
return f"Error: An unexpected error occurred: {str(e)}"
|
| 2293 |
|
| 2294 |
+
# Upload and return HTML tag
|
| 2295 |
filename = "image_to_image_result.jpg"
|
| 2296 |
temp_url = upload_media_to_hf(image_bytes, filename, "image", token, use_temp=True)
|
|
|
|
|
|
|
| 2297 |
if temp_url.startswith("Error"):
|
| 2298 |
return temp_url
|
|
|
|
| 2299 |
return f"<img src=\"{temp_url}\" alt=\"{prompt}\" style=\"max-width: 100%; height: auto; border-radius: 8px; margin: 10px 0;\" loading=\"lazy\" />"
|
| 2300 |
except Exception as e:
|
| 2301 |
print(f"Image-to-image generation error: {str(e)}")
|