Example #1
0
    def solve_component(component_id):

        # extract the nodes in this component
        sub_nodes = np.where(cc_labels == component_id)[0].astype('uint64')
        # if we only have a single node, return trivial labeling
        if len(sub_nodes) == 1:
            return sub_nodes, np.array([0], dtype='uint64'), 1

        # extract the subgraph corresponding to this component
        inner_edges, _ = graph.extractSubgraphFromNodes(sub_nodes)
        sub_uvs = uv_ids[inner_edges]
        assert len(inner_edges) == len(sub_uvs), "%i, %i" % (len(inner_edges),
                                                             len(sub_uvs))

        # relabel sub-nodes and associated uv-ids
        sub_nodes_relabeled, max_local, node_mapping = relabelConsecutive(
            sub_nodes, start_label=0, keep_zeros=False)
        sub_uvs = nifty.tools.takeDict(node_mapping, sub_uvs)

        # build the graph
        sub_graph = nifty.graph.undirectedGraph(max_local + 1)
        sub_graph.insertEdges(sub_uvs)

        # solve local multicut
        sub_costs = costs[inner_edges]
        assert len(sub_costs) == sub_graph.numberOfEdges, "%i, %i" % (
            len(sub_costs), sub_graph.numberOfEdges)
        sub_labels = solver(sub_graph, sub_costs, time_limit=time_limit)
        # relabel the solution
        sub_labels, max_seg_local, _ = relabelConsecutive(sub_labels,
                                                          start_label=0,
                                                          keep_zeros=False)
        assert len(sub_labels) == len(sub_nodes), "%i, %i" % (len(sub_labels),
                                                              len(sub_nodes))
        return sub_nodes, sub_labels, max_seg_local + 1
Example #2
0
def mutex_watershed(affs, offsets, strides,
                    randomize_strides=False, mask=None,
                    noise_level=0):
    """ Compute mutex watershed segmentation.

    Introduced in "The Mutex Watershed and its Objective: Efficient, Parameter-Free Image Partitioning":
    https://arxiv.org/pdf/1904.12654.pdf

    Arguments:
        affs [np.ndarray] - input affinity map
        offsets [list[list[int]]] - pixel offsets corresponding to affinity channels
        strides [list[int]] - strides used to sub-sample long range edges
        randomize_strides [bool] - randomize the strides? (default: False)
        mask [np.ndarray] - mask to exclude from segmentation (default: None)
        noise_level [float] - sigma of noise added to affinities (default: 0)
    """
    ndim = len(offsets[0])
    if noise_level > 0:
        affs += noise_level * np.random.rand(*affs.shape)
    affs[:ndim] *= -1
    affs[:ndim] += 1
    seg = compute_mws_segmentation(affs, offsets,
                                   number_of_attractive_channels=ndim,
                                   strides=strides, mask=mask,
                                   randomize_strides=randomize_strides)
    relabelConsecutive(seg, out=seg, start_label=1, keep_zeros=mask is not None)
    return seg
Example #3
0
def mutex_watershed_with_seeds(affs, offsets, seeds, strides,
                               randomize_strides=False, mask=None,
                               noise_level=0, return_graph=False,
                               seed_state=None):
    """ Compute mutex watershed segmentation with seeds.

    Introduced in "The Mutex Watershed and its Objective: Efficient, Parameter-Free Image Partitioning":
    https://arxiv.org/pdf/1904.12654.pdf

    Arguments:
        affs [np.ndarray] - input affinity map
        offsets [list[list[int]]] - pixel offsets corresponding to affinity channels
        seeds [np.ndarray] - array with seed points
        strides [list[int]] - strides used to sub-sample long range edges
        randomize_strides [bool] - randomize the strides? (default: False)
        mask [np.ndarray] - mask to exclude from segmentation (default: None)
        noise_level [float] - sigma of noise added to affinities (default: 0)
        seed_state [dict] - seed state (default: None)
    """
    ndim = len(offsets[0])
    if noise_level > 0:
        affs += noise_level * np.random.rand(*affs.shape)
    affs[:ndim] *= -1
    affs[:ndim] += 1

    # compute grid graph with seeds and optional mask
    shape = affs.shape[1:]
    grid_graph = compute_grid_graph(shape, mask, seeds)

    # compute nn and mutex nh
    grid_graph.intra_seed_weight = 1  # set intra-seed weight to maximal attractive
    if seed_state is not None:
        attractive_edges, attractive_weights = seed_state['attractive']
        grid_graph.set_seed_state(attractive_edges, attractive_weights)
    uvs, weights = grid_graph.compute_nh_and_weights(np.require(affs[:ndim], requirements='C'),
                                                     offsets[:ndim])

    grid_graph.intra_seed_weight = 0  # set intral-seed weight to minimal repulsive
    if seed_state is not None:
        repulsive_edges, repulsive_weights = seed_state['repulsive']
        grid_graph.clear_seed_state()
        grid_graph.set_seed_state(repulsive_edges, repulsive_weights)
    mutex_uvs, mutex_weights = grid_graph.compute_nh_and_weights(np.require(affs[ndim:],
                                                                            requirements='C'),
                                                                 offsets[ndim:], strides,
                                                                 randomize_strides)

    # compute the segmentation
    n_nodes = grid_graph.n_nodes
    seg = compute_mws_clustering(n_nodes, uvs, mutex_uvs, weights, mutex_weights)
    relabelConsecutive(seg, out=seg, start_label=1, keep_zeros=mask is not None)
    seg = seg.reshape(shape)
    if mask is not None:
        seg[np.logical_not(mask)] = 0

    if return_graph:
        return seg, grid_graph
    else:
        return seg
def mutex_watershed(affs, offsets, strides,
                    randomize_strides=False, mask=None,
                    noise_level=0):
    assert compute_mws_segmentation is not None, "Need affogato for mutex watershed"
    ndim = len(offsets[0])
    if noise_level > 0:
        affs += noise_level * np.random.rand(*affs.shape)
    affs[:ndim] *= -1
    affs[:ndim] += 1
    seg = compute_mws_segmentation(affs, offsets,
                                   number_of_attractive_channels=ndim,
                                   strides=strides, mask=mask,
                                   randomize_strides=randomize_strides)
    relabelConsecutive(seg, out=seg, start_label=1, keep_zeros=mask is not None)
    return seg
Example #5
0
    def solve_subproblem(block_id):

        # extract nodes from this block
        block = blocking.getBlock(block_id) if halo is None else\
            blocking.getBlockWithHalo(block_id, halo).outerBlock
        bb = tuple(slice(beg, end) for beg, end in zip(block.begin, block.end))
        node_ids = np.unique(segmentation[bb])

        # get the sub-graph corresponding to the nodes
        inner_edges, outer_edges = graph.extractSubgraphFromNodes(node_ids)
        sub_uvs = uv_ids[inner_edges]

        # relabel the sub-nodes and associated uv-ids for more efficient processing
        nodes_relabeled, max_id, mapping = relabelConsecutive(node_ids,
                                                              start_label=0,
                                                              keep_zeros=False)
        sub_uvs = nifty.tools.takeDict(mapping, sub_uvs)
        n_local_nodes = max_id + 1
        sub_graph = nifty.graph.undirectedGraph(n_local_nodes)
        sub_graph.insertEdges(sub_uvs)

        sub_costs = costs[inner_edges]
        assert len(sub_costs) == sub_graph.numberOfEdges

        # solve multicut for the sub-graph
        sub_result = solver(sub_graph, sub_costs)
        assert len(sub_result) == len(node_ids), "%i, %i" % (len(sub_result),
                                                             len(node_ids))

        sub_edgeresult = sub_result[sub_uvs[:, 0]] != sub_result[sub_uvs[:, 1]]
        assert len(sub_edgeresult) == len(inner_edges)
        cut_edge_ids = inner_edges[sub_edgeresult]
        cut_edge_ids = np.concatenate([cut_edge_ids, outer_edges])
        return cut_edge_ids
Example #6
0
    def mws_block(block_id):
        block = blocking.getBlock(block_id)
        bb = tuple(slice(beg, end) for beg, end in zip(block.begin, block.end))

        bb_affs = (slice(None), ) + bb
        affs_ = affs[bb_affs].copy(
        )  # we need to copy here to leave the original affs unchanged
        mask_ = None if mask is None else mask[bb]

        if noise_level > 0:
            affs_ += noise_level * np.random.rand(*affs_.shape)
        affs_[:ndim] *= -1
        affs_[:ndim] += 1
        seg = compute_mws_segmentation(affs_,
                                       offsets,
                                       number_of_attractive_channels=ndim,
                                       strides=strides,
                                       mask=mask_,
                                       randomize_strides=randomize_strides)
        max_id = relabelConsecutive(seg,
                                    out=seg,
                                    start_label=1,
                                    keep_zeros=mask is not None)[1]
        segmentation[bb] = seg
        return max_id
Example #7
0
def _merge_nodes(problem_path, scale, blocking, block_list, nodes, uv_ids,
                 initial_node_labeling, n_threads):
    # load the cut edge ids
    n_edges = len(uv_ids)
    cut_edge_ids = _load_cut_edges(problem_path, scale, blocking, block_list,
                                   n_threads)
    assert len(cut_edge_ids) < n_edges, "%i = %i, does not reduce problem" % (
        len(cut_edge_ids), n_edges)

    merge_edges = np.ones(n_edges, dtype='bool')
    merge_edges[cut_edge_ids] = False
    fu.log('merging %i / %i edges' % (np.sum(merge_edges), n_edges))

    # merge node pairs with ufd
    ufd = nufd.boost_ufd(nodes)
    ufd.merge(uv_ids[merge_edges])

    # get the node results and label them consecutively
    node_labeling = ufd.find(nodes)
    node_labeling, max_new_id, _ = relabelConsecutive(node_labeling,
                                                      start_label=0,
                                                      keep_zeros=False)
    assert node_labeling[0] == 0
    # FIXME this looks fishy, redo !!!
    # # make sure that zero is still mapped to zero
    # if node_labeling[0] != 0:
    #     # if it isn't, swap labels accordingly
    #     zero_label = node_labeling[0]
    #     to_relabel = node_labeling == 0
    #     node_labeling[node_labeling == zero_label] = 0
    #     node_labeling[to_relabel] = zero_laebl
    n_new_nodes = max_new_id + 1
    fu.log("have %i nodes in new node labeling" % n_new_nodes)

    # get the labeling of initial nodes
    if initial_node_labeling is None:
        # if we don't have an initial node labeling, we are in the first scale.
        # here, the graph nodes might not be consecutive / not start at zero.
        # to keep the node labeling valid, we must make the labeling consecutive by inserting zeros
        fu.log("don't have an initial node labeling")

        # check if `nodes` are consecutive and start at zero
        node_max_id = int(nodes.max())
        if node_max_id + 1 != len(nodes):
            fu.log("nodes are not consecutve and/or don't start at zero")
            fu.log("inflating node labels accordingly")
            node_labeling = nt.inflateLabeling(nodes, node_labeling,
                                               node_max_id)

        new_initial_node_labeling = node_labeling
    else:
        fu.log(
            "mapping new node labeling to labeling of inital (= scale 0) nodes"
        )
        # NOTE access like this is ok because all node labelings will be consecutive
        new_initial_node_labeling = node_labeling[initial_node_labeling]
        assert len(new_initial_node_labeling) == len(initial_node_labeling)

    return n_new_nodes, node_labeling, new_initial_node_labeling
def merge_nodes(tmp_folder, scale, block_list, n_nodes, uv_ids,
                initial_node_labeling, n_threads):
    n_edges = len(uv_ids)

    # load the cut-edge ids from the prev. blocks and make merge edge ids
    with futures.ThreadPoolExecutor(n_threads) as tp:
        tasks = [tp.submit(np.load, os.path.join(tmp_folder,
                                                 'subproblem_s%i_%i.npy' % (scale, block_id)))
                 for block_id in block_list]
        res = [t.result() for t in tasks]
        cut_edge_ids = np.concatenate([re for re in res if re.size])
    cut_edge_ids = np.unique(cut_edge_ids).astype('uint64')

    # print("Number of cut edges:", len(cut_edge_ids))
    # print("                   /", n_edges)
    assert len(cut_edge_ids) < n_edges, "%i = %i, does not reduce problem" % (len(cut_edge_ids), n_edges)

    merge_edges = np.ones(n_edges, dtype='bool')
    merge_edges[cut_edge_ids] = False

    # TODO make sure that zero stayes mapped to zero
    # additionally, we make sure that all edges are cut
    ignore_edges = (uv_ids == 0).any(axis=1)
    merge_edges[ignore_edges] = False

    # merge node pairs with ufd
    # FIXME if we process roi's, the number of nodes is underestimated gravely
    n_nodes = int(uv_ids.max()) + 1

    # TODO try relabeling consecutively here to increase processing speed
    # the issue is that we have to do awkward things to get out the complete node
    # labeling again ...
    # uv_ids, n_nodes, mapping = vigra.analysis.relabelConsecutive(uv_ids)
    # inv_mappin = {val: key for key, val in mapping.items()}

    ufd = nifty.ufd.ufd(n_nodes)
    merge_pairs = uv_ids[merge_edges]
    ufd.merge(merge_pairs)

    # get the node results and label them consecutively
    node_labeling = ufd.elementLabeling()
    node_labeling, max_new_id, _ = relabelConsecutive(node_labeling, keep_zeros=True, start_label=1)
    n_new_nodes = max_new_id + 1

    # TODO this is not all we need to do
    # full_node_labeling = np.arange(n_nodes_total, dtype='uint64')
    # node_ids = list(mapping.keys())
    # full_node_labeling[node_ids] = node_labeling

    # get the labeling of initial nodes
    if initial_node_labeling is None:
        new_initial_node_labeling = node_labeling
    else:
        # should this ever become a bottleneck, we can parallelize this in nifty
        # but for now this would really be premature optimization
        new_initial_node_labeling = node_labeling[initial_node_labeling]

    return n_new_nodes, node_labeling, new_initial_node_labeling
def mutex_watershed_with_seeds(affs, offsets, seeds, strides,
                               randomize_strides=False, mask=None,
                               noise_level=0, return_graph=False,
                               seed_state=None):
    assert compute_mws_segmentation is not None, "Need affogato for mutex watershed"
    ndim = len(offsets[0])
    if noise_level > 0:
        affs += noise_level * np.random.rand(*affs.shape)
    affs[:ndim] *= -1
    affs[:ndim] += 1

    # compute grid graph with seeds and optional mask
    shape = affs.shape[1:]
    grid_graph = compute_grid_graph(shape, mask, seeds)

    # compute nn and mutex nh
    grid_graph.intra_seed_weight = 1  # set intra-seed weight to maximal attractive
    if seed_state is not None:
        attractive_edges, attractive_weights = seed_state['attractive']
        grid_graph.set_seed_state(attractive_edges, attractive_weights)
    uvs, weights = grid_graph.compute_nh_and_weights(np.require(affs[:ndim], requirements='C'),
                                                     offsets[:ndim])

    grid_graph.intra_seed_weight = 0  # set intral-seed weight to minimal repulsive
    if seed_state is not None:
        repulsive_edges, repulsive_weights = seed_state['repulsive']
        grid_graph.clear_seed_state()
        grid_graph.set_seed_state(repulsive_edges, repulsive_weights)
    mutex_uvs, mutex_weights = grid_graph.compute_nh_and_weights(np.require(affs[ndim:],
                                                                            requirements='C'),
                                                                 offsets[ndim:], strides, randomize_strides)

    # compute the segmentation
    n_nodes = grid_graph.n_nodes
    seg = compute_mws_clustering(n_nodes, uvs, mutex_uvs, weights, mutex_weights)
    relabelConsecutive(seg, out=seg, start_label=1, keep_zeros=mask is not None)
    seg = seg.reshape(shape)
    if mask is not None:
        seg[np.logical_not(mask)] = 0

    if return_graph:
        return seg, grid_graph
    else:
        return seg
Example #10
0
def _cluster_segmentation_impl(segmentation,
                               input_map,
                               cluster_function,
                               offsets=None,
                               n_threads=None,
                               min_segment_size=0,
                               **cluster_kwargs):
    graph, edge_weights, edge_sizes = compute_graph_and_features(
        segmentation, input_map, offsets=offsets, n_threads=n_threads)
    clusters = cluster_function(graph=graph,
                                edge_features=edge_weights,
                                edge_sizes=edge_sizes,
                                **cluster_kwargs)
    clusters = relabelConsecutive(clusters, start_label=1,
                                  keep_zeros=False)[0].astype('uint32')
    seg = project_node_labels_to_pixels(graph, clusters)
    if min_segment_size > 0:
        inp = input_map if offsets is None else input_map[0]
        seg = apply_size_filter(seg, inp, min_segment_size)[0]
        seg = relabelConsecutive(seg, start_label=1, keep_zeros=False)[0]
    return seg
Example #11
0
def analyse_multicut_problem(graph,
                             costs,
                             verbose=True,
                             cost_threshold=0,
                             topk=5):
    # problem size and cost summary
    n_nodes, n_edges = graph.numberOfNodes, graph.numberOfEdges
    min_cost, max_cost = costs.min(), costs.max()
    mean_cost, std_cost = costs.mean(), costs.std()

    # component analysis
    merge_edges = costs > cost_threshold
    ufd = nufd.ufd(n_nodes)
    uv_ids = graph.uvIds()
    ufd.merge(uv_ids[merge_edges])
    cc_labels = ufd.elementLabeling()
    cc_labels, max_id, _ = relabelConsecutive(cc_labels,
                                              start_label=0,
                                              keep_zeros=False)
    n_components = max_id + 1
    _, component_sizes = np.unique(cc_labels, return_counts=True)
    component_sizes = np.sort(component_sizes)[::-1]
    topk_rel_sizes = component_sizes[:topk].astype("float32") / n_nodes

    # TODO add partial optimality analysis from
    # http://proceedings.mlr.press/v80/lange18a.html
    if verbose:
        print("Analysis of multicut problem:")
        print("The graph has", n_nodes, "nodes and", n_edges, "edges")
        print("The costs are in range", min_cost, "to", max_cost)
        print("The mean cost is", mean_cost, "+-", std_cost)
        print("The problem decomposes into", n_components,
              "components at threshold", cost_threshold)
        print("The 5 largest components have the following sizes:",
              topk_rel_sizes)

    data = [
        n_nodes, n_edges, max_cost, min_cost, mean_cost, std_cost,
        n_components, cost_threshold
    ]
    data.extend(topk_rel_sizes.tolist())
    columns = [
        "n_nodes", "n_edges", "max_cost", "min_cost", "mean_cost", "std_cost",
        "n_components", "cost_threshold"
    ]
    columns.extend([
        f"relative_size_top{i+1}_component" for i in range(len(topk_rel_sizes))
    ])
    df = pd.DataFrame(data=[data], columns=columns)
    return df
Example #12
0
def mws_with_seeds(affs,
                   offsets,
                   seeds,
                   strides,
                   randomize_strides=False,
                   mask=None):
    ndim = len(offsets[0])

    # compute grid graph with seeds and optional mask
    shape = affs.shape[1:]
    grid_graph = compute_grid_graph(shape, mask, seeds)

    # compute nn and mutex nh
    grid_graph.add_attractive_seed_edges = True
    uvs, weights = grid_graph.compute_nh_and_weights(
        1. - np.require(affs[:ndim], requirements='C'), offsets[:ndim])

    grid_graph.add_attractive_seed_edges = False
    mutex_uvs, mutex_weights = grid_graph.compute_nh_and_weights(
        np.require(affs[ndim:], requirements='C'), offsets[ndim:], strides,
        randomize_strides)

    # compute the segmentation
    n_nodes = grid_graph.n_nodes
    seg = compute_mws_clustering(n_nodes, uvs, mutex_uvs, weights,
                                 mutex_weights)
    relabelConsecutive(seg,
                       out=seg,
                       start_label=1,
                       keep_zeros=mask is not None)
    grid_graph.relabel_to_seeds(seg)
    seg = seg.reshape(shape)
    if mask is not None:
        seg[np.logical_not(mask)] = 0

    return seg
def merge_nodes(tmp_folder, scale, n_jobs, n_nodes, uv_ids,
                initial_node_labeling):
    n_edges = len(uv_ids)
    # load the cut-edge ids from the prev. jobs and make merge edge ids
    # TODO we could parallelize this
    cut_edge_ids = np.concatenate([
        np.load(
            os.path.join(tmp_folder, '1_output_s%i_%i.npy' % (scale, job_id)))
        for job_id in range(n_jobs)
    ])
    cut_edge_ids = np.unique(cut_edge_ids).astype('uint64')

    # print("Number of cut edges:", len(cut_edge_ids))
    # print("                   /", n_edges)
    assert len(cut_edge_ids) < n_edges, "%i = %i, does not reduce problem" % (
        len(cut_edge_ids), n_edges)

    merge_edges = np.ones(n_edges, dtype='bool')
    merge_edges[cut_edge_ids] = False

    # TODO make sure that zero stayes mapped to zero
    # additionally, we make sure that all edges are cut
    ignore_edges = (uv_ids == 0).any(axis=1)
    merge_edges[ignore_edges] = False

    # merge node pairs with ufd
    ufd = nufd.ufd(n_nodes)
    merge_pairs = uv_ids[merge_edges]
    ufd.merge(merge_pairs)

    # get the node results and label them consecutively
    node_labeling = ufd.elementLabeling()
    node_labeling, max_new_id, _ = relabelConsecutive(node_labeling)
    n_new_nodes = max_new_id + 1

    # get the labeling of initial nodes
    if initial_node_labeling is None:
        new_initial_node_labeling = node_labeling
    else:
        # should this ever become a bottleneck, we can parallelize this in nifty
        # but for now this would really be premature optimization
        new_initial_node_labeling = node_labeling[initial_node_labeling]

    return n_new_nodes, node_labeling, new_initial_node_labeling
Example #14
0
def update_boutons():
    with h5py.File('./test_data.h5', 'r') as f:
        raw = f['raw'][:]
        boutons = f['boutons'][:]

    with napari.gui_qt():
        viewer = napari.Viewer()
        viewer.add_image(raw)
        viewer.add_labels(boutons)

        boutons = viewer.layers['boutons'].data

    boutons = relabelConsecutive(boutons.astype('uint32'),
                                 start_label=1,
                                 keep_zeros=True)[0]

    with h5py.File('./test_data.h5', 'a') as f:
        ds = f.require_dataset('boutons_corrected',
                               shape=boutons.shape,
                               dtype=boutons.dtype,
                               compression='gzip')
        ds[:] = boutons
Example #15
0
def multicut_decomposition(graph,
                           costs,
                           time_limit=None,
                           n_threads=1,
                           internal_solver='kernighan-lin',
                           **kwargs):
    """ Solve multicut problem with decomposition solver.

    Arguments:
        graph [nifty.graph] - graph of multicut problem
        costs [np.ndarray] - edge costs of multicut problem
        time_limit [float] - time limit for inference (default: None)
        n_threads [int] - number of threads (default: 1)
        internal_solver [str] - name of solver used for connected components
            (default: 'kernighan-lin')
    """

    # get the agglomerator
    solver = get_multicut_solver(internal_solver)

    # merge attractive edges with ufd to
    # obtain natural connected components
    merge_edges = costs > 0
    ufd = nufd.ufd(graph.numberOfNodes)
    uv_ids = graph.uvIds()
    ufd.merge(uv_ids[merge_edges])
    cc_labels = ufd.elementLabeling()

    # relabel component ids consecutively
    cc_labels, max_id, _ = relabelConsecutive(cc_labels,
                                              start_label=0,
                                              keep_zeros=False)

    # solve a component sub-problem
    def solve_component(component_id):

        # extract the nodes in this component
        sub_nodes = np.where(cc_labels == component_id)[0].astype('uint64')
        # if we only have a single node, return trivial labeling
        if len(sub_nodes) == 1:
            return sub_nodes, np.array([0], dtype='uint64'), 1

        # extract the subgraph corresponding to this component
        inner_edges, _ = graph.extractSubgraphFromNodes(sub_nodes)
        sub_uvs = uv_ids[inner_edges]
        assert len(inner_edges) == len(sub_uvs), "%i, %i" % (len(inner_edges),
                                                             len(sub_uvs))

        # relabel sub-nodes and associated uv-ids
        sub_nodes_relabeled, max_local, node_mapping = relabelConsecutive(
            sub_nodes, start_label=0, keep_zeros=False)
        sub_uvs = nifty.tools.takeDict(node_mapping, sub_uvs)

        # build the graph
        sub_graph = nifty.graph.undirectedGraph(max_local + 1)
        sub_graph.insertEdges(sub_uvs)

        # solve local multicut
        sub_costs = costs[inner_edges]
        assert len(sub_costs) == sub_graph.numberOfEdges, "%i, %i" % (
            len(sub_costs), sub_graph.numberOfEdges)
        sub_labels = solver(sub_graph, sub_costs, time_limit=time_limit)
        # relabel the solution
        sub_labels, max_seg_local, _ = relabelConsecutive(sub_labels,
                                                          start_label=0,
                                                          keep_zeros=False)
        assert len(sub_labels) == len(sub_nodes), "%i, %i" % (len(sub_labels),
                                                              len(sub_nodes))
        return sub_nodes, sub_labels, max_seg_local + 1

    # solve all components in parallel
    with futures.ThreadPoolExecutor(n_threads) as tp:
        tasks = [
            tp.submit(solve_component, component_id)
            for component_id in range(max_id + 1)
        ]
        results = [t.result() for t in tasks]

    sub_nodes = [res[0] for res in results]
    sub_results = [res[1] for res in results]
    offsets = np.array([res[2] for res in results], dtype='uint64')

    # make proper offsets for the component results
    offsets = np.roll(offsets, 1)
    offsets[0] = 0
    offsets = np.cumsum(offsets)

    # insert sub-results into the components
    node_labels = np.zeros_like(cc_labels, dtype='uint64')

    def insert_solution(component_id):
        nodes = sub_nodes[component_id]
        node_labels[nodes] = (sub_results[component_id] +
                              offsets[component_id])

    with futures.ThreadPoolExecutor(n_threads) as tp:
        tasks = [
            tp.submit(insert_solution, component_id)
            for component_id in range(max_id + 1)
        ]
        [t.result() for t in tasks]

    return node_labels
Example #16
0
def _agglomerate_block(blocking, block_id, ds_in, ds_out, config):
    fu.log("start processing block %i" % block_id)
    have_ignore_label = config['have_ignore_label']
    use_mala_agglomeration = config.get('use_mala_agglomeration', True)
    threshold = config.get('threshold', 0.9)
    size_regularizer = config.get('size_regularizer', .5)
    invert_inputs = config.get('invert_inputs', False)
    offsets = config.get('offsets', None)

    bb = vu.block_to_bb(blocking.getBlock(block_id))
    # load the segmentation / output
    seg = ds_out[bb]

    # check if this block is empty
    if np.sum(seg) == 0:
        fu.log_block_success(block_id)
        return

    # load the input data
    ndim_in = ds_in.ndim
    if ndim_in == 4:
        assert offsets is not None
        assert len(offsets) <= ds_in.shape[0]
        bb_in = (slice(0, len(offsets)),) + bb
        input_ = vu.normalize(ds_in[bb_in])
    else:
        assert offsets is None
        input_ = vu.normalize(ds_in[bb])

    if invert_inputs:
        input_ = 1. - input_

    id_offset = int(seg[seg != 0].min())

    # relabel the segmentation
    _, max_id, _ = relabelConsecutive(seg, out=seg, keep_zeros=True, start_label=1)
    seg = seg.astype('uint32')

    # construct rag
    rag = nrag.gridRag(seg, numberOfLabels=max_id + 1,
                       numberOfThreads=1)

    # extract edge features
    if offsets is None:
        edge_features = nrag.accumulateEdgeMeanAndLength(rag, input_, numberOfThreads=1)
    else:
        edge_features = nrag.accumulateAffinityStandartFeatures(rag, input_, offsets,
                                                                numberOfThreads=1)
    edge_features, edge_sizes = edge_features[:, 0], edge_features[:, -1]
    uv_ids = rag.uvIds()
    # set edges to ignore label to be maximally repulsive
    if have_ignore_label:
        ignore_mask = (uv_ids == 0).any(axis=1)
        edge_features[ignore_mask] = 1

    # build undirected graph
    n_nodes = rag.numberOfNodes
    graph = nifty.graph.undirectedGraph(n_nodes)
    graph.insertEdges(uv_ids)

    if use_mala_agglomeration:
        node_labels = mala_clustering(graph, edge_features,
                                      edge_sizes, threshold)
    else:
        node_ids, node_sizes = np.unique(seg, return_counts=True)
        if node_ids[0] != 0:
            node_sizes = np.concatenate([np.array([0]), node_sizes])
        n_stop = int(threshold * n_nodes)
        node_labels = agglomerative_clustering(graph, edge_features,
                                               node_sizes, edge_sizes,
                                               n_stop, size_regularizer)

    # run clusteting
    node_labels, max_id, _ = relabelConsecutive(node_labels, start_label=1, keep_zeros=True)

    fu.log("reduced number of labels from %i to %i" % (n_nodes, max_id + 1))

    # project node labels back to segmentation
    seg = nrag.projectScalarNodeDataToPixels(rag, node_labels, numberOfThreads=1)
    seg = seg.astype('uint64')
    # add offset back to segmentation
    seg[seg != 0] += id_offset

    ds_out[bb] = seg
    # log block success
    fu.log_block_success(block_id)