简介YOLO直肠息肉检测数据集面向医学影像与目标检测开发者适用于直肠息肉识别模型的训练和评估尤其适合需要快速搭建YOLO训练流程的算法工程师和医工交叉学习者。资源共19795个文件其中7804张jpg为图像样本4830个txt标签可供YOLO系列直接训练读取7161个xml标签便于可视化查看或迁移到其他检测框架双格式设计减少了格式转换成本压缩包整体177.21MB体积适中方便下载与迭代实验。目前已有1146人学习浏览具备较好的热度参考价值。数据预处理完整图像与标注一一对应有效降低人工筛选负担除标注文件外还附有作者整理的检测结果参考博文可帮助使用者了解模型在当前数据集上的表现便于对比不同训练设置下的检测效果、分析误差来源并针对性地调整参数从而更高效地完成直肠息肉自动检测模型的复现与优化。1. 为什么“YOLO直肠息肉检测数据集”值得你亲手做一遍公开集永远差一口气做肠镜AI辅助诊断的从业者大多绕不开同一个尴尬局面公开的息肉分割数据集虽然不少但基本是结肠镜静态图像分辨率统一、光照均衡、病灶清晰居中。到了实际内镜视频流里情况完全是另一回事——镜头贴壁时对焦失败、冲洗水反光、黏膜褶皱遮挡、息肉只露出半个边缘、帧与帧之间病灶快速滑动。你拿公开数据集训出来的YOLO模型验证集上 mAP 能到 0.9一接到真实内镜设备就露馅。解决路径不是换更重的模型而是自己构建一份能覆盖这些真实退化场景的数据集。本文要讲的就是这个如何从零做一份 YOLO 直肠息肉检测数据集包括视频抽帧、标注口径、数据清洗、类别平衡和训练前检查并给出可直接复制的脚本和三个最常被问到的边界坑。目标人群是准备用 YOLOv8 或 YOLO11 做内镜项目的算法工程师、医工交叉方向的研究生以及想验证“自己攒的数据能不能跑通训练”的硬件工程师。文中不会推荐某个现成数据集打包下载因为真正的落地价值不在那几万张图里而在你能否复现一套符合临床场景的数据生产管线。2. 先搞清楚一份 YOLO 检测数据集的组成不是“图 一个 txt”那么简单2.1 最小的可用数据集应该包含哪四类文件缺一不可很多从分类任务转过来的同学第一次打开 YOLO 数据集的目录会以为只要 images 和 labels 两个文件夹就够了。实际上一份能被 YOLOv8 正常训练的直肠息肉数据集最少需要以下四层内容images/train、images/valJPG 或 PNG 格式的原始帧分辨率建议不低于 640×640 有效内容区labels/train、labels/val与图像同名的 .txt 文件每行记录一个目标的 class_id、归一化后的中心点 x、中心点 y、宽 w、高 hdataset.yaml声明路径、类别数量和类别名称一个划分记录可以是固定随机种子生成的 train/val 名单保证多次训练不改变验证集。前两者是模型真正读取的内容yaml 是训练入口配置第四点是实验可复现的地基。缺少划分记录你会遇到后面第 5 章要讲的“验证集泄漏”问题——同一个息肉的相邻帧同时出现在训练和验证集中指标虚高部署后立刻现原形。2.2 直肠息肉的类别口径怎么定一分类起步还是细分到病理类型息肉检测数据集的类别设计直接决定标注成本和模型可用范围。最稳妥的起步方案是单类也就是 class 0 统一为 polyp框里包含息肉可见主体。这样做的好处是标注一致性容易保证两个标注员对“这个框到底该覆盖息肉的哪个边界”的争议会大幅减少YOLO 训练也更干净。只有在你确实需要做“腺瘤性息肉 vs 非腺瘤性息肉”的预后提示时才考虑分成两到三类。此时要注意类别标签必须从病理活检结果回填不能靠内镜图像的视觉特征猜——锯齿状腺瘤和增生性息肉在镜头下长得非常接近猜出来的标签就是脏标签模型会学到一堆噪声特征。常见做法是先做一分类检测跑通整个流程再结合病理信息做第二版细分类模型两版互不阻塞。2.3 数据量级估算多少帧、多少个框才能喂饱 YOLOv8工业界对 YOLO 目标检测的数据量级有一个比较通用的经验区间单类别检测任务实例数也就是标注框总数在 5000 个以上基本能训出一个可用的模型如果目标占图像面积很小建议超过 10000 个实例。对于直肠息肉这种目标尺寸跨度极大——扁平息肉可能只有几十像素宽带蒂息肉能占到画面四分之一所以实例总数建议取区间上限而不是下限。从内镜视频抽帧来说一条 30 分钟的高清肠镜检查视频去掉进出镜、冲洗、模糊和重复帧后大约能筛出 300 到 600 张有效帧每帧平均 1 到 3 个可见息肉或可疑病灶也就是一条视频能产出 800 到 1800 个实例。攒到 10000 个实例大约需要 8 到 12 条完整检查视频。这个量级对单人标注来说每天标 300 到 400 帧一个月内能完成属于可承受的工程投入。3. 用真实内镜视频制作 YOLO 数据集从抽帧到标注再到划分的完整脚本3.1 第一步用 ffmpeg 做主抽帧再用感知哈希去重原始内镜视频一般体积大、帧率高直接逐帧抽取会产生大量近乎重复的画面后续标注员会在相似帧上浪费大量时间。常见做法是先抽一个低帧率的候选帧再剔除感知重复帧。# 每 10 秒抽一帧输出到 frames_raw 目录 # -vf fps 是抽帧率按视频速度可调-q:v 控制 JPG 质量2 表示画质较好 ffmpeg -i colonoscopy_001.mp4 -vf fps0.1 -q:v 2 frames_raw/colonoscopy_001_%04d.jpg这个命令的逻辑是让 ffmpeg 以每 10 秒一张的节奏抽帧适合镜头推进速度中等、息肉停留时间较长的肠镜检查。如果视频里息肉在某个位置只停留两三秒可以改成fps0.3但会引入更多重复帧模糊帧也会变多。抽帧结束后用一个简单的感知哈希脚本去重保留同一片段中中间位置的那一帧。# 感知哈希去重只去除画面几乎完全相同的帧保留时间靠后的那一帧 import os, hashlib from PIL import Image import imagehash def dhash(img_path, hash_size8): with Image.open(img_path) as img: img img.convert(L).resize((hash_size 1, hash_size)) diff [] for row in range(hash_size): row_pixels list(img.getdata())[row * (hash_size 1):(row 1) * (hash_size 1)] for i in range(hash_size): diff.append(row_pixels[i] row_pixels[i 1]) return .join(1 if d else 0 for d in diff) seen {} for f in sorted(os.listdir(frames_raw)): path os.path.join(frames_raw, f) h dhash(path) if h in seen: os.remove(path) # 删掉重复帧 else: seen[h] f这里解释一下感知哈希把图像缩小成 8×9 的灰度图并逐行比较相邻像素亮度得到一个 64 位指纹指纹相同即视为几乎同样的画面。真正的息肉检测场景里相邻抽帧的息肉位置会有轻微位移哈希不会把它们误判为重复只有镜头完全停顿或画面几乎静止的帧会被去掉。这个脚本删掉的是“镜头没动”的帧没有被删除的帧之间往往仍然存在连续性后面还需要做帧间隔采样防止连续帧进入同一个数据集子集。3.2 第二步标注工具与导出 YOLO txt 的格式细节推荐用 LabelImg矩形检测框或 Labelme多边形可导出矩形。如果目标是检测LabelImg 足够如果后续想转向分割任务直接上 Labelme。这里的关键不是工具本身而是导出格式的细节。YOLO 格式的 txt 每一行是class_id x_center y_center width height四个坐标值全部是相对图像宽高的归一化小数。常见换算错误是使用边界框左上角坐标 (xmin, ymin) 和右下角坐标 (xmax, ymax) 时忘记转成中心点格式# 从 Labelme JSON 转 YOLO txt 的核心逻辑 import json, os from PIL import Image def convert_labelme_to_yolo(json_path, out_txt_path, img_width, img_height, class_map): with open(json_path, encodingutf-8) as f: data json.load(f) lines [] for shape in data[shapes]: cls_name shape[label] # 只接受矩形标注多边形时先取外接矩形再做形状校验 pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] xmin, xmax min(xs), max(xs) ymin, ymax min(ys), max(ys) # 归一化所有值必须除以原图宽高不是除以缩放后的宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{class_map[cls_name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))参数说明class_map 是一个字典把标注时写的中文标签如“息肉”映射到整数 0如果一开始标注就统一用 polyps 作为唯一标签class_map 可以简化为{polyp: 0}。注意代码里显式强调除以原图宽高因为内镜视频抽帧后如果做过去黑边处理原图尺寸变了标注 json 里记录的坐标不会被 yolo 脚本自动适应这里容易出现一个全场翻车的点。3.3 第三步目录组织与固定随机种子的训练验证划分目录结构建议按 YOLO 官方约定展开polyp_data/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── dataset.yaml └── split_seed.txt划分脚本要特别处理一件事来自同一段视频连续多帧的图像不能既进 train 又进 val。这就是“按病例或视频划分而不是按单帧随机划分”。# 按视频来源划分训练/验证集防止数据泄漏 import os, random, shutil random.seed(42) # 固定种子保证每次运行结果一致 video_ids [001, 002, 003, 004, 005, 006, 007, 008] val_videos set(random.sample(video_ids, 2)) # 抽出 2 个视频作为验证集 for fname in os.listdir(images): video_id fname.split(_)[0] # 文件名约定视频ID_帧号.jpg dest val if video_id in val_videos else train os.makedirs(fimages/{dest}, exist_okTrue) os.makedirs(flabels/{dest}, exist_okTrue) shutil.move(fimages/{fname}, fimages/{dest}/{fname}) label fname.replace(.jpg, .txt) if os.path.exists(flabels/{label}): shutil.move(flabels/{label}, flabels/{dest}/{label})这段脚本的关键参数是split_seed和val_videos的数目。经验是保留 15% 到 20% 的视频作为验证集不要贪多。固定种子是为了让后续每次训练、每次调整超参数时对比的对象一致否则你改了一个增强参数却因为数据划分不同而无法判断效果差异是来自参数还是来自数据。dataset.yaml 的内容很简短但文件名不能写错YOLOv8 对路径非常敏感path: /home/user/polyp_data # 改成你自己的绝对路径 train: images/train val: images/val names: 0: polyp到这里一份可以启动训练的数据集已经成型。下一步是训练前必须做的三个质量把控动作——这三个动作直接决定你第一版模型的可用性。4. 训练前必须做的三件事类别平衡、小目标增强和脏数据清洗4.1 类别与尺寸失衡息肉有大有小不能一视同仁很多人直接拿整理好的数据集跑 YOLOv8 默认配置结果小息肉 mAP 只有大息肉的一半。原因在于默认的训练采样是均匀的每个 batch 里大尺寸目标占主导小目标的梯度贡献被稀释。一个在实践里验证有效的做法是先统计标注框的相对面积分布然后按面积区间重采样# 统计训练集中所有标注框的相对面积用于判断是否需要小目标过采样 import os areas_small, areas_medium, areas_large 0, 0, 0 for f in os.listdir(labels/train): with open(os.path.join(labels/train, f)) as fp: for line in fp: _, x, y, w, h line.strip().split() area float(w) * float(h) if area 0.015: # 相对面积小于 1.5% areas_small 1 elif area 0.1: # 1.5% - 10% areas_medium 1 else: areas_large 1 total areas_small areas_medium areas_large print(f小目标占比: {areas_small/total:.2%}, 中目标: {areas_medium/total:.2%}, 大目标: {areas_large/total:.2%})如果小目标占比低于 20%训练时要考虑开 YOLOv8 的augmenttrue之外再叠加一个随机裁剪增强——在每轮迭代时把图像按 0.5 到 0.8 倍缩放后随机裁剪一块送入网络等效于把小息肉“放大”后让模型多看到几次。具体实现时可以直接调用 YOLO 训练参数里的crop_fraction在 Ultralytics 的 8.x 版本中可用也可以在前处理里手动叠。本方案更推荐前者因为参数由官方统一调度不会干扰 mosaic 增强的触发概率。4.2 增强参数别贪多mosaic 和 copy-paste 对内镜数据的特殊副作用YOLOv8 默认开了 mosaic 增强把四张图拼成一张新图训练。对自然图像是好事对内镜图像却有一个隐藏副作用息肉本身是类圆形、边缘光滑的病灶四张图的拼接边界会产生锐利的伪边缘模型可能会学到“边界线也是息肉特征”。这就是实践中常说的“玄学训练指标好看、真实视频上泛化差”的重要来源之一。一个建议配置是保留 mosaic但将mosaic0.5附近调低而不是默认的 1.0开启hsv_h0.0因为真实内镜图像通常不改变色调训练结束后模型的色彩鲁棒性不应被虚假的颜色抖动引入degrees5留一点点旋转超过 5 度与真实肠镜的镜头姿态关系不大fliplr0.5保留水平翻转但注意如果标注时对“息肉位于镜头左侧还是右侧”有临床语义比如辅助进镜方向就不能做翻转。这些参数在训练脚本里通过model.train(augmentTrue, mosaic0.5, degrees5, hsv_h0.0)这种形式传入。没有哪一种增强是绝对正确的关键是你改一个参数前想清楚它模拟的是哪一种真实退化。4.3 脏数据清洗用一段快速推理把明显错误框找出来人工标注再仔细几千帧标注下来也难免出现漏标和错框。比人眼复核更高效的方式是先用当前版本的 YOLO 模型对训练集自身做一次预测把预测结果和人工标注不一致的帧挑出来重点复查。这个过程在工程上叫“困难样本挖掘”也是数据迭代的核心闭环。# 用训练中的模型对训练集做回灌验证输出置信度高于阈值的未匹配框 from ultralytics import YOLO import os model YOLO(runs/detect/train/weights/best.pt) results model.predict(sourceimages/train, conf0.25, save_txtFalse, save_confTrue) for r in results: img_path r.path # 人工标注的框数量 label_path img_path.replace(images, labels).replace(.jpg, .txt) if not os.path.exists(label_path): continue with open(label_path) as f: num_gt len(f.readlines()) num_pred len(r.boxes) # 预测框远多于标注框时大概率标注漏标 if num_pred num_gt 2: print(f疑似漏标: {img_path}, 人工标注框数 {num_gt}, 预测框数 {num_pred})这段脚本的实用价值在于它能把“标注员当时没看到的小息肉”重新找出来。参数 conf0.25 是保守阈值太低会输出大量噪声框太高会把小目标的弱响应过滤掉。一般先按 0.25 跑一遍人工确认是否漏标把确认的帧重新标注再把 conf 上调到 0.4 跑一遍专查高置信度误检——比如把黏膜高光当成息肉的框。这个循环每做一轮数据质量都能上一个台阶比盲目增加标注量更值得投入。5. 避坑指南YOLO 息肉检测数据集最常见的 5 个踩坑点5.1 训练正常收敛验证集 mAP 很高但真实视频上几乎检不出小息肉现象用 YOLOv8 默认分辨率 640 训练loss 曲线平滑下降val mAP50 超过 0.85但拿到另一台内镜设备录的视频上测试5 毫米以下的扁平息肉全部漏检。原因原始内镜视频抽帧后直接缩放成 640×640扁平息肉原本只有 30×20 像素左右缩放后变成 15×10 甚至更小小于检测器在小尺度特征图上的最小可感知目标。解决训练分辨率改为 1280 并同时调大输入尺寸或者使用 YOLO 的小目标专门配置在 8.x 版本里表现为增加一层 P2 输出头。注意调大分辨率会等比增加训练显存占用需要同步降低 batch size。实测相同数据从 640 升到 1280小息肉召回率能提升 8 到 15 个百分点代价是单卡训练时间增加约 2.5 倍。5.2 验证集指标一路绿灯部署后同一个病灶检测结果上下跳变现象模型在视频流中检测同一个息肉时而稳定框住时而丢失一两帧再又重新检出。原因按视频划分训练/验证集时验证集选到了镜头推进速度特别慢、画面特别清晰的一段训练集则充满了冲洗、出血、模糊帧分布不一致。解决划分前先对视频片段做内容复杂度打标保证训练集和验证集都包含“清晰平稳段”和“困难退化段”。常用做法是抽帧时按帧的不清晰度指数分桶再在每个桶内做视频级划分。没有这一步模型在真实场景中的实际鲁棒性是靠运气的。5.3 同一条视频的连续帧被拆进了 train 和 val指标虚高现象mAP50 有 0.9但换一台设备的视频就掉到 0.6差距大得反常。原因按单帧随机 8:2 划分同一个息肉病灶的相邻十几帧画面几乎相同好几帧进了训练集、好几帧进了验证集验证时模型相当于“见过”目标了。解决严格按视频 ID 划分一步到位如果视频数量不够至少要保证同一视频内每隔 N 帧抽一张N 根据镜头移动速度动态调整最终目的就是避免同源画面被拆开。这是最容易修、也最容易被忽略的坑。5.4 标注框把所有“可疑的东西”都框进去类别定义前后不一致现象训练结束后模型总是把肠道内残留粪水、气泡、冲洗水反光也检测成息肉。原因标注阶段没有规定严格的阳性标准第 100 帧标了息肉的红色隆起第 500 帧时标注员对一个小隆起拿不准也框了还标成了 polyp。训练集里混入了大量“非息肉但像息肉”的噪声正样本。解决标注规范前置在我的流程里要求只能框“确认是息肉”的病灶拿不准的另建一个 hard_samples 文件夹不进入训练集。对于已经全局标完的一批数据用 4.3 的高置信度误检脚本专门筛出这类样本清洗掉或移出训练集能显著降低误检率。5.5 水面反光和暗角导致训练 loss 振荡模型学不到稳定特征现象训练曲线在 epoch 40 到 50 之间突然出现抖动然后 mAP 下降约 10 个点之后缓慢回弹但恢复不到之前的高点。原因增强里开启了随机 HSV 抖动红光反射区域被随机改成暗红色或亮红色自动增强滋生了一部分虚假的“颜色不变性”样本干扰了模型对息肉红色调特征的稳定提取。解决把hsv_h、hsv_s调到接近 0仅保留少量hsv_v亮度抖动同时关闭 mosaic 的拼接补丁或设置mosaic0.3。这类“增强副作用”问题最难排查建议在每次改增强参数后用同一固定种子重训并记录 mAP 波动而不是直接叠加使用默认配置。6. 从检测框走向分割与实时辅助数据集的下一步进化方向当基础检测模型稳定后数据集的下一个迭代方向是分割掩码。YOLOv8-seg 可以直接消费你的检测数据集只要把 labels 里的矩形 txt 升级为多边形坐标的 segment 格式。这个转换比想象中省事标注工具里把已标好的矩形框转成多边形微调边缘让掩码贴合息肉轮廓然后导出为 YOLO segment 格式的 txt每行开头仍是 class_id后面不再是“x y w h”而是一串成对的多边形归一化坐标点。分割头的收益在于矩形框在息肉不规则边缘上天然包含背景导致置信度被背景像素干扰掩码能提供更精准的边界便于用户在辅助诊断界面上直接看到病灶轮廓。验证数据集质量是否真正提升除了 mAP我还会用一组固定的困难视频作为“金标准测试集”每个版本迭代后都跑一遍这组视频统计召回率、误检数和帧间稳定性。这比反复调参更有判断价值。另一条实用建议是在导出 ONNX 到实际内镜设备之前先将视频按原始分辨率切成若干短片段分别测试不同场景的漏检分布记录每段视频的失败帧回灌到数据清洗流程里做补充标注——我习惯把这条路称为数据集迭代的闭环每走一轮模型就前进一步。希望这些从标注规范到数据清洗的经验能帮到你让你做出来的数据集和模型真正经得起真实内镜视频的检验。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?