C/C++讀取SEGY文件(三)


SEGY IO (IBM&PC)

SEGY是地震勘探存儲數據的標准格式,最早在1975年發布revision 0。由於那時候地震數據的處理一般是在服務器上進行的,當時SEGY文件采用大字節序,數據點是IBM 32位浮點數。2002年發布的版本revision 1增加了IEEE 32位浮點數,但還是采用大字節序。IBM 32位與IEEE 32位的差異主要在於浮點的組成不同:

大字節序(Big Endian)是指其最高有效字節落在低地址的存儲方式,而小字節序正好相反:

PC的處理器都是小字節序的,因此在PC上讀取SEGY格式的文件時,必須進行浮點數的轉換與字節序交換,才能正確地表示為PC上的數。

本文檔將介紹SEGY的讀取與寫入過程,其中包括IBMPC兩種數據格式的轉換。

程序將調用IEEE2IBM.cpp文件完成IBM與PC格式的互相轉換。

新建頭文件ReadSeismic.h與C++文件ReadSeismic.cpp,以及主程序main.cpp

1 頭文件ReadSeismic.h的編寫及其規范

1.1 程序描述、調用、聲明、定義

/**********************************************************************

 * Copyright(C) 2018,Company All Rights Reserved   (1)版權說明
   *
 * @file    : ReadSeismic.cpp                      (2) 文件名
   *
 * @brief   :  實現地震數據的讀、寫操作                 (3) 該文件主要功能簡介
   *
 * @version : 1.0                                  (4) 版本信息
   *
 * @author  : Fan XinRan                           (5) 創建作者
   *
 * @date    : 2022/2/8 星期二                       (6) 創建時間
   *
 * Others  :                                       (7) 備注、改動信息等
   **********************************************************************/
  
//調用需要的C頭文件
#include<stdio.h>   //C Language header file  
#include<stdlib.h>
#include<string.h>
#include<math.h>

//調用需要的C++頭文件
#include<iostream>  // C++ header file
#include<vector>
#include<algorithm>

//調用非標准庫
#include"alloc.h"   // 用於創建多維數組
#include"segy.h"   // 包含segy與bhed結構體,用於提取卷頭和道頭中采集、存儲的信息

// 定義全局變量及命名空間
#define PI 3.141592654   //Constant Number Definition
#define EPS 0.0000001

using namespace std;

1.2 聲明函數

unsigned short exchangeLowHigh16(unsigned short Data_temp);//16位高低位轉換函數  short占2字節,2*8
unsigned int exchangeLowHigh32(unsigned int Data_temp); //32位高低位轉換函數  4*8
float ibm2pc(unsigned int Data_temp);      //IBM轉PC數據
unsigned int pc2ibm(float input);          //PC轉IBM數據
float ieee2pc(unsigned int Data_temp);   //IEEE轉為PC

void trace_ibm2pc(float *data_output, int *data_input, int nt); //地震道數據由IBM轉換為PC格式
void trace_pc2ibm(float *data_input, int *data_output, int nt); //地震道數據由PC轉換為IBM格式

bool copySeismicDataIBM(const char *filenameInput, const char *filenameOutput); //Copy seismic data from Inputfile to Outputfile

2 C++文件ReadSeismic.cpp的編寫及其規范

2.1 必要的說明

/*************************************************************************************************************

 Function:       copySeismicDataIBM                                                (1)函數名
 Description:    copy segy file from input data to output data                     (2)簡要描述其功能
 Input:          
                 const char *filenameInput [in]    input filename (.segy)          (3)輸入變量及輸入文件類型
 Output:         
                 const char  *filenameOutput[out]  output filename (.segy)         (4)輸出變量及輸出文件類型
 Return:         
             bool  true    program success
			 bool  false   program failed                                          (5)返回值及其說明
 Author:     Fan XinRan                                                            (6)創建作者
 Date  :     2022/2/8                                                              (7)創建時間
 Others:                                                                           (8)備注、改動信息等
 
*************************************************************************************************************/

2.2 定義讀、寫函數

#include "ReadSeismic.h"
bool copySeismicDataIBM(const char *filenameInput, const char *filenameOutput){

    //實現代碼        
    ...

}

(1)定義待使用的結構體變量、數值型變量

bhedsegy均為定義在segy.h中的結構體(structure),分別包含了二進制卷頭信息與道頭信息,使用成員訪問運算符(.)獲取其內容;

使用unsigned int 聲明整型變量,使用long long__int64聲明SEGY文件字節數、地震道字節數,防止數據量超出范圍,並且盡可能初始化變量。

bhed fileheader;        // file header  卷頭
segy traceheader;       // trace header  道頭
unsigned int nt=0;        // number of sample  采樣點數
unsigned int sizefileheader=sizeof(fileheader);   // size of fileheader;
unsigned int sizetraceheader=sizeof(traceheader);  // size of traceheader;

unsigned int ncdp = 0;      // number of cdp  道數
long long  size_file = 0;   //size of input file
long long  size_trace = 0;  //size of per-trace

(2)新建指針變量

在讀、寫地震道數據這一任務中,需要用到輸入指針、輸出指針、地震道數據指針、道頭指針以及一個臨時指針變量,共五個指針變量。

FILE *fpinput = NULL;   // input file pointer
FILE *fpoutput = NULL;   // output file pointer
float *dataInput = NULL;  // input data pointer
segy *traceheaderArray = NULL; // traceheader pointer
int* temp = NULL;  // temp pointer 

之前的任務中fread(dataInput[itrace],nt * sizeof(float),1,fpinput)

整個讀、寫流程為:fpinput-->讀取nt個數據-->dataInput[itrace]-->寫入nt個數據-->fpoutput

本次任務中需要對數據進行變換,流程變為:fpinput-->讀取nt個數據-->temp-->IBM/PC轉換-->dataInput[itrace]-->寫入nt個數據-->fpoutput

(3)打開輸入、輸出文件指針

fpinput = fopen(filenameInput, "rb");  //open input file pointer 
fpoutput = fopen(filenameOutput,"wb");  //open output file pointer

//讀寫操作
...
    
//fopen()與fclose()成對出現,在對文件的操作完成后切記關閉文件
fclose(fpinput);  //close input file pointer
fclose(fpoutput); //close output file pointer

(4)判斷文件打開情況

if(fpinput==NULL){                                            //如果文件指針為NULL
	printf("Cannot open %s file\n", filenameInput);           //打印“文件打開失敗”
	return false;                                             //結束程序
}

if(fpoutput==NULL){
	printf("Cannot open %s file\n", filenameOutput);
	return false;
}

(5)讀取/計算卷、道信息

fread(&fileheader,sizefileheader,1,fpinput); // 從輸入流(fpinput)中讀取卷頭信息到指定地址---->fileheader

nt = exchangeLowHigh16(fileheader.hns) // 高低位轉換

_fseeki64(fpinput,0,SEEK_END);   // 從文件末尾偏移這個結構體0個長度給文件指針fpinput,即fpinput此時指向文件尾

size_file = _ftelli64(fpinput);  // 返回當前文件位置,即文件總字節數
size_trace = nt*sizeof(float)+sizetraceheader;  // 每一道的字節數 = 采樣點字節數+道頭字節數

ncdp = (size_file - (long long)sizefileheader)/size_trace; // 道數 = (文件總字節數 - 卷頭字節數)/每一道的字節數

_fseeki64(fpinput,sizefileheader,SEEK_SET); // 從文件開頭偏移sizefileheader(卷頭字節數)個長度給指針fpinput,即fpinput此時指向第一道的開始
fwrite(&fileheader, sizefileheader, 1, fpoutput); // 先寫入卷頭
  1. fread() 從給定流讀取數據到指針所指向的數組中;
  2. fwrite(*ptr, size, nmemb,*stream) 參數與fread()相同,把ptr所指向的數組中的數據寫入到給定流stream中;
  3. _fseeki64的用法與fseek相同,表示從文件指定位置偏移一定字節數;前者具有更高的兼容性;
  4. _ftelli64ftell同理,返回給定流的當前文件位置;
  5. exchangeLowHigh16()完成short型的高低位轉換,int型的高低位轉換使用exchangeLowHigh64()

(6)遍歷每一條地震道,讀、寫數據

dataInput=alloc2float(nt,ncdp); // 分配nt*ncdp(采樣點數×道數)所需的內存空間,用來存放二維地震道數據
//其中,alloc2float是卸載alloc.h中的函數,創建一個float型的二維數組
//dataInput為二級指針,可簡記為dataInput指向每一行的開頭

memset(dataInput[0], 0, nt*ncdp * sizeof(float)); // 從第一行的開頭開始,將內存塊中nt*ncdp個字符賦值為0
// dataInput指向每行開頭,而dataInput[0]則為整個二維數組的起始位置

// 在內存的動態存儲區中分配ncdp個長度為sizetraceheader的連續空間
traceheaderArray = (segy*)calloc(ncdp,sizetraceheader);

temp = (int*)calloc(nt,sizeof(int));

//逐道讀取道頭與地震道數據
for(int itrace = 0; itrace < ncdp; itrace++){
    fread(&traceheaderArray[itrace],sizetraceheader,1,fpinput); // &traceheaderArray[itrace]為第itrace道的地址,讀取該道頭信息
    fread(temp,nt * sizeof(float),1,fpinput); // 讀取nt個采樣點的信息並將結果指向temp
    
    // 使用trace_ibm2pc將temp位置后nt個采樣點的數據,進行IBM-->PC的轉換,結果指向每一道開頭(dataInput[itrace])
    trace_ibm2pc(dataInput[itrace], temp, nt); 
    
}//end for(int itrace = 0; itrace < ncdp; itrace++)

//逐道寫入道頭與地震道數據
for (int itrace = 0; itrace < ncdp; itrace++) {
    fwrite(&traceheaderArray[itrace], sizetraceheader, 1, fpoutput); // 寫入該道頭信息
    
    // 使用trace_pc2ibm將temp位置后nt個采樣點的數據,進行PC-->IBM的轉換,結果指向每一道開頭(dataInput[itrace])
    trace_pc2ibm(dataInput[itrace],temp,nt); 
    
    fwrite(temp, nt * sizeof(int), 1, fpoutput); // 以IBM的格式存回fpoutput
}//end for(int itrace = 0; itrace < ncdp; itrace++)
 // 在每個循環末尾的"}"添加備注,便於尋找和區分

//在寫操作完成后釋放內存
free(temp); // free temp pointer
free(traceheaderArray); // free traceheader pointer
free2float(dataInput);  // free data input pointer
  1. calloc(num,size):在內存的動態存儲區中分配num個長度為size的連續空間,函數返回一個指向分配起始地址的指針;如果分配不成功,返回NULL;
  2. malloc(size):功能與calloc() 相似,不同之處是malloc() 不會將內存值初始化為0,而 calloc()會將新申請的內存填充0。

3 主函數main.cpp及運行結果

#include"ReadSeismic.h"

void main(){

	copySeismicDataIBM("Azi6-Ang35-BZ19-6-1.segy","Outputibm.segy");

}

運行主函數后,程序會讀入Azi6-Ang35-BZ19-6-1.segy,這是一個IBM格式的數據。再寫入到Outputibm.segy,從而完成對SEGY文件的復制。

完整代碼

I ReadSeismic.h

/**********************************************************************

 * Copyright(C) 2018,Company All Rights Reserved   
   *
 * @file    : ReadSeismic.h               
   *
 * @brief   :  實現地震數據的讀、寫操作                 
   *
 * @version : 1.0                                  
   *
 * @author  : Fan XinRan                          
   *
 * @date    : 2022/2/8 星期二                       
   *
 * Others  :                                       
   **********************************************************************/

//(1)調用需要的C頭文件
#include<stdio.h>   // C Language header file  
#include<stdlib.h>
#include<string.h>
#include<math.h>

//(2)調用需要的C++頭文件
#include<iostream>  // C++ header file
#include<vector>
#include<algorithm>

//(3)調用需要的非標准庫頭文件
#include"alloc.h"   // project header file 
#include"segy.h"
#include

//(4)定義全局常量
#define PI 3.141592654   // Constant Number Definition
#define EPS 0.0000001

//(5)聲明命名空間
using namespace std;

//(6)聲明函數名、輸入、輸出及其類型
unsigned short exchangeLowHigh16(unsigned short Data_temp);//16位高低位轉換函數  short占2字節,2*8
unsigned int exchangeLowHigh32(unsigned int Data_temp); //32位高低位轉換函數  4*8
float ibm2pc(unsigned int Data_temp);      //IBM轉PC數據
unsigned int pc2ibm(float input);          //PC轉IBM數據
float ieee2pc(unsigned int Data_temp);   //IEEE轉為PC

void trace_ibm2pc(float *data_output, int *data_input, int nt); //地震道數據由IBM轉換為PC格式
void trace_pc2ibm(float *data_input, int *data_output, int nt); //地震道數據由PC轉換為IBM格式

bool copySeismicDataIBM(const char *filenameInput, const char *filenameOutput); //Copy seismic data from Inputfile to Outputfile

II ReadSeismic.cpp

/*****************************************************************************
 Function:       CopySeismicData                                              
 Description:    copy segy file from input data to output data                   
 Input:          
                 const char *filenameInput [in]    input filename (.segy)         
 Output:         
                 const char  *filenameOutput[out]  output filename (.segy)        
 Return:         
             bool  true    program success
			 bool  false   program failed                                         
 Author:     Fan XinRan                                                         
 Date  :     2022/2/8                                                             
 Others:                                                                           
*****************************************************************************/
#include "ReadSeismic.h"

bool copySeismicDataIBM(const char *filenameInput, const char *filenameOutput){

	segy traceheader;       // trace header 
	bhed fileheader;        // file header
	unsigned int nt = 0;        // number of sample
	unsigned int sizetraceheader = sizeof(traceheader);  // size of traceheader;
	unsigned int sizefileheader = sizeof(fileheader);   // size of fileheader;
	unsigned int ncdp = 0;     // number of cdp
	long long   size_file = 0;  //size of input file
	long long  size_trace = 0;  //size of per-trace

	FILE *fpinput = NULL;   // input file pointer
	FILE *fpoutput = NULL;   //output file pointer
	float **dataInput = NULL;  //input data pointer
	segy *traceheaderArray = NULL; //
	int* temp = NULL;

	fpinput = fopen(filenameInput, "rb");  //open input file pointer 
	fpoutput = fopen(filenameOutput, "wb");  //open output file pointer


	if (fpinput == NULL) {
		printf("Cannot open %s file\n", filenameInput);
		return false;
	}

	if (fpoutput == NULL) {
		printf("Cannot open %s file\n", filenameOutput);
		return false;
	}

	fread(&fileheader, sizefileheader, 1, fpinput);

	nt = fileheader.hns;
	nt = exchangeLowHigh16(fileheader.hns);
	_fseeki64(fpinput, 0, SEEK_END);
	size_file = _ftelli64(fpinput);
	size_trace = nt * sizeof(float) + sizetraceheader;

	ncdp = (size_file - (long long)sizefileheader) / size_trace;

	_fseeki64(fpinput, sizefileheader, SEEK_SET);


	dataInput = alloc2float(nt, ncdp);
	memset(dataInput[0], 0, nt*ncdp * sizeof(float));
	traceheaderArray = (segy*)calloc(ncdp, sizetraceheader);

	temp = (int*)calloc(nt,sizeof(int));


	fwrite(&fileheader,sizefileheader,1, fpoutput);
	for (int itrace = 0; itrace < ncdp; itrace++) {

		fread(&traceheaderArray[itrace], sizetraceheader, 1, fpinput);
		fread(temp, nt * sizeof(int), 1, fpinput);
		trace_ibm2pc(dataInput[itrace], temp, nt);
	}//end for(int itrace = 0; itrace < ncdp; itrace++)

	for (int itrace = 0; itrace < ncdp; itrace++) {
		fwrite(&traceheaderArray[itrace], sizetraceheader, 1, fpoutput);
		trace_pc2ibm(dataInput[itrace],temp,nt);
		fwrite(temp, nt * sizeof(int), 1, fpoutput);
	}//end for(int itrace = 0; itrace < ncdp; itrace++)


	free(temp);
	free(traceheaderArray);
	free2float(dataInput);  // free data input pointer
	fclose(fpoutput); //close output file pointer
	fclose(fpinput);  //close input file pointer
	return true;
}

III IEEE2IBM.cpp

#include"ReadSeismic.h"

unsigned short exchangeLowHigh16(unsigned short Data_temp)//16位高低位轉換函數  short占2字節,2*8
{
        unsigned short result;
        unsigned short temp1=Data_temp>>8;//右移位
        unsigned short temp2=Data_temp<<8;
        result=temp1^temp2;
        return result;//返回高低位轉換結果
}


unsigned int exchangeLowHigh32(unsigned int Data_temp)//32位高低位轉換函數  4*8
{
        signed int *pp=(signed int *)&Data_temp;//讀取Data_temp地址
        char *b;
        b=(char *)pp;
        unsigned char temp = b[0]; //8位為單位
        b[0] = b[3];	b[3] = temp;
        temp = b[1];	b[1] = b[2];	b[2] = temp;
        unsigned int result=*pp;
        return result;//返回高低位轉換結果
}


float ibm2pc(unsigned int Data_temp)//IBM轉PC數據
{
        signed int *pp=(signed int *)&Data_temp;//無符號整型方式讀取Data_temp地址
        char *b;
        b=(char *)pp;
        unsigned char temp = b[0]; //8位為單位
        b[0] = b[3];	b[3] = temp;
        temp = b[1];	b[1] = b[2];	b[2] = temp;
        double sign = (double)( Data_temp >>31) ; //取符號位
        double exp =(double )( ( Data_temp &0x7f000000)  >>24) ;
        exp = exp - 64 ;
        double frac1 = (double )( Data_temp &0x00ffffff  ) ;
        double frac = frac1/ (pow(2.0,24) );
        float result;
        result = (float)(( 1-2*sign)*( pow( 16 ,exp) ) *frac);
        return result;  //返回PC格式數據
}

unsigned int pc2ibm(float input)//PC轉IBM數據
{

    if(input==0)
    {
        unsigned int result;
        result=0;
        return result;
    }
    else
    {
        int sign;//符號位
        sign =   ( input<0?1:0 ) ;
        int exp;//
        float input1 ; // attention : cannot use   long input1;
        input   = input * pow(-1.0, sign);//
        exp=0;
        input1 = input;
        if (input >0 )    //
        {
                if( (int)input>0)//	sgyReadWrite();
                {
                        exp++;
                        while   ((int) input1/16 > 0)
                        {
                                exp++;
                                input1= input1/16;
                        }
                }
                else
                {
                        while ( (int)input1*16 ==0)
                        {
                                exp--;
                                input1=input1*16;
                        }
                        exp++;//
                }
        }
        int e;
        e = (   exp + 64 ) ;
        double     fm = input * pow(16.0,-exp);////////////////
        int fmant=(int) (   fm * pow(2.0,24) ) ;//
        unsigned int result ;
        result = ( sign<<31) | (   e <<24   )    |   fmant ;
        char *b;
        //unsigned int *b;
        b=(char*)&result;
        unsigned char temp = b[0];//8位為單位
        b[0] = b[3];	b[3] = temp;
        temp = b[1];	b[1] = b[2];	b[2] = temp;
        return result;//返回IBM格式數據
    }
}

float ieee2pc(unsigned int Data_temp) //IEEE轉為PC
{
        signed int *pp=(signed int *)&Data_temp;//無符號整型方式讀取Data_temp地址
        char *b;
        b=(char *)pp;
        unsigned char temp = b[0]; //8位為單位
        b[0] = b[3];	b[3] = temp;
        temp = b[1];	b[1] = b[2];	b[2] = temp;
        double sign = (double)( Data_temp >>31) ; //取符號位
        int e;//
        e=(int)((Data_temp & 0x7f800000 )>>23)-127;
        unsigned int x ; //
        x = (unsigned int)((Data_temp & 0x007fffff) -sign); //- sign : for value < 0
        float x0 ;
        x0 = x* pow(2.0,-23);
        float result;
        if ( x0 ==0 && e + 127 ==0 ) //
                result = 0;
        else
                result = pow(-1.0,sign)*(1+x0)*pow(2.0,e);
        return result;//返回PC格式數據
}


void trace_ibm2pc(float *data_output, int *data_input, int nt) {

	for (unsigned int it = 0; it < nt; it++) {
		data_output[it] = ibm2pc(data_input[it]);
	}
}

void trace_pc2ibm(float *data_input, int *data_output, int nt) {

	for (unsigned int it = 0; it < nt; it++) {
		data_output[it] = pc2ibm(data_input[it]);
	}
}

IV main.cpp

#include"ReadSeismic.h"

void main(){

	copySeismicDataIBM("Azi6-Ang35-BZ19-6-1.segy","Outputibm.segy");

}

參考:

符茂松.32位IEEE和IBM浮點數結構及其轉換方法[J].工程地球物理學報,2011,8(06):759-766.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM