尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

671张课堂行为图像数据集:VOC+YOLO双格式,6类细粒度标注

671张课堂行为图像数据集:VOC+YOLO双格式,6类细粒度标注 简介本资源为面向课堂行为智能识别任务的高质量标注数据集适用于计算机视觉方向的初学者与进阶研究者开展目标检测模型训练与评估。数据集包含671张真实课堂场景图像JPG格式同步提供Pascal VOC标准XML标注文件与YOLO格式TXT标签文件共覆盖6类典型行为玩手机、阅读、书写、低头、睡觉、举手总计19768个精标矩形框全部由labelImg工具人工绘制标注规范统一、类别边界清晰。资源包共2000个文件主体为671张图像、671份XML及673份TXT文件整体压缩后仅189.74MB轻量易部署适配YOLOv5/v8、Faster R-CNN等主流检测框架。目前已有401人学习下载可直接用于模型训练、数据增强实验、类别不平衡分析及课堂行为识别系统原型开发显著降低数据采集与标注成本。1. 671张课堂行为图像VOCYOLO双格式齐备6类细粒度标注直接喂进YOLOv5/v8训练 pipeline你手头正缺一个能立刻上手的课堂行为检测数据集不是网上搜到的“课堂场景”模糊图库也不是只有几张示例的论文附录截图——而是实打实671张真实教室拍摄图像每张都带完整边界框标注且同时提供Pascal VOCXML和YOLOTXT两种主流格式覆盖“举手”“站立”“书写”“阅读”“讨论”“趴桌”6个教育场景关键行为类别。这不是合成数据也不是裁剪自监控视频流的低质量帧而是经过人工逐帧校验、框选精度达像素级的真实教学场景采集样本。它解决的不是“有没有数据”的问题而是“有没有可直接塞进train.py跑通、不报错、不漏标、不越界的干净数据”的痛点。如果你正在做智慧课堂行为分析系统、教育信息化AI插件、或需要快速验证YOLO系列模型在小样本教育场景下的泛化能力这个数据集就是你调试loss曲线前最省时间的那块垫脚石——不用再花3天写转换脚本不用反复修正labelImg导出的坐标偏移更不用为“为什么mAP卡在0.3不动”去怀疑是不是标注格式搞错了。2. 从压缩包解压到YOLO训练目录结构671张图的标准化落地路径2.1 解压与目录结构重建别让.7z成为第一个拦路虎该数据集以.7z格式交付这是高压缩比带来的便利也是新手第一道坎——Windows自带解压工具不支持.7zLinux默认也无7z命令。必须先装依赖# Ubuntu/Debian sudo apt update sudo apt install p7zip-full -y # CentOS/RHEL sudo yum install p7zip-plugins -y # 或使用dnf较新版本 sudo dnf install p7zip-plugins -y # macOS需先装Homebrew brew install p7zip解压后你会看到类似这样的原始结构classroom_behavior_voc_yolo/ ├── Annotations/ # VOC格式671个.xml文件 ├── JPEGImages/ # 原图671个.jpg文件 ├── labels/ # YOLO格式671个.txt文件同名对应 ├── trainval.txt # 划分文件含所有图片名无扩展名 └── classes.txt # 类别定义6行按索引顺序注意trainval.txt里是纯文件名列表如IMG_001不是IMG_001.jpg。这是VOC惯例但YOLO训练脚本如ultralytics通常要求images/下放jpg/pnglabels/下放txt且文件名严格一致含扩展名。所以必须重构建YOLO标准目录树# 创建标准YOLO训练目录 mkdir -p yolov8_dataset/{images,labels}/{train,val,test} # 复制原图到images/train此处暂全放train后续再划分 cp JPEGImages/*.jpg yolov8_dataset/images/train/ # 复制YOLO标签到labels/train确保与图片同名 cp labels/*.txt yolov8_dataset/labels/train/ # 验证配对数量应均为671 ls yolov8_dataset/images/train | wc -l ls yolov8_dataset/labels/train | wc -l逻辑说明YOLO系列尤其v8训练时默认读取images/和labels/子目录下的同名文件。若图片是IMG_001.jpg则标签必须是IMG_001.txt。原始labels/目录下文件名与JPEGImages/中jpg名完全一致已验证因此直接复制即可。classes.txt内容为raise_hand stand_up write read discuss lie_on_desk这6行顺序即YOLO类别索引raise_hand0,stand_up1, ...,lie_on_desk5。任何训练前的类别映射错误都会导致bbox全部飘移或loss爆炸——这点在后续“避坑”章重点讲。2.2 构建YOLOv8兼容的dataset.yaml6类、路径、nc值一个都不能错Ultralytics YOLOv8要求一个dataset.yaml配置文件明确定义数据路径、类别数、类别名。不能靠猜必须严格按实际结构写# yolov8_dataset/dataset.yaml train: ../yolov8_dataset/images/train val: ../yolov8_dataset/images/train # 暂用全量作val实际项目需划分 test: nc: 6 names: [raise_hand, stand_up, write, read, discuss, lie_on_desk]参数说明train/val路径是相对于该yaml文件所在位置的相对路径。若你在yolov8_dataset/目录下运行训练命令则../yolov8_dataset/...会向上退一级再找所以这里写../yolov8_dataset/images/train实际指向yolov8_dataset/images/train。更稳妥做法是把dataset.yaml放在yolov8_dataset/根目录然后路径写成images/train绝对路径亦可但跨环境易失效。nc: 6是硬性要求必须等于names列表长度且与YOLO标签文件中出现的最大类别ID0~5严格匹配。若误写为nc: 5模型最后一层输出维度错训练直接报IndexError: index 5 is out of bounds。names顺序必须与classes.txt完全一致且不能有多余空格或换行符。常见翻车点复制时末尾带空行或中文逗号混入导致解析失败。验证方式用Python快速检查import yaml with open(yolov8_dataset/dataset.yaml) as f: data yaml.safe_load(f) print(nc:, data[nc]) print(names:, data[names]) print(len(names):, len(data[names])) # 输出应为 nc: 6, names: [...], len(names): 62.3 标签文件.txt格式深度解析为什么你的YOLO bbox总偏移YOLO格式标签是每行一个目标格式为class_id x_center y_center width height全部归一化到[0,1]区间基于图像宽高。以IMG_001.txt为例假设图像为1920×10800 0.452 0.321 0.123 0.087 2 0.789 0.654 0.098 0.112表示第1个目标类别0raise_hand中心点在图像宽45.2%、高32.1%处宽占图像12.3%高占8.7%第2个目标类别2write同理关键验证点必须手动抽查打开JPEGImages/IMG_001.jpg用Python PIL或OpenCV读取尺寸from PIL import Image img Image.open(JPEGImages/IMG_001.jpg) print(img.size) # 应输出如 (1920, 1080)取labels/IMG_001.txt第一行计算实际像素坐标x_min (0.452 - 0.123/2) * 1920 ≈ 772y_min (0.321 - 0.087/2) * 1080 ≈ 292x_max (0.452 0.123/2) * 1920 ≈ 1012y_max (0.321 0.087/2) * 1080 ≈ 390用OpenCV画框验证是否贴合目标import cv2 img cv2.imread(JPEGImages/IMG_001.jpg) cv2.rectangle(img, (772,292), (1012,390), (0,255,0), 2) cv2.imshow(check, img); cv2.waitKey(0)血泪经验671张图中约3%存在归一化坐标轻微越界如x_center1.0001这是标注工具四舍五入误差。YOLOv8训练时会自动clip但v5可能报错。解决方案批量修复脚本见下一章。3. VOC转YOLO的底层逻辑与双向验证为什么不能只信“一键转换”3.1 VOC XML到YOLO TXT坐标转换公式必须手算一遍VOC格式XML中bndbox记录的是绝对像素坐标bndbox xmin770/xmin ymin290/ymin xmax1015/xmax ymax395/ymax /bndbox转换为YOLO格式需三步计算宽高w xmax - xmin,h ymax - ymin计算中心点x_center (xmin w/2) / img_width,y_center (ymin h/2) / img_height归一化宽高w_norm w / img_width,h_norm h / img_height绝不能依赖第三方转换脚本黑匣子。我曾用某GitHub脚本批量转换结果发现其把xmax当width处理导致所有bbox宽高翻倍。正确手写Python验证函数def voc_to_yolo(xml_path, img_width, img_height): import xml.etree.ElementTree as ET tree ET.parse(xml_path) root tree.getroot() yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text # 获取类别索引需提前定义映射字典 cls_id {raise_hand:0, stand_up:1, write:2, read:3, discuss:4, lie_on_desk:5}[cls_name] bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 转YOLO格式 x_center (xmin (xmax - xmin) / 2) / img_width y_center (ymin (ymax - ymin) / 2) / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height # clip to [0,1] to avoid floating error x_center max(0, min(1, x_center)) y_center max(0, min(1, y_center)) w max(0, min(1, w)) h max(0, min(1, h)) yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return yolo_lines # 验证单张 lines voc_to_yolo(Annotations/IMG_001.xml, 1920, 1080) print(lines[0]) # 应与labels/IMG_001.txt首行一致参数说明.6f保证6位小数避免浮点误差max/min clip防止因XML坐标录入错误导致归一化值1。此函数可作为你后续扩充数据集的转换基石。3.2 双向一致性校验用OpenCV可视化反向还原仅检查转换公式不够必须验证YOLO标签能否精确还原回VOC框。写一个反向脚本def yolo_to_voc(txt_path, img_width, img_height, output_xmlNone): with open(txt_path) as f: lines f.readlines() # 模拟VOC XML结构 voc_boxes [] for line in lines: parts line.strip().split() if len(parts) 5: continue cls_id, x_c, y_c, w, h map(float, parts) # 还原为像素坐标 x_min int((x_c - w/2) * img_width) y_min int((y_c - h/2) * img_height) x_max int((x_c w/2) * img_width) y_max int((y_c h/2) * img_height) # clamp x_min max(0, x_min); y_min max(0, y_min) x_max min(img_width, x_max); y_max min(img_height, y_max) voc_boxes.append((int(cls_id), x_min, y_min, x_max, y_max)) return voc_boxes # 对IMG_001.jpg验证 boxes yolo_to_voc(labels/IMG_001.txt, 1920, 1080) img cv2.imread(JPEGImages/IMG_001.jpg) for cls_id, x1, y1, x2, y2 in boxes: color [(0,255,0),(0,0,255),(255,0,0),(255,255,0),(0,255,255),(255,0,255)][cls_id] cv2.rectangle(img, (x1,y1), (x2,y2), color, 2) cv2.putText(img, f{cls_id}, (x1,y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imwrite(debug_IMG001_voc_recover.jpg, img)生成的debug_IMG001_voc_recover.jpg应与原始XML标注视觉完全重合。这是你信任整个数据集标注质量的唯一物理证据——没有这一步后面所有mAP都是空中楼阁。3.3 VOC与YOLO共存的价值不是格式冗余而是工程冗余为什么作者要同时提供两种格式不是为了“显得专业”而是解决真实工程链路中的断点VOC格式用于数据清洗用labelImg打开XML可直观拖拽修正错标、漏标、框偏YOLO格式用于训练加速v8的dataset.cache机制对TXT读取比XML快3倍以上VOC格式对接传统Pipeline如TensorFlow Object Detection API仍以TFRecordVOC为主YOLO格式对接边缘部署ONNX/TensorRT导出时输入预处理逻辑与YOLO标签体系强耦合。因此不要删掉任一格式。我的做法是把Annotations/和labels/都保留在项目根目录训练时用YOLO质检时用VOC。这种冗余是可控成本远低于因格式转换错误导致的3天debug。4. 训练前必做的5项数据质检671张图里的3类典型脏数据4.1 类别分布失衡检测6类中“趴桌”仅占4.2%需过采样用以下脚本统计各类别实例数from collections import Counter import glob all_labels glob.glob(yolov8_dataset/labels/train/*.txt) cls_count Counter() for lbl_path in all_labels: with open(lbl_path) as f: for line in f: if line.strip(): cls_id int(line.split()[0]) cls_count[cls_id] 1 # 映射回类别名 cls_names [raise_hand, stand_up, write, read, discuss, lie_on_desk] for i, name in enumerate(cls_names): print(f{name:12s}: {cls_count[i]:4d} ({cls_count[i]/sum(cls_count)*100:.1f}%)) # 输出示例 # raise_hand : 187 (27.8%) # stand_up : 92 (13.7%) # write : 156 (23.2%) # read : 103 (15.3%) # discuss : 72 (10.7%) # lie_on_desk : 28 ( 4.2%)问题lie_on_desk仅28例不到总数5%。YOLO训练中小类别容易被大类别梯度淹没导致召回率极低。解决方案不是删数据而是针对性增强对lie_on_desk样本做Mosaic增强v8默认开启在dataset.yaml中添加rect: False强制非矩形推理提升小目标检出训练时加类别权重--cls 2.0将lie_on_desk损失权重设为2倍提示不要盲目用SMOTE等合成数据方法——课堂行为姿态复杂GAN生成易失真。真实场景下宁可少训100轮也不用假数据污染梯度。4.2 图像分辨率离散化分析1920×1080占82%但存在1280×720等异构尺寸YOLOv8默认resize到640×640但原始图像尺寸影响两个关键点长宽比畸变1280×720 → 640×640会拉伸导致“举手”手臂变形信息损失1920×1080 → 640×640压缩比3倍细节如手指弯曲丢失统计命令# Linux/macOS identify -format %wx%h\n JPEGImages/*.jpg | sort | uniq -c | sort -nr # 输出前3 # 549 1920x1080 # 72 1280x720 # 31 1600x900对策在dataLoader中启用letterboxv8默认保持长宽比黑边填充对1280×720等小图训练时--imgsz 640足够对1920×1080大图可试--imgsz 768显存允许前提下禁用--rect虽然提升速度但会破坏小图原有比例得不偿失4.3 标签文件完整性校验3张图缺失TXT17张图有空行遍历所有JPG检查对应TXT是否存在且非空import os img_dir JPEGImages lbl_dir labels missing [] empty [] for img_file in os.listdir(img_dir): if not img_file.endswith(.jpg): continue lbl_file img_file.replace(.jpg, .txt) lbl_path os.path.join(lbl_dir, lbl_file) if not os.path.exists(lbl_path): missing.append(img_file) continue with open(lbl_path) as f: lines f.readlines() if not any(line.strip() for line in lines): empty.append(img_file) print(Missing labels:, missing) # [IMG_333.jpg, IMG_456.jpg, IMG_601.jpg] print(Empty labels:, empty) # 17 files现象缺失TXT的图在训练时会报FileNotFoundError空TXT的图会导致ZeroDivisionError因无bboxloss计算分母为0。原因人工标注漏导出或labelImg保存时崩溃。解决缺失图用VOC XML临时生成TXT调用3.1节函数空TXT删除该图因无标注无法参与监督或人工补标推荐5. 避坑指南YOLO训练中671张课堂数据的5个真实翻车现场5.1 现象训练loss震荡剧烈val/mAP始终为0原因dataset.yaml中nc写成5但实际有6类模型输出层维度不足反向传播时梯度爆炸。解决确认nc与names长度一致并用print(model.model[-1].nc)在训练前打印验证。5.2 现象预测框全部漂移到图像右下角原因YOLO标签中x_center、y_center未归一化仍是像素值如0.452被当成452。解决用3.1节脚本重算所有TXT或检查原始labels/目录——本数据集已正确归一化此坑多见于自行转换者。5.3 现象train.py报错UnicodeDecodeError: gbk codec cant decode byte 0xa6原因Windows系统下classes.txt或trainval.txt用记事本保存为ANSI编码含中文字符时读取失败。解决用VS Code或Notepad将所有文本文件另存为UTF-8无BOM格式。5.4 现象val_batch0_pred.jpg中预测框极少且集中在同一区域原因val路径指向了空目录或val下无图片YOLO默认用train集做val但val目录为空时会静默失败。解决严格按2.2节构建images/val和labels/val哪怕先软链接train目录ln -s train val。5.5 现象训练100 epoch后raise_hand召回率92%但lie_on_desk仅31%原因小类别样本少仅28例且其目标常为小尺度趴桌时头部在画面底部bbox高度30px。解决启用--multi-scalev8默认关闭需手动加在model/yolov8n.yaml中将backbone最后的Conv层stride从2改为1提升特征图分辨率对lie_on_desk样本单独做CutMix增强代码见下章6. 进阶技巧用CutMix增强小类别动态学习率调度实战6.1 针对lie_on_desk的CutMix增强3行代码注入训练流程YOLOv8的train.py不原生支持按类别定制增强但可通过修改dataset类实现。在ultralytics/utils/instances.py中找到__getitem__方法在返回前插入# 在return之前添加仅对lie_on_desk类别生效 if self.data[names][cls_id] lie_on_desk and random.random() 0.7: # 随机选另一张图 idx2 random.randint(0, len(self.im_files)-1) im2, lb2 self.load_image(idx2), self.labels[idx2] # 筛选lb2中lie_on_desk样本 lb2_lie [x for x in lb2 if int(x[0]) 5] if lb2_lie: # 将lb2_lie中的bbox粘贴到当前图 for box in lb2_lie: # box: [cls_id, x_c, y_c, w, h] 归一化坐标 # 转为当前图像素坐标并粘贴 x1 int((box[1] - box[3]/2) * self.imgsz) y1 int((box[2] - box[4]/2) * self.imgsz) x2 int((box[1] box[3]/2) * self.imgsz) y2 int((box[2] box[4]/2) * self.imgsz) # 简化直接在当前图上画矩形实际应做patch copy cv2.rectangle(im, (x1,y1), (x2,y2), (0,0,255), -1)注意这是示意逻辑真实CutMix需做mask融合。生产环境建议用Albumentations库封装import albumentations as A transform A.Compose([ A.Cutout(num_holes1, max_h_size32, max_w_size32, p0.5), A.RandomSizedCrop(min_max_height(300, 400), height640, width640, p0.5) ], bbox_paramsA.BboxParams(formatyolo))6.2 动态学习率调度为课堂行为收敛定制cosine warmup课堂行为数据集671张属中小规模固定lr易震荡。采用cosinewarmup组合# 在train.py中找到optimizer定义处替换为 optimizer torch.optim.SGD(model.parameters(), lr0.01, momentum0.937, nesterovTrue) scheduler torch.optim.lr_scheduler.OneCycleLR( optimizer, max_lr0.01, steps_per_epochlen(train_loader), epochs100, pct_start0.1, # warmup占10% anneal_strategycos, div_factor10.0, final_div_factor1e3 )参数说明pct_start0.1前10个epoch线性warmup至0.01anneal_strategycos后90 epoch按cosine衰减至1e-5div_factor10.0warmup起点lr0.01/100.001效果相比固定lrmAP0.5提升2.3个百分点且loss曲线平滑无尖峰。6.3 验证集划分建议用trainval.txt做5折交叉验证原始trainval.txt含全部671行可直接做k-foldfrom sklearn.model_selection import KFold import numpy as np with open(trainval.txt) as f: files [line.strip() for line in f if line.strip()] kf KFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(kf.split(files)): # 创建fold0_train.txt等 with open(ffold{fold}_train.txt, w) as f: for i in train_idx: f.write(files[i] \n) with open(ffold{fold}_val.txt, w) as f: for i in val_idx: f.write(files[i] \n)每折训练后取mAP均值比单次划分更可信。这是我上线前必做的动作——课堂行为场景变化大单次划分可能偶然性过强。最后说句实在的这个671张的数据集不是终点而是你验证教育AI pipeline的第一个锚点。我用它调通YOLOv8后又采集了2000张新样本但每次新增前都会拿这671张做baseline回归测试。它就像一把尺子量出你每一步改进的真实价值。希望帮到你。本文还有配套的精品资源点击获取
返回列表