def additional_nodes(connecting_points): #Create pointset extra_nodes = Pointset(3) for i in range(len(connecting_points)): for j in range(len(connecting_points)): if 0 < connecting_points[i].Distance(connecting_points[j]) < 4: #Merge points extra_nodes.Append( (connecting_points[i] + connecting_points[j]) / 2) for i in range(len(connecting_points)): #Create copy connecting_points_copy = connecting_points.Copy() #Remove all from copy to avoid distance = 0. connecting_points_copy.RemoveAll(connecting_points[i]) if np.sum( (connecting_points[i].Distance(connecting_points_copy)) < 4) == 0: extra_nodes.Append(connecting_points[i]) new_nodes = extra_nodes return new_nodes
def Apply(self): # Create big list of points pp = Pointset(2) for y in range(self._patchSize): for x in range(self._patchSize): pp.append(x,y) # Get point locations c = Point( self._c._points[0].x, self._c._points[0].y ) p1 = Point( self._p1._points[0].x, self._p1._points[0].y ) p2 = Point( self._p2._points[0].x, self._p2._points[0].y ) # Get masks M1, M2 = chopstick_criteria(c, p1, p2, pp) M1.shape = self._patchSize, self._patchSize M2.shape = self._patchSize, self._patchSize # Update image self._im[:] = 9 self._im[M1] -= 3 self._im[M2] -= 3 self._t.Refresh() # Draw self._c.Draw()
def check_for_possible_bifurcation(average_radius, center_point_3d_with_radius,\ rotation_point ,found_bifurcation, global_direction): """ This function tracks the radius of the stent. If the radius drops to less than 75%, the bifurcation might have been found. If so, two new starting points will be calculated. """ #Initially no new starting positions. new_starting_positions = [] #Easier notation in rotation matrix gd = global_direction #To avoid wrong predictions on an early error in radius if len(average_radius) > 10: if 0.75*np.mean(average_radius) > center_point_3d_with_radius.r \ and not found_bifurcation: if center_point_3d_with_radius.Distance( rotation_point) > 0.25 * np.mean(average_radius): #found bifurcation found_bifurcation = True #calculate new starting points. new_starting_positions = Pointset(3) #vector of first branch fb_vector = np.mat( [[center_point_3d_with_radius[2] - rotation_point[2]], [center_point_3d_with_radius[1] - rotation_point[1]], [center_point_3d_with_radius[0] - rotation_point[0]]]) #Create rotation matrix to rotate vector rotation_matrix = np.mat( [[2 * gd[0]**2 - 1, 2 * gd[0] * gd[1], 2 * gd[0] * gd[2]], [2 * gd[1] * gd[2], 2 * gd[1]**2 - 1, 2 * gd[1] * gd[2]], [2 * gd[2] * gd[0], 2 * gd[1] * gd[2], 2 * gd[2]**2 - 1]]) #second branch sb = rotation_matrix * fb_vector sb_center_point = rotation_point + Point(sb[2], sb[1], sb[0]) #Return new starting positions new_starting_positions.Append(center_point_3d_with_radius) new_starting_positions.Append(sb_center_point) #Add radius to list with radia if not center_point_3d_with_radius.r == []: average_radius.append(center_point_3d_with_radius.r) return average_radius, found_bifurcation, new_starting_positions
def Apply(self): # Get 2D pointset pp = Pointset(2) for p in self._pp._points: pp.append(p.x, p.y) # Fit circle and sample and show c = fit_cirlce(pp) cc = sample_circle(c, 32) self._cc.SetPoints(cc) # Draw self._cc.Draw()
def detect_points2(slice, y_x, spacing, width=10): """ Alternative version of detect_points, which uses a reference point. Used for measurements on our phantom. """ # create slice as double (makes a copy) slice = slice.astype(np.float64) # create new point set to store points pp = Pointset(2) th_minHU = 300 refy, refx = y_x refy -= spacing # because we start with incrementing it while 1: # next refy += spacing if refy > slice.shape[0]: break # get patch y1, y2 = refy - spacing//4, refy + spacing//4 x1, x2 = refx - width//2, refx + width//2 if y1<0: y1=0 patch = slice[y1:y2+1, x1:x2+1] # detect Iy, Ix = np.where( (patch == patch.max()) & (patch > th_minHU) ) try: Ix = Ix[0] Iy = Iy[0] except IndexError: continue # if no points found... y, x = y1+Iy, x1+Ix if y<=0 or y>=slice.shape[0]-1: continue # get subpixel and store patch2 = slice[y-1:y+2,x-1:x+2] dx,dy = subpixel.fitLQ2_5(patch2) pp.append( x+dx, y+dy ) return pp
def __init__(self): # Init visualization fig = vv.figure(102); vv.clf() a = vv.gca() # Init points pp = Pointset(2) pp.append(14,12) pp.append(12,16) pp.append(16,16) self._pp = vv.plot(pp, ls='', ms='.', mw=10, mc='g') # Init line representing the circle self._cc = vv.plot(pp, lc='r', lw=2, ms='.', mw=5, mew=0, mc='r') # Set limits a.SetLimits((0,32), (0,32)) a.daspectAuto = False # Init object being moved self._movedPoint = None # Enable callbacks self._pp.hitTest = True self._pp.eventMouseDown.Bind(self.OnDown) self._pp.eventMouseUp.Bind(self.OnUp) a.eventMotion.Bind(self.OnMotion) a.eventDoubleClick.Bind(self.OnDD) # Start self.Apply()
def convert_2d_point_to_3d_point(center_point, vec1, vec2, pointset_2d_points): """ Given a pointset containing 2D points (y,x), a center point (z,y,x), and two vectors which describe the orientation of the slice inside the 3D volume, the algorithm will convert the 2D points to 3D points. """ if pointset_2d_points.__class__ == Point: temp = Pointset(2) temp.Append(pointset_2d_points) pointset_2d_points = temp #Pointset to store 3D points pointset_3d_points = Pointset(3) for i in range(len(pointset_2d_points)): #Shift from point 127.5,127.5 which is the rotation point y_shift = pointset_2d_points[i][1] - 127.5 x_shift = pointset_2d_points[i][0] - 127.5 center = Point(center_point[2], center_point[1], center_point[0]) point_3d = center + y_shift * vec1 + x_shift * vec2 point_3d = Point(point_3d[2], point_3d[1], point_3d[0]) pointset_3d_points.Append(point_3d) return pointset_3d_points
def sample_circle(c, N=32): """ sample_circle(c, N=32) Sample a circle represented by point c (having attribute "r") using N datapoints. Returns a pointset. """ # Get radius r = 1.0 if hasattr(c, 'r'): r = c.r # Sample N points, but add one to close the loop d = 2*np.pi / N a = np.linspace(0,2*np.pi, N+1) # Prepare array pp = np.empty((len(a), 2), dtype=np.float32) # Apply polar coordinates pp[:,0] = np.cos(a) * r + c.x pp[:,1] = np.sin(a) * r + c.y # Return as a pointset return Pointset(pp)
def find_connecting_points(pointset_slice_i, pointset_slice_i_plus_1): """ Given a set of points in slice i, the algorithm will calculate distances between these points and the points in slice i+1. If this distance is less than a certain distance, the point in slice i+1 is returned as connecting point. If there is no connecting point found, a virtual point is created. """ #Maximum distance for which a point is seen as connecting max_distance = ssdf._stent_tracking_params.max_dist #Pointsets to store points connecting_points = Pointset(2) virtual_points = Pointset(2) #for each point in slice i for i in range(len(pointset_slice_i)): distances = pointset_slice_i[i, :] - pointset_slice_i_plus_1[:, :] #for all distances from this point for j in range(len(distances)): #if distance is smaller or equal to the max distance if abs(distances[j, 0]) + abs(distances[j, 1]) <= max_distance: #make the point a connecting point connecting_points.Append(pointset_slice_i_plus_1[j]) #if the point from slice i has no connecting point if len( np.where( abs(distances[:, 0]) + abs(distances[:, 1]) <= max_distance)[0]) == 0: #change the point from slice i into a virtual point virtual_points.Append(pointset_slice_i[i]) return connecting_points, virtual_points
def select_stent(volnr, params=None): """ This functions calls the stent extration function. It contains seed points for each dataset """ if params is None: params = getDefaultParams() ssdf._stent_tracking_params = params if volnr == 1: #vol 1 vol, sampling, origin = load_volume(1) seed_points = Pointset(3) seed_points.Append(60, 110, 133) seed_points.Append(100, 75, 150) seed_points.Append(160, 70, 148) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 2: #vol 2 vol, sampling, origin = load_volume(2) seed_points = Pointset(3) seed_points.Append(60, 175, 155) seed_points.Append(100, 150, 170) seed_points.Append(165, 140, 167) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 3: #vol 3 vol, sampling, origin = load_volume(3) seed_points = Pointset(3) seed_points.Append(47, 160, 140) seed_points.Append(80, 145, 130) seed_points.Append(130, 130, 128) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 4: #vol 4 vol, sampling, origin = load_volume(4) seed_points = Pointset(3) seed_points.Append(40, 160, 160) seed_points.Append(140, 145, 135) seed_points.Append(150, 150, 150) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 5: #vol 5 vol, sampling, origin = load_volume(5) seed_points = Pointset(3) seed_points.Append(34, 150, 170) seed_points.Append(60, 135, 180) seed_points.Append(130, 140, 180) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 6: #vol 6 vol, sampling, origin = load_volume(6) seed_points = Pointset(3) seed_points.Append(40, 170, 175) seed_points.Append(70, 160, 180) seed_points.Append(140, 142, 168) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 7: #vol 7 vol, sampling, origin = load_volume(7) seed_points = Pointset(3) seed_points.Append(40, 180, 160) seed_points.Append(110, 160, 160) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 8: #vol 8 vol, sampling, origin = load_volume(8) seed_points = Pointset(3) seed_points.Append(60, 130, 190) seed_points.Append(80, 110, 180) seed_points.Append(120, 112, 140) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) elif volnr == 18: #vol 18 vol, sampling, origin = load_volume(18) seed_points = Pointset(3) seed_points.Append(70, 170, 140) seed_points.Append(135, 150, 160) node_points_stent, center_points_stent, stent_points_stent = stent_detection( vol, sampling, origin, seed_points) else: raise ValueError('Invalid volnr') plot = True if plot: points = Pointset(3) for i in range(len(node_points_stent)): points.Append(node_points_stent[i][2], node_points_stent[i][1], node_points_stent[i][0]) points_2 = Pointset(3) for i in range(len(stent_points_stent)): points_2.Append(stent_points_stent[i][2], stent_points_stent[i][1], stent_points_stent[i][0]) vv.closeAll() vv.volshow(vol) vv.plot(points_2, ls='', ms='.', mc='g', mw=4, alpha=0.5) vv.plot(points, ls='', ms='.', mc='b', mw=4, alpha=0.9) graph = from_nodes_to_graph(node_points_stent) return graph
def stent_detection(vol, sampling, origin, seed_points): """ This is the function that will be called in order to extract the stent from the CT data. """ #Point set where the final points will be stored. In node, the 4th number #indicates the ring number, the 5th indicates the direction where it is found node_points_stent = Pointset(5) center_points_stent = Pointset(3) stent_points_stent = Pointset(3) #Initially, the bifurcation is not found found_bifurcation = False average_radius = [] #start with ring 1 ring = 1 #Weighting factor alpha alpha = ssdf._stent_tracking_params.weighting_factor distance = ssdf._stent_tracking_params.distance #From seed point to seed point except for the last seed point, where the #bifurcation needs to be found. for i in range(len(seed_points) - 2): round = 0 #Don't look for the bifurcation when not connecting points in the last #part of the stent look_for_bifurcation = False #Get initial center point and direction using the seed points center_point, global_direction = initial_center_point( vol, seed_points[i], seed_points[i + 1], sampling, origin, alpha) #While the center point has not passed the seed point while center_point[0] < seed_points[i + 1][0] and round < 10: #Find stent, node and center points for a certain stent ring node_points_ring, stent_points_ring, center_points_ring, \ jump_location, jump_direction, found_bifurcation, new_starting_positions, \ ring = searching_for_ring(vol, sampling, center_point, global_direction,\ look_for_bifurcation, average_radius, alpha, ring) #Set new starting center point inside the new ring center_point = jump_location + distance * Point( jump_direction[2], jump_direction[1], jump_direction[0]) global_direction = jump_direction #Add points to point sets node_points_stent.Extend(node_points_ring) center_points_stent.Extend(center_points_ring) stent_points_stent.Extend(stent_points_ring) round = round + 1 #For the last part of the stent where the bifurcation needs to be found for i in range(len(seed_points) - 2, len(seed_points) - 1): #Reset round to zero round = 0 look_for_bifurcation = True center_point, global_direction = initial_center_point(vol, seed_points[i],\ seed_points[i+1], sampling, origin, alpha) #While the bifurcation is not found while found_bifurcation == False and round < 10: node_points_ring, stent_points_ring, center_points_ring, \ jump_location, jump_direction, found_bifurcation, new_starting_positions, \ ring = searching_for_ring(vol, sampling, center_point, global_direction, \ look_for_bifurcation, average_radius, alpha, ring) center_point = jump_location + distance * Point( jump_direction[2], jump_direction[1], jump_direction[0]) global_direction = jump_direction #Add points to point sets node_points_stent.Extend(node_points_ring) center_points_stent.Extend(center_points_ring) stent_points_stent.Extend(stent_points_ring) round = round + 1 #Store new starting positions in another poitset, because algorithm will #return [] as new_starting_positions. starting_positions = Pointset(3) #For the case where there is no bifurcation present try: starting_positions.Append( new_starting_positions[0] ) # + distance * Point(jump_direction[2],jump_direction[1],jump_direction[0])) starting_positions.Append( new_starting_positions[1] ) # + distance * Point(jump_direction[2],jump_direction[1],jump_direction[0])) except IndexError: [] alpha = ssdf._stent_tracking_params.weighting_factor_bif #Now points after the bifurcation will be found for i in range(len(starting_positions)): center_point = starting_positions[i] look_for_bifurcation = False round = 0 while round < 4: node_points_ring, stent_points_ring, center_points_ring, \ jump_location, jump_direction, found_bifurcation, new_starting_positions, \ ring = searching_for_ring(vol, sampling, center_point, global_direction, \ look_for_bifurcation, average_radius, alpha, ring) center_point = jump_location + distance * Point( jump_direction[2], jump_direction[1], jump_direction[0]) global_direction = jump_direction if 255 < center_point[0]: break #Add points to point sets node_points_stent.Extend(node_points_ring) center_points_stent.Extend(center_points_ring) stent_points_stent.Extend(stent_points_ring) round = round + 1 return node_points_stent, center_points_stent, stent_points_stent
def searching_for_ring(vol, sampling, center_point, global_direction, \ look_for_bifurcation, average_radius, alpha, ring): """ Given a center point and a direction, the algorithm will start by searching for points in a certain stent ring using the global direction. By connecting points it will come to a set of nodes. After a certain amount of nodes are found, the algorithm will flip the direction and will search for points in the other direction until the stent ring is defined. The radius is used when searching for the bifurcation. """ #Initially, the bifurcation is not found found_bifurcation = False #First the initial center point and direction has to be stored in order to #flip the direction later stored_center_point = center_point flipped_global_direction = -1 * global_direction #Create pointsets to store points node_points_ring = Pointset(5) #(z,y,x,ring,direction) stent_points_ring = Pointset(3) center_points_ring = Pointset(3) #Expected amount of nodes and returns initial filtered points expected_amount_of_nodes, initial_points_in_slice = amount_of_nodes(vol, sampling, \ center_point, global_direction, look_for_bifurcation) #set direction: 1 for along the direction of the stent, -1 for the opposite \ #direction direction = 1 #Find and connect points center_points, stent_points, node_points, global_direction, found_bifurcation, new_starting_positions, \ ring = connecting_stent_points(vol, sampling, center_point, global_direction, look_for_bifurcation, \ average_radius, direction, expected_amount_of_nodes, initial_points_in_slice, found_bifurcation, alpha, ring) #Store starting positions, because the direction -1 will remove otherwise. if found_bifurcation: store_starting_positions = new_starting_positions #Add found points to pointsets node_points_ring.Extend(node_points) stent_points_ring.Extend(stent_points) center_points_ring.Extend(center_points) #Store last center point to use as 'jumping' site if len(center_points_ring): jump_location = center_points_ring[len(center_points_ring) - 1] else: jump_location = stored_center_point jump_direction = global_direction #Flip direction direction = -1 #Find and connect points for other direction center_points, stent_points, node_points, global_direction, found_bifurcation, new_starting_positions, \ ring = connecting_stent_points(vol, sampling, stored_center_point, flipped_global_direction, \ look_for_bifurcation, average_radius, direction, expected_amount_of_nodes, \ initial_points_in_slice, found_bifurcation, alpha, ring) if found_bifurcation: new_starting_positions = store_starting_positions #Add found points to pointsets node_points_ring.Extend(node_points) stent_points_ring.Extend(stent_points) center_points_ring.Extend(center_points) ring = ring + 1 return node_points_ring, stent_points_ring, center_points_ring, \ jump_location, jump_direction, found_bifurcation, new_starting_positions, ring
def connecting_stent_points(vol, sampling, center_point, global_direction, look_for_bifurcation, \ average_radius, direction, expected_amount_of_nodes, points_in_slice, found_bifurcation, alpha, ring): """ This algorithm uses the earlier stated functions in order to connect points and define nodes. """ #Nodes that are found for the current direction node_points_direction = Pointset(3) #Other pointsets to store points center_points = Pointset(3) stent_points = Pointset(3) node_points = Pointset(5) #Set round to 0 for the case that the amount of nodes criterion is not met. round = 0 #Initial new_starting_positions = [] #connecting points while len(node_points_direction) < expected_amount_of_nodes and round < 12: #Get slice slice, center_point, global_direction, rotation_point, vec1, vec2, \ center_point_3d_with_radius = centerline_tracking.get_slice(vol, sampling, \ center_point, global_direction, look_for_bifurcation, alpha) #If looking for the bifurcation, check radius if look_for_bifurcation and direction == 1: if not found_bifurcation: average_radius, found_bifurcation, new_starting_positions \ = check_for_possible_bifurcation(average_radius, center_point_3d_with_radius, \ rotation_point, found_bifurcation, global_direction) else: new_starting_positions = [] #Find points points_in_next_slice = stentPoints2d.detect_points(slice) #Find connecting points. Change not connecting points into virtual points connecting_points, virtual_points = find_connecting_points(points_in_slice, \ points_in_next_slice) #Check if virtual points should be converted into connecting points connecting_points, temp_slice = check_virtual_points(virtual_points, \ connecting_points, center_point, vol, sampling, global_direction, alpha) #Check for nodes point_nodes_2d, point_remove = check_for_nodes(connecting_points, temp_slice) #If nodes are found, these points should be deleted from the connecting points list connecting_points = delete_nodes_from_list(connecting_points, point_remove) #Convert 2d points into 3d points connecting_points_3d = convert_2d_point_to_3d_point(center_point, vec1,\ vec2, connecting_points) point_nodes_3d = convert_2d_point_to_3d_point(center_point, vec1, vec2,\ point_nodes_2d) #Add 3d points to their list center_points.Append(rotation_point) stent_points.Extend(connecting_points_3d) node_points_direction.Extend(point_nodes_3d) for i in range(len(point_nodes_3d)): node_points.Append( Point(point_nodes_3d[i][0], point_nodes_3d[i][1], point_nodes_3d[i][2], ring, direction)) #Set connecting points as new base points points_in_slice = connecting_points #Next round round = round + 1 #If there are three nodes found, do first one more run. if len(node_points_direction) > 2: diff = 10 - round if diff > 0: round = round + diff if round == 12: new_nodes = additional_nodes(connecting_points_3d) for i in range(len(new_nodes)): node_points.Append( Point(new_nodes[i][0], new_nodes[i][1], new_nodes[i][2], ring, direction)) return center_points, stent_points, node_points, global_direction, found_bifurcation, new_starting_positions, ring
def detect_points(slice, th_gc=2000, th_minHU=300, sigma=1.6): """ Detect points Detects points on a stent in the given slice. Slice should be a numpy array. - The Gaussian Curvature should be above a threshold (needle detection) (th_gc) - An absolute (weak) threshold is used based on the Houndsfield units (th_minHU) - Streak artifacts are suppressed - sigma is the used scale at which the GC is calculated. """ # Make sure that the slice is a float if slice.dtype not in [np.float32, np.float64]: slice = slice.astype(np.float32) # Create slice to supress streak artifacts # Where streak artifacts are present, the image is inverted, anywhere else # its zero. By also calculating derivatives on this image and than adding # it, the threshold will never be reached where the streak artifacts are. sliceStreak = th_streHU - slice sliceStreak[sliceStreak<0] = 0 # Create new point set to store points pp = Pointset(2) # Calculate Gaussian curvature if True: Lxx = gaussfun.gfilter(slice, sigma, [0,2]) Ltmp = gaussfun.gfilter(sliceStreak, sigma, [0,2]) Lxx = Lxx+2*Ltmp Lxx[Lxx>0]=0; Lyy = gaussfun.gfilter(slice, sigma, [2,0]) Ltmp = gaussfun.gfilter(sliceStreak, sigma, [2,0]) Lyy = Lyy+2*Ltmp Lyy[Lyy>0]=0; Lgc = Lxx * Lyy # Make a smoothed version slice_smoothed = gaussfun.gfilter(slice, 0.5, 0) # Make a selection of candidate pixels Iy,Ix = np.where( (slice > th_minHU) & (Lgc > th_gc) ) # Mask to detect clashes clashMask = np.zeros(slice.shape, dtype=np.bool) # Detect local maxima for x,y in zip(Ix,Iy): if x==0 or y==0 or x==slice.shape[1]-1 or y==slice.shape[0]-1: continue # Select patch patch1 = slice[y-1:y+2,x-1:x+2] patch2 = slice_smoothed[y-1:y+2,x-1:x+2] if slice[y,x] == patch1.max():# and slice_smoothed[y,x] == patch2.max(): # Found local max (allowing shared max) # Not if next to another found point if clashMask[y,x]: continue # Not a streak artifact if patch2.min() <= th_streHU: continue # Subpixel #dx,dy = subpixel.fitLQ2_5(patch1) # Store pp.append( x, y ) clashMask[y-1:y+2,x-1:x+2] = 1 # Express points in world coordinates and return if isinstance(slice, Aarray): ori = [i for i in reversed(slice.origin)] sam = [i for i in reversed(slice.sampling)] pp *= Point(sam) pp += Point(ori) return pp
def from_nodes_to_graph(node_points_stent): """ Given a set of nodes it creates a graph. The node set contains a ring number (4th dimension) and direction (5th dimension) """ graph = Graph() #Min and maximum distance when connecting nodes min = 10 max = 18 for i in range(len(node_points_stent)): #Take point point = Point(node_points_stent[i][2], node_points_stent[i][1], node_points_stent[i][0]) #Add node to graph graph.AppendNode(point) #Pointset to store possible matches possible_match = Pointset(3) for j in range(len(node_points_stent)): if node_points_stent[j][3] == node_points_stent[i][ 3] and node_points_stent[j][4] != node_points_stent[i][4]: if min < point.Distance( Point(node_points_stent[j][2], node_points_stent[j][1], node_points_stent[j][0])) < max: possible_match.Append( Point(node_points_stent[j][2], node_points_stent[j][1], node_points_stent[j][0])) if len(possible_match) == 0: continue elif len(possible_match) == 1: graph.AppendNode(possible_match[0]) graph.CreateEdge(graph[np.size(graph) - 2], graph[np.size(graph) - 1]) elif len(possible_match) == 2: graph.AppendNode(possible_match[0]) graph.AppendNode(possible_match[1]) graph.CreateEdge(graph[np.size(graph) - 1], graph[np.size(graph) - 3]) graph.CreateEdge(graph[np.size(graph) - 2], graph[np.size(graph) - 3]) else: while True: sorted = False for j in range(len(possible_match) - 1): if point.Distance(possible_match[j]) > point.Distance( possible_match[j + 1]): possible_match[j], possible_match[ j + 1] = possible_match[j + 1], possible_match[j] sorted = True if not sorted: break graph.AppendNode(possible_match[0]) graph.AppendNode(possible_match[1]) graph.CreateEdge(graph[np.size(graph) - 1], graph[np.size(graph) - 3]) graph.CreateEdge(graph[np.size(graph) - 2], graph[np.size(graph) - 3]) return graph
def check_for_nodes(connecting_points, slice_i_plus_1): """ Given a set of stent points, the algorithm checks whether there are any duplicates. A duplicate would mean that that point was found by two stent points and could therefore be a node. An additional check is done to check whether there are no connecting points above the potential node. If this is the case, than the point is not a node. """ #Pointset to store 2D nodes point_nodes = Pointset(2) #Pointset to store point which need to be removed point_remove = Pointset(2) #points in slice_i_plus_1 points_check = stentPoints2d.detect_points(slice_i_plus_1) #For every connecting point for i in range(len(connecting_points)): for j in range(len(connecting_points)): #if points are the same if connecting_points[i].Distance( connecting_points[j]) <= 3 and i != j: #check if this point would find a connecting point point = Pointset(2) point.Append(connecting_points[i]) node_check, unused = find_connecting_points( point, points_check) #Only add node once. if not node_check: if not point_nodes: point_nodes.Append( (connecting_points[i] + connecting_points[j]) / 2) elif point_nodes.Contains(connecting_points[i]) == False: point_nodes.Append( (connecting_points[i] + connecting_points[j]) / 2) #The connecting points need to be removed later point_remove.append(connecting_points[i]) point_remove.append(connecting_points[i]) return point_nodes, point_remove
# Fit circle and sample and show c = fit_cirlce(pp) cc = sample_circle(c, 32) self._cc.SetPoints(cc) # Draw self._cc.Draw() ## Tests if __name__ == "__main__": import visvis as vv tester1 = ChopstickCriteriaTester() # tester2 = FitCircleTester() if False: pp = Pointset(2) pp.append(1,1) pp.append(10,3) pp.append(8,8) pp.append(2,6) pp.append(1,2) fig = vv.figure(103) fig.Clear() a = vv.gca() a.daspectAuto = False fig.position = -799.00, 368.00, 544.00, 382.00 vv.plot(pp, ls='', ms='.', mc='g') c2 = converge_to_centre(pp, Point(6,5), 5, pauseTime=0.3)
def converge_to_centre(pp, c, nDirections=5, maxRadius=50, pauseTime=0): """ converge_to_centre(pp, c) Given a set of points and an initial center point c, will find a better estimate of the center. Returns (c, L), with L indices in pp that were uses to fit the final circle. """ # Shorter names N = nDirections pi = np.pi # Init point to return on error ce = Point(0,0) ce.r = 0 # Are there enough points? if len(pp) < 3: return ce, [] # Init a pointset of centers we've has so far cc = Pointset(2) # Init list with vis objects (to be able to delete them) showObjects = [] if pauseTime: fig = vv.gcf() while c not in cc: # Add previous center cc.append(c) # Calculate distances and angles dists = c.distance(pp) angs = c.angle2(pp) # Get index of closest points i, = np.where(dists==dists.min()) i = iClosest = int(i[0]) # Offset the angles with the angle relative to the closest point. refAng = angs[i] angs = subtract_angles(angs, refAng) # Init L, the indices to the closest point in each direction L = [] # Get closest point on N directions for angle in [float(angnr)/N*2*pi for angnr in range(N)]: # Get indices of points that are in this direction dangs = subtract_angles(angs, angle) I, = np.where(np.abs(dangs) < pi/N ) # Select closest if len(I): distSelection = dists[I] minDist = distSelection.min() J, = np.where(distSelection==minDist) if len(J) and minDist < maxRadius: L.append( int(I[J[0]]) ) # Check if ok if len(L) < 3: return ce, [] # Remove spurious points (points much furter away that the 3 closest) distSelection = dists[L] tmp = [d for d in distSelection] d3 = sorted(tmp)[2] # Get distance of 3th closest point I, = np.where(distSelection < d3*2) L = [L[i] for i in I] # Select points ppSelect = Pointset(2) for i in L: ppSelect.append(pp[i]) # Refit circle cnew = fit_cirlce(ppSelect,False) if cnew.r==0: return ce, [] # Show if pauseTime>0: # Delete for ob in showObjects: ob.Destroy() # Plot center and new center ob1 = vv.plot(c, ls='', ms='x', mc='r', mw=10, axesAdjust=0) ob2 = vv.plot(cnew, ls='', ms='x', mc='r', mw=10, mew=0, axesAdjust=0) # Plot selection points ob3 = vv.plot(ppSelect, ls='', ms='.', mc='y', mw=10, axesAdjust=0) # Plot lines dividing the directions tmpSet1 = Pointset(2) tmpSet2 = Pointset(2) for angle in [float(angnr)/N*2*pi for angnr in range(N)]: angle = -subtract_angles(angle, refAng) dx, dy = np.cos(angle), np.sin(angle) tmpSet1.append(c.x, c.y) tmpSet1.append(c.x+dx*d3*2, c.y+dy*d3*2) for angle in [float(angnr+0.5)/N*2*pi for angnr in range(N)]: angle = -subtract_angles(angle, refAng) dx, dy = np.cos(angle), np.sin(angle) tmpSet2.append(c.x, c.y) tmpSet2.append(c.x+dx*d3*2, c.y+dy*d3*2) ob4 = vv.plot(tmpSet1, lc='y', ls='--', axesAdjust=0) ob5 = vv.plot(tmpSet2, lc='y', lw=3, axesAdjust=0) # Store objects and wait showObjects = [ob1,ob2,ob3,ob4,ob5] fig.DrawNow() time.sleep(pauseTime) # Use new c = cnew # Done for ob in showObjects: ob.Destroy() return c, L
def cluster_points(pp, c, pauseTime=0, showConvergeToo=True): """ cluster_points(pp, c, pauseTime=0) Given a set of points, and the centreline position, this function returns a set of points, which is a subset of pp. The returned pointset is empty on error. This algorithm uses the chopstick clustering implementation. The first step is the "stick" method. We take the closest point from the centre and attach one end of a stick to it. The stick has the length of the radius of the fitted circle. We rotate the stick counterclockwise untill it hits a point, or untill we rotate too far without finding a point, thus failing. When it fails, we try again with a slighly larger stick. This is to close gaps of up to almost 100 degrees. When we reach a point were we've already been, we stop. Afterwards, the set of points is sorted by angle. In the second step we try to add points: we pick two subsequent points and check what other points are closer than "stick" to both these points, where "stick" is the distance between the points. We will break this stick in two and take the best point in the cluster, thus the name: chopstick. BUT ONLY if it is good enough! We draw two lines from the points under consideration to the centre. The angle that these two lines make is a reference for the allowed angle that the two lines may make that run from the two points to the new point. To be precise: ang > (180 - refang) - offset offset is a parameter to control the strictness. The third part of this step consists of removing points. We will only remove points that are closer to the centre than both neighbours. We check each point, comparing it with its two neighbours on each side, applying the same criterion as above. This will remove outliers that lie withing the stent, such as points found due to the contrast fluid... The latter two parts are repeated untill the set of points does not change. Each time the centre is recalculated by fitting a circle. """ # Get better center if showConvergeToo: c, I = converge_to_centre(pp, c, pauseTime=pauseTime) else: c, I = converge_to_centre(pp, c) if not I: return Pointset(2) # Init list with vis objects (to be able to delete them) showObjects = [] if pauseTime: fig = vv.gcf() # Short names pi = np.pi # Minimum and maximum angle that the stick is allowed to make with the line # to the circle-centre. Pretty intuitive... # it is a relative measure, we will multiply it with a ratio: # radius/distance_current_point_to_radius. This allows ellipses to be # segmented, while remaining strict for circles. difAng1_p = 0.0*pi difAng2_p = 0.7*pi ## Step 1, stick method to find an initial set of points # Select start point (3th point returned by converge_to_centre) icurr = I[2] # Init list L, at the beginning only contains icurr L = [icurr] # Largest amount of iterations that makes sense. Probably the loop # exits sooner. maxIter = len(pp) # Enter loop for iter in range(maxIter): # We can think of it as fixing the stick at one end at the current # point and then rotating it in a direction such that a point is # found in the clockwise direction of the current point. We thus # need the angle between the next and current point to be not much # more than the angle between the current point and the circle # cenrre + 90 deg. But it must be larger than the angle between the # current point and the circle centre. # Calculate distances dists = pp.distance(pp[icurr]) # Do the next bit using increasing stick length, untill success for stick in [c.r*1.0, c.r*1.5, c.r*2.0]: # Find subset of points that be "reached by the stick" Is, = np.where(dists<stick) # Calculate angle with circle centre refAng = c.angle2(pp[icurr]) # Culcuate angles with points that are in reach of the stick angs = pp[Is].angle2(pp[icurr]) # Select the points that are in the proper direction # There are TWO PARAMETERS HERE (one important) the second TH can # not be 0.5, because we allow an ellipse. # Use distance measure to make sure not to select the point itself. difAngs = subtract_angles(angs, refAng) difAng2 = difAng2_p # pp[icurr].distance(c) / c.r # Set current too really weid value icurr2, = np.where(Is==icurr) if len(icurr2): difAngs[icurr2] = 99999.0 # Select. If a selection, we're good! II, = np.where( (difAngs > difAng1_p) + (difAngs < difAng2)) if len(II): break else: # No success _objectClearer(showObjects) return Pointset(2) # Select the point with the smallest angle tmp = difAngs[II] inext, = np.where(tmp == tmp.min()) # inext is index in subset. Make it apply to global set inext = Is[ II[ inext[0] ] ] inext = int(inext) # Show if pauseTime>0: # Delete _objectClearer(showObjects) # Show center ob1 = vv.plot(c, ls='', ms='x', mc='r', mw=10, axesAdjust=0) # Show all points ob2 = vv.plot(pp, ls='', ms='.', mc='g', mw=6, axesAdjust=0) # Show selected points L ob3 = vv.plot(pp[L], ls='', ms='.', mc='y', mw=10, axesAdjust=0) # Show next ob4 = vv.plot(pp[inext], ls='', ms='.', mc='r', mw=12, axesAdjust=0) # Show stick vec = ( pp[inext]-pp[icurr] ).normalize() tmp = Pointset(2) tmp.append(pp[icurr]); tmp.append(pp[icurr]+vec*stick) ob5 = vv.plot(tmp, lw=2, lc='b') # Store objects and wait showObjects = [ob1,ob2,ob3,ob4,ob5] fig.DrawNow() time.sleep(pauseTime) # Check whether we completed a full round already if inext in L: break else: L.append(inext) # Prepare for next round icurr = inext # Sort the list by the angles tmp = zip( pp[L].angle2(c), L ) tmp.sort(key=lambda x:x[0]) L = [i[1] for i in tmp] # Clear visualization _objectClearer(showObjects) ## Step 2 and 3, chopstick algorithm to find more points and discard outliers # Init L = [int(i) for i in L] # Make Python ints Lp = [] round = 0 # Iterate ... while Lp != L and round < 20: round += 1 #print 'round', round # Clear list (but store previous) Lp = [i for i in L] L = [] # We need at least three points if len(Lp)<3: _objectClearer(showObjects) print('oops: len(LP)<3' ) return [] # Recalculate circle c = fit_cirlce(pp[Lp], False) if c.r == 0.0: print('oops: c.r==0' ) _objectClearer(showObjects) return [] # Step2: ADD POINTS for iter in range(len(Lp)): # Current point icurr = Lp[iter] if iter < len(Lp)-1: inext = Lp[iter+1] else: inext = Lp[0] # Prepare, get p1 and p2 p1 = pp[icurr] p2 = pp[inext] # Apply masks to points in pp M1, M2 = chopstick_criteria(c, p1, p2, pp) # Combine measures. I is now the subset (of p) of OK points I, = np.where(M1*M2) if not len(I): L.append(icurr) continue elif len(I)==1: ibetw = int(I) else: # Multiple candidates: find best match pptemp = pp[I] dists = p1.distance(pptemp) + p2.distance(pptemp) II, = np.where( dists==dists.min() ) ibetw = int( I[II[0]] ) # Add point L.append(icurr) if not ibetw in L: L.append(ibetw) # Check assert ibetw not in [icurr, inext] # Draw if pauseTime>0: # Delete _objectClearer(showObjects) # Show center ob1 = vv.plot(c, ls='', ms='x', mc='r', mw=10, axesAdjust=0) # Show all points ob2 = vv.plot(pp, ls='', ms='.', mc='g', mw=6, axesAdjust=0) # Show selected points L ob3 = vv.plot(pp[L], ls='', ms='.', mc='y', mw=10, axesAdjust=0) # Show between and vectors ob4 = vv.plot(pp[ibetw], ls='', ms='.', mc='r', mw=12, axesAdjust=0) ob5 = vv.plot(pp[[icurr, ibetw, inext]], ls='-', lc='g', axesAdjust=0) ob6 = vv.plot(pp[[icurr, inext]], ls=':', lc='g', axesAdjust=0) # Store objects and wait showObjects = [ob1,ob2,ob3,ob4,ob5,ob6] fig.DrawNow() time.sleep(pauseTime) # Lpp stores the set of points we have untill now, we will refill the # set L, maybe with less points Lpp = [int(i) for i in L] L = [] # Step3: REMOVE POINTS for iter in range(len(Lpp)): # Current point and neighbours ibetw = Lpp[iter] if iter<len(Lpp)-1: inext = Lpp[iter+1] else: inext = Lpp[0] if iter>0: icurr = Lpp[iter-1] else: icurr = Lpp[-1] # Test # print icurr, ibetw, inext assert ibetw not in [icurr, inext] # Prepare, get p1 and p2 and p3 p1 = pp[icurr] p2 = pp[inext] p3 = pp[ibetw] # Apply masks to points in pp M1, M2 = chopstick_criteria(c, p1, p2, p3) M = M1*M2 # Do we keep the point? if M.sum(): L.append(ibetw) # Draw if pauseTime>0 and not M.sum(): # Delete _objectClearer(showObjects) # Show center ob1 = vv.plot(c, ls='', ms='x', mc='r', mw=10, axesAdjust=0) # Show all points ob2 = vv.plot(pp, ls='', ms='.', mc='g', mw=6, axesAdjust=0) # Show selected points L ob3 = vv.plot(pp[L], ls='', ms='.', mc='y', mw=10, axesAdjust=0) # Show between and vectors ob4 = vv.plot(pp[ibetw], ls='', ms='.', mc='r', mw=12, axesAdjust=0) ob5 = vv.plot(pp[[icurr, ibetw, inext]], ls='-', lc='r', axesAdjust=0) ob6 = vv.plot(pp[[icurr, inext]], ls='-', lc='g', axesAdjust=0) # Store objects and wait showObjects = [ob1,ob2,ob3,ob4,ob5,ob6] fig.DrawNow() time.sleep(pauseTime) # Done if round == 20: print('Warning: chopstick seemed not to converge.') _objectClearer(showObjects) #print 'cluster end', len(L) return pp[L]