美文网首页
语义分割

语义分割

作者: 小黄不头秃 | 来源:发表于2022-09-22 09:45 被阅读0次

    (一)语义分割和数据集

    (1)什么是语义分割?

    语义分割将图片的每一个像素分类到对应的类别。神经网络能够在像素级别上能够将图片的每一个像素分类,即对每一个像素点分类。


    应用:背景虚化、无人驾驶的路面分割。

    另一个应用是实例分割,这个技术和语义分割很相似。但是他在语义的基础上加上了不仅要区分类,还要把列里面的实例标注出来。例如一张图片里有猫有狗,实例分割能知道有两只不同的狗和一只猫

    (2)语义分割的数据集

    最重要的语义分割的数据集之一是Pascal VOC2012

    (二)代码实现加载数据集

    关于数据集可以直接用浏览器下载,也可以通过代码下载
    下载地址:http://d2l-data.s3-accelerate.amazonaws.com/VOCtrainval_11-May-2012.tar

    如果你想知道语义分割的具体代码可以看我后续的文章:
    【全连接卷积神经网络】:https://www.jianshu.com/p/c12882cd99b1

    %matplotlib inline 
    import os 
    import torch 
    import torchvision 
    from d2l import torch as d2l 
    
    
    # 下载数据集
    if os.path.isdir('../data/VOCdevkit/'):
        voc_dir = "../data/VOCdevkit/VOC2012"
    else: 
        d2l.DATA_HUB['voc2012'] = (d2l.DATA_URL + 'VOCtrainval_11-May-2012.tar',
                               '4e443f8a2eca6b1dac8a6c57641b67dd40621a49')
    
        voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')
    
    # 由于对图片中的而每一个像素都要进行标号的话,所以label不能像以前一样,
    # 他的做法是将所有的标号转换成一种颜色,存成一张图片
    # 这个数据集中图片和label是不同文件夹下的同名文件下,文件名存储在.txt文件中
    # 使用png格式是为了label不被压缩。
    def read_voc_images(voc_dir, is_train=True):
        """读取所有的VOC图像并标注"""
        txt_fname = os.path.join(voc_dir,'ImageSets','Segmentation',"train.txt" if is_train else 'val.txt')
        mode = torchvision.io.image.ImageReadMode.RGB # 模式,用于读取文件
        with open(txt_fname,'r') as f:
            images = f.read().split() # f.read().split()把文件内容当成一个列表返回
        features, labels = [],[]
        for i ,name in enumerate(images):
            features.append(torchvision.io.read_image(os.path.join(voc_dir,'JPEGImages',f'{name}.jpg')))
            labels.append(torchvision.io.read_image(os.path.join(voc_dir,'SegmentationClass',f'{name}.png'),mode))
        return features, labels
    
    train_features, train_labels = read_voc_images(voc_dir,is_train=True)
    
    n = 5
    imgs = train_features[0:n] + train_labels[0:n]
    imgs = [img.permute(1,2,0) for img in imgs] # 调整通道数
    d2l.show_images(imgs,2,n)
    
    # 列举RGB颜色值和类名
    VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],
                    [0, 0, 128], [128, 0, 128], [0, 128, 128], [128, 128, 128],
                    [64, 0, 0], [192, 0, 0], [64, 128, 0], [192, 128, 0],
                    [64, 0, 128], [192, 0, 128], [64, 128, 128], [192, 128, 128],
                    [0, 64, 0], [128, 64, 0], [0, 192, 0], [128, 192, 0],
                    [0, 64, 128]]
    
    VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat',
                   'bottle', 'bus', 'car', 'cat', 'chair', 'cow',
                   'diningtable', 'dog', 'horse', 'motorbike', 'person',
                   'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor']
    
    # 查找标签中的每个像素的类索引,如果说每次都遍历数组的话是一件非常耗时的事情
    # 所以这里构建了一个类似dictionary的数组
    # 下标:256颜色,值:对应的label
    def voc_colormap2label():
        """构建从RGB到VOC类别索引的映射"""
        # 一维向量
        colormap2label = torch.zeros(256**3,dtype=torch.long)
        for i, colormap in enumerate(VOC_COLORMAP):
            # 这就是256进制转十进制,可以这么理解
            colormap2label[(colormap[0]*256+colormap[1])*256 + colormap[2]] = i
        return colormap2label
    
    # 这里是给你一个分类后的像素点,获取label
    def voc_label_indices(colormap, colormap2label):
        """将VOC标签中的RGB值映射到他们的类别索引"""
        # 因为一般卷积的输入都是,通道x高x宽,图象是高x宽x通道,这里把卷积输入的格式变成图象格式
        colormap = colormap.permute(1,2,0).numpy().astype('int32')
        # print(colormap.shape) # (281, 500, 3),方便计算下标
        idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256 + colormap[:, :, 2])
        # print(idx.shape) # (281, 500)
        return colormap2label[idx]
    
    
    y = voc_label_indices(train_labels[0], voc_colormap2label())
    print(len(y))
    y[105:115, 130:140], VOC_CLASSES[1]
    
    y = voc_label_indices(train_labels[0], voc_colormap2label())
    print(train_labels[0].shape) # torch.Size([3, 281, 500])
    print(len(y)) # 281
    y[105:115, 130:140], VOC_CLASSES[1]
    
    # 使用图片增广,裁剪,翻转,变换颜色等
    # 这里需要注意的是,这里是语义分割,如果我们对图片进行了裁剪,那么我们也需要对他的label和边缘框进行裁剪
    # 参数列表(需要裁剪的img,需要裁剪的label,目标高,目标宽)
    def voc_rand_crop(feature, label,height,width):
        """随机裁剪特征和标签图像"""
        # 获取随即裁剪的参数
        rect = torchvision.transforms.RandomCrop.get_params(feature,(height,width))
        # 按照刚设定的值,裁剪特征
        feature = torchvision.transforms.functional.crop(feature, *rect)
        # 按照刚设定的值,裁剪标签
        label = torchvision.transforms.functional.crop(label, *rect)
        return feature, label
    
    imgs = []
    # n=5
    # 随机裁剪5个观察结果
    for _ in range(n):
        imgs += voc_rand_crop(train_features[0], train_labels[0], 200, 300)
    
    imgs = [img.permute(1, 2, 0) for img in imgs]
    # 因为他输出的是feature,label,所需需要隔一个跳一个
    d2l.show_images(imgs[::2] + imgs[1::2], 2, n)
    d2l.show_images(imgs,2,n)
    
    # 构建数据集
    class VOCSegDataset(torch.utils.data.Dataset):
        """一个用于加载VOC数据集的自定义数据集"""
    
        def __init__(self, is_train, crop_size, voc_dir):
            self.transform = torchvision.transforms.Normalize(
                mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
            self.crop_size = crop_size
            features, labels = read_voc_images(voc_dir, is_train=is_train)
            self.features = [self.normalize_image(feature) for feature in self.filter(features)]
            self.labels = self.filter(labels)
            self.colormap2label = voc_colormap2label()
            print('read ' + str(len(self.features)) + ' examples')
    
        # 因为模型使用resnet,所以沿用了这个
        def normalize_image(self, img):
            return self.transform(img.float() / 255)
    
        # 过滤器,过滤那些宽高比size小的图片。因为我们不能使用resize,因为label不能resiaze
        def filter(self, imgs):
            return [img for img in imgs if (
                img.shape[1] >= self.crop_size[0] and
                img.shape[2] >= self.crop_size[1])]
    
        # 重写__getitem__方法,每次调用获取feature和label
        def __getitem__(self, idx):
            feature, label = voc_rand_crop(self.features[idx], self.labels[idx],
                                           *self.crop_size)
            return (feature, voc_label_indices(label, self.colormap2label))
    
        def __len__(self):
            return len(self.features)
    
    crop_size = (320, 480)
    voc_train = VOCSegDataset(True, crop_size, voc_dir)
    voc_test = VOCSegDataset(False, crop_size, voc_dir)
    
    batch_size = 64
    train_iter = torch.utils.data.DataLoader(voc_train, batch_size, shuffle=True,
                                        drop_last=True,
                                        num_workers=0)
    for X, Y in train_iter:
        print(X.shape)
        print(Y.shape)
        break
    
    # 整合所有组件
    def load_data_voc(batch_size, crop_size):
        """加载VOC语义分割数据集"""
        voc_dir = d2l.download_extract('voc2012', os.path.join(
            'VOCdevkit', 'VOC2012'))
        num_workers = d2l.get_dataloader_workers()
        train_iter = torch.utils.data.DataLoader(
            VOCSegDataset(True, crop_size, voc_dir), batch_size,
            shuffle=True, drop_last=True, num_workers=num_workers)
        test_iter = torch.utils.data.DataLoader(
            VOCSegDataset(False, crop_size, voc_dir), batch_size,
            drop_last=True, num_workers=num_workers)
        return train_iter, test_iter
    

    训练语义分割的网络:
    https://www.jianshu.com/p/c12882cd99b1

    相关文章

      网友评论

          本文标题:语义分割

          本文链接:https://www.haomeiwen.com/subject/oylfortx.html