satcube 0.1.0__tar.gz → 0.1.1__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.

Potentially problematic release.


This version of satcube might be problematic. Click here for more details.

satcube-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.1
2
+ Name: satcube
3
+ Version: 0.1.1
4
+ Summary: A Python package to create cloud-free monthly composites by fusing Landsat and Sentinel-2 data.
5
+ Home-page: https://github.com/IPL-UV/satcube
6
+ Author: Cesar Aybar
7
+ Author-email: fcesar.aybar@uv.es
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Provides-Extra: full
14
+ Requires-Dist: cubexpress (>=0.1.2)
15
+ Requires-Dist: earthengine-api (>=0.1.4.0) ; extra == "full"
16
+ Requires-Dist: fastcubo (>=0.0.999)
17
+ Requires-Dist: mlstac (>=0.5.0)
18
+ Requires-Dist: numpy (>=1.25.0)
19
+ Requires-Dist: pandas (>=2.0.0)
20
+ Requires-Dist: pydantic (>=2.8.0)
21
+ Requires-Dist: rasterio (>=1.2.0) ; extra == "full"
22
+ Requires-Dist: requests (>=2.26.0)
23
+ Requires-Dist: satalign (>=0.0.999)
24
+ Requires-Dist: scikit-learn (>=1.2.0) ; extra == "full"
25
+ Requires-Dist: segmentation-models-pytorch (>=0.3.0) ; extra == "full"
26
+ Requires-Dist: torch (>=2.0.0) ; extra == "full"
27
+ Requires-Dist: xarray (>=2023.7.0)
28
+ Project-URL: Documentation, https://ipl-uv.github.io/satcube/
29
+ Project-URL: Repository, https://github.com/IPL-UV/satcube
30
+ Description-Content-Type: text/markdown
31
+
32
+ #
33
+
34
+ <p align="center">
35
+ <img src="https://huggingface.co/datasets/JulioContrerasH/DataMLSTAC/resolve/main/banner_satcube.png" width="33%">
36
+ </p>
37
+
38
+ <p align="center">
39
+ <em>A Python package for managing Sentinel-2 satellite data cubes</em> 🚀
40
+ </p>
41
+
42
+ <p align="center">
43
+ <a href='https://pypi.python.org/pypi/satcube'>
44
+ <img src='https://img.shields.io/pypi/v/satcube.svg' alt='PyPI' />
45
+ </a>
46
+ <a href="https://opensource.org/licenses/MIT" target="_blank">
47
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
48
+ </a>
49
+ <a href="https://github.com/psf/black" target="_blank">
50
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Black">
51
+ </a>
52
+ <a href="https://pycqa.github.io/isort/" target="_blank">
53
+ <img src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336" alt="isort">
54
+ </a>
55
+ </p>
56
+
57
+ ---
58
+
59
+ **GitHub**: [https://github.com/IPL-UV/satcube](https://github.com/IPL-UV/satcube) 🌐
60
+
61
+ **PyPI**: [https://pypi.org/project/satcube/](https://pypi.org/project/satcube/) 🛠️
62
+
63
+ ---
64
+
65
+ ## **Overview** 📊
66
+
67
+ **satcube** is a Python package designed for efficient management, processing, and analysis of Sentinel-2 satellite image cubes. It allows for downloading, cloud masking, gap filling, and super-resolving Sentinel-2 imagery, as well as creating monthly composites and performing interpolation.
68
+
69
+ ## **Key Features** ✨
70
+ - **Satellite image download**: Retrieve Sentinel-2 images from Earth Engine efficiently. 🛰️
71
+ - **Cloud masking**: Automatically remove clouds from Sentinel-2 images. ☁️
72
+ - **Gap filling**: Fill missing data using methods like linear interpolation and histogram matching. 🧩
73
+ - **Super-resolution**: Apply super-resolution models to enhance image quality. 🔍
74
+ - **Monthly composites**: Aggregate images into monthly composites with various statistical methods. 📅
75
+ - **Temporal smoothing**: Smooth reflectance values across time using interpolation techniques. 📈
76
+ ## **Installation** ⚙️
77
+
78
+ Install the latest version from PyPI:
79
+
80
+ ```bash
81
+ pip install satcube
82
+ ```
83
+
84
+ ## **How to use** 🛠️
85
+
86
+ ### **Basic usage: working with sentinel-2 data** 🌍
87
+
88
+ #### **Load libraries**
89
+
90
+ ```python
91
+ import ee
92
+ import satcube
93
+ ```
94
+
95
+ #### **Authenticate and initialize earth engine**
96
+
97
+ ```python
98
+ ee.Authenticate()
99
+ ee.Initialize(project="ee-csaybar-real")
100
+ ```
101
+ #### **Download model weights**
102
+ ```python
103
+ outpath = satcube.download_weights(path="weights")
104
+ ```
105
+
106
+ #### **Create a satellite dataCube**
107
+ ```python
108
+ datacube = satcube.SatCube(
109
+ coordinates=(-77.68598590138802,-8.888223962022263),
110
+ sensor=satcube.Sentinel2(weight_path=outpath, edge_size=384),
111
+ output_dir="wendy01",
112
+ max_workers=12,
113
+ device="cuda",
114
+ )
115
+ ```
116
+
117
+
118
+ ### **Query and process sentinel-2 data** 🛰️
119
+
120
+ #### **Query the sentinel-2 image collection**
121
+
122
+ ```python
123
+ # Query the Sentinel-2 image collection
124
+ table_query = datacube.metadata_s2()
125
+
126
+ # Filter images based on cloud cover and remove duplicates
127
+ table_query_subset = table_query[table_query["cs_cdf"] > 0.30]
128
+ table_query_subset = table_query_subset.drop_duplicates(subset="img_date")
129
+ mgrs_tile_max = table_query_subset["mgrs_title"].value_counts().idxmax()
130
+ table_query_subset = table_query_subset[table_query_subset["mgrs_title"] == mgrs_tile_max]
131
+ ```
132
+
133
+ #### **Download sentinel-2 images**
134
+
135
+ ```python
136
+ table_download = datacube.download_s2_image(table_query_subset)
137
+ ```
138
+ #### **Cloud masking**
139
+
140
+ ```python
141
+ # Remove clouds from the images
142
+ table_nocloud = datacube.cloudmasking_s2(table_download)
143
+ table_nocloud = table_nocloud[table_nocloud["cloud_cover"] < 0.75]
144
+ table_nocloud.reset_index(drop=True, inplace=True)
145
+ ```
146
+
147
+ #### **Gap filling**
148
+
149
+ ```python
150
+ # Fill missing data in the images
151
+ table_nogaps = datacube.gapfilling_s2(table_nocloud)
152
+ table_nogaps = table_nogaps[table_nogaps["match_error"] < 0.1]
153
+ ```
154
+ ### **Monthly composites and image smoothing 📅**
155
+
156
+ #### **Create monthly composites**
157
+
158
+ ```python
159
+ # Generate monthly composites
160
+ table_composites = datacube.monthly_composites_s2(
161
+ table_nogaps, agg_method="median", date_range=("2016-01-01", "2024-07-31")
162
+ )
163
+ ```
164
+
165
+ #### **Interpolate missing data**
166
+
167
+ ```python
168
+ # Interpolate missing months if necessary
169
+ table_interpolate = datacube.interpolate_s2(table=table_composites)
170
+ ```
171
+
172
+ #### **Smooth reflectance values**
173
+
174
+ ```python
175
+ # Smooth reflectance values across time
176
+ table_smooth = datacube.smooth_s2(table=table_interpolate)
177
+ ```
178
+
179
+ ### **Super-resolution and visualization** 📐
180
+
181
+
182
+
183
+ #### **Super-resolution**
184
+
185
+ ```python
186
+ # Apply super-resolution to the image cube
187
+ # table_final = datacube.super_s2(table_smooth)
188
+ ```
189
+
190
+
191
+ #### **Display images**
192
+
193
+ ```python
194
+ # Display the images from the data cube
195
+ datacube.display_images(table=table_smooth)
196
+ ```
197
+
198
+ #### **Create a GIF**
199
+
200
+ ```python
201
+ # !apt-get install imagemagick
202
+ import os
203
+ os.system("convert -delay 20 -loop 0 wendy01/z_s2_07_smoothed_png/temp_07*.png animation.gif")
204
+
205
+ from IPython.display import Image
206
+ Image(filename='animation.gif', width=500)
207
+ ```
208
+
209
+ <p align="center">
210
+ <img src="https://huggingface.co/datasets/JulioContrerasH/DataMLSTAC/resolve/main/gif_satcube.gif" width="100%">
211
+ </p>
212
+
213
+ #### **Smooth reflectance values**
214
+
215
+ ```python
216
+ # Smooth reflectance values across time
217
+ table_smooth = datacube.smooth_s2(table=table_interpolate)
218
+ ```
219
+
220
+ ## **Supported features and filters** ✨
221
+
222
+ - **Cloud masking:** Efficient removal of clouds from satellite images.
223
+ - **Resampling methods:** Various methods for resampling and aligning imagery.
224
+ - **Super-resolution:** ONNX-based models for improving image resolution.
@@ -0,0 +1,193 @@
1
+ #
2
+
3
+ <p align="center">
4
+ <img src="https://huggingface.co/datasets/JulioContrerasH/DataMLSTAC/resolve/main/banner_satcube.png" width="33%">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <em>A Python package for managing Sentinel-2 satellite data cubes</em> 🚀
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href='https://pypi.python.org/pypi/satcube'>
13
+ <img src='https://img.shields.io/pypi/v/satcube.svg' alt='PyPI' />
14
+ </a>
15
+ <a href="https://opensource.org/licenses/MIT" target="_blank">
16
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
17
+ </a>
18
+ <a href="https://github.com/psf/black" target="_blank">
19
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Black">
20
+ </a>
21
+ <a href="https://pycqa.github.io/isort/" target="_blank">
22
+ <img src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336" alt="isort">
23
+ </a>
24
+ </p>
25
+
26
+ ---
27
+
28
+ **GitHub**: [https://github.com/IPL-UV/satcube](https://github.com/IPL-UV/satcube) 🌐
29
+
30
+ **PyPI**: [https://pypi.org/project/satcube/](https://pypi.org/project/satcube/) 🛠️
31
+
32
+ ---
33
+
34
+ ## **Overview** 📊
35
+
36
+ **satcube** is a Python package designed for efficient management, processing, and analysis of Sentinel-2 satellite image cubes. It allows for downloading, cloud masking, gap filling, and super-resolving Sentinel-2 imagery, as well as creating monthly composites and performing interpolation.
37
+
38
+ ## **Key Features** ✨
39
+ - **Satellite image download**: Retrieve Sentinel-2 images from Earth Engine efficiently. 🛰️
40
+ - **Cloud masking**: Automatically remove clouds from Sentinel-2 images. ☁️
41
+ - **Gap filling**: Fill missing data using methods like linear interpolation and histogram matching. 🧩
42
+ - **Super-resolution**: Apply super-resolution models to enhance image quality. 🔍
43
+ - **Monthly composites**: Aggregate images into monthly composites with various statistical methods. 📅
44
+ - **Temporal smoothing**: Smooth reflectance values across time using interpolation techniques. 📈
45
+ ## **Installation** ⚙️
46
+
47
+ Install the latest version from PyPI:
48
+
49
+ ```bash
50
+ pip install satcube
51
+ ```
52
+
53
+ ## **How to use** 🛠️
54
+
55
+ ### **Basic usage: working with sentinel-2 data** 🌍
56
+
57
+ #### **Load libraries**
58
+
59
+ ```python
60
+ import ee
61
+ import satcube
62
+ ```
63
+
64
+ #### **Authenticate and initialize earth engine**
65
+
66
+ ```python
67
+ ee.Authenticate()
68
+ ee.Initialize(project="ee-csaybar-real")
69
+ ```
70
+ #### **Download model weights**
71
+ ```python
72
+ outpath = satcube.download_weights(path="weights")
73
+ ```
74
+
75
+ #### **Create a satellite dataCube**
76
+ ```python
77
+ datacube = satcube.SatCube(
78
+ coordinates=(-77.68598590138802,-8.888223962022263),
79
+ sensor=satcube.Sentinel2(weight_path=outpath, edge_size=384),
80
+ output_dir="wendy01",
81
+ max_workers=12,
82
+ device="cuda",
83
+ )
84
+ ```
85
+
86
+
87
+ ### **Query and process sentinel-2 data** 🛰️
88
+
89
+ #### **Query the sentinel-2 image collection**
90
+
91
+ ```python
92
+ # Query the Sentinel-2 image collection
93
+ table_query = datacube.metadata_s2()
94
+
95
+ # Filter images based on cloud cover and remove duplicates
96
+ table_query_subset = table_query[table_query["cs_cdf"] > 0.30]
97
+ table_query_subset = table_query_subset.drop_duplicates(subset="img_date")
98
+ mgrs_tile_max = table_query_subset["mgrs_title"].value_counts().idxmax()
99
+ table_query_subset = table_query_subset[table_query_subset["mgrs_title"] == mgrs_tile_max]
100
+ ```
101
+
102
+ #### **Download sentinel-2 images**
103
+
104
+ ```python
105
+ table_download = datacube.download_s2_image(table_query_subset)
106
+ ```
107
+ #### **Cloud masking**
108
+
109
+ ```python
110
+ # Remove clouds from the images
111
+ table_nocloud = datacube.cloudmasking_s2(table_download)
112
+ table_nocloud = table_nocloud[table_nocloud["cloud_cover"] < 0.75]
113
+ table_nocloud.reset_index(drop=True, inplace=True)
114
+ ```
115
+
116
+ #### **Gap filling**
117
+
118
+ ```python
119
+ # Fill missing data in the images
120
+ table_nogaps = datacube.gapfilling_s2(table_nocloud)
121
+ table_nogaps = table_nogaps[table_nogaps["match_error"] < 0.1]
122
+ ```
123
+ ### **Monthly composites and image smoothing 📅**
124
+
125
+ #### **Create monthly composites**
126
+
127
+ ```python
128
+ # Generate monthly composites
129
+ table_composites = datacube.monthly_composites_s2(
130
+ table_nogaps, agg_method="median", date_range=("2016-01-01", "2024-07-31")
131
+ )
132
+ ```
133
+
134
+ #### **Interpolate missing data**
135
+
136
+ ```python
137
+ # Interpolate missing months if necessary
138
+ table_interpolate = datacube.interpolate_s2(table=table_composites)
139
+ ```
140
+
141
+ #### **Smooth reflectance values**
142
+
143
+ ```python
144
+ # Smooth reflectance values across time
145
+ table_smooth = datacube.smooth_s2(table=table_interpolate)
146
+ ```
147
+
148
+ ### **Super-resolution and visualization** 📐
149
+
150
+
151
+
152
+ #### **Super-resolution**
153
+
154
+ ```python
155
+ # Apply super-resolution to the image cube
156
+ # table_final = datacube.super_s2(table_smooth)
157
+ ```
158
+
159
+
160
+ #### **Display images**
161
+
162
+ ```python
163
+ # Display the images from the data cube
164
+ datacube.display_images(table=table_smooth)
165
+ ```
166
+
167
+ #### **Create a GIF**
168
+
169
+ ```python
170
+ # !apt-get install imagemagick
171
+ import os
172
+ os.system("convert -delay 20 -loop 0 wendy01/z_s2_07_smoothed_png/temp_07*.png animation.gif")
173
+
174
+ from IPython.display import Image
175
+ Image(filename='animation.gif', width=500)
176
+ ```
177
+
178
+ <p align="center">
179
+ <img src="https://huggingface.co/datasets/JulioContrerasH/DataMLSTAC/resolve/main/gif_satcube.gif" width="100%">
180
+ </p>
181
+
182
+ #### **Smooth reflectance values**
183
+
184
+ ```python
185
+ # Smooth reflectance values across time
186
+ table_smooth = datacube.smooth_s2(table=table_interpolate)
187
+ ```
188
+
189
+ ## **Supported features and filters** ✨
190
+
191
+ - **Cloud masking:** Efficient removal of clouds from satellite images.
192
+ - **Resampling methods:** Various methods for resampling and aligning imagery.
193
+ - **Super-resolution:** ONNX-based models for improving image resolution.
@@ -0,0 +1,54 @@
1
+ [tool.poetry]
2
+ name = "satcube"
3
+ version = "0.1.1"
4
+ description = "A Python package to create cloud-free monthly composites by fusing Landsat and Sentinel-2 data."
5
+ authors = ["Cesar Aybar <fcesar.aybar@uv.es>"]
6
+ repository = "https://github.com/IPL-UV/satcube"
7
+ documentation = "https://ipl-uv.github.io/satcube/"
8
+ readme = "README.md"
9
+ packages = [{ include = "satcube" }]
10
+
11
+ [tool.poetry.dependencies]
12
+ python = ">=3.10,<4.0"
13
+ cubexpress = ">=0.1.2"
14
+ mlstac = ">=0.5.0"
15
+ fastcubo = ">=0.0.999"
16
+ satalign = ">=0.0.999"
17
+ torch = ">=2.0.0"
18
+ pandas = ">=2.0.0"
19
+ pydantic = ">=2.8.0"
20
+ earthengine-api = ">=0.1.4.0"
21
+ numpy = ">=1.25.0"
22
+ rasterio = ">=1.2.0"
23
+ requests = ">=2.26.0"
24
+ scikit-learn = ">=1.2.0"
25
+ segmentation-models-pytorch = ">=0.3.0"
26
+ xarray = ">=2023.7.0"
27
+
28
+ [tool.poetry.extras]
29
+ full = [
30
+ "torch",
31
+ "segmentation-models-pytorch",
32
+ "scikit-learn",
33
+ "rasterio",
34
+ "earthengine-api",
35
+ "fastparquet"
36
+ ]
37
+
38
+ # --- tooling fixes ----------------------------------------------------------
39
+ [tool.mypy]
40
+ files = ["satcube"]
41
+ disallow_untyped_defs = true
42
+ disallow_any_unimported = true
43
+ no_implicit_optional = true
44
+ check_untyped_defs = true
45
+ warn_return_any = true
46
+ warn_unused_ignores = true
47
+ show_error_codes = true
48
+
49
+ [tool.ruff]
50
+ target-version = "py310"
51
+ line-length = 120
52
+ fix = true
53
+ select = ["YTT","S","B","A","C4","T10","SIM","I","C90","E","W","F","PGH","UP","RUF","TRY"]
54
+ ignore = ["E501","E731"]
@@ -0,0 +1,160 @@
1
+ """Predict cloud masks for Sentinel-2 GeoTIFFs with the SEN2CloudEnsemble model.
2
+
3
+ The callable :pyfunc:`cloud_masking` accepts **either** a single ``.tif`` file
4
+ or a directory tree; in both cases it writes a masked copy of every image (and,
5
+ optionally, the binary mask) to *output*.
6
+
7
+ Example
8
+ -------
9
+ >>> from satcube.cloud_detection import cloud_masking
10
+ >>> cloud_masking("~/s2/input", "~/s2/output", device="cuda")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import time
16
+ from pathlib import Path
17
+ from typing import List
18
+
19
+ import mlstac
20
+ import numpy as np
21
+ import rasterio as rio
22
+ import torch
23
+
24
+ from satcube.utils import DeviceManager, _reset_gpu
25
+
26
+
27
+ def cloud_masking(
28
+ input: str | Path, # noqa: A002 (shadowing built-in is OK here)
29
+ output: str | Path,
30
+ *,
31
+ tile: int = 512,
32
+ pad: int = 64,
33
+ save_mask: bool = False,
34
+ device: str = "cpu",
35
+ max_pix_cpu: float = 7.0e7,
36
+ ) -> List[Path]:
37
+ """Write cloud-masked Sentinel-2 images.
38
+
39
+ Parameters
40
+ ----------
41
+ input
42
+ Path to a single ``.tif`` file **or** a directory containing them.
43
+ output
44
+ Destination directory (created if missing).
45
+ tile, pad
46
+ Tile size and padding (pixels) when tiling is required.
47
+ save_mask
48
+ If *True*, store the binary mask alongside the masked image.
49
+ device
50
+ Torch device for inference, e.g. ``"cpu"`` or ``"cuda:0"``.
51
+ max_pix_cpu
52
+ Tile images larger than this when running on CPU.
53
+
54
+ Returns
55
+ -------
56
+ list[pathlib.Path]
57
+ Paths to the generated masked images.
58
+ """
59
+ t_start = time.perf_counter()
60
+
61
+ src = Path(input).expanduser().resolve()
62
+ dst_dir = Path(output).expanduser().resolve()
63
+ dst_dir.mkdir(parents=True, exist_ok=True)
64
+
65
+ # Collect files to process -------------------------------------------------
66
+ tif_paths: list[Path]
67
+ if src.is_dir():
68
+ tif_paths = [p for p in src.rglob("*.tif")]
69
+ elif src.is_file() and src.suffix.lower() == ".tif":
70
+ tif_paths = [src]
71
+ src = src.parent # for relative-path bookkeeping below
72
+ else:
73
+ raise ValueError(f"Input must be a .tif or directory, got: {src}")
74
+
75
+ if not tif_paths:
76
+ print(f"[cloud_masking] No .tif files found in {src}")
77
+ return []
78
+
79
+ experiment = mlstac.load("SEN2CloudEnsemble")
80
+ dm = DeviceManager(experiment, init_device=device)
81
+
82
+ masked_paths: list[Path] = []
83
+
84
+ # -------------------------------------------------------------------------
85
+ for idx, tif_path in enumerate(tif_paths, 1):
86
+ rel = tif_path.relative_to(src)
87
+ out_dir = dst_dir / rel.parent
88
+ out_dir.mkdir(parents=True, exist_ok=True)
89
+
90
+ mask_path = out_dir / f"{tif_path.stem}_cloudmask.tif"
91
+ masked_path = out_dir / f"{tif_path.stem}_masked.tif"
92
+
93
+ with rio.open(tif_path) as src_img:
94
+ profile = src_img.profile
95
+ h, w = src_img.height, src_img.width
96
+
97
+ mask_prof = profile.copy()
98
+ mask_prof.update(driver="GTiff", count=1, dtype="uint8", nodata=255)
99
+
100
+ do_tiling = (dm.device == "cuda") or (h * w > max_pix_cpu)
101
+ full_mask = np.full((h, w), 255, np.uint8)
102
+
103
+ t0 = time.perf_counter()
104
+
105
+ # ----------------------- inference -----------------------------------
106
+ if not do_tiling: # full frame
107
+ with rio.open(tif_path) as src_img, torch.inference_mode():
108
+ img = src_img.read().astype(np.float32) / 1e4
109
+ h32, w32 = (h + 31) // 32 * 32, (w + 31) // 32 * 32
110
+ pad_b, pad_r = h32 - h, w32 - w
111
+ tensor = torch.from_numpy(img).unsqueeze(0)
112
+ if pad_b or pad_r:
113
+ tensor = torch.nn.functional.pad(tensor, (0, pad_r, 0, pad_b))
114
+ mask = dm.model(tensor.to(dm.device)).squeeze(0)
115
+ full_mask[:] = mask[..., :h, :w].cpu().numpy().astype(np.uint8)
116
+ else: # tiled
117
+ with rio.open(tif_path) as src_img, torch.inference_mode():
118
+ for y0 in range(0, h, tile):
119
+ for x0 in range(0, w, tile):
120
+ y0r, x0r = max(0, y0 - pad), max(0, x0 - pad)
121
+ y1r, x1r = min(h, y0 + tile + pad), min(w, x0 + tile + pad)
122
+ win = rio.windows.Window(x0r, y0r, x1r - x0r, y1r - y0r)
123
+
124
+ patch = src_img.read(window=win).astype(np.float32) / 1e4
125
+ tensor = torch.from_numpy(patch).unsqueeze(0).to(dm.device)
126
+ mask = dm.model(tensor).squeeze(0).cpu().numpy().astype(np.uint8)
127
+
128
+ y_in0 = pad if y0r else 0
129
+ x_in0 = pad if x0r else 0
130
+ y_in1 = mask.shape[0] - (pad if y1r < h else 0)
131
+ x_in1 = mask.shape[1] - (pad if x1r < w else 0)
132
+ core = mask[y_in0:y_in1, x_in0:x_in1]
133
+ full_mask[y0 : y0 + core.shape[0], x0 : x0 + core.shape[1]] = core
134
+
135
+ # ----------------------- output --------------------------------------
136
+ if save_mask:
137
+ with rio.open(mask_path, "w", **mask_prof) as dst:
138
+ dst.write(full_mask, 1)
139
+
140
+ with rio.open(tif_path) as src_img:
141
+ data = src_img.read()
142
+ img_prof = src_img.profile.copy()
143
+
144
+ masked = data.copy()
145
+ masked[:, full_mask != 0] = 65535
146
+ img_prof.update(dtype="uint16", nodata=65535)
147
+
148
+ with rio.open(masked_path, "w", **img_prof) as dst:
149
+ dst.write(masked)
150
+
151
+ masked_paths.append(masked_path)
152
+ dt = time.perf_counter() - t0
153
+ print(f"[{idx}/{len(tif_paths)}] {rel} → done in {dt:.1f}s")
154
+
155
+ if dm.device == "cuda":
156
+ _reset_gpu()
157
+
158
+ total_time = time.perf_counter() - t_start
159
+ print(f"Processed {len(masked_paths)} image(s) in {total_time:.1f}s.")
160
+ return masked_paths
@@ -0,0 +1,65 @@
1
+ import pathlib
2
+ import ee
3
+ import cubexpress
4
+ import pandas as pd
5
+
6
+
7
+ def download_data(
8
+ *, # keyword-only
9
+ lon: float,
10
+ lat: float,
11
+ cloud_max: int = 40,
12
+ edge_size: int = 2_048,
13
+ start: str,
14
+ end: str,
15
+ output: str = "raw",
16
+ scale: int = 10,
17
+ nworks: int = 4,
18
+ mosaic: bool = True,
19
+ auto_init_gee: bool = True,
20
+ ) -> pd.DataFrame:
21
+ """
22
+ Download a Sentinel cube for (lon, lat) and return its metadata.
23
+
24
+ Parameters
25
+ ----------
26
+ lon, lat Center point in degrees.
27
+ cloud_max Max cloud cover (%).
28
+ edge_size Square side length (m).
29
+ start, end YYYY-MM-DD date range.
30
+ output Folder for GeoTIFFs.
31
+ scale Pixel size (m).
32
+ nworks Parallel workers.
33
+ mosaic Merge scenes per date.
34
+ auto_init_gee Call ee.Initialize() if needed.
35
+
36
+ Returns
37
+ -------
38
+ pandas.DataFrame
39
+ Scene catalogue used for the request.
40
+ """
41
+ # EE ready
42
+ if auto_init_gee:
43
+ try:
44
+ ee.Initialize()
45
+ except ee.EEException:
46
+ ee.Authenticate(); ee.Initialize()
47
+
48
+ # Filter scenes
49
+ df = cubexpress.cloud_table(
50
+ lon=lon,
51
+ lat=lat,
52
+ edge_size=edge_size,
53
+ scale=scale,
54
+ cloud_max=cloud_max,
55
+ start=start,
56
+ end=end,
57
+ )
58
+
59
+ # Build requests + ensure dir
60
+ requests = cubexpress.table_to_requestset(df, mosaic=mosaic)
61
+ pathlib.Path(output).mkdir(parents=True, exist_ok=True)
62
+
63
+ # Download cube
64
+ cubexpress.get_cube(requests, output, nworks)
65
+ return df
@@ -0,0 +1,82 @@
1
+ import ee
2
+ import cubexpress
3
+ import pathlib
4
+ from typing import Optional
5
+ from datetime import datetime
6
+
7
+ def download_data(
8
+ lon: float,
9
+ lat: float,
10
+ cs_cdf: Optional[float] = 0.6,
11
+ buffer_size: Optional[int] = 1280,
12
+ start_date: Optional[str] = "2015-01-01",
13
+ end_date: Optional[str] = datetime.today().strftime('%Y-%m-%d'),
14
+ outfolder: Optional[str] = "raw/"
15
+ ) -> pathlib.Path:
16
+ """
17
+ Download Sentinel-2 imagery data using cubexpress and Earth Engine API.
18
+
19
+ Args:
20
+ lon (float): Longitude of the point of interest.
21
+ lat (float): Latitude of the point of interest.
22
+ cs_cdf (Optional[float]): Cloud mask threshold (default 0.6).
23
+ buffer_size (Optional[int]): Buffer size for image extraction (default 1280).
24
+ start_date (Optional[str]): Start date for image filtering (default "2015-01-01").
25
+ end_date (Optional[str]): End date for image filtering (default today’s date).
26
+ outfolder (Optional[str]): Output folder to save images (default "raw/").
27
+
28
+ Returns:
29
+ pathlib.Path: Path to the folder where the data is stored.
30
+ """
31
+
32
+ # Initialize Earth Engine
33
+ ee.Initialize(project="ee-julius013199")
34
+
35
+ # Define point of interest
36
+ point = ee.Geometry.Point([lon, lat])
37
+
38
+ # Filter image collection by location and date
39
+ collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") \
40
+ .filterBounds(point) \
41
+ .filterDate(start_date, end_date)
42
+
43
+ # Get image IDs
44
+ image_ids = collection.aggregate_array('system:id').getInfo()
45
+
46
+ # Cloud mask function
47
+ def cloud_mask(image) -> ee.Image:
48
+ """Apply cloud mask to the image."""
49
+ return image.select('MSK_CLDPRB').lt(20)
50
+
51
+ # Apply cloud mask
52
+ collection = collection.map(cloud_mask)
53
+
54
+ # Generate geotransform for cubexpress
55
+ geotransform = cubexpress.lonlat2rt(lon=lon, lat=lat, edge_size=buffer_size, scale=10)
56
+
57
+ # Prepare requests for cubexpress
58
+ requests = [
59
+ cubexpress.Request(
60
+ id=f"s2test_{i}",
61
+ raster_transform=geotransform,
62
+ bands=["B4", "B3", "B2"], # RGB bands
63
+ image=ee.Image(image_id).divide(10000) # Adjust image scaling
64
+ )
65
+ for i, image_id in enumerate(image_ids)
66
+ ]
67
+
68
+ # Create request set
69
+ cube_requests = cubexpress.RequestSet(requestset=requests)
70
+
71
+ # Set output folder
72
+ output_path = pathlib.Path(outfolder)
73
+
74
+ # Download the data
75
+ cubexpress.getcube(
76
+ request=cube_requests,
77
+ output_path=output_path,
78
+ nworkers=4,
79
+ max_deep_level=5
80
+ )
81
+
82
+ return output_path
@@ -7,7 +7,7 @@ import pandas as pd
7
7
  import torch
8
8
 
9
9
  from satcube.dataclass import Sensor
10
- from satcube.utils import (aligned_s2, cloudmasking_s2, display_images,
10
+ from satcube.utils_old import (aligned_s2, cloudmasking_s2, display_images,
11
11
  gapfilling_s2, intermediate_process, interpolate_s2,
12
12
  metadata_s2, monthly_composites_s2, smooth_s2, super_s2)
13
13
 
@@ -252,7 +252,7 @@ class SatCube:
252
252
  out_table["folder"] = out_folder
253
253
 
254
254
  return out_table
255
-
255
+
256
256
  def monthly_composites_s2(
257
257
  self,
258
258
  table: Optional[pd.DataFrame],
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ import gc
4
+ from typing import Any, Optional
5
+
6
+ import torch
7
+
8
+
9
+ def _reset_gpu() -> None:
10
+ """Release CUDA memory and reset allocation statistics.
11
+
12
+ Calling this on a system without a CUDA device is a no-op.
13
+ """
14
+ torch.cuda.empty_cache()
15
+ torch.cuda.reset_peak_memory_stats()
16
+
17
+
18
+ class DeviceManager:
19
+ """Hold a compiled mlstac model and move it between devices on demand."""
20
+
21
+ def __init__(self, experiment: Any, init_device: str = "cpu") -> None:
22
+ """
23
+ Parameters
24
+ ----------
25
+ experiment
26
+ An mlstac experiment exposing ``compiled_model``.
27
+ init_device
28
+ Device where the model is first compiled, e.g. ``"cpu"`` or
29
+ ``"cuda:0"``.
30
+ """
31
+ self._experiment: Any = experiment
32
+ self.device: Optional[str] = None
33
+ self.model: Optional[torch.nn.Module] = None
34
+ self.switch(init_device)
35
+
36
+ def switch(self, new_device: str) -> torch.nn.Module:
37
+ """Return a model compiled for *new_device*, recompiling if needed.
38
+
39
+ Parameters
40
+ ----------
41
+ new_device
42
+ Target device identifier.
43
+
44
+ Returns
45
+ -------
46
+ torch.nn.Module
47
+ The model resident on *new_device*.
48
+
49
+ Raises
50
+ ------
51
+ AssertionError
52
+ If *new_device* requests CUDA but no GPU is available.
53
+ """
54
+ if new_device == self.device:
55
+ return self.model # type: ignore[return-value]
56
+
57
+ if self.model is not None:
58
+ del self.model
59
+ gc.collect()
60
+
61
+ if self.device == "cuda":
62
+ _reset_gpu()
63
+
64
+ if new_device == "cuda":
65
+ assert torch.cuda.is_available(), "CUDA device not detected"
66
+
67
+ print(f"→ Compiling model on {new_device} …")
68
+ self.model = self._experiment.compiled_model(device=new_device, mode="max")
69
+ self.device = new_device
70
+ return self.model
satcube-0.1.0/PKG-INFO DELETED
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: satcube
3
- Version: 0.1.0
4
- Summary: A Python package to create cloud-free monthly composites by fusing Landsat and Sentinel-2 data.
5
- Home-page: https://github.com/IPL-UV/satcube
6
- Author: Cesar Aybar
7
- Author-email: fcesar.aybar@uv.es
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
- Requires-Dist: earthengine-api (>=0.1.4.0)
12
- Requires-Dist: fastcubo (>=0.0.999)
13
- Requires-Dist: matplotlib (>=3.7.0)
14
- Requires-Dist: numpy (>=1.25.0)
15
- Requires-Dist: pandas (>=2.0.0)
16
- Requires-Dist: phicloudmask (>=0.0.2)
17
- Requires-Dist: pydantic (>=2.8.0)
18
- Requires-Dist: rasterio (>=1.2.0)
19
- Requires-Dist: requests (>=2.26.0)
20
- Requires-Dist: satalign (>=0.0.999)
21
- Requires-Dist: scikit-learn (>=1.2.0)
22
- Requires-Dist: segmentation-models-pytorch (>=0.2.0)
23
- Requires-Dist: torch (>=2.0.0)
24
- Requires-Dist: xarray (>=2023.7.0)
25
- Project-URL: Documentation, https://ipl-uv.github.io/satcube/
26
- Project-URL: Repository, https://github.com/IPL-UV/satcube
27
- Description-Content-Type: text/markdown
28
-
29
- # satcube
30
-
31
- [colab code](https://colab.research.google.com/drive/1)
satcube-0.1.0/README.md DELETED
@@ -1,3 +0,0 @@
1
- # satcube
2
-
3
- [colab code](https://colab.research.google.com/drive/1)
@@ -1,116 +0,0 @@
1
- [tool.poetry]
2
- name = "satcube"
3
- version = "0.1.0"
4
- description = "A Python package to create cloud-free monthly composites by fusing Landsat and Sentinel-2 data."
5
- authors = ["Cesar Aybar <fcesar.aybar@uv.es>"]
6
- repository = "https://github.com/IPL-UV/satcube"
7
- documentation = "https://ipl-uv.github.io/satcube/"
8
- readme = "README.md"
9
- packages = [
10
- {include = "satcube"}
11
- ]
12
-
13
- [tool.poetry.dependencies]
14
- python = ">=3.10,<4.0"
15
- fastcubo = ">=0.0.999"
16
- torch = ">=2.0.0"
17
- pandas = ">=2.0.0"
18
- pydantic = ">=2.8.0"
19
- earthengine-api = ">=0.1.4.0"
20
- matplotlib = ">=3.7.0"
21
- numpy = ">=1.25.0"
22
- phicloudmask = ">=0.0.2"
23
- rasterio = ">=1.2.0"
24
- requests = ">=2.26.0"
25
- satalign = ">=0.0.999"
26
- scikit-learn = ">=1.2.0"
27
- segmentation-models-pytorch = ">=0.2.0"
28
- xarray = ">=2023.7.0"
29
-
30
-
31
- [tool.poetry.group.dev.dependencies]
32
- pytest = "^7.2.0"
33
- pytest-cov = "^4.0.0"
34
- deptry = "^0.12.0"
35
- mypy = "^1.5.1"
36
- pre-commit = "^3.4.0"
37
- tox = "^4.11.1"
38
-
39
- [tool.poetry.group.docs.dependencies]
40
- mkdocs = "^1.4.2"
41
- mkdocs-material = "^9.2.7"
42
- mkdocstrings = {extras = ["python"], version = "^0.23.0"}
43
-
44
- [build-system]
45
- requires = ["poetry-core>=1.0.0"]
46
- build-backend = "poetry.core.masonry.api"
47
-
48
- [tool.mypy]
49
- files = ["satcube"]
50
- disallow_untyped_defs = "True"
51
- disallow_any_unimported = "True"
52
- no_implicit_optional = "True"
53
- check_untyped_defs = "True"
54
- warn_return_any = "True"
55
- warn_unused_ignores = "True"
56
- show_error_codes = "True"
57
-
58
- [tool.pytest.ini_options]
59
- testpaths = ["tests"]
60
-
61
- [tool.ruff]
62
- target-version = "py37"
63
- line-length = 120
64
- fix = true
65
- select = [
66
- # flake8-2020
67
- "YTT",
68
- # flake8-bandit
69
- "S",
70
- # flake8-bugbear
71
- "B",
72
- # flake8-builtins
73
- "A",
74
- # flake8-comprehensions
75
- "C4",
76
- # flake8-debugger
77
- "T10",
78
- # flake8-simplify
79
- "SIM",
80
- # isort
81
- "I",
82
- # mccabe
83
- "C90",
84
- # pycodestyle
85
- "E", "W",
86
- # pyflakes
87
- "F",
88
- # pygrep-hooks
89
- "PGH",
90
- # pyupgrade
91
- "UP",
92
- # ruff
93
- "RUF",
94
- # tryceratops
95
- "TRY",
96
- ]
97
- ignore = [
98
- # LineTooLong
99
- "E501",
100
- # DoNotAssignLambda
101
- "E731",
102
- ]
103
-
104
- [tool.ruff.format]
105
- preview = true
106
-
107
- [tool.coverage.report]
108
- skip_empty = true
109
-
110
- [tool.coverage.run]
111
- branch = true
112
- source = ["satcube"]
113
-
114
-
115
- [tool.ruff.per-file-ignores]
116
- "tests/*" = ["S101"]
@@ -1,3 +0,0 @@
1
- from satcube.dataclass import Sentinel2
2
- from satcube.main import SatCube
3
- from satcube.utils import download_weights, monthly_calendar
satcube-0.1.0/setup.py DELETED
@@ -1,43 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['satcube']
6
-
7
- package_data = \
8
- {'': ['*']}
9
-
10
- install_requires = \
11
- ['earthengine-api>=0.1.4.0',
12
- 'fastcubo>=0.0.999',
13
- 'matplotlib>=3.7.0',
14
- 'numpy>=1.25.0',
15
- 'pandas>=2.0.0',
16
- 'phicloudmask>=0.0.2',
17
- 'pydantic>=2.8.0',
18
- 'rasterio>=1.2.0',
19
- 'requests>=2.26.0',
20
- 'satalign>=0.0.999',
21
- 'scikit-learn>=1.2.0',
22
- 'segmentation-models-pytorch>=0.2.0',
23
- 'torch>=2.0.0',
24
- 'xarray>=2023.7.0']
25
-
26
- setup_kwargs = {
27
- 'name': 'satcube',
28
- 'version': '0.1.0',
29
- 'description': 'A Python package to create cloud-free monthly composites by fusing Landsat and Sentinel-2 data.',
30
- 'long_description': '# satcube\n\n[colab code](https://colab.research.google.com/drive/1)',
31
- 'author': 'Cesar Aybar',
32
- 'author_email': 'fcesar.aybar@uv.es',
33
- 'maintainer': 'None',
34
- 'maintainer_email': 'None',
35
- 'url': 'https://github.com/IPL-UV/satcube',
36
- 'packages': packages,
37
- 'package_data': package_data,
38
- 'install_requires': install_requires,
39
- 'python_requires': '>=3.10,<4.0',
40
- }
41
-
42
-
43
- setup(**setup_kwargs)
File without changes
File without changes