简介:面向地震数据处理与地球物理勘探研究人员,这份Matlab工具包以SegyMAT为核心,集中解决SEGY标准格式地震数据的读取、解析、显示与分析问题。无论是初涉地震数据的新手,还是已有处理经验的技术人员,借助工具包中的完整读写接口,均可将SEGY文件快速载入工作区,随后完成道头信息提取、数据样本操作和地震剖面可视化。压缩包共包含75个文件,主体为63个.m脚本与函数,另有7个.fig界面文件、2个.dat数据文件、1个.segy样例数据以及license、revision版本说明,整体仅516KB,轻量且结构清晰。目前已有646人学习使用。资源还提供演示脚本与GUI交互界面,用户无需从零搭建工具链即可直接读取样例数据,并利用数据转换模块扩充不同来源的地震资料。对于希望掌握SEGY格式解析、批量处理地震道或搭建处理流程的读者而言,这份工具包堪称可直接运行的实用起点:既适合学习格式细节,也便于作为项目开发的基础设施,能显著降低上手门槛并加速项目迭代。
1. 地震数据处理的第一个坎:SEGY读取没你想的那么简单
做地震数据处理的人,八成躲不开SEGY这个格式。它是野外采集和机房处理之间的通用语言,几乎所有工区的炮集、叠加剖面、速度谱,最后都要落到一个或几个SEGY文件里。可这格式跟CSV、文本文件不一样,它是纯二进制加老式编码,卷头是EBCDIC不是ASCII,直接拿MATLAB的fread去读,十有八九读成乱码,甚至把道头坐标读错位。我最早在某高校实验室做模拟项目X时,就用裸fread硬啃一个三分量数据,结果到做偏移成像才发现道号顺序错了,整个流程白跑。后来用了一套现成的SEGY读取工具包,十分钟就把那批数据读进MATLAB,当时就想:这轮子为什么不早点用。这篇笔记会把工具包的核心逻辑、实际调法以及我在两个工区里踩过的坑讲透,给正在用MATLAB处理地震数据却还在手写字节解析的开发者一条省力的路。
2. SEGY文件结构拆解:卷头、道头和二进制振幅的读取逻辑
2.1 卷头与二进制头:EBCDIC解码和格式码的辨识
SEGY标准开头固定是3600字节的卷头,其中前面3200字节是文本头,用EBCDIC编码记录着测线名、采集日期这些人类可读信息;后面400字节是二进制头,记录格式码、采样点数、采样间隔等机器需要的参数。工具包之所以比手工fread稳定,第一步就赢在文本头解码上。MATLAB的fread默认把字节按ASCII解释,而EBCDIC和ASCII的码表完全不同,比如ASCII的'A'是65,EBCDIC里是193,所以不转换直接读就是一堆乱码符号。工具包通常会把3200字节整块读入,再用一个256字节的映射表把EBCDIC码转为ASCII码,这样文本头里的信息就能正常显示。
二进制头里最关键的字段是格式码(bytes 3221-3222),它决定了数据体每个采样值的二进制布局。常见值有这么几档:1是IBM 4字节浮点,2是IEEE 4字节浮点,5是4字节定点整数,8是8字节IEEE浮点。IBM浮点是一种特殊的老格式,指数基数是16而不是2,用MATLAB的普通single类型直接读会得到完全错误的数值,需要先按uint32读入,再按IBM浮点公式转换。工具包一般会自动读格式码并分发到对应的解码函数,这也是我推荐用工具包而不是自己写解析的最大原因——格式码这个坑,新手几乎必踩。
下面的代码展示了工具包中最基本的卷头读取调用(以我用的那套为例):
% 读取SEGY文件的文本头和二进制头 [textHeader, binHeader] = read_segy_headers('line_001.sgy'); % 打印解码后的文本头 disp(textHeader); % 检查二进制头关键参数 fprintf('FormatCode: %d\n', binHeader.FormatCode); fprintf('SampleCount: %d\n', binHeader.HNS); fprintf('SampleIntervalMicroSec: %d\n', binHeader.DT);这里read_segy_headers返回两个结构体,textHeader是字符数组,binHeader里包含FormatCode、HNS(每条道的采样点数)、DT(采样间隔,单位微秒)。注意HNS和DT是从二进制头第3221字节之后的偏移位置读取的,工具包已经替你算好了偏移量。如果读出的FormatCode不是常见的1、2、5、8,就要查一下文件是不是非标准SEGY。
下面这张表是我处理不同来源数据时整理的格式码速查:
| 格式码 | 数据格式 | 每个采样字节数 | 常见来源 |
|---|---|---|---|
| 1 | IBM 4字节浮点 | 4 | 老式野外记录设备 |
| 2 | IEEE 4字节浮点 | 4 | 现代处理系统输出 |
| 3 | 2字节定点整数 | 2 | 部分早期压缩存储 |
| 5 | 4字节定点整数 | 4 | 某些仪器厂商私有格式 |
| 8 | 8字节浮点 | 8 | 高精度重处理数据 |
第一次用工具包读一个未知来源的SEGY时,我建议先把FormatCode打出来,再和文件提供方确认一遍。别嫌多这一步,格式码错了后面全是白算。
2.2 道头与数据体:如何从文件头定位到每一道
卷头后面逐道排列,每道由240字节道头加采样数据组成。道头里记录了道号、炮点坐标、接收点坐标、采样点数这些信息,但它们的字节偏移非常琐碎。比如道号在第1-4字节,炮点X坐标在第73-76字节,接收点Y坐标在第85-88字节。如果手写解析,需要记住一整套偏移表,还得处理有符号整数和坐标比例因子的互相换算。工具包把这些偏移封装成结构体字段,省掉大量查表工作。
当需要读取特定某一道时,先计算跳过的字节数:前3600字节是卷头,之前每一道占(240 + 采样点数 × 采样值字节数)。工具包内部就是这样按偏移定位的。我一般会用下面的代码读取指定道号范围的数据:
% 读取从第1道到第100道的全部振幅数据 traceMatrix = read_segy_traces('line_001.sgy', 1, 100); % traceMatrix是100×N的矩阵,N为采样点数 size(traceMatrix)read_segy_traces的第二个参数是起始道号,第三个参数是结束道号。返回的矩阵每一行对应一道,每一列对应一个采样时间点。如果你只要某一道,可以这样:
% 读取第42道的振幅和对应道头 [trace, header] = read_segy_trace('line_001.sgy', 42); plot(trace);这里header结构体里可以直接取header.TraceNumber、header.SourceX、header.ReceiverY等字段。需要特别说明的是,道头里的坐标值通常存在比例因子,工具包返回的是已经乘好比例因子后的实际坐标,不需要你再去翻标准文档做换算。这点在三维数据里特别重要,因为源点和接收点坐标一旦少乘或多乘一个量级,整个工区的位置就全错了。
为了让你有个直观印象,我列一份常用道头字段的偏移表,即便工具包帮你处理,知道这些在排查时能帮你定位问题:
| 字段 | 字节偏移 | 类型 | 说明 |
|---|---|---|---|
| Trace Number | 1-4 | int32 | 道序号 |
| Source X | 73-76 | int32 | 炮点X坐标(乘以比例因子) |
| Source Y | 77-80 | int32 | 炮点Y坐标 |
| Receiver X | 81-84 | int32 | 检波点X坐标 |
| Receiver Y | 85-88 | int32 | 检波点Y坐标 |
| Sample Count | 115-116 | int16 | 本道采样点数 |
注意Sample Count字段是2字节的int16,最大值32767,对于常规地震道够用了。如果你看到某个道头里这个数异常,通常是读到了道头中间的填充字节,和真正的采样点数对不上。
2.3 为什么工具包比手写fread更可靠
手写读取SEGY的痛点可以归纳成四个字:字节序、编码、变体、校验。字节序方面,有些仪器记录的是大端序,有些是小端序,MATLAB读多字节数值时要改machineformat参数,稍不留神就读出天文数字。编码就是前面说的EBCDIC,看似小问题,但乱码会直接影响你判断数据内容。变体则更头疼,SEGY标准曾经更新过,有的文件在二进制头里加了扩展字段,或者把坐标比例因子的定义改过,手写代码很难兼容所有情况。校验是指读取后是否检查道数与文件尺寸匹配,工具包一般会在解析完所有道后做一次长度核对,如果文件被截断或有多余字节,会给出警告,而不是让你拿着错误的道集继续处理。
这些正是工具包的真正价值:它像一层黑匣子,把标准细节和边界条件都包住了,你只需要提供文件名和想要的道范围,剩下的字节操作交给它。当然,黑匣子也不是万能,遇到非标文件时你还是得回到原始字节去排查,但至少九成的标准SEGY文件它能一次读对。下一章我会讲怎么把这套工具包接进你的MATLAB环境,并给出三种常用读取方式的具体代码。
3. 用工具包把SEGY读进MATLAB:安装、调用与数据落地
3.1 工具包获取与路径配置
这套工具包一般在开源平台上能找到,搜索“MATLAB SEGY”就能看到几个不同版本。我用的是其中维护得比较活跃的一个,文件结构不复杂,核心函数就是读取卷头和道数据的几个M文件。下载解压后,打开MATLAB,把整个文件夹加入路径即可。我习惯放在一个专门的tools目录下,然后用addpath带genpath方式加入子路径:
% 将工具包所在目录加入MATLAB搜索路径 addpath(genpath('D:\work\matlab_tools\segy_toolbox')); savepath; % 保存路径,避免下次启动丢失参数说明:genpath会递归添加所有子文件夹,确保工具包自带的私有函数也能被发现。savepath会把当前路径列表写入默认路径文件,这样下次启动MATLAB不用重新addpath。如果你用MATLAB的Projects管理代码,也可以在startup.m里加这一行。注意不要直接把整个文件夹复制到MATLAB安装目录,那样升级或迁移都不方便。
另外,新版MATLAB支持matlab.addons.install,但工具包如果是纯M文件,手动管理路径其实更干净。我比较懒,直接用了addpath(genpath(...)),这套组合拳在R2019b到现在的版本上都稳定。
3.2 三种常见读取方式与适用场景
工具包一般提供三个层次的读取接口:整体读取、按道号区间读取、按坐标或偏移距抽取。整体读取适合文件不大、道数在几千以内的情况,一次全装进内存,后续操作简单。按道号区间读取适合大文件或需要并行处理的场景,只把需要的道加载进来,节省内存。第三种按坐标抽取则多用于处理三维数据,比如只抽取炮检距在一定范围内的道集。
第一种,一整个文件读入:
% 一次性读取全部道数据和完整头信息 data = read_segy_full('gather_050.sgy');返回的data结构体里,data.traces就是所有道振幅组成的二维矩阵,data.headers是道头数组,data.binHeader是二进制头。这种读法实现最简单,代价是文件大小直接决定内存占用。一个三维工区的单线数据可能几百MB,全读进来就可能接近1GB,要量力而行。
第二种,按区间读取:
% 只读取第200到第300道 partial = read_segy_range('gather_050.sgy', 200, 300);这种读法内部每次先跳过多余的字节,再读取目标区间,适合在循环里每次抓取一小段道集。我处理某工区时就是通过循环调用这个函数,把整条测线按炮集逐块处理,内存占用始终控制在几百MB。
第三种,按坐标抽取。这个稍微复杂,因为要先读完道头才能判断某一道是否满足炮检距条件。工具包一般会提供读道头的函数:
% 先读取所有道头信息 headers = read_segy_headers_only('line_003.sgy'); % 找到炮检距在1000到2000米之间的道 offset = headers.ReceiverX - headers.SourceX; % 简化计算,实际需要按三维距离 idx = find(offset >= 1000 & offset <= 2000); % 再按索引读取对应道 subset = read_segy_traces_by_index('line_003.sgy', idx);这里read_segy_headers_only只解析道头,不会把整道振幅读进内存,所以即使文件有几个GB也能快速扫完。offset的计算在真实数据处理中要用炮点和检波点的X、Y坐标算水平距离,我这里为了示意简化为只算X方向差值,实际代码里别这么干。三维数据里X、Y方向差值的平方和开根号才是真正的水平距。
3.3 读取后的数据形态与常用字段
工具包返回的结构体字段名可能因为版本有些差异,但核心内容差不多。我常用的几个字段是:
| 字段 | 含义 |
|---|---|
| traceMatrix | 道振幅矩阵,行是道,列是采样点 |
| headerArray | 结构体数组,每个元素是一道的完整道头 |
| sampleInterval | 采样间隔(微秒) |
| sampleCount | 每道采样点数 |
| formatCode | 原始格式码 |
访问某一道的振幅,用traceMatrix(i,:);要画某一道,直接绘图。如果需要按时间轴显示,可以用采样间隔算出时间向量:
dt = data.sampleInterval * 1e-6; % 转换为秒 time = (0 : data.sampleCount - 1) * dt; plot(time, data.traceMatrix(10,:));参数说明:采样间隔在二进制头里通常是微秒单位,所以要乘1e-6变成秒。时间向量的长度必须等于采样点数,直接用sampleCount生成即可。这类基础操作虽然简单,但最容易出错的恰恰是单位转换,后面避坑章我再具体说。
还需要注意,有些工具包会把traceMatrix按列存储而不是按行存储。读取后最好先用size确认一下维度含义,避免把道和采样点搞反。我一般会在读取后立刻写一行注释记录矩阵形状,方便后续脚本引用。
4. 避坑与排查:SEGY读取中的常见问题与处理技巧
4.1 振幅数值数量级不对
现象:读出来的振幅范围明显偏大或偏小,比如正常地震道振幅应该在几千到几万的量级,结果读出来是1e30或只有1e-5。
原因:最常见是格式码识别错误。文件二进制头里写的是IBM浮点(格式码1),工具包如果按IEEE浮点解释,数值就会变成完全不同的范围。IBM浮点的指数基数是16,同一个二进制模式换成IEEE解读,结果可能差10^38倍。
解决:先检查binHeader.FormatCode。如果与实际文件记录格式不符,可以手动指定格式码覆盖自动判断。工具包有些版本提供可选参数,比如read_segy_full('file.sgy', 'FormatCode', 1)。实在不行,用十六进制人工查看一段数据:IBM浮点的第一个字节高四位是指数,IEEE浮点的第一个字节高位是符号位和指数的一部分,肉眼能看出差异。
4.2 道头坐标读到极大或负的异常值
现象:读取坐标后,炮点X坐标变成-999999或2.1e9这种明显不对的数字。
原因:坐标字段在有符号整数和无符号整数之间混用,以及比例因子没乘。SEGY的坐标值常带一个比例因子,比如道头里有Scalar字段,取值为负数表示乘法,正数表示除法。工具包如果没处理这个,读出来就是原始整数。
解决:检查头结构里是否有相关缩放字段。我用的工具包默认会处理,但如果读的是别人改过的非标文件,就得手动补上比例因子。可以对比两炮已知坐标,反推缩放方向。比如已知实际坐标是12345,而道头读出来是1234500,说明比例因子是0.01,需要缩小100倍。注意有的系统用百分之一度做单位,不是米,那种情况还要再做单位换算。
4.3 读取大文件时内存溢出
现象:执行读取命令后,MATLAB报“Out of Memory”或者直接卡死。
原因:一次性读入大文件。SEGY文件动辄几百MB甚至数GB,如果全部道都读入内存,会直接超出可用内存。再加上MATLAB本身占用的内存,很容易崩。
解决:改用按区间读取或按道头索引读取,分块处理。另外确认操作系统是64位,MATLAB的Java堆内存不要设置太大,留给数据矩阵更多内存。这里有个血泪经验:我曾在某个工区用整体读取方式尝试读4GB的SEGY文件,MATLAB卡死了十五分钟,最后强制退出。后来改成循环按炮集读取,原本要卡死的任务几分钟就完成。
4.4 文本头读出来全是乱码
现象:调用读取函数后,文本头显示为“!!!!!“或者一堆不可读符号。
原因:文本头是EBCDIC编码,而某些简易读取函数直接按ASCII返回。这是最典型的SEGY新手坑,但有时候连老手也会忽略——尤其当工具包更新后默认行为变了。
解决:用支持EBCDIC解码的函数。如果工具包没有内置,自己写个映射:读入3200字节,然后按标准转换表逐字节映射为ASCII。这属于低概率踩坑,但要清醒地知道乱码不一定是你代码写错了,可能是编码没转换。我自己就翻车过一次,当时拿到一个旧的SEGY文件,工具包版本居然没有自动转换EBCDIC,害得我以为是文件损坏。
4.5 道数比预期少或文件尾部报错
现象:读取的文件道数比文件列表里标称的少,或者在读取最后几道时报错“Failed to read trace”。
原因:文件里可能含有道尾标记或额外填充字节,也可能文件被截断。标准SEGY在最后一条道后没有特殊标记,但有些采集系统会在文件末尾加一些填充数据。另一些情况下,文件在传输过程中被截断,导致最后一道数据不完整。
解决:用工具包的文件长度校验函数,或自己检查文件字节数与按道数计算的理论长度是否一致。如果只差几个字节,大概率是文件尾部有多余填充,跳过即可;如果差很多,说明文件损坏或道数设置不对。我一般会用check_segy_integrity先跑一遍,再决定后面怎么处理。养成这个习惯可以减少很多莫名其妙的结果。
5. 进阶:批量读取与快速验证的实用技巧
处理多个SEGY文件时,逐个手动调用太累。我习惯把读取放在循环里,用dir批量获取文件列表,再用parfor并行处理。但并行池开启有开销,小文件串行更快。下面是我常用的批量扫描函数:
files = dir('segy_files/*.sgy'); for i = 1 : length(files) info = read_segy_info(fullfile(files(i).folder, files(i).name)); fprintf('%s: %d traces, %d samples/trace\n', ... files(i).name, info.traceCount, info.sampleCount); endread_segy_info是我调用工具包里的一个快速函数,只读取卷头和道头统计信息,不加载振幅数据,所以速度很快。批量验证时,把所有文件的道数和采样点数打印出来,一眼就能看出哪些文件有问题。之前我在某跨平台系统项目里处理一批凌晨采集的SEGY,有两条线文件名写错了,单看文件大小正常,但用这个方式一刷,道数不对的文件很快就暴露出来。
做正式处理前,我还会对第一个文件做一次“加载→绘制→对比”的三步验证。具体是:读取第一炮数据,绘制成波形图,再和原始记录的图件或已知数值做对比。一旦波形形态、初至时间对得上,说明读取的解释正确。如果波形像噪声,多半是格式码或字节序不对。
除此之外,还有一个小习惯:处理好后把读取用的参数(格式码、采样间隔、道头缩放)都记录到日志里。SEGY文件容易来自不同设备,参数混着用很容易翻车。我上次就是没记录某条线的坐标比例因子,隔了一周再处理同一个工区的新数据,拿错参数当成默认值,结果坐标偏了几百米,最后全靠原始文件重新读才救回来。从那以后我每次面对新的SEGY数据集,都会强制走一遍“文件清单扫描→单文件抽检→参数记录”的流程,再跑正式流程。希望帮到你。
本文还有配套的精品资源,点击获取