Python 2.7 使用mlab_source.reset进行数据更新时Python Enthound Mayavi崩溃

Python 2.7 使用mlab_source.reset进行数据更新时Python Enthound Mayavi崩溃,python-2.7,segmentation-fault,enthought,mayavi,mayavi.mlab,Python 2.7,Segmentation Fault,Enthought,Mayavi,Mayavi.mlab,我正在尝试更新mayavi 3D绘图中的数据。数据的某些更改不会影响数据形状,因此可以使用mlab_source.set()方法(更新基础数据并刷新显示,而无需重置相机、重新生成VTK管道、重新生成基础数据结构等)。这是动画或快速绘图更新的最佳情况 如果基础数据改变了形状,文档建议使用mlab_source.reset()方法,该方法虽然不会重新创建整个管道或弄乱当前场景的摄影机,但确实会导致重建数据结构,从而导致一些性能开销。这会导致崩溃 最糟糕的方法是完全删除打印源,并通过新调用mlab.m

我正在尝试更新mayavi 3D绘图中的数据。数据的某些更改不会影响数据形状,因此可以使用mlab_source.set()方法(更新基础数据并刷新显示,而无需重置相机、重新生成VTK管道、重新生成基础数据结构等)。这是动画或快速绘图更新的最佳情况

如果基础数据改变了形状,文档建议使用mlab_source.reset()方法,该方法虽然不会重新创建整个管道或弄乱当前场景的摄影机,但确实会导致重建数据结构,从而导致一些性能开销。这会导致崩溃

最糟糕的方法是完全删除打印源,并通过新调用mlab.mesh()或任何用于打印数据的函数生成新的打印源。这将重新创建新的VTK管道、新的数据结构,并重置场景的视图(丢失当前的缩放和相机设置,这可能导致无法根据应用程序进行平滑交互)

我已经从我的应用程序中演示了一个简单的示例,其中一个球体类可以操纵其属性(位置、大小和分辨率)。更改位置和大小会导致坐标刷新,但数据大小保持不变。但是,更改分辨率会影响用于表示球体的纬度和经度细分的数量,从而更改坐标的数量。尝试使用“重置”时函数,解释器完全崩溃。我很确定这是基于网络上类似错误的VTK代码中的C级SEGFULT。似乎表明核心开发人员在大约5年前处理过这个问题,但我无法判断它是否真的得到了解决

我使用的是Mayavi 4.3.1,我使用的是Python(x,y)的Enthow工具套件。我使用的是Windows 7 64位Python 2.7.5。我使用的是PySide,但我删除了这些调用,并让mlab在本例中自行工作

下面是我的示例,它显示了mlab_source.set()正在工作,但在mlab_source.reset()上崩溃。您对它崩溃的原因有何想法?其他人是否可以复制它?我非常确定有其他方法可以通过源(TVTK)更新数据对象,但我在文档中找不到它,而且与特征相关的几十个属性很难浏览

感谢您的帮助

#Numpy Imports
from time import sleep
import numpy as np
from numpy import sin, cos, pi


class Sphere(object):
    """
    Class for a sphere
    """
    def __init__(self, c=None, r=None, n=None):

        #Initial defaults
        self._coordinates = None
        self._c = np.array([0.0, 0.0, 0.0])
        self._r = 1.0
        self._n = 20
        self._hash = []
        self._required_inputs = [('c', list),
                                 ('r', float)]

        #Assign Inputs
        if c is not None:
            self.c = c
        else:
            self.c = self._c

        if r is not None:
            self.r = r
        else:
            self.r = self._r


        if n is not None:
            self.n = n
        else:
            self.n = self._n

    @property
    def c(self):
        """
        Center point of sphere
         - Point is specified as a cartesian coordinate triplet, [x, y, z]
         - Coordinates are stored as a numpy array
         - Coordinates input as a list will be coerced to a numpy array
        """
        return self._c

    @c.setter
    def c(self, val):
        if isinstance(val, list):
            val = np.array(val)

        self._c = val

    @property
    def r(self):
        """
        Radius of sphere
        """
        return self._r

    @r.setter
    def r(self, val):
        if val < 0:
            raise ValueError("Sphere radius input must be positive")
        self._r = val

    @property
    def n(self):
        """
        Resolution of curvature
         - Number of points used to represent circles and arcs
         - For a sphere, n is the number of subdivisions per hemisphere (in both latitude and longitude)
        """
        return self._n

    @n.setter
    def n(self, val):
        if val < 0:
            raise ValueError("Sphere n-value for specifying arc/circle resolution must be positive")
        self._n = val

    @property
    def coordinates(self):
        """
        Returns x, y, z coordinate arrays to visualize the shape in 3D
        """
        self._lazy_update()
        return self._coordinates

    def _lazy_update(self):
        """
        Only update the coordinates data if necessary
        """
        #Get a newly calculated hash based on the sphere's inputs
        new_hash = self._get_hash()
        #Get the old hash
        old_hash = self._hash
        #Check if the sphere's state has changed
        if new_hash != old_hash:
            #Something changed - update the coordinates
            self._update_coordinates()

    def _get_hash(self):
        """
        Get the sphere's inputs as an immutable data structure
        """
        return tuple(map(tuple, [self._c, [self._r, self._n]]))

    def _update_coordinates(self):
        """
        Calculate 3D coordinates to represent the sphere
        """

        c, r, n = self._c, self._r, self._n

        #Get the angular distance between latitude and longitude lines
        dphi, dtheta = pi / n, pi / n

        #Generate a latitude and longitude grid
        [phi, theta] = np.mgrid[0:pi + dphi*1.0:dphi,
                                0:2 * pi + dtheta*1.0:dtheta]

        #Map the latitude longitude grid into cartesian x, y, z coordinates
        x = c[0] + r * cos(phi) * sin(theta)
        y = c[1] + r * sin(phi) * sin(theta)
        z = c[2] + r * cos(theta)

        #Store the coordinates
        self._coordinates = x, y, z
        #Update the hash to coordinates to these coordinates
        self._hash = self._get_hash()


if __name__ == '__main__':

    from mayavi import mlab

    #Make a sphere
    sphere = Sphere()

    #Plot the sphere
    source = mlab.mesh(*sphere.coordinates, representation='wireframe')

    #Get the mlab_source
    ms = source.mlab_source

    #Increase the sphere's radius by 2
    sphere.r *= 2

    #New coordinates (with larger radius)
    x, y, z = sphere.coordinates
    #Currently plotted coordinates
    x_old, y_old, z_old = ms.x, ms.y, ms.z

    #Verify that new x, y, z  are all same shape as old x, y, z
    data_is_same_shape = all([i.shape == j.shape for i, j in zip([x_old, y_old, z_old], [x, y, z])])

    #Pause to see the old sphere
    sleep(2)

    #Check if data has changed shape... (shouldn't have)
    if data_is_same_shape:
        print "Updating same-shaped data"
        ms.set(x=x, y=y, z=z)
    else:
        print "Updating with different shaped data"
        ms.reset(x=x, y=y, z=z)

    #Increase the arc resolution
    sphere.n = 50

    #New coordinates (with more points)
    x, y, z = sphere.coordinates
    #Currently plotted coordinates
    x_old, y_old, z_old = ms.x, ms.y, ms.z

    #Verify that new x, y, z  are all same shape as old x, y, z
    data_is_same_shape = all([i.shape == j.shape for i, j in zip([x_old, y_old, z_old], [x, y, z])])

    #Pause to see the bigger sphere
    sleep(2)

    #Check if data has changed shape... (should have this time...)
    if data_is_same_shape:
        print "Updating same-shaped data"
        ms.set(x=x, y=y, z=z)
    else:
        #This is where the segfault / crash occurs
        print "Updating with different shaped data"
        ms.reset(x=x, y=y, z=z)

    mlab.show()
直接修改TVTK Polydata对象似乎有一定的效果。它似乎在更新点,而没有自动修复连接,这就是为什么我还必须运行mlab_source.reset()。我假设reset()现在可以工作了,因为输入的数据具有相同数量的点,并且mlab_源处理自动生成连接数据。当减少点的数量时,它仍然会崩溃,可能是因为存在不存在的点的连接数据?我仍然对此感到非常失望

编辑3:

我已经实现了蛮力方法,仅从mlab.mesh()生成一个新曲面。为了防止重置视图,我禁用渲染并存储摄影机设置,然后在mlab.mesh()之后恢复摄影机设置,然后重新启用渲染。似乎工作得够快-仍然希望可以使用reset()更新基础数据

下面是我用来管理绘图对象的整个类(在进行编辑后响应GUI信号)


reset
有时会出现错误。您的所有调试方法看起来都在正确的轨道上。我认为应该可以在tvtk级别调整数据的形状,但您必须修改vtk源的多个字段(正如您在一个解决方案中指出的)。感谢您的肯定!在我看到或听到更好的解决方案或遇到性能问题之前,我将坚持使用“new_source”方法。如果我能想出如何在python中实现reset()在tvtk级别所做的事情,我会这么做,但我没能想出办法。
p = np.array([x.flatten(), y.flatten(), z.flatten()]).T
ms.dataset.points = p
ms.dataset.point_data.scalars = np.zeros(x.shape)
ms.dataset.points.modified()

#Regenerate the data structure
ms.reset(x=x, y=y, z=z)
class PlottablePrimitive(QtCore.QObject):

    def __init__(self, parent=None, shape=None, scene=None, mlab=None):
        super(PlottablePrimitive, self).__init__(parent=parent)

        self._shape = None
        self._scene = None
        self._mlab = None
        self._source = None
        self._color = [0.706, 0.510, 0.196]
        self._visible = True
        self._opacity = 1.0
        self._camera = {'position': None,
                        'focal_point': None,
                        'view_angle': None,
                        'view_up': None,
                        'clipping_range': None}

        if shape is not None:
            self._shape = shape

        if scene is not None:
            self._scene = scene

        if mlab is not None:
            self._mlab = mlab

    @property
    def shape(self):
        return self._shape

    @shape.setter
    def shape(self, val):
        self._shape = val

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        self._color = color
        if self._source is not None:
            surface = self._source.children[0].children[0].children[0]
            surface.actor.mapper.scalar_visibility = False
            surface.actor.property.color = tuple(color)

    def plot(self):
        x, y, z = self._shape.coordinates
        self._source = self._mlab.mesh(x, y, z)

    def update_plot(self):
        ms = self._source.mlab_source
        x, y, z = self._shape.coordinates
        a, b, c = ms.x, ms.y, ms.z
        data_is_same_shape = all([i.shape == j.shape for i, j in zip([a, b, c], [x, y, z])])

        if data_is_same_shape:
            print "Same Data Shape... updating"
            #Update the data in-place
            ms.set(x=x, y=y, z=z)
        else:
            print "New Data Shape... resetting data"

            method = 'new_source'

            if method == 'tvtk':
                #Modify TVTK directly
                p = np.array([x.flatten(), y.flatten(), z.flatten()]).T
                ms.dataset.points = p
                ms.dataset.point_data.scalars = np.zeros(x.shape)
                ms.dataset.points.modified()

                #Regenerate the data structure
                ms.reset(x=x, y=y, z=z)
            elif method == 'reset':
                #Regenerate the data structure
                ms.reset(x=x, y=y, z=z)
            elif method == 'new_source':
                scene = self._scene

                #Save camera settings
                self._save_camera()

                #Disable rendering
                self._scene.disable_render = True

                #Delete old plot
                self.delete_plot()

                #Generate new mesh
                self._source = self._mlab.mesh(x, y, z)

                #Reset camera
                self._restore_camera()
                self._scene.disable_render = False

    def _save_camera(self):
        scene = self._scene

        #Save camera settings
        for setting in self._camera.keys():
            self._camera[setting] = getattr(scene.camera, setting)

    def _restore_camera(self):
        scene = self._scene

        #Save camera settings
        for setting in self._camera.keys():
            if self._camera[setting] is not None:
                setattr(scene.camera, setting, self._camera[setting])

    def delete_plot(self):
        #Remove
        if self._source is not None:
            self._source.remove()
            self._source = None