"""
Functions for parsing geometry data
"""
import numpy as np
from scipy.spatial.distance import pdist, squareform
def _parse_txt(path, n_channels):
"""Parse a geometry file in txt format
"""
f = open(path)
lines = f.readlines()
f.close()
geom = np.zeros((0, 2))
for i, line in zip(range(n_channels), lines):
line = line.replace('\r', '')
line = line.replace('\n', '')
row = line.split(' ')
geom = np.vstack((geom, row[:2])).astype('float')
return geom
[docs]def parse(path, n_channels):
"""
Parse a geometry txt (one x, y pair per line, separated
by spaces) or a npy file with shape (n_channels, 2), where every row
contains a x, y pair
path: str
Path to geometry file
n_channels: int
Number of channels
Returns
-------
numpy.ndarray
2-dimensional numpy array where each row contains the x, y coordinates
for a channel
Examples
--------
.. code-block:: python
from yass import geometry
geom = geometry.parse('path/to/geom.npy', n_channels=500)
geom = geometry.parse('path/to/geom.txt', n_channels=500)
"""
# TODO: infer the number of channels by the number of lines
extension = path.split('.')[-1]
if extension == 'txt':
geom = _parse_txt(path, n_channels)
elif extension == 'npy':
geom = np.load(path)
else:
raise ValueError('Invalid file: {} extension not supported'
.format(extension))
read_channels, _ = geom.shape
if read_channels != n_channels:
raise ValueError('Expected {} channels, but read {}'
.format(n_channels, read_channels))
return geom
[docs]def find_channel_neighbors(geom, radius):
"""Compute a channel neighbors matrix
Parameters
----------
geom: np.array
Array with the cartesian coordinates for the channels
radius: float
Maximum radius for the channels to be considered neighbors
Returns
-------
numpy.ndarray (n_channels, n_channels)
Symmetric boolean matrix with the i, j as True if the ith and jth
channels are considered neighbors
"""
return (squareform(pdist(geom)) <= radius)
[docs]def n_steps_neigh_channels(neighbors, steps):
"""Compute a neighbors matrix by considering neighbors of neighbors
Parameters
----------
neighbors: numpy.ndarray
Neighbors matrix
steps: int
Number of steps to still consider channels as neighbors
Returns
-------
numpy.ndarray (n_channels, n_channels)
Symmetric boolean matrix with the i, j as True if the ith and jth
channels are considered neighbors
"""
C = neighbors.shape[0]
output = np.eye(C, dtype='bool')
for j in range(steps):
for c in range(C):
output[c][np.sum(neighbors[output[c]], axis=0).astype('bool')] = 1
return output
# TODO: add documentation
# TODO: remove n_channels, we can infer it from neighbors or geom
[docs]def make_channel_groups(n_channels, neighbors, geom):
"""[DESCRIPTION]
Parameters
----------
n_channels: int
Number of channels
neighbors: numpy.ndarray
Neighbors matrix
geom: numpy.ndarray
geometry matrix
Returns
-------
list
List of channel groups based on [?]
"""
channel_groups = list()
c_left = np.array(range(n_channels))
neighChan_temp = np.array(neighbors)
while len(c_left) > 0:
c_tops = c_left[geom[c_left, 1] == np.max(geom[c_left, 1])]
c_topleft = c_tops[np.argmin(geom[c_tops, 0])]
c_group = np.where(
np.sum(neighChan_temp[neighChan_temp[c_topleft]], 0))[0]
neighChan_temp[c_group, :] = 0
neighChan_temp[:, c_group] = 0
for c in c_group:
c_left = np.delete(c_left, int(np.where(c_left == c)[0]))
channel_groups.append(c_group)
return channel_groups
[docs]def order_channels_by_distance(reference, channels, geom):
"""Order channels by distance using certain channel as reference
Parameters
----------
reference: int
Reference channel
channels: np.ndarray
Channels to order
geom
Geometry matrix
Returns
-------
numpy.ndarray
1D array with the channels ordered by distance using the reference
channels
numpy.ndarray
1D array with the indexes for the ordered channels
"""
coord_main = geom[reference]
coord_others = geom[channels]
idx = np.argsort(np.sum(np.square(coord_others - coord_main), axis=1))
return channels[idx], idx
[docs]def ordered_neighbors(geom, neighbors):
"""
Compute a list of arrays whose ith element contains the ordered
(by distance) neighbors for the ith channel
Parameters
----------
geom: numpy.ndarray
geometry matrix
neighbors: numpy.ndarray
Neighbors matrix
"""
n_channels, _ = neighbors.shape
# determine the max number of neighbors
max_neighbors = np.max(np.sum(neighbors, axis=0))
# build matrix filled with n_channels
channel_indexes = []
for c in range(n_channels):
# get neighbors for channel c
c_neighs = np.where(neighbors[c])[0]
# order neighbors by distance
ch_idx, _ = order_channels_by_distance(c, c_neighs, geom)
# set the row for channel c as their ordered neighbors
channel_indexes.append(ch_idx)
return channel_indexes, max_neighbors
def make_channel_index(neighbors, channel_geometry, steps=1):
C, C2 = neighbors.shape
if C != C2:
raise ValueError('neighbors is not a square matrix, verify')
# neighboring info
neighbors = n_steps_neigh_channels(neighbors, steps)
# neighboring channel info
nneigh = np.max(np.sum(neighbors, 0))
channel_index = np.ones((C, nneigh), 'int32')*C
for c_ref in range(C):
neighbor_channels = np.where(neighbors[c_ref])[0]
ch_idx, temp = order_channels_by_distance(c_ref, neighbor_channels,
channel_geometry)
channel_index[c_ref, :ch_idx.shape[0]] = ch_idx
return channel_index