spacr 0.1.16__py3-none-any.whl → 0.1.55__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.
spacr/measure.py CHANGED
@@ -617,6 +617,13 @@ def _measure_crop_core(index, time_ls, file, settings):
617
617
  start = time.time()
618
618
  try:
619
619
  source_folder = os.path.dirname(settings['input_folder'])
620
+ #if not os.path.basename(source_folder).endswith('merged'):
621
+ # source_folder = os.path.join(source_folder, 'merged')
622
+ # print(f'changed source_folder to {source_folder}')
623
+
624
+ #if not os.path.exists(source_folder):
625
+ # return
626
+
620
627
  file_name = os.path.splitext(file)[0]
621
628
  data = np.load(os.path.join(settings['input_folder'], file))
622
629
 
@@ -750,6 +757,15 @@ def _measure_crop_core(index, time_ls, file, settings):
750
757
  if isinstance(settings['crop_mode'], list):
751
758
  crop_ls = settings['crop_mode']
752
759
  size_ls = settings['png_size']
760
+
761
+ if isinstance(size_ls[0], int):
762
+ size_ls = [size_ls]
763
+ if len(crop_ls) > 1 and len(size_ls) == 1:
764
+ size_ls = size_ls * len(crop_ls)
765
+
766
+ if len(crop_ls) != len(size_ls):
767
+ print(f"Setting: size_ls: {settings['png_size']} should be a list of integers, or a list of lists of integers if crop_ls: {settings['crop_mode']} has multiple elements")
768
+
753
769
  for crop_idx, crop_mode in enumerate(crop_ls):
754
770
  width, height = size_ls[crop_idx]
755
771
  if crop_mode == 'cell':
@@ -926,9 +942,14 @@ def measure_crop(settings):
926
942
  settings = get_measure_crop_settings(settings)
927
943
  settings = measure_test_mode(settings)
928
944
 
929
- if not os.path.exists(settings['input_folder']):
930
- print(f"Error: {settings['input_folder']} does not exist")
931
- return
945
+ #src_fldr = settings['input_folder']
946
+ #if not os.path.basename(src_fldr).endswith('merged'):
947
+ # settings['input_folder'] = os.path.join(src_fldr, 'merged')
948
+ # print(f"changed input_folder to {src_fldr}")
949
+
950
+ #if not os.path.exists(settings['input_folder']):
951
+ # print(f'input_folder: {settings["input_folder"]} does not exist')
952
+ # return
932
953
 
933
954
  if settings['cell_mask_dim'] is None:
934
955
  settings['include_uninfected'] = True
spacr/sequencing.py CHANGED
@@ -37,22 +37,6 @@ def analyze_reads(settings):
37
37
  None
38
38
  """
39
39
 
40
- def save_chunk_to_hdf5_v1(output_file_path, data_chunk, chunk_counter):
41
- """
42
- Save a data chunk to an HDF5 file.
43
-
44
- Parameters:
45
- - output_file_path (str): The path to the output HDF5 file.
46
- - data_chunk (list): The data chunk to be saved.
47
- - chunk_counter (int): The counter for the current chunk.
48
-
49
- Returns:
50
- None
51
- """
52
- df = pd.DataFrame(data_chunk, columns=['combined_read', 'grna', 'plate_row', 'column', 'sample'])
53
- with pd.HDFStore(output_file_path, mode='a', complevel=5, complib='blosc') as store:
54
- store.put(f'reads/chunk_{chunk_counter}', df, format='table', append=True)
55
-
56
40
  def save_chunk_to_hdf5(output_file_path, data_chunk, chunk_counter):
57
41
  """
58
42
  Save a data chunk to an HDF5 file.
@@ -306,7 +290,7 @@ def analyze_reads(settings):
306
290
  qc_df = pd.DataFrame([qc])
307
291
  qc_df.to_csv(qc_file_path, index=False)
308
292
 
309
- from .utils import get_analyze_reads_default_settings
293
+ from .settings import get_analyze_reads_default_settings
310
294
 
311
295
  settings = get_analyze_reads_default_settings(settings)
312
296
 
spacr/settings.py CHANGED
@@ -1,4 +1,4 @@
1
- import os
1
+ import os, ast
2
2
 
3
3
  def set_default_plot_merge_settings():
4
4
  settings = {}
@@ -27,7 +27,13 @@ def set_default_plot_merge_settings():
27
27
 
28
28
  def set_default_settings_preprocess_generate_masks(src, settings={}):
29
29
  # Main settings
30
- settings['src'] = src
30
+ if src != None:
31
+ settings['src'] = src
32
+ else:
33
+ settings.setdefault('src', 'path')
34
+ if 'src' not in settings:
35
+ settings['src'] = 'path'
36
+
31
37
  settings.setdefault('preprocess', True)
32
38
  settings.setdefault('masks', True)
33
39
  settings.setdefault('save', True)
@@ -162,9 +168,9 @@ def _get_object_settings(object_type, settings):
162
168
  if settings['verbose']:
163
169
  print(object_settings)
164
170
 
165
- return object_settings
171
+ return object_settings
166
172
 
167
- def get_umap_image_settings(settings={}):
173
+ def set_default_umap_image_settings(settings={}):
168
174
  settings.setdefault('src', 'path')
169
175
  settings.setdefault('row_limit', 1000)
170
176
  settings.setdefault('tables', ['cell', 'cytoplasm', 'nucleus', 'pathogen'])
@@ -212,9 +218,12 @@ def get_umap_image_settings(settings={}):
212
218
 
213
219
  def get_measure_crop_settings(settings):
214
220
 
221
+ settings.setdefault('src', 'path')
222
+
215
223
  # Test mode
216
224
  settings.setdefault('test_mode', False)
217
225
  settings.setdefault('test_nr', 10)
226
+ settings.setdefault('channels', [0,1,2,3])
218
227
 
219
228
  #measurement settings
220
229
  settings.setdefault('save_measurements',True)
@@ -260,7 +269,7 @@ def get_measure_crop_settings(settings):
260
269
 
261
270
  # Miscellaneous settings
262
271
  settings.setdefault('experiment', 'exp')
263
- settings.setdefault('cells', 'HeLa')
272
+ settings.setdefault('cells', ['HeLa'])
264
273
  settings.setdefault('cell_loc', None)
265
274
  settings.setdefault('pathogens', ['ME49Dku80WT', 'ME49Dku80dgra8:GRA8', 'ME49Dku80dgra8', 'ME49Dku80TKO'])
266
275
  settings.setdefault('pathogen_loc', [['c1', 'c2', 'c3', 'c4', 'c5', 'c6'], ['c7', 'c8', 'c9', 'c10', 'c11', 'c12'], ['c13', 'c14', 'c15', 'c16', 'c17', 'c18'], ['c19', 'c20', 'c21', 'c22', 'c23', 'c24']])
@@ -304,6 +313,8 @@ def set_default_analyze_screen(settings):
304
313
 
305
314
  def set_default_train_test_model(settings):
306
315
  cores = os.cpu_count()-2
316
+
317
+ settings.setdefault('src','path')
307
318
  settings.setdefault('train',True)
308
319
  settings.setdefault('test',False)
309
320
  settings.setdefault('classes',['nc','pc'])
@@ -474,4 +485,427 @@ def get_identify_masks_finetune_default_settings(settings):
474
485
  settings.setdefault('rescale', False)
475
486
  settings.setdefault('resample', False)
476
487
  settings.setdefault('grayscale', True)
477
- return settings
488
+ return settings
489
+
490
+ q = None
491
+
492
+ def check_settings(vars_dict):
493
+ global q
494
+ from .gui_utils import parse_list
495
+ settings = {}
496
+ # Define the expected types for each key, including None where applicable
497
+ expected_types = {
498
+ "src": str,
499
+ "metadata_type": str,
500
+ "custom_regex": (str, type(None)),
501
+ "experiment": str,
502
+ "channels": list,
503
+ "magnification": int,
504
+ "nucleus_channel": (int, type(None)),
505
+ "nucleus_background": int,
506
+ "nucleus_Signal_to_noise": float,
507
+ "nucleus_CP_prob": float,
508
+ "nucleus_FT": float,
509
+ "cell_channel": (int, type(None)),
510
+ "cell_background": (int, float),
511
+ "cell_Signal_to_noise": (int, float),
512
+ "cell_CP_prob": (int, float),
513
+ "cell_FT": (int, float),
514
+ "pathogen_channel": (int, type(None)),
515
+ "pathogen_background": (int, float),
516
+ "pathogen_Signal_to_noise": (int, float),
517
+ "pathogen_CP_prob": (int, float),
518
+ "pathogen_FT": (int, float),
519
+ "preprocess": bool,
520
+ "masks": bool,
521
+ "examples_to_plot": int,
522
+ "randomize": bool,
523
+ "batch_size": int,
524
+ "timelapse": bool,
525
+ "timelapse_displacement": int,
526
+ "timelapse_memory": int,
527
+ "timelapse_frame_limits": list, # This can be a list of lists
528
+ "timelapse_remove_transient": bool,
529
+ "timelapse_mode": str,
530
+ "timelapse_objects": list,
531
+ "fps": int,
532
+ "remove_background": bool,
533
+ "lower_percentile": (int, float),
534
+ "merge_pathogens": bool,
535
+ "normalize_plots": bool,
536
+ "all_to_mip": bool,
537
+ "pick_slice": bool,
538
+ "skip_mode": str,
539
+ "save": bool,
540
+ "plot": bool,
541
+ "workers": int,
542
+ "verbose": bool,
543
+ "input_folder": str,
544
+ "cell_mask_dim": int,
545
+ "cell_min_size": int,
546
+ "cytoplasm_min_size": int,
547
+ "nucleus_mask_dim": int,
548
+ "nucleus_min_size": int,
549
+ "pathogen_mask_dim": int,
550
+ "pathogen_min_size": int,
551
+ "save_png": bool,
552
+ "crop_mode": list,
553
+ "use_bounding_box": bool,
554
+ "png_size": list, # This can be a list of lists
555
+ "normalize": bool,
556
+ "png_dims": list,
557
+ "normalize_by": str,
558
+ "save_measurements": bool,
559
+ "representative_images": bool,
560
+ "plot_filtration": bool,
561
+ "include_uninfected": bool,
562
+ "dialate_pngs": bool,
563
+ "dialate_png_ratios": list,
564
+ "max_workers": int,
565
+ "cells": list,
566
+ "cell_loc": list,
567
+ "pathogens": list,
568
+ "pathogen_loc": (list, list), # This can be a list of lists
569
+ "treatments": list,
570
+ "treatment_loc": (list, list), # This can be a list of lists
571
+ "channel_of_interest": int,
572
+ "compartments": list,
573
+ "measurement": str,
574
+ "nr_imgs": int,
575
+ "um_per_pixel": (int, float),
576
+ # Additional settings based on provided defaults
577
+ "include_noninfected": bool,
578
+ "include_multiinfected": bool,
579
+ "include_multinucleated": bool,
580
+ "filter_min_max": (list, type(None)),
581
+ "channel_dims": list,
582
+ "backgrounds": list,
583
+ "outline_thickness": int,
584
+ "outline_color": str,
585
+ "overlay_chans": list,
586
+ "overlay": bool,
587
+ "normalization_percentiles": list,
588
+ "print_object_number": bool,
589
+ "nr": int,
590
+ "figuresize": int,
591
+ "cmap": str,
592
+ "test_mode": bool,
593
+ "test_images": int,
594
+ "remove_background_cell": bool,
595
+ "remove_background_nucleus": bool,
596
+ "remove_background_pathogen": bool,
597
+ "pathogen_model": (str, type(None)),
598
+ "filter": bool,
599
+ "upscale": bool,
600
+ "upscale_factor": float,
601
+ "adjust_cells": bool,
602
+ "row_limit": int,
603
+ "tables": list,
604
+ "visualize": str,
605
+ "image_nr": int,
606
+ "dot_size": int,
607
+ "n_neighbors": int,
608
+ "min_dist": float,
609
+ "metric": str,
610
+ "eps": float,
611
+ "min_samples": int,
612
+ "filter_by": str,
613
+ "img_zoom": float,
614
+ "plot_by_cluster": bool,
615
+ "plot_cluster_grids": bool,
616
+ "remove_cluster_noise": bool,
617
+ "remove_highly_correlated": bool,
618
+ "log_data": bool,
619
+ "black_background": bool,
620
+ "remove_image_canvas": bool,
621
+ "plot_outlines": bool,
622
+ "plot_points": bool,
623
+ "smooth_lines": bool,
624
+ "clustering": str,
625
+ "exclude": (str, type(None)),
626
+ "col_to_compare": str,
627
+ "pos": str,
628
+ "neg": str,
629
+ "embedding_by_controls": bool,
630
+ "plot_images": bool,
631
+ "reduction_method": str,
632
+ "save_figure": bool,
633
+ "color_by": (str, type(None)),
634
+ "analyze_clusters": bool,
635
+ "resnet_features": bool,
636
+ "test_nr": int,
637
+ "radial_dist": bool,
638
+ "calculate_correlation": bool,
639
+ "manders_thresholds": list,
640
+ "homogeneity": bool,
641
+ "homogeneity_distances": list,
642
+ "save_arrays": bool,
643
+ "cytoplasm": bool,
644
+ "merge_edge_pathogen_cells": bool,
645
+ "cells_per_well": int,
646
+ "pathogen_size_range": list,
647
+ "nucleus_size_range": list,
648
+ "cell_size_range": list,
649
+ "pathogen_intensity_range": list,
650
+ "nucleus_intensity_range": list,
651
+ "cell_intensity_range": list,
652
+ "target_intensity_min": int,
653
+ "model_type": str,
654
+ "heatmap_feature": str,
655
+ "grouping": str,
656
+ "min_max": str,
657
+ "minimum_cell_count": int,
658
+ "n_estimators": int,
659
+ "test_size": float,
660
+ "location_column": str,
661
+ "positive_control": str,
662
+ "negative_control": str,
663
+ "n_repeats": int,
664
+ "top_features": int,
665
+ "remove_low_variance_features": bool,
666
+ "n_jobs": int,
667
+ "classes": list,
668
+ "schedule": str,
669
+ "loss_type": str,
670
+ "image_size": int,
671
+ "epochs": int,
672
+ "val_split": float,
673
+ "train_mode": str,
674
+ "learning_rate": float,
675
+ "weight_decay": float,
676
+ "dropout_rate": float,
677
+ "init_weights": bool,
678
+ "amsgrad": bool,
679
+ "use_checkpoint": bool,
680
+ "gradient_accumulation": bool,
681
+ "gradient_accumulation_steps": int,
682
+ "intermedeate_save": bool,
683
+ "pin_memory": bool,
684
+ "num_workers": int,
685
+ "augment": bool,
686
+ "target": str,
687
+ "cell_types": list,
688
+ "cell_plate_metadata": (list, type(None)),
689
+ "pathogen_types": list,
690
+ "pathogen_plate_metadata": (list, list), # This can be a list of lists
691
+ "treatment_plate_metadata": (list, list), # This can be a list of lists
692
+ "metadata_types": list,
693
+ "cell_chann_dim": int,
694
+ "nucleus_chann_dim": int,
695
+ "pathogen_chann_dim": int,
696
+ "plot_nr": int,
697
+ "plot_control": bool,
698
+ "remove_background": bool,
699
+ "target": str,
700
+ "upstream": str,
701
+ "downstream": str,
702
+ "barecode_length_1": int,
703
+ "barecode_length_2": int,
704
+ "chunk_size": int,
705
+ "grna": str,
706
+ "barcodes": str,
707
+ "plate_dict": dict,
708
+ "pc": str,
709
+ "pc_loc": str,
710
+ "nc": str,
711
+ "nc_loc": str,
712
+ "dependent_variable": str,
713
+ "transform": (str, type(None)),
714
+ "agg_type": str,
715
+ "min_cell_count": int,
716
+ "regression_type": str,
717
+ "remove_row_column_effect": bool,
718
+ "alpha": float,
719
+ "fraction_threshold": float,
720
+ "class_1_threshold": (float, type(None)),
721
+ "batch_size": int,
722
+ "CP_prob": float,
723
+ "flow_threshold": float,
724
+ "percentiles": (list, type(None)),
725
+ "circular": bool,
726
+ "invert": bool,
727
+ "diameter": int,
728
+ "grayscale": bool,
729
+ "resize": bool,
730
+ "target_height": (int, type(None)),
731
+ "target_width": (int, type(None)),
732
+ "rescale": bool,
733
+ "resample": bool,
734
+ "model_name": str,
735
+ "Signal_to_noise": int,
736
+ "learning_rate": float,
737
+ "weight_decay": float,
738
+ "batch_size": int,
739
+ "n_epochs": int,
740
+ "from_scratch": bool,
741
+ "width_height": list,
742
+ "resize": bool,
743
+ "gene_weights_csv": str,
744
+ "fraction_threshold": float,
745
+ }
746
+
747
+ for key, (label, widget, var) in vars_dict.items():
748
+ if key not in expected_types:
749
+ if key not in ["General","Nucleus","Cell","Pathogen","Timelapse","Plot","Object Image","Annotate Data","Measurements","Advanced","Miscellaneous","Test"]:
750
+
751
+ q.put(f"Key {key} not found in expected types.")
752
+ continue
753
+
754
+ value = var.get()
755
+ expected_type = expected_types.get(key, str)
756
+
757
+ try:
758
+ if key in ["png_size", "pathogen_plate_metadata", "treatment_plate_metadata"]:
759
+ parsed_value = ast.literal_eval(value) if value else None
760
+ if isinstance(parsed_value, list):
761
+ if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
762
+ settings[key] = parsed_value
763
+ else:
764
+ raise ValueError("Invalid format: Mixed list and list of lists")
765
+ else:
766
+ raise ValueError("Invalid format for list or list of lists")
767
+ elif expected_type == list:
768
+ settings[key] = parse_list(value) if value else None
769
+ elif expected_type == bool:
770
+ settings[key] = value if isinstance(value, bool) else value.lower() in ['true', '1', 't', 'y', 'yes']
771
+ elif expected_type == (int, type(None)):
772
+ settings[key] = int(value) if value else None
773
+ elif expected_type == (float, type(None)):
774
+ settings[key] = float(value) if value else None
775
+ elif expected_type == (int, float):
776
+ settings[key] = float(value) if '.' in value else int(value)
777
+ elif expected_type == (str, type(None)):
778
+ settings[key] = str(value) if value else None
779
+ elif isinstance(expected_type, tuple):
780
+ for typ in expected_type:
781
+ try:
782
+ settings[key] = typ(value) if value else None
783
+ break
784
+ except (ValueError, TypeError):
785
+ continue
786
+ else:
787
+ raise ValueError
788
+ else:
789
+ settings[key] = expected_type(value) if value else None
790
+ except (ValueError, SyntaxError):
791
+ expected_type_name = ' or '.join([t.__name__ for t in expected_type]) if isinstance(expected_type, tuple) else expected_type.__name__
792
+ q.put(f"Error: Invalid format for {key}. Expected type: {expected_type_name}.")
793
+ return
794
+
795
+ return settings
796
+
797
+ def generate_fields(variables, scrollable_frame):
798
+ from .gui_utils import create_input_field, spacrToolTip
799
+ row = 1
800
+ vars_dict = {}
801
+ tooltips = {
802
+ "src": "Path to the folder containing the images.",
803
+ "metadata_type": "Type of metadata to expect in the images. This will determine how the images are processed. If 'custom' is selected, you can provide a custom regex pattern to extract metadata from the image names.",
804
+ "custom_regex": "Custom regex pattern to extract metadata from the image names. This will only be used if 'custom' is selected for 'metadata_type'.",
805
+ "experiment": "Name of the experiment. This will be used to name the output files.",
806
+ "channels": "List of channels to use for the analysis. The first channel is 0, the second is 1, and so on. For example, [0,1,2] will use channels 0, 1, and 2.",
807
+ "magnification": "At what magnification the images were taken. This will be used to determine the size of the objects in the images.",
808
+ "nucleus_channel": "The channel to use for the nucleus. If None, the nucleus will not be segmented.",
809
+ "nucleus_background": "The background intensity for the nucleus channel. This will be used to remove background noise.",
810
+ "nucleus_Signal_to_noise": "The signal-to-noise ratio for the nucleus channel. This will be used to determine the range of intensities to normalize images to for nucleus segmentation.",
811
+ "nucleus_CP_prob": "The cellpose probability threshold for the nucleus channel. This will be used to segment the nucleus.",
812
+ "nucleus_FT": "The flow threshold for nucleus objects. This will be used in nuclues segmentation.",
813
+ "cell_channel": "The channel to use for the cell. If None, the cell will not be segmented.",
814
+ "cell_background": "The background intensity for the cell channel. This will be used to remove background noise.",
815
+ "cell_Signal_to_noise": "The signal-to-noise ratio for the cell channel. This will be used to determine the range of intensities to normalize images to for cell segmentation.",
816
+ "cell_CP_prob": "The cellpose probability threshold for the cell channel. This will be used in cell segmentation.",
817
+ "cell_FT": "The flow threshold for cell objects. This will be used to segment the cells.",
818
+ "pathogen_channel": "The channel to use for the pathogen. If None, the pathogen will not be segmented.",
819
+ "pathogen_background": "The background intensity for the pathogen channel. This will be used to remove background noise.",
820
+ "pathogen_Signal_to_noise": "The signal-to-noise ratio for the pathogen channel. This will be used to determine the range of intensities to normalize images to for pathogen segmentation.",
821
+ "pathogen_CP_prob": "The cellpose probability threshold for the pathogen channel. This will be used to segment the pathogen.",
822
+ "pathogen_FT": "The flow threshold for pathogen objects. This will be used in pathogen segmentation.",
823
+ "preprocess": "Whether to preprocess the images before segmentation. This includes background removal and normalization. Set to False only if this step has already been done.",
824
+ "masks": "Whether to generate masks for the segmented objects. If True, masks will be generated for the nucleus, cell, and pathogen.",
825
+ "examples_to_plot": "The number of images to plot for each segmented object. This will be used to visually inspect the segmentation results and normalization.",
826
+ "randomize": "Whether to randomize the order of the images before processing. Recommended to avoid bias in the segmentation.",
827
+ "batch_size": "The batch size to use for processing the images. This will determine how many images are processed at once. Images are normalized and segmented in batches. Lower if application runs out of RAM or VRAM.",
828
+ "timelapse": "Whether to process the images as a timelapse.",
829
+ "timelapse_displacement": "The displacement between frames in the timelapse. This will be used to align the frames before processing.",
830
+ "timelapse_memory": "The number of frames to in tandem objects must be present in to be considered the same object in the timelapse.",
831
+ "timelapse_frame_limits": "The frame limits to use for the timelapse. This will determine which frames are processed. For example, [5,20] will process frames 5 to 20.",
832
+ "timelapse_remove_transient": "Whether to remove transient objects in the timelapse. Transient objects are present in fewer than all frames.",
833
+ "timelapse_mode": "The mode to use for processing the timelapse. 'trackpy' uses the trackpy library for tracking objects, while 'btrack' uses the btrack library.",
834
+ "timelapse_objects": "The objects to track in the timelapse (cell, nucleus or pathogen). This will determine which objects are tracked over time. If None, all objects will be tracked.",
835
+ "fps": "Frames per second of the automatically generated timelapse movies.",
836
+ "remove_background": "Whether to remove background noise from the images. This will help improve the quality of the segmentation.",
837
+ "lower_percentile": "The lower quantile to use for normalizing the images. This will be used to determine the range of intensities to normalize images to.",
838
+ "merge_pathogens": "Whether to merge pathogen objects that share more than 75% of their perimeter.",
839
+ "normalize_plots": "Whether to normalize the plots.",
840
+ "all_to_mip": "Whether to convert all images to maximum intensity projections before processing.",
841
+ "pick_slice": "Whether to pick a single slice from the z-stack images. If False, the maximum intensity projection will be used.",
842
+ "skip_mode": "The mode to use for skipping images. This will determine how to handle images that cannot be processed.",
843
+ "save": "Whether to save the results to disk.",
844
+ "merge_edge_pathogen_cells": "Whether to merge cells that share pathogen objects.",
845
+ "plot": "Whether to plot the results.",
846
+ "workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
847
+ "verbose": "Whether to print verbose output during processing.",
848
+ "input_folder": "Path to the folder containing the images.",
849
+ "cell_mask_dim": "The dimension of the array the cell mask is saved in.",
850
+ "cell_min_size": "The minimum size of cell objects in pixels^2.",
851
+ "cytoplasm": "Whether to segment the cytoplasm (Cell - Nucleus + Pathogen).",
852
+ "cytoplasm_min_size": "The minimum size of cytoplasm objects in pixels^2.",
853
+ "nucleus_mask_dim": "The dimension of the array the nucleus mask is saved in.",
854
+ "nucleus_min_size": "The minimum size of nucleus objects in pixels^2.",
855
+ "pathogen_mask_dim": "The dimension of the array the pathogen mask is saved in.",
856
+ "pathogen_min_size": "The minimum size of pathogen objects in pixels^2.",
857
+ "save_png": "Whether to save the segmented objects as PNG images.",
858
+ "crop_mode": "The mode to use for cropping the images. This will determine which objects are cropped from the images (cell, nucleus, pathogen, cytoplasm).",
859
+ "use_bounding_box": "Whether to use the bounding box of the objects for cropping. If False, only the object itself will be cropped.",
860
+ "png_size": "The size of the PNG images to save. This will determine the size of the saved images.",
861
+ "normalize": "The percentiles to use for normalizing the images. This will be used to determine the range of intensities to normalize images to. If None, no normalization is done.",
862
+ "png_dims": "The dimensions of the PNG images to save. This will determine the dimensions of the saved images. Maximum of 3 dimensions e.g. [1,2,3].",
863
+ "normalize_by": "Whether to normalize the images by field of view (fov) or by PNG image (png).",
864
+ "save_measurements": "Whether to save the measurements to disk.",
865
+ "representative_images": "Whether to save representative images of the segmented objects (Not working yet).",
866
+ "plot_filtration": "Whether to plot the filtration steps.",
867
+ "include_uninfected": "Whether to include uninfected cells in the analysis.",
868
+ "dialate_pngs": "Whether to dilate the PNG images before saving.",
869
+ "dialate_png_ratios": "The ratios to use for dilating the PNG images. This will determine the amount of dilation applied to the images before cropping.",
870
+ "max_workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
871
+ "cells": "The cell types to include in the analysis.",
872
+ "cell_loc": "The locations of the cell types in the images.",
873
+ "pathogens": "The pathogen types to include in the analysis.",
874
+ "pathogen_loc": "The locations of the pathogen types in the images.",
875
+ "treatments": "The treatments to include in the analysis.",
876
+ "treatment_loc": "The locations of the treatments in the images.",
877
+ "channel_of_interest": "The channel of interest to use for the analysis.",
878
+ "compartments": "The compartments to measure in the images.",
879
+ "measurement": "The measurement to use for the analysis.",
880
+ "nr_imgs": "The number of images to plot.",
881
+ "um_per_pixel": "The micrometers per pixel for the images."
882
+ }
883
+
884
+ for key, (var_type, options, default_value) in variables.items():
885
+ label, widget, var = create_input_field(scrollable_frame.scrollable_frame, key, row, var_type, options, default_value)
886
+ vars_dict[key] = (label, widget, var) # Store the label, widget, and variable
887
+
888
+ # Add tooltip to the label if it exists in the tooltips dictionary
889
+ if key in tooltips:
890
+ spacrToolTip(label, tooltips[key])
891
+ row += 1
892
+ return vars_dict
893
+
894
+ categories = {
895
+ "General": ["src", "input_folder", "metadata_type", "custom_regex", "experiment", "channels", "magnification"],
896
+ "Nucleus": ["nucleus_channel", "nucleus_background", "nucleus_Signal_to_noise", "nucleus_CP_prob", "nucleus_FT", "remove_background_nucleus", "nucleus_min_size", "nucleus_mask_dim", "nucleus_loc"],
897
+ "Cell": ["cell_channel", "cell_background", "cell_Signal_to_noise", "cell_CP_prob", "cell_FT", "remove_background_cell", "cell_min_size", "cell_mask_dim", "cytoplasm", "cytoplasm_min_size", "include_uninfected", "merge_edge_pathogen_cells", "adjust_cells"],
898
+ "Pathogen": ["pathogen_channel", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogen_mask_dim"],
899
+ "Timelapse": ["timelapse", "fps", "timelapse_displacement", "timelapse_memory", "timelapse_frame_limits", "timelapse_remove_transient", "timelapse_mode", "timelapse_objects", "compartments"],
900
+ "Plot": ["plot_filtration", "examples_to_plot", "normalize_plots", "normalize", "cmap", "figuresize", "plot", "plot_cluster_grids", "img_zoom", "row_limit", "color_by", "plot_images", "smooth_lines", "plot_points", "plot_outlines", "black_background", "plot_by_cluster", "heatmap_feature","grouping","min_max","cmap","save_figure"],
901
+ "Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "dialate_png_ratios", "crop_mode", "dialate_pngs", "normalize", "use_bounding_box"],
902
+ "Annotate Data": ["positive_control","negative_control", "location_column", "treatment_loc", "cells", "cell_loc", "pathogens", "pathogen_loc", "channel_of_interest", "measurement", "treatments", "representative_images", "um_per_pixel", "nr_imgs", "exclude", "exclude_conditions", "mix", "pos", "neg"],
903
+ "Measurements": ["remove_image_canvas", "remove_highly_correlated", "homogeneity", "homogeneity_distances", "radial_dist", "calculate_correlation", "manders_thresholds", "save_measurements", "tables", "image_nr", "dot_size", "filter_by", "remove_highly_correlated_features", "remove_low_variance_features", "channel_of_interest"],
904
+ "Advanced": ["schedule", "test_size","exclude","n_repeats","top_features","n_jobs", "model_type","minimum_cell_count","n_estimators","preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "max_workers", "workers", "train_mode","amsgrad","use_checkpoint","gradient_accumulation","gradient_accumulation_steps","intermedeate_save","pin_memory","num_workers","channels","augment"],
905
+ "Clustering": ["eps","min_samples","analyze_clusters","clustering","remove_cluster_noise"],
906
+ "Embedding": ["visualize","n_neighbors","min_dist","metric","resnet_features","reduction_method","embedding_by_controls","col_to_compare","log_data"],
907
+ "Train DL Model": ["epochs", "loss_type", "optimizer_type","image_size","val_split","learning_rate","weight_decay","dropout_rate","init_weights", "train", "classes"],
908
+ "Miscellaneous": ["all_to_mip", "pick_slice", "skip_mode", "upscale", "upscale_factor"],
909
+ "Test": ["test_mode", "test_images", "random_test", "test_nr"],
910
+ "Sequencing": ["upstream", "downstream", "barecode_length_1", "barecode_length_2", "chunk_size", "test"]
911
+ }
spacr/utils.py CHANGED
@@ -73,6 +73,65 @@ from scipy import stats
73
73
 
74
74
  from .logger import log_function_call
75
75
 
76
+ import os
77
+ import signal
78
+ import psutil
79
+ import platform
80
+ from multiprocessing import set_start_method, get_start_method
81
+
82
+ def reset_mp():
83
+ current_method = get_start_method()
84
+ system = platform.system()
85
+
86
+ if system == 'Windows':
87
+ if current_method != 'spawn':
88
+ set_start_method('spawn', force=True)
89
+ elif system in ('Linux', 'Darwin'): # Darwin is macOS
90
+ if current_method != 'fork':
91
+ set_start_method('fork', force=True)
92
+
93
+ def is_multiprocessing_process(process):
94
+ """ Check if the process is a multiprocessing process. """
95
+ try:
96
+ for cmd in process.cmdline():
97
+ if 'multiprocessing' in cmd:
98
+ return True
99
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
100
+ pass
101
+ return False
102
+
103
+ def close_file_descriptors():
104
+ """ Close file descriptors and shared memory objects. """
105
+ import resource
106
+
107
+ soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
108
+ for fd in range(3, soft):
109
+ try:
110
+ os.close(fd)
111
+ except OSError:
112
+ pass
113
+
114
+ def close_multiprocessing_processes():
115
+ """ Close all multiprocessing processes. """
116
+ current_pid = os.getpid()
117
+ for proc in psutil.process_iter(['pid', 'cmdline']):
118
+ try:
119
+ # Skip the current process
120
+ if proc.info['pid'] == current_pid:
121
+ continue
122
+
123
+ # Check if the process is a multiprocessing process
124
+ if is_multiprocessing_process(proc):
125
+ proc.terminate()
126
+ proc.wait(timeout=5) # Wait up to 5 seconds for the process to terminate
127
+ print(f"Terminated process {proc.info['pid']}")
128
+
129
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e:
130
+ print(f"Failed to terminate process {proc.info['pid']}: {e}")
131
+
132
+ # Close file descriptors
133
+ close_file_descriptors()
134
+
76
135
  def check_mask_folder(src,mask_fldr):
77
136
 
78
137
  mask_folder = os.path.join(src,'norm_channel_stack',mask_fldr)
@@ -92,19 +151,14 @@ def check_mask_folder(src,mask_fldr):
92
151
 
93
152
  def smooth_hull_lines(cluster_data):
94
153
  hull = ConvexHull(cluster_data)
95
-
96
154
  # Extract vertices of the hull
97
155
  vertices = hull.points[hull.vertices]
98
-
99
156
  # Close the loop
100
157
  vertices = np.vstack([vertices, vertices[0, :]])
101
-
102
158
  # Parameterize the vertices
103
159
  tck, u = splprep(vertices.T, u=None, s=0.0)
104
-
105
160
  # Evaluate spline at new parameter values
106
161
  new_points = splev(np.linspace(0, 1, 100), tck)
107
-
108
162
  return new_points[0], new_points[1]
109
163
 
110
164
  def _gen_rgb_image(image, channels):