保姆级教程:用Python脚本一键搞定CrowdHuman数据集转YOLOv5格式(含只保留person类别的代码)

一只特立独行的cherry

Python自动化实战:CrowdHuman数据集高效转YOLOv5格式全流程解析

每次面对新数据集时,最头疼的就是格式转换问题。上周我接手一个行人检测项目,需要用到CrowdHuman数据集,但它的ODGT标注格式和YOLOv5不兼容。经过两天折腾,终于总结出一套完整的自动化处理方案,今天就把这个保姆级教程分享给大家,包含完整的代码实现和原理讲解。

1. 环境准备与数据获取

在开始之前,我们需要准备好基础环境。建议使用Python 3.8+版本,并安装以下依赖库:

bash复制pip install pandas numpy tqdm opencv-python

CrowdHuman数据集可以从官网下载,主要包含以下文件:

  • 图像文件(ZIP压缩包):

    • CrowdHuman_train01.zip
    • CrowdHuman_train02.zip
    • CrowdHuman_train03.zip
    • CrowdHuman_val.zip
  • 标注文件:

    • annotation_train.odgt
    • annotation_val.odgt

提示:下载完成后,建议先校验文件完整性,避免后续处理出错。

2. 数据集结构解析与预处理

CrowdHuman的标注格式比较特殊,采用ODGT(Open Dataset Ground Truth)格式,这是一种基于JSON Lines的文本格式。每个标注文件包含多行,每行是一个JSON对象,对应一张图片的标注信息。

典型的标注结构如下:

json复制{
  "ID": "273271,1017c000ac1360b7",
  "gtboxes": [
    {
      "tag": "person",
      "vbox": [x,y,w,h],
      "fbox": [x,y,w,h],
      "hbox": [x,y,w,h],
      "extra": {...}
    },
    ...
  ]
}

其中关键字段说明:

  • vbox: 可见框 (visible box)
  • fbox: 全框 (full box)
  • hbox: 头部框 (head box)

对于行人检测任务,我们通常使用fbox作为检测目标。

3. ODGT转YOLOv5格式的核心代码实现

下面是我编写的转换脚本核心部分,包含详细注释:

python复制import json
import os
from pathlib import Path
import cv2
from tqdm import tqdm

def convert_odgt_to_yolo(odgt_path, images_dir, output_dir, class_filter=None):
    """
    将CrowdHuman ODGT格式转换为YOLOv5格式
    
    参数:
        odgt_path: ODGT标注文件路径
        images_dir: 图像文件目录
        output_dir: 输出目录
        class_filter: 需要保留的类别列表
    """
    os.makedirs(output_dir, exist_ok=True)
    
    with open(odgt_path, 'r') as f:
        lines = f.readlines()
    
    for line in tqdm(lines, desc=f"Processing {odgt_path}"):
        data = json.loads(line)
        img_id = data['ID']
        img_path = find_image(images_dir, img_id)
        
        if not img_path:
            continue
            
        img = cv2.imread(img_path)
        img_h, img_w = img.shape[:2]
        
        txt_path = os.path.join(output_dir, f"{Path(img_path).stem}.txt")
        
        with open(txt_path, 'w') as f_txt:
            for box in data.get('gtboxes', []):
                if box['tag'] == 'mask':
                    continue
                    
                # 使用全框(fbox)作为检测目标
                x, y, w, h = box['fbox']
                
                # 转换为YOLO格式(中心点坐标和宽高,归一化)
                x_center = (x + w/2) / img_w
                y_center = (y + h/2) / img_h
                w_norm = w / img_w
                h_norm = h / img_h
                
                # 类别处理:0-head, 1-person
                class_id = 0 if box['tag'] == 'head' else 1
                
                # 如果设置了类别过滤,只保留指定类别
                if class_filter is not None and class_id not in class_filter:
                    continue
                    
                # 写入YOLO格式标注
                f_txt.write(f"{class_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n")

def find_image(images_dir, img_id):
    """根据ID查找对应的图像文件"""
    for ext in ['.jpg', '.png', '.jpeg']:
        img_path = os.path.join(images_dir, f"{img_id}{ext}")
        if os.path.exists(img_path):
            return img_path
    return None

4. 只保留person类别的进阶处理

很多实际项目中,我们只需要检测person类别。下面是如何过滤掉head类别的代码实现:

python复制def filter_person_only(input_dir, output_dir):
    """
    过滤YOLO格式标注,只保留person类别(类别1)
    并将类别重新映射为0(因为只剩一个类别)
    """
    os.makedirs(output_dir, exist_ok=True)
    
    for txt_file in tqdm(os.listdir(input_dir), desc="Filtering person only"):
        input_path = os.path.join(input_dir, txt_file)
        output_path = os.path.join(output_dir, txt_file)
        
        with open(input_path, 'r') as f_in, open(output_path, 'w') as f_out:
            for line in f_in:
                parts = line.strip().split()
                if not parts:
                    continue
                    
                class_id = int(parts[0])
                if class_id == 1:  # 只保留person
                    # 将类别ID改为0
                    parts[0] = '0'
                    f_out.write(' '.join(parts) + '\n')

使用示例:

python复制# 转换整个数据集
convert_odgt_to_yolo(
    "annotation_train.odgt",
    "Images",
    "labels/train"
)

# 只保留person类别
filter_person_only(
    "labels/train",
    "labels/train_person_only"
)

5. 自动化处理流水线搭建

为了进一步提高效率,我编写了一个完整的自动化处理脚本process_crowdhuman.py,包含以下功能:

  1. 自动解压数据集
  2. 格式转换
  3. 类别过滤
  4. 数据集划分
  5. 生成YOLOv5配置文件
python复制import argparse
import zipfile
import shutil
from concurrent.futures import ThreadPoolExecutor

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--input-dir', required=True, help='原始数据集目录')
    parser.add_argument('--output-dir', default='yolov5_data', help='输出目录')
    parser.add_argument('--person-only', action='store_true', help='是否只保留person类别')
    parser.add_argument('--val-split', type=float, default=0.1, help='验证集比例')
    args = parser.parse_args()

    # 创建输出目录结构
    os.makedirs(args.output_dir, exist_ok=True)
    images_dir = os.path.join(args.output_dir, 'images')
    labels_dir = os.path.join(args.output_dir, 'labels')
    os.makedirs(images_dir, exist_ok=True)
    os.makedirs(labels_dir, exist_ok=True)

    # 解压图像文件
    print("解压图像文件...")
    zip_files = [
        'CrowdHuman_train01.zip',
        'CrowdHuman_train02.zip',
        'CrowdHuman_train03.zip',
        'CrowdHuman_val.zip'
    ]
    
    with ThreadPoolExecutor() as executor:
        for zip_file in zip_files:
            zip_path = os.path.join(args.input_dir, zip_file)
            executor.submit(unzip_file, zip_path, images_dir)

    # 处理标注文件
    print("处理标注文件...")
    convert_odgt_to_yolo(
        os.path.join(args.input_dir, 'annotation_train.odgt'),
        images_dir,
        os.path.join(labels_dir, 'train')
    )
    
    convert_odgt_to_yolo(
        os.path.join(args.input_dir, 'annotation_val.odgt'), 
        images_dir,
        os.path.join(labels_dir, 'val')
    )

    # 类别过滤
    if args.person_only:
        print("过滤只保留person类别...")
        filter_person_only(
            os.path.join(labels_dir, 'train'),
            os.path.join(labels_dir, 'train_person')
        )
        filter_person_only(
            os.path.join(labels_dir, 'val'),
            os.path.join(labels_dir, 'val_person')
        )
        shutil.rmtree(os.path.join(labels_dir, 'train'))
        shutil.rmtree(os.path.join(labels_dir, 'val'))
        os.rename(os.path.join(labels_dir, 'train_person'), os.path.join(labels_dir, 'train'))
        os.rename(os.path.join(labels_dir, 'val_person'), os.path.join(labels_dir, 'val'))

    # 生成YOLOv5配置文件
    print("生成YOLOv5配置文件...")
    with open(os.path.join(args.output_dir, 'crowdhuman.yaml'), 'w') as f:
        f.write(f"""path: {os.path.abspath(args.output_dir)}
train: images/train
val: images/val

names:
  0: person
""")

def unzip_file(zip_path, extract_to):
    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(extract_to)

if __name__ == '__main__':
    main()

使用方式:

bash复制python process_crowdhuman.py --input-dir ./raw_data --output-dir ./yolov5_data --person-only

6. 常见问题与解决方案

在实际使用过程中,可能会遇到以下问题:

  1. 图像与标注不匹配

    • 检查图像文件名是否与标注中的ID一致
    • 确保解压时没有重命名文件
  2. 标注框超出图像边界

    • 添加边界检查代码:
      python复制x_center = max(0, min(1, x_center))
      y_center = max(0, min(1, y_center))
      w_norm = max(0, min(1, w_norm))
      h_norm = max(0, min(1, h_norm))
      
  3. 处理速度慢

    • 使用多线程处理:
      python复制from concurrent.futures import ThreadPoolExecutor
      
      with ThreadPoolExecutor(max_workers=8) as executor:
          executor.map(process_image, image_paths)
      
  4. 内存不足

    • 分批处理数据
    • 使用生成器而非一次性加载所有数据

7. 性能优化技巧

经过多次实践,我总结出几个提升处理效率的技巧:

  • 使用更快的JSON解析库:替换标准库的json为orjson

    python复制import orjson
    
    data = orjson.loads(line)
    
  • 并行处理:对于大型数据集,使用多进程处理

    python复制from multiprocessing import Pool
    
    with Pool(processes=4) as pool:
        pool.map(process_func, data_chunks)
    
  • 缓存机制:对于重复操作,使用缓存避免重复计算

    python复制from functools import lru_cache
    
    @lru_cache(maxsize=1000)
    def find_image_cached(img_id):
        return find_image(images_dir, img_id)
    
  • 进度显示:使用tqdm显示处理进度

    python复制from tqdm import tqdm
    
    for item in tqdm(items, desc="Processing"):
        process_item(item)
    

这套方案在我最近的行人检测项目中表现良好,处理完整的CrowdHuman数据集(约15GB)只需不到30分钟,比手动处理节省了大量时间。特别是在需要多次实验不同参数时,自动化脚本的优势更加明显。

内容推荐

从零多普勒面到斜距平面:深入解析SAR成像中的核心几何概念
本文深入解析SAR(合成孔径雷达)成像中的核心几何概念,包括零多普勒面、斜距平面等关键术语。通过实际案例说明这些几何概念如何影响SAR图像质量,并提供实用建议帮助读者避免常见误区,提升对SAR成像技术的理解与应用能力。
MIT 6.824 分布式系统课程与Lab实战:Go语言实现全解析
本文深入解析MIT 6.824分布式系统课程的Go语言实现,涵盖Lab实战经验与核心设计思路。重点探讨Go语言的goroutine和channel如何简化Raft共识算法等分布式系统开发,并提供MapReduce、Raft选举等关键Lab的代码示例与调试技巧,帮助开发者高效掌握分布式系统核心技术。
GD32F103待机模式实战:RTC闹钟唤醒与低功耗设计
本文详细介绍了GD32F103待机模式与RTC闹钟唤醒的低功耗设计实践。通过硬件配置、RTC初始化、闹钟设置等关键步骤,实现μA级超低功耗运行,并分享外围设备断电管理、功耗优化等实战技巧,帮助开发者构建高效节能的嵌入式系统。
告别手搓Redis锁!Redisson实战指南:从基础锁到高可用集群部署
本文深入解析Redisson分布式锁框架的核心功能与高可用部署方案,对比手工实现Redis锁的缺陷,展示Redisson在自动续期、可重入、集群支持等方面的优势。通过电商秒杀、票务系统等实战案例,详细演示可重入锁、公平锁、读写锁的应用场景和代码实现,并提供哨兵模式与集群模式的最佳配置实践。
高通Camera开发者的效率神器:Pipeline可视化工具V1.4安装与高阶使用指南
本文详细介绍了高通Camera Pipeline可视化工具V1.4的安装部署与高阶使用技巧,帮助开发者提升调试效率。该工具通过可视化交互图谱优化Pipeline调试流程,支持团队协作、性能瓶颈分析和跨版本对比,特别适用于高通骁龙平台的Camera开发团队。
保姆级教程:用Python+OpenNI2驱动奥比中光Astra Pro,实现RGBD数据实时采集与可视化
本文提供了一份详细的Python+OpenNI2驱动奥比中光Astra Pro的保姆级教程,涵盖从环境配置到RGBD数据实时采集与可视化的全流程。通过实战案例,读者将学习如何安装驱动、配置Python环境、处理深度图与彩色图数据,并实现数据集的自动化保存,适用于三维重建、机器人导航等应用场景。
智能车竞赛节能组“偷电”秘籍:如何用LCC补偿网络让你的小车充电又快又稳?
本文深入解析了智能车竞赛节能组中LCC谐振补偿网络的应用,通过理论分析和实战案例,展示了如何利用LCC网络解决无线充电中的效率与稳定性问题。文章详细介绍了LCC网络的工作原理、参数计算及优化策略,帮助参赛者在动态比赛中实现快速稳定的能量传输,提升竞赛成绩。
Spring 5.0.x 源码本地编译实战:从仓库克隆到IDEA构建
本文详细介绍了如何在本地环境中编译Spring 5.0.x源码,从GitHub仓库克隆代码到使用IDEA构建项目的完整流程。内容包括环境配置、Gradle构建工具的使用、常见问题解决及源码阅读技巧,帮助开发者深入理解Spring框架的内部实现机制。
从7812/7912电源到信号发生器:一个完整电子小系统的DIY实战记录
本文详细记录了从7812/7912电源设计到多功能信号发生器构建的全过程,涵盖正弦波、方波、三角波等多种波形生成技术。通过电源系统设计、文氏电桥调校及波形转换电路实现,展示了电子DIY项目的完整实践方案,特别强调了电源稳定性对信号质量的关键影响。
别再乱用$了!Godot 4.2中GDScript获取节点的5种正确姿势与性能对比
本文深入探讨了Godot 4.2中GDScript获取节点的5种高效方法,包括$符号、get_node()、find_child()等,并对比了它们的性能差异。通过详细解析每种方法的适用场景与优化技巧,帮助开发者避免常见性能陷阱,提升游戏开发效率与代码质量。特别强调了$符号的局限性与唯一节点特性的优势。
从Endnote转投Zotero?我的无缝迁移与深度调教全记录(含GB/T 7714格式完美适配方案)
本文详细记录了从Endnote迁移到Zotero的全过程,特别针对中文论文写作中的GB/T 7714格式提供了深度适配方案。通过云原生设计、插件生态系统和中文友好度三大优势,Zotero显著提升了科研工作效率。文章还分享了零数据损失的迁移方法、GB/T 7714格式的终极适配方案以及科研工作流的重构与优化策略。
从图像边缘检测到流体模拟:深入浅出聊聊中心差分法的那些实际应用
本文深入探讨了中心差分法在图像边缘检测和流体模拟中的实际应用,通过MATLAB代码示例展示了二阶与四阶差分格式的精度与稳定性差异。文章详细分析了中心差分法在数值计算中的核心作用,包括其在Sobel算子中的应用及流体力学模拟中的CFL条件影响,为工程实践提供了实用的差分格式选择策略。
STM32串口通信实战:从字符串收发到数据解析的完整流程
本文详细介绍了STM32串口通信的完整流程,从基础配置到字符串收发与数据解析,涵盖初始化设置、字符串格式化、可靠接收与帧解析等关键环节。通过实战案例展示电机控制系统中的数据通信实现,提供常见问题解决方案和性能优化技巧,帮助开发者高效完成嵌入式通信开发。
从入门到精通:构建你的高效Vim工作流(万字指南)
本文是一份全面的Vim使用指南,从基础配置到高级技巧,帮助开发者构建高效的工作流。涵盖Vim核心模式、高效编辑技巧、插件管理和性能优化,特别适合希望提升编码效率的程序员。通过实战案例和配置建议,读者可以快速掌握这款强大的文本编辑器。
告别EfficientNet的‘龟速’:用RegNet在GPU上实现5倍推理加速的保姆级配置指南
本文详细介绍了如何通过RegNet在GPU上实现5倍推理加速,替代EfficientNet的缓慢推理。从环境配置、模型加载到性能调优,提供全流程保姆级指南,特别适合边缘计算和实时视频分析场景。RegNet作为Facebook AI团队的创新成果,在保持精度的同时显著提升推理效率。
软件设计师考试必看:数据流图(DFD)的5个实战避坑技巧
本文针对软件设计师考试中的数据流图(DFD)题目,总结了5个实战避坑技巧,包括平衡原则、数据字典、加工黑洞与奇迹、外部实体混淆和分层命名一致性。通过真实考题案例和详细解题步骤,帮助考生避免常见错误,提升考试成绩。特别强调了平衡原则在DFD中的关键作用。
R语言生存分析实战:基于GBM(梯度提升机)的临床预后模型构建与评估
本文详细介绍了如何使用R语言和GBM(梯度提升机)构建临床预后模型,特别适用于生存分析任务。通过数据准备、模型构建、评估优化及实战预测等步骤,帮助研究人员高效处理复杂临床数据,提升预测精度。GBM算法在捕捉变量间非线性关系和交互作用方面表现卓越,适用于癌症预后等医学研究。
告别物理键盘:在Vue后台里为触摸屏集成虚拟键盘的完整避坑指南(simple-keyboard配置详解)
本文详细介绍了如何在Vue管理后台中集成simple-keyboard虚拟键盘组件,解决触摸屏输入的核心痛点。从基础集成到高级定制,涵盖焦点管理、中英文切换、动态布局等企业级解决方案,显著提升输入效率和用户体验。特别适合医疗、工业PDA等专业场景。
【矩阵论】Hermite矩阵与正定矩阵:从定义到不等式,核心要点精讲
本文深入解析Hermite矩阵与正定矩阵的核心概念与应用,涵盖从定义到不等式的关键知识点。通过实例代码和实际应用场景,如量子力学、信号处理和机器学习,展示这些矩阵在复空间中的对称特性和优化问题中的重要性。特别强调正定矩阵在凸优化和统计学中的基石作用,以及矩阵不等式在系统分析和量子信息中的应用。
深度解析:CAD Exchanger SDK 3.21.0 新特性如何重塑三维数据处理流程
本文深度解析CAD Exchanger SDK 3.21.0版本在三维数据处理流程中的革新特性,包括Drawing模式、智能网格简化和PMI导入等核心功能。这些升级显著提升了CAD数据转换效率和工程信息管理能力,特别适合处理大型CAD文件的工程师和开发团队。
已经到底了哦
精选内容
热门内容
最新内容
Windows游戏逆向实战:用VEH和硬件断点实现无痕Hook的完整C++类封装
本文深入探讨了Windows游戏逆向工程中基于VEH(向量化异常处理)和硬件断点的无痕Hook技术,提供了一套完整的C++类封装方案。通过详细的架构设计、线程安全管理和异常回调优化,开发者可以构建对抗现代反作弊系统的强大工具,实现零内存修改的函数拦截与监控。
分立式BUCK电路实战:从伏秒平衡到电感选型全解析
本文深入解析分立式BUCK电路的设计与实现,从伏秒平衡原理到电感选型全流程。详细介绍了BUCK电路的三种工作模式特性、电感参数工程计算方法及关键设计验证技巧,帮助工程师掌握高效DC-DC降压转换器的设计要点,特别适合高压差、大电流应用场景。
从HNU实验报告到实战:手把手教你用74LS00和74LS10搭建三人表决器(附完整电路图与避坑指南)
本文详细介绍了如何使用74LS00和74LS10芯片搭建三人表决器,从实验准备、电路设计到硬件实现和调试技巧,提供了完整的工程实践指南。通过与非门的逻辑转换和面包板布线,帮助读者掌握数字IC的基础应用,并附有常见问题排查和扩展应用方向,适合电子工程学习者和爱好者参考。
从LR寄存器到内存映射:手把手教你分析STM32 HardFault时的栈回溯与地址反查
本文详细解析了STM32发生HardFault异常时的调试方法,从LR寄存器分析到内存映射反查,手把手教你通过栈回溯和地址反查定位问题根源。文章涵盖了寄存器分析、内存堆栈检查、反汇编定位等实用技术,并提供了预防性编程规范和实时错误追踪系统等高级调试技巧,帮助开发者快速解决STM32 HardFault问题。
金蝶 Apusic 应用服务器任意文件上传漏洞深度剖析与自动化检测
本文深度剖析了金蝶Apusic应用服务器的任意文件上传漏洞(CVE-2022-XXXXX),详细解析了漏洞原理、攻击链构造及自动化检测方案。通过Python代码示例展示了恶意ZIP文件的构造技巧,并提供了优化后的检测脚本与误报规避策略,帮助企业有效识别和防御这一高危漏洞。
开源协作新选择:ONLYOFFICE深度集成与AI赋能实战
本文深入探讨了ONLYOFFICE作为开源协作工具的核心优势与实战应用。从API集成、企业级单点登录到AI插件赋能,详细解析了如何利用ONLYOFFICE提升文档处理效率,并提供了私有化部署方案与性能优化技巧,助力企业实现高效协作与智能化文档管理。
从零搭建Gazebo仿真平台:Livox Mid360与IMU融合驱动FAST-LIO2实战
本文详细介绍了如何在Gazebo仿真平台中从零搭建Livox Mid360激光雷达与IMU融合的传感器系统,并驱动FAST-LIO2算法进行建图定位。通过Xacro定义机器人模型、配置Gazebo环境、集成Livox Mid360和IMU传感器,最终实现与FAST-LIO2的无缝对接,为硬件缺货情况下的算法开发提供高效解决方案。
Spire.PDF for .NET 9.8.5 新特性与修复详解:从PDF文本比对到打印优化
本文详细解析了Spire.PDF for .NET 9.8.5版本的新特性与关键修复,重点介绍了PDF文本比对功能和打印优化。新版本通过改进文本比对算法和解决打印膨胀问题,显著提升了开发效率。同时修复了水印变化、特殊字符显示等关键bug,适用于法律、金融等行业的文档处理需求。
别再买示波器了!用Keil5软件仿真+STM32F103C8T6,5分钟搞定PWM波形调试
本文详细介绍了如何利用Keil5软件仿真和STM32F103C8T6开发板快速调试PWM波形,替代昂贵的示波器。通过逻辑分析仪功能,开发者可以在不连接硬件的情况下观测PWM波形,适用于电机控制、LED调光等低频应用。文章提供了完整的配置步骤、代码示例和实战技巧,帮助用户高效完成波形调试。
Android Automotive开发避坑指南:Car API连接CarService的5个关键细节与超时处理
本文深入解析Android Automotive开发中Car API连接CarService的底层机制,揭示五个关键陷阱并提供优化方案。涵盖双重重试机制、主线程阻塞风险、版本兼容性问题等核心挑战,帮助开发者提升连接稳定性和性能,适用于车载系统开发场景。