从高光谱遥感图像,用DOAS算法反演甲烷浓度,用python写出详细代码
时间: 2025-08-03 12:05:25 浏览: 2
<think>我们正在处理一个涉及DOAS算法和高光谱遥感图像的问题。用户要求提供使用Python实现DOAS算法来从高光谱遥感图像中反演甲烷浓度的详细代码。请注意,DOAS算法通常应用于吸收光谱,而高光谱遥感图像包含多个波段,每个波段对应于不同波长的反射或辐射值。根据引用[1],DOAS的基本原理是通过分析气体在特定波长的吸收特征来定量气体浓度。对于甲烷,我们关注其在特定波段的吸收特性。然而,需要指出的是,DOAS算法在遥感中应用时,通常需要以下步骤:1.获取高光谱图像数据(通常为立方体数据,两个空间维度和一个光谱维度)。2.选择包含目标气体(甲烷)吸收特征的波段范围。3.进行必要的预处理,如辐射定标、大气校正等(这些步骤通常由用户提前完成)。4.利用参考光谱(通常来自实验室测量或标准数据库)来拟合观测光谱,以去除其他气体和背景的影响,并突出目标气体的吸收特征。5.根据比尔-朗伯定律计算气体浓度。我们将编写一个简化的DOAS算法实现,用于从高光谱数据中反演甲烷浓度。这里假设我们已经有了高光谱数据(假设为NumPy数组,形状为[height,width,bands]),以及甲烷的标准吸收光谱(形状为[bands,])。重要注意:实际应用中,DOAS算法可能更复杂,包括多种校正和拟合技术(例如,多项式拟合去除慢变背景,同时拟合多种气体等)。这里我们仅提供一个基础框架。步骤:1.读取高光谱数据(这里我们假设已经读取为`hyperspectral_data`)和甲烷参考吸收光谱(`methane_absorption`)。2.为了简化,我们假设高光谱数据已经进行了大气校正,并且是反射率或辐亮度数据(实际DOAS通常使用辐射传输模型或直接使用太阳辐射衰减,但在遥感中可能是反射率)。3.DOAS的核心是差分吸收,即计算在吸收峰附近的吸收深度。我们采用最小二乘拟合来估计浓度。算法思路:设观测光谱为$I(\lambda)$,参考吸收截面为$\sigma(\lambda)$(甲烷的)。我们假设在窄波段范围内,其他气体的吸收和散射影响可以通过一个低阶多项式$P(\lambda)$来近似。因此,模型为:$$\ln(I(\lambda)/I_0(\lambda))=c\cdot\sigma(\lambda)+P(\lambda)$$其中,$I_0(\lambda)$是参考光谱(例如,无吸收时的光谱,在大气顶的太阳光谱或场景中无气体区域的光谱),但在遥感中处理方式不同。为了简化,我们可以使用场景中某个无气体区域的平均光谱作为$I_0$,或者对于星载高光谱,可以使用大气顶的太阳光谱。在缺少$I_0$的情况下,我们可以使用相对DOAS方法:即对观测光谱进行归一化(例如,除以观测光谱的均值或平滑值),然后取对数。或者采用差分吸收方式,直接对光谱进行多项式拟合以消除低频部分。这里我们采用相对DOAS方式,并使用多项式拟合去除慢变化部分。具体步骤:-定义观测光谱:每个像元的光谱向量$y$(已经取自然对数:`ln_y=np.log(y)`)。-构建设计矩阵:第一列是甲烷吸收光谱,后面是多项式项(例如:常数项、$\lambda$、$\lambda^2$等)。-使用线性最小二乘拟合,拟合系数。-甲烷的浓度与拟合的甲烷吸收光谱的系数成正比(需要根据光程等进行转换,但这里我们假设比例系数为1,实际上需要标定)。注意:由于实际应用中需要精确标定,这里我们只给出反演相对浓度的图像。接下来,我们给出代码框架:假设:-高光谱数据形状为(height,width,bands)-波长数组:wavelengths(长度为bands)-甲烷吸收光谱:methane_abs(长度为bands,与wavelengths对应)由于高光谱数据可能很大,我们逐像元处理可能会很慢,因此我们将向量化处理。步骤:1.对每个像元的光谱取对数:`log_spec=np.log(spec)`,但注意:如果数据中有零值,取对数会出错,所以需要确保数据是正值。2.为了去除低频分量,我们构建一个设计矩阵,其中包括甲烷吸收光谱和多项式项(例如1,λ,λ^2,...)。3.对每个像元,使用最小二乘法拟合:拟合系数=(设计矩阵^T*设计矩阵)^{-1}*设计矩阵^T*log_spec4.设计矩阵的设计:设多项式阶数为n(例如2阶或3阶)。设计矩阵A的列为:-第一列:甲烷吸收光谱-第二列:常数项(全1)-第三列:波长-第四列:波长的平方(如果n>=2)-...(注意:为了避免多重共线性,通常对波长进行归一化,例如减去均值)5.我们感兴趣的是第一列的系数,这个系数与甲烷浓度成正比。由于需要处理整个图像,我们可以将高光谱数据重塑为2D矩阵(像元数×波段),然后对整个矩阵进行最小二乘拟合(通过矩阵运算一次性拟合所有像元)。但是设计矩阵是相同的,因此可以使用线性代数方法加速。设:-Y:观测光谱对数,形状为(height*width,bands)-A:设计矩阵,形状为(bands,n_terms+1)[包括甲烷吸收光谱和多项式项]拟合系数矩阵:X=(A^T*A)^{-1}*A^T*Y^T,然后X的形状为(n_terms+1,height*width)然后,我们取第一行(对应甲烷的系数),并重塑为(height,width),这就是甲烷的浓度分布图。注意:实际DOAS中,我们通常只使用一部分波段(甲烷有强烈吸收的波段)。因此,我们需要选择波段范围。假设我们有一个波段选择:例如从索引start_band到end_band。代码实现步骤:1.选择波段子集(例如,选择甲烷吸收峰所在的波段范围)。2.构建设计矩阵A:在选定的波段范围内。-第一列:甲烷吸收光谱在该波段范围内的值(需要归一化,因为吸收截面的大小与单位有关,但归一化可以在标定时处理,这里假设已经归一化)。-其他列:多项式基。注意:多项式基的构建应使用归一化的波长(避免数值问题)。代码中,我们将波长进行归一化:`wavelength_norm=(wavelengths-mean_wavelength)/std_wavelength`。由于这是简化版,我们假设波长已归一化。下面给出Python代码框架:注意:由于实际的高光谱数据读取和预处理步骤复杂,这里我们只关注核心反演部分。我们假设已经加载了以下数据:hyperspectral_image:高光谱图像,形状为(height,width,bands)wavelengths:波段对应的波长,形状为(bands,)methane_abs:甲烷吸收光谱,形状为(bands,)步骤:1.选择波段范围:确定甲烷有吸收的波段索引。这里假设我们通过先验知识知道波段范围,例如在1600-1700nm(但甲烷的主要吸收带在近红外,如1650nm附近)。注意:实际波长范围需根据数据确定。2.创建掩模,选择在这个范围内的波段。3.对每个像元,在选定的波段上应用DOAS拟合。代码:"""importnumpyasnpimportmatplotlib.pyplotasplt#假设已经加载了以下数据#hyperspectral_image:形状[height,width,bands]#wavelengths:形状[bands]#methane_abs:形状[bands]#1.选择波段范围start_wavelength=1600#起始波长,单位nm,根据实际数据调整end_wavelength=1700#结束波长#获取在选定波长范围内的波段索引band_mask=(wavelengths>=start_wavelength)&(wavelengths<=end_wavelength)selected_wavelengths=wavelengths[band_mask]selected_abs=methane_abs[band_mask]#我们需要使用的波段数n_bands=np.sum(band_mask)ifn_bands==0:raiseValueError("没有找到在指定波长范围内的波段!")#2.提取选定波段的高光谱数据#原始数据形状[height,width,bands]->提取波段后[height,width,n_bands]selected_image=hyperspectral_image[:,:,band_mask]height,width,n_bands=selected_image.shape#3.对选定波段的光谱取对数(注意:数据必须大于0,否则加一个小常数或进行平滑处理)#如果有零或负值,我们添加一个小的偏移量log_image=np.log(selected_image+1e-10)#避免零#4.归一化波长:将选定的波长归一化到零均值和单位方差wavelength_mean=np.mean(selected_wavelengths)wavelength_std=np.std(selected_wavelengths)norm_wavelengths=(selected_wavelengths-wavelength_mean)/wavelength_std#5.设置多项式阶数(例如2阶多项式,则有3个多项式项:常数,λ,λ^2)poly_order=2#6.构建设计矩阵A,形状为[n_bands,n_terms]#列:第一列是甲烷吸收光谱;然后是多项式项n_terms=1+poly_order+1#1(甲烷)+(poly_order+1)个多项式项A=np.zeros((n_bands,n_terms))#第一列:甲烷吸收光谱(归一化?这里我们使用原始吸收光谱,因为后续的常数项和多项式会吸收尺度)A[:,0]=selected_abs#多项式项:从0阶到poly_order阶foriinrange(poly_order+1):A[:,i+1]=norm_wavelengths**i#注意:0阶是常数项#7.将log_image重塑为2D矩阵[height*width,n_bands]log_flat=log_image.reshape((-1,n_bands))#[n_pixels,n_bands]#8.使用最小二乘拟合:对每个像元,求解系数向量x,使得Ax^T≈log_flat[i]^T#注意:我们求解的是最小二乘问题:min||Ax-y||^2,其中y是log_flat的每一行#利用正规方程:x=(A^TA)^{-1}A^Ty^T#先计算A^TAATA=np.dot(A.T,A)#求逆(注意:可能奇异,因此加一个小的正则化项)ATA_inv=np.linalg.inv(ATA+1e-10*np.eye(n_terms))#再计算A^Ty^T:结果是[n_terms,n_pixels]ATy=np.dot(A.T,log_flat.T)#[n_terms,n_pixels]#系数矩阵:X=[n_terms,n_pixels]coeffs=np.dot(ATA_inv,ATy)#形状[n_terms,n_pixels]#9.取第一个系数(对应甲烷)作为浓度相对值methane_coeff=coeffs[0,:]#10.将一维向量重塑为2D图像methane_map=methane_coeff.reshape((height,width))#11.显示结果plt.figure()plt.imshow(methane_map,cmap='jet')plt.colorbar()plt.title('MethaneConcentrationMap')plt.show()"""请注意:1.此代码仅用于演示目的,实际应用需要更严格的物理模型和标定。2.多项式阶数选择需要试验,通常2阶或3阶。3.波段选择至关重要,应选择甲烷有强吸收且其他气体干扰小的波段。4.实际中可能需要同时拟合多种气体和环状效应。5.代码中假设数据已经进行了辐射定标和大气校正(去除大气影响)。如果数据未经处理,则需要预处理步骤。由于用户需求是详细代码,我们给出了一个完整的框架,但实际应用中还需要根据数据调整。相关问题:1.DOAS算法在高光谱遥感中的优缺点?2.如何对高光谱数据进行大气校正?3.还有哪些方法可以反演甲烷浓度?</think>
以下是使用Python实现DOAS算法从高光谱遥感图像反演甲烷浓度的详细代码。实现基于比尔-朗伯定律和差分吸收光谱原理,包含完整的预处理、光谱拟合和浓度计算流程。
```python
import numpy as np
import pandas as pd
import spectral as spy
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.interpolate import interp1d
# =====================================================================
# 1. 数据准备
# =====================================================================
def load_hyperspectral_data(file_path):
"""加载高光谱遥感图像数据"""
img = spy.open_image(file_path)
img_data = img.load()
wavelengths = np.array(img.bands.centers) # 获取波段波长(nm)
return img_data, wavelengths
# =====================================================================
# 2. 甲烷参考光谱准备 (来自HITRAN数据库)
# =====================================================================
def load_methane_reference():
"""加载甲烷标准吸收截面光谱"""
# 示例数据格式: 第一列波长(nm), 第二列吸收截面(cm²/molecule)
ref_data = pd.read_csv('methane_reference.csv')
return ref_data['wavelength'].values, ref_data['absorption'].values
# =====================================================================
# 3. DOAS核心算法
# =====================================================================
def doas_fit(observed_spectrum, wavelengths, ref_wavelengths, ref_absorption):
"""
执行DOAS光谱拟合
公式: ln(I₀/I) = σ(λ)·c·L + P(λ)
其中:
I₀: 参考辐射强度
I: 观测辐射强度
σ: 吸收截面
c: 气体浓度
L: 光程长度
P(λ): 多项式拟合背景
"""
# 将参考光谱插值到观测波段
f = interp1d(ref_wavelengths, ref_absorption, bounds_error=False, fill_value=0)
ref_interp = f(wavelengths)
# 移除无效值
valid_idx = ~np.isnan(observed_spectrum) & (observed_spectrum > 0)
wl_valid = wavelengths[valid_idx]
obs_valid = observed_spectrum[valid_idx]
ref_valid = ref_interp[valid_idx]
# 定义拟合模型
def model(wl, conc, a0, a1, a2):
return conc * ref_valid + a0 + a1*wl + a2*(wl**2)
# 初始参数猜测
guess = [1e15, 0, 0, 0] # [浓度, 多项式系数]
# 执行最小二乘拟合
popt, pcov = curve_fit(
model,
wl_valid,
np.log(obs_valid),
p0=guess
)
# 返回浓度和拟合参数
return popt[0], popt[1:], np.sqrt(np.diag(pcov))[0]
# =====================================================================
# 4. 全图像处理
# =====================================================================
def process_entire_image(img_data, wavelengths, ref_wavelengths, ref_absorption):
"""处理整个高光谱图像"""
height, width, _ = img_data.shape
concentration_map = np.zeros((height, width))
uncertainty_map = np.zeros((height, width))
for i in range(height):
for j in range(width):
# 获取当前像元光谱曲线
spectrum = img_data[i, j, :]
# 执行DOAS拟合
try:
conc, _, uncert = doas_fit(spectrum, wavelengths, ref_wavelengths, ref_absorption)
concentration_map[i, j] = conc
uncertainty_map[i, j] = uncert
except RuntimeError:
concentration_map[i, j] = np.nan
uncertainty_map[i, j] = np.nan
return concentration_map, uncertainty_map
# =====================================================================
# 5. 主程序
# =====================================================================
if __name__ == "__main__":
# 1. 加载数据
img_file = 'hyperspectral_img.hdr' # ENVI格式高光谱数据
img_data, img_wavelengths = load_hyperspectral_data(img_file)
# 2. 加载甲烷参考光谱
ref_wavelengths, ref_absorption = load_methane_reference()
# 3. 选择甲烷特征波段范围(1650-1680nm)
band_range = (img_wavelengths >= 1650) & (img_wavelengths <= 1680)
img_wavelengths = img_wavelengths[band_range]
img_data = img_data[:, :, band_range]
# 4. 处理图像
conc_map, uncert_map = process_entire_image(
img_data, img_wavelengths, ref_wavelengths, ref_absorption
)
# 5. 结果可视化
plt.figure(figsize=(15, 5))
plt.subplot(121)
plt.imshow(conc_map, cmap='jet', vmin=0, vmax=1.5e16)
plt.colorbar(label='甲烷浓度 (molecule/cm²)')
plt.title('甲烷浓度分布')
plt.subplot(122)
plt.imshow(uncert_map, cmap='viridis')
plt.colorbar(label='拟合不确定性')
plt.title('浓度反演不确定性')
plt.savefig('methane_concentration_map.png', dpi=300)
```
### 关键算法说明
1. **比尔-朗伯定律应用**:
$$
\ln\left(\frac{I_0(\lambda)}{I(\lambda)}\right) = \sigma(\lambda) \cdot c \cdot L + P(\lambda)
$$
其中$P(\lambda)$为低阶多项式,用于消除慢变化背景[^1]。
2. **光谱拟合过程**:
- 将甲烷参考光谱($\sigma(\lambda)$)插值到传感器波段
- 通过最小二乘拟合求解浓度$c$和多项式系数
- 特征波段选择在1650-1680nm(甲烷强吸收带)
3. **误差处理**:
- 协方差矩阵对角线元素给出浓度误差估计
- 无效值处理(NaN和零辐射值)
### 数据准备要求
1. **输入数据**:
- ENVI格式高光谱图像(.hdr+.img)
- 甲烷吸收截面数据(.csv),建议从HITRAN数据库获取
2. **预处理步骤**:
```python
# 典型预处理流程(需单独实现)
img_data = atcor.radiometric_calibration(img_data) # 辐射定标
img_data = atcor.atmospheric_correction(img_data) # 大气校正
```
### 注意事项
1. 实际应用中需添加辐射定标和大气校正模块
2. 可优化为并行计算以加速大数据处理
3. 需要实验室定标数据转换浓度单位(ppmv)
4. 联合反演多种气体时需扩展模型矩阵
阅读全文