Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Masking #172

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ learn more!
``TreeNeurons``, ``MeshNeurons``, ``VoxelNeurons`` and ``Dotprops`` are neuron
classes. ``NeuronLists`` are containers thereof.

| Class | Description |
|------|------|
| [`navis.TreeNeuron`][] | Skeleton representation of a neuron. |
| [`navis.MeshNeuron`][] | Meshes with vertices and faces. |
| [`navis.VoxelNeuron`][] | 3D images (e.g. from confocal stacks). |
| [`navis.Dotprops`][] | Point cloud + vector representations, used for NBLAST. |
| [`navis.NeuronList`][] | Containers for neurons. |
| Class | Description |
|-------------------------|---------------------------------------------------------|
| [`navis.TreeNeuron`][] | Skeleton representation of a neuron. |
| [`navis.MeshNeuron`][] | Meshes with vertices and faces. |
| [`navis.VoxelNeuron`][] | 3D images (e.g. from confocal stacks). |
| [`navis.Dotprops`][] | Point cloud + vector representations, used for NBLAST. |
| [`navis.NeuronList`][] | Containers for neurons. |

### General Neuron methods

Expand Down Expand Up @@ -89,6 +89,7 @@ to all neurons:
| `Neuron.type` | {{ autosummary("navis.BaseNeuron.type") }} |
| `Neuron.soma` | {{ autosummary("navis.BaseNeuron.soma") }} |
| `Neuron.bbox` | {{ autosummary("navis.BaseNeuron.bbox") }} |
| `Neuron.is_masked` | {{ autosummary("navis.BaseNeuron.is_masked") }} |

!!! note

Expand Down Expand Up @@ -119,6 +120,8 @@ this neuron type. Note that most of them are simply short-hands for the other
| [`TreeNeuron.reroot()`][navis.TreeNeuron.reroot] | {{ autosummary("navis.TreeNeuron.reroot") }} |
| [`TreeNeuron.resample()`][navis.TreeNeuron.resample] | {{ autosummary("navis.TreeNeuron.resample") }} |
| [`TreeNeuron.snap()`][navis.TreeNeuron.snap] | {{ autosummary("navis.TreeNeuron.snap") }} |
| [`TreeNeuron.mask()`][navis.TreeNeuron.mask] | {{ autosummary("navis.TreeNeuron.mask") }} |
| [`TreeNeuron.unmask()`][navis.TreeNeuron.unmask] | {{ autosummary("navis.TreeNeuron.unmask") }} |

In addition, a [`navis.TreeNeuron`][] has a range of different properties:

Expand Down Expand Up @@ -146,7 +149,6 @@ In addition, a [`navis.TreeNeuron`][] has a range of different properties:
| [`TreeNeuron.vertices`][navis.TreeNeuron.vertices] | {{ autosummary("navis.TreeNeuron.vertices") }} |
| [`TreeNeuron.volume`][navis.TreeNeuron.volume] | {{ autosummary("navis.TreeNeuron.volume") }} |


#### Skeleton utility functions

| Function | Description |
Expand All @@ -158,7 +160,6 @@ In addition, a [`navis.TreeNeuron`][] has a range of different properties:
| [`navis.graph.skeleton_adjacency_matrix()`][navis.graph.skeleton_adjacency_matrix] | {{ autosummary("navis.graph.skeleton_adjacency_matrix") }} |



### Mesh neurons

Properties specific to [`navis.MeshNeuron`][]:
Expand All @@ -178,6 +179,8 @@ Methods specific to [`navis.MeshNeuron`][]:
| [`MeshNeuron.skeletonize()`][navis.MeshNeuron.skeletonize] | {{ autosummary("navis.MeshNeuron.skeletonize") }} |
| [`MeshNeuron.snap()`][navis.MeshNeuron.snap] | {{ autosummary("navis.MeshNeuron.snap") }} |
| [`MeshNeuron.validate()`][navis.MeshNeuron.validate] | {{ autosummary("navis.MeshNeuron.validate") }} |
| [`MeshNeuron.mask()`][navis.MeshNeuron.mask] | {{ autosummary("navis.MeshNeuron.mask") }} |
| [`MeshNeuron.unmask()`][navis.MeshNeuron.unmask] | {{ autosummary("navis.MeshNeuron.unmask") }} |


### Voxel neurons
Expand Down Expand Up @@ -215,6 +218,8 @@ These are methods and properties specific to [Dotprops][navis.Dotprops]:
| [`Dotprops.alpha`][navis.Dotprops.alpha] | {{ autosummary("navis.Dotprops.alpha") }} |
| [`Dotprops.to_skeleton()`][navis.Dotprops.to_skeleton] | {{ autosummary("navis.Dotprops.to_skeleton") }} |
| [`Dotprops.snap()`][navis.Dotprops.snap] | {{ autosummary("navis.Dotprops.snap") }} |
| [`Dotprops.mask()`][navis.Dotprops.mask] | {{ autosummary("navis.Dotprops.mask") }} |
| [`Dotprops.unmask()`][navis.Dotprops.unmask] | {{ autosummary("navis.Dotprops.unmask") }} |

### Converting between types

Expand Down Expand Up @@ -575,6 +580,14 @@ Functions to export neurons.
| [`navis.write_precomputed()`][navis.write_precomputed] | {{ autosummary("navis.write_precomputed") }} |
| [`navis.write_parquet()`][navis.write_parquet] | {{ autosummary("navis.write_parquet") }} |

## Masking

Functions and classes for masking:

| Function/Class | Description |
|----------------|-------------|
| [`navis.NeuronMask`][navis.NeuronMask] | {{ autosummary("navis.NeuronMask") }} |

## Utility

Various utility functions.
Expand Down
15 changes: 13 additions & 2 deletions navis/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@
from .dotprop import Dotprops
from .voxel import VoxelNeuron
from .neuronlist import NeuronList
from .masking import NeuronMask
from .core_utils import make_dotprops, to_neuron_space, NeuronProcessor

from typing import Union

NeuronObject = Union[NeuronList, TreeNeuron, BaseNeuron, MeshNeuron]

__all__ = ['Volume', 'Neuron', 'BaseNeuron', 'TreeNeuron', 'MeshNeuron',
'Dotprops', 'VoxelNeuron', 'NeuronList', 'make_dotprops']
__all__ = [
"Volume",
"Neuron",
"BaseNeuron",
"TreeNeuron",
"MeshNeuron",
"NeuronMask",
"Dotprops",
"VoxelNeuron",
"NeuronList",
"make_dotprops",
]
118 changes: 117 additions & 1 deletion navis/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@


def Neuron(
x: Union[nx.DiGraph, str, pd.DataFrame, "TreeNeuron", "MeshNeuron"], **metadata
x: Union[nx.DiGraph, str, pd.DataFrame, "TreeNeuron", "MeshNeuron"], # noqa: F821
**metadata, # noqa: F821
):
"""Constructor for Neuron objects. Depending on the input, either a
`TreeNeuron` or a `MeshNeuron` is returned.
Expand Down Expand Up @@ -195,6 +196,9 @@ class BaseNeuron(UnitObject):
#: Core data table(s) used to calculate hash
CORE_DATA = []

#: Property used to calculate length of neuron
_LENGTH_DATA = None

def __init__(self, **kwargs):
# Set a random ID -> may be replaced later
self.id = uuid.uuid4()
Expand Down Expand Up @@ -303,6 +307,14 @@ def __isub__(self, other):
"""Subtraction with assignment (-=)."""
return self.__sub__(other, copy=False)

def __len__(self):
if self._LENGTH_DATA is None:
return None
# Deal with potential empty neurons
if not hasattr(self, self._LENGTH_DATA):
return 0
return len(getattr(self, self._LENGTH_DATA))

def _repr_html_(self):
frame = self.summary().to_frame()
frame.columns = [""]
Expand Down Expand Up @@ -652,8 +664,13 @@ def copy(self, deepcopy=False) -> "BaseNeuron":

return x

def view(self) -> "BaseNeuron":
"""Create a view of the neuron without copying data."""
raise NotImplementedError(f"View not implemented for neuron of type {type(self)}.")

def summary(self, add_props=None) -> pd.Series:
"""Get a summary of this neuron."""

# Do not remove the list -> otherwise we might change the original!
props = list(self.SUMMARY_PROPS)

Expand All @@ -674,6 +691,11 @@ def summary(self, add_props=None) -> pd.Series:
warnings.simplefilter("ignore")
s = pd.Series([getattr(self, at, "NA") for at in props], index=props)

# Show mask status
if self.is_masked:
if "masked" not in s.index:
s["masked"] = True

return s

def plot2d(self, **kwargs):
Expand Down Expand Up @@ -721,6 +743,100 @@ def plot3d(self, **kwargs):

return plot3d(core.NeuronList(self, make_copy=False), **kwargs)

@property
def is_masked(self):
"""Test if neuron is masked.

See Also
--------
[`navis.BaseNeuron.mask`][]
Mask neuron.
[`navis.BaseNeuron.unmask`][]
Remove mask from neuron.
[`navis.NeuronMask`][]
Context manager for masking neurons.
"""
return hasattr(self, "_masked_data")

def mask(self, mask):
"""Mask neuron.

Implementation details depend on the neuron type (see below).

See Also
--------
[`navis.TreeNeuron.mask`][]
Mask skeleton.
[`navis.MeshNeuron.mask`][]
Mask mesh.
[`navis.Dotprops.mask`][]
Mask dotprops.

"""
raise NotImplementedError(
f"Masking not implemented for neuron of type {type(self)}."
)

def unmask(self):
"""Unmask neuron.

Returns the neuron to its original state before masking.

Returns
-------
self

See Also
--------
[`Neuron.is_masked`][navis.BaseNeuron.is_masked]
Check if neuron. is masked.
[`Neuron.mask`][navis.BaseNeuron.unmask]
Mask neuron.
[`navis.NeuronMask`][]
Context manager for masking neurons.

"""
if not self.is_masked:
raise ValueError("Neuron is not masked.")

for k, v in self._masked_data.items():
if hasattr(self, k):
setattr(self, k, v)

delattr(self, "_mask")
delattr(self, "_masked_data")
self._clear_temp_attr()

return self

def apply_mask(self, inplace=False):
"""Apply mask to neuron.

This will effectively make the mask permanent.

Parameters
----------
inplace : bool
If True will apply mask in-place. If False
will return a copy and the original neuron
will remain masked.

Returns
-------
Neuron
Neuron with mask applied.

"""
if not self.is_masked:
raise ValueError("Neuron is not masked.")

n = self if inplace else self.copy()

delattr(n, "_mask")
delattr(n, "_masked_data")

return n

def map_units(
self,
units: Union[pint.Unit, str],
Expand Down
Loading
Loading