spacr 0.2.3__py3-none-any.whl → 0.2.5__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.
Files changed (61) hide show
  1. spacr/app_annotate.py +3 -4
  2. spacr/core.py +100 -282
  3. spacr/gui.py +20 -38
  4. spacr/gui_core.py +406 -499
  5. spacr/gui_elements.py +395 -60
  6. spacr/gui_utils.py +393 -73
  7. spacr/io.py +130 -50
  8. spacr/measure.py +199 -154
  9. spacr/plot.py +108 -42
  10. spacr/resources/font/open_sans/OFL.txt +93 -0
  11. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  13. spacr/resources/font/open_sans/README.txt +100 -0
  14. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  49. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  50. spacr/resources/icons/logo.pdf +2786 -6
  51. spacr/resources/icons/logo_spacr.png +0 -0
  52. spacr/resources/icons/logo_spacr_1.png +0 -0
  53. spacr/settings.py +12 -87
  54. spacr/utils.py +45 -10
  55. {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/METADATA +5 -1
  56. spacr-0.2.5.dist-info/RECORD +100 -0
  57. spacr-0.2.3.dist-info/RECORD +0 -58
  58. {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/LICENSE +0 -0
  59. {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/WHEEL +0 -0
  60. {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/entry_points.txt +0 -0
  61. {spacr-0.2.3.dist-info → spacr-0.2.5.dist-info}/top_level.txt +0 -0
spacr/app_annotate.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import tkinter as tk
2
2
  from tkinter import ttk
3
3
  from .gui import MainApp
4
- from .gui_elements import set_dark_style
4
+ from .gui_elements import set_dark_style, spacrButton
5
5
 
6
6
  def initiate_annotation_app(parent_frame):
7
7
  from .gui_utils import generate_annotate_fields, annotate_app
@@ -20,8 +20,6 @@ def initiate_annotation_app(parent_frame):
20
20
  settings['img_size'] = list(map(int, settings['img_size'].split(','))) # Convert string to list of integers
21
21
  settings['percentiles'] = list(map(int, settings['percentiles'].split(','))) # Convert string to list of integers
22
22
  settings['normalize'] = settings['normalize'].lower() == 'true'
23
- settings['rows'] = int(settings['rows'])
24
- settings['columns'] = int(settings['columns'])
25
23
 
26
24
  try:
27
25
  settings['measurement'] = settings['measurement'].split(',') if settings['measurement'] else None
@@ -40,9 +38,10 @@ def initiate_annotation_app(parent_frame):
40
38
  settings[key] = None
41
39
 
42
40
  settings_window.destroy()
41
+
43
42
  annotate_app(parent_frame, settings)
44
43
 
45
- start_button = tk.Button(settings_window, text="Start Annotation", command=start_annotation_app, bg=style_out['bg_color'], fg=style_out['bg_color'])
44
+ start_button = spacrButton(settings_window, text="annotate", command=start_annotation_app, show_text=False)
46
45
  start_button.pack(pady=10)
47
46
 
48
47
  def start_annotate_app():
spacr/core.py CHANGED
@@ -39,6 +39,10 @@ matplotlib.use('Agg')
39
39
 
40
40
  from .logger import log_function_call
41
41
 
42
+ import warnings
43
+ warnings.filterwarnings("ignore", message="3D stack used, but stitch_threshold=0 and do_3D=False, so masks are made per plane only")
44
+
45
+
42
46
  def analyze_plaques(folder):
43
47
  summary_data = []
44
48
  details_data = []
@@ -80,7 +84,6 @@ def analyze_plaques(folder):
80
84
 
81
85
  print(f"Analysis completed and saved to database '{db_name}'.")
82
86
 
83
-
84
87
  def train_cellpose(settings):
85
88
 
86
89
  from .io import _load_normalized_images_and_labels, _load_images_and_labels
@@ -974,7 +977,7 @@ def generate_dataset(src, file_metadata=None, experiment='TSG101_screen', sample
974
977
  def apply_model_to_tar(tar_path, model_path, file_type='cell_png', image_size=224, batch_size=64, normalize=True, preload='images', n_jobs=10, threshold=0.5, verbose=False):
975
978
 
976
979
  from .io import TarImageDataset
977
- from .utils import process_vision_results
980
+ from .utils import process_vision_results, print_progress
978
981
 
979
982
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
980
983
  if normalize:
@@ -1015,15 +1018,22 @@ def apply_model_to_tar(tar_path, model_path, file_type='cell_png', image_size=22
1015
1018
 
1016
1019
  prediction_pos_probs = []
1017
1020
  filenames_list = []
1021
+ time_ls = []
1018
1022
  gc.collect()
1019
1023
  with torch.no_grad():
1020
1024
  for batch_idx, (batch_images, filenames) in enumerate(data_loader, start=1):
1025
+ start = time.time()
1021
1026
  images = batch_images.to(torch.float).to(device)
1022
1027
  outputs = model(images)
1023
1028
  batch_prediction_pos_prob = torch.sigmoid(outputs).cpu().numpy()
1024
1029
  prediction_pos_probs.extend(batch_prediction_pos_prob.tolist())
1025
1030
  filenames_list.extend(filenames)
1026
- print(f'batch: {batch_idx}/{len(data_loader)}', end='\r', flush=True)
1031
+ stop = time.time()
1032
+ duration = stop - start
1033
+ time_ls.append(duration)
1034
+ files_processed = batch_idx*batch_size
1035
+ files_to_process = len(data_loader)
1036
+ print_progress(files_processed, files_to_process, n_jobs=n_jobs, time_ls=time_ls, batch_size=batch_size, operation_type="Tar dataset")
1027
1037
 
1028
1038
  data = {'path':filenames_list, 'pred':prediction_pos_probs}
1029
1039
  df = pd.DataFrame(data, index=None)
@@ -1037,6 +1047,7 @@ def apply_model_to_tar(tar_path, model_path, file_type='cell_png', image_size=22
1037
1047
  def apply_model(src, model_path, image_size=224, batch_size=64, normalize=True, n_jobs=10):
1038
1048
 
1039
1049
  from .io import NoClassDataset
1050
+ from .utils import print_progress
1040
1051
 
1041
1052
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
1042
1053
 
@@ -1065,14 +1076,22 @@ def apply_model(src, model_path, image_size=224, batch_size=64, normalize=True,
1065
1076
  model = model.to(device)
1066
1077
  prediction_pos_probs = []
1067
1078
  filenames_list = []
1079
+ time_ls = []
1068
1080
  with torch.no_grad():
1069
1081
  for batch_idx, (batch_images, filenames) in enumerate(data_loader, start=1):
1082
+ start = time.time()
1070
1083
  images = batch_images.to(torch.float).to(device)
1071
1084
  outputs = model(images)
1072
1085
  batch_prediction_pos_prob = torch.sigmoid(outputs).cpu().numpy()
1073
1086
  prediction_pos_probs.extend(batch_prediction_pos_prob.tolist())
1074
1087
  filenames_list.extend(filenames)
1075
- print(f'\rbatch: {batch_idx}/{len(data_loader)}', end='\r', flush=True)
1088
+ stop = time.time()
1089
+ duration = stop - start
1090
+ time_ls.append(duration)
1091
+ files_processed = batch_idx*batch_size
1092
+ files_to_process = len(data_loader)
1093
+ print_progress(files_processed, files_to_process, n_jobs=n_jobs, time_ls=time_ls, batch_size=batch_size, operation_type="Generating predictions")
1094
+
1076
1095
  data = {'path':filenames_list, 'pred':prediction_pos_probs}
1077
1096
  df = pd.DataFrame(data, index=None)
1078
1097
  df.to_csv(result_loc, index=True, header=True, mode='w')
@@ -1164,12 +1183,14 @@ def training_dataset_from_annotation(db_path, dst, annotation_column='test', ann
1164
1183
  return class_paths
1165
1184
 
1166
1185
  def generate_dataset_from_lists(dst, class_data, classes, test_split=0.1):
1186
+ from .utils import print_progress
1167
1187
  # Make sure that the length of class_data matches the length of classes
1168
1188
  if len(class_data) != len(classes):
1169
1189
  raise ValueError("class_data and classes must have the same length.")
1170
1190
 
1171
1191
  total_files = sum(len(data) for data in class_data)
1172
1192
  processed_files = 0
1193
+ time_ls = []
1173
1194
 
1174
1195
  for cls, data in zip(classes, class_data):
1175
1196
  # Create directories
@@ -1183,15 +1204,21 @@ def generate_dataset_from_lists(dst, class_data, classes, test_split=0.1):
1183
1204
 
1184
1205
  # Copy train files
1185
1206
  for path in train_data:
1207
+ start = time.time()
1186
1208
  shutil.copy(path, os.path.join(train_class_dir, os.path.basename(path)))
1187
1209
  processed_files += 1
1188
- print(f'{processed_files}/{total_files}', end='\r', flush=True)
1210
+ duration = time.time() - start
1211
+ time_ls.append(duration)
1212
+ print_progress(processed_files, total_files, n_jobs=1, time_ls=None, batch_size=None, operation_type="Copying files for Train dataset")
1189
1213
 
1190
1214
  # Copy test files
1191
1215
  for path in test_data:
1216
+ start = time.time()
1192
1217
  shutil.copy(path, os.path.join(test_class_dir, os.path.basename(path)))
1193
1218
  processed_files += 1
1194
- print(f'{processed_files}/{total_files}', end='\r', flush=True)
1219
+ duration = time.time() - start
1220
+ time_ls.append(duration)
1221
+ print_progress(processed_files, total_files, n_jobs=1, time_ls=None, batch_size=None, operation_type="Copying files for Test dataset")
1195
1222
 
1196
1223
  # Print summary
1197
1224
  for cls in classes:
@@ -1667,9 +1694,9 @@ def analyze_recruitment(src, metadata_settings={}, advanced_settings={}):
1667
1694
  def preprocess_generate_masks(src, settings={}):
1668
1695
 
1669
1696
  from .io import preprocess_img_data, _load_and_concatenate_arrays
1670
- from .plot import plot_merged, plot_arrays
1671
- from .utils import _pivot_counts_table, check_mask_folder, adjust_cell_masks
1672
- from .settings import set_default_settings_preprocess_generate_masks, set_default_plot_merge_settings
1697
+ from .plot import plot_image_mask_overlay, plot_arrays
1698
+ from .utils import _pivot_counts_table, check_mask_folder, adjust_cell_masks, print_progress
1699
+ from .settings import set_default_settings_preprocess_generate_masks
1673
1700
 
1674
1701
  settings = set_default_settings_preprocess_generate_masks(src, settings)
1675
1702
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
@@ -1702,20 +1729,43 @@ def preprocess_generate_masks(src, settings={}):
1702
1729
 
1703
1730
  if settings['preprocess']:
1704
1731
  settings, src = preprocess_img_data(settings)
1705
-
1732
+
1733
+ files_to_process = 3
1706
1734
  if settings['masks']:
1707
1735
  mask_src = os.path.join(src, 'norm_channel_stack')
1708
1736
  if settings['cell_channel'] != None:
1737
+ start = time.time()
1709
1738
  if check_mask_folder(src, 'cell_mask_stack'):
1710
1739
  generate_cellpose_masks(mask_src, settings, 'cell')
1740
+ stop = time.time()
1741
+ duration = (stop - start)
1742
+ time_ls.append(duration)
1743
+ files_processed += 1
1744
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type=f'cell_mask_gen')
1711
1745
 
1712
1746
  if settings['nucleus_channel'] != None:
1747
+ start = time.time()
1713
1748
  if check_mask_folder(src, 'nucleus_mask_stack'):
1714
1749
  generate_cellpose_masks(mask_src, settings, 'nucleus')
1750
+ stop = time.time()
1751
+ duration = (stop - start)
1752
+ time_ls.append(duration)
1753
+ files_processed += 1
1754
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type=f'nucleus_mask_gen')
1715
1755
 
1716
1756
  if settings['pathogen_channel'] != None:
1757
+ start = time.time()
1717
1758
  if check_mask_folder(src, 'pathogen_mask_stack'):
1759
+ stop = time.time()
1760
+ duration = (stop - start)
1761
+ time_ls.append(duration)
1762
+ files_processed += 1
1718
1763
  generate_cellpose_masks(mask_src, settings, 'pathogen')
1764
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type=f'pathogen_mask_gen')
1765
+
1766
+ #if settings['organelle'] != None:
1767
+ # if check_mask_folder(src, 'organelle_mask_stack'):
1768
+ # generate_cellpose_masks(mask_src, settings, 'organelle')
1719
1769
 
1720
1770
  if settings['adjust_cells']:
1721
1771
  if settings['pathogen_channel'] != None and settings['cell_channel'] != None and settings['nucleus_channel'] != None:
@@ -1724,12 +1774,8 @@ def preprocess_generate_masks(src, settings={}):
1724
1774
  cell_folder = os.path.join(mask_src, 'cell_mask_stack')
1725
1775
  nuclei_folder = os.path.join(mask_src, 'nucleus_mask_stack')
1726
1776
  parasite_folder = os.path.join(mask_src, 'pathogen_mask_stack')
1727
- #image_folder = os.path.join(src, 'stack')
1777
+ #organelle_folder = os.path.join(mask_src, 'organelle_mask_stack')
1728
1778
 
1729
- #process_masks(cell_folder, image_folder, settings['cell_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1730
- #process_masks(nuclei_folder, image_folder, settings['nucleus_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1731
- #process_masks(parasite_folder, image_folder, settings['pathogen_channel'], settings['batch_size'], n_clusters=2, plot=settings['plot'])
1732
-
1733
1779
  adjust_cell_masks(parasite_folder, cell_folder, nuclei_folder, overlap_threshold=5, perimeter_threshold=30)
1734
1780
  stop = time.time()
1735
1781
  adjust_time = (stop-start)/60
@@ -1743,38 +1789,28 @@ def preprocess_generate_masks(src, settings={}):
1743
1789
 
1744
1790
  if settings['plot']:
1745
1791
  if not settings['timelapse']:
1746
- plot_dims = len(settings['channels'])
1747
- overlay_channels = [2,1,0]
1748
- cell_mask_dim = nucleus_mask_dim = pathogen_mask_dim = None
1749
- plot_counter = plot_dims
1750
-
1751
- if settings['cell_channel'] is not None:
1752
- cell_mask_dim = plot_counter
1753
- plot_counter += 1
1754
-
1755
- if settings['nucleus_channel'] is not None:
1756
- nucleus_mask_dim = plot_counter
1757
- plot_counter += 1
1758
-
1759
- if settings['pathogen_channel'] is not None:
1760
- pathogen_mask_dim = plot_counter
1761
-
1762
- overlay_channels = [settings['nucleus_channel'], settings['pathogen_channel'], settings['cell_channel']]
1763
- overlay_channels = [element for element in overlay_channels if element is not None]
1764
-
1765
- plot_settings = set_default_plot_merge_settings()
1766
- plot_settings['channel_dims'] = settings['channels']
1767
- plot_settings['cell_mask_dim'] = cell_mask_dim
1768
- plot_settings['nucleus_mask_dim'] = nucleus_mask_dim
1769
- plot_settings['pathogen_mask_dim'] = pathogen_mask_dim
1770
- plot_settings['overlay_chans'] = overlay_channels
1771
- plot_settings['nr'] = settings['examples_to_plot']
1772
1792
 
1773
1793
  if settings['test_mode'] == True:
1774
- plot_settings['nr'] = len(os.path.join(src,'merged'))
1794
+ settings['examples_to_plot'] = len(os.path.join(src,'merged'))
1775
1795
 
1776
1796
  try:
1777
- fig = plot_merged(src=os.path.join(src,'merged'), settings=plot_settings)
1797
+ merged_src = os.path.join(src,'merged')
1798
+ files = os.listdir(merged_src)
1799
+ random.shuffle(files)
1800
+ time_ls = []
1801
+
1802
+ for i, file in enumerate(files):
1803
+ start = time.time()
1804
+ if i+1 <= settings['examples_to_plot']:
1805
+ file_path = os.path.join(merged_src, file)
1806
+ plot_image_mask_overlay(file_path, settings['channels'], settings['cell_channel'], settings['nucleus_channel'], settings['pathogen_channel'], figuresize=10, normalize=True, thickness=3, save_pdf=True)
1807
+ stop = time.time()
1808
+ duration = stop-start
1809
+ time_ls.append(duration)
1810
+ files_processed = i+1
1811
+ files_to_process = settings['examples_to_plot']
1812
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Plot mask outlines")
1813
+ print("Successfully completed run")
1778
1814
  except Exception as e:
1779
1815
  print(f'Failed to plot image mask overly. Error: {e}')
1780
1816
  else:
@@ -1788,16 +1824,13 @@ def preprocess_generate_masks(src, settings={}):
1788
1824
  def identify_masks_finetune(settings):
1789
1825
 
1790
1826
  from .plot import print_mask_and_flows
1791
- from .utils import get_files_from_dir, resize_images_and_labels
1827
+ from .utils import get_files_from_dir, resize_images_and_labels, print_progress
1792
1828
  from .io import _load_normalized_images_and_labels, _load_images_and_labels
1793
1829
  from .settings import get_identify_masks_finetune_default_settings
1794
1830
 
1795
1831
  settings = get_identify_masks_finetune_default_settings(settings)
1796
-
1797
- #User defined settings
1798
1832
  src=settings['src']
1799
1833
  dst=settings['dst']
1800
-
1801
1834
  model_name=settings['model_name']
1802
1835
  custom_model=settings['custom_model']
1803
1836
  channels = settings['channels']
@@ -1903,8 +1936,12 @@ def identify_masks_finetune(settings):
1903
1936
  stop = time.time()
1904
1937
  duration = (stop - start)
1905
1938
  time_ls.append(duration)
1906
- average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
1907
- print(f'Processing {file_index+1}/{len(images)} images : Time/image {average_time:.3f} sec', end='\r', flush=True)
1939
+ files_processed = len(images)
1940
+ files_to_process = file_index+1
1941
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls)
1942
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="")
1943
+
1944
+
1908
1945
  if verbose:
1909
1946
  if resize:
1910
1947
  stack = resizescikit(stack, dims, preserve_range=True, anti_aliasing=False).astype(stack.dtype)
@@ -1917,212 +1954,6 @@ def identify_masks_finetune(settings):
1917
1954
  gc.collect()
1918
1955
  return
1919
1956
 
1920
- def identify_masks(src, object_type, model_name, batch_size, channels, diameter, minimum_size, maximum_size, filter_intensity, flow_threshold=30, cellprob_threshold=1, figuresize=25, cmap='inferno', refine_masks=True, filter_size=True, filter_dimm=True, remove_border_objects=False, verbose=False, plot=False, merge=False, save=True, start_at=0, file_type='.npz', net_avg=True, resample=True, timelapse=False, timelapse_displacement=None, timelapse_frame_limits=None, timelapse_memory=3, timelapse_remove_transient=False, timelapse_mode='btrack', timelapse_objects='cell'):
1921
- """
1922
- Identify masks from the source images.
1923
-
1924
- Args:
1925
- src (str): Path to the source images.
1926
- object_type (str): Type of object to identify.
1927
- model_name (str): Name of the model to use for identification.
1928
- batch_size (int): Number of images to process in each batch.
1929
- channels (list): List of channel names.
1930
- diameter (float): Diameter of the objects to identify.
1931
- minimum_size (int): Minimum size of objects to keep.
1932
- maximum_size (int): Maximum size of objects to keep.
1933
- flow_threshold (int, optional): Threshold for flow detection. Defaults to 30.
1934
- cellprob_threshold (int, optional): Threshold for cell probability. Defaults to 1.
1935
- figuresize (int, optional): Size of the figure. Defaults to 25.
1936
- cmap (str, optional): Colormap for plotting. Defaults to 'inferno'.
1937
- refine_masks (bool, optional): Flag indicating whether to refine masks. Defaults to True.
1938
- filter_size (bool, optional): Flag indicating whether to filter based on size. Defaults to True.
1939
- filter_dimm (bool, optional): Flag indicating whether to filter based on intensity. Defaults to True.
1940
- remove_border_objects (bool, optional): Flag indicating whether to remove border objects. Defaults to False.
1941
- verbose (bool, optional): Flag indicating whether to display verbose output. Defaults to False.
1942
- plot (bool, optional): Flag indicating whether to plot the masks. Defaults to False.
1943
- merge (bool, optional): Flag indicating whether to merge adjacent objects. Defaults to False.
1944
- save (bool, optional): Flag indicating whether to save the masks. Defaults to True.
1945
- start_at (int, optional): Index to start processing from. Defaults to 0.
1946
- file_type (str, optional): File type for saving the masks. Defaults to '.npz'.
1947
- net_avg (bool, optional): Flag indicating whether to use network averaging. Defaults to True.
1948
- resample (bool, optional): Flag indicating whether to resample the images. Defaults to True.
1949
- timelapse (bool, optional): Flag indicating whether to generate a timelapse. Defaults to False.
1950
- timelapse_displacement (float, optional): Displacement threshold for timelapse. Defaults to None.
1951
- timelapse_frame_limits (tuple, optional): Frame limits for timelapse. Defaults to None.
1952
- timelapse_memory (int, optional): Memory for timelapse. Defaults to 3.
1953
- timelapse_remove_transient (bool, optional): Flag indicating whether to remove transient objects in timelapse. Defaults to False.
1954
- timelapse_mode (str, optional): Mode for timelapse. Defaults to 'btrack'.
1955
- timelapse_objects (str, optional): Objects to track in timelapse. Defaults to 'cell'.
1956
-
1957
- Returns:
1958
- None
1959
- """
1960
-
1961
- from .utils import _masks_to_masks_stack, _filter_cp_masks, _get_cellpose_batch_size
1962
- from .io import _create_database, _save_object_counts_to_database, _check_masks, _get_avg_object_size
1963
- from .timelapse import _npz_to_movie, _btrack_track_cells, _trackpy_track_cells
1964
- from .plot import plot_masks
1965
-
1966
- #Note add logic that handles batches of size 1 as these will break the code batches must all be > 2 images
1967
- gc.collect()
1968
-
1969
- if not torch.cuda.is_available():
1970
- print(f'Torch CUDA is not available, using CPU')
1971
-
1972
- device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
1973
- model = cp_models.Cellpose(gpu=True, model_type=model_name, device=device)
1974
-
1975
- if file_type == '.npz':
1976
- paths = [os.path.join(src, file) for file in os.listdir(src) if file.endswith('.npz')]
1977
- else:
1978
- paths = [os.path.join(src, file) for file in os.listdir(src) if file.endswith('.png')]
1979
- if timelapse:
1980
- print(f'timelaps is only compatible with npz files')
1981
- return
1982
-
1983
- chans = [2, 1] if model_name == 'cyto2' else [0,0] if model_name == 'nucleus' else [2,0] if model_name == 'cyto' else [2, 0]
1984
-
1985
- if verbose == True:
1986
- print(f'source: {src}')
1987
- print()
1988
- print(f'Settings: object_type: {object_type}, minimum_size: {minimum_size}, maximum_size:{maximum_size}, figuresize:{figuresize}, cmap:{cmap}, , net_avg:{net_avg}, resample:{resample}')
1989
- print()
1990
- print(f'Cellpose settings: Model: {model_name}, batch_size: {batch_size}, channels: {channels}, cellpose_chans: {chans}, diameter:{diameter}, flow_threshold:{flow_threshold}, cellprob_threshold:{cellprob_threshold}')
1991
- print()
1992
- print(f'Bool Settings: verbose:{verbose}, plot:{plot}, merge:{merge}, save:{save}, start_at:{start_at}, file_type:{file_type}, timelapse:{timelapse}')
1993
- print()
1994
-
1995
- count_loc = os.path.dirname(src)+'/measurements/measurements.db'
1996
- os.makedirs(os.path.dirname(src)+'/measurements', exist_ok=True)
1997
- _create_database(count_loc)
1998
-
1999
- average_sizes = []
2000
- time_ls = []
2001
- for file_index, path in enumerate(paths):
2002
-
2003
- name = os.path.basename(path)
2004
- name, ext = os.path.splitext(name)
2005
- if file_type == '.npz':
2006
- if start_at:
2007
- print(f'starting at file index:{start_at}')
2008
- if file_index < start_at:
2009
- continue
2010
- output_folder = os.path.join(os.path.dirname(path), object_type+'_mask_stack')
2011
- os.makedirs(output_folder, exist_ok=True)
2012
- overall_average_size = 0
2013
- with np.load(path) as data:
2014
- stack = data['data']
2015
- filenames = data['filenames']
2016
- if timelapse:
2017
- if len(stack) != batch_size:
2018
- print(f'Changed batch_size:{batch_size} to {len(stack)}, data length:{len(stack)}')
2019
- batch_size = len(stack)
2020
- if isinstance(timelapse_frame_limits, list):
2021
- if len(timelapse_frame_limits) >= 2:
2022
- stack = stack[timelapse_frame_limits[0]: timelapse_frame_limits[1], :, :, :].astype(stack.dtype)
2023
- filenames = filenames[timelapse_frame_limits[0]: timelapse_frame_limits[1]]
2024
- batch_size = len(stack)
2025
- print(f'Cut batch an indecies: {timelapse_frame_limits}, New batch_size: {batch_size} ')
2026
-
2027
- for i in range(0, stack.shape[0], batch_size):
2028
- mask_stack = []
2029
- start = time.time()
2030
-
2031
- if stack.shape[3] == 1:
2032
- batch = stack[i: i+batch_size, :, :, [0,0]].astype(stack.dtype)
2033
- else:
2034
- batch = stack[i: i+batch_size, :, :, channels].astype(stack.dtype)
2035
-
2036
- batch_filenames = filenames[i: i+batch_size].tolist()
2037
-
2038
- if not plot:
2039
- batch, batch_filenames = _check_masks(batch, batch_filenames, output_folder)
2040
- if batch.size == 0:
2041
- print(f'Processing: {file_index}/{len(paths)}: Images/N100pz {batch.shape[0]}')
2042
- #print(f'Processing {file_index}/{len(paths)}: Images/N100pz {batch.shape[0]}', end='\r', flush=True)
2043
- continue
2044
- if batch.max() > 1:
2045
- batch = batch / batch.max()
2046
-
2047
- if timelapse:
2048
- stitch_threshold=100.0
2049
- movie_path = os.path.join(os.path.dirname(src), 'movies')
2050
- os.makedirs(movie_path, exist_ok=True)
2051
- save_path = os.path.join(movie_path, f'timelapse_{object_type}_{name}.mp4')
2052
- _npz_to_movie(batch, batch_filenames, save_path, fps=2)
2053
- else:
2054
- stitch_threshold=0.0
2055
-
2056
- cellpose_batch_size = _get_cellpose_batch_size()
2057
-
2058
- masks, flows, _, _ = model.eval(x=batch,
2059
- batch_size=cellpose_batch_size,
2060
- normalize=False,
2061
- channels=chans,
2062
- channel_axis=3,
2063
- diameter=diameter,
2064
- flow_threshold=flow_threshold,
2065
- cellprob_threshold=cellprob_threshold,
2066
- rescale=None,
2067
- resample=resample,
2068
- stitch_threshold=stitch_threshold,
2069
- progress=None)
2070
-
2071
- print('Masks shape',masks.shape)
2072
- if timelapse:
2073
- _save_object_counts_to_database(masks, object_type, batch_filenames, count_loc, added_string='_timelapse')
2074
- if object_type in timelapse_objects:
2075
- if timelapse_mode == 'btrack':
2076
- if not timelapse_displacement is None:
2077
- radius = timelapse_displacement
2078
- else:
2079
- radius = 100
2080
-
2081
- n_jobs = os.cpu_count()-2
2082
- if n_jobs < 1:
2083
- n_jobs = 1
2084
-
2085
- mask_stack = _btrack_track_cells(src, name, batch_filenames, object_type, plot, save, masks_3D=masks, mode=timelapse_mode, timelapse_remove_transient=timelapse_remove_transient, radius=radius, n_jobs=n_jobs)
2086
- if timelapse_mode == 'trackpy':
2087
- mask_stack = _trackpy_track_cells(src, name, batch_filenames, object_type, masks, timelapse_displacement, timelapse_memory, timelapse_remove_transient, plot, save, timelapse_mode)
2088
-
2089
- else:
2090
- mask_stack = _masks_to_masks_stack(masks)
2091
-
2092
- else:
2093
- _save_object_counts_to_database(masks, object_type, batch_filenames, count_loc, added_string='_before_filtration')
2094
- mask_stack = _filter_cp_masks(masks, flows, filter_size, filter_intensity, minimum_size, maximum_size, remove_border_objects, merge, batch, plot, figuresize)
2095
- _save_object_counts_to_database(mask_stack, object_type, batch_filenames, count_loc, added_string='_after_filtration')
2096
-
2097
- if not np.any(mask_stack):
2098
- average_obj_size = 0
2099
- else:
2100
- average_obj_size = _get_avg_object_size(mask_stack)
2101
-
2102
- average_sizes.append(average_obj_size)
2103
- overall_average_size = np.mean(average_sizes) if len(average_sizes) > 0 else 0
2104
-
2105
- stop = time.time()
2106
- duration = (stop - start)
2107
- time_ls.append(duration)
2108
- average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
2109
- time_in_min = average_time/60
2110
- time_per_mask = average_time/batch_size
2111
- print(f'Processing: {len(paths)} files with {batch_size} imgs: {(file_index+1)*(batch_size+1)}/{(len(paths))*(batch_size+1)}: Time/batch {time_in_min:.3f} min: Time/mask {time_per_mask:.3f}sec: {object_type} size: {overall_average_size:.3f} px2')
2112
- #print(f'Processing {len(paths)} files with {batch_size} imgs: {(file_index+1)*(batch_size+1)}/{(len(paths))*(batch_size+1)}: Time/batch {time_in_min:.3f} min: Time/mask {time_per_mask:.3f}sec: {object_type} size: {overall_average_size:.3f} px2', end='\r', flush=True)
2113
- if not timelapse:
2114
- if plot:
2115
- plot_masks(batch, mask_stack, flows, figuresize=figuresize, cmap=cmap, nr=batch_size, file_type='.npz')
2116
- if save:
2117
- if file_type == '.npz':
2118
- for mask_index, mask in enumerate(mask_stack):
2119
- output_filename = os.path.join(output_folder, batch_filenames[mask_index])
2120
- np.save(output_filename, mask)
2121
- mask_stack = []
2122
- batch_filenames = []
2123
- gc.collect()
2124
- return
2125
-
2126
1957
  def all_elements_match(list1, list2):
2127
1958
  # Check if all elements in list1 are in list2
2128
1959
  return all(element in list2 for element in list1)
@@ -2141,7 +1972,7 @@ def prepare_batch_for_cellpose(batch):
2141
1972
 
2142
1973
  def generate_cellpose_masks(src, settings, object_type):
2143
1974
 
2144
- from .utils import _masks_to_masks_stack, _filter_cp_masks, _get_cellpose_batch_size, _get_cellpose_channels, _choose_model, mask_object_count
1975
+ from .utils import _masks_to_masks_stack, _filter_cp_masks, _get_cellpose_batch_size, _get_cellpose_channels, _choose_model, mask_object_count, print_progress
2145
1976
  from .io import _create_database, _save_object_counts_to_database, _check_masks, _get_avg_object_size
2146
1977
  from .timelapse import _npz_to_movie, _btrack_track_cells, _trackpy_track_cells
2147
1978
  from .plot import plot_masks
@@ -2187,7 +2018,7 @@ def generate_cellpose_masks(src, settings, object_type):
2187
2018
 
2188
2019
  if object_type == 'pathogen' and not settings['pathogen_model'] is None:
2189
2020
  model_name = settings['pathogen_model']
2190
-
2021
+
2191
2022
  model = _choose_model(model_name, device, object_type=object_type, restore_type=None, object_settings=object_settings)
2192
2023
 
2193
2024
  chans = [2, 1] if model_name == 'cyto2' else [0,0] if model_name == 'nucleus' else [2,0] if model_name == 'cyto' else [2, 0] if model_name == 'cyto3' else [2, 0]
@@ -2199,16 +2030,18 @@ def generate_cellpose_masks(src, settings, object_type):
2199
2030
 
2200
2031
  average_sizes = []
2201
2032
  time_ls = []
2033
+
2202
2034
  for file_index, path in enumerate(paths):
2203
2035
  name = os.path.basename(path)
2204
2036
  name, ext = os.path.splitext(name)
2205
2037
  output_folder = os.path.join(os.path.dirname(path), object_type+'_mask_stack')
2206
2038
  os.makedirs(output_folder, exist_ok=True)
2207
2039
  overall_average_size = 0
2040
+
2208
2041
  with np.load(path) as data:
2209
2042
  stack = data['data']
2210
2043
  filenames = data['filenames']
2211
-
2044
+
2212
2045
  for i, filename in enumerate(filenames):
2213
2046
  output_path = os.path.join(output_folder, filename)
2214
2047
 
@@ -2233,11 +2066,9 @@ def generate_cellpose_masks(src, settings, object_type):
2233
2066
  filenames = filenames[timelapse_frame_limits[0]: timelapse_frame_limits[1]]
2234
2067
  batch_size = len(stack)
2235
2068
  print(f'Cut batch at indecies: {timelapse_frame_limits}, New batch_size: {batch_size} ')
2236
-
2069
+
2237
2070
  for i in range(0, stack.shape[0], batch_size):
2238
2071
  mask_stack = []
2239
- start = time.time()
2240
-
2241
2072
  if stack.shape[3] == 1:
2242
2073
  batch = stack[i: i+batch_size, :, :, [0,0]].astype(stack.dtype)
2243
2074
  else:
@@ -2248,7 +2079,6 @@ def generate_cellpose_masks(src, settings, object_type):
2248
2079
  if not settings['plot']:
2249
2080
  batch, batch_filenames = _check_masks(batch, batch_filenames, output_folder)
2250
2081
  if batch.size == 0:
2251
- print(f'Processing {file_index}/{len(paths)}: Images/npz {batch.shape[0]}')
2252
2082
  continue
2253
2083
 
2254
2084
  batch = prepare_batch_for_cellpose(batch)
@@ -2259,16 +2089,6 @@ def generate_cellpose_masks(src, settings, object_type):
2259
2089
  save_path = os.path.join(movie_path, f'timelapse_{object_type}_{name}.mp4')
2260
2090
  _npz_to_movie(batch, batch_filenames, save_path, fps=2)
2261
2091
 
2262
- if settings['verbose']:
2263
- print(f'Processing {file_index}/{len(paths)}: Images/npz {batch.shape[0]}')
2264
-
2265
- #cellpose_normalize_dict = {'lowhigh':[0.0,1.0], #pass in normalization values for 0.0 and 1.0 as list [low, high] if None all other keys ignored
2266
- # 'sharpen':object_settings['diameter']/4, #recommended to be 1/4-1/8 diameter of cells in pixels
2267
- # 'normalize':True, #(if False, all following parameters ignored)
2268
- # 'percentile':[2,98], #[perc_low, perc_high]
2269
- # 'tile_norm':224, #normalize by tile set to e.g. 100 for normailize window to be 100 px
2270
- # 'norm3D':True} #compute normalization across entire z-stack rather than plane-by-plane in stitching mode.
2271
-
2272
2092
  output = model.eval(x=batch,
2273
2093
  batch_size=cellpose_batch_size,
2274
2094
  normalize=False,
@@ -2378,14 +2198,8 @@ def generate_cellpose_masks(src, settings, object_type):
2378
2198
 
2379
2199
  average_sizes.append(average_obj_size)
2380
2200
  overall_average_size = np.mean(average_sizes) if len(average_sizes) > 0 else 0
2201
+ print(f'object_size:{object_type}: {overall_average_size:.3f} px2')
2381
2202
 
2382
- stop = time.time()
2383
- duration = (stop - start)
2384
- time_ls.append(duration)
2385
- average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
2386
- time_in_min = average_time/60
2387
- time_per_mask = average_time/batch_size
2388
- print(f'Processing {len(paths)} files with {batch_size} imgs: {(file_index+1)*(batch_size+1)}/{(len(paths))*(batch_size+1)}: Time/batch {time_in_min:.3f} min: Time/mask {time_per_mask:.3f}sec: {object_type} size: {overall_average_size:.3f} px2')
2389
2203
  if not timelapse:
2390
2204
  if settings['plot']:
2391
2205
  plot_masks(batch, mask_stack, flows, figuresize=figuresize, cmap='inferno', nr=batch_size)
@@ -2396,6 +2210,7 @@ def generate_cellpose_masks(src, settings, object_type):
2396
2210
  np.save(output_filename, mask)
2397
2211
  mask_stack = []
2398
2212
  batch_filenames = []
2213
+
2399
2214
  gc.collect()
2400
2215
  torch.cuda.empty_cache()
2401
2216
  return
@@ -2403,7 +2218,7 @@ def generate_cellpose_masks(src, settings, object_type):
2403
2218
  def generate_masks_from_imgs(src, model, model_name, batch_size, diameter, cellprob_threshold, flow_threshold, grayscale, save, normalize, channels, percentiles, circular, invert, plot, resize, target_height, target_width, remove_background, background, Signal_to_noise, verbose):
2404
2219
 
2405
2220
  from .io import _load_images_and_labels, _load_normalized_images_and_labels
2406
- from .utils import resize_images_and_labels, resizescikit
2221
+ from .utils import resize_images_and_labels, resizescikit, print_progress
2407
2222
  from .plot import print_mask_and_flows
2408
2223
 
2409
2224
  dst = os.path.join(src, model_name)
@@ -2462,8 +2277,11 @@ def generate_masks_from_imgs(src, model, model_name, batch_size, diameter, cellp
2462
2277
  stop = time.time()
2463
2278
  duration = (stop - start)
2464
2279
  time_ls.append(duration)
2465
- average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
2466
- print(f'Processing {file_index+1}/{len(images)} images : Time/image {average_time:.3f} sec', end='\r', flush=True)
2280
+ files_processed = file_index+1
2281
+ files_to_process = len(images)
2282
+
2283
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Generating masks")
2284
+
2467
2285
  if plot:
2468
2286
  if resize:
2469
2287
  stack = resizescikit(stack, dims, preserve_range=True, anti_aliasing=False).astype(stack.dtype)