def construct_tree(d): items = [] for idx in d: x = X[self.index_map == idx] y = Y[self.index_map == idx] z = Z[self.index_map == idx] f = self.data[self.index_map == idx] if type(d[idx]) == tuple: sub_items = construct_tree(d[idx][0]) b = Branch(sub_items, x[0], y[0], z[0], f[0], id=idx) for i in range(1, len(x)): b.add_point(x[i], y[i], z[i], f[i]) items.append(b) else: l = Leaf(x[0], y[0], z[0], f[0], id=idx) for i in range(1, len(x)): l.add_point(x[i], y[i], z[i], f[i]) items.append(l) return items
def _compute(self, data, minimum_flux=-np.inf, minimum_npix=0, minimum_delta=0, verbose=True): # Reset ID counter self._reset_idx() # Initialize list of ancestors ancestor = {} # If array is 2D, recast to 3D if len(data.shape) == 2: self.n_dim = 2 self.data = data.reshape(1, data.shape[0], data.shape[1]) else: self.n_dim = 3 self.data = data # Extract data shape nz, ny, nx = self.data.shape # Create arrays with pixel positions x = np.arange(self.data.shape[2], dtype=np.int32) y = np.arange(self.data.shape[1], dtype=np.int32) z = np.arange(self.data.shape[0], dtype=np.int32) X, Y, Z = meshgrid_nd(x, y, z) # Convert to 1D flux, X, Y, Z = self.data.ravel(), X.ravel(), Y.ravel(), Z.ravel() # Keep only values above minimum required keep = flux > minimum_flux flux, X, Y, Z = flux[keep], X[keep], Y[keep], Z[keep] if verbose: print "Number of points above minimum: %i" % np.sum(keep) # Sort by decreasing flux order = np.argsort(flux)[::-1] flux, X, Y, Z = flux[order], X[order], Y[order], Z[order] # Define index array indicating what item each cell is part of self.index_map = np.zeros(self.data.shape, dtype=np.int32) # Loop from largest to smallest value. Each time, check if the pixel # connects to any existing leaf. Otherwise, create new leaf. items = {} for i in range(len(flux)): # Print stats if verbose and i % 10000 == 0: print "%i..." % i # Check if point is adjacent to any leaf adjacent = [] if X[i] > 0 and self.index_map[Z[i], Y[i], X[i] - 1] > 0: adjacent.append(self.index_map[Z[i], Y[i], X[i] - 1]) if X[i] < nx - 1 and self.index_map[Z[i], Y[i], X[i] + 1] > 0: adjacent.append(self.index_map[Z[i], Y[i], X[i] + 1]) if Y[i] > 0 and self.index_map[Z[i], Y[i] - 1, X[i]] > 0: adjacent.append(self.index_map[Z[i], Y[i] - 1, X[i]]) if Y[i] < ny - 1 and self.index_map[Z[i], Y[i] + 1, X[i]] > 0: adjacent.append(self.index_map[Z[i], Y[i] + 1, X[i]]) if Z[i] > 0 and self.index_map[Z[i] - 1, Y[i], X[i]] > 0: adjacent.append(self.index_map[Z[i] - 1, Y[i], X[i]]) if Z[i] < nz - 1 and self.index_map[Z[i] + 1, Y[i], X[i]] > 0: adjacent.append(self.index_map[Z[i] + 1, Y[i], X[i]]) # Replace adjacent elements by its ancestor for j in range(len(adjacent)): if ancestor[adjacent[j]] is not None: adjacent[j] = ancestor[adjacent[j]] # Remove duplicates adjacent = list(set(adjacent)) # Find how many unique adjacent structures there are n_adjacent = len(adjacent) if n_adjacent == 0: # Create new leaf # Set absolute index of the new element idx = self._next_idx() # Create leaf leaf = Leaf(X[i], Y[i], Z[i], flux[i], id=idx) # Add leaf to overall list items[idx] = leaf # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx # Create new entry for ancestor ancestor[idx] = None elif n_adjacent == 1: # Add to existing leaf or branch # Get absolute index of adjacent element idx = adjacent[0] # Get adjacent item item = items[idx] # Add point to item item.add_point(X[i], Y[i], Z[i], flux[i]) # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx else: # Merge leaves # At this stage, the adjacent items might consist of an arbitrary # number of leaves and branches. # Find all leaves that are not important enough to be kept # separate. These leaves will now be treated the same as the pixel # under consideration merge = [] for idx in adjacent: if type(items[idx]) == Leaf: leaf = items[idx] if leaf.npix < minimum_npix or leaf.fmax - flux[i] < minimum_delta: merge.append(idx) # Remove merges from list of adjacent items for idx in merge: adjacent.remove(idx) # If there is only one item left, then if it is a leaf, add to the # list to merge, and if it is a branch then add the merges to the # branch. if len(adjacent) == 0: # There are no separate leaves left (and no branches), so pick the # first one as the reference and merge all the others onto it idx = merge[0] leaf = items[idx] # Add current point to the leaf leaf.add_point(X[i], Y[i], Z[i], flux[i]) # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx for i in merge[1:]: # print "Merging leaf %i onto leaf %i" % (i, idx) # Remove leaf removed = items.pop(i) # Merge old leaf onto reference leaf leaf.merge(removed) # Update index map self.index_map = removed.add_footprint(self.index_map, idx) elif len(adjacent) == 1: if type(items[adjacent[0]]) == Leaf: idx = adjacent[0] leaf = items[idx] # Add current point to the leaf leaf.add_point(X[i], Y[i], Z[i], flux[i]) # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx for i in merge: # print "Merging leaf %i onto leaf %i" % (i, idx) # Remove leaf removed = items.pop(i) # Merge old leaf onto reference leaf leaf.merge(removed) # Update index map self.index_map = removed.add_footprint(self.index_map, idx) else: idx = adjacent[0] branch = items[idx] # Add current point to the branch branch.add_point(X[i], Y[i], Z[i], flux[i]) # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx for i in merge: # print "Merging leaf %i onto branch %i" % (i, idx) # Remove leaf removed = items.pop(i) # Merge old leaf onto reference leaf branch.merge(removed) # Update index map self.index_map = removed.add_footprint(self.index_map, idx) else: # Set absolute index of the new element idx = self._next_idx() # Create branch branch = Branch([items[j] for j in adjacent], \ X[i], Y[i], Z[i], flux[i], id=idx) # Add branch to overall list items[idx] = branch # Set absolute index of pixel in index map self.index_map[Z[i], Y[i], X[i]] = idx # Create new entry for ancestor ancestor[idx] = None for i in merge: # print "Merging leaf %i onto branch %i" % (i, idx) # Remove leaf removed = items.pop(i) # Merge old leaf onto reference leaf branch.merge(removed) # Update index map self.index_map = removed.add_footprint(self.index_map, idx) for j in adjacent: ancestor[j] = idx for a in ancestor: if ancestor[a] == j: ancestor[a] = idx if verbose and not i % 10000 == 0: print "%i..." % i # Remove orphan leaves that aren't large enough remove = [] for idx in items: item = items[idx] if type(item) == Leaf: if item.npix < minimum_npix or item.fmax - item.fmin < minimum_delta: remove.append(idx) for idx in remove: items.pop(idx) # Create trunk from objects with no ancestors self.trunk = Trunk() for idx in items: if ancestor[idx] is None: self.trunk.append(items[idx]) # Make map of leaves vs branches self.item_type_map = np.zeros(self.data.shape, dtype=np.uint8) for idx in items: item = items[idx] if type(item) == Leaf: self.item_type_map = item.add_footprint(self.item_type_map, 2) else: self.item_type_map = item.add_footprint(self.item_type_map, 1, recursive=False) # Re-cast to 2D if original dataset was 2D if self.n_dim == 2: self.data = self.data[0, :, :] self.index_map = self.index_map[0, :, :] self.item_type_map = self.item_type_map[0, :, :]