def img_from_tex(tex, font_size=20): figure = matplotlib.figure.Figure(None, facecolor='white') #canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(None, -1, figure) canvas = matplotlib.backends.backend_wxagg.FigureCanvasAgg(figure) font_size = font_size tex = "${0}$".format(tex) figure.clear() figure.text(0.05, 0.5, tex, size=font_size) canvas.draw() filename = 'equ.png' figure.savefig(filename, dpi=600) img = Image.open(filename) imginv = ImageChops.invert(img.convert('L')) box = imginv.getbbox() img = img.crop(box) img = ImageOps.expand(img, border=10, fill=(255, 255, 255)) img.save(filename) return img
def tex2img(tex, filename="equ.png", font_size=20): font_size = 20 figure = matplotlib.figure.Figure(None, facecolor=(0, 0, 0, 0)) canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(figure) font_size = font_size tex = "${0}$".format(tex) figure.clear() figure.text(0.05, 0.5, tex, size=font_size) canvas.draw() png = "equ.png" figure.savefig(png, transparent=True, dpi=300, format="png") img = Image.open(png) imginv = ImageChops.invert(img.convert('L')) box = imginv.getbbox() img = img.crop(box) img = ImageOps.expand(img, border=10, fill=(255, 255, 255, 0)) img.save(png, format="png") return img
def deleteLater(self): """ Deletes this tab (later). """ for figure in self._figures: figure.clear() for axes in self._axes: axes.cla() del (self._figures, self._axes) gc.collect() super(WcpTab, self).deleteLater()
def draw_smiley(pos_x: float, pos_y: float, fade_in_frames: int) -> Generator[Image.Image, None, None]: """ Generator method for rendering an emoji style smiley face :param pos_x: x axes offset for the center of the smiley face :param pos_y: y axes offset for the center of the smiley face :param fade_in_frames: Number of frames to fade in the smiley face :return: generator to produce the PIL Image frames """ figure = plt.figure(figsize=(19.2, 10.8)) fade_in_alpha = np.power(np.linspace(0, 1, fade_in_frames), 2) for alpha in fade_in_alpha: figure.clear() ax = figure.add_axes([0, 0, 1, 1]) ax.set_xlim(-960, 960) ax.set_ylim(-540, 540) face_circle = Circle((pos_x, pos_y), 200, color="yellow", alpha=alpha) face_circle.set_facecolor("yellow") ax.add_patch(face_circle) left_eye_circle = Circle((pos_x - 80, pos_y + 50), 50, color="black", alpha=alpha) ax.add_patch(left_eye_circle) right_eye_circle = Circle((pos_x + 80, pos_y + 50), 50, color="black", alpha=alpha) ax.add_patch(right_eye_circle) smile = Wedge((pos_x, pos_y - 50), 100, 180, 0, color="black", alpha=alpha) ax.add_patch(smile) figure.set_facecolor("None") ax.axis("off") im = convert_plot_to_image(figure) yield im while True: yield im
def main(): """ Entry point for rendering the plot """ anim_file_path = Path("./test.mp4") figure = plt.figure(figsize=(19.2, 10.8)) file_writer = FFMpegFileWriter(fps=FRAME_RATE) with file_writer.saving(figure, anim_file_path, dpi=100): intro_text = Scene( 0, 169, 1, draw_text( sentence="I've seen things you people wouldn't believe", text_pos_list=[16, 44], alpha_transitions=60, persist_frames=0, fade_out_frames=24, font_size=48, left_offset=0.12, bottom_offset=0.0, ), ) eye = Scene( intro_text.start_frame, intro_text.end_frame, 0, draw_eye(axes_dims=[0, 0.22, 1.0, 0.8], persist_frames=24, fade_out_frames=24), ) heatmap = Scene( intro_text.end_frame - 47, intro_text.end_frame + 145, 2, draw_fire_automata( axes_dims=[0.2, 0.35, 0.6, 0.6], fade_in_frames=24, update_frames=144, fade_out_frames=24, ), ) gaussian = Scene( intro_text.end_frame + 1, intro_text.end_frame + 145, 1, draw_gaussian( axes_dims=[0.05, 0.1, 0.9, 0.25], fade_in_frames=24, update_frames=72, persist_frames=24, fade_out_frames=24, ), ) heatmaps_text = Scene( 145, 313, 1, draw_text( sentence="Heat maps on fire off the shoulder of a Gaussian", text_pos_list=[17, 48], alpha_transitions=60, persist_frames=24, fade_out_frames=24, font_size=48, left_offset=0.08, bottom_offset=0.0, ), ) learning_curve = Scene( heatmaps_text.end_frame + 1, heatmaps_text.end_frame + 277, 1, draw_learning_curve( topo_axes_dims=[0.01, 0.15, 0.5, 0.8], learning_curve_axes_dims=[0.54, 0.15, 0.44, 0.8], fade_in_frames=24, update_frames=156, persist_frames=72, fade_out_frames=24, ), ) residuals_text = Scene( heatmaps_text.end_frame + 1, heatmaps_text.end_frame + 277, 2, draw_text( sentence= "I watched residuals diminish down the arc of ten thousand weights", text_pos_list=[10, 41, 65], alpha_transitions=60, persist_frames=72, fade_out_frames=24, font_size=40, left_offset=0.015, bottom_offset=0.0, ), ) fade_text_1 = Scene( residuals_text.end_frame + 1, residuals_text.end_frame + 193, 2, draw_text( sentence="All these visuals", text_pos_list=[17], alpha_transitions=60, persist_frames=84, fade_out_frames=48, font_size=100, left_offset=0.2, bottom_offset=0.53, ), ) fade_text_2 = Scene( residuals_text.end_frame + 60, residuals_text.end_frame + 193, 2, draw_text( sentence="will fade in time", text_pos_list=[17], alpha_transitions=60, persist_frames=24, fade_out_frames=48, font_size=100, left_offset=0.2, bottom_offset=0.37, ), ) terrain = Scene( fade_text_2.end_frame + 49, fade_text_2.end_frame + 169, 1, draw_terrain( axes_dims=[0.05, 0.2, 0.9, 0.8], fade_in_frames=24, update_frames=72, fade_out_frames=24, frame_jiggle=0.01, ), ) tears_text = Scene( fade_text_2.end_frame + 1, fade_text_2.end_frame + 169, 2, draw_text( sentence="Like tears in terrain", text_pos_list=[11, 21], alpha_transitions=48, persist_frames=48, fade_out_frames=24, font_size=60, left_offset=0.3, bottom_offset=0, ), ) pi_text_1 = Scene( tears_text.end_frame + 1, tears_text.end_frame + 217, 2, draw_text( sentence="Time to pi", text_pos_list=[4, 10], alpha_transitions=72, persist_frames=72, fade_out_frames=0, font_size=80, left_offset=0.08, bottom_offset=0.8, ), ) pi_text_2 = Scene( tears_text.end_frame + 169, tears_text.end_frame + 217, 1, draw_text( sentence="Time to pip install matplotlib", text_pos_list=[30], alpha_transitions=24, persist_frames=100, fade_out_frames=0, font_size=80, left_offset=0.08, bottom_offset=0.8, ), ) smiley = Scene( tears_text.end_frame + 169, tears_text.end_frame + 217, 2, draw_smiley(fade_in_frames=24, pos_x=0, pos_y=0), ) active_scenes_list: List[Scene] = [ intro_text, eye, heatmap, gaussian, heatmaps_text, learning_curve, residuals_text, fade_text_1, fade_text_2, terrain, tears_text, pi_text_1, pi_text_2, smiley, ] active_scenes_list.sort(key=lambda scene: scene.zorder, reverse=True) for frame_number in itertools.count(): figure.clear() render_axes = figure.add_axes([0.0, 0.0, 1.0, 1.0]) render_axes.axis("off") active_scene_count = len(active_scenes_list) if active_scene_count <= 0: break rendered_scene: bool = False for scene_index in range(active_scene_count - 1, -1, -1): scene = active_scenes_list[scene_index] if frame_number >= scene.start_frame: if frame_number > scene.end_frame: del active_scenes_list[scene_index] else: render_axes.imshow(next(scene.render_frame)) rendered_scene = True if rendered_scene is True: file_writer.grab_frame(facecolor=BACKGROUND_COLOUR)
def draw_terrain( axes_dims: List[float], fade_in_frames: int, update_frames: int, fade_out_frames: int, frame_jiggle: float, ) -> Generator[Image.Image, None, None]: """ Generator method to draw California area, large cities and faults :param axes_dims: matplotlib offset and dimensions :param fade_in_frames: Number of frames to fade in graphic :param update_frames: Number of frames to animate for :param fade_out_frames: Number of frames to fade out the graphic :param frame_jiggle: Random offset to apply to the axes each animation :return: generator to produce the PIL Image frames """ # Downloaded http://naciscdn.org/naturalearth/packages/natural_earth_vector.zip and # unzipped into project directory coastlines_gdf = gpd.read_file( "./natural_earth_vector/10m_physical/ne_10m_land_scale_rank2.shp") populated_gdf = gpd.read_file( "./natural_earth_vector/10m_cultural/ne_10m_populated_places.dbf") # Downloaded https://earthquake.usgs.gov/static/lfs/nshm/qfaults/Qfaults_GIS.zip and # unzipped into project directory faults_gdf = gpd.read_file("./GIS Files/Shapefile/QFaults.shp") area = Polygon([(-124, 33.5), (-124, 38), (-115, 38), (-115, 33.5)]) pop_mask = populated_gdf.within(area) california_gdf = populated_gdf.loc[pop_mask] california_highpop_gdf = california_gdf[california_gdf["SCALERANK"] < 3] fault_mask = faults_gdf.intersects(area) im: Image.Image = Image.fromarray(np.zeros((1, 1, 4), dtype=np.uint8)) figure = plt.figure(figsize=(19.2, 10.8)) fade_in_alpha = np.power(np.linspace(0, 1, fade_in_frames), 2) for alpha in fade_in_alpha: figure.clear() with plt.style.context("dark_background"): ax = figure.add_axes(axes_dims) california_highpop_gdf.plot(ax=ax, zorder=2, color="blue", markersize=100) coastlines_gdf.plot(ax=ax, zorder=1, color="forestgreen") for x, y, label in zip( california_highpop_gdf.geometry.x, california_highpop_gdf.geometry.y, california_highpop_gdf["NAME"], ): ax.annotate( label, xy=(x, y), xytext=(15, -5), textcoords="offset points", zorder=3, fontsize=30, ) ax.set_xlim(-125, -116.111) ax.set_ylim(33.5, 38) fault_mask = faults_gdf.intersects(area) faults_gdf.loc[fault_mask].plot(color="red", ax=ax, zorder=1, alpha=0, linewidth=2) figure.set_facecolor("None") ax.axis("off") im = convert_plot_to_image(figure) pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) for frame_number in range(update_frames): figure.clear() with plt.style.context("dark_background"): jiggled_dims = copy.deepcopy(axes_dims) x_jiggle = np.random.random() x_jiggle = (x_jiggle * 2 - 1) * frame_jiggle y_jiggle = np.random.random() y_jiggle = (y_jiggle * 2 - 1) * frame_jiggle jiggled_dims[0] += x_jiggle jiggled_dims[1] += y_jiggle tear_alpha = (np.sin(frame_number / 4) + 1) / 2 ax = figure.add_axes(jiggled_dims) california_highpop_gdf.plot(ax=ax, zorder=2, color="blue", markersize=100) coastlines_gdf.plot(ax=ax, zorder=1, color="forestgreen") for x, y, label in zip( california_highpop_gdf.geometry.x, california_highpop_gdf.geometry.y, california_highpop_gdf["NAME"], ): ax.annotate( label, xy=(x, y), xytext=(15, -5), textcoords="offset points", zorder=3, fontsize=30, ) ax.set_xlim(-125, -116.111) ax.set_ylim(33.5, 38) faults_gdf.loc[fault_mask].plot(color="red", ax=ax, zorder=1, alpha=tear_alpha, linewidth=2) figure.set_facecolor("None") ax.axis("off") im = convert_plot_to_image(figure) yield im fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for frame_number in range(fade_out_frames): alpha = fade_out_alpha[frame_number] figure.clear() with plt.style.context("dark_background"): jiggled_dims = copy.deepcopy(axes_dims) x_jiggle = np.random.random() x_jiggle = (x_jiggle * 2 - 1) * frame_jiggle y_jiggle = np.random.random() y_jiggle = (y_jiggle * 2 - 1) * frame_jiggle jiggled_dims[0] += x_jiggle jiggled_dims[1] += y_jiggle tear_alpha = (np.sin((update_frames + frame_number) / 4) + 1) / 2 ax = figure.add_axes(jiggled_dims) california_highpop_gdf.plot(ax=ax, zorder=2, color="blue", markersize=100) coastlines_gdf.plot(ax=ax, zorder=1, color="forestgreen") for x, y, label in zip( california_highpop_gdf.geometry.x, california_highpop_gdf.geometry.y, california_highpop_gdf["NAME"], ): ax.annotate( label, xy=(x, y), xytext=(15, -5), textcoords="offset points", zorder=3, fontsize=30, ) ax.set_xlim(-125, -116.111) ax.set_ylim(33.5, 38) faults_gdf.loc[fault_mask].plot(color="red", ax=ax, zorder=1, alpha=tear_alpha, linewidth=2) figure.set_facecolor("None") ax.axis("off") im = convert_plot_to_image(figure) pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im
def draw_learning_curve( topo_axes_dims: List[float], learning_curve_axes_dims: List[float], fade_in_frames: int, update_frames: int, persist_frames: int, fade_out_frames: int, ) -> Generator[Image.Image, None, None]: """ Generator method to draw a mock logistic regression and learning curve :param topo_axes_dims: logistic regression topology axes :param learning_curve_axes_dims: learning curve plot axes :param fade_in_frames: Number of frames to fade in the visual :param update_frames: Number of frames to animate the graphic for :param persist_frames: Number of frames to persist the graphic :param fade_out_frames: Number of frames to fade out the graphic :return: generator for producing the PIL Image frames """ weights = 41 lines = np.zeros((weights + 2, 2, 2)) lines[:weights, 0, 0] = 20 lines[:weights, 0, 1] = np.linspace(-420, 420, weights) lines[:weights, 1, 0] = 300 lines[:weights, 1, 1] = np.linspace(-100, 100, weights) lines[weights] = np.array([[470, 0], [590, 0]]) lines[weights + 1] = np.array([[750, 0], [870, 0]]) colors = np.zeros((weights + 2, 4)) colors[:, [2, 3]] = 1 line_widths = np.ones(weights + 2) * 2 line_widths[weights:] = 5 epoch = np.linspace(0, 20, update_frames) error = (1.8 - 1.7 / (1 + np.exp(-epoch))) + np.random.randn( update_frames) * np.linspace(0, 0.005, update_frames) figure = plt.figure(figsize=(19.2, 10.8)) colors[:weights, 2] = np.random.random(weights) with plt.style.context("dark_background"): draw_learning_curve_axes( topo_axes_dims, learning_curve_axes_dims, epoch, error, figure, lines, colors, line_widths, 0, ) figure.set_facecolor("None") im = convert_plot_to_image(figure) # Fade in the axes over this many frames fade_in_alpha = np.power(np.linspace(0, 1, fade_in_frames), 2) for alpha in fade_in_alpha: pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) for frame in range(update_frames): colors[:weights, 2] = np.random.random(weights) figure.clear() with plt.style.context("dark_background"): draw_learning_curve_axes( topo_axes_dims, learning_curve_axes_dims, epoch, error, figure, lines, colors, line_widths, frame, ) figure.set_facecolor("None") im = convert_plot_to_image(figure) yield im for frame in range(persist_frames): yield im # Fade out the image over this many frames fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for alpha in fade_out_alpha: pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im
def draw_eye(axes_dims: List[float], persist_frames: int, fade_out_frames: int) -> Generator[Image.Image, None, None]: """ Generator method for rendering a stylized eye :param axes_dims: offset and dimensions of the axes to render to :param persist_frames: Number of frames to persist the frames to :param fade_out_frames: Number of frames to fade out the eye :return: Generator for producing PIL Image frames """ interval_count = 361 angle = np.linspace(0, np.pi * 2.0, interval_count) radius = np.array([num % 2 for num in range(0, interval_count)]) * 2.5 + 1.5 x = radius * np.cos(angle) y = radius * np.sin(angle) iris = np.vstack([x.reshape(1, -1), y.reshape(1, -1)]) intervals = np.linspace(-7.05, 7.05, interval_count) positive_curve = 0.075 * intervals**2 - 3.75 negative_curve = -0.075 * (intervals**2) + 3.75 im: Image.Image = Image.fromarray(np.zeros((1, 1, 4), dtype=np.uint8)) figure = plt.figure(figsize=(19.2, 10.8)) for i in range(1, interval_count + 3, 3): figure.clear() # Draw Iris ax = figure.add_axes(axes_dims) ax.fill_between( intervals[interval_count - i:], positive_curve[interval_count - i:], negative_curve[interval_count - i:], color="white", zorder=1, ) ax.plot(iris[0, 0:i], iris[1, 0:i], linewidth=5, color="steelblue", zorder=3) ax.fill_between( intervals, np.ones(interval_count) * 5, negative_curve, color="black", alpha=1.0, zorder=4, ) ax.fill_between( intervals, -np.ones(interval_count) * 5, positive_curve, color="black", alpha=1.0, zorder=4, ) ax.set_xlim(-9.6, 9.6) ax.set_ylim(-4.32, 4.32) ax.axis("off") patch = patches.Circle((0, 0), radius=4.02, color="black", zorder=2) ax.add_patch(patch) im = convert_plot_to_image(figure) yield im # Keep the image for this many frames for i in range(persist_frames): yield im # Fade out the image over this many frames fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for alpha in fade_out_alpha: pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im
def draw_gaussian( axes_dims: List[float], fade_in_frames: int, update_frames: int, persist_frames: int, fade_out_frames: int, ) -> Generator[Image.Image, None, None]: """ Generator method for drawing Gaussian :param axes_dims: Offset and dimensions of the plot axes :param fade_in_frames: Number of frames to fade in the graphic :param update_frames: Number of frames to update the graphic :param persist_frames: Number of frames to persist the graphic :param fade_out_frames: Number of frames to fade out the graphic :return: generator for producing the PIL Image frames """ figure = plt.figure(figsize=(19.2, 10.8)) with plt.style.context("dark_background"): ax = figure.add_axes(axes_dims) ax.spines["right"].set_visible(False) ax.spines["top"].set_visible(False) ax.spines["left"].set_linewidth(2) ax.spines["bottom"].set_linewidth(2) ax.set_xlim((-8.8, 8.8)) ax.set_ylim((-0.02, 0.42)) im = convert_plot_to_image(figure) # [0.05, 0.1, 0.9, 0.25] # Fade in the axes over this many frames fade_in_alpha = np.power(np.linspace(0, 1, fade_in_frames), 2) for alpha in fade_in_alpha: pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) # Animate the Guassian mu = 0 variance = 1 sigma = np.sqrt(variance) for frame in range(0, update_frames * 4, 4): figure.clear() with plt.style.context("dark_background"): ax = figure.add_axes(axes_dims) x = np.linspace(mu - 8 * sigma, mu + 8 * sigma, update_frames * 4) ax.plot( x[:frame], stats.norm.pdf(x[:frame], mu, sigma), linewidth=3, color="skyblue", ) ax.spines["right"].set_visible(False) ax.spines["top"].set_visible(False) ax.spines["left"].set_linewidth(2) ax.spines["bottom"].set_linewidth(2) ax.set_xlim((-8.8, 8.8)) ax.set_ylim((-0.02, 0.42)) im = convert_plot_to_image(figure) yield im for frame in range(persist_frames): yield im # Fade out the image over this many frames fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for alpha in fade_out_alpha: pixels = np.array(im) alpha_layer = pixels[:, :, 3] alpha_layer[alpha_layer > 0] = int(255 * alpha) yield Image.fromarray(pixels) # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im
def draw_fire_automata( axes_dims: List[float], fade_in_frames: int, update_frames: int, fade_out_frames: int, ) -> Generator[Image.Image, None, None]: """ Generator method for rendering the fire automata :param axes_dims: Offset and dimensions of the axes :param fade_in_frames: Number of frames to fade in the graphic :param update_frames: Number of frames to update the graphic :param fade_out_frames: Number of frames to fade out the graphic :return: Generator for producing PIL Image frames """ im: Image.Image = Image.fromarray(np.zeros((1, 1, 4), dtype=np.uint8)) fire_automata = FireAutomata(height=65, width=64, decay=0.95, spawn_points=20) figure = plt.figure(figsize=(19.2, 10.8)) fade_in_alpha = np.power(np.linspace(0, 1, fade_in_frames), 2) for alpha in fade_in_alpha: figure.clear() render_axes = figure.add_axes(axes_dims) fire_automata.update_heatmap() render_axes.imshow( fire_automata.heatmap[:-1, :], cmap="hot", interpolation="nearest", alpha=alpha, ) render_axes.axis("off") im = convert_plot_to_image(figure) yield im for frame_number in range(update_frames): figure.clear() render_axes = figure.add_axes(axes_dims) fire_automata.update_heatmap() render_axes.imshow(fire_automata.heatmap[:-1, :], cmap="hot", interpolation="nearest") render_axes.axis("off") im = convert_plot_to_image(figure) yield im fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for alpha in fade_out_alpha: figure.clear() render_axes = figure.add_axes(axes_dims) fire_automata.update_heatmap() render_axes.imshow( fire_automata.heatmap[:-1, :], cmap="hot", interpolation="nearest", alpha=alpha, ) render_axes.axis("off") im = convert_plot_to_image(figure) yield im # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im
def draw_text( sentence: str, text_pos_list: List[int], alpha_transitions: int, persist_frames: int, fade_out_frames: int, font_size: int, left_offset: float, bottom_offset: float, ) -> Generator[Image.Image, None, None]: """ Renders a sentence with configurable phrase boundaries :param sentence: Full text to render :param text_pos_list: Character offsets in the sentence to fade in :param alpha_transitions: Number of alpha increments to fade in each phrase :param persist_frames: Number of frames to persist the sentence once drawn :param fade_out_frames: Number of frames to fade out the sentence :param font_size: Size of the font to render the sentence with :param left_offset: axes offset for the text from the left boundary :param bottom_offset: axes offset for the text from the bottom boundary :return: Generator for producing PIL Image frames """ im: Image.Image = Image.fromarray(np.zeros((1, 1, 4), dtype=np.uint8)) figure = plt.figure(figsize=(19.2, 10.8)) alpha_array = np.power(np.linspace(0, 1, alpha_transitions), 2) for idx, text_pos in enumerate(text_pos_list): for alpha in alpha_array: figure.clear() text_axes = figure.add_axes([0.0, 0.0, 1.0, 1.0]) text_axes.axis("off") if idx > 0: text_axes.text( left_offset, bottom_offset, s=sentence[:text_pos_list[idx - 1]], fontsize=font_size, style="oblique", ha="left", va="bottom", color="white", alpha=1.0, ) text_axes.text( left_offset, bottom_offset, s=sentence[:text_pos], fontsize=font_size, style="oblique", ha="left", va="bottom", color="white", alpha=alpha, ) im = convert_plot_to_image(figure) yield im # Keep the image for this many frames for i in range(persist_frames): yield im # Fade out the image over this many frames fade_out_alpha = np.power(np.linspace(1, 0, fade_out_frames), 2) for alpha in fade_out_alpha: figure.clear() text_axes = figure.add_axes([0.0, 0.0, 1.0, 1.0]) text_axes.axis("off") text_axes.text( left_offset, bottom_offset, s=sentence, fontsize=font_size, style="oblique", ha="left", va="bottom", color="white", alpha=alpha, ) im = convert_plot_to_image(figure) yield im # Stay black for the remainder black_screen = np.array(im) black_screen[:, :, :] = 0 im = Image.fromarray(black_screen) while True: yield im