Skip to content

GeoDeep - Object Detection & Segmentation

GeoDeep is a lightweight library for applying ONNX AI models to geospatial imagery. It supports object detection (bounding boxes) and semantic segmentation (pixel masks) with built-in models for common remote sensing tasks.

  • CPU & GPU: Runs on ONNX Runtime — CPU by default, NVIDIA CUDA when available
  • Input: GeoTIFF rasters
  • Output: GeoDataFrame, GeoJSON, GeoPackage, Shapefile, GeoTIFF masks

Installation

Install GeoAI with the GeoDeep extra:

1
2
pip install geoai-py[geodeep]          # CPU only
pip install geoai-py[geodeep-gpu]      # GPU (CUDA) support

Or install GeoDeep standalone:

1
2
pip install geodeep                    # CPU
pip install geodeep onnxruntime-gpu    # GPU

Available Models

Model ID Type Description Resolution
cars Detection Car detection (YOLOv7-m) 10 cm/px
trees Detection Tree crown detection (RetinaNet) 10 cm/px
trees_yolov9 Detection Tree crown detection (YOLOv9) 10 cm/px
birds Detection Bird detection (RetinaNet) 2 cm/px
planes Detection Plane detection (YOLOv7-tiny) 70 cm/px
aerovision Detection Multi-class aerial detection (YOLOv8) — vehicles, pools, fields, courts, bridges, etc. 30 cm/px
utilities Detection Utility infrastructure (YOLOv8) — Gas, Manhole, Power, Sewer, Telecom, Water 3 cm/px
buildings Segmentation Building footprint segmentation (XUNet) 50 cm/px
roads Segmentation Road network segmentation 21 cm/px

Models are automatically downloaded and cached on first use.

Quick Start

Object Detection

1
2
3
4
5
6
from geoai import GeoDeep

gd = GeoDeep("cars")
detections = gd.detect("aerial_image.tif")
print(f"Found {len(detections)} cars")
detections.head()

The result is a GeoDataFrame with geometry (bounding box polygons in EPSG:4326), score (confidence), and class (label) columns.

Semantic Segmentation

1
2
3
gd = GeoDeep("buildings")
result = gd.segment("satellite.tif", output_raster_path="buildings_mask.tif")
print(result["mask"].shape)  # (height, width) uint8 array

Save Detection Results

1
2
gd = GeoDeep("cars")
gd.detect("image.tif", output_path="detections.geojson")

Supported output formats: .geojson, .gpkg, .shp, .parquet.

Usage Examples

Detection with Confidence Filtering

1
2
3
4
5
gd = GeoDeep("aerovision", conf_threshold=0.7)
detections = gd.detect(
    "image.tif",
    classes=["small-vehicle", "plane"],
)

Batch Detection

Process multiple images at once with combined results:

1
2
3
4
5
6
7
gd = GeoDeep("trees")
results = gd.detect_batch(
    ["area1.tif", "area2.tif", "area3.tif"],
    output_dir="results/",
)
print(f"Total detections: {len(results)}")
# results includes a 'source_file' column to track which image each detection came from

Batch Segmentation

1
2
3
4
5
6
gd = GeoDeep("roads")
results = gd.segment_batch(
    ["tile1.tif", "tile2.tif"],
    output_dir="masks/",
    output_format="raster",  # or "vector" or "both"
)

Segmentation to Vector

Export segmentation results as vector polygons:

1
2
3
4
5
6
7
gd = GeoDeep("buildings")
result = gd.segment(
    "city.tif",
    output_vector_path="buildings.gpkg",
)
buildings_gdf = result["gdf"]
print(f"Found {len(buildings_gdf)} building polygons")

List Available Models

1
2
3
4
from geoai import list_geodeep_models

for name, desc in list_geodeep_models().items():
    print(f"{name}: {desc}")

Using Convenience Functions

For one-off calls without creating a class instance:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from geoai import geodeep_detect, geodeep_segment

# Detection
detections = geodeep_detect("image.tif", model_id="planes")

# Segmentation
result = geodeep_segment(
    "image.tif",
    model_id="roads",
    output_raster_path="roads_mask.tif",
)

Custom ONNX Models

You can use a custom ONNX model file instead of a built-in model:

1
2
gd = GeoDeep("/path/to/custom_model.onnx")
detections = gd.detect("image.tif")

API Reference

GeoDeep integration for object detection and segmentation on geospatial imagery.

This module provides a Python interface to GeoDeep (https://github.com/uav4geo/GeoDeep), a lightweight library for applying ONNX AI models to geospatial rasters. Supports object detection (cars, trees, birds, planes, utilities, aerovision) and semantic segmentation (buildings, roads) with georeferenced output.

Requirements
  • geodeep
  • onnxruntime (CPU) or onnxruntime-gpu (NVIDIA CUDA)

Install with::

1
2
pip install geoai-py[geodeep]          # CPU only
pip install geoai-py[geodeep-gpu]      # GPU (CUDA) support

GeoDeep

Object detection and segmentation on geospatial imagery using GeoDeep.

GeoDeep is a lightweight library for applying ONNX AI models to geospatial rasters. It supports object detection (bounding boxes) and semantic segmentation (pixel masks) with built-in models for cars, trees, birds, planes, buildings, roads, and more.

Models are automatically downloaded and cached on first use.

Parameters:

Name Type Description Default
model_id str

Built-in model name (see GEODEEP_MODELS) or path to a custom .onnx file. Defaults to "cars".

'cars'
conf_threshold float

Override the default confidence threshold for detections. None uses the model default.

None
classes list

Filter results to specific class names. Only applicable to multi-class models like "aerovision" and "utilities". None keeps all classes.

None
resolution float

Override the image resolution in cm/pixel. None lets GeoDeep estimate it from the raster.

None
device str

Inference device — "auto" (default) selects CUDA if onnxruntime-gpu is installed, otherwise CPU. Use "cpu" to force CPU or "cuda" to require GPU.

'auto'
max_threads int

Maximum number of ONNX inference threads. None uses the default.

None
Example

from geoai import GeoDeep gd = GeoDeep("cars") detections = gd.detect("aerial_image.tif") print(f"Found {len(detections)} cars")

Example (GPU): >>> gd = GeoDeep("buildings", device="cuda") >>> print(gd.device) # 'cuda'

Source code in geoai/geodeep.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
class GeoDeep:
    """Object detection and segmentation on geospatial imagery using GeoDeep.

    GeoDeep is a lightweight library for applying ONNX AI models to
    geospatial rasters. It supports object detection (bounding boxes)
    and semantic segmentation (pixel masks) with built-in models for
    cars, trees, birds, planes, buildings, roads, and more.

    Models are automatically downloaded and cached on first use.

    Args:
        model_id (str): Built-in model name (see ``GEODEEP_MODELS``) or
            path to a custom ``.onnx`` file. Defaults to ``"cars"``.
        conf_threshold (float, optional): Override the default confidence
            threshold for detections. ``None`` uses the model default.
        classes (list, optional): Filter results to specific class names.
            Only applicable to multi-class models like ``"aerovision"``
            and ``"utilities"``. ``None`` keeps all classes.
        resolution (float, optional): Override the image resolution in
            cm/pixel. ``None`` lets GeoDeep estimate it from the raster.
        device (str, optional): Inference device — ``"auto"`` (default)
            selects CUDA if ``onnxruntime-gpu`` is installed, otherwise
            CPU. Use ``"cpu"`` to force CPU or ``"cuda"`` to require GPU.
        max_threads (int, optional): Maximum number of ONNX inference
            threads. ``None`` uses the default.

    Example:
        >>> from geoai import GeoDeep
        >>> gd = GeoDeep("cars")
        >>> detections = gd.detect("aerial_image.tif")
        >>> print(f"Found {len(detections)} cars")

    Example (GPU):
        >>> gd = GeoDeep("buildings", device="cuda")
        >>> print(gd.device)  # 'cuda'
    """

    def __init__(
        self,
        model_id: str = "cars",
        conf_threshold: Optional[float] = None,
        classes: Optional[List[str]] = None,
        resolution: Optional[float] = None,
        device: str = "auto",
        max_threads: Optional[int] = None,
    ) -> None:
        check_geodeep_available()

        self.model_id = model_id
        self.conf_threshold = conf_threshold
        self.classes = classes
        self.resolution = resolution
        self.max_threads = max_threads

        # Resolve device
        if device == "auto":
            self._device = _get_onnx_device()
        else:
            self._device = device

        # Determine model type if it's a built-in model
        if model_id in GEODEEP_MODELS:
            self._model_info = GEODEEP_MODELS[model_id]
        else:
            # Custom model — metadata not available
            self._model_info = None

    @property
    def model_type(self) -> Optional[str]:
        """Return the model type (``'detection'`` or ``'segmentation'``)."""
        if self._model_info is not None:
            return self._model_info["type"]
        return None

    @property
    def model_info(self) -> Optional[Dict[str, Any]]:
        """Return model metadata dict (type, description, resolution, classes).

        Returns ``None`` for custom ONNX models not in the built-in registry.
        """
        return self._model_info

    @property
    def available_classes(self) -> Optional[List[str]]:
        """Return the list of class names this model can detect/segment.

        Returns ``None`` for custom ONNX models not in the built-in registry.
        """
        if self._model_info is not None:
            return list(self._model_info["classes"])
        return None

    @property
    def device(self) -> str:
        """Return the inference device (``'cpu'`` or ``'cuda'``)."""
        return self._device

    def _build_run_kwargs(
        self,
        verbose: bool = True,
        **overrides: Any,
    ) -> Dict[str, Any]:
        """Build keyword arguments for ``geodeep.run()``.

        Merges instance-level settings with per-call overrides.
        Per-call values take precedence over instance defaults.
        """
        kwargs: Dict[str, Any] = {}

        # Confidence threshold: per-call > instance > model default
        threshold = overrides.get("conf_threshold")
        if threshold is None:
            threshold = self.conf_threshold
        if threshold is not None:
            kwargs["conf_threshold"] = threshold

        # Class filter: per-call > instance
        cls_filter = overrides.get("classes")
        if cls_filter is None:
            cls_filter = self.classes
        if cls_filter is not None:
            kwargs["classes"] = cls_filter

        # Resolution: per-call > instance
        resolution = overrides.get("resolution")
        if resolution is None:
            resolution = self.resolution
        if resolution is not None:
            kwargs["resolution"] = resolution

        if self.max_threads is not None:
            kwargs["max_threads"] = self.max_threads
        if not verbose:
            kwargs["progress_callback"] = lambda *a: None

        return kwargs

    def detect(
        self,
        image_path: str,
        conf_threshold: Optional[float] = None,
        classes: Optional[List[str]] = None,
        resolution: Optional[float] = None,
        output_path: Optional[str] = None,
        verbose: bool = True,
    ) -> "gpd.GeoDataFrame":
        """Run object detection on a GeoTIFF image.

        Args:
            image_path (str): Path to the input GeoTIFF file.
            conf_threshold (float, optional): Confidence threshold override
                for this call. Falls back to the instance threshold, then
                the model default.
            classes (list, optional): Filter to specific class names for
                this call. Falls back to the instance setting.
            resolution (float, optional): Override image resolution in
                cm/pixel. Falls back to the instance setting.
            output_path (str, optional): Path to save results as a vector
                file (GeoJSON, GeoPackage, Shapefile). Format is inferred
                from the extension.
            verbose (bool): Print progress information. Defaults to True.

        Returns:
            geopandas.GeoDataFrame: Detection results with columns:
                - ``geometry``: Bounding box polygons (EPSG:4326).
                - ``score``: Confidence score (float).
                - ``class``: Class label (str).

        Raises:
            FileNotFoundError: If ``image_path`` does not exist.
            RuntimeError: If inference fails.

        Example:
            >>> gd = GeoDeep("cars")
            >>> detections = gd.detect("aerial.tif", conf_threshold=0.6)
            >>> print(detections[["class", "score"]].head())
        """
        self._validate_image_path(image_path)

        kwargs = self._build_run_kwargs(
            verbose=verbose,
            conf_threshold=conf_threshold,
            classes=classes,
            resolution=resolution,
        )
        kwargs["output_type"] = "geojson"

        try:
            geojson_str = _geodeep.run(image_path, self.model_id, **kwargs)
        except Exception as exc:
            raise RuntimeError(
                f"GeoDeep detection failed on '{image_path}': {exc}"
            ) from exc

        gdf = self._geojson_to_geodataframe(geojson_str)

        if output_path is not None:
            self._save_vector(gdf, output_path)
            if verbose:
                print(f"Detections saved to: {output_path}")

        return gdf

    def segment(
        self,
        image_path: str,
        output_raster_path: Optional[str] = None,
        output_vector_path: Optional[str] = None,
        resolution: Optional[float] = None,
        verbose: bool = True,
    ) -> Dict[str, Any]:
        """Run semantic segmentation on a GeoTIFF image.

        Args:
            image_path (str): Path to the input GeoTIFF file.
            output_raster_path (str, optional): Path to save the
                segmentation mask as a georeferenced GeoTIFF.
            output_vector_path (str, optional): Path to save vectorized
                segmentation polygons (GeoJSON, GeoPackage, Shapefile).
            resolution (float, optional): Override image resolution in
                cm/pixel. Falls back to the instance setting.
            verbose (bool): Print progress information. Defaults to True.

        Returns:
            dict: Result dictionary with keys:
                - ``"mask"`` (numpy.ndarray): Segmentation mask of shape
                  ``(height, width)`` with ``uint8`` class indices.
                - ``"gdf"`` (geopandas.GeoDataFrame, optional): Vectorized
                  segmentation polygons. Present only when
                  ``output_vector_path`` is provided.

        Raises:
            FileNotFoundError: If ``image_path`` does not exist.
            RuntimeError: If inference fails.

        Example:
            >>> gd = GeoDeep("buildings")
            >>> result = gd.segment("city.tif",
            ...                     output_raster_path="mask.tif")
            >>> print(result["mask"].shape)
        """
        self._validate_image_path(image_path)

        run_kwargs = self._build_run_kwargs(
            verbose=verbose,
            resolution=resolution,
        )

        # Choose output_type based on what the caller needs.
        # If only vector is requested (no raster), use 'geojson' to avoid
        # running inference twice. Otherwise get 'raw' mask first.
        need_mask = output_raster_path is not None or output_vector_path is None
        need_vector = output_vector_path is not None

        result: Dict[str, Any] = {}

        if need_mask:
            try:
                mask = _geodeep.run(
                    image_path,
                    self.model_id,
                    output_type="raw",
                    **run_kwargs,
                )
            except Exception as exc:
                raise RuntimeError(
                    f"GeoDeep segmentation failed on '{image_path}': {exc}"
                ) from exc

            result["mask"] = mask

            # Save raster mask
            if output_raster_path is not None:
                _save_mask_to_raster(image_path, mask, output_raster_path)
                result["raster_path"] = output_raster_path
                if verbose:
                    print(f"Mask saved to: {output_raster_path}")

        # Vectorize segmentation
        if need_vector:
            try:
                geojson_str = _geodeep.run(
                    image_path,
                    self.model_id,
                    output_type="geojson",
                    **run_kwargs,
                )
                gdf = self._geojson_to_geodataframe(geojson_str)
            except Exception as exc:
                raise RuntimeError(f"GeoDeep vectorization failed: {exc}") from exc

            self._save_vector(gdf, output_vector_path)
            result["gdf"] = gdf
            result["vector_path"] = output_vector_path
            if verbose:
                print(f"Vectors saved to: {output_vector_path}")

            # If mask wasn't computed above, still get it for completeness
            if "mask" not in result:
                try:
                    mask = _geodeep.run(
                        image_path,
                        self.model_id,
                        output_type="raw",
                        **run_kwargs,
                    )
                    result["mask"] = mask
                except Exception:
                    pass  # Vector output was the priority

        return result

    def detect_batch(
        self,
        image_paths: List[str],
        output_dir: Optional[str] = None,
        conf_threshold: Optional[float] = None,
        classes: Optional[List[str]] = None,
        verbose: bool = True,
    ) -> "gpd.GeoDataFrame":
        """Run object detection on multiple GeoTIFF images.

        Args:
            image_paths (list): List of paths to input GeoTIFF files.
            output_dir (str, optional): Directory to save per-image
                detection results as GeoJSON files.
            conf_threshold (float, optional): Confidence threshold override.
            classes (list, optional): Filter to specific class names.
            verbose (bool): Print progress information. Defaults to True.

        Returns:
            geopandas.GeoDataFrame: Combined detections from all images
                with an additional ``source_file`` column.

        Example:
            >>> gd = GeoDeep("trees")
            >>> results = gd.detect_batch(
            ...     ["area1.tif", "area2.tif"],
            ...     output_dir="results/"
            ... )
            >>> print(f"Total detections: {len(results)}")
        """
        if output_dir is not None:
            os.makedirs(output_dir, exist_ok=True)

        all_gdfs = []
        total = len(image_paths)

        for idx, path in enumerate(image_paths, 1):
            if verbose:
                print(f"Processing [{idx}/{total}]: {path}")
            try:
                out_path = None
                if output_dir is not None:
                    base = os.path.splitext(os.path.basename(path))[0]
                    out_path = os.path.join(output_dir, f"{base}_detections.geojson")

                gdf = self.detect(
                    path,
                    conf_threshold=conf_threshold,
                    classes=classes,
                    output_path=out_path,
                    verbose=False,
                )
                gdf["source_file"] = path
                all_gdfs.append(gdf)
            except Exception as exc:
                if verbose:
                    print(f"  Warning: Failed on '{path}': {exc}")
                continue

        if all_gdfs:
            import pandas as pd

            combined = gpd.GeoDataFrame(
                pd.concat(all_gdfs, ignore_index=True),
                crs=all_gdfs[0].crs,
            )
        else:
            combined = gpd.GeoDataFrame(
                columns=["geometry", "class", "score", "source_file"],
                crs="EPSG:4326",
            )

        if verbose:
            print(f"Total detections: {len(combined)}")

        return combined

    def segment_batch(
        self,
        image_paths: List[str],
        output_dir: Optional[str] = None,
        output_format: str = "raster",
        verbose: bool = True,
    ) -> List[Dict[str, Any]]:
        """Run semantic segmentation on multiple GeoTIFF images.

        Args:
            image_paths (list): List of paths to input GeoTIFF files.
            output_dir (str, optional): Directory to save results.
            output_format (str): Output format — ``"raster"`` for GeoTIFF
                masks, ``"vector"`` for GeoPackage polygons, or ``"both"``.
                Defaults to ``"raster"``.
            verbose (bool): Print progress information. Defaults to True.

        Returns:
            list: List of result dictionaries from ``segment()``,
                one per input image.

        Example:
            >>> gd = GeoDeep("roads")
            >>> results = gd.segment_batch(
            ...     ["tile1.tif", "tile2.tif"],
            ...     output_dir="masks/",
            ...     output_format="raster",
            ... )
        """
        if output_dir is not None:
            os.makedirs(output_dir, exist_ok=True)

        results = []
        total = len(image_paths)

        for idx, path in enumerate(image_paths, 1):
            if verbose:
                print(f"Processing [{idx}/{total}]: {path}")
            try:
                base = os.path.splitext(os.path.basename(path))[0]
                raster_out = None
                vector_out = None

                if output_dir is not None:
                    if output_format in ("raster", "both"):
                        raster_out = os.path.join(output_dir, f"{base}_mask.tif")
                    if output_format in ("vector", "both"):
                        vector_out = os.path.join(output_dir, f"{base}_segments.gpkg")

                result = self.segment(
                    path,
                    output_raster_path=raster_out,
                    output_vector_path=vector_out,
                    verbose=False,
                )
                result["source_file"] = path
                results.append(result)
            except Exception as exc:
                if verbose:
                    print(f"  Warning: Failed on '{path}': {exc}")
                continue

        if verbose:
            print(f"Processed {len(results)}/{total} images")

        return results

    @staticmethod
    def _geojson_to_geodataframe(
        geojson_str: str,
        crs: str = "EPSG:4326",
    ) -> "gpd.GeoDataFrame":
        """Convert a GeoJSON string from GeoDeep to a GeoDataFrame.

        Args:
            geojson_str (str): GeoJSON FeatureCollection string.
            crs (str): Coordinate reference system. Defaults to EPSG:4326.

        Returns:
            geopandas.GeoDataFrame: Parsed features with geometry and
                attribute columns.
        """
        geojson_dict = json.loads(geojson_str)
        features = geojson_dict.get("features", [])

        if not features:
            return gpd.GeoDataFrame(
                columns=["geometry", "class", "score"],
                crs=crs,
            )

        geometries = []
        properties_list = []

        for feat in features:
            geom = shape(feat["geometry"])
            geometries.append(geom)
            props = dict(feat.get("properties", {}))
            properties_list.append(props)

        gdf = gpd.GeoDataFrame(
            properties_list,
            geometry=geometries,
            crs=crs,
        )

        return gdf

    @staticmethod
    def _save_vector(
        gdf: "gpd.GeoDataFrame",
        output_path: str,
        driver: Optional[str] = None,
    ) -> str:
        """Save a GeoDataFrame to a vector file.

        The file format is inferred from the extension unless ``driver``
        is specified explicitly.

        Args:
            gdf (geopandas.GeoDataFrame): Data to save.
            output_path (str): Output file path.
            driver (str, optional): OGR driver name. Inferred from
                extension if ``None``.

        Returns:
            str: The output path.
        """
        if driver is None:
            ext = os.path.splitext(output_path)[1].lower()
            driver_map = {
                ".geojson": "GeoJSON",
                ".json": "GeoJSON",
                ".gpkg": "GPKG",
                ".shp": "ESRI Shapefile",
                ".parquet": None,
                ".geoparquet": None,
            }
            driver = driver_map.get(ext, "GeoJSON")

        out_dir = os.path.dirname(output_path)
        if out_dir:
            os.makedirs(out_dir, exist_ok=True)

        if driver is None:
            gdf.to_parquet(output_path)
        else:
            gdf.to_file(output_path, driver=driver)

        return output_path

    @staticmethod
    def _validate_image_path(image_path: str) -> None:
        """Raise FileNotFoundError if the image does not exist."""
        if not os.path.isfile(image_path):
            raise FileNotFoundError(f"Image file not found: '{image_path}'")

    def __repr__(self) -> str:
        parts = [f"model_id={self.model_id!r}"]
        if self.conf_threshold is not None:
            parts.append(f"conf_threshold={self.conf_threshold}")
        if self.classes is not None:
            parts.append(f"classes={self.classes!r}")
        if self.resolution is not None:
            parts.append(f"resolution={self.resolution}")
        parts.append(f"device={self._device!r}")
        return f"GeoDeep({', '.join(parts)})"

    def __str__(self) -> str:
        model_type = self.model_type or "custom"
        desc = ""
        if self._model_info is not None:
            desc = f" - {self._model_info['description']}"
        return f"GeoDeep[{self.model_id}] ({model_type}, {self._device}){desc}"

available_classes property

Return the list of class names this model can detect/segment.

Returns None for custom ONNX models not in the built-in registry.

device property

Return the inference device ('cpu' or 'cuda').

model_info property

Return model metadata dict (type, description, resolution, classes).

Returns None for custom ONNX models not in the built-in registry.

model_type property

Return the model type ('detection' or 'segmentation').

detect(image_path, conf_threshold=None, classes=None, resolution=None, output_path=None, verbose=True)

Run object detection on a GeoTIFF image.

Parameters:

Name Type Description Default
image_path str

Path to the input GeoTIFF file.

required
conf_threshold float

Confidence threshold override for this call. Falls back to the instance threshold, then the model default.

None
classes list

Filter to specific class names for this call. Falls back to the instance setting.

None
resolution float

Override image resolution in cm/pixel. Falls back to the instance setting.

None
output_path str

Path to save results as a vector file (GeoJSON, GeoPackage, Shapefile). Format is inferred from the extension.

None
verbose bool

Print progress information. Defaults to True.

True

Returns:

Type Description
GeoDataFrame

geopandas.GeoDataFrame: Detection results with columns: - geometry: Bounding box polygons (EPSG:4326). - score: Confidence score (float). - class: Class label (str).

Raises:

Type Description
FileNotFoundError

If image_path does not exist.

RuntimeError

If inference fails.

Example

gd = GeoDeep("cars") detections = gd.detect("aerial.tif", conf_threshold=0.6) print(detections[["class", "score"]].head())

Source code in geoai/geodeep.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
def detect(
    self,
    image_path: str,
    conf_threshold: Optional[float] = None,
    classes: Optional[List[str]] = None,
    resolution: Optional[float] = None,
    output_path: Optional[str] = None,
    verbose: bool = True,
) -> "gpd.GeoDataFrame":
    """Run object detection on a GeoTIFF image.

    Args:
        image_path (str): Path to the input GeoTIFF file.
        conf_threshold (float, optional): Confidence threshold override
            for this call. Falls back to the instance threshold, then
            the model default.
        classes (list, optional): Filter to specific class names for
            this call. Falls back to the instance setting.
        resolution (float, optional): Override image resolution in
            cm/pixel. Falls back to the instance setting.
        output_path (str, optional): Path to save results as a vector
            file (GeoJSON, GeoPackage, Shapefile). Format is inferred
            from the extension.
        verbose (bool): Print progress information. Defaults to True.

    Returns:
        geopandas.GeoDataFrame: Detection results with columns:
            - ``geometry``: Bounding box polygons (EPSG:4326).
            - ``score``: Confidence score (float).
            - ``class``: Class label (str).

    Raises:
        FileNotFoundError: If ``image_path`` does not exist.
        RuntimeError: If inference fails.

    Example:
        >>> gd = GeoDeep("cars")
        >>> detections = gd.detect("aerial.tif", conf_threshold=0.6)
        >>> print(detections[["class", "score"]].head())
    """
    self._validate_image_path(image_path)

    kwargs = self._build_run_kwargs(
        verbose=verbose,
        conf_threshold=conf_threshold,
        classes=classes,
        resolution=resolution,
    )
    kwargs["output_type"] = "geojson"

    try:
        geojson_str = _geodeep.run(image_path, self.model_id, **kwargs)
    except Exception as exc:
        raise RuntimeError(
            f"GeoDeep detection failed on '{image_path}': {exc}"
        ) from exc

    gdf = self._geojson_to_geodataframe(geojson_str)

    if output_path is not None:
        self._save_vector(gdf, output_path)
        if verbose:
            print(f"Detections saved to: {output_path}")

    return gdf

detect_batch(image_paths, output_dir=None, conf_threshold=None, classes=None, verbose=True)

Run object detection on multiple GeoTIFF images.

Parameters:

Name Type Description Default
image_paths list

List of paths to input GeoTIFF files.

required
output_dir str

Directory to save per-image detection results as GeoJSON files.

None
conf_threshold float

Confidence threshold override.

None
classes list

Filter to specific class names.

None
verbose bool

Print progress information. Defaults to True.

True

Returns:

Type Description
GeoDataFrame

geopandas.GeoDataFrame: Combined detections from all images with an additional source_file column.

Example

gd = GeoDeep("trees") results = gd.detect_batch( ... ["area1.tif", "area2.tif"], ... output_dir="results/" ... ) print(f"Total detections: {len(results)}")

Source code in geoai/geodeep.py
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
def detect_batch(
    self,
    image_paths: List[str],
    output_dir: Optional[str] = None,
    conf_threshold: Optional[float] = None,
    classes: Optional[List[str]] = None,
    verbose: bool = True,
) -> "gpd.GeoDataFrame":
    """Run object detection on multiple GeoTIFF images.

    Args:
        image_paths (list): List of paths to input GeoTIFF files.
        output_dir (str, optional): Directory to save per-image
            detection results as GeoJSON files.
        conf_threshold (float, optional): Confidence threshold override.
        classes (list, optional): Filter to specific class names.
        verbose (bool): Print progress information. Defaults to True.

    Returns:
        geopandas.GeoDataFrame: Combined detections from all images
            with an additional ``source_file`` column.

    Example:
        >>> gd = GeoDeep("trees")
        >>> results = gd.detect_batch(
        ...     ["area1.tif", "area2.tif"],
        ...     output_dir="results/"
        ... )
        >>> print(f"Total detections: {len(results)}")
    """
    if output_dir is not None:
        os.makedirs(output_dir, exist_ok=True)

    all_gdfs = []
    total = len(image_paths)

    for idx, path in enumerate(image_paths, 1):
        if verbose:
            print(f"Processing [{idx}/{total}]: {path}")
        try:
            out_path = None
            if output_dir is not None:
                base = os.path.splitext(os.path.basename(path))[0]
                out_path = os.path.join(output_dir, f"{base}_detections.geojson")

            gdf = self.detect(
                path,
                conf_threshold=conf_threshold,
                classes=classes,
                output_path=out_path,
                verbose=False,
            )
            gdf["source_file"] = path
            all_gdfs.append(gdf)
        except Exception as exc:
            if verbose:
                print(f"  Warning: Failed on '{path}': {exc}")
            continue

    if all_gdfs:
        import pandas as pd

        combined = gpd.GeoDataFrame(
            pd.concat(all_gdfs, ignore_index=True),
            crs=all_gdfs[0].crs,
        )
    else:
        combined = gpd.GeoDataFrame(
            columns=["geometry", "class", "score", "source_file"],
            crs="EPSG:4326",
        )

    if verbose:
        print(f"Total detections: {len(combined)}")

    return combined

segment(image_path, output_raster_path=None, output_vector_path=None, resolution=None, verbose=True)

Run semantic segmentation on a GeoTIFF image.

Parameters:

Name Type Description Default
image_path str

Path to the input GeoTIFF file.

required
output_raster_path str

Path to save the segmentation mask as a georeferenced GeoTIFF.

None
output_vector_path str

Path to save vectorized segmentation polygons (GeoJSON, GeoPackage, Shapefile).

None
resolution float

Override image resolution in cm/pixel. Falls back to the instance setting.

None
verbose bool

Print progress information. Defaults to True.

True

Returns:

Name Type Description
dict Dict[str, Any]

Result dictionary with keys: - "mask" (numpy.ndarray): Segmentation mask of shape (height, width) with uint8 class indices. - "gdf" (geopandas.GeoDataFrame, optional): Vectorized segmentation polygons. Present only when output_vector_path is provided.

Raises:

Type Description
FileNotFoundError

If image_path does not exist.

RuntimeError

If inference fails.

Example

gd = GeoDeep("buildings") result = gd.segment("city.tif", ... output_raster_path="mask.tif") print(result["mask"].shape)

Source code in geoai/geodeep.py
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
def segment(
    self,
    image_path: str,
    output_raster_path: Optional[str] = None,
    output_vector_path: Optional[str] = None,
    resolution: Optional[float] = None,
    verbose: bool = True,
) -> Dict[str, Any]:
    """Run semantic segmentation on a GeoTIFF image.

    Args:
        image_path (str): Path to the input GeoTIFF file.
        output_raster_path (str, optional): Path to save the
            segmentation mask as a georeferenced GeoTIFF.
        output_vector_path (str, optional): Path to save vectorized
            segmentation polygons (GeoJSON, GeoPackage, Shapefile).
        resolution (float, optional): Override image resolution in
            cm/pixel. Falls back to the instance setting.
        verbose (bool): Print progress information. Defaults to True.

    Returns:
        dict: Result dictionary with keys:
            - ``"mask"`` (numpy.ndarray): Segmentation mask of shape
              ``(height, width)`` with ``uint8`` class indices.
            - ``"gdf"`` (geopandas.GeoDataFrame, optional): Vectorized
              segmentation polygons. Present only when
              ``output_vector_path`` is provided.

    Raises:
        FileNotFoundError: If ``image_path`` does not exist.
        RuntimeError: If inference fails.

    Example:
        >>> gd = GeoDeep("buildings")
        >>> result = gd.segment("city.tif",
        ...                     output_raster_path="mask.tif")
        >>> print(result["mask"].shape)
    """
    self._validate_image_path(image_path)

    run_kwargs = self._build_run_kwargs(
        verbose=verbose,
        resolution=resolution,
    )

    # Choose output_type based on what the caller needs.
    # If only vector is requested (no raster), use 'geojson' to avoid
    # running inference twice. Otherwise get 'raw' mask first.
    need_mask = output_raster_path is not None or output_vector_path is None
    need_vector = output_vector_path is not None

    result: Dict[str, Any] = {}

    if need_mask:
        try:
            mask = _geodeep.run(
                image_path,
                self.model_id,
                output_type="raw",
                **run_kwargs,
            )
        except Exception as exc:
            raise RuntimeError(
                f"GeoDeep segmentation failed on '{image_path}': {exc}"
            ) from exc

        result["mask"] = mask

        # Save raster mask
        if output_raster_path is not None:
            _save_mask_to_raster(image_path, mask, output_raster_path)
            result["raster_path"] = output_raster_path
            if verbose:
                print(f"Mask saved to: {output_raster_path}")

    # Vectorize segmentation
    if need_vector:
        try:
            geojson_str = _geodeep.run(
                image_path,
                self.model_id,
                output_type="geojson",
                **run_kwargs,
            )
            gdf = self._geojson_to_geodataframe(geojson_str)
        except Exception as exc:
            raise RuntimeError(f"GeoDeep vectorization failed: {exc}") from exc

        self._save_vector(gdf, output_vector_path)
        result["gdf"] = gdf
        result["vector_path"] = output_vector_path
        if verbose:
            print(f"Vectors saved to: {output_vector_path}")

        # If mask wasn't computed above, still get it for completeness
        if "mask" not in result:
            try:
                mask = _geodeep.run(
                    image_path,
                    self.model_id,
                    output_type="raw",
                    **run_kwargs,
                )
                result["mask"] = mask
            except Exception:
                pass  # Vector output was the priority

    return result

segment_batch(image_paths, output_dir=None, output_format='raster', verbose=True)

Run semantic segmentation on multiple GeoTIFF images.

Parameters:

Name Type Description Default
image_paths list

List of paths to input GeoTIFF files.

required
output_dir str

Directory to save results.

None
output_format str

Output format — "raster" for GeoTIFF masks, "vector" for GeoPackage polygons, or "both". Defaults to "raster".

'raster'
verbose bool

Print progress information. Defaults to True.

True

Returns:

Name Type Description
list List[Dict[str, Any]]

List of result dictionaries from segment(), one per input image.

Example

gd = GeoDeep("roads") results = gd.segment_batch( ... ["tile1.tif", "tile2.tif"], ... output_dir="masks/", ... output_format="raster", ... )

Source code in geoai/geodeep.py
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
def segment_batch(
    self,
    image_paths: List[str],
    output_dir: Optional[str] = None,
    output_format: str = "raster",
    verbose: bool = True,
) -> List[Dict[str, Any]]:
    """Run semantic segmentation on multiple GeoTIFF images.

    Args:
        image_paths (list): List of paths to input GeoTIFF files.
        output_dir (str, optional): Directory to save results.
        output_format (str): Output format — ``"raster"`` for GeoTIFF
            masks, ``"vector"`` for GeoPackage polygons, or ``"both"``.
            Defaults to ``"raster"``.
        verbose (bool): Print progress information. Defaults to True.

    Returns:
        list: List of result dictionaries from ``segment()``,
            one per input image.

    Example:
        >>> gd = GeoDeep("roads")
        >>> results = gd.segment_batch(
        ...     ["tile1.tif", "tile2.tif"],
        ...     output_dir="masks/",
        ...     output_format="raster",
        ... )
    """
    if output_dir is not None:
        os.makedirs(output_dir, exist_ok=True)

    results = []
    total = len(image_paths)

    for idx, path in enumerate(image_paths, 1):
        if verbose:
            print(f"Processing [{idx}/{total}]: {path}")
        try:
            base = os.path.splitext(os.path.basename(path))[0]
            raster_out = None
            vector_out = None

            if output_dir is not None:
                if output_format in ("raster", "both"):
                    raster_out = os.path.join(output_dir, f"{base}_mask.tif")
                if output_format in ("vector", "both"):
                    vector_out = os.path.join(output_dir, f"{base}_segments.gpkg")

            result = self.segment(
                path,
                output_raster_path=raster_out,
                output_vector_path=vector_out,
                verbose=False,
            )
            result["source_file"] = path
            results.append(result)
        except Exception as exc:
            if verbose:
                print(f"  Warning: Failed on '{path}': {exc}")
            continue

    if verbose:
        print(f"Processed {len(results)}/{total} images")

    return results

check_geodeep_available()

Check if geodeep is installed.

Raises:

Type Description
ImportError

If geodeep is not installed.

Source code in geoai/geodeep.py
157
158
159
160
161
162
163
164
165
166
167
168
def check_geodeep_available() -> None:
    """Check if geodeep is installed.

    Raises:
        ImportError: If geodeep is not installed.
    """
    if not GEODEEP_AVAILABLE:
        raise ImportError(
            "geodeep is not installed. "
            "Please install it with: pip install geodeep "
            "or: pip install geoai-py[geodeep]"
        )

geodeep_detect(image_path, model_id='cars', conf_threshold=None, classes=None, output_path=None, max_threads=None, **kwargs)

Run object detection on a GeoTIFF image using GeoDeep.

Convenience function that creates a GeoDeep instance and runs detection in one call. For repeated use, instantiate GeoDeep directly to avoid repeated initialization.

Parameters:

Name Type Description Default
image_path str

Path to the input GeoTIFF file.

required
model_id str

Model identifier. Defaults to "cars".

'cars'
conf_threshold float

Confidence threshold.

None
classes list

Filter to specific class names.

None
output_path str

Path to save vector results.

None
max_threads int

Max ONNX inference threads.

None
**kwargs Any

Additional keyword arguments passed to GeoDeep.detect().

{}

Returns:

Type Description
GeoDataFrame

geopandas.GeoDataFrame: Detection results.

Example

from geoai import geodeep_detect detections = geodeep_detect("image.tif", model_id="planes")

Source code in geoai/geodeep.py
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
def geodeep_detect(
    image_path: str,
    model_id: str = "cars",
    conf_threshold: Optional[float] = None,
    classes: Optional[List[str]] = None,
    output_path: Optional[str] = None,
    max_threads: Optional[int] = None,
    **kwargs: Any,
) -> "gpd.GeoDataFrame":
    """Run object detection on a GeoTIFF image using GeoDeep.

    Convenience function that creates a ``GeoDeep`` instance and runs
    detection in one call. For repeated use, instantiate ``GeoDeep``
    directly to avoid repeated initialization.

    Args:
        image_path (str): Path to the input GeoTIFF file.
        model_id (str): Model identifier. Defaults to ``"cars"``.
        conf_threshold (float, optional): Confidence threshold.
        classes (list, optional): Filter to specific class names.
        output_path (str, optional): Path to save vector results.
        max_threads (int, optional): Max ONNX inference threads.
        **kwargs: Additional keyword arguments passed to
            ``GeoDeep.detect()``.

    Returns:
        geopandas.GeoDataFrame: Detection results.

    Example:
        >>> from geoai import geodeep_detect
        >>> detections = geodeep_detect("image.tif", model_id="planes")
    """
    gd = GeoDeep(
        model_id=model_id,
        conf_threshold=conf_threshold,
        classes=classes,
        max_threads=max_threads,
    )
    return gd.detect(
        image_path,
        conf_threshold=conf_threshold,
        classes=classes,
        output_path=output_path,
        **kwargs,
    )

geodeep_detect_batch(image_paths, model_id='cars', output_dir=None, conf_threshold=None, classes=None, max_threads=None, **kwargs)

Run object detection on multiple GeoTIFF images using GeoDeep.

Parameters:

Name Type Description Default
image_paths list

List of paths to input GeoTIFF files.

required
model_id str

Model identifier. Defaults to "cars".

'cars'
output_dir str

Directory to save per-image results.

None
conf_threshold float

Confidence threshold.

None
classes list

Filter to specific class names.

None
max_threads int

Max ONNX inference threads.

None
**kwargs Any

Additional keyword arguments passed to GeoDeep.detect_batch().

{}

Returns:

Type Description
GeoDataFrame

geopandas.GeoDataFrame: Combined detection results.

Example

from geoai import geodeep_detect_batch results = geodeep_detect_batch( ... ["img1.tif", "img2.tif"], model_id="trees" ... )

Source code in geoai/geodeep.py
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
def geodeep_detect_batch(
    image_paths: List[str],
    model_id: str = "cars",
    output_dir: Optional[str] = None,
    conf_threshold: Optional[float] = None,
    classes: Optional[List[str]] = None,
    max_threads: Optional[int] = None,
    **kwargs: Any,
) -> "gpd.GeoDataFrame":
    """Run object detection on multiple GeoTIFF images using GeoDeep.

    Args:
        image_paths (list): List of paths to input GeoTIFF files.
        model_id (str): Model identifier. Defaults to ``"cars"``.
        output_dir (str, optional): Directory to save per-image results.
        conf_threshold (float, optional): Confidence threshold.
        classes (list, optional): Filter to specific class names.
        max_threads (int, optional): Max ONNX inference threads.
        **kwargs: Additional keyword arguments passed to
            ``GeoDeep.detect_batch()``.

    Returns:
        geopandas.GeoDataFrame: Combined detection results.

    Example:
        >>> from geoai import geodeep_detect_batch
        >>> results = geodeep_detect_batch(
        ...     ["img1.tif", "img2.tif"], model_id="trees"
        ... )
    """
    gd = GeoDeep(
        model_id=model_id,
        conf_threshold=conf_threshold,
        classes=classes,
        max_threads=max_threads,
    )
    return gd.detect_batch(
        image_paths,
        output_dir=output_dir,
        conf_threshold=conf_threshold,
        classes=classes,
        **kwargs,
    )

geodeep_segment(image_path, model_id='buildings', output_raster_path=None, output_vector_path=None, max_threads=None, **kwargs)

Run semantic segmentation on a GeoTIFF image using GeoDeep.

Convenience function that creates a GeoDeep instance and runs segmentation in one call.

Parameters:

Name Type Description Default
image_path str

Path to the input GeoTIFF file.

required
model_id str

Model identifier. Defaults to "buildings".

'buildings'
output_raster_path str

Path to save mask GeoTIFF.

None
output_vector_path str

Path to save vector polygons.

None
max_threads int

Max ONNX inference threads.

None
**kwargs Any

Additional keyword arguments passed to GeoDeep.segment().

{}

Returns:

Name Type Description
dict Dict[str, Any]

Result dictionary with "mask" and optionally "gdf".

Example

from geoai import geodeep_segment result = geodeep_segment("image.tif", model_id="roads", ... output_raster_path="roads.tif")

Source code in geoai/geodeep.py
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
def geodeep_segment(
    image_path: str,
    model_id: str = "buildings",
    output_raster_path: Optional[str] = None,
    output_vector_path: Optional[str] = None,
    max_threads: Optional[int] = None,
    **kwargs: Any,
) -> Dict[str, Any]:
    """Run semantic segmentation on a GeoTIFF image using GeoDeep.

    Convenience function that creates a ``GeoDeep`` instance and runs
    segmentation in one call.

    Args:
        image_path (str): Path to the input GeoTIFF file.
        model_id (str): Model identifier. Defaults to ``"buildings"``.
        output_raster_path (str, optional): Path to save mask GeoTIFF.
        output_vector_path (str, optional): Path to save vector polygons.
        max_threads (int, optional): Max ONNX inference threads.
        **kwargs: Additional keyword arguments passed to
            ``GeoDeep.segment()``.

    Returns:
        dict: Result dictionary with ``"mask"`` and optionally ``"gdf"``.

    Example:
        >>> from geoai import geodeep_segment
        >>> result = geodeep_segment("image.tif", model_id="roads",
        ...                          output_raster_path="roads.tif")
    """
    gd = GeoDeep(model_id=model_id, max_threads=max_threads)
    return gd.segment(
        image_path,
        output_raster_path=output_raster_path,
        output_vector_path=output_vector_path,
        **kwargs,
    )

geodeep_segment_batch(image_paths, model_id='buildings', output_dir=None, output_format='raster', max_threads=None, **kwargs)

Run semantic segmentation on multiple GeoTIFF images using GeoDeep.

Parameters:

Name Type Description Default
image_paths list

List of paths to input GeoTIFF files.

required
model_id str

Model identifier. Defaults to "buildings".

'buildings'
output_dir str

Directory to save results.

None
output_format str

"raster", "vector", or "both". Defaults to "raster".

'raster'
max_threads int

Max ONNX inference threads.

None
**kwargs Any

Additional keyword arguments passed to GeoDeep.segment_batch().

{}

Returns:

Name Type Description
list List[Dict[str, Any]]

List of result dictionaries.

Example

from geoai import geodeep_segment_batch results = geodeep_segment_batch( ... ["t1.tif", "t2.tif"], ... model_id="roads", ... output_dir="masks/", ... )

Source code in geoai/geodeep.py
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
def geodeep_segment_batch(
    image_paths: List[str],
    model_id: str = "buildings",
    output_dir: Optional[str] = None,
    output_format: str = "raster",
    max_threads: Optional[int] = None,
    **kwargs: Any,
) -> List[Dict[str, Any]]:
    """Run semantic segmentation on multiple GeoTIFF images using GeoDeep.

    Args:
        image_paths (list): List of paths to input GeoTIFF files.
        model_id (str): Model identifier. Defaults to ``"buildings"``.
        output_dir (str, optional): Directory to save results.
        output_format (str): ``"raster"``, ``"vector"``, or ``"both"``.
            Defaults to ``"raster"``.
        max_threads (int, optional): Max ONNX inference threads.
        **kwargs: Additional keyword arguments passed to
            ``GeoDeep.segment_batch()``.

    Returns:
        list: List of result dictionaries.

    Example:
        >>> from geoai import geodeep_segment_batch
        >>> results = geodeep_segment_batch(
        ...     ["t1.tif", "t2.tif"],
        ...     model_id="roads",
        ...     output_dir="masks/",
        ... )
    """
    gd = GeoDeep(model_id=model_id, max_threads=max_threads)
    return gd.segment_batch(
        image_paths,
        output_dir=output_dir,
        output_format=output_format,
        **kwargs,
    )

list_geodeep_models()

List available GeoDeep built-in models.

Returns:

Type Description
Dict[str, str]

Dict[str, str]: Dictionary mapping model IDs to descriptions.

Example

from geoai import list_geodeep_models models = list_geodeep_models() for name, desc in models.items(): ... print(f"{name}: {desc}")

Source code in geoai/geodeep.py
171
172
173
174
175
176
177
178
179
180
181
182
183
def list_geodeep_models() -> Dict[str, str]:
    """List available GeoDeep built-in models.

    Returns:
        Dict[str, str]: Dictionary mapping model IDs to descriptions.

    Example:
        >>> from geoai import list_geodeep_models
        >>> models = list_geodeep_models()
        >>> for name, desc in models.items():
        ...     print(f"{name}: {desc}")
    """
    return {name: info["description"] for name, info in GEODEEP_MODELS.items()}