def test_tile_image_3D(): shapes = [ (3, 5, 21, 21, 1), (1, 10, 21, 31, 2), (1, 15, 31, 21, 1), ] model_input_shapes = [(4, 3, 4), (3, 5, 5), (3, 7, 7), (5, 12, 15)] stride_ratios = [0.25, 0.33, 0.5, 0.66, 0.75, 0.8, 1] dtypes = ['int32', 'float32', 'uint16', 'float16'] prod = product(shapes, model_input_shapes, stride_ratios, dtypes) for shape, input_shape, stride_ratio, dtype in prod: big_image = (np.random.random(shape) * 100).astype(dtype) tiles, tiles_info = utils.tile_image_3D(big_image, input_shape, stride_ratio=stride_ratio) assert tiles.shape[1:] == input_shape + (shape[-1], ) assert tiles.dtype == big_image.dtype ceil = lambda x: int(np.ceil(x)) round_to_even = lambda x: int(np.ceil(x / 2.0) * 2) image_size_z, image_size_x, image_size_y = big_image.shape[1:4] tile_size_z = input_shape[0] tile_size_x = input_shape[1] tile_size_y = input_shape[2] stride_x = round_to_even(stride_ratio * tile_size_x) stride_y = round_to_even(stride_ratio * tile_size_y) stride_z = round_to_even(stride_ratio * tile_size_z) if stride_z > tile_size_z: stride_z = tile_size_z if stride_x > tile_size_x: stride_x = tile_size_x if stride_y > tile_size_y: stride_y = tile_size_y rep_number_x = ceil((image_size_x - tile_size_x) / stride_x + 1) rep_number_y = ceil((image_size_y - tile_size_y) / stride_y + 1) rep_number_z = ceil((image_size_z - tile_size_z) / stride_z + 1) expected_batches = big_image.shape[ 0] * rep_number_x * rep_number_y * rep_number_z assert tiles.shape[0] == expected_batches # test bad input shape bad_shape = (21, 21, 1) bad_image = (np.random.random(bad_shape) * 100) with pytest.raises(ValueError): utils.tile_image(bad_image, (5, 5), stride_ratio=0.75)
def reshape_training_data(X_data, y_data, resize_ratio, final_size, stride_ratio=1, tolerance=1.5): """Takes a stack of X and y data and reshapes and crops them to match output dimensions Args: X_data: 4D numpy array of image data y_data: 4D numpy array of labeled data resize_ratio: resize ratio for the images final_size: the desired shape of the output image stride_ratio: amount of overlap between crops (1 is no overlap, 0.5 is half crop size) tolerance: ratio that determines when resizing occurs Returns: reshaped_X, reshaped_y: resized and cropped version of input images Raises: ValueError: If image data is not 4D """ if len(X_data.shape) != 4: raise ValueError('Image data must be 4D') # resize if needed if resize_ratio > tolerance or resize_ratio < (1 / tolerance): new_shape = (int(X_data.shape[1] * resize_ratio), int(X_data.shape[2] * resize_ratio)) X_data = resize(data=X_data, shape=new_shape) y_data = resize(data=y_data, shape=new_shape, labeled_image=True) # crop if needed if X_data.shape[1:3] != final_size: # pad image so that crops divide evenly X_data = pad_image_stack(images=X_data, crop_size=final_size) y_data = pad_image_stack(images=y_data, crop_size=final_size) # create x and y crops X_data, _ = tile_image(image=X_data, model_input_shape=final_size, stride_ratio=stride_ratio) y_data, _ = tile_image(image=y_data, model_input_shape=final_size, stride_ratio=stride_ratio) return X_data, y_data
def test_untile_image(): shapes = [(3, 8, 16, 2), (1, 64, 64, 1), (1, 41, 58, 1), (1, 93, 61, 1)] rand_rel_diff_thresh = 2e-2 model_input_shapes = [(16, 20), (32, 32), (41, 51), (64, 64), (100, 90)] stride_ratios = [0.33, 0.5, 0.51, 0.66, 0.75, 1] dtypes = ['int32', 'float32', 'uint16', 'float16'] prod = product(shapes, model_input_shapes, stride_ratios, dtypes) # Test that randomly generated arrays are unchanged within a moderate tolerance for shape, input_shape, stride_ratio, dtype in prod: big_image = (np.random.random(shape) * 100).astype(dtype) tiles, tiles_info = utils.tile_image(big_image, model_input_shape=input_shape, stride_ratio=stride_ratio) untiled_image = utils.untile_image(tiles, tiles_info) assert untiled_image.dtype == dtype assert untiled_image.shape == shape np.testing.assert_allclose(big_image, untiled_image, rand_rel_diff_thresh) # Test that constant arrays are unchanged by tile/untile for shape, input_shape, stride_ratio, dtype in prod: for x in [0, 1, np.random.randint(2, 99)]: big_image = np.empty(shape).astype(dtype).fill(x) tiles, tiles_info = utils.tile_image(big_image, model_input_shape=input_shape, stride_ratio=stride_ratio) untiled_image = utils.untile_image(tiles, tiles_info) assert untiled_image.dtype == dtype assert untiled_image.shape == shape np.testing.assert_equal(big_image, untiled_image) # test that a stride_fraction of 0 raises an error with pytest.raises(ValueError): big_image_test = np.zeros((4, 4)).astype('int32') tiles, tiles_info = utils.tile_image(big_image_test, model_input_shape=(2, 2), stride_ratio=0) untiled_image = utils.untile_image(tiles, tiles_info)
def _tile_input(self, image, pad_mode='constant'): """Tile the input image to match shape expected by model using the ``deepcell_toolbox`` function. Only supports 4D images. Args: image (numpy.array): Input image to tile pad_mode (str): The padding mode, one of "constant" or "reflect". Raises: ValueError: Input images must have only 4 dimensions Returns: (numpy.array, dict): Tuple of tiled image and dict of tiling information. """ if len(image.shape) != 4: raise ValueError( 'deepcell_toolbox.tile_image only supports 4d images.' 'Image submitted for predict has {} dimensions'.format( len(image.shape))) # Check difference between input and model image size x_diff = image.shape[1] - self.model_image_shape[0] y_diff = image.shape[2] - self.model_image_shape[1] # Check if the input is smaller than model image size if x_diff < 0 or y_diff < 0: # Calculate padding x_diff, y_diff = abs(x_diff), abs(y_diff) x_pad = (x_diff // 2, x_diff // 2 + 1) if x_diff % 2 else (x_diff // 2, x_diff // 2) y_pad = (y_diff // 2, y_diff // 2 + 1) if y_diff % 2 else (y_diff // 2, y_diff // 2) tiles = np.pad(image, [(0, 0), x_pad, y_pad, (0, 0)], 'reflect') tiles_info = {'padding': True, 'x_pad': x_pad, 'y_pad': y_pad} # Otherwise tile images larger than model size else: # Tile images, needs 4d tiles, tiles_info = tile_image( image, model_input_shape=self.model_image_shape, stride_ratio=0.75, pad_mode=pad_mode) return tiles, tiles_info
def _tile_input(self, image): """Tile the input image to match shape expected by model using the deepcell_toolbox function. Currently only supports 4d images and otherwise raises an error Args: image (array): Input image to tile Raises: ValueError: Input images must have only 4 dimensions Returns: (array, dict): Tuple of tiled image and dictionary of tiling specs """ if len(image.shape) != 4: raise ValueError( 'deepcell_toolbox.tile_image only supports 4d images.' 'Image submitted for predict has {} dimensions'.format( len(image.shape))) # Check difference between input and model image size x_diff = image.shape[1] - self.model_image_shape[0] y_diff = image.shape[2] - self.model_image_shape[1] # Check if the input is smaller than model image size if x_diff < 0 or y_diff < 0: # Calculate padding x_diff, y_diff = abs(x_diff), abs(y_diff) x_pad = (x_diff // 2, x_diff // 2 + 1) if x_diff % 2 else (x_diff // 2, x_diff // 2) y_pad = (y_diff // 2, y_diff // 2 + 1) if y_diff % 2 else (y_diff // 2, y_diff // 2) tiles = np.pad(image, [(0, 0), x_pad, y_pad, (0, 0)], 'reflect') tiles_info = {'padding': True, 'x_pad': x_pad, 'y_pad': y_pad} # Otherwise tile images larger than model size else: # Tile images, needs 4d tiles, tiles_info = tile_image( image, model_input_shape=self.model_image_shape) return tiles, tiles_info