spacr 0.2.46__py3-none-any.whl → 0.2.53__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 (58) hide show
  1. spacr/core.py +24 -11
  2. spacr/gui.py +0 -2
  3. spacr/gui_core.py +70 -55
  4. spacr/gui_elements.py +367 -152
  5. spacr/gui_utils.py +59 -68
  6. spacr/io.py +2 -3
  7. spacr/measure.py +196 -145
  8. spacr/plot.py +2 -42
  9. spacr/resources/font/open_sans/OFL.txt +93 -0
  10. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  11. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/README.txt +100 -0
  13. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  14. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  49. spacr/sequencing.py +107 -13
  50. spacr/settings.py +27 -84
  51. spacr/utils.py +9 -9
  52. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/METADATA +6 -4
  53. spacr-0.2.53.dist-info/RECORD +100 -0
  54. spacr-0.2.46.dist-info/RECORD +0 -60
  55. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/LICENSE +0 -0
  56. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/WHEEL +0 -0
  57. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/entry_points.txt +0 -0
  58. {spacr-0.2.46.dist-info → spacr-0.2.53.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,10 +984,9 @@ 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)
@@ -943,9 +994,14 @@ def measure_crop(settings):
943
994
 
944
995
  src_fldr = settings['src']
945
996
  if not os.path.basename(src_fldr).endswith('merged'):
946
- print(f"WARNING: Source folder, settings: src: {src_fldr} should end with 'merged'")
997
+ print(f"WARNING: Source folder, settings: src: {src_fldr} should end with '/merged'")
947
998
  src_fldr = os.path.join(src_fldr, 'merged')
948
999
  print(f"Changed source folder to: {src_fldr}")
1000
+
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')
949
1005
 
950
1006
  if settings['cell_mask_dim'] is None:
951
1007
  settings['include_uninfected'] = True
@@ -957,7 +1013,15 @@ def measure_crop(settings):
957
1013
  settings['cytoplasm'] = True
958
1014
  else:
959
1015
  settings['cytoplasm'] = False
960
-
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
+
961
1025
  dirname = os.path.dirname(settings['src'])
962
1026
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
963
1027
  settings_csv = os.path.join(dirname,'settings','measure_crop_settings.csv')
@@ -994,76 +1058,63 @@ def measure_crop(settings):
994
1058
 
995
1059
  _save_settings_to_db(settings)
996
1060
  files = [f for f in os.listdir(settings['src']) if f.endswith('.npy')]
997
- n_jobs = settings['n_jobs'] or mp.cpu_count()-4
1061
+ n_jobs = settings['n_jobs']
998
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
+
999
1074
  with mp.Manager() as manager:
1000
1075
  time_ls = manager.list()
1076
+ completed_jobs = set() # Set to keep track of completed jobs
1077
+
1001
1078
  with mp.Pool(n_jobs) as pool:
1002
- result = pool.starmap_async(_measure_crop_core, [(index, time_ls, file, settings) for index, file in enumerate(files)])
1003
-
1004
- # Track progress in the main process
1005
- while not result.ready():
1006
- time.sleep(1)
1007
- files_processed = len(time_ls)
1008
- files_to_process = len(files)
1009
- print_progress(files_processed, files_to_process, n_jobs, time_ls=time_ls, operation_type='Measure and Crop')
1010
- result.get()
1011
-
1012
- if settings['representative_images']:
1013
- if settings['save_png']:
1014
- img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
1015
- sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
1016
-
1017
- for i, well_src in enumerate(sc_img_fldrs):
1018
- if len(os.listdir(well_src)) < 16:
1019
- nr_imgs = len(os.listdir(well_src))
1020
- standardize = False
1021
- else:
1022
- nr_imgs = 16
1023
- standardize = True
1024
- try:
1025
- all_folders = len(sc_img_fldrs)
1026
- _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)
1027
-
1028
- except Exception as e:
1029
- print(f"Unable to generate figure for folder {well_src}: {e}", end='\r', flush=True)
1030
- #traceback.print_exc()
1031
-
1032
- if settings['save_measurements']:
1033
- db_path = os.path.join(os.path.dirname(settings['src']), 'measurements', 'measurements.db')
1034
- channel_indices = settings['png_dims']
1035
- channel_indices = [min(value, 2) for value in channel_indices]
1036
- _generate_representative_images(db_path,
1037
- cells=settings['cells'],
1038
- cell_loc=settings['cell_loc'],
1039
- pathogens=settings['pathogens'],
1040
- pathogen_loc=settings['pathogen_loc'],
1041
- treatments=settings['treatments'],
1042
- treatment_loc=settings['treatment_loc'],
1043
- channel_of_interest=settings['channel_of_interest'],
1044
- compartments = settings['compartments'],
1045
- measurement = settings['measurement'],
1046
- nr_imgs=settings['nr_imgs'],
1047
- channel_indices=channel_indices,
1048
- um_per_pixel=settings['um_per_pixel'],
1049
- scale_bar_length_um=10,
1050
- plot=False,
1051
- fontsize=12,
1052
- show_filename=True,
1053
- 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()
1054
1084
 
1055
1085
  if settings['timelapse']:
1056
1086
  if settings['timelapse_objects'] == 'nucleus':
1057
1087
  folder_path = settings['src']
1058
- mask_channels = [settings['nucleus_mask_dim'], settings['pathogen_mask_dim'],settings['cell_mask_dim']]
1059
- 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']
1060
1090
  _timelapse_masks_to_gif(folder_path, mask_channels, object_types)
1061
1091
 
1062
- #if settings['save_png']:
1063
- img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
1064
- sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
1065
- _scmovie(sc_img_fldrs)
1066
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)
1067
1118
 
1068
1119
  def generate_cellpose_train_set(folders, dst, min_objects=5):
1069
1120
  os.makedirs(dst, exist_ok=True)
spacr/plot.py CHANGED
@@ -908,49 +908,9 @@ def _plot_cropped_arrays(stack, filename, figuresize=20, cmap='inferno', thresho
908
908
  fig, axs = plt.subplots(1, num_channels, figsize=(figuresize, figuresize))
909
909
  for channel in range(num_channels):
910
910
  plot_single_array(stack[:, :, channel], axs[channel], f'C. {channel}', plt.get_cmap(cmap))
911
- fig.tight_layout()
912
- plt.show()
913
-
914
- #stop = time.time()
915
- #duration = stop - start
916
- #print('plot_cropped_arrays', duration)
911
+ fig.tight_layout()
917
912
  print(f'{filename}')
918
-
919
- def _plot_cropped_arrays_v1(stack, figuresize=20, cmap='inferno'):
920
- """
921
- Plot cropped arrays.
922
-
923
- Args:
924
- stack (ndarray): The array to be plotted.
925
- figuresize (int, optional): The size of the figure. Defaults to 20.
926
- cmap (str, optional): The colormap to be used. Defaults to 'inferno'.
927
-
928
- Returns:
929
- None
930
- """
931
- start = time.time()
932
- dim = stack.shape
933
- channel=min(dim)
934
- if len(stack.shape) == 2:
935
- f, a = plt.subplots(1, 1,figsize=(figuresize,figuresize))
936
- a.imshow(stack, cmap=plt.get_cmap(cmap))
937
- a.set_title('Channel one',size=18)
938
- a.axis('off')
939
- f.tight_layout()
940
- plt.show()
941
- if len(stack.shape) > 2:
942
- anr = stack.shape[2]
943
- f, a = plt.subplots(1, anr,figsize=(figuresize,figuresize))
944
- for channel in range(anr):
945
- a[channel].imshow(stack[:,:,channel], cmap=plt.get_cmap(cmap))
946
- a[channel].set_title('Channel '+str(channel),size=18)
947
- a[channel].axis('off')
948
- f.tight_layout()
949
- plt.show()
950
- stop = time.time()
951
- duration = stop - start
952
- print('plot_cropped_arrays', duration)
953
- return
913
+ return fig
954
914
 
955
915
  def _visualize_and_save_timelapse_stack_with_tracks(masks, tracks_df, save, src, name, plot, filenames, object_type, mode='btrack', interactive=False):
956
916
  """
@@ -0,0 +1,93 @@
1
+ Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ https://openfontlicense.org
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.