Envi Writer¶
Module: spectralbridge.envi_writer.
ENVI header and float32 BSQ writing helpers adapted from HyTools.
The current implementation is modified and narrowed to SpectralBridge's output contracts; its implementation lineage remains documented.
HyTools: Hyperspectral image processing library Copyright (C) 2021 University of Wisconsin Authors named in the relevant source notices: Adam Chlus, Zhiwei Ye, Philip Townsend. License: GNU General Public License, version 3 Upstream: https://github.com/EnSpec/hytools Audited revision: 31286d64541791a9815d29443a33726fa4d54031
Modified for SpectralBridge. See LICENSE, NOTICE, and
HYTOOLS_PROVENANCE.md.
API glossary ยท Workflow / scientific context
Signatures and documentation below are collected from the current source. Advanced objects are not newly declared stable by inclusion here.
Audience: advanced.
build_envi_header_text ¶
build_envi_header_text(header_dict: Dict) -> str
Build the ENVI .hdr file text from a header_dict.
Attribution: Adapted from hytools.io.envi.write_envi_header() and hytools.io.envi.envi_header_from_neon() (GPLv3). HyTools Authors: Adam Chlus, Zhiwei Ye, Philip Townsend. This simplified version is NEON-only and assumes BSQ float32 output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
header_dict
|
dict
|
A dictionary with at least the following keys: 'samples' int 'lines' int 'bands' int 'interleave' str (must be 'bsq' or 'BSQ') 'data type' int (ENVI code 4 -> float32) 'byte order' int (0 for little endian) 'wavelength' list[float] 'fwhm' list[float] 'wavelength units' str 'map info' list or str 'projection' str You may also include optional keys like: 'description' 'transform' 'ulx' 'uly' |
required |
Returns:
| Type | Description |
|---|---|
str
|
A string containing the full .hdr contents to be written to disk. |
Behavior
- You MUST write a header that ENVI (and typical GDAL ENVI readers)
understand.
- The header should begin with "ENVI" on the first line.
- Then list key/value lines in ENVI style, for example:
samples = 1000
lines = 500
bands = 426
data type = 4
interleave = bsq
byte order = 0
map info = {UTM, 1.0, 1.0, 500000.0, 4420000.0, 1.0, -1.0, 13, North, WGS-84}
projection = <...WKT or similar...>
wavelength = { 400.0, 401.0, ... }
fwhm = { ... }
wavelength units = Nanometers
- You MUST serialize lists (like wavelength, fwhm) into ENVI-style
curly-brace lists separated by commas.
- You MUST serialize "map info" consistently with how hytools writes it.
"map info" in ENVI is written as:
map info = {UTM, 1.0, 1.0, <ulx>, <uly>, <xres>, <yres>, <utm zone>, North, WGS-84}
If header_dict['map info'] is already such a list, just join it.
- Include 'description' in the header if header_dict has it. Wrap it
ENVI-style like:
description = { BRDF+topographic corrected reflectance ... }
Implementation details: - Make sure numeric types are converted to Python floats/ints before string join, so numpy scalars don't show up weird.
Audience: advanced.
EnviWriter ¶
EnviWriter(out_stem: Path, header_dict: Dict)
EnviWriter assembles a corrected hyperspectral cube into a BSQ ENVI file.
Portions adapted from hytools.io.envi (GPLv3). HyTools Authors: Adam Chlus, Zhiwei Ye, Philip Townsend. This version is NEON-only (no GLT, no NetCDF).
Usage pattern:
header = cube.build_envi_header()
# optionally edit header['description'] before writing
writer = EnviWriter(out_stem=Path("/path/to/output_stem"),
header_dict=header)
for (ys, ye, xs, xe, chunk) in cube.iter_chunks(...):
corrected = ... # run apply_topo_correct(), apply_brdf_correct(), etc.
corrected = corrected.astype("float32", copy=False)
writer.write_chunk(corrected, ys, xs)
writer.close()
After close(), you'll have: /path/to/output_stem.img /path/to/output_stem.hdr
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
out_stem
|
Path
|
Output path WITHOUT extension.
We will create:
|
required |
header_dict
|
dict
|
The header dict produced by NeonCube.build_envi_header(), possibly updated to include a human-readable description like "BRDF+topographic corrected reflectance". |
required |
Behavior
-
Extract: samples = header_dict["samples"] lines = header_dict["lines"] bands = header_dict["bands"]
-
Allocate a memory-mapped array shaped: (bands, lines, samples) dtype float32, order='C'.
This is a BSQ layout: band-major on disk.
- Store that memmap in self._mm so we can fill it incrementally.
- Store header_dict and out_stem so we can write the .hdr in close().
Implementation detail: Use numpy.memmap with mode='w+'. The filename MUST be f"{out_stem}.img".
write_chunk ¶
write_chunk(corrected_chunk: ndarray, ys: int, xs: int) -> None
Write a corrected reflectance chunk into the BSQ memmap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corrected_chunk
|
ndarray
|
Shape (y_size, x_size, bands), float32. This chunk is aligned with cube.data[ys:ys+y_size, xs:xs+x_size, :]. |
required |
ys
|
int
|
Row offset (top of chunk in full image coordinates). |
required |
xs
|
int
|
Column offset (left of chunk in full image coordinates). |
required |
Behavior
For each band index b: self._mm[b, ys:ys+y_size, xs:xs+x_size] = corrected_chunk[:, :, b]
You MUST do band-major assignment to match BSQ layout.
This method should NOT flush to disk; flushing happens in close().
Assumptions: - corrected_chunk is already float32 - corrected_chunk has no-data values already handled
close ¶
close() -> None
Finalize the ENVI output:
Behavior
- Flush/close the memmap.
- Write the .hdr file next to the .img file using the build_envi_header_text() function.
Implementation detail: - .img path is f"{self.out_stem}.img" - .hdr path is f"{self.out_stem}.hdr" - build the header string via build_envi_header_text(self.header_dict) and write it as ASCII/UTF-8 text.