Skip to content

Commit 208aa6c

Browse files
committed
remove atlas instantiation from histology module
1 parent a0889f5 commit 208aa6c

File tree

6 files changed

+37
-28
lines changed

6 files changed

+37
-28
lines changed

examples/one/histology/register_lasagna_tracks_alyx.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"""
2525
# Author: Olivier, Gaelle
2626
from pathlib import Path
27-
27+
from ibllib.atlas import AllenAtlas
2828
from one.api import ONE
2929

3030
from ibllib.pipes import histology
@@ -42,11 +42,12 @@
4242

4343
# ======== DO NOT EDIT BELOW ====
4444
one = ONE(base_url=ALYX_URL)
45+
ba = AllenAtlas()
4546

4647
if EXAMPLE_OVERWRITE:
4748
# TODO Olivier : Function to download examples folder
4849
cachepath = Path(one.alyx.cache_dir)
4950
path_tracks = cachepath.joinpath('examples', 'histology', 'tracks_to_add')
5051

51-
histology.register_track_files(path_tracks=path_tracks, one=one, overwrite=True)
52-
histology.detect_missing_histology_tracks(path_tracks=path_tracks, one=one)
52+
histology.register_track_files(path_tracks=path_tracks, one=one, overwrite=True, brain_atlas=ba)
53+
histology.detect_missing_histology_tracks(path_tracks=path_tracks, one=one, brain_atlas=ba)

ibllib/atlas/atlas.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
import matplotlib.pyplot as plt
44
from pathlib import Path, PurePosixPath
5-
5+
from functools import lru_cache
66
import numpy as np
77
import nrrd
88

@@ -710,6 +710,7 @@ def get_brain_entry(traj, brain_atlas):
710710
return Insertion._get_surface_intersection(traj, brain_atlas, surface='top')
711711

712712

713+
@lru_cache(maxsize=1)
713714
class AllenAtlas(BrainAtlas):
714715
"""
715716
Instantiates an atlas.BrainAtlas corresponding to the Allen CCF at the given resolution

ibllib/pipes/histology.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@
1616

1717
_logger = logging.getLogger('ibllib')
1818

19-
# origin Allen left, front, up
20-
brain_atlas = atlas.AllenAtlas(res_um=25)
2119

22-
23-
def load_track_csv(file_track):
20+
def load_track_csv(file_track, brain_atlas=None):
2421
"""
2522
Loads a lasagna track and convert to IBL-ALlen coordinate framework
2623
:param file_track:
2724
:return: xyz
2825
"""
26+
27+
brain_atlas = brain_atlas or atlas.AllenAtlas(25)
2928
# apmldv in the histology file is flipped along y direction
3029
file_track = Path(file_track)
3130
if file_track.stat().st_size == 0:
@@ -38,20 +37,21 @@ def load_track_csv(file_track):
3837
return xyz
3938

4039

41-
def get_picked_tracks(histology_path, glob_pattern="*_pts_transformed.csv"):
40+
def get_picked_tracks(histology_path, glob_pattern="*_pts_transformed.csv", brain_atlas=None):
4241
"""
4342
This outputs reads in the Lasagna output and converts the picked tracks in the IBL coordinates
4443
:param histology_path: Path object: folder path containing tracks
4544
:return: xyz coordinates in
4645
"""
46+
brain_atlas = brain_atlas or atlas.AllenAtlas()
4747
xyzs = []
4848
histology_path = Path(histology_path)
4949
if histology_path.is_file():
5050
files_track = [histology_path]
5151
else:
5252
files_track = list(histology_path.rglob(glob_pattern))
5353
for file_track in files_track:
54-
xyzs.append(load_track_csv(file_track))
54+
xyzs.append(load_track_csv(file_track, brain_atlas=brain_atlas))
5555
return {'files': files_track, 'xyz': xyzs}
5656

5757

@@ -100,13 +100,14 @@ def get_micro_manipulator_data(subject, one=None, force_extract=False):
100100
return probes
101101

102102

103-
def plot2d_all(trajectories, tracks):
103+
def plot2d_all(trajectories, tracks, brain_atlas=None):
104104
"""
105105
Plot all tracks on a single 2d slice
106106
:param trajectories: dictionary output of the Alyx REST query on trajectories
107107
:param tracks:
108108
:return:
109109
"""
110+
brain_atlas = brain_atlas or atlas.AllenAtlas(25)
110111
plt.figure()
111112
axs = brain_atlas.plot_sslice(brain_atlas.bc.i2x(190), cmap=plt.get_cmap('bone'))
112113
plt.figure()
@@ -121,14 +122,15 @@ def plot2d_all(trajectories, tracks):
121122
axs.plot(xyz[:, 1] * 1e3, xyz[:, 2] * 1e6, 'r')
122123

123124

124-
def plot3d_all(trajectories, tracks):
125+
def plot3d_all(trajectories, tracks, brain_atlas=None):
125126
"""
126127
Plot all tracks on a single 2d slice
127128
:param trajectories: dictionary output of the Alyx REST query on trajectories
128129
:param tracks:
129130
:return:
130131
"""
131132
from mayavi import mlab
133+
brain_atlas = brain_atlas or atlas.AllenAtlas()
132134
src = mlab.pipeline.scalar_field(brain_atlas.label)
133135
mlab.pipeline.iso_surface(src, contours=[0.5, ], opacity=0.3)
134136

@@ -168,7 +170,7 @@ def interpolate_along_track(xyz_track, depths):
168170
return xyz_channels
169171

170172

171-
def get_brain_regions(xyz, channels_positions=None, brain_atlas=brain_atlas):
173+
def get_brain_regions(xyz, channels_positions=None, brain_atlas=None):
172174
"""
173175
:param xyz: numpy array of 3D coordinates corresponding to a picked track or a trajectory
174176
the deepest point is assumed to be the tip.
@@ -183,6 +185,7 @@ def get_brain_regions(xyz, channels_positions=None, brain_atlas=brain_atlas):
183185
Due to the blockiness, depths may not be unique along the track so it has to be prepared
184186
"""
185187

188+
brain_atlas = brain_atlas or atlas.AllenAtlas(25)
186189
if channels_positions is None:
187190
geometry = trace_header(version=1)
188191
channels_positions = np.c_[geometry['x'], geometry['y']]
@@ -215,7 +218,7 @@ def get_brain_regions(xyz, channels_positions=None, brain_atlas=brain_atlas):
215218
return brain_regions, insertion
216219

217220

218-
def register_track(probe_id, picks=None, one=None, overwrite=False, channels=True):
221+
def register_track(probe_id, picks=None, one=None, overwrite=False, channels=True, brain_atlas=None):
219222
"""
220223
Register the user picks to a probe in Alyx
221224
Here we update Alyx models on the database in 3 steps
@@ -225,6 +228,7 @@ def register_track(probe_id, picks=None, one=None, overwrite=False, channels=Tru
225228
3) Channel locations are set in the table
226229
"""
227230
assert one
231+
brain_atlas = brain_atlas or atlas.AllenAtlas()
228232
# 0) if it's an empty track, create a null trajectory and exit
229233
if picks is None or picks.size == 0:
230234
tdict = {'probe_insertion': probe_id,
@@ -241,7 +245,7 @@ def register_track(probe_id, picks=None, one=None, overwrite=False, channels=Tru
241245
insertion_histology = None
242246
# Here need to change the track qc to critical and also extended qc to zero
243247
else:
244-
brain_locations, insertion_histology = get_brain_regions(picks)
248+
brain_locations, insertion_histology = get_brain_regions(picks, brain_atlas=brain_atlas)
245249
# 1) update the alyx models, first put the picked points in the insertion json
246250
one.alyx.json_field_update(endpoint='insertions', uuid=probe_id, field_name='json',
247251
data={'xyz_picks': np.int32(picks * 1e6).tolist()})
@@ -275,14 +279,15 @@ def register_track(probe_id, picks=None, one=None, overwrite=False, channels=Tru
275279

276280

277281
def register_aligned_track(probe_id, xyz_channels, chn_coords=None, one=None, overwrite=False,
278-
channels=True):
282+
channels=True, brain_atlas=None):
279283
"""
280284
Register ephys aligned trajectory and channel locations to Alyx
281285
Here we update Alyx models on the database in 2 steps
282286
1) The trajectory computed from the final electrode channel locations
283287
2) Channel locations are set to the trajectory
284288
"""
285289
assert one
290+
brain_atlas = brain_atlas or atlas.AllenAtlas(25)
286291
if chn_coords is None:
287292
geometry = trace_header(version=1)
288293
chn_coords = np.c_[geometry['x'], geometry['y']]
@@ -374,14 +379,14 @@ def _parse_filename(track_file):
374379
return search_filter
375380

376381

377-
def register_track_files(path_tracks, one=None, overwrite=False):
382+
def register_track_files(path_tracks, one=None, overwrite=False, brain_atlas=None):
378383
"""
379384
:param path_tracks: path to directory containing tracks; also works with a single file name
380385
:param one:
381386
:return:
382387
"""
388+
brain_atlas = brain_atlas or atlas.AllenAtlas()
383389
glob_pattern = "*_probe*_pts*.csv"
384-
385390
path_tracks = Path(path_tracks)
386391

387392
if not path_tracks.is_dir():
@@ -415,22 +420,23 @@ def register_track_files(path_tracks, one=None, overwrite=False):
415420
raise ValueError("Multiple probes found.")
416421
probe_id = probe['id']
417422
try:
418-
xyz_picks = load_track_csv(track_file)
419-
register_track(probe_id, xyz_picks, one=one, overwrite=overwrite)
423+
xyz_picks = load_track_csv(track_file, brain_atlas=brain_atlas)
424+
register_track(probe_id, xyz_picks, one=one, overwrite=overwrite, brain_atlas=brain_atlas)
420425
except Exception as e:
421426
_logger.error(str(track_file))
422427
raise e
423428
_logger.info(f"{ind + 1}/{ntracks}, {str(track_file)}")
424429

425430

426-
def detect_missing_histology_tracks(path_tracks=None, one=None, subject=None):
431+
def detect_missing_histology_tracks(path_tracks=None, one=None, subject=None, brain_atlas=None):
427432
"""
428433
Compares the number of probe insertions to the number of registered histology tracks to see if
429434
there is a discrepancy so that missing tracks can be properly logged in the database
430435
:param path_tracks: path to track files to be registered
431436
:param subject: subject nickname for which to detect missing tracks
432437
"""
433438

439+
brain_atlas = brain_atlas or atlas.AllenAtlas()
434440
if path_tracks:
435441
glob_pattern = "*_probe*_pts*.csv"
436442

@@ -479,7 +485,7 @@ def detect_missing_histology_tracks(path_tracks=None, one=None, subject=None):
479485
probe_id = insertions[idx]['id']
480486
print(insertions[idx]['session'])
481487
print(probe_id)
482-
register_track(probe_id, one=one)
488+
register_track(probe_id, one=one, brain_atlas=brain_atlas)
483489
else:
484490
_logger.info('Histology track for this probe insertion will not be registered')
485491
continue

ibllib/qc/alignment_qc.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ def upload_channels(self, alignment_key, upload_alyx, upload_flatiron):
307307
if alignment_key != self.align_keys_sorted[0]:
308308
histology.register_aligned_track(self.eid, channels_mlapdv / 1e6,
309309
chn_coords=chns, one=self.one,
310-
overwrite=True, channels=self.channels_flag)
310+
overwrite=True, channels=self.channels_flag,
311+
brain_atlas=self.brain_atlas)
311312

312313
ephys_traj = self.one.alyx.get(f'/trajectories?&probe_insertion={self.eid}'
313314
'&provenance=Ephys aligned histology track',
@@ -416,4 +417,4 @@ def get_aligned_channels(ins, chn_coords, one, ba=None, save_dir=None):
416417
np.save(f_name, channels_atlas_ids)
417418
out_files.append(f_name)
418419

419-
return out_files
420+
return out_files

ibllib/tests/qc/test_alignment_qc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ def setUpClass(cls) -> None:
5656

5757
def test_tracing_exists(self):
5858
register_track(self.probe00_id, picks=self.xyz_picks, one=one, overwrite=True,
59-
channels=False)
59+
channels=False, brain_atlas=brain_atlas)
6060
insertion = one.alyx.get('/insertions/' + self.probe00_id, clobber=True)
6161

6262
assert (insertion['json']['qc'] == 'NOT_SET')
6363
assert (insertion['json']['extended_qc']['tracing_exists'] == 1)
6464

6565
def test_tracing_not_exists(self):
6666
register_track(self.probe01_id, picks=None, one=one, overwrite=True,
67-
channels=False)
67+
channels=False, brain_atlas=brain_atlas)
6868
insertion = one.alyx.get('/insertions/' + self.probe01_id, clobber=True)
6969
assert (insertion['json']['qc'] == 'CRITICAL')
7070
assert (insertion['json']['extended_qc']['tracing_exists'] == 0)

ibllib/tests/test_histology.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def setUp(self) -> None:
1919
def test_histology_get_brain_regions(self):
2020
# first part of the test is to check on an actual track file
2121
for file_track in self.path_tracks.rglob("*_pts.csv"):
22-
xyz = histology.load_track_csv(file_track)
22+
xyz = histology.load_track_csv(file_track, brain_atlas=brain_atlas)
2323
channels, ins = histology.get_brain_regions(xyz=xyz, brain_atlas=brain_atlas)
2424
# also check that it works from an insertion
2525
channels, ins2 = histology.get_brain_regions(xyz=ins.xyz, brain_atlas=brain_atlas)
@@ -32,7 +32,7 @@ def test_histology_get_brain_regions(self):
3232
def test_histology_insertion_from_track(self):
3333

3434
for file_track in self.path_tracks.rglob("*_pts.csv"):
35-
xyz = histology.load_track_csv(file_track)
35+
xyz = histology.load_track_csv(file_track, brain_atlas=brain_atlas)
3636
insertion = atlas.Insertion.from_track(xyz, brain_atlas=brain_atlas)
3737
# checks that the tip coordinate is not the deepest point but its projection
3838
self.assertFalse(np.all(np.isclose(insertion.tip, xyz[-1])))

0 commit comments

Comments
 (0)