segment-geospatial 1.0.3__tar.gz → 1.2.0__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.0.3 → segment_geospatial-1.2.0}/.github/workflows/draft-pdf.yml +1 -1
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/ubuntu.yml +5 -3
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.pre-commit-config.yaml +1 -1
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/PKG-INFO +2 -1
- segment_geospatial-1.2.0/docs/examples/sam3_tiled_segmentation.ipynb +274 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/mkdocs.yml +1 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/pyproject.toml +2 -2
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/requirements.txt +1 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/__init__.py +1 -1
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/common.py +260 -13
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/samgeo3.py +492 -9
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/segment_geospatial.egg-info/PKG-INFO +2 -1
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/segment_geospatial.egg-info/SOURCES.txt +1 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/segment_geospatial.egg-info/requires.txt +1 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.editorconfig +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/FUNDING.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/dependabot.yaml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/docker-image.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/docker-publish.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/docs-build.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/docs.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/macos.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/pypi.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.github/workflows/windows.yml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/.gitignore +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/CITATION.cff +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/CODE_OF_CONDUCT.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/Dockerfile +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/LICENSE +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/MANIFEST.in +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/README.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/CNAME +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/assets/README.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/assets/favicon.png +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/assets/logo.png +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/assets/logo_rect.png +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/caption.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/changelog.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/changelog_update.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/common.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/contributing.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/arcgis.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/automatic_mask_generator.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/automatic_mask_generator_hq.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/box_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/data/tree_boxes.geojson +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/fast_sam.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/image_captioning.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/input_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/input_prompts_hq.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/maxar_open_data.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_automatic.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_box_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_point_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_predictor.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_text_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam2_video.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_automated_segmentation.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_batch_segmentation.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_box_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_image_segmentation.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_image_segmentation_jpg.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_interactive.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_object_tracking.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_point_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_point_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_video_masks.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_video_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/sam3_video_segmentation.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/satellite-predictor.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/satellite.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/text_prompts.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/text_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/text_swimming_pools.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/examples/tree_mapping.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/faq.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/fast_sam.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/hq_sam.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/index.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/installation.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/overrides/main.html +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/samgeo.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/samgeo2.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/samgeo3.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/text_sam.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/usage.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/workshops/AIforGood_2025.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/workshops/IPPN_2024.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/workshops/cn_workshop.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/workshops/jupytext.toml +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/docs/workshops/purdue.ipynb +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/paper/10.21105.joss.05663.pdf +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/paper/paper.bib +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/paper/paper.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/LICENSE +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/README.md +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/__init__.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/icons/icon.png +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/install_plugin.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/install_plugin.sh +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/map_tools.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/metadata.txt +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/resources.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/samgeo_plugin.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/qgis-samgeo-plugin/test_plugin.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/requirements_dev.txt +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/requirements_docs.txt +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/caption.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/fast_sam.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/fer.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/hq_sam.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/samgeo.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/samgeo2.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/text_sam.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/samgeo/utmconv.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/segment_geospatial.egg-info/dependency_links.txt +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/segment_geospatial.egg-info/top_level.txt +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/setup.cfg +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/tests/__init__.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/tests/test_common.py +0 -0
- {segment_geospatial-1.0.3 → segment_geospatial-1.2.0}/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
|
|
@@ -48,6 +48,8 @@ jobs:
|
|
|
48
48
|
pip install --user -r requirements.txt
|
|
49
49
|
pip install --user -r requirements_dev.txt
|
|
50
50
|
pip install --user .
|
|
51
|
-
- name:
|
|
52
|
-
run:
|
|
53
|
-
|
|
51
|
+
- name: Test import
|
|
52
|
+
run: python -c "import samgeo; print('samgeo import successful')"
|
|
53
|
+
# - name: PKG-TEST
|
|
54
|
+
# run: |
|
|
55
|
+
# python -m unittest discover tests/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: segment-geospatial
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
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.0
|
|
7
|
+
version = "1.2.0"
|
|
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.0
|
|
114
|
+
current_version = "1.2.0"
|
|
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
|
|
@@ -3737,27 +3737,50 @@ def make_temp_dir(**kwargs) -> str:
|
|
|
3737
3737
|
return temp_dir
|
|
3738
3738
|
|
|
3739
3739
|
|
|
3740
|
-
def geotiff_to_jpg(
|
|
3740
|
+
def geotiff_to_jpg(
|
|
3741
|
+
geotiff_path: str,
|
|
3742
|
+
output_path: str,
|
|
3743
|
+
bands: Optional[List[int]] = None,
|
|
3744
|
+
) -> None:
|
|
3741
3745
|
"""Convert a GeoTIFF file to a JPG file.
|
|
3742
3746
|
|
|
3743
3747
|
Args:
|
|
3744
3748
|
geotiff_path (str): The path to the input GeoTIFF file.
|
|
3745
3749
|
output_path (str): The path to the output JPG file.
|
|
3750
|
+
bands (List[int], optional): List of band indices (1-based) to use for RGB.
|
|
3751
|
+
For example, [4, 3, 2] for NIR-R-G false color composite.
|
|
3752
|
+
If None, uses the first 3 bands. Defaults to None.
|
|
3746
3753
|
"""
|
|
3747
3754
|
|
|
3748
3755
|
from PIL import Image
|
|
3749
3756
|
|
|
3750
3757
|
# Open the GeoTIFF file
|
|
3751
3758
|
with rasterio.open(geotiff_path) as src:
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3759
|
+
if bands is not None:
|
|
3760
|
+
# Validate band indices (1-based)
|
|
3761
|
+
if len(bands) != 3:
|
|
3762
|
+
raise ValueError("bands must contain exactly 3 band indices for RGB.")
|
|
3763
|
+
for band in bands:
|
|
3764
|
+
if band < 1 or band > src.count:
|
|
3765
|
+
raise ValueError(
|
|
3766
|
+
f"Band index {band} is out of range. "
|
|
3767
|
+
f"Image has {src.count} bands (1-indexed)."
|
|
3768
|
+
)
|
|
3769
|
+
# Read specified bands (rasterio uses 1-based indexing)
|
|
3770
|
+
array = np.stack([src.read(b) for b in bands], axis=0)
|
|
3771
|
+
else:
|
|
3772
|
+
# Read all bands
|
|
3773
|
+
array = src.read()
|
|
3774
|
+
|
|
3775
|
+
# If the array has more than 3 bands, reduce it to the first 3 (RGB)
|
|
3776
|
+
if array.shape[0] >= 3:
|
|
3777
|
+
array = array[:3, :, :] # Select the first 3 bands (R, G, B)
|
|
3778
|
+
elif array.shape[0] == 1:
|
|
3779
|
+
# For single-band images, repeat the band to create a grayscale RGB
|
|
3780
|
+
array = np.repeat(array, 3, axis=0)
|
|
3781
|
+
elif array.shape[0] == 2:
|
|
3782
|
+
# For two-band images, repeat the first band to create a 3-band image
|
|
3783
|
+
array = np.concatenate([array, array[0:1, :, :]], axis=0)
|
|
3761
3784
|
|
|
3762
3785
|
# Transpose the array from (bands, height, width) to (height, width, bands)
|
|
3763
3786
|
array = np.transpose(array, (1, 2, 0))
|
|
@@ -3774,12 +3797,19 @@ def geotiff_to_jpg(geotiff_path: str, output_path: str) -> None:
|
|
|
3774
3797
|
image.save(output_path)
|
|
3775
3798
|
|
|
3776
3799
|
|
|
3777
|
-
def geotiff_to_jpg_batch(
|
|
3800
|
+
def geotiff_to_jpg_batch(
|
|
3801
|
+
input_folder: str,
|
|
3802
|
+
output_folder: str = None,
|
|
3803
|
+
bands: Optional[List[int]] = None,
|
|
3804
|
+
) -> str:
|
|
3778
3805
|
"""Convert all GeoTIFF files in a folder to JPG files.
|
|
3779
3806
|
|
|
3780
3807
|
Args:
|
|
3781
3808
|
input_folder (str): The path to the folder containing GeoTIFF files.
|
|
3782
3809
|
output_folder (str): The path to the folder to save the output JPG files.
|
|
3810
|
+
bands (List[int], optional): List of band indices (1-based) to use for RGB.
|
|
3811
|
+
For example, [4, 3, 2] for NIR-R-G false color composite.
|
|
3812
|
+
If None, uses the first 3 bands. Defaults to None.
|
|
3783
3813
|
|
|
3784
3814
|
Returns:
|
|
3785
3815
|
str: The path to the output folder containing the JPG files.
|
|
@@ -3800,7 +3830,7 @@ def geotiff_to_jpg_batch(input_folder: str, output_folder: str = None) -> str:
|
|
|
3800
3830
|
geotiff_path = os.path.join(input_folder, filename)
|
|
3801
3831
|
jpg_filename = os.path.splitext(filename)[0] + ".jpg"
|
|
3802
3832
|
output_path = os.path.join(output_folder, jpg_filename)
|
|
3803
|
-
geotiff_to_jpg(geotiff_path, output_path)
|
|
3833
|
+
geotiff_to_jpg(geotiff_path, output_path, bands=bands)
|
|
3804
3834
|
|
|
3805
3835
|
return output_folder
|
|
3806
3836
|
|
|
@@ -4025,3 +4055,220 @@ def get_device() -> torch.device:
|
|
|
4025
4055
|
return torch.device("mps")
|
|
4026
4056
|
else:
|
|
4027
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
|