【Pair使用笔记指南1】标注文件语义分割 mask 处理

本文讲述了如何解析Pair标注的NII文件,通过旋转和翻转操作调整mask与原始图像对应,并详细介绍了多标签类别拆分与选择的实现过程,以便于后续数据处理和应用如转为LabelMe格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pair标注后保存的文件,是一个压缩文件。解压后会包含一个json文件和一个nii文件。本文就将对nii文件进行解析。完成各个类别的分拆,验证是否存在问题

官方Pair标注结果读取说明可以通过软件获取。标记后解析nii文件后的mask图例,如下右侧图。

(因为是发现了问题,才尝试了去解决问题。下文采用倒叙方式,由处理好的结果,一步步的探究由来)。

标记后解析nii文件后的mask

就发现直接从nii文件读取保存后的标注文件,与在pair软件上的位置不太对。经过仔细的对比才发现,是方位发生了变化。下面就将pair标记好的mask,与原始图进行匹配。

1.标注mask匹配

经过了两次变换,分别是旋转90度和水平翻转,就发现转出来的结果就可以和pair软件匹配上了,匹配的结果如下所示:

(这块没有来得及和官方人员进行沟通,没有明白其中的思路,为什么保存的nii文件,还需要两步操作才能和pair软件显示的对应上)

# 旋转90度
mask = cv2.rotate(mask, cv2.ROTATE_90_CLOCKWISE)
# 水平翻转
right_mask = cv2.flip(mask, 1)

匹配图
上述变换和图像展示的完整代码也分享出来,如下这样:


import cv2
import os
import numpy as np

mask_Dir = r'F:\Pair\data\label-all'
raw_Dir = r'F:\Pair\data\image'
mask_list = os.listdir(mask_Dir)
raw_list = os.listdir(raw_Dir)
for i in range(len(mask_list)):
    maskfile_name = mask_list[i]
    rawfile_name = raw_list[i]
    mask_path = os.path.join(mask_Dir, maskfile_name)
    raw_path = os.path.join(raw_Dir, rawfile_name)

    raw_img = cv2.imread(raw_path, 1)

    mask = cv2.imread(mask_path, 0)
    # 旋转90度
    mask = cv2.rotate(mask, cv2.ROTATE_90_CLOCKWISE)
    # 水平翻转
    mask = cv2.flip(mask, 1)

    cv2.imshow(maskfile_name+'mask', mask)
    # cv2.waitKey(1000)

    contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        xmin, ymin, xmax, ymax = x, y, x + w, y + h
        print(xmin, ymin, xmax, ymax)

        cv2.drawContours(raw_img, [contour], 0, (0, 0, 255), 1)
        cv2.rectangle(raw_img, (int(xmin), int(ymin)), (int(xmax), int(ymax)), (0, 255, 0), 1)

    cv2.imshow(maskfile_name+'img', raw_img)
    cv2.waitKey(1000)

其中image和label文件夹内容如下所示:

image-label
到这里就要问,那label是如何获取的呢?获取方式如下:

  • pair标注生成标注压缩文件
  • 解析压缩文件产生json和nii.gz文件
  • nii.gz文件转储为上述label文件这样的图片形式

具体的产生代码如下(这里稍微会复杂一点点,因为我想要引出pair的多标签标注的解析):

import numpy as np
import nibabel as nib
import os
from PIL import Image

def nii_2img(select=False):
    label = nib.load(r"./data\17\1_2_840_113704_1_1762661706_15468_1049307570_11_1_3_2_0_62_0_62_dcm_Label.nii.gz")

    #Convert them to numpy format,
    label_data = label.get_fdata()
    print("=======label shape=======")
    print(label_data.shape)
    print("=======label value=======")
    print(label_data[label_data != 0])

    # 挑选类别
    if select:
        print('change after ---')
        label_data[label_data != 20.0] = 0

    #extract 2D slices from 3D volume for training cases while
    # e.g. slice 000
    for i in range(label_data.shape[2]):
        label_one = label_data[:, :, i]
        label_slice000 = label_one * 255
				
				# 旋转和翻转也可以直接放到这里,就不存在开篇对不上的问题
				# 旋转90度
        mask = cv2.rotate(label_slice000, cv2.ROTATE_90_CLOCKWISE)
        # 水平翻转
        label_slice000 = cv2.flip(mask, 1)

        nozero_list = label_one[label_one != 0]
        if 21.0 in nozero_list:
            print("=======label shape=======")
            print(label_one.shape)
            print("=======label value=======")
            print(label_one[label_one != 0])

            np.savetxt(r"./data/txt/" + str(i) + ".txt", label_one, delimiter=',', fmt='%5s')

        label = Image.fromarray(label_slice000)
        label = label.convert("L")
        label.save(r"./data/label/" + str(i) + "_label.png")

if __name__ == "__main__":
    nii_2img(select=False)

可以看到select这个选项,这是干神马的呢?简单点就是多标签的类别解析

label

2.各类别拆分和mask转储

上图可以看到,此case的标注类别有两个,分别是类别20和类别21,此时的类别就变得丰富了起来,不再只是单类问题了。通过上述代码中:

np.savetxt(r"./data/txt/" + str(i) + ".txt", label_one, delimiter=',', fmt='%5s')

查看下保存的TXT文件内容,就知晓了。

同时,也为选择某一类别,提供了依据,有时标记了很多类,但是在使用阶段,可能并不需要那么多类,这就是select存在的意义。

TXT

修改下面内容,即可实现对对应类别的筛选。20就是标记序号为20多类,如果这个类的标号是31,那此事想要拿到这个类的位置信息,就需要将这里设定为21的保留下来,其余的都设为0:

# 挑选类别
if select:
    print('change after ---')
    label_data[label_data != 20.0] = 0

那就展示下各个类别分开后的样子,如下:

label对比
至此,大功告成,完成了pair软件标注文件的后处理。有了这个各个类别的mask图,想干点其他的事情,都方便了很多。例如转labelme的json文件等等,都可以通用了。

PS:如果你的标记是3个类别,但是你只想取其中的两个类为目标,其他类别为0展示,对select部分补充如下:

# 挑选类别
    if select:
        print('change after ---')
        print(type(label_data))

        # method select one class
        # label_data[label_data != 19.0] = 0

        # method select one or mult class
        index = np.ones(label_data.shape)
        for class_i in TB_ClassList:
            index_i = label_data != class_i
            index = index_i * np.array(index, dtype=bool)

        print(index, type(index))
        label_data[index] = 0

这样就实现了我们的想法,展示结果如下:

mult select

上述内容不仅对于pair软件试用,对于大多数语义分割的数据标注集的处理同样适用。关于nii.gz这部分更多的处理,可以参考这片文章:nii、npz、dcm 的数据处理,及多目标分割处理汇总

对于mask图转labelme能标注的json文件的方式,欢迎浏览这片文章:SIIM-ACR Pneumothorax Segmentation 气胸x光识别比赛数据处理

<think>好的,用户想使用DINOv2模型在TUM数据集上实现语义分割。首先,我需要确认DINOv2的基本信息和适用性。DINOv2是Meta提出的自监督视觉模型,主要用于特征提取,但本身不直接支持语义分割。所以用户需要在其基础上进行微调或者添加分割头。 接下来,TUM数据集的情况。TUM数据集主要用于SLAM(同步定位与地图构建),包含RGB-D图像和相机轨迹,但可能没有语义标签。因此,用户可能需要先对TUM数据集进行标注,或者寻找已有的标注版本。如果数据没有标签,可能需要使用半监督方法或者迁移学习。 然后,步骤拆分。首先需要加载预训练的DINOv2模型,通常使用torch.hub或者官方提供的模型。然后修改模型结构,添加分割头,比如U-Net类似的解码器部分。数据预处理部分,TUM数据集是RGB-D,可能需要将深度信息与RGB结合,但DINOv2可能只接受RGB输入,所以需要考虑如何处理深度信息,可能作为额外通道或者单独处理。 数据加载部分,需要读取TUM的数据结构,通常是按时间戳排列的图像序列,并配对深度图和位姿信息。但语义分割需要像素级标签,所以必须确保有对应的标注文件。如果用户没有标注,可能需要自己标注使用其他方法生成伪标签。 训练流程方面,定义损失函数,如交叉熵损失,选择合适的优化器。可能需要冻结DINOv2的主干部分,只训练分割头,或者微调整个网络,取决于数据集的大小和计算资源。 评估部分,使用常见的语义分割指标,如mIoU、像素准确率等。但需要注意TUM数据集原本用于SLAM,可能没有标准的分割评估协议,需要用户自己划分训练集和测试集。 潜在问题:TUM数据集缺乏语义标签,这是主要障碍。可能需要使用其他已标注的数据集进行预训练,再迁移到TUM,或者采用无监督/半监督方法。另外,DINOv2作为ViT模型,处理高分辨率图像时可能需要调整窗口大小或使用分层特征。 最后,生成相关问题需要考虑用户可能的后续疑问,比如数据标注工具、模型微调技巧、处理计算资源不足的方法等。</think>使用DINOv2模型在TUM数据集上实现语义分割的步骤如下: ### 一、环境准备 1. 安装依赖库: ```bash pip install torch torchvision timm matplotlib opencv-python ``` 2. 下载DINOv2预训练模型(选择ViT-L/14版本): ```python import torch dinov2 = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14') ``` ### 二、数据预处理 TUM数据集需要转换为适合语义分割的格式: 1. 创建RGB与深度图对齐的配对数据 2. 生成语义标注mask(需自行标注使用标注工具) 目录结构示例: ``` tum_seg/ ├── rgb/ │ ├── 1305031102.175304.png │ └── ... ├── depth/ ├── masks/ # 语义标注 ``` ### 三、模型改造 添加分割解码器到DINOv2: ```python class DinoSeg(torch.nn.Module): def __init__(self): super().__init__() self.encoder = dinov2 # 冻结前3层 self.decoder = torch.nn.Sequential( torch.nn.ConvTranspose2d(1024, 512, 3), torch.nn.ReLU(), torch.nn.ConvTranspose2d(512, 256, 3), torch.nn.ReLU(), torch.nn.ConvTranspose2d(256, num_classes, 3) ) def forward(self, x): features = self.encoder.forward_features(x) return self.decoder(features['x_norm_patchtokens'].permute(0,2,1)) ``` ### 四、训练流程 ```python # 数据加载 dataset = torch.utils.data.DatasetFolder( root='tum_seg', loader=lambda x: load_pair(x), # 实现RGB与mask配对加载 extensions=('.png') ) # 训练循环 for epoch in range(100): for rgb, mask in dataloader: pred = model(rgb) loss = torch.nn.functional.cross_entropy(pred, mask) loss.backward() optimizer.step() ``` ### 五、关键技巧 1. **深度信息融合**:将深度图作为第四通道输入: ```python input_tensor = torch.cat([rgb_tensor, depth_tensor], dim=1) ``` 2. **迁移学习策略**:前10个epoch冻结DINOv2主干,仅训练解码器 3. **数据增强**:对RGB-D数据实施同步的空间变换 ### 六、评估指标 使用mIoU进行评估: $$ \text{mIoU} = \frac{1}{C}\sum_{c=1}^C \frac{TP_c}{TP_c + FP_c + FN_c} $$ 实际部署时需注意TUM数据集的时间连续性特性,建议使用时序一致性约束提升视频分割稳定性[^1]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值