Initial commit

This commit is contained in:
2026-04-10 16:46:45 +08:00
commit 4fd1b0a203
165 changed files with 25698 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Evan Greenberg.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
The :mod:`hytools.correction` module include functions image correction.
"""
from .glint import *
from .gao_2021 import *
from .hedley_2005 import *
from .hochberg_2003 import *

View File

@ -0,0 +1,216 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Evan Greenberg.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from ..masks import mask_create
REFRACTIVE_INDICES = np.array([
[200, 1.396],
[225, 1.373],
[250, 1.362],
[275, 1.354],
[300, 1.349],
[325, 1.346],
[350, 1.343],
[375, 1.341],
[400, 1.339],
[425, 1.338],
[450, 1.337],
[475, 1.336],
[500, 1.335],
[525, 1.334],
[550, 1.333],
[575, 1.333],
[600, 1.332],
[625, 1.332],
[650, 1.331],
[675, 1.331],
[700, 1.331],
[725, 1.33],
[750, 1.33],
[775, 1.33],
[800, 1.329],
[825, 1.329],
[850, 1.329],
[875, 1.328],
[900, 1.328],
[925, 1.328],
[950, 1.327],
[975, 1.327],
[1000, 1.327],
[1200, 1.324],
[1400, 1.321],
[1600, 1.317],
[1800, 1.312],
[2000, 1.306],
[2200, 1.296],
[2400, 1.279],
[2600, 1.242],
[2650, 1.219],
[2700, 1.188],
[2750, 1.157],
[2800, 1.142],
[2850, 1.149],
[2900, 1.201],
[2950, 1.292],
[3000, 1.371]
])
def apply_gao_2021_correction(hy_obj, data, dimension, index):
"""
Glint correction algorithm following:
Gao BC, Li RR.
Correction of Sunglint Effects in High Spatial Resolution
Hyperspectral Imagery Using SWIR or NIR Bands and Taking Account of
Spectral Variation of Refractive Index of Water.
Adv Environ Eng Res 2021;2(3):16; doi:10.21926/aeer.2103017.
"""
if 'apply_glint' not in hy_obj.mask:
hy_obj.gen_mask(mask_create,'apply_glint',hy_obj.glint['apply_mask'])
if hy_obj.mask['apply_glint'].sum() == 0:
return data
hy_obj.glint['correction_band'] = hy_obj.wave_to_band(hy_obj.glint['correction_wave'])
if 'gao_b_simu' not in hy_obj.ancillary:
hy_obj.ancillary['gao_b_simu'] = get_b_simu(hy_obj)
if 'gao_rto' not in hy_obj.ancillary:
hy_obj.ancillary['gao_rto'] = get_rto(hy_obj)
if dimension == 'line':
rto_line = hy_obj.ancillary['gao_rto'][index, :]
rto_line = np.reshape(rto_line, (len(rto_line), 1))
correction = rto_line * hy_obj.ancillary['gao_b_simu']
elif dimension == 'column':
rto_col = hy_obj.ancillary['gao_rto'][:, index]
rto_col = np.reshape(rto_col, (len(rto_col), 1))
correction = rto_col * hy_obj.ancillary['gao_b_simu']
elif (dimension == 'band'):
correction = (
hy_obj.ancillary['gao_b_simu'][0, :][index]
* hy_obj.ancillary['gao_rto']
)
elif dimension == 'chunk':
x1, x2, y1, y2 = index
rto_chunk = hy_obj.ancillary['gao_rto'][y1:y2, x1:x2]
rto_chunk = np.reshape(
rto_chunk,
(
rto_chunk.shape[0],
rto_chunk.shape[1],
1
)
)
correction = rto_chunk * hy_obj.ancillary['gao_b_simu']
elif dimension == 'pixels':
y, x = index
rto_pixels = hy_obj.ancillary['gao_rto'][y, x]
rto_pixels = np.reshape(rto_pixels, (len(rto_pixels), 1))
correction = rto_pixels * hy_obj.ancillary['gao_b_simu']
return data - correction
def zenith_refracted(theta, n):
"""
Find zenith of the outgoing reflected light
n is the refractive index of water at a specific wavelength
"""
theta_p = np.degrees(
np.arcsin(np.sin(np.radians(theta)) / n)
)
return theta_p
def fresnel_reflectence(theta, theta_p):
"""
Uses the fresnel equation to find the
percentege of incident light reflected
"""
theta_rad = np.radians(theta)
theta_p_rad = np.radians(theta_p)
return (
(
(np.sin(theta_rad - theta_p_rad)**2)
/ (np.sin(theta_rad + theta_p_rad)**2)
) + (
(np.tan(theta_rad - theta_p_rad)**2)
/ (np.tan(theta_rad + theta_p_rad)**2)
)
) / 2
def fresnel_spectra(theta, xs, ns):
"""
Solves for the spectrum of reflected light
according to fresnels equations
"""
spectra = []
for x in xs:
n = np.interp(x, ns[:, 0], ns[:, 1])
theta_p = zenith_refracted(theta, n)
spectra.append(fresnel_reflectence(theta, theta_p))
return np.array(spectra)
def get_b_simu(hy_obj):
b_simu = fresnel_spectra(
10**-5,
hy_obj.wavelengths,
REFRACTIVE_INDICES
)
return np.reshape(
b_simu, (1, len(b_simu))
)
def get_rto(hy_obj):
b_ref = hy_obj.get_wave(hy_obj.glint['correction_wave'])
b_ref_min = np.percentile(
b_ref[
(hy_obj.mask['apply_glint'])
& (b_ref > 0)
],
.0001
)
b_ref = b_ref - b_ref_min
rto = (
b_ref
/ hy_obj.ancillary['gao_b_simu'][0, :][hy_obj.glint['correction_band']]
)
rto[~hy_obj.mask['apply_glint']] = 0
return rto

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Evan Greenberg.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import ray
from ..misc import set_glint
from .hochberg_2003 import apply_hochberg_2003_correction
from .gao_2021 import apply_gao_2021_correction
from .hedley_2005 import apply_hedley_2005_correction
def set_glint_parameters(actors, config_dict):
# Assign glint dict
glint_dict = config_dict['glint']
# Set Glint dict
_ = ray.get([
a.do.remote(set_glint, glint_dict) for a in actors
])
# Add glint correction
_ = ray.get([
a.do.remote(lambda x: x.corrections.append('glint')) for a in actors
])
def set_glint_parameters_single(hy_obj, config_dict):
# Assign glint dict
glint_dict = config_dict['glint']
# Set Glint dict
set_glint(hy_obj, glint_dict)
# Add glint correction
hy_obj.corrections.append('glint')
def apply_glint_correct(hy_obj, data, dimension, index):
''' Corrects glint based on the specified algorithm in the config.
Options include:
Hochberg et al., 2003: hochberg
Gao et al., 2021: gao
Hedley et al. 2005: hedley
...
'''
# Perform one of the corrections
if hy_obj.glint['type'] == 'hochberg':
data = apply_hochberg_2003_correction(hy_obj, data, dimension, index)
elif hy_obj.glint['type'] == 'gao':
data = apply_gao_2021_correction(hy_obj, data, dimension, index)
elif hy_obj.glint['type'] == 'hedley':
data = apply_hedley_2005_correction(hy_obj, data, dimension, index)
#Truncate reflectance values below 0
if hy_obj.glint['truncate']:
data[(data < 0) & (data != hy_obj.no_data)]= 0
return data

View File

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Evan Greenberg.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from scipy import stats
from ..masks import mask_create
def apply_hedley_2005_correction(hy_obj, data, dimension, index):
"""
Glint correction algorithm following:
Hedley, J. D., Harborne, A. R., & Mumby, P. J. (2005).
Simple and robust removal of sun glint for mapping shallowwater benthos.
International Journal of Remote Sensing, 26(10), 2107-2112.
"""
# Raise exception is there is no deep water sample provided
if isinstance(hy_obj.glint.get('deep_water_sample'), type(None)):
raise KeyError("No Deep Water Sample Provided")
if 'apply_glint' not in hy_obj.mask:
hy_obj.gen_mask(mask_create,'apply_glint',hy_obj.glint['apply_mask'])
if hy_obj.mask['apply_glint'].sum() == 0:
return data
hy_obj.glint['correction_band'] = hy_obj.wave_to_band(
hy_obj.glint['correction_wave']
)
if 'hedley_slopes' not in hy_obj.ancillary:
hy_obj.ancillary['hedley_slopes'] = optimize_slopes(hy_obj)
if 'hedley_nir_swir_diff' not in hy_obj.ancillary:
hy_obj.ancillary['hedley_nir_swir_diff'] = nir_swir_diff(hy_obj)
if dimension == 'line':
correction = (
hy_obj.ancillary['hedley_nir_swir_diff'][index, :].reshape(-1, 1)
* hy_obj.ancillary['hedley_slopes']
)
correction[~hy_obj.mask['apply_glint'][index, :], :] = 0
elif dimension == 'column':
correction = (
hy_obj.ancillary['hedley_nir_swir_diff'][:, index].reshape(-1, 1)
* hy_obj.ancillary['hedley_slopes']
)
correction[~hy_obj.mask['apply_glint'][:, index], :] = 0
elif (dimension == 'band'):
correction = (
hy_obj.ancillary['hedley_nir_swir_diff']
* hy_obj.ancillary['hedley_slopes'][0, index]
)
correction[~hy_obj.mask['apply_glint']] = 0
elif dimension == 'chunk':
x1, x2, y1, y2 = index
corr_diff = hy_obj.ancillary['hedley_nir_swir_diff'][y1:y2, x1:x2]
bandnums = data.shape[2]
corr_diff = np.repeat(
corr_diff[:, :, np.newaxis],
bandnums,
axis=2
)
correction = corr_diff * hy_obj.ancillary['hedley_slopes']
correction[~hy_obj.mask['apply_glint'][y1:y2, x1:x2], :] = 0
elif dimension == 'pixels':
y, x = index
correction = (
hy_obj.ancillary['hedley_nir_swir_diff'][y, x].reshape(-1, 1)
* hy_obj.ancillary['hedley_slopes']
)
correction[~hy_obj.mask['apply_glint'][y, x], :] = 0
return data - correction
def optimize_slopes(hy_obj):
deep_water = hy_obj.get_chunk(
*hy_obj.glint['deep_water_sample'][hy_obj.file_name]
)
deep_correction = (
deep_water[:, :, hy_obj.glint['correction_band']].flatten()
)
# Iterate through each band to find the band-slope
slopes = np.empty([1, len(hy_obj.wavelengths)])
for i, band in enumerate(hy_obj.wavelengths):
# Get flattened deep water sample of band
wave_num = np.argmin(
np.abs(hy_obj.wavelengths - band)
)
wave = deep_water[:, :, wave_num].flatten()
# Regress
(
slope,
intercept,
r_value,
p_value,
std_err
) = stats.linregress(
deep_correction,
wave
)
slopes[0, i] = slope
return slopes
def nir_swir_diff(hy_obj):
nir_swir_array = np.copy(
hy_obj.get_wave(hy_obj.glint['correction_wave'])
)
nir_swir_array[~hy_obj.mask['apply_glint']] = 0
nir_swir_min = np.percentile(nir_swir_array[nir_swir_array > 0], .0001)
return nir_swir_array - nir_swir_min

View File

@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Evan Greenberg.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from ..masks import mask_create
def apply_hochberg_2003_correction(hy_obj, data, dimension, index):
"""
Glint correction algorithm following:
Hochberg, EJ, Andréfouët, S and Tyler, MR. 2003.
Sea surface correction of high spatial resolution Ikonos images to
improve bottom mapping in nearshore environments..
IEEE Transactions on Geoscience and Remote Sensing, 41: 17241729.
"""
if 'apply_glint' not in hy_obj.mask:
hy_obj.gen_mask(mask_create,'apply_glint',hy_obj.glint['apply_mask'])
if hy_obj.mask['apply_glint'].sum() == 0:
return data
if 'hochberg_correction' not in hy_obj.ancillary:
hy_obj.ancillary['hochberg_correction'] = (
get_hochberg_correction(hy_obj)
)
if dimension == 'line':
correction = hy_obj.ancillary['hochberg_correction'][index, :][:,np.newaxis]
elif dimension == 'column':
correction = hy_obj.ancillary['hochberg_correction'][:, index][np.newaxis,:]
elif dimension == 'band':
correction = hy_obj.ancillary['hochberg_correction']
elif dimension == 'chunk':
x1, x2, y1, y2 = index
correction = hy_obj.ancillary['hochberg_correction'][y1:y2, x1:x2]
elif dimension == 'pixels':
y, x = index
correction = hy_obj.ancillary['hochberg_correction'][y, x]
return data - correction
def get_hochberg_correction(hy_obj):
"""
Calculates the hochberg correction across entire image.
Uses the NIR or SWIR wavelengths to find the amount of signal
attributed to glint. Zeros out non-water pixels
"""
if isinstance(hy_obj.glint['correction_wave'],list):
nir_swir_array = np.zeros((hy_obj.lines,hy_obj.columns))
for wave in hy_obj.glint['correction_wave']:
nir_swir_array+= hy_obj.get_wave(wave)
nir_swir_array/=len(hy_obj.glint['correction_wave'])
else:
nir_swir_array = np.copy(hy_obj.get_wave(hy_obj.glint['correction_wave']))
nir_swir_array[~hy_obj.mask['apply_glint']] = 0
nir_swir_min = np.percentile(
nir_swir_array[nir_swir_array > 0], .001
)
hochberg_correction = nir_swir_array - nir_swir_min
hochberg_correction[~hy_obj.mask['apply_glint']] = 0
return hochberg_correction