Source code for fafbseg.xform.xform

#    A collection of tools to interface with manually traced and autosegmented
#    data in FAFB.
#
#    Copyright (C) 2019 Philipp Schlegel
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.

import navis

import numpy as np
import trimesh as tm

from .. import utils
use_pbars = utils.use_pbars

__all__ = ['fafb14_to_flywire', 'flywire_to_fafb14']


[docs]def fafb14_to_flywire(x, coordinates='nm', mip=4, inplace=False, on_fail='warn'): """Transform neurons/coordinates from FAFB v14 to flywire. This uses a service hosted by Eric Perlman. Parameters ---------- x : CatmaidNeuron/List | np.ndarray (N, 3) Data to transform. mip : int Resolution of mapping. Lower = more precise but much slower. Currently only mip 4 available! coordinates : "nm" | "pixel" Units of the provided data in ``x``. inplace : bool If ``True`` will modify Neuron object(s) in place. If ``False`` work with a copy. on_fail : "warn" | "ignore" | "raise" What to do if points failed to xform. Returns ------- xformed data Returns same data type as input. Coordinates are returned in pixel (at 4x4x40 nm). """ return _flycon(x, dataset='flywire_v1_inverse', coordinates=coordinates, inplace=inplace, on_fail=on_fail, mip=mip)
[docs]def flywire_to_fafb14(x, coordinates=None, mip=2, inplace=False, on_fail='warn'): """Transform neurons/coordinates from flywire to FAFB V14. This uses a service hosted by Eric Perlman. Parameters ---------- x : CatmaidNeuron/List | np.ndarray (N, 3) Data to transform. mip : int Resolution of mapping. Lower = more precise but much slower. coordinates : None | "nm" | "pixel" Units of the provided data in ``x``. If ``None`` will assume that Neuron/List are in nanometers and everything else is in pixel. inplace : bool If ``True`` will modify Neuron object(s) in place. If ``False`` work with a copy. on_fail : "warn" | "ignore" | "raise" What to do if points failed to xform. Returns ------- xformed data Returns same data type as input. Coordinates are returned in nm. """ if isinstance(coordinates, type(None)): if isinstance(x, (navis.BaseNeuron, navis.NeuronList)): coordinates = 'nm' else: coordinates = 'pixel' xf = _flycon(x, dataset='flywire_v1', coordinates=coordinates, inplace=inplace, on_fail=on_fail, mip=mip) # _flycon always returns pixels - we have to convert to back to nanometers if isinstance(xf, navis.NeuronList): for n in xf: if isinstance(n, navis.TreeNeuron): n *= [4, 4, 40, 1] elif isinstance(n, navis.MeshNeuron): n *= [4, 4, 40] xf.units = 'nm' # manually set the units elif isinstance(xf, navis.TreeNeuron): # The 4th value is the radius and that is assumed to not change xf *= [4, 4, 40, 1] xf.units = 'nm' # manually set the units elif hasattr(xf, 'vertices'): xf.vertices *= [4, 4, 40] else: xf *= [4, 4, 40] return xf
def _flycon(x, dataset, base_url='https://spine.janelia.org/app/transform-service', coordinates='nm', mip=2, inplace=False, on_fail='warn'): """Transform neurons/coordinates between flywire and FAFB V14. This uses a service hosted by Eric Perlman. Parameters ---------- x : CatmaidNeuron/List | np.ndarray (N, 3) Data to transform. dataset : str Dataset to use for transform. Currently available: - 'flywire_v1' - 'flywire_v1_inverse' (only mip 4) base_url : str URL for xform service. mip : int Resolution of mapping. Lower = more precise but much slower. Currently only mip >= 2 available. coordinates : "nm" | "pixel" Units of the provided coordinates in ``x``. inplace : bool If ``True`` will modify Neuron object(s) in place. If ``False`` work with a copy. on_fail : "warn" | "ignore" | "raise" What to do if points failed to xform. Returns ------- xformed data Returns same data type as input. """ if isinstance(x, navis.NeuronList): return x.__class__([_flycon(n, dataset=dataset, on_fail=on_fail, coordinates=coordinates, mip=mip, base_url=base_url, inplace=inplace) for n in x]) elif isinstance(x, (navis.BaseNeuron, navis.Volume, tm.Trimesh)): if not inplace: x = x.copy() if isinstance(x, navis.TreeNeuron): x.nodes[['x', 'y', 'z']] = _flycon(x.nodes[['x', 'y', 'z']].values, dataset=dataset, on_fail=on_fail, coordinates=coordinates, mip=mip, base_url=base_url, inplace=inplace) elif isinstance(x, (navis.MeshNeuron, navis.Volume, tm.Trimesh)): x.vertices = _flycon(x.vertices, dataset=dataset, on_fail=on_fail, coordinates=coordinates, mip=mip, base_url=base_url, inplace=inplace) else: raise TypeError(f'Unable to convert neuron of type "{type(x)}"') if isinstance(x, navis.BaseNeuron) and x.has_connectors: x.connectors[['x', 'y', 'z']] = _flycon(x.connectors[['x', 'y', 'z']].values, dataset=dataset, on_fail=on_fail, coordinates=coordinates, mip=mip, base_url=base_url, inplace=inplace) return x # Make sure we are working on array x = np.asarray(x) if x.ndim != 2 or x.shape[1] != 3: raise ValueError(f'Expected coordinates of shape (N, 3), got {x.shape}') # This returns offsets along x and y axis offsets = utils.query_spine(x, dataset, query='transform', coordinates=coordinates, mip=mip, on_fail=on_fail) # We need to cast x to the same type as offsets -> likely float 64 x = x.astype(offsets.dtype) # Transform points x[:, :2] += offsets return x