Files
BRDF/Flexbrdf/scripts/brdf_correct_bip.py
2026-04-10 16:46:45 +08:00

138 lines
5.2 KiB
Python

import argparse
import json
import os
import ray
import hytools as ht
from hytools.io.envi import WriteENVI
from hytools.brdf import calc_brdf_coeffs
def build_anc_mapping(anc_file, anc_names):
return dict(zip(anc_names, [[anc_file, i] for i in range(len(anc_names))]))
def build_brdf_config(args):
mask = [["ndi", {"band_1": args.ndi_band_1, "band_2": args.ndi_band_2, "min": args.ndi_min, "max": args.ndi_max}]]
brdf = {
"type": args.brdf_type,
"grouped": False,
"geometric": args.geometric,
"volume": args.volume,
"b/r": args.b_r,
"h/b": args.h_b,
"sample_perc": args.sample_perc,
"calc_mask": mask,
"apply_mask": mask,
"solar_zn_type": args.solar_zn_type
}
if args.brdf_type == "universal":
brdf["diagnostic_plots"] = False
brdf["diagnostic_waves"] = []
if args.brdf_type == "flex":
brdf["interp_kind"] = "linear"
brdf["bin_type"] = "dynamic"
brdf["num_bins"] = args.num_bins
brdf["ndvi_bin_min"] = args.ndvi_bin_min
brdf["ndvi_bin_max"] = args.ndvi_bin_max
brdf["ndvi_perc_min"] = args.ndvi_perc_min
brdf["ndvi_perc_max"] = args.ndvi_perc_max
return brdf
def export_brdf_corrected(hy_obj, args):
header_dict = hy_obj.get_header()
header_dict["data ignore value"] = hy_obj.no_data
header_dict["data type"] = 4
output_name = os.path.join(
args["output_dir"],
f"{os.path.splitext(os.path.basename(hy_obj.file_name))[0]}_{args['suffix']}"
)
writer = WriteENVI(output_name, header_dict)
iterator = hy_obj.iterate(by="line", corrections=hy_obj.corrections)
while not iterator.complete:
line = iterator.read_next()
writer.write_line(line, iterator.current_line)
writer.close()
def export_brdf_coeffs(hy_obj, args):
output_name = os.path.join(
args["output_dir"],
f"{os.path.splitext(os.path.basename(hy_obj.file_name))[0]}_brdf_coeffs_{args['suffix']}.json"
)
with open(output_name, "w") as outfile:
json.dump(hy_obj.brdf, outfile)
def main():
parser = argparse.ArgumentParser(description="BRDF correction for ENVI BIP images")
parser.add_argument("image", type=str)
parser.add_argument("anc_file", type=str)
parser.add_argument("output_dir", type=str)
parser.add_argument("--anc-map", type=str, default="")
parser.add_argument("--anc-names", type=str, default="path_length,sensor_az,sensor_zn,solar_az,solar_zn,phase,slope,aspect,cosine_i,utc_time")
parser.add_argument("--brdf-type", type=str, default="universal", choices=["universal", "flex"])
parser.add_argument("--suffix", type=str, default="brdf")
parser.add_argument("--num-cpus", type=int, default=1)
parser.add_argument("--bad-bands-json", type=str, default="")
parser.add_argument("--solar-zn-type", type=str, default="scene")
parser.add_argument("--geometric", type=str, default="li_dense_r")
parser.add_argument("--volume", type=str, default="ross_thick")
parser.add_argument("--b-r", type=float, default=2.5)
parser.add_argument("--h-b", type=float, default=2.0)
parser.add_argument("--sample-perc", type=float, default=0.1)
parser.add_argument("--ndi-band-1", type=float, default=850.0)
parser.add_argument("--ndi-band-2", type=float, default=660.0)
parser.add_argument("--ndi-min", type=float, default=0.05)
parser.add_argument("--ndi-max", type=float, default=1.0)
parser.add_argument("--num-bins", type=int, default=18)
parser.add_argument("--ndvi-bin-min", type=float, default=0.05)
parser.add_argument("--ndvi-bin-max", type=float, default=1.0)
parser.add_argument("--ndvi-perc-min", type=float, default=10.0)
parser.add_argument("--ndvi-perc-max", type=float, default=95.0)
parser.add_argument("--export-coeffs", action="store_true")
args = parser.parse_args()
os.makedirs(args.output_dir, exist_ok=True)
if args.anc_map:
with open(args.anc_map, "r") as infile:
anc_map = json.load(infile)
else:
anc_names = [x.strip() for x in args.anc_names.split(",") if x.strip()]
anc_map = build_anc_mapping(args.anc_file, anc_names)
config_dict = {
"file_type": "envi",
"input_files": [args.image],
"anc_files": {args.image: anc_map},
"corrections": ["brdf"],
"brdf": build_brdf_config(args),
"num_cpus": args.num_cpus
}
if args.bad_bands_json:
config_dict["bad_bands"] = json.loads(args.bad_bands_json)
if ray.is_initialized():
ray.shutdown()
ray.init(num_cpus=config_dict["num_cpus"])
HyTools = ray.remote(ht.HyTools)
actor = HyTools.remote()
ray.get(actor.read_file.remote(args.image, "envi", anc_map))
if "bad_bands" in config_dict:
ray.get(actor.create_bad_bands.remote(config_dict["bad_bands"]))
calc_brdf_coeffs([actor], config_dict)
ray.get(actor.do.remote(export_brdf_corrected, {"output_dir": args.output_dir, "suffix": args.suffix}))
if args.export_coeffs:
ray.get(actor.do.remote(export_brdf_coeffs, {"output_dir": args.output_dir, "suffix": args.suffix}))
ray.shutdown()
if __name__ == "__main__":
main()