Skip to content

Commit a187b2e

Browse files
authored
New features for v0.4.0 (#23)
* New features for v0.4.0 * Added automatic mask generator example * Updated README * Improved docs * Improved array_to_image function * Reformatted code * Added blend_images function * Added docstrings
1 parent 42f3fd9 commit a187b2e

File tree

9 files changed

+960
-112
lines changed

9 files changed

+960
-112
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ private/
1010
**/*.shp
1111
**/*.shx
1212
**/*.dbf
13+
**/*.jpg
14+
**/*.png
1315

1416
# C extensions
1517
*.so
@@ -110,4 +112,6 @@ ENV/
110112
.mypy_cache/
111113

112114
# IDE settings
113-
.vscode/
115+
.vscode/
116+
docs/examples/segment.cpg
117+
docs/examples/segment.prj

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@
1111
The **segment-geospatial** package draws its inspiration from [segment-anything-eo](https://github.com/aliaksandr960/segment-anything-eo) repository authored by [Aliaksandr Hancharenka](https://github.com/aliaksandr960). To facilitate the use of the Segment Anything Model (SAM) for geospatial data, I have developed the [segment-anything-py](https://github.com/opengeos/segment-anything) and [segment-geospatial](https://github.com/opengeos/segment-geospatial) Python packages, which are now available on PyPI and conda-forge. My primary objective is to simplify the process of leveraging SAM for geospatial data analysis by enabling users to achieve this with minimal coding effort. I have adapted the source code of segment-geospatial from the [segment-anything-eo](https://github.com/aliaksandr960/segment-anything-eo) repository, and credit for its original version goes to Aliaksandr Hancharenka.
1212

1313
- Free software: MIT license
14-
- Documentation: https://samgeo.gishub.org
14+
- Documentation: <https://samgeo.gishub.org>
1515

1616
## Features
1717

1818
- Download map tiles from Tile Map Service (TMS) servers and create GeoTIFF files
1919
- Segment GeoTIFF files using the Segment Anything Model (SAM)
2020
- Save segmentation results as common vector formats (GeoPackage, Shapefile, GeoJSON, etc.)
21+
- Visualize segmentation results on interactive maps
2122

2223
## Examples
2324

24-
- [Segmenting satellite imagery](https://samgeo.gishub.org/examples/satellite)
25+
- [Segmenting satellite imagery](https://samgeo.gishub.org/examples/satellite)
26+
- [Automatically generating object masks](https://samgeo.gishub.org/examples/automatic_mask_generator)
2527

2628
## Acknowledgements
2729

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Automatically generating object masks with SAM\n",
8+
"\n",
9+
"[![image](https://studiolab.sagemaker.aws/studiolab.svg)](https://studiolab.sagemaker.aws/import/github/opengeos/segment-geospatial/blob/main/docs/examples/automatic_mask_generator.ipynb)\n",
10+
"[![image](https://img.shields.io/badge/Open-Planetary%20Computer-black?style=flat&logo=microsoft)](https://pccompute.westeurope.cloudapp.azure.com/compute/hub/user-redirect/git-pull?repo=https://github.com/opengeos/segment-geospatial&urlpath=lab/tree/segment-geospatial/docs/examples/automatic_mask_generator.ipynb&branch=main)\n",
11+
"[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/segment-geospatial/blob/main/docs/examples/automatic_mask_generator.ipynb)\n",
12+
"\n",
13+
"This notebook shows how to segment objects from an image using the Segment Anything Model (SAM) with a few lines of code. \n",
14+
"\n",
15+
"Make sure you use GPU runtime for this notebook. For Google Colab, go to `Runtime` -> `Change runtime type` and select `GPU` as the hardware accelerator. \n",
16+
"\n",
17+
"The notebook is adapted from [segment-anything/notebooks/automatic_mask_generator_example.ipynb](https://github.com/opengeos/segment-anything/blob/pypi/notebooks/automatic_mask_generator_example.ipynb), but I have made it much easier to save the segmentation results and visualize them."
18+
]
19+
},
20+
{
21+
"cell_type": "markdown",
22+
"metadata": {},
23+
"source": [
24+
"## Install dependencies\n",
25+
"\n",
26+
"Uncomment and run the following cell to install the required dependencies."
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": null,
32+
"metadata": {},
33+
"outputs": [],
34+
"source": [
35+
"# %pip install segment-geospatial leafmap localtileserver"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": null,
41+
"metadata": {},
42+
"outputs": [],
43+
"source": [
44+
"import os\n",
45+
"import leafmap\n",
46+
"from samgeo import SamGeo, show_image, download_file, overlay_images, tms_to_geotiff"
47+
]
48+
},
49+
{
50+
"cell_type": "markdown",
51+
"metadata": {},
52+
"source": [
53+
"## Create an interactive map"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": null,
59+
"metadata": {},
60+
"outputs": [],
61+
"source": [
62+
"m = leafmap.Map(center=[37.8713, -122.2580], zoom=17, height='800px')\n",
63+
"m.add_basemap('SATELLITE')\n",
64+
"m"
65+
]
66+
},
67+
{
68+
"cell_type": "markdown",
69+
"metadata": {},
70+
"source": [
71+
"\n",
72+
"Pan and zoom the map to select the area of interest. Use the draw tools to draw a polygon or rectangle on the map"
73+
]
74+
},
75+
{
76+
"cell_type": "code",
77+
"execution_count": null,
78+
"metadata": {},
79+
"outputs": [],
80+
"source": [
81+
"if m.user_roi_bounds() is not None:\n",
82+
" bbox = m.user_roi_bounds()\n",
83+
"else:\n",
84+
" bbox = [-122.2659, 37.8682, -122.2521, 37.8741]"
85+
]
86+
},
87+
{
88+
"cell_type": "markdown",
89+
"metadata": {},
90+
"source": [
91+
"## Download a sample image"
92+
]
93+
},
94+
{
95+
"cell_type": "code",
96+
"execution_count": null,
97+
"metadata": {},
98+
"outputs": [],
99+
"source": [
100+
"image = 'satellite.tif'\n",
101+
"tms_to_geotiff(output=image, bbox=bbox, zoom=17, source='Satellite', overwrite=True)"
102+
]
103+
},
104+
{
105+
"cell_type": "markdown",
106+
"metadata": {},
107+
"source": [
108+
"You can also use your own image. Uncomment and run the following cell to use your own image."
109+
]
110+
},
111+
{
112+
"cell_type": "code",
113+
"execution_count": null,
114+
"metadata": {},
115+
"outputs": [],
116+
"source": [
117+
"# image = '/path/to/your/own/image.tif'"
118+
]
119+
},
120+
{
121+
"cell_type": "markdown",
122+
"metadata": {},
123+
"source": [
124+
"Display the downloaded image on the map."
125+
]
126+
},
127+
{
128+
"cell_type": "code",
129+
"execution_count": null,
130+
"metadata": {},
131+
"outputs": [],
132+
"source": [
133+
"m.layers[-1].visible = False\n",
134+
"m.add_raster(image, layer_name='Image')\n",
135+
"m"
136+
]
137+
},
138+
{
139+
"cell_type": "markdown",
140+
"metadata": {},
141+
"source": [
142+
"## Initialize SAM class\n",
143+
"\n",
144+
"Specify the file path to the model checkpoint. If it is not specified, the model will to downloaded to the working directory."
145+
]
146+
},
147+
{
148+
"cell_type": "code",
149+
"execution_count": null,
150+
"metadata": {},
151+
"outputs": [],
152+
"source": [
153+
"out_dir = os.path.join(os.path.expanduser('~'), 'Downloads')\n",
154+
"checkpoint = os.path.join(out_dir, 'sam_vit_h_4b8939.pth')"
155+
]
156+
},
157+
{
158+
"cell_type": "code",
159+
"execution_count": null,
160+
"metadata": {},
161+
"outputs": [],
162+
"source": [
163+
"sam = SamGeo(\n",
164+
" model_type='vit_h',\n",
165+
" checkpoint=checkpoint,\n",
166+
" sam_kwargs=None,\n",
167+
")"
168+
]
169+
},
170+
{
171+
"cell_type": "markdown",
172+
"metadata": {},
173+
"source": [
174+
"## Automatic mask generation\n",
175+
"\n",
176+
"Segment the image and save the results to a GeoTIFF file. Set `unique=True` to assign a unique ID to each object. "
177+
]
178+
},
179+
{
180+
"cell_type": "code",
181+
"execution_count": null,
182+
"metadata": {},
183+
"outputs": [],
184+
"source": [
185+
"sam.generate(image, output='masks.tif', foreground=True, unique=True)"
186+
]
187+
},
188+
{
189+
"cell_type": "code",
190+
"execution_count": null,
191+
"metadata": {},
192+
"outputs": [],
193+
"source": [
194+
"sam.show_masks(cmap='binary_r')"
195+
]
196+
},
197+
{
198+
"cell_type": "markdown",
199+
"metadata": {},
200+
"source": [
201+
"Show the object annotations (objects with random color) on the map."
202+
]
203+
},
204+
{
205+
"cell_type": "code",
206+
"execution_count": null,
207+
"metadata": {},
208+
"outputs": [],
209+
"source": [
210+
"sam.show_anns(axis='off', alpha=1, output='annotations.tif')"
211+
]
212+
},
213+
{
214+
"cell_type": "markdown",
215+
"metadata": {},
216+
"source": [
217+
"Convert the object annotations to vector format, such as GeoPackage, Shapefile, or GeoJSON."
218+
]
219+
},
220+
{
221+
"cell_type": "code",
222+
"execution_count": null,
223+
"metadata": {},
224+
"outputs": [],
225+
"source": [
226+
"sam.tiff_to_vector('masks.tif', 'masks.gpkg')"
227+
]
228+
},
229+
{
230+
"cell_type": "markdown",
231+
"metadata": {},
232+
"source": [
233+
"## Automatic mask generation options\n",
234+
"\n",
235+
"There are several tunable parameters in automatic mask generation that control how densely points are sampled and what the thresholds are for removing low quality or duplicate masks. Additionally, generation can be automatically run on crops of the image to get improved performance on smaller objects, and post-processing can remove stray pixels and holes. Here is an example configuration that samples more masks:"
236+
]
237+
},
238+
{
239+
"cell_type": "code",
240+
"execution_count": null,
241+
"metadata": {},
242+
"outputs": [],
243+
"source": [
244+
"sam_kwargs = {\n",
245+
" 'points_per_side': 32,\n",
246+
" 'pred_iou_thresh': 0.86,\n",
247+
" 'stability_score_thresh': 0.92,\n",
248+
" 'crop_n_layers': 1,\n",
249+
" 'crop_n_points_downscale_factor': 2,\n",
250+
" 'min_mask_region_area': 100,\n",
251+
"}"
252+
]
253+
},
254+
{
255+
"cell_type": "code",
256+
"execution_count": null,
257+
"metadata": {},
258+
"outputs": [],
259+
"source": [
260+
"sam = SamGeo(\n",
261+
" model_type='vit_h',\n",
262+
" checkpoint=checkpoint,\n",
263+
" sam_kwargs=sam_kwargs,\n",
264+
")"
265+
]
266+
},
267+
{
268+
"cell_type": "code",
269+
"execution_count": null,
270+
"metadata": {},
271+
"outputs": [],
272+
"source": [
273+
"sam.generate(image, output='masks2.tif', foreground=True)"
274+
]
275+
},
276+
{
277+
"cell_type": "code",
278+
"execution_count": null,
279+
"metadata": {},
280+
"outputs": [],
281+
"source": [
282+
"sam.show_masks(cmap='binary_r')"
283+
]
284+
},
285+
{
286+
"cell_type": "code",
287+
"execution_count": null,
288+
"metadata": {},
289+
"outputs": [],
290+
"source": [
291+
"sam.show_anns(axis='off', opacity=1, output='annotations2.tif')"
292+
]
293+
},
294+
{
295+
"cell_type": "markdown",
296+
"metadata": {},
297+
"source": [
298+
"Overlay the annotations on the image and use the slider to change the opacity interactively."
299+
]
300+
},
301+
{
302+
"cell_type": "code",
303+
"execution_count": null,
304+
"metadata": {},
305+
"outputs": [],
306+
"source": [
307+
"overlay_images(image, 'annotations2.tif', backend='TkAgg')"
308+
]
309+
},
310+
{
311+
"cell_type": "markdown",
312+
"metadata": {},
313+
"source": [
314+
"![](https://i.imgur.com/I1IhDgz.gif)"
315+
]
316+
}
317+
],
318+
"metadata": {
319+
"kernelspec": {
320+
"display_name": "sam",
321+
"language": "python",
322+
"name": "python3"
323+
},
324+
"language_info": {
325+
"codemirror_mode": {
326+
"name": "ipython",
327+
"version": 3
328+
},
329+
"file_extension": ".py",
330+
"mimetype": "text/x-python",
331+
"name": "python",
332+
"nbconvert_exporter": "python",
333+
"pygments_lexer": "ipython3",
334+
"version": "3.9.16"
335+
},
336+
"orig_nbformat": 4
337+
},
338+
"nbformat": 4,
339+
"nbformat_minor": 2
340+
}

0 commit comments

Comments
 (0)