使用WPF绘制地震图像

大家好,业余C#、WPF使用者;在这里我写了一个绘制地震波形图的窗体程序,遇到了一些问题:
1.数据量达到1000道左右时,很卡,即使数据量不多,一两百道也要等好一会儿才出结果
2.当该窗口被关闭时,没有释放内存
该函数如下:

readonly FileStream fileStream;
        readonly int nsamples;
        readonly int startTrace;
        readonly int endTrace;
        readonly float interval;
        double[,] datax;
        double[] datay;
        //readonly float maxAmpliude = 1;

        public Waveform(FileStream fileStream, int nsamples, int startTrace, int endTrace, float startTime, float endTime, float interval, int type)
        {
            //判断一下吧
            if (nsamples < 0)
            {
                MessageBox.Show("num. of samples error!");
                return;
            }
            if (startTrace <= 0)
            {
                MessageBox.Show("num. of start trace(s) error!");
                return;
            }
            if (endTrace < startTrace)
            {
                MessageBox.Show("num. of end trace(s) error!");
                return;
            }
            if (startTime < 0)
            {
                MessageBox.Show("num. of start time error!");
                return;
            }
            if (endTime < startTime)
            {
                MessageBox.Show("num. of end time error!");
                return;
            }
            if (interval < 0)
            {
                MessageBoxResult result = MessageBox.Show("num. of sample interval error!\ndo you want to replay it using 1 ms ?", "Requick", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                    interval = 1;
                else if (result == MessageBoxResult.No)
                    return;
            }

            InitializeComponent();

            this.fileStream = fileStream;
            this.nsamples = nsamples;
            this.startTrace = startTrace;
            this.endTrace = endTrace;
            this.interval = interval;

            int startSampl = Convert.ToInt32(startTime / interval);
            int endSampl = Convert.ToInt32(endTime / interval);
            this.datax = new double[endSampl - startSampl + 1, endTrace - startTrace + 1];
            this.datay = new double[endSampl - startSampl + 1];
            for (int i = 0; i < datay.Length; i++)
                datay[i] = startTime + i * interval;

            fileStream.Seek(3600 + (startTrace - 1) * (240 + 4 * nsamples), SeekOrigin.Begin);
            if (type == 1)
                IBMSwap(startSampl, endSampl);
            else if (type == 2)
                IBM(startSampl, endSampl);
            else if (type == 3)
                IEEESwap(startSampl, endSampl);
            else
                IEEE(startSampl, endSampl);

            ChartArea chartArea = new ChartArea();
            ChartPlot_Waveform.ChartAreas.Add(chartArea);

            for (int i = 0; i < endTrace - startTrace + 1; i++)
            {
                Series series = new Series();
                series.ChartType = SeriesChartType.Line;
                series.Color = System.Drawing.Color.Black;

                for (int j = 0; j < datay.Length; j++)
                {
                    series.Points.AddXY(datax[j, i], datay[j]);
                }
                ChartPlot_Waveform.Series.Add(series);
            }


            int traceInterval = (endTrace - startTrace + 1) / 5;


            //ChartPlot_Waveform.Dock = System.Windows.Forms.DockStyle.Fill;
            //设置属性
            ChartPlot_Waveform.ChartAreas[0].AxisX.Title = "Trace(s)";
            ChartPlot_Waveform.ChartAreas[0].AxisX.TitleFont = new System.Drawing.Font("Times new Roman", 8);
            ChartPlot_Waveform.ChartAreas[0].AxisX.Minimum = startTrace - 1;
            ChartPlot_Waveform.ChartAreas[0].AxisX.Maximum = endTrace + 1;
            ChartPlot_Waveform.ChartAreas[0].AxisX.Interval = traceInterval;
            ChartPlot_Waveform.ChartAreas[0].AxisX.MajorGrid.Enabled = false;

            ChartPlot_Waveform.ChartAreas[0].AxisY.IsReversed = true;
            ChartPlot_Waveform.ChartAreas[0].AxisY.Title = "Time / ms";
            ChartPlot_Waveform.ChartAreas[0].AxisY.Minimum = startTime;
            ChartPlot_Waveform.ChartAreas[0].AxisY.Maximum = endTime;
            //ChartPlot_Waveform.ChartAreas[0].AxisY.Interval = interval;
            ChartPlot_Waveform.ChartAreas[0].AxisY.MajorGrid.LineColor = System.Drawing.Color.Gray;
            ChartPlot_Waveform.ChartAreas[0].AxisY.TitleFont = new System.Drawing.Font("Times new Roman", 8);

            ChartPlot_Waveform.ChartAreas[0].BorderDashStyle = ChartDashStyle.Solid;
            ChartPlot_Waveform.ChartAreas[0].BorderColor = System.Drawing.Color.Black;
            ChartPlot_Waveform.Legends.Clear();

            //

        }

调用函数是另外一个窗体的一个事件
调用函数如下:

private void WaveForm_Grapic_Button_Click(object sender, RoutedEventArgs e)
        {
            int k = 0;
            if (Data_IBM_ComboBox.IsSelected == true && Data_CheckBox.IsChecked == true)
                k = 1;
            else if (Data_IBM_ComboBox.IsSelected == true && Data_CheckBox.IsChecked == false)
                k = 2;
            else if (Data_IBM_ComboBox.IsSealed == false && Data_CheckBox.IsChecked == true)
                k = 3;
            else
                k = 4;

            Waveform waveform = new Waveform(fileStraeamOperater, nsamples, Convert.ToInt32(StartTrace.Text),
            Convert.ToInt32(EndTrace.Text), Convert.ToSingle(StartTime.Text), Convert.ToSingle(EndTime.Text), Convert.ToInt32(fileHead[5].Value) / 1000, k);
            waveform.Show();

        }

附一张200道的图吧,以便理解

img

另外:还有两个个问题:
1.我想正相位填充颜色:以第一道为例,其取值范围为0-1,以0.5为界,想将大于0.5的部分填充为纯色
2.绘制颜色图,类似于这样,两张图是同一个数据

img

请各位帮忙解决问题

内容来源与ChatGpt4及newbing和百度:


问题分析:

  1. 数据量达到1000道左右时,很卡,即使数据量不多,一两百道也要等好一会儿才出结果

这个问题可能是由于绘制时使用了较为耗费资源的方式造成的。可以考虑使用DrawingVisual对象进行绘制,DrawingVisual对象比Canvas等容器对象更轻量级,绘制效率更高。

  1. 当该窗口被关闭时,没有释放资源

可以在窗口关闭时,手动释放资源,如绘制所使用的DrawingVisual对象等。

解决思路:

  1. 使用DrawingVisual对象进行绘制,可以通过以下代码创建DrawingVisual对象并添加到Canvas中:
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
    //在DrawingContext中进行绘制
}
canvas.AddVisual(visual);
  1. 在窗口关闭时,手动释放资源,如下:
protected override void OnClosed(EventArgs e)
{
    //释放DrawingVisual对象等资源
    base.OnClosed(e);
}

代码示例:

以下是使用DrawingVisual对象进行绘制的示例代码:

private void DrawData(List<double> data)
{
    if (data == null || data.Count == 0) return;
    //创建DrawingVisual对象并添加到Canvas中
    DrawingVisual visual = new DrawingVisual();
    using (DrawingContext dc = visual.RenderOpen())
    {
        double width = canvas.ActualWidth;
        double height = canvas.ActualHeight;
        double xInterval = width / data.Count;
        double yInterval = height / 2;
        Point start = new Point(0, yInterval);
        for (int i = 0; i < data.Count; i++)
        {
            double y = yInterval - data[i] * yInterval;
            Point end = new Point((i + 1) * xInterval, yInterval - data[i] * yInterval);
            dc.DrawLine(new Pen(Brushes.Black, 1), start, end);
            start = end;
        }
    }
    canvas.AddVisual(visual);
}

在窗口关闭时释放资源的代码示例:

protected override void OnClosed(EventArgs e)
{
    //释放DrawingVisual对象等资源
    canvas.ClearVisuals();
    base.OnClosed(e);
}

祝您问题迎刃而解

个人经验,你可以参考一下
1,首先ChartPlot_Waveform这个控件,你是要调查一下,他可能性能本身就不好
2,WPF本质上性能就是低下的,他比不上winform,更比不上C++,但加载绘制简单的图像,我的经验是10万就撑不住了,2,3万就有点卡了,但控件必须是你自己写的,自己去优化。
3,你现在的代码使用ChartPlot_Waveform.Series.Add(series);添加线,但你添加全是在一个for循环里加的,等于你画面展示时要一起加载,所以这个是你1000条线就卡的主因。目测没有什么特别好的解决办法,你只能试试分批加载数据和寻找一下控件自身有没有缓存的设置,你给开启一下。
4,建议换组件,有个叫LightningChart的组件,收费的,性能非常好,你也可以搜搜其他组件,有一些性能不错的组件

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
针对问题一,数据量大时卡顿的问题,您可以考虑对数据进行分段处理,不一次性全部绘制出来。比如将所有数据分成若干个段,每次只绘制一段数据,等待响应之后再绘制下一段数据。

针对问题二,填充颜色可以通过设置Series的FillColor属性和BorderWidth属性来实现。具体来说,可以将大于0.5的部分绘制成红色,小于0.5的部分不填充色。代码如下:

Series series = new Series();
series.ChartType = SeriesChartType.Line;

for (int i = 0; i < data.Length; i++)
{
    double xValue = i;
    double yValue = data[i];
    series.Points.AddXY(xValue, yValue);
            
    if (yValue >= 0.5)
    {
        DataPoint point = series.Points[i];
        point.BorderColor = Color.Red;
        point.BorderWidth = 2;
        point.Color = Color.FromArgb(100, 255, 0, 0);
    }
}

针对问题三,绘制颜色图可以使用Chart的HeatMap图表类型。HeatMap需要二维数据作为输入,可以将数据的每个时间点作为一个维度,每个道作为另外一个维度。例如可以使用以下代码来绘制颜色图:

double[,] data = GetData(); // 获取二维数据

ChartArea chartArea = new ChartArea();
chartArea.AxisX.Title = "Time";
chartArea.AxisY.Title = "Trace";
chartArea.AxisX.Minimum = 0;
chartArea.AxisX.Maximum = data.GetLength(0);
chartArea.AxisY.Minimum = 0;
chartArea.AxisY.Maximum = data.GetLength(1);
chartArea.AxisX.Interval = 1;
chartArea.AxisY.Interval = 1;
chartArea.AxisX.MajorGrid.Enabled = false;
chartArea.AxisY.MajorGrid.Enabled = false;
ChartPlot_Waveform.ChartAreas.Add(chartArea);

Series series = new Series("HeatMap");
series.ChartType = SeriesChartType.HeatMap;
series["PixelPointWidth"] = "10"; // 设置每个像素点的宽度
series["PixelPointGapDepth"] = "0"; // 设置两个像素点之间的间隙
series["Minimum"] = "0"; // 颜色范围最小值
series["Maximum"] = "1"; // 颜色范围最大值
series.Points.DataBindXY(GetLabels(data.GetLength(0), data.GetLength(1)), GetValues(data));
ChartPlot_Waveform.Series.Add(series);

其中,GetLabels和GetValues分别返回二维数组的横、纵轴标签和数值数组。
如果我的回答解决了您的问题,请采纳!