Skip to content

Commit 3a586f0

Browse files
committed
Fix bug in envi.SpectralLibrary (closes #102)
1 parent 4352dcf commit 3a586f0

File tree

2 files changed

+82
-31
lines changed

2 files changed

+82
-31
lines changed

spectral/io/envi.py

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -931,55 +931,77 @@ class SpectralLibrary:
931931
932932
'''
933933

934-
def __init__(self, data, header, params):
934+
def __init__(self, data, header=None, params=None):
935+
'''Creates a new spectral library array
936+
937+
Arguments:
938+
939+
`data` (array-like):
940+
941+
Array with shape `CxB`, where `C` is the number of spectra in
942+
the library and `B` is the number of bands for each spectrum.
943+
944+
`header` (dict):
945+
946+
Optional dict of ENVI header parameters.
947+
948+
`params` (Params):
949+
950+
Optional SpyFile Params object
951+
'''
935952
from spectral.spectral import BandInfo
953+
936954
self.spectra = data
955+
(n_spectra, n_bands) = data.shape
956+
957+
if header is None:
958+
header = {}
959+
header = header.copy()
960+
937961
self.bands = BandInfo()
938-
if 'wavelength' in header:
939-
try:
940-
self.bands.centers = [float(b) for b in header['wavelength']]
941-
except:
942-
pass
943-
if 'fwhm' in header:
944-
try:
945-
self.bands.bandwidths = [float(f) for f in header['fwhm']]
946-
except:
947-
pass
948-
if 'spectra names' in header:
949-
self.names = header['spectra names']
962+
centers = header.pop('wavelength', None)
963+
if centers is not None:
964+
if len(centers) != n_bands:
965+
raise ValueError('Number of band centers does not match data')
966+
self.bands.centers = [float(c) for c in centers]
967+
fwhm = header.pop('fwhm', None)
968+
if fwhm is not None:
969+
if len(fwhm) != n_bands:
970+
raise ValueError('Number of fwhm values does not match data')
971+
self.bands.bandwidths = [float(f) for f in fwhm]
972+
names = header.pop('spectra names', None)
973+
if names is not None:
974+
if len(names) != n_spectra:
975+
raise ValueError('Number of spectrum names does not match data')
976+
self.names = names
950977
else:
951-
self.names = [''] * self.bands.shape[0]
952-
self.bands.band_unit = header.get('wavelength units', "")
978+
self.names = [str(i + 1) for i in range(n_spectra)]
979+
self.bands.band_unit = header.get('wavelength units', "<unspecified>")
953980
self.bands.band_quantity = "Wavelength"
954981
self.params = params
955-
self.metadata = {}
956-
self.metadata.update(header)
982+
self.metadata = header.copy()
957983
self.metadata['data ignore value'] = 'NaN'
958984

959-
def save(self, fileBaseName, description=None):
985+
def save(self, file_basename, description=None):
960986
'''
961987
Saves the spectral library to a library file.
962988
963989
Arguments:
964990
965-
`fileBaseName` (str):
991+
`file_basename` (str):
966992
967993
Name of the file (without extension) to save.
968994
969995
`description` (str):
970996
971997
Optional text description of the library.
972998
973-
This method creates two files: `fileBaseName`.hdr and
974-
`fileBaseName`.sli.
999+
This method creates two files: `file_basename`.hdr and
1000+
`file_basename`.sli.
9751001
'''
9761002
import spectral
977-
meta = {}
978-
meta.update(self.metadata)
979-
if self.bands.centers:
980-
meta['samples'] = len(self.bands.centers)
981-
else:
982-
meta['samples'] = len(self.spectra.shape[0])
1003+
meta = self.metadata.copy()
1004+
meta['samples'] = self.spectra.shape[1]
9831005
meta['lines'] = self.spectra.shape[0]
9841006
meta['bands'] = 1
9851007
meta['header offset'] = 0
@@ -988,12 +1010,14 @@ def save(self, fileBaseName, description=None):
9881010
meta['byte order'] = spectral.byte_order
9891011
meta['wavelength units'] = self.bands.band_unit
9901012
meta['spectra names'] = [str(n) for n in self.names]
991-
meta['wavelength'] = self.bands.centers
992-
meta['fwhm'] = self.bands.bandwidths
1013+
if self.bands.centers is not None:
1014+
meta['wavelength'] = self.bands.centers
1015+
if self.bands.bandwidths is not None:
1016+
meta['fwhm'] = self.bands.bandwidths
9931017
if (description):
9941018
meta['description'] = description
995-
write_envi_header(fileBaseName + '.hdr', meta, True)
996-
fout = builtins.open(fileBaseName + '.sli', 'wb')
1019+
write_envi_header(file_basename + '.hdr', meta, True)
1020+
fout = builtins.open(file_basename + '.sli', 'wb')
9971021
self.spectra.astype('f').tofile(fout)
9981022
fout.close()
9991023

spectral/tests/envi.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,33 @@ def test_open_missing_data_raises_envidatafilenotfounderror(self):
333333
else:
334334
raise Exception('Expected EnviDataFileNotFoundError')
335335

336+
def test_create_spectral_lib_with_header(self):
337+
'''Can create ENVI spectral library from numpy array with bands.'''
338+
import os
339+
import spectral as spy
340+
from spectral.io.envi import SpectralLibrary
341+
img = spy.open_image('92AV3C.lan')
342+
(nrows, ncols, nbands) = img.shape
343+
header = {'wavelength': np.arange(nbands).astype(np.float32)}
344+
slib = SpectralLibrary(img[0, :20, :].squeeze(), header)
345+
basename = os.path.join(testdir, 'slib')
346+
slib.save(basename)
347+
slib = spy.envi.open(basename + '.hdr')
348+
assert(slib.spectra.shape == (20, nbands))
349+
350+
def test_create_spectral_lib_without_header(self):
351+
'''Can create ENVI spectral library from numpy array without bands.'''
352+
import os
353+
import spectral as spy
354+
from spectral.io.envi import SpectralLibrary
355+
img = spy.open_image('92AV3C.lan')
356+
(nrows, ncols, nbands) = img.shape
357+
slib = SpectralLibrary(img[0, :20, :].squeeze())
358+
basename = os.path.join(testdir, 'slib')
359+
slib.save(basename)
360+
slib = spy.envi.open(basename + '.hdr')
361+
assert(slib.spectra.shape == (20, nbands))
362+
336363
def run():
337364
print('\n' + '-' * 72)
338365
print('Running ENVI tests.')

0 commit comments

Comments
 (0)