Skip to content

Commit 1a0631c

Browse files
committed
save electrodes files and channels in each collection on alignment resolved; allow single alignment resolution with manually resolved
1 parent ed26b47 commit 1a0631c

File tree

2 files changed

+72
-65
lines changed

2 files changed

+72
-65
lines changed

ibllib/qc/alignment_qc.py

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
import numpy as np
44

55
from ibllib.atlas import AllenAtlas
6-
from ibllib.atlas.regions import BrainRegions
76
from ibllib.pipes import histology
87
from ibllib.pipes.ephys_alignment import EphysAlignment
98
from ibllib.qc import base
109
from ibllib.oneibl.patcher import FTPPatcher
1110
from ibllib.qc.base import CRITERIA as CRITERIA_BASE
11+
from ibllib.io.spikeglx import _geometry_from_meta, read_meta_data
12+
from ibllib.ephys.neuropixel import trace_header
1213

1314
_log = logging.getLogger('ibllib')
1415
CRITERIA = {"PASS": 0.8}
@@ -37,7 +38,7 @@ def __init__(self, probe_id, one=None, brain_atlas=None, channels=True, collecti
3738
# Get the brain atlas
3839
self.brain_atlas = brain_atlas or AllenAtlas(25)
3940
# Flag for uploading channels to alyx. For testing purposes
40-
self.channels = channels
41+
self.channels_flag = channels
4142

4243
self.insertion = self.one.alyx.get(f'/insertions/{self.eid}', clobber=True)
4344
self.resolved = (self.insertion.get('json', {'temp': 0}).get('extended_qc').
@@ -66,14 +67,14 @@ def load_data(self, prev_alignments=None, xyz_picks=None, depths=None, cluster_c
6667
align_keys = [*self.alignments.keys()]
6768
self.align_keys_sorted = sorted(align_keys, reverse=True)
6869

69-
if len(self.alignments) < 2:
70-
return
71-
7270
if not np.any(xyz_picks):
7371
self.xyz_picks = np.array(self.insertion['json']['xyz_picks']) / 1e6
7472
else:
7573
self.xyz_picks = xyz_picks
7674

75+
if len(self.alignments) < 2:
76+
return
77+
7778
if not self.probe_collection:
7879
all_collections = self.one.list_collections(self.insertion['session'])
7980
if f'alf/{self.insertion["name"]}/pykilosort' in all_collections:
@@ -182,7 +183,11 @@ def resolve_manual(self, align_key, update=True, upload_alyx=True, upload_flatir
182183
f"set 'force=True'")
183184
file_paths = []
184185
else:
185-
results = self.compute_alignment_status()
186+
if self.sim_matrix.shape[0] > 1:
187+
results = self.compute_alignment_status()
188+
else:
189+
results = {'alignment_count': self.sim_matrix.shape[0]}
190+
186191
results['alignment_resolved'] = True
187192
results['alignment_stored'] = align_key
188193
results['alignment_resolved_by'] = 'experimenter'
@@ -203,7 +208,7 @@ def compute_similarity_matrix(self):
203208
parent brain region
204209
"""
205210

206-
r = BrainRegions()
211+
r = self.brain_atlas.regions
207212

208213
clusters = dict()
209214
for iK, key in enumerate(self.align_keys_sorted):
@@ -248,7 +253,7 @@ def compute_alignment_status(self):
248253
"""
249254
# Set diagonals to zero so we don't use those to find max
250255
np.fill_diagonal(self.sim_matrix, 0)
251-
# self.sim_matrix[self.sim_matrix == 1] = 0
256+
252257
max_sim = np.max(self.sim_matrix)
253258

254259
results = {'alignment_qc': max_sim,
@@ -260,14 +265,10 @@ def compute_alignment_status(self):
260265
results.update({'alignment_resolved': True})
261266
results.update({'alignment_resolved_by': 'qc'})
262267

263-
# outcome = 'PASS'
264-
265268
else:
266269
results.update({'alignment_stored': self.align_keys_sorted[0]})
267270
results.update({'alignment_resolved': False})
268271

269-
# outcome = 'WARNING'
270-
271272
return results
272273

273274
def upload_channels(self, alignment_key, upload_alyx, upload_flatiron):
@@ -277,73 +278,89 @@ def upload_channels(self, alignment_key, upload_alyx, upload_flatiron):
277278

278279
feature = np.array(self.alignments[alignment_key][0])
279280
track = np.array(self.alignments[alignment_key][1])
280-
ephysalign = EphysAlignment(self.xyz_picks, self.depths,
281+
282+
try:
283+
meta_dset = self.one.list_datasets(self.insertion['session'], '*ap.meta',
284+
collection=f'raw_ephys_data/{self.insertion["name"]}')
285+
286+
meta_file = self.one.load_dataset(self.insertion['session'], meta_dset[0].split('/')[-1],
287+
collection=f'raw_ephys_data/{self.insertion["name"]}',
288+
download_only=True)
289+
geometry = _geometry_from_meta(read_meta_data(meta_file))
290+
chns = np.c_[geometry['x'], geometry['y']]
291+
except Exception as err:
292+
self.log.warning(f"Could not compute channel locations from meta file, errored with message: {err}. "
293+
f"Will use default Neuropixel 1 channels")
294+
geometry = trace_header(version=1)
295+
chns = np.c_[geometry['x'], geometry['y']]
296+
297+
ephysalign = EphysAlignment(self.xyz_picks, chns[:, 1],
281298
track_prev=track,
282299
feature_prev=feature,
283300
brain_atlas=self.brain_atlas)
284-
285-
# Find the channels
286301
channels_mlapdv = np.int32(ephysalign.get_channel_locations(feature, track) * 1e6)
287-
channels_brainID = ephysalign.get_brain_locations(channels_mlapdv / 1e6)['id']
302+
channels_atlas_id = ephysalign.get_brain_locations(channels_mlapdv / 1e6)['id']
288303

289-
# Find the clusters
290-
r = BrainRegions()
291-
clusters_mlapdv = channels_mlapdv[self.cluster_chns]
292-
clusters_brainID = channels_brainID[self.cluster_chns]
293-
clusters_brainAcro = r.get(ids=clusters_brainID).acronym
304+
# Need to change channels stored on alyx as well as the stored key is not the same as the latest key
305+
if upload_alyx:
306+
if alignment_key != self.align_keys_sorted[0]:
307+
histology.register_aligned_track(self.eid, channels_mlapdv / 1e6,
308+
chn_coords=chns, one=self.one,
309+
overwrite=True, channels=self.channels_flag)
310+
311+
ephys_traj = self.one.alyx.get(f'/trajectories?&probe_insertion={self.eid}'
312+
'&provenance=Ephys aligned histology track',
313+
clobber=True)
314+
patch_dict = {'json': self.alignments}
315+
self.one.alyx.rest('trajectories', 'partial_update', id=ephys_traj[0]['id'],
316+
data=patch_dict)
294317

295-
# upload datasets to flatiron
296318
files_to_register = []
297319
if upload_flatiron:
298320
ftp_patcher = FTPPatcher(one=self.one)
299-
insertion = self.one.alyx.get(f'/insertions/{self.eid}', clobber=True)
300-
alf_path = self.one.eid2path(insertion['session']).joinpath(self.probe_collection)
321+
322+
alf_path = self.one.eid2path(self.insertion['session']).joinpath('alf', self.insertion["name"])
301323
alf_path.mkdir(exist_ok=True, parents=True)
302324

303-
# Make the channels.mlapdv dataset
304-
f_name = alf_path.joinpath('channels.mlapdv.npy')
325+
f_name = alf_path.joinpath('electrodeSites.mlapdv.npy')
305326
np.save(f_name, channels_mlapdv)
306327
files_to_register.append(f_name)
307328

308-
# Make the channels.brainLocationIds dataset
309-
f_name = alf_path.joinpath('channels.brainLocationIds_ccf_2017.npy')
310-
np.save(f_name, channels_brainID)
329+
f_name = alf_path.joinpath('electrodeSites.brainLocationIds_ccf_2017.npy')
330+
np.save(f_name, channels_atlas_id)
311331
files_to_register.append(f_name)
312332

313-
# Make the clusters.mlapdv dataset
314-
f_name = alf_path.joinpath('clusters.mlapdv.npy')
315-
np.save(f_name, clusters_mlapdv)
333+
f_name = alf_path.joinpath('electrodeSites.localCoordinates.npy')
334+
np.save(f_name, chns)
316335
files_to_register.append(f_name)
317336

318-
# Make the clusters.brainLocationIds dataset
319-
f_name = alf_path.joinpath('clusters.brainLocationIds_ccf_2017.npy')
320-
np.save(f_name, clusters_brainID)
321-
files_to_register.append(f_name)
337+
probe_collections = self.one.list_collections(self.insertion['session'], filename='channels*',
338+
collection=f'alf/{self.insertion["name"]}*')
322339

323-
# Make the clusters.brainLocationAcronym dataset
324-
f_name = alf_path.joinpath('clusters.brainLocationAcronyms_ccf_2017.npy')
325-
np.save(f_name, clusters_brainAcro)
326-
files_to_register.append(f_name)
340+
for collection in probe_collections:
341+
chns = self.one.load_dataset(self.insertion['session'], 'channels.localCoordinates', collection=collection)
342+
ephysalign = EphysAlignment(self.xyz_picks, chns[:, 1],
343+
track_prev=track,
344+
feature_prev=feature,
345+
brain_atlas=self.brain_atlas)
346+
channels_mlapdv = np.int32(ephysalign.get_channel_locations(feature, track) * 1e6)
347+
channels_atlas_id = ephysalign.get_brain_locations(channels_mlapdv / 1e6)['id']
348+
349+
alf_path = self.one.eid2path(self.insertion['session']).joinpath(collection)
350+
alf_path.mkdir(exist_ok=True, parents=True)
351+
352+
f_name = alf_path.joinpath('channels.mlapdv.npy')
353+
np.save(f_name, channels_mlapdv)
354+
files_to_register.append(f_name)
355+
356+
f_name = alf_path.joinpath('channels.brainLocationIds_ccf_2017.npy')
357+
np.save(f_name, channels_atlas_id)
358+
files_to_register.append(f_name)
327359

328360
self.log.info("Writing datasets to FlatIron")
329361
ftp_patcher.create_dataset(path=files_to_register,
330362
created_by=self.one.alyx.user)
331363

332-
# Need to change channels stored on alyx as well as the stored key is not the same as the
333-
# latest key
334-
if upload_alyx:
335-
if alignment_key != self.align_keys_sorted[0]:
336-
histology.register_aligned_track(self.eid, channels_mlapdv / 1e6,
337-
chn_coords=self.chn_coords, one=self.one,
338-
overwrite=True, channels=self.channels)
339-
340-
ephys_traj = self.one.alyx.get(f'/trajectories?&probe_insertion={self.eid}'
341-
'&provenance=Ephys aligned histology track',
342-
clobber=True)
343-
patch_dict = {'json': self.alignments}
344-
self.one.alyx.rest('trajectories', 'partial_update', id=ephys_traj[0]['id'],
345-
data=patch_dict)
346-
347364
return files_to_register
348365

349366
def update_experimenter_evaluation(self, prev_alignments=None, override=False):

ibllib/tests/qc/test_alignment_qc.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -359,16 +359,6 @@ def test_data_content(self):
359359
channels_id = np.load(alf_path.joinpath('channels.brainLocationIds_ccf_2017.npy'))
360360
assert(channels_mlapdv.shape[0] == channels_id.shape[0])
361361

362-
clusters_mlapdv = np.load(alf_path.joinpath('clusters.mlapdv.npy'))
363-
assert(np.all(np.abs(clusters_mlapdv) > 0))
364-
clusters_id = np.load(alf_path.joinpath('clusters.brainLocationIds_ccf_2017.npy'))
365-
assert(clusters_mlapdv.shape[0] == clusters_id.shape[0])
366-
assert(np.all(np.in1d(clusters_mlapdv, channels_mlapdv)))
367-
assert (np.all(np.in1d(clusters_id, channels_id)))
368-
clusters_acro = np.load(alf_path.joinpath('clusters.brainLocationAcronyms_ccf_2017.npy'),
369-
allow_pickle=True)
370-
assert(clusters_acro.shape == clusters_id.shape)
371-
372362
def test_upload_to_flatiron(self):
373363
for file in self.file_paths:
374364
file_registered = one.alyx.get(f'/datasets?&session={EPHYS_SESSION}'

0 commit comments

Comments
 (0)