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,27 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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 .topo import *
from .scsc import *
from .c import *
from .cosine import *
from .scs import *
from .modminn import *

222
Flexbrdf/hytools/topo/c.py Normal file
View File

@ -0,0 +1,222 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
This module contains functions to apply a topographic correction (SCS+C)
described in the following papers:
Scott A. Soenen, Derek R. Peddle, & Craig A. Coburn (2005).
SCS+C: A Modified Sun-Canopy-Sensor Topographic Correction in Forested Terrain.
IEEE Transactions on Geoscience and Remote Sensing, 43(9), 2148-2159.
https://doi.org/10.1109/TGRS.2005.852480
Topographic correction consists of the following steps:
1. calculate incidence angle if it is not provided
2. estimate C-Correction value
3. apply C-Correction value to the image data
TODO: Rationale/ examples for using different fitting algorithms
"""
import numpy as np
from scipy.optimize import nnls
from ..io.envi import WriteENVI
def calc_c(data,cosine_i,fit_type = 'ols'):
"""Calculate the topographic correction coefficient (c) for the input data.
Used for both the cosine and SCS+S topographic corrections.
Args:
band (numpy.ndarray): Image array.
cosine_i (numpy.ndarray): Cosine i array.
fit_type (str): Linear model fitting type.
Returns:
numpy.ndarray: Topographic correction coefficient.
"""
# Reshape for regression
cosine_i = np.expand_dims(cosine_i,axis=1)
if cosine_i.shape[0]==0:
return 100000.0
X = np.concatenate([cosine_i,np.ones(cosine_i.shape)],axis=1)
# Eq 7. Soenen et al. 2005
if fit_type == 'ols':
slope, intercept = np.linalg.lstsq(X, data,rcond=-1)[0].flatten()
elif fit_type == 'nnls':
slope, intercept = nnls(X, data)[0].flatten()
# Eq 8. Soenen et al. 2005
c= intercept/slope
# Set a large number if slope is zero
if not np.isfinite(c):
c = 100000.0
return c
def calc_c_coeffs(hy_obj,topo_dict):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
Returns:
None.
'''
topo_dict['coeffs'] = {}
cosine_i = hy_obj.cosine_i()
for band_num,band in enumerate(hy_obj.bad_bands):
if ~band:
band = hy_obj.get_band(band_num,mask='calc_topo')
topo_dict['coeffs'][band_num] = calc_c(band,cosine_i[hy_obj.mask['calc_topo']],
fit_type=topo_dict['c_fit_type'])
hy_obj.topo = topo_dict
def get_band_samples(hy_obj,args):
band = hy_obj.get_band(args['band_num'],
corrections = hy_obj.corrections)
return band[hy_obj.ancillary['sample_mask'] !=0]
def get_cosine_i_samples(hy_obj):
'''Calculate and sample cosine_i
'''
cosine_i=hy_obj.cosine_i()
cosine_i = cosine_i[hy_obj.ancillary['sample_mask'] !=0]
return cosine_i
def calc_c_coeffs_group(actors,topo_dict,group_tag):
cosine_i_samples = ray.get([a.do.remote(get_cosine_i_samples) for a in actors])
cosine_i_samples = np.concatenate(cosine_i_samples)
print(f'Topo Subgroup {group_tag}')
bad_bands = ray.get(actors[0].do.remote(lambda x: x.bad_bands))
coeffs = {}
for band_num,band in enumerate(bad_bands):
if ~band:
coeffs[band_num] = {}
band_samples = ray.get([a.do.remote(get_band_samples,
{'band_num':band_num}) for a in actors])
band_samples = np.concatenate(band_samples)
coeffs[band_num] = calc_c(band_samples,cosine_i_samples,fit_type=topo_dict['c_fit_type'])
progbar(np.sum(~bad_bands[:band_num+1]),np.sum(~bad_bands))
print('\n')
#Update TOPO coeffs
_ = ray.get([a.do.remote(update_topo,{'key':'coeffs',
'value': coeffs}) for a in actors])
_ = ray.get([a.do.remote(update_topo,{'key':'subgroup',
'value': group_tag}) for a in actors])
def apply_c(hy_obj,data,dimension,index):
''' Apply SCSS correction to a slice of the data
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if 'cos_sz' not in hy_obj.ancillary.keys():
cos_sz = np.cos(hy_obj.get_anc('solar_zn'))
hy_obj.ancillary['cos_sz'] = cos_sz
if 'cosine_i' not in hy_obj.ancillary.keys():
cosine_i = hy_obj.cosine_i()
hy_obj.ancillary['cosine_i'] = cosine_i
C_bands = list(hy_obj.topo['coeffs'].keys())
C = np.array(list(hy_obj.topo['coeffs'].values()))
#Convert to float
data = data.astype(np.float32)
if dimension == 'line':
#index= 3000
#data = hy_obj.get_line(3000)
data = data[:,C_bands]
mask = hy_obj.mask['apply_topo'][index,:]
cosine_i = hy_obj.ancillary['cosine_i'][[index],:].T
cos_sz = hy_obj.ancillary['cos_sz'][[index],:].T
correction_factor = (cos_sz + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
elif dimension == 'column':
# index= 300
# data = hy_obj.get_column(index)
data = data[:,C_bands]
mask = hy_obj.mask['apply_topo'][:,index]
cosine_i = hy_obj.ancillary['cosine_i'][:,[index]]
cos_sz = hy_obj.ancillary['cos_sz'][:,[index]]
correction_factor = (cos_sz + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
elif dimension == 'band':
#index= 8
#data = hy_obj.get_band(index)
C = hy_obj.topo['coeffs'][index]
correction_factor = (hy_obj.ancillary['cos_sz'] + C)/(hy_obj.ancillary['cosine_i'] + C)
data[hy_obj.mask['apply_topo']] = data[hy_obj.mask['apply_topo']] * correction_factor[hy_obj.mask['apply_topo']]
elif dimension == 'chunk':
# index = 200,501,3000,3501
x1,x2,y1,y2 = index
# data = hy_obj.get_chunk(x1,x2,y1,y2)
data = data[:,:,C_bands]
mask = hy_obj.mask['apply_topo'][y1:y2,x1:x2]
cosine_i = hy_obj.ancillary['cosine_i'][y1:y2,x1:x2][:,:,np.newaxis]
cos_sz = hy_obj.ancillary['cos_sz'][y1:y2,x1:x2][:,:,np.newaxis]
correction_factor = (cos_sz + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
elif dimension == 'pixels':
# index = [[2000,2001],[200,501]]
y,x = index
# data = hy_obj.get_pixels(y,x)
data = data[:,C_bands]
mask = hy_obj.mask['apply_topo'][y,x]
cosine_i = hy_obj.ancillary['cosine_i'][[y],[x]].T
cos_sz = hy_obj.ancillary['cos_sz'][[y],[x]].T
correction_factor = (cos_sz + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
return data

View File

@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
This module contains functions to apply the Modified topographic correction (SCS+C)
described in the following paper:
Richter, R., Kellenberger, T., & Kaufmann, H. (2009).
Comparison of topographic correction methods.
Remote Sensing, 1(3), 184-196.
https://doi.org/10.3390/rs1030184
Topographic correction consists of the following steps:
"""
import numpy as np
def calc_cosine_coeffs(hy_obj,topo_dict):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
Returns:
None.
'''
hy_obj.topo = topo_dict
hy_obj.anc_data = {}
cos_i = hy_obj.cosine_i()
cos_solar_zn = np.cos(hy_obj.get_anc('solar_zn'))
c_factor = cos_solar_zn/cos_i
c_factor[~hy_obj.mask['no_data']] = 1.
hy_obj.ancillary['cosine_factor'] =c_factor
def apply_cosine(hy_obj,data,dimension,index):
''' Apply cosine correction to a slice of the data
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if 'cosine_factor' not in hy_obj.ancillary.keys():
calc_cosine_coeffs(hy_obj)
#Convert to float
data = data.astype(np.float32)
if dimension == 'line':
#index= 3000
#data = hy_obj.get_line(3000)
data = data*hy_obj.ancillary['cosine_factor'][np.newaxis,index,:]
elif dimension == 'column':
#index= 300
#data = hy_obj.get_column(index)
data = hy_obj.ancillary['cosine_factor'][:,index,np.newaxis]
elif dimension == 'band':
#index= 8
#data = hy_obj.get_band(index)
data = data * hy_obj.ancillary['cosine_factor']
elif dimension == 'chunk':
#index = 200,501,3000,3501
x1,x2,y1,y2 = index
#data = hy_obj.get_chunk(x1,x2,y1,y2)
data = data*hy_obj.ancillary['cosine_factor'][y1:y2,x1:x2][:,:,np.newaxis]
elif dimension == 'pixels':
#index = [[2000,2001],[200,501]]
y,x = index
#data = hy_obj.get_pixels(y,x)
data = data*hy_obj.ancillary['cosine_factor'][y,x][:, np.newaxis]
return data

View File

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
This module contains functions to apply the Modified topographic correction (SCS+C)
described in the following paper:
Richter, R., Kellenberger, T., & Kaufmann, H. (2009).
Comparison of topographic correction methods.
Remote Sensing, 1(3), 184-196.
https://doi.org/10.3390/rs1030184
Topographic correction consists of the following steps:
"""
import numpy as np
def calc_modminn_coeffs(hy_obj,topo_dict):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
Returns:
None.
'''
hy_obj.topo =topo_dict
cos_i = hy_obj.cosine_i()
i = np.rad2deg(np.arccos(cos_i))
solar_zn = hy_obj.get_anc('solar_zn',radians=False)
solar_zn_t = np.zeros(solar_zn.shape)
solar_zn_t[solar_zn < 45] = solar_zn[solar_zn < 45] +20
solar_zn_t[(solar_zn >= 45) & (solar_zn <= 55)] = solar_zn[(solar_zn >= 45) & (solar_zn <= 55)] +15
solar_zn_t[solar_zn > 55] = solar_zn[solar_zn > 55] +10
#Create NDVI mask to seperate vegetation
ir = hy_obj.get_wave(850)
red = hy_obj.get_wave(660)
ndvi = (ir-red)/(ir+red)
veg_mask = ndvi > 0.2
c_factors = np.ones((2,hy_obj.lines,hy_obj.columns))
c_factors[:] = cos_i/np.cos(np.radians(solar_zn_t))
# Non vegetation correction factor
c_factors[0][~veg_mask] = c_factors[0][~veg_mask]**(1/2)
c_factors[1][~veg_mask] = c_factors[1][~veg_mask]**(1/2)
# Vegetation correction factor
c_factors[0][veg_mask] = c_factors[0][veg_mask]**(3/4)
c_factors[1][veg_mask] = c_factors[1][veg_mask]**(1/3)
#Adjust correction factors to prevent too strong correction
c_factors[c_factors <.25] = .25
c_factors[c_factors > 1] = 1
#Correct pixels only where i > threshold
c_factors[0][i < solar_zn_t] = 1
c_factors[1][i < solar_zn_t] = 1
c_factors[0][ir == hy_obj.no_data] = 1
c_factors[1][ir == hy_obj.no_data] = 1
hy_obj.ancillary['mm_c_factor'] = c_factors
def apply_modminn(hy_obj,data,dimension,index):
''' Apply SCSS correction to a slice of the data
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if 'mm_c_factor' not in hy_obj.ancillary.keys():
calc_modminn_coeffs(hy_obj)
#Convert to float
data = data.astype(np.float32)
wave_mask =hy_obj.wavelengths >=720
if dimension == 'line':
#index= 3000
#data = hy_obj.get_line(3000)
data[:,wave_mask] = data[:,wave_mask]*hy_obj.ancillary['mm_c_factor'][1,index,:][:,np.newaxis]
data[:,~wave_mask] = data[:,~wave_mask]*hy_obj.ancillary['mm_c_factor'][0,index,:][:,np.newaxis]
elif dimension == 'column':
#index= 300
#data = hy_obj.get_column(index)
data[:,wave_mask] = data[:,wave_mask]*hy_obj.ancillary['mm_c_factor'][1,:,index][:,np.newaxis]
data[:,~wave_mask] = data[:,~wave_mask]*hy_obj.ancillary['mm_c_factor'][0,:,index][:,np.newaxis]
elif dimension == 'band':
#index= 50
#data = hy_obj.get_band(index)
if hy_obj.wavelengths[index] >=720:
cf_index = 1
else:
cf_index = 0
data = data * hy_obj.ancillary['mm_c_factor'][cf_index]
elif dimension == 'chunk':
#index = 200,501,3000,3501
x1,x2,y1,y2 = index
#data = hy_obj.get_chunk(x1,x2,y1,y2)
data[:,:,wave_mask] = data[:,:,wave_mask]*hy_obj.ancillary['mm_c_factor'][1,y1:y2,x1:x2][:,:,np.newaxis]
data[:,:,~wave_mask] = data[:,:,~wave_mask]*hy_obj.ancillary['mm_c_factor'][0,y1:y2,x1:x2][:,:,np.newaxis]
elif dimension == 'pixels':
#index = [[2000,2001],[200,501]]
y,x = index
#data = hy_obj.get_pixels(y,x)
data[:,wave_mask] = data[:,wave_mask]*hy_obj.ancillary['mm_c_factor'][1,y,x][:, np.newaxis]
data[:,~wave_mask] = data[:,~wave_mask]*hy_obj.ancillary['mm_c_factor'][0,y,x][:, np.newaxis]
return data

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
This module contains functions to apply the Modified topographic correction (SCS+C)
described in the following paper:
Richter, R., Kellenberger, T., & Kaufmann, H. (2009).
Comparison of topographic correction methods.
Remote Sensing, 1(3), 184-196.
https://doi.org/10.3390/rs1030184
Topographic correction consists of the following steps:
"""
import numpy as np
def calc_scs_coeffs(hy_obj,topo_dict):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
Returns:
None.
'''
hy_obj.topo = topo_dict
hy_obj.anc_data = {}
cos_i = hy_obj.cosine_i()
cos_solar_zn = np.cos(hy_obj.get_anc('solar_zn'))
cos_slope = np.cos(hy_obj.get_anc('slope'))
c_factor = (cos_slope *cos_solar_zn)/cos_i
c_factor[~hy_obj.mask['no_data']] = 1.
hy_obj.ancillary['scs_factor'] =c_factor
def apply_scs(hy_obj,data,dimension,index):
''' Apply SCSS correction to a slice of the data
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if 'scs_factor' not in hy_obj.ancillary.keys():
calc_scs_coeffs(hy_obj)
#Convert to float
data = data.astype(np.float32)
if dimension == 'line':
#index= 3000
#data = hy_obj.get_line(3000)
data = data*hy_obj.ancillary['scs_factor'][np.newaxis,index,:]
elif dimension == 'column':
#index= 300
#data = hy_obj.get_column(index)
data = hy_obj.ancillary['scs_factor'][:,index,np.newaxis]
elif dimension == 'band':
#index= 8
#data = hy_obj.get_band(index)
data = data * hy_obj.ancillary['scs_factor']
elif dimension == 'chunk':
#index = 200,501,3000,3501
x1,x2,y1,y2 = index
#data = hy_obj.get_chunk(x1,x2,y1,y2)
data = data*hy_obj.ancillary['scs_factor'][y1:y2,x1:x2][:,:,np.newaxis]
elif dimension == 'pixels':
#index = [[2000,2001],[200,501]]
y,x = index
#data = hy_obj.get_pixels(y,x)
data = data*hy_obj.ancillary['scs_factor'][y,x][:, np.newaxis]
return data

View File

@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
This module contains functions to apply a topographic correction (SCS+C)
described in the following papers:
Scott A. Soenen, Derek R. Peddle, & Craig A. Coburn (2005).
SCS+C: A Modified Sun-Canopy-Sensor Topographic Correction in Forested Terrain.
IEEE Transactions on Geoscience and Remote Sensing, 43(9), 2148-2159.
https://doi.org/10.1109/TGRS.2005.852480
Topographic correction consists of the following steps:
1. calculate incidence angle if it is not provided
2. estimate C-Correction value
3. apply C-Correction value to the image data
TODO: Rationale/ examples for using different fitting algorithms
"""
import numpy as np
from .c import calc_c, get_band_samples, get_cosine_i_samples
import ray
from ..misc import update_topo
from ..misc import progbar
def calc_scsc_c1(solar_zn,slope):
""" Calculate c1
All input geometry units must be in radians.
Args:
solar_zn (numpy.ndarray): Solar zenith angle.
slope (numpy.ndarray): Ground slope.
Returns:
numpy.ndarray: C1.
"""
# Eq 11. Soenen et al. 2005
scsc_c1 = np.cos(solar_zn) * np.cos(slope)
return scsc_c1
def calc_scsc_coeffs(hy_obj,topo_dict):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
Returns:
None.
'''
topo_dict['coeffs'] = {}
cosine_i = hy_obj.cosine_i()
for band_num,band in enumerate(hy_obj.bad_bands):
if ~band:
band = hy_obj.get_band(band_num,mask='calc_topo')
topo_dict['coeffs'][band_num] = calc_c(band,cosine_i[hy_obj.mask['calc_topo']],
fit_type=topo_dict['c_fit_type'])
hy_obj.topo = topo_dict
def calc_scsc_coeffs_group(actors,topo_dict,group_tag):
cosine_i_samples = ray.get([a.do.remote(get_cosine_i_samples) for a in actors])
cosine_i_samples = np.concatenate(cosine_i_samples)
print(f'Topo Subgroup {group_tag}')
bad_bands = ray.get(actors[0].do.remote(lambda x: x.bad_bands))
coeffs = {}
for band_num,band in enumerate(bad_bands):
if ~band:
coeffs[band_num] = {}
band_samples = ray.get([a.do.remote(get_band_samples,
{'band_num':band_num}) for a in actors])
band_samples = np.concatenate(band_samples)
coeffs[band_num] = calc_c(band_samples,cosine_i_samples,fit_type=topo_dict['c_fit_type'])
progbar(np.sum(~bad_bands[:band_num+1]),np.sum(~bad_bands))
print('\n')
#Update TOPO coeffs
_ = ray.get([a.do.remote(update_topo,{'key':'coeffs',
'value': coeffs}) for a in actors])
_ = ray.get([a.do.remote(update_topo,{'key':'subgroup',
'value': group_tag}) for a in actors])
def apply_scsc_band(hy_obj,band,index):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
c1 = np.cos(hy_obj.get_anc('slope')) * np.cos(hy_obj.get_anc('solar_zn'))
cosine_i = hy_obj.cosine_i()
C = hy_obj.topo['coeffs'][index]
correction_factor = (c1 + C)/(cosine_i + C)
band[hy_obj.mask['calc_topo']] = band[hy_obj.mask['calc_topo']] * correction_factor[hy_obj.mask['calc_topo']]
band[~hy_obj.mask['no_data']] = hy_obj.no_data
return band
def apply_scsc(hy_obj,data,dimension,index):
''' Apply SCSS correction to a slice of the data
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if 'c1' not in hy_obj.ancillary.keys():
c1 = np.cos(hy_obj.get_anc('slope')) * np.cos(hy_obj.get_anc('solar_zn'))
hy_obj.ancillary['c1'] = c1
if 'cosine_i' not in hy_obj.ancillary.keys():
cosine_i = hy_obj.cosine_i()
hy_obj.ancillary['cosine_i'] = cosine_i
C_bands = list([int(x) for x in hy_obj.topo['coeffs'].keys()])
C = np.array(list(hy_obj.topo['coeffs'].values()))
#Convert to float
data = data.astype(np.float32)
hy_obj.topo['coeffs'] = {int(k): hy_obj.topo['coeffs'][k] for k in hy_obj.topo['coeffs']}
if (dimension != 'band') & (dimension != 'chunk'):
if dimension == 'line':
#index= 3000
#data = hy_obj.get_line(3000)
mask = hy_obj.mask['apply_topo'][index,:]
cosine_i = hy_obj.ancillary['cosine_i'][[index],:].T
c1 = hy_obj.ancillary['c1'][[index],:].T
elif dimension == 'column':
#index= 300
#data = hy_obj.get_column(index)
mask = hy_obj.mask['apply_topo'][:,index]
cosine_i = hy_obj.ancillary['cosine_i'][:,[index]]
c1 = hy_obj.ancillary['c1'][:,[index]]
elif dimension == 'pixels':
#index = [[2000,2001],[200,501]]
y,x = index
#data = hy_obj.get_pixels(y,x)
mask = hy_obj.mask['apply_topo'][y,x]
cosine_i = hy_obj.ancillary['cosine_i'][[y],[x]].T
c1 = hy_obj.ancillary['c1'][[y],[x]].T
correction_factor = np.ones(data.shape)
correction_factor[:,C_bands] = (c1 + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
elif dimension == 'chunk':
#index = 200,501,3000,3501
x1,x2,y1,y2 = index
#data = hy_obj.get_chunk(x1,x2,y1,y2)
mask = hy_obj.mask['apply_topo'][y1:y2,x1:x2]
cosine_i = hy_obj.ancillary['cosine_i'][y1:y2,x1:x2][:,:,np.newaxis]
c1 = hy_obj.ancillary['c1'][y1:y2,x1:x2][:,:,np.newaxis]
correction_factor = np.ones(data.shape)
correction_factor[:,:,C_bands] = (c1 + C)/(cosine_i + C)
data[mask,:] = data[mask,:]*correction_factor[mask,:]
elif (dimension == 'band') and (index in hy_obj.topo['coeffs']):
#index= 8
#data = hy_obj.get_band(index)
C = hy_obj.topo['coeffs'][index]
correction_factor = (hy_obj.ancillary['c1'] + C)/(hy_obj.ancillary['cosine_i'] + C)
data[hy_obj.mask['apply_topo']] = data[hy_obj.mask['apply_topo']] * correction_factor[hy_obj.mask['apply_topo']]
return data

View File

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
"""
HyTools: Hyperspectral image processing library
Copyright (C) 2021 University of Wisconsin
Authors: Adam Chlus, Zhiwei Ye, Philip Townsend.
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/>.
Topographic correction
"""
import json
import numpy as np
import ray
from .modminn import apply_modminn,calc_modminn_coeffs
from .scsc import apply_scsc,calc_scsc_coeffs, calc_scsc_coeffs_group
from .cosine import apply_cosine,calc_cosine_coeffs
from .c import apply_c,calc_c_coeffs, calc_c_coeffs_group
from .scs import apply_scs,calc_scs_coeffs
from ..masks import mask_create
from ..misc import set_topo
def calc_cosine_i(solar_zn, solar_az, aspect ,slope):
"""Generate cosine i image. The cosine of the incidence angle (i) is
defined as the angle between the normal to the pixel surface
and the solar zenith direction.
All input geometry units must be in radians.
Args:
solar_az (numpy.ndarray): Solar azimuth angle.
solar_zn (numpy.ndarray): Solar zenith angle.
aspect (numpy.ndarray): Ground aspect.
slope (numpy.ndarray): Ground slope.
Returns:
cnumpy.ndarray: Cosine i image.
"""
relative_az = aspect - solar_az
cosine_i = np.cos(solar_zn)*np.cos(slope) + np.sin(solar_zn)*np.sin(slope)* np.cos(relative_az)
return cosine_i
def apply_topo_correct(hy_obj,data,dimension,index):
'''
Args:
hy_obj (TYPE): DESCRIPTION.
band (TYPE): DESCRIPTION.
index (TYPE): DESCRIPTION.
Returns:
band (TYPE): DESCRIPTION.
'''
if ('apply_topo' not in hy_obj.mask) & ('apply_mask' in hy_obj.topo):
hy_obj.gen_mask(mask_create,'apply_topo',hy_obj.topo['apply_mask'])
if hy_obj.topo['type'] == 'mod_minneart':
data = apply_modminn(hy_obj,data,dimension,index)
elif hy_obj.topo['type'] == 'scs+c':
data = apply_scsc(hy_obj,data,dimension,index)
elif hy_obj.topo['type'] == 'cosine':
data = apply_cosine(hy_obj,data,dimension,index)
elif hy_obj.topo['type'] == 'c':
data = apply_c(hy_obj,data,dimension,index)
elif hy_obj.topo['type'] == 'scs':
data = apply_scs(hy_obj,data,dimension,index)
return data
def load_topo_precomputed(hy_obj,topo_dict):
with open(topo_dict['coeff_files'][hy_obj.file_name], 'r') as outfile:
hy_obj.topo = json.load(outfile)
def get_topo_sample_mask(hy_obj,topo_dict):
sample_ratio = float(topo_dict["sample_perc"])
subsample_mask = np.copy(hy_obj.mask['calc_topo'])
idx = np.array(np.where(subsample_mask!=0)).T
if idx.shape[0]>5:
idxRand= idx[np.random.choice(range(len(idx)),int(len(idx)*(1-sample_ratio)), replace = False)].T
subsample_mask[idxRand[0],idxRand[1]] = 0
subsample_mask = subsample_mask.astype(np.int8)
hy_obj.ancillary['sample_mask']=subsample_mask
def calc_topo_coeffs(actors,topo_dict,actor_group_list=None,group_tag_list=None):
#def calc_topo_coeffs(actors,actor_group_list,topo_dict,group_tag_list):
if topo_dict['type'] == 'precomputed':
print("Using precomputed topographic coefficients.")
_ = ray.get([a.do.remote(load_topo_precomputed,topo_dict) for a in actors]) # actors
#_ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])
else:
print("Calculating topographic coefficients.")
_ = ray.get([a.do.remote(set_topo,topo_dict) for a in actors])
_ = ray.get([a.gen_mask.remote(mask_create,'calc_topo',
topo_dict['calc_mask']) for a in actors])
if (actor_group_list is None) or (topo_dict['type'] in ['scs','mod_minneart','cosine']):
# no grouping
if topo_dict['type'] == 'scs+c':
_ = ray.get([a.do.remote(calc_scsc_coeffs,topo_dict) for a in actors])
elif topo_dict['type'] == 'scs':
_ = ray.get([a.do.remote(calc_scs_coeffs,topo_dict) for a in actors])
elif topo_dict['type'] == 'mod_minneart':
_ = ray.get([a.do.remote(calc_modminn_coeffs,topo_dict) for a in actors])
elif topo_dict['type'] == 'cosine':
_ = ray.get([a.do.remote(calc_cosine_coeffs,topo_dict) for a in actors])
elif topo_dict['type'] == 'c':
_ = ray.get([a.do.remote(calc_c_coeffs,topo_dict) for a in actors])
#_ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])
else:
_ = ray.get([a.do.remote(get_topo_sample_mask,topo_dict) for a in actors])
for group_order, sub_actors in enumerate(actor_group_list):
#return 0
if topo_dict['type'] == 'scs+c':
calc_scsc_coeffs_group(sub_actors,topo_dict,group_tag_list[group_order])
elif topo_dict['type'] == 'c':
calc_c_coeffs_group(sub_actors,topo_dict,group_tag_list[group_order])
_ = ray.get([a.do.remote(lambda x: x.corrections.append('topo')) for a in actors])
def calc_topo_coeffs_single(hy_obj,topo_dict):
if topo_dict['type'] == 'precomputed':
print("Using precomputed topographic coefficients.")
load_topo_precomputed(hy_obj,topo_dict)
else:
print("Calculating topographic coefficients.")
hy_obj.gen_mask(mask_create,'calc_topo',topo_dict['calc_mask'])
if topo_dict['type'] == 'scs+c':
calc_scsc_coeffs(hy_obj,topo_dict)
elif topo_dict['type'] == 'scs':
calc_scs_coeffs(hy_obj,topo_dict)
elif topo_dict['type'] == 'mod_minneart':
calc_modminn_coeffs(hy_obj,topo_dict)
elif topo_dict['type'] == 'cosine':
calc_cosine_coeffs(hy_obj,topo_dict)
elif topo_dict['type'] == 'c':
calc_c_coeffs(hy_obj,topo_dict)
hy_obj.corrections.append('topo')