22from brainbox .numerical import ismember
33from oneibl .one import ONE
44from ibllib .pipes import histology
5- from ibllib .atlas import AllenAtlas
5+ from ibllib .atlas import AllenAtlas , atlas
66from ibllib .ephys .neuropixel import TIP_SIZE_UM , SITES_COORDINATES
77from ibllib .pipes .ephys_alignment import EphysAlignment
88import time
9+ from scipy .signal import fftconvolve
10+ from ibllib .dsp import fcn_cosine
911
1012PROV_2_VAL = {
1113 'Resolved' : 90 ,
@@ -28,7 +30,11 @@ def __init__(self, one=None, ba=None):
2830 'Ephys aligned histology track' : {},
2931 'Resolved' : {},
3032 'Best' : {}}
33+ self .ins = {}
3134
35+ # for histology get the insertions and compute the trajectory from them. Means only one
36+ # long query to alyx and use xyz picks for channels and coverage lower down. More complicated
37+ # but will speed things up
3238 self .get_traj_for_provenance (provenance = 'Histology track' , django = ['x__isnull,False' ])
3339 self .get_traj_for_provenance (provenance = 'Ephys aligned histology track' )
3440 self .get_traj_for_provenance (provenance = 'Ephys aligned histology track' ,
@@ -38,13 +44,15 @@ def __init__(self, one=None, ba=None):
3844 self .find_traj_is_best (provenance = 'Ephys aligned histology track' )
3945 self .traj ['Resolved' ]['is_best' ] = np .arange (len (self .traj ['Resolved' ]['traj' ]))
4046
47+ self .get_insertions_with_xyz ()
48+
4149 @staticmethod
4250 def get_traj_info (traj ):
4351 return traj ['probe_insertion' ], traj ['x' ], traj ['y' ]
4452
4553 def get_traj_for_provenance (self , provenance = 'Histology track' , django = None ,
4654 prov_dict = None ):
47-
55+ start = time . time ()
4856 if django is None :
4957 django = []
5058
@@ -62,6 +70,19 @@ def get_traj_for_provenance(self, provenance='Histology track', django=None,
6270 self .traj [prov_dict ]['ins' ] = np .array (ins_ids )
6371 self .traj [prov_dict ]['x' ] = np .array (x )
6472 self .traj [prov_dict ]['y' ] = np .array (y )
73+ end = time .time ()
74+ print (end - start )
75+
76+ def get_insertions_with_xyz (self ):
77+ start = time .time ()
78+ django_str = 'session__project__name__icontains,ibl_neuropixel_brainwide_01,' \
79+ 'json__has_key,xyz_picks'
80+ self .ins ['insertions' ] = self .one .alyx .rest ('insertions' , 'list' , django = django_str )
81+ self .ins ['ids' ] = np .array ([ins ['id' ] for ins in self .ins ['insertions' ]])
82+
83+ end = time .time ()
84+ print (end - start )
85+
6586
6687 def compute_best_for_provenance (self , provenance = 'Histology track' ):
6788 val = PROV_2_VAL [provenance ]
@@ -114,39 +135,13 @@ def find_traj_is_best(self, provenance='Histology track'):
114135 self .traj [provenance ]['is_best' ] = (self .traj [provenance ]['is_best' ]
115136 [np .where (np .invert (isin ))[0 ]])
116137
117- def get_channels (self , provenance ):
118-
119- depths = SITES_COORDINATES [:, 1 ]
120- start = time .time ()
121-
122- # Need to account for case when no insertions for planned and micro can skip the insertions
123- insertions = []
124- step = 150
125- for i in range (np .ceil (self .traj [provenance ]['ins' ].shape [0 ] / step ).astype (np .int )):
126- insertions += self .one .alyx .rest (
127- 'insertions' , 'list' , django = f"id__in,{ list (self .traj [provenance ]['ins' ][i * step :(i + 1 ) * step ])} " )
128-
129- ins_id = np .ravel ([np .where (ins ['id' ] == self .traj [provenance ]['ins' ])
130- for ins in insertions ])
131-
132- end = time .time ()
133- print (end - start )
138+ def get_all_channels (self , provenance ):
134139
140+ depths = SITES_COORDINATES [:, 1 ]
135141 start1 = time .time ()
136- for iT , ( traj , ins ) in enumerate (zip ( self .traj [provenance ]['traj' ][ ins_id ], insertions ) ):
142+ for iT , traj in enumerate (self .traj [provenance ]['traj' ]):
137143 try :
138- xyz_picks = np .array (ins ['json' ]['xyz_picks' ]) / 1e6
139- if traj ['provenance' ] == 'Histology track' :
140- xyz_picks = xyz_picks [np .argsort (xyz_picks [:, 2 ]), :]
141- xyz_channels = histology .interpolate_along_track (xyz_picks , (depths + TIP_SIZE_UM ) / 1e6 )
142- else :
143- align_key = ins ['json' ]['extended_qc' ]['alignment_stored' ]
144- feature = traj ['json' ][align_key ][0 ]
145- track = traj ['json' ][align_key ][1 ]
146- ephysalign = EphysAlignment (xyz_picks , depths , track_prev = track ,
147- feature_prev = feature ,
148- brain_atlas = self .ba , speedy = True )
149- xyz_channels = ephysalign .get_channel_locations (feature , track )
144+ xyz_channels = xyz_channels = self .get_channels (traj , depths )
150145
151146 if iT == 0 :
152147 all_channels = xyz_channels
@@ -158,23 +153,80 @@ def get_channels(self, provenance):
158153
159154 end = time .time ()
160155 print (end - start1 )
156+ iii = self .ba .bc .xyz2i (all_channels )
157+ keep_idx = np .setdiff1d (np .arange (all_channels .shape [0 ]), np .unique (np .where (iii < 0 )[0 ]))
158+ return all_channels [keep_idx , :]
159+
160+ def compute_coverage (self , all_channels ):
161+
162+ start = time .time ()
163+ cvol = np .zeros (self .ba .image .shape , dtype = np .float )
164+ val , counts = np .unique (self .ba ._lookup (all_channels ), return_counts = True )
165+ cvol [np .unravel_index (val , cvol .shape )] = counts
166+
167+ DIST_FCN = np .array ([100 , 150 ]) / 1e6
168+ dx = self .ba .bc .dx
169+ template = np .arange (- np .max (DIST_FCN ) - dx , np .max (DIST_FCN ) + 2 * dx , dx ) ** 2
170+ kernel = sum (np .meshgrid (template , template , template ))
171+ kernel = 1 - fcn_cosine (DIST_FCN )(np .sqrt (kernel ))
172+ #
173+ cvol = fftconvolve (cvol , kernel )
174+ end = time .time ()
175+ print (end - start )
176+
177+ return cvol
178+
179+ def add_coverage (self , x , y , depth = 4000 , theta = 10 , phi = 180 ):
180+ traj = {'x' : x ,
181+ 'y' : y ,
182+ 'z' : 0.0 ,
183+ 'phi' : phi ,
184+ 'theta' : theta ,
185+ 'depth' : depth ,
186+ 'provenance' : 'Planned' }
187+
188+ cov = histology .coverage ([traj ], self .ba )
189+ return cov , traj
190+
191+
192+ def get_channels (self , traj , depths = None ):
193+ if depths is None :
194+ depths = SITES_COORDINATES [:, 1 ]
195+ if traj ['provenance' ] == 'Planned' or traj ['provenance' ] == 'Micro-manipulator' :
196+ ins = atlas .Insertion .from_dict (traj )
197+ # Deepest coordinate first
198+ xyz = np .c_ [ins .tip , ins .entry ].T
199+ xyz_channels = histology .interpolate_along_track (xyz , (depths +
200+ TIP_SIZE_UM ) / 1e6 )
201+ else :
202+ ins_idx = np .where (traj ['probe_insertion' ] == self .ins ['ids' ])[0 ][0 ]
203+ xyz = np .array (self .ins ['insertions' ][ins_idx ]['json' ]['xyz_picks' ]) / 1e6
204+ if traj ['provenance' ] == 'Histology track' :
205+ xyz = xyz [np .argsort (xyz [:, 2 ]), :]
206+ xyz_channels = histology .interpolate_along_track (xyz , (depths +
207+ TIP_SIZE_UM ) / 1e6 )
208+ else :
209+ align_key = (self .ins ['insertions' ][ins_idx ]['json' ]['extended_qc' ]
210+ ['alignment_stored' ])
211+ feature = traj ['json' ][align_key ][0 ]
212+ track = traj ['json' ][align_key ][1 ]
213+ ephysalign = EphysAlignment (xyz , depths , track_prev = track ,
214+ feature_prev = feature ,
215+ brain_atlas = self .ba , speedy = True )
216+ xyz_channels = ephysalign .get_channel_locations (feature , track )
217+
218+ return xyz_channels
219+
220+ def get_brain_regions (self , traj ):
221+ depths = SITES_COORDINATES [:, 1 ]
222+ xyz_channels = self .get_channels (traj , depths )
223+ (region , region_label ,
224+ region_colour , _ ) = EphysAlignment .get_histology_regions (xyz_channels , depths ,
225+ brain_atlas = self .ba )
226+ return region , region_label , region_colour
227+
161228
162- return all_channels
163229
164- from scipy .signal import fftconvolve
165- cvol = np .zeros (ba .image .shape , dtype = np .float )
166- val , counts = np .unique (ba ._lookup (all_channels ), return_counts = True )
167- cvol [np .unravel_index (val , cvol .shape )] = counts
168- #
169- from ibllib .dsp import fcn_cosine
170- DIST_FCN = np .array ([100 , 150 ]) / 1e6
171- dx = ba .bc .dx
172- template = np .arange (- np .max (DIST_FCN ) - dx , np .max (DIST_FCN ) + 2 * dx , dx ) ** 2
173- kernel = sum (np .meshgrid (template , template , template ))
174- kernel = 1 - fcn_cosine (DIST_FCN )(np .sqrt (kernel ))
175- #
176- cvol = fftconvolve (cvol , kernel )
177- #
178230#
179231# cvol[np.unravel_index(ba._lookup(all_channels), cvol.shape)] = 1
180232
@@ -188,4 +240,51 @@ def get_channels(self, provenance):
188240# plt.add(actor)
189241# plt.show()
190242
191-
243+ #from vedo import *
244+ #from ibllib.atlas import AllenAtlas
245+ #ba = AllenAtlas()
246+ #import numpy as np
247+ #import vtk
248+ #la = ba.label
249+ #la[la != 0] = 1
250+ #vol2 = Volume(la).alpha([0, 0, 0.5])
251+ #vol = Volume(ba.image).alpha([0, 0, 0.8]).c('bone').pickable(False)
252+ ##vol = Volume(ba.image).c('bone').pickable(False)
253+ #
254+ #plane = vtk.vtkPlane()
255+ #clipping_planes = vtk.vtkPlaneCollection()
256+ #clipping_planes.AddItem(plane)
257+ #vol2.mapper().SetClippingPlanes(clipping_planes)
258+ ##
259+ #plane.SetOrigin(vol.center() + np.array([0, -100, 0]))
260+ #plane.SetNormal(0.5, 0.866, 0)
261+ ##
262+ #sl = vol.slicePlane(origin=vol.center() + np.array([0, 100, 50]), normal=(0.5, 0.866, 0))
263+ #s2 = vol.slicePlane(origin=vol.center(), normal=(0.5, 0.866, 0))
264+ #s3 = vol.slicePlane(origin=vol.center() + np.array([0, -100, -50]), normal=(0.5, 0.866, 0)) # this is 30 degree in coronal
265+ ##s3 = vol.slicePlane(origin=vol.center(), normal=(0.5, 0, 0.866)) # this is 30 degree in coronal
266+ #
267+ #sl.cmap('Purples_r').lighting('off').addScalarBar(title='Slice', c='w')
268+ #s2.cmap('Blues_r').lighting('off')
269+ #s3.cmap('Greens_r').lighting('off')
270+ #plt = show(vol, vol2, sl, s2, s3, __doc__, axes=9, bg='k', bg2='bb', interactive=False)
271+ #
272+ #interactive()
273+ #
274+ #from vedo.utils import versor
275+ #
276+ #
277+ #from vedo import *
278+ #
279+ #vol = Volume(dataurl+'embryo.slc').alpha([0,0,0.5]).c('k')
280+ #
281+ #slices = []
282+ #for i in range(4):
283+ # sl = vol.slicePlane(origin=[150,150,i*50+50], normal=(-1,0,1))
284+ # slices.append(sl)
285+ #
286+ #amap = [0, 1, 1, 1, 1] # hide low value points giving them alpha 0
287+ #mslices = merge(slices) # merge all slices into a single Mesh
288+ #mslices.cmap('hot_r', alpha=amap).lighting('off').addScalarBar3D()
289+ #
290+ #show(vol, mslices, __doc__, axes=1)
0 commit comments