cls_transforms.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import math
  15. import numpy as np
  16. import cv2
  17. from PIL import Image
  18. from .operators import Transform, Compose, RandomHorizontalFlip, RandomVerticalFlip, Normalize, \
  19. ResizeByShort, CenterCrop, RandomDistort, ArrangeClassifier
  20. class RandomCrop(Transform):
  21. """对图像进行随机剪裁,模型训练时的数据增强操作。
  22. 1. 根据lower_scale、lower_ratio、upper_ratio计算随机剪裁的高、宽。
  23. 2. 根据随机剪裁的高、宽随机选取剪裁的起始点。
  24. 3. 剪裁图像。
  25. 4. 调整剪裁后的图像的大小到crop_size*crop_size。
  26. Args:
  27. crop_size (int): 随机裁剪后重新调整的目标边长。默认为224。
  28. lower_scale (float): 裁剪面积相对原面积比例的最小限制。默认为0.08。
  29. lower_ratio (float): 宽变换比例的最小限制。默认为3. / 4。
  30. upper_ratio (float): 宽变换比例的最大限制。默认为4. / 3。
  31. """
  32. def __init__(self,
  33. crop_size=224,
  34. lower_scale=0.08,
  35. lower_ratio=3. / 4,
  36. upper_ratio=4. / 3):
  37. super(RandomCrop, self).__init__()
  38. self.crop_size = crop_size
  39. self.lower_scale = lower_scale
  40. self.lower_ratio = lower_ratio
  41. self.upper_ratio = upper_ratio
  42. def apply_im(self, image):
  43. image = _random_crop(image, self.crop_size, self.lower_scale,
  44. self.lower_ratio, self.upper_ratio)
  45. return image
  46. def apply(self, sample):
  47. sample['image'] = self.apply_im(sample['image'])
  48. return sample
  49. class RandomRotate(Transform):
  50. def __init__(self, rotate_range=30, prob=.5):
  51. """
  52. Randomly rotate image(s) by an arbitrary angle between -rotate_range and rotate_range.
  53. Args:
  54. rotate_range(int, optional): Range of the rotation angle. Defaults to 30.
  55. prob(float, optional): Probability of operating rotation. Defaults to .5.
  56. """
  57. self.rotate_range = rotate_range
  58. self.prob = prob
  59. def apply_im(self, image, angle):
  60. image = image.astype('uint8')
  61. image = Image.fromarray(image)
  62. image = image.rotate(angle)
  63. image = np.asarray(image).astype('float32')
  64. return image
  65. def apply(self, sample):
  66. rotate_lower = -self.rotate_range
  67. rotate_upper = self.rotate_range
  68. if np.random.uniform(0, 1) < self.prob:
  69. angle = np.random.uniform(rotate_lower, rotate_upper)
  70. sample['image'] = self.apply_im(sample['image'], angle)
  71. return sample
  72. class ComposedClsTransforms(Compose):
  73. """ 分类模型的基础Transforms流程,具体如下
  74. 训练阶段:
  75. 1. 随机从图像中crop一块子图,并resize成crop_size大小
  76. 2. 将1的输出按0.5的概率随机进行水平翻转
  77. 3. 将图像进行归一化
  78. 验证/预测阶段:
  79. 1. 将图像按比例Resize,使得最小边长度为crop_size[0] * 1.14
  80. 2. 从图像中心crop出一个大小为crop_size的图像
  81. 3. 将图像进行归一化
  82. Args:
  83. mode(str): 图像处理流程所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
  84. crop_size(int|list): 输入模型里的图像大小
  85. mean(list): 图像均值
  86. std(list): 图像方差
  87. random_horizontal_flip(bool): 是否以0.5的概率使用随机水平翻转增强,该仅在mode为`train`时生效,默认为True
  88. """
  89. def __init__(self,
  90. mode,
  91. crop_size=[224, 224],
  92. mean=[0.485, 0.456, 0.406],
  93. std=[0.229, 0.224, 0.225],
  94. random_horizontal_flip=True):
  95. width = crop_size
  96. if isinstance(crop_size, list):
  97. if crop_size[0] != crop_size[1]:
  98. raise Exception(
  99. "In classifier model, width and height should be equal, please modify your parameter `crop_size`"
  100. )
  101. width = crop_size[0]
  102. if width % 32 != 0:
  103. raise Exception(
  104. "In classifier model, width and height should be multiple of 32, e.g 224、256、320...., please modify your parameter `crop_size`"
  105. )
  106. if mode == 'train':
  107. # 训练时的transforms,包含数据增强
  108. transforms = [
  109. RandomCrop(crop_size=width), Normalize(
  110. mean=mean, std=std)
  111. ]
  112. if random_horizontal_flip:
  113. transforms.insert(0, RandomHorizontalFlip())
  114. else:
  115. # 验证/预测时的transforms
  116. transforms = [
  117. ResizeByShort(short_size=int(width * 1.14)),
  118. CenterCrop(crop_size=width), Normalize(
  119. mean=mean, std=std)
  120. ]
  121. super(ComposedClsTransforms, self).__init__(transforms)
  122. def _random_crop(im,
  123. crop_size=224,
  124. lower_scale=0.08,
  125. lower_ratio=3. / 4,
  126. upper_ratio=4. / 3):
  127. scale = [lower_scale, 1.0]
  128. ratio = [lower_ratio, upper_ratio]
  129. aspect_ratio = math.sqrt(np.random.uniform(*ratio))
  130. w = 1. * aspect_ratio
  131. h = 1. / aspect_ratio
  132. bound = min((float(im.shape[0]) / im.shape[1]) / (h**2),
  133. (float(im.shape[1]) / im.shape[0]) / (w**2))
  134. scale_max = min(scale[1], bound)
  135. scale_min = min(scale[0], bound)
  136. target_area = im.shape[0] * im.shape[1] * np.random.uniform(scale_min,
  137. scale_max)
  138. target_size = math.sqrt(target_area)
  139. w = int(target_size * w)
  140. h = int(target_size * h)
  141. i = np.random.randint(0, im.shape[0] - h + 1)
  142. j = np.random.randint(0, im.shape[1] - w + 1)
  143. im = im[i:i + h, j:j + w, :]
  144. im = cv2.resize(im, (crop_size, crop_size))
  145. return im