import gradio as gr import requests import io from PIL import Image, UnidentifiedImageError import os import sys import traceback import base64 from json import JSONDecodeError # 导入 JSONDecodeError print("Current Python version:") print(sys.version) # 使用 InstructPix2Pix API URL API_URL = "https://api-inference.huggingface.co/models/timbrooks/instruct-pix2pix" HF_TOKEN = os.environ.get("HF_TOKEN") HEADERS = {} if not HF_TOKEN: print("警告:未在 Space Secrets 中找到 HF_TOKEN!API 调用可能会失败。") else: HEADERS = { "Authorization": f"Bearer {HF_TOKEN}", "Content-Type": "application/json" } print("Successfully loaded HF_TOKEN from secrets.") # 处理函数 def edit_image_with_text(image, prompt): if image is None: print("Error: Input image is None.") return "错误:未检测到输入图片,请上传图片。" if not HEADERS or "Authorization" not in HEADERS: return "错误:Hugging Face API Token 未配置。" if not isinstance(image, Image.Image): try: image = Image.fromarray(image) print("Input converted from array to PIL Image.") except AttributeError: print("Error converting input: Input might be None or invalid type.") return "错误:输入图片类型无效。" except Exception as e: print(f"Error converting input image to PIL: {e}") return f"错误:处理输入图片时出错 - {e}" print(f"Processing request with prompt: '{prompt}' for image size: {image.size}") # 将 PIL Image 转换为 Base64 try: buffered = io.BytesIO() if image.mode != 'RGB': image = image.convert('RGB') image.save(buffered, format="JPEG") img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") print("Image converted to Base64 string.") except Exception as e: print(f"Error converting image to Base64: {e}") return f"错误:转换图片为 Base64 时出错 - {e}" # 构造 JSON payload payload = { "inputs": img_str, "parameters": { "prompt": prompt } } print("Sending request to Hugging Face API (JSON payload)...") try: response = requests.post( API_URL, headers=HEADERS, json=payload, timeout=180 ) # --- **修改点:改进响应处理逻辑** --- print(f"API request finished with status code: {response.status_code}.") # 打印状态码 # 首先检查状态码是否成功 (2xx) if response.ok: # response.ok 检查状态码是否在 200-299 之间 # 尝试将响应体作为图片直接打开 try: # 假设 API 直接返回图片二进制数据 result_image = Image.open(io.BytesIO(response.content)) print("Successfully opened API response content as image.") return result_image except UnidentifiedImageError: # 如果直接打开失败,再尝试按 JSON 解析 (以防万一) print("Could not open response as image directly, attempting to parse as JSON...") try: result_data = response.json() base64_image = None # ... (之前的 JSON 解析逻辑) ... if isinstance(result_data, list) and len(result_data) > 0 and isinstance(result_data[0], dict) and "blob" in result_data[0]: base64_image = result_data[0]["blob"] elif isinstance(result_data, list) and len(result_data) > 0 and isinstance(result_data[0], str): base64_image = result_data[0] # ... (可以添加更多对不同 JSON 结构的检查) ... if base64_image: if "," in base64_image: base64_image = base64_image.split(',')[1] img_data = base64.b64decode(base64_image) result_image = Image.open(io.BytesIO(img_data)) print("Successfully decoded image from JSON response.") return result_image else: print(f"Error: Could not find image data in JSON response: {result_data}") return f"错误:API 响应 JSON 中未找到图片数据。" except JSONDecodeError: # 如果连 JSON 解析也失败,说明响应格式未知 print("Error: API response is not a valid image nor valid JSON.") print(f"API Response Content (first 100 bytes): {response.content[:100]}") return "错误:API 返回了无法识别的数据格式。" except (KeyError, IndexError, TypeError, base64.binascii.Error, UnidentifiedImageError) as decode_e: print(f"Error decoding JSON response: {decode_e}") print(f"API Response Content: {response.text[:500]}") return f"错误:解码 API 响应 JSON 失败。" except Exception as img_open_e: # 捕获 Image.open 可能的其他错误 print(f"Unexpected error opening response content as image: {img_open_e}") return f"错误:处理 API 返回的图片时出错。" else: # 如果状态码不是 2xx # 使用之前的错误处理逻辑来报告非成功状态码 response.raise_for_status() # 这将触发下面的 RequestException 处理 # ----------------------------------------- except requests.exceptions.Timeout: print("API request timed out.") return "错误:API 请求超时,请稍后再试或尝试更简单的提示。" except requests.exceptions.RequestException as e: # raise_for_status() 会触发这里 error_message = f"API Request Failed: {e}" if hasattr(e, 'response') and e.response is not None: print(f"API Error {e.response.status_code}:") try: error_detail = e.response.json() print(f"Error Detail from API: {error_detail}") error_message = f"错误:API 请求失败 ({e.response.status_code})。详情: {error_detail.get('error', e.response.text[:100])}" except JSONDecodeError: print(f"Raw Error Response ({e.response.status_code}): {e.response.text[:500]}") error_message = f"错误:API 请求失败 ({e.response.status_code})。无法解析错误详情。" else: error_message += " (Check network connection or API URL)" print(error_message) # 打印我们构造的错误消息 return error_message # 返回给用户 except Exception as e: print(f"An unexpected error occurred: {e}") print(traceback.format_exc()) return f"发生意外错误: {e}" # --- Gradio 界面定义 (保持不变) --- try: print("Attempting to create Gradio Interface for text-based editing...") iface = gr.Interface( fn=edit_image_with_text, inputs=[ gr.Image(type="pil", label="原始图片"), gr.Textbox(label="编辑指令 (Prompt)", lines=2, info="例如:'make the background a beach', 'add sunglasses to the person'") ], outputs=gr.Image(type="pil", label="结果图片"), title="AI 图片编辑 (InstructPix2Pix)", description="上传一张图片,并用文字指令描述你希望如何修改它。", ) print("Gradio Interface created successfully.") if __name__ == "__main__": print("Attempting to launch Gradio Interface...") iface.launch() print("Gradio launch command executed.") except Exception as e: print(f"Error during Gradio Interface creation or launch: {e}") print(traceback.format_exc()) print("app.py execution finished.")