segment-geospatial 1.3.3__tar.gz → 1.4.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.3.3 → segment_geospatial-1.4.0}/.github/workflows/ubuntu.yml +2 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.pre-commit-config.yaml +1 -1
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/PKG-INFO +2 -2
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/api.md +4 -4
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/installation.md +13 -3
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/pyproject.toml +3 -3
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/metadata.txt +1 -1
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/__init__.py +1 -1
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/api.py +21 -31
- segment_geospatial-1.4.0/samgeo/model_registry.py +28 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo3.py +55 -3
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/PKG-INFO +2 -2
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/SOURCES.txt +5 -1
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/requires.txt +1 -1
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/test_api.py +60 -2
- segment_geospatial-1.4.0/tests/test_model_registry.py +39 -0
- segment_geospatial-1.4.0/tests/test_samgeo3.py +264 -0
- segment_geospatial-1.4.0/tests/test_utmconv.py +124 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.editorconfig +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/FUNDING.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/dependabot.yaml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docker-image.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docker-publish.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docs-build.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docs.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/draft-pdf.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/macos.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/publish.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/pypi.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/windows.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.gitignore +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/CITATION.cff +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/CODE_OF_CONDUCT.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/Dockerfile +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/LICENSE +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/MANIFEST.in +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/README.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/SAMGEO.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/README.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/__main__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/data.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/export.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/model.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/project.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/segment.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/session.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/vector.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/samgeo_cli.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/skills/SKILL.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/TEST.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/test_core.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/test_full_e2e.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/repl_skin.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/samgeo_backend.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/setup.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/CNAME +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/README.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/favicon.png +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/logo.png +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/logo_rect.png +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/caption.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/changelog.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/changelog_update.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/common.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/contributing.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/detectree2.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/arcgis.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/automatic_mask_generator.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/automatic_mask_generator_hq.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/box_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/data/tree_boxes.geojson +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/detectree2.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/fast_sam.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/image_captioning.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/input_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/input_prompts_hq.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/maxar_open_data.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_automatic.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_box_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_point_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_predictor.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_text_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_video.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_automated_segmentation.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_batch_segmentation.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_box_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_image_segmentation.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_image_segmentation_jpg.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_interactive.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_object_tracking.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_point_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_point_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_tiled_segmentation.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_masks.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_segmentation.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/satellite-predictor.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/satellite.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_prompts.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_prompts_batch.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_swimming_pools.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/tree_mapping.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/faq.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/fast_sam.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/hq_sam.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/index.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/overrides/main.html +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo2.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo3.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/text_sam.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/usage.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/AIforGood_2025.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/IPPN_2024.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/cn_workshop.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/jupytext.toml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/purdue.ipynb +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/mkdocs.yml +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/package_plugin.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/10.21105.joss.05663.pdf +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/paper.bib +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/paper.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/LICENSE +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/README.md +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/icons/icon.png +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/install_plugin.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/install_plugin.sh +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/map_tools.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/resources.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/samgeo_plugin.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/test_plugin.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements_dev.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements_docs.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/caption.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/common.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/detectree2.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/fast_sam.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/fer.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/hq_sam.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo2.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/text_sam.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/utmconv.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/scripts/upload_to_qgis_plugin_repo.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/dependency_links.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/entry_points.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/top_level.txt +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/setup.cfg +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/__init__.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/test_common.py +0 -0
- {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/test_samgeo.py +0 -0
|
@@ -50,6 +50,8 @@ jobs:
|
|
|
50
50
|
pip install --user .
|
|
51
51
|
- name: Test import
|
|
52
52
|
run: python -c "import samgeo; print('samgeo import successful')"
|
|
53
|
+
- name: Fast unit tests
|
|
54
|
+
run: python -m unittest tests.test_utmconv
|
|
53
55
|
# - name: PKG-TEST
|
|
54
56
|
# run: |
|
|
55
57
|
# python -m unittest discover tests/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: segment-geospatial
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Meta AI's Segment Anything Model (SAM) for Geospatial Data.
|
|
5
5
|
Author-email: Qiusheng Wu <giswqs@gmail.com>
|
|
6
6
|
License: MIT license
|
|
@@ -71,7 +71,7 @@ Requires-Dist: scikit-image; extra == "samgeo2"
|
|
|
71
71
|
Requires-Dist: scipy; extra == "samgeo2"
|
|
72
72
|
Provides-Extra: samgeo3
|
|
73
73
|
Requires-Dist: segment_geospatial[core]; extra == "samgeo3"
|
|
74
|
-
Requires-Dist: sam3; extra == "samgeo3"
|
|
74
|
+
Requires-Dist: sam3>=0.1.4; extra == "samgeo3"
|
|
75
75
|
Requires-Dist: scikit-learn; extra == "samgeo3"
|
|
76
76
|
Requires-Dist: scikit-image; extra == "samgeo3"
|
|
77
77
|
Requires-Dist: transformers; extra == "samgeo3"
|
|
@@ -96,7 +96,7 @@ Runs automatic mask generation on an uploaded image. Supports SAM, SAM2, and SAM
|
|
|
96
96
|
|-----------|------|---------|-------------|
|
|
97
97
|
| `file` | file | required | Image file (TIFF, PNG, JPEG) |
|
|
98
98
|
| `model_version` | string | `sam2` | One of `sam`, `sam2`, `sam3` |
|
|
99
|
-
| `model_id` | string | auto | Model identifier (e.g., `sam2-hiera-large`) |
|
|
99
|
+
| `model_id` | string | auto | Model identifier (e.g., `sam2-hiera-large`, `facebook/sam3.1`) |
|
|
100
100
|
| `output_format` | string | `geojson` | One of `geojson`, `geotiff`, `png`, `json`, `detections` |
|
|
101
101
|
| `foreground` | bool | `true` | Extract foreground objects only |
|
|
102
102
|
| `unique` | bool | `true` | Assign unique ID to each object |
|
|
@@ -131,7 +131,7 @@ For SAM3 with bounding box prompts, the model finds **all similar objects** in t
|
|
|
131
131
|
|-----------|------|---------|-------------|
|
|
132
132
|
| `file` | file | required | Image file (TIFF, PNG, JPEG) |
|
|
133
133
|
| `model_version` | string | `sam3` | One of `sam`, `sam2`, `sam3` |
|
|
134
|
-
| `model_id` | string | auto | Model identifier |
|
|
134
|
+
| `model_id` | string | auto | Model identifier (e.g., `facebook/sam3.1`) |
|
|
135
135
|
| `output_format` | string | `geojson` | One of `geojson`, `geotiff`, `png`, `json`, `detections` |
|
|
136
136
|
| `point_coords` | string | none | JSON array of `[[x, y], ...]` |
|
|
137
137
|
| `point_labels` | string | none | JSON array of `[1, 0, ...]` (1=foreground, 0=background) |
|
|
@@ -222,8 +222,8 @@ Runs text-prompt segmentation using SAM3.
|
|
|
222
222
|
|-----------|------|---------|-------------|
|
|
223
223
|
| `file` | file | required | Image file (TIFF, PNG, JPEG) |
|
|
224
224
|
| `prompt` | string | required | Text description (e.g., `building`, `tree`) |
|
|
225
|
-
| `model_id` | string | auto | SAM3 model identifier |
|
|
226
|
-
| `backend` | string | `meta` | One of `meta`, `transformers` |
|
|
225
|
+
| `model_id` | string | auto | SAM3 model identifier (`facebook/sam3` or `facebook/sam3.1`) |
|
|
226
|
+
| `backend` | string | `meta` | One of `meta`, `transformers`; SAM 3.1 uses `meta` |
|
|
227
227
|
| `output_format` | string | `geojson` | One of `geojson`, `geotiff`, `png`, `json`, `detections` |
|
|
228
228
|
| `confidence_threshold` | float | `0.5` | Detection confidence threshold |
|
|
229
229
|
| `min_size` | int | `0` | Minimum mask size in pixels |
|
|
@@ -145,9 +145,9 @@ If CUDA is `False`, check:
|
|
|
145
145
|
|
|
146
146
|
---
|
|
147
147
|
|
|
148
|
-
### 6) Request access to SAM 3 (Optional)
|
|
148
|
+
### 6) Request access to SAM 3 and SAM 3.1 (Optional)
|
|
149
149
|
|
|
150
|
-
To use SAM 3, you will need to request access by filling out this form on Hugging Face at <https://huggingface.co/facebook/sam3>. Once your request has been approved, run the following command in the terminal to authenticate:
|
|
150
|
+
To use SAM 3, you will need to request access by filling out this form on Hugging Face at <https://huggingface.co/facebook/sam3>. To use SAM 3.1, request access at <https://huggingface.co/facebook/sam3.1>. Once your request has been approved, run the following command in the terminal to authenticate:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
153
|
pixi run hf auth login
|
|
@@ -159,7 +159,17 @@ After authentication, you can download the SAM 3 model from Hugging Face:
|
|
|
159
159
|
pixi run hf download facebook/sam3
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
For SAM 3.1:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pixi run hf download facebook/sam3.1
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
When `segment-geospatial` is installed with a released `sam3` package whose
|
|
169
|
+
checkpoint helper only downloads `facebook/sam3`, SAM 3.1 still downloads
|
|
170
|
+
`facebook/sam3.1` directly through Hugging Face Hub.
|
|
171
|
+
|
|
172
|
+
**Important Note**: SAM 3 and SAM 3.1 currently require an NVIDIA GPU with CUDA support. You won't be able to use them if you have a CPU only system ([source](https://github.com/facebookresearch/sam3/issues/164)). You will get an error message like this: `Failed to load model: Torch not compiled with CUDA enabled`.
|
|
163
173
|
|
|
164
174
|
---
|
|
165
175
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "segment-geospatial"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
8
8
|
dynamic = [
|
|
9
9
|
"dependencies",
|
|
10
10
|
]
|
|
@@ -69,7 +69,7 @@ samgeo2 = [
|
|
|
69
69
|
]
|
|
70
70
|
samgeo3 = [
|
|
71
71
|
"segment_geospatial[core]",
|
|
72
|
-
"sam3",
|
|
72
|
+
"sam3>=0.1.4",
|
|
73
73
|
"scikit-learn",
|
|
74
74
|
"scikit-image",
|
|
75
75
|
"transformers",
|
|
@@ -117,7 +117,7 @@ dependencies = {file = ["requirements.txt"]}
|
|
|
117
117
|
universal = true
|
|
118
118
|
|
|
119
119
|
[tool.bumpversion]
|
|
120
|
-
current_version = "1.
|
|
120
|
+
current_version = "1.4.0"
|
|
121
121
|
commit = true
|
|
122
122
|
tag = true
|
|
123
123
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name=SamGeo
|
|
3
3
|
qgisMinimumVersion=3.22
|
|
4
4
|
description=Segment remote sensing images using SAM (Segment Anything Model) with text, point, and box prompts.
|
|
5
|
-
version=1.
|
|
5
|
+
version=1.4.0
|
|
6
6
|
author=Qiusheng Wu
|
|
7
7
|
email=giswqs@gmail.com
|
|
8
8
|
about=A QGIS plugin for remote sensing image segmentation powered by Meta's Segment Anything Model (SAM).
|
|
@@ -35,6 +35,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
35
35
|
from fastapi.responses import FileResponse, JSONResponse
|
|
36
36
|
|
|
37
37
|
from samgeo import __version__
|
|
38
|
+
from samgeo.model_registry import AVAILABLE_MODELS, DEFAULT_MODEL_IDS, EXTRAS_MAP
|
|
38
39
|
|
|
39
40
|
logger = logging.getLogger("uvicorn.error")
|
|
40
41
|
|
|
@@ -77,30 +78,10 @@ _model_cache_lock = threading.Lock()
|
|
|
77
78
|
# When the same image is sent again, we skip the expensive set_image() call.
|
|
78
79
|
_image_hash_cache: dict = {}
|
|
79
80
|
|
|
80
|
-
#
|
|
81
|
-
_DEFAULT_MODEL_IDS =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"sam3": "facebook/sam3",
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
_AVAILABLE_MODELS = {
|
|
88
|
-
"sam": ["vit_h", "vit_l", "vit_b"],
|
|
89
|
-
"sam2": [
|
|
90
|
-
"sam2-hiera-tiny",
|
|
91
|
-
"sam2-hiera-small",
|
|
92
|
-
"sam2-hiera-base-plus",
|
|
93
|
-
"sam2-hiera-large",
|
|
94
|
-
],
|
|
95
|
-
"sam3": ["facebook/sam3"],
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# Maps model_version to the correct pip extra name
|
|
99
|
-
_EXTRAS_MAP = {
|
|
100
|
-
"sam": "samgeo",
|
|
101
|
-
"sam2": "samgeo2",
|
|
102
|
-
"sam3": "samgeo3",
|
|
103
|
-
}
|
|
81
|
+
# Backward-compatible aliases for code that imported these private constants.
|
|
82
|
+
_DEFAULT_MODEL_IDS = DEFAULT_MODEL_IDS
|
|
83
|
+
_AVAILABLE_MODELS = AVAILABLE_MODELS
|
|
84
|
+
_EXTRAS_MAP = EXTRAS_MAP
|
|
104
85
|
|
|
105
86
|
|
|
106
87
|
def _freeze_kwargs(kwargs: dict):
|
|
@@ -147,19 +128,19 @@ def get_model(model_version: str, model_id: Optional[str] = None, **kwargs):
|
|
|
147
128
|
HTTPException: If model_version or model_id is invalid, or
|
|
148
129
|
dependencies are missing.
|
|
149
130
|
"""
|
|
150
|
-
if model_version not in
|
|
131
|
+
if model_version not in DEFAULT_MODEL_IDS:
|
|
151
132
|
raise HTTPException(
|
|
152
133
|
status_code=400,
|
|
153
134
|
detail=(
|
|
154
135
|
f"Invalid model_version '{model_version}'. "
|
|
155
|
-
f"Must be one of: {list(
|
|
136
|
+
f"Must be one of: {list(DEFAULT_MODEL_IDS.keys())}"
|
|
156
137
|
),
|
|
157
138
|
)
|
|
158
139
|
|
|
159
140
|
if not model_id:
|
|
160
|
-
model_id =
|
|
141
|
+
model_id = DEFAULT_MODEL_IDS[model_version]
|
|
161
142
|
|
|
162
|
-
valid_ids =
|
|
143
|
+
valid_ids = AVAILABLE_MODELS[model_version]
|
|
163
144
|
if model_id not in valid_ids:
|
|
164
145
|
raise HTTPException(
|
|
165
146
|
status_code=400,
|
|
@@ -185,7 +166,7 @@ def get_model(model_version: str, model_id: Optional[str] = None, **kwargs):
|
|
|
185
166
|
return model, lock, key
|
|
186
167
|
|
|
187
168
|
logger.info("Loading model %s", key)
|
|
188
|
-
extra =
|
|
169
|
+
extra = EXTRAS_MAP.get(model_version, model_version)
|
|
189
170
|
try:
|
|
190
171
|
if model_version == "sam":
|
|
191
172
|
from samgeo.samgeo import SamGeo
|
|
@@ -199,7 +180,16 @@ def get_model(model_version: str, model_id: Optional[str] = None, **kwargs):
|
|
|
199
180
|
from samgeo.samgeo3 import SamGeo3
|
|
200
181
|
|
|
201
182
|
kwargs.setdefault("enable_inst_interactivity", True)
|
|
202
|
-
model = SamGeo3(**kwargs)
|
|
183
|
+
model = SamGeo3(model_id=model_id, **kwargs)
|
|
184
|
+
except ValueError as e:
|
|
185
|
+
msg = str(e)
|
|
186
|
+
is_user_config_error = model_version == "sam3" and (
|
|
187
|
+
msg.startswith("Invalid backend")
|
|
188
|
+
or "only supported with backend='meta'" in msg
|
|
189
|
+
)
|
|
190
|
+
if is_user_config_error:
|
|
191
|
+
raise HTTPException(status_code=400, detail=msg) from e
|
|
192
|
+
raise
|
|
203
193
|
except ImportError as e:
|
|
204
194
|
raise HTTPException(
|
|
205
195
|
status_code=503,
|
|
@@ -570,7 +560,7 @@ def health():
|
|
|
570
560
|
def list_models():
|
|
571
561
|
"""List available and currently loaded models."""
|
|
572
562
|
loaded = [list(key) for key in _model_cache]
|
|
573
|
-
return {"models":
|
|
563
|
+
return {"models": AVAILABLE_MODELS, "loaded": loaded}
|
|
574
564
|
|
|
575
565
|
|
|
576
566
|
@app.delete("/models")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Shared model identifiers used across samgeo entry points."""
|
|
2
|
+
|
|
3
|
+
SAM3_MODEL_ID = "facebook/sam3"
|
|
4
|
+
SAM31_MODEL_ID = "facebook/sam3.1"
|
|
5
|
+
SAM3_MODEL_IDS = (SAM3_MODEL_ID, SAM31_MODEL_ID)
|
|
6
|
+
|
|
7
|
+
DEFAULT_MODEL_IDS = {
|
|
8
|
+
"sam": "vit_h",
|
|
9
|
+
"sam2": "sam2-hiera-large",
|
|
10
|
+
"sam3": SAM3_MODEL_ID,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
AVAILABLE_MODELS = {
|
|
14
|
+
"sam": ["vit_h", "vit_l", "vit_b"],
|
|
15
|
+
"sam2": [
|
|
16
|
+
"sam2-hiera-tiny",
|
|
17
|
+
"sam2-hiera-small",
|
|
18
|
+
"sam2-hiera-base-plus",
|
|
19
|
+
"sam2-hiera-large",
|
|
20
|
+
],
|
|
21
|
+
"sam3": list(SAM3_MODEL_IDS),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
EXTRAS_MAP = {
|
|
25
|
+
"sam": "samgeo",
|
|
26
|
+
"sam2": "samgeo2",
|
|
27
|
+
"sam3": "samgeo3",
|
|
28
|
+
}
|
|
@@ -3,6 +3,7 @@ https://github.com/facebookresearch/sam3
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import glob
|
|
6
|
+
import inspect
|
|
6
7
|
import os
|
|
7
8
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
9
|
|
|
@@ -13,8 +14,15 @@ from tqdm import tqdm
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
SAM3_META_IMPORT_ERROR = None
|
|
17
|
+
download_ckpt_from_hf = None
|
|
16
18
|
try:
|
|
17
19
|
from sam3.model_builder import build_sam3_image_model, build_sam3_video_predictor
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from sam3.model_builder import download_ckpt_from_hf
|
|
23
|
+
except ImportError:
|
|
24
|
+
download_ckpt_from_hf = None
|
|
25
|
+
|
|
18
26
|
from sam3.model.sam3_image_processor import Sam3Processor as MetaSam3Processor
|
|
19
27
|
from sam3.visualization_utils import load_frame
|
|
20
28
|
|
|
@@ -43,6 +51,7 @@ except ImportError as e:
|
|
|
43
51
|
print(f"To use SamGeo 3, install it as:\n\tpip install segment-geospatial[samgeo3]")
|
|
44
52
|
|
|
45
53
|
from samgeo import common
|
|
54
|
+
from samgeo.model_registry import DEFAULT_MODEL_IDS, SAM31_MODEL_ID
|
|
46
55
|
from .common import show_image
|
|
47
56
|
|
|
48
57
|
try:
|
|
@@ -53,13 +62,37 @@ except ImportError:
|
|
|
53
62
|
pass
|
|
54
63
|
|
|
55
64
|
|
|
65
|
+
SAM31_CKPT_NAME = "sam3.1_multiplex.pt"
|
|
66
|
+
SAM31_CONFIG_NAME = "config.json"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _download_sam31_checkpoint():
|
|
70
|
+
"""Download the SAM 3.1 checkpoint with released and source sam3 builds."""
|
|
71
|
+
if download_ckpt_from_hf is not None:
|
|
72
|
+
signature = inspect.signature(download_ckpt_from_hf)
|
|
73
|
+
if "version" in signature.parameters:
|
|
74
|
+
return download_ckpt_from_hf(version="sam3.1")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
from huggingface_hub import hf_hub_download
|
|
78
|
+
except ImportError as e:
|
|
79
|
+
raise ImportError(
|
|
80
|
+
"SAM 3.1 requires either a sam3 package whose "
|
|
81
|
+
"download_ckpt_from_hf supports version='sam3.1' or "
|
|
82
|
+
"huggingface_hub to download facebook/sam3.1 directly."
|
|
83
|
+
) from e
|
|
84
|
+
|
|
85
|
+
_ = hf_hub_download(repo_id=SAM31_MODEL_ID, filename=SAM31_CONFIG_NAME)
|
|
86
|
+
return hf_hub_download(repo_id=SAM31_MODEL_ID, filename=SAM31_CKPT_NAME)
|
|
87
|
+
|
|
88
|
+
|
|
56
89
|
class SamGeo3:
|
|
57
90
|
"""The main class for segmenting geospatial data with the Segment Anything Model 3 (SAM3)."""
|
|
58
91
|
|
|
59
92
|
def __init__(
|
|
60
93
|
self,
|
|
61
94
|
backend="meta",
|
|
62
|
-
model_id="
|
|
95
|
+
model_id=DEFAULT_MODEL_IDS["sam3"],
|
|
63
96
|
bpe_path=None,
|
|
64
97
|
device=None,
|
|
65
98
|
eval_mode=True,
|
|
@@ -78,8 +111,10 @@ class SamGeo3:
|
|
|
78
111
|
|
|
79
112
|
Args:
|
|
80
113
|
backend (str): Backend to use ('meta' or 'transformers'). Default is 'meta'.
|
|
81
|
-
model_id (str): Model ID
|
|
82
|
-
|
|
114
|
+
model_id (str): Model ID to use. Official values include
|
|
115
|
+
'facebook/sam3' and 'facebook/sam3.1'. The Meta backend uses
|
|
116
|
+
'facebook/sam3.1' to select the SAM 3.1 Hugging Face checkpoint
|
|
117
|
+
when load_from_HF=True and checkpoint_path is not provided.
|
|
83
118
|
bpe_path (str, optional): Path to the BPE tokenizer vocabulary (Meta backend only).
|
|
84
119
|
device (str, optional): Device to load the model on ('cuda' or 'cpu').
|
|
85
120
|
eval_mode (bool, optional): Whether to set the model to evaluation mode (Meta backend only).
|
|
@@ -117,6 +152,13 @@ class SamGeo3:
|
|
|
117
152
|
f"Invalid backend '{backend}'. Choose 'meta' or 'transformers'."
|
|
118
153
|
)
|
|
119
154
|
|
|
155
|
+
if backend == "transformers" and model_id == SAM31_MODEL_ID:
|
|
156
|
+
raise ValueError(
|
|
157
|
+
"facebook/sam3.1 is a checkpoint repository and is only "
|
|
158
|
+
"supported with backend='meta'. Use model_id='facebook/sam3' "
|
|
159
|
+
"for the Transformers backend."
|
|
160
|
+
)
|
|
161
|
+
|
|
120
162
|
if backend == "meta" and not SAM3_META_AVAILABLE:
|
|
121
163
|
error_msg = (
|
|
122
164
|
"Meta SAM3 is not available. Please install it as:\n"
|
|
@@ -152,6 +194,7 @@ class SamGeo3:
|
|
|
152
194
|
# Initialize backend-specific components
|
|
153
195
|
if backend == "meta":
|
|
154
196
|
self._init_meta_backend(
|
|
197
|
+
model_id=model_id,
|
|
155
198
|
bpe_path=bpe_path,
|
|
156
199
|
device=device,
|
|
157
200
|
eval_mode=eval_mode,
|
|
@@ -191,6 +234,7 @@ class SamGeo3:
|
|
|
191
234
|
|
|
192
235
|
def _init_meta_backend(
|
|
193
236
|
self,
|
|
237
|
+
model_id,
|
|
194
238
|
bpe_path,
|
|
195
239
|
device,
|
|
196
240
|
eval_mode,
|
|
@@ -223,6 +267,14 @@ class SamGeo3:
|
|
|
223
267
|
raise ValueError(f"Checkpoint path {checkpoint_path} does not exist.")
|
|
224
268
|
load_from_HF = False
|
|
225
269
|
|
|
270
|
+
if (
|
|
271
|
+
load_from_HF
|
|
272
|
+
and checkpoint_path is None
|
|
273
|
+
and model_id == SAM31_MODEL_ID
|
|
274
|
+
):
|
|
275
|
+
checkpoint_path = _download_sam31_checkpoint()
|
|
276
|
+
load_from_HF = False
|
|
277
|
+
|
|
226
278
|
model = build_sam3_image_model(
|
|
227
279
|
bpe_path=bpe_path,
|
|
228
280
|
device=device,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: segment-geospatial
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Meta AI's Segment Anything Model (SAM) for Geospatial Data.
|
|
5
5
|
Author-email: Qiusheng Wu <giswqs@gmail.com>
|
|
6
6
|
License: MIT license
|
|
@@ -71,7 +71,7 @@ Requires-Dist: scikit-image; extra == "samgeo2"
|
|
|
71
71
|
Requires-Dist: scipy; extra == "samgeo2"
|
|
72
72
|
Provides-Extra: samgeo3
|
|
73
73
|
Requires-Dist: segment_geospatial[core]; extra == "samgeo3"
|
|
74
|
-
Requires-Dist: sam3; extra == "samgeo3"
|
|
74
|
+
Requires-Dist: sam3>=0.1.4; extra == "samgeo3"
|
|
75
75
|
Requires-Dist: scikit-learn; extra == "samgeo3"
|
|
76
76
|
Requires-Dist: scikit-image; extra == "samgeo3"
|
|
77
77
|
Requires-Dist: transformers; extra == "samgeo3"
|
{segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/SOURCES.txt
RENAMED
|
@@ -136,6 +136,7 @@ samgeo/detectree2.py
|
|
|
136
136
|
samgeo/fast_sam.py
|
|
137
137
|
samgeo/fer.py
|
|
138
138
|
samgeo/hq_sam.py
|
|
139
|
+
samgeo/model_registry.py
|
|
139
140
|
samgeo/samgeo.py
|
|
140
141
|
samgeo/samgeo2.py
|
|
141
142
|
samgeo/samgeo3.py
|
|
@@ -151,4 +152,7 @@ segment_geospatial.egg-info/top_level.txt
|
|
|
151
152
|
tests/__init__.py
|
|
152
153
|
tests/test_api.py
|
|
153
154
|
tests/test_common.py
|
|
154
|
-
tests/
|
|
155
|
+
tests/test_model_registry.py
|
|
156
|
+
tests/test_samgeo.py
|
|
157
|
+
tests/test_samgeo3.py
|
|
158
|
+
tests/test_utmconv.py
|
|
@@ -55,9 +55,19 @@ def test_list_models():
|
|
|
55
55
|
assert "sam" in data["models"]
|
|
56
56
|
assert "sam2" in data["models"]
|
|
57
57
|
assert "sam3" in data["models"]
|
|
58
|
+
assert "facebook/sam3.1" in data["models"]["sam3"]
|
|
58
59
|
assert data["loaded"] == []
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
def test_private_model_constant_aliases_remain_available():
|
|
63
|
+
"""Legacy private constants still point to the shared registry values."""
|
|
64
|
+
from samgeo import api
|
|
65
|
+
|
|
66
|
+
assert api._DEFAULT_MODEL_IDS is api.DEFAULT_MODEL_IDS
|
|
67
|
+
assert api._AVAILABLE_MODELS is api.AVAILABLE_MODELS
|
|
68
|
+
assert api._EXTRAS_MAP is api.EXTRAS_MAP
|
|
69
|
+
|
|
70
|
+
|
|
61
71
|
def test_clear_models():
|
|
62
72
|
"""Test clearing the model cache."""
|
|
63
73
|
response = client.delete("/models")
|
|
@@ -190,7 +200,7 @@ def test_get_model_caches_automatic_and_predict_separately():
|
|
|
190
200
|
``'SamGeo2' object has no attribute 'predictor'``. The cache key now
|
|
191
201
|
includes the ``automatic`` flag so the two are distinct instances.
|
|
192
202
|
"""
|
|
193
|
-
|
|
203
|
+
from samgeo import api
|
|
194
204
|
|
|
195
205
|
with patch("samgeo.samgeo2.SamGeo2") as mock_cls:
|
|
196
206
|
mock_cls.side_effect = lambda **kw: MagicMock()
|
|
@@ -217,7 +227,7 @@ def test_get_model_caches_distinct_configs_separately():
|
|
|
217
227
|
request with a different ``points_per_side``) silently reused the first,
|
|
218
228
|
differently-configured instance. The key now folds in the kwargs.
|
|
219
229
|
"""
|
|
220
|
-
|
|
230
|
+
from samgeo import api
|
|
221
231
|
|
|
222
232
|
# SAM3: two text requests differing only by confidence_threshold.
|
|
223
233
|
with patch("samgeo.samgeo3.SamGeo3") as mock_sam3:
|
|
@@ -238,6 +248,54 @@ def test_get_model_caches_distinct_configs_separately():
|
|
|
238
248
|
assert s_a is not s_b
|
|
239
249
|
|
|
240
250
|
|
|
251
|
+
def test_get_model_accepts_sam31_model_id():
|
|
252
|
+
"""SAM 3.1 is a supported SAM3 model id and keeps its own cache key."""
|
|
253
|
+
from samgeo import api
|
|
254
|
+
|
|
255
|
+
with patch("samgeo.samgeo3.SamGeo3") as mock_sam3:
|
|
256
|
+
mock_sam3.side_effect = lambda **kw: MagicMock()
|
|
257
|
+
sam3_model, _, sam3_key = api.get_model("sam3", "facebook/sam3")
|
|
258
|
+
sam31_model, _, sam31_key = api.get_model("sam3", "facebook/sam3.1")
|
|
259
|
+
sam31_again, _, _ = api.get_model("sam3", "facebook/sam3.1")
|
|
260
|
+
|
|
261
|
+
assert sam3_key != sam31_key
|
|
262
|
+
assert sam3_model is not sam31_model
|
|
263
|
+
assert sam31_again is sam31_model
|
|
264
|
+
mock_sam3.assert_any_call(
|
|
265
|
+
model_id="facebook/sam3",
|
|
266
|
+
enable_inst_interactivity=True,
|
|
267
|
+
)
|
|
268
|
+
mock_sam3.assert_any_call(
|
|
269
|
+
model_id="facebook/sam3.1",
|
|
270
|
+
enable_inst_interactivity=True,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def test_get_model_returns_400_for_invalid_constructor_config():
|
|
275
|
+
"""Invalid model/backend config should be reported as a user error."""
|
|
276
|
+
from samgeo import api
|
|
277
|
+
|
|
278
|
+
with patch("samgeo.samgeo3.SamGeo3") as mock_sam3:
|
|
279
|
+
mock_sam3.side_effect = ValueError(
|
|
280
|
+
"facebook/sam3.1 is only supported with backend='meta'"
|
|
281
|
+
)
|
|
282
|
+
with pytest.raises(api.HTTPException) as excinfo:
|
|
283
|
+
api.get_model("sam3", "facebook/sam3.1", backend="transformers")
|
|
284
|
+
|
|
285
|
+
assert excinfo.value.status_code == 400
|
|
286
|
+
assert "backend='meta'" in excinfo.value.detail
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_get_model_does_not_hide_unexpected_constructor_value_errors():
|
|
290
|
+
"""Unexpected constructor ValueErrors should not be reported as bad input."""
|
|
291
|
+
from samgeo import api
|
|
292
|
+
|
|
293
|
+
with patch("samgeo.samgeo3.SamGeo3") as mock_sam3:
|
|
294
|
+
mock_sam3.side_effect = ValueError("unexpected runtime failure")
|
|
295
|
+
with pytest.raises(ValueError, match="unexpected runtime failure"):
|
|
296
|
+
api.get_model("sam3", "facebook/sam3.1", backend="meta")
|
|
297
|
+
|
|
298
|
+
|
|
241
299
|
def test_freeze_kwargs_is_order_independent():
|
|
242
300
|
"""_freeze_kwargs yields the same hashable key regardless of arg order."""
|
|
243
301
|
from samgeo.api import _freeze_kwargs
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Tests for shared model registry constants."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _load_model_registry():
|
|
8
|
+
module_path = Path(__file__).resolve().parents[1] / "samgeo" / "model_registry.py"
|
|
9
|
+
spec = importlib.util.spec_from_file_location("model_registry", module_path)
|
|
10
|
+
module = importlib.util.module_from_spec(spec)
|
|
11
|
+
spec.loader.exec_module(module)
|
|
12
|
+
return module
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
model_registry = _load_model_registry()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_default_model_ids_are_available_models():
|
|
19
|
+
"""Every default model id should be listed as available."""
|
|
20
|
+
for model_version, model_id in model_registry.DEFAULT_MODEL_IDS.items():
|
|
21
|
+
assert model_id in model_registry.AVAILABLE_MODELS[model_version]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_registry_keys_stay_in_sync():
|
|
25
|
+
"""Shared registry sections should describe the same model versions."""
|
|
26
|
+
assert set(model_registry.DEFAULT_MODEL_IDS) == set(model_registry.AVAILABLE_MODELS)
|
|
27
|
+
assert set(model_registry.DEFAULT_MODEL_IDS) == set(model_registry.EXTRAS_MAP)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_sam31_is_registered_as_a_sam3_model():
|
|
31
|
+
"""SAM 3.1 is exposed as a SAM3-compatible model id."""
|
|
32
|
+
assert model_registry.SAM3_MODEL_ID == "facebook/sam3"
|
|
33
|
+
assert model_registry.SAM31_MODEL_ID == "facebook/sam3.1"
|
|
34
|
+
assert model_registry.SAM3_MODEL_ID in model_registry.SAM3_MODEL_IDS
|
|
35
|
+
assert model_registry.SAM31_MODEL_ID in model_registry.SAM3_MODEL_IDS
|
|
36
|
+
assert (
|
|
37
|
+
list(model_registry.SAM3_MODEL_IDS)
|
|
38
|
+
== model_registry.AVAILABLE_MODELS["sam3"]
|
|
39
|
+
)
|