Пример #1
0
def test_cut_real_neuron():
    filename = DATA / 'Neuron_slice.h5'
    result = CutPlane.find(filename, bin_width=10).to_json()
    assert_equal(result['status'], 'ok')
    assert_almost_equal(result['cut-plane']['a'], 0)
    assert_almost_equal(result['cut-plane']['b'], 0)
    assert_almost_equal(result['cut-plane']['c'], 1)
    assert_almost_equal(result['cut-plane']['d'],
                        -48.68020515427703,
                        decimal=5)
    assert_equal(result['cut-plane']['comment'],
                 'Equation: a*X + b*Y + c*Z + d < 0')

    leaves_coord = [[63.97577896, 61.52564626, 44.46020393],
                    [70.55578079, 91.74564748, 44.07020454],
                    [-99.8342186, -2.24435372, 48.60020332],
                    [-141.51421891, 20.18564611, 48.68020515],
                    [-53.53421936, 97.40564351, 46.4102047],
                    [56.85578384, -71.19435496, 43.36020546],
                    [36.01578369, 4.57564645, 44.46020393],
                    [34.87578048, 4.86564641, 39.14020424],
                    [16.15578308, -22.08435435, 45.86020546],
                    [34.8457817, 3.39564615, 39.69020348],
                    [61.36578216, -80.39435191, 40.55020409],
                    [85.11578598, -43.26435465, 44.38020592],
                    [39.88578262, -15.05435366, 45.24020271],
                    [88.63578262, 11.38564592, 45.08020287],
                    [132.03578415, 48.62564474, 40.1602047],
                    [-14.65421734, -9.67435355, 47.27020531],
                    [-30.67421685, -16.84435458, 45.71020393],
                    [-35.61421738, -15.95435328, 46.57020454],
                    [-24.96421776, -0.52435374, 46.64020424],
                    [-16.08421765, 19.26564603, 46.49020271],
                    [-6.47421751, 13.07564645, 46.18020515],
                    [-7.89421759, 29.27564626, 45.39020424],
                    [28.88578262, 36.64564519, 42.6602047],
                    [27.37578239, 49.95564657, 45.86020546],
                    [-3.61421762, 44.92564779, 46.02020531],
                    [-65.55421982, 55.61564641, 39.14020424],
                    [37.63578262, 43.8256455, 43.99020271],
                    [48.4157814, 65.95564657, 42.50020485],
                    [21.21578254, 49.98564535, 44.14020424],
                    [35.52578201, 70.56564718, 42.89020424],
                    [5.38578214, 61.3256455, 44.93020515]]
    assert_array_almost_equal(result['cut-leaves'], leaves_coord, decimal=5)

    plane = CutPlane.from_json(result, filename)
    assert_array_almost_equal(
        [sec.points[-1, COLS.XYZ] for sec in plane.cut_sections],
        leaves_coord,
        decimal=5)
Пример #2
0
    def __init__(self,
                 inputfile: Path,
                 axons: Optional[Path] = None,
                 seed: Optional[int] = 0,
                 cut_leaves_coordinates: Optional[NDArray[(3, Any)]] = None,
                 legacy_detection: bool = False,
                 repair_flags: Optional[Dict[RepairType, bool]] = None):
        '''Repair the input morphology

        Args:
            inputfile: the input neuron to repair
            axons: donor axons whose section will be used to repair this axon
            seed: the numpy seed
            cut_leaves_coordinates: List of 3D coordinates from which to start the repair
            legacy_detection: if True, use the legacy cut plane detection
                (see neuror.legacy_detection)
            repair_flags: a dict of flags where key is a RepairType and value is whether
                it should be repaired or not. If not provided, all types will be repaired.

        Note: based on https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/repair.cpp#n469  # noqa, pylint: disable=line-too-long
        '''
        np.random.seed(seed)
        self.legacy_detection = legacy_detection
        self.inputfile = inputfile
        self.axon_donors = axons or list()
        self.donated_intact_axon_sections = list()
        self.repair_flags = repair_flags or dict()

        if legacy_detection:
            self.cut_leaves = CutPlane.find_legacy(inputfile,
                                                   'z').cut_leaves_coordinates
        elif cut_leaves_coordinates is None:
            self.cut_leaves = CutPlane.find(
                inputfile, bin_width=15).cut_leaves_coordinates
        else:
            self.cut_leaves = np.asarray(cut_leaves_coordinates)

        self.neuron = load_neuron(inputfile)
        self.repair_type_map = dict()
        self.max_y_cylindrical_extent = _max_y_dendritic_cylindrical_extent(
            self.neuron)
        self.max_y_extent = max(
            np.max(section.points[:, COLS.Y])
            for section in self.neuron.iter())

        self.info = dict()
        apical_section_id, _ = apical_point_section_segment(self.neuron)
        if apical_section_id:
            self.apical_section = self.neuron.sections[apical_section_id]
        else:
            self.apical_section = None
Пример #3
0
def unravel_all(raw_dir,
                unravelled_dir,
                raw_planes_dir,
                unravelled_planes_dir,
                window_half_length=DEFAULT_WINDOW_HALF_LENGTH):
    '''Repair all morphologies in input folder
    '''
    if not os.path.exists(raw_planes_dir):
        raise Exception('{} does not exist'.format(raw_planes_dir))

    if not os.path.exists(unravelled_planes_dir):
        os.mkdir(unravelled_planes_dir)

    for inputfilename in iter_morphology_files(raw_dir):
        L.info('Unravelling: %s', inputfilename)
        outfilename = Path(unravelled_dir, inputfilename.name)
        raw_plane = CutPlane.from_json(
            Path(raw_planes_dir, inputfilename.name).with_suffix('.json'))
        unravelled_plane = Path(unravelled_planes_dir,
                                inputfilename.name).with_suffix('.json')

        try:
            neuron, mapping = unravel(str(inputfilename), window_half_length)
            neuron.write(str(outfilename))
            with open(str(unravelled_plane), 'w') as f:
                json.dump(unravel_plane(raw_plane, mapping).to_json(),
                          f,
                          cls=RepairJSON)

        except Exception as e:  # noqa, pylint: disable=broad-except
            L.warning('Unravelling %s failed', f)
            L.warning(e, exc_info=True)
Пример #4
0
def test_cut_plane_from_rotations_translations():
    filename = DATA / 'Neuron_slice.h5'
    equation = CutPlane.from_rotations_translations([0, 21, -21, 0, 0, 50],
                                                    morphology=filename,
                                                    bin_width=10)
    assert_array_almost_equal(equation.coefs,
                              [35.836795, 0., 93.358043, -4667.902132])
Пример #5
0
def test_cut_neuron_simple():
    filename = DATA / 'simple2.asc'
    result = CutPlane.find(filename, bin_width=0.2).to_json()
    ok_('The probability that there is in fact NO cut plane is high: -log(p)'
        in result['status'])
    assert_almost_equal(result['cut-plane']['a'], 0)
    assert_almost_equal(result['cut-plane']['b'], 0)
    assert_almost_equal(result['cut-plane']['c'], 1)
    assert_allclose(result['cut-plane']['d'], -2, rtol=0.2)
Пример #6
0
def test_repaired_neuron():
    result = CutPlane.find(DATA / 'bio_neuron-000.h5', bin_width=10).to_json()
    assert_not_equal(result['status'], 'ok')
Пример #7
0
    def run(self, outputfile: Path, plot_file: Optional[Path] = None):
        '''Run'''
        if self.cut_leaves.size == 0:
            L.warning('No cut leaves. Nothing to repair for morphology %s',
                      self.inputfile)
            self.neuron.write(outputfile)
            return

        # See https://github.com/BlueBrain/MorphIO/issues/161
        keep_axons_alive = list()

        for axon_donor in self.axon_donors:
            if self.legacy_detection:
                plane = CutPlane.find_legacy(axon_donor, 'z')
            else:
                plane = CutPlane.find(axon_donor)
            keep_axons_alive.append(plane)
            self.donated_intact_axon_sections.extend([
                section for section in iter_sections(plane.morphology)
                if section.type == SectionType.axon
                and is_branch_intact(section, plane.cut_leaves_coordinates)
            ])

        self._fill_repair_type_map()
        self._fill_statistics_for_intact_subtrees()
        intact_axonal_sections = [
            section for section in iter_sections(self.neuron)
            if section.type == SectionType.axon
            and is_branch_intact(section, self.cut_leaves)
        ]

        # BlueRepairSDK used to have a bounding cylinder filter but
        # I don't know what is it good at so I have commented
        # the only relevant line
        # bounding_cylinder_radius = 10000
        cut_sections_in_bounding_cylinder = [
            section for section in iter_sections(self.neuron) if
            (is_cut_section(section, cut_points=self.cut_leaves)
             # and np.linalg.norm(section.points[-1, COLS.XZ]) < bounding_cylinder_radius
             )
        ]

        used_axon_branches = set()

        cut_leaves_ids = {
            section: len(section.points)
            for section in cut_sections_in_bounding_cylinder
        }

        for section in sorted(cut_sections_in_bounding_cylinder,
                              key=section_path_length):
            type_ = self.repair_type_map[section]
            if not self.repair_flags.get(type_, True):
                continue
            L.info('Repairing: %s, section id: %s', type_, section.id)
            if type_ in {
                    RepairType.basal, RepairType.oblique, RepairType.tuft
            }:
                origin = self._get_origin(section)
                if section.type == NeuriteType.basal_dendrite:
                    _continuation(section, origin)
                self._grow(section, self._get_order_offset(section), origin)
            elif type_ == RepairType.axon:
                axon.repair(self.neuron, section, intact_axonal_sections,
                            self.donated_intact_axon_sections,
                            used_axon_branches, self.max_y_extent)
            elif type_ == RepairType.trunk:
                L.info('Trunk repair is not (nor has ever been) implemented')
            else:
                raise Exception('Unknown type: {}'.format(type_))

        if plot_file is not None:
            try:
                from neuror.view import plot_repaired_neuron
                plot_repaired_neuron(self.neuron, cut_leaves_ids, plot_file)
            except ImportError:
                L.warning(
                    'Skipping writing plots as [plotly] extra is not installed'
                )

        self.neuron.write(outputfile)
        L.info('Repair successful for %s', self.inputfile)
Пример #8
0
    def __init__(
            self,  # pylint: disable=too-many-arguments
            inputfile: Path,
            axons: Optional[Path] = None,
            seed: Optional[int] = 0,
            cut_leaves_coordinates: Optional[NDArray[(3, Any)]] = None,
            legacy_detection: bool = False,
            repair_flags: Optional[Dict[RepairType, bool]] = None,
            apical_point: NDArray[3, float] = None,
            params: Dict = None):
        '''Repair the input morphology

        The repair algorithm uses sholl analysis of intact branches to grow new branches from cut
        leaves. The algorithm is fairly complex, but can be controled via a few parameters in the
        params dictionary. By default, they are:
        _PARAMS = {
            'seg_length': 5.0,  # lenghts of new segments
            'sholl_layer_size': 10,  # resoluion of the shll profile
            'noise_continuation': 0.5,  # together with seg_length, this controls the tortuosity
            'soma_repulsion': 0.2,  # if 0, previous section direction, if 1, radial direction
            'bifurcation_angle': 20,  # noise amplitude in degree around mean bif angle on the cell
            'path_length_ratio': 0.5,  # a smaller value will make a strornger taper rate
            'children_diameter_ratio': 0.8,  # 1: child diam = parent diam, 0: child diam = tip diam
            'tip_percentile': 25,  # percentile of tip radius distributions to use as tip radius
        }

        Args:
            inputfile: the input neuron to repair
            axons: donor axons whose section will be used to repair this axon
            seed: the numpy seed
            cut_leaves_coordinates: List of 3D coordinates from which to start the repair
            legacy_detection: if True, use the legacy cut plane detection
                (see neuror.legacy_detection)
            repair_flags: a dict of flags where key is a RepairType and value is whether
                it should be repaired or not. If not provided, all types will be repaired.
            apical_point: 3d vector for apical point, else, the automatic apical detection is used
                if apical_point == -1, no automatic detection will be tried
            params: repair internal parameters (see comments in code for details)

        Note: based on https://bbpcode.epfl.ch/browse/code/platform/BlueRepairSDK/tree/BlueRepairSDK/src/repair.cpp#n469  # noqa, pylint: disable=line-too-long
        '''
        np.random.seed(seed)
        self.legacy_detection = legacy_detection
        self.inputfile = inputfile
        self.axon_donors = axons or list()
        self.donated_intact_axon_sections = list()
        self.repair_flags = repair_flags or dict()
        self.params = params if params is not None else _PARAMS

        if legacy_detection:
            self.cut_leaves = CutPlane.find_legacy(inputfile,
                                                   'z').cut_leaves_coordinates
        elif cut_leaves_coordinates is None:
            self.cut_leaves = CutPlane.find(
                inputfile, bin_width=15).cut_leaves_coordinates
        else:
            self.cut_leaves = np.asarray(cut_leaves_coordinates)

        self.neuron = load_neuron(inputfile)
        self.repair_type_map = dict()
        self.max_y_cylindrical_extent = _max_y_dendritic_cylindrical_extent(
            self.neuron)
        self.max_y_extent = max(
            np.max(section.points[:, COLS.Y])
            for section in self.neuron.iter())

        self.info = dict()
        apical_section_id = None
        if apical_point != -1:
            if apical_point:
                # recall MorphIO ID = NeuroM ID - 1
                apical_section_id = point_to_section_segment(
                    self.neuron, apical_point)[0] - 1
            else:
                apical_section_id, _ = apical_point_section_segment(
                    self.neuron)

        if apical_section_id:
            self.apical_section = self.neuron.sections[apical_section_id]
        else:
            self.apical_section = None

        # record the tip radius as a lower bound for diameters with taper, excluding axons
        # because they are treated separately, with thinner diameters
        _diameters = [
            np.mean(leaf.points[:, COLS.R]) for neurite in self.neuron.neurites
            if neurite.type is not NeuriteType.axon
            for leaf in iter_sections(neurite, iterator_type=Section.ileaf)
        ]
        self.tip_radius = (np.percentile(
            _diameters, self.params["tip_percentile"]) if _diameters else None)
        self.current_trunk_radius = None

        # estimate a global tapering rate of the morphology as a function of trunk radius,
        # such that the tip radius is attained on average af max_path_length, defined as a fraction
        # of the maximal path length via the parameter path_lengt_ratio. The smaller this parameter
        # the faster the radii will convert to tip_radius.
        # TODO: maybe we would need this per neurite_type
        max_path_length = self.params["path_length_ratio"] * np.max(
            nm.get("terminal_path_lengths_per_neurite", self.neuron))
        self.taper = (lambda trunk_radius: (trunk_radius - self.tip_radius) *
                      self.params["seg_length"] / max_path_length)