剪下
在整个输入上用0替换进行抠图并同时裁剪目标
抠图非常直观。它涉及随机删除输入图像的区域。它的工作方式与我们之前提到的裁剪相同。但是,我们不删除相关区域。因此,我们可以再次允许用户提供每个比例的要删除区域的最小和最大大小,最大区域数,以同时或不同时从目标剪切区域,我们可以剪切每个通道,还选择已删除区域的默认替换值。
输入剪切通道替换为1,不裁剪目标
class Cutout(object): def __init__(self, min_size_ratio, max_size_ratio, channel_wise=False, crop_target=True, max_crop=10, replacement=0): self.min_size_ratio = np.array(list(min_size_ratio)) self.max_size_ratio = np.array(list(max_size_ratio)) self.channel_wise = channel_wise self.crop_target = crop_target self.max_crop = max_crop self.replacement = replacement def __call__(self, X, Y): size = np.array(X.shape[:2]) mini = self.min_size_ratio * size maxi = self.max_size_ratio * size for _ in range(self.max_crop): # random size h = np.random.randint(mini[0], maxi[0]) w = np.random.randint(mini[1], maxi[1]) # random place shift_h = np.random.randint(0, size[0] - h) shift_w = np.random.randint(0, size[1] - w) if self.channel_wise: c = np.random.randint(0, X.shape[-1]) X[shift_h:shift_h+h, shift_w:shift_w+w, c] = self.replacement if self.crop_target: Y[shift_h:shift_h+h, shift_w:shift_w+w] = self.replacement else: X[shift_h:shift_h+h, shift_w:shift_w+w] = self.replacement if self.crop_target: Y[shift_h:shift_h+h, shift_w:shift_w+w] = self.replacement return X, Y
色彩空间
现在,我们进入最有趣的部分。也是很少考虑的部分。如果我们知道色彩空间,则可以利用它们的属性来增广图像。举一个简单的例子,借助HSV颜色空间,我们可以很容易地提取树叶的颜色,并根据我们的意愿随机更改其颜色。那是一件很酷的事情!并且我们可以了解自己的图像增广功能的原理。当然,这需要更多的创造力。因此,重要的是要了解我们的色彩空间,以充分利用它们。特别是因为它们对于我们(深度)机器学习模型的预处理至关重要。
class Leaf(object): def __init__(self): pass def __call__(self, X, Y): blur = cv2.GaussianBlur(X, (7, 7), 0) hsv_blur = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV) # lower mask (0-10) lower_red = np.array([0,130,130]) upper_red = np.array([20,255,255]) mask_0 = cv2.inRange(hsv_blur, lower_red, upper_red) # upper mask (170-180) lower_red = np.array([165,130,130]) upper_red = np.array([185,255,255]) mask_1 = cv2.inRange(hsv_blur, lower_red, upper_red) hsv_blur[np.where(mask_1)] = hsv_blur[np.where(mask_1)] - np.array([165, 0, 0]) mask = mask_0 + mask_1 # change color turn_color = np.random.randint(0, 255) hsv_blur[np.where(mask)] = hsv_blur[np.where(mask)] + np.array([turn_color, 0, 0]) X_blur = cv2.cvtColor(hsv_blur, cv2.COLOR_HSV2BGR) X[np.where(mask)] = X_blur[np.where(mask)] return X, Y
亮度
亮度从-100到100
图像增广的一个伟大经典就是能够改变亮度。有几种方法可以做到这一点,最简单的就是简单地添加一个随机偏差。
class Brightness(object): def __init__(self, range_brightness=(-50, 50)): self.range_brightness = range_brightness def __call__(self, X, Y): brightness = np.random.randint(*self.range_brightness) X = X + brightness return X, Y
对比度
对比度从-100到100
同样,使用对比非常简单。这也可以通过随机数完成。
class Contrast(object): def __init__(self, range_contrast=(-50, 50)): self.range_contrast = range_contrast def __call__(self, X, Y): contrast = np.random.randint(*self.range_contrast) X = X * (contrast / 127 + 1) - contrast return X, Y
噪声注入
最后一种相当常见的图像增广技术是噪声注入。实际上,我们只添加与输入大小相同的矩阵。该矩阵由遵循随机分布的元素组成。可以从任何随机分布中进行噪声注入。实际上,我们只看到其中两个。但是随时可以进一步😃
一般方法
class UniformNoise(object): def __init__(self, low=-50, high=50): self.low = low self.high = high def __call__(self, X, Y): noise = np.random.uniform(self.low, self.high, X.shape) X = X + noise return X, Y
高斯方法
class GaussianNoise(object): def __init__(self, center=0, std=50): self.center = center self.std = std def __call__(self, X, Y): noise = np.random.normal(self.center, self.std, X.shape) X = X + noise return X, Y
渐晕
最后,更少使用但并非没有用的一种方法。有些相机会产生渐晕效果。考虑如何通过随机模仿这种现象来增广图像也很有趣。我们还将尝试为用户提供灵活性。我们将能够确定距离效果和可以随机开始的最小距离,确定其强度,甚至可以确定效果是朝着黑色还是向白色方向发展。
class Vignetting(object): def __init__(self, ratio_min_dist=0.2, range_vignette=(0.2, 0.8), random_sign=False): self.ratio_min_dist = ratio_min_dist self.range_vignette = np.array(range_vignette) self.random_sign = random_sign def __call__(self, X, Y): h, w = X.shape[:2] min_dist = np.array([h, w]) / 2 * np.random.random() * self.ratio_min_dist # create matrix of distance from the center on the two axis x, y = np.meshgrid(np.linspace(-w/2, w/2, w), np.linspace(-h/2, h/2, h)) x, y = np.abs(x), np.abs(y) # create the vignette mask on the two axis x = (x - min_dist[0]) / (np.max(x) - min_dist[0]) x = np.clip(x, 0, 1) y = (y - min_dist[1]) / (np.max(y) - min_dist[1]) y = np.clip(y, 0, 1) # then get a random intensity of the vignette vignette = (x + y) / 2 * np.random.uniform(*self.range_vignette) vignette = np.tile(vignette[..., None], [1, 1, 3]) sign = 2 * (np.random.random() < 0.5) * (self.random_sign) - 1 X = X * (1 + sign * vignette) return X, Y
镜头变形
最后,这是一种非常好的方法。我很惊讶它不经常被使用。但是它可以模仿相机镜头的失真。就像透过圆形玻璃看。在我们看来,由于透镜(玻璃)是圆形的,因此失真了。因此,如果我们的图像是从带镜头的相机拍摄的,为什么不模拟它们。默认情况下,应将其用于图像。至少我是这样认为的。
因此,我建议在最后一个函数中,通过播放径向系数k1,k2,k3和切向系数p1,p2,可以随机模拟我们的镜头失真。在该方法中,系数的顺序如下:k1,k2,p1,p2,k3。
class LensDistortion(object): def __init__(self, d_coef=(0.15, 0.15, 0.1, 0.1, 0.05)): self.d_coef = np.array(d_coef) def __call__(self, X, Y): # get the height and the width of the image h, w = X.shape[:2] # compute its diagonal f = (h ** 2 + w ** 2) ** 0.5 # set the image projective to carrtesian dimension K = np.array([[f, 0, w / 2], [0, f, h / 2], [0, 0, 1]]) d_coef = self.d_coef * np.random.random(5) # value d_coef = d_coef * (2 * (np.random.random(5) < 0.5) - 1) # sign # Generate new camera matrix from parameters M, _ = cv2.getOptimalNewCameraMatrix(K, d_coef, (w, h), 0) # Generate look-up tables for remapping the camera image remap = cv2.initUndistortRectifyMap(K, d_coef, None, M, (w, h), 5) # Remap the original image to a new image X = cv2.remap(X, *remap, cv2.INTER_LINEAR) Y = cv2.remap(Y, *remap, cv2.INTER_LINEAR) return X, Y
希望对您有用!请随时关注我或就您喜欢或不喜欢的内容给我反馈。✏️待会儿见!😘