spacr 0.4.12__py3-none-any.whl → 0.4.60__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/core.py +54 -8
- spacr/deep_spacr.py +2 -3
- spacr/gui_core.py +259 -75
- spacr/gui_elements.py +133 -2
- spacr/gui_utils.py +24 -20
- spacr/io.py +553 -61
- spacr/measure.py +11 -12
- spacr/ml.py +141 -258
- spacr/plot.py +76 -34
- spacr/sequencing.py +73 -38
- spacr/settings.py +160 -93
- spacr/submodules.py +620 -214
- spacr/timelapse.py +25 -25
- spacr/toxo.py +23 -23
- spacr/utils.py +249 -95
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/METADATA +2 -1
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/RECORD +21 -21
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/LICENSE +0 -0
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/WHEEL +0 -0
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/entry_points.txt +0 -0
- {spacr-0.4.12.dist-info → spacr-0.4.60.dist-info}/top_level.txt +0 -0
spacr/utils.py
CHANGED
@@ -78,7 +78,7 @@ def filepaths_to_database(img_paths, settings, source_folder, crop_mode):
|
|
78
78
|
|
79
79
|
parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=settings['timelapse'])))
|
80
80
|
|
81
|
-
columns = ['
|
81
|
+
columns = ['plateID', 'rowID', 'columnID', 'fieldID']
|
82
82
|
|
83
83
|
if settings['timelapse']:
|
84
84
|
columns = columns + ['time_id']
|
@@ -113,7 +113,7 @@ def activation_maps_to_database(img_paths, source_folder, settings):
|
|
113
113
|
png_df = pd.DataFrame(img_paths, columns=['png_path'])
|
114
114
|
png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
|
115
115
|
parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=False)))
|
116
|
-
columns = ['
|
116
|
+
columns = ['plateID', 'rowID', 'columnID', 'fieldID', 'prcfo', 'object']
|
117
117
|
png_df[columns] = parts
|
118
118
|
|
119
119
|
dataset_name = os.path.splitext(os.path.basename(settings['dataset']))[0]
|
@@ -136,7 +136,7 @@ def activation_correlations_to_database(df, img_paths, source_folder, settings):
|
|
136
136
|
png_df = pd.DataFrame(img_paths, columns=['png_path'])
|
137
137
|
png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
|
138
138
|
parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=False)))
|
139
|
-
columns = ['
|
139
|
+
columns = ['plateID', 'rowID', 'columnID', 'fieldID', 'prcfo', 'object']
|
140
140
|
png_df[columns] = parts
|
141
141
|
|
142
142
|
# Align both DataFrames by file_name
|
@@ -319,23 +319,30 @@ 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
|
-
|
326
|
-
if show:
|
327
|
-
display(settings_df)
|
324
|
+
settings_2 = settings.copy()
|
328
325
|
|
329
|
-
if isinstance(
|
330
|
-
src =
|
331
|
-
#if os.path.exists(src):
|
332
|
-
|
326
|
+
if isinstance(settings_2['src'], list):
|
327
|
+
src = settings_2['src'][0]
|
333
328
|
name = f"{name}_list"
|
334
329
|
else:
|
335
|
-
src =
|
330
|
+
src = settings_2['src']
|
331
|
+
|
332
|
+
if 'test_mode' in settings_2.keys():
|
333
|
+
settings_2['test_mode'] = False
|
334
|
+
|
335
|
+
if 'plot' in settings_2.keys():
|
336
|
+
settings_2['plot'] = False
|
337
|
+
|
338
|
+
settings_df = pd.DataFrame(list(settings_2.items()), columns=['Key', 'Value'])
|
339
|
+
|
340
|
+
if show:
|
341
|
+
display(settings_df)
|
336
342
|
|
337
343
|
settings_csv = os.path.join(src,'settings',f'{name}.csv')
|
338
344
|
os.makedirs(os.path.join(src,'settings'), exist_ok=True)
|
345
|
+
print(f"Saving settings to {settings_csv}")
|
339
346
|
settings_df.to_csv(settings_csv, index=False)
|
340
347
|
|
341
348
|
def print_progress(files_processed, files_to_process, n_jobs, time_ls=None, batch_size=None, operation_type=""):
|
@@ -550,7 +557,7 @@ def _get_cellpose_batch_size():
|
|
550
557
|
def _extract_filename_metadata(filenames, src, regular_expression, metadata_type='cellvoyager', pick_slice=False, skip_mode='01'):
|
551
558
|
|
552
559
|
images_by_key = defaultdict(list)
|
553
|
-
|
560
|
+
|
554
561
|
for filename in filenames:
|
555
562
|
match = regular_expression.match(filename)
|
556
563
|
if match:
|
@@ -593,7 +600,7 @@ def _extract_filename_metadata(filenames, src, regular_expression, metadata_type
|
|
593
600
|
except IndexError:
|
594
601
|
print(f"Could not extract information from filename {filename} using provided regex")
|
595
602
|
else:
|
596
|
-
print(f"Filename {filename} did not match provided regex")
|
603
|
+
print(f"Filename {filename} did not match provided regex: {regular_expression}")
|
597
604
|
continue
|
598
605
|
|
599
606
|
return images_by_key
|
@@ -635,11 +642,11 @@ def _update_database_with_merged_info(db_path, df, table='png_list', columns=['p
|
|
635
642
|
if 'prcfo' not in df.columns:
|
636
643
|
print(f'generating prcfo columns')
|
637
644
|
try:
|
638
|
-
df['prcfo'] = df['
|
645
|
+
df['prcfo'] = df['plateID'].astype(str) + '_' + df['rowID'].astype(str) + '_' + df['columnID'].astype(str) + '_' + df['fieldID'].astype(str) + '_o' + df['object_label'].astype(int).astype(str)
|
639
646
|
except Exception as e:
|
640
647
|
print('Merging on cell failed, trying with cell_id')
|
641
648
|
try:
|
642
|
-
df['prcfo'] = df['
|
649
|
+
df['prcfo'] = df['plateID'].astype(str) + '_' + df['rowID'].astype(str) + '_' + df['columnID'].astype(str) + '_' + df['fieldID'].astype(str) + '_o' + df['cell_id'].astype(int).astype(str)
|
643
650
|
except Exception as e:
|
644
651
|
print(e)
|
645
652
|
|
@@ -731,7 +738,7 @@ def _map_values(row, values, locs):
|
|
731
738
|
if locs:
|
732
739
|
value_dict = {loc: value for value, loc_list in zip(values, locs) for loc in loc_list}
|
733
740
|
# Determine if we're dealing with row or column based on first location identifier
|
734
|
-
type_ = '
|
741
|
+
type_ = 'rowID' if locs[0][0][0] == 'r' else 'columnID'
|
735
742
|
return value_dict.get(row[type_], None)
|
736
743
|
return values[0] if values else None
|
737
744
|
|
@@ -916,21 +923,21 @@ def _merge_and_save_to_database(morph_df, intensity_df, table_type, source_folde
|
|
916
923
|
merged_df['file_name'] = file_name
|
917
924
|
merged_df['path_name'] = os.path.join(source_folder, file_name + '.npy')
|
918
925
|
if timelapse:
|
919
|
-
merged_df[['
|
926
|
+
merged_df[['plateID', 'rowID', 'columnID', 'fieldID', 'timeid', 'prcf']] = merged_df['file_name'].apply(lambda x: pd.Series(_map_wells(x, timelapse)))
|
920
927
|
else:
|
921
|
-
merged_df[['
|
928
|
+
merged_df[['plateID', 'rowID', 'columnID', 'fieldID', 'prcf']] = merged_df['file_name'].apply(lambda x: pd.Series(_map_wells(x, timelapse)))
|
922
929
|
cols = merged_df.columns.tolist() # get the list of all columns
|
923
930
|
if table_type == 'cell' or table_type == 'cytoplasm':
|
924
|
-
column_list = ['object_label', '
|
931
|
+
column_list = ['object_label', 'plateID', 'rowID', 'columnID', 'fieldID', 'prcf', 'file_name', 'path_name']
|
925
932
|
elif table_type == 'nucleus' or table_type == 'pathogen':
|
926
|
-
column_list = ['object_label', 'cell_id', '
|
933
|
+
column_list = ['object_label', 'cell_id', 'plateID', 'rowID', 'columnID', 'fieldID', 'prcf', 'file_name', 'path_name']
|
927
934
|
else:
|
928
935
|
raise ValueError(f"Invalid table_type: {table_type}")
|
929
936
|
# Check if all columns in column_list are in cols
|
930
937
|
missing_columns = [col for col in column_list if col not in cols]
|
931
938
|
if len(missing_columns) == 1 and missing_columns[0] == 'cell_id':
|
932
939
|
missing_columns = False
|
933
|
-
column_list = ['object_label', '
|
940
|
+
column_list = ['object_label', 'plateID', 'rowID', 'columnID', 'fieldID', 'prcf', 'file_name', 'path_name']
|
934
941
|
if missing_columns:
|
935
942
|
raise ValueError(f"Columns missing in DataFrame: {missing_columns}")
|
936
943
|
for i, col in enumerate(column_list):
|
@@ -1144,42 +1151,22 @@ def _masks_to_masks_stack(masks):
|
|
1144
1151
|
for idx, mask in enumerate(masks):
|
1145
1152
|
mask_stack.append(mask)
|
1146
1153
|
return mask_stack
|
1147
|
-
|
1154
|
+
|
1148
1155
|
def _get_diam(mag, obj):
|
1149
1156
|
|
1150
|
-
if
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
elif obj == 'nucleus':
|
1164
|
-
diamiter = 80
|
1165
|
-
elif obj == 'pathogen':
|
1166
|
-
diamiter = 40
|
1167
|
-
else:
|
1168
|
-
raise ValueError("Invalid magnification: Use 20, 40 or 60")
|
1169
|
-
|
1170
|
-
elif mag == 60:
|
1171
|
-
if obj == 'cell':
|
1172
|
-
diamiter = 200
|
1173
|
-
if obj == 'nucleus':
|
1174
|
-
diamiter = 90
|
1175
|
-
if obj == 'pathogen':
|
1176
|
-
diamiter = 60
|
1177
|
-
else:
|
1178
|
-
raise ValueError("Invalid magnification: Use 20, 40 or 60")
|
1179
|
-
else:
|
1180
|
-
raise ValueError("Invalid magnification: Use 20, 40 or 60")
|
1181
|
-
|
1182
|
-
return diamiter
|
1157
|
+
if obj == 'cell':
|
1158
|
+
diamiter = 2 * mag + 80
|
1159
|
+
|
1160
|
+
if obj == 'cell_large':
|
1161
|
+
diamiter = 2 * mag + 120
|
1162
|
+
|
1163
|
+
if obj == 'nucleus':
|
1164
|
+
diamiter = 0.75 * mag + 45
|
1165
|
+
|
1166
|
+
if obj == 'pathogen':
|
1167
|
+
diamiter = mag
|
1168
|
+
|
1169
|
+
return int(diamiter)
|
1183
1170
|
|
1184
1171
|
def _get_object_settings(object_type, settings):
|
1185
1172
|
object_settings = {}
|
@@ -1323,30 +1310,35 @@ def annotate_conditions(df, cells=None, cell_loc=None, pathogens=None, pathogen_
|
|
1323
1310
|
"""
|
1324
1311
|
|
1325
1312
|
def _get_type(val):
|
1326
|
-
"""Determine if a value maps to '
|
1313
|
+
"""Determine if a value maps to 'rowID' or 'columnID'."""
|
1327
1314
|
if isinstance(val, str) and val.startswith('c'):
|
1328
|
-
return '
|
1315
|
+
return 'columnID'
|
1329
1316
|
elif isinstance(val, str) and val.startswith('r'):
|
1330
|
-
return '
|
1317
|
+
return 'rowID'
|
1331
1318
|
return None
|
1332
1319
|
|
1333
1320
|
def _map_or_default(column_name, values, loc, df):
|
1334
1321
|
"""
|
1335
1322
|
Consolidates the logic for mapping values or assigning defaults when loc is None.
|
1336
|
-
|
1323
|
+
|
1337
1324
|
Args:
|
1338
1325
|
column_name (str): The column in the DataFrame to annotate.
|
1339
1326
|
values (list/str): The list of values or a single string to annotate.
|
1340
1327
|
loc (list of lists): Location mapping for the values, or None if not used.
|
1341
1328
|
df (pandas.DataFrame): The DataFrame to modify.
|
1342
1329
|
"""
|
1343
|
-
if isinstance(values, str)
|
1344
|
-
#
|
1345
|
-
df[column_name] = values
|
1330
|
+
if isinstance(values, str) and loc is None:
|
1331
|
+
# If a single string is provided and loc is None, assign the value to all rows
|
1332
|
+
df[column_name] = values
|
1333
|
+
|
1334
|
+
elif isinstance(values, list) and loc is None:
|
1335
|
+
# If a list of values is provided but no loc, assign the first value to all rows
|
1336
|
+
df[column_name] = values[0]
|
1337
|
+
|
1346
1338
|
elif values is not None and loc is not None:
|
1347
|
-
# Perform
|
1339
|
+
# Perform location-based mapping
|
1348
1340
|
value_dict = {val: key for key, loc_list in zip(values, loc) for val in loc_list}
|
1349
|
-
df[column_name] = np.nan
|
1341
|
+
df[column_name] = np.nan # Start with NaN
|
1350
1342
|
for val, key in value_dict.items():
|
1351
1343
|
loc_type = _get_type(val)
|
1352
1344
|
if loc_type:
|
@@ -1390,7 +1382,7 @@ def _split_data(df, group_by, object_type):
|
|
1390
1382
|
# Ensure 'prcf' column exists by concatenating specific columns
|
1391
1383
|
if 'prcf' not in df.columns:
|
1392
1384
|
try:
|
1393
|
-
df['prcf'] = df['
|
1385
|
+
df['prcf'] = df['plateID'].astype(str) + '_' + df['rowID'].astype(str) + '_' + df['columnID'].astype(str) + '_' + df['fieldID'].astype(str)
|
1394
1386
|
except Exception as e:
|
1395
1387
|
print(e)
|
1396
1388
|
|
@@ -1487,7 +1479,7 @@ def _group_by_well(df):
|
|
1487
1479
|
non_numeric_cols = df.select_dtypes(include=['object']).columns
|
1488
1480
|
|
1489
1481
|
# Apply mean function to numeric columns and first to non-numeric
|
1490
|
-
df_grouped = df.groupby(['
|
1482
|
+
df_grouped = df.groupby(['plateID', 'rowID', 'columnID']).agg({**{col: np.mean for col in numeric_cols}, **{col: 'first' for col in non_numeric_cols}})
|
1491
1483
|
return df_grouped
|
1492
1484
|
|
1493
1485
|
###################################################
|
@@ -2166,11 +2158,11 @@ def augment_classes(dst, nc, pc, generate=True,move=True):
|
|
2166
2158
|
def annotate_predictions(csv_loc):
|
2167
2159
|
df = pd.read_csv(csv_loc)
|
2168
2160
|
df['filename'] = df['path'].apply(lambda x: x.split('/')[-1])
|
2169
|
-
df[['
|
2161
|
+
df[['plateID', 'well', 'fieldID', 'object']] = df['filename'].str.split('_', expand=True)
|
2170
2162
|
df['object'] = df['object'].str.replace('.png', '')
|
2171
2163
|
|
2172
2164
|
def assign_condition(row):
|
2173
|
-
plate = int(row['
|
2165
|
+
plate = int(row['plateID'])
|
2174
2166
|
col = int(row['well'][1:])
|
2175
2167
|
|
2176
2168
|
if col > 3:
|
@@ -2321,7 +2313,7 @@ def check_multicollinearity(x):
|
|
2321
2313
|
|
2322
2314
|
def lasso_reg(merged_df, alpha_value=0.01, reg_type='lasso'):
|
2323
2315
|
# Separate predictors and response
|
2324
|
-
X = merged_df[['gene', 'grna', '
|
2316
|
+
X = merged_df[['gene', 'grna', 'plateID', 'rowID', 'columnID']]
|
2325
2317
|
y = merged_df['pred']
|
2326
2318
|
|
2327
2319
|
# One-hot encode the categorical predictors
|
@@ -3099,12 +3091,8 @@ def _get_regex(metadata_type, img_format, custom_regex=None):
|
|
3099
3091
|
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
3092
|
elif metadata_type == 'cq1':
|
3101
3093
|
regex = f'W(?P<wellID>.*)F(?P<fieldID>.*)T(?P<timeID>.*)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
|
3102
|
-
elif metadata_type == '
|
3103
|
-
regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID
|
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}'
|
3094
|
+
elif metadata_type == 'auto':
|
3095
|
+
regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>.*)C(?P<chanID>.*).tif'
|
3108
3096
|
elif metadata_type == 'custom':
|
3109
3097
|
regex = f'({custom_regex}){img_format}'
|
3110
3098
|
|
@@ -3122,7 +3110,7 @@ def _run_test_mode(src, regex, timelapse=False, test_images=10, random_test=True
|
|
3122
3110
|
|
3123
3111
|
if os.path.exists(os.path.join(src, 'orig')):
|
3124
3112
|
src = os.path.join(src, 'orig')
|
3125
|
-
|
3113
|
+
|
3126
3114
|
all_filenames = [filename for filename in os.listdir(src) if regular_expression.match(filename)]
|
3127
3115
|
print(f'Found {len(all_filenames)} files')
|
3128
3116
|
images_by_set = defaultdict(list)
|
@@ -3164,7 +3152,6 @@ def _choose_model(model_name, device, object_type='cell', restore_type=None, obj
|
|
3164
3152
|
model_path = os.path.join(current_dir, 'models', 'cp', 'toxo_pv_lumen.CP_model')
|
3165
3153
|
print(model_path)
|
3166
3154
|
model = cp_models.CellposeModel(gpu=torch.cuda.is_available(), model_type=None, pretrained_model=model_path, diam_mean=diameter, device=device)
|
3167
|
-
#model = cp_models.Cellpose(gpu=torch.cuda.is_available(), model_type='cyto', device=device)
|
3168
3155
|
print(f'Using Toxoplasma PV lumen model to generate pathogen masks')
|
3169
3156
|
return model
|
3170
3157
|
|
@@ -4693,12 +4680,12 @@ def process_vision_results(df, threshold=0.5):
|
|
4693
4680
|
# Split the 'path' column using _map_wells function
|
4694
4681
|
mapped_values = df['path'].apply(lambda x: _map_wells(x))
|
4695
4682
|
|
4696
|
-
df['
|
4697
|
-
df['
|
4698
|
-
df['
|
4699
|
-
df['
|
4683
|
+
df['plateID'] = mapped_values.apply(lambda x: x[0])
|
4684
|
+
df['rowID'] = mapped_values.apply(lambda x: x[1])
|
4685
|
+
df['columnID'] = mapped_values.apply(lambda x: x[2])
|
4686
|
+
df['fieldID'] = mapped_values.apply(lambda x: x[3])
|
4700
4687
|
df['object'] = df['path'].str.split('_').str[3].str.split('.').str[0]
|
4701
|
-
df['prc'] = df['
|
4688
|
+
df['prc'] = df['plateID'].astype(str) + '_' + df['rowID'].astype(str) + '_' + df['columnID'].astype(str)
|
4702
4689
|
df['cv_predictions'] = (df['pred'] >= threshold).astype(int)
|
4703
4690
|
|
4704
4691
|
return df
|
@@ -4945,7 +4932,7 @@ def download_models(repo_id="einarolafsson/models", retries=5, delay=5):
|
|
4945
4932
|
if not os.path.exists(local_dir):
|
4946
4933
|
os.makedirs(local_dir)
|
4947
4934
|
elif len(os.listdir(local_dir)) > 0:
|
4948
|
-
print(f"Models already downloaded to: {local_dir}")
|
4935
|
+
#print(f"Models already downloaded to: {local_dir}")
|
4949
4936
|
return local_dir
|
4950
4937
|
|
4951
4938
|
attempt = 0
|
@@ -5113,24 +5100,24 @@ def fill_holes_in_mask(mask):
|
|
5113
5100
|
|
5114
5101
|
def correct_metadata_column_names(df):
|
5115
5102
|
if 'plate_name' in df.columns:
|
5116
|
-
df = df.rename(columns={'plate_name': '
|
5103
|
+
df = df.rename(columns={'plate_name': 'plateID'})
|
5117
5104
|
if 'column_name' in df.columns:
|
5118
|
-
df = df.rename(columns={'column_name': '
|
5105
|
+
df = df.rename(columns={'column_name': 'columnID'})
|
5119
5106
|
if 'col' in df.columns:
|
5120
|
-
df = df.rename(columns={'col': '
|
5107
|
+
df = df.rename(columns={'col': 'columnID'})
|
5121
5108
|
if 'row_name' in df.columns:
|
5122
|
-
df = df.rename(columns={'row_name': '
|
5109
|
+
df = df.rename(columns={'row_name': 'rowID'})
|
5123
5110
|
if 'grna_name' in df.columns:
|
5124
5111
|
df = df.rename(columns={'grna_name': 'grna'})
|
5125
5112
|
if 'plate_row' in df.columns:
|
5126
|
-
df[['
|
5113
|
+
df[['plateID', 'rowID']] = df['plate_row'].str.split('_', expand=True)
|
5127
5114
|
return df
|
5128
5115
|
|
5129
|
-
def control_filelist(folder, mode='
|
5116
|
+
def control_filelist(folder, mode='columnID', values=['01','02']):
|
5130
5117
|
files = os.listdir(folder)
|
5131
|
-
if mode is '
|
5118
|
+
if mode is 'columnID':
|
5132
5119
|
filtered_files = [file for file in files if file.split('_')[1][1:] in values]
|
5133
|
-
if mode is '
|
5120
|
+
if mode is 'rowID':
|
5134
5121
|
filtered_files = [file for file in files if file.split('_')[1][:1] in values]
|
5135
5122
|
return filtered_files
|
5136
5123
|
|
@@ -5148,12 +5135,12 @@ def rename_columns_in_db(db_path):
|
|
5148
5135
|
columns_info = cursor.fetchall()
|
5149
5136
|
column_names = [col[1] for col in columns_info]
|
5150
5137
|
|
5151
|
-
# Check if columns '
|
5138
|
+
# Check if columns 'rowID' or 'columnID' exist
|
5152
5139
|
columns_to_rename = {}
|
5153
5140
|
if 'row' in column_names:
|
5154
|
-
columns_to_rename['row'] = '
|
5141
|
+
columns_to_rename['row'] = 'rowID'
|
5155
5142
|
if 'col' in column_names:
|
5156
|
-
columns_to_rename['col'] = '
|
5143
|
+
columns_to_rename['col'] = 'columnID'
|
5157
5144
|
|
5158
5145
|
# Rename columns if necessary
|
5159
5146
|
if columns_to_rename:
|
@@ -5337,3 +5324,170 @@ def calculate_shortest_distance(df, object1, object2):
|
|
5337
5324
|
df[f'{object1}_{object2}_shortest_distance'] = np.maximum(shortest_distance, 0)
|
5338
5325
|
|
5339
5326
|
return df
|
5327
|
+
|
5328
|
+
def format_path_for_system(path):
|
5329
|
+
"""
|
5330
|
+
Takes a file path and reformats it to be compatible with the current operating system.
|
5331
|
+
|
5332
|
+
Args:
|
5333
|
+
path (str): The file path to be formatted.
|
5334
|
+
|
5335
|
+
Returns:
|
5336
|
+
str: The formatted path for the current operating system.
|
5337
|
+
"""
|
5338
|
+
system = platform.system()
|
5339
|
+
|
5340
|
+
# Convert Windows-style paths to Unix-style (Linux/macOS)
|
5341
|
+
if system in ["Linux", "Darwin"]: # Darwin is macOS
|
5342
|
+
formatted_path = path.replace("\\", "/")
|
5343
|
+
|
5344
|
+
# Convert Unix-style paths to Windows-style
|
5345
|
+
elif system == "Windows":
|
5346
|
+
formatted_path = path.replace("/", "\\")
|
5347
|
+
|
5348
|
+
else:
|
5349
|
+
raise ValueError(f"Unsupported OS: {system}")
|
5350
|
+
|
5351
|
+
# Normalize path to ensure consistency
|
5352
|
+
new_path = os.path.normpath(formatted_path)
|
5353
|
+
if os.path.exists(new_path):
|
5354
|
+
print(f"Found path: {new_path}")
|
5355
|
+
else:
|
5356
|
+
print(f"Path not found: {new_path}")
|
5357
|
+
|
5358
|
+
return new_path
|
5359
|
+
|
5360
|
+
|
5361
|
+
def normalize_src_path(src):
|
5362
|
+
"""
|
5363
|
+
Ensures that the 'src' value is properly formatted as either a list of strings or a single string.
|
5364
|
+
|
5365
|
+
Args:
|
5366
|
+
src (str or list): The input source path(s).
|
5367
|
+
|
5368
|
+
Returns:
|
5369
|
+
list or str: A correctly formatted list if the input was a list (or string representation of a list),
|
5370
|
+
otherwise a single string.
|
5371
|
+
"""
|
5372
|
+
if isinstance(src, list):
|
5373
|
+
return src # Already a list, return as-is
|
5374
|
+
|
5375
|
+
if isinstance(src, str):
|
5376
|
+
try:
|
5377
|
+
# Check if it is a string representation of a list
|
5378
|
+
evaluated_src = ast.literal_eval(src)
|
5379
|
+
if isinstance(evaluated_src, list) and all(isinstance(item, str) for item in evaluated_src):
|
5380
|
+
return evaluated_src # Convert to real list
|
5381
|
+
except (SyntaxError, ValueError):
|
5382
|
+
pass # Not a valid list, treat as a string
|
5383
|
+
|
5384
|
+
return src # Return as a string if not a list
|
5385
|
+
|
5386
|
+
raise ValueError(f"Invalid type for 'src': {type(src).__name__}, expected str or list")
|
5387
|
+
|
5388
|
+
def generate_image_path_map(root_folder, valid_extensions=("tif", "tiff", "png", "jpg", "jpeg", "bmp", "czi", "nd2", "lif")):
|
5389
|
+
"""
|
5390
|
+
Recursively scans a folder and its subfolders for images, then creates a mapping of:
|
5391
|
+
{original_image_path: new_image_path}, where the new path includes all subfolder names.
|
5392
|
+
|
5393
|
+
Args:
|
5394
|
+
root_folder (str): The root directory to scan for images.
|
5395
|
+
valid_extensions (tuple): Tuple of valid image file extensions.
|
5396
|
+
|
5397
|
+
Returns:
|
5398
|
+
dict: A dictionary mapping original image paths to their new paths.
|
5399
|
+
"""
|
5400
|
+
image_path_map = {}
|
5401
|
+
|
5402
|
+
for dirpath, _, filenames in os.walk(root_folder):
|
5403
|
+
for file in filenames:
|
5404
|
+
ext = file.lower().split('.')[-1]
|
5405
|
+
if ext in valid_extensions:
|
5406
|
+
# Get relative path of the image from root_folder
|
5407
|
+
relative_path = os.path.relpath(dirpath, root_folder)
|
5408
|
+
|
5409
|
+
# Construct new filename: Embed folder hierarchy into the name
|
5410
|
+
folder_parts = relative_path.split(os.sep) # Get all folder names
|
5411
|
+
folder_info = "_".join(folder_parts) if folder_parts else "" # Join with underscores
|
5412
|
+
|
5413
|
+
# Generate new filename
|
5414
|
+
new_filename = f"{folder_info}_{file}" if folder_info else file
|
5415
|
+
|
5416
|
+
# Store in dictionary (original path -> new path)
|
5417
|
+
original_path = os.path.join(dirpath, file)
|
5418
|
+
new_path = os.path.join(root_folder, new_filename)
|
5419
|
+
image_path_map[original_path] = new_path
|
5420
|
+
|
5421
|
+
return image_path_map
|
5422
|
+
|
5423
|
+
def copy_images_to_consolidated(image_path_map, root_folder):
|
5424
|
+
"""
|
5425
|
+
Copies images from their original locations to a 'consolidated' folder,
|
5426
|
+
renaming them according to the generated dictionary.
|
5427
|
+
|
5428
|
+
Args:
|
5429
|
+
image_path_map (dict): Dictionary mapping {original_path: new_path}.
|
5430
|
+
root_folder (str): The root directory where the 'consolidated' folder will be created.
|
5431
|
+
"""
|
5432
|
+
|
5433
|
+
consolidated_folder = os.path.join(root_folder, "consolidated")
|
5434
|
+
os.makedirs(consolidated_folder, exist_ok=True) # Ensure 'consolidated' folder exists
|
5435
|
+
files_processed = 0
|
5436
|
+
files_to_process = len(image_path_map)
|
5437
|
+
time_ls= []
|
5438
|
+
|
5439
|
+
for original_path, new_path in image_path_map.items():
|
5440
|
+
|
5441
|
+
start = time.time()
|
5442
|
+
new_filename = os.path.basename(new_path) # Extract only the new filename
|
5443
|
+
new_file_path = os.path.join(consolidated_folder, new_filename) # Place in 'consolidated' folder
|
5444
|
+
|
5445
|
+
shutil.copy2(original_path, new_file_path) # Copy file with metadata preserved
|
5446
|
+
|
5447
|
+
files_processed += 1
|
5448
|
+
stop = time.time()
|
5449
|
+
duration = (stop - start)
|
5450
|
+
time_ls.append(duration)
|
5451
|
+
|
5452
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type=f'Consolidating images')
|
5453
|
+
#print(f"Copied: {original_path} -> {new_file_path}")
|
5454
|
+
|
5455
|
+
def correct_metadata(df):
|
5456
|
+
|
5457
|
+
#if 'object' in df.columns:
|
5458
|
+
# df['objectID'] = df['object']
|
5459
|
+
|
5460
|
+
if 'object_name' in df.columns:
|
5461
|
+
df['objectID'] = df['object_name']
|
5462
|
+
|
5463
|
+
if 'field_name' in df.columns:
|
5464
|
+
df['fieldID'] = df['field_name']
|
5465
|
+
|
5466
|
+
if 'plate' in df.columns:
|
5467
|
+
df['plateID'] = df['plate']
|
5468
|
+
|
5469
|
+
if 'plate_name' in df.columns:
|
5470
|
+
df['plateID'] = df['plate_name']
|
5471
|
+
|
5472
|
+
if 'row' in df.columns:
|
5473
|
+
df = df.rename(columns={'row': 'rowID'})
|
5474
|
+
|
5475
|
+
if 'row_name' in df.columns:
|
5476
|
+
df = df.rename(columns={'row_name': 'rowID'})
|
5477
|
+
|
5478
|
+
if 'col' in df.columns:
|
5479
|
+
df = df.rename(columns={'col': 'columnID'})
|
5480
|
+
|
5481
|
+
if 'column' in df.columns:
|
5482
|
+
df = df.rename(columns={'column': 'columnID'})
|
5483
|
+
|
5484
|
+
if 'column_name' in df.columns:
|
5485
|
+
df = df.rename(columns={'column_name': 'columnID'})
|
5486
|
+
|
5487
|
+
if 'field' in df.columns:
|
5488
|
+
df = df.rename(columns={'field': 'fieldID'})
|
5489
|
+
|
5490
|
+
if 'field_name' in df.columns:
|
5491
|
+
df = df.rename(columns={'field_name': 'fieldID'})
|
5492
|
+
|
5493
|
+
return df
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: spacr
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.60
|
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=
|
13
|
-
spacr/deep_spacr.py,sha256=
|
12
|
+
spacr/core.py,sha256=ghesCiRKxdHqnujztb9XiuhhZ-3gBs7aQIIzvqaf-iI,51409
|
13
|
+
spacr/deep_spacr.py,sha256=055tIo3WP3elGFiIuSZaLURgu2XyUDxAdbw5ezASEqM,54526
|
14
14
|
spacr/gui.py,sha256=ARyn9Q_g8HoP-cXh1nzMLVFCKqthY4v2u9yORyaQqQE,8230
|
15
|
-
spacr/gui_core.py,sha256=
|
16
|
-
spacr/gui_elements.py,sha256=
|
17
|
-
spacr/gui_utils.py,sha256=
|
18
|
-
spacr/io.py,sha256=
|
15
|
+
spacr/gui_core.py,sha256=AYXZpdKMRezJfS7Xcfztdc2yco6lV21ovwkyKQGbIZg,56206
|
16
|
+
spacr/gui_elements.py,sha256=5a3BOpctBPklsT1NungqS72h1Bg1FArUndE0OfvWD8Y,152646
|
17
|
+
spacr/gui_utils.py,sha256=dWVPFwDj793Z3ERG4mMC0hI0MKkOrvXJpUYlcjpCBsU,41357
|
18
|
+
spacr/io.py,sha256=eZj5RCqje3xb_4BwPC0ASBqI-c7_6EeQYv8zrQf0tCc,159371
|
19
19
|
spacr/logger.py,sha256=lJhTqt-_wfAunCPl93xE65Wr9Y1oIHJWaZMjunHUeIw,1538
|
20
|
-
spacr/measure.py,sha256=
|
20
|
+
spacr/measure.py,sha256=Z3u4BU5RzcY82IZuboQ0OsxuXaPVwOlH65Rw6FrL5z4,55045
|
21
21
|
spacr/mediar.py,sha256=FwLvbLQW5LQzPgvJZG8Lw7GniA2vbZx6Jv6vIKu7I5c,14743
|
22
|
-
spacr/ml.py,sha256=
|
22
|
+
spacr/ml.py,sha256=XCRZeX7UkbMctQICIoskeWVx8CCmmCoHNauUOAkfFq0,91692
|
23
23
|
spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
|
24
|
-
spacr/plot.py,sha256=
|
25
|
-
spacr/sequencing.py,sha256=
|
26
|
-
spacr/settings.py,sha256=
|
24
|
+
spacr/plot.py,sha256=lmpIJozfxMyOyGAMno4j-C7NPjDYRMr29SDAnrV-bb4,170815
|
25
|
+
spacr/sequencing.py,sha256=EY12RdW5QRKpHDRQCw1QoAlxCq8FK2v6WoVa5uuDBXQ,26745
|
26
|
+
spacr/settings.py,sha256=j2H5OieJKwu-TITlfP5tAnUGUhBjng8ECy3jXm3JMb8,86599
|
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=
|
31
|
-
spacr/timelapse.py,sha256=
|
32
|
-
spacr/toxo.py,sha256=
|
33
|
-
spacr/utils.py,sha256=
|
30
|
+
spacr/submodules.py,sha256=dNiUqwDYwJ2J1s3DkwGLOTB-TzyGq_GXoojpCrLMm7A,82812
|
31
|
+
spacr/timelapse.py,sha256=lh3Aev5S7Ou1YWPBYBYeSGU0I-NPb0-4znYfm2NYf_I,39629
|
32
|
+
spacr/toxo.py,sha256=GoNfgyH-NJx3WOzNQPgzODir7Jp65fs7UM46XpzcrUo,26056
|
33
|
+
spacr/utils.py,sha256=20FRbevoFmPA61vS50tR48g6DpGkIvInGPv9nE810gQ,231668
|
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.
|
157
|
-
spacr-0.4.
|
158
|
-
spacr-0.4.
|
159
|
-
spacr-0.4.
|
160
|
-
spacr-0.4.
|
161
|
-
spacr-0.4.
|
156
|
+
spacr-0.4.60.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
|
157
|
+
spacr-0.4.60.dist-info/METADATA,sha256=Na4GGSJEuobmjgAo8HSbejQ0h8E1etVHb249v5oBB6Y,6096
|
158
|
+
spacr-0.4.60.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
159
|
+
spacr-0.4.60.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
|
160
|
+
spacr-0.4.60.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
|
161
|
+
spacr-0.4.60.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|