get_neuropixels_sample_shifts_from_probe does not work for NPX2 4sh with ONIX
Hi there, I have been trying to estimate the sample shift using the function "get_neuropixels_sample_shifts_from_probe", but it throws the following error.
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[79], line 3
1 from spikeinterface.extractors.neuropixels_utils import get_neuropixels_sample_shifts_from_probe
----> 3 sample_shifts = get_neuropixels_sample_shifts_from_probe(np2_config_b)
File [E:\py_script\spikeinterface\src\spikeinterface\extractors\neuropixels_utils.py:28](file:///E:/py_script/spikeinterface/src/spikeinterface/extractors/neuropixels_utils.py#line=27), in get_neuropixels_sample_shifts_from_probe(probe, stream_name)
11 """
12 Get the inter-sample shifts for Neuropixels probes based on the probe information.
13
(...)
25 Array of relative phase shifts for each channel.
26 """
27 # get inter-sample shifts based on the probe information and mux channels
---> 28 model_description = probe.annotations.get("description", None)
29 num_channels_per_adc = probe.annotations.get("num_channels_per_adc", None)
30 mux_channels = probe.contact_annotations.get("mux_channels", None)
AttributeError: 'ProbeGroup' object has no attribute 'annotations'`
The "np2_config_b" is loaded from a JSON file for a 4-shank NPX2 probe.
For now, I use the other function:get_neuropixels_sample_shifts. But this one is going to be deprecated in 0.104.0
Hi @alirezasaeedi1988 , the way we find the sample shifts has changed. Previously we were using a formula depending on which probe number etc you were using. This was unreliable, because we had to assume a lot about the probe (there are ~30 NP probes, see them all here: https://github.com/SpikeInterface/probeinterface_library/tree/main/imec). We weren't confident that the results were right for all the probes.
The tricky bit is figuring which channels belong to the same "sampling group" as each other. Now the sample groups are generated in ProbeInterface from metadata generated by the NP team (https://github.com/billkarsh/probetable/). Basically, we don't compute these groups on-the-fly anymore: they are now part of the probe metadata.
If you use OpenEphys or SpikeGLX, the probe is read in automatically with your recording. You can get the probe by doing probe = recording.get_probe(). Once you have this probe, you can feed it to get_neuropixels_sample_shifts_from_probe . Or you can read in the settings.xml file using pi.read_openephys, or the e.g. rec_g0_t0.imec0.ap.meta using pi.read_spikeglx.
If you want to use your own custom probe format, that seems a bit trickier. The groups are defined at the bottom of the probe_features.json file here: https://github.com/billkarsh/ProbeTable/blob/main/Tables/probe_features.json
If you do want to do this, and need some help, let me know :)
Hi @chrishalcrow ,
Thanks for your prompt response and useful info. I am loading a binary file and then load the probe configuration used for the recording. once I load the prob, it is considered as probe_group.
rec_b = se.read_binary(find_file(rec_subfolders[0],'np2-b-amp_*.raw'),
sampling_frequency=fs_hz,
dtype=np.uint16,
num_channels=num_channels,
gain_to_uV=gain_to_uV,
offset_to_uV=offset_to_uV)
rec_b.set_times(np.fromfile(find_file(rec_subfolders[0],'np2-b-clock_*.raw'), dtype=np.uint64).astype(np.double) / meta['acq_clk_hz'],
with_warning=False)
np2_config_b = probeinterface.io.read_probeinterface(find_file(base_folder, 'stag*.json'))
rec_b = rec_b.set_probegroup(np2_config_b,in_place=True)
I am not sure how to find the grouping. I am attaching one of my probe configs. stag_4_shank_2.json
Oups. We need to fix this when we handle probegroup and not probe! Sorry for this.
Sorry for the delay. This is a bit of a tricky problem - and I discussed with @samuelgarcia offline.
Just to understand your set up a little better: what software do you use to generate the probe file? Or: how do you generate the file? And I see that the probe has all 5120 channels. Usually the spike_glx and openephys loaders would pick the appropriate channels from the full 5120, based on the metadata they save. How do you choose the right subset of channels you’ve recorded on?
Your options right now (from easiest to hardest) are:
- Don’t compute the sample shifts
- Port our deprecated function (get_neuropixels_sample_shifts ) over to your codebase for the moment, and use it. Downside: we changed how we did this because we were worried it could be incorrect in some setups. So this isn’t really advisable.
- Use the mux_table info (https://github.com/billkarsh/ProbeTable/blob/main/Tables/probe_features.json). If it’s a normal NP2 probe, then its model is NP2013, which uses the z_mux_table “mux_np2000” (at the very bottom of the file). That’s string that starts
(24,16)(0 1 32 33 64 65 96 97 128 129 160 161 192 193 224 225 256 257 …. The first two numbers are numbers of different groups of channels (24), and the number of channels in each group (16). Then the long lists of numbers detail which channels are in each group. So channel ids 0,1,32,33… are in the first group. Using this, you can say which channel is in which group. It might be helpful to see our implementation here (https://github.com/SpikeInterface/probeinterface/blob/456c2795fe6147c44cc0f36a7f2b075703a13a01/src/probeinterface/neuropixels_tools.py#L114). Then you need to attach this to your probe as a annotation ("mux_channels").
To be honest, it’s gonna be pretty annoying to do the last option. Maybe if we understand your set up better, we can implement something that can help!
Thanks @chrishalcrow,
I generated the probe file using OpenEphys (Onix Source plugin) by manually selecting the channels and saving as a JSON file. Some information may be missing in this file @bparks13? Sorry to call you here, Brandon, but do you think that might be worth checking?
Hi @chrishalcrow @alirezasaeedi1988, I developed the ONIX Source plugin, so I can give some insight into this.
The JSON file that is saved is a full representation of the Probe Interface probe for a Neuropixels probe, so it does contain the full 5120 electrodes. The active electrodes can be determined by checking the indices where the device_channel_indices are >= 0. This was prompted because of the library that we use for Bonsai (OpenEphys.Onix1) handles the full representation of the ProbeInterface object and actually uses it for plotting / selecting electrodes for recording, and we want to maintain compatibility across both libraries so that the files were completely interchangeable.
From my (very limited) testing of the ProbeInterface loading functions, I believe that it automatically removes unused electrodes if the device channel indices are -1, but I could be wrong about that.
Since this plugin is part of the Open Ephys GUI, we can use the SpikeInterface load_openephys function to get the ephys data, but unfortunately the ProbeInterface integration does not exist yet as it is solely catered for other Neuropixels plugins (e.g., OneBox and Neuropixels-PXI). As a workaround for @alirezasaeedi1988 I recommended manually exporting this JSON file and attaching it to the recording object that gets created, but this comes with the downside of it does not have the sample shifts or any other metadata; it solely contains the physical representation of the electrodes as shown in the JSON file he sent.
I've been communicating with @alejoe91 on this issue to help get the integration started for the ONIX Source plugin, and I also created a PR yesterday that will automatically generate JSON files for the plugin so that this step can be automated down the road.
One major addition to the JSON files in this draft PR that was not there previously is the probe part number, which I believe should remove any ambiguity about what metadata to grab from the Probe Table, but please let me know if there is any information missing from the JSON file and we can try to add that.
Amazing!!
Yeah - the probe part number should uniquely determined the Probe Table information.
I'm happy to help with a PR modifiying pi.read_openephys on the ProbeInterface side. Let's discuss on the other issue :)
@alirezasaeedi1988 I think you should keep using get_neuropixels_sample_shifts until we make progress implementing an ONIX solution in ProbeInterface.
This is great!
I want to second Heberto here. Love the community involvement. Hopefully we can get this all patched up soon!