cytometer/utils.py
def one_image_per_label_v2(vols, resize_to=None, resample=None, bbox_inc=1.0, only_central_label=False,
                           return_bbox=False):
    """
    Crop a squared bounding box around each label in a segmentation array. Optionally, more volumes of the same size
    can be provided and they will be cropped according to the same labels (this is useful if e.g. you want to also crop
    the image the segmentation was computed on).

    Also optionally:

        * The crops can all be scaled to the same size. This is useful to create inputs for a neural network.

        * The labels volume can be masked so that only the label used to compute the bounding box is in the output.

    A typical syntax of this function is

        (labels_crop, im_crop), index, scaling = one_image_per_label_v2((labels, im), resize_to=(401, 401),
                                                                        resample=(Image.NEAREST, Image.LINEAR),
                                                                        bbox_inc=1.0, only_central_label=True)

    :param vols: Input np.arrays to be cropped. At least one needs to be provided. This volume is expected to have size
    (rows, cols) or (n, rows, cols), and contain labels from a segmentation. The cropping bounding boxes will be
    computed from these labels.

    Optionally, a tuple or list of volumes can be provided too, e.g. [labels, vol0, vol1, vol2]. Each of the other
    volumes needs to have the same (n, rows, cols), but can have a different number of channels. For instance,
    labels = (n, rows, cols)
    vol0 = (n, rows, cols, 3)
    vol1 = (n, rows, cols)
    vol2 = (n, rows, cols, 16)

    All volumes will be cropped according to the bounding boxes computed from the first volume.

    :param resize_to: (def None) (rows_to, cols_to) Tuple with the final size of the croppings after resizing.
    :param resample: (def None) List with the resampling method for each volume. By default (None), we use
    Image.NEAREST for all volumes, which is appropriate for labels. For RBG or grayscale images, you can also use
    Image.LINEAR.
    :param bbox_inc: (def 1.0) Increment in size applied to the bounding box.
    :param only_central_label: (def False) If True, delete the labels that are around the central label.
    :param return_bbox: (def False) If True, return the four coordinates of the bounding box in the index_list output
    argument as (r0, c0, rend, cend).

    First, the bounding box is computed as the tightest square that contains the label. Then, this size is increased by
    bbox_inc*100%. For example, bbox_inc=0.2 will increase the size by 20%. By default, bbox_inc=1.0 increases the size
    by 100%, i.e. it doubles it.

    :return:
    * vols_crop: tuple with the cropped windows, e.g. (labels_crop, vol0_crop, vol1_crop, vol2_crop).

        If resize_to=None, each element in the tuple is a list with the different-sized crops.

        If resize_to had some value, e.g. (401, 401), each list has been collapsed into an array, e.g.

            labels = (m, 401, 401)
            vol0 = (m, 401, 401, 3)
            vol1 = (m, 401, 401)
            vol2 = (m, 401, 401, 16)

        where m is the total number of labels found in the first volume.

    * index_list: List of tuples (i, lab), where i is the image index, and lab is the segmentation label of each
    crop. If input return_bbox=True, then (i, lab, r0, c0, rend, cend).

    * scaling_factor_rc_list: List of tuples (sr, sc), where sr, sc are the scaling factor applied to rows and columns.
    """

    # input preprocessing
    if type(vols) == tuple:
        vols = list(vols)
    vols_islist = type(vols) == list  # used at the end to return an array or a list of arrays
    if not vols_islist:
        vols = [vols]  # for code simplicity, we treat an input array as a list with one array
    labels = vols[0]  # pointer to the labels volume, for convenience
    if labels.ndim < 2 or labels.ndim > 3:
        raise ValueError('Input labels array expected to be (row, col) or (n, row, col)')
    if labels.ndim == 2:
        # if labels are (row, col), convert all volumes to (1, row, col, ...)
        for i, vol in enumerate(vols):
            vols[i] = np.expand_dims(vol, axis=0)
    # init empty lists to place crops of vols
    vols_crop = [[] for foo in range(len(vols))]
    if resize_to is not None and resample is None:
        resample = (Image.NEAREST, ) * len(vols)

    # loop labels in each image (we could also use regionprops() and reuse code from one_image_per_label, but this way
    # the code is a lot shorter and clearer)
    index_list = []
    scaling_factor_rc_list = []
    for i in range(labels.shape[0]):
        for lab in np.unique(labels[i, :, :]):

            if lab == 0:
                # skip background label
                continue

            # bounding box for current label: bbox_rc = (r0, c0, rend, cend)
            bbox_rc = bounding_box_with_margin(labels[i, :, :] == lab, inc=bbox_inc, coordinates='rc')

            # compute scaling factor
            if resize_to is None:
                scaling_factor_rc = np.array([1., 1.])
            else:
                # scaling factor for the cropped image: bbox_rc = (r0, c0, rend, cend)
                scaling_factor_rc = np.array(resize_to) / np.array([bbox_rc[2] - bbox_rc[0],
                                                                 bbox_rc[3] - bbox_rc[1]])

            # append results to output
            scaling_factor_rc_list.append(scaling_factor_rc)
            if return_bbox:
                index_list.append((i, lab) + bbox_rc)
            else:
                index_list.append((i, lab))

            # loop vols
            for k, vol in enumerate(vols):

                # crop image with bounding box
                vol_bbox = extract_bbox(vol[i, ...], bbox_rc)

                # mask central label
                if only_central_label and k == 0:
                    vol_bbox = (vol_bbox == lab).astype(vol_bbox.dtype)

                if resize_to is not None:
                    # resize the image to target window size
                    vol_bbox = resize(vol_bbox, size=resize_to, resample=resample[k])

                # append results to output
                vols_crop[k].append(np.expand_dims(vol_bbox, axis=0))

    # if all crops are resized to the same size, they can be collapsed into an array
    if resize_to is not None:
        for k, vol_crop in enumerate(vols_crop):
            vols_crop[k] = np.concatenate(vols_crop[k], axis=0)

    # if the input was a single array, we return an array too, not a list with a single array
    if not vols_islist:
        vols_crop = vols_crop[0]

    # exit function
    return vols_crop, index_list, scaling_factor_rc_list

    Crop a squared bounding box around each label in a segmentation array. Optionally, more volumes of the same size
    can be provided and they will be cropped according to the same labels (this is useful if e.g. you want to also crop
    the image the segmentation was computed on).

    Also optionally:

        * The crops can all be scaled to the same size. This is useful to create inputs for a neural network.

        * The labels volume can be masked so that only the label used to compute the bounding box is in the output.

    A typical syntax of this function is

        (labels_crop, im_crop), index, scaling = one_image_per_label_v2((labels, im), resize_to=(401, 401),
                                                                        resample=(Image.NEAREST, Image.LINEAR),
                                                                        bbox_inc=1.0, only_central_label=True)

    :param vols: Input np.arrays to be cropped. At least one needs to be provided. This volume is expected to have size
    (rows, cols) or (n, rows, cols), and contain labels from a segmentation. The cropping bounding boxes will be
    computed from these labels.

    Optionally, a tuple or list of volumes can be provided too, e.g. [labels, vol0, vol1, vol2]. Each of the other
    volumes needs to have the same (n, rows, cols), but can have a different number of channels. For instance,
    labels = (n, rows, cols)
    vol0 = (n, rows, cols, 3)
    vol1 = (n, rows, cols)
    vol2 = (n, rows, cols, 16)

    All volumes will be cropped according to the bounding boxes computed from the first volume.

    :param resize_to: (def None) (rows_to, cols_to) Tuple with the final size of the croppings after resizing.
    :param resample: (def None) List with the resampling method for each volume. By default (None), we use
    Image.NEAREST for all volumes, which is appropriate for labels. For RBG or grayscale images, you can also use
    Image.LINEAR.
    :param bbox_inc: (def 1.0) Increment in size applied to the bounding box.
    :param only_central_label: (def False) If True, delete the labels that are around the central label.
    :param return_bbox: (def False) If True, return the four coordinates of the bounding box in the index_list output
    argument as (r0, c0, rend, cend).

    First, the bounding box is computed as the tightest square that contains the label. Then, this size is increased by
    bbox_inc*100%. For example, bbox_inc=0.2 will increase the size by 20%. By default, bbox_inc=1.0 increases the size
    by 100%, i.e. it doubles it.

    :return:
    * vols_crop: tuple with the cropped windows, e.g. (labels_crop, vol0_crop, vol1_crop, vol2_crop).

        If resize_to=None, each element in the tuple is a list with the different-sized crops.

        If resize_to had some value, e.g. (401, 401), each list has been collapsed into an array, e.g.

            labels = (m, 401, 401)
            vol0 = (m, 401, 401, 3)
            vol1 = (m, 401, 401)
            vol2 = (m, 401, 401, 16)

        where m is the total number of labels found in the first volume.

    * index_list: List of tuples (i, lab), where i is the image index, and lab is the segmentation label of each
    crop. If input return_bbox=True, then (i, lab, r0, c0, rend, cend).

    * scaling_factor_rc_list: List of tuples (sr, sc), where sr, sc are the scaling factor applied to rows and columns.
    
Crop a squared bounding box around each label in a segmentation array. Optionally, more volumes of the same size
can be provided and they will be cropped according to the same labels (this is useful if e.g. you want to also crop
the image the segmentation was computed on).

Also optionally.

The crops can all be scaled to the same size. This is useful to create inputs for a neural network.

The labels volume can be masked so that only the label used to compute the bounding box is in the output.

A typical syntax of this function is


Crop a squared bounding box around each label in a segmentation array . Optionally more volumes of the same size can be provided and they will be cropped according to the same labels ( this is useful if e . g . you want to also crop the image the segmentation was computed on ) . Also optionally . The crops can all be scaled to the same size . This is useful to create inputs for a neural network . The labels volume can be masked so that only the label used to compute the bounding box is in the output . A typical syntax of this function is
def one_image_per_label_v2(vols, resize_to=None, resample=None, bbox_inc=1.0, only_central_label=False,
                           return_bbox=False):
    if type(vols) == tuple:
        vols = list(vols)
    vols_islist = type(vols) == list  
    if not vols_islist:
        vols = [vols]  
    labels = vols[0]  
    if labels.ndim < 2 or labels.ndim > 3:
        raise ValueError('Input labels array expected to be (row, col) or (n, row, col)')
    if labels.ndim == 2:
        for i, vol in enumerate(vols):
            vols[i] = np.expand_dims(vol, axis=0)
    vols_crop = [[] for foo in range(len(vols))]
    if resize_to is not None and resample is None:
        resample = (Image.NEAREST, ) * len(vols)
    index_list = []
    scaling_factor_rc_list = []
    for i in range(labels.shape[0]):
        for lab in np.unique(labels[i, :, :]):
            if lab == 0:
                continue
            bbox_rc = bounding_box_with_margin(labels[i, :, :] == lab, inc=bbox_inc, coordinates='rc')
            if resize_to is None:
                scaling_factor_rc = np.array([1., 1.])
            else:
                scaling_factor_rc = np.array(resize_to) / np.array([bbox_rc[2] - bbox_rc[0],
                                                                 bbox_rc[3] - bbox_rc[1]])
            scaling_factor_rc_list.append(scaling_factor_rc)
            if return_bbox:
                index_list.append((i, lab) + bbox_rc)
            else:
                index_list.append((i, lab))
            for k, vol in enumerate(vols):
                vol_bbox = extract_bbox(vol[i, ...], bbox_rc)
                if only_central_label and k == 0:
                    vol_bbox = (vol_bbox == lab).astype(vol_bbox.dtype)
                if resize_to is not None:
                    vol_bbox = resize(vol_bbox, size=resize_to, resample=resample[k])
                vols_crop[k].append(np.expand_dims(vol_bbox, axis=0))
    if resize_to is not None:
        for k, vol_crop in enumerate(vols_crop):
            vols_crop[k] = np.concatenate(vols_crop[k], axis=0)
    if not vols_islist:
        vols_crop = vols_crop[0]
    return vols_crop, index_list, scaling_factor_rc_list
def one_image_per_label_v2 ( vols , resize_to = None , resample = None , bbox_inc = 1.0 , only_central_label = False , return_bbox = False ) : if type ( vols ) == tuple : vols = list ( vols ) vols_islist = type ( vols ) == list if not vols_islist : vols = [ vols ] labels = vols [ 0 ] if labels . ndim < 2 or labels . ndim > 3 : raise ValueError ( 'Input labels array expected to be (row, col) or (n, row, col)' ) if labels . ndim == 2 : for i , vol in enumerate ( vols ) : vols [ i ] = np . expand_dims ( vol , axis = 0 ) vols_crop = [ [ ] for foo in range ( len ( vols ) ) ] if resize_to is not None and resample is None : resample = ( Image . NEAREST , ) * len ( vols ) index_list = [ ] scaling_factor_rc_list = [ ] for i in range ( labels . shape [ 0 ] ) : for lab in np . unique ( labels [ i , : , : ] ) : if lab == 0 : continue bbox_rc = bounding_box_with_margin ( labels [ i , : , : ] == lab , inc = bbox_inc , coordinates = 'rc' ) if resize_to is None : scaling_factor_rc = np . array ( [ 1. , 1. ] ) else : scaling_factor_rc = np . array ( resize_to ) / np . array ( [ bbox_rc [ 2 ] - bbox_rc [ 0 ] , bbox_rc [ 3 ] - bbox_rc [ 1 ] ] ) scaling_factor_rc_list . append ( scaling_factor_rc ) if return_bbox : index_list . append ( ( i , lab ) + bbox_rc ) else : index_list . append ( ( i , lab ) ) for k , vol in enumerate ( vols ) : vol_bbox = extract_bbox ( vol [ i , ... ] , bbox_rc ) if only_central_label and k == 0 : vol_bbox = ( vol_bbox == lab ) . astype ( vol_bbox . dtype ) if resize_to is not None : vol_bbox = resize ( vol_bbox , size = resize_to , resample = resample [ k ] ) vols_crop [ k ] . append ( np . expand_dims ( vol_bbox , axis = 0 ) ) if resize_to is not None : for k , vol_crop in enumerate ( vols_crop ) : vols_crop [ k ] = np . concatenate ( vols_crop [ k ] , axis = 0 ) if not vols_islist : vols_crop = vols_crop [ 0 ] return vols_crop , index_list , scaling_factor_rc_list
Crop a squared bounding box around each label in a segmentation array.
Crop a squared bounding box around each label in a segmentation array .
"""
    Crop a squared bounding box around each label in a segmentation array. Optionally, more volumes of the same size
    can be provided and they will be cropped according to the same labels (this is useful if e.g. you want to also crop
    the image the segmentation was computed on).

    Also optionally:

        * The crops can all be scaled to the same size. This is useful to create inputs for a neural network.

        * The labels volume can be masked so that only the label used to compute the bounding box is in the output.

    A typical syntax of this function is

        (labels_crop, im_crop), index, scaling = one_image_per_label_v2((labels, im), resize_to=(401, 401),
                                                                        resample=(Image.NEAREST, Image.LINEAR),
                                                                        bbox_inc=1.0, only_central_label=True)

    :param vols: Input np.arrays to be cropped. At least one needs to be provided. This volume is expected to have size
    (rows, cols) or (n, rows, cols), and contain labels from a segmentation. The cropping bounding boxes will be
    computed from these labels.

    Optionally, a tuple or list of volumes can be provided too, e.g. [labels, vol0, vol1, vol2]. Each of the other
    volumes needs to have the same (n, rows, cols), but can have a different number of channels. For instance,
    labels = (n, rows, cols)
    vol0 = (n, rows, cols, 3)
    vol1 = (n, rows, cols)
    vol2 = (n, rows, cols, 16)

    All volumes will be cropped according to the bounding boxes computed from the first volume.

    :param resize_to: (def None) (rows_to, cols_to) Tuple with the final size of the croppings after resizing.
    :param resample: (def None) List with the resampling method for each volume. By default (None), we use
    Image.NEAREST for all volumes, which is appropriate for labels. For RBG or grayscale images, you can also use
    Image.LINEAR.
    :param bbox_inc: (def 1.0) Increment in size applied to the bounding box.
    :param only_central_label: (def False) If True, delete the labels that are around the central label.
    :param return_bbox: (def False) If True, return the four coordinates of the bounding box in the index_list output
    argument as (r0, c0, rend, cend).

    First, the bounding box is computed as the tightest square that contains the label. Then, this size is increased by
    bbox_inc*100%. For example, bbox_inc=0.2 will increase the size by 20%. By default, bbox_inc=1.0 increases the size
    by 100%, i.e. it doubles it.

    :return:
    * vols_crop: tuple with the cropped windows, e.g. (labels_crop, vol0_crop, vol1_crop, vol2_crop).

        If resize_to=None, each element in the tuple is a list with the different-sized crops.

        If resize_to had some value, e.g. (401, 401), each list has been collapsed into an array, e.g.

            labels = (m, 401, 401)
            vol0 = (m, 401, 401, 3)
            vol1 = (m, 401, 401)
            vol2 = (m, 401, 401, 16)

        where m is the total number of labels found in the first volume.

    * index_list: List of tuples (i, lab), where i is the image index, and lab is the segmentation label of each
    crop. If input return_bbox=True, then (i, lab, r0, c0, rend, cend).

    * scaling_factor_rc_list: List of tuples (sr, sc), where sr, sc are the scaling factor applied to rows and columns.
    """ # input preprocessing # used at the end to return an array or a list of arrays # for code simplicity, we treat an input array as a list with one array # pointer to the labels volume, for convenience # if labels are (row, col), convert all volumes to (1, row, col, ...) # init empty lists to place crops of vols # loop labels in each image (we could also use regionprops() and reuse code from one_image_per_label, but this way # the code is a lot shorter and clearer) # skip background label # bounding box for current label: bbox_rc = (r0, c0, rend, cend) # compute scaling factor # scaling factor for the cropped image: bbox_rc = (r0, c0, rend, cend) # append results to output # loop vols # crop image with bounding box # mask central label # resize the image to target window size # append results to output # if all crops are resized to the same size, they can be collapsed into an array # if the input was a single array, we return an array too, not a list with a single array # exit function
tuple with the cropped windows, e.g.

If resize_to had some value, e.g. (401, 401), each list has been collapsed into an array, e.g.





List of tuples (i, lab), where i is the image index, and lab is the segmentation label of each
crop. If input return_bbox=True, then (i, lab, r0, c0, rend, cend).

List of tuples (sr, sc), where sr, sc are the scaling factor applied to rows and columns.
tuple with the cropped windows e . g . If resize_to had some value e . g . ( 401 401 ) each list has been collapsed into an array e . g . List of tuples ( i lab ) where i is the image index and lab is the segmentation label of each crop . If input return_bbox = True then ( i lab r0 c0 rend cend ) . List of tuples ( sr sc ) where sr sc are the scaling factor applied to rows and columns .
vols
Input np.arrays to be cropped. At least one needs to be provided. This volume is expected to have size
(rows, cols) or (n, rows, cols), and contain labels from a segmentation. The cropping bounding boxes will be
computed from these labels.

Optionally, a tuple or list of volumes can be provided too, e.g. [labels, vol0, vol1, vol2]. Each of the other
volumes needs to have the same (n, rows, cols), but can have a different number of channels. For instance,
labels = (n, rows, cols)
vol0 = (n, rows, cols, 3)
vol1 = (n, rows, cols)
vol2 = (n, rows, cols, 16)

All volumes will be cropped according to the bounding boxes computed from the first volume.
Input np . arrays to be cropped . At least one needs to be provided . This volume is expected to have size ( rows cols ) or ( n rows cols ) and contain labels from a segmentation . The cropping bounding boxes will be computed from these labels . Optionally a tuple or list of volumes can be provided too e . g . [ labels vol0 vol1 vol2 ] . Each of the other volumes needs to have the same ( n rows cols ) but can have a different number of channels . For instance labels = ( n rows cols ) vol0 = ( n rows cols 3 ) vol1 = ( n rows cols ) vol2 = ( n rows cols 16 ) All volumes will be cropped according to the bounding boxes computed from the first volume .
resize_to
(def None) (rows_to, cols_to) Tuple with the final size of the croppings after resizing.
( def None ) ( rows_to cols_to ) Tuple with the final size of the croppings after resizing .
resample
(def None) List with the resampling method for each volume. By default (None), we use
Image.NEAREST for all volumes, which is appropriate for labels. For RBG or grayscale images, you can also use
Image.LINEAR.
( def None ) List with the resampling method for each volume . By default ( None ) we use Image . NEAREST for all volumes which is appropriate for labels . For RBG or grayscale images you can also use Image . LINEAR .
bbox_inc
(def 1.0) Increment in size applied to the bounding box.
( def 1 . 0 ) Increment in size applied to the bounding box .
only_central_label
(def False) If True, delete the labels that are around the central label.
( def False ) If True delete the labels that are around the central label .
return_bbox
(def False) If True, return the four coordinates of the bounding box in the index_list output
argument as (r0, c0, rend, cend).

First, the bounding box is computed as the tightest square that contains the label. Then, this size is increased by
bbox_inc*100%. For example, bbox_inc=0.2 will increase the size by 20%. By default, bbox_inc=1.0 increases the size
by 100%, i.e. it doubles it.
( def False ) If True return the four coordinates of the bounding box in the index_list output argument as ( r0 c0 rend cend ) . First the bounding box is computed as the tightest square that contains the label . Then this size is increased by bbox_inc * 100% . For example bbox_inc = 0 . 2 will increase the size by 20% . By default bbox_inc = 1 . 0 increases the size by 100% i . e . it doubles it .