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/gui_elements.py
CHANGED
@@ -726,8 +726,8 @@ class spacrProgressBar(ttk.Progressbar):
|
|
726
726
|
|
727
727
|
def set_label_position(self):
|
728
728
|
if self.label and self.progress_label:
|
729
|
-
row_info = self.grid_info().get('
|
730
|
-
col_info = self.grid_info().get('
|
729
|
+
row_info = self.grid_info().get('rowID', 0)
|
730
|
+
col_info = self.grid_info().get('columnID', 0)
|
731
731
|
col_span = self.grid_info().get('columnspan', 1)
|
732
732
|
self.progress_label.grid(row=row_info + 1, column=col_info, columnspan=col_span, pady=5, padx=5, sticky='ew')
|
733
733
|
|
@@ -2285,6 +2285,9 @@ class AnnotateApp:
|
|
2285
2285
|
|
2286
2286
|
self.train_button = Button(self.button_frame,text="orig.",command=self.swich_back_annotation_column,bg=self.bg_color,fg=self.fg_color,highlightbackground=self.fg_color,highlightcolor=self.fg_color,highlightthickness=1)
|
2287
2287
|
self.train_button.pack(side="right", padx=5)
|
2288
|
+
|
2289
|
+
self.settings_button = Button(self.button_frame, text="Settings", command=self.open_settings_window, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color,highlightcolor=self.fg_color,highlightthickness=1)
|
2290
|
+
self.settings_button.pack(side="right", padx=5)
|
2288
2291
|
|
2289
2292
|
# Calculate grid rows and columns based on the root window size and image size
|
2290
2293
|
self.calculate_grid_dimensions()
|
@@ -2308,6 +2311,134 @@ class AnnotateApp:
|
|
2308
2311
|
for col in range(self.grid_cols):
|
2309
2312
|
self.grid_frame.grid_columnconfigure(col, weight=1)
|
2310
2313
|
|
2314
|
+
def open_settings_window(self):
|
2315
|
+
from .gui_utils import generate_annotate_fields, convert_to_number
|
2316
|
+
|
2317
|
+
# Create settings window
|
2318
|
+
settings_window = tk.Toplevel(self.root)
|
2319
|
+
settings_window.title("Modify Annotation Settings")
|
2320
|
+
|
2321
|
+
style_out = set_dark_style(ttk.Style())
|
2322
|
+
settings_window.configure(bg=style_out['bg_color'])
|
2323
|
+
|
2324
|
+
settings_frame = tk.Frame(settings_window, bg=style_out['bg_color'])
|
2325
|
+
settings_frame.pack(fill=tk.BOTH, expand=True)
|
2326
|
+
|
2327
|
+
# Generate fields with current settings pre-filled
|
2328
|
+
vars_dict = generate_annotate_fields(settings_frame)
|
2329
|
+
|
2330
|
+
# Pre-fill the current settings into vars_dict
|
2331
|
+
current_settings = {
|
2332
|
+
'image_type': self.image_type or '',
|
2333
|
+
'channels': ','.join(self.channels) if self.channels else '',
|
2334
|
+
'img_size': f"{self.image_size[0]},{self.image_size[1]}",
|
2335
|
+
'annotation_column': self.annotation_column or '',
|
2336
|
+
'normalize': str(self.normalize),
|
2337
|
+
'percentiles': ','.join(map(str, self.percentiles)),
|
2338
|
+
'measurement': ','.join(self.measurement) if self.measurement else '',
|
2339
|
+
'threshold': str(self.threshold) if self.threshold is not None else '',
|
2340
|
+
'normalize_channels': ','.join(self.normalize_channels) if self.normalize_channels else ''
|
2341
|
+
}
|
2342
|
+
|
2343
|
+
for key, data in vars_dict.items():
|
2344
|
+
if key in current_settings:
|
2345
|
+
data['entry'].delete(0, tk.END)
|
2346
|
+
data['entry'].insert(0, current_settings[key])
|
2347
|
+
|
2348
|
+
def apply_new_settings():
|
2349
|
+
settings = {key: data['entry'].get() for key, data in vars_dict.items()}
|
2350
|
+
|
2351
|
+
# Process settings exactly as your original initiation function does
|
2352
|
+
settings['channels'] = settings['channels'].split(',') if settings['channels'] else None
|
2353
|
+
settings['img_size'] = list(map(int, settings['img_size'].split(',')))
|
2354
|
+
settings['percentiles'] = list(map(convert_to_number, settings['percentiles'].split(','))) if settings['percentiles'] else [1, 99]
|
2355
|
+
settings['normalize'] = settings['normalize'].lower() == 'true'
|
2356
|
+
settings['normalize_channels'] = settings['normalize_channels'].split(',') if settings['normalize_channels'] else None
|
2357
|
+
|
2358
|
+
try:
|
2359
|
+
settings['measurement'] = settings['measurement'].split(',') if settings['measurement'] else None
|
2360
|
+
settings['threshold'] = None if settings['threshold'].lower() == 'none' else int(settings['threshold'])
|
2361
|
+
except:
|
2362
|
+
settings['measurement'] = None
|
2363
|
+
settings['threshold'] = None
|
2364
|
+
|
2365
|
+
# Convert empty strings to None
|
2366
|
+
for key, value in settings.items():
|
2367
|
+
if isinstance(value, list):
|
2368
|
+
settings[key] = [v if v != '' else None for v in value]
|
2369
|
+
elif value == '':
|
2370
|
+
settings[key] = None
|
2371
|
+
|
2372
|
+
# Apply these settings dynamically using update_settings method
|
2373
|
+
self.update_settings(**{
|
2374
|
+
'image_type': settings.get('image_type'),
|
2375
|
+
'channels': settings.get('channels'),
|
2376
|
+
'image_size': settings.get('img_size'),
|
2377
|
+
'annotation_column': settings.get('annotation_column'),
|
2378
|
+
'normalize': settings.get('normalize'),
|
2379
|
+
'percentiles': settings.get('percentiles'),
|
2380
|
+
'measurement': settings.get('measurement'),
|
2381
|
+
'threshold': settings.get('threshold'),
|
2382
|
+
'normalize_channels': settings.get('normalize_channels')
|
2383
|
+
})
|
2384
|
+
|
2385
|
+
settings_window.destroy()
|
2386
|
+
|
2387
|
+
apply_button = spacrButton(settings_window, text="Apply Settings", command=apply_new_settings,show_text=False)
|
2388
|
+
apply_button.pack(pady=10)
|
2389
|
+
|
2390
|
+
def update_settings(self, **kwargs):
|
2391
|
+
allowed_attributes = {
|
2392
|
+
'image_type', 'channels', 'image_size', 'annotation_column',
|
2393
|
+
'normalize', 'percentiles', 'measurement', 'threshold', 'normalize_channels'
|
2394
|
+
}
|
2395
|
+
|
2396
|
+
updated = False
|
2397
|
+
|
2398
|
+
for attr, value in kwargs.items():
|
2399
|
+
if attr in allowed_attributes and value is not None:
|
2400
|
+
setattr(self, attr, value)
|
2401
|
+
updated = True
|
2402
|
+
|
2403
|
+
if 'image_size' in kwargs:
|
2404
|
+
if isinstance(self.image_size, list):
|
2405
|
+
self.image_size = (int(self.image_size[0]), int(self.image_size[0]))
|
2406
|
+
elif isinstance(self.image_size, int):
|
2407
|
+
self.image_size = (self.image_size, self.image_size)
|
2408
|
+
else:
|
2409
|
+
raise ValueError("Invalid image size")
|
2410
|
+
|
2411
|
+
self.calculate_grid_dimensions()
|
2412
|
+
self.recreate_image_grid()
|
2413
|
+
|
2414
|
+
if updated:
|
2415
|
+
current_index = self.index # Retain current index
|
2416
|
+
self.prefilter_paths_annotations()
|
2417
|
+
|
2418
|
+
# Ensure the retained index is still valid (not out of bounds)
|
2419
|
+
max_index = len(self.filtered_paths_annotations) - 1
|
2420
|
+
self.index = min(current_index, max_index := max(0, max(0, max(len(self.filtered_paths_annotations) - self.grid_rows * self.grid_cols, 0))))
|
2421
|
+
self.load_images()
|
2422
|
+
|
2423
|
+
def recreate_image_grid(self):
|
2424
|
+
# Remove current labels
|
2425
|
+
for label in self.labels:
|
2426
|
+
label.destroy()
|
2427
|
+
self.labels.clear()
|
2428
|
+
|
2429
|
+
# Recreate the labels grid with updated dimensions
|
2430
|
+
for i in range(self.grid_rows * self.grid_cols):
|
2431
|
+
label = Label(self.grid_frame, bg=self.root.cget('bg'))
|
2432
|
+
label.grid(row=i // self.grid_cols, column=i % self.grid_cols, padx=2, pady=2, sticky="nsew")
|
2433
|
+
self.labels.append(label)
|
2434
|
+
|
2435
|
+
# Reconfigure grid weights
|
2436
|
+
for row in range(self.grid_rows):
|
2437
|
+
self.grid_frame.grid_rowconfigure(row, weight=1)
|
2438
|
+
for col in range(self.grid_cols):
|
2439
|
+
self.grid_frame.grid_columnconfigure(col, weight=1)
|
2440
|
+
|
2441
|
+
|
2311
2442
|
def swich_back_annotation_column(self):
|
2312
2443
|
self.annotation_column = self.orig_annotation_columns
|
2313
2444
|
self.prefilter_paths_annotations()
|
spacr/gui_utils.py
CHANGED
@@ -106,7 +106,6 @@ def parse_list(value):
|
|
106
106
|
except (ValueError, SyntaxError) as e:
|
107
107
|
raise ValueError(f"Invalid format for list: {value}. Error: {e}")
|
108
108
|
|
109
|
-
# Usage example in your create_input_field function
|
110
109
|
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
111
110
|
"""
|
112
111
|
Create an input field in the specified frame.
|
@@ -365,27 +364,30 @@ def convert_settings_dict_for_gui(settings):
|
|
365
364
|
from torchvision import models as torch_models
|
366
365
|
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
367
366
|
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
367
|
+
chan_list = ['[0,1,2,3,4,5,6,7,8]','[0,1,2,3,4,5,6,7]','[0,1,2,3,4,5,6]','[0,1,2,3,4,5]','[0,1,2,3,4]','[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]', '[0,0]']
|
368
368
|
chans_v2 = [0, 1, 2, 3, None]
|
369
|
+
chans_v3 = list(range(0, 21, 1)) + [None]
|
370
|
+
chans_v4 = [0, 1, 2, 3, None]
|
369
371
|
variables = {}
|
370
372
|
special_cases = {
|
371
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', '
|
372
|
-
'channels': ('combo',
|
373
|
+
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'auto', 'custom'], 'cellvoyager'),
|
374
|
+
'channels': ('combo', chan_list, '[0,1,2,3]'),
|
373
375
|
'train_channels': ('combo', ["['r','g','b']", "['r','g']", "['r','b']", "['g','b']", "['r']", "['g']", "['b']"], "['r','g','b']"),
|
374
|
-
'channel_dims': ('combo',
|
376
|
+
'channel_dims': ('combo', chan_list, '[0,1,2,3]'),
|
375
377
|
'dataset_mode': ('combo', ['annotation', 'metadata', 'recruitment'], 'metadata'),
|
376
378
|
'cov_type': ('combo', ['HC0', 'HC1', 'HC2', 'HC3', None], None),
|
377
|
-
'cell_mask_dim': ('combo',
|
378
|
-
'cell_chann_dim': ('combo',
|
379
|
-
'nucleus_mask_dim': ('combo',
|
380
|
-
'nucleus_chann_dim': ('combo',
|
381
|
-
'pathogen_mask_dim': ('combo',
|
382
|
-
'pathogen_chann_dim': ('combo',
|
383
|
-
'crop_mode': ('combo', [['cell'], ['nucleus'], ['pathogen'], ['cell', 'nucleus'], ['cell', 'pathogen'], ['nucleus', 'pathogen'], ['cell', 'nucleus', 'pathogen']], ['cell']),
|
384
|
-
|
385
|
-
'nucleus_channel': ('combo',
|
386
|
-
'cell_channel': ('combo',
|
387
|
-
'channel_of_interest': ('combo',
|
388
|
-
'pathogen_channel': ('combo',
|
379
|
+
#'cell_mask_dim': ('combo', chans_v3, None),
|
380
|
+
#'cell_chann_dim': ('combo', chans_v3, None),
|
381
|
+
#'nucleus_mask_dim': ('combo', chans_v3, None),
|
382
|
+
#'nucleus_chann_dim': ('combo', chans_v3, None),
|
383
|
+
#'pathogen_mask_dim': ('combo', chans_v3, None),
|
384
|
+
#'pathogen_chann_dim': ('combo', chans_v3, None),
|
385
|
+
'crop_mode': ('combo', ["['cell']", "['nucleus']", "['pathogen']", "['cell', 'nucleus']", "['cell', 'pathogen']", "['nucleus', 'pathogen']", "['cell', 'nucleus', 'pathogen']"], "['cell']"),
|
386
|
+
#'magnification': ('combo', [20, 40, 60], 20),
|
387
|
+
#'nucleus_channel': ('combo', chans_v3, None),
|
388
|
+
#'cell_channel': ('combo', chans_v3, None),
|
389
|
+
#'channel_of_interest': ('combo', chans_v3, None),
|
390
|
+
#'pathogen_channel': ('combo', chans_v3, None),
|
389
391
|
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
390
392
|
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
391
393
|
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
@@ -462,10 +464,11 @@ def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imp
|
|
462
464
|
finally:
|
463
465
|
# Restore the original plt.show function
|
464
466
|
plt.show = original_show
|
467
|
+
|
465
468
|
|
469
|
+
|
466
470
|
def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
467
471
|
|
468
|
-
from .gui_utils import process_stdout_stderr
|
469
472
|
from .core import generate_image_umap, preprocess_generate_masks
|
470
473
|
from .cellpose import identify_masks_finetune, check_cellpose_models, compare_cellpose_masks
|
471
474
|
from .submodules import analyze_recruitment
|
@@ -476,9 +479,10 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
476
479
|
from .sim import run_multiple_simulations
|
477
480
|
from .deep_spacr import deep_spacr, apply_model_to_tar
|
478
481
|
from .sequencing import generate_barecode_mapping
|
482
|
+
|
479
483
|
process_stdout_stderr(q)
|
480
|
-
|
481
|
-
print(f'run_function_gui settings_type: {settings_type}')
|
484
|
+
|
485
|
+
print(f'run_function_gui settings_type: {settings_type}')
|
482
486
|
|
483
487
|
if settings_type == 'mask':
|
484
488
|
function = preprocess_generate_masks
|
@@ -523,7 +527,7 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
523
527
|
function = process_non_tif_non_2D_images
|
524
528
|
imports = 1
|
525
529
|
else:
|
526
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
530
|
+
raise ValueError(f"Error: Invalid settings type: {settings_type}")
|
527
531
|
try:
|
528
532
|
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
529
533
|
except Exception as e:
|