ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

成人小孩检测数据集:COCO JSON转YOLO训练全流程解析

成人小孩检测数据集:COCO JSON转YOLO训练全流程解析 简介面向计算机视觉与深度学习目标检测开发者这份成人与小孩识别数据集提供1738张原始JPG图像并采用COCO JSON格式完成标注专门用于区分图片中的成人与小孩。资源压缩包内共有1741个文件以jpg图像和json标注文件为主整体大小91.49MBCOCO标注规范可直接衔接主流检测框架也可转换为其他常见标注格式适合用于模型训练、精度评估与数据增强等环节。目前该数据集已有1998人学习下载标注对应的模型识别率约70.9%可作为基线模型参考从内容预览来看图像涵盖不同拍摄场景、人物姿态与光照条件能有效测试算法在真实环境中的鲁棒性大幅降低早期数据收集与标注成本。对于高校课程设计、竞赛练习或目标检测项目预研这套数据都能提供即时可用的标注样本帮助快速验证想法、对比算法效果并推动后续优化迭代。1. 成人/小孩检测数据集70.9%识别率背后1738张图的真实分量做儿童安全监控或商场客流统计的人多半撞过同一个尴尬公开数据集里成人样本多得筛不完小孩的标注却稀稀拉拉想训一个能同时识别成人和小孩的检测模型光标数据就耗掉两周。这份1738张原始图片的数据集把最费时的标注活先干完了——COCO JSON格式直接给到不用再碰LabelImg或者labelme来回倒格式。70.9%的识别率听着不算惊艳但这是真实数据上跑出来的基线不是刷榜数字用在幼儿园考勤、室内安防这类场景里基于它微调模型比从零开始标数据省事得多。适合手里有训练工具链、缺的只是干净标注集的人——拿到手先解析JSON确认标注质量再转成目标格式三五个小时就能跑出第一条pipeline。2. COCO JSON标注拆解先看清楚这份数据集装了什么拿到手先别急着开训。哪怕是别人标注好的数据也值得花十分钟确认三件事类别定义是什么、标注框的数量和质量如何、train/valid/test怎么划分的。这一步做扎实了后面训练少走很多弯路。COCO JSON是这个领域最通用的标注交换格式读懂它将来任何COCO格式的数据集到手都能快速上手。2.1 原始图片构成与标注文件的对应关系从文件名就能看出不少信息。000000000368_jpg.rf.a354cea163edd946205e4d13985aba16.jpg这种带_rf.后缀的命名是Roboflow导出工具的特征——_rf.后面跟的32位hash是每张图的唯一标识用来防止重名覆盖。原始来源是COCO、VOC还是自己拍的并不重要导出成这个格式后所有图片和标注文件都在同一套命名体系下处理起来很统一。图片格式以jpg为主里面混了少量原始bmp图比如person_406_bmp.rf.fe806abab8d8cc3c7a8d81b60be6bc73.jpg这条命名说明原图是一张bmp格式的图导出时被Roboflow转成了jpg。这种混用不会出大问题OpenCV的imread对两种格式都支持不过如果你打算用tf.data或者mmcv的某些模块读图bmp转jpg这一步还是统一做掉更稳。数据集的关键信息可以归纳成下面这张表信息项内容图片总数1738张目标类别2类成人和小孩标注格式COCO JSON含categories/images/annotations三个顶层字段标注内容每人一个bbox框含类别id参考识别率mAP0.5约70.9%一份标准的Roboflow COCO导出会把数据按train/valid/test拆分放在三个目录里每个目录下有一份_annotations.coco.json和对应的图片文件。如果你拿到手的就是这类目录结构直接按目录用就行如果只有一份_annotations.coco.json和一堆图片那就需要自己动手划分这个在第3章会详细说。2.2 用Python读取COCO JSON的三个核心字段COCO标注格式的JSON顶层固定有images、annotations、categories三个必填字段外加info和licenses可选的元信息。搞懂这三个字段的对应关系这份数据集的结构就完全打开了。import json with open(_annotations.coco.json, r, encodingutf-8) as f: coco json.load(f) # categories: 类别定义id是类别编号name是人类可读的名称 for c in coco[categories]: print(category:, c[id], c[name]) # images: 图片元信息列表每一条包含id、file_name、width、height print(image count:, len(coco[images])) for img in coco[images][:3]: print(image:, img[id], img[file_name], img[width], img[height]) # annotations: 标注框列表每个框关联到一张图和一个类别 print(annotation count:, len(coco[annotations])) for ann in coco[annotations][:3]: print(ann:, ann[image_id], ann[category_id], ann[bbox])这段脚本做了三件事打印出类别定义、图片元信息和前几个标注框。coco[categories]里每个元素是一个dictid字段是类别编号name字段是类别名称这个数据集里大概率就是adult和child也可能是person_adult和person_child这类命名以实际JSON内容为准。coco[images]里的id是这张图在数据集里的唯一编号file_name是文件名width和height是原始图片尺寸。coco[annotations]是标注框的集合image_id关联到images里的某张图category_id关联到categories里的某个类别bbox是一个长度为4的列表格式是[x, y, width, height]坐标原点是图片左上角。这里最容易搞混的是bbox的格式。COCO的bbox是x, y, w, h其中x和y是框左上角的坐标。跟YOLO的归一化中心点坐标完全是两套体系后面转换的时候必须算一遍不能直接把数字抄过去。2.3 标注可视化确认bbox和类别真的对得上读过JSON只是第一步标注质量才是决定模型上限的东西。JSON里的数字可能看着正常但框可能标歪、类别可能标反、甚至漏标。把标注框画到原图上肉眼过一遍比任何统计指标都直观。import cv2 import json from collections import defaultdict with open(_annotations.coco.json, r, encodingutf-8) as f: coco json.load(f) cat_map {c[id]: c[name] for c in coco[categories]} anns_by_img defaultdict(list) for ann in coco[annotations]: anns_by_img[ann[image_id]].append(ann) color_by_cat {} colors [(0, 0, 255), (0, 255, 0)] for i, c in enumerate(coco[categories]): color_by_cat[c[id]] colors[i % len(colors)] for img_meta in coco[images][:20]: img cv2.imread(img_meta[file_name]) if img is None: print(skip missing:, img_meta[file_name]) continue for ann in anns_by_img[img_meta[id]]: x, y, w, h [int(round(v)) for v in ann[bbox]] color color_by_cat[ann[category_id]] cv2.rectangle(img, (x, y), (x w, y h), color, 2) label f{cat_map[ann[category_id]]} {ann[category_id]} cv2.putText(img, label, (x, max(y - 4, 12)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) out_name vis_ img_meta[file_name].replace(.jpg, .png) cv2.imwrite(out_name, img)这段脚本把前20张图的标注框画出来按类别用不同颜色区分保存成vis_前缀的图片。画框前把bbox的float数值转成int因为cv2.rectangle不接受浮点坐标。文本绘制时用max(y - 4, 12)防止标签画到图片外面去。跑完这步打开vis图片扫一遍成人框是不是都套在成人身上、小孩框有没有明显偏移、有没有框完全对不上内容的。如果发现大量标注错位后面训练出来的70.9%就没什么参考价值得先修标注或者裁掉脏数据。这一章做完你手里应该有一份对数据集完整认知类别是什么、图有多少张、框画得对不对。这些信息直接决定后面的训练策略——类别定义决定YOLO的names配置框质量决定要不要花时间清洗图谱分布决定数据增强的强度。3. 从COCO JSON到YOLO训练格式转换与数据划分YOLO训练和COCO标注之间隔着一个绕不开的格式鸿沟。YOLO要的是每个图片对应一个同名txt、每行一个目标、内容是归一化的中心点坐标和宽高COCO给的是全局JSON里的左上角坐标和像素宽高。不转换没法喂给YOLO转换脚本的正确性直接决定模型学到的语义对不对。3.1 格式转换脚本COCO转YOLO txtCOCO转YOLO的核心就三件事把左上角坐标系的bbox转成中心点坐标系把像素值归一化成相对值把COCO的category_id映射成从0开始的连续索引。这三件事任何一件做错训练出来的模型都是废的。import json import os from collections import defaultdict def coco_to_yolo(ann_file, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) with open(ann_file, r, encodingutf-8) as f: coco json.load(f) # 1. 建立类别映射按categories的存储顺序映射到0,1,2... cat_id_to_idx {c[id]: i for i, c in enumerate(coco[categories])} # 2. 建立image_id到文件信息和尺寸的映射 img_info {img[id]: img for img in coco[images]} # 3. 按image_id组织标注 anns_by_img defaultdict(list) for ann in coco[annotations]: anns_by_img[ann[image_id]].append(ann) for img_id, anns in anns_by_img.items(): img img_info[img_id] w, h img[width], img[height] txt_path os.path.join( out_dir, os.path.splitext(img[file_name])[0] .txt ) lines [] for ann in anns: x, y, bw, bh ann[bbox] cx (x bw / 2.0) / w cy (y bh / 2.0) / h nw bw / w nh bh / h if nw 0 or nh 0: continue # 过滤异常空框 lines.append( f{cat_id_to_idx[ann[category_id]]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f} ) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 用法示例分别处理train/valid/test三个split coco_to_yolo(train/_annotations.coco.json, train, train_labels)这里的转换逻辑是最常见的Roboflow COCO转YOLO做法。类别索引映射用enumerate按categories在JSON里的顺序生成而不是直接用coco的category_id因为COCO的id可能从1开始而YOLO要求从0开始直接抄数字会让两类错位训练出来的模型类别全反。bbox的x, y是左上角先加一半宽高得到中心点再除以图片宽高得到归一化结果。continue那一行是过滤空框用的COCO标注里偶尔会出现宽或高为0的异常框不处理会在训练时触发坐标相关报错。转换完记得抽查几个txt文件打开看一眼内容是不是0 0.345 0.678 0.234 0.567这种格式以及标注框数量和JSON里对得上对不上。这一步检查花不了两分钟能挡住后面训练的大部分问题。3.2 数据集划分train/val比例怎么定1738张图的数据量不算大划分比例很关键。val集太小会导致验证曲线波动剧烈val集太大又会让训练数据不够用。我一般会按90/10拆分1738张里拿出约174张做验证训练集留1564张。这不光是数量问题——两个类别的样本在验证集里都要有如果小孩样本本来就少随机拆分时全被分进train的概率不低val里没有小孩类别那mAP曲线就是在骗人。如果拿到手的是Roboflow导出结构它默认按70/20/10或85/10/5拆分过直接沿用就行。如果只有一份JSON自己写个脚本拆import json import random import shutil import os with open(_annotations.coco.json, r, encodingutf-8) as f: coco json.load(f) random.seed(42) img_ids [img[id] for img in coco[images]] random.shuffle(img_ids) val_ratio 0.1 split_idx int(len(img_ids) * (1 - val_ratio)) train_ids set(img_ids[:split_idx]) val_ids set(img_ids[split_idx:]) for split_name, ids in [(train, train_ids), (valid, val_ids)]: os.makedirs(f{split_name}_images, exist_okTrue) os.makedirs(f{split_name}_labels, exist_okTrue) # 按划分结果把图片复制到对应目录标注txt也一起搬 for img in coco[images]: dest train_images if img[id] in train_ids else valid_images src_path img[file_name] shutil.copy(src_path, os.path.join(dest, os.path.basename(src_path)))这是最简单的一种划分方案seed固定保证可复现。random.seed(42)让每次跑出来的划分完全一样分享代码给别人时能对齐结果。拆完以后检查一下两个类别的标注框在train和val里的数量分布理想情况是两个split里类别比例大致一致。如果发现类别比例差很多说明划分不合理考虑改成按类别分层抽样。3.3 数据YAML配置与常见参数YOLOv8用yaml文件描述数据集配置训练命令直接引用这个文件。里面内容很少但每一项错了都会导致训练起不来或类别错乱。# data.yaml train: ./train_images val: ./valid_images nc: 2 names: 0: adult 1: childtrain和val指向图片目录YOLO会在同目录下找对应的labels文件夹所以图片目录的上一级必须有一个labels文件夹放转换出来的txt。nc是类别数必须和names里的数量一致。names的顺序必须和转换脚本里cat_id_to_idx映射的顺序一致如果JSON里categories顺序是child在前、adult在后这里就写成0: child, 1: adult顺序错了整个模型的输出语义就是反的。这个yaml写在项目根目录就够用不要放中文路径下YOLO在Windows下对中文路径的处理一直不叫人省心训练时经常报奇怪的读取错误。4. 复现70.9%识别率训练配置与调优实录数据准备好了接下来是训练。这个数据集规模不大训练策略跟大数据集完全不同模型不能选太大、epochs要给足但又要靠早停防止过拟合、输入尺寸要根据目标大小反过来推导。每个参数都有它的理由照搬默认值不是不行但出了问题你不知道去哪找原因。4.1 模型选型与输入尺寸选择二分类检测任务模型不用上大的。YOLOv8n只有约3.2M参数在1738张图的小数据集上限绰绰有余——这种数据量喂给YOLOv8x纯属浪费算力还容易过拟合。标题里说的70.9%识别率在检测任务的语境下一般指mAP0.5也就是IoU阈值取0.5时的平均精度。落到自己机器上复现受随机种子、数据划分和训练参数影响在0.68到0.72之间浮动都算正常不必纠结那一个点。输入尺寸的选择反而比模型选择更重要。YOLOv8默认imgsz640但这份数据集的原始图片来源杂VOC、COCO、自定义拍摄混在一起尺寸跨度大概率不小。如果你的图片里成人大都是大目标、小孩是小目标640的输入会把小孩压到十几个像素特征根本学不出来。这种情况下把imgsz提到768或832对小孩这类小目标有明显的提升。反过来如果原图分辨率很低四五百像素的VGA图强行上768只会让图片被放模糊不如用512甚至416跑得更快更稳。4.2 训练超参数设置与命令执行确定了模型和输入尺寸直接跑训练命令。我以YOLOv8n为例给出一个在1738张图上比较稳妥的配置yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ seed42参数拆开说。epochs150对小数据集来说足够模型收敛配合patience30做早停——连续30个epoch val损失没有改善就自动停防止过拟合又不浪费算力。lr00.01是YOLOv8的默认初始学习率如果loss曲线震荡剧烈降到0.005再试。batch16考虑的是显存16G显存跑YOLOv8n在640输入下没问题显存小就降到8。seed42跟划分脚本的seed保持一致保证复现链路是完整的。参数推荐值调整时机modelyolov8n.pt显存富余可换yolov8s.ptimgsz640小目标多时提到768/832原图低分辨率时降到512batch16显存不足降到8lr00.01loss震荡时降到0.005patience30val曲线波动大时加到50seed42固定保证可复现训练跑起来后注意看终端日志里Class和Images两列。正常流程是前面几十个epoch val mAP快速爬升爬升变缓后进入平台期最后被patience截断。如果日志里accuracy那一列一直在1附近来回跳先查数据有没有乱而不是调参——八成是类别索引映射错了。4.3 训练日志监控从loss曲线判断过拟合很多人在小数据集上翻车是因为不会读日志。YOLO训练过程中每个epoch会打印loss和mAP指标关键看三个趋势训练loss是不是持续下降、val loss是不是在某个点开始回升、val mAP是不是还在涨。过拟合的典型信号是train loss一路走低但从某个epoch开始val mAP不再涨甚至往下掉val loss同步回升。这是模型开始死记训练集细节了此时该做的是停止训练而不是继续磨。我在4.2节配置里用patience30就是给这个场景兜底——曲线震荡可以忍连续30个epoch不改善就直接收工把前面积累的最优权重保留下来。如果发现模型在epoch 30左右就过拟合了说明150轮的上限设得过高不用改参数重训直接从早停保存的权重里取best.pt就行。反过来如果150轮跑完loss还在明显下降说明量给少了可以续个50轮。这些判断都来自日志里那几列数字养成每轮扫一眼的习惯比什么玄学调参都管用。训练结束后YOLO会在runs/detect/train/目录下生成weights/best.pt和weights/last.pt取前者做评估和部署。5. 避坑这份数据集最常见的5个翻车点训练检测模型跟做别的算法项目不一样数据格式、命名规则、类别映射这些细节任何一个环节出错模型要么不收敛要么收敛出一个语义错乱的废模型。这5个坑我都在类似项目上踩过写出来给你省点排查时间。5.1 坑一COCO的category_id直接填进YOLO txt类别全反现象训练正常跑完验证时发现模型把成人全识别成小孩、小孩全识别成成人mAP曲线却异常好看。原因COCO的categories字段里id是1和2而YOLO要求类别索引从0开始。转换脚本如果直接用ann[category_id]当类别索引等于在告诉YOLO有两个类别分别叫1和2跟data.yaml里的0和1完全错位。YOLO自己不会报错它只是老老实实把「类别1」当成「第一个类别」来学于是语义整个颠倒。解决转换的时候统一按categories在JSON里的存储顺序重新编号用enumerate生成{old_id: new_idx}映射不要写死id - 1这种硬编码。转换完抽样检查txt里的第一列数字应该只有0和1两个值。5.2 坑二带_rf.hash的文件名和bmp混用读取环节卡住现象训练时报错FileNotFoundError或者ValueError: image format not supported但用文件管理器能看到图片明明存在。原因文件名里的_rf.加32位hash让文件名变得很长某些工具链对文件名的处理有长度或特殊字符限制。另外原图里有bmp格式虽然OpenCV能读但部分深度学习数据加载器比如tf.data的某些解码器不支持bmp只有jpg和png的编解码器。解决拿到数据先统一做一遍预处理把所有图片复制出来转成jpg文件名里的_rf.hash可以去掉或缩短统一用000001.jpg这种编号命名。顺便把bmp后缀的文件名一起改掉。这一步用Python的os.rename加cv2.imwrite十分钟能跑完换来的是一路顺畅的训练启动。5.3 坑三缩放后bbox算出负值或超过1训练报越界错误现象训练跑到一半报invalid bbox或AssertionError: coordinates out of range中断退出。原因少数标注框贴图边缘或者原图被缩放过导致COCO JSON里的坐标跟实际图片尺寸对不上。转换脚本里归一化的cx cy w h算出来小于0或者大于1YOLO的增强模块会对坐标范围做断言检查一发现越界就崩。解决转换脚本里对归一化结果做clamp(0, 1)处理把越界的坐标截断到合法范围nw max(0.0, min(1.0, bw / w)) nh max(0.0, min(1.0, bh / h))顺便把nw 0 or nh 0的空框过滤掉转换环节就解决掉的崩溃不需要跑到训练中途再回头排查。5.4 坑四成人样本远多于小孩模型变成只会识别成人的「偏科生」现象训练的mAP看着在0.7上下浮动但单独看child类的AP只有0.2甚至更低推理时漏检一堆小孩。原因真实场景采集的成人/小孩数据集几乎必然不均衡成人样本多、小孩样本少模型优化整体loss时把资源全压在了大类的准确性上。解决先统计两个类别各自的标注框数量确认不均衡的程度。两种常用的处理手段一是训练时给样本少的类别加loss权重YOLOv8可以在data.yaml里配置class_weights二是对小孩类样本做重复采样把包含小孩的图片复制几份混进训练集简单直接但不建议复制太多轮两倍以内一般够用。5.5 坑五用默认conf_thres评估把真实能力看走眼现象用训练完的模型跑验证集框出来的目标数量明显比标注少误以为数据集质量差导致模型没学会。原因YOLO默认的conf阈值是0.25推理时置信度低于0.25的框全被过滤。模型对小孩这类小目标的置信度普遍偏低0.20.3之间的有效框被默认阈值一把没收看起来就像漏检严重。解决评估时不加conf限制跑yolo val ... conf0.001看模型的真实上限。等确认性能达标后部署推理时再根据业务容忍度把conf阈值调回0.250.4之间。这两个数值记录在模型卡里调参时先看conf0.001的mAP再看业务阈值下的精确率和召回率就不会被中间那层过滤误导。6. 落地验证写一个独立的评测脚本看真实效果训练完不要只看YOLO自带的日志就收工我习惯写一个独立的评测脚本把日志里看不到的东西翻出来每一类的AP到底差多少、低置信度的漏检长什么样、框的位置偏移有没有系统性偏差。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val( datadata.yaml, conf0.001, # 评估时收窄conf阈值看模型真实上限 iou0.5, # 与习惯的mAP0.5口径对齐 splitval ) print(mAP50:, metrics.box.map50) print(mAP50-95:, metrics.box.map)conf0.001把置信度阈值压到接近零让模型把所有可能的框都吐出来这样算出的mAP才是模型的真实上限不被默认阈值过滤掉的框影响。iou0.5对应常用的mAP0.5口径标题里的70.9%如果不注明IoU阈值一般也是指这个值。跑完打印两个指标重点看metrics.box.map50是不是稳定在0.7附近符合数据集的说明就可以进入下一步。只看数字不够再用真实图片跑一次推理把框画出来看视觉上的质量import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) img cv2.imread(test_sample.jpg) results model.predict(img, conf0.25) for box in results[0].boxes: x1, y1, x2, y2 [int(v) for v in box.xyxy[0]] cls int(box.cls[0]) conf float(box.conf[0]) name model.names[cls] color (0, 0, 255) if cls 0 else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, f{name} {conf:.2f}, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(result.jpg, img)这段脚本用训练好的best.pt跑一张图按类别画不同颜色的框和置信度。box.xyxy[0]是YOLO输出的边界框格式是[x1, y1, x2, y2]跟COCO那种[x,y,w,h]又不一样直接用int转成像素坐标就能画。多找几张各含成人、小孩、以及两个都有的图跑一遍重点看三个点小孩小目标的框稳不稳、人群密集时框有没有重叠打架、类别有没有标反。这一步比任何指标都更能暴露模型在真实场景里的毛病。从那以后我每训完一个检测模型都强制走一遍「评估conf0.001看上限 固定conf0.25看落地点 真实图目视检查」三步。日志里的mAP高只能说明平均表现好不等于在业务场景里能用。如果你也准备用这个成人和小孩数据集做训练建议把这套验证流程照搬一次确认无误再谈部署。希望帮到你。本文还有配套的精品资源点击获取
返回列表