Notebook showing all the spectral normalization methods implemented in pyFRESCO

[1]:
import pyfresco

Data import

In the following code cell we define the paths pointing to the img and header files of the proper hyperspectral datacube and of the summary parameters datacube. These two datacubes are the ones needed by pyFRESCO to work properly.

[2]:
path_if_mtrdr = "FRT00009b5a/frt00009b5a_07_if165j_mtr3.img" # Path to the proper hyperspectral datacube
head_if_mtrdr = "FRT00009b5a/frt00009b5a_07_if165j_mtr3.hdr" # Path to the header of the proper hyperspectral datacube

path_sr_mtrdr = "FRT00009b5a/frt00009b5a_07_sr165j_mtr3.img" # Path to the summary parameters datacube
head_sr_mtrdr = "FRT00009b5a/frt00009b5a_07_sr165j_mtr3.hdr" # Path to the header of the summary parameters datacube

img , img_sr , wavelength , sr_names = pyfresco.open_raw(path_if_mtrdr , head_if_mtrdr , path_sr_mtrdr , head_sr_mtrdr) # Open the two datacubes

Nbands = len(wavelength)
print(sr_names)
['R770', 'RBR', 'BD530_2', 'SH600_2', 'SH770', 'BD640_2', 'BD860_2', 'BD920_2', 'RPEAK1', 'BDI1000VIS', 'R440', 'IRR1', 'BDI1000IR', 'OLINDEX3', 'R1330', 'BD1300', 'LCPINDEX2', 'HCPINDEX2', 'VAR', 'ISLOPE1', 'BD1400', 'BD1435', 'BD1500_2', 'ICER1_2', 'BD1750_2', 'BD1900_2', 'BD1900R2', 'BDI2000', 'BD2100_2', 'BD2165', 'BD2190', 'MIN2200', 'BD2210_2', 'D2200', 'BD2230', 'BD2250', 'MIN2250', 'BD2265', 'BD2290', 'D2300', 'BD2355', 'SINDEX2', 'ICER2_2', 'MIN2295_2480', 'MIN2345_2537', 'BD2500_2', 'BD3000', 'BD3100', 'BD3200', 'BD3400_2', 'CINDEX2', 'BD2600', 'IRR2', 'IRR3', 'R530', 'R600', 'R1080', 'R1506', 'R2529', 'R3920']

Target spectrum extraction

Before applying any normalization method we need a target ROI spectrum. Here we repeat the polygon extraction used in the first and second tutorial notebooks.

[3]:
SE = pyfresco.SpectraExtract(img , Nbands , wavelength , 750 , 2600) # Definition of the object for spectral extraction

RGB = SE.upload_map('PHY' , folder = 'FRT00009b5a') # Upload an RGB map for the ROI selection
[4]:
polygon_spectra , L , mask = SE.polygon_spectra(save_pixel = True ,
 folder = 'FRT00009b5a' ,
 name = 'target_polygon_coordinates') # Select the target ROI

polygon_spectrum , polygon_error = SE.final_spectra(mean = True , c = 'blue') # Mean and standard deviation target spectrum
Number of points inside the drewn polygon:  297

The output of the above cell should look like this: Point selection Point selection2

Definition of the normalization object

The same SpectraNorm object can be reused to test different neutral-spectrum choices. Each method overwrites the currently stored neutral spectrum, so the normalization is computed immediately after each neutral selection.

[5]:
SN = pyfresco.SpectraNorm(RGB , Nbands , img , img_sr , wavelength , # Definition of the object for spectral normalization
 polygon_spectrum , polygon_error , polygon_spectra ,
 750 , 2600)

Neutral Polygon Method

The first normalization method uses a manually selected neutral polygon. The median spectrum of this neutral ROI is used as denominator for the target spectrum.

[6]:
neutral_polygon_spectra , neutral_polygon , neutral_polygon_error , L_neutral , mask_neutral = SN.neutral_polygon_spectra(
 save_pixel = True ,
 folder = 'FRT00009b5a' ,
 name = 'neutral_polygon_coordinates') # Draw a neutral ROI

SN.plot_together() # Plot target and neutral spectra

norm_polygon , error_polygon = SN.norm_spectra() # Classical ratio and propagated error
SN.normplot()

norm_polygon_boot , error_polygon_boot = SN.bootstrapnorm(convexhull = False , N = 5000) # Bootstrap normalization
SN.normplot()
Number of points inside the drewn polygon:  2150
9.386281588447654 % of the errors, set to zero for simplicity, are in reality NaN values.

The output of the above cell should look like this: Neutral polygon selection Spectra together Spectra classical norm Spectra bootstrap norm

Convex Hull Method

The convex hull method uses the continuum of the target spectrum as neutral spectrum. This is useful when an external neutral ROI is not available.

[7]:
neutral_hull , hull_spectrum , hull_error = SN.neutral_convex_hull(interp = 'linear') # Compute the convex hull neutral spectrum

SN.plot_together(convex_hull = True)

norm_hull , error_hull = SN.norm_spectra(convex_hull = True) # Normalize using the convex hull
SN.normplot(convex_hull = True)

norm_hull_boot , error_hull_boot = SN.bootstrapnorm(convexhull = True , N = 5000) # Bootstrap version
SN.normplot(convex_hull = True)
100.0 % of the errors, set to zero for simplicity, are in reality NaN values.

The output of the above cell should look like this: convex hull classic norm bootstrap norm

Single All-Map Method

The single all-map method searches for pixels with low values in one RGB map. Those pixels are used to build the neutral spectrum.

[9]:
neutral_single , neutral_single_median , neutral_single_mad , x_single , y_single = SN.neutral_all_map_single(threshold = 0) # Search low-valued pixels in the current RGB map

SN.plot_together()

norm_single , error_single = SN.norm_spectra()
SN.normplot()

norm_single_boot , error_single_boot = SN.bootstrapnorm(convexhull = False , N = 5000) # Bootstrap version
SN.normplot(convex_hull = True)
0.0 % of the errors, set to zero for simplicity, are in reality NaN values.

The output of the above cell should look like this: zero px together sam classic norm2 bootstrap norm2

Multiple All-Map Method

The multiple all-map method repeats the same low-value pixel search on several RGB maps. Only pixels satisfying the requested overlap condition are retained.

[10]:
RGBs_names = ['HYD' , 'PHY' , 'MAF'] # RGB maps previously saved with RGBImageManipulator.savemap()
RGB_path = 'FRT00009b5a/'

neutral_multiple , neutral_multiple_median , neutral_multiple_mad , superimposed , zero_map , xx , yy = SN.neutral_all_map_multiple(
 RGBs_names = RGBs_names ,
 RGB_path = RGB_path ,
 p = len(RGBs_names) ,
 threshold = 0 ,
 names = RGBs_names)

SN.plot_together()

norm_multiple , error_multiple = SN.norm_spectra()
SN.normplot()

norm_multiple_boot , error_multiple_boot = SN.bootstrapnorm(convexhull = False , N = 5000) # Bootstrap version
SN.normplot(convex_hull = True)
0.0 % of the errors, set to zero for simplicity, are in reality NaN values.

The output of the above cell should look like this: zero px multi zero px multi total together mam classic norm3 bootstrap norm3

Mineral Mask Method

The mineral mask method uses spectral-parameter thresholds to select the neutral pixels. Reflectance parameters are treated differently from band-depth parameters.

[13]:
band_names = ['R770' , 'BDI1000IR' , 'OLINDEX3' , 'BD1300' , 'LCPINDEX2' , 'HCPINDEX2',
             'D2200' , 'BD2290' , 'D2300' , 'BD2500_2' , 'SINDEX2' , 'BD1900_2']
band_values = [0.23,0.035,0.09,0,0,0,0.001,0.001,0.001,0,0.0045,0.012]

neutral_mask , neutral_mask_median , neutral_mask_mad = SN.mineral_mask(
 b = 500 ,
 band_n = band_names ,
 band_v = band_values)

SN.plot_together()

norm_mask , error_mask = SN.norm_spectra()
SN.normplot()

norm_mineralmask_boot , error_mineralmask_boot = SN.bootstrapnorm(convexhull = False , N = 5000) # Bootstrap version
SN.normplot(convex_hull = True)
constraints_mask.size=581064
constraints_mask.sum()=np.int64(13961)
0.0 % of the errors, set to zero for simplicity, are in reality NaN values.

The output of the above cell should look like this: mmdist zero px multi total mm together mmm classic norm4 bootstrap norm4

Smoothing of the normalized spectrum

After any normalization, the normalized spectrum can be smoothed with either a moving average or a Savitzky-Golay filter.

[14]:
smooth_movmean = SN.moving_average(window_size = 5) # Moving average smoothing

smooth_savgol = SN.savgol(window = 7 , order = 2) # Savitzky-Golay smoothing

The output of the above cell should look like this: movingmean savgol

Saving normalized spectra

The normalized spectrum and associated neutral spectrum can be saved for later analysis notebooks.

[ ]:
SN.save_spectrum(name = 'target_polygon' ,
 folder = 'FRT00009b5a/' ,
 method = 'min' ,
 normalized = True)
[ ]: