microproduct/soilMoisture_geo_sar/SoilMoistureMain.py

501 lines
22 KiB
Python
Raw Normal View History

2023-08-28 10:17:29 +00:00
# -*- coding: UTF-8 -*-
"""
@Project microproduct
@File SoilMoistureMain.py
@Author SHJ
@Contact
@Date 2021/7/26 17:11
@Version 1.0.0
"""
import logging
import os
import datetime
import sys
import numpy as np
from PIL import Image
import multiprocessing
from scipy.optimize import minimize, fmin
from tool.algorithm.algtools.PreProcess import PreProcess as pp
from tool.algorithm.image.ImageHandle import ImageHandler
from tool.algorithm.xml.AlgXmlHandle import ManageAlgXML, CheckSource,InitPara
from tool.algorithm.algtools.logHandler import LogHandler
from tool.algorithm.block.blockprocess import BlockProcess
from tool.config.ConfigeHandle import Config as cf
from tool.algorithm.xml.CreatMetafile import CreateMetafile
from tool.algorithm.algtools.ROIAlg import ROIAlg as roi
from SoilMoistureXmlInfo import CreateDict, CreateStadardXmlFile
from tool.file.fileHandle import fileHandle
from tool.algorithm.algtools.MetaDataHandler import MetaDataHandler
from tool.algorithm.algtools.filter.lee_Filter import Filter
import math
multiprocessing_num = int(cf.get('multiprocessing_num'))
if cf.get('debug') == 'True':
DEBUG = True
else:
DEBUG = False
EXE_NAME = cf.get('exe_name')
FILTER_SIZE = int(cf.get('filter_size'))
soil_moisture_value_min = float(cf.get('product_value_min'))
soil_moisture_value_max = float(cf.get('product_value_max'))
LogHandler.init_log_handler('run_log\\' + EXE_NAME)
logger = logging.getLogger("mylog")
env_str = os.path.split(os.path.realpath(__file__))[0]
os.environ['PROJ_LIB'] = env_str
file =fileHandle(DEBUG)
class MoistureMain:
"""
土壤水分处理主函数
"""
def __init__(self, alg_xml_path):
self.alg_xml_path = alg_xml_path
self.imageHandler = ImageHandler()
self.__alg_xml_handler = ManageAlgXML(alg_xml_path)
self.__check_handler = CheckSource(self.__alg_xml_handler)
self.__workspace_path = None
self.__input_paras = {}
self.__out_para = None
self.__processing_paras = {}
self.__preprocessed_paras = {}
# 参考影像路径
self.__ref_img_path = ''
# 宽/列数,高/行数
self.__cols, self.__rows = 0, 0
# 坐标系
self.__proj = ''
# 影像投影变换矩阵
self.__geo = [0, 0, 0, 0, 0, 0]
# 极化影像的名称
self.__polar_name = None
# 多时相影像名称
self.__sar_names_list = []
def check_source(self):
"""
检查算法相关的配置文件图像辅助文件是否齐全
"""
env_str = os.getcwd()
logger.info("sysdir: %s", env_str)
self.__check_handler.check_alg_xml()
self.__check_handler.check_run_env()
self.__input_paras = self.__alg_xml_handler.get_input_paras()
if self.__check_handler.check_input_paras(self.__input_paras) is False:
return False
self.__workspace_path = self.__alg_xml_handler.get_workspace_path()
self.__create_work_space()
self.__out_para = os.path.join(self.__workspace_path, EXE_NAME, 'Output', r"SoilMoistureProduct.tar.gz")
self.__alg_xml_handler.write_out_para("SoilMoistureProduct", self.__out_para) #写入输出参数
Polarization = self.__input_paras['Polarization']['ParaValue']
if Polarization == "HH":
self.__polar_name = "HH"
elif Polarization == "VV":
self.__polar_name = "VV"
elif Polarization == 'empty' or Polarization == 'Empty' or Polarization == 'EMPTY':
self.__polar_name = 'empty'
else:
raise ValueError("input Para 'Polarization' is not 'HH''VV'or 'empty'!")
self.__processing_paras = InitPara.init_processing_paras(self.__input_paras, self.__workspace_preprocessed_path)
2023-08-28 10:17:29 +00:00
self.__processing_paras.update(InitPara(DEBUG).get_mult_tar_gz_infs(self.__processing_paras, self.__workspace_preprocessing_path))
self.__sar_names_list = self.__processing_paras['name_list']
if len(self.__sar_names_list) != 2:
raise ValueError('number of SingleTempSAR(input para) != 2 !')
logger.info('check_source success!')
logger.info('progress bar: 10%')
return True
def __create_work_space(self):
"""
删除原有工作区文件夹,创建新工作区文件夹
"""
self.__workspace_preprocessing_path = self.__workspace_path + EXE_NAME + r'\Temporary\preprocessing''\\'
self.__workspace_preprocessed_path = self.__workspace_path + EXE_NAME + r'\Temporary\preprocessed''\\'
self.__workspace_processing_path = self.__workspace_path + EXE_NAME + r'\Temporary\processing''\\'
self.__workspace_block_tif_path = self.__workspace_path + EXE_NAME + r'\Temporary\blockTif''\\'
self.__workspace_block_tif_processed_path = self.__workspace_path + EXE_NAME + r'\Temporary\blockTifProcessed''\\'
self.__product_dic = self.__workspace_processing_path + 'product\\'
path_list = [self.__workspace_preprocessing_path, self.__workspace_preprocessed_path,
self.__workspace_processing_path, self.__workspace_block_tif_path,
self.__workspace_block_tif_processed_path, self.__product_dic]
file.creat_dirs(path_list)
logger.info('create new workspace success!')
def del_temp_workspace(self):
"""
临时工作区
"""
if DEBUG is True:
return
path = self.__workspace_path + EXE_NAME + r'\Temporary'
if os.path.exists(path):
file.del_folder(path)
def preprocess_handle(self):
"""
预处理
"""
# 读取每一张图像,检查图像坐标系
para_names = ['Covering', 'NDVI']
ref_img_name =''
for name in self.__sar_names_list:
para_names.append(name+'_'+self.__polar_name) # todo 名称根据LT04实际命名进行缩短
2023-08-28 10:17:29 +00:00
if ref_img_name == '':
ref_img_name = name+'_'+self.__polar_name
para_names.append(name+'_LocalIncidenceAngle')
p = pp()
self.__preprocessed_paras = p.preprocessing(para_names, ref_img_name,self.__processing_paras,
self.__workspace_preprocessing_path, self.__workspace_preprocessed_path)
self.__ref_img_path, self.__cols, self.__rows, self.__proj, self.__geo = p.get_ref_inf(self.__preprocessed_paras[ref_img_name])
self.__ref_img_name = ref_img_name
logger.info('progress bar: 40%')
logger.info('preprocess_handle success!')
def create_roi(self):
"""
计算ROI掩膜
:return: 掩膜路径
"""
names = [self.__sar_names_list[0] + '_' +'LocalIncidenceAngle', self.__ref_img_name, 'Covering', 'NDVI']
r = roi()
bare_land_mask_path = r.roi_process(names, self.__workspace_processing_path + "/roi/", self.__processing_paras, self.__preprocessed_paras)
logger.info('create masks success!')
# 计算roi区域
for name in self.__sar_names_list:
# roi区域掩盖无效区域
roi.cal_roi(self.__preprocessed_paras[name+'_'+'LocalIncidenceAngle'],self.__preprocessed_paras[name+'_'+'LocalIncidenceAngle'],
bare_land_mask_path, background_value=1)
logger.info('create ROI image success!')
return bare_land_mask_path
def cal_bare_soil_moisure(self,moisure1_path,moisure2_path, sar1_path, angle1_path,sar2_path, angle2_path ,mask_path,x_min,x_max,f, T, bd, vsand, vclay,block_num=0,x0=0.3):
"""
Dobsen模型土壤水分反演
:param f:微波频率单位GHz
:param T:温度摄氏度
:param bd:土壤容重
:param vsand:沙土含量范围0-1
:param vclay:黏土含量范围0-1
:param soil_dielectric_array:土壤介电常数
:param x0 :土壤水分寻优初始值
:return: True or False
"""
alpha = 0.65
sd = 2.65
dcs = (1.01 + 0.44 * sd) ** 2 - 0.062
dc0 = 0.008854
dcw0 = 88.045 - 0.4147 * T + 6.295e-4 * (T ** 2) + 1.075e-5 * (T ** 3)
tpt = 0.11109 - 3.824e-3 * T + 6.938e-5 * (T ** 2) - 5.096e-7 * (T ** 3)
dcwinf = 4.9
if f >= 1.4:
sigma = -1.645 + 1.939 * bd - 2.013e-2 * vsand + 1.594e-2 * vclay
else:
sigma = 0.0467 + 0.2204 * bd - 4.111e-3 * vsand + 6.614e-3 * vclay
dcwr = dcwinf + ((dcw0 - dcwinf) / (1 + (tpt * f) ** 2))
betar = 1.2748 - 0.00519 * vsand - 0.00152 * vclay
betai = 1.33797 - 0.00603 * vsand - 0.00166 * vclay
fun_mois = lambda vwc: np.abs(np.sqrt(
((1.0 + (bd / sd) * ((dcs ** alpha) - 1.0) + (vwc ** betar) * (dcwr ** alpha) - vwc) ** (1 / alpha)) ** 2 +
((vwc ** (betai / alpha)) * (
(tpt * f * (dcw0 - dcwinf)) / (1 + (tpt * f) ** 2) + sigma * (1.0 - (bd / sd)) / (
8 * math.atan(1.0) * dc0 * f * vwc))) ** 2
) - soil_dielectric)
# 计算土壤水分
sar1 = self.imageHandler.get_data(sar1_path)
angle1 = self.imageHandler.get_data(angle1_path)
sar2 = self.imageHandler.get_data(sar2_path)
angle2 = self.imageHandler.get_data(angle2_path)
mask = self.imageHandler.get_data(mask_path)
sin_angle1 = np.sin(angle1) ** 2
cos_angle1 = np.cos(angle1)
sin_angle2 = np.sin(angle2) ** 2
cos_angle2 = np.cos(angle2)
tmp = (sar2 / sar1) ** 0.5
w = np.where(mask > 0)
sin_angle1 = sin_angle1[w]
cos_angle1 = cos_angle1[w]
sin_angle2 = sin_angle2[w]
cos_angle2 = cos_angle2[w]
tmp = tmp[w]
num = len(w[0])
mois1, mois2, fun_value = np.zeros(num), np.zeros(num), np.zeros(num)
if self.__polar_name == 'HH':
fun = lambda x: np.abs(
np.abs((c2 - (x[1] - s2) ** 0.5) / (c2 + (x[1] - s2) ** 0.5))
- t * np.abs((c1 - (x[0] - s1) ** 0.5) / (c1 + (x[0] - s1) ** 0.5))
)
elif self.__polar_name == 'VV':
fun = lambda x: np.abs(
np.abs((x[1] - 1) * (s2 - x[1] * (1 + s2)) / (x[1] * c2 + (x[1] - s2) ** 0.5) ** 2)
- t * np.abs((x[0] - 1) * (s1 - x[0] * (1 + s1)) / (x[0] * c1 + (x[0] - s1) ** 0.5) ** 2)
)
x_mid = 0.5 * (x_max - x_min)
bnds = ((x_min, x_max), (x_min, x_max))
start = datetime.datetime.now()
bar_list = [0, int(0.1 * num), int(0.2 * num), int(0.3 * num), int(0.4 * num), int(0.5 * num), int(0.6 * num),
int(0.7 * num), int(0.8 * num), int(0.9 * num), num - 1]
for s1, c1, s2, c2, t, n in zip(sin_angle1, cos_angle1, sin_angle2, cos_angle2, tmp, range(num)):
# 计算介电常数
res = minimize(fun, (x_mid, x_mid), method='SLSQP', bounds=bnds)
# print(s1, c1, s2, c2, t, n)
# print("res::", res)
if res.fun < 0.000001:
# 计算时相1的土壤水分
soil_dielectric = res.x[0]
mois1[n] = fmin(fun_mois, x0, disp=0) # Dobson模型
# 计算时相2的土壤水分
soil_dielectric = res.x[1]
mois2[n] = fmin(fun_mois, x0, disp=0) # Dobson模型
if n in bar_list:
logger.info('block:{},cal soil_moisture proggress bar:{}%,use time :{}'.format(block_num, round(n / num * 100), (datetime.datetime.now() - start)))
sar1[:] = 0
sar2[:] = 0
sar1[w] = mois1 # 土壤水分
sar2[w] = mois2 # 土壤水分
# 保存土壤水分
out_image = Image.fromarray(sar1)
out_image.save(moisure1_path)
out_image = Image.fromarray(sar2)
out_image.save(moisure2_path)
'''
f: 频率通常以赫兹Hz为单位它可能与土壤中电磁波的传播有关
T: 温度通常以摄氏度°C为单位它影响土壤水分的介电常数
er: 测量的土壤介电常数这是一个无量纲的量它表示土壤对电磁波的响应
bd: 土壤的干容重Bulk Density通常以克/立方厘米g/cm³为单位它表示单位体积土壤中固体的质量
vsand: 砂土体积比例这是一个介于0和1之间的无量纲量表示土壤中砂土的体积占比
vclay: 黏土体积比例这也是一个介于0和1之间的无量纲量表示土壤中黏土的体积占比
x0: 初始猜测值用于数值优化算法的起始点通常是一个介于0和1之间的量表示体积含水量Volume Water Content的初始估计
'''
def dobsen_inverse(self, f, T, er, bd, vsand, vclay, x0):
# 设置模型参数
alpha = 0.65
sd = 2.65
dcs = (1.01 + 0.44 * sd) ** 2 - 0.062
dc0 = 0.008854
dcw0 = 88.045 - 0.4147 * T + 6.295e-4 * T ** 2 + 1.075e-5 * T ** 3
tpt = 0.11109 - 3.824e-3 * T + 6.938e-5 * T ** 2 - 5.096e-7 * T ** 3
dcwinf = 4.9
# 计算土壤的电导率 sigma
if f >= 1.4:
sigma = -1.645 + 1.939 * bd - 0.0225622 * vsand + 0.01594 * vclay
else:
sigma = 0.0467 + 0.2204 * bd - 0.004111 * vsand + 0.006614 * vclay
# 计算水分相关土壤颗粒的直径 dcwr
dcwr = dcwinf + ((dcw0 - dcwinf) / (1 + (tpt * f) ** 2))
# 计算与土壤水分保持能力相关的参数 betar 和 betai
betar = 1.2748 - 0.00519 * vsand - 0.00152 * vclay
betai = 1.33797 - 0.00603 * vsand - 0.00166 * vclay
# 定义目标函数
def objective(vwc):
return np.abs(np.sqrt(
((1.0 + (bd / sd) * ((dcs ** alpha) - 1.0) + (vwc ** betar) * (dcwr ** alpha) - vwc) ** (
1 / alpha)) ** 2 +
((vwc ** (betai / alpha)) * (
(tpt * f * (dcw0 - dcwinf)) / (1 + (tpt * f) ** 2) + sigma * (1.0 - (bd / sd)) /
(8 * math.atan(1.0) * dc0 * f * vwc))) ** 2
) - er)
# 使用数值优化方法找到最佳的 vwc 值
result = minimize(objective, x0, method='Nelder-Mead')
# 返回最优解
return result.x[0]
2023-08-28 10:17:29 +00:00
def process_handle(self, start):
"""
算法主处理函数
:return: True or False
"""
# 计算ROI区域
bare_land_mask_path = self.create_roi()
radar_center_frequency = MetaDataHandler.get_RadarCenterFrequency(self.__processing_paras[self.__sar_names_list[0] + '_Origin_META'])
2023-08-28 10:17:29 +00:00
file.copyfile2dir(bare_land_mask_path, self.__workspace_preprocessed_path)
logger.info('progress bar: 50%')
for name in self.__sar_names_list:
file_key = name + '_' + self.__polar_name
if os.path.exists(self.__preprocessed_paras[file_key]):
lee_path = os.path.join(self.__workspace_preprocessed_path,
os.path.basename(self.__preprocessed_paras[file_key]).split("-")[0] + '_lee.tif')
Filter().lee_process_sar(self.__preprocessed_paras[file_key], lee_path, 3, 0.25)
logger.info('lee process finish: ' + self.__preprocessed_paras[file_key])
os.remove(self.__preprocessed_paras[file_key])
self.__preprocessed_paras.update({file_key: lee_path})
2023-08-28 10:17:29 +00:00
# 分块
bp = BlockProcess()
block_size = bp.get_block_size(self.__rows, self.__cols)
bp.cut(self.__workspace_preprocessed_path, self.__workspace_block_tif_path, ['tif', 'tiff'], 'tif', block_size)
logger.info('blocking tifs success!')
img_dir, img_name = bp.get_file_names(self.__workspace_block_tif_path, ['tif'])
dir_dict = bp.get_same_img(img_dir, img_name)
for key in dir_dict.keys():
tmp = key
if self.__sar_names_list[0]+'_LocalIncidenceAngle' in tmp:
angle1_list = dir_dict[key]
elif self.__sar_names_list[0].split('-')[0]+'_lee' in tmp:
2023-08-28 10:17:29 +00:00
sar1_list = dir_dict[key]
elif self.__sar_names_list[1] + '_LocalIncidenceAngle' in tmp:
angle2_list = dir_dict[key]
elif self.__sar_names_list[1].split('-')[0]+'_lee' in tmp:
2023-08-28 10:17:29 +00:00
sar2_list = dir_dict[key]
elif 'bare_land_mask'in tmp :
bare_land_mask_list = dir_dict[key]
if not len(angle1_list) == len(sar1_list) == len(angle2_list) == len(sar2_list) == len(bare_land_mask_list):
raise Exception('[len(angle1_list) == len(sar1_list) == len(angle2_list) == len(sar2_list) == len(bare_land_mask_list] is False!')
# 开启多进程处理
processes_num = min([len(angle1_list), multiprocessing_num])
pool = multiprocessing.Pool(processes=processes_num)
for i in range(len(angle1_list)):
name = os.path.basename(angle1_list[i])
suffix = '_' + name.split('_')[-4] + '_' + name.split('_')[-3] + '_' + name.split('_')[-2] + '_' + \
name.split('_')[-1]
# 计算土壤介电常数
moisture1_path = self.__workspace_block_tif_processed_path+self.__sar_names_list[0]+'_moisture' + suffix
moisture2_path = self.__workspace_block_tif_processed_path+self.__sar_names_list[1]+'_moisture' + suffix
sar1_path = sar1_list[i]
angle1_path = angle1_list[i]
sar2_path = sar2_list[i]
angle2_path = angle2_list[i]
mask_path = bare_land_mask_list[i]
x_min = self.__processing_paras['SoilCDCMin']
x_max = self.__processing_paras['SoilCDCMax']
logger.info('total:%s, block:%s cal soil_moisture success!', len(angle1_list), i)
pool.apply_async(self.cal_bare_soil_moisure, (moisture1_path, moisture2_path, sar1_path, angle1_path, sar2_path, angle2_path, mask_path, x_min, x_max, radar_center_frequency, self.__processing_paras['T'],
self.__processing_paras['Pb'], self.__processing_paras['vsand'], self.__processing_paras['vclay'], i))
pool.close()
pool.join()
logger.info('progress bar: 80%')
# 合并处理后的影像
data_dir = self.__workspace_block_tif_processed_path
w = self.__cols
h = self.__rows
out_path = self.__workspace_processing_path[0:-1]
tif_type = np.float32
bp.combine(data_dir, w, h, out_path, file_type=['tif'], datetype=tif_type)
# 添加地理信息
soil_moisture1_path = self.__workspace_processing_path + self.__sar_names_list[0]+'_moisture'+'.tif'
soil_moisture2_path = self.__workspace_processing_path + self.__sar_names_list[1]+'_moisture'+'.tif'
bp.assign_spatial_reference_byfile(self.__preprocessed_paras[self.__sar_names_list[0]+'_'+self.__polar_name], soil_moisture1_path)
bp.assign_spatial_reference_byfile(self.__preprocessed_paras[self.__sar_names_list[0]+'_'+self.__polar_name], soil_moisture2_path)
# 生成roi区域
product1_path = self.__product_dic + self.__sar_names_list[0] + '_SoilMoistureProduct.tif'
roi.cal_roi(product1_path, soil_moisture1_path, bare_land_mask_path, background_value=np.nan)
product2_path = self.__product_dic + self.__sar_names_list[1] + '_SoilMoistureProduct.tif'
roi.cal_roi(product2_path, soil_moisture2_path, bare_land_mask_path, background_value=np.nan)
logger.info('cal soil_moisture success!')
for path in [product1_path, product2_path]:
proj, geos, data = self.imageHandler.read_img(path)
data[data < soil_moisture_value_min] = soil_moisture_value_min
data[data > soil_moisture_value_max] = soil_moisture_value_max
self.imageHandler.write_img(path, proj, geos, data)
# 生成快视图
self.imageHandler.write_quick_view(product1_path)
self.imageHandler.write_quick_view(product2_path)
# 生成元文件案例
xml_path = "./model_meta.xml"
tem_folder = self.__workspace_path + EXE_NAME + r"\Temporary""\\"
image_path = product1_path
out_path1 = os.path.join(tem_folder, "trans_geo_projcs.tif")
out_path2 = os.path.join(tem_folder, "trans_projcs_geo.tif")
par_dict = CreateDict(image_path, out_path1, out_path2).calu_nature(start)
model_xml_path = os.path.join(tem_folder, "creat_standard.meta.xml") # 输出xml路径
id_min = 0
id_max = 1000
threshold_of_ndvi_min = 0
threshold_of_ndvi_max = 1
set_threshold = [id_max, id_min, threshold_of_ndvi_min, threshold_of_ndvi_max]
CreateStadardXmlFile(xml_path, self.alg_xml_path, par_dict, set_threshold, model_xml_path).create_standard_xml()
SrcImagePath = self.__input_paras["SingleTempSAR"]['ParaValue']
paths = SrcImagePath.split(';')
SrcImageName=os.path.split(paths[0])[1].split('.tar.gz')[0]
if len(paths) >= 2:
for i in range(1, len(paths)):
SrcImageName=SrcImageName+";"+os.path.split(paths[i])[1].split('.tar.gz')[0]
meta_xml_path = self.__product_dic+EXE_NAME+"Product.meta.xml"
CreateMetafile(self.__processing_paras['META'], self.alg_xml_path, model_xml_path, meta_xml_path).process(SrcImageName)
# 文件夹打包
file.make_targz(self.__out_para, self.__product_dic)
logger.info('process_handle success!')
logger.info('progress bar: 100%')
if __name__ == '__main__':
multiprocessing.freeze_support()
start = datetime.datetime.now()
try:
if len(sys.argv) < 2:
xml_path = 'SoilMoisture_geo.xml'
else:
xml_path = sys.argv[1]
main_handler = MoistureMain(xml_path)
if main_handler.check_source() is False:
raise Exception('check_source() failed!')
if main_handler.preprocess_handle() is False:
raise Exception('preprocess_handle() failed!')
if main_handler.process_handle(start) is False:
raise Exception('process_handle() failed!')
logger.info('successful production of ' + EXE_NAME + ' products!')
except Exception:
logger.exception('run-time error!')
finally:
main_handler.del_temp_workspace()
end = datetime.datetime.now()
msg = 'running use time: %s ' % (end - start)
logger.info(msg)