Skip to content

Commit bf4e52b

Browse files
authored
Merge pull request #889 from danforthcenter/write-hyperspectral-data
Write hyperspectral data
2 parents 6319617 + a3c8f8f commit bf4e52b

File tree

6 files changed

+150
-2
lines changed

6 files changed

+150
-2
lines changed

docs/updating.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,11 @@ pages for more details on the input and output variable types.
390390
* pre v4.x: NA
391391
* post v4.x: rot_hsi = **plantcv.hyperspectral.rot90**(*spectral_data, k*)
392392

393+
#### plantcv.hyperspectral.write_data
394+
395+
* pre v4.0: NA
396+
* post v4.0: **plantcv.hyperspectral.write_data**(*filename, spectral_data*)
397+
393398
#### plantcv.image_add
394399

395400
* pre v3.0dev2: device, added_img = **plantcv.image_add**(*img1, img2, device, debug=None*)

docs/write_data.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## Write data
2+
3+
Write a hyperspectral image in ENVI format to the specified file.
4+
It creates a text header file with extension .hdr and a binary file with
5+
extension .raw. This function only supports Band-Interleaved-by-Line (BIL)
6+
interleave.
7+
8+
**plantcv.hyperspectral.write_data**(*filename, spectral_data*):
9+
10+
11+
- **Parameters:**
12+
- filename- desired name of the hyperspectral image file. The extensions are ignored and .hdr and .raw are used.
13+
- spectral_data- Hyperspectral data object
14+
15+
- **Context:**
16+
- Used to save a modified hyperspectral image
17+
18+
- **Example use:**
19+
20+
```python
21+
from plantcv import plantcv as pcv
22+
23+
modified_spectral = pcv.Spectral_data(array_data=modified_array_data,
24+
max_wavelength=list(source_spectral.wavelength_dict.keys())[-1],
25+
min_wavelength=list(source_spectral.wavelength_dict.keys())[0],
26+
max_value=float(np.amax(modified_array_data)),
27+
min_value=float(np.amin(modified_array_data)),
28+
d_type=modified_array_data.dtype,
29+
wavelength_dict=source_spectral.wavelength_dict,
30+
samples=modified_array_data.shape[1],
31+
lines=modified_array_data.shape[0], interleave='bil',
32+
wavelength_units=source_spectral.wavelength_units,
33+
array_type="datacube",
34+
pseudo_rgb=None,
35+
filename=source_spectral.filename,
36+
default_bands=None)
37+
38+
pcv.hyperspectral.write_data('test-hyperspectral', modified_spectral)
39+
```
40+
41+
**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/hyperspectral/write_data.py)

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ nav:
7070
- 'Calibrate': calibrate.md
7171
- 'Spectral Index': spectral_index.md
7272
- 'Extract wavelength': extract_wavelength.md
73-
- 'Spectral data objects': Spectral_data.md
7473
- 'Rotate Hyperspectral Datacubes ': hyperspectral_rot90.md
74+
- 'Spectral data objects': Spectral_data.md
75+
- 'Write data': write_data.md
7576
- 'Image Add': image_add.md
7677
- 'Image Subtract': image_subtract.md
7778
- 'Image Fusion': image_fusion.md

plantcv/plantcv/hyperspectral/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from plantcv.plantcv.hyperspectral._avg_reflectance import _avg_reflectance
99
from plantcv.plantcv.hyperspectral._inverse_covariance import _inverse_covariance
1010
from plantcv.plantcv.hyperspectral.rot90 import rot90
11+
from plantcv.plantcv.hyperspectral.write_data import write_data
1112

1213
# add new functions to end of lists
1314
__all__ = ["read_data", "_find_closest", "analyze_spectral", "analyze_index", "calibrate",
14-
"_make_pseudo_rgb", "extract_wavelength", "_avg_reflectance", "_inverse_covariance", "rot90"]
15+
"_make_pseudo_rgb", "extract_wavelength", "_avg_reflectance", "_inverse_covariance",
16+
"rot90", "write_data"]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
import numpy as np
3+
4+
from plantcv.plantcv import _version
5+
6+
__version__ = _version.get_versions()['version']
7+
8+
def write_data(filename, spectral_data):
9+
"""Write hyperspectral image data to a file.
10+
Inputs:
11+
filename = Name of file to write
12+
spectral_data = Hyperspectral data object
13+
14+
Returns:
15+
16+
:param filename: str
17+
:param spectral_data: __main__.Spectral_data
18+
"""
19+
20+
filename = os.path.splitext(filename)[0]
21+
22+
# create header
23+
lines, samples, bands = spectral_data.array_data.shape
24+
dtype_dict = {'B': "1", 'h': "2", 'i': "3", 'f': "4", 'd': "5", 'F': "6",
25+
'D': "9", 'H': "12", 'I': "13", 'l': "14", 'L': "15"}
26+
wavelenghths = list(spectral_data.wavelength_dict.keys())
27+
with open(filename+'.hdr', mode='w') as f:
28+
f.write('ENVI\n')
29+
f.write(f'; this file was created using PlantCV version {__version__}\n')
30+
f.write(f'; original file: {spectral_data.filename}\n')
31+
f.write('interleave = bil\n')
32+
f.write(f'samples = {samples}\n')
33+
f.write(f'lines = {lines}\n')
34+
f.write(f'bands = {bands}\n')
35+
f.write(f'data type = {dtype_dict[spectral_data.array_data.dtype.char]}\n')
36+
f.write(f'wavelength units = {spectral_data.wavelength_units}\n')
37+
f.write(f'default bands ={spectral_data.default_bands}\n')
38+
f.write('wavelength = {\n')
39+
for wl in wavelenghths[:-1]:
40+
f.write(f'{wl},\n')
41+
f.write(f'{wavelenghths[-1]}\n')
42+
f.write('}')
43+
44+
# create raw binary file containing the hyperspectral array values
45+
with open(filename+'.raw', mode='w+b') as f:
46+
f.write(spectral_data.array_data.transpose(0,2,1).tobytes(order='C'))
47+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
import pytest
3+
import numpy as np
4+
5+
from plantcv.plantcv import Spectral_data
6+
from plantcv.plantcv.hyperspectral import read_data
7+
from plantcv.plantcv.hyperspectral import write_data
8+
9+
def test_write_data_default(tmpdir):
10+
"""Test for PlantCV."""
11+
rng = np.random.default_rng()
12+
13+
# Create a test tmp directory
14+
cache_dir = tmpdir.mkdir("cache")
15+
16+
lines = 32
17+
samples = 32
18+
bands = 5
19+
20+
# Create random array data in the interval [0-65535] and wavelengths in the
21+
# interval [400-1000)
22+
rand_array = rng.integers(0, 65535, size=(lines, samples, bands), dtype=np.uint16, endpoint=True)
23+
rand_wavelengths = np.sort(600.0*rng.random(size=bands) + 400.0)
24+
# Create dictionary of wavelengths
25+
wavelength_dict = {}
26+
for j, wavelength in enumerate(rand_wavelengths):
27+
wavelength_dict.update({wavelength: float(j)})
28+
29+
# Create spectral data object
30+
rand_spectral_array = Spectral_data(array_data=rand_array,
31+
max_wavelength=rand_wavelengths[-1],
32+
min_wavelength=rand_wavelengths[0],
33+
max_value=float(np.amax(rand_array)),
34+
min_value=float(np.amin(rand_array)),
35+
d_type=rand_array.dtype,
36+
wavelength_dict=wavelength_dict,
37+
samples=samples,
38+
lines=lines,
39+
interleave='bil',
40+
wavelength_units='nm',
41+
array_type="datacube",
42+
pseudo_rgb=None,
43+
filename='random_hyperspectral_test',
44+
default_bands=None)
45+
46+
47+
filename = os.path.join(cache_dir, 'plantcv_hyperspectral_write_data.raw')
48+
write_data(filename=filename, spectral_data=rand_spectral_array)
49+
50+
# Read written hyperspectral image
51+
array_data = read_data(filename=filename)
52+
assert np.shape(array_data.array_data) == (lines, samples, bands)

0 commit comments

Comments
 (0)