ARTICLE DETAIL

资讯详情

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

工程机械识别数据集构建全流程:从采集标注到YOLOv8训练

工程机械识别数据集构建全流程:从采集标注到YOLOv8训练 简介面向目标检测与深度学习实践者的工程机械识别数据集涵盖挖掘机、装载机、自卸卡车、汽车起重机、压路机、推土机、平地机七类常见作业设备共包含六千三百三十八张图片可支撑YOLO系列、Faster RCNN、SSD等主流模型的训练与评估。数据已按通用训练流程划分训练集、验证集与测试集压缩包共两千个文件以txt标注文件为主并含一个yaml类别配置文件整体约三百六十一点七四MB目录结构清晰方便直接投入训练。标签与图片一一对应可省去自行标注与格式转换环节适合智慧工地、工程车辆监控、施工现场安全巡检等场景的模型落地与算法验证整体属于开箱即用的监督学习数据包。目前已有三百零八人浏览学习适合具备一定深度学习基础的开发者直接使用。1. 工程机械识别数据集工地监控最难补齐的那块拼图固定摄像头拍到的挖掘机、装载机、塔吊和摄影图库里看网图完全是两回事镜头离得远、目标占像素少、机身蒙着灰土换个角度同一台设备看起来像另一台。用 COCO 预训练权重直接在工地上跑 YOLO一到傍晚就是漏检晴天强曝光又出误检。要把目标检测真正落到施工安全、工程进度统计这类场景先要解决工程机械识别数据集“有没有、像不像、干不干净”的问题而不是继续堆模型复杂度。这篇内容正是围绕“从零搭一套工程机械识别的目标检测数据集”展开从类别定义、采集、标注、格式转换、增强、训练评估到难例回流每一步都给可执行命令和可复现脚本适合正在为施工安全数据集发愁或打算用 YOLOv5/YOLOv8 训练自己数据集的工程视觉团队。2. 工程机械识别数据集的类别体系与采集先定边界再拍图2.1 类别不要按品牌分按作业功能分成 8 到 12 类常见错误是往数据集里塞“沃尔沃挖掘机 EC240”“三一装载机 SYL956”这种产品型号标签。工地检测的最终目标是统计“现场有哪几类设备在干活”不是识别二手交易里的具体型号。工程机械识别数据集按作业功能分类间差异更稳定换个品牌换个涂装仍然能框住目标。我一般建议初始类别控制在 8 到 12 类以内覆盖工地上出现频率最高的设备同时不要包含“人”“安全帽”这类与工程机械无关的对象避免训练时抢置信度。一张工地监控画面里可能同时出现挖掘机、自卸车和压路机类别间物理结构差异足以用水平边界框区分不需要做分层类别树只有塔吊这种长臂结构在特殊角度下会发生伸出画面才需要考虑是否单独拆成旋转框标注。类别工地常见别名外观差异主要标注难点excavator挖掘机、挖机履带、驾驶室、铲斗臂明显铲斗臂完全伸展时框要包含全部外沿不能只框车身loader装载机、铲车前端大铲斗车身短粗正面入射角时铲斗遮挡前轮边界模糊bulldozer推土机前方推土铲后方松土器与挖掘机的履带部分容易混类别判定先看铲刀crane吊车、汽车吊伸缩臂或桁架臂底盘轮式/履带式吊臂悬空横穿画面时背景干扰强烈需要标注完整吊臂dump truck自卸车、翻斗车货箱可举升车头独立货箱举升状态前后轮廓变化大tower crane塔吊塔身竖直吊臂水平中景以上高度常超出取景框部分遮挡的目标是否算要提前定规则roller压路机、碾压机圆滚轮为主驾驶室居中远处滚轮比车身高框容易偏小concrete pump混凝土泵车多节臂架底盘后侧有料斗臂架折叠状态和展开状态外形完全不同2.2 采集通道固定监控、无人机巡检、公开影像各占多少合适工程机械识别数据集的采集核心是覆盖“视角 光照 遮挡”三个维度。固定枪机是主力通道放在工地出入口和作业面旁边它产生的基本都是俯视和斜俯视角度目标像素占比小正是小目标检测最需要的样本。无人机巡检负责补充大角度变化悬停俯拍、侧前方航拍、卫星式正摄给同一台设备制造十几个不同投影姿态。公开影像和短视频平台可以做最后一层补充但要筛掉摆拍、滤镜和广告图这类图色调太干净放进数据集容易让模型在工地尘土环境下失效。采集时给每个视频片段记下工地类型、时间段、天气和机位高度这些元数据不直接参与训练但后续做数据划分时会很有用。常见做法是让三个通道比例大约为固定监控 60%、无人机 20%、公开补充 20%。如果全用监控模型对高角度和侧面视角会严重偏置全用无人机部署回监控机位又会出现视角迁移问题。2.3 清洗图像并统一命名避免用爬虫目录直接进训练采集得到的原始素材不要直接扔进标注软件。先用 Python 做一遍初筛剔除监控回放里的静态空帧、运动模糊帧、分辨率过低的帧以及带明显水印或电视台角标的画面。水印会被模型当成与类别相关的纹理在真实场景中反复误报。对长视频抽帧时我习惯每隔 25 到 30 帧抽一次既保留连续动作中的不同姿态又不会让相邻帧高度相似。import os import cv2 from pathlib import Path raw_dir Path(raw_videos) out_dir Path(images_pool) out_dir.mkdir(exist_okTrue) frame_step 25 target_min_size 800 for video_path in raw_dir.glob(*.mp4): cap cv2.VideoCapture(str(video_path)) name video_path.stem idx 0 saved 0 while True: ret, frame cap.read() if not ret: break if idx % frame_step 0: h, w frame.shape[:2] if min(h, w) target_min_size: idx 1 continue # 统一命名来源_原始文件名_帧号.jpg out_name f{video_path.parent.name}_{name}_{idx:06d}.jpg cv2.imwrite(str(out_dir / out_name), frame, [cv2.IMWRITE_JPEG_QUALITY, 90]) saved 1 idx 1 cap.release() print(f{name}: saved {saved} frames)这段代码通过frame_step 25控制抽帧间隔工程车作业动作缓慢25 到 40 帧抽一帧足够覆盖姿态变化但又不会让数据集膨胀得失去多样性。target_min_size是分辨率下限低于它的小画面放进训练集只会框出一个糊目标模型学到的不是结构特征而是红绿噪声。抽出来的图按“来源目录名_视频名_帧号”命名保证每张图可回溯。清理完原始图以后需要把同类设备在画面里只有十几像素的极端样本也统计出来。后续第 4 章做小目标增强时这些样本是否有数量、是否干净直接决定增强策略是否有效。3. 用标注工具完成框选转成 YOLO 目标检测数据集结构3.1 标注规范与边界框选择水平框为主特殊设备单独建项目工程机械识别数据集里绝大多数类别适合用水平矩形框。挖掘机吊臂、装载机铲斗虽然会呈现长条形态但水平框带来的背景噪声在可接受范围内而旋转框标注会显著增加人工成本还会和 YOLO 系列默认检测头不兼容。唯一需要单独考虑的是塔吊塔身加上超长吊臂在监控画面里长宽比经常超过 10:1水平框会包含大量天空背景我一般把塔吊拆成“塔吊下部塔身”和“塔吊吊臂”两个独立类别互相不抢梯度。标注工具建议直接用 X-AnyLabeling 或 LabelImg。X-AnyLabeling 支持半自动分割模型辅助框选对挖掘机这类轮廓清晰的设备能省不少时间LabelImg 更轻量适合几十 GB 高清监控图做离线批量标注。框选时统一规则设备主体完全可见就按主体外包络标遮挡超过 40% 且无法判断类别时不标目标面积小于画面千分之一时除非能明显看出属于哪类否则也先跳过。3.2 把标注结果统一转成 YOLO 格式并校验边界坐标标注软件默认导出的格式通常是 XMLPascal VOC或 JSONCOCOYOLO 训练需要的是归一化的class_id x_center y_center width height文本文件中心坐标和宽高都除以图片宽高。下面这段脚本把 LabelIMG 导出的 XML 统一转成 YOLO 格式并剔除越界框import xml.etree.ElementTree as ET from pathlib import Path classes [excavator, loader, bulldozer, crane, dump_truck, tower_crane, roller, concrete_pump] xml_dir Path(annotations_xml) label_dir Path(labels) label_dir.mkdir(exist_okTrue) for xml_path in xml_dir.glob(*.xml): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue xmin float(obj.find(bndbox/xmin).text) ymin float(obj.find(bndbox/ymin).text) xmax float(obj.find(bndbox/xmax).text) ymax float(obj.find(bndbox/ymax).text) # 过滤完全越界或面积太小的目标 if xmax xmin or ymax ymin: continue dw 1.0 / img_w dh 1.0 / img_h cx ((xmin xmax) / 2.0) * dw cy ((ymin ymax) / 2.0) * dh bw (xmax - xmin) * dw bh (ymax - ymin) * dh lines.append(f{classes.index(name)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) label_name xml_path.stem .txt (label_dir / label_name).write_text(\n.join(lines), encodingutf-8)转换脚本里的classes列表顺序就是最终训练时的类别顺序一旦模型开始训练这个顺序就不能再改。后续新增类别时只能追加到列表尾部否则已生成的标签文件全部错位。坐标做了 1.0 / 宽高归一化模型读取时无需再关心原始分辨率。xmax xmin的过滤条件看着低级但标注软件偶发会导出坐标反转的脏矩形放进训练会直接拉低损失。3.3 按工地场景划分 train/val/test再用 YAML 统一描述数据划分不能完全随机切否则同一个监控视频里的连续帧会被同时分到训练集和验证集验证指标虚高。我习惯按视频源或工地项目文件级别的哈希做分组保证同一工地画风只出现在一个集合里。比例按 8:1:1测试集只在全部训练完成后碰一次用来估算真实部署效果。import random import shutil from pathlib import Path random.seed(2024) img_paths sorted(Path(images_pool).glob(*.jpg)) random.shuffle(img_paths) n_total len(img_paths) n_train int(n_total * 0.8) n_val int(n_total * 0.1) for phase, start, end in [(train, 0, n_train), (val, n_train, n_train n_val), (test, n_train n_val, n_total)]: out_img Path(fdatasets/engineering_machinery/images/{phase}) out_lbl Path(fdatasets/engineering_machinery/labels/{phase}) out_img.mkdir(parentsTrue, exist_okTrue) out_lbl.mkdir(parentsTrue, exist_okTrue) for img_path in img_paths[start:end]: shutil.copy(str(img_path), str(out_img / img_path.name)) label_path Path(labels) / (img_path.stem .txt) if label_path.exists(): shutil.copy(str(label_path), str(out_lbl / label_path.name))划分完成后工程机械识别数据集的标准目录结构如下datasets/engineering_machinery/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml需要对齐这个目录路径。写绝对路径还是相对路径取决于训练机环境我推荐把datasets/engineering_machinery放在代码仓库同级然后data.yaml写相对路径path: ./ train: images/train val: images/val test: images/test names: 0: excavator 1: loader 2: bulldozer 3: crane 4: dump_truck 5: tower_crane 6: roller 7: concrete_pumppath字段指向datasets/engineering_machinery所在目录。训练机环境不同绝对路径很容易造成跨机器迁移时报错相对路径对团队协作更友好。4. 解决工程机械小目标与样本不均衡增强、贴图与切图4.1 先做样本统计再决定增强策略拿到标注好的工程机械识别数据集第一件事不是开训练而是统计每个类别的框数量、平均宽高比和像素面积。一个挖掘机在 1920×1080 监控图里可能只有 40×30 像素另一个在同一画面里占了 400×600 像素两类样本混在一起训练模型最后往往只学会识别近距离大目标这是施工安全数据集成型率低的最常见原因。from pathlib import Path import collections label_dirs [ Path(datasets/engineering_machinery/labels/train), Path(datasets/engineering_machinery/labels/val), ] category_names [excavator, loader, bulldozer, crane, dump_truck, tower_crane, roller, concrete_pump] area_bins collections.defaultdict(list) for label_dir in label_dirs: for txt_path in label_dir.glob(*.txt): img_name txt_path.stem .jpg for line in txt_path.read_text().strip().splitlines(): cls_id, cx, cy, bw_n, bh_n map(float, line.split()) # 用 800x800 作为参考分辨率计算像素面积 pixel_w int(bw_n * 800) pixel_h int(bh_n * 800) area_bins[int(cls_id)].append((img_name, pixel_w * pixel_h)) for cls_id, samples in area_bins.items(): areas [s[1] for s in samples] small_ratio len([a for a in areas if a 32 * 32]) / max(len(areas), 1) print(f{category_names[cls_id]}: {len(areas)} samples, farea 32x32 ratio{small_ratio:.1%})如果某个类别的small_ratio超过 40%说明该类的样本距离普遍偏远。后续增强策略就要优先提升小目标占比而不是盲目加旋转增强。下面的表格给出了几条可直接套用的处理策略现象工程机械场景原因增强与处理策略某类别样本数远低于其他类吊车、混凝土泵车在普通工地出现频率低Copy-Paste 贴图合成复制该目标到有其他设备的背景图小面积目标占比低监控焦距固定、设备作业范围集中大图切块训练把 1920×1080 切成多个 640×640 patch类别互检混淆装载机与挖掘机在远距离下轮廓相似做 Cutout 局部遮挡迫使模型关注完整轮廓画面全部白天晴天采集周期短、季节单一对整图做亮度扰动或用简单雾化模拟清晨工地4.2 Copy-Paste 增强把小样本设备贴到真实工地背景对小样本类别最有效的方法不是翻转而是从含该设备的原始图中抠出目标贴到另一张没有该设备的工地背景里。工程机械是刚性结构透视变形不严重贴图后只要保持尺度比例合理模型不会学到异常纹理。import random import cv2 import numpy as np roi_image cv2.imread(excavator_source.jpg) roi_xmin, roi_ymin, roi_xmax, roi_ymax 100, 200, 260, 420 # 从标签中读出的目标框 bg_image cv2.imread(background_loader_work.jpg) obj roi_image[roi_ymin:roi_ymax, roi_xmin:roi_xmax] scale random.uniform(0.4, 0.9) obj cv2.resize(obj, (int(obj.shape[1] * scale), int(obj.shape[0] * scale))) h_bg, w_bg bg_image.shape[:2] h_obj, w_obj obj.shape[:2] x_pos random.randint(0, w_bg - w_obj) y_pos random.randint(0, h_bg - h_obj) roi bg_image[y_pos:y_pos h_obj, x_pos:x_pos w_obj] mask np.full((h_obj, w_obj), 0, dtypenp.uint8) gray_obj cv2.cvtColor(obj, cv2.COLOR_BGR2GRAY) mask[gray_obj 20] 255 # 粗略过滤物体边缘黑边 bg_image[y_pos:y_pos h_obj, x_pos:x_pos w_obj] cv2.bitwise_and( obj, obj, maskcv2.bitwise_not(mask)) cv2.bitwise_and(roi, roi, maskmask) cv2.imwrite(synthetic_augmented.jpg, bg_image)这段代码演示的是最基本的像素级贴图实际生产中可以换成ultralytics自带的copy_paste增强或者专门做前景分割后贴图。贴图时目标框的原始坐标会随缩放平移变化需要同步写入新标签文件中。不在代码里写死是因为不同工程机械目标的空腔和履带区域灰度范围不同mask[gray_obj 20]只是初始阈值贴图效果要在预览图上人工抽样检查。4.3 对高分辨率监控图做 Tile 切图把远处工程机械放大工程机械识别数据集里的监控原图普遍是 1920×1080 或 2560×1440直接缩放成 640×640 输入 YOLO一个小目标会被压缩到十几个像素。常见做法不是改模型而是把大图按重叠窗口切成小块每块 640×640再单独训练。切图后原本 40×30 像素的挖掘机可能在一个 patch 里变成 120×90 像素检测器能学到的边缘纹理显著增加。import cv2 from pathlib import Path tile_size 640 overlap 80 img cv2.imread(monitor_1920.jpg) img_h, img_w img.shape[:2] valid_labels parse_label(monitor_1920.txt) # 返回 [cls, cx, cy, w, h] 列表 total_w (img_w - tile_size) // (tile_size - overlap) 1 total_h (img_h - tile_size) // (tile_size - overlap) 1 for row in range(total_h): for col in range(total_w): x_start col * (tile_size - overlap) y_start row * (tile_size - overlap) patch img[y_start:y_start tile_size, x_start:x_start tile_size] new_labels [] for cls_id, cx, cy, w, h in valid_labels: x_min_tile (cx - w / 2) * img_w - x_start x_max_tile (cx w / 2) * img_w - x_start y_min_tile (cy - h / 2) * img_h - y_start y_max_tile (cy h / 2) * img_h - y_start if x_max_tile 0 or y_max_tile 0 or x_min_tile tile_size or y_min_tile tile_size: continue clipped_x_min max(0, x_min_tile) clipped_x_max min(tile_size, x_max_tile) clipped_y_min max(0, y_min_tile) clipped_y_max min(tile_size, y_max_tile) if clipped_x_max - clipped_x_min 8 or clipped_y_max - clipped_y_min 8: continue new_cx ((clipped_x_min clipped_x_max) / 2) / tile_size new_cy ((clipped_y_min clipped_y_max) / 2) / tile_size new_w (clipped_x_max - clipped_x_min) / tile_size new_h (clipped_y_max - clipped_y_min) / tile_size new_labels.append(f{int(cls_id)} {new_cx:.6f} {new_cy:.6f} {new_w:.6f} {new_h:.6f}) if new_labels: # 保存 patch 和相应 label切图时overlap取 80 像素是为了让跨越切图边界的设备至少在一个 patch 里保持完整。target 被切成碎片时那些只有 8 像素的残片会被过滤掉避免训练出一堆 “半台挖掘机”的假阳性特征。切图比例建议 0.6 到 1.0 之间全部切图会让目标重复训练次数大幅增加训练时间变长收益反而下降。5. 工程机械识别数据集的 YOLOv8 训练与指标验证5.1 训练命令与关键参数设置把工程机械识别数据集切成 YOLO 目录结构后用 YOLOv8 训练自己的数据集只需要一条命令。先选模型规模固定摄像头部署建议用yolov8s.pt检测速度和精度均衡如果数据量已经超过 3000 张、每类框数超过 1500 个可以换yolov8m.pt获取更高的召回率。无人机俯拍画面目标较小yolov8m比yolov8n的小目标特征提取能力强出一截但显存占用也大约翻倍。yolo detect train \ modelyolov8s.pt \ datadatasets/engineering_machinery/data.yaml \ epochs120 \ imgsz640 \ batch16 \ lr00.005 \ mosaic1.0 \ close_mosaic10 \ patience20imgsz640是输入尺寸如果原始数据集里小目标占比高可以改成imgsz960但训练和推理速度都会下降约 50%。close_mosaic10表示最后 10 个 epoch 关闭 Mosaic 增强避免大量拼接背景干扰模型收敛。patience20表示验证集指标连续 20 轮不提升就停止工程机械数据集类别多、样本分布不均匀早停阈值设太小容易让模型停在局部最优。5.2 评估指标mAP50、mAP50-95、每类 AP 与混淆矩阵训练结束后用验证集评估不能只看一个 mAP。YOLO 默认输出两组指标其中mAP50是 IoU 阈值 0.5 下的均值平均精度mAP50-95是 0.5 到 0.95 每间隔 0.05 共 10 个 IoU 阈值的均值。工程机械识别数据集里大量目标是远端小目标mAP50-95比mAP50更敏感能说明检测框定位是否精准这也是 YOLO 目标检测流程里最常被忽略的部分。yolo detect val \ modelruns/detect/train/weights/best.pt \ datadatasets/engineering_machinery/data.yaml \ conf0.001 \ iou0.6conf0.001是验证阶段把置信度阈值压到最低保证召回率计算完整iou0.6是 NMS 的 IoU 阈值工程机械之间遮挡少一般 0.5 到 0.7 都合理。验证结果会生成confusion_matrix.png这张图上最值得关注的是挖掘机、装载机、推土机三者之间有没有大量互相误检。这三类设备都有履带或宽体轮廓远距离下确实会出现类别混淆。一般来说工程机械识别数据集在施工场景验收时大目标类别挖掘机、装载机、自卸车的 mAP50 应达到 0.85 以上远处小目标较多的监管应用mAP50 达到 0.7 以上才具备上线价值。下表是我个人常用的验收基线只能在同等数据分布下做参考部署条件模型mAP50mAP50-95单卡 640 推理帧率边缘盒低算力yolov8s≥0.75≥0.4540~60 FPS工地服务器yolov8m≥0.80≥0.5225~40 FPS离线批量分析yolov8l≥0.84≥0.5815~25 FPS5.3 用一次 Batch 预测检查标注质量而不是只看 loss 曲线训练 loss 降到平稳不代表数据集没问题。工程机械识别数据集最常见的坑是标签类别错位有人在标装载机时把铲斗单独框出来标成了挖掘机。这种噪声在 loss 曲线上只会表现为轻微震荡但混淆矩阵里对应类别的误检会明显异常。我的做法是训练完成后随机挑 8 张验证集图片用模型输出带标签和置信度的预测图from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcedatasets/engineering_machinery/images/val, conf0.3, saveTrue, save_txtTrue, projectruns/predict/sample_val, ) for res in results[:3]: for box in res.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(res.path.split(/)[-1], cls_id, round(conf, 2), [round(x, 0) for x in xyxy])把打印出的cls_id和xyxy与原图上目标做比对如果发现验证集里已经存在标注遗漏或类别错误优先修正验证集而不是急着调整训练参数。验证集是“标尺”标尺本身不准再换损失函数也没有意义。6. 建立数据集迭代闭环把漏检和误检重新流回训练集工程机械识别数据集不会一次成型工地现场的光照、扬尘、手机拍照和无人机视角会不断挑战模型边界。有效做法是把模型在新增监控片段上的漏检和误检抓出来人工确认后回流到数据集下一轮训练自然提升。先在未参与训练的工地监控视频上跑推理过滤出置信度在 0.3 到 0.6 之间的预测框。这个区间是模型“犹豫”的区域玩家等设备的低置信度预测通常就是漏检来源yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcestreams/new_site.mp4 \ conf0.3 \ iou0.5 \ save_txtTrue \ save_cropFalse \ projectruns/predict/hard_examples导出后把预测框画回原图人工批量确认框住的确实是工程机械但没有 GT 标注说明原始数据集缺这类视角模型标了错误类别说明类别边界样本不足。把这些问题图转成训练样本补充到train或val对应目录里。每轮只回流最难的那 200 到 500 张不要在图像增强上继续堆量让模型在工程机械识别上的提升从“学习更多纹理”变成“修正决策边界”。除了难例回流还要在数据集目录里维护一个metadata.csv记录每张图片的来源、采集时间、工地类型、宽高、目标数量。这个文件看起来不影响训练但在模型出现地域性退化时可以从工地类型列快速定位是不是新增样本和原分布差异过大。发布数据集版本时也建议直接给 Git tag例如v1.0是冷启动基线v1.1加入夜间红外样本v1.2修正吊车类别混淆。版本记录里的改造点比模型权重本身更能说明工程机械识别数据集为什么有效。本文还有配套的精品资源点击获取
返回列表