Ejemplo n.º 1
0
def skeletonize_index(binary,
                      points=None,
                      steps=None,
                      removals=False,
                      radii=False,
                      return_points=False,
                      check_border=True,
                      delete_border=False,
                      verbose=True):
    """Skeletonize a binary 3d array using PK12 algorithm via index coordinates.
  
  Arguments
  ---------
  binary : array
    Binary image to be skeletonized. 
  steps : int or None
    Number of maximal iteration steps. If None, use maximal reduction.
  removals :bool
    If True, returns the steps in which the pixels in the input data 
    were removed.
  radii :bool
    If True, the estimate of the local radius is returned.
  verbose :bool
    If True, print progress info.
    
  Returns
  -------
  skeleton : array
    The skeleton of the binary input.
  points : nxd array
    The point coordinates of the skeleton.
  """

    if verbose:
        print('#############################################################')
        print('Skeletonization PK12 [convolution, index]')
        timer = tmr.Timer()

    #TODO: make this work for any memmapable source
    if not isinstance(binary, np.ndarray):
        raise ValueError('Numpy array required for binary in skeletonization!')
    if binary.ndim != 3:
        raise ValueError('The binary array dimension is %d, 3 is required!' %
                         binary.ndim)

    if delete_border:
        binary = t3d.delete_border(binary)
        check_border = False

    if check_border:
        if not t3d.check_border(binary):
            raise ValueError(
                'The binary array needs to have not points on the border!')

    binary_flat = binary.reshape(-1, order='A')

    # detect points
    if points is None:
        points = ap.where(binary_flat).array
    npoints = points.shape[0]

    if verbose:
        timer.print_elapsed_time('Foreground points: %d' % (points.shape[0], ))

    if removals is True or radii is True:
        #birth = np.zeros(binary.shape, dtype = 'uint16');
        order = 'C'
        if binary.flags.f_contiguous:
            order = 'F'
        death = np.zeros(binary.shape, dtype='uint16', order=order)
        deathflat = death.reshape(-1, order='A')
        with_info = True
    else:
        with_info = False

    # iterate
    if steps is None:
        steps = -1
    step = 1
    nnonrem = 0
    while True:
        if verbose:
            print(
                '#############################################################'
            )
            print('Iteration %d' % step)
            timer_iter = tmr.Timer()

        print(type(points), points.dtype, binary.dtype)
        border = cpl.convolve_3d_indices_if_smaller_than(
            binary, t3d.n6, points, 6)
        borderpoints = points[border]
        #borderids    = np.nonzero(border)[0];
        borderids = ap.where(border).array
        keep = np.ones(len(border), dtype=bool)
        if verbose:
            timer_iter.print_elapsed_time('Border points: %d' %
                                          (len(borderpoints), ))

        #if info is not None:
        #  b = birth[borderpoints[:,0], borderpoints[:,1], borderpoints[:,2]];
        #  bids = b == 0;
        #  birth[borderpoints[bids,0], borderpoints[bids,1], borderpoints[bids,2]] = step;

        # sub iterations
        remiter = 0
        for i in range(12):
            if verbose:
                print(
                    '-------------------------------------------------------------'
                )
                print('Sub-Iteration %d' % i)
                timer_sub_iter = tmr.Timer()

            remborder = delete[cpl.convolve_3d_indices(binary, rotations[i],
                                                       borderpoints)]
            rempoints = borderpoints[remborder]
            if verbose:
                timer_sub_iter.print_elapsed_time('Matched points  : %d' %
                                                  (len(rempoints), ))

            binary_flat[rempoints] = 0
            keep[borderids[remborder]] = False
            rem = len(rempoints)
            remiter += rem

            #death times
            if with_info is True:
                #remo = np.logical_not(keep);
                deathflat[rempoints] = 12 * step + i

            if verbose:
                timer_sub_iter.print_elapsed_time('Sub-Iteration %d' % (i, ))

        if verbose:
            print(
                '-------------------------------------------------------------'
            )

        #update foregroud
        points = points[keep]

        if step % 3 == 0:
            npts = len(points)
            points = points[consider[cpl.convolve_3d_indices(
                binary, base, points)]]
            nnonrem += npts - len(points)
            if verbose:
                print('Non-removable points: %d' % (npts - len(points)))

        if verbose:
            print('Foreground points   : %d' % points.shape[0])

        if verbose:
            print(
                '-------------------------------------------------------------'
            )
            timer_iter.print_elapsed_time('Iteration %d' % (step, ))

        step += 1
        if steps >= 0 and step >= steps:
            break
        if remiter == 0:
            break

    if verbose:
        print('#############################################################')
        timer.print_elapsed_time('Skeletonization done')
        print('Total removed:   %d' % (npoints - (len(points) + nnonrem)))
        print('Total remaining: %d' % (len(points) + nnonrem))

    if radii is True or return_points is True:
        points = ap.where(binary_flat).array

    if radii is True:
        #calculate average diameter as death average death of neighbourhood
        radii = cpl.convolve_3d_indices(death,
                                        t3d.n18,
                                        points,
                                        out_dtype='uint16')
    else:
        radii = None

    result = [binary]
    if return_points:
        result.append(points)
    if removals is True:
        result.append(death)
    if radii is not None:
        result.append(radii)

    if len(result) > 1:
        return tuple(result)
    else:
        return result[0]
Ejemplo n.º 2
0
def graph_from_skeleton(skeleton, points = None, radii = None, vertex_coordinates = True, 
                        check_border = True, delete_border = False, verbose = False):
  """Converts a binary skeleton image to a graph-tool graph.
  
  Arguments
  ---------
  skeleton : array
    Source with 2d/3d binary skeleton.
  points : array
    List of skeleton points as 1d indices of flat skeleton array (optional to save processing time).
  radii  : array
    List of radii associated with each vertex.
  vertex_coordinates : bool
    If True, store coordiantes of the vertices / edges.
  check_border : bool
    If True, check if the boder is empty. The algorithm reuqires this.
  delete_border : bool
    If True, delete the border.
  verbose : bool
    If True, print progress information.
    
  Returns
  -------
  graph : Graph class
    The graph corresponding to the skeleton. 
    
  Note
  ----
  Edges are detected between neighbouring foreground pixels using 26-connectivty.
  """
  skeleton = io.as_source(skeleton);
  
  if delete_border:
    skeleton = t3d.delete_border(skeleton);
    check_border = False;
  
  if check_border:
    if not t3d.check_border(skeleton):
      raise ValueError('The skeleton array needs to have no points on the border!');  

  
  if verbose:
    timer = tmr.Timer();
    timer_all = tmr.Timer();
    print('Graph from skeleton calculation initialize.!');
  
  if points is None:
    points = ap.where(skeleton.reshape(-1, order = 'A')).array;
    
    if verbose:
      timer.print_elapsed_time('Point list generation');
      timer.reset();
  
  #create graph
  n_vertices = points.shape[0];
  g = ggt.Graph(n_vertices=n_vertices, directed=False);
  g.shape = skeleton.shape;
    
  if verbose:
    timer.print_elapsed_time('Graph initialized with %d vertices' % n_vertices);
    timer.reset();
  
  #detect edges
  edges_all = np.zeros((0,2), dtype = int);
  for i,o in enumerate(t3d.orientations()):
    # calculate off set
    offset = np.sum((np.hstack(np.where(o))-[1,1,1]) * skeleton.strides) 
    edges = ap.neighbours(points, offset);
    if len(edges) > 0:
      edges_all = np.vstack([edges_all, edges]);
      
    if verbose:
      timer.print_elapsed_time('%d edges with orientation %d/13 found' % (edges.shape[0], i+1));
      timer.reset();
  
  if edges_all.shape[0] > 0:
    g.add_edge(edges_all);
    
  if verbose:
    timer.print_elapsed_time('Added %d edges to graph' % (edges_all.shape[0]));
    timer.reset();

  if vertex_coordinates:
    vertex_coordinates = np.array(np.unravel_index(points, skeleton.shape, order=skeleton.order)).T;
    g.set_vertex_coordinates(vertex_coordinates);
  
  if radii is not None:
    g.set_vertex_radius(radii);  
  
  if verbose:
    timer_all.print_elapsed_time('Skeleton to Graph');

  return g;
Ejemplo n.º 3
0
def skeletonize(binary,
                points=None,
                steps=None,
                removals=False,
                radii=False,
                check_border=True,
                delete_border=False,
                return_points=False,
                verbose=True):
    """Skeletonize a binary 3d array using PK12 algorithm.
  
  Arguments
  ---------
  binary : array
    Binary image to skeletonize.
  points : array or None.
    Optional list of points in the binary to speed up processing.
  steps : int or None
    Number of maximal iteration steps (if None maximal reduction).
  removals : bool
    If True, returns also the steps at which the pixels in the input data 
    where removed. 
  radii : bool
    If True, the estimate of the local radius is returned.
  check_border : bool
    If True, check if the boder is empty. The algorithm reuqires this.
  delete_border : bool
    If True, delete the border.
  verbose : bool
    If True print progress info.
    
  Returns
  -------
  skeleton : array
    The skeleton of the binary.
  points : array
    The point coordinates of the skeleton nx3
  
  Note
  ----
  The skeletonization is done in place on the binary. Copy the binary if
  needed for further processing.
  """

    if verbose:
        print('#############################################################')
        print('Skeletonization PK12 [convolution]')
        timer = tmr.Timer()

    #TODO: make this work for any memmapable source !
    if not isinstance(binary, np.ndarray):
        raise ValueError('Numpy array required for binary in skeletonization!')
    if binary.ndim != 3:
        raise ValueError('The binary array dimension is %d, 3 is required!' %
                         binary.ndim)

    if delete_border:
        binary = t3d.delete_border(binary)
        check_border = False

    if check_border:
        if not t3d.check_border(binary):
            raise ValueError(
                'The binary array needs to have no points on the border!')

    # detect points
    #points = np.array(np.nonzero(binary)).T;
    if points is None:
        points = ap.where(binary).array

    if verbose:
        timer.print_elapsed_time(head='Foreground points: %d' %
                                 (points.shape[0], ))

    if removals is True or radii is True:
        #birth = np.zeros(binary.shape, dtype = 'uint16');
        death = np.zeros(binary.shape, dtype='uint16')
        with_info = True
    else:
        with_info = False

    # iterate
    if steps is None:
        steps = -1
    step = 1
    removed = 0
    while True:
        if verbose:
            print(
                '#############################################################'
            )
            print('Iteration %d' % step)
            timer_iter = tmr.Timer()

        border = cpl.convolve_3d_points(binary, t3d.n6, points) < 6
        borderpoints = points[border]
        borderids = np.nonzero(border)[0]
        keep = np.ones(len(border), dtype=bool)
        if verbose:
            timer_iter.print_elapsed_time('Border points: %d' %
                                          (len(borderpoints), ))

        #if info is not None:
        #  b = birth[borderpoints[:,0], borderpoints[:,1], borderpoints[:,2]];
        #  bids = b == 0;
        #  birth[borderpoints[bids,0], borderpoints[bids,1], borderpoints[bids,2]] = step;

        # sub iterations
        remiter = 0
        for i in range(12):
            if verbose:
                print(
                    '-------------------------------------------------------------'
                )
                print('Sub-Iteration %d' % i)
                timer_sub_iter = tmr.Timer()

            remborder = delete[cpl.convolve_3d_points(binary, rotations[i],
                                                      borderpoints)]
            rempoints = borderpoints[remborder]
            if verbose:
                timer_sub_iter.print_elapsed_time('Matched points: %d' %
                                                  (len(rempoints), ))

            binary[rempoints[:, 0], rempoints[:, 1], rempoints[:, 2]] = 0
            keep[borderids[remborder]] = False
            rem = len(rempoints)
            remiter += rem
            removed += rem
            if verbose:
                print('Deleted points: %d' % (rem))
                timer_sub_iter.print_elapsed_time('Sub-Iteration %d' % (i))

            #death times
            if with_info is True:
                #remo = np.logical_not(keep);
                death[rempoints[:, 0], rempoints[:, 1],
                      rempoints[:, 2]] = 12 * step + i

        #update foreground
        points = points[keep]
        if verbose:
            print('Foreground points: %d' % points.shape[0])

        if verbose:
            print(
                '-------------------------------------------------------------'
            )
            timer_iter.print_elapsed_time('Iteration %d' % (step, ))

        step += 1
        if steps >= 0 and step >= steps:
            break
        if remiter == 0:
            break

    if verbose:
        print('#############################################################')
        print('Total removed:   %d' % (removed))
        print('Total remaining: %d' % (len(points)))
        timer.print_elapsed_time('Skeletonization')

    result = [binary]
    if return_points:
        result.append(points)
    if removals is True:
        result.append(death)
    if radii is True:
        #calculate average diameter as average death of neighbourhood
        radii = cpl.convolve_3d(death, np.array(t3d.n18, dtype='uint16'),
                                points)
        result.append(radii)

    if len(result) > 1:
        return tuple(result)
    else:
        return result[0]