Spaces:
Running
Running
import requests | |
import base64 | |
import json | |
import os | |
from io import BytesIO | |
from typing import Optional, Dict | |
# Pillow is required for image format conversion and normalization. | |
# Please install it using: pip install Pillow | |
try: | |
from PIL import Image | |
except ImportError: | |
print("Pillow library not found. Please install it using: pip install Pillow") | |
exit() | |
# --- Configuration --- | |
IMGBB_API_KEY = os.getenv("IMGBB_API_KEY") | |
def upload_to_imgbb(image_path: str, file_name: str) -> Optional[str]: | |
""" | |
Uploads the image at image_path to ImgBB. | |
Returns the public URL or None on failure. | |
""" | |
if not IMGBB_API_KEY: | |
print("Warning: IMGBB_API_KEY not set, skipping upload") | |
return None | |
print(f"Uploading {file_name} to ImgBB...") | |
try: | |
with open(image_path, 'rb') as f: | |
files = {"image": (file_name, f.read())} | |
resp = requests.post( | |
"https://api.imgbb.com/1/upload", | |
params={"key": IMGBB_API_KEY}, | |
files=files, | |
timeout=20 | |
) | |
resp.raise_for_status() | |
data = resp.json().get("data", {}) | |
url = data.get("url") | |
if url: | |
print(f"Successfully uploaded to ImgBB: {url}") | |
return url | |
else: | |
print(f"Error: ImgBB API response missing 'url'. Response: {resp.json()}") | |
return None | |
except requests.exceptions.RequestException as e: | |
print(f"Error: ImgBB upload failed: {e}") | |
return None | |
def _download_image_from_url(image_url: str, save_path: str) -> bool: | |
""" | |
Downloads an image from a URL and saves it to a local path. | |
Args: | |
image_url: The URL of the image to download. | |
save_path: The local path to save the downloaded image. | |
Returns: | |
True if the download was successful, False otherwise. | |
""" | |
print(f"Downloading generated image from: {image_url}") | |
try: | |
image_response = requests.get(image_url, stream=True, timeout=30) | |
# Check if the download request was successful. | |
if image_response.status_code == 200: | |
content_type = image_response.headers.get('Content-Type', '') | |
if 'image' in content_type: | |
with open(save_path, 'wb') as f: | |
for chunk in image_response.iter_content(1024): | |
f.write(chunk) | |
print(f"Image successfully saved to {save_path}") | |
return True | |
else: | |
print(f"Error: Content at URL is not an image. Content-Type: {content_type}") | |
return False | |
else: | |
print(f"Error: Failed to download image. Status code: {image_response.status_code}") | |
return False | |
except requests.exceptions.RequestException as e: | |
print(f"An error occurred during image download: {e}") | |
return False | |
def generate_image( | |
prompt_text: str, | |
image_path: str, | |
download_path: Optional[str] = None | |
) -> Optional[Dict]: | |
""" | |
Sends a request to the image generation API, optionally downloads the result, | |
and uploads it to ImgBB. | |
Args: | |
prompt_text: The instructional text for image modification. | |
image_path: The file path to the input image (any common format). | |
download_path: If provided, the path to save the generated image. | |
Returns: | |
A dictionary of the JSON response from the API, including the ImgBB URL, | |
or None on error. | |
""" | |
url = "https://kontext-chat.replicate.dev/generate-image" | |
try: | |
# --- Image Normalization Step --- | |
with Image.open(image_path) as img: | |
if img.mode != 'RGB': | |
img = img.convert('RGB') | |
with BytesIO() as output_buffer: | |
img.save( | |
output_buffer, | |
format="JPEG", | |
quality=95, | |
subsampling=0, | |
progressive=False | |
) | |
image_bytes = output_buffer.getvalue() | |
except FileNotFoundError: | |
print(f"Error: Image file not found at {image_path}") | |
return None | |
except Exception as e: | |
print(f"Error processing image file. Ensure it's a valid image. Details: {e}") | |
return None | |
encoded_string = base64.b64encode(image_bytes).decode('utf-8') | |
input_image_data_uri = f"data:image/jpeg;base64,{encoded_string}" | |
payload = { | |
"prompt": prompt_text, | |
"input_image": input_image_data_uri | |
} | |
headers = { | |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0", | |
"Accept": "*/*", | |
"Content-Type": "application/json", | |
"Referer": "https://kontext-chat.replicate.dev/", | |
"Origin": "https://kontext-chat.replicate.dev", | |
} | |
try: | |
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=45) | |
response.raise_for_status() | |
api_response_data = response.json() | |
imgbb_url = None # Initialize imgbb_url | |
# --- Download & Upload Logic --- | |
if download_path and isinstance(api_response_data, dict): | |
image_url = api_response_data.get("imageUrl") | |
if image_url: | |
if _download_image_from_url(image_url, download_path): | |
# If download is successful, upload the saved file to ImgBB | |
file_name = os.path.basename(download_path) | |
imgbb_url = upload_to_imgbb(download_path, file_name) | |
else: | |
print("Warning: 'imageUrl' not found in API response, cannot download or upload image.") | |
# Add the ImgBB URL to the response dictionary if it was generated | |
if isinstance(api_response_data, dict): | |
api_response_data['imgbb_url'] = imgbb_url | |
return api_response_data | |
except requests.exceptions.RequestException as e: | |
print(f"API request failed: {e}") | |
return None | |