# -*- coding: utf-8 -*- """ 从 seed_assets/medias 生成迷你 YOLO 分割演示数据集(占位标注,仅用于试跑训练流程)。 输出: training_templates/bridge_hazard_demo/ training_templates/bridge_hazard_demo.zip """ from __future__ import annotations import shutil import zipfile from pathlib import Path from PIL import Image ROOT = Path(__file__).resolve().parents[1] ASSETS = ROOT / 'seed_assets' / 'medias' OUT_ROOT = ROOT / 'training_templates' / 'bridge_hazard_demo' ZIP_PATH = ROOT / 'training_templates' / 'bridge_hazard_demo.zip' # (源图, 划分, 类别 id, 类别说明) SAMPLES = [ ('01_concrete_crack_bridge.jpg', 'train', 0, 'concrete_crack'), ('02_bridge_concrete_cracks.jpg', 'train', 0, 'concrete_crack'), ('04_concrete_bending_cracks.jpg', 'train', 0, 'concrete_crack'), ('06_shrinkage_cracks_concrete.jpg', 'train', 0, 'concrete_crack'), ('03_steel_bridge_corrosion.jpg', 'val', 1, 'steel_corrosion'), ('08_concrete_rebar_corrosion.jpg', 'val', 1, 'steel_corrosion'), ] def rect_polygon(class_id: int, cx=0.5, cy=0.5, w=0.35, h=0.35) -> str: """生成归一化矩形四顶点分割标注行。""" x1, y1 = cx - w / 2, cy - h / 2 x2, y2 = cx + w / 2, cy - h / 2 x3, y3 = cx + w / 2, cy + h / 2 x4, y4 = cx - w / 2, cy + h / 2 pts = [x1, y1, x2, y2, x3, y3, x4, y4] pts = [max(0.0, min(1.0, p)) for p in pts] return f"{class_id} " + " ".join(f"{p:.6f}" for p in pts) def write_data_yaml(root: Path): content = """# 检澜演示数据集 — 占位标注,仅用于试跑 YOLOv8 分割训练 path: . train: train/images val: val/images nc: 2 names: 0: concrete_crack 1: steel_corrosion """ (root / 'data.yaml').write_text(content, encoding='utf-8') def main(): if not ASSETS.is_dir(): print(f'缺少 {ASSETS},请先运行 scripts/download_real_medias.py') return 1 if OUT_ROOT.exists(): shutil.rmtree(OUT_ROOT) OUT_ROOT.mkdir(parents=True) for split in ('train', 'val'): (OUT_ROOT / split / 'images').mkdir(parents=True) (OUT_ROOT / split / 'labels').mkdir(parents=True) idx = 0 for src_name, split, class_id, _ in SAMPLES: src = ASSETS / src_name if not src.is_file(): print(f'[skip] 缺少 {src_name}') continue idx += 1 stem = f'demo_{idx:03d}' img_name = f'{stem}.jpg' dest_img = OUT_ROOT / split / 'images' / img_name with Image.open(src) as im: im.convert('RGB').save(dest_img, format='JPEG', quality=90) label_line = rect_polygon(class_id) (OUT_ROOT / split / 'labels' / f'{stem}.txt').write_text( label_line + '\n', encoding='utf-8' ) print(f'[ok] {split} {img_name} class={class_id}') if idx < 2: print('有效图片不足,无法生成数据集') return 1 write_data_yaml(OUT_ROOT) if ZIP_PATH.is_file(): ZIP_PATH.unlink() with zipfile.ZipFile(ZIP_PATH, 'w', zipfile.ZIP_DEFLATED) as zf: for file in OUT_ROOT.rglob('*'): if file.is_file(): arc = file.relative_to(OUT_ROOT.parent) zf.write(file, arc.as_posix()) print() print('已生成:') print(f' 目录 {OUT_ROOT}') print(f' ZIP {ZIP_PATH}') print() print('下一步:登录 developer → 模型训练 → 上传 bridge_hazard_demo.zip') print('建议试跑:yolov8n-seg,epochs=5,batch=2') return 0 if __name__ == '__main__': raise SystemExit(main())