改为 6 张 sheet,每张单形状,取消全部 FILL/留白,AI 填满画布后多画少取 新增洋红去背步骤,对接 platform-image alpha 管线 新增 find_non_transparent_bounds 四方向内容边界扫描 新增 fill_transparent_with_opaque_average 透明像素填充 自适应网格检测 (detect_cell_grid_seed) 用于组间边界对齐 重写 slice_puzzle_clear_sheet 为两阶段:group bbox → 等分 cell 提示词优化:主前缀改为裁片级描述,每 sheet 增加精确占格约束 修复 jump_hop 测试断言 (1×1×1 → 1×1×1 的立方体) 新增分析脚本 tools/analyze_puzzle_clear_output.py 和 tools/test_ve_api.py Sheet-06 为纵向 1×3 缓冲区
205 lines
8.0 KiB
Python
205 lines
8.0 KiB
Python
"""
|
||
分析拼消消测试输出,检测卡片透明区域并溯源问题成因。
|
||
|
||
用法:
|
||
python tools/analyze_puzzle_clear_output.py
|
||
python tools/analyze_puzzle_clear_output.py --dir path/to/output
|
||
python tools/analyze_puzzle_clear_output.py --detail # 详细逐卡输出
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import argparse
|
||
from collections import defaultdict
|
||
from PIL import Image
|
||
|
||
|
||
def scan_transparent_pixels(img_path):
|
||
"""扫描图片,返回 (总像素数, 透明像素数, 边缘透明列比例)"""
|
||
img = Image.open(img_path).convert("RGBA")
|
||
w, h = img.size
|
||
pixels = img.load()
|
||
total = w * h
|
||
transparent = 0
|
||
edge_cols_with_transparent = 0
|
||
edge_rows_with_transparent = 0
|
||
|
||
# 统计透明像素
|
||
for y in range(h):
|
||
for x in range(w):
|
||
if pixels[x, y][3] < 128:
|
||
transparent += 1
|
||
|
||
# 检测四边是否有透明像素(边缘列/行透明占比 > 10%)
|
||
for x in range(w):
|
||
col_transparent = sum(1 for y in range(h) if pixels[x, y][3] < 128)
|
||
if col_transparent > h * 0.1:
|
||
edge_cols_with_transparent += 1
|
||
|
||
for y in range(h):
|
||
row_transparent = sum(1 for x in range(w) if pixels[x, y][3] < 128)
|
||
if row_transparent > w * 0.1:
|
||
edge_rows_with_transparent += 1
|
||
|
||
ratio = transparent / total * 100 if total > 0 else 0
|
||
has_edge = edge_cols_with_transparent > 0 or edge_rows_with_transparent > 0
|
||
return total, transparent, ratio, has_edge, edge_cols_with_transparent, edge_rows_with_transparent
|
||
|
||
|
||
def analyze_sheet_cleaned(sheet_path):
|
||
"""分析去背后的 sheet 图,检查各 group 区域的透明情况"""
|
||
img = Image.open(sheet_path).convert("RGBA")
|
||
w, h = img.size
|
||
pixels = img.load()
|
||
|
||
# 统计整体透明像素
|
||
total = w * h
|
||
transparent = sum(1 for y in range(h) for x in range(w) if pixels[x, y][3] < 128)
|
||
return total, transparent, transparent / total * 100 if total > 0 else 0
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="分析拼消消测试输出")
|
||
parser.add_argument("--dir", default="", help="输出目录路径")
|
||
parser.add_argument("--detail", action="store_true", help="详细逐卡输出")
|
||
args = parser.parse_args()
|
||
|
||
# 自动查找输出目录
|
||
if args.dir:
|
||
base = args.dir
|
||
else:
|
||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||
repo_root = os.path.dirname(script_dir)
|
||
candidates = [
|
||
os.path.join(repo_root, "server-rs", "crates", "api-server", "target", "test-output", "puzzle-clear-real"),
|
||
os.path.join(repo_root, "server-rs", "target", "test-output", "puzzle-clear-real"),
|
||
]
|
||
base = None
|
||
for c in candidates:
|
||
if os.path.isdir(c):
|
||
base = c
|
||
break
|
||
if not base:
|
||
print("未找到测试输出目录。请用 --dir 指定路径。")
|
||
sys.exit(1)
|
||
|
||
sheets_dir = os.path.join(base, "sheets")
|
||
cards_dir = os.path.join(base, "cards")
|
||
|
||
if not os.path.isdir(cards_dir):
|
||
print(f"cards 目录不存在: {cards_dir}")
|
||
sys.exit(1)
|
||
|
||
# ==================== 阶段 1: 分析卡片 ====================
|
||
print("=" * 70)
|
||
print("阶段 1: 卡片透明像素分析")
|
||
print("=" * 70)
|
||
|
||
card_results = [] # (sheet, card_name, total, transparent, ratio, has_edge, edge_cols, edge_rows)
|
||
problem_cards = []
|
||
|
||
for sheet_name in sorted(os.listdir(cards_dir)):
|
||
sheet_dir = os.path.join(cards_dir, sheet_name)
|
||
if not os.path.isdir(sheet_dir):
|
||
continue
|
||
for card_name in sorted(os.listdir(sheet_dir)):
|
||
if not card_name.endswith(".png"):
|
||
continue
|
||
card_path = os.path.join(sheet_dir, card_name)
|
||
total, trans, ratio, has_edge, ec, er = scan_transparent_pixels(card_path)
|
||
card_results.append((sheet_name, card_name, total, trans, ratio, has_edge, ec, er))
|
||
if ratio > 5 or has_edge:
|
||
problem_cards.append((sheet_name, card_name, total, trans, ratio, has_edge, ec, er))
|
||
|
||
# 按 sheet 汇总
|
||
by_sheet = defaultdict(list)
|
||
for r in card_results:
|
||
by_sheet[r[0]].append(r)
|
||
|
||
print(f"\n总卡片数: {len(card_results)}")
|
||
print(f"问题卡片数 (透明>5% 或 有边缘透明): {len(problem_cards)}")
|
||
print()
|
||
|
||
for sheet_name in sorted(by_sheet.keys()):
|
||
cards = by_sheet[sheet_name]
|
||
problem_count = sum(1 for r in cards if r[4] > 5 or r[5])
|
||
print(f" {sheet_name}: {len(cards)} cards, {problem_count} problems")
|
||
|
||
if problem_cards:
|
||
print(f"\n--- 问题卡片详情 ---")
|
||
problem_cards.sort(key=lambda r: -r[4]) # sort by ratio desc
|
||
for sheet, name, total, trans, ratio, has_edge, ec, er in problem_cards:
|
||
group_id = name.split("-part-")[0]
|
||
edge_info = f", 边缘透明列={ec} 行={er}" if has_edge else ""
|
||
print(f" {sheet}/{name} group={group_id} transparent={ratio:.1f}% ({trans}/{total}){edge_info}")
|
||
|
||
# ==================== 阶段 2: 溯源分析 ====================
|
||
print()
|
||
print("=" * 70)
|
||
print("阶段 2: 溯源 — 对比原始 sheet 与去背后 sheet")
|
||
print("=" * 70)
|
||
|
||
if os.path.isdir(sheets_dir):
|
||
for fname in sorted(os.listdir(sheets_dir)):
|
||
if not fname.endswith(".png"):
|
||
continue
|
||
sheet_path = os.path.join(sheets_dir, fname)
|
||
total, trans, ratio = analyze_sheet_cleaned(sheet_path)
|
||
is_cleaned = "-cleaned" in fname
|
||
label = "去背后" if is_cleaned else "原始"
|
||
print(f" {fname} ({label}): {trans}/{total} 透明像素 ({ratio:.1f}%)")
|
||
|
||
# ==================== 阶段 3: 问题溯源推理 ====================
|
||
print()
|
||
print("=" * 70)
|
||
print("阶段 3: 问题成因分析")
|
||
print("=" * 70)
|
||
|
||
if not problem_cards:
|
||
print(" 无问题卡片,管线正常。")
|
||
return
|
||
|
||
# 分析问题卡片的 group 分布
|
||
problem_groups = defaultdict(list)
|
||
for r in problem_cards:
|
||
group_id = r[1].split("-part-")[0]
|
||
problem_groups[group_id].append(r)
|
||
|
||
print(f"\n 涉及 {len(problem_groups)} 个 group:")
|
||
for group_id in sorted(problem_groups.keys()):
|
||
cards = problem_groups[group_id]
|
||
avg_ratio = sum(r[4] for r in cards) / len(cards)
|
||
edge_count = sum(1 for r in cards if r[5])
|
||
print(f" {group_id}: {len(cards)} cells, avg透明={avg_ratio:.1f}%, {edge_count} cells有边缘透明")
|
||
|
||
# 检查原始 sheet 和去背后 sheet 的差异
|
||
if os.path.isdir(sheets_dir):
|
||
cleaned_files = [f for f in os.listdir(sheets_dir) if "cleaned" in f]
|
||
raw_files = [f for f in os.listdir(sheets_dir) if "cleaned" not in f and f.endswith(".png")]
|
||
if cleaned_files and raw_files:
|
||
for raw_f in sorted(raw_files):
|
||
raw_p = os.path.join(sheets_dir, raw_f)
|
||
cleaned_f = raw_f.replace(".png", "-cleaned.png")
|
||
cleaned_p = os.path.join(sheets_dir, cleaned_f)
|
||
if not os.path.exists(cleaned_p):
|
||
continue
|
||
_, raw_trans, raw_ratio = analyze_sheet_cleaned(raw_p)
|
||
_, cleaned_trans, cleaned_ratio = analyze_sheet_cleaned(cleaned_p)
|
||
print(f"\n {raw_f}:")
|
||
print(f" 原始透明: {raw_ratio:.1f}%")
|
||
print(f" 去后透明: {cleaned_ratio:.1f}%")
|
||
delta = cleaned_ratio - raw_ratio
|
||
if delta > 1:
|
||
print(f" ** 去背增加了 {delta:.1f}% 透明像素 — 可能误删了主体内容")
|
||
|
||
print()
|
||
print(" 可能原因:")
|
||
print(" 1. AI 未将内容画满整个 group 区域(内容在组内偏移)")
|
||
print(" 2. 洋红去背误将主题内近似洋红的像素也变透明")
|
||
print(" 3. find_non_transparent_bounds 扫描范围包括了相邻 group 的透明间隙")
|
||
print(" 4. group resize 后未完全覆盖目标尺寸(内容比例与目标比例不匹配)")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|