kuva-reader 1.0.4__tar.gz → 1.1.6__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.
- kuva_reader-1.1.6/.gitignore +134 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/PKG-INFO +13 -22
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/README.md +2 -2
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/__init__.py +3 -9
- kuva_reader-1.1.6/kuva_reader/reader/image.py +28 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/level0.py +53 -55
- kuva_reader-1.1.6/kuva_reader/reader/level1.py +403 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/level2.py +52 -21
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/product_base.py +9 -8
- kuva_reader-1.1.6/poetry.lock +987 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/pyproject.toml +32 -26
- kuva_reader-1.1.6/tests/__init__.py +0 -0
- kuva_reader-1.1.6/tests/test_reader.py +148 -0
- kuva_reader-1.1.6/uv.lock +892 -0
- kuva_reader-1.0.4/kuva_reader/reader/image.py +0 -203
- kuva_reader-1.0.4/kuva_reader/reader/level1.py +0 -258
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/py.typed +0 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/__init__.py +0 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/py.typed +0 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/read.py +0 -0
- {kuva_reader-1.0.4 → kuva_reader-1.1.6}/kuva_reader/reader/utils.py +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
*.ipynb
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Translations
|
|
52
|
+
*.mo
|
|
53
|
+
*.pot
|
|
54
|
+
|
|
55
|
+
# Django stuff:
|
|
56
|
+
*.log
|
|
57
|
+
local_settings.py
|
|
58
|
+
db.sqlite3
|
|
59
|
+
|
|
60
|
+
# Flask stuff:
|
|
61
|
+
instance/
|
|
62
|
+
.webassets-cache
|
|
63
|
+
|
|
64
|
+
# Torch stuff
|
|
65
|
+
lightning_logs/
|
|
66
|
+
|
|
67
|
+
# Scrapy stuff:
|
|
68
|
+
.scrapy
|
|
69
|
+
|
|
70
|
+
# Sphinx documentation
|
|
71
|
+
docs/_build/
|
|
72
|
+
|
|
73
|
+
# PyBuilder
|
|
74
|
+
target/
|
|
75
|
+
|
|
76
|
+
# Jupyter Notebook
|
|
77
|
+
.ipynb_checkpoints
|
|
78
|
+
|
|
79
|
+
# VSCode
|
|
80
|
+
.vscode
|
|
81
|
+
|
|
82
|
+
# pyenv
|
|
83
|
+
.python-version
|
|
84
|
+
|
|
85
|
+
# celery beat schedule file
|
|
86
|
+
celerybeat-schedule
|
|
87
|
+
|
|
88
|
+
# SageMath parsed files
|
|
89
|
+
*.sage.py
|
|
90
|
+
|
|
91
|
+
# Environments
|
|
92
|
+
.env
|
|
93
|
+
.venv
|
|
94
|
+
env/
|
|
95
|
+
venv/
|
|
96
|
+
ENV/
|
|
97
|
+
env.bak/
|
|
98
|
+
venv.bak/
|
|
99
|
+
|
|
100
|
+
# Spyder project settings
|
|
101
|
+
.spyderproject
|
|
102
|
+
.spyproject
|
|
103
|
+
|
|
104
|
+
# Rope project settings
|
|
105
|
+
.ropeproject
|
|
106
|
+
|
|
107
|
+
# mkdocs documentation
|
|
108
|
+
/site
|
|
109
|
+
|
|
110
|
+
# mypy
|
|
111
|
+
.mypy_cache/
|
|
112
|
+
|
|
113
|
+
# Direnv stuff
|
|
114
|
+
.direnv/
|
|
115
|
+
.envrc
|
|
116
|
+
|
|
117
|
+
# Poetry
|
|
118
|
+
# poetry.lock
|
|
119
|
+
|
|
120
|
+
# Supervisord
|
|
121
|
+
supervisord.log
|
|
122
|
+
supervisord.pid
|
|
123
|
+
|
|
124
|
+
# Debug folders
|
|
125
|
+
_debug/
|
|
126
|
+
|
|
127
|
+
# Kuva data
|
|
128
|
+
*.tif
|
|
129
|
+
*.tiff
|
|
130
|
+
*.npy
|
|
131
|
+
hyperfield*.json
|
|
132
|
+
|
|
133
|
+
# Do not ignore Kuva files in the test_data directory
|
|
134
|
+
!kuva-reader/tests/test_data/**
|
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: kuva-reader
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.6
|
|
4
4
|
Summary: Manipulate the Kuva Space image and metadata formats
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Requires-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Requires-Dist: kuva-geometry
|
|
15
|
-
Requires-Dist: kuva-metadata
|
|
16
|
-
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
|
17
|
-
Requires-Dist: numpy-quaternion (>=2022.4.4,<2023.0.0)
|
|
18
|
-
Requires-Dist: pint (>=0.22,<0.23)
|
|
19
|
-
Requires-Dist: rasterio (>=1.4.1,<2.0.0)
|
|
20
|
-
Requires-Dist: rioxarray (>=0.12.4,<0.13.0)
|
|
21
|
-
Requires-Dist: xarray (>=2022.12.0,<2023.0.0)
|
|
5
|
+
Author-email: Guillem Ballesteros <guillem@kuvaspace.com>, Lennert Antson <lennert.antson@kuvaspace.com>, Arthur Vandenhoeke <arthur.vandenhoeke@kuvaspace.com>, Olli Eloranta <olli.eloranta@kuvaspace.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: <=3.13,>=3.10
|
|
8
|
+
Requires-Dist: kuva-geometry<2.0.0,>=1.0.1
|
|
9
|
+
Requires-Dist: kuva-metadata<2.0.0,>=1.1.1
|
|
10
|
+
Requires-Dist: numpy-quaternion>=2023.4.4
|
|
11
|
+
Requires-Dist: numpy>=1.26.4
|
|
12
|
+
Requires-Dist: pint<1.0.0,>=0.22
|
|
13
|
+
Requires-Dist: rasterio<2,>=1.4.3
|
|
22
14
|
Description-Content-Type: text/markdown
|
|
23
15
|
|
|
24
16
|
<div align="center">
|
|
@@ -36,7 +28,7 @@ The Kuva Space images are in GeoTIFF format. The products consist of an image or
|
|
|
36
28
|
images along with its metadata to give all the necessary information to use the products.
|
|
37
29
|
The metadata lives either in a Kuva Space database, or alternatively in a sidecar JSON file.
|
|
38
30
|
|
|
39
|
-
This library allows the reading of the image GeoTIFFs into `
|
|
31
|
+
This library allows the reading of the image GeoTIFFs into `rasterio.DatasetReader` objects that
|
|
40
32
|
allow convenient raster manipulations, along with their `kuva-metadata` metadata objects.
|
|
41
33
|
|
|
42
34
|
# Installation
|
|
@@ -56,7 +48,7 @@ pip install kuva-reader
|
|
|
56
48
|
This is a minimal example that allows you to read and print the image shape of a L2 product.
|
|
57
49
|
|
|
58
50
|
The result product is in this case an L2A product (as seen from the folder name).
|
|
59
|
-
The loaded product is stored in a `
|
|
51
|
+
The loaded product is stored in a `rasterio.DatasetReader` object, which contains extensive GIS functionalities [(examples for usage)](https://rasterio.readthedocs.io/en/stable/api/rasterio.io.html#rasterio.io.DatasetReader).
|
|
60
52
|
|
|
61
53
|
```python
|
|
62
54
|
from kuva_reader import read_product
|
|
@@ -130,4 +122,3 @@ The `kuva-reader` project software is under the [MIT license](https://github.com
|
|
|
130
122
|
# Status of unit tests
|
|
131
123
|
|
|
132
124
|
[](https://github.com/KuvaSpace/kuva-data-processing/actions/workflows/test-kuva-reader.yml)
|
|
133
|
-
|
|
@@ -13,7 +13,7 @@ The Kuva Space images are in GeoTIFF format. The products consist of an image or
|
|
|
13
13
|
images along with its metadata to give all the necessary information to use the products.
|
|
14
14
|
The metadata lives either in a Kuva Space database, or alternatively in a sidecar JSON file.
|
|
15
15
|
|
|
16
|
-
This library allows the reading of the image GeoTIFFs into `
|
|
16
|
+
This library allows the reading of the image GeoTIFFs into `rasterio.DatasetReader` objects that
|
|
17
17
|
allow convenient raster manipulations, along with their `kuva-metadata` metadata objects.
|
|
18
18
|
|
|
19
19
|
# Installation
|
|
@@ -33,7 +33,7 @@ pip install kuva-reader
|
|
|
33
33
|
This is a minimal example that allows you to read and print the image shape of a L2 product.
|
|
34
34
|
|
|
35
35
|
The result product is in this case an L2A product (as seen from the folder name).
|
|
36
|
-
The loaded product is stored in a `
|
|
36
|
+
The loaded product is stored in a `rasterio.DatasetReader` object, which contains extensive GIS functionalities [(examples for usage)](https://rasterio.readthedocs.io/en/stable/api/rasterio.io.html#rasterio.io.DatasetReader).
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
39
|
from kuva_reader import read_product
|
|
@@ -17,17 +17,14 @@ Key Features
|
|
|
17
17
|
Dependencies
|
|
18
18
|
- kuva-metadata: A specialized library that handles the extraction and
|
|
19
19
|
parsing of metadata associated with Kuva Space products.
|
|
20
|
-
-
|
|
21
|
-
including
|
|
20
|
+
- rasterio: Used for loading image data as arrays with extra functionality,
|
|
21
|
+
including GIS specific functions and metadata, which are useful for analysis and
|
|
22
22
|
visualization.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
__version__ = "
|
|
25
|
+
__version__ = "1.1.2"
|
|
26
26
|
|
|
27
27
|
from .reader.image import (
|
|
28
|
-
image_to_dtype_range,
|
|
29
|
-
image_to_original_range,
|
|
30
|
-
image_to_uint16_range,
|
|
31
28
|
image_footprint,
|
|
32
29
|
)
|
|
33
30
|
from .reader.level0 import Level0Product
|
|
@@ -40,9 +37,6 @@ __all__ = [
|
|
|
40
37
|
"Level1ABProduct",
|
|
41
38
|
"Level1CProduct",
|
|
42
39
|
"Level2AProduct",
|
|
43
|
-
"image_to_dtype_range",
|
|
44
|
-
"image_to_original_range",
|
|
45
|
-
"image_to_uint16_range",
|
|
46
40
|
"image_footprint",
|
|
47
41
|
"read_product",
|
|
48
42
|
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Utilities to process images related to product processing."""
|
|
2
|
+
|
|
3
|
+
import rasterio as rio
|
|
4
|
+
from shapely.geometry import box, Polygon
|
|
5
|
+
from rasterio.warp import transform_bounds
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def image_footprint(image: rio.DatasetReader, crs: str = "") -> Polygon:
|
|
9
|
+
"""Return a product footprint as a shapely polygon
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
image
|
|
14
|
+
The product image
|
|
15
|
+
crs, optional
|
|
16
|
+
CRS to convert to, by default "", keeping the image's CRS
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
A shapely polygon footprint
|
|
21
|
+
"""
|
|
22
|
+
if crs:
|
|
23
|
+
# Transform the bounds to the new CRS using rasterio's built-in function
|
|
24
|
+
bounds = transform_bounds(image.crs, crs, *image.bounds)
|
|
25
|
+
footprint = box(*bounds)
|
|
26
|
+
else:
|
|
27
|
+
footprint = box(*image.bounds)
|
|
28
|
+
return footprint
|
|
@@ -2,15 +2,14 @@ from pathlib import Path
|
|
|
2
2
|
from typing import cast
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
|
-
import
|
|
6
|
-
import xarray
|
|
5
|
+
import rasterio as rio
|
|
7
6
|
from kuva_metadata import MetadataLevel0
|
|
8
7
|
from pint import UnitRegistry
|
|
9
8
|
from shapely import Polygon
|
|
10
9
|
|
|
11
|
-
from kuva_reader import
|
|
10
|
+
from kuva_reader import image_footprint
|
|
12
11
|
|
|
13
|
-
from .product_base import ProductBase
|
|
12
|
+
from .product_base import NUM_THREADS, ProductBase
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
class Level0Product(ProductBase[MetadataLevel0]):
|
|
@@ -39,13 +38,6 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
39
38
|
target_ureg, optional
|
|
40
39
|
Pint Unit Registry to swap to. This is only relevant when parsing data from a
|
|
41
40
|
JSON file, which by default uses the kuva-metadata ureg.
|
|
42
|
-
as_physical_unit
|
|
43
|
-
Whether to denormalize data from full data type range back to the physical
|
|
44
|
-
units stored with the data, by default False
|
|
45
|
-
target_dtype
|
|
46
|
-
Target data type to normalize data to. This will first denormalize the data
|
|
47
|
-
to its original range and then normalize to new data type range to keep a
|
|
48
|
-
scale and offset, by default None
|
|
49
41
|
|
|
50
42
|
Attributes
|
|
51
43
|
----------
|
|
@@ -53,10 +45,9 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
53
45
|
Path to the folder containing the images.
|
|
54
46
|
metadata: MetadataLevel0
|
|
55
47
|
The metadata associated with the images
|
|
56
|
-
images: Dict[str,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
can be retrieved like so: `ds.rio.get_gcps()`
|
|
48
|
+
images: Dict[str, rasterio.DatasetReader]
|
|
49
|
+
A dictionary that maps camera names to their respective Rasterio DatasetReader
|
|
50
|
+
objects.
|
|
60
51
|
data_tags: Dict[str, Any]
|
|
61
52
|
Tags stored along with the data. These can be used e.g. to check the physical
|
|
62
53
|
units of pixels or normalisation factors.
|
|
@@ -67,58 +58,53 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
67
58
|
image_path: Path,
|
|
68
59
|
metadata: MetadataLevel0 | None = None,
|
|
69
60
|
target_ureg: UnitRegistry | None = None,
|
|
70
|
-
as_physical_unit: bool = False,
|
|
71
|
-
target_dtype: np.dtype | None = None,
|
|
72
61
|
) -> None:
|
|
73
62
|
super().__init__(image_path, metadata, target_ureg)
|
|
74
63
|
|
|
75
|
-
self.
|
|
64
|
+
self._images = {
|
|
76
65
|
camera: cast(
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
rio.DatasetReader,
|
|
67
|
+
rio.open(
|
|
79
68
|
self.image_path / (cube.camera.name + ".tif"),
|
|
69
|
+
num_threads=NUM_THREADS,
|
|
80
70
|
),
|
|
81
71
|
)
|
|
82
72
|
for camera, cube in self.metadata.image.data_cubes.items() # type: ignore
|
|
83
73
|
}
|
|
84
|
-
self.crs = self.images[list(self.images.keys())[0]].
|
|
74
|
+
self.crs = self.images[list(self.images.keys())[0]].crs
|
|
85
75
|
|
|
86
76
|
# Read tags for images and denormalize / renormalize if needed
|
|
87
|
-
self.data_tags = {camera:
|
|
88
|
-
if as_physical_unit or target_dtype:
|
|
89
|
-
for camera, img in self.images.items():
|
|
90
|
-
# Move from normalized full scale back to original data float values.
|
|
91
|
-
# pop() since values not true anymore after denormalization.
|
|
92
|
-
norm_img = image_to_original_range(
|
|
93
|
-
img,
|
|
94
|
-
self.data_tags[camera].pop("data_offset"),
|
|
95
|
-
self.data_tags[camera].pop("data_scale"),
|
|
96
|
-
)
|
|
97
|
-
self.images[camera] = norm_img
|
|
98
|
-
|
|
99
|
-
if target_dtype:
|
|
100
|
-
# For algorithm needs, cast and normalize to a specific dtype range
|
|
101
|
-
# NOTE: This may remove data precision e.g. uint16 -> uint8
|
|
102
|
-
norm_img, offset, scale = image_to_dtype_range(img, target_dtype)
|
|
103
|
-
self.data_tags[camera]["data_offset"] = offset
|
|
104
|
-
self.data_tags[camera]["data_scale"] = scale
|
|
77
|
+
self.data_tags = {camera: src.tags() for camera, src in self.images.items()}
|
|
105
78
|
|
|
106
79
|
def __repr__(self):
|
|
107
80
|
"""Pretty printing of the object with the most important info"""
|
|
108
81
|
if self.images is not None and len(self.images):
|
|
82
|
+
image_shapes = []
|
|
83
|
+
for camera_name, image in self.images.items():
|
|
84
|
+
shape_str = f"({image.count}, {image.height}, {image.width})"
|
|
85
|
+
image_shapes.append(f"{camera_name.upper()} shape {shape_str}")
|
|
86
|
+
|
|
87
|
+
shapes_description = " and ".join(image_shapes)
|
|
88
|
+
|
|
109
89
|
return (
|
|
110
|
-
f"{self.__class__.__name__}"
|
|
111
|
-
f"with
|
|
112
|
-
f"
|
|
113
|
-
f"(CRS '{self.crs}'). Loaded from: '{self.image_path}'."
|
|
90
|
+
f"{self.__class__.__name__} "
|
|
91
|
+
f"with {shapes_description} and "
|
|
92
|
+
f"CRS: '{self.crs}'. Loaded from: '{self.image_path}'."
|
|
114
93
|
)
|
|
115
94
|
else:
|
|
116
95
|
return f"{self.__class__.__name__} loaded from '{self.image_path}'."
|
|
117
96
|
|
|
118
|
-
def __getitem__(self, camera: str) ->
|
|
97
|
+
def __getitem__(self, camera: str) -> rio.DatasetReader:
|
|
119
98
|
"""Return the datarray for the chosen camera."""
|
|
120
99
|
return self.images[camera]
|
|
121
100
|
|
|
101
|
+
@property
|
|
102
|
+
def images(self) -> dict[str, rio.DatasetReader]:
|
|
103
|
+
if self._images is None:
|
|
104
|
+
e_ = "Images have been released. Re-open the product to access it again."
|
|
105
|
+
raise RuntimeError(e_)
|
|
106
|
+
return self._images
|
|
107
|
+
|
|
122
108
|
def keys(self) -> list[str]:
|
|
123
109
|
"""Easy access to the camera keys."""
|
|
124
110
|
return list(self.images.keys())
|
|
@@ -192,7 +178,11 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
192
178
|
def read_frame(self, cube: str, band_id: int, frame_idx: int) -> np.ndarray:
|
|
193
179
|
"""Extract a specific frame from a cube and band."""
|
|
194
180
|
frame_offset = self.calculate_frame_offset(cube, band_id, frame_idx)
|
|
195
|
-
|
|
181
|
+
|
|
182
|
+
# Rasterio index starts at 1
|
|
183
|
+
frame_offset += 1
|
|
184
|
+
|
|
185
|
+
return self[cube].read(frame_offset)
|
|
196
186
|
|
|
197
187
|
def read_band(self, cube: str, band_id: int) -> np.ndarray:
|
|
198
188
|
"""Extract a specific band from a cube"""
|
|
@@ -201,7 +191,12 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
201
191
|
# Calculate the final frame offset for this band and frame
|
|
202
192
|
band_offset_ll = band_offsets[band_id]
|
|
203
193
|
band_offset_ul = band_offset_ll + band_n_frames[band_id]
|
|
204
|
-
|
|
194
|
+
|
|
195
|
+
# Rasterio index starts at 1
|
|
196
|
+
band_offset_ll += 1
|
|
197
|
+
band_offset_ul += 1
|
|
198
|
+
|
|
199
|
+
return self[cube].read(list(np.arange(band_offset_ll, band_offset_ul)))
|
|
205
200
|
|
|
206
201
|
def read_data_units(self) -> np.ndarray:
|
|
207
202
|
"""Read unit of product and validate they match between cameras"""
|
|
@@ -213,7 +208,7 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
213
208
|
e_ = "Cameras have different physical units stored to them."
|
|
214
209
|
raise ValueError(e_)
|
|
215
210
|
|
|
216
|
-
def get_bad_pixel_mask(self, camera: str | None = None) ->
|
|
211
|
+
def get_bad_pixel_mask(self, camera: str | None = None) -> rio.DatasetReader:
|
|
217
212
|
"""Get the bad pixel mask associated to each camera of the L0 product
|
|
218
213
|
|
|
219
214
|
Returns
|
|
@@ -226,7 +221,7 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
226
221
|
bad_pixel_filename = f"{camera}_per_frame_bad_pixel_mask.tif"
|
|
227
222
|
return self._read_array(self.image_path / bad_pixel_filename)
|
|
228
223
|
|
|
229
|
-
def get_cloud_mask(self, camera: str | None = None) ->
|
|
224
|
+
def get_cloud_mask(self, camera: str | None = None) -> rio.DatasetReader:
|
|
230
225
|
"""Get the cloud mask associated to the product.
|
|
231
226
|
|
|
232
227
|
Returns
|
|
@@ -240,14 +235,17 @@ class Level0Product(ProductBase[MetadataLevel0]):
|
|
|
240
235
|
return self._read_array(self.image_path / bad_pixel_filename)
|
|
241
236
|
|
|
242
237
|
def release_memory(self):
|
|
243
|
-
"""Explicitely
|
|
244
|
-
|
|
245
|
-
NOTE: this function is implemented because of a memory leak inside the Rioxarray
|
|
246
|
-
library that doesn't release memory properly. Only use it when the image data is
|
|
247
|
-
not needed anymore.
|
|
238
|
+
"""Explicitely closes the Rasterio DatasetReaders and releases the memory of
|
|
239
|
+
the `images` variable.
|
|
248
240
|
"""
|
|
249
|
-
|
|
250
|
-
|
|
241
|
+
if self._images is not None:
|
|
242
|
+
for k in self._images.keys():
|
|
243
|
+
self._images[k].close()
|
|
244
|
+
|
|
245
|
+
del self._images
|
|
246
|
+
# We know that images are not None as long as somebody doesn't call
|
|
247
|
+
# this function beforehand....
|
|
248
|
+
self._images = None
|
|
251
249
|
|
|
252
250
|
|
|
253
251
|
def generate_level_0_metafile():
|