spacr 0.2.4__py3-none-any.whl → 0.2.8__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 (63) hide show
  1. spacr/__init__.py +1 -11
  2. spacr/core.py +277 -349
  3. spacr/deep_spacr.py +248 -269
  4. spacr/gui.py +58 -54
  5. spacr/gui_core.py +689 -535
  6. spacr/gui_elements.py +1002 -153
  7. spacr/gui_utils.py +452 -107
  8. spacr/io.py +158 -91
  9. spacr/measure.py +199 -151
  10. spacr/plot.py +159 -47
  11. spacr/resources/font/open_sans/OFL.txt +93 -0
  12. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  13. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  14. spacr/resources/font/open_sans/README.txt +100 -0
  15. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  49. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  50. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  51. spacr/resources/icons/logo.pdf +2786 -6
  52. spacr/resources/icons/logo_spacr.png +0 -0
  53. spacr/resources/icons/logo_spacr_1.png +0 -0
  54. spacr/sequencing.py +477 -587
  55. spacr/settings.py +217 -144
  56. spacr/utils.py +46 -46
  57. {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/METADATA +46 -35
  58. spacr-0.2.8.dist-info/RECORD +100 -0
  59. {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/WHEEL +1 -1
  60. spacr-0.2.4.dist-info/RECORD +0 -58
  61. {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/LICENSE +0 -0
  62. {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/entry_points.txt +0 -0
  63. {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/top_level.txt +0 -0
spacr/measure.py CHANGED
@@ -13,6 +13,8 @@ from skimage.feature import graycomatrix, graycoprops
13
13
  from mahotas.features import zernike_moments
14
14
  from skimage import morphology, measure, filters
15
15
  from skimage.util import img_as_bool
16
+ import matplotlib.pyplot as plt
17
+ from math import ceil, sqrt
16
18
 
17
19
  from .logger import log_function_call
18
20
 
@@ -582,6 +584,113 @@ def _intensity_measurements(cell_mask, nucleus_mask, pathogen_mask, cytoplasm_ma
582
584
 
583
585
  return pd.concat(cell_dfs, axis=1), pd.concat(nucleus_dfs, axis=1), pd.concat(pathogen_dfs, axis=1), pd.concat(cytoplasm_dfs, axis=1)
584
586
 
587
+ def save_and_add_image_to_grid(png_channels, img_path, grid, plot=False):
588
+ """
589
+ Add an image to a grid and save it as PNG.
590
+
591
+ Args:
592
+ png_channels (ndarray): The array representing the image channels.
593
+ img_path (str): The path to save the image as PNG.
594
+ grid (list): The grid of images to be plotted later.
595
+
596
+ Returns:
597
+ grid (list): Updated grid with the new image added.
598
+ """
599
+
600
+ # Save the image as a PNG
601
+ cv2.imwrite(img_path, png_channels)
602
+
603
+ if plot:
604
+
605
+ # Ensure the image is in uint8 format for cv2 functions
606
+ if png_channels.dtype == np.uint16:
607
+ png_channels = (png_channels / 256).astype(np.uint8)
608
+
609
+ # Get the filename without the extension
610
+ filename = os.path.splitext(os.path.basename(img_path))[0]
611
+
612
+ # Add the label to the image
613
+ #labeled_image = cv2.putText(png_channels.copy(), filename, (10, 30),
614
+ # cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
615
+
616
+ # Add the labeled image to the grid
617
+ grid.append(png_channels)
618
+
619
+ return grid
620
+
621
+ def img_list_to_grid(grid, titles=None):
622
+ """
623
+ Plot a grid of images with optional titles.
624
+
625
+ Args:
626
+ grid (list): List of images to be plotted.
627
+ titles (list): List of titles for the images.
628
+
629
+ Returns:
630
+ fig (Figure): The matplotlib figure object containing the image grid.
631
+ """
632
+ n_images = len(grid)
633
+ grid_size = ceil(sqrt(n_images))
634
+
635
+ fig, axs = plt.subplots(grid_size, grid_size, figsize=(15, 15), facecolor='black')
636
+
637
+ for i, ax in enumerate(axs.flat):
638
+ if i < n_images:
639
+ image = grid[i]
640
+ ax.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
641
+ ax.axis('off')
642
+ ax.set_facecolor('black')
643
+
644
+ if titles:
645
+ # Determine text size
646
+ img_height, img_width = image.shape[:2]
647
+ text_size = max(min(img_width / (len(titles[i]) * 1.5), img_height / 10), 4)
648
+ ax.text(5, 5, titles[i], color='white', fontsize=text_size, ha='left', va='top', fontweight='bold')
649
+ else:
650
+ fig.delaxes(ax)
651
+
652
+ # Adjust spacing
653
+ plt.subplots_adjust(wspace=0.05, hspace=0.05)
654
+ plt.tight_layout(pad=0.1)
655
+ return fig
656
+
657
+ def filepaths_to_database(img_paths, settings, source_folder, crop_mode):
658
+ from. utils import _map_wells_png
659
+ png_df = pd.DataFrame(img_paths, columns=['png_path'])
660
+
661
+ png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
662
+
663
+ parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=settings['timelapse'])))
664
+
665
+ columns = ['plate', 'row', 'col', 'field']
666
+
667
+ if settings['timelapse']:
668
+ columns = columns + ['time_id']
669
+
670
+ columns = columns + ['prcfo']
671
+
672
+ if crop_mode == 'cell':
673
+ columns = columns + ['cell_id']
674
+
675
+ if crop_mode == 'nucleus':
676
+ columns = columns + ['nucleus_id']
677
+
678
+ if crop_mode == 'pathogen':
679
+ columns = columns + ['pathogen_id']
680
+
681
+ if crop_mode == 'cytoplasm':
682
+ columns = columns + ['cytoplasm_id']
683
+
684
+ png_df[columns] = parts
685
+
686
+ try:
687
+ conn = sqlite3.connect(f'{source_folder}/measurements/measurements.db', timeout=5)
688
+ png_df.to_sql('png_list', conn, if_exists='append', index=False)
689
+ conn.commit()
690
+ except sqlite3.OperationalError as e:
691
+ print(f"SQLite error: {e}", flush=True)
692
+ traceback.print_exc()
693
+
585
694
  #@log_function_call
586
695
  def _measure_crop_core(index, time_ls, file, settings):
587
696
 
@@ -603,20 +712,15 @@ def _measure_crop_core(index, time_ls, file, settings):
603
712
  A list of cropped images.
604
713
  """
605
714
 
606
- from .io import _create_database
607
715
  from .plot import _plot_cropped_arrays
608
716
  from .utils import _merge_overlapping_objects, _filter_object, _relabel_parent_with_child_labels, _exclude_objects, normalize_to_dtype
609
- from .utils import _merge_and_save_to_database, _crop_center, _find_bounding_box, _generate_names, _get_percentiles, _map_wells_png
610
-
717
+ from .utils import _merge_and_save_to_database, _crop_center, _find_bounding_box, _generate_names, _get_percentiles
718
+
719
+ figs = {}
720
+ grid = []
611
721
  start = time.time()
612
722
  try:
613
723
  source_folder = os.path.dirname(settings['src'])
614
- #if not os.path.basename(source_folder).endswith('merged'):
615
- # source_folder = os.path.join(source_folder, 'merged')
616
- # print(f'changed source_folder to {source_folder}')
617
-
618
- #if not os.path.exists(source_folder):
619
- # return
620
724
 
621
725
  file_name = os.path.splitext(file)[0]
622
726
  data = np.load(os.path.join(settings['src'], file))
@@ -628,18 +732,13 @@ def _measure_crop_core(index, time_ls, file, settings):
628
732
  if settings['verbose']:
629
733
  print(f'Converted data from {data_type_before} to {data_type}')
630
734
 
631
- if settings['save_measurements']:
632
- os.makedirs(source_folder+'/measurements', exist_ok=True)
633
- _create_database(source_folder+'/measurements/measurements.db')
634
-
635
- if settings['plot_filtration']:
636
-
735
+ if settings['plot']:
637
736
  if len(data.shape) == 3:
638
737
  figuresize = data.shape[2]*10
639
738
  else:
640
739
  figuresize = 10
641
- print('')
642
- _plot_cropped_arrays(data, file, figuresize)
740
+ fig = _plot_cropped_arrays(data, file, figuresize)
741
+ figs[f'{file_name}__before_filtration'] = fig
643
742
 
644
743
  channel_arrays = data[:, :, settings['channels']].astype(data_type)
645
744
  if settings['cell_mask_dim'] is not None:
@@ -714,9 +813,9 @@ def _measure_crop_core(index, time_ls, file, settings):
714
813
  if settings['cytoplasm']:
715
814
  data = np.concatenate((data, cytoplasm_mask[:, :, np.newaxis]), axis=2)
716
815
 
717
- if settings['plot_filtration']:
718
- _plot_cropped_arrays(data, file, figuresize)
719
- #_plot_cropped_arrays(data)
816
+ if settings['plot']:
817
+ fig = _plot_cropped_arrays(data, file, figuresize)
818
+ figs[f'{file_name}__after_filtration'] = fig
720
819
 
721
820
  if settings['save_measurements']:
722
821
 
@@ -837,53 +936,12 @@ def _measure_crop_core(index, time_ls, file, settings):
837
936
  if png_channels.shape[2] == 2:
838
937
  dummy_channel = np.zeros_like(png_channels[:,:,0]) # Create a 2D zero array with same shape as one channel
839
938
  png_channels = np.dstack((png_channels, dummy_channel))
840
- cv2.imwrite(img_path, png_channels)
939
+ grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
841
940
  else:
842
- cv2.imwrite(img_path, png_channels)
941
+ grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
843
942
 
844
943
  if len(img_paths) == len(objects_in_image):
845
-
846
- png_df = pd.DataFrame(img_paths, columns=['png_path'])
847
-
848
- png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
849
-
850
- parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=settings['timelapse'])))
851
-
852
- columns = ['plate', 'row', 'col', 'field']
853
-
854
- if settings['timelapse']:
855
- columns = columns + ['time_id']
856
-
857
- columns = columns + ['prcfo']
858
-
859
- if crop_mode == 'cell':
860
- columns = columns + ['cell_id']
861
-
862
- if crop_mode == 'nucleus':
863
- columns = columns + ['nucleus_id']
864
-
865
- if crop_mode == 'pathogen':
866
- columns = columns + ['pathogen_id']
867
-
868
- if crop_mode == 'cytoplasm':
869
- columns = columns + ['cytoplasm_id']
870
-
871
- png_df[columns] = parts
872
-
873
- try:
874
- conn = sqlite3.connect(f'{source_folder}/measurements/measurements.db', timeout=5)
875
- png_df.to_sql('png_list', conn, if_exists='append', index=False)
876
- conn.commit()
877
- except sqlite3.OperationalError as e:
878
- print(f"SQLite error: {e}", flush=True)
879
- traceback.print_exc()
880
-
881
- if settings['plot']:
882
- if len(png_channels.shape) == 3:
883
- figuresize = png_channels.shape[2]*10
884
- else:
885
- figuresize = 10
886
- _plot_cropped_arrays(png_channels, img_name, figuresize, threshold=1)
944
+ filepaths_to_database(img_paths, settings, source_folder, crop_mode)
887
945
 
888
946
  if settings['save_arrays']:
889
947
  row_idx, col_idx = np.where(region)
@@ -891,21 +949,12 @@ def _measure_crop_core(index, time_ls, file, settings):
891
949
  array_folder = f"{fldr}/region_array/"
892
950
  os.makedirs(array_folder, exist_ok=True)
893
951
  np.save(os.path.join(array_folder, img_name), region_array)
894
- if settings['plot']:
895
- if len(png_channels.shape) == 3:
896
- figuresize = png_channels.shape[2]*10
897
- else:
898
- figuresize = 10
899
- _plot_cropped_arrays(png_channels, img_name, figuresize, threshold=1)
900
-
901
- if not settings['save_arrays'] and not settings['save_png'] and settings['plot']:
902
- row_idx, col_idx = np.where(region)
903
- region_array = data[row_idx.min():row_idx.max()+1, col_idx.min():col_idx.max()+1, :]
904
- if len(png_channels.shape) == 3:
905
- figuresize = png_channels.shape[2]*10
906
- else:
907
- figuresize = 10
908
- _plot_cropped_arrays(png_channels, file, figuresize, threshold=1)
952
+
953
+ grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
954
+
955
+ img_paths.append(img_path)
956
+ if len(img_paths) == len(objects_in_image):
957
+ filepaths_to_database(img_paths, settings, source_folder, crop_mode)
909
958
 
910
959
  cells = np.unique(cell_mask)
911
960
  except Exception as e:
@@ -917,7 +966,10 @@ def _measure_crop_core(index, time_ls, file, settings):
917
966
  duration = end-start
918
967
  time_ls.append(duration)
919
968
  average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
920
- return average_time, cells
969
+ if settings['plot']:
970
+ fig = img_list_to_grid(grid)
971
+ figs[f'{file_name}__pngs'] = fig
972
+ return index, average_time, cells, figs
921
973
 
922
974
  #@log_function_call
923
975
  def measure_crop(settings):
@@ -932,23 +984,24 @@ def measure_crop(settings):
932
984
  None
933
985
  """
934
986
 
935
- from .io import _save_settings_to_db
936
- from .timelapse import _timelapse_masks_to_gif, _scmovie
937
- from .plot import _save_scimg_plot
938
- from .utils import _list_endpoint_subdirectories, _generate_representative_images, measure_test_mode, print_progress
987
+ from .io import _save_settings_to_db, _create_database
988
+ from .timelapse import _timelapse_masks_to_gif
989
+ from .utils import measure_test_mode, print_progress
939
990
  from .settings import get_measure_crop_settings
940
991
 
941
992
  settings = get_measure_crop_settings(settings)
942
993
  settings = measure_test_mode(settings)
943
994
 
944
- #src_fldr = settings['src']
945
- #if not os.path.basename(src_fldr).endswith('merged'):
946
- # settings['src'] = os.path.join(src_fldr, 'merged')
947
- # print(f"changed src to {src_fldr}")
995
+ src_fldr = settings['src']
996
+ if not os.path.basename(src_fldr).endswith('merged'):
997
+ print(f"WARNING: Source folder, settings: src: {src_fldr} should end with '/merged'")
998
+ src_fldr = os.path.join(src_fldr, 'merged')
999
+ print(f"Changed source folder to: {src_fldr}")
948
1000
 
949
- #if not os.path.exists(settings['src']):
950
- # print(f'src: {settings["src"]} does not exist')
951
- # return
1001
+ if settings['save_measurements']:
1002
+ source_folder = os.path.dirname(settings['src'])
1003
+ os.makedirs(source_folder+'/measurements', exist_ok=True)
1004
+ _create_database(source_folder+'/measurements/measurements.db')
952
1005
 
953
1006
  if settings['cell_mask_dim'] is None:
954
1007
  settings['include_uninfected'] = True
@@ -960,7 +1013,15 @@ def measure_crop(settings):
960
1013
  settings['cytoplasm'] = True
961
1014
  else:
962
1015
  settings['cytoplasm'] = False
963
-
1016
+
1017
+ spacr_cores = int(mp.cpu_count() - 6)
1018
+ if spacr_cores <= 2:
1019
+ spacr_cores = 1
1020
+
1021
+ if settings['n_jobs'] > spacr_cores:
1022
+ print(f'Warning reserving 6 CPU cores for other processes, setting n_jobs to {spacr_cores}')
1023
+ settings['n_jobs'] = spacr_cores
1024
+
964
1025
  dirname = os.path.dirname(settings['src'])
965
1026
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
966
1027
  settings_csv = os.path.join(dirname,'settings','measure_crop_settings.csv')
@@ -997,76 +1058,63 @@ def measure_crop(settings):
997
1058
 
998
1059
  _save_settings_to_db(settings)
999
1060
  files = [f for f in os.listdir(settings['src']) if f.endswith('.npy')]
1000
- n_jobs = settings['n_jobs'] or mp.cpu_count()-4
1061
+ n_jobs = settings['n_jobs']
1001
1062
  print(f'using {n_jobs} cpu cores')
1063
+ print_progress(files_processed=0, files_to_process=len(files), n_jobs=n_jobs, time_ls=[], operation_type='Measure and Crop')
1064
+
1065
+ def job_callback(result):
1066
+ completed_jobs.add(result[0])
1067
+ process_meassure_crop_results([result], settings)
1068
+ files_processed = len(completed_jobs)
1069
+ files_to_process = len(files)
1070
+ print_progress(files_processed, files_to_process, n_jobs, time_ls=time_ls, operation_type='Measure and Crop')
1071
+ if files_processed >= files_to_process:
1072
+ pool.terminate()
1073
+
1002
1074
  with mp.Manager() as manager:
1003
1075
  time_ls = manager.list()
1076
+ completed_jobs = set() # Set to keep track of completed jobs
1077
+
1004
1078
  with mp.Pool(n_jobs) as pool:
1005
- result = pool.starmap_async(_measure_crop_core, [(index, time_ls, file, settings) for index, file in enumerate(files)])
1006
-
1007
- # Track progress in the main process
1008
- while not result.ready():
1009
- time.sleep(1)
1010
- files_processed = len(time_ls)
1011
- files_to_process = len(files)
1012
- print_progress(files_processed, files_to_process, n_jobs, time_ls=None)
1013
- result.get()
1014
-
1015
- if settings['representative_images']:
1016
- if settings['save_png']:
1017
- img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
1018
- sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
1019
-
1020
- for i, well_src in enumerate(sc_img_fldrs):
1021
- if len(os.listdir(well_src)) < 16:
1022
- nr_imgs = len(os.listdir(well_src))
1023
- standardize = False
1024
- else:
1025
- nr_imgs = 16
1026
- standardize = True
1027
- try:
1028
- all_folders = len(sc_img_fldrs)
1029
- _save_scimg_plot(src=well_src, nr_imgs=nr_imgs, channel_indices=settings['png_dims'], um_per_pixel=0.1, scale_bar_length_um=10, standardize=standardize, fontsize=12, show_filename=True, channel_names=['red','green','blue'], dpi=300, plot=False, i=i, all_folders=all_folders)
1030
-
1031
- except Exception as e:
1032
- print(f"Unable to generate figure for folder {well_src}: {e}", end='\r', flush=True)
1033
- #traceback.print_exc()
1034
-
1035
- if settings['save_measurements']:
1036
- db_path = os.path.join(os.path.dirname(settings['src']), 'measurements', 'measurements.db')
1037
- channel_indices = settings['png_dims']
1038
- channel_indices = [min(value, 2) for value in channel_indices]
1039
- _generate_representative_images(db_path,
1040
- cells=settings['cells'],
1041
- cell_loc=settings['cell_loc'],
1042
- pathogens=settings['pathogens'],
1043
- pathogen_loc=settings['pathogen_loc'],
1044
- treatments=settings['treatments'],
1045
- treatment_loc=settings['treatment_loc'],
1046
- channel_of_interest=settings['channel_of_interest'],
1047
- compartments = settings['compartments'],
1048
- measurement = settings['measurement'],
1049
- nr_imgs=settings['nr_imgs'],
1050
- channel_indices=channel_indices,
1051
- um_per_pixel=settings['um_per_pixel'],
1052
- scale_bar_length_um=10,
1053
- plot=False,
1054
- fontsize=12,
1055
- show_filename=True,
1056
- channel_names=None)
1079
+ for index, file in enumerate(files):
1080
+ pool.apply_async(_measure_crop_core, args=(index, time_ls, file, settings), callback=job_callback)
1081
+
1082
+ pool.close()
1083
+ pool.join()
1057
1084
 
1058
1085
  if settings['timelapse']:
1059
1086
  if settings['timelapse_objects'] == 'nucleus':
1060
1087
  folder_path = settings['src']
1061
- mask_channels = [settings['nucleus_mask_dim'], settings['pathogen_mask_dim'],settings['cell_mask_dim']]
1062
- object_types = ['nucleus','pathogen','cell']
1088
+ mask_channels = [settings['nucleus_mask_dim'], settings['pathogen_mask_dim'], settings['cell_mask_dim']]
1089
+ object_types = ['nucleus', 'pathogen', 'cell']
1063
1090
  _timelapse_masks_to_gif(folder_path, mask_channels, object_types)
1064
1091
 
1065
- #if settings['save_png']:
1066
- img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
1067
- sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
1068
- _scmovie(sc_img_fldrs)
1069
1092
  print("Successfully completed run")
1093
+
1094
+ def process_meassure_crop_results(partial_results, settings):
1095
+ """
1096
+ Process the results, display, and optionally save the figures.
1097
+
1098
+ Args:
1099
+ partial_results (list): List of partial results.
1100
+ settings (dict): Settings dictionary.
1101
+ save_figures (bool): Flag to save figures or not.
1102
+ """
1103
+ for result in partial_results:
1104
+ if result is None:
1105
+ continue
1106
+ index, avg_time, cells, figs = result
1107
+ if figs is not None:
1108
+ for key, fig in figs.items():
1109
+ part_1, part_2 = key.split('__')
1110
+ save_dir = os.path.join(os.path.dirname(settings['src']), 'results', f"{part_1}")
1111
+ os.makedirs(save_dir, exist_ok=True)
1112
+ fig_path = os.path.join(save_dir, f"{part_2}.pdf")
1113
+ fig.savefig(fig_path)
1114
+ plt.figure(fig.number)
1115
+ plt.show()
1116
+ plt.close(fig)
1117
+ result = (index, None, None, None)
1070
1118
 
1071
1119
  def generate_cellpose_train_set(folders, dst, min_objects=5):
1072
1120
  os.makedirs(dst, exist_ok=True)