一、引言
在地震勘探领域,SEGY(Society of Exploration Geophysicists Y-data)文件格式是常见的地震数据存储格式。对于地震数据的可视化,通常会将 SEGY 文件中的振幅数据通过图像进行展示,以便进行分析。本文将介绍如何使用 C# WPF 应用程序绘制基于 SEGY 数据的二维地震图。
二、需求
我们将从 SEGY 文件中读取地震数据,提取振幅信息,并通过色卡将这些振幅映射到颜色空间中。最终,我们将在 WPF 应用中生成二维地震图,绘制出不同时间步长和采样点的地震波形。
2.1 Segy文件格式简介
EGY 是一种用于存储地震数据的标准文件格式,包含头部信息和多个地震数据轨迹。每个轨迹包含一系列时间采样点的振幅值。绘制地震图时,我们需要读取这些轨迹,并将其显示为图像。
2.2准备工作
首先,我们需要安装和配置 SEGY 文件的读取工具。为了简化这个过程,我们假设你已经有了一个 SegyReader
类,它能从 SEGY 文件中读取轨迹数据。
三、WPF绘制二维地震图
在本项目中,我们将使用 WPF 和 WriteableBitmap
来绘制二维地震图。WriteableBitmap
允许我们直接操作像素数据,因此非常适合此类任务。
3.1读取SEGY文件并提取数据
我们使用一个 SEGY 文件读取器 (SegyReader
) 来读取 SEGY 文件中的数据。以下是读取文件头和轨迹数据的代码:
var reader = new SegyReader(); var filePath = @"D:\Code\python\Study\Segy\Inline790.segy"; var header = reader.ReadTextHeader(filePath); // 读取文本头部 ISegyFile line = reader.Read(filePath); // 读取地震数据 ITrace trace = line.Traces[0]; int traceCount = line.Traces.Count; // 控制绘图宽度,防止越界 int sampleCount = line.Traces[0].Values.Count; // 控制绘图高度,防止越界
解析Segy数据文件,参考来源:https://github.com/jfoshee/UnpluggedSegy
3.2色卡映射
为了将振幅值转换为颜色,我们使用一个色卡。色卡是一个预定义的图像文件,其中每个像素代表一种振幅值对应的颜色。通过将振幅值归一化到色卡的高度范围内,我们可以将振幅映射到色卡上的颜色。
// 色卡 Bitmap colorRamp = Properties.Resources.ColorRamp; int colorRampHeight = colorRamp.Height; // 1023 int colorRampWidth = colorRamp.Width; // 45
3.3计算振幅范围
我们需要遍历所有的轨迹,计算振幅的最小值和最大值。这将帮助我们将振幅数据归一化到 0 到 1 的范围。
// 获取振幅的最小值和最大值,以便后续映射 float minAmplitude = float.MaxValue; float maxAmplitude = float.MinValue; foreach (var itemTrace in line.Traces) { foreach (var amplitude in itemTrace.Values) { if (amplitude < minAmplitude) minAmplitude = amplitude; if (amplitude > maxAmplitude) maxAmplitude = amplitude; } }
3.4创建图像并绘制数据
接下来,我们将使用 WriteableBitmap
来创建图像并绘制每个像素的颜色。对于每个采样点,我们使用色卡来获取对应的颜色,并将其写入图像数据。
WriteableBitmap bitmap = new WriteableBitmap(traceCount, sampleCount, 192, 192, PixelFormats.Bgra32, null); byte[] pixelData = new byte[traceCount * sampleCount * 4]; // BGRA 格式 for (int t = 0; t < traceCount; t++) { for (int s = 0; s < sampleCount; s++) { float amplitude = line.Traces[t].Values[s]; // 归一化振幅到 [0, 1] 范围 double normalizedAmplitude = (amplitude - minAmplitude) / (maxAmplitude - minAmplitude); // 映射到色卡的高度 int colorIndex = colorRampHeight - 1 - (int)(normalizedAmplitude * (colorRampHeight - 1)); colorIndex = Clamp(colorIndex, 0, colorRampHeight - 1); // 防止越界 // 获取对应的颜色 System.Drawing.Color color; lock (colorRamp) { color = colorRamp.GetPixel(colorRampWidth / 2, colorIndex); // 使用色卡的中间列 } // 写入像素数组 (BGRA 格式) int pixelIndex = (s * traceCount + t) * 4; pixelData[pixelIndex + 0] = color.B; // 蓝色 pixelData[pixelIndex + 1] = color.G; // 绿色 pixelData[pixelIndex + 2] = color.R; // 红色 pixelData[pixelIndex + 3] = 255; // 不透明度 } } // 写入图像数据 bitmap.WritePixels(new Int32Rect(0, 0, traceCount, sampleCount), pixelData, traceCount * 4, 0); SeismicImage.Source = bitmap; // 将图像显示在界面
3.5防止越界
在获取色卡颜色时,我们使用了 Clamp
函数来防止数组越界。这确保了我们不会超出色卡的高度范围。
private void DrawSeismicData() { var reader = new SegyReader(); var filePath = @"D:\Code\python\Study\Segy\Inline790.segy"; var header = reader.ReadTextHeader(filePath); ISegyFile line = reader.Read(filePath); ITrace trace = line.Traces[0]; int traceCount = line.Traces.Count; // 控制绘图宽度,防止越界 int sampleCount = line.Traces[0].Values.Count; // 控制绘图高度,防止越界 //色卡 System.Drawing.Bitmap colorRamp = Properties.Resources.ColorRamp; int colorRampHeight = colorRamp.Height; // 1023 int colorRampWidth = colorRamp.Width; // 45 WriteableBitmap bitmap = new WriteableBitmap(traceCount, sampleCount, 192, 192, PixelFormats.Bgra32, null); // 创建一个像素数组,BGRA 格式 byte[] pixelData = new byte[traceCount * sampleCount * 4]; // 获取振幅的最小值和最大值,以便后续映射 float minAmplitude = float.MaxValue; float maxAmplitude = float.MinValue; foreach (var itemTrace in line.Traces) { foreach (var amplitude in itemTrace.Values) { if (amplitude < minAmplitude) minAmplitude = amplitude; if (amplitude > maxAmplitude) maxAmplitude = amplitude; } } for (int t = 0; t < traceCount; t++) { for (int s = 0; s < sampleCount; s++) { float amplitude = line.Traces[t].Values[s]; // 1. 使用原始振幅值直接映射到色卡的高度 // 例如,假设振幅的范围为 [minAmplitude, maxAmplitude],则将其映射到色卡的高度 double normalizedAmplitude = (amplitude - minAmplitude) / (maxAmplitude - minAmplitude); // 归一化振幅到 [0, 1] 范围 // 2. 将归一化的振幅映射到色卡的高度 int colorIndex = colorRampHeight - 1 - (int)(normalizedAmplitude * (colorRampHeight - 1)); colorIndex = Clamp(colorIndex, 0, colorRampHeight - 1); // 防止越界 // 3. 从色卡图像中获取对应的颜色 System.Drawing.Color color; lock (colorRamp) { color = colorRamp.GetPixel(colorRampWidth / 2, colorIndex); // 使用色卡的中间列 } // 4. 将颜色写入像素数组 (BGRA 格式) int pixelIndex = (s * traceCount + t) * 4; pixelData[pixelIndex + 0] = color.B; // 蓝色 pixelData[pixelIndex + 1] = color.G; // 绿色 pixelData[pixelIndex + 2] = color.R; // 红色 pixelData[pixelIndex + 3] = 255; // 不透明度 } } // 写入图像数据 bitmap.WritePixels(new Int32Rect(0, 0, traceCount, sampleCount), pixelData, traceCount * 4, 0); SeismicImage.Source = bitmap; this.ColorImage.Source= ConvertToImageSource(colorRamp); } public static int Clamp(int value, int min, int max) { if (value < min) return min; if (value > max) return max; return value; } /// <summary> /// System.Drawing.Bitmap 转换为 ImageSource /// </summary> /// <param name="bitmap"></param> /// <returns></returns> public static BitmapImage ConvertToImageSource(System.Drawing.Image image) { if (image == null) return null; using (var memoryStream = new MemoryStream()) { image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); // 保存为 PNG 格式 memoryStream.Position = 0; var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.StreamSource = memoryStream; bitmapImage.EndInit(); bitmapImage.Freeze(); // 冻结以便跨线程使用 return bitmapImage; } }
五、效果图
六、总结
本文演示了如何使用 C# WPF 绘制二维地震图,结合 SEGY 文件格式和色卡映射。通过读取 SEGY 文件的轨迹数据并对振幅进行归一化,我们可以将其映射到色卡的颜色值,并将其呈现在 WPF 图像控件中。这种方法不仅能有效地展示地震数据的振幅变化,也为地震勘探数据的可视化分析提供了强有力的支持。
标签:二维地震,SEGY,C#,振幅,int,色卡,amplitude,traceCount From: https://www.cnblogs.com/xu53105656/p/18651716