You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用多进程将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)

核心修复说明

  1. 迭代器转列表:把 anno_dir.glob('*.txt') 的结果转成列表,让 Pool 能计算长度,解决 len(iterable) 报错。
  2. Worker 参数化:给 Worker 传递包含文件、目录的元组,不再依赖全局变量,避免子进程变量丢失问题。
  3. 批量写入文件:收集所有标注行后一次性写入,大幅提升效率。
  4. 正确的进度条用法:用 p.imap 配合 tqdm 实现多进程进度显示,替代原代码中错误的进度条嵌套。

内容的提问来源于stack exchange,提问作者Noam Levi Avshalom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:35:16