def get_input(): ''' Method called for POST `/getInput` ''' bottle.response.content_type = 'application/json' data = bottle.request.json data["remote_address"] = bottle.request.client_ip SESSION_HANDLER.get_session(bottle.request.session.id).add_entry( data) # record this interaction # Inputs extent = data["extent"] dataset = data["dataset"] if dataset not in DATASETS: raise ValueError( "Dataset doesn't seem to be valid, please check Datasets.py") naip_data, naip_crs, naip_transform, naip_bounds, naip_index = DATASETS[ dataset]["data_loader"].get_data_from_extent(extent) naip_data = np.rollaxis(naip_data, 0, 3) naip_data, new_bounds = warp_data_to_3857(naip_data, naip_crs, naip_transform, naip_bounds) naip_data = crop_data_by_extent(naip_data, new_bounds, extent) naip_img = naip_data[:, :, :3].copy().astype( np.uint8) # keep the RGB channels to save as a color image naip_img = cv2.imencode(".png", cv2.cvtColor(naip_img, cv2.COLOR_RGB2BGR))[1].tostring() naip_img = base64.b64encode(naip_img).decode("utf-8") data["input_naip"] = naip_img bottle.response.status = 200 return json.dumps(data)
def pred_tile(): ''' Method called for POST `/predTile`''' bottle.response.content_type = 'application/json' data = bottle.request.json data["remote_address"] = bottle.request.client_ip SESSION_HANDLER.get_session(bottle.request.session.id).add_entry( data) # record this interaction # Inputs geom = data["polygon"] class_list = data["classes"] name_list = [item["name"] for item in class_list] color_list = [item["color"] for item in class_list] dataset = data["dataset"] zone_layer_name = data["zoneLayerName"] if dataset not in DATASETS: raise ValueError( "Dataset doesn't seem to be valid, do the datasets in js/tile_layers.js correspond to those in TileLayers.py" ) try: naip_data, raster_profile, raster_transform, raster_bounds, raster_crs = DATASETS[ dataset]["data_loader"].get_data_from_shape(geom["geometry"]) naip_data = np.rollaxis(naip_data, 0, 3) shape_area = get_area_from_geometry(geom["geometry"]) except NotImplementedError as e: bottle.response.status = 400 return json.dumps({ "error": "Cannot currently download imagery with 'Basemap' based datasets" }) output = SESSION_HANDLER.get_session(bottle.request.session.id).model.run( naip_data, geom, True) output_hard = output.argmax(axis=2) print("Finished, output dimensions:", output.shape) # apply nodata mask from naip_data nodata_mask = np.sum(naip_data == 0, axis=2) == naip_data.shape[2] output_hard[nodata_mask] = 255 vals, counts = np.unique(output_hard[~nodata_mask], return_counts=True) # ------------------------------------------------------ # Step 4 # Convert images to base64 and return # ------------------------------------------------------ tmp_id = get_random_string(8) img_hard = np.round( class_prediction_to_img(output, True, color_list) * 255, 0).astype(np.uint8) img_hard = cv2.cvtColor(img_hard, cv2.COLOR_RGB2BGRA) img_hard[nodata_mask] = [0, 0, 0, 0] img_hard, img_hard_bounds = warp_data_to_3857(img_hard, raster_crs, raster_transform, raster_bounds, resolution=10) cv2.imwrite(os.path.join(ROOT_DIR, "downloads/%s.png" % (tmp_id)), img_hard) data["downloadPNG"] = "downloads/%s.png" % (tmp_id) new_profile = raster_profile.copy() new_profile['driver'] = 'GTiff' new_profile['dtype'] = 'uint8' new_profile['compress'] = "lzw" new_profile['count'] = 1 new_profile['transform'] = raster_transform new_profile['height'] = naip_data.shape[0] new_profile['width'] = naip_data.shape[1] new_profile['nodata'] = 255 f = rasterio.open(os.path.join(ROOT_DIR, "downloads/%s.tif" % (tmp_id)), 'w', **new_profile) f.write(output_hard.astype(np.uint8), 1) f.close() data["downloadTIFF"] = "downloads/%s.tif" % (tmp_id) f = open(os.path.join(ROOT_DIR, "downloads/%s.txt" % (tmp_id)), "w") f.write("Class id\tClass name\tPercent area\tArea (km^2)\n") for i in range(len(vals)): pct_area = (counts[i] / np.sum(counts)) if shape_area is not None: real_area = shape_area * pct_area else: real_area = -1 f.write("%d\t%s\t%0.4f%%\t%0.4f\n" % (vals[i], name_list[vals[i]], pct_area * 100, real_area)) f.close() data["downloadStatistics"] = "downloads/%s.txt" % (tmp_id) bottle.response.status = 200 return json.dumps(data)
def pred_patch(): ''' Method called for POST `/predPatch`''' bottle.response.content_type = 'application/json' data = bottle.request.json data["remote_address"] = bottle.request.client_ip SESSION_HANDLER.get_session(bottle.request.session.id).add_entry( data) # record this interaction # Inputs extent = data["extent"] dataset = data["dataset"] class_list = data["classes"] name_list = [item["name"] for item in class_list] color_list = [item["color"] for item in class_list] # ------------------------------------------------------ # Step 1 # Transform the input extent into a shapely geometry # Find the tile assosciated with the geometry # ------------------------------------------------------ # ------------------------------------------------------ # Step 2 # Load the input data sources for the given tile # ------------------------------------------------------ if dataset not in DATASETS: raise ValueError( "Dataset doesn't seem to be valid, do the datasets in js/tile_layers.js correspond to those in TileLayers.py" ) naip_data, naip_crs, naip_transform, naip_bounds, naip_index = DATASETS[ dataset]["data_loader"].get_data_from_extent(extent) naip_data = np.rollaxis( naip_data, 0, 3 ) # we do this here instead of get_data_by_extent because not all GeoDataTypes will have a channel dimension SESSION_HANDLER.get_session( bottle.request.session.id).current_transform = (naip_crs, naip_transform, naip_index) # ------------------------------------------------------ # Step 3 # Run a model on the input data # Apply reweighting # Fix padding # ------------------------------------------------------ output = SESSION_HANDLER.get_session(bottle.request.session.id).model.run( naip_data, extent, False) assert len( output.shape ) == 3, "The model function should return an image shaped as (height, width, num_classes)" assert ( output.shape[2] < output.shape[0] and output.shape[2] < output.shape[1] ), "The model function should return an image shaped as (height, width, num_classes)" # assume that num channels is less than img dimensions # ------------------------------------------------------ # Step 4 # Warp output to EPSG:3857 and crop off the padded area # ------------------------------------------------------ output, output_bounds = warp_data_to_3857(output, naip_crs, naip_transform, naip_bounds) output = crop_data_by_extent(output, output_bounds, extent) # ------------------------------------------------------ # Step 5 # Convert images to base64 and return # ------------------------------------------------------ img_soft = np.round( class_prediction_to_img(output, False, color_list) * 255, 0).astype(np.uint8) img_soft = cv2.imencode(".png", cv2.cvtColor(img_soft, cv2.COLOR_RGB2BGR))[1].tostring() img_soft = base64.b64encode(img_soft).decode("utf-8") data["output_soft"] = img_soft img_hard = np.round( class_prediction_to_img(output, True, color_list) * 255, 0).astype(np.uint8) img_hard = cv2.imencode(".png", cv2.cvtColor(img_hard, cv2.COLOR_RGB2BGR))[1].tostring() img_hard = base64.b64encode(img_hard).decode("utf-8") data["output_hard"] = img_hard bottle.response.status = 200 return json.dumps(data)