告别混乱!用Python+shutil一键整理UCF101数据集(附完整代码)

张开发
2026/4/4 6:59:31 15 分钟阅读
告别混乱!用Python+shutil一键整理UCF101数据集(附完整代码)
告别混乱用Pythonshutil一键整理UCF101数据集附完整代码刚接触行为识别的研究者十有八九会在UCF101这类经典数据集的预处理环节卡壳——下载的压缩包解压后视频文件散落在101个子目录中而官方提供的训练/测试划分文件又需要手动匹配。这种目录结构的混乱不仅拖慢实验进度更可能因人为操作失误导致数据泄露训练集混入测试样本。本文将用不到100行Python代码解决这个痛点实现从原始压缩包到标准目录结构的全自动转换。1. 为什么需要自动化预处理UCF101的官方文件结构设计存在三个典型问题原始视频存放方式按类别存放在101个独立文件夹文件名仅含序号如v_ApplyEyeMakeup_g01_c01.avi划分文件格式特殊trainlist01.txt仅记录相对路径如ApplyEyeMakeup/v_ApplyEyeMakeup_g01_c01.avi多版本划分方案提供三种不同的训练测试划分01/02/03手动操作易混淆手动整理时常见的翻车现场包括错误地将同一视频同时放入train和test目录因路径拼接错误导致文件复制失败需要重复处理三种划分方案时效率低下# 典型问题示例手动处理时的路径拼接陷阱 wrong_path os.path.join(UCF-101, ApplyEyeMakeup_v_ApplyEyeMakeup_g01_c01.avi) # 错误 correct_path os.path.join(UCF-101, ApplyEyeMakeup, v_ApplyEyeMakeup_g01_c01.avi) # 正确2. 工程化解决方案设计我们的脚本需要实现以下核心功能智能路径解析自动识别原始压缩包解压后的嵌套目录结构多划分方案支持通过命令行参数指定使用哪种划分01/02/03原子化操作确保文件移动过程的幂等性重复执行不报错2.1 目录结构设计目标最终生成的标准化结构应满足dataset/ ├── test/ │ ├── ApplyEyeMakeup/ │ │ ├── v_ApplyEyeMakeup_g01_c01.avi │ │ └── ... ├── train/ │ ├── ApplyLipstick/ │ │ ├── v_ApplyLipstick_g01_c01.avi │ │ └── ... └── splits/ # 原始划分文件备份2.2 关键技术实现使用shutil库的三大核心方法shutil.move()原子化文件移动比复制删除更安全os.makedirs()递归创建目录避免父目录不存在时报错os.path.exists()操作前检查路径有效性import shutil import os from pathlib import Path def safe_move(src, dst): 线程安全的文件移动操作 Path(dst).parent.mkdir(parentsTrue, exist_okTrue) if Path(src).exists(): shutil.move(src, dst)3. 完整实现代码以下代码支持三种划分方案通过--split参数指定默认为01#!/usr/bin/env python3 import argparse import shutil from pathlib import Path def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--data_root, typestr, requiredTrue, helpUCF101解压后的根目录含UCF-101文件夹和划分文件) parser.add_argument(--output_dir, typestr, defaultdataset, help输出目录路径) parser.add_argument(--split, typestr, choices[01, 02, 03], default01, help使用的划分方案01/02/03) return parser.parse_args() def main(): args parse_args() src_videos Path(args.data_root) / UCF-101 splits_dir Path(args.data_root) / UCF101TrainTestSplits # 创建输出目录 (Path(args.output_dir)/splits).mkdir(parentsTrue, exist_okTrue) # 处理测试集 with open(splits_dir/ftestlist{args.split}.txt) as f: for line in f: video_rel_path line.strip() class_name, video_name video_rel_path.split(/) src src_videos / video_rel_path dst Path(args.output_dir)/test/class_name/video_name safe_move(src, dst) # 剩余文件作为训练集 for class_dir in src_videos.iterdir(): if class_dir.is_dir(): for video in class_dir.glob(*.avi): dst Path(args.output_dir)/train/class_dir.name/video.name safe_move(video, dst) def safe_move(src, dst): dst.parent.mkdir(parentsTrue, exist_okTrue) if src.exists(): shutil.move(str(src), str(dst)) if __name__ __main__: main()4. 进阶使用技巧4.1 与帧提取工具联动整理后的目录可直接配合FFmpeg进行帧提取# 批量提取训练集视频帧 find dataset/train -name *.avi | xargs -I {} ffmpeg -i {} {.}/frame_%04d.jpg4.2 异常处理增强建议添加以下安全检查验证原始文件是否完整101个类别文件夹移动文件后校验MD5确保数据一致性记录操作日志便于回溯# MD5校验示例 import hashlib def get_file_md5(file_path): with open(file_path, rb) as f: return hashlib.md5(f.read()).hexdigest()4.3 性能优化方案当处理超大规模数据时使用多线程加速concurrent.futures.ThreadPoolExecutor先复制再删除替代直接移动避免跨磁盘操作慢使用rsync替代shutil支持断点续传from concurrent.futures import ThreadPoolExecutor def batch_move(file_pairs, workers4): with ThreadPoolExecutor(max_workersworkers) as executor: executor.map(lambda x: safe_move(*x), file_pairs)5. 常见问题解决方案Q1执行时报PermissionError怎么办方案确保脚本有写入权限或使用sudo执行预防提前用os.access()检查权限Q2如何验证划分是否正确# 检查训练测试集无交集 train_videos set(p.name for p in Path(dataset/train).glob(*/*.avi)) test_videos set(p.name for p in Path(dataset/test).glob(*/*.avi)) assert not (train_videos test_videos), 存在数据泄露Q3需要处理其他视频格式怎么办 修改代码中的视频扩展名过滤条件# 原版只处理.avi for video in class_dir.glob(*.avi): # 支持多种格式 VIDEO_EXTS {.avi, .mp4, .mov} for video in class_dir.iterdir(): if video.suffix.lower() in VIDEO_EXTS:

更多文章