DataWhale 零基础入门语义分割-地表建筑物识别-Task1
1. Rle编码理解
RLE是一种简单的非破坏性资料压缩法,经常用在在语义分割比赛中对标签进行编码
(1) 示例代码
import numpy as np
import pandas as pd
import cv2
# 将图片编码为rle格式
def rle_encode(im):
'''
im: numpy array, 1 - mask, 0 - background
Returns run length as string formated
'''
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)
# 将rle格式进行解码为图片
def rle_decode(mask_rle, shape=(512, 512)):
'''
mask_rle: run-length as string formated (start length)
shape: (height,width) of array to return
Returns numpy array, 1 - mask, 0 - background
'''
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')
(2)代码分析
通过encode过程对rle编码的思路进行分析和理解:
1)以 111011101111101111 为例进行分析
pixels = [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1]
2)np.flatten()主要的功能是将多维矩阵转换成一维向量
3)将一维像素矩阵首尾补0
pixels = np.concatenate([[0], pixels, [0]])
首尾补0的结果是:
[0 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 0]
这样的做法主要的目的和后面的代码有关,前后补0是为了保证像素的完整性不被丢失
4)pixels[1:] 取得的是从下标“1”开始的所有值;pixels[:-1]取得的是从下标“0”开始到倒数第二个的所有值,也就是:
# np.where()在这里的主要的功能是返回满足括号中condition的位置序号
runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
'''
pixels[1:] = [1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 0]
pixels[:-1]= [0 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1]
runs = [ 1 4 5 8 9 14 15 19]
runs代表的就是值不相同的下标
'''
5)rle编码的目的就是压缩,本例子的实际目的就是统计原始pixels中连续“1”的起始位置和连续的个数,对runs每两个进行组合得到的就是连续“1”的起始和终止的下标,用终止的下标减去起始的下标就是连续的个数
# runs[1::2]表示的是从下标1开始,间隔为2进行取值
runs[1::2] -= runs[::2]
'''
相减得到的结果是:
[1 3 5 3 9 5 15 4]
意思是:从位置1开始有连续3个相同的值,从位置5开始有连续3个相同的值
'''
2 赛题数据读入和可视化
(1)赛题数据读入:
# 天池读取数据:
class TianChiDataset(D.Dataset):
def __init__(self, paths, rles, transform, test_mode=False):
self.paths = paths
self.rles = rles
self.transform = transform
self.test_mode = test_mode
self.len = len(paths)
self.as_tensor = T.Compose([
T.ToPILImage(),
T.Resize(IMAGE_SIZE),
T.ToTensor(),
T.Normalize([0.625, 0.448, 0.688],
[0.131, 0.177, 0.101]),
])
# get data operation
def __getitem__(self, index):
img = cv2.imread(self.paths[index])
if not self.test_mode:
mask = rle_decode(self.rles[index])
augments = self.transform(image=img, mask=mask)
return self.as_tensor(augments['image']), augments['mask'][None]
else:
return self.as_tensor(img), ''
def __len__(self):
"""
Total number of samples in the dataset
"""
return self.len
(2)数据可视化展示:
通过对TianChiDataset进行实例化,再输入对应的索引即可获取数据:
dataset = TianChiDataset(
train_mask['name'].values,
train_mask['mask'].fillna('').values,
trfm, False
)
# 可视化训练的数据:
image, mask = dataset[0]
plt.figure(figsize=(16,8))
plt.subplot(121)
plt.imshow(mask[0], cmap='gray')
plt.subplot(122)
plt.imshow(image[0])
3 任务拓展
(1)统计所有图片整图中没有任何建筑物的图片占所有训练集图片的比例
# 统计所有图片中没有任何建筑物的图片的比例:
train_mask = pd.read_csv('./data/train_mask.csv', sep='\t', names=['name', 'mask'])
num = 0
for index, row in train_mask.iterrows():
if pd.isnull(row['mask']):
num += 1
print(num / len(train_mask))
结果是:0.17346666666666666
(2)统计所有图片中建筑物像素占所有像素的比例和统计所有图片中建筑物区域平均区域大小
# 统计所有图片中建筑物像素占所有像素的比例:
# 统计所有图片中建筑物的平均区域的大小
ratio_list = []
sum_construction_list = []
length = len(train_mask)
for i in tqdm(range(length)):
row = train_mask.iloc[i]
# 只对图片中有建筑物的图片进行统计:
if not pd.isnull(row['mask']):
# 得到图片中建筑物的像素数组:
pixels_construction = list(map(int, row['mask'].split(' ')))[1::2]
# 图片中建筑物的区域的大小:
summation = sum(pixels_construction)
sum_construction_list.append(summation)
# 得到图片总的像素大小
image_total = cv2.imread(row['name'])
pixels_total = image_total.shape[0] * image_total.shape[1]
# 计算比例:
ratio_list.append( summation / pixels_total)
所有图片中建筑物像素占所有像素的比例统计结果为(只统计了图片中有建筑物的图片):
最大比例:0.9992218017578125;
最小比例:3.814697265625e-06;
平均比例:0.19004847807621914
所有图片中建筑物区域平均区域大小为(只统计了图片中有建筑物的图片):
49820.06823681239