1+ import cv2
2+ import typing
3+ import numpy as np
4+
5+ """ Implemented augmentors:
6+ - RandomBrightness
7+ - RandomRotate
8+ - RandomErodeDilate
9+ """
10+
11+ class Augmentor :
12+ """ Object that should be inherited by all augmentors
13+ Args:
14+ image (np.ndarray): Image to augment
15+ annotation (np.ndarray): Annotation to augment
16+
17+ Returns:
18+ typing.Tuple[np.ndarray, np.ndarray]: Augmented image and mask
19+ """
20+ def __init__ (self , random_chance : float = 0.5 ) -> None :
21+ """
22+ Args:
23+ random_chance (float, optional): Chance of applying the augmentor. Defaults to 0.5.
24+ """
25+ self ._random_chance = random_chance
26+
27+ def __call__ (self , image : np .ndarray , annotation : np .ndarray ) -> typing .Tuple [np .ndarray , np .ndarray ]:
28+ if np .random .random () <= self ._random_chance :
29+ pass
30+
31+ return image , annotation
32+
33+ class RandomBrightness (Augmentor ):
34+ """ Randomly adjust image brightness
35+
36+ Args:
37+ image (np.ndarray): Image to be adjusted
38+ annotation (np.ndarray): Annotation to be adjusted
39+
40+ Returns:
41+ image (np.ndarray): Adjusted image
42+ annotation (np.ndarray): Adjusted annotation
43+ """
44+ def __init__ (self , random_chance :float = 0.5 , delta :int = 100 )-> None :
45+ """
46+ Args:
47+ random_chance (float): Float between 0.0 and 1.0 setting bounds for random probability
48+ delta (int): Integer value for brightness adjustment
49+ """
50+ assert delta >= 0.0
51+ assert delta <= 255.0
52+
53+ self ._random_chance = random_chance
54+ self ._delta = delta
55+
56+ def __call__ (self , image :np .ndarray , annotation :np .ndarray )-> typing .Tuple [np .ndarray , np .ndarray ]:
57+ if np .random .rand () <= self ._random_chance :
58+
59+ image = cv2 .cvtColor (image , cv2 .COLOR_BGR2HSV )
60+
61+ value = 1 + np .random .uniform (- self ._delta , self ._delta ) / 255
62+
63+ hsv = np .array (image , dtype = np .float32 )
64+
65+ hsv [:, :, 1 ] = hsv [:, :, 1 ] * value
66+ hsv [:, :, 2 ] = hsv [:, :, 2 ] * value
67+
68+ hsv = np .uint8 (np .clip (hsv , 0 , 255 ))
69+
70+ image = cv2 .cvtColor (hsv , cv2 .COLOR_HSV2BGR )
71+
72+ return image , annotation
73+
74+ class RandomRotate (Augmentor ):
75+ """ Randomly rotate image
76+
77+ Args:
78+ image (np.ndarray): Image to be rotated
79+ annotation (np.ndarray): Annotation to be rotated
80+
81+ Returns:
82+ image (np.ndarray): Rotated image
83+ annotation (np.ndarray): Rotated annotation
84+ """
85+ def __init__ (self , random_chance :float = 0.5 , angle :int = 10 , borderValue :typing .Tuple [int , int , int ]= (255 , 255 , 255 ))-> None :
86+ """
87+ Args:
88+ random_chance (float): Float between 0.0 and 1.0 setting bounds for random probability
89+ angle (int): Integer value for rotation angle, in degrees
90+ borderValue (tuple): Tuple of 3 integers, setting border color for image rotation
91+ """
92+ self ._random_chance = random_chance
93+ self ._angle = angle
94+ self ._borderValue = borderValue
95+
96+ def __call__ (self , image :np .ndarray , annotation :np .ndarray )-> typing .Tuple [np .ndarray , np .ndarray ]:
97+ if np .random .rand () <= self ._random_chance :
98+
99+ angle = np .random .uniform (- self ._angle , self ._angle )
100+
101+ h , w , _ = image .shape
102+ m = cv2 .getRotationMatrix2D ((w / 2 , h / 2 ), angle , 1 )
103+ image = cv2 .warpAffine (image , m , (w , h ), borderValue = self ._borderValue )
104+ # Check if annotation is image mask
105+ if not isinstance (annotation , str ):
106+ annotation = cv2 .warpAffine (annotation , m , (w , h ), borderValue = self ._borderValue )
107+
108+ return image , annotation
109+
110+ class RandomErodeDilate :
111+ """ Randomly erode and dilate image
112+
113+ Args:
114+ image (np.ndarray): Image to be eroded and dilated
115+
116+ Returns:
117+ image (np.ndarray): Eroded and dilated image
118+ """
119+ def __init__ (self , random_chance :float = 0.5 , kernel_size :typing .Tuple [int , int ]= (1 , 1 ))-> None :
120+ """
121+ Args:
122+ random_chance (float): Float between 0.0 and 1.0 setting bounds for random probability
123+ kernel_size (tuple): Tuple of 2 integers, setting kernel size for erosion and dilation
124+ """
125+ self ._random_chance = random_chance
126+ self ._kernel_size = kernel_size
127+
128+ def __call__ (self , image :np .ndarray , annotation )-> typing .Tuple [np .ndarray , np .ndarray ]:
129+ if np .random .rand () <= self ._random_chance :
130+
131+ kernel = np .ones (self ._kernel_size , np .uint8 )
132+
133+ if np .random .rand () <= 0.5 :
134+ image = cv2 .erode (image , kernel , iterations = 1 )
135+ else :
136+ image = cv2 .dilate (image , kernel , iterations = 1 )
137+
138+ return image , annotation
0 commit comments