def filter_radiuscon(self, alpha, k, inner='in'): '''Filter noisy points based on contuity in radius when compared to near points''' self.D['filter_radiuscon'] = np.zeros(self.m) == True for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_'+inner][i] r_p = self.D['ma_radii_'+inner][i] if c_p != None: indices,dists = self.flann.nn_index(p, k+1) # print indices,dists M = [] for index in indices[0][1:]: M.append(self.D['ma_coords_'+inner][index]) # print M L = [] for m in M: # projection_len = np.linalg.norm(proj(m-p,c_p-p)) val = np.linalg.norm(p-m) * cos_angle(m-p, c_p-p) L.append(val) # print L, alpha * max(L), r_p if r_p < alpha * max(L): self.D['filter_radiuscon'][i] = True else: self.D['filter_radiuscon'][i] = False
def filter_radiuscon(self, alpha, k, inner='in'): '''Filter noisy points based on contuity in radius when compared to near points''' self.D['filter_radiuscon'] = np.zeros(self.m) == True for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_' + inner][i] r_p = self.D['ma_radii_' + inner][i] if c_p != None: indices, dists = self.flann.nn_index(p, k + 1) # print indices,dists M = [] for index in indices[0][1:]: M.append(self.D['ma_coords_' + inner][index]) # print M L = [] for m in M: # projection_len = np.linalg.norm(proj(m-p,c_p-p)) val = np.linalg.norm(p - m) * cos_angle(m - p, c_p - p) L.append(val) # print L, alpha * max(L), r_p if r_p < alpha * max(L): self.D['filter_radiuscon'][i] = True else: self.D['filter_radiuscon'][i] = False
def decimate_heur(self, xi=0.1, k=3, omega=math.pi/20, inner='in'): '''Decimation based on heuristics as defined in ma (2012)''' cos_omega = math.cos(omega) self.D['filtered'] = np.zeros(self.m) == True for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_'+inner][i] r_p = self.D['ma_radii_'+inner][i] if not np.isnan(c_p[0]): # test 1 - angle feature points p_ = self.D['coords'][self.D['ma_f2_'+inner][i]] if cos_angle(p, c_p, p_) < cos_omega: self.D['filtered'][i] = True break # test 2 - ball containmment indices,dists = self.flann.nn_index(p, k+1) M = [ ( self.D['ma_coords_'+inner][index], self.D['ma_radii_'+inner][index] ) for index in indices[0][1:] ] for m, r_m in M: # can this medial ball (c_p) be contained by medial ball at m? if np.linalg.norm(m-c_p) + r_p < r_m * (1+xi): self.D['filtered'][i] = True break
def update_plot(self, p_i, inner): q_indices = self.master.ma.D['ma_shrinkhist_' + inner][p_i] if len(q_indices) == 0: return # perhaps also clear the plot... q_coords = self.master.ma.D['coords'][q_indices] p_n = self.master.ma.D['normals'][p_i] # if not is_inner: p_n = -p_n p = self.master.ma.D['coords'][p_i] radii = [compute_radius(p, p_n, q) for q in q_coords] centers = [p - p_n * r for r in radii] thetas = [ acos(cos_angle(p - c, q - c)) * (180 / pi) for c, q in zip(centers, q_coords) ] lambdas = [norm(p - q) for q in q_coords] r_initial = radii[0] radii_proportional = [r / r_initial for r in radii] lambda_proportional = [l / lambdas[0] for l in lambdas] self.plotline_a.set_xdata(range(1, 1 + len(thetas))) self.plotline_b.set_xdata(range(1, 1 + len(thetas))) self.plotline_c.set_xdata(range(1, 1 + len(thetas))) self.plotline_a.set_ydata(thetas) self.plotline_b.set_ydata(radii_proportional) self.plotline_c.set_ydata(lambda_proportional) self.canvas.draw()
def decimate_heur(self, xi=0.1, k=3, omega=math.pi / 20, inner='in'): '''Decimation based on heuristics as defined in ma (2012)''' cos_omega = math.cos(omega) self.D['filtered'] = np.zeros(self.m) == True for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_' + inner][i] r_p = self.D['ma_radii_' + inner][i] if not np.isnan(c_p[0]): # test 1 - angle feature points p_ = self.D['coords'][self.D['ma_f2_' + inner][i]] if cos_angle(p, c_p, p_) < cos_omega: self.D['filtered'][i] = True break # test 2 - ball containmment indices, dists = self.flann.nn_index(p, k + 1) M = [(self.D['ma_coords_' + inner][index], self.D['ma_radii_' + inner][index]) for index in indices[0][1:]] for m, r_m in M: # can this medial ball (c_p) be contained by medial ball at m? if np.linalg.norm(m - c_p) + r_p < r_m * (1 + xi): self.D['filtered'][i] = True break
def update_plot(self, p_i, inner): q_indices = self.master.ma.D['ma_shrinkhist_'+inner][p_i] if len(q_indices) == 0: return # perhaps also clear the plot... q_coords = self.master.ma.D['coords'][q_indices] p_n = self.master.ma.D['normals'][p_i] # if not is_inner: p_n = -p_n p = self.master.ma.D['coords'][p_i] radii = [ compute_radius(p,p_n,q) for q in q_coords ] centers = [ p - p_n * r for r in radii ] thetas = [ acos(cos_angle(p-c,q-c))*(180/pi) for c, q in zip(centers, q_coords) ] lambdas = [ norm(p-q) for q in q_coords ] r_initial = radii[0] radii_proportional = [r/r_initial for r in radii] lambda_proportional = [l/lambdas[0] for l in lambdas] self.plotline_a.set_xdata(range(1,1+len(thetas))) self.plotline_b.set_xdata(range(1,1+len(thetas))) self.plotline_c.set_xdata(range(1,1+len(thetas))) self.plotline_a.set_ydata(thetas) self.plotline_b.set_ydata(radii_proportional) self.plotline_c.set_ydata(lambda_proportional) self.canvas.draw()
def compute_theta(self, inner='in'): '''Compute for every boundary point p, corresponding ma point m, and other feature point p_ the angle p-m-p_ ''' self.D['theta_'+inner] = np.zeros(self.m) self.D['theta_'+inner][:] = np.nan for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_'+inner][i] if not np.isnan(c_p[0]): p_ = self.D['coords'][self.D['ma_f2_'+inner][i]] self.D['theta_'+inner][i] = cos_angle(p-c_p, p_-c_p)
def compute_theta(self, inner='in'): '''Compute for every boundary point p, corresponding ma point m, and other feature point p_ the angle p-m-p_ ''' self.D['theta_' + inner] = np.zeros(self.m) self.D['theta_' + inner][:] = np.nan for i, p in enumerate(self.D['coords']): c_p = self.D['ma_coords_' + inner][i] if not np.isnan(c_p[0]): p_ = self.D['coords'][self.D['ma_f2_' + inner][i]] self.D['theta_' + inner][i] = cos_angle(p - c_p, p_ - c_p)
def filter_thetacon(self, theta_min=37, theta_delta=45, theta_absmin=26, inner='in'): """Filter noisy points based on continuity in separation angle as function of the ith iteration in the shrinking ball process""" # TODO: points with k=1 now receive no filtering... just discard them? self.D['filter_thetacon'] = np.zeros(self.m) == True theta_min *= (math.pi / 180) theta_delta *= (math.pi / 180) theta_absmin *= (math.pi / 180) def find_optimal_theta(thetas): theta_prev = thetas[0] for j, theta in enumerate(thetas[1:]): if ((theta_prev - theta) >= theta_delta and theta <= theta_min) or (theta < theta_absmin): return j theta_prev = theta # print return None for i, p in enumerate(self.D['coords']): p_n = self.D['normals'][i] q_indices = self.D['ma_shrinkhist_' + inner][i] if len(q_indices) <= 1: continue q_coords = self.D['coords'][q_indices] # if not is_inner: p_n = -p_n radii = [compute_radius(p, p_n, q) for q in q_coords] centers = [p - p_n * r for r in radii] thetas = [ math.acos(cos_angle(p - c, q - c)) for c, q in zip(centers, q_coords) ] optimal_theta = find_optimal_theta(thetas) # print optimal_theta if optimal_theta is not None: self.D['filter_thetacon'][i] = True
def filter_thetacon(self, theta_min=37, theta_delta=45, theta_absmin=26, inner='in'): """Filter noisy points based on continuity in separation angle as function of the ith iteration in the shrinking ball process""" # TODO: points with k=1 now receive no filtering... just discard them? self.D['filter_thetacon'] = np.zeros(self.m) == True theta_min *= (math.pi/180) theta_delta *= (math.pi/180) theta_absmin *= (math.pi/180) def find_optimal_theta(thetas): theta_prev = thetas[0] for j, theta in enumerate(thetas[1:]): if ( (theta_prev - theta) >= theta_delta and theta <= theta_min ) or (theta < theta_absmin): return j theta_prev = theta # print return None for i, p in enumerate(self.D['coords']): p_n = self.D['normals'][i] q_indices = self.D['ma_shrinkhist_'+inner][i] if len(q_indices) <= 1: continue q_coords = self.D['coords'][q_indices] # if not is_inner: p_n = -p_n radii = [ compute_radius(p,p_n,q) for q in q_coords ] centers = [ p - p_n * r for r in radii ] thetas = [ math.acos(cos_angle(p-c,q-c)) for c, q in zip(centers, q_coords) ] optimal_theta = find_optimal_theta(thetas) # print optimal_theta if optimal_theta is not None: self.D['filter_thetacon'][i] = True
def compute_balls(self, inner=True, verbose=False): """Balls shrinking algorithm. Set `inner` to False when outer balls are wanted.""" for i, pn in enumerate(zip(self.D['coords'], self.D['normals'])): p, n = pn if not inner: n = -n # when approximating 1st point initialize q with random point not equal to p q=p # if i==0: # while (q == p).all(): # random_index = int(rand(1)*self.D['coords'].shape[0]) # q = self.D['coords'][random_index] # r = compute_radius(p,n,q) # forget optimization of r: r=self.SuperR msg='New iteration, initial r = {:.5}'.format(float(r)) if verbose: print msg yield {'stage': 1, 'geom': (p,n), 'msg':msg} r_ = None c = None j = -1 q_i = None q_history = [] while True: j+=1 # initialize r on last found radius if j>0: r = r_ elif j==0 and i>0: r = r # compute ball center c = p - n*r # q_i_previous = q_i msg = 'Current iteration: #' + str(i) +', r = {:.5}'.format(float(r)) if verbose: print msg yield {'stage': 2, 'geom': (q,c,r), 'msg':msg} ### FINDING NEAREST NEIGHBOR OF c # find closest point to c and assign to q indices,dists = self.flann.nn_index(c, 2) # dists, indices = self.kd_tree.query(array([c]), k=2) candidate_c = self.D['coords'][indices] # candidate_n= self.D['normals'][indices] # print 'candidates:', candidates q = candidate_c[0][0] # q_n = candidate_n[0][0] q_i = indices[0][0] # yield {'stage': 3, 'geom': (q)} # What to do if closest point is p itself? if (q==p).all(): # 1) if r==SuperR, apparantly no other points on the halfspace spanned by -n => that's an infinite ball if r == self.SuperR: r_ = r break # 2) otherwise just pick the second closest point else: q = candidate_c[0][1] # q_n = candidate_n[0][1] q_i = indices[0][1] q_history.append(q_i) # compute new candidate radius r_ r_ = compute_radius(p,n,q) # print r, r_, p-c, q-c, cos_angle(p-c, q-c) ### BOUNDARY CASES # if r_ < 0 closest point was on the wrong side of plane with normal n => start over with SuperRadius on the right side of that plance if r_ < 0: r_ = self.SuperR # if r_ > SuperR, stop now because otherwise in case of planar surface point configuration, we end up in an infinite loop elif r_ > self.SuperR: # elif cos_angle(p-c, q-c) >= self.normal_thres: r_ = self.SuperR break c_ = p - n*r_ # this seems to work well against noisy ma points. if self.denoise_absmin is not None: if math.acos(cos_angle(p-c_, q-c_)) < self.denoise_absmin and j>0 and r_>np.linalg.norm(q-p): # msg = 'Current iteration: -#' + str(i) +', r = {:.5}'.format(float(r)) # yield {'stage': 2, 'geom': (q,c_,r), 'msg':msg} # keep previous radius: r_=r q_i = q_i_previous break if self.denoise_delta is not None and j>0: theta_now = math.acos(cos_angle(p-c_, q-c_)) q_previous = self.D['coords'][q_i_previous] theta_prev = math.acos(cos_angle(p-c_, q_previous-c_)) if theta_prev-theta_now > self.denoise_delta and theta_now < self.denoise_min and r_>np.linalg.norm(q-p): # print "theta_prev:",theta_prev/math.pi * 180 # print "theta_now:",theta_now/math.pi * 180 # print "self.denoise_delta:",self.denoise_delta/math.pi * 180 # print "self.denoise_min:",self.denoise_min/math.pi * 180 # keep previous radius: r_=r q_i = q_i_previous break if self.detect_planar != None: if math.acos( cos_angle(q-p, -n) ) > self.detect_planar and j<2: # yield {'stage': 2, 'geom': (q,p - n*r_,r_), 'msg':msg} r_= self.SuperR # r_= r # q_i = q_i_previous break ### NORMAL STOP CONDITION # stop iteration if r has converged if r == r_: break if inner: inout = 'in' else: inout = 'out' if r_ >= self.SuperR: pass else: self.D['ma_radii_'+inout][i] = r_ self.D['ma_coords_'+inout][i] = c self.D['ma_f1_'+inout][i] = i self.D['ma_f2_'+inout][i] = q_i self.D['ma_shrinkhist_'+inout].append(q_history[:-1])
def compute_balls(self, inner=True, verbose=False): """Balls shrinking algorithm. Set `inner` to False when outer balls are wanted.""" for i, pn in enumerate(zip(self.D['coords'], self.D['normals'])): p, n = pn if not inner: n = -n # when approximating 1st point initialize q with random point not equal to p q = p # if i==0: # while (q == p).all(): # random_index = int(rand(1)*self.D['coords'].shape[0]) # q = self.D['coords'][random_index] # r = compute_radius(p,n,q) # forget optimization of r: r = self.SuperR msg = 'New iteration, initial r = {:.5}'.format(float(r)) if verbose: print msg yield {'stage': 1, 'geom': (p, n), 'msg': msg} r_ = None c = None j = -1 q_i = None q_history = [] while True: j += 1 # initialize r on last found radius if j > 0: r = r_ elif j == 0 and i > 0: r = r # compute ball center c = p - n * r # q_i_previous = q_i msg = 'Current iteration: #' + str(i) + ', r = {:.5}'.format( float(r)) if verbose: print msg yield {'stage': 2, 'geom': (q, c, r), 'msg': msg} ### FINDING NEAREST NEIGHBOR OF c # find closest point to c and assign to q indices, dists = self.flann.nn_index(c, 2) # dists, indices = self.kd_tree.query(array([c]), k=2) candidate_c = self.D['coords'][indices] # candidate_n= self.D['normals'][indices] # print 'candidates:', candidates q = candidate_c[0][0] # q_n = candidate_n[0][0] q_i = indices[0][0] # yield {'stage': 3, 'geom': (q)} # What to do if closest point is p itself? if (q == p).all(): # 1) if r==SuperR, apparantly no other points on the halfspace spanned by -n => that's an infinite ball if r == self.SuperR: r_ = r break # 2) otherwise just pick the second closest point else: q = candidate_c[0][1] # q_n = candidate_n[0][1] q_i = indices[0][1] q_history.append(q_i) # compute new candidate radius r_ r_ = compute_radius(p, n, q) # print r, r_, p-c, q-c, cos_angle(p-c, q-c) ### BOUNDARY CASES # if r_ < 0 closest point was on the wrong side of plane with normal n => start over with SuperRadius on the right side of that plance if r_ < 0: r_ = self.SuperR # if r_ > SuperR, stop now because otherwise in case of planar surface point configuration, we end up in an infinite loop elif r_ > self.SuperR: # elif cos_angle(p-c, q-c) >= self.normal_thres: r_ = self.SuperR break c_ = p - n * r_ # this seems to work well against noisy ma points. if self.denoise_absmin is not None: if math.acos( cos_angle(p - c_, q - c_) ) < self.denoise_absmin and j > 0 and r_ > np.linalg.norm( q - p): # msg = 'Current iteration: -#' + str(i) +', r = {:.5}'.format(float(r)) # yield {'stage': 2, 'geom': (q,c_,r), 'msg':msg} # keep previous radius: r_ = r q_i = q_i_previous break if self.denoise_delta is not None and j > 0: theta_now = math.acos(cos_angle(p - c_, q - c_)) q_previous = self.D['coords'][q_i_previous] theta_prev = math.acos(cos_angle(p - c_, q_previous - c_)) if theta_prev - theta_now > self.denoise_delta and theta_now < self.denoise_min and r_ > np.linalg.norm( q - p): # print "theta_prev:",theta_prev/math.pi * 180 # print "theta_now:",theta_now/math.pi * 180 # print "self.denoise_delta:",self.denoise_delta/math.pi * 180 # print "self.denoise_min:",self.denoise_min/math.pi * 180 # keep previous radius: r_ = r q_i = q_i_previous break if self.detect_planar != None: if math.acos(cos_angle(q - p, -n)) > self.detect_planar and j < 2: # yield {'stage': 2, 'geom': (q,p - n*r_,r_), 'msg':msg} r_ = self.SuperR # r_= r # q_i = q_i_previous break ### NORMAL STOP CONDITION # stop iteration if r has converged if r == r_: break if inner: inout = 'in' else: inout = 'out' if r_ >= self.SuperR: pass else: self.D['ma_radii_' + inout][i] = r_ self.D['ma_coords_' + inout][i] = c self.D['ma_f1_' + inout][i] = i self.D['ma_f2_' + inout][i] = q_i self.D['ma_shrinkhist_' + inout].append(q_history[:-1])