通过对遥感技术获取的航拍图进行计算机视觉识别可以获取植被面积、建筑物检测等地表覆盖信息,是一项高效且意义重大的任务。本次任务是根据航拍图识别图片中的地表建筑具体像素位置。
知识准备——简述语义分割与其他视觉任务的区别
目前,我了解到的计算机视觉的任务包括图像分类(分辨猫狗)、目标检测(定位)、语义分割(地表建筑物识别)。
他们之间的关系可以在第12章视觉和语音找到解释,简而言之就是:
- 图像分类是训练集包含K个类别的N个训练样本,学习各类特征获得一个分类器,最后用于预测新图像的类标签。重点是一个图对应一个标签
- 目标检测是针对图像中的符合类别特征的多个对象进行定位的问题。像是在汽车检测中,必须使用边界框检测所给定图像中的所有汽车。
- 语义分割是在图像按像素组聚类的基础上,进行语义标记。
- 实例分割是在像素级识别对象轮廓的任务。是在语义分割的基础上给出属于各个实例的像素的行为。
赛题理解
赛题任务
- 赛题目标:通过地标建筑物识别入门语义分割的解题流程和模型
- 数据说明:图片是航拍图jpg,数据标签是图像像素类别(无建筑和有建筑)RLE编码字符串。
- 评价函数:
解题思路
- 步骤1:使用FCN模型跑通具体模型训练过程,并对结果进行预测提交
- 步骤2:在现有基础上加入数据扩增方法,并划分验证集以监督模型精度;
- 步骤3:使用更加强大模型结构(如Unet 和PSPNet)或尺寸更大的输入完成训练;
- 步骤4:训练多个模型完成模型集成操作;
课后习题
1. RLE解码与编码
1.1 理解RLE编码过程
RLE(run-length encoding)算法的基本思路是对于连续重复数据块以【块数】+【数据块】的形式表达,从而达到节省存储空间的目的。RLE算法参考
在语义分割任务中,RLE编码是对图片中连续的黑、白像素以不同的码字(0/1)进行编码,生成压缩的字符串形式。RLE解码则相反,将RLE字符串解析成图像掩膜(mask)。
import numpy as np
def rle_encode(im):
'''将图片编码为rle格式'''
pixels = im.flatten(order='F')
pixels = np.concatenate([[0],pixels,[0]])
runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
runs[1::2] -= runs[::2]
return ' '.join(str(x) for x in runs)
def rle_decode(mask_rle,shape=(512,512)):
'''将rle格式进行解码为图片'''
if mask_rle is np.nan:
return None
s = mask_rle.split()
starts,lengths = [np.asarray(x,dtype=int) for x in (s[0:][::2],s[1:][::2])]
starts -= 1
ends = starts + lengths
img = np.zeros(shape[0]*shape[1],dtype=np.uint8)
for lo,hi in zip(starts,ends):
img[lo:hi] = 1
return img.reshape(shape,order='F')
1.2 赛题数据读取并可视化
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import albumentations as A
def add_mask2image_binary(img_path, mask=None):
# Add binary masks to images 带掩膜效果的图片
if img_path.endswith('.jpg') and mask is not None:
img = cv2.imread(img_path)
masked = cv2.add(img, np.zeros(np.shape(img), dtype=np.uint8), mask=mask) #将image的相素值和mask像素值相加得到结果
# cv2图片格式转为RGB显示
fig,ax = plt.subplots(1,2)
ax[0].imshow(img[:,:,::-1])
ax[1].imshow(masked[:,:,::-1])
train_mask = pd.read_csv('./train_mask.csv',sep='\t',names=['name','mask'])
for i in range(len(train_mask)):
img_path = './train/'+train_mask['name'].iloc[i]
img = cv2.imread(img_path)
mask = rle_decode(train_mask['mask'].iloc[i])
add_mask2image_binary(img_path, mask)
if i > 3:
break
2. 图片建筑物像素统计
- 统计所有图片整图中没有任何建筑物像素占所有训练集图片的比例
- 统计所有图片中建筑物像素占所有相似度的比例
- 统计所有图片中建筑物区域平均区域大小
def rle_mask_stats(x):
if x is np.nan:
return (None,None,None)
mask = rle_decode(x)
return (mask.sum()/(mask.shape[0]*mask.shape[1]), mask.sum(),mask.shape[0]*mask.shape[1])
train_mask[['mask_01','mask_02','mask_03']] = train_mask['mask'].apply(rle_mask_stats).apply(pd.Series)
train_mask['mask_01'].hist(bins=500)
print('统计所有图片整图中没有任何建筑物像素占所有训练集图片的比例>>>')
print('1%%为没有任何建筑物阈值>>>%.3f'%train_mask['mask_01'].le(0.01).mean())
print('5%%为没有任何建筑物阈值>>>%.3f'%train_mask['mask_01'].le(0.05).mean())
print('10%%为没有任何建筑物阈值>>>%.3f'%train_mask['mask_01'].le(0.1).mean())
print('统计所有图片中建筑物像素占比比例>>>',train_mask['mask_01'].describe())
print('统计所有图片中建筑物区域平均区域大小>>>',train_mask['mask_02'].mean(),train_mask['mask_01'].mean())