零基礎(chǔ)入門語義分割-Task2 數(shù)據(jù)擴(kuò)增
本章對語義分割任務(wù)中常見的數(shù)據(jù)擴(kuò)增方法進(jìn)行介紹,并使用OpenCV和albumentations兩個(gè)庫完成具體的數(shù)據(jù)擴(kuò)增操作。
2 數(shù)據(jù)擴(kuò)增方法
本章主要內(nèi)容為數(shù)據(jù)擴(kuò)增方法、OpenCV數(shù)據(jù)擴(kuò)增、albumentations數(shù)據(jù)擴(kuò)增和Pytorch讀取賽題數(shù)據(jù)四個(gè)部分組成。
2.1 學(xué)習(xí)目標(biāo)
- 理解基礎(chǔ)的數(shù)據(jù)擴(kuò)增方法
- 學(xué)習(xí)OpenCV和albumentations完成數(shù)據(jù)擴(kuò)增
- Pytorch完成賽題讀取
2.2 常見的數(shù)據(jù)擴(kuò)增方法
數(shù)據(jù)擴(kuò)增是一種有效的正則化方法,可以防止模型過擬合,在深度學(xué)習(xí)模型的訓(xùn)練過程中應(yīng)用廣泛。數(shù)據(jù)擴(kuò)增的目的是增加數(shù)據(jù)集中樣本的數(shù)據(jù)量,同時(shí)也可以有效增加樣本的語義空間。
需注意:
不同的數(shù)據(jù),擁有不同的數(shù)據(jù)擴(kuò)增方法;
數(shù)據(jù)擴(kuò)增方法需要考慮合理性,不要隨意使用;
數(shù)據(jù)擴(kuò)增方法需要與具體任何相結(jié)合,同時(shí)要考慮到標(biāo)簽的變化;
對于圖像分類,數(shù)據(jù)擴(kuò)增方法可以分為兩類:
- 標(biāo)簽不變的數(shù)據(jù)擴(kuò)增方法:數(shù)據(jù)變換之后圖像類別不變;
- 標(biāo)簽變化的數(shù)據(jù)擴(kuò)增方法:數(shù)據(jù)變換之后圖像類別變化;
而對于語義分割而言,常規(guī)的數(shù)據(jù)擴(kuò)增方法都會改變圖像的標(biāo)簽。如水平翻轉(zhuǎn)、垂直翻轉(zhuǎn)、旋轉(zhuǎn)90%、旋轉(zhuǎn)和隨機(jī)裁剪,這些常見的數(shù)據(jù)擴(kuò)增方法都會改變圖像的標(biāo)簽,即會導(dǎo)致地標(biāo)建筑物的像素發(fā)生改變。

2.3 OpenCV數(shù)據(jù)擴(kuò)增
OpenCV是計(jì)算機(jī)視覺必備的庫,可以很方便的完成數(shù)據(jù)讀取、圖像變化、邊緣檢測和模式識別等任務(wù)。為了加深各位對數(shù)據(jù)可做的影響,這里首先介紹OpenCV完成數(shù)據(jù)擴(kuò)增的操作。
# 首先讀取原始圖片
img = cv2.imread(train_mask['name'].iloc[0])
mask = rle_decode(train_mask['mask'].iloc[0])
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.subplot(1, 2, 2)
plt.imshow(mask)

# 垂直翻轉(zhuǎn)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(cv2.flip(img, 0))
plt.subplot(1, 2, 2)
plt.imshow(cv2.flip(mask, 0))

# 水平翻轉(zhuǎn)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(cv2.flip(img, 0))
plt.subplot(1, 2, 2)
plt.imshow(cv2.flip(mask, 0))

# 隨機(jī)裁剪
x, y = np.random.randint(0, 256), np.random.randint(0, 256)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(img[x:x+256, y:y+256])
plt.subplot(1, 2, 2)
plt.imshow(mask[x:x+256, y:y+256])

2.4 albumentations數(shù)據(jù)擴(kuò)增
albumentations是基于OpenCV的快速訓(xùn)練數(shù)據(jù)增強(qiáng)庫,擁有非常簡單且強(qiáng)大的可以用于多種任務(wù)(分割、檢測)的接口,易于定制且添加其他框架非常方便。
albumentations也是計(jì)算機(jī)視覺數(shù)據(jù)競賽中最常用的庫:
- GitHub: https://github.com/albumentations-team/albumentations
- 示例:https://github.com/albumentations-team/albumentations_examples
與OpenCV相比albumentations具有以下優(yōu)點(diǎn):
- albumentations支持的操作更多,使用更加方便;
- albumentations可以與深度學(xué)習(xí)框架(Keras或Pytorch)配合使用;
- albumentations支持各種任務(wù)(圖像分流)的數(shù)據(jù)擴(kuò)增操作
albumentations它可以對數(shù)據(jù)集進(jìn)行逐像素的轉(zhuǎn)換,如模糊、下采樣、高斯造點(diǎn)、高斯模糊、動態(tài)模糊、RGB轉(zhuǎn)換、隨機(jī)霧化等;也可以進(jìn)行空間轉(zhuǎn)換(同時(shí)也會對目標(biāo)進(jìn)行轉(zhuǎn)換),如裁剪、翻轉(zhuǎn)、隨機(jī)裁剪等。
import albumentations as A
# 水平翻轉(zhuǎn)
augments = A.HorizontalFlip(p=1)(image=img, mask=mask)
img_aug, mask_aug = augments['image'], augments['mask']
# 隨機(jī)裁剪
augments = A.RandomCrop(p=1, height=256, width=256)(image=img, mask=mask)
img_aug, mask_aug = augments['image'], augments['mask']
# 旋轉(zhuǎn)
augments = A.ShiftScaleRotate(p=1)(image=img, mask=mask)
img_aug, mask_aug = augments['image'], augments['mask']
albumentations還可以組合多個(gè)數(shù)據(jù)擴(kuò)增操作得到更加復(fù)雜的數(shù)據(jù)擴(kuò)增操作:
trfm = A.Compose([
A.Resize(256, 256),
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.5),
A.RandomRotate90(),
])
augments = trfm(image=img, mask=mask)
img_aug, mask_aug = augments['image'], augments['mask']
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
plt.imshow(augments['image'])
plt.subplot(1, 2, 2)
plt.imshow(augments['mask'])aug

2.5 Pytorch數(shù)據(jù)讀取
由于本次賽題我們使用Pytorch框架講解具體的解決方案,接下來將是解決賽題的第一步使用Pytorch讀取賽題數(shù)據(jù)。在Pytorch中數(shù)據(jù)是通過Dataset進(jìn)行封裝,并通過DataLoder進(jìn)行并行讀取。所以我們只需要重載一下數(shù)據(jù)讀取的邏輯就可以完成數(shù)據(jù)的讀取。
- Dataset:數(shù)據(jù)集,對數(shù)據(jù)進(jìn)行讀取并進(jìn)行數(shù)據(jù)擴(kuò)增;
- DataLoder:數(shù)據(jù)讀取器,對Dataset進(jìn)行封裝并進(jìn)行批量讀??;
定義Dataset:
import torch.utils.data as D
class TianChiDataset(D.Dataset):
def __init__(self, paths, rles, transform):
self.paths = paths
self.rles = rles
self.transform = transform
self.len = len(paths)
def __getitem__(self, index):
img = cv2.imread(self.paths[index])
mask = rle_decode(self.rles[index])
augments = self.transform(image=img, mask=mask)
return self.as_tensor(augments['image']), augments['mask'][None]
def __len__(self):
return self.len
實(shí)例化Dataset:
trfm = A.Compose([
A.Resize(IMAGE_SIZE, IMAGE_SIZE),
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.5),
A.RandomRotate90(),
])
dataset = TianChiDataset(
train_mask['name'].values,
train_mask['mask'].fillna('').values,
trfm
)
實(shí)例化DataLoder,批大小為10:
loader = D.DataLoader(
dataset, batch_size=10, shuffle=True, num_workers=0)
2.6 其他數(shù)據(jù)擴(kuò)增方式
- 使用OpenCV完成圖像加噪數(shù)據(jù)擴(kuò)增
def gasuss_noise(image, path_out_gasuss, mean=0, var=0.001):
'''
添加高斯噪聲
mean : 均值
var : 方差
'''
image = np.array(image / 255, dtype=float)
noise = np.random.normal(mean, var ** 0.5, image.shape)
out = image + noise
if out.min() < 0:
low_clip = -1.
else:
low_clip = 0.
out = np.clip(out, low_clip, 1.0)
out = np.uint8(out * 255)
cv.imwrite(path_out_gasuss, out)
- 使用OpenCV完成圖像旋轉(zhuǎn)數(shù)據(jù)擴(kuò)增;
def rotate(image, path_out_rotate):
'''
旋轉(zhuǎn)
'''
rows, cols = image.shape[:2]
M = cv.getRotationMatrix2D((cols / 2, rows / 2), 10, 1)
dst = cv.warpAffine(image, M, (cols, rows))
cv.imwrite(path_out_rotate, dst)
- 使用albumentations其他的的操作完成擴(kuò)增操作