class ReadMRIGraph: ''' Read 'tre' file for graphs genrated from MRI images''' def __init__(self, filepath): self.FilePath = filepath self.__ObjectType = [] self.__ObjectSubType = [] self.__NDims = [] self.__ID = [] self.__ParentID = [] self.__Color = [] self.__TransformMatrix = [] self.__Offset = [] self.__CenterOfRotation = [] self.__ElementSpacing = [] self.__Root = [] self.__Artery = [] self.__PointDim = [] self.__NPoints = [] self.__Points = [] self.__StartObjectIndices = [] self.__StartPointsIndices = [] # Private def __ReadFile(self): # read info from .tre with open(self.FilePath, 'r') as f: self.__Lines = f.readlines() Read = False for idx, line in enumerate(self.__Lines): if line.split()[0] == 'ObjectType' and line.split()[2] == 'Tube': Read = True self.__StartObjectIndices.append(idx) self.__ObjectType.append(line.split()[2:]) else: if line.split( )[0] == 'ObjectType' and line.split()[2] != 'Tube': Read = False if line.split()[0] == 'ObjectSubType' and Read: self.__ObjectSubType.append(line.split()[2:]) if line.split()[0] == 'NDims' and Read: self.__NDims.append(line.split()[2:]) if line.split()[0] == 'ID' and Read: self.__ID.append(line.split()[2:]) if line.split()[0] == 'ParentID' and Read: self.__ParentID.append(line.split()[2:]) if line.split()[0] == 'Color' and Read: self.__Color.append(line.split()[2:]) if line.split()[0] == 'TransformMatrix' and Read: self.__TransformMatrix.append(line.split()[2:]) if line.split()[0] == 'Offset' and Read: self.__Offset.append(line.split()[2:]) if line.split()[0] == 'CenterOfRotation' and Read: self.__CenterOfRotation.append(line.split()[2:]) if line.split()[0] == 'ElementSpacing' and Read: self.__ElementSpacing.append(line.split()[2:]) if line.split()[0] == 'Root' and Read: self.__Root.append(line.split()[2:]) if line.split()[0] == 'Artery' and Read: self.__Artery.append(line.split()[2:]) if line.split()[0] == 'PointDim' and Read: self.__PointDim.append(line.split()[2:]) if line.split()[0] == 'NPoints' and Read: self.__NPoints.append(line.split()[2:]) if line.split()[0] == 'Points' and Read: self.__StartPointsIndices.append(idx + 1) # number of points in each segment self.__NPoints = np.array(self.__NPoints).astype(int) self.__NPoints = self.__NPoints.ravel() def __ReadSegments(self): #read segments (tube objects) self.Segments = [] self.SegmentsRadii = [] for start, npoints in zip(self.__StartPointsIndices, self.__NPoints): s = self.__Lines[start:start + int(npoints)] s = [i.split() for i in s] s = np.array(s).astype(float) self.SegmentsRadii.append(s[:, 3]) self.Segments.append(s[:, (0, 1, 2)]) def __ReadNodes(self): # nodes from segments self.SegmentsNodes = [] self.SegmentsNodesRadii = [] self.NNodes = [] for i, r in zip(self.Segments, self.SegmentsRadii): nodes, _, ids = AssignToClusters(i) radii = [np.max(r[k]) for k in ids] self.SegmentsNodes.append(nodes) self.SegmentsNodesRadii.append(radii) self.NNodes.append(len(nodes)) def __CreateConnections(self): # connections from segments self.SegmentsConnections = [] for segment in self.SegmentsNodes: length = len(segment) Tree = sp.spatial.cKDTree(segment) c = [ Tree.query(i, k=3, distance_upper_bound=2.0)[1] for i in segment ] c = np.array(c) # obtain and fix connection from tree.query c1 = c[:, (0, 1)] exclude1 = np.where(c1[:, 1] >= len(segment)) c1[exclude1] = 0 c2 = c[:, (0, 2)] exclude2 = np.where(c2[:, 1] >= len(segment)) c2[exclude2] = 0 c = np.vstack((c1, c2)) self.SegmentsConnections.append(c) def __CreateGraph(self): # build graph self.Graph = Graph() totalnodes = 0 for nodes, c, radii, n in zip(self.SegmentsNodes, self.SegmentsConnections, self.SegmentsNodesRadii, self.NNodes): ind = np.array(range(n)) + totalnodes self.Graph.add_nodes_from(ind) for i, p, r in zip(ind, nodes, radii): self.Graph.node[i]['pos'] = p self.Graph.node[i]['r'] = r self.Graph.add_edges_from(c + totalnodes) totalnodes += n self.Graph.remove_edges_from(self.Graph.selfloop_edges()) self.Graph = fixG(self.Graph) # public def Update(self): self.__ReadFile() self.__ReadSegments() self.__ReadNodes() self.__CreateConnections() self.__CreateGraph() def GetSegmentsNodes(self): return self.SegmentsNodes def GetSegmentsNodesRadii(self): return self.SegmentsNodesRadii def GetSegmentsConnections(self): return self.SegmentsConnections def GetOutput(self): refine = RefineGraph(self.Graph) refine.Update() self.Graph = refine.GetOutput() return self.Graph
class ReadCenterlineCSV: ''' Class to create a graph given only a centerline (points supposed to have equal spacing between each other) Constructer Input: CSV file with columns: X, Y, Z, Radius ''' def __init__(self, filepath): self.FilePath = filepath self.Resolution = 1.0 self.ConnectionParam = 4 self.__X = [] self.__Y = [] self.__Z = [] self.__Radius = [] # Private def __ReadFile(self): # read info from .tre with open(self.FilePath, 'r') as f: self.__Lines = f.readlines() X = [i.split(',')[0] for i in self.__Lines] self.__X = X[1:] Y = [i.split(',')[1] for i in self.__Lines] self.__Y = Y[1:] Z = [i.split(',')[2] for i in self.__Lines] self.__Z = Z[1:] Radius = [i.split(',')[3] for i in self.__Lines] self.__Radius = np.array(Radius[1:]).astype(float) def __ReadNodes(self): # graph nodes from centerline self.GraphNodes = np.array([self.__X, self.__Y, self.__Z]).T self.GraphNodes = self.GraphNodes.astype('float') self.GraphNodes, ClustersPos, Clusters = AssignToClusters( self.GraphNodes, resolution=self.Resolution) self.GraphRadius = [ np.max([self.__Radius[i] for i in j]) for j in Clusters ] self.NNodes = len(self.GraphNodes) def __CreateConnections(self): # connections from graph nodes self.Connections = [] length = len(self.GraphNodes) Tree = sp.spatial.cKDTree(self.GraphNodes) c = [Tree.query(i, k=self.ConnectionParam)[1] for i in self.GraphNodes] c = np.array(c) connections = [] for i in range(self.ConnectionParam): # obtain and fix connection from tree.query if i > 0: cc = c[:, (0, i)] exclude = np.where(cc[:, 1] >= len(self.GraphNodes)) cc[exclude] = 0 connections.append(cc) self.Connections = np.vstack(tuple(connections)) def __CreateGraph(self): # build graph self.Graph = Graph() ind = np.array(range(self.NNodes)) self.Graph.add_nodes_from(ind) for i, p, r in zip(ind, self.GraphNodes, self.GraphRadius): self.Graph.node[i]['pos'] = p self.Graph.node[i]['r'] = r self.Graph.add_edges_from(self.Connections) self.Graph.remove_edges_from(self.Graph.selfloop_edges()) self.Graph = fixG(self.Graph) # public def Update(self, ConnectionParam=4, Resolution=0.75): ''' Update class Graph Input: ConnectionParam: control number of closest neighbors to a centreline point. Resolution: control at which resolution centerline points should sampled. Higher value imposes lower sampling rate. 0<'Resolution'<=1 Output: create NetworkX undirected graph ''' self.ConnectionParam = ConnectionParam self.Resolution = Resolution self.__ReadFile() self.__ReadNodes() self.__CreateConnections() self.__CreateGraph() def GetOutput(self): refine = RefineGraph(self.Graph) refine.Update() self.Graph = refine.GetOutput() return self.Graph