JulioContrerasH commited on
Commit
6cd4c88
·
verified ·
1 Parent(s): a28ae79

Upload 14 files

Browse files
ensemble/ft_g_1dpwdeeplabv3.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T09:09:20Z",
44
  "created": "2025-10-27T09:09:20Z",
45
- "updated": "2025-11-06T14:39:28.632388Z",
46
  "description": "DeepLabV3 + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWDEEPLABV3 Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwdeeplabv3_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:28.632388Z",
167
  "custom:training_datetime": "2025-10-27T09:09:20Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T09:09:20Z",
44
  "created": "2025-10-27T09:09:20Z",
45
+ "updated": "2025-11-09T18:20:25.792758Z",
46
  "description": "DeepLabV3 + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWDEEPLABV3 Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwdeeplabv3_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:25.792758Z",
169
  "custom:training_datetime": "2025-10-27T09:09:20Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_1dpwdeeplabv3.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:ba9af2a9393f46a19a87efe9279d6b298ddf3c1a26ff919c95f85da043483a5a
3
  size 55868251
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eb1a97144e6404ac8b197c5dc3bfaf91b00abe53a80563986a69f6cb39f8361a
3
  size 55868251
ensemble/ft_g_1dpwseg.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T10:10:41Z",
44
  "created": "2025-10-27T10:10:41Z",
45
- "updated": "2025-11-06T14:39:31.359344Z",
46
  "description": "SegFormer + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWSEG Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwseg_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:31.359344Z",
167
  "custom:training_datetime": "2025-10-27T10:10:41Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T10:10:41Z",
44
  "created": "2025-10-27T10:10:41Z",
45
+ "updated": "2025-11-09T18:20:30.990281Z",
46
  "description": "SegFormer + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWSEG Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwseg_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:30.990281Z",
169
  "custom:training_datetime": "2025-10-27T10:10:41Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_1dpwseg.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:cd554de116acb3ff75f602ec6c2aaeb08a44661d2ff1fabf9c325e62cd56b3f7
3
  size 16934922
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5fd1141ad3f77704948e9adf7ebe5edfdd2e1e1aad483fe42aa8616b7c0416da
3
  size 16934922
ensemble/ft_g_1dpwunet.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T10:31:37Z",
44
  "created": "2025-10-27T10:31:37Z",
45
- "updated": "2025-11-06T14:39:23.087913Z",
46
  "description": "UNet + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWUNET Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwunet_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:23.087913Z",
167
  "custom:training_datetime": "2025-10-27T10:31:37Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T10:31:37Z",
44
  "created": "2025-10-27T10:31:37Z",
45
+ "updated": "2025-11-09T18:20:14.166827Z",
46
  "description": "UNet + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWUNET Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwunet_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:14.166827Z",
169
  "custom:training_datetime": "2025-10-27T10:31:37Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_1dpwunet.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:f7dff64617de9f8c6b543cb2096cb9d447a1a1effffb1e4a8dffff9a7254dfc0
3
  size 31813924
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b7fcaee1a0722d51acd9eb7f01d465fa32860865825f33e1531c835f216e71a9
3
  size 31813924
ensemble/ft_g_1dpwunetpp.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T09:45:39Z",
44
  "created": "2025-10-27T09:45:39Z",
45
- "updated": "2025-11-06T14:39:20.164134Z",
46
  "description": "UNet++ + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWUNETPP Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwunetpp_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:20.164134Z",
167
  "custom:training_datetime": "2025-10-27T09:45:39Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T09:45:39Z",
44
  "created": "2025-10-27T09:45:39Z",
45
+ "updated": "2025-11-09T18:20:07.449926Z",
46
  "description": "UNet++ + PixelWise MLP architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "1DPWUNETPP Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "1dpwunetpp_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:07.449926Z",
169
  "custom:training_datetime": "2025-10-27T09:45:39Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_1dpwunetpp.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:9f0a35ceb55200bd5b6786fdd6bffc879fc7cab20e534ae544b774a46227093b
3
  size 32840445
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6bf4096004d9af6e2a954295e970d6a713ed0ba582cb0511752fe1855ac32a0
3
  size 32840445
ensemble/ft_g_deeplabv3.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T11:37:50Z",
44
  "created": "2025-10-27T11:37:50Z",
45
- "updated": "2025-11-06T14:39:40.356235Z",
46
  "description": "DeepLabV3 architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "DEEPLABV3 Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "deeplabv3_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:40.356235Z",
167
  "custom:training_datetime": "2025-10-27T11:37:50Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T11:37:50Z",
44
  "created": "2025-10-27T11:37:50Z",
45
+ "updated": "2025-11-09T18:20:45.623530Z",
46
  "description": "DeepLabV3 architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "DEEPLABV3 Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "deeplabv3_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:45.623530Z",
169
  "custom:training_datetime": "2025-10-27T11:37:50Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_deeplabv3.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:035fd5da25035ee4ae7f95f5f1f6ef806a9450e081ab145fe0a0e2db6a63f4d4
3
  size 55758820
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d20aea4a3537a852de8b32ca2fa1fcec6d79578e6dc0b981f28a6061d6532fe6
3
  size 55758820
ensemble/ft_g_segformer.json CHANGED
@@ -42,7 +42,7 @@
42
  "properties": {
43
  "datetime": "2025-10-27T10:55:12Z",
44
  "created": "2025-10-27T10:55:12Z",
45
- "updated": "2025-11-06T14:39:25.707855Z",
46
  "description": "SegFormer architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "SEGFORMER Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "segformer_fdr4vgt_cloudmask_ft",
@@ -131,7 +131,9 @@
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
- "post_processing_function": "Apply threshold >= 0.5 for binary mask (adjustable: conservative=0.3, aggressive=0.7)",
 
 
135
  "value_range": [
136
  0.0,
137
  1.0
@@ -163,7 +165,7 @@
163
  "custom:export_format": "torch.export.pt2",
164
  "custom:has_sigmoid": true,
165
  "custom:sigmoid_location": "built-in wrapper",
166
- "custom:export_datetime": "2025-11-06T14:39:25.707855Z",
167
  "custom:training_datetime": "2025-10-27T10:55:12Z",
168
  "custom:training_stage": "fine-tuning",
169
  "custom:base_model_source": "global_cloudmask",
 
42
  "properties": {
43
  "datetime": "2025-10-27T10:55:12Z",
44
  "created": "2025-10-27T10:55:12Z",
45
+ "updated": "2025-11-09T18:20:20.306738Z",
46
  "description": "SegFormer architecture fine-tuned for cloud detection in VGT-1, VGT-2, and PROBA-V satellite imagery from the FDR4VGT harmonized dataset.",
47
  "title": "SEGFORMER Cloud Detection Model - VGT1/VGT2/Proba-V (Fine-tuned)",
48
  "mlm:name": "segformer_fdr4vgt_cloudmask_ft",
 
131
  "color_hint": "FFFF00"
132
  }
133
  ],
134
+ "post_processing_function": "Apply threshold to get binary mask. Standard threshold: 0.5. Recommended (balanced) threshold: 0.4.",
135
+ "standard_threshold": 0.5,
136
+ "recommended_threshold": 0.4,
137
  "value_range": [
138
  0.0,
139
  1.0
 
165
  "custom:export_format": "torch.export.pt2",
166
  "custom:has_sigmoid": true,
167
  "custom:sigmoid_location": "built-in wrapper",
168
+ "custom:export_datetime": "2025-11-09T18:20:20.306738Z",
169
  "custom:training_datetime": "2025-10-27T10:55:12Z",
170
  "custom:training_stage": "fine-tuning",
171
  "custom:base_model_source": "global_cloudmask",
ensemble/ft_g_segformer.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:73ff4399354bbb5144807e03492278e1c2aea57436d7ce524f89c9d193587c2b
3
  size 16825964
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:792337973bec27a2a4cd84ccaae35cccd4dee272055b74fef08cdf8882a6c824
3
  size 16825964
ensemble/load.py CHANGED
@@ -2,17 +2,12 @@ import torch
2
  import torch.nn
3
  import pathlib
4
  import pystac
5
- import math
6
  from typing import Literal
7
-
8
- # --- Imports necesarios para la inferencia ---
9
  import numpy as np
10
  import itertools
11
  from tqdm import tqdm
12
 
13
- # --- 1. CLASE ENSEMBLE (MEJORADA Y CORREGIDA) ---
14
- # Esta versión es flexible y compatible con tu 'predict_large_v3'
15
- # (Solo devuelve probabilidades, no incertidumbre)
16
  class EnsembleModel(torch.nn.Module):
17
  def __init__(self, *models, mode="max"):
18
  super(EnsembleModel, self).__init__()
@@ -31,32 +26,20 @@ class EnsembleModel(torch.nn.Module):
31
  return None
32
 
33
  concatenated_outputs = torch.cat(outputs, dim=1)
34
-
35
- # ¡IMPORTANTE! Tu predict_large_v3 espera UNA salida (probabilidades).
36
- # Esta lógica ha sido adaptada de tu 'EnsembleModel' original.
37
  if self.mode == "max":
38
- # Usamos keepdim=True para mantener (B, 1, H, W) y ser seguros
39
  output_probs = torch.max(concatenated_outputs, dim=1, keepdim=True)[0]
40
  elif self.mode == "mean":
41
  output_probs = torch.mean(concatenated_outputs, dim=1, keepdim=True)
42
  elif self.mode == "min":
43
  output_probs = torch.min(concatenated_outputs, dim=1, keepdim=True)[0]
44
  elif self.mode == "none":
45
- return concatenated_outputs # Devuelve (B, N, H, W)
46
  else:
47
  raise ValueError("Mode must be 'min', 'mean', or 'max'.")
48
 
49
- return output_probs # Devuelve (B, 1, H, W)
50
-
51
-
52
- # --- 2. API DE MLSTAC (FUNCIONES REQUERIDAS) ---
53
-
54
- def trainable_model(path: pathlib.Path, stac_item: pystac.Item, *args, **kwargs):
55
- """
56
- Este modelo de ensemble no soporta re-entrenamiento.
57
- """
58
- print("El modelo de ensemble no está disponible en modo de entrenamiento.")
59
- return None
60
 
61
  def compiled_model(
62
  path: pathlib.Path,
@@ -65,37 +48,35 @@ def compiled_model(
65
  *args, **kwargs
66
  ) -> EnsembleModel:
67
  """
68
- Carga el ensemble dinámicamente usando el 'stac_item' (el ensemble.json)
69
- como la ÚNICA fuente de verdad.
70
  """
71
- print("Cargando ensemble dinámicamente desde 'stac_item'...")
72
 
73
  model_paths = []
74
 
75
  for asset_key, asset in stac_item.assets.items():
76
  if asset.href.endswith(".pt2"):
77
- print(f" > Encontrado asset de modelo: {pathlib.Path(asset.href).name}")
78
  model_paths.append(asset.href)
79
 
80
  if not model_paths:
81
- raise ValueError("No se encontraron archivos .pt2 en los assets del STAC item.")
82
 
83
- print(f"Cargando {len(model_paths)} modelos .pt2 (en CPU)...")
84
 
85
  model_paths.sort()
86
  models = [torch.export.load(p).module() for p in model_paths]
87
 
88
  cloud_model = EnsembleModel(*models, mode=mode)
89
 
90
- print("Ensemble cargado exitosamente en CPU.")
91
  return cloud_model
92
 
93
 
94
- # --- 3. FUNCIONES DE INFERENCIA (HERRAMIENTAS) ---
95
-
96
  def define_iteration(dimension: tuple, chunk_size: int, overlap: int = 0):
97
  """
98
- Define la estrategia de iteración para recorrer la imagen con solapamiento.
99
  """
100
  dimy, dimx = dimension
101
  if chunk_size > max(dimx, dimy):
@@ -111,7 +92,7 @@ def define_iteration(dimension: tuple, chunk_size: int, overlap: int = 0):
111
 
112
  def fix_lastchunk(iterchunks, s2dim, chunk_size):
113
  """
114
- Ajusta los últimos chunks para que no se salgan de los bordes.
115
  """
116
  itercontainer = []
117
  for index_i, index_j in iterchunks:
@@ -120,37 +101,39 @@ def fix_lastchunk(iterchunks, s2dim, chunk_size):
120
  if index_j + chunk_size > s2dim[1]:
121
  index_j = max(s2dim[1] - chunk_size, 0)
122
  itercontainer.append((index_i, index_j))
123
- return list(set(itercontainer)) # Devuelve únicos por si acaso
124
 
125
 
126
- def predict_large_array(
127
- image_array: np.ndarray,
128
- cloud_model: torch.nn.Module,
129
  chunk_size: int = 512,
130
- overlap: int = 32,
131
- device: str = "cpu"
 
132
  ) -> np.ndarray:
133
  """
134
- Predice un 'image_array' (C, H, W) completo en parches solapados.
135
 
136
- Esta es una versión modificada de 'predict_large_v3' que:
137
- 1. Acepta un np.ndarray en lugar de paths.
138
- 2. Devuelve un np.ndarray (máscara de prob) en lugar de guardar en disco.
 
139
 
140
- Compatible con:
141
- - Modelos normales (con .eval())
142
- - Modelos exportados (.pt2) y Ensembles (sin .eval())
143
  """
144
 
145
- # 1) Validar metadata desde el array
146
- if image_array.ndim != 3:
147
- raise ValueError(f"Input array debe ser (C, H, W). Se recibió {image_array.shape}")
148
 
149
- bands, height, width = image_array.shape
150
- print(f"Iniciando predicción en array de shape: {(bands, height, width)}")
151
 
152
- # 2) Preparar array de salida (1-band float)
153
- output_array = np.zeros((1, height, width), dtype=np.float32)
154
 
155
  # 3) Get the list of tile offsets
156
  coords = define_iteration(
@@ -159,36 +142,59 @@ def predict_large_array(
159
  overlap=overlap
160
  )
161
 
162
- # 4) Preparar modelo (lógica de compatibilidad .pt2)
163
  try:
164
- cloud_model.eval()
165
- for p in cloud_model.parameters():
166
  p.requires_grad = False
167
- cloud_model = cloud_model.to(device)
168
  except (NotImplementedError, AttributeError):
169
- # Es modelo exportado (.pt2) o EnsembleModel
170
- cloud_model = cloud_model.to(device)
171
 
172
- # 5) Iterar sobre los tiles (solo con numpy)
173
  for idx, (row_off, col_off) in enumerate(tqdm(coords, desc="Inference")):
174
 
175
- # (a) Leer chunk (slicing de Numpy)
176
- patch = image_array[
177
  :,
178
  row_off : row_off + chunk_size,
179
  col_off : col_off + chunk_size
180
  ]
181
 
182
- # (b) Forward pass
183
  patch_tensor = torch.from_numpy(patch).float().unsqueeze(0).to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  with torch.no_grad():
185
- # Asumimos que el modelo (o ensemble) devuelve probabilidades (B, 1, H, W)
186
- probs = cloud_model(patch_tensor)
 
 
 
 
 
 
 
187
 
188
- # .squeeze(0) -> quita la dimensión de batch (1, H, W)
189
- result = probs.squeeze(0).cpu().numpy()
190
 
191
- # (c-f) Lógica de escritura parcial
192
  if col_off == 0:
193
  offset_x = 0
194
  else:
@@ -213,19 +219,26 @@ def predict_large_array(
213
  length_y = chunk_size - (overlap // 2)
214
  sub_y_start = overlap // 2 if row_off != 0 else 0
215
 
 
 
 
 
 
 
 
216
  to_write = result[
217
  :,
218
  sub_y_start : sub_y_start + length_y,
219
  sub_x_start : sub_x_start + length_x
220
  ]
221
 
222
- # Escribir en el array de salida de numpy
223
  output_array[
224
  :,
225
  offset_y : offset_y + length_y,
226
  offset_x : offset_x + length_x
227
  ] = to_write
228
 
229
- print("Predicción completada.")
230
- # Devuelve el array de (1, H, W)
231
  return output_array
 
2
  import torch.nn
3
  import pathlib
4
  import pystac
 
5
  from typing import Literal
 
 
6
  import numpy as np
7
  import itertools
8
  from tqdm import tqdm
9
 
10
+ # Ensemble model for combining multiple models' outputs
 
 
11
  class EnsembleModel(torch.nn.Module):
12
  def __init__(self, *models, mode="max"):
13
  super(EnsembleModel, self).__init__()
 
26
  return None
27
 
28
  concatenated_outputs = torch.cat(outputs, dim=1)
29
+
 
 
30
  if self.mode == "max":
31
+ # Use keepdim=True to maintain (B, 1, H, W) and be safe
32
  output_probs = torch.max(concatenated_outputs, dim=1, keepdim=True)[0]
33
  elif self.mode == "mean":
34
  output_probs = torch.mean(concatenated_outputs, dim=1, keepdim=True)
35
  elif self.mode == "min":
36
  output_probs = torch.min(concatenated_outputs, dim=1, keepdim=True)[0]
37
  elif self.mode == "none":
38
+ return concatenated_outputs # Returns (B, N, H, W)
39
  else:
40
  raise ValueError("Mode must be 'min', 'mean', or 'max'.")
41
 
42
+ return output_probs # Returns (B, 1, H, W)
 
 
 
 
 
 
 
 
 
 
43
 
44
  def compiled_model(
45
  path: pathlib.Path,
 
48
  *args, **kwargs
49
  ) -> EnsembleModel:
50
  """
51
+ Loads the ensemble dynamically using the 'stac_item' (the ensemble.json)
52
+ as the ONLY source of truth.
53
  """
54
+ print("Loading ensemble dynamically from 'stac_item'...")
55
 
56
  model_paths = []
57
 
58
  for asset_key, asset in stac_item.assets.items():
59
  if asset.href.endswith(".pt2"):
60
+ print(f" > Found model asset: {pathlib.Path(asset.href).name}")
61
  model_paths.append(asset.href)
62
 
63
  if not model_paths:
64
+ raise ValueError("No .pt2 files found in STAC item assets.")
65
 
66
+ print(f"Loading {len(model_paths)} .pt2 models (on CPU)...")
67
 
68
  model_paths.sort()
69
  models = [torch.export.load(p).module() for p in model_paths]
70
 
71
  cloud_model = EnsembleModel(*models, mode=mode)
72
 
73
+ print("Ensemble loaded successfully on CPU.")
74
  return cloud_model
75
 
76
 
 
 
77
  def define_iteration(dimension: tuple, chunk_size: int, overlap: int = 0):
78
  """
79
+ Defines iteration strategy to traverse the image with overlap.
80
  """
81
  dimy, dimx = dimension
82
  if chunk_size > max(dimx, dimy):
 
92
 
93
  def fix_lastchunk(iterchunks, s2dim, chunk_size):
94
  """
95
+ Adjusts last chunks to prevent them from exceeding boundaries.
96
  """
97
  itercontainer = []
98
  for index_i, index_j in iterchunks:
 
101
  if index_j + chunk_size > s2dim[1]:
102
  index_j = max(s2dim[1] - chunk_size, 0)
103
  itercontainer.append((index_i, index_j))
104
+ return list(set(itercontainer)) # Returns unique values just in case
105
 
106
 
107
+ def predict_large(
108
+ image: np.ndarray,
109
+ model: torch.nn.Module,
110
  chunk_size: int = 512,
111
+ overlap: int = 64,
112
+ device: str = "cpu",
113
+ nodata: float = 0.0
114
  ) -> np.ndarray:
115
  """
116
+ Predict a full 'image' (C, H, W) using overlapping patches.
117
 
118
+ This is a modified version of 'predict_large_v3' that:
119
+ 1. Accepts an np.ndarray instead of file paths.
120
+ 2. Returns an np.ndarray (probability mask) instead of saving to disk.
121
+ 3. Handles tiles smaller than chunk_size via padding.
122
 
123
+ Compatible with:
124
+ - Normal models (with .eval())
125
+ - Exported models (.pt2) and Ensembles (without .eval())
126
  """
127
 
128
+ # 1) Validate input array dimensions
129
+ if image.ndim != 3:
130
+ raise ValueError(f"Input array must be (C, H, W). Received {image.shape}")
131
 
132
+ bands, height, width = image.shape
133
+ print(f"Starting prediction on array with shape: {(bands, height, width)}")
134
 
135
+ # 2) Initialize output array (1-band float) with nodata values
136
+ output_array = np.full((1, height, width), nodata, dtype=np.float32)
137
 
138
  # 3) Get the list of tile offsets
139
  coords = define_iteration(
 
142
  overlap=overlap
143
  )
144
 
145
+ # 4) Prepare model (compatibility logic for .pt2 models)
146
  try:
147
+ model.eval()
148
+ for p in model.parameters():
149
  p.requires_grad = False
150
+ model = model.to(device)
151
  except (NotImplementedError, AttributeError):
152
+ # Exported model (.pt2) or EnsembleModel
153
+ model = model.to(device)
154
 
155
+ # 5) Iterate over tiles (using numpy only)
156
  for idx, (row_off, col_off) in enumerate(tqdm(coords, desc="Inference")):
157
 
158
+ # (a) Read chunk (numpy slicing)
159
+ patch = image[
160
  :,
161
  row_off : row_off + chunk_size,
162
  col_off : col_off + chunk_size
163
  ]
164
 
165
+ # (b) Convert to tensor and handle padding if tile is smaller than chunk_size
166
  patch_tensor = torch.from_numpy(patch).float().unsqueeze(0).to(device)
167
+ _, _, h_tile, w_tile = patch_tensor.shape
168
+
169
+ # Calculate padding needed
170
+ pad_h = chunk_size - h_tile
171
+ pad_w = chunk_size - w_tile
172
+
173
+ # Apply padding if necessary
174
+ if pad_h > 0 or pad_w > 0:
175
+ patch_tensor = torch.nn.functional.pad(
176
+ patch_tensor, (0, pad_w, 0, pad_h), "constant", nodata
177
+ )
178
+
179
+ # Create mask for nodata areas (all bands are nodata)
180
+ mask_all = (patch_tensor == nodata).all(dim=1, keepdim=True)
181
+
182
+ # Forward pass
183
  with torch.no_grad():
184
+ # Assume the model (or ensemble) returns probabilities (B, 1, H, W)
185
+ probs = model(patch_tensor)
186
+
187
+ # Apply nodata mask to output
188
+ probs = probs.masked_fill(mask_all, nodata)
189
+
190
+ # Remove batch dimension and ensure (1, H, W)
191
+ if probs.ndim == 4:
192
+ probs = probs.squeeze(0) # (1, H, W)
193
 
194
+ # Convert to numpy
195
+ result = probs.cpu().numpy() # (1, H, W)
196
 
197
+ # (c-f) Logic for partial writing
198
  if col_off == 0:
199
  offset_x = 0
200
  else:
 
219
  length_y = chunk_size - (overlap // 2)
220
  sub_y_start = overlap // 2 if row_off != 0 else 0
221
 
222
+ # Ensure we don't exceed array bounds
223
+ if offset_y + length_y > height:
224
+ length_y = height - offset_y
225
+ if offset_x + length_x > width:
226
+ length_x = width - offset_x
227
+
228
+ # Extract the valid region from the result
229
  to_write = result[
230
  :,
231
  sub_y_start : sub_y_start + length_y,
232
  sub_x_start : sub_x_start + length_x
233
  ]
234
 
235
+ # Write to the output numpy array
236
  output_array[
237
  :,
238
  offset_y : offset_y + length_y,
239
  offset_x : offset_x + length_x
240
  ] = to_write
241
 
242
+ print("Prediction completed.")
243
+ # Return the array of shape (1, H, W)
244
  return output_array