GeoAI教程:从NAIP影像提取建筑足迹
A Tutorial on GeoAI: Designing Footprint Extraction from NAIP Imagery Using U-Net, Grounding DINO, SAM, and Mask R-CNN
In this tutorial, we design a complete GeoAI workflow for extracting building footprints from high-resolution NAIP aerial imagery. We begin by configuring the geospatial deep learning environment, downloading raster imagery and vector labels, and inspecting their spatial properties before generating georeferenced image chips and segmentation masks. We then train a U-Net model with a ResNet-34 encoder, evaluate its learning behavior, and apply sliding-window inference to an unseen scene. Beyond semantic segmentation, we convert predicted masks into cleaned and regularized building polygons, calculate IoU and F1 metrics, explore zero-shot segmentation with Grounding DINO and SAM, and compare the results with a pretrained Mask R-CNN instance segmentation model. We also demonstrate how the same pipeline extends to real-world areas using NAIP imagery from Microsoft Planetary Computer and building labels from Overture Maps.
import os import subprocess import sys import time import warnings warnings.filterwarnings("ignore") IN_COLAB = "google.colab" in sys.modules def pip_install(packages, quiet=True): """Install packages with pip from inside the notebook process.""" cmd = [sys.executable, "-m", "pip", "install", "--upgrade"] if quiet: cmd.append("-q") subprocess.run(cmd + list(packages), check=False) try: import geoai except ImportError: print(">>> Installing geoai-py and friends (takes ~2-4 minutes on Colab)...") pip_install( [ "geoai-py", "segmentation-models-pytorch", "buildingregulariser", ] ) try: import geoai except Exception as e: raise SystemExit( f"Import failed after install ({e}).\n" "=> Runtime > Restart session, then re-run this cell. " "The install is cached, so it will be fast the second time." ) import geopandas as gpd import matplotlib.pyplot as plt import numpy as np import rasterio import torch from rasterio.plot import plotting_extent from IPython.display import display print(f"geoai : {geoai.__version__}") print(f"torch : {torch.__version__}") print(f"CUDA available: {torch.cuda.is_available }") if torch.cuda.is_available : print(f"GPU : {torch.cuda.get_device_name(0)}") else: print("!! No GPU detected. Training will still run but be much slower.") print(" Colab: Runtime > Change runtime type > Hardware accelerator > T4 GPU") DEVICE = geoai.get_device print(f"geoai device : {DEVICE}") CFG = { "tile_size": 512, "stride": 256, "buffer_radius": 0, "architecture": "unet", "encoder": "resnet34", "encoder_weights": "imagenet", "num_channels": 3, "num_classes": 2, "batch_size": 8, "num_epochs": 12, "learning_rate": 1e-3, "val_split": 0.2, "window_size": 512, "overlap": 256, "run_zero_shot": True, "run_pretrained": True, "run_real_aoi": False, } WORK = "/content/geoai_tutorial" if IN_COLAB else os.path.abspath("geoai_tutorial") os.makedirs(WORK, exist_ok=True) os.chdir(WORK) print(f"working dir : {WORK}") def banner(text): print("\n" + "=" * 92 + f"\n {text}\n" + "=" * 92) def timed(fn, label): """Run fn , report wall time, never let one step kill the notebook.""" banner(label) t0 = time.time try: out = fn print(f"\n[OK] {label} — {time.time - t0:.1f}s") return out except Exception as exc: import traceback print(f"\n[SKIPPED] {label}\n{type(exc).__name__}: {exc}") traceback.print_exc(limit=3) return None HF = "https://huggingface.co/datasets/giswqs/geospatial/resolve/main" train_raster_url = f"{HF}/naip_rgb_train.tif" train_vector_url = f"{HF}/naip_train_buildings.geojson" test_raster_url = f"{HF}/naip_test.tif" def step1 : train_raster = geoai.download_file(train_raster_url) train_vector = geoai.download_file(train_vector_url) test_raster = geoai.download_file(test_raster_url) for p in (train_raster, train_vector, test_raster): print(f" {os.path.getsize(p) / 1e6:8.2f} MB {p}") return train_raster, train_vector, test_raster paths = timed(step1, "STEP 1 — Downloading sample NAIP imagery and building labels") TRAIN_RASTER, TRAIN_VECTOR, TEST_RASTER = paths
We configure the environment, install the required GeoAI and deep learning libraries, and verify GPU availability. We define the central configuration parameters for dataset creation, model training, inference, and optional processing stages. We then create the working directory, define reusable execution utilities, and download the NAIP imagery and building footprint labels.
def step2 : info = geoai.get_raster_info(TRAIN_RASTER) for k, v in info.items : print(f" {k:<16}: {v}") print("\n--- per-band statistics ---") print(geoai.get_raster_stats(TRAIN_RASTER)) print("\n--- vector info ---") vinfo = geoai.get_vector_info(TRAIN_VECTOR) for k, v in vinfo.items : print(f" {k:<16}: {v}") gdf = gpd.read_file(TRAIN_VECTOR) print(f"\n {len(gdf)} training buildings | CRS {gdf.crs}") print(gdf.head(3)) geoai.view_vector( gdf, raster_path=TRAIN_RASTER, outline_only=True, edge_color="yellow", outline_linewidth=0.8, figsize=(11, 11), title="NAIP training scene + building footprints", ) try: display(geoai.view_vector_interactive(TRAIN_VECTOR, layer_name="Buildings")) except Exception as e: print(f" (interactive map unavailable here: {e})") return gdf LABELS_GDF = timed(step2, "STEP 2 — Inspecting raster + vector data") TILES_DIR = os.path.join(WORK, "tiles") def step3 : stats = geoai.export_geotiff_tiles( in_raster=TRAIN_RASTER, out_folder=TILES_DIR, in_class_data=TRAIN_VECTOR, tile_size=CFG["tile_size"], stride=CFG["stride"], buffer_radius=CFG["buffer_radius"], all_touched=True, skip_empty_tiles=False, quiet=False, ) n_img = len(os.listdir(f"{TILES_DIR}/images")) n_lbl = len(os.listdir(f"{TILES_DIR}/labels")) print(f"\n chips: {n_img} images / {n_lbl} masks") if isinstance(stats, dict): tot = max(stats.get("total_tiles", n_img), 1) print(f" tiles containing buildings: {stats.get('tiles_with_features')} " f"({100 * stats.get('tiles_with_features', 0) / tot:.1f}%)") print(f" foreground pixels: {stats.get('feature_pixels'):,}") geoai.display_training_tiles(TILES_DIR, num_tiles=6, figsize=(18, 6)) return stats TILE_STATS = timed(step3, "STEP 3 — Exporting image chips and label masks")
We inspect the raster and vector datasets to understand their coordinate systems, dimensions, statistics, and feature structures. We visualize the building labels over the aerial imagery and generate an interactive map for spatial exploration. We then divide the source imagery into overlapping georeferenced chips and create matching raster masks for model training.
MODEL_DIR = os.path.join(WORK, "models_unet") BEST_MODEL = os.path.join(MODEL_DIR, "best_model.pth") def step4 : geoai.train_segmentation_model( images_dir=f"{TILES_DIR}/images", labels_dir=f"{TILES_DIR}/labels", output_dir=MODEL_DIR, architecture=CFG["architecture"], encoder_name=CFG["encoder"], encoder_weights=CFG["encoder_weights"], num_channels=CFG["num_channels"], num_classes=CFG["num_classes"], batch_size=CFG["batch_size"], num_epochs=CFG["num_epochs"], learning_rate=CFG["learning_rate"], val_split=CFG["val_split"], save_best_only=True, early_stopping_patience=5, verbose=True, ) print(f"\n best checkpoint: {BEST_MODEL}") print(f" size: {os.path.getsize(BEST_MODEL) / 1e6:.1f} MB") return BEST_MODEL timed(step4, f"STEP 4 — Training {CFG['architecture']}/{CFG['encoder']} " f"for {CFG['num_epochs']} epochs") def step5 : hist_path = os.path.join(MODEL_DIR, "training_history.pth") geoai.plot_performance_metrics( history_path=hist_path, figsize=(15, 5), verbose=True, save_path=os.path.join(WORK, "training_curves.png"), ) h = torch.load(hist_path, weights_only=False) best_ep = int(np.argmax(h["val_iou"])) + 1 print(f"\n best val IoU {max(h['val_iou']):.4f} at epoch {best_ep}") print(" Reading the curves: val loss rising while train loss falls => overfitting;") print(" both flat and high => underfitting (more epochs, bigger encoder, or more chips).") timed(step5, "STEP 5 — Training diagnostics")
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力