def naxes_connectivity(I): # Get the pixels we want to check (exclude edge pixels) Ir = np.ravel(I) edgeidcs = iu.edge_coords(I.shape, dtype='flat') allpix = set(np.where(Ir == 1)[0]) dopix = allpix - edgeidcs savepix = list(dopix) naxes = [] while dopix: pix = dopix.pop() count = 0 if Ir[pix + 1] == 1 or Ir[pix - 1] == 1: count = count + 1 if Ir[pix + I.shape[1]] == 1 or Ir[pix - I.shape[1]] == 1: count = count + 1 if Ir[pix + 1 + I.shape[1]] == 1 or Ir[pix - 1 - I.shape[1]] == 1: count = count + 1 if Ir[pix - 1 + I.shape[1]] == 1 or Ir[pix + 1 - I.shape[1]] == 1: count = count + 1 naxes.append(count) Inax = np.zeros_like(Ir, dtype=np.uint8) Inax[savepix] = naxes Inax = np.reshape(Inax, I.shape) return Inax
def test_edge_coords(): """Test edge_coords() function.""" I = np.zeros((3, 3)) sizeI = np.shape(I) edgepts = im_utils.edge_coords(sizeI, dtype='xy') # make assertions to known edge coordinates in x and y assert np.all(edgepts[0] == [0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0]) assert np.all(edgepts[1] == [0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0])
def isbp_walk_for_bps(I, bpi): bpi = set(bpi) # Use raveled image Ir = np.ravel(I) # Get edge pixels edgeidcs = iu.edge_coords(I.shape, dtype='flat') # Get emanators from first bp do_first = set() # These are the 4-connected emanators emanators = set() for bp in bpi: emanators = emanators | set(iu.neighbors_flat(bp, Ir, I.shape[1])[0]) do_first.update(iu.four_conn([bp], I)[0]) # Create set containing pixels that have already been visited walked = bpi | emanators while emanators: if do_first: idx = do_first.pop() emanators.remove(idx) else: idx = emanators.pop() walking = 1 while walking: walked.add(idx) neighs = set(iu.neighbors_flat(idx, Ir, I.shape[1])[0]) neighs = neighs - walked if len(neighs) == 0: walking = 0 elif len(neighs) == 1: idx = neighs.pop() if idx in edgeidcs: walking = 0 else: bpi.add(idx) fourconn = iu.four_conn([idx], I)[0] fourconn = [f for f in fourconn if f in neighs] do_first.update(fourconn) emanators = emanators | neighs walked.add(idx) walked.update(neighs) return bpi
def naxes_connectivity(I): """ Compute number of axes of connectivity. Computes the number of axes of connectivity for each pixel in an input skeleton. The maximum is four; horizontal, vertical, and two diagonals. The number of axes of pixel connectivity is used to determine where to place branchpoints. Parameters ---------- I : np.ndarray Binary image of a skeleton. In RivGraph, the skeleton is a reduced and padded version of Iskel. Returns ------- Inax : np.ndarray Same shape as I; values correspond to the number of axes represented by each pixel's connectivity. """ # Get the pixels we want to check (exclude edge pixels) Ir = np.ravel(I) edgeidcs = iu.edge_coords(I.shape, dtype='flat') allpix = set(np.where(Ir == 1)[0]) dopix = allpix - edgeidcs savepix = list(dopix) naxes = [] while dopix: pix = dopix.pop() count = 0 if Ir[pix + 1] == 1 or Ir[pix - 1] == 1: count = count + 1 if Ir[pix + I.shape[1]] == 1 or Ir[pix - I.shape[1]] == 1: count = count + 1 if Ir[pix + 1 + I.shape[1]] == 1 or Ir[pix - 1 - I.shape[1]] == 1: count = count + 1 if Ir[pix - 1 + I.shape[1]] == 1 or Ir[pix + 1 - I.shape[1]] == 1: count = count + 1 naxes.append(count) Inax = np.zeros_like(Ir, dtype=np.uint8) Inax[savepix] = naxes Inax = np.reshape(Inax, I.shape) return Inax
def isbp_walk_for_bps(I, bpi): """ Find branchpoints by walking from a pixel. Finds branchpoints by ensuring that all pixels in the sub-skeleton can be walked to from the set of already-found branchpoints, without visiting the same pixel more than once. Parameters ---------- I : np.ndarray Binary image of a skeleton. In RivGraph, the skeleton is a reduced and padded version of Iskel. bpi : list Indices within I of the branchpoint to begin walk. Returns ------- bpi : list Branchpoint indices in I. """ bpi = set(bpi) # Use raveled image Ir = np.ravel(I) # Get edge pixels edgeidcs = iu.edge_coords(I.shape, dtype='flat') # Get emanators from first bp do_first = set() # These are the 4-connected emanators emanators = set() for bp in bpi: emanators = emanators | set(iu.neighbors_flat(bp, Ir, I.shape[1])[0]) do_first.update(iu.four_conn([bp], I)[0]) # Create set containing pixels that have already been visited walked = bpi | emanators while emanators: if do_first: idx = do_first.pop() emanators.remove(idx) else: idx = emanators.pop() walking = 1 while walking: walked.add(idx) neighs = set(iu.neighbors_flat(idx, Ir, I.shape[1])[0]) neighs = neighs - walked if len(neighs) == 0: walking = 0 elif len(neighs) == 1: idx = neighs.pop() if idx in edgeidcs: walking = 0 else: bpi.add(idx) fourconn = iu.four_conn([idx], I)[0] fourconn = [f for f in fourconn if f in neighs] do_first.update(fourconn) emanators = emanators | neighs walked.add(idx) walked.update(neighs) return bpi
def isbp_parsimonious(Ic, Icr, Inar, Infr): """ Computes parsimonious set of branchpoints. Parameters ---------- Ic : np.ndarray Image of possible branchpoints; values correspond to number of neighbors. Icr : np.ndarray Raveled (np.ravel) version of Ic. Inar : np.ndarray Raveled version of image returned by naxes_connectivity. Infr : np.ndarray Raveled version of image returned by nfour_connectivity. Returns ------- bps : list All branchpoint indices within Ic. """ # Find all possible branchpoints by considerng those with conn>2 bp_poss = np.where(Ic > 2) bp_poss_i = np.ravel_multi_index(bp_poss, Ic.shape) bp_poss_i = list(set(bp_poss_i) - iu.edge_coords(Ic.shape)) # Find all possible branchpoint combinations by walking from each # possible initial branchpoint bpsave = [] for bpi in bp_poss_i: bptemp = isbp_walk_for_bps(np.array(Ic, dtype=np.bool), [bpi]) bpsave.append(bptemp) # Number of branchpoints for each possible initial branchpoint bpcounts = [len(b) for b in bpsave] bpsolo = [bpsave[i].pop() for i, c in enumerate(bpcounts) if c == 1] # If only one branchpoint is required, use it. However, there could be # multiple branchpoints that can serve as the single; use the one with # highest naxes-connectivity; if there are still multiple choices, take the # highest 4-connectivity. If there are still no unique choices, choose the # highest 4-connected among the highest naxes-connected. if len(bpsolo) > 0: naxconn = Inar[bpsolo] maxnax = [ bps for bps, nl in zip(bpsolo, naxconn) if nl == max(naxconn) ] if len(maxnax) == 1: return [maxnax] else: fourconn = Infr[bpsolo] maxfour = [ bpsolo[i] for i, fc in enumerate(fourconn) if fc == max(fourconn) ] if len(maxfour) == 1: return [maxfour] # Now see if there's a max 4-conn within the max naxes-conn fourconn = Infr[maxnax] maxfour = [ maxnax[i] for i, fc in enumerate(fourconn) if fc == max(fourconn) ] return [min(maxfour)] # Set bp_must according to conn, naxes, nfour keepvals = [[6, 4, 2], [5, 3, 1], [5, 3, 4], [3, 3, 2], [3, 3, 1], [4, 2, 4]] bp_must = [] for kv in keepvals: keeps = np.ndarray.tolist( np.where( np.logical_and(np.logical_and(Icr == kv[0], Inar == kv[1]), Infr == kv[2]) == 1)[0]) bp_must = bp_must + keeps # Special cases - 4,4,2 - choose one keepvals = [[4, 4, 2]] for kv in keepvals: keeps = np.ndarray.tolist( np.where( np.logical_and(np.logical_and(Icr == kv[0], Inar == kv[1]), Infr == kv[2]) == 1)[0]) if len(keeps) == 2: bp_must = bp_must + [keeps[0]] # Only consider combinations that have branchpoints where they must be placed if len(bp_must) > 0: bps = isbp_walk_for_bps(np.array(Ic, dtype=np.bool), bp_must) return bps # If there are no branchpoints that must exist based on patterns, # use the set with the smallest number of branchpoints. If there are multiple # sets, we move on... mincount = min(bpcounts) minidcs = [i for i, bpi in enumerate(bpcounts) if bpi == mincount] if len(minidcs) == 1: return bpsave[minidcs[0]] # Finally, choose branchpoints based on the most common branchpoints created # when walking from all possible branchpoints mode = stats.mode([p for b in bpsave for p in b]) bp_init = np.ndarray.tolist(mode.mode) bps = isbp_walk_for_bps(np.array(Ic, dtype=np.bool), bp_init) return bps