microlive 1.0.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,297 @@
1
+ """Pipeline module for MicroLive.
2
+
3
+ This module is part of the microlive package.
4
+ """
5
+ from microlive.imports import *
6
+
7
+ def metadata_folding_efficiency(filename,computer_user_name,original_lif_name, SNR_SELECTION_FOR_CHANNEL_1, SNR_SELECTION_FOR_CHANNEL_0, MIN_LEN_TRAJECTORY, MEMORY,
8
+ SPOT_SIZE_PX, PLOT_FILTERED_IMAGES, MIN_INTENSITY_FOR_BACKGROUND, MIN_SPOTS_FOR_BACKGROUND,use_max_tem_projection_for_plotting,
9
+ max_spots_for_threshold, channels_cytosol, channels_nucleus, pixel_xy_um, voxel_z_um, channel_for_tracking, channel_folding,
10
+ CROP_SIZE_PX, max_crops_to_display, selected_time_point, list_quality_text,maximum_spots_cluster,ml_threshold,use_ml_for_spot_clasification):
11
+
12
+ metadata = {
13
+ "computer_user_name": computer_user_name,
14
+ "Date and Time": pd.Timestamp.now().round('min'),
15
+ "original_lif_name": original_lif_name,
16
+ "SNR_SELECTION_FOR_CHANNEL_1": SNR_SELECTION_FOR_CHANNEL_1,
17
+ "SNR_SELECTION_FOR_CHANNEL_0": SNR_SELECTION_FOR_CHANNEL_0,
18
+ "MIN_LEN_TRAJECTORY": MIN_LEN_TRAJECTORY,
19
+ "MEMORY": MEMORY,
20
+ "SPOT_SIZE_PX": SPOT_SIZE_PX,
21
+ "PLOT_FILTERED_IMAGES": PLOT_FILTERED_IMAGES,
22
+ "MIN_INTENSITY_FOR_BACKGROUND": MIN_INTENSITY_FOR_BACKGROUND,
23
+ "MIN_SPOTS_FOR_BACKGROUND": MIN_SPOTS_FOR_BACKGROUND,
24
+ "use_max_tem_projection_for_plotting": use_max_tem_projection_for_plotting,
25
+ "max_spots_for_threshold": max_spots_for_threshold,
26
+ "channels_cytosol": channels_cytosol,
27
+ "channels_nucleus": channels_nucleus,
28
+ "pixel_xy_nm": int(pixel_xy_um ),
29
+ "voxel_z_nm": int(voxel_z_um ),
30
+ "list_voxels": [int(voxel_z_um ), int(pixel_xy_um )],
31
+ "list_psfs": [int(voxel_z_um ), int(pixel_xy_um )],
32
+ "channel_for_tracking": channel_for_tracking,
33
+ "channel_folding": channel_folding,
34
+ "CROP_SIZE_PX": CROP_SIZE_PX,
35
+ "max_crops_to_display": max_crops_to_display,
36
+ "selected_time_point": selected_time_point,
37
+ "maximum_spots_cluster": maximum_spots_cluster,
38
+ "ml_threshold": ml_threshold,
39
+ "use_ml_for_spot_clasification": use_ml_for_spot_clasification
40
+ }
41
+
42
+ with open(filename, 'w') as file:
43
+ max_key_length = max(len(key) for key in metadata.keys())
44
+ for key, value in metadata.items():
45
+ file.write(f"{key.ljust(max_key_length)} : {value}\n")
46
+
47
+ file.write("\nProcessed Files:\n")
48
+ for index, name in enumerate(list_quality_text, start=1):
49
+ file.write(f"{index}. {name}\n")
50
+ return None
51
+
52
+
53
+ def pipeline_folding_efficiency(original_lif_name, list_images,list_images_names, data_folder_path, current_dir, list_psfs, list_voxels,
54
+ max_spots_for_threshold, MIN_INTENSITY_FOR_BACKGROUND,MIN_SPOTS_FOR_BACKGROUND, use_max_tem_projection_for_plotting,
55
+ channel_for_tracking,channel_folding, channel_names,crop_size,max_crops_to_display,results_folder_summary,SNR_SELECTION_FOR_CHANNEL_0,
56
+ SNR_SELECTION_FOR_CHANNEL_1,selected_time_point,voxel_z_nm,channels_cytosol,channels_nucleus, PLOT_FILTERED_IMAGES,SPOT_SIZE_PX,
57
+ MEMORY,MIN_LEN_TRAJECTORY,low_quality_pdf,maximum_spots_cluster,ml_threshold,use_ml_for_spot_clasification,pixel_xy_um):
58
+ list_images_quality = []
59
+ list_results_df =[]
60
+ date_lif = data_folder_path.stem[:8]
61
+ construct_lif = data_folder_path.stem[9:]
62
+ list_image_paths_for_pdf = []
63
+ path_summary_df = results_folder_summary.joinpath('results_quantification_'+original_lif_name+ '.csv')
64
+ path_summary_pdf= results_folder_summary.joinpath('results_quantification_'+original_lif_name+'.pdf')
65
+ path_summary_metadata = results_folder_summary.joinpath('metadata_'+original_lif_name+'.txt')
66
+ path_summary_wisker_plot = results_folder_summary.joinpath('results_efficiency_'+original_lif_name+ '.png')
67
+ path_summary_croparray = results_folder_summary.joinpath('croparray_'+original_lif_name+'.pdf')
68
+ list_quality_text = []
69
+ list_crop_array_paths = []
70
+ list_tracking_success = []
71
+ for selected_image, image_TZYXC in enumerate( list_images):
72
+ print('Processing image:', selected_image)
73
+ results_name = f'results_{data_folder_path.stem}_cell_id_{selected_image}'
74
+ results_folder = current_dir.joinpath('results_folding', results_name)
75
+ results_folder.mkdir(parents=True, exist_ok=True)
76
+ mi.Utilities().clear_folder_except_substring(results_folder, 'mask')
77
+ # Clean up existing files
78
+ results_df = results_folder.joinpath('results_df.csv')
79
+ path_efficiency_df = results_folder.joinpath('results_df_efficiency.csv')
80
+ path_quantification_image = results_folder.joinpath('results_image.png')
81
+ path_tracking_df = results_folder.joinpath('results_df_tracking.csv')
82
+ path_crop_array = results_folder.joinpath('crop_array.png')
83
+ list_crop_array_paths.append(path_crop_array)
84
+ for path in [path_efficiency_df, path_quantification_image, path_tracking_df, results_df, path_summary_df, path_summary_pdf, path_summary_metadata, path_summary_wisker_plot,path_summary_croparray]:
85
+ if path.exists():
86
+ path.unlink()
87
+ # Read the masks and calculate the threshold
88
+ mask_file_name = f'mask_{data_folder_path.stem}_image_{selected_image}.tif'
89
+ masks = imread(str(results_folder.joinpath(mask_file_name))).astype(bool)
90
+ threshold_tracking = mi.Utilities().calculate_threshold_for_spot_detection(image_TZYXC, list_psfs, list_voxels, [channel_for_tracking], max_spots_for_threshold, show_plot=True)
91
+ print('Threshold for tracking:', threshold_tracking)
92
+ # Plot histograms and check image quality
93
+ plot_name_histogram = results_folder.joinpath('pixel_histogram_in_cell.png')
94
+ masked_data = image_TZYXC * masks[np.newaxis, np.newaxis, :, :, np.newaxis].astype(float)
95
+ list_median_intensity = mi.Plots().plot_image_pixel_intensity_distribution(image=np.mean(masked_data, axis=0), figsize=(14, 3), bins=100, remove_outliers=True, remove_zeros=True, save_plots=True, plot_name=plot_name_histogram, single_color=None, list_colors=channel_names, tracking_channel=channel_for_tracking, threshold_tracking=threshold_tracking)
96
+ # Image quality assessment
97
+ text_image_quality = ' - [LOW QUALITY IMAGE]' if threshold_tracking < MIN_INTENSITY_FOR_BACKGROUND else ''
98
+ image_to_plot, suptitle_suffix = (np.max(image_TZYXC, axis=0), '- Maximum time projection') if use_max_tem_projection_for_plotting else (image_TZYXC[0], '')
99
+ suptitle = f'Image: {data_folder_path.stem[:16]}- {list_images_names[selected_image]} - Cell_ID: {selected_image} {text_image_quality} {suptitle_suffix}'
100
+ plot_name_original_image = results_folder.joinpath('original_image.png')
101
+ mi.Plots().plot_images(image_to_plot, df=None, masks=masks, figsize=(14, 3), suptitle=suptitle, show_plot=True, selected_time=0, use_maximum_projection=True, use_gaussian_filter=True, cmap='binary', min_max_percentile=[0.01, 99.2], show_gird=False, save_plots=True, plot_name=plot_name_original_image)
102
+ if threshold_tracking < MIN_INTENSITY_FOR_BACKGROUND:
103
+ path_image_quality = results_folder.joinpath('results_image_quality.png')
104
+ mi.Utilities().combine_images_vertically([plot_name_original_image, plot_name_histogram], path_image_quality, delete_originals=True)
105
+ list_image_paths_for_pdf.append(path_image_quality)
106
+ list_quality_text.append(list_images_names[selected_image] + ' Rejected : Low quality')
107
+ list_tracking_success.append(False)
108
+ else:
109
+ # particle tracking
110
+ try:
111
+ list_dataframes_trajectories, _ = mi.ParticleTracking (image=image_TZYXC,channels_spots= [channel_for_tracking], masks=masks, memory=MEMORY ,list_voxels=list_voxels,list_psfs=list_psfs, channels_cytosol=channels_cytosol,channels_nucleus=channels_nucleus,min_length_trajectory=MIN_LEN_TRAJECTORY,threshold_for_spot_detection=threshold_tracking,yx_spot_size_in_px=SPOT_SIZE_PX,maximum_spots_cluster=maximum_spots_cluster).run()
112
+ df_tracking= list_dataframes_trajectories[0]
113
+ except:
114
+ df_tracking = pd.DataFrame()
115
+ if df_tracking.empty:
116
+ list_quality_text.append(list_images_names[selected_image] + ' Rejected : No spots detected')
117
+ list_tracking_success.append(False)
118
+ else:
119
+ # remove low quality tracks. those that have a SNR less a threshold
120
+ field_for_quality = 'snr_ch_1' # 'snr_ch_1'
121
+ array_selected_field_ch1= mi.Utilities().df_trajectories_to_array(dataframe=df_tracking, selected_field=field_for_quality, fill_value='nans')
122
+ mean_selected_field_quality = np.nanmean(array_selected_field_ch1, axis=1)
123
+ indices_low_quality_tracks = np.where(mean_selected_field_quality < SNR_SELECTION_FOR_CHANNEL_1 )[0] # SNR_SELECTION_FOR_CHANNEL_1
124
+ # removing low quality tracks
125
+ df_tracking = df_tracking[~df_tracking['particle'].isin(indices_low_quality_tracks)]
126
+ df_tracking = df_tracking.reset_index(drop=True)
127
+ df_tracking['particle'] = df_tracking.groupby('particle').ngroup()
128
+ if df_tracking.empty:
129
+ list_quality_text.append(list_images_names[selected_image] + ' Rejected : No spots detected')
130
+ list_tracking_success.append(False)
131
+ continue
132
+ else:
133
+ list_images_quality.append(selected_image)
134
+ plot_name_original_image_spots = results_folder.joinpath('original_image_spots.png')
135
+ if use_max_tem_projection_for_plotting:
136
+ image_to_plot = np.max(image_TZYXC,axis=0)
137
+ max_time_projection_title = '- Maximum time projection'
138
+ selected_time = None
139
+ else:
140
+ image_to_plot = image_TZYXC[0]
141
+ max_time_projection_title = ''
142
+ selected_time = 0
143
+ suptitle = 'Image: ' + data_folder_path.stem[:16]+'- '+list_images_names[selected_image] +' - Cell_ID: '+ str(selected_image) + max_time_projection_title
144
+ mi.Plots().plot_images(image_ZYXC=image_to_plot, df=df_tracking, masks=masks,figsize=(14, 3), show_trajectories=True, suptitle=suptitle,show_plot=True,selected_time=selected_time, use_maximum_projection=True, use_gaussian_filter=True,cmap='binary',min_max_percentile=[0.01,99.2],show_gird=False,save_plots=True,plot_name=plot_name_original_image_spots)
145
+ # crops
146
+ selected_field = 'snr' # options are: psf_sigma, snr, 'spot_int'
147
+ plot_name_selected_field = results_folder.joinpath('spots_'+selected_field+'.png')
148
+ array_selected_field_ch0= mi.Utilities().df_trajectories_to_array(dataframe=df_tracking, selected_field=selected_field+'_ch_0', fill_value='nans')
149
+ array_selected_field_ch1= mi.Utilities().df_trajectories_to_array(dataframe=df_tracking, selected_field=selected_field+'_ch_1', fill_value='nans')
150
+ mi.Plots().plot_crops_properties(list_particles_arrays=[array_selected_field_ch0, array_selected_field_ch1],figsize=(15, 3),save_plots=True,plot_name=plot_name_selected_field,selection_threshold=SNR_SELECTION_FOR_CHANNEL_0, label =selected_field,list_colors=channel_names)
151
+ # plot snr histogram
152
+ plot_name_snr = results_folder.joinpath('histogram_snr.png')
153
+ mean_snr = mi.Plots().plot_histograms_from_df(df_tracking, selected_field=selected_field,figsize=(8,2), plot_name=plot_name_snr, bin_count=60, save_plot=True, list_colors= channel_names,remove_outliers=True)
154
+ # plot crops
155
+ normalize_each_particle = True
156
+ if PLOT_FILTERED_IMAGES:
157
+ filtered_image = mi.Utilities().gaussian_laplace_filter_image(image_TZYXC,list_psfs,list_voxels)
158
+ croparray_filtered, mean_crop_filtered, first_appearance, crop_size = mi.CropArray(image=filtered_image, df_crops=df_tracking, crop_size=crop_size, remove_outliers=False, max_percentile=99.9,selected_time_point=selected_time_point,normalize_each_particle=normalize_each_particle).run()
159
+ else:
160
+ croparray_filtered, mean_crop_filtered, first_appearance, crop_size = mi.CropArray(image=image_TZYXC, df_crops=df_tracking, crop_size=crop_size, remove_outliers=False, max_percentile=99.9,selected_time_point=selected_time_point,normalize_each_particle=normalize_each_particle).run()
161
+ # extracting crops from the croparray
162
+ number_particles = croparray_filtered.shape[1]//crop_size
163
+ number_time_points = croparray_filtered.shape[0]//crop_size
164
+
165
+ list_crops_selected_particle_all_time_points = []
166
+ for particle_id in range(number_particles):
167
+ list_crops_selected_particle = []
168
+ for time_point in range(number_time_points):
169
+ crop = croparray_filtered[time_point * crop_size: (time_point + 1) * crop_size, particle_id * crop_size: (particle_id + 1) * crop_size, :]
170
+ list_crops_selected_particle.append(crop)
171
+ list_crops_selected_particle_all_time_points.append(list_crops_selected_particle)
172
+ # detect spots in Channel 0
173
+ if use_ml_for_spot_clasification:
174
+ list_crops_nomalized = mi.Utilities().normalize_crop_return_list(array_crops_YXC=mean_crop_filtered,crop_size=crop_size,selected_color_channel=channel_folding,normalize_to_255=True)
175
+ flag_vector = ML.predict_crops(model_ML, list_crops_nomalized,threshold=ml_threshold)
176
+ #flag_vector= mi.Utilities().test_particle_presence_all_frames_with_ML(croparray=croparray_filtered,crop_size=crop_size,selected_color_channel=0,minimal_number_spots_in_time=4,ml_threshold=ml_threshold)
177
+ else:
178
+ number_crops = mean_crop_filtered.shape[0]//crop_size
179
+ flag_vector = np.zeros(number_crops, dtype=bool)
180
+ for crop_id in range(number_crops):
181
+ flag_vector[crop_id]= mi.Utilities().is_spot_in_crop(crop_id, crop_size=crop_size, selected_color_channel=channel_folding, array_crops_YXC=mean_crop_filtered,show_plot=False)
182
+ plot_name_crops_filter = results_folder.joinpath('crops.png')
183
+ mi.Plots().plot_matrix_pair_crops (mean_crop_filtered, crop_size,save_plots=True,plot_name=plot_name_crops_filter,flag_vector=flag_vector)
184
+ # Calculating folding efficiency and saving to dataframe
185
+ number_of_detected_particles_ch1 = array_selected_field_ch1.shape[0]
186
+ if number_of_detected_particles_ch1 < MIN_SPOTS_FOR_BACKGROUND:
187
+ list_quality_text.append(list_images_names[selected_image] + ' Rejected : less than ' + str(MIN_SPOTS_FOR_BACKGROUND)+ ' spots detected')
188
+ list_tracking_success.append(False)
189
+ else:
190
+ list_quality_text.append(list_images_names[selected_image] + ' Accepted')
191
+ list_tracking_success.append(True)
192
+ particles_above_threshold = np.sum(flag_vector)
193
+ efficiency = particles_above_threshold / number_of_detected_particles_ch1
194
+ df_folding_efficiency = pd.DataFrame({'Series': list_images_names[selected_image],'cell_index': np.array([selected_image]),
195
+ 'spots_ch1':number_of_detected_particles_ch1,
196
+ 'spots_ch0_above_ts':particles_above_threshold,
197
+ 'ts_int_ch1': threshold_tracking,
198
+ 'ts_snr': SNR_SELECTION_FOR_CHANNEL_0,
199
+ 'mean_snr_ch0':np.round(mean_snr[0],2),'mean_snr_ch1':np.round(mean_snr[1],2),
200
+ 'median_int_ch0': np.round(list_median_intensity[0],2), 'median_int_ch1': np.round(list_median_intensity[1],2),
201
+ 'efficiency':np.round(efficiency,4) })
202
+ df_folding_efficiency['date'] = date_lif
203
+ df_folding_efficiency['construct'] = construct_lif
204
+ # save df_tracking to csv to the results folder
205
+ list_results_df.append(df_folding_efficiency)
206
+ df_folding_efficiency.to_csv(path_efficiency_df, index=False)
207
+ df_tracking.to_csv(path_tracking_df, index=False)
208
+ # plotting the complete croparray
209
+ mi.Plots().plot_croparray(croparray_filtered, crop_size, save_plots=True,plot_name= path_crop_array,suptitle=None,show_particle_labels=True, cmap='binary_r',max_percentile = 99,flag_vector=flag_vector)
210
+ # save the results
211
+ mi.Utilities().combine_images_vertically([plot_name_crops_filter, plot_name_selected_field], results_folder.joinpath('results_quantification.png'), delete_originals=True)
212
+ mi.Utilities().combine_images_vertically([plot_name_original_image,plot_name_original_image_spots, plot_name_histogram,plot_name_snr], results_folder.joinpath('results_image_quality_processed.png'), delete_originals=True)
213
+ mi.Utilities().combine_images_vertically([results_folder.joinpath('results_image_quality_processed.png'), results_folder.joinpath('results_quantification.png')], path_quantification_image, delete_originals=True)
214
+ list_image_paths_for_pdf.append(path_quantification_image)
215
+ # concatenate the final dataframes with the results
216
+ df_quantification = pd.concat(list_results_df)
217
+ df_quantification = df_quantification.reset_index(drop=True)
218
+ df_quantification = df_quantification[df_quantification.columns[-2:].tolist() + df_quantification.columns[:-2].tolist()]
219
+ df_quantification.to_csv(path_summary_df, index=False)
220
+ # create wisker plot
221
+ fig, ax = plt.subplots(figsize=(8, 5))
222
+ df_quantification['location'] = 1
223
+ boxplot = df_quantification.boxplot(column='efficiency', by='location', ax=ax, grid=False, showfliers=False,
224
+ boxprops=dict(color="k", linewidth=2),
225
+ whiskerprops=dict(color="k", linewidth=2),
226
+ medianprops=dict(color="orangered", linewidth=2))
227
+ jitter = 0.02
228
+ df_quantification['jitter'] = np.random.uniform(-jitter, jitter, df_quantification.shape[0])
229
+ df_quantification['lif_name_jitter'] = df_quantification['location'] + df_quantification['jitter']
230
+ scatter = ax.scatter(df_quantification['lif_name_jitter'], df_quantification['efficiency'], color='red', marker='o', edgecolor='black', s=50, alpha=0.7)
231
+ # Customize plot aesthetics
232
+ plt.title('Efficiency of Folding')
233
+ plt.suptitle('') # Remove the default suptitle
234
+ plt.xticks([1], [original_lif_name], fontsize=14) # Set tick labels (enclose original_lif_name in a list if it's a single string)
235
+ plt.yticks(fontsize=14) # Set y-tick labels
236
+ plt.ylabel('Efficiency', fontsize=14)
237
+ plt.xlabel('Dataset', fontsize=14)
238
+ plt.ylim(0, 1)
239
+ plt.grid(axis='y', linestyle='--', alpha=0.7)
240
+ plt.savefig(path_summary_wisker_plot, dpi=300, bbox_inches='tight')
241
+ plt.show()
242
+ # Create PDF with images and quality text
243
+ pdf = FPDF()
244
+ pdf.set_auto_page_break(auto=True, margin=15)
245
+ pdf.set_font("Arial", size=12)
246
+ for i, image_path in enumerate(list_image_paths_for_pdf):
247
+ pdf.add_page()
248
+ pdf.set_xy(10, 10)
249
+ pdf.cell(0, 10, list_quality_text[i], 0, 1, 'L')
250
+ if low_quality_pdf:
251
+ img = Image.open(image_path)
252
+ base_width = 150 # Desired width in mm in the PDF
253
+ w_percent = (base_width / float(img.size[0]))
254
+ h_size = int((float(img.size[1]) * float(w_percent))) # Height in mm to maintain aspect ratio
255
+ # Temporarily save resized image for quality adjustment
256
+ temp_path = Path(image_path).with_name(Path(image_path).stem + '_temp').with_suffix('.jpg')
257
+ img.save(temp_path, 'JPEG', quality=85) # You can adjust quality to manage file size
258
+ pdf.image(str(temp_path), x=25, y=25, w=base_width, h=h_size) # Now specifying both width and height
259
+ temp_path.unlink() # Delete the temporary file
260
+ else:
261
+ # Directly embed the image at specified dimensions without resizing beforehand
262
+ img = Image.open(image_path)
263
+ w_percent = (150 / float(img.size[0]))
264
+ h_size = int((float(img.size[1]) * float(w_percent))) # Calculate height to maintain aspect ratio
265
+ pdf.image(str(image_path), x=25, y=25, w=150, h=h_size)
266
+ pdf.output(path_summary_pdf)
267
+
268
+ # save metadata
269
+ metadata_folding_efficiency(
270
+ path_summary_metadata,
271
+ computer_user_name=computer_user_name,
272
+ original_lif_name=original_lif_name,
273
+ SNR_SELECTION_FOR_CHANNEL_1=SNR_SELECTION_FOR_CHANNEL_1,
274
+ SNR_SELECTION_FOR_CHANNEL_0=SNR_SELECTION_FOR_CHANNEL_0,
275
+ MIN_LEN_TRAJECTORY=MIN_LEN_TRAJECTORY,
276
+ MEMORY=MEMORY,
277
+ SPOT_SIZE_PX=SPOT_SIZE_PX,
278
+ PLOT_FILTERED_IMAGES=PLOT_FILTERED_IMAGES,
279
+ MIN_INTENSITY_FOR_BACKGROUND=MIN_INTENSITY_FOR_BACKGROUND,
280
+ MIN_SPOTS_FOR_BACKGROUND=MIN_SPOTS_FOR_BACKGROUND,
281
+ use_max_tem_projection_for_plotting=use_max_tem_projection_for_plotting,
282
+ max_spots_for_threshold=max_spots_for_threshold,
283
+ channels_cytosol=channels_cytosol,
284
+ channels_nucleus=channels_nucleus,
285
+ pixel_xy_um=pixel_xy_um,
286
+ voxel_z_um=voxel_z_nm,
287
+ channel_for_tracking=channel_for_tracking,
288
+ channel_folding=channel_folding,
289
+ CROP_SIZE_PX=crop_size,
290
+ max_crops_to_display=max_crops_to_display,
291
+ selected_time_point=selected_time_point,
292
+ list_quality_text = list_quality_text,
293
+ maximum_spots_cluster = maximum_spots_cluster,
294
+ ml_threshold = ml_threshold,
295
+ use_ml_for_spot_clasification = use_ml_for_spot_clasification)
296
+
297
+ return df_quantification