File size: 8,225 Bytes
edcf5ee |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
"""
dual augmentation on both images and their masks Script ver: Apr 10th 11:20
"""
import random
import numpy as np
import cv2
from PIL import Image
from torchvision import transforms
from utils.tools import to_2tuple
class DualCompose: # fit pytorch transform
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, image, mask=None):
# process the cv2 transformation first
for t in self.transforms:
image, mask = t(image, mask)
# NOTICE 转回图片 值总和还变成了cv2 numpy的1/255
# Then, Transform cv2 BGR image to PIL RGB image
# BGR -> RGB channel
b, g, r = cv2.split(image)
image = cv2.merge([r, g, b])
b, g, r = cv2.split(mask)
mask = cv2.merge([r, g, b])
# Image.fromarray make the 0-255 to PIL 0-1 range values
return Image.fromarray(np.uint8(image)), Image.fromarray(np.uint8(mask))
class DualImageTransform:
# Transform cv2 BGR image to PIL RGB image
def __init__(self):
pass
def __call__(self, image, mask=None):
# BGR -> RGB channel
b, g, r = cv2.split(image)
image = cv2.merge([r, g, b])
b, g, r = cv2.split(mask)
mask = cv2.merge([r, g, b])
# Image.fromarray make the 0-255 to PIL 0-1 range values
return Image.fromarray(np.uint8(image)), Image.fromarray(np.uint8(mask))
class Dual_RandomHorizontalFlip:
"""
Random horizontal flip.
image shape: (height, width, channels)
mask shape: (height, width)
possibility: possibility for flip
"""
def __init__(self, possibility=0.5):
assert isinstance(possibility, (int, float))
self.possibility = possibility
def __call__(self, image, mask):
if random.random() <= self.possibility:
image = np.flip(image, axis=1)
mask = np.flip(mask, axis=1)
return image, mask
class Dual_RandomVerticalFlip:
"""
Random vertical flip.
image shape: (height, width, channels)
mask shape: (height, width)
possibility: possibility for flip
"""
def __init__(self, possibility=0.5):
assert isinstance(possibility, (int, float))
self.possibility = possibility
def __call__(self, image, mask):
if random.random() <= self.possibility:
image = np.flip(image, axis=0)
mask = np.flip(mask, axis=0)
return image, mask
class Dual_Rotate:
"""
Random rotation.
image shape: (height, width, channels)
mask shape: (height, width)
possibility: possibility for rotate
range: range of rotation angles
"""
def __init__(self, possibility=0.5, range=20):
self.possibility = possibility
self.range = range
def __call__(self, image, mask):
# 这里cv2读到的是反的,因此这里是height, width而不是width,height,图片input不是正方形时会有严重后果
height, width = image.shape[:2]
if random.random() <= self.possibility:
angle = np.random.randint(0, self.range)
center = (width // 2, height // 2)
# 得到旋转矩阵,第一个参数为旋转中心,第二个参数为旋转角度,第三个参数为旋转之前原图像缩放比例
M = cv2.getRotationMatrix2D(center, -angle, 1)
# 进行仿射变换,第一个参数图像,第二个参数是旋转矩阵,第三个参数是变换之后的图像大小
image = cv2.warpAffine(image, M, (width, height))
mask = cv2.warpAffine(mask.astype(np.uint8), M, (width, height))
return image.astype(np.uint8), mask.astype(np.int)
def Four_step_dual_augmentation(data_augmentation_mode=0, edge_size=384):
"""
Get data augmentation methods
Dual_transform : Transform CV2 images and their mask by Rotate, RandomHorizontalFlip, etc.
DualImage : Transform CV2 images and their mask to PIL images
train_domain_transform : transforms.ColorJitter on PIL images
transform: PIL crop, resize and to Tensor
USAGE:
IN Train:
image, mask = self.Dual_transform(image, mask)
# image color jitter shifting
image = self.train_domain_transform(image)
# crop + resize
image = self.transform(image)
IN Val $ Test:
# 0/255 mask -> binary mask
image, mask = self.DualImage(image, mask)
# crop + resize
image = self.transform(image)
"""
edge_size = to_2tuple(edge_size)
if data_augmentation_mode == 0: # ROSE + MARS
# apply the on-time synchornized transform on image and mask togather
Dual_transform = DualCompose([
Dual_Rotate(possibility=0.8, range=180),
Dual_RandomHorizontalFlip(),
Dual_RandomVerticalFlip(),
])
# val & test use DualImage to convert PIL Image
DualImage = DualImageTransform()
# ColorJitter for image only
train_domain_transform = transforms.Compose([
# HSL shift operation
transforms.ColorJitter(brightness=0.15, contrast=0.3, saturation=0.3, hue=0.06),
])
# lastly, the synchornized separate transform
transform = transforms.Compose([
transforms.CenterCrop(700), # center area for classification
transforms.Resize(edge_size),
transforms.ToTensor(), # hwc -> chw tensor
])
elif data_augmentation_mode == 1: # Cervical
# apply the on-time synchornized transform on image and mask togather
Dual_transform = DualCompose([
Dual_Rotate(possibility=0.8, range=180),
Dual_RandomHorizontalFlip(),
Dual_RandomVerticalFlip(),
])
# val & test use DualImage to convert PIL Image
DualImage = DualImageTransform()
# ColorJitter for image only
train_domain_transform = transforms.Compose([
# HSL shift operation
transforms.ColorJitter(brightness=0.15, contrast=0.3, saturation=0.3, hue=0.06),
])
# lastly, the synchornized separate transform
transform = transforms.Compose([
transforms.Resize(edge_size),
transforms.ToTensor(), # hwc -> chw tensor
])
elif data_augmentation_mode == 2: #
# apply the on-time synchornized transform on image and mask togather
Dual_transform = DualCompose([
Dual_Rotate(possibility=0.8, range=180),
Dual_RandomHorizontalFlip(),
Dual_RandomVerticalFlip(),
])
# val & test use DualImage to convert PIL Image
DualImage = DualImageTransform()
# ColorJitter for image only
train_domain_transform = transforms.Compose([
# HSL shift operation
transforms.ColorJitter(brightness=0.15, contrast=0.3, saturation=0.3, hue=0.06),
])
# lastly, the synchornized separate transform
transform = transforms.Compose([
transforms.CenterCrop(360), # center area for classification
transforms.Resize(edge_size),
transforms.ToTensor(), # hwc -> chw tensor
])
elif data_augmentation_mode == 3: # for the squre input: just resize
# apply the on-time synchornized transform on image and mask togather
Dual_transform = DualCompose([
# Dual_Rotate(possibility=0.8, range=180),
Dual_RandomHorizontalFlip(),
Dual_RandomVerticalFlip(),
])
# val & test use DualImage to convert PIL Image
DualImage = DualImageTransform()
# ColorJitter for image only
train_domain_transform = transforms.Compose([
# HSL shift operation
transforms.ColorJitter(brightness=0.15, contrast=0.3, saturation=0.3, hue=0.06),
])
# lastly, the synchornized separate transform
transform = transforms.Compose([
transforms.Resize(edge_size),
transforms.ToTensor(), # hwc -> chw tensor
])
else:
print('no legal data augmentation is selected')
return -1
return Dual_transform, DualImage, train_domain_transform, transform
|