使用多进程将VisDrone标注转YOLO格式时遇类型错误求助
VisDrone 转 YOLO 多进程转换报错排查
问题背景
用 VisDrone.yaml 转标注到 YOLO 格式速度太慢,改完 yaml 没效果,自己写了多进程转换代码,但运行时报错:
File "/usr/lib/python3.9/multiprocessing/pool.py", line 364, in map return self._map_async(func, iterable, mapstar, chunksize).get() File "/usr/lib/python3.9/multiprocessing/pool.py", line 478, in _map_async chunksize, extra = divmod(len(iterable), len(self._pool) * 4) TypeError: 'NoneType' object cannot be interpreted as an integer
把进度条去掉后还是报错,求解决。
问题根源
- 迭代器长度无法获取:
tqdm包装的glob结果是迭代器,而multiprocessing.Pool.map需要能计算长度的序列,迭代器拿不到len(),导致divmod计算时出错。 - Worker 函数逻辑错误:原
visdrone2yolo_worker内部又写了for f in pbar,但 Worker 函数是用来处理单个文件的,这不仅变量冲突,而且子进程里访问不到主进程的pbar,会变成None触发报错。 - 依赖全局变量:Worker 里直接用了
dir这类全局变量,多进程环境下子进程无法正确获取,也会导致None值。 - 文件写入效率低:原代码每处理一行标注就打开写入一次文件,既慢又容易出问题。
修复后的代码
from multiprocessing import Pool from pathlib import Path import os from PIL import Image from tqdm import tqdm def convert_box(size, box): # 把 VisDrone 框转成 YOLO 的 xywh 格式 dw = 1. / size[0] dh = 1. / size[1] return (box[0] + box[2] / 2) * dw, (box[1] + box[3] / 2) * dh, box[2] * dw, box[3] * dh def visdrone2yolo_worker(args): f, img_dir, label_dir = args try: # 拼接对应图片路径 img_path = img_dir / f.name.replace('.txt', '.jpg') img_size = Image.open(img_path).size lines = [] with open(f, 'r') as file: for row in [x.split(',') for x in file.read().strip().splitlines()]: # 跳过忽略区域 if row[4] == '0': continue cls = int(row[5]) - 1 box = convert_box(img_size, tuple(map(int, row[:4]))) lines.append(f"{cls} {' '.join(f'{x:.6f}' for x in box)}\n") # 收集完所有标注后一次性写入 label_path = label_dir / f.name with open(label_path, 'w') as fl: fl.writelines(lines) except Exception as e: print(f"处理文件 {f.name} 出错: {str(e)}") def visdrone2yolo(dir): img_dir = dir / 'images' anno_dir = dir / 'annotations' label_dir = dir / 'labels' label_dir.mkdir(parents=True, exist_ok=True) # 把迭代器转成列表,让 Pool 能获取长度 anno_files = list(anno_dir.glob('*.txt')) # 准备每个 worker 的参数,避免依赖全局变量 worker_args = [(f, img_dir, label_dir) for f in anno_files] with Pool(50) as p: # 用 imap 配合 tqdm 显示多进程进度 list(tqdm(p.imap(visdrone2yolo_worker, worker_args), total=len(anno_files), desc=f'转换 {dir}')) # 执行转换 yaml = {'path': '/你的数据集根路径'} # 替换成实际路径 root_dir = Path(yaml['path']) for subset in ['VisDrone2019-DET-train', 'VisDrone2019-DET-val', 'VisDrone2019-DET-test-dev']: visdrone2yolo(root_dir / subset)
核心修复说明
- 迭代器转列表:把
anno_dir.glob('*.txt')的结果转成列表,让Pool能计算长度,解决len(iterable)报错。 - Worker 参数化:给 Worker 传递包含文件、目录的元组,不再依赖全局变量,避免子进程变量丢失问题。
- 批量写入文件:收集所有标注行后一次性写入,大幅提升效率。
- 正确的进度条用法:用
p.imap配合tqdm实现多进程进度显示,替代原代码中错误的进度条嵌套。
内容的提问来源于stack exchange,提问作者Noam Levi Avshalom
相关产品推荐
相关产品推荐

