feat: GPT Image 请求体超 20MB 时自动等比缩放图像
This commit is contained in:
@@ -70,6 +70,41 @@ class GptImageClient:
|
|||||||
|
|
||||||
# ── 图像转换工具 ──────────────────────────────────────────────────────────
|
# ── 图像转换工具 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# ── 请求体大小限制 ────────────────────────────────────────────────────────
|
||||||
|
_MAX_BODY_BYTES = 20 * 1024 * 1024 # 20 MB
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _shrink_png_to_limit(png_bytes: bytes, max_bytes: int, label: str = "") -> bytes:
|
||||||
|
"""
|
||||||
|
若 PNG bytes 超过 max_bytes,按等比缩放反复压缩直到满足限制。
|
||||||
|
每次将面积缩小至约 80%(线性尺寸缩小至约 89.4%)。
|
||||||
|
"""
|
||||||
|
if len(png_bytes) <= max_bytes:
|
||||||
|
return png_bytes
|
||||||
|
|
||||||
|
img = Image.open(BytesIO(png_bytes))
|
||||||
|
w, h = img.size
|
||||||
|
original_size = len(png_bytes)
|
||||||
|
step = 0
|
||||||
|
|
||||||
|
while len(png_bytes) > max_bytes:
|
||||||
|
scale = 0.894 # sqrt(0.8),面积缩小 20%
|
||||||
|
w = max(1, int(w * scale))
|
||||||
|
h = max(1, int(h * scale))
|
||||||
|
img = img.resize((w, h), Image.LANCZOS)
|
||||||
|
buf = BytesIO()
|
||||||
|
img.save(buf, format="PNG")
|
||||||
|
png_bytes = buf.getvalue()
|
||||||
|
step += 1
|
||||||
|
|
||||||
|
tag = f" ({label})" if label else ""
|
||||||
|
print(
|
||||||
|
f"[o1key GPT Image] 图像{tag}超出 {max_bytes // (1024*1024)}MB 限制,"
|
||||||
|
f"已等比缩放 {step} 次:{original_size // 1024}KB → {len(png_bytes) // 1024}KB "
|
||||||
|
f"({w}×{h})"
|
||||||
|
)
|
||||||
|
return png_bytes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _tensor_to_png_bytes(tensor: torch.Tensor) -> bytes:
|
def _tensor_to_png_bytes(tensor: torch.Tensor) -> bytes:
|
||||||
"""
|
"""
|
||||||
@@ -220,8 +255,20 @@ class GptImageClient:
|
|||||||
if image_tensor is not None:
|
if image_tensor is not None:
|
||||||
pil_images = tensor_to_pil(image_tensor)
|
pil_images = tensor_to_pil(image_tensor)
|
||||||
data_urls = []
|
data_urls = []
|
||||||
for img in pil_images:
|
for idx_img, img in enumerate(pil_images):
|
||||||
b64 = encode_image_to_base64(img, format="PNG")
|
buf = BytesIO()
|
||||||
|
img.save(buf, format="PNG")
|
||||||
|
png_bytes = buf.getvalue()
|
||||||
|
# 单张图像预算:20MB 按图数平摊,至少保留 1MB 给其他字段
|
||||||
|
per_image_budget = max(
|
||||||
|
1024 * 1024,
|
||||||
|
(self._MAX_BODY_BYTES - 1024 * 1024) // len(pil_images),
|
||||||
|
)
|
||||||
|
# base64 膨胀约 4/3,所以 PNG 目标上限 = budget * 3/4
|
||||||
|
png_budget = int(per_image_budget * 3 / 4)
|
||||||
|
label = f"第{idx_img + 1}张" if len(pil_images) > 1 else ""
|
||||||
|
png_bytes = self._shrink_png_to_limit(png_bytes, png_budget, label)
|
||||||
|
b64 = base64.b64encode(png_bytes).decode("utf-8")
|
||||||
data_urls.append(f"data:image/png;base64,{b64}")
|
data_urls.append(f"data:image/png;base64,{b64}")
|
||||||
body["image"] = data_urls[0] if len(data_urls) == 1 else data_urls
|
body["image"] = data_urls[0] if len(data_urls) == 1 else data_urls
|
||||||
mode = f"图生图(参考图 {len(data_urls)} 张)"
|
mode = f"图生图(参考图 {len(data_urls)} 张)"
|
||||||
@@ -302,9 +349,17 @@ class GptImageClient:
|
|||||||
form.add_field("size", size if size else "auto")
|
form.add_field("size", size if size else "auto")
|
||||||
|
|
||||||
# 多图:用 image[] 数组字段逐张附加,支持 gpt-image-1.5 最多 16 张
|
# 多图:用 image[] 数组字段逐张附加,支持 gpt-image-1.5 最多 16 张
|
||||||
|
# 预算:20MB 按图数平摊,蒙版预留 1MB
|
||||||
|
mask_reserve = 1024 * 1024 if mask_tensor is not None else 0
|
||||||
|
per_image_budget = max(
|
||||||
|
1024 * 1024,
|
||||||
|
(self._MAX_BODY_BYTES - mask_reserve) // num_images,
|
||||||
|
)
|
||||||
for i in range(num_images):
|
for i in range(num_images):
|
||||||
frame = image_tensor[i:i+1] # [1,H,W,C]
|
frame = image_tensor[i:i+1] # [1,H,W,C]
|
||||||
img_bytes = self._tensor_to_png_bytes(frame)
|
img_bytes = self._tensor_to_png_bytes(frame)
|
||||||
|
label = f"第{i + 1}张" if num_images > 1 else ""
|
||||||
|
img_bytes = self._shrink_png_to_limit(img_bytes, per_image_budget, label)
|
||||||
form.add_field(
|
form.add_field(
|
||||||
"image[]",
|
"image[]",
|
||||||
img_bytes,
|
img_bytes,
|
||||||
|
|||||||
Reference in New Issue
Block a user