spacr 0.9.2__py3-none-any.whl → 0.9.4__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 +4 -0
- spacr/gui_core.py +2 -2
- spacr/gui_elements.py +79 -10
- spacr/gui_utils.py +44 -2
- spacr/io.py +1 -1
- spacr/measure.py +6 -75
- spacr/plot.py +1 -1
- spacr/resources/icons/flow_chart_v3.png +0 -0
- spacr/settings.py +6 -3
- spacr/sp_stats.py +0 -1
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/LICENSE +1 -1
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/METADATA +34 -13
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/RECORD +16 -15
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/WHEEL +0 -0
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/entry_points.txt +0 -0
- {spacr-0.9.2.dist-info → spacr-0.9.4.dist-info}/top_level.txt +0 -0
spacr/app_annotate.py
CHANGED
@@ -30,6 +30,10 @@ def initiate_annotation_app(parent_frame):
|
|
30
30
|
settings['percentiles'] = list(map(convert_to_number, settings['percentiles'].split(','))) if settings['percentiles'] else [2, 98]
|
31
31
|
settings['normalize'] = settings['normalize'].lower() == 'true'
|
32
32
|
settings['normalize_channels'] = settings['normalize_channels'].split(',')
|
33
|
+
settings['outline'] = settings['outline'].split(',') if settings['outline'] else None
|
34
|
+
settings['outline_threshold_factor'] = float(settings['outline_threshold_factor']) if settings['outline_threshold_factor'] else 1.0
|
35
|
+
settings['outline_sigma'] = float(settings['outline_threshold_factor']) if settings['outline_threshold_factor'] else 1.0
|
36
|
+
|
33
37
|
try:
|
34
38
|
settings['measurement'] = settings['measurement'].split(',') if settings['measurement'] else None
|
35
39
|
settings['threshold'] = None if settings['threshold'].lower() == 'none' else int(settings['threshold'])
|
spacr/gui_core.py
CHANGED
@@ -361,8 +361,8 @@ def setup_plot_section(vertical_container, settings_type):
|
|
361
361
|
if settings_type == 'map_barcodes':
|
362
362
|
current_dir = os.path.dirname(__file__)
|
363
363
|
resources_path = os.path.join(current_dir, 'resources', 'icons')
|
364
|
-
gif_path = os.path.join(resources_path, 'dna_matrix.mp4')
|
365
|
-
display_media_in_plot_frame(gif_path, plot_frame)
|
364
|
+
#gif_path = os.path.join(resources_path, 'dna_matrix.mp4')
|
365
|
+
#display_media_in_plot_frame(gif_path, plot_frame)
|
366
366
|
|
367
367
|
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
368
368
|
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
spacr/gui_elements.py
CHANGED
@@ -10,18 +10,23 @@ import numpy as np
|
|
10
10
|
import pandas as pd
|
11
11
|
from PIL import Image, ImageOps, ImageTk, ImageDraw, ImageFont, ImageEnhance
|
12
12
|
from concurrent.futures import ThreadPoolExecutor
|
13
|
-
from skimage.exposure import rescale_intensity
|
14
13
|
from IPython.display import display, HTML
|
15
14
|
import imageio.v2 as imageio
|
16
15
|
from collections import deque
|
16
|
+
from skimage.filters import threshold_otsu
|
17
|
+
from skimage.exposure import rescale_intensity
|
17
18
|
from skimage.draw import polygon, line
|
18
19
|
from skimage.transform import resize
|
19
|
-
from
|
20
|
+
from skimage.morphology import dilation, disk
|
21
|
+
from skimage.segmentation import find_boundaries
|
22
|
+
from skimage.util import img_as_ubyte
|
23
|
+
from scipy.ndimage import binary_fill_holes, label, gaussian_filter
|
20
24
|
from tkinter import ttk, scrolledtext
|
21
25
|
from sklearn.model_selection import train_test_split
|
22
26
|
from xgboost import XGBClassifier
|
23
27
|
from sklearn.metrics import classification_report, confusion_matrix
|
24
28
|
|
29
|
+
|
25
30
|
fig = None
|
26
31
|
|
27
32
|
def restart_gui_app(root):
|
@@ -2209,7 +2214,7 @@ class ModifyMaskApp:
|
|
2209
2214
|
self.update_display()
|
2210
2215
|
|
2211
2216
|
class AnnotateApp:
|
2212
|
-
def __init__(self, root, db_path, src, image_type=None, channels=None, image_size=200, annotation_column='annotate', normalize=False, percentiles=(1, 99), measurement=None, threshold=None, normalize_channels=None):
|
2217
|
+
def __init__(self, root, db_path, src, image_type=None, channels=None, image_size=200, annotation_column='annotate', normalize=False, percentiles=(1, 99), measurement=None, threshold=None, normalize_channels=None, outline=None, outline_threshold_factor=1, outline_sigma=1):
|
2213
2218
|
self.root = root
|
2214
2219
|
self.db_path = db_path
|
2215
2220
|
self.src = src
|
@@ -2237,7 +2242,10 @@ class AnnotateApp:
|
|
2237
2242
|
self.measurement = measurement
|
2238
2243
|
self.threshold = threshold
|
2239
2244
|
self.normalize_channels = normalize_channels
|
2240
|
-
|
2245
|
+
self.outline = outline #([s.strip().lower() for s in outline.split(',') if s.strip()]if isinstance(outline, str) and outline else None)
|
2246
|
+
self.outline_threshold_factor = outline_threshold_factor
|
2247
|
+
self.outline_sigma = outline_sigma
|
2248
|
+
|
2241
2249
|
style_out = set_dark_style(ttk.Style())
|
2242
2250
|
self.font_loader = style_out['font_loader']
|
2243
2251
|
self.font_size = style_out['font_size']
|
@@ -2337,7 +2345,12 @@ class AnnotateApp:
|
|
2337
2345
|
'percentiles': ','.join(map(str, self.percentiles)),
|
2338
2346
|
'measurement': ','.join(self.measurement) if self.measurement else '',
|
2339
2347
|
'threshold': str(self.threshold) if self.threshold is not None else '',
|
2340
|
-
'normalize_channels': ','.join(self.normalize_channels) if self.normalize_channels else ''
|
2348
|
+
'normalize_channels': ','.join(self.normalize_channels) if self.normalize_channels else '',
|
2349
|
+
'outline': ','.join(self.outline) if self.outline else '',
|
2350
|
+
'outline_threshold_factor': str(self.outline_threshold_factor) if hasattr(self, 'outline_threshold_factor') else '1.0',
|
2351
|
+
'outline_sigma': str(self.outline_sigma) if hasattr(self, 'outline_sigma') else '1.0',
|
2352
|
+
'src': self.src,
|
2353
|
+
'db_path': self.db_path,
|
2341
2354
|
}
|
2342
2355
|
|
2343
2356
|
for key, data in vars_dict.items():
|
@@ -2354,7 +2367,10 @@ class AnnotateApp:
|
|
2354
2367
|
settings['percentiles'] = list(map(convert_to_number, settings['percentiles'].split(','))) if settings['percentiles'] else [1, 99]
|
2355
2368
|
settings['normalize'] = settings['normalize'].lower() == 'true'
|
2356
2369
|
settings['normalize_channels'] = settings['normalize_channels'].split(',') if settings['normalize_channels'] else None
|
2357
|
-
|
2370
|
+
settings['outline'] = settings['outline'].split(',') if settings['outline'] else None
|
2371
|
+
settings['outline_threshold_factor'] = float(settings['outline_threshold_factor'].replace(',', '.')) if settings['outline_threshold_factor'] else 1.0
|
2372
|
+
settings['outline_sigma'] = float(settings['outline_sigma'].replace(',', '.')) if settings['outline_sigma'] else 1.0
|
2373
|
+
|
2358
2374
|
try:
|
2359
2375
|
settings['measurement'] = settings['measurement'].split(',') if settings['measurement'] else None
|
2360
2376
|
settings['threshold'] = None if settings['threshold'].lower() == 'none' else int(settings['threshold'])
|
@@ -2379,7 +2395,12 @@ class AnnotateApp:
|
|
2379
2395
|
'percentiles': settings.get('percentiles'),
|
2380
2396
|
'measurement': settings.get('measurement'),
|
2381
2397
|
'threshold': settings.get('threshold'),
|
2382
|
-
'normalize_channels': settings.get('normalize_channels')
|
2398
|
+
'normalize_channels': settings.get('normalize_channels'),
|
2399
|
+
'outline': settings.get('outline'),
|
2400
|
+
'outline_threshold_factor': settings.get('outline_threshold_factor'),
|
2401
|
+
'outline_sigma': settings.get('outline_sigma'),
|
2402
|
+
'src': self.src,
|
2403
|
+
'db_path': self.db_path
|
2383
2404
|
})
|
2384
2405
|
|
2385
2406
|
settings_window.destroy()
|
@@ -2389,22 +2410,32 @@ class AnnotateApp:
|
|
2389
2410
|
|
2390
2411
|
def update_settings(self, **kwargs):
|
2391
2412
|
allowed_attributes = {
|
2392
|
-
'image_type', 'channels', 'image_size', 'annotation_column',
|
2393
|
-
'normalize', 'percentiles', 'measurement', 'threshold', 'normalize_channels'
|
2413
|
+
'image_type', 'channels', 'image_size', 'annotation_column', 'src', 'db_path',
|
2414
|
+
'normalize', 'percentiles', 'measurement', 'threshold', 'normalize_channels', 'outline', 'outline_threshold_factor', 'outline_sigma'
|
2394
2415
|
}
|
2395
2416
|
|
2396
2417
|
updated = False
|
2397
|
-
|
2418
|
+
|
2398
2419
|
for attr, value in kwargs.items():
|
2399
2420
|
if attr in allowed_attributes and value is not None:
|
2421
|
+
if attr == 'outline':
|
2422
|
+
if isinstance(value, str):
|
2423
|
+
value = [s.strip().lower() for s in value.split(',') if s.strip()]
|
2424
|
+
elif attr == 'outline_threshold_factor':
|
2425
|
+
value = float(value)
|
2426
|
+
elif attr == 'outline_sigma':
|
2427
|
+
value = float(value)
|
2400
2428
|
setattr(self, attr, value)
|
2401
2429
|
updated = True
|
2402
2430
|
|
2431
|
+
|
2403
2432
|
if 'image_size' in kwargs:
|
2404
2433
|
if isinstance(self.image_size, list):
|
2405
2434
|
self.image_size = (int(self.image_size[0]), int(self.image_size[0]))
|
2406
2435
|
elif isinstance(self.image_size, int):
|
2407
2436
|
self.image_size = (self.image_size, self.image_size)
|
2437
|
+
elif isinstance(self.image_size, tuple) and len(self.image_size) == 2:
|
2438
|
+
self.image_size = tuple(map(int, self.image_size))
|
2408
2439
|
else:
|
2409
2440
|
raise ValueError("Invalid image size")
|
2410
2441
|
|
@@ -2599,9 +2630,47 @@ class AnnotateApp:
|
|
2599
2630
|
img = self.normalize_image(img, self.normalize, self.percentiles, self.normalize_channels)
|
2600
2631
|
img = img.convert('RGB')
|
2601
2632
|
img = self.filter_channels(img)
|
2633
|
+
|
2634
|
+
if self.outline:
|
2635
|
+
img = self.outline_image(img, self.outline_sigma)
|
2636
|
+
|
2602
2637
|
img = img.resize(self.image_size)
|
2603
2638
|
return img, annotation
|
2604
2639
|
|
2640
|
+
def outline_image(self, img, edge_sigma=1, edge_thickness=1):
|
2641
|
+
"""
|
2642
|
+
For each selected channel, compute a continuous outline from the intensity landscape
|
2643
|
+
using Otsu threshold scaled by a correction factor. Replace only that channel.
|
2644
|
+
"""
|
2645
|
+
arr = np.asarray(img)
|
2646
|
+
if arr.ndim != 3 or arr.shape[2] != 3:
|
2647
|
+
return img # not RGB
|
2648
|
+
|
2649
|
+
out_img = arr.copy()
|
2650
|
+
channel_map = {'r': 0, 'g': 1, 'b': 2}
|
2651
|
+
factor = getattr(self, 'outline_threshold_factor', 1.0)
|
2652
|
+
|
2653
|
+
for ch in self.outline:
|
2654
|
+
if ch not in channel_map:
|
2655
|
+
continue
|
2656
|
+
idx = channel_map[ch]
|
2657
|
+
channel_data = arr[:, :, idx]
|
2658
|
+
|
2659
|
+
try:
|
2660
|
+
channel_data = gaussian_filter(channel_data, sigma=edge_sigma)
|
2661
|
+
otsu_thresh = threshold_otsu(channel_data)
|
2662
|
+
corrected_thresh = min(255, otsu_thresh * factor)
|
2663
|
+
fg_mask = channel_data > corrected_thresh
|
2664
|
+
except Exception:
|
2665
|
+
continue
|
2666
|
+
|
2667
|
+
edge = find_boundaries(fg_mask, mode='inner')
|
2668
|
+
thick_edge = dilation(edge, disk(edge_thickness))
|
2669
|
+
|
2670
|
+
out_img[:, :, idx] = (thick_edge * 255).astype(np.uint8)
|
2671
|
+
|
2672
|
+
return Image.fromarray(out_img)
|
2673
|
+
|
2605
2674
|
@staticmethod
|
2606
2675
|
def normalize_image(img, normalize=False, percentiles=(1, 99), normalize_channels=None):
|
2607
2676
|
"""
|
spacr/gui_utils.py
CHANGED
@@ -252,7 +252,7 @@ def annotate(settings):
|
|
252
252
|
app.load_images()
|
253
253
|
root.mainloop()
|
254
254
|
|
255
|
-
def
|
255
|
+
def generate_annotate_fields_v1(frame):
|
256
256
|
from .settings import set_annotate_default_settings
|
257
257
|
from .gui_elements import set_dark_style
|
258
258
|
|
@@ -281,6 +281,48 @@ def generate_annotate_fields(frame):
|
|
281
281
|
|
282
282
|
return vars_dict
|
283
283
|
|
284
|
+
def generate_annotate_fields(frame):
|
285
|
+
from .settings import set_annotate_default_settings
|
286
|
+
from .gui_elements import set_dark_style
|
287
|
+
|
288
|
+
style_out = set_dark_style(ttk.Style())
|
289
|
+
font_loader = style_out['font_loader']
|
290
|
+
font_size = style_out['font_size'] - 2
|
291
|
+
|
292
|
+
vars_dict = {}
|
293
|
+
settings = set_annotate_default_settings(settings={})
|
294
|
+
|
295
|
+
for setting in settings:
|
296
|
+
vars_dict[setting] = {
|
297
|
+
'entry': ttk.Entry(frame),
|
298
|
+
'value': settings[setting]
|
299
|
+
}
|
300
|
+
|
301
|
+
# Arrange input fields and labels
|
302
|
+
for row, (name, data) in enumerate(vars_dict.items()):
|
303
|
+
tk.Label(
|
304
|
+
frame,
|
305
|
+
text=f"{name.replace('_', ' ').capitalize()}:",
|
306
|
+
bg=style_out['bg_color'],
|
307
|
+
fg=style_out['fg_color'],
|
308
|
+
font=font_loader.get_font(size=font_size)
|
309
|
+
).grid(row=row, column=0)
|
310
|
+
|
311
|
+
value = data['value']
|
312
|
+
if isinstance(value, list):
|
313
|
+
string_value = ','.join(map(str, value))
|
314
|
+
elif isinstance(value, (int, float, bool)):
|
315
|
+
string_value = str(value)
|
316
|
+
elif value is None:
|
317
|
+
string_value = ''
|
318
|
+
else:
|
319
|
+
string_value = value
|
320
|
+
|
321
|
+
data['entry'].insert(0, string_value)
|
322
|
+
data['entry'].grid(row=row, column=1)
|
323
|
+
|
324
|
+
return vars_dict
|
325
|
+
|
284
326
|
def run_annotate_app(vars_dict, parent_frame):
|
285
327
|
settings = {key: data['entry'].get() for key, data in vars_dict.items()}
|
286
328
|
settings['channels'] = settings['channels'].split(',')
|
@@ -349,7 +391,7 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
|
|
349
391
|
screen_height = root.winfo_screenheight()
|
350
392
|
root.geometry(f"{screen_width}x{screen_height}")
|
351
393
|
|
352
|
-
app = AnnotateApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'], normalize_channels=settings['normalize_channels'])
|
394
|
+
app = AnnotateApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'], normalize_channels=settings['normalize_channels'], outline=settings['outline'], outline_threshold_factor=settings['outline_threshold_factor'], outline_sigma=settings['outline_sigma'])
|
353
395
|
|
354
396
|
# Set the canvas background to black
|
355
397
|
root.configure(bg='black')
|
spacr/io.py
CHANGED
@@ -1587,7 +1587,7 @@ def preprocess_img_data(settings):
|
|
1587
1587
|
print(f"Found {extension_counts[most_common_extension]} {most_common_extension} files")
|
1588
1588
|
|
1589
1589
|
else:
|
1590
|
-
print(f"Could not find any {valid_ext} files in {src}
|
1590
|
+
print(f"Could not find any {valid_ext} files in {src}")
|
1591
1591
|
print(f"{files} in {src}")
|
1592
1592
|
print(f"Please check the folder and try again")
|
1593
1593
|
|
spacr/measure.py
CHANGED
@@ -331,7 +331,7 @@ def _extended_regionprops_table(labels, image, intensity_props):
|
|
331
331
|
df['frac_low10'] = frac_low10
|
332
332
|
df['entropy_intensity'] = entropy_intensity
|
333
333
|
|
334
|
-
percentiles = [5, 10, 25,
|
334
|
+
percentiles = [5, 10, 25, 75, 85, 95]
|
335
335
|
for p in percentiles:
|
336
336
|
df[f'percentile_{p}'] = [
|
337
337
|
np.percentile(region.intensity_image[region.image], p)
|
@@ -339,78 +339,6 @@ def _extended_regionprops_table(labels, image, intensity_props):
|
|
339
339
|
]
|
340
340
|
return df
|
341
341
|
|
342
|
-
def _extended_regionprops_table_v2(labels, image, intensity_props):
|
343
|
-
"""
|
344
|
-
Calculate extended region properties table, adding integrated intensity,
|
345
|
-
skewness, kurtosis, std, and median intensity per region.
|
346
|
-
"""
|
347
|
-
# regionprops_table gives you vectorized props, but not everything you want
|
348
|
-
props = regionprops_table(labels, image, properties=intensity_props)
|
349
|
-
df = pd.DataFrame(props)
|
350
|
-
|
351
|
-
# Compute extra features region-by-region
|
352
|
-
regions = regionprops(labels, intensity_image=image)
|
353
|
-
integrated_intensity = []
|
354
|
-
std_intensity = []
|
355
|
-
median_intensity = []
|
356
|
-
skew_intensity = []
|
357
|
-
kurtosis_intensity = []
|
358
|
-
for region in regions:
|
359
|
-
intens = region.intensity_image[region.image]
|
360
|
-
# Handle empty region edge-case (shouldn't happen)
|
361
|
-
if intens.size == 0:
|
362
|
-
integrated_intensity.append(np.nan)
|
363
|
-
std_intensity.append(np.nan)
|
364
|
-
median_intensity.append(np.nan)
|
365
|
-
skew_intensity.append(np.nan)
|
366
|
-
kurtosis_intensity.append(np.nan)
|
367
|
-
else:
|
368
|
-
integrated_intensity.append(np.sum(intens))
|
369
|
-
std_intensity.append(np.std(intens))
|
370
|
-
median_intensity.append(np.median(intens))
|
371
|
-
# Only valid for >2 pixels
|
372
|
-
skew_intensity.append(skew(intens) if intens.size > 2 else np.nan)
|
373
|
-
kurtosis_intensity.append(kurtosis(intens) if intens.size > 3 else np.nan)
|
374
|
-
|
375
|
-
df['integrated_intensity'] = integrated_intensity
|
376
|
-
df['std_intensity'] = std_intensity
|
377
|
-
df['median_intensity'] = median_intensity
|
378
|
-
df['skew_intensity'] = skew_intensity
|
379
|
-
df['kurtosis_intensity'] = kurtosis_intensity
|
380
|
-
|
381
|
-
# You can add other features here if desired
|
382
|
-
|
383
|
-
# Percentiles (your existing code—optional if you want to keep)
|
384
|
-
percentiles = [5, 10, 25, 50, 75, 85, 95]
|
385
|
-
for p in percentiles:
|
386
|
-
df[f'percentile_{p}'] = [
|
387
|
-
np.percentile(region.intensity_image[region.image], p)
|
388
|
-
for region in regions
|
389
|
-
]
|
390
|
-
return df
|
391
|
-
|
392
|
-
def _extended_regionprops_table_v1(labels, image, intensity_props):
|
393
|
-
"""
|
394
|
-
Calculate extended region properties table.
|
395
|
-
|
396
|
-
Args:
|
397
|
-
labels (ndarray): Labeled image.
|
398
|
-
image (ndarray): Input image.
|
399
|
-
intensity_props (list): List of intensity properties to calculate.
|
400
|
-
|
401
|
-
Returns:
|
402
|
-
DataFrame: Extended region properties table.
|
403
|
-
|
404
|
-
"""
|
405
|
-
regions = regionprops(labels, image)
|
406
|
-
props = regionprops_table(labels, image, properties=intensity_props)
|
407
|
-
percentiles = [5, 10, 25, 50, 75, 85, 95]
|
408
|
-
for p in percentiles:
|
409
|
-
props[f'percentile_{p}'] = [
|
410
|
-
np.percentile(region.intensity_image.flatten()[~np.isnan(region.intensity_image.flatten())], p)
|
411
|
-
for region in regions]
|
412
|
-
return pd.DataFrame(props)
|
413
|
-
|
414
342
|
def _calculate_homogeneity(label, channel, distances=[2,4,8,16,32,64]):
|
415
343
|
"""
|
416
344
|
Calculate the homogeneity values for each region in the label mask.
|
@@ -767,8 +695,11 @@ def _intensity_measurements(cell_mask, nucleus_mask, pathogen_mask, cytoplasm_ma
|
|
767
695
|
df.append(mask_intensity_df)
|
768
696
|
|
769
697
|
if isinstance(settings['distance_gaussian_sigma'], int):
|
770
|
-
|
771
|
-
|
698
|
+
if settings['distance_gaussian_sigma'] != 0:
|
699
|
+
if settings['cell_mask_dim'] != None:
|
700
|
+
if settings['nucleus_mask_dim'] != None or settings['pathogen_mask_dim'] != None:
|
701
|
+
intensity_distance_df = _measure_intensity_distance(cell_mask, nucleus_mask, pathogen_mask, channel_arrays, settings)
|
702
|
+
cell_dfs.append(intensity_distance_df)
|
772
703
|
|
773
704
|
if radial_dist:
|
774
705
|
if np.max(nucleus_mask) != 0:
|
spacr/plot.py
CHANGED
@@ -1154,7 +1154,7 @@ def _plot_cropped_arrays(stack, filename, figuresize=10, cmap='inferno', thresho
|
|
1154
1154
|
for channel in range(num_channels):
|
1155
1155
|
plot_single_array(stack[:, :, channel], axs[channel], f'C. {channel}', plt.get_cmap(cmap))
|
1156
1156
|
fig.tight_layout()
|
1157
|
-
print(f'{filename}')
|
1157
|
+
#print(f'{filename}')
|
1158
1158
|
return fig
|
1159
1159
|
|
1160
1160
|
def _visualize_and_save_timelapse_stack_with_tracks(masks, tracks_df, save, src, name, plot, filenames, object_type, mode='btrack', interactive=False):
|
Binary file
|
spacr/settings.py
CHANGED
@@ -314,7 +314,7 @@ def get_measure_crop_settings(settings={}):
|
|
314
314
|
settings.setdefault('cytoplasm_min_size',0)
|
315
315
|
settings.setdefault('merge_edge_pathogen_cells', True)
|
316
316
|
|
317
|
-
settings.setdefault('distance_gaussian_sigma',
|
317
|
+
settings.setdefault('distance_gaussian_sigma', 10)
|
318
318
|
|
319
319
|
if settings['test_mode']:
|
320
320
|
settings['verbose'] = True
|
@@ -1003,7 +1003,7 @@ expected_types = {
|
|
1003
1003
|
"nucleus_diamiter":int,
|
1004
1004
|
"pathogen_diamiter":int,
|
1005
1005
|
"consolidate":bool,
|
1006
|
-
"distance_gaussian_sigma":int
|
1006
|
+
"distance_gaussian_sigma": (int, type(None))
|
1007
1007
|
}
|
1008
1008
|
|
1009
1009
|
categories = {"Paths":[ "src", "grna", "barcodes", "custom_model_path", "dataset","model_path","grna_csv","row_csv","column_csv", "metadata_files", "score_data","count_data"],
|
@@ -1053,7 +1053,7 @@ def check_settings(vars_dict, expected_types, q=None):
|
|
1053
1053
|
expected_type = expected_types.get(key, str)
|
1054
1054
|
|
1055
1055
|
try:
|
1056
|
-
if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "png_dims", "pathogen_plate_metadata", "treatment_plate_metadata", "timelapse_objects", "class_metadata", "crop_mode"]:
|
1056
|
+
if key in ["cell_plate_metadata", "timelapse_frame_limits", "png_size", "png_dims", "pathogen_plate_metadata", "treatment_plate_metadata", "timelapse_objects", "class_metadata", "crop_mode", "dialate_png_ratios"]:
|
1057
1057
|
if value is None:
|
1058
1058
|
parsed_value = None
|
1059
1059
|
else:
|
@@ -1467,6 +1467,9 @@ def set_annotate_default_settings(settings):
|
|
1467
1467
|
settings.setdefault('annotation_column', 'test')
|
1468
1468
|
settings.setdefault('normalize', 'False')
|
1469
1469
|
settings.setdefault('normalize_channels', "r,g,b")
|
1470
|
+
settings.setdefault('outline', None)
|
1471
|
+
settings.setdefault('outline_threshold_factor', 1)
|
1472
|
+
settings.setdefault('outline_sigma', 1)
|
1470
1473
|
settings.setdefault('percentiles', [2, 98])
|
1471
1474
|
settings.setdefault('measurement', '') #'cytoplasm_channel_3_mean_intensity,pathogen_channel_3_mean_intensity')
|
1472
1475
|
settings.setdefault('threshold', '') #'2')
|
spacr/sp_stats.py
CHANGED
@@ -7,7 +7,6 @@ from scipy.stats import chi2_contingency, fisher_exact
|
|
7
7
|
import itertools
|
8
8
|
from statsmodels.stats.multitest import multipletests
|
9
9
|
|
10
|
-
|
11
10
|
def choose_p_adjust_method(num_groups, num_data_points):
|
12
11
|
"""
|
13
12
|
Selects the most appropriate p-value adjustment method based on data characteristics.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: spacr
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.4
|
4
4
|
Summary: Spatial phenotype analysis of crisp screens (SpaCr)
|
5
5
|
Home-page: https://github.com/EinarOlafsson/spacr
|
6
6
|
Author: Einar Birnir Olafsson
|
@@ -13,7 +13,7 @@ License-File: LICENSE
|
|
13
13
|
Requires-Dist: numpy <2.0,>=1.26.4
|
14
14
|
Requires-Dist: pandas <3.0,>=2.2.1
|
15
15
|
Requires-Dist: scipy <2.0,>=1.12.0
|
16
|
-
Requires-Dist: cellpose <
|
16
|
+
Requires-Dist: cellpose <5.0,>=4.0
|
17
17
|
Requires-Dist: scikit-image <1.0,>=0.22.0
|
18
18
|
Requires-Dist: scikit-learn <2.0,>=1.4.1
|
19
19
|
Requires-Dist: scikit-posthocs <0.20,>=0.10.0
|
@@ -86,8 +86,8 @@ Requires-Dist: opencv-python-headless ; extra == 'headless'
|
|
86
86
|
:target: https://badge.fury.io/py/spacr
|
87
87
|
.. |Python version| image:: https://img.shields.io/pypi/pyversions/spacr
|
88
88
|
:target: https://pypistats.org/packages/spacr
|
89
|
-
.. |Licence:
|
90
|
-
:target: https://github.com/EinarOlafsson/spacr/blob/
|
89
|
+
.. |Licence: MIT| image:: https://img.shields.io/github/license/EinarOlafsson/spacr
|
90
|
+
:target: https://github.com/EinarOlafsson/spacr/blob/main/LICENSE
|
91
91
|
.. |repo size| image:: https://img.shields.io/github/repo-size/EinarOlafsson/spacr
|
92
92
|
:target: https://github.com/EinarOlafsson/spacr/
|
93
93
|
|
@@ -95,7 +95,7 @@ Requires-Dist: opencv-python-headless ; extra == 'headless'
|
|
95
95
|
|
96
96
|
Badges
|
97
97
|
------
|
98
|
-
|Docs| |PyPI version| |Python version| |Licence:
|
98
|
+
|Docs| |PyPI version| |Python version| |Licence: MIT| |repo size|
|
99
99
|
|
100
100
|
SpaCr
|
101
101
|
=====
|
@@ -118,7 +118,7 @@ Features
|
|
118
118
|
- **Sequencing:** Map FASTQ reads to barcode and gRNA barcode metadata.
|
119
119
|
- **Misc:** Analyze Ca oscillation, recruitment, infection rate, plaque size/count.
|
120
120
|
|
121
|
-
.. image:: https://github.com/EinarOlafsson/spacr/raw/main/spacr/resources/icons/
|
121
|
+
.. image:: https://github.com/EinarOlafsson/spacr/raw/main/spacr/resources/icons/flow_chart_v3.png
|
122
122
|
:alt: SpaCr workflow
|
123
123
|
:align: center
|
124
124
|
|
@@ -167,27 +167,48 @@ Data Availability
|
|
167
167
|
|
168
168
|
- **Raw sequencing data** are available from NCBI BioProject `PRJNA1261935 <https://www.ncbi.nlm.nih.gov/bioproject/PRJNA1261935>`_ and SRA accessions: `SRR33531217 <https://www.ncbi.nlm.nih.gov/sra/SRR33531217>`_, `SRR33531218 <https://www.ncbi.nlm.nih.gov/sra/SRR33531218>`_, `SRR33531219 <https://www.ncbi.nlm.nih.gov/sra/SRR33531219>`_, `SRR33531220 <https://www.ncbi.nlm.nih.gov/sra/SRR33531220>`_
|
169
169
|
|
170
|
-
- **Image data** is deposited at EBI BioStudies under accession:
|
170
|
+
- **Image data** is deposited at EBI BioStudies under accession:
|
171
|
+
`S-BIAD2076 <https://www.ebi.ac.uk/biostudies/studies/S-BIAD2076>`_
|
172
|
+
*(If the link redirects to the main BioStudies portal, copy and paste it directly into your browser.)*
|
173
|
+
|
171
174
|
|
172
175
|
Example Notebooks
|
173
176
|
-----------------
|
174
177
|
|
175
178
|
The following example Jupyter notebooks illustrate common workflows using spaCR.
|
176
179
|
|
177
|
-
- `
|
180
|
+
- `Generate masks <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/1_spacr_generate_masks.ipynb>`_
|
178
181
|
*Generate cell, nuclei, and pathogen segmentation masks from microscopy images using Cellpose.*
|
179
182
|
|
180
|
-
- `
|
183
|
+
- `Capture single cell images and measurements <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/2_spacr_generate_mesurments_crop_images.ipynb>`_
|
181
184
|
*Extract object-level measurements and crop single-cell images for downstream analysis.*
|
182
185
|
|
183
|
-
- `
|
186
|
+
- `Machine learning based object classification <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/3a_spacr_machine_learning.ipynb>`_
|
184
187
|
*Train traditional machine learning models (e.g., XGBoost) to classify cell phenotypes based on extracted features.*
|
185
188
|
|
186
|
-
- `
|
189
|
+
- `Computer vision based object classification <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/3b_spacr_computer_vision.ipynb>`_
|
187
190
|
*Train and evaluate deep learning models (PyTorch CNNs/Transformers) on cropped object images.*
|
188
191
|
|
189
|
-
- `
|
192
|
+
- `Map sequencing barcodes <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/4_spacr_map_barecodes.ipynb>`_
|
190
193
|
*Map sequencing reads to row, column, and gRNA barcodes for CRISPR screen genotype-phenotype mapping.*
|
191
194
|
|
192
|
-
- `
|
195
|
+
- `Finetune cellpose models <https://github.com/EinarOlafsson/spacr/blob/main/Notebooks/5_spacr_train_cellpose.ipynb>`_
|
193
196
|
*Finetune Cellpose models using your own annotated training data for improved segmentation accuracy.*
|
197
|
+
|
198
|
+
Interactive Tutorial (under construction)
|
199
|
+
-----------------------------------------
|
200
|
+
|
201
|
+
Click below to explore the step-by-step GUI and Notebook tutorials for spaCR:
|
202
|
+
|
203
|
+
`spaCR Tutorial Page <https://einarolafsson.github.io/spacr/tutorial/>`_
|
204
|
+
|
205
|
+
License
|
206
|
+
-------
|
207
|
+
spaCR is distributed under the terms of the MIT License.
|
208
|
+
See the `LICENSE <https://github.com/EinarOlafsson/spacr/blob/main/LICENSE>`_ file for details.
|
209
|
+
|
210
|
+
How to Cite
|
211
|
+
-----------
|
212
|
+
If you use spaCR in your research, please cite:
|
213
|
+
Olafsson EB, et al. SpaCr: Spatial phenotype analysis of CRISPR-Cas9 screens. *Manuscript in preparation*.
|
214
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
spacr/__init__.py,sha256=EoGInYks0M4foZElYNhksrQK6aEO1au7cncWexWNhRw,1376
|
2
2
|
spacr/__main__.py,sha256=H4MjaMF9ohZL6xfl1kTxVn1Nt_vEhhZArENMMBv8f4E,77
|
3
|
-
spacr/app_annotate.py,sha256=
|
3
|
+
spacr/app_annotate.py,sha256=p0OyvgFycIug7RcLfejFmc4HWB7yQskCBxxy3Sdq_Y0,2905
|
4
4
|
spacr/app_classify.py,sha256=urTP_wlZ58hSyM5a19slYlBxN0PdC-9-ga0hvq8CGWc,165
|
5
5
|
spacr/app_make_masks.py,sha256=pqDhRpluiHZz-kPX2Zh_KbYe4TsU43qYBa_7f-rsjpw,1694
|
6
6
|
spacr/app_mask.py,sha256=l-dBY8ftzCMdDe6-pXc2Nh_u-idNL9G7UOARiLJBtds,153
|
@@ -11,20 +11,20 @@ spacr/chat_bot.py,sha256=n3Fhqg3qofVXHmh3H9sUcmfYy9MmgRnr48663MVdY9E,1244
|
|
11
11
|
spacr/core.py,sha256=w4E3Pg-ZnA8BOK0iUMTjiNO0GeR5YCEs8fUTbESzqjY,47392
|
12
12
|
spacr/deep_spacr.py,sha256=055tIo3WP3elGFiIuSZaLURgu2XyUDxAdbw5ezASEqM,54526
|
13
13
|
spacr/gui.py,sha256=NhMh96KoArrSAaJBV6PhDQpIC1cQpxgb6SclhRbYG8s,8122
|
14
|
-
spacr/gui_core.py,sha256=
|
15
|
-
spacr/gui_elements.py,sha256=
|
16
|
-
spacr/gui_utils.py,sha256=
|
17
|
-
spacr/io.py,sha256=
|
14
|
+
spacr/gui_core.py,sha256=RtpdB8S8yF9WARRsUjrZ1szZi4ZMfG7R_W34BTBEGYo,52729
|
15
|
+
spacr/gui_elements.py,sha256=OTU7aeLrPiMUTnyCT-J7ygng3beI9tdA0MmypOavEkw,156123
|
16
|
+
spacr/gui_utils.py,sha256=F6KfNY3OqNkvfkOP1rxwBha5IOdLVyBgqZYPw3xPLes,42293
|
17
|
+
spacr/io.py,sha256=P6k3BYsy8VM0p7ficy2Ae4xTPb6tZM4xq47pY57-GBk,157933
|
18
18
|
spacr/logger.py,sha256=lJhTqt-_wfAunCPl93xE65Wr9Y1oIHJWaZMjunHUeIw,1538
|
19
|
-
spacr/measure.py,sha256=
|
19
|
+
spacr/measure.py,sha256=nYvrfVfCIqD1AUk4QBE2jtpeSFtLdfUcnkhkqf9G4xQ,60877
|
20
20
|
spacr/mediar.py,sha256=p0F515eFbm6_rePSnChsgqrgH-H5Sr_3zWrghtOnAUg,14863
|
21
21
|
spacr/ml.py,sha256=XCRZeX7UkbMctQICIoskeWVx8CCmmCoHNauUOAkfFq0,91692
|
22
22
|
spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
|
23
|
-
spacr/plot.py,sha256=
|
23
|
+
spacr/plot.py,sha256=Y1ON8Bu-FsZZZasXIK7nvnOohFzucCvFhyPE2bDGz1A,167340
|
24
24
|
spacr/sequencing.py,sha256=EY12RdW5QRKpHDRQCw1QoAlxCq8FK2v6WoVa5uuDBXQ,26745
|
25
|
-
spacr/settings.py,sha256=
|
25
|
+
spacr/settings.py,sha256=YpGfMzNgHGpqamfZ0wVoVG64UOb0rW9lfxCc9W4g7Ss,87683
|
26
26
|
spacr/sim.py,sha256=1xKhXimNU3ukzIw-3l9cF3Znc_brW8h20yv8fSTzvss,71173
|
27
|
-
spacr/sp_stats.py,sha256=
|
27
|
+
spacr/sp_stats.py,sha256=C93Xe5fphQOKthw4Tmj8pHx-Nb1houIL-YYVIfmnQPg,9535
|
28
28
|
spacr/spacr_cellpose.py,sha256=RBHMs2vwXcfkj0xqAULpALyzJYXddSRycgZSzmwI7v0,14755
|
29
29
|
spacr/submodules.py,sha256=Z2i4kv_rWdxqoXsOKCF7BaSXtvaCZB69Ow8_FQBnZsY,83093
|
30
30
|
spacr/timelapse.py,sha256=-5ZupTsCCpbenIQ2zsUmnwXh45B82fO-gPrSXOxu2s8,42980
|
@@ -83,6 +83,7 @@ spacr/resources/icons/default.png,sha256=KoNhaSHukO4wDyivyYEgSbb5mGj-sAxmhKikLLt
|
|
83
83
|
spacr/resources/icons/dna_matrix.mp4,sha256=NegOQkn4q4kHhFgqcIX2dd58wVytBtnkmbgg0ZegL8U,23462876
|
84
84
|
spacr/resources/icons/download.png,sha256=1nUoWRaTc4vIsK6gompdeqk0cIv2GdH-gCNHaEBX6Mc,20467
|
85
85
|
spacr/resources/icons/flow_chart_v2.png,sha256=4U4uzJlyQ8L-exWIXIhyqtkoO-KIiubO23kA7eLZYYE,640609
|
86
|
+
spacr/resources/icons/flow_chart_v3.png,sha256=Vw7ykdgmXEpA5BLUpDEnp_bAaJ6gPz94EVYqMHXmn1k,638047
|
86
87
|
spacr/resources/icons/logo.pdf,sha256=VB4cS41V3VV_QxD7l6CwdQKQiYLErugLBxWoCoxjQU0,377925
|
87
88
|
spacr/resources/icons/logo_spacr.png,sha256=qG3e3bdrAefhl1281rfo0R2XP0qA-c-oaBCXjxMGXkw,42587
|
88
89
|
spacr/resources/icons/logo_spacr_1.png,sha256=g9y2ZmnV3hab8r1idDfytm8AaHbBiQdu_93Jd7YKzwA,610892
|
@@ -102,9 +103,9 @@ spacr/resources/icons/umap.png,sha256=dOLF3DeLYy9k0nkUybiZMe1wzHQwLJFRmgccppw-8b
|
|
102
103
|
spacr/resources/images/plate1_E01_T0001F001L01A01Z01C02.tif,sha256=Tl0ZUfZ_AYAbu0up_nO0tPRtF1BxXhWQ3T3pURBCCRo,7958528
|
103
104
|
spacr/resources/images/plate1_E01_T0001F001L01A02Z01C01.tif,sha256=m8N-V71rA1TT4dFlENNg8s0Q0YEXXs8slIn7yObmZJQ,7958528
|
104
105
|
spacr/resources/images/plate1_E01_T0001F001L01A03Z01C03.tif,sha256=Pbhk7xn-KUP6RSIhJsxQcrHFImBm3GEpLkzx7WOc-5M,7958528
|
105
|
-
spacr-0.9.
|
106
|
-
spacr-0.9.
|
107
|
-
spacr-0.9.
|
108
|
-
spacr-0.9.
|
109
|
-
spacr-0.9.
|
110
|
-
spacr-0.9.
|
106
|
+
spacr-0.9.4.dist-info/LICENSE,sha256=t0Pov6pnK8thLteoF4xZGmdCwe5mhNwl3OXxLYTGD9U,1081
|
107
|
+
spacr-0.9.4.dist-info/METADATA,sha256=rEwXv8HSYOVMSYZpzjNXFds9XEIlk0y85AsxsZbGbGc,10324
|
108
|
+
spacr-0.9.4.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
109
|
+
spacr-0.9.4.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
|
110
|
+
spacr-0.9.4.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
|
111
|
+
spacr-0.9.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|