spacr 0.2.68__py3-none-any.whl → 0.3.0__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/__init__.py +2 -1
- spacr/core.py +107 -12
- spacr/gui.py +3 -2
- spacr/gui_core.py +160 -109
- spacr/gui_elements.py +190 -18
- spacr/gui_utils.py +4 -1
- spacr/io.py +1 -1
- spacr/measure.py +4 -4
- spacr/mediar.py +366 -0
- spacr/plot.py +4 -1
- spacr/resources/MEDIAR/.git +1 -0
- spacr/resources/MEDIAR/.gitignore +18 -0
- spacr/resources/MEDIAR/LICENSE +21 -0
- spacr/resources/MEDIAR/README.md +189 -0
- spacr/resources/MEDIAR/SetupDict.py +39 -0
- spacr/resources/MEDIAR/config/baseline.json +60 -0
- spacr/resources/MEDIAR/config/mediar_example.json +72 -0
- spacr/resources/MEDIAR/config/pred/pred_mediar.json +17 -0
- spacr/resources/MEDIAR/config/step1_pretraining/phase1.json +55 -0
- spacr/resources/MEDIAR/config/step1_pretraining/phase2.json +58 -0
- spacr/resources/MEDIAR/config/step2_finetuning/finetuning1.json +66 -0
- spacr/resources/MEDIAR/config/step2_finetuning/finetuning2.json +66 -0
- spacr/resources/MEDIAR/config/step3_prediction/base_prediction.json +16 -0
- spacr/resources/MEDIAR/config/step3_prediction/ensemble_tta.json +23 -0
- spacr/resources/MEDIAR/core/BasePredictor.py +120 -0
- spacr/resources/MEDIAR/core/BaseTrainer.py +240 -0
- spacr/resources/MEDIAR/core/Baseline/Predictor.py +59 -0
- spacr/resources/MEDIAR/core/Baseline/Trainer.py +113 -0
- spacr/resources/MEDIAR/core/Baseline/__init__.py +2 -0
- spacr/resources/MEDIAR/core/Baseline/utils.py +80 -0
- spacr/resources/MEDIAR/core/MEDIAR/EnsemblePredictor.py +105 -0
- spacr/resources/MEDIAR/core/MEDIAR/Predictor.py +234 -0
- spacr/resources/MEDIAR/core/MEDIAR/Trainer.py +172 -0
- spacr/resources/MEDIAR/core/MEDIAR/__init__.py +3 -0
- spacr/resources/MEDIAR/core/MEDIAR/utils.py +429 -0
- spacr/resources/MEDIAR/core/__init__.py +2 -0
- spacr/resources/MEDIAR/core/utils.py +40 -0
- spacr/resources/MEDIAR/evaluate.py +71 -0
- spacr/resources/MEDIAR/generate_mapping.py +121 -0
- spacr/resources/MEDIAR/image/examples/img1.tiff +0 -0
- spacr/resources/MEDIAR/image/examples/img2.tif +0 -0
- spacr/resources/MEDIAR/image/failure_cases.png +0 -0
- spacr/resources/MEDIAR/image/mediar_framework.png +0 -0
- spacr/resources/MEDIAR/image/mediar_model.PNG +0 -0
- spacr/resources/MEDIAR/image/mediar_results.png +0 -0
- spacr/resources/MEDIAR/main.py +125 -0
- spacr/resources/MEDIAR/predict.py +70 -0
- spacr/resources/MEDIAR/requirements.txt +14 -0
- spacr/resources/MEDIAR/train_tools/__init__.py +3 -0
- spacr/resources/MEDIAR/train_tools/data_utils/__init__.py +1 -0
- spacr/resources/MEDIAR/train_tools/data_utils/custom/CellAware.py +88 -0
- spacr/resources/MEDIAR/train_tools/data_utils/custom/LoadImage.py +161 -0
- spacr/resources/MEDIAR/train_tools/data_utils/custom/NormalizeImage.py +77 -0
- spacr/resources/MEDIAR/train_tools/data_utils/custom/__init__.py +3 -0
- spacr/resources/MEDIAR/train_tools/data_utils/custom/modalities.pkl +0 -0
- spacr/resources/MEDIAR/train_tools/data_utils/datasetter.py +208 -0
- spacr/resources/MEDIAR/train_tools/data_utils/transforms.py +148 -0
- spacr/resources/MEDIAR/train_tools/data_utils/utils.py +84 -0
- spacr/resources/MEDIAR/train_tools/measures.py +200 -0
- spacr/resources/MEDIAR/train_tools/models/MEDIARFormer.py +102 -0
- spacr/resources/MEDIAR/train_tools/models/__init__.py +1 -0
- spacr/resources/MEDIAR/train_tools/utils.py +70 -0
- spacr/resources/MEDIAR_weights/.DS_Store +0 -0
- spacr/resources/icons/.DS_Store +0 -0
- spacr/resources/icons/plaque.png +0 -0
- spacr/resources/images/plate1_E01_T0001F001L01A01Z01C02.tif +0 -0
- spacr/resources/images/plate1_E01_T0001F001L01A02Z01C01.tif +0 -0
- spacr/resources/images/plate1_E01_T0001F001L01A03Z01C03.tif +0 -0
- spacr/sequencing.py +234 -422
- spacr/settings.py +16 -10
- spacr/utils.py +14 -11
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/METADATA +10 -2
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/RECORD +77 -18
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/LICENSE +0 -0
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/WHEEL +0 -0
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.68.dist-info → spacr-0.3.0.dist-info}/top_level.txt +0 -0
spacr/__init__.py
CHANGED
@@ -23,9 +23,9 @@ from . import app_measure
|
|
23
23
|
from . import app_classify
|
24
24
|
from . import app_sequencing
|
25
25
|
from . import app_umap
|
26
|
+
from . import mediar
|
26
27
|
from . import logger
|
27
28
|
|
28
|
-
|
29
29
|
__all__ = [
|
30
30
|
"core",
|
31
31
|
"io",
|
@@ -48,6 +48,7 @@ __all__ = [
|
|
48
48
|
"app_classify",
|
49
49
|
"app_sequencing",
|
50
50
|
"app_umap",
|
51
|
+
"mediar",
|
51
52
|
"logger"
|
52
53
|
]
|
53
54
|
|
spacr/core.py
CHANGED
@@ -50,8 +50,6 @@ import random
|
|
50
50
|
from PIL import Image
|
51
51
|
from torchvision.transforms import ToTensor
|
52
52
|
|
53
|
-
|
54
|
-
|
55
53
|
def analyze_plaques(folder):
|
56
54
|
summary_data = []
|
57
55
|
details_data = []
|
@@ -1638,7 +1636,7 @@ def preprocess_generate_masks(src, settings={}):
|
|
1638
1636
|
|
1639
1637
|
settings = set_default_settings_preprocess_generate_masks(src, settings)
|
1640
1638
|
settings['src'] = src
|
1641
|
-
save_settings(settings)
|
1639
|
+
save_settings(settings, name='gen_mask')
|
1642
1640
|
|
1643
1641
|
if not settings['pathogen_channel'] is None:
|
1644
1642
|
custom_model_ls = ['toxo_pv_lumen','toxo_cyto']
|
@@ -1674,7 +1672,10 @@ def preprocess_generate_masks(src, settings={}):
|
|
1674
1672
|
time_ls=[]
|
1675
1673
|
if check_mask_folder(src, 'cell_mask_stack'):
|
1676
1674
|
start = time.time()
|
1677
|
-
|
1675
|
+
if settings['segmantation_model'] == 'cellpose':
|
1676
|
+
generate_cellpose_masks(mask_src, settings, 'cell')
|
1677
|
+
elif settings['segmantation_model'] == 'mediar':
|
1678
|
+
generate_mediar_masks(mask_src, settings, 'cell')
|
1678
1679
|
stop = time.time()
|
1679
1680
|
duration = (stop - start)
|
1680
1681
|
time_ls.append(duration)
|
@@ -1685,7 +1686,10 @@ def preprocess_generate_masks(src, settings={}):
|
|
1685
1686
|
time_ls=[]
|
1686
1687
|
if check_mask_folder(src, 'nucleus_mask_stack'):
|
1687
1688
|
start = time.time()
|
1688
|
-
|
1689
|
+
if settings['segmantation_model'] == 'cellpose':
|
1690
|
+
generate_cellpose_masks(mask_src, settings, 'nucleus')
|
1691
|
+
elif settings['segmantation_model'] == 'mediar':
|
1692
|
+
generate_mediar_masks(mask_src, settings, 'nucleus')
|
1689
1693
|
stop = time.time()
|
1690
1694
|
duration = (stop - start)
|
1691
1695
|
time_ls.append(duration)
|
@@ -1696,7 +1700,10 @@ def preprocess_generate_masks(src, settings={}):
|
|
1696
1700
|
time_ls=[]
|
1697
1701
|
if check_mask_folder(src, 'pathogen_mask_stack'):
|
1698
1702
|
start = time.time()
|
1699
|
-
|
1703
|
+
if settings['segmantation_model'] == 'cellpose':
|
1704
|
+
generate_cellpose_masks(mask_src, settings, 'pathogen')
|
1705
|
+
elif settings['segmantation_model'] == 'mediar':
|
1706
|
+
generate_mediar_masks(mask_src, settings, 'pathogen')
|
1700
1707
|
stop = time.time()
|
1701
1708
|
duration = (stop - start)
|
1702
1709
|
time_ls.append(duration)
|
@@ -1898,7 +1905,7 @@ def all_elements_match(list1, list2):
|
|
1898
1905
|
# Check if all elements in list1 are in list2
|
1899
1906
|
return all(element in list2 for element in list1)
|
1900
1907
|
|
1901
|
-
def
|
1908
|
+
def prepare_batch_for_segmentation(batch):
|
1902
1909
|
# Ensure the batch is of dtype float32
|
1903
1910
|
if batch.dtype != np.float32:
|
1904
1911
|
batch = batch.astype(np.float32)
|
@@ -2021,7 +2028,7 @@ def generate_cellpose_masks(src, settings, object_type):
|
|
2021
2028
|
if batch.size == 0:
|
2022
2029
|
continue
|
2023
2030
|
|
2024
|
-
batch =
|
2031
|
+
batch = prepare_batch_for_segmentation(batch)
|
2025
2032
|
|
2026
2033
|
if timelapse:
|
2027
2034
|
movie_path = os.path.join(os.path.dirname(src), 'movies')
|
@@ -2180,7 +2187,7 @@ def generate_masks_from_imgs(src, model, model_name, batch_size, diameter, cellp
|
|
2180
2187
|
image_files = all_image_files[i:i+batch_size]
|
2181
2188
|
|
2182
2189
|
if normalize:
|
2183
|
-
images, _, image_names, _ = _load_normalized_images_and_labels(image_files, None, channels, percentiles, circular, invert, plot, remove_background, background, Signal_to_noise)
|
2190
|
+
images, _, image_names, _, orig_dims = _load_normalized_images_and_labels(image_files, None, channels, percentiles, circular, invert, plot, remove_background, background, Signal_to_noise, target_height, target_width)
|
2184
2191
|
images = [np.squeeze(img) if img.shape[-1] == 1 else img for img in images]
|
2185
2192
|
orig_dims = [(image.shape[0], image.shape[1]) for image in images]
|
2186
2193
|
else:
|
@@ -2300,7 +2307,7 @@ def compare_cellpose_masks(src, verbose=False, processes=None, save=True):
|
|
2300
2307
|
from .io import _read_mask
|
2301
2308
|
|
2302
2309
|
dirs = [os.path.join(src, d) for d in os.listdir(src) if os.path.isdir(os.path.join(src, d)) and d != 'results']
|
2303
|
-
dirs.sort()
|
2310
|
+
dirs.sort()
|
2304
2311
|
conditions = [os.path.basename(d) for d in dirs]
|
2305
2312
|
|
2306
2313
|
# Get common files in all directories
|
@@ -2316,7 +2323,7 @@ def compare_cellpose_masks(src, verbose=False, processes=None, save=True):
|
|
2316
2323
|
|
2317
2324
|
# Filter out None results (from skipped files)
|
2318
2325
|
results = [res for res in results if res is not None]
|
2319
|
-
|
2326
|
+
print(results)
|
2320
2327
|
if verbose:
|
2321
2328
|
for result in results:
|
2322
2329
|
filename = result['filename']
|
@@ -3102,4 +3109,92 @@ def reducer_hyperparameter_search(settings={}, reduction_params=None, dbscan_par
|
|
3102
3109
|
else:
|
3103
3110
|
plt.show()
|
3104
3111
|
|
3105
|
-
return
|
3112
|
+
return
|
3113
|
+
|
3114
|
+
def generate_mediar_masks(src, settings, object_type):
|
3115
|
+
"""
|
3116
|
+
Generates masks using the MEDIARPredictor.
|
3117
|
+
|
3118
|
+
:param src: Source folder containing images or npz files.
|
3119
|
+
:param settings: Dictionary of settings for generating masks.
|
3120
|
+
:param object_type: Type of object to detect (e.g., 'cell', 'nucleus', etc.).
|
3121
|
+
"""
|
3122
|
+
from .mediar import MEDIARPredictor
|
3123
|
+
from .io import _create_database, _save_object_counts_to_database
|
3124
|
+
from .plot import plot_masks
|
3125
|
+
from .settings import set_default_settings_preprocess_generate_masks, _get_object_settings
|
3126
|
+
|
3127
|
+
# Clear CUDA cache and check if CUDA is available
|
3128
|
+
gc.collect()
|
3129
|
+
if not torch.cuda.is_available():
|
3130
|
+
print(f'Torch CUDA is not available, using CPU')
|
3131
|
+
|
3132
|
+
# Preprocess settings
|
3133
|
+
settings = set_default_settings_preprocess_generate_masks(src, settings)
|
3134
|
+
|
3135
|
+
if settings['verbose']:
|
3136
|
+
settings_df = pd.DataFrame(list(settings.items()), columns=['setting_key', 'setting_value'])
|
3137
|
+
settings_df['setting_value'] = settings_df['setting_value'].apply(str)
|
3138
|
+
display(settings_df)
|
3139
|
+
|
3140
|
+
figuresize = 10
|
3141
|
+
timelapse = settings['timelapse']
|
3142
|
+
batch_size = settings['batch_size']
|
3143
|
+
|
3144
|
+
# Get object settings and initialize MEDIARPredictor
|
3145
|
+
mediar_predictor = MEDIARPredictor(input_path=None, output_path=None, normalize=settings['normalize'], use_tta=False)
|
3146
|
+
|
3147
|
+
# Paths to input npz files
|
3148
|
+
paths = [os.path.join(src, file) for file in os.listdir(src) if file.endswith('.npz')]
|
3149
|
+
|
3150
|
+
# Initialize a database for saving measurements
|
3151
|
+
count_loc = os.path.join(os.path.dirname(src), 'measurements', 'measurements.db')
|
3152
|
+
os.makedirs(os.path.dirname(src) + '/measurements', exist_ok=True)
|
3153
|
+
_create_database(count_loc)
|
3154
|
+
|
3155
|
+
for file_index, path in enumerate(paths):
|
3156
|
+
name = os.path.basename(path)
|
3157
|
+
name, ext = os.path.splitext(name)
|
3158
|
+
output_folder = os.path.join(os.path.dirname(path), f'{object_type}_mask_stack')
|
3159
|
+
os.makedirs(output_folder, exist_ok=True)
|
3160
|
+
|
3161
|
+
with np.load(path) as data:
|
3162
|
+
stack = data['data']
|
3163
|
+
filenames = data['filenames']
|
3164
|
+
|
3165
|
+
for i, filename in enumerate(filenames):
|
3166
|
+
output_path = os.path.join(output_folder, filename)
|
3167
|
+
if os.path.exists(output_path):
|
3168
|
+
print(f"File {filename} already exists. Skipping...")
|
3169
|
+
continue
|
3170
|
+
|
3171
|
+
# Process each batch of images in the stack
|
3172
|
+
for i in range(0, stack.shape[0], batch_size):
|
3173
|
+
batch = stack[i: i + batch_size]
|
3174
|
+
batch_filenames = filenames[i: i + batch_size]
|
3175
|
+
|
3176
|
+
# Prepare batch for MEDIARPredictor (optional)
|
3177
|
+
batch = prepare_batch_for_segmentation(batch)
|
3178
|
+
|
3179
|
+
# Predict masks using MEDIARPredictor
|
3180
|
+
predicted_masks = mediar_predictor.predict_batch(batch)
|
3181
|
+
|
3182
|
+
# Save predicted masks
|
3183
|
+
for j, mask in enumerate(predicted_masks):
|
3184
|
+
output_filename = os.path.join(output_folder, batch_filenames[j])
|
3185
|
+
mask = mask.astype(np.uint16)
|
3186
|
+
np.save(output_filename, mask)
|
3187
|
+
|
3188
|
+
# Optional: Plot the masks
|
3189
|
+
if settings['plot']:
|
3190
|
+
for idx, mask in enumerate(predicted_masks):
|
3191
|
+
plot_masks(batch[idx], mask, cmap='inferno', figuresize=figuresize)
|
3192
|
+
|
3193
|
+
# Save object counts to database
|
3194
|
+
_save_object_counts_to_database(predicted_masks, object_type, batch_filenames, count_loc)
|
3195
|
+
|
3196
|
+
# Clear CUDA cache after each file
|
3197
|
+
gc.collect()
|
3198
|
+
torch.cuda.empty_cache()
|
3199
|
+
|
3200
|
+
print("Mask generation completed.")
|
spacr/gui.py
CHANGED
@@ -32,7 +32,7 @@ class MainApp(tk.Tk):
|
|
32
32
|
# Set the window size to the dimensions of the monitor where it is located
|
33
33
|
self.geometry(f"{width}x{height}")
|
34
34
|
self.title("SpaCr GUI Collection")
|
35
|
-
self.configure(bg='#333333')
|
35
|
+
self.configure(bg='#333333')
|
36
36
|
|
37
37
|
style = ttk.Style()
|
38
38
|
self.color_settings = set_dark_style(style, parent_frame=self)
|
@@ -55,7 +55,8 @@ class MainApp(tk.Tk):
|
|
55
55
|
"Cellpose All": (lambda frame: initiate_root(self, 'cellpose_all'), "Run Cellpose on all images."),
|
56
56
|
"Map Barcodes": (lambda frame: initiate_root(self, 'map_barcodes'), "Map barcodes to data."),
|
57
57
|
"Regression": (lambda frame: initiate_root(self, 'regression'), "Perform regression analysis."),
|
58
|
-
"Recruitment": (lambda frame: initiate_root(self, 'recruitment'), "Analyze recruitment data.")
|
58
|
+
"Recruitment": (lambda frame: initiate_root(self, 'recruitment'), "Analyze recruitment data."),
|
59
|
+
"Plaque": (lambda frame: initiate_root(self, 'analyze_plaques'), "Analyze plaque data.")
|
59
60
|
}
|
60
61
|
|
61
62
|
self.selected_app = tk.StringVar()
|
spacr/gui_core.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import traceback, ctypes, csv, re
|
1
|
+
import traceback, ctypes, csv, re, platform, time
|
2
2
|
import tkinter as tk
|
3
3
|
from tkinter import ttk
|
4
4
|
from tkinter import filedialog
|
@@ -19,7 +19,7 @@ try:
|
|
19
19
|
except AttributeError:
|
20
20
|
pass
|
21
21
|
|
22
|
-
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , set_dark_style
|
22
|
+
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , spacrSlider, set_dark_style, standardize_figure
|
23
23
|
|
24
24
|
# Define global variables
|
25
25
|
q = None
|
@@ -35,8 +35,6 @@ figures = None
|
|
35
35
|
figure_index = None
|
36
36
|
progress_bar = None
|
37
37
|
usage_bars = None
|
38
|
-
fig_memory_limit = None
|
39
|
-
figure_current_memory_usage = None
|
40
38
|
|
41
39
|
thread_control = {"run_thread": None, "stop_requested": False}
|
42
40
|
|
@@ -80,35 +78,10 @@ def toggle_settings(button_scrollable_frame):
|
|
80
78
|
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
81
79
|
vars_dict = hide_all_settings(vars_dict, categories)
|
82
80
|
|
83
|
-
def process_fig_queue():
|
84
|
-
global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index
|
85
|
-
|
86
|
-
from .gui_elements import standardize_figure
|
87
|
-
try:
|
88
|
-
while not fig_queue.empty():
|
89
|
-
fig = fig_queue.get_nowait()
|
90
|
-
|
91
|
-
if fig is None:
|
92
|
-
print("Warning: Retrieved a None figure from fig_queue.")
|
93
|
-
continue # Skip processing if the figure is None
|
94
|
-
|
95
|
-
# Standardize the figure appearance before adding it to the list
|
96
|
-
standardize_figure(fig)
|
97
|
-
|
98
|
-
figures.append(fig)
|
99
|
-
if figure_index == len(figures) - 2:
|
100
|
-
figure_index += 1
|
101
|
-
display_figure(fig)
|
102
|
-
except Exception as e:
|
103
|
-
traceback.print_exc()
|
104
|
-
finally:
|
105
|
-
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
106
|
-
parent_frame.after_tasks.append(after_id)
|
107
|
-
|
108
81
|
def display_figure(fig):
|
109
82
|
global canvas, canvas_widget
|
110
83
|
|
111
|
-
from .gui_elements import
|
84
|
+
from .gui_elements import save_figure_as_format, modify_figure
|
112
85
|
|
113
86
|
# Apply the dark style to the context menu
|
114
87
|
style_out = set_dark_style(ttk.Style())
|
@@ -197,7 +170,7 @@ def display_figure(fig):
|
|
197
170
|
#flash_feedback("right")
|
198
171
|
show_next_figure()
|
199
172
|
|
200
|
-
def
|
173
|
+
def zoom_v1(event):
|
201
174
|
nonlocal scale_factor
|
202
175
|
|
203
176
|
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
@@ -230,16 +203,55 @@ def display_figure(fig):
|
|
230
203
|
# Redraw the figure
|
231
204
|
fig.canvas.draw_idle()
|
232
205
|
|
206
|
+
def zoom(event):
|
207
|
+
nonlocal scale_factor
|
208
|
+
|
209
|
+
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
210
|
+
|
211
|
+
# Determine the zoom direction based on the scroll event
|
212
|
+
if event.num == 4 or event.delta > 0: # Scroll up (zoom in)
|
213
|
+
scale_factor /= (1 + zoom_speed) # Divide to zoom in
|
214
|
+
elif event.num == 5 or event.delta < 0: # Scroll down (zoom out)
|
215
|
+
scale_factor *= (1 + zoom_speed) # Multiply to zoom out
|
216
|
+
|
217
|
+
# Adjust the axes limits based on the new scale factor
|
218
|
+
for ax in canvas.figure.get_axes():
|
219
|
+
xlim = ax.get_xlim()
|
220
|
+
ylim = ax.get_ylim()
|
221
|
+
|
222
|
+
x_center = (xlim[1] + xlim[0]) / 2
|
223
|
+
y_center = (ylim[1] + ylim[0]) / 2
|
224
|
+
|
225
|
+
x_range = (xlim[1] - xlim[0]) * scale_factor
|
226
|
+
y_range = (ylim[1] - ylim[0]) * scale_factor
|
227
|
+
|
228
|
+
# Set the new limits
|
229
|
+
ax.set_xlim([x_center - x_range / 2, x_center + x_range / 2])
|
230
|
+
ax.set_ylim([y_center - y_range / 2, y_center + y_range / 2])
|
231
|
+
|
232
|
+
# Redraw the figure efficiently
|
233
|
+
canvas.draw_idle()
|
234
|
+
|
235
|
+
|
233
236
|
# Bind events for hover, click interactions, and zoom
|
234
237
|
canvas_widget.bind("<Motion>", on_hover)
|
235
238
|
canvas_widget.bind("<Leave>", on_leave)
|
236
239
|
canvas_widget.bind("<Button-1>", on_click)
|
237
240
|
canvas_widget.bind("<Button-3>", on_right_click)
|
238
241
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
242
|
+
|
243
|
+
# Detect the operating system and bind the appropriate mouse wheel events
|
244
|
+
current_os = platform.system()
|
245
|
+
|
246
|
+
if current_os == "Windows":
|
247
|
+
canvas_widget.bind("<MouseWheel>", zoom) # Windows
|
248
|
+
elif current_os == "Darwin": # macOS
|
249
|
+
canvas_widget.bind("<MouseWheel>", zoom)
|
250
|
+
canvas_widget.bind("<Button-4>", zoom) # Scroll up
|
251
|
+
canvas_widget.bind("<Button-5>", zoom) # Scroll down
|
252
|
+
elif current_os == "Linux":
|
253
|
+
canvas_widget.bind("<Button-4>", zoom) # Linux Scroll up
|
254
|
+
canvas_widget.bind("<Button-5>", zoom) # Linux Scroll down
|
243
255
|
|
244
256
|
def clear_unused_figures():
|
245
257
|
global figures, figure_index
|
@@ -253,6 +265,7 @@ def clear_unused_figures():
|
|
253
265
|
|
254
266
|
def show_previous_figure():
|
255
267
|
global figure_index, figures, fig_queue
|
268
|
+
|
256
269
|
if figure_index is not None and figure_index > 0:
|
257
270
|
figure_index -= 1
|
258
271
|
display_figure(figures[figure_index])
|
@@ -270,8 +283,104 @@ def show_next_figure():
|
|
270
283
|
figure_index += 1
|
271
284
|
display_figure(fig)
|
272
285
|
|
273
|
-
def
|
274
|
-
global
|
286
|
+
def process_fig_queue():
|
287
|
+
global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index, index_control
|
288
|
+
|
289
|
+
from .gui_elements import standardize_figure
|
290
|
+
try:
|
291
|
+
while not fig_queue.empty():
|
292
|
+
fig = fig_queue.get_nowait()
|
293
|
+
|
294
|
+
if fig is None:
|
295
|
+
print("Warning: Retrieved a None figure from fig_queue.")
|
296
|
+
continue # Skip processing if the figure is None
|
297
|
+
|
298
|
+
# Standardize the figure appearance before adding it to the list
|
299
|
+
fig = standardize_figure(fig)
|
300
|
+
|
301
|
+
figures.append(fig)
|
302
|
+
|
303
|
+
# Update the slider range and set the value to the latest figure index
|
304
|
+
index_control.set_to(len(figures) - 1)
|
305
|
+
|
306
|
+
if figure_index == -1:
|
307
|
+
figure_index += 1
|
308
|
+
display_figure(figures[figure_index])
|
309
|
+
index_control.set(figure_index)
|
310
|
+
|
311
|
+
except Exception as e:
|
312
|
+
traceback.print_exc()
|
313
|
+
finally:
|
314
|
+
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
315
|
+
parent_frame.after_tasks.append(after_id)
|
316
|
+
|
317
|
+
def update_figure(value):
|
318
|
+
global figure_index, figures
|
319
|
+
|
320
|
+
# Convert the value to an integer
|
321
|
+
index = int(value)
|
322
|
+
|
323
|
+
# Check if the index is valid
|
324
|
+
if 0 <= index < len(figures):
|
325
|
+
figure_index = index
|
326
|
+
display_figure(figures[figure_index])
|
327
|
+
|
328
|
+
# Update the index control widget's range and value
|
329
|
+
index_control.set_to(len(figures) - 1)
|
330
|
+
index_control.set(figure_index)
|
331
|
+
|
332
|
+
def setup_plot_section(vertical_container):
|
333
|
+
global canvas, canvas_widget, figures, figure_index, index_control
|
334
|
+
|
335
|
+
# Initialize deque for storing figures and the current index
|
336
|
+
figures = deque()
|
337
|
+
|
338
|
+
# Create a frame for the plot section
|
339
|
+
plot_frame = tk.Frame(vertical_container)
|
340
|
+
vertical_container.add(plot_frame, stretch="always")
|
341
|
+
|
342
|
+
# Set up the plot
|
343
|
+
figure = Figure(figsize=(30, 4), dpi=100)
|
344
|
+
plot = figure.add_subplot(111)
|
345
|
+
plot.plot([], [])
|
346
|
+
plot.axis('off')
|
347
|
+
|
348
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
349
|
+
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
350
|
+
canvas_widget = canvas.get_tk_widget()
|
351
|
+
canvas_widget.grid(row=0, column=0, sticky="nsew")
|
352
|
+
|
353
|
+
plot_frame.grid_rowconfigure(0, weight=1)
|
354
|
+
plot_frame.grid_columnconfigure(0, weight=1)
|
355
|
+
|
356
|
+
canvas.draw()
|
357
|
+
canvas.figure = figure # Ensure that the figure is linked to the canvas
|
358
|
+
style_out = set_dark_style(ttk.Style())
|
359
|
+
bg = style_out['bg_color']
|
360
|
+
fg = style_out['fg_color']
|
361
|
+
|
362
|
+
figure.patch.set_facecolor(bg)
|
363
|
+
plot.set_facecolor(bg)
|
364
|
+
containers = [plot_frame]
|
365
|
+
|
366
|
+
# Create slider
|
367
|
+
control_frame = tk.Frame(plot_frame, height=15*2, bg=bg) # Fixed height based on knob_radius
|
368
|
+
control_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=5)
|
369
|
+
control_frame.grid_propagate(False) # Prevent the frame from resizing
|
370
|
+
|
371
|
+
# Pass the update_figure function as the command to spacrSlider
|
372
|
+
index_control = spacrSlider(control_frame, from_=0, to=0, value=0, thickness=2, knob_radius=10, position="center", show_index=True, command=update_figure)
|
373
|
+
index_control.grid(row=0, column=0, sticky="ew")
|
374
|
+
control_frame.grid_columnconfigure(0, weight=1)
|
375
|
+
|
376
|
+
widgets = [canvas_widget, index_control]
|
377
|
+
style = ttk.Style(vertical_container)
|
378
|
+
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
379
|
+
|
380
|
+
return canvas, canvas_widget
|
381
|
+
|
382
|
+
def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, figures_var, figure_index_var, index_control_var, progress_bar_var, usage_bars_var):
|
383
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, index_control
|
275
384
|
thread_control = thread_control_var
|
276
385
|
q = q_var
|
277
386
|
console_output = console_output_var
|
@@ -285,8 +394,7 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
|
|
285
394
|
figure_index = figure_index_var
|
286
395
|
progress_bar = progress_bar_var
|
287
396
|
usage_bars = usage_bars_var
|
288
|
-
|
289
|
-
figure_current_memory_usage = figure_current_memory_usage_var
|
397
|
+
index_control = index_control_var
|
290
398
|
|
291
399
|
def import_settings(settings_type='mask'):
|
292
400
|
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
@@ -332,6 +440,8 @@ def import_settings(settings_type='mask'):
|
|
332
440
|
settings = set_default_umap_image_settings(settings={})
|
333
441
|
elif settings_type == 'recruitment':
|
334
442
|
settings = get_analyze_recruitment_default_settings(settings={})
|
443
|
+
elif settings_type == 'analyze_plaques':
|
444
|
+
settings = {}
|
335
445
|
else:
|
336
446
|
raise ValueError(f"Invalid settings type: {settings_type}")
|
337
447
|
|
@@ -385,6 +495,8 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
385
495
|
settings = get_perform_regression_default_settings(settings={})
|
386
496
|
elif settings_type == 'recruitment':
|
387
497
|
settings = get_analyze_recruitment_default_settings(settings={})
|
498
|
+
elif settings_type == 'analyze_plaques':
|
499
|
+
settings = {}
|
388
500
|
else:
|
389
501
|
raise ValueError(f"Invalid settings type: {settings_type}")
|
390
502
|
|
@@ -400,46 +512,6 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
|
|
400
512
|
print("Settings panel setup complete")
|
401
513
|
return scrollable_frame, vars_dict
|
402
514
|
|
403
|
-
def setup_plot_section(vertical_container):
|
404
|
-
global canvas, canvas_widget, figures, figure_index
|
405
|
-
|
406
|
-
from .gui_elements import set_element_size
|
407
|
-
|
408
|
-
# Initialize deque for storing figures and the current index
|
409
|
-
figures = deque()
|
410
|
-
figure_index = -1
|
411
|
-
|
412
|
-
# Create a frame for the plot section
|
413
|
-
plot_frame = tk.Frame(vertical_container)
|
414
|
-
vertical_container.add(plot_frame, stretch="always")
|
415
|
-
|
416
|
-
# Set up the plot
|
417
|
-
figure = Figure(figsize=(30, 4), dpi=100)
|
418
|
-
plot = figure.add_subplot(111)
|
419
|
-
plot.plot([], [])
|
420
|
-
plot.axis('off')
|
421
|
-
|
422
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
423
|
-
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
424
|
-
canvas_widget = canvas.get_tk_widget()
|
425
|
-
canvas_widget.grid(row=0, column=0, sticky="nsew")
|
426
|
-
|
427
|
-
plot_frame.grid_rowconfigure(0, weight=1)
|
428
|
-
plot_frame.grid_columnconfigure(0, weight=1)
|
429
|
-
|
430
|
-
canvas.draw()
|
431
|
-
canvas.figure = figure # Ensure that the figure is linked to the canvas
|
432
|
-
style_out = set_dark_style(ttk.Style())
|
433
|
-
|
434
|
-
figure.patch.set_facecolor(style_out['bg_color'])
|
435
|
-
plot.set_facecolor(style_out['bg_color'])
|
436
|
-
containers = [plot_frame]
|
437
|
-
widgets = [canvas_widget]
|
438
|
-
style = ttk.Style(vertical_container)
|
439
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
440
|
-
|
441
|
-
return canvas, canvas_widget
|
442
|
-
|
443
515
|
def setup_console(vertical_container):
|
444
516
|
global console_output
|
445
517
|
from .gui_elements import set_dark_style
|
@@ -479,27 +551,6 @@ def setup_console(vertical_container):
|
|
479
551
|
|
480
552
|
return console_output, console_frame
|
481
553
|
|
482
|
-
def setup_progress_frame(vertical_container):
|
483
|
-
global progress_output
|
484
|
-
style_out = set_dark_style(ttk.Style())
|
485
|
-
font_loader = style_out['font_loader']
|
486
|
-
font_size = style_out['font_size']
|
487
|
-
progress_frame = tk.Frame(vertical_container)
|
488
|
-
vertical_container.add(progress_frame, stretch="always")
|
489
|
-
label_frame = tk.Frame(progress_frame)
|
490
|
-
label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
|
491
|
-
progress_label = spacrLabel(label_frame, text="Processing: 0%", font=font_loader.get_font(size=font_size), anchor='w', justify='left', align="left")
|
492
|
-
progress_label.grid(row=0, column=0, sticky="w")
|
493
|
-
progress_output = scrolledtext.ScrolledText(progress_frame, height=10)
|
494
|
-
progress_output.grid(row=1, column=0, sticky="nsew")
|
495
|
-
progress_frame.grid_rowconfigure(1, weight=1)
|
496
|
-
progress_frame.grid_columnconfigure(0, weight=1)
|
497
|
-
containers = [progress_frame, label_frame]
|
498
|
-
widgets = [progress_label, progress_output]
|
499
|
-
style = ttk.Style(vertical_container)
|
500
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
501
|
-
return progress_output
|
502
|
-
|
503
554
|
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
504
555
|
global thread_control, parent_frame, button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
505
556
|
from .gui_utils import download_hug_dataset
|
@@ -598,7 +649,7 @@ def setup_usage_panel(horizontal_container, btn_col, uppdate_frequency):
|
|
598
649
|
widgets = [usage_scrollable_frame.scrollable_frame]
|
599
650
|
|
600
651
|
usage_bars = []
|
601
|
-
max_elements_per_column =
|
652
|
+
max_elements_per_column = 5
|
602
653
|
row = 0
|
603
654
|
col = 0
|
604
655
|
|
@@ -726,9 +777,9 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
|
|
726
777
|
initialize_cuda()
|
727
778
|
|
728
779
|
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
729
|
-
if settings_type in ['mask', 'umap', 'measure', 'simulation', 'sequencing',
|
730
|
-
'
|
731
|
-
'regression', 'recruitment', '
|
780
|
+
if settings_type in ['mask', 'umap', 'measure', 'simulation', 'sequencing', 'classify', 'analyze_plaques',
|
781
|
+
'cellpose_dataset', 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
782
|
+
'regression', 'recruitment', 'cellpose_compare', 'vision_scores', 'vision_dataset']:
|
732
783
|
|
733
784
|
# Start the process
|
734
785
|
process = Process(target=run_function_gui, args=process_args)
|
@@ -836,7 +887,7 @@ def initiate_root(parent, settings_type='mask'):
|
|
836
887
|
tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
|
837
888
|
"""
|
838
889
|
|
839
|
-
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index,
|
890
|
+
global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index, index_control, usage_bars
|
840
891
|
|
841
892
|
from .gui_utils import setup_frame
|
842
893
|
from .settings import descriptions
|
@@ -853,8 +904,6 @@ def initiate_root(parent, settings_type='mask'):
|
|
853
904
|
# Initialize global variables
|
854
905
|
figures = deque()
|
855
906
|
figure_index = -1
|
856
|
-
fig_memory_limit = 200 * 1024 * 1024 # 200 MB limit
|
857
|
-
figure_current_memory_usage = 0
|
858
907
|
|
859
908
|
parent_frame = parent
|
860
909
|
|
@@ -886,7 +935,7 @@ def initiate_root(parent, settings_type='mask'):
|
|
886
935
|
button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
|
887
936
|
_, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
|
888
937
|
|
889
|
-
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars
|
938
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, index_control, progress_bar, usage_bars)
|
890
939
|
description_text = descriptions.get(settings_type, "No description available for this module.")
|
891
940
|
|
892
941
|
q.put(f"Console")
|
@@ -899,4 +948,6 @@ def initiate_root(parent, settings_type='mask'):
|
|
899
948
|
parent_window.after_tasks.append(after_id)
|
900
949
|
|
901
950
|
print("Root initialization complete")
|
902
|
-
return parent_frame, vars_dict
|
951
|
+
return parent_frame, vars_dict
|
952
|
+
|
953
|
+
|