def visualise_results(results, tile_size, out_path): """Given the predictions, false positves and the labels of our model visualise them on the satellite image they belong to.""" # The tiles of the predictions, false positives and labels are all in "results". # We need ways to get extract them individually to pass them to overlay_bitmap. get_predictions = lambda (tiles, pos, path): (tiles[0], pos, path) get_labels = lambda (tiles, pos, path): (tiles[1], pos, path) get_false_positives = lambda (tiles, pos, path): (tiles[2], pos, path) get_path = lambda (tiles,pos , path): path sorted_by_path = sorted(results, key=get_path) for path, result_tiles in itertools.groupby(sorted_by_path, get_path): raster_dataset = rasterio.open(path) dataset_shape = (raster_dataset.shape[0], raster_dataset.shape[1]) result_tiles = list(result_tiles) predictions = map(get_predictions, result_tiles) labels = map(get_labels, result_tiles) false_positives = map(get_false_positives, result_tiles) satellite_img_name = get_file_name(path) out_file_name = "{}_results.tif".format(satellite_img_name) out = os.path.join(out_path, out_file_name) # We first write the labels in blue, then predictions in green and then false positives in red. # This way the true positives will be green, false positives red, false negatives blue and everything # else in the image will be true negatives. for tiles, color in [(labels, 'blue'), (predictions, 'green'), (false_positives, 'red')]: bitmap = image_from_tiles(tiles, tile_size, dataset_shape) raster_dataset = overlay_bitmap(bitmap, raster_dataset, out, color=color)
def reproject_dataset(geotiff_path): """Project a GeoTIFF to the WGS84 coordinate reference system. See https://mapbox.github.io/rasterio/topics/reproject.html""" # We want to project the GeoTIFF coordinate reference system (crs) # to WGS84 (e.g. into the familiar Lat/Lon pairs). WGS84 is analogous # to EPSG:4326 dst_crs = 'EPSG:4326' with rasterio.open(geotiff_path) as src: transform, width, height = rasterio.warp.calculate_default_transform( src.crs, dst_crs, src.width, src.height, *src.bounds) kwargs = src.meta.copy() kwargs.update({ 'crs': dst_crs, 'transform': transform, 'width': width, 'height': height }) satellite_img_name = get_file_name(geotiff_path) out_file_name = "{}_wgs84.tif".format(satellite_img_name) out_path = os.path.join(WGS84_DIR, out_file_name) with rasterio.open(out_path, 'w', **kwargs) as dst: for i in range(1, src.count + 1): rasterio.warp.reproject( source=rasterio.band(src, i), destination=rasterio.band(dst, i), src_transform=src.transform, src_crs=src.crs, dst_transform=transform, dst_crs=dst_crs, resampling=rasterio.warp.Resampling.nearest) return rasterio.open(out_path), out_path
def create_patched_features_and_labels(geotiff_path, geotiff_bitmap_path, patch_size, dist, only_cache=False): """ Create the features and labels for a given satellite image and its shapefiles. """ # Try to load patch from cache. satellite_img_name = get_file_name(geotiff_path) cache_file_name = "{}_{}.pickle".format(satellite_img_name, patch_size) cache_path = os.path.join(PATCHES_DIR, cache_file_name) try: print('Load patches from {}.'.format(cache_path)) with open(cache_path, 'rb') as f: patches = pickle.load(f) return patches["features"], patches["labels"] except IOError as e: if only_cache: raise print("Cache not available. Compute patches.") # The provided satellite images have a different coordinate reference system as # the familiar WGS84 which uses Latitude and Longitude. SO we need to reproject # the satellite image to the WGS84 coordinate reference system #print('geotiff_path: ',geotiff_path) dataset, wgs84_path = reproject_dataset(geotiff_path) bands = np.dstack(dataset.read()) #print('bands,dataset,wgs84_path : ',bands,dataset,wgs84_path) # For the given satellite image, create a bitmap which has 1 at every pixel corresponding # to building in the satellite image. In order to do this we use building polygons from OSM. #The building polygons are stored in forms of shapefiles and are given by "shapefiles_paths". building_bitmap = create_bitmap(geotiff_path, geotiff_bitmap_path, dataset) #patched_bands = create_patches(bands, patch_size, wgs84_path) #patched_bitmap = create_patches(building_bitmap, patch_size, wgs84_path) #patched_bitmap = create_patches(building_bitmap, patch_size, geotiff_bitmap_path) patched_bands, patched_bitmap = create_patches(bands, building_bitmap, patch_size, wgs84_path, geotiff_bitmap_path, dist) # Due to the projection, the satellite image in the GeoTIFF is not a perfect rectangle and the # remaining space on the edges is blacked out. When we overlay the GeoTIFF with the # shapefile it also overlays features for the blacked out parts which means that if we don't # remove these patches the classifier will be fed with non-empty labels for empty features. #patched_bands, patched_bitmap = remove_edge_patches(patched_bands, # patched_bitmap, patch_size, dataset.shape) save_patches(cache_path, patched_bands, patched_bitmap) return patched_bands, patched_bitmap
def create_bitmap(raster_dataset, shapefile_paths, satellite_path): """Create the bitmap for a given satellite image.""" satellite_img_name = get_file_name(satellite_path) cache_file_name = "{}_water.tif".format(satellite_img_name) cache_path = os.path.join(WATER_BITMAPS_DIR, cache_file_name) try: # Try loading the water bitmap from cache. print("Load water bitmap from {}".format(cache_path)) bitmap = load_bitmap(cache_path) bitmap[bitmap == 255] = 1 return bitmap except IOError as e: print("No cache file found.") water_features = np.empty((0, )) print("Create bitmap for water features.") for shapefile_path in shapefile_paths: try: print("Load shapefile {}.".format(shapefile_path)) with fiona.open(shapefile_path) as shapefile: # Each feature in the shapefile also contains meta information such as # wether the features is a lake or a river. We only care about the geometry # of the feature i.e. where it is located and what shape it has. geometries = [feature['geometry'] for feature in shapefile] water_features = np.concatenate( (water_features, geometries), axis=0) except IOError as e: print("No shapefile found.") sys.exit(1) # Now that we have the vector data of all water features in our satellite image # we "burn it" into a new raster so that we get a B/W image with water features # in white and the rest in black. We choose the value 255 so that there is a stark # contrast between water and non-water pixels. This is only for visualisation # purposes. For the classifier we use 0s and 1s. bitmap_image = rasterio.features.rasterize( ((g, 255) for g in water_features), out_shape=raster_dataset.shape, transform=raster_dataset.transform) save_bitmap(cache_path, bitmap_image, raster_dataset) bitmap_image[bitmap_image == 255] = 1 return bitmap_image
def create_tiled_features_and_labels(geotiff_path, shapefile_paths, tile_size, only_cache=False): """Create the features and labels for a given satellite image and its shapefiles.""" # Try to load tiles from cache. satellite_img_name = get_file_name(geotiff_path) cache_file_name = "{}_{}.pickle".format(satellite_img_name, tile_size) cache_path = os.path.join(TILES_DIR, cache_file_name) try: print("Load tiles from {}.".format(cache_path)) with open(cache_path) as f: tiles = pickle.load(f) return tiles["features"], tiles["labels"] except IOError as e: if only_cache: raise print("Cache not available. Compute tiles.") # The provided satellite images have a different coordinate reference system as # the familiar WGS 84 which uses Latitude and Longitude. So we need to reproject # the satellite image to the WGS 84 coordinate reference system. dataset, wgs84_path = reproject_dataset(geotiff_path) bands = np.dstack(dataset.read()) # For the given satellite image create a bitmap which has 1 at every pixel which corresponds # to water in the satellite image. In order to do this we use water polygons from OpenStreetMap. # The water polygons are stored in forms of shapefiles and are given by "shapefile_paths". water_bitmap = create_bitmap(dataset, shapefile_paths, geotiff_path) # Tile the RGB bands of the satellite image and the bitmap. tiled_bands = create_tiles(bands, tile_size, wgs84_path) tiled_bitmap = create_tiles(water_bitmap, tile_size, wgs84_path) # Due to the projection the satellite image in the GeoTIFF is not a perfect rectangle and the # remaining space on the edges is blacked out. When we overlay the GeoTIFF with the # shapefile it also overlays features for the blacked out parts which means that if we don't # remove these tiles the classifier will be fed with non-empty labels for empty features. tiled_bands, tiled_bitmap = remove_edge_tiles(tiled_bands, tiled_bitmap, tile_size, dataset.shape) save_tiles(cache_path, tiled_bands, tiled_bitmap) return tiled_bands, tiled_bitmap
def visualise_labels(labels, tile_size, out_path): """Given the labels of a satellite image as tiles. Overlay the source image with the labels to check if labels are roughly correct.""" # The tiles might come from different satellite images so we have to # group them according to their source image. get_path = lambda (tiles, pos, path): path sorted_by_path = sorted(labels, key=get_path) for path, predictions in itertools.groupby(sorted_by_path, get_path): raster_dataset = rasterio.open(path) bitmap_shape = (raster_dataset.shape[0], raster_dataset.shape[1]) bitmap = image_from_tiles(predictions, tile_size, bitmap_shape) satellite_img_name = get_file_name(path) out_file_name = "{}.tif".format(satellite_img_name) out = os.path.join(out_path, out_file_name) overlay_bitmap(bitmap, raster_dataset, out)
def create_bitmap(satellite_path, satellite_bitmap_path, raster_dataset): """ load the bitmap for a given satellite image. """ #print('satellite_path: {}, satellite_bitmap_path: {}'.format(satellite_path, satellite_bitmap_path)) satellite_img_name = get_file_name(satellite_path) cache_file_name = "{}_building.tif".format(satellite_img_name) cache_path = os.path.join(BUILDING_BITMAPS_DIR, cache_file_name) try: # Try loading the building bitmap from cache. print("Load building bitmap {}".format(cache_path)) bitmap = load_bitmap(cache_path) bitmap[bitmap == 255] = 1 return bitmap except IOError as e: print("No cache file found.") print('Create bitmaps for building features') print("Load shapefile {}.".format(satellite_bitmap_path)) bitmap_image = rasterio.open(satellite_bitmap_path).read(1) save_bitmap(cache_path, bitmap_image, raster_dataset) bitmap_image[bitmap_image == 255] = 1 return bitmap_image
def visualise_results(results, tile_size, out_path, out_format="GeoTIFF"): """Given the predictions, false positves and the labels of our model visualise them on the satellite image they belong to.""" # The tiles of the predictions, false positives and labels are all in "results". # We need ways to get extract them individually to pass them to overlay_bitmap. get_predictions = lambda (tiles, pos, path): (tiles[0], pos, path) get_labels = lambda (tiles, pos, path): (tiles[1], pos, path) get_false_positives = lambda (tiles, pos, path): (tiles[2], pos, path) get_path = lambda (tiles,pos , path): path sorted_by_path = sorted(results, key=get_path) for path, result_tiles in itertools.groupby(sorted_by_path, get_path): raster_dataset = rasterio.open(path) dataset_shape = (raster_dataset.shape[0], raster_dataset.shape[1]) result_tiles = list(result_tiles) predictions = map(get_predictions, result_tiles) labels = map(get_labels, result_tiles) false_positives = map(get_false_positives, result_tiles) satellite_img_name = get_file_name(path) file_extension = "tif" if out_format == "GeoTIFF" else "shp" out_file_name = "{}_results.{}".format(satellite_img_name, file_extension) out = os.path.join(out_path, out_file_name) if out_format == "GeoTIFF": # We first write the labels in blue, then predictions in green and then false positives in red. # This way the true positives will be green, false positives red, false negatives blue and everything # else in the image will be true negatives. for tiles, color in [(labels, 'blue'), (predictions, 'green'), (false_positives, 'red')]: bitmap = image_from_tiles(tiles, tile_size, dataset_shape) raster_dataset = overlay_bitmap(bitmap, raster_dataset, out, color=color) elif out_format == "Shapefile": bitmap = image_from_tiles(predictions, tile_size, dataset_shape) create_shapefile(bitmap, raster_dataset, out)