spacr 0.0.20__py3-none-any.whl → 0.0.35__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/alpha.py +291 -14
- spacr/annotate_app.py +7 -5
- spacr/chris.py +50 -0
- spacr/core.py +1301 -426
- spacr/foldseek.py +793 -0
- spacr/get_alfafold_structures.py +72 -0
- spacr/gui.py +144 -0
- spacr/gui_classify_app.py +65 -74
- spacr/gui_mask_app.py +110 -87
- spacr/gui_measure_app.py +104 -81
- spacr/gui_utils.py +276 -31
- spacr/io.py +261 -102
- spacr/mask_app.py +6 -3
- spacr/measure.py +150 -64
- spacr/plot.py +151 -12
- spacr/sim.py +666 -119
- spacr/timelapse.py +139 -9
- spacr/train.py +18 -10
- spacr/utils.py +43 -49
- {spacr-0.0.20.dist-info → spacr-0.0.35.dist-info}/METADATA +5 -2
- spacr-0.0.35.dist-info/RECORD +35 -0
- spacr-0.0.35.dist-info/entry_points.txt +8 -0
- spacr-0.0.20.dist-info/RECORD +0 -31
- spacr-0.0.20.dist-info/entry_points.txt +0 -7
- {spacr-0.0.20.dist-info → spacr-0.0.35.dist-info}/LICENSE +0 -0
- {spacr-0.0.20.dist-info → spacr-0.0.35.dist-info}/WHEEL +0 -0
- {spacr-0.0.20.dist-info → spacr-0.0.35.dist-info}/top_level.txt +0 -0
spacr/io.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import os, re, sqlite3, gc, torch, time, random, shutil, cv2, tarfile, cellpose
|
1
|
+
import os, re, sqlite3, gc, torch, time, random, shutil, cv2, tarfile, cellpose, glob
|
2
2
|
import numpy as np
|
3
3
|
import pandas as pd
|
4
4
|
import tifffile
|
@@ -45,19 +45,19 @@ def _load_images_and_labels(image_files, label_files, circular=False, invert=Fal
|
|
45
45
|
|
46
46
|
if not image_files is None and not label_files is None:
|
47
47
|
for img_file, lbl_file in zip(image_files, label_files):
|
48
|
-
image = cellpose.imread(img_file)
|
48
|
+
image = cellpose.io.imread(img_file)
|
49
49
|
if invert:
|
50
50
|
image = invert_image(image)
|
51
51
|
if circular:
|
52
52
|
image = apply_mask(image, output_value=0)
|
53
|
-
label = cellpose.imread(lbl_file)
|
53
|
+
label = cellpose.io.imread(lbl_file)
|
54
54
|
if image.max() > 1:
|
55
55
|
image = image / image.max()
|
56
56
|
images.append(image)
|
57
57
|
labels.append(label)
|
58
58
|
elif not image_files is None:
|
59
59
|
for img_file in image_files:
|
60
|
-
image = cellpose.imread(img_file)
|
60
|
+
image = cellpose.io.imread(img_file)
|
61
61
|
if invert:
|
62
62
|
image = invert_image(image)
|
63
63
|
if circular:
|
@@ -67,7 +67,7 @@ def _load_images_and_labels(image_files, label_files, circular=False, invert=Fal
|
|
67
67
|
images.append(image)
|
68
68
|
elif not image_files is None:
|
69
69
|
for lbl_file in label_files:
|
70
|
-
label = cellpose.imread(lbl_file)
|
70
|
+
label = cellpose.io.imread(lbl_file)
|
71
71
|
if circular:
|
72
72
|
label = apply_mask(label, output_value=0)
|
73
73
|
labels.append(label)
|
@@ -109,15 +109,17 @@ def _load_normalized_images_and_labels(image_files, label_files, signal_threshol
|
|
109
109
|
|
110
110
|
if label_files is not None:
|
111
111
|
label_names = [os.path.basename(f) for f in label_files]
|
112
|
+
label_dir = os.path.dirname(label_files[0])
|
112
113
|
|
113
114
|
# Load images and check percentiles
|
114
115
|
for i,img_file in enumerate(image_files):
|
115
|
-
|
116
|
+
#print(img_file)
|
117
|
+
image = cellpose.io.imread(img_file)
|
116
118
|
if invert:
|
117
119
|
image = invert_image(image)
|
118
120
|
if circular:
|
119
121
|
image = apply_mask(image, output_value=0)
|
120
|
-
|
122
|
+
#print(image.shape)
|
121
123
|
# If specific channels are specified, select them
|
122
124
|
if channels is not None and image.ndim == 3:
|
123
125
|
image = image[..., channels]
|
@@ -169,7 +171,7 @@ def _load_normalized_images_and_labels(image_files, label_files, signal_threshol
|
|
169
171
|
|
170
172
|
if label_files is not None:
|
171
173
|
for lbl_file in label_files:
|
172
|
-
labels.append(cellpose.imread(lbl_file))
|
174
|
+
labels.append(cellpose.io.imread(lbl_file))
|
173
175
|
else:
|
174
176
|
label_names = []
|
175
177
|
label_dir = None
|
@@ -178,85 +180,6 @@ def _load_normalized_images_and_labels(image_files, label_files, signal_threshol
|
|
178
180
|
|
179
181
|
return normalized_images, labels, image_names, label_names
|
180
182
|
|
181
|
-
class MyDataset(Dataset):
|
182
|
-
"""
|
183
|
-
Custom dataset class for loading and processing image data.
|
184
|
-
|
185
|
-
Args:
|
186
|
-
data_dir (str): The directory path where the data is stored.
|
187
|
-
loader_classes (list): List of class names.
|
188
|
-
transform (callable, optional): A function/transform that takes in an PIL image and returns a transformed version. Default is None.
|
189
|
-
shuffle (bool, optional): Whether to shuffle the dataset. Default is True.
|
190
|
-
load_to_memory (bool, optional): Whether to load images into memory. Default is False.
|
191
|
-
|
192
|
-
Attributes:
|
193
|
-
data_dir (str): The directory path where the data is stored.
|
194
|
-
classes (list): List of class names.
|
195
|
-
transform (callable): A function/transform that takes in an PIL image and returns a transformed version.
|
196
|
-
shuffle (bool): Whether to shuffle the dataset.
|
197
|
-
load_to_memory (bool): Whether to load images into memory.
|
198
|
-
filenames (list): List of file paths.
|
199
|
-
labels (list): List of labels corresponding to each file.
|
200
|
-
images (list): List of loaded images.
|
201
|
-
image_cache (Cache): Cache object for storing loaded images.
|
202
|
-
|
203
|
-
Methods:
|
204
|
-
load_image: Load an image from file.
|
205
|
-
__len__: Get the length of the dataset.
|
206
|
-
shuffle_dataset: Shuffle the dataset.
|
207
|
-
__getitem__: Get an item from the dataset.
|
208
|
-
|
209
|
-
"""
|
210
|
-
|
211
|
-
def _init__(self, data_dir, loader_classes, transform=None, shuffle=True, load_to_memory=False):
|
212
|
-
from .utils import Cache
|
213
|
-
self.data_dir = data_dir
|
214
|
-
self.classes = loader_classes
|
215
|
-
self.transform = transform
|
216
|
-
self.shuffle = shuffle
|
217
|
-
self.load_to_memory = load_to_memory
|
218
|
-
self.filenames = []
|
219
|
-
self.labels = []
|
220
|
-
self.images = []
|
221
|
-
self.image_cache = Cache(50)
|
222
|
-
for class_name in self.classes:
|
223
|
-
class_path = os.path.join(data_dir, class_name)
|
224
|
-
class_files = [os.path.join(class_path, f) for f in os.listdir(class_path) if os.path.isfile(os.path.join(class_path, f))]
|
225
|
-
self.filenames.extend(class_files)
|
226
|
-
self.labels.extend([self.classes.index(class_name)] * len(class_files))
|
227
|
-
if self.shuffle:
|
228
|
-
self.shuffle_dataset()
|
229
|
-
if self.load_to_memory:
|
230
|
-
self.images = [self.load_image(f) for f in self.filenames]
|
231
|
-
|
232
|
-
def load_image(self, img_path):
|
233
|
-
img = self.image_cache.get(img_path)
|
234
|
-
if img is None:
|
235
|
-
img = Image.open(img_path).convert('RGB')
|
236
|
-
self.image_cache.put(img_path, img)
|
237
|
-
return img
|
238
|
-
|
239
|
-
def _len__(self):
|
240
|
-
return len(self.filenames)
|
241
|
-
|
242
|
-
def shuffle_dataset(self):
|
243
|
-
combined = list(zip(self.filenames, self.labels))
|
244
|
-
random.shuffle(combined)
|
245
|
-
self.filenames, self.labels = zip(*combined)
|
246
|
-
|
247
|
-
def _getitem__(self, index):
|
248
|
-
label = self.labels[index]
|
249
|
-
filename = self.filenames[index]
|
250
|
-
if self.load_to_memory:
|
251
|
-
img = self.images[index]
|
252
|
-
else:
|
253
|
-
img = self.load_image(filename)
|
254
|
-
if self.transform is not None:
|
255
|
-
img = self.transform(img)
|
256
|
-
else:
|
257
|
-
img = ToTensor()(img)
|
258
|
-
return img, label, filename
|
259
|
-
|
260
183
|
class CombineLoaders:
|
261
184
|
"""
|
262
185
|
A class that combines multiple data loaders into a single iterator.
|
@@ -383,6 +306,85 @@ class NoClassDataset(Dataset):
|
|
383
306
|
img = ToTensor()(img)
|
384
307
|
# Return both the image and its filename
|
385
308
|
return img, self.filenames[index]
|
309
|
+
|
310
|
+
class MyDataset_v1(Dataset):
|
311
|
+
"""
|
312
|
+
Custom dataset class for loading and processing image data.
|
313
|
+
|
314
|
+
Args:
|
315
|
+
data_dir (str): The directory path where the data is stored.
|
316
|
+
loader_classes (list): List of class names.
|
317
|
+
transform (callable, optional): A function/transform that takes in an PIL image and returns a transformed version. Default is None.
|
318
|
+
shuffle (bool, optional): Whether to shuffle the dataset. Default is True.
|
319
|
+
load_to_memory (bool, optional): Whether to load images into memory. Default is False.
|
320
|
+
|
321
|
+
Attributes:
|
322
|
+
data_dir (str): The directory path where the data is stored.
|
323
|
+
classes (list): List of class names.
|
324
|
+
transform (callable): A function/transform that takes in an PIL image and returns a transformed version.
|
325
|
+
shuffle (bool): Whether to shuffle the dataset.
|
326
|
+
load_to_memory (bool): Whether to load images into memory.
|
327
|
+
filenames (list): List of file paths.
|
328
|
+
labels (list): List of labels corresponding to each file.
|
329
|
+
images (list): List of loaded images.
|
330
|
+
image_cache (Cache): Cache object for storing loaded images.
|
331
|
+
|
332
|
+
Methods:
|
333
|
+
load_image: Load an image from file.
|
334
|
+
__len__: Get the length of the dataset.
|
335
|
+
shuffle_dataset: Shuffle the dataset.
|
336
|
+
__getitem__: Get an item from the dataset.
|
337
|
+
|
338
|
+
"""
|
339
|
+
|
340
|
+
def __init__(self, data_dir, loader_classes, transform=None, shuffle=True, load_to_memory=False):
|
341
|
+
from .utils import Cache
|
342
|
+
self.data_dir = data_dir
|
343
|
+
self.classes = loader_classes
|
344
|
+
self.transform = transform
|
345
|
+
self.shuffle = shuffle
|
346
|
+
self.load_to_memory = load_to_memory
|
347
|
+
self.filenames = []
|
348
|
+
self.labels = []
|
349
|
+
self.images = []
|
350
|
+
self.image_cache = Cache(50)
|
351
|
+
for class_name in self.classes:
|
352
|
+
class_path = os.path.join(data_dir, class_name)
|
353
|
+
class_files = [os.path.join(class_path, f) for f in os.listdir(class_path) if os.path.isfile(os.path.join(class_path, f))]
|
354
|
+
self.filenames.extend(class_files)
|
355
|
+
self.labels.extend([self.classes.index(class_name)] * len(class_files))
|
356
|
+
if self.shuffle:
|
357
|
+
self.shuffle_dataset()
|
358
|
+
if self.load_to_memory:
|
359
|
+
self.images = [self.load_image(f) for f in self.filenames]
|
360
|
+
|
361
|
+
def load_image(self, img_path):
|
362
|
+
img = self.image_cache.get(img_path)
|
363
|
+
if img is None:
|
364
|
+
img = Image.open(img_path).convert('RGB')
|
365
|
+
self.image_cache.put(img_path, img)
|
366
|
+
return img
|
367
|
+
|
368
|
+
def _len__(self):
|
369
|
+
return len(self.filenames)
|
370
|
+
|
371
|
+
def shuffle_dataset(self):
|
372
|
+
combined = list(zip(self.filenames, self.labels))
|
373
|
+
random.shuffle(combined)
|
374
|
+
self.filenames, self.labels = zip(*combined)
|
375
|
+
|
376
|
+
def _getitem__(self, index):
|
377
|
+
label = self.labels[index]
|
378
|
+
filename = self.filenames[index]
|
379
|
+
if self.load_to_memory:
|
380
|
+
img = self.images[index]
|
381
|
+
else:
|
382
|
+
img = self.load_image(filename)
|
383
|
+
if self.transform is not None:
|
384
|
+
img = self.transform(img)
|
385
|
+
else:
|
386
|
+
img = ToTensor()(img)
|
387
|
+
return img, label, filename
|
386
388
|
|
387
389
|
class MyDataset(Dataset):
|
388
390
|
"""
|
@@ -398,7 +400,7 @@ class MyDataset(Dataset):
|
|
398
400
|
specific_labels (list, optional): A list of specific labels corresponding to the specific files. Default is None.
|
399
401
|
"""
|
400
402
|
|
401
|
-
def
|
403
|
+
def __init__(self, data_dir, loader_classes, transform=None, shuffle=True, pin_memory=False, specific_files=None, specific_labels=None):
|
402
404
|
self.data_dir = data_dir
|
403
405
|
self.classes = loader_classes
|
404
406
|
self.transform = transform
|
@@ -427,7 +429,7 @@ class MyDataset(Dataset):
|
|
427
429
|
img = Image.open(img_path).convert('RGB')
|
428
430
|
return img
|
429
431
|
|
430
|
-
def
|
432
|
+
def __len__(self):
|
431
433
|
return len(self.filenames)
|
432
434
|
|
433
435
|
def shuffle_dataset(self):
|
@@ -439,7 +441,7 @@ class MyDataset(Dataset):
|
|
439
441
|
filename = os.path.basename(filepath) # Get just the filename from the full path
|
440
442
|
return filename.split('_')[0]
|
441
443
|
|
442
|
-
def
|
444
|
+
def __getitem__(self, index):
|
443
445
|
label = self.labels[index]
|
444
446
|
filename = self.filenames[index]
|
445
447
|
img = self.load_image(filename)
|
@@ -600,7 +602,7 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
|
|
600
602
|
shutil.move(os.path.join(src, filename), move)
|
601
603
|
return
|
602
604
|
|
603
|
-
def
|
605
|
+
def _merge_file_v1(chan_dirs, stack_dir, file):
|
604
606
|
"""
|
605
607
|
Merge multiple channels into a single stack and save it as a numpy array.
|
606
608
|
|
@@ -625,15 +627,80 @@ def _merge_file(chan_dirs, stack_dir, file):
|
|
625
627
|
stack = np.concatenate(channels, axis=2)
|
626
628
|
np.save(new_file, stack)
|
627
629
|
|
628
|
-
def
|
630
|
+
def _merge_file_v1(chan_dirs, stack_dir, file):
|
629
631
|
"""
|
630
|
-
|
632
|
+
Merge multiple channels into a single stack and save it as a numpy array.
|
633
|
+
Args:
|
634
|
+
chan_dirs (list): List of directories containing channel images.
|
635
|
+
stack_dir (str): Directory to save the merged stack.
|
636
|
+
file (str): File name of the channel image.
|
631
637
|
|
638
|
+
Returns:
|
639
|
+
None
|
640
|
+
"""
|
641
|
+
new_file = stack_dir / (file.stem + '.npy')
|
642
|
+
if not new_file.exists():
|
643
|
+
stack_dir.mkdir(exist_ok=True)
|
644
|
+
channels = []
|
645
|
+
for i, chan_dir in enumerate(chan_dirs):
|
646
|
+
img_path = str(chan_dir / file.name)
|
647
|
+
img = cv2.imread(img_path, -1)
|
648
|
+
if img is None:
|
649
|
+
print(f"Warning: Failed to read image {img_path}")
|
650
|
+
continue
|
651
|
+
chan = np.expand_dims(img, axis=2)
|
652
|
+
channels.append(chan)
|
653
|
+
del img # Explicitly delete the reference to the image to free up memory
|
654
|
+
if i % 10 == 0: # Periodically suggest garbage collection
|
655
|
+
gc.collect()
|
656
|
+
|
657
|
+
if channels:
|
658
|
+
stack = np.concatenate(channels, axis=2)
|
659
|
+
np.save(new_file, stack)
|
660
|
+
else:
|
661
|
+
print(f"No valid channels to merge for file {file.name}")
|
662
|
+
|
663
|
+
def _merge_file(chan_dirs, stack_dir, file_name):
|
664
|
+
"""
|
665
|
+
Merge multiple channels into a single stack and save it as a numpy array, using os module for path handling.
|
666
|
+
|
632
667
|
Args:
|
633
|
-
|
668
|
+
chan_dirs (list): List of directories containing channel images.
|
669
|
+
stack_dir (str): Directory to save the merged stack.
|
670
|
+
file_name (str): File name of the channel image.
|
634
671
|
|
635
672
|
Returns:
|
636
|
-
|
673
|
+
None
|
674
|
+
"""
|
675
|
+
# Construct new file path
|
676
|
+
file_root, file_ext = os.path.splitext(file_name)
|
677
|
+
new_file = os.path.join(stack_dir, file_root + '.npy')
|
678
|
+
|
679
|
+
# Check if the new file exists and create the stack directory if it doesn't
|
680
|
+
if not os.path.exists(new_file):
|
681
|
+
os.makedirs(stack_dir, exist_ok=True)
|
682
|
+
channels = []
|
683
|
+
for i, chan_dir in enumerate(chan_dirs):
|
684
|
+
img_path = os.path.join(chan_dir, file_name)
|
685
|
+
img = cv2.imread(img_path, -1)
|
686
|
+
if img is None:
|
687
|
+
print(f"Warning: Failed to read image {img_path}")
|
688
|
+
continue
|
689
|
+
chan = np.expand_dims(img, axis=2)
|
690
|
+
channels.append(chan)
|
691
|
+
del img # Explicitly delete the reference to the image to free up memory
|
692
|
+
if i % 10 == 0: # Periodically suggest garbage collection
|
693
|
+
gc.collect()
|
694
|
+
|
695
|
+
if channels:
|
696
|
+
stack = np.concatenate(channels, axis=2)
|
697
|
+
np.save(new_file, stack)
|
698
|
+
else:
|
699
|
+
print(f"No valid channels to merge for file {file_name}")
|
700
|
+
|
701
|
+
def _is_dir_empty(dir_path):
|
702
|
+
"""
|
703
|
+
Check if a directory is empty using os module.
|
637
704
|
"""
|
638
705
|
return len(os.listdir(dir_path)) == 0
|
639
706
|
|
@@ -733,7 +800,7 @@ def _move_to_chan_folder(src, regex, timelapse=False, metadata_type=''):
|
|
733
800
|
shutil.move(os.path.join(src, filename), move)
|
734
801
|
return
|
735
802
|
|
736
|
-
def
|
803
|
+
def _merge_channels_v2(src, plot=False):
|
737
804
|
from .plot import plot_arrays
|
738
805
|
"""
|
739
806
|
Merge the channels in the given source directory and save the merged files in a 'stack' directory.
|
@@ -761,7 +828,8 @@ def _merge_channels(src, plot=False):
|
|
761
828
|
print(f'generated folder with merged arrays: {stack_dir}')
|
762
829
|
|
763
830
|
if _is_dir_empty(stack_dir):
|
764
|
-
with Pool(cpu_count()) as pool:
|
831
|
+
with Pool(max(cpu_count() // 2, 1)) as pool:
|
832
|
+
#with Pool(cpu_count()) as pool:
|
765
833
|
merge_func = partial(_merge_file, chan_dirs, stack_dir)
|
766
834
|
pool.map(merge_func, dir_files)
|
767
835
|
|
@@ -773,6 +841,47 @@ def _merge_channels(src, plot=False):
|
|
773
841
|
|
774
842
|
return
|
775
843
|
|
844
|
+
def _merge_channels(src, plot=False):
|
845
|
+
"""
|
846
|
+
Merge the channels in the given source directory and save the merged files in a 'stack' directory without using multiprocessing.
|
847
|
+
"""
|
848
|
+
|
849
|
+
from .plot import plot_arrays
|
850
|
+
|
851
|
+
stack_dir = os.path.join(src, 'stack')
|
852
|
+
allowed_names = ['01', '02', '03', '04', '00', '1', '2', '3', '4', '0']
|
853
|
+
|
854
|
+
# List directories that match the allowed names
|
855
|
+
chan_dirs = [d for d in os.listdir(src) if os.path.isdir(os.path.join(src, d)) and d in allowed_names]
|
856
|
+
chan_dirs.sort()
|
857
|
+
|
858
|
+
print(f'List of folders in src: {chan_dirs}. Single channel folders.')
|
859
|
+
start_time = time.time()
|
860
|
+
|
861
|
+
# Assuming chan_dirs[0] is not empty and exists, adjust according to your logic
|
862
|
+
first_dir_path = os.path.join(src, chan_dirs[0])
|
863
|
+
dir_files = os.listdir(first_dir_path)
|
864
|
+
|
865
|
+
# Create the 'stack' directory if it doesn't exist
|
866
|
+
if not os.path.exists(stack_dir):
|
867
|
+
os.makedirs(stack_dir, exist_ok=True)
|
868
|
+
print(f'Generated folder with merged arrays: {stack_dir}')
|
869
|
+
|
870
|
+
if _is_dir_empty(stack_dir):
|
871
|
+
for file_name in dir_files:
|
872
|
+
full_file_path = os.path.join(first_dir_path, file_name)
|
873
|
+
if os.path.isfile(full_file_path):
|
874
|
+
_merge_file([os.path.join(src, d) for d in chan_dirs], stack_dir, file_name)
|
875
|
+
|
876
|
+
elapsed_time = time.time() - start_time
|
877
|
+
avg_time = elapsed_time / len(dir_files) if dir_files else 0
|
878
|
+
print(f'Average Time: {avg_time:.3f} sec, Total Elapsed Time: {elapsed_time:.3f} sec')
|
879
|
+
|
880
|
+
if plot:
|
881
|
+
plot_arrays(os.path.join(src, 'stack'))
|
882
|
+
|
883
|
+
return
|
884
|
+
|
776
885
|
def _mip_all(src, include_first_chan=True):
|
777
886
|
|
778
887
|
"""
|
@@ -1191,6 +1300,7 @@ def preprocess_img_data(settings):
|
|
1191
1300
|
extension_counts = Counter(extensions)
|
1192
1301
|
most_common_extension = extension_counts.most_common(1)[0][0]
|
1193
1302
|
img_format = None
|
1303
|
+
|
1194
1304
|
|
1195
1305
|
delete_empty_subdirectories(src)
|
1196
1306
|
|
@@ -1206,7 +1316,7 @@ def preprocess_img_data(settings):
|
|
1206
1316
|
print('Found existing channel_stack folder.')
|
1207
1317
|
if os.path.exists(src+'/norm_channel_stack'):
|
1208
1318
|
print('Found existing norm_channel_stack folder. Skipping preprocessing')
|
1209
|
-
return
|
1319
|
+
return settings, src
|
1210
1320
|
|
1211
1321
|
cmap = 'inferno'
|
1212
1322
|
figuresize = 20
|
@@ -1214,8 +1324,10 @@ def preprocess_img_data(settings):
|
|
1214
1324
|
save_dtype = 'uint16'
|
1215
1325
|
correct_illumination = False
|
1216
1326
|
|
1217
|
-
mask_channels = [settings['nucleus_channel'], settings['pathogen_channel'], settings['cell_channel']]
|
1218
|
-
backgrounds = [settings['nucleus_background'], settings['pathogen_background'], settings['cell_background']]
|
1327
|
+
#mask_channels = [settings['nucleus_channel'], settings['pathogen_channel'], settings['cell_channel']]
|
1328
|
+
#backgrounds = [settings['nucleus_background'], settings['pathogen_background'], settings['cell_background']]
|
1329
|
+
mask_channels = [settings['nucleus_channel'], settings['cell_channel'], settings['pathogen_channel']]
|
1330
|
+
backgrounds = [settings['nucleus_background'], settings['cell_background'], settings['pathogen_background']]
|
1219
1331
|
|
1220
1332
|
metadata_type = settings['metadata_type']
|
1221
1333
|
custom_regex = settings['custom_regex']
|
@@ -1230,7 +1342,6 @@ def preprocess_img_data(settings):
|
|
1230
1342
|
pick_slice = settings['pick_slice']
|
1231
1343
|
skip_mode = settings['skip_mode']
|
1232
1344
|
|
1233
|
-
|
1234
1345
|
if not img_format == None:
|
1235
1346
|
if metadata_type == 'cellvoyager':
|
1236
1347
|
regex = f'(?P<plateID>.*)_(?P<wellID>.*)_T(?P<timeID>.*)F(?P<fieldID>.*)L(?P<laserID>..)A(?P<AID>..)Z(?P<sliceID>.*)C(?P<chanID>.*){img_format}'
|
@@ -1248,6 +1359,8 @@ def preprocess_img_data(settings):
|
|
1248
1359
|
print(f'regex mode:{metadata_type} regex:{regex}')
|
1249
1360
|
|
1250
1361
|
if settings.get('test_mode', False):
|
1362
|
+
print(f'Running spacr in test mode')
|
1363
|
+
settings['plot'] = True
|
1251
1364
|
try:
|
1252
1365
|
os.rmdir(os.path.join(src, 'test'))
|
1253
1366
|
print(f"Deleted test directory: {os.path.join(src, 'test')}")
|
@@ -1256,6 +1369,10 @@ def preprocess_img_data(settings):
|
|
1256
1369
|
|
1257
1370
|
src = _run_test_mode(settings['src'], regex, timelapse=timelapse)
|
1258
1371
|
settings['src'] = src
|
1372
|
+
|
1373
|
+
if img_format == None:
|
1374
|
+
if not os.path.exists(src+'/stack'):
|
1375
|
+
_merge_channels(src, plot=False)
|
1259
1376
|
|
1260
1377
|
if not os.path.exists(src+'/stack'):
|
1261
1378
|
try:
|
@@ -2273,6 +2390,8 @@ def convert_numpy_to_tiff(folder_path, limit=None):
|
|
2273
2390
|
for i, filename in enumerate(files):
|
2274
2391
|
if limit is not None and i >= limit:
|
2275
2392
|
break
|
2393
|
+
if not filename.endswith('.npy'):
|
2394
|
+
continue
|
2276
2395
|
|
2277
2396
|
# Construct the full file path
|
2278
2397
|
file_path = os.path.join(folder_path, filename)
|
@@ -2289,7 +2408,47 @@ def convert_numpy_to_tiff(folder_path, limit=None):
|
|
2289
2408
|
print(f"Converted {filename} to {tiff_filename} and saved in 'tiff' subdirectory.")
|
2290
2409
|
return
|
2291
2410
|
|
2292
|
-
|
2411
|
+
def generate_cellpose_train_test(src, test_split=0.1):
|
2412
|
+
|
2413
|
+
mask_src = os.path.join(src, 'masks')
|
2414
|
+
img_paths = glob.glob(os.path.join(src, '*.tif'))
|
2415
|
+
img_filenames = [os.path.basename(file) for file in img_paths + img_paths]
|
2416
|
+
img_filenames = [file for file in img_filenames if os.path.exists(os.path.join(mask_src, file))]
|
2417
|
+
print(f'Found {len(img_filenames)} images with masks')
|
2418
|
+
|
2419
|
+
random.shuffle(img_filenames)
|
2420
|
+
split_index = int(len(img_filenames) * test_split)
|
2421
|
+
train_files = img_filenames[split_index:]
|
2422
|
+
test_files = img_filenames[:split_index]
|
2423
|
+
list_of_lists = [test_files, train_files]
|
2424
|
+
print(f'Split dataset into Train {len(train_files)} and Test {len(test_files)} files')
|
2425
|
+
|
2426
|
+
train_dir = os.path.join(os.path.dirname(src), 'train')
|
2427
|
+
train_dir_masks = os.path.join(train_dir, 'mask')
|
2428
|
+
test_dir = os.path.join(os.path.dirname(src), 'test')
|
2429
|
+
test_dir_masks = os.path.join(test_dir, 'mask')
|
2430
|
+
|
2431
|
+
os.makedirs(train_dir_masks, exist_ok=True)
|
2432
|
+
os.makedirs(test_dir_masks, exist_ok=True)
|
2433
|
+
for i, ls in enumerate(list_of_lists):
|
2434
|
+
|
2435
|
+
if i == 0:
|
2436
|
+
dst = test_dir
|
2437
|
+
dst_mask = test_dir_masks
|
2438
|
+
_type = 'Test'
|
2439
|
+
if i == 1:
|
2440
|
+
dst = train_dir
|
2441
|
+
dst_mask = train_dir_masks
|
2442
|
+
_type = 'Train'
|
2443
|
+
|
2444
|
+
for idx, filename in enumerate(ls):
|
2445
|
+
img_path = os.path.join(src, filename)
|
2446
|
+
mask_path = os.path.join(mask_src, filename)
|
2447
|
+
new_img_path = os.path.join(dst, filename)
|
2448
|
+
new_mask_path = os.path.join(dst_mask, filename)
|
2449
|
+
shutil.copy(img_path, new_img_path)
|
2450
|
+
shutil.copy(mask_path, new_mask_path)
|
2451
|
+
print(f'Copied {idx+1}/{len(ls)} images to {_type} set', end='\r', flush=True)
|
2293
2452
|
|
2294
2453
|
|
2295
2454
|
|
spacr/mask_app.py
CHANGED
@@ -13,7 +13,7 @@ from ttkthemes import ThemedTk
|
|
13
13
|
|
14
14
|
from .logger import log_function_call
|
15
15
|
|
16
|
-
from .gui_utils import ScrollableFrame, set_dark_style, set_default_font, create_dark_mode
|
16
|
+
from .gui_utils import ScrollableFrame, set_dark_style, set_default_font, create_dark_mode, style_text_boxes, create_menu_bar
|
17
17
|
|
18
18
|
class modify_masks:
|
19
19
|
|
@@ -759,9 +759,12 @@ def initiate_mask_app_root(width, height):
|
|
759
759
|
root = ThemedTk(theme=theme)
|
760
760
|
style = ttk.Style(root)
|
761
761
|
set_dark_style(style)
|
762
|
-
|
762
|
+
|
763
|
+
style_text_boxes(style)
|
764
|
+
set_default_font(root, font_name="Arial", size=8)
|
763
765
|
root.geometry(f"{width}x{height}")
|
764
766
|
root.title("Mask App")
|
767
|
+
create_menu_bar(root)
|
765
768
|
|
766
769
|
container = tk.PanedWindow(root, orient=tk.HORIZONTAL)
|
767
770
|
container.pack(fill=tk.BOTH, expand=True)
|
@@ -806,7 +809,7 @@ def initiate_mask_app_root(width, height):
|
|
806
809
|
create_dark_mode(root, style, console_output=None)
|
807
810
|
|
808
811
|
run_button = ttk.Button(scrollable_frame.scrollable_frame, text="Run", command=run_app)
|
809
|
-
run_button.grid(row=row, column=0, columnspan=2, pady=10)
|
812
|
+
run_button.grid(row=row, column=0, columnspan=2, pady=10, padx=10)
|
810
813
|
|
811
814
|
return root
|
812
815
|
|