第三阶段改进

This commit is contained in:
Ukawka
2026-09-22 22:18:34 +08:00
parent 8080d52a99
commit 00f476022d
12 changed files with 873 additions and 74 deletions
+21 -5
View File
@@ -145,6 +145,9 @@ def merge_candidate(folder, candidate_file, output_file, size_policy='strict'):
raise ValueError('Unknown candidate size policy')
folder = Path(folder)
task = read_json(folder/'task.json')
if task.get('profile') == 'adaptive':
from completion_planning import merge_adaptive
return merge_adaptive(folder, candidate_file, output_file, size_policy)
original = np.array(Image.open(folder/'visible.png').convert('RGBA'))
allowed = np.array(Image.open(folder/'allowed-mask.png').convert('L')) > 0
candidate = Image.open(candidate_file).convert('RGBA')
@@ -228,6 +231,9 @@ def finalize(directory):
meta = read_json(out/'tasks'/name/'task.json')
ox, oy = meta['origin_xy']
task.update(image=f'parts/{name}.png', crop_xyxy=[bbox[0]+ox, bbox[1]+oy, bbox[2]+ox, bbox[3]+oy])
if meta.get('preservation'):
task['preservation'] = meta['preservation']
task['transform'] = meta['transform']
view = f'<img src="{task["image"]}">' if task.get('image') else ''
for index, item in enumerate(task.get('candidates', []), 1):
view += f'<p><a href="{html.escape(item["file"], quote=True)}">原候选 {index}</a> · <a href="tasks/{name}/qa-{index}.json">QA</a>'
@@ -235,11 +241,14 @@ def finalize(directory):
view += f' · <a href="tasks/{name}/merged-{index}-normalized.png">缩放后候选</a>'
view += '</p>'
warnings = html.escape(', '.join(task.get('upstream_warnings', [])))
rows.append(f'<tr><td>{html.escape(name)}<p>{warnings}</p><a href="tasks/{name}/task.json">任务参数</a> · <a href="tasks/{name}/prompt.txt">提示词</a></td><td>{html.escape(task["status"])}</td><td><a href="tasks/{name}/visible.png"><img src="tasks/{name}/visible.png"></a></td><td><a href="tasks/{name}/allowed-mask.png"><img src="tasks/{name}/allowed-mask.png"></a><p><a href="tasks/{name}/context.png">原图局部参考</a></p></td><td>{view}</td><td><pre>{html.escape(json.dumps(qa, ensure_ascii=False, indent=2))}</pre></td></tr>')
reference = f'<p><a href="tasks/{name}/input.png">实际请求主图</a> · <a href="tasks/{name}/request-plan.json">发送计划</a></p>'
if (out/'tasks'/name/'reference.png').exists():
reference += f'<a href="tasks/{name}/reference.png">完整角色参考</a>'
rows.append(f'<tr><td>{html.escape(name)}<p>{warnings}</p><a href="tasks/{name}/task.json">任务参数</a> · <a href="tasks/{name}/prompt.txt">提示词</a>{reference}</td><td>{html.escape(task["status"])}</td><td><a href="tasks/{name}/visible.png"><img src="tasks/{name}/visible.png"></a></td><td><a href="tasks/{name}/allowed-mask.png"><img src="tasks/{name}/allowed-mask.png"></a><p><a href="tasks/{name}/context.png">原图局部参考</a></p></td><td>{view}</td><td><pre>{html.escape(json.dumps(qa, ensure_ascii=False, indent=2))}</pre></td></tr>')
write_json(out/'completion-result.json', result)
(out/'report.html').write_text('''<!doctype html><html lang="zh-CN"><meta charset="utf-8"><title>部件补全</title>
<style>body{font:16px system-ui;margin:24px}td,th{border:1px solid #ccc;padding:8px}img{max-width:260px;max-height:300px;background:#eee}pre{white-space:pre-wrap}table{border-collapse:collapse}</style>
<h1>第三阶段:部件补全</h1><p>新增区域受允许掩码限制,原图可见像素逐像素恢复。几何通过仅表示候选可供复核,不代表遮挡和关节纹理已正确补全。上游 SAM 错分仍需修正。</p>
<h1>第三阶段:部件补全</h1><p>输出受允许掩码限制,保留区域逐像素恢复;adaptive 允许明确规划的附件清理、替换和移除,原始 SAM 始终保留。几何通过仅表示候选可供复核,不代表遮挡和关节纹理已正确补全。</p>
<table><tr><th>部件 / 上游告警</th><th>状态</th><th>原可见区域</th><th>允许新增区域(白色)</th><th>补全候选</th><th>几何检查</th></tr>'''+''.join(rows)+'</table><h2>跳过</h2><pre>'+html.escape(json.dumps(result['skipped'], ensure_ascii=False, indent=2))+'</pre></html>', encoding='utf-8')
@@ -249,16 +258,23 @@ def main():
parser.add_argument('--out', required=True)
parser.add_argument('--part', action='append', default=[])
parser.add_argument('--size', type=int, default=1024)
parser.add_argument('--radius', type=int, default=32)
parser.add_argument('--radius', type=int)
parser.add_argument('--profile', choices=['legacy', 'adaptive'], default='legacy')
parser.add_argument('--reference-mode', choices=['none', 'local', 'full', 'both', 'board'], default='board')
parser.add_argument('--review-file')
parser.add_argument('--candidate')
parser.add_argument('--merged')
parser.add_argument('--qa')
parser.add_argument('--candidate-size-policy', choices=['strict', 'uniform'], default='strict')
args = parser.parse_args()
if args.action == 'prepare':
if not 128 <= args.size <= 4096 or not 1 <= args.radius <= 256:
if not 128 <= args.size <= 4096 or (args.radius is not None and not 1 <= args.radius <= 256):
parser.error('size must be 128..4096 and radius 1..256')
prepare(args.out, args.part, args.size, args.radius)
if args.profile == 'adaptive':
from completion_planning import prepare_adaptive
prepare_adaptive(args.out, args.part, args.size, args.radius, args.reference_mode, args.review_file)
else:
prepare(args.out, args.part, args.size, args.radius or 32)
elif args.action == 'merge':
write_json(args.qa, merge_candidate(args.out, args.candidate, args.merged, args.candidate_size_policy))
else: