spacr 1.0.5__py3-none-any.whl → 1.0.6__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/app_annotate.py CHANGED
@@ -11,7 +11,7 @@ def convert_to_number(value):
11
11
  return float(value)
12
12
  except ValueError:
13
13
  raise ValueError(f"Unable to convert '{value}' to an integer or float.")
14
-
14
+
15
15
  def initiate_annotation_app(parent_frame):
16
16
  from .gui_utils import generate_annotate_fields, annotate_app, convert_to_number
17
17
  # Set up the settings window
spacr/core.py CHANGED
@@ -238,6 +238,7 @@ def generate_cellpose_masks(src, settings, object_type):
238
238
  model_name = object_settings['model_name']
239
239
 
240
240
  cellpose_channels = _get_cellpose_channels(src, settings['nucleus_channel'], settings['pathogen_channel'], settings['cell_channel'])
241
+
241
242
  if settings['verbose']:
242
243
  print(cellpose_channels)
243
244
 
spacr/deep_spacr.py CHANGED
@@ -1,10 +1,8 @@
1
- import os, torch, time, gc, datetime, cv2
1
+ import os, torch, time, gc, datetime
2
2
  torch.backends.cudnn.benchmark = True
3
-
4
3
  import numpy as np
5
4
  import pandas as pd
6
5
  from torch.optim import Adagrad, AdamW
7
- from torch.autograd import grad
8
6
  from torch.optim.lr_scheduler import StepLR
9
7
  import torch.nn.functional as F
10
8
  import matplotlib.pyplot as plt
spacr/gui.py CHANGED
@@ -4,6 +4,7 @@ from multiprocessing import set_start_method
4
4
  from .gui_elements import spacrButton, create_menu_bar, set_dark_style
5
5
  from .gui_core import initiate_root
6
6
  from screeninfo import get_monitors
7
+ import webbrowser
7
8
 
8
9
  class MainApp(tk.Tk):
9
10
  def __init__(self, default_app=None):
@@ -22,6 +23,7 @@ class MainApp(tk.Tk):
22
23
  for monitor in get_monitors():
23
24
  if monitor.x <= x < monitor.x + monitor.width and monitor.y <= y < monitor.y + monitor.height:
24
25
  width = monitor.width
26
+ self.width = width
25
27
  height = monitor.height
26
28
  break
27
29
  else:
@@ -42,22 +44,22 @@ class MainApp(tk.Tk):
42
44
  self.main_gui_apps = {
43
45
  "Mask": (lambda frame: initiate_root(self, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
44
46
  "Measure": (lambda frame: initiate_root(self, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
45
- "Annotate": (lambda frame: initiate_root(self, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
46
- "Make Masks": (lambda frame: initiate_root(self, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
47
+ #"Annotate": (lambda frame: initiate_root(self, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
48
+ #"Make Masks": (lambda frame: initiate_root(self, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
47
49
  "Classify": (lambda frame: initiate_root(self, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
48
50
  }
49
51
 
50
52
  self.additional_gui_apps = {
51
- "Umap": (lambda frame: initiate_root(self, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
52
- "Train Cellpose": (lambda frame: initiate_root(self, 'train_cellpose'), "Train custom Cellpose models."),
53
- "ML Analyze": (lambda frame: initiate_root(self, 'ml_analyze'), "Machine learning analysis of data."),
54
- "Cellpose Masks": (lambda frame: initiate_root(self, 'cellpose_masks'), "Generate Cellpose masks."),
55
- "Cellpose All": (lambda frame: initiate_root(self, 'cellpose_all'), "Run Cellpose on all images."),
53
+ #"Umap": (lambda frame: initiate_root(self, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
54
+ #"Train Cellpose": (lambda frame: initiate_root(self, 'train_cellpose'), "Train custom Cellpose models."),
55
+ #"ML Analyze": (lambda frame: initiate_root(self, 'ml_analyze'), "Machine learning analysis of data."),
56
+ #"Cellpose Masks": (lambda frame: initiate_root(self, 'cellpose_masks'), "Generate Cellpose masks."),
57
+ #"Cellpose All": (lambda frame: initiate_root(self, 'cellpose_all'), "Run Cellpose on all images."),
56
58
  "Map Barcodes": (lambda frame: initiate_root(self, 'map_barcodes'), "Map barcodes to data."),
57
59
  "Regression": (lambda frame: initiate_root(self, 'regression'), "Perform regression analysis."),
58
- "Recruitment": (lambda frame: initiate_root(self, 'recruitment'), "Analyze recruitment data."),
60
+ #"Recruitment": (lambda frame: initiate_root(self, 'recruitment'), "Analyze recruitment data."),
59
61
  "Activation": (lambda frame: initiate_root(self, 'activation'), "Generate activation maps of computer vision models and measure channel-activation correlation."),
60
- "Plaque": (lambda frame: initiate_root(self, 'analyze_plaques'), "Analyze plaque data.")
62
+ #"Plaque": (lambda frame: initiate_root(self, 'analyze_plaques'), "Analyze plaque data.")
61
63
  }
62
64
 
63
65
  self.selected_app = tk.StringVar()
@@ -88,6 +90,13 @@ class MainApp(tk.Tk):
88
90
  set_dark_style(ttk.Style(), containers=[self.content_frame, self.inner_frame])
89
91
 
90
92
  self.create_startup_screen()
93
+
94
+ def _update_wraplength(self, event):
95
+ if self.description_label.winfo_exists():
96
+ # Use the actual width of the inner_frame as a proxy for full width
97
+ available_width = self.inner_frame.winfo_width()
98
+ if available_width > 0:
99
+ self.description_label.config(wraplength=int(available_width * 0.9)) # or 0.9
91
100
 
92
101
  def create_startup_screen(self):
93
102
  self.clear_frame(self.inner_frame)
@@ -99,18 +108,30 @@ class MainApp(tk.Tk):
99
108
  additional_buttons_frame = tk.Frame(self.inner_frame)
100
109
  additional_buttons_frame.pack(pady=10)
101
110
  set_dark_style(ttk.Style(), containers=[additional_buttons_frame])
102
-
103
- description_frame = tk.Frame(self.inner_frame, height=70)
111
+
112
+ description_frame = tk.Frame(self.inner_frame)
104
113
  description_frame.pack(fill=tk.X, pady=10)
105
- description_frame.pack_propagate(False)
114
+ description_frame.columnconfigure(0, weight=1)
115
+
106
116
  set_dark_style(ttk.Style(), containers=[description_frame])
117
+ style_out = set_dark_style(ttk.Style())
118
+ font_loader = style_out['font_loader']
119
+ font_size = style_out['font_size']
120
+
121
+ self.description_label = tk.Label( description_frame, text="", wraplength=int(self.width * 0.9), justify="center", font=font_loader.get_font(size=font_size), fg=self.color_settings['fg_color'], bg=self.color_settings['bg_color'])
122
+
123
+ # Pack it without expanding
124
+ self.description_label.pack(pady=10)
107
125
 
108
- self.description_label = tk.Label(description_frame, text="", wraplength=800, justify="center", font=('Helvetica', 12), fg=self.color_settings['fg_color'], bg=self.color_settings['bg_color'])
109
- self.description_label.pack(fill=tk.BOTH, pady=10)
110
-
111
- logo_button = spacrButton(main_buttons_frame, text="SpaCr", command=lambda: self.load_app("logo_spacr", initiate_root), icon_name="logo_spacr", size=100, show_text=False)
126
+ # Force character width and center it
127
+ self.description_label.configure(width=int(self.width * 0.5 // 7))
128
+ self.description_label.pack_configure(anchor='center')
129
+
130
+ #logo_button = spacrButton(main_buttons_frame, text="SpaCR", command=lambda: self.load_app("logo_spacr", initiate_root), icon_name="logo_spacr", size=100, show_text=False)
131
+ logo_button = spacrButton(main_buttons_frame,text="SpaCR",command=lambda: webbrowser.open_new("https://einarolafsson.github.io/spacr/tutorial/"),icon_name="logo_spacr",size=100,show_text=False)
132
+
112
133
  logo_button.grid(row=0, column=0, padx=5, pady=5)
113
- self.main_buttons[logo_button] = "SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens."
134
+ self.main_buttons[logo_button] = "spaCR provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens. Click to open the spaCR tutorial in your browser."
114
135
 
115
136
  for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
116
137
  app_func, app_desc = app_data
@@ -125,6 +146,7 @@ class MainApp(tk.Tk):
125
146
  self.additional_buttons[button] = app_desc
126
147
 
127
148
  self.update_description()
149
+ self.inner_frame.bind("<Configure>", self._update_wraplength)
128
150
 
129
151
  def update_description(self):
130
152
  for button, desc in {**self.main_buttons, **self.additional_buttons}.items():
spacr/gui_elements.py CHANGED
@@ -50,18 +50,18 @@ def create_menu_bar(root):
50
50
  gui_apps = {
51
51
  "Mask": lambda: initiate_root(root, settings_type='mask'),
52
52
  "Measure": lambda: initiate_root(root, settings_type='measure'),
53
- "Annotate": lambda: initiate_root(root, settings_type='annotate'),
54
- "Make Masks": lambda: initiate_root(root, settings_type='make_masks'),
53
+ "Annotate (Beta)": lambda: initiate_root(root, settings_type='annotate'),
54
+ #"Make Masks": lambda: initiate_root(root, settings_type='make_masks'),
55
55
  "Classify": lambda: initiate_root(root, settings_type='classify'),
56
- "Umap": lambda: initiate_root(root, settings_type='umap'),
57
- "Train Cellpose": lambda: initiate_root(root, settings_type='train_cellpose'),
56
+ #"Umap": lambda: initiate_root(root, settings_type='umap'),
57
+ #"Train Cellpose": lambda: initiate_root(root, settings_type='train_cellpose'),
58
58
  "ML Analyze": lambda: initiate_root(root, settings_type='ml_analyze'),
59
- "Cellpose Masks": lambda: initiate_root(root, settings_type='cellpose_masks'),
60
- "Cellpose All": lambda: initiate_root(root, settings_type='cellpose_all'),
59
+ #"Cellpose Masks": lambda: initiate_root(root, settings_type='cellpose_masks'),
60
+ #"Cellpose All": lambda: initiate_root(root, settings_type='cellpose_all'),
61
61
  "Map Barcodes": lambda: initiate_root(root, settings_type='map_barcodes'),
62
62
  "Regression": lambda: initiate_root(root, settings_type='regression'),
63
63
  "Activation": lambda: initiate_root(root, settings_type='activation'),
64
- "Recruitment": lambda: initiate_root(root, settings_type='recruitment')
64
+ "Recruitment (Beta)": lambda: initiate_root(root, settings_type='recruitment')
65
65
  }
66
66
 
67
67
  # Create the menu bar
spacr/io.py CHANGED
@@ -1,4 +1,4 @@
1
- import os, re, sqlite3, gc, torch, time, random, shutil, cv2, tarfile, cellpose, glob, queue, tifffile, czifile, atexit, datetime
1
+ import os, re, sqlite3, gc, torch, time, random, shutil, cv2, tarfile, cellpose, glob, queue, tifffile, czifile, atexit, datetime, readlif
2
2
  import numpy as np
3
3
  import pandas as pd
4
4
  from PIL import Image, ImageOps
@@ -22,7 +22,6 @@ import seaborn as sns
22
22
  from nd2reader import ND2Reader
23
23
  from torchvision import transforms
24
24
  from sklearn.model_selection import train_test_split
25
- import readlif
26
25
  from pylibCZIrw import czi as pyczi
27
26
 
28
27
  def process_non_tif_non_2D_images(folder):
@@ -1133,6 +1132,7 @@ def _normalize_img_batch(stack, channels, save_dtype, settings):
1133
1132
 
1134
1133
  def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={}):
1135
1134
  from .utils import print_progress
1135
+ from .plot import plot_arrays
1136
1136
 
1137
1137
  """
1138
1138
  Concatenates and normalizes channel data from multiple files and saves the normalized data.
@@ -1155,6 +1155,8 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1155
1155
  """
1156
1156
 
1157
1157
  channels = [item for item in channels if item is not None]
1158
+
1159
+ print(f"Generating concatenated and normalized channel data for channels: {channels}")
1158
1160
 
1159
1161
  paths = []
1160
1162
  time_ls = []
@@ -1175,7 +1177,7 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1175
1177
  name = parts[0] + '_' + parts[1] + '_' + parts[2]
1176
1178
  array = np.load(path)
1177
1179
  stack_region.append(array)
1178
- filenames_region.append(os.path.basename(path))
1180
+ filenames_region.append(os.path.basename(path))
1179
1181
  stop = time.time()
1180
1182
  duration = stop - start
1181
1183
  time_ls.append(duration)
@@ -1193,6 +1195,10 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1193
1195
 
1194
1196
  save_loc = os.path.join(output_fldr, f'{name}_norm_timelapse.npz')
1195
1197
  np.savez(save_loc, data=normalized_stack, filenames=filenames_region)
1198
+
1199
+ if i == 0:
1200
+ plot_arrays(save_loc, settings['figuresize'], settings['cmap'], nr=settings['nr'], normalize=False)
1201
+
1196
1202
  print(save_loc)
1197
1203
  del stack, normalized_stack
1198
1204
  except Exception as e:
@@ -1250,7 +1256,11 @@ def concatenate_and_normalize(src, channels, save_dtype=np.float32, settings={})
1250
1256
  normalized_stack = normalized_stack[..., channels]
1251
1257
 
1252
1258
  save_loc = os.path.join(output_fldr, f'stack_{batch_index}_norm.npz')
1253
- np.savez(save_loc, data=normalized_stack, filenames=filenames_batch)
1259
+ np.savez(save_loc, data=normalized_stack, filenames=filenames_batch)
1260
+ if batch_index == 0:
1261
+ print(f"plotting: {save_loc}")
1262
+ plot_arrays(save_loc, settings['figuresize'], settings['cmap'], nr=settings['nr'], normalize=False)
1263
+
1254
1264
  batch_index += 1
1255
1265
  del stack, normalized_stack
1256
1266
  stack_ls = []
spacr/plot.py CHANGED
@@ -760,7 +760,63 @@ def _filter_objects_in_plot(stack, cell_mask_dim, nucleus_mask_dim, pathogen_mas
760
760
 
761
761
  return stack
762
762
 
763
+
763
764
  def plot_arrays(src, figuresize=10, cmap='inferno', nr=1, normalize=True, q1=1, q2=99):
765
+ """
766
+ Plot randomly selected arrays from a given directory or a single .npz/.npy file.
767
+
768
+ Parameters:
769
+ - src (str): The directory path or file path containing the arrays.
770
+ - figuresize (int): The size of the figure (default: 10).
771
+ - cmap (str): The colormap to use for displaying the arrays (default: 'inferno').
772
+ - nr (int): The number of arrays to plot (default: 1).
773
+ - normalize (bool): Whether to normalize the arrays (default: True).
774
+ - q1 (int): The lower percentile for normalization (default: 1).
775
+ - q2 (int): The upper percentile for normalization (default: 99).
776
+ """
777
+ from .utils import normalize_to_dtype
778
+
779
+ mask_cmap = random_cmap()
780
+ paths = []
781
+
782
+ if src.endswith('.npz') or src.endswith('.npy'):
783
+ paths = [src]
784
+ else:
785
+ paths = [os.path.join(src, f) for f in os.listdir(src) if f.endswith(('.npy', '.npz'))]
786
+ paths = random.sample(paths, min(nr, len(paths)))
787
+
788
+ for path in paths:
789
+ print(f'Image path: {path}')
790
+ if path.endswith('.npz'):
791
+ with np.load(path) as data:
792
+ key = list(data.keys())[0] # assume first key
793
+ img = data[key][0] # get first image in batch
794
+ else:
795
+ img = np.load(path)
796
+
797
+ if normalize:
798
+ img = normalize_to_dtype(array=img, p1=q1, p2=q2)
799
+
800
+ if img.ndim == 3:
801
+ array_nr = img.shape[2]
802
+ fig, axs = plt.subplots(1, array_nr, figsize=(figuresize, figuresize))
803
+ if array_nr == 1:
804
+ axs = [axs] # ensure iterable
805
+ for channel in range(array_nr):
806
+ i = img[:, :, channel]
807
+ axs[channel].imshow(i, cmap=plt.get_cmap(cmap))
808
+ axs[channel].set_title(f'Channel {channel}', size=24)
809
+ axs[channel].axis('off')
810
+ else:
811
+ fig, ax = plt.subplots(1, 1, figsize=(figuresize, figuresize))
812
+ ax.imshow(img, cmap=plt.get_cmap(cmap))
813
+ ax.set_title('Channel 0', size=24)
814
+ ax.axis('off')
815
+
816
+ fig.tight_layout()
817
+ plt.show()
818
+
819
+ def plot_arrays_v1(src, figuresize=10, cmap='inferno', nr=1, normalize=True, q1=1, q2=99):
764
820
  """
765
821
  Plot randomly selected arrays from a given directory.
766
822
 
@@ -780,6 +836,7 @@ def plot_arrays(src, figuresize=10, cmap='inferno', nr=1, normalize=True, q1=1,
780
836
 
781
837
  mask_cmap = random_cmap()
782
838
  paths = []
839
+
783
840
  for file in os.listdir(src):
784
841
  if file.endswith('.npy'):
785
842
  path = os.path.join(src, file)
Binary file
Binary file
Binary file