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 _resize_output(self, image, original_shape): """Rescales input if the shape does not match the original shape excluding the batch and channel dimensions Args: image (array): Image to be rescaled to original shape original_shape (tuple): Shape of the original input image Returns: array: Rescaled image """ # Compare x,y based on rank of image if len(image.shape) == 4: same = image.shape[1:-1] == original_shape[1:-1] elif len(image.shape) == 3: same = image.shape[1:] == original_shape[1:-1] else: same = image.shape == original_shape[1:-1] # Resize if same is false if not same: # Resize function only takes the x,y dimensions for shape new_shape = original_shape[1:-1] image = resize(image, new_shape, data_format='channels_last', labeled_image=True) return image
def get_image_features(X, y, appearance_dim=32): """Return features for every object in the array. Args: X (np.array): a 3D numpy array of raw data of shape (x, y, c). y (np.array): a 3D numpy array of integer labels of shape (x, y, 1). appearance_dim (int): The resized shape of the appearance feature. Returns: dict: A dictionary of feature names to np.arrays of shape (n, c) or (n, x, y, c) where n is the number of objects. """ appearance_dim = int(appearance_dim) # each feature will be ordered based on the label. # labels are also stored and can be fetched by index. num_labels = len(np.unique(y)) - 1 labels = np.zeros((num_labels, ), dtype='int32') centroids = np.zeros((num_labels, 2), dtype='float32') morphologies = np.zeros((num_labels, 3), dtype='float32') appearances = np.zeros( (num_labels, appearance_dim, appearance_dim, X.shape[-1]), dtype='float32') # iterate over all objects in y props = regionprops(y[..., 0], cache=False) for i, prop in enumerate(props): # Get label labels[i] = prop.label # Get centroid centroid = np.array(prop.centroid) centroids[i] = centroid # Get morphology morphology = np.array([prop.area, prop.perimeter, prop.eccentricity]) morphologies[i] = morphology # Get appearance minr, minc, maxr, maxc = prop.bbox appearance = np.copy(X[minr:maxr, minc:maxc, :]) resize_shape = (appearance_dim, appearance_dim) appearance = resize(appearance, resize_shape) appearances[i] = appearance # Get adjacency matrix # distance = cdist(centroids, centroids, metric='euclidean') < distance_threshold # adj_matrix = distance.astype('float32') return { 'appearances': appearances, 'centroids': centroids, 'labels': labels, 'morphologies': morphologies, # 'adj_matrix': adj_matrix, }
def _resize_input(self, image, image_mpp): """Checks if there is a difference between image and model resolution and resizes if they are different. Otherwise returns the unmodified image. Args: image (array): Input image to resize image_mpp (float): Microns per pixel for the input image Returns: array: Input image resized if necessary to match `model_mpp` """ # Don't scale the image if mpp is the same or not defined if image_mpp not in {None, self.model_mpp}: scale_factor = image_mpp / self.model_mpp new_shape = (int(image.shape[1] * scale_factor), int(image.shape[2] * scale_factor)) image = resize(image, new_shape, data_format='channels_last') return image
def _resize_output(self, image, original_shape): """Rescales input if the shape does not match the original shape excluding the batch and channel dimensions. Args: image (numpy.array): Image to be rescaled to original shape original_shape (tuple): Shape of the original input image Returns: numpy.array: Rescaled image """ if not isinstance(image, list): image = [image] for i in range(len(image)): img = image[i] # Compare x,y based on rank of image if len(img.shape) == 4: same = img.shape[1:-1] == original_shape[1:-1] elif len(img.shape) == 3: same = img.shape[1:] == original_shape[1:-1] else: same = img.shape == original_shape[1:-1] # Resize if same is false if not same: # Resize function only takes the x,y dimensions for shape new_shape = original_shape[1:-1] img = resize(img, new_shape, data_format='channels_last', labeled_image=True) image[i] = img if len(image) == 1: image = image[0] return image
def test_resize(): base_shape = (32, 32) out_shapes = [ (40, 40), (42, 40), (40, 42), (24, 24), (16, 24), (24, 16), (17, 37), ] channel_sizes = (1, 3) for out in out_shapes: for c in channel_sizes: # batch, channel first c = tuple([c]) in_shape = c + base_shape + (4, ) out_shape = c + out + (4, ) rs = utils.resize(np.random.rand(*in_shape), out, data_format='channels_first') assert out_shape == rs.shape # batch, channel last in_shape = (4, ) + base_shape + c out_shape = (4, ) + out + c rs = utils.resize(np.random.rand(*in_shape), out, data_format='channels_last') assert out_shape == rs.shape # no batch, channel first in_shape = c + base_shape out_shape = c + out rs = utils.resize(np.random.rand(*in_shape), out, data_format='channels_first') assert out_shape == rs.shape # no batch, channel last in_shape = base_shape + c out_shape = out + c rs = utils.resize(np.random.rand(*in_shape), out, data_format='channels_last') assert out_shape == rs.shape # make sure label data is not linearly interpolated and returns only the same ints # no batch, channel last in_shape = base_shape + c out_shape = out + c in_data = np.random.choice(a=[0, 1, 9, 20], size=in_shape, replace=True) rs = utils.resize(in_data, out, data_format='channels_last', labeled_image=True) assert out_shape == rs.shape assert np.all(rs == np.floor(rs)) assert np.all(np.unique(rs) == [0, 1, 9, 20]) # batch, channel first in_shape = c + base_shape + (4, ) out_shape = c + out + (4, ) in_data = np.random.choice(a=[0, 1, 9, 20], size=in_shape, replace=True) rs = utils.resize(in_data, out, data_format='channels_first', labeled_image=True) assert out_shape == rs.shape assert np.all(rs == np.floor(rs)) assert np.all(np.unique(rs) == [0, 1, 9, 20]) # Wrong data size with pytest.raises(ValueError): im = np.random.rand(20, 20) out_shape = (10, 10) rs = utils.resize(im, out_shape) # Wrong shape with pytest.raises(ValueError): im = np.random.rand(20, 20, 1) out_shape = (10, 10, 1) rs = utils.resize(im, out_shape, data_format='channels_last')