11

我一直在试图弄清楚在 WPF 中将实时数据呈现为折线图的适当方法是什么。我所说的实时是指从 USB 设备收集的数据,该设备以大约 40Hz 的速率生成数据。我以异步方式以 40Hz 的频率读取多个(最多 7 个)数据流。

我尝试过使用两种现成的解决方案(WPF 工具包图表和剑鱼图表)并且几乎研究了动态数据可视化组件,但在阅读了他们论坛上的一些评论后放弃了它。似乎现成的图表解决方案是针对静态图表的,我实际上需要类似于 Windows 任务管理器的东西——只是速度更快,数据点也更多。

目前,我推出了自己的解决方案,目前看来效果最好,但我感觉我错过了一些东西,因为我似乎应该能够从中获得更好的性能。

要求是它应该能够在滑动窗口中处理大约 10000 个点的恒定范围——当新数据进入(40Hz)时,旧数据被推到可见范围之外的左侧。并且它需要将这个速率维持至少 20 - 30 分钟(每个数据流总共大约 75 - 10 万个点)。

我当前的自定义实现是基于从 Shape 继承并使用 StreamingGeometry 作为 DefinigGeometry 的组件。由于固有的“突发效应”,从设备传入的数据通过队列传递给组件以提高性能,并且在出队操作后,组件将失效。

所以,我的问题是,我是在正确的道路上还是我完全错了?在 WPF 中完成此类数据可视化的最有效方法是什么?任何帮助或提示将不胜感激。

4

4 回答 4

5

如前所述,执行此操作的 WPF“标准”方式不会为您提供所需的性能。在尝试了几种不同的免费和商业产品但没有得到我需要的东西后,我开始尝试以下方法:

  1. 使用 WPF 几何。
  2. 使用 Direct2D。

他们两个都没有表现。

我知道的一件事是 WPF 擅长渲染图像(BitmapSource),所以我决定朝那个方向前进并使用 WriteableBitmapEx 在 WriteableBitmap 上绘制图形,然后将其传递给...

WriteableBitmapEx 库的一个问题是它没有很多绘图功能,例如 GDI。那么为什么不直接使用 GDI 呢?如果你做对了,没有区别。

例子:

    public void BeginDraw()
    {
        _writeable_bitmap = new WriteableBitmap((int)Math.Max(_size.Width, 1), 
        (int)Math.Max(_size.Height, 1), 96.0, 96.0, PixelFormats.Pbgra32, null);

        _gdi_bitmap = new System.Drawing.Bitmap(_writeable_bitmap.PixelWidth, 
        _writeable_bitmap.PixelHeight,_writeable_bitmap.BackBufferStride,
        System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
        _writeable_bitmap.BackBuffer);

        _writeable_bitmap.Lock();

        _g = System.Drawing.Graphics.FromImage(_gdi_bitmap);
        _g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

        _g.Clear(System.Drawing.Color.Transparent);
    }

    public void DrawSeries(IEnumerable<System.Drawing.PointF> points)
    {
        _g.DrawCurve(dataSeries.GdiPen, points.ToArray());
    }

    public void EndDraw()
    {
        _writeable_bitmap.AddDirtyRect(new Int32Rect(0, 0, 
        _writeable_bitmap.PixelWidth, _writeable_bitmap.PixelHeight));
        _writeable_bitmap.Unlock();

        var cloned = _writeable_bitmap.Clone();
        cloned.Freeze();

        Dispatcher.BeginInvoke(new Action((() =>
        {
            Image = cloned;
        })));

        _g.Dispose();
    }

如您所见,我正在使用“特殊技巧”将图形直接绘制到使用 GDI 的本机 WPF BitmapSource。

我不确定这是不是正确的测量方法,但使用这种方法我已经设法获得了一些非常不错的结果:

  • 超过 10 亿个数据点。
  • 刷新率为5FPS。
  • 以 100hz 推送 10,000 个新数据点。
  • i7 Core 笔记本电脑 10% 的 CPU。
  • WPF UI 和渲染线程完全可以自由地执行其他操作。

我正是这样做的开源库 RealTimeGraphX(用于 WPF 和 UWP)的作者。 https://github.com/royben/RealTimeGraphX

除实际线系列外,其余图形组件均使用标准 WPF 控件和形状制作,因此可以轻松自定义和操作。

于 2019-02-08T14:46:52.953 回答
4

披露:我拥有 ABT 软件并开发了SciChart ,并为WriteableBitmapEx开源库做出了贡献

不幸的是,你没有错过任何东西。WPF / Silverlight 中的保留模式渲染引擎为此类工作提供了较差的性能。我曾在许多从 Windows 窗体升级到 WPF 的系统上工作过,在这些系统中,客户端对这个“GPU 加速”框架的渲染性能感到非常失望!

无论如何,有办法。使用即时模式渲染。查看 WriteableBitmap 或 InteropBitmap 类。有一个优秀的开源库,名为WriteableBitmapEx,作者是Rene Schulte,我为它做出了贡献。WriteableBitmapEx 提供了一些低级绘图函数(GDI 风格),用于直接绘制位图。这提供了出色的性能和低内存占用(是的,MS 的奇特框架被几个优化良好的 for 循环和指向字节数组的指针击败)。

如果它是您正在寻找的特定第三方图表组件,请尝试SciChart。SciChart 是我自己开发的一个组件,旨在填补超高性能 WPF 或 Silverlight 科学/股票图表的空白。它使用专有的重采样算法在绘图、立即模式渲染和许多其他优化(如对象池和资源重用)之前减少数据集,从而为非常大的数据集提供平滑的刷新率和低内存占用。

单击上面链接上的性能演示(需要 Silverlight 4)。目前 SciChart 能够以大约 5FPS(取决于目标硬件)的速度渲染 1,000,000 个数据点,相当于每秒 5,000,000 个数据点。商业许可证将于 2012 年第一季度提供。

于 2011-12-21T12:19:58.923 回答
3

通过低级 DirectX 编程和使用 HLSL 着色器可以获得最佳结果。当最大性能和实时需求很重要时,应该立即忘记基于 System.Windows.Media 命名空间的渲染。

我们能够开发出可以绘制超过 10 亿个数据点的例程,例如 8 个数据馈送 x 125 M 个数据点,使用宽线,没有下采样。这些例程是LightningChart、 WPF 图表(和 WinForms 图表)的一部分。我们花了大约 7 年的时间才达到这一点……我们做了十亿分的例子,包括 VS 项目和 Youtube 视频。

[我是 LightningChart 的技术负责人]

于 2016-08-30T21:39:19.297 回答
2

WPF 的保留模式渲染使得绘制/重绘自定义图表和图像变得难以提高性能,尤其是当这些绘图包含大量对象时。

我在 WPF 中能够完成的最快绘图是使用 WritableBitmap 并通过调用 WritePixels 来填充它,这可能是您的一个选择。它大大超过了我使用 PathGeometry 编写的图表并绘制到 Canvas 的绘制速度。

我有兴趣看看是否有更快的中间立场。

于 2010-03-25T17:44:39.233 回答