Close Menu
  • AI
  • Content Creation
  • Tech
  • Robotics
AI-trends.todayAI-trends.today
  • AI
  • Content Creation
  • Tech
  • Robotics
Trending
  • Appeals Court docket Lets the Pentagon Designate Anthropic a Provide-Chain Threat
  • Aikido Safety Releases Altar-1: An Open-Weight Safety Mannequin Pruned From GLM-5.3 to 328 GB
  • Black Forest Labs Releases FLUX 3 Motion: A 7B Open-Weights World Motion Mannequin That Tops RoboLab-120
  • Fastino Releases GLiNER2.5-Resolve: A 340M Open-Weight Determination Mannequin That Runs on CPU
  • BottleCap AI Releases ThinkingCap-Qwen3.8-27B: 37.2% Fewer Considering Tokens at a 0.86pp Accuracy Price
  • What if I find an AI agent that is worth the risk?
  • Google’s Gemini Can Now Make Requires You on Pixel Telephones
  • An OpenAI Agent Hacked Australia’s Well being Service. Their Authorities Discovered Out Months Later
AI-trends.todayAI-trends.today
Home»Tech»GeoAI Tutorial: Footprint extraction from NAIP images using U-Net Grounding DINO SAM and Mask R-CNN

GeoAI Tutorial: Footprint extraction from NAIP images using U-Net Grounding DINO SAM and Mask R-CNN

Tech By Gavin Wallace02/08/202610 Mins Read
Facebook Twitter LinkedIn Email
LifelongAgentBench: A Benchmark for Evaluating Continuous Learning in LLM-Based Agents
LifelongAgentBench: A Benchmark for Evaluating Continuous Learning in LLM-Based Agents
Share
Facebook Twitter LinkedIn Email

We will design the complete system in this tutorial. GeoAI Workflow for extracting footprints of buildings from high resolution NAIP aerial images. Configuring the deep geospatial learning environment is first, then downloading vector and raster labels and inspecting spatial properties, before creating georeferenced images chips and segmentation Masks. Then, we train a UNet model using a ResNet 34 encoder. We evaluate the learning behavior of this model and then apply sliding window inference on an unknown scene. In addition to semantic segmentation we also explore other methods, such as converting predicted masks into regularized and cleaned building polygons. The pipeline is also extended to actual areas by using NAIP imagery and Overture Maps building labels.

Import os
Subprocess import
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
If you get an ImportError, it's because your import is not working.
   print(">>> Installing geoai-py and friends (takes ~2-4 minutes on Colab)...")
   pip_install(
       [
           "geoai-py",
           "segmentation-models-pytorch",
           "buildingregulariser",
       ]
   )
   try:
       import geoai
 In the absence of e.
       raise SystemExit(
 The 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."
       )
Geopandas can be imported as GPD
Import matplotlib.pyplot into plt
Import numpy as an np
Import rasterio
Import torch
Import plotting_extent from rasterio.plot
Display import from IPython
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_deviceCFG =()
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,
}
The 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)
Label: def timed
   """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 Excl.
 Import traceback
       print(f"n[SKIPPED] {label}n{type(exc).__name__}: {exc}")
       traceback.print_exc(limit=3)
 Return None
H = "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)
 If p is in the (train_raster train_vector test_raster), then:
       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

Configure the environment and install GeoAI, deep learning libraries. Verify GPU compatibility. The central parameters are defined for the dataset generation, training of models, inference and optional post-processing stages. Then we create the directory for the work, specify the reusable utilities and download NAIP images and footprint labels.

Step2():
   info = geoai.get_raster_info(TRAIN_RASTER)
 Information about k and v():
       print(f"  {k:

In order to better understand the datasets, we examine their dimension, statistics and feature structure. Visualizing the labels of the buildings over aerial imagery, we create an interactive map to explore spatially. After dividing the original imagery into overlapping georeferenced tiles, we create matching raster masks to train models.

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")
 Best_Model
Step4: Timed"STEP 4 — Training {CFG['architecture']}/{CFG['encoder']} "
 The n"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")

We then train the U-Net model to segment images and masks using ResNet-34. Configure the training with early stopping, validation splitting, checkpoint saving and performance monitoring. Then we load the history of training, plot learning curves and determine the epoch with the highest IoU for validation.

PRED_MASK = os.path.join(WORK, "test_prediction.tif")
PRED_PROB = os.path.join(WORK, "test_probability.tif")
Step6():
   geoai.semantic_segmentation(
       input_path=TEST_RASTER,
       output_path=PRED_MASK,
       model_path=BEST_MODEL,
       architecture=CFG["architecture"],
       encoder_name=CFG["encoder"],
       num_channels=CFG["num_channels"],
       num_classes=CFG["num_classes"],
       window_size=CFG["window_size"],
       overlap=CFG["overlap"],
       batch_size=4,
       probability_path=PRED_PROB,
   )
   geoai.print_raster_info(PRED_MASK, show_preview=False)
   geoai.plot_prediction_comparison(
       original_image=TEST_RASTER,
       prediction_image=PRED_MASK,
       titles=["NAIP test scene", "Predicted building mask"],
       figsize=(16, 8),
       prediction_colormap="viridis",
       save_path=os.path.join(WORK, "prediction_comparison.png"),
   )
   with rasterio.open(PRED_MASK) as src:
 The m value is src.read(1)
       px = float(abs(src.transform.a) * abs(src.transform.e))
   print(f"  predicted building pixels: {int((m > 0).sum()):,} "
 F"({100 * (m > 0).mean():.2f}% of scene, ~{(m > 0).sum() * px:,.0f} m2)")
timed(step6, "STEP 6 — Sliding-window inference on the test scene")
VEC_RAW = os.path.join(WORK, "buildings_raw.geojson")
VEC_ORTHO = os.path.join(WORK, "buildings_orthogonal.geojson")
VEC_FINAL = os.path.join(WORK, "buildings_final.geojson")
Step7():
 Grouped = Geoai.region_groups
       PRED_MASK,
       connectivity=2,
       min_size=50,
       out_image=os.path.join(WORK, "test_prediction_cleaned.tif"),
   )
   clean_mask = os.path.join(WORK, "test_prediction_cleaned.tif")
   raw = geoai.raster_to_vector(
       clean_mask,
       output_path=VEC_RAW,
       threshold=0,
       min_area=15,
       simplify_tolerance=0.5,
   )
   print(f"  raw polygons        : {len(raw)}")
 Ortho = Geoai.Orthogonalize
       input_path=clean_mask,
       output_path=VEC_ORTHO,
       epsilon=1.5,
       min_area=15,
   )
   print(f"  orthogonalized      : {len(ortho)}")
   final = geoai.regularization(ortho, angle_tolerance=12, simplify_tolerance=0.4)
   final = geoai.add_geometric_properties(
       final, properties=["area", "perimeter", "solidity", "elongation", "orientation"]
   )
   final.to_file(VEC_FINAL, driver="GeoJSON")
   print(f"  final footprints    : {len(final)}")
   print(final.head())
 If you want to know more about if "area" In final.columns
       print("n  footprint area stats (m2):")
       print(final["area"].describe().round(1).to_string())
 Axes = plt.subplots (1, 2, figsize= (16, 8).
   with rasterio.open(TEST_RASTER) as src:
 rgb= src.read[1, 2, 3]).transpose(1, 2, 0)
 rgb= np.clip (rgb/ np.percentile()(rgb), 99), 0, 1
 The plotting_extent() function returns the extent of the image.
   for ax, g, t in zip(axes, [raw, final], ["Raw polygonization", "Orthogonalized + regularized"]):
       ax.imshow(rgb, extent=ext)
       g.plot(ax=ax, facecolor="none", edgecolor="red", linewidth=1.1)
       ax.set_title(t)
       ax.set_axis_off()
   plt.tight_layout()
   plt.show()
   try:
       display(geoai.view_vector_interactive(VEC_FINAL, layer_name="Predicted buildings"))
 Except Exception
       pass
 Return final
FINAL_GDF = timed(step7, "STEP 7 — Vectorizing and regularizing the predicted footprints")

The sliding-window method is used to generate prediction and probability rasters for an unknown NAIP scene. To produce more clean building boundaries, we remove noisy areas, convert the predicted mask to vector polygons and normalize the footprint geometry. Also, we calculate geometric properties. We compare raw polygonized results to the regularized and orthogonalized outputs.

Def step8():
   gt_raster = os.path.join(WORK, "train_gt_mask.tif")
   geoai.vector_to_raster(
       vector_path=TRAIN_VECTOR,
       output_path=gt_raster,
       reference_raster=TRAIN_RASTER,
       fill_value=0,
       all_touched=True,
       dtype=np.uint8,
   )
   train_pred = os.path.join(WORK, "train_prediction.tif")
   geoai.semantic_segmentation(
       input_path=TRAIN_RASTER,
       output_path=train_pred,
       model_path=BEST_MODEL,
       architecture=CFG["architecture"],
       encoder_name=CFG["encoder"],
       num_channels=CFG["num_channels"],
       num_classes=CFG["num_classes"],
       window_size=CFG["window_size"],
       overlap=CFG["overlap"],
       quiet=True,
   )
   metrics = geoai.calc_segmentation_metrics(
       ground_truth=gt_raster,
       prediction=train_pred,
       num_classes=2,
       metrics=["iou", "f1"],
   )
   print("n  --- pixel-wise metrics (class 0 = background, class 1 = building) ---")
 Items for k and v():
       print(f"  {k:

The segmentation model is evaluated by comparing it to the rasterized labels of real-world buildings. Calculating pixel-level IoU metrics, we visualize imagery, prediction, and the reference masks. Then, we use Grounding DINO/SAM for zero-shot segmentation of buildings using only text prompts.

Step10():
 If not CFG["run_pretrained"]:
       print("  disabled in CFG"( ) Return
   extractor = geoai.BuildingFootprintExtractor(model_path="building_footprints_usa.pth")
   gdf = extractor.process_raster(
       TEST_RASTER,
       output_path=os.path.join(WORK, "buildings_maskrcnn.geojson"),
       batch_size=4,
       confidence_threshold=0.5,
       overlap=0.25,
       mask_threshold=0.5,
       min_object_area=100,
       filter_edges=True,
   )
 If len(gdf), or None is gdf, then:
       print("  no instances returned"( ) Return
   print(f"  building instances: {len(gdf)}")
   reg = extractor.regularize_buildings(gdf, min_area=20, angle_threshold=15)
   reg.to_file(os.path.join(WORK, "buildings_maskrcnn_regularized.geojson"), driver="GeoJSON")
   extractor.visualize_results(TEST_RASTER, gdf=reg, figsize=(12, 12))
 If FINAL_GDF does not equal None:
       print(f"n  your U-Net      : {len(FINAL_GDF)} polygons")
       print(f"  pretrained R-CNN: {len(reg)} polygons")
       print("  Different counts are expected: U-Net merges adjacent roofs, Mask R-CNN splits")
       print("  them into instances. Pick the paradigm that matches your downstream question.")
   geoai.empty_cache()
timed(step10, "STEP 10 — Pretrained Mask R-CNN instance segmentation")
Step 11():
 If not CFG["run_real_aoi"]:
       print("  disabled (set CFG['run_real_aoi'] = True to run; needs open internet)")
 Return to the Homepage
 bbox= (-83.9400. 35.9500. -83.9250. 35.9600).
   items = geoai.pc_stac_search(
       collection="naip",
       bbox=list(bbox),
       time_range="2021-01-01/2023-12-31",
       max_items=3,
   )
   print(f"  STAC items found: {len(items)}")
   aoi_dir = os.path.join(WORK, "aoi")
   tif = geoai.download_naip(bbox=bbox, output_dir=aoi_dir, max_items=1, preview=False)
   print(f"  NAIP: {tif}")
   ovt = os.path.join(aoi_dir, "overture_buildings.geojson")
   geoai.download_overture_buildings(bbox=bbox, output=ovt, overture_type="building")
   print(f"  Overture buildings: {ovt}")
   print(geoai.extract_building_stats(ovt))
 The tif file format is raster.[0] If isinstance (tif, list, tuple), else tif
   geoai.export_geotiff_tiles(
       in_raster=raster,
       out_folder=os.path.join(aoi_dir, "tiles"),
       in_class_data=ovt,
       tile_size=512,
       stride=256,
   )
   print("  AOI dataset ready — feed it to train_segmentation_model() exactly as in STEP 4.")
timed(step11, "STEP 11 — (optional) Real AOI: Planetary Computer NAIP + Overture Maps labels")
Def step12():
 Outputs = [f for f in sorted(os.listdir(WORK))
              if f.endswith((".tif", ".geojson", ".png", ".pth"))]
   print("  artifacts produced:")
 For f, outputs are:
       print(f"    {os.path.getsize(os.path.join(WORK, f)) / 1e6:8.2f} MB  {f}")
   zip_path = os.path.join(WORK, "geoai_results.zip")
   subprocess.run(
       ["zip", "-qr", zip_path, ".", "-i", "*.geojson", "*.png", "*.tif", "-x", "*tiles*"],
       cwd=WORK, check=False,
   )
   print(f"n  bundle: {zip_path}")
 If IN_COLAB
       print("  Download it with:  from google.colab import files; "
             "files.download('%s')" % zip_path)
timed(step12, "STEP 12 — Results summary")
banner("DONE")
print("""
Next stop:
----------------
* Swap the head, keep the code: architecture="deeplabv3plus", encoder_name="efficientnet-b3"
 (or any timm encoder) in train_segmentation_model().
* 4-band NAIP (RGB+NIR): num_channels=4 everywhere; the first conv is auto-adapted.
* Multi-class land cover: geoai.train_segmentation_landcover() + geoai.export_landcover_tiles(),
 with DiceLoss / FocalLoss / TverskyLoss from geoai.landcover_train for class imbalance.
* Instance segmentation you train yourself: geoai.train_MaskRCNN_model() You can also read about the importance of a good quality eyewear
 geoai.instance_segmentation(..., vectorize=True).
* Object detection with georeferenced boxes: geoai.train.object_detection() /
 geoai.object_detection(text="cars"() is the open-vocabulary basis of DINO.
* Foundation models: geoai.prithvi_inference (NASA/IBM Prithvi), geoai.universat_inference,
 geoai.DINOv3GeoProcessor for embeddings and similarity maps.
* Change detection: geoai.change_detection (torchange backends).
* Deploy: geoai.export_to_onnx() + geoai.onnx_semantic_segmentation()QGIS is a plugin that can be used to enhance QGIS.
Docs and notebooks: https://opengeoai.org  |  Book: https://book.opengeoai.org
""")

The U-Net model is compared with a pre-trained Mask R-CNN to identify individual buildings. We can optionally generate a real dataset using NAIP imagery downloaded from Microsoft Planetary Computer, and building labels matched from Overture Maps. The generated plots, vectors and model outputs are then compiled into a results package that can be downloaded or analyzed.

We have completed an end to end geospatial pipeline for deep learning that converts aerial imagery into structured data on building footprints. In order to refine raster geometries and generate useful spatial attributes, we refined the raster outputs and prepared training samples. We also trained and evaluated an orthogonalized segmentation model. We explored alternative extraction methods using zero-shot models, pretrained segmentation and pre-trained foundations. This helped us better understand the differences between custom-training, prompt-based detection and ready-to use models. The generated masks were packaged with the probability rasters and evaluation plots. This created a base that can be used for large-scale GeoAI, land-cover mapping and infrastructure detection.


Take a look at the Full Codes here. Also, feel free to follow us on Twitter Don’t forget about our 150k+ML SubReddit Subscribe now our Newsletter. Wait! Are you using Telegram? now you can join us on telegram as well.

Want to promote your GitHub repo, Hugging Face page, Product release or Webinar?? Connect with us


Sana Hassan is a dual-degree IIT Madras student and consulting intern with Marktechpost. She loves to apply technology and AI in order to solve real-world problems. Sana Hassan, an intern at Marktechpost and dual-degree student at IIT Madras is passionate about applying technology and AI to real-world challenges.

AI Net x
Share. Facebook Twitter LinkedIn Email
Avatar
Gavin Wallace

Related Posts

Aikido Safety Releases Altar-1: An Open-Weight Safety Mannequin Pruned From GLM-5.3 to 328 GB

25/09/2026

Black Forest Labs Releases FLUX 3 Motion: A 7B Open-Weights World Motion Mannequin That Tops RoboLab-120

25/09/2026

Fastino Releases GLiNER2.5-Resolve: A 340M Open-Weight Determination Mannequin That Runs on CPU

25/09/2026

BottleCap AI Releases ThinkingCap-Qwen3.8-27B: 37.2% Fewer Considering Tokens at a 0.86pp Accuracy Price

24/09/2026
Top News

Video Games: The New Battleground For Actors And AI Protection

Can Cursor remain a platform for OpenAI’s and Anthropic’s models inside SpaceX?

I Let an AI Agent Hack All My Gadgets—and I’d Do It Again

New York City date night for AI lovers

A robot that can learn on the spot is the future of AI.

Load More
AI-Trends.Today

Your daily source of AI news and trends. Stay up to date with everything AI and automation!

X (Twitter) Instagram
Top Insights

Ilya Sutskever Stands by His Role in Sam Altman’s OpenAI Ouster: ‘I Didn’t Want It to Be Destroyed’

12/05/2026

NVIDIA BioNeMo Agent Tools Turns Biomolecular Modells into Callable Skill for AI Agents to Drug Discovery

29/06/2026
Latest News

Appeals Court docket Lets the Pentagon Designate Anthropic a Provide-Chain Threat

25/09/2026

Aikido Safety Releases Altar-1: An Open-Weight Safety Mannequin Pruned From GLM-5.3 to 328 GB

25/09/2026
X (Twitter) Instagram
  • Privacy Policy
  • Contact Us
  • Terms and Conditions
© 2026 AI-Trends.Today

Type above and press Enter to search. Press Esc to cancel.