예제 #1
0
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
예제 #2
0
    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
예제 #3
0
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,
    }
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
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')