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.
Files changed (160) hide show
  1. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/ubuntu.yml +2 -0
  2. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.pre-commit-config.yaml +1 -1
  3. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/PKG-INFO +2 -2
  4. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/api.md +4 -4
  5. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/installation.md +13 -3
  6. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/pyproject.toml +3 -3
  7. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/metadata.txt +1 -1
  8. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/__init__.py +1 -1
  9. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/api.py +21 -31
  10. segment_geospatial-1.4.0/samgeo/model_registry.py +28 -0
  11. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo3.py +55 -3
  12. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/PKG-INFO +2 -2
  13. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/SOURCES.txt +5 -1
  14. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/requires.txt +1 -1
  15. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/test_api.py +60 -2
  16. segment_geospatial-1.4.0/tests/test_model_registry.py +39 -0
  17. segment_geospatial-1.4.0/tests/test_samgeo3.py +264 -0
  18. segment_geospatial-1.4.0/tests/test_utmconv.py +124 -0
  19. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.editorconfig +0 -0
  20. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/FUNDING.yml +0 -0
  21. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  22. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  23. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  24. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/dependabot.yaml +0 -0
  25. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docker-image.yml +0 -0
  26. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docker-publish.yml +0 -0
  27. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docs-build.yml +0 -0
  28. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/docs.yml +0 -0
  29. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/draft-pdf.yml +0 -0
  30. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/macos.yml +0 -0
  31. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/publish.yml +0 -0
  32. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/pypi.yml +0 -0
  33. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.github/workflows/windows.yml +0 -0
  34. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/.gitignore +0 -0
  35. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/CITATION.cff +0 -0
  36. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/CODE_OF_CONDUCT.md +0 -0
  37. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/Dockerfile +0 -0
  38. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/LICENSE +0 -0
  39. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/MANIFEST.in +0 -0
  40. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/README.md +0 -0
  41. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/SAMGEO.md +0 -0
  42. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/README.md +0 -0
  43. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/__init__.py +0 -0
  44. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/__main__.py +0 -0
  45. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/__init__.py +0 -0
  46. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/data.py +0 -0
  47. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/export.py +0 -0
  48. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/model.py +0 -0
  49. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/project.py +0 -0
  50. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/segment.py +0 -0
  51. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/session.py +0 -0
  52. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/core/vector.py +0 -0
  53. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/samgeo_cli.py +0 -0
  54. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/skills/SKILL.md +0 -0
  55. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/TEST.md +0 -0
  56. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/__init__.py +0 -0
  57. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/test_core.py +0 -0
  58. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/tests/test_full_e2e.py +0 -0
  59. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/__init__.py +0 -0
  60. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/repl_skin.py +0 -0
  61. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/cli_anything/samgeo/utils/samgeo_backend.py +0 -0
  62. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/agent-harness/setup.py +0 -0
  63. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/CNAME +0 -0
  64. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/README.md +0 -0
  65. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/favicon.png +0 -0
  66. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/logo.png +0 -0
  67. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/assets/logo_rect.png +0 -0
  68. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/caption.md +0 -0
  69. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/changelog.md +0 -0
  70. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/changelog_update.py +0 -0
  71. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/common.md +0 -0
  72. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/contributing.md +0 -0
  73. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/detectree2.md +0 -0
  74. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/arcgis.ipynb +0 -0
  75. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/automatic_mask_generator.ipynb +0 -0
  76. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/automatic_mask_generator_hq.ipynb +0 -0
  77. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/box_prompts.ipynb +0 -0
  78. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/data/tree_boxes.geojson +0 -0
  79. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/detectree2.ipynb +0 -0
  80. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/fast_sam.ipynb +0 -0
  81. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/image_captioning.ipynb +0 -0
  82. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/input_prompts.ipynb +0 -0
  83. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/input_prompts_hq.ipynb +0 -0
  84. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/maxar_open_data.ipynb +0 -0
  85. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_automatic.ipynb +0 -0
  86. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_box_prompts.ipynb +0 -0
  87. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_point_prompts.ipynb +0 -0
  88. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_predictor.ipynb +0 -0
  89. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_text_prompts.ipynb +0 -0
  90. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam2_video.ipynb +0 -0
  91. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_automated_segmentation.ipynb +0 -0
  92. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_batch_segmentation.ipynb +0 -0
  93. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_box_prompts.ipynb +0 -0
  94. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_image_segmentation.ipynb +0 -0
  95. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_image_segmentation_jpg.ipynb +0 -0
  96. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_interactive.ipynb +0 -0
  97. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_object_tracking.ipynb +0 -0
  98. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_point_prompts.ipynb +0 -0
  99. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_point_prompts_batch.ipynb +0 -0
  100. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_tiled_segmentation.ipynb +0 -0
  101. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_masks.ipynb +0 -0
  102. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_prompts.ipynb +0 -0
  103. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/sam3_video_segmentation.ipynb +0 -0
  104. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/satellite-predictor.ipynb +0 -0
  105. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/satellite.ipynb +0 -0
  106. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_prompts.ipynb +0 -0
  107. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_prompts_batch.ipynb +0 -0
  108. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/text_swimming_pools.ipynb +0 -0
  109. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/examples/tree_mapping.ipynb +0 -0
  110. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/faq.md +0 -0
  111. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/fast_sam.md +0 -0
  112. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/hq_sam.md +0 -0
  113. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/index.md +0 -0
  114. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/overrides/main.html +0 -0
  115. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo.md +0 -0
  116. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo2.md +0 -0
  117. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/samgeo3.md +0 -0
  118. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/text_sam.md +0 -0
  119. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/usage.md +0 -0
  120. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/AIforGood_2025.ipynb +0 -0
  121. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/IPPN_2024.ipynb +0 -0
  122. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/cn_workshop.ipynb +0 -0
  123. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/jupytext.toml +0 -0
  124. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/docs/workshops/purdue.ipynb +0 -0
  125. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/mkdocs.yml +0 -0
  126. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/package_plugin.py +0 -0
  127. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/10.21105.joss.05663.pdf +0 -0
  128. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/paper.bib +0 -0
  129. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/paper/paper.md +0 -0
  130. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/LICENSE +0 -0
  131. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/README.md +0 -0
  132. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/__init__.py +0 -0
  133. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/icons/icon.png +0 -0
  134. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/install_plugin.py +0 -0
  135. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/install_plugin.sh +0 -0
  136. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/map_tools.py +0 -0
  137. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/resources.py +0 -0
  138. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/samgeo_plugin.py +0 -0
  139. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/qgis-samgeo-plugin/test_plugin.py +0 -0
  140. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements.txt +0 -0
  141. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements_dev.txt +0 -0
  142. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/requirements_docs.txt +0 -0
  143. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/caption.py +0 -0
  144. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/common.py +0 -0
  145. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/detectree2.py +0 -0
  146. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/fast_sam.py +0 -0
  147. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/fer.py +0 -0
  148. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/hq_sam.py +0 -0
  149. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo.py +0 -0
  150. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/samgeo2.py +0 -0
  151. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/text_sam.py +0 -0
  152. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/samgeo/utmconv.py +0 -0
  153. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/scripts/upload_to_qgis_plugin_repo.py +0 -0
  154. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/dependency_links.txt +0 -0
  155. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/entry_points.txt +0 -0
  156. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/segment_geospatial.egg-info/top_level.txt +0 -0
  157. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/setup.cfg +0 -0
  158. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/__init__.py +0 -0
  159. {segment_geospatial-1.3.3 → segment_geospatial-1.4.0}/tests/test_common.py +0 -0
  160. {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/
@@ -12,7 +12,7 @@ repos:
12
12
  args: ["--maxkb=500"]
13
13
 
14
14
  - repo: https://github.com/astral-sh/ruff-pre-commit
15
- rev: v0.15.16
15
+ rev: v0.15.17
16
16
  hooks:
17
17
  - id: ruff
18
18
  types_or: [pyi, jupyter]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: segment-geospatial
3
- Version: 1.3.3
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
- **Important Note**: SAM 3 currently requires an NVIDIA GPU with CUDA support. You won't be able to use SAM 3 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`.
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.3.3"
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.3.3"
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.0.0
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).
@@ -2,7 +2,7 @@
2
2
 
3
3
  __author__ = """Qiusheng Wu"""
4
4
  __email__ = "giswqs@gmail.com"
5
- __version__ = "1.3.3"
5
+ __version__ = "1.4.0"
6
6
 
7
7
 
8
8
  from .samgeo import * # noqa: F403
@@ -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
- # Default model IDs per version
81
- _DEFAULT_MODEL_IDS = {
82
- "sam": "vit_h",
83
- "sam2": "sam2-hiera-large",
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 _DEFAULT_MODEL_IDS:
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(_DEFAULT_MODEL_IDS.keys())}"
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 = _DEFAULT_MODEL_IDS[model_version]
141
+ model_id = DEFAULT_MODEL_IDS[model_version]
161
142
 
162
- valid_ids = _AVAILABLE_MODELS[model_version]
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 = _EXTRAS_MAP.get(model_version, model_version)
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": _AVAILABLE_MODELS, "loaded": loaded}
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="facebook/sam3",
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 for Transformers backend (e.g., 'facebook/sam3').
82
- Only used when backend='transformers'.
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.3
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"
@@ -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/test_samgeo.py
155
+ tests/test_model_registry.py
156
+ tests/test_samgeo.py
157
+ tests/test_samgeo3.py
158
+ tests/test_utmconv.py
@@ -76,7 +76,7 @@ scipy
76
76
 
77
77
  [samgeo3]
78
78
  segment_geospatial[core]
79
- sam3
79
+ sam3>=0.1.4
80
80
  scikit-learn
81
81
  scikit-image
82
82
  transformers
@@ -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
- import samgeo.api as api
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
- import samgeo.api as api
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
+ )