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,294 @@
'''Template script for generating image_correct configuration JSON files.
These setting are meant only as an example, are not appropriate for
all situations and may need to be adjusted
'''
import json
import glob
import numpy as np
#Output path for configuration file
config_file = "/data1/temp/ht_test/ic_test.json"
config_dict = {}
#Only coefficients for good bands will be calculated
config_dict['bad_bands'] =[[300,400],[1337,1430],[1800,1960],[2450,2600]]
#config_dict['bad_bands'] =[[300,400],[900,2600]] # Subset for testing
# Input data settings for NEON
#################################################################
# config_dict['file_type'] = 'neon'
# images= glob.glob("/data1/temp/ht_test/*.h5")
# images.sort()
# config_dict["input_files"] = images
# Input data settings for ENVI
#################################################################
''' Only difference between ENVI and NEON settings is the specification
of the ancillary datasets (ex. viewing and solar geometry). All hytools
functions assume that the ancillary data and the image date are the same
size, spatially, and are ENVI formatted files.
The ancillary parameter is a dictionary with a key per image. Each value
per image is also a dictionary where the key is the dataset name and the
value is list consisting of the file path and the band number.
'''
config_dict['file_type'] = 'envi'
aviris_anc_names = ['path_length','sensor_az','sensor_zn',
'solar_az', 'solar_zn','phase','slope',
'aspect', 'cosine_i','utc_time']
images= glob.glob("/data1/temp/ht_test/ang20190707t203417_rfl_v2v2_img")
images.sort()
config_dict["input_files"] = images
config_dict["anc_files"] = {}
anc_files = glob.glob("/data1/temp/ht_test/ang20190707t203417_rdn_v2v2_obs_ort_corr")
anc_files.sort()
for i,image in enumerate(images):
config_dict["anc_files"][image] = dict(zip(aviris_anc_names,
[[anc_files[i],a] for a in range(len(aviris_anc_names))]))
# Export settings
#################################################################
''' Options for subset waves:
1. List of subset wavelenths
2. Empty list, this will output all good bands, if resampler is
set it will also resample.
- Currently resampler cannot be used in conjuction with option 1
'''
config_dict['export'] = {}
config_dict['export']['coeffs'] = True
config_dict['export']['image'] = True
config_dict['export']['masks'] = True
config_dict['export']['subset_waves'] = []
config_dict['export']['output_dir'] = "/data1/temp/ht_test/"
config_dict['export']["suffix"] = 'anc_nodata_test'
#Corrections
#################################################################
''' Specify correction(s) to be applied, corrections will be applied
in the order they are specified.
Options include:
['topo']
['brdf']
['glint']
['topo','brdf']
['brdf','topo']
['brdf','topo','glint']
[] <---Export uncorrected images
'''
config_dict["corrections"] = ['brdf']
#Topographic Correction options
#################################################################
'''
Types supported:
- 'cosine'
- 'c'
- 'scs
- 'scs+c'
- 'mod_minneart'
- 'precomputed'
Apply and calc masks are only needed for C and SCS+C corrections. They will
be ignored in all other cases and correction will be applied to all
non no-data pixels.
'c_fit_type' is only applicable for the C or SCS+C correction type. Options
include 'ols' or 'nnls'. Choosing 'nnls' can limit overcorrection.
For precomputed topographic coefficients 'coeff_files' is a
dictionary where each key is the full the image path and value
is the full path to coefficients file, one per image.
'''
config_dict["topo"] = {}
config_dict["topo"]['type'] = 'scs+c'
config_dict["topo"]['calc_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.1,'max': 1.0}],
['ancillary',{'name':'slope',
'min': np.radians(5),'max':'+inf' }],
['ancillary',{'name':'cosine_i',
'min': 0.12,'max':'+inf' }],
['cloud',{'method':'zhai_2018',
'cloud':True,'shadow':True,
'T1': 0.01,'t2': 1/10,'t3': 1/4,
't4': 1/2,'T7': 9,'T8': 9}]]
config_dict["topo"]['apply_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.1,'max': 1.0}],
['ancillary',{'name':'slope',
'min': np.radians(5),'max':'+inf' }],
['ancillary',{'name':'cosine_i',
'min': 0.12,'max':'+inf' }]]
config_dict["topo"]['c_fit_type'] = 'nnls'
# config_dict["topo"]['type'] = 'precomputed'
# config_dict["brdf"]['coeff_files'] = {}
#BRDF Correction options
#################################################################3
'''
Types supported:
- 'universal': Simple kernel multiplicative correction.
- 'local': Correction by class. (Future.....)
- 'flex' : Correction by NDVI class
- 'precomputed' : Use precomputed coefficients
If 'bin_type' == 'user'
'bins' should be a list of lists, each list the NDVI bounds [low,high]
Object shapes ('h/b','b/r') only needed for Li kernels.
For precomputed topographic coefficients 'coeff_files' is a
dictionary where each key is the full the image path and value
is the full path to coefficients file, one per image.
'''
config_dict["brdf"] = {}
# Options are 'line','scene', or a float for a custom solar zn
# Custom solar zenith angle should be in radians
config_dict["brdf"]['solar_zn_type'] ='scene'
# Universal BRDF config
#----------------------
# config_dict["brdf"]['type'] = 'universal'
# config_dict["brdf"]['grouped'] = True
# config_dict["brdf"]['sample_perc'] = 0.1
# config_dict["brdf"]['geometric'] = 'li_dense_r'
# config_dict["brdf"]['volume'] = 'ross_thick'
# config_dict["brdf"]["b/r"] = 2.5
# config_dict["brdf"]["h/b"] = 2
# config_dict["brdf"]['calc_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
# 'min': 0.1,'max': 1.0}]]
# config_dict["brdf"]['apply_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
# 'min': 0.1,'max': 1.0}]]
# config_dict["brdf"]['diagnostic_plots'] = True
# config_dict["brdf"]['diagnostic_waves'] = [440,550,660,850]
#----------------------
# ## Flex BRDF configs
# ##------------------
config_dict["brdf"]['type'] = 'flex'
config_dict["brdf"]['grouped'] = True
config_dict["brdf"]['geometric'] = 'li_dense_r'
config_dict["brdf"]['volume'] = 'ross_thick'
config_dict["brdf"]["b/r"] = 2.5
config_dict["brdf"]["h/b"] = 2
config_dict["brdf"]['sample_perc'] = 0.1
config_dict["brdf"]['interp_kind'] = 'linear'
config_dict["brdf"]['calc_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.1,'max': 1.0}],
['kernel_finite',{}],
['ancillary',{'name':'sensor_zn',
'min':np.radians(2),'max':'inf' }],
['neon_edge',{'radius': 30}],
['cloud',{'method':'zhai_2018',
'cloud':True,'shadow':True,
'T1': 0.01,'t2': 1/10,'t3': 1/4,
't4': 1/2,'T7': 9,'T8': 9}]]
config_dict["brdf"]['apply_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.05,'max': 1.0}]]
# ## Flex dynamic NDVI params
config_dict["brdf"]['bin_type'] = 'dynamic'
config_dict["brdf"]['num_bins'] = 18
config_dict["brdf"]['ndvi_bin_min'] = 0.05
config_dict["brdf"]['ndvi_bin_max'] = 1.0
config_dict["brdf"]['ndvi_perc_min'] = 10
config_dict["brdf"]['ndvi_perc_max'] = 95
# ## Flex fixed bins specified by user
# config_dict["brdf"]['bin_type'] = 'user'
# config_dict["brdf"]['bins'] = [[0.1,.25],[.25,.75],[.75,1]]
# ##-----------------
## Precomputed BRDF coefficients
##------------------------------
# config_dict["brdf"]['type'] = 'precomputed'
# config_dict["brdf"]['coeff_files'] = {}
##------------------------------
#Glint Correction options
#################################################################
'''
Types supported:
- hochberg
- hedley
- gao
Common reference bands include:
- 860nm (NIR)
- 1650nm (SWIR)
- 2190nm (SWIR)
The Hedley-specific config would be in the form of:
[ImagePath]: [y1, y2, x1, x1]
e.g.:
config_dict["glint"]["deep_water_sample"] = {
"/path_to_image1": [
137, 574, 8034, 8470
],
"/path_to_image2": [
48, 393, 5780, 5925
],
}
'''
config_dict["glint"] = {}
config_dict['glint']['type'] = 'hedley'
config_dict['glint']['correction_wave'] = 1650
# External masks for glint correction
mask_files = glob.glob("/data2/prisma/rfl/PRS_20210629153937_20210629153942_0001_modtran/*_cls")
mask_files.sort()
file_dict = dict(zip(images,mask_files))
config_dict['glint']['apply_mask'] = [["external", {'class' : 1,
'files' : file_dict}]]
config_dict["glint"]["deep_water_sample"] = {
images[0]: [
225,250,240,260
]}
#Wavelength resampling options
##############################
'''
Types supported:
- 'gaussian': needs output waves and output FWHM
- 'linear', 'nearest', 'nearest-up',
'zero', 'slinear', 'quadratic','cubic': Piecewise
interpolation using Scipy interp1d
config_dict["resampler"] only needed when resampling == True
'''
config_dict["resample"] = False
# config_dict["resampler"] = {}
# config_dict["resampler"]['type'] = 'cubic'
# config_dict["resampler"]['out_waves'] = []
# config_dict["resampler"]['out_fwhm'] = []
# Remove bad bands from output waves
# for wavelength in range(450,660,100):
# bad=False
# for start,end in config_dict['bad_bands']:
# bad = ((wavelength >= start) & (wavelength <=end)) or bad
# if not bad:
# config_dict["resampler"]['out_waves'].append(wavelength)
config_dict['num_cpus'] = len(images)
with open(config_file, 'w') as outfile:
json.dump(config_dict,outfile,indent=3)

View File

@ -0,0 +1,338 @@
# python c:/mydata/image_correct_json_generate_gui.py
import os
import json
import glob
import numpy as np
import tkinter as tk
from tkinter.filedialog import asksaveasfilename, askdirectory
def fill_config(images, anc_files,out_coef_dir,img_file_type,corr_list, flag_pre_compute=False, topo_coeff = [], brdf_coeff=[]):
config_dict = {}
#Only coefficients for good bands will be calculated
config_dict['bad_bands'] =[[300,400],[1337,1430],[1800,1960],[2450,2600]]
config_dict['file_type'] = img_file_type #'envi'
aviris_anc_names = ['path_length','sensor_az','sensor_zn',
'solar_az', 'solar_zn','phase','slope',
'aspect', 'cosine_i','utc_time']
#aviris_anc_names = ['sensor_az','sensor_zn',
# 'solar_az', 'solar_zn']
images.sort()
config_dict["input_files"] = images
if img_file_type=='envi':
config_dict["anc_files"] = {}
anc_files.sort()
for i,image in enumerate(images):
config_dict["anc_files"][image] = dict(zip(aviris_anc_names,
[[anc_files[i],a] for a in range(len(aviris_anc_names))]))
config_dict['export'] = {}
config_dict["topo"] = {}
config_dict["brdf"] = {}
if flag_pre_compute:
config_dict['export']['coeffs'] = False
config_dict['export']['image'] = True
else:
config_dict['export']['coeffs'] = True
config_dict['export']['image'] = False
config_dict['export']['masks'] = False
config_dict['export']['subset_waves'] = [660,550,440] #[440,550,660,850] #
config_dict['export']['output_dir'] = out_coef_dir
print('_'.join(corr_list))
if len(corr_list)>0:
config_dict['export']["suffix"] = '_'.join(corr_list) # 'brdf'
else:
config_dict['export']["suffix"] = 'raw'
config_dict["corrections"] = corr_list # ['brdf']
if flag_pre_compute and len(topo_coeff)==len(images):
config_dict["topo"]['type'] = 'precomputed'
topo_files = sorted(topo_coeff)
#print(dict(zip(images, topo_files)))
config_dict["topo"]['coeff_files'] = dict(zip(images, topo_files))
else:
config_dict["topo"]['type'] = 'scs+c'
config_dict["topo"]['calc_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.05,'max': 1.0}]
]
config_dict["topo"]['apply_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.05,'max': 1.0}]]
config_dict["topo"]['c_fit_type'] = 'nnls' #'ols' #'nnls' #
if flag_pre_compute and len(brdf_coeff)==len(images):
config_dict["brdf"]['type'] = 'precomputed'
brdf_files = sorted(brdf_coeff)
config_dict["brdf"]['coeff_files'] = dict(zip(images, brdf_files))
else:
# Options are 'line','scene', or a float for a custom solar zn
# Custom solar zenith angle should be in radians
config_dict["brdf"]['solar_zn_type'] ='scene'
#----------------------
# ## Flex BRDF configs
# ##------------------
config_dict["brdf"]['type'] = 'flex'
config_dict["brdf"]['grouped'] = True
config_dict["brdf"]['geometric'] = 'li_sparse_r'
config_dict["brdf"]['volume'] = 'ross_thick'
config_dict["brdf"]["b/r"] = 2.5
config_dict["brdf"]["h/b"] = 2
config_dict["brdf"]['sample_perc'] = 0.1
config_dict["brdf"]['interp_kind'] = 'linear'
config_dict["brdf"]['calc_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.05,'max': 1.0}],
['kernel_finite',{}],
['ancillary',{'name':'sensor_zn',
'min':np.radians(2),'max':'inf' }]
]
config_dict["brdf"]['apply_mask'] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.05,'max': 1.0}]]
# ## Flex dynamic NDVI params
config_dict["brdf"]['bin_type'] = 'dynamic'
config_dict["brdf"]['num_bins'] = 18
config_dict["brdf"]['ndvi_bin_min'] = 0.05
config_dict["brdf"]['ndvi_bin_max'] = 1.0
config_dict["brdf"]['ndvi_perc_min'] = 10
config_dict["brdf"]['ndvi_perc_max'] = 95
config_dict["resample"] = False
config_dict['num_cpus'] = len(images)
return config_dict
'''
def update_corr_list(corr_list_):
#print(chk_topo.get(),chk_brdf.get())
corr_list_ = ['topo']*chk_topo.get()+['brdf']*chk_brdf.get()
#print(corr_list)
'''
def gen_config(entry_outdir, entry_outjson,img_list_out, obs_list_out, radio_f_type, corr_list,chk_precompute, topo_list_out,brdf_list_out):
outdir_name = entry_outdir.get()+'/'
out_json = entry_outjson.get()
images = img_list_out['text'].split('\n')
anc_files = obs_list_out['text'].split('\n')
img_file_type = str(radio_f_type.get()).lower()
corr_list_str = ['topo']*(corr_list[0].get()) + ['brdf']*(corr_list[1].get())
flag_pre_compute = bool(chk_precompute.get())
#print(flag_pre_compute,chk_precompute.get())
if flag_pre_compute:
topo_json_list = topo_list_out['text'].split('\n')
brdf_json_list = brdf_list_out['text'].split('\n')
return_json_dict = fill_config(images, anc_files,outdir_name,img_file_type,corr_list_str,flag_pre_compute=flag_pre_compute, topo_coeff = topo_json_list, brdf_coeff=brdf_json_list)
else:
return_json_dict = fill_config(images, anc_files,outdir_name,img_file_type,corr_list_str)
with open(out_json, 'w') as outfile:
json.dump(return_json_dict,outfile,indent=3)
window.title(f"File saved- {out_json}")
def save_file(txt_out_json):
"""Save the current file as a new file."""
filepath = asksaveasfilename(
defaultextension=".json",
filetypes=[("JSON Files", "*.json"), ("All Files", "*.*")],
)
if not filepath:
return
txt_out_json.delete(0,tk.END)
txt_out_json.insert(0,filepath)
window.title(f"Export Configuration JSON - {filepath}")
def open_folder(out_component):
"""Open a file for editing."""
in_img_dir = askdirectory()
if not in_img_dir:
return
#print(in_img_dir)
out_component.delete(0, tk.END)
out_component.insert(0,in_img_dir)
window.title(f"Folder- {in_img_dir}")
def open_file(out_component, list_component, radio_f_type=None, pattern=None):
"""Open a file for editing."""
in_img_dir = askdirectory()
if not in_img_dir:
return
#out_component.delete("1.0", tk.END)
#out_component.insert("1.0",in_img_dir)
out_component["text"] = in_img_dir
if pattern is None:
img_file_type = str(radio_f_type.get()).lower()
file_ext_list = {'envi':'*img','neon':'*.h5'}
pattern = file_ext_list[img_file_type]
#if pattern is None:
# return in_img_dir
file_list = sorted(glob.glob(in_img_dir+'/'+pattern))
if len(file_list)==0:
list_component['text']= 'No files selected'
return
list_component['text']= '\n'.join([ os.path.normpath(x) for x in file_list])
#print(list_component['text'])
window.title(f"Folder- {in_img_dir}")
window = tk.Tk()
window.title("Setup image correction configuration file")
#window.rowconfigure(0, minsize=400, weight=1)
#window.columnconfigure(1, minsize=600, weight=1)
#txt_edit = tk.Text(window)
frm_img_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_img_buttons.columnconfigure(1, minsize=300, weight=1)
frm_img_buttons.rowconfigure(1, minsize=50, weight=1)
frm_obs_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_obs_buttons.columnconfigure(1, minsize=300, weight=1)
frm_obs_buttons.rowconfigure(1, minsize=50, weight=1)
frm_out_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_out_buttons.columnconfigure(1, weight=1) #, minsize=300
frm_out_json_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_out_json_buttons.columnconfigure(1, weight=1) #minsize=300,
frm_file_type = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_corr_type = tk.Frame(window, relief=tk.RAISED, bd=2)
frm_precomp = tk.Frame(window, relief=tk.RAISED, bd=2,highlightbackground="grey",highlightthickness=5,padx=5, pady=5)
frm_precomp.columnconfigure(1,weight=1)
frm_precomp.rowconfigure(1, minsize=50, weight=1)
frm_pre_topo_buttons = tk.Frame(frm_precomp, relief=tk.RAISED, bd=2)
frm_pre_topo_buttons.columnconfigure(1, minsize=280, weight=1)
#frm_pre_topo_buttons.rowconfigure(1, minsize=50, weight=1)
frm_pre_brdf_buttons = tk.Frame(frm_precomp, relief=tk.RAISED, bd=2)
frm_pre_brdf_buttons.columnconfigure(1, minsize=280, weight=1)
#frm_pre_brdf_buttons.rowconfigure(1, minsize=50, weight=1)
frm_final_gen = tk.Frame(window, relief=tk.RAISED, bd=2,highlightbackground="grey",highlightthickness=5)
frm_final_gen.rowconfigure(0, minsize=50, weight=0)
frm_final_gen.columnconfigure(0, minsize=100, weight=1) #
frm_final_gen.columnconfigure(1, minsize=50, weight=0)
frm_final_gen.columnconfigure(2, minsize=100, weight=1)
label_img_dir = tk.Label(frm_img_buttons,text="image", fg="white", bg="black")
label_obs_dir = tk.Label(frm_obs_buttons,text="image", fg="white", bg="black")
txt_outdir = tk.Entry(frm_out_buttons)
img_list_out = tk.Label(frm_img_buttons,bg="grey",anchor="w")
obs_list_out = tk.Label(frm_obs_buttons,bg="grey",anchor="w")
btn_open1 = tk.Button(frm_img_buttons, text="Image Folder...", command=lambda: open_file(label_img_dir,img_list_out,radio_f_type=f_type) ) #'*img'
btn_open2 = tk.Button(frm_obs_buttons, text="Obs_ort Folder...", command=lambda: open_file(label_obs_dir,obs_list_out,pattern='*obs_ort'))
btn_open3 = tk.Button(frm_out_buttons, text="Output coeff Folder...", command=lambda: open_folder(txt_outdir)) #, command=open_file(txt_edit, None)
#btn_open1 = tk.Button(frm_img_buttons, text="Image Folder...", command=lambda: open_file(label_img_dir,img_list_out,radio_f_type=f_type) ) #'*img'
#btn_open2 = tk.Button(frm_obs_buttons, text="Obs_ort Folder...", command=lambda: open_file(label_obs_dir,obs_list_out,pattern='*obs_ort'))
btn_open_topo = tk.Button(frm_pre_topo_buttons, text="TOPO json Folder...", command=lambda: open_file(label_pre_topo_dir,topo_list_out,pattern='*topo_coeffs*.json'))
btn_open_brdf = tk.Button(frm_pre_brdf_buttons, text="BRDF json Folder...", command=lambda: open_file(label_pre_brdf_dir,brdf_list_out,pattern='*brdf_coeffs*.json'))
txt_out_json = tk.Entry(frm_out_json_buttons)
btn_save = tk.Button(frm_out_json_buttons, text="Save As...", command=lambda: save_file(txt_out_json))
btn_open1.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
label_img_dir.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
img_list_out.grid(row=1, columnspan=2, sticky="nsew", padx=5, pady=5)
btn_open2.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
label_obs_dir.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
obs_list_out.grid(row=1, columnspan=2, sticky="nsew", padx=5, pady=5)
btn_open3.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
txt_outdir.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
btn_save.grid(row=0, column=0, sticky="ew", padx=5)
txt_out_json.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
f_type = tk.StringVar(value="envi")
ev_btn = tk.Radiobutton(frm_file_type, text='ENVI (*img)', variable=f_type, value='envi')
h5_btn = tk.Radiobutton(frm_file_type, text='NEON HDF5 (*.h5)', variable=f_type, value='neon')
ev_btn.grid(row=0, column=0, sticky="ew", padx=5)
h5_btn.grid(row=0, column=1, sticky="ew", padx=5)
chk_topo = tk.IntVar(value=0)
chk_brdf = tk.IntVar(value=1)
chk_topo_btn = tk.Checkbutton(frm_corr_type, text='TOPO', variable=chk_topo, onvalue=1, offvalue=0) #, command=lambda: update_corr_list(corr_list))
chk_brdf_btn = tk.Checkbutton(frm_corr_type, text='BRDF', variable=chk_brdf, onvalue=1, offvalue=0) #, command=lambda: update_corr_list(corr_list))
chk_topo_btn.grid(row=0, column=0, sticky="ew", padx=5)
chk_brdf_btn.grid(row=0, column=1, sticky="ew", padx=5)
corr_list = [chk_topo,chk_brdf]
chk_precompute = tk.IntVar(value=0)
coeff_list= []
label_precomput = tk.Checkbutton(frm_precomp, text='Load Precomputed Coefficients', variable=chk_precompute, onvalue=1, offvalue=0,anchor="center") #tk.Label(frm_precomp,text="Precomputed Coefficients")
label_precomput.grid(row=0, columnspan=2, sticky="ew", padx=2, pady=2)
btn_open_topo.grid(row=0, column=0, sticky="ew", padx=2, pady=2)
btn_open_brdf.grid(row=0, column=0, sticky="ew", padx=2, pady=2)
label_pre_topo_dir = tk.Label(frm_pre_topo_buttons,text="topo json", fg="white", bg="black")
label_pre_topo_dir.grid(row=0, column=1, sticky="ew", padx=2, pady=2)
label_pre_brdf_dir = tk.Label(frm_pre_brdf_buttons,text="brdf json", fg="white", bg="black")
label_pre_brdf_dir.grid(row=0, column=1, sticky="ew", padx=2, pady=2)
topo_list_out = tk.Label(frm_pre_topo_buttons,bg="grey",anchor="w")
topo_list_out.grid(row=1, columnspan=2, sticky="ew", padx=2,pady=2)
brdf_list_out = tk.Label(frm_pre_brdf_buttons,bg="grey",anchor="w")
brdf_list_out.grid(row=1, columnspan=2, sticky="ew", padx=2,pady=2)
frm_pre_topo_buttons.grid(row=1, column=0, sticky="ew")
frm_pre_brdf_buttons.grid(row=1, column=1, sticky="ew")
#img_list_out = tk.Label(frm_img_buttons,bg="grey",anchor="w")
#obs_list_out = tk.Label(frm_obs_buttons,bg="grey",anchor="w")
btn_gen = tk.Button(frm_final_gen, text="Generate", font=("Calibri",12,"bold"), command=lambda: gen_config(txt_outdir,txt_out_json,img_list_out, obs_list_out,f_type,corr_list,chk_precompute,topo_list_out, brdf_list_out))
#btn_gen.place(relx=.5, rely=.5,anchor= 'e')
btn_gen.grid(row=0,column=1,sticky="wens") #anchor='center',
#btn_gen.place(relx=0.5, rely=0.95, anchor=tk.CENTER)
frm_img_buttons.grid(row=0, column=0, sticky="ew")
frm_obs_buttons.grid(row=0, column=1, sticky="ew")
frm_out_buttons.grid(row=1, columnspan=2, sticky="we")
frm_file_type.grid(row=2,column=0, sticky="ew")
frm_corr_type.grid(row=2,column=1, sticky="ew")
frm_precomp.grid(row=3,columnspan=2, sticky="nsew")
frm_out_json_buttons.grid(row=4, columnspan=2, sticky="ew")
frm_final_gen.grid(row=6, columnspan=2, sticky="nsew") #, sticky="nsew"
window.mainloop()

View File

@ -0,0 +1,117 @@
# -*- 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/>.
JSON format for PLSR trait models v0.1
This version assumes that all transforms are applied using all model wavelengths.
TODO: Develop standard codes for spectrometer, both airborne/spaceborne and field.
TODO: Allow for more options for specifying wavelength subsets
"""
import json
model_dict = {}
# Metadata
#####################################
'''
trait : Trait name (str)
units : Trait units (str)
description: Model description (str)
wavelength_units: Wavelength units (str)
wavelengths : Model wavelengths (list)
Only wavelengths used in the model should be
included in the list of wavelengths.
fwhm : Model fwhm (list)
type : Model type (str)
'''
model_dict["name"] = ''
model_dict["units"] = ''
model_dict["description"] = ''
model_dict["wavelength_units"] = ''
model_dict["wavelengths"] = []
model_dict["fwhm"] = []
model_dict["spectrometer"] = ''
model_dict["type"] = ''
# Diagnostics
#####################################
'''Currently the only required diagnostics are 'min'
and 'max', these are the min and max values of the
dataset used to build the model and are used to generate
the data range mask, which identifies pixels with predictions
outside of the model dataset range.
'''
model_dict["model_diagnostics"] = {}
model_dict["model_diagnostics"]["rmse"] = 0.0
model_dict["model_diagnostics"]["r_squared"] = 0.0
model_dict["model_diagnostics"]["min"] = 0.0
model_dict["model_diagnostics"]["max"] = 0.0
# Model
#####################################
'''
transform: List of transforms to be applied in order of application.
Options:
- 'vector': vector norm using np.linalg.norm
- 'mean' : Normalize to mean
- 'absorb' : log(1/R)
Examples:
['vector','absorb']
Empty list for no transforms ([])
coefficients: List of lists, sublists are the coefficients for
model iterations.
intercepts : Permuted model intercepts (list)
components : Number of model component (int)
'''
model_dict['model'] = {}
model_dict['model']["components"] = 0
model_dict['model']["transform"] = ['mean']
model_dict['model']["intercepts"] = []
model_dict['model']["coefficients"] =[[],[]]
model_path = '*.json'
with open(model_path, 'w') as outfile:
json.dump(model_dict,outfile)

View File

@ -0,0 +1,99 @@
'''Template script for generating trait_estimate configuration JSON files.
'''
import os
import json
import glob
home = os.path.expanduser("~")
#Output path for configuration file
config_file = "/.json"
config_dict = {}
config_dict['file_type'] = 'envi'
config_dict["output_dir"] = './'
config_dict['bad_bands'] =[[300,400],[1337,1430],[1800,1960],[2450,2600]]
# Input data settings for NEON
#################################################################
# config_dict['file_type'] = 'neon'
# images= glob.glob("*.h5")
# images.sort()
# config_dict["input_files"] = images
# Input data settings for ENVI
#################################################################
''' Only difference between ENVI and NEON settings is the specification
of the ancillary datasets (ex. viewing and solar geometry). All hytools
functions assume that the ancillary data and the image date are the same
size, spatially, and are ENVI formatted files.
The ancillary parameter is a dictionary with a key per image. Each value
per image is also a dictionary where the key is the dataset name and the
value is list consisting of the file path and the band number.
'''
config_dict['file_type'] = 'envi'
aviris_anc_names = ['path_length','sensor_az','sensor_zn',
'solar_az', 'solar_zn','phase','slope',
'aspect', 'cosine_i','utc_time']
images= glob.glob("*img")
images.sort()
config_dict["input_files"] = images
config_dict["anc_files"] = {}
anc_files = glob.glob("*ort")
anc_files.sort()
for i,image in enumerate(images):
config_dict["anc_files"][image] = dict(zip(aviris_anc_names,
[[anc_files[i],a] for a in range(len(aviris_anc_names))]))
config_dict['num_cpus'] = len(images)
# Assign correction coefficients
##########################################################
''' Specify correction(s) to apply and paths to coefficients.
'''
config_dict['corrections'] = ['topo','brdf']
topo_files = glob.glob("*topo.json")
topo_files.sort()
config_dict["topo"] = dict(zip(images,topo_files))
brdf_files = glob.glob("*brdf.json")
brdf_files.sort()
config_dict["brdf"] = dict(zip(images,brdf_files))
# Select wavelength resampling type
##########################################################
'''Wavelength resampler will only be used if image wavelengths
and model wavelengths do not match exactly
See image_correct_json_generate.py for options.
'''
config_dict["resampling"] = {}
config_dict["resampling"]['type'] = 'cubic'
# Masks
##########################################################
'''Specify list of masking layers to be appended to the
trait map. Each will be placed in a separate layer.
For no masks provide an empty list: []
'''
config_dict["masks"] = [["ndi", {'band_1': 850,'band_2': 660,
'min': 0.1,'max': 1.0}],
['neon_edge',{'radius': 30}]]
# Define trait coefficients
##########################################################
models = glob.glob('*.json')
models.sort()
config_dict["trait_models"] = models
with open(config_file, 'w') as outfile:
json.dump(config_dict,outfile)