223 lines
7.2 KiB
Python
223 lines
7.2 KiB
Python
# -*- 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
|
|
|
|
|
|
|