Skip to content

Commit c2d156f

Browse files
authored
Merge pull request #495 from int-brain-lab/atlas
Atlas
2 parents 201851b + 154f526 commit c2d156f

File tree

4 files changed

+51
-17
lines changed

4 files changed

+51
-17
lines changed

ibllib/atlas/atlas.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,34 @@ def _lookup(self, xyz):
300300
"""
301301
return self._lookup_inds(self.bc.xyz2i(xyz))
302302

303-
def get_labels(self, xyz, mapping='Allen'):
303+
def get_labels(self, xyz, mapping='Allen', radius_um=None):
304304
"""
305305
Performs a 3D lookup from real world coordinates to the volume labels
306306
and return the regions ids according to the mapping
307307
:param xyz: [n, 3] array of coordinates
308308
:param mapping: brain region mapping (defaults to original Allen mapping)
309+
:param radius_um: if not null, returns a regions ids array and an array of proportion
310+
of regions in a sphere of size radius around the coordinates.
309311
:return: n array of region ids
310312
"""
311-
regions_indices = self._get_mapping(mapping=mapping)[self.label.flat[self._lookup(xyz)]]
312-
return self.regions.id[regions_indices]
313+
if radius_um:
314+
nrx = int(np.ceil(radius_um / abs(self.bc.dx) / 1e6))
315+
nry = int(np.ceil(radius_um / abs(self.bc.dy) / 1e6))
316+
nrz = int(np.ceil(radius_um / abs(self.bc.dz) / 1e6))
317+
nr = [nrx, nry, nrz]
318+
iii = self.bc.xyz2i(xyz)
319+
# computing the cube radius and indices is more complicated as volume indices are not
320+
# necessariy in ml, ap, dv order so the indices order is dynamic
321+
rcube = np.meshgrid(*tuple((np.arange(
322+
-nr[i], nr[i] + 1) * self.bc.dxyz[i]) ** 2 for i in self.xyz2dims))
323+
rcube = np.sqrt(rcube[0] + rcube[1], rcube[2]) * 1e6
324+
icube = tuple(slice(-nr[i] + iii[i], nr[i] + iii[i] + 1) for i in self.xyz2dims)
325+
cube = self.regions.mappings[mapping][self.label[icube]]
326+
ilabs, counts = np.unique(cube[rcube <= radius_um], return_counts=True)
327+
return self.regions.id[ilabs], counts / np.sum(counts)
328+
else:
329+
regions_indices = self._get_mapping(mapping=mapping)[self.label.flat[self._lookup(xyz)]]
330+
return self.regions.id[regions_indices]
313331

314332
def _get_mapping(self, mapping='Allen'):
315333
"""

ibllib/atlas/beryl.npy

-56 Bytes
Binary file not shown.

ibllib/io/extractors/training_audio.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
import logging
55

66
import numpy as np
7-
from scipy import signal
7+
import scipy.signal
8+
import scipy.ndimage
89
from scipy.io import wavfile
910

11+
1012
from neurodsp.utils import WindowGenerator
1113
from neurodsp import fourier
1214
import ibllib.io.raw_data_loaders as ioraw
1315
from ibllib.io.extractors.training_trials import GoCueTimes
1416

17+
1518
logger_ = logging.getLogger('ibllib')
1619

1720
NS_WIN = 2 ** 18 # 2 ** np.ceil(np.log2(1 * fs))
@@ -22,16 +25,19 @@
2225
READY_TONE_SPL = 85
2326

2427

25-
def _running_mean(x, N):
26-
cumsum = np.cumsum(np.insert(x, 0, 0))
27-
return (cumsum[N:] - cumsum[:-N]) / N
28-
29-
30-
def _detect_ready_tone(w, fs):
28+
def detect_ready_tone(w, fs, ftone=FTONE, threshold=0.8):
29+
"""
30+
Detects a transient sinusoid signal in a time-serie
31+
:param w: audio time seried
32+
:param fs: sampling frequency (Hz)
33+
:param ftone: frequency of the tone to detect
34+
:param threshold: ratio of the Hilbert to the signal, between 0 and 1 (set to 0.8)
35+
:return:
36+
"""
3137
# get envelope of DC free signal and envelope of BP signal around freq of interest
32-
h = np.abs(signal.hilbert(w - np.median(w)))
33-
fh = np.abs(signal.hilbert(fourier.bp(w, si=1 / fs, b=FTONE * np.array([0.9, 0.95, 1.15, 1.1]))))
34-
dtect = _running_mean(fh / (h + 1e-3), int(fs * 0.1)) > 0.8
38+
h = np.abs(scipy.signal.hilbert(w - np.median(w)))
39+
fh = np.abs(scipy.signal.hilbert(fourier.bp(w, si=1 / fs, b=ftone * np.array([0.9, 0.95, 1.15, 1.1]))))
40+
dtect = scipy.ndimage.uniform_filter1d(fh / (h + 1e-3), int(fs * 0.1)) > threshold
3541
return np.where(np.diff(dtect.astype(int)) == 1)[0]
3642
# tone = np.sin(2 * np.pi * FTONE * np.arange(0, fs * 0.1) / fs)
3743
# tone = tone / np.sum(tone ** 2)
@@ -56,7 +62,7 @@ def _get_conversion_factor(unit=UNIT, ready_tone_spl=READY_TONE_SPL):
5662
return fac
5763

5864

59-
def welchogram(fs, wav, nswin=NS_WIN, overlap=OVERLAP, nperseg=NS_WELCH):
65+
def welchogram(fs, wav, nswin=NS_WIN, overlap=OVERLAP, nperseg=NS_WELCH, detect_kwargs=None):
6066
"""
6167
Computes a spectrogram on a very large audio file.
6268
@@ -65,6 +71,7 @@ def welchogram(fs, wav, nswin=NS_WIN, overlap=OVERLAP, nperseg=NS_WELCH):
6571
:param nswin: n samples of the sliding window
6672
:param overlap: n samples of the overlap between windows
6773
:param nperseg: n samples for the computation of the spectrogram
74+
:param detect_kwargs: specified paramaters for detection
6875
:return: tscale, fscale, downsampled_spectrogram
6976
"""
7077
ns = wav.shape[0]
@@ -78,16 +85,17 @@ def welchogram(fs, wav, nswin=NS_WIN, overlap=OVERLAP, nperseg=NS_WELCH):
7885
# load the current window into memory
7986
w = np.float64(wav[first:last]) * _get_conversion_factor()
8087
# detection of ready tones
81-
a = [d + first for d in _detect_ready_tone(w, fs)]
88+
detect_kwargs = detect_kwargs or {}
89+
a = [d + first for d in detect_ready_tone(w, fs, **detect_kwargs)]
8290
if len(a):
8391
detect += a
8492
# the last window may not allow a pwelch
8593
if (last - first) < nperseg:
8694
continue
8795
# compute PSD estimate for the current window
8896
iw = window_generator.iw
89-
_, W[iw, :] = signal.welch(w, fs=fs, window='hann', nperseg=nperseg, axis=-1,
90-
detrend='constant', return_onesided=True, scaling='density')
97+
_, W[iw, :] = scipy.signal.welch(w, fs=fs, window='hann', nperseg=nperseg, axis=-1,
98+
detrend='constant', return_onesided=True, scaling='density')
9199
# the onset detection may have duplicates with sliding window, average them and remove
92100
detect = np.sort(np.array(detect)) / fs
93101
ind = np.where(np.diff(detect) < 0.1)[0]

ibllib/tests/test_atlas.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,14 @@ def test_lookups(self):
347347
assert self.ba.get_labels([0, 0, self.ba.bc.i2z(103)], mapping='Cosmos') == 997
348348
assert self.ba.get_labels([0, 0, 0], mapping='Cosmos') == 0
349349

350+
def test_lookups_probabilistic(self):
351+
xyz = np.array([0, -.0058, -.0038])
352+
aid = self.ba.get_labels(xyz, mapping='Beryl')
353+
aids, proportions = self.ba.get_labels(xyz, mapping='Beryl', radius_um=250)
354+
self.assertEqual(aid, 0)
355+
assert (np.all(aids == np.array([0])))
356+
assert (np.isclose(proportions, np.array([1.])))
357+
350358
def test_slice(self):
351359
ba = self.ba
352360
nx, ny, nz = ba.bc.nxyz

0 commit comments

Comments
 (0)