geoai-py 0.13.1__tar.gz → 0.13.2__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.
- {geoai_py-0.13.1 → geoai_py-0.13.2}/PKG-INFO +1 -1
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/__init__.py +1 -1
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/train.py +118 -8
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/PKG-INFO +1 -1
- {geoai_py-0.13.1 → geoai_py-0.13.2}/pyproject.toml +2 -2
- {geoai_py-0.13.1 → geoai_py-0.13.2}/.dockerignore +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/.editorconfig +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/.gitignore +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/.pre-commit-config.yaml +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/Dockerfile +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/LICENSE +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/MANIFEST.in +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/README.md +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/agents/__init__.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/agents/geo_agents.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/agents/map_tools.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/change_detection.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/classify.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/detectron2.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/dinov3.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/download.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/extract.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/geoai.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/hf.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/map_widgets.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/sam.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/segment.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/segmentation.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai/utils.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/SOURCES.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/dependency_links.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/entry_points.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/requires.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/geoai_py.egg-info/top_level.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/mkdocs.yml +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/pytest.ini +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/requirements.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/requirements_docs.txt +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/setup.cfg +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/__init__.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/create_test_data.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_classify.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_download.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_extract.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_fixtures.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_geoai.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_segment.py +0 -0
- {geoai_py-0.13.1 → geoai_py-0.13.2}/tests/test_utils.py +0 -0
@@ -2625,6 +2625,8 @@ def semantic_inference_on_geotiff(
|
|
2625
2625
|
num_channels: int = 3,
|
2626
2626
|
num_classes: int = 2,
|
2627
2627
|
device: Optional[torch.device] = None,
|
2628
|
+
probability_path: Optional[str] = None,
|
2629
|
+
probability_threshold: Optional[float] = None,
|
2628
2630
|
quiet: bool = False,
|
2629
2631
|
**kwargs: Any,
|
2630
2632
|
) -> Tuple[str, float]:
|
@@ -2641,6 +2643,11 @@ def semantic_inference_on_geotiff(
|
|
2641
2643
|
num_channels (int): Number of channels to use from the input image.
|
2642
2644
|
num_classes (int): Number of classes in the model output.
|
2643
2645
|
device (torch.device, optional): Device to run inference on.
|
2646
|
+
probability_path (str, optional): Path to save probability map. If provided,
|
2647
|
+
the normalized class probabilities will be saved as a multi-band raster.
|
2648
|
+
probability_threshold (float, optional): Probability threshold for binary classification.
|
2649
|
+
Only used when num_classes=2. If provided, pixels with class 1 probability >= threshold
|
2650
|
+
are classified as class 1, otherwise class 0. If None (default), uses argmax.
|
2644
2651
|
quiet (bool): If True, suppress progress bar. Defaults to False.
|
2645
2652
|
**kwargs: Additional arguments.
|
2646
2653
|
|
@@ -2811,10 +2818,19 @@ def semantic_inference_on_geotiff(
|
|
2811
2818
|
/ count_accumulator[valid_pixels]
|
2812
2819
|
)
|
2813
2820
|
|
2814
|
-
#
|
2815
|
-
|
2816
|
-
|
2817
|
-
|
2821
|
+
# Apply threshold for binary classification or use argmax
|
2822
|
+
if probability_threshold is not None and num_classes == 2:
|
2823
|
+
# Use threshold: classify as class 1 if probability >= threshold
|
2824
|
+
mask[valid_pixels] = (
|
2825
|
+
normalized_probs[1, valid_pixels] >= probability_threshold
|
2826
|
+
).astype(np.uint8)
|
2827
|
+
if not quiet:
|
2828
|
+
print(f"Using probability threshold: {probability_threshold}")
|
2829
|
+
else:
|
2830
|
+
# Take argmax to get final class predictions
|
2831
|
+
mask[valid_pixels] = np.argmax(
|
2832
|
+
normalized_probs[:, valid_pixels], axis=0
|
2833
|
+
).astype(np.uint8)
|
2818
2834
|
|
2819
2835
|
# Check class distribution in predictions (summary only)
|
2820
2836
|
unique_classes, class_counts = np.unique(
|
@@ -2839,6 +2855,29 @@ def semantic_inference_on_geotiff(
|
|
2839
2855
|
if not quiet:
|
2840
2856
|
print(f"Saved prediction to {output_path}")
|
2841
2857
|
|
2858
|
+
# Save probability map if requested
|
2859
|
+
if probability_path is not None:
|
2860
|
+
prob_dir = os.path.abspath(os.path.dirname(probability_path))
|
2861
|
+
os.makedirs(prob_dir, exist_ok=True)
|
2862
|
+
|
2863
|
+
# Prepare probability output metadata
|
2864
|
+
prob_meta = meta.copy()
|
2865
|
+
prob_meta.update({"count": num_classes, "dtype": "float32"})
|
2866
|
+
|
2867
|
+
# Save normalized probabilities
|
2868
|
+
with rasterio.open(probability_path, "w", **prob_meta) as dst:
|
2869
|
+
for class_idx in range(num_classes):
|
2870
|
+
# Normalize probabilities
|
2871
|
+
prob_band = np.zeros((height, width), dtype=np.float32)
|
2872
|
+
prob_band[valid_pixels] = (
|
2873
|
+
prob_accumulator[class_idx, valid_pixels]
|
2874
|
+
/ count_accumulator[valid_pixels]
|
2875
|
+
)
|
2876
|
+
dst.write(prob_band, class_idx + 1)
|
2877
|
+
|
2878
|
+
if not quiet:
|
2879
|
+
print(f"Saved probability map to {probability_path}")
|
2880
|
+
|
2842
2881
|
return output_path, inference_time
|
2843
2882
|
|
2844
2883
|
|
@@ -2853,6 +2892,8 @@ def semantic_inference_on_image(
|
|
2853
2892
|
num_classes: int = 2,
|
2854
2893
|
device: Optional[torch.device] = None,
|
2855
2894
|
binary_output: bool = True,
|
2895
|
+
probability_path: Optional[str] = None,
|
2896
|
+
probability_threshold: Optional[float] = None,
|
2856
2897
|
quiet: bool = False,
|
2857
2898
|
**kwargs: Any,
|
2858
2899
|
) -> Tuple[str, float]:
|
@@ -2870,6 +2911,11 @@ def semantic_inference_on_image(
|
|
2870
2911
|
num_classes (int): Number of classes in the model output.
|
2871
2912
|
device (torch.device, optional): Device to run inference on.
|
2872
2913
|
binary_output (bool): If True, convert multi-class output to binary (class > 0).
|
2914
|
+
probability_path (str, optional): Path to save probability map. If provided,
|
2915
|
+
the normalized class probabilities will be saved as a multi-band raster.
|
2916
|
+
probability_threshold (float, optional): Probability threshold for binary classification.
|
2917
|
+
Only used when num_classes=2. If provided, pixels with class 1 probability >= threshold
|
2918
|
+
are classified as class 1, otherwise class 0. If None (default), uses argmax.
|
2873
2919
|
quiet (bool): If True, suppress progress bar. Defaults to False.
|
2874
2920
|
**kwargs: Additional arguments.
|
2875
2921
|
|
@@ -3056,10 +3102,19 @@ def semantic_inference_on_image(
|
|
3056
3102
|
/ count_accumulator[valid_pixels]
|
3057
3103
|
)
|
3058
3104
|
|
3059
|
-
#
|
3060
|
-
|
3061
|
-
|
3062
|
-
|
3105
|
+
# Apply threshold for binary classification or use argmax
|
3106
|
+
if probability_threshold is not None and num_classes == 2:
|
3107
|
+
# Use threshold: classify as class 1 if probability >= threshold
|
3108
|
+
mask[valid_pixels] = (
|
3109
|
+
normalized_probs[1, valid_pixels] >= probability_threshold
|
3110
|
+
).astype(np.uint8)
|
3111
|
+
if not quiet:
|
3112
|
+
print(f"Using probability threshold: {probability_threshold}")
|
3113
|
+
else:
|
3114
|
+
# Take argmax to get final class predictions
|
3115
|
+
mask[valid_pixels] = np.argmax(
|
3116
|
+
normalized_probs[:, valid_pixels], axis=0
|
3117
|
+
).astype(np.uint8)
|
3063
3118
|
|
3064
3119
|
# Check class distribution in predictions before binary conversion
|
3065
3120
|
unique_classes, class_counts = np.unique(mask, return_counts=True)
|
@@ -3116,6 +3171,40 @@ def semantic_inference_on_image(
|
|
3116
3171
|
if not quiet:
|
3117
3172
|
print(f"Saved prediction to {output_path}")
|
3118
3173
|
|
3174
|
+
# Save probability map if requested
|
3175
|
+
if probability_path is not None:
|
3176
|
+
prob_dir = os.path.abspath(os.path.dirname(probability_path))
|
3177
|
+
os.makedirs(prob_dir, exist_ok=True)
|
3178
|
+
|
3179
|
+
# For regular images, we'll save as a multi-channel TIFF
|
3180
|
+
# since we need to preserve floating point values
|
3181
|
+
import rasterio
|
3182
|
+
from rasterio.transform import from_bounds
|
3183
|
+
|
3184
|
+
# Create a simple affine transform (identity transform for pixel coordinates)
|
3185
|
+
transform = from_bounds(0, 0, width, height, width, height)
|
3186
|
+
|
3187
|
+
# Prepare probability output metadata
|
3188
|
+
prob_meta = {
|
3189
|
+
"driver": "GTiff",
|
3190
|
+
"height": height,
|
3191
|
+
"width": width,
|
3192
|
+
"count": num_classes,
|
3193
|
+
"dtype": "float32",
|
3194
|
+
"transform": transform,
|
3195
|
+
}
|
3196
|
+
|
3197
|
+
# Save normalized probabilities
|
3198
|
+
with rasterio.open(probability_path, "w", **prob_meta) as dst:
|
3199
|
+
for class_idx in range(num_classes):
|
3200
|
+
# Normalize probabilities
|
3201
|
+
prob_band = np.zeros((height, width), dtype=np.float32)
|
3202
|
+
prob_band[valid_pixels] = normalized_probs[class_idx, valid_pixels]
|
3203
|
+
dst.write(prob_band, class_idx + 1)
|
3204
|
+
|
3205
|
+
if not quiet:
|
3206
|
+
print(f"Saved probability map to {probability_path}")
|
3207
|
+
|
3119
3208
|
return output_path, inference_time
|
3120
3209
|
|
3121
3210
|
|
@@ -3131,6 +3220,8 @@ def semantic_segmentation(
|
|
3131
3220
|
overlap: int = 256,
|
3132
3221
|
batch_size: int = 4,
|
3133
3222
|
device: Optional[torch.device] = None,
|
3223
|
+
probability_path: Optional[str] = None,
|
3224
|
+
probability_threshold: Optional[float] = None,
|
3134
3225
|
quiet: bool = False,
|
3135
3226
|
**kwargs: Any,
|
3136
3227
|
) -> None:
|
@@ -3152,6 +3243,12 @@ def semantic_segmentation(
|
|
3152
3243
|
overlap (int): Overlap between adjacent windows.
|
3153
3244
|
batch_size (int): Batch size for inference.
|
3154
3245
|
device (torch.device, optional): Device to run inference on.
|
3246
|
+
probability_path (str, optional): Path to save probability map. If provided,
|
3247
|
+
the normalized class probabilities will be saved as a multi-band raster.
|
3248
|
+
probability_threshold (float, optional): Probability threshold for binary classification.
|
3249
|
+
Only used when num_classes=2. If provided, pixels with class 1 probability >= threshold
|
3250
|
+
are classified as class 1, otherwise class 0. If None (default), uses argmax.
|
3251
|
+
Must be between 0 and 1.
|
3155
3252
|
quiet (bool): If True, suppress progress bar. Defaults to False.
|
3156
3253
|
**kwargs: Additional arguments.
|
3157
3254
|
|
@@ -3205,6 +3302,15 @@ def semantic_segmentation(
|
|
3205
3302
|
model.to(device)
|
3206
3303
|
model.eval()
|
3207
3304
|
|
3305
|
+
# Validate probability_threshold
|
3306
|
+
if probability_threshold is not None:
|
3307
|
+
if not (0 <= probability_threshold <= 1):
|
3308
|
+
raise ValueError("probability_threshold must be between 0 and 1")
|
3309
|
+
if num_classes != 2:
|
3310
|
+
raise ValueError(
|
3311
|
+
"probability_threshold is only supported for binary classification (num_classes=2)"
|
3312
|
+
)
|
3313
|
+
|
3208
3314
|
# Use appropriate inference function based on file format
|
3209
3315
|
if is_geotiff:
|
3210
3316
|
semantic_inference_on_geotiff(
|
@@ -3217,6 +3323,8 @@ def semantic_segmentation(
|
|
3217
3323
|
num_channels=num_channels,
|
3218
3324
|
num_classes=num_classes,
|
3219
3325
|
device=device,
|
3326
|
+
probability_path=probability_path,
|
3327
|
+
probability_threshold=probability_threshold,
|
3220
3328
|
quiet=quiet,
|
3221
3329
|
**kwargs,
|
3222
3330
|
)
|
@@ -3235,6 +3343,8 @@ def semantic_segmentation(
|
|
3235
3343
|
num_classes=num_classes,
|
3236
3344
|
device=device,
|
3237
3345
|
binary_output=True, # Convert to binary output for better visualization
|
3346
|
+
probability_path=probability_path,
|
3347
|
+
probability_threshold=probability_threshold,
|
3238
3348
|
quiet=quiet,
|
3239
3349
|
**kwargs,
|
3240
3350
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "geoai-py"
|
3
|
-
version = "0.13.
|
3
|
+
version = "0.13.2"
|
4
4
|
dynamic = [
|
5
5
|
"dependencies",
|
6
6
|
]
|
@@ -44,7 +44,7 @@ universal = true
|
|
44
44
|
|
45
45
|
|
46
46
|
[tool.bumpversion]
|
47
|
-
current_version = "0.13.
|
47
|
+
current_version = "0.13.2"
|
48
48
|
commit = true
|
49
49
|
tag = true
|
50
50
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|