From 14f85b1ad2d9bef03f022640728e6db88b950861 Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 12:53:52 -0800 Subject: [PATCH 1/9] initial commit --- .../movie_dataset_formation_breakdown.py | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py new file mode 100644 index 00000000..97c02d36 --- /dev/null +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py @@ -0,0 +1,177 @@ +# %% +# create the saving directory if it doesn"'t exist +import os +import matplotlib.pyplot as plt +from matplotlib.collections import PatchCollection # type: ignore +import pandas as pd + +from nuc_morph_analysis.analyses.dataset_images_for_figures import figure_helper +from nuc_morph_analysis.lib.preprocessing import global_dataset_filtering +from nuc_morph_analysis.lib.visualization.example_tracks import EXAMPLE_TRACKS + +from nuc_morph_analysis.analyses.dataset_images_for_figures.figure_helper import AxesCreator + +import matplotlib +matplotlib.rcParams["pdf.fonttype"] = 42 +matplotlib.rcParams["font.family"] = "Arial" + +load_local = True +make_frames = True + +# set figure, panel to get save directory info +figure = "dataset" +panel = "formation_and_breakdown" +savedir, fig_panel_str = figure_helper.get_save_dir_and_fig_panel_str(figure, panel) + +# set colony to medium +colony = "medium" + +if make_frames: + if load_local: + if not os.path.exists(savedir/ "df_fmb.pkl"): + print("no saved df_fmb.pkl, processing images and saving") + # load the tracking CSV for medium from FMS + # define dataset from which to collect images + # collect information for the dataset + df = global_dataset_filtering.load_dataset_with_features() + df = df[df["colony"] == colony] + df_fb = figure_helper.assemble_formation_breakdown_movie_dataframe(df) + # load the images for each timepoint and add them to the dataframe + seg_img_list, raw_img_list = figure_helper.load_images_for_formation_middle_breakdown( + df_fb, df, colony + ) + df_fb = figure_helper.process_images_and_add_to_dataframe(df_fb, df, seg_img_list, raw_img_list) + df_fb.to_pickle(os.path.join(savedir, "df_fmb.pkl")) + print("saved df_fmb.pkl") + else: + print("reading from saved df_fmb.pkl") + df_fb = pd.read_pickle(os.path.join(savedir, "df_fmb.pkl")) + + # %% + # determine line width for tails and contours and ROI drawn with matplotlib + contour_linewidth = 0.3 + axes_outline_width = 2 + dpi = 400 + fontsize = 8 + + # intialize the figure + # create a figure with the desired total width + fig_width = 8.5 # total width of the figure in inches + fig_height = 11 # total height of the figure in inches + + # define the width of the axes in inches and convert to figure units + ax_width_inch = 1.5 # width of the axes in inches + + # define the x and y coordinates + ax_x_inch = 0.1 # x position of the axes in inches + ax_y_inch = 0.1 # y position of the axes in inches + ax_gap_inch = 0.03 # gap between axes in inches + + ax_y_gap_inch = 0.4 # gap between top middle and bottom axes in inches + ax_y_small_gap_inch = 0.02 # gap between yx and zx axes in inches + + # Create the figure + fig = plt.figure(figsize=(fig_width, fig_height)) + zx_crop = [0.2, 0.7] # percentage of the image to crop in the zx view (lower, higher) + + # %% + # Loop over frames and views (ZX, ZY) to create the movie + ax_dict = {} + dfb = df_fb.reset_index().set_index("t") + for i in range(dfb.shape[0]): + for v, view in enumerate(["zx", "yx"]): + + img_name = f"{view}_{i}" + img = dfb.loc[i, f"raw_{view}"][0] + seg_contours = dfb.loc[i, f"seg_{view}_contours"][0] + + if "zx" in img_name: + ac = AxesCreator(fig, img, zcrop_percentages=zx_crop) + else: + ac = AxesCreator(fig, img, zcrop_percentages=None) + + if "zx" in img_name: + ax_y_inch_input = ax_y_inch + else: + ax_y_inch_input = ( + ax_dict["zx_0"].get_position().y1 * fig_height + ) + ax_y_small_gap_inch + + ac.define_axis_size( + ax_width_inch=ax_width_inch, + ax_x_inch=ax_x_inch, + ax_y_inch=ax_y_inch_input, + ax_gap_inch=ax_gap_inch, + ) + ax_x = ac.ax_x + ax = ac.add_axes(ax_x=ax_x) + ax_dict[img_name] = ax + + ax.imshow( + img, + cmap="gray", + interpolation="nearest", + zorder=1, + aspect="auto", + ) + + if "zx" in img_name: # crop the image in Z using ylim + img_height = img.shape[0] + ylimits = [ + img_height * (1 - zx_crop[0]), + img_height * (1 - zx_crop[1]), + ] # need to keep them reversed for image to display with Z=0 on bottom! + ax.set_ylim(ylimits) + + # draw the segmentation contours as dashed yellow lines + contour_and_color_list = dfb.loc[i, f"seg_{view}_contours"][0] + if (contour_and_color_list is not None) & (contour_and_color_list != "None"): + for contour_and_color in contour_and_color_list: + contour = contour_and_color[0] + linestyle = (0, (5, 5)) # 5 point length, 10 point gap + + pc_contours = PatchCollection( + [contour], + edgecolor=contour_and_color[1], + linewidths=figure_helper.determine_linewidth_from_desired_microns( + fig, ax, contour_linewidth + ), + facecolor="None", + zorder=1000, + linestyle=linestyle, + ) + ax.add_collection(pc_contours) + + for ax_name, ax in ax_dict.items(): + # add scale bar, timestamp and view arrows as appropriate + if "zx" in ax_name: + view = "ZX" + else: + # for just the YX view, add a scale bar and timestamp + view = "YX" + figure_helper.draw_scale_bar_matplotlib( + ax, colony, scalebarum=5, add_text=True, fontsize=fontsize, loc="right" + ) + frame = dfb.loc[i, "index_sequence"] + figure_helper.draw_timestamp(ax, frame, fontsize=fontsize) + # add arrows that orient the viewer + figure_helper.add_view_arrows(ax, view=view) + + + # now save the figure as a png + track_id = EXAMPLE_TRACKS["figure_dataset_formation_and_breakdown"] + savepath = os.path.join(savedir, "movie", f"frame_{i}.png") + fig.savefig( + savepath, + bbox_inches="tight", + dpi=dpi, + transparent=True, + ) + os.chmod(savepath, 0o777) + +print ("Making mp4") +# get list of paths to all movie frames, make sure they are +# sorted in timepoint order then save to an mp4 movie +frame_list = os.listdir(savedir / "movie") +sorted_frames = sorted(frame_list, key=lambda x: int(x.split("_")[1].split(".")[0])) +figure_helper.make_mp4(savedir, "formation_breakdown_example_movie", frame_list) From 596f2c9115e97f651d405b2a8d7c125e1500ab6d Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 12:55:27 -0800 Subject: [PATCH 2/9] move axescreator class here, adjust scalebar to not overlap with its text, add timestamp function and add function for the movie specific df creation --- .../figure_helper.py | 195 +++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py index a660bd6d..c4732d88 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py @@ -17,6 +17,7 @@ from tqdm import tqdm from matplotlib.patches import Polygon from skimage import measure +import imageio INTENSITIES_DICT = { "egfp_max": (110, 140), @@ -31,6 +32,57 @@ "seg": ("max", "mid", "mid"), } +class AxesCreator: + def __init__(self, fig, img, zcrop_percentages): + self.fig = fig + self.img = img + self.zcrop_percentages = zcrop_percentages + fig_width, fig_height = fig.get_size_inches() + self.fig_width = fig_width + self.fig_height = fig_height + + def define_axis_size(self, ax_width_inch, ax_x_inch, ax_y_inch, ax_gap_inch): + # determine axis height based on image aspect ratio + if self.zcrop_percentages is None: + ax_height_inch = ax_width_inch * ( + self.img.shape[0] / self.img.shape[1] + ) # Height of the axes in inches + else: + self_height = self.img.shape[0] * ( + self.zcrop_percentages[1] - self.zcrop_percentages[0] + ) + self_width = self.img.shape[1] + ax_height_inch = ax_width_inch * ( + self_height / self_width + ) # Height of the axes in inches + + # convert to figure units + self.ax_width = ax_width_inch / self.fig_width # Width of the axes in figure units + self.ax_height = ax_height_inch / self.fig_height # Height of the axes in figure units + self.ax_x = ax_x_inch / self.fig_width # x position of the axes in figure units + self.ax_y = ax_y_inch / self.fig_height # y position of the axes in figure units + self.ax_gap = ax_gap_inch / self.fig_width # gap between axes in figure units + + def add_axes( + self, + ax_x=None, + ax_y=None, + ax_width=None, + ax_height=None, + ): + if ax_x is None: + ax_x = self.ax_x + if ax_y is None: + ax_y = self.ax_y + if ax_width is None: + ax_width = self.ax_width + if ax_height is None: + ax_height = self.ax_height + + ax = self.fig.add_axes([ax_x, ax_y, ax_width, ax_height]) + ax.axis("off") + return ax + def process_channels_into_slices(img, channel, dtype="uint16"): """ @@ -315,7 +367,7 @@ def draw_scale_bar_matplotlib( # the height is 5% of the axes height - ax.plot([x, x2], [y, y], color="w", linewidth=2) + ax.plot([x, x2], [y + 3, y + 3], color="w", linewidth=2) # y2 needs to define a location above the linewidth if add_text: unitsstr = r"μm" @@ -329,6 +381,81 @@ def draw_scale_bar_matplotlib( va="bottom", ) +def draw_timestamp(ax, frame, fontsize=8): + """ + Draw a timestep in the upper right hand corner of an image + + Parameters + ---------- + ax : matplotlib axis + The axis to draw the scale bar on + frame : int + frame number + fontsize : int + The fontsize of the text + + Returns + ------- + None + """ + + # define the X coordinates as fixed distance from left side of image + x = 5 + + # define the Y coordinate as 5% of the axes height + y_min_inches = 0.12 # inches from the bottom of the axes + ylim_width = np.abs(ax.get_ylim()[1] - ax.get_ylim()[0]) + axis_height_inches = ( + ax.get_position().height * ax.get_figure().get_figheight() + ) # convert axes height to inches + y = ylim_width * ( + y_min_inches / axis_height_inches + ) # NOTE: the max of ylim is the bottom of an image in matplotlib + + # get time of frame in hr and min and make hr always 2 digits + t_hr = str(int(np.floor(frame * 5/ 60))) + if len(t_hr) == 1: + t_hr = "0"+t_hr + t_min = str(int(frame * 5 % 60)) + if len(t_min) == 1: + t_min = "0"+ t_min + + ax.text( + x, + y, + f"{t_hr}:{t_min} hr:min", + color="w", + fontsize=fontsize, + ha="left", + va="bottom", + ) + +def make_mp4(save_path, movie_name, files, fps=4): + """ + Make an mp4 movie from a list of images + + Parameters + ---------- + save_path : Path + The path to save the movie to + movie_name : str + The name of the movie file (without extension) + files : list + The list of paths to image filew to load and make movie from + fps : int + The frames per second of the movie + + Returns + ------- + None + """ + writer = imageio.get_writer(save_path / f"{movie_name}.mp4", fps=fps) + for im in files: + if "png" in im: + writer.append_data(imageio.imread(save_path / "movie" / im)) # add image to mp4 + # os.remove(im) # remove image from directory + writer.close() + def get_list_of_tail_lines( df, @@ -672,6 +799,72 @@ def assemble_formation_middle_breakdown_dataframe(df): df_fmb.loc[middle_of_track, "title_str"] = "middle" return df_fmb +# %% +def assemble_formation_breakdown_movie_dataframe(df): + """ + Assemble the dataframe for example movie of lamin shell formation and breakdown. This movie + includes timepoints from 2 frames before formation to 2 frames after breakdown for a specific nucleus + This dataframe specifies the coordinates of that nucleus at each timepoint along with the time + + Parameters + ---------- + df : pd.DataFrame + The tracking dataframe + + Returns + ------- + df_fb : pd.DataFrame + The dataframe for the timepoints two before frames before formation + through two frames after breakdown + """ + csv_dir = Path(__file__).parent / "csvs" + dff = pd.read_csv(csv_dir / "F1P4_track_id_83322_formation_centroids.csv") + dfb = pd.read_csv(csv_dir / "F1P4_track_id_83322_breakdown_centroids.csv") + dfman = pd.concat([dff, dfb]) + dfman.set_index("index_sequence", inplace=True) + # %% + # now use the tracking dataframe to retrieve the tracking centroids throughout the track + # combine the information from the CSVs and the tracking dataframe into a small dataframe specific to the track of interest + track_id = EXAMPLE_TRACKS["figure_dataset_formation_and_breakdown"] + dfi = df[df["track_id"] == track_id].set_index("index_sequence") + + # get list of all timepoints to include the movie + predicted_formation = dfi["predicted_formation"].values[0] + predicted_breakdown = dfi["predicted_breakdown"].values[0] + timepoints = np.arange( + predicted_formation-2, predicted_breakdown+2+1, 1 + ).astype(int) + + dflist = [] + + # iterate through all timepoints and add the coordinates to the dataframe + for ti, timepoint in enumerate(timepoints): + # use tracking centroid if segmentation present + if timepoint in dfi.index: + y = dfi.loc[timepoint, "centroid_y"] + x = dfi.loc[timepoint, "centroid_x"] + else: + # otherwise use manual tracking centroid + x = dfman.loc[timepoint, "x"] + y = dfman.loc[timepoint, "y"] + + feats = {} + feats["index_sequence"] = np.uint16(timepoint) + feats["t"] = np.uint16(ti) + feats["x"] = np.uint16(x) + feats["y"] = np.uint16(y) + feats["track_id"] = np.uint16(track_id) + dflist.append(pd.DataFrame(data=feats.values(), index=feats.keys()).T) + df_fb = pd.concat(dflist) + df_fb.set_index("index_sequence", inplace=True) + + df_fb = df_fb.groupby("index_sequence").agg("first") + df_fb["ival"] = [x for x in range(df_fb.shape[0])] + df_fb["x"] = df_fb["x"].astype("uint16") + df_fb["y"] = df_fb["y"].astype("uint16") + + return df_fb + def load_images_for_formation_middle_breakdown(df_fmb, df, colony): """ From 2103b47a0ce6259bda423949d6826527760fac41 Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 12:56:12 -0800 Subject: [PATCH 3/9] move axexcreator class def out so it can be reused elsewhere --- .../figure_dataset_formation_and_breakdown.py | 56 +------------------ 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_dataset_formation_and_breakdown.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_dataset_formation_and_breakdown.py index 6dc2befd..2c546448 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_dataset_formation_and_breakdown.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_dataset_formation_and_breakdown.py @@ -77,58 +77,6 @@ ax_y_small_gap_inch = 0.02 # gap between yx and zx axes in inches -class AxesCreator: - def __init__(self, fig, img, zcrop_percentages): - self.fig = fig - self.img = img - self.zcrop_percentages = zcrop_percentages - fig_width, fig_height = fig.get_size_inches() - self.fig_width = fig_width - self.fig_height = fig_height - - def define_axis_size(self, ax_width_inch, ax_x_inch, ax_y_inch, ax_gap_inch): - # determine axis height based on image aspect ratio - if self.zcrop_percentages is None: - ax_height_inch = ax_width_inch * ( - self.img.shape[0] / self.img.shape[1] - ) # Height of the axes in inches - else: - self_height = self.img.shape[0] * ( - self.zcrop_percentages[1] - self.zcrop_percentages[0] - ) - self_width = self.img.shape[1] - ax_height_inch = ax_width_inch * ( - self_height / self_width - ) # Height of the axes in inches - - # convert to figure units - self.ax_width = ax_width_inch / self.fig_width # Width of the axes in figure units - self.ax_height = ax_height_inch / self.fig_height # Height of the axes in figure units - self.ax_x = ax_x_inch / self.fig_width # x position of the axes in figure units - self.ax_y = ax_y_inch / self.fig_height # y position of the axes in figure units - self.ax_gap = ax_gap_inch / self.fig_width # gap between axes in figure units - - def add_axes( - self, - ax_x=None, - ax_y=None, - ax_width=None, - ax_height=None, - ): - if ax_x is None: - ax_x = self.ax_x - if ax_y is None: - ax_y = self.ax_y - if ax_width is None: - ax_width = self.ax_width - if ax_height is None: - ax_height = self.ax_height - - ax = self.fig.add_axes([ax_x, ax_y, ax_width, ax_height]) - ax.axis("off") - return ax - - # for img_str in ['BOTTOM_LEFT_IMG','BOTTOM_CENTER_IMG','BOTTOM_RIGHT_IMG']: # pass # Define the list of parameters @@ -154,9 +102,9 @@ def add_axes( seg_contours = dfb.loc[(class_name, ti), f"seg_{view}_contours"][0] if "zx" in img_name: - ac = AxesCreator(fig, img, zcrop_percentages=zx_crop) + ac = figure_helper.AxesCreator(fig, img, zcrop_percentages=zx_crop) else: - ac = AxesCreator(fig, img, zcrop_percentages=None) + ac = figure_helper.AxesCreator(fig, img, zcrop_percentages=None) if ("breakdown" in img_name) and ("zx" in img_name): ax_y_inch_input = ax_y_inch From a1f13da10b5f81ca8c1bcc5e4245271cdf9ae970 Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 12:56:42 -0800 Subject: [PATCH 4/9] add movie making to fig workflow --- .../analyses/dataset_images_for_figures/figure_1_workflow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py index a71b25fc..3ba4a2fa 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py @@ -9,3 +9,4 @@ import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_formation_and_breakdown import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_processing_workflow import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_save_3d_image_for_agave +import nuc_morph_analysis.analyses.dataset_images_for_figures.movie_dataset_formation_breakdown \ No newline at end of file From 7e20f1e0c729673f38e3e1d5e2b764ff2bce2dc2 Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 13:21:17 -0800 Subject: [PATCH 5/9] dont default to load local --- .../movie_dataset_formation_breakdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py index 97c02d36..10da11bd 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py @@ -15,7 +15,7 @@ matplotlib.rcParams["pdf.fonttype"] = 42 matplotlib.rcParams["font.family"] = "Arial" -load_local = True +load_local = False make_frames = True # set figure, panel to get save directory info From ade304ea5efa69fd2233d4dc7c545ff5382aac9a Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 13:25:16 -0800 Subject: [PATCH 6/9] dont make movie as part of fig 1 workflow or run all manuscript workflows will be verrrrrrrrrrrrry slooooooow --- .../analyses/dataset_images_for_figures/figure_1_workflow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py index 3ba4a2fa..be32bc17 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py @@ -8,5 +8,4 @@ import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_colony_snapshots import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_formation_and_breakdown import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_processing_workflow -import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_save_3d_image_for_agave -import nuc_morph_analysis.analyses.dataset_images_for_figures.movie_dataset_formation_breakdown \ No newline at end of file +import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_save_3d_image_for_agave \ No newline at end of file From 2d73ba5688bc134ab02e291fdb5c83b2e4a41fbf Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 13:29:20 -0800 Subject: [PATCH 7/9] readd blank line at file end --- .../analyses/dataset_images_for_figures/figure_1_workflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py index be32bc17..a71b25fc 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_1_workflow.py @@ -8,4 +8,4 @@ import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_colony_snapshots import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_formation_and_breakdown import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_processing_workflow -import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_save_3d_image_for_agave \ No newline at end of file +import nuc_morph_analysis.analyses.dataset_images_for_figures.figure_dataset_save_3d_image_for_agave From 2ff02624289202beb6e99aa0d6a6469902160506 Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Thu, 5 Dec 2024 13:54:47 -0800 Subject: [PATCH 8/9] remove commented code --- .../analyses/dataset_images_for_figures/figure_helper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py index 9c0e5ace..03a7736b 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/figure_helper.py @@ -455,7 +455,6 @@ def make_mp4(save_path, movie_name, files, fps=4): for im in files: if "png" in im: writer.append_data(imageio.imread(save_path / "movie" / im)) # add image to mp4 - # os.remove(im) # remove image from directory writer.close() From e56d3cf67f93b5c9b9366f41e36444e98b2b567c Mon Sep 17 00:00:00 2001 From: Julie Dixon Date: Fri, 6 Dec 2024 09:07:18 -0800 Subject: [PATCH 9/9] use sorted frames in mp4 creation --- .../movie_dataset_formation_breakdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py index 10da11bd..7bc77d63 100644 --- a/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py +++ b/nuc_morph_analysis/analyses/dataset_images_for_figures/movie_dataset_formation_breakdown.py @@ -174,4 +174,4 @@ # sorted in timepoint order then save to an mp4 movie frame_list = os.listdir(savedir / "movie") sorted_frames = sorted(frame_list, key=lambda x: int(x.split("_")[1].split(".")[0])) -figure_helper.make_mp4(savedir, "formation_breakdown_example_movie", frame_list) +figure_helper.make_mp4(savedir, "formation_breakdown_example_movie", sorted_frames)