spacr 0.2.41__py3-none-any.whl → 0.2.46__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 +46 -66
- spacr/gui.py +20 -38
- spacr/gui_core.py +327 -625
- spacr/gui_elements.py +175 -24
- spacr/gui_utils.py +324 -62
- spacr/io.py +43 -46
- spacr/plot.py +106 -0
- spacr/resources/icons/logo.pdf +2786 -6
- spacr/resources/icons/logo_spacr.png +0 -0
- spacr/resources/icons/logo_spacr_1.png +0 -0
- spacr/settings.py +0 -2
- spacr/utils.py +4 -24
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/METADATA +1 -1
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/RECORD +18 -16
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/LICENSE +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/WHEEL +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.41.dist-info → spacr-0.2.46.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -1,48 +1,19 @@
|
|
1
|
-
import os, io, sys, ast, ctypes,
|
1
|
+
import os, io, sys, ast, ctypes, ast, sqlite3, requests, time, traceback
|
2
|
+
import traceback
|
2
3
|
import tkinter as tk
|
3
4
|
from tkinter import ttk
|
5
|
+
import matplotlib
|
6
|
+
import matplotlib.pyplot as plt
|
7
|
+
matplotlib.use('Agg')
|
8
|
+
from huggingface_hub import list_repo_files
|
4
9
|
|
5
10
|
from . gui_core import initiate_root
|
6
|
-
from .gui_elements import
|
11
|
+
from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo, set_default_font
|
7
12
|
|
8
13
|
try:
|
9
14
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
10
15
|
except AttributeError:
|
11
16
|
pass
|
12
|
-
|
13
|
-
def proceed_with_app_v1(root, app_name, app_func):
|
14
|
-
from .gui import gui_app
|
15
|
-
|
16
|
-
# Clear the current content frame
|
17
|
-
if hasattr(root, 'content_frame'):
|
18
|
-
for widget in root.content_frame.winfo_children():
|
19
|
-
try:
|
20
|
-
widget.destroy()
|
21
|
-
except tk.TclError as e:
|
22
|
-
print(f"Error destroying widget: {e}")
|
23
|
-
else:
|
24
|
-
root.content_frame = tk.Frame(root)
|
25
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
26
|
-
root.grid_rowconfigure(1, weight=1)
|
27
|
-
root.grid_columnconfigure(0, weight=1)
|
28
|
-
|
29
|
-
# Initialize the new app in the content frame
|
30
|
-
if app_name == "Mask":
|
31
|
-
initiate_root(root.content_frame, 'mask')
|
32
|
-
elif app_name == "Measure":
|
33
|
-
initiate_root(root.content_frame, 'measure')
|
34
|
-
elif app_name == "Classify":
|
35
|
-
initiate_root(root.content_frame, 'classify')
|
36
|
-
elif app_name == "Sequencing":
|
37
|
-
initiate_root(root.content_frame, 'sequencing')
|
38
|
-
elif app_name == "Umap":
|
39
|
-
initiate_root(root.content_frame, 'umap')
|
40
|
-
elif app_name == "Annotate":
|
41
|
-
initiate_root(root.content_frame, 'annotate')
|
42
|
-
elif app_name == "Make Masks":
|
43
|
-
initiate_root(root.content_frame, 'make_masks')
|
44
|
-
else:
|
45
|
-
raise ValueError(f"Invalid app name: {app_name}")
|
46
17
|
|
47
18
|
def proceed_with_app(root, app_name, app_func):
|
48
19
|
# Clear the current content frame
|
@@ -93,19 +64,25 @@ def parse_list(value):
|
|
93
64
|
|
94
65
|
# Usage example in your create_input_field function
|
95
66
|
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
67
|
+
from .gui_elements import set_dark_style
|
96
68
|
label_column = 0
|
97
69
|
widget_column = 1
|
98
70
|
|
71
|
+
style_out = set_dark_style(ttk.Style())
|
72
|
+
|
73
|
+
# Replace underscores with spaces and capitalize the first letter
|
74
|
+
label_text = label_text.replace('_', ' ').capitalize()
|
75
|
+
|
99
76
|
# Configure the column widths
|
100
77
|
frame.grid_columnconfigure(label_column, weight=0) # Allow the label column to expand
|
101
78
|
frame.grid_columnconfigure(widget_column, weight=1) # Allow the widget column to expand
|
102
79
|
|
103
|
-
#
|
104
|
-
label = ttk.Label(frame, text=label_text, background=
|
105
|
-
label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5)
|
80
|
+
# Create and configure the label
|
81
|
+
label = ttk.Label(frame, text=label_text, background=style_out['bg_color'], foreground=style_out['fg_color'], font=(style_out['font_family'], style_out['font_size']), anchor='e', justify='right')
|
82
|
+
label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5)
|
106
83
|
|
107
84
|
if var_type == 'entry':
|
108
|
-
var = tk.StringVar(value=default_value)
|
85
|
+
var = tk.StringVar(value=default_value)
|
109
86
|
entry = spacrEntry(frame, textvariable=var, outline=False)
|
110
87
|
entry.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
|
111
88
|
return (label, entry, var) # Return both the label and the entry, and the variable
|
@@ -157,23 +134,6 @@ def main_thread_update_function(root, q, fig_queue, canvas_widget):
|
|
157
134
|
#ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
158
135
|
while not q.empty():
|
159
136
|
message = q.get_nowait()
|
160
|
-
#clean_message = ansi_escape_pattern.sub('', message)
|
161
|
-
#if clean_message.startswith("Progress"):
|
162
|
-
# progress_label.config(text=clean_message)
|
163
|
-
#if clean_message.startswith("\rProgress"):
|
164
|
-
# progress_label.config(text=clean_message)
|
165
|
-
#elif clean_message.startswith("Successfully"):
|
166
|
-
# progress_label.config(text=clean_message)
|
167
|
-
#elif clean_message.startswith("Processing"):
|
168
|
-
# progress_label.config(text=clean_message)
|
169
|
-
#elif clean_message.startswith("scale"):
|
170
|
-
# pass
|
171
|
-
#elif clean_message.startswith("plot_cropped_arrays"):
|
172
|
-
# pass
|
173
|
-
#elif clean_message == "" or clean_message == "\r" or clean_message.strip() == "":
|
174
|
-
# pass
|
175
|
-
#else:
|
176
|
-
# print(clean_message)
|
177
137
|
except Exception as e:
|
178
138
|
print(f"Error updating GUI canvas: {e}")
|
179
139
|
finally:
|
@@ -352,11 +312,313 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
|
|
352
312
|
def set_element_size(widget):
|
353
313
|
screen_width = widget.winfo_screenwidth()
|
354
314
|
screen_height = widget.winfo_screenheight()
|
355
|
-
btn_size = screen_width
|
356
|
-
bar_size = screen_width
|
357
|
-
|
358
|
-
|
359
|
-
|
315
|
+
btn_size = screen_width // 40
|
316
|
+
bar_size = screen_width // 50
|
317
|
+
settings_width = screen_width // 5
|
318
|
+
panel_height = screen_height // 12
|
319
|
+
panel_width = settings_width
|
320
|
+
size_dict = {
|
321
|
+
'btn_size': btn_size,
|
322
|
+
'bar_size': bar_size,
|
323
|
+
'settings_width': settings_width,
|
324
|
+
'panel_width': panel_width,
|
325
|
+
'panel_height': panel_height
|
326
|
+
}
|
360
327
|
return size_dict
|
361
328
|
|
329
|
+
def convert_settings_dict_for_gui(settings):
|
330
|
+
from torchvision import models as torch_models
|
331
|
+
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
332
|
+
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
333
|
+
chans_v2 = [0, 1, 2, 3, None]
|
334
|
+
variables = {}
|
335
|
+
special_cases = {
|
336
|
+
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
337
|
+
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
338
|
+
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
339
|
+
'cell_mask_dim': ('combo', chans, None),
|
340
|
+
'cell_chann_dim': ('combo', chans, None),
|
341
|
+
'nucleus_mask_dim': ('combo', chans, None),
|
342
|
+
'nucleus_chann_dim': ('combo', chans, None),
|
343
|
+
'pathogen_mask_dim': ('combo', chans, None),
|
344
|
+
'pathogen_chann_dim': ('combo', chans, None),
|
345
|
+
'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
346
|
+
'magnification': ('combo', [20, 40, 60], 20),
|
347
|
+
'nucleus_channel': ('combo', chans_v2, None),
|
348
|
+
'cell_channel': ('combo', chans_v2, None),
|
349
|
+
'channel_of_interest': ('combo', chans_v2, None),
|
350
|
+
'pathogen_channel': ('combo', chans_v2, None),
|
351
|
+
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
352
|
+
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
353
|
+
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
354
|
+
'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
|
355
|
+
'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
|
356
|
+
'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
|
357
|
+
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
358
|
+
'model_type': ('combo', torchvision_models, 'resnet50'),
|
359
|
+
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
360
|
+
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
361
|
+
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
362
|
+
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
363
|
+
'agg_type': ('combo', ['mean', 'median'], 'mean'),
|
364
|
+
'grouping': ('combo', ['mean', 'median'], 'mean'),
|
365
|
+
'min_max': ('combo', ['allq', 'all'], 'allq'),
|
366
|
+
'transform': ('combo', ['log', 'sqrt', 'square', None], None)
|
367
|
+
}
|
368
|
+
|
369
|
+
for key, value in settings.items():
|
370
|
+
if key in special_cases:
|
371
|
+
variables[key] = special_cases[key]
|
372
|
+
elif isinstance(value, bool):
|
373
|
+
variables[key] = ('check', None, value)
|
374
|
+
elif isinstance(value, int) or isinstance(value, float):
|
375
|
+
variables[key] = ('entry', None, value)
|
376
|
+
elif isinstance(value, str):
|
377
|
+
variables[key] = ('entry', None, value)
|
378
|
+
elif value is None:
|
379
|
+
variables[key] = ('entry', None, value)
|
380
|
+
elif isinstance(value, list):
|
381
|
+
variables[key] = ('entry', None, str(value))
|
382
|
+
else:
|
383
|
+
variables[key] = ('entry', None, str(value))
|
384
|
+
return variables
|
362
385
|
|
386
|
+
def spacrFigShow(fig_queue=None):
|
387
|
+
"""
|
388
|
+
Replacement for plt.show() that queues figures instead of displaying them.
|
389
|
+
"""
|
390
|
+
fig = plt.gcf()
|
391
|
+
if fig_queue:
|
392
|
+
fig_queue.put(fig)
|
393
|
+
else:
|
394
|
+
fig.show()
|
395
|
+
plt.close(fig)
|
396
|
+
|
397
|
+
def function_gui_wrapper(function=None, settings={}, q=None, fig_queue=None, imports=1):
|
398
|
+
|
399
|
+
"""
|
400
|
+
Wraps the run_multiple_simulations function to integrate with GUI processes.
|
401
|
+
|
402
|
+
Parameters:
|
403
|
+
- settings: dict, The settings for the run_multiple_simulations function.
|
404
|
+
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
405
|
+
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
406
|
+
"""
|
407
|
+
|
408
|
+
# Temporarily override plt.show
|
409
|
+
original_show = plt.show
|
410
|
+
plt.show = lambda: spacrFigShow(fig_queue)
|
411
|
+
|
412
|
+
try:
|
413
|
+
if imports == 1:
|
414
|
+
function(settings=settings)
|
415
|
+
elif imports == 2:
|
416
|
+
function(src=settings['src'], settings=settings)
|
417
|
+
except Exception as e:
|
418
|
+
# Send the error message to the GUI via the queue
|
419
|
+
errorMessage = f"Error during processing: {e}"
|
420
|
+
q.put(errorMessage)
|
421
|
+
traceback.print_exc()
|
422
|
+
finally:
|
423
|
+
# Restore the original plt.show function
|
424
|
+
plt.show = original_show
|
425
|
+
|
426
|
+
def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
427
|
+
from .gui_utils import process_stdout_stderr
|
428
|
+
from .core import preprocess_generate_masks, generate_ml_scores, identify_masks_finetune, check_cellpose_models, analyze_recruitment, train_cellpose, compare_cellpose_masks, analyze_plaques, generate_dataset, apply_model_to_tar
|
429
|
+
from .io import generate_cellpose_train_test
|
430
|
+
from .measure import measure_crop
|
431
|
+
from .sim import run_multiple_simulations
|
432
|
+
from .deep_spacr import train_test_model
|
433
|
+
from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
|
434
|
+
process_stdout_stderr(q)
|
435
|
+
|
436
|
+
print(f'run_function_gui settings_type: {settings_type}')
|
437
|
+
|
438
|
+
if settings_type == 'mask':
|
439
|
+
function = preprocess_generate_masks
|
440
|
+
imports = 2
|
441
|
+
elif settings_type == 'measure':
|
442
|
+
function = measure_crop
|
443
|
+
imports = 1
|
444
|
+
elif settings_type == 'simulation':
|
445
|
+
function = run_multiple_simulations
|
446
|
+
imports = 1
|
447
|
+
elif settings_type == 'sequencing':
|
448
|
+
function = analyze_reads
|
449
|
+
imports = 1
|
450
|
+
elif settings_type == 'classify':
|
451
|
+
function = train_test_model
|
452
|
+
imports = 2
|
453
|
+
elif settings_type == 'train_cellpose':
|
454
|
+
function = train_cellpose
|
455
|
+
imports = 1
|
456
|
+
elif settings_type == 'ml_analyze':
|
457
|
+
function = generate_ml_scores
|
458
|
+
imports = 2
|
459
|
+
elif settings_type == 'cellpose_masks':
|
460
|
+
function = identify_masks_finetune
|
461
|
+
imports = 1
|
462
|
+
elif settings_type == 'cellpose_all':
|
463
|
+
function = check_cellpose_models
|
464
|
+
imports = 1
|
465
|
+
elif settings_type == 'map_barcodes':
|
466
|
+
function = map_barcodes_folder
|
467
|
+
imports = 2
|
468
|
+
elif settings_type == 'regression':
|
469
|
+
function = perform_regression
|
470
|
+
imports = 2
|
471
|
+
elif settings_type == 'recruitment':
|
472
|
+
function = analyze_recruitment
|
473
|
+
imports = 2
|
474
|
+
else:
|
475
|
+
raise ValueError(f"Invalid settings type: {settings_type}")
|
476
|
+
try:
|
477
|
+
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
478
|
+
except Exception as e:
|
479
|
+
q.put(f"Error during processing: {e}")
|
480
|
+
traceback.print_exc()
|
481
|
+
finally:
|
482
|
+
stop_requested.value = 1
|
483
|
+
|
484
|
+
def hide_all_settings(vars_dict, categories):
|
485
|
+
"""
|
486
|
+
Function to initially hide all settings in the GUI.
|
487
|
+
|
488
|
+
Parameters:
|
489
|
+
- categories: dict, The categories of settings with their corresponding settings.
|
490
|
+
- vars_dict: dict, The dictionary containing the settings and their corresponding widgets.
|
491
|
+
"""
|
492
|
+
|
493
|
+
if categories is None:
|
494
|
+
from .settings import categories
|
495
|
+
|
496
|
+
for category, settings in categories.items():
|
497
|
+
if any(setting in vars_dict for setting in settings):
|
498
|
+
vars_dict[category] = (None, None, tk.IntVar(value=0))
|
499
|
+
|
500
|
+
# Initially hide all settings
|
501
|
+
for setting in settings:
|
502
|
+
if setting in vars_dict:
|
503
|
+
label, widget, _ = vars_dict[setting]
|
504
|
+
label.grid_remove()
|
505
|
+
widget.grid_remove()
|
506
|
+
return vars_dict
|
507
|
+
|
508
|
+
def setup_frame(parent_frame):
|
509
|
+
from .gui_elements import set_dark_style, set_default_font
|
510
|
+
|
511
|
+
style = ttk.Style(parent_frame)
|
512
|
+
size_dict = set_element_size(parent_frame)
|
513
|
+
style_out = set_dark_style(style)
|
514
|
+
|
515
|
+
settings_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'])
|
516
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg=style_out['bg_color'])
|
517
|
+
horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], bg=style_out['bg_color'])
|
518
|
+
|
519
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
520
|
+
parent_frame.grid_rowconfigure(1, weight=0)
|
521
|
+
parent_frame.grid_columnconfigure(0, weight=0)
|
522
|
+
parent_frame.grid_columnconfigure(1, weight=1)
|
523
|
+
|
524
|
+
settings_container.grid(row=0, column=0, rowspan=2, sticky="nsew")
|
525
|
+
vertical_container.grid(row=0, column=1, sticky="nsew")
|
526
|
+
horizontal_container.grid(row=1, column=1, sticky="nsew")
|
527
|
+
|
528
|
+
# Lock the width of the horizontal_container
|
529
|
+
horizontal_container.update_idletasks() # Ensure geometry manager calculates size
|
530
|
+
fixed_width = horizontal_container.winfo_width()
|
531
|
+
parent_frame.grid_columnconfigure(1, weight=0)
|
532
|
+
horizontal_container.config(width=fixed_width)
|
533
|
+
|
534
|
+
tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
|
535
|
+
tk.Label(vertical_container, text="Vertical Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
|
536
|
+
|
537
|
+
set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container])
|
538
|
+
|
539
|
+
size = style_out['font_size'] - 2
|
540
|
+
|
541
|
+
set_default_font(parent_frame, font_name=style_out['font_family'], size=size)
|
542
|
+
|
543
|
+
return parent_frame, vertical_container, horizontal_container, settings_container
|
544
|
+
|
545
|
+
def download_hug_dataset(q, vars_dict):
|
546
|
+
dataset_repo_id = "einarolafsson/toxo_mito"
|
547
|
+
settings_repo_id = "einarolafsson/spacr_settings"
|
548
|
+
dataset_subfolder = "plate1"
|
549
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
550
|
+
|
551
|
+
# Download the dataset
|
552
|
+
try:
|
553
|
+
dataset_path = download_dataset(q, dataset_repo_id, dataset_subfolder, local_dir)
|
554
|
+
if 'src' in vars_dict:
|
555
|
+
vars_dict['src'][2].set(dataset_path)
|
556
|
+
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
557
|
+
q.put(f"Dataset downloaded to: {dataset_path}\n")
|
558
|
+
except Exception as e:
|
559
|
+
q.put(f"Failed to download dataset: {e}\n")
|
560
|
+
|
561
|
+
# Download the settings files
|
562
|
+
try:
|
563
|
+
settings_path = download_dataset(q, settings_repo_id, "", local_dir)
|
564
|
+
q.put(f"Settings downloaded to: {settings_path}\n")
|
565
|
+
except Exception as e:
|
566
|
+
q.put(f"Failed to download settings: {e}\n")
|
567
|
+
|
568
|
+
def download_dataset(q, repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
569
|
+
"""
|
570
|
+
Downloads a dataset or settings files from Hugging Face and returns the local path.
|
571
|
+
|
572
|
+
Args:
|
573
|
+
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
|
574
|
+
subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
|
575
|
+
local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
|
576
|
+
retries (int): Number of retry attempts in case of failure.
|
577
|
+
delay (int): Delay in seconds between retries.
|
578
|
+
|
579
|
+
Returns:
|
580
|
+
str: The local path to the downloaded files.
|
581
|
+
"""
|
582
|
+
if local_dir is None:
|
583
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
584
|
+
|
585
|
+
local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
|
586
|
+
if not os.path.exists(local_subfolder_dir):
|
587
|
+
os.makedirs(local_subfolder_dir)
|
588
|
+
elif len(os.listdir(local_subfolder_dir)) > 0:
|
589
|
+
q.put(f"Files already downloaded to: {local_subfolder_dir}")
|
590
|
+
return local_subfolder_dir
|
591
|
+
|
592
|
+
attempt = 0
|
593
|
+
while attempt < retries:
|
594
|
+
try:
|
595
|
+
files = list_repo_files(repo_id, repo_type="dataset")
|
596
|
+
subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
|
597
|
+
|
598
|
+
for file_name in subfolder_files:
|
599
|
+
for download_attempt in range(retries):
|
600
|
+
try:
|
601
|
+
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
602
|
+
response = requests.get(url, stream=True)
|
603
|
+
response.raise_for_status()
|
604
|
+
|
605
|
+
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
606
|
+
with open(local_file_path, 'wb') as file:
|
607
|
+
for chunk in response.iter_content(chunk_size=8192):
|
608
|
+
file.write(chunk)
|
609
|
+
q.put(f"Downloaded file: {file_name}")
|
610
|
+
break
|
611
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
612
|
+
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
613
|
+
time.sleep(delay)
|
614
|
+
else:
|
615
|
+
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
616
|
+
|
617
|
+
return local_subfolder_dir
|
618
|
+
|
619
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
620
|
+
q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
|
621
|
+
attempt += 1
|
622
|
+
time.sleep(delay)
|
623
|
+
|
624
|
+
raise Exception("Failed to download files after multiple attempts.")
|
spacr/io.py
CHANGED
@@ -588,20 +588,21 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
|
|
588
588
|
regular_expression = re.compile(regex)
|
589
589
|
images_by_key = defaultdict(list)
|
590
590
|
stack_path = os.path.join(src, 'stack')
|
591
|
+
files_processed = 0
|
591
592
|
if not os.path.exists(stack_path) or (os.path.isdir(stack_path) and len(os.listdir(stack_path)) == 0):
|
592
593
|
all_filenames = [filename for filename in os.listdir(src) if filename.endswith(img_format)]
|
593
|
-
print(f'All_files:{len(all_filenames)} in {src}')
|
594
|
+
print(f'All_files: {len(all_filenames)} in {src}')
|
594
595
|
time_ls = []
|
595
|
-
|
596
|
-
for
|
596
|
+
|
597
|
+
for idx in range(0, len(all_filenames), batch_size):
|
597
598
|
start = time.time()
|
598
|
-
batch_filenames = all_filenames[
|
599
|
-
|
599
|
+
batch_filenames = all_filenames[idx:idx+batch_size]
|
600
|
+
files_processed = 0
|
600
601
|
for filename in batch_filenames:
|
601
602
|
images_by_key = _extract_filename_metadata(batch_filenames, src, images_by_key, regular_expression, metadata_type, pick_slice, skip_mode)
|
602
|
-
|
603
|
+
|
603
604
|
if pick_slice:
|
604
|
-
for key in images_by_key:
|
605
|
+
for i, key in enumerate(images_by_key):
|
605
606
|
plate, well, field, channel, mode = key
|
606
607
|
max_intensity_slice = max(images_by_key[key], key=lambda x: np.percentile(x, 90))
|
607
608
|
mip_image = Image.fromarray(max_intensity_slice)
|
@@ -609,21 +610,19 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
|
|
609
610
|
os.makedirs(output_dir, exist_ok=True)
|
610
611
|
output_filename = f'{plate}_{well}_{field}.tif'
|
611
612
|
output_path = os.path.join(output_dir, output_filename)
|
612
|
-
|
613
|
-
if os.path.exists(output_path):
|
614
|
-
print(f'WARNING: A file with the same name already exists at location {output_filename}')
|
615
|
-
else:
|
616
|
-
mip_image.save(output_path)
|
617
|
-
|
613
|
+
files_processed += 1
|
618
614
|
stop = time.time()
|
619
615
|
duration = stop - start
|
620
616
|
time_ls.append(duration)
|
621
|
-
files_processed = processed
|
622
617
|
files_to_process = len(all_filenames)
|
623
618
|
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type='Preprocessing filenames')
|
624
619
|
|
620
|
+
if os.path.exists(output_path):
|
621
|
+
print(f'WARNING: A file with the same name already exists at location {output_filename}')
|
622
|
+
else:
|
623
|
+
mip_image.save(output_path)
|
625
624
|
else:
|
626
|
-
for key, images in images_by_key.items():
|
625
|
+
for i, (key, images) in enumerate(images_by_key.items()):
|
627
626
|
mip = np.max(np.stack(images), axis=0)
|
628
627
|
mip_image = Image.fromarray(mip)
|
629
628
|
plate, well, field, channel = key[:4]
|
@@ -631,18 +630,17 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
|
|
631
630
|
os.makedirs(output_dir, exist_ok=True)
|
632
631
|
output_filename = f'{plate}_{well}_{field}.tif'
|
633
632
|
output_path = os.path.join(output_dir, output_filename)
|
634
|
-
|
635
|
-
if os.path.exists(output_path):
|
636
|
-
print(f'WARNING: A file with the same name already exists at location {output_filename}')
|
637
|
-
else:
|
638
|
-
mip_image.save(output_path)
|
633
|
+
files_processed += 1
|
639
634
|
stop = time.time()
|
640
635
|
duration = stop - start
|
641
636
|
time_ls.append(duration)
|
642
|
-
files_processed = processed
|
643
637
|
files_to_process = len(all_filenames)
|
644
638
|
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type='Preprocessing filenames')
|
645
639
|
|
640
|
+
if os.path.exists(output_path):
|
641
|
+
print(f'WARNING: A file with the same name already exists at location {output_filename}')
|
642
|
+
else:
|
643
|
+
mip_image.save(output_path)
|
646
644
|
images_by_key.clear()
|
647
645
|
|
648
646
|
# Move original images to a new directory
|
@@ -656,6 +654,7 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
|
|
656
654
|
print(f'WARNING: A file with the same name already exists at location {move}')
|
657
655
|
else:
|
658
656
|
shutil.move(os.path.join(src, filename), move)
|
657
|
+
files_processed = 0
|
659
658
|
return
|
660
659
|
|
661
660
|
def _merge_file(chan_dirs, stack_dir, file_name):
|
@@ -975,7 +974,7 @@ def _concatenate_channel(src, channels, randomize=True, timelapse=False, batch_s
|
|
975
974
|
time_ls.append(duration)
|
976
975
|
files_processed = i+1
|
977
976
|
files_to_process = time_stack_path_lists
|
978
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=
|
977
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
|
979
978
|
stack = np.stack(stack_region)
|
980
979
|
save_loc = os.path.join(channel_stack_loc, f'{name}.npz')
|
981
980
|
np.savez(save_loc, data=stack, filenames=filenames_region)
|
@@ -1006,7 +1005,7 @@ def _concatenate_channel(src, channels, randomize=True, timelapse=False, batch_s
|
|
1006
1005
|
time_ls.append(duration)
|
1007
1006
|
files_processed = i+1
|
1008
1007
|
files_to_process = nr_files
|
1009
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=
|
1008
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
|
1010
1009
|
if (i+1) % batch_size == 0 or i+1 == nr_files:
|
1011
1010
|
unique_shapes = {arr.shape[:-1] for arr in stack_ls}
|
1012
1011
|
if len(unique_shapes) > 1:
|
@@ -1104,7 +1103,7 @@ def _normalize_img_batch(stack, channels, save_dtype, settings):
|
|
1104
1103
|
time_ls.append(duration)
|
1105
1104
|
files_processed = i+1
|
1106
1105
|
files_to_process = len(channels)
|
1107
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=
|
1106
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type=f"Normalizing")
|
1108
1107
|
|
1109
1108
|
return normalized_stack.astype(save_dtype)
|
1110
1109
|
|
@@ -1151,7 +1150,6 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
|
|
1151
1150
|
parts = file.split('_')
|
1152
1151
|
name = parts[0] + '_' + parts[1] + '_' + parts[2]
|
1153
1152
|
array = np.load(path)
|
1154
|
-
#array = np.take(array, channels, axis=2)
|
1155
1153
|
stack_region.append(array)
|
1156
1154
|
filenames_region.append(os.path.basename(path))
|
1157
1155
|
stop = time.time()
|
@@ -1159,7 +1157,7 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
|
|
1159
1157
|
time_ls.append(duration)
|
1160
1158
|
files_processed = i+1
|
1161
1159
|
files_to_process = len(time_stack_path_lists)
|
1162
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=
|
1160
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
|
1163
1161
|
stack = np.stack(stack_region)
|
1164
1162
|
|
1165
1163
|
normalized_stack = _normalize_img_batch(stack=stack,
|
@@ -1188,18 +1186,18 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
|
|
1188
1186
|
stack_ls = []
|
1189
1187
|
filenames_batch = []
|
1190
1188
|
time_ls = []
|
1189
|
+
files_processed = 0
|
1191
1190
|
for i, path in enumerate(paths):
|
1192
1191
|
start = time.time()
|
1193
1192
|
array = np.load(path)
|
1194
|
-
#array = np.take(array, channels, axis=2)
|
1195
1193
|
stack_ls.append(array)
|
1196
1194
|
filenames_batch.append(os.path.basename(path))
|
1197
1195
|
stop = time.time()
|
1198
1196
|
duration = stop - start
|
1199
1197
|
time_ls.append(duration)
|
1200
|
-
files_processed
|
1198
|
+
files_processed += 1
|
1201
1199
|
files_to_process = nr_files
|
1202
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=
|
1200
|
+
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
|
1203
1201
|
|
1204
1202
|
if (i + 1) % settings['batch_size'] == 0 or i + 1 == nr_files:
|
1205
1203
|
unique_shapes = {arr.shape[:-1] for arr in stack_ls}
|
@@ -1350,12 +1348,12 @@ def _normalize_stack(src, backgrounds=[100, 100, 100], remove_backgrounds=[False
|
|
1350
1348
|
average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
|
1351
1349
|
print(f'channels:{chan_index}/{stack.shape[-1] - 1}, arrays:{array_index + 1}/{single_channel.shape[0]}, Signal:{upper:.1f}, noise:{lower:.1f}, Signal-to-noise:{average_stnr:.1f}, Time/channel:{average_time:.2f}sec')
|
1352
1350
|
|
1353
|
-
stop = time.time()
|
1354
|
-
duration = stop - start
|
1355
|
-
time_ls.append(duration)
|
1356
|
-
files_processed = file_index + 1
|
1357
|
-
files_to_process = len(paths)
|
1358
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
|
1351
|
+
#stop = time.time()
|
1352
|
+
#duration = stop - start
|
1353
|
+
#time_ls.append(duration)
|
1354
|
+
#files_processed = file_index + 1
|
1355
|
+
#files_to_process = len(paths)
|
1356
|
+
#print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
|
1359
1357
|
|
1360
1358
|
normalized_stack[:, :, :, channel] = arr_2d_normalized
|
1361
1359
|
|
@@ -1405,12 +1403,12 @@ def _normalize_timelapse(src, lower_percentile=2, save_dtype=np.float32):
|
|
1405
1403
|
|
1406
1404
|
print(f'channels:{chan_index+1}/{stack.shape[-1]}, arrays:{array_index+1}/{single_channel.shape[0]}', end='\r')
|
1407
1405
|
|
1408
|
-
stop = time.time()
|
1409
|
-
duration = stop - start
|
1410
|
-
time_ls.append(duration)
|
1411
|
-
files_processed = file_index+1
|
1412
|
-
files_to_process = len(paths)
|
1413
|
-
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
|
1406
|
+
#stop = time.time()
|
1407
|
+
#duration = stop - start
|
1408
|
+
#time_ls.append(duration)
|
1409
|
+
#files_processed = file_index+1
|
1410
|
+
#files_to_process = len(paths)
|
1411
|
+
#print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
|
1414
1412
|
|
1415
1413
|
save_loc = os.path.join(output_fldr, f'{name}_norm_timelapse.npz')
|
1416
1414
|
np.savez(save_loc, data=normalized_stack, filenames=filenames)
|
@@ -1620,8 +1618,8 @@ def preprocess_img_data(settings):
|
|
1620
1618
|
save_dtype=np.float32,
|
1621
1619
|
settings=settings)
|
1622
1620
|
|
1623
|
-
if plot:
|
1624
|
-
|
1621
|
+
#if plot:
|
1622
|
+
# _plot_4D_arrays(src+'/norm_channel_stack', nr_npz=1, nr=nr)
|
1625
1623
|
|
1626
1624
|
return settings, src
|
1627
1625
|
|
@@ -1951,7 +1949,7 @@ def _load_and_concatenate_arrays(src, channels, cell_chann_dim, nucleus_chann_di
|
|
1951
1949
|
all_imgs = len(os.listdir(reference_folder))
|
1952
1950
|
time_ls = []
|
1953
1951
|
# Iterate through each file in the reference folder
|
1954
|
-
for filename in os.listdir(reference_folder):
|
1952
|
+
for idx, filename in enumerate(os.listdir(reference_folder)):
|
1955
1953
|
start = time.time()
|
1956
1954
|
stack_ls = []
|
1957
1955
|
if filename.endswith('.npy'):
|
@@ -2012,7 +2010,7 @@ def _load_and_concatenate_arrays(src, channels, cell_chann_dim, nucleus_chann_di
|
|
2012
2010
|
stop = time.time()
|
2013
2011
|
duration = stop - start
|
2014
2012
|
time_ls.append(duration)
|
2015
|
-
files_processed =
|
2013
|
+
files_processed = idx+1
|
2016
2014
|
files_to_process = all_imgs
|
2017
2015
|
print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Merging Arrays")
|
2018
2016
|
|
@@ -2550,7 +2548,6 @@ def _read_mask(mask_path):
|
|
2550
2548
|
mask = img_as_uint(mask)
|
2551
2549
|
return mask
|
2552
2550
|
|
2553
|
-
|
2554
2551
|
def convert_numpy_to_tiff(folder_path, limit=None):
|
2555
2552
|
"""
|
2556
2553
|
Converts all numpy files in a folder to TIFF format and saves them in a subdirectory 'tiff'.
|