import numpy as np
from larcv import larcv
[docs]def parse_sparse2d(sparse_event_list):
"""
A function to retrieve sparse tensor input from larcv::EventSparseTensor2D object
Returns the data in format to pass to SCN
.. code-block:: yaml
schema:
input_data:
parser: parse_sparse2d
args:
sparse_event_list:
- sparse2d_pcluster_0 (, 0)
- sparse2d_pcluster_1 (, 1)
- ...
Configuration
-------------
sparse_event_list: list of larcv::EventSparseTensor2D
Optionally, give an array of (larcv::EventSparseTensor2D, int) for projection id
Returns
-------
voxels: np.ndarray(int32)
Coordinates with shape (N,2)
data: np.ndarray(float32)
Pixel values/channels with shape (N,C)
"""
meta = None
output = []
np_voxels = None
for sparse_event in sparse_event_list:
projection_id = 0 # default
if isinstance(sparse_event, tuple):
projection_id = sparse_event[1]
sparse_event = sparse_event[0]
tensor = sparse_event.sparse_tensor_2d(projection_id)
num_point = tensor.as_vector().size()
if meta is None:
meta = tensor.meta()
np_voxels = np.empty(shape=(num_point, 2), dtype=np.int32)
larcv.fill_2d_voxels(tensor, np_voxels)
# else:
# assert meta == tensor.meta()
np_data = np.empty(shape=(num_point, 1), dtype=np.float32)
larcv.fill_2d_pcloud(tensor, np_data)
output.append(np_data)
return np_voxels, np.concatenate(output, axis=-1)
[docs]def parse_sparse3d(sparse_event_list, features=None, hit_keys=[], nhits_idx=None):
"""
A function to retrieve sparse tensor input from larcv::EventSparseTensor3D object
Returns the data in format to pass to DataLoader
.. code-block:: yaml
schema:
input_data:
parser: parse_sparse3d
args:
sparse_event_list:
- sparse3d_pcluster_0
- sparse3d_pcluster_1
- ...
Configuration
-------------
sparse_event_list: list of larcv::EventSparseTensor3D
Can be repeated to load more features (one per feature).
features: int, optional
Default is None (ignored). If a positive integer is specified,
the sparse_event_list will be split in equal lists of length
`features`. Each list will be concatenated along the feature
dimension separately. Then all lists are concatenated along the
first dimension (voxels). For example, this lets you work with
distinct detector volumes whose input data is stored in separate
TTrees.`features` is required to be a divider of the `sparse_event_list`
length.
hit_keys: list of int, optional
Indices among the input features of the _hit_key_ TTrees that can be
used to infer the _nhits_ quantity (doublet vs triplet point).
nhits_idx: int, optional
Index among the input features where the _nhits_ feature (doublet vs triplet)
should be inserted.
Returns
-------
voxels: numpy array(int32) with shape (N,3)
Coordinates
data: numpy array(float32) with shape (N,C)
Pixel values/channels, as many channels as specified larcv::EventSparseTensor3D.
"""
split_sparse_event_list = [sparse_event_list]
if features is not None and features > 0:
if len(sparse_event_list) % features > 0:
raise Exception("features number in parse_sparse3d should be a divider of the sparse_event_list length.")
split_sparse_event_list = np.split(np.array(sparse_event_list), len(sparse_event_list) / features)
voxels, features = [], []
features_count = None
compute_nhits = len(hit_keys) > 0
if compute_nhits and nhits_idx is None:
raise Exception("nhits_idx needs to be specified if you want to compute the _nhits_ feature.")
for sparse_event_list in split_sparse_event_list:
if features_count is None:
features_count = len(sparse_event_list)
assert len(sparse_event_list) == features_count
meta = None
output = []
np_voxels = None
hit_key_array = []
for idx, sparse_event in enumerate(sparse_event_list):
num_point = sparse_event.as_vector().size()
if meta is None:
meta = sparse_event.meta()
np_voxels = np.empty(shape=(num_point, 3), dtype=np.int32)
larcv.fill_3d_voxels(sparse_event, np_voxels)
else:
assert meta == sparse_event.meta()
np_data = np.empty(shape=(num_point, 1), dtype=np.float32)
larcv.fill_3d_pcloud(sparse_event, np_data)
output.append(np_data)
if compute_nhits:
if idx in hit_keys:
hit_key_array.append(np_data)
voxels.append(np_voxels)
features_array = np.concatenate(output, axis=-1)
if compute_nhits:
hit_key_array = np.concatenate(hit_key_array, axis=-1)
doublets = (hit_key_array < 0).any(axis=1)
nhits = 3. * np.ones((np_voxels.shape[0],), dtype=np.float32)
nhits[doublets] = 2.
if nhits_idx < 0 or nhits_idx > features_array.shape[1]:
raise Exception("nhits_idx is out of range")
features_array = np.concatenate([features_array[..., :nhits_idx], nhits[:, None], features_array[..., nhits_idx:]], axis=-1)
features.append(features_array)
return np.concatenate(voxels, axis=0), np.concatenate(features, axis=0)
[docs]def parse_sparse3d_ghost(sparse_event_semantics):
"""
A function to retrieve sparse tensor input from larcv::EventSparseTensor3D object
Converts the sematic class to a ghost vs non-ghost label.
.. code-block:: yaml
schema:
ghost_label:
parser: parse_sparse3d
args:
sparse_event_semantics: sparse3d_semantics
Configuration
-------------
sparse_event_semantics: larcv::EventSparseTensor3D
Returns
-------
np.ndarray
a numpy array with the shape (N,3+1) where 3+1 represents
(x,y,z) coordinate and 1 stored ghost labels (channels).
"""
np_voxels, np_data = parse_sparse3d([sparse_event_semantics])
return np_voxels, (np_data==5).astype(np.float32)
[docs]def parse_sparse3d_charge_rescaled(sparse_event_list):
# Produces sparse3d_reco_rescaled on the fly on datasets that do not have it
np_voxels, output = parse_sparse3d(sparse_event_list)
deghost = output[:, -1] < 5
hit_charges = output[deghost, :3]
hit_ids = output[deghost, 3:6]
pmask = hit_ids > -1
_, inverse, counts = np.unique(hit_ids, return_inverse=True, return_counts=True)
multiplicity = counts[inverse].reshape(-1,3)
charges = np.sum((hit_charges*pmask)/multiplicity, axis=1)/np.sum(pmask, axis=1)
return np_voxels[deghost], charges.reshape(-1,1)