ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

全景牙齿X光片牙位标注数据集制作:从FDI编号到Pascal VOC格式

2026/9/8 22:05:16 拓冰建站 浏览量
全景牙齿X光片牙位标注数据集制作:从FDI编号到Pascal VOC格式 简介全景牙齿X光片牙位标注数据集面向医疗影像智能分析与目标检测任务使用Labelimg完成精细标注内含经过筛选的清晰X光片与对应XML标注文件。压缩包共210个文件包括105张JPG影像和105个XML标注体积约28.49MB文件与标注一一对应、结构紧凑可直接用于Faster R-CNN、YOLO、SSD等主流目标检测模型的训练与验证也方便自行划分训练集和验证集。XML文件按标准格式记录每颗牙齿的边界框坐标与类别图片覆盖多种牙齿状态和位置能有效降低数据清洗与人工标注时间成本并提升模型在不同牙位识别、龋齿检测等场景下的泛化能力。目前已有642人学习下载适合医疗影像算法工程师、AI研究人员及牙科智能诊断系统开发者可辅助开展牙位定位、口腔疾病初筛等应用探索。 做口腔AI相关项目时第一步往往不是选模型而是先解决数据问题。全景牙齿X光片也叫曲面断层片、口腔全景片上的牙齿牙位标注难处其实不在“画框”本身而在于“这个框里的牙齿到底叫什么名字、编号得标对”。我整理的这份全景牙齿X光片牙齿牙位标注数据集就是围绕这个需求做的用labelimg逐张框出牙冠区域并把每一颗牙标成对应的FDI牙位编号最终为每张图片生成一份精细的Pascal VOC格式XML标注文件。这份数据很适合正在训练目标检测模型、做口腔影像AI科研或者需要一套现成标注参考来做毕业设计的同学。我会在这篇博文里把整套制作思路、标注规范、XML格式解析方法以及我在实操中踩过的坑全部拆开讲清楚。包括为什么选labelimg而不是其他标注工具、牙位编号体系怎么设计、标注边界怎么判断、XML文件怎么批量校验和转换为YOLO格式以及最常遇到的“labelimg闪退”“正方形框切换不生效”这类问题的处理办法。1. 从零构建牙位标注数据集为什么值得做1.1 这类数据集的真实用途很多算法工程师一开始低估了牙科影像标注的复杂度以为这就是普通的目标检测数据集把“牙齿”当一个大类标上去就完事了。但实际临床和科研需要的往往是“牙位级”的定位——也就是说不仅要告诉模型“这里有颗牙”还得告诉它“这是右上颌第一磨牙FDI编号是16”。这个粒度直接决定了后续AI应用的上限。举个例子做龋齿筛查时如果模型只能输出“牙科全景片里有异常”医生没法用但如果模型能输出“16号牙远中邻面疑似龋坏”医生就能直接复核并纳入诊断路径。再比如智齿拔除辅助、正畸方案里的牙齿排列分析、牙科保险理赔里的牙齿计数与缺牙识别这些场景全都依赖牙位级检测模型。而这种模型的基础就是一份牙位标注严格正确的数据集。也正因如此我才坚持在用labelimg标注时直接以FDI编号作为类别名而不是用“切牙”“磨牙”这种宽泛名称。这会让下游模型天然学会输出临床可用的牙位信息省去后续做类别映射的麻烦。1.2 一份合适的牙科数据集应该长什么样从数据工程的角度看一份合格的牙位标注数据集至少要满足三个条件。第一图片要经过筛选成像清晰、层次分明不能把大量重度模糊、严重运动伪影或缺失一半牙列的片子直接堆进来否则会严重拖累训练效果。第二标注框要落在牙冠区域因为牙根在二维全景片上经常与邻牙、骨组织、伪影重叠边界极不稳定牙冠的形态和位置相对可靠适合作为目标框的基准。第三XML标注文件必须完整且可解析每一项的类别名、坐标值、图像尺寸都要能对得上。这份数据集在选择图片时我自己定的筛选标准是牙列连续度较好、无大范围金属伪影遮挡、上下颌咬合关系基本可见。每张片子上的每颗牙都独立标注缺失牙不强行标注但保留天然缺失和拔牙后愈合状态的样本这样模型在真实场景里遇到缺牙也能正确处理。2. 全景X光片的成像特点与标注难点2.1 看懂全景片曲面断层成像的基本特征做标注前必须先理解全景片的成像原理否则很多边界判断会出错。全景片使用的“曲面断层摄影”技术是让X射线源和探测器围绕患者头部旋转把弧形的上下颌骨展开成一张二维平面图像。这就有点像把一个弧形的大地拍成一张长卷轴照片——整体结构一目了然但嘴角、磨牙后区、颧骨投影等区域会出现不同程度的几何形变和重叠。在全景片上牙冠和牙根都能看到但牙根的清晰度受很多因素影响。下颌神经管、上颌窦底、鼻腔底这些解剖结构可能叠加在牙根区域造成局部低密度影或双线影看起来像“根尖阴影”其实只是正常解剖结构。如果在标注时把框画到这些区域模型就很容易混淆。我的处理习惯是标注框一律以牙冠及其颈缘线为界如果牙根清晰可见且与邻牙边界分得清楚可以适当包住根中1/3但绝不要硬把根尖包进去。根尖区域的影像干扰太多冒然扩大框只会增加类别间的特征重叠。2.2 标注边界判据牙冠、邻接点与重叠区全景片里相邻牙之间通常会有明显的邻接接触点前牙区邻间隙窄后牙区邻间隙稍微宽一些。标注的时候最需要留意的就是相邻牙冠的边界不要互相侵蚀。我的经验是先放大图像找到牙冠与牙冠之间的“缝隙线”——牙釉质在X光片上表现为高密度亮带牙本质密度稍低所以相邻牙冠之间通常存在一条可辨认的细线。沿着这条线切开就是两颗牙的边界。如果遇到邻面重叠严重的情况比如重度拥挤或牙齿倾斜就看牙体长轴的方向。每颗牙大体上沿着自己的牙体长轴排列从牙冠中心向根方延伸。当两牙投影重叠时优先按照牙冠暴露出来的外轮廓画框宁可少包住一些模糊区域也不要为了“看起来完整”而把相邻牙的牙冠划进来。另外需要特别注意的是牙釉质的高亮伪影。全景片边缘的金属修复体比如烤瓷冠、种植体、颌面部金属饰品会产生放射状或条带状伪影。这类伪影区域里的牙齿形态是被破坏的标注时如果实在看不清边界我的做法是标出可辨认的牙冠轮廓同时在数据整理时把这类样本单独分开不要让它们占训练集的比例过高。3. 牙位编号体系与标注规范设计3.1 FDI牙位标记法速查标注类别之前必须先确定牙位编号体系。目前全球口腔医学领域最通用的是FDI牙位标记法全称是Fédération Dentaire Internationale国际牙科联盟提出的两位数字系统。第一位数代表象限第二位数代表牙位从中线往后依次从1排到8。成年人恒牙一共32颗以患者自身为参照分为四个象限右上象限为1左上象限为2左下象限为3右下象限为4。每颗牙在对应象限里的编号如下表牙位编号对应牙齿常见别名1中切牙门牙2侧切牙侧门牙3尖牙犬齿、虎牙4第一前磨牙第一双尖牙5第二前磨牙第二双尖牙6第一磨牙六龄齿7第二磨牙十二龄齿8第三磨牙智齿举例来说“11”就是右上中切牙“16”是右上第一磨牙“38”是左下智齿。如果是混合牙列期的儿童片乳牙用5、6、7、8开头来区分象限即51-55、61-65、71-75、81-85但我这份数据集以成人恒牙为主因此类别名集中在11到48之间。3.2 定义一套能落地的标注规范标注规范的好坏直接影响数据集的可用性。我最终采用的原则是类别名直接使用两位数字字符串比如“11”“12”“33”“48”而不是“upper_right_central_incisor”这类长名称。原因有两个一是模型输出“16”这种编号程序里可以直接跟FDI体系对接临床含义明确二是labelimg里输入长类别名很容易拼错而且会在下拉框里铺满一长串影响标注效率。在边界判断上我要求标注框紧贴牙冠外缘保证牙冠完整包住但不外扩太多。两颗邻牙之间的距离很近但你不需要让框与框之间的空隙为零框稍微触碰甚至轻微重叠都可以接受只要牙冠主体没有被“吃”掉就行。缺失牙不标注但这里要区分两种情况一种是牙齿根本没萌出或者被拔除另一种是牙冠被严重破坏只剩下残根。我倾向于只保留天然缺牙、且牙位通过邻牙关系可以推断的样本残根牙如果实在无法判断牙冠轮廓也不标宁可少标也不要标错。修复体的处理上我是这样规定的全冠修复牙、种植牙如果整体形态完整、牙冠边界清晰就正常标注但如果高密度金属修复体产生了严重星芒状伪影导致边界无法确认就跳过该牙。这条规则保证了数据集不会因为伪影出现大量“看起来是金属放射线”的错误框。4. labelimg标注实操全流程4.1 安装与环境准备labelimg是一款非常经典的开源图像标注工具支持PascalVOC和YOLO两种格式。安装方式非常简单pip install labelimg如果网络环境不好也可以先安装PyQt5再装labelimg或者用condaconda install -c conda-forge labelimg装好之后在命令行输入labelimg回车就能打开窗口。需要注意一点labelimg依赖PyQt5如果机器上同时装了多个Python环境最好先确认启动的是哪个环境里的labelimg。我建议单独建一个标注用的虚拟环境避免依赖冲突。数据准备阶段把全景X光片统一转成jpg或png格式文件名尽量用英文和数字比如pano_001.jpg。早期我用过中文文件名结果在XML解析和后续训练时遇到过编码兼容问题后来就全部改成英文命名了。4.2 标注时的核心设置与工作流打开labelimg后第一件事是确认标注格式。点击左侧“PascalVOC”按钮切换到XML格式如果按钮显示的是“YOLO”说明当前是txt输出模式。确保XML模式下标注才会生成你要的.xml文件。熟悉界面的布局很重要左侧是图像预览和标注属性区右侧是图像操作区底部是当前图像文件路径。在标注之前我喜欢先在“Open Dir”里打开图片目录再把“Change Save Dir”指定到一个专门的annotation目录这样XML文件和图片分开存后面整理数据会清爽得多。标注的画框流程其实很简单点击界面左侧的“Create RectBox”按钮或者在图像区按快捷键W进入画框状态。在牙冠左上角按住鼠标左键拖动到牙冠右下角松开后弹出标签选择框。输入或选择对应的FDI编号比如“16”点OK确认。按CtrlS保存当前图片的标注然后按D切到下一张图片继续。实际操作时尤其是处理一张有28颗牙的全景片每张图至少需要3-5分钟。我一般先放大图像对照牙位分布图逐象限标注顺序是右上→左上→左下→右下这样能有效减少漏标和错位。4.3 快捷键与效率提升技巧标注几千张图片是个体力活熟练使用快捷键能省下大量时间。下面是我最常用的快捷键清单快捷键功能W创建矩形框A / D上一张 / 下一张图片Ctrl S保存当前标注Ctrl 滚轮缩放图像滚轮上下滚动图像Delete删除当前选中的框Ctrl E编辑当前框的标签方向键移动当前选中的框有一个特别实用的组合在创建框之前先用Ctrl滚轮把牙冠区域放到足够大画完框之后再按Ctrl0恢复原始大小查看整体效果。这样做能明显降低相邻牙框偏移的概率。另外如果发现某颗牙的框稍微偏了不要删掉重画直接选中这个框用方向键微调即可。关于网上经常被问到的“切换正方形框不生效”问题我的结论是labelimg本身并没有“锁定正方形框”的内置功能默认画出来的就是任意矩形。网上有些人说按某个键可以强制正方形其实是把其他软件的快捷键记混了。如果必须使用正方形框标注最稳定的是画完矩形后在属性面板里手动把框的高和宽改成相同像素值或者用Python脚本批量校正。5. XML标注文件解析与格式转换5.1 Pascal VOC XML结构逐项拆解labelimg在PascalVOC模式下生成的XML文件本质上是一种结构化的描述文件用标签对的形式存储了图片路径、尺寸以及每一个目标的类别和坐标。下面是一个典型例子annotation folderpano_images/folder filenamepano_001.jpg/filename path/home/user/pano_images/pano_001.jpg/path source databaseUnknown/database /source size width2048/width height1024/height depth3/depth /size segmented0/segmented object name11/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin120/xmin ymin240/ymin xmax160/xmax ymax280/ymax /bndbox /object /annotation这里最核心的是size节点和object节点。size里记录了图片的宽、高和通道数这是后续坐标归一化的重要依据。每个object节点对应一个目标检测框name是类别名也就是FDI牙位编号bndbox里存了框的左上角(xmin, ymin)和右下角(xmax, ymax)坐标单位是像素。5.2 用Python批量解析与可视化校验数据标注完成后第一件事不是急着扔进模型训练而是先写脚本解析这些XML检查有没有类别名拼错、坐标越界、框面积异常等问题。我用Python标准库里的xml.etree.ElementTree就能完成这项工作。统计各类别数量import glob import xml.etree.ElementTree as ET def count_classes(xml_dir): class_count {} for xml_file in glob.glob(xml_dir /*.xml): tree ET.parse(xml_file) root tree.getroot() for obj in root.findall(object): name obj.find(name).text class_count[name] class_count.get(name, 0) 1 return class_count result count_classes(annotations) for cls, cnt in sorted(result.items()): print(f{cls}: {cnt})运行后我会重点看两个地方第一是类别名是否都在FDI合法范围内第二是每张图的平均目标数量是否符合预期。每张全景片正常应该有25颗以上的标注框如果某张图只有十几个框说明漏标了需要回去补标。还需要检查坐标是否越界也就是xmax是否超过图片宽度、ymax是否超过图片高度def check_bounds(xml_file, img_w, img_h): tree ET.parse(xml_file) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) for obj in root.findall(object): box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) if xmin 0 or ymin 0 or xmax w or ymax h: print(f越界: {xml_file}, {obj.find(name).text})如果要把数据转成YOLO格式训练核心逻辑是把PascalVOC的像素坐标归一化到0到1之间。计算公式是中心点x(xminxmax)/2/w中心点y(yminymax)/2/h宽度(xmax-xmin)/w高度(ymax-ymin)/h。我习惯写一个批量转换脚本顺便做可视化校验。所谓可视化校验就是把每一张图片的框画出来叠在原始X光片上人工扫一眼确认框和牙冠的对齐程度。这一步特别值得做因为从坐标数字上看不出来的“轻微偏左”“框太大”等问题在可视化图里一秒就能发现。6. 常见问题与排查技巧实录做标注做久了各种工具链问题都会遇到。我把自己踩过的坑整理成了一张速查表给各位参考现象常见原因解决方案labelimg打开后闪退PyQt5版本冲突或图像尺寸过大使用虚拟环境重新安装PyQt5或先压缩图片分辨率再标注标注格式变成txt而不是xml界面左侧选中了YOLO模式点击“PascalVOC”切换回XML模式切换正方形框不生效labelimg无内置正方形锁定功能手动微调框的宽高像素值或用脚本批量校正中文文件名导致XML路径乱码系统编码与XML编码不一致图片和文件夹全部改成英文命名XML文件双击打不开编辑软件关联错误用文本编辑器打开或用Python脚本解析校验标注框整体偏移缩放图像后未注意坐标基准标注完成后统一可视化复查发现偏移批量修正有几个问题值得多说两句。第一个是labelimg闪退。这通常发生在打开超高分辨率全景片比如4000x2000以上尺寸的时候。我自己的方案是先把原始DICOM或超大图片缩放到宽边2048像素既保证牙冠细节可见又能稳定运行标注工具。第二个是XML文件“打不开”。实际上XML就是纯文本文件用记事本、VS Code、Sublime Text都能打开。如果用Excel双击打开XML出现乱码不是文件坏了而是关联程序选错了。我推荐用VS Code打开装一个XML插件还能自动格式化嵌套结构分析速度快很多。第三个是标注类别名不一致。比如同一颗牙第一张图标成“16”第二张图标成“016”或者混入“upper 16”这种带空格的命名会导致训练时类别数爆炸且准确率骤降。解决方法是写完标注后用脚本做一次类别名白名单过滤凡是FDI编号之外的类别全部标记出来人工复核。写在最后的一点经验整个数据集从出片、初筛、规范制定、逐张标注到XML校验最耗时其实不是画框而是规范的统一。标注人员心里要时刻清楚这个框包住的是牙冠、牙根根尖3/4还是整个牙齿两颗邻牙重叠时以哪边为准金属伪影覆盖时是标还是不标。这套标准只有固化到操作流程里数据集的精细度才能稳定体现出来。我个人最大的体会是医学影像标注领域里“能用”和“好用”的差距基本都藏在标注规范和质检流程里。如果你也打算做一套自己的牙齿数据集建议先从100张图开始标完全部用可视化脚本检查一遍把暴露出来的边界问题逐条修订进规范再回到第101张图继续标。这样迭代出来的数据集拿去训YOLOv8也好训RTMDet也好出来的模型指标和实际落地的稳定性都会比“闭着眼睛一顿标”强太多。另外做这类数据集一定要重视影像数据的合规使用。全景X光片属于敏感的医疗健康信息使用前必须完成患者隐私脱敏确保不包含姓名、病历号等身份信息且用途仅限于符合规定的科研或教学场景。从源头做好合规整个项目才能走得更远。本文还有配套的精品资源点击获取