
简介本资源为舌头分割图像数据集面向医学图像处理、计算机视觉方向的学习者与算法开发者可用于训练和评估语义分割模型解决舌头区域自动提取与背景分离问题。包内共约2000个文件以png掩膜标签为主另含1个类别说明txt与1个可视化py脚本压缩包约101.53MB。数据分为训练集与测试集训练集含2127张jpg原图及2127张对应mask测试集含537张原图及537张mask图像分辨率统一为640×640mask为0/1阈值图像0代表背景、1代表舌头具体类别可在classes文本中查看。配套可视化脚本无需修改即可直接运行随机抽取一张图片同时展示原始图像、GT图像以及GT在原图上的蒙板效果并保存至当前目录便于快速核验标注质量。目前已有229人学习下载适合需要现成数据与可视化工具开展分割实验的读者。1. 舌头分割数据集从原始图像到二类掩码一条能跑通的链路舌头图像分割在中医舌诊数字化、口腔正畸评估、吞咽功能分析里都是绕不开的前置环节。拿到一份「图像分割数据集舌头分割2类【数据集标签文件数据可视化代码】」多数人第一反应是打开图片看看然后卡在三个地方标签到底怎么编码的、2类指的是哪两类、可视化代码怎么把掩码叠回原图。这份数据集的价值不在于图片数量而在于它把「图像—标签—可视化」这条链路完整交到你手上让你能直接进入训练环节而不是花两周做标注。它适合三类人想入门医学图像分割的算法工程师、需要舌体区域做下游特征提取的研究者、以及拿它当分割 pipeline 练手的学生。下面按「先看懂标签、再跑通可视化、最后接上训练」的顺序拆开讲每一步都给出可复现的命令和参数。2. 拆开数据集2类标签的编码方式与目录结构2.1 2类分割到底分的是哪两类「2类」在分割任务里通常有两种含义一种是前景/背景二分类标签只有 0 和 1另一种是语义类别数为 2比如舌体与背景各占一个类别索引。舌头分割数据集绝大多数采用前者——舌体为前景像素值 1 或 255其余为背景像素值 0。判断方法很直接读一张标签图看唯一值集合。import numpy as np from PIL import Image mask np.array(Image.open(labels/0001.png)) print(shape:, mask.shape) print(dtype:, mask.dtype) print(unique values:, np.unique(mask))这段代码做三件事读取标签、打印尺寸和数据类型、列出所有唯一像素值。如果unique values输出[0 1]说明是标准二值掩码如果输出[0 255]训练前需要除以 255 归一化到 0/1如果出现[0 1 2]那才是真正的多类语义标签此时 2 类指的是类别索引 0 和 1 之外还有别的含义要回去核对说明。dtype也要留意bool类型直接可用uint8需要确认取值范围。2.2 目录结构与文件命名对齐常见做法是 images 和 labels 两个平行目录文件名一一对应。但血泪经验是不少数据集图片是.jpg标签是.png扩展名不一致导致按文件名匹配时漏样本。先做一次对齐检查。# 列出两个目录的文件名去掉扩展名并比对 ls images | sed s/\.[^.]*$// | sort /tmp/img_names.txt ls labels | sed s/\.[^.]*$// | sort /tmp/lbl_names.txt diff /tmp/img_names.txt /tmp/lbl_names.txtsed s/\.[^.]*$//去掉最后一个点及其后缀sort保证顺序一致diff输出差异。如果 diff 有输出说明存在图片没有对应标签或反之训练时 DataLoader 会在这些样本上翻车。处理办法是以标签为准过滤图片或补齐缺失标签。这个检查花不到十秒能省掉训练到一半报错的后悔药。2.3 图像尺寸与标签尺寸必须一致分割任务里图像和标签的宽高必须严格相等否则叠加可视化会错位训练时 resize 也会引入标签插值问题。批量检查一遍import os from PIL import Image img_dir, lbl_dir images, labels mismatch [] for name in os.listdir(lbl_dir): stem os.path.splitext(name)[0] img_path None for ext in (.jpg, .png, .jpeg, .bmp): p os.path.join(img_dir, stem ext) if os.path.exists(p): img_path p break if img_path is None: mismatch.append((name, no image)) continue w1, h1 Image.open(img_path).size w2, h2 Image.open(os.path.join(lbl_dir, name)).size if (w1, h1) ! (w2, h2): mismatch.append((name, (w1, h1), (w2, h2))) print(mismatch count:, len(mismatch)) for m in mismatch[:10]: print(m)逻辑是遍历标签目录为每个标签找同名图片尝试多种扩展名比较尺寸。mismatch列表收集所有异常打印前十条便于定位。参数上扩展名元组按你数据集实际情况增删。如果尺寸不一致常见做法是统一 resize 到 256×256 或 512×512但标签必须用最近邻插值否则会出现 0.5 这种非法类别值。3. 数据可视化代码把掩码叠回原图看清楚3.1 单张可视化轮廓叠加与彩色掩码可视化代码的核心作用有两个确认标签质量、给论文或报告出图。最实用的方式是原图上叠加半透明彩色掩码加轮廓线。import numpy as np import matplotlib.pyplot as plt from PIL import Image def visualize_one(img_path, mask_path, alpha0.45): img np.array(Image.open(img_path).convert(RGB)) mask np.array(Image.open(mask_path)) mask_bin (mask 0).astype(np.uint8) # 兼容 0/1 与 0/255 color np.zeros_like(img) color[..., 0] 255 # 红色掩码 overlay img.copy() overlay[mask_bin 1] ( (1 - alpha) * img[mask_bin 1] alpha * color[mask_bin 1] ).astype(np.uint8) fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(img); axes[0].set_title(image) axes[1].imshow(mask_bin, cmapgray); axes[1].set_title(mask) axes[2].imshow(overlay); axes[2].set_title(overlay) for ax in axes: ax.axis(off) plt.tight_layout() plt.savefig(vis_one.png, dpi150) plt.show() visualize_one(images/0001.jpg, labels/0001.png)mask 0把 0/255 和 0/1 两种编码统一成二值避免因编码差异导致掩码全黑。alpha控制透明度0.45 在多数舌象上既能看清掩码边界又不遮住舌质纹理。color[..., 0] 255只改红色通道得到红色掩码。三栏并排输出便于对比。保存用dpi150够报告用论文可提到 300。3.2 批量可视化网格图快速筛查脏标签单张看效率低批量出网格图能一眼扫出标注异常样本。import os import numpy as np import matplotlib.pyplot as plt from PIL import Image def grid_vis(img_dir, lbl_dir, n16, cols4, savevis_grid.png): names sorted(os.listdir(lbl_dir))[:n] rows (len(names) cols - 1) // cols fig, axes plt.subplots(rows, cols, figsize(cols * 3, rows * 3)) axes np.array(axes).reshape(-1) for ax, name in zip(axes, names): stem os.path.splitext(name)[0] img_path None for ext in (.jpg, .png, .jpeg, .bmp): p os.path.join(img_dir, stem ext) if os.path.exists(p): img_path p break if img_path is None: ax.axis(off); continue img np.array(Image.open(img_path).convert(RGB)) mask np.array(Image.open(os.path.join(lbl_dir, name))) mask_bin (mask 0).astype(np.uint8) overlay img.copy() overlay[mask_bin 1] (0.55 * img[mask_bin 1] 0.45 * np.array([255, 0, 0])).astype(np.uint8) ax.imshow(overlay); ax.set_title(stem, fontsize8); ax.axis(off) for ax in axes[len(names):]: ax.axis(off) plt.tight_layout() plt.savefig(save, dpi120) plt.show() grid_vis(images, labels, n16, cols4)n控制展示数量cols控制列数rows自动算。axes[len(names):]把多余子图关掉避免空白格。批量看的时候重点盯三类问题掩码缺失全黑、掩码溢出覆盖到脸颊或牙齿、掩码断裂舌体中间有洞。这些脏标签不处理训练 loss 会震荡模型学出玄学边界。3.3 可视化代码里最容易忽略的通道顺序PIL 读出来是 RGBOpenCV 读出来是 BGRmatplotlib 按 RGB 显示。三者混用是可视化翻车的高频原因——掩码叠上去颜色对但原图偏蓝。统一用 PIL 或统一用 cv2 加cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。另外标签若是单通道imshow要指定cmapgray否则 matplotlib 会套默认色图看起来像多类标签误导判断。4. 接上训练从这份数据集到可用的分割模型4.1 转成通用训练格式多数分割框架吃的是「图像 单通道掩码」配对或 COCO/VOC 格式。这份数据集已经是配对形式最省事的是写一个 Dataset 类直接读。import os import numpy as np import torch from torch.utils.data import Dataset from PIL import Image class TongueSegDataset(Dataset): def __init__(self, img_dir, lbl_dir, size(256, 256)): self.img_dir img_dir self.lbl_dir lbl_dir self.size size self.names [n for n in sorted(os.listdir(lbl_dir)) if os.path.splitext(n)[0] .jpg in os.listdir(img_dir) or os.path.splitext(n)[0] .png in os.listdir(img_dir)] def __len__(self): return len(self.names) def __getitem__(self, idx): name self.names[idx] stem os.path.splitext(name)[0] img_path None for ext in (.jpg, .png, .jpeg, .bmp): p os.path.join(self.img_dir, stem ext) if os.path.exists(p): img_path p break img Image.open(img_path).convert(RGB).resize(self.size, Image.BILINEAR) mask Image.open(os.path.join(self.lbl_dir, name)).resize(self.size, Image.NEAREST) img np.array(img, dtypenp.float32) / 255.0 mask (np.array(mask) 0).astype(np.float32) img torch.from_numpy(img).permute(2, 0, 1) mask torch.from_numpy(mask).unsqueeze(0) return img, mask关键参数size统一到 256×256图像用双线性插值标签用最近邻这是铁律。mask 0兼容两种编码。permute(2,0,1)把 HWC 转 CHW 适配 PyTorch。unsqueeze(0)给掩码加通道维变成 1×H×W。__init__里的 names 过滤保证只保留有配对图片的标签避免运行时 FileNotFoundError。4.2 训练时的损失与指标选择二类分割用 BCEWithLogitsLoss 或 Dice Loss两者结合更稳。BCE 对类别不平衡敏感舌体占比小的时候背景会主导梯度Dice 能拉回来。指标看 IoU 和 Dice不要只看 accuracy——全预测背景也能有 80% 以上 accuracy但 IoU 为 0。import torch import torch.nn as nn bce nn.BCEWithLogitsLoss() def dice_loss(logits, targets, eps1e-6): probs torch.sigmoid(logits) inter (probs * targets).sum(dim(2, 3)) union probs.sum(dim(2, 3)) targets.sum(dim(2, 3)) return 1 - ((2 * inter eps) / (union eps)).mean() def total_loss(logits, targets, w0.5): return w * bce(logits, targets) (1 - w) * dice_loss(logits, targets)eps防止除零w0.5是常用起点舌体占比特别小时可调到 0.3 让 Dice 权重更大。sigmoid只在 Dice 里用BCEWithLogitsLoss 内部已含 sigmoid不要重复。4.3 验证集划分与过拟合判断这份数据集若样本量不大按 8:1:1 划分且要保证同一受试者的图片不跨集。判断过拟合看训练 loss 持续降而验证 IoU 连续多个 epoch 不升。此时优先加数据增强水平翻转、小角度旋转、亮度抖动而不是加 dropout。舌体左右近似对称水平翻转安全垂直翻转会让舌根朝上不符合解剖结构别用。5. 避坑与排查舌头分割数据集常见的五个翻车点现象一可视化掩码全黑。原因标签编码是 0/255代码里直接当 0/1 用mask 1匹配不到任何像素。解决统一用mask 0转二值或在 Dataset 里除以 255。现象二训练 loss 一直不降。原因图像和标签没对齐比如 resize 时一个用双线性一个用最近邻导致边界错位或文件名匹配错位读了别的样本的标签。解决先跑 3.3 的网格可视化确认配对正确再检查 resize 插值方式。现象三验证 IoU 高但实际预测全是背景。原因只看 accuracy 或 loss没看 IoU。背景占比大时模型偷懒全预测背景。解决监控 Dice 和 IoU损失里加 Dice 项必要时对前景像素加权。现象四不同图片尺寸混在一起训练报错。原因数据集里图像尺寸不统一DataLoader 的 batch 拼接失败。解决Dataset 里强制 resize 到固定尺寸或自定义 collate_fn 做 padding。推荐前者简单可靠。现象五可视化颜色正常但保存的图偏色。原因matplotlib 保存时用了默认色彩空间或 cv2 读图 BGR 没转 RGB。解决统一 PIL 读图保存前确认数组是 RGB 且 dtype 为 uint8savefig不要开transparent叠在非白底上。6. 进阶技巧用可视化反推标签质量并做半自动清洗可视化不只是出图它能反过来当质检工具。我的习惯是训练前先跑一遍全量网格图按掩码面积排序面积异常小或异常大的排前面重点看。面积占比低于 1% 的可能是漏标高于 60% 的可能是把整张脸都标进去了。用下面这段算面积分布比一张张看快得多。import os import numpy as np from PIL import Image areas [] for name in sorted(os.listdir(labels)): mask np.array(Image.open(os.path.join(labels, name))) ratio (mask 0).mean() areas.append((name, ratio)) areas.sort(keylambda x: x[1]) print(smallest 5:, areas[:5]) print(largest 5:, areas[-5:])(mask 0).mean()直接给出前景像素占比排序后两头就是可疑样本。正常舌体在正面照里占比大概 15% 到 45%超出这个范围先怀疑标签。发现脏标签后可以只对这些样本重新标注或剔除不必全量返工。另一个技巧是把预测结果和原始标签叠在一起做对比图训练几个 epoch 后跑一次能直观看出模型是欠拟合掩码比标签小一圈还是过拟合掩码贴着标签但泛化差。我一般每 10 个 epoch 存一张对比图比盯 loss 曲线直观。这套流程跑下来从拿到数据集到出一个能用的二类分割模型顺利的话半天到一天。真正花时间的从来不是写模型而是把标签看清楚、把可视化跑通、把脏样本挑出来。希望帮到你。本文还有配套的精品资源点击获取