spacr 0.2.32__py3-none-any.whl → 0.2.45__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/gui_utils.py CHANGED
@@ -1,48 +1,19 @@
1
- import os, io, sys, ast, ctypes, re, ast, sqlite3
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 spacrLabel, spacrCheckbutton, AnnotateApp, spacrEntry, spacrCheck, spacrCombo, set_default_font
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
- # Right-align the label text and the label itself
104
- label = ttk.Label(frame, text=label_text, background="black", foreground="white", anchor='e', justify='right')
105
- label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5) # Align label to the right
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) # Set 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:
@@ -349,4 +309,302 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
349
309
  # Call load_images after setting up the root window
350
310
  app.load_images()
351
311
 
312
+ def set_element_size(widget):
313
+ screen_width = widget.winfo_screenwidth()
314
+ screen_height = widget.winfo_screenheight()
315
+ btn_size = screen_width // 40
316
+ bar_size = screen_width // 50
317
+ settings_width = screen_width // 6
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
+ }
327
+ return size_dict
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
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):
352
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
+ style = ttk.Style(parent_frame)
511
+ size_dict = set_element_size(parent_frame)
512
+
513
+ settings_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['settings_width'])
514
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL)
515
+ horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'])
516
+
517
+ parent_frame.grid_rowconfigure(0, weight=1)
518
+ parent_frame.grid_rowconfigure(1, weight=0)
519
+ parent_frame.grid_columnconfigure(0, weight=0) # Change this line
520
+ parent_frame.grid_columnconfigure(1, weight=1) # Change this line
521
+
522
+ settings_container.grid(row=0, column=0, rowspan=2, sticky="nsew") # Change this line
523
+ vertical_container.grid(row=0, column=1, sticky="nsew") # Change this line
524
+ horizontal_container.grid(row=1, column=1, sticky="ew") # Change this line
525
+
526
+ set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container])
527
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
528
+
529
+ return parent_frame, vertical_container, horizontal_container, settings_container
530
+
531
+ def download_hug_dataset(q, vars_dict):
532
+ dataset_repo_id = "einarolafsson/toxo_mito"
533
+ settings_repo_id = "einarolafsson/spacr_settings"
534
+ dataset_subfolder = "plate1"
535
+ local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
536
+
537
+ # Download the dataset
538
+ try:
539
+ dataset_path = download_dataset(q, dataset_repo_id, dataset_subfolder, local_dir)
540
+ if 'src' in vars_dict:
541
+ vars_dict['src'][2].set(dataset_path)
542
+ q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
543
+ q.put(f"Dataset downloaded to: {dataset_path}\n")
544
+ except Exception as e:
545
+ q.put(f"Failed to download dataset: {e}\n")
546
+
547
+ # Download the settings files
548
+ try:
549
+ settings_path = download_dataset(q, settings_repo_id, "", local_dir)
550
+ q.put(f"Settings downloaded to: {settings_path}\n")
551
+ except Exception as e:
552
+ q.put(f"Failed to download settings: {e}\n")
553
+
554
+ def download_dataset(q, repo_id, subfolder, local_dir=None, retries=5, delay=5):
555
+ """
556
+ Downloads a dataset or settings files from Hugging Face and returns the local path.
557
+
558
+ Args:
559
+ repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito' or 'einarolafsson/spacr_settings').
560
+ subfolder (str): The subfolder path within the repository (e.g., 'plate1' or the settings subfolder).
561
+ local_dir (str): The local directory where the files will be saved. Defaults to the user's home directory.
562
+ retries (int): Number of retry attempts in case of failure.
563
+ delay (int): Delay in seconds between retries.
564
+
565
+ Returns:
566
+ str: The local path to the downloaded files.
567
+ """
568
+ if local_dir is None:
569
+ local_dir = os.path.join(os.path.expanduser("~"), "datasets")
570
+
571
+ local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
572
+ if not os.path.exists(local_subfolder_dir):
573
+ os.makedirs(local_subfolder_dir)
574
+ elif len(os.listdir(local_subfolder_dir)) > 0:
575
+ q.put(f"Files already downloaded to: {local_subfolder_dir}")
576
+ return local_subfolder_dir
577
+
578
+ attempt = 0
579
+ while attempt < retries:
580
+ try:
581
+ files = list_repo_files(repo_id, repo_type="dataset")
582
+ subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
583
+
584
+ for file_name in subfolder_files:
585
+ for download_attempt in range(retries):
586
+ try:
587
+ url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
588
+ response = requests.get(url, stream=True)
589
+ response.raise_for_status()
590
+
591
+ local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
592
+ with open(local_file_path, 'wb') as file:
593
+ for chunk in response.iter_content(chunk_size=8192):
594
+ file.write(chunk)
595
+ q.put(f"Downloaded file: {file_name}")
596
+ break
597
+ except (requests.HTTPError, requests.Timeout) as e:
598
+ q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
599
+ time.sleep(delay)
600
+ else:
601
+ raise Exception(f"Failed to download {file_name} after multiple attempts.")
602
+
603
+ return local_subfolder_dir
604
+
605
+ except (requests.HTTPError, requests.Timeout) as e:
606
+ q.put(f"Error downloading files: {e}. Retrying in {delay} seconds...")
607
+ attempt += 1
608
+ time.sleep(delay)
609
+
610
+ 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
- processed = 0
596
+
596
597
  for i in range(0, len(all_filenames), batch_size):
597
598
  start = time.time()
598
599
  batch_filenames = all_filenames[i:i+batch_size]
599
- processed += len(batch_filenames)
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)
@@ -614,16 +615,8 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
614
615
  print(f'WARNING: A file with the same name already exists at location {output_filename}')
615
616
  else:
616
617
  mip_image.save(output_path)
617
-
618
- stop = time.time()
619
- duration = stop - start
620
- time_ls.append(duration)
621
- files_processed = processed
622
- files_to_process = len(all_filenames)
623
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type='Preprocessing filenames')
624
-
625
618
  else:
626
- for key, images in images_by_key.items():
619
+ for i, (key, images) in enumerate(images_by_key.items()):
627
620
  mip = np.max(np.stack(images), axis=0)
628
621
  mip_image = Image.fromarray(mip)
629
622
  plate, well, field, channel = key[:4]
@@ -636,14 +629,13 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
636
629
  print(f'WARNING: A file with the same name already exists at location {output_filename}')
637
630
  else:
638
631
  mip_image.save(output_path)
639
- stop = time.time()
640
- duration = stop - start
641
- time_ls.append(duration)
642
- files_processed = processed
643
- files_to_process = len(all_filenames)
644
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type='Preprocessing filenames')
645
-
646
632
  images_by_key.clear()
633
+ stop = time.time()
634
+ duration = stop - start
635
+ time_ls.append(duration)
636
+ files_processed += len(batch_filenames)
637
+ files_to_process = len(all_filenames)
638
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type='Preprocessing filenames')
647
639
 
648
640
  # Move original images to a new directory
649
641
  valid_exts = [img_format]
@@ -656,6 +648,7 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
656
648
  print(f'WARNING: A file with the same name already exists at location {move}')
657
649
  else:
658
650
  shutil.move(os.path.join(src, filename), move)
651
+ files_processed = 0
659
652
  return
660
653
 
661
654
  def _merge_file(chan_dirs, stack_dir, file_name):
@@ -975,7 +968,7 @@ def _concatenate_channel(src, channels, randomize=True, timelapse=False, batch_s
975
968
  time_ls.append(duration)
976
969
  files_processed = i+1
977
970
  files_to_process = time_stack_path_lists
978
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=None, batch_size=None, operation_type="Concatinating")
971
+ #print_progress(files_processed, files_to_process, n_jobs=1, time_ls=None, batch_size=None, operation_type="Concatinating")
979
972
  stack = np.stack(stack_region)
980
973
  save_loc = os.path.join(channel_stack_loc, f'{name}.npz')
981
974
  np.savez(save_loc, data=stack, filenames=filenames_region)
@@ -1104,7 +1097,7 @@ def _normalize_img_batch(stack, channels, save_dtype, settings):
1104
1097
  time_ls.append(duration)
1105
1098
  files_processed = i+1
1106
1099
  files_to_process = len(channels)
1107
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=None, batch_size=None, operation_type=f"Normalizing: Channel: {channel}")
1100
+ #print_progress(files_processed, files_to_process, n_jobs=1, time_ls=None, batch_size=None, operation_type=f"Normalizing: Channel: {channel}")
1108
1101
 
1109
1102
  return normalized_stack.astype(save_dtype)
1110
1103
 
@@ -1191,7 +1184,6 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1191
1184
  for i, path in enumerate(paths):
1192
1185
  start = time.time()
1193
1186
  array = np.load(path)
1194
- #array = np.take(array, channels, axis=2)
1195
1187
  stack_ls.append(array)
1196
1188
  filenames_batch.append(os.path.basename(path))
1197
1189
  stop = time.time()
@@ -1199,7 +1191,7 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1199
1191
  time_ls.append(duration)
1200
1192
  files_processed = i+1
1201
1193
  files_to_process = nr_files
1202
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=None, batch_size=None, operation_type="Concatinating")
1194
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
1203
1195
 
1204
1196
  if (i + 1) % settings['batch_size'] == 0 or i + 1 == nr_files:
1205
1197
  unique_shapes = {arr.shape[:-1] for arr in stack_ls}
@@ -1350,12 +1342,12 @@ def _normalize_stack(src, backgrounds=[100, 100, 100], remove_backgrounds=[False
1350
1342
  average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
1351
1343
  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
1344
 
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")
1345
+ #stop = time.time()
1346
+ #duration = stop - start
1347
+ #time_ls.append(duration)
1348
+ #files_processed = file_index + 1
1349
+ #files_to_process = len(paths)
1350
+ #print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
1359
1351
 
1360
1352
  normalized_stack[:, :, :, channel] = arr_2d_normalized
1361
1353
 
@@ -1405,12 +1397,12 @@ def _normalize_timelapse(src, lower_percentile=2, save_dtype=np.float32):
1405
1397
 
1406
1398
  print(f'channels:{chan_index+1}/{stack.shape[-1]}, arrays:{array_index+1}/{single_channel.shape[0]}', end='\r')
1407
1399
 
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")
1400
+ #stop = time.time()
1401
+ #duration = stop - start
1402
+ #time_ls.append(duration)
1403
+ #files_processed = file_index+1
1404
+ #files_to_process = len(paths)
1405
+ #print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Normalizing")
1414
1406
 
1415
1407
  save_loc = os.path.join(output_fldr, f'{name}_norm_timelapse.npz')
1416
1408
  np.savez(save_loc, data=normalized_stack, filenames=filenames)
@@ -2550,7 +2542,6 @@ def _read_mask(mask_path):
2550
2542
  mask = img_as_uint(mask)
2551
2543
  return mask
2552
2544
 
2553
-
2554
2545
  def convert_numpy_to_tiff(folder_path, limit=None):
2555
2546
  """
2556
2547
  Converts all numpy files in a folder to TIFF format and saves them in a subdirectory 'tiff'.
spacr/measure.py CHANGED
@@ -941,14 +941,11 @@ def measure_crop(settings):
941
941
  settings = get_measure_crop_settings(settings)
942
942
  settings = measure_test_mode(settings)
943
943
 
944
- #src_fldr = settings['src']
945
- #if not os.path.basename(src_fldr).endswith('merged'):
946
- # settings['src'] = os.path.join(src_fldr, 'merged')
947
- # print(f"changed src to {src_fldr}")
948
-
949
- #if not os.path.exists(settings['src']):
950
- # print(f'src: {settings["src"]} does not exist')
951
- # return
944
+ src_fldr = settings['src']
945
+ if not os.path.basename(src_fldr).endswith('merged'):
946
+ print(f"WARNING: Source folder, settings: src: {src_fldr} should end with 'merged'")
947
+ src_fldr = os.path.join(src_fldr, 'merged')
948
+ print(f"Changed source folder to: {src_fldr}")
952
949
 
953
950
  if settings['cell_mask_dim'] is None:
954
951
  settings['include_uninfected'] = True
@@ -1009,7 +1006,7 @@ def measure_crop(settings):
1009
1006
  time.sleep(1)
1010
1007
  files_processed = len(time_ls)
1011
1008
  files_to_process = len(files)
1012
- print_progress(files_processed, files_to_process, n_jobs, time_ls=None)
1009
+ print_progress(files_processed, files_to_process, n_jobs, time_ls=time_ls, operation_type='Measure and Crop')
1013
1010
  result.get()
1014
1011
 
1015
1012
  if settings['representative_images']: