You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

py_transforms.py 11 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. # Copyright 2019 Huawei Technologies Co., Ltd
  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. # ==============================================================================
  15. """
  16. The module transforms.py_transform is implemented based on Python. It provides common
  17. operations including OneHotOp.
  18. """
  19. from .validators import check_one_hot_op, check_compose_list, check_random_apply, check_transforms_list, \
  20. check_compose_call
  21. from . import py_transforms_util as util
  22. def not_random(function):
  23. function.random = False
  24. return function
  25. class OneHotOp:
  26. """
  27. Apply one hot encoding transformation to the input label, make label be more smoothing and continuous.
  28. Args:
  29. num_classes (int): Number of classes of objects in dataset.
  30. It should be larger than the largest label number in the dataset.
  31. smoothing_rate (float, optional): Adjustable hyperparameter for label smoothing level.
  32. (Default=0.0 means no smoothing is applied.)
  33. Examples:
  34. >>> # Assume that dataset has 10 classes, thus the label ranges from 0 to 9
  35. >>> transforms_list = [py_transforms.OneHotOp(num_classes=10, smoothing_rate=0.1)]
  36. >>> transform = py_transforms.Compose(transforms_list)
  37. >>> mnist_dataset = mnist_dataset(input_columns=["label"], operations=transform)
  38. """
  39. @check_one_hot_op
  40. def __init__(self, num_classes, smoothing_rate=0.0):
  41. self.num_classes = num_classes
  42. self.smoothing_rate = smoothing_rate
  43. self.random = False
  44. def __call__(self, label):
  45. """
  46. Call method.
  47. Args:
  48. label (numpy.ndarray): label to be applied label smoothing.
  49. Returns:
  50. label (numpy.ndarray), label after being Smoothed.
  51. """
  52. return util.one_hot_encoding(label, self.num_classes, self.smoothing_rate)
  53. class Compose:
  54. """
  55. Compose a list of transforms.
  56. .. Note::
  57. Compose takes a list of transformations either provided in py_transforms or from user-defined implementation;
  58. each can be an initialized transformation class or a lambda function, as long as the output from the last
  59. transformation is a single tensor of type numpy.ndarray. See below for an example of how to use Compose
  60. with py_transforms classes and check out FiveCrop or TenCrop for the use of them in conjunction with lambda
  61. functions.
  62. Args:
  63. transforms (list): List of transformations to be applied.
  64. Examples:
  65. >>> image_folder_dataset_dir = "/path/to/image_folder_dataset_directory"
  66. >>> # create a dataset that reads all files in dataset_dir with 8 threads
  67. >>> image_folder_dataset = ds.ImageFolderDataset(image_folder_dataset_dir, num_parallel_workers=8)
  68. >>> # create a list of transformations to be applied to the image data
  69. >>> transform = py_transforms.Compose([py_vision.Decode(),
  70. ... py_vision.RandomHorizontalFlip(0.5),
  71. ... py_vision.ToTensor(),
  72. ... py_vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)),
  73. ... py_vision.RandomErasing()])
  74. >>> # apply the transform to the dataset through dataset.map function
  75. >>> image_folder_dataset = image_folder_dataset.map(operations=transform, input_columns=["image"])
  76. >>>
  77. >>> # Compose is also be invoked implicitly, by just passing in a list of ops
  78. >>> # the above example then becomes:
  79. >>> transforms_list = [py_vision.Decode(),
  80. ... py_vision.RandomHorizontalFlip(0.5),
  81. ... py_vision.ToTensor(),
  82. ... py_vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)),
  83. ... py_vision.RandomErasing()]
  84. >>>
  85. >>> # apply the transform to the dataset through dataset.map()
  86. >>> image_folder_dataset_1 = image_folder_dataset_1.map(operations=transforms_list, input_columns=["image"])
  87. >>>
  88. >>> # Certain C++ and Python ops can be combined, but not all of them
  89. >>> # An example of combined operations
  90. >>> arr = [0, 1]
  91. >>> dataset = ds.NumpySlicesDataset(arr, column_names=["cols"], shuffle=False)
  92. >>> transformed_list = [py_transforms.OneHotOp(2), c_transforms.Mask(c_transforms.Relational.EQ, 1)]
  93. >>> dataset = dataset.map(operations=transformed_list, input_columns=["cols"])
  94. >>>
  95. >>> # Here is an example of mixing vision ops
  96. >>> import numpy as np
  97. >>> op_list=[c_vision.Decode(),
  98. ... c_vision.Resize((224, 244)),
  99. ... py_vision.ToPIL(),
  100. ... np.array, # need to convert PIL image to a NumPy array to pass it to C++ operation
  101. ... c_vision.Resize((24, 24))]
  102. >>> image_folder_dataset = image_folder_dataset.map(operations=op_list, input_columns=["image"])
  103. """
  104. @check_compose_list
  105. def __init__(self, transforms):
  106. self.transforms = transforms
  107. if all(hasattr(transform, "random") and not transform.random for transform in self.transforms):
  108. self.random = False
  109. @check_compose_call
  110. def __call__(self, *args):
  111. """
  112. Call method.
  113. Returns:
  114. lambda function, Lambda function that takes in an args to apply transformations on.
  115. """
  116. return util.compose(self.transforms, *args)
  117. @staticmethod
  118. def reduce(operations):
  119. """
  120. Wraps adjacent Python operations in a Compose to allow mixing of Python and C++ operations
  121. Args:
  122. operations (list): list of tensor operations
  123. Returns:
  124. list, the reduced list of operations
  125. """
  126. #
  127. if len(operations) == 1:
  128. return operations
  129. new_ops, start_ind, end_ind = [], 0, 0
  130. for i, op in enumerate(operations):
  131. if str(op).find("c_transform") >= 0:
  132. # reset counts
  133. if start_ind != end_ind:
  134. new_ops.append(Compose(operations[start_ind:end_ind]))
  135. new_ops.append(op)
  136. start_ind, end_ind = i + 1, i + 1
  137. else:
  138. end_ind += 1
  139. # do additional check in case the last operation is a Python operation
  140. if start_ind != end_ind:
  141. new_ops.append(Compose(operations[start_ind:end_ind]))
  142. return new_ops
  143. class RandomApply:
  144. """
  145. Randomly perform a series of transforms with a given probability.
  146. Args:
  147. transforms (list): List of transformations to apply.
  148. prob (float, optional): The probability to apply the transformation list (default=0.5).
  149. Examples:
  150. >>> from mindspore.dataset.transforms.py_transforms import Compose
  151. >>> transforms_list = [py_vision.RandomHorizontalFlip(0.5),
  152. ... py_vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)),
  153. ... py_vision.RandomErasing()]
  154. >>> transforms = Compose([py_vision.Decode(),
  155. ... py_transforms.RandomApply(transforms_list, prob=0.6),
  156. ... py_vision.ToTensor()])
  157. >>> image_folder_dataset = image_folder_dataset.map(operations=transforms, input_columns=["image"])
  158. """
  159. @check_random_apply
  160. def __init__(self, transforms, prob=0.5):
  161. self.prob = prob
  162. self.transforms = transforms
  163. def __call__(self, img):
  164. """
  165. Call method.
  166. Args:
  167. img (PIL image): Image to be randomly applied a list transformations.
  168. Returns:
  169. img (PIL image), Transformed image.
  170. """
  171. return util.random_apply(img, self.transforms, self.prob)
  172. class RandomChoice:
  173. """
  174. Randomly select one transform from a series of transforms and applies that on the image.
  175. Args:
  176. transforms (list): List of transformations to be chosen from to apply.
  177. Examples:
  178. >>> from mindspore.dataset.transforms.py_transforms import Compose, RandomChoice
  179. >>> transforms_list = [py_vision.RandomHorizontalFlip(0.5),
  180. ... py_vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)),
  181. ... py_vision.RandomErasing()]
  182. >>> transforms = Compose([py_vision.Decode(),
  183. ... py_transforms.RandomChoice(transforms_list),
  184. ... py_vision.ToTensor()])
  185. >>> image_folder_dataset = image_folder_dataset.map(operations=transforms, input_columns=["image"])
  186. """
  187. @check_transforms_list
  188. def __init__(self, transforms):
  189. self.transforms = transforms
  190. def __call__(self, img):
  191. """
  192. Call method.
  193. Args:
  194. img (PIL image): Image to be applied transformation.
  195. Returns:
  196. img (PIL image), Transformed image.
  197. """
  198. return util.random_choice(img, self.transforms)
  199. class RandomOrder:
  200. """
  201. Perform a series of transforms to the input PIL image in a random order.
  202. Args:
  203. transforms (list): List of the transformations to apply.
  204. Examples:
  205. >>> from mindspore.dataset.transforms.py_transforms import Compose
  206. >>> transforms_list = [py_vision.RandomHorizontalFlip(0.5),
  207. ... py_vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)),
  208. ... py_vision.RandomErasing()]
  209. >>> transforms = Compose([py_vision.Decode(),
  210. ... py_transforms.RandomOrder(transforms_list),
  211. ... py_vision.ToTensor()])
  212. >>> image_folder_dataset = image_folder_dataset.map(operations=transforms, input_columns=["image"])
  213. """
  214. @check_transforms_list
  215. def __init__(self, transforms):
  216. self.transforms = transforms
  217. def __call__(self, img):
  218. """
  219. Call method.
  220. Args:
  221. img (PIL image): Image to apply transformations in a random order.
  222. Returns:
  223. img (PIL image), Transformed image.
  224. """
  225. return util.random_order(img, self.transforms)