def play_movie(x, y, images=None, extent=None, aspect='equal', color='blue', edgecolor='none', orientation=None, save=False, save_movie_path='', nskip=0, artists=[[]], xlim=None, ylim=None, colornorm=None, colormap='jet', ghost_tail=20, ax=None, wedge_radius=0.01, circle_radius=0.005, deg=False, flip=False, imagecolormap='jet', mono=True, flipimgx=False, flipimgy=False): ''' Show an animation of N x,y trajectories with color, orientation, and a tail. And optionally background images. x -- list or np.array for x position of trajectory, OR, if multiple trajectories, list of lists/np.arrays y -- list or np.array for y position of trajectory, OR, if multiple trajectories, list of lists/np.arrays images -- - list of images to set as the background for the animation - or a single static image - or path to a directory with a sequence of pyplot.imread readable images (jpeg, png, etc), numbered in order - or a type with a __call__ attribute (eg. a function or class method). The function __call__ attribute should take a single input, the frame number, and return the image to be used. extent, aspect -- see matplotlib.pyplot.imshow for details - note "origin" from imshow does not work as expected, use flipimgx and flipimgy instead flipimgx -- flip the image in along the "x" axis (eg. reverse image columns), default: False. flipimgy -- flip the image in along the "y" axis (eg. reverse image rows), default: False. imagecolormap -- colormap for images, eg. 'jet' or 'gray' mono -- if the image is, or should be, mono (grayscale) set this to True (default: True). Required for colormaps to work properly color -- list, OR list of lists/np.arrays, OR string colornorm -- [min, max] to use for normalizing color If None, uses first trajectory to set colornorm orientation -- list, OR list of lists/np.arrays, OR None. If None, movie uses circles, else uses oriented wedges from fly_plot_lib.get_wedges... artists -- optional list of matplotlib artists xlim -- xlim for plot, if None, automatically generate limits based on trajectory ylim -- ylim for plot, if None, automatically generate limits based on trajectory ghost_tail -- number of frames of 'tail' to show ax -- matplotlib axis, optional wedge_radius -- size_radius used in fpl.get_wedges circle_radius -- radius used in fpl.get_circles currently changing size is NOT supported for animations. deg -- (bool) is orientation given in degrees? True means yes. Passed to fpl.get_wedges... flip -- (bool) flip orientation? True means yes. Passed to fpl.get_wedges... ''' plt.close('all') # prep plot if ax is None: fig = plt.figure() ax = fig.add_subplot(111) anim_params = {'frame': -1*(1+nskip)+1, 'movie_finished': False} # fix format for single trajectories if type(x) is list: if type(x[0]) is list or type(x[0]) is np.ndarray: print print type(x[0]) print str(len(x)) + ' flies!' else:# type(x[0]) is float or type(x[0]) is int or type(x[0]) is long or type(x[0]): print print 'Just one fly!' x = [x] y = [y] color = [color] edgecolor = [edgecolor] orientation = [orientation] else: print print str(len(x)) + ' flies!' if len(color) != len(x): only_color = color[0] color = [only_color for i in range(len(x))] if type(edgecolor) is not list: edgecolor = [edgecolor] if len(edgecolor) != len(x): only_edgecolor = edgecolor[0] edgecolor = [only_edgecolor for i in range(len(x))] if type(color[0]) is not str: if colornorm is None: colornorm = [np.min(color), np.max(color)] norm = matplotlib.colors.Normalize(colornorm[0], colornorm[1]) color_mappable = matplotlib.cm.ScalarMappable(norm, plt.get_cmap('jet')) else: colornorm = None colormap= None norm = None color_mappable = None flies = [] for i, xpos in enumerate(x): if orientation[i] is None: # use circles from scatter radius = circle_radius alpha = 1 radiusnorm = None maxradius = 1 minradius = 0 fly = fpl.get_circles_for_scatter(x[i], y[i], color=color[i], colormap=colormap, radius=radius, colornorm=colornorm, alpha=alpha, radiusnorm=radiusnorm, maxradius=maxradius, minradius=minradius, edgecolor=edgecolor) elif orientation[i] is not None: # use wedges from get_wedges fly = fpl.get_wedges_for_heading_plot(x[i], y[i], color[i], orientation[i], size_radius=wedge_radius, size_angle=20, colormap=colormap, colornorm=colornorm, alpha=1, flip=flip, deg=deg, nskip=0, center_offset_fraction=0.75, edgecolor=edgecolor) flies.append(fly) # add artists for artist in artists[i]: ax.add_artist(artist) # add images if images is not None: frame = 0 if hasattr(images, '__call__'): imgdata = images(frame) if len(imgdata.shape) > 2: imgdata = imgdata[:,:,0] if flipimgx: if flipimgy: imgdata = imgdata[::-1,::-1] else: imgdata = imgdata[::-1,:] elif flipimgy: imgdata = imgdata[:,::-1] else: imgdata = imgdata[:,:] else: imgdata = get_image_data(images, frame, mono=mono, flipimgx=flipimgx, flipimgy=flipimgy) img = ax.imshow( imgdata, extent=extent, cmap=plt.get_cmap(imagecolormap), zorder=-10) for fly in flies: ax.add_collection(fly) def init_plot(): for fly in flies: fly.set_color('none') if images is None: return flies else: animation_objects = [fly for fly in flies] animation_objects.insert(0, img) return animation_objects def updatefig(*args): anim_params['frame'] += 1 + nskip print anim_params['frame'] if anim_params['frame'] >= len(x[0])-1: anim_params['frame'] = 1 anim_params['movie_finished'] = True frame_start = anim_params['frame'] - ghost_tail frame_end = anim_params['frame'] if frame_start < 0: frame_start = 0 frames = np.arange(frame_start, frame_end, 1).tolist() for i, fly in enumerate(flies): if frame_end > len(x[i]): colors = ['none' for f in range(len(x[i]))] else: colors = ['none' for f in range(len(x[i]))] for f in frames: try: colors[f] = color_mappable.to_rgba(color[i][f]) except: colors[f] = color[i] if frame_end > len(x[i]): edgecolors = ['none' for f in range(len(x[i]))] else: edgecolors = ['none' for f in range(len(x[i]))] for f in frames: edgecolors[f] = edgecolor[i] fly.set_edgecolors(edgecolors) fly.set_facecolors(colors) if images is not None: if hasattr(images, '__call__'): imgdata = images(frames[-1]) if len(imgdata.shape) > 2: imgdata = imgdata[:,:,0] if flipimgx: if flipimgy: imgdata = imgdata[::-1,::-1] else: imgdata = imgdata[::-1,:] elif flipimgy: imgdata = imgdata[:,::-1] else: imgdata = imgdata[:,:] else: imgdata = get_image_data(images, frames[-1], mono=mono, flipimgx=flipimgx, flipimgy=flipimgy) img.set_array(imgdata) if save and not anim_params['movie_finished']: print 'saving frame: ', str(anim_params['frame']), ' -- if the animation you see is strange, do not worry, look at the pngs' frame_prefix = '_tmp' frame_prefix = os.path.join(save_movie_path, frame_prefix) strnum = str(anim_params['frame']) num_frame_digits = np.ceil( np.log10(len(x[0]))) + 1 while len(strnum) < num_frame_digits: strnum = '0' + strnum frame_name = frame_prefix + '_' + strnum + '_' + '.png' fig.savefig(frame_name, format='png') if save and anim_params['movie_finished']: print print 'Movie finished saving! Close the plot screen now.' print 'PNGs are at: ', save_movie_path print 'To turn the PNGs into a movie, you can run this command from inside the directory with the tmp files: ' print 'mencoder \'mf://*.png\' -mf type=png:fps=30 -ovc lavc -lavcopts vcodec=mpeg4 -oac copy -o animation.avi' if images is None: return flies else: animation_objects = [fly for fly in flies] animation_objects.insert(0, img) return animation_objects # generate and set limits: if xlim is None: xlim = [np.min(x[0]), np.max(x[0])] if ylim is None: ylim = [np.min(y[0]), np.max(y[0])] ax.set_xlim(xlim[0], xlim[1]) ax.set_ylim(ylim[0], ylim[1]) ax.set_aspect(aspect) # fpl.adjust_spines(ax, ['left', 'bottom'], xticks=xlim, yticks=ylim) ax.set_xlabel('x position, m') ax.set_ylabel('y position, m') ani = animation.FuncAnimation(fig, updatefig, init_func=init_plot, fargs=anim_params, interval=50, blit=True) plt.show()
def play_movie_3_axes(x, y, z, color, orientation=None, save=False, save_movie_path='', nskip=0, images=[None, None, None], images_extents=[None, None, None], artists=[[], [], []], lims=[None, None, None], colornorm=None, colormap='jet', ghost_tail=20): ''' Show an animation of an x,y,z trajectory with color and a tail. The axes are referred to in the following order: (xy, xz, yz) x, y, z -- lists or arrays of length N color -- list or array of length N, or color name string for constant color orientation -- list of length N, or None. If None, movie uses circles, else uses oriented wedges from fly_plot_lib.get_wedges... artists -- list of length 3, corresponding to x/y/z axes, each component should be a list of matplotlib artists lims -- list of limits for the three axes (xy, xz, yz), if None, automatically generate limits based on trajectory ghost_tail -- number of frames of 'tail' to show TODO: test and animate images, add image function instead of requiring premade list ''' # prep plot fig = plt.figure() ax_xy = fig.add_subplot(221) ax_xz = fig.add_subplot(223) ax_yz = fig.add_subplot(224) axes = [ax_xy, ax_xz, ax_yz] anim_params = {'frame': -1*(1+nskip)} if colornorm is None: colornorm = [np.min(color), np.max(color)] norm = matplotlib.colors.Normalize(colornorm[0], colornorm[1]) color_mappable = matplotlib.cm.ScalarMappable(norm, plt.get_cmap('jet')) if orientation is None: radius = 0.01 alpha = 1 radiusnorm = None maxradius = 1 minradius = 0 flies_xy = fpl.get_circles_for_scatter(x, y, color=color, colormap=colormap, radius=radius, colornorm=colornorm, alpha=alpha, radiusnorm=radiusnorm, maxradius=maxradius, minradius=minradius) flies_xz = fpl.get_circles_for_scatter(x, z, color=color, colormap=colormap, radius=radius, colornorm=colornorm, alpha=alpha, radiusnorm=radiusnorm, maxradius=maxradius, minradius=minradius) flies_yz = fpl.get_circles_for_scatter(y, z, color=color, colormap=colormap, radius=radius, colornorm=colornorm, alpha=alpha, radiusnorm=radiusnorm, maxradius=maxradius, minradius=minradius) def get_images(f): ims = [None for i in range(3)] origin = 'lower' zorder_image = 0 alpha_image = 0.5 for i, image in enumerate(images): if image is not None: if image_extents[i] is None: print 'Must include extent for image' raise(ValueError) if type(image) is list: im = image[f] else: im = image ims[i] = im return ims # initialize images: ims = get_images(0) ax_ims = [None for i in range(3)] for i, ax in enumerate(axes): if ims[i] is not None: im = ax.imshow(ims[i], extent=image_extens[i], origin='lower', cmap=plt.get_cmap(colormap), norm=norm, alpha=0.5, zorder=0) ax_ims[i] = im # add artists for i, ax in enumerate(axes): for artist in artists[i]: ax.add_artist(artist) ax_xy.add_collection(flies_xy) ax_xz.add_collection(flies_xz) ax_yz.add_collection(flies_yz) def init_plot(): flies_xy.set_color('none') flies_xz.set_color('none') flies_yz.set_color('none') flies_xy.set_edgecolors('none') flies_xz.set_edgecolors('none') flies_yz.set_edgecolors('none') return flies_xy, flies_xz, flies_yz def updatefig(*args): #x = anim_params['x'] #y = anim_params['y'] #z = anim_params['z'] #color = anim_params['color'] print anim_params['frame'] anim_params['frame'] += 1 + nskip frame_end = anim_params['frame'] + ghost_tail if frame_end > len(x): frame_end = len(x) if anim_params['frame'] >= len(x)-1: anim_params['frame'] = 0 frame_end = anim_params['frame'] + ghost_tail frames = np.arange(anim_params['frame'], frame_end, 1).tolist() colors = ['none' for i in range(len(x))] for f in frames: colors[f] = color_mappable.to_rgba(color[f]) flies_xy.set_facecolors(colors) flies_xz.set_facecolors(colors) flies_yz.set_facecolors(colors) if save: print anim_params['frame'] frame_prefix = '_tmp' frame_prefix = os.path.join(save_movie_path, frame_prefix) strnum = str(anim_params['frame']) num_frame_digits = np.ceil(len(x) / 10.) + 1 while len(strnum) < num_frame_digits: strnum = '0' + strnum frame_name = frame_prefix + '_' + strnum + '_' + '.png' fig.savefig(frame_name, format='png') return flies_xy, flies_xz, flies_yz # generate and set limits: maxs = [np.max(x), np.max(y), np.max(z)] mins = [np.min(x), np.min(y), np.min(z)] for i, lim in enumerate(lims): if lim is None: lims[i] = [mins[i], maxs[i]] ax_xy.set_xlim(lims[0][0], lims[0][1]) ax_xy.set_ylim(lims[1][0], lims[1][1]) ax_xz.set_xlim(lims[0][0], lims[0][1]) ax_xz.set_ylim(lims[2][0], lims[2][1]) ax_yz.set_xlim(lims[1][0], lims[1][1]) ax_yz.set_ylim(lims[2][0], lims[2][1]) for ax in axes: ax.set_aspect('equal') # fpl.adjust_spines(ax_xy, ['left'], yticks=[-.15, 0, .15]) fpl.adjust_spines(ax_xz, ['left', 'bottom'], xticks=[-.2, 0, 1], yticks=[-.15, 0, .15]) fpl.adjust_spines(ax_yz, ['right', 'bottom'], xticks=[-.15, 0, .15], yticks=[-.15, 0, .15]) # top left plot ax_xy.set_ylabel('y position, m') # bottom left plot ax_xz.set_xlabel('x position, m') ax_xz.set_ylabel('z position, m') # bottom right plot ax_yz.set_xlabel('y position, m') #ax_yz.set_ylabel('z position, m') # I can't figure out how to make this appear on the right side... ani = animation.FuncAnimation(fig, updatefig, init_func=init_plot, fargs=anim_params, interval=50, blit=True) plt.show()
def play_movie(x, y, images=None, extent=None, aspect='equal', color='blue', edgecolor='none', orientation=None, save=False, save_movie_path='', nskip=0, artists=[], xlim=None, ylim=None, colornorm=None, colormap='jet', ghost_tail=20, ax=None, wedge_radius=0.01, circle_radius=0.005, deg=False, flip=False, imagecolormap='jet', mono=True, flipimgx=False, flipimgy=False, strobe=False, sync_frames=None, figsize=(5, 3), dpi=72): ''' Show an animation of N x,y trajectories with color, orientation, and a tail. And optionally background images. x -- list or np.array for x position of trajectory, OR, if multiple trajectories, list of lists/np.arrays y -- list or np.array for y position of trajectory, OR, if multiple trajectories, list of lists/np.arrays images -- - list of images to set as the background for the animation - or a single static image - or path to a directory with a sequence of pyplot.imread readable images (jpeg, png, etc), numbered in order - or a type with a __call__ attribute (eg. a function or class method). The function __call__ attribute should take a single input, the frame number, and return the image to be used. extent, aspect -- see matplotlib.pyplot.imshow for details - note "origin" from imshow does not work as expected, use flipimgx and flipimgy instead flipimgx -- flip the image in along the "x" axis (eg. reverse image columns), default: False. flipimgy -- flip the image in along the "y" axis (eg. reverse image rows), default: False. imagecolormap -- colormap for images, eg. 'jet' or 'gray' mono -- if the image is, or should be, mono (grayscale) set this to True (default: True). Required for colormaps to work properly color -- list, OR list of lists/np.arrays, OR string colornorm -- [min, max] to use for normalizing color If None, uses first trajectory to set colornorm orientation -- list, OR list of lists/np.arrays, OR None. If None, movie uses circles, else uses oriented wedges from fly_plot_lib.get_wedges... artists -- optional list of matplotlib artists xlim -- xlim for plot, if None, automatically generate limits based on trajectory ylim -- ylim for plot, if None, automatically generate limits based on trajectory ghost_tail -- number of frames of 'tail' to show ax -- matplotlib axis, optional wedge_radius -- size_radius used in fpl.get_wedges circle_radius -- radius used in fpl.get_circles currently changing size is NOT supported for animations. deg -- (bool) is orientation given in degrees? True means yes. Passed to fpl.get_wedges... flip -- (bool) flip orientation? True means yes. Passed to fpl.get_wedges... ''' x = copy.copy(x) y = copy.copy(y) orientation = copy.copy(orientation) if orientation is None: orientation = [None for i in range(len(x))] plt.close('all') # prep plot if ax is None: fig = plt.figure(figsize=figsize, dpi=dpi) ax = fig.add_subplot(111) anim_params = { 'frame': -1 * (1 + nskip) + 1, 'movie_finished': False, 'strobe': strobe, 'strobeimg': None, 'frames': [] } # fix format for single trajectories if type(x) is list: if type(x[0]) is list or type(x[0]) is np.ndarray: print print type(x[0]) print str(len(x)) + ' flies!' if sync_frames is not None: largest_sync_frame = np.max(sync_frames) first_frames = [] for i, xi in enumerate(x): padding = [np.nan] * (largest_sync_frame - sync_frames[i]) x[i] = np.hstack((padding, x[i])) y[i] = np.hstack((padding, y[i])) color[i] = np.hstack((padding, color[i])) if orientation[0] is not None: orientation[i] = np.hstack((padding, orientation[i])) first_frames.append(len(padding)) else: first_frames = [0] * len(x) final_frames = [] longest_trajectory = 0 for i, xi in enumerate(x): if len(xi) > longest_trajectory: longest_trajectory = len(xi) for i, xi in enumerate(x): final_frames.append(len(xi)) for xi in x: print len(xi) else: # type(x[0]) is float or type(x[0]) is int or type(x[0]) is long or type(x[0]): print print 'Just one fly!' x = [x] y = [y] color = [color] edgecolor = [edgecolor] orientation = [orientation] first_frames = [0] final_frames = [len(x[0])] else: print print 'Not sure what format x is!' anim_params.setdefault('final_frames', final_frames) anim_params.setdefault('first_frames', first_frames) print 'length color: ', len(color), 'n flies: ', len(x) if len(color) != len(x): only_color = color[0] color = [only_color for i in range(len(x))] if type(edgecolor) is not list: edgecolor = [edgecolor] if len(edgecolor) != len(x): only_edgecolor = edgecolor[0] edgecolor = [only_edgecolor for i in range(len(x))] if type(color[0]) is not str: if colornorm is None: colornorm = [np.min(color), np.max(color)] norm = matplotlib.colors.Normalize(colornorm[0], colornorm[1]) color_mappable = matplotlib.cm.ScalarMappable(norm, plt.get_cmap(colormap)) else: colornorm = None colormap = None norm = None color_mappable = None flies = [] for i, xpos in enumerate(x): if orientation[i] is None: # use circles from scatter radius = circle_radius alpha = 1 radiusnorm = None maxradius = 1 minradius = 0 fly = fpl.get_circles_for_scatter(x[i], y[i], color=color[i], colormap=colormap, radius=radius, colornorm=colornorm, alpha=alpha, radiusnorm=radiusnorm, maxradius=maxradius, minradius=minradius, edgecolor=edgecolor) elif orientation[i] is not None: # use wedges from get_wedges fly = fpl.get_wedges_for_heading_plot(x[i], y[i], color[i], orientation[i], size_radius=wedge_radius, size_angle=20, colormap=colormap, colornorm=colornorm, alpha=1, flip=flip, deg=deg, nskip=0, center_offset_fraction=0.75, edgecolor=edgecolor) flies.append(fly) # add artists for artist in artists: ax.add_artist(artist) # add images if images is not None: frame = 0 if hasattr(images, '__call__'): imgdata = images(frame) if len(imgdata.shape) > 2: imgdata = imgdata[:, :, 0] if flipimgx: if flipimgy: imgdata = imgdata[::-1, ::-1] else: imgdata = imgdata[::-1, :] elif flipimgy: imgdata = imgdata[:, ::-1] else: imgdata = imgdata[:, :] else: imgdata = get_image_data(images, frame, mono=mono, flipimgx=flipimgx, flipimgy=flipimgy) img = ax.imshow(imgdata, extent=extent, cmap=plt.get_cmap(imagecolormap), zorder=-10) for fly in flies: ax.add_collection(fly) def init_plot(): for fly in flies: fly.set_color('none') if images is None: return flies else: animation_objects = [fly for fly in flies] animation_objects.insert(0, img) return animation_objects def updatefig(*args): anim_params['frame'] += 1 + nskip if anim_params['frame'] >= len(x[0]) - 1: anim_params['frame'] = 1 anim_params['movie_finished'] = True anim_params['frames'].append(anim_params['frame']) print anim_params['frame'] for i, fly in enumerate(flies): frame_start = anim_params['frame'] - ghost_tail frame_end = anim_params['frame'] if frame_start < anim_params['first_frames'][i]: frame_start = anim_params['first_frames'][i] if frame_end > anim_params['final_frames'][i]: frame_end = anim_params['final_frames'][i] if frame_end < frame_start + nskip: continue frames = np.arange(frame_start, frame_end, nskip + 1).tolist() colors = ['none' for f in range(len(x[i]))] edgecolors = ['none' for f in range(len(x[i]))] for f in frames: try: colors[f] = color_mappable.to_rgba(color[i][f]) except: colors[f] = color[i] edgecolors[f] = edgecolor[i] fly.set_edgecolors(edgecolors) fly.set_facecolors(colors) if images is not None: if hasattr(images, '__call__'): imgdata = images(frames[-1]) if len(imgdata.shape) > 2: imgdata = imgdata[:, :, 0] if flipimgx: if flipimgy: imgdata = imgdata[::-1, ::-1] else: imgdata = imgdata[::-1, :] elif flipimgy: imgdata = imgdata[:, ::-1] else: imgdata = imgdata[:, :] else: imgdata = get_image_data(images, frames[-1], mono=mono, flipimgx=flipimgx, flipimgy=flipimgy) if anim_params['strobe']: if anim_params['strobeimg'] is None: anim_params['strobeimg'] = imgdata else: anim_params['strobeimg'] = nim.darken( [anim_params['strobeimg'], imgdata]) img.set_array(anim_params['strobeimg']) else: img.set_array(imgdata) if save and not anim_params['movie_finished']: print 'saving frame: ', str( anim_params['frame'] ), ' -- if the animation you see is strange, do not worry, look at the pngs' frame_prefix = '_tmp' frame_prefix = os.path.join(save_movie_path, frame_prefix) strnum = str(anim_params['frame']) num_frame_digits = np.ceil(np.log10(len(x[0]))) + 1 while len(strnum) < num_frame_digits: strnum = '0' + strnum frame_name = frame_prefix + '_' + strnum + '_' + '.png' flytext.set_fontsize(fig, 8) fig.savefig(frame_name, format='png') if save and anim_params['movie_finished']: print print 'Movie finished saving! Close the plot screen now.' print 'PNGs are at: ', save_movie_path print 'To turn the PNGs into a movie, you can run this command from inside the directory with the tmp files: ' print 'mencoder \'mf://*.png\' -mf type=png:fps=30 -ovc lavc -lavcopts vcodec=mpeg4 -oac copy -o animation.avi' if images is None: return flies else: animation_objects = [fly for fly in flies] animation_objects.insert(0, img) return animation_objects # generate and set limits: if xlim is None: xlim = [np.min(x[0]), np.max(x[0])] if ylim is None: ylim = [np.min(y[0]), np.max(y[0])] ax.set_xlim(xlim[0], xlim[1]) ax.set_ylim(ylim[0], ylim[1]) ax.set_aspect(aspect) # fpl.adjust_spines(ax, ['left', 'bottom'], xticks=xlim, yticks=ylim) ax.set_xlabel('x position, m') ax.set_ylabel('y position, m') ani = animation.FuncAnimation(fig, updatefig, init_func=init_plot, fargs=anim_params, interval=50, blit=True) plt.show()