fix: enforce orthographic top bottom subject views

This commit is contained in:
2026-05-25 09:27:31 +08:00
parent 8e60c7dff9
commit f8c51b5ef6
5 changed files with 47 additions and 7 deletions

View File

@@ -2641,8 +2641,8 @@ SUBJECT_VIEW_LABELS: dict[str, str] = {
"three_quarter_right": "右前 45°", "three_quarter_right": "右前 45°",
"side": "侧面", "side": "侧面",
"side_walk": "侧面走路", "side_walk": "侧面走路",
"top": "顶部视角", "top": "正投影俯视图",
"bottom": "底部视角", "bottom": "正投影仰视图",
"expression_neutral": "中性表情", "expression_neutral": "中性表情",
"expression_smile": "微笑表情", "expression_smile": "微笑表情",
"expression_happy": "开心表情", "expression_happy": "开心表情",
@@ -2691,11 +2691,27 @@ def _subject_view_labels(kind: SubjectKind, requested: list[str] | None = None)
("back", "背面"), ("back", "背面"),
("left", "左侧"), ("left", "左侧"),
("right", "右侧"), ("right", "右侧"),
("top", "顶部"), ("top", "正投影俯视图"),
("bottom", "底部"), ("bottom", "正投影仰视图"),
] ]
def _subject_view_projection_clause(view: str) -> str:
if view == "top":
return (
"Patent-style orthographic top view: look straight down from directly above the product, "
"with the viewing direction perpendicular to the top face. No perspective, no tilt, no 3/4 angle, "
"no oblique overhead camera, no visible front/side depth unless it is true product thickness in orthographic projection. "
)
if view == "bottom":
return (
"Patent-style orthographic bottom view: look straight up at the underside/bottom face, "
"with the viewing direction perpendicular to the bottom face. No perspective, no tilt, no 3/4 angle, "
"no low-angle perspective camera, no visible front/side depth unless it is true product thickness in orthographic projection. "
)
return ""
def _attach_temporal_metrics(items: list[dict]) -> None: def _attach_temporal_metrics(items: list[dict]) -> None:
"""相邻低清帧差异:转场 / 动作目标依赖它,不需要逐帧高分辨率扫描。""" """相邻低清帧差异:转场 / 动作目标依赖它,不需要逐帧高分辨率扫描。"""
for i, it in enumerate(items): for i, it in enumerate(items):
@@ -6910,11 +6926,13 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
else: else:
view_prompt = f"complete object/product reference, {view_label} view" view_prompt = f"complete object/product reference, {view_label} view"
view_name = view.replace("_", " ") view_name = view.replace("_", " ")
projection_clause = _subject_view_projection_clause(view)
single_view_clause = ( single_view_clause = (
f"Single-image output rule: this output file is ONLY for the {view_label} view ({view_name}). " f"Single-image output rule: this output file is ONLY for the {view_label} view ({view_name}). "
"Render exactly one subject, one time, in one pose and one camera angle. " "Render exactly one subject, one time, in one pose and one camera angle. "
"Do not create a multi-view sheet, contact sheet, grid, storyboard, lineup, comparison layout, before/after layout, mirrored pair, duplicate subjects, thumbnails, labels, captions, arrows, view names, panel borders, or multiple versions in the same image. " "Do not create a multi-view sheet, contact sheet, grid, storyboard, lineup, comparison layout, before/after layout, mirrored pair, duplicate subjects, thumbnails, labels, captions, arrows, view names, panel borders, or multiple versions in the same image. "
"Do not include any other views in this image. " "Do not include any other views in this image. "
+ projection_clause
) )
framing_clause = ( framing_clause = (
"For this close-up view, intentionally crop as an upper-body asset from head/neck to chest or upper back; the neck, shoulders, collarbone or upper spine area must be large, clear, and useful for placing a neck-and-shoulder massage device. " "For this close-up view, intentionally crop as an upper-body asset from head/neck to chest or upper back; the neck, shoulders, collarbone or upper spine area must be large, clear, and useful for placing a neck-and-shoulder massage device. "

View File

@@ -1181,6 +1181,19 @@ ProductRefStateItem {
<h2>变更记录</h2> <h2>变更记录</h2>
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p> <p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
<div class="changelog"> <div class="changelog">
<article class="change">
<header>
<h3>2026-05-25 · 主体套图俯视和仰视改为正投影口径</h3>
<span class="tag amber">API</span>
<span class="tag violet">UI</span>
<span class="tag blue">Docs</span>
</header>
<div class="body">
<p><strong>问题:</strong>原先主体/产品套图里把 <code>top</code><code>bottom</code> 简写为“顶部/底部”或“俯视/仰视”,模型容易理解成斜上方、斜下方或 3/4 透视镜头,不符合外观专利六面正投影视图的要求。</p>
<p><strong>改动:</strong><code>api/main.py</code><code>SUBJECT_VIEW_LABELS</code>、默认物体 6 视图和主体生图 prompt 已改为“正投影俯视图 / 正投影仰视图”,并在生成单图时加入 <code>orthographic top/bottom view</code><code>no perspective</code><code>no tilt</code><code>no 3/4 angle</code> 约束。<code>web/components/lightbox.tsx</code><code>web/components/ad-recreation-board.tsx</code><code>web/components/storyboard-editor.tsx</code> 同步更新显示文案和后续分镜提示词。</p>
<p><strong>影响:</strong>以后描述专利六面图需求时应说“正投影俯视图 / 正投影仰视图”,不要只说“俯视角度 / 仰视角度”;这些视图是垂直投影,不是带角度的摄影机视角。</p>
</div>
</article>
<article class="change"> <article class="change">
<header> <header>
<h3>2026-05-24 · 默认首页完整重设计为多人创作平台</h3> <h3>2026-05-24 · 默认首页完整重设计为多人创作平台</h3>

View File

@@ -2019,6 +2019,11 @@ function selectProductItemsForRow(
} }
function subjectViewLabel(view: string) { function subjectViewLabel(view: string) {
const labels: Record<string, string> = {
top: "正投影俯视图",
bottom: "正投影仰视图",
}
if (labels[view]) return labels[view]
return SUBJECT_ASSET_VIEWS.find((item) => item.value === view)?.label ?? view return SUBJECT_ASSET_VIEWS.find((item) => item.value === view)?.label ?? view
} }
@@ -2034,6 +2039,8 @@ function subjectViewRoleHint(view: string) {
bust_left_45: "肩颈左前近景、手部调整、佩戴贴合", bust_left_45: "肩颈左前近景、手部调整、佩戴贴合",
bust_right_45: "肩颈右前近景、手部调整、佩戴贴合", bust_right_45: "肩颈右前近景、手部调整、佩戴贴合",
back_neck_detail: "后颈肩背特写、触点位置、产品贴合", back_neck_detail: "后颈肩背特写、触点位置、产品贴合",
top: "正投影俯视、无透视、无倾斜、产品顶部轮廓",
bottom: "正投影仰视、无透视、无倾斜、产品底部结构",
} }
return hints[view] ?? "主体参考视角" return hints[view] ?? "主体参考视角"
} }
@@ -2050,6 +2057,8 @@ function subjectViewPromptHint(view: string) {
bust_left_45: "left three-quarter neck-and-shoulder close-up, hand adjustment, wearable fit", bust_left_45: "left three-quarter neck-and-shoulder close-up, hand adjustment, wearable fit",
bust_right_45: "right three-quarter neck-and-shoulder close-up, hand adjustment, wearable fit", bust_right_45: "right three-quarter neck-and-shoulder close-up, hand adjustment, wearable fit",
back_neck_detail: "back-neck and upper-back detail, contact-pad position, product fit", back_neck_detail: "back-neck and upper-back detail, contact-pad position, product fit",
top: "orthographic top view, straight down from above, no perspective, no tilt, no three-quarter angle",
bottom: "orthographic bottom view, straight up at underside, no perspective, no tilt, no three-quarter angle",
} }
return hints[view] ?? "subject reference view" return hints[view] ?? "subject reference view"
} }

View File

@@ -36,8 +36,8 @@ const OBJECT_VIEW_OPTIONS = [
["back", "背面"], ["back", "背面"],
["left", "左侧"], ["left", "左侧"],
["right", "右侧"], ["right", "右侧"],
["top", "顶部"], ["top", "正投影俯视图"],
["bottom", "底部"], ["bottom", "正投影仰视图"],
] ]
const LIVING_VIEW_OPTIONS = [ const LIVING_VIEW_OPTIONS = [

View File

@@ -129,7 +129,7 @@ export function StoryboardEditor({ job, frameIndex, onClose }: Props) {
📐 📐
</div> </div>
<div className="text-[9.5px] text-white/45 leading-tight"> <div className="text-[9.5px] text-white/45 leading-tight">
/ / / /
</div> </div>
</button> </button>
<button <button