def test__draw_samples__single_value_hysteresis(self): seed = 1 nb_images = 1000 aug = iaa.Canny( alpha=0.2, hysteresis_thresholds=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], sobel_kernel_size=[3, 5, 7], random_state=np.random.RandomState(seed)) example_image = np.zeros((5, 5, 3), dtype=np.uint8) samples = aug._draw_samples([example_image] * nb_images, random_state=np.random.RandomState(seed)) alpha_samples = samples[0] hthresh_samples = samples[1] sobel_samples = samples[2] rss = ia.derive_random_states(np.random.RandomState(seed), 4) alpha_expected = [0.2] * nb_images hthresh_expected = rss[1].choice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size=(nb_images, 2)) sobel_expected = rss[3].choice([3, 5, 7], size=(nb_images, )) invalid = hthresh_expected[:, 0] > hthresh_expected[:, 1] assert np.any(invalid) hthresh_expected[invalid, :] = hthresh_expected[invalid, :][:, [1, 0]] assert hthresh_expected.shape == (nb_images, 2) assert not np.any(hthresh_expected[:, 0] > hthresh_expected[:, 1]) assert np.allclose(alpha_samples, alpha_expected) assert np.allclose(hthresh_samples, hthresh_expected) assert np.allclose(sobel_samples, sobel_expected)
def generate_maps(self, image, random_state): intensity_mean_sample = self.intensity_mean.draw_sample(random_state) alpha_min_sample = self.alpha_min.draw_sample(random_state) alpha_multiplier_sample = \ self.alpha_multiplier.draw_sample(random_state) alpha_size_px_max = self.alpha_size_px_max intensity_freq_exponent = self.intensity_freq_exponent alpha_freq_exponent = self.alpha_freq_exponent sparsity_sample = self.sparsity.draw_sample(random_state) density_multiplier_sample = \ self.density_multiplier.draw_sample(random_state) height, width = image.shape[0:2] rss_alpha, rss_intensity = ia.derive_random_states(random_state, 2) intensity_coarse = self._generate_intensity_map_coarse( height, width, intensity_mean_sample, iap.Normal(0, scale=self.intensity_coarse_scale), rss_intensity) intensity_fine = self._generate_intensity_map_fine( height, width, intensity_mean_sample, intensity_freq_exponent, rss_intensity) intensity = intensity_coarse + intensity_fine alpha = self._generate_alpha_mask(height, width, alpha_min_sample, alpha_multiplier_sample, alpha_freq_exponent, alpha_size_px_max, sparsity_sample, density_multiplier_sample, rss_alpha) return alpha, intensity
def _draw_samples(self, augmentables, random_state): nb_augmentables = len(augmentables) rss = ia.derive_random_states(random_state, 2) thresh_samples = self.lightness_threshold.draw_samples( (nb_augmentables, ), rss[1]) lmul_samples = self.lightness_multiplier.draw_samples( (nb_augmentables, ), rss[0]) return thresh_samples, lmul_samples
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=[ "bool", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64" ], disallowed=[ "uint128", "uint256", "int128", "int256", "float16", "float32", "float64", "float96", "float128", "float256" ], augmenter=self) nb_images = len(images) rss = ia.derive_random_states(random_state, 1 + nb_images) n_segments_samples = self.n_segments.draw_samples((nb_images, ), random_state=rss[0]) # We cant reduce images to 0 or less segments, hence we pick the # lowest possible value in these cases (i.e. 1). The alternative # would be to not perform superpixel detection in these cases # (akin to n_segments=#pixels). # TODO add test for this n_segments_samples = np.clip(n_segments_samples, 1, None) for i, (image, rs) in enumerate(zip(images, rss[1:])): replace_samples = self.p_replace.draw_samples( (n_segments_samples[i], ), random_state=rs) if np.max(replace_samples) == 0: # not a single superpixel would be replaced by its average # color, i.e. the image would not be changed, so just keep it continue image = images[i] orig_shape = image.shape image = self._ensure_max_size(image, self.max_size, self.interpolation) segments = segmentation.slic(image, n_segments=n_segments_samples[i], compactness=10) image_sp = self._replace_segments(image, segments, replace_samples) if orig_shape != image.shape: image_sp = ia.imresize_single_image( image_sp, orig_shape[0:2], interpolation=self.interpolation) images[i] = image_sp return images
def _draw_samples(self, augmentables, random_state): nb_images = len(augmentables) rss = ia.derive_random_states(random_state, 2) mode = "single" if self.kernel_size[1] is None else "two" kernel_sizes_h = self.kernel_size[0].draw_samples((nb_images, ), random_state=rss[0]) if mode == "single": kernel_sizes_w = kernel_sizes_h else: kernel_sizes_w = self.kernel_size[1].draw_samples( (nb_images, ), random_state=rss[1]) return kernel_sizes_h, kernel_sizes_w
def _augment_images(self, images, random_state, parents, hooks): # Make sure that all images have 3 channels ia.do_assert(all([image.shape[2] == 3 for image in images]), ("BilateralBlur can currently only be applied to images with 3 channels." + "Got channels: %s") % ([image.shape[2] for image in images],)) nb_images = len(images) rss = ia.derive_random_states(random_state, 3) samples_d = self.d.draw_samples((nb_images,), random_state=rss[0]) samples_sigma_color = self.sigma_color.draw_samples((nb_images,), random_state=rss[1]) samples_sigma_space = self.sigma_space.draw_samples((nb_images,), random_state=rss[2]) gen = enumerate(zip(images, samples_d, samples_sigma_color, samples_sigma_space)) for i, (image, di, sigma_color_i, sigma_space_i) in gen: if di != 1: images[i] = cv2.bilateralFilter(image, di, sigma_color_i, sigma_space_i) return images
def _augment_images(self, images, random_state, parents, hooks): # Make sure that all images have 3 channels ia.do_assert(all([image.shape[2] == 3 for image in images]), ("BilateralBlur can currently only be applied to images with 3 channels." + "Got channels: %s") % ([image.shape[2] for image in images],)) nb_images = len(images) rss = ia.derive_random_states(random_state, 3) samples_d = self.d.draw_samples((nb_images,), random_state=rss[0]) samples_sigma_color = self.sigma_color.draw_samples((nb_images,), random_state=rss[1]) samples_sigma_space = self.sigma_space.draw_samples((nb_images,), random_state=rss[2]) gen = enumerate(zip(images, samples_d, samples_sigma_color, samples_sigma_space)) for i, (image, di, sigma_color_i, sigma_space_i) in gen: if di != 1: images[i] = cv2.bilateralFilter(image, di, sigma_color_i, sigma_space_i) return images
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=[ "bool", "uint8", "uint16", "int8", "int16", "float16", "float32", "float64" ], disallowed=[ "uint32", "uint64", "uint128", "uint256", "int32", "int64", "int128", "int256", "float96", "float128", "float256" ], augmenter=self) nb_images = len(images) if self.mode == "single": samples = self.k.draw_samples((nb_images, ), random_state=random_state) samples = (samples, samples) else: rss = ia.derive_random_states(random_state, 2) samples = ( self.k[0].draw_samples((nb_images, ), random_state=rss[0]), self.k[1].draw_samples((nb_images, ), random_state=rss[1]), ) for i, (image, kh, kw) in enumerate(zip(images, samples[0], samples[1])): kernel_impossible = (kh == 0 or kw == 0) kernel_does_nothing = (kh == 1 and kw == 1) if not kernel_impossible and not kernel_does_nothing: input_dtype = image.dtype if image.dtype in [np.bool_, np.float16]: image = image.astype(np.float32, copy=False) elif image.dtype == np.int8: image = image.astype(np.int16, copy=False) image_aug = cv2.blur(image, (kh, kw)) # cv2.blur() removes channel axis for single-channel images if image_aug.ndim == 2: image_aug = image_aug[..., np.newaxis] if input_dtype == np.bool_: image_aug = image_aug > 0.5 elif input_dtype in [np.int8, np.float16]: image_aug = iadt.restore_dtypes_(image_aug, input_dtype) images[i] = image_aug return images
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=["uint8"], disallowed=[ "bool", "uint16", "uint32", "uint64", "uint128", "uint256", "int8", "int16", "int32", "int64", "int128", "int256", "float32", "float64", "float96", "float128", "float256"], augmenter=self) rss = ia.derive_random_states(random_state, len(images)) samples = self._draw_samples(images, rss[-1]) alpha_samples = samples[0] hthresh_samples = samples[1] sobel_samples = samples[2] result = images gen = enumerate(zip(images, alpha_samples, hthresh_samples, sobel_samples)) for i, (image, alpha, hthreshs, sobel) in gen: assert image.ndim == 3 assert image.shape[-1] in [1, 3, 4], ( "Canny edge detector can currently only handle images with " "channel numbers that are 1, 3 or 4. Got %d.") % ( image.shape[-1],) if alpha > 0 and sobel > 1: image_canny = cv2.Canny( image[:, :, 0:3], threshold1=hthreshs[0], threshold2=hthreshs[1], apertureSize=sobel, L2gradient=True) image_canny = (image_canny > 0) # canny returns a boolean (H,W) image, so we change it to # (H,W,C) and then uint8 image_canny_color = self.colorizer.colorize( image_canny, image, nth_image=i, random_state=rss[i]) result[i] = blend.blend_alpha(image_canny_color, image, alpha) return result
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=["bool", "uint8", "uint16", "int8", "int16", "float16", "float32", "float64"], disallowed=["uint32", "uint64", "uint128", "uint256", "int32", "int64", "int128", "int256", "float96", "float128", "float256"], augmenter=self) nb_images = len(images) if self.mode == "single": samples = self.k.draw_samples((nb_images,), random_state=random_state) samples = (samples, samples) else: rss = ia.derive_random_states(random_state, 2) samples = ( self.k[0].draw_samples((nb_images,), random_state=rss[0]), self.k[1].draw_samples((nb_images,), random_state=rss[1]), ) for i, (image, kh, kw) in enumerate(zip(images, samples[0], samples[1])): kernel_impossible = (kh == 0 or kw == 0) kernel_does_nothing = (kh == 1 and kw == 1) if not kernel_impossible and not kernel_does_nothing: input_dtype = image.dtype if image.dtype in [np.bool_, np.float16]: image = image.astype(np.float32, copy=False) elif image.dtype == np.int8: image = image.astype(np.int16, copy=False) image_aug = cv2.blur(image, (kh, kw)) # cv2.blur() removes channel axis for single-channel images if image_aug.ndim == 2: image_aug = image_aug[..., np.newaxis] if input_dtype == np.bool_: image_aug = image_aug > 0.5 elif input_dtype in [np.int8, np.float16]: image_aug = iadt.restore_dtypes_(image_aug, input_dtype) images[i] = image_aug return images
def test__draw_samples__tuple_as_hysteresis(self): seed = 1 nb_images = 10 aug = iaa.Canny( alpha=0.2, hysteresis_thresholds=([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], iap.DiscreteUniform(5, 100)), sobel_kernel_size=[3, 5, 7], random_state=np.random.RandomState(seed)) example_image = np.zeros((5, 5, 3), dtype=np.uint8) samples = aug._draw_samples([example_image] * nb_images, random_state=np.random.RandomState(seed)) alpha_samples = samples[0] hthresh_samples = samples[1] sobel_samples = samples[2] rss = ia.derive_random_states(np.random.RandomState(seed), 4) alpha_expected = [0.2] * nb_images hthresh_expected = ( rss[1].choice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size=(nb_images, )), # TODO simplify this to rss[2].randint(5, 100+1) # would currenlty be a bit more ugly, because DiscrUniform # samples two values for a and b first from rss[2] iap.DiscreteUniform(5, 100).draw_samples((nb_images, ), rss[2])) hthresh_expected = np.stack(hthresh_expected, axis=-1) sobel_expected = rss[3].choice([3, 5, 7], size=(nb_images, )) invalid = hthresh_expected[:, 0] > hthresh_expected[:, 1] hthresh_expected[invalid, :] = hthresh_expected[invalid, :][:, [1, 0]] assert hthresh_expected.shape == (nb_images, 2) assert not np.any(hthresh_expected[:, 0] > hthresh_expected[:, 1]) assert np.allclose(alpha_samples, alpha_expected) assert np.allclose(hthresh_samples, hthresh_expected) assert np.allclose(sobel_samples, sobel_expected)
def _draw_samples(self, augmentables, random_state): nb_images = len(augmentables) rss = ia.derive_random_states(random_state, 4) alpha_samples = self.alpha.draw_samples((nb_images,), rss[0]) hthresh = self.hysteresis_thresholds if isinstance(hthresh, tuple): assert len(hthresh) == 2 min_values = hthresh[0].draw_samples((nb_images,), rss[1]) max_values = hthresh[1].draw_samples((nb_images,), rss[2]) hthresh_samples = np.stack([min_values, max_values], axis=-1) else: hthresh_samples = hthresh.draw_samples((nb_images, 2), rss[1]) sobel_samples = self.sobel_kernel_size.draw_samples((nb_images,), rss[3]) # verify for hysteresis thresholds that min_value < max_value everywhere invalid = (hthresh_samples[:, 0] > hthresh_samples[:, 1]) if np.any(invalid): hthresh_samples[invalid, :] = hthresh_samples[invalid, :][:, [1, 0]] # ensure that sobel kernel sizes are correct # note that OpenCV accepts only kernel sizes that are (a) even # and (b) <=7 assert not np.any(sobel_samples < 0), ( "Sampled a sobel kernel size below 0 in Canny. " "Allowed value range is 0 to 7.") assert not np.any(sobel_samples > 7), ( "Sampled a sobel kernel size above 7 in Canny. " "Allowed value range is 0 to 7.") even_idx = (np.mod(sobel_samples, 2) == 0) sobel_samples[even_idx] -= 1 return alpha_samples, hthresh_samples, sobel_samples
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=[ "bool", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64" ], disallowed=[ "uint128", "uint256", "int128", "int256", "float16", "float32", "float64", "float96", "float128", "float256" ], augmenter=self) nb_images = len(images) rss = ia.derive_random_states(random_state, 1 + nb_images) n_segments_samples = self.n_segments.draw_samples((nb_images, ), random_state=rss[0]) for i, (image, rs) in enumerate(zip(images, rss[1:])): # TODO this results in an error when n_segments is 0 replace_samples = self.p_replace.draw_samples( (n_segments_samples[i], ), random_state=rs) if np.max(replace_samples) == 0: # not a single superpixel would be replaced by its average color, # i.e. the image would not be changed, so just keep it pass else: image = images[i] min_value, _center_value, max_value = iadt.get_value_range_of_dtype( image.dtype) orig_shape = image.shape if self.max_size is not None: size = max(image.shape[0], image.shape[1]) if size > self.max_size: resize_factor = self.max_size / size new_height, new_width = int( image.shape[0] * resize_factor), int( image.shape[1] * resize_factor) image = ia.imresize_single_image( image, (new_height, new_width), interpolation=self.interpolation) image_sp = np.copy(image) segments = segmentation.slic(image, n_segments=n_segments_samples[i], compactness=10) nb_channels = image.shape[2] for c in sm.xrange(nb_channels): # segments+1 here because otherwise regionprops always misses # the last label regions = measure.regionprops(segments + 1, intensity_image=image[..., c]) for ridx, region in enumerate(regions): # with mod here, because slic can sometimes create more superpixel # than requested. replace_samples then does not have enough # values, so we just start over with the first one again. if replace_samples[ridx % len(replace_samples)] >= 0.5: mean_intensity = region.mean_intensity image_sp_c = image_sp[..., c] if image_sp_c.dtype.kind in ["i", "u", "b"]: # After rounding the value can end up slightly outside of the value_range. # Hence, we need to clip. We do clip via min(max(...)) instead of np.clip # because the latter one does not seem to keep dtypes for dtypes with # large itemsizes (e.g. uint64). value = int(np.round(mean_intensity)) value = min(max(value, min_value), max_value) image_sp_c[segments == ridx] = value else: image_sp_c[segments == ridx] = mean_intensity if orig_shape != image.shape: image_sp = ia.imresize_single_image( image_sp, orig_shape[0:2], interpolation=self.interpolation) images[i] = image_sp return images
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=["bool", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64"], disallowed=["uint128", "uint256", "int128", "int256", "float16", "float32", "float64", "float96", "float128", "float256"], augmenter=self) nb_images = len(images) rss = ia.derive_random_states(random_state, 1+nb_images) n_segments_samples = self.n_segments.draw_samples((nb_images,), random_state=rss[0]) for i, (image, rs) in enumerate(zip(images, rss[1:])): # TODO this results in an error when n_segments is 0 replace_samples = self.p_replace.draw_samples((n_segments_samples[i],), random_state=rs) if np.max(replace_samples) == 0: # not a single superpixel would be replaced by its average color, # i.e. the image would not be changed, so just keep it pass else: image = images[i] min_value, _center_value, max_value = iadt.get_value_range_of_dtype(image.dtype) orig_shape = image.shape if self.max_size is not None: size = max(image.shape[0], image.shape[1]) if size > self.max_size: resize_factor = self.max_size / size new_height, new_width = int(image.shape[0] * resize_factor), int(image.shape[1] * resize_factor) image = ia.imresize_single_image(image, (new_height, new_width), interpolation=self.interpolation) image_sp = np.copy(image) segments = segmentation.slic(image, n_segments=n_segments_samples[i], compactness=10) nb_channels = image.shape[2] for c in sm.xrange(nb_channels): # segments+1 here because otherwise regionprops always misses # the last label regions = measure.regionprops(segments+1, intensity_image=image[..., c]) for ridx, region in enumerate(regions): # with mod here, because slic can sometimes create more superpixel # than requested. replace_samples then does not have enough # values, so we just start over with the first one again. if replace_samples[ridx % len(replace_samples)] >= 0.5: mean_intensity = region.mean_intensity image_sp_c = image_sp[..., c] if image_sp_c.dtype.kind in ["i", "u", "b"]: # After rounding the value can end up slightly outside of the value_range. # Hence, we need to clip. We do clip via min(max(...)) instead of np.clip # because the latter one does not seem to keep dtypes for dtypes with # large itemsizes (e.g. uint64). value = int(np.round(mean_intensity)) value = min(max(value, min_value), max_value) image_sp_c[segments == ridx] = value else: image_sp_c[segments == ridx] = mean_intensity if orig_shape != image.shape: image_sp = ia.imresize_single_image(image_sp, orig_shape[0:2], interpolation=self.interpolation) images[i] = image_sp return images
def _augment_images(self, images, random_state, parents, hooks): iadt.gate_dtypes(images, allowed=["bool", "uint8", "uint16", "int8", "int16", "float16", "float32", "float64"], disallowed=["uint32", "uint64", "uint128", "uint256", "int32", "int64", "int128", "int256", "float96", "float128", "float256"], augmenter=self) rss = ia.derive_random_states(random_state, len(images)) for i, image in enumerate(images): _height, _width, nb_channels = images[i].shape input_dtype = image.dtype if image.dtype.type in [np.bool_, np.float16]: image = image.astype(np.float32, copy=False) elif image.dtype.type == np.int8: image = image.astype(np.int16, copy=False) if self.matrix_type == "None": matrices = [None] * nb_channels elif self.matrix_type == "constant": matrices = [self.matrix] * nb_channels elif self.matrix_type == "function": matrices = self.matrix(images[i], nb_channels, rss[i]) if ia.is_np_array(matrices) and matrices.ndim == 2: matrices = np.tile( matrices[..., np.newaxis], (1, 1, nb_channels)) is_valid_list = (isinstance(matrices, list) and len(matrices) == nb_channels) is_valid_array = (ia.is_np_array(matrices) and matrices.ndim == 3 and matrices.shape[2] == nb_channels) ia.do_assert( is_valid_list or is_valid_array, "Callable provided to Convole must return either a " "list of 2D matrices (one per image channel) " "or a 2D numpy array " "or a 3D numpy array where the last dimension's size " "matches the number of image channels. " "Got type %s." % (type(matrices),) ) if ia.is_np_array(matrices): # Shape of matrices is currently (H, W, C), but in the # loop below we need the first axis to be the channel # index to unify handling of lists of arrays and arrays. # So we move the channel axis here to the start. matrices = matrices.transpose((2, 0, 1)) else: raise Exception("Invalid matrix type") image_aug = image for channel in sm.xrange(nb_channels): if matrices[channel] is not None: # ndimage.convolve caused problems here cv2.filter2D() # always returns same output dtype as input dtype image_aug[..., channel] = cv2.filter2D( image_aug[..., channel], -1, matrices[channel] ) if input_dtype == np.bool_: image_aug = image_aug > 0.5 elif input_dtype in [np.int8, np.float16]: image_aug = iadt.restore_dtypes_(image_aug, input_dtype) images[i] = image_aug return images
def _augment_images(self, images, random_state, parents, hooks): rss = ia.derive_random_states(random_state, len(images)) result = images for i, (image, rs) in enumerate(zip(images, rss)): result[i] = self.draw_on_image(image, rs) return result
def _augment_images(self, images, random_state, parents, hooks): input_dtypes = iadt.copy_dtypes_for_restore(images, force_list=True) result = images nb_images = len(images) # surprisingly, placing this here seems to be slightly slower than placing it inside the loop # if isinstance(images_hsv, list): # images_hsv = [img.astype(np.int32) for img in images_hsv] # else: # images_hsv = images_hsv.astype(np.int32) rss = ia.derive_random_states(random_state, 3) images_hsv = self.colorspace_changer._augment_images(images, rss[0], parents + [self], hooks) samples = self.value.draw_samples((nb_images, 2), random_state=rss[1]).astype(np.int32) samples_hue = ((samples.astype(np.float32) / 255.0) * (360/2)).astype(np.int32) per_channel = self.per_channel.draw_samples((nb_images,), random_state=rss[2]) rs_inv = random_state ia.do_assert(-255 <= samples[0, 0] <= 255) # this is needed if no cache for LUT is used: # value_range = np.arange(0, 256, dtype=np.int16) gen = enumerate(zip(images_hsv, samples, samples_hue, per_channel)) for i, (image_hsv, samples_i, samples_hue_i, per_channel_i) in gen: assert image_hsv.dtype.name == "uint8" sample_saturation = samples_i[0] if per_channel_i > 0.5: sample_hue = samples_hue_i[1] else: sample_hue = samples_hue_i[0] if self.backend == "cv2": # this has roughly the same speed as the numpy backend for 64x64 and is about 25% faster for 224x224 # code without using cache: # table_hue = np.mod(value_range + sample_hue, 180) # table_saturation = np.clip(value_range + sample_saturation, 0, 255) # table_hue = table_hue.astype(np.uint8, copy=False) # table_saturation = table_saturation.astype(np.uint8, copy=False) # image_hsv[..., 0] = cv2.LUT(image_hsv[..., 0], table_hue) # image_hsv[..., 1] = cv2.LUT(image_hsv[..., 1], table_saturation) # code with using cache (at best maybe 10% faster for 64x64): image_hsv[..., 0] = cv2.LUT(image_hsv[..., 0], self._LUT_CACHE[0][int(sample_hue)]) image_hsv[..., 1] = cv2.LUT(image_hsv[..., 1], self._LUT_CACHE[1][int(sample_saturation)]) else: image_hsv = image_hsv.astype(np.int16) # int16 seems to be slightly faster than int32 # np.mod() works also as required here for negative values image_hsv[..., 0] = np.mod(image_hsv[..., 0] + sample_hue, 180) image_hsv[..., 1] = np.clip(image_hsv[..., 1] + sample_saturation, 0, 255) image_hsv = image_hsv.astype(input_dtypes[i]) # the inverse colorspace changer has a deterministic output (always <from_colorspace>, so that can # always provide it the same random state as input image_rgb = self.colorspace_changer_inv._augment_images([image_hsv], rs_inv, parents + [self], hooks)[0] result[i] = image_rgb return result
def _augment_images(self, images, random_state, parents, hooks): input_dtypes = iadt.copy_dtypes_for_restore(images, force_list=True) result = images nb_images = len(images) # surprisingly, placing this here seems to be slightly slower than placing it inside the loop # if isinstance(images_hsv, list): # images_hsv = [img.astype(np.int32) for img in images_hsv] # else: # images_hsv = images_hsv.astype(np.int32) rss = ia.derive_random_states(random_state, 3) images_hsv = self.colorspace_changer._augment_images( images, rss[0], parents + [self], hooks) samples = self.value.draw_samples((nb_images, 2), random_state=rss[1]).astype(np.int32) samples_hue = ((samples.astype(np.float32) / 255.0) * (360 / 2)).astype(np.int32) per_channel = self.per_channel.draw_samples((nb_images, ), random_state=rss[2]) rs_inv = random_state ia.do_assert(-255 <= samples[0, 0] <= 255) # this is needed if no cache for LUT is used: # value_range = np.arange(0, 256, dtype=np.int16) gen = enumerate(zip(images_hsv, samples, samples_hue, per_channel)) for i, (image_hsv, samples_i, samples_hue_i, per_channel_i) in gen: assert image_hsv.dtype.name == "uint8" sample_saturation = samples_i[0] if per_channel_i > 0.5: sample_hue = samples_hue_i[1] else: sample_hue = samples_hue_i[0] if self.backend == "cv2": # this has roughly the same speed as the numpy backend for 64x64 and is about 25% faster for 224x224 # code without using cache: # table_hue = np.mod(value_range + sample_hue, 180) # table_saturation = np.clip(value_range + sample_saturation, 0, 255) # table_hue = table_hue.astype(np.uint8, copy=False) # table_saturation = table_saturation.astype(np.uint8, copy=False) # image_hsv[..., 0] = cv2.LUT(image_hsv[..., 0], table_hue) # image_hsv[..., 1] = cv2.LUT(image_hsv[..., 1], table_saturation) # code with using cache (at best maybe 10% faster for 64x64): image_hsv[..., 0] = cv2.LUT(image_hsv[..., 0], self._LUT_CACHE[0][int(sample_hue)]) image_hsv[..., 1] = cv2.LUT( image_hsv[..., 1], self._LUT_CACHE[1][int(sample_saturation)]) else: image_hsv = image_hsv.astype( np.int16) # int16 seems to be slightly faster than int32 # np.mod() works also as required here for negative values image_hsv[..., 0] = np.mod(image_hsv[..., 0] + sample_hue, 180) image_hsv[..., 1] = np.clip(image_hsv[..., 1] + sample_saturation, 0, 255) image_hsv = image_hsv.astype(input_dtypes[i]) # the inverse colorspace changer has a deterministic output (always <from_colorspace>, so that can # always provide it the same random state as input image_rgb = self.colorspace_changer_inv._augment_images( [image_hsv], rs_inv, parents + [self], hooks)[0] result[i] = image_rgb return result