Ejemplo n.º 1
0
def ReadGraphfromMat(filename):

    f = sio.loadmat(filename)
    mat = f['im2'][0, 0]

    nX = int(mat['nX'])
    nY = int(mat['nY'])
    nZ = int(mat['nZ'])

    scale = mat['Hvox'][0]

    xx = int(nX * scale[0])
    yy = int(nY * scale[1])
    zz = int(nZ * scale[2])

    # read nodes
    pos = mat['nodePos'].astype(float)
    radii = mat['nodeDiam'].T

    # read edges
    edg = (mat['nodeEdges']).astype('int')
    connections = []
    for i in range(len(edg)):
        connections.append((edg[i, 0] - 1, edg[i, 1] - 1))

    from VascGraph.GeomGraph import Graph
    G = Graph()
    G.add_nodes_from(range(pos.shape[0]))
    G.add_edges_from(connections)

    for i, p, r in zip(G.GetNodes(), pos, radii):
        G.node[i]['pos'] = p
        G.node[i]['r'] = r

    return G
Ejemplo n.º 2
0
    def Update(self):

        try:
            x1, x2 = self.xls['x1'], self.xls['x2']
            y1, y2 = self.xls['y1'], self.xls['y2']
            z1, z2 = self.xls['z1'], self.xls['z2']

        except:

            x1, x2 = self.xls['V1 x'], self.xls['V2 x']
            y1, y2 = self.xls['V1 y'], self.xls['V2 y']
            z1, z2 = self.xls['V1 z'], self.xls['V2 z']

        ps = [(i, j, k) for i, j, k in zip(x1, y1, z1)]  # start node
        pe = [(i, j, k) for i, j, k in zip(x2, y2, z2)]  # end node

        # all nodes with their id's and pos's
        p = list(set(ps).union(set(pe)))
        pid = dict()
        pos = dict()
        for idx, i in enumerate(p):
            pid[str(i)] = idx
            pos[idx] = i

        # graph
        nodes = range(len(p))
        e = [(pid[str(i)], pid[str(j)]) for i, j in zip(ps, pe)]
        edges = [i for i in e if i[0] != i[1]]

        g = Graph()
        g.add_nodes_from(nodes)
        g.add_edges_from(edges)

        for i in g.GetNodes():
            g.node[i]['pos'] = np.array(pos[i])

        self.Graph = g
Ejemplo n.º 3
0
class GenerateGraph:
    def __init__(self, Label):

        self.Label = Label
        self.Shape = np.shape(self.Label)  # size of image
        self.Length = self.Shape[0] * self.Shape[1] * self.Shape[
            2]  # number of voxels
        self.__ComputeArea()

    # private

    def __ComputeArea(self):

        self.Area = np.sum(self.Label > 0)

    def __CalculateDistMap(self):
        #
        #        XY=[self.Label[i,:,:] for i in range(self.Shape[0])] #Z-XY
        #        ZX=[self.Label[:,:,i] for i in range(self.Shape[2])] #Y-ZX
        #        ZY=[self.Label[:,i,:] for i in range(self.Shape[1])] #X-ZY
        #
        #        DistXY=np.array([image.morphology.distance_transform_edt(i) for i in XY])
        #        DistZX=np.array([image.morphology.distance_transform_edt(i) for i in ZX])
        #        DistZY=np.array([image.morphology.distance_transform_edt(i) for i in ZY])
        #
        #        DistZX=np.rollaxis(DistZX, 0, 3)
        #        DistZY=np.rollaxis(DistZY, 0, 2)
        #
        #        DistMap_=np.maximum(DistXY, DistZX)
        #        DistMap=np.maximum(DistMap_, DistZY)
        #
        #        DistMap=filt.maximum_filter(DistMap, size=(3,3,3))

        DistMap = image.morphology.distance_transform_edt(self.Label)
        self.DistMap = DistMap

    def __AssignDistMapToGraph(self):
        '''
        Assign dist values to graph nodes 
        '''
        Nodes = self.Graph.GetNodes()

        for i in Nodes:

            Pos = tuple(self.Graph.node[i]['pos'].astype(int))

            if Pos[0] < self.Shape[0] and Pos[1] < self.Shape[1] and Pos[
                    2] < self.Shape[2]:

                Dist = self.DistMap[Pos]

                if Dist < 1:
                    Dist = 1

                self.Graph.node[i]['r'] = Dist

            else:
                self.Graph.node[i]['r'] = 1

    def __GenerateRandomGraphFromLabel(self):

        #random sampling
        x = np.random.uniform(low=0,
                              high=self.Shape[0],
                              size=self.NInitialNodes).tolist()
        y = np.random.uniform(low=0,
                              high=self.Shape[1],
                              size=self.NInitialNodes).tolist()
        z = np.random.uniform(low=0,
                              high=self.Shape[2],
                              size=self.NInitialNodes).tolist()

        NodesIndices = self.Label[(np.floor(x).astype('int'),
                                   np.floor(y).astype('int'),
                                   np.floor(z).astype('int'))] > 0

        Index = np.array([x, y, z]).T
        NodesPos = Index[NodesIndices]

        # build graph
        self.NNodes = len(NodesPos)
        self.Graph = Graph()
        self.Graph.add_nodes_from(range(self.NNodes))

        # assign positions to nodes
        for i, p in zip(self.Graph.GetNodes(), NodesPos):
            self.Graph.node[i]['pos'] = p

        # build connectivity
        Tree = sp.spatial.cKDTree(NodesPos)
        NeigborsIndices = Tree.query(NodesPos, k=self.Connection + 1)[1]

        Edges = []
        for ind, i in enumerate(NeigborsIndices):
            Neigbours = np.unique(i)
            c = [[ind, j] for j in Neigbours if j != ind and j != self.NNodes]
            if c:
                Edges.append(c)

        #assign connections
        Edges = [j for i in Edges for j in i]  # unravel
        self.Graph.add_edges_from(Edges)

    def __GenerateRandomGridGraphFromLabel(self):

        IndexTrueVoxels = np.where(self.Label)
        Index = np.array(IndexTrueVoxels).T

        #Limit NNodes to # of ture voxels
        if self.NNodes > len(Index): self.NNodes = len(Index)

        # probibility of true voxels
        Probability = (self.Label).astype(float) / np.sum(self.Label)
        Probability = Probability[IndexTrueVoxels]

        # obtain nodes
        NodesIndices = np.random.choice(range(len(Probability)),
                                        self.NNodes,
                                        p=Probability)
        NodesPos = Index[NodesIndices]

        # build graph
        self.Graph = Graph()
        self.Graph.add_nodes_from(range(self.NNodes))

        # assign positions to nodes
        for i, p in zip(self.Graph.GetNodes(), NodesPos):
            self.Graph.node[i]['pos'] = p

        # build connectivity
        Tree = sp.spatial.cKDTree(NodesPos)
        NeigborsIndices = Tree.query(NodesPos, k=self.Connection + 1)[1]

        Edges = []
        for ind, i in enumerate(NeigborsIndices):
            Neigbours = np.unique(i)
            c = [[ind, j] for j in Neigbours if j != ind and j != self.NNodes]
            if c:
                Edges.append(c)

        #assign connections
        Edges = [j for i in Edges for j in i]  # unravel
        self.Graph.add_edges_from(Edges)

    def __GenerateGridGraphFromLabel(self):
        def VoxelsPositions(Label, Shape, Length):
            '''
            Shape: shape of array     
            indexing in order: rows by row->depth
            '''
            # positions of each voxel
            z, x, y = np.meshgrid(range(Shape[0]),
                                  range(Shape[1]),
                                  range(Shape[2]),
                                  indexing='ij')
            x = x[Label.astype(bool)]
            y = y[Label.astype(bool)]
            z = z[Label.astype(bool)]
            VoxelsPos = np.transpose([z, x, y])

            return VoxelsPos

        def GetConnections(Label, Shape, Length):

            # connections from pathways on array grid
            Array = (np.reshape(range(Length), Shape) + 1) * Label

            # incides of voxels in the Array
            VoxelsIndices = Array[Label.astype(bool)]

            #--------
            path1 = iter(
                np.transpose(
                    [Array[:, :, 0:-1].ravel(), Array[:, :, 1:].ravel()]))
            path1 = (i for i in path1 if all(i))
            #--------
            path2 = iter(
                np.transpose([
                    np.swapaxes(Array[:, 0:-1, :], 1, 2).ravel(),
                    np.swapaxes(Array[:, 1:, :], 1, 2).ravel()
                ]))
            path2 = (i for i in path2 if all(i))
            #--------
            path3 = iter(
                np.transpose([
                    np.swapaxes(Array[0:-1, :, :], 0, 2).ravel(),
                    np.swapaxes(Array[1:, :, :], 0, 2).ravel()
                ]))
            path3 = (i for i in path3 if all(i))

            return VoxelsIndices, path1, path2, path3

        if self.Sampling is not None:
            Scale = (1.0 / self.Sampling, 1.0 / self.Sampling,
                     1.0 / self.Sampling)
            Label = image.zoom(self.Label.astype(int), Scale)
            Shape = np.shape(Label)  # size of image
            Length = Shape[0] * Shape[1] * Shape[2]  # number of voxels
        else:
            Label = self.Label
            Shape = self.Shape
            Length = self.Length

        # voxel indices and thier positions
        t1 = time()
        VoxelsPos = VoxelsPositions(Label, Shape, Length)
        print('create nodes: ' + str(time() - t1))

        t1 = time()
        VoxelsIndices, Connections1, Connections2, Connections3 = GetConnections(
            Label, Shape, Length)
        print('create connections: ' + str(time() - t1))

        # build graph
        t1 = time()
        self.Graph = Graph()
        self.Graph.add_nodes_from(VoxelsIndices)
        for ind, p in zip(VoxelsIndices, VoxelsPos):
            self.Graph.node[ind]['pos'] = p

        self.Graph.add_edges_from(Connections1)
        self.Graph.add_edges_from(Connections2)
        self.Graph.add_edges_from(Connections3)

        #exclude nodes with less than 2 neighbors
        NNodesToExclude = 1
        while NNodesToExclude > 0:
            NodesToExclude = [
                i for i in self.Graph.GetNodes()
                if len(self.Graph.GetNeighbors(i)) <= 2
            ]
            self.Graph.remove_nodes_from(NodesToExclude)
            NNodesToExclude = len(NodesToExclude)

        if self.Sampling is not None:
            for i in self.Graph.GetNodes():
                self.Graph.node[i][
                    'pos'] = self.Graph.node[i]['pos'] * self.Sampling

        print('create graph: ' + str(time() - t1))

    # public
    def UpdateRandomGraph(self, connection=8, nInitialNodes=100000):
        self.Connection = connection
        self.NInitialNodes = nInitialNodes
        self.__GenerateRandomGraphFromLabel()

    def UpdateRandomGridGraph(self, connection=8, nNodes=100000):
        self.Connection = connection
        self.NNodes = nNodes
        self.__GenerateRandomGridGraphFromLabel()

    def UpdateGridGraph(self, Sampling=None):
        if Sampling is not None:
            self.Sampling = float(Sampling)
        else:
            self.Sampling = Sampling
        self.__GenerateGridGraphFromLabel()

    def GetOutput(self):
        self.__CalculateDistMap()
        self.__AssignDistMapToGraph()
        self.Graph = fixG(self.Graph)
        self.Graph.Area = self.Area
        return self.Graph

    def GetArea(self):
        return self.Area

    def GetDistMap(self):
        self.__CalculateDistMap()
        return self.DistMap
Ejemplo n.º 4
0
class GenerateGraph:
    def __init__(self, Label, DisMap=None, label_ext=False):

        self.Label = Label
        self.label_ext = label_ext
        self.Shape = np.shape(self.Label)  # size of image
        self.Length = self.Shape[0] * self.Shape[1] * self.Shape[
            2]  # number of voxels
        self.__ComputeArea()
        self.DistMap = DisMap

    # private

    def __ComputeArea(self):

        self.Area = np.sum(self.Label > 0)

    def __CalculateDistMap(self):

        if self.DistMap is None:
            #self.DistMap=image.morphology.distance_transform_edt(self.Label)
            self.DistMap = DistMap3D(self.Label)

    def __AssignDistMapToGraph(self):
        '''
        Assign dist values to graph nodes 
        '''
        Nodes = self.Graph.GetNodes()

        for i in Nodes:

            Pos = tuple(self.Graph.node[i]['pos'].astype(int))

            if Pos[0] < self.Shape[0] and Pos[1] < self.Shape[1] and Pos[
                    2] < self.Shape[2]:

                Dist = self.DistMap[Pos]

                if Dist < 1:
                    Dist = 1

                self.Graph.node[i]['r'] = Dist

            else:
                self.Graph.node[i]['r'] = 1

    def __GenerateRandomGraphFromLabel(self):

        #random sampling
        x = np.random.uniform(low=0,
                              high=self.Shape[0],
                              size=self.NInitialNodes).tolist()
        y = np.random.uniform(low=0,
                              high=self.Shape[1],
                              size=self.NInitialNodes).tolist()
        z = np.random.uniform(low=0,
                              high=self.Shape[2],
                              size=self.NInitialNodes).tolist()

        NodesIndices = self.Label[(np.floor(x).astype('int'),
                                   np.floor(y).astype('int'),
                                   np.floor(z).astype('int'))] > 0

        Index = np.array([x, y, z]).T
        NodesPos = Index[NodesIndices]

        # build graph
        self.NNodes = len(NodesPos)
        self.Graph = Graph()
        self.Graph.add_nodes_from(range(self.NNodes))

        # assign positions to nodes
        for i, p in zip(self.Graph.GetNodes(), NodesPos):
            self.Graph.node[i]['pos'] = p

        # build connectivity
        Tree = sp.spatial.cKDTree(NodesPos)
        NeigborsIndices = Tree.query(NodesPos, k=self.Connection + 1)[1]

        Edges = []
        for ind, i in enumerate(NeigborsIndices):
            Neigbours = np.unique(i)
            c = [[ind, j] for j in Neigbours if j != ind and j != self.NNodes]
            if c:
                Edges.append(c)

        #assign connections
        Edges = [j for i in Edges for j in i]  # unravel
        self.Graph.add_edges_from(Edges)

    def __GenerateRandomGridGraphFromLabel(self):

        IndexTrueVoxels = np.where(self.Label)
        Index = np.array(IndexTrueVoxels).T

        #Limit NNodes to # of ture voxels
        if self.NNodes > len(Index): self.NNodes = len(Index)

        # probibility of true voxels
        Probability = (self.Label).astype(float) / np.sum(self.Label)
        Probability = Probability[IndexTrueVoxels]

        # obtain nodes
        NodesIndices = np.random.choice(range(len(Probability)),
                                        self.NNodes,
                                        p=Probability)
        NodesPos = Index[NodesIndices]

        # build graph
        self.Graph = Graph()
        self.Graph.add_nodes_from(range(self.NNodes))

        # assign positions to nodes
        for i, p in zip(self.Graph.GetNodes(), NodesPos):
            self.Graph.node[i]['pos'] = p

        # build connectivity
        Tree = sp.spatial.cKDTree(NodesPos)
        NeigborsIndices = Tree.query(NodesPos, k=self.Connection + 1)[1]

        Edges = []
        for ind, i in enumerate(NeigborsIndices):
            Neigbours = np.unique(i)
            c = [[ind, j] for j in Neigbours if j != ind and j != self.NNodes]
            if c:
                Edges.append(c)

        #assign connections
        Edges = [j for i in Edges for j in i]  # unravel
        self.Graph.add_edges_from(Edges)

    def __GenerateGridGraphFromLabel(self):
        def VoxelsPositions(Label, Shape, Length):
            '''
            Shape: shape of array     
            indexing in order: rows by row->depth
            '''
            # positions of each voxel
            z, x, y = np.meshgrid(range(Shape[0]),
                                  range(Shape[1]),
                                  range(Shape[2]),
                                  indexing='ij')
            x = x[Label.astype(bool)]
            y = y[Label.astype(bool)]
            z = z[Label.astype(bool)]
            VoxelsPos = np.transpose([z, x, y])

            return VoxelsPos

        def GetConnections(Label, Shape, Length):

            # connections from pathways on array grid
            Array = (np.reshape(range(Length), Shape) + 1) * Label

            # incides of voxels in the Array
            VoxelsIndices = Array[Label.astype(bool)]

            #--------
            path1 = iter(
                np.transpose(
                    [Array[:, :, 0:-1].ravel(), Array[:, :, 1:].ravel()]))
            path1 = (i for i in path1 if all(i))
            #--------
            path2 = iter(
                np.transpose([
                    np.swapaxes(Array[:, 0:-1, :], 1, 2).ravel(),
                    np.swapaxes(Array[:, 1:, :], 1, 2).ravel()
                ]))
            path2 = (i for i in path2 if all(i))
            #--------
            path3 = iter(
                np.transpose([
                    np.swapaxes(Array[0:-1, :, :], 0, 2).ravel(),
                    np.swapaxes(Array[1:, :, :], 0, 2).ravel()
                ]))
            path3 = (i for i in path3 if all(i))

            return VoxelsIndices, path1, path2, path3

        if self.Sampling is not None:
            Scale = (1.0 / self.Sampling, 1.0 / self.Sampling,
                     1.0 / self.Sampling)
            Label = image.zoom(self.Label.astype(int), Scale)
            Shape = np.shape(Label)  # size of image
            Length = Shape[0] * Shape[1] * Shape[2]  # number of voxels
        else:
            Label = self.Label
            Shape = self.Shape
            Length = self.Length

        # voxel indices and thier positions
        t1 = time()
        VoxelsPos = VoxelsPositions(Label, Shape, Length)
        print('create nodes: ' + str(time() - t1))

        t1 = time()
        VoxelsIndices, Connections1, Connections2, Connections3 = GetConnections(
            Label, Shape, Length)
        print('create connections: ' + str(time() - t1))

        # build graph
        t1 = time()
        self.Graph = Graph()
        self.Graph.add_nodes_from(VoxelsIndices)
        for ind, p in zip(VoxelsIndices, VoxelsPos):
            self.Graph.node[ind]['pos'] = p

        self.Graph.add_edges_from(Connections1)
        self.Graph.add_edges_from(Connections2)
        self.Graph.add_edges_from(Connections3)

        #exclude nodes with less than 2 neighbors
        # NNodesToExclude=1
        # while NNodesToExclude>0:
        #     NodesToExclude=[i for i in self.Graph.GetNodes() if len(self.Graph.GetNeighbors(i))<=2]
        #     self.Graph.remove_nodes_from(NodesToExclude)
        #     NNodesToExclude=len(NodesToExclude)

        # reconnect nodes with less that 2 edges (request from Sreekanth )
        pos = np.array(self.Graph.GetNodesPos())
        nodes = np.array(self.Graph.GetNodes())
        NodesToModify = [
            i for i in self.Graph.GetNodes()
            if len(self.Graph.GetNeighbors(i)) <= 2
        ]
        pos_xc_nodes = np.array(
            [self.Graph.node[k]['pos'] for k in NodesToModify])
        new_edges = []
        for nn, pp in zip(NodesToModify, pos_xc_nodes):
            checkp = pos - pp[None, :]
            checkp = np.sum(checkp**2, axis=1)**0.5
            ed_nodes = nodes[checkp <= 2**0.5]
            new_ed = [[nn, kk] for kk in ed_nodes if kk != nn]
            #print(len(new_ed))
            new_edges.append(new_ed)
        new_edges = [k2 for k1 in new_edges for k2 in k1]
        self.Graph.add_edges_from(new_edges)

        # label extremity nodes at imageborders
        if self.label_ext:

            maxx, maxy, maxz = Label.shape
            maxx -= 1
            maxy -= 1
            maxz -= 1

            pos = self.Graph.GetNodesPos()
            ext = []
            for n, p in zip(self.Graph.GetNodes(), self.Graph.GetNodesPos()):

                if p[0] == 0 or p[0] == maxx:
                    self.Graph.node[n]['ext'] = 1

                if p[1] == 0 or p[1] == maxy:
                    self.Graph.node[n]['ext'] = 1

                if p[2] == 0 or p[2] == maxz:
                    self.Graph.node[n]['ext'] = 1

                try:
                    dumb = self.Graph.node[n]['ext']
                except:
                    self.Graph.node[n]['ext'] = 0

        if self.Sampling is not None:
            for i in self.Graph.GetNodes():
                self.Graph.node[i][
                    'pos'] = self.Graph.node[i]['pos'] * self.Sampling

        print('create graph: ' + str(time() - t1))

    # public
    def UpdateRandomGraph(self, connection=8, nInitialNodes=100000):
        self.Connection = connection
        self.NInitialNodes = nInitialNodes
        self.__GenerateRandomGraphFromLabel()

    def UpdateRandomGridGraph(self, connection=8, nNodes=100000):
        self.Connection = connection
        self.NNodes = nNodes
        self.__GenerateRandomGridGraphFromLabel()

    def UpdateGridGraph(self, Sampling=None):
        if Sampling is not None:
            self.Sampling = float(Sampling)
        else:
            self.Sampling = Sampling
        self.__GenerateGridGraphFromLabel()

    def GetOutput(self):
        self.__CalculateDistMap()
        self.__AssignDistMapToGraph()
        self.Graph = fixG(self.Graph)
        self.Graph.Area = self.Area
        return self.Graph

    def GetArea(self):
        return self.Area

    def GetDistMap(self):
        self.__CalculateDistMap()
        return self.DistMap
Ejemplo n.º 5
0
    def UpdateWithStitching(self,
                            size,
                            niter1=10,
                            niter2=5,
                            is_parallel=False,
                            n_parallel=5,
                            ret=False):
        '''
        this funtion allow to generate graphs as follows:
            1) image patching -->  2) patch-based contraction (fixing boundary nodes) 
            --> 3) graph stitching --> 4) boundary contraction --> 5) global contraction --> refinement 
        
        it is helpful when graphing large inputs.
        
        Inputs:
            size: dimention of a 3D patch --> [size, size, size] 
            niter1: number of contraction iterations on patches 
            niter2: number of contraction iterations on boundary nodes 
            is_parallel: if True, patch-based contraction will run in parallel using 'ray'
            n_parallel: number of parallel processes (note: for limited RAM memory, 'n_parallel' should be smaller) 
            ret: if True, this function will return the output graph
        '''
        try:
            from sklearn import neighbors
        except:
            print('  \'scikit-learn\' must be instaled to run this funtion!')

        if is_parallel:

            try:
                import ray
            except:
                print(
                    '  \'ray\' must be installed to run patch-based contraction in parallel!'
                )

            GraphParallel = activate_parallel()

        # obtain distance map
        #self.label=image.morphology.distance_transform_edt(self.label)
        self.label = DistMap3D(self.label)

        # patching
        print('--Extract patches ...')
        patches, patchesid = Decompose(self.label,
                                       size=size)  # extract patches
        patches_shape = [patches.shape[0], patches.shape[1], patches.shape[2]]

        print('--Obtain semi-contracted graphs from patches ...')
        # run contraction avoiding boundary nodes for each patch
        graphs = []
        inds = np.arange(0, len(patchesid), n_parallel)
        patchesid_ = [patchesid[ind:ind + n_parallel] for ind in inds]

        for inds in patchesid_:

            if is_parallel:  # in parallel
                ray.init()
                subpatches = [ray.put(patches[ind]) for ind in inds]
                subgraphs = [
                    GraphParallel.remote(
                        patch,
                        niter=niter1,
                        Sampling=self.sampling,
                        DistParam=self.dist_param,
                        MedParam=self.med_param,
                        SpeedParam=self.speed_param,
                        DegreeThreshold=self.degree_threshold,
                        ClusteringResolution=self.clustering_resolution)
                    for patch in subpatches
                ]
                subgraphs = [ray.get(g) for g in subgraphs]
                ray.shutdown()
                graphs.append(subgraphs)

            else:  # in serial
                subpatches = [patches[ind] for ind in inds]
                subgraphs = [
                    GraphSerial(
                        patch,
                        niter=niter1,
                        Sampling=self.sampling,
                        DistParam=self.dist_param,
                        MedParam=self.med_param,
                        SpeedParam=self.speed_param,
                        DegreeThreshold=self.degree_threshold,
                        ClusteringResolution=self.clustering_resolution)
                    for patch in subpatches
                ]
                subgraphs = [g for g in subgraphs]
                graphs.append(subgraphs)
        graphs = [k1 for k in graphs for k1 in k]  # uravel
        del patches

        # adjust the position of graph nodes coming from each patch
        area = np.sum([k.Area for k in graphs if k is not None])
        pluspos = (size) * np.array(patchesid)
        for plus, g in zip(pluspos, graphs):
            if g is not None:
                AddPos(g, plus)

        print('--Combine semi-contracted graphs ...')
        fullgraph = EmptyGraph()
        nnodes = 0
        for idx, g in enumerate(graphs):
            if g is not None:
                print('    graph id ' + str(idx) + ' added')
                nnodes += fullgraph.number_of_nodes()
                new_nodes = nnodes + np.array(range(g.number_of_nodes()))
                mapping = dict(zip(g.GetNodes(), new_nodes))
                g = nx.relabel_nodes(g, mapping)
                fullgraph.add_nodes_from(g.GetNodes())
                fullgraph.add_edges_from(g.GetEdges())
                for k in new_nodes:
                    fullgraph.node[k]['pos'] = g.node[k]['pos']
                    fullgraph.node[k]['r'] = g.node[k]['r']
                    fullgraph.node[k]['ext'] = g.node[k]['ext']
            else:
                print('    graph id ' + str(idx) + ' is None')

        fullgraph = fixG(fullgraph)
        fullgraph.Area = area
        del graphs

        print('--Stitch semi-contracted graphs ...')
        nodes = np.array(
            [k for k in fullgraph.GetNodes() if fullgraph.node[k]['ext'] == 1])
        nodesid = dict(zip(range(len(nodes)), nodes))
        pos = np.array([fullgraph.node[k]['pos'] for k in nodes])
        pos_tree = neighbors.KDTree(pos)
        a = pos_tree.query_radius(pos, r=1.0)
        new_edges = [[(nodesid[k[0]], nodesid[k1]) for k1 in k[1:]] for k in a]
        new_edges = [k1 for k in new_edges for k1 in k]
        fullgraph.add_edges_from(new_edges)

        del a
        del nodes
        del pos
        del new_edges
        del pos_tree

        print('--Contract ext nodes ...')
        ContractExt(fullgraph,
                    niter=niter2,
                    DistParam=self.dist_param,
                    MedParam=self.med_param,
                    SpeedParam=self.speed_param,
                    DegreeThreshold=self.degree_threshold,
                    ClusteringResolution=self.clustering_resolution)

        print('--Generate final skeleton ...')
        contract_final = ContractGraph(Graph=fullgraph)
        contract_final.Update(DistParam=self.dist_param,
                              MedParam=self.med_param,
                              SpeedParam=self.speed_param,
                              DegreeThreshold=self.degree_threshold,
                              StopParam=self.stop_param,
                              NFreeIteration=self.n_free_iteration)
        gc = contract_final.GetOutput()

        print('--Refine final skeleton ...')
        refine = RefineGraph(Graph=gc)
        refine.Update()
        gr = refine.GetOutput()
        gr = fixG(gr)

        # ----- return ----#
        if ret:
            return gr
        else:
            self.Graph = gr