尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

骶骨腰痛脊椎分割数据集:三切面五类别标注与可视化实战

骶骨腰痛脊椎分割数据集:三切面五类别标注与可视化实战 简介面向医学图像分割与深度学习研究的骶骨腰痛脊椎分割数据集来自公开脊柱分割项目按轴位面、冠状面、矢状面三个方向切分出二维图像共标注五个类别。所有切片已滤除感兴趣区域占比不足百分之三的样本并完成医学影像常用的窗宽窗位对比度增强统一缩放至512×512像素图像采用JPG格式存储对应掩码为PNG格式八位灰度图非零数值为前景、零为背景类别编号见classes.txt。资源包共2000个文件以1998个PNG图像与掩码文件为主另含1个类别说明文本和1个可视化脚本show.py压缩包总大小472.47MB目录按x、y、z三个切面分别组织images与masks子目录各含2113对、2272对、6404对图像和掩码可直接用于训练UNet、DeepLab等分割网络。show.py可叠加显示分割结果便于快速核对标注质量。目前已有224人学习使用适合需要标准预处理脊椎CT数据的研究者、算法工程师及医学影像相关专业学生。1. 骶骨腰痛脊椎分割的数据集到底长什么样3个切面、5个类别的起点医学图像分割数据集这个方向最容易让新手失控的不是模型而是数据本身。手头这套“骶骨腰痛脊椎分割”数据集主打的就是三样东西3个切面划分好的图像、5类别的标签文件、以及现成的可视化代码。换句话说它不是一个只给你一堆 PNG 和 mask 的裸数据包而是把矢状面、冠状面、轴位都按统一逻辑切好把骶骨和腰椎的关键结构标成 5 个类别并且附带能让你第一时间把分割结果“叠回原图”的工具脚本。对正在做腰痛相关影像分析、脊柱外科辅助诊断或者分割模型复现的工程师来说这套数据能帮你省掉最脏最累的格式整理和对齐工作直接进入训练和评估环节。2. 数据集文件拆解三个切面与5类别标签如何对应2.1 三切面划分逻辑矢状面、冠状面、轴位各自覆盖什么结构腰痛和骶骨相关的影像评估临床上最依赖的是矢状面因为腰椎的生理曲度、椎间盘退变、腰骶角变化、骶骨倾斜度在矢状面上看得最清楚。这套数据把矢状面作为第一个切面目录原因就在这里。冠状面对观察脊柱侧弯和左右对称性有帮助轴位则是看椎间盘突出方向、椎管狭窄和关节突关节的关键视角。三个切面不是简单把同一个三维体积各切一刀而是按照解剖结构重新组织成独立的 2D 切片序列这一点在你后续做训练集划分时要格外留意。我在处理这类数据集时第一件事永远是确认目录里的切面划分是否与 DICOM 原始扫描的 orientation 一致。有的数据包表面写着“矢状位”实际是从轴位体数据里重采样出来的并不等价于真正的矢状扫描序列。区别在于层厚和平面内分辨率原生矢状位的层厚通常较薄而重采样得到的矢状位切片在 z 方向上有明显拉伸。从训练角度说模型学习到的纹理特征会有差异迁移到真实临床数据时表现会打折。tree -L 2 data/data/ ├── sagittal/ │ ├── case001/ │ │ ├── img/ │ │ ├── label/ │ │ └── info.json │ ├── case002/ │ └── ... ├── coronal/ │ ├── case001/ │ └── ... ├── axial/ │ ├── case001/ │ └── ... ├── class_names.json ├── train_val_split.txt └── visualize.py上面这个目录结构是这套数据最常见的组织形式。sagittal、coronal、axial三个目录各放各的切片同一个case001在三个切面下都有对应的子目录。每个 case 下的img放原始灰度图label放同尺寸的分割标签info.json则记录切片编号、层厚、像素间距、窗宽窗位等元信息。不要小看这个 json它在你做切面对齐和体数据重建时是救命稻草。2.2 5类别的索引约定与标签文件格式5 个类别需要先搞清楚索引约定。常见做法是把 0 留作背景然后按解剖结构排列类别索引结构名称说明0background背景1sacrum骶骨2vertebra腰椎椎体3disc椎间盘4posterior棘突、椎弓根等后柱结构为什么这样分腰痛场景里椎间盘是最常见的病变位置骶骨是承重和力线传导的关键椎体骨折和滑脱也要看后柱结构则关系到手术入路和内固定规划。五类刚好覆盖了腰痛影像评估的主要解剖对象但又不会细到每个椎体单独编号否则标注成本会暴涨。标签文件的格式通常是 PNG、NIfTI 或 NRRD 三者之一。2D 切片数据多用 PNG 保存优点是体积小且查看方便缺点是容易丢失原始像素间距等空间信息。3D 体数据则常用 NIfTI 格式头文件里保留了 direction、spacing、origin 三个关键矩阵。如果你的数据包同时包含 NIfTI 体数据和 2D 切片 PNG建议以 NIfTI 为准因为从三维信息可以随时重新切片反过来却不行。import json, cv2 import numpy as np with open(data/class_names.json, r, encodingutf-8) as f: class_names json.load(f) # 检查一张标签图的类别值分布 label_path data/sagittal/case001/label/slice_023.png label cv2.imread(label_path, cv2.IMREAD_UNCHANGED) # 有些标签PNG是16位或带调色板的转成灰度索引图再统计 if label.ndim 3: label label[:, :, 0] unique_vals np.unique(label) print(class mapping:, class_names) print(unique values in label:, unique_vals)这段代码做的事情是读取类名映射 JSON加载一张标签 PNG并统计图像里面出现了哪些像素值。如果标签文件保存成 RGB 三通道 PNG代码里会取第一个通道来获得索引值。统计结果能快速帮你确认这张图上到底标了哪几个类别以及是否存在超出 04 范围的异常像素值。常见的一个坑是某些标注工具会把背景保存成 255 而不是 0如果发现 unique values 里出现 255记得做一次重映射否则训练时背景类会直接少掉一大块。2.3 用一套小脚本核对文件完整性与切面属性拿到数据集后的第一件事不是训练而是做一次完整性检查。三个切面目录下的切片数量是否一致每张图像和标签的尺寸是否匹配标签类别是否都在约定范围内这些都是必须先验的。写过分割训练的人都知道模型跑起来之后报“size mismatch”已经是比较友好的错误了最恶心的是图像和标签尺寸一致但内容对不上那才叫半夜加班。import os import cv2 def verify_dataset(base_dir): for plane in [sagittal, coronal, axial]: case_root os.path.join(base_dir, plane) for case_name in os.listdir(case_root): case_dir os.path.join(case_root, case_name) img_dir os.path.join(case_dir, img) lbl_dir os.path.join(case_dir, label) img_files sorted(os.listdir(img_dir)) lbl_files sorted(os.listdir(lbl_dir)) if len(img_files) ! len(lbl_files): print(f[MISMATCH] {plane}/{case_name}: img{len(img_files)} lbl{len(lbl_files)}) continue for img_f, lbl_f in zip(img_files, lbl_files): img cv2.imread(os.path.join(img_dir, img_f), cv2.IMREAD_GRAYSCALE) lbl cv2.imread(os.path.join(lbl_dir, lbl_f), cv2.IMREAD_UNCHANGED) if lbl.ndim 3: lbl lbl[:, :, 0] if img.shape ! lbl.shape: print(f[SIZE] {plane}/{case_name}/{img_f}: img{img.shape} lbl{lbl.shape}) break else: print(f[OK] {plane}/{case_name}: {len(img_files)} slices) verify_dataset(data)这段脚本会对三个切面目录逐一遍历检查图像与标签的文件数量是否一致、每张图的尺寸是否相同。代码里的for...else表示循环正常结束时执行else分支一旦发现尺寸不一致就会break跳出避免后续无用检查。三类问题中“数量不一致”最常见于标注过程中有人删了某几张质量差的切片但忘了同步删除对应图像“尺寸不一致”则通常源于某次预处理时用了不同的裁剪参数。建议把脚本输出重定向到日志文件里方便和原始数据描述逐条比对。3. 用可视化代码把标签叠回原图两步走别让黑匣子骗你3.1 读取 NIfTI 体数据并抽取一个切面如果你的数据包不是纯 2D 切片而是以三维体形式提供的那么需要先知道怎么从体数据里抽出指定切面。用 SimpleITK 读取 NIfTI 时拿到的顺序是(x, y, z)注意不是(z, y, x)这一点经常让人翻车。抽取矢状面时固定 x 坐标抽取冠状面固定 y 坐标抽取轴位固定 z 坐标具体取哪个索引要看标记在哪个结构附近。import SimpleITK as sitk import numpy as np def load_and_extract_plane(image_path, label_path, planesagittal, indexNone): img sitk.ReadImage(image_path) lbl sitk.ReadImage(label_path) arr_img sitk.GetArrayFromImage(img) # shape: (z, y, x) arr_lbl sitk.GetArrayFromImage(lbl) if plane sagittal: # 固定x方向索引保留(z, y) idx index if index is not None else arr_img.shape[2] // 2 slice_img arr_img[:, :, idx] slice_lbl arr_lbl[:, :, idx] elif plane coronal: idx index if index is not None else arr_img.shape[1] // 2 slice_img arr_img[:, idx, :] slice_lbl arr_lbl[:, idx, :] else: # axial idx index if index is not None else arr_img.shape[0] // 2 slice_img arr_img[idx, :, :] slice_lbl arr_lbl[idx, :, :] return slice_img, slice_lbl, img.GetSpacing(), idxGetArrayFromImage返回的数组维度顺序是 z、y、x和 ITK 内部的 x、y、z 顺序相反。所以矢状面切片取arr[:, :, idx]其中idx对应着体数据在 x 方向上的索引。这里把索引写成可传参数而不是硬编码是因为在实际操作中你想看的往往不是正中间那张而是腰骶角最明显的旁正中矢状位切片。抽取结果返回的同时带上spacing信息后面如果要做像素级统计或者可视化时按实际物理尺寸标注会方便很多。3.2 彩色叠加与窗宽窗位对照有了单个切片之后叠加显示是最直观的验证手段。灰度图作为底图标签用半透明的彩色蒙版覆盖在上面。关键参数是透明度和配色方案透明度太低标注重叠不明显透明度太高又看不清底下的解剖结构。0.350.5 是一个相对可靠的区间。配色建议每种类别分配一个固定颜色尤其是椎间盘这类小结构用亮色系不然叠上去很容易被背景吞掉。import matplotlib.pyplot as plt import numpy as np COLORS { 0: [0, 0, 0], # 背景 1: [255, 0, 0], # 骶骨 红 2: [0, 255, 0], # 椎体 绿 3: [255, 255, 0], # 椎间盘 黄 4: [0, 255, 255], # 后柱 青 } def overlay_label(image, label, alpha0.4, save_pathNone): rgb np.stack([image] * 3, axis-1) label_rgb np.zeros_like(rgb) for cls, color in COLORS.items(): mask label cls label_rgb[mask] color result rgb * (1 - alpha) label_rgb * alpha result result.astype(np.uint8) plt.figure(figsize(8, 8)) plt.imshow(result) plt.axis(off) if save_path: plt.savefig(save_path, dpi150, bbox_inchestight) plt.show()这段代码直接对 NumPy 数组做混合像素值会被等比例拉伸到 0255 的显示范围。注意plt.imshow处理灰度图时默认用 colormap 映射叠加时如果底图是 float 类型且取值范围不在 01会显示成一片白或者一片黑。所以实际使用时要先检查输入 image 的数据范围。这里选择给每一类单独设置 RGB 颜色而不是用matplotlib的jetcolormap原因在于直接看颜色就知道是哪一类结构不需要额外猜索引也比图例更直观。3.3 可视化在标注质量检查里的用法可视化代码的价值不只是“看一眼结果”更在于校验标签边界是否贴合解剖轮廓。我一般会生成“左一右三”的对比图左边是原图中间是标签叠加右边只显示标签。这样能同时看清原图结构、标注覆盖位置、以及有没有明显偏离。生成全部切片的对比图之后按目录顺序快速翻过去能在几分钟之内发现绝大多数质量问题。def save_grid_case(img_slices, lbl_slices, case_name, output_dir): os.makedirs(output_dir, exist_okTrue) for i, (img, lbl) in enumerate(zip(img_slices, lbl_slices)): fig, axes plt.subplots(1, 2, figsize(10, 5)) axes[0].imshow(img, cmapgray) axes[0].set_title(original) axes[1].imshow(img, cmapgray) axes[1].imshow(lbl, cmapjet, alpha0.4) axes[1].set_title(overlay) plt.savefig(os.path.join(output_dir, f{case_name}_{i:03d}.png)) plt.close(fig)这里把cmapjet用于标签图而不是手工定义每个类别的颜色优点是代码简洁、不需要维护颜色映射表缺点则是相邻类别在视觉上可能分不清。所以这条代码只适合快速筛不适合做正式结果的报告图。正式报告请回到 3.2 中固定颜色的方案。保存到输出目录时用case名序号命名这样翻图时能快速定位到具体哪个案例和哪一层出了问题。4. 从数据集到训练集预处理、归一化与三切面训练策略4.1 CT HU 值裁剪与 MRI 归一化别用一套参数打天下腰痛脊椎分割的数据来源可能同时包含 CT 和 MRI。CT 图像是标准的 HU 值单位正常范围是 -1024 到 3071但有用的信息集中在骨组织和软组织之间。骶骨和腰椎椎体在 CT 上表现为高亮度的皮质骨边缘和相对低密度的松质骨内部如果直接做全局 min-max 归一化软组织对比度会被压缩到几乎看不见分割效果必然崩。我通常对 CT 做窗宽窗位裁剪骨窗用窗宽 1500、窗位 500软组织窗用窗宽 400、窗位 50。def ct_window_clip(image, window_width1500, window_level500): lower window_level - window_width / 2 upper window_level window_width / 2 return np.clip(image, lower, upper).astype(np.float32) def normalize(image, modect_bone): if mode ct_bone: image ct_window_clip(image, 1500, 500) return (image - (-250)) / (1250 - (-250)) elif mode ct_soft: image ct_window_clip(image, 400, 50) return (image - (-150)) / (250 - (-150)) elif mode mri: mean image.mean() std image.std() return (image - mean) / (std 1e-8)窗口裁剪之后的归一化不是把整个数组线性缩放到 01而是基于窗宽的下界和上界做映射。窗口下界是level - width/2在这个例子里是 500 - 750 -250上界是 1250。这样裁剪后小于下界的像素统一变 0大于上界的统一变 1中间的组织细节得到最大保留。MRI 没有统一的物理单位直接 z-score 归一化即可。关键在于qcut那张图的顺序如果你把 CT 和 MRI 放在同一个训练集里却用同一套归一化参数模型会学到“亮度不一致”这种完全没有意义的特征匹配。4.2 类别不平衡与损失函数选择骶骨和椎体在整张切片里占的面积不小但椎间盘是典型的小目标几个像素宽的结构夹在两个椎体之间后柱结构也是细条状分布。这种情况下如果直接用 softmax cross-entropy模型大概率会“学会”把所有像素都预测成背景和前两大类别椎间盘直接消失。标准做法是给每个类别算权重权重和像素频率成反比同时配合 DICE Loss。from collections import Counter def compute_class_weights(label_paths, num_classes5): counts Counter() for lp in label_paths: lbl cv2.imread(lp, cv2.IMREAD_UNCHANGED) if lbl.ndim 3: lbl lbl[:, :, 0] vals, cnts np.unique(lbl, return_countsTrue) for v, c in zip(vals, cnts): counts[int(v)] c total sum(counts.values()) class_weights [] for i in range(num_classes): freq counts.get(i, 0) / total class_weights.append(1.0 / (freq 1e-6)) # 归一化让背景权重为1其他类别是相对值 class_weights np.array(class_weights) class_weights / class_weights[0] return class_weights这套计算前需要先跑一遍完整的标签统计默认假设标签 PNG 是灰度索引图。代码里对三通道 PNG 的处理是取第一个通道如果标签恰好是调色板 PNG第一个通道可能不是索引排查的方法很简单——直接打印np.unique(lbl)看值域是否在 04。权重计算出来后在损失函数里这样用loss F.cross_entropy(logits, target, weighttorch.from_numpy(class_weights).float())。背景权重被归一化成 1椎间盘的权重可能到 10 甚至更高模型才会被迫认真学习小结构的边缘。4.3 数据增强和控制输入尺寸的实操值三切面各自的图像长宽比例不一样。矢状面通常细长轴位接近方形训练时如果直接 resize 到统一尺寸椎间盘那种窄长结构会被拉伸到失真。一个常见做法是限制短边再随机裁剪比如把输入固定为 256×256但矢状面先 resize 成 192×320 再随机裁剪。这个操作在验证时也要同步否则推理时的输入分布和训练时不一致边界预测会变得飘忽。def train_augmentation(img, label, crop_size(256, 256), p0.5): h, w img.shape[:2] # 先做短边缩放长边等比缩放 scale max(crop_size) / min(h, w) new_h, new_w int(round(h * scale)), int(round(w * scale)) img cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) label cv2.resize(label, (new_w, new_h), interpolationcv2.INTER_NEAREST) # 随机裁剪到目标尺寸 if new_h crop_size[0] and new_w crop_size[1]: top np.random.randint(0, new_h - crop_size[0]) left np.random.randint(0, new_w - crop_size[1]) img img[top:top crop_size[0], left:left crop_size[1]] label label[top:top crop_size[0], left:left crop_size[1]] else: img cv2.resize(img, crop_size, interpolationcv2.INTER_LINEAR) label cv2.resize(label, crop_size, interpolationcv2.INTER_NEAREST) if np.random.rand() p: img np.fliplr(img).copy() label np.fliplr(label).copy() return img, label代码里两个关键点先说清一是标签的 resize 插值方式必须是INTER_NEAREST用线性插值会让类别边界出现小数产生非法类别值二是随机翻转时标签和图像必须使用相同的轴并且要copy()因为np.fliplr返回的是只读视图后续转 tensor 会报错。旋转和弹性形变我一般不在第一版训练里加先跑基线后面看模型的过拟合情况再逐步加。5. 避坑指南三切面数据集的5个常见踩坑记录5.1 现象标签和图像明明同尺寸错位却有一层——方向矩阵没有对齐某个案例的矢状面切片序列前期看单个切片图像和标签看不出异常但把所有切片叠成体积后发现标签整体向右偏移了一个像素。单个切面上这种偏移完全不可见因为边界本身就模糊。原因读取 NIfTI 时用了sitk.GetArrayFromImage得到的是 z、y、x 顺序的数组但可视化时用了plt.imshow的默认坐标轴。ITK 的物理坐标系原点在图像左下角而 NumPy 数组的索引原点在左上角两者在 y 轴方向上天然差了一个翻转。如果标签是从 ITK 读取的而图像是从 nibabel 读取的两个库对数组维度的处理方式不同就会悄悄让 mask 翻转或者错位。解决统一使用同一个库读图像和标签。如果图像来自nibabel标签也要用nibabel读然后再做np.flip对齐。在训练代码里推荐在数据加载类中固定direction方向统一输出为“矢状面右前后”朝向不要每个切片单独处理。5.2 现象Dice 很高可医生看了说“骶骨磨没了”——边界与临床标准不一致自动分割的 Dice 在验证集上到了 0.9 甚至 0.93但把结果交给临床合作医生看反馈是骶骨边缘少了一圈、椎间盘有假阳性。原因在于训练时默认把所有类别的像素一视同仁而临床关注的是骶骨终板和椎间盘边缘的完整性。这个例子说明“指标好”和“可用”是两码事。解决对骶骨和椎间盘这两个类别单独算边界 Dice也就是只评估距离边界若干个像素范围内的预测正确率。同时把损失函数换成soft-dice boundary loss的组合让模型在边界区域更谨慎。另外一个更朴素的办法是可以考虑用可视化代码生成局部放大图把骶髂关节附近单独截出来一张大图写进测试报告里不只看全局指标。5.3 现象椎间盘和椎体边界黏在一起——类间距过近导致混淆矢状面上椎间盘夹在上下椎体之间灰度值和邻近骨组织在 CT 上有重叠尤其在椎间盘退变脱水后其密度接近软组织。模型经常把椎间盘区域并进椎体类单看数值叫类混但视觉上就是“没看到椎间盘”。原因类别之间在低分辨率特征图上相互干扰加上椎间盘像素占比小特征提取阶段就丢失了。解决训练时不再从全图随机裁剪而是以椎间盘中心为锚点做局部裁剪提高小类别在 batch 里的出现频率。推理后处理上加一条先验约束椎间盘必须出现在两个椎体标签之间且形态偏向扁椭圆形。如果分割结果不具备这个特征大概率是预测错了需要回炉。后处理的做法帮你把“模型学不到”的部分用解剖学知识兜住。5.4 现象验证集预测稳定但新样本翻车——把三个切面当独立数据集的问题同一套数据里矢状面、冠状面、轴位三个切面单独训练效果都不差但把三个切面的推理结果融合到同一个三维体积里时互相矛盾比如轴位预测说这里有个椎间盘矢状位预测却把它标成椎体。原因把一个病例的三个切面当三个独立数据集用切分训练集时没有按病例锁定。同一个病人的三组切片被拆进了 train 和 val验证集形同虚设。解决划分时必须保证同一人的三切面数据要么全在训练集要么全在验证集不能混。一个可靠策略是用train_val_split.txt里的病例 ID 列表来划分而不是按文件路径随机分割。这在三切面数据集中是高概率踩中的坑处理不干净时所有后续对比都不成立。5.5 现象读取标签时出现小数 mask——归一化放错了阶段训练中途报错target里出现非整数像素值损失函数直接崩。查了半天发现是数据加载时先做了归一化再调到了 float32。原因标签图像在预处理流水线里被当成了普通图像处理在归一化公共函数里被整体缩放到 01。分割标签本质上是一个离散索引图不是灰度图像任何对它的数值变换都会破坏类别语义。解决图像和标签永远走两条独立的预处理通道。图像可以归一化、裁剪、增强标签只允许最近邻插值、翻转和裁剪不能做任何数值缩放。建议在 DataLoader 的__getitem__里直接将标签astype(np.int64)一旦出现 float 型就抛异常从源头拦住这类低级错误。6. 进阶三切面推理融合与质控报告让分割结果可复用6.1 把三个切面的概率图融合成一个体积当三个切面各有模型时融合是一件值得认真对待的事。常见做法不是做“投票”而是将每个切面模型输出的概率图重采样到同一三维空间然后逐体素取平均。矢状面对椎间盘敏感轴位对椎管形态和关节突更准简单平均会导致分辨率高的细节被分辨率低的切面拉平。更稳的方案是按结构加权骶骨与椎体用矢状面权重高一些椎间盘用轴位权重高一些后柱结构用冠状面权重高一些。三个权重参数不需要精调按 0.5/0.3/0.2 起步在验证集上微调即可。重采样时用sitk.Resample的线性插值处理概率图最终取 argmax 得到硬标签再套一层连通域过滤掉孤立小斑块。6.2 生成可视化质控页用“不看指标看切片”做最终把关自动化指标永远不能代替目检我用一个简单的脚本把验证集每例生成一页质控报告上半部分放三张有代表性的切面叠加图下半部分放每类别的 Dice、IoU、表面距离。打印之前先快速翻一遍叠加图哪一例的边缘不对就回到那一例单独查不进入发布流程。做这一步的价值在于防止测试集过拟合也防止“指标没崩但结构位置错”这种隐蔽翻车被发出去。def generate_quality_report(case_name, slices, labels, preds, metric_fn, out_pdf): from matplotlib.backends.backend_pdf import PdfPages with PdfPages(out_pdf) as pdf: for i, (sl, lb, pr) in enumerate(zip(slices, labels, preds)): if i % 10 ! 0: continue fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(sl, cmapgray) axes[0].set_title(input) axes[1].imshow(lb, cmapjet, alpha0.5) axes[1].set_title(ground truth) axes[2].imshow(pr, cmapjet, alpha0.5) axes[2].set_title(prediction) fig.suptitle(f{case_name} slice {i}) pdf.savefig(fig) plt.close(fig)每十层输出一页既保证覆盖度又不至于生成几百页 PDF。PdfPages会把多页拼成一个文件方便邮件发送和存档。这里的metric_fn传入每层的指标字典按你的需求计算即可。我自己一直坚持先看切片再谈优化因为分割模型的翻车往往不在整体数值上而在个别病例的解剖变异里。这类数据集的正确打开方式就是三切面互相验证、可视化全程兜底。希望这套经验能让你少走几步弯路直接看到真实问题所在。本文还有配套的精品资源点击获取
返回列表