【问题标题】:Most performant way to graph thousands of data points with WPF?使用 WPF 绘制数千个数据点的最高效方法?
【发布时间】:2009-06-04 19:35:58
【问题描述】:

我编写了一个显示财务数据的图表。当我使用PathGeometry 以及PathFigureLineSegments 绘制少于10.000 个点时,性能很好。但现在我需要同时显示多达 100.000 个点(无需滚动),而且 50.000 个点已经很慢了。我在想StreamGeometry,但我不确定,因为它与将信息存储为字节流的PathGeometry 基本相同。有没有人有想法让这个性能更高,或者也许有人已经做了类似的事情?

编辑:这些数据点一旦绘制就不会改变,所以如果有可能对其进行优化,请告诉我(线段现在被冻结)。

编辑:我尝试了 StreamGeometry。由于某种原因,创建图形需要更长的时间,但这不是问题。绘制完所有点后在图表上绘制仍然和以前的方法一样慢。我认为 WPF 需要处理的数据点太多了。

编辑:我进行了一些实验,我注意到通过将以前为 double 的坐标转换为 int 以防止 WPF 抗锯齿子像素线,性能有所提高。

编辑:感谢所有建议减少线段数量的回复。我已经将它们降低到最多两倍的阶梯线水平分辨率和最多简单线的水平分辨率,现在性能非常好。

【问题讨论】:

  • 一次屏幕上有这么多点(即,我认为仅靠眼睛很难离散地识别所有点)您不能在绘制之前优化点(例如当几个点形成一条“直线”时,删除“中间”点并保留“端点”)?这将减少要绘制的点数,从而减少绘制时间。
  • 谢谢迈克尔,我会试试的。

标签: c# .net wpf performance 2d


【解决方案1】:

我会考虑对您尝试渲染的点数进行下采样。您可能有 50,000 个数据点,但不太可能将它们全部显示在屏幕上;即使您在一个显示器中绘制了每个点,您也需要100,000 水平分辨率像素才能将它们全部绘制出来!即使在 D3D 中也有很多东西要画。

由于您更有可能拥有类似 2,048 像素的像素,因此您不妨减少绘制图形的点并绘制一条适合屏幕且只有几千个顶点的近似曲线。例如,如果用户绘制一个包含 10000 个点的时间框架,则在绘制图形之前将这 10000 个点下采样到 1000 个。您可以尝试多种技术,从简单平均到中值邻域到高斯卷积再到(我的建议)bicubic interpolationDrawing any number of points greater than 1/2 the screen resolution will simply be a waste.

当用户放大图形的一部分时,您可以重新采样以获得更高的分辨率和更准确的曲线拟合。

【讨论】:

  • 谢谢,我正在考虑这两个。我遇到的问题是数据点在 x 轴上的间隔不相等(它们基于时间)。点 x 处可能有多个点,但 y 可能不同。我也不知道过滤器有多干净,因为我不知道点将被渲染的分辨率,因为它在 WPF 中都是矢量图形。
  • PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice.M11 将为您提供从逻辑单位转换为设备单位的因素。请参阅wpftutorial.net/DrawOnPhysicalDevicePixels.html 了解更多信息。
【解决方案2】:

当您开始处理几何图形中数十万个不同的顶点和向量时,您可能应该考虑迁移图形代码以使用图形框架而不是依赖于 WPF(虽然 WPF 构建在 Direct3D 之上,因此能够非常高效的矢量图形渲染,有很多额外的开销阻碍了它的效率)。可以在 WPF 中同时托管 Direct3D 和 OpenGL 图形渲染窗口——我建议改变这个方向,而不是继续单独在 WPF 中工作。

(编辑:将原始答案中的“DirectX”更改为“Direct3D”)

【讨论】:

  • 感谢您的建议。将来我肯定会对此进行研究,但我对其他图形框架一无所知,所以现在这不是我的选择。我希望有一种 WPF 方法来解决这个问题。再次感谢。
【解决方案3】:

刚刚遇到这个问题,但正如我在this 线程中提到的,最高效的方法可能是针对 WPF 的可视层进行编程

WPF 中的所有视觉元素最终都会与这一层背道而驰……因此它是所有方法中最轻量级的。

请参阅thisthis 了解更多信息。 Matthew MacDonald 的 Pro WPF in C# 2008 书的第 14 章也有一个很好的部分。

作为另一个参考...参见 Pavan Podila 的书的第 2 章 WPF Control Development Unleashed。在第 13 页,他讨论了 DrawingVisuals 如何成为图表组件的绝佳选择。

最后,我刚刚注意到 Charles Petzold 写了一篇 MSDN 杂志 article,其中最好的整体(无论如何性能)解决方案(散点图)是 DrawingVisual 方法。

【讨论】:

  • 为了记录,我喜欢上面的许多建议……尤其是对点数进行下采样。显然,这种方法可以与针对 Visual 层的编程结合使用。
  • 我对我的应用程序中的所有内容都使用了 Visuals,并且我使用 Visuals 快速实现了行,但它没有任何区别。我很快就会再做一次,看看是否真的没有区别。感谢您提供指向其他线程的链接!
  • 您正在使用 DrawingVisual?奇怪的是,它应该会带来巨大的性能提升......
  • 我知道这很奇怪。它在其他图表上的性能确实非常好,而且绘图视觉效果比这张图表要多,但是由于某种原因,当涉及到彼此非常接近的线条时,WPF 无法处理它,但正如我所说,我需要重新-正确实施以进行最终评估。我在另一篇文章 (techzone.enterra-inc.com/architecture/…) 中读过,但作者也注意到 WPF 在性能方面特别糟糕。
【解决方案4】:

另一个想法是使用 Image 控件,并将 Source 属性设置为您动态创建的 DrawingImage。

根据 WPF Control Development Unleashed 中的 Pavan Podila 所说,当您拥有成千上万个不需要任何交互性的视觉效果时,这种方法会非常有用。查看他的书的第 25 页了解更多信息。

这是一个旧线程,但我认为值得一提的是,您可以通过使用 MouseUp() 事件与上述方法实现交互。您知道图像视口的大小、图像的分辨率和鼠标的位置。例如,您可以通过附加到 UserControl_SizeChanged 事件的计时器来维护集合 actualScreenPoints:

    double xworth = viewport.ActualWidth / (XEnd - XStart);
    double xworth = viewport.ActualHeight / (YEnd - YStart);
    List<Point> actualScreenPoints = new List<Point>(); 
    for (var i = 0; i < points.Count; i++)
    {
        double posX = points[i].X * xworth;
        double posY = points[i].Y * yworth;
        actualScreenPoints.Add(posX, posY);
    }

然后当您的 MouseUp() 事件触发时,检查集合中的任何点是否在 +-2px 内。你的 MouseUp 在给定点上。

【讨论】:

    【解决方案5】:

    我不知道它的扩展性如何,但我在 WPF 中使用 ZedGraph 取得了一些成功(WindowsFormsPresenter 中的 WinForms 控件)。我很惊讶还没有人提到它。值得一看,即使您不打算将它用于当前项目。

    ZedGraph

    祝你好运!

    【讨论】:

    • 我总是爱我一些 ZedGraph
    • 你提到 ZedGraph 很有趣,因为这是我以前在 ZedGraph 中所做的 WPF 端口。 ZedGraph 在这个图表上表现相当不错,但是对于我需要的其他图表来说,性能是不可接受的。
    • 你不是说 WindowsFormsHost ... 作为互操作 Windows 窗体图形组件的方式吗? +1 用于互操作性能更高的想法。但是请意识到,这种方法将限制您在 Windows 窗体图形组件之上编写 WPF 内容的方式。这就是我们当前的应用程序是如何做事的,而且效果很好……但现在我们想开始在我们的图形组件之上编写东西……这导致我们在 WPF 中重写我们的图形组件。
    • 无论 WPF 的 Windows 窗体互操作组件被称为什么,这就是我的意思 :) 很好! (+1)
    【解决方案6】:

    我相信在 WPF 框架中保留可能更快的唯一方法是在自定义控件中覆盖 OnRender。然后,您可以将几何图形直接渲染到持久场景,剔除视野之外的任何东西。如果用户一次只能看到一小部分数据集,那么仅靠剔除就足够了。

    有了这么多数据点,当整个数据集都在视图中时,用户不太可能看到完整的细节。因此,可能还值得考虑简化数据集以获得完整视图,然后在放大时显示更详细的视图。

    编辑:另外,试试 StreamGeometry。它存在的全部原因是性能,除非您尝试过,否则您永远不会知道。

    【讨论】:

    • 在覆盖 OnRender 时,请小心尝试将 WPF 驱动为像立即模式绘图系统而不是保留模式系统。这样你会失去性能。
    【解决方案7】:

    这是一个非常很好的问题,其核心问题是“任何用户都可以实际使用包含 100,000 个离散点的屏幕或从中做出业务决策吗?”。

    按照 GUI 设计理念的最佳实践,答案应该是,这让我怀疑是否没有其他方法可以满足应用程序的要求。

    如果确实有在屏幕上显示 100,000 个点且不滚动的真实案例,那么使用屏幕外缓冲区是可行的方法。将您的图像合成为位图,然后根据需要将该位图放到您的窗口/页面上。这样繁重的工作只需要完成一次,之后每次需要绘制窗口时都可以使用硬件加速。

    希望这会有所帮助。

    【讨论】:

    • 谢谢,好主意。我会尝试,如果我将来有性能问题,现在减少点数似乎没问题。
    【解决方案8】:

    我没有使用过 WPF(免责声明),但我怀疑您的性能问题是因为您的代码试图通过您的所有数据拟合一条平滑的曲线,并且所需的时间以几何方式(或更糟)增加数据点的数量。

    我不知道这在外观上是否可以接受,但请尝试通过用直线将每个点连接到最后一个点来绘制您的数据。这应该使生成图形的时间与数据点的数量成正比,并且与您拥有的图形一样多的点最终可能看起来完全一样。

    【讨论】:

    • 不,曲线穿过所有数据并不平滑。
    • 实际上,对点进行下采样,然后使用 b 样条在它们之间进行平滑插值是提高性能的常用技术。
    【解决方案9】:

    另一个想法是使用 Image 控件,并将 Source 属性设置为您动态创建的 DrawingImage。

    根据WPF Control Development Unleashed 中的Pavan Podila,当您拥有成千上万个不需要任何交互性的视觉效果时,这种方法会非常有用。查看他的书的第 25 页了解更多信息。

    【讨论】:

    • 不幸的是,虽然线条没有改变,但我确实需要互动。不过感谢您的提示。
    猜你喜欢
    • 2014-08-19
    • 1970-01-01
    • 2011-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多