cytometer/utils.py
def colour_labels_with_receptive_field(labels, receptive_field):
    """
    Take a segmentation where each object has a different label, and colour them with a distance constraint:

    Let c(i) be the center of mass of label i that we have assigned colour k. If we draw a rectangle of size
    receptive_field around c(i), the only object with colour k within the rectangle is i.

    :param labels: 2D np.ndarray that describes a segmentation. All pixels that correspond to the same object have
    the same label.
    :param receptive_field: (width, height) tuple with the size of the receptive field in pixels. If
    receptive_field is a scalar, then it's used for both the width and height.
    :return: colours, coloured_labels:

    colours is a dictionary with pairs {label: colour}.
    coloured_labels: np.ndarray of the same size as labels, with the labels replaced by colours.
    """

    # receptive_field = (width/cols, height/rows)
    if np.isscalar(receptive_field):
        receptive_field = (receptive_field, receptive_field)
    if not isinstance(receptive_field, tuple):
        raise TypeError('receptive_field must be a scalar or a tuple')

    # constant for clearer nomenclature
    no_colour = 0
    background = 0

    # make receptive_field (height/rows, width/cols)
    receptive_field = receptive_field[::-1]

    # half-sizes of the receptive field rectangle
    if receptive_field[0] % 2:
        receptive_field_half = ((receptive_field[0] - 1) / 2,)
    else:
        receptive_field_half = (receptive_field[0] / 2,)
    if receptive_field[1] % 2:
        receptive_field_half += ((receptive_field[1] - 1) / 2,)
    else:
        receptive_field_half += (receptive_field[1] / 2, )

    # compute the center of mass for each labelled region. E.g. centroids_rc[53, :] is the centroid for label 53.
    # Note: if the centroid is in the center of pixel (row=3, col=6), regionprops gives the centroid as
    # index (3.0, 6.0), instead of the physical centre (3.5, 6.5)
    labels_prop = regionprops(labels, coordinates='rc')
    centroids_rc = {}
    for lp in labels_prop:
        centroids_rc[lp['label']] = lp['centroid']

    # compute Region Adjacency Graph (RAG) for labels. Note that we don't care about the mean colour difference
    # between regions. We only care about whether labels are adjacent to others or not
    rag = rag_mean_color(image=labels, labels=labels)

    # node 0 corresponds to the background, and it's ignored in terms of colouring
    if rag.has_node(background):
        rag.remove_node(background)

    # initially, we colour all nodes as 0 (no colour)
    nx.set_node_attributes(rag, no_colour, 'colour')

    # initialize list of subset nodes that can be considered for colouring.
    # we start with only the first node in the list of nodes in the graph
    v_candidates = set([list(rag)[0]])

    while len(v_candidates) > 0:  # iterate the algorithm until all nodes are labelled

        # we are going to colour the first candidate in the list
        v = v_candidates.pop()

        # add uncoloured neighbours to the list of candidates for the next iteration
        for x in rag[v]:
            if rag.nodes[x]['colour'] == no_colour:
                v_candidates.add(x)

        # (row, col) coordinates of the current vertex
        c = centroids_rc[v]

        # rectangle corners with receptive_field size around current node. Note that if the rectangle goes
        # outside of the image, we have to crop it to avoid an index error
        rmin = int(max(0.0, np.round(c[0] - receptive_field_half[0])))
        rmax = int(min(labels.shape[0] - 1.0, np.round(c[0] + receptive_field_half[0])))
        cmin = int(max(0.0, np.round(c[1] - receptive_field_half[1])))
        cmax = int(min(labels.shape[1] - 1.0, np.round(c[1] + receptive_field_half[1])))

        # extract labels that will interfere with the current vertex at training time
        v_interference = np.unique(labels[rmin:rmax+1, cmin:cmax+1])

        # ignore background pixels (label=0)
        v_interference = v_interference[v_interference != background]

        # get the colours of the interfering labels (if they have any)
        colour_interference = nx.get_node_attributes(rag, 'colour')
        colour_interference = [colour_interference[x] for x in v_interference]

        # remove no_colour from the list of colours
        colour_interference = np.array(colour_interference)
        colour_interference = colour_interference[colour_interference != no_colour]

        # assign the lowest possible colour given the existing colours
        if len(colour_interference) == 0:  # no colours assigned to neighbours
            nx.set_node_attributes(rag, {v: 1}, 'colour')
        else:
            # list of reusable colours between 1 and the highest colour
            max_colour = np.max(colour_interference)
            recyclable_colours = list(set(range(1, max_colour + 1)) - set(colour_interference))

            if len(recyclable_colours) == 0:
                # no recyclable colours, so we have to create a new one
                nx.set_node_attributes(rag, {v: max_colour + 1}, 'colour')
            else:
                # we recycle the lowest colour index for the current node
                nx.set_node_attributes(rag, {v: np.min(recyclable_colours)}, 'colour')

        if DEBUG:
            centroids_xy = {}
            for n in rag.nodes():
                centroids_xy[n] = centroids_rc[n][::-1]

            plt.clf()
            plt.subplot(221)
            plt.imshow(labels)
            plt.title('labels')
            plt.subplot(222)
            plt.imshow(labels)
            nx.draw(rag, pos=centroids_xy, node_size=30)
            plt.title('label adjacency graph')
            plt.subplot(223)
            plt.imshow(labels)
            plt.plot(c[1], c[0], 'or')
            plt.plot([cmin, cmax, cmax, cmin, cmin], [rmin, rmin, rmax, rmax, rmin], 'r')
            plt.title('receptive field')

    # list of coloured nodes
    colours = nx.get_node_attributes(rag, 'colour')

    # transfer colours to labels image
    coloured_labels = labels.copy()
    for lab in colours.keys():
        coloured_labels[labels == lab] = colours[lab]

    if DEBUG:
        plt.clf()
        plt.subplot(221)
        plt.imshow(labels)
        plt.title('labels')
        plt.subplot(222)
        plt.imshow(labels)
        nx.draw(rag, pos=centroids_xy, node_size=30)
        plt.title('label adjacency graph')
        plt.subplot(223)
        plt.imshow(coloured_labels, cmap='tab20')
        c = centroids_rc[38]
        plt.plot(c[1], c[0], 'ok')
        rmin = int(max(0.0, np.round(c[0] - receptive_field_half[0])))
        rmax = int(min(labels.shape[0] - 1.0, np.round(c[0] + receptive_field_half[0])))
        cmin = int(max(0.0, np.round(c[1] - receptive_field_half[1])))
        cmax = int(min(labels.shape[1] - 1.0, np.round(c[1] + receptive_field_half[1])))
        plt.plot([cmin, cmax, cmax, cmin, cmin], [rmin, rmin, rmax, rmax, rmin], 'k')
        plt.title('coloured labels')

    return colours, coloured_labels

    Take a segmentation where each object has a different label, and colour them with a distance constraint:

    Let c(i) be the center of mass of label i that we have assigned colour k. If we draw a rectangle of size
    receptive_field around c(i), the only object with colour k within the rectangle is i.

    :param labels: 2D np.ndarray that describes a segmentation. All pixels that correspond to the same object have
    the same label.
    :param receptive_field: (width, height) tuple with the size of the receptive field in pixels. If
    receptive_field is a scalar, then it's used for both the width and height.
    :return: colours, coloured_labels:

    colours is a dictionary with pairs {label: colour}.
    coloured_labels: np.ndarray of the same size as labels, with the labels replaced by colours.
    
Take a segmentation where each object has a different label, and colour them with a distance constraint:
Let c(i) be the center of mass of label i that we have assigned colour k. If we draw a rectangle of size
receptive_field around c(i), the only object with colour k within the rectangle is i.
Take a segmentation where each object has a different label and colour them with a distance constraint : Let c ( i ) be the center of mass of label i that we have assigned colour k . If we draw a rectangle of size receptive_field around c ( i ) the only object with colour k within the rectangle is i .
def colour_labels_with_receptive_field(labels, receptive_field):
    if np.isscalar(receptive_field):
        receptive_field = (receptive_field, receptive_field)
    if not isinstance(receptive_field, tuple):
        raise TypeError('receptive_field must be a scalar or a tuple')
    no_colour = 0
    background = 0
    receptive_field = receptive_field[::-1]
    if receptive_field[0] % 2:
        receptive_field_half = ((receptive_field[0] - 1) / 2,)
    else:
        receptive_field_half = (receptive_field[0] / 2,)
    if receptive_field[1] % 2:
        receptive_field_half += ((receptive_field[1] - 1) / 2,)
    else:
        receptive_field_half += (receptive_field[1] / 2, )
    labels_prop = regionprops(labels, coordinates='rc')
    centroids_rc = {}
    for lp in labels_prop:
        centroids_rc[lp['label']] = lp['centroid']
    rag = rag_mean_color(image=labels, labels=labels)
    if rag.has_node(background):
        rag.remove_node(background)
    nx.set_node_attributes(rag, no_colour, 'colour')
    v_candidates = set([list(rag)[0]])
    while len(v_candidates) > 0:  
        v = v_candidates.pop()
        for x in rag[v]:
            if rag.nodes[x]['colour'] == no_colour:
                v_candidates.add(x)
        c = centroids_rc[v]
        rmin = int(max(0.0, np.round(c[0] - receptive_field_half[0])))
        rmax = int(min(labels.shape[0] - 1.0, np.round(c[0] + receptive_field_half[0])))
        cmin = int(max(0.0, np.round(c[1] - receptive_field_half[1])))
        cmax = int(min(labels.shape[1] - 1.0, np.round(c[1] + receptive_field_half[1])))
        v_interference = np.unique(labels[rmin:rmax+1, cmin:cmax+1])
        v_interference = v_interference[v_interference != background]
        colour_interference = nx.get_node_attributes(rag, 'colour')
        colour_interference = [colour_interference[x] for x in v_interference]
        colour_interference = np.array(colour_interference)
        colour_interference = colour_interference[colour_interference != no_colour]
        if len(colour_interference) == 0:  
            nx.set_node_attributes(rag, {v: 1}, 'colour')
        else:
            max_colour = np.max(colour_interference)
            recyclable_colours = list(set(range(1, max_colour + 1)) - set(colour_interference))
            if len(recyclable_colours) == 0:
                nx.set_node_attributes(rag, {v: max_colour + 1}, 'colour')
            else:
                nx.set_node_attributes(rag, {v: np.min(recyclable_colours)}, 'colour')
        if DEBUG:
            centroids_xy = {}
            for n in rag.nodes():
                centroids_xy[n] = centroids_rc[n][::-1]
            plt.clf()
            plt.subplot(221)
            plt.imshow(labels)
            plt.title('labels')
            plt.subplot(222)
            plt.imshow(labels)
            nx.draw(rag, pos=centroids_xy, node_size=30)
            plt.title('label adjacency graph')
            plt.subplot(223)
            plt.imshow(labels)
            plt.plot(c[1], c[0], 'or')
            plt.plot([cmin, cmax, cmax, cmin, cmin], [rmin, rmin, rmax, rmax, rmin], 'r')
            plt.title('receptive field')
    colours = nx.get_node_attributes(rag, 'colour')
    coloured_labels = labels.copy()
    for lab in colours.keys():
        coloured_labels[labels == lab] = colours[lab]
    if DEBUG:
        plt.clf()
        plt.subplot(221)
        plt.imshow(labels)
        plt.title('labels')
        plt.subplot(222)
        plt.imshow(labels)
        nx.draw(rag, pos=centroids_xy, node_size=30)
        plt.title('label adjacency graph')
        plt.subplot(223)
        plt.imshow(coloured_labels, cmap='tab20')
        c = centroids_rc[38]
        plt.plot(c[1], c[0], 'ok')
        rmin = int(max(0.0, np.round(c[0] - receptive_field_half[0])))
        rmax = int(min(labels.shape[0] - 1.0, np.round(c[0] + receptive_field_half[0])))
        cmin = int(max(0.0, np.round(c[1] - receptive_field_half[1])))
        cmax = int(min(labels.shape[1] - 1.0, np.round(c[1] + receptive_field_half[1])))
        plt.plot([cmin, cmax, cmax, cmin, cmin], [rmin, rmin, rmax, rmax, rmin], 'k')
        plt.title('coloured labels')
    return colours, coloured_labels
def colour_labels_with_receptive_field ( labels , receptive_field ) : if np . isscalar ( receptive_field ) : receptive_field = ( receptive_field , receptive_field ) if not isinstance ( receptive_field , tuple ) : raise TypeError ( 'receptive_field must be a scalar or a tuple' ) no_colour = 0 background = 0 receptive_field = receptive_field [ : : - 1 ] if receptive_field [ 0 ] % 2 : receptive_field_half = ( ( receptive_field [ 0 ] - 1 ) / 2 , ) else : receptive_field_half = ( receptive_field [ 0 ] / 2 , ) if receptive_field [ 1 ] % 2 : receptive_field_half += ( ( receptive_field [ 1 ] - 1 ) / 2 , ) else : receptive_field_half += ( receptive_field [ 1 ] / 2 , ) labels_prop = regionprops ( labels , coordinates = 'rc' ) centroids_rc = { } for lp in labels_prop : centroids_rc [ lp [ 'label' ] ] = lp [ 'centroid' ] rag = rag_mean_color ( image = labels , labels = labels ) if rag . has_node ( background ) : rag . remove_node ( background ) nx . set_node_attributes ( rag , no_colour , 'colour' ) v_candidates = set ( [ list ( rag ) [ 0 ] ] ) while len ( v_candidates ) > 0 : v = v_candidates . pop ( ) for x in rag [ v ] : if rag . nodes [ x ] [ 'colour' ] == no_colour : v_candidates . add ( x ) c = centroids_rc [ v ] rmin = int ( max ( 0.0 , np . round ( c [ 0 ] - receptive_field_half [ 0 ] ) ) ) rmax = int ( min ( labels . shape [ 0 ] - 1.0 , np . round ( c [ 0 ] + receptive_field_half [ 0 ] ) ) ) cmin = int ( max ( 0.0 , np . round ( c [ 1 ] - receptive_field_half [ 1 ] ) ) ) cmax = int ( min ( labels . shape [ 1 ] - 1.0 , np . round ( c [ 1 ] + receptive_field_half [ 1 ] ) ) ) v_interference = np . unique ( labels [ rmin : rmax + 1 , cmin : cmax + 1 ] ) v_interference = v_interference [ v_interference != background ] colour_interference = nx . get_node_attributes ( rag , 'colour' ) colour_interference = [ colour_interference [ x ] for x in v_interference ] colour_interference = np . array ( colour_interference ) colour_interference = colour_interference [ colour_interference != no_colour ] if len ( colour_interference ) == 0 : nx . set_node_attributes ( rag , { v : 1 } , 'colour' ) else : max_colour = np . max ( colour_interference ) recyclable_colours = list ( set ( range ( 1 , max_colour + 1 ) ) - set ( colour_interference ) ) if len ( recyclable_colours ) == 0 : nx . set_node_attributes ( rag , { v : max_colour + 1 } , 'colour' ) else : nx . set_node_attributes ( rag , { v : np . min ( recyclable_colours ) } , 'colour' ) if DEBUG : centroids_xy = { } for n in rag . nodes ( ) : centroids_xy [ n ] = centroids_rc [ n ] [ : : - 1 ] plt . clf ( ) plt . subplot ( 221 ) plt . imshow ( labels ) plt . title ( 'labels' ) plt . subplot ( 222 ) plt . imshow ( labels ) nx . draw ( rag , pos = centroids_xy , node_size = 30 ) plt . title ( 'label adjacency graph' ) plt . subplot ( 223 ) plt . imshow ( labels ) plt . plot ( c [ 1 ] , c [ 0 ] , 'or' ) plt . plot ( [ cmin , cmax , cmax , cmin , cmin ] , [ rmin , rmin , rmax , rmax , rmin ] , 'r' ) plt . title ( 'receptive field' ) colours = nx . get_node_attributes ( rag , 'colour' ) coloured_labels = labels . copy ( ) for lab in colours . keys ( ) : coloured_labels [ labels == lab ] = colours [ lab ] if DEBUG : plt . clf ( ) plt . subplot ( 221 ) plt . imshow ( labels ) plt . title ( 'labels' ) plt . subplot ( 222 ) plt . imshow ( labels ) nx . draw ( rag , pos = centroids_xy , node_size = 30 ) plt . title ( 'label adjacency graph' ) plt . subplot ( 223 ) plt . imshow ( coloured_labels , cmap = 'tab20' ) c = centroids_rc [ 38 ] plt . plot ( c [ 1 ] , c [ 0 ] , 'ok' ) rmin = int ( max ( 0.0 , np . round ( c [ 0 ] - receptive_field_half [ 0 ] ) ) ) rmax = int ( min ( labels . shape [ 0 ] - 1.0 , np . round ( c [ 0 ] + receptive_field_half [ 0 ] ) ) ) cmin = int ( max ( 0.0 , np . round ( c [ 1 ] - receptive_field_half [ 1 ] ) ) ) cmax = int ( min ( labels . shape [ 1 ] - 1.0 , np . round ( c [ 1 ] + receptive_field_half [ 1 ] ) ) ) plt . plot ( [ cmin , cmax , cmax , cmin , cmin ] , [ rmin , rmin , rmax , rmax , rmin ] , 'k' ) plt . title ( 'coloured labels' ) return colours , coloured_labels
Take a segmentation where each object has a different label, and colour them with a distance constraint:
Let c(i) be the center of mass of label i that we have assigned colour k. If we draw a rectangle of size
receptive_field around c(i), the only object with colour k within the rectangle is i.
Take a segmentation where each object has a different label and colour them with a distance constraint : Let c ( i ) be the center of mass of label i that we have assigned colour k . If we draw a rectangle of size receptive_field around c ( i ) the only object with colour k within the rectangle is i .
"""
    Take a segmentation where each object has a different label, and colour them with a distance constraint:

    Let c(i) be the center of mass of label i that we have assigned colour k. If we draw a rectangle of size
    receptive_field around c(i), the only object with colour k within the rectangle is i.

    :param labels: 2D np.ndarray that describes a segmentation. All pixels that correspond to the same object have
    the same label.
    :param receptive_field: (width, height) tuple with the size of the receptive field in pixels. If
    receptive_field is a scalar, then it's used for both the width and height.
    :return: colours, coloured_labels:

    colours is a dictionary with pairs {label: colour}.
    coloured_labels: np.ndarray of the same size as labels, with the labels replaced by colours.
    """ # receptive_field = (width/cols, height/rows) # constant for clearer nomenclature # make receptive_field (height/rows, width/cols) # half-sizes of the receptive field rectangle # compute the center of mass for each labelled region. E.g. centroids_rc[53, :] is the centroid for label 53. # Note: if the centroid is in the center of pixel (row=3, col=6), regionprops gives the centroid as # index (3.0, 6.0), instead of the physical centre (3.5, 6.5) # compute Region Adjacency Graph (RAG) for labels. Note that we don't care about the mean colour difference # between regions. We only care about whether labels are adjacent to others or not # node 0 corresponds to the background, and it's ignored in terms of colouring # initially, we colour all nodes as 0 (no colour) # initialize list of subset nodes that can be considered for colouring. # we start with only the first node in the list of nodes in the graph # iterate the algorithm until all nodes are labelled # we are going to colour the first candidate in the list # add uncoloured neighbours to the list of candidates for the next iteration # (row, col) coordinates of the current vertex # rectangle corners with receptive_field size around current node. Note that if the rectangle goes # outside of the image, we have to crop it to avoid an index error # extract labels that will interfere with the current vertex at training time # ignore background pixels (label=0) # get the colours of the interfering labels (if they have any) # remove no_colour from the list of colours # assign the lowest possible colour given the existing colours # no colours assigned to neighbours # list of reusable colours between 1 and the highest colour # no recyclable colours, so we have to create a new one # we recycle the lowest colour index for the current node # list of coloured nodes # transfer colours to labels image
colours, coloured_labels:
colours is a dictionary with pairs {label: colour}.
coloured_labels: np.ndarray of the same size as labels, with the labels replaced by colours.
colours coloured_labels : colours is a dictionary with pairs { label : colour } . coloured_labels : np . ndarray of the same size as labels with the labels replaced by colours .
labels
2D np.ndarray that describes a segmentation. All pixels that correspond to the same object have
the same label.
2D np . ndarray that describes a segmentation . All pixels that correspond to the same object have the same label .
receptive_field
(width, height) tuple with the size of the receptive field in pixels. If
receptive_field is a scalar, then it's used for both the width and height.
( width height ) tuple with the size of the receptive field in pixels . If receptive_field is a scalar then it ' s used for both the width and height .