WannaBeDataScientist commited on
Commit
920ac9e
1 Parent(s): a9f0a89

Final report submission

Browse files
Files changed (7) hide show
  1. .gitattributes +1 -0
  2. Paper.pdf +3 -0
  3. about_data_indices.ipynb +0 -0
  4. chabud.py +752 -0
  5. distribution_mask_size.ipynb +337 -0
  6. main.py +36 -0
  7. submission.py +149 -0
.gitattributes CHANGED
@@ -32,3 +32,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ Paper.pdf filter=lfs diff=lfs merge=lfs -text
Paper.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c848cf800ba48198acd2e208da5cbed9bff28442ca3c02e74682fe87946bcda
3
+ size 2988104
about_data_indices.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
chabud.py ADDED
@@ -0,0 +1,752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ##
2
+ ## chabud.py - Hilfsfunktionen für die ChaBuD ECML Challenge 2023
3
+ ##
4
+ ## CHANGES:
5
+ ## 2023-05-23: Erste Version veröffentlicht
6
+ ##
7
+ ## TODO:
8
+ ## * Funktion um Vorhersage als CSV zu speichern für Leaderboard
9
+ ## * Argument um Anzahl Trainingsepochen zu steuern (epoch, max_epoch, ... ?)
10
+ ## * Finales Modell ausgeben und ggf. auch Vorhersage auf Validierungsdaten speichern
11
+ ##
12
+ import logging
13
+ import os
14
+ from pathlib import Path
15
+ import pandas as pd
16
+ import albumentations as A
17
+ import albumentations.pytorch.transforms as Atorch
18
+ import h5py
19
+ import numpy as np
20
+ import pytorch_lightning as pl
21
+ import segmentation_models_pytorch as smp
22
+ import torch
23
+ import xarray as xr
24
+
25
+ from pytorch_lightning.callbacks import ModelCheckpoint
26
+
27
+ fn = Path("A:/CodingProjekte/DataMining/src/train_eval.hdf5")
28
+
29
+ #Wir wollen ein Dataframe erstellen, welches nur die Namen der Datensätze enthält, die eine größere Brandfläche als 2% haben.
30
+ #Sogesehen ist es dann eine whitelist
31
+
32
+ def basic_df():
33
+ res = []
34
+ #Anzahl aller Datensätze ("name")
35
+ count_ds = 0
36
+
37
+ with h5py.File(fn, "r") as fd:
38
+
39
+ for name, ds in fd.items():
40
+ count_burnt_pixels = 0
41
+ #Standardmäßig ist überall ein pre_fire verfügbar
42
+ pre_miss = 0
43
+ #Weil wir den Datensatz schon gecheckt haben, ist ein sicherer Zugrif auf post_fire und mask möglich (hier fehlen keine ganzen Datensätze)
44
+ post = ds["post_fire"]
45
+ mask = ds["mask"]
46
+
47
+ count_burnt_pixels = np.sum(mask)
48
+ count_pixels =512 * 512
49
+ burnt_pixel_rel = count_burnt_pixels / count_pixels
50
+ #Anders als bei mask und Post müssen wir vor Zugriff überprüfen ob "pre_fire" überhaupt existiert - Vermeidung einer Fehlermeldung
51
+ if "pre_fire" not in ds:
52
+ pre_miss = 1
53
+ res.append({"name": name, "pre_missing": pre_miss, "burnt_pixel_abs": count_burnt_pixels, "burnt_pixel_rel": burnt_pixel_rel})
54
+
55
+ return pd.DataFrame(res)
56
+
57
+ def miss_dp_df():
58
+ BANDS = ["coastal_aerosol", "blue", "green", "red",
59
+ "veg_red_1", "veg_red_2", "veg_red_3", "nir",
60
+ "veg_red_4", "water_vapour", "swir_1", "swir_2"]
61
+
62
+ res = basic_df().values
63
+
64
+ # miss_dp ist eine Liste mit "name", "pre" "post" (Werte von Pre + Postt werden mit den Bandnamen selektiert)
65
+ miss_count = 0
66
+ miss_dp = []
67
+ with h5py.File(fn, "r") as fd:
68
+ for x in res:
69
+ # skippe die Datensätze mit fehlendem Pre-Bild
70
+ # if x["pre_missing"] == 1:
71
+ # continue
72
+ pre_miss = False
73
+
74
+ # Laden der Daten aus dem Originaldatensatz
75
+ name = x["name"]
76
+ ds = to_xarray(fd[name])
77
+ pre = ds["pre"][...]
78
+ post = ds["post"][...]
79
+ mask = ds["mask"][...]
80
+
81
+ if x["pre_missing"] == 1:
82
+ # Code für den Fall, dass 'pre_missing' gleich 1 ist
83
+ post_miss = []
84
+ for band in range(pre.shape[2]):
85
+ post_miss.append((np.sum(post[band] == 0).values))
86
+ x_post_miss = xr.DataArray(post_miss, dims=["band"], coords={"band": BANDS})
87
+ miss_dp.append({"name": name, "pre": [], "post": x_post_miss.values})
88
+ else:
89
+ # Code für den Fall, dass 'pre_missing' nicht gleich 1 ist
90
+ pre_miss = []
91
+ post_miss = []
92
+ for band in range(pre.shape[2]):
93
+ pre_miss.append((np.sum(pre[band] == 0).values))
94
+ post_miss.append((np.sum(post[band] == 0).values))
95
+ x_pre_miss = xr.DataArray(pre_miss, dims=["band"], coords={"band": BANDS})
96
+ x_post_miss = xr.DataArray(post_miss, dims=["band"], coords={"band": BANDS})
97
+ miss_dp.append({"name": name, "pre": x_pre_miss.values, "post": x_post_miss.values})
98
+
99
+ return miss_dp
100
+
101
+
102
+ def wl():
103
+ whitelist = []
104
+ df = basic_df()
105
+
106
+ for index, row in df.iterrows():
107
+ if row["burnt_pixel_rel"] < 0.0025:
108
+ continue
109
+ whitelist.append(row["name"])
110
+ return whitelist
111
+
112
+ checkpoint_callback = ModelCheckpoint(
113
+ #dirpath='checkpoints/',
114
+ filename='model-{epoch:02d}-{val_iou:.2f}',
115
+ monitor='valid_iou',
116
+ mode='max',
117
+ save_top_k=3
118
+ )
119
+
120
+
121
+ __version__ = "1.0.0"
122
+ logger = logging.getLogger(__name__)
123
+
124
+ ds_path = "A:/CodingProjekte/DataMining/src/train_eval.hdf5"
125
+
126
+
127
+ def to_xarray(dataset, pretty_band_names=True):
128
+ """Konvertiert ein HDF5-Gruppenobjekt, das Vor- und Nach-Brandbilder enthält, in xarray DataArrays.
129
+
130
+ Parameters
131
+ ----------
132
+ dataset : h5py.Group
133
+ Ein HDF5-Gruppenobjekt, das die Vor- und Nach-Brandbilder, die Maske und die Metadaten enthält.
134
+ pretty_band_names : bool, optional
135
+ Wenn True (Standard), werden die "Pretty" Bandnamen verwendet, ansonsten die ursprünglichen MSI Bandnummern.
136
+
137
+ Returns
138
+ -------
139
+ dict
140
+ Ein Dictionary, das die xarray DataArrays für die Vor- und Nach-Brandbilder, die Maske und die Fold-Informationen enthält.
141
+ """
142
+ if pretty_band_names:
143
+ BANDS = ["coastal_aerosol", "blue", "green", "red",
144
+ "veg_red_1", "veg_red_2", "veg_red_3", "nir",
145
+ "veg_red_4", "water_vapour", "swir_1", "swir_2"]
146
+ else:
147
+ BANDS = ["1", "2", "3", "4", "5", "6", "7", "8", "8a", "9", "11", "12"]
148
+
149
+ post = dataset["post_fire"][...].astype("float32") / 10000.0
150
+
151
+ try:
152
+ pre = dataset["pre_fire"][...].astype("float32") / 10000.0
153
+ except KeyError:
154
+ pre = np.zeros_like(post, dtype="float32")
155
+
156
+ mask = dataset["mask"][..., 0]
157
+
158
+ return {"pre": xr.DataArray(pre, dims=["x", "y", "band"], coords={"x": range(512), "y": range(512), "band": BANDS}),
159
+ "post": xr.DataArray(post, dims=["x", "y", "band"], coords={"x": range(512), "y": range(512), "band": BANDS}),
160
+ "mask": xr.DataArray(mask, dims=["x", "y"], coords={"x": range(512), "y": range(512)}),
161
+ "fold": dataset.attrs["fold"]}
162
+
163
+ class BandExtractor:
164
+ def __init__(self, index, name) -> None:
165
+ self.index = index
166
+ self.name = name
167
+
168
+ def __call__(self, data):
169
+ if isinstance(data, np.ndarray):
170
+ return data[..., self.index]
171
+ elif isinstance(data, xr.DataArray):
172
+ return data.sel(band=self.name).values
173
+ else:
174
+ msg = "Unknown data format."
175
+ raise Exception(msg)
176
+
177
+ def __repr__(self) -> str:
178
+ return f'BandExtractor({self.index}, "{self.name}")'
179
+
180
+
181
+ band_1 = BandExtractor(0, "coastal_aerosol")
182
+ band_2 = BandExtractor(1, "blue")
183
+ band_3 = BandExtractor(2, "green")
184
+ band_4 = BandExtractor(3, "red")
185
+ band_5 = BandExtractor(4, "veg_red_1")
186
+ band_6 = BandExtractor(5, "veg_red_2")
187
+ band_7 = BandExtractor(6, "veg_red_3")
188
+ band_8 = BandExtractor(7, "nir")
189
+ band_8a = BandExtractor(8, "veg_red_4")
190
+ band_9 = BandExtractor(9, "water_vapour")
191
+ band_11 = BandExtractor(10, "swir_1")
192
+ band_12 = BandExtractor(11, "swir_2")
193
+
194
+
195
+ def NBR(data):
196
+ """Normalized Burn Ratio.
197
+
198
+ nbr = (nir - swir_2) / (nir + swir_2)
199
+ """
200
+ if isinstance(data, np.ndarray):
201
+ nir = data[..., 7]
202
+ swir_2 = data[..., 11]
203
+ elif isinstance(data, xr.DataArray):
204
+ nir = data.sel(band="nir").values
205
+ swir_2 = data.sel(band="swir_2").values
206
+ else:
207
+ msg = "Unknown data format."
208
+ raise Exception(msg)
209
+
210
+ zaehler = nir - swir_2
211
+ nenner = nir + swir_2
212
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
213
+
214
+
215
+ def NDVI(data):
216
+ """Normalized Difference Vegetation Index."""
217
+ if isinstance(data, np.ndarray):
218
+ red = data[..., 3]
219
+ nir = data[..., 7]
220
+ elif isinstance(data, xr.DataArray):
221
+ red = data.sel(band="red").values
222
+ nir = data.sel(band="nir").values
223
+ else:
224
+ msg = "Unknown data format."
225
+ raise Exception(msg)
226
+
227
+ zaehler = nir - red
228
+ nenner = nir + red
229
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
230
+
231
+
232
+ def GNDVI(data):
233
+ """Green Normalized Difference Vegetation Index."""
234
+ if isinstance(data, np.ndarray):
235
+ green = data[..., 2]
236
+ red = data[..., 3]
237
+ nir = data[..., 7]
238
+ elif isinstance(data, xr.DataArray):
239
+ green = data.sel(band="green").values
240
+ red = data.sel(band="red").values
241
+ nir = data.sel(band="nir").values
242
+ else:
243
+ msg = "Unknown data format."
244
+ raise Exception(msg)
245
+
246
+ zaehler = nir - green
247
+ nenner = nir + red
248
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
249
+
250
+
251
+ def EVI(data):
252
+ """Enhanced Vegetation Index."""
253
+ if isinstance(data, np.ndarray):
254
+ blue = data[..., 1]
255
+ red = data[..., 3]
256
+ nir = data[..., 7]
257
+ elif isinstance(data, xr.DataArray):
258
+ blue = data.sel(band="blue").values
259
+ red = data.sel(band="red").values
260
+ nir = data.sel(band="nir").values
261
+ else:
262
+ msg = "Unknown data format."
263
+ raise Exception(msg)
264
+
265
+ zaehler = nir - red
266
+ nenner = nir + 6 * red - 7.5 * blue + 1
267
+
268
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
269
+
270
+
271
+ def AVI(data):
272
+ """Advanced Vegetation Index."""
273
+ if isinstance(data, np.ndarray):
274
+ red = data[..., 3]
275
+ nir = data[..., 7]
276
+ elif isinstance(data, xr.DataArray):
277
+ red = data.sel(band="red").values
278
+ nir = data.sel(band="nir").values
279
+ else:
280
+ msg = "Unknown data format."
281
+ raise Exception(msg)
282
+
283
+ base = nir * (1 - red) * (nir - red)
284
+ ## FIXME: Deal with cube roots of negative values?
285
+ return np.power(base, 1./3., out=np.zeros_like(base), where=base>0)
286
+
287
+
288
+ def SAVI(data):
289
+ """Soil Adjusted Vegetation Index."""
290
+ if isinstance(data, np.ndarray):
291
+ red = data[..., 3]
292
+ nir = data[..., 7]
293
+ elif isinstance(data, xr.DataArray):
294
+ red = data.sel(band="red").values
295
+ nir = data.sel(band="nir").values
296
+ else:
297
+ msg = "Unknown data format."
298
+ raise Exception(msg)
299
+
300
+ return (nir - red) / (nir + red + 0.428) * 1.428
301
+
302
+
303
+ def NDMI(data):
304
+ if isinstance(data, np.ndarray):
305
+ nir = data[..., 7]
306
+ swir_1 = data[..., 10]
307
+ elif isinstance(data, xr.DataArray):
308
+ nir = data.sel(band="nir").values
309
+ swir_1 = data.sel(band="swir_1").values
310
+ else:
311
+ msg = "Unknown data format."
312
+ raise Exception(msg)
313
+
314
+ zaehler = nir - swir_1
315
+ nenner = nir + swir_1
316
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
317
+
318
+
319
+ def MSI(data):
320
+ """Moisture Stress Index.
321
+
322
+ Moisture Stress Index is used for canopy stress analysis, productivity
323
+ prediction and biophysical modeling. Interpretation of the MSI is inverted
324
+ relative to other water vegetation indices; thus, higher values of the
325
+ index indicate greater plant water stress and in inference, less soil
326
+ moisture content. The values of this index range from 0 to more than 3 with
327
+ the common range for green vegetation being 0.2 to 2.
328
+ """
329
+ if isinstance(data, np.ndarray):
330
+ nir = data[..., 7]
331
+ swir_1 = data[..., 10]
332
+ elif isinstance(data, xr.DataArray):
333
+ nir = data.sel(band="nir").values
334
+ swir_1 = data.sel(band="swir_1").values
335
+ else:
336
+ msg = "Unknown data format."
337
+ raise Exception(msg)
338
+
339
+ return swir_1 - nir
340
+
341
+
342
+ def GCI(data):
343
+ """Green Chlorophyll Index."""
344
+ if isinstance(data, np.ndarray):
345
+ green = data[..., 2]
346
+ water_vapour = data[..., 9]
347
+ elif isinstance(data, xr.DataArray):
348
+ green = data.sel(band="green").values
349
+ water_vapour = data.sel(band="water_vapour").values
350
+ else:
351
+ msg = "Unknown data format."
352
+ raise Exception(msg)
353
+
354
+ return water_vapour - green
355
+
356
+
357
+ def BSI(data):
358
+ """Bare Soil Index."""
359
+ if isinstance(data, np.ndarray):
360
+ blue = data[..., 1]
361
+ red = data[..., 3]
362
+ nir = data[..., 7]
363
+ swir_1 = data[..., 10]
364
+ elif isinstance(data, xr.DataArray):
365
+ blue = data.sel(band="blue").values
366
+ red = data.sel(band="red").values
367
+ nir = data.sel(band="nir").values
368
+ swir_1 = data.sel(band="swir_1").values
369
+ else:
370
+ msg = "Unknown data format."
371
+ raise Exception(msg)
372
+
373
+ swir_red = swir_1 + red
374
+ nir_blue = nir + blue
375
+ zaehler = swir_red - nir_blue
376
+ nenner = swir_red + nir_blue
377
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
378
+
379
+
380
+ def NDWI(data):
381
+ """Normalized Difference Water Index."""
382
+ if isinstance(data, np.ndarray):
383
+ green = data[..., 2]
384
+ nir = data[..., 7]
385
+ elif isinstance(data, xr.DataArray):
386
+ green = data.sel(band="green").values
387
+ nir = data.sel(band="nir").values
388
+ else:
389
+ msg = "Unknown data format."
390
+ raise Exception(msg)
391
+
392
+ zaehler = green - nir
393
+ nenner = green + nir
394
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
395
+
396
+
397
+ def NDSI(data):
398
+ """Normalized Difference Snow Index."""
399
+ if isinstance(data, np.ndarray):
400
+ green = data[..., 2]
401
+ swir_1 = data[..., 10]
402
+ elif isinstance(data, xr.DataArray):
403
+ green = data.sel(band="green").values
404
+ swir_1 = data.sel(band="swir_1").values
405
+ else:
406
+ msg = "Unknown data format."
407
+ raise Exception(msg)
408
+
409
+ zaehler = green - swir_1
410
+ nenner = green + swir_1
411
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
412
+
413
+
414
+ def NDGI(data):
415
+ if isinstance(data, np.ndarray):
416
+ green = data[..., 2]
417
+ red = data[..., 3]
418
+ elif isinstance(data, xr.DataArray):
419
+ green = data.sel(band="green").values
420
+ red = data.sel(band="red").values
421
+ else:
422
+ msg = "Unknown data format."
423
+ raise Exception(msg)
424
+
425
+ zaehler = green - red
426
+ nenner = green + red
427
+ return np.divide(zaehler, nenner, out=np.zeros_like(zaehler), where=nenner != 0.0)
428
+
429
+ #Die Bänder kommen in Channels
430
+ class FiresDataset(torch.utils.data.Dataset):
431
+ def __init__(self, filename, folds=(0, 1, 2, 3, 4),
432
+ channels=[],
433
+ include_pre=False,
434
+ transform=None) -> None:
435
+ self._filename = filename
436
+ self._fd = h5py.File(filename, "r")
437
+ self._channels = channels
438
+ self._transform = transform
439
+ self._names = []
440
+
441
+ whitelist = wl()
442
+ for name in self._fd:
443
+ if self._fd[name].attrs["fold"] not in folds:
444
+ continue
445
+ if name in whitelist:
446
+ self._names.append((name, "post_fire"))
447
+ if include_pre and "pre_fire" in self._fd[name]:
448
+ pre_image = self._fd[name]["pre_fire"][...]
449
+ # Include only "real" pre_fire images
450
+ if np.mean(pre_image > 0) > 0.8:
451
+ self._names.append((name, "pre_fire"))
452
+
453
+ def number_of_channels(self):
454
+ return len(self._channels)
455
+
456
+ def __getitem__(self, idx):
457
+ name, state = self._names[idx]
458
+ data = self._fd[name][state][...].astype("float32") / 10000.0
459
+ if state == "pre_fire":
460
+ mask = np.zeros((512, 512), dtype="float32")
461
+ else:
462
+ mask = self._fd[name]["mask"][..., 0].astype("float32")
463
+
464
+ channels = []
465
+ for channel in self._channels:
466
+ channels.append(channel(data))
467
+
468
+ # Stack indices into a new image in CHW format.
469
+ image = np.stack(channels)
470
+
471
+ if self._transform:
472
+ # Transpose image so we get HWC instead of CHW format.
473
+ # Transform is responsible for transposing back as required by PyTorch.
474
+ image = image.transpose((1, 2, 0))
475
+ xfrm = self._transform(image=image, mask=mask)
476
+ image, mask = xfrm["image"], xfrm["mask"]
477
+ logger.debug("Final tensor shape: %s", image.shape)
478
+
479
+ return {"image": image, "mask": mask[None, :]}
480
+
481
+ def __len__(self) -> int:
482
+ return len(self._names)
483
+
484
+
485
+ class FireModel(pl.LightningModule):
486
+ def __init__(self,
487
+ datafile,
488
+ model,
489
+ encoder,
490
+ encoder_depth,
491
+ encoder_weights,
492
+ loss,
493
+ channels,
494
+ train_transform,
495
+ train_use_pre_fire,
496
+ n_cpus,
497
+ batch_size,
498
+ lr=0.00025,
499
+ **kwargs) -> None:
500
+ super().__init__()
501
+ self.save_hyperparameters()
502
+ self.datafile = datafile
503
+ self.lr = lr
504
+ self.channels = channels
505
+ if model == "unet":
506
+ decoder_channels = [2**(8 - d) for d in range(encoder_depth, 0, -1)]
507
+ self.model = smp.Unet(encoder_name=encoder, encoder_depth=encoder_depth, encoder_weights=encoder_weights,
508
+ decoder_channels=decoder_channels,
509
+ in_channels=len(channels), classes=1)
510
+ elif model == "unetpp":
511
+ decoder_channels = [2**(8 - d) for d in range(encoder_depth, 0, -1)]
512
+ self.model = smp.UnetPlusPlus(encoder_name=encoder, encoder_depth=encoder_depth, encoder_weights=encoder_weights,
513
+ decoder_channels=decoder_channels,
514
+ in_channels=len(channels), classes=1)
515
+ elif model == "fpn":
516
+ if encoder_depth == 3:
517
+ upsampling = 1
518
+ elif encoder_depth == 4:
519
+ upsampling = 2
520
+ elif encoder_depth == 5:
521
+ upsampling = 4
522
+ else:
523
+ raise "FPN: Unsupported encoder depth {encoder_depth}."
524
+ self.model = smp.FPN(encoder_name=encoder, encoder_weights=encoder_weights, encoder_depth=encoder_depth,
525
+ upsampling=upsampling,
526
+ in_channels=len(channels), classes=1)
527
+ elif model == "dlv3":
528
+ self.model = smp.DeepLabV3(encoder_name=encoder, encoder_weights=encoder_weights, encoder_depth=encoder_depth,
529
+ in_channels=len(channels), classes=1)
530
+ elif model == "dlv3p":
531
+ if encoder_depth != 5:
532
+ raise f"Unsupported encoder depth {encoder_depth} for DeepLabV3+ (must be 5)."
533
+ self.model = smp.DeepLabV3Plus(encoder_name=encoder, encoder_weights=encoder_weights, encoder_depth=encoder_depth,
534
+ in_channels=len(channels), classes=1)
535
+ else:
536
+ raise f"Unsupported model '{model}'."
537
+
538
+ if loss == "dice":
539
+ self.loss_fn = smp.losses.DiceLoss(smp.losses.BINARY_MODE, from_logits=True)
540
+ elif loss == "bce":
541
+ self.loss_fn = smp.losses.SoftBCEWithLogitsLoss()
542
+ else:
543
+ raise f"Unsupported loss function '{loss}'."
544
+
545
+ self.train_transform = train_transform
546
+ self.train_use_pre_fire = train_use_pre_fire
547
+ self.n_cpus = n_cpus
548
+ self.batch_size = batch_size
549
+
550
+ def forward(self, image):
551
+ mask = self.model(image)
552
+ return mask
553
+
554
+ def shared_step(self, batch, stage):
555
+ image, mask = batch["image"], batch["mask"]
556
+
557
+ logits_mask = self.forward(image)
558
+ loss = self.loss_fn(logits_mask, mask)
559
+
560
+ prob_mask = logits_mask.sigmoid()
561
+ pred_mask = (prob_mask > 0.5).long()
562
+ tp, fp, fn, tn = smp.metrics.get_stats(pred_mask, mask.long(), mode="binary")
563
+ iou = smp.metrics.iou_score(tp, fp, fn, tn, reduction="micro-imagewise")
564
+
565
+ self.log(f"{stage}_loss", loss, on_step=False, on_epoch=True, prog_bar=True, logger=True)
566
+ self.log(f"{stage}_iou", iou, on_step=False, on_epoch=True, prog_bar=True, logger=True)
567
+ return loss
568
+
569
+ def training_step(self, batch, batch_idx):
570
+ return self.shared_step(batch, "train")
571
+
572
+ def train_dataloader(self):
573
+ train_ds = FiresDataset(self.datafile, folds=[1, 2, 3, 4],
574
+ channels=self.channels,
575
+ transform=self.train_transform,
576
+ include_pre=self.train_use_pre_fire)
577
+ train_dl = torch.utils.data.DataLoader(train_ds,
578
+ batch_size=self.batch_size,
579
+ num_workers=self.n_cpus,
580
+ shuffle=True,
581
+ pin_memory=True,
582
+ drop_last=False)
583
+ return train_dl
584
+
585
+ def validation_step(self, batch, batch_idx):
586
+ return self.shared_step(batch, "valid")
587
+
588
+ def val_dataloader(self):
589
+ val_ds = FiresDataset(self.datafile, folds=[0],
590
+ channels=self.channels,
591
+ transform=None,
592
+ include_pre=False)
593
+ val_dl = torch.utils.data.DataLoader(val_ds,
594
+ batch_size=self.batch_size,
595
+ num_workers=self.n_cpus,
596
+ shuffle=False,
597
+ pin_memory=True,
598
+ drop_last=False)
599
+ return val_dl
600
+
601
+ def test_step(self, batch, batch_idx):
602
+ return self.shared_step(batch, "test")
603
+
604
+ def configure_optimizers(self):
605
+ # TODO: Can we do better? We should probably implement a learning rate schedule?
606
+ return torch.optim.Adam(self.parameters(), lr=self.lr)
607
+
608
+
609
+ def main(accelerator,
610
+ datafile,
611
+ batch_size,
612
+ channels,
613
+ n_cpus,
614
+ model,
615
+ encoder,
616
+ encoder_depth,
617
+ encoder_weights,
618
+ loss,
619
+ train_use_pre_fire,
620
+ train_use_augmentation,
621
+ learning_rate,
622
+ ):
623
+
624
+
625
+ if train_use_augmentation:
626
+ train_xfrm = A.Compose([
627
+ A.VerticalFlip(p=0.5),
628
+ A.HorizontalFlip(p=0.5),
629
+ A.Transpose(p=0.5),
630
+ A.RandomRotate90(p=0.5),
631
+ Atorch.ToTensorV2(),
632
+ ])
633
+ else:
634
+ train_xfrm = None
635
+
636
+ logger.info("Instantiating model.")
637
+ mdl = FireModel(datafile=datafile,
638
+ model=model,
639
+ encoder=encoder,
640
+ encoder_depth=encoder_depth,
641
+ encoder_weights=encoder_weights,
642
+ loss=loss,
643
+ channels=channels,
644
+ n_cpus=n_cpus,
645
+ train_transform=train_xfrm,
646
+ train_use_pre_fire=train_use_pre_fire,
647
+ batch_size=batch_size,
648
+ lr=learning_rate)
649
+
650
+ trainer = pl.Trainer(accelerator=accelerator, devices="auto",
651
+ log_every_n_steps=10, max_epochs=30, callbacks=[checkpoint_callback])
652
+ #callbacks=[checkpoint_callback]
653
+ logger.info("Start training.")
654
+ trainer.fit(mdl)
655
+
656
+
657
+ CHANNEL_MAP = {
658
+ "band_1": band_1,
659
+ "band_2": band_2,
660
+ "band_3": band_3,
661
+ "band_4": band_4,
662
+ "band_5": band_5,
663
+ "band_6": band_6,
664
+ "band_7": band_7,
665
+ "band_8": band_8,
666
+ "band_8a": band_8a,
667
+ "band_9": band_9,
668
+ "band_11": band_11,
669
+ "band_12": band_12,
670
+ "nbr": NBR,
671
+ "ndvi": NDVI,
672
+ "gndvi": GNDVI,
673
+ "evi": EVI,
674
+ "avi": AVI,
675
+ "savi": SAVI,
676
+ "ndmi": NDMI,
677
+ "msi": MSI,
678
+ "gci": GCI,
679
+ "bsi": BSI,
680
+ "ndwi": NDWI,
681
+ "ndsi": NDSI,
682
+ "ndgi": NDGI,
683
+ }
684
+
685
+ if __name__ == "__main__":
686
+
687
+ import argparse # Only import when needed
688
+
689
+ N_CPUS = int(os.getenv("SLURM_CPUS_PER_TASK", 1))
690
+ parser = argparse.ArgumentParser("chabud.py")
691
+ parser.add_argument("--accelerator", type=str, choices=["cpu", "gpu", "auto"], default="auto")
692
+ parser.add_argument("--datafile", type=Path, default=ds_path,
693
+ help="Location of data file used for training.")
694
+ parser.add_argument("--n-cpus", type=int, default=N_CPUS, help="Number of CPU cores to use.")
695
+ parser.add_argument("--batch-size", type=int, default=2,
696
+ help="Training and validation batch size.")
697
+ parser.add_argument("--learning-rate", type=float, default=0.00025,
698
+ help="Learning rate of optimizer.")
699
+ parser.add_argument("--model", choices=["unet", "unetpp", "fpn", "dlv3", "dlv3p"], default="unet",
700
+ help="Segmentation model")
701
+ parser.add_argument("--encoder", choices=["resnet18", "resnet34", "resnet50", "vgg13", "dpn68", "dpn92", "timm-efficientnet-b0"], default="resnet34",
702
+ help="Encoder of segmentation model")
703
+ parser.add_argument("--encoder-depth", type=int, default=5,
704
+ help="Depth of encoder stage")
705
+ parser.add_argument("--encoder-weights", choices=["random", "imagenet"], default="imagenet",
706
+ help="Weight initialization for encoder")
707
+ parser.add_argument("--loss", choices=["dice", "bce"], default="dice",
708
+ help="Loss function")
709
+ parser.add_argument("--train-use-pre_fire", action="store_true",
710
+ help="Use pre_fire data for training?")
711
+ parser.add_argument("--train-use-augmentation", action="store_true",
712
+ help="Use data augmentation in training step?")
713
+ parser.add_argument("--channels", nargs="+", choices=CHANNEL_MAP.keys(),
714
+ default=["band_1", "band_2", "band_3", "band_4", "band_5", "band_6", "band_7", "band_8", "band_8a", "band_9", "band_11", "band_12"],
715
+ help="Channels to use for prediction")
716
+ parser.add_argument("--log-level", type=str, choices=["info", "debug"], default="info")
717
+
718
+ args = parser.parse_args()
719
+
720
+ LOGGING_MAP = {"info": logging.INFO, "debug": logging.DEBUG}
721
+ logging.basicConfig(level=LOGGING_MAP[args.log_level],
722
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
723
+ datefmt="%d-%b-%y %H:%M:%S")
724
+
725
+
726
+ if args.encoder_weights == "random":
727
+ args.encoder_weights = None
728
+
729
+ # Translate channel names to function that calculates the channel / index.
730
+ logger.info(f"Selected channels: {args.channels}")
731
+ channels = []
732
+ for channel in args.channels:
733
+ channels.append(CHANNEL_MAP[channel])
734
+
735
+
736
+ torch.set_num_threads(args.n_cpus)
737
+ torch.set_float32_matmul_precision("medium")
738
+
739
+ main(accelerator=args.accelerator,
740
+ datafile=args.datafile,
741
+ batch_size=args.batch_size,
742
+ learning_rate=args.learning_rate,
743
+ channels=channels,
744
+ n_cpus=args.n_cpus,
745
+ model=args.model,
746
+ encoder=args.encoder,
747
+ encoder_depth=args.encoder_depth,
748
+ encoder_weights=args.encoder_weights,
749
+ loss=args.loss,
750
+ train_use_pre_fire=args.train_use_pre_fire,
751
+ train_use_augmentation=args.train_use_augmentation)
752
+
distribution_mask_size.ipynb ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "8b77c765-edb9-437f-bab9-9bb1217ab8a8",
7
+ "metadata": {
8
+ "execution": {
9
+ "iopub.execute_input": "2023-07-12T19:18:23.207656Z",
10
+ "iopub.status.busy": "2023-07-12T19:18:23.207403Z",
11
+ "iopub.status.idle": "2023-07-12T19:18:25.557392Z",
12
+ "shell.execute_reply": "2023-07-12T19:18:25.556690Z",
13
+ "shell.execute_reply.started": "2023-07-12T19:18:23.207621Z"
14
+ },
15
+ "tags": []
16
+ },
17
+ "outputs": [],
18
+ "source": [
19
+ "import h5py\n",
20
+ "import numpy as np\n",
21
+ "import pandas as pd\n",
22
+ "import xarray as xr\n",
23
+ "import skimage as ski\n",
24
+ "import seaborn as sns\n",
25
+ "import matplotlib.pyplot as plt\n",
26
+ "from matplotlib.colors import ListedColormap\n",
27
+ "from tqdm.auto import tqdm\n",
28
+ "from pathlib import Path\n",
29
+ "import matplotlib.pyplot as plt\n",
30
+ "\n",
31
+ "BASEDIR = Path(\"/global/public/chabud-ecml-pkdd2023/\")\n",
32
+ "fn = BASEDIR / \"train_eval.hdf5\""
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 2,
38
+ "id": "1fafd16b-c043-4c47-9e30-2c75b72eaccf",
39
+ "metadata": {
40
+ "execution": {
41
+ "iopub.execute_input": "2023-07-12T19:18:25.562789Z",
42
+ "iopub.status.busy": "2023-07-12T19:18:25.561013Z",
43
+ "iopub.status.idle": "2023-07-12T19:18:25.571051Z",
44
+ "shell.execute_reply": "2023-07-12T19:18:25.569973Z",
45
+ "shell.execute_reply.started": "2023-07-12T19:18:25.562762Z"
46
+ },
47
+ "tags": []
48
+ },
49
+ "outputs": [],
50
+ "source": [
51
+ "def to_xarray(dataset, pretty_band_names=True):\n",
52
+ " \"\"\"Convert a single example into an xarray for easy access\"\"\"\n",
53
+ " \n",
54
+ " if pretty_band_names:\n",
55
+ " BANDS = [\"coastal_aerosol\", \"blue\", \"green\", \"red\",\n",
56
+ " \"veg_red_1\", \"veg_red_2\", \"veg_red_3\", \"nir\", \n",
57
+ " \"veg_red_4\", \"water_vapour\", \"swir_1\", \"swir_2\"]\n",
58
+ " else:\n",
59
+ " BANDS = [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"8a\", \"9\", \"11\", \"12\"]\n",
60
+ " \n",
61
+ " post = dataset[\"post_fire\"][...].astype(\"float32\") / 10000.0\n",
62
+ " \n",
63
+ " # Da `pre_fire` manchmal fehlt ersetzen wir es durch 0 Werte was\n",
64
+ " # eh der Platzhalter für einen fehlenden Messwert ist.\n",
65
+ " try:\n",
66
+ " pre = dataset[\"pre_fire\"][...].astype(\"float32\") / 10000.0\n",
67
+ " except KeyError:\n",
68
+ " pre = np.zeros_like(post, dtype=\"float32\")\n",
69
+ " \n",
70
+ " # Da die Maske nur ein \"Band\" hat können wir die dritte Dimension einfach\n",
71
+ " # weglassen. Das erreichen wir in dem wir mit `0` am Ende indizieren.\n",
72
+ " mask = dataset[\"mask\"][..., 0].astype(\"bool\")\n",
73
+ " \n",
74
+ " return {\"pre\": xr.DataArray(pre, dims=[\"x\", \"y\", \"band\"], coords={\"x\": range(512), \"y\": range(512), \"band\": BANDS}),\n",
75
+ " \"post\": xr.DataArray(post, dims=[\"x\", \"y\", \"band\"], coords={\"x\": range(512), \"y\": range(512), \"band\": BANDS}),\n",
76
+ " \"mask\": xr.DataArray(mask, dims=[\"x\", \"y\"], coords={\"x\": range(512), \"y\": range(512)}),\n",
77
+ " \"fold\": dataset.attrs[\"fold\"]}"
78
+ ]
79
+ },
80
+ {
81
+ "cell_type": "markdown",
82
+ "id": "385378a8-6c11-483c-a936-825af060f0c2",
83
+ "metadata": {},
84
+ "source": [
85
+ "The following code was used and edited in order to analize the effect on the median and standart deviation if data with a too small masks are removed. This was done so that the Neuronal Network gets trained to predict larger masks."
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": 3,
91
+ "id": "3a4eb708-4a8d-484a-a3d5-79318fa2e1ce",
92
+ "metadata": {
93
+ "execution": {
94
+ "iopub.execute_input": "2023-07-12T19:18:25.575871Z",
95
+ "iopub.status.busy": "2023-07-12T19:18:25.574255Z",
96
+ "iopub.status.idle": "2023-07-12T19:18:36.454225Z",
97
+ "shell.execute_reply": "2023-07-12T19:18:36.453237Z",
98
+ "shell.execute_reply.started": "2023-07-12T19:18:25.575846Z"
99
+ },
100
+ "tags": []
101
+ },
102
+ "outputs": [],
103
+ "source": [
104
+ "res = []\n",
105
+ "\n",
106
+ "with h5py.File(fn, \"r\") as fd:\n",
107
+ " for name in fd:\n",
108
+ " ds = to_xarray(fd[name])\n",
109
+ " mask = ds[\"mask\"].values\n",
110
+ " burned = np.sum(mask) / (512*512)\n",
111
+ " #if(burned==1):\n",
112
+ " #print(name)\n",
113
+ " if(burned<=0.02):\n",
114
+ " continue;\n",
115
+ " res.append({\"burned\": burned}) \n",
116
+ " #break;"
117
+ ]
118
+ },
119
+ {
120
+ "cell_type": "code",
121
+ "execution_count": 4,
122
+ "id": "450fe581-0d56-47e0-85d6-d82d3e22afde",
123
+ "metadata": {
124
+ "execution": {
125
+ "iopub.execute_input": "2023-07-12T19:18:36.460360Z",
126
+ "iopub.status.busy": "2023-07-12T19:18:36.458625Z",
127
+ "iopub.status.idle": "2023-07-12T19:18:36.467408Z",
128
+ "shell.execute_reply": "2023-07-12T19:18:36.466800Z",
129
+ "shell.execute_reply.started": "2023-07-12T19:18:36.460317Z"
130
+ },
131
+ "tags": []
132
+ },
133
+ "outputs": [],
134
+ "source": [
135
+ "df = pd.DataFrame(res)\n",
136
+ "del res"
137
+ ]
138
+ },
139
+ {
140
+ "cell_type": "code",
141
+ "execution_count": 5,
142
+ "id": "87df05e4-3401-428a-88ea-98a9510933c8",
143
+ "metadata": {
144
+ "execution": {
145
+ "iopub.execute_input": "2023-07-12T19:18:36.471822Z",
146
+ "iopub.status.busy": "2023-07-12T19:18:36.470260Z",
147
+ "iopub.status.idle": "2023-07-12T19:18:36.788703Z",
148
+ "shell.execute_reply": "2023-07-12T19:18:36.787968Z",
149
+ "shell.execute_reply.started": "2023-07-12T19:18:36.471799Z"
150
+ },
151
+ "tags": []
152
+ },
153
+ "outputs": [
154
+ {
155
+ "name": "stdout",
156
+ "output_type": "stream",
157
+ "text": [
158
+ "0.23417302673938228\n",
159
+ "0.26324914249621933\n"
160
+ ]
161
+ },
162
+ {
163
+ "data": {
164
+ "text/plain": [
165
+ "<matplotlib.lines.Line2D at 0x150591a3e230>"
166
+ ]
167
+ },
168
+ "execution_count": 5,
169
+ "metadata": {},
170
+ "output_type": "execute_result"
171
+ },
172
+ {
173
+ "data": {
174
+ "image/png": "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O3ZszJw5M55//vmDnv/ss8/GzJkzY+zYsfHBD34wli5dWqRJgWIazL3hxz/+cZx99tlx1FFHRV1dXTQ1NcXTTz9dxGmBYhrs7x32++UvfxmVlZXxsY99bHgHBFIx2HvD7t2747bbbouGhoYYM2ZMHHvssfHII48UaVqgWAZ7b3jsscfiox/9aNTU1MTkyZPjM5/5TGzfvr1I0wLF8Nxzz8UFF1wQU6ZMiUwmE0888cQhr5FHZidEL5Lly5fHwoUL47bbbot169bFrFmz4txzz43NmzdnPf/111+P8847L2bNmhXr1q2LL33pS7FgwYL40Y9+VOTJgeE02HvDc889F2effXasXLky1q5dG2eccUZccMEFsW7duiJPDgy3wd4f9mttbY358+fHWWedVaRJgWLK595wySWXxM9//vN4+OGH4x//8R/j8ccfjz/6oz8q4tTAcBvsveGFF16I+fPnx1VXXRWvvPJKrFixIv7+7/8+rr766iJPDgyn9vb2+OhHPxr333//gM6XR+aWSZIkSXuIcnDqqafGySefHA899FDPsenTp8fcuXNjyZIl/c6/+eab48knn4yNGzf2HGtubo5f/epXsXr16qLMDAy/wd4bsjnhhBNi3rx5cfvttw/XmEAK8r0/XHrppXHcccdFRUVFPPHEE7F+/foiTAsUy2DvDU899VRceuml8dprr8URRxxRzFGBIhrsveFrX/taPPTQQ7Fp06aeY/fdd1/cfffd8eabbxZlZqC4MplM/OQnP4m5c+fmPEcemZsmehF0dnbG2rVrY86cOb2Oz5kzJ1588cWs16xevbrf+eecc06sWbMmurq6hm1WoHjyuTf0tXfv3tixY4c/FMMok+/94dFHH41NmzbFHXfcMdwjAinI597w5JNPRmNjY9x9991xzDHHxPHHHx833XRT7Nq1qxgjA0WQz73htNNOi7feeitWrlwZSZLEb3/72/jhD38Y559/fjFGBkqUPDK3yrQHKAfbtm2L7u7umDRpUq/jkyZNii1btmS9ZsuWLVnP37NnT2zbti0mT548bPMCxZHPvaGve+65J9rb2+OSSy4ZjhGBlORzf3j11Vfjlltuieeffz4qK/0WD0ajfO4Nr732WrzwwgsxduzY+MlPfhLbtm2La6+9Nt555x170WGUyOfecNppp8Vjjz0W8+bNiz/84Q+xZ8+e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",
175
+ "text/plain": [
176
+ "<Figure size 1500x500 with 1 Axes>"
177
+ ]
178
+ },
179
+ "metadata": {},
180
+ "output_type": "display_data"
181
+ }
182
+ ],
183
+ "source": [
184
+ "mean = np.mean(df[\"burned\"])\n",
185
+ "std = np.std(df[\"burned\"])\n",
186
+ "print(mean)\n",
187
+ "print(std)\n",
188
+ "#print(mean-std)\n",
189
+ "fig_1 = sns.displot(df, x=\"burned\", kind =\"ecdf\", height=5, aspect=3)\n",
190
+ "fig_1.ax.axvline(mean, color = \"red\")\n",
191
+ "#fig_1.ax.axvline(mean-std, color = \"blue\")\n",
192
+ "#fig_1.ax.axvline(std+mean, color = \"blue\")\n",
193
+ "#fig_1.savefig('size_mask_no_2.eps' , format = 'eps')"
194
+ ]
195
+ },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": 6,
199
+ "id": "826fa232-df66-44ec-bbe0-6d43459512f4",
200
+ "metadata": {
201
+ "execution": {
202
+ "iopub.execute_input": "2023-07-12T19:18:36.792861Z",
203
+ "iopub.status.busy": "2023-07-12T19:18:36.791384Z",
204
+ "iopub.status.idle": "2023-07-12T19:18:36.796827Z",
205
+ "shell.execute_reply": "2023-07-12T19:18:36.795906Z",
206
+ "shell.execute_reply.started": "2023-07-12T19:18:36.792836Z"
207
+ },
208
+ "tags": []
209
+ },
210
+ "outputs": [],
211
+ "source": [
212
+ "# wir sehen, dass die meisten Masken zwiscehn 0 und 20% des Bildes abdecken"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": 7,
218
+ "id": "1574fdb1-6442-4803-b8d0-b8355e1c2377",
219
+ "metadata": {
220
+ "execution": {
221
+ "iopub.execute_input": "2023-07-12T19:18:36.801555Z",
222
+ "iopub.status.busy": "2023-07-12T19:18:36.799900Z",
223
+ "iopub.status.idle": "2023-07-12T19:18:36.805446Z",
224
+ "shell.execute_reply": "2023-07-12T19:18:36.804561Z",
225
+ "shell.execute_reply.started": "2023-07-12T19:18:36.801531Z"
226
+ },
227
+ "tags": []
228
+ },
229
+ "outputs": [],
230
+ "source": [
231
+ "# ein treshhold von 2% für die Masken sorgt schon dafür, dass der Datensatz ausgeglichener wirkt"
232
+ ]
233
+ },
234
+ {
235
+ "cell_type": "code",
236
+ "execution_count": 12,
237
+ "id": "e79099d4-e737-4909-9b18-1e900e6d73e0",
238
+ "metadata": {
239
+ "execution": {
240
+ "iopub.execute_input": "2023-07-12T19:24:35.628647Z",
241
+ "iopub.status.busy": "2023-07-12T19:24:35.628293Z",
242
+ "iopub.status.idle": "2023-07-12T19:24:35.854670Z",
243
+ "shell.execute_reply": "2023-07-12T19:24:35.853911Z",
244
+ "shell.execute_reply.started": "2023-07-12T19:24:35.628618Z"
245
+ },
246
+ "tags": []
247
+ },
248
+ "outputs": [
249
+ {
250
+ "data": {
251
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAApwUlEQVR4nO3dfXBU9aH/8c+akDWEZC8hZDeRNVAFrpoABbxIKs+QkAqIOIVWB4migkA0A1w0eL2mlhKgBfTKlNpeLiCiwRlEveVBgjwoptQQS3nQ4UINEGrWVAxZgukGwvn90fH8XEiADYn5JrxfM2fGPee753wP0wPvnn1yWJZlCQAAwCA3NPcEAAAALkagAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADBOeHNPoCEuXLigL774QtHR0XI4HM09HQAAcBUsy9KZM2eUmJioG264/D2SFhkoX3zxhbxeb3NPAwAANEBpaak6dep02TEtMlCio6Ml/fMEY2Jimnk2AADgavj9fnm9Xvvf8ctpkYHy7cs6MTExBAoAAC3M1bw9gzfJAgAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOOHNPQETdX5mY3NPATDWsQX3NPcUAFwHuIMCAACME1KgLF++XD169FBMTIxiYmLUv39/bd682d6emZkph8MRtNx1111B+wgEAsrKylJcXJyioqI0ZswYnTx5snHOBgAAtAohBUqnTp20YMEC7d27V3v37tXQoUN177336tChQ/aYkSNHqqyszF42bdoUtI/s7Gxt2LBB+fn52r17t6qqqjRq1CjV1tY2zhkBAIAWL6T3oIwePTro8S9/+UstX75ce/bs0R133CFJcjqd8ng8dT6/srJSK1as0Jo1azR8+HBJ0muvvSav16tt27YpPT29IecAAABamQa/B6W2tlb5+fk6e/as+vfvb6/fuXOn4uPj1a1bNz322GMqLy+3txUXF+vcuXNKS0uz1yUmJio5OVmFhYX1HisQCMjv9wctAACg9Qo5UA4cOKB27drJ6XRq6tSp2rBhg26//XZJUkZGhtauXavt27dr8eLFKioq0tChQxUIBCRJPp9PERERat++fdA+3W63fD5fvcfMy8uTy+WyF6/XG+q0AQBACxLyx4y7d++uffv26fTp01q/fr0mTZqkXbt26fbbb9eECRPsccnJyerbt6+SkpK0ceNGjRs3rt59WpYlh8NR7/acnBzNnDnTfuz3+4kUAABasZADJSIiQrfeeqskqW/fvioqKtJLL72kV1555ZKxCQkJSkpK0pEjRyRJHo9HNTU1qqioCLqLUl5ertTU1HqP6XQ65XQ6Q50qAABooa75e1Asy7JfwrnYqVOnVFpaqoSEBElSnz591KZNGxUUFNhjysrKdPDgwcsGCgAAuL6EdAdl7ty5ysjIkNfr1ZkzZ5Sfn6+dO3dqy5YtqqqqUm5uru6//34lJCTo2LFjmjt3ruLi4nTfffdJklwulyZPnqxZs2apQ4cOio2N1ezZs5WSkmJ/qgcAACCkQPnyyy81ceJElZWVyeVyqUePHtqyZYtGjBih6upqHThwQK+++qpOnz6thIQEDRkyROvWrVN0dLS9j6VLlyo8PFzjx49XdXW1hg0bplWrViksLKzRTw4AALRMDsuyrOaeRKj8fr9cLpcqKysVExPT6Pvnt3iA+vFbPAAaKpR/v/ktHgAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGCekQFm+fLl69OihmJgYxcTEqH///tq8ebO93bIs5ebmKjExUZGRkRo8eLAOHToUtI9AIKCsrCzFxcUpKipKY8aM0cmTJxvnbAAAQKsQUqB06tRJCxYs0N69e7V3714NHTpU9957rx0hixYt0pIlS7Rs2TIVFRXJ4/FoxIgROnPmjL2P7OxsbdiwQfn5+dq9e7eqqqo0atQo1dbWNu6ZAQCAFsthWZZ1LTuIjY3Vr371Kz3yyCNKTExUdna2nn76aUn/vFvidru1cOFCTZkyRZWVlerYsaPWrFmjCRMmSJK++OILeb1ebdq0Senp6Vd1TL/fL5fLpcrKSsXExFzL9OvU+ZmNjb5PoLU4tuCe5p4CgBYqlH+/G/welNraWuXn5+vs2bPq37+/SkpK5PP5lJaWZo9xOp0aNGiQCgsLJUnFxcU6d+5c0JjExEQlJyfbYwAAAMJDfcKBAwfUv39//eMf/1C7du20YcMG3X777XZguN3uoPFut1vHjx+XJPl8PkVERKh9+/aXjPH5fPUeMxAIKBAI2I/9fn+o0wYAAC1IyHdQunfvrn379mnPnj164oknNGnSJH366af2dofDETTesqxL1l3sSmPy8vLkcrnsxev1hjptAADQgoQcKBEREbr11lvVt29f5eXlqWfPnnrppZfk8Xgk6ZI7IeXl5fZdFY/Ho5qaGlVUVNQ7pi45OTmqrKy0l9LS0lCnDQAAWpBr/h4Uy7IUCATUpUsXeTweFRQU2Ntqamq0a9cupaamSpL69OmjNm3aBI0pKyvTwYMH7TF1cTqd9kebv10AAEDrFdJ7UObOnauMjAx5vV6dOXNG+fn52rlzp7Zs2SKHw6Hs7GzNnz9fXbt2VdeuXTV//ny1bdtWDzzwgCTJ5XJp8uTJmjVrljp06KDY2FjNnj1bKSkpGj58eJOcIAAAaHlCCpQvv/xSEydOVFlZmVwul3r06KEtW7ZoxIgRkqQ5c+aourpa06ZNU0VFhfr166etW7cqOjra3sfSpUsVHh6u8ePHq7q6WsOGDdOqVasUFhbWuGcGAABarGv+HpTmwPegAM2H70EB0FDfy/egAAAANBUCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxgkpUPLy8nTnnXcqOjpa8fHxGjt2rA4fPhw0JjMzUw6HI2i56667gsYEAgFlZWUpLi5OUVFRGjNmjE6ePHntZwMAAFqFkAJl165dmj59uvbs2aOCggKdP39eaWlpOnv2bNC4kSNHqqyszF42bdoUtD07O1sbNmxQfn6+du/eraqqKo0aNUq1tbXXfkYAAKDFCw9l8JYtW4Ier1y5UvHx8SouLtbAgQPt9U6nUx6Pp859VFZWasWKFVqzZo2GDx8uSXrttdfk9Xq1bds2paenh3oOAACglbmm96BUVlZKkmJjY4PW79y5U/Hx8erWrZsee+wxlZeX29uKi4t17tw5paWl2esSExOVnJyswsLCOo8TCATk9/uDFgAA0Ho1OFAsy9LMmTN19913Kzk52V6fkZGhtWvXavv27Vq8eLGKioo0dOhQBQIBSZLP51NERITat28ftD+32y2fz1fnsfLy8uRyuezF6/U2dNoAAKAFCOklnu+aMWOG9u/fr927dwetnzBhgv3fycnJ6tu3r5KSkrRx40aNGzeu3v1ZliWHw1HntpycHM2cOdN+7Pf7iRQAAFqxBt1BycrK0rvvvqsdO3aoU6dOlx2bkJCgpKQkHTlyRJLk8XhUU1OjioqKoHHl5eVyu9117sPpdComJiZoAQAArVdIgWJZlmbMmKG33npL27dvV5cuXa74nFOnTqm0tFQJCQmSpD59+qhNmzYqKCiwx5SVlengwYNKTU0NcfoAAKA1CuklnunTp+v111/XO++8o+joaPs9Iy6XS5GRkaqqqlJubq7uv/9+JSQk6NixY5o7d67i4uJ033332WMnT56sWbNmqUOHDoqNjdXs2bOVkpJif6oHAABc30IKlOXLl0uSBg8eHLR+5cqVyszMVFhYmA4cOKBXX31Vp0+fVkJCgoYMGaJ169YpOjraHr906VKFh4dr/Pjxqq6u1rBhw7Rq1SqFhYVd+xkBAIAWz2FZltXckwiV3++Xy+VSZWVlk7wfpfMzGxt9n0BrcWzBPc09BQAtVCj/fvNbPAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAME5IgZKXl6c777xT0dHRio+P19ixY3X48OGgMZZlKTc3V4mJiYqMjNTgwYN16NChoDGBQEBZWVmKi4tTVFSUxowZo5MnT1772QAAgFYhpEDZtWuXpk+frj179qigoEDnz59XWlqazp49a49ZtGiRlixZomXLlqmoqEgej0cjRozQmTNn7DHZ2dnasGGD8vPztXv3blVVVWnUqFGqra1tvDMDAAAtlsOyLKuhT/773/+u+Ph47dq1SwMHDpRlWUpMTFR2draefvppSf+8W+J2u7Vw4UJNmTJFlZWV6tixo9asWaMJEyZIkr744gt5vV5t2rRJ6enpVzyu3++Xy+VSZWWlYmJiGjr9enV+ZmOj7xNoLY4tuKe5pwCghQrl3+9reg9KZWWlJCk2NlaSVFJSIp/Pp7S0NHuM0+nUoEGDVFhYKEkqLi7WuXPngsYkJiYqOTnZHnOxQCAgv98ftAAAgNarwYFiWZZmzpypu+++W8nJyZIkn88nSXK73UFj3W63vc3n8ykiIkLt27evd8zF8vLy5HK57MXr9TZ02gAAoAVocKDMmDFD+/fv1xtvvHHJNofDEfTYsqxL1l3scmNycnJUWVlpL6WlpQ2dNgAAaAEaFChZWVl69913tWPHDnXq1Mle7/F4JOmSOyHl5eX2XRWPx6OamhpVVFTUO+ZiTqdTMTExQQsAAGi9QgoUy7I0Y8YMvfXWW9q+fbu6dOkStL1Lly7yeDwqKCiw19XU1GjXrl1KTU2VJPXp00dt2rQJGlNWVqaDBw/aYwAAwPUtPJTB06dP1+uvv6533nlH0dHR9p0Sl8ulyMhIORwOZWdna/78+eratau6du2q+fPnq23btnrggQfssZMnT9asWbPUoUMHxcbGavbs2UpJSdHw4cMb/wwBAECLE1KgLF++XJI0ePDgoPUrV65UZmamJGnOnDmqrq7WtGnTVFFRoX79+mnr1q2Kjo62xy9dulTh4eEaP368qqurNWzYMK1atUphYWHXdjYAAKBVuKbvQWkufA8K0Hz4HhQADfW9fQ8KAABAUyBQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgnPDmngAANIfOz2xs7ikARju24J5mPT53UAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABgn5ED54IMPNHr0aCUmJsrhcOjtt98O2p6ZmSmHwxG03HXXXUFjAoGAsrKyFBcXp6ioKI0ZM0YnT568phMBAACtR8iBcvbsWfXs2VPLli2rd8zIkSNVVlZmL5s2bQranp2drQ0bNig/P1+7d+9WVVWVRo0apdra2tDPAAAAtDrhoT4hIyNDGRkZlx3jdDrl8Xjq3FZZWakVK1ZozZo1Gj58uCTptddek9fr1bZt25Senh7qlAAAQCvTJO9B2blzp+Lj49WtWzc99thjKi8vt7cVFxfr3LlzSktLs9clJiYqOTlZhYWFde4vEAjI7/cHLQAAoPVq9EDJyMjQ2rVrtX37di1evFhFRUUaOnSoAoGAJMnn8ykiIkLt27cPep7b7ZbP56tzn3l5eXK5XPbi9Xobe9oAAMAgIb/EcyUTJkyw/zs5OVl9+/ZVUlKSNm7cqHHjxtX7PMuy5HA46tyWk5OjmTNn2o/9fj+RAgBAK9bkHzNOSEhQUlKSjhw5IknyeDyqqalRRUVF0Ljy8nK53e469+F0OhUTExO0AACA1qvJA+XUqVMqLS1VQkKCJKlPnz5q06aNCgoK7DFlZWU6ePCgUlNTm3o6AACgBQj5JZ6qqiodPXrUflxSUqJ9+/YpNjZWsbGxys3N1f3336+EhAQdO3ZMc+fOVVxcnO677z5Jksvl0uTJkzVr1ix16NBBsbGxmj17tlJSUuxP9QAAgOtbyIGyd+9eDRkyxH787XtDJk2apOXLl+vAgQN69dVXdfr0aSUkJGjIkCFat26doqOj7ecsXbpU4eHhGj9+vKqrqzVs2DCtWrVKYWFhjXBKAACgpQs5UAYPHizLsurd/t57711xHzfeeKNefvllvfzyy6EeHgAAXAf4LR4AAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABgn5ED54IMPNHr0aCUmJsrhcOjtt98O2m5ZlnJzc5WYmKjIyEgNHjxYhw4dChoTCASUlZWluLg4RUVFacyYMTp58uQ1nQgAAGg9Qg6Us2fPqmfPnlq2bFmd2xctWqQlS5Zo2bJlKioqksfj0YgRI3TmzBl7THZ2tjZs2KD8/Hzt3r1bVVVVGjVqlGpraxt+JgAAoNUID/UJGRkZysjIqHObZVl68cUX9eyzz2rcuHGSpNWrV8vtduv111/XlClTVFlZqRUrVmjNmjUaPny4JOm1116T1+vVtm3blJ6efg2nAwAAWoNGfQ9KSUmJfD6f0tLS7HVOp1ODBg1SYWGhJKm4uFjnzp0LGpOYmKjk5GR7zMUCgYD8fn/QAgAAWq9GDRSfzydJcrvdQevdbre9zefzKSIiQu3bt693zMXy8vLkcrnsxev1Nua0AQCAYZrkUzwOhyPosWVZl6y72OXG5OTkqLKy0l5KS0sbba4AAMA8jRooHo9Hki65E1JeXm7fVfF4PKqpqVFFRUW9Yy7mdDoVExMTtAAAgNarUQOlS5cu8ng8KigosNfV1NRo165dSk1NlST16dNHbdq0CRpTVlamgwcP2mMAAMD1LeRP8VRVVeno0aP245KSEu3bt0+xsbG6+eablZ2drfnz56tr167q2rWr5s+fr7Zt2+qBBx6QJLlcLk2ePFmzZs1Shw4dFBsbq9mzZyslJcX+VA8AALi+hRwoe/fu1ZAhQ+zHM2fOlCRNmjRJq1at0pw5c1RdXa1p06apoqJC/fr109atWxUdHW0/Z+nSpQoPD9f48eNVXV2tYcOGadWqVQoLC2uEUwIAAC2dw7Isq7knESq/3y+Xy6XKysomeT9K52c2Nvo+gdbi2IJ7mnsKjYLrHLi8prjWQ/n3m9/iAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgnEYPlNzcXDkcjqDF4/HY2y3LUm5urhITExUZGanBgwfr0KFDjT0NAADQgjXJHZQ77rhDZWVl9nLgwAF726JFi7RkyRItW7ZMRUVF8ng8GjFihM6cOdMUUwEAAC1QkwRKeHi4PB6PvXTs2FHSP++evPjii3r22Wc1btw4JScna/Xq1frmm2/0+uuvN8VUAABAC9QkgXLkyBElJiaqS5cu+ulPf6rPP/9cklRSUiKfz6e0tDR7rNPp1KBBg1RYWFjv/gKBgPx+f9ACAABar0YPlH79+unVV1/Ve++9p9///vfy+XxKTU3VqVOn5PP5JElutzvoOW63295Wl7y8PLlcLnvxer2NPW0AAGCQRg+UjIwM3X///UpJSdHw4cO1ceNGSdLq1avtMQ6HI+g5lmVdsu67cnJyVFlZaS+lpaWNPW0AAGCQJv+YcVRUlFJSUnTkyBH70zwX3y0pLy+/5K7KdzmdTsXExAQtAACg9WryQAkEAvrss8+UkJCgLl26yOPxqKCgwN5eU1OjXbt2KTU1tamnAgAAWojwxt7h7NmzNXr0aN18880qLy/XvHnz5Pf7NWnSJDkcDmVnZ2v+/Pnq2rWrunbtqvnz56tt27Z64IEHGnsqAACghWr0QDl58qR+9rOf6auvvlLHjh111113ac+ePUpKSpIkzZkzR9XV1Zo2bZoqKirUr18/bd26VdHR0Y09FQAA0EI1eqDk5+dfdrvD4VBubq5yc3Mb+9AAAKCV4Ld4AACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgnGYNlN/85jfq0qWLbrzxRvXp00cffvhhc04HAAAYotkCZd26dcrOztazzz6rP//5zxowYIAyMjJ04sSJ5poSAAAwRLMFypIlSzR58mQ9+uijuu222/Tiiy/K6/Vq+fLlzTUlAABgiPDmOGhNTY2Ki4v1zDPPBK1PS0tTYWHhJeMDgYACgYD9uLKyUpLk9/ubZH4XAt80yX6B1qCprrvvG9c5cHlNca1/u0/Lsq44tlkC5auvvlJtba3cbnfQerfbLZ/Pd8n4vLw8/fznP79kvdfrbbI5Aqib68XmngGA70NTXutnzpyRy+W67JhmCZRvORyOoMeWZV2yTpJycnI0c+ZM+/GFCxf09ddfq0OHDnWOR+vh9/vl9XpVWlqqmJiY5p4OgCbCtX59sCxLZ86cUWJi4hXHNkugxMXFKSws7JK7JeXl5ZfcVZEkp9Mpp9MZtO5f/uVfmnKKMExMTAx/aQHXAa711u9Kd06+1Sxvko2IiFCfPn1UUFAQtL6goECpqanNMSUAAGCQZnuJZ+bMmZo4caL69u2r/v3763e/+51OnDihqVOnNteUAACAIZotUCZMmKBTp07phRdeUFlZmZKTk7Vp0yYlJSU115RgIKfTqeeff/6Sl/gAtC5c67iYw7qaz/oAAAB8j/gtHgAAYBwCBQAAGIdAAQAAxiFQrnODBw9WdnZ2c0/je/X222/r1ltvVVhYmLKzs7Vq1Sq+Vwet0s6dO+VwOHT69Olr2s+xY8fkcDi0b9++RpmXySzL0uOPP67Y2Fj7nK/HvydNwJtkr3ODBw9Wr1699OKLLzb3VL43brdbDz/8sJ588klFR0crPDxcZ86cUXx8fHNPDWhUNTU1+vrrr+V2u6/pW7dra2v197//XXFxcQoPb9YvIG9ymzdv1r333qudO3fqBz/4geLi4uT3+9WmTRtFR0c39/SuK637f2loVWpra+VwOHTDDQ2/8VdVVaXy8nKlp6cHfdVyZGRkvc85d+6c2rRp0+BjAs0lIiJCHo/nmvcTFhbWKPtpajU1NYqIiLimffz1r39VQkJC0JeGxsbGNvlxcSle4oEuXLigOXPmKDY2Vh6PR7m5ufa2JUuWKCUlRVFRUfJ6vZo2bZqqqqrs7cePH9fo0aPVvn17RUVF6Y477tCmTZuueMxvbz1v3LhRPXv21I033qh+/frpwIED9phvX3r5wx/+oNtvv11Op1PHjx9XTU2N5syZo5tuuklRUVHq16+fdu7ceVXH/Pb/AQ0dOlQOh0M7d+685CWe3Nxc9erVS//zP/+jH/zgB3I6nbIsS5WVlXr88ccVHx+vmJgYDR06VH/5y1+u/AcMNJLBgwcrKytL2dnZat++vdxut373u9/p7NmzevjhhxUdHa1bbrlFmzdvlnTpSzyXu14rKir04IMPqmPHjoqMjFTXrl21cuVKSZe+xPPtft9//3317dtXbdu2VWpqqg4fPhw033nz5ik+Pl7R0dF69NFH9cwzz6hXr15Xda6ZmZkaO3asfv7zn9vX3JQpU1RTUxP05zFjxgzNnDlTcXFxGjFihCTp008/1Y9//GO1a9dObrdbEydO1FdffXVVx8zKytKJEyfkcDjUuXNn+zjffYmnc+fOmjdvnjIzM+VyufTYY49JkgoLCzVw4EBFRkbK6/XqySef1NmzZ6/qfHEpAgVavXq1oqKi9Kc//UmLFi3SCy+8YP8MwQ033KD/+q//0sGDB7V69Wpt375dc+bMsZ87ffp0BQIBffDBBzpw4IAWLlyodu3aXfWx//3f/12//vWvVVRUpPj4eI0ZM0bnzp2zt3/zzTfKy8vTf//3f+vQoUOKj4/Xww8/rI8++kj5+fnav3+/fvKTn2jkyJE6cuTIZY/13b9A169fr7Kysnp/WuHo0aN68803tX79evsv5XvuuUc+n0+bNm1ScXGxevfurWHDhunrr7++6vMFrtXq1asVFxenjz/+WFlZWXriiSf0k5/8RKmpqfrkk0+Unp6uiRMn6ptvvrnkuZe7Xp977jl9+umn2rx5sz777DMtX75ccXFxl53Ls88+q8WLF2vv3r0KDw/XI488Ym9bu3atfvnLX2rhwoUqLi7WzTffrOXLl4d0ru+//74+++wz7dixQ2+88YY2bNhwyS/br169WuHh4froo4/0yiuvqKysTIMGDVKvXr20d+9ebdmyRV9++aXGjx9/xeO99NJLeuGFF9SpUyeVlZWpqKio3rG/+tWvlJycrOLiYj333HM6cOCA0tPTNW7cOO3fv1/r1q3T7t27NWPGjJDOGd9h4bo2aNAg6+677w5ad+edd1pPP/10nePffPNNq0OHDvbjlJQUKzc3N+Tj7tixw5Jk5efn2+tOnTplRUZGWuvWrbMsy7JWrlxpSbL27dtnjzl69KjlcDisv/3tb0H7GzZsmJWTk3PF41ZUVFiSrB07dtjrVq5cablcLvvx888/b7Vp08YqLy+3173//vtWTEyM9Y9//CNof7fccov1yiuvXNU5A9fq4uv1/PnzVlRUlDVx4kR7XVlZmSXJ+uMf/2hfZxUVFZZlXf56HT16tPXwww/Xua2kpMSSZP35z3+2LOv/X7/btm2zx2zcuNGSZFVXV1uWZVn9+vWzpk+fHrSfH/3oR1bPnj2v6lwnTZpkxcbGWmfPnrXXLV++3GrXrp1VW1tr/3n06tUr6HnPPfeclZaWFrSutLTUkmQdPnz4isddunSplZSUFLRu0KBB1lNPPWU/TkpKssaOHRs0ZuLEidbjjz8etO7DDz+0brjhBvvPBKHhDgrUo0ePoMcJCQkqLy+XJO3YsUMjRozQTTfdpOjoaD300EM6deqUfdvyySef1Lx58/SjH/1Izz//vPbv3x/Ssfv372//d2xsrLp3767PPvvMXhcRERE0v08++USWZalbt25q166dvezatUt//etfQz73+iQlJaljx4724+LiYlVVValDhw5Bxy0pKWnU4wJX8t3rISwsTB06dFBKSoq97ttfhP/2Gv6uy12vTzzxhPLz89WrVy/NmTNHhYWFIc0lISEh6LiHDx/Wv/3bvwWNv/jxlfTs2VNt27a1H/fv319VVVUqLS211/Xt2zfoOcXFxdqxY0fQdfqv//qvktSo12pdx121alXQcdPT03XhwgWVlJQ02nGvJ7xJFpe8AdThcOjChQs6fvy4fvzjH2vq1Kn6xS9+odjYWO3evVuTJ0+2X4Z59NFHlZ6ero0bN2rr1q3Ky8vT4sWLlZWV1eD5fPfTBpGRkUGPL1y4oLCwMBUXFyssLCzoeaG8tHQlUVFRQY8vXLighISEOt/rwkeU8X2q63r97rpvr5cLFy5c8tzLXa8ZGRk6fvy4Nm7cqG3btmnYsGGaPn26fv3rX1/VXOo67sWfHLIa6UOj391vXdfq6NGjtXDhwkue921ENYa6jjtlyhQ9+eSTl4y9+eabG+241xMCBfXau3evzp8/r8WLF9ufnHnzzTcvGef1ejV16lRNnTpVOTk5+v3vf3/VgbJnzx774q2oqND//d//2f9vpy4//OEPVVtbq/Lycg0YMKABZ9UwvXv3ls/nU3h4uP3GOaAlutz12rFjR2VmZiozM1MDBgyw3yPWEN27d9fHH3+siRMn2uv27t0b0j7+8pe/qLq62v6U3Z49e9SuXTt16tSp3uf07t1b69evV+fOnb/Xj0T37t1bhw4d0q233vq9HbO14yUe1OuWW27R+fPn9fLLL+vzzz/XmjVr9Nvf/jZoTHZ2tt577z2VlJTok08+0fbt23Xbbbdd9TFeeOEFvf/++zp48KAyMzMVFxensWPH1ju+W7duevDBB/XQQw/prbfeUklJiYqKirRw4cKr+vRQQw0fPlz9+/fX2LFj9d577+nYsWMqLCzUf/zHf4T8ly7QXC53vf7nf/6n3nnnHR09elSHDh3SH/7wh5Cu5YtlZWVpxYoVWr16tY4cOaJ58+Zp//79IX0fS01NjSZPnmy/eff555/XjBkzLvtVA9OnT9fXX3+tn/3sZ/r444/1+eefa+vWrXrkkUdUW1vb4PO5kqefflp//OMfNX36dO3bt09HjhzRu+++e013k693BArq1atXLy1ZskQLFy5UcnKy1q5dq7y8vKAxtbW1mj59um677TaNHDlS3bt3129+85urPsaCBQv01FNPqU+fPiorK9O77757xe8TWLlypR566CHNmjVL3bt315gxY/SnP/1JXq+3Qed5NRwOhzZt2qSBAwfqkUceUbdu3fTTn/5Ux44ds1/zB0x3ues1IiJCOTk56tGjhwYOHKiwsDDl5+c3+FgPPvigcnJyNHv2bPXu3VslJSXKzMzUjTfeeNX7GDZsmLp27aqBAwdq/PjxGj16dNDXINQlMTFRH330kWpra5Wenq7k5GQ99dRTcrlc1/QdSlfSo0cP7dq1S0eOHNGAAQP0wx/+UM8991yjvqx0veGbZNEsdu7cqSFDhqiiooL3cADXiREjRsjj8WjNmjVXHJuZmanTp0/r7bffbvqJwUi8BwUA0Oi++eYb/fa3v1V6errCwsL0xhtvaNu2bfZ3LAFXwks8aBJTp04N+rjdd5epU6c22XEzMjLqPe78+fOb7LgAgn37suiAAQPUp08f/e///q/Wr1+v4cOHS1K912m7du304YcfNsmcTpw4cdnjnjhxokmOi4bhJR40ifLycvn9/jq3xcTENNkP8/3tb39TdXV1ndtiY2Ov+JsaAL4fR48erXfbTTfddNnfx2qo8+fP69ixY/Vu/74/+YPLI1AAAIBxeIkHAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYJz/B+/9wvdVmH6ZAAAAAElFTkSuQmCC",
252
+ "text/plain": [
253
+ "<Figure size 640x480 with 1 Axes>"
254
+ ]
255
+ },
256
+ "metadata": {},
257
+ "output_type": "display_data"
258
+ }
259
+ ],
260
+ "source": [
261
+ "res_has_pre = []\n",
262
+ "res_no_pre = []\n",
263
+ "with h5py.File(fn, \"r\") as fd:\n",
264
+ " for name, ds in fd.items():\n",
265
+ " if \"pre_fire\" in ds:\n",
266
+ " res_has_pre.append(name)\n",
267
+ " else:\n",
268
+ " res_no_pre.append(name)\n",
269
+ " \n",
270
+ "x = [\"has_pre_fire\", \"missing_pre_fire\"] # X-axis values\n",
271
+ "y = [len(res_has_pre), len(res_no_pre)] # Y-axis values\n",
272
+ "\n",
273
+ "plt.bar(x, y)\n",
274
+ "#plt.show()\n",
275
+ "\n",
276
+ "plt.savefig('missing_prefire.eps')"
277
+ ]
278
+ },
279
+ {
280
+ "cell_type": "code",
281
+ "execution_count": null,
282
+ "id": "664466a8-906e-46c2-b269-336450464187",
283
+ "metadata": {
284
+ "tags": []
285
+ },
286
+ "outputs": [],
287
+ "source": []
288
+ },
289
+ {
290
+ "cell_type": "code",
291
+ "execution_count": null,
292
+ "id": "5f48ae8f-32e9-457f-bf9a-e6aeddf2e86f",
293
+ "metadata": {
294
+ "execution": {
295
+ "iopub.status.busy": "2023-07-12T19:18:37.515168Z",
296
+ "iopub.status.idle": "2023-07-12T19:18:37.516757Z",
297
+ "shell.execute_reply": "2023-07-12T19:18:37.516583Z",
298
+ "shell.execute_reply.started": "2023-07-12T19:18:37.516563Z"
299
+ },
300
+ "tags": []
301
+ },
302
+ "outputs": [],
303
+ "source": [
304
+ "df"
305
+ ]
306
+ },
307
+ {
308
+ "cell_type": "code",
309
+ "execution_count": null,
310
+ "id": "5cc93207-9157-459f-aab1-c712e6714b10",
311
+ "metadata": {},
312
+ "outputs": [],
313
+ "source": []
314
+ }
315
+ ],
316
+ "metadata": {
317
+ "kernelspec": {
318
+ "display_name": "Python 3.10 / DM",
319
+ "language": "python",
320
+ "name": "py310-dm"
321
+ },
322
+ "language_info": {
323
+ "codemirror_mode": {
324
+ "name": "ipython",
325
+ "version": 3
326
+ },
327
+ "file_extension": ".py",
328
+ "mimetype": "text/x-python",
329
+ "name": "python",
330
+ "nbconvert_exporter": "python",
331
+ "pygments_lexer": "ipython3",
332
+ "version": "3.10.10"
333
+ }
334
+ },
335
+ "nbformat": 4,
336
+ "nbformat_minor": 5
337
+ }
main.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.cuda
2
+
3
+ import chabud as ch
4
+
5
+ ds_path = "A:/CodingProjekte/DataMining/src/train_eval.hdf5"
6
+
7
+ # Press the green button in the gutter to run the script.
8
+ if __name__ == '__main__':
9
+ print(ch.__version__)
10
+ print(torch.cuda.is_available())
11
+ # See PyCharm help at https://www.jetbrains.com/help/pycharm/
12
+ channels = ["band_1", "band_2", "band_3", "band_4", "band_5", "band_6", "band_7", "band_8", "band_8a", "band_9",
13
+ "band_11", "band_12", "nbr", "ndvi", "gndvi", "evi", "avi", "savi", "ndmi", "msi", "gci", "bsi", "ndwi",
14
+ "ndgi"]
15
+ # channels = ["band_1", "band_2", "band_3", "band_4", "band_5", "band_6", "band_7", "band_8", "band_8a", "band_9",
16
+ # "band_11", "band_12", "nbr", "ndmi", "ndvi", "bsi", "ndwi"]
17
+ channels_fun = []
18
+
19
+ for channel in channels:
20
+ channels_fun.append(ch.CHANNEL_MAP[channel])
21
+
22
+ ch.main(accelerator="gpu",
23
+ datafile=ds_path,
24
+ batch_size=5,
25
+ learning_rate=0.00025,
26
+ channels=channels_fun,
27
+ n_cpus=0,
28
+ model="unet",
29
+ encoder="resnet34",
30
+ encoder_depth=5,
31
+ encoder_weights="imagenet",
32
+ loss="dice",
33
+ train_use_pre_fire=False,
34
+ train_use_augmentation=True)
35
+
36
+
submission.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ import h5py
4
+ import torch
5
+ from trimesh.voxel.runlength import dense_to_brle
6
+ from pathlib import Path
7
+ from matplotlib.colors import ListedColormap
8
+ from collections import defaultdict
9
+ from scipy import ndimage as ski
10
+ from typing import Any, Union, Dict, Literal
11
+ from numpy.typing import NDArray
12
+ import matplotlib as plt
13
+
14
+ import chabud
15
+ from pathlib import Path
16
+ dataset = Path("A:/CodingProjekte/DataMining/src/train_eval.hdf5")
17
+ #Es liegen 15 vortrainierte Modelle auf dem Server im Verzeichnis
18
+ #/global/public/chabud-ecml-pkdd2023/checkpoints/.
19
+ #Sie können sich verfügbaren Checkpoints wie folgt anzeigen lassen:
20
+ ckpt = Path('A:/CodingProjekte/DataMining/src/lightning_logs/version_30/checkpoints/model-epoch=25-val_iou=0.00.ckpt')
21
+
22
+ #Sie können einen beliebigen Checkpoint wie folgt laden:
23
+ mdl = chabud.FireModel.load_from_checkpoint(ckpt, map_location="cpu")
24
+
25
+ # Vom Modell `mdl` benötigte Kanäle extrahieren
26
+ # channels = np.stack([c(bands) for c in mdl.channels])
27
+
28
+
29
+ # with torch.set_grad_enabled(False):
30
+ # # Modell auf 1xlen(channels)x512x512 großen Tensor anwenden
31
+ # # D.h. wir haben eine Batchgröße von 1 (ineffizient aber einfach).
32
+ # print(channels)
33
+ # # channels = channels.astype(float)
34
+ # pred = mdl.forward(torch.Tensor(channels[np.newaxis, ...])).sigmoid() > 0.5
35
+ # # Ersten beiden Dimensionen (batch und channel) löschen und in ein numpy Array wandeln
36
+ # pred = pred[0, 0, ...].detach().numpy()
37
+
38
+
39
+ def process_dataset(scene, bands, true):
40
+ rgb = ski.exposure.adjust_gamma(np.clip(bands[..., [3, 2, 1]], 0, 1), 0.4)
41
+
42
+ channels = np.stack([c(bands) for c in mdl.channels])
43
+ with torch.set_grad_enabled(False):
44
+ pred = mdl.forward(torch.Tensor(channels[np.newaxis, ...])).sigmoid() > 0.5
45
+ pred = pred[0, 0, ...].detach().numpy()
46
+
47
+ cmap = ListedColormap(["white", "tab:brown", "tab:orange", "tab:blue"])
48
+ mask = np.zeros_like(pred, dtype=int)
49
+ mask = np.where(true & pred, 1, mask)
50
+ mask = np.where(~true & pred, 2, mask)
51
+ mask = np.where(true & ~pred, 3, mask)
52
+
53
+ true_edge = ski.feature.canny(true.astype("float")).astype("uint8")
54
+ pred_edge = ski.feature.canny(pred.astype("float")).astype("uint8")
55
+
56
+ fig, (axm, axi) = plt.subplots(ncols=2, figsize=(20, 10))
57
+ axm.imshow(mask, cmap=cmap, interpolation="nearest")
58
+
59
+ axi.imshow(rgb, interpolation="nearest")
60
+ axi.imshow(true_edge, cmap=ListedColormap(["#00000000", "tab:blue"]), interpolation="nearest")
61
+ axi.imshow(pred_edge, cmap=ListedColormap(["#00000000", "tab:orange"]), interpolation="nearest")
62
+
63
+ for ax in [axm, axi]:
64
+ ax.axes.xaxis.set_ticklabels([])
65
+ ax.axes.yaxis.set_ticklabels([])
66
+
67
+ fig.tight_layout()
68
+
69
+ fig.savefig(f"masks/{scene}-f_{dataset.attrs['fold']}.png")
70
+ plt.close()
71
+ # class RandomModel:
72
+ # def __init__(self, shape):
73
+ # self.shape = shape
74
+ # return
75
+
76
+ # def __call__(self, input):
77
+ # # input is ignored, just generate some random predictions
78
+ # return np.random.randint(0, 2, size=self.shape, dtype=bool)
79
+
80
+ class FixedModel:
81
+ def __init__(self, shape) -> None:
82
+ self.shape = shape
83
+ return
84
+
85
+ def __call__(self, input) -> Any:
86
+ # input is ignored, just generate a mask filled with zeros, with fixed pixels set to 1
87
+ mask = np.zeros(self.shape, dtype=bool)
88
+ mask[100:250, 100:250] = True
89
+ return mask
90
+
91
+
92
+ def retrieve_validation_fold(path: Union[str, Path]) -> Dict[str, NDArray]:
93
+ result = defaultdict(dict)
94
+ with h5py.File(path, 'r') as fp:
95
+ for uuid, values in fp.items():
96
+ if values.attrs['fold'] != 0:
97
+ continue
98
+
99
+ result[uuid]['post'] = values['post_fire'][...]
100
+ # result[uuid]['pre'] = values['pre_fire'][...]
101
+
102
+ return dict(result)
103
+
104
+
105
+ def compute_submission_mask(id: str, mask: NDArray):
106
+ brle = dense_to_brle(mask.astype(bool).flatten())
107
+ return {"id": id, "rle_mask": brle, "index": np.arange(len(brle))}
108
+
109
+
110
+ #der Code aus dem letzten Workshop
111
+ class PPModel:
112
+ def __init__(self,model):
113
+ self._model = model
114
+ self._model.eval()
115
+
116
+ def __call__(self,bands) -> Any:
117
+ #preprocessing
118
+ bands = bands /10000
119
+ channels = np.stack([c(bands) for c in self._model.channels])
120
+ channels = torch.Tensor(channels[np.newaxis, ...])
121
+ #Modell auswerten
122
+ with torch.set_grad_enabled(False):
123
+ mask = self._model.forward(channels).sigmoid() > 0.5
124
+ #postprocessing
125
+ mask = mask[0,0, ...].detach().numpy()
126
+ return mask
127
+
128
+
129
+
130
+ if __name__ == '__main__':
131
+ validation_fold = retrieve_validation_fold('train_eval.hdf5')
132
+
133
+ # use a list to accumulate results
134
+ result = []
135
+ # instantiate the model
136
+ # model = FixedModel(shape=(512, 512))
137
+ model = PPModel(mdl)
138
+ for uuid in validation_fold:
139
+ input_images = validation_fold[uuid]['post']
140
+
141
+ # perform the prediction
142
+ predicted = model(input_images)
143
+ # convert the prediction in RLE format
144
+ encoded_prediction = compute_submission_mask(uuid, predicted)
145
+ result.append(pd.DataFrame(encoded_prediction))
146
+
147
+ # concatenate all dataframes
148
+ submission_df = pd.concat(result)
149
+ submission_df.to_csv('predictions.csv', index=False)