antspymm 1.4.6__py3-none-any.whl → 1.4.9__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.
- antspymm/mm.py +115 -8
- {antspymm-1.4.6.dist-info → antspymm-1.4.9.dist-info}/METADATA +8 -3
- antspymm-1.4.9.dist-info/RECORD +7 -0
- {antspymm-1.4.6.dist-info → antspymm-1.4.9.dist-info}/WHEEL +1 -1
- antspymm-1.4.6.dist-info/RECORD +0 -7
- {antspymm-1.4.6.dist-info → antspymm-1.4.9.dist-info}/licenses/LICENSE +0 -0
- {antspymm-1.4.6.dist-info → antspymm-1.4.9.dist-info}/top_level.txt +0 -0
antspymm/mm.py
CHANGED
@@ -3882,9 +3882,13 @@ def joint_dti_recon(
|
|
3882
3882
|
print("recon after distortion correction", flush=True)
|
3883
3883
|
|
3884
3884
|
if impute:
|
3885
|
+
print("impute begin", flush=True)
|
3885
3886
|
img_LRdwp=impute_dwi( img_LRdwp, verbose=True )
|
3887
|
+
print("impute done", flush=True)
|
3886
3888
|
elif censor:
|
3889
|
+
print("censor begin", flush=True)
|
3887
3890
|
img_LRdwp, reg_LR['bvals'], reg_LR['bvecs'] = censor_dwi( img_LRdwp, reg_LR['bvals'], reg_LR['bvecs'], verbose=True )
|
3891
|
+
print("censor done", flush=True)
|
3888
3892
|
if impute and img_RL is not None:
|
3889
3893
|
img_RLdwp=impute_dwi( img_RLdwp, verbose=True )
|
3890
3894
|
elif censor and img_RL is not None:
|
@@ -7282,8 +7286,8 @@ def mm(
|
|
7282
7286
|
img_RL=dw_image[1],
|
7283
7287
|
bval_RL=bvals[1],
|
7284
7288
|
bvec_RL=bvecs[1],
|
7285
|
-
motion_correct=
|
7286
|
-
denoise=
|
7289
|
+
motion_correct=dti_motion_correct, # set to False if using input from qsiprep
|
7290
|
+
denoise=dti_denoise,
|
7287
7291
|
verbose = verbose)
|
7288
7292
|
mydti = output_dict['DTI']
|
7289
7293
|
# summarize dwi with T1 outputs
|
@@ -10602,9 +10606,9 @@ def censor_dwi( dwi, bval, bvec, threshold = 0.20, imputeb0=False, mask=None, ve
|
|
10602
10606
|
list1 = segment_timeseries_by_meanvalue( dwi )['highermeans']
|
10603
10607
|
if imputeb0:
|
10604
10608
|
dwib = impute_timeseries( dwi, list1 ) # focus on the dwi - not the b0
|
10605
|
-
looped, list2 = loop_timeseries_censoring( dwib, threshold, mask )
|
10609
|
+
looped, list2 = loop_timeseries_censoring( dwib, threshold, mask, verbose=verbose)
|
10606
10610
|
else:
|
10607
|
-
looped, list2 = loop_timeseries_censoring( dwi, threshold, mask )
|
10611
|
+
looped, list2 = loop_timeseries_censoring( dwi, threshold, mask, verbose=verbose )
|
10608
10612
|
if verbose:
|
10609
10613
|
print( list1 )
|
10610
10614
|
print( list2 )
|
@@ -10627,18 +10631,21 @@ def flatten_time_series(time_series):
|
|
10627
10631
|
n_volumes = time_series.shape[3]
|
10628
10632
|
return time_series.reshape(-1, n_volumes).T
|
10629
10633
|
|
10630
|
-
def
|
10634
|
+
def calculate_loop_scores_full(flattened_series, n_neighbors=20, verbose=True ):
|
10631
10635
|
"""
|
10632
10636
|
Calculate Local Outlier Probabilities for each volume.
|
10633
10637
|
|
10634
10638
|
:param flattened_series: A 2D numpy array from flatten_time_series.
|
10635
10639
|
:param n_neighbors: Number of neighbors to use for calculating LOF scores.
|
10640
|
+
:param verbose: boolean
|
10636
10641
|
:return: An array of LoOP scores.
|
10637
10642
|
"""
|
10638
10643
|
from PyNomaly import loop
|
10639
10644
|
from sklearn.neighbors import NearestNeighbors
|
10640
10645
|
from sklearn.preprocessing import StandardScaler
|
10641
10646
|
# replace nans with zero
|
10647
|
+
if verbose:
|
10648
|
+
print("loop: nan_to_num")
|
10642
10649
|
flattened_series=np.nan_to_num(flattened_series, nan=0)
|
10643
10650
|
scaler = StandardScaler()
|
10644
10651
|
scaler.fit(flattened_series)
|
@@ -10646,12 +10653,109 @@ def calculate_loop_scores(flattened_series, n_neighbors=20):
|
|
10646
10653
|
data=np.nan_to_num(data, nan=0)
|
10647
10654
|
if n_neighbors > int(flattened_series.shape[0]/2.0):
|
10648
10655
|
n_neighbors = int(flattened_series.shape[0]/2.0)
|
10656
|
+
if verbose:
|
10657
|
+
print("loop: nearest neighbors init")
|
10649
10658
|
neigh = NearestNeighbors(n_neighbors=n_neighbors, metric='minkowski')
|
10659
|
+
if verbose:
|
10660
|
+
print("loop: nearest neighbors fit")
|
10650
10661
|
neigh.fit(data)
|
10651
10662
|
d, idx = neigh.kneighbors(data, return_distance=True)
|
10663
|
+
if verbose:
|
10664
|
+
print("loop: probability")
|
10652
10665
|
m = loop.LocalOutlierProbability(distance_matrix=d, neighbor_matrix=idx, n_neighbors=n_neighbors).fit()
|
10653
10666
|
return m.local_outlier_probabilities[:]
|
10654
10667
|
|
10668
|
+
|
10669
|
+
def calculate_loop_scores(flattened_series, n_neighbors=20,
|
10670
|
+
n_features_sample=10000, seed=42, verbose=True):
|
10671
|
+
"""
|
10672
|
+
Approximate LoOP scores using a random subset of features to reduce memory usage.
|
10673
|
+
|
10674
|
+
Parameters:
|
10675
|
+
flattened_series (np.ndarray): 2D array of shape (n_samples, n_features)
|
10676
|
+
n_neighbors (int): Number of neighbors for LOF/LoOP computation
|
10677
|
+
n_features_sample (int): Number of features to sample for approximation
|
10678
|
+
seed (int): Random seed for reproducible feature sampling
|
10679
|
+
verbose (bool): If True, print detailed progress and dimensions
|
10680
|
+
|
10681
|
+
Returns:
|
10682
|
+
np.ndarray: 1D array of local outlier probabilities (length n_samples)
|
10683
|
+
"""
|
10684
|
+
import numpy as np
|
10685
|
+
from sklearn.preprocessing import StandardScaler
|
10686
|
+
from sklearn.neighbors import NearestNeighbors
|
10687
|
+
from PyNomaly import loop
|
10688
|
+
|
10689
|
+
# -------------------------------
|
10690
|
+
# Step 1: Input stats and cleanup
|
10691
|
+
# -------------------------------
|
10692
|
+
X = np.nan_to_num(flattened_series, nan=0).astype(np.float32)
|
10693
|
+
n_samples, n_features = X.shape
|
10694
|
+
|
10695
|
+
if verbose:
|
10696
|
+
print("\n[LoOP Approximation - Verbose Mode]")
|
10697
|
+
print(f"- Original input shape: {X.shape} (samples x features)")
|
10698
|
+
print(f"- Requested sampled features: {n_features_sample}")
|
10699
|
+
|
10700
|
+
if n_features_sample > n_features:
|
10701
|
+
n_features_sample = n_features
|
10702
|
+
if verbose:
|
10703
|
+
print(f"- Requested n_features_sample exceeds available features. Using all {n_features} features.")
|
10704
|
+
|
10705
|
+
# -------------------------------
|
10706
|
+
# Step 2: Feature sampling
|
10707
|
+
# -------------------------------
|
10708
|
+
rng = np.random.default_rng(seed)
|
10709
|
+
sampled_indices = rng.choice(n_features, n_features_sample, replace=False)
|
10710
|
+
X_sampled = X[:, sampled_indices]
|
10711
|
+
|
10712
|
+
if verbose:
|
10713
|
+
print(f"- Sampled feature shape: {X_sampled.shape} (samples x sampled_features)")
|
10714
|
+
print(f"- Random seed for reproducibility: {seed}")
|
10715
|
+
|
10716
|
+
# -------------------------------
|
10717
|
+
# Step 3: Standardization
|
10718
|
+
# -------------------------------
|
10719
|
+
scaler = StandardScaler(copy=False)
|
10720
|
+
X_sampled = scaler.fit_transform(X_sampled)
|
10721
|
+
X_sampled = np.nan_to_num(X_sampled, nan=0)
|
10722
|
+
|
10723
|
+
# -------------------------------
|
10724
|
+
# Step 4: KNN setup for LoOP
|
10725
|
+
# -------------------------------
|
10726
|
+
if n_neighbors >= n_samples:
|
10727
|
+
n_neighbors = max(1, n_samples // 2)
|
10728
|
+
if verbose:
|
10729
|
+
print(f"- Adjusted n_neighbors to {n_neighbors} (was too large for available samples).")
|
10730
|
+
|
10731
|
+
if verbose:
|
10732
|
+
print(f"- Performing KNN using Minkowski distance (default p=2, Euclidean)")
|
10733
|
+
print(f"- Each point will use its {n_neighbors} nearest neighbors for local density estimation")
|
10734
|
+
|
10735
|
+
neigh = NearestNeighbors(n_neighbors=n_neighbors)
|
10736
|
+
neigh.fit(X_sampled)
|
10737
|
+
dists, indices = neigh.kneighbors(X_sampled, return_distance=True)
|
10738
|
+
|
10739
|
+
# -------------------------------
|
10740
|
+
# Step 5: LoOP probability calculation
|
10741
|
+
# -------------------------------
|
10742
|
+
if verbose:
|
10743
|
+
print(f"- Distance matrix shape: {dists.shape} (samples x n_neighbors)")
|
10744
|
+
print(f"- Neighbor index matrix shape: {indices.shape}")
|
10745
|
+
print("- Estimating Local Outlier Probabilities (LoOP)...")
|
10746
|
+
|
10747
|
+
model = loop.LocalOutlierProbability(
|
10748
|
+
distance_matrix=dists,
|
10749
|
+
neighbor_matrix=indices,
|
10750
|
+
n_neighbors=n_neighbors
|
10751
|
+
).fit()
|
10752
|
+
|
10753
|
+
if verbose:
|
10754
|
+
print("- LoOP scoring complete.\n")
|
10755
|
+
|
10756
|
+
return model.local_outlier_probabilities[:]
|
10757
|
+
|
10758
|
+
|
10655
10759
|
def score_fmri_censoring(cbfts, csf_seg, gm_seg, wm_seg ):
|
10656
10760
|
"""
|
10657
10761
|
Process CBF time series to remove high-leverage points.
|
@@ -10719,7 +10823,7 @@ def score_fmri_censoring(cbfts, csf_seg, gm_seg, wm_seg ):
|
|
10719
10823
|
cbfts_recon_ants = ants.copy_image_info(cbfts, cbfts_recon_ants)
|
10720
10824
|
return cbfts_recon_ants, indx
|
10721
10825
|
|
10722
|
-
def loop_timeseries_censoring(x, threshold=0.5, mask=None, verbose=
|
10826
|
+
def loop_timeseries_censoring(x, threshold=0.5, mask=None, n_features_sample=10000, verbose=True):
|
10723
10827
|
"""
|
10724
10828
|
Censor high leverage volumes from a time series using Local Outlier Probabilities (LoOP).
|
10725
10829
|
|
@@ -10727,6 +10831,7 @@ def loop_timeseries_censoring(x, threshold=0.5, mask=None, verbose=False):
|
|
10727
10831
|
x (ANTsImage): A 4D time series image.
|
10728
10832
|
threshold (float): Threshold for determining high leverage volumes based on LoOP scores.
|
10729
10833
|
mask (antsImage): restricts to a ROI
|
10834
|
+
n_features_sample (int): feature sample size default 5000
|
10730
10835
|
verbose (bool)
|
10731
10836
|
|
10732
10837
|
Returns:
|
@@ -10740,10 +10845,12 @@ def loop_timeseries_censoring(x, threshold=0.5, mask=None, verbose=False):
|
|
10740
10845
|
flattened_series = flatten_time_series(x.numpy())
|
10741
10846
|
else:
|
10742
10847
|
flattened_series = ants.timeseries_to_matrix( x, mask )
|
10743
|
-
|
10848
|
+
if verbose:
|
10849
|
+
print("loop_timeseries_censoring: flattened")
|
10850
|
+
loop_scores = calculate_loop_scores(flattened_series, n_features_sample=n_features_sample, verbose=verbose )
|
10744
10851
|
high_leverage_volumes = np.where(loop_scores > threshold)[0]
|
10745
10852
|
if verbose:
|
10746
|
-
print("
|
10853
|
+
print("loop_timeseries_censoring: High Leverage Volumes:", high_leverage_volumes)
|
10747
10854
|
new_asl = remove_volumes_from_timeseries(x, high_leverage_volumes)
|
10748
10855
|
return new_asl, high_leverage_volumes
|
10749
10856
|
|
@@ -1,10 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: antspymm
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.9
|
4
4
|
Summary: multi-channel/time-series medical image processing with antspyx
|
5
5
|
Author-email: "Avants, Gosselin, Tustison, Reardon" <stnava@gmail.com>
|
6
|
-
License: Apache
|
7
|
-
|
6
|
+
License: Apache-2.0
|
7
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Operating System :: OS Independent
|
10
|
+
Requires-Python: >=3.10
|
8
11
|
Description-Content-Type: text/markdown
|
9
12
|
License-File: LICENSE
|
10
13
|
Requires-Dist: h5py>=2.10.0
|
@@ -23,6 +26,8 @@ Dynamic: license-file
|
|
23
26
|
|
24
27
|
# ANTsPyMM
|
25
28
|
|
29
|
+
[](https://doi.org/10.5281/zenodo.15131653)
|
30
|
+
|
26
31
|
[](https://dl.circleci.com/status-badge/redirect/gh/ANTsX/ANTsPyMM/tree/main)
|
27
32
|
|
28
33
|

|
@@ -0,0 +1,7 @@
|
|
1
|
+
antspymm/__init__.py,sha256=ZdNJyHwS6rzq59v0OK3tE3qSTD0za2iULzSLGkM_0uc,4527
|
2
|
+
antspymm/mm.py,sha256=tJXaT-81XEjNjCOhmGKCjSRB7HEM2z_mlAWxKwJlc3M,517529
|
3
|
+
antspymm-1.4.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
4
|
+
antspymm-1.4.9.dist-info/METADATA,sha256=6uEIRuATG8mqBBCQxdXHyd15xDeVBcyFOH3T-OnFYHw,25940
|
5
|
+
antspymm-1.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
6
|
+
antspymm-1.4.9.dist-info/top_level.txt,sha256=iyD1sRhCKzfwKRJLq5ZUeV9xsv1cGQl8Ejp6QwXM1Zg,9
|
7
|
+
antspymm-1.4.9.dist-info/RECORD,,
|
antspymm-1.4.6.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
antspymm/__init__.py,sha256=ZdNJyHwS6rzq59v0OK3tE3qSTD0za2iULzSLGkM_0uc,4527
|
2
|
-
antspymm/mm.py,sha256=XXEbuqcFKR-f9zaFfQG53qzGMbm2hGzw7ai_Z6jPcx4,513312
|
3
|
-
antspymm-1.4.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
4
|
-
antspymm-1.4.6.dist-info/METADATA,sha256=Edy68PT2PRJsLt55BIlx49m_1n_4bJI9NpKGxIVk4r8,25690
|
5
|
-
antspymm-1.4.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
6
|
-
antspymm-1.4.6.dist-info/top_level.txt,sha256=iyD1sRhCKzfwKRJLq5ZUeV9xsv1cGQl8Ejp6QwXM1Zg,9
|
7
|
-
antspymm-1.4.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|