spacr 0.1.1__py3-none-any.whl → 0.1.7__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/measure.py CHANGED
@@ -16,12 +16,6 @@ from skimage.util import img_as_bool
16
16
 
17
17
  from .logger import log_function_call
18
18
 
19
- #from .io import create_database, _save_settings_to_db
20
- #from .timelapse import _timelapse_masks_to_gif, _scmovie
21
- #from .plot import _plot_cropped_arrays, _save_scimg_plot
22
- #from .utils import _merge_overlapping_objects, _filter_object, _relabel_parent_with_child_labels, _exclude_objects
23
- #from .utils import _merge_and_save_to_database, _crop_center, _find_bounding_box, _generate_names, _get_percentiles, normalize_to_dtype, _map_wells_png, _list_endpoint_subdirectories, _generate_representative_images
24
-
25
19
  def get_components(cell_mask, nucleus_mask, pathogen_mask):
26
20
  """
27
21
  Get the components (nucleus and pathogens) for each cell in the given masks.
@@ -617,10 +611,23 @@ def _measure_crop_core(index, time_ls, file, settings):
617
611
  start = time.time()
618
612
  try:
619
613
  source_folder = os.path.dirname(settings['input_folder'])
614
+ #if not os.path.basename(source_folder).endswith('merged'):
615
+ # source_folder = os.path.join(source_folder, 'merged')
616
+ # print(f'changed source_folder to {source_folder}')
617
+
618
+ #if not os.path.exists(source_folder):
619
+ # return
620
+
620
621
  file_name = os.path.splitext(file)[0]
621
622
  data = np.load(os.path.join(settings['input_folder'], file))
622
-
623
623
  data_type = data.dtype
624
+ if data_type not in ['uint8','uint16']:
625
+ data_type_before = data_type
626
+ data = data.astype(np.uint16)
627
+ data_type = data.dtype
628
+ if settings['verbose']:
629
+ print(f'Converted data from {data_type_before} to {data_type}')
630
+
624
631
  if settings['save_measurements']:
625
632
  os.makedirs(source_folder+'/measurements', exist_ok=True)
626
633
  _create_database(source_folder+'/measurements/measurements.db')
@@ -750,6 +757,15 @@ def _measure_crop_core(index, time_ls, file, settings):
750
757
  if isinstance(settings['crop_mode'], list):
751
758
  crop_ls = settings['crop_mode']
752
759
  size_ls = settings['png_size']
760
+
761
+ if isinstance(size_ls[0], int):
762
+ size_ls = [size_ls]
763
+ if len(crop_ls) > 1 and len(size_ls) == 1:
764
+ size_ls = size_ls * len(crop_ls)
765
+
766
+ if len(crop_ls) != len(size_ls):
767
+ print(f"Setting: size_ls: {settings['png_size']} should be a list of integers, or a list of lists of integers if crop_ls: {settings['crop_mode']} has multiple elements")
768
+
753
769
  for crop_idx, crop_mode in enumerate(crop_ls):
754
770
  width, height = size_ls[crop_idx]
755
771
  if crop_mode == 'cell':
@@ -816,7 +832,6 @@ def _measure_crop_core(index, time_ls, file, settings):
816
832
  png_channels = normalize_to_dtype(png_channels, settings['normalize'][0], settings['normalize'][1], percentile_list=percentile_list)
817
833
  else:
818
834
  png_channels = normalize_to_dtype(png_channels, 0, 100)
819
-
820
835
  os.makedirs(png_folder, exist_ok=True)
821
836
 
822
837
  if png_channels.shape[2] == 2:
@@ -926,9 +941,14 @@ def measure_crop(settings):
926
941
  settings = get_measure_crop_settings(settings)
927
942
  settings = measure_test_mode(settings)
928
943
 
929
- if not os.path.exists(settings['input_folder']):
930
- print(f"Error: {settings['input_folder']} does not exist")
931
- return
944
+ #src_fldr = settings['input_folder']
945
+ #if not os.path.basename(src_fldr).endswith('merged'):
946
+ # settings['input_folder'] = os.path.join(src_fldr, 'merged')
947
+ # print(f"changed input_folder to {src_fldr}")
948
+
949
+ #if not os.path.exists(settings['input_folder']):
950
+ # print(f'input_folder: {settings["input_folder"]} does not exist')
951
+ # return
932
952
 
933
953
  if settings['cell_mask_dim'] is None:
934
954
  settings['include_uninfected'] = True
@@ -978,12 +998,12 @@ def measure_crop(settings):
978
998
  _save_settings_to_db(settings)
979
999
 
980
1000
  files = [f for f in os.listdir(settings['input_folder']) if f.endswith('.npy')]
981
- max_workers = settings['max_workers'] or mp.cpu_count()-4
982
- print(f'using {max_workers} cpu cores')
1001
+ n_job = settings['n_job'] or mp.cpu_count()-4
1002
+ print(f'using {n_job} cpu cores')
983
1003
 
984
1004
  with mp.Manager() as manager:
985
1005
  time_ls = manager.list()
986
- with mp.Pool(max_workers) as pool:
1006
+ with mp.Pool(n_job) as pool:
987
1007
  result = pool.starmap_async(_measure_crop_core, [(index, time_ls, file, settings) for index, file in enumerate(files)])
988
1008
 
989
1009
  # Track progress in the main process
@@ -992,7 +1012,7 @@ def measure_crop(settings):
992
1012
  files_processed = len(time_ls)
993
1013
  files_to_process = len(files)
994
1014
  average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
995
- time_left = (((files_to_process-files_processed)*average_time)/max_workers)/60
1015
+ time_left = (((files_to_process-files_processed)*average_time)/n_job)/60
996
1016
  print(f'Progress: {files_processed}/{files_to_process} Time/img {average_time:.3f}sec, Time Remaining {time_left:.3f} min.', end='\r', flush=True)
997
1017
  result.get()
998
1018
 
spacr/measure_app.py ADDED
@@ -0,0 +1,246 @@
1
+ import sys, traceback, matplotlib, ctypes
2
+ import tkinter as tk
3
+ from tkinter import ttk, scrolledtext
4
+ from matplotlib.figure import Figure
5
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
6
+ matplotlib.use('Agg') # Use the non-GUI Agg backend
7
+ from multiprocessing import Process, Queue, Value
8
+ from tkinter import filedialog
9
+
10
+ try:
11
+ ctypes.windll.shcore.SetProcessDpiAwareness(True)
12
+ except AttributeError:
13
+ pass
14
+
15
+ from .logger import log_function_call
16
+ from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, ToggleSwitch, ToolTip
17
+ from .gui_utils import process_stdout_stderr, set_dark_style, set_default_font, generate_fields, main_thread_update_function, create_menu_bar
18
+ from .gui_utils import measure_variables, measure_crop_wrapper, clear_canvas, check_measure_gui_settings, read_settings_from_csv, update_settings_from_csv, style_text_boxes
19
+
20
+ thread_control = {"run_thread": None, "stop_requested": False}
21
+
22
+ def import_settings(scrollable_frame):
23
+ global vars_dict
24
+
25
+ csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
26
+ csv_settings = read_settings_from_csv(csv_file_path)
27
+ variables = measure_variables()
28
+ new_settings = update_settings_from_csv(variables, csv_settings)
29
+ vars_dict = generate_fields(new_settings, scrollable_frame)
30
+
31
+ def toggle_test_mode():
32
+ global vars_dict
33
+ current_state = vars_dict['test_mode'][2].get()
34
+ new_state = not current_state
35
+ vars_dict['test_mode'][2].set(new_state)
36
+ if new_state:
37
+ test_mode_button.config(bg="blue")
38
+ else:
39
+ test_mode_button.config(bg="gray")
40
+
41
+ def toggle_advanced_settings():
42
+ global vars_dict
43
+
44
+ timelapse_settings = ['timelapse', 'timelapse_objects']
45
+ misc_settings = ['representative_images', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'dialate_png_ratios']
46
+ opperational_settings = ['max_workers','experiment','cells','cell_loc','pathogens','pathogen_loc','treatments','treatment_loc','channel_of_interest','compartments','measurement','nr_imgs', 'um_per_pixel']
47
+
48
+ advanced_settings = timelapse_settings+misc_settings+opperational_settings
49
+
50
+ # Toggle visibility of advanced settings
51
+ for setting in advanced_settings:
52
+ label, widget, var = vars_dict[setting]
53
+ if advanced_var.get() is False:
54
+ label.grid_remove() # Hide the label
55
+ widget.grid_remove() # Hide the widget
56
+ else:
57
+ label.grid() # Show the label
58
+ widget.grid() # Show the widget
59
+
60
+ #@log_function_call
61
+ def run_measure_gui(q, fig_queue, stop_requested):
62
+ global vars_dict
63
+ process_stdout_stderr(q)
64
+ try:
65
+ print('hello')
66
+ settings = check_measure_gui_settings(vars_dict)
67
+ measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
68
+ except Exception as e:
69
+ q.put(f"Error during processing: {e}")
70
+ traceback.print_exc()
71
+ finally:
72
+ stop_requested.value = 1
73
+
74
+ #@log_function_call
75
+ def start_process(q, fig_queue):
76
+ global thread_control
77
+ if thread_control.get("run_thread") is not None:
78
+ initiate_abort()
79
+
80
+ stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
81
+ thread_control["stop_requested"] = stop_requested
82
+ thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
83
+ thread_control["run_thread"].start()
84
+
85
+ #@log_function_call
86
+ def initiate_abort():
87
+ global thread_control
88
+ if thread_control.get("stop_requested") is not None:
89
+ thread_control["stop_requested"].value = 1
90
+
91
+ if thread_control.get("run_thread") is not None:
92
+ thread_control["run_thread"].join(timeout=5)
93
+ if thread_control["run_thread"].is_alive():
94
+ thread_control["run_thread"].terminate()
95
+ thread_control["run_thread"] = None
96
+
97
+ #@log_function_call
98
+ def initiate_measure_root(parent_frame):
99
+ global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
100
+
101
+ style = ttk.Style(parent_frame)
102
+ set_dark_style(style)
103
+ style_text_boxes(style)
104
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
105
+
106
+ parent_frame.configure(bg='black')
107
+ parent_frame.grid_rowconfigure(0, weight=1)
108
+ parent_frame.grid_columnconfigure(0, weight=1)
109
+
110
+ fig_queue = Queue()
111
+
112
+ # Initialize after_tasks if not already done
113
+ if not hasattr(parent_frame, 'after_tasks'):
114
+ parent_frame.after_tasks = []
115
+
116
+ def _process_fig_queue():
117
+ global canvas
118
+ try:
119
+ while not fig_queue.empty():
120
+ clear_canvas(canvas)
121
+ fig = fig_queue.get_nowait()
122
+ for ax in fig.get_axes():
123
+ ax.set_xticks([]) # Remove x-axis ticks
124
+ ax.set_yticks([]) # Remove y-axis ticks
125
+ ax.xaxis.set_visible(False) # Hide the x-axis
126
+ ax.yaxis.set_visible(False) # Hide the y-axis
127
+ fig.tight_layout()
128
+ fig.set_facecolor('black')
129
+ canvas.figure = fig
130
+ fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
131
+ fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
132
+ canvas.draw_idle()
133
+ except Exception as e:
134
+ traceback.print_exc()
135
+ finally:
136
+ after_id = canvas_widget.after(100, _process_fig_queue)
137
+ parent_frame.after_tasks.append(after_id)
138
+
139
+ def _process_console_queue():
140
+ while not q.empty():
141
+ message = q.get_nowait()
142
+ console_output.insert(tk.END, message)
143
+ console_output.see(tk.END)
144
+ after_id = console_output.after(100, _process_console_queue)
145
+ parent_frame.after_tasks.append(after_id)
146
+
147
+ # Clear previous content if any
148
+ for widget in parent_frame.winfo_children():
149
+ widget.destroy()
150
+
151
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
152
+ vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
153
+ parent_frame.grid_rowconfigure(0, weight=1)
154
+ parent_frame.grid_columnconfigure(0, weight=1)
155
+
156
+ # Settings Section
157
+ settings_frame = tk.Frame(vertical_container, bg='black')
158
+ vertical_container.add(settings_frame, stretch="always")
159
+ settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
160
+ settings_label.grid(row=0, column=0, pady=10, padx=10)
161
+ scrollable_frame = ScrollableFrame(settings_frame, width=500)
162
+ scrollable_frame.grid(row=1, column=0, sticky="nsew")
163
+ settings_frame.grid_rowconfigure(1, weight=1)
164
+ settings_frame.grid_columnconfigure(0, weight=1)
165
+
166
+ # Create advanced settings checkbox
167
+ advanced_var = tk.BooleanVar(value=False)
168
+ advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
169
+ advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
170
+ variables = measure_variables()
171
+ vars_dict = generate_fields(variables, scrollable_frame)
172
+ toggle_advanced_settings()
173
+ vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
174
+
175
+ # Button section
176
+ test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
177
+ test_mode_button.grid(row=47, column=1, pady=10, padx=10)
178
+ import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
179
+ import_btn.grid(row=47, column=0, pady=20, padx=20)
180
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
181
+ run_button.grid(row=45, column=0, pady=20, padx=20)
182
+ abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
183
+ abort_button.grid(row=45, column=1, pady=20, padx=20)
184
+ progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
185
+ progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
186
+
187
+ # Plot Canvas Section
188
+ plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
189
+ vertical_container.add(plot_frame, stretch="always")
190
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
191
+ plot = figure.add_subplot(111)
192
+ plot.plot([], [])
193
+ plot.axis('off')
194
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
195
+ canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
196
+ canvas_widget = canvas.get_tk_widget()
197
+ plot_frame.add(canvas_widget, stretch="always")
198
+ canvas.draw()
199
+ canvas.figure = figure
200
+
201
+ # Console Section
202
+ console_frame = tk.Frame(vertical_container, bg='black')
203
+ vertical_container.add(console_frame, stretch="always")
204
+ console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
205
+ console_label.grid(row=0, column=0, pady=10, padx=10)
206
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
207
+ console_output.grid(row=1, column=0, sticky="nsew")
208
+ console_frame.grid_rowconfigure(1, weight=1)
209
+ console_frame.grid_columnconfigure(0, weight=1)
210
+
211
+ q = Queue()
212
+ sys.stdout = StdoutRedirector(console_output)
213
+ sys.stderr = StdoutRedirector(console_output)
214
+
215
+ _process_console_queue()
216
+ _process_fig_queue()
217
+
218
+ after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
219
+ parent_frame.after_tasks.append(after_id)
220
+
221
+ return parent_frame, vars_dict
222
+
223
+ def gui_measure():
224
+ root = tk.Tk()
225
+ width = root.winfo_screenwidth()
226
+ height = root.winfo_screenheight()
227
+ root.geometry(f"{width}x{height}")
228
+ root.title("SpaCr: measure objects")
229
+
230
+ # Clear previous content if any
231
+ if hasattr(root, 'content_frame'):
232
+ for widget in root.content_frame.winfo_children():
233
+ widget.destroy()
234
+ root.content_frame.grid_forget()
235
+ else:
236
+ root.content_frame = tk.Frame(root)
237
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
238
+ root.grid_rowconfigure(1, weight=1)
239
+ root.grid_columnconfigure(0, weight=1)
240
+
241
+ initiate_measure_root(root.content_frame)
242
+ create_menu_bar(root)
243
+ root.mainloop()
244
+
245
+ if __name__ == "__main__":
246
+ gui_measure()
spacr/plot.py CHANGED
@@ -1565,4 +1565,56 @@ def plot_feature_importance(feature_importance_df):
1565
1565
  ax.set_xlabel('Feature Importance', fontsize=font_size)
1566
1566
  ax.tick_params(axis='both', which='major', labelsize=font_size)
1567
1567
  plt.tight_layout()
1568
- return fig
1568
+ return fig
1569
+
1570
+ def read_and_plot__vision_results(base_dir, y_axis='accuracy', name_split='_time', y_lim=[0.8, 0.9]):
1571
+ # List to store data from all CSV files
1572
+ data_frames = []
1573
+
1574
+ dst = os.path.join(base_dir, 'result')
1575
+ os.mkdir(dst,exists=True)
1576
+
1577
+ # Walk through the directory
1578
+ for root, dirs, files in os.walk(base_dir):
1579
+ for file in files:
1580
+ if file.endswith("_test_result.csv"):
1581
+ file_path = os.path.join(root, file)
1582
+ # Extract model information from the file name
1583
+ file_name = os.path.basename(file_path)
1584
+ model = file_name.split(f'{name_split}')[0]
1585
+
1586
+ # Extract epoch information from the file name
1587
+ epoch_info = file_name.split('_time')[1]
1588
+ base_folder = os.path.dirname(file_path)
1589
+ epoch = os.path.basename(base_folder)
1590
+
1591
+ # Read the CSV file
1592
+ df = pd.read_csv(file_path)
1593
+ df['model'] = model
1594
+ df['epoch'] = epoch
1595
+
1596
+ # Append the data frame to the list
1597
+ data_frames.append(df)
1598
+
1599
+ # Concatenate all data frames
1600
+ if data_frames:
1601
+ result_df = pd.concat(data_frames, ignore_index=True)
1602
+
1603
+ # Calculate average y_axis per model
1604
+ avg_metric = result_df.groupby('model')[y_axis].mean().reset_index()
1605
+ avg_metric = avg_metric.sort_values(by=y_axis)
1606
+ print(avg_metric)
1607
+
1608
+ # Plotting the results
1609
+ plt.figure(figsize=(10, 6))
1610
+ plt.bar(avg_metric['model'], avg_metric[y_axis])
1611
+ plt.xlabel('Model')
1612
+ plt.ylabel(f'{y_axis}')
1613
+ plt.title(f'Average {y_axis.capitalize()} per Model')
1614
+ plt.xticks(rotation=45)
1615
+ plt.tight_layout()
1616
+ if y_lim is not None:
1617
+ plt.ylim(y_lim)
1618
+ plt.show()
1619
+ else:
1620
+ print("No CSV files found in the specified directory.")
spacr/sequencing.py CHANGED
@@ -37,22 +37,6 @@ def analyze_reads(settings):
37
37
  None
38
38
  """
39
39
 
40
- def save_chunk_to_hdf5_v1(output_file_path, data_chunk, chunk_counter):
41
- """
42
- Save a data chunk to an HDF5 file.
43
-
44
- Parameters:
45
- - output_file_path (str): The path to the output HDF5 file.
46
- - data_chunk (list): The data chunk to be saved.
47
- - chunk_counter (int): The counter for the current chunk.
48
-
49
- Returns:
50
- None
51
- """
52
- df = pd.DataFrame(data_chunk, columns=['combined_read', 'grna', 'plate_row', 'column', 'sample'])
53
- with pd.HDFStore(output_file_path, mode='a', complevel=5, complib='blosc') as store:
54
- store.put(f'reads/chunk_{chunk_counter}', df, format='table', append=True)
55
-
56
40
  def save_chunk_to_hdf5(output_file_path, data_chunk, chunk_counter):
57
41
  """
58
42
  Save a data chunk to an HDF5 file.
@@ -306,7 +290,7 @@ def analyze_reads(settings):
306
290
  qc_df = pd.DataFrame([qc])
307
291
  qc_df.to_csv(qc_file_path, index=False)
308
292
 
309
- from .utils import get_analyze_reads_default_settings
293
+ from .settings import get_analyze_reads_default_settings
310
294
 
311
295
  settings = get_analyze_reads_default_settings(settings)
312
296