@@ -109,6 +109,37 @@ def fk(x, si=.002, dx=1, vbounds=None, btype='highpass', ntr_pad=0, ntr_tap=None
109109 return xf / gain
110110
111111
112+ def car (x , collection = None , lagc = 300 , butter_kwargs = None ):
113+ """
114+ Applies common average referencing with optional automatic gain control
115+ :param x: the input array to be filtered. dimension, the filtering is considering
116+ axis=0: spatial dimension, axis=1 temporal dimension. (ntraces, ns)
117+ :param collection:
118+ :param lagc: window size for time domain automatic gain control (no agc otherwise)
119+ :param butter_kwargs: filtering parameters: defaults: {'N': 3, 'Wn': 0.1, 'btype': 'highpass'}
120+ :return:
121+ """
122+ if butter_kwargs is None :
123+ butter_kwargs = {'N' : 3 , 'Wn' : 0.1 , 'btype' : 'highpass' }
124+ if collection is not None :
125+ xout = np .zeros_like (x )
126+ for c in np .unique (collection ):
127+ sel = collection == c
128+ xout [sel , :] = kfilt (x = x [sel , :], ntr_pad = 0 , ntr_tap = None , collection = None ,
129+ butter_kwargs = butter_kwargs )
130+ return xout
131+
132+ # apply agc and keep the gain in handy
133+ if not lagc :
134+ xf = np .copy (x )
135+ gain = 1
136+ else :
137+ xf , gain = agc (x , wl = lagc , si = 1.0 )
138+ # apply CAR and then un-apply the gain
139+ xf = xf - np .median (xf , axis = 0 )
140+ return xf / gain
141+
142+
112143def kfilt (x , collection = None , ntr_pad = 0 , ntr_tap = None , lagc = 300 , butter_kwargs = None ):
113144 """
114145 Applies a butterworth filter on the 0-axis with tapering / padding
@@ -209,13 +240,19 @@ def destripe(x, fs, neuropixel_version=1, butter_kwargs=None, k_kwargs=None, cha
209240 True: deduces the bad channels from the data provided
210241 :param butter_kwargs: (optional, None) butterworth params, see the code for the defaults dict
211242 :param k_kwargs: (optional, None) K-filter params, see the code for the defaults dict
243+ can also be set to 'car', in which case the median accross channels will be subtracted
212244 :return: x, filtered array
213245 """
214246 if butter_kwargs is None :
215247 butter_kwargs = {'N' : 3 , 'Wn' : 300 / fs * 2 , 'btype' : 'highpass' }
216248 if k_kwargs is None :
217249 k_kwargs = {'ntr_pad' : 60 , 'ntr_tap' : 0 , 'lagc' : 3000 ,
218250 'butter_kwargs' : {'N' : 3 , 'Wn' : 0.01 , 'btype' : 'highpass' }}
251+ spatial_fcn = lambda dat : kfilt (dat , ** k_kwargs ) # noqa
252+ elif isinstance (k_kwargs , dict ):
253+ spatial_fcn = lambda dat : kfilt (dat , ** k_kwargs ) # noqa
254+ else :
255+ spatial_fcn = lambda dat : car (dat , lagc = int (0.1 * fs )) # noqa
219256 h = neuropixel .trace_header (version = neuropixel_version )
220257 if channel_labels is True :
221258 channel_labels , _ = detect_bad_channels (x , fs )
@@ -231,9 +268,9 @@ def destripe(x, fs, neuropixel_version=1, butter_kwargs=None, k_kwargs=None, cha
231268 if channel_labels is not None :
232269 x = interpolate_bad_channels (x , channel_labels , h )
233270 inside_brain = np .where (channel_labels != 3 )[0 ]
234- x [inside_brain , :] = kfilt (x [inside_brain , :], ** k_kwargs ) # apply the k-filter
271+ x [inside_brain , :] = spatial_fcn (x [inside_brain , :]) # apply the k-filter
235272 else :
236- x = kfilt ( x , ** k_kwargs )
273+ x = spatial_fcn ( x )
237274 return x
238275
239276
@@ -245,7 +282,7 @@ def decompress_destripe_cbin(sr_file, output_file=None, h=None, wrot=None, appen
245282 Production version with optimized FFTs - requires pyfftw
246283 :param sr: seismic reader object (spikeglx.Reader)
247284 :param output_file: (optional, defaults to .bin extension of the compressed bin file)
248- :param h: (optional)
285+ :param h: (optional) neuropixel trace header. Dictionary with key 'sample_shift'
249286 :param wrot: (optional) whitening matrix [nc x nc] or amplitude scalar to apply to the output
250287 :param append: (optional, False) for chronic recordings, append to end of file
251288 :param nc_out: (optional, True) saves non selected channels (synchronisation trace) in output
@@ -273,7 +310,7 @@ def decompress_destripe_cbin(sr_file, output_file=None, h=None, wrot=None, appen
273310 k_kwargs = {'ntr_pad' : 60 , 'ntr_tap' : 0 , 'lagc' : 3000 ,
274311 'butter_kwargs' : {'N' : 3 , 'Wn' : 0.01 , 'btype' : 'highpass' }}
275312 h = neuropixel .trace_header (version = 1 ) if h is None else h
276- ncv = h ['x ' ].size # number of channels
313+ ncv = h ['sample_shift ' ].size # number of channels
277314 output_file = sr .file_bin .with_suffix ('.bin' ) if output_file is None else output_file
278315 assert output_file != sr .file_bin
279316 taper = np .r_ [0 , scipy .signal .windows .cosine ((SAMPLES_TAPER - 1 ) * 2 ), 0 ]
@@ -504,7 +541,7 @@ def nxcor(x, ref):
504541 xcor = channels_similarity (raw )
505542 fscale , psd = scipy .signal .welch (raw * 1e6 , fs = fs ) # units; uV ** 2 / Hz
506543
507- sos_hp = scipy .signal .butter (** {'N' : 3 , 'Wn' : 1000 / fs / 2 , 'btype' : 'highpass' }, output = 'sos' )
544+ sos_hp = scipy .signal .butter (** {'N' : 3 , 'Wn' : 300 / fs * 2 , 'btype' : 'highpass' }, output = 'sos' )
508545 hf = scipy .signal .sosfiltfilt (sos_hp , raw )
509546 xcorf = channels_similarity (hf )
510547
@@ -513,7 +550,7 @@ def nxcor(x, ref):
513550 'rms_raw' : rms (raw ), # very similar to the rms avfter butterworth filter
514551 'xcor_hf' : detrend (xcor , 11 ),
515552 'xcor_lf' : xcorf - detrend (xcorf , 11 ) - 1 ,
516- 'psd_hf' : np .mean (psd [:, fscale > 12000 ], axis = - 1 ),
553+ 'psd_hf' : np .mean (psd [:, fscale > ( fs / 2 * 0.8 ) ], axis = - 1 ), # 80% nyquists
517554 })
518555
519556 # make recommendation
0 commit comments