1. 数据文件检查脚本开发背景
在自动驾驶和计算机视觉领域,我们经常需要处理包含多摄像头和激光雷达点云数据的大规模数据集。这类数据集通常按照特定结构组织,比如每个场景包含8个摄像头视角(CAM_1到CAM_8)和1个激光点云数据(MergedPointCloud)。但在实际工作中,我发现数据下载或解压过程中经常出现文件缺失的情况,导致后续处理流程中断。
这个问题看似简单,但当数据集包含数万个场景时,手动检查每个文件夹的结构几乎不可能。更糟糕的是,这种错误往往要到数据处理流程的中后期才会被发现,浪费大量时间和计算资源。为此,我开发了这个Python脚本来自动化检查过程,它能快速定位问题文件夹并提供修复选项。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 脚本核心功能解析
2.1 文件夹结构验证机制
脚本的核心是验证每个主文件夹是否包含正确的子文件夹结构。标准结构要求:
- 8个以"CAM_"开头的文件夹(CAM_1到CAM_8)
- 1个名为"MergedPointCloud"的文件夹
- 不允许存在其他类型的文件夹
验证逻辑通过以下步骤实现:
- 遍历指定目录下的所有文件夹
- 对每个文件夹统计三类子文件夹数量:
- CAM_开头的文件夹(摄像头数据)
- MergedPointCloud文件夹(点云数据)
- 其他类型文件夹(视为异常)
- 将统计结果与标准结构(8+1+0)比对
提示:脚本特意将CAM_*文件夹的数量检查与命名格式检查分离,这样即使CAM编号不连续(如只有CAM_1,CAM_3,CAM_5),也能准确识别缺失情况。
2.2 智能修复功能设计
当检测到结构异常时,脚本不仅会报告问题,还能提供一键修复功能。修复逻辑考虑了两个关键点:
-
点云文件夹处理:
- 如果缺失MergedPointCloud,直接创建空文件夹
- 因为点云数据通常独立存储,无法自动生成内容
-
摄像头文件夹处理:
- 分析现有CAM文件夹的编号(如CAM_1, CAM_3)
- 自动补全缺失的编号(如创建CAM_2, CAM_4等)
- 保留现有数据不被覆盖
这个设计确保了修复过程安全可靠,不会意外修改或删除已有数据。
3. 脚本使用详解
3.1 部署与运行方法
将脚本部署到数据集的根目录下(如dataset/sensor_blobs/trainval),然后执行:
bash复制python check.py
典型输出示例:
code复制============================================================
文件夹结构检查工具
确保每个文件夹包含: 8个CAM_*文件夹 + 1个MergedPointCloud文件夹
============================================================
当前工作目录: /path/to/dataset
找到 152 个文件夹需要检查
检查文件夹: 2021.06.07.12.42.11_veh-38_01777_02078
- CAM_* 文件夹: 7 个
- MergedPointCloud 文件夹: 1 个
- 其他文件夹: 0 个
✗ 2021.06.07.12.42.11_veh-38_01777_02078 文件夹结构不正确
建议: 2021.06.07.12.42.11_veh-38_01777_02078 中需要 1 个 CAM_* 文件夹
3.2 输出报告解读
脚本会生成详细的诊断报告,包含:
- 问题文件夹总数
- 问题文件夹列表
- 每个问题文件夹的详细分析:
- 实际子文件夹数量 vs 应有数量
- 多余文件夹的名称(如有)
- 具体修复建议
报告格式清晰,可直接作为文档保存或分享给团队成员。
4. 脚本代码深度解析
4.1 核心函数实现
check_and_organize_folders()函数是脚本的核心,其工作流程:
- 获取当前工作目录
- 筛选出所有子文件夹(排除文件)
- 对每个文件夹:
- 分类统计CAM、MergedPointCloud和其他文件夹
- 验证数量是否符合8+1+0的标准
- 记录验证结果和详细统计信息
python复制def check_and_organize_folders():
current_dir = os.getcwd()
folders = [f for f in os.listdir(current_dir)
if os.path.isdir(os.path.join(current_dir, f))]
invalid_folders = []
folder_details = {}
for main_folder in folders:
main_folder_path = os.path.join(current_dir, main_folder)
contents = os.listdir(main_folder_path)
subfolders = [item for item in contents
if os.path.isdir(os.path.join(main_folder_path, item))]
cam_folders = [f for f in subfolders if f.startswith('CAM_')]
merged_folders = [f for f in subfolders if f == 'MergedPointCloud']
other_folders = [f for f in subfolders if not f.startswith('CAM_') and f != 'MergedPointCloud']
is_valid = (len(subfolders) == 9 and
len(cam_folders) == 8 and
len(merged_folders) == 1)
folder_details[main_folder] = {
'is_valid': is_valid,
'cam_folders': len(cam_folders),
'merged_folders': len(merged_folders),
'other_folders': len(other_folders),
'total_subfolders': len(subfolders),
'other_folder_names': other_folders
}
if not is_valid:
invalid_folders.append(main_folder)
return invalid_folders, folder_details
4.2 修复功能实现
create_missing_structure()函数实现了智能修复:
- 检查每个主文件夹的现有结构
- 补全缺失的MergedPointCloud文件夹
- 分析现有CAM文件夹编号,补全缺失编号
python复制def create_missing_structure():
current_dir = os.getcwd()
folders = [f for f in os.listdir(current_dir)
if os.path.isdir(os.path.join(current_dir, f))]
for main_folder in folders:
main_folder_path = os.path.join(current_dir, main_folder)
contents = os.listdir(main_folder_path)
subfolders = [item for item in contents
if os.path.isdir(os.path.join(main_folder_path, item))]
cam_folders = [f for f in subfolders if f.startswith('CAM_')]
merged_folders = [f for f in subfolders if f == 'MergedPointCloud']
if len(merged_folders) == 0:
merged_path = os.path.join(main_folder_path, 'MergedPointCloud')
os.makedirs(merged_path, exist_ok=True)
if len(cam_folders) < 8:
existing_cam_numbers = []
for cam_folder in cam_folders:
try:
num = int(cam_folder.split('_')[1])
existing_cam_numbers.append(num)
except (IndexError, ValueError):
continue
for i in range(1, 9):
if i not in existing_cam_numbers:
cam_path = os.path.join(main_folder_path, f'CAM_{i}')
os.makedirs(cam_path, exist_ok=True)
5. 实际应用中的经验分享
5.1 性能优化技巧
在处理超大规模数据集时(10万+文件夹),原始脚本可能需要优化:
- 并行处理:
python复制from concurrent.futures import ThreadPoolExecutor
def check_folder(main_folder):
# 原检查逻辑...
with ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(check_folder, folders))
- 进度显示:
python复制from tqdm import tqdm
for main_folder in tqdm(folders, desc="检查进度"):
# 检查逻辑...
- 结果缓存:
将检查结果保存为JSON文件,避免重复检查:
python复制import json
if os.path.exists('check_results.json'):
with open('check_results.json', 'r') as f:
return json.load(f)
# ...检查完成后保存结果
5.2 常见问题排查
-
权限问题:
- 修复功能需要写权限
- 解决方案:提前检查权限
python复制if not os.access(main_folder_path, os.W_OK): print(f"警告: 无写权限 - {main_folder_path}") continue -
符号链接处理:
- 默认情况下os.listdir会跟随符号链接
- 如需排除:
python复制subfolders = [item for item in contents if os.path.isdir(os.path.join(main_folder_path, item)) and not os.path.islink(os.path.join(main_folder_path, item))] -
特殊字符问题:
- 处理包含空格或特殊字符的路径:
python复制main_folder_path = os.path.join(current_dir, main_folder.encode('utf-8').decode('utf-8'))
6. 脚本扩展与定制
6.1 支持自定义结构规则
通过修改VALID_STRUCTURE字典,可以轻松适配不同的文件夹结构要求:
python复制VALID_STRUCTURE = {
'CAM_*': {'min': 8, 'max': 8},
'MergedPointCloud': {'min': 1, 'max': 1},
'RADAR_*': {'min': 0, 'max': 0} # 不允许RADAR文件夹
}
def is_structure_valid(subfolders):
counts = {}
for pattern in VALID_STRUCTURE:
if '*' in pattern:
prefix = pattern.split('*')[0]
count = sum(1 for f in subfolders if f.startswith(prefix))
else:
count = subfolders.count(pattern)
counts[pattern] = count
for pattern, rules in VALID_STRUCTURE.items():
if not (rules['min'] <= counts[pattern] <= rules['max']):
return False
return True
6.2 集成到数据处理流水线
可以将检查脚本作为数据预处理的一部分,在pipeline中自动运行:
python复制class DataValidator:
def __init__(self, dataset_path):
self.dataset_path = dataset_path
def validate(self):
original_dir = os.getcwd()
os.chdir(self.dataset_path)
try:
invalid_folders, _ = check_and_organize_folders()
return len(invalid_folders) == 0
finally:
os.chdir(original_dir)
# 在pipeline中使用
validator = DataValidator('dataset/sensor_blobs/trainval')
if not validator.validate():
raise ValueError("数据集结构验证失败")
7. 实际应用案例
在一次自动驾驶项目的数据准备阶段,我们收到了约50TB的原始数据,包含超过20万个场景。使用这个脚本,我们在15分钟内就发现了873个结构不完整的文件夹,主要问题包括:
- 缺失1-2个摄像头文件夹(占75%)
- 完全缺失点云文件夹(占20%)
- 存在多余文件夹(占5%)
脚本的自动修复功能为我们节省了至少40小时的手动检查时间,而且确保了后续训练流程不会因为数据缺失而意外中断。
8. 注意事项与最佳实践
-
备份优先:
- 执行自动修复前,建议先备份原始数据
- 可以添加备份选项:
python复制def backup_folder(folder_path): backup_path = folder_path + "_backup" if not os.path.exists(backup_path): shutil.copytree(folder_path, backup_path) -
日志记录:
- 将检查结果和修复操作记录到日志文件
python复制import logging logging.basicConfig(filename='data_check.log', level=logging.INFO) -
异常处理:
- 增强对异常情况的处理能力
python复制try: os.makedirs(cam_path, exist_ok=True) except Exception as e: logging.error(f"创建文件夹失败: {cam_path} - {str(e)}") -
性能考量:
- 对于特别大的数据集,考虑分批次处理
- 添加内存使用监控
这个脚本虽然简单,但在实际项目中发挥了巨大价值。它体现了"用自动化解决重复性问题"的开发理念,也展示了Python在文件系统操作方面的强大能力。根据项目需求,你可以进一步扩展其功能,比如增加文件内容验证、哈希校验等更复杂的检查逻辑。
