| |
| """ |
| 预训练数据准备脚本 |
| 生成三个任务的训练数据: |
| 1. 环境描述(天气、道路、光照) |
| 2. 单帧事故判断 |
| 3. 序列事故预测和描述 |
| """ |
|
|
| import json |
| import os |
| import pickle |
| import random |
| from pathlib import Path |
| from typing import Dict, List, Tuple |
|
|
| random.seed(42) |
|
|
| |
| PRETRAIN_ROOT = Path("PROJECT_ROOT/data/dataset/pretrain") |
| OUTPUT_DIR = PRETRAIN_ROOT / "train" |
| OUTPUT_DIR.mkdir(exist_ok=True) |
|
|
| NEXAR_ROOT = PRETRAIN_ROOT / "nexar" |
| DADA_ROOT = PRETRAIN_ROOT / "DADA-2000" |
|
|
| TRAIN_RATIO = 0.7 |
| VAL_RATIO = 0.15 |
| TEST_RATIO = 0.15 |
|
|
|
|
| |
| def load_all_annotations(): |
| """加载所有annotation.json""" |
| all_data = [] |
| |
| |
| for split in ["positive", "negative"]: |
| split_dir = NEXAR_ROOT / split |
| if not split_dir.exists(): |
| continue |
| for case_dir in sorted(split_dir.iterdir()): |
| if not case_dir.is_dir(): |
| continue |
| anno_file = case_dir / "annotation.json" |
| if not anno_file.exists(): |
| continue |
| |
| with open(anno_file) as f: |
| data = json.load(f) |
| data["dataset"] = "nexar" |
| data["case_dir"] = str(case_dir) |
| all_data.append(data) |
| |
| |
| for case_dir in sorted(DADA_ROOT.iterdir()): |
| if not case_dir.is_dir(): |
| continue |
| anno_file = case_dir / "annotation.json" |
| if not anno_file.exists(): |
| continue |
| |
| with open(anno_file) as f: |
| data = json.load(f) |
| data["dataset"] = "dada" |
| data["case_dir"] = str(case_dir) |
| data["id"] = case_dir.name |
| all_data.append(data) |
| |
| print(f"加载 {len(all_data)} 案例") |
| return all_data |
|
|
|
|
| def split_data(all_data): |
| """划分train/val/test""" |
| random.shuffle(all_data) |
| n = len(all_data) |
| n_train = int(n * TRAIN_RATIO) |
| n_val = int(n * VAL_RATIO) |
| |
| train_data = all_data[:n_train] |
| val_data = all_data[n_train:n_train + n_val] |
| test_data = all_data[n_train + n_val:] |
| |
| print(f"训练: {len(train_data)}, 验证: {len(val_data)}, 测试: {len(test_data)}") |
| return train_data, val_data, test_data |
|
|
|
|
| |
| def prepare_task1_environment(data_split, split_name): |
| """单帧环境描述: weather, road_type, light""" |
| samples = [] |
| |
| for data in data_split: |
| case_dir = Path(data["case_dir"]) |
| frames = sorted([f for f in case_dir.glob("*.jpg")]) |
| |
| if len(frames) == 0: |
| continue |
| |
| |
| n_samples = random.randint(3, 5) |
| sampled = random.sample(frames, min(n_samples, len(frames))) |
| |
| for frame_path in sampled: |
| if data["dataset"] == "nexar": |
| weather = data.get("weather", "Unknown") |
| road = data.get("road_type", "Unknown") |
| light = data.get("light_conditions", "Unknown") |
| else: |
| weather = data.get("weather", "Unknown") |
| road = data.get("road_type", "Unknown") |
| light = data.get("time_of_day", "Unknown") |
| |
| label = f"Weather: {weather}, Road: {road}, Light: {light}" |
| |
| samples.append({ |
| "task": "environment", |
| "image_path": str(frame_path), |
| "label": label, |
| "metadata": { |
| "case_id": data["id"], |
| "dataset": data["dataset"] |
| } |
| }) |
| |
| print(f"[{split_name}] 任务1: {len(samples)} 样本") |
| return samples |
|
|
|
|
| |
| def prepare_task2_accident(data_split, split_name): |
| """单帧判断是否事故""" |
| samples = [] |
| |
| for data in data_split: |
| case_dir = Path(data["case_dir"]) |
| frames = sorted([f for f in case_dir.glob("*.jpg")]) |
| |
| if len(frames) == 0: |
| continue |
| |
| has_accident = data.get("accident", False) |
| if isinstance(has_accident, str): |
| has_accident = has_accident.lower() == "true" |
| |
| accident_time = data.get("accident_time") |
| |
| |
| if isinstance(accident_time, str): |
| try: |
| accident_time = int(accident_time) |
| except ValueError: |
| accident_time = None |
| |
| |
| n_samples = random.randint(3, 5) |
| sampled_idx = random.sample(range(len(frames)), min(n_samples, len(frames))) |
| |
| for idx in sampled_idx: |
| frame_path = frames[idx] |
| frame_num = int(frame_path.stem) |
| |
| |
| is_accident_frame = False |
| if has_accident and accident_time is not None and accident_time > 0: |
| if abs(frame_num - accident_time) <= 20: |
| is_accident_frame = True |
| |
| label = "Yes" if is_accident_frame else "No" |
| |
| samples.append({ |
| "task": "accident_detection", |
| "image_path": str(frame_path), |
| "label": label, |
| "metadata": { |
| "case_id": data["id"], |
| "dataset": data["dataset"], |
| "frame_num": frame_num |
| } |
| }) |
| |
| print(f"[{split_name}] 任务2: {len(samples)} 样本") |
| return samples |
|
|
|
|
| |
| def prepare_task3_sequence(data_split, split_name): |
| """序列判断事故+描述""" |
| samples = [] |
| |
| for data in data_split: |
| case_dir = Path(data["case_dir"]) |
| frames = sorted([f for f in case_dir.glob("*.jpg")]) |
| |
| if len(frames) < 8: |
| continue |
| |
| |
| risky_time = data.get("risky_time") |
| |
| |
| if isinstance(risky_time, str): |
| try: |
| risky_time = int(risky_time) |
| except ValueError: |
| risky_time = None |
| |
| |
| has_accident = data.get("accident", False) |
| if isinstance(has_accident, str): |
| has_accident = has_accident.lower() == "true" |
| |
| accident_type = data.get("accident_type", "No accident") |
| if accident_type is None or accident_type == "null": |
| accident_type = "No accident" |
| |
| |
| if risky_time is not None and risky_time > 0 and has_accident: |
| |
| start_frame = max(0, risky_time - 8) |
| else: |
| |
| |
| max_start = len(frames) - 16 |
| if max_start <= 0: |
| start_frame = 0 |
| else: |
| start_frame = random.randint(0, max_start) |
| |
| |
| |
| |
| |
| |
| STRIDE = 8 |
| T_MAX = 16 |
|
|
| |
| seq_full = list(range(start_frame, len(frames), STRIDE)) |
| seq_full = [str(frames[i]) for i in seq_full if i < len(frames)] |
|
|
| |
| if len(seq_full) > T_MAX: |
| import numpy as np |
| idx = np.linspace(0, len(seq_full) - 1, T_MAX).round().astype(int).tolist() |
| sequence = [seq_full[j] for j in idx] |
| else: |
| sequence = seq_full |
| |
| |
| if len(sequence) < 2: |
| continue |
| |
| |
| accident_label = "Yes" if has_accident else "No" |
| label = f"Accident: {accident_label}. Description: {accident_type}" |
| |
| samples.append({ |
| "task": "sequence_prediction", |
| "image_sequence": sequence, |
| "label": label, |
| "metadata": { |
| "case_id": data["id"], |
| "dataset": data["dataset"], |
| "sequence_length": len(sequence), |
| "has_accident": has_accident, |
| "start_frame": start_frame |
| } |
| }) |
| |
| print(f"[{split_name}] 任务3: {len(samples)} 样本") |
| return samples |
|
|
|
|
| |
| def main(): |
| print("=" * 50) |
| print("准备预训练数据") |
| print("=" * 50) |
| |
| |
| all_data = load_all_annotations() |
| |
| |
| train_data, val_data, test_data = split_data(all_data) |
| |
| |
| results = {} |
| |
| for split_name, data_split in [("train", train_data), |
| ("val", val_data), |
| ("test", test_data)]: |
| print(f"\n处理 {split_name}...") |
| |
| task1 = prepare_task1_environment(data_split, split_name) |
| task2 = prepare_task2_accident(data_split, split_name) |
| task3 = prepare_task3_sequence(data_split, split_name) |
| |
| results[split_name] = { |
| "task1_environment": task1, |
| "task2_accident_detection": task2, |
| "task3_sequence_prediction": task3, |
| "total_cases": len(data_split) |
| } |
| |
| |
| print("\n" + "=" * 50) |
| print("保存数据...") |
| |
| output_file = OUTPUT_DIR / "pretrain_data.pkl" |
| with open(output_file, "wb") as f: |
| pickle.dump(results, f) |
| print(f"✓ 保存到: {output_file}") |
| |
| |
| summary = {} |
| for split in ["train", "val", "test"]: |
| summary[split] = { |
| "cases": results[split]["total_cases"], |
| "task1": len(results[split]["task1_environment"]), |
| "task2": len(results[split]["task2_accident_detection"]), |
| "task3": len(results[split]["task3_sequence_prediction"]) |
| } |
| |
| output_json = OUTPUT_DIR / "pretrain_summary.json" |
| with open(output_json, "w") as f: |
| json.dump(summary, f, indent=2) |
| print(f"✓ 统计: {output_json}") |
| |
| print("\n" + "=" * 50) |
| print("统计:") |
| for split in ["train", "val", "test"]: |
| print(f"\n{split.upper()}: {summary[split]['cases']} 案例") |
| print(f" 任务1: {summary[split]['task1']}") |
| print(f" 任务2: {summary[split]['task2']}") |
| print(f" 任务3: {summary[split]['task3']}") |
| |
| print("\n✅ 完成!") |
|
|
|
|
| if __name__ == "__main__": |
| main() |