segment-geospatial 1.1.0__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/draft-pdf.yml +1 -1
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.pre-commit-config.yaml +1 -1
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/PKG-INFO +2 -1
- segment_geospatial-1.2.1/docs/examples/sam3_tiled_segmentation.ipynb +274 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/mkdocs.yml +1 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/pyproject.toml +2 -2
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/requirements.txt +1 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/__init__.py +1 -1
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/common.py +218 -1
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/samgeo3.py +445 -6
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/segment_geospatial.egg-info/PKG-INFO +2 -1
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/segment_geospatial.egg-info/SOURCES.txt +1 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/segment_geospatial.egg-info/requires.txt +1 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.editorconfig +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/FUNDING.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/dependabot.yaml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/docker-image.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/docker-publish.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/docs-build.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/docs.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/macos.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/pypi.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/ubuntu.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.github/workflows/windows.yml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/.gitignore +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/CITATION.cff +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/CODE_OF_CONDUCT.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/Dockerfile +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/LICENSE +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/MANIFEST.in +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/README.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/CNAME +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/assets/README.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/assets/favicon.png +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/assets/logo.png +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/assets/logo_rect.png +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/caption.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/changelog.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/changelog_update.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/common.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/contributing.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/arcgis.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/automatic_mask_generator.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/automatic_mask_generator_hq.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/box_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/data/tree_boxes.geojson +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/fast_sam.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/image_captioning.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/input_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/input_prompts_hq.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/maxar_open_data.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_automatic.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_box_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_point_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_predictor.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_text_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam2_video.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_automated_segmentation.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_batch_segmentation.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_box_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_image_segmentation.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_image_segmentation_jpg.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_interactive.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_object_tracking.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_point_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_point_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_video_masks.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_video_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/sam3_video_segmentation.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/satellite-predictor.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/satellite.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/text_prompts.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/text_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/text_swimming_pools.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/examples/tree_mapping.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/faq.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/fast_sam.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/hq_sam.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/index.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/installation.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/overrides/main.html +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/samgeo.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/samgeo2.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/samgeo3.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/text_sam.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/usage.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/workshops/AIforGood_2025.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/workshops/IPPN_2024.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/workshops/cn_workshop.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/workshops/jupytext.toml +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/docs/workshops/purdue.ipynb +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/paper/10.21105.joss.05663.pdf +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/paper/paper.bib +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/paper/paper.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/LICENSE +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/README.md +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/__init__.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/icons/icon.png +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/install_plugin.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/install_plugin.sh +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/map_tools.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/metadata.txt +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/resources.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/samgeo_plugin.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/qgis-samgeo-plugin/test_plugin.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/requirements_dev.txt +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/requirements_docs.txt +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/caption.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/fast_sam.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/fer.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/hq_sam.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/samgeo.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/samgeo2.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/text_sam.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/samgeo/utmconv.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/segment_geospatial.egg-info/dependency_links.txt +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/segment_geospatial.egg-info/top_level.txt +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/setup.cfg +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/tests/__init__.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/tests/test_common.py +0 -0
- {segment_geospatial-1.1.0 → segment_geospatial-1.2.1}/tests/test_samgeo.py +0 -0
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
# This should be the path to the paper within your repo.
|
|
15
15
|
paper-path: paper/paper.md
|
|
16
16
|
- name: Upload
|
|
17
|
-
uses: actions/upload-artifact@
|
|
17
|
+
uses: actions/upload-artifact@v6
|
|
18
18
|
with:
|
|
19
19
|
name: paper
|
|
20
20
|
# This is the output path where Pandoc will write the compiled
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: segment-geospatial
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Meta AI' Segment Anything Model (SAM) for Geospatial Data.
|
|
5
5
|
Author-email: Qiusheng Wu <giswqs@gmail.com>
|
|
6
6
|
License: MIT license
|
|
@@ -29,6 +29,7 @@ Requires-Dist: pyproj
|
|
|
29
29
|
Requires-Dist: rasterio
|
|
30
30
|
Requires-Dist: segment_anything
|
|
31
31
|
Requires-Dist: shapely
|
|
32
|
+
Requires-Dist: smoothify
|
|
32
33
|
Requires-Dist: torch
|
|
33
34
|
Requires-Dist: torchvision
|
|
34
35
|
Requires-Dist: tqdm
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Tiled Segmentation for Large GeoTIFF Images with SAM 3\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"[](https://colab.research.google.com/github/opengeos/segment-geospatial/blob/main/docs/examples/sam3_tiled_segmentation.ipynb)\n",
|
|
10
|
+
"\n",
|
|
11
|
+
"This notebook demonstrates how to use the sliding window (tiled) approach to segment large GeoTIFF images that would otherwise exceed GPU memory limits.\n",
|
|
12
|
+
"\n",
|
|
13
|
+
"## Overview\n",
|
|
14
|
+
"\n",
|
|
15
|
+
"When working with large satellite or aerial imagery, loading the entire image into GPU memory for SAM 3 inference is often not feasible. The `generate_masks_tiled()` method solves this by:\n",
|
|
16
|
+
"\n",
|
|
17
|
+
"1. **Dividing** the large image into smaller, overlapping tiles\n",
|
|
18
|
+
"2. **Processing** each tile independently with SAM 3\n",
|
|
19
|
+
"3. **Merging** the results back into a seamless output mask\n",
|
|
20
|
+
"4. **Preserving** georeferencing information from the original GeoTIFF\n",
|
|
21
|
+
"\n",
|
|
22
|
+
"## Key Parameters\n",
|
|
23
|
+
"\n",
|
|
24
|
+
"- **tile_size**: Size of each processing tile (default: 1024 pixels)\n",
|
|
25
|
+
"- **overlap**: Overlap between adjacent tiles (default: 128 pixels) - helps prevent edge artifacts\n",
|
|
26
|
+
"- **prompt**: Text description of objects to segment\n",
|
|
27
|
+
"- **min_size/max_size**: Filter objects by pixel area"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"cell_type": "markdown",
|
|
32
|
+
"metadata": {},
|
|
33
|
+
"source": [
|
|
34
|
+
"## Installation\n",
|
|
35
|
+
"\n",
|
|
36
|
+
"Uncomment and run the following cell to install the required packages:"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"cell_type": "code",
|
|
41
|
+
"execution_count": null,
|
|
42
|
+
"metadata": {},
|
|
43
|
+
"outputs": [],
|
|
44
|
+
"source": [
|
|
45
|
+
"# %pip install \"segment-geospatial[samgeo3]\""
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"cell_type": "markdown",
|
|
50
|
+
"metadata": {},
|
|
51
|
+
"source": [
|
|
52
|
+
"## Import Libraries"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"cell_type": "code",
|
|
57
|
+
"execution_count": null,
|
|
58
|
+
"metadata": {},
|
|
59
|
+
"outputs": [],
|
|
60
|
+
"source": [
|
|
61
|
+
"import os\n",
|
|
62
|
+
"import leafmap\n",
|
|
63
|
+
"from samgeo import SamGeo3, common"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"cell_type": "markdown",
|
|
68
|
+
"metadata": {},
|
|
69
|
+
"source": [
|
|
70
|
+
"## Download Sample Data\n",
|
|
71
|
+
"\n",
|
|
72
|
+
"We'll use a sample satellite image for this demonstration. You can replace this with your own large GeoTIFF."
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"cell_type": "code",
|
|
77
|
+
"execution_count": null,
|
|
78
|
+
"metadata": {},
|
|
79
|
+
"outputs": [],
|
|
80
|
+
"source": [
|
|
81
|
+
"# Download a sample satellite image\n",
|
|
82
|
+
"url = \"https://huggingface.co/datasets/giswqs/geospatial/resolve/main/naip/naip_water_train.tif\"\n",
|
|
83
|
+
"image_path = \"naip_water_train.tif\"\n",
|
|
84
|
+
"\n",
|
|
85
|
+
"if not os.path.exists(image_path):\n",
|
|
86
|
+
" common.download_file(url, image_path)"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"cell_type": "markdown",
|
|
91
|
+
"metadata": {},
|
|
92
|
+
"source": [
|
|
93
|
+
"## Check Raster Info\n",
|
|
94
|
+
"\n",
|
|
95
|
+
"Let's check the dimensions of our image to understand why tiling might be necessary."
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"cell_type": "code",
|
|
100
|
+
"execution_count": null,
|
|
101
|
+
"metadata": {},
|
|
102
|
+
"outputs": [],
|
|
103
|
+
"source": [
|
|
104
|
+
"common.print_raster_info(image_path)"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"cell_type": "markdown",
|
|
109
|
+
"metadata": {},
|
|
110
|
+
"source": [
|
|
111
|
+
"## Initialize SamGeo3"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"cell_type": "code",
|
|
116
|
+
"execution_count": null,
|
|
117
|
+
"metadata": {},
|
|
118
|
+
"outputs": [],
|
|
119
|
+
"source": [
|
|
120
|
+
"sam = SamGeo3(backend=\"meta\")"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"cell_type": "markdown",
|
|
125
|
+
"metadata": {},
|
|
126
|
+
"source": [
|
|
127
|
+
"## Run Tiled Segmentation\n",
|
|
128
|
+
"\n",
|
|
129
|
+
"Now we'll use the `generate_masks_tiled()` method to process the image. This method:\n",
|
|
130
|
+
"\n",
|
|
131
|
+
"1. Reads the image tile by tile\n",
|
|
132
|
+
"2. Processes each tile with SAM3\n",
|
|
133
|
+
"3. Merges overlapping regions intelligently\n",
|
|
134
|
+
"4. Saves the result as a georeferenced GeoTIFF"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"cell_type": "code",
|
|
139
|
+
"execution_count": null,
|
|
140
|
+
"metadata": {},
|
|
141
|
+
"outputs": [],
|
|
142
|
+
"source": [
|
|
143
|
+
"# Output path for the mask\n",
|
|
144
|
+
"output_path = \"segmentation_mask.tif\"\n",
|
|
145
|
+
"\n",
|
|
146
|
+
"# Run tiled segmentation\n",
|
|
147
|
+
"sam.generate_masks_tiled(\n",
|
|
148
|
+
" source=image_path,\n",
|
|
149
|
+
" prompt=\"water\", # Change prompt based on what you want to segment\n",
|
|
150
|
+
" output=output_path,\n",
|
|
151
|
+
" tile_size=1024, # Size of each tile (adjust based on GPU memory)\n",
|
|
152
|
+
" overlap=128, # Overlap between tiles\n",
|
|
153
|
+
" min_size=100, # Minimum object size in pixels\n",
|
|
154
|
+
" unique=False, # Create binary mask (0 or 1)\n",
|
|
155
|
+
" dtype=\"int32\", # Data type for output\n",
|
|
156
|
+
" verbose=True, # Show progress\n",
|
|
157
|
+
")"
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"cell_type": "markdown",
|
|
162
|
+
"metadata": {},
|
|
163
|
+
"source": [
|
|
164
|
+
"## Visualize Results"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"cell_type": "code",
|
|
169
|
+
"execution_count": null,
|
|
170
|
+
"metadata": {},
|
|
171
|
+
"outputs": [],
|
|
172
|
+
"source": [
|
|
173
|
+
"m = leafmap.Map()\n",
|
|
174
|
+
"m.add_raster(image_path, layer_name=\"Original Image\")\n",
|
|
175
|
+
"m.add_raster(\n",
|
|
176
|
+
" output_path, nodata=0, opacity=0.8, cmap=\"Blues\", layer_name=\"Segmentation Mask\"\n",
|
|
177
|
+
")\n",
|
|
178
|
+
"m"
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"cell_type": "markdown",
|
|
183
|
+
"metadata": {},
|
|
184
|
+
"source": [
|
|
185
|
+
"## Convert Mask to Vector\n",
|
|
186
|
+
"\n",
|
|
187
|
+
"You can convert the raster mask to vector format (GeoPackage, Shapefile, etc.) for further analysis in GIS software."
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"cell_type": "code",
|
|
192
|
+
"execution_count": null,
|
|
193
|
+
"metadata": {},
|
|
194
|
+
"outputs": [],
|
|
195
|
+
"source": [
|
|
196
|
+
"# Convert mask to vector\n",
|
|
197
|
+
"vector_path = \"segmentation_mask.gpkg\"\n",
|
|
198
|
+
"common.raster_to_vector(output_path, vector_path)"
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"cell_type": "markdown",
|
|
203
|
+
"metadata": {},
|
|
204
|
+
"source": [
|
|
205
|
+
"## Smooth Vector"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"cell_type": "code",
|
|
210
|
+
"execution_count": null,
|
|
211
|
+
"metadata": {},
|
|
212
|
+
"outputs": [],
|
|
213
|
+
"source": [
|
|
214
|
+
"smooth_vector_path = \"segmentation_mask_smooth.gpkg\"\n",
|
|
215
|
+
"gdf = common.smooth_vector(vector_path, smooth_vector_path)"
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"cell_type": "code",
|
|
220
|
+
"execution_count": null,
|
|
221
|
+
"metadata": {},
|
|
222
|
+
"outputs": [],
|
|
223
|
+
"source": [
|
|
224
|
+
"m.add_gdf(gdf, layer_name=\"Smoothed Vector\", info_mode=None)\n",
|
|
225
|
+
"m"
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"cell_type": "code",
|
|
230
|
+
"execution_count": null,
|
|
231
|
+
"metadata": {},
|
|
232
|
+
"outputs": [],
|
|
233
|
+
"source": []
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"cell_type": "markdown",
|
|
237
|
+
"metadata": {},
|
|
238
|
+
"source": [
|
|
239
|
+
"## Tips for Processing Large Images\n",
|
|
240
|
+
"\n",
|
|
241
|
+
"1. **Tile Size**: Larger tiles capture more context but require more GPU memory. Start with 512 or 1024 and increase if you have sufficient GPU memory.\n",
|
|
242
|
+
"\n",
|
|
243
|
+
"2. **Overlap**: Higher overlap (e.g., 128-256) helps prevent artifacts at tile boundaries but increases processing time. Lower overlap (e.g., 64) is faster but may have more edge effects.\n",
|
|
244
|
+
"\n",
|
|
245
|
+
"3. **Memory Management**: The method automatically clears GPU memory after each tile. If you still encounter memory issues, try reducing the tile_size.\n",
|
|
246
|
+
"\n",
|
|
247
|
+
"4. **Data Type**: Use `int32` for images with many objects, `int16` for up to 65535 objects, or `int8` for up to 255 objects.\n",
|
|
248
|
+
"\n",
|
|
249
|
+
"5. **Filtering**: Use `min_size` and `max_size` to filter out noise (small objects) or irrelevant large regions."
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
],
|
|
253
|
+
"metadata": {
|
|
254
|
+
"kernelspec": {
|
|
255
|
+
"display_name": "geo",
|
|
256
|
+
"language": "python",
|
|
257
|
+
"name": "python3"
|
|
258
|
+
},
|
|
259
|
+
"language_info": {
|
|
260
|
+
"codemirror_mode": {
|
|
261
|
+
"name": "ipython",
|
|
262
|
+
"version": 3
|
|
263
|
+
},
|
|
264
|
+
"file_extension": ".py",
|
|
265
|
+
"mimetype": "text/x-python",
|
|
266
|
+
"name": "python",
|
|
267
|
+
"nbconvert_exporter": "python",
|
|
268
|
+
"pygments_lexer": "ipython3",
|
|
269
|
+
"version": "3.12.2"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"nbformat": 4,
|
|
273
|
+
"nbformat_minor": 4
|
|
274
|
+
}
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "segment-geospatial"
|
|
7
|
-
version = "1.1
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
dynamic = [
|
|
9
9
|
"dependencies",
|
|
10
10
|
]
|
|
@@ -111,7 +111,7 @@ dependencies = {file = ["requirements.txt"]}
|
|
|
111
111
|
universal = true
|
|
112
112
|
|
|
113
113
|
[tool.bumpversion]
|
|
114
|
-
current_version = "1.1
|
|
114
|
+
current_version = "1.2.1"
|
|
115
115
|
commit = true
|
|
116
116
|
tag = true
|
|
117
117
|
|
|
@@ -4,7 +4,7 @@ The source code is adapted from https://github.com/aliaksandr960/segment-anythin
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import tempfile
|
|
7
|
-
from typing import Any, List, Optional, Tuple, Union
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
8
|
|
|
9
9
|
import cv2
|
|
10
10
|
import geopandas as gpd
|
|
@@ -4055,3 +4055,220 @@ def get_device() -> torch.device:
|
|
|
4055
4055
|
return torch.device("mps")
|
|
4056
4056
|
else:
|
|
4057
4057
|
return torch.device("cpu")
|
|
4058
|
+
|
|
4059
|
+
|
|
4060
|
+
def get_raster_info(raster_path: str) -> Dict[str, Any]:
|
|
4061
|
+
"""Display basic information about a raster dataset.
|
|
4062
|
+
|
|
4063
|
+
Args:
|
|
4064
|
+
raster_path (str): Path to the raster file
|
|
4065
|
+
|
|
4066
|
+
Returns:
|
|
4067
|
+
dict: Dictionary containing the basic information about the raster
|
|
4068
|
+
"""
|
|
4069
|
+
# Open the raster dataset
|
|
4070
|
+
with rasterio.open(raster_path) as src:
|
|
4071
|
+
# Get basic metadata
|
|
4072
|
+
info = {
|
|
4073
|
+
"driver": src.driver,
|
|
4074
|
+
"width": src.width,
|
|
4075
|
+
"height": src.height,
|
|
4076
|
+
"count": src.count,
|
|
4077
|
+
"dtype": src.dtypes[0],
|
|
4078
|
+
"crs": src.crs.to_string() if src.crs else "No CRS defined",
|
|
4079
|
+
"transform": src.transform,
|
|
4080
|
+
"bounds": src.bounds,
|
|
4081
|
+
"resolution": (src.transform[0], -src.transform[4]),
|
|
4082
|
+
"nodata": src.nodata,
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
# Calculate statistics for each band
|
|
4086
|
+
stats = []
|
|
4087
|
+
for i in range(1, src.count + 1):
|
|
4088
|
+
band = src.read(i, masked=True)
|
|
4089
|
+
band_stats = {
|
|
4090
|
+
"band": i,
|
|
4091
|
+
"min": float(band.min()),
|
|
4092
|
+
"max": float(band.max()),
|
|
4093
|
+
"mean": float(band.mean()),
|
|
4094
|
+
"std": float(band.std()),
|
|
4095
|
+
}
|
|
4096
|
+
stats.append(band_stats)
|
|
4097
|
+
|
|
4098
|
+
info["band_stats"] = stats
|
|
4099
|
+
|
|
4100
|
+
return info
|
|
4101
|
+
|
|
4102
|
+
|
|
4103
|
+
def get_raster_stats(raster_path: str, divide_by: float = 1.0) -> Dict[str, Any]:
|
|
4104
|
+
"""Calculate statistics for each band in a raster dataset.
|
|
4105
|
+
|
|
4106
|
+
This function computes min, max, mean, and standard deviation values
|
|
4107
|
+
for each band in the provided raster, returning results in a dictionary
|
|
4108
|
+
with lists for each statistic type.
|
|
4109
|
+
|
|
4110
|
+
Args:
|
|
4111
|
+
raster_path (str): Path to the raster file
|
|
4112
|
+
divide_by (float, optional): Value to divide pixel values by.
|
|
4113
|
+
Defaults to 1.0, which keeps the original pixel values unchanged.
|
|
4114
|
+
|
|
4115
|
+
Returns:
|
|
4116
|
+
dict: Dictionary containing lists of statistics with keys:
|
|
4117
|
+
- 'min': List of minimum values for each band
|
|
4118
|
+
- 'max': List of maximum values for each band
|
|
4119
|
+
- 'mean': List of mean values for each band
|
|
4120
|
+
- 'std': List of standard deviation values for each band
|
|
4121
|
+
"""
|
|
4122
|
+
# Initialize the results dictionary with empty lists
|
|
4123
|
+
stats = {"min": [], "max": [], "mean": [], "std": []}
|
|
4124
|
+
|
|
4125
|
+
# Open the raster dataset
|
|
4126
|
+
with rasterio.open(raster_path) as src:
|
|
4127
|
+
# Calculate statistics for each band
|
|
4128
|
+
for i in range(1, src.count + 1):
|
|
4129
|
+
band = src.read(i, masked=True)
|
|
4130
|
+
|
|
4131
|
+
# Append statistics for this band to each list
|
|
4132
|
+
stats["min"].append(float(band.min()) / divide_by)
|
|
4133
|
+
stats["max"].append(float(band.max()) / divide_by)
|
|
4134
|
+
stats["mean"].append(float(band.mean()) / divide_by)
|
|
4135
|
+
stats["std"].append(float(band.std()) / divide_by)
|
|
4136
|
+
|
|
4137
|
+
return stats
|
|
4138
|
+
|
|
4139
|
+
|
|
4140
|
+
def print_raster_info(
|
|
4141
|
+
raster_path: str, show_preview: bool = True, figsize: Tuple[int, int] = (10, 8)
|
|
4142
|
+
) -> Optional[Dict[str, Any]]:
|
|
4143
|
+
"""Print formatted information about a raster dataset and optionally show a preview.
|
|
4144
|
+
|
|
4145
|
+
Args:
|
|
4146
|
+
raster_path (str): Path to the raster file
|
|
4147
|
+
show_preview (bool, optional): Whether to display a visual preview of the raster.
|
|
4148
|
+
Defaults to True.
|
|
4149
|
+
figsize (tuple, optional): Figure size as (width, height). Defaults to (10, 8).
|
|
4150
|
+
|
|
4151
|
+
Returns:
|
|
4152
|
+
dict: Dictionary containing raster information if successful, None otherwise
|
|
4153
|
+
"""
|
|
4154
|
+
import matplotlib.pyplot as plt
|
|
4155
|
+
from rasterio.plot import show
|
|
4156
|
+
|
|
4157
|
+
try:
|
|
4158
|
+
info = get_raster_info(raster_path)
|
|
4159
|
+
|
|
4160
|
+
# Print basic information
|
|
4161
|
+
print(f"===== RASTER INFORMATION: {raster_path} =====")
|
|
4162
|
+
print(f"Driver: {info['driver']}")
|
|
4163
|
+
print(f"Dimensions: {info['width']} x {info['height']} pixels")
|
|
4164
|
+
print(f"Number of bands: {info['count']}")
|
|
4165
|
+
print(f"Data type: {info['dtype']}")
|
|
4166
|
+
print(f"Coordinate Reference System: {info['crs']}")
|
|
4167
|
+
print(f"Georeferenced Bounds: {info['bounds']}")
|
|
4168
|
+
print(f"Pixel Resolution: {info['resolution'][0]}, {info['resolution'][1]}")
|
|
4169
|
+
print(f"NoData Value: {info['nodata']}")
|
|
4170
|
+
|
|
4171
|
+
# Print band statistics
|
|
4172
|
+
print("\n----- Band Statistics -----")
|
|
4173
|
+
for band_stat in info["band_stats"]:
|
|
4174
|
+
print(f"Band {band_stat['band']}:")
|
|
4175
|
+
print(f" Min: {band_stat['min']:.2f}")
|
|
4176
|
+
print(f" Max: {band_stat['max']:.2f}")
|
|
4177
|
+
print(f" Mean: {band_stat['mean']:.2f}")
|
|
4178
|
+
print(f" Std Dev: {band_stat['std']:.2f}")
|
|
4179
|
+
|
|
4180
|
+
# Show a preview if requested
|
|
4181
|
+
if show_preview:
|
|
4182
|
+
with rasterio.open(raster_path) as src:
|
|
4183
|
+
# For multi-band images, show RGB composite or first band
|
|
4184
|
+
if src.count >= 3:
|
|
4185
|
+
# Try to show RGB composite
|
|
4186
|
+
rgb = np.dstack([src.read(i) for i in range(1, 4)])
|
|
4187
|
+
plt.figure(figsize=figsize)
|
|
4188
|
+
plt.imshow(rgb)
|
|
4189
|
+
plt.title(f"RGB Preview: {raster_path}")
|
|
4190
|
+
else:
|
|
4191
|
+
# Show first band for single-band images
|
|
4192
|
+
plt.figure(figsize=figsize)
|
|
4193
|
+
show(
|
|
4194
|
+
src.read(1),
|
|
4195
|
+
cmap="viridis",
|
|
4196
|
+
title=f"Band 1 Preview: {raster_path}",
|
|
4197
|
+
)
|
|
4198
|
+
plt.colorbar(label="Pixel Value")
|
|
4199
|
+
plt.show()
|
|
4200
|
+
|
|
4201
|
+
return info
|
|
4202
|
+
|
|
4203
|
+
except Exception as e:
|
|
4204
|
+
print(f"Error reading raster: {str(e)}")
|
|
4205
|
+
return None
|
|
4206
|
+
|
|
4207
|
+
|
|
4208
|
+
def smooth_vector(
|
|
4209
|
+
vector_data: Union[str, gpd.GeoDataFrame],
|
|
4210
|
+
output_path: str = None,
|
|
4211
|
+
segment_length: float = None,
|
|
4212
|
+
smooth_iterations: int = 3,
|
|
4213
|
+
num_cores: int = 0,
|
|
4214
|
+
merge_collection: bool = True,
|
|
4215
|
+
merge_field: str = None,
|
|
4216
|
+
merge_multipolygons: bool = True,
|
|
4217
|
+
preserve_area: bool = True,
|
|
4218
|
+
area_tolerance: float = 0.01,
|
|
4219
|
+
**kwargs: Any,
|
|
4220
|
+
) -> gpd.GeoDataFrame:
|
|
4221
|
+
"""Smooth a vector data using the smoothify library.
|
|
4222
|
+
See https://github.com/DPIRD-DMA/Smoothify for more details.
|
|
4223
|
+
|
|
4224
|
+
Args:
|
|
4225
|
+
vector_data: The vector data to smooth.
|
|
4226
|
+
output_path: The path to save the smoothed vector data. If None, returns the smoothed vector data.
|
|
4227
|
+
segment_length: Resolution of the original raster data in map units. If None (default), automatically
|
|
4228
|
+
detects by finding the minimum segment length (from a data sample). Recommended to specify explicitly when known.
|
|
4229
|
+
smooth_iterations: The number of iterations to smooth the vector data.
|
|
4230
|
+
num_cores: Number of cores to use for parallel processing. If 0 (default), uses all available cores.
|
|
4231
|
+
merge_collection: Whether to merge/dissolve adjacent geometries in collections before smoothing.
|
|
4232
|
+
merge_field: Column name to use for dissolving geometries. Only valid when merge_collection=True.
|
|
4233
|
+
If None, dissolves all geometries together. If specified, dissolves geometries grouped by the column values.
|
|
4234
|
+
merge_multipolygons: Whether to merge adjacent polygons within MultiPolygons before smoothing
|
|
4235
|
+
preserve_area: Whether to restore original area after smoothing via buffering (applies to Polygons only)
|
|
4236
|
+
area_tolerance: Percentage of original area allowed as error (e.g., 0.01 = 0.01% error = 99.99% preservation).
|
|
4237
|
+
Only affects Polygons when preserve_area=True
|
|
4238
|
+
|
|
4239
|
+
Returns:
|
|
4240
|
+
gpd.GeoDataFrame: The smoothed vector data.
|
|
4241
|
+
|
|
4242
|
+
Examples:
|
|
4243
|
+
>>> from samgeo import common
|
|
4244
|
+
>>> gdf = common.read_vector("path/to/vector.geojson")
|
|
4245
|
+
>>> smoothed_gdf = common.smooth_vector(gdf, smooth_iterations=3, output_path="path/to/smoothed_vector.geojson")
|
|
4246
|
+
>>> smoothed_gdf.head()
|
|
4247
|
+
>>> smoothed_gdf.explore()
|
|
4248
|
+
"""
|
|
4249
|
+
import leafmap
|
|
4250
|
+
|
|
4251
|
+
try:
|
|
4252
|
+
from smoothify import smoothify
|
|
4253
|
+
except ImportError:
|
|
4254
|
+
install_package("smoothify")
|
|
4255
|
+
from smoothify import smoothify
|
|
4256
|
+
|
|
4257
|
+
if isinstance(vector_data, str):
|
|
4258
|
+
vector_data = leafmap.read_vector(vector_data)
|
|
4259
|
+
|
|
4260
|
+
smoothed_vector_data = smoothify(
|
|
4261
|
+
geom=vector_data,
|
|
4262
|
+
segment_length=segment_length,
|
|
4263
|
+
smooth_iterations=smooth_iterations,
|
|
4264
|
+
num_cores=num_cores,
|
|
4265
|
+
merge_collection=merge_collection,
|
|
4266
|
+
merge_field=merge_field,
|
|
4267
|
+
merge_multipolygons=merge_multipolygons,
|
|
4268
|
+
preserve_area=preserve_area,
|
|
4269
|
+
area_tolerance=area_tolerance,
|
|
4270
|
+
**kwargs,
|
|
4271
|
+
)
|
|
4272
|
+
if output_path is not None:
|
|
4273
|
+
smoothed_vector_data.to_file(output_path)
|
|
4274
|
+
return smoothed_vector_data
|