文档库

最新最全的文档下载
当前位置:文档库 > 利用QT实现地震数据CST文件的二维显示

利用QT实现地震数据CST文件的二维显示

2010.3

图形图像处理

1前言

目前主流的地震资料处理与解释系统有CGG (地震资料

处理系统),LAND MARK (地震资料解释系统)等,它们都是大型专业的地质处理软件,运行于工作站环境之上,由于依靠工作站强大的数据处理能力和图形处理能力,所以不可以运行在微机上。但是随着微机硬件设备的不断提高,微机的处理能力已经足以满足上述功能要求,所以开发可以运行在微机上的地震资料处理与解释系统是迫在眉睫的需要解决的问题。而解析地震勘探数据CST 文件,实现二维显示则是解决问题的必经之路。同时因为传统的地质处理软件占用的存储空间都是很庞大的,运行时必须进行繁琐的数据加载,参数设置等一系列步骤以后才可以显示二维地震剖面,并在其上面实现一系列复杂的操作和交互,如果做一些简单地显示二维剖面和基本交互的实现,那么本程序的优势就更显得突出。该程序占用的资源相对比较少,实现数据处理能力和图形处理的功能运行效率很高,而且可以平稳地运行于各种微机操作系统,这也是上述专业地质软件所不能比拟的。

本程序的开发工具是采用QT ,集成开发环境选用最新的

Qt Creator1.0。QT 是与平台无关的编程语言,这也是可以实现

跨平台的关键因素。

2

CST 文件格式

这部分内容将会分3步对CST 文件进行解析。

2.1

CST 文件由剖面组成

一个CST 文件的总体结构首先是由不同的剖面组成,不同

的CST 文件的剖面个数是不同的,有的文件可以多达几百个剖面,而有的只有一个剖面,文件中不会显式地说明到底有多少个剖面,剖面的个数隐藏在每一个剖面的道头数据中,下面讲如何获取文件的剖面数,如图1所示是一个CST 文件的基本格式。

2.2剖面由地震波组成

每一个剖面数据的格式都是相同的,所以在此只研究其中

的一个剖面,任何一个剖面数据都包含若干条地震波数据(道),如图2所示是每一个剖面的结构图。

2.3地震波由三类数据组成

一个地震剖面数据CST 文件中任何一条地震波(道)的格

式都是相同的,它的格式都是包含以下3个部分,如图3所示是一个地震波的结构图。

2.3.1道头数据

主要存放一些地震剖面数据的参数,占用256个字节的存储空间,共形成64个道头字,每一个道头字都代表一定的意义,以下只介绍几个比较常用的道头字的意义。

第9个道头字:代表地震波的采样频率,单位是微妙。第17个道头字:记录这条地震波在所属的剖面中的编号,又称为道号。

第19个道头字:记录这条地震波所在的剖面的编号,又称为线号。

2.3.2振幅数据

这部分存储的是地震波的振幅数据,根据采样频率的不同所占用的空间也是不同的,因为每一个采样点的数据是用一个四个字节的FLOAT 类型保存的,所以假如采样点为6000,那么该段占用的存储空间为24000个字节。

2.3.3备用空间

这部分是预留下的备用字段,在经过处理后的CST 文件中,不同的采样点数对应不同的空白区域,6000、3000和

1500采样点数分别对应400、32、400个字节的空白区域。

利用QT 实现地震数据CST 文件的二维显示

张燕梅

孟保国

摘要:完整地解析了地震数据CST 文件的存储格式,并在此基础上详细说明了利用QT 的开发

工具对该种数据格式进行读取,以及实现二维地震剖面的基本显示、二维地震剖面的交互显示和保存到位图的具体操作。关键词:QT ;CST ;

利用QT实现地震数据CST文件的二维显示

地震勘探

图3一条地震波的结构图

图1CST 文件总

体结构图

图2一个剖面的结构图

65

利用QT实现地震数据CST文件的二维显示

2010.3

3程序模块划分和数据流图

首先根据模块化设计的思想,将程序进行比较详细的模块

划分,每个模块都要完成一部分相对比较独立的功能。该程序可以分为4个大的模块,分别是数据输入模块、二维显示模块、二维交互模块、保存位图模块。程序总体的数据流向如图

4所示。

4

关键代码

4.1

数据输入模块

数据输入模块的主要功能就是直接读取CST 文件的采样频

率、起始剖面编号、计算出剖面个数、每一个剖面的地震波条数等等一系列参数,为二维的显示做好充足的准备。

(1)直接参数的获取:直接参数包括文件的采样时间,文

件的大小等。

1)采样时间:采样时间存储在CST 文件中的任一个剖面

的任一条地震波的第9个道头字里面,一个地震剖面CST 文件中的采样时间是固定的,所以读取任意一条地震波的第9个道头字就行了。程序中采用读取第一个剖面第一个地震波的数据,存放在变量mod9里面。

2)文件大小:得到文件的大小主要用来最后计算文件的

剖面数以及每个剖面中包含的地震波个数。

3)直接参数获取关键代码

file.setFileName(fileName);//设置文件的名字

file.open(QIODevice::ReadOnly);//以只读的方式打开文件in.setDevice(&file);//使用输入输出流file.seek(32);//文件位置定位in>>mod9;//获取采样时间

size=file.size();//获取文件的大小file.close();//关闭文件

(2)间接参数的获取:间接参数包括采样点个数、剖面的

个数、剖面的编号、每个剖面的地震波的个数、地震波的编号、每条地震波后面的空白空间的大小。

1)采样点个数:采样点的个数和采样点的时间有关系,

因为总的采样点时间是固定的,为6秒钟,所以采样点的个数

和采样时间成反比。

2)剖面个数和剖面编号:主要是由每一个地震波的第19

个道头字决定的,同一个剖面的所有地震波的该段数据都是相同的,且存储的是该剖面的编号,所以要得到这个参数,必须经过对整个数据文件的遍历得到。

3)地震波个数和地震波编号:确定了剖面的个数和剖面

的编号以后,任选一个剖面,对其中的所有的地震波都进行遍历,就得到了该剖面的地震波个数,而地震波的编号存储在道头部分的第17个道头字里面。

4)空白空间:空白空间的大小虽然不存放任何的有用数

据,但是它在计算剖面的个数时候也是要考虑其所占用的存储空间的。

5)间接参数获取关键代码

file.setFileName(fileName);

file.open(QIODevice::ReadOnly);in.setDevice(&file);

switch(mod9)//依据不同的采样时间获得空白空间的大小{

case 1000:blank=400;break;

case 2000:blank=32;break;

case 4000:blank=400;break;default:break;}

file.seek(72);

in>>firstMod19;//得到第一个剖面的编号

sampleNumber=6*1000000/mod9;//得到采样点的个数int number;//用于记录所有的线中总共有多少条道count=0;//用于记录剖面的个数quint32tmpCurrent,tmpPrevious;

number=file.size()/(sampleNumber*4+256+blank);//总大小除以每一条线的大小

for(int i=0;i

if(i==0){

file.seek(72);in>>tmpCurrent;

tmpPrevious=tmpCurrent;}else {

利用QT实现地震数据CST文件的二维显示

tmpPrevious=tmpCurrent;

图4数据流图

66

2010.3

图形图像处理

file.seek(i*(sampleNumber*4+256+blank)+72);in>>tmpCurrent;

if(tmpCurrent!=tmpPrevious){

traceNum[count]++;count++;}else {

traceNum[count]++;}}}

traceNum[count]++;

//traceNum[]这个一维数组中存放着每一个剖面所含有的//地震波的数

return count++;//count 中存放的是整个文件的剖面个数4.2二维显示模块

二维显示模块的主要功能就是调用上一个模块获得的文件参数,首先让用户选择所要进行二维剖面显示的剖面编号,然后分配相应的内存空间,经过单道归一化和全局归一化以后再将处理后的数据传递过去,调用画位图的函数将二维剖面先画在位图上,然后再在窗口中显示。这一个模块是后面所有模块的基础,也是系统中比较重要的模块之一。

(1)设置要显示的剖面以及初始化:一个文件中可能有很多个剖面,但是二维显示模块一次只能显示一个剖面,所以需要用户去选择初始显示的剖面。

1)设置要显示的剖面示意图:如图5所示。2)设置要显示的剖面以及初始化关键代码

void PainterWidget::setStart(int lineNum)//设置每一个剖面的初始位置的函数{

start=0;

curLineNumber=lineNum;//存放当前的线号traceNumber=traceNum[curLineNumber-1];//存放当前剖面包含的地震波个数for(int i=curLineNumber;i>1;i--){

start +=traceNum [i -2]*(sampleNumber*4+256+blank);//计算振幅数据起始位置}}

(2)分配内存读取数据:为要显示的剖面分配必要的内存空间,以供程序使用。这部分的空间主要是用来存放该剖面的所有地震波的振幅数据的,数据量比较大。

分配内存读取数据关键代码

file.setFileName(fileName);

file.open(QIODevice::ReadOnly);in.setDevice(&file);

for(int i=0;i

//读取每一道的振幅数据存放在相应的数据结构中{

file.seek(start+i*(sampleNumber*4+256+blank)+256);for(int j=0;j

in>>mem[i][j];//存放当前显示剖面的振幅数据}}

file.seek(start+64);

in>>mod17;//存放当前显示的剖面的第一个地震波编号file.seek(start+72);

in>>mod19;//存放当前显示剖面的剖面编号file.close();

(3)单道归一化:单道归一化就是在显示每一条地震波的时候,这条波里面绝对值最大的采样点都充斥满所属的画图区域里面,这就要求对显示的剖面所有地震波一一进行归一化,就是先找出每条地震波的绝对值最大的值,然后让这条道的所有采样点都除以这个绝对值。

1)单道归一化示意图:如图6所示,图中的几个振幅比

较大的点,它们之间其实没有什么大小关系,只是在各自的道中绝对值占最大值的比例相近罢了。

2)单道归一化关键代码

float tmp[6000];

for(int k=0;k

for(int p=0;p

tmp[p]=mem[k][p];}

testMax[k]=findMax(tmp);

//调用最大值函数找每条地震波绝对值最大值}

图5

利用QT实现地震数据CST文件的二维显示

利用QT实现地震数据CST文件的二维显示

设置要显示的剖面

图6

单道归一化图

67

利用QT实现地震数据CST文件的二维显示

2010.3

for(int m=0;m

for(int n=0;n

me[m][n]=mem[m][n]*interval/testMax[m];//单道归一化的实现}}

(4)全局归一化:全局归一化是在进行变密度显示的时候

用到的,所谓变密度显示,就是利用颜色之间的渐变去显现出整个剖面的地层结构,颜色的渐变是一个整体的概念,所以需要找到当前剖面的所有振幅数据中正向绝对值最大的和负向绝对值最大的分别作为两个方向上的基数,让所有正向振幅除以正向最大值,所有负向振幅除以负向最大值,形成全局归一化。

1)全局归一化示意图:如图7所示。

2)全局归一化关键代码:

for(int i=0;i

if(rawData[i]>0)//如果是正向的振幅{

QPen pen1(QColor(255,255-(int)rawData[i]/rightColorInterval ,255-(int)rawData[i]/rightColorInterval),sampleWidth);

//根据正振幅不同的值去设置填充颜色的深浅painter.setPen(pen1);

painter.drawLine(20+trace*interval,topHeight+i*sampleWidth ,20+trace*interval +interval,topHeight +i*sampleWidth);//填充}

else if(rawData[i]<0)//如果是负向的振幅{

QPen pen2(QColor(255-(int)rawData[i]/leftColorInterval ,255-(int)rawData[i]/leftColorInterval ,255),sampleWidth);

//根据负振幅不同的值去设置填充颜色的深浅painter.setPen(pen2);

painter.drawLine(20+trace*interval,topHeight+i*sampleWidth ,20+trace*interval -interval,topHeight +i*sampleWidth);//填充

}}

(5)画位图:这是将所要显示的剖面事先画在一张

QPixmap 上,这样做的好处有很多,首先可以在显示的时候不

会产生延迟现象,也不会因为不停地刷新而产生的闪烁现象,还有一点就是在保存位图的时候可以直接将此对象的指针传递过去,使得保存时候变得很简单。

画位图关键代码:

void PainterWidget::createPixmap(){

pixmap =new QPixmap (40+interval*(traceNumber +1),topHeight+20+sampleWidth*(sampleNumber-1));//新建一个QPixmap 对象

pixmap->fill(QColor(214,190,241));//创建位图的底色QPainter painter(pixmap);//在位图上建立画笔drawFrame(painter);//调用画边框的函数if(!sort)//如果是正序{

QProgressBar progressBar_a;

progressBar_a.setWindowIcon(QIcon(":/images/system.png"));progressBar_a.setWindowTitle(tr("正在处理数据..."));progressBar_a.setWindowModality(QT::WindowModal);progressBar_a.setRange(1,traceNumber);progressBar_a.show();//以上代码用来显示进度条for(int k=1;k<=traceNumber;k++){

progressBar_a.setValue(k);

getData(k);

//获得单道归一化数据for(int i=1;i

drawP(k,i,data[i-1],data[i],painter);//调用画波形的函数}

if(consistencyShow){

getRawData(k);//获得全局归一化数据

showConsistency(k,painter);//调用变密度显示的函数}else {

fillArea(k,painter);//调用填充波形的函数}}}4.3

二维交互模块和保存位图模块

二维交互模块主要实现的功能是通过对剖面的不同属性的设置,形成不同组合和搭配的二维剖面,包括显示的剖面颜色、填充的方式、变密度显示、正逆序显示、是否显示波形等,来实现特定的功能,以满足用户不同需求。

(1)填充方式:填充方式在本程序中主要分为4种:

利用QT实现地震数据CST文件的二维显示

填充

国7全局归一化图

68

2010.3

图形图像处理

正振幅、填充负振幅、填充正负振幅、不填充。1)填充方式示意图:如图8所示。

2)填充方式实现方法:这四种方式主要是用一个状态变

量来控制的,用户在点击菜单上的切换填充方式的功能按钮时候,状态变量的状态会进行切换,所选择的填充方式也是不同的。每次在重新刷新显示区域的时候都要去判断这个变量是处于哪一个状态,然后程序选择不同的执行路径。

3)填充方式关键代码

fillModel=(fillModel+1)%4;

//fillModel 存放填充模式,响应按钮,每次切换到下一个模式if(fillModel==0)

//在此只把填充正振幅的代码放在这里做参考,其他的同理{

painter.setPen(fillRightColor);//设置画笔颜色for(int i=0;i

if(data[i]>0){

painter.drawLine(20+interval*trace,topHeight+i*sampleWidth ,20+interval*trace+(int)data[i],topHeight+i*sampleWidth);

//以连线的方式进行填充}}}

(2)改变颜色:用户可以随时更改填充波形的颜色,可以

将正向填充和负向填充设置为不同的颜色。

1)改变颜色示意图:如图9所示。

2)改变颜色实现方法:该部分功能主要是调用系统的颜

色对话框,获取了用户选择的颜色之后返回设置填充时候的画笔的颜色即可,当系统进行刷新以后,填充的颜色就已经被改变了。

3)改变颜色关键代码:

if(fillModel==0){

QColor color=QColorDialog::getColor(QT::red);//调用系统的颜色对话框if(color.isValid()){

fillRightColor=color;

//设置填充正振幅的颜色}}

else if(fillModel==1){

QColor color=QColorDialog::getColor(QT::blue);if(color.isValid()){

fillLeftColor=color;//设置填充负振幅的颜色}}

(3)变密度显示:变密度显示是这个模块的一个很重要的

功能,通过变密度显示可以很清楚、看到整个剖面的地层结构,这一功能也是在大型地震资料处理与解释系统中很常见,也是很重要的一种功能。

1)变密度显示示意图:如图10所示。

2)变密度显示实现方法:它主要是通过对于正负两个方

向上,绝对值越大,颜色的深度越深,并且填充满整个区域,使用户可以直观地看到地层的走向,实现的时候也是本着这个思想,将不同的振幅映射到不同的颜色深度上,从而达到变密度显示的效果。

3)变密度显示关键代码

int rightColorInterval=(int)(maxRight)/256;//计算正振幅颜色的级差

int leftColorInterval=(int)(maxLeft)/256;//计算负振幅颜色的级差

for(int i=0;i

a 填充正振幅

b 填充负振幅

c 填充正负振幅

d 不填充

图8填充方式

图9

利用QT实现地震数据CST文件的二维显示

利用QT实现地震数据CST文件的二维显示

改变颜色

图10变密度显示

69

利用QT实现地震数据CST文件的二维显示

2010.3

if(rawData[i]>0){

QPen pen1(QColor(255,255-(int)rawData[i]/rightColorInterval ,255-(int)rawData[i]/rightColorInterval)

,sampleWidth);//设置该采样点出颜色的深度painter.setPen(pen1);

painter.drawLine(20+trace*interval,topHeight+i*sampleWidth ,20+trace*interval+interval,topHeight+i*sampleWidth);//正向填充满每一道}

else if(rawData[i]<0){

QPen pen2(QColor(255-(int)rawData[i]/leftColorInterval ,255-(int)rawData[i]/leftColorInterval

,255),sampleWidth);//设置该采样点出颜色的深度painter.setPen(pen2);

painter.drawLine(20+trace*interval,topHeight+i*sampleWidth ,20+trace*interval-interval,topHeight+i*sampleWidth);//负向填充满每一道}}

(4)显示波形:该功能是一个附属功能,主要是在显示剖

面的时候是否显示地震波的波形,这一功能在某一些场合下是非常有用的。

1)显示波形示意图:如图11所示。

2)显示波形实现方法:它的实现方法是定义一个BOOL

类型的开关变量,用于存储显示时候是否显示波形。如果显示,则顺次连接每一个地震道上的每一个采样点就形成了地震波的波形;否则只是有填充的内容,没有边缘上的地震波。

3)显示波形关键代码:

if(!wave)//wave 就是一个开关变量{

int x=20+interval*trace;

int y2=topHeight+i*sampleWidth;int y1=y2-sampleWidth;

painter.drawLine(x+(int)x1,y1,x+(int)x2,y2);//画出这个采样点和下个采样点,循环产生波形}

(5)正逆序显示:剖面默认的显示顺序是正序,即是地震波的编号从小到大,但是在某些特殊情况下需要逆序显示,宏观地看,其实就是顺序的话假设是在剖面的前侧观察剖面,而逆序显示的话观察者是在剖面的后面观察剖面。

1)正逆序显示示意图:如图12所示。

2)正逆序显示实现方法:是在依次画地震波时候,用以

提供数据的函数,先从剖面的后面开始提供数据,让显示函数依次画出,也就达到了逆序显示的功能。如果非逆序显示,提供数据的函数还是从地震剖面的第一个地震波开始直到最后一个地震波结束。

3)正逆序显示关键代码:

if(!sort)//是否逆序显示的开关变量{

for(int k=1;k<=traceNumber;k++)//正序传递数据{

getData(k);

for(int i=1;i

drawP(k,i,data[i-1],data[i],painter);}}else {

for(int k=traceNumber;k>=1;k--)//逆序传递数据{

getData(k);

for(int i=1;i

drawP(traceNumber-k+1,i,data[i-1],data[i],painter);}}

(6)缩放剖面:剖面的放大和缩小在地震资料的处理和解

释中的作用是很明显的,有时候要对某一些细节的地方进行处理的时候必须放大,要宏观地观察一个剖面的时候必须缩小到一定的比例。

1)缩放剖面示意图:如图13所示。

2)缩放剖面实现方法:本程序中的放大和缩小是通过线

形的插值来完成的,

利用QT实现地震数据CST文件的二维显示

当纵向拉伸的时候必须在原来的基础上进

图11

利用QT实现地震数据CST文件的二维显示

显示和隐藏波形

图12正逆序显示

70

2010.3

图形图像处理

行插值,由于线形插值有一定的误差,所以在纵向拉伸方面本系统做了一定的限制,不能超过一定的范围,横向拉伸的话不用进行插值,所以可以拉伸到很宽。

3)缩放剖面关键代码

ScaleDialog*scaleDialog=new ScaleDialog(this);//调用缩放对话框

int flag=scaleDialog->exec();if(flag==1){

interval=scaleDialog->getTrace();//获得用户设置的横向缩放比例

sampleWidth=scaleDialog->getTime ();//获得用户设置的

纵向比例

if(interval<2){

interval=2;//横向缩放比例的控制}

if(sampleWidth<1){

sampleWidth=1;/纵向比例的控制}

scaleDialog->setTrace(interval);

scaleDialog->setTime(sampleWidth);}

normalized();createPixmap();//重新画位图update();//刷新主窗口

(7)保存位图模块:该模块可以在二维地震剖面基本显示以及交互中随时选择保存当前显示的剖面,保存成位图的格式。

1)保存位图示意图:如图14所示。

2)保存位图实现方法:在前期进行显示的时候,就已经

将剖面画在了一个QPixmap 上面,当用户选择保存的时候,只要将QPixmap 对象的指针传递给保存图像的函数,保存图像函数将该对象以位图的形式输出到文件。

3)保存位图关键代码

QString name=QFileDialog::getSaveFileName(this ,tr("保存图像")

,QDir::currentPath()

,tr("bmp 图像(*.bmp)"));//调用保存文件对话框。if(!name.isEmpty()){

pixmap->save(name,"BMP");//以位图的形式保存图片}

5总结

通过完整地解析通用地震勘探数据CST 文件的存储格式,

并且使用跨平台的编程语言QT 去读取其中的振幅数据形成二维地震剖面,在此基础上又实现了二维地震剖面和用户之间的交互显示,最终可以将显示的剖面输出到位图文件。本程序的意义在于可以在任意的微机操作系统中快速地读取地震勘探数据,形成相应的剖面,避免了传统方式的繁琐和局限性。

参考文献

[1]蔡志明.精通QT 编程[M].北京:电子工业出版社,

2008.

[2]Trolltech.Qt Reference Documentation (Open Source Edition).

2007.

(收稿日期:2009-12-21

利用QT实现地震数据CST文件的二维显示

图13

缩放剖面

图14

利用QT实现地震数据CST文件的二维显示

保存位图

!!!!!!!!!!!!!!!!!!!!!"

!!!!!!!!!!!!!!!!!!!!!!!"

!!!!!!!!!!!!!!!!!!!!!"

!!!!!!!!!!!!!!!!!!!!!!!"

微软或推自有品牌手机完成三屏一云战略

美国投资公司Jefferies 分析师凯瑟琳-埃格伯特(Katherine

Egbert)称,就在谷歌与手机厂商宏达电联手发布其Nexus One

手机之际,微软正在与一家手机厂商合作发布自有品牌手机。

该计划好像是微软已致力于的“Pink ”项目,还可能包括收购Danger 工作。

凯瑟琳在致客户报告中称:

“我们与业界的最近沟通显

示,微软将在未来2个月内推自有品牌手机。我们预计微软

会在于2月15-18日召开的全球移动大会(World Mobile

Congress)上或此后1个月举办的CTIA (美国无线通信展)上

推自有品牌手机。”

凯瑟琳称,微软或通过代工厂商发布该款类似于Zune 的

手机。她认为该款手机将配置500万像素摄像头,支持720p 高清视频和音乐订阅/购买服务。此外,凯瑟琳也是一无所知。

她解释说:

“我们并不清楚Pink 手机的成本消息,也不知道

运营商。多年来,来自手机的营收似乎没有多大意义,但Pink 手机将成为微软‘第三块屏’,是其‘三屏一云’战略的

最后组成部分。”

71