Skip to content

Images

siapy.utils.images

save_image

save_image(image: ndarray, save_path: str | Path, *, metadata: dict[str, Any] | None = None, overwrite: bool = True, dtype: type[ImageDataType] = np.float32)
PARAMETER DESCRIPTION
image

TYPE: ndarray

save_path

TYPE: str | Path

metadata

TYPE: dict[str, Any] | None DEFAULT: None

overwrite

TYPE: bool DEFAULT: True

dtype

TYPE: type[ImageDataType] DEFAULT: float32

Source code in siapy/utils/images.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def save_image(
    image: Annotated[np.ndarray, "The image to save."],
    save_path: Annotated[
        str | Path, "Header file (with '.hdr' extension) name with path."
    ],
    *,
    metadata: Annotated[
        dict[str, Any] | None,
        "A dict containing ENVI header parameters (e.g., parameters extracted from a source image).",
    ] = None,
    overwrite: Annotated[
        bool,
        "If the associated image file or header already exist and set to True, the files will be overwritten; otherwise, if either of the files exist, an exception will be raised.",
    ] = True,
    dtype: Annotated[
        type[ImageDataType],
        "The numpy data type with which to store the image.",
    ] = np.float32,
):
    if isinstance(save_path, str):
        save_path = Path(save_path)
    if metadata is None:
        metadata = {}

    os.makedirs(save_path.parent, exist_ok=True)
    sp.envi.save_image(
        hdr_file=save_path,
        image=image,
        dtype=dtype,
        force=overwrite,
        metadata=metadata,
    )
    logger.info(f"Image saved as:  {save_path}")

create_image

create_image(image: ndarray, save_path: str | Path, *, metadata: dict[str, Any] | None = None, overwrite: bool = True, dtype: type[ImageDataType] = np.float32) -> SpectralImage
PARAMETER DESCRIPTION
image

TYPE: ndarray

save_path

TYPE: str | Path

metadata

TYPE: dict[str, Any] | None DEFAULT: None

overwrite

TYPE: bool DEFAULT: True

dtype

TYPE: type[ImageDataType] DEFAULT: float32

Source code in siapy/utils/images.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def create_image(
    image: Annotated[np.ndarray, "The image to save."],
    save_path: Annotated[
        str | Path, "Header file (with '.hdr' extension) name with path."
    ],
    *,
    metadata: Annotated[
        dict[str, Any] | None,
        "A dict containing ENVI header parameters (e.g., parameters extracted from a source image).",
    ] = None,
    overwrite: Annotated[
        bool,
        "If the associated image file or header already exist and set to True, the files will be overwritten; otherwise, if either of the files exist, an exception will be raised.",
    ] = True,
    dtype: Annotated[
        type[ImageDataType],
        "The numpy data type with which to store the image.",
    ] = np.float32,
) -> SpectralImage:
    if isinstance(save_path, str):
        save_path = Path(save_path)
    if metadata is None:
        metadata = {
            "lines": image.shape[0],
            "samples": image.shape[1],
            "bands": image.shape[2],
        }

    os.makedirs(save_path.parent, exist_ok=True)
    spectral_image = sp.envi.create_image(
        hdr_file=save_path,
        metadata=metadata,
        dtype=dtype,
        force=overwrite,
    )
    mmap = spectral_image.open_memmap(writable=True)
    mmap[:, :, :] = image
    logger.info(f"Image created as:  {save_path}")
    return SpectralImage(spectral_image)

merge_images_by_specter

merge_images_by_specter(*, image_original: SpectralImage, image_to_merge: SpectralImage, save_path: str | Path, overwrite: bool = True, dtype: type[ImageDataType] = np.float32, auto_metadata_extraction: bool = True)
PARAMETER DESCRIPTION
image_original

TYPE: SpectralImage

image_to_merge

TYPE: SpectralImage

save_path

TYPE: str | Path

overwrite

TYPE: bool DEFAULT: True

dtype

TYPE: type[ImageDataType] DEFAULT: float32

auto_metadata_extraction

TYPE: bool DEFAULT: True

Source code in siapy/utils/images.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def merge_images_by_specter(
    *,
    image_original: Annotated[SpectralImage, "Original image."],
    image_to_merge: Annotated[
        SpectralImage, "Image which will be merged onto original image."
    ],
    save_path: Annotated[
        str | Path, "Header file (with '.hdr' extension) name with path."
    ],
    overwrite: Annotated[
        bool,
        "If the associated image file or header already exist and set to True, the files will be overwritten; otherwise, if either of the files exist, an exception will be raised.",
    ] = True,
    dtype: Annotated[
        type[ImageDataType],
        "The numpy data type with which to store the image.",
    ] = np.float32,
    auto_metadata_extraction: Annotated[
        bool,
        "Whether to automatically extract metadata images.",
    ] = True,
):
    image_original_np = image_original.to_numpy()
    image_to_merge_np = image_to_merge.to_numpy()

    metadata = {
        "lines": image_original.shape[0],
        "samples": image_original.shape[1],
        "bands": image_original.shape[2] + image_to_merge.shape[2],
    }
    if auto_metadata_extraction:
        original_meta = image_original.metadata
        merged_meta = image_to_merge.metadata
        metadata_ext = {}

        metadata_ext["wavelength"] = original_meta.get(
            "wavelength", []
        ) + merged_meta.get("wavelength", [])
        metadata_ext["data type"] = original_meta.get("data type", "")
        metadata_ext["byte order"] = original_meta.get("byte order", "")
        metadata_ext["data ignore value"] = original_meta.get("data ignore value", "")
        metadata_ext["header offset"] = original_meta.get("header offset", 0)
        metadata_ext["interleave"] = original_meta.get("interleave", "")
        metadata_ext["wavelength units"] = original_meta.get("wavelength units", "")
        metadata_ext["acquisition date"] = original_meta.get("acquisition date", "")

        metadata_ext["default bands"] = original_meta.get("default bands", [])
        metadata_ext["default bands additional"] = merged_meta.get("default bands", [])
        metadata_ext["description"] = original_meta.get("description", "")
        # metadata_ext["description additional"] = merged_meta.get("description", "")

        metadata.update(metadata_ext)

    image_to_merge_np = rescale(
        image_to_merge_np,
        (image_original_np.shape[0], image_original_np.shape[1]),
    )
    image_to_merge_np = image_to_merge_np.astype(image_original_np.dtype)
    image_merged = np.concatenate((image_original_np, image_to_merge_np), axis=2)

    return create_image(
        image=image_merged,
        save_path=save_path,
        metadata=metadata,
        overwrite=overwrite,
        dtype=dtype,
    )

convert_radiance_image_to_reflectance

convert_radiance_image_to_reflectance(image: SpectralImage, panel_correction: ndarray, save_path: str | Path | None = None, **kwargs: Any) -> ndarray | SpectralImage
PARAMETER DESCRIPTION
image

TYPE: SpectralImage

panel_correction

TYPE: ndarray

save_path

TYPE: str | Path | None DEFAULT: None

**kwargs

TYPE: Any DEFAULT: {}

Source code in siapy/utils/images.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def convert_radiance_image_to_reflectance(
    image: SpectralImage,
    panel_correction: np.ndarray,
    save_path: Annotated[
        str | Path | None, "Header file (with '.hdr' extension) name with path."
    ] = None,
    **kwargs: Any,
) -> np.ndarray | SpectralImage:
    image_ref_np = image.to_numpy() * panel_correction
    if save_path is None:
        return image_ref_np

    return create_image(
        image=image_ref_np, save_path=save_path, metadata=image.metadata, **kwargs
    )

calculate_correction_factor_from_panel

calculate_correction_factor_from_panel(image: SpectralImage, panel_reference_reflectance: float, panel_shape_label: str | None = None) -> ndarray
PARAMETER DESCRIPTION
image

TYPE: SpectralImage

panel_reference_reflectance

TYPE: float

panel_shape_label

TYPE: str | None DEFAULT: None

Source code in siapy/utils/images.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def calculate_correction_factor_from_panel(
    image: SpectralImage,
    panel_reference_reflectance: float,
    panel_shape_label: str | None = None,
) -> np.ndarray:
    if panel_shape_label:
        panel_shape = image.geometric_shapes.get_by_name(panel_shape_label)
        if not panel_shape:
            raise InvalidInputError(
                input_value={"panel_shape_label": panel_shape_label},
                message="Panel shape label not found.",
            )
        panel_signatures = image.to_signatures(panel_shape.convex_hull())
        panel_radiance_mean = panel_signatures.signals.mean()

    else:
        temp_mean = image.mean(axis=(0, 1))
        if not isinstance(temp_mean, np.ndarray):
            raise InvalidInputError(
                input_value={"image": image},
                message=f"Expected image.mean(axis=(0, 1)) to return np.ndarray, but got {type(temp_mean).__name__}.",
            )
        panel_radiance_mean = temp_mean

    panel_reflectance_mean = np.full(image.bands, panel_reference_reflectance)
    panel_correction = panel_reflectance_mean / panel_radiance_mean
    return panel_correction

blockfy_image

blockfy_image(image: ImageType, p: int, q: int) -> list[ndarray]
PARAMETER DESCRIPTION
image

TYPE: ImageType

p

TYPE: int

q

TYPE: int

Source code in siapy/utils/images.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
def blockfy_image(
    image: ImageType,
    p: Annotated[int, "block row size"],
    q: Annotated[int, "block column size"],
) -> list[np.ndarray]:
    image_np = validate_image_to_numpy(image)
    # Calculate how many blocks can cover the entire image
    bpr = (image_np.shape[0] - 1) // p + 1  # blocks per row
    bpc = (image_np.shape[1] - 1) // q + 1  # blocks per column

    # Pad array with NaNs so it can be divided by p row-wise and by q column-wise
    image_pad = np.nan * np.ones([p * bpr, q * bpc, image_np.shape[2]])
    image_pad[: image_np.shape[0], : image_np.shape[1], : image_np.shape[2]] = image_np

    image_slices = []
    row_prev = 0

    for row_block in range(bpc):
        row_prev = row_block * p
        column_prev = 0

        for column_block in range(bpr):
            column_prev = column_block * q
            block = image_pad[
                row_prev : row_prev + p,
                column_prev : column_prev + q,
            ]

            if block.shape == (p, q, image_np.shape[2]):
                image_slices.append(block)

    return image_slices

calculate_image_background_percentage

calculate_image_background_percentage(image: ImageType)
PARAMETER DESCRIPTION
image

TYPE: ImageType

Source code in siapy/utils/images.py
251
252
253
254
255
256
257
def calculate_image_background_percentage(image: ImageType):
    image_np = validate_image_to_numpy(image)
    # Check where any of bands include nan values (axis=2) to get positions of background
    mask_nan = np.any(np.isnan(image_np), axis=2)
    # Calculate percentage of background
    percentage = np.sum(mask_nan) / mask_nan.size * 100
    return percentage