返回 Pixelle-Video
image_gpt.py
1 import os
2 import time
3 import uuid
4 import base64
5 import httpx
6 from openai import OpenAI
7 try:
8 from .image_processor import ImageProcessor
9 except ImportError:
10 from image_processor import ImageProcessor
11
12
13 class ImageGPT:
14 """
15 OpenAI 图片生成客户端
16 支持模型:
17 - sora_image → Images API
18 - gpt-image-2 → Responses API
19 """
20 def __init__(self,
21 api_key: str = None,
22 base_url: str = None,
23 local_proxy: str = None,
24 timeout: float = 300.0):
25 """
26 OpenAI 图片生成客户端
27 :param api_key: API Key
28 :param base_url: 自定义 Base URL(如果传入,则不使用本地代理)
29 :param timeout: 超时时间
30 """
31 self.api_key = api_key or os.getenv("OPENAI_API_KEY")
32 self.timeout = timeout
33
34 kwargs = {"api_key": self.api_key, "timeout": self.timeout}
35
36 self.base_url = base_url
37 if local_proxy:
38 kwargs["http_client"] = httpx.Client(
39 proxy=local_proxy,
40 timeout=self.timeout,
41 )
42 if self.base_url:
43 kwargs["base_url"] = self.base_url
44
45 self.client = OpenAI(**kwargs)
46 self.max_attempts = 10
47 self.image_processor = ImageProcessor(local_proxy=local_proxy)
48
49 def _encode_image_to_base64(self, image_path: str) -> str:
50 """将本地图片转换为 Base64 编码"""
51 if not image_path or not os.path.exists(image_path):
52 return image_path
53
54 try:
55 with open(image_path, "rb") as f:
56 img_data = base64.b64encode(f.read()).decode("utf-8")
57 ext = os.path.splitext(image_path)[1].lower().replace(".", "")
58 if ext not in ["png", "jpg", "jpeg", "webp"]:
59 ext = "png"
60 return f"data:image/{ext};base64,{img_data}"
61 except Exception as e:
62 print(f"Error encoding image {image_path}: {e}")
63 return image_path
64
65 def generate_image(self, prompt, size="1024x1024", quality="high", model="gpt-image-2",
66 save_dir=None, image_urls=None):
67 """Generate a single image, download it, and return the local file path.
68
69 Args:
70 prompt: 图片描述提示词
71 size: 图片尺寸
72 quality: 图片质量
73 model: 模型名称 (sora_image / gpt-image-2)
74 save_dir: 保存目录(不传则返回 URL 或 base64)
75 image_urls: 参考图片 URL 列表(仅 gpt-image-2 支持)
76 """
77
78 attempts = 0
79 last_error = None
80
81 # 处理参考图片
82 extra_body = {}
83 if image_urls and isinstance(image_urls, list) and len(image_urls) > 0:
84 # 中转站通常支持通过 extra_body 传递 image_url 或 ref_image
85 # 这里我们将第一张图作为参考图
86 ref_images = [self._encode_image_to_base64(image_urls[i]) for i in range(min(len(image_urls), 6))]
87 extra_body = {"image_url": ref_images}
88
89 while attempts < self.max_attempts:
90 try:
91 response = self.client.images.generate(
92 model=model,
93 prompt=prompt,
94 size=size,
95 quality=quality,
96 n=1,
97 extra_body=extra_body
98 )
99
100 if not response or not response.data:
101 raise RuntimeError("OpenAI API 返回数据为空")
102
103 img_data = response.data[0]
104 file_path = None
105
106 # 1. 处理 Base64 格式 (中转站常用)
107 if hasattr(img_data, 'b64_json') and img_data.b64_json:
108 if save_dir:
109 os.makedirs(save_dir, exist_ok=True)
110 file_name = f"gpt_{int(time.time())}_{uuid.uuid4().hex[:6]}.png"
111 file_path = os.path.join(save_dir, file_name)
112 with open(file_path, "wb") as f:
113 f.write(base64.b64decode(img_data.b64_json))
114 return file_path
115 return img_data.b64_json
116
117 # 2. 处理 URL 格式
118 elif hasattr(img_data, 'url') and img_data.url:
119 url = img_data.url
120 if save_dir:
121 os.makedirs(save_dir, exist_ok=True)
122 file_name = f"gpt_{int(time.time())}_{uuid.uuid4().hex[:6]}.png"
123 file_path = os.path.join(save_dir, file_name)
124 if self.image_processor.download_image(url, file_path):
125 return file_path
126 return url
127
128 raise RuntimeError("未在响应中找到 url 或 b64_json")
129 except Exception as e:
130 last_error = e
131 msg = str(e)
132 # Other errors: wait before retry
133 print(f"Image generation error: {e}. Retrying in 10 seconds.")
134 time.sleep(10)
135 break # Break inner loop to retry all models
136 attempts += 1
137 raise Exception(f"Max attempts reached, failed to generate image. Last error: {last_error}")
138
139 def generate_images(self, prompt, count=4, size="1024x1024", quality="standard", model=None):
140 """Generate multiple image URLs by calling Images API 'count' times."""
141 urls = []
142 for _ in range(count):
143 url = self.generate_image(prompt=prompt, size=size, quality=quality, model=model)
144 urls.append(url)
145 return urls
146
147
148 if __name__ == "__main__":
149 import sys
150 import tempfile
151 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
152 from config import Config
153
154 MODELS = ["gpt-image-2"]
155 save_dir = "code/result/image/test_avail"
156 api_key = Config.OPENAI_API_KEY
157 base_url = Config.OPENAI_BASE_URL
158 if not api_key:
159 print("✗ OPENAI_API_KEY 未设置,跳过")
160 sys.exit(1)
161 print("=== GPT 图片生成测试 ===")
162 print(f" API Key: {api_key[:6]}***")
163 print(f" Base URL: {base_url}")
164
165
166 # 文生图
167 print("\n=== GPT 文生图可用性测试 ===")
168 img_prompt = "A cute orange cat lying on a sunny windowsill, watercolor style"
169 img_path = ""
170 client = ImageGPT(api_key=api_key, base_url=Config.OPENAI_BASE_URL, local_proxy=Config.LOCAL_PROXY)
171 for model in MODELS:
172 print(f"\nTesting model: {model}")
173 print(f"Prompt: {img_prompt}")
174 print(f"Image path: {img_path}")
175 client.max_attempts = 1
176 t0 = time.time()
177 os.makedirs(save_dir, exist_ok=True)
178 try:
179 path = client.generate_image(prompt=img_prompt, size="1024x1024",
180 model=model, save_dir=save_dir)
181 elapsed = time.time() - t0
182 print(f"✓ 生成成功 ({elapsed:.1f}s): {path}")
183 except Exception as e:
184 elapsed = time.time() - t0
185 print(f"✗ 失败 ({elapsed:.1f}s): {e}")
186
187 # 图生图
188 print("\n=== GPT 图生图可用性测试 ===")
189 img_prompt = "Turn this cat into a cute cartoon character with big eyes and a playful expression"
190 img_path = "code/result/image/test_avail/test_input.jpg"
191 for model in MODELS:
192 print(f"\nTesting model: {model}")
193 print(f"Prompt: {img_prompt}")
194 print(f"Image path: {img_path}")
195 client.max_attempts = 1
196 t0 = time.time()
197 os.makedirs(save_dir, exist_ok=True)
198 try:
199 path = client.generate_image(prompt=img_prompt, size="1024x1024",
200 model=model, save_dir=save_dir, image_urls=[img_path])
201 elapsed = time.time() - t0
202 print(f"✓ 生成成功 ({elapsed:.1f}s): {path}")
203 except Exception as e:
204 elapsed = time.time() - t0
205 print(f"✗ 失败 ({elapsed:.1f}s): {e}")
206
206 lines PYTHON