feat(detect): YOLO 数据集统一为 PNG、按类别重命名并附 classes.txt

- 图片统一成 PNG。pearlescent / blue / uv 的原始数据是 BMP,generate_boxes.py
  按原后缀链接,于是 runs/boxes_shap 里 3685 张 BMP 与 7526 张 PNG 混在一起,
  ripple 与 uneven 两类全是 BMP。按文件头(而非后缀)判断:真 PNG 照旧硬链接,
  其余无损转换,不缩放像素,归一化框坐标原样有效;CMYK 等 PNG 存不下的模式转 RGB。
  在真实数据上核对过 3685 张转换结果逐像素一致。
- 文件按 <类别>_<序号> 重新编号。类别取标签第一列,文件名不会与标签矛盾;
  序号在类内按源路径排序、与划分无关,换种子重跑名字不变。新旧名对照写入
  name_map.csv,源文件名里的正反面、位置、时间戳不丢。--keep-names 保留原名。
- 输出 classes.txt(行号即 class_id),供 LabelImg / X-AnyLabeling 复核框时使用。
- 撞名检查改为按输出文件名的小写比较:统一后缀后 a.png 与 a.bmp 会撞,
  macOS 默认文件系统也不区分大小写。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Shaun Zhou
2026-09-15 16:45:58 +08:00
co-authored by Claude Opus 5
parent cc053a6e9c
commit 7adc5b6332
4 changed files with 346 additions and 29 deletions
+15
View File
@@ -18,6 +18,21 @@ from pathlib import Path
IMAGE_SUFFIXES = (".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".webp")
def write_classes_txt(path: str | Path, classes: Sequence[str]) -> Path:
"""写 classes.txt:每行一个类别名,行号(从 0 起)即标签里的 class_id。
ultralytics 训练只读 data.yaml;这份是给 LabelImg / X-AnyLabeling 之类
标注工具用的——人工复核、修改框时它们靠它把 class_id 映射回类别名。
"""
bad = [name for name in classes if not name.strip() or "\n" in name]
if bad:
raise ValueError(f"类别名不能为空或包含换行,否则行号与 class_id 对不上: {bad!r}")
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("".join(f"{name}\n" for name in classes), encoding="utf-8")
return path
def write_data_yaml(
path: str | Path,
classes: Sequence[str],