
简介行人实例分割数据集面向智能安防、自动驾驶感知、服务机器人交互及计算机视觉研究等场景提供精细化的行人轮廓标注样本适合从事目标检测与实例分割的开发者、算法工程师及高校研究人员使用。资源包共约2000个文件以1226个txt标注文件、772张jpg图片为主另含1个yaml配置与1份docx说明文档压缩包整体约96.18MB目录结构清晰便于直接接入训练流程。数据集划分为训练集1131张、验证集48张、测试集47张类别聚焦Person行人目标采用原生YOLO实例分割格式每个实例包含50余个多边形轮廓点可精确刻画复杂姿态与遮挡情况。样本覆盖监控视角、航拍视角等多角度并包含不同光照与天气条件标注经交叉校验保证一致性。该数据可直接对接YOLOv5、YOLOv8等主流框架支持实例分割、目标检测与姿态估计等多任务迁移也可作为行人重识别的前期处理与算法基准测试素材。目前已有277人学习下载。1. 行人实例分割数据集.zip从解压到喂进模型中间隔着多少坑拿到一个名为「行人实例分割数据集.zip」的压缩包很多人第一反应是解压、翻目录、找 images 和 labels然后直接开训。我见过太多人卡在这一步mask 是 PNG 还是 JSON类别 id 从 0 还是 1 开始标注格式是 COCO polygon 还是 YOLO segment这些问题不搞清楚后面训练 loss 不降、mask 全黑、评估 mAP 为零全是玄学。行人实例分割和普通目标检测最大的区别在于它要求模型对每个行人像素级区分——不只是框出来还要精确到轮廓。这个方向在智能安防、客流统计、自动驾驶行人避让里都是刚需。如果你手头正好有这样一个数据集压缩包或者打算自己标注一份下面这套从解压验收到训练跑通的路径是我踩过坑之后固定下来的流程。2. 先搞清楚压缩包里到底装了什么行人实例分割数据集的四种常见结构2.1 解压后先看目录树别急着写 dataloader拿到压缩包第一件事不是写代码是看目录。行人实例分割数据集常见的组织方式有四种COCO 风格annotations/instances_train.json images/、YOLO 风格images/ labels/ 每张图一个 txt、PNG mask 风格images/ masks/ 同名 PNG、以及混合风格json 里存 polygon同时给 mask 图。不同结构决定了你后面用哪个框架、写哪种 dataset 类。我一般会先跑一条命令把目录结构和文件数量摸清楚# 查看目录树限制深度避免输出爆炸 find ./pedestrian_seg_dataset -maxdepth 3 -type d | head -50 # 统计图片和标注文件数量 echo images: $(find ./pedestrian_seg_dataset -name *.jpg -o -name *.png | wc -l) echo json: $(find ./pedestrian_seg_dataset -name *.json | wc -l) echo txt: $(find ./pedestrian_seg_dataset -name *.txt | wc -l)逻辑说明find -maxdepth 3防止目录太深刷屏先看顶层结构。统计数量是为了判断标注文件和图片是否一一对应——如果 images 有 5000 张但 labels 只有 4800 个 txt说明有 200 张图漏标了这种脏数据不处理训练时 dataloader 会直接报错或者静默跳过后者更可怕。参数说明-name *.jpg -o -name *.png里的-o是 or 的意思因为行人数据集里两种格式混用很常见。如果你的数据集图片是.jpeg或.bmp把对应扩展名加进去。2.2 判断标注格式COCO polygon、YOLO segment 还是 PNG mask目录看完接下来要确认标注到底怎么存的。打开一个 json 文件看前几百个字符或者打开一个 txt 看内容。COCO 格式的 instances_train.json 里会有segmentation: [[x1,y1,x2,y2,...]]这样的 polygon 点列YOLO segment 格式的 txt 每行是class_id x1 y1 x2 y2 ...归一化到 0-1PNG mask 则是每张图对应一张黑白图白色区域是行人。import json # 查看 COCO json 的顶层结构和第一个标注 with open(./pedestrian_seg_dataset/annotations/instances_train.json, r) as f: data json.load(f) print(keys:, data.keys()) # 看有哪些顶层字段 print(categories:, data[categories]) # 类别定义重点看 id 和 name print(first ann:, data[annotations][0]) # 第一条标注看 segmentation 格式 print(image count:, len(data[images])) # 图片总数逻辑说明categories字段告诉你类别 id 和名称的映射关系。行人数据集通常只有一个类别person但 id 可能是 0 也可能是 1这个后面写 dataset 类时要对应上。annotations[0]里的segmentation如果是嵌套列表就是 polygon如果是 RLE 字典含counts字段就是掩码编码需要 pycocotools 解码。参数说明如果 json 文件很大几百 MB不要直接json.load全部读入用ijson流式读取或者只看前几条。我一般会先head -c 2000看文件开头确认结构后再决定怎么读。2.3 用一条脚本验证图片与标注的一一对应关系确认格式之后必须做一次完整性校验。行人实例分割数据集最常见的脏数据是图片存在但标注缺失、标注里的 image_id 在 images 列表里找不到、polygon 点数少于 3 个构不成多边形、坐标超出图片范围。这些问题不提前发现训练时要么报错中断要么模型学到错误信号。import json from pathlib import Path from PIL import Image ann_file ./pedestrian_seg_dataset/annotations/instances_train.json img_dir Path(./pedestrian_seg_dataset/images/train) with open(ann_file) as f: data json.load(f) # 建立 image_id 到文件名的映射 id2img {img[id]: img[file_name] for img in data[images]} img_ids_in_ann set(id2img.keys()) # 检查标注里的 image_id 是否都有对应图片 missing_img [] for img_id in img_ids_in_ann: fname id2img[img_id] if not (img_dir / fname).exists(): missing_img.append(fname) # 检查每张图的标注数量找出没有标注的图 from collections import Counter ann_count Counter(ann[image_id] for ann in data[annotations]) no_ann [id2img[i] for i in img_ids_in_ann if ann_count[i] 0] # 检查 polygon 点数 bad_poly [] for ann in data[annotations]: seg ann.get(segmentation, []) if isinstance(seg, list): for poly in seg: if len(poly) 6: # 至少 3 个点每个点 x,y bad_poly.append(ann[id]) break print(f标注中缺失图片: {len(missing_img)}) print(f无标注图片: {len(no_ann)}) print(f无效多边形: {len(bad_poly)})逻辑说明这段脚本做了三层校验。第一层检查标注里引用的图片文件是否真实存在第二层找出那些在 images 列表里但没有任何标注的图片可能是背景图也可能是漏标第三层检查 polygon 点数少于 3 个点的多边形在训练时会引发除零或面积计算错误。参数说明len(poly) 6是因为 polygon 是[x1,y1,x2,y2,x3,y3,...]的扁平列表3 个点最少 6 个数值。如果你的数据集用 RLE 编码这段要换成 pycocotools 的frPyObjects来验证。2.4 可视化抽查把 mask 叠回原图看对齐数值校验通过不代表标注质量没问题。我习惯随机抽 10 张图把 polygon 或 mask 叠回原图看一眼。行人实例分割最容易出的标注问题是mask 边缘粗糙、多人重叠区域归属混乱、遮挡行人只标了可见部分。这些在数值上完全合法但会直接影响模型学到的边界质量。import json import numpy as np import matplotlib.pyplot as plt from PIL import Image, ImageDraw with open(ann_file) as f: data json.load(f) # 随机抽 4 张有标注的图 import random random.seed(42) sample_ids random.sample(list(ann_count.keys()), 4) fig, axes plt.subplots(1, 4, figsize(20, 5)) for ax, img_id in zip(axes, sample_ids): fname id2img[img_id] img Image.open(img_dir / fname).convert(RGB) draw ImageDraw.Draw(img, RGBA) for ann in data[annotations]: if ann[image_id] ! img_id: continue for poly in ann[segmentation]: pts [(poly[i], poly[i1]) for i in range(0, len(poly), 2)] draw.polygon(pts, fill(255, 0, 0, 80), outline(255, 0, 0, 255)) ax.imshow(img) ax.set_title(fname) ax.axis(off) plt.tight_layout() plt.savefig(./check_masks.png, dpi100) print(saved to check_masks.png)逻辑说明用ImageDraw.polygon把标注多边形以半透明红色叠回原图肉眼判断 mask 是否贴合行人轮廓。如果发现某个行人的 mask 明显偏移或形状怪异说明标注有问题需要决定是修正还是丢弃。参数说明fill(255,0,0,80)里的 80 是 alpha 通道控制透明度太深会遮住原图细节太浅看不清。random.seed(42)保证每次抽同样的图方便对比。3. 把行人实例分割数据集转成模型能吃的格式COCO 转 YOLO segment 实操3.1 为什么我优先选 YOLO segment 格式做训练COCO 格式适合做 benchmark 和评估但实际训练时尤其是用 YOLOv8-seg 或 YOLOv11-seg 这类框架YOLO segment 格式的读取效率更高。COCO json 每次都要全量加载再按 image_id 索引数据量大时 dataloader 的 CPU 占用很高。YOLO segment 每张图一个 txt按需读取内存友好。而且 YOLO 官方训练脚本对 segment 格式的支持最直接不需要额外写 dataset 类。转换的核心逻辑是遍历 COCO 的 annotations按 image_id 分组把每个 polygon 的坐标归一化到 0-1写入对应的 txt 文件。同时要把图片按 train/val 划分复制到对应目录。3.2 COCO polygon 转 YOLO segment txt 的完整脚本import json import os import shutil from pathlib import Path from sklearn.model_selection import train_test_split # 配置路径 coco_json ./pedestrian_seg_dataset/annotations/instances_train.json img_dir Path(./pedestrian_seg_dataset/images/train) out_dir Path(./yolo_seg_dataset) val_ratio 0.2 with open(coco_json) as f: data json.load(f) # 建立映射 id2img {img[id]: img for img in data[images]} id2anns {} for ann in data[annotations]: id2anns.setdefault(ann[image_id], []).append(ann) # 类别映射COCO 的 category_id 转 YOLO 的 0-based cat_ids sorted([c[id] for c in data[categories]]) cat2yolo {cid: i for i, cid in enumerate(cat_ids)} # 划分 train/val all_ids list(id2img.keys()) train_ids, val_ids train_test_split(all_ids, test_sizeval_ratio, random_state42) def convert(split_ids, split_name): img_out out_dir / images / split_name lbl_out out_dir / labels / split_name img_out.mkdir(parentsTrue, exist_okTrue) lbl_out.mkdir(parentsTrue, exist_okTrue) for img_id in split_ids: info id2img[img_id] fname info[file_name] w, h info[width], info[height] # 复制图片 src img_dir / fname dst img_out / fname if src.exists(): shutil.copy2(src, dst) # 写 label lines [] for ann in id2anns.get(img_id, []): cls cat2yolo[ann[category_id]] for poly in ann[segmentation]: # 归一化坐标 norm [] for i in range(0, len(poly), 2): x max(0, min(poly[i] / w, 1.0)) y max(0, min(poly[i1] / h, 1.0)) norm.extend([x, y]) # YOLO segment 格式: class x1 y1 x2 y2 ... line f{cls} .join(f{v:.6f} for v in norm) lines.append(line) lbl_file lbl_out / (Path(fname).stem .txt) lbl_file.write_text(\n.join(lines)) convert(train_ids, train) convert(val_ids, val) print(ftrain: {len(train_ids)}, val: {len(val_ids)})逻辑说明cat2yolo把 COCO 的 category_id 映射成从 0 开始的连续整数这是 YOLO 的硬性要求。坐标归一化时用max(0, min(x/w, 1.0))做截断防止个别标注坐标超出图片边界导致 YOLO 报错。每个 polygon 写成一行格式是class_id x1 y1 x2 y2 ...坐标保留 6 位小数足够精度。参数说明val_ratio 0.2是验证集比例行人数据集如果总量少于 2000 张建议调到 0.15 或 0.1保证训练数据充足。random_state42固定随机种子方便复现。shutil.copy2保留文件元数据比copyfile更稳妥。3.3 生成 data.yaml 并检查类别映射YOLO 训练需要一个 data.yaml 指定路径和类别。行人实例分割通常只有一个类别但也要写清楚。# data.yaml path: ./yolo_seg_dataset train: images/train val: images/val names: 0: person逻辑说明path是数据集根目录train和val是相对路径。names字典的 key 必须从 0 开始连续和 txt 里的 class_id 对应。如果转换脚本里cat2yolo映射正确这里就写0: person。参数说明如果数据集里除了 person 还有 rider、pedestrian 等细分标签names 要按映射顺序全部列出。我一般会在转换后跑一条命令抽查几个 txt 文件确认 class_id 和坐标范围。# 抽查前 3 个 label 文件 for f in $(ls ./yolo_seg_dataset/labels/train/*.txt | head -3); do echo $f head -2 $f done3.4 用 YOLOv8-seg 跑通第一个 epoch格式转好之后先别急着调参用默认配置跑一个 epoch 确认整条链路通畅。yolo segment train \ data./data.yaml \ modelyolov8n-seg.pt \ epochs1 \ imgsz640 \ batch8 \ device0 \ project./runs \ namepedestrian_seg_test逻辑说明modelyolov8n-seg.pt用 nano 版本速度快适合验证流程。epochs1只跑一轮看 loss 是否正常下降、有没有报错。imgsz640是默认输入尺寸行人数据集如果图片分辨率很高比如 1920x1080可以先用 640 跑通再考虑放大。参数说明batch8根据显存调整8GB 显存跑 640 尺寸的 segment 模型batch 8 比较安全。device0指定第一块 GPUCPU 训练把 device 改成cpu。project和name控制输出目录方便区分不同实验。跑通之后看输出日志里的seg_loss、box_loss、cls_loss是否在下降。如果 seg_loss 一直是 nan 或 0大概率是 label 格式有问题回到 3.2 检查 txt 内容。4. 行人实例分割训练避坑从 mask 全黑到类别不均衡的排查清单4.1 坑一训练 loss 正常但预测 mask 全黑现象训练日志里 seg_loss 在降但推理时可视化结果没有任何 mask 输出或者 mask 全是黑色。原因最常见的是类别 id 不匹配。YOLO 的 txt 里 class_id 从 0 开始但 data.yaml 里 names 的 key 如果写成1: person模型学到的类别和评估时对不上。另一个原因是 polygon 点序错误——COCO 的 polygon 是顺时针或逆时针有序的如果转换时打乱了点序多边形自交YOLO 的 mask 生成会失败。解决先检查 data.yaml 的 names 是否从 0 开始。再用yolo segment predict单独跑一张训练集图片看输出。如果训练集都预测不出 mask说明格式转换有问题。我一般会写一个小脚本把 YOLO txt 重新画回图片上肉眼确认 polygon 是否正确。import cv2 import numpy as np img cv2.imread(./yolo_seg_dataset/images/train/000001.jpg) h, w img.shape[:2] with open(./yolo_seg_dataset/labels/train/000001.txt) as f: for line in f: parts line.strip().split() cls int(parts[0]) coords list(map(float, parts[1:])) pts np.array([[coords[i]*w, coords[i1]*h] for i in range(0, len(coords), 2)], np.int32) cv2.polylines(img, [pts], True, (0, 0, 255), 2) cv2.imwrite(./check_yolo_label.jpg, img)逻辑说明把归一化坐标还原成像素坐标用cv2.polylines画多边形轮廓。如果画出来的形状和行人轮廓一致说明转换正确如果形状乱飞说明点序或归一化有问题。参数说明True表示闭合多边形(0,0,255)是红色线宽 2 像素。4.2 坑二小目标行人 mask 质量差AP 很低现象大尺寸行人的 mask 还行但远处小行人的 mask 几乎不可用评估时 AP_small 接近零。原因行人数据集里小目标占比很高尤其是监控场景。YOLO segment 默认的 mask 分支分辨率有限小目标经过多次下采样后信息丢失严重。另外如果标注时小行人的 polygon 本身就粗糙模型学不到精细边界。解决把imgsz从 640 提到 1024 或 1280让小目标在输入中占更多像素。同时检查标注质量把面积小于 32x32 像素的行人标注单独拿出来看如果 polygon 明显不准考虑丢弃或重新标注。YOLOv8-seg 支持overlap_mask和mask_ratio参数mask_ratio4是默认值调到 2 可以提高 mask 分辨率但显存占用会增加。yolo segment train \ data./data.yaml \ modelyolov8s-seg.pt \ epochs100 \ imgsz1024 \ batch4 \ mask_ratio2 \ device0参数说明imgsz1024比 640 需要更多显存batch 要相应减小。mask_ratio2让 mask 分支的输出分辨率翻倍对小目标更友好但训练速度会下降约 30%。4.3 坑三多人重叠区域 mask 归属混乱现象两个行人挨得很近或重叠时预测的 mask 把两个人连成一片或者边界互相侵入。原因实例分割模型在重叠区域依赖 NMS 和 mask 分支的区分能力。如果训练数据里重叠行人的标注边界不清晰比如一个人遮挡另一个人标注时把遮挡部分也标给了前面的人模型学到的边界就是模糊的。解决检查标注规范遮挡行人的 mask 应该只包含可见部分还是包含完整轮廓这个要统一。如果数据集里两种标法混用模型会困惑。我一般会统计一下重叠区域面积占比如果超过 30% 的标注存在重叠建议在训练时开启copy_paste数据增强让模型见到更多重叠场景。# 统计标注中的重叠情况 import json import numpy as np from shapely.geometry import Polygon with open(coco_json) as f: data json.load(f) id2anns {} for ann in data[annotations]: id2anns.setdefault(ann[image_id], []).append(ann) overlap_count 0 for img_id, anns in id2anns.items(): if len(anns) 2: continue polys [] for ann in anns: for seg in ann[segmentation]: pts [(seg[i], seg[i1]) for i in range(0, len(seg), 2)] if len(pts) 3: polys.append(Polygon(pts)) for i in range(len(polys)): for j in range(i1, len(polys)): if polys[i].intersects(polys[j]): inter polys[i].intersection(polys[j]).area if inter 0.1 * min(polys[i].area, polys[j].area): overlap_count 1 break print(f存在显著重叠的图片数: {overlap_count})逻辑说明用 shapely 计算多边形之间的交集面积如果交集超过较小多边形面积的 10%认为存在显著重叠。这个统计帮你判断数据集的难度分布。参数说明0.1是重叠阈值可以根据实际情况调整。shapely 需要单独安装pip install shapely。4.4 坑四类别不均衡导致模型偏向大目标现象数据集中大部分是正常行走的行人少量是骑车人、蹲坐行人等特殊姿态模型对特殊姿态的 mask 预测很差。原因实例分割的 loss 对每个实例平均如果特殊姿态样本太少梯度被大量正常样本淹没。解决在 data.yaml 里给不同类别设置不同的cls权重或者在训练时用fraction参数控制采样比例。更直接的办法是过采样稀有类别——把包含稀有姿态的图片复制多份或者在 dataset 类里做 weighted sampling。YOLO 本身不直接支持类别权重但可以通过复制图片实现。# 统计每个类别的实例数量 python -c import json from collections import Counter with open($coco_json) as f: data json.load(f) cat_names {c[id]: c[name] for c in data[categories]} cnt Counter(ann[category_id] for ann in data[annotations]) for cid, n in cnt.most_common(): print(f{cat_names[cid]}: {n}) 逻辑说明快速统计各类别实例数如果某个类别占比低于 5%就要考虑过采样或调整 loss 权重。参数说明most_common()按数量降序排列方便一眼看出长尾分布。4.5 坑五验证集 mask 评估指标异常低但训练 loss 正常现象训练 loss 稳步下降但验证时 mask mAP 一直在 0.1 以下或者波动极大。原因验证集的标注格式和训练集不一致。常见情况是训练集用 YOLO segment txt验证集还是 COCO json或者验证集的图片没有对应的 label 文件。另一个原因是验证集里图片尺寸和训练集差异大模型泛化不过来。解决确认 train 和 val 的 label 格式完全一致。用yolo segment val单独跑验证看输出里每个类别的指标。如果某个类别 AP 为 0说明该类在验证集里没有正确标注。我一般会在转换脚本里对 train 和 val 用同一套逻辑避免人为不一致。yolo segment val \ model./runs/segment/pedestrian_seg_test/weights/best.pt \ data./data.yaml \ imgsz640 \ batch8参数说明model指向训练好的权重data和训练时用同一个 yaml。如果 val 的 mAP 和 train 的 loss 趋势严重不符优先检查 val 的 label 目录是否为空或格式错误。5. 行人实例分割的进阶技巧用 SAHI 切片推理提升小目标 mask 召回5.1 为什么默认推理会漏掉远处行人YOLO segment 在推理时把整张图缩放到imgsz尺寸如果原图是 4K 监控画面远处行人可能只有十几个像素缩放后几乎消失。这在行人实例分割里是致命问题——漏掉的小目标在客流统计里就是漏计。SAHISlicing Aided Hyper Inference的思路是把大图切成重叠的小块每块单独推理再把结果合并。这样小目标在切片里占的像素比例大幅提升召回率能提高 10-20 个点。5.2 SAHI 切片推理的代码实现from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction import cv2 # 加载 YOLOv8-seg 模型 detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_path./runs/segment/pedestrian_seg_test/weights/best.pt, confidence_threshold0.3, devicecuda:0 ) # 切片推理 result get_sliced_prediction( ./test_4k.jpg, detection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2, perform_standard_predTrue # 同时跑全图推理保留大目标 ) # 导出可视化结果 result.export_visuals(export_dir./sahi_output/) print(f检测到 {len(result.object_prediction_list)} 个行人实例)逻辑说明slice_height和slice_width控制切片大小一般设为训练时的imgsz。overlap_height_ratio0.2表示相邻切片有 20% 重叠防止目标被切在边界上。perform_standard_predTrue会同时跑一次全图推理避免切片推理漏掉超大目标。最后export_visuals输出带 mask 的可视化图。参数说明confidence_threshold0.3比训练时的默认值低因为切片推理会产生更多候选框需要靠 NMS 合并。如果发现误检多可以调到 0.4 或 0.5。overlap比例太小会导致边界目标被切碎太大则推理速度下降0.2 是经验值。5.3 切片推理后的 mask 合并与去重SAHI 内部用 NMS 做去重但实例分割的 mask 合并比检测框复杂。如果两个切片都预测了同一个行人mask 会有重叠区域。SAHI 默认保留置信度高的那个但在行人密集场景我一般会手动做一次 mask IoU 去重。import numpy as np from sahi.postprocess.combine import nms # 提取所有预测的 mask 和分数 masks [] scores [] for pred in result.object_prediction_list: mask pred.mask.bool_mask # 二值 mask masks.append(mask) scores.append(pred.score.value) # 用 mask IoU 做去重 def mask_iou(m1, m2): inter np.logical_and(m1, m2).sum() union np.logical_or(m1, m2).sum() return inter / union if union 0 else 0 keep [] for i in range(len(masks)): if scores[i] 0.3: continue suppressed False for j in keep: if mask_iou(masks[i], masks[j]) 0.5: suppressed True break if not suppressed: keep.append(i) print(f去重后保留 {len(keep)} 个实例)逻辑说明mask_iou计算两个二值 mask 的交并比超过 0.5 认为重复。keep列表按分数从高到低遍历保留高分实例抑制低分重叠实例。参数说明0.5是 IoU 阈值行人重叠场景可以调到 0.6 避免误删。scores[i] 0.3过滤低置信度预测减少噪声。5.4 一个我固定下来的验证习惯每次训练完我不会只看 mAP 数字。我会挑三类图做可视化密集人群图、远处小目标图、遮挡严重图。每类抽 5 张用 SAHI 切片推理跑一遍把结果和原图并排保存。如果密集人群的 mask 粘连严重说明 NMS 阈值需要调如果远处小目标漏检多说明切片尺寸或 overlap 不够如果遮挡行人的 mask 形状怪异说明训练数据里遮挡样本太少。这个习惯帮我省了很多「指标好看但实际不能用」的后悔药。希望帮到你。本文还有配套的精品资源点击获取