Exemplo n.º 1
0
    def _initialize(self):

        # Count number of plots to create:
        num_plots = 0
        for config in self._config.itervalues():
            num_plots += len(config)

        # Set default grid of plot positions:
        if not self._rows * self._cols == num_plots:
            self._cols = int(np.ceil(np.sqrt(num_plots)))
            self._rows = int(np.ceil(num_plots / float(self._cols)))
        self.f, self.axarr = plt.subplots(self._rows,
                                          self._cols,
                                          figsize=self._figsize)

        # Remove unused subplots:
        for i in xrange(num_plots, self._rows * self._cols):
            plt.delaxes(self.axarr[np.unravel_index(i,
                                                    (self._rows, self._cols))])
        cnt = 0
        self.handles = []
        self.types = []
        keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
        # TODO: Irregular grid in U will make the plot better
        U, V = np.mgrid[0:np.pi / 2:complex(0, 60), 0:2 * np.pi:complex(0, 60)]
        X = np.cos(V) * np.sin(U)
        Y = np.sin(V) * np.sin(U)
        Z = np.cos(U)
        self._dome_pos_flat = (X.flatten(), Y.flatten(), Z.flatten())
        self._dome_pos = (X, Y, Z)
        self._dome_arr_shape = X.shape
        if not isinstance(self.axarr, np.ndarray):
            self.axarr = np.asarray([self.axarr])
        for LPU, configs in self._config.iteritems():
            for plt_id, config in enumerate(configs):
                ind = np.unravel_index(cnt, self.axarr.shape)
                cnt += 1

                # Some plot types require specific numbers of
                # neuron ID arrays:
                if 'type' in config:
                    if config['type'] == 'quiver':
                        assert len(config['ids']) == 2
                        config['type'] = 0
                    elif config['type'] == 'hsv':
                        assert len(config['ids']) == 2
                        config['type'] = 1
                    elif config['type'] == 'image':
                        assert len(config['ids']) == 1
                        config['type'] = 2
                    elif config['type'] == 'waveform':
                        config['type'] = 3
                    elif config['type'] == 'raster':
                        config['type'] = 4
                    elif config['type'] == 'rate':
                        config['type'] = 5
                    elif config['type'] == 'dome':
                        config['type'] = 6
                    else:
                        raise ValueError('Plot type not supported')
                else:
                    if str(LPU).startswith(
                            'input') and not self._graph[LPU].node[str(
                                config['ids'][0][0])]['spiking']:
                        config['type'] = 2
                    else:
                        config['type'] = 4

                if config['type'] < 3:
                    if not 'shape' in config:

                        # XXX This can cause problems when the number
                        # of neurons is not equal to
                        # np.prod(config['shape'])
                        num_neurons = len(config['ids'][0])
                        config['shape'] = [int(np.ceil(np.sqrt(num_neurons)))]
                        config['shape'].append(
                            int(
                                np.ceil(num_neurons /
                                        float(config['shape'][0]))))

                if config['type'] == 0:
                    config['handle'] = self.axarr[ind].quiver(\
                               np.reshape(self._data[LPU][config['ids'][0],0],config['shape']),\
                               np.reshape(self._data[LPU][config['ids'][1],0],config['shape']))
                elif config['type'] == 1:
                    X = np.reshape(self._data[LPU][config['ids'][0], 0],
                                   config['shape'])
                    Y = np.reshape(self._data[LPU][config['ids'][1], 0],
                                   config['shape'])
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X, Y) + np.pi) / (2 * np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H, S, V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'] = self.axarr[ind].imshow(RGB)
                elif config['type'] == 2:
                    if 'trans' in config:
                        if config['trans'] is True:
                            to_transpose = True
                        else:
                            to_transpose = False
                    else:
                        to_transpose = False
                        config['trans'] = False

                    if to_transpose:
                        temp = self.axarr[ind].imshow(np.transpose(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape'])))
                    else:
                        temp = self.axarr[ind].imshow(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape']))

                    temp.set_clim(self._imlim)
                    temp.set_cmap(plt.cm.gist_gray)
                    config['handle'] = temp
                elif config['type'] == 3:
                    fmt = config['fmt'] if 'fmt' in config else ''
                    self.axarr[ind].set_xlim(self._xlim)
                    self.axarr[ind].set_ylim(self._ylim)
                    if len(config['ids'][0]) == 1:
                        config['handle'] = self.axarr[ind].plot([0], \
                                            [self._data[LPU][config['ids'][0][0],0]], fmt)[0]
                        config['ydata'] = [
                            self._data[LPU][config['ids'][0][0], 0]
                        ]
                    else:
                        config['handle'] = self.axarr[ind].plot(
                            self._data[LPU][config['ids'][0], 0])[0]

                elif config['type'] == 4:
                    config['handle'] = self.axarr[ind]
                    config['handle'].vlines(0, 0, 0.01)
                    config['handle'].set_ylim([.5, len(config['ids'][0]) + .5])
                    config['handle'].set_ylabel('Neurons',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')
                    config['handle'].set_xlabel('Time (s)',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')
                    min_id = min(self._id_to_data_idx[LPU].keys())
                    min_idx = self._id_to_data_idx[LPU][min_id]
                    config['handle'].set_xlim(
                        [0, len(self._data[LPU][min_idx, :]) * self._dt])
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                elif config['type'] == 6:
                    self.axarr[ind].axes.set_yticks([])
                    self.axarr[ind].axes.set_xticks([])
                    self.axarr[ind] = self.f.add_subplot(self._rows,
                                                         self._cols,
                                                         cnt,
                                                         projection='3d')
                    config['handle'] = self.axarr[ind]
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    if 'norm' not in config.keys():
                        config['norm'] = Normalize(vmin=-70, vmax=0, clip=True)
                    elif config['norm'] == 'auto':
                        if self._data[LPU].shape[1] > 100:
                            config['norm'] = Normalize(
                                vmin=np.min(self._data[LPU][config['ids'][0],
                                                            100:]),
                                vmax=np.max(self._data[LPU][config['ids'][0],
                                                            100:]),
                                clip=True)
                        else:
                            config['norm'] = Normalize(
                                vmin=np.min(
                                    self._data[LPU][config['ids'][0], :]),
                                vmax=np.max(
                                    self._data[LPU][config['ids'][0], :]),
                                clip=True)

                    node_dict = self._graph[LPU].node
                    if str(LPU).startswith('input'):
                        latpositions = np.asarray([ node_dict[str(nid)]['lat'] \
                                                    for nid in range(len(node_dict)) \
                                                    if node_dict[str(nid)]['extern'] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long'] \
                                                     for nid in range(len(node_dict)) \
                                                     if node_dict[str(nid)]['extern'] ])
                    else:
                        latpositions = np.asarray([
                            node_dict[str(nid)]['lat']
                            for nid in config['ids'][0]
                        ])
                        longpositions = np.asarray([
                            node_dict[str(nid)]['long']
                            for nid in config['ids'][0]
                        ])
                    xx = np.cos(longpositions) * np.sin(latpositions)
                    yy = np.sin(longpositions) * np.sin(latpositions)
                    zz = np.cos(latpositions)
                    config['positions'] = (xx, yy, zz)
                    colors = griddata(config['positions'],
                                      self._data[LPU][config['ids'][0],
                                                      0], self._dome_pos_flat,
                                      'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(
                        np.reshape(colors, [
                            self._dome_arr_shape[0], self._dome_arr_shape[1], 1
                        ]), [1, 1, 4])
                    colors[:, :, 3] = 1.0
                    config['handle'].plot_surface(self._dome_pos[0],
                                                  self._dome_pos[1],
                                                  self._dome_pos[2],
                                                  rstride=1,
                                                  cstride=1,
                                                  facecolors=colors,
                                                  antialiased=False,
                                                  shade=False)

                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind], key,
                                              config[key])
                        except:
                            pass
                        try:
                            self._set_wrapper(config['handle'], key,
                                              config[key])
                        except:
                            pass

                if config['type'] < 3:
                    config['handle'].axes.set_xticks([])
                    config['handle'].axes.set_yticks([])

            if self.suptitle is not None:
                self.f.suptitle(self._title,
                                fontsize=self._fontsize + 1,
                                x=0.5,
                                y=0.03,
                                weight='bold')

        plt.tight_layout()

        if self.out_filename:
            if self.FFMpeg is None:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                elif which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg or avconv')
            elif self.FFMpeg:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg')
            else:
                if which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find avconv')

            # Use the output file to determine the name of the temporary frame
            # files so that two concurrently run visualizations don't clobber
            # each other's frames:
            self.writer.setup(
                self.f,
                self.out_filename,
                dpi=80,
                frame_prefix=os.path.splitext(self.out_filename)[0] + '_')
            self.writer.frame_format = 'png'
            self.writer.grab_frame()
        else:
            self.f.show()
Exemplo n.º 2
0
class visualizer(object):
    """
    Visualize the output produced by LPU models.

    Examples
    --------
    >>> import neurokernel.LPU.utils.visualizer as vis
    >>> V = vis.visualizer()
    >>> config1 = {}
    >>> config1['type'] = 'image'
    >>> config1['shape'] = [32,24]
    >>> config1['clim'] = [-0.6,0.5]
    >>> config2 = config1.copy()
    >>> config2['clim'] = [-0.55,-0.45]
    >>> V.add_LPU('lamina_output.h5', 'lamina.gexf.gz','lamina')
    >>> V.add_plot(config1, 'lamina', 'R1')
    >>> V.add_plot(config2, 'lamina', 'L1')
    >>> V.update_interval = 50
    >>> V.out_filename = 'test.avi'
    >>> V.run()
    """
    def __init__(self):
        self._xlim = [0, 1]
        self._ylim = [-1, 1]
        self._imlim = [-1, 1]
        self._update_interval = 50
        self._out_file = None
        self._fps = 5
        self._codec = 'libtheora'
        self._config = OrderedDict()
        self._rows = 0
        self._cols = 0
        self._figsize = (16, 9)
        self._fontsize = 18
        self._t = 1
        self._dt = 1
        self._data = {}
        self._graph = {}
        self._id_to_data_idx = {}
        self._maxt = None
        self._title = None
        self._FFMpeg = None

    def add_LPU(self,
                data_file,
                gexf_file=None,
                LPU=None,
                win=None,
                is_input=False):
        '''
        Add data associated with a specific LPU to a visualization.
        To add a plot containing neurons from a particular LPU,
        the LPU needs to be added to the visualization using this
        function. Note that outputs from multiple neurons can
        be visualized using the same visualizer object.

        Parameters
        ----------
        data_file: str
             Location of the h5 file generated by neurokernel
             containing the output of the LPU
        gexf_file: str
            Location of the gexf file describing the LPU.
            If not specified, it will be assumed that the h5 file
            contains input.
        LPU: str
            Name of the LPU. Will be used as identifier to add plots.
            For input signals, the name of the LPU will be prepended
            with 'input_'. For example::

                V.add_LPU('vision_in.h5', LPU='vision')

            will create the LPU identifier 'input_vision'.
            Therefore, adding a plot depicting this input can be done by::

                V.add_plot({''type':'image',imlim':[-0.5,0.5]},LPU='input_vision)
        win: slice/list
            Can be used to limit the visualization to a specific time window.
        '''

        if gexf_file and not is_input:
            self._graph[LPU] = nx.read_gexf(gexf_file)

            # Map neuron ids to index into output data array:
            self._id_to_data_idx[LPU] = {m:i for i, m in \
                enumerate(sorted([int(n) for n, k in \
                                  self._graph[LPU].nodes_iter(True) if k['spiking']]))}
        else:
            if LPU:
                LPU = 'input_' + str(LPU)
            else:
                LPU = 'input_' + str(len(self._data))
            if gexf_file:
                self._graph[LPU] = nx.read_gexf(gexf_file)
        if not LPU:
            LPU = len(self._data)
        self._data[LPU] = np.transpose(sio.read_array(data_file))
        if win is not None:
            self._data[LPU] = self._data[LPU][:, win]
        if self._maxt:
            self._maxt = min(self._maxt, self._data[LPU].shape[1])
        else:
            self._maxt = self._data[LPU].shape[1]

    def run(self, final_frame_name=None, dpi=300):
        '''
        Starts the visualization process. If the property out_filename is set,
        the visualization is saved as a video to the disk. If it is not
        specified, the animation will be displayed on screen.
        Please refer to documentation of add_LPU, add_plot and
        the properties of this class on how to configure the visualizer before call this
        method. An example can be found in the class doc string.

        Parameters
        ----------
        final_frame_name: str
            Optional. If specified, the final frame of the animation will be saved
            to disk.
        dpi: int
            Default(300). If final_frame_name is specified, this parameter will control
            the resolution at which the final frame is saved to disk.

        Note:
        -----
        If update_interval is set to 0 or None, it will be replaced by the
        index of the final time step. As a result, the visualizer will only
        generate the final frame.
        '''

        self._initialize()
        if not self._update_interval:
            self._update_interval = self._maxt - 1
        self._t = self._update_interval + 1
        for _ in range(self._update_interval, self._maxt,
                       self._update_interval):
            self._update()
        if final_frame_name is not None:
            self.f.savefig(final_frame_name, dpi=dpi)
        if self.out_filename:
            self._close()

    def _set_wrapper(self, obj, name, value):
        name = name.lower()
        func = getattr(obj, 'set_' + name, None)
        if func:
            try:
                func(value, fontsize=self._fontsize, weight='bold')
            except:
                try:
                    func(value)
                except:
                    pass

    def _initialize(self):

        # Count number of plots to create:
        num_plots = 0
        for config in self._config.itervalues():
            num_plots += len(config)

        # Set default grid of plot positions:
        if not self._rows * self._cols == num_plots:
            self._cols = int(np.ceil(np.sqrt(num_plots)))
            self._rows = int(np.ceil(num_plots / float(self._cols)))
        self.f, self.axarr = plt.subplots(self._rows,
                                          self._cols,
                                          figsize=self._figsize)

        # Remove unused subplots:
        for i in xrange(num_plots, self._rows * self._cols):
            plt.delaxes(self.axarr[np.unravel_index(i,
                                                    (self._rows, self._cols))])
        cnt = 0
        self.handles = []
        self.types = []
        keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
        # TODO: Irregular grid in U will make the plot better
        U, V = np.mgrid[0:np.pi / 2:complex(0, 60), 0:2 * np.pi:complex(0, 60)]
        X = np.cos(V) * np.sin(U)
        Y = np.sin(V) * np.sin(U)
        Z = np.cos(U)
        self._dome_pos_flat = (X.flatten(), Y.flatten(), Z.flatten())
        self._dome_pos = (X, Y, Z)
        self._dome_arr_shape = X.shape
        if not isinstance(self.axarr, np.ndarray):
            self.axarr = np.asarray([self.axarr])
        for LPU, configs in self._config.iteritems():
            for plt_id, config in enumerate(configs):
                ind = np.unravel_index(cnt, self.axarr.shape)
                cnt += 1

                # Some plot types require specific numbers of
                # neuron ID arrays:
                if 'type' in config:
                    if config['type'] == 'quiver':
                        assert len(config['ids']) == 2
                        config['type'] = 0
                    elif config['type'] == 'hsv':
                        assert len(config['ids']) == 2
                        config['type'] = 1
                    elif config['type'] == 'image':
                        assert len(config['ids']) == 1
                        config['type'] = 2
                    elif config['type'] == 'waveform':
                        config['type'] = 3
                    elif config['type'] == 'raster':
                        config['type'] = 4
                    elif config['type'] == 'rate':
                        config['type'] = 5
                    elif config['type'] == 'dome':
                        config['type'] = 6
                    else:
                        raise ValueError('Plot type not supported')
                else:
                    if str(LPU).startswith(
                            'input') and not self._graph[LPU].node[str(
                                config['ids'][0][0])]['spiking']:
                        config['type'] = 2
                    else:
                        config['type'] = 4

                if config['type'] < 3:
                    if not 'shape' in config:

                        # XXX This can cause problems when the number
                        # of neurons is not equal to
                        # np.prod(config['shape'])
                        num_neurons = len(config['ids'][0])
                        config['shape'] = [int(np.ceil(np.sqrt(num_neurons)))]
                        config['shape'].append(
                            int(
                                np.ceil(num_neurons /
                                        float(config['shape'][0]))))

                if config['type'] == 0:
                    config['handle'] = self.axarr[ind].quiver(\
                               np.reshape(self._data[LPU][config['ids'][0],0],config['shape']),\
                               np.reshape(self._data[LPU][config['ids'][1],0],config['shape']))
                elif config['type'] == 1:
                    X = np.reshape(self._data[LPU][config['ids'][0], 0],
                                   config['shape'])
                    Y = np.reshape(self._data[LPU][config['ids'][1], 0],
                                   config['shape'])
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X, Y) + np.pi) / (2 * np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H, S, V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'] = self.axarr[ind].imshow(RGB)
                elif config['type'] == 2:
                    if 'trans' in config:
                        if config['trans'] is True:
                            to_transpose = True
                        else:
                            to_transpose = False
                    else:
                        to_transpose = False
                        config['trans'] = False

                    if to_transpose:
                        temp = self.axarr[ind].imshow(np.transpose(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape'])))
                    else:
                        temp = self.axarr[ind].imshow(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape']))

                    temp.set_clim(self._imlim)
                    temp.set_cmap(plt.cm.gist_gray)
                    config['handle'] = temp
                elif config['type'] == 3:
                    fmt = config['fmt'] if 'fmt' in config else ''
                    self.axarr[ind].set_xlim(self._xlim)
                    self.axarr[ind].set_ylim(self._ylim)
                    if len(config['ids'][0]) == 1:
                        config['handle'] = self.axarr[ind].plot([0], \
                                            [self._data[LPU][config['ids'][0][0],0]], fmt)[0]
                        config['ydata'] = [
                            self._data[LPU][config['ids'][0][0], 0]
                        ]
                    else:
                        config['handle'] = self.axarr[ind].plot(
                            self._data[LPU][config['ids'][0], 0])[0]

                elif config['type'] == 4:
                    config['handle'] = self.axarr[ind]
                    config['handle'].vlines(0, 0, 0.01)
                    config['handle'].set_ylim([.5, len(config['ids'][0]) + .5])
                    config['handle'].set_ylabel('Neurons',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')
                    config['handle'].set_xlabel('Time (s)',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')
                    min_id = min(self._id_to_data_idx[LPU].keys())
                    min_idx = self._id_to_data_idx[LPU][min_id]
                    config['handle'].set_xlim(
                        [0, len(self._data[LPU][min_idx, :]) * self._dt])
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                elif config['type'] == 6:
                    self.axarr[ind].axes.set_yticks([])
                    self.axarr[ind].axes.set_xticks([])
                    self.axarr[ind] = self.f.add_subplot(self._rows,
                                                         self._cols,
                                                         cnt,
                                                         projection='3d')
                    config['handle'] = self.axarr[ind]
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    if 'norm' not in config.keys():
                        config['norm'] = Normalize(vmin=-70, vmax=0, clip=True)
                    elif config['norm'] == 'auto':
                        if self._data[LPU].shape[1] > 100:
                            config['norm'] = Normalize(
                                vmin=np.min(self._data[LPU][config['ids'][0],
                                                            100:]),
                                vmax=np.max(self._data[LPU][config['ids'][0],
                                                            100:]),
                                clip=True)
                        else:
                            config['norm'] = Normalize(
                                vmin=np.min(
                                    self._data[LPU][config['ids'][0], :]),
                                vmax=np.max(
                                    self._data[LPU][config['ids'][0], :]),
                                clip=True)

                    node_dict = self._graph[LPU].node
                    if str(LPU).startswith('input'):
                        latpositions = np.asarray([ node_dict[str(nid)]['lat'] \
                                                    for nid in range(len(node_dict)) \
                                                    if node_dict[str(nid)]['extern'] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long'] \
                                                     for nid in range(len(node_dict)) \
                                                     if node_dict[str(nid)]['extern'] ])
                    else:
                        latpositions = np.asarray([
                            node_dict[str(nid)]['lat']
                            for nid in config['ids'][0]
                        ])
                        longpositions = np.asarray([
                            node_dict[str(nid)]['long']
                            for nid in config['ids'][0]
                        ])
                    xx = np.cos(longpositions) * np.sin(latpositions)
                    yy = np.sin(longpositions) * np.sin(latpositions)
                    zz = np.cos(latpositions)
                    config['positions'] = (xx, yy, zz)
                    colors = griddata(config['positions'],
                                      self._data[LPU][config['ids'][0],
                                                      0], self._dome_pos_flat,
                                      'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(
                        np.reshape(colors, [
                            self._dome_arr_shape[0], self._dome_arr_shape[1], 1
                        ]), [1, 1, 4])
                    colors[:, :, 3] = 1.0
                    config['handle'].plot_surface(self._dome_pos[0],
                                                  self._dome_pos[1],
                                                  self._dome_pos[2],
                                                  rstride=1,
                                                  cstride=1,
                                                  facecolors=colors,
                                                  antialiased=False,
                                                  shade=False)

                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind], key,
                                              config[key])
                        except:
                            pass
                        try:
                            self._set_wrapper(config['handle'], key,
                                              config[key])
                        except:
                            pass

                if config['type'] < 3:
                    config['handle'].axes.set_xticks([])
                    config['handle'].axes.set_yticks([])

            if self.suptitle is not None:
                self.f.suptitle(self._title,
                                fontsize=self._fontsize + 1,
                                x=0.5,
                                y=0.03,
                                weight='bold')

        plt.tight_layout()

        if self.out_filename:
            if self.FFMpeg is None:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                elif which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg or avconv')
            elif self.FFMpeg:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg')
            else:
                if which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find avconv')

            # Use the output file to determine the name of the temporary frame
            # files so that two concurrently run visualizations don't clobber
            # each other's frames:
            self.writer.setup(
                self.f,
                self.out_filename,
                dpi=80,
                frame_prefix=os.path.splitext(self.out_filename)[0] + '_')
            self.writer.frame_format = 'png'
            self.writer.grab_frame()
        else:
            self.f.show()

    def _update(self):
        dt = self._dt
        t = self._t
        for key, configs in self._config.iteritems():
            data = self._data[key]
            for config in configs:
                if config['type'] == 3:
                    if len(config['ids'][0]) == 1:
                        config['ydata'].extend(np.reshape(np.double(\
                                                                    data[config['ids'][0], \
                                                                         max(0,t-self._update_interval):t]),(-1,)))
                        config['handle'].set_xdata(dt * np.arange(0, t))
                        config['handle'].set_ydata(np.asarray(config['ydata']))
                    else:
                        config['handle'].set_ydata(\
                                                   data[config['ids'][0], t])

                elif config['type'] == 4:

                    for j, id in enumerate(config['ids'][0]):

                        # Convert neuron id to index into array of generated outputs:
                        try:
                            idx = self._id_to_data_idx[key][id]
                        except:
                            continue
                        else:
                            for time in np.where(
                                    data[idx,
                                         max(0, t -
                                             self._update_interval):t])[0]:
                                config['handle'].vlines(
                                    float(t - time) * self._dt, j + 0.75,
                                    j + 1.25)
                elif config['type'] == 0:
                    shape = config['shape']
                    ids = config['ids']
                    config['handle'].U = np.reshape(data[ids[0], t], shape)
                    config['handle'].V = np.reshape(data[ids[1], t], shape)
                elif config['type'] == 1:
                    shape = config['shape']
                    ids = config['ids']
                    X = np.reshape(data[ids[0], t], shape)
                    Y = np.reshape(data[ids[1], t], shape)
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X, Y) + np.pi) / (2 * np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H, S, V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'].set_data(RGB)
                elif config['type'] == 2:
                    ids = config['ids']
                    if config['trans']:
                        config['handle'].set_data(
                            np.transpose(
                                np.reshape(data[ids[0], t], config['shape'])))
                    else:
                        config['handle'].set_data(
                            np.reshape(data[ids[0], t], config['shape']))
                elif config['type'] == 6:
                    ids = config['ids']
                    d = data[ids[0], t]
                    colors = griddata(config['positions'], d,
                                      self._dome_pos_flat,
                                      'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(
                        np.reshape(colors, [
                            self._dome_arr_shape[0], self._dome_arr_shape[1], 1
                        ]), [1, 1, 4])
                    colors[:, :, 3] = 1.0

                    config['handle'].clear()
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])

                    config['handle'].plot_surface(self._dome_pos[0],
                                                  self._dome_pos[1],
                                                  self._dome_pos[2],
                                                  rstride=1,
                                                  cstride=1,
                                                  facecolors=colors,
                                                  antialiased=False,
                                                  shade=False)
                keywds = [
                    'handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm'
                ]
                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind], key,
                                              config[key])
                        except:
                            pass

                        try:
                            self._set_wrapper(config['handle'], key,
                                              config[key])
                        except:
                            pass
        self.f.canvas.draw()
        if self.out_filename:
            self.writer.grab_frame()

        self._t += self._update_interval

    def add_plot(self, config_dict, LPU, names=[''], shift=0):
        '''
        Add a plot to the visualizer

        Parameters
        ----------
        config_dict: dict
            A dictionary specifying the plot attributes. The attribute
            names should be the keys.
            
            The following are the plot attributes that can be specfied using
            this dict.

            type - str
                This specifies the type of the plot. Has to be one of
                ['waveform', 'raster', 'image','hsv','quiver', 'dome']
                For plots of type 'dome', lat and long are required
                to be specified in the gexf file.
            ids - dict with either 1 or 2 entries
                Specifies the neuron ids from the associated LPU.
                The keys should be in [0,1] and the values
                should be a list of ids.
                For example::

                    {'ids':{0:[1,2]}}

                will plot neurons with ids 1 and 2.
                Two entries in the dictionary  are needed if the plot is
                of type 'hsv' or 'quiver'
                For example::

                     {'ids':{0:[:768],1:[768:1536]},'type':'HSV'}

                can be used to generate a HSV plot where the hue channel is
                controlled by the angle of the vector defined by the membrane
                potentials of the neurons with ids [:768] and [768:1536] and
                the value will be the magnitude of the same vector. 
            
                This parameter is optional for the following cases::

                    1) The plot is associated with input signals.
                    2) The names parameter is specified.

                If the above doesn't hold, this attribute needs to be specified.

            shape - list or tuple with two entries
                This attribute specifies the dimensions for plots of type image,
                hsv or quiver.
  
            title - str
                Optional. Can be used to control the title of the plot.

            
            In addition to the above, any parameter supported by matlpotlib
            for the particular type of plot can be specified.
            For example - 'imlim','clim','xlim','ylim' etc.
              
        LPU: str
            The name of the LPU associated to this plot.

        names: list
            Optional. A list of str specifying the neurons
            to plot. Can be used instead of specifying ids in the
            config_dict. The gexf file of the LPU needs to have
            the name attribute in order for this to be used.

        
        '''
        config = config_dict.copy()
        if not isinstance(names, list):
            names = [names]
        if not LPU in self._config:
            self._config[LPU] = []
        if 'ids' in config:
            # XXX should check whether the specified ids are within range
            self._config[LPU].append(config)
        elif str(LPU).startswith('input'):
            config['ids'] = [range(0, self._data[LPU].shape[0])]
            self._config[LPU].append(config)
        else:
            config['ids'] = {}
            for i, name in enumerate(names):
                config['ids'][i] = []
                for id in range(len(self._graph[LPU].node)):
                    if self._graph[LPU].node[str(id)]['name'] == name:
                        config['ids'][i].append(id - shift)
            self._config[LPU].append(config)
        if not 'title' in config:
            if names[0]:
                config['title'] = "{0} - {1}".format(str(LPU), str(names[0]))
            else:
                if str(LPU).startswith('input_'):
                    config['title'] = LPU.split('_', 1)[1] + ' - ' + 'Input'
                else:
                    config['title'] = str(LPU)

    def _close(self):
        self.writer.finish()
        plt.close(self.f)

    @property
    def xlim(self):
        '''
        Get or set the limits of the x-axis for all the raster and waveform plots.
        Can be superseded for individual plots by specifying xlim in the confid_dict
        for that plot.

        See also
        --------
            add_plot
        '''
        return self._xlim

    @xlim.setter
    def xlim(self, value):
        self._xlim = value

    @property
    def ylim(self):
        '''
        Get or set the limits of the y-axis for all the raster and waveform plots.
        Can be superseded for individual plots by specifying xlim in the confid_dict
        for that plot.

        See also
        --------
            add_plot
        '''
        return self._ylim

    @ylim.setter
    def ylim(self, value):
        self._ylim = value

    @property
    def FFMpeg(self):
        return self._FFMpeg

    @FFMpeg.setter
    def FFMpeg(self, value):
        self._FFMpeg = value

    @property
    def imlim(self):
        return self._imlim

    @imlim.setter
    def imlim(self, value):
        self._imlim = value

    @property
    def out_filename(self):
        return self._out_file

    @out_filename.setter
    def out_filename(self, value):
        assert (isinstance(value, str))
        self._out_file = value

    @property
    def fps(self):
        return self._fps

    @fps.setter
    def fps(self, value):
        assert (isinstance(value, int))
        self._fps = value

    @property
    def codec(self):
        return self._codec

    @codec.setter
    def codec(self, value):
        assert (isinstance(value, str))
        self._codec = value

    @property
    def rows(self):
        return self._rows

    @rows.setter
    def rows(self, value):
        self._rows = value

    @property
    def cols(self):
        return self._cols

    @cols.setter
    def cols(self, value):
        self._cols = value

    @property
    def dt(self):
        return self._dt

    @dt.setter
    def dt(self, value):
        self._dt = value

    @property
    def figsize(self):
        return self._figsize

    @figsize.setter
    def figsize(self, value):
        assert (isinstance(value, tuple) and len(value) == 2)
        self._figsize = value

    @property
    def fontsize(self):
        return self._fontsize

    @fontsize.setter
    def fontsize(self, value):
        self._fontsize = value

    @property
    def suptitle(self):
        return self._title

    @suptitle.setter
    def suptitle(self, value):
        self._title = value

    @property
    def update_interval(self):
        """
        Gets or sets the update interval(in terms of time steps) for the animation.
        If value is 0 or None, update_interval will be set to the index of the
        final step. As a consequence, only the final frame will be generated.
        """
        return self._update_interval

    @update_interval.setter
    def update_interval(self, value):
        self._update_interval = value
Exemplo n.º 3
0
class visualizer(object):
    """
    Visualize the output produced by LPU models.

    Examples
    --------
    >>> import neurokernel.LPU.utils.visualizer as vis
    >>> V = vis.visualizer()
    >>> config1 = {}
    >>> config1['type'] = 'image'
    >>> config1['shape'] = [32,24]
    >>> config1['clim'] = [-0.6,0.5]
    >>> config2 = config1.copy()
    >>> config2['clim'] = [-0.55,-0.45]
    >>> V.add_LPU('lamina_output.h5', 'lamina.gexf.gz','lamina')
    >>> V.add_plot(config1, 'lamina', 'R1')
    >>> V.add_plot(config2, 'lamina', 'L1')
    >>> V.update_interval = 50
    >>> V.out_filename = 'test.avi'
    >>> V.run()
    """

    def __init__(self):
        self._xlim = [0,1]
        self._ylim = [-1,1]
        self._imlim = [-1, 1]
        self._update_interval = 50
        self._out_file = None
        self._fps = 5
        self._codec = 'libtheora'
        self._config = OrderedDict()
        self._rows = 0
        self._cols = 0
        self._figsize = (16,9)
        self._fontsize = 18
        self._t = 1
        self._dt = 1
        self._data = {}
        self._graph = {}
        self._id_to_data_idx = {}
        self._maxt = None
        self._title = None
        self._FFMpeg = None

    def add_LPU(self, data_file, gexf_file=None, LPU=None, win=None,
                is_input=False):
        '''
        Add data associated with a specific LPU to a visualization.
        To add a plot containing neurons from a particular LPU,
        the LPU needs to be added to the visualization using this
        function. Note that outputs from multiple neurons can
        be visualized using the same visualizer object.

        Parameters
        ----------
        data_file: str
             Location of the h5 file generated by neurokernel
             containing the output of the LPU
        gexf_file: str
            Location of the gexf file describing the LPU.
            If not specified, it will be assumed that the h5 file
            contains input.
        LPU: str
            Name of the LPU. Will be used as identifier to add plots.
            For input signals, the name of the LPU will be prepended
            with 'input_'. For example::

                V.add_LPU('vision_in.h5', LPU='vision')

            will create the LPU identifier 'input_vision'.
            Therefore, adding a plot depicting this input can be done by::

                V.add_plot({''type':'image',imlim':[-0.5,0.5]},LPU='input_vision)
        win: slice/list
            Can be used to limit the visualization to a specific time window.
        '''

        if gexf_file and not is_input:
            self._graph[LPU] = nx.read_gexf(gexf_file)

            # Map neuron ids to index into output data array:
            self._id_to_data_idx[LPU] = {m:i for i, m in \
                enumerate(sorted([int(n) for n, k in \
                                  self._graph[LPU].nodes_iter(True) if k['spiking']]))}
        else:
            if LPU:
                LPU = 'input_' + str(LPU)
            else:
                LPU = 'input_' + str(len(self._data))
            if gexf_file:
                self._graph[LPU] = nx.read_gexf(gexf_file)
        if not LPU:
            LPU = len(self._data)
        self._data[LPU] = np.transpose(sio.read_array(data_file))
        if win is not None:
            self._data[LPU] = self._data[LPU][:,win]
        if self._maxt:
            self._maxt = min(self._maxt, self._data[LPU].shape[1])
        else:
            self._maxt = self._data[LPU].shape[1]

    def run(self, final_frame_name=None, dpi=300):
        '''
        Starts the visualization process. If the property out_filename is set,
        the visualization is saved as a video to the disk. If it is not
        specified, the animation will be displayed on screen.
        Please refer to documentation of add_LPU, add_plot and
        the properties of this class on how to configure the visualizer before call this
        method. An example can be found in the class doc string.

        Parameters
        ----------
        final_frame_name: str
            Optional. If specified, the final frame of the animation will be saved
            to disk.
        dpi: int
            Default(300). If final_frame_name is specified, this parameter will control
            the resolution at which the final frame is saved to disk.

        Note:
        -----
        If update_interval is set to 0 or None, it will be replaced by the
        index of the final time step. As a result, the visualizer will only
        generate the final frame.
        '''

        self._initialize()
        if not self._update_interval:
            self._update_interval = self._maxt - 1
        self._t = self._update_interval + 1
        for _ in range(self._update_interval, 
                       self._maxt, self._update_interval):
            self._update()
        if final_frame_name is not None:
            self.f.savefig(final_frame_name, dpi=dpi)
        if self.out_filename:
            self._close()

    def _set_wrapper(self, obj, name, value):
        name = name.lower()
        func = getattr(obj, 'set_'+name, None)
        if func:
            try:
                func(value, fontsize=self._fontsize, weight='bold')
            except:
                try:
                    func(value)
                except:
                    pass

    def _initialize(self):

        # Count number of plots to create:
        num_plots = 0
        for config in self._config.itervalues():
            num_plots += len(config)

        # Set default grid of plot positions:
        if not self._rows*self._cols == num_plots:
            self._cols = int(np.ceil(np.sqrt(num_plots)))
            self._rows = int(np.ceil(num_plots/float(self._cols)))
        self.f, self.axarr = plt.subplots(self._rows, self._cols,
                                          figsize=self._figsize)

        # Remove unused subplots:
        for i in xrange(num_plots, self._rows*self._cols):
            plt.delaxes(self.axarr[np.unravel_index(i, (self._rows, self._cols))])
        cnt = 0
        self.handles = []
        self.types = []
        keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
        # TODO: Irregular grid in U will make the plot better
        U, V = np.mgrid[0:np.pi/2:complex(0, 60),
                        0:2*np.pi:complex(0, 60)]
        X = np.cos(V)*np.sin(U)
        Y = np.sin(V)*np.sin(U)
        Z = np.cos(U)
        self._dome_pos_flat = (X.flatten(), Y.flatten(), Z.flatten())
        self._dome_pos = (X, Y, Z)
        self._dome_arr_shape = X.shape
        if not isinstance(self.axarr, np.ndarray):
            self.axarr = np.asarray([self.axarr])
        for LPU, configs in self._config.iteritems():
            for plt_id, config in enumerate(configs):
                ind = np.unravel_index(cnt, self.axarr.shape)
                cnt+=1

                # Some plot types require specific numbers of
                # neuron ID arrays:
                if 'type' in config:
                    if config['type'] == 'quiver':
                        assert len(config['ids'])==2
                        config['type'] = 0
                    elif config['type'] == 'hsv':
                        assert len(config['ids'])==2
                        config['type'] = 1
                    elif config['type'] == 'image':
                        assert len(config['ids'])==1
                        config['type'] = 2
                    elif config['type'] == 'waveform':
                        config['type'] = 3
                    elif config['type'] == 'raster':
                        config['type'] = 4
                    elif config['type'] == 'rate':
                        config['type'] = 5
                    elif config['type'] == 'dome':
                        config['type'] = 6
                    else:
                        raise ValueError('Plot type not supported')
                else:
                    if str(LPU).startswith('input') and not self._graph[LPU].node[str(config['ids'][0][0])]['spiking']:
                        config['type'] = 2
                    else:
                        config['type'] = 4

                if config['type'] < 3:
                    if not 'shape' in config:

                        # XXX This can cause problems when the number
                        # of neurons is not equal to
                        # np.prod(config['shape'])
                        num_neurons = len(config['ids'][0])
                        config['shape'] = [int(np.ceil(np.sqrt(num_neurons)))]
                        config['shape'].append(int(np.ceil(num_neurons/float(config['shape'][0]))))

                if config['type'] == 0:
                    config['handle'] = self.axarr[ind].quiver(\
                               np.reshape(self._data[LPU][config['ids'][0],0],config['shape']),\
                               np.reshape(self._data[LPU][config['ids'][1],0],config['shape']))
                elif config['type'] == 1:
                    X = np.reshape(self._data[LPU][config['ids'][0],0],config['shape'])
                    Y = np.reshape(self._data[LPU][config['ids'][1],0],config['shape'])
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X,Y)+np.pi)/(2*np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H,S,V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'] = self.axarr[ind].imshow(RGB)
                elif config['type'] == 2:
                    if 'trans' in config:
                        if config['trans'] is True:
                            to_transpose = True
                        else:
                            to_transpose = False
                    else:
                        to_transpose = False
                        config['trans'] = False

                    if to_transpose:
                        temp = self.axarr[ind].imshow(np.transpose(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape'])))
                    else:
                        temp = self.axarr[ind].imshow(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape']))



                    temp.set_clim(self._imlim)
                    temp.set_cmap(plt.cm.gist_gray)
                    config['handle'] = temp
                elif config['type'] == 3:
                    fmt = config['fmt'] if 'fmt' in config else '' 
                    self.axarr[ind].set_xlim(self._xlim)
                    self.axarr[ind].set_ylim(self._ylim)
                    if len(config['ids'][0])==1:
                        config['handle'] = self.axarr[ind].plot([0], \
                                            [self._data[LPU][config['ids'][0][0],0]], fmt)[0]
                        config['ydata'] = [self._data[LPU][config['ids'][0][0],0]]
                    else:
                        config['handle'] = self.axarr[ind].plot(self._data[LPU][config['ids'][0],0])[0]

                elif config['type'] == 4:
                    config['handle'] = self.axarr[ind]
                    config['handle'].vlines(0, 0, 0.01)
                    config['handle'].set_ylim([.5, len(config['ids'][0]) + .5])
                    config['handle'].set_ylabel('Neurons',
                                                fontsize=self._fontsize-1, weight='bold')
                    config['handle'].set_xlabel('Time (s)',fontsize=self._fontsize-1, weight='bold')
                    min_id = min(self._id_to_data_idx[LPU].keys())
                    min_idx = self._id_to_data_idx[LPU][min_id]
                    config['handle'].set_xlim([0,len(self._data[LPU][min_idx,:])*self._dt])
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                elif config['type'] == 6:
                    self.axarr[ind].axes.set_yticks([])
                    self.axarr[ind].axes.set_xticks([])
                    self.axarr[ind] = self.f.add_subplot(self._rows,
                                                         self._cols,
                                                         cnt,
                                                         projection='3d')
                    config['handle' ] = self.axarr[ind]
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    if 'norm' not in config.keys():
                        config['norm'] = Normalize(vmin=-70, vmax=0, clip=True)
                    elif config['norm'] == 'auto':
                        if self._data[LPU].shape[1] > 100:
                            config['norm'] = Normalize(vmin = np.min(self._data[LPU][config['ids'][0],100:]),
                                                       vmax = np.max(self._data[LPU][config['ids'][0],100:]),
                                                       clip = True)
                        else:
                            config['norm'] = Normalize(vmin = np.min(self._data[LPU][config['ids'][0],:]),
                                                       vmax = np.max(self._data[LPU][config['ids'][0],:]),
                                                       clip = True)
                            
                    node_dict = self._graph[LPU].node
                    if str(LPU).startswith('input'):
                        latpositions = np.asarray([ node_dict[str(nid)]['lat'] \
                                                    for nid in range(len(node_dict)) \
                                                    if node_dict[str(nid)]['extern'] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long'] \
                                                     for nid in range(len(node_dict)) \
                                                     if node_dict[str(nid)]['extern'] ])
                    else:
                        latpositions = np.asarray([ node_dict[str(nid)]['lat']
                                                    for nid in config['ids'][0] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long']
                                                     for nid in config['ids'][0] ])
                    xx = np.cos(longpositions) * np.sin(latpositions)
                    yy = np.sin(longpositions) * np.sin(latpositions)
                    zz = np.cos(latpositions)
                    config['positions'] = (xx, yy, zz)
                    colors = griddata(config['positions'], self._data[LPU][config['ids'][0],0],
                                      self._dome_pos_flat, 'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(np.reshape(colors,
                                                [self._dome_arr_shape[0],self._dome_arr_shape[1],1])
                                     ,[1,1,4])
                    colors[:,:,3] = 1.0
                    config['handle'].plot_surface(self._dome_pos[0], self._dome_pos[1],
                                                  self._dome_pos[2], rstride=1, cstride=1,
                                                  facecolors=colors, antialiased=False,
                                                  shade=False)
                    
                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind],key, config[key])
                        except:
                            pass
                        try:
                            self._set_wrapper(config['handle'],key, config[key])
                        except:
                            pass
                
                if config['type']<3:
                    config['handle'].axes.set_xticks([])
                    config['handle'].axes.set_yticks([])

            if self.suptitle is not None:
                self.f.suptitle(self._title, fontsize=self._fontsize+1, x=0.5,y=0.03, weight='bold')

        plt.tight_layout()

        if self.out_filename:
            if self.FFMpeg is None:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps, codec=self.codec)
                elif which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg or avconv')
            elif self.FFMpeg:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg')
            else:
                if which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find avconv')

            # Use the output file to determine the name of the temporary frame
            # files so that two concurrently run visualizations don't clobber
            # each other's frames:
            self.writer.setup(self.f, self.out_filename, dpi=80,
                              frame_prefix=os.path.splitext(self.out_filename)[0]+'_')
            self.writer.frame_format = 'png'
            self.writer.grab_frame()
        else:
            self.f.show()

    def _update(self):
        dt = self._dt
        t = self._t
        for key, configs in self._config.iteritems():
            data = self._data[key]
            for config in configs:
                if config['type'] == 3:
                    if len(config['ids'][0])==1:
                        config['ydata'].extend(np.reshape(np.double(\
                                                                    data[config['ids'][0], \
                                                                         max(0,t-self._update_interval):t]),(-1,)))
                        config['handle'].set_xdata(dt*np.arange(0, t))
                        config['handle'].set_ydata(np.asarray(config['ydata']))
                    else:
                        config['handle'].set_ydata(\
                                                   data[config['ids'][0], t])

                elif config['type']==4:

                    for j, id in enumerate(config['ids'][0]):

                        # Convert neuron id to index into array of generated outputs:
                        try:
                            idx = self._id_to_data_idx[key][id]
                        except:
                            continue
                        else:
                            for time in np.where(data[idx, max(0,t-self._update_interval):t])[0]:
                                config['handle'].vlines(float(t-time)*self._dt,j+0.75, j+1.25)
                elif config['type'] == 0:
                    shape = config['shape']
                    ids = config['ids']
                    config['handle'].U = np.reshape(data[ids[0], t],shape)
                    config['handle'].V = np.reshape(data[ids[1], t],shape)
                elif config['type']==1:
                    shape = config['shape']
                    ids = config['ids']
                    X = np.reshape(data[ids[0], t],shape)
                    Y = np.reshape(data[ids[1], t],shape)
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X,Y)+np.pi)/(2*np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H,S,V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'].set_data(RGB)
                elif config['type'] == 2:
                    ids = config['ids']
                    if config['trans']:
                        config['handle'].set_data(
                            np.transpose(np.reshape(data[ids[0], t], config['shape'
                                                                        ])))
                    else:
                        config['handle'].set_data(
                            np.reshape(data[ids[0], t], config['shape']))
                elif config['type'] == 6:
                    ids = config['ids']
                    d = data[ids[0], t]
                    colors = griddata(config['positions'], d,
                                      self._dome_pos_flat, 'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(np.reshape(colors,
                                                [self._dome_arr_shape[0],self._dome_arr_shape[1],1])
                                     ,[1,1,4])
                    colors[:,:,3] = 1.0

                    config['handle'].clear()
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    
                    config['handle'].plot_surface(self._dome_pos[0], self._dome_pos[1],
                                                  self._dome_pos[2], rstride=1, cstride=1,
                                                  facecolors=colors, antialiased=False,
                                                  shade=False)
                keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind],key, config[key])
                        except:
                            pass
                        
                        try:
                            self._set_wrapper(config['handle'],key, config[key])
                        except:
                            pass
        self.f.canvas.draw()
        if self.out_filename:
            self.writer.grab_frame()

        self._t+=self._update_interval
        
    def add_plot(self, config_dict, LPU, names=[''], shift=0):
        '''
        Add a plot to the visualizer

        Parameters
        ----------
        config_dict: dict
            A dictionary specifying the plot attributes. The attribute
            names should be the keys.
            
            The following are the plot attributes that can be specfied using
            this dict.

            type - str
                This specifies the type of the plot. Has to be one of
                ['waveform', 'raster', 'image','hsv','quiver', 'dome']
                For plots of type 'dome', lat and long are required
                to be specified in the gexf file.
            ids - dict with either 1 or 2 entries
                Specifies the neuron ids from the associated LPU.
                The keys should be in [0,1] and the values
                should be a list of ids.
                For example::

                    {'ids':{0:[1,2]}}

                will plot neurons with ids 1 and 2.
                Two entries in the dictionary  are needed if the plot is
                of type 'hsv' or 'quiver'
                For example::

                     {'ids':{0:[:768],1:[768:1536]},'type':'HSV'}

                can be used to generate a HSV plot where the hue channel is
                controlled by the angle of the vector defined by the membrane
                potentials of the neurons with ids [:768] and [768:1536] and
                the value will be the magnitude of the same vector. 
            
                This parameter is optional for the following cases::

                    1) The plot is associated with input signals.
                    2) The names parameter is specified.

                If the above doesn't hold, this attribute needs to be specified.

            shape - list or tuple with two entries
                This attribute specifies the dimensions for plots of type image,
                hsv or quiver.
  
            title - str
                Optional. Can be used to control the title of the plot.

            
            In addition to the above, any parameter supported by matlpotlib
            for the particular type of plot can be specified.
            For example - 'imlim','clim','xlim','ylim' etc.
              
        LPU: str
            The name of the LPU associated to this plot.

        names: list
            Optional. A list of str specifying the neurons
            to plot. Can be used instead of specifying ids in the
            config_dict. The gexf file of the LPU needs to have
            the name attribute in order for this to be used.

        
        '''
        config = config_dict.copy()
        if not isinstance(names, list):
            names = [names]
        if not LPU in self._config:
            self._config[LPU] = []
        if 'ids' in config:
            # XXX should check whether the specified ids are within range
            self._config[LPU].append(config)
        elif str(LPU).startswith('input'):
            config['ids'] = [range(0, self._data[LPU].shape[0])]
            self._config[LPU].append(config)
        else:
            config['ids'] = {}
            for i,name in enumerate(names):
                config['ids'][i]=[]
                for id in range(len(self._graph[LPU].node)):
                    if self._graph[LPU].node[str(id)]['name'] == name:
                        config['ids'][i].append(id-shift)
            self._config[LPU].append(config)
        if not 'title' in config:
            if names[0]:
                config['title'] = "{0} - {1}".format(str(LPU),str(names[0]))
            else:
                if str(LPU).startswith('input_'):
                    config['title'] = LPU.split('_',1)[1] + ' - ' + 'Input'
                else:
                    config['title'] = str(LPU)

    def _close(self):
        self.writer.finish()
        plt.close(self.f)

    @property
    def xlim(self):
        '''
        Get or set the limits of the x-axis for all the raster and waveform plots.
        Can be superseded for individual plots by specifying xlim in the confid_dict
        for that plot.

        See also
        --------
            add_plot
        '''
        return self._xlim

    @xlim.setter
    def xlim(self, value):
        self._xlim = value

    @property
    def ylim(self):
        '''
        Get or set the limits of the y-axis for all the raster and waveform plots.
        Can be superseded for individual plots by specifying xlim in the confid_dict
        for that plot.

        See also
        --------
            add_plot
        '''
        return self._ylim

    @ylim.setter
    def ylim(self, value):
        self._ylim = value

    @property
    def FFMpeg(self): return self._FFMpeg
    
    @FFMpeg.setter
    def FFMpeg(self, value):
        self._FFMpeg = value

    @property
    def imlim(self): return self._imlim

    @imlim.setter
    def imlim(self, value):
        self._imlim = value

    @property
    def out_filename(self): return self._out_file

    @out_filename.setter
    def out_filename(self, value):
        assert(isinstance(value, str))
        self._out_file = value

    @property
    def fps(self): return self._fps

    @fps.setter
    def fps(self, value):
        assert(isinstance(value, int))
        self._fps = value

    @property
    def codec(self): return self._codec

    @codec.setter
    def codec(self, value):
        assert(isinstance(value, str))
        self._codec = value

    @property
    def rows(self): return self._rows

    @rows.setter
    def rows(self, value):
        self._rows = value

    @property
    def cols(self): return self._cols

    @cols.setter
    def cols(self, value):
        self._cols = value

    @property
    def dt(self): return self._dt

    @dt.setter
    def dt(self, value):
        self._dt = value

    @property
    def figsize(self): return self._figsize

    @figsize.setter
    def figsize(self, value):
        assert(isinstance(value, tuple) and len(value)==2)
        self._figsize = value

    @property
    def fontsize(self): return self._fontsize

    @fontsize.setter
    def fontsize(self, value):
        self._fontsize = value

    @property
    def suptitle(self): return self._title

    @suptitle.setter
    def suptitle(self, value):
        self._title = value

    @property
    def update_interval(self):
        """
        Gets or sets the update interval(in terms of time steps) for the animation.
        If value is 0 or None, update_interval will be set to the index of the
        final step. As a consequence, only the final frame will be generated.
        """
        return self._update_interval

    @update_interval.setter
    def update_interval(self, value):
        self._update_interval = value
Exemplo n.º 4
0
    def _initialize(self):

        # Count number of plots to create:
        num_plots = 0
        for config in self._config.itervalues():
            num_plots += len(config)

        # Set default grid of plot positions:
        if not self._rows*self._cols == num_plots:
            self._cols = int(np.ceil(np.sqrt(num_plots)))
            self._rows = int(np.ceil(num_plots/float(self._cols)))
        self.f, self.axarr = plt.subplots(self._rows, self._cols,
                                          figsize=self._figsize)

        # Remove unused subplots:
        for i in xrange(num_plots, self._rows*self._cols):
            plt.delaxes(self.axarr[np.unravel_index(i, (self._rows, self._cols))])
        cnt = 0
        self.handles = []
        self.types = []
        keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
        # TODO: Irregular grid in U will make the plot better
        U, V = np.mgrid[0:np.pi/2:complex(0, 60),
                        0:2*np.pi:complex(0, 60)]
        X = np.cos(V)*np.sin(U)
        Y = np.sin(V)*np.sin(U)
        Z = np.cos(U)
        self._dome_pos_flat = (X.flatten(), Y.flatten(), Z.flatten())
        self._dome_pos = (X, Y, Z)
        self._dome_arr_shape = X.shape
        if not isinstance(self.axarr, np.ndarray):
            self.axarr = np.asarray([self.axarr])
        for LPU, configs in self._config.iteritems():
            for plt_id, config in enumerate(configs):
                ind = np.unravel_index(cnt, self.axarr.shape)
                cnt+=1

                # Some plot types require specific numbers of
                # neuron ID arrays:
                if 'type' in config:
                    if config['type'] == 'quiver':
                        assert len(config['ids'])==2
                        config['type'] = 0
                    elif config['type'] == 'hsv':
                        assert len(config['ids'])==2
                        config['type'] = 1
                    elif config['type'] == 'image':
                        assert len(config['ids'])==1
                        config['type'] = 2
                    elif config['type'] == 'waveform':
                        config['type'] = 3
                    elif config['type'] == 'raster':
                        config['type'] = 4
                    elif config['type'] == 'rate':
                        config['type'] = 5
                    elif config['type'] == 'dome':
                        config['type'] = 6
                    else:
                        raise ValueError('Plot type not supported')
                else:
                    if str(LPU).startswith('input') and not self._graph[LPU].node[str(config['ids'][0][0])]['spiking']:
                        config['type'] = 2
                    else:
                        config['type'] = 4

                if config['type'] < 3:
                    if not 'shape' in config:

                        # XXX This can cause problems when the number
                        # of neurons is not equal to
                        # np.prod(config['shape'])
                        num_neurons = len(config['ids'][0])
                        config['shape'] = [int(np.ceil(np.sqrt(num_neurons)))]
                        config['shape'].append(int(np.ceil(num_neurons/float(config['shape'][0]))))

                if config['type'] == 0:
                    config['handle'] = self.axarr[ind].quiver(\
                               np.reshape(self._data[LPU][config['ids'][0],0],config['shape']),\
                               np.reshape(self._data[LPU][config['ids'][1],0],config['shape']))
                elif config['type'] == 1:
                    X = np.reshape(self._data[LPU][config['ids'][0],0],config['shape'])
                    Y = np.reshape(self._data[LPU][config['ids'][1],0],config['shape'])
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X,Y)+np.pi)/(2*np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H,S,V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'] = self.axarr[ind].imshow(RGB)
                elif config['type'] == 2:
                    if 'trans' in config:
                        if config['trans'] is True:
                            to_transpose = True
                        else:
                            to_transpose = False
                    else:
                        to_transpose = False
                        config['trans'] = False

                    if to_transpose:
                        temp = self.axarr[ind].imshow(np.transpose(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape'])))
                    else:
                        temp = self.axarr[ind].imshow(np.reshape(\
                                self._data[LPU][config['ids'][0],0], config['shape']))



                    temp.set_clim(self._imlim)
                    temp.set_cmap(plt.cm.gist_gray)
                    config['handle'] = temp
                elif config['type'] == 3:
                    fmt = config['fmt'] if 'fmt' in config else '' 
                    self.axarr[ind].set_xlim(self._xlim)
                    self.axarr[ind].set_ylim(self._ylim)
                    if len(config['ids'][0])==1:
                        config['handle'] = self.axarr[ind].plot([0], \
                                            [self._data[LPU][config['ids'][0][0],0]], fmt)[0]
                        config['ydata'] = [self._data[LPU][config['ids'][0][0],0]]
                    else:
                        config['handle'] = self.axarr[ind].plot(self._data[LPU][config['ids'][0],0])[0]

                elif config['type'] == 4:
                    config['handle'] = self.axarr[ind]
                    config['handle'].vlines(0, 0, 0.01)
                    config['handle'].set_ylim([.5, len(config['ids'][0]) + .5])
                    config['handle'].set_ylabel('Neurons',
                                                fontsize=self._fontsize-1, weight='bold')
                    config['handle'].set_xlabel('Time (s)',fontsize=self._fontsize-1, weight='bold')
                    min_id = min(self._id_to_data_idx[LPU].keys())
                    min_idx = self._id_to_data_idx[LPU][min_id]
                    config['handle'].set_xlim([0,len(self._data[LPU][min_idx,:])*self._dt])
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                elif config['type'] == 6:
                    self.axarr[ind].axes.set_yticks([])
                    self.axarr[ind].axes.set_xticks([])
                    self.axarr[ind] = self.f.add_subplot(self._rows,
                                                         self._cols,
                                                         cnt,
                                                         projection='3d')
                    config['handle' ] = self.axarr[ind]
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    if 'norm' not in config.keys():
                        config['norm'] = Normalize(vmin=-70, vmax=0, clip=True)
                    elif config['norm'] == 'auto':
                        if self._data[LPU].shape[1] > 100:
                            config['norm'] = Normalize(vmin = np.min(self._data[LPU][config['ids'][0],100:]),
                                                       vmax = np.max(self._data[LPU][config['ids'][0],100:]),
                                                       clip = True)
                        else:
                            config['norm'] = Normalize(vmin = np.min(self._data[LPU][config['ids'][0],:]),
                                                       vmax = np.max(self._data[LPU][config['ids'][0],:]),
                                                       clip = True)
                            
                    node_dict = self._graph[LPU].node
                    if str(LPU).startswith('input'):
                        latpositions = np.asarray([ node_dict[str(nid)]['lat'] \
                                                    for nid in range(len(node_dict)) \
                                                    if node_dict[str(nid)]['extern'] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long'] \
                                                     for nid in range(len(node_dict)) \
                                                     if node_dict[str(nid)]['extern'] ])
                    else:
                        latpositions = np.asarray([ node_dict[str(nid)]['lat']
                                                    for nid in config['ids'][0] ])
                        longpositions = np.asarray([ node_dict[str(nid)]['long']
                                                     for nid in config['ids'][0] ])
                    xx = np.cos(longpositions) * np.sin(latpositions)
                    yy = np.sin(longpositions) * np.sin(latpositions)
                    zz = np.cos(latpositions)
                    config['positions'] = (xx, yy, zz)
                    colors = griddata(config['positions'], self._data[LPU][config['ids'][0],0],
                                      self._dome_pos_flat, 'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(np.reshape(colors,
                                                [self._dome_arr_shape[0],self._dome_arr_shape[1],1])
                                     ,[1,1,4])
                    colors[:,:,3] = 1.0
                    config['handle'].plot_surface(self._dome_pos[0], self._dome_pos[1],
                                                  self._dome_pos[2], rstride=1, cstride=1,
                                                  facecolors=colors, antialiased=False,
                                                  shade=False)
                    
                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind],key, config[key])
                        except:
                            pass
                        try:
                            self._set_wrapper(config['handle'],key, config[key])
                        except:
                            pass
                
                if config['type']<3:
                    config['handle'].axes.set_xticks([])
                    config['handle'].axes.set_yticks([])

            if self.suptitle is not None:
                self.f.suptitle(self._title, fontsize=self._fontsize+1, x=0.5,y=0.03, weight='bold')

        plt.tight_layout()

        if self.out_filename:
            if self.FFMpeg is None:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps, codec=self.codec)
                elif which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg or avconv')
            elif self.FFMpeg:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg')
            else:
                if which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps, codec=self.codec)
                else:
                    raise RuntimeError('cannot find avconv')

            # Use the output file to determine the name of the temporary frame
            # files so that two concurrently run visualizations don't clobber
            # each other's frames:
            self.writer.setup(self.f, self.out_filename, dpi=80,
                              frame_prefix=os.path.splitext(self.out_filename)[0]+'_')
            self.writer.frame_format = 'png'
            self.writer.grab_frame()
        else:
            self.f.show()
Exemplo n.º 5
0
class visualizer(object):
    """
    Visualize the output produced by LPU models.

    Examples
    --------
    >>> import neurokernel.LPU.utils.visualizer as vis
    >>> V = vis.visualizer()
    >>> config1 = {}
    >>> config1['type'] = 'image'
    >>> config1['shape'] = [32,24]
    >>> config1['clim'] = [-0.6,0.5]
    >>> config2 = config1.copy()
    >>> config2['clim'] = [-0.55,-0.45]
    >>> V.add_LPU('lamina_output.h5', 'lamina.gexf.gz','lamina')
    >>> V.add_plot(config1, 'lamina', 'R1')
    >>> V.add_plot(config2, 'lamina', 'L1')
    >>> V.update_interval = 50
    >>> V.out_filename = 'test.avi'
    >>> V.run()
    """
    def __init__(self):
        self._xlim = [0, 1]
        self._ylim = [-1, 1]
        self._imlim = [-1, 1]
        self._update_interval = -1
        self._out_file = None
        self._fps = 5
        self._codec = 'libtheora'
        self._config = OrderedDict()
        self._rows = 0
        self._cols = 0
        self._figsize = (16, 9)
        self._fontsize = 18
        self._t = 0
        self._dts = {}
        self._sample_intervals = {}
        self._start_times = {}
        self._data = {}
        self._uids = {}
        self._maxt = None
        self._title = None
        self._FFMpeg = None

    def add_LPU(self,
                data_file,
                LPU='',
                win=None,
                is_input=False,
                gexf_file=None,
                sample_interval=1,
                start_time=0,
                dt=1e-4,
                transpose_axes=[1, 0]):
        """
        Add data associated with a specific LPU to a visualization.

        To add a plot containing neurons from a particular LPU,
        the LPU needs to be added to the visualization using this
        function. Note that outputs from multiple neurons can
        be visualized using the same visualizer object. The IDs
        specified in the arguments passed to `add_plot()` are assumed to be
        indices into array stored in the HDF5 file.

        Parameters
        ----------
        data_file : str
             Location of the HDF5 file generated by neurokernel
             containing the output of the LPU
        LPU : str
            Name of the LPU. Will be used as identifier to add plots.
            For input signals, the name of the LPU will be prepended
            with 'input_'. For example::

                V.add_LPU('vision_in.h5', LPU='vision')

            will create the LPU identifier 'input_vision'.
            Therefore, adding a plot depicting this input can be done by::

                V.add_plot({''type':'image',imlim':[-0.5,0.5]},LPU='input_vision)
        win : slice/list (Optional)
            Can be used to limit the visualization to a specific time window.
        gexf_file : string (Optional)
            Location of gexf file containg the graph of the LPU configuration
        graph : networkx graph object(Otional)
            NetworkX graph object representing the LPU configuration
        is_input : Boolean (optional)
            Set to true if the data_file represents input
        dt, start_time, sample_interval: double, double, int (All Optional)
            These arguments will only be used to set these attributes for
            input h5 files. For all other cases, these will be read from
            the h5 file
        
        All arguments beyond LPU should be considered strictly keyword only
        """

        if is_input:
            LPU = 'input_' + str(LPU)
            self._sample_intervals[LPU] = sample_interval
            self._dts[LPU] = dt * sample_interval
            self._start_times[LPU] = start_time
            f = h5py.File(data_file)
            self._uids[LPU] = {}
            self._data[LPU] = {}
            for k, d in f.items():
                self._uids[LPU][k] = f[k]['uids'].value
                self._data[LPU][k] = np.transpose(f[k]['data'].value,
                                                  axes=transpose_axes)

            self._config[LPU] = []
            if self._maxt:
                self._maxt = min(self._maxt,
                                 (self._data[LPU][k].shape[1] - 1) *
                                 self._dts[LPU])
            else:
                self._maxt = (self._data[LPU][k].shape[1] - 1) * self._dts[LPU]
            f.close()
            return

        self._config[LPU] = []
        f = h5py.File(data_file)

        self._sample_intervals[LPU] = f['metadata'].attrs['sample_interval']
        self._dts[
            LPU] = f['metadata'].attrs['dt'] * self._sample_intervals[LPU]
        self._start_times[LPU] = f['metadata'].attrs['start_time']
        self._uids[LPU] = {}
        self._data[LPU] = {}
        for k, d in f.items():
            if k == 'metadata': continue
            self._uids[LPU][k] = f[k]['uids'].value
            self._data[LPU][k] = np.transpose(f[k]['data'].value,
                                              axes=transpose_axes)

        if win is not None:
            for k in self._data[LPU].keys():
                self._data[LPU][k] = self._data[LPU][k][:, win]
        k = self._data[LPU].keys()[0]
        if self._maxt:
            self._maxt = min(self._maxt, (self._data[LPU][k].shape[1] - 1) *
                             self._dts[LPU])
        else:
            self._maxt = (self._data[LPU][k].shape[1] - 1) * self._dts[LPU]
        f.close()

    def run(self, final_frame_name=None, dpi=300):
        """
        Starts the visualization process.

        If the property `out_filename` is set, the visualization is saved as a
        video to the disk; if not, the animation is displayed on screen.
        Please refer to documentation of `add_LPU`, `add_plot`
        and the properties of this class on how to configure the visualizer
        before calling this method. An example can be found in the class doc
        string.

        Parameters
        ----------
        final_frame_name : str, optional
            If specified, the final frame of the animation is saved
            to disk.
        dpi : int, default=300
            Resolution at which final frame is saved to disk if
            `final_frame_name` is specified.

        Notes
        -----
        If `update_interval` is set to 0 or None, it will be replaced by the
        index of the final time step. As a result, the visualizer will only
        generate and save the final frame if `final_frame_name` is set.
        """

        self.final_frame_name = final_frame_name
        self._initialize()
        if not self._update_interval:
            self._update_interval = self._maxt
            self._t = self._maxt
        if self._update_interval == -1:
            self._update_interval = max(np.asarray(self._dts.values())) * 50
        for _ in np.arange(self._t, self._maxt * (1 + np.finfo(float).eps),
                           self._update_interval):
            self._update()
        if final_frame_name is not None:
            self.f.savefig(final_frame_name, dpi=dpi)
        if self.out_filename:
            self._close()

    def _set_wrapper(self, obj, name, value):
        name = name.lower()
        func = getattr(obj, 'set_' + name, None)
        if func:
            try:
                func(value, fontsize=self._fontsize, weight='bold')
            except:
                try:
                    func(value)
                except:
                    pass

    def _initialize(self):

        # Count number of plots to create:
        num_plots = 0
        for config in self._config.itervalues():
            num_plots += len(config)

        # Set default grid of plot positions:
        if not self._rows * self._cols == num_plots:
            self._cols = int(np.ceil(np.sqrt(num_plots)))
            self._rows = int(np.ceil(num_plots / float(self._cols)))
        self.f, self.axarr = plt.subplots(self._rows,
                                          self._cols,
                                          figsize=self._figsize)

        # Remove unused subplots:
        for i in xrange(num_plots, self._rows * self._cols):
            plt.delaxes(self.axarr[np.unravel_index(i,
                                                    (self._rows, self._cols))])
        cnt = 0
        self.handles = []
        self.types = []
        keywds = ['handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm']
        # TODO: Irregular grid in U will make the plot better
        U, V = np.mgrid[0:np.pi / 2:complex(0, 60), 0:2 * np.pi:complex(0, 60)]
        X = np.cos(V) * np.sin(U)
        Y = np.sin(V) * np.sin(U)
        Z = np.cos(U)
        self._dome_pos_flat = (X.flatten(), Y.flatten(), Z.flatten())
        self._dome_pos = (X, Y, Z)
        self._dome_arr_shape = X.shape
        if not isinstance(self.axarr, np.ndarray):
            self.axarr = np.asarray([self.axarr])
        for LPU, configs in self._config.iteritems():
            dt = self._dts[LPU]
            for plt_id, config in enumerate(configs):
                var = config['variable']
                ind = np.unravel_index(cnt, self.axarr.shape)
                cnt += 1
                # Some plot types require specific numbers of
                # neuron ID arrays:
                if 'type' in config:
                    if config['type'] == 'quiver':
                        assert len(config['ids']) == 2
                        config['type'] = 0
                    elif config['type'] == 'hsv':
                        assert len(config['ids']) == 2
                        config['type'] = 1
                    elif config['type'] == 'image':
                        assert len(config['ids']) == 1
                        config['type'] = 2
                    elif config['type'] == 'waveform':
                        config['type'] = 3
                    elif config['type'] == 'raster':
                        config['type'] = 4
                    elif config['type'] == 'rate':
                        config['type'] = 5
                    elif config['type'] == 'dome':
                        config['type'] = 6
                        assert ('lat' in config and 'long' in config)
                    else:
                        raise ValueError('Plot type not supported')
                else:
                    if (str(LPU).startswith('input')
                            and not self._graph[LPU].node[str(
                                config['ids'][0][0])]['spiking']):
                        config['type'] = 2
                    else:
                        config['type'] = 3

                if config['type'] < 3:
                    if not 'shape' in config:
                        num_neurons = len(config['ids'][0])
                        config['shape'] = [int(np.ceil(np.sqrt(num_neurons)))]
                        config['shape'].append(
                            int(
                                np.ceil(num_neurons /
                                        float(config['shape'][0]))))

                if config['type'] == 0:
                    config['handle'] = self.axarr[ind].quiver(\
                               np.reshape(self._data[LPU][var][config['ids'][0],0],config['shape']),\
                               np.reshape(self._data[LPU][var][config['ids'][1],0],config['shape']))
                elif config['type'] == 1:
                    X = np.reshape(self._data[LPU][var][config['ids'][0], 0],
                                   config['shape'])
                    Y = np.reshape(self._data[LPU][var][config['ids'][1], 0],
                                   config['shape'])
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X, Y) + np.pi) / (2 * np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H, S, V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'] = self.axarr[ind].imshow(RGB)
                elif config['type'] == 2:
                    if 'trans' in config:
                        if config['trans'] is True:
                            to_transpose = True
                        else:
                            to_transpose = False
                    else:
                        to_transpose = False
                        config['trans'] = False

                    if to_transpose:
                        temp = self.axarr[ind].imshow(np.transpose(np.reshape(\
                                self._data[LPU][var][config['ids'][0],0], config['shape'])))
                    else:
                        temp = self.axarr[ind].imshow(np.reshape(\
                                self._data[LPU][var][config['ids'][0],0], config['shape']))

                    temp.set_clim(self._imlim)
                    temp.set_cmap(plt.cm.gist_gray)
                    config['handle'] = temp
                elif config['type'] == 3:
                    fmt = config['fmt'] if 'fmt' in config else ''
                    self.axarr[ind].set_xlim(self._xlim)
                    self.axarr[ind].set_ylim(self._ylim)
                    if len(config['ids'][0]) == 1:
                        config['handle'] = self.axarr[ind].plot([0], \
                                            [self._data[LPU][var][config['ids'][0][0],0]], fmt)[0]
                        config['ydata'] = [
                            self._data[LPU][var][config['ids'][0][0], 0]
                        ]
                    else:
                        config['handle'] = self.axarr[ind].plot(
                            self._data[LPU][var][config['ids'][0], 0])[0]

                elif config['type'] == 4:
                    config['handle'] = self.axarr[ind]
                    config['handle'].vlines(0, 0, 0.01)
                    config['handle'].set_ylim([.5, len(config['ids'][0]) + .5])
                    config['handle'].set_ylabel('Neurons',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')
                    config['handle'].set_xlabel('Time (s)',
                                                fontsize=self._fontsize - 1,
                                                weight='bold')

                    config['handle'].set_xlim(
                        [0, self._data[LPU][var].shape[1] * dt])
                    config['handle'].axes.set_yticks([])
                    eps = np.finfo(float).eps
                    config['handle'].axes.set_xticks(\
                            np.linspace(0+self._start_times[LPU],
                                        self._start_times[LPU]+self._maxt,11))
                elif config['type'] == 6:
                    self.axarr[ind].axes.set_yticks([])
                    self.axarr[ind].axes.set_xticks([])
                    self.axarr[ind] = self.f.add_subplot(self._rows,
                                                         self._cols,
                                                         cnt,
                                                         projection='3d')
                    config['handle'] = self.axarr[ind]
                    config['handle'].axes.set_yticks([])
                    config['handle'].axes.set_xticks([])
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])
                    if 'norm' not in config.keys():
                        config['norm'] = Normalize(vmin=-70, vmax=0, clip=True)
                    elif config['norm'] == 'auto':
                        if self._data[LPU][var].shape[1] > 100:
                            config['norm'] = Normalize(
                                vmin=np.min(
                                    self._data[LPU][var][config['ids'][0],
                                                         100:]),
                                vmax=np.max(
                                    self._data[LPU][var][config['ids'][0],
                                                         100:]),
                                clip=True)
                        else:
                            config['norm'] = Normalize(
                                vmin=np.min(
                                    self._data[LPU][var][config['ids'][0], :]),
                                vmax=np.max(
                                    self._data[LPU][var][config['ids'][0], :]),
                                clip=True)

                    node_dict = self._graph[LPU].node
                    latpositions = config['lat']
                    longpositions = config['long']
                    xx = np.cos(longpositions) * np.sin(latpositions)
                    yy = np.sin(longpositions) * np.sin(latpositions)
                    zz = np.cos(latpositions)
                    config['positions'] = (xx, yy, zz)
                    colors = griddata(
                        config['positions'],
                        self._data[LPU][var][config['ids'][0],
                                             0], self._dome_pos_flat,
                        'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(
                        np.reshape(colors, [
                            self._dome_arr_shape[0], self._dome_arr_shape[1], 1
                        ]), [1, 1, 4])
                    colors[:, :, 3] = 1.0
                    config['handle'].plot_surface(self._dome_pos[0],
                                                  self._dome_pos[1],
                                                  self._dome_pos[2],
                                                  rstride=1,
                                                  cstride=1,
                                                  facecolors=colors,
                                                  antialiased=False,
                                                  shade=False)

                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind], key,
                                              config[key])
                        except:
                            pass
                        try:
                            self._set_wrapper(config['handle'], key,
                                              config[key])
                        except:
                            pass

                if config['type'] < 3:
                    config['handle'].axes.set_xticks([])
                    config['handle'].axes.set_yticks([])

            if self.suptitle is not None:
                self.f.suptitle(self._title,
                                fontsize=self._fontsize + 1,
                                x=0.5,
                                y=0.03,
                                weight='bold')

        plt.tight_layout()

        if self.out_filename and self.update_interval:
            if self.FFMpeg is None:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                elif which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg or avconv')
            elif self.FFMpeg:
                if which(matplotlib.rcParams['animation.ffmpeg_path']):
                    self.writer = FFMpegFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find ffmpeg')
            else:
                if which(matplotlib.rcParams['animation.avconv_path']):
                    self.writer = AVConvFileWriter(fps=self.fps,
                                                   codec=self.codec)
                else:
                    raise RuntimeError('cannot find avconv')

            # Use the output file to determine the name of the temporary frame
            # files so that two concurrently run visualizations don't clobber
            # each other's frames:
            self.writer.setup(
                self.f,
                self.out_filename,
                dpi=80,
                frame_prefix=os.path.splitext(self.out_filename)[0] + '_')
            self.writer.frame_format = 'png'
            self.writer.grab_frame()
        elif not self.final_frame_name:
            self.f.show()

    def _update(self):
        t = self._t
        for LPU, configs in self._config.iteritems():
            dt = self._dts[LPU]
            for config in configs:
                var = config['variable']
                data = self._data[LPU][var]
                if config['type'] == 3:
                    if round(float(t) / dt) >= data.shape[1]: continue
                    if len(config['ids'][0]) == 1:
                        s = int(round(float(t - self._update_interval) / dt))
                        e = int(round(float(t) / dt))
                        config['ydata'].extend(
                            np.reshape(np.double(data[config['ids'][0], \
                                        max(0,s):min(e,data.shape[1])]),(-1,)))
                        config['handle'].set_xdata(dt* np.arange(0, \
                                len(config['ydata']))+self._start_times[LPU])
                        config['handle'].set_ydata(np.asarray(config['ydata']))
                    else:
                        config['handle'].set_ydata(
                            data[config['ids'][0],
                                 int(round(float(t) / dt))])
                elif config['type'] == 4:
                    if int(round(float(t) / dt)) >= data.shape[1]: continue
                    for j, id in enumerate((config['ids'][0])):
                        s = int(round(float(t - self._update_interval) / dt))
                        e = int(round(float(t) / dt))
                        tmp = np.where(data[id, \
                                      max(0,s):min(e,data.shape[1])])[0]
                        for tind in tmp:
                            shift = self._start_times[LPU]
                            config['handle'].vlines(
                                float(shift + (max(0, s) + tind) * dt),
                                j + 0.75, j + 1.25)
                elif config['type'] == 0:
                    if int(round(float(t) / dt)) >= data.shape[1]: continue
                    ind = int(round(float(t) / dt))
                    shape = config['shape']
                    ids = config['ids']
                    config['handle'].U = np.reshape(data[ids[0], ind], shape)
                    config['handle'].V = np.reshape(data[ids[1], ind], shape)
                elif config['type'] == 1:
                    if int(round(float(t) / dt)) >= data.shape[1]: continue
                    ind = int(round(float(t) / dt))
                    shape = config['shape']
                    ids = config['ids']
                    X = np.reshape(data[ids[0], ind], shape)
                    Y = np.reshape(data[ids[1], ind], shape)
                    V = (X**2 + Y**2)**0.5
                    H = (np.arctan2(X, Y) + np.pi) / (2 * np.pi)
                    S = np.ones_like(V)
                    HSV = np.dstack((H, S, V))
                    RGB = hsv_to_rgb(HSV)
                    config['handle'].set_data(RGB)
                elif config['type'] == 2:
                    if int(round(float(t) / dt)) >= data.shape[1]: continue
                    ind = int(round(float(t) / dt))
                    ids = config['ids']
                    if config['trans']:
                        config['handle'].set_data(
                            np.transpose(
                                np.reshape(data[ids[0], ind],
                                           config['shape'])))
                    else:
                        config['handle'].set_data(
                            np.reshape(data[ids[0], ind], config['shape']))
                elif config['type'] == 6:
                    if int(round(float(t) / dt)) >= data.shape[1]: continue
                    ind = int(round(float(t) / dt))
                    ids = config['ids']
                    d = data[ids[0], ind]
                    colors = griddata(config['positions'], d,
                                      self._dome_pos_flat,
                                      'nearest').reshape(self._dome_arr_shape)
                    colors = config['norm'](colors).data
                    colors = np.tile(
                        np.reshape(colors, [
                            self._dome_arr_shape[0], self._dome_arr_shape[1], 1
                        ]), [1, 1, 4])
                    colors[:, :, 3] = 1.0

                    config['handle'].clear()
                    config['handle'].xaxis.set_ticks([])
                    config['handle'].yaxis.set_ticks([])
                    config['handle'].zaxis.set_ticks([])

                    config['handle'].plot_surface(self._dome_pos[0],
                                                  self._dome_pos[1],
                                                  self._dome_pos[2],
                                                  rstride=1,
                                                  cstride=1,
                                                  facecolors=colors,
                                                  antialiased=False,
                                                  shade=False)
                keywds = [
                    'handle', 'ydata', 'fmt', 'type', 'ids', 'shape', 'norm'
                ]
                for key in config.iterkeys():
                    if key not in keywds:
                        try:
                            self._set_wrapper(self.axarr[ind], key,
                                              config[key])
                        except:
                            pass

                        try:
                            self._set_wrapper(config['handle'], key,
                                              config[key])
                        except:
                            pass
        self.f.canvas.draw()
        if self.out_filename:
            self.writer.grab_frame()

        self._t += self._update_interval

    def add_plot(self, config_dict, LPU):
        """
        Add a plot to the visualizer

        Parameters
        ----------
        config_dict: dict
            A dictionary specifying the plot attributes. The attribute
            names should be the keys.

            The following are the plot attributes that can be specfied using
            this dict.

            type - str  (Required)
                This specifies the type of the plot. Has to be one of
                ['waveform', 'raster', 'image','hsv','quiver', 'dome']
                'dome' requires lat and long to specified. The size of lat and long
                should be the same as uids
            variable - str (Required)
                The variable to plot
            uids - list/tuple of either 1 or 2 list/tuple of uids
                Specifies the neuron uids from the associated LPU.
                The list of uids consistent with uids in the h5file.
                For example::

                    {'variable':'V','uids':[['1','2']]}

                will plot neurons with uids 1 and 2.
                Two lists of uids are needed if the plot is
                of type 'hsv' or 'quiver'
                For example::

                     {'variable':'V:,
                     'uids':[map(str,range(768)),map(str,range(768,1536)],'type':'HSV'}

                can be used to generate a HSV plot where the hue channel is
                controlled by the angle of the vector with the membrane
                potentials of the component with uids '0' through '767' as the x component
                and potentials of neurons with 'uids' '768' through '1535' as the y component.
                The value will be the magnitude of the same vector.

                This parameter must be specified for plots of type 'HSV' or 'quiver'
        
                If not specified, all components in the h5 file for which that variable was recorded
                will be plotted
        
            shape - list or tuple with two entries
                This attribute specifies the dimensions for plots of type image,
                hsv or quiver.

            title - str
                Optional. Can be used to control the title of the plot.


            In addition to the above, any parameter supported by matlpotlib
            for the particular type of plot can be specified.
            For example - 'imlim','clim','xlim','ylim' etc.
        LPU: str
            The name of the LPU associated to this plot.
        """

        config = config_dict.copy()
        assert ('variable' in config)
        var = config['variable']
        assert (LPU in self._uids and LPU in self._data)
        if 'uids' in config:
            if not (isinstance(config['uids'], list)
                    or isinstance(config['uids'], np.ndarray)):
                config['uids'] = [config['uids']]
            config['ids'] = []
            for uids in config['uids']:
                config['ids'].append([
                    np.where(self._uids[LPU][var] == uid)[0][0] for uid in uids
                ])
            self._config[LPU].append(config)
        elif str(LPU).startswith('input'):
            config['ids'] = [range(0, self._data[LPU][var].shape[0])]
            self._config[LPU].append(config)
        else:
            config['uids'] = self._uids[LPU][var]
            config['ids'] = [[range(0, len(config['uids']))]]
            self._config[LPU].append(config)
            #raise ValueError('uids must be provided')
            '''
            config['ids'] = {}
            for i,name in enumerate(names):
                config['ids'][i]=[]
                for id in range(len(self._graph[LPU].node)):
                    if self._graph[LPU].node[str(id)]['name'] == name:
                        config['ids'][i].append(id-shift)
            
            self._config[LPU].append(config)
            '''
        if not 'title' in config:
            if str(LPU).startswith('input_'):
                config['title'] = LPU.split('_', 1)[1] + ' - ' + 'Input'
            else:
                config['title'] = str(LPU)

    def _close(self):
        self.writer.finish()
        plt.close(self.f)

    @property
    def xlim(self):
        """
        X-axis limits for all the raster and waveform plots. Can be superseded
        for individual plots by specifying xlim in the config_dict for that
        plot.

        See Also
        --------
        add_plot

        """
        return self._xlim

    @xlim.setter
    def xlim(self, value):
        self._xlim = value

    @property
    def ylim(self):
        """
        Get or set the limits of the y-axis for all the raster and waveform plots.
        Can be superseded for individual plots by specifying xlim in the config_dict
        for that plot.

        See Also
        --------
        add_plot
        """
        return self._ylim

    @ylim.setter
    def ylim(self, value):
        self._ylim = value

    @property
    def FFMpeg(self):
        return self._FFMpeg

    @FFMpeg.setter
    def FFMpeg(self, value):
        self._FFMpeg = value

    @property
    def imlim(self):
        return self._imlim

    @imlim.setter
    def imlim(self, value):
        self._imlim = value

    @property
    def out_filename(self):
        return self._out_file

    @out_filename.setter
    def out_filename(self, value):
        assert (isinstance(value, str))
        self._out_file = value

    @property
    def fps(self):
        return self._fps

    @fps.setter
    def fps(self, value):
        assert (isinstance(value, int))
        self._fps = value

    @property
    def codec(self):
        return self._codec

    @codec.setter
    def codec(self, value):
        assert (isinstance(value, str))
        self._codec = value

    @property
    def rows(self):
        return self._rows

    @rows.setter
    def rows(self, value):
        self._rows = value

    @property
    def cols(self):
        return self._cols

    @cols.setter
    def cols(self, value):
        self._cols = value

    @property
    def figsize(self):
        return self._figsize

    @figsize.setter
    def figsize(self, value):
        assert (isinstance(value, tuple) and len(value) == 2)
        self._figsize = value

    @property
    def fontsize(self):
        return self._fontsize

    @fontsize.setter
    def fontsize(self, value):
        self._fontsize = value

    @property
    def suptitle(self):
        return self._title

    @suptitle.setter
    def suptitle(self, value):
        self._title = value

    @property
    def update_interval(self):
        """
        Update interval (in unit of time same as dt in the h5 files) for the animation.
        """
        return self._update_interval

    @update_interval.setter
    def update_interval(self, value):
        self._update_interval = value
Exemplo n.º 6
0
    def generate_video(self, data, coordinates, rng, videofile):
        from scipy.interpolate import griddata
        from mpl_toolkits.mplot3d import Axes3D
        import matplotlib
        matplotlib.use('Agg')
        from matplotlib import cm
        import matplotlib.pyplot as plt
        from matplotlib.animation import FFMpegFileWriter, AVConvFileWriter
        from matplotlib.colors import Normalize

        radius = self._radius

        # unpacking coordinates of data
        zpositions, thetapositions = coordinates
        # conversion to cartesian
        x = radius*np.cos(thetapositions).flatten()
        y = radius*np.sin(thetapositions).flatten()
        z = zpositions.flatten()

        # constructing screen grid
        Z, Theta = np.mgrid[z.min():z.max():complex(0, 60),
                            0:2*np.pi:complex(0, 60)]

        # conversion to cartesian
        X = radius*np.cos(Theta)
        Y = radius*np.sin(Theta)
        X_flat = X.flatten()
        Y_flat = Y.flatten()
        Z_flat = Z.flatten()

        # initialization
        fig = plt.figure(figsize=plt.figaspect(0.8), dpi=80)
        writer = AVConvFileWriter(fps=5, codec='mpeg4')
        writer.setup(
            fig, videofile, dpi=80,
            frame_prefix=os.path.splitext(videofile)[0]+'_')
        writer.frame_format = 'png'

        step = 100
        plt.hold(False)

        ax = fig.add_subplot('111', projection='3d')
        ax.set_title('Input')

        ax.xaxis.set_ticks([])
        ax.yaxis.set_ticks([])
        ax.zaxis.set_ticks([])

        norm = Normalize(vmin=rng[0], vmax=rng[1], clip=True)

        for i in range(0, len(data), step):
            data_flat = data[i].flatten()
            colors = griddata((x, y, z), data_flat,
                              (X_flat, Y_flat, Z_flat),
                              'nearest').reshape(X.shape)
            # normalize values
            colors = norm(colors).data
            # convert to RGB (equal values of R,G,B = greyscale)
            colors = np.tile(np.reshape(colors, [X.shape[0], X.shape[1], 1]),
                             [1, 1, 4])
            colors[:, :, 3] = 1.0

            ax.clear()
            ax.xaxis.set_ticks([])
            ax.yaxis.set_ticks([])
            ax.zaxis.set_ticks([])
            ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                            facecolors=colors, antialiased=False,
                            shade=False)
            fig.canvas.draw()
            writer.grab_frame()
        writer.finish()
Exemplo n.º 7
0
    def generate_video(self, data, coordinates, rng, videofile):
        from scipy.interpolate import griddata
        from mpl_toolkits.mplot3d import Axes3D
        import matplotlib
        matplotlib.use('Agg')
        from matplotlib import cm
        import matplotlib.pyplot as plt
        from matplotlib.animation import FFMpegFileWriter, AVConvFileWriter
        from matplotlib.colors import Normalize

        radius = self._radius

        # unpacking coordinates of data
        zpositions, thetapositions = coordinates
        # conversion to cartesian
        x = radius * np.cos(thetapositions).flatten()
        y = radius * np.sin(thetapositions).flatten()
        z = zpositions.flatten()

        # constructing screen grid
        Z, Theta = np.mgrid[z.min():z.max():complex(0, 60),
                            0:2 * np.pi:complex(0, 60)]

        # conversion to cartesian
        X = radius * np.cos(Theta)
        Y = radius * np.sin(Theta)
        X_flat = X.flatten()
        Y_flat = Y.flatten()
        Z_flat = Z.flatten()

        # initialization
        fig = plt.figure(figsize=plt.figaspect(0.8), dpi=80)
        writer = AVConvFileWriter(fps=5, codec='mpeg4')
        writer.setup(fig,
                     videofile,
                     dpi=80,
                     frame_prefix=os.path.splitext(videofile)[0] + '_')
        writer.frame_format = 'png'

        step = 100
        plt.hold(False)

        ax = fig.add_subplot('111', projection='3d')
        ax.set_title('Input')

        ax.xaxis.set_ticks([])
        ax.yaxis.set_ticks([])
        ax.zaxis.set_ticks([])

        norm = Normalize(vmin=rng[0], vmax=rng[1], clip=True)

        for i in range(0, len(data), step):
            data_flat = data[i].flatten()
            colors = griddata((x, y, z), data_flat, (X_flat, Y_flat, Z_flat),
                              'nearest').reshape(X.shape)
            # normalize values
            colors = norm(colors).data
            # convert to RGB (equal values of R,G,B = greyscale)
            colors = np.tile(np.reshape(colors, [X.shape[0], X.shape[1], 1]),
                             [1, 1, 4])
            colors[:, :, 3] = 1.0

            ax.clear()
            ax.xaxis.set_ticks([])
            ax.yaxis.set_ticks([])
            ax.zaxis.set_ticks([])
            ax.plot_surface(X,
                            Y,
                            Z,
                            rstride=1,
                            cstride=1,
                            facecolors=colors,
                            antialiased=False,
                            shade=False)
            fig.canvas.draw()
            writer.grab_frame()
        writer.finish()