简介:这是一套面向目标检测算法开发与流体力学研究的YOLO格式液滴检测数据集,包含1,918张工业级图像,分成训练集1,342张、验证集576张,覆盖单液滴与多液滴交互、聚合飞溅等动态形态,可直接用于工业流体监测、化学实验分析、农业喷雾优化及医疗雾化设备评估等模型训练。包内共2,000个文件,以1,918个txt边界框标注文件为核心,配80张jpg原图、1个yaml类别配置和1个docx数据说明,压缩包整体17.26MB,结构紧凑,可无缝接入YOLOv5、YOLOv8、YOLOv12等主流框架。所有标注遵循YOLO规范化坐标体系,边界框精确匹配液滴轮廓,数据采集自真实工业检测场景,涵盖不同光照条件、背景复杂度与拍摄角度,包含液滴重叠、高速运动等挑战性案例,最小可检测单元达微小液滴级别,能有效提升模型在复杂流体环境下的鲁棒性与泛化能力。目前已有53人学习下载,适合具备一定目标检测基础、需要高质量行业数据集进行算法验证或课题研究的工程师与研究者。
1. 液滴检测目标检测数据集:开箱能训,但别急着欢呼
做液滴检测的人大多是从喷墨打印、微流控、喷雾粒径分析这几个方向过来的,手里攒了一堆高速相机拍的液滴图,但一聊到标注就头疼:液滴边缘半透明、反光强烈、飞行速度快导致运动模糊,一个框怎么画都像在赌博。所以看到一个打包好的「液滴检测目标检测数据集.zip」,第一反应肯定是「能直接训吗」。我的回答是:能,但你必须先花半小时把它翻个底朝天。这类数据集通常是以 YOLO 或 VOC 格式打包的标注数据,里面真正值钱的是那几千个手工框,而不是那些截图。这篇文章我从解压、体检、训练到踩坑一步步拆开讲,保证你拿到 zip 后知道第一步敲什么命令,也知道哪些地方最容易翻车。
2. 拆开数据集先做三件事:解压、看结构、验标注质量
2.1 解压后先梳理目录结构:images 与 labels 的对应关系
拿到 zip 后,我习惯先不开 IDE,直接开终端看一眼目录长什么样。
unzip 液滴检测目标检测数据集.zip -d droplet_data cd droplet_data find . -maxdepth 2 -type d | sort这个命令把压缩包解压到 droplet_data 目录,然后列出两层以内的所有目录,先搞清楚它到底是 YOLO 格式(images/ 和 labels/ 平级)、VOC 格式(JPEGImages/ 和 Annotations/),还是把标签和图片混在一起的散装结构。
大多数情况下,你在终端里看到的是类似下面的结构:
droplet_data/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml这里有个关键点:如果 images 和 labels 是平级的,并且里面还分好了 train/val/test,说明对方已经按 YOLO 训练的习惯整理过了;如果只有一个 images 目录和一个 labels 目录,没有 train/val/test 切分,这说明你拿到的是「未切分原档」,后面需要自己划分。
逻辑说明:目标检测数据集的落地路径,第一步永远是「确认格式」。YOLO 格式的标签是 txt 文件,每个文件对应一张同名图片,每行是一个目标——格式是「类别 x_center y_center width height」,四维数据都是相对于图片宽高的比例值,取值 0 到 1。而 VOC 格式是 XML 文件,存的是绝对像素坐标。两种格式差了十万八千里,后续训练脚本完全不互通。
参数说明:如果解压后发现根本没有 data.yaml,你需要自己写一个,内容一般是这样:
# data.yaml train: ./images/train val: ./images/val nc: 1 names: ['droplet']nc 表示类别数,液滴检测通常就是一个类,除非数据里还标了卫星液滴或气泡。names 的列表顺序必须和标签文件里第一个数字对应,不是你想写啥就写啥。
2.2 验证标注质量前,先看懂液滴标注的特殊性
很多人拿到数据集的第一步是直接扔进 YOLO 里训练,然后发现 loss 曲线抖得像心电图。我现在的习惯是:先随机抽 30 张图,把标注框直接画在图上人工看一遍。因为液滴检测的标注质量,比一般的目标检测更容易出问题。
常见的液滴标注问题有三个:框尺寸极小、框内含大量背景、同一张图上液滴大小差异悬殊。第一点尤其要命——液滴在高速相机画面里可能只有 10×10 像素,这样的小目标在 YOLO 的默认配置下很容易被当噪声忽略掉。
用 Python 脚本抽查一下标签文件里的框尺寸分布:
import os label_dir = 'labels/train' file_list = os.listdir(label_dir) sizes = [] for fname in file_list: if not fname.endswith('.txt'): continue with open(os.path.join(label_dir, fname), 'r') as f: for line in f: parts = line.strip().split() w = float(parts[3]) h = float(parts[4]) sizes.append((w, h)) # 找出哪些框小于 0.02(即图片宽高的2%) small = [(w, h) for w, h in sizes if w < 0.02 or h < 0.02] print(f"总共 {len(sizes)} 个框,其中小目标(<2%)有 {len(small)} 个")逻辑说明:这个脚本读的是 YOLO 格式的 txt 标签,parts[3] 是归一化后的框宽,parts[4] 是框高。如果某个框的宽或高小于 0.02,换算到一张 1920×1080 的图上,就是宽度小于 38 像素。对于液滴检测来说,这个尺寸已经属于小目标范畴,需要特殊处理。
参数说明:0.02 这个阈值不是死的。如果你用的是 640×640 输入,训练时图像会被缩放到 640,原本 38 像素的液滴在缩放后只有 12 像素左右。按照小目标检测的经验法则,小于 16×16 像素的目标建议用更高分辨率输入或者切片推理,后面第 5 章会细讲。
人工看图这一步不能省。写个小脚本把标注框画到图上,按「正常、偏小、模糊、反光」四个维度归档:
import cv2 img_path = 'images/train/droplet_001.jpg' label_path = 'labels/train/droplet_001.txt' img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path, 'r') as f: for line in f: parts = line.strip().split() cx, cy, bw, bh = map(float, parts[1:]) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 1) cv2.imwrite('check_001.jpg', img)逻辑说明:这段代码把 txt 里的归一化坐标换算成像素坐标,再画框。你不需要一张一张全看,随机抽 30 张就够发现规律。液滴检测的翻车点通常不在「标错了物体」,而在「框住了太多背景」——因为液滴边缘透明,标注人员容易把光晕圈进去。
2.3 用脚本做一轮自动体检:空标签、超界框、重复框
人工抽检之后,再做一轮自动化体检。目标检测数据集的三个常见毛病:空标签文件、超出边界的框、坐标错位的框。
import os label_dir = 'labels/train' empty_files = [] out_of_bound = [] for fname in os.listdir(label_dir): path = os.path.join(label_dir, fname) if os.path.getsize(path) == 0: empty_files.append(fname) continue with open(path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cx, cy, bw, bh = map(float, parts[1:]) if cx < 0 or cy < 0 or bw <= 0 or bh <= 0: out_of_bound.append(fname) break if cx + bw / 2 > 1 or cx - bw / 2 < 0: out_of_bound.append(fname) break if cy + bh / 2 > 1 or cy - bh / 2 < 0: out_of_bound.append(fname) break print(f"空标签文件:{len(empty_files)} 个") print(f"超界框涉及文件:{len(out_of_bound)} 个")逻辑说明:YOLO 格式的坐标是归一化的,所有值都应该在 0 到 1 之间。如果 cx - bw/2 < 0,说明框的左边界超出了图片左边缘。这类框必须处理,否则训练时模型会学到错误的边界预测。
处理策略:空标签文件直接删除或移到 backup 目录;超界框可以裁剪回边界,也可以直接删掉那一行。我之前遇到过一批液滴贴着图片边缘飞行的数据,大概 3% 的框越界——这时候最稳妥的做法是「clip 后保留」,因为那些液滴是真实存在的目标,值得让模型学到它。
注意:不要看到越界就直接删整张图。液滴检测的很多场景下,目标就是会出现在边缘——比如微流控通道的侧壁附近,一条边贴着视野边界。删图会让模型在边缘位置的泛化能力变得更差。
3. 用 YOLO 跑通液滴检测的最小训练流程
3.1 数据划分与 YAML 配置:手动切分比脚本拷文件更稳
拿到没有切分的原档时,很多人喜欢用一个脚本把文件按 8:1:1 比例复制到 train/val/test 三个目录。但这里有个坑:如果图片序列是连续拍摄的,直接按文件名哈希随机划分,会让同一段液滴飞行轨迹的连续帧同时出现在训练集和验证集里,造成数据泄露。验证集的 mAP 会虚高,部署到真实视频流里就露馅。
我一般会按照「按序列划分」的原则来做:先看一眼文件名,如果命名里有时间戳或帧序号(比如 frame_0001、frame_0002),就按序列号段切分——前 80% 的序列做训练,中间 10% 做验证,最后 10% 做测试。
import os import shutil import random src_images = 'images' src_labels = 'labels' out_base = 'split_data' all_images = sorted(os.listdir(src_images)) random.seed(42) # 如果文件名有序号,按序号排序后切片;如果没有,按固定随机种子划分 n_total = len(all_images) n_val = int(n_total * 0.1) n_test = int(n_total * 0.1) # 从尾部取 val 和 test,保证训练、验证、测试的时间段不重叠 test_set = all_images[:n_test] val_set = all_images[n_test:n_test + n_val] train_set = all_images[n_test + n_val:] for split, file_list in [('train', train_set), ('val', val_set), ('test', test_set)]: image_out = os.path.join(out_base, 'images', split) label_out = os.path.join(out_base, 'labels', split) os.makedirs(image_out, exist_ok=True) os.makedirs(label_out, exist_ok=True) for fname in file_list: shutil.copy(os.path.join(src_images, fname), os.path.join(image_out, fname)) label_name = fname.rsplit('.', 1)[0] + '.txt' src_label = os.path.join(src_labels, label_name) if os.path.exists(src_label): shutil.copy(src_label, os.path.join(label_out, label_name))逻辑说明:这段脚本的核心思路是「按时间顺序切分」,而不是按随机数切分。液滴检测的数据往往来自高速相机连拍,相邻帧之间的液滴位置、形态高度相似。如果训练集和验证集来自同一段视频的相邻帧,验证集的 mAP 会被严重高估。按头部取 test、中间取 val、尾部取 train 是我常用的办法,也可以反过来,但核心原则是三个集合的时间段彼此不重叠。
参数说明:random.seed(42) 保证了划分可复现。如果你拿到的是不同类型的液滴图像(比如有甲醇液滴、水滴、油滴),建议按「类型」划分而不是按时间划分——每个集合里都包含所有类型,但同一类型的不同样本之间要尽量独立。
划分完之后,确认 data.yaml 里的路径指向切分后的目录:
# data.yaml path: ./split_data train: images/train val: images/val test: images/test nc: 1 names: ['droplet']3.2 训练命令与关键参数:分辨率、batch、anchor 三个旋钮
数据准备好了,接下来就是训练。我的推荐配置是直接用 YOLOv8 或 YOLO11,因为液滴检测的场景相对简单,不需要上复杂的检测头。
yolo detect train \ model=yolo11n.pt \ data=data.yaml \ epochs=100 \ imgsz=1280 \ batch=16 \ patience=20 \ device=0 \ project=./runs \ name=drop_v1这是我能给的最小可用命令。解释一下几个参数的选择逻辑:
- imgsz=1280是液滴检测最重要的一步。液滴是典型的小目标,640 输入下许多液滴只有 8-10 像素,训练出来的模型会大量漏检。提到 1280 后,小液滴的像素量翻了四倍,检测效果提升非常明显。代价是显存占用变大——用 1280 输入、batch=16 的情况下,大概需要 12GB 以上的显存;你的卡只有 8GB 时,把 batch 降到 8 或者 4,别把 imgsz 降回 640。
- patience=20是早停轮数。液滴数据的训练集如果只有几千张图,通常跑到 60-80 轮就收敛了,过早停能省时间。
- model=yolo11n.pt是 nano 版本。液滴检测的输入图像往往分辨率很高,轻量级模型配合高分辨率输入,往往比重量级模型配合低分辨率输入效果更好。
训练时我要额外做的事:把 YOLO 默认的数据增强关一部分。液滴是刚性的、形状相对固定的目标,不像行人检测需要大量随机裁剪和旋转增强。液滴图像的典型特征是背景单一(通常是白色光源下的深色液滴,或者暗场下的亮液滴),过度旋转和颜色扰动反而会让模型学到错误的噪声。
yolo detect train \ model=yolo11n.pt \ data=data.yaml \ epochs=100 \ imgsz=1280 \ batch=16 \ patience=20 \ degrees=0 \ hsv_h=0.0 \ hsv_s=0.2 \ hsv_v=0.2 \ fliplr=0.5 \ project=./runs \ name=drop_v2逻辑说明:degrees=0 表示不做旋转增强——液滴在物理上不会水平旋转,它的形状就是圆或椭圆,旋转增强会产生不真实的训练样本。hsv_h、hsv_s、hsv_v 控制色调、饱和度、亮度的扰动幅度,液滴在不同光照条件下的颜色变化确实存在,这部分增强保留但幅度调小。fliplr=0.5 表示水平翻转,液滴飞行时左右对称,这个增强是安全的。
参数说明:以上增强参数的默认值在 YOLOv8 里是 degrees=0.0、hsv_h=0.015、hsv_s=0.7、hsv_v=0.4。我把饱和度扰动从 0.7 调到 0.2,是因为液滴检测的颜色信息本来就少,过度扰动会让模型把颜色当成关键特征。如果你训练时发现 loss 很难降下来,先检查是不是增强太激进了。
训练结束后,看两个东西:一个是 weights/best.pt 是否生成,另一个是 results.csv 里 val 的 mAP50 和 mAP50-95 曲线。
cat runs/drop_v2/results.csv | tail -203.3 训练后先看指标还是先看图:先看图,指标是后话
很多人训练完第一件事是看 mAP,然后发现 mAP50 到了 0.95 就觉得自己完事了。做液滴检测你不能只看 mAP,因为液滴通常太小,mAP 对检测框的微小偏移不敏感——一个框偏移 10 个像素,在 mAP50 里可能还是正样本,但实际应用中你的液滴计数可能就错了。
我的习惯是训练完立刻做一次推理,把预测结果可视化出来。
yolo detect predict \ model=runs/drop_v2/weights/best.pt \ source=split_data/images/test \ imgsz=1280 \ conf=0.25 \ save=True \ project=./predictions \ name=test_v2跑完之后去 predictions/test_v2 目录里看图。重点关注三类错误:漏检(图上有明显液滴但没框出来)、误检(背景纹理或反光被框了)、框偏(框的位置明显偏移了液滴中心)。
逻辑说明:conf=0.25 是置信度阈值。液滴检测的推理阶段这个值可以设在 0.25 到 0.3 之间,因为液滴的形态相对固定,模型给出的置信度通常比较高。如果很多液滴的置信度在 0.1 到 0.2 之间徘徊,说明训练还不够充分,或者前面说的数据增强配置有问题。
4. 液滴检测的避坑指南:小目标、运动模糊与标注边界
4.1 小目标全被过滤:默认 anchor 与过滤阈值的锅
现象:训练完成后推理,发现小液滴几乎一个都检测不到,大液滴倒是全都框出来了。验证集 mAP50 看着还行,但一看 recall 不到 0.5。
原因:YOLO 的默认配置里有一个针对小目标的过滤机制——如果 ground truth 的框尺寸小于某个阈值,训练时会被直接忽略。另外,如果数据集中大部分液滴都很小,默认的 anchor 尺寸和输入分辨率会导致小目标特征在下采样过程中丢失。
解决:第一,把输入分辨率从 640 提到 1280 或以上,这一步就能解决大部分小目标漏检问题。第二,检查数据集中小于 3×3 像素的框——如果数量很少(几十个),直接过滤掉;如果数量很多(占到 10% 以上),就要考虑用 SAHI 切片推理或者单独做一个高分辨率分支。
注意:小目标过滤阈值在 YOLO 源码里是 3 像素。但这个值是基于 640 输入计算的,如果你把 imgsz 提到 1280,输入尺寸变大,小目标的有效像素数并没有改变,真正起作用的是「目标在输入图像中的像素占比」。
4.2 液滴反光边缘被框大:标的是亮区还是整个液滴
现象:训练出来的模型在暗场图像上表现很好,但在亮场图像上框明显偏大,几乎把整个光晕都框进去了。mAP 指标挺高,但实际计算的液滴直径明显偏大。
原因:标注人员标注的时候,有的人按液滴轮廓(包括半透明边缘)标注,有的人只按高光核心标注。如果训练集里两种标准混着来,模型学到的就是「模糊边界」。
解决:重新统一标注标准。做一次全量检查,把同时存在「小框」(只框核心)和「大框」(框到光晕边缘)的图片挑出来,确认你的标注规范是「液滴实际轮廓」,还是「液滴不透光核心」。这个标准必须在标注阶段就定死,否则训练出来的模型在跨数据集测试时一定会翻车。我自己的做法是:在数据集的 README 或说明文件里写清标注规范,方便后续迭代时对齐。
4.3 验证集 mAP 高但视频里乱跳:序列数据泄露
现象:训练时 mAP50 到了 0.9 以上,但把模型放到一段连续拍摄的液滴视频里推理,检测框在相邻帧之间疯狂跳动,一会儿漏检一会儿又重复框。
原因:前面提到了数据划分问题——如果训练集和验证集来自同一段视频的连续帧,模型在验证集上看到的样本和训练样本几乎一样,mAP 虚高。但部署到新视频上时,模型没见过真实的帧间变化,泛化能力撑不住。
解决:严格按时间序列或按视频文件切分数据,保证训练、验证、测试来自不同的采集时段。如果数据集压缩包里有多个子目录,优先按子目录切分。另外可以做一个帧间稳定性测试:取一段 1 秒的高速视频(比如 1000fps 就是 1000 帧),跑完整段推理,统计检测框中心点在帧间的位移。如果中心点在相邻帧之间来回跳跃超过液滴直径的 30%,说明模型对位置的预测不稳定,需要回到训练阶段调整。
4.4 训练 loss 不降:预处理路径踩坑
现象:训练跑了 20 轮,box_loss 和 cls_loss 几乎一条平线,基本没降。
原因:我遇到过三次这个问题,三次都是数据读取环节的问题。最常见的是 labels 目录下的 txt 文件里类别编号和 data.yaml 里的 names 对不上——比如 txt 里写的是 0 1,但 data.yaml 里 nc=2 且 names 顺序是 ['drop', 'bubble'],这本身没问题;但如果 txt 里出现类别编号 1 而 data.yaml 里 nc=1,训练时会直接跳过那些标签,相当于一半图片没有标签。
解决:在训练启动前先确认标签编号合法。用一段简单脚本过一遍:
import os label_dir = 'labels/train' max_class = 0 for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname)) as f: for line in f: cls = int(line.split()[0]) if cls > max_class: max_class = cls print(f"标签文件里最大的类别编号是 {max_class}")然后确认 data.yaml 里的 nc 要大于这个编号(编号从 0 开始)。液滴检测通常 nc=1,但如果这个数据集里还标注了气泡或杂质颗粒,nc 就要相应增加。另一个容易踩的点是:图片是 PNG 格式但带 alpha 通道,或者图片是 16 位深度,YOLO 读取时默认转 8 位,某些情况下会导致图像全黑。遇到这种,用 OpenCV 统一转一遍格式再训练。
5. 把液滴检测做得更稳的两个进阶方向
5.1 对极小液滴用 SAHI 切片推理
如果训练完仍然漏检那些 5×5 像素以内的极小液滴,调大 imgsz 的边际收益已经很低——因为输入分辨率过大,显存放不下,而且训练速度会慢到没法接受。这时候我推荐用 SAHI 做切片推理。核心思路是:推理时把大图按重叠区域切成小片,每片用模型检测,最后把结果合并回原图坐标。
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="yolov8", model_path="runs/drop_v2/weights/best.pt", confidence_threshold=0.3, image_size=1280, device="cuda:0" ) result = get_sliced_prediction( image="test_frame_0421.jpg", detection_model=detection_model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, ) result.export_visuals(export_dir="sahi_output/")逻辑说明:切片尺寸设为 640×640,意味着每张原图被切成多块 640 分辨率的片,片与片之间保留 20% 的重叠,避免液滴正好被切在边界上导致漏检。所有检测结果最后会映射回原图坐标系,不需要手动拼接。SAHI 对小目标的提升非常明显,尤其适合液滴这类「目标多、尺寸小」的场景。
参数说明:slice_height 和 slice_width 建议设为训练时 imgsz 的一半,这样每片的有效分辨率等于训练分辨率,检测效果最稳定。overlap_height_ratio 设为 0.2,如果发现液滴在切片边界被切碎,可以提到 0.3,代价是推理时间增加约 20%。
5.2 验证检测效果要落到实际应用指标
最后聊一个很多人忽略的点:液滴检测的评估不能只停留在 mAP 上。做液滴检测的人最终要的不是「框得准」,而是「数得对」或者「量得准」。
- 做液滴计数(比如喷墨打印中统计卫星液滴数量),要测的是计数准确率,应该按「每张图的实际液滴数与检测液滴数之差」来评估。
- 做液滴粒径测量,要测的是检测框的宽度和真实液滴直径之间的误差,这个误差和标注框的边界标准直接相关,也就是第 4.2 节讨论的光晕问题。
我的习惯是训练完写一个 20 行的脚本,把测试集里所有预测框的宽度取出来,和真实标注框宽度算一个平均绝对误差,误差超过 8% 就回去检查标注边界是否统一。这个习惯帮我避免了好几次「mAP 好看但实际没法用」的状况。血泪经验告诉你:目标检测在液滴这种小目标、高精度应用上,验证指标和实际效果之间隔着一道鸿沟,只看 mAP 迟早翻车。希望我的这些踩坑经验能帮到你——拿到数据集之后按这个流程走一遍,你大概率能比直接开训少浪费两到三天。
本文还有配套的精品资源,点击获取