1- from typing import Optional
1+ from typing import Optional , Literal
22
33from collections import defaultdict
44import copy
1111import sys
1212
1313import numpy as np
14+ import numpy .typing as npt
15+
1416import networkx as nx
1517
1618import fastremap
@@ -79,12 +81,22 @@ class Skeleton:
7981
8082 Note that for backwards compatibility, skel.radius is treated
8183 specially and is synonymous with skel.radii.
84+
85+ default_attributes: when True, ensure that radius and vertex_types
86+ are initialized regardless of the content of extra_attributes.
87+ This was to conform to the SWC/Neuroglancer Precomputed
88+ conception of a skeleton.
8289 """
8390 def __init__ (self ,
84- vertices = None , edges = None ,
85- radii = None , vertex_types = None ,
86- segid = None , transform = None ,
87- space = 'voxel' , extra_attributes = None
91+ vertices :Optional [np .ndarray ] = None ,
92+ edges :Optional [np .ndarray ] = None ,
93+ radii :Optional [npt .NDArray [np .float32 ]] = None ,
94+ vertex_types :Optional [npt .NDArray [np .uint8 ]] = None ,
95+ segid :Optional [int ] = None ,
96+ transform :Optional [npt .NDArray [np .float32 ]] = None ,
97+ space :Literal ['voxel' , 'physical' ] = 'voxel' ,
98+ extra_attributes :Optional [list ] = None ,
99+ default_attributes :bool = True ,
88100 ):
89101 self .id = segid
90102 self .space = space
@@ -94,39 +106,42 @@ def __init__(self,
94106 elif type (vertices ) is list :
95107 self .vertices = np .array (vertices , dtype = np .float32 )
96108 else :
97- self .vertices = vertices . astype ( np . float32 )
109+ self .vertices = vertices
98110
99111 if edges is None :
100112 self .edges = np .array ([[]], dtype = np .uint32 ).reshape (0 ,2 )
101113 elif type (edges ) is list :
102114 self .edges = np .array (edges , dtype = np .uint32 )
103115 else :
104- self .edges = edges . astype ( np . uint32 )
116+ self .edges = edges
105117
106- if radii is None :
118+ if radii is None and default_attributes :
107119 self .radius = np .full (shape = self .vertices .shape [0 ], fill_value = - 1 , dtype = np .float32 )
108120 elif type (radii ) is list :
109- self .radius = np .array (radii , dtype = np .float32 )
110- else :
121+ self .radius = np .asarray (radii , dtype = np .float32 )
122+ elif radii is not None :
111123 self .radius = radii
112124
113- if vertex_types is None :
125+ if vertex_types is None and default_attributes :
114126 # 0 = undefined in SWC (http://research.mssm.edu/cnic/swc.html)
115127 self .vertex_types = np .zeros (shape = self .vertices .shape [0 ], dtype = np .uint8 )
116- elif type ( vertex_types ) is list :
117- self .vertex_types = np .array (vertex_types , dtype = np .uint8 )
118- else :
119- self .vertex_types = vertex_types . astype ( np . uint8 )
128+ elif vertex_types is not None :
129+ self .vertex_types = np .asarray (vertex_types , dtype = np .uint8 )
130+
131+ self .default_attributes = bool ( default_attributes )
120132
121133 if extra_attributes is None :
122- self .extra_attributes = self ._default_attributes ()
134+ if default_attributes :
135+ self .extra_attributes = self ._default_attributes ()
136+ else :
137+ self .extra_attributes = []
123138 else :
124139 self .extra_attributes = extra_attributes
125140
126141 if transform is None :
127142 self .transform = np .copy (IDENTITY )
128143 else :
129- self .transform = np .array (transform ).reshape ( (3 , 4 ) )
144+ self .transform = np .asarray (transform ).reshape ( (3 , 4 ) )
130145
131146 @classmethod
132147 def _default_attributes (self ):
0 commit comments