from typing import List
import numpy as np
import pandas as pd
from scipy.spatial.distance import cdist
from scipy.special import expit
from ..classes.particle import Particle
[docs]def match_points_to_particles(ppn_points : np.ndarray,
particles : List[Particle],
semantic_type=None, ppn_distance_threshold=2):
"""Function for matching ppn points to particles.
For each particle, match ppn_points that have hausdorff distance
less than <threshold> and inplace update particle.ppn_candidates
If semantic_type is set to a class integer value,
points will be matched to particles with the same
predicted semantic type.
Parameters
----------
ppn_points : (N x 4 np.array)
PPN point array with (coords, point_type)
particles : list of <Particle> objects
List of particles for which to match ppn points.
semantic_type: int
If set to an integer, only match ppn points with prescribed
semantic type
ppn_distance_threshold: int or float
Maximum distance required to assign ppn point to particle.
Returns
-------
None (operation is in-place)
"""
if semantic_type is not None:
ppn_points_type = ppn_points[ppn_points[:, -1] == semantic_type]
else:
ppn_points_type = ppn_points
# TODO: Fix semantic type ppn selection
ppn_coords = ppn_points_type[:, :3]
for particle in particles:
dist = cdist(ppn_coords, particle.points)
matches = ppn_points_type[dist.min(axis=1) < ppn_distance_threshold]
particle.ppn_candidates = matches
# Deprecated
[docs]def get_track_endpoints(particle : Particle, verbose=False):
"""Function for getting endpoints of tracks (DEPRECATED)
Using ppn_candiates attached to <Particle>, get two
endpoints of tracks by farthest distance from the track's
spatial centroid.
Parameters
----------
particle : <Particle> object
Track particle for which to obtain endpoint coordinates
verbose : bool
If set to True, output print message indicating whether
particle has no or only one PPN candidate.
Returns
-------
endpoints : (2, 3) np.array
Xyz coordinates of two endpoints predicted or manually found
by network.
"""
if verbose:
print("Found {} PPN candidate points for particle {}".format(
particle.ppn_candidates.shape[0], particle.id))
if particle.semantic_type != 1:
raise AttributeError(
"Particle {} has type {}, can only give"\
" endpoints to tracks!".format(particle.id,
particle.semantic_type))
if particle.ppn_candidates.shape[0] == 0:
if verbose:
print("Particle {} has no PPN candidates!"\
" Running brute-force endpoint finder...".format(particle.id))
endpoints = get_track_endpoints_centroid(particle)
elif particle.ppn_candidates.shape[0] == 1:
if verbose:
print("Particle {} has only one PPN candidate!"\
" Running brute-force endpoint finder...".format(particle.id))
endpoints = get_track_endpoints_centroid(particle)
else:
centroid = particle.points.mean(axis=0)
ppn_coordinates = particle.ppn_candidates[:, :3]
dist = cdist(centroid.reshape(1, -1), ppn_coordinates).squeeze()
endpt_inds = dist.argsort()[-2:]
endpoints = particle.ppn_candidates[endpt_inds]
particle.endpoints = endpoints
assert endpoints.shape[0] == 2
return endpoints
[docs]def get_track_endpoints_centroid(particle):
"""Helper function for getting track endpoints.
Computes track endpoints without ppn predictions by
selecting the farthest two points from the coordinate centroid.
Parameters
----------
particle : <Particle> object
Returns
-------
endpoints : (2, 3) np.array
Xyz coordinates of two endpoints predicted or manually found
by network.
"""
coords = particle.points
centroid = coords.mean(axis=0)
dist = cdist(coords, centroid.reshape(1, -1))
inds = dist.squeeze().argsort()[-2:]
endpoints = coords[inds]
particle.endpoints = endpoints
return endpoints
# Deprecated
[docs]def get_shower_startpoint(particle : Particle, verbose=False):
"""Function for getting startpoint of EM showers. (DEPRECATED)
Using ppn_candiates attached to <Particle>, get one
startpoint of shower by nearest hausdorff distance.
Parameters
----------
particle : <Particle> object
Track particle for which to obtain endpoint coordinates
verbose : bool
If set to True, output print message indicating whether
particle has no or only one PPN candidate.
Returns
-------
endpoints : (2, 3) np.array
Xyz coordinates of two endpoints predicted or manually found
by network.
"""
if particle.semantic_type != 0:
raise AttributeError(
"Particle {} has type {}, can only give"\
" startpoints to shower fragments!".format(
particle.id, particle.semantic_type))
if verbose:
print("Found {} PPN candidate points for particle {}".format(
particle.ppn_candidates.shape[0], particle.id))
if particle.ppn_candidates.shape[0] == 0:
if verbose:
print("Particle {} has no PPN candidates!".format(particle.id))
startpoint = -np.ones(3)
else:
centroid = particle.points.mean(axis=0)
ppn_coordinates = particle.ppn_candidates[:, :3]
dist = np.linalg.norm((ppn_coordinates - centroid), axis=1)
index = dist.argsort()[0]
startpoint = ppn_coordinates[index]
particle.startpoint = startpoint
assert sum(startpoint.shape) == 3
return startpoint