
简介本资源是一份开箱即用的行人目标检测专用数据集严格遵循YOLOv5目录结构规范面向计算机视觉初学者、算法工程师及模型训练实践者解决小规模场景下人形目标检测模型快速验证与微调的数据需求。压缩包共2000个文件主体为1999个YOLO格式标注txt文件含训练集3000张图片对应标签、测试集300张图片对应标签及1个可视化脚本show.py所有文件按images/train、labels/train等标准路径组织无需额外转换即可直接接入YOLOv5训练流程配套的classes.txt字典与可视化脚本支持即时效果验证运行后自动绘制边界框并保存结果图。资源大小523.04MB结构清晰、标注准确、环境覆盖多样已获356人学习下载是开展行人检测baseline实验、模型性能对比或课程实训的理想数据基础。1. 行人检测不是调个参数就能跑通YOLOv5目录格式数据集为什么卡住90%新手的训练第一步你手上有几张行人照片想用YOLOv5跑出一个能框出人的模型——结果train.py报错FileNotFoundError: No labels found in .../labels/train/或者训练完 mAP 始终为 0或者验证时满屏乱框。这不是代码写错了而是数据集结构没对齐 YOLOv5 的硬性契约。YOLOv5 不接受任意命名的图片txt文件夹它只认一种目录骨架images/train/、images/val/、labels/train/、labels/val/四个路径必须存在且每张xxx.jpg必须在同名xxx.txt中有严格格式的归一化坐标中心点x,y 宽高w,h全部0~1之间类别索引从0开始。这不是“建议”是Dataset类里__getitem__方法直接os.path.join(self.img_path, self.label_path)拼路径、torch.load()读取前校验文件存在的刚性逻辑。很多新手卡在第2小时不是不会写训练命令而是根本没意识到YOLOv5 的“数据集”本质是一个带强约束的文件系统协议而非通用图像集合。本文不讲YOLOv5原理只聚焦一件事如何把原始行人图片哪怕只有20张零误差地塞进这个协议让train.py第一次运行就成功加载、不报路径错、不丢样本、不混标签。适合刚下载完 CrowdHuman 或自己手机拍了10张路人的你也适合被--data mydata.yaml卡住三天、反复重命名文件夹的工程师。2. 从原始图片到YOLOv5可识别结构四步落地流程与每个环节的致命细节2.1 确认原始数据形态先别急着改名先看懂你的“原料”YOLOv5 对数据源不挑食但必须知道它是什么形态。常见行人数据源有三类自有拍摄图手机/监控截图无标注需先用 LabelImg 或 CVAT 标注生成.txt公开数据集如 CrowdHuman、CityPersons已提供标注但格式多为 COCO JSON、XML 或自定义 TXT绝非 YOLOv5 原生格式半成品数据集如某公司内部导出的 CSV图片坐标可能是像素值、未归一化、类别名非数字。提示用ls -l images/ | head -5和head -3 labels/000001.txt快速判断。若labels/下是.xml或.json跳转 2.3若是空文件夹或报错No such file说明标注根本没生成回退到标注工具环节。2.2 构建 YOLOv5 目录骨架4个文件夹的创建逻辑与权限陷阱YOLOv5 要求的最小结构如下注意大小写和斜杠方向my_person_dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/关键细节images/和labels/是同级兄弟目录不是images/labels/train/和val/必须是子目录不能是train.jpg和val.jpg这样的文件Linux/macOS 下区分大小写Images/≠images/Windows 虽不敏感但ultralytics代码中Path(img_path).glob(*.jpg)默认小写匹配建议统一小写若用mkdir -p images/train labels/val创建检查权限ls -ld images/应显示drwxr-xr-x若为drw-------无执行权os.listdir()会报PermissionError用chmod 755 images/修复。# 一行创建标准骨架Linux/macOS mkdir -p my_person_dataset/{images/{train,val},labels/{train,val}} # 验证结构 tree my_person_dataset -L 2 # 输出应为 # my_person_dataset # ├── images # │ ├── train # │ └── val # └── labels # ├── train # └── val2.3 标注格式转换从 COCO JSON/XML 到 YOLOv5 TXT 的不可省略步骤CrowdHuman、CityPersons 等主流行人数据集提供 COCO 格式 JSON。其标注含categories类别列表、annotations每个 bbox 的image_id,category_id,bbox[x,y,w,h]。YOLOv5 要求每个图片对应一个.txt每行class_id center_x center_y width height全部归一化到 0~1。转换核心逻辑读取 JSON提取images列表含file_name,width,height遍历annotations用image_id匹配图片尺寸将bbox从[x_top_left, y_top_left, w, h]→ 转为中心点坐标x_center (x_top_left w/2) / img_width y_center (y_top_left h/2) / img_height w_norm w / img_width h_norm h / img_height写入labels/train/xxx.txt每行0 {x_center} {y_center} {w_norm} {h_norm}行人统一为 class 0。# coco2yolo.py将 CrowdHuman 的 annotation.json 转为 YOLOv5 格式 import json import os from pathlib import Path def convert_coco_to_yolo(json_path, img_dir, label_dir): with open(json_path) as f: data json.load(f) # 构建 image_id - info 映射 img_info {img[id]: img for img in data[images]} # 创建 label_dir 下所有图片的空 .txt for img in data[images]: txt_path Path(label_dir) / f{Path(img[file_name]).stem}.txt txt_path.parent.mkdir(exist_okTrue) txt_path.write_text() # 预创建避免后续写入失败 # 写入标注 for ann in data[annotations]: img_id ann[image_id] img img_info[img_id] w, h img[width], img[height] # COCO bbox: [x, y, width, height] x, y, box_w, box_h ann[bbox] # 归一化 x_center (x box_w / 2) / w y_center (y box_h / 2) / h w_norm box_w / w h_norm box_h / h # 行人类别固定为 0 line f0 {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n txt_path Path(label_dir) / f{Path(img[file_name]).stem}.txt with open(txt_path, a) as f: f.write(line) # 使用示例 convert_coco_to_yolo( json_pathcrowdhuman/annotation.json, img_dircrowdhuman/Images, label_dirmy_person_dataset/labels/train )逻辑说明Path(img[file_name]).stem去掉.jpg/.png后缀确保图片000001.jpg对应000001.txt.6f保证浮点精度避免0.000000导致解析失败a模式追加写入因一张图可能有多个行人多个ann。2.4 图片与标签的严格配对文件名一致性检查与自动清洗脚本YOLOv5 在datasets.py中通过self.img_files sorted(glob.glob(...))获取图片列表再用label_path img_path.replace(images, labels).replace(.jpg, .txt)推导标签路径。这意味着images/train/a.jpg→labels/train/a.txt若images/train/a.jpeg存在但labels/train/a.txt不存在该图被静默跳过若labels/train/b.txt存在但images/train/b.jpg不存在训练时IndexError: list index out of range因self.img_files长度 self.label_files。以下脚本自动清洗不配对文件# sync_images_labels.py import os from pathlib import Path def sync_dataset(img_dir, label_dir, exts(.jpg, .jpeg, .png)): img_dir Path(img_dir) label_dir Path(label_dir) # 获取所有图片文件名不含扩展名 img_stems set() for ext in exts: img_stems.update(p.stem for p in img_dir.rglob(f*{ext})) # 获取所有标签文件名不含扩展名 label_stems set(p.stem for p in label_dir.rglob(*.txt)) # 找出只在图片中存在、标签缺失的 missing_labels img_stems - label_stems if missing_labels: print(f[WARNING] {len(missing_labels)} images missing labels:) for stem in sorted(missing_labels)[:5]: # 只打印前5个 print(f {stem}) # 自动创建空 .txtYOLOv5 允许无目标图片但必须有空文件 for stem in missing_labels: (label_dir / f{stem}.txt).write_text() # 找出只在标签中存在、图片缺失的 missing_imgs label_stems - img_stems if missing_imgs: print(f[WARNING] {len(missing_imgs)} labels missing images:) for stem in sorted(missing_imgs)[:5]: print(f {stem}) # 删除孤立标签 for stem in missing_imgs: (label_dir / f{stem}.txt).unlink() # 使用示例 sync_dataset( img_dirmy_person_dataset/images/train, label_dirmy_person_dataset/labels/train )参数说明exts指定支持的图片格式避免.JPG大写导致匹配失败rglob递归查找兼容子文件夹write_text()创建空文件比touch更跨平台unlink()安全删除比os.remove更鲁棒。3. 配置文件 yaml为什么 80% 的训练失败源于 data.yaml 的三个隐藏字段3.1 data.yaml 的最小必要字段路径、类别数、类别名缺一不可YOLOv5 通过--data my_person_dataset/data.yaml加载配置。该文件不是可选的而是数据集的元数据身份证。最简data.yaml必须包含train: 训练图片路径相对于data.yaml文件位置val: 验证图片路径nc: 类别数量行人检测为1names: 类别名称列表顺序与nc对应[person]。错误示范常见翻车点train: ./images/train→ 正确train: images/train→ 缺少./YOLOv5 解析为绝对路径/images/trainnc: 1→ 正确nc: 1→ 字符串int(nc)报错names: [person]→ 缺少引号YAML 解析为变量而非字符串# my_person_dataset/data.yaml train: ../images/train # 注意此文件放在 my_person_dataset/ 下所以用 ../ 回退一级 val: ../images/val nc: 1 names: [person]逻辑说明train和val是相对于data.yaml所在目录的路径。若data.yaml在my_person_dataset/而images/也在同级则写../images/train若data.yaml放在my_person_dataset/内images/是其子目录则写images/train。用ls -l my_person_dataset/data.yaml确认位置。3.2 路径调试技巧用 Python 一行验证 data.yaml 是否可被正确解析在运行train.py前先用以下代码验证路径是否真实存在且可读# test_yaml.py import yaml from pathlib import Path def test_data_yaml(yaml_path): with open(yaml_path) as f: data yaml.safe_load(f) for split in [train, val]: path Path(data[split]) if not path.exists(): print(f[ERROR] {split} path not found: {path.absolute()}) return False if not path.is_dir(): print(f[ERROR] {split} path is not a directory: {path.absolute()}) return False # 检查是否有图片 imgs list(path.rglob(*.jpg)) list(path.rglob(*.png)) if len(imgs) 0: print(f[ERROR] {split} directory is empty: {path.absolute()}) return False print(f[OK] {split}: {len(imgs)} images found) print(f[OK] nc{data[nc]}, names{data[names]}) return True # 使用 test_data_yaml(my_person_dataset/data.yaml)运行后输出OK才代表data.yaml无路径问题。若报path not found说明train:路径写错若报directory is empty说明图片没复制到正确位置或扩展名不匹配。3.3 高级字段test和download的实际价值与弃用建议YOLOv5 官方data.yaml模板含test:和download:字段test:用于测试集路径但train.py默认不使用需手动修改val为test或在val.py中指定download:是自动下载脚本 URL对自定义数据集无意义且易触发网络超时中断训练。实战建议删除test:和download:字段。它们不参与训练流程反而增加 YAML 解析复杂度。ultralytics代码中data.get(test, data[val])会 fallback 到val无需显式定义。4. 避坑指南YOLOv5 数据集准备阶段的5个血泪经验4.1 现象训练启动后立即报错OSError: image file is truncated原因图片文件损坏如传输中断、SD卡故障YOLOv5 用cv2.imread()读取时返回None后续img.shape报错。解决批量检查图片完整性# Linux/macOS用 identifyImageMagick检查 find my_person_dataset/images -name *.jpg -exec identify {} \; 2/dev/null | grep -v JPEG # 或用 Python 脚本 from PIL import Image for img_path in Path(my_person_dataset/images).rglob(*.jpg): try: Image.open(img_path).verify() except Exception as e: print(fCorrupted: {img_path} - {e}) img_path.unlink() # 删除损坏文件4.2 现象训练时mAP0.5始终为 0.000loss 不下降原因标签文件中坐标超出 [0,1] 范围如x_center1.05YOLOv5 在general.py的xywhn2xyxy()中 clip 到 [0,1]导致 bbox 被压扁为线段。解决检查labels/下随机.txt文件用以下脚本扫描越界值for txt_path in Path(my_person_dataset/labels).rglob(*.txt): with open(txt_path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) 5: continue try: x, y, w, h map(float, parts[1:5]) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): print(fOut of range in {txt_path}:{i1} - x{x:.3f} y{y:.3f} w{w:.3f} h{h:.3f}) except ValueError: pass4.3 现象train.py报错AssertionError: train: No labels found原因labels/train/下存在空文件0字节YOLOv5 的LoadImagesAndLabels.__init__()中len(labels) 0触发断言。解决删除所有空.txtfind my_person_dataset/labels -name *.txt -size 0c -delete4.4 现象验证时出现ZeroDivisionError: division by zero原因val/目录下图片数极少如仅1张YOLOv5 计算 mAP 时分母为0。解决确保val/至少有 50 张图片建议占总数 10~20%。用wc -l my_person_dataset/images/val/*.jpg | tail -1统计。4.5 现象训练日志显示2000 images, 2000 labels但val阶段2000 images, 0 labels原因labels/val/下文件名与images/val/不一致如IMG_001.jpgvsimg_001.txt大小写或下划线差异。解决统一文件名# Linux批量小写重命名 for f in my_person_dataset/images/val/*.jpg; do mv $f $(dirname $f)/$(basename $f | tr [:upper:] [:lower:]); done for f in my_person_dataset/labels/val/*.txt; do mv $f $(dirname $f)/$(basename $f | tr [:upper:] [:lower:]); done5. 验证与可视化用 detect.py 和 utils.plots 看懂你的数据集是否真正就绪5.1 用 detect.py 做端到端数据流验证不训练只跑推理链detect.py可加载训练好的权重如yolov5s.pt对images/val/进行推理这是检验数据集是否“活”的最快方法python detect.py \ --weights yolov5s.pt \ --source my_person_dataset/images/val \ --data my_person_dataset/data.yaml \ --conf 0.25 \ --save-txt \ --save-conf--save-txt: 在runs/detect/exp/labels/生成预测.txt格式与训练标签一致--save-conf: 在runs/detect/exp/labels/的.txt中保留置信度便于分析漏检/误检若输出0/20000张图处理说明--source路径无图片若报No labels found说明--data指向错误。关键观察点runs/detect/exp/下是否生成图片labels/下是否生成.txt打开一个.txt看内容是否为0 0.5 0.5 0.2 0.3 0.95class x y w h conf若有证明数据集路径、格式、读取全链路通畅。5.2 可视化标签真值用 plot_one_box 直接画出 labels/train/ 中的标注YOLOv5 自带utils.plots模块可将labels/train/xxx.txt叠加到images/train/xxx.jpg上# visualize_labels.py import cv2 from pathlib import Path from utils.plots import plot_one_box def visualize_labels(img_dir, label_dir, save_dir, conf_thres0.0): save_dir Path(save_dir) save_dir.mkdir(exist_okTrue) for img_path in Path(img_dir).rglob(*.jpg): label_path Path(label_dir) / f{img_path.stem}.txt if not label_path.exists(): continue img cv2.imread(str(img_path)) h, w img.shape[:2] # 读取标签 with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls, x, y, w_norm, h_norm map(float, parts[:5]) # 归一化坐标转像素 x1 int((x - w_norm/2) * w) y1 int((y - h_norm/2) * h) x2 int((x w_norm/2) * w) y2 int((y h_norm/2) * h) plot_one_box([x1, y1, x2, y2], img, labelfperson {cls:.0f}, color(0,255,0), line_thickness2) cv2.imwrite(str(save_dir / fvis_{img_path.name}), img) visualize_labels( img_dirmy_person_dataset/images/train, label_dirmy_person_dataset/labels/train, save_dirmy_person_dataset/visualize_train )运行后my_person_dataset/visualize_train/下生成带绿色框的图片。这是你数据集的“X光片”若框完全偏离行人如框在天空或地面说明标注坐标计算错误若框严重变形宽高比失真说明归一化时w/h用反了。5.3 统计分析用 pandas 透视数据集分布发现隐性偏差行人检测常因姿态、遮挡、尺度不均导致性能瓶颈。用以下脚本生成统计报告# analyze_dataset.py import pandas as pd import numpy as np from pathlib import Path def analyze_labels(label_dir): records [] for txt_path in Path(label_dir).rglob(*.txt): with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls, x, y, w, h map(float, parts[:5]) # 计算面积归一化面积 area w * h # 计算长宽比 ratio w / h if h 0 else 0 records.append({ file: txt_path.stem, class: int(cls), area: area, ratio: ratio, x: x, y: y }) df pd.DataFrame(records) print( Dataset Statistics ) print(fTotal boxes: {len(df)}) print(fAverage area: {df[area].mean():.4f} (range: {df[area].min():.4f}-{df[area].max():.4f})) print(fAspect ratio median: {df[ratio].median():.3f} (range: {df[ratio].min():.3f}-{df[ratio].max():.3f})) print(fCoordinate distribution:\n{df[[x,y]].describe()}) # 检测极端值 small_boxes df[df[area] 0.001] if len(small_boxes) 0: print(f[WARNING] {len(small_boxes)} boxes with area 0.001 (likely noise or annotation error)) return df df analyze_labels(my_person_dataset/labels/train)输出示例 Dataset Statistics Total boxes: 12480 Average area: 0.0245 (range: 0.0001-0.3210) Aspect ratio median: 0.421 (range: 0.052-2.103) Coordinate distribution: x y count 12480.0 12480.0 mean 0.512 0.498 ...这告诉你若area平均值 0.005说明大量小目标需开启mosaic或multi-scale若x均值远离 0.5说明行人总在画面一侧需数据增强中的random_perspective。6. 进阶技巧用自定义 DataLoader 替换默认逻辑绕过 YOLOv5 的路径硬编码6.1 为什么需要自定义 DataLoader当你的数据不在标准路径时YOLOv5 默认假设images/和labels/同级但实际场景中数据分散在不同磁盘/data1/images/,/data2/labels/图片是视频帧按video_id/frame_id.jpg组织无法简单用stem匹配标签是数据库查询结果需实时生成不存为.txt。此时硬改datasets.py风险高升级后覆盖更安全的方式是继承torch.utils.data.Dataset重写__getitem__。6.2 构建 PersonDataset支持任意路径、动态标签、多尺度采样# person_dataset.py import torch from torch.utils.data import Dataset from PIL import Image import numpy as np import cv2 from pathlib import Path class PersonDataset(Dataset): def __init__(self, img_paths, label_func, img_size640, augmentFalse): :param img_paths: List[str], 所有图片路径 :param label_func: Callable[[str], List[List[float]]], 输入图片路径返回 [[cls,x,y,w,h], ...] :param img_size: int, 输入尺寸 :param augment: bool, 是否启用增强 self.img_paths img_paths self.label_func label_func self.img_size img_size self.augment augment def __len__(self): return len(self.img_paths) def __getitem__(self, index): # 读图 img_path self.img_paths[index] img cv2.imread(str(img_path)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 获取标签 labels self.label_func(img_path) # [[0,0.5,0.5,0.2,0.3], ...] labels np.array(labels, dtypenp.float32) if labels else np.zeros((0,5), dtypenp.float32) # 增强简化版实际用 albumentations if self.augment: # 随机缩放、裁剪、HSV增强 pass # 归一化到 0~1 img img.astype(np.float32) / 255.0 # 调整尺寸 img cv2.resize(img, (self.img_size, self.img_size)) img torch.from_numpy(img).permute(2,0,1) # HWC - CHW return img, torch.from_numpy(labels) # 使用示例从数据库动态获取标签 def db_label_func(img_path): # 伪代码根据 img_path 查询 PostgreSQL # SELECT class_id, x_center, y_center, w, h # FROM annotations WHERE image_path %s; return [[0, 0.5, 0.5, 0.2, 0.3]] # 返回示例 # 构建数据集 img_list list(Path(/data1/person_images).rglob(*.jpg)) dataset PersonDataset( img_pathsimg_list, label_funcdb_label_func, img_size640, augmentTrue ) # 直接用于 DataLoader dataloader torch.utils.data.DataLoader(dataset, batch_size16, shuffleTrue)优势完全脱离images//labels/目录约束label_func可对接数据库、API、甚至大模型生成标注__getitem__中可插入任意预处理逻辑如去雾、超分。6.3 集成到 YOLOv5 训练流程替换 train.py 中的数据加载部分YOLOv5 的train.py中数据加载由create_dataloader()函数控制。找到该函数调用处约第350行将其替换为# 在 train.py 开头添加 from person_dataset import PersonDataset # 替换原 create_dataloader(...) 调用 # 原代码 # train_loader create_dataloader(train_path, ...)[0] # 新代码 img_list list(Path(opt.data).parent / images / train).rglob(*.jpg) # 或你的路径 dataset PersonDataset( img_pathslist(img_list), label_funclambda p: load_yolo_labels(p), # 你的标签加载函数 img_sizeopt.imgsz, augmentTrue ) train_loader torch.utils.data.DataLoader( dataset, batch_sizeopt.batch_size, shuffleTrue, num_workersopt.workers, collate_fnlambda x: tuple(zip(*x)) # 保持 img, labels 分离 )这样你既复用了 YOLOv5 的训练引擎损失计算、优化器、日志又彻底解耦了数据加载逻辑。我做行人检测项目时曾因 CrowdHuman 的ignore区域遮挡行人未过滤导致 mAP 虚高 5%后来在label_func中加入if ann[iscrowd] 0:过滤才回归真实。数据集不是静态文件夹而是你对现实世界的建模入口——它的结构、分布、噪声直接决定模型的天花板。希望帮到你。本文还有配套的精品资源点击获取