def competetive_fast_marching(vertices, graph, seeds): ''' Label all vertices on highres mesh to the closest seed vertex using a balanced binary search tree ''' import numpy as np import sys from bintrees import FastAVLTree # make a labelling container to be filled with the search tree # first column are the vertex indices of the complex mesh # second column are the labels from the simple mesh # (-1 for all but the corresponding points for now) labels = np.zeros((vertices.shape[0],2), dtype='int64')-1 labels[:,0] = range(vertices.shape[0]) for i in range(seeds.shape[0]): labels[seeds[i]][1] = i # initiate AVLTree for binary search tree = FastAVLTree() # organisation of the tree will be # key: edge length; value: tuple of vertices (source, target) # add all neighbours of the voronoi seeds for v in seeds: add_neighbours(v, 0, graph, labels, tree) # Competetive fast marching starting from voronoi seeds printcount = 0 while tree.count > 0: printcount += 1 #pdb.set_trace() # pop the item with minimum edge length min_item = tree.pop_min() length = min_item[0] source = min_item[1][0] target = min_item[1][1] #if target no label yet (but source does!), assign label of source if labels[target][1] == -1: if labels[source][1] == -1: sys.exit('Source has no label, something went wrong!') else: # assign label of source to target labels[target][1] = labels[source][1] # test if labelling is complete if any(labels[:,1]==-1): # if not, add neighbours of target to tree add_neighbours(target, length, graph, labels, tree) else: break if np.mod(printcount, 100) == 0.0: print 'tree '+str(tree.count) print 'labels '+str(np.where(labels[:,1]==-1)[0].shape[0]) return labels
def find_voronoi_seeds(simple_vertices, simple_faces, complex_vertices, complex_faces, log_file, cutoff_angle=(np.pi / 2)): ''' Finds those points on the complex mesh that correspond best to the simple mesh (taking into accound euclidian distance and direction of normals) while forcing a one-to-one mapping ''' from bintrees import FastAVLTree import scipy.spatial as spatial from utils import log # calculate normals for simple and complex vertices simple_normals = calculate_normals(simple_vertices, simple_faces) complex_normals = calculate_normals(complex_vertices, complex_faces) # prepare array to store seeds voronoi_seed_idx = np.zeros( (simple_vertices.shape[0], ), dtype='int64') - 1 missing = np.where(voronoi_seed_idx == -1)[0].shape[0] # initialize with all vertices and small number of neighbours remaining_idxs = range(simple_vertices.shape[0]) neighbours = 100 while missing > 0: log(log_file, 'producing nearest neighbours k=%i' % (neighbours)) # find nearest neighbours of simple vertices on complex mesh using kdtree inaccuracy, mapping = spatial.KDTree(complex_vertices).query( simple_vertices[remaining_idxs], k=neighbours) # create tidy long-format lists simple_idxs = np.asarray([ neighbours * [simple_idx] for simple_idx in remaining_idxs ]).flatten() candidate_idxs = mapping.flatten() diff_euclid = inaccuracy.flatten() # for each vertex pair calculate the angle between their normals diff_normals, _ = compare_normals(simple_normals[simple_idxs], complex_normals[candidate_idxs]) log(log_file, 'candidates %i' % (diff_normals.shape[0])) # remove those pairs that have an angle / distance above cutoff #mask = np.unique(np.concatenate((np.where(diff_euclid>cutoff_euclid)[0], np.where(diff_normals>cutoff_rad)[0]))) mask = np.unique(np.where(diff_normals > cutoff_angle)[0]) diff_normals = np.delete(diff_normals, mask) diff_euclid = np.delete(diff_euclid, mask) simple_idxs = np.delete(simple_idxs, mask) candidate_idxs = np.delete(candidate_idxs, mask) log(log_file, 'remaining candidates %i' % (diff_normals.shape[0])) # calculate scores for each vertex pair scores = (diff_normals - np.mean(diff_normals)) + (diff_euclid - np.mean(diff_euclid)) log(log_file, 'producing tree') # make a binary search tree from the scores and vertex pairs, # organisation is key: score, values: tuple(simple_vertex, candiate_complex_vertex) tree = FastAVLTree(zip(scores, zip(simple_idxs, candidate_idxs))) while tree.count > 0: min_item = tree.pop_min() simple_idx = min_item[1][0] candidate_idx = min_item[1][1] if (voronoi_seed_idx[simple_idx] == -1): if candidate_idx not in voronoi_seed_idx: voronoi_seed_idx[simple_idx] = candidate_idx else: pass else: pass missing = np.where(voronoi_seed_idx == -1)[0].shape[0] if missing == 0: break # if the tree is empty, but there are still seeds missing, increase the number of nearest neighbours # and repeat procedure, but only for those simple vertices that have not been matched yet log(log_file, 'missing %i' % (missing)) remaining_idxs = np.where(voronoi_seed_idx == -1)[0] neighbours *= 5 return voronoi_seed_idx, inaccuracy, log_file
def competetive_fast_marching(vertices, graph, seeds): ''' Label all vertices on highres mesh to the closest seed vertex using a balanced binary search tree ''' import numpy as np import sys from bintrees import FastAVLTree # make a labelling container to be filled with the search tree # first column are the vertex indices of the complex mesh # second column are the labels from the simple mesh # (-1 for all but the corresponding points for now) labels = np.zeros((vertices.shape[0], 2), dtype='int64') - 1 labels[:, 0] = range(vertices.shape[0]) for i in range(seeds.shape[0]): labels[seeds[i]][1] = i # initiate AVLTree for binary search tree = FastAVLTree() # organisation of the tree will be # key: edge length; value: tuple of vertices (source, target) # add all neighbours of the voronoi seeds for v in seeds: add_neighbours(v, 0, graph, labels, tree) # Competetive fast marching starting from voronoi seeds printcount = 0 while tree.count > 0: printcount += 1 # pdb.set_trace() # pop the item with minimum edge length min_item = tree.pop_min() length = min_item[0] source = min_item[1][0] target = min_item[1][1] # if target no label yet (but source does!), assign label of source if labels[target][1] == -1: if labels[source][1] == -1: sys.exit('Source has no label, something went wrong!') else: # assign label of source to target labels[target][1] = labels[source][1] # test if labelling is complete if any(labels[:, 1] == -1): # if not, add neighbours of target to tree add_neighbours(target, length, graph, labels, tree) else: break # if the target already has a label the item is just popped out of the # tree and nothing else happens else: pass # for monitoring the progress if np.mod(printcount, 100) == 0.0: print 'tree ' + str(tree.count) print 'labels ' + str(np.where(labels[:, 1] == -1)[0].shape[0]) return labels
class PriorityQueue(object): """ Combined priority queue and set data structure. Acts like a priority queue, except that its items are guaranteed to be unique. Provides O(1) membership test, O(log N) insertion and O(log N) removal of the smallest item. Important: the items of this data structure must be both comparable and hashable (i.e. must implement __cmp__ and __hash__). This is true of Python's built-in objects, but you should implement those methods if you want to use the data structure for custom objects. """ def __init__(self, items=[], key = None , maxitems=None, maxkey=None): """ Create a new PriorityQueueSet. items: An initial item list - it can be unsorted and non-unique. The data structure will be created in O(N). """ if key == None: self.key=lambda x: x else: self.key=key self.tree = FastAVLTree() #self.tree = AVLTree() self.maxitems = maxitems self.maxkey = maxkey for x in items: self.add(x) def has_item(self, item): """ Check if *item* exists in the queue """ return bool(self.tree.get(self.key(item), False)) def pop_smallest(self): return self.tree.pop_min() def peek(self, d = None): try: return self.tree.min_item()[1] except: return d def __setitem__(self, key, value): self.tree[self.key(key)]=value def __getitem__(self, item): return self.tree[self.key(item)] # updateing by removing and reinserting # i cant find a anode by object ?? # i hate your data structures ... index in O(n) :( def update(self, item): itemsbykey = self.tree[self.key(item):self.key(item)] del self.tree[self.key(item):self.key(item)] for x in itemsbykey: #if not (x is item): self.add(x) def add(self, item): """ Add *item* to the queue. The item will be added only if it doesn't already exist in the queue. """ #print "PriorityQue add " + str(item) if self.maxkey and self.key(item) > self.maxkey: return if self.tree.get(self.key(item), None) is None: self.tree[self.key(item)]=item # sholdnt it be pop biggest??? [yes we need a tree] if self.maxitems and self.tree.__len__() > self.maxitems: self.tree.pop_max() #print "PriorityQue add peek " + str(self.peek()) def prettyprint(self): pp = operator.methodcaller('prettyprint') return "".join(map(pp,self.tree.values())) """