【问题标题】:Vispy plotting dataVispy 绘图数据
【发布时间】:2021-10-18 00:39:52
【问题描述】:

我关注了这个Add labels to real-time signal plotting with Vispy 线程并尝试使用来自https://vispy.org/gallery/scene/line_update.html#sphx-glr-gallery-scene-line-update-py 的示例。我如何保存绘制的信息,我应该能够通过平移视图来查看过去的数据。我应该将绘制的数据存储在其他地方并检索它以查看旧数据吗?随着新数据添加到视图中,我试图保留所有信息。任何有关如何执行此操作的建议都会非常有帮助。

谢谢。

更新 1:

'''

import sys
import numpy as np
from vispy import app, scene

# vertex positions of data to draw
N = 100
pos = np.zeros((N, 2), dtype=np.float32)
x_lim = [0, 10.]
y_lim = [-2., 2.]
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)
pos[:, 1] = np.random.normal(size=N)

# color array
color = np.ones((N, 4), dtype=np.float32)
color[:, 0] = np.linspace(0, 1, N)
color[:, 1] = color[::-1, 0]

canvas = scene.SceneCanvas(keys='interactive', show=True)
grid = canvas.central_widget.add_grid(spacing=0)

viewbox = grid.add_view(row=0, col=1, camera='panzoom')

# add some axes
x_axis = scene.AxisWidget(orientation='bottom')
x_axis.stretch = (1, 0.1)
grid.add_widget(x_axis, row=1, col=1)
x_axis.link_view(viewbox)
y_axis = scene.AxisWidget(orientation='left')
y_axis.stretch = (0.1, 1)
grid.add_widget(y_axis, row=0, col=0)
y_axis.link_view(viewbox)

# add a line plot inside the viewbox
line = scene.Line(pos, color, parent=viewbox.scene)

# auto-scale to see the whole line.
viewbox.camera.set_range()
import time
t0 = time.time()
x_val = []
y_val= []



def update(ev):
    '''
     New x and y coordinates are generated and assigned to `pos`. 
     `datastore` variable holds all the data (past and current 
     generated data). Only `pos` data is used to update the plot in
     real time. 
    '''

    global pos, color, line, x_val, y_val,datastore
    
    
    pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)+time.time()-t0
    pos[:, 1] = np.sin(pos[:, 0])+0.2*np.random.normal(size=(N))
    
    
    x_val= np.append(x_val,pos[:,0])
    y_val= np.append(y_val,pos[:,1])
    datastore=np.stack((x_val, y_val),axis=1)
#     print(pos.size)
    
    color = np.roll(color, 1, axis=0)
    line.set_data(pos=pos)
    
    viewbox.camera.set_range(y=(pos[:, 1].max(), pos[:, 1].min()), x=(pos[:, 0].max(), pos[:, 0].min()))
    
    
def mouseMove(ev):
   '''
     `datastore` consists of all data. Based on the camera view, 
      data is extracted from `datastore`. this extracted data is 
      used to update the plot.
    '''
    
    
    print(viewbox.camera.get_state()['rect'])
    currentBounds= viewbox.camera.get_state()['rect']
    currentMin_x = currentBounds.right
    currentMax_x = currentBounds.left

    
    if currentMin_x < datastore[:,0].min():
        currentMin_x = datastore[:,0].min()
    if currentMax_x > datastore[:,0].max():
        currentMax_x = datastore[:,0].max()

    getData = datastore[(datastore[:,0]> currentMin_x) & (datastore[:,0]< currentMax_x)][:100] #Should be equal to N, for code to work
    
    print(getData.size)
    line.set_data(pos=getData, color=color) #Updates plot with the extracted data
    viewbox.camera.set_range(y=(getData[:, 1].max(), getData[:, 1].min()), x=(getData[:, 0].max(), getData[:, 0].min()))
    
    
print(viewbox.camera.get_state())
  
t = app.Timer()
t.connect(update)
t.start(iterations=60*5)
viewbox.events.mouse_release.connect(mouseMove)
t.events.stop.connect(lambda x: app.quit())

if __name__ == '__main__' and sys.flags.interactive == 0:
    app.run()

'''

Update 2

import sys
import numpy as np
from vispy import app, scene

# vertex positions of data to draw
N = 100
pos = np.zeros((N, 2), dtype=np.float32)
x_lim = [0, 10.]
y_lim = [-2., 2.]
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)
pos[:, 1] = np.random.normal(size=N)



canvas = scene.SceneCanvas(keys='interactive', show=True)
grid = canvas.central_widget.add_grid(spacing=0)

viewbox = grid.add_view(row=0, col=1, camera='panzoom')

# add some axes
x_axis = scene.AxisWidget(orientation='bottom')
x_axis.stretch = (1, 0.1)
grid.add_widget(x_axis, row=1, col=1)
x_axis.link_view(viewbox)
y_axis = scene.AxisWidget(orientation='left')
y_axis.stretch = (0.1, 1)
grid.add_widget(y_axis, row=0, col=0)
y_axis.link_view(viewbox)

# add a line plot inside the viewbox
line = scene.Line(pos,  parent=viewbox.scene)

# auto-scale to see the whole line.
viewbox.camera.set_range()
import time
t0 = time.time()
x_val = []
y_val= []



def update(ev):
    global pos, line, x_val, y_val,datastore
    
    
    pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)+time.time()-t0
    pos[:, 1] = np.sin(pos[:, 0])+0.2*np.random.normal(size=(N))
    
    
    x_val= np.append(x_val,pos[:,0])
    y_val= np.append(y_val,pos[:,1])
    datastore=np.stack((x_val, y_val),axis=1)
#     print(pos.size)
    
    line.set_data(pos=pos)
    
    viewbox.camera.set_range(x=(pos[:, 0].min(),pos[:, 0].max()),y=(pos[:, 1].min(),pos[:, 1].max()))
    
    
def mouseMove(ev):
    
    
    print(viewbox.camera.get_state()['rect'])
    currentBounds= viewbox.camera.get_state()['rect']
    currentMin_x = currentBounds.left
    currentMax_x = currentBounds.right


    print(currentMin_x, currentMax_x)
    getData = datastore[(datastore[:,0]>currentMin_x) & (datastore[:,0]< currentMax_x)] #Should be equal to N, for code to work
    print(getData)
    print(getData.size)
    line.set_data(pos=getData)
    viewbox.camera.set_range( x=( getData[:, 0].min(),getData[:, 0].max()),y=(getData[:, 1].min(),getData[:, 1].max()))
    
    
print(viewbox.camera.get_state())
  
t = app.Timer()
t.connect(update)
t.start(iterations=60*15)
viewbox.events.mouse_release.connect(mouseMove)
t.events.stop.connect(lambda x: app.quit())

if __name__ == '__main__' and sys.flags.interactive == 0:
    app.run()

【问题讨论】:

    标签: python plot real-time vispy


    【解决方案1】:

    总的来说,我认为这取决于您希望您的应用程序在用户体验方面如何运作,以及您希望对用户系统造成什么样的影响。你有几个选择:

    1. 保留 GPU 中的所有数据。由于 OpenGL 和 VisPy 的工作方式,这意味着您还必须在 CPU 上拥有所有数据,以便在新数据进入时更新整个数组,然后将整个数组上传到 GPU。有一些方法可以只上传您要发送的部分数据,但我不记得它们是否适用于此处使用的 LineVisual 或您想到的用例(新数据代替旧数据)。
    2. 保留 CPU 中的所有数据和 GPU 中的一个子集,在用户交互时自动更新 GPU 数据。这需要将各个部分连接在一起,以便您知道相机何时平移并更新 GPU 上的数据。不可怕,但有点复杂。
    3. 与 2 相同,但强制用户使用其他 UI 元素(例如 Qt 下拉菜单、按钮等)手动更新数据范围。这样您就不必为了交互而连接事物,您只需更新您需要的数据。

    这也让我提出一个问题,当人们需要像您这样的应用程序时,我总是担心:您需要显示所有数据吗?显示最后 N 条记录(即使只是默认情况下)是否足够好?这让您(应用程序开发人员)更加轻松,但以用户的灵活性为代价,他们可能在大多数情况下甚至都不会注意到。

    我应该在您链接到的示例中指出,如果还不清楚,这些是数据数组正在使用新数据更新的行,然后调用 .set_data 将数据上传到 GPU下次抽奖:

        pos[:, 1] = np.random.normal(size=N)
        color = np.roll(color, 1, axis=0)
        line.set_data(pos=pos, color=color)
    

    颜色只是对数组的假修改以改变颜色。随机数据只是生成假数据并且只更新 Y 坐标。您显然会有需要更新的真实数据,但是如果您只显示最后 N 条记录,那么在这件事中只更新 Y 位置将是一种简单且高效的方法。使用 numpy 数组可以完成很多技巧,以减少您需要制作的数据副本数量并获得最佳性能。如果您有任何问题,请告诉我。

    【讨论】:

    • 感谢@djhoese 的详尽回答,我正在考虑将流数据存储到 .csv 并在用户平移视图以查看过去的数据时检索它。如何将过去的数据存储在 GPU 中?是否有任何占位符来保存数据(就像我们使用 set_data 将数据发送到 GPU)。就我而言,我可以只保留并显示最后 N 条记录,并在用户需要时加载旧记录。除了 Y 坐标,我还需要使用当前时间戳更新 X 坐标。我将查看 API 以查看是否有任何方法可以更新 X 和 Y 坐标。非常感谢。
    • 为了在 GPU 中存储过去的数据,有一些更好的方法,但要坚持使用 LineVisual 样式界面中可用的内容,它可能就像存储更多点和更改视图一样简单画布只显示你想要的。至于您在磁盘上的存储,这似乎是合理的,但我建议不要使用 .csv 而是使用二进制格式。 Numpy 允许您将二进制数据写入磁盘并将其作为“memmap”(或 mmap)读取,这将更快并且需要更少的磁盘存储空间。
    • 我现在尝试将数据存储在一个变量中。我使用通过平移视图获得的范围边界提取了过去的数据。我这里有几个问题,带有过去数据的图看起来像一堆随机线,我不得不将检索到的数据的长度设置为我在开始时绘制的顶点数(N),它正在抛出当我尝试在平移视图中呈现所有数据时出现错误。以下是情节的图像 [之前]:ibb.co/ng8Fk3Y,[之后]:ibb.co/3y1W3Z4。请在Update 1下找到代码。
    • @aven 澄清一下,你现在只是想画一条线,对吧?我注意到您在update 方法中使用了pos,但也更新了datastore。您只需将pos 传递给该行,然后在您的鼠标处理程序中使用datastore。我认为posdatastore 中没有相同的数据。如果我误解了什么,请纠正我。
    • 这个想法是在我平移相机时绘制/渲染过去的数据。 datastore 就像一个包含所有当前和过去数据的文件,每次生成数据并将其分配给pos 时,我还将其附加到datastore。因此,当我移动相机时,应该渲染提取的数据并填充视图。 getData 包含提取的数据,并且该行使用getData 进行更新。我希望这在某种程度上可以澄清,我还在代码中添加了 cmets。谢谢。
    猜你喜欢
    • 2020-07-27
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多