spatialproteomics 0.8.2__tar.gz → 0.8.3__tar.gz
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.
- {spatialproteomics-0.8.2/spatialproteomics.egg-info → spatialproteomics-0.8.3}/PKG-INFO +1 -1
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/pyproject.toml +1 -1
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pl/plot.py +30 -14
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pp/utils.py +76 -40
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3/spatialproteomics.egg-info}/PKG-INFO +1 -1
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/LICENSE +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/README.md +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/setup.cfg +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/base_logger.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/constants.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/container.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/image_container/ImageContainer.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/image_container/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/la/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/la/label.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/la/utils.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/nh/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/nh/neighborhood.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/nh/utils.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pl/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pl/utils.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pp/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pp/intensity.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/pp/preprocessing.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/sd/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/sd/utils.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/tl/__init__.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/tl/tool.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/tl/utils.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/SOURCES.txt +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/dependency_links.txt +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/requires.txt +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/top_level.txt +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/tests/test_container.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/tests/test_integration.py +0 -0
- {spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/tests/test_to_zarr.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialproteomics
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: Spatialproteomics is an interoperable toolbox for analyzing highly multiplexed fluorescence image data
|
|
5
5
|
Author: Matthias Meyer-Bender, Harald Vohringer
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -1114,6 +1114,18 @@ class PlotAccessor:
|
|
|
1114
1114
|
extent=bounds,
|
|
1115
1115
|
)
|
|
1116
1116
|
|
|
1117
|
+
# draw any pending boxes added via add_box()
|
|
1118
|
+
for box in self._obj.attrs.pop("_pending_boxes", []):
|
|
1119
|
+
rect = patches.Rectangle(
|
|
1120
|
+
(box["xlim"][0], box["ylim"][0]),
|
|
1121
|
+
box["xlim"][1] - box["xlim"][0],
|
|
1122
|
+
box["ylim"][1] - box["ylim"][0],
|
|
1123
|
+
edgecolor=box["color"],
|
|
1124
|
+
linewidth=box["linewidth"],
|
|
1125
|
+
facecolor="none",
|
|
1126
|
+
)
|
|
1127
|
+
ax.add_patch(rect)
|
|
1128
|
+
|
|
1117
1129
|
legend = []
|
|
1118
1130
|
|
|
1119
1131
|
if legend_image:
|
|
@@ -1401,20 +1413,24 @@ class PlotAccessor:
|
|
|
1401
1413
|
xr.Dataset
|
|
1402
1414
|
The updated spatialproteomics object.
|
|
1403
1415
|
"""
|
|
1404
|
-
if ax is None:
|
|
1405
|
-
ax
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1416
|
+
if ax is not None:
|
|
1417
|
+
# existing immediate-draw behaviour when ax is explicit
|
|
1418
|
+
rect = patches.Rectangle(
|
|
1419
|
+
(xlim[0], ylim[0]), # bottom left corner
|
|
1420
|
+
xlim[1] - xlim[0], # width
|
|
1421
|
+
ylim[1] - ylim[0], # height
|
|
1422
|
+
edgecolor=color,
|
|
1423
|
+
linewidth=linewidth,
|
|
1424
|
+
facecolor="none",
|
|
1425
|
+
)
|
|
1426
|
+
ax.add_patch(rect)
|
|
1427
|
+
return self._obj
|
|
1428
|
+
|
|
1429
|
+
# no ax provided: defer rendering by storing box params in the dataset attrs
|
|
1430
|
+
# the boxes are then rendered in pl.imshow() when the plot is drawn
|
|
1431
|
+
pending = list(self._obj.attrs.get("_pending_boxes", []))
|
|
1432
|
+
pending.append(dict(xlim=xlim, ylim=ylim, color=color, linewidth=linewidth))
|
|
1433
|
+
self._obj.attrs["_pending_boxes"] = pending
|
|
1418
1434
|
return self._obj
|
|
1419
1435
|
|
|
1420
1436
|
def autocrop(
|
|
@@ -602,12 +602,12 @@ def _threshold(
|
|
|
602
602
|
if (quantile is None and intensity is None) or (quantile is not None and intensity is not None):
|
|
603
603
|
raise ValueError("Please provide a quantile or absolute intensity cut off.")
|
|
604
604
|
|
|
605
|
-
if isinstance(quantile, (float, int)):
|
|
605
|
+
if isinstance(quantile, (float, int, np.generic)):
|
|
606
606
|
quantile = np.array([quantile])
|
|
607
607
|
if isinstance(quantile, list):
|
|
608
608
|
quantile = np.array(quantile)
|
|
609
609
|
|
|
610
|
-
if isinstance(intensity, (float, int)):
|
|
610
|
+
if isinstance(intensity, (float, int, np.generic)):
|
|
611
611
|
intensity = np.array([intensity])
|
|
612
612
|
if isinstance(intensity, list):
|
|
613
613
|
intensity = np.array(intensity)
|
|
@@ -626,68 +626,104 @@ def _threshold(
|
|
|
626
626
|
if quantile is not None:
|
|
627
627
|
assert len(channels) == len(quantile), "The number of channels must match the number of quantile values."
|
|
628
628
|
quantile_dict = dict(zip(channels, quantile))
|
|
629
|
-
|
|
629
|
+
# we cannot just use 0 in the get() here, because the minimum intensity of a marker might be above 0,
|
|
630
|
+
# #in which case we would be thresholding the wrong value. Instead, we use np.nan and then check for this later on.
|
|
631
|
+
quantile = np.array([quantile_dict.get(channel, np.nan) for channel in all_channels])
|
|
630
632
|
if intensity is not None:
|
|
631
633
|
assert len(channels) == len(intensity), "The number of channels must match the number of intensity values."
|
|
632
634
|
intensity_dict = dict(zip(channels, intensity))
|
|
633
|
-
intensity = np.array([intensity_dict.get(channel,
|
|
635
|
+
intensity = np.array([intensity_dict.get(channel, np.nan) for channel in all_channels])
|
|
634
636
|
else:
|
|
635
637
|
# If no channels provided, assume the threshold applies to all channels
|
|
636
638
|
if quantile is not None:
|
|
637
639
|
assert len(quantile) == 1 or len(quantile) == len(
|
|
638
640
|
all_channels
|
|
639
641
|
), "Quantile threshold must be a single value or a list of values with the same length as the number of channels. If you only want to threshold a subset of channels, you can use the channels argument."
|
|
640
|
-
quantile = np.full(len(all_channels), quantile.item() if quantile.size == 1 else quantile)
|
|
641
642
|
if intensity is not None:
|
|
642
643
|
assert len(intensity) == 1 or len(intensity) == len(
|
|
643
644
|
all_channels
|
|
644
645
|
), "Intensity threshold must be a single value or a list of values with the same length as the number of channels. If you only want to threshold a subset of channels, you can use the channels argument."
|
|
645
646
|
assert np.all(intensity >= 0), "Intensity values must be positive."
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
647
|
+
|
|
648
|
+
# Get underlying numpy array and its dtype
|
|
649
|
+
img = image.values # shape (C, Y, X)
|
|
650
|
+
img_dtype = img.dtype
|
|
650
651
|
|
|
651
652
|
if quantile is not None:
|
|
652
|
-
|
|
653
|
+
if quantile.size == 1:
|
|
654
|
+
quantile = np.full(len(all_channels), quantile.item(), dtype=float)
|
|
655
|
+
else:
|
|
656
|
+
quantile = np.asarray(quantile, dtype=float)
|
|
653
657
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
lower = np.quantile(image.values.reshape(image.values.shape[0], -1), quantile, axis=1).astype(image.dtype)
|
|
658
|
-
|
|
659
|
-
filtered = np.clip(
|
|
660
|
-
image, a_min=np.expand_dims(np.diag(lower) if lower.ndim > 1 else lower, (1, 2)), a_max=None
|
|
661
|
-
).astype(image.dtype) - np.expand_dims(np.diag(lower) if lower.ndim > 1 else lower, (1, 2)).astype(
|
|
662
|
-
image.dtype
|
|
663
|
-
)
|
|
658
|
+
if intensity is not None:
|
|
659
|
+
if intensity.size == 1:
|
|
660
|
+
intensity = np.full(len(all_channels), intensity.item(), dtype=img_dtype)
|
|
664
661
|
else:
|
|
665
|
-
#
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
[np.quantile(flattened_values[i], q) for i, q in enumerate(quantile)]
|
|
671
|
-
) # Compute quantile per channel.
|
|
662
|
+
# cast each provided value to image dtype
|
|
663
|
+
intensity = np.asarray(intensity, dtype=img_dtype)
|
|
664
|
+
|
|
665
|
+
max_val = np.max(img)
|
|
666
|
+
assert np.all(intensity <= max_val), "Intensity values must be smaller than the maximum intensity."
|
|
672
667
|
|
|
673
|
-
|
|
674
|
-
lower = lower[:, np.newaxis, np.newaxis] # Reshape to add height and width dimensions.
|
|
668
|
+
filtered = None
|
|
675
669
|
|
|
676
|
-
|
|
677
|
-
|
|
670
|
+
# --- QUANTILE BRANCH ---
|
|
671
|
+
if quantile is not None:
|
|
672
|
+
assert np.all(quantile[~np.isnan(quantile)] >= 0) and np.all(
|
|
673
|
+
quantile[~np.isnan(quantile)] <= 1
|
|
674
|
+
), "Quantile values must be between 0 and 1."
|
|
678
675
|
|
|
676
|
+
flat = img.reshape(img.shape[0], -1)
|
|
677
|
+
lower = np.array([np.quantile(flat[i], q) if not np.isnan(q) else 0.0 for i, q in enumerate(quantile)]).astype(
|
|
678
|
+
img_dtype
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
lower = lower[:, np.newaxis, np.newaxis]
|
|
682
|
+
filtered = np.empty_like(img)
|
|
683
|
+
|
|
684
|
+
if shift:
|
|
685
|
+
for c in range(img.shape[0]):
|
|
686
|
+
lc = lower[c, 0, 0]
|
|
687
|
+
if np.isnan(quantile[c]): # no-op channel: copy as-is
|
|
688
|
+
np.copyto(filtered[c], img[c])
|
|
689
|
+
else:
|
|
690
|
+
np.maximum(img[c], lc, out=filtered[c])
|
|
691
|
+
filtered[c] -= lc
|
|
692
|
+
else:
|
|
693
|
+
for c in range(img.shape[0]):
|
|
694
|
+
lc = lower[c, 0, 0]
|
|
695
|
+
np.copyto(filtered[c], img[c])
|
|
696
|
+
if not np.isnan(quantile[c]): # only threshold specified channels
|
|
697
|
+
mask = filtered[c] < lc
|
|
698
|
+
filtered[c][mask] = img_dtype.type(0)
|
|
699
|
+
|
|
700
|
+
# --- INTENSITY BRANCH (dtype-safe & channel-wise) ---
|
|
679
701
|
if intensity is not None:
|
|
702
|
+
base = img if filtered is None else filtered
|
|
703
|
+
result = np.empty_like(base)
|
|
704
|
+
n_channels = base.shape[0]
|
|
705
|
+
|
|
680
706
|
if shift:
|
|
681
|
-
|
|
682
|
-
|
|
707
|
+
for c in range(n_channels):
|
|
708
|
+
if np.isnan(intensity[c]): # no-op channel
|
|
709
|
+
np.copyto(result[c], base[c])
|
|
710
|
+
else:
|
|
711
|
+
thr = img_dtype.type(intensity[c])
|
|
712
|
+
np.maximum(base[c], thr, out=result[c])
|
|
713
|
+
result[c] -= thr
|
|
683
714
|
else:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
715
|
+
zero = img_dtype.type(0)
|
|
716
|
+
for c in range(n_channels):
|
|
717
|
+
np.copyto(result[c], base[c])
|
|
718
|
+
if not np.isnan(intensity[c]): # only threshold specified channels
|
|
719
|
+
thr = img_dtype.type(intensity[c])
|
|
720
|
+
mask = result[c] < thr
|
|
721
|
+
result[c][mask] = zero
|
|
722
|
+
|
|
723
|
+
filtered = result
|
|
724
|
+
|
|
725
|
+
if filtered is None:
|
|
726
|
+
raise RuntimeError("Internal error: neither quantile nor intensity branch produced a result.")
|
|
691
727
|
|
|
692
728
|
return filtered
|
|
693
729
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialproteomics
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: Spatialproteomics is an interoperable toolbox for analyzing highly multiplexed fluorescence image data
|
|
5
5
|
Author: Matthias Meyer-Bender, Harald Vohringer
|
|
6
6
|
Requires-Python: >=3.10
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics/image_container/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.8.2 → spatialproteomics-0.8.3}/spatialproteomics.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|