spacr 0.1.12__py3-none-any.whl → 0.1.50__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 +2 -3
- spacr/app_classify.py +5 -198
- spacr/app_make_masks.py +1 -2
- spacr/app_make_masks_v2.py +2 -4
- spacr/app_mask.py +5 -248
- spacr/app_measure.py +5 -245
- spacr/deep_spacr.py +3 -1
- spacr/gui.py +26 -16
- spacr/gui_utils.py +1008 -668
- spacr/measure.py +24 -3
- spacr/settings.py +13 -2
- spacr/utils.py +59 -5
- {spacr-0.1.12.dist-info → spacr-0.1.50.dist-info}/METADATA +13 -26
- {spacr-0.1.12.dist-info → spacr-0.1.50.dist-info}/RECORD +18 -18
- spacr-0.1.50.dist-info/entry_points.txt +8 -0
- spacr-0.1.12.dist-info/entry_points.txt +0 -9
- {spacr-0.1.12.dist-info → spacr-0.1.50.dist-info}/LICENSE +0 -0
- {spacr-0.1.12.dist-info → spacr-0.1.50.dist-info}/WHEEL +0 -0
- {spacr-0.1.12.dist-info → spacr-0.1.50.dist-info}/top_level.txt +0 -0
spacr/app_measure.py
CHANGED
@@ -1,248 +1,8 @@
|
|
1
|
-
|
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
|
1
|
+
from .gui import MainApp
|
9
2
|
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
test_mode_button = None
|
32
|
-
|
33
|
-
def toggle_test_mode():
|
34
|
-
global vars_dict
|
35
|
-
current_state = vars_dict['test_mode'][2].get()
|
36
|
-
new_state = not current_state
|
37
|
-
vars_dict['test_mode'][2].set(new_state)
|
38
|
-
if new_state:
|
39
|
-
test_mode_button.config(bg="blue")
|
40
|
-
else:
|
41
|
-
test_mode_button.config(bg="gray")
|
42
|
-
|
43
|
-
def toggle_advanced_settings():
|
44
|
-
global vars_dict
|
45
|
-
|
46
|
-
timelapse_settings = ['timelapse', 'timelapse_objects']
|
47
|
-
misc_settings = ['representative_images', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'dialate_png_ratios']
|
48
|
-
opperational_settings = ['max_workers','experiment','cells','cell_loc','pathogens','pathogen_loc','treatments','treatment_loc','channel_of_interest','compartments','measurement','nr_imgs', 'um_per_pixel']
|
49
|
-
|
50
|
-
advanced_settings = timelapse_settings+misc_settings+opperational_settings
|
51
|
-
|
52
|
-
# Toggle visibility of advanced settings
|
53
|
-
for setting in advanced_settings:
|
54
|
-
label, widget, var = vars_dict[setting]
|
55
|
-
if advanced_var.get() is False:
|
56
|
-
label.grid_remove() # Hide the label
|
57
|
-
widget.grid_remove() # Hide the widget
|
58
|
-
else:
|
59
|
-
label.grid() # Show the label
|
60
|
-
widget.grid() # Show the widget
|
61
|
-
|
62
|
-
#@log_function_call
|
63
|
-
def run_measure_gui(q, fig_queue, stop_requested):
|
64
|
-
global vars_dict
|
65
|
-
process_stdout_stderr(q)
|
66
|
-
try:
|
67
|
-
print('hello')
|
68
|
-
settings = check_measure_gui_settings(vars_dict)
|
69
|
-
measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
|
70
|
-
except Exception as e:
|
71
|
-
q.put(f"Error during processing: {e}")
|
72
|
-
traceback.print_exc()
|
73
|
-
finally:
|
74
|
-
stop_requested.value = 1
|
75
|
-
|
76
|
-
#@log_function_call
|
77
|
-
def start_process(q, fig_queue):
|
78
|
-
global thread_control
|
79
|
-
if thread_control.get("run_thread") is not None:
|
80
|
-
initiate_abort()
|
81
|
-
|
82
|
-
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
83
|
-
thread_control["stop_requested"] = stop_requested
|
84
|
-
thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
|
85
|
-
thread_control["run_thread"].start()
|
86
|
-
|
87
|
-
#@log_function_call
|
88
|
-
def initiate_abort():
|
89
|
-
global thread_control
|
90
|
-
if thread_control.get("stop_requested") is not None:
|
91
|
-
thread_control["stop_requested"].value = 1
|
92
|
-
|
93
|
-
if thread_control.get("run_thread") is not None:
|
94
|
-
thread_control["run_thread"].join(timeout=5)
|
95
|
-
if thread_control["run_thread"].is_alive():
|
96
|
-
thread_control["run_thread"].terminate()
|
97
|
-
thread_control["run_thread"] = None
|
98
|
-
|
99
|
-
#@log_function_call
|
100
|
-
def initiate_measure_root(parent_frame):
|
101
|
-
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
|
102
|
-
|
103
|
-
style = ttk.Style(parent_frame)
|
104
|
-
set_dark_style(style)
|
105
|
-
style_text_boxes(style)
|
106
|
-
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
107
|
-
|
108
|
-
parent_frame.configure(bg='black')
|
109
|
-
parent_frame.grid_rowconfigure(0, weight=1)
|
110
|
-
parent_frame.grid_columnconfigure(0, weight=1)
|
111
|
-
|
112
|
-
fig_queue = Queue()
|
113
|
-
|
114
|
-
# Initialize after_tasks if not already done
|
115
|
-
if not hasattr(parent_frame, 'after_tasks'):
|
116
|
-
parent_frame.after_tasks = []
|
117
|
-
|
118
|
-
def _process_fig_queue():
|
119
|
-
global canvas
|
120
|
-
try:
|
121
|
-
while not fig_queue.empty():
|
122
|
-
clear_canvas(canvas)
|
123
|
-
fig = fig_queue.get_nowait()
|
124
|
-
for ax in fig.get_axes():
|
125
|
-
ax.set_xticks([]) # Remove x-axis ticks
|
126
|
-
ax.set_yticks([]) # Remove y-axis ticks
|
127
|
-
ax.xaxis.set_visible(False) # Hide the x-axis
|
128
|
-
ax.yaxis.set_visible(False) # Hide the y-axis
|
129
|
-
fig.tight_layout()
|
130
|
-
fig.set_facecolor('black')
|
131
|
-
canvas.figure = fig
|
132
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
133
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
134
|
-
canvas.draw_idle()
|
135
|
-
except Exception as e:
|
136
|
-
traceback.print_exc()
|
137
|
-
finally:
|
138
|
-
after_id = canvas_widget.after(100, _process_fig_queue)
|
139
|
-
parent_frame.after_tasks.append(after_id)
|
140
|
-
|
141
|
-
def _process_console_queue():
|
142
|
-
while not q.empty():
|
143
|
-
message = q.get_nowait()
|
144
|
-
console_output.insert(tk.END, message)
|
145
|
-
console_output.see(tk.END)
|
146
|
-
after_id = console_output.after(100, _process_console_queue)
|
147
|
-
parent_frame.after_tasks.append(after_id)
|
148
|
-
|
149
|
-
# Clear previous content if any
|
150
|
-
for widget in parent_frame.winfo_children():
|
151
|
-
widget.destroy()
|
152
|
-
|
153
|
-
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
154
|
-
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
155
|
-
parent_frame.grid_rowconfigure(0, weight=1)
|
156
|
-
parent_frame.grid_columnconfigure(0, weight=1)
|
157
|
-
|
158
|
-
# Settings Section
|
159
|
-
settings_frame = tk.Frame(vertical_container, bg='black')
|
160
|
-
vertical_container.add(settings_frame, stretch="always")
|
161
|
-
settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
|
162
|
-
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
163
|
-
scrollable_frame = ScrollableFrame(settings_frame, width=500)
|
164
|
-
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
165
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
166
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
167
|
-
|
168
|
-
# Create advanced settings checkbox
|
169
|
-
advanced_var = tk.BooleanVar(value=False)
|
170
|
-
advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
|
171
|
-
advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
|
172
|
-
variables = measure_variables()
|
173
|
-
vars_dict = generate_fields(variables, scrollable_frame)
|
174
|
-
toggle_advanced_settings()
|
175
|
-
vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
|
176
|
-
|
177
|
-
# Button section
|
178
|
-
test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
|
179
|
-
test_mode_button.grid(row=47, column=1, pady=10, padx=10)
|
180
|
-
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
|
181
|
-
import_btn.grid(row=47, column=0, pady=20, padx=20)
|
182
|
-
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
|
183
|
-
run_button.grid(row=45, column=0, pady=20, padx=20)
|
184
|
-
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
|
185
|
-
abort_button.grid(row=45, column=1, pady=20, padx=20)
|
186
|
-
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
|
187
|
-
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
188
|
-
|
189
|
-
# Plot Canvas Section
|
190
|
-
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
191
|
-
vertical_container.add(plot_frame, stretch="always")
|
192
|
-
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
193
|
-
plot = figure.add_subplot(111)
|
194
|
-
plot.plot([], [])
|
195
|
-
plot.axis('off')
|
196
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
197
|
-
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
198
|
-
canvas_widget = canvas.get_tk_widget()
|
199
|
-
plot_frame.add(canvas_widget, stretch="always")
|
200
|
-
canvas.draw()
|
201
|
-
canvas.figure = figure
|
202
|
-
|
203
|
-
# Console Section
|
204
|
-
console_frame = tk.Frame(vertical_container, bg='black')
|
205
|
-
vertical_container.add(console_frame, stretch="always")
|
206
|
-
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
207
|
-
console_label.grid(row=0, column=0, pady=10, padx=10)
|
208
|
-
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
209
|
-
console_output.grid(row=1, column=0, sticky="nsew")
|
210
|
-
console_frame.grid_rowconfigure(1, weight=1)
|
211
|
-
console_frame.grid_columnconfigure(0, weight=1)
|
212
|
-
|
213
|
-
q = Queue()
|
214
|
-
sys.stdout = StdoutRedirector(console_output)
|
215
|
-
sys.stderr = StdoutRedirector(console_output)
|
216
|
-
|
217
|
-
_process_console_queue()
|
218
|
-
_process_fig_queue()
|
219
|
-
|
220
|
-
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
221
|
-
parent_frame.after_tasks.append(after_id)
|
222
|
-
|
223
|
-
return parent_frame, vars_dict
|
224
|
-
|
225
|
-
def gui_measure():
|
226
|
-
root = tk.Tk()
|
227
|
-
width = root.winfo_screenwidth()
|
228
|
-
height = root.winfo_screenheight()
|
229
|
-
root.geometry(f"{width}x{height}")
|
230
|
-
root.title("SpaCr: measure objects")
|
231
|
-
|
232
|
-
# Clear previous content if any
|
233
|
-
if hasattr(root, 'content_frame'):
|
234
|
-
for widget in root.content_frame.winfo_children():
|
235
|
-
widget.destroy()
|
236
|
-
root.content_frame.grid_forget()
|
237
|
-
else:
|
238
|
-
root.content_frame = tk.Frame(root)
|
239
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
240
|
-
root.grid_rowconfigure(1, weight=1)
|
241
|
-
root.grid_columnconfigure(0, weight=1)
|
242
|
-
|
243
|
-
initiate_measure_root(root.content_frame)
|
244
|
-
create_menu_bar(root)
|
245
|
-
root.mainloop()
|
3
|
+
def start_measure_app():
|
4
|
+
app = MainApp(default_app="Measure")
|
5
|
+
app.mainloop()
|
246
6
|
|
247
7
|
if __name__ == "__main__":
|
248
|
-
|
8
|
+
start_measure_app()
|
spacr/deep_spacr.py
CHANGED
@@ -10,6 +10,9 @@ import matplotlib.pyplot as plt
|
|
10
10
|
from PIL import Image
|
11
11
|
|
12
12
|
from .logger import log_function_call
|
13
|
+
from .utils import close_multiprocessing_processes, reset_mp
|
14
|
+
#reset_mp()
|
15
|
+
#close_multiprocessing_processes()
|
13
16
|
|
14
17
|
def evaluate_model_core(model, loader, loader_name, epoch, loss_type):
|
15
18
|
"""
|
@@ -42,7 +45,6 @@ def evaluate_model_core(model, loader, loader_name, epoch, loss_type):
|
|
42
45
|
for batch_idx, (data, target, _) in enumerate(loader, start=1):
|
43
46
|
start_time = time.time()
|
44
47
|
data, target = data.to(device), target.to(device).float()
|
45
|
-
#data, target = data.to(torch.float).to(device), target.to(device).float()
|
46
48
|
output = model(data)
|
47
49
|
loss += F.binary_cross_entropy_with_logits(output, target, reduction='sum').item()
|
48
50
|
loss = calculate_loss(output, target, loss_type=loss_type)
|
spacr/gui.py
CHANGED
@@ -1,20 +1,15 @@
|
|
1
1
|
import tkinter as tk
|
2
2
|
from tkinter import ttk
|
3
|
-
from tkinter import font as tkFont
|
4
3
|
from PIL import Image, ImageTk
|
5
4
|
import os
|
6
5
|
import requests
|
7
|
-
|
8
|
-
|
9
|
-
from .app_mask import initiate_mask_root
|
10
|
-
from .app_measure import initiate_measure_root
|
6
|
+
from multiprocessing import set_start_method
|
7
|
+
from .gui_utils import set_dark_style, create_menu_bar, initiate_root
|
11
8
|
from .app_annotate import initiate_annotation_app_root
|
12
9
|
from .app_make_masks import initiate_mask_app_root
|
13
|
-
from .app_classify import initiate_classify_root
|
14
|
-
from .gui_utils import CustomButton, style_text_boxes, create_menu_bar
|
15
10
|
|
16
11
|
class MainApp(tk.Tk):
|
17
|
-
def __init__(self):
|
12
|
+
def __init__(self, default_app=None):
|
18
13
|
super().__init__()
|
19
14
|
width = self.winfo_screenwidth()
|
20
15
|
height = self.winfo_screenheight()
|
@@ -22,19 +17,31 @@ class MainApp(tk.Tk):
|
|
22
17
|
self.title("SpaCr GUI Collection")
|
23
18
|
self.configure(bg="black")
|
24
19
|
style = ttk.Style()
|
25
|
-
|
20
|
+
set_dark_style(style)
|
26
21
|
|
27
22
|
self.gui_apps = {
|
28
|
-
"Mask": (
|
29
|
-
"Measure": (
|
23
|
+
"Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
|
24
|
+
"Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
|
30
25
|
"Annotate": (initiate_annotation_app_root, "Annotation single object images on a grid. Annotations are saved to database."),
|
31
26
|
"Make Masks": (initiate_mask_app_root, "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
|
32
|
-
"Classify": (
|
27
|
+
"Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images.")
|
33
28
|
}
|
34
29
|
|
35
30
|
self.selected_app = tk.StringVar()
|
36
31
|
self.create_widgets()
|
37
32
|
|
33
|
+
|
34
|
+
if default_app == "Mask":
|
35
|
+
self.load_app(default_app, self.gui_apps[default_app][0])
|
36
|
+
elif default_app == "Measure":
|
37
|
+
self.load_app(default_app, self.gui_apps[default_app][1])
|
38
|
+
elif default_app == "Annotate":
|
39
|
+
self.load_app(default_app, self.gui_apps[default_app][2])
|
40
|
+
elif default_app == "Make Masks":
|
41
|
+
self.load_app(default_app, self.gui_apps[default_app][3])
|
42
|
+
elif default_app == "Classify":
|
43
|
+
self.load_app(default_app, self.gui_apps[default_app][4])
|
44
|
+
|
38
45
|
def create_widgets(self):
|
39
46
|
# Create the menu bar
|
40
47
|
create_menu_bar(self)
|
@@ -74,7 +81,8 @@ class MainApp(tk.Tk):
|
|
74
81
|
app_func, app_desc = app_data
|
75
82
|
|
76
83
|
# Create custom button with text
|
77
|
-
button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name, app_func), font=('Helvetica', 12))
|
84
|
+
#button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), font=('Helvetica', 12))
|
85
|
+
button = ttk.Button(buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), style='Custom.TButton')
|
78
86
|
button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
|
79
87
|
|
80
88
|
description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
|
@@ -100,7 +108,6 @@ class MainApp(tk.Tk):
|
|
100
108
|
|
101
109
|
try:
|
102
110
|
img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
|
103
|
-
print(f"Trying to load logo from {img_path}")
|
104
111
|
logo_image = Image.open(img_path)
|
105
112
|
except (FileNotFoundError, Image.UnidentifiedImageError):
|
106
113
|
print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
|
@@ -117,7 +124,9 @@ class MainApp(tk.Tk):
|
|
117
124
|
print(f"An error occurred while loading the logo: {e}")
|
118
125
|
return False
|
119
126
|
try:
|
120
|
-
|
127
|
+
screen_height = frame.winfo_screenheight()
|
128
|
+
new_height = int(screen_height // 4)
|
129
|
+
logo_image = logo_image.resize((new_height, new_height), Image.Resampling.LANCZOS)
|
121
130
|
logo_photo = ImageTk.PhotoImage(logo_image)
|
122
131
|
logo_label = tk.Label(frame, image=logo_photo, bg="black")
|
123
132
|
logo_label.image = logo_photo # Keep a reference to avoid garbage collection
|
@@ -145,4 +154,5 @@ def gui_app():
|
|
145
154
|
app.mainloop()
|
146
155
|
|
147
156
|
if __name__ == "__main__":
|
148
|
-
|
157
|
+
set_start_method('spawn', force=True)
|
158
|
+
gui_app()
|