Spaces:
Running
Running
mask tự động giữ nguyên vùng SP/người, chỉ edit background
Browse files
backend/app/api/listing.py
CHANGED
|
@@ -294,7 +294,10 @@ def _run_generation(sid, image_prompts):
|
|
| 294 |
output_path = os.path.join(s["dir"], f"{p['type']}_{sid}.png")
|
| 295 |
|
| 296 |
if is_openai:
|
| 297 |
-
|
|
|
|
|
|
|
|
|
|
| 298 |
else:
|
| 299 |
cf_label = cf_account.get("custom", False) and "CF custom" or f"CF #{cf_account_tried + 1}"
|
| 300 |
s["last_status"] = {"level": "info", "message": f" → dùng {cf_label}", "time": time.time()}
|
|
@@ -339,7 +342,7 @@ def _run_generation(sid, image_prompts):
|
|
| 339 |
continue
|
| 340 |
try:
|
| 341 |
fb_path = os.path.join(s["dir"], f"{p['type']}_{sid}_fallback.png")
|
| 342 |
-
generate_image_openai(base_prompt, fb_path, model, ref_img, quality, api_key=custom_image_key or custom_key)
|
| 343 |
s["results"].append({
|
| 344 |
"type": p["type"], "label": p.get("label", p["type"]),
|
| 345 |
"prompt": base_prompt, "images": [fb_path], "status": "success",
|
|
@@ -481,7 +484,7 @@ async def edit_image(
|
|
| 481 |
modified_prompt = f"{orig_prompt}\n\n{preserve}\n\nYêu cầu chỉnh sửa: {modification}"
|
| 482 |
custom_key = s.get("api_key")
|
| 483 |
custom_image_key = s.get("image_api_key")
|
| 484 |
-
generate_image_openai(modified_prompt, edit_path, model, product_ref, "low", api_key=custom_image_key or custom_key)
|
| 485 |
else:
|
| 486 |
modified_prompt = f"{orig_prompt}\n\n{preserve}\n\nYêu cầu chỉnh sửa: {modification}" if not ref_path else modification
|
| 487 |
cf_account_id = s.get("cf_account_id") or None
|
|
|
|
| 294 |
output_path = os.path.join(s["dir"], f"{p['type']}_{sid}.png")
|
| 295 |
|
| 296 |
if is_openai:
|
| 297 |
+
mask_mode = "auto"
|
| 298 |
+
if ref_type == "scene":
|
| 299 |
+
mask_mode = "invert"
|
| 300 |
+
generate_image_openai(base_prompt, output_path, model, ref_img, quality, api_key=custom_image_key or custom_key, mask_mode=mask_mode)
|
| 301 |
else:
|
| 302 |
cf_label = cf_account.get("custom", False) and "CF custom" or f"CF #{cf_account_tried + 1}"
|
| 303 |
s["last_status"] = {"level": "info", "message": f" → dùng {cf_label}", "time": time.time()}
|
|
|
|
| 342 |
continue
|
| 343 |
try:
|
| 344 |
fb_path = os.path.join(s["dir"], f"{p['type']}_{sid}_fallback.png")
|
| 345 |
+
generate_image_openai(base_prompt, fb_path, model, ref_img, quality, api_key=custom_image_key or custom_key, mask_mode=mask_mode)
|
| 346 |
s["results"].append({
|
| 347 |
"type": p["type"], "label": p.get("label", p["type"]),
|
| 348 |
"prompt": base_prompt, "images": [fb_path], "status": "success",
|
|
|
|
| 484 |
modified_prompt = f"{orig_prompt}\n\n{preserve}\n\nYêu cầu chỉnh sửa: {modification}"
|
| 485 |
custom_key = s.get("api_key")
|
| 486 |
custom_image_key = s.get("image_api_key")
|
| 487 |
+
generate_image_openai(modified_prompt, edit_path, model, product_ref, "low", api_key=custom_image_key or custom_key, mask_mode="auto")
|
| 488 |
else:
|
| 489 |
modified_prompt = f"{orig_prompt}\n\n{preserve}\n\nYêu cầu chỉnh sửa: {modification}" if not ref_path else modification
|
| 490 |
cf_account_id = s.get("cf_account_id") or None
|
backend/app/services/listing/generator.py
CHANGED
|
@@ -2,7 +2,21 @@ import base64
|
|
| 2 |
import io
|
| 3 |
import os
|
| 4 |
import requests
|
| 5 |
-
from PIL import Image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
|
| 8 |
CF_ACCOUNTS = [
|
|
@@ -116,7 +130,7 @@ def generate_image_flux(prompt, output_path, input_path=None, negative_prompt=No
|
|
| 116 |
raise RuntimeError(f"Flux API error: {resp.status_code} {resp.text[:500]}")
|
| 117 |
|
| 118 |
|
| 119 |
-
def generate_image_openai(prompt, output_path, model, input_path=None, quality="high", api_key=None):
|
| 120 |
import requests as req
|
| 121 |
from ...config import settings
|
| 122 |
api_key = api_key or settings.OPENAI_IMAGE_API_KEY or settings.OPENAI_API_KEY
|
|
@@ -132,13 +146,24 @@ def generate_image_openai(prompt, output_path, model, input_path=None, quality="
|
|
| 132 |
resp_json = None
|
| 133 |
if input_path and os.path.exists(input_path):
|
| 134 |
pil_img = Image.open(input_path).convert("RGBA")
|
| 135 |
-
|
| 136 |
-
pil_img.save(
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
r = req.post(
|
| 139 |
"https://api.openai.com/v1/images/edits",
|
| 140 |
headers=headers,
|
| 141 |
-
files=
|
| 142 |
data=body,
|
| 143 |
timeout=300,
|
| 144 |
)
|
|
@@ -146,7 +171,7 @@ def generate_image_openai(prompt, output_path, model, input_path=None, quality="
|
|
| 146 |
resp_json = r.json()
|
| 147 |
else:
|
| 148 |
err_msg = f"Edits endpoint failed: HTTP {r.status_code} {r.text[:500]}"
|
| 149 |
-
print(f"[generator] {err_msg}")
|
| 150 |
raise RuntimeError(err_msg)
|
| 151 |
|
| 152 |
if resp_json is None:
|
|
|
|
| 2 |
import io
|
| 3 |
import os
|
| 4 |
import requests
|
| 5 |
+
from PIL import Image, ImageFilter
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _make_mask(img, invert=False):
|
| 9 |
+
gray = img.convert("L")
|
| 10 |
+
threshold = 200
|
| 11 |
+
mask = gray.point(lambda p: 0 if p > threshold else 255)
|
| 12 |
+
if invert:
|
| 13 |
+
mask = mask.point(lambda p: 255 - p)
|
| 14 |
+
mask = mask.filter(ImageFilter.MaxFilter(7))
|
| 15 |
+
mask = mask.filter(ImageFilter.GaussianBlur(radius=3))
|
| 16 |
+
ext = mask.getextrema()
|
| 17 |
+
if ext == (255, 255) or ext == (0, 0):
|
| 18 |
+
return None
|
| 19 |
+
return mask.convert("L")
|
| 20 |
|
| 21 |
|
| 22 |
CF_ACCOUNTS = [
|
|
|
|
| 130 |
raise RuntimeError(f"Flux API error: {resp.status_code} {resp.text[:500]}")
|
| 131 |
|
| 132 |
|
| 133 |
+
def generate_image_openai(prompt, output_path, model, input_path=None, quality="high", api_key=None, mask_mode="auto"):
|
| 134 |
import requests as req
|
| 135 |
from ...config import settings
|
| 136 |
api_key = api_key or settings.OPENAI_IMAGE_API_KEY or settings.OPENAI_API_KEY
|
|
|
|
| 146 |
resp_json = None
|
| 147 |
if input_path and os.path.exists(input_path):
|
| 148 |
pil_img = Image.open(input_path).convert("RGBA")
|
| 149 |
+
img_buf = io.BytesIO()
|
| 150 |
+
pil_img.save(img_buf, format="PNG")
|
| 151 |
+
img_buf.seek(0)
|
| 152 |
+
|
| 153 |
+
files = {"image": ("input.png", img_buf, "image/png")}
|
| 154 |
+
|
| 155 |
+
if mask_mode != "none":
|
| 156 |
+
mask = _make_mask(pil_img, invert=(mask_mode == "invert"))
|
| 157 |
+
if mask is not None:
|
| 158 |
+
mask_buf = io.BytesIO()
|
| 159 |
+
mask.save(mask_buf, format="PNG")
|
| 160 |
+
mask_buf.seek(0)
|
| 161 |
+
files["mask"] = ("mask.png", mask_buf, "image/png")
|
| 162 |
+
|
| 163 |
r = req.post(
|
| 164 |
"https://api.openai.com/v1/images/edits",
|
| 165 |
headers=headers,
|
| 166 |
+
files=files,
|
| 167 |
data=body,
|
| 168 |
timeout=300,
|
| 169 |
)
|
|
|
|
| 171 |
resp_json = r.json()
|
| 172 |
else:
|
| 173 |
err_msg = f"Edits endpoint failed: HTTP {r.status_code} {r.text[:500]}"
|
| 174 |
+
print(f"[generator] {err_msg}")
|
| 175 |
raise RuntimeError(err_msg)
|
| 176 |
|
| 177 |
if resp_json is None:
|