feat(scripts): 按聚类结果导出数据集

新增 scripts/export_clusters.py:读聚类产出的 assignments.csv,把原图复制成
data/<源目录名>_<骨架标签>_cluster_k<簇数>/,子目录用从 0 开始的数字编号。

- 复制真实文件(copy2 保留修改时间),不建软链、不改名
- 目录名自动推导:源目录名取各路径的公共父目录,骨架标签取自 summary.json 的
  feature_source;--tag / --out 可覆盖
- manifest.json 记录来源 run、特征配置与每张图的原始路径,便于追溯
- 保护:源文件缺失、同簇内重名(保持原名会互相覆盖)直接报错;
  目标目录非空需显式 --overwrite;--dry-run 只打印计划
- 聚类产出的 summary.json 新增 feature_source 字段(method / backbone /
  feature_stages),供上面的自动命名与日后追溯

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Shaun Zhou
2026-09-06 13:17:53 +08:00
co-authored by Claude Opus 5
parent 7f13ec5d18
commit f8a52c9382
4 changed files with 420 additions and 1 deletions
+17 -1
View File
@@ -122,10 +122,26 @@ def write_outputs(cfg, work_dir, metas, result, num_clusters, stability):
width, height = meta["size"]
writer.writerow([meta["path"], label, f"{distance:.6f}", meta["source_dir"], width, height])
method = str(cfg.get("method", "pretrained"))
backbone_cfg = (
dict(cfg.get("simclr", {}).get("backbone", {}))
if method == "simclr"
else dict(cfg.get("backbone", {}))
)
summary = {
"num_clusters": num_clusters,
"num_images": len(metas),
"method": cfg.get("method", "pretrained"),
"method": method,
# 记录特征来源,供 export_clusters.py 自动命名以及日后追溯
"feature_source": {
"method": method,
"backbone": backbone_cfg,
"feature_stages": (
cfg.get("simclr", {}).get("feature_stages")
if method == "simclr"
else cfg.get("feature_stages")
),
},
"image_size": cfg.get("image_size"),
"cluster_sizes": result.sizes,
"silhouette": result.silhouette,