feat: AI炫图接入gpt-image-2.5云端生图(结构化提示词工程+照片参考合成)
This commit is contained in:
@@ -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"
|
||||
Reference in New Issue
Block a user