eoml 0.9.0__py3-none-any.whl

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 (47) hide show
  1. eoml/__init__.py +74 -0
  2. eoml/automation/__init__.py +7 -0
  3. eoml/automation/configuration.py +105 -0
  4. eoml/automation/dag.py +233 -0
  5. eoml/automation/experience.py +618 -0
  6. eoml/automation/tasks.py +825 -0
  7. eoml/bin/__init__.py +6 -0
  8. eoml/bin/clean_checkpoint.py +146 -0
  9. eoml/bin/land_cover_mapping_toml.py +435 -0
  10. eoml/bin/mosaic_images.py +137 -0
  11. eoml/data/__init__.py +7 -0
  12. eoml/data/basic_geo_data.py +214 -0
  13. eoml/data/dataset_utils.py +98 -0
  14. eoml/data/persistence/__init__.py +7 -0
  15. eoml/data/persistence/generic.py +253 -0
  16. eoml/data/persistence/lmdb.py +379 -0
  17. eoml/data/persistence/serializer.py +82 -0
  18. eoml/raster/__init__.py +7 -0
  19. eoml/raster/band.py +141 -0
  20. eoml/raster/dataset/__init__.py +6 -0
  21. eoml/raster/dataset/extractor.py +604 -0
  22. eoml/raster/raster_reader.py +602 -0
  23. eoml/raster/raster_utils.py +116 -0
  24. eoml/torch/__init__.py +7 -0
  25. eoml/torch/cnn/__init__.py +7 -0
  26. eoml/torch/cnn/augmentation.py +150 -0
  27. eoml/torch/cnn/dataset_evaluator.py +68 -0
  28. eoml/torch/cnn/db_dataset.py +605 -0
  29. eoml/torch/cnn/map_dataset.py +579 -0
  30. eoml/torch/cnn/map_dataset_const_mem.py +135 -0
  31. eoml/torch/cnn/outputs_transformer.py +130 -0
  32. eoml/torch/cnn/torch_utils.py +404 -0
  33. eoml/torch/cnn/training_dataset.py +241 -0
  34. eoml/torch/cnn/windows_dataset.py +120 -0
  35. eoml/torch/dataset/__init__.py +6 -0
  36. eoml/torch/dataset/shade_dataset_tester.py +46 -0
  37. eoml/torch/dataset/shade_tree_dataset_creators.py +537 -0
  38. eoml/torch/model_low_use.py +507 -0
  39. eoml/torch/models.py +282 -0
  40. eoml/torch/resnet.py +437 -0
  41. eoml/torch/sample_statistic.py +260 -0
  42. eoml/torch/trainer.py +782 -0
  43. eoml/torch/trainer_v2.py +253 -0
  44. eoml-0.9.0.dist-info/METADATA +93 -0
  45. eoml-0.9.0.dist-info/RECORD +47 -0
  46. eoml-0.9.0.dist-info/WHEEL +4 -0
  47. eoml-0.9.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,241 @@
1
+ """PyTorch datasets for training from raster images.
2
+
3
+ Provides dataset classes that extract training samples from raster images by sliding
4
+ windows. Includes support for data augmentation, temporal features (year), and
5
+ multi-band output processing.
6
+ """
7
+
8
+ import itertools
9
+
10
+ import numpy as np
11
+ import torch
12
+ from libterra_gis.raster_utils import RasterImage
13
+ from eoml.torch.cnn.augmentation import rotate_flip_transform
14
+ from torch.utils.data import Dataset
15
+
16
+
17
+ class BasicTrainingDataset(Dataset):
18
+ """Training dataset that extracts windows from raster images.
19
+
20
+ Loads raster images, extracts overlapping windows using stride, and stores all
21
+ samples in memory. Applies output function to determine sample validity.
22
+ Currently used for shade generation tasks.
23
+
24
+ Attributes:
25
+ f_transform: Data augmentation function.
26
+ transform_param (np.ndarray, optional): Per-sample augmentation parameters.
27
+ paths (list): Paths to input raster files.
28
+ size (int): Window size for extraction.
29
+ func: Function to process output windows and determine validity.
30
+ stride (int): Stride for window extraction.
31
+ n_out (int): Number of output bands.
32
+ samples (list): List of (input, output) sample tuples.
33
+ """
34
+ def __init__(self, paths, size, stride, n_out, func, f_transform=None, transform_param=None):
35
+ """Initialize BasicTrainingDataset.
36
+
37
+ Args:
38
+ paths (list): List of paths to raster image files.
39
+ size (int): Size of windows to extract.
40
+ stride (int): Stride between consecutive windows.
41
+ n_out (int): Number of output bands (taken from last bands of raster).
42
+ func: Function applied to output windows. Returns None for invalid samples.
43
+ f_transform (callable, optional): Data augmentation function. Defaults to None.
44
+ transform_param (list, optional): Per-sample transform parameters. Defaults to None.
45
+ """
46
+
47
+ self.f_transform = f_transform
48
+
49
+ if transform_param is not None:
50
+ self.transform_param = np.array(transform_param)
51
+ else:
52
+ self.transform_param = transform_param
53
+
54
+ self.paths = paths
55
+ self.size = size
56
+ self.func = func
57
+ self.stride = stride
58
+ self.n_out = n_out
59
+
60
+ self.samples = self.extract()
61
+
62
+ def extract(self):
63
+
64
+ samples = []
65
+
66
+ for path in self.paths:
67
+ data = RasterImage.from_file(path).data
68
+
69
+ bands, height, width = data.shape
70
+
71
+ # print(data.shape)
72
+ # nh = math.floor((height-self.size)/self.stride+1)
73
+ # nw = math.floor((width-self.size)/self.stride+1)
74
+
75
+ for i in range(0, height - self.size + 1, self.stride):
76
+ for j in range(0, width - self.size + 1, self.stride):
77
+ source_w = data[:-self.n_out, i:i + self.size, j:j + self.size]
78
+ output_w = data[-self.n_out:, i:i + self.size, j:j + self.size]
79
+ output_w = self.func(output_w)
80
+
81
+ if output_w is not None:
82
+ samples.append((source_w, output_w))
83
+
84
+ return samples
85
+
86
+ def __len__(self):
87
+ return len(self.samples)
88
+
89
+ def __getitem__(self, idx):
90
+
91
+ if hasattr(idx, '__iter__'):
92
+ return self._get_items(idx)
93
+
94
+ if isinstance(idx, int):
95
+ return self._get_one_item(idx)
96
+
97
+ if isinstance(idx, slice):
98
+ # Get the start, stop, and step from the slice
99
+ return self._get_items(range(idx.start, idx.stop, idx.step))
100
+
101
+ def _get_items(self, iterable):
102
+
103
+ labels = []
104
+
105
+ batch = len(iterable)
106
+ iterable = iterable.__iter__()
107
+ try:
108
+ (one_input,), target = self._get_one_item(next(iterable))
109
+ except StopIteration:
110
+ return []
111
+
112
+ # compute the shape
113
+ shape_out = (batch,) + one_input.shape
114
+ datas = torch.empty(shape_out, dtype=torch.long)
115
+
116
+ if isinstance(target, int):
117
+ shape_out = batch
118
+ labels = torch.empty(shape_out, dtype=torch.long)
119
+ else:
120
+ shape_out = (batch,) + target.shape
121
+ labels = torch.empty(shape_out, dtype=torch.float32)
122
+
123
+ datas[0] = one_input
124
+ labels[0] = target
125
+
126
+ for i, key in enumerate(iterable, 1):
127
+ # the nn take on parameter so we unpack the 1 tuples and make it for the batch
128
+ (datas[i],), labels[i] = self._get_one_item(key)
129
+ # the nn take on parameter so we make a 1 element tuple
130
+ return (datas,), labels
131
+
132
+
133
+ def _get_one_item(self, idx):
134
+ inputs, output = self.samples[idx]
135
+
136
+ inputs = torch.from_numpy(inputs)
137
+ output = torch.from_numpy(output)
138
+
139
+ if self.f_transform is not None:
140
+ if self.transform_param is not None:
141
+ inputs = self.f_transform(inputs, *self.transform_param[idx])
142
+ else:
143
+ inputs = self.f_transform(inputs)
144
+
145
+ return (inputs,), output
146
+
147
+ def add_rotation_flip(self):
148
+ self.f_transform = rotate_flip_transform
149
+ self.samples, self.transform_param = self._augmentation_setup(self.samples, [0, 90, 180, 270], [False, True])
150
+
151
+ def _augmentation_setup(self, samples, angles=None, flip=None):
152
+
153
+ if angles is None:
154
+ angles = [0, 90, 180, -90]
155
+
156
+ if flip is None:
157
+ flip = [False, True]
158
+
159
+ t_param_list = list(itertools.product(angles, flip))
160
+
161
+ t_params = []
162
+ samples_split = []
163
+ for k in samples:
164
+ for p in t_param_list:
165
+ t_params.append(p)
166
+ samples_split.append(k)
167
+
168
+ return samples_split, t_params
169
+
170
+
171
+
172
+ class BasicYearTrainingDataset(BasicTrainingDataset):
173
+ """Training dataset with year as additional input feature.
174
+
175
+ Extends BasicTrainingDataset to include temporal information (year) as an additional
176
+ input alongside image patches. Year values are normalized for neural network input.
177
+
178
+ Attributes:
179
+ years (list): Year values corresponding to each raster image.
180
+ year_normalisation (int): Value used to normalize years (year/year_normalisation).
181
+ """
182
+
183
+ def __init__(self, paths, years, size, stride, n_out, func, f_transform=None, transform_param=None, year_normalisation=2050):
184
+ """Initialize BasicYearTrainingDataset.
185
+
186
+ Args:
187
+ paths (list): List of paths to raster image files.
188
+ years (list): Year value for each raster file.
189
+ size (int): Size of windows to extract.
190
+ stride (int): Stride between consecutive windows.
191
+ n_out (int): Number of output bands.
192
+ func: Function applied to output windows. Returns None for invalid samples.
193
+ f_transform (callable, optional): Data augmentation function. Defaults to None.
194
+ transform_param (list, optional): Per-sample transform parameters. Defaults to None.
195
+ year_normalisation (int, optional): Normalization divisor for year values.
196
+ Defaults to 2050.
197
+ """
198
+ self.years = years
199
+ self.year_normalisation = year_normalisation
200
+ super().__init__(paths, size, stride, n_out, func, f_transform, transform_param)
201
+
202
+
203
+ def extract(self):
204
+
205
+ samples = []
206
+
207
+ for path, year in zip(self.paths, self.years):
208
+ data = RasterImage.from_file(path).data
209
+
210
+ bands, height, width = data.shape
211
+
212
+ # print(data.shape)
213
+ # nh = math.floor((height-self.size)/self.stride+1)
214
+ # nw = math.floor((width-self.size)/self.stride+1)
215
+
216
+ for i in range(0, height - self.size + 1, self.stride):
217
+ for j in range(0, width - self.size + 1, self.stride):
218
+ source_w = data[:-self.n_out, i:i + self.size, j:j + self.size]
219
+ output_w = data[-self.n_out:, i:i + self.size, j:j + self.size]
220
+ output_w = self.func(output_w)
221
+
222
+ if output_w is not None:
223
+ samples.append((source_w, np.array([year/self.year_normalisation], dtype= np.float32), output_w))
224
+
225
+ return samples
226
+
227
+ def _get_one_item(self, idx):
228
+ inputs, year, output = self.samples[idx]
229
+
230
+ inputs = torch.from_numpy(inputs)
231
+ output = torch.from_numpy(output)
232
+
233
+ if self.f_transform is not None:
234
+ if self.transform_param is not None:
235
+ inputs = self.f_transform(inputs, *self.transform_param[idx])
236
+ else:
237
+ inputs = self.f_transform(inputs)
238
+
239
+ return inputs, year, output
240
+
241
+
@@ -0,0 +1,120 @@
1
+ import math
2
+ from bisect import bisect_right
3
+ from typing import List
4
+
5
+ import torch
6
+ from eoml.raster.raster_reader import AbstractRasterReader
7
+ from eoml.torch.cnn.torch_utils import conv_out_size
8
+ from rasterio.coords import BoundingBox
9
+ from rasterio.windows import Window
10
+ from torch.utils.data import Dataset
11
+
12
+
13
+ class WindowsTrainingDataset(Dataset):
14
+ """
15
+ Basic implementation of training dataset, receive a list of images, cut windows through them and store everything
16
+ in memories
17
+
18
+ Todo need to be finished
19
+ """
20
+ def __init__(self,
21
+ input_raster_reader: AbstractRasterReader,
22
+ target_raster_reader: List[AbstractRasterReader],
23
+ size,
24
+ stride,
25
+ padding,
26
+ transform_output):
27
+
28
+ self.input_raster_reader = input_raster_reader
29
+
30
+ self.target_raster_readers = target_raster_reader
31
+
32
+ self.size = size
33
+ self.stride = stride
34
+ self.padding = padding
35
+
36
+ self.transform_output = transform_output
37
+
38
+ self.conv_sum = []
39
+
40
+ self.radius = math.floor(size)
41
+
42
+
43
+ def compute_stats(self):
44
+
45
+ sum = 0
46
+ conv_sum = []
47
+ for raster in self.target_raster_readers:
48
+ width = conv_out_size(raster.ref_raster_info().width, self.size, self.stride, self.padding)
49
+ height = conv_out_size(raster.ref_raster_info().height, self.size, self.stride, self.padding)
50
+
51
+ sum+= width*height
52
+ self.conv_sum.append(sum)
53
+
54
+ def _find_image_index(self, conv_b):
55
+ """index of the image
56
+ bisect right return the index (on the right in case of equality to insert value to keep the list ordered)
57
+ """
58
+ index_image = bisect_right(self.conv_sum, conv_b) - 1
59
+ index_in_image = conv_b - self.conv_sum[index_image]
60
+
61
+ con_per_row = conv_out_size(self.target_raster_readers[index_image].ref_raster_info().width, self.size, self.stride, self.padding)
62
+
63
+ col, row = divmod(index_in_image, con_per_row)
64
+
65
+ return index_image, col, row
66
+ def generate_input(self, index_image, col, row):
67
+ # this way would interpol the input.
68
+ out_reader = self.target_raster_readers[index_image]
69
+ target_window = Window(col - self.radius, row - self.radius, self.size, self.size)
70
+
71
+ bounding_box = out_reader.ref_raster_info().window_bounds(target_window)
72
+
73
+ out_reader.read_windows_around_coordinate(target_window)
74
+
75
+ with self.input_raster_reader:
76
+ input = self.input_raster_reader.read_bound(bounding_box)
77
+
78
+ with out_reader:
79
+ output = out_reader.read_windows_around_coordinate(target_window)
80
+
81
+ return input, output
82
+
83
+ def __len__(self):
84
+ return len(self.conv_sum[-1])
85
+
86
+ def __getitem__(self, idx):
87
+
88
+ if hasattr(idx, '__iter__'):
89
+ return self._get_items(idx)
90
+
91
+ if isinstance(idx, int):
92
+ return self._get_one_item(idx)
93
+
94
+ if isinstance(idx, slice):
95
+ # Get the start, stop, and step from the slice
96
+ return self._get_items(range(idx.start, idx.stop, idx.step))
97
+
98
+ def _get_items(self, iterable):
99
+ datas = []
100
+ labels = []
101
+ for key in iterable:
102
+ data, label = self._get_one_item(key)
103
+ datas.append(data)
104
+ labels.append(label)
105
+ return [datas, labels]
106
+
107
+ def _get_one_item(self, idx):
108
+ index_image, col, row = self._find_image_index(idx)
109
+
110
+ inputs, output = self.generate_input(self, index_image, col, row)
111
+
112
+ inputs = torch.from_numpy(inputs)
113
+ output = torch.from_numpy(output)
114
+
115
+ if self.transform_output is not None:
116
+ output = self.transform_output(inputs)
117
+
118
+ return inputs, output
119
+
120
+
@@ -0,0 +1,6 @@
1
+ """
2
+ Dataset Submodule for PyTorch.
3
+
4
+ This submodule provides specialized dataset creators and utilities for
5
+ specific machine learning tasks in Earth observation applications.
6
+ """
@@ -0,0 +1,46 @@
1
+ """Evaluation utilities for shade detection datasets.
2
+
3
+ This module provides classes for testing and evaluating shade detection models
4
+ on validation datasets.
5
+ """
6
+
7
+ from eoml.torch.cnn.dataset_evaluator import DatasetEvaluator
8
+
9
+
10
+ class ShadeDatasetTestet:
11
+ """Test and evaluate shade detection models on datasets.
12
+
13
+ Note: Class name appears to be a typo (Testet instead of Tester).
14
+
15
+ Attributes:
16
+ rasters (list): List of raster file paths.
17
+ datasets (list): List of dataset objects.
18
+ metric_list (list): List of metrics to compute.
19
+ model_path (str): Path to trained model file.
20
+ """
21
+
22
+ def __init__(self, rasters, datasets, metric_list, model_path):
23
+ """Initialize ShadeDatasetTestet.
24
+
25
+ Args:
26
+ rasters (list): List of raster file paths for evaluation.
27
+ datasets (list): List of dataset objects.
28
+ metric_list (list): Metrics to compute during evaluation.
29
+ model_path (str): Path to trained model weights.
30
+ """
31
+ self.rasters=rasters
32
+ self.datasets=datasets
33
+ self.metric_list=metric_list
34
+
35
+ self.model_path=model_path
36
+
37
+
38
+ def compute_metric(self):
39
+ """Compute evaluation metrics on datasets.
40
+
41
+ Note: Implementation appears incomplete - references undefined variables.
42
+
43
+ Returns:
44
+ tuple: Reference and predicted values (when properly implemented).
45
+ """
46
+ ref, pred = DatasetEvaluator(model_path).evaluate(train_dataloader, device=device)