天池竞赛-地表建筑物识别 语义分割

目录

1 案例介绍

2 数据预处理

2.1 rle编码转换

2.2 数据扩增

2.3 异常数据的处理

3 自定义数据库类

4 模型训练

5 语义分割的准确率评价方法

5.1 像素准确率(PA)

5.2 类别像素准确率(CPA)

5.3 类别平均像素准确率(MPA)

5.4 交并比(IoU)

5.5  平均交并比(MIoU)

6 模型验证

7 模型测试

8 结果提交

参考资料

附件


1 案例介绍

遥感技术已成为获取地表覆盖信息最为行之有效的手段,遥感技术已经成功应用于地表覆盖检测、植被面积检测和建筑物检测任务。本赛题使用航拍数据,需要参赛选手完成地表建筑物识别,将地表航拍图像素划分为有建筑物和无建筑物两类。

如下图,左边为原始航拍图,右边为对应的建筑物标注。

本案例训练集为航拍的地标建筑物,训练集图像为30000张图片。其中训练集的标签为rle序列的csv文档。测试集为2500个图像。

2 数据预处理

2.1 rle编码转换

RLE编码是微软开发为AVI格式开发的一种编码。假设一个图像的像素色彩值是这样排列的:红红红红红红红红红红红红蓝蓝蓝蓝蓝蓝绿绿绿绿,经过RLE压缩后就成为了:红12蓝6绿4。这样既保证了压缩的可行性,而且不会有损失。而且可以看到,当颜色数越少时,压缩效率会更高。

在本案例中,我们首先要对rle编码进行读取,将其转换为jpg格式的图片。

官方给出的解码文件可以将rle编码序列转化为一个numpy矩阵。转码函数如下:

我们首先对csv文件进行读取,保存到一个二维数组中。

train_mask = pd.read_csv('../dataset/train_mask.csv/train_mask.csv', sep='\t', names=['name', 'mask'])
# 读取第一张图,并将对于的rle解码为mask矩阵
img = cv2.imread('../dataset/train/' + train_mask['name'].iloc[0])  # name列的第0行
mask = rle_decode(train_mask['mask'].iloc[0])
print(train_mask.head())

train_mask['name'].lioc[0]:lioc用于提取行数据,整体含义为name列第0行数据

names字段作用:命名csv文件列名

train_mask.head()输出检验列名,我们可以看到csv文件如下:

 

我们通过观察发现,转码后的变量是一个矩阵,我们将矩阵转化为一个二值图,再将其做为标签存放。需要注意的是,矩阵中的值都是0或1,而二值图的8位编码范围为0-255,这样我们在观察标签的时候会看到几乎全黑的情况。所以我们在得到输出后的矩阵,一定要将其乘上255。

要注意的是二值图和灰度图的区别。二值图是一种单通道图像,其矩阵形式只可表现为两个数值;灰度图是一种RGB三通道图像,每个通道的数值相等,它相比于二值图更多的保留了原始图像的信息。

for i in range(30000):
    try:
        train_mask = rle_decode(train_rle['mask'].iloc[i])
        print(type(train_mask))  # 矩阵形式
        train_mask = train_mask * 255
        train_mask = train_mask.astype(np.uint8)
        cv2.imwrite('D:\\00Com_TianChi\\dataset\\train\\build_label\\' + train_rle['name'].iloc[i], train_mask)
    except:
        pass
        train_mask = np.zeros((512, 512)).astype('uint8')
        train_mask = train_mask * 255
        cv2.imwrite('D:\\00Com_TianChi\\dataset\\train\\build_label\\' + train_rle['name'].iloc[i], train_mask)

其中将矩阵转为numpy格式并存储成图片的转换函数为astype()。

使用方法为 train_mask = train_mask.astype(np.uint8)

在训练集中有很多异常数据,对于异常数据,我们使用try-except语法来进行处理。

try:正常情况       

except:数据异常情况

2.2 数据扩增

数据扩增是一种有效的正则化方法,可以防止模型过拟合,在深度学习模型的训练过程中应用广泛。数据扩增的目的是增加数据集中样本的数据量,同时也可以有效增加样本的语义空间。

在语义分割领域,我们通常将训练集的图像与标签进行同步的图像变换,这样可以对模型进行有效的训练。

本案例利用albumentations库进行数据扩增。albumentations是基于OpenCV的快速训练数据增强库,拥有非常简单且强大的可以用于多种任务(分割、检测)的接口,易于定制且添加其他框架非常方便。

# ---------------数据扩增部分---------------
    aug_data = 'D:\\00Com_TianChi\\dataset\\train_aug\\'
    image_build_aug = "build_image_aug"
    label_build_aug = "build_label_aug"
    # 扩增img和扩增label的路径
    image_build_aug_path = os.path.join(aug_data, image_build_aug)
    label_build_aug_path = os.path.join(aug_data, label_build_aug)

        # 原始图像的名称 build_dataset.image_list[0] build_dataset.label_list[0]

    # 路径测试
    # print(os.path.join(root_dir, image_build,  build_dataset.image_list[0]))

    # print( os.path.join(image_build_aug_path,  'scale' + build_dataset.image_list[0]))

    for i in range(0, 5):
        print(i)
        # 将 原始图像和原始标签路径 放入函数 得到路径
        img_path = os.path.join(root_dir, image_build,  build_dataset.image_list[i])
        label_path = os.path.join(root_dir, label_build, build_dataset.label_list[i])
        # 根据路径加载图片 转为np类
        trans_img = np.asarray(Image.open(img_path))
        trans_label = np.asarray(Image.open(label_path))


        # 水平翻转操作
        augments = aug.HorizontalFlip(p=1)(image=trans_img, mask=trans_label)
        img_aug_hor, mask_aug_hor = augments['image'], augments['mask']
        # 随即裁剪操作
        augments = aug.RandomCrop(p=1, height=256, width=256)(image=trans_img, mask=trans_label)
        img_aug_ran, mask_aug_ran = augments['image'], augments['mask']
        # 旋转操作
        augments = aug.ShiftScaleRotate(p=1)(image=trans_img, mask=trans_label)
        img_aug_rot, mask_aug_rot = augments['image'], augments['mask']
        # 复合操作
        trfm = aug.Compose([
            aug.Resize(256, 256),
            aug.HorizontalFlip(p=0.5),
            aug.VerticalFlip(p=0.5),
            aug.RandomRotate90(),
        ])
        augments = trfm(image=trans_img, mask=trans_label)
        img_aug_mix, mask_aug_mix = augments['image'], augments['mask']


        # 保存路径 变换后的文件名
        # 水平翻转
        save_hor_path_img = os.path.join(image_build_aug_path,  'hor' + build_dataset.image_list[i])
        save_hor_path_label = os.path.join(label_build_aug_path, 'hor' + build_dataset.label_list[i])
        cv2.imwrite(save_hor_path_img, img_aug_hor)
        cv2.imwrite(save_hor_path_label, mask_aug_hor)
        # 随即裁剪
        save_ran_path_img = os.path.join(image_build_aug_path, 'ran' + build_dataset.image_list[i])
        save_ran_path_label = os.path.join(label_build_aug_path, 'ran' + build_dataset.label_list[i])
        cv2.imwrite(save_ran_path_img, img_aug_ran)
        cv2.imwrite(save_ran_path_label, mask_aug_ran)
        # 旋转操作
        save_rot_path_img = os.path.join(image_build_aug_path, 'rot' + build_dataset.image_list[i])
        save_rot_path_label = os.path.join(label_build_aug_path, 'rot' + build_dataset.label_list[i])
        cv2.imwrite(save_rot_path_img, img_aug_rot)
        cv2.imwrite(save_rot_path_label, mask_aug_rot)
        # 复合操作
        save_mix_path_img = os.path.join(image_build_aug_path, 'rot' + build_dataset.image_list[i])
        save_mix_path_label = os.path.join(label_build_aug_path, 'rot' + build_dataset.label_list[i])
        cv2.imwrite(save_mix_path_img, img_aug_mix)
        cv2.imwrite(save_mix_path_label, mask_aug_mix)

2.3 异常数据的处理

在rle转mask编码的处理中,我们将异常rle数据转换成全黑图片处理。可是在后面的训练中发现,损失函数的振荡较大,于是考虑将异常数据全部剔除,再次训练函数观察损失函数的变化。

后来经过观察又发现,文件中数据异常的rle序列对应的图像即是没有建筑物的图像,输出全黑的标签是没有问题的。

3 自定义数据库类

在数据预处理后,我们进行数据库类的定义。在每次进行模型训练前,我们要将训练集的数据输入给一个类中,这样能够使我们清晰地有条理地利用好我们的训练集数据。本案例的数据库类定义如下。


                
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我本逍遥Kurt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值