spacr 0.4.1__py3-none-any.whl → 0.4.3__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/settings.py CHANGED
@@ -86,7 +86,7 @@ def set_default_settings_preprocess_generate_masks(settings={}):
86
86
  settings.setdefault('fps', 2)
87
87
  settings.setdefault('timelapse_displacement', None)
88
88
  settings.setdefault('timelapse_memory', 3)
89
- settings.setdefault('timelapse_frame_limits', None)
89
+ settings.setdefault('timelapse_frame_limits', [5,])
90
90
  settings.setdefault('timelapse_remove_transient', False)
91
91
  settings.setdefault('timelapse_mode', 'trackpy')
92
92
  settings.setdefault('timelapse_objects', None)
@@ -256,7 +256,13 @@ def get_measure_crop_settings(settings={}):
256
256
  settings.setdefault('homogeneity', True)
257
257
  settings.setdefault('homogeneity_distances', [8,16,32])
258
258
 
259
- # Cropping settings
259
+ # Cropping settings # Cropping settings
260
+ settings.setdefault('save_arrays', False)
261
+ settings.setdefault('save_png',True)
262
+ settings.setdefault('use_bounding_box',False)
263
+ settings.setdefault('png_size',[224,224])
264
+ settings.setdefault('png_dims',[0,1,2])
265
+ settings.setdefault('normalize',False) # Cropping settings
260
266
  settings.setdefault('save_arrays', False)
261
267
  settings.setdefault('save_png',True)
262
268
  settings.setdefault('use_bounding_box',False)
@@ -277,9 +283,9 @@ def get_measure_crop_settings(settings={}):
277
283
  settings.setdefault('n_jobs', os.cpu_count()-2)
278
284
 
279
285
  # Object settings
280
- settings.setdefault('cell_mask_dim',None)
281
- settings.setdefault('nucleus_mask_dim',None)
282
- settings.setdefault('pathogen_mask_dim',None)
286
+ settings.setdefault('cell_mask_dim',4)
287
+ settings.setdefault('nucleus_mask_dim',5)
288
+ settings.setdefault('pathogen_mask_dim',6)
283
289
  settings.setdefault('cytoplasm',False)
284
290
  settings.setdefault('uninfected',True)
285
291
  settings.setdefault('cell_min_size',0)
@@ -473,7 +479,7 @@ def get_train_test_model_settings(settings):
473
479
  return settings
474
480
 
475
481
  def get_analyze_recruitment_default_settings(settings):
476
- settings.setdefault('src','path')
482
+ settings.setdefault('src', 'path')
477
483
  settings.setdefault('target','protein')
478
484
  settings.setdefault('cell_types',['HeLa'])
479
485
  settings.setdefault('cell_plate_metadata',None)
@@ -672,6 +678,7 @@ expected_types = {
672
678
  "timelapse_displacement": int,
673
679
  "timelapse_memory": int,
674
680
  "timelapse_frame_limits": (list, type(None)), # This can be a list of lists
681
+ #"timelapse_frame_limits": (list, type(None)), # This can be a list of lists
675
682
  "timelapse_remove_transient": bool,
676
683
  "timelapse_mode": str,
677
684
  "timelapse_objects": list,
@@ -944,13 +951,13 @@ expected_types = {
944
951
  }
945
952
 
946
953
  categories = {"Paths":[ "src", "grna", "barcodes", "custom_model_path", "dataset","model_path","grna_csv","row_csv","column_csv", "metadata_files", "score_data","count_data"],
947
- "General": ["metadata_type", "custom_regex", "experiment", "channels", "magnification", "channel_dims", "apply_model_to_dataset", "generate_training_dataset", "train_DL_model", "segmentation_mode", "delete_intermediate"],
954
+ "General": ["cell_mask_dim", "cytoplasm", "cell_chann_dim", "cell_channel", "nucleus_chann_dim", "nucleus_channel", "nucleus_mask_dim", "pathogen_mask_dim", "pathogen_chann_dim", "pathogen_channel", "test_mode", "plot", "metadata_type", "custom_regex", "experiment", "channels", "magnification", "channel_dims", "apply_model_to_dataset", "generate_training_dataset", "train_DL_model", "segmentation_mode", "delete_intermediate", "uninfected", ],
948
955
  "Cellpose":["fill_in","from_scratch", "n_epochs", "width_height", "model_name", "custom_model", "resample", "rescale", "CP_prob", "flow_threshold", "percentiles", "invert", "diameter", "grayscale", "Signal_to_noise", "resize", "target_height", "target_width"],
949
- "Cell": ["cell_diamiter","cell_intensity_range", "cell_size_range", "cell_chann_dim", "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", "uninfected", "merge_edge_pathogen_cells", "adjust_cells", "cells", "cell_loc"],
950
- "Nucleus": ["nucleus_diamiter","nucleus_intensity_range", "nucleus_size_range", "nucleus_chann_dim", "nucleus_channel", "nucleus_background", "nucleus_Signal_to_noise", "nucleus_CP_prob", "nucleus_FT", "remove_background_nucleus", "nucleus_min_size", "nucleus_mask_dim", "nucleus_loc"],
951
- "Pathogen": ["pathogen_diamiter","pathogen_intensity_range", "pathogen_size_range", "pathogen_chann_dim", "pathogen_channel", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogen_mask_dim", "pathogens", "pathogen_loc", "pathogen_types", "pathogen_plate_metadata", ],
956
+ "Cell": ["cell_diamiter","cell_intensity_range", "cell_size_range", "cell_background", "cell_Signal_to_noise", "cell_CP_prob", "cell_FT", "remove_background_cell", "cell_min_size", "cytoplasm_min_size", "adjust_cells", "cells", "cell_loc"],
957
+ "Nucleus": ["nucleus_diamiter","nucleus_intensity_range", "nucleus_size_range", "nucleus_background", "nucleus_Signal_to_noise", "nucleus_CP_prob", "nucleus_FT", "remove_background_nucleus", "nucleus_min_size", "nucleus_loc"],
958
+ "Pathogen": ["pathogen_diamiter","pathogen_intensity_range", "pathogen_size_range", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogens", "pathogen_loc", "pathogen_types", "pathogen_plate_metadata", ],
952
959
  "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"],
953
- "Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "crop_mode", "normalize", "use_bounding_box"],
960
+ "Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "crop_mode", "use_bounding_box"],
954
961
  "Sequencing": ["outlier_detection","offset_start","chunk_size","single_direction", "signal_direction","mode","comp_level","comp_type","save_h5","expected_end","offset","target_sequence","regex", "highlight"],
955
962
  "Generate Dataset":["save_to_db","file_metadata","class_metadata", "annotation_column","annotated_classes", "dataset_mode", "metadata_type_by","custom_measurement", "sample", "size"],
956
963
  "Hyperparamiters (Training)": ["png_type", "score_threshold","file_type", "train_channels", "epochs", "loss_type", "optimizer_type","image_size","val_split","learning_rate","weight_decay","dropout_rate", "init_weights", "train", "classes", "augment", "amsgrad","use_checkpoint","gradient_accumulation","gradient_accumulation_steps","intermedeate_save","pin_memory"],
@@ -959,11 +966,10 @@ categories = {"Paths":[ "src", "grna", "barcodes", "custom_model_path", "dataset
959
966
  "Hyperparamiters (Regression)":["cross_validation","prune_features","reg_lambda","reg_alpha","cov_type", "class_1_threshold", "plate", "other", "fraction_threshold", "alpha", "random_row_column_effects", "regression_type", "min_cell_count", "agg_type", "transform", "dependent_variable"],
960
967
  "Hyperparamiters (Activation)":["cam_type", "overlay", "correlation", "target_layer", "normalize_input"],
961
968
  "Annotation": ["filter_column", "filter_value","volcano", "toxo", "controls", "nc_loc", "pc_loc", "nc", "pc", "cell_plate_metadata","treatment_plate_metadata", "metadata_types", "cell_types", "target","positive_control","negative_control", "location_column", "treatment_loc", "channel_of_interest", "measurement", "treatments", "um_per_pixel", "nr_imgs", "exclude", "exclude_conditions", "mix", "pos", "neg"],
962
- "Plot": ["plot", "split_axis_lims", "x_lim","log_x","log_y", "plot_control", "plot_nr", "examples_to_plot", "normalize_plots", "cmap", "figuresize", "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"],
963
- "Test": ["test_mode", "test_images", "random_test", "test_nr", "test", "test_split"],
969
+ "Plot": ["split_axis_lims", "x_lim","log_x","log_y", "plot_control", "plot_nr", "examples_to_plot", "normalize_plots", "cmap", "figuresize", "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"],
964
970
  "Timelapse": ["timelapse", "fps", "timelapse_displacement", "timelapse_memory", "timelapse_frame_limits", "timelapse_remove_transient", "timelapse_mode", "timelapse_objects", "compartments"],
965
- "Advanced": ["target_unique_count","threshold_multiplier", "threshold_method", "min_n","shuffle", "target_intensity_min", "cells_per_well", "nuclei_limit", "pathogen_limit", "background", "backgrounds", "schedule", "test_size","exclude","n_repeats","top_features", "model_type_ml", "model_type","minimum_cell_count","n_estimators","preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "n_jobs"],
966
- "Miscellaneous": ["all_to_mip", "pick_slice", "skip_mode", "upscale", "upscale_factor"]
971
+ "Advanced": ["merge_edge_pathogen_cells", "test_images", "random_test", "test_nr", "test", "test_split", "normalize", "target_unique_count","threshold_multiplier", "threshold_method", "min_n","shuffle", "target_intensity_min", "cells_per_well", "nuclei_limit", "pathogen_limit", "background", "backgrounds", "schedule", "test_size","exclude","n_repeats","top_features", "model_type_ml", "model_type","minimum_cell_count","n_estimators","preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "n_jobs"],
972
+ "Beta": ["all_to_mip", "pick_slice", "skip_mode", "upscale", "upscale_factor"]
967
973
  }
968
974
 
969
975
 
@@ -972,6 +978,127 @@ category_keys = list(categories.keys())
972
978
  def check_settings(vars_dict, expected_types, q=None):
973
979
  from .gui_utils import parse_list
974
980
 
981
+ if q is None:
982
+ from multiprocessing import Queue
983
+ q = Queue()
984
+
985
+ settings = {}
986
+ errors = [] # Collect errors instead of stopping at the first one
987
+
988
+ for key, (label, widget, var, _) in vars_dict.items():
989
+ if key not in expected_types and key not in category_keys:
990
+ errors.append(f"Warning: Key '{key}' not found in expected types.")
991
+ continue
992
+
993
+ value = var.get()
994
+ if value in ['None', '']:
995
+ value = None
996
+
997
+ expected_type = expected_types.get(key, str)
998
+
999
+ try:
1000
+ if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "png_dims", "pathogen_plate_metadata", "treatment_plate_metadata", "class_metadata", "crop_mode"]:
1001
+ if value is None:
1002
+ parsed_value = None
1003
+ else:
1004
+ try:
1005
+ parsed_value = ast.literal_eval(value)
1006
+ except (ValueError, SyntaxError):
1007
+ raise ValueError(f"Expected a list or list of lists but got an invalid format: {value}")
1008
+
1009
+ if isinstance(parsed_value, list):
1010
+ if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
1011
+ settings[key] = parsed_value
1012
+ else:
1013
+ raise ValueError(f"Invalid format: '{key}' contains mixed types (single values and lists).")
1014
+
1015
+ else:
1016
+ raise ValueError(f"Expected a list for '{key}', but got {type(parsed_value).__name__}.")
1017
+
1018
+ elif expected_type == list:
1019
+ settings[key] = parse_list(value) if value else None
1020
+
1021
+ if isinstance(settings[key], list) and len(settings[key]) == 1:
1022
+ settings[key] = settings[key][0]
1023
+
1024
+ elif expected_type == bool:
1025
+ settings[key] = value.lower() in ['true', '1', 't', 'y', 'yes'] if isinstance(value, str) else bool(value)
1026
+
1027
+ elif expected_type == (int, type(None)):
1028
+ if value is None or str(value).isdigit():
1029
+ settings[key] = int(value) if value is not None else None
1030
+ else:
1031
+ raise ValueError(f"Expected an integer or None for '{key}', but got '{value}'.")
1032
+
1033
+ elif expected_type == (float, type(None)):
1034
+ if value is None or (isinstance(value, str) and value.replace(".", "", 1).isdigit()):
1035
+ settings[key] = float(value) if value is not None else None
1036
+ else:
1037
+ raise ValueError(f"Expected a float or None for '{key}', but got '{value}'.")
1038
+
1039
+ elif expected_type == (int, float):
1040
+ try:
1041
+ settings[key] = float(value) if '.' in str(value) else int(value)
1042
+ except ValueError:
1043
+ raise ValueError(f"Expected an integer or float for '{key}', but got '{value}'.")
1044
+
1045
+ elif expected_type == (str, type(None)):
1046
+ settings[key] = str(value) if value is not None else None
1047
+
1048
+ elif expected_type == (str, type(None), list):
1049
+ if isinstance(value, list):
1050
+ settings[key] = parse_list(value) if value else None
1051
+ elif isinstance(value, str):
1052
+ settings[key] = str(value)
1053
+ else:
1054
+ settings[key] = None
1055
+
1056
+ elif expected_type == dict:
1057
+ try:
1058
+ if isinstance(value, str):
1059
+ parsed_dict = ast.literal_eval(value)
1060
+ else:
1061
+ raise ValueError("Expected a string representation of a dictionary.")
1062
+
1063
+ if not isinstance(parsed_dict, dict):
1064
+ raise ValueError(f"Expected a dictionary for '{key}', but got {type(parsed_dict).__name__}.")
1065
+
1066
+ settings[key] = parsed_dict
1067
+ except (ValueError, SyntaxError) as e:
1068
+ settings[key] = {}
1069
+ errors.append(f"Error: Invalid dictionary format for '{key}'. Expected type: dict. Error: {e}")
1070
+
1071
+ elif isinstance(expected_type, tuple):
1072
+ for typ in expected_type:
1073
+ try:
1074
+ settings[key] = typ(value) if value else None
1075
+ break
1076
+ except (ValueError, TypeError):
1077
+ continue
1078
+ else:
1079
+ raise ValueError(f"Value '{value}' for '{key}' does not match any expected types: {expected_type}.")
1080
+
1081
+ else:
1082
+ try:
1083
+ settings[key] = expected_type(value) if value else None
1084
+ except (ValueError, TypeError):
1085
+ raise ValueError(f"Expected type {expected_type.__name__} for '{key}', but got '{value}'.")
1086
+
1087
+ except (ValueError, SyntaxError) as e:
1088
+ expected_type_name = ' or '.join([t.__name__ for t in expected_type]) if isinstance(expected_type, tuple) else expected_type.__name__
1089
+ errors.append(f"Error: '{key}' has invalid format. Expected type: {expected_type_name}. Got value: '{value}'. Error: {e}")
1090
+
1091
+ # Send all collected errors to the queue
1092
+ for error in errors:
1093
+ q.put(error)
1094
+
1095
+
1096
+
1097
+ return settings, errors
1098
+
1099
+ def check_settings_v1(vars_dict, expected_types, q=None):
1100
+ from .gui_utils import parse_list
1101
+
975
1102
  if q is None:
976
1103
  from multiprocessing import Queue
977
1104
  q = Queue()
@@ -984,22 +1111,26 @@ def check_settings(vars_dict, expected_types, q=None):
984
1111
  q.put(f"Key {key} not found in expected types.")
985
1112
  continue
986
1113
 
987
- value = var.get()
988
- if value == 'None':
1114
+ value = var.get()
1115
+ if value in ['None', '']:
989
1116
  value = None
990
1117
 
991
1118
  expected_type = expected_types.get(key, str)
992
1119
 
993
1120
  try:
994
- if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "pathogen_loc", "treatment_loc", "pathogen_plate_metadata", "treatment_plate_metadata", "barcode_coordinates", "class_metadata"]:
995
- parsed_value = ast.literal_eval(value) if value else None
1121
+ #if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "pathogen_loc", "treatment_loc", "pathogen_plate_metadata", "treatment_plate_metadata", "barcode_coordinates", "class_metadata"]:
1122
+ if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "png_dims", "pathogen_plate_metadata", "treatment_plate_metadata", "class_metadata", "crop_mode"]:
1123
+
1124
+ if value is None:
1125
+ parsed_value = None
1126
+ else:
1127
+ parsed_value = ast.literal_eval(value) if isinstance(value, str) and value.strip() else None
1128
+
996
1129
  if isinstance(parsed_value, list):
997
1130
  if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
998
1131
  settings[key] = parsed_value
999
1132
  else:
1000
1133
  raise ValueError("Invalid format: Mixed list and list of lists")
1001
- #elif parsed_value == None:
1002
- # settings[key] = None
1003
1134
  else:
1004
1135
  raise ValueError("Invalid format for list or list of lists")
1005
1136
 
@@ -1180,30 +1311,7 @@ def generate_fields(variables, scrollable_frame):
1180
1311
  "n_epochs": "(int) - Number of epochs for training the Cellpose model.",
1181
1312
  "n_jobs": "(int) - The number of n_jobs to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
1182
1313
  "n_neighbors": "(int) - Number of neighbors for UMAP.",
1183
- "n_repeats": "(int) - Number of repeats for cross-validation.",
1184
- "normalize": "(list) - 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.",
1185
- "normalize_by": "(str) - Whether to normalize the images by field of view (fov) or by PNG image (png).",
1186
- "normalize_plots": "(bool) - Whether to normalize the plots.",
1187
- "nr_imgs": "(int) - The number of images to plot.",
1188
- "nucleus_CP_prob": "(float) - The cellpose probability threshold for the nucleus channel. This will be used to segment the nucleus.",
1189
- "nucleus_FT": "(float) - The flow threshold for nucleus objects. This will be used in nucleus segmentation.",
1190
- "nucleus_background": "(float) - The background intensity for the nucleus channel. This will be used to remove background noise.",
1191
- "nucleus_chann_dim": "(int) - Dimension of the channel to use for nucleus segmentation.",
1192
- "nucleus_channel": "(int) - The channel to use for the nucleus. If None, the nucleus will not be segmented.",
1193
- "nucleus_intensity_range": "(list) - Intensity range for nucleus segmentation.",
1194
- "nucleus_loc": "(str) - Location of the nucleus in the images.",
1195
- "nucleus_mask_dim": "(int) - The dimension of the array the nucleus mask is saved in.",
1196
- "nucleus_min_size": "(int) - The minimum size of nucleus objects in pixels^2.",
1197
- "nucleus_Signal_to_noise": "(float) - 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.",
1198
- "nucleus_size_range": "(list) - Size range for nucleus segmentation.",
1199
- "optimizer_type": "(str) - Type of optimizer to use.",
1200
- "other": "(dict) - Additional parameters for the regression analysis.",
1201
- "pathogen_CP_prob": "(float) - The cellpose probability threshold for the pathogen channel. This will be used to segment the pathogen.",
1202
- "pathogen_FT": "(float) - The flow threshold for pathogen objects. This will be used in pathogen segmentation.",
1203
- "pathogen_background": "(float) - The background intensity for the pathogen channel. This will be used to remove background noise.",
1204
- "pathogen_chann_dim": "(int) - Dimension of the channel to use for pathogen segmentation.",
1205
- "pathogen_channel": "(int) - The channel to use for the pathogen. If None, the pathogen will not be segmented.",
1206
- "pathogen_intensity_range": "(str) - Metadata for the pathogen plate.",
1314
+ "n_repeats": "(int) - Number of repeats for the pathogen plate.",
1207
1315
  "pathogen_Signal_to_noise": "(float) - 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.",
1208
1316
  "pathogen_size_range": "(list) - Size range for pathogen segmentation.",
1209
1317
  "pathogen_types": "(list) - Types of pathogens to include in the analysis.",
@@ -1222,7 +1330,7 @@ def generate_fields(variables, scrollable_frame):
1222
1330
  "plot_nr": "(int) - Number of plots to generate.",
1223
1331
  "plot_outlines": "(bool) - Whether to plot outlines of segmented objects.",
1224
1332
  "png_dims": "(list) - 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].",
1225
- "png_size": "(int) - The size of the PNG images to save. This will determine the size of the saved images.",
1333
+ "png_size": "(list) - The size of the PNG images to save. This will determine the size of the saved images.",
1226
1334
  "positive_control": "(str) - Identifier for the positive control.",
1227
1335
  "preprocess": "(bool) - Whether to preprocess the images before segmentation. This includes background removal and normalization. Set to False only if this step has already been done.",
1228
1336
  "radial_dist": "(list) - Radial distances for measuring features.",
@@ -1385,8 +1493,8 @@ def set_annotate_default_settings(settings):
1385
1493
  settings.setdefault('normalize', 'False')
1386
1494
  settings.setdefault('normalize_channels', "r,g,b")
1387
1495
  settings.setdefault('percentiles', [2, 98])
1388
- settings.setdefault('measurement', '')#'cytoplasm_channel_3_mean_intensity,pathogen_channel_3_mean_intensity')
1389
- settings.setdefault('threshold', '')#'2')
1496
+ settings.setdefault('measurement', '') #'cytoplasm_channel_3_mean_intensity,pathogen_channel_3_mean_intensity')
1497
+ settings.setdefault('threshold', '') #'2')
1390
1498
  return settings
1391
1499
 
1392
1500
  def set_default_generate_barecode_mapping(settings={}):
spacr/submodules.py CHANGED
@@ -21,7 +21,7 @@ from sklearn.metrics import mean_absolute_error
21
21
  import matplotlib.pyplot as plt
22
22
  from natsort import natsorted
23
23
 
24
- def analyze_recruitment(settings={}):
24
+ def analyze_recruitment(settings):
25
25
  """
26
26
  Analyze recruitment data by grouping the DataFrame by well coordinates and plotting controls and recruitment data.
27
27
 
spacr/utils.py CHANGED
@@ -319,7 +319,6 @@ def load_settings(csv_file_path, show=False, setting_key='setting_key', setting_
319
319
 
320
320
  return result_dict
321
321
 
322
-
323
322
  def save_settings(settings, name='settings', show=False):
324
323
 
325
324
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
@@ -328,14 +327,19 @@ def save_settings(settings, name='settings', show=False):
328
327
 
329
328
  if isinstance(settings['src'], list):
330
329
  src = settings['src'][0]
331
- #if os.path.exists(src):
332
-
333
330
  name = f"{name}_list"
334
331
  else:
335
332
  src = settings['src']
333
+
334
+ if 'test_mode' in settings.keys():
335
+ settings['test_mode'] = False
336
+
337
+ if 'plot' in settings.keys():
338
+ settings['plot'] = False
336
339
 
337
340
  settings_csv = os.path.join(src,'settings',f'{name}.csv')
338
341
  os.makedirs(os.path.join(src,'settings'), exist_ok=True)
342
+ print(f"Saving settings to {settings_csv}")
339
343
  settings_df.to_csv(settings_csv, index=False)
340
344
 
341
345
  def print_progress(files_processed, files_to_process, n_jobs, time_ls=None, batch_size=None, operation_type=""):
@@ -1145,7 +1149,7 @@ def _masks_to_masks_stack(masks):
1145
1149
  mask_stack.append(mask)
1146
1150
  return mask_stack
1147
1151
 
1148
- def _get_diam(mag, obj):
1152
+ def _get_diam_v1(mag, obj):
1149
1153
 
1150
1154
  if mag == 20:
1151
1155
  if obj == 'cell':
@@ -1176,11 +1180,28 @@ def _get_diam(mag, obj):
1176
1180
  diamiter = 60
1177
1181
  else:
1178
1182
  raise ValueError("Invalid magnification: Use 20, 40 or 60")
1183
+
1179
1184
  else:
1180
1185
  raise ValueError("Invalid magnification: Use 20, 40 or 60")
1181
1186
 
1182
1187
  return diamiter
1183
1188
 
1189
+ def _get_diam(mag, obj):
1190
+
1191
+ if obj == 'cell':
1192
+ diamiter = 2 * mag + 80
1193
+
1194
+ if obj == 'cell_large':
1195
+ diamiter = 2 * mag + 120
1196
+
1197
+ if obj == 'nucleus':
1198
+ diamiter = 0.75 * mag + 45
1199
+
1200
+ if obj == 'pathogen':
1201
+ diamiter = mag
1202
+
1203
+ return int(diamiter)
1204
+
1184
1205
  def _get_object_settings(object_type, settings):
1185
1206
  object_settings = {}
1186
1207
 
@@ -1333,20 +1354,25 @@ def annotate_conditions(df, cells=None, cell_loc=None, pathogens=None, pathogen_
1333
1354
  def _map_or_default(column_name, values, loc, df):
1334
1355
  """
1335
1356
  Consolidates the logic for mapping values or assigning defaults when loc is None.
1336
-
1357
+
1337
1358
  Args:
1338
1359
  column_name (str): The column in the DataFrame to annotate.
1339
1360
  values (list/str): The list of values or a single string to annotate.
1340
1361
  loc (list of lists): Location mapping for the values, or None if not used.
1341
1362
  df (pandas.DataFrame): The DataFrame to modify.
1342
1363
  """
1343
- if isinstance(values, str) or (isinstance(values, list) and loc is None):
1344
- # Assign all rows the first value in the list or the single string
1345
- df[column_name] = values if isinstance(values, str) else values[0]
1364
+ if isinstance(values, str) and loc is None:
1365
+ # If a single string is provided and loc is None, assign the value to all rows
1366
+ df[column_name] = values
1367
+
1368
+ elif isinstance(values, list) and loc is None:
1369
+ # If a list of values is provided but no loc, assign the first value to all rows
1370
+ df[column_name] = values[0]
1371
+
1346
1372
  elif values is not None and loc is not None:
1347
- # Perform the location-based mapping
1373
+ # Perform location-based mapping
1348
1374
  value_dict = {val: key for key, loc_list in zip(values, loc) for val in loc_list}
1349
- df[column_name] = np.nan
1375
+ df[column_name] = np.nan # Start with NaN
1350
1376
  for val, key in value_dict.items():
1351
1377
  loc_type = _get_type(val)
1352
1378
  if loc_type:
@@ -3099,12 +3125,8 @@ def _get_regex(metadata_type, img_format, custom_regex=None):
3099
3125
  regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>..)A(?P<AID>..)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
3100
3126
  elif metadata_type == 'cq1':
3101
3127
  regex = f'W(?P<wellID>.*)F(?P<fieldID>.*)T(?P<timeID>.*)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
3102
- elif metadata_type == 'nikon':
3103
- regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>..)A(?P<AID>..)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
3104
- elif metadata_type == 'zeis':
3105
- regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>..)A(?P<AID>..)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
3106
- elif metadata_type == 'leica':
3107
- regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>..)A(?P<AID>..)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
3128
+ elif metadata_type == 'auto':
3129
+ regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>.*)C(?P<chanID>.*).tif'
3108
3130
  elif metadata_type == 'custom':
3109
3131
  regex = f'({custom_regex}){img_format}'
3110
3132
 
@@ -4945,7 +4967,7 @@ def download_models(repo_id="einarolafsson/models", retries=5, delay=5):
4945
4967
  if not os.path.exists(local_dir):
4946
4968
  os.makedirs(local_dir)
4947
4969
  elif len(os.listdir(local_dir)) > 0:
4948
- print(f"Models already downloaded to: {local_dir}")
4970
+ #print(f"Models already downloaded to: {local_dir}")
4949
4971
  return local_dir
4950
4972
 
4951
4973
  attempt = 0
@@ -5337,3 +5359,63 @@ def calculate_shortest_distance(df, object1, object2):
5337
5359
  df[f'{object1}_{object2}_shortest_distance'] = np.maximum(shortest_distance, 0)
5338
5360
 
5339
5361
  return df
5362
+
5363
+ def format_path_for_system(path):
5364
+ """
5365
+ Takes a file path and reformats it to be compatible with the current operating system.
5366
+
5367
+ Args:
5368
+ path (str): The file path to be formatted.
5369
+
5370
+ Returns:
5371
+ str: The formatted path for the current operating system.
5372
+ """
5373
+ system = platform.system()
5374
+
5375
+ # Convert Windows-style paths to Unix-style (Linux/macOS)
5376
+ if system in ["Linux", "Darwin"]: # Darwin is macOS
5377
+ formatted_path = path.replace("\\", "/")
5378
+
5379
+ # Convert Unix-style paths to Windows-style
5380
+ elif system == "Windows":
5381
+ formatted_path = path.replace("/", "\\")
5382
+
5383
+ else:
5384
+ raise ValueError(f"Unsupported OS: {system}")
5385
+
5386
+ # Normalize path to ensure consistency
5387
+ new_path = os.path.normpath(formatted_path)
5388
+ if os.path.exists(new_path):
5389
+ print(f"Found path: {new_path}")
5390
+ else:
5391
+ print(f"Path not found: {new_path}")
5392
+
5393
+ return new_path
5394
+
5395
+
5396
+ def normalize_src_path(src):
5397
+ """
5398
+ Ensures that the 'src' value is properly formatted as either a list of strings or a single string.
5399
+
5400
+ Args:
5401
+ src (str or list): The input source path(s).
5402
+
5403
+ Returns:
5404
+ list or str: A correctly formatted list if the input was a list (or string representation of a list),
5405
+ otherwise a single string.
5406
+ """
5407
+ if isinstance(src, list):
5408
+ return src # Already a list, return as-is
5409
+
5410
+ if isinstance(src, str):
5411
+ try:
5412
+ # Check if it is a string representation of a list
5413
+ evaluated_src = ast.literal_eval(src)
5414
+ if isinstance(evaluated_src, list) and all(isinstance(item, str) for item in evaluated_src):
5415
+ return evaluated_src # Convert to real list
5416
+ except (SyntaxError, ValueError):
5417
+ pass # Not a valid list, treat as a string
5418
+
5419
+ return src # Return as a string if not a list
5420
+
5421
+ raise ValueError(f"Invalid type for 'src': {type(src).__name__}, expected str or list")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -41,6 +41,7 @@ Requires-Dist: pillow<11.0,>=10.2.0
41
41
  Requires-Dist: tifffile>=2023.4.12
42
42
  Requires-Dist: nd2reader<4.0,>=3.3.0
43
43
  Requires-Dist: czifile
44
+ Requires-Dist: readlif
44
45
  Requires-Dist: imageio<3.0,>=2.34.0
45
46
  Requires-Dist: pingouin<1.0,>=0.5.5
46
47
  Requires-Dist: umap-learn<1.0,>=0.5.6
@@ -9,28 +9,28 @@ spacr/app_sequencing.py,sha256=DjG26jy4cpddnV8WOOAIiExtOe9MleVMY4MFa5uTo5w,157
9
9
  spacr/app_umap.py,sha256=ZWAmf_OsIKbYvolYuWPMYhdlVe-n2CADoJulAizMiEo,153
10
10
  spacr/cellpose.py,sha256=RBHMs2vwXcfkj0xqAULpALyzJYXddSRycgZSzmwI7v0,14755
11
11
  spacr/chat_bot.py,sha256=n3Fhqg3qofVXHmh3H9sUcmfYy9MmgRnr48663MVdY9E,1244
12
- spacr/core.py,sha256=lKeqmsVrGQ8cPU_WkoNGNBWrk-gtR1RkRkwDdnJ0u64,48829
12
+ spacr/core.py,sha256=2VPM-nemOoYI3dbDudsvFGg4F_y2OlCcXKos5vWNKeU,50422
13
13
  spacr/deep_spacr.py,sha256=WN64EaQqF87JZg3Uan46t5Y28xsAGD2KMjr2ht6CyDs,54563
14
14
  spacr/gui.py,sha256=ARyn9Q_g8HoP-cXh1nzMLVFCKqthY4v2u9yORyaQqQE,8230
15
- spacr/gui_core.py,sha256=U0A7waKgWq_Es9fMwcZbXUZYGzCqt2bgfY3HbxiFXnw,47466
16
- spacr/gui_elements.py,sha256=HmITDncklKwtdFhxLhtYXOwndsRfgwWIPVi83VlXHB4,146419
17
- spacr/gui_utils.py,sha256=0rDF23BUGcmjSJvfCiLoxhlGJdHkio1jTxyCzrMXr-g,41211
18
- spacr/io.py,sha256=oqJwDJWksVdWE0bRAwytTOsjlL0o-J9lr_pQaw2cQ4Y,138288
15
+ spacr/gui_core.py,sha256=qMHQPY7LZEKQ5c7dIQtRyVBy03xNmwYWO8S85btVeEw,60170
16
+ spacr/gui_elements.py,sha256=Or38I_X9-lJRsyIAtbEfZPXzls0IORagf7vEe6IlI5Y,152647
17
+ spacr/gui_utils.py,sha256=dWVPFwDj793Z3ERG4mMC0hI0MKkOrvXJpUYlcjpCBsU,41357
18
+ spacr/io.py,sha256=R_cIPJYzzd1clpQ3mpYOXaSLPYsaU7apx1739RC27Dw,147018
19
19
  spacr/logger.py,sha256=lJhTqt-_wfAunCPl93xE65Wr9Y1oIHJWaZMjunHUeIw,1538
20
- spacr/measure.py,sha256=jmOnLBudq3TuY723Cfo1EJBn67P6rlEvL6I-2FSkUgI,55315
20
+ spacr/measure.py,sha256=Z3u4BU5RzcY82IZuboQ0OsxuXaPVwOlH65Rw6FrL5z4,55045
21
21
  spacr/mediar.py,sha256=FwLvbLQW5LQzPgvJZG8Lw7GniA2vbZx6Jv6vIKu7I5c,14743
22
22
  spacr/ml.py,sha256=MrIAtUUxMOibWVL1SjCUnYlizawCp3l3SeY4Y9yEsPw,97251
23
23
  spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
24
24
  spacr/plot.py,sha256=Q5TbsR2NUWhA7z4HyF_2_FAEBFSNMU-G3UNDbRzW6mM,169485
25
25
  spacr/sequencing.py,sha256=ClUfwPPK6rNUbUuiEkzcwakzVyDKKUMv9ricrxT8qQY,25227
26
- spacr/settings.py,sha256=fEk-9LSSvV1wGsn6xTaJWY7wF7_u8Fc-S1DaDHqZU3I,83997
26
+ spacr/settings.py,sha256=72bhOwr0U83uoWwaKtSNZucYhpgwTP1_GItRk9f6A6c,87592
27
27
  spacr/sim.py,sha256=1xKhXimNU3ukzIw-3l9cF3Znc_brW8h20yv8fSTzvss,71173
28
28
  spacr/sp_stats.py,sha256=mbhwsyIqt5upsSD346qGjdCw7CFBa0tIS7zHU9e0jNI,9536
29
29
  spacr/stats.py,sha256=mbhwsyIqt5upsSD346qGjdCw7CFBa0tIS7zHU9e0jNI,9536
30
- spacr/submodules.py,sha256=mb2g0igUTws7y6xW1zIJw1E7eQyxsjEj5mk2Z-Qd8uw,67629
30
+ spacr/submodules.py,sha256=jFlJeVNuIEf63TtCOpTlOZ4iiSLr238kRBiGAAAgKE4,67626
31
31
  spacr/timelapse.py,sha256=KGfG4L4-QnFfgbF7L6C5wL_3gd_rqr05Foje6RsoTBg,39603
32
32
  spacr/toxo.py,sha256=TmuhejSIPLBvsgeblsUgSvBFCR1gOkApyTKidooJ5Us,26044
33
- spacr/utils.py,sha256=of2t5Tq_RKdJ1QRDo4nJ3oEVev_6s2Oko3-lBxl4ScU,226293
33
+ spacr/utils.py,sha256=v13vzgEQwUnQAtQya_akKIkem26-0Nn5j9uhx_NcJXQ,228599
34
34
  spacr/version.py,sha256=axH5tnGwtgSnJHb5IDhiu4Zjk5GhLyAEDRe-rnaoFOA,409
35
35
  spacr/resources/MEDIAR/.gitignore,sha256=Ff1q9Nme14JUd-4Q3jZ65aeQ5X4uttptssVDgBVHYo8,152
36
36
  spacr/resources/MEDIAR/LICENSE,sha256=yEj_TRDLUfDpHDNM0StALXIt6mLqSgaV2hcCwa6_TcY,1065
@@ -153,9 +153,9 @@ spacr/resources/icons/umap.png,sha256=dOLF3DeLYy9k0nkUybiZMe1wzHQwLJFRmgccppw-8b
153
153
  spacr/resources/images/plate1_E01_T0001F001L01A01Z01C02.tif,sha256=Tl0ZUfZ_AYAbu0up_nO0tPRtF1BxXhWQ3T3pURBCCRo,7958528
154
154
  spacr/resources/images/plate1_E01_T0001F001L01A02Z01C01.tif,sha256=m8N-V71rA1TT4dFlENNg8s0Q0YEXXs8slIn7yObmZJQ,7958528
155
155
  spacr/resources/images/plate1_E01_T0001F001L01A03Z01C03.tif,sha256=Pbhk7xn-KUP6RSIhJsxQcrHFImBm3GEpLkzx7WOc-5M,7958528
156
- spacr-0.4.1.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
157
- spacr-0.4.1.dist-info/METADATA,sha256=ibaavHM7Kuo-dVo-SkOgjKfL0D8mG8ouYCHEzTqsbwg,6072
158
- spacr-0.4.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
159
- spacr-0.4.1.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
160
- spacr-0.4.1.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
161
- spacr-0.4.1.dist-info/RECORD,,
156
+ spacr-0.4.3.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
157
+ spacr-0.4.3.dist-info/METADATA,sha256=IAsmOAnJO0zAZ1eMw6KDX2IjKA5w4poSFur3-7GDcMs,6095
158
+ spacr-0.4.3.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
159
+ spacr-0.4.3.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
160
+ spacr-0.4.3.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
161
+ spacr-0.4.3.dist-info/RECORD,,
File without changes
File without changes