Beispiel #1
0
def presynapse_focality(
    x: Union[navis.TreeNeuron, navis.NeuronList],
    heal_fragmented_neuron: bool = False,
    confidence_threshold: tuple((float, float)) = (0.9, 0.9),
    num_synapses_threshold: int = 1,
):

    """
    Finds the connections that are downstream of 'x', where the presynpases of 'x' are focalised

    Parameters
    --------
    x:                       A matrix to perform DBSCAN on

    heal_fragmented_neuron:  bool
                             Whether to heal the neuron or not.
                             N.B. Its better to heal neurons during
                             import to save time in this function.

    connector_confidence:    tuple of floats
                             The confidence value used to threshold the synapses.
                             The first value (connector_confidence[0]) will be used to threshold presynapses
                             The second value (connector_confidence[1]) will be used to threshold postsynapses

    num_samples_threshold:   int
                             The minimum number of synapses a partner must have
                             to be included in the permutation test

    Returns
    --------
    synapse_connections:     A dataframe detailing the presynaptic connections
    df:                      A dataframe to be populated by the permutation test function

    Examples
    --------

    """

    x = check_valid_neuron_input(x)

    if heal_fragmented_neuron is True:

        x = navis.heal_fragmented_neuron(x)

    # Getting the connector table of synapses where x.id is the source
    synapse_connections = navis.interfaces.neuprint.fetch_synapse_connections(
        source_criteria=x.id
    )
    synapse_connections.astype(object)
    synapse_connections = nbm.match_connectors_to_nodes(synapse_connections,
                                                        x,
                                                        synapse_type='pre')

    truth_list = [
        True if len(np.unique(i)) > 1 else False
        for i in synapse_connections.node.values
    ]
    if synapse_connections[truth_list].shape[0] == 0:

        synapse_connections.node = [
            np.unique(k)[0] for k in synapse_connections.node.tolist()
        ]

    else:

        return "There are synapses associated with multiple nodes!!!!"

    synapse_connections = synapse_connections[
        synapse_connections.confidence_pre > confidence_threshold[0]
    ][synapse_connections.confidence_post > confidence_threshold[1]].copy()
    count = Counter(synapse_connections.bodyId_post.tolist())
    count = {
        k: v for k, v in sorted(count.items(), key=lambda item: item[1], reverse=True)
    }
    truth_list = [
        True if count[i] > num_synapses_threshold else False
        for i in synapse_connections.bodyId_post
    ]
    synapse_connections = synapse_connections[truth_list].copy()

    df = pd.DataFrame()
    df["partner_neuron"] = synapse_connections.bodyId_post.unique().tolist()
    df["gT"] = ""
    df["significance_val"] = ""
    df["p_val"] = ""
    df["num_syn"] = [count[i] for i in df.partner_neuron]

    return (synapse_connections, df)
Beispiel #2
0
def merge_flywire_neuron(id,
                         target_instance,
                         tag,
                         flywire_dataset='production',
                         assert_id_match=True,
                         drop_soma_hairball=True,
                         **kwargs):
    """Merge flywire neuron into FAFB.

    This function (1) fetches a mesh from flywire, (2) turns it into a skeleton,
    (3) maps the coordinates to FAFB 14 and (4) runs ``fafbseg.merge_neuron``
    to merge the skeleton into CATMAID. See Examples below on how to run these
    individual steps yourself if you want more control over e.g. how the mesh
    is skeletonized.

    Parameters
    ----------
    id  :                int
                         ID of the flywire neuron you want to merge.
    target_instance :    pymaid.CatmaidInstance
                         Instance to merge the neuron into into.
    tag :                str
                         You personal tag to add as annotation once import into
                         CATMAID is complete.
    dataset :            str | CloudVolume
                         Against which flywire dataset to query::
                            - "production" (current production dataset, fly_v31)
                            - "sandbox" (i.e. fly_v26)
    assert_id_match :    bool
                         If True, will check if skeleton nodes map to the
                         correct segment ID and if not will move them back into
                         the segment. This is potentially very slow!
    drop_soma_hairball : bool
                         If True, we will try to drop the hairball that is
                         typically created inside the soma.
    **kwargs
                Keyword arguments are passed on to ``fafbseg.merge_neuron``.

    Examples
    --------
    # Import flywire neuron
    >>> _ = merge_flywire_neuron(id=720575940610453042,
    ...                          cvpath='graphene://https://prodv1.flywire-daf.com/segmentation/1.0/fly_v26',
    ...                          target_instance=manual,
    ...                          tag='WTCam')

    """
    if not sk:
        raise ImportError('Must install skeletor: pip3 install skeletor')

    vol = parse_volume(flywire_dataset)

    # Make sure this is a valid integer
    id = int(id)

    # Download the mesh
    mesh = vol.mesh.get(id, deduplicate_chunk_boundaries=False)[id]

    # Convert to neuron
    n_fw, simp, cntr = skeletonize_neuron(
        mesh,
        drop_soma_hairball=drop_soma_hairball,
        dataset=flywire_dataset,
        assert_id_match=assert_id_match)

    # Confirm
    viewer = navis.Viewer(title='Confirm skeletonization')
    # Make sure viewer is actually visible and cleared
    viewer.show()
    viewer.clear()
    # Add skeleton
    viewer.add(n_fw, color='r')

    msg = """
    Please carefully inspect the skeletonization of the flywire mesh.
    Hit ENTER to proceed if happy or CTRL-C to cancel.
    """

    # Add mesh last - otherwise it might mask out other objects despite alpha
    viewer.add(navis.MeshNeuron(mesh), color='w', alpha=.2)

    try:
        _ = input(msg)
    except KeyboardInterrupt:
        raise KeyboardInterrupt('Merge process aborted by user.')
    except BaseException:
        raise
    finally:
        viewer.close()

    # Xform to FAFB
    n_fafb = xform.flywire_to_fafb14(n_fw,
                                     on_fail='raise',
                                     coordinates='nm',
                                     inplace=False)
    mesh_fafb = xform.flywire_to_fafb14(tm.Trimesh(mesh.vertices, mesh.faces),
                                        on_fail='raise',
                                        coordinates='nm',
                                        inplace=False)

    # Heal neuron
    n_fafb = navis.heal_fragmented_neuron(n_fafb)

    # Merge neuron
    return merge_into_catmaid(n_fafb,
                              target_instance=target_instance,
                              tag=tag,
                              mesh=mesh_fafb,
                              **kwargs)
Beispiel #3
0
def merge_flywire_neuron(id,
                         target_instance,
                         tag,
                         flywire_dataset='production',
                         assert_id_match=True,
                         drop_soma_hairball=True,
                         **kwargs):
    """Merge FlyWire neuron into FAFB CATMAID.

    This function (1) fetches a mesh from FlyWire, (2) turns it into a skeleton,
    (3) maps the coordinates to FAFB v14 space and (4) runs
    ``fafbseg.merge_neuron`` to merge the skeleton into CATMAID.

    Disclaimer:

     1. It is your responsibility to make sure that your export of FlyWire data
        does not conflict with the FlyWire community guidelines. Mass export of
        reconstructions is not OK!
     2. As with all imports to CATMAID, the importing user is responsible for
        the quality of the imported skeleton and to make sure no existing
        tracings (including annotations) are negatively impacted.

    Parameters
    ----------
    id  :                int
                         ID of the FlyWire neuron you want to merge.
    target_instance :    pymaid.CatmaidInstance
                         Instance to merge the neuron into into.
    tag :                str
                         You personal tag to add as annotation once import into
                         CATMAID is complete.
    dataset :            str | CloudVolume
                         Against which FlyWire dataset to query::
                            - "production" (current production dataset, fly_v31)
                            - "sandbox" (i.e. fly_v26)
    assert_id_match :    bool
                         If True, will check if skeleton nodes map to the
                         correct segment ID and if not will move them back into
                         the segment. This is potentially very slow!
    drop_soma_hairball : bool
                         If True, we will try to drop the hairball that is
                         typically created inside the soma.
    **kwargs
                Keyword arguments are passed on to ``fafbseg.merge_neuron``.

    Examples
    --------
    Import a FlyWire neuron:

    >>> _ = fafbseg.flywire.merge_flywire_neuron(id=720575940610453042,
    ...                                          target_instance=manual,
    ...                                          tag='WTCam')

    """
    if not sk:
        raise ImportError('Must install skeletor: pip3 install skeletor')

    vol = parse_volume(flywire_dataset)

    # Make sure this is a valid integer
    id = int(id)

    # Download the mesh
    mesh = vol.mesh.get(id, deduplicate_chunk_boundaries=False)[id]

    # Convert to neuron
    n_fw = skeletonize_neuron(mesh,
                              remove_soma_hairball=drop_soma_hairball,
                              dataset=flywire_dataset,
                              assert_id_match=assert_id_match)

    # Confirm
    viewer = navis.Viewer(title='Confirm skeletonization')
    # Make sure viewer is actually visible and cleared
    viewer.show()
    viewer.clear()
    # Add skeleton
    viewer.add(n_fw, color='r')

    msg = """
    Please carefully inspect the skeletonization of the FlyWire neuron.
    Hit ENTER to proceed if happy or CTRL-C to cancel.
    """

    # Add mesh last - otherwise it might mask out other objects despite alpha
    viewer.add(navis.MeshNeuron(mesh), color='w', alpha=.2)

    try:
        _ = input(msg)
    except KeyboardInterrupt:
        raise KeyboardInterrupt('Merge process aborted by user.')
    except BaseException:
        raise
    finally:
        viewer.close()

    # Xform to FAFB
    n_fafb = xform.flywire_to_fafb14(n_fw,
                                     on_fail='raise',
                                     coordinates='nm',
                                     inplace=False)
    mesh_fafb = xform.flywire_to_fafb14(tm.Trimesh(mesh.vertices, mesh.faces),
                                        on_fail='raise',
                                        coordinates='nm',
                                        inplace=False)

    # Heal neuron
    n_fafb = navis.heal_fragmented_neuron(n_fafb)

    # Merge neuron
    return merge_into_catmaid(n_fafb,
                              target_instance=target_instance,
                              tag=tag,
                              mesh=mesh_fafb,
                              **kwargs)
def interactive_dendrogram(
    z: Union[navis.TreeNeuron, navis.NeuronList],
    heal_neuron: bool = True,
    plot_nodes: bool = True,
    plot_connectors: bool = True,
    highlight_connectors: Optional = None,
    in_volume: Optional = None,
    prog: str = "dot",
    inscreen: bool = True,
    filename: Optional = None,
):
    """
    Takes a navis neuron and returns an interactive 2D dendrogram.
    In this dendrogram, nodes or connector locations can be highlighted


    Parameters
    ----------
    x:                    A navis neuron object

    heal_neuron: bool

                    Whether you want to heal the neuron or not. N.B. Navis neurons
                    should be healed on import, i.e. navis.fetch_skeletons(bodyid, heal = True)
                    see navis.fetch_skeletons and navis.heal_fragmented_neuron for more details

    plot_nodes:           bool

                    Whether treenodes should be plotted

    plot_connectors:      bool

                    Whether connectors should be plotted

    highlight_connectors: dict

                    A dictionary containing the treenodes of
                    the connectors you want to highlight as keys
                    and the colours you want to colour them as values.
                    This allows for multiple colours to be plotted.

                    N.B. Plotly colours are in the range of 0 - 255
                    whereas matplotlib colours are between 0-1. For the
                    interactive dendrogram colours need to be in the
                    plotly range, whereas in the static dendrogram
                    the colours need to be in the matplotlib range.

    in_volume:            navis.Volume object

                    A navis.Volume object corresponding to an ROI in the brain.
                    This will then highlight the nodes of
                    the neuron which are in that volume


    prog:                 str

                    The layout type used by navis.nx_agraph.graphviz_layout()
                    Valid programs include [dot, neato or fdp].
                    The dot program provides a hierarchical layout, this is the fastest program
                    The neato program creates edges between nodes proportional to their real length.
                    The neato program takes the longest amount of time, can be ~2hrs for a single neuron!

    inscreen:             bool

                    Whether to plot the graph inscreen (juptyer notebooks) or to plot it as a
                    separate HTML file that can be saved to file, opened in the browser and opened any time

    filename:             str

                    The filename of your interactive dendrogram html file. This parameter is only appropriate
                    when inscreen = False.



    Returns
    -------
    plotly.fig object containing the dendrogram - this can be either inscreen or as a separate html file
    with the filename specified by the filename parameter


    Examples
    --------
    from neuroboom.utils import create_graph_structure
    from neuroboom.dendrogram import interactive_dendrogram
    import navis.interfaces.neuprint as nvneu
    from matplotlib import pyplot as plt


    test_neuron = nvneu.fetch_skeletons(722817260)


    interactive_dendrogram(test_neuron, prog = 'dot', inscreen = True)


    """

    z = check_valid_neuron_input(z)

    if heal_neuron:
        z = navis.heal_fragmented_neuron(z)

    valid_progs = ["neato", "dot"]
    if prog not in valid_progs:
        raise ValueError("Unknown program parameter!")

    # save start time

    start = time.time()

    # Necessary for neato layouts for preservation of segment lengths

    if "parent_dist" not in z.nodes:
        z = calc_cable(z, return_skdata=True)

    # Generation of networkx diagram

    g, pos = create_graph_structure(z,
                                    returned_object="graph_and_positions",
                                    prog=prog)

    print("Now creating plotly graph...")

    # Convering networkx nodes for plotly
    # NODES

    x = []
    y = []
    node_info = []

    if plot_nodes:

        for n in g.nodes():
            x_, y_ = pos[n]

            x.append(x_)
            y.append(y_)
            node_info.append("Node ID: {}".format(n))

        node_trace = go.Scatter(
            x=x,
            y=y,
            mode="markers",
            text=node_info,
            hoverinfo="text",
            marker=go.scatter.Marker(showscale=False),
        )
    else:

        node_trace = go.Scatter()

    # EDGES

    xe = []
    ye = []

    for e in g.edges():

        x0, y0 = pos[e[0]]
        x1, y1 = pos[e[1]]

        xe += [x0, x1, None]
        ye += [y0, y1, None]

    edge_trace = go.Scatter(
        x=xe,
        y=ye,
        line=go.scatter.Line(width=1.0, color="#000"),
        hoverinfo="none",
        mode="lines",
    )

    # SOMA

    xs = []
    ys = []

    for n in g.nodes():
        if n != z.soma:
            continue
        elif n == z.soma:

            x__, y__ = pos[n]
            xs.append(x__)
            ys.append(y__)

        else:

            break

    soma_trace = go.Scatter(
        x=xs,
        y=ys,
        mode="markers",
        hoverinfo="text",
        marker=dict(size=20, color="rgb(0,0,0)"),
        text="Soma, node:{}".format(z.soma),
    )

    # CONNECTORS:
    # RELATION  = 0 ARE PRESYNAPSES, RELATION = 1 ARE POSTSYNAPSES

    if plot_connectors is False:

        presynapse_connector_trace = go.Scatter()

        postsynapse_connector_trace = go.Scatter()

    elif plot_connectors is True:

        presynapse_connector_list = list(
            z.connectors[z.connectors.type == "pre"].node_id.values)

        x_pre = []
        y_pre = []
        presynapse_connector_info = []

        for node in g.nodes():
            for tn in presynapse_connector_list:

                if node == tn:

                    x, y = pos[node]

                    x_pre.append(x)
                    y_pre.append(y)
                    presynapse_connector_info.append(
                        "Presynapse, connector_id: {}".format(tn))

        presynapse_connector_trace = go.Scatter(
            x=x_pre,
            y=y_pre,
            text=presynapse_connector_info,
            mode="markers",
            hoverinfo="text",
            marker=dict(size=10, color="rgb(0,255,0)"),
        )

        postsynapse_connectors_list = list(
            z.connectors[z.connectors.type == "post"].node_id.values)

        x_post = []
        y_post = []
        postsynapse_connector_info = []

        for node in g.nodes():
            for tn in postsynapse_connectors_list:

                if node == tn:

                    x, y = pos[node]

                    x_post.append(x)
                    y_post.append(y)

                    postsynapse_connector_info.append(
                        "Postsynapse, connector id: {}".format(tn))

        postsynapse_connector_trace = go.Scatter(
            x=x_post,
            y=y_post,
            text=postsynapse_connector_info,
            mode="markers",
            hoverinfo="text",
            marker=dict(size=10, color="rgb(0,0,255)"),
        )

    if highlight_connectors is None:

        HC_trace = go.Scatter()

    elif isinstance(highlight_connectors, dict):

        HC_nodes = []
        HC_color = []

        for i in list(highlight_connectors.keys()):

            HC_nodes.append(
                z.connectors[z.connectors.connector_id == i].node_id.values[0])
            HC_color.append("rgb({}, {}, {})".format(
                int(highlight_connectors[i][0]),
                int(highlight_connectors[i][1]),
                int(highlight_connectors[i][2]),
            ))

            HC_x = []
            HC_y = []

            HC_info = []

            for node in g.nodes():

                for tn in HC_nodes:

                    if node == tn:

                        x, y = pos[node]

                        HC_x.append(x)
                        HC_y.append(y)

                        HC_info.append(
                            "Connector of Interest, connector_id: {}, treenode_id: {}"
                            .format(
                                z.connectors[z.connectors.node_id ==
                                             node].connector_id.values[0],
                                node,
                            ))

            HC_trace = go.Scatter(
                x=HC_x,
                y=HC_y,
                text=HC_info,
                mode="markers",
                hoverinfo="text",
                marker=dict(size=12.5, color=HC_color),
            )

    # Highlight the nodes that are in a particular volume

    if in_volume is None:

        in_volume_trace = go.Scatter()

    elif in_volume is not None:

        # Volume of interest

        res = navis.in_volume(z.nodes, volume=in_volume, mode="IN")
        z.nodes["IN_VOLUME"] = res

        x_VOI = []
        y_VOI = []

        VOI_info = []

        for nodes in g.nodes():

            for tn in list(z.nodes[z.nodes.IN_VOLUME == True].node_id.values):

                if nodes == tn:

                    x, y = pos[tn]

                    x_VOI.append(x)
                    y_VOI.append(y)

                    VOI_info.append("Treenode {} is in {} volume".format(
                        tn, in_volume))

        in_volume_trace = go.Scatter(
            x=x_VOI,
            y=y_VOI,
            text=VOI_info,
            mode="markers",
            hoverinfo="text",
            marker=dict(size=5, color="rgb(35,119,0)"),
        )

    print("Creating Plotly Graph")

    fig = go.Figure(
        data=[
            edge_trace,
            node_trace,
            soma_trace,
            presynapse_connector_trace,
            postsynapse_connector_trace,
            HC_trace,
            in_volume_trace,
        ],
        layout=go.Layout(
            title="Plotly graph of {} with {} layout".format(z.name, prog),
            titlefont=dict(size=16),
            showlegend=False,
            hovermode="closest",
            margin=dict(b=20, l=50, r=5, t=40),
            annotations=[
                dict(showarrow=False,
                     xref="paper",
                     yref="paper",
                     x=0.005,
                     y=-0.002)
            ],
            xaxis=go.layout.XAxis(showgrid=False,
                                  zeroline=False,
                                  showticklabels=False),
            yaxis=go.layout.YAxis(showgrid=False,
                                  zeroline=False,
                                  showticklabels=False),
        ),
    )

    if inscreen is True:
        print("Finished in {} seconds".format(time.time() - start))
        return iplot(fig)

    else:
        print("Finished in {} seconds".format(time.time() - start))
        return plot(fig, filename=filename)
Beispiel #5
0
def get_elastictransformed_neurons_by_skid(skids,
                                           transform=None,
                                           include_connectors=True,
                                           y_coordinate_cutoff=None,
                                           **kwargs):
    """
    Apply an arbitrary transformation to a neuron.
    skids: A skeleton ID or list of skeleton IDs to transform.
    transform: a function that takes in an Nx3 numpy array representing a list
        of treenodes' coordinates and returns an Nx3 numpy array representing
        the transformed coordinates. Defaults to using warp_points_FANC_to_template
        from coordinate_transforms.warp_points_between_FANC_and_template.
    include_connectors (bool): If True, connectors will be transformed,
        otherwise will not be transformed (which saves execution time).
    y_coordinate_cutoff (numerical): If not None, filter out treenodes and
        connectors with y coordinate < y_coordinate_cutoff before transforming.

    kwargs:
        left_right_flip (bool): Flips the neuron across the VNC template's
            midplane. Only relevant for the default transform function.
    """

    left_right_flip = kwargs.get('left_right_flip', False)

    if transform is None:
        try:
            from .coordinate_transforms.warp_points_between_FANC_and_template \
                    import warp_points_FANC_to_template as warp
        except:
            from coordinate_transforms.warp_points_between_FANC_and_template \
                    import warp_points_FANC_to_template as warp
        transform = lambda x: warp(x, reflect=left_right_flip)
        y_coordinate_cutoff = 322500  # 300000 * 4.3/4. In nm

    print('Pulling source neuron data from catmaid')
    source_project.clear_cache()
    neurons = pymaid.get_neuron(skids, remote_instance=source_project)
    if type(neurons) is pymaid.core.CatmaidNeuron:
        neurons = pymaid.core.CatmaidNeuronList(neurons)

    if y_coordinate_cutoff is not None:
        for neuron in neurons:
            kept_rows = neuron.nodes.y >= y_coordinate_cutoff
            if neuron.n_nodes == kept_rows.sum():  # No nodes to cut off
                continue
            print(f'Applying y coordinate cutoff of {y_coordinate_cutoff}'
                  f' to {neuron.neuron_name}')
            kept_node_ids = neuron.nodes[kept_rows].node_id.values
            # TODO double check whether this removes synapses as well. I think it does
            navis.subset_neuron(neuron, kept_node_ids, inplace=True)
            if neuron.n_skeletons > 1:
                print(
                    f'{neuron.neuron_name} is fragmented. Healing before continuing.'
                )
                navis.heal_fragmented_neuron(neuron, inplace=True)

    for neuron in neurons:
        print(f'Transforming {neuron.neuron_name}')
        neuron.neuron_name += ' - elastic transform'
        if left_right_flip:
            neuron.neuron_name += ' - flipped'
            neuron.annotations.append('left-right flipped')

        neuron.nodes[['x', 'y', 'z']] = transform(neuron.nodes[['x', 'y',
                                                                'z']])
        if include_connectors and neuron.n_connectors > 0:
            neuron.connectors[['x', 'y', 'z']] = transform(
                neuron.connectors[['x', 'y', 'z']])

    return neurons