def find_centerline_from_loop(loop, ap_coords): """Given a loop or lariat structure, do as good a job as possible in finding a plausible start/end point for the centerline, then return path coordinates along that line.""" # Basic strategy: assume that the worm nose/tail comes together at some point # on the loop. Assume that this point is midway between the lowest and highest # value of the ap_coords array along that loop. Then break the loop at that # point, and find the shortest path from the nose to tail. # This doesn't deal wonderfully with lariat structures, but that's a really hard # problem to solve correctly unless the ap coordinate image is really reliable # which it currently is not... masked_coords = ap_coords.copy() masked_coords[~loop] = numpy.nan low = numpy.unravel_index(numpy.nanargmin(masked_coords), masked_coords.shape) high = numpy.unravel_index(numpy.nanargmax(masked_coords), masked_coords.shape) costs = numpy.array(loop, dtype=numpy.float32) costs[~loop] = numpy.inf path, cost = graph.route_through_array(costs, low, high, geometric=False) midpoint = len(path) // 2 costs[tuple(numpy.transpose(path[midpoint - 1:midpoint + 2]))] = numpy.inf # break the loop path, cost = graph.route_through_array(costs, path[midpoint - 2], path[midpoint + 2], geometric=False) return numpy.array(path)
def crossover(route1, route2, timeGrid): """ combines two routes of the population to two new routes Parameters ---------- route1 : array first route which should be used route2 : array second route which schould be used timeGrid: array a grid containing the time for one specific context of bearing and speed """ timeGridNew= [[random.random() for i in range(timeGrid.shape[1])] for j in range(timeGrid.shape[0])] timeGridNew = np.where(timeGrid >999, timeGrid, timeGridNew) index1 = math.floor(random.random()*min(len(route1), len(route2))) index2 = min(len(route2) -1, len(route1)-1, (index1 + math.floor(random.random()*(len(route1) -index1)+ 10))) crossoverPoint1 = route1[index1] crossoverPoint2 = route2[index2] crossoverRoute1, weight = route_through_array(timeGridNew, crossoverPoint1[0:2], crossoverPoint2[0:2], fully_connected=False, geometric=True) crossoverRoute1= makeArrays(crossoverRoute1) crossoverPoint1 = route2[index1] crossoverPoint2 = route1[index2] crossoverRoute2, weight = route_through_array(timeGridNew, crossoverPoint1[0:2], crossoverPoint2[0:2], fully_connected=False, geometric=True) crossoverRoute2= makeArrays(crossoverRoute2) child1= [] child2= [] for i in range(index1-1): child1.append(route1[i]) child2.append(route2[i]) for x in crossoverRoute1: child1.append(x) for x in crossoverRoute2: child2.append(x) for i in range(index2 +1,len(route2)): child1.append(route2[i]) for i in range(index2 +1,len(route1)): child2.append(route1[i]) return[child1, child2, crossoverRoute1]
def __init__(self, image): digit_seg = image > 20 self._skeleton = morphology.skeletonize(digit_seg) morphology.remove_small_objects(self._skeleton, min_size=8, connectivity=2, in_place=True) self.top_left = max_score_point(lambda i, j: -1 * (i ** 2 + j ** 2), self._skeleton) self.top_right = max_score_point(lambda i, j: -1 * (i ** 2 + (SIZE - 3 - j) ** 2), self._skeleton) self.bottom = max_score_point(lambda i, j: i, self._skeleton) g = np.where(self._skeleton, 1, 300) top, _ = graph.route_through_array(g, self.top_left, self.top_right, geometric=False) leg, _ = graph.route_through_array(g, self.top_right, self.bottom, geometric=False) self.features = { 'top': segment_path(top, digit_seg, width=5), 'leg': segment_path(leg, digit_seg, width=5), }
def create_path(self, raster, costSurfaceArray, start, end): """ Calculate the least cost path :param raster: Raster file :param costSurfaceArray: raster file as numeric array :param start: start point :param end: end point :return: least cost path as grid array """ # coordinates to array index startCoordX = start.x startCoordY = start.y startIndexX, startIndexY = self.coord2pixeloffset( raster, startCoordX, startCoordY) stopCoordX = end.x stopCoordY = end.y stopIndexX, stopIndexY = self.coord2pixeloffset( raster, stopCoordX, stopCoordY) # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY, startIndexX), (stopIndexY, stopIndexX), geometric=True, fully_connected=True) indices = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = 1 return path
def createPath(CostSurfacefn, costSurfaceArray, startCoord, stopCoord): # coordinates to array index startCoordX = startCoord[0] startCoordY = startCoord[1] startIndexX, startIndexY = coord2pixelOffset(CostSurfacefn, startCoordX, startCoordY) stopCoordX = stopCoord[0] stopCoordY = stopCoord[1] stopIndexX, stopIndexY = coord2pixelOffset(CostSurfacefn, stopCoordX, stopCoordY) # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY, startIndexX), (stopIndexY, stopIndexX), geometric=False, fully_connected=True) #mcp_init = MCP(costSurfaceArray, offsets=None, fully_connected=True, sampling=None) #indices, weight = mcp_init.find_costs(costSurfaceArray, (startIndexY,startIndexX), (stopIndexY,stopIndexX)) indices = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = 1 return path
def createPath(CostSurfacefn, costSurfaceArray, startCoord, stopCoord, pathValue=255): print np.min(costSurfaceArray) print costSurfaceArray.shape print startCoord print stopCoord # coordinates to array index startCoordX = startCoord[0] startCoordY = startCoord[1] startIndexX, startIndexY = coord2pixelOffset(CostSurfacefn, startCoordX, startCoordY) stopCoordX = stopCoord[0] stopCoordY = stopCoord[1] stopIndexX, stopIndexY = coord2pixelOffset(CostSurfacefn, stopCoordX, stopCoordY) print startIndexX, startIndexY, stopIndexX, stopIndexY # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY, startIndexX), (stopIndexY, stopIndexX), geometric=True, fully_connected=True) indices = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = pathValue return path, weight
def leastCostSolution(imgArr, boundarySide1indices, boundarySide2indices, step): weight = 1e22 indices = [] for b1 in range(len(boundarySide1indices)): if b1 % step == 0: startPoint = np.array(boundarySide1indices[b1], dtype=int) #if b1 % step == 0: # print(' '+str(b1+1)+' of '+str(len(boundarySide1indices))+' indices tested') for b2 in range(len(boundarySide2indices)): if b2 % step == 0: endPoint = np.array(boundarySide2indices[b2], dtype=int) testIndices, testWeight = route_through_array(imgArr, (startPoint[1], startPoint[0]),\ (endPoint[1], endPoint[0]), geometric=True,\ fully_connected=True) tmpIndices = np.array(testIndices) testIndices = np.hstack([ np.reshape(tmpIndices[:, 1], (np.shape(tmpIndices)[0], 1)), np.reshape(tmpIndices[:, 0], (np.shape(tmpIndices)[0], 1)) ]) if testWeight < weight: weight = testWeight indices = testIndices return (indices)
def get_path_from_cost_surface(cost_dataset, start_point, dest_point): CostSurfacefn = "C:\\Users\\Diego\\Desktop\\python_workspace\\10x10_bsp.tif" # coordinates to array index startCoordX = start_point.x startCoordY = start_point.y startIndexX, startIndexY = cookbook.coord2pixelOffset( CostSurfacefn, startCoordX, startCoordY) stopCoordX = dest_point.x stopCoordY = dest_point.y stopIndexX, stopIndexY = cookbook.coord2pixelOffset( CostSurfacefn, stopCoordX, stopCoordY) costSurfaceArray = convert_Tiff_raster_to_array(CostSurfacefn) _display_array(costSurfaceArray) print("##############") # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY, startIndexX), (stopIndexY, stopIndexX), geometric=True, fully_connected=True) indices = numpy.array(indices).T path = numpy.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = 1 _display_array(path) return path
def createPath(CostSurfacefn,costSurfaceArray,selectedCell,gridfn): # get index of selected cell selectedCellIndexX,selectedCellIndexY = cellID2cellIndex(selectedCell,gridfn,CostSurfacefn) # get index of existing road (first point that occurs) StraightLineDict = {} count = 0 roadIndexY, roadIndexX = np.where(costSurfaceArray == 0) while count < len(roadIndexY): dist = sqrt((selectedCellIndexY-roadIndexY[count])**2+(selectedCellIndexX-roadIndexX[count])**2) index = (roadIndexY[count],roadIndexX[count]) StraightLineDict[index] = dist count +=1 roadIndex = min(StraightLineDict, key=StraightLineDict.get) # create path indices, weight = route_through_array(costSurfaceArray, (selectedCellIndexY,selectedCellIndexX), roadIndex) indices = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = 1 # merge path with existing roads costSurfaceArray[path == 1] = 0 return costSurfaceArray
def calculate_path_minimum_profile(de_Lab, start_location, end_location, r_exp_mm, h_ref_microns): [RR, HH] = np.meshgrid(r_exp_mm, h_ref_microns) r_start_index = np.array(np.where(r_exp_mm == start_location[0])) h_start_index = np.array(np.where(h_ref_microns == start_location[1])) r_end_index = np.array(np.where(r_exp_mm == end_location[0])) h_end_index = np.array(np.where(h_ref_microns == end_location[1])) indices, weight = route_through_array(de_Lab, [h_start_index, r_start_index], [h_end_index, r_end_index], fully_connected=True, geometric=True) indices1 = np.asarray(indices) r_path = RR[indices1[:, 0], indices1[:, 1]] h_path = HH[indices1[:, 0], indices1[:, 1]] path = np.array([r_path, h_path]).T points = np.array([start_location, end_location]) return path, points
def least_cost(image, start, end): indices, cost = route_through_array(image, start, end) indices = np.array(indices).T path = np.zeros_like(image) path[indices[0], indices[1]] = 1 return path, cost
def createPath(costSurfaceArray, startIndexX, startIndexY, stopIndexX, stopIndexY): indices, weight = route_through_array(costSurfaceArray, (startIndexY, startIndexX), (stopIndexY, stopIndexX), geometric=True, fully_connected=True) indices = np.array(indices).T return indices
def highlight(img, nodes): #axon = np.load('data/s3m0v0/coordinates_axon.npy') axon = [] for startP, endP in nodes: cost = np.array(route_through_array(img, startP, endP)[0]) for i in cost: img[tuple(i)] = 255 axon.append(i) return img, axon
def plot_tracks(image, trk_averages, trk_points): ''' ''' if trk_averages is None: print('No averages (possibly only one track).') return None image_rgb = gray2rgb(image) # preparing the plot window. fig, ax = plt.subplots(nrows=1, ncols=1) label_text = '' for trk_idx, trk_pt in enumerate(trk_points): # calculating route and distances. route, _ = route_through_array(~image, trk_pt[0], trk_pt[1]) distances, _, _ = track_parameters(trk_pt[0], trk_pt[1], route) # generating minimal distance line. rows, cols = line(trk_pt[0][0], trk_pt[0][1], trk_pt[1][0], trk_pt[1][1]) image_rgb[rows, cols] = [0, 255, 0] # plotting minimal distance and route. ax.imshow(image_rgb, cmap='gray') for rt_pt in route: ax.scatter(rt_pt[1], rt_pt[0], c='b') #plotting extreme points. for pt in trk_pt: ax.scatter(pt[1], pt[0], c='g') # preparing the label text. label_text += 'Track index: ' + str(trk_idx) + \ '\n Extreme points: ' + str(trk_pt[0]) + ', ' + str(trk_pt[1]) + \ '\n Length (route): ' + str(len(route)) + \ '\n Euclidean distance: ' + str(distances[0]) + \ '\n Difference: ' + str(distances[1]) + '\n\n' # removing plot ticks. ax.axes.get_xaxis().set_ticks([]) ax.axes.get_yaxis().set_ticks([]) # removing plot 'spines'. ax.spines['top'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['left'].set_visible(False) ax.spines['right'].set_visible(False) # setting x label. ax.set_xlabel(label_text) return None
def plot_and_count(image_bin, intensity_image=None, ax=None): """ """ if ax is None: ax = plt.gca() total_tracks = 0 props = regionprops(label(image_bin)) img_skel = skeletonize_3d(image_bin) rows, cols = np.where(img_skel != 0) if intensity_image is not None: img_rgb = gray2rgb(img_as_ubyte(intensity_image)) else: img_rgb = gray2rgb(img_as_ubyte(image_bin)) img_rgb[rows, cols] = [255, 0, 255] for prop in props: obj_info = [] aux = skeletonize_3d(prop.image) trk_area, trk_px = tracks_classify(aux) count_auto = count_by_region(regions_and_skel(prop.image)) total_tracks += np.sum(count_auto[2]) x_min, y_min, x_max, y_max = prop.bbox obj_info.append( [prop.centroid[0], prop.centroid[1], str(count_auto[2][0][0])]) for obj in obj_info: ax.text(obj[1], obj[0], obj[2], family='monospace', color='yellow', size='medium', weight='bold') if trk_area is not None: for px in trk_px: route, _ = route_through_array(~aux, px[0], px[1]) for rx in route: ax.scatter(y_min + rx[1], x_min + rx[0], s=20, c='b') for p in px: ax.scatter(y_min + p[1], x_min + p[0], s=20, c='g') rows, cols = line(x_min + px[0][0], y_min + px[0][1], x_min + px[1][0], y_min + px[1][1]) img_rgb[rows, cols] = [0, 255, 0] ax.imshow(img_rgb, cmap='gray') return ax, total_tracks
def __init__(self, image): digit_seg = image > 20 self._skeleton = morphology.skeletonize(digit_seg) morphology.remove_small_objects(self._skeleton, 8, connectivity=2, in_place=True) self.top_left = self._find_top_left() self.top_right = max_score_point(lambda x, y: -1 * (x ** 2 + (SIZE - y) ** 2), self._skeleton) self.bottom_left = self._find_bottom_left() self.bottom_right = max_score_point(lambda x, y: -1 * ((SIZE - x) ** 2 + (SIZE - y) ** 2), self._skeleton) self.center = self._find_center() g = np.where(self._skeleton, 1, 300) top1, _ = graph.route_through_array(g, self.top_left, self.top_right, geometric=False) top2, _ = graph.route_through_array(g, self.top_right, self.center, geometric=False) bottom1, _ = graph.route_through_array(g, self.bottom_left, self.bottom_right, geometric=False) bottom2, _ = graph.route_through_array(g, self.bottom_right, self.center, geometric=False) self.features = { 'top_half': segment_path(top1 + top2, digit_seg), 'bottom_half': segment_path(bottom1 + bottom2, digit_seg), }
def shortest_path(self,p1,p2): # Based on Dijkstra's minimum cost path algorithm start = self.coord2pixelOffset(*p1)[::-1] #We need to reverse them for the route_to_array end = self.coord2pixelOffset(*p2)[::-1] indices, weight = route_through_array( self.costRaster, start,end,geometric=True,fully_connected=True) indices = np.array(indices).T path = np.zeros_like(self.costRaster) path[indices[0], indices[1]] = 1 return path, weight
def tracks_classify(image): """ """ px_ext, px_int = pixels_interest(image) trk_areas, trk_points = [], [] # first, checking the extreme pixels. if len(px_ext) == 2: # only one track (two extreme points). return None, None while len(px_ext) != 0: areas, points = [], [] if len(px_ext) >= 2: for i, j in product(px_ext, px_ext): if ((j, i) not in points) and (i is not j): route, _ = route_through_array(~image, i, j) points.append([i, j]) areas.append(tracks_by_area(i, j, route, image.shape)) else: # then, checking extreme vs. intersection pixels. for i, j in product(px_ext, px_int): if ((j, i) not in points) and (i is not j): route, _ = route_through_array(~image, i, j) points.append([i, j]) areas.append(tracks_by_area(i, j, route, image.shape)) # check if an extreme pixel united with an intersection pixel # has another part (constituted by an inter px + an ext px) # get the best candidate, erase the points and repeat. min_idx = min(enumerate(areas), key=itemgetter(1))[0] trk_areas.append(areas[min_idx]) trk_points.append(points[min_idx]) for point in points[min_idx]: if point in px_ext: px_ext.remove(point) return trk_areas, trk_points
def mblur(inpts,image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (3, 3), 0) edges = auto_canny(blurred) edges[-1,:] = 255 edges[0,:] = 255 edges[:,-1] = 255 edges[:,0] = 255 totpt = len(inpts) path = np.zeros_like(edges) H,W = edges.shape x,y = np.meshgrid(np.arange(W),np.arange(H)) for i in range(totpt): sigma2 = ((inpts[(i+1)%totpt][1]-inpts[i][1])**2 + (inpts[(i+1)%totpt][0]-inpts[i][0])**2 )/4 gmask = np.exp(-((x-inpts[i][1])**2 + (y-inpts[i][0])**2)/(2.0*100.0)) gmask += np.exp(-((x-inpts[(i+1)%totpt][1])**2 + (y-inpts[(i+1)%totpt][0])**2)/(2.0*sigma2)) emask = np.multiply(gmask,edges)/gmask.max() emask = 255 - emask indices, weight = route_through_array(emask, inpts[i],inpts[(i+1)%totpt]) indices = np.array(indices).T path[indices[0], indices[1]] = 1 cent = ndimage.measurements.center_of_mass(path) h,w = path.shape mask = np.zeros((h+2,w+2),dtype=np.uint8) cv2.floodFill(path, mask, (int(cent[1]),int(cent[0])), 1) skeleton = morphology.skeletonize(path) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3)) dilim = cv2.dilate(path,kernel,iterations=10) erodim = cv2.erode(path,kernel,iterations=5) dilb = cv2.dilate(dilim,kernel) - dilim im2, contours, hierarchy = cv2.findContours(dilim,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) x,y,w,h = cv2.boundingRect(contours[0]) grabmask = np.zeros_like(path) # grabmask[y:y+h,x:x+w] = 2 grabmask[dilim==1] = 3 grabmask[dilb==1] = 2 grabmask[skeleton==1] = 1 bgdModel = np.zeros((1,65),np.float64) fgdModel = np.zeros((1,65),np.float64) grabmask, bgdModel, fgdModel = cv2.grabCut(image,grabmask,None,bgdModel,fgdModel,1,cv2.GC_INIT_WITH_MASK) mask2 = np.where((grabmask==2) | (grabmask==0),0,1).astype('uint8') out1 = image*mask2[:,:,np.newaxis] mask2 = np.bitwise_or(mask2,erodim) out = image*mask2[:,:,np.newaxis] ksize = 31 kernel = np.zeros((ksize,ksize)) kernel[int((ksize-1)/2),:] = np.ones(ksize) kernel/=ksize bimg = cv2.filter2D(image,-1,kernel) bimg[mask2==1] = 0 bimg = bimg + out plt.imsave("bImg",bimg) return bimg
def get_perimeter(image_cost, waypoints): path = [] for idx in range(0, len(waypoints)): start_point = waypoints[idx] end_point = waypoints[(idx + 1) % len(waypoints)] path_segment, cost = graph.route_through_array(image_cost, start_point, end_point) for e in path_segment: path.append(e) return path
def route_through(ips, snap, poins,para): para['max']=True para['lcost']=2 img = snap.astype('float32') if para['max']: img *= -1 np.add(img, para['lcost']-img.min(), casting='unsafe', out=img) minv, maxv = ips.range routes = [] for line in poins: pts = np.array(list(zip(line[:-1], line[1:]))) for p0, p1 in pts[:,:,::-1].astype(int): indices, weight = route_through_array(img, p0, p1) routes.append(indices) return routes
def postGetPath(): import cv2 import numpy from skimage import graph import cells.images.filter project_hash = request.form['project_hash'] project = get_project(project_hash) task_hash = request.form['task_hash'] task_hash_decoded = base64.urlsafe_b64decode(task_hash.encode("ascii")) task_type = task_hash_decoded.split("::")[0] task_key = task_hash_decoded.split("::")[1] image_name = task_key image_file = os.path.join(project.get_input_image_directory(), image_name) point_1_x = int(request.form['point_1_x']) point_1_y = int(request.form['point_1_y']) point_2_x = int(request.form['point_2_x']) point_2_y = int(request.form['point_2_y']) start_r, start_c = point_1_y, point_1_x end_r, end_c = point_2_y, point_2_x start_point = point_1_y, point_1_x end_point = point_2_y, point_2_x input_image = cv2.imread(image_file) cost_image = cells.images.filter.generate_cost_image_perimeter(input_image) # image_intensity = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY) #cost_image = image_intensity.copy() #cost_image = (numpy.max(cost_image) - cost_image) print start_point, end_point path_segment, cost = graph.route_through_array(cost_image, start_point, end_point) path = [] x_y_path = [] for e in path_segment: path.append(e) x_y_path.append([e[1], e[0]]) return jsonify(x_y_path)
def shortest_path(self, p1, p2): # Based on Dijkstra's minimum cost path algorithm start = self.coord2pixelOffset( *p1)[::-1] #We need to reverse them for the route_to_array end = self.coord2pixelOffset(*p2)[::-1] indices, weight = route_through_array(self.costRaster, start, end, geometric=True, fully_connected=True) indices = np.array(indices).T path = np.zeros_like(self.costRaster) path[indices[0], indices[1]] = 1 return path, weight
def shortest_path(self): self.paths = [] #if segments is empty then don't do anything if len(self.segments) == 0: return [] for s in self.segments: imshape = self.im.shape xmin = min(s[0][0], s[1][0]) xmax = max(s[0][0], s[1][0]) ymin = min(s[0][1], s[1][1]) ymax = max(s[0][1], s[1][1]) #Calculate local image to compute shortet path, including a buffer around the points of 100px (maybe this should be base?) buffer_v = 100 xmin = int( max(xmin - buffer_v, 0) ) #n.b. min/max args ensure bounds remain within the image... ymin = int(max(ymin - buffer_v, 0)) xmax = int(min(xmax + buffer_v, self.imshape[0])) ymax = int(min(ymax + buffer_v, self.imshape[1])) #extract local image im = self.im[xmin:xmax, ymin:ymax] #get start and end points in local-image coords start = [int(s[0][0] - xmin), int(s[0][1] - ymin)] end = [int(s[1][0] - xmin), int(s[1][1] - ymin)] #compute shortest path path, cost = route_through_array(im, start, end, fully_connected=True, geometric=True) if len(self.paths) > 0: if path[0][0] + xmin != self.paths[-1][0] or \ path[0][1] + ymin != self.paths[-1][1]: for p in reversed(path): self.paths.append([p[0] + xmin, p[1] + ymin]) else: for p in path: self.paths.append([p[0] + xmin, p[1] + ymin]) else: for p in path: self.paths.append([p[0] + xmin, p[1] + ymin]) #paths2 = [] return self.paths #self.segments
def __find_paths(self): logger.debug("Generating Internal Edges...") # By using the object image as a mask we find only internal edges and disregard silhouette edges. edge_image = feature.canny(self.surface.z_image, sigma=1, mask=self.surface.obj_image.astype(bool)) # Identify all continuous lines. labels = measure.label(edge_image, connectivity=2) for region in measure.regionprops(labels): image = region.image if image.shape < (3, 3): continue # Condition the line to ensure each cell has only one or two neighbours (remove "L"s). image = morphology.skeletonize(image) # A cell with only one neighbour can be considered as the end of a continuous line, i.e # the sum of a cell's 8 surrounding neighbours will equal 1. kernel = [[1, 1, 1], [1, 0, 1], [1, 1, 1]] convolved = ndimage.convolve(image.astype(float), kernel, mode="constant") # Refine the convolution to only contain cells which were present in the original image. convolved[util.invert(image)] = 0 # Extract the region coordinates of cells which meet the condition for being a start/end position. terminator_pair = np.argwhere(convolved == 1) if len(terminator_pair) < 2: # Unable to extract two meaningful points. continue # Make edges have a value of 0 ("cheap"). image = util.invert(image) # An ordered list of coordinates for each line is now found by computing the cheapest route between each # terminator_pair. route, cost = graph.route_through_array(image, terminator_pair[0], terminator_pair[1]) # Convert regional route coords back to global coords. region_offset = [region.bbox[0], region.bbox[1]] coords = np.array(route) + region_offset self.paths.append(Path(coords, is_rc=True))
def cellbody_skeletonization(distmap, threshold, maxthre, physicspacing): """ Function to extract the cell body skeleton after madial axis transform thresholding. :param distmap: :param threshold: :return: """ skelCellBody = { 'skeleton': [], 'endpointCoord': [], 'paths': [], 'legnths': [], 'physical-space': physicspacing, 'threshold': threshold, 'maxthreshold': maxthre } BodyThreshInd = distmap < threshold distCellBody = deepcopy(distmap) distCellBody[BodyThreshInd] = 0 # if more separate regions are selected, choose the longest one as cell body skeleton by label each separated region # and disacrding the shortest ones. structure = np.ones( (3, 3), dtype=np.int) # in this case we allow any kind of connection labeled, ncomponents = label(distCellBody, structure) regprop = regionprops(labeled) len_region = [i.perimeter for i in regprop] skelCellBody['skeleton'] = labeled == (np.argmax(len_region) + 1) # cell body edge detection skelCellBody['endpointCoord'] = edgepoint_detect(skelCellBody['skeleton']) pathCellBody = [] cellBodyLengths = [] for coord in skelCellBody['endpointCoord'][1:]: Path, _ = route_through_array(skelCellBody['skeleton'] == 0, start=skelCellBody['endpointCoord'][0], end=coord) Path = np.array(Path) pathCellBody.append(Path) Length = path_length(Path, skelCellBody['skeleton'], physicspacing) cellBodyLengths.append(Length) skelCellBody['paths'] = pathCellBody skelCellBody['lengths'] = cellBodyLengths return skelCellBody
def compute_linestrings(direction): log.ODM_INFO("Computing %s cutlines" % direction) # Initialize cost map cost_map = np.full((height, width), 1, dtype=np.float32) # Write edges to cost map cost_map[edges == True] = 0 # Low cost # Write "barrier, floor is lava" costs if direction == 'vertical': lines = [((i, 0), (i, height - 1)) for i in range(line_hor_offset, width - line_hor_offset, line_hor_offset)] points = [] pad_x = int(line_hor_offset / 2.0) for i in range(0, len(lines)): a, b = lines[i] points.append(((a[0] - pad_x, a[1]), (b[0] - pad_x, b[1]))) a, b = lines[-1] points.append(((a[0] + pad_x, a[1]), (b[0] + pad_x, b[1]))) else: lines = [((0, j), (width - 1, j)) for j in range(line_ver_offset, height - line_ver_offset, line_ver_offset)] points = [] pad_y = int(line_ver_offset / 2.0) for i in range(0, len(lines)): a, b = lines[i] points.append(((a[0], a[1] - pad_y), (b[0], b[1] - pad_y))) a, b = lines[-1] points.append(((a[0], a[1] + pad_y), (b[0], b[1] + pad_y))) for a, b in lines: rr, cc = line(*a, *b) cost_map[cc, rr] = 9999 # Lava # Calculate route for a, b in points: line_coords, cost = route_through_array(cost_map, (a[1], a[0]), (b[1], b[0]), fully_connected=True, geometric=True) # Convert to geographic geo_line_coords = [f.xy(*c) for c in line_coords] # Simplify ls = LineString(geo_line_coords) linestrings.append(ls.simplify(0.05, preserve_topology=False))
def find_route(skeleton, begin, end): """ Perform shortest path algorithm on skeleton image Find the shortest route on a skeleton between 2 pixels using graph shortest path algorithm :param skeleton: (numpy 2D array of binary uint8) representing the skeleton of side view image of a maize plant :param begin: (list of 2 int) pixel of the bottom of the stem :param end: (list of 2 int) pixel of the top of the stem :return: (list of list of 2 int) list of all the pixels to follow to get the shortest path between begin and end """ skeleton_inverted = np.array(skeleton) skeleton_inverted[skeleton_inverted == 0] = 255 return graph.route_through_array(skeleton_inverted, begin, end)[0]
def find_path(start, end, skel): """Find the shortest path along the skeleton from point to point.""" # route_through_array treats zeros as having zero cost. In our case, zeros # are points off the path. Mark them as -1 so route_through_array ignores # them. skel = np.where(skel > 0, 1, -1) # TODO: Why do matplotlib/skimage not agree on array shapes? route, cost = graph.route_through_array(skel, [start[1], start[0]], [end[1], end[0]], fully_connected=True) ys, xs = zip(*route) xs, ys = np.asarray(xs), np.asarray(ys) return xs, ys, cost
def _plot_aux(image, ax=None): """ """ if ax is None: ax = plt.gca() img_skel = skeletonize_3d(image) rows, cols = np.where(img_skel != 0) img_rgb = gray2rgb(img_as_ubyte(image)) img_rgb[rows, cols] = [255, 0, 255] ax.imshow(img_rgb, cmap='gray') props = regionprops(label(image)) for prop in props: aux = skeletonize_3d(prop.image) trk_area, trk_px = ds.tracks_classify(aux) x_min, y_min, x_max, y_max = prop.bbox if trk_area is not None: for px in trk_px: route, _ = route_through_array(~aux, px[0], px[1]) # plotting route. for rx in route: ax.scatter(y_min + rx[1], x_min + rx[0], c='b', s=SCATTER_SIZE + 25) # plotting extreme points. for p in px: ax.scatter(y_min + p[1], x_min + p[0], c='g', s=SCATTER_SIZE + 25) rows, cols = line(x_min + px[0][0], y_min + px[0][1], x_min + px[1][0], y_min + px[1][1]) img_rgb[rows, cols] = [0, 255, 0] return ax
def createPath(costSurfaceArray,startCoord,stopCoord): # coordinates to array index startCoordX = startCoord[0] startCoordY = startCoord[1] startIndexX,startIndexY = coord2pixelOffset(startCoordX,startCoordY) stopCoordX = stopCoord[0] stopCoordY = stopCoord[1] stopIndexX,stopIndexY = coord2pixelOffset(stopCoordX,stopCoordY) # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY,startIndexX), (stopIndexY,stopIndexX),geometric=True,fully_connected=True) indices = np.array(indices).T indices = indices.astype(float) indices[1] = indices[1]*pixelWidth + originX indices[0] = indices[0]*pixelHeight + originY return indices
def compute_lcps(node_dict, cost_map, snow): # for each node compute LCPs to all other nodes for k in node_dict: lcp_start = node_dict[k]['src'] for d in node_dict[k]['dsts']: lcp_stop = node_dict[k]['dsts'][d]['coords'] indices, weight = graph.route_through_array(cost_map, lcp_start, lcp_stop, fully_connected=True, geometric=True) node_dict[k]['dsts'][d]['lcp indices'] = indices node_dict[k]['dsts'][d]['lcp cost'] = weight indices = np.array(indices).T path = np.zeros_like(snow) path[indices[0], indices[1]] = 1 node_dict[k]['dsts'][d]['route'] = path return node_dict
def createPath(cost_surface_path, startCoord,stopCoord): costSurfaceArray = raster2array(cost_surface_path) # coordinates to array index startCoordX = startCoord[0] startCoordY = startCoord[1] startIndexX,startIndexY = coord2pixelOffset(cost_surface_path,startCoordX,startCoordY) stopCoordX = stopCoord[0] stopCoordY = stopCoord[1] stopIndexX,stopIndexY = coord2pixelOffset(cost_surface_path,stopCoordX,stopCoordY) # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY,startIndexX), (stopIndexY,stopIndexX),geometric=True,fully_connected=True) indiceT = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indiceT[0], indiceT[1]] = 1 return path, indices
def mutation(crossover_child, timeGrid): """ mutates one route, so changes a random part of it Parameters ---------- crossover_child : array one route the ship is going, returned by the crossover """ timeGridNew = [[random.random() for i in range(timeGrid.shape[1])] for j in range(timeGrid.shape[0])] timeGridNew = np.where(timeGrid > 999, timeGrid, timeGridNew) crossover_child_split_list = [] crossover_child = makeArrays(crossover_child) #split crossover over child into 3 lists randomNumber = random.random() * len(crossover_child) startIndex = math.floor(randomNumber) startpoint = crossover_child[startIndex] endIndex = math.floor(startIndex + (random.random() * (len(crossover_child) - startIndex))) endpoint = crossover_child[endIndex] # recalculate route from end point of list 1 to sarting point of list 3 route, weight = route_through_array(timeGridNew, startpoint[0:2], endpoint[0:2], fully_connected=False, geometric=True) route_list = makeArrays(route) first_component = makeArrays(crossover_child[0:startIndex]) second_component = route_list[ 0:-1] #filter out starting point and endpoint from the new mid list third_component = makeArrays( crossover_child[endIndex:len(crossover_child)]) #combine all the sections to a final mutated route mutated_child = first_component + second_component + third_component #print(mutated_child) return mutated_child
def splitTwo(pred, subCCs, cc): if pred.shape[0] > 1 and pred.shape[1] > 1: midY = subCCs.shape[0] / 2 g = np.zeros(pred.shape) for x in range(0, subCCs.shape[1]): for y in range(0, subCCs.shape[0]): if subCCs[y, x] == cc: g[y, x] = 100 else: g[y, x] = 0.01 + abs(y - midY) * 0.001 indices, weight = route_through_array(g, (midY, 0), (midY, subCCs.shape[1] - 1), fully_connected=False) #print '--------------------' #print indices #print '--------------------' for p in indices: if subCCs[p] == cc: subCCs[p] = 0 pred[p] = 0
def skeletonizeWorm(self): self.skeletonizedWormImage = morphology.skeletonize(self.bwWormImage) skeletonEnds = wp.find1Cpixels(self.skeletonizedWormImage) skeletonEndPts = cv2.findNonZero(np.uint8(skeletonEnds)) if skeletonEndPts is None: skeletonEndPts = [] nEndPts = len(skeletonEndPts) if nEndPts < 2: # skeleton is a cirle (Omega turn) self.badSkeletonization = True self.crossedWorm = True elif nEndPts > 2: # skeleton has spurs self.badSkeletonization = True else: skeletonInverted = np.logical_not(self.skeletonizedWormImage) skeletonPts, cost = \ graph.route_through_array(np.uint8(skeletonInverted), np.flipud(skeletonEndPts[0][0]), np.flipud(skeletonEndPts[1][0]), geometric=True) self.skeleton = np.array([[pt[0], pt[1]] for pt in skeletonPts]) self.badSkeletonization = False
def createPath(CostSurfacefn,costSurfaceArray,startCoord,stopCoord,pathValue=255): print np.min(costSurfaceArray) print costSurfaceArray.shape print startCoord print stopCoord # coordinates to array index startCoordX = startCoord[0] startCoordY = startCoord[1] startIndexX,startIndexY = coord2pixelOffset(CostSurfacefn,startCoordX,startCoordY) stopCoordX = stopCoord[0] stopCoordY = stopCoord[1] stopIndexX,stopIndexY = coord2pixelOffset(CostSurfacefn,stopCoordX,stopCoordY) print startIndexX,startIndexY,stopIndexX,stopIndexY # create path indices, weight = route_through_array(costSurfaceArray, (startIndexY,startIndexX), (stopIndexY,stopIndexX),geometric=True,fully_connected=True) indices = np.array(indices).T path = np.zeros_like(costSurfaceArray) path[indices[0], indices[1]] = pathValue return path, weight
def _walk_through_graph(graph, heatmap, start_end_vertices, method='shortest-path', verbose=True): """Find path through the path with different a-priori. Parameters ---------- graph : ndarray, shape (n_serie * nb_bins, n_serie * nb_bins) Fully connected graph computed from the heatmap. heatmap : ndarray, shape (n_serie, nb_bins) The heatmap from which the graph will be built. start_end_vertices : tuple, shape ((start_vertice, end_vertice)) Tuple containing the starting and ending vertices to enter and exit the graph. method : str, optional (default='shortest-path') Method to use to walk through the graph. The following possibilities: - 'shortest-path' : find shortest-path using scipy implementation. - 'route-through-graph' : find the path using MCP algorithm from skimage. verbose : bool Show the processing stage. Returns ------- path : ndarray, shape (n_serie, 2) The path found to walk through the graph. """ # Split the starting and ending vertices start_tuple = start_end_vertices[0] end_tuple = start_end_vertices[1] if method == 'shortest-path': # Define a function to go from px to vertices def px2v(px, im_shape): return np.ravel_multi_index(px, im_shape) # Define a function to go from vertices to px def v2px(v, im_shape): return np.unravel_index(v, im_shape) # Compute the shortest path for the whole graph d, p = shortest_path(graph, return_predecessors=True) # Initialize the path path_list = [end_tuple] # Find the shortest path thorugh an iterative process while end_tuple != start_tuple: # Convert coord from px to v s_v = px2v(start_tuple, heatmap.shape) e_v = px2v(end_tuple, heatmap.shape) # Find the predecessor pred_v = p[s_v, e_v] # Convert into pixel pred_px = v2px(pred_v, heatmap.shape) path_list.append(pred_px) # Update the last point of the path end_tuple = pred_px elif method == 'route-through-graph': # Call the function from skimage indices, weight = route_through_array(heatmap, start_tuple, end_tuple, geometric=True) path_list = np.array(indices) else: raise NotImplementedError # Convert the list into array path_list = np.array(path_list) # Clean the path serie by serie to have a unique value cleaned_path = [] for t_serie in range(heatmap.shape[0]): # Find all the intensities corresponding to this serie poi = path_list[np.nonzero(path_list[:, 0] == t_serie)] # Compute the median of the second column med_path = np.median(poi[:, 1]) cleaned_path.append([t_serie, med_path]) if verbose: print 'Walked through the graph ...' # Convert list to array return np.round(np.array(cleaned_path))
def compute_downstream_lines(gdir): """Compute the lines continuing the glacier (one per divide). The idea is simple: starting from the glacier tail, compute all the routes to all local minimas found at the domain edge. The cheapest is the One. Parameters ---------- gdir: GlacierDir object I/O --- Generates:: - downstream_line.p: line instance as pickle """ log.info('%s: downstream lines', gdir.rgi_id) with netCDF4.Dataset(gdir.get_filepath('grids', div_id=0)) as nc: topo = nc.variables['topo_smoothed'][:] # Variables we gonna need xmesh, ymesh = np.meshgrid(np.arange(0, gdir.grid.nx, 1), np.arange(0, gdir.grid.ny, 1)) _h = [topo[:, 0], topo[0, :], topo[:, -1], topo[-1, :]] _x = [xmesh[:, 0], xmesh[0, :], xmesh[:, -1], xmesh[-1, :]] _y = [ymesh[:, 0], ymesh[0, :], ymesh[:, -1], ymesh[-1, :]] # First we have to find the "major" divide heads = [] heigts = [] div_ids = list(gdir.divide_ids) for div_id in div_ids: head = gdir.read_pickle('centerlines', div_id=div_id)[-1].tail heigts.append(topo[head.y, head.x]) heads.append((head.y, head.x)) # Now the lowest first aso = np.argsort(heigts) major_id = aso[0] a = aso[0] head = heads[a] head_h = heigts[a] # Find all local minimas and their coordinates min_cost = np.Inf line = None min_thresold = 100 while True: for h, x, y in zip(_h, _x, _y): ids = scipy.signal.argrelmin(h, order=10, mode='wrap') for i in ids[0]: if topo[y[i], x[i]] > (head_h-min_thresold): continue lids, cost = route_through_array(topo, head, (y[i], x[i])) if cost < min_cost: min_cost = cost line = shpg.LineString(np.array(lids)[:, [1, 0]]) if line is None: min_thresold -= 10 else: break if min_thresold < 0: raise RuntimeError('Downstream line not found') # Write the data gdir.write_pickle(line, 'downstream_line', div_id=div_ids[a]) if len(div_ids) == 1: return # If we have divides, there is just one route and we have to interrupt # It at the glacier boundary with netCDF4.Dataset(gdir.get_filepath('grids', div_id=div_ids[a])) as nc: mask = nc.variables['glacier_mask'][:] ext = nc.variables['glacier_ext'][:] mask[np.where(ext==1)] = 0 tail = head topo[np.where(mask==1)] = 0 for a in aso[1:]: head = heads[a] lids, _ = route_through_array(topo, head, tail) lids = [l for l in lids if mask[l[0], l[1]] == 0][0:-1] line = shpg.LineString(np.array(lids)[:, [1, 0]]) gdir.write_pickle(line, 'downstream_line', div_id=div_ids[a]) fn = gdir.get_filepath('major_divide', div_id=0) with open(fn, 'w') as text_file: text_file.write('{}'.format(div_ids[major_id]))
def compute_downstream_lines(gdir): """Compute the lines continuing the glacier (one per divide). The idea is simple: starting from the glacier tail, compute all the routes to all local minimas found at the domain edge. The cheapest is "the One". The task also determines the so-called "major flowline" which is the only line flowing out of the domain. The other ones are flowing into the branch. The rest of the job (merging all centerlines + downstreams into one single glacier is realized by :py:func:`~oggm.tasks.init_present_time_glacier`). Parameters ---------- gdir : oggm.GlacierDirectory """ with netCDF4.Dataset(gdir.get_filepath('gridded_data', div_id=0)) as nc: topo = nc.variables['topo_smoothed'][:] # Make going up very costy topo = topo**10 # Variables we gonna need xmesh, ymesh = np.meshgrid(np.arange(0, gdir.grid.nx, 1, dtype=np.int64), np.arange(0, gdir.grid.ny, 1, dtype=np.int64)) _h = [topo[:, 0], topo[0, :], topo[:, -1], topo[-1, :]] _x = [xmesh[:, 0], xmesh[0, :], xmesh[:, -1], xmesh[-1, :]] _y = [ymesh[:, 0], ymesh[0, :], ymesh[:, -1], ymesh[-1, :]] # First we have to find the "major" divide heads = [] heigts = [] div_ids = list(gdir.divide_ids) for div_id in div_ids: head = gdir.read_pickle('centerlines', div_id=div_id)[-1].tail heigts.append(topo[int(head.y), int(head.x)]) heads.append((int(head.y), int(head.x))) # Now the lowest first aso = np.argsort(heigts) major_id = aso[0] a = aso[0] head = heads[a] head_h = heigts[a] # Find all local minimas and their coordinates min_cost = np.Inf line = None min_thresold = 50 while True: for h, x, y in zip(_h, _x, _y): ids = scipy.signal.argrelmin(h, order=10, mode='wrap') for i in ids[0]: # Prevent very high local minimas # if topo[y[i], x[i]] > ((head_h-min_thresold)**10): # continue lids, cost = route_through_array(topo, head, (y[i], x[i]), geometric=True) if cost < min_cost: min_cost = cost line = shpg.LineString(np.array(lids)[:, [1, 0]]) if line is None: min_thresold -= 10 else: break if min_thresold < 0: raise RuntimeError('Downstream line not found') # Write the data mdiv = div_ids[major_id] gdir.write_pickle(mdiv, 'major_divide', div_id=0) fn = gdir.get_filepath('major_divide') gdir.write_pickle(line, 'downstream_line', div_id=div_ids[a]) if len(div_ids) == 1: return # If we have divides, there is just one route and we have to interrupt # It at the glacier boundary fpath = gdir.get_filepath('gridded_data', div_id=div_ids[a]) with netCDF4.Dataset(fpath) as nc: mask = nc.variables['glacier_mask'][:] ext = nc.variables['glacier_ext'][:] mask[np.where(ext==1)] = 0 tail = head topo[np.where(mask==1)] = 0 for a in aso[1:]: head = heads[a] lids, _ = route_through_array(topo, head, tail) lids = [l for l in lids if mask[l[0], l[1]] == 0][0:-1] line = shpg.LineString(np.array(lids)[:, [1, 0]]) gdir.write_pickle(line, 'downstream_line', div_id=div_ids[a])
def compute_centerlines(gdir, div_id=None): """Compute the centerlines following Kienholz et al., (2014). They are then sorted according to the modified Strahler number: http://en.wikipedia.org/wiki/Strahler_number Parameters ---------- gdir : oggm.GlacierDirectory """ # open geom = gdir.read_pickle('geometries', div_id=div_id) grids_file = gdir.get_filepath('gridded_data', div_id=div_id) with netCDF4.Dataset(grids_file) as nc: # Variables glacier_mask = nc.variables['glacier_mask'][:] glacier_ext = nc.variables['glacier_ext'][:] topo = nc.variables['topo_smoothed'][:] poly_pix = geom['polygon_pix'] # Find for local maximas on the outline x, y = tuple2int(poly_pix.exterior.xy) ext_yx = tuple(reversed(poly_pix.exterior.xy)) zoutline = topo[y[:-1], x[:-1]] # last point is first point # Size of the half window to use to look for local maximas maxorder = np.rint(cfg.PARAMS['localmax_window'] / gdir.grid.dx) maxorder = np.clip(maxorder, 5., np.rint((len(zoutline) / 5.))) heads_idx = scipy.signal.argrelmax(zoutline, mode='wrap', order=maxorder.astype(np.int64)) if (not cfg.PARAMS['use_multiple_flowlines']) or len(heads_idx[0]) <= 1: # small glaciers with one or less heads: take the absolute max heads_idx = (np.atleast_1d(np.argmax(zoutline)),) # Remove the heads that are too low zglacier = topo[np.where(glacier_mask)] head_threshold = np.percentile(zglacier, (1./3.)*100) heads_idx = heads_idx[0][np.where(zoutline[heads_idx] > head_threshold)] heads = np.asarray(ext_yx)[:, heads_idx] heads_z = zoutline[heads_idx] # careful, the coords are in y, x order! heads = [shpg.Point(x, y) for y, x in zip(heads[0, :], heads[1, :])] # get radius of the buffer according to Kienholz eq. (1) radius = cfg.PARAMS['q1'] * geom['polygon_area'] + cfg.PARAMS['q2'] radius = np.clip(radius, 0, cfg.PARAMS['rmax']) radius /= gdir.grid.dx # in raster coordinates # Plus our criteria, quite usefull to remove short lines: radius += cfg.PARAMS['flowline_junction_pix'] * cfg.PARAMS['flowline_dx'] log.debug('%s: radius in raster coordinates: %.2f', gdir.rgi_id, radius) # OK. Filter and see. log.debug('%s: number of heads before radius filter: %d', gdir.rgi_id, len(heads)) heads, heads_z = _filter_heads(heads, heads_z, radius, poly_pix) log.debug('%s: number of heads after radius filter: %d', gdir.rgi_id, len(heads)) # Cost array costgrid = _make_costgrid(glacier_mask, glacier_ext, topo) # Terminus t_coord = np.asarray(ext_yx)[:, np.argmin(zoutline)].astype(np.int64) # Compute the routes lines = [] for h in heads: h_coord = np.asarray(h.xy)[::-1].astype(np.int64) indices, _ = route_through_array(costgrid, h_coord, t_coord) lines.append(shpg.LineString(np.array(indices)[:, [1, 0]])) log.debug('%s: computed the routes', gdir.rgi_id) # Filter the shortest lines out radius = cfg.PARAMS['flowline_junction_pix'] * cfg.PARAMS['flowline_dx'] radius += 6 * cfg.PARAMS['flowline_dx'] olines, _ = _filter_lines(lines, heads, cfg.PARAMS['kbuffer'], radius) log.debug('%s: number of heads after lines filter: %d', gdir.rgi_id, len(olines)) # And rejoin the cutted tails olines = _join_lines(olines) # Adds the line level for cl in olines: cl.order = _line_order(cl) # And sort them per order !!! several downstream tasks rely on this cls = [] for i in np.argsort([cl.order for cl in olines]): cls.append(olines[i]) # Final check if len(cls) == 0: raise RuntimeError('{} : no centerline found!'.format(gdir.rgi_id)) # Write the data gdir.write_pickle(cls, 'centerlines', div_id=div_id) # Netcdf with netCDF4.Dataset(grids_file, 'a') as nc: v = nc.createVariable('cost_grid', 'f4', ('y', 'x', ), zlib=True) v.units = '-' v.long_name = 'Centerlines cost grid' v[:] = costgrid
def catchment_area(gdir, div_id=None): """Compute the catchment areas of each tributary line. The idea is to compute the route of lowest cost for any point on the glacier to rejoin a centerline. These routes are then put together if they belong to the same centerline, thus creating "catchment areas" for each centerline. Parameters ---------- gdir : oggm.GlacierDirectory """ # Variables cls = gdir.read_pickle('centerlines', div_id=div_id) geoms = gdir.read_pickle('geometries', div_id=div_id) glacier_pix = geoms['polygon_pix'] fpath = gdir.get_filepath('gridded_data', div_id=div_id) with netCDF4.Dataset(fpath) as nc: costgrid = nc.variables['cost_grid'][:] mask = nc.variables['glacier_mask'][:] # If we have only one centerline this is going to be easy: take the # mask and return if len(cls) == 1: cl_catchments = [np.array(np.nonzero(mask == 1)).T] gdir.write_pickle(cl_catchments, 'catchment_indices', div_id=div_id) return # Initialise costgrid and the "catching" dict cost_factor = 0. # Make it cheap dic_catch = dict() for i, cl in enumerate(cls): x, y = tuple2int(cl.line.xy) costgrid[y, x] = cost_factor for x, y in [(int(x), int(y)) for x, y in cl.line.coords]: assert (y, x) not in dic_catch dic_catch[(y, x)] = set([(y, x)]) # It is much faster to make the array as small as possible (especially # with divides). We have to trick: pm = np.nonzero(mask == 1) ymi, yma = np.min(pm[0])-1, np.max(pm[0])+2 xmi, xma = np.min(pm[1])-1, np.max(pm[1])+2 costgrid = costgrid[ymi:yma, xmi:xma] mask = mask[ymi:yma, xmi:xma] # Where did we compute the path already? computed = np.where(mask == 1, 0, np.nan) # Coords of Terminus (converted) endcoords = np.array(cls[0].tail.coords[0])[::-1].astype(np.int64) endcoords -= [ymi, xmi] # Start with all the paths at the boundaries, they are more likely # to cover much of the glacier for headx, heady in tuple2int(glacier_pix.exterior.coords): headcoords = np.array([heady-ymi, headx-xmi]) # convert indices, _ = route_through_array(costgrid, headcoords, endcoords) inds = np.array(indices).T computed[inds[0], inds[1]] = 1 set_dump = set([]) for y, x in indices: y, x = y+ymi, x+xmi # back to original set_dump.add((y, x)) if (y, x) in dic_catch: dic_catch[(y, x)] = dic_catch[(y, x)].union(set_dump) break # Repeat for the not yet computed pixels while True: not_computed = np.where(computed == 0) if len(not_computed[0]) == 0: # All points computed !! break headcoords = np.array([not_computed[0][0], not_computed[1][0]]).astype(np.int64) indices, _ = route_through_array(costgrid, headcoords, endcoords) inds = np.array(indices).T computed[inds[0], inds[1]] = 1 set_dump = set([]) for y, x in indices: y, x = y+ymi, x+xmi # back to original set_dump.add((y, x)) if (y, x) in dic_catch: dic_catch[(y, x)] = dic_catch[(y, x)].union(set_dump) break # For each centerline, make a set of all points flowing into it cl_catchments = [] for cl in cls: # Union of all cl_catch = set() for x, y in [(int(x), int(y)) for x, y in cl.line.coords]: cl_catch = cl_catch.union(dic_catch[(y, x)]) cl_catchments.append(cl_catch) # The higher order centerlines will get the points from the upstream # ones too. The idea is to store the points which are unique to each # centerline: now, in decreasing line order we remove the indices from # the tributaries cl_catchments = cl_catchments[::-1] for i, cl in enumerate(cl_catchments): cl_catchments[i] = np.array(list(cl.difference(*cl_catchments[i+1:]))) cl_catchments = cl_catchments[::-1] # put it back in order # Write the data gdir.write_pickle(cl_catchments, 'catchment_indices', div_id=div_id)
def compute_centerlines(gdir, div_id=None): """Compute the centerlines from a pre-processed directory. It follows quite closely Kienholz et al. (2014). Modified Strahler number http://en.wikipedia.org/wiki/Strahler_number Parameters ---------- gdir: GlacierDir object div_id: the divide ID to process (should be left to None) I/O --- Generates:: - centerlines.p: list of centerline instances as pickle Updates:: - grids.nc with the costgrid """ if div_id is None: for i in gdir.divide_ids: log.info('%s: centerlines, divide %d', gdir.rgi_id, i) compute_centerlines(gdir, div_id=i) return # open geom = gdir.read_pickle('geometries', div_id=div_id) grids_file = gdir.get_filepath('grids', div_id=div_id) nc = netCDF4.Dataset(grids_file) # Variables glacier_mask = nc.variables['glacier_mask'][:] glacier_ext = nc.variables['glacier_ext'][:] topo = nc.variables['topo_smoothed'][:] poly_pix = geom['polygon_pix'] nc.close() # Find for local maximas on the outline ext_yx = tuple(reversed(poly_pix.exterior.xy)) ext_yx = (ext_yx[0][:-1], ext_yx[1][:-1]) # last point is first point zoutline = topo[ext_yx] # Size of the half window to use to look for local maximas maxorder = np.rint(cfg.params['localmax_window'] / gdir.grid.dx) maxorder = np.clip(maxorder, 5., np.rint((len(zoutline) / 5.))) heads_idx = scipy.signal.argrelmax(zoutline, mode='wrap', order=maxorder.astype(np.int64)) if len(heads_idx[0]) <= 1: # small glaciers with one or less heads: take the absolute max heads_idx = (np.atleast_1d(np.argmax(zoutline)),) # Remove the heads that are too low zglacier = topo[np.where(glacier_mask)] head_threshold = np.percentile(zglacier, (1./3.)*100) heads_idx = heads_idx[0][np.where(zoutline[heads_idx] > head_threshold)] heads = np.asarray(ext_yx)[:, heads_idx] heads_z = zoutline[heads_idx] # careful, the coords are in y, x order! heads = [shpg.Point(x, y) for y, x in zip(heads[0, :], heads[1, :])] # get radius of the buffer according to Kienholz eq. (1) radius = cfg.params['q1'] * geom['polygon_area'] + cfg.params['q2'] radius = np.clip(radius, 0, cfg.params['rmax']) radius /= gdir.grid.dx # in raster coordinates # Plus our criteria, quite usefull to remove short lines: radius += cfg.params['flowline_junction_pix'] * cfg.params['flowline_dx'] log.debug('%s: radius in raster coordinates: %.2f', gdir.rgi_id, radius) # OK. Filter and see. log.debug('%s: number of heads before radius filter: %d', gdir.rgi_id, len(heads)) heads, heads_z = _filter_heads(heads, heads_z, radius, poly_pix) log.debug('%s: number of heads after radius filter: %d', gdir.rgi_id, len(heads)) # Cost array costgrid = _make_costgrid(glacier_mask, glacier_ext, topo) # Terminus t_coord = np.asarray(ext_yx)[:, np.argmin(zoutline)] # Compute the routes lines = [] for h in heads: h_coord = np.asarray(h.xy)[::-1] indices, _ = route_through_array(costgrid, h_coord, t_coord) lines.append(shpg.LineString(np.array(indices)[:, [1, 0]])) log.debug('%s: computed the routes', gdir.rgi_id) # Filter the shortest lines out radius = cfg.params['flowline_junction_pix'] * cfg.params['flowline_dx'] radius += 6 * cfg.params['flowline_dx'] olines, _ = _filter_lines(lines, heads, cfg.params['kbuffer'], radius) log.debug('%s: number of heads after lines filter: %d', gdir.rgi_id, len(olines)) # And rejoin the cutted tails olines = _join_lines(olines) # Adds the line level for cl in olines: cl.order = _line_order(cl) # And sort them per order !!! several tools downstream rely on this cls = [] for i in np.argsort([cl.order for cl in olines]): cls.append(olines[i]) # Final check if len(cls) == 0: raise RuntimeError('No centerline found!') # Write the data gdir.write_pickle(cls, 'centerlines', div_id=div_id) # Netcdf nc = netCDF4.Dataset(grids_file, 'a') v = nc.createVariable('cost_grid', 'f4', ('y', 'x', ), zlib=True) v.units = '-' v.long_name = 'Centerlines cost grid' v[:] = costgrid nc.close()
def catchment_area(gdir, div_id=None): """Compute the catchment areas of each tributary line. The idea is to compute the route of lowest cost for any point on the glacier to rejoin a centerline. These routes are then put together if they belong to the same centerline, thus creating "catchment areas" for each centerline. Parameters ---------- gdir: GlacierDir object div_id: the divide ID to process (should be left to None) I/O --- catchment_indices.p: a list of """ if div_id is None: for i in gdir.divide_ids: log.info('%s: catchment areas, divide %d', gdir.rgi_id, i) catchment_area(gdir, div_id=i) return # Variables cls = gdir.read_pickle('centerlines', div_id=div_id) geoms = gdir.read_pickle('geometries', div_id=div_id) glacier_pix = geoms['polygon_pix'] nc = netCDF4.Dataset(gdir.get_filepath('grids', div_id=div_id)) costgrid = nc.variables['cost_grid'][:] mask = nc.variables['glacier_mask'][:] nc.close() # If we have only one centerline this is going to be easy: take the # mask and return if len(cls) == 1: cl_catchments = [np.array(np.where(mask == 1)).T] gdir.write_pickle(cl_catchments, 'catchment_indices', div_id=div_id) return # Initialise costgrid and the "catching" dict cost_factor = 0. # Make it cheap dic_catch = dict() for i, cl in enumerate(cls): x, y = cl.line.xy costgrid[y, x] *= cost_factor for x, y in [(int(x), int(y)) for x, y in cl.line.coords]: assert (y, x) not in dic_catch dic_catch[(y, x)] = set([(y, x)]) # Where did we compute the path already? computed = np.where(mask == 1, 0, np.nan) # Coords of Terminus endcoords = np.array(cls[0].tail.coords[0])[::-1] # Start with all the paths at the boundaries, they are more likely # to cover much of the glacier for headx, heady in glacier_pix.exterior.coords: indices, _ = route_through_array(costgrid, np.array([heady, headx]), endcoords) inds = np.array(indices).T computed[inds[0], inds[1]] = 1 set_dump = set([]) for y, x in indices: set_dump.add((y, x)) if (y, x) in dic_catch: dic_catch[(y, x)] = dic_catch[(y, x)].union(set_dump) break # Repeat for the not yet computed pixels while True: not_computed = np.where(computed == 0) if len(not_computed[0]) == 0: # All points computed !! break headcoords = np.array([not_computed[0][0], not_computed[1][0]]) indices, _ = route_through_array(costgrid, headcoords, endcoords) inds = np.array(indices).T computed[inds[0], inds[1]] = 1 set_dump = set([]) for y, x in indices: set_dump.add((y, x)) if (y, x) in dic_catch: dic_catch[(y, x)] = dic_catch[(y, x)].union(set_dump) break # For each centerline, make a set of all points flowing into it cl_catchments = [] for cl in cls: # Union of all cl_catch = set() for x, y in [(int(x), int(y)) for x, y in cl.line.coords]: cl_catch = cl_catch.union(dic_catch[(y, x)]) cl_catchments.append(cl_catch) # The higher order centerlines will get the points from the upstream # ones too. The idea is to store the points which are unique to each # centerline: now, in decreasing line order we remove the indices from # the tributaries cl_catchments = cl_catchments[::-1] for i, cl in enumerate(cl_catchments): cl_catchments[i] = np.array(list(cl.difference(*cl_catchments[i+1:]))) cl_catchments = cl_catchments[::-1] # put it back in order # Write the data gdir.write_pickle(cl_catchments, 'catchment_indices', div_id=div_id)