
简介这套YOLO格式的工业液滴气泡多目标检测数据集面向工业视觉、流体力学与人工智能检测开发场景聚焦气液两相流中单液滴、单气泡、液泡混合体及高密度液滴群的定位识别适合化工能源管道监测、流体实验、水体净化等方向。资源共2000个文件整体大小约26.32MB以1971个txt标注文件为主体另含27张jpg图像、1个yaml配置和1份docx说明文档数据划分为训练集1219张、验证集752张覆盖不同光照、拍摄角度及稀疏与密集等流体状态。已有335人学习。数据集标注经流体力学专家校验四级分类体系严格包含气泡破裂、液滴聚合等动态过程的连续帧数据可直接用于YOLOv12等模型的训练与验证也可支持密集小目标识别、多尺度目标评测以及两相流数值模拟、工业数字孪生视觉模块等科研与工程任务。1. 工业液滴气泡多目标检测数据集.zip一个压缩包里装的不只是图片拿到“工业液滴气泡多目标检测数据集.zip”这个名字第一反应往往是解压看一下扔给 yolov8 开始训练。真正在产线上摸过一轮的人才明白这个压缩包里装的不是“图片加标签”而是一整套关于目标怎么定义、边界怎么画、分布怎么切、格式怎么转的博弈。工业场景里的液滴和气泡出现在喷墨打印、喷淋降温、微流控反应、电池注液这类环节目标小、边缘糊、反光强背景还有纹理干扰通用检测模型直接拿过来训练mAP 常常很难看。这类数据集的价值不在“有多少张图”而在标注规范、分布设计和格式转换这些看不见的工序。这篇文章就按一线工程师处理这类数据集的常规做法把从解压到训练、再到把数据资产做厚的路径走一遍。适合准备做工业视觉检测、用 yolov8 或 mmrotate 训练自己数据的算法工程师。2. 数据集的结构与标注多目标检测的关卡在数据工艺2.1 先看压缩包结构再谈模型训练拿到 zip 之后我一般不会急着解压到当前目录而是先列一下压缩包内容心里有个底。unzip -l 工业液滴气泡多目标检测数据集.zip | head -60这条命令会把 zip 内的文件清单打印出来。重点看三件事images和labels是否分目录标签文件是.txt还是.json有没有train.txt / val.txt这类划分文件。常见的数据集组织方式是工业液滴气泡多目标检测数据集/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── train.txt ├── val.txt └── README.md如果看到的是这种结构说明数据集已经按 YOLO 惯例组织好了。注意看README.md里写了什么类别名、标注坐标系、拍摄环境、是否包含遮挡目标。这些信息决定了你的预处理和后处理策略不要跳过。2.2 液滴气泡为什么要单独定义标注规范通用目标检测数据集里目标是“人、车、猫”边界相对清楚。液滴和气泡不一样透明、边缘与背景亮度接近有时连人眼都分不清边界。这带来三个标注特异点直接决定检测上限。第一个是边界定义。气泡在光源照射下会出现高光环标注框到底框到高光内侧还是外侧不同标注员的习惯不同会导致同一个目标的框大小有 10% 到 20% 的波动。处理这类数据集我会在预处理阶段统一做一次缩放把所有框按原始标注的 0.95 系数向中心收缩消除标注员之间的边缘冗余。第二个是目标尺寸分布。工业场景中液滴常常只有十几个像素属于典型小目标。小目标在标注时容易被漏标一个漏标的前景会变成训练时的难负样本直接把 recall 拉下来。评估一个数据集能不能用先看你标注里小于 32×32 像素的框占比超过 40% 就要在训练时用小目标增强策略而不是靠加大 mosaic 碰运气。第三个是密集遮挡。喷雾场景中相邻液滴几乎贴在一起重叠区域大这时候标注框的严格边界比类别本身更敏感。密集目标场景建议用旋转框标注而不是普通水平框否则 NMS 阶段会产生大量误抑制。这个问题在第 5 章展开。2.3 标注格式互转txt 与 json 的桥梁大部分工业数据集交付时给的是 YOLO 格式的 txt每行一个目标结构是class_id x_center y_center width height坐标都是相对图片宽高的归一化值。这种格式的好处是简单一个文件夹加同名 txt 就完成了标注。坏处是扩展性差没有 segmentation、没有属性字段、没有数据集级元信息。如果你后面要切换检测框架比如从 YOLO 换到 mmrotate 或 Detectron2需要转成 COCO json。coco2017 数据集结构之所以成为事实标准就是因为它把 images、annotations、categories 拆成三个数组注解字段可扩展。下面这段代码把 YOLO txt 转成 COCO 格式import json import os from PIL import Image def yolo_txt_to_coco(images_dir, labels_dir, class_names, output_json): coco { images: [], annotations: [], categories: [{id: i, name: n} for i, n in enumerate(class_names)] } ann_id 0 for img_name in sorted(os.listdir(images_dir)): if not img_name.endswith((.jpg, .png, .bmp)): continue img_path os.path.join(images_dir, img_name) with Image.open(img_path) as img: width, height img.size coco[images].append({ id: len(coco[images]), file_name: img_name, width: width, height: height }) txt_path os.path.join(labels_dir, os.path.splitext(img_name)[0] .txt) if not os.path.exists(txt_path): continue with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id, x_c, y_c, w, h map(float, parts) x (x_c - w / 2) * width y (y_c - h / 2) * height w_pix, h_pix w * width, h * height coco[annotations].append({ id: ann_id, image_id: coco[images][-1][id], category_id: int(cls_id), bbox: [x, y, w_pix, h_pix], area: w_pix * h_pix, iscrowd: 0 }) ann_id 1 with open(output_json, w) as f: json.dump(coco, f, indent2) yolo_txt_to_coco(images/train, labels/train, [droplet, bubble], train_coco.json)这段代码把 txt 中的归一化坐标乘上图片宽高转成 COCO 需要的像素坐标。iscrowd置 0 表示普通目标如果原数据集标了密集群体的轮廓可以把iscrowd置 1在评估时跳过该框。转之前先统计 txt 里的坐标是否都在 0 到 1 之间这一步能拦掉 80% 的文件格式错误。转换后的 json 用任何支持 COCO 的可视化脚本打开检查一遍再进入下一步。2.4 划分数据集按来源分组而不是随机打散模型训练前要做 train/val 划分最容易犯的错是直接random_split。工业数据集往往是连续采样的同一段视频抽帧得到的几十帧背景几乎一样液滴位置只差几个像素。如果这些相似帧同时落在 train 和 val 里验证集指标会虚高实际部署到新工位就露馅。正确的做法是按采样来源分组。数据集的目录如果按日期或机台号组织比如20240112_camera1_shot01/就把相同前缀的图片分到同一组再按组划分import os import random images os.listdir(images) groups {} for img in images: group img.split(_)[0] _ img.split(_)[1] # 按日期和相机分组 groups.setdefault(group, []).append(img) all_groups list(groups.keys()) random.Random(42).shuffle(all_groups) val_group_count max(1, int(len(all_groups) * 0.2)) val_groups set(all_groups[:val_group_count]) with open(train.txt, w) as ft, open(val.txt, w) as fv: for group, imgs in groups.items(): for img in imgs: line os.path.join(images, img) \n if group in val_groups: fv.write(line) else: ft.write(line)按组划分后val 里的场景分布和 train 不再重叠训练的泛化评估才有意义。注意random.seed固定下来否则同一份数据每次划分结果不同后续实验对比就不公平了。3. 解压后的第一件事用数据体检脚本把 zip 变成可信任的训练集3.1 校验完整性与目录一致性从网上下载的数据集尤其是 zip 这种压缩包第一件事不是解压而是校验。zip 文件在传输过程中可能截断也可能在 Windows 上解压后出现中文文件名乱码。我的常规流程是两步unzip -t 工业液滴气泡多目标检测数据集.zip-t参数逐文件测试 zip 的 CRC 校验和只要有一个文件报错就说明压缩包损坏后面训练出的模型没有任何参考价值。测试通过后再解压unzip 工业液滴气泡多目标检测数据集.zip -d ./dataset解压后我会跑一个一致性检查确认每一张图片都有对应的标注文件每一个标注文件都有对应的图片。图片和 txt 数量不一致通常是打包时丢文件或者后期人工删除图片时没同步删标签。这一步用一行 Python 就能做完import os from pathlib import Path img_dir Path(dataset/images/train) lbl_dir Path(dataset/labels/train) img_files {p.stem for p in img_dir.glob(*.jpg)} | {p.stem for p in img_dir.glob(*.png)} lbl_files {p.stem for p in lbl_dir.glob(*.txt)} print(图片无标签:, img_files - lbl_files) print(标签无图片:, lbl_files - img_files)输出里如果出现文件先检查这些文件是否真的无用再决定是补标注还是从训练集剔除。3.2 标注分布统计类别、尺寸、密度一个都不能少判定数据集质量最有效的手段是统计目标的类别分布和尺寸分布。下面是分析 YOLO 标注的核心脚本import os import numpy as np from collections import Counter def analyze_annotations(labels_dir, img_size_map): cls_counter Counter() sizes [] boxes_per_img [] aspect_ratios [] for txt_name in os.listdir(labels_dir): if not txt_name.endswith(.txt): continue img_w, img_h img_size_map[txt_name[:-4]] count 0 with open(os.path.join(labels_dir, txt_name)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, xc, yc, w, h map(float, parts) w_pix, h_pix w * img_w, h * img_h sizes.append(max(w_pix, h_pix)) aspect_ratios.append(w_pix / max(h_pix, 1e-6)) cls_counter[int(cls)] 1 count 1 boxes_per_img.append(count) sizes np.array(sizes) print(类别分布:, dict(cls_counter)) print(目标像素尺寸: p50%.1f p90%.1f p99%.1f % ( np.percentile(sizes, 50), np.percentile(sizes, 90), np.percentile(sizes, 99))) print(每图目标数: p50%.1f p90%.1f % ( np.percentile(boxes_per_img, 50), np.percentile(boxes_per_img, 90))) analyze_annotations(dataset/labels/train, img_size_map)img_size_map是图片宽高字典由上一节读取图片尺寸时顺便生成。类别分布能直接反映类别不平衡程度如果气泡样本数是液滴的 10 倍训练时要设置cls损失权重。尺寸分布里的 p90 如果小于 40 像素说明这是小目标数据集配imgsz960甚至1280比配640的效果好得多每图目标数则决定 NMS 的阈值和最大检测数上限。3.3 四类异常检查越界、空标签、坏图、错位数据集的“脏”主要体现在四类异常逐项排查第一坐标越界。归一化坐标中x_center width / 2 1.0或y_center height / 2 1.0这类框在裁剪时会触发数组越界训练直接崩溃。第二空标签。某些图片的 txt 是空文件图形上有目标但没标会成为持续的误检源头。第三图片损坏。某些 jpg 文件头被截断OpenCV 读取时报错。第四类别 id 错位。txt 里写的 class_id 超出了数据集的类别总数通常是两类数据合并时忘了改代号。一次性处理这些异常的脚本我放在一个函数里import cv2 import os import numpy as np def sanitize_labels(img_dir, lbl_dir, num_classes): for txt_name in os.listdir(lbl_dir): if not txt_name.endswith(.txt): continue img_path os.path.join(img_dir, txt_name[:-4] .jpg) if not os.path.exists(img_path): img_path os.path.join(img_dir, txt_name[:-4] .png) img cv2.imread(img_path) if img is None: print(f坏图: {img_path}) continue h, w img.shape[:2] lines [] with open(os.path.join(lbl_dir, txt_name)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, xc, yc, bw, bh map(float, parts) if cls num_classes: print(f类别越界: {txt_name} cls{cls}) continue if bw 0 or bh 0 or bw 1 or bh 1: continue if xc - bw/2 -0.05 or xc bw/2 1.05 or yc - bh/2 -0.05 or yc bh/2 1.05: xc min(max(xc, bw/2), 1 - bw/2) yc min(max(yc, bh/2), 1 - bh/2) lines.append(f{int(cls)} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if lines: with open(os.path.join(lbl_dir, txt_name), w) as f: f.write(\n.join(lines) \n) else: print(f空标签: {txt_name})num_classes必须和类别列表一一对应。坐标略超边界的情况我用 clamp 修正超出太多则删除该框。处理完之后把打印出的文件列表人工再过一眼确认不是大批量异常。注意批量修改标签前先把原始文件备份一份。cp -r labels labels_backup即可别用 git 来回切大文件数据集没必要。3.4 可视化抽检画框看图比任何指标都直接统计指标只能告诉你“数据长什么样”画框叠加图才能告诉你“标注对不对”。这一步通常用 OpenCV 批量画框输出到单独的支持目录python draw_boxes.py --images dataset/images/val --labels dataset/labels/val --output dataset/check_visualdraw_boxes.py的核心逻辑是读一张图、读同名 txt、cv2.rectangle画框、cv2.imwrite写出。关键参数是--max_samples建议每类随机抽 30 张不要全量画时间成本高且信息冗余。抽检时重点看三类问题框有没有压到液滴的反射光斑上两个相邻目标是否被画成一个大框背景纹理滤网、管道接缝有没有被误标为目标。这三类问题在统计脚本里看不出来但画出来一眼就能发现。4. 用 yolov8 在自己的数据集上完成一个可评估的训练闭环4.1 把数据集接进 ultralytics一个 yaml 文件的事数据体检完毕接下来进入 yolov8 训练自己的数据集的环节。ultralytics 的训练入口只认一个配置 yaml内容是路径、类别名、类别数# droplet_bubble.yaml path: /data/industrial_droplet # 数据集根目录 train: images/train # 相对 path 的训练集目录 val: images/val # 相对 path 的验证集目录 names: 0: droplet 1: bubblepath建议写绝对路径因为训练脚本跨机器迁移时相对路径解析容易出错。train和val指向图片目录即可yolov8 会自动去同名labels目录找 txt。如果标签目录叫annotations而不是labels需要建一个软链接ln -s /data/industrial_droplet/annotations /data/industrial_droplet/labels这一步经常被忽略报错却是“AssertionError: Label not found”排查半天发现只是目录名不匹配。yaml 里names的序号必须和 txt 里第一列的 class_id 对应差一个位子就会把液滴当成气泡训练。4.2 训练时真正值得调的参数接好数据后合理做法是先跑一个 20 轮的 baseline不要动任何增强参数。这轮训练的目的不是刷指标而是确认数据链路通畅、loss 能正常下降、验证集没有报错。baseline 通过后再往下调参数参数默认值液滴气泡场景建议理由imgsz640960 或 1280目标偏小放大输入分辨率等效于丰富小目标信息batch16按显存决定调大优先小目标对 batch size 敏感过小导致梯度噪声大mosaic1.00.5~1.0液滴之间本来就近mosaic 截断后目标更碎降低剪切比例hsv_h0.0150.0~0.005工业场景颜色变化小过度色偏会让模型学到假的颜色特征lrf0.010.005液滴场景收敛慢末期小学习率帮助跑稳patience10030数据量大时提前收敛没必要等满轮次启动训练的命令yolo train modelyolov8s.pt datadroplet_bubble.yaml imgsz960 batch32 epochs100 patience30 projectruns/droplet nameexp1yolov8s.pt是预训练权重在 COCO 上收敛过迁移到工业小目标数据上效果比随机初始化好很多。project和name参数控制输出目录建议每次实验换一个 name方便对比。epochs 设为 100 而不是 300工业数据集通常几万张100 轮足够收敛多出的轮次只是过拟合。4.3 训练后看这四张图而不是只看 mAP训练结束输出目录runs/droplet/exp1下会有results.png、confusion_matrix.png、PR_curve.png、val_batch*.jpg几张图。这些图比一个孤零零的 mAP 数字有用得多。results.png里的train/box_loss和val/box_loss是关键。两个 loss 同步下降且没有明显回升说明训练正常val loss 先降后升而 train loss 还在降就是过拟合回退到早停的权重文件。confusion_matrix.png里重点关注背景background列如果液滴那一行的 background 响应值高说明模型把液滴当成了背景通常是漏标太多导致。把漏标样本补进数据集比调任何超参数都有效。PR_curve.png里曲线和坐标轴围成的面积就是 AP看曲线右下角的“长尾”部分尾越长说明置信度阈值对结果的影响越大。部署时把置信度阈值调到 0.3 还是 0.7从这里看。val_batch*.jpg是验证集上的预测叠加图看误检集中在哪里。工业场景里常见的是把滤网纹理、玻璃管边缘、气泡反光高光误检成小目标。如果是反光类误检在增强里加gaussian_blur和dropout比加数据更节省时间。4.4 训练失败的常见原因与排错顺序训练过程中报错按这个顺序排查能省下大量时间第一CUDA out of memory。优先降 batch而不是降 imgsz因为小 batch 对精度影响小于小分辨率。第二AssertionError: image not found检查 yaml 里路径是否写对注意 ultralytics 要求路径里不能有中文。第三loss 是nan。在 yaml 中补充seed: 42多数情况是增强爆了梯度。第四mAP 一直是 0。打开训练日志看val/box_loss是否下降如果下降但 mAP 为 0几乎可以断定是标签里的类别序号和names对不上。注意工业数据集解压后如果放在中文路径下ultralytics 的某些版本解析不了带中文的绝对路径。整个项目目录用英文命名是成本最低的防坑手段。5. 最后三件事把 zip 数据集的剩余价值榨干5.1 推理阶段用 TTA不花标注成本换两个点训练好的模型往往还有一点提升空间测试时增强TTA是最省事的手段。yolov8 的推理接口直接支持yolo predict modelruns/droplet/exp1/weights/best.pt sourcetest_images/ imgsz960 ttaTrueTTA 会对每张图做多尺度缩放和翻转推理再融合结果小目标场景通常能带来 1 到 3 个点的 mAP 提升代价是推理时间乘三到四倍。线上实时检测用不起 TTA但离线抽检和产线联调阶段用 TTA 评估模型上限非常划算。5.2 液滴贴壁检测从水平框切换到旋转框如果数据集里大量气泡贴附在管壁、液滴沿边缘滑动水平框会同时框住目标和背景NMS 阶段相互抑制严重。这类场景有个专门的说法叫 rotated object detectionmmrotate 就是专门干这个的框架。处理过程不复杂先把水平框标注转成旋转框标注格式是class_id xc yc w h angle再按 mmrotate 的 DOTA 格式组织数据。python tools/analysis/rotate_dataset.py \ --data /data/industrial_droplet \ --format yolo \ --out /data/industrial_droplet/rotated转换后训练的命令与普通检测类似只是模型换成RotatedRetinaNet或Oriented R-CNN。注意旋转框数据集的验证集划分必须和水平框数据集完全一致否则两组实验没法对比。多目标检测做到旋转框这一层基本就到工业视觉数据标注的极限了往下的收益很有限。5.3 用自训练把静态 zip 变成动态数据资产静态数据集训练出的模型上线三个月后会因为光源老化、喷嘴磨损逐渐失效。把 zip 数据集做厚的工程做法是自训练用当前 best 模型去跑新采集的未标注视频生成伪标签再用低置信度过滤和人工抽检修正。yolo predict modelbest.pt sourceproduction_videos/ imgsz960 conf0.35 \ save_txtTrue save_confTrueconf0.35和save_confTrue是关键伪标签里附带置信度分值后续用脚本过滤掉低于 0.8 的框。剩下高置信度框直接入库作为下一轮训练数据低置信度部分交给人工挑几张错图补充标注。这样每一轮生产数据回流都会让模型在真实环境下的分布上再适应一轮精度不掉部署寿命拉长。这也是数据质量要求及评价方法在工业界落地最实在的形态数据集不是一次交付暂停而是一条不断回收、不断验证的流水线。本文还有配套的精品资源点击获取