feat: AI炫图接入gpt-image-2.5云端生图(结构化提示词工程+照片参考合成)

This commit is contained in:
HHY
2026-09-25 04:15:45 -07:00
parent 710b3521a2
commit 6726a9fa3a
12 changed files with 496 additions and 48 deletions
+169
View File
@@ -0,0 +1,169 @@
"""AI 炫图提示词构建器:从 drawing_data 提取窗型结构特征,生成专业英文生图提示词。
设计原则(生图模型工程实践):
- 主体先行: 窗户的几何/材质/分格描述置前,场景氛围居后
- 结构量化: 宽高 mm、分格数、扇数、开向全部显式给出,抑制模型自由发挥
- 负面约束: 明确"保持窗型几何不变"(edits 模式关键)
- 光影专项: 时间(日/黄昏/夜)与室内外视角分别给出摄影级光照词
"""
from huamenchuang.core.drawing import parse_drawing, replay_shape
# 风格 → 型材材质/颜色描述
STYLE_FINISH = {
"modern": ("matte anthracite grey aluminum frames, minimalist slim profiles", "modern minimalist"),
"chinese": ("dark bronze aluminum frames with wood-grain inner finish and subtle Chinese lattice grille", "new-Chinese style"),
"european": ("cream white aluminum frames with classic decorative architrave", "classic European"),
"nordic": ("white aluminum frames with light oak interior foil", "Scandinavian Nordic"),
}
# 时间 → 摄影光照
TIME_LIGHT = {
"day": "bright midday daylight, soft natural shadows, clear sky",
"dusk": "warm golden-hour sunset light, long soft shadows, amber sky",
"night": "night scene, warm interior glow shining through the glass, cool blue ambience",
}
# 场景 → 场景描述(照片式 7 场景对齐源站 renderV2)
SCENE_DESC = {
"render": "the window installed on a clean rendered exterior wall, straight-on elevation view",
"install": "the brand-new window just mounted into the wall opening, installation foam and fixing screws still visible at the edges, home renovation context",
"erase": "renovation scene: the old worn window has been removed from the masonry opening and this new window is being fitted in its place, dusty sill",
"indoor": "interior view, the window as the focal point of a tastefully furnished {style_word} living room with sofa, wooden floor, curtains and green plants",
"villa": "exterior view of a {style_word} villa facade featuring this large window, landscaped garden with lawn below, blue sky",
"fence": "view from inside looking out through the window, clear unobstructed garden and distant hills beyond the glass, no railings or bars outside",
"blank": "unfurnished rough-condition room with bare grey cement walls, the window as the only fitted element, pre-renovation state",
}
# 快速模式场景(无照片)
QUICK_SCENE = {
"indoor": "interior view in a tastefully furnished {style_word} room, the window as the light source",
"outdoor": "exterior facade view on a residential building, mild urban greenery around",
"studio": "studio product shot on a seamless neutral background, softbox lighting, slight floor reflection",
}
def _sash_kind(sub: dict) -> str | None:
t = str(sub.get("type", ""))
is_door = bool(sub.get("isDoor"))
if "Screen" in t:
return None # 纱扇不参与文字描述
if "Kfc" in t:
return "tilt-and-turn " + ("door leaf" if is_door else "sash")
if "DoubleSash" in t:
return "pair of French " + ("door leaves" if is_door else "casement sashes opening from the center")
if "Slide" in t:
return "sliding " + ("door panel" if is_door else "sash")
if t in ("Sash",):
return "side-hung casement " + ("door leaf" if is_door else "sash")
return None
def _opening_words(sub: dict) -> str:
toward = str(sub.get("openToward") or "")
od = sub.get("od")
if "up" in str(sub.get("openDirection") or "").lower() or (isinstance(od, (int, float)) and 60 <= od <= 180 and "up" in str(sub.get("openDirection", "")).lower()):
return "top-hung"
return "inward-opening" if toward == "inward" else "outward-opening"
def describe_window(drawing: str | dict) -> dict:
"""从 drawing_data 提取窗型结构特征(供提示词与前端展示)。"""
dd = parse_drawing(drawing)
shapes = [s for s in dd.get("sm", []) if isinstance(s, dict)]
if not shapes:
return {"count": 0, "summary": "", "width_mm": 0, "height_mm": 0}
xs, ys = [], []
parts: list[str] = []
sash_total = 0
has_arc = has_screen = has_deco = False
for shape in shapes:
regions = replay_shape(shape)
pts = [[(seg["ps"]["x"], seg["ps"]["y"]) for seg in r] for r in regions]
if not pts:
continue
flat = [p for r in pts for p in r]
x0, y0 = min(p[0] for p in flat), min(p[1] for p in flat)
x1, y1 = max(p[0] for p in flat), max(p[1] for p in flat)
xs += [x0, x1]; ys += [y0, y1]
w, h = round(x1 - x0), round(y1 - y0)
n_region = len(regions)
# 扇描述
sashes = [sub for sub in (shape.get("sm") or []) if isinstance(sub, dict)]
kind_words: list[str] = []
for sub in sashes:
k = _sash_kind(sub)
if k is None:
if "Screen" in str(sub.get("type", "")):
has_screen = True
continue
sash_total += 1
kind_words.append(f"{_opening_words(sub)} {k}")
if shape.get("rt"):
has_arc = True
if shape.get("decs"):
has_deco = True
part = f"{w}×{h}mm unit with {n_region} glass panes"
if kind_words:
part += ", " + " and ".join(dict.fromkeys(kind_words))
parts.append(part)
if not xs:
return {"count": 0, "summary": "", "width_mm": 0, "height_mm": 0}
width_mm, height_mm = round(max(xs) - min(xs)), round(max(ys) - min(ys))
multi = len(parts) > 1
summary = "; ".join(parts)
if multi:
summary = f"a combination of {len(parts)} window units joined together: " + summary
else:
summary = parts[0]
if has_arc:
summary += ", arched/curved top (heritage style)"
if has_screen:
summary += ", with retractable fly screen"
if has_deco:
summary += ", with decorative gliding bars (grilles) between the glass"
return {"count": len(parts), "summary": summary, "width_mm": width_mm,
"height_mm": height_mm, "sashes": sash_total}
def build_prompt(drawing: str | dict, style: str = "modern", time: str = "day",
scene: str = "render", ratio: str = "landscape") -> str:
"""构建优化后的完整英文提示词。
ratio: portrait/landscape/square → 依据窗型宽高比的构图引导。
"""
info = describe_window(drawing)
finish, style_word = STYLE_FINISH.get(style, STYLE_FINISH["modern"])
light = TIME_LIGHT.get(time, TIME_LIGHT["day"])
scene_tpl = SCENE_DESC.get(scene) or QUICK_SCENE.get(scene) or SCENE_DESC["render"]
scene_text = scene_tpl.replace("{style_word}", style_word)
window_desc = info["summary"] or "a modern aluminum window"
composition = {
"portrait": "vertical composition, window fills most of the frame height",
"landscape": "horizontal composition, generous wall context around the window",
"square": "balanced square composition, window centered",
}.get(ratio, "")
prompt = (
f"Photorealistic architectural photograph of {scene_text}. "
f"The window: {window_desc}; {finish}. "
f"Lighting: {light}. {composition} "
"Keep the window geometry, pane division and frame profiles exactly as described — do not add or remove sashes. "
"Realistic glass with soft sky reflection, physically correct shadows and contact shadows, "
"true-to-material aluminum texture, high dynamic range, ultra-detailed, 8k, professional architectural photography, no people, no text, no watermark."
)
return prompt
def pick_size(width_mm: int, height_mm: int) -> str:
"""按窗型宽高比选择生成尺寸(gpt-image 支持 1024x1024 / 1536x1024 / 1024x1536)。"""
if not width_mm or not height_mm:
return "1024x1024"
r = width_mm / height_mm
if r > 1.2:
return "1536x1024"
if r < 0.83:
return "1024x1536"
return "1024x1024"