目录
一、前言
二、SeisInvNet-2020
三、RTM研究
四、遇到的问题及解决
4.1 KeyError: 'data'
4.2 将mat文件转换为npy文件
五、小结
5.1 存在的问题及疑惑
5.2 下周安排
本周的主要安排是阅读论文查看一些好的点子。
但是想法总是美好的,之前答应的伴娘任务没想到这么重,以至于任务完成情况不太好。这个过程中或多或少也有一些收获,学到了一些新的知识点,对之前模糊的概念也有了更清晰的认识。
标题:Deep learning Inversion of Seismic Data—地震数据的深度反演
数据集:自主创立的SeisInv数据集—10000用于训练,1000用于验证,1000用于测试。
主要思想:在SeisInvNet模型中,首先对地震数据进行预处理,包括噪声去除、信号增强等操作,以提高数据的质量和可读性;然后,利用深度学习技术对地震数据进行特征提取,提取出与地下地质结构相关的特征信息;接下来,将这些特征信息输入到分类器中进行分类,以实现对地下地质结构的自动识别和解释。
主要解决的问题:
现有方法存在的不足:
创新点:
地震数据的不确定性和时变特性是地震数据处理和分析中的两个重要问题。
该部分参考张星移师兄的博客:2023 7.10~7.16 周报 (RTM研究与正演的Python复现) (8.3更新)-CSDN博客
叠前逆时偏移技术是一种地震成像技术,其原理基于双程波波动方程,利用各检波点接收到的波形信号,沿时间轴逆推求解波动方程,得到反向延拓波场。再从震源处求解波动方程,正向得到震源的传播波场。将得到的两个波场利用一定的成像条件进行成像,最后把所有炮点的成像剖面叠加起来,就得到了地层模型的偏移成像效果图。
大致步骤:
导入相关库:
from bruges.filters.wavelets import ricker # 滤波器
import matplotlib.pyplot as plt # 绘制图表和图形
from scipy.signal import convolve # 用于执行一维信号的卷积操作
import skimage.filters # Python图像处理库,包括滤波、变换、分割、特征提取等
import time # 用于导入Python的内置时间模块,用于获取当前时间、格式化时间、计算时间差等
from multiprocessing import Pool # 实现多进程编程
import numpy as np # 支持大量的维度数组与矩阵运算
import scipy.io # 提供了与MATLAB文件格式(.mat文件)的读写功能
import cv2 # 计算机视觉库,提供了丰富的图像处理和计算机视觉功能
import matplotlib
matplotlib.use('TkAgg') # 创建图形用户界面
- from bruges.filters.wavelets import ricker:
bruges
:用于石油和地质领域的数据处理和分析,提供了很多用于地震数据处理的工具和功能;
filters.wavelets:
Bruges库中的一个子模块,它提供了与小波相关的功能和工具;
ricker:
Ricker小波在信号处理和地震学中经常被用作滤波器或分析工具;
- from scipy.signal import convolve
scipy
:是一个开源的Python数学、科学和工程库,SciPy库提供了大量的数学函数、算法和工具,用于科学计算、数据分析、信号处理、线性代数等;
signal
:是SciPy库中的一个子模块,专门用于信号处理,提供了各种信号处理相关的函数和工具,用于分析、处理和变换信号;
convolve
:是scipy.signal
模块中的一个函数,用于执行一维信号的卷积操作;
- import skimage.filters
skimage
:scikit-image
库的缩写,是一个开源的Python图像处理库,提供了丰富的图像处理功能,包括滤波、变换、分割、特征提取等。
filters
:skimage
库中的一个子模块,专门用于实现各种图像滤波算法,提供了多种滤波器函数,如高斯滤波、中值滤波、边缘检测等。
- from multiprocessing import Pool
multiprocessing:
用于实现多进程编程;
Pool:
multiprocessing
模块中的一个类,用于管理进程池,可以将一个可迭代对象中的任务分配给多个进程来并行处理;
在下面的介绍中使用到的是SEG盐数据(OpenFWI的数据还有问题没有调试完成),以下是SEG盐数据的基本参数:
正演间隔(像素点之间的间隔) | 震源频率(Hz) |
采样间距 | 炮数 | 接收器范围 | 接收器数量 | 时间采样 | |
10m | 25Hz | 103.45m | 29 | 10m | 301 | 2000 |
加载数据,并对数据进行一些处理与转换:
# 共炮道集的地震数据
csg = np.load("seismic1668.npy")[0]
# 速度模型
vmod = scipy.io.loadmat('vmodel1668.mat')["data"].astype(np.float64)
# 恢复到原本的时间采样个数
csg = np.array([cv2.resize(image, dsize=(301, 2000), interpolation=cv2.INTER_CUBIC) for image in csg])
# 应用高斯滤波器(标准差设置为5)
init_vmodel = skimage.filters.gaussian(vmod, 5)
# 显示原始和滤波后的图像
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(vmod)
plt.title('Original Image')
plt.subplot(1, 2, 2)
plt.imshow(init_vmodel)
plt.title('Image after Gaussian Filter')
plt.show()
原始和滤波后的图像展示:
挑选参与RTM计算的炮集:
# 挑选参与RTM计算的炮集 (若炮集数量不是太大, 建议全部加入, SEG略大, 故做删减)
sx_list = [20, 60, 100, 140, 180, 220, 260]
mapping_indice = np.array([1, 5, 9, 13, 17, 21, 25])
# 全部导入
# sx_list = list(range(10, 291, 10))
# mapping_indice = np.arange(29)
csg = csg[mapping_indice]
RTM = construct_rtm(common_shot_gathers=csg,
init_vmodel=init_vmodel,
dx=10, dz=10,
sx_list = sx_list,
sz=0, dt=0.001, nt=2000, f0=25, source_time=0.08,
temp_process_batch_list=[4,3])
# temp_process_batch_list=[4,4,4,4,4,4,4,1]) # temp_process_batch_list总和等于炮位个数, 表示一次同时计算多少个程序块
震源设置:在2000个时域采样点中,在前121个采样点设置雷克子波的震源,即2s中的前0.121内进行释放(调用库)
source_wav = ricker(duration=source_time, dt=dt, f=f0)[0] # 定义波形(雷克子波)
消除走时信息: (在师兄博客写的是前向波,但是提及前向波不能完全取代走时信息,我觉得这里还是理解为走时信息好一些)
# 走时信息
muted_gather = common_shot_gather.copy() # 复制单炮的地震观测记录
x_array = np.arange(0, nx*dx, dx) # 基于采样进行标点, 得到地表处每个标点的数组 [0米, dx米, 2dx米, 3dx米, ... , (nx-1)dx米]
v0 = init_vmodel[0,:] # v0表示地表一层的默认地层速度值 (长度为nx的数组)
# np.cumsum(dx/v0)表示在地表第一层区域内, 波从左到右传播的用时数组 [dx/v0秒, 2dx/v0秒, 3dx/v0秒 ...]
# ...[isx]自然是指的传到中间点的用时
# 相减后就得到中间出发向左右两边传递的波在不同位置的时间 [... -2dx/v0秒, -dx/v0秒, 0秒, dx/v0秒, 2dx/v0秒 ...]
traveltimes = abs(np.cumsum(dx/v0) - np.cumsum(dx/v0)[sx])
for traceno in range(len(x_array)):
muted_gather[0:int(traveltimes[traceno]/dt + source_wave_duration), traceno] = 0 # 最后加上len(wav1)是雷克子波的时间, 因为雷克子波能量完全释放需要时间
上形波和下形波是根据震源到地表的传播路径的不同来划分的。在地面激发,井中各点接收时,可记录到各种波,如直达波、反射波、多次波等。这些波就其传播方向可分为两类:一为下行波,二为上行波。直达波和二次反射波为下行波,一次反射波为上行波。
construct_rtm函数获取RTM图像部分介绍:
# 创建一个填充零值的数组
RTM = np.zeros([len(sx_list), init_vmodel.shape[0], init_vmodel.shape[1]])
process_list = []
process_batch_list = temp_process_batch_list # 设置每次进程池大小 (总和与炮数一致)
init_index = 0
# 创建进程池
for max_process_num in process_batch_list:
pool = Pool(processes = max_process_num) # 设置进程池大小
index_range = range(init_index, init_index + max_process_num) # 设置当前进程池中的序号
# 将进程分配出去
for index in index_range:
source_x = sx_list[index]
# apply_async用于异步地运行一个函数,并返回一个AsyncResult对象;single_shot_rtm是异步运行的函数,后面的参数是传递给该函数的
# 并发地运行 single_shot_rtm 函数,并将结果添加到 process_list 列表中。每个进程的参数都通过传递给 .apply_async() 的参数来指定
process_list.append(pool.apply_async(single_shot_rtm,(common_shot_gathers[index], init_vmodel, dx, dz, source_x, sz, dt, nt, f0, source_time)))
# 确保所有任务都执行并且进程池被关闭
pool.close() # 确保不再添加更多任务,已经提交给进程池的任务将继续执行直到完成
pool.join() # 等待所有任务完成
# 接受各个进程的结果
for index in index_range:
RTM[index] = process_list[index].get()
init_index += (max_process_num * 1)
time_elapsed = time.time() - ti
print("均完成! 总用时{:.0f}m {:.0f}s".format(time_elapsed // 60, time_elapsed % 60))
# np.save(output_dir + "seismic{}.npy".format(vmodel_id), CSG)
return RTM
下图是一个结果展示,但是距离师兄博客中展示的效果还差很远,这个问题还没有解决。
问题描述:正在尝试访问字典中不存在的键
问题解决:查看其中关键字,将data修改为svmodel
在代码中涉及到npy的SEG盐数据,但是我没有npy格式,所以就想到可以将mat文件进行转换。
在Pycharm中,可以编写以下Python脚本来转换.mat文件到.npy文件:
import scipy.io
import numpy as np
# 通过scipy.io读取.mat文件:
data = scipy.io.loadmat('D:\\桌面\\地震反演\\FCNVMB_paper_with_code1\\data\\train_data\\SimulateData\\vmodel_train\\vmodel1.mat')
# 假设字典中'vmodel'是你要的数据:
data = data['vmodel']
# 转换为.npy文件:
numpy_data = np.array(data)
# 保存为numpy数组文件(.npy文件)
np.save('vmodel1601.npy', numpy_data)
可以使用numpy的load函数检查生成的文件是否正确:
data = np.load('vmodel1601.npy',allow_pickle=True)
print(data)
(RTM)为什么效果差距这么大,还没找到原因,猜测可能是数据不对?
下周把Deep learning Inversion of Seismic Data论文读完。